#include <config.h>
#include <util.h>
#include <assert.h>
#include <machine/io.h>
#include <arch/linker.h>
#include <plat/machine.h>
#include <plat/machine/acpi.h>
#include <plat/machine/devices.h>
enum acpi_type {
ACPI_RSDP,
ACPI_RSDT
};
typedef struct acpi_rsdp {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
uint32_t length;
uint32_t xsdt_address[2];
uint8_t extended_checksum;
char reserved[3];
} acpi_rsdp_t;
compile_assert(acpi_rsdp_packed, sizeof(acpi_rsdp_t) == 36)
typedef struct acpi_dmar {
acpi_header_t header;
uint8_t host_addr_width;
uint8_t flags;
uint8_t reserved[10];
} acpi_dmar_t;
compile_assert(acpi_dmar_packed,
sizeof(acpi_dmar_t) == sizeof(acpi_header_t) + 12)
typedef struct acpi_dmar_header {
uint16_t type;
uint16_t length;
} acpi_dmar_header_t;
compile_assert(acpi_dmar_header_packed, sizeof(acpi_dmar_header_t) == 4)
enum acpi_table_dmar_struct_type {
DMAR_DRHD = 0,
DMAR_RMRR = 1,
DMAR_ATSR = 2,
};
typedef struct acpi_dmar_drhd {
acpi_dmar_header_t header;
uint8_t flags;
uint8_t reserved;
uint16_t segment;
uint32_t reg_base[2];
} acpi_dmar_drhd_t;
compile_assert(acpi_dmar_drhd_packed,
sizeof(acpi_dmar_drhd_t) == sizeof(acpi_dmar_header_t) + 12)
typedef struct acpi_dmar_devscope {
uint8_t type;
uint8_t length;
uint16_t reserved;
uint8_t enum_id;
uint8_t start_bus;
struct {
uint8_t dev;
uint8_t fun;
} path_0;
} acpi_dmar_devscope_t;
compile_assert(acpi_dmar_devscope_packed, sizeof(acpi_dmar_devscope_t) == 8)
typedef struct acpi_dmar_rmrr {
acpi_dmar_header_t header;
uint16_t reserved;
uint16_t segment;
uint32_t reg_base[2];
uint32_t reg_limit[2];
acpi_dmar_devscope_t devscope_0;
} acpi_dmar_rmrr_t;
compile_assert(acpi_dmar_rmrr_packed, sizeof(acpi_dmar_rmrr_t) ==
sizeof(acpi_dmar_header_t) + 20 + sizeof(acpi_dmar_devscope_t))
typedef struct acpi_fadt {
acpi_header_t header;
uint8_t reserved[76];
uint32_t flags;
} acpi_fadt_t;
compile_assert(acpi_fadt_packed,
sizeof(acpi_fadt_t) == sizeof(acpi_header_t) + 80)
typedef struct acpi_madt {
acpi_header_t header;
uint32_t apic_addr;
uint32_t flags;
} acpi_madt_t;
compile_assert(acpi_madt_packed,
sizeof(acpi_madt_t) == sizeof(acpi_header_t) + 8)
typedef struct acpi_madt_header {
uint8_t type;
uint8_t length;
} acpi_madt_header_t;
compile_assert(acpi_madt_header_packed, sizeof(acpi_madt_header_t) == 2)
enum acpi_table_madt_struct_type {
MADT_APIC = 0,
MADT_IOAPIC = 1,
MADT_ISO = 2,
MADT_x2APIC = 9
};
typedef struct acpi_madt_apic {
acpi_madt_header_t header;
uint8_t cpu_id;
uint8_t apic_id;
uint32_t flags;
} acpi_madt_apic_t;
compile_assert(acpi_madt_apic_packed,
sizeof(acpi_madt_apic_t) == sizeof(acpi_madt_header_t) + 6)
typedef struct acpi_madt_x2apic {
acpi_madt_header_t header;
uint16_t reserved;
uint32_t x2apic_id;
uint32_t flags;
uint32_t acpi_processor_uid;
} acpi_madt_x2apic_t;
compile_assert(acpi_madt_apic_packed,
sizeof(acpi_madt_x2apic_t) == sizeof(acpi_madt_header_t) + 14)
typedef struct acpi_madt_ioapic {
acpi_madt_header_t header;
uint8_t ioapic_id;
uint8_t reserved[1];
uint32_t ioapic_addr;
uint32_t gsib;
} acpi_madt_ioapic_t;
compile_assert(acpi_madt_ioapic_packed,
sizeof(acpi_madt_ioapic_t) == sizeof(acpi_madt_header_t) + 10)
typedef struct acpi_madt_iso {
acpi_madt_header_t header;
uint8_t bus;
uint8_t source;
uint32_t gsi;
uint16_t flags;
} acpi_madt_iso_t;
compile_assert(acpi_madt_iso_packed,
OFFSETOF(acpi_madt_iso_t, flags) == sizeof(acpi_madt_header_t) + 6)
const char acpi_str_rsd[] = {'R', 'S', 'D', ' ', 'P', 'T', 'R', ' ', 0};
const char acpi_str_fadt[] = {'F', 'A', 'C', 'P', 0};
const char acpi_str_apic[] = {'A', 'P', 'I', 'C', 0};
const char acpi_str_dmar[] = {'D', 'M', 'A', 'R', 0};
BOOT_CODE static uint8_t
acpi_calc_checksum(char* start, uint32_t length)
{
uint8_t checksum = 0;
while (length > 0) {
checksum += *start;
start++;
length--;
}
return checksum;
}
BOOT_CODE static acpi_rsdp_t*
acpi_get_rsdp(void)
{
char* addr;
for (addr = (char*)BIOS_PADDR_START; addr < (char*)BIOS_PADDR_END; addr += 16) {
if (strncmp(addr, acpi_str_rsd, 8) == 0) {
if (acpi_calc_checksum(addr, 20) == 0) {
return (acpi_rsdp_t*)addr;
}
}
}
return NULL;
}
void* acpi_table_init(void* entry, enum acpi_type table_type);
BOOT_CODE void*
acpi_table_init(void* entry, enum acpi_type table_type)
{
void* acpi_table;
unsigned int pages_for_table;
unsigned int pages_for_header = 1;
unsigned long offset_in_page = (unsigned long)entry & MASK(LARGE_PAGE_BITS);
if (MASK(LARGE_PAGE_BITS) - offset_in_page < sizeof(acpi_rsdp_t)) {
pages_for_header++;
}
acpi_table = map_temp_boot_page(entry, pages_for_header);
switch (table_type) {
case ACPI_RSDP: {
acpi_rsdp_t *rsdp_entry = (acpi_rsdp_t*)entry;
pages_for_table = (rsdp_entry->length + offset_in_page) / MASK(LARGE_PAGE_BITS) + 1;
break;
}
case ACPI_RSDT: { acpi_rsdt_t *rsdt_entry = (acpi_rsdt_t*)entry;
pages_for_table = (rsdt_entry->header.length + offset_in_page) / MASK(LARGE_PAGE_BITS) + 1;
break;
}
default:
printf("Error: Mapping unknown ACPI table type\n");
assert(false);
return NULL;
}
acpi_table = map_temp_boot_page(entry, pages_for_table);
return acpi_table;
}
BOOT_CODE acpi_rsdt_t*
acpi_init(void)
{
acpi_rsdp_t* acpi_rsdp = acpi_get_rsdp();
acpi_rsdt_t* acpi_rsdt;
acpi_rsdt_t* acpi_rsdt_mapped;
if (acpi_rsdp == NULL) {
printf("BIOS: No ACPI support detected\n");
return NULL;
}
printf("ACPI: RSDP paddr=%p\n", acpi_rsdp);
acpi_rsdp = acpi_table_init(acpi_rsdp, ACPI_RSDP);
printf("ACPI: RSDP vaddr=%p\n", acpi_rsdp);
acpi_rsdt = (acpi_rsdt_t*)(word_t)acpi_rsdp->rsdt_address;
printf("ACPI: RSDT paddr=%p\n", acpi_rsdt);
acpi_rsdt_mapped = (acpi_rsdt_t*)acpi_table_init(acpi_rsdt, ACPI_RSDT);
printf("ACPI: RSDT vaddr=%p\n", acpi_rsdt_mapped);
assert(acpi_rsdt_mapped->header.length > 0);
if (acpi_calc_checksum((char*)acpi_rsdt_mapped, acpi_rsdt_mapped->header.length) != 0) {
printf("ACPI: RSDT checksum failure\n");
return NULL;
}
return acpi_rsdt;
}
BOOT_CODE uint32_t
acpi_madt_scan(
acpi_rsdt_t* acpi_rsdt,
cpu_id_t* cpu_list,
uint32_t* num_ioapic,
paddr_t* ioapic_paddrs
)
{
unsigned int entries;
uint32_t num_cpu;
uint32_t count;
acpi_madt_t* acpi_madt;
acpi_madt_header_t* acpi_madt_header;
acpi_rsdt_t* acpi_rsdt_mapped;
acpi_madt_t* acpi_madt_mapped;
acpi_rsdt_mapped = (acpi_rsdt_t*)acpi_table_init(acpi_rsdt, ACPI_RSDT);
num_cpu = 0;
*num_ioapic = 0;
assert(acpi_rsdt_mapped->header.length >= sizeof(acpi_header_t));
entries = (acpi_rsdt_mapped->header.length - sizeof(acpi_header_t)) / sizeof(uint32_t);
for (count = 0; count < entries; count++) {
acpi_madt = (acpi_madt_t*)(word_t)acpi_rsdt_mapped->entry[count];
acpi_madt_mapped = (acpi_madt_t*)acpi_table_init(acpi_madt, ACPI_RSDT);
if (strncmp(acpi_str_apic, acpi_madt_mapped->header.signature, 4) == 0) {
printf("ACPI: MADT paddr=%p\n", acpi_madt);
printf("ACPI: MADT vaddr=%p\n", acpi_madt_mapped);
printf("ACPI: MADT apic_addr=0x%x\n", acpi_madt_mapped->apic_addr);
printf("ACPI: MADT flags=0x%x\n", acpi_madt_mapped->flags);
acpi_madt_header = (acpi_madt_header_t*)(acpi_madt_mapped + 1);
while ((char*)acpi_madt_header < (char*)acpi_madt_mapped + acpi_madt_mapped->header.length) {
switch (acpi_madt_header->type) {
case MADT_APIC: {
uint8_t cpu_id = ((acpi_madt_apic_t*)acpi_madt_header)->apic_id;
uint32_t flags = ((acpi_madt_apic_t*)acpi_madt_header)->flags;
if (flags == 1) {
printf("ACPI: MADT_APIC apic_id=0x%x\n", cpu_id);
if (num_cpu == CONFIG_MAX_NUM_NODES) {
printf("ACPI: Not recording this APIC, only support %d\n", CONFIG_MAX_NUM_NODES);
} else {
cpu_list[num_cpu] = cpu_id;
num_cpu++;
}
}
break;
}
case MADT_x2APIC: {
uint32_t cpu_id = ((acpi_madt_x2apic_t*)acpi_madt_header)->x2apic_id;
uint32_t flags = ((acpi_madt_x2apic_t*)acpi_madt_header)->flags;
if (flags == 1) {
printf("ACPI: MADT_x2APIC apic_id=0x%x\n", cpu_id);
if (num_cpu == CONFIG_MAX_NUM_NODES) {
printf("ACPI: Not recording this APIC, only support %d\n", CONFIG_MAX_NUM_NODES);
} else {
cpu_list[num_cpu] = cpu_id;
num_cpu++;
}
}
break;
}
case MADT_IOAPIC:
printf(
"ACPI: MADT_IOAPIC ioapic_id=%d ioapic_addr=0x%x gsib=%d\n",
((acpi_madt_ioapic_t*)acpi_madt_header)->ioapic_id,
((acpi_madt_ioapic_t*)acpi_madt_header)->ioapic_addr,
((acpi_madt_ioapic_t*)acpi_madt_header)->gsib
);
if (*num_ioapic == CONFIG_MAX_NUM_IOAPIC) {
printf("ACPI: Not recording this IOAPIC, only support %d\n", CONFIG_MAX_NUM_IOAPIC);
} else {
ioapic_paddrs[*num_ioapic] = ((acpi_madt_ioapic_t*)acpi_madt_header)->ioapic_addr;
(*num_ioapic)++;
}
break;
case MADT_ISO:
printf("ACPI: MADT_ISO bus=%d source=%d gsi=%d flags=0x%x\n",
((acpi_madt_iso_t*)acpi_madt_header)->bus,
((acpi_madt_iso_t*)acpi_madt_header)->source,
((acpi_madt_iso_t*)acpi_madt_header)->gsi,
((acpi_madt_iso_t*)acpi_madt_header)->flags);
break;
default:
break;
}
acpi_madt_header = (acpi_madt_header_t*)((char*)acpi_madt_header + acpi_madt_header->length);
}
}
}
printf("ACPI: %d CPU(s) detected\n", num_cpu);
return num_cpu;
}
BOOT_CODE bool_t
acpi_fadt_scan(
acpi_rsdt_t* acpi_rsdt
)
{
unsigned int entries;
uint32_t count;
acpi_fadt_t* acpi_fadt;
acpi_rsdt_t* acpi_rsdt_mapped;
acpi_fadt_t* acpi_fadt_mapped;
acpi_rsdt_mapped = (acpi_rsdt_t*)acpi_table_init(acpi_rsdt, ACPI_RSDT);
assert(acpi_rsdt_mapped->header.length >= sizeof(acpi_header_t));
entries = (acpi_rsdt_mapped->header.length - sizeof(acpi_header_t)) / sizeof(uint32_t);
for (count = 0; count < entries; count++) {
acpi_fadt = (acpi_fadt_t*)(word_t)acpi_rsdt_mapped->entry[count];
acpi_fadt_mapped = (acpi_fadt_t*)acpi_table_init(acpi_fadt, ACPI_RSDT);
if (strncmp(acpi_str_fadt, acpi_fadt_mapped->header.signature, 4) == 0) {
printf("ACPI: FADT paddr=%p\n", acpi_fadt);
printf("ACPI: FADT vaddr=%p\n", acpi_fadt_mapped);
printf("ACPI: FADT flags=0x%x\n", acpi_fadt_mapped->flags);
if (config_set(CONFIG_USE_LOGICAL_IDS) &&
acpi_fadt_mapped->flags & BIT(19)) {
printf("system requires apic physical mode\n");
return false;
}
}
}
return true;
}
BOOT_CODE void
acpi_dmar_scan(
acpi_rsdt_t* acpi_rsdt,
paddr_t* drhu_list,
uint32_t* num_drhu,
uint32_t max_drhu_list_len,
acpi_rmrr_list_t *rmrr_list
)
{
word_t i;
unsigned int entries;
uint32_t count;
uint32_t reg_basel, reg_baseh;
int rmrr_count;
dev_id_t dev_id;
acpi_dmar_t* acpi_dmar;
acpi_dmar_header_t* acpi_dmar_header;
acpi_dmar_rmrr_t* acpi_dmar_rmrr;
acpi_dmar_devscope_t* acpi_dmar_devscope;
acpi_rsdt_t* acpi_rsdt_mapped;
acpi_dmar_t* acpi_dmar_mapped;
acpi_rsdt_mapped = (acpi_rsdt_t*)acpi_table_init(acpi_rsdt, ACPI_RSDT);
*num_drhu = 0;
rmrr_count = 0;
assert(acpi_rsdt_mapped->header.length >= sizeof(acpi_header_t));
entries = (acpi_rsdt_mapped->header.length - sizeof(acpi_header_t)) / sizeof(uint32_t);
for (count = 0; count < entries; count++) {
acpi_dmar = (acpi_dmar_t*)(word_t)acpi_rsdt_mapped->entry[count];
acpi_dmar_mapped = (acpi_dmar_t*)acpi_table_init(acpi_dmar, ACPI_RSDT);
if (strncmp(acpi_str_dmar, acpi_dmar_mapped->header.signature, 4) == 0) {
printf("ACPI: DMAR paddr=%p\n", acpi_dmar);
printf("ACPI: DMAR vaddr=%p\n", acpi_dmar_mapped);
printf("ACPI: IOMMU host address width: %d\n", acpi_dmar_mapped->host_addr_width + 1);
acpi_dmar_header = (acpi_dmar_header_t*)(acpi_dmar_mapped + 1);
while ((char*)acpi_dmar_header < (char*)acpi_dmar_mapped + acpi_dmar_mapped->header.length) {
switch (acpi_dmar_header->type) {
case DMAR_DRHD:
if (*num_drhu == max_drhu_list_len) {
printf("ACPI: too many IOMMUs, disabling IOMMU support\n");
*num_drhu = 0;
return;
}
reg_basel = ((acpi_dmar_drhd_t*)acpi_dmar_header)->reg_base[0];
reg_baseh = ((acpi_dmar_drhd_t*)acpi_dmar_header)->reg_base[1];
if (reg_baseh != 0) {
printf("ACPI: DMAR_DRHD reg_base exceeds 32 bit, disabling IOMMU support\n");
*num_drhu = 0;
return;
}
drhu_list[*num_drhu] = (paddr_t)reg_basel;
(*num_drhu)++;
break;
case DMAR_RMRR:
acpi_dmar_rmrr = (acpi_dmar_rmrr_t*)acpi_dmar_header;
if (acpi_dmar_rmrr->reg_base[1] != 0 ||
acpi_dmar_rmrr->reg_limit[1] != 0) {
printf("ACPI: RMRR device above 4GiB, disabling IOMMU support\n");
*num_drhu = 0;
return ;
}
for (i = 0; i <= (acpi_dmar_header->length - sizeof(acpi_dmar_rmrr_t)) / sizeof(acpi_dmar_devscope_t); i++) {
acpi_dmar_devscope = &acpi_dmar_rmrr->devscope_0 + i;
if (acpi_dmar_devscope->type != 1) {
printf("ACPI: RMRR device scope: non-PCI-Endpoint-Devices not supported yet, disabling IOMMU support\n");
*num_drhu = 0;
return;
}
if (acpi_dmar_devscope->length > sizeof(acpi_dmar_devscope_t)) {
printf("ACPI: RMRR device scope: devices behind bridges not supported yet, disabling IOMMU support\n");
*num_drhu = 0;
return;
}
dev_id =
get_dev_id(
acpi_dmar_devscope->start_bus,
acpi_dmar_devscope->path_0.dev,
acpi_dmar_devscope->path_0.fun
);
if (rmrr_count == CONFIG_MAX_RMRR_ENTRIES) {
printf("ACPI: Too many RMRR entries, disabling IOMMU support\n");
*num_drhu = 0;
return;
}
printf("\tACPI: registering RMRR entry for region for device: bus=0x%x dev=0x%x fun=0x%x\n",
acpi_dmar_devscope->start_bus,
acpi_dmar_devscope->path_0.dev,
acpi_dmar_devscope->path_0.fun
);
rmrr_list->entries[rmrr_count].device = dev_id;
rmrr_list->entries[rmrr_count].base = acpi_dmar_rmrr->reg_base[0];
rmrr_list->entries[rmrr_count].limit = acpi_dmar_rmrr->reg_limit[0];
rmrr_count++;
}
break;
case DMAR_ATSR:
break;
default:
printf("ACPI: Unknown DMA remapping structure type: %x\n", acpi_dmar_header->type);
}
acpi_dmar_header = (acpi_dmar_header_t*)((char*)acpi_dmar_header + acpi_dmar_header->length);
}
}
}
rmrr_list->num = rmrr_count;
printf("ACPI: %d IOMMUs detected\n", *num_drhu);
}