@@ -39,6 +39,16 @@ volatile unsigned smp_processors = 1;
3939
4040using boost::intrusive::get_parent_from_member;
4141
42+ static void register_cpu (unsigned cpu_id, u32 apic_id, u32 acpi_id = 0 )
43+ {
44+ auto c = new sched::cpu (cpu_id);
45+ c->arch .apic_id = apic_id;
46+ c->arch .acpi_id = acpi_id;
47+ c->arch .initstack .next = smp_stack_free;
48+ smp_stack_free = &c->arch .initstack ;
49+ sched::cpus.push_back (c);
50+ }
51+
4252void parse_madt ()
4353{
4454 char madt_sig[] = ACPI_SIG_MADT ;
@@ -57,12 +67,7 @@ void parse_madt()
5767 if (!(lapic->LapicFlags & ACPI_MADT_ENABLED )) {
5868 break ;
5969 }
60- auto c = new sched::cpu (nr_cpus++);
61- c->arch .apic_id = lapic->Id ;
62- c->arch .acpi_id = lapic->ProcessorId ;
63- c->arch .initstack .next = smp_stack_free;
64- smp_stack_free = &c->arch .initstack ;
65- sched::cpus.push_back (c);
70+ register_cpu (nr_cpus++, lapic->Id , lapic->ProcessorId );
6671 break ;
6772 }
6873 default :
@@ -73,16 +78,108 @@ void parse_madt()
7378 debug (fmt (" %d CPUs detected\n " ) % nr_cpus);
7479}
7580
81+ #define MPF_IDENTIFIER ((' _' <<24 ) | (' P' <<16 ) | (' M' <<8 ) | ' _' )
82+ struct mpf_structure {
83+ char signature[4 ];
84+ uint32_t configuration_table;
85+ uint8_t length; // In 16 bytes (e.g. 1 = 16 bytes, 2 = 32 bytes)
86+ uint8_t specification_revision;
87+ uint8_t checksum; // This value should make all bytes in the table equal 0 when added together
88+ uint8_t default_configuration; // If this is not zero then configuration_table should be
89+ // ignored and a default configuration should be loaded instead
90+ uint32_t features; // If bit 7 is then the IMCR is present and PIC mode is being used, otherwise
91+ // virtual wire mode is; all other bits are reserved
92+ } __attribute__((packed));
93+
94+ #define MP_TABLE_IDENTIFIER ((' P' <<24 ) | (' M' <<16 ) | (' C' <<8 ) | ' P' )
95+ struct mp_table {
96+ char signature[4 ]; // "PCMP"
97+ uint16_t length;
98+ uint8_t mp_specification_revision;
99+ uint8_t checksum; // Again, the byte should be all bytes in the table add up to 0
100+ char oem_id[8 ];
101+ char product_id[12 ];
102+ uint32_t oem_table;
103+ uint16_t oem_table_size;
104+ uint16_t entry_count; // This value represents how many entries are following this table
105+ uint32_t lapic_address; // This is the memory mapped address of the local APICs
106+ uint16_t extended_table_length;
107+ uint8_t extended_table_checksum;
108+ uint8_t reserved;
109+ } __attribute__((packed));
110+
111+ struct mp_processor {
112+ uint8_t type; // Always 0
113+ uint8_t local_apic_id;
114+ uint8_t local_apic_version;
115+ uint8_t flags; // If bit 0 is clear then the processor must be ignored
116+ // If bit 1 is set then the processor is the bootstrap processor
117+ uint32_t signature;
118+ uint32_t feature_flags;
119+ uint64_t reserved;
120+ } __attribute__((packed));
121+
122+ static mp_table *find_mp_table (unsigned long base, long length)
123+ {
124+ // First find MP floating pointer structure in the physical memory
125+ // region specified by the base and length
126+ void *addr = mmu::phys_to_virt (base);
127+ while (length > 0 ) {
128+ if (*static_cast <uint32_t *>(addr) == MPF_IDENTIFIER ) {
129+ // We found the MP floating pointer structure
130+ auto mpf_struct = static_cast <mpf_structure*>(addr);
131+ // Now let us dereference physical address of MP table itself,
132+ // check signature and return its virtual address
133+ void *mp_table_addr = mmu::phys_to_virt (mpf_struct->configuration_table );
134+ if (*static_cast <uint32_t *>(mp_table_addr) == MP_TABLE_IDENTIFIER ) {
135+ return static_cast <mp_table*>(mp_table_addr);
136+ }
137+ else {
138+ return nullptr ;
139+ }
140+ }
141+
142+ addr += 16 ;
143+ length -= 16 ;
144+ }
145+ return nullptr ;
146+ }
147+
148+ #define LAST_KB_IN_BASE_MEMORY_ADDR 639 * 0x400
149+ #define FIRST_KB_IN_BASE_MEMORY_ADDR 0x0
150+ #define NON_PROCESSOR_ENTRY_SIZE 8
76151void parse_mp_table ()
77152{
78- // TODO: This a nasty hack to support single vCPU. Eventually we should
79- // parse out equivalent information about all vCPUs from MP table. For
80- // details please see https://wiki.osdev.org/Symmetric_Multiprocessing#Finding_information_using_MP_Table
81- auto c = new sched::cpu (0 );
82- c->arch .apic_id = 0 ;
83- c->arch .initstack .next = smp_stack_free;
84- smp_stack_free = &c->arch .initstack ;
85- sched::cpus.push_back (c);
153+ // Parse information about all vCPUs from MP table. For details please see
154+ // https://wiki.osdev.org/Symmetric_Multiprocessing#Finding_information_using_MP_Table
155+ // or http://www.osdever.net/tutorials/view/multiprocessing-support-for-hobby-oses-explained
156+ mp_table *table = find_mp_table (LAST_KB_IN_BASE_MEMORY_ADDR , 0x400 );
157+ if (!table) {
158+ table = find_mp_table (FIRST_KB_IN_BASE_MEMORY_ADDR , 0x400 );
159+ }
160+
161+ unsigned nr_cpus = 0 ;
162+ if (table) {
163+ void *mp_entries = static_cast <void *>(table) + sizeof (mp_table);
164+ int entries_size = table->length - sizeof (mp_table);
165+
166+ while (entries_size > 0 ) {
167+ int entry_size = NON_PROCESSOR_ENTRY_SIZE ;
168+ auto proc_desc = static_cast <mp_processor*>(mp_entries);
169+ if (proc_desc->type == 0 ) {
170+ register_cpu (nr_cpus++, proc_desc->local_apic_id );
171+ entry_size = sizeof (mp_processor);
172+ }
173+ entries_size -= entry_size;
174+ mp_entries += entry_size;
175+ }
176+ }
177+
178+ if (!nr_cpus) { // No MP table was found or no cpu was found in there -> assume uni-processor
179+ register_cpu (nr_cpus++, 0 );
180+ }
181+
182+ debug (fmt (" %d CPUs detected\n " ) % nr_cpus);
86183}
87184
88185void smp_init ()
0 commit comments