From bc67c946affa9f74150711e13de3b9ce9b96e725 Mon Sep 17 00:00:00 2001 From: stefan Date: Fri, 26 Nov 2010 05:33:02 +0000 Subject: [PATCH] - add APIC support for the SCC - seems to work, only the APIC timer doesn't work! git-svn-id: http://svn.lfbs.rwth-aachen.de/svn/scc/trunk/MetalSVM@273 315a16e6-25f9-4109-90ae-ca3045a26c18 --- arch/x86/include/asm/scc.h | 2 + arch/x86/kernel/apic.c | 125 ++++++++++++++++++++++++++----------- arch/x86/kernel/entry.asm | 2 +- arch/x86/kernel/irq.c | 11 +++- arch/x86/kernel/isrs.c | 8 ++- arch/x86/kernel/scc.c | 46 +++++++++++++- arch/x86/kernel/timer.c | 6 ++ 7 files changed, 155 insertions(+), 45 deletions(-) diff --git a/arch/x86/include/asm/scc.h b/arch/x86/include/asm/scc.h index e5b4a758..1775031a 100644 --- a/arch/x86/include/asm/scc.h +++ b/arch/x86/include/asm/scc.h @@ -87,6 +87,8 @@ typedef struct { typedef struct { uint32_t pid; + uint32_t tile_frequency; // in MHz + uint32_t router_frequency; // in MHz mem_region_t private_mem[SCC_PMEM_REGIONS]; } scc_info_t; diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c index 8501ffb1..50bf0caa 100644 --- a/arch/x86/kernel/apic.c +++ b/arch/x86/kernel/apic.c @@ -34,7 +34,7 @@ #define APIC_TPR 0x0080 // Task Priority Regster #define APIC_EOI 0x00B0 // EOI Register #define APIC_SVR 0x00F0 // Spurious Interrupt Vector Register -#define APIC_LVT_T 0x0320 // LVT Thermal Sensor Register (P4/Xeon only) +#define APIC_LVT_T 0x0320 // LVT Timer Register #define APIC_LVT_PMC 0x0340 // LVT Performance Monitoring Counters Register #define APIC_LINT0 0x0350 // LVT LINT0 Register #define APIC_LINT1 0x0360 // LVT LINT1 Register @@ -45,8 +45,14 @@ static apic_mp_t* apic_mp = NULL; static apic_config_table_t* apic_config = NULL; +static uint32_t lapic = 0; static uint32_t ncores = 1; +static inline void apic_write(uint32_t addr, uint32_t value) +{ + asm volatile ("movl (%%eax), %%edx; movl %%ebx, (%%eax)" :: "a"(addr), "b"(value) : "%edx"); +} + #ifndef CONFIG_MULTIBOOT static unsigned int* search_apic(unsigned int base, unsigned int limit) { unsigned int *ptr; @@ -65,7 +71,8 @@ static unsigned int* search_apic(unsigned int base, unsigned int limit) { */ void apic_eoi(void) { - *((uint32_t*) (apic_config->lapic+APIC_EOI)) = 0; + if (lapic) + apic_write(lapic+APIC_EOI, 0); } /* @@ -74,6 +81,7 @@ void apic_eoi(void) */ int apic_calibration(void) { +#ifndef CONFIG_ROCKCREEK uint64_t ticks, old; uint32_t diff; @@ -86,23 +94,55 @@ int apic_calibration(void) while((ticks = get_clock_tick()) - old == 0) ; - *((uint32_t*) (apic_config->lapic+APIC_DCR)) = 0xB; // set it to 1 clock increments - *((uint32_t*) (apic_config->lapic+APIC_LVT_T)) = 0x20030; // connects the timer to 48 and enables it - *((uint32_t*) (apic_config->lapic+APIC_ICR)) = 0xFFFFFFFF; + apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments + apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it + apic_write(lapic+APIC_ICR), 0xFFFFFFFF); /* wait 3 time slices to determine a ICR */ while(get_clock_tick() - ticks < 3) ; - diff = 0xFFFFFFFF - *((uint32_t*) (apic_config->lapic+APIC_CCR)); + diff = 0xFFFFFFFF - *((uint32_t*) (lapic+APIC_CCR)); - *((uint32_t*) (apic_config->lapic+APIC_DCR)) = 0xB; // set it to 1 clock increments - *((uint32_t*) (apic_config->lapic+APIC_LVT_T)) = 0x20030; // connects the timer to 48 and enables it - *((uint32_t*) (apic_config->lapic+APIC_ICR)) = diff / 3; + apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments + apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it + apic_write(lapic+APIC_ICR, diff / 3); // Now, MetalSVM is able to use the APIC => Therefore, we disable the PIC outportb(0xA1, 0xFF); outportb(0x21, 0xFF); +#else + /* + * On the SCC, we already know the processor frequency + * and possess no PIC timer. Therfore, we use the rdtsc to + * to calibrate the APIC timer. + */ + uint64_t start, end, ticks; + uint32_t diff; + + if (!has_apic()) + return -ENXIO; + + apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments + apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it + apic_write(lapic+APIC_ICR, 0xFFFFFFFF); + + /* wait 3 time slices to determine a ICR */ + start = rdtsc(); + do { + flush_pipeline(); + end = rdtsc(); + ticks = end > start ? end - start : start - end; + } while(ticks < 3*scc_info.tile_frequency*1000000 / TIMER_FREQ); + + diff = 0xFFFFFFFF - *((uint32_t*) (lapic+APIC_CCR)); + + apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments + apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it + apic_write(lapic+APIC_ICR, diff / 3); +#endif + + kprintf("APIC calibration detects an ICR of 0x%x\n", diff / 3); return 0; } @@ -110,7 +150,7 @@ int apic_calibration(void) static int apic_probe(void) { size_t addr; - uint32_t i, count, dummy; + uint32_t i, count; // searching MP signature in the reserved memory areas #ifdef CONFIG_MULTIBOOT @@ -133,28 +173,28 @@ static int apic_probe(void) mmap++; } } +found_mp: #else apic_mp = (apic_mp_t*) search_apic(0xF0000, 0x100000); if (!apic_mp) apic_mp = (apic_mp_t*) search_apic(0x9F000, 0xA0000); #endif -found_mp: if (!apic_mp) - goto out; + goto no_mp; kprintf("System uses Multiprocessing Specification 1.%d\n", apic_mp->version); kprintf("MP features 1: %d\n", apic_mp->features[0]); if (apic_mp->features[0]) { kputs("Currently, MetalSVM supports only multiprocessing via the MP config tables!\n"); - goto out; + goto no_mp; } apic_config = (apic_config_table_t*) apic_mp->mp_config; if (!apic_config || strncmp((void*) &apic_config->signature, "PCMP", 4) !=0) { kputs("Invalid MP config table\n"); - goto out; + goto no_mp; } addr = (size_t) apic_config; @@ -172,34 +212,45 @@ found_mp: if (count > MAX_CORES) { kputs("Found too many cores! Increase the macro MAX_CORES!\n"); - goto out; + goto no_mp; } ncores = count; - i = *((uint32_t*) (apic_config->lapic+APIC_VERSION)); - kprintf("Found APIC at 0x%x\n", apic_config->lapic); +check_lapic: + if (apic_config) { + lapic = apic_config->lapic; + } else { + uint32_t eax, edx, dummy; + + cpuid(0x1, &eax, &dummy, &dummy, &edx); + if (edx & (1 << 9)) + lapic = 0xFEE00000; + + kprintf("Processor familiy %u, model %u, stepping %u\n", (eax>>8)&0xF, (eax>>4)&0xF, eax&0xF); + } + + if (!lapic) + goto out; + + i = *((uint32_t*) (lapic+APIC_VERSION)); + kprintf("Found APIC at 0x%x\n", lapic); kprintf("Maximum LVT Entry: 0x%x\n", (i >> 16) & 0xFF); kprintf("APIC Version: 0x%x\n", i & 0xFF); - cpuid(0x1, &i, &dummy, &dummy, &dummy); - if (!(i & (1 << 5))) { - kputs("Unable to use Machine-Specific Registers (MSR)\n"); - goto out; - } - - if (!(rdmsr(0x1B) & (1 << 11))) { - kputs("Unable to use APIC Global Enable flag!\n"); - goto out; - } - return 0; out: apic_mp = NULL; apic_config = NULL; + lapic = 0; ncores = 1; - return -ENXIO; + +no_mp: + apic_mp = NULL; + apic_config = NULL; + ncores = 1; + goto check_lapic; } int apic_init(void) @@ -210,18 +261,18 @@ int apic_init(void) if (!ret) return ret; - *((uint32_t*) (apic_config->lapic+APIC_TPR)) = 0x20; // inhibit softint delivery - *((uint32_t*) (apic_config->lapic+APIC_LVT_T)) = 0x10000; // disable timer interrupt - *((uint32_t*) (apic_config->lapic+APIC_LVT_PMC)) = 0x10000; // disable performance counter interrupt - *((uint32_t*) (apic_config->lapic+APIC_LINT0)) = 0x31; // connect LINT0 to idt entry 49 - *((uint32_t*) (apic_config->lapic+APIC_LINT1)) = 0x32; // connect LINT1 to idt entry 50 - *((uint32_t*) (apic_config->lapic+APIC_LVT_ER)) = 0x33; // connect error to idt entry 51 - *((uint32_t*) (apic_config->lapic+APIC_SVR)) = 0x134; // enable the apic and connect to the idt entry 52 + apic_write(lapic+APIC_TPR, 0x00); // allow all interrupts + apic_write(lapic+APIC_LVT_T, 0x10000); // disable timer interrupt + apic_write(lapic+APIC_LVT_PMC, 0x10000);// disable performance counter interrupt + apic_write(lapic+APIC_LINT0, 0x31); // connect LINT0 to idt entry 49 + apic_write(lapic+APIC_LINT1, 0x32); // connect LINT1 to idt entry 50 + apic_write(lapic+APIC_LVT_ER, 0x33); // connect error to idt entry 51 + apic_write(lapic+APIC_SVR, 0x17F); // enable the apic and connect to the idt entry 207 return 0; } int has_apic(void) { - return ((apic_mp != NULL) && (apic_config != NULL)); + return (lapic != 0); } diff --git a/arch/x86/kernel/entry.asm b/arch/x86/kernel/entry.asm index 94016d6b..b1b3a9a7 100644 --- a/arch/x86/kernel/entry.asm +++ b/arch/x86/kernel/entry.asm @@ -718,7 +718,7 @@ apic_svr: ; Therefore, the interrupt flag (IF) is already cleared. ; cli push byte 0 - push byte 52 + push byte 127 jmp irq_common_stub diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index a624fd5e..e56bbab5 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -51,7 +51,7 @@ extern void apic_lint1(void); extern void apic_error(void); extern void apic_svr(void); -#define MAX_HANDLERS 32 +#define MAX_HANDLERS 256 /* * This array is actually an array of function pointers. We use @@ -93,6 +93,7 @@ int irq_uninstall_handler(unsigned int irq) */ static int irq_remap(void) { +#ifndef CONFIG_ROCKCREEK outportb(0x20, 0x11); outportb(0xA0, 0x11); outportb(0x21, 0x20); @@ -103,6 +104,7 @@ static int irq_remap(void) outportb(0xA1, 0x01); outportb(0x21, 0x0); outportb(0xA1, 0x0); +#endif return 0; } @@ -158,7 +160,7 @@ static int irq_install(void) IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(51, (unsigned)apic_error, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); - idt_set_gate(52, (unsigned)apic_svr, KERNEL_CODE_SELECTOR, + idt_set_gate(127, (unsigned)apic_svr, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); } @@ -194,6 +196,11 @@ void irq_handler(struct state *s) /* This is a blank function pointer */ void (*handler) (struct state * s); + if (s->int_no - 32 > MAX_HANDLERS) { + apic_eoi(); + return; + } + /* * Find out if we have a custom handler to run for this * IRQ and then finally, run it diff --git a/arch/x86/kernel/isrs.c b/arch/x86/kernel/isrs.c index c226ddcd..9db010ae 100644 --- a/arch/x86/kernel/isrs.c +++ b/arch/x86/kernel/isrs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include /* @@ -172,9 +173,12 @@ void fault_handler(struct state *s) kputs(" Exception.\n"); /* Now, we signalize that we have handled the interrupt */ - outportb(0x20, 0x20); - irq_enable(); + if (has_apic()) + apic_eoi(); + else + outportb(0x20, 0x20); + irq_enable(); abort(); } } diff --git a/arch/x86/kernel/scc.c b/arch/x86/kernel/scc.c index 9e918da3..3d09db03 100644 --- a/arch/x86/kernel/scc.c +++ b/arch/x86/kernel/scc.c @@ -18,25 +18,29 @@ */ #include +#include #include #include #ifdef CONFIG_ROCKCREEK +#define CCR_INTR_ACTIVE 0x02 + scc_info_t scc_info; int scc_init(void) { - int tmp, x, y, z; + uint32_t x, y, z; + uint32_t tmp; kprintf("Initialize Rock Creek!\n"); - tmp = *((int*) (CRB_OWN+MYTILEID)); + tmp = *((uint32_t*) (CRB_OWN+MYTILEID)); x=(tmp>>3) & 0x0f; // bits 06:03 y=(tmp>>7) & 0x0f; // bits 10:07 z=(tmp ) & 0x07; // bits 02:00 scc_info.pid = PID(x, y, z); - kprintf("SCC Processor Id: %d (%d,%d,%d)\n", scc_info.pid, x, y, z); + kprintf("SCC Processor Id: %u (%u,%u,%u)\n", scc_info.pid, x, y, z); /* default values for 16 GB system */ scc_info.private_mem[0].low = 0x00; @@ -44,6 +48,42 @@ int scc_init(void) scc_info.private_mem[1].low = 0xFF000000; scc_info.private_mem[1].high = 0xFFFFFFFF; + tmp = *((uint32_t*) (CRB_OWN+GCBCFG)); + tmp = (tmp & 0x3FFFFFF) >> 7; + //kprintf("Own GCBCFG is 0x%x\n", tmp); + if (tmp == 0x70E1) { + scc_info.tile_frequency = 800; + scc_info.router_frequency = 1600; + } else { + scc_info.tile_frequency = 533; + scc_info.router_frequency = 800; + } + + kprintf("The default tile frequency is %u MHz\nThe default router frequency is %u MHz\n", + scc_info.tile_frequency, scc_info.router_frequency); + + if (z == 0) + tmp = *((uint32_t*) (CRB_OWN+GLCFG0)); + else if (z == 1) + tmp = *((uint32_t*) (CRB_OWN+GLCFG1)); + else + tmp = 0; + + /* set INTR to enable maskable interrupts */ + tmp = tmp | CCR_INTR_ACTIVE; + if (z == 0) + *((uint32_t*) (CRB_OWN+GLCFG0)) = tmp; + else if (z == 1) + *((uint32_t*) (CRB_OWN+GLCFG1)) = tmp; + + /* reload core configuration */ + tmp = 0; + if (z == 0) + tmp = *((uint32_t*) (CRB_OWN+GLCFG0)); + else if (z == 1) + tmp = *((uint32_t*) (CRB_OWN+GLCFG1)); + kprintf("Core Configuration %u: 0x%x\n", z, tmp); + return 0; } diff --git a/arch/x86/kernel/timer.c b/arch/x86/kernel/timer.c index f9211e14..fb113385 100644 --- a/arch/x86/kernel/timer.c +++ b/arch/x86/kernel/timer.c @@ -87,6 +87,11 @@ int timer_init(void) irq_install_handler(0, timer_handler); irq_install_handler(16, timer_handler); + /* + * The Rock Creek processor doesn't posseess an tradional PIC. + * Therefore, we have not to configure the PIC timer. + */ +#ifndef CONFIG_ROCKCREEK outportb(0x43, 0x34); /* before we write to 0x40, we wait some time */ start = rdtsc(); @@ -98,6 +103,7 @@ int timer_init(void) while(rdtsc() - start < 1000000) ; outportb(0x40, LATCH(TIMER_FREQ) >> 8); /* high byte */ +#endif return 0; }