Skip to content
Permalink
Browse files

Enhance SMP logic to parse MP table

Adds logic to parse information about CPUs on
system where ACPI is not available. It does it
by parsing so called MP table. If MP table
not found assumes single vCPU.

Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Message-Id: <20190302063049.2203-1-jwkozaczuk@gmail.com>
  • Loading branch information...
wkozaczuk authored and nyh committed Mar 2, 2019
1 parent 97fbc06 commit 47ae2b65e0428336a841d07d9add01359f523377
Showing with 111 additions and 14 deletions.
  1. +111 −14 arch/x64/smp.cc
@@ -39,6 +39,16 @@ volatile unsigned smp_processors = 1;

using boost::intrusive::get_parent_from_member;

static void register_cpu(unsigned cpu_id, u32 apic_id, u32 acpi_id = 0)
{
auto c = new sched::cpu(cpu_id);
c->arch.apic_id = apic_id;
c->arch.acpi_id = acpi_id;
c->arch.initstack.next = smp_stack_free;
smp_stack_free = &c->arch.initstack;
sched::cpus.push_back(c);
}

void parse_madt()
{
char madt_sig[] = ACPI_SIG_MADT;
@@ -57,12 +67,7 @@ void parse_madt()
if (!(lapic->LapicFlags & ACPI_MADT_ENABLED)) {
break;
}
auto c = new sched::cpu(nr_cpus++);
c->arch.apic_id = lapic->Id;
c->arch.acpi_id = lapic->ProcessorId;
c->arch.initstack.next = smp_stack_free;
smp_stack_free = &c->arch.initstack;
sched::cpus.push_back(c);
register_cpu(nr_cpus++, lapic->Id, lapic->ProcessorId);
break;
}
default:
@@ -73,16 +78,108 @@ void parse_madt()
debug(fmt("%d CPUs detected\n") % nr_cpus);
}

#define MPF_IDENTIFIER (('_'<<24) | ('P'<<16) | ('M'<<8) | '_')
struct mpf_structure {
char signature[4];
uint32_t configuration_table;
uint8_t length; // In 16 bytes (e.g. 1 = 16 bytes, 2 = 32 bytes)
uint8_t specification_revision;
uint8_t checksum; // This value should make all bytes in the table equal 0 when added together
uint8_t default_configuration; // If this is not zero then configuration_table should be
// ignored and a default configuration should be loaded instead
uint32_t features; // If bit 7 is then the IMCR is present and PIC mode is being used, otherwise
// virtual wire mode is; all other bits are reserved
} __attribute__((packed));

#define MP_TABLE_IDENTIFIER (('P'<<24) | ('M'<<16) | ('C'<<8) | 'P')
struct mp_table {
char signature[4]; // "PCMP"
uint16_t length;
uint8_t mp_specification_revision;
uint8_t checksum; // Again, the byte should be all bytes in the table add up to 0
char oem_id[8];
char product_id[12];
uint32_t oem_table;
uint16_t oem_table_size;
uint16_t entry_count; // This value represents how many entries are following this table
uint32_t lapic_address; // This is the memory mapped address of the local APICs
uint16_t extended_table_length;
uint8_t extended_table_checksum;
uint8_t reserved;
} __attribute__((packed));

struct mp_processor {
uint8_t type; // Always 0
uint8_t local_apic_id;
uint8_t local_apic_version;
uint8_t flags; // If bit 0 is clear then the processor must be ignored
// If bit 1 is set then the processor is the bootstrap processor
uint32_t signature;
uint32_t feature_flags;
uint64_t reserved;
} __attribute__((packed));

static mp_table *find_mp_table(unsigned long base, long length)
{
// First find MP floating pointer structure in the physical memory
// region specified by the base and length
void *addr = mmu::phys_to_virt(base);
while (length > 0) {
if (*static_cast<uint32_t *>(addr) == MPF_IDENTIFIER) {
// We found the MP floating pointer structure
auto mpf_struct = static_cast<mpf_structure*>(addr);
// Now let us dereference physical address of MP table itself,
// check signature and return its virtual address
void *mp_table_addr = mmu::phys_to_virt(mpf_struct->configuration_table);
if (*static_cast<uint32_t *>(mp_table_addr) == MP_TABLE_IDENTIFIER) {
return static_cast<mp_table*>(mp_table_addr);
}
else {
return nullptr;
}
}

addr += 16;
length -= 16;
}
return nullptr;
}

#define LAST_KB_IN_BASE_MEMORY_ADDR 639 * 0x400
#define FIRST_KB_IN_BASE_MEMORY_ADDR 0x0
#define NON_PROCESSOR_ENTRY_SIZE 8
void parse_mp_table()
{
//TODO: This a nasty hack to support single vCPU. Eventually we should
// parse out equivalent information about all vCPUs from MP table. For
// details please see https://wiki.osdev.org/Symmetric_Multiprocessing#Finding_information_using_MP_Table
auto c = new sched::cpu(0);
c->arch.apic_id = 0;
c->arch.initstack.next = smp_stack_free;
smp_stack_free = &c->arch.initstack;
sched::cpus.push_back(c);
// Parse information about all vCPUs from MP table. For details please see
// https://wiki.osdev.org/Symmetric_Multiprocessing#Finding_information_using_MP_Table
// or http://www.osdever.net/tutorials/view/multiprocessing-support-for-hobby-oses-explained
mp_table *table = find_mp_table(LAST_KB_IN_BASE_MEMORY_ADDR, 0x400);
if (!table) {
table = find_mp_table(FIRST_KB_IN_BASE_MEMORY_ADDR, 0x400);
}

unsigned nr_cpus = 0;
if (table) {
void *mp_entries = static_cast<void*>(table) + sizeof(mp_table);
int entries_size = table->length - sizeof(mp_table);

while (entries_size > 0) {
int entry_size = NON_PROCESSOR_ENTRY_SIZE;
auto proc_desc = static_cast<mp_processor*>(mp_entries);
if (proc_desc->type == 0) {
register_cpu(nr_cpus++, proc_desc->local_apic_id);
entry_size = sizeof(mp_processor);
}
entries_size -= entry_size;
mp_entries += entry_size;
}
}

if (!nr_cpus) { // No MP table was found or no cpu was found in there -> assume uni-processor
register_cpu(nr_cpus++, 0);
}

debug(fmt("%d CPUs detected\n") % nr_cpus);
}

void smp_init()

0 comments on commit 47ae2b6

Please sign in to comment.
You can’t perform that action at this time.