From 08cbc0a2574a64f72057f86b668cf9b3aa6342f7 Mon Sep 17 00:00:00 2001 From: stefan Date: Mon, 25 Oct 2010 16:58:31 +0000 Subject: [PATCH] - add the first steps to support the (local and IO) APIC - remove some typos in the comments git-svn-id: http://svn.lfbs.rwth-aachen.de/svn/scc/trunk/MetalSVM@196 315a16e6-25f9-4109-90ae-ca3045a26c18 --- arch/x86/include/asm/processor.h | 16 ++++ arch/x86/kernel/Makefile | 2 +- arch/x86/kernel/apic.c | 147 +++++++++++++++++++++++++++++++ arch/x86/kernel/irq.c | 12 +-- arch/x86/kernel/isrs.c | 6 +- arch/x86/kernel/schedule.asm | 8 +- arch/x86/kernel/timer.c | 49 ++++------- 7 files changed, 195 insertions(+), 45 deletions(-) create mode 100644 arch/x86/kernel/apic.c diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 35aa6aac..83b0e808 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -22,6 +22,7 @@ #include #include +#include #ifdef CONFIG_PCI #include #endif @@ -65,6 +66,20 @@ inline static void rmb(void) { asm volatile("lfence" ::: "memory"); } inline static void wmb(void) { asm volatile("sfence" ::: "memory"); } #endif +inline static uint32_t cpuid(uint32_t code, uint32_t* feature) { + uint32_t ret; + asm volatile("cpuid" : "=a"(ret), "=d"(*feature) : "0"(code)); + return ret; +} + +inline static uint64_t rdmsr(uint32_t msr) { + uint32_t low, high; + + asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr)); + + return ((uint64_t)high << 32) | low; +} + #define NOP1 asm volatile ("nop") #define NOP2 asm volatile ("nop;nop") #define NOP4 asm volatile ("nop;nop;nop;nop") @@ -76,6 +91,7 @@ inline static int system_init(void) scc_init(); #endif gdt_install(); + apic_init(); #ifdef CONFIG_PCI pci_init(); #endif diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index b19470ee..dc9ad173 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -1,4 +1,4 @@ -C_source = scc.c gdt.c kb.c timer.c irq.c isrs.c idt.c vga.c multiboot.c pci.c +C_source = scc.c gdt.c kb.c timer.c irq.c isrs.c idt.c vga.c multiboot.c apic.c pci.c ASM_source = entry.asm string.asm OBJS += $(patsubst %.c, %.o, $(filter %.c, $(C_source))) diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c new file mode 100644 index 00000000..95c0f8e2 --- /dev/null +++ b/arch/x86/kernel/apic.c @@ -0,0 +1,147 @@ +/* + * Copyright 2010 Stefan Lankes, Chair for Operating Systems, + * RWTH Aachen University + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of MetalSVM. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LAPIC_VERSION 0x0030 +#define LAPIC_SVR 0x00F0 + +// 8259 controllers' I/O ports +#define MAST_8259_APORT 0x20 +#define MAST_8259_DPORT 0x21 +#define SLAV_8259_APORT 0xA0 +#define SLAV_8259_DPORT 0xA1 + + +static apic_mp_t* apic_mp = NULL; +static apic_config_table_t* apic_config = NULL; +static uint32_t ncores = 1; + +int apic_init(void) +{ + size_t addr; + uint32_t i, count; + + // searching MP signature in the reserved memory areas +#ifdef CONFIG_MULTIBOOT + if (mb_info && (mb_info->flags & (1 << 6))) { + multiboot_memory_map_t* mmap = (multiboot_memory_map_t*) mb_info->mmap_addr; + multiboot_memory_map_t* mmap_end = (void*) ((size_t) mb_info->mmap_addr + mb_info->mmap_length); + + while (mmap < mmap_end) { + if (mmap->type == MULTIBOOT_MEMORY_RESERVED) { + addr = mmap->addr; + + for(i=0; ilen; i++, addr++) { + if (strncmp((void*)addr, "_MP_", 4) == 0) { + apic_mp = (apic_mp_t*) addr; + goto found_mp; + } + } + + } + mmap++; + } + } +#endif +found_mp: + + if (!apic_mp) + goto out; + + 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; + } + + 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; + } + + addr = (size_t) apic_config; + addr += sizeof(apic_config_table_t); + if (addr % 4) + addr += 4 - addr % 4; + + for(i=0, count=0; ientry_count; i++) { + if (*((uint8_t*) addr) == 0) { + count++; + addr += 20; + } else addr += 8; + } + kprintf("Found %d cores\n", count); + + if (count > MAX_CORES) { + kputs("Found too many cores! Increase the macro MAX_CORES!\n"); + goto out; + } + ncores = count; + + addr = apic_config->lapic; + i = *((uint32_t*) (addr+LAPIC_VERSION)); + kprintf("Found LAPIC at 0x%x\n", addr); + kprintf("Maximum LVT Entry: 0x%x\n", (i >> 16) & 0xFF); + kprintf("LAPIC Version: 0x%x\n", i & 0xFF); +#if 0 + cpuid(0x1, &i); + 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; + } + + i = *((uint32_t*) (addr+LAPIC_SVR)); + kprintf("Supurious Interrupt Vector Register: %x\n", i); + i = i | (1 << 8); + *((uint32_t*) (addr+LAPIC_SVR)) = i; + + // Disable the 8259's because we are going to use the IOAPIC for interrupt processing + outportb(MAST_8259_DPORT, 0xFF); // OCW1 master: inhibit all interrupts + udelay(100); + outportb(SLAV_8259_DPORT, 0xFF); // OCW1 slave: inhibit all interrupts + + return 0; +#endif +out: + apic_mp = NULL; + apic_config = NULL; + ncores = 1; + + return -EINVAL; +} + +int has_apic(void) +{ + return (apic_mp != NULL); +} diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 46c8722d..ef5adbe1 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -24,7 +24,7 @@ #include /* - * These are own ISRs that point to our special IRQ handler + * These are our own ISRs that point to our special IRQ handler * instead of the regular 'fault_handler' function */ extern void irq0(void); @@ -66,8 +66,8 @@ void irq_uninstall_handler(unsigned int irq) * Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This * is a problem in protected mode, because IDT entry 8 is a * Double Fault! Without remapping, every time IRQ0 fires, - * you get a Double Fault Exception, which is NOT actually - * what's happening. We send commands to the Programmable + * you get a Double Fault Exception, which is NOT what's + * actually happening. We send commands to the Programmable * Interrupt Controller (PICs - also called the 8259's) in * order to make IRQ0 to 15 be remapped to IDT entries 32 to * 47 @@ -140,8 +140,8 @@ void irq_init(void) * the 'fault_handler' in 'isrs.c'. The IRQ Controllers need * to be told when you are done servicing them, so you need * to send them an "End of Interrupt" command (0x20). There - * are two 8259 chips: The first exists at 0x20, the second - * exists at 0xA0. If the second controller (an IRQ from 8 to + * are two 8259 chips: The first one exists at 0x20, the second + * one exists at 0xA0. If the second controller (an IRQ from 8 to * 15) gets an interrupt, you need to acknowledge the * interrupt at BOTH controllers, otherwise, you only send * an EOI command to the first controller. If you don't send @@ -154,7 +154,7 @@ void irq_handler(struct state *s) /* * Find out if we have a custom handler to run for this - * IRQ, and then finally, run it + * IRQ and then finally, run it */ handler = irq_routines[s->int_no - 32]; if (handler) { diff --git a/arch/x86/kernel/isrs.c b/arch/x86/kernel/isrs.c index a82efe39..c226ddcd 100644 --- a/arch/x86/kernel/isrs.c +++ b/arch/x86/kernel/isrs.c @@ -27,7 +27,7 @@ /* * These are function prototypes for all of the exception * handlers: The first 32 entries in the IDT are reserved - * by Intel, and are designed to service exceptions! + * by Intel and are designed to service exceptions! */ extern void isr0(void); extern void isr1(void); @@ -144,7 +144,7 @@ void isrs_install(void) /* * This is a simple string array. It contains the message that * corresponds to each and every exception. We get the correct - * message by accessing like: + * message by accessing it like this: * exception_message[interrupt_number] */ static const char *exception_messages[] = { @@ -160,7 +160,7 @@ static const char *exception_messages[] = { /* * All of our Exception handling Interrupt Service Routines will * point to this function. This will tell us what exception has - * happened! Right now, we simply abort the current task. + * occured! Right now, we simply abort the current task. * All ISRs disable interrupts while they are being * serviced as a 'locking' mechanism to prevent an IRQ from * happening and messing up kernel data structures diff --git a/arch/x86/kernel/schedule.asm b/arch/x86/kernel/schedule.asm index 05ff928a..0a4b0286 100644 --- a/arch/x86/kernel/schedule.asm +++ b/arch/x86/kernel/schedule.asm @@ -31,12 +31,12 @@ extern current_task ; ... ; } task_t; -; After an interrupt, the original return address has to push on the stack -; because we want to comeback... +; After an interrupt, the original return address has to be pushed +; on the stack because we want to comeback... global schedule_entry schedule_entry: cli - sub esp, 4 ; add some space for the retunr address, + sub esp, 4 ; add some space for the return address, ; which is stored in current_task->ip pushfd @@ -64,7 +64,7 @@ schedule_entry: jmp L1 -; Scheduler, which switchs to the new tasks +; Scheduler, which switches to the new tasks global schedule schedule: ret diff --git a/arch/x86/kernel/timer.c b/arch/x86/kernel/timer.c index cc06cf16..9a2f19c0 100644 --- a/arch/x86/kernel/timer.c +++ b/arch/x86/kernel/timer.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -29,11 +30,10 @@ #include /* - * This will keep track of how many ticks that the system + * This will keep track of how many ticks the system * has been running for */ static volatile uint64_t timer_ticks = 0; -static unsigned int curr_timer_freq = 18; /* * Handles the timer. In this case, it's very simple: We @@ -66,44 +66,31 @@ void timer_wait(unsigned int ticks) reschedule(); } +#define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f) + /* * Sets up the system clock by installing the timer handler * into IRQ0 */ int timer_init(void) { + uint64_t start; + /* Installs 'timer_handler' to IRQ0 */ irq_install_handler(0, timer_handler); - return 0; -} - -#define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f) - -int timer_set_frequency(unsigned int freq) -{ - irq_disable(); - - /* set the timer frequency */ -#ifdef CONFIG_LAPICTIMER - /* Use LAPIC timer instead of 8254 PIT */ -#else - outportb(0x43, 0x34); - udelay(10); - outportb(0x40, LATCH(freq) & 0xFF); /* low byte */ - udelay(10); - outportb(0x40, LATCH(freq) >> 8); /* high byte */ -#endif - - curr_timer_freq = freq; - - irq_enable(); + if (!has_apic()) { + /* set default timer */ + outportb(0x43, 0x34); + start = rdtsc(); + while(rdtsc() - start < 1000000) + ; + outportb(0x40, LATCH(TIMER_FREQ) & 0xFF); /* low byte */ + start = rdtsc(); + while(rdtsc() - start < 1000000) + ; + outportb(0x40, LATCH(TIMER_FREQ) >> 8); /* high byte */ + } return 0; } - -unsigned int timer_get_frequency(void) -{ - return curr_timer_freq; -} -