blob: bdde2a3631a7ef12cad89f9c446acd72199ee4b9 [file] [log] [blame] [edit]
/*
* Copyright (c) 2009 The Regents of the University of California
* See LICENSE for details.
*/
#ifdef __SHARC__
#pragma nosharc
#endif
// Not currently sharc complient. However
// we should never be modifying these structures post smp_boot().
#include <arch/ioapic.h>
#include <arch/pci.h>
#include <arch/mptables.h>
#include <ros/common.h>
#include <stdio.h>
#include <string.h>
#include <kmalloc.h>
#include <arch/x86.h>
/** @file
* @brief Basic MP Tables Parser
*
* This file is responsible for locating, checksuming, and parsing the
* MultiProcessor Specification Tables.
*
* See Intel Multiprocessor Specification for more info
*
* @author Paul Pearce <pearce@eecs.berkeley.edu>
*
* @todo Extended table support (why?)
* @todo Expanded error checking?
* @todo virtaddr_t to match physaddr_t support?
*/
// Important global items
enum interrupt_modes current_interrupt_mode;
proc_entry_t *COUNT(mp_entries_count[PROC]) mp_proc_entries = NULL;
bus_entry_t *COUNT(mp_entries_count[BUS]) mp_bus_entries = NULL;
ioapic_entry_t *COUNT(mp_entries_count[IOAPIC]) mp_ioapic_entries = NULL;
int_entry_t *COUNT(mp_entries_count[INT]) mp_int_entries = NULL;
int_entry_t *COUNT(mp_entries_count[LINT]) mp_lint_entries = NULL;
// ^ Not a typo. lint entries == int entries, so We just use that.
int mp_entries_count[NUM_ENTRY_TYPES]; // How large each array is.
pci_int_device_t pci_int_devices[PCI_MAX_BUS][PCI_MAX_DEV];
isa_int_entry_t isa_int_entries[NUM_IRQS];
ioapic_entry_t ioapic_entries[IOAPIC_MAX_ID];
/** @brief Entry function to the mptable parser. Calling this function results in the parsing of the tables and setup of all structures
*
* This function does the following:
* - Search various locations in memory for the MP Floating Structure
* - Checkum the floating structure to make sure its valid
* - Locate the MP Configuration Header, and checksum it
* - Locate all entries of type proc, bus, ioapic, int, lint
* - Parse the above entries and form the data structures that the rest of the system relies upon
*/
void mptables_parse() {
// Before we do anything. We didn't pack our structs because BSD didnt. Make sure we're sane.
if ( (sizeof(proc_entry_t) != entry_types[PROC].length) ||
(sizeof(bus_entry_t) != entry_types[BUS].length) ||
(sizeof(ioapic_entry_t) != entry_types[IOAPIC].length) ||
(sizeof(int_entry_t) != entry_types[INT].length) ||
(sizeof(mpfps_t) != MPFPS_SIZE) ||
(sizeof(mpcth_t) != MPCTH_SIZE) )
panic("MPTable structure sizes out of sync with spec");
mpfps_t *mpfps;
// Memsets to initalize all our structures to invalid entries
/* Setup the indexable ioapic array.
* YOU MUST check the flag field to see if its 0. If 0, unusable.
* This is defined by MPTables, and I leaverage this with the memset below to set invalid
*/
memset(ioapic_entries, 0, sizeof(ioapic_entries));
// We define an IOAPIC DEST ID of 0xFF (>=256) to be invalid. Pack with all 1's.
memset(pci_int_devices, 0xFF, sizeof(pci_int_devices));
memset(isa_int_entries, 0xFF, sizeof(isa_int_entries));
mptables_info("Starting MPTables Parsing...\n");
/* Basic procedure:
* 1) Find floating pointer
* 2) Go to addr referenced by floating pointer
* 3) Read table header info
*
* We now have to search through 3 address regions searching for a magic string.
*
*
* Note: The pointer can actually be elsewhere. See the FBSD MPTables implimentation for more info
* Just doesn't make sense right now to check more places.
*/
// Search the BIOS ROM Address Space (MOST LIKELY)
mptables_dump("-->Searching BIOS ROM Area...\n");
mpfps = find_floating_pointer((physaddr_t)KADDR(BIOS_ROM_BASE), (physaddr_t)KADDR(BIOS_ROM_BOUND));
if (mpfps == NULL) {
/* Search the EBDA UNTESTED, haven't found something that uses this.
*
* First, we have to find the EBDA Addr.
* This USSUALLY works (based on third hand info). May be some cases where it doesnt.
* See osdev x86 mem-map for more information
*/
physaddr_t ebda_base = READ_FROM_STORED_PHYSADDR32(EBDA_POINTER);
if (ebda_base) {
ebda_base = ebda_base << 4;
ebda_base = (physaddr_t)KADDR(ebda_base);
physaddr_t ebda_bound = ebda_base + EBDA_SIZE - sizeof(mpfps_t);
mptables_dump("-->Searching EBDA...\n");
mpfps = find_floating_pointer(ebda_base, ebda_bound);
}
}
if (mpfps == NULL) {
/* Search the last KB of system memory UNTESTED
* Note: Will only be there if it not in the EBDA. So this must be called after the EBDA check.
* This logic is ripped from mptables without much understanding. No machine to test it on.
*/
physaddr_t top_of_mem = READ_FROM_STORED_PHYSADDR32(TOPOFMEM_POINTER);
if (top_of_mem) {
--top_of_mem;
top_of_mem = top_of_mem * 1024;
top_of_mem = (physaddr_t)KADDR(top_of_mem);
mptables_dump("-->Searching top of (real mode) Ram...\n");
mpfps = find_floating_pointer(top_of_mem, top_of_mem + 1024 - sizeof(mpfps_t));
}
}
if (mpfps == NULL) {
/* Search the last KB of system memory based on a 640K limited, due to CMOS lying
* Note: Will only be there if it not in the EBDA. So this must be called after the EBDA check.
* This IS tested. Thanks VirtualBox!
*/
physaddr_t top_of_mem = DEFAULT_TOPOFMEM;
if (top_of_mem) {
top_of_mem = top_of_mem - 1024;
top_of_mem = (physaddr_t)KADDR(top_of_mem);
mptables_dump("-->Searching top of (real mode) Ram 640K cap, incase CMOS lied...\n");
mpfps = find_floating_pointer(top_of_mem, top_of_mem + 1024 - sizeof(mpfps_t));
}
}
/* If we can't find the pointer, it means we are running on a non-mp compliant machine.
* This is bad. We can't do interrupts the way we want.
* We could have this trigger a MODE in which we operate using the standard PIC, if we really wanted...
*/
if (mpfps == NULL) {
panic("MPTABLES Not found. IOAPIC and interrupts will not function properly. <Insert whale held up by smaller birds here>");
}
mptables_info("-->MPTables Floating Pointer Structure found @ KVA %p.\n", mpfps);
mptables_info("-->Current Interrupt Mode: ");
// Identify our interrupt mode
if (mpfps->mpfb2 & IMCRP_MASK) {
current_interrupt_mode = PIC;
mptables_info("PIC\n");
// TODO: Do SOMETHING here. We've never found such a system (they are generally ancient). Should we just panic?
}
else {
current_interrupt_mode = VW;
mptables_info("Virtual Wire\n");
}
configuration_parse((physaddr_t)KADDR((uintptr_t)(mpfps->pap)));
proc_parse();
bus_parse();
ioapic_parse();
int_parse();
lint_parse();
}
/** @brief Take the given memory range and search for the MP Floating Structure
*
* This function will look at every sizeof(mpfps_t) chunch of memory for a given 4byte value (_MP_)
* until bound is reached (inclusive).
*
* Note: Doesn't ensure bounds are sane. This shouldnt be an issue as this function should be priviate to mptables_parse()
*
* @param[in] base The base (kernel virtual) address we start looking at
* @param[in] bound The bound (inclusive kernel virtual) address we stop looking at
*
* @return MPFPS The virtual address of the base of the floating point structure
* @return NULL No floating point structure exists in this range
*/
mpfps_t *find_floating_pointer(physaddr_t base, physaddr_t bound) {
uint32_t count = (bound - base + sizeof(mpfps_t))/sizeof(mpfps_t);
// This trusted count was authorized with the blessing of Zach.
// Blame Intel and the MP Spec for making me do this cast.
mpfps_t* mpfps = (mpfps_t* COUNT(count)) TC(base);
// Loop over the entire range looking for the signature. The signature is ascii _MP_, which is
// stored in the given MP_SIG
while ( ((physaddr_t)mpfps <= bound) && (READ_FROM_STORED_VIRTADDR32(mpfps->signature) != MP_SIG)) {
mpfps++;
}
if ((physaddr_t)mpfps > bound)
return NULL;
// Now perform a checksum on the float
if (checksum((physaddr_t)mpfps, sizeof(mpfps_t) * mpfps->length) == FALSE) {
mptables_dump("-->Failed a MPTables Floating Pointer Structure checksum @ KVA 0x%p.\n", mpfps);
// If we fail we need to keep checking. But if we are on the last addr
// we just fail.
if ((physaddr_t)mpfps == bound)
return NULL;
return find_floating_pointer((physaddr_t)(mpfps + 1), bound);
}
return mpfps;
}
/** @brief Perform the mptable checksum on the memory given by addr and len
*
* This function will take len bytes of memory starting at the (kernel virtual)
* address addr and sum them together. If the result is 0, the checksum is valid.
*
* @param[in] addr The base (kernel virtual) address we start checking
* @param[in] len How many bytes to look at
*
* @return TRUE Valid checksum
* @return FALSE Invalid checksum
*/
bool checksum(physaddr_t addr, uint32_t len) {
// MP Table checksums must add up to 0.
uint8_t checksum = 0;
// Yet another trusted cast.
// See comment at start of find_floating_pointer
uint8_t *addr_p = (uint8_t* COUNT(len)) TC(addr);
for (int i = 0; i < len; i++)
checksum += *(addr_p + i);
return (checksum == 0);
}
/** @brief Parse the configuration MP Table given a valid address to the base of the table
*
* This function begin examining a given (kernel virtual) address assuming it is the base
* of the configuration table. It will determine the size of the table, and then loop over
* each entry in the table, loading all entires into the correct corresponding data structures
*
* @param[in] conf_addr The base (kernel virtual) address of the configuration table
*/
void configuration_parse(physaddr_t conf_addr) {
int num_processed[NUM_ENTRY_TYPES];
// Another. See comment at start of find_floating_pointer
mpcth_t *mpcth = (mpcth_t* COUNT(1)) TC(conf_addr);
for (int i = 0; i < NUM_ENTRY_TYPES; i++) {
mp_entries_count[i] = num_processed[i] = 0;
}
// Do 1 pass to figure out how much space to allocate.
// Note: Length here means length in bytes. This is from the mp spec.
uint16_t num_entries = mpcth->entry_count;
uint16_t mpct_length = mpcth->base_table_length;
uint16_t entries_length = mpct_length - sizeof(mpcth);
// Now perform a checksum on the configuration table
if (checksum((physaddr_t)mpcth, mpct_length) == FALSE) {
panic("FAILED MP CONFIGURATION CHECKSUM.");
}
uint8_t * COUNT(entries_length) entry_base = (uint8_t* COUNT(entries_length)) TC(mpcth + 1);
uint8_t * BND(entry_base, entry_base + entries_length) current_addr = entry_base;
for (int i = 0; i < num_entries; i++) {
uint8_t current_type = *current_addr;
if (current_type >= NUM_ENTRY_TYPES)
panic("CORRUPT MPTABLES CONFIGURATION ENTRY");
mp_entries_count[current_type]++;
current_addr += entry_types[current_type].length;
}
// Allocate the correct space in the arrays (unrolled for ivy reasons)
if (mp_entries_count[PROC] != 0) {
mp_proc_entries = kmalloc(mp_entries_count[PROC] * entry_types[PROC].length , 0);
if (mp_proc_entries == NULL)
panic("Failed to allocate space for mp_proc_entires");
}
if (mp_entries_count[BUS] != 0) {
mp_bus_entries = kmalloc(mp_entries_count[BUS] * entry_types[BUS].length , 0);
if (mp_bus_entries == NULL)
panic("Failed to allocate space for mp_bus_entires");
}
if (mp_entries_count[IOAPIC] != 0) {
mp_ioapic_entries = kmalloc(mp_entries_count[IOAPIC] * entry_types[IOAPIC].length , 0);
if (mp_ioapic_entries == NULL)
panic("Failed to allocate space for mp_ioapic_entires");
}
if (mp_entries_count[INT] != 0) {
mp_int_entries = kmalloc(mp_entries_count[INT] * entry_types[INT].length , 0);
if (mp_int_entries == NULL)
panic("Failed to allocate space for mp_int_entires");
}
if (mp_entries_count[LINT] != 0) {
mp_lint_entries = kmalloc(mp_entries_count[LINT] * entry_types[LINT].length , 0);
if (mp_lint_entries == NULL)
panic("Failed to allocate space for mp_lint_entires");
}
current_addr = entry_base;
for (int i = 0; i < num_entries; i++) {
uint8_t current_type = *((uint8_t*)current_addr);
if (current_type >= NUM_ENTRY_TYPES)
panic("CORRUPT MPTABLES CONFIGURATION ENTRY.. after we already checked? Huh.");
if (num_processed[current_type] >= mp_entries_count[current_type])
panic("MPTABLES LIED ABOUT NUMBER OF ENTRIES. NO IDEA WHAT TO DO!");
switch (current_type) {
case PROC:
memcpy( &mp_proc_entries[num_processed[PROC]],
current_addr,
entry_types[PROC].length);
break;
case BUS:
memcpy( &mp_bus_entries[num_processed[BUS]],
current_addr,
entry_types[BUS].length);
break;
case IOAPIC:
memcpy( &mp_ioapic_entries[num_processed[IOAPIC]],
// This is needed due to the void* in the entry
// no clean way of doing this. Sorry Zach.
(ioapic_entry_t* COUNT(1)) TC(current_addr),
entry_types[IOAPIC].length);
break;
case INT:
memcpy( &mp_int_entries[num_processed[INT]],
current_addr,
entry_types[INT].length);
break;
case LINT:
memcpy( &mp_lint_entries[num_processed[LINT]],
(void*)current_addr,
entry_types[LINT].length);
break;
default: panic("UNKNOWN ENTRY TYPE");
}
num_processed[current_type]++;
current_addr += entry_types[current_type].length;
}
// We'd do extended table support stuff here (or alter the loop above)
// We now have all of our entries copied into a single structure we can index into. Yay.
}
/** @brief Parse all processor mptable entires
*
* This function will loop over the raw proc entry structure and parse it into a usable form.
* This currently just prints stuff if dumping is enabled.
*/
void proc_parse() {
// For now, we don't do anything with the processor entries. Just print them.
for (int i = 0; i < mp_entries_count[PROC]; i++){
mptables_dump("Proc entry %u\n", i);
mptables_dump("-->type: %x\n", mp_proc_entires[i].type);
mptables_dump("-->apic ID: %x\n", mp_proc_entires[i].apic_id);
mptables_dump("-->apic Version: %x\n", mp_proc_entires[i].apic_version);
mptables_dump("-->cpu Flags: %x\n", mp_proc_entires[i].cpu_flags);
mptables_dump("-->cpu Signaure: %x\n", mp_proc_entires[i].cpu_signature);
mptables_dump("-->feature Flags: %x\n", mp_proc_entires[i].feature_flags);
}
mptables_dump("\n");
}
/** @brief Parse all bus mptable entires
*
* This function will loop over the raw bus entry structure and parse it into a usable form
* This currently just prints stuff if dumping is enabled. (With a basic sanity check).
*/
void bus_parse() {
// Do we need to sort this?
// For now, don't. We assume the index into this structure matches the type.
// This seems to be implied from the configuration
for (int i = 0; i < mp_entries_count[BUS]; i++){
if (i != mp_bus_entries[i].bus_id)
panic("Oh noes! We need to sort entries. The MP Spec lied! Ok lied is too strong a word, it implied.");
mptables_dump("Bus entry %u\n", i);
mptables_dump("-->type: %x\n", mp_bus_entries[i].type);
mptables_dump("-->Bus ID: %x\n", mp_bus_entries[i].bus_id);
mptables_dump("-->Bus: %c%c%c\n", mp_bus_entries[i].bus_type[0], mp_bus_entries[i].bus_type[1], mp_bus_entries[i].bus_type[2]);
}
mptables_dump("\n");
}
/** @brief Parse all ioapic mptable entires
*
* This function will loop over the raw ioapic entry structure and parse it into a usable form.
* ioapic_entires[] contains all found ioapics after this function.
*/
void ioapic_parse() {
// Note: We don't check if the apicFlags is 0. If zero, unusable
// This should be done elsewhere.
// mp_entries_count[IOAPIC] contains the number of ioapics on this system
for (int i = 0; i < mp_entries_count[IOAPIC]; i++){
memcpy((void*)(ioapic_entries + mp_ioapic_entries[i].apic_id), (void*)(mp_ioapic_entries + i), sizeof(ioapic_entry_t));
mptables_dump("IOAPIC entry %u\n", i);
mptables_dump("-->type: %x\n", mp_ioapic_entries[i].type);
mptables_dump("-->apic_id: %x\n", mp_ioapic_entries[i].apic_id);
mptables_dump("-->apic_version: %x\n", mp_ioapic_entries[i].apic_version);
mptables_dump("-->apic_flags: %x\n", mp_ioapic_entries[i].apic_flags);
mptables_dump("-->apic_address: %p\n", mp_ioapic_entries[i].apic_address);
}
mptables_dump("\n");
}
/** @brief Parse all interrupt mptable entires
*
* This function will loop over the raw interrupt entry structure and parse it into a usable form.
* pci_int_devices[] and isa_int_entries[] will be populated after this function is called.
*/
void int_parse() {
// create a massive array, tied together with bus/dev, for indexing
for (int i = 0; i < mp_entries_count[INT]; i++){
mptables_dump("Interrupt entry %u\n", i);
mptables_dump("-->type: %x\n", mp_int_entries[i].type);
mptables_dump("-->int Type: %x\n", mp_int_entries[i].int_type);
mptables_dump("-->int Flags: %x\n", mp_int_entries[i].int_flags);
mptables_dump("-->src Bus ID: %u\n", mp_int_entries[i].src_bus_id);
mptables_dump("-->src Device: %u (PCI ONLY)\n", (mp_int_entries[i].src_bus_irq >> 2) & 0x1F);
mptables_dump("-->src Bus IRQ: %x\n", mp_int_entries[i].src_bus_irq);
mptables_dump("-->dst Apic ID: %u\n", mp_int_entries[i].dst_apic_id);
mptables_dump("-->dst Apic INT: %u\n", mp_int_entries[i].dst_apic_int);
}
mptables_dump("\n");
// Populate the PCI/ISA structure with the interrupt entries.
for (int i = 0; i < mp_entries_count[INT]; i++) {
if (strncmp(mp_bus_entries[mp_int_entries[i].src_bus_id].bus_type, "PCI", 3) == 0) {
int bus_idx, dev_idx, line_idx;
bus_idx = mp_int_entries[i].src_bus_id;
dev_idx = (mp_int_entries[i].src_bus_irq >> 2) & 0x1F;
line_idx = mp_int_entries[i].src_bus_irq & 0x3;
pci_int_devices[bus_idx][dev_idx].line[line_idx].dst_apic_id = mp_int_entries[i].dst_apic_id;
pci_int_devices[bus_idx][dev_idx].line[line_idx].dst_apic_int = mp_int_entries[i].dst_apic_int;
}
if (strncmp(mp_bus_entries[mp_int_entries[i].src_bus_id].bus_type, "ISA", 3) == 0) {
int irq = mp_int_entries[i].src_bus_irq;
int int_type = mp_int_entries[i].int_type;
if (int_type == 3) {
/* THIS IS WHERE THE PIC CONNECTS TO THE IOAPIC
* WE DON'T CURRENTLY DO ANYTHING WITH THIS, BUT SHOULD WE NEED TO
* HERES WHERE TO LOOK!
* WE MUST NOT PLACE THIS INTO OUR TABLE AS IRQ HAS NO REAL MEANING AFAPK
*/
continue;
/* Note. On the dev boxes the pit and pic both claim to be on irq 0
* However the pit and the pic are on different ioapic entries.
* Seems odd. Not sure whats up with this. Paul assumes the IRQ has no meaning
* in regards to the pic... which makes sense.
*/
}
if ((isa_int_entries[irq].dst_apic_id != 0xFFFF) &&
((isa_int_entries[irq].dst_apic_id != mp_int_entries[i].dst_apic_id)
|| (isa_int_entries[irq].dst_apic_int != mp_int_entries[i].dst_apic_int)))
panic("SAME IRQ MAPS TO DIFFERENT IOAPIC/INTN'S. THIS DEFIES LOGIC.");
isa_int_entries[irq].dst_apic_id = mp_int_entries[i].dst_apic_id;
isa_int_entries[irq].dst_apic_int = mp_int_entries[i].dst_apic_int;
}
}
}
/** @brief Parse all local interrupt mptable entires
*
* This function will loop over the raw local interrupt entry structure and parse it into a usable form.
* This currently just prints stuff if dumping is enabled.
*/
void lint_parse() {
// For now, we don't do anything with the local interrupt entries
for (int i = 0; i < mp_entries_count[LINT]; i++){
mptables_dump("Local Interrupt entry %u\n", i);
mptables_dump("-->type: %x\n", mp_lint_entries[i].type);
mptables_dump("-->int Type: %x\n", mp_lint_entries[i].int_type);
mptables_dump("-->src Bus ID: %x\n", mp_lint_entries[i].src_bus_id);
mptables_dump("-->src Bus IRQ: %x\n", mp_lint_entries[i].src_bus_irq);
mptables_dump("-->dst Apic ID: %p\n", mp_lint_entries[i].dst_apic_id);
mptables_dump("-->dst Apic INT: %p\n", mp_lint_entries[i].dst_apic_int);
}
}