| #include "libcflat.h" | |
| #include "apic.h" | |
| #include "msr.h" | |
| #include "processor.h" | |
| #include "smp.h" | |
| #include "asm/barrier.h" | |
| /* xAPIC and I/O APIC are identify mapped, and never relocated. */ | |
| static void *g_apic = (void *)APIC_DEFAULT_PHYS_BASE; | |
| static void *g_ioapic = (void *)IO_APIC_DEFAULT_PHYS_BASE; | |
| u32 id_map[MAX_TEST_CPUS]; | |
| 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 struct apic_ops *get_apic_ops(void) | |
| { | |
| return this_cpu_read_apic_ops(); | |
| } | |
| static void outb(unsigned char data, unsigned short port) | |
| { | |
| asm volatile ("out %0, %1" : : "a"(data), "d"(port)); | |
| } | |
| void eoi(void) | |
| { | |
| apic_write(APIC_EOI, 0); | |
| } | |
| 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 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) | |
| { | |
| mb(); | |
| asm volatile ("wrmsr" : : "a"(val), "d"(dest), | |
| "c"(APIC_BASE_MSR + APIC_ICR/16)); | |
| } | |
| static uint32_t x2apic_id(void) | |
| { | |
| return x2apic_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 get_apic_ops()->reg_read(reg); | |
| } | |
| void apic_write(unsigned reg, u32 val) | |
| { | |
| get_apic_ops()->reg_write(reg, val); | |
| } | |
| bool apic_read_bit(unsigned reg, int n) | |
| { | |
| reg += (n >> 5) << 4; | |
| n &= 31; | |
| return (apic_read(reg) & (1 << n)) != 0; | |
| } | |
| void apic_icr_write(u32 val, u32 dest) | |
| { | |
| get_apic_ops()->icr_write(val, dest); | |
| } | |
| uint32_t apic_id(void) | |
| { | |
| return get_apic_ops()->id(); | |
| } | |
| uint8_t apic_get_tpr(void) | |
| { | |
| unsigned long tpr; | |
| #ifdef __x86_64__ | |
| asm volatile ("mov %%cr8, %0" : "=r"(tpr)); | |
| #else | |
| tpr = apic_read(APIC_TASKPRI) >> 4; | |
| #endif | |
| return tpr; | |
| } | |
| void apic_set_tpr(uint8_t tpr) | |
| { | |
| #ifdef __x86_64__ | |
| asm volatile ("mov %0, %%cr8" : : "r"((unsigned long) tpr)); | |
| #else | |
| apic_write(APIC_TASKPRI, tpr << 4); | |
| #endif | |
| } | |
| 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_IA32_APICBASE)); | |
| a |= 1 << 10; | |
| asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_IA32_APICBASE)); | |
| this_cpu_write_apic_ops((void *)&x2apic_ops); | |
| return 1; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| uint32_t pre_boot_apic_id(void) | |
| { | |
| u32 msr_lo, msr_hi; | |
| asm ("rdmsr" : "=a"(msr_lo), "=d"(msr_hi) : "c"(MSR_IA32_APICBASE)); | |
| return (msr_lo & APIC_EXTD) ? x2apic_id() : xapic_id(); | |
| } | |
| void disable_apic(void) | |
| { | |
| wrmsr(MSR_IA32_APICBASE, rdmsr(MSR_IA32_APICBASE) & ~(APIC_EN | APIC_EXTD)); | |
| this_cpu_write_apic_ops((void *)&xapic_ops); | |
| } | |
| void reset_apic(void) | |
| { | |
| disable_apic(); | |
| wrmsr(MSR_IA32_APICBASE, rdmsr(MSR_IA32_APICBASE) | APIC_EN); | |
| xapic_write(APIC_SPIV, 0x1ff); | |
| } | |
| u32 ioapic_read_reg(unsigned reg) | |
| { | |
| *(volatile u32 *)g_ioapic = reg; | |
| return *(volatile u32 *)(g_ioapic + 0x10); | |
| } | |
| 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]); | |
| } | |
| ioapic_redir_entry_t ioapic_read_redir(unsigned line) | |
| { | |
| ioapic_redir_entry_t e; | |
| ((u32 *)&e)[0] = ioapic_read_reg(0x10 + line * 2 + 0); | |
| ((u32 *)&e)[1] = ioapic_read_reg(0x10 + line * 2 + 1); | |
| return e; | |
| } | |
| void ioapic_set_redir(unsigned line, unsigned vec, | |
| trigger_mode_t trig_mode) | |
| { | |
| ioapic_redir_entry_t e = { | |
| .vector = vec, | |
| .delivery_mode = 0, | |
| .trig_mode = trig_mode, | |
| }; | |
| ioapic_write_redir(line, e); | |
| } | |
| void set_mask(unsigned line, int mask) | |
| { | |
| ioapic_redir_entry_t e = ioapic_read_redir(line); | |
| e.mask = mask; | |
| ioapic_write_redir(line, e); | |
| } | |
| void set_irq_line(unsigned line, int val) | |
| { | |
| asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); | |
| } | |
| void enable_apic(void) | |
| { | |
| printf("enabling apic\n"); | |
| xapic_write(APIC_SPIV, 0x1ff); | |
| } | |
| void mask_pic_interrupts(void) | |
| { | |
| outb(0xff, 0x21); | |
| outb(0xff, 0xa1); | |
| } | |
| void init_apic_map(void) | |
| { | |
| unsigned int i, j = 0; | |
| for (i = 0; i < MAX_TEST_CPUS; i++) { | |
| if ((1ul << (i % 8)) & (online_cpus[i / 8])) | |
| id_map[j++] = i; | |
| } | |
| } | |
| void apic_setup_timer(int vector, u32 mode) | |
| { | |
| apic_cleanup_timer(); | |
| assert((mode & APIC_LVT_TIMER_MASK) == mode); | |
| apic_write(APIC_TDCR, APIC_TDR_DIV_1); | |
| apic_write(APIC_LVTT, vector | mode); | |
| } | |
| void apic_start_timer(u32 value) | |
| { | |
| /* | |
| * APIC timer runs at the 'core crystal clock', divided by the value in | |
| * APIC_TDCR. | |
| */ | |
| apic_write(APIC_TMICT, value); | |
| } | |
| void apic_stop_timer(void) | |
| { | |
| apic_write(APIC_TMICT, 0); | |
| } | |
| void apic_cleanup_timer(void) | |
| { | |
| u32 lvtt = apic_read(APIC_LVTT); | |
| /* Stop the timer/counter. */ | |
| apic_stop_timer(); | |
| /* Mask the timer interrupt in the local vector table. */ | |
| apic_write(APIC_LVTT, lvtt | APIC_LVT_MASKED); | |
| /* Enable interrupts to ensure any pending timer IRQs are serviced. */ | |
| sti_nop_cli(); | |
| } |