From ce6c4c7195a4e12fc4fa2fff5b35434fe2ac8c4f Mon Sep 17 00:00:00 2001 From: "Ronald G. Minnich" Date: Mon, 15 Oct 2001 18:19:40 +0000 Subject: [PATCH] Minor SMP fixes. Fix to linuxpci to catch buggy chipsets --- src/arch/i386/include/arch/smp/mpspec.h | 5 +++-- src/arch/i386/lib/hardwaremain.c | 22 +++++++++++++++++++--- src/arch/i386/smp/mpspec.c | 5 +++-- src/arch/i386/smp/start_stop.c | 25 ++++++++++++++++++++----- src/include/smp/start_stop.h | 4 ++-- src/lib/linuxpci.c | 20 +++++++++++++++++++- src/mainboard/intel/l440gx/mptable.c | 8 ++++---- 7 files changed, 70 insertions(+), 19 deletions(-) diff --git a/src/arch/i386/include/arch/smp/mpspec.h b/src/arch/i386/include/arch/smp/mpspec.h index be2acf43b5..65fd9054c0 100644 --- a/src/arch/i386/include/arch/smp/mpspec.h +++ b/src/arch/i386/include/arch/smp/mpspec.h @@ -238,7 +238,8 @@ void smp_write_processor(struct mp_config_table *mc, unsigned char apicid, unsigned char apicver, unsigned char cpuflag, unsigned int cpufeature, unsigned int featureflag); -void smp_write_processors(struct mp_config_table *mc); +void smp_write_processors(struct mp_config_table *mc, + unsigned long *processor_map); void smp_write_bus(struct mp_config_table *mc, unsigned char id, unsigned char *bustype); void smp_write_ioapic(struct mp_config_table *mc, @@ -264,7 +265,7 @@ void smp_write_compatibility_address_space(struct mp_config_table *mc, unsigned int range_list); unsigned char smp_compute_checksum(void *v, int len); void smp_write_floating_table(void *v); -void write_smp_table(void *v); +void write_smp_table(void *v, unsigned long *processor_map); #else /* HAVE_MP_TABLE */ diff --git a/src/arch/i386/lib/hardwaremain.c b/src/arch/i386/lib/hardwaremain.c index ef94e648dd..789b4276e7 100644 --- a/src/arch/i386/lib/hardwaremain.c +++ b/src/arch/i386/lib/hardwaremain.c @@ -109,10 +109,12 @@ void secondary_cpu_init(void) unsigned long totalram; int processor_id; + printk_spew(__FUNCTION__ "\n"); atomic_inc(&active_cpus); totalram = get_ramsize(); processor_id = cpu_initialize(totalram); atomic_dec(&active_cpus); + printk_spew(__FUNCTION__ " id is %d\n", processor_id); stop_cpu(processor_id); } @@ -135,6 +137,18 @@ static void wait_for_other_cpus(void) void hardwaremain(int boot_complete) { + // The processor map. + // Now that SMP is in linuxbios, and Linux counts on us + // giving accurate information about processors, we need a map + // of what processors are out there. This could be a bit mask, + // but we will be optimistic and hope we someday run on + // REALLY BIG SMPs. Also we may need more than one bit of + // info per processor at some point. I hope we don't need + // anything more complex than an int. + unsigned long processor_map[MAX_CPUS]; + // Processor ID of the BOOT cpu (i.e. the one running this code + int boot_cpu; + // Comment: the NEW_SUPERIO architecture is actually pretty good. // I think we need to move to the same sort of architecture for // everything: A config file generated sequence of calls @@ -189,12 +203,14 @@ void hardwaremain(int boot_complete) printk_info("totalram: %ldM\n", totalram/1024); /* Fully initialize the cpu before configuring the bus */ - cpu_initialize(totalram); + boot_cpu = cpu_initialize(totalram); + printk_spew("BOOT CPU is %d\n", boot_cpu); + processor_map[boot_cpu] = CPU_BOOTPROCESSOR|CPU_ENABLED; /* Now start the other cpus initializing * The sooner they start the sooner they stop. */ - startup_other_cpus(); + startup_other_cpus(processor_map); // Now do the real bus // we round the total ram up a lot for thing like the SISFB, which @@ -233,7 +249,7 @@ void hardwaremain(int boot_complete) pci_zero_irq_settings(); /* copy the smp block to address 0 */ - write_smp_table((void *)16); + write_smp_table((void *)16, processor_map); post_code(0x96); check_pirq_routing_table(); diff --git a/src/arch/i386/smp/mpspec.c b/src/arch/i386/smp/mpspec.c index ab403282c4..014b286877 100644 --- a/src/arch/i386/smp/mpspec.c +++ b/src/arch/i386/smp/mpspec.c @@ -90,7 +90,8 @@ void smp_write_processor(struct mp_config_table *mc, * Having the proper apicid's in the table so the non-bootstrap * processors can be woken up should be enough. */ -void smp_write_processors(struct mp_config_table *mc) +void smp_write_processors(struct mp_config_table *mc, + unsigned long *processor_map) { int i; int processor_id; @@ -105,7 +106,7 @@ void smp_write_processors(struct mp_config_table *mc) cpu_feature_flags = edx; for(i = 0; i < MAX_CPUS; i++) { smp_write_processor(mc, i, apic_version, - ((processor_id == i)? CPU_BOOTPROCESSOR:0) | CPU_ENABLED, + processor_map[i], cpu_features, cpu_feature_flags ); diff --git a/src/arch/i386/smp/start_stop.c b/src/arch/i386/smp/start_stop.c index fd9aa8dcc9..5f086d2c56 100644 --- a/src/arch/i386/smp/start_stop.c +++ b/src/arch/i386/smp/start_stop.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -57,7 +58,10 @@ void stop_cpu(int apicid) } -void start_cpu(int apicid) +// This is a lot more paranoid now, since Linux can NOT handle +// being told there is a CPU when none exists. So any errors +// will return 0, meaning no CPU. +int start_cpu(int apicid) { int timeout; unsigned long send_status, accept_status, start_eip; @@ -87,7 +91,10 @@ void start_cpu(int apicid) send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); if (timeout >= 1000) { - printk_spew("timed out\n"); + printk_err("CPU %d: First apic write timed out. Disabling\n", + apicid); + // too bad. + return 0; } mdelay(10); @@ -108,7 +115,10 @@ void start_cpu(int apicid) send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); if (timeout >= 1000) { - printk_spew("timed out\n"); + printk_err("CPU %d: Second apic write timed out. Disabling\n", + apicid); + // too bad. + return 0; } /* FIXME start_eip should be more flexible! */ @@ -178,9 +188,12 @@ void start_cpu(int apicid) printk_warning("APIC never delivered???\n"); if (accept_status) printk_warning("APIC delivery error (%lx).\n", accept_status); + if (send_status || accept_status) + return 0; + return 1; } -void startup_other_cpus(void) +void startup_other_cpus(unsigned long *processor_map) { int apicid = this_processors_id(); int i; @@ -189,6 +202,8 @@ void startup_other_cpus(void) if (i == apicid ) { continue; } - start_cpu(i); + if (start_cpu(i)) + processor_map[i] = CPU_ENABLED; + } } diff --git a/src/include/smp/start_stop.h b/src/include/smp/start_stop.h index 91fc26aa45..b804439206 100644 --- a/src/include/smp/start_stop.h +++ b/src/include/smp/start_stop.h @@ -5,8 +5,8 @@ #include int this_processors_id(void); void stop_cpu(int processor_id); -void start_cpu(int processor_id); -void startup_other_cpus(void); +int start_cpu(int processor_id); +void startup_other_cpus(unsigned long *processor_map); #else #define this_processors_id() 0 #define startup_other_cpus() do {} while(0) diff --git a/src/lib/linuxpci.c b/src/lib/linuxpci.c index 1f257b9120..dcb66e592f 100644 --- a/src/lib/linuxpci.c +++ b/src/lib/linuxpci.c @@ -129,8 +129,26 @@ void pci_get_size(struct pci_dev *dev, unsigned long reg, unsigned long addr) // restore addr pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), addr); + // some broken hardware has read-only registers that do not + // really size correctly. You can tell this if addr == size + // Example: the acer m7229 has BARs 1-4 normally read-only. + // so BAR1 at offset 0x10 reads 0x1f1. If you size that register + // by writing 0xffffffff to it, it will read back as 0x1f1 -- a + // violation of the spec. + // We catch this case and ignore it by settting size and type to 0. + // This incidentally catches the common case where registers + // read back as 0 for both address and size. + if (addr == size) { + printk_err(__FUNCTION__ + "dev_fn 0x%x, register %d, read-only" + " SO, ignoring it\n", + dev->devfn, reg); + printk_err("addr was 0x%x, size was 0x%x\n",addr,size); + type = 0; + size = 0; + } // Now compute the actual size, See PCI Spec 6.2.5.1 ... - if (size & PCI_BASE_ADDRESS_SPACE_IO) { + else if (size & PCI_BASE_ADDRESS_SPACE_IO) { type = size & (~PCI_BASE_ADDRESS_IO_MASK); size &= (PCI_BASE_ADDRESS_IO_MASK); // BUG! Top 16 bits can be zero (or not) diff --git a/src/mainboard/intel/l440gx/mptable.c b/src/mainboard/intel/l440gx/mptable.c index 63224a04e1..fa13423433 100644 --- a/src/mainboard/intel/l440gx/mptable.c +++ b/src/mainboard/intel/l440gx/mptable.c @@ -2,7 +2,7 @@ #include #include -void smp_write_config_table(void *v) +void smp_write_config_table(void *v, unsigned long * processor_map) { int ioapicid = 0; static const char sig[4] = "PCMP"; @@ -28,7 +28,7 @@ void smp_write_config_table(void *v) mc->reserved = 0; - smp_write_processors(mc); + smp_write_processors(mc, processor_map); ioapicid = 2; smp_write_bus(mc, 0, "PCI "); @@ -122,10 +122,10 @@ void smp_write_config_table(void *v) mc, smp_next_mpe_entry(mc)); } -void write_smp_table(void *v) +void write_smp_table(void *v, unsigned long *processor_map) { smp_write_floating_table(v); - smp_write_config_table(v); + smp_write_config_table(v, processor_map); }