* Initial support for ACPI tables to detect multiprocessor configurations

* ACPI is evaluated first as it also handles things like multi core or hyper threading setups
* Removed other (disabled) hyper threading code per the notes in the corresponding ToDo
* Limit the detected CPU count to 2 for now as I wasn't able to get it working in either emulation nor real hardware with more than 2 CPUs
* Added a reserved byte to the mp_config_table struct, it worked only by luck as the compiler did padding there to get to the same size

I can now boot my Core 2 Quad with two out of four processors active :-)

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23100 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2007-12-09 21:01:55 +00:00
parent 1a802bca9d
commit f108445b5e
3 changed files with 192 additions and 56 deletions

View File

@ -0,0 +1,69 @@
/*
* Copyright 2007, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_x86_SMP_ACPI_H
#define _KERNEL_ARCH_x86_SMP_ACPI_H
#define ACPI_RSDP_SIGNATURE "RSD PTR "
#define ACPI_RSDT_SIGNATURE "RSDT"
#define ACPI_MADT_SIGNATURE "APIC"
struct acpi_rsdp {
char signature[8]; /* "RSD PTR " including blank */
uint8 checksum; /* checksum of bytes 0-19 (per ACPI 1.0) */
char oem_id[6]; /* not null terminated */
uint8 revision; /* 0 = ACPI 1.0, 2 = ACPI 3.0 */
uint32 rsdt_address; /* physical memory address of RSDT */
uint32 rsdt_length; /* length in bytes including header */
uint64 xsdt_address; /* 64bit physical memory address of XSDT */
uint8 extended_checksum; /* including entire table */
uint8 reserved[3];
} _PACKED;
struct acpi_descriptor_header {
char signature[4]; /* table identifier as ASCII string */
uint32 length; /* length in bytes of the entire table */
uint8 revision;
uint8 checksum; /* checksum of entire table */
char oem_id[6]; /* not null terminated */
char oem_table_id[8]; /* oem supplied table identifier */
uint32 oem_revision; /* oem supplied revision number */
char creator_id[4]; /* creator / asl compiler id */
uint32 creator_revision; /* compiler revision */
} _PACKED;
struct acpi_madt {
acpi_descriptor_header header; /* "APIC" signature */
uint32 local_apic_address; /* physical address for local CPUs APICs */
uint32 flags;
} _PACKED;
enum {
ACPI_MADT_LOCAL_APIC = 0,
ACPI_MADT_IO_APIC = 1
};
struct acpi_apic {
uint8 type;
uint8 length;
} _PACKED;
struct acpi_local_apic {
uint8 type; /* 0 = processor local APIC */
uint8 length; /* 8 bytes */
uint8 acpi_processor_id;
uint8 apic_id; /* the id of this APIC */
uint32 flags; /* 1 = enabled */
} _PACKED;
struct acpi_io_apic {
uint8 type; /* 1 = I/O APIC */
uint8 length; /* 12 bytes */
uint8 io_apic_id; /* the id of this APIC */
uint8 reserved;
uint32 io_apic_address; /* phyisical address of I/O APIC */
uint32 interrupt_base; /* global system interrupt base */
} _PACKED;
#endif /* _KERNEL_ARCH_x86_SMP_ACPI_H */

View File

@ -125,6 +125,7 @@ struct mp_config_table {
uint32 apic; /* address of apic */
uint16 ext_length; /* length of extended section */
uint8 ext_checksum; /* checksum of extended table entries */
uint8 reserved;
};
struct mp_floating_struct {

View File

@ -16,6 +16,7 @@
#include <safemode.h>
#include <boot/stage2.h>
#include <boot/menu.h>
#include <arch/x86/smp_acpi.h>
#include <arch/x86/smp_apic.h>
#include <arch/x86/arch_system_info.h>
@ -47,6 +48,11 @@ static struct smp_scan_spots_struct smp_scan_spots[] = {
{ 0, 0, 0 }
};
static struct smp_scan_spots_struct acpi_scan_spots[] = {
{ 0x0, 0x400, 0x400 - 0x0 },
{ 0xe0000, 0x100000, 0x100000 - 0xe0000 },
{ 0, 0, 0 }
};
extern "C" void execute_n_instructions(int count);
@ -74,21 +80,6 @@ apic_write(uint32 offset, uint32 data)
}
static bool
supports_hyper_threading(void)
{
cpuid_info info;
if (get_current_cpuid(&info, 0) == B_OK
&& !strncmp(info.eax_0.vendor_id, "GenuineIntel", 12)
&& info.eax_0.max_eax > 0) {
if (get_current_cpuid(&info, 1) == B_OK)
return (info.eax_1.features & (1 << 28)) != 0;
}
return false;
}
static int
smp_get_current_cpu(void)
{
@ -100,15 +91,15 @@ smp_get_current_cpu(void)
static uint32 *
smp_probe(uint32 base, uint32 limit)
smp_mp_probe(uint32 base, uint32 limit)
{
uint32 *ptr;
TRACE(("smp_probe: entry base 0x%lx, limit 0x%lx\n", base, limit));
TRACE(("smp_mp_probe: entry base 0x%lx, limit 0x%lx\n", base, limit));
for (ptr = (uint32 *) base; (uint32)ptr < limit; ptr++) {
if (*ptr == MP_FLOATING_SIGNATURE) {
TRACE(("smp_probe: found floating pointer structure at %p\n", ptr));
TRACE(("smp_mp_probe: found floating pointer structure at %p\n", ptr));
return ptr;
}
}
@ -116,8 +107,23 @@ smp_probe(uint32 base, uint32 limit)
}
static acpi_rsdp *
smp_acpi_probe(uint32 base, uint32 limit)
{
TRACE(("smp_acpi_probe: entry base 0x%lx, limit 0x%lx\n", base, limit));
for (char *pointer = (char *)base; (uint32)pointer < limit; pointer += 16) {
if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) {
TRACE(("smp_acpi_probe: found ACPI RSDP signature at %p\n", pointer));
return (acpi_rsdp *)pointer;
}
}
return NULL;
}
static void
smp_do_config(void)
smp_do_mp_config()
{
struct mp_config_table *config;
char *ptr;
@ -138,11 +144,8 @@ smp_do_config(void)
/* print out our new found configuration. */
ptr = (char *)&(config->oem[0]);
TRACE(("smp: oem id: %c%c%c%c%c%c%c%c product id: "
"%c%c%c%c%c%c%c%c%c%c%c%c\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4],
ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12],
ptr[13], ptr[14], ptr[15], ptr[16], ptr[17], ptr[18], ptr[19]));
TRACE(("smp: oem id: %.8s product id: %.12s\n", config->oem,
config->product));
TRACE(("smp: base table has %d entries, extended section %d bytes\n",
config->num_base_entries, config->ext_length));
@ -212,32 +215,92 @@ smp_do_config(void)
(void *)gKernelArgs.arch_args.ioapic_phys,
gKernelArgs.num_cpus);
// Try to detect single CPU hyper threading setup
// ToDo: this should be done using the ACPI APIC table
// ToDo: this only works with a single HT enabled CPU anyway
if (gKernelArgs.num_cpus == 1 && supports_hyper_threading()) {
cpuid_info info;
get_current_cpuid(&info, 1);
dprintf("CPU supports Hyper-Threading, %ld processors in package\n",
info.eax_1.logical_cpus);
// enable the second logical processor
/*
gKernelArgs.num_cpus = 2;
gKernelArgs.arch_args.cpu_apic_id[1] = gKernelArgs.arch_args.cpu_apic_id[0] + 1;
gKernelArgs.arch_args.cpu_os_id[gKernelArgs.arch_args.cpu_apic_id[1]] = 1;
gKernelArgs.arch_args.cpu_apic_version[1] = gKernelArgs.arch_args.cpu_apic_version[0];;
*/
}
// this BIOS looks broken, because it didn't report any cpus (VMWare)
if (gKernelArgs.num_cpus == 0)
gKernelArgs.num_cpus = 1;
}
static status_t
smp_do_acpi_config(acpi_rsdp *rsdp)
{
TRACE(("smp: using ACPI to detect MP configuration\n"));
TRACE(("smp: found rsdp at %p oem id: %.6s\n", rsdp, rsdp->oem_id));
TRACE(("smp: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address));
// reset CPU count
gKernelArgs.num_cpus = 0;
// map and validate the root system description table
acpi_descriptor_header *rsdt
= (acpi_descriptor_header *)mmu_map_physical_memory(
rsdp->rsdt_address, B_PAGE_SIZE, kDefaultPageFlags);
if (!rsdt || strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
TRACE(("smp: invalid root system description table\n"));
return B_ERROR;
}
int32 numEntries = (rsdt->length - sizeof(acpi_descriptor_header)) / 4;
if (numEntries <= 0) {
TRACE(("smp: root system description table is empty\n"));
return B_ERROR;
}
TRACE(("smp: searching %ld entries for APIC information\n", numEntries));
uint32 *pointer = (uint32 *)((uint8 *)rsdt + sizeof(acpi_descriptor_header));
for (int32 j = 0; j < numEntries; j++, pointer++) {
acpi_descriptor_header *header = (acpi_descriptor_header *)
mmu_map_physical_memory(*pointer, B_PAGE_SIZE, kDefaultPageFlags);
if (!header || strncmp(header->signature, ACPI_MADT_SIGNATURE, 4) != 0) {
// not interesting for us
TRACE(("smp: skipping uninteresting header '%.4s'\n", header->signature));
continue;
}
acpi_madt *madt = (acpi_madt *)header;
gKernelArgs.arch_args.apic_phys = madt->local_apic_address;
TRACE(("smp: local apic address is 0x%lx\n", madt->local_apic_address));
acpi_apic *apic = (acpi_apic *)((uint8 *)madt + sizeof(acpi_madt));
acpi_apic *end = (acpi_apic *)((uint8 *)madt + header->length);
while (apic < end) {
switch (apic->type) {
case ACPI_MADT_LOCAL_APIC:
{
acpi_local_apic *localApic = (acpi_local_apic *)apic;
TRACE(("smp: found local APIC with id %u\n", localApic->apic_id));
gKernelArgs.arch_args.cpu_apic_id[gKernelArgs.num_cpus] = localApic->apic_id;
gKernelArgs.arch_args.cpu_os_id[localApic->apic_id] = gKernelArgs.num_cpus;
// ToDo: how to find out? putting 0x10 in to indicate a local apic
gKernelArgs.arch_args.cpu_apic_version[gKernelArgs.num_cpus] = 0x10;
gKernelArgs.num_cpus++;
break;
}
case ACPI_MADT_IO_APIC: {
acpi_io_apic *ioApic = (acpi_io_apic *)apic;
TRACE(("smp: found io APIC with id %u and address 0x%lx\n",
ioApic->io_apic_id, ioApic->io_apic_address));
gKernelArgs.arch_args.ioapic_phys = ioApic->io_apic_address;
break;
}
}
apic = (acpi_apic *)((uint8 *)apic + apic->length);
}
if (gKernelArgs.num_cpus > 0)
break;
}
// ToDo: remove CPU limit
if (gKernelArgs.num_cpus > 2)
gKernelArgs.num_cpus = 2;
return gKernelArgs.num_cpus > 0 ? B_OK : B_ERROR;
}
static int
smp_find_mp_config(void)
{
@ -248,8 +311,18 @@ smp_find_mp_config(void)
return gKernelArgs.num_cpus = 1;
#endif
// first try to find ACPI tables to get MP configuration as it handles
// physical as well as logical MP configurations as in multiple cpus,
// multiple cores or hyper threading.
for (i = 0; acpi_scan_spots[i].length > 0; i++) {
acpi_rsdp *rsdp = smp_acpi_probe(smp_scan_spots[i].start,
smp_scan_spots[i].stop);
if (rsdp != NULL && smp_do_acpi_config(rsdp) == B_OK)
return gKernelArgs.num_cpus;
}
for (i = 0; smp_scan_spots[i].length > 0; i++) {
sFloatingStruct = (struct mp_floating_struct *)smp_probe(smp_scan_spots[i].start,
sFloatingStruct = (struct mp_floating_struct *)smp_mp_probe(smp_scan_spots[i].start,
smp_scan_spots[i].stop);
if (sFloatingStruct != NULL)
break;
@ -265,6 +338,7 @@ smp_find_mp_config(void)
if (sFloatingStruct->config_table == NULL) {
// XXX need to implement
#if 1
TRACE(("smp: standard configuration %d unimplemented\n", sFloatingStruct->mp_feature_1));
gKernelArgs.num_cpus = 1;
return 1;
#else
@ -280,8 +354,9 @@ smp_find_mp_config(void)
*/
#endif
} else {
smp_do_config();
smp_do_mp_config();
}
return gKernelArgs.num_cpus;
}
@ -515,18 +590,11 @@ smp_add_safemode_menus(Menu *menu)
if (gKernelArgs.num_cpus < 2)
return;
// ToDo: this should work with dual CPUs with HT as well!
if (gKernelArgs.num_cpus > 2 || !supports_hyper_threading()) {
if (gKernelArgs.num_cpus > 2) {
menu->AddItem(item = new(nothrow) MenuItem("Disable SMP"));
item->SetData(B_SAFEMODE_DISABLE_SMP);
item->SetType(MENU_ITEM_MARKABLE);
}
if (supports_hyper_threading()) {
menu->AddItem(item = new(nothrow) MenuItem("Disable Hyper-Threading"));
item->SetData(B_SAFEMODE_DISABLE_HYPER_THREADING);
item->SetType(MENU_ITEM_MARKABLE);
}
}
@ -561,5 +629,3 @@ smp_init(void)
}
}
}