Skip to content

Commit

Permalink
libsgxstep: support x2apic mode
Browse files Browse the repository at this point in the history
Some recent CPUs with LEGACY_XAPIC_DISABLED only support x2apic mode with SGX.
In x2apic mode all APIC configuration needs to go through ring-0 RD/WRMSR
instructions, which is also the only access mode supported for
IA32_TSC_DEADLINE.

cf issue #72
  • Loading branch information
jovanbulck committed Sep 17, 2024
1 parent d9064df commit 513c622
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 157 deletions.
91 changes: 61 additions & 30 deletions libsgxstep/apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,65 @@
#include "apic.h"
#include "pt.h"
#include "cpu.h"
#include "idt.h"
#include "sched.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include "../kernel/sgxstep_ioctl.h"

extern void *apic_base;
void *dummy_pt = NULL;
int g_apic_setup = 0;
uint64_t g_apic_deadline_tsc_begin = -1;

#if !X2APIC
extern void *apic_base;

/*
* Code below maps APIC timer MMIO registers in user space.
*
* NOTE: we require xAPIC mode, since "In x2APIC mode, the memory mapped
* interface is not available and any access to the MMIO interface will behave
* similar to that of a legacy xAPIC in globally disabled state" (Intel SDM
* 10.12.2).
*
* Advised Linux kernel parameters are: "nox2apic iomem=relaxed no_timer_check"
*/
static void do_apic_init(void)
{
uintptr_t apic_base_addr = 0x0;
#if APIC_CONFIG_MSR
uint64_t apic_base_msr = 0x0;
rdmsr_on_cpu(IA32_APIC_BASE_MSR, get_cpu(), &apic_base_msr);
ASSERT( (apic_base_msr & APIC_BASE_MSR_ENABLE) );
ASSERT( !(apic_base_msr & APIC_BASE_MSR_X2APIC) );
apic_base_addr = apic_base_msr & ~APIC_BASE_ADDR_MASK;
#else
apic_base_addr = APIC_BASE;
#endif

apic_base = remap(apic_base_addr);
libsgxstep_info("established local memory mapping for APIC_BASE=%p at %p", (void*) apic_base_addr, apic_base);
}
#else /* X2APIC */
/* See irq_entry.S to see how these are used. */
void __wrmsr_gate(void);
void __rdmsr_gate(void);

/*
* Install custom ring-0 IRQ gates to read/write privileged X2APIC MSR registers.
*/
static void do_apic_init(void)
{
install_priv_gate(__wrmsr_gate, WRMSR_GATE_VECTOR);
install_priv_gate(__rdmsr_gate, RDMSR_GATE_VECTOR);
}
#endif /* X2APIC */

/*
* Code below maps APIC timer MMIO registers in user space.
*
* NOTE: we require xAPIC mode, since "In x2APIC mode, the memory mapped
* interface is not available and any access to the MMIO interface will behave
* similar to that of a legacy xAPIC in globally disabled state" (Intel SDM
* 10.12.2).
*
* Advised Linux kernel parameters are: "nox2apic iomem=relaxed no_timer_check"
*/
void apic_init(void)
{
if (apic_base) return;
if (g_apic_setup) return;

uintptr_t apic_base_addr = 0x0;
#if APIC_CONFIG_MSR
uint64_t apic_base_msr = 0x0;
rdmsr_on_cpu(IA32_APIC_BASE_MSR, get_cpu(), &apic_base_msr);
ASSERT( (apic_base_msr & APIC_BASE_MSR_ENABLE) );
ASSERT( !(apic_base_msr & APIC_BASE_MSR_X2APIC) );
apic_base_addr = apic_base_msr & ~APIC_BASE_ADDR_MASK;
#else
apic_base_addr = APIC_BASE;
#endif

apic_base = remap(apic_base_addr);
libsgxstep_info("established local memory mapping for APIC_BASE=%p at %p", (void*) apic_base_addr, apic_base);
do_apic_init();
g_apic_setup = 1;

libsgxstep_info("APIC_ID=%#x; LVTT=%#x; TDCR=%#x", apic_id(),
apic_read(APIC_LVTT), apic_read(APIC_TDCR));
Expand All @@ -65,7 +89,10 @@ void apic_init(void)
uint8_t apic_id(void)
{
uint32_t id = apic_read(APIC_ID);
id = (id & APIC_ID_MASK) >> APIC_ID_SHIFT;
/* SDM: Figure 11-6. Local APIC ID Register */
#if !X2APIC
id = (id & APIC_ID_MASK) >> APIC_ID_SHIFT;
#endif
return (uint8_t) id;
}

Expand All @@ -88,10 +115,14 @@ int apic_timer_deadline(uint8_t vector)

/* In xAPIC mode the memory-mapped write to LVTT needs to be serialized. */
asm volatile("mfence" : : : "memory");

libsgxstep_info("APIC timer tsc-deadline mode (lvtt=%x/tdcr=%x)",
apic_read(APIC_LVTT), apic_read(APIC_TDCR));
}

void apic_timer_deadline_irq(int tsc_diff)
void apic_timer_deadline_irq(int tsc_offset)
{
uint64_t now = rdtsc_begin();
wrmsr_on_cpu(IA32_TSC_DEADLINE_MSR, get_cpu(), now + tsc_diff);
g_apic_deadline_tsc_begin = rdtsc_begin();
/* NOTE: don't use apic_write here as this is a 64-bit MSR */
wrmsr(IA32_TSC_DEADLINE_MSR, g_apic_deadline_tsc_begin + tsc_offset);
}
162 changes: 91 additions & 71 deletions libsgxstep/apic.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,84 +22,104 @@
#define SGX_STEP_APIC_H

#include "debug.h"
#include "sched.h"
#include "cpu.h"
#include "config.h"
#include <stdint.h>

#if APIC_CONFIG_MSR
#define APIC_BASE_MSR_X2APIC 0x400
#define APIC_BASE_MSR_ENABLE 0x800
#define APIC_BASE_ADDR_MASK 0xfff
#else
#define APIC_BASE 0xfee00000
#endif

#define APIC_ICR_LOW 0x300
#define APIC_ICR_HIGH 0x310

#define APIC_LVTT 0x320
#define APIC_TDCR 0x3e0
#define APIC_TMICT 0x380
#define APIC_TMCCT 0x390

#define APIC_ID 0x20
#define APIC_ID_SHIFT 24
#define APIC_ID_MASK (0xff << APIC_ID_SHIFT)

#define APIC_EOI 0xb0

#define APIC_TPR 0x80
#define APIC_PPR 0xa0

#define APIC_TDR_DIV_1 0xb
#define APIC_TDR_DIV_2 0x0
#define APIC_LVTT_ONESHOT (0 << 17)
#define APIC_LVTT_DEADLINE (2 << 17)

#define APIC_IPI_CFG 0xc08f1

#define APIC_ICR_VECTOR(n) (n & 0xFF)
#define APIC_ICR_DELIVERY_FIXED (0x0 << 8)
#define APIC_ICR_LEVEL_ASSERT (0x1 << 14)
#define APIC_ICR_DEST_SELF (0x1 << 18)
#define APIC_ICR_DEST_PHYSICAL (0x0 << 11)
#define APIC_ICR_DEST_LOGICAL (0x1 << 11)
#define APIC_ICR_DEST_MASK 0xff000000

extern void* apic_base;
extern uint32_t apic_lvtt;
/* APIC MMIO/MSR register address map (cf. SDM Table 11.6) */
#if !X2APIC
#if APIC_CONFIG_MSR
#define APIC_BASE_MSR_X2APIC 0x400
#define APIC_BASE_MSR_ENABLE 0x800
#define APIC_BASE_ADDR_MASK 0xfff
#else
#define APIC_BASE 0xfee00000
#endif

#define APIC_ID 0x20
#define APIC_EOI 0xb0
#define APIC_ICR_LOW 0x300
#define APIC_ICR_HIGH 0x310
#define APIC_LVTT 0x320
#define APIC_TMICT 0x380
#define APIC_TDCR 0x3e0
#else /* X2APIC */
#define APIC_ID 0x802
#define APIC_EOI 0x80b
#define APIC_ICR 0x830
#define APIC_LVTT 0x832
#define APIC_TMICT 0x838
#define APIC_TDCR 0x83e
#endif /* X2APIC */

#define APIC_ID_SHIFT 24
#define APIC_ID_MASK (0xff << APIC_ID_SHIFT)
#define APIC_TDR_DIV_1 0xb
#define APIC_TDR_DIV_2 0x0
#define APIC_LVTT_ONESHOT (0 << 17)
#define APIC_LVTT_DEADLINE (2 << 17)

#define APIC_ICR_VECTOR(n) (n & 0xFF)
#define APIC_ICR_DELIVERY_FIXED (0x0 << 8)
#define APIC_ICR_LEVEL_ASSERT (0x1 << 14)
#define APIC_ICR_DEST_SELF (0x1 << 18)
#define APIC_ICR_DEST_PHYSICAL (0x0 << 11)
#define APIC_ICR_DEST_LOGICAL (0x1 << 11)
#define APIC_ICR_DEST_MASK 0xff000000

extern int g_apic_setup;
extern uint64_t g_apic_deadline_tsc_begin;
void apic_init(void);

/*
* From Linux kernel source: /arch/x86/include/asm/apic.h
* NOTE: Intel SDM: "any access that touches bytes 4 through 15 of an APIC
* register may cause undefined behavior and must not be executed."
*/
static inline int apic_write(uint32_t reg, uint32_t v)
{
volatile uint32_t *addr;
if (!apic_base) apic_init();

addr = (volatile uint32_t *)(apic_base + reg);
__asm__ volatile ("movl %1, %0\n\t"
:"=m"(*addr):"r"(v):);

return 0;
}

static inline uint32_t apic_read(uint32_t reg)
{
if (!apic_base) apic_init();

return *((volatile uint32_t *)(apic_base + reg));
}

//#define apic_send_ipi() apic_write(APIC_ICR_LOW, APIC_IPI_CFG)
#define apic_timer_irq(tsc) apic_write(APIC_TMICT, tsc);
#define apic_send_ipi_self(n) apic_write(APIC_ICR_LOW, APIC_ICR_VECTOR(n) | APIC_ICR_DELIVERY_FIXED | APIC_ICR_LEVEL_ASSERT | APIC_ICR_DEST_SELF)

/* read/write functions for XAPIC in MMIO access mode */
#if !X2APIC
/*
* From Linux kernel source: /arch/x86/include/asm/apic.h
* NOTE: Intel SDM: "any access that touches bytes 4 through 15 of an APIC
* register may cause undefined behavior and must not be executed."
*/
static inline int apic_write(uint32_t reg, uint32_t v)
{
volatile uint32_t *addr;
if (!g_apic_setup) apic_init();

addr = (volatile uint32_t *)(apic_base + reg);
__asm__ volatile ("movl %1, %0\n\t"
:"=m"(*addr):"r"(v):);

return 0;
}

static inline uint32_t apic_read(uint32_t reg)
{
if (!g_apic_setup) apic_init();

return *((volatile uint32_t *)(apic_base + reg));
}
#else /* X2APIC */
static inline int apic_write(uint32_t reg, uint32_t v)
{
if (!g_apic_setup) apic_init();

wrmsr(reg, v);
return 0;
}

static inline uint32_t apic_read(uint32_t reg)
{
if (!g_apic_setup) apic_init();

return rdmsr(reg);
}
#endif /* X2APIC */

#define apic_timer_irq(interval) apic_write(APIC_TMICT, interval);
#define apic_send_ipi_self(n) apic_write(APIC_ICR_LOW, APIC_ICR_VECTOR(n) | APIC_ICR_DELIVERY_FIXED | APIC_ICR_LEVEL_ASSERT | APIC_ICR_DEST_SELF)

uint8_t apic_id(void);
int apic_timer_oneshot(uint8_t vector);
int apic_timer_deadline(uint8_t vector);
void apic_timer_deadline_irq(int tsc_diff);
void apic_timer_deadline_irq(int tsc_offset);

#endif
16 changes: 13 additions & 3 deletions libsgxstep/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,21 @@
#define USER_IDT_ENABLE 1
#define IRQ_VECTOR 45
#define IRQ_PRIV_VECTOR 49
#define RDMSR_GATE_VECTOR 50
#define WRMSR_GATE_VECTOR 51
#define GDT_VECTOR 13
#if (M32 != 1)
#define APIC_CONFIG_MSR 1

/*
* Some recent CPUs with LEGACY_XAPIC_DISABLED only support x2apic mode with
* SGX. In x2apic mode all APIC configuration needs to go through ring-0
* RD/WRMSR instructions, which is also the only access mode supported for
* IA32_TSC_DEADLINE.
*/
#define X2APIC 1
#if (!X2APIC && !M32)
#define APIC_CONFIG_MSR 1
#else
#define APIC_CONFIG_MSR 0
#define APIC_CONFIG_MSR 0
#endif

#define VICTIM_CPU 1
Expand Down
14 changes: 0 additions & 14 deletions libsgxstep/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,3 @@ void clflush(void *p)
: "c" (p)
: "rax");
}

uint64_t rdmsr(uint32_t msr)
{
uint64_t lo, hi;
asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((hi<<32) | lo);
}

void wrmsr(uint32_t msr, uint64_t val)
{
uint32_t hi = (uint32_t) (val>>32);
uint32_t lo = (uint32_t) val;
asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr));
}
Loading

0 comments on commit 513c622

Please sign in to comment.