
- MetalSVM detects the APIC and initializes the APIC timer - If an a APIC is available, the PIC timer will be disabled - SMP is currently not supported git-svn-id: http://svn.lfbs.rwth-aachen.de/svn/scc/trunk/MetalSVM@233 315a16e6-25f9-4109-90ae-ca3045a26c18
227 lines
6.2 KiB
C
227 lines
6.2 KiB
C
/*
|
|
* 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 <metalsvm/time.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/irqflags.h>
|
|
#include <asm/apic.h>
|
|
#include <asm/multiboot.h>
|
|
|
|
#define MP_FLT_SIGNATURE 0x5f504d5f
|
|
|
|
#define APIC_VERSION 0x0030 // Local APIC Version Register
|
|
#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_PMC 0x0340 // LVT Performance Monitoring Counters Register
|
|
#define APIC_LINT0 0x0350 // LVT LINT0 Register
|
|
#define APIC_LINT1 0x0360 // LVT LINT1 Register
|
|
#define APIC_LVT_ER 0x0370 // LVT Error Register
|
|
#define APIC_ICR 0x0380 // Initial Count Register
|
|
#define APIC_CCR 0x0390 // Current Count Register
|
|
#define APIC_DCR 0x03E0 // Divide Configuration Register
|
|
|
|
static apic_mp_t* apic_mp = NULL;
|
|
static apic_config_table_t* apic_config = NULL;
|
|
static uint32_t ncores = 1;
|
|
|
|
#ifndef CONFIG_MULTIBOOT
|
|
static unsigned int* search_apic(unsigned int base, unsigned int limit) {
|
|
unsigned int *ptr;
|
|
|
|
for (ptr = (unsigned int *) base; (unsigned int) ptr < limit; ptr++) {
|
|
if (*ptr == MP_FLT_SIGNATURE)
|
|
return ptr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Send a 'End of Interrupt' command to the APIC
|
|
*/
|
|
void apic_eoi(void)
|
|
{
|
|
*((uint32_t*) (apic_config->lapic+APIC_EOI)) = 0;
|
|
}
|
|
|
|
/*
|
|
* detects the timer frequency of the APIC and restart
|
|
* the APIC timer with the correct period
|
|
*/
|
|
int apic_calibration(void)
|
|
{
|
|
uint64_t ticks, old;
|
|
uint32_t diff;
|
|
|
|
if (!has_apic())
|
|
return -ENXIO;
|
|
|
|
old = get_clock_tick();
|
|
|
|
/* wait for the next time slice */
|
|
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;
|
|
|
|
/* wait 3 time slices to determine a ICR */
|
|
while(get_clock_tick() - ticks < 3)
|
|
;
|
|
|
|
diff = 0xFFFFFFFF - *((uint32_t*) (apic_config->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;
|
|
|
|
// Now, MetalSVM is able to use the APIC => Therefore, we disable the PIC
|
|
outportb(0xA1, 0xFF);
|
|
outportb(0x21, 0xFF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int apic_probe(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++;
|
|
}
|
|
}
|
|
#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;
|
|
|
|
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;
|
|
|
|
i = *((uint32_t*) (apic_config->lapic+APIC_VERSION));
|
|
kprintf("Found APIC at 0x%x\n", apic_config->lapic);
|
|
kprintf("Maximum LVT Entry: 0x%x\n", (i >> 16) & 0xFF);
|
|
kprintf("APIC Version: 0x%x\n", i & 0xFF);
|
|
|
|
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;
|
|
}
|
|
|
|
return 0;
|
|
|
|
out:
|
|
apic_mp = NULL;
|
|
apic_config = NULL;
|
|
ncores = 1;
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
int apic_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = apic_probe();
|
|
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
|
|
|
|
return 0;
|
|
}
|
|
|
|
int has_apic(void)
|
|
{
|
|
return ((apic_mp != NULL) && (apic_config != NULL));
|
|
}
|