diff options
Diffstat (limited to 'kvm/user/test/lib/x86')
-rw-r--r-- | kvm/user/test/lib/x86/apic-defs.h | 133 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/apic.c | 143 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/apic.h | 34 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/fake-apic.h | 14 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/fwcfg.c | 40 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/fwcfg.h | 44 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/io.c | 23 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/smp.c | 128 | ||||
-rw-r--r-- | kvm/user/test/lib/x86/smp.h | 17 |
9 files changed, 576 insertions, 0 deletions
diff --git a/kvm/user/test/lib/x86/apic-defs.h b/kvm/user/test/lib/x86/apic-defs.h new file mode 100644 index 000000000..c061e3d4a --- /dev/null +++ b/kvm/user/test/lib/x86/apic-defs.h @@ -0,0 +1,133 @@ +#ifndef _ASM_X86_APICDEF_H +#define _ASM_X86_APICDEF_H + +/* + * Constants for various Intel APICs. (local APIC, IOAPIC, etc.) + * + * Alan Cox <Alan.Cox@linux.org>, 1995. + * Ingo Molnar <mingo@redhat.com>, 1999, 2000 + */ + +#define APIC_DEFAULT_PHYS_BASE 0xfee00000 + +#define APIC_ID 0x20 + +#define APIC_LVR 0x30 +#define APIC_LVR_MASK 0xFF00FF +#define GET_APIC_VERSION(x) ((x) & 0xFFu) +#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu) +#ifdef CONFIG_X86_32 +# define APIC_INTEGRATED(x) ((x) & 0xF0u) +#else +# define APIC_INTEGRATED(x) (1) +#endif +#define APIC_XAPIC(x) ((x) >= 0x14) +#define APIC_TASKPRI 0x80 +#define APIC_TPRI_MASK 0xFFu +#define APIC_ARBPRI 0x90 +#define APIC_ARBPRI_MASK 0xFFu +#define APIC_PROCPRI 0xA0 +#define APIC_EOI 0xB0 +#define APIC_EIO_ACK 0x0 +#define APIC_RRR 0xC0 +#define APIC_LDR 0xD0 +#define APIC_LDR_MASK (0xFFu << 24) +#define GET_APIC_LOGICAL_ID(x) (((x) >> 24) & 0xFFu) +#define SET_APIC_LOGICAL_ID(x) (((x) << 24)) +#define APIC_ALL_CPUS 0xFFu +#define APIC_DFR 0xE0 +#define APIC_DFR_CLUSTER 0x0FFFFFFFul +#define APIC_DFR_FLAT 0xFFFFFFFFul +#define APIC_SPIV 0xF0 +#define APIC_SPIV_FOCUS_DISABLED (1 << 9) +#define APIC_SPIV_APIC_ENABLED (1 << 8) +#define APIC_ISR 0x100 +#define APIC_ISR_NR 0x8 /* Number of 32 bit ISR registers. */ +#define APIC_TMR 0x180 +#define APIC_IRR 0x200 +#define APIC_ESR 0x280 +#define APIC_ESR_SEND_CS 0x00001 +#define APIC_ESR_RECV_CS 0x00002 +#define APIC_ESR_SEND_ACC 0x00004 +#define APIC_ESR_RECV_ACC 0x00008 +#define APIC_ESR_SENDILL 0x00020 +#define APIC_ESR_RECVILL 0x00040 +#define APIC_ESR_ILLREGA 0x00080 +#define APIC_ICR 0x300 +#define APIC_DEST_SELF 0x40000 +#define APIC_DEST_ALLINC 0x80000 +#define APIC_DEST_ALLBUT 0xC0000 +#define APIC_ICR_RR_MASK 0x30000 +#define APIC_ICR_RR_INVALID 0x00000 +#define APIC_ICR_RR_INPROG 0x10000 +#define APIC_ICR_RR_VALID 0x20000 +#define APIC_INT_LEVELTRIG 0x08000 +#define APIC_INT_ASSERT 0x04000 +#define APIC_ICR_BUSY 0x01000 +#define APIC_DEST_LOGICAL 0x00800 +#define APIC_DEST_PHYSICAL 0x00000 +#define APIC_DM_FIXED 0x00000 +#define APIC_DM_LOWEST 0x00100 +#define APIC_DM_SMI 0x00200 +#define APIC_DM_REMRD 0x00300 +#define APIC_DM_NMI 0x00400 +#define APIC_DM_INIT 0x00500 +#define APIC_DM_STARTUP 0x00600 +#define APIC_DM_EXTINT 0x00700 +#define APIC_VECTOR_MASK 0x000FF +#define APIC_ICR2 0x310 +#define GET_APIC_DEST_FIELD(x) (((x) >> 24) & 0xFF) +#define SET_APIC_DEST_FIELD(x) ((x) << 24) +#define APIC_LVTT 0x320 +#define APIC_LVTTHMR 0x330 +#define APIC_LVTPC 0x340 +#define APIC_LVT0 0x350 +#define APIC_LVT_TIMER_BASE_MASK (0x3 << 18) +#define GET_APIC_TIMER_BASE(x) (((x) >> 18) & 0x3) +#define SET_APIC_TIMER_BASE(x) (((x) << 18)) +#define APIC_TIMER_BASE_CLKIN 0x0 +#define APIC_TIMER_BASE_TMBASE 0x1 +#define APIC_TIMER_BASE_DIV 0x2 +#define APIC_LVT_TIMER_PERIODIC (1 << 17) +#define APIC_LVT_MASKED (1 << 16) +#define APIC_LVT_LEVEL_TRIGGER (1 << 15) +#define APIC_LVT_REMOTE_IRR (1 << 14) +#define APIC_INPUT_POLARITY (1 << 13) +#define APIC_SEND_PENDING (1 << 12) +#define APIC_MODE_MASK 0x700 +#define GET_APIC_DELIVERY_MODE(x) (((x) >> 8) & 0x7) +#define SET_APIC_DELIVERY_MODE(x, y) (((x) & ~0x700) | ((y) << 8)) +#define APIC_MODE_FIXED 0x0 +#define APIC_MODE_NMI 0x4 +#define APIC_MODE_EXTINT 0x7 +#define APIC_LVT1 0x360 +#define APIC_LVTERR 0x370 +#define APIC_TMICT 0x380 +#define APIC_TMCCT 0x390 +#define APIC_TDCR 0x3E0 +#define APIC_SELF_IPI 0x3F0 +#define APIC_TDR_DIV_TMBASE (1 << 2) +#define APIC_TDR_DIV_1 0xB +#define APIC_TDR_DIV_2 0x0 +#define APIC_TDR_DIV_4 0x1 +#define APIC_TDR_DIV_8 0x2 +#define APIC_TDR_DIV_16 0x3 +#define APIC_TDR_DIV_32 0x8 +#define APIC_TDR_DIV_64 0x9 +#define APIC_TDR_DIV_128 0xA +#define APIC_EILVT0 0x500 +#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */ +#define APIC_EILVT_NR_AMD_10H 4 +#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF) +#define APIC_EILVT_MSG_FIX 0x0 +#define APIC_EILVT_MSG_SMI 0x2 +#define APIC_EILVT_MSG_NMI 0x4 +#define APIC_EILVT_MSG_EXT 0x7 +#define APIC_EILVT_MASKED (1 << 16) +#define APIC_EILVT1 0x510 +#define APIC_EILVT2 0x520 +#define APIC_EILVT3 0x530 + +#define APIC_BASE_MSR 0x800 + +#endif /* _ASM_X86_APICDEF_H */ diff --git a/kvm/user/test/lib/x86/apic.c b/kvm/user/test/lib/x86/apic.c new file mode 100644 index 000000000..7bb98ed34 --- /dev/null +++ b/kvm/user/test/lib/x86/apic.c @@ -0,0 +1,143 @@ +#include "libcflat.h" +#include "apic.h" + +static void *g_apic = (void *)0xfee00000; +static void *g_ioapic = (void *)0xfec00000; + +struct apic_ops { + u32 (*reg_read)(unsigned reg); + void (*reg_write)(unsigned reg, u32 val); + void (*icr_write)(u32 val, u32 dest); + u32 (*id)(void); +}; + +static void outb(unsigned char data, unsigned short port) +{ + asm volatile ("out %0, %1" : : "a"(data), "d"(port)); +} + +static u32 xapic_read(unsigned reg) +{ + return *(volatile u32 *)(g_apic + reg); +} + +static void xapic_write(unsigned reg, u32 val) +{ + *(volatile u32 *)(g_apic + reg) = val; +} + +static void xapic_icr_write(u32 val, u32 dest) +{ + while (xapic_read(APIC_ICR) & APIC_ICR_BUSY) + ; + xapic_write(APIC_ICR2, dest << 24); + xapic_write(APIC_ICR, val); +} + +static uint32_t xapic_id(void) +{ + return xapic_read(APIC_ID) >> 24; +} + +static const struct apic_ops xapic_ops = { + .reg_read = xapic_read, + .reg_write = xapic_write, + .icr_write = xapic_icr_write, + .id = xapic_id, +}; + +static const struct apic_ops *apic_ops = &xapic_ops; + +static u32 x2apic_read(unsigned reg) +{ + unsigned a, d; + + asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(APIC_BASE_MSR + reg/16)); + return a | (u64)d << 32; +} + +static void x2apic_write(unsigned reg, u32 val) +{ + asm volatile ("wrmsr" : : "a"(val), "d"(0), "c"(APIC_BASE_MSR + reg/16)); +} + +static void x2apic_icr_write(u32 val, u32 dest) +{ + asm volatile ("wrmsr" : : "a"(val), "d"(dest), + "c"(APIC_BASE_MSR + APIC_ICR/16)); +} + +static uint32_t x2apic_id(void) +{ + return xapic_read(APIC_ID); +} + +static const struct apic_ops x2apic_ops = { + .reg_read = x2apic_read, + .reg_write = x2apic_write, + .icr_write = x2apic_icr_write, + .id = x2apic_id, +}; + +u32 apic_read(unsigned reg) +{ + return apic_ops->reg_read(reg); +} + +void apic_write(unsigned reg, u32 val) +{ + apic_ops->reg_write(reg, val); +} + +void apic_icr_write(u32 val, u32 dest) +{ + apic_ops->icr_write(val, dest); +} + +uint32_t apic_id(void) +{ + return apic_ops->id(); +} + +#define MSR_APIC_BASE 0x0000001b + +int enable_x2apic(void) +{ + unsigned a, b, c, d; + + asm ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1)); + + if (c & (1 << 21)) { + asm ("rdmsr" : "=a"(a), "=d"(d) : "c"(MSR_APIC_BASE)); + a |= 1 << 10; + asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_APIC_BASE)); + apic_ops = &x2apic_ops; + return 1; + } else { + return 0; + } +} + +void ioapic_write_reg(unsigned reg, u32 value) +{ + *(volatile u32 *)g_ioapic = reg; + *(volatile u32 *)(g_ioapic + 0x10) = value; +} + +void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e) +{ + ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]); + ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]); +} + +void enable_apic(void) +{ + printf("enabling apic\n"); + xapic_write(0xf0, 0x1ff); /* spurious vector register */ +} + +void mask_pic_interrupts(void) +{ + outb(0xff, 0x21); + outb(0xff, 0xa1); +} diff --git a/kvm/user/test/lib/x86/apic.h b/kvm/user/test/lib/x86/apic.h new file mode 100644 index 000000000..e325e9aae --- /dev/null +++ b/kvm/user/test/lib/x86/apic.h @@ -0,0 +1,34 @@ +#ifndef CFLAT_APIC_H +#define CFLAT_APIC_H + +#include <stdint.h> +#include "apic-defs.h" + +typedef struct { + uint8_t vector; + uint8_t delivery_mode:3; + uint8_t dest_mode:1; + uint8_t delivery_status:1; + uint8_t polarity:1; + uint8_t remote_irr:1; + uint8_t trig_mode:1; + uint8_t mask:1; + uint8_t reserve:7; + uint8_t reserved[4]; + uint8_t dest_id; +} ioapic_redir_entry_t; + +void mask_pic_interrupts(void); + +void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e); +void ioapic_write_reg(unsigned reg, uint32_t value); + +void enable_apic(void); +uint32_t apic_read(unsigned reg); +void apic_write(unsigned reg, uint32_t val); +void apic_icr_write(uint32_t val, uint32_t dest); +uint32_t apic_id(void); + +int enable_x2apic(void); + +#endif diff --git a/kvm/user/test/lib/x86/fake-apic.h b/kvm/user/test/lib/x86/fake-apic.h new file mode 100644 index 000000000..eed63baef --- /dev/null +++ b/kvm/user/test/lib/x86/fake-apic.h @@ -0,0 +1,14 @@ +#ifndef SILLY_APIC_H +#define SILLY_APIC_H + +#define APIC_BASE 0x1000 +#define APIC_SIZE 0x100 + +#define APIC_REG_NCPU 0x00 +#define APIC_REG_ID 0x04 +#define APIC_REG_SIPI_ADDR 0x08 +#define APIC_REG_SEND_SIPI 0x0c +#define APIC_REG_IPI_VECTOR 0x10 +#define APIC_REG_SEND_IPI 0x14 + +#endif diff --git a/kvm/user/test/lib/x86/fwcfg.c b/kvm/user/test/lib/x86/fwcfg.c new file mode 100644 index 000000000..2cf7cecdd --- /dev/null +++ b/kvm/user/test/lib/x86/fwcfg.c @@ -0,0 +1,40 @@ +#include "fwcfg.h" + +uint64_t fwcfg_get_u(uint16_t index, int bytes) +{ + uint64_t r = 0; + uint8_t b; + int i; + + asm volatile ("out %0, %1" : : "a"(index), "d"((uint16_t)BIOS_CFG_IOPORT)); + for (i = 0; i < bytes; ++i) { + asm volatile ("in %1, %0" : "=a"(b) : "d"((uint16_t)(BIOS_CFG_IOPORT + 1))); + r |= (uint64_t)b << (i * 8); + } + return r; +} + +uint8_t fwcfg_get_u8(unsigned index) +{ + return fwcfg_get_u(index, 1); +} + +uint16_t fwcfg_get_u16(unsigned index) +{ + return fwcfg_get_u(index, 2); +} + +uint32_t fwcfg_get_u32(unsigned index) +{ + return fwcfg_get_u(index, 4); +} + +uint64_t fwcfg_get_u64(unsigned index) +{ + return fwcfg_get_u(index, 8); +} + +unsigned fwcfg_get_nb_cpus(void) +{ + return fwcfg_get_u16(FW_CFG_NB_CPUS); +} diff --git a/kvm/user/test/lib/x86/fwcfg.h b/kvm/user/test/lib/x86/fwcfg.h new file mode 100644 index 000000000..e0836ca4d --- /dev/null +++ b/kvm/user/test/lib/x86/fwcfg.h @@ -0,0 +1,44 @@ +#ifndef FWCFG_H +#define FWCFG_H + +#include <stdint.h> + +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_MAX_ENTRY 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) + +#define FW_CFG_INVALID 0xffff + +#define BIOS_CFG_IOPORT 0x510 + +#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) +#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) +#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) + +uint8_t fwcfg_get_u8(unsigned index); +uint16_t fwcfg_get_u16(unsigned index); +uint32_t fwcfg_get_u32(unsigned index); +uint64_t fwcfg_get_u64(unsigned index); + +unsigned fwcfg_get_nb_cpus(void); + +#endif + diff --git a/kvm/user/test/lib/x86/io.c b/kvm/user/test/lib/x86/io.c new file mode 100644 index 000000000..894f398b1 --- /dev/null +++ b/kvm/user/test/lib/x86/io.c @@ -0,0 +1,23 @@ +#include "libcflat.h" +#include "smp.h" + +static struct spinlock lock; + +static void print_serial(const char *buf) +{ + unsigned long len = strlen(buf); + + asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1)); +} + +void puts(const char *s) +{ + spin_lock(&lock); + print_serial(s); + spin_unlock(&lock); +} + +void exit(int code) +{ + asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4)); +} diff --git a/kvm/user/test/lib/x86/smp.c b/kvm/user/test/lib/x86/smp.c new file mode 100644 index 000000000..241f7551e --- /dev/null +++ b/kvm/user/test/lib/x86/smp.c @@ -0,0 +1,128 @@ + +#include <libcflat.h> +#include "smp.h" +#include "apic.h" +#include "fwcfg.h" + +#define IPI_VECTOR 0x20 + +static struct spinlock ipi_lock; +static void (*ipi_function)(void *data); +static void *ipi_data; +static volatile int ipi_done; + +static __attribute__((used)) void ipi() +{ + ipi_function(ipi_data); + apic_write(APIC_EOI, 0); + ipi_done = 1; +} + +asm ( + "ipi_entry: \n" + " call ipi \n" +#ifndef __x86_64__ + " iret" +#else + " iretq" +#endif + ); + + +static void set_ipi_descriptor(void (*ipi_entry)(void)) +{ + unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2); + unsigned short cs; + unsigned long ipi = (unsigned long)ipi_entry; + + asm ("mov %%cs, %0" : "=r"(cs)); + desc[0] = ipi; + desc[1] = cs; + desc[2] = 0x8e00; + desc[3] = ipi >> 16; +#ifdef __x86_64__ + desc[4] = ipi >> 32; + desc[5] = ipi >> 48; + desc[6] = 0; + desc[7] = 0; +#endif +} + +void spin_lock(struct spinlock *lock) +{ + int v = 1; + + do { + asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v)); + } while (v); + asm volatile ("" : : : "memory"); +} + +void spin_unlock(struct spinlock *lock) +{ + asm volatile ("" : : : "memory"); + lock->v = 0; +} + +int cpu_count(void) +{ + return fwcfg_get_nb_cpus(); +} + +int smp_id(void) +{ + unsigned id; + + asm ("mov %%gs:0, %0" : "=r"(id)); + return id; +} + +static void setup_smp_id(void *data) +{ + asm ("mov %0, %%gs:0" : : "r"(apic_id()) : "memory"); +} + +static void __on_cpu(int cpu, void (*function)(void *data), void *data, + int wait) +{ + spin_lock(&ipi_lock); + if (cpu == smp_id()) + function(data); + else { + ipi_done = 0; + ipi_function = function; + ipi_data = data; + apic_icr_write(APIC_INT_ASSERT | APIC_DEST_PHYSICAL | APIC_DM_FIXED + | IPI_VECTOR, + cpu); + if (wait) { + while (!ipi_done) + ; + } + } + spin_unlock(&ipi_lock); +} + +void on_cpu(int cpu, void (*function)(void *data), void *data) +{ + __on_cpu(cpu, function, data, 1); +} + +void on_cpu_async(int cpu, void (*function)(void *data), void *data) +{ + __on_cpu(cpu, function, data, 0); +} + + +void smp_init(void) +{ + int i; + void ipi_entry(void); + + set_ipi_descriptor(ipi_entry); + + setup_smp_id(0); + for (i = 1; i < cpu_count(); ++i) + on_cpu(i, setup_smp_id, 0); + +} diff --git a/kvm/user/test/lib/x86/smp.h b/kvm/user/test/lib/x86/smp.h new file mode 100644 index 000000000..c2e73501c --- /dev/null +++ b/kvm/user/test/lib/x86/smp.h @@ -0,0 +1,17 @@ +#ifndef __SMP_H +#define __SMP_H + +struct spinlock { + int v; +}; + +void smp_init(void); + +int cpu_count(void); +int smp_id(void); +void on_cpu(int cpu, void (*function)(void *data), void *data); +void on_cpu_async(int cpu, void (*function)(void *data), void *data); +void spin_lock(struct spinlock *lock); +void spin_unlock(struct spinlock *lock); + +#endif |