| /* Copyright (c) 2017 Google Inc. |
| * See LICENSE for details. |
| * |
| * ACPI setup. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/fcntl.h> |
| #include <sys/mman.h> |
| #include <ros/arch/mmu.h> |
| #include <vmm/util.h> |
| #include <vmm/vmm.h> |
| |
| #include <vmm/acpi/acpi.h> |
| #include <vmm/acpi/vmm_simple_dsdt.h> |
| |
| /* By 1999, you could just scan the hardware |
| * and work it out. But 2005, that was no longer possible. How sad. |
| * so we have to fake acpi to make it all work. |
| * This will be copied to memory at 0xe0000, so the kernel can find it. |
| */ |
| |
| /* assume they're all 256 bytes long just to make it easy. |
| * Just have pointers that point to aligned things. |
| */ |
| |
| struct acpi_table_rsdp rsdp = { |
| .signature = ACPI_SIG_RSDP, |
| .oem_id = "AKAROS", |
| .revision = 2, |
| .length = 36, |
| }; |
| |
| struct acpi_table_xsdt xsdt = { |
| .header = { |
| .signature = ACPI_SIG_DSDT, |
| .revision = 2, |
| .oem_id = "AKAROS", |
| .oem_table_id = "ALPHABET", |
| .oem_revision = 0, |
| .asl_compiler_id = "RON ", |
| .asl_compiler_revision = 0, |
| }, |
| }; |
| struct acpi_table_fadt fadt = { |
| .header = { |
| .signature = ACPI_SIG_FADT, |
| .revision = 2, |
| .oem_id = "AKAROS", |
| .oem_table_id = "ALPHABET", |
| .oem_revision = 0, |
| .asl_compiler_id = "RON ", |
| .asl_compiler_revision = 0, |
| }, |
| }; |
| |
| |
| /* This has to be dropped into memory, then the other crap just follows it. |
| */ |
| struct acpi_table_madt madt = { |
| .header = { |
| .signature = ACPI_SIG_MADT, |
| .revision = 2, |
| .oem_id = "AKAROS", |
| .oem_table_id = "ALPHABET", |
| .oem_revision = 0, |
| .asl_compiler_id = "RON ", |
| .asl_compiler_revision = 0, |
| }, |
| |
| .address = APIC_GPA, |
| .flags = 0, |
| }; |
| |
| struct acpi_madt_io_apic |
| Apic1 = {.header = {.type = ACPI_MADT_TYPE_IO_APIC, |
| .length = sizeof(struct acpi_madt_io_apic)}, |
| .id = 0, .address = 0xfec00000, .global_irq_base = 0}; |
| |
| struct acpi_madt_interrupt_override isor[] = { |
| /* From the ACPI Specification Version 6.1: For example, if your machine |
| * has the ISA Programmable Interrupt Timer (PIT) connected to ISA IRQ |
| * 0, but in APIC mode, it is connected to I/O APIC interrupt input 2, |
| * then you would need an Interrupt Source Override where the source |
| * entry is ‘0’ and the Global System Interrupt is ‘2.’ */ |
| }; |
| |
| void lowmem(void) |
| { |
| asm volatile (".section .lowmem, \"aw\";" |
| "low: ;" |
| ".=0x1000;" |
| ".align 0x100000;" |
| ".previous;"); |
| } |
| |
| static uint8_t acpi_tb_checksum(uint8_t *buffer, uint32_t length) |
| { |
| uint8_t sum = 0; |
| uint8_t *end = buffer + length; |
| |
| fprintf(stderr, "tbchecksum %p for %d", buffer, length); |
| while (buffer < end) { |
| if (end - buffer < 2) |
| fprintf(stderr, "%02x\n", sum); |
| sum = (uint8_t)(sum + *(buffer++)); |
| } |
| fprintf(stderr, " is %02x\n", sum); |
| return sum; |
| } |
| |
| static void gencsum(uint8_t *target, void *data, int len) |
| { |
| uint8_t csum; |
| |
| // blast target to zero so it does not get counted |
| // (it might be in the struct we checksum) And, yes, it is, goodness. |
| fprintf(stderr, "gencsum %p target %p source %d bytes\n", target, data, |
| len); |
| *target = 0; |
| csum = acpi_tb_checksum((uint8_t *)data, len); |
| *target = ~csum + 1; |
| fprintf(stderr, "Cmoputed is %02x\n", *target); |
| } |
| |
| /* Initialize the MADT structs for each local apic. */ |
| static void *init_madt_local_apic(struct virtual_machine *vm, void *start) |
| { |
| struct acpi_madt_local_apic *apic = start; |
| |
| for (int i = 0; i < vm->nr_gpcs; i++) { |
| apic->header.type = ACPI_MADT_TYPE_LOCAL_APIC; |
| apic->header.length = sizeof(struct acpi_madt_local_apic); |
| apic->processor_id = i; |
| apic->id = i; |
| apic->lapic_flags = 1; |
| apic = (void *)apic + sizeof(struct acpi_madt_local_apic); |
| } |
| return apic; |
| } |
| |
| /* Initialize the MADT structs for each local x2apic. */ |
| static void *init_madt_local_x2apic(struct virtual_machine *vm, void *start) |
| { |
| struct acpi_madt_local_x2apic *apic = start; |
| |
| for (int i = 0; i < vm->nr_gpcs; i++) { |
| apic->header.type = ACPI_MADT_TYPE_LOCAL_X2APIC; |
| apic->header.length = sizeof(struct acpi_madt_local_x2apic); |
| apic->local_apic_id = i; |
| apic->uid = i; |
| apic->lapic_flags = 1; |
| apic = (void *)apic + sizeof(struct acpi_madt_local_x2apic); |
| } |
| return apic; |
| } |
| |
| /* smbios reads the smbios into the e segment. By convention the |
| * e segment includes the f segment and is 128 kbytes. */ |
| static int smbios(char *smbiostable, void *esegment) |
| { |
| int amt; |
| |
| amt = cat(smbiostable, esegment, 128 * 1024); |
| if (amt < 0) { |
| fprintf(stderr, "%s: %r\n", smbiostable); |
| exit(1); |
| } |
| |
| return amt; |
| } |
| |
| void *setup_biostables(struct virtual_machine *vm, |
| void *a, void *smbiostable) |
| { |
| struct acpi_table_rsdp *r; |
| struct acpi_table_fadt *f; |
| struct acpi_table_madt *m; |
| struct acpi_table_xsdt *x; |
| void *low1m; |
| uint8_t csum; |
| |
| |
| // The low 1m is so we can fill in bullshit like ACPI. |
| // And, sorry, due to the STUPID format of the RSDP for now we need the |
| // low 1M. |
| low1m = mmap((int*)4096, MiB-4096, PROT_READ | PROT_WRITE, |
| MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
| if (low1m != (void *)4096) { |
| perror("Unable to mmap low 1m"); |
| exit(1); |
| } |
| |
| /* As I understood it, the spec was that SMBIOS |
| * tables live at f0000. We've been finding that |
| * they can have pointers to exxxx. So, for now, |
| * we assume you will take a 128K snapshot of flash |
| * and we'll just splat the whole mess in at |
| * 0xe0000. We can get more sophisticated about |
| * this later if needed. TODO: parse the table, |
| * and make sure that ACPI doesn't trash it. |
| * Although you'll know instantly if that happens |
| * as you'll get dmidecode errors. But it still needs |
| * to be better. */ |
| if (smbiostable) { |
| fprintf(stderr, "Using SMBIOS table %s\n", smbiostable); |
| smbios(smbiostable, (void *)0xe0000); |
| } |
| |
| r = a; |
| fprintf(stderr, "install rsdp to %p\n", r); |
| *r = rsdp; |
| a += sizeof(*r); |
| r->xsdt_physical_address = (uint64_t)a; |
| gencsum(&r->checksum, r, ACPI_RSDP_CHECKSUM_LENGTH); |
| csum = acpi_tb_checksum((uint8_t *) r, ACPI_RSDP_CHECKSUM_LENGTH); |
| if (csum != 0) { |
| fprintf(stderr, "RSDP has bad checksum; summed to %x\n", csum); |
| exit(1); |
| } |
| |
| /* Check extended checksum if table version >= 2 */ |
| gencsum(&r->extended_checksum, r, ACPI_RSDP_XCHECKSUM_LENGTH); |
| if ((rsdp.revision >= 2) && |
| (acpi_tb_checksum((uint8_t*)r, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)) { |
| fprintf(stderr, "RSDP has bad checksum v2\n"); |
| exit(1); |
| } |
| |
| /* just leave a bunch of space for the xsdt. */ |
| /* we need to zero the area since it has pointers. */ |
| x = a; |
| a += sizeof(*x) + 8*sizeof(void *); |
| memset(x, 0, a - (void *)x); |
| fprintf(stderr, "install xsdt to %p\n", x); |
| *x = xsdt; |
| x->table_offset_entry[0] = 0; |
| x->table_offset_entry[1] = 0; |
| x->header.length = a - (void *)x; |
| |
| f = a; |
| fprintf(stderr, "install fadt to %p\n", f); |
| *f = fadt; |
| x->table_offset_entry[0] = (uint64_t)f; // fadt MUST be first in xsdt! |
| a += sizeof(*f); |
| f->header.length = a - (void *)f; |
| |
| f->Xdsdt = (uint64_t) a; |
| fprintf(stderr, "install dsdt to %p\n", a); |
| memcpy(a, &DSDT_DSDTTBL_Header, 36); |
| a += 36; |
| |
| gencsum(&f->header.checksum, f, f->header.length); |
| if (acpi_tb_checksum((uint8_t *)f, f->header.length) != 0) { |
| fprintf(stderr, "fadt has bad checksum v2\n"); |
| exit(1); |
| } |
| |
| m = a; |
| *m = madt; |
| x->table_offset_entry[3] = (uint64_t) m; |
| a += sizeof(*m); |
| fprintf(stderr, "install madt to %p\n", m); |
| |
| a = init_madt_local_apic(vm, a); |
| |
| memmove(a, &Apic1, sizeof(Apic1)); |
| a += sizeof(Apic1); |
| |
| a = init_madt_local_x2apic(vm, a); |
| |
| memmove(a, &isor, sizeof(isor)); |
| a += sizeof(isor); |
| m->header.length = a - (void *)m; |
| |
| gencsum(&m->header.checksum, m, m->header.length); |
| if (acpi_tb_checksum((uint8_t *) m, m->header.length) != 0) { |
| fprintf(stderr, "madt has bad checksum v2\n"); |
| exit(1); |
| } |
| |
| gencsum(&x->header.checksum, x, x->header.length); |
| csum = acpi_tb_checksum((uint8_t *) x, x->header.length); |
| if (csum != 0) { |
| fprintf(stderr, "XSDT has bad checksum; summed to %x\n", csum); |
| exit(1); |
| } |
| |
| fprintf(stderr, "allchecksums ok\n"); |
| |
| a = (void *)(((unsigned long)a + 0xfff) & ~0xfff); |
| |
| return a; |
| } |