- 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:
parent
01ff479e11
commit
08cbc0a257
7 changed files with 195 additions and 45 deletions
|
@ -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
|
||||
|
|
|
@ -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
147
arch/x86/kernel/apic.c
Normal 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);
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue