DSDT consists of the usual common table header plus a definition block in AML encoding which describes all devices in the platform.
After initializing DSDT with header information the namespace is created which is followed by the device encodings. The devices are described using the Resource Template for the 32-Bit Fixed Memory Range and the Extended Interrupt Descriptors.
Additional helper functions are implemented to add devices with the Resource Template and calculating their multiple size fields which are not fixed.
The following devices are included in the DSDT: - CPUs - UART - RTC - NAND Flash - virtio-mmio
Signed-off-by: Alexander Spyridakis a.spyridakis@virtualopensystems.com --- hw/arm/virt-acpi.c | 284 +++++++++++++++++++++++++++++++++++++++++++- include/hw/acpi/acpi-defs.h | 34 ++++++ include/hw/arm/virt-acpi.h | 9 ++ 3 files changed, 325 insertions(+), 2 deletions(-)
diff --git a/hw/arm/virt-acpi.c b/hw/arm/virt-acpi.c index a53a620..be490e7 100644 --- a/hw/arm/virt-acpi.c +++ b/hw/arm/virt-acpi.c @@ -22,6 +22,258 @@ static void *acpi_table[NUM_ACPI_TABLES]; static int acpi_size[NUM_ACPI_TABLES];
/* + * Package length encoding: + * The package length can range from 1 to 4 bytes. For a package size less than + * 64, only 1 byte is needed, bits 0 - 5 represent the size and the rest should + * be zero. In case of larger package sizes, for the first byte (Package Lead + * Byte), bits 0 - 3 hold the least significant nibble of the size value, bits + * 4 - 5 are reserved and must be zero, bits 6 - 7 signify how many size bytes + * are following. + */ +static void *acpi_calc_size(uint8_t *buf, uint8_t *end, bool resource) +{ + uint8_t *ret, *ptr = buf; + unsigned int size = end - buf; + unsigned int i, shift, byte_num = 1; + + if (size < (1 << 6)) { + byte_num = 1; + } else if (size < (1 << 12)) { + byte_num = 2; + } else if (size < (1 << 20)) { + byte_num = 3; + } else if (size < (1 << 28)) { + byte_num = 4; + } + + if (resource) { + memmove(ptr + (byte_num * 2) + 1, buf, size); + size += (byte_num * 2) + 1; + } else { + memmove(ptr + byte_num, buf, size); + size += byte_num; + } + + *ptr++ = byte_num == 1 ? size : (size & 0x0F) | ((byte_num - 1) << 6); + + shift = 4; + for (i = 1; i < byte_num; i++) { + *ptr++ = size >> shift; + shift += 8; + } + + /* In the case of a resource template we have to add another size + * value just after the package size. This field needs to be + * prefixed by a byte signifying how many bytes the size is. */ + if (resource) { + switch (byte_num) { + case 1: + *ptr++ = ACPI_AML_BYTE_PREFIX; + break; + case 2: + *ptr++ = ACPI_AML_WORD_PREFIX; + break; + case 3: + *ptr++ = ACPI_AML_DWORD_PREFIX; + break; + case 4: + *ptr++ = ACPI_AML_QWORD_PREFIX; + break; + } + + shift = 0; + for (i = 0; i < byte_num; i++) { + *ptr++ = (size - byte_num - 2) >> shift; + shift += 8; + } + } + + ret = buf + size; + return ret; +} + +static void *acpi_dsdt_init_device(uint8_t *buf, const char *name, + const char *hid, int uid) +{ + uint8_t *ptr = buf; + + /* DeviceOp */ + *ptr++ = ACPI_AML_DEVICE_OP; + *ptr++ = ACPI_AML_OP_PREFIX; + + /* Device name */ + ptr = mempcpy(ptr, name, 4); + *ptr++ = ACPI_AML_NAME_OP; + + /* Hardware ID */ + ptr = mempcpy(ptr, "_HID", 4); + *ptr++ = ACPI_AML_STRING_PREFIX; + ptr = mempcpy(ptr, hid, 9); + *ptr++ = ACPI_AML_NAME_OP; + + /* Unique ID - Values greater than one need the byte prefix */ + ptr = mempcpy(ptr, "_UID", 4); + if (uid > 1) { + *ptr++ = ACPI_AML_BYTE_PREFIX; + } + *ptr++ = uid; + + return ptr; +} + +static void *acpi_add_res_memory32(uint8_t *buf, hwaddr addr, + hwaddr size, uint8_t rw_flag) +{ + AcpiAmlResourceFixedMemory32 *memory = (AcpiAmlResourceFixedMemory32 *)buf; + + memory->descriptor_type = ACPI_AML_RESOURCE_MEMORY32; + memory->resource_length = sizeof(*memory) - 3; + + memory->flags = rw_flag; + memory->address = addr; + memory->address_length = size; + + return (void *)memory + sizeof(*memory); +} + +static void *acpi_add_res_extended_irq(uint8_t *buf, uint8_t irq_flags, int irq) +{ + AcpiAmlResourceExtendedIrq *ext_irq = (AcpiAmlResourceExtendedIrq *)buf; + + ext_irq->descriptor_type = ACPI_AML_RESOURCE_EXTENDED_IRQ; + ext_irq->resource_length = sizeof(*ext_irq) - 3; + + ext_irq->flags = irq_flags; + ext_irq->interrupt_count = 0x01; + ext_irq->interrupts[0] = irq; + + return (void *)ext_irq + sizeof(*ext_irq); +} + +/* + * Create a device definition based on the ACPI ResourceTemplate: + * First the name, hardware and unique ID segments are created, + * then the resource template regions are added to define the + * address, size and interrupts of the device. + * Finally, the End Tag is used to signify the end of the device, + * and the total length is calculated. + */ +static void *acpi_dsdt_add_simple_resource(uint8_t *buf, const char *name, + const char *hid, int uid, + hwaddr base, hwaddr size, + int irq, uint8_t irq_flags) +{ + uint8_t *res_len, *ptr = buf; + + ptr = acpi_dsdt_init_device(ptr, name, hid, uid); + + /* Skip the creation of a resource template if it is not provided */ + if (!size && !irq) { + goto ret; + } + + /* Resource template */ + *ptr++ = ACPI_AML_NAME_OP; + ptr = mempcpy(ptr, "_CRS", 4); + *ptr++ = ACPI_AML_BUFFER_PREFIX; + + /* Point to resource size for later */ + res_len = ptr; + + /* Add resource template regions */ + if (size) { + ptr = acpi_add_res_memory32(ptr, base, size, 0x01); + } + if (irq) { + ptr = acpi_add_res_extended_irq(ptr, irq_flags, irq); + } + + /* End the resource template */ + *ptr++ = ACPI_AML_END_TAG; + *ptr++ = 0x00; + + /* Update resource size */ + ptr = acpi_calc_size(res_len, ptr, 1); + +ret: + /* Calculate total device size */ + ptr = acpi_calc_size(buf + 2, ptr, 0); + + return ptr; +} + +static void *acpi_dsdt_add_cpus(uint8_t *buf, int cpus) +{ + int i; + char name[5]; + uint8_t *ptr = buf; + + /* In the case of CPUs there is no need for a resource template */ + for (i = 0; i < cpus; i++) { + snprintf(name, 5, "CPU%u", i); + ptr = acpi_dsdt_add_simple_resource(ptr, name, "ACPI0007", + i, 0, 0, 0, 0); + } + + return ptr; +} + +static void *acpi_dsdt_add_uart(uint8_t *buf, const hwaddr *uart_addr, + const int *uart_irq) +{ + uint8_t *ptr = buf; + + ptr = acpi_dsdt_add_simple_resource(ptr, "COM0", "ARMH0011", 0, + uart_addr[0], uart_addr[1], *uart_irq + 32, 0x01); + return ptr; +} + +static void *acpi_dsdt_add_rtc(uint8_t *buf, const hwaddr *rtc_addrs, + const int *rtc_irq) +{ + uint8_t *ptr = buf; + int irq = *rtc_irq + 32; + + ptr = acpi_dsdt_add_simple_resource(ptr, "RTC0", "LNRO0013", 0, + rtc_addrs[0], rtc_addrs[1], irq, 0x01); + + return ptr; +} + +static void *acpi_dsdt_add_flash(uint8_t *buf, const hwaddr *flash_addrs) +{ + uint8_t *ptr = buf; + hwaddr base = flash_addrs[0]; + hwaddr size = flash_addrs[1]; + + ptr = acpi_dsdt_add_simple_resource(ptr, "FLS0", "LNRO0015", + 0, base, size, 0, 0); + ptr = acpi_dsdt_add_simple_resource(ptr, "FLS1", "LNRO0015", + 1, base + size, size, 0, 0); + return ptr; +} + +static void *acpi_dsdt_add_virtio(uint8_t *buf, const hwaddr *mmio_addrs, + const int *mmio_irq, int num) +{ + int i; + char name[5]; + uint8_t *ptr = buf; + hwaddr base = mmio_addrs[0]; + hwaddr size = mmio_addrs[1]; + int irq = *mmio_irq + 32; + + for (i = 0; i < num; i++) { + snprintf(name, 5, "VR%02u", i); + ptr = acpi_dsdt_add_simple_resource(ptr, name, "LNRO0005", + i, base, size, irq + i, 0x01); + base += size; + } + + return ptr; +} + +/* * Many of the ACPI tables use the same header structure * This is a common function to fill such information. */ @@ -235,8 +487,36 @@ static void acpi_create_facs(void)
static void acpi_create_dsdt(int smp_cpus, const struct acpi_dsdt_info *info) { - acpi_table[DSDT] = NULL; - acpi_size[DSDT] = 0; + uint8_t *ptr; + AcpiTableHeader *header; + + /* As we don't know the size of DSDT beforehand, allocate a chunk of + * memory and pass this to all other calls that add device definitions */ + acpi_table[DSDT] = g_malloc0(ACPI_DSDT_MAX_SIZE); + ptr = acpi_table[DSDT]; + header = (AcpiTableHeader *)acpi_table[DSDT]; + + acpi_fill_common_header_data(ptr, "DSDT", 1, sizeof(AcpiTableHeader)); + ptr = acpi_table[DSDT] + sizeof(AcpiTableHeader); + + /* Scope and namespace, length will be filled at the end */ + *ptr++ = ACPI_AML_SCOPE_OP; + ptr = mempcpy(ptr, "_SB_", 4); + + /* Add device definitions */ + ptr = acpi_dsdt_add_cpus(ptr, smp_cpus); + ptr = acpi_dsdt_add_uart(ptr, info->uart_addr, info->uart_irq); + ptr = acpi_dsdt_add_rtc(ptr, info->rtc_addr, info->rtc_irq); + ptr = acpi_dsdt_add_flash(ptr, info->flash_addr); + ptr = acpi_dsdt_add_virtio(ptr, info->virtio_mmio_addr, + info->virtio_mmio_irq, info->virtio_mmio_num); + + /* Table and namespace size calculation */ + ptr = acpi_calc_size(acpi_table[DSDT] + sizeof(*header) + 1, ptr, 0); + acpi_size[DSDT] = (void *)ptr - acpi_table[DSDT]; + header->length = acpi_size[DSDT]; + + acpi_do_checksum(acpi_table[DSDT], acpi_size[DSDT], &header->checksum); }
void acpi_build_tables(int smp_cpus, diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index f5e5222..dc4ae46 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -227,6 +227,40 @@ typedef struct AcpiFacsDescriptorRev5_1 AcpiFacsDescriptorRev5_1; * Differentiated System Description Table (DSDT) */
+#define ACPI_AML_SCOPE_OP 0x10 +#define ACPI_AML_DEVICE_OP 0x5B +#define ACPI_AML_NAME_OP 0x08 +#define ACPI_AML_OP_PREFIX 0x82 +#define ACPI_AML_BYTE_PREFIX 0x0A +#define ACPI_AML_WORD_PREFIX 0x0B +#define ACPI_AML_DWORD_PREFIX 0x0C +#define ACPI_AML_QWORD_PREFIX 0x0E +#define ACPI_AML_STRING_PREFIX 0x0D +#define ACPI_AML_BUFFER_PREFIX 0x11 +#define ACPI_AML_END_TAG 0x79 +#define ACPI_AML_RESOURCE_MEMORY32 0x86 +#define ACPI_AML_RESOURCE_EXTENDED_IRQ 0x89 + +#define AML_RESOURCE_LARGE_HEADER_COMMON \ + uint8_t descriptor_type;\ + uint16_t resource_length; + +struct AcpiAmlResourceFixedMemory32 { + AML_RESOURCE_LARGE_HEADER_COMMON + uint8_t flags; + uint32_t address; + uint32_t address_length; +} QEMU_PACKED; +typedef struct AcpiAmlResourceFixedMemory32 AcpiAmlResourceFixedMemory32; + +struct AcpiAmlResourceExtendedIrq { + AML_RESOURCE_LARGE_HEADER_COMMON + uint8_t flags; + uint8_t interrupt_count; + uint32_t interrupts[1]; +} QEMU_PACKED; +typedef struct AcpiAmlResourceExtendedIrq AcpiAmlResourceExtendedIrq; + /* * MADT values and structures */ diff --git a/include/hw/arm/virt-acpi.h b/include/hw/arm/virt-acpi.h index 216a3a2..39bb14d 100644 --- a/include/hw/arm/virt-acpi.h +++ b/include/hw/arm/virt-acpi.h @@ -28,6 +28,7 @@ #define ACPI_VIRT_MACH_STR_8 "MACHVIRT"
#define ACPI_BASE_ADDRESS 0x47000000 +#define ACPI_DSDT_MAX_SIZE 0x4000
enum { RSDP, @@ -53,6 +54,14 @@ struct acpi_madt_info { };
struct acpi_dsdt_info { + const hwaddr *uart_addr; + const int *uart_irq; + const hwaddr *virtio_mmio_addr; + const int *virtio_mmio_irq; + int virtio_mmio_num; + const hwaddr *rtc_addr; + const int *rtc_irq; + const hwaddr *flash_addr; };
void acpi_build_tables(int smp_cpus,