diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 647a1fd..dec70a9 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -57,6 +57,133 @@
 #define ACPI_BUILD_DPRINTF(fmt, ...)
 #endif
 
+static void acpi_dsdt_add_cpus(Aml *scope, int max_cpus)
+{
+    Aml *dev, *crs;
+    int i;
+    char name[5];
+    for (i = 0; i < max_cpus; i++) {
+        snprintf(name, 5, "CPU%u", i);
+        dev = aml_device("%s", name);
+        aml_append(dev, aml_name_decl("_HID", aml_string("ACPI007")));
+        aml_append(dev, aml_name_decl("_UID", aml_int(i)));
+        crs = aml_resource_template();
+        aml_append(dev, aml_name_decl("_CRS", crs));
+        aml_append(scope, dev);
+    }
+}
+
+static void acpi_dsdt_add_uart(Aml *scope, const hwaddr *uart_addr,
+                                           const int *uart_irq)
+{
+    Aml *dev, *crs;
+
+    dev = aml_device("COM0");
+    aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+    crs = aml_resource_template();
+    aml_append(crs,
+               aml_memory32_fixed(uart_addr[0], uart_addr[1], 0x01));
+    aml_append(crs,
+               aml_interrupt(0x01, *uart_irq + 32));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_rtc(Aml *scope, const hwaddr *rtc_addr,
+                                          const int *rtc_irq)
+{
+    Aml *dev, *crs;
+
+    dev = aml_device("RTC0");
+    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+    crs = aml_resource_template();
+    aml_append(crs,
+               aml_memory32_fixed(rtc_addr[0], rtc_addr[1], 0x01));
+    aml_append(crs,
+               aml_interrupt(0x01, *rtc_irq + 32));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_flash(Aml *scope, const hwaddr *flash_addr)
+{
+    Aml *dev, *crs;
+    hwaddr base = flash_addr[0];
+    hwaddr size = flash_addr[1];
+
+    dev = aml_device("FLS0");
+    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+    crs = aml_resource_template();
+    aml_append(crs,
+               aml_memory32_fixed(base, size, 0x01));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+
+    dev = aml_device("FLS1");
+    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+    crs = aml_resource_template();
+    aml_append(crs,
+               aml_memory32_fixed(base + size, size, 0x01));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_virtio(Aml *scope, const hwaddr *mmio_addrs,
+                                             const int *mmio_irq, int num)
+{
+    Aml *dev, *crs;
+    hwaddr base = mmio_addrs[0];
+    hwaddr size = mmio_addrs[1];
+    int irq = *mmio_irq + 32;
+    int i;
+    char name[5];
+
+    for (i = 0; i < num; i++) {
+        snprintf(name, 5, "VR%02u", i);
+        dev = aml_device("%s", name);
+        aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
+        aml_append(dev, aml_name_decl("_UID", aml_int(i)));
+
+        crs = aml_resource_template();
+        aml_append(crs,
+                   aml_memory32_fixed(base, size, 0x01));
+        aml_append(crs,
+                   aml_interrupt(0x01, irq + i));
+        aml_append(dev, aml_name_decl("_CRS", crs));
+        aml_append(scope, dev);
+        base += size;
+    }
+}
+
+/* DSDT */
+static void
+build_dsdt(Aml *table_data, GArray *linker, VirtGuestInfo *guest_info)
+{
+    Aml *scope, *dsdt;
+    const struct acpi_dsdt_info *info = guest_info->dsdt_info;
+
+    dsdt = aml_def_block("DSDT", 1, ACPI_BUILD_APPNAME6,
+                          ACPI_BUILD_APPNAME4, 1,
+                          ACPI_BUILD_APPNAME4_HEX, 1);
+    scope = aml_scope("\\_SB");
+    acpi_dsdt_add_cpus(scope, guest_info->max_cpus);
+    acpi_dsdt_add_uart(scope, info->uart_addr, info->uart_irq);
+    acpi_dsdt_add_rtc(scope, info->rtc_addr, info->rtc_irq);
+    acpi_dsdt_add_flash(scope, info->flash_addr);
+    acpi_dsdt_add_virtio(scope, info->virtio_mmio_addr,
+             info->virtio_mmio_irq, info->virtio_mmio_num);
+
+    aml_append(dsdt, scope);
+    aml_append(table_data, dsdt);
+}
+
 typedef
 struct AcpiBuildState {
     /* Copy of table in RAM (for patching). */
@@ -74,6 +201,8 @@ static
 void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
 {
     GArray *table_offsets;
+    unsigned dsdt;
+    GArray *tables_blob = tables->table_data->buf;
 
     table_offsets = g_array_new(false, true /* clear */,
                                         sizeof(uint32_t));
@@ -91,6 +220,10 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
      * DSDT
      */
 
+    /* DSDT is pointed to by FADT */
+    dsdt = tables_blob->len;
+    build_dsdt(tables->table_data, tables->linker, guest_info);
+
     /* Cleanup memory that's no longer used. */
     g_array_free(table_offsets, true);
 }
