This patch contains a few general comments in the source code of the
arch subsystem.
Signed-off-by: Claudio Scordino <
cla...@evidence.eu.com>
---
hypervisor/arch/arm/entry.S | 8 +++++++-
hypervisor/arch/arm/include/asm/percpu.h | 3 +++
hypervisor/arch/x86/apic.c | 30 +++++++++++++++++++++++++++++-
hypervisor/arch/x86/control.c | 13 +++++++++++++
hypervisor/arch/x86/entry.S | 8 +++++++-
hypervisor/arch/x86/include/asm/cell.h | 2 +-
hypervisor/arch/x86/setup.c | 4 ++++
hypervisor/arch/x86/vmx.c | 8 ++++++++
8 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/hypervisor/arch/arm/entry.S b/hypervisor/arch/arm/entry.S
index 8c53edc..c990e0a 100644
--- a/hypervisor/arch/arm/entry.S
+++ b/hypervisor/arch/arm/entry.S
@@ -15,7 +15,13 @@
#include <asm/percpu.h>
#include <asm/processor.h>
-/* Entry point for Linux loader module on JAILHOUSE_ENABLE */
+/**
+ * Entry point for Linux loader module on JAILHOUSE_ENABLE.
+ *
+ * This code locates the per_cpu region for a given cpu_id, stores the Linux
+ * stack pointer and cpu_id in it, sets the Jailhouse stack, and calls the
+ * architecture-independent entry() function, passing it a pointer to cpu_data.
+ */
.text
.globl arch_entry
arch_entry:
diff --git a/hypervisor/arch/arm/include/asm/percpu.h b/hypervisor/arch/arm/include/asm/percpu.h
index 97f902e..4d39ffa 100644
--- a/hypervisor/arch/arm/include/asm/percpu.h
+++ b/hypervisor/arch/arm/include/asm/percpu.h
@@ -31,7 +31,10 @@
(BITS_PER_LONG - __builtin_clzl(sizeof(struct per_cpu) - 1))
struct per_cpu {
+ /** Stack used while in hypervisor mode. */
u8 stack[PAGE_SIZE];
+
+ /** Linux stack pointer, used for handover to hypervisor. */
unsigned long linux_sp;
unsigned long linux_ret;
unsigned long linux_flags;
diff --git a/hypervisor/arch/x86/apic.c b/hypervisor/arch/x86/apic.c
index e37ecda..c1439f0 100644
--- a/hypervisor/arch/x86/apic.c
+++ b/hypervisor/arch/x86/apic.c
@@ -23,7 +23,29 @@
#define XAPIC_REG(x2apic_reg) ((x2apic_reg) << 4)
+ /**
+ * Modern x86 processors are equipped with a "local advanced programmable
+ * interrupt controller" (LAPIC) that handles delivery of inter-processor
+ * interrupts (IPIs) as well as external interrupts that the I/O APIC, which
+ * is part of the system's chipset, generates.
+ *
+ * The LAPIC can work in two modes
+ * - xAPIC: programmed via memory mapped I/O (MMIO)
+ * - x2APIC: programmed throughs model-specific registers (MSRs) and
+ * backward-compatible with xAPIC.
+ *
+ * Currently, Jailhouse virtualizes the LAPIC only, while moderates access to
+ * the I/O APIC.
+ */
+
bool using_x2apic;
+
+/**
+ * Mapping from the apic_id to the cpu_id.
+ * Used to enforce isolation by preventing a CPU from sending an IPI to a CPU
+ * which is not in its own CPU set.
+ * @note See apic_send_ipi()
+ */
u8 apic_to_cpu_id[] = { [0 ... APIC_MAX_PHYS_ID] = CPU_ID_INVALID };
/* Initialized for x2APIC, adjusted for xAPIC during init */
@@ -54,7 +76,7 @@ static struct {
u32 (*read)(unsigned int reg);
u32 (*read_id)(void);
void (*write)(unsigned int reg, u32 val);
- void (*send_ipi)(u32 apic_id, u32 icr_lo);
+ void (*send_ipi)(u32 apic_id, u32 icr_lo); /* send_x*apic_ipi() */
} apic_ops;
static u32 read_xapic(unsigned int reg)
@@ -163,13 +185,16 @@ int apic_init(void)
unsigned long apicbase = read_msr(MSR_IA32_APICBASE);
int err;
+ /* Check if x2APIC mode of LAPIC is enabled: */
if (apicbase & APIC_BASE_EXTD) {
+ /* x2APIC mode: */
apic_ops.read = read_x2apic;
apic_ops.read_id = read_x2apic_id;
apic_ops.write = write_x2apic;
apic_ops.send_ipi = send_x2apic_ipi;
using_x2apic = true;
} else if (apicbase & APIC_BASE_EN) {
+ /* xAPIC mode: */
xapic_page = page_alloc(&remap_pool, 1);
if (!xapic_page)
return trace_error(-ENOMEM);
@@ -359,6 +384,9 @@ static bool apic_valid_ipi_mode(u32 lo_val)
return true;
}
+/**
+ * Send an IPI only if the recipient is in the sender's CPU set.
+ */
static void apic_send_ipi(unsigned int target_cpu_id, u32 orig_icr_hi,
u32 icr_lo)
{
diff --git a/hypervisor/arch/x86/control.c b/hypervisor/arch/x86/control.c
index 46bf2cb..376b8be 100644
--- a/hypervisor/arch/x86/control.c
+++ b/hypervisor/arch/x86/control.c
@@ -148,6 +148,17 @@ void arch_shutdown(void)
ioapic_shutdown();
}
+/**
+ * CPU suspension is used during maintenance operations on one of the
+ * cells. In that case, a specific CPU of the root cell issues a request to
+ * suspend all CPUs of some cell, the root cell or some non-root cell.
+ * Suspension works by sending those CPUs (in case of the root cell, the
+ * caller CPU is excluded) a maintenance signal (NMI on x86, SGI_EVENT on
+ * ARM) and sets a per-CPU flag to trigger the suspension. The target CPU
+ * will leave the guest and handle that request in the event loop
+ * (x86_check_events, check_events). Suspension is a simple busy-wait loop
+ * in hypervisor mode, because it usually doesn't take very long.
+ */
void arch_suspend_cpu(unsigned int cpu_id)
{
struct per_cpu *target_data = per_cpu(cpu_id);
@@ -161,8 +172,10 @@ void arch_suspend_cpu(unsigned int cpu_id)
spin_unlock(&target_data->control_lock);
if (!target_suspended) {
+ /* Signal a NMI to each CPU */
apic_send_nmi_ipi(target_data);
+ /* Wait for target_data->cpu_suspend...*/
while (!target_data->cpu_suspended)
cpu_relax();
}
diff --git a/hypervisor/arch/x86/entry.S b/hypervisor/arch/x86/entry.S
index 986bfee..08a1029 100644
--- a/hypervisor/arch/x86/entry.S
+++ b/hypervisor/arch/x86/entry.S
@@ -12,7 +12,13 @@
#include <asm/asm-defines.h>
-/* Entry point for Linux loader module on JAILHOUSE_ENABLE */
+/**
+ * Entry point for Linux loader module on JAILHOUSE_ENABLE.
+ *
+ * This code locates the per_cpu region for a given cpu_id, stores the Linux
+ * stack pointer and cpu_id in it, sets the Jailhouse stack, and calls the
+ * architecture-independent entry() function, passing it a pointer to cpu_data.
+ */
.text
.globl arch_entry
arch_entry:
diff --git a/hypervisor/arch/x86/include/asm/cell.h b/hypervisor/arch/x86/include/asm/cell.h
index 2223532..ba38121 100644
--- a/hypervisor/arch/x86/include/asm/cell.h
+++ b/hypervisor/arch/x86/include/asm/cell.h
@@ -54,7 +54,7 @@ struct arch_cell {
/** Shadow value of PCI config space address port register. */
u32 pci_addr_port_val;
- /** List of IOAPICs assigned to this cell. */
+ /** List of IOAPICs assigned to this cell (Jailhouse only virtualizes LAPICs). */
struct cell_ioapic *ioapics;
/** Number of assigned IOAPICs. */
unsigned int num_ioapics;
diff --git a/hypervisor/arch/x86/setup.c b/hypervisor/arch/x86/setup.c
index 8234167..0e13c8a 100644
--- a/hypervisor/arch/x86/setup.c
+++ b/hypervisor/arch/x86/setup.c
@@ -50,6 +50,9 @@ static void set_idt_int_gate(unsigned int vector, unsigned long entry)
idt[vector * 4 + 2] = entry >> 32;
}
+/*
+ * Initialize APIC and create Jailhouse's Interrupt Descriptor Table (IDT)
+ */
int arch_init_early(void)
{
unsigned long entry;
@@ -203,6 +206,7 @@ int arch_cpu_init(struct per_cpu *cpu_data)
if (err)
goto error_out;
+ /* Configure Virtual Machine Extensions (VMX) for the CPU */
err = vcpu_init(cpu_data);
if (err)
goto error_out;
diff --git a/hypervisor/arch/x86/vmx.c b/hypervisor/arch/x86/vmx.c
index f832612..f8fa704 100644
--- a/hypervisor/arch/x86/vmx.c
+++ b/hypervisor/arch/x86/vmx.c
@@ -78,7 +78,10 @@ static u8 __attribute__((aligned(PAGE_SIZE))) msr_bitmap[][0x2000/8] = {
[ 0/8 ... 0x1fff/8 ] = 0,
},
};
+
+/* Special access page to trap guest's attempts of accessing LAPIC in xAPIC mode */
static u8 __attribute__((aligned(PAGE_SIZE))) apic_access_page[PAGE_SIZE];
+
static struct paging ept_paging[EPT_PAGE_DIR_LEVELS];
static u32 secondary_exec_addon;
static unsigned long cr_maybe1[2], cr_required1[2];
@@ -339,6 +342,8 @@ int vcpu_vendor_cell_init(struct cell *cell)
cell->arch.vmx.ept_structs.root_table =
(page_table_t)cell->arch.root_table_page;
+ /* Map the special LAPIC access page into the guest's physical address
+ * space at XAPIC_BASE */
err = paging_create(&cell->arch.vmx.ept_structs,
paging_hvirt2phys(apic_access_page),
PAGE_SIZE, XAPIC_BASE,
@@ -511,6 +516,8 @@ static bool vmcs_setup(struct per_cpu *cpu_data)
ok &= vmcs_write64(HOST_RSP, (unsigned long)cpu_data->stack +
sizeof(cpu_data->stack));
+
+ /* Function executed when trapping to the hypervisor */
ok &= vmcs_write64(HOST_RIP, (unsigned long)vmx_vmexit);
ok &= vmx_set_guest_cr(CR0_IDX, cpu_data->linux_cr0);
@@ -672,6 +679,7 @@ int vcpu_init(struct per_cpu *cpu_data)
cpu_data->vmx_state = VMXON;
+ /* Prepare the Virtual Machine Control Structure (VMCS) */
if (!vmcs_clear(cpu_data) ||
!vmcs_load(cpu_data) ||
!vmcs_setup(cpu_data))
--
2.7.4