--- i386/i386/apic.c | 87 ++++++++++++++++++++++++++++++---- i386/i386/apic.h | 110 +++++++++++++++++++++++++++++++++++++++++-- i386/i386/smp.c | 89 +++++++++++++++++++++++++++++++++- i386/i386/smp.h | 7 +++ i386/i386at/ioapic.c | 103 ++++++++++++---------------------------- 5 files changed, 308 insertions(+), 88 deletions(-)
diff --git a/i386/i386/apic.c b/i386/i386/apic.c index d30084e2..78a2d006 100644 --- a/i386/i386/apic.c +++ b/i386/i386/apic.c @@ -1,5 +1,5 @@ /* apic.c - APIC controller management for Mach. - Copyright (C) 2020 Free Software Foundation, Inc. + Copyright (C) 2021 Free Software Foundation, Inc. Written by Almudena Garcia Jurado-Centurion This file is part of GNU Mach. @@ -19,6 +19,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include <i386/apic.h> +#include <i386/cpu.h> +#include <i386at/idt.h> #include <string.h> #include <vm/vm_kern.h> #include <kern/printf.h> @@ -116,11 +118,29 @@ uint16_t apic_get_cpu_apic_id(int kernel_id) { if (kernel_id >= NCPUS) - return -1; + return 0; return apic_data.cpu_lapic_list[kernel_id]; } + +/* + * apic_get_cpu_kernel_id: returns the kernel_id of a cpu. + * Receives as input the APIC ID of a CPU. + */ +int +apic_get_cpu_kernel_id(uint16_t apic_id) +{ + int i; + + for (i = 0; i < apic_data.ncpus; i++) { + if (apic_data.cpu_lapic_list[i] == apic_id) + return i; + } + + return 0; +} + /* apic_get_lapic: returns a reference to the common memory address for Local APIC. */ volatile ApicLocalUnit* apic_get_lapic(void) @@ -161,14 +181,10 @@ apic_get_num_ioapics(void) uint16_t apic_get_current_cpu(void) { - uint16_t apic_id; - if(lapic == NULL) - apic_id = 0; - else - apic_id = lapic->apic_id.r; + return 0; - return apic_id; + return (lapic->apic_id.r >> 24) & 0xff; } @@ -235,6 +251,61 @@ void apic_print_info(void) } } +void apic_send_ipi(unsigned dest_shorthand, unsigned deliv_mode, unsigned dest_mode, unsigned level, unsigned trig_mode, unsigned vector, unsigned dest_id) +{ + IcrLReg icrl_values; + IcrHReg icrh_values; + + icrl_values.destination_shorthand = dest_shorthand; + icrl_values.delivery_mode = deliv_mode; + icrl_values.destination_mode = dest_mode; + icrl_values.level = level; + icrl_values.trigger_mode = trig_mode; + icrl_values.vector = vector; + icrh_values.destination_field = dest_id; + + lapic->icr_high = icrh_values; + lapic->icr_low = icrl_values; +} + +void +lapic_enable(void) +{ + unsigned long flags; + int apic_id; + volatile uint32_t dummy; + + cpu_intr_save(&flags); + + apic_id = apic_get_current_cpu(); + + dummy = lapic->dest_format.r; + lapic->dest_format.r = 0xffffffff; /* flat model */ + dummy = lapic->logical_dest.r; + lapic->logical_dest.r = lapic->apic_id.r; /* target self */ + dummy = lapic->lvt_lint0.r; + lapic->lvt_lint0.r = dummy | LAPIC_DISABLE; + dummy = lapic->lvt_lint1.r; + lapic->lvt_lint1.r = dummy | LAPIC_DISABLE; + dummy = lapic->lvt_performance_monitor.r; + lapic->lvt_performance_monitor.r = dummy | LAPIC_DISABLE; + if (apic_id != 0) + { + dummy = lapic->lvt_timer.r; + lapic->lvt_timer.r = dummy | LAPIC_DISABLE; + } + dummy = lapic->task_pri.r; + lapic->task_pri.r = 0; + + /* Enable LAPIC to send or recieve IPI/SIPIs */ + dummy = lapic->spurious_vector.r; + lapic->spurious_vector.r = dummy | LAPIC_ENABLE; + + lapic->error_status.r = 0; + + cpu_intr_restore(flags); +} + void lapic_eoi(void) { diff --git a/i386/i386/apic.h b/i386/i386/apic.h index 0bb1bd73..293b0486 100644 --- a/i386/i386/apic.h +++ b/i386/i386/apic.h @@ -61,10 +61,99 @@ union ioapic_route_entry_union { struct ioapic_route_entry both; }; + +/* Grateful to trasterlabs for this snippet */ + +typedef union u_icr_low +{ + uint32_t value[4]; + struct + { + uint32_t r; // FEE0 0300H - 4 bytes + unsigned :32; // FEE0 0304H + unsigned :32; // FEE0 0308H + unsigned :32; // FEE0 030CH + }; + struct + { + unsigned vector: 8; /* Vector of interrupt. Lowest 8 bits of routine address */ + unsigned delivery_mode : 3; + unsigned destination_mode: 1; + unsigned delivery_status: 1; + unsigned :1; + unsigned level: 1; + unsigned trigger_mode: 1; + unsigned :2; + unsigned destination_shorthand: 2; + unsigned :12; + }; +} IcrLReg; + +typedef union u_icr_high +{ + uint32_t value[4]; + struct + { + uint32_t r; // FEE0 0310H - 4 bytes + unsigned :32; // FEE0 0314H + unsigned :32; // FEE0 0318H + unsigned :32; // FEE0 031CH + }; + struct + { + unsigned :24; // FEE0 0310H - 4 bytes + unsigned destination_field :8; /* APIC ID (in physical mode) or MDA (in logical) of destination processor */ + }; +} IcrHReg; + + +typedef enum e_icr_dest_shorthand +{ + NO_SHORTHAND = 0, + SELF = 1, + ALL_INCLUDING_SELF = 2, + ALL_EXCLUDING_SELF = 3 +} icr_dest_shorthand; + +typedef enum e_icr_deliv_mode +{ + FIXED = 0, + LOWEST_PRIORITY = 1, + SMI = 2, + NMI = 4, + INIT = 5, + STARTUP = 6, +} icr_deliv_mode; + +typedef enum e_icr_dest_mode +{ + PHYSICAL = 0, + LOGICAL = 1 +} icr_dest_mode; + +typedef enum e_icr_deliv_status +{ + IDLE = 0, + SEND_PENDING = 1 +} icr_deliv_status; + +typedef enum e_icr_level +{ + DE_ASSERT = 0, + ASSERT = 1 +} icr_level; + +typedef enum e_irc_trigger_mode +{ + EDGE = 0, + LEVEL = 1 +} irc_trigger_mode; + + typedef struct ApicLocalUnit { ApicReg reserved0; /* 0x000 */ ApicReg reserved1; /* 0x010 */ - ApicReg apic_id; /* 0x020 */ + ApicReg apic_id; /* 0x020. Hardware ID of current processor */ ApicReg version; /* 0x030 */ ApicReg reserved4; /* 0x040 */ ApicReg reserved5; /* 0x050 */ @@ -84,8 +173,8 @@ typedef struct ApicLocalUnit { ApicReg error_status; /* 0x280 */ ApicReg reserved28[6]; /* 0x290 */ ApicReg lvt_cmci; /* 0x2f0 */ - ApicReg icr_low; /* 0x300 */ - ApicReg icr_high; /* 0x310 */ + IcrLReg icr_low; /* 0x300. Store the information to send an IPI (Inter-processor Interrupt) */ + IcrHReg icr_high; /* 0x310. Store the IPI destination */ ApicReg lvt_timer; /* 0x320 */ ApicReg lvt_thermal; /* 0x330 */ ApicReg lvt_performance_monitor; /* 0x340 */ @@ -138,8 +227,10 @@ void apic_add_cpu(uint16_t apic_id); void apic_lapic_init(ApicLocalUnit* lapic_ptr); void apic_add_ioapic(struct IoApicData); void apic_add_irq_override(struct IrqOverrideData irq_over); +void apic_send_ipi(unsigned dest_shorthand, unsigned deliv_mode, unsigned dest_mode, unsigned level, unsigned trig_mode, unsigned vector, unsigned dest_id); IrqOverrideData *acpi_get_irq_override(uint8_t gsi); uint16_t apic_get_cpu_apic_id(int kernel_id); +int apic_get_cpu_kernel_id(uint16_t apic_id); volatile ApicLocalUnit* apic_get_lapic(void); struct IoApicData *apic_get_ioapic(int kernel_id); uint8_t apic_get_numcpus(void); @@ -150,12 +241,13 @@ int apic_refit_cpulist(void); void picdisable(void); void lapic_eoi(void); void ioapic_irq_eoi(int pin); +void lapic_enable(void); void lapic_enable_timer(void); void ioapic_mask_irqs(void); void ioapic_toggle(int pin, int mask); void ioapic_configure(void); -extern int timer_pin; +extern int duplicate_pin; extern void intnull(int unit); extern volatile ApicLocalUnit* lapic; @@ -172,9 +264,13 @@ extern volatile ApicLocalUnit* lapic; # define IMCR_USE_PIC 0 # define IMCR_USE_APIC 1 +#define LAPIC_LOW_PRIO 0x100 +#define LAPIC_NMI 0x400 +#define LAPIC_EXTINT 0x700 +#define LAPIC_LEVEL_TRIGGERED 0x8000 + #define LAPIC_ENABLE 0x100 #define LAPIC_FOCUS 0x200 -#define LAPIC_NMI 0x400 #define LAPIC_ENABLE_DIRECTED_EOI 0x1000 #define LAPIC_DISABLE 0x10000 #define LAPIC_TIMER_PERIODIC 0x20000 @@ -198,6 +294,10 @@ extern volatile ApicLocalUnit* lapic; #define IOAPIC_MASK_ENABLED 0 #define IOAPIC_MASK_DISABLED 1 +#define APIC_MSR 0x1b +#define APIC_MSR_BSP 0x100 /* Processor is a BSP */ +#define APIC_MSR_ENABLE 0x800 + /* Set or clear a bit in a 255-bit APIC mask register. These registers are spread through eight 32-bit registers. */ #define APIC_SET_MASK_BIT(reg, bit) \ diff --git a/i386/i386/smp.c b/i386/i386/smp.c index d7523a73..c351efaa 100644 --- a/i386/i386/smp.c +++ b/i386/i386/smp.c @@ -18,11 +18,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ -#include <i386/i386/apic.h> -#include <i386/i386/smp.h> +#include <i386/apic.h> +#include <i386/smp.h> +#include <i386/cpu.h> +#include <i386/pit.h> +#include <i386at/idt.h> +#include <i386at/acpi_parse_apic.h> +#include <kern/printf.h> +#include <mach/machine.h> #include <kern/smp.h> +#define pause_memory asm volatile ("pause" : : : "memory") /* * smp_data_init: initialize smp_data structure @@ -33,6 +40,84 @@ static void smp_data_init(void) { uint8_t numcpus = apic_get_numcpus(); smp_set_numcpus(numcpus); + + for(int i = 0; i < numcpus; i++){ + machine_slot[i].is_cpu = TRUE; + } + +} + +void smp_pmap_update(unsigned apic_id) +{ + unsigned long flags; + + cpu_intr_save(&flags); + + printf("Sending IPI(%u) to call TLB shootdown...", apic_id); + apic_send_ipi(NO_SHORTHAND, FIXED, PHYSICAL, ASSERT, EDGE, CALL_SINGLE_FUNCTION_BASE, apic_id); + + do { + pause_memory; + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + printf("done\n"); + + cpu_intr_restore(flags); +} + +/* See Intel IA32/64 Software Developer's Manual 3A Section 8.4.4.1 */ +void smp_startup_cpu(unsigned apic_id, unsigned vector) +{ + /* Clear APIC errors */ + lapic->error_status.r = 0; + + printf("Sending IPIs to APIC ID %u...", apic_id); + + /* Assert INIT IPI */ + apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, ASSERT, LEVEL, 0, apic_id); + + /* Wait for delivery */ + do { + pause_memory; + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + /* Deassert INIT IPI */ + apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, DE_ASSERT, LEVEL, 0, apic_id); + + /* Wait for delivery */ + do { + pause_memory; + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + /* Wait 10 msec */ + pit_mdelay(10); + + /* Clear APIC errors */ + lapic->error_status.r = 0; + + /* First StartUp IPI */ + apic_send_ipi(NO_SHORTHAND, STARTUP, PHYSICAL, ASSERT, LEVEL, vector >> 12, apic_id); + + /* Wait 200 usec */ + pit_udelay(200); + + /* Wait for delivery */ + do { + pause_memory; + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + /* Second StartUp IPI */ + apic_send_ipi(NO_SHORTHAND, STARTUP, PHYSICAL, ASSERT, LEVEL, vector >> 12, apic_id); + + /* Wait 200 usec */ + pit_udelay(200); + + /* Wait for delivery */ + do { + pause_memory; + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + printf("done\n"); } /* diff --git a/i386/i386/smp.h b/i386/i386/smp.h index b36ead08..79337022 100644 --- a/i386/i386/smp.h +++ b/i386/i386/smp.h @@ -18,4 +18,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ +#ifndef _SMP_H_ +#define _SMP_H_ + int smp_init(void); +void smp_pmap_update(unsigned apic_id); +void smp_startup_cpu(unsigned apic_id, unsigned vector); + +#endif diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c index c5eb3536..392e296a 100644 --- a/i386/i386at/ioapic.c +++ b/i386/i386at/ioapic.c @@ -32,8 +32,7 @@ #include <kern/printf.h> static int has_irq_specific_eoi = 1; /* FIXME: Assume all machines have this */ -static int timer_gsi; -int timer_pin; +int duplicate_pin; uint32_t lapic_timer_val = 0; uint32_t calibrated_ticks = 0; @@ -78,6 +77,7 @@ void picdisable(void) { asm("cli"); + curr_ipl = SPLHI; /* ** Disable PIC @@ -147,40 +147,13 @@ ioapic_toggle_entry(int apic, int pin, int mask) ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); } -static void -cpu_rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi) -{ - __asm__ __volatile__("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); -} - -static void -cpu_wrmsr(uint32_t msr, uint32_t lo, uint32_t hi) -{ - __asm__ __volatile__("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); -} - -static void -global_enable_apic(void) -{ - uint32_t lo = 0; - uint32_t hi = 0; - uint32_t msr = 0x1b; - - cpu_rdmsr(msr, &lo, &hi); - - if (!(lo & (1 << 11))) { - lo |= (1 << 11); - cpu_wrmsr(msr, lo, hi); - } -} - static uint32_t pit_measure_apic_hz(void) { uint32_t start = 0xffffffff; - /* Prepare accurate delay for 1/100 seconds */ - pit_prepare_sleep(100); + /* Prepare accurate delay for 1/hz seconds */ + pit_prepare_sleep(hz); /* Set APIC timer */ lapic->init_count.r = start; @@ -189,7 +162,7 @@ pit_measure_apic_hz(void) pit_sleep(); /* Stop APIC timer */ - lapic->lvt_timer.r = LAPIC_DISABLE; + lapic->lvt_timer.r |= LAPIC_DISABLE; return start - lapic->cur_count.r; } @@ -203,26 +176,18 @@ void lapic_update_timer(void) void lapic_enable_timer(void) { - spl_t s; - - s = sploff(); - asm("cli"); - /* Set up counter */ lapic->init_count.r = calibrated_ticks; - lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; /* Set the timer to interrupt periodically on remapped timer GSI */ - lapic->lvt_timer.r = (IOAPIC_INT_BASE + timer_gsi) | LAPIC_TIMER_PERIODIC; + lapic->lvt_timer.r = IOAPIC_INT_BASE | LAPIC_TIMER_PERIODIC; /* Some buggy hardware requires this set again */ - lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; - - /* Unmask the remapped timer pin and pin 0 always */ - ioapic_toggle(0, IOAPIC_MASK_ENABLED); - ioapic_toggle(timer_pin, IOAPIC_MASK_ENABLED); + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; - splon(s); + /* Enable interrupts for the first time on BSP */ + asm("sti"); printf("LAPIC timer configured\n"); } @@ -239,6 +204,9 @@ ioapic_irq_eoi(int pin) int apic = 0; union ioapic_route_entry_union oldentry, entry; + if (pin == 0) + goto skip_specific_eoi; + if (!has_irq_specific_eoi) { /* Workaround for old IOAPICs with no specific EOI */ @@ -258,6 +226,7 @@ ioapic_irq_eoi(int pin) ioapic->eoi.r = entry.both.vector; } +skip_specific_eoi: lapic_eoi (); } @@ -303,12 +272,12 @@ ioapic_configure(void) /* Assume first IO APIC maps to GSI base 0 */ int gsi, apic = 0, bsp = 0, pin; IrqOverrideData *irq_over; + int timer_gsi; /* Disable IOAPIC interrupts and set spurious interrupt */ lapic->spurious_vector.r = IOAPIC_SPURIOUS_BASE; union ioapic_route_entry_union entry = {{0, 0}}; - union ioapic_route_entry_union timer_entry = {{0, 0}}; entry.both.delvmode = IOAPIC_FIXED; entry.both.destmode = IOAPIC_PHYSICAL; @@ -332,16 +301,17 @@ ioapic_configure(void) if (pin == 0) { /* Save timer info */ timer_gsi = gsi; - timer_entry = entry; } else { - /* Get the actual timer pin by assuming that the pin - * with duplicated gsi from pin 0 maps to the timer pin */ + /* Disable duplicated timer gsi */ if (gsi == timer_gsi) { - timer_pin = pin; - /* Remap pin 0 interrupt vector to GSI base + duplicate_pin = pin; + /* Remap this interrupt pin to GSI base * so we don't duplicate vectors */ - timer_entry.both.vector = IOAPIC_INT_BASE; - ioapic_write_entry(apic, 0, timer_entry.both); + entry.both.vector = IOAPIC_INT_BASE; + ioapic_write_entry(apic, duplicate_pin, entry.both); + /* Mask the ioapic pin with deduplicated vector as + * we will never use it, since timer is on another gsi */ + mask_irq(duplicate_pin); } } } @@ -361,16 +331,7 @@ ioapic_configure(void) } /* Start the IO APIC receiving interrupts */ - lapic->apic_id.r = apic_get_cpu_apic_id(bsp); - lapic->dest_format.r = 0xffffffff; /* flat model */ - lapic->logical_dest.r = 0x01000000; /* target bsp */ - lapic->lvt_timer.r = LAPIC_DISABLE; - lapic->lvt_performance_monitor.r = LAPIC_NMI; - lapic->lvt_lint0.r = LAPIC_DISABLE; - lapic->lvt_lint1.r = LAPIC_DISABLE; - lapic->task_pri.r = 0; - - global_enable_apic(); + lapic_enable(); /* Enable IOAPIC processor focus */ lapic->spurious_vector.r |= LAPIC_FOCUS; @@ -381,23 +342,19 @@ ioapic_configure(void) lapic->spurious_vector.r |= LAPIC_ENABLE_DIRECTED_EOI; } - /* Enable IOAPIC interrupts */ - lapic->spurious_vector.r |= LAPIC_ENABLE; - /* Set one-shot timer */ - lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; - lapic->lvt_timer.r = IOAPIC_INT_BASE + timer_gsi; + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + lapic->lvt_timer.r = IOAPIC_INT_BASE; - /* Measure number of APIC timer ticks in 10ms */ - calibrated_ticks = pit_measure_apic_hz(); + /* Measure number of APIC timer ticks in 1/hz seconds + * but calibrate the timer to expire at rate of hz */ + calibrated_ticks = pit_measure_apic_hz() * 2; /* Set up counter later */ lapic->lvt_timer.r = LAPIC_DISABLE; - /* Install clock interrupt handler on both remapped timer pin and pin 0 - * since nobody knows how all x86 timers are wired up */ + /* Install clock interrupt handler on pin 0 */ ivect[0] = (interrupt_handler_fn)hardclock; - ivect[timer_pin] = (interrupt_handler_fn)hardclock; printf("IOAPIC 0 configured\n"); } -- 2.34.1