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 <jwkozac...@gmail.com>
---
 arch/x64/smp.cc | 125 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 111 insertions(+), 14 deletions(-)

diff --git a/arch/x64/smp.cc b/arch/x64/smp.cc
index 2869d6e2..3386bd90 100644
--- a/arch/x64/smp.cc
+++ b/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()
-- 
2.19.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to