Skip to content

Commit b8300b3

Browse files
authored
Merge pull request #1 from rianquinn/suspend_boxy_support
Add vClock/vIRQ support
2 parents 957d0ed + 986452a commit b8300b3

File tree

14 files changed

+678
-2
lines changed

14 files changed

+678
-2
lines changed

arch/x86/Kbuild

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ obj-$(CONFIG_XEN) += xen/
1010
# Hyper-V paravirtualization support
1111
obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/
1212

13+
obj-$(CONFIG_BOXY) += boxy/
14+
1315
obj-y += realmode/
1416
obj-y += kernel/
1517
obj-y += mm/

arch/x86/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,8 @@ config QUEUED_LOCK_STAT
782782
behavior of paravirtualized queued spinlocks and report
783783
them on debugfs.
784784

785+
source "arch/x86/boxy/Kconfig"
786+
785787
source "arch/x86/xen/Kconfig"
786788

787789
config KVM_GUEST

arch/x86/boxy/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
# This Kconfig describes Boxy options
4+
#
5+
6+
config BOXY
7+
bool "Boxy guest support"
8+
depends on PARAVIRT && X86_64
9+
---help---
10+
This option allows you to run Linux as a Boxy guest virtual machine.
11+
Boxy requires a very specific kernel config so in general, this
12+
option should not be manually selected and instead is enabled by the
13+
Boxy build system with the proper config in place.

arch/x86/boxy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
obj-y := init.o virq.o vclock.o vmcall.o quirk.o

arch/x86/boxy/init.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Copyright (C) 2019 Assured Information Security, Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
*/
18+
19+
#include <asm/boxy.h>
20+
#include <asm/i8259.h>
21+
22+
static uint32_t __init boxy_detect(void)
23+
{
24+
uint32_t eax;
25+
uint32_t ignore[3];
26+
27+
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR))
28+
return 0;
29+
30+
cpuid(CPUID_BAREFLANK_SYN, &eax, &ignore[0], &ignore[1], &ignore[2]);
31+
32+
/**
33+
* TODO:
34+
*
35+
* We need to add a Boxy specific CPUID leaf at 0x40000000 that acks like
36+
* VMWare and HyperV so that we play nice with nested virtualization.
37+
* More importantly, right now we are acking with Bareflank and not
38+
* Boxy, so this code could end up detecting someone elses hypervisor.
39+
*/
40+
41+
/**
42+
* TODO:
43+
*
44+
* We need to implement versioning to ensure that we are using a guest
45+
* that actually knows how to talk to the hypervisor.
46+
*/
47+
48+
if (eax == CPUID_BAREFLANK_ACK)
49+
return 1;
50+
51+
return 0;
52+
}
53+
54+
static void __init boxy_init_platform(void)
55+
{
56+
pv_info.name = "Boxy Hypervisor";
57+
58+
boxy_virq_init();
59+
boxy_vclock_init();
60+
61+
x86_init.resources.probe_roms = x86_init_noop;
62+
x86_init.mpparse.find_smp_config = x86_init_noop;
63+
x86_init.mpparse.get_smp_config = boxy_apic_quirk;
64+
x86_init.irqs.pre_vector_init = x86_init_noop;
65+
x86_init.oem.arch_setup = x86_init_noop;
66+
x86_init.oem.banner = x86_init_noop;
67+
68+
x86_platform.legacy.rtc = 0;
69+
x86_platform.legacy.warm_reset = 0;
70+
x86_platform.legacy.i8042 = X86_LEGACY_I8042_PLATFORM_ABSENT;
71+
72+
legacy_pic = &null_legacy_pic;
73+
}
74+
75+
static bool __init boxy_x2apic_available(void)
76+
{ return true; }
77+
78+
const __initconst struct hypervisor_x86 x86_hyper_boxy = {
79+
.name = "Boxy Hypervisor",
80+
.detect = boxy_detect,
81+
.type = X86_HYPER_BOXY,
82+
.init.init_platform = boxy_init_platform,
83+
.init.x2apic_available = boxy_x2apic_available,
84+
};

arch/x86/boxy/quirk.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright (C) 2019 Assured Information Security, Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
*/
18+
19+
#include <asm/boxy.h>
20+
#include <asm/apic.h>
21+
#include <linux/init.h>
22+
23+
/**
24+
* Quirk Notes
25+
*
26+
* Like Jailhouse, we require an x2apic, and currently, if we don't set the
27+
* code below, the kernel will crash as it attempts to read an x2apic
28+
* register before the apic variable is set. The code below ensures that
29+
* we end up with symmetric IO mode with a physical x2apic while also setting
30+
* the apic variable so that the kernel doesn't segfault.
31+
*
32+
* If you enable ACPI, this bug will go away as ACPI happens to call the
33+
* default_acpi_madt_oem_check() function which sets the apic variable in the
34+
* kernel before the init_apic_mappings() function is called. The crash
35+
* occurs here:
36+
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/apic/apic.c#L1969
37+
*
38+
* Since we are calling the default_acpi_madt_oem_check() function manually,
39+
* we need to ensure that the apic is configured properly for Boxy guests
40+
* which includes a physical x2apic and symmetric IO mode.
41+
*/
42+
43+
void __init boxy_apic_quirk(unsigned int early)
44+
{
45+
x2apic_phys = 1;
46+
smp_found_config = 1;
47+
48+
default_acpi_madt_oem_check("", "");
49+
}

arch/x86/boxy/vclock.c

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* Copyright (C) 2019 Assured Information Security, Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
*/
18+
19+
#include <asm/boxy.h>
20+
#include <linux/time64.h>
21+
#include <linux/clocksource.h>
22+
#include <linux/clockchips.h>
23+
24+
static uint64_t g_tsc_offset = 0;
25+
static uint64_t g_tsc_freq_khz = 0;
26+
27+
/******************************************************************************/
28+
/* helpers */
29+
/******************************************************************************/
30+
31+
static uint64_t mul_div(uint64_t x, uint64_t n, uint64_t d)
32+
{ return ((x / d) * n) + (((x % d) * n) / d); }
33+
34+
static uint64_t tsc_to_nsec(uint64_t tsc)
35+
{ return mul_div(tsc, 1000000, g_tsc_freq_khz); }
36+
37+
/******************************************************************************/
38+
/* clock source */
39+
/******************************************************************************/
40+
41+
static u64 boxy_clocksource_read(struct clocksource *cs)
42+
{ return rdtsc_ordered() - g_tsc_offset; }
43+
44+
static struct clocksource boxy_clocksource = {
45+
.name = "boxy-clocksource",
46+
.read = boxy_clocksource_read,
47+
.rating = 500,
48+
.mask = CLOCKSOURCE_MASK(64),
49+
.flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES,
50+
.archdata.vclock_mode = VCLOCK_TSC
51+
};
52+
53+
/******************************************************************************/
54+
/* clock event */
55+
/******************************************************************************/
56+
57+
static int boxy_set_next_event(
58+
unsigned long delta, struct clock_event_device *evt)
59+
{
60+
if (hypercall_vclock_op__set_next_event(delta) != SUCCESS) {
61+
pr_err("hypercall_vclock_op__set_next_event failed");
62+
BUG();
63+
}
64+
65+
return 0;
66+
}
67+
68+
static struct clock_event_device boxy_clock_event_device = {
69+
.name = "boxy-clock-event-device",
70+
.features = CLOCK_EVT_FEAT_ONESHOT,
71+
.mult = 1,
72+
.shift = 0,
73+
.rating = 500,
74+
.set_next_event = boxy_set_next_event,
75+
};
76+
77+
void boxy_vclock_event_handler(void)
78+
{ boxy_clock_event_device.event_handler(&boxy_clock_event_device); }
79+
80+
static void __init boxy_setup_percpu_clockev(void)
81+
{
82+
boxy_clock_event_device.cpumask = cpumask_of(smp_processor_id());
83+
84+
clockevents_config_and_register(
85+
&boxy_clock_event_device, g_tsc_freq_khz * 1000, 0, ~0UL);
86+
}
87+
88+
/******************************************************************************/
89+
/* pv_ops */
90+
/******************************************************************************/
91+
92+
static u64 boxy_sched_clock(void)
93+
{ return tsc_to_nsec(boxy_clocksource_read(0)); }
94+
95+
static u64 boxy_steal_clock(int cpu)
96+
{
97+
/**
98+
* Note:
99+
*
100+
* For now we do not support the steal clock. Timekeeping seems to work
101+
* fine without it, and implementing this would require not only an
102+
* additional VMCall on every sched_clock() call, but it would also
103+
* require the hypervisor to perform time keeping on every exit and
104+
* entry to account for the time that the VM is actually executing.
105+
*/
106+
107+
return 0;
108+
}
109+
110+
/******************************************************************************/
111+
/* x86_platform_ops */
112+
/******************************************************************************/
113+
114+
static unsigned long tsc_freq_khz(void)
115+
{ return g_tsc_freq_khz; }
116+
117+
/******************************************************************************/
118+
/* init functions */
119+
/******************************************************************************/
120+
121+
static void wallclock_init(void)
122+
{
123+
if (hypercall_vclock_op__reset_host_wallclock() != SUCCESS) {
124+
pr_err("hypercall_vclock_op__reset_host_wallclock failed");
125+
BUG();
126+
}
127+
128+
if (hypercall_vclock_op__set_guest_wallclock_rtc() != SUCCESS) {
129+
pr_err("hypercall_vclock_op__set_guest_wallclock_rtc failed");
130+
BUG();
131+
}
132+
133+
if (hypercall_vclock_op__set_guest_wallclock_tsc() != SUCCESS) {
134+
pr_err("hypercall_vclock_op__set_guest_wallclock_tsc failed");
135+
BUG();
136+
}
137+
}
138+
139+
void __init read_persistent_wall_and_boot_offset(
140+
struct timespec64 *wall_time, struct timespec64 *boot_offset)
141+
{
142+
uint64_t ret, tsc;
143+
struct timespec64 wallclock;
144+
145+
ret = hypercall_vclock_op__get_guest_wallclock(
146+
&wallclock.tv_sec, &wallclock.tv_nsec, &tsc);
147+
if (ret != SUCCESS) {
148+
pr_err("hypercall_vclock_op__get_wallclock failed");
149+
BUG();
150+
}
151+
152+
*wall_time = wallclock;
153+
*boot_offset = ns_to_timespec64(tsc_to_nsec(tsc - g_tsc_offset));
154+
}
155+
156+
void __init boxy_vclock_init(void)
157+
{
158+
g_tsc_freq_khz = hypercall_vclock_op__get_tsc_freq_khz();
159+
if (g_tsc_freq_khz == FAILURE) {
160+
pr_err("hypercall_vclock_op__get_tsc_freq_khz failed");
161+
BUG();
162+
}
163+
164+
pv_ops.time.sched_clock = boxy_sched_clock;
165+
pv_ops.time.steal_clock = boxy_steal_clock;
166+
167+
x86_init.timers.setup_percpu_clockev = boxy_setup_percpu_clockev;
168+
x86_init.timers.timer_init = x86_init_noop;
169+
x86_init.timers.wallclock_init = wallclock_init;
170+
171+
x86_platform.calibrate_tsc = tsc_freq_khz;
172+
x86_platform.calibrate_cpu = tsc_freq_khz;
173+
174+
g_tsc_offset = rdtsc_ordered();
175+
clocksource_register_khz(&boxy_clocksource, g_tsc_freq_khz);
176+
177+
setup_force_cpu_cap(X86_FEATURE_NONSTOP_TSC);
178+
setup_force_cpu_cap(X86_FEATURE_CONSTANT_TSC);
179+
setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
180+
setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
181+
}

0 commit comments

Comments
 (0)