forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from rianquinn/suspend_boxy_support
Add vClock/vIRQ support
- Loading branch information
Showing
14 changed files
with
678 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
# | ||
# This Kconfig describes Boxy options | ||
# | ||
|
||
config BOXY | ||
bool "Boxy guest support" | ||
depends on PARAVIRT && X86_64 | ||
---help--- | ||
This option allows you to run Linux as a Boxy guest virtual machine. | ||
Boxy requires a very specific kernel config so in general, this | ||
option should not be manually selected and instead is enabled by the | ||
Boxy build system with the proper config in place. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
obj-y := init.o virq.o vclock.o vmcall.o quirk.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/** | ||
* Copyright (C) 2019 Assured Information Security, Inc. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License along | ||
* with this program; if not, write to the Free Software Foundation, Inc., | ||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
*/ | ||
|
||
#include <asm/boxy.h> | ||
#include <asm/i8259.h> | ||
|
||
static uint32_t __init boxy_detect(void) | ||
{ | ||
uint32_t eax; | ||
uint32_t ignore[3]; | ||
|
||
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR)) | ||
return 0; | ||
|
||
cpuid(CPUID_BAREFLANK_SYN, &eax, &ignore[0], &ignore[1], &ignore[2]); | ||
|
||
/** | ||
* TODO: | ||
* | ||
* We need to add a Boxy specific CPUID leaf at 0x40000000 that acks like | ||
* VMWare and HyperV so that we play nice with nested virtualization. | ||
* More importantly, right now we are acking with Bareflank and not | ||
* Boxy, so this code could end up detecting someone elses hypervisor. | ||
*/ | ||
|
||
/** | ||
* TODO: | ||
* | ||
* We need to implement versioning to ensure that we are using a guest | ||
* that actually knows how to talk to the hypervisor. | ||
*/ | ||
|
||
if (eax == CPUID_BAREFLANK_ACK) | ||
return 1; | ||
|
||
return 0; | ||
} | ||
|
||
static void __init boxy_init_platform(void) | ||
{ | ||
pv_info.name = "Boxy Hypervisor"; | ||
|
||
boxy_virq_init(); | ||
boxy_vclock_init(); | ||
|
||
x86_init.resources.probe_roms = x86_init_noop; | ||
x86_init.mpparse.find_smp_config = x86_init_noop; | ||
x86_init.mpparse.get_smp_config = boxy_apic_quirk; | ||
x86_init.irqs.pre_vector_init = x86_init_noop; | ||
x86_init.oem.arch_setup = x86_init_noop; | ||
x86_init.oem.banner = x86_init_noop; | ||
|
||
x86_platform.legacy.rtc = 0; | ||
x86_platform.legacy.warm_reset = 0; | ||
x86_platform.legacy.i8042 = X86_LEGACY_I8042_PLATFORM_ABSENT; | ||
|
||
legacy_pic = &null_legacy_pic; | ||
} | ||
|
||
static bool __init boxy_x2apic_available(void) | ||
{ return true; } | ||
|
||
const __initconst struct hypervisor_x86 x86_hyper_boxy = { | ||
.name = "Boxy Hypervisor", | ||
.detect = boxy_detect, | ||
.type = X86_HYPER_BOXY, | ||
.init.init_platform = boxy_init_platform, | ||
.init.x2apic_available = boxy_x2apic_available, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* Copyright (C) 2019 Assured Information Security, Inc. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License along | ||
* with this program; if not, write to the Free Software Foundation, Inc., | ||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
*/ | ||
|
||
#include <asm/boxy.h> | ||
#include <asm/apic.h> | ||
#include <linux/init.h> | ||
|
||
/** | ||
* Quirk Notes | ||
* | ||
* Like Jailhouse, we require an x2apic, and currently, if we don't set the | ||
* code below, the kernel will crash as it attempts to read an x2apic | ||
* register before the apic variable is set. The code below ensures that | ||
* we end up with symmetric IO mode with a physical x2apic while also setting | ||
* the apic variable so that the kernel doesn't segfault. | ||
* | ||
* If you enable ACPI, this bug will go away as ACPI happens to call the | ||
* default_acpi_madt_oem_check() function which sets the apic variable in the | ||
* kernel before the init_apic_mappings() function is called. The crash | ||
* occurs here: | ||
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/apic/apic.c#L1969 | ||
* | ||
* Since we are calling the default_acpi_madt_oem_check() function manually, | ||
* we need to ensure that the apic is configured properly for Boxy guests | ||
* which includes a physical x2apic and symmetric IO mode. | ||
*/ | ||
|
||
void __init boxy_apic_quirk(unsigned int early) | ||
{ | ||
x2apic_phys = 1; | ||
smp_found_config = 1; | ||
|
||
default_acpi_madt_oem_check("", ""); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/** | ||
* Copyright (C) 2019 Assured Information Security, Inc. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License along | ||
* with this program; if not, write to the Free Software Foundation, Inc., | ||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
*/ | ||
|
||
#include <asm/boxy.h> | ||
#include <linux/time64.h> | ||
#include <linux/clocksource.h> | ||
#include <linux/clockchips.h> | ||
|
||
static uint64_t g_tsc_offset = 0; | ||
static uint64_t g_tsc_freq_khz = 0; | ||
|
||
/******************************************************************************/ | ||
/* helpers */ | ||
/******************************************************************************/ | ||
|
||
static uint64_t mul_div(uint64_t x, uint64_t n, uint64_t d) | ||
{ return ((x / d) * n) + (((x % d) * n) / d); } | ||
|
||
static uint64_t tsc_to_nsec(uint64_t tsc) | ||
{ return mul_div(tsc, 1000000, g_tsc_freq_khz); } | ||
|
||
/******************************************************************************/ | ||
/* clock source */ | ||
/******************************************************************************/ | ||
|
||
static u64 boxy_clocksource_read(struct clocksource *cs) | ||
{ return rdtsc_ordered() - g_tsc_offset; } | ||
|
||
static struct clocksource boxy_clocksource = { | ||
.name = "boxy-clocksource", | ||
.read = boxy_clocksource_read, | ||
.rating = 500, | ||
.mask = CLOCKSOURCE_MASK(64), | ||
.flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES, | ||
.archdata.vclock_mode = VCLOCK_TSC | ||
}; | ||
|
||
/******************************************************************************/ | ||
/* clock event */ | ||
/******************************************************************************/ | ||
|
||
static int boxy_set_next_event( | ||
unsigned long delta, struct clock_event_device *evt) | ||
{ | ||
if (hypercall_vclock_op__set_next_event(delta) != SUCCESS) { | ||
pr_err("hypercall_vclock_op__set_next_event failed"); | ||
BUG(); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct clock_event_device boxy_clock_event_device = { | ||
.name = "boxy-clock-event-device", | ||
.features = CLOCK_EVT_FEAT_ONESHOT, | ||
.mult = 1, | ||
.shift = 0, | ||
.rating = 500, | ||
.set_next_event = boxy_set_next_event, | ||
}; | ||
|
||
void boxy_vclock_event_handler(void) | ||
{ boxy_clock_event_device.event_handler(&boxy_clock_event_device); } | ||
|
||
static void __init boxy_setup_percpu_clockev(void) | ||
{ | ||
boxy_clock_event_device.cpumask = cpumask_of(smp_processor_id()); | ||
|
||
clockevents_config_and_register( | ||
&boxy_clock_event_device, g_tsc_freq_khz * 1000, 0, ~0UL); | ||
} | ||
|
||
/******************************************************************************/ | ||
/* pv_ops */ | ||
/******************************************************************************/ | ||
|
||
static u64 boxy_sched_clock(void) | ||
{ return tsc_to_nsec(boxy_clocksource_read(0)); } | ||
|
||
static u64 boxy_steal_clock(int cpu) | ||
{ | ||
/** | ||
* Note: | ||
* | ||
* For now we do not support the steal clock. Timekeeping seems to work | ||
* fine without it, and implementing this would require not only an | ||
* additional VMCall on every sched_clock() call, but it would also | ||
* require the hypervisor to perform time keeping on every exit and | ||
* entry to account for the time that the VM is actually executing. | ||
*/ | ||
|
||
return 0; | ||
} | ||
|
||
/******************************************************************************/ | ||
/* x86_platform_ops */ | ||
/******************************************************************************/ | ||
|
||
static unsigned long tsc_freq_khz(void) | ||
{ return g_tsc_freq_khz; } | ||
|
||
/******************************************************************************/ | ||
/* init functions */ | ||
/******************************************************************************/ | ||
|
||
static void wallclock_init(void) | ||
{ | ||
if (hypercall_vclock_op__reset_host_wallclock() != SUCCESS) { | ||
pr_err("hypercall_vclock_op__reset_host_wallclock failed"); | ||
BUG(); | ||
} | ||
|
||
if (hypercall_vclock_op__set_guest_wallclock_rtc() != SUCCESS) { | ||
pr_err("hypercall_vclock_op__set_guest_wallclock_rtc failed"); | ||
BUG(); | ||
} | ||
|
||
if (hypercall_vclock_op__set_guest_wallclock_tsc() != SUCCESS) { | ||
pr_err("hypercall_vclock_op__set_guest_wallclock_tsc failed"); | ||
BUG(); | ||
} | ||
} | ||
|
||
void __init read_persistent_wall_and_boot_offset( | ||
struct timespec64 *wall_time, struct timespec64 *boot_offset) | ||
{ | ||
uint64_t ret, tsc; | ||
struct timespec64 wallclock; | ||
|
||
ret = hypercall_vclock_op__get_guest_wallclock( | ||
&wallclock.tv_sec, &wallclock.tv_nsec, &tsc); | ||
if (ret != SUCCESS) { | ||
pr_err("hypercall_vclock_op__get_wallclock failed"); | ||
BUG(); | ||
} | ||
|
||
*wall_time = wallclock; | ||
*boot_offset = ns_to_timespec64(tsc_to_nsec(tsc - g_tsc_offset)); | ||
} | ||
|
||
void __init boxy_vclock_init(void) | ||
{ | ||
g_tsc_freq_khz = hypercall_vclock_op__get_tsc_freq_khz(); | ||
if (g_tsc_freq_khz == FAILURE) { | ||
pr_err("hypercall_vclock_op__get_tsc_freq_khz failed"); | ||
BUG(); | ||
} | ||
|
||
pv_ops.time.sched_clock = boxy_sched_clock; | ||
pv_ops.time.steal_clock = boxy_steal_clock; | ||
|
||
x86_init.timers.setup_percpu_clockev = boxy_setup_percpu_clockev; | ||
x86_init.timers.timer_init = x86_init_noop; | ||
x86_init.timers.wallclock_init = wallclock_init; | ||
|
||
x86_platform.calibrate_tsc = tsc_freq_khz; | ||
x86_platform.calibrate_cpu = tsc_freq_khz; | ||
|
||
g_tsc_offset = rdtsc_ordered(); | ||
clocksource_register_khz(&boxy_clocksource, g_tsc_freq_khz); | ||
|
||
setup_force_cpu_cap(X86_FEATURE_NONSTOP_TSC); | ||
setup_force_cpu_cap(X86_FEATURE_CONSTANT_TSC); | ||
setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE); | ||
setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); | ||
} |
Oops, something went wrong.