Skip to content

Commit

Permalink
Merge pull request #1 from rianquinn/suspend_boxy_support
Browse files Browse the repository at this point in the history
Add vClock/vIRQ support
  • Loading branch information
rianquinn authored Jun 29, 2019
2 parents 957d0ed + 986452a commit b8300b3
Show file tree
Hide file tree
Showing 14 changed files with 678 additions and 2 deletions.
2 changes: 2 additions & 0 deletions arch/x86/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ obj-$(CONFIG_XEN) += xen/
# Hyper-V paravirtualization support
obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/

obj-$(CONFIG_BOXY) += boxy/

obj-y += realmode/
obj-y += kernel/
obj-y += mm/
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,8 @@ config QUEUED_LOCK_STAT
behavior of paravirtualized queued spinlocks and report
them on debugfs.

source "arch/x86/boxy/Kconfig"

source "arch/x86/xen/Kconfig"

config KVM_GUEST
Expand Down
13 changes: 13 additions & 0 deletions arch/x86/boxy/Kconfig
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.
1 change: 1 addition & 0 deletions arch/x86/boxy/Makefile
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
84 changes: 84 additions & 0 deletions arch/x86/boxy/init.c
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,
};
49 changes: 49 additions & 0 deletions arch/x86/boxy/quirk.c
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("", "");
}
181 changes: 181 additions & 0 deletions arch/x86/boxy/vclock.c
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);
}
Loading

0 comments on commit b8300b3

Please sign in to comment.