/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include void acpi_create_dmar(acpi_dmar_t *dmar, enum dmar_flags flags, unsigned long (*acpi_fill_dmar)(unsigned long)) { acpi_header_t *header = &(dmar->header); unsigned long current = (unsigned long)dmar + sizeof(acpi_dmar_t); memset((void *)dmar, 0, sizeof(acpi_dmar_t)); if (!header) return; /* Fill out header fields. */ memcpy(header->signature, "DMAR", 4); memcpy(header->oem_id, OEM_ID, 6); memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); memcpy(header->asl_compiler_id, ASLC, 4); header->asl_compiler_revision = asl_revision; header->length = sizeof(acpi_dmar_t); header->revision = get_acpi_table_revision(DMAR); dmar->host_address_width = soc_phys_address_size() - 1; dmar->flags = flags; current = acpi_fill_dmar(current); /* (Re)calculate length and checksum. */ header->length = current - (unsigned long)dmar; header->checksum = acpi_checksum((void *)dmar, header->length); } unsigned long acpi_create_dmar_drhd_4k(unsigned long current, u8 flags, u16 segment, u64 bar) { return acpi_create_dmar_drhd(current, flags, segment, bar, 4 * KiB); } unsigned long acpi_create_dmar_drhd(unsigned long current, u8 flags, u16 segment, u64 bar, size_t size) { /* * Refer to IntelĀ® Virtualization Technology for Directed I/O * Architecture Specification Revision 4.1, * size is at least 1 page and max 2^15 pages, 4 KiB each, and the bar * should be aligned with size. */ assert(4 * KiB <= size && size <= (1 << 15) * 4 * KiB && IS_POWER_OF_2(size)); assert(IS_ALIGNED(bar, size)); dmar_entry_t *drhd = (dmar_entry_t *)current; memset(drhd, 0, sizeof(*drhd)); drhd->type = DMAR_DRHD; drhd->length = sizeof(*drhd); /* will be fixed up later */ drhd->flags = flags; drhd->segment = segment; drhd->bar = bar; drhd->size = log2_64(size) - 12; return drhd->length; } unsigned long acpi_create_dmar_rmrr(unsigned long current, u16 segment, u64 bar, u64 limit) { dmar_rmrr_entry_t *rmrr = (dmar_rmrr_entry_t *)current; memset(rmrr, 0, sizeof(*rmrr)); rmrr->type = DMAR_RMRR; rmrr->length = sizeof(*rmrr); /* will be fixed up later */ rmrr->segment = segment; rmrr->bar = bar; rmrr->limit = limit; return rmrr->length; } unsigned long acpi_create_dmar_atsr(unsigned long current, u8 flags, u16 segment) { dmar_atsr_entry_t *atsr = (dmar_atsr_entry_t *)current; memset(atsr, 0, sizeof(*atsr)); atsr->type = DMAR_ATSR; atsr->length = sizeof(*atsr); /* will be fixed up later */ atsr->flags = flags; atsr->segment = segment; return atsr->length; } unsigned long acpi_create_dmar_rhsa(unsigned long current, u64 base_addr, u32 proximity_domain) { dmar_rhsa_entry_t *rhsa = (dmar_rhsa_entry_t *)current; memset(rhsa, 0, sizeof(*rhsa)); rhsa->type = DMAR_RHSA; rhsa->length = sizeof(*rhsa); rhsa->base_address = base_addr; rhsa->proximity_domain = proximity_domain; return rhsa->length; } unsigned long acpi_create_dmar_andd(unsigned long current, u8 device_number, const char *device_name) { dmar_andd_entry_t *andd = (dmar_andd_entry_t *)current; int andd_len = sizeof(dmar_andd_entry_t) + strlen(device_name) + 1; memset(andd, 0, andd_len); andd->type = DMAR_ANDD; andd->length = andd_len; andd->device_number = device_number; memcpy(&andd->device_name, device_name, strlen(device_name)); return andd->length; } unsigned long acpi_create_dmar_satc(unsigned long current, u8 flags, u16 segment) { dmar_satc_entry_t *satc = (dmar_satc_entry_t *)current; int satc_len = sizeof(dmar_satc_entry_t); memset(satc, 0, satc_len); satc->type = DMAR_SATC; satc->length = satc_len; satc->flags = flags; satc->segment_number = segment; return satc->length; } void acpi_dmar_drhd_fixup(unsigned long base, unsigned long current) { dmar_entry_t *drhd = (dmar_entry_t *)base; drhd->length = current - base; } void acpi_dmar_rmrr_fixup(unsigned long base, unsigned long current) { dmar_rmrr_entry_t *rmrr = (dmar_rmrr_entry_t *)base; rmrr->length = current - base; } void acpi_dmar_atsr_fixup(unsigned long base, unsigned long current) { dmar_atsr_entry_t *atsr = (dmar_atsr_entry_t *)base; atsr->length = current - base; } void acpi_dmar_satc_fixup(unsigned long base, unsigned long current) { dmar_satc_entry_t *satc = (dmar_satc_entry_t *)base; satc->length = current - base; } static unsigned long acpi_create_dmar_ds(unsigned long current, enum dev_scope_type type, u8 enumeration_id, u8 bus, u8 dev, u8 fn) { /* we don't support longer paths yet */ const size_t dev_scope_length = sizeof(dev_scope_t) + 2; dev_scope_t *ds = (dev_scope_t *)current; memset(ds, 0, dev_scope_length); ds->type = type; ds->length = dev_scope_length; ds->enumeration = enumeration_id; ds->start_bus = bus; ds->path[0].dev = dev; ds->path[0].fn = fn; return ds->length; } unsigned long acpi_create_dmar_ds_pci_br(unsigned long current, u8 bus, u8 dev, u8 fn) { return acpi_create_dmar_ds(current, SCOPE_PCI_SUB, 0, bus, dev, fn); } unsigned long acpi_create_dmar_ds_pci(unsigned long current, u8 bus, u8 dev, u8 fn) { return acpi_create_dmar_ds(current, SCOPE_PCI_ENDPOINT, 0, bus, dev, fn); } unsigned long acpi_create_dmar_ds_ioapic(unsigned long current, u8 enumeration_id, u8 bus, u8 dev, u8 fn) { return acpi_create_dmar_ds(current, SCOPE_IOAPIC, enumeration_id, bus, dev, fn); } unsigned long acpi_create_dmar_ds_ioapic_from_hw(unsigned long current, u32 addr, u8 bus, u8 dev, u8 fn) { u8 enumeration_id = get_ioapic_id((uintptr_t)addr); return acpi_create_dmar_ds(current, SCOPE_IOAPIC, enumeration_id, bus, dev, fn); } unsigned long acpi_create_dmar_ds_msi_hpet(unsigned long current, u8 enumeration_id, u8 bus, u8 dev, u8 fn) { return acpi_create_dmar_ds(current, SCOPE_MSI_HPET, enumeration_id, bus, dev, fn); }