2011-08-28 11:01:47 +00:00
|
|
|
#include "Tier0/smp.h"
|
2011-08-28 01:36:23 +00:00
|
|
|
#include "Tier0/paging.h"
|
|
|
|
#include "Tier0/physmem.h"
|
|
|
|
#include "Tier0/kstdio.h"
|
2011-08-28 02:30:57 +00:00
|
|
|
#include "Tier0/kstdlib.h"
|
2011-08-28 01:36:23 +00:00
|
|
|
#include "Tier0/panic.h"
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
#define SMP_TESMP_BUFFER_LENGTH 1024
|
|
|
|
u8 g_EntriesBuffer[SMP_TESMP_BUFFER_LENGTH];
|
2011-08-28 02:30:57 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
#define SMP_MAX_CPU 32
|
2011-08-28 11:19:12 +00:00
|
|
|
#define SMP_MAX_IOAPIC 9
|
2011-08-28 10:56:46 +00:00
|
|
|
|
|
|
|
struct {
|
2011-08-28 11:01:47 +00:00
|
|
|
T_SMP_CPU CPUs[SMP_MAX_CPU];
|
2011-08-28 10:56:46 +00:00
|
|
|
u8 NumCPUs;
|
2011-08-28 11:19:12 +00:00
|
|
|
|
|
|
|
T_SMP_IOAPIC IOAPICs[SMP_MAX_IOAPIC];
|
|
|
|
u8 NumIOAPICs;
|
2011-08-28 11:01:47 +00:00
|
|
|
} g_SMP;
|
2011-08-28 10:56:46 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
u64 smp_find_pointer(u64 Start, u64 End)
|
2011-08-28 01:36:23 +00:00
|
|
|
{
|
|
|
|
for (u64 i = Start & ~((u64)0x10); i < End; i += 16)
|
|
|
|
{
|
|
|
|
u8 Pointer[16];
|
|
|
|
physmem_read(i, 16, Pointer);
|
|
|
|
|
|
|
|
if (kmemcmp(Pointer, (u8 *)"_MP_", 4) < 0)
|
|
|
|
{
|
|
|
|
// Found something! Let's calculate the checksum, just to be sure.
|
|
|
|
u8 Sum = 0;
|
|
|
|
for (u8 j = 0; j < 16; j++)
|
|
|
|
Sum += Pointer[j];
|
|
|
|
|
|
|
|
if (Sum)
|
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[w] Found SMP pointer, but its checksum was invalid.");
|
2011-08-28 01:36:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[i] Found SMP pointer @0x%x\n", i);
|
2011-08-28 01:36:23 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
void smp_initialize(void)
|
2011-08-28 01:36:23 +00:00
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[i] Looking for SMP Pointer:\n");
|
|
|
|
// According to the Intel spec, we must look for the SMP Pointer in four
|
2011-08-28 01:36:23 +00:00
|
|
|
// places:
|
|
|
|
|
|
|
|
// EBDA
|
|
|
|
u32 EBDAStart;
|
|
|
|
physmem_read(0x40e, 2, &EBDAStart);
|
|
|
|
EBDAStart = EBDAStart << 4;
|
|
|
|
|
|
|
|
kprintf(" EBDA @0x%x.\n", EBDAStart);
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
u64 Pointer = smp_find_pointer(EBDAStart, 0x0009FFFF);
|
2011-08-28 01:36:23 +00:00
|
|
|
|
|
|
|
// Last kilobyte of base memory
|
|
|
|
if (!Pointer)
|
|
|
|
{
|
|
|
|
u32 BaseMemoryEnd;
|
|
|
|
physmem_read(0x413, 2, &BaseMemoryEnd);
|
|
|
|
kprintf(" Base memory end @0x%x.\n", BaseMemoryEnd - 1024);
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
Pointer = smp_find_pointer(BaseMemoryEnd - 1024, BaseMemoryEnd);
|
2011-08-28 01:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BIOS ROM
|
|
|
|
if (!Pointer)
|
|
|
|
{
|
|
|
|
kprintf(" BIOS ROM @0x00E00000.\n");
|
2011-08-28 11:01:47 +00:00
|
|
|
Pointer = smp_find_pointer(0x000F0000, 0x000FFFFF);
|
2011-08-28 01:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Just give up already.
|
|
|
|
if (!Pointer)
|
2011-08-28 11:01:47 +00:00
|
|
|
PANIC("No SMP pointer found! Boo.");
|
2011-08-28 01:36:23 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
T_SMP_POINTER PointerTable;
|
2011-08-28 01:36:23 +00:00
|
|
|
|
|
|
|
physmem_read(Pointer, 16, &PointerTable);
|
|
|
|
|
|
|
|
if (PointerTable.Specification != 4)
|
2011-08-28 11:01:47 +00:00
|
|
|
PANIC("Unsupported SMP spec!");
|
2011-08-28 01:36:23 +00:00
|
|
|
|
|
|
|
if (PointerTable.TablePhysical)
|
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[i] SMP Configuration Table present.\n");
|
|
|
|
smp_parse_configuration_table(PointerTable.TablePhysical);
|
2011-08-28 01:36:23 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[i] SMP Configuration Type present.\n");
|
2011-08-28 02:30:57 +00:00
|
|
|
// do something else... like panic.
|
2011-08-28 11:01:47 +00:00
|
|
|
PANIC("SMP types not ismplemented!");
|
2011-08-28 02:30:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-28 16:06:40 +00:00
|
|
|
u8 smp_parse_io_interrupt(u32 EntryAddress)
|
|
|
|
{
|
|
|
|
//T_SMP_ENTRY_IO_INTERRUPT *Interrupt = (T_SMP_ENTRY_IO_INTERRUPT *)&g_EntriesBuffer[EntryAddress];
|
|
|
|
|
|
|
|
//kprintf(" IO Interrupt %i %i %i %i %i %i %i\n", Interrupt->InterruptType, Interrupt->Polarity, Interrupt->TriggerMode, Interrupt->SourceBusID, Interrupt->SourceBusIRQ, Interrupt->DestinationIOAPICID, Interrupt->DestinationIOAPICINTIN);
|
|
|
|
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 smp_parse_local_interrupt(u32 EntryAddress)
|
|
|
|
{
|
|
|
|
//T_SMP_ENTRY_LOCAL_INTERRUPT *Interrupt = (T_SMP_ENTRY_LOCAL_INTERRUPT *)&g_EntriesBuffer[EntryAddress];
|
|
|
|
|
|
|
|
//kprintf(" L Interrupt %i %i %i %i %i %i %i\n", Interrupt->InterruptType, Interrupt->Polarity, Interrupt->TriggerMode, Interrupt->SourceBusID, Interrupt->SourceBusIRQ, Interrupt->DestinationLAPICID, Interrupt->DestinationLAPICLINTIN);
|
|
|
|
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
2011-08-28 11:19:12 +00:00
|
|
|
u8 smp_parse_ioapic(u32 EntryAddress)
|
|
|
|
{
|
|
|
|
T_SMP_ENTRY_IOAPIC *IOAPIC = (T_SMP_ENTRY_IOAPIC *)&g_EntriesBuffer[EntryAddress];
|
|
|
|
|
|
|
|
if (IOAPIC->Available)
|
|
|
|
{
|
|
|
|
g_SMP.IOAPICs[g_SMP.NumIOAPICs].ID = IOAPIC->ID;
|
|
|
|
g_SMP.IOAPICs[g_SMP.NumIOAPICs].Address = IOAPIC->Address;
|
|
|
|
|
|
|
|
kprintf(" IOAPIC ID %i, @0x%x\n", IOAPIC->ID, IOAPIC->Address);
|
|
|
|
}
|
|
|
|
else
|
2011-12-20 15:28:31 +00:00
|
|
|
kprintf(" IOAPIC ID %i, unavailable!!!", IOAPIC->ID);
|
2011-08-28 11:19:12 +00:00
|
|
|
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 smp_parse_bus(u32 EntryAddress)
|
|
|
|
{
|
|
|
|
T_SMP_ENTRY_BUS *Bus = (T_SMP_ENTRY_BUS *)&g_EntriesBuffer[EntryAddress];
|
|
|
|
|
|
|
|
s8 Name[7];
|
|
|
|
kmemcpy(Name, Bus->BusType, 6);
|
|
|
|
Name[6] = 0x00;
|
|
|
|
|
|
|
|
kprintf(" Bus #%i, %s\n", Bus->BusID, Name);
|
|
|
|
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
u8 smp_parse_cpu(u32 EntryAddress)
|
2011-08-28 02:30:57 +00:00
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
u8 i = g_SMP.NumCPUs;
|
|
|
|
T_SMP_ENTRY_CPU *CPU = (T_SMP_ENTRY_CPU *)&g_EntriesBuffer[EntryAddress];
|
2011-08-28 02:30:57 +00:00
|
|
|
|
|
|
|
if (CPU->FlagBootstrap)
|
2011-08-28 10:56:46 +00:00
|
|
|
{
|
|
|
|
kprintf(" CPU #%i, LAPIC sig %x, cpuid %x (bootstrap)\n", i, CPU->LAPICID, CPU->CPUID);
|
2011-08-28 11:01:47 +00:00
|
|
|
g_SMP.CPUs[i].Bootstrap = 1;
|
|
|
|
g_SMP.CPUs[i].State = E_SMP_CPU_STATE_RUNNING;
|
2011-08-28 10:56:46 +00:00
|
|
|
}
|
2011-08-28 02:30:57 +00:00
|
|
|
else if (!CPU->FlagAvailable)
|
2011-08-28 10:56:46 +00:00
|
|
|
{
|
2011-12-20 15:28:31 +00:00
|
|
|
kprintf(" CPU #%i, LAPIC sig %x, cpuid %x, (unavailable", i, CPU->LAPICID, CPU->CPUID);
|
2011-08-28 11:01:47 +00:00
|
|
|
g_SMP.CPUs[i].Bootstrap = 0;
|
|
|
|
g_SMP.CPUs[i].State = E_SMP_CPU_STATE_DISABLED;
|
2011-08-28 10:56:46 +00:00
|
|
|
}
|
2011-08-28 02:30:57 +00:00
|
|
|
else
|
2011-08-28 10:56:46 +00:00
|
|
|
{
|
|
|
|
kprintf(" CPU #%i, LAPIC sig %x, cpuid %x (halted)\n", i, CPU->LAPICID, CPU->CPUID);
|
2011-08-28 11:01:47 +00:00
|
|
|
g_SMP.CPUs[i].Bootstrap = 0;
|
|
|
|
g_SMP.CPUs[i].State = E_SMP_CPU_STATE_HALTED;
|
2011-08-28 10:56:46 +00:00
|
|
|
}
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
g_SMP.CPUs[i].ID = i;
|
|
|
|
g_SMP.CPUs[i].CPUID = CPU->CPUID;
|
|
|
|
g_SMP.CPUs[i].LAPICID = CPU->LAPICID;
|
2011-08-28 10:56:46 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
g_SMP.NumCPUs++;
|
2011-08-28 02:30:57 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
return sizeof(T_SMP_ENTRY_CPU);
|
2011-08-28 02:30:57 +00:00
|
|
|
}
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
void smp_parse_configuration_table(u32 TableAddress)
|
2011-08-28 02:30:57 +00:00
|
|
|
{
|
2011-08-28 11:01:47 +00:00
|
|
|
T_SMP_CONFIGURATION_HEADER Header;
|
2011-08-28 02:30:57 +00:00
|
|
|
physmem_read(TableAddress, 44, &Header);
|
|
|
|
|
|
|
|
s8 OEMName[9];
|
|
|
|
kmemcpy(OEMName, Header.OEMName, 8);
|
|
|
|
OEMName[8] = 0x00;
|
|
|
|
|
|
|
|
s8 ProductName[13];
|
|
|
|
kmemcpy(ProductName, Header.ProductName, 12);
|
|
|
|
ProductName[12] = 0x00;
|
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[i] SMP OEM: %s\n", OEMName);
|
|
|
|
kprintf("[i] SMP Product: %s\n", ProductName);
|
2011-08-28 02:30:57 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
kprintf("[i] SMP Base Configuration Table length: %i bytes.\n", Header.BaseTableLength);
|
2011-08-28 02:30:57 +00:00
|
|
|
|
2011-08-28 11:01:47 +00:00
|
|
|
if (Header.BaseTableLength > SMP_TESMP_BUFFER_LENGTH)
|
|
|
|
PANIC("SMP BCT too big!");
|
2011-08-28 02:30:57 +00:00
|
|
|
|
|
|
|
physmem_read(TableAddress + 44, Header.BaseTableLength - 44, g_EntriesBuffer);
|
|
|
|
|
|
|
|
u32 EntryAddress = 0;
|
2011-08-28 11:01:47 +00:00
|
|
|
g_SMP.NumCPUs = 0;
|
2011-08-28 11:19:12 +00:00
|
|
|
g_SMP.NumIOAPICs = 0;
|
|
|
|
|
2011-08-28 16:06:40 +00:00
|
|
|
while (EntryAddress < Header.BaseTableLength - sizeof(T_SMP_CONFIGURATION_HEADER))
|
2011-08-28 02:30:57 +00:00
|
|
|
{
|
|
|
|
u8 EntryType = g_EntriesBuffer[EntryAddress];
|
|
|
|
switch (EntryType)
|
|
|
|
{
|
|
|
|
case 0x00:
|
2011-08-28 11:01:47 +00:00
|
|
|
EntryAddress += smp_parse_cpu(EntryAddress);
|
2011-08-28 02:30:57 +00:00
|
|
|
break;
|
2011-08-28 11:19:12 +00:00
|
|
|
case 0x01:
|
|
|
|
EntryAddress += smp_parse_bus(EntryAddress);
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
EntryAddress += smp_parse_ioapic(EntryAddress);
|
|
|
|
break;
|
2011-08-28 16:06:40 +00:00
|
|
|
case 0x03:
|
|
|
|
EntryAddress += smp_parse_io_interrupt(EntryAddress);
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
EntryAddress += smp_parse_local_interrupt(EntryAddress);
|
|
|
|
break;
|
2011-08-28 02:30:57 +00:00
|
|
|
}
|
2011-08-28 01:36:23 +00:00
|
|
|
}
|
|
|
|
}
|