static acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp) { ACPI_FUNCTION_ENTRY(); /* * The signature and checksum must both be correct * * Note: Sometimes there exists more than one RSDP in memory; the valid * RSDP has a valid checksum, all others have an invalid checksum. */ if (ACPI_STRNCMP((char *)rsdp, ACPI_SIG_RSDP, sizeof(ACPI_SIG_RSDP) - 1) != 0) { /* Nope, BAD Signature */ return (AE_BAD_SIGNATURE); } /* Check the standard checksum */ if (acpi_tb_checksum((u8 *) rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) { return (AE_BAD_CHECKSUM); } /* Check extended checksum if table version >= 2 */ if ((rsdp->revision >= 2) && (acpi_tb_checksum((u8 *) rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)) { return (AE_BAD_CHECKSUM); } return (AE_OK); }
acpi_status acpi_tb_verify_table_checksum ( struct acpi_table_header *table_header) { u8 checksum; acpi_status status = AE_OK; ACPI_FUNCTION_TRACE ("tb_verify_table_checksum"); /* Compute the checksum on the table */ checksum = acpi_tb_checksum (table_header, table_header->length); /* Return the appropriate exception */ if (checksum) { ACPI_REPORT_WARNING (( "Invalid checksum in table [%4.4s] (%02X, sum %02X is not zero)\n", table_header->signature, (u32) table_header->checksum, (u32) checksum)); status = AE_BAD_CHECKSUM; } return_ACPI_STATUS (status); }
u8 * acpi_tb_scan_memory_for_rsdp ( u8 *start_address, u32 length) { u32 offset; u8 *mem_rover; ACPI_FUNCTION_TRACE ("tb_scan_memory_for_rsdp"); /* Search from given start addr for the requested length */ for (offset = 0, mem_rover = start_address; offset < length; offset += ACPI_RSDP_SCAN_STEP, mem_rover += ACPI_RSDP_SCAN_STEP) { /* The signature and checksum must both be correct */ if (ACPI_STRNCMP ((char *) mem_rover, RSDP_SIG, sizeof (RSDP_SIG)-1) == 0 && acpi_tb_checksum (mem_rover, ACPI_RSDP_CHECKSUM_LENGTH) == 0) { /* If so, we have found the RSDP */ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP located at physical address %p\n",mem_rover)); return_PTR (mem_rover); } } /* Searched entire block, no RSDP was found */ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,"Searched entire block, no RSDP was found.\n")); return_PTR (NULL); }
ACPI_STATUS acpi_tb_verify_table_checksum ( ACPI_TABLE_HEADER *table_header) { u8 checksum; ACPI_STATUS status = AE_OK; /* Compute the checksum on the table */ checksum = acpi_tb_checksum (table_header, table_header->length); /* Return the appropriate exception */ if (checksum) { REPORT_WARNING (("Invalid checksum (%X) in table %4.4s\n", checksum, &table_header->signature)); status = AE_BAD_CHECKSUM; } return (status); }
static int __init pvh_setup_acpi(struct domain *d, paddr_t start_info) { unsigned long pfn, nr_pages; paddr_t madt_paddr, xsdt_paddr, rsdp_paddr; unsigned int i; int rc; struct acpi_table_rsdp *native_rsdp, rsdp = { .signature = ACPI_SIG_RSDP, .revision = 2, .length = sizeof(rsdp), }; /* Scan top-level tables and add their regions to the guest memory map. */ for( i = 0; i < acpi_gbl_root_table_list.count; i++ ) { const char *sig = acpi_gbl_root_table_list.tables[i].signature.ascii; unsigned long addr = acpi_gbl_root_table_list.tables[i].address; unsigned long size = acpi_gbl_root_table_list.tables[i].length; /* * Make sure the original MADT is also mapped, so that Dom0 can * properly access the data returned by _MAT methods in case it's * re-using MADT memory. */ if ( strncmp(sig, ACPI_SIG_MADT, ACPI_NAME_SIZE) ? pvh_acpi_table_allowed(sig) : !acpi_memory_banned(addr, size) ) pvh_add_mem_range(d, addr, addr + size, E820_ACPI); } /* Identity map ACPI e820 regions. */ for ( i = 0; i < d->arch.nr_e820; i++ ) { if ( d->arch.e820[i].type != E820_ACPI && d->arch.e820[i].type != E820_NVS ) continue; pfn = PFN_DOWN(d->arch.e820[i].addr); nr_pages = PFN_UP((d->arch.e820[i].addr & ~PAGE_MASK) + d->arch.e820[i].size); rc = modify_identity_mmio(d, pfn, nr_pages, true); if ( rc ) { printk("Failed to map ACPI region [%#lx, %#lx) into Dom0 memory map\n", pfn, pfn + nr_pages); return rc; } } rc = pvh_setup_acpi_madt(d, &madt_paddr); if ( rc ) return rc; rc = pvh_setup_acpi_xsdt(d, madt_paddr, &xsdt_paddr); if ( rc ) return rc; /* Craft a custom RSDP. */ native_rsdp = acpi_os_map_memory(acpi_os_get_root_pointer(), sizeof(rsdp)); if ( !native_rsdp ) { printk("Failed to map native RSDP\n"); return -ENOMEM; } memcpy(rsdp.oem_id, native_rsdp->oem_id, sizeof(rsdp.oem_id)); acpi_os_unmap_memory(native_rsdp, sizeof(rsdp)); rsdp.xsdt_physical_address = xsdt_paddr; /* * Calling acpi_tb_checksum here is a layering violation, but * introducing a wrapper for such simple usage seems overkill. */ rsdp.checksum -= acpi_tb_checksum(ACPI_CAST_PTR(u8, &rsdp), ACPI_RSDP_REV0_SIZE); rsdp.extended_checksum -= acpi_tb_checksum(ACPI_CAST_PTR(u8, &rsdp), sizeof(rsdp)); /* * Place the new RSDP in guest memory space. * * NB: this RSDP is not going to replace the original RSDP, which should * still be accessible to the guest. However that RSDP is going to point to * the native RSDT, and should not be used for the Dom0 kernel's boot * purposes (we keep it visible for post boot access). */ if ( pvh_steal_ram(d, sizeof(rsdp), 0, GB(4), &rsdp_paddr) ) { printk("Unable to allocate guest RAM for RSDP\n"); return -ENOMEM; } /* Mark this region as E820_ACPI. */ if ( pvh_add_mem_range(d, rsdp_paddr, rsdp_paddr + sizeof(rsdp), E820_ACPI) ) printk("Unable to add RSDP region to memory map\n"); /* Copy RSDP into guest memory. */ rc = hvm_copy_to_guest_phys(rsdp_paddr, &rsdp, sizeof(rsdp), d->vcpu[0]); if ( rc ) { printk("Unable to copy RSDP into guest memory\n"); return rc; } /* Copy RSDP address to start_info. */ rc = hvm_copy_to_guest_phys(start_info + offsetof(struct hvm_start_info, rsdp_paddr), &rsdp_paddr, sizeof(((struct hvm_start_info *) 0)->rsdp_paddr), d->vcpu[0]); if ( rc ) { printk("Unable to copy RSDP into guest memory\n"); return rc; } return 0; } int __init dom0_construct_pvh(struct domain *d, const module_t *image, unsigned long image_headroom, module_t *initrd, void *(*bootstrap_map)(const module_t *), char *cmdline) { paddr_t entry, start_info; int rc; printk("** Building a PVH Dom0 **\n"); iommu_hwdom_init(d); rc = pvh_setup_p2m(d); if ( rc ) { printk("Failed to setup Dom0 physical memory map\n"); return rc; } rc = pvh_load_kernel(d, image, image_headroom, initrd, bootstrap_map(image), cmdline, &entry, &start_info); if ( rc ) { printk("Failed to load Dom0 kernel\n"); return rc; } rc = pvh_setup_cpus(d, entry, start_info); if ( rc ) { printk("Failed to setup Dom0 CPUs: %d\n", rc); return rc; } rc = pvh_setup_acpi(d, start_info); if ( rc ) { printk("Failed to setup Dom0 ACPI tables: %d\n", rc); return rc; } panic("Building a PVHv2 Dom0 is not yet supported."); return 0; }
static int __init pvh_setup_acpi_xsdt(struct domain *d, paddr_t madt_addr, paddr_t *addr) { struct acpi_table_xsdt *xsdt; struct acpi_table_header *table; struct acpi_table_rsdp *rsdp; unsigned long size = sizeof(*xsdt); unsigned int i, j, num_tables = 0; paddr_t xsdt_paddr; int rc; /* * Restore original DMAR table signature, we are going to filter it from * the new XSDT that is presented to the guest, so it is no longer * necessary to have it's signature zapped. */ acpi_dmar_reinstate(); /* Count the number of tables that will be added to the XSDT. */ for( i = 0; i < acpi_gbl_root_table_list.count; i++ ) { const char *sig = acpi_gbl_root_table_list.tables[i].signature.ascii; if ( pvh_acpi_table_allowed(sig) ) num_tables++; } /* * No need to add or subtract anything because struct acpi_table_xsdt * includes one array slot already, and we have filtered out the original * MADT and we are going to add a custom built MADT. */ size += num_tables * sizeof(xsdt->table_offset_entry[0]); xsdt = xzalloc_bytes(size); if ( !xsdt ) { printk("Unable to allocate memory for XSDT table\n"); rc = -ENOMEM; goto out; } /* Copy the native XSDT table header. */ rsdp = acpi_os_map_memory(acpi_os_get_root_pointer(), sizeof(*rsdp)); if ( !rsdp ) { printk("Unable to map RSDP\n"); rc = -EINVAL; goto out; } xsdt_paddr = rsdp->xsdt_physical_address; acpi_os_unmap_memory(rsdp, sizeof(*rsdp)); table = acpi_os_map_memory(xsdt_paddr, sizeof(*table)); if ( !table ) { printk("Unable to map XSDT\n"); rc = -EINVAL; goto out; } xsdt->header = *table; acpi_os_unmap_memory(table, sizeof(*table)); /* Add the custom MADT. */ xsdt->table_offset_entry[0] = madt_addr; /* Copy the addresses of the rest of the allowed tables. */ for( i = 0, j = 1; i < acpi_gbl_root_table_list.count; i++ ) { const char *sig = acpi_gbl_root_table_list.tables[i].signature.ascii; if ( pvh_acpi_table_allowed(sig) ) xsdt->table_offset_entry[j++] = acpi_gbl_root_table_list.tables[i].address; } xsdt->header.revision = 1; xsdt->header.length = size; /* * Calling acpi_tb_checksum here is a layering violation, but * introducing a wrapper for such simple usage seems overkill. */ xsdt->header.checksum -= acpi_tb_checksum(ACPI_CAST_PTR(u8, xsdt), size); /* Place the new XSDT in guest memory space. */ if ( pvh_steal_ram(d, size, 0, GB(4), addr) ) { printk("Unable to find guest RAM for XSDT\n"); rc = -ENOMEM; goto out; } /* Mark this region as E820_ACPI. */ if ( pvh_add_mem_range(d, *addr, *addr + size, E820_ACPI) ) printk("Unable to add XSDT region to memory map\n"); rc = hvm_copy_to_guest_phys(*addr, xsdt, size, d->vcpu[0]); if ( rc ) { printk("Unable to copy XSDT into guest memory\n"); goto out; } rc = 0; out: xfree(xsdt); return rc; }
static int __init pvh_setup_acpi_madt(struct domain *d, paddr_t *addr) { struct acpi_table_madt *madt; struct acpi_table_header *table; struct acpi_madt_io_apic *io_apic; struct acpi_madt_local_x2apic *x2apic; acpi_status status; unsigned long size; unsigned int i, max_vcpus; int rc; /* Count number of interrupt overrides in the MADT. */ acpi_table_parse_madt(ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, acpi_count_intr_ovr, UINT_MAX); /* Count number of NMI sources in the MADT. */ acpi_table_parse_madt(ACPI_MADT_TYPE_NMI_SOURCE, acpi_count_nmi_src, UINT_MAX); max_vcpus = dom0_max_vcpus(); /* Calculate the size of the crafted MADT. */ size = sizeof(*madt); size += sizeof(*io_apic) * nr_ioapics; size += sizeof(*intsrcovr) * acpi_intr_overrides; size += sizeof(*nmisrc) * acpi_nmi_sources; size += sizeof(*x2apic) * max_vcpus; madt = xzalloc_bytes(size); if ( !madt ) { printk("Unable to allocate memory for MADT table\n"); rc = -ENOMEM; goto out; } /* Copy the native MADT table header. */ status = acpi_get_table(ACPI_SIG_MADT, 0, &table); if ( !ACPI_SUCCESS(status) ) { printk("Failed to get MADT ACPI table, aborting.\n"); rc = -EINVAL; goto out; } madt->header = *table; madt->address = APIC_DEFAULT_PHYS_BASE; /* * NB: this is currently set to 4, which is the revision in the ACPI * spec 6.1. Sadly ACPICA doesn't provide revision numbers for the * tables described in the headers. */ madt->header.revision = min_t(unsigned char, table->revision, 4); /* Setup the IO APIC entries. */ io_apic = (void *)(madt + 1); for ( i = 0; i < nr_ioapics; i++ ) { io_apic->header.type = ACPI_MADT_TYPE_IO_APIC; io_apic->header.length = sizeof(*io_apic); io_apic->id = domain_vioapic(d, i)->id; io_apic->address = domain_vioapic(d, i)->base_address; io_apic->global_irq_base = domain_vioapic(d, i)->base_gsi; io_apic++; } x2apic = (void *)io_apic; for ( i = 0; i < max_vcpus; i++ ) { x2apic->header.type = ACPI_MADT_TYPE_LOCAL_X2APIC; x2apic->header.length = sizeof(*x2apic); x2apic->uid = i; x2apic->local_apic_id = i * 2; x2apic->lapic_flags = ACPI_MADT_ENABLED; x2apic++; } /* Setup interrupt overrides. */ intsrcovr = (void *)x2apic; acpi_table_parse_madt(ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, acpi_set_intr_ovr, acpi_intr_overrides); /* Setup NMI sources. */ nmisrc = (void *)intsrcovr; acpi_table_parse_madt(ACPI_MADT_TYPE_NMI_SOURCE, acpi_set_nmi_src, acpi_nmi_sources); ASSERT(((void *)nmisrc - (void *)madt) == size); madt->header.length = size; /* * Calling acpi_tb_checksum here is a layering violation, but * introducing a wrapper for such simple usage seems overkill. */ madt->header.checksum -= acpi_tb_checksum(ACPI_CAST_PTR(u8, madt), size); /* Place the new MADT in guest memory space. */ if ( pvh_steal_ram(d, size, 0, GB(4), addr) ) { printk("Unable to find allocate guest RAM for MADT\n"); rc = -ENOMEM; goto out; } /* Mark this region as E820_ACPI. */ if ( pvh_add_mem_range(d, *addr, *addr + size, E820_ACPI) ) printk("Unable to add MADT region to memory map\n"); rc = hvm_copy_to_guest_phys(*addr, madt, size, d->vcpu[0]); if ( rc ) { printk("Unable to copy MADT into guest memory\n"); goto out; } rc = 0; out: xfree(madt); return rc; }
acpi_status acpi_get_firmware_table ( acpi_string signature, u32 instance, u32 flags, struct acpi_table_header **table_pointer) { struct acpi_pointer rsdp_address; struct acpi_pointer address; acpi_status status; struct acpi_table_header header; struct acpi_table_desc table_info; struct acpi_table_desc rsdt_info; u32 table_count; u32 i; u32 j; ACPI_FUNCTION_TRACE ("acpi_get_firmware_table"); /* * Ensure that at least the table manager is initialized. We don't * require that the entire ACPI subsystem is up for this interface */ /* * If we have a buffer, we must have a length too */ if ((instance == 0) || (!signature) || (!table_pointer)) { return_ACPI_STATUS (AE_BAD_PARAMETER); } rsdt_info.pointer = NULL; if (!acpi_gbl_RSDP) { /* Get the RSDP */ status = acpi_os_get_root_pointer (flags, &rsdp_address); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP not found\n")); return_ACPI_STATUS (AE_NO_ACPI_TABLES); } /* Map and validate the RSDP */ if ((flags & ACPI_MEMORY_MODE) == ACPI_LOGICAL_ADDRESSING) { status = acpi_os_map_memory (rsdp_address.pointer.physical, sizeof (struct rsdp_descriptor), (void *) &acpi_gbl_RSDP); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } } else { acpi_gbl_RSDP = rsdp_address.pointer.logical; } /* * The signature and checksum must both be correct */ if (ACPI_STRNCMP ((char *) acpi_gbl_RSDP, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { /* Nope, BAD Signature */ return_ACPI_STATUS (AE_BAD_SIGNATURE); } if (acpi_tb_checksum (acpi_gbl_RSDP, ACPI_RSDP_CHECKSUM_LENGTH) != 0) { /* Nope, BAD Checksum */ return_ACPI_STATUS (AE_BAD_CHECKSUM); } } /* Get the RSDT and validate it */ acpi_tb_get_rsdt_address (&address); ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP located at %p, RSDT physical=%8.8X%8.8X \n", acpi_gbl_RSDP, ACPI_HIDWORD (address.pointer.value), ACPI_LODWORD (address.pointer.value))); /* Insert processor_mode flags */ address.pointer_type |= flags; status = acpi_tb_get_table (&address, &rsdt_info); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } status = acpi_tb_validate_rsdt (rsdt_info.pointer); if (ACPI_FAILURE (status)) { goto cleanup; } /* Get the number of table pointers within the RSDT */ table_count = acpi_tb_get_table_count (acpi_gbl_RSDP, rsdt_info.pointer); address.pointer_type = acpi_gbl_table_flags | flags; /* * Search the RSDT/XSDT for the correct instance of the * requested table */ for (i = 0, j = 0; i < table_count; i++) { /* Get the next table pointer, handle RSDT vs. XSDT */ if (acpi_gbl_RSDP->revision < 2) { address.pointer.value = ((RSDT_DESCRIPTOR *) rsdt_info.pointer)->table_offset_entry[i]; } else { address.pointer.value = ((XSDT_DESCRIPTOR *) rsdt_info.pointer)->table_offset_entry[i]; } /* Get the table header */ status = acpi_tb_get_table_header (&address, &header); if (ACPI_FAILURE (status)) { goto cleanup; } /* Compare table signatures and table instance */ if (!ACPI_STRNCMP (header.signature, signature, ACPI_NAME_SIZE)) { /* An instance of the table was found */ j++; if (j >= instance) { /* Found the correct instance, get the entire table */ status = acpi_tb_get_table_body (&address, &header, &table_info); if (ACPI_FAILURE (status)) { goto cleanup; } *table_pointer = table_info.pointer; goto cleanup; } } } /* Did not find the table */ status = AE_NOT_EXIST; cleanup: acpi_os_unmap_memory (rsdt_info.pointer, (acpi_size) rsdt_info.pointer->length); return_ACPI_STATUS (status); }
acpi_status acpi_tb_verify_rsdp ( struct acpi_pointer *address) { struct acpi_table_desc table_info; acpi_status status; struct rsdp_descriptor *rsdp; ACPI_FUNCTION_TRACE ("tb_verify_rsdp"); switch (address->pointer_type) { case ACPI_LOGICAL_POINTER: rsdp = address->pointer.logical; break; case ACPI_PHYSICAL_POINTER: /* * Obtain access to the RSDP structure */ status = acpi_os_map_memory (address->pointer.physical, sizeof (struct rsdp_descriptor), (void *) &rsdp); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } break; default: return_ACPI_STATUS (AE_BAD_PARAMETER); } /* * The signature and checksum must both be correct */ if (ACPI_STRNCMP ((char *) rsdp, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { /* Nope, BAD Signature */ status = AE_BAD_SIGNATURE; goto cleanup; } /* Check the standard checksum */ if (acpi_tb_checksum (rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) { status = AE_BAD_CHECKSUM; goto cleanup; } /* Check extended checksum if table version >= 2 */ if (rsdp->revision >= 2) { if (acpi_tb_checksum (rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0) { status = AE_BAD_CHECKSUM; goto cleanup; } } /* The RSDP supplied is OK */ table_info.pointer = ACPI_CAST_PTR (struct acpi_table_header, rsdp); table_info.length = sizeof (struct rsdp_descriptor); table_info.allocation = ACPI_MEM_MAPPED; /* Save the table pointers and allocation info */ status = acpi_tb_init_table_descriptor (ACPI_TABLE_RSDP, &table_info); if (ACPI_FAILURE (status)) { goto cleanup; } /* Save the RSDP in a global for easy access */ acpi_gbl_RSDP = ACPI_CAST_PTR (struct rsdp_descriptor, table_info.pointer); return_ACPI_STATUS (status); /* Error exit */ cleanup: if (acpi_gbl_table_flags & ACPI_PHYSICAL_POINTER) { acpi_os_unmap_memory (rsdp, sizeof (struct rsdp_descriptor)); } return_ACPI_STATUS (status); }