Skip to content

Commit 47ae2b6

Browse files
wkozaczuknyh
authored andcommitted
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>
1 parent 97fbc06 commit 47ae2b6

1 file changed

Lines changed: 111 additions & 14 deletions

File tree

‎arch/x64/smp.cc‎

Lines changed: 111 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ volatile unsigned smp_processors = 1;
3939

4040
using 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+
4252
void 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
76151
void 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

88185
void smp_init()

0 commit comments

Comments
 (0)