- kernel detects CPUs by searching ACPI tables for local apic nodes - each CPU has its own TSS that points to its own stack. All cpus boot on the same boot stack (in sequence) but switch to its private stack as soon as they can. - final booting code in main() placed in bsp_finish_booting() which is executed only after the BSP switches to its final stack - apic functions to send startup interrupts - assembler functions to handle CPU features not needed for single cpu mode like memory barries, HT detection etc. - new files kernel/smp.[ch], kernel/arch/i386/arch_smp.c and kernel/arch/i386/include/arch_smp.h - 16-bit trampoline code for the APs. It is executed by each AP after receiving startup IPIs it brings up the CPUs to 32bit mode and let them spin in an infinite loop so they don't do any damage. - implementation of kernel spinlock - CONFIG_SMP and CONFIG_MAX_CPUS set by the build system
276 lines
5.6 KiB
C
276 lines
5.6 KiB
C
#include "kernel/kernel.h"
|
|
#include "acpi.h"
|
|
|
|
typedef int ((* acpi_read_t)(phys_bytes addr, void * buff, size_t size));
|
|
|
|
struct acpi_rsdp acpi_rsdp;
|
|
|
|
PRIVATE acpi_read_t read_func;
|
|
|
|
#define MAX_RSDT 35 /* ACPI defines 35 signatures */
|
|
|
|
PRIVATE struct acpi_rsdt {
|
|
struct acpi_sdt_header hdr;
|
|
u32_t data[MAX_RSDT];
|
|
} rsdt;
|
|
|
|
PRIVATE struct {
|
|
char signature [ACPI_SDT_SIGNATURE_LEN + 1];
|
|
size_t length;
|
|
} sdt_trans[MAX_RSDT];
|
|
|
|
PRIVATE int sdt_count;
|
|
|
|
PRIVATE int acpi_check_csum(struct acpi_sdt_header * tb, size_t size)
|
|
{
|
|
u8_t total = 0;
|
|
int i;
|
|
for (i = 0; i < size; i++)
|
|
total += ((unsigned char *)tb)[i];
|
|
return total == 0 ? 0 : -1;
|
|
}
|
|
|
|
PRIVATE int acpi_check_signature(const char * orig, const char * match)
|
|
{
|
|
return strncmp(orig, match, ACPI_SDT_SIGNATURE_LEN);
|
|
}
|
|
|
|
PRIVATE int acpi_read_sdt_at(phys_bytes addr,
|
|
struct acpi_sdt_header * tb,
|
|
size_t size,
|
|
const char * name)
|
|
{
|
|
struct acpi_sdt_header hdr;
|
|
|
|
/* if NULL is supplied, we only return the size of the table */
|
|
if (tb == NULL) {
|
|
if (read_func(addr, &hdr, sizeof(struct acpi_sdt_header))) {
|
|
printf("ERROR acpi cannot read %s header\n", name);
|
|
return -1;
|
|
}
|
|
|
|
return hdr.length;
|
|
}
|
|
|
|
if (read_func(addr, tb, sizeof(struct acpi_sdt_header))) {
|
|
printf("ERROR acpi cannot read %s header\n", name);
|
|
return -1;
|
|
}
|
|
|
|
if (acpi_check_signature(tb->signature, name)) {
|
|
printf("ERROR acpi %s signature does not match\n", name);
|
|
return -1;
|
|
}
|
|
|
|
if (size < tb->length) {
|
|
printf("ERROR acpi buffer too small for %s\n", name);
|
|
return -1;
|
|
}
|
|
|
|
if (read_func(addr, tb, size)) {
|
|
printf("ERROR acpi cannot read %s\n", name);
|
|
return -1;
|
|
}
|
|
|
|
if (acpi_check_csum(tb, tb->length)) {
|
|
printf("ERROR acpi %s checksum does not match\n", name);
|
|
return -1;
|
|
}
|
|
|
|
return tb->length;
|
|
}
|
|
|
|
PRIVATE phys_bytes acpi_get_table_base(const char * name)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < sdt_count; i++) {
|
|
if (strncmp(name, sdt_trans[i].signature,
|
|
ACPI_SDT_SIGNATURE_LEN) == 0)
|
|
return (phys_bytes) rsdt.data[i];
|
|
}
|
|
|
|
return (phys_bytes) NULL;
|
|
}
|
|
|
|
PRIVATE size_t acpi_get_table_length(const char * name)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < sdt_count; i++) {
|
|
if (strncmp(name, sdt_trans[i].signature,
|
|
ACPI_SDT_SIGNATURE_LEN) == 0)
|
|
return sdt_trans[i].length;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
PRIVATE void * acpi_madt_get_typed_item(struct acpi_madt_hdr * hdr,
|
|
unsigned char type,
|
|
unsigned idx)
|
|
{
|
|
u8_t * t, * end;
|
|
int i;
|
|
|
|
t = (u8_t *) hdr + sizeof(struct acpi_madt_hdr);
|
|
end = (u8_t *) hdr + hdr->hdr.length;
|
|
|
|
i = 0;
|
|
while(t < end) {
|
|
if (type == ((struct acpi_madt_item_hdr *) t)->type) {
|
|
if (i == idx)
|
|
return t;
|
|
else
|
|
i++;
|
|
}
|
|
t += ((struct acpi_madt_item_hdr *) t)->length;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PRIVATE void * acpi_madt_get_item(struct acpi_madt_hdr * hdr,
|
|
unsigned idx)
|
|
{
|
|
u8_t * t, * end;
|
|
int i;
|
|
|
|
t = (u8_t *) hdr + sizeof(struct acpi_madt_hdr);
|
|
end = (u8_t *) hdr + hdr->hdr.length;
|
|
|
|
for(i = 0 ; i <= idx && t < end; i++) {
|
|
if (i == idx)
|
|
return t;
|
|
t += ((struct acpi_madt_item_hdr *) t)->length;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
PRIVATE int acpi_rsdp_test(void * buff)
|
|
{
|
|
struct acpi_rsdp * rsdp = (struct acpi_rsdp *) buff;
|
|
|
|
if (!platform_tbl_checksum_ok(buff, 20))
|
|
return 0;
|
|
if (strncmp(rsdp->signature, "RSD PTR ", 8))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
PRIVATE int get_acpi_rsdp(void)
|
|
{
|
|
u16_t ebda;
|
|
/*
|
|
* Read 40:0Eh - to find the starting address of the EBDA.
|
|
*/
|
|
phys_copy (0x40E, vir2phys(&ebda), sizeof(ebda));
|
|
if (ebda) {
|
|
ebda <<= 4;
|
|
if(platform_tbl_ptr(ebda, ebda + 0x400, 16, &acpi_rsdp,
|
|
sizeof(acpi_rsdp), &machine.acpi_rsdp,
|
|
acpi_rsdp_test))
|
|
return 1;
|
|
}
|
|
|
|
/* try BIOS read only mem space */
|
|
if(platform_tbl_ptr(0xE0000, 0x100000, 16, &acpi_rsdp,
|
|
sizeof(acpi_rsdp), &machine.acpi_rsdp,
|
|
acpi_rsdp_test))
|
|
return 1;
|
|
|
|
machine.acpi_rsdp = 0; /* RSDP cannot be found at this address therefore
|
|
it is a valid negative value */
|
|
return 0;
|
|
}
|
|
|
|
PRIVATE int acpi_read_kernel(phys_bytes addr, void * buff, size_t size)
|
|
{
|
|
phys_copy(addr, vir2phys(buff), size);
|
|
return 0;
|
|
}
|
|
|
|
PUBLIC void acpi_init(void)
|
|
{
|
|
int s, i;
|
|
read_func = acpi_read_kernel;
|
|
|
|
if (!get_acpi_rsdp()) {
|
|
printf("WARNING : Cannot configure ACPI\n");
|
|
return;
|
|
}
|
|
|
|
s = acpi_read_sdt_at(acpi_rsdp.rsdt_addr, (struct acpi_sdt_header *) &rsdt,
|
|
sizeof(struct acpi_rsdt), ACPI_SDT_SIGNATURE(RSDT));
|
|
|
|
sdt_count = (s - sizeof(struct acpi_sdt_header)) / sizeof(u32_t);
|
|
|
|
for (i = 0; i < sdt_count; i++) {
|
|
struct acpi_sdt_header hdr;
|
|
int j;
|
|
if (read_func(rsdt.data[i], &hdr, sizeof(struct acpi_sdt_header))) {
|
|
printf("ERROR acpi cannot read header at 0x%x\n",
|
|
rsdt.data[i]);
|
|
return;
|
|
}
|
|
|
|
for (j = 0 ; j < ACPI_SDT_SIGNATURE_LEN; j++)
|
|
sdt_trans[i].signature[j] = hdr.signature[j];
|
|
sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0';
|
|
sdt_trans[i].length = hdr.length;
|
|
}
|
|
}
|
|
|
|
PUBLIC struct acpi_madt_ioapic * acpi_get_ioapic_next(void)
|
|
{
|
|
static unsigned idx = 0;
|
|
static struct acpi_madt_hdr * madt_hdr;
|
|
|
|
struct acpi_madt_ioapic * ret;
|
|
|
|
if (idx == 0) {
|
|
madt_hdr = (struct acpi_madt_hdr *)
|
|
phys2vir(acpi_get_table_base("APIC"));
|
|
if (madt_hdr == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
ret = (struct acpi_madt_ioapic *)
|
|
acpi_madt_get_typed_item(madt_hdr, ACPI_MADT_TYPE_IOAPIC, idx);
|
|
if (ret)
|
|
idx++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
PUBLIC struct acpi_madt_lapic * acpi_get_lapic_next(void)
|
|
{
|
|
static unsigned idx = 0;
|
|
static struct acpi_madt_hdr * madt_hdr;
|
|
|
|
struct acpi_madt_lapic * ret;
|
|
|
|
if (idx == 0) {
|
|
madt_hdr = (struct acpi_madt_hdr *)
|
|
phys2vir(acpi_get_table_base("APIC"));
|
|
if (madt_hdr == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
for (;;) {
|
|
ret = (struct acpi_madt_lapic *)
|
|
acpi_madt_get_typed_item(madt_hdr,
|
|
ACPI_MADT_TYPE_LAPIC, idx);
|
|
if (!ret)
|
|
break;
|
|
|
|
idx++;
|
|
|
|
/* report only usable CPUs */
|
|
if (ret->flags & 1)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|