diff options
Diffstat (limited to 'smp.c')
-rw-r--r-- | smp.c | 620 |
1 files changed, 436 insertions, 184 deletions
@@ -1,136 +1,113 @@ /* - * smp.c -- + * MemTest86+ V5 Specific code (GPL V2.0) + * By Samuel DEMEULEMEESTER, sdemeule@memtest.org + * http://www.canardpc.com - http://www.memtest.org + * ------------------------------------------------ + * smp.c - MemTest-86 Version 3.5 * - * Implements support for SMP machines. For reasons of - * simplicity, we do not handle all possible cases allowed by the - * MP spec. For example, we expect an explicit MP configuration - * table and do not handle default configurations. We also expect - * an on-chip local apic and do not support an external 82489DX - * apic controller. - * + * Released under version 2 of the Gnu Public License. + * By Chris Brady */ #include "stddef.h" -#include "smp.h" +#include "stdint.h" #include "cpuid.h" +#include "smp.h" #include "test.h" + #define DELAY_FACTOR 1 +unsigned num_cpus = 1; // There is at least one cpu, the BSP +int act_cpus; +unsigned found_cpus = 0; + extern void memcpy(void *dst, void *src , int len); extern void test_start(void); +extern int run_cpus; +extern int maxcpus; +extern char cpu_mask[]; +extern struct cpu_ident cpu_id; -typedef struct { - bool started; -} ap_info_t; +struct barrier_s *barr; -volatile apic_register_t *APIC = NULL; -unsigned number_of_cpus = 1; // There is at least one cpu, the BSP -/* CPU number to APIC ID mapping table. CPU 0 is the BSP. */ -static unsigned cpu_num_to_apic_id[MAX_CPUS]; -volatile ap_info_t AP[MAX_CPUS]; +void smp_find_cpus(); -uint8_t -checksum(uint8_t *data, unsigned len) +void barrier_init(int max) { - uint32_t sum = 0; - uint8_t *end = data + len; - while (data < end) { - sum += *data; - data++; - } - return (uint8_t)(sum % 0x100); + /* Set the adddress of the barrier structure */ + barr = (struct barrier_s *)0x9ff00; + barr->lck.slock = 1; + barr->mutex.slock = 1; + barr->maxproc = max; + barr->count = max; + barr->st1.slock = 1; + barr->st2.slock = 0; } - -bool -read_mp_config_table(uintptr_t addr) +void s_barrier_init(int max) { - mp_config_table_header_t *mpc = (mp_config_table_header_t*)addr; - uint8_t *tab_entry_ptr; - uint8_t *mpc_table_end; - extern unsigned num_hyper_threads_per_core; - - if (mpc->signature != MPCSignature) { - return FALSE; - } - if (checksum((uint8_t*)mpc, mpc->length) != 0) { - return FALSE; - } - - - /* FIXME: the uintptr_t cast here works around a compilation problem on - * AMD64, but it ignores the real problem, which is that lapic_addr - * is only 32 bits. Maybe that's OK, but it should be investigated. - */ - APIC = (volatile apic_register_t*)(uintptr_t)mpc->lapic_addr; - - tab_entry_ptr = ((uint8_t*)mpc) + sizeof(mp_config_table_header_t); - mpc_table_end = ((uint8_t*)mpc) + mpc->length; - while (tab_entry_ptr < mpc_table_end) { - switch (*tab_entry_ptr) { - case MP_PROCESSOR: { - mp_processor_entry_t *pe = (mp_processor_entry_t*)tab_entry_ptr; - - if (pe->cpu_flag & CPU_BOOTPROCESSOR) { - // BSP is CPU 0 - cpu_num_to_apic_id[0] = pe->apic_id; - } else if (number_of_cpus < MAX_CPUS) { - cpu_num_to_apic_id[number_of_cpus] = pe->apic_id; - number_of_cpus++; - } - if (num_hyper_threads_per_core > 1 ) { - cpu_num_to_apic_id[number_of_cpus] = pe->apic_id | 1; - number_of_cpus++; - } - - // we cannot handle non-local 82489DX apics - if ((pe->apic_ver & 0xf0) != 0x10) { - return 0; - } - - // we don't know what to do with disabled cpus - - tab_entry_ptr += sizeof(mp_processor_entry_t); - break; - } - case MP_BUS: { - tab_entry_ptr += sizeof(mp_bus_entry_t); - break; - } - case MP_IOAPIC: { - tab_entry_ptr += sizeof(mp_io_apic_entry_t); - break; - } - case MP_INTSRC: - tab_entry_ptr += sizeof(mp_interrupt_entry_t); - case MP_LINTSRC: - tab_entry_ptr += sizeof(mp_local_interrupt_entry_t); - break; - default: - return FALSE; - } - } - return TRUE; + barr->s_lck.slock = 1; + barr->s_maxproc = max; + barr->s_count = max; + barr->s_st1.slock = 1; + barr->s_st2.slock = 0; } +void barrier() +{ + if (num_cpus == 1 || v->fail_safe & 3) { + return; + } + spin_wait(&barr->st1); /* Wait if the barrier is active */ + spin_lock(&barr->lck); /* Get lock for barr struct */ + if (--barr->count == 0) { /* Last process? */ + barr->st1.slock = 0; /* Hold up any processes re-entering */ + barr->st2.slock = 1; /* Release the other processes */ + barr->count++; + spin_unlock(&barr->lck); + } else { + spin_unlock(&barr->lck); + spin_wait(&barr->st2); /* wait for peers to arrive */ + spin_lock(&barr->lck); + if (++barr->count == barr->maxproc) { + barr->st1.slock = 1; + barr->st2.slock = 0; + } + spin_unlock(&barr->lck); + } +} -floating_pointer_struct_t * -scan_for_floating_ptr_struct(uintptr_t addr, uint32_t length) +void s_barrier() { - floating_pointer_struct_t *fp; - uintptr_t end = addr + length; + if (run_cpus == 1 || v->fail_safe & 3) { + return; + } + spin_wait(&barr->s_st1); /* Wait if the barrier is active */ + spin_lock(&barr->s_lck); /* Get lock for barr struct */ + if (--barr->s_count == 0) { /* Last process? */ + barr->s_st1.slock = 0; /* Hold up any processes re-entering */ + barr->s_st2.slock = 1; /* Release the other processes */ + barr->s_count++; + spin_unlock(&barr->s_lck); + } else { + spin_unlock(&barr->s_lck); + spin_wait(&barr->s_st2); /* wait for peers to arrive */ + spin_lock(&barr->s_lck); + if (++barr->s_count == barr->s_maxproc) { + barr->s_st1.slock = 1; + barr->s_st2.slock = 0; + } + spin_unlock(&barr->s_lck); + } +} +typedef struct { + bool started; +} ap_info_t; - fp = (floating_pointer_struct_t*)addr; - while ((uintptr_t)fp < end) { - if (fp->signature == FPSignature) { - if (fp->length == 1 && checksum((uint8_t*)fp, 16) == 0) { - return fp; - } - } - fp++; - } - return NULL; -} +volatile apic_register_t *APIC = NULL; +/* CPU number to APIC ID mapping table. CPU 0 is the BSP. */ +static unsigned cpu_num_to_apic_id[MAX_CPUS]; +volatile ap_info_t AP[MAX_CPUS]; void PUT_MEM16(uintptr_t addr, uint16_t val) { @@ -199,7 +176,40 @@ memset (void *dst, *((char *) dst + i) = value; } } +void initialise_cpus(void) +{ + int i; + + act_cpus = 0; + + if (maxcpus > 1) { + smp_find_cpus(); + /* The total number of CPUs may be limited */ + if (num_cpus > maxcpus) { + num_cpus = maxcpus; + } + /* Determine how many cpus have been selected */ + for(i = 0; i < num_cpus; i++) { + if (cpu_mask[i]) { + act_cpus++; + } + } + } else { + act_cpus = found_cpus = num_cpus = 1; + } + + /* Initialize the barrier before starting AP's */ + barrier_init(act_cpus); + + /* let the BSP initialise the APs. */ + for(i = 1; i < num_cpus; i++) { + /* Only start this CPU if it is selected by the mask */ + if (cpu_mask[i]) { + smp_boot_ap(i); + } + } +} void kick_cpu(unsigned cpu_num) { unsigned num_sipi, apic_id; @@ -223,7 +233,7 @@ void kick_cpu(unsigned cpu_num) APIC_WRITE(APICR_ESR, 0); - SEND_IPI(apic_id, 0, 0, APIC_DELMODE_STARTUP, (uint32_t)startup_32 >> 12); + SEND_IPI(apic_id, 0, 0, APIC_DELMODE_STARTUP, (unsigned)startup_32 >> 12); timeout = 0; do { @@ -233,15 +243,15 @@ void kick_cpu(unsigned cpu_num) } while (send_pending && timeout < 1000); if (send_pending) { - //cprint(LINE_STATUS+1, 0, "SMP: STARTUP IPI was never sent"); + cprint(LINE_STATUS+3, 0, "SMP: STARTUP IPI was never sent"); } delay(100000 / DELAY_FACTOR); err = APIC_READ(APICR_ESR) & 0xef; if (err) { - //cprint(LINE_STATUS+1, 0, "SMP: After STARTUP IPI: err = 0x"); - //hprint(LINE_STATUS+1, COL_MID, err); + cprint(LINE_STATUS+3, 0, "SMP: After STARTUP IPI: err = 0x"); + hprint(LINE_STATUS+3, COL_MID, err); } } } @@ -304,91 +314,330 @@ void boot_ap(unsigned cpu_num) } while (send_pending && timeout < 1000); if (send_pending) { - //cprint(LINE_STATUS+1, 0, "SMP: STARTUP IPI was never sent"); + cprint(LINE_STATUS+3, 0, "SMP: STARTUP IPI was never sent"); } delay(100000 / DELAY_FACTOR); err = APIC_READ(APICR_ESR) & 0xef; if (err) { - //cprint(LINE_STATUS+1, 0, "SMP: After STARTUP IPI: err = 0x"); - // hprint(LINE_STATUS+1, COL_MID, err); + cprint(LINE_STATUS+3, 0, "SMP: After STARTUP IPI: err = 0x"); + hprint(LINE_STATUS+3, COL_MID, err); } } } -void -smp_init_bsp() +static int checksum(unsigned char *mp, int len) { - floating_pointer_struct_t *fp; - /* gets the details about the cpu, the type, the brand - * whether it is a multi-core package etc. - */ - cpuid_init(); - - memset(&AP, 0, sizeof AP); + int sum = 0; - fp = scan_for_floating_ptr_struct(0x0, 0x400); - if (fp == NULL) { - fp = scan_for_floating_ptr_struct(639*0x400, 0x400); + while (len--) { + sum += *mp++; } - if (fp == NULL) { - fp = scan_for_floating_ptr_struct(0xf0000, 0x10000); + return (sum & 0xFF); +} + +/* Parse an MP config table for CPU information */ +bool read_mp_config_table(uintptr_t addr) +{ + mp_config_table_header_t *mpc = (mp_config_table_header_t*)addr; + uint8_t *tab_entry_ptr; + uint8_t *mpc_table_end; + + if (mpc->signature != MPCSignature) { + return FALSE; } - if (fp == NULL) { - /* - * If it is an SMP machine we should know now, unless the - * configuration is in an EISA/MCA bus machine with an - * extended bios data area. - * - * there is a real-mode segmented pointer pointing to the - * 4K EBDA area at 0x40E, calculate and scan it here. - */ - unsigned int address = *(unsigned short *)0x40E; - address <<= 4; - if (address) { - fp = scan_for_floating_ptr_struct(address, 0x400); - } + if (checksum((unsigned char*)mpc, mpc->length) != 0) { + return FALSE; } - if (fp != NULL && fp->phys_addr != 0) { - if (!read_mp_config_table(fp->phys_addr)) { - //cprint(LINE_STATUS+1,0, "SMP: Error while parsing MP config table"); + /* FIXME: the uintptr_t cast here works around a compilation problem on + * AMD64, but it ignores the real problem, which is that lapic_addr + * is only 32 bits. Maybe that's OK, but it should be investigated. + */ + APIC = (volatile apic_register_t*)(uintptr_t)mpc->lapic_addr; + + tab_entry_ptr = ((uint8_t*)mpc) + sizeof(mp_config_table_header_t); + mpc_table_end = ((uint8_t*)mpc) + mpc->length; + + while (tab_entry_ptr < mpc_table_end) { + switch (*tab_entry_ptr) { + case MP_PROCESSOR: { + mp_processor_entry_t *pe = (mp_processor_entry_t*)tab_entry_ptr; + + if (pe->cpu_flag & CPU_BOOTPROCESSOR) { + // BSP is CPU 0 + cpu_num_to_apic_id[0] = pe->apic_id; + } else if (num_cpus < MAX_CPUS) { + cpu_num_to_apic_id[num_cpus] = pe->apic_id; + num_cpus++; + } + found_cpus++; + + // we cannot handle non-local 82489DX apics + if ((pe->apic_ver & 0xf0) != 0x10) { + return 0; + } + + tab_entry_ptr += sizeof(mp_processor_entry_t); + break; + } + case MP_BUS: { + tab_entry_ptr += sizeof(mp_bus_entry_t); + break; + } + case MP_IOAPIC: { + tab_entry_ptr += sizeof(mp_io_apic_entry_t); + break; + } + case MP_INTSRC: + tab_entry_ptr += sizeof(mp_interrupt_entry_t); + break; + case MP_LINTSRC: + tab_entry_ptr += sizeof(mp_local_interrupt_entry_t); + break; + default: + return FALSE; } } -/* - if (fp == NULL) { - cprint(LINE_STATUS+1,0,"SMP: No floating pointer structure found"); + return TRUE; +} + +/* Search for a Floating Pointer structure */ +floating_pointer_struct_t * +scan_for_floating_ptr_struct(uintptr_t addr, uint32_t length) +{ + floating_pointer_struct_t *fp; + uintptr_t end = addr + length; + + + while ((uintptr_t)addr < end) { + fp = (floating_pointer_struct_t*)addr; + if (*(unsigned int *)addr == FPSignature && fp->length == 1 && checksum((unsigned char*)addr, 16) == 0 && ((fp->spec_rev == 1) || (fp->spec_rev == 4))) { + return fp; + } + addr += 4; } -*/ + return NULL; } -void -smp_init_aps() +/* Search for a Root System Descriptor Pointer */ +rsdp_t *scan_for_rsdp(uintptr_t addr, uint32_t length) { - int cpuNum; - for(cpuNum = 0 ; cpuNum < MAX_CPUS ; cpuNum++) { - AP[cpuNum].started = FALSE; + rsdp_t *rp; + uintptr_t end = addr + length; + + + while ((uintptr_t)addr < end) { + rp = (rsdp_t*)addr; + if (*(unsigned int *)addr == RSDPSignature && + checksum((unsigned char*)addr, rp->length) == 0) { + return rp; + } + addr += 4; } + return NULL; } -unsigned -my_apic_id() +/* Parse a MADT table for processor entries */ +int parse_madt(uintptr_t addr) { + + mp_config_table_header_t *mpc = (mp_config_table_header_t*)addr; + uint8_t *tab_entry_ptr; + uint8_t *mpc_table_end; + + if (checksum((unsigned char*)mpc, mpc->length) != 0) { + return FALSE; + } + + APIC = (volatile apic_register_t*)(uintptr_t)mpc->lapic_addr; + + tab_entry_ptr = ((uint8_t*)mpc) + sizeof(mp_config_table_header_t); + mpc_table_end = ((uint8_t*)mpc) + mpc->length; + while (tab_entry_ptr < mpc_table_end) { + + madt_processor_entry_t *pe = (madt_processor_entry_t*)tab_entry_ptr; + if (pe->type == MP_PROCESSOR) { + if (pe->enabled) { + if (num_cpus < MAX_CPUS) { + cpu_num_to_apic_id[num_cpus] = pe->apic_id; + + /* the first CPU is the BSP, don't increment */ + if (found_cpus) { + num_cpus++; + } + } + found_cpus++; + } + } + tab_entry_ptr += pe->length; + } + return TRUE; +} + +/* This is where we search for SMP information in the following order + * look for a floating MP pointer + * found: + * check for a default configuration + * found: + * setup config, return + * check for a MP config table + * found: + * validate: + * good: + * parse the MP config table + * good: + * setup config, return + * + * find & validate ACPI RSDP (Root System Descriptor Pointer) + * found: + * find & validate RSDT (Root System Descriptor Table) + * found: + * find & validate MSDT + * found: + * parse the MADT table + * good: + * setup config, return + */ +void smp_find_cpus() +{ + floating_pointer_struct_t *fp; + rsdp_t *rp; + rsdt_t *rt; + uint8_t *tab_ptr, *tab_end; + unsigned int *ptr; + unsigned int uiptr; + + if(v->fail_safe & 3) { return; } + + memset(&AP, 0, sizeof AP); + + if(v->fail_safe & 8) + { + // Search for the Floating MP structure pointer + fp = scan_for_floating_ptr_struct(0x0, 0x400); + if (fp == NULL) { + fp = scan_for_floating_ptr_struct(639*0x400, 0x400); + } + if (fp == NULL) { + fp = scan_for_floating_ptr_struct(0xf0000, 0x10000); + } + if (fp == NULL) { + // Search the BIOS ESDS area + unsigned int address = *(unsigned short *)0x40E; + address <<= 4; + if (address) { + fp = scan_for_floating_ptr_struct(address, 0x400); + } + } + + if (fp != NULL) { + // We have a floating MP pointer + // Is this a default configuration? + + if (fp->feature[0] > 0 && fp->feature[0] <=7) { + // This is a default config so plug in the numbers + num_cpus = 2; + APIC = (volatile apic_register_t*)0xFEE00000; + cpu_num_to_apic_id[0] = 0; + cpu_num_to_apic_id[1] = 1; + return; + } + + // Do we have a pointer to a MP configuration table? + if ( fp->phys_addr != 0) { + if (read_mp_config_table(fp->phys_addr)) { + // Found a good MP table, done + return; + } + } + } + } + + + /* No MP table so far, try to find an ACPI MADT table + * We try to use the MP table first since there is no way to distinguish + * real cores from hyper-threads in the MADT */ + + /* Search for the RSDP */ + rp = scan_for_rsdp(0xE0000, 0x20000); + if (rp == NULL) { + /* Search the BIOS ESDS area */ + unsigned int address = *(unsigned short *)0x40E; + address <<= 4; + if (address) { + rp = scan_for_rsdp(address, 0x400); + } + } + + if (rp == NULL) { + /* RSDP not found, give up */ + return; + } + + /* Found the RSDP, now get either the RSDT or XSDT */ + if (rp->revision >= 2) { + rt = (rsdt_t *)rp->xrsdt[0]; + + if (rt == 0) { + return; + } + // Validate the XSDT + if (*(unsigned int *)rt != XSDTSignature) { + return; + } + if ( checksum((unsigned char*)rt, rt->length) != 0) { + return; + } + + } else { + rt = (rsdt_t *)rp->rsdt; + if (rt == 0) { + return; + } + /* Validate the RSDT */ + if (*(unsigned int *)rt != RSDTSignature) { + return; + } + if ( checksum((unsigned char*)rt, rt->length) != 0) { + return; + } + } + + /* Scan the RSDT or XSDT for a pointer to the MADT */ + tab_ptr = ((uint8_t*)rt) + sizeof(rsdt_t); + tab_end = ((uint8_t*)rt) + rt->length; + + while (tab_ptr < tab_end) { + + uiptr = *((unsigned int *)tab_ptr); + ptr = (unsigned int *)uiptr; + + /* Check for the MADT signature */ + if (ptr && *ptr == MADTSignature) { + /* Found it, now parse it */ + if (parse_madt((uintptr_t)ptr)) { + return; + } + } + tab_ptr += 4; + } +} + +unsigned my_apic_id() { return (APIC[APICR_ID][0]) >> 24; } -void -smp_ap_booted(unsigned cpu_num) +void smp_ap_booted(unsigned cpu_num) { AP[cpu_num].started = TRUE; } -void -smp_boot_ap(unsigned cpu_num) +void smp_boot_ap(unsigned cpu_num) { unsigned timeout; - extern bool smp_mode; + boot_ap(cpu_num); timeout = 0; do { @@ -397,21 +646,13 @@ smp_boot_ap(unsigned cpu_num) } while (!AP[cpu_num].started && timeout < 100000 / DELAY_FACTOR); if (!AP[cpu_num].started) { - //cprint(LINE_STATUS+1, 0, "SMP: Boot timeout for"); - //dprint(LINE_STATUS+1, COL_MID, cpu_num,2,1); - //cprint(LINE_STATUS+1, 26, "Turning off SMP"); - smp_mode = FALSE; + cprint(LINE_STATUS+3, 0, "SMP: Boot timeout for"); + dprint(LINE_STATUS+3, COL_MID, cpu_num,2,1); + cprint(LINE_STATUS+3, 26, "Turning off SMP"); } } -unsigned -smp_num_cpus() -{ - return number_of_cpus; -} - -unsigned -smp_my_cpu_num() +unsigned smp_my_cpu_num() { unsigned apicid = my_apic_id(); unsigned i; @@ -427,14 +668,25 @@ smp_my_cpu_num() return i; } -volatile spinlock_t barr_lk={1}; -void barrier(volatile int *barr, int n) +/* A set of simple functions used to preserve assigned CPU ordinals since + * they are lost after relocation (the stack is reloaded). + */ +int num_to_ord[MAX_CPUS]; +void smp_set_ordinal(int me, int ord) { - spin_lock(&barr_lk); - barr++; - spin_unlock(&barr_lk); - while((uint32_t)barr<n); - barr = 0; - return; + num_to_ord[me] = ord; } +int smp_my_ord_num(int me) +{ + return num_to_ord[me]; +} + +int smp_ord_to_cpu(int me) +{ + int i; + for (i=0; i<MAX_CPUS; i++) { + if (num_to_ord[i] == me) return i; + } + return -1; +}
\ No newline at end of file |