- 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
This commit is contained in:
stefan 2010-10-25 16:58:31 +00:00
parent 01ff479e11
commit 08cbc0a257
7 changed files with 195 additions and 45 deletions

View file

@ -22,6 +22,7 @@
#include <metalsvm/stddef.h>
#include <asm/gdt.h>
#include <asm/apic.h>
#ifdef CONFIG_PCI
#include <asm/pci.h>
#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

View file

@ -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)))

147
arch/x86/kernel/apic.c Normal file
View file

@ -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 <metalsvm/stddef.h>
#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/errno.h>
#include <metalsvm/processor.h>
#include <asm/apic.h>
#include <asm/multiboot.h>
#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; i<mmap->len; 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; i<apic_config->entry_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);
}

View file

@ -24,7 +24,7 @@
#include <asm/isrs.h>
/*
* 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) {

View file

@ -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

View file

@ -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

View file

@ -22,6 +22,7 @@
#include <metalsvm/tasks.h>
#include <metalsvm/time.h>
#include <metalsvm/processor.h>
#include <asm/apic.h>
#include <asm/irq.h>
#include <asm/irqflags.h>
#include <asm/gdt.h>
@ -29,11 +30,10 @@
#include <asm/vga.h>
/*
* 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;
}