Hi,
This series adds virtio-mmio support and an acpi dsdt parser (for device discovery) to seabios. This series with some additional debug patches on top can also be found at https://git.kraxel.org/cgit/seabios/log/?h=microvm
Gerd Hoffmann (9): virtio-mmio: device registry virtio-mmio: device probing and initialization. virtio-mmio: add support to vp_*() functions virtio-mmio: add support for scsi devices. virtio-mmio: add support for block devices. acpi: add xsdt support acpi: add dsdt parser acpi: skip kbd init if not present acpi: find and register virtio-mmio devices
Makefile | 2 +- src/hw/virtio-blk.h | 1 + src/hw/virtio-mmio.h | 78 +++++ src/hw/virtio-pci.h | 1 + src/hw/virtio-scsi.h | 1 + src/std/acpi.h | 11 + src/util.h | 13 + src/block.c | 2 + src/boot.c | 19 ++ src/fw/biostables.c | 662 ++++++++++++++++++++++++++++++++++++++++++- src/hw/ps2port.c | 4 + src/hw/virtio-blk.c | 71 +++++ src/hw/virtio-mmio.c | 101 +++++++ src/hw/virtio-pci.c | 68 ++++- src/hw/virtio-scsi.c | 73 ++++- src/post.c | 4 + src/Kconfig | 7 + 17 files changed, 1085 insertions(+), 33 deletions(-) create mode 100644 src/hw/virtio-mmio.h create mode 100644 src/hw/virtio-mmio.c
Add a new virtio-mmio.c source file, providing a function to register virtio-mmio devices.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- Makefile | 2 +- src/hw/virtio-mmio.h | 6 ++++++ src/hw/virtio-mmio.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/hw/virtio-mmio.h create mode 100644 src/hw/virtio-mmio.c
diff --git a/Makefile b/Makefile index 5f7d5370198a..985ef591a13b 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c optionroms.c \ fw/coreboot.c fw/lzmadecode.c fw/multiboot.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \ - hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \ + hw/virtio-ring.c hw/virtio-pci.c hw/virtio-mmio.c hw/virtio-blk.c hw/virtio-scsi.c \ hw/tpm_drivers.c hw/nvme.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc diff --git a/src/hw/virtio-mmio.h b/src/hw/virtio-mmio.h new file mode 100644 index 000000000000..751984241f49 --- /dev/null +++ b/src/hw/virtio-mmio.h @@ -0,0 +1,6 @@ +#ifndef _VIRTIO_MMIO_H +#define _VIRTIO_MMIO_H + +void virtio_mmio_register(u64 mmio); + +#endif /* _VIRTIO_MMIO_H */ diff --git a/src/hw/virtio-mmio.c b/src/hw/virtio-mmio.c new file mode 100644 index 000000000000..6c969d787ec7 --- /dev/null +++ b/src/hw/virtio-mmio.c @@ -0,0 +1,30 @@ +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "malloc.h" // free +#include "output.h" // dprintf +#include "virtio-pci.h" +#include "virtio-ring.h" + +/* qemu microvm supports 8 virtio-mmio devices */ +static u64 devs[8]; + +void virtio_mmio_register(u64 mmio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(devs); i++) { + if (devs[i] == mmio) { + /* + * This can happen in case we have multiple scsi devices + * attached to a single virtio-scsi controller + */ + dprintf(3, "virtio-mmio: duplicate device at 0x%llx, ignoring\n", mmio); + return; + } + if (devs[i] == 0) { + dprintf(3, "virtio-mmio: register device at 0x%llx\n", mmio); + devs[i] = mmio; + return; + } + } + dprintf(1, "virtio-mmio: device list full\n"); +}
On Fri, Apr 03, 2020 at 10:31:13AM +0200, Gerd Hoffmann wrote:
Add a new virtio-mmio.c source file, providing a function to register virtio-mmio devices.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
Makefile | 2 +- src/hw/virtio-mmio.h | 6 ++++++ src/hw/virtio-mmio.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/hw/virtio-mmio.h create mode 100644 src/hw/virtio-mmio.c
diff --git a/Makefile b/Makefile index 5f7d5370198a..985ef591a13b 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c optionroms.c \ fw/coreboot.c fw/lzmadecode.c fw/multiboot.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \
- hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \
- hw/virtio-ring.c hw/virtio-pci.c hw/virtio-mmio.c hw/virtio-blk.c hw/virtio-scsi.c \ hw/tpm_drivers.c hw/nvme.c
SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc diff --git a/src/hw/virtio-mmio.h b/src/hw/virtio-mmio.h new file mode 100644 index 000000000000..751984241f49 --- /dev/null +++ b/src/hw/virtio-mmio.h @@ -0,0 +1,6 @@ +#ifndef _VIRTIO_MMIO_H +#define _VIRTIO_MMIO_H
+void virtio_mmio_register(u64 mmio);
+#endif /* _VIRTIO_MMIO_H */ diff --git a/src/hw/virtio-mmio.c b/src/hw/virtio-mmio.c new file mode 100644 index 000000000000..6c969d787ec7 --- /dev/null +++ b/src/hw/virtio-mmio.c @@ -0,0 +1,30 @@ +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "malloc.h" // free +#include "output.h" // dprintf +#include "virtio-pci.h" +#include "virtio-ring.h"
+/* qemu microvm supports 8 virtio-mmio devices */ +static u64 devs[8];
It would be preferable to avoid using global variables for temporary state. Because of all the weird linker rules and segment rules, the use of global variables is conceptually harder in SeaBIOS.
If I understand this patch series correctly, the ultimate result is an acpi parser that walks the dsdt and calls virtio_mmio_register(). Could that code directly launch the appropriate hardware registration directly?
-Kevin
+void virtio_mmio_register(u64 mmio) +{
- int i;
- for (i = 0; i < ARRAY_SIZE(devs); i++) {
if (devs[i] == mmio) {
/*
* This can happen in case we have multiple scsi devices
* attached to a single virtio-scsi controller
*/
dprintf(3, "virtio-mmio: duplicate device at 0x%llx, ignoring\n", mmio);
return;
}
if (devs[i] == 0) {
dprintf(3, "virtio-mmio: register device at 0x%llx\n", mmio);
devs[i] = mmio;
return;
}
- }
- dprintf(1, "virtio-mmio: device list full\n");
+}
2.18.2 _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
+/* qemu microvm supports 8 virtio-mmio devices */ +static u64 devs[8];
It would be preferable to avoid using global variables for temporary state. Because of all the weird linker rules and segment rules, the use of global variables is conceptually harder in SeaBIOS.
If I understand this patch series correctly, the ultimate result is an acpi parser that walks the dsdt and calls virtio_mmio_register(). Could that code directly launch the appropriate hardware registration directly?
With the initial series using bootorder I had the problem that virtio-scsi controllers show up multiple times in case there are multiple scsi devices connected to it, so I had to weed out duplicates somehow. This is where virtio_mmio_register() comes from.
But, yes, when using acpi for device discovery this is not needed any more. I'll drop it for the next revision.
take care, Gerd
Add virtio_mmio_setup() to probe virtio mmio devices. Add vp_init_mmio() to initialize device struct. Because virtio-pci and virtio-mmio are quite simliar we reuse the infrastructure we already have for virtio-pci and just setup struct vp_cap for virtio-mmio.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-mmio.h | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/hw/virtio-pci.h | 1 + src/block.c | 2 ++ src/hw/virtio-mmio.c | 49 ++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+)
diff --git a/src/hw/virtio-mmio.h b/src/hw/virtio-mmio.h index 751984241f49..5e24f692c1ae 100644 --- a/src/hw/virtio-mmio.h +++ b/src/hw/virtio-mmio.h @@ -1,6 +1,77 @@ #ifndef _VIRTIO_MMIO_H #define _VIRTIO_MMIO_H
+struct vp_device; + +typedef struct virtio_mmio_cfg { + u32 magic; + u32 version; + u32 device_id; + u32 vendor_id; + + u32 device_feature; + u32 device_feature_select; + u32 res_18; + u32 res_1c; + + u32 guest_feature; + u32 guest_feature_select; + u32 legacy_guest_page_size; + u32 res_2c; + + u32 queue_select; + u32 queue_num_max; + u32 queue_num; + u32 legacy_queue_align; + + u32 legacy_queue_pfn; + u32 queue_ready; + u32 res_48; + u32 res_4c; + + u32 queue_notify; + u32 res_54; + u32 res_58; + u32 res_5c; + + u32 irq_status; + u32 irq_ack; + u32 res_68; + u32 res_6c; + + u32 device_status; + u32 res_74; + u32 res_78; + u32 res_7c; + + u32 queue_desc_lo; + u32 queue_desc_hi; + u32 res_88; + u32 res_8c; + + u32 queue_driver_lo; + u32 queue_driver_hi; + u32 res_98; + u32 res_9c; + + u32 queue_device_lo; + u32 queue_device_hi; + u32 res_a8; + u32 shm_sel; + + u32 shmem_len_lo; + u32 shmem_len_hi; + u32 shmem_base_lo; + u32 shmem_base_hi; + + u32 res_c0_f7[14]; + + u32 res_f8; + u32 config_generation; +} virtio_mmio_cfg; + void virtio_mmio_register(u64 mmio); +void virtio_mmio_setup(void); +void vp_init_mmio(struct vp_device *vp, void *mmio);
#endif /* _VIRTIO_MMIO_H */ diff --git a/src/hw/virtio-pci.h b/src/hw/virtio-pci.h index 492e5c7c9166..269626448558 100644 --- a/src/hw/virtio-pci.h +++ b/src/hw/virtio-pci.h @@ -111,6 +111,7 @@ struct vp_device { struct vp_cap common, notify, isr, device, legacy; u32 notify_off_multiplier; u8 use_modern; + u8 use_mmio; };
u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size); diff --git a/src/block.c b/src/block.c index 1f600b85391b..9ef607610c40 100644 --- a/src/block.c +++ b/src/block.c @@ -20,6 +20,7 @@ #include "hw/usb-uas.h" // uas_process_op #include "hw/virtio-blk.h" // process_virtio_blk_op #include "hw/virtio-scsi.h" // virtio_scsi_process_op +#include "hw/virtio-mmio.h" // virtio_mmio_setup #include "hw/nvme.h" // nvme_process_op #include "malloc.h" // malloc_low #include "output.h" // dprintf @@ -514,6 +515,7 @@ block_setup(void) ramdisk_setup(); virtio_blk_setup(); virtio_scsi_setup(); + virtio_mmio_setup(); lsi_scsi_setup(); esp_scsi_setup(); megasas_setup(); diff --git a/src/hw/virtio-mmio.c b/src/hw/virtio-mmio.c index 6c969d787ec7..9f4f5b780f59 100644 --- a/src/hw/virtio-mmio.c +++ b/src/hw/virtio-mmio.c @@ -1,8 +1,11 @@ #include "config.h" // CONFIG_DEBUG_LEVEL #include "malloc.h" // free #include "output.h" // dprintf +#include "stacks.h" // run_thread +#include "string.h" // memset #include "virtio-pci.h" #include "virtio-ring.h" +#include "virtio-mmio.h"
/* qemu microvm supports 8 virtio-mmio devices */ static u64 devs[8]; @@ -28,3 +31,49 @@ void virtio_mmio_register(u64 mmio) } dprintf(1, "virtio-mmio: device list full\n"); } + +void virtio_mmio_setup(void) +{ + u32 magic, version, devid; + void *mmio; + int i; + + for (i = 0; i < ARRAY_SIZE(devs); i++) { + if (devs[i] == 0) + return; + mmio = (void*)(u32)(devs[i]); + magic = readl(mmio); + if (magic != 0x74726976) + continue; + version = readl(mmio+4); + if (version != 1 /* legacy */ && + version != 2 /* 1.0 */) + continue; + devid = readl(mmio+8); + dprintf(1, "virtio-mmio: %llx: device id %x%s\n", + devs[i], devid, version == 1 ? " (legacy)" : ""); + switch (devid) { + case 2: /* blk */ + /* TODO */ + break; + case 8: /* scsi */ + /* TODO */ + break; + default: + break; + } + } +} + +void vp_init_mmio(struct vp_device *vp, void *mmio) +{ + memset(vp, 0, sizeof(*vp)); + vp->use_mmio = 1; + vp->common.mode = VP_ACCESS_MMIO; + vp->common.memaddr = mmio; + vp->device.mode = VP_ACCESS_MMIO; + vp->device.memaddr = mmio + 0x100; + vp_reset(vp); + vp_set_status(vp, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER); +}
Add support for virtio-mmio to the vp_*() helper functions. Both legacy and 1.0 virto-mmio versions are supported. They are very simliar anyway, only the virtqueue initialization is slightly different.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-pci.c | 68 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 11 deletions(-)
diff --git a/src/hw/virtio-pci.c b/src/hw/virtio-pci.c index d5435218fb0a..213c49777c40 100644 --- a/src/hw/virtio-pci.c +++ b/src/hw/virtio-pci.c @@ -23,6 +23,7 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_0 #include "string.h" // memset #include "virtio-pci.h" +#include "virtio-mmio.h" #include "virtio-ring.h"
u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) @@ -189,7 +190,11 @@ u64 vp_get_features(struct vp_device *vp) { u32 f0, f1;
- if (vp->use_modern) { + if (vp->use_mmio) { + vp_write(&vp->common, virtio_mmio_cfg, device_feature_select, 0); + f0 = vp_read(&vp->common, virtio_mmio_cfg, device_feature); + f1 = 0; + } else if (vp->use_modern) { vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 0); f0 = vp_read(&vp->common, virtio_pci_common_cfg, device_feature); vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 1); @@ -208,7 +213,10 @@ void vp_set_features(struct vp_device *vp, u64 features) f0 = features; f1 = features >> 32;
- if (vp->use_modern) { + if (vp->use_mmio) { + vp_write(&vp->common, virtio_mmio_cfg, guest_feature_select, f0); + vp_write(&vp->common, virtio_mmio_cfg, guest_feature, f0); + } else if (vp->use_modern) { vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 0); vp_write(&vp->common, virtio_pci_common_cfg, guest_feature, f0); vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 1); @@ -220,7 +228,9 @@ void vp_set_features(struct vp_device *vp, u64 features)
u8 vp_get_status(struct vp_device *vp) { - if (vp->use_modern) { + if (vp->use_mmio) { + return vp_read(&vp->common, virtio_mmio_cfg, device_status); + } else if (vp->use_modern) { return vp_read(&vp->common, virtio_pci_common_cfg, device_status); } else { return vp_read(&vp->legacy, virtio_pci_legacy, status); @@ -231,7 +241,9 @@ void vp_set_status(struct vp_device *vp, u8 status) { if (status == 0) /* reset */ return; - if (vp->use_modern) { + if (vp->use_mmio) { + vp_write(&vp->common, virtio_mmio_cfg, device_status, status); + } else if (vp->use_modern) { vp_write(&vp->common, virtio_pci_common_cfg, device_status, status); } else { vp_write(&vp->legacy, virtio_pci_legacy, status, status); @@ -240,7 +252,9 @@ void vp_set_status(struct vp_device *vp, u8 status)
u8 vp_get_isr(struct vp_device *vp) { - if (vp->use_modern) { + if (vp->use_mmio) { + return vp_read(&vp->common, virtio_mmio_cfg, irq_status); + } else if (vp->use_modern) { return vp_read(&vp->isr, virtio_pci_isr, isr); } else { return vp_read(&vp->legacy, virtio_pci_legacy, isr); @@ -249,7 +263,10 @@ u8 vp_get_isr(struct vp_device *vp)
void vp_reset(struct vp_device *vp) { - if (vp->use_modern) { + if (vp->use_mmio) { + vp_write(&vp->common, virtio_mmio_cfg, device_status, 0); + vp_read(&vp->common, virtio_mmio_cfg, irq_status); + } else if (vp->use_modern) { vp_write(&vp->common, virtio_pci_common_cfg, device_status, 0); vp_read(&vp->isr, virtio_pci_isr, isr); } else { @@ -260,7 +277,9 @@ void vp_reset(struct vp_device *vp)
void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq) { - if (vp->use_modern) { + if (vp->use_mmio) { + vp_write(&vp->common, virtio_mmio_cfg, queue_notify, vq->queue_index); + } else if (vp->use_modern) { u32 offset = vq->queue_notify_off * vp->notify_off_multiplier; switch (vp->notify.mode) { case VP_ACCESS_IO: @@ -305,14 +324,21 @@ int vp_find_vq(struct vp_device *vp, int queue_index,
/* select the queue */ - if (vp->use_modern) { + if (vp->use_mmio) { + vp_write(&vp->common, virtio_mmio_cfg, queue_select, queue_index); + } else if (vp->use_modern) { vp_write(&vp->common, virtio_pci_common_cfg, queue_select, queue_index); } else { vp_write(&vp->legacy, virtio_pci_legacy, queue_sel, queue_index); }
/* check if the queue is available */ - if (vp->use_modern) { + if (vp->use_mmio) { + num = vp_read(&vp->common, virtio_mmio_cfg, queue_num_max); + if (num > MAX_QUEUE_NUM) + num = MAX_QUEUE_NUM; + vp_write(&vp->common, virtio_mmio_cfg, queue_num, num); + } else if (vp->use_modern) { num = vp_read(&vp->common, virtio_pci_common_cfg, queue_size); if (num > MAX_QUEUE_NUM) { vp_write(&vp->common, virtio_pci_common_cfg, queue_size, @@ -332,7 +358,9 @@ int vp_find_vq(struct vp_device *vp, int queue_index, }
/* check if the queue is already active */ - if (vp->use_modern) { + if (vp->use_mmio) { + /* TODO */; + } else if (vp->use_modern) { if (vp_read(&vp->common, virtio_pci_common_cfg, queue_enable)) { dprintf(1, "ERROR: queue already active\n"); goto fail; @@ -354,7 +382,25 @@ int vp_find_vq(struct vp_device *vp, int queue_index, * NOTE: vr->desc is initialized by vring_init() */
- if (vp->use_modern) { + if (vp->use_mmio) { + if (vp_read(&vp->common, virtio_mmio_cfg, version) == 2) { + vp_write(&vp->common, virtio_mmio_cfg, queue_desc_lo, + (unsigned long)virt_to_phys(vr->desc)); + vp_write(&vp->common, virtio_mmio_cfg, queue_desc_hi, 0); + vp_write(&vp->common, virtio_mmio_cfg, queue_driver_lo, + (unsigned long)virt_to_phys(vr->avail)); + vp_write(&vp->common, virtio_mmio_cfg, queue_driver_hi, 0); + vp_write(&vp->common, virtio_mmio_cfg, queue_device_lo, + (unsigned long)virt_to_phys(vr->used)); + vp_write(&vp->common, virtio_mmio_cfg, queue_device_hi, 0); + vp_write(&vp->common, virtio_mmio_cfg, queue_ready, 1); + } else { + vp_write(&vp->common, virtio_mmio_cfg, legacy_guest_page_size, + (unsigned long)1 << PAGE_SHIFT); + vp_write(&vp->common, virtio_mmio_cfg, legacy_queue_pfn, + (unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT); + } + } else if (vp->use_modern) { vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_lo, (unsigned long)virt_to_phys(vr->desc)); vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_hi, 0);
Add new fields to struct virtio_lun_s for mmio support, add mmio parameter to virtio_scsi_init_lun(), so both pci and mmio devices can be handled.
Add and use bootprio_find_scsi_mmio_device() to figure boot priority of devices connected to a virtio-mmio scsi controller.
Finally add init_virtio_scsi_mmio() to initialize one virtio-mmio scsi controller.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-scsi.h | 1 + src/util.h | 1 + src/boot.c | 10 ++++++ src/hw/virtio-mmio.c | 3 +- src/hw/virtio-scsi.c | 73 +++++++++++++++++++++++++++++++++++++------- 5 files changed, 76 insertions(+), 12 deletions(-)
diff --git a/src/hw/virtio-scsi.h b/src/hw/virtio-scsi.h index 7532cc98e56e..8f01de4cba99 100644 --- a/src/hw/virtio-scsi.h +++ b/src/hw/virtio-scsi.h @@ -43,5 +43,6 @@ struct virtio_scsi_resp_cmd { struct disk_op_s; int virtio_scsi_process_op(struct disk_op_s *op); void virtio_scsi_setup(void); +void init_virtio_scsi_mmio(void *data);
#endif /* _VIRTIO_SCSI_H */ diff --git a/src/util.h b/src/util.h index 94592a2d2e8d..1c82e09ed87b 100644 --- a/src/util.h +++ b/src/util.h @@ -31,6 +31,7 @@ u8 is_bootprio_strict(void); struct pci_device; int bootprio_find_pci_device(struct pci_device *pci); int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun); +int bootprio_find_scsi_mmio_device(void *mmio, int target, int lun); int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave); int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid); int bootprio_find_pci_rom(struct pci_device *pci, int instance); diff --git a/src/boot.c b/src/boot.c index 4f12988f687c..f2f084bea843 100644 --- a/src/boot.c +++ b/src/boot.c @@ -328,6 +328,16 @@ int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun) return find_prio(desc); }
+int bootprio_find_scsi_mmio_device(void *mmio, int target, int lun) +{ + if (!CONFIG_BOOTORDER) + return -1; + char desc[256]; + snprintf(desc, sizeof(desc), "/virtio-mmio@%016x/*@0/*@%x,%x", + (u32)mmio, target, lun); + return find_prio(desc); +} + int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave) { if (CONFIG_CSM) diff --git a/src/hw/virtio-mmio.c b/src/hw/virtio-mmio.c index 9f4f5b780f59..a72f5fc5e6a1 100644 --- a/src/hw/virtio-mmio.c +++ b/src/hw/virtio-mmio.c @@ -4,6 +4,7 @@ #include "stacks.h" // run_thread #include "string.h" // memset #include "virtio-pci.h" +#include "virtio-scsi.h" #include "virtio-ring.h" #include "virtio-mmio.h"
@@ -57,7 +58,7 @@ void virtio_mmio_setup(void) /* TODO */ break; case 8: /* scsi */ - /* TODO */ + run_thread(init_virtio_scsi_mmio, mmio); break; default: break; diff --git a/src/hw/virtio-scsi.c b/src/hw/virtio-scsi.c index a5332848b8c8..59f1c654d76b 100644 --- a/src/hw/virtio-scsi.c +++ b/src/hw/virtio-scsi.c @@ -22,10 +22,13 @@ #include "virtio-pci.h" #include "virtio-ring.h" #include "virtio-scsi.h" +#include "virtio-mmio.h"
struct virtio_lun_s { struct drive_s drive; struct pci_device *pci; + void *mmio; + char name[16]; struct vring_virtqueue *vq; struct vp_device *vp; u16 target; @@ -94,7 +97,8 @@ virtio_scsi_process_op(struct disk_op_s *op) }
static void -virtio_scsi_init_lun(struct virtio_lun_s *vlun, struct pci_device *pci, +virtio_scsi_init_lun(struct virtio_lun_s *vlun, + struct pci_device *pci, void *mmio, struct vp_device *vp, struct vring_virtqueue *vq, u16 target, u16 lun) { @@ -102,10 +106,15 @@ virtio_scsi_init_lun(struct virtio_lun_s *vlun, struct pci_device *pci, vlun->drive.type = DTYPE_VIRTIO_SCSI; vlun->drive.cntl_id = pci->bdf; vlun->pci = pci; + vlun->mmio = mmio; vlun->vp = vp; vlun->vq = vq; vlun->target = target; vlun->lun = lun; + if (vlun->pci) + snprintf(vlun->name, sizeof(vlun->name), "pci:%pP", vlun->pci); + if (vlun->mmio) + snprintf(vlun->name, sizeof(vlun->name), "mmio:%08x", (u32)vlun->mmio); }
static int @@ -114,12 +123,17 @@ virtio_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv) u8 skip_nonbootable = is_bootprio_strict(); struct virtio_lun_s *tmpl_vlun = container_of(tmpl_drv, struct virtio_lun_s, drive); - int prio = bootprio_find_scsi_device(tmpl_vlun->pci, tmpl_vlun->target, tmpl_vlun->lun); + int prio = -1; + + if (tmpl_vlun->pci) + prio = bootprio_find_scsi_device(tmpl_vlun->pci, tmpl_vlun->target, tmpl_vlun->lun); + if (tmpl_vlun->mmio) + prio = bootprio_find_scsi_mmio_device(tmpl_vlun->mmio, tmpl_vlun->target, tmpl_vlun->lun);
if (skip_nonbootable && prio < 0) { - dprintf(1, "skipping init of a non-bootable virtio-scsi dev at %pP," + dprintf(1, "skipping init of a non-bootable virtio-scsi dev at %s," " target %d, lun %d\n", - tmpl_vlun->pci, tmpl_vlun->target, tmpl_vlun->lun); + tmpl_vlun->name, tmpl_vlun->target, tmpl_vlun->lun); return -1; }
@@ -128,11 +142,12 @@ virtio_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv) warn_noalloc(); return -1; } - virtio_scsi_init_lun(vlun, tmpl_vlun->pci, tmpl_vlun->vp, tmpl_vlun->vq, - tmpl_vlun->target, lun); + virtio_scsi_init_lun(vlun, tmpl_vlun->pci, tmpl_vlun->mmio,tmpl_vlun->vp, + tmpl_vlun->vq, tmpl_vlun->target, lun);
- boot_lchs_find_scsi_device(vlun->pci, vlun->target, vlun->lun, - &(vlun->drive.lchs)); + if (vlun->pci) + boot_lchs_find_scsi_device(vlun->pci, vlun->target, vlun->lun, + &(vlun->drive.lchs)); int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio); if (ret) goto fail; @@ -144,13 +159,13 @@ fail: }
static int -virtio_scsi_scan_target(struct pci_device *pci, struct vp_device *vp, +virtio_scsi_scan_target(struct pci_device *pci, void *mmio, struct vp_device *vp, struct vring_virtqueue *vq, u16 target) {
struct virtio_lun_s vlun0;
- virtio_scsi_init_lun(&vlun0, pci, vp, vq, target, 0); + virtio_scsi_init_lun(&vlun0, pci, mmio, vp, vq, target, 0);
int ret = scsi_rep_luns_scan(&vlun0.drive, virtio_scsi_add_lun); return ret < 0 ? 0 : ret; @@ -198,7 +213,43 @@ init_virtio_scsi(void *data)
int i, tot; for (tot = 0, i = 0; i < 256; i++) - tot += virtio_scsi_scan_target(pci, vp, vq, i); + tot += virtio_scsi_scan_target(pci, NULL, vp, vq, i); + + if (!tot) + goto fail; + + return; + +fail: + vp_reset(vp); + free(vp); + free(vq); +} + +void +init_virtio_scsi_mmio(void *mmio) +{ + dprintf(1, "found virtio-scsi-mmio at %p\n", mmio); + struct vring_virtqueue *vq = NULL; + struct vp_device *vp = malloc_high(sizeof(*vp)); + if (!vp) { + warn_noalloc(); + return; + } + vp_init_mmio(vp, mmio); + u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER; + + if (vp_find_vq(vp, 2, &vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-scsi-mmio %p\n", mmio); + goto fail; + } + + status |= VIRTIO_CONFIG_S_DRIVER_OK; + vp_set_status(vp, status); + + int i, tot; + for (tot = 0, i = 0; i < 256; i++) + tot += virtio_scsi_scan_target(NULL, mmio, vp, vq, i);
if (!tot) goto fail;
Add and use bootprio_find_mmio_device() to figure the boot priority of virtio-mmio block devices.
Add init_virtio_blk_mmio to initialize one virtio-mmio block device.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-blk.h | 1 + src/util.h | 1 + src/boot.c | 9 ++++++ src/hw/virtio-blk.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/hw/virtio-mmio.c | 3 +- 5 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/src/hw/virtio-blk.h b/src/hw/virtio-blk.h index 157bed62744a..d20461a2a3b2 100644 --- a/src/hw/virtio-blk.h +++ b/src/hw/virtio-blk.h @@ -39,5 +39,6 @@ struct virtio_blk_outhdr { struct disk_op_s; int virtio_blk_process_op(struct disk_op_s *op); void virtio_blk_setup(void); +void init_virtio_blk_mmio(void *mmio);
#endif /* _VIRTIO_BLK_H */ diff --git a/src/util.h b/src/util.h index 1c82e09ed87b..4f27fc307439 100644 --- a/src/util.h +++ b/src/util.h @@ -30,6 +30,7 @@ void bcv_prepboot(void); u8 is_bootprio_strict(void); struct pci_device; int bootprio_find_pci_device(struct pci_device *pci); +int bootprio_find_mmio_device(void *mmio); int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun); int bootprio_find_scsi_mmio_device(void *mmio, int target, int lun); int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave); diff --git a/src/boot.c b/src/boot.c index f2f084bea843..cc87b1d98476 100644 --- a/src/boot.c +++ b/src/boot.c @@ -316,6 +316,15 @@ int bootprio_find_pci_device(struct pci_device *pci) return find_prio(desc); }
+int bootprio_find_mmio_device(void *mmio) +{ + if (!CONFIG_BOOTORDER) + return -1; + char desc[256]; + snprintf(desc, sizeof(desc), "/virtio-mmio@%016x/*", (u32)mmio); + return find_prio(desc); +} + int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun) { if (!CONFIG_BOOTORDER) diff --git a/src/hw/virtio-blk.c b/src/hw/virtio-blk.c index a5e28fc858b1..3b198965c8ba 100644 --- a/src/hw/virtio-blk.c +++ b/src/hw/virtio-blk.c @@ -20,6 +20,7 @@ #include "string.h" // memset #include "util.h" // usleep, bootprio_find_pci_device, is_bootprio_strict #include "virtio-pci.h" +#include "virtio-mmio.h" #include "virtio-ring.h" #include "virtio-blk.h"
@@ -193,6 +194,76 @@ fail: free(vdrive); }
+void +init_virtio_blk_mmio(void *mmio) +{ + u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER; + dprintf(1, "found virtio-blk-mmio at %p\n", mmio); + struct virtiodrive_s *vdrive = malloc_low(sizeof(*vdrive)); + if (!vdrive) { + warn_noalloc(); + return; + } + memset(vdrive, 0, sizeof(*vdrive)); + vdrive->drive.type = DTYPE_VIRTIO_BLK; + vdrive->drive.cntl_id = (u32)mmio; + + vp_init_mmio(&vdrive->vp, mmio); + if (vp_find_vq(&vdrive->vp, 0, &vdrive->vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-blk-mmio %p\n", mmio); + goto fail; + } + + struct vp_device *vp = &vdrive->vp; + u64 features = vp_get_features(vp); + u64 version1 = 1ull << VIRTIO_F_VERSION_1; + u64 blk_size = 1ull << VIRTIO_BLK_F_BLK_SIZE; + + features = features & (version1 | blk_size); + vp_set_features(vp, features); + status |= VIRTIO_CONFIG_S_FEATURES_OK; + vp_set_status(vp, status); + if (!(vp_get_status(vp) & VIRTIO_CONFIG_S_FEATURES_OK)) { + dprintf(1, "device didn't accept features: %p\n", mmio); + goto fail; + } + + vdrive->drive.sectors = + vp_read(&vp->device, struct virtio_blk_config, capacity); + if (features & blk_size) { + vdrive->drive.blksize = + vp_read(&vp->device, struct virtio_blk_config, blk_size); + } else { + vdrive->drive.blksize = DISK_SECTOR_SIZE; + } + if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { + dprintf(1, "virtio-blk-mmio %p block size %d is unsupported\n", + mmio, vdrive->drive.blksize); + goto fail; + } + dprintf(1, "virtio-blk-mmio %p blksize=%d sectors=%u\n", + mmio, vdrive->drive.blksize, (u32)vdrive->drive.sectors); + + vdrive->drive.pchs.cylinder = + vp_read(&vp->device, struct virtio_blk_config, cylinders); + vdrive->drive.pchs.head = + vp_read(&vp->device, struct virtio_blk_config, heads); + vdrive->drive.pchs.sector = + vp_read(&vp->device, struct virtio_blk_config, sectors); + + char *desc = znprintf(MAXDESCSIZE, "Virtio disk mmio:%p", mmio); + boot_add_hd(&vdrive->drive, desc, bootprio_find_mmio_device(mmio)); + + status |= VIRTIO_CONFIG_S_DRIVER_OK; + vp_set_status(&vdrive->vp, status); + return; + +fail: + vp_reset(&vdrive->vp); + free(vdrive->vq); + free(vdrive); +} + void virtio_blk_setup(void) { diff --git a/src/hw/virtio-mmio.c b/src/hw/virtio-mmio.c index a72f5fc5e6a1..b205ec6ef6ae 100644 --- a/src/hw/virtio-mmio.c +++ b/src/hw/virtio-mmio.c @@ -4,6 +4,7 @@ #include "stacks.h" // run_thread #include "string.h" // memset #include "virtio-pci.h" +#include "virtio-blk.h" #include "virtio-scsi.h" #include "virtio-ring.h" #include "virtio-mmio.h" @@ -55,7 +56,7 @@ void virtio_mmio_setup(void) devs[i], devid, version == 1 ? " (legacy)" : ""); switch (devid) { case 2: /* blk */ - /* TODO */ + run_thread(init_virtio_blk_mmio, mmio); break; case 8: /* scsi */ run_thread(init_virtio_scsi_mmio, mmio);
In case a xsdt table is present (and located below 4G) prefer it over rsdt.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/std/acpi.h | 11 +++++++++++ src/fw/biostables.c | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/src/std/acpi.h b/src/std/acpi.h index c01fa7be827c..81c22757f50e 100644 --- a/src/std/acpi.h +++ b/src/std/acpi.h @@ -132,6 +132,17 @@ struct rsdt_descriptor_rev1 /* ACPI tables */ } PACKED;
+/* + * ACPI 2.0 eXtended System Description Table (XSDT) + */ +#define XSDT_SIGNATURE 0x54445358 // XSDT +struct xsdt_descriptor_rev2 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u64 table_offset_entry[0]; /* Array of pointers to other */ + /* ACPI tables */ +} PACKED; + /* * ACPI 1.0 Firmware ACPI Control Structure (FACS) */ diff --git a/src/fw/biostables.c b/src/fw/biostables.c index fe8626efc05d..0d4fdb9c22e8 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -141,18 +141,38 @@ find_acpi_table(u32 signature) if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE) return NULL; struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address; + struct xsdt_descriptor_rev2 *xsdt = + RsdpAddr->xsdt_physical_address >= 0x100000000 + ? NULL : (void*)(u32)(RsdpAddr->xsdt_physical_address); dprintf(4, "rsdt=%p\n", rsdt); - if (!rsdt || rsdt->signature != RSDT_SIGNATURE) - return NULL; - void *end = (void*)rsdt + rsdt->length; - int i; - for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) { - struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i]; - if (!tbl || tbl->signature != signature) - continue; - dprintf(4, "table(%x)=%p\n", signature, tbl); - return tbl; + dprintf(4, "xsdt=%p\n", xsdt); + + if (xsdt && xsdt->signature == XSDT_SIGNATURE) { + void *end = (void*)xsdt + xsdt->length; + int i; + for (i=0; (void*)&xsdt->table_offset_entry[i] < end; i++) { + if (xsdt->table_offset_entry[i] >= 0x100000000) + continue; /* above 4G */ + struct acpi_table_header *tbl = (void*)(u32)xsdt->table_offset_entry[i]; + if (!tbl || tbl->signature != signature) + continue; + dprintf(1, "table(%x)=%p (via xsdt)\n", signature, tbl); + return tbl; + } } + + if (rsdt && rsdt->signature == RSDT_SIGNATURE) { + void *end = (void*)rsdt + rsdt->length; + int i; + for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) { + struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i]; + if (!tbl || tbl->signature != signature) + continue; + dprintf(1, "table(%x)=%p (via rsdt)\n", signature, tbl); + return tbl; + } + } + dprintf(4, "no table %x found\n", signature); return NULL; }
Create a list of devices found in the DSDT table. Add helper functions to find devices, walk the list and figure device informations like mmio ranges and irqs.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/util.h | 10 + src/fw/biostables.c | 622 ++++++++++++++++++++++++++++++++++++++++++++ src/post.c | 2 + src/Kconfig | 7 + 4 files changed, 641 insertions(+)
diff --git a/src/util.h b/src/util.h index 4f27fc307439..8ee0370492b8 100644 --- a/src/util.h +++ b/src/util.h @@ -94,6 +94,16 @@ void display_uuid(void); void copy_table(void *pos); void smbios_setup(void);
+struct acpi_device; +void acpi_dsdt_parse(void); +struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev, const char *hid); +struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid); +char *acpi_dsdt_name(struct acpi_device *dev); +int acpi_dsdt_present_eisaid(u16 eisaid); +int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max); +int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max); +int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq); + // fw/coreboot.c extern const char *CBvendor, *CBpart; struct cbfs_file; diff --git a/src/fw/biostables.c b/src/fw/biostables.c index 0d4fdb9c22e8..ebe1c90fca5e 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -17,6 +17,7 @@ #include "std/smbios.h" // struct smbios_entry_point #include "string.h" // memcpy #include "util.h" // copy_table +#include "list.h" // hlist_* #include "x86.h" // outb
struct pir_header *PirAddr VARFSEG; @@ -509,3 +510,624 @@ copy_table(void *pos) copy_acpi_rsdp(pos); copy_smbios(pos); } + +/**************************************************************** + * DSDT parser + ****************************************************************/ + +struct acpi_device { + struct hlist_node node; + char name[16]; + u8 *hid_aml; + u8 *sta_aml; + u8 *crs_data; + int crs_size; +}; +static struct hlist_head acpi_devices; + +static int parse_error = 0; +static int parse_dumptree = 0; +static char parse_name[32]; +static struct acpi_device *parse_dev; + +static void parse_termlist(u8 *ptr, int offset, int pkglength); + +static void hex(const u8 *ptr, int count, int lvl, const char *item) +{ + int l = 0, i; + + do { + dprintf(lvl, "%s: %04x: ", item, l); + for (i = l; i < l+16; i += 4) + dprintf(lvl, "%02x %02x %02x %02x ", + ptr[i+0], ptr[i+1], ptr[i+2], ptr[i+3]); + for (i = l; i < l+16; i++) + dprintf(lvl, "%c", (ptr[i] > 0x20 && ptr[i] < 0x80) ? ptr[i] : '.'); + dprintf(lvl, "\n"); + l += 16; + } while (l < count); +} + +static u64 parse_resource_int(u8 *ptr, int count) +{ + u64 value = 0; + int index = 0; + + for (index = 0; index < count; index++) + value |= (u64)ptr[index] << (index * 8); + return value; +} + +static int parse_resource_bit(u8 *ptr, int count) +{ + int bit; + + for (bit = 0; bit < count*8; bit++) + if (ptr[bit/8] & (1 << (bit%8))) + return bit; + return 0; +} + +static int parse_resource(u8 *ptr, int length, int *type, u64 *min, u64 *max) +{ + int rname, rsize; + u64 len; + + *type = -1; + *min = 0; + *max = 0; + len = 0; + if (!(ptr[0] & 0x80)) { + /* small resource */ + rname = (ptr[0] >> 3) & 0x0f; + rsize = ptr[0] & 0x07; + rsize++; + switch (rname) { + case 0x04: /* irq */ + *min = parse_resource_bit(ptr + 1, rsize); + *max = *min; + *type = 3; + break; + case 0x0f: /* end marker */ + return 0; + case 0x08: /* io */ + *min = parse_resource_int(ptr + 2, 2); + *max = parse_resource_int(ptr + 4, 2); + if (*min == *max) { + *max = *min + ptr[7] - 1; + *type = 1; + } + break; + case 0x09: /* fixed io */ + *min = parse_resource_int(ptr + 2, 2); + *max = *min + ptr[4] - 1; + *type = 1; + break; + default: + dprintf(3, "%s: small: 0x%x (len %d)\n", + __func__, rname, rsize); + break; + } + } else { + /* large resource */ + rname = ptr[0] & 0x7f; + rsize = ptr[2] << 8 | ptr[1]; + rsize += 3; + switch (rname) { + case 0x06: /* 32-bit Fixed Location Memory Range Descriptor */ + *min = parse_resource_int(ptr + 4, 4); + len = parse_resource_int(ptr + 8, 4); + *max = *min + len - 1; + *type = 0; + break; + case 0x07: /* DWORD Address Space Descriptor */ + *min = parse_resource_int(ptr + 10, 4); + *max = parse_resource_int(ptr + 14, 4); + *type = ptr[3]; + break; + case 0x08: /* WORD Address Space Descriptor */ + *min = parse_resource_int(ptr + 8, 2); + *max = parse_resource_int(ptr + 10, 2); + *type = ptr[3]; + break; + case 0x09: /* irq */ + *min = parse_resource_int(ptr + 5, 4); + *max = *min; + *type = 3; + break; + case 0x0a: /* QWORD Address Space Descriptor */ + *min = parse_resource_int(ptr + 14, 8); + *max = parse_resource_int(ptr + 22, 8); + *type = ptr[3]; + break; + default: + dprintf(3, "%s: large: 0x%x (len %d)\n", __func__, rname, rsize); + break; + } + } + return rsize; +} + +static int find_resource(u8 *ptr, int len, int kind, u64 *min, u64 *max) +{ + int type, size, offset = 0; + + do { + size = parse_resource(ptr + offset, len - offset, + &type, min, max); + if (kind == type) + return 0; + offset += size; + } while (size > 0 && offset < len); + return -1; +} + +static int print_resources(const char *prefix, u8 *ptr, int len) +{ + static const char *typename[] = { "mem", "i/o", "bus" }; + int type, size, offset = 0; + u64 min, max; + + do { + size = parse_resource(ptr + offset, len - offset, + &type, &min, &max); + switch (type) { + case 0: + case 1: + case 2: + dprintf(1, "%s%s 0x%llx -> 0x%llx\n", + prefix, typename[type], min, max); + break; + case 3: + dprintf(1, "%sirq %lld\n", prefix, min); + break; + } + offset += size; + } while (size > 0 && offset < len); + return -1; +} + +static int parse_nameseg(u8 *ptr, char **dst) +{ + if (dst && *dst) { + *(dst[0]++) = ptr[0]; + if (ptr[1] != '_') + *(dst[0]++) = ptr[1]; + if (ptr[2] != '_') + *(dst[0]++) = ptr[2]; + if (ptr[3] != '_') + *(dst[0]++) = ptr[3]; + *(dst[0]) = 0; + } + return 4; +} + +static int parse_namestring(u8 *ptr, const char *item) +{ + char *dst = parse_name; + int offset = 0; + int i, count; + +again: + switch (ptr[offset]) { + case 0: /* null name */ + offset++; + *(dst++) = 0; + break; + case 0x2e: + offset++; + offset += parse_nameseg(ptr + offset, &dst); + *(dst++) = '.'; + offset += parse_nameseg(ptr + offset, &dst); + break; + case 0x2f: + offset++; + count = ptr[offset]; + offset++; + for (i = 0; i < count; i++) { + if (i) + *(dst++) = '.'; + offset += parse_nameseg(ptr + offset, &dst); + } + break; + case '\': + *(dst++) = '\'; + offset++; + goto again; + case '^': + *(dst++) = '^'; + offset++; + goto again; + case 'A' ... 'Z': + case '_': + offset += parse_nameseg(ptr, &dst); + break; + default: + hex(ptr, 16, 3, __func__); + parse_error = 1; + break; + } + dprintf(5, "%s: %s '%s'\n", __func__, item, parse_name); + return offset; +} + +static int parse_termarg_int(u8 *ptr, u64 *dst) +{ + u64 value; + int offset = 1; + + switch (ptr[0]) { + case 0x00: /* zero */ + value = 0; + break; + case 0x01: /* one */ + value = 1; + break; + case 0x0a: /* byte prefix */ + value = ptr[1]; + offset++; + break; + case 0x0b: /* word prefix */ + value = ptr[1] | + ((unsigned long)ptr[2] << 8); + offset += 2; + break; + case 0x0c: /* dword prefix */ + value = ptr[1] | + ((unsigned long)ptr[2] << 8) | + ((unsigned long)ptr[3] << 16) | + ((unsigned long)ptr[4] << 24); + offset += 4; + break; + default: + value = 0; + hex(ptr, 16, 3, __func__); + parse_error = 1; + break; + } + + if (dst) + *dst = value; + dprintf(5, "%s: 0x%llx\n", __func__, value); + return offset; +} + +static int parse_pkglength(u8 *ptr, int *pkglength) +{ + int offset = 2; + + *pkglength = 0; + switch (ptr[0] >> 6) { + case 3: + *pkglength |= ptr[3] << 20; + offset++; + case 2: + *pkglength |= ptr[2] << 12; + offset++; + case 1: + *pkglength |= ptr[1] << 4; + *pkglength |= ptr[0] & 0x0f; + return offset; + case 0: + default: + *pkglength |= ptr[0] & 0x3f; + return 1; + } +} + +static int parse_pkg_common(u8 *ptr, const char *item, int *pkglength) +{ + int offset; + + offset = parse_pkglength(ptr, pkglength); + offset += parse_namestring(ptr + offset, item); + return offset; +} + +static int parse_pkg_scope(u8 *ptr) +{ + int offset, pkglength; + + offset = parse_pkg_common(ptr, "skope", &pkglength); + parse_termlist(ptr, offset, pkglength); + return pkglength; +} + +static int parse_pkg_device(u8 *ptr) +{ + int offset, pkglength; + + offset = parse_pkg_common(ptr, "device", &pkglength); + + parse_dev = malloc_high(sizeof(*parse_dev)); + if (!parse_dev) { + warn_noalloc(); + parse_error = 1; + return pkglength; + } + + memset(parse_dev, 0, sizeof(*parse_dev)); + hlist_add_head(&parse_dev->node, &acpi_devices); + strtcpy(parse_dev->name, parse_name, sizeof(parse_dev->name)); + + parse_termlist(ptr, offset, pkglength); + return pkglength; +} + +static int parse_pkg_buffer(u8 *ptr) +{ + u64 blen; + int pkglength, offset; + + offset = parse_pkglength(ptr, &pkglength); + offset += parse_termarg_int(ptr + offset, &blen); + if (strcmp(parse_name, "_CRS") == 0) { + parse_dev->crs_data = ptr + offset; + parse_dev->crs_size = blen; + } + return pkglength; +} + +static int parse_pkg_skip(u8 *ptr, int op, int name) +{ + int pkglength, offset; + char item[8]; + + snprintf(item, sizeof(item), "op %x", op); + offset = parse_pkglength(ptr, &pkglength); + if (name) { + parse_namestring(ptr + offset, item); + } else { + dprintf(5, "%s: %s (%d)\n", __func__, item, pkglength); + } + return pkglength; +} + +static int parse_termobj(u8 *ptr) +{ + int offset = 1; + + switch (ptr[0]) { + case 0x00: /* zero */ + break; + case 0x01: /* one */ + break; + case 0x08: /* name op */ + offset += parse_namestring(ptr + offset, "name"); + offset += parse_termobj(ptr + offset); + if (strcmp(parse_name, "_HID") == 0) + parse_dev->hid_aml = ptr; + if (strcmp(parse_name, "_STA") == 0) + parse_dev->sta_aml = ptr; + break; + case 0x0a: /* byte prefix */ + offset++; + break; + case 0x0b: /* word prefix */ + offset += 2; + break; + case 0x0c: /* dword prefix */ + offset += 4; + break; + case 0x0d: /* string prefix */ + while (ptr[offset]) + offset++; + offset++; + break; + case 0x10: /* scope op */ + offset += parse_pkg_scope(ptr + offset); + break; + case 0x11: /* buffer op */ + offset += parse_pkg_buffer(ptr + offset); + break; + case 0x12: /* package op */ + case 0x13: /* var package op */ + offset += parse_pkg_skip(ptr + offset, ptr[0], 0); + break; + case 0x14: /* method op */ + offset += parse_pkg_skip(ptr + offset, ptr[0], 1); + if (strcmp(parse_name, "_STA") == 0) + parse_dev->sta_aml = ptr; + break; + case 0x5b: /* ext op prefix */ + offset++; + switch (ptr[1]) { + case 0x01: /* mutex op */ + offset += parse_namestring(ptr + offset, "mutex"); + offset++; /* sync flags */ + break; + case 0x80: /* op region op */ + offset += parse_namestring(ptr + offset, "op region"); + offset++; /* region space */ + offset += parse_termarg_int(ptr + offset, NULL); + offset += parse_termarg_int(ptr + offset, NULL); + break; + case 0x81: /* field op */ + case 0x83: /* processor op */ + case 0x84: /* power resource op */ + case 0x85: /* thermal zone op */ + offset += parse_pkg_skip(ptr + offset, 0x5b00 | ptr[1], 1); + break; + case 0x82: /* device op */ + offset += parse_pkg_device(ptr + offset); + break; + default: + hex(ptr, 16, 3, __func__); + parse_error = 1; + break; + } + break; + default: + hex(ptr, 16, 3, __func__); + parse_error = 1; + break; + } + + return offset; +} + +static void parse_termlist(u8 *ptr, int offset, int pkglength) +{ + for (;;) { + offset += parse_termobj(ptr + offset); + if (offset == pkglength) + return; + if (offset > pkglength) { + dprintf(1, "%s: overrun: %d/%d\n", __func__, + offset, pkglength); + parse_error = 1; + return; + } + if (parse_error) { + dprintf(1, "%s: parse error, skip from %d/%d\n", __func__, + offset, pkglength); + parse_error = 0; + return; + } + } +} + +static struct acpi_device *acpi_dsdt_find(struct acpi_device *prev, const u8 *aml, int size) +{ + struct acpi_device *dev; + struct hlist_node *node; + + if (!prev) + node = acpi_devices.first; + else + node = prev->node.next; + + for (; node != NULL; node = dev->node.next) { + dev = container_of(node, struct acpi_device, node); + if (!aml) + return dev; + if (!dev->hid_aml) + continue; + if (memcmp(dev->hid_aml + 5, aml, size) == 0) + return dev; + } + return NULL; +} + +static int acpi_dsdt_present(struct acpi_device *dev) +{ + if (!dev) + return 0; /* no */ + if (!dev->sta_aml) + return 1; /* yes */ + if (dev->sta_aml[0] == 0x14) + return -1; /* unknown (can't evaluate method) */ + if (dev->sta_aml[0] == 0x08) { + u64 value = 0; + parse_termarg_int(dev->sta_aml + 5, &value); + if (value == 0) + return 0; /* no */ + else + return 1; /* yes */ + } + return -1; /* unknown (should not happen) */ +} + +/**************************************************************** + * DSDT parser, public interface + ****************************************************************/ + +struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev, const char *hid) +{ + if (!CONFIG_ACPI_PARSE) + return NULL; + + u8 aml[10]; + int len = snprintf((char*)aml, sizeof(aml), "\x0d%s", hid); + return acpi_dsdt_find(prev, aml, len); +} + +struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid) +{ + if (!CONFIG_ACPI_PARSE) + return NULL; + u8 aml[] = { + 0x0c, 0x41, 0xd0, + eisaid >> 8, + eisaid & 0xff + }; + return acpi_dsdt_find(prev, aml, 5); +} + +char *acpi_dsdt_name(struct acpi_device *dev) +{ + if (!CONFIG_ACPI_PARSE || !dev) + return NULL; + return dev->name; +} + +int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max) +{ + if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data) + return -1; + return find_resource(dev->crs_data, dev->crs_size, + 1 /* I/O */, min, max); +} + +int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max) +{ + if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data) + return -1; + return find_resource(dev->crs_data, dev->crs_size, + 0 /* mem */, min, max); +} + +int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq) +{ + u64 max; + if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data) + return -1; + return find_resource(dev->crs_data, dev->crs_size, + 3 /* irq */, irq, &max); +} + +int acpi_dsdt_present_eisaid(u16 eisaid) +{ + if (!CONFIG_ACPI_PARSE) + return -1; /* unknown */ + + struct acpi_device *dev = acpi_dsdt_find_eisaid(NULL, eisaid); + return acpi_dsdt_present(dev); +} + +void acpi_dsdt_parse(void) +{ + if (!CONFIG_ACPI_PARSE) + return; + + struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE); + if (!fadt) + return; + u8 *dsdt = (void*)(fadt->dsdt); + if (!dsdt) + return; + + u32 length = *(u32*)(dsdt + 4); + u32 offset = 0x24; + dprintf(1, "ACPI: parse DSDT at %p (len %d)\n", dsdt, length); + parse_termlist(dsdt, offset, length); + + if (parse_dumptree) { + struct acpi_device *dev; + dprintf(1, "ACPI: dumping dsdt devices\n"); + for (dev = acpi_dsdt_find(NULL, NULL, 0); + dev != NULL; + dev = acpi_dsdt_find(dev, NULL, 0)) { + dprintf(1, " %s", acpi_dsdt_name(dev)); + if (dev->hid_aml) + dprintf(1, ", hid"); + if (dev->sta_aml) + dprintf(1, ", sta (0x%x)", dev->sta_aml[0]); + if (dev->crs_data) + dprintf(1, ", crs"); + dprintf(1, "\n"); + if (dev->crs_data) + print_resources(" ", dev->crs_data, dev->crs_size); + } + } +} diff --git a/src/post.c b/src/post.c index f93106a1c9c9..febdc0859764 100644 --- a/src/post.c +++ b/src/post.c @@ -149,6 +149,8 @@ platform_hardware_setup(void) qemu_platform_setup(); coreboot_platform_setup();
+ acpi_dsdt_parse(); + // Setup timers and periodic clock interrupt timer_setup(); clock_setup(); diff --git a/src/Kconfig b/src/Kconfig index 6606ce4d46c9..ab36e676bf12 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -524,6 +524,13 @@ menu "BIOS Tables" This option can be disabled for QEMU 1.6 and older to save some space in the ROM file. If unsure, say Y. + config ACPI_PARSE + bool "Include ACPI DSDT parser." + default n + help + Support parsing ACPI DSDT for device probing. + Needed to find virtio-mmio devices. + If unsure, say N. endmenu
source vgasrc/Kconfig
On Fri, Apr 03, 2020 at 10:31:19AM +0200, Gerd Hoffmann wrote:
Create a list of devices found in the DSDT table. Add helper functions to find devices, walk the list and figure device informations like mmio ranges and irqs.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
src/util.h | 10 + src/fw/biostables.c | 622 ++++++++++++++++++++++++++++++++++++++++++++ src/post.c | 2 + src/Kconfig | 7 + 4 files changed, 641 insertions(+)
diff --git a/src/util.h b/src/util.h index 4f27fc307439..8ee0370492b8 100644 --- a/src/util.h +++ b/src/util.h @@ -94,6 +94,16 @@ void display_uuid(void); void copy_table(void *pos); void smbios_setup(void);
+struct acpi_device; +void acpi_dsdt_parse(void); +struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev, const char *hid); +struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid); +char *acpi_dsdt_name(struct acpi_device *dev); +int acpi_dsdt_present_eisaid(u16 eisaid); +int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max); +int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max); +int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq);
// fw/coreboot.c extern const char *CBvendor, *CBpart; struct cbfs_file; diff --git a/src/fw/biostables.c b/src/fw/biostables.c index 0d4fdb9c22e8..ebe1c90fca5e 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -17,6 +17,7 @@ #include "std/smbios.h" // struct smbios_entry_point #include "string.h" // memcpy #include "util.h" // copy_table +#include "list.h" // hlist_* #include "x86.h" // outb
struct pir_header *PirAddr VARFSEG; @@ -509,3 +510,624 @@ copy_table(void *pos) copy_acpi_rsdp(pos); copy_smbios(pos); }
+/****************************************************************
- DSDT parser
- ****************************************************************/
I think this code is sufficiently large to demand it's own C file - for example src/fw/dsdt_parser.c .
+struct acpi_device {
- struct hlist_node node;
- char name[16];
- u8 *hid_aml;
- u8 *sta_aml;
- u8 *crs_data;
- int crs_size;
+}; +static struct hlist_head acpi_devices;
It would be good to add VARVERIFY32INIT to this global.
+static int parse_error = 0; +static int parse_dumptree = 0; +static char parse_name[32]; +static struct acpi_device *parse_dev;
I think it would be preferable to not use global variables for temporary state. I think the above could be moved into a new "struct dsdt_parsing_state" and passed between functions. (I suspect the "u8 *ptr" could be moved into that struct as well.)
+static void parse_termlist(u8 *ptr, int offset, int pkglength);
I'm a little concerned about the unbounded recursion in this parsing code. The main SeaBIOS execution stack is pretty large, but nothing stops the dsdt table from doing something goofy. I think a sanity check on recursion depth may be worthwhile.
+static void hex(const u8 *ptr, int count, int lvl, const char *item) +{
- int l = 0, i;
- do {
dprintf(lvl, "%s: %04x: ", item, l);
for (i = l; i < l+16; i += 4)
dprintf(lvl, "%02x %02x %02x %02x ",
ptr[i+0], ptr[i+1], ptr[i+2], ptr[i+3]);
for (i = l; i < l+16; i++)
dprintf(lvl, "%c", (ptr[i] > 0x20 && ptr[i] < 0x80) ? ptr[i] : '.');
dprintf(lvl, "\n");
l += 16;
- } while (l < count);
+}
+static u64 parse_resource_int(u8 *ptr, int count) +{
- u64 value = 0;
- int index = 0;
- for (index = 0; index < count; index++)
value |= (u64)ptr[index] << (index * 8);
- return value;
+}
+static int parse_resource_bit(u8 *ptr, int count) +{
- int bit;
- for (bit = 0; bit < count*8; bit++)
if (ptr[bit/8] & (1 << (bit%8)))
return bit;
- return 0;
+}
+static int parse_resource(u8 *ptr, int length, int *type, u64 *min, u64 *max) +{
- int rname, rsize;
- u64 len;
- *type = -1;
- *min = 0;
- *max = 0;
- len = 0;
- if (!(ptr[0] & 0x80)) {
/* small resource */
rname = (ptr[0] >> 3) & 0x0f;
rsize = ptr[0] & 0x07;
rsize++;
switch (rname) {
case 0x04: /* irq */
*min = parse_resource_bit(ptr + 1, rsize);
*max = *min;
*type = 3;
break;
case 0x0f: /* end marker */
return 0;
case 0x08: /* io */
*min = parse_resource_int(ptr + 2, 2);
*max = parse_resource_int(ptr + 4, 2);
if (*min == *max) {
*max = *min + ptr[7] - 1;
*type = 1;
}
break;
case 0x09: /* fixed io */
*min = parse_resource_int(ptr + 2, 2);
*max = *min + ptr[4] - 1;
*type = 1;
break;
default:
dprintf(3, "%s: small: 0x%x (len %d)\n",
__func__, rname, rsize);
break;
}
- } else {
/* large resource */
rname = ptr[0] & 0x7f;
rsize = ptr[2] << 8 | ptr[1];
rsize += 3;
switch (rname) {
case 0x06: /* 32-bit Fixed Location Memory Range Descriptor */
*min = parse_resource_int(ptr + 4, 4);
len = parse_resource_int(ptr + 8, 4);
*max = *min + len - 1;
*type = 0;
break;
case 0x07: /* DWORD Address Space Descriptor */
*min = parse_resource_int(ptr + 10, 4);
*max = parse_resource_int(ptr + 14, 4);
*type = ptr[3];
break;
case 0x08: /* WORD Address Space Descriptor */
*min = parse_resource_int(ptr + 8, 2);
*max = parse_resource_int(ptr + 10, 2);
*type = ptr[3];
break;
case 0x09: /* irq */
*min = parse_resource_int(ptr + 5, 4);
*max = *min;
*type = 3;
break;
case 0x0a: /* QWORD Address Space Descriptor */
*min = parse_resource_int(ptr + 14, 8);
*max = parse_resource_int(ptr + 22, 8);
*type = ptr[3];
break;
default:
dprintf(3, "%s: large: 0x%x (len %d)\n", __func__, rname, rsize);
break;
}
- }
- return rsize;
+}
+static int find_resource(u8 *ptr, int len, int kind, u64 *min, u64 *max) +{
- int type, size, offset = 0;
- do {
size = parse_resource(ptr + offset, len - offset,
&type, min, max);
if (kind == type)
return 0;
offset += size;
- } while (size > 0 && offset < len);
- return -1;
+}
+static int print_resources(const char *prefix, u8 *ptr, int len) +{
- static const char *typename[] = { "mem", "i/o", "bus" };
- int type, size, offset = 0;
- u64 min, max;
- do {
size = parse_resource(ptr + offset, len - offset,
&type, &min, &max);
switch (type) {
case 0:
case 1:
case 2:
dprintf(1, "%s%s 0x%llx -> 0x%llx\n",
prefix, typename[type], min, max);
break;
case 3:
dprintf(1, "%sirq %lld\n", prefix, min);
break;
}
offset += size;
- } while (size > 0 && offset < len);
- return -1;
+}
+static int parse_nameseg(u8 *ptr, char **dst) +{
- if (dst && *dst) {
*(dst[0]++) = ptr[0];
if (ptr[1] != '_')
*(dst[0]++) = ptr[1];
if (ptr[2] != '_')
*(dst[0]++) = ptr[2];
if (ptr[3] != '_')
*(dst[0]++) = ptr[3];
*(dst[0]) = 0;
- }
- return 4;
+}
+static int parse_namestring(u8 *ptr, const char *item) +{
- char *dst = parse_name;
- int offset = 0;
- int i, count;
+again:
- switch (ptr[offset]) {
- case 0: /* null name */
offset++;
*(dst++) = 0;
break;
- case 0x2e:
offset++;
offset += parse_nameseg(ptr + offset, &dst);
*(dst++) = '.';
offset += parse_nameseg(ptr + offset, &dst);
break;
- case 0x2f:
offset++;
count = ptr[offset];
offset++;
for (i = 0; i < count; i++) {
if (i)
*(dst++) = '.';
offset += parse_nameseg(ptr + offset, &dst);
}
break;
- case '\':
*(dst++) = '\\';
offset++;
goto again;
- case '^':
*(dst++) = '^';
offset++;
goto again;
I think this code would be more clear if it used "for (;;) {" and "continue" instead of a backwards goto.
- case 'A' ... 'Z':
- case '_':
offset += parse_nameseg(ptr, &dst);
break;
- default:
hex(ptr, 16, 3, __func__);
parse_error = 1;
break;
- }
- dprintf(5, "%s: %s '%s'\n", __func__, item, parse_name);
- return offset;
+}
+static int parse_termarg_int(u8 *ptr, u64 *dst) +{
- u64 value;
- int offset = 1;
- switch (ptr[0]) {
- case 0x00: /* zero */
value = 0;
break;
- case 0x01: /* one */
value = 1;
break;
- case 0x0a: /* byte prefix */
value = ptr[1];
offset++;
break;
- case 0x0b: /* word prefix */
value = ptr[1] |
((unsigned long)ptr[2] << 8);
offset += 2;
break;
- case 0x0c: /* dword prefix */
value = ptr[1] |
((unsigned long)ptr[2] << 8) |
((unsigned long)ptr[3] << 16) |
((unsigned long)ptr[4] << 24);
offset += 4;
break;
- default:
value = 0;
hex(ptr, 16, 3, __func__);
parse_error = 1;
break;
- }
- if (dst)
*dst = value;
- dprintf(5, "%s: 0x%llx\n", __func__, value);
- return offset;
+}
+static int parse_pkglength(u8 *ptr, int *pkglength) +{
- int offset = 2;
- *pkglength = 0;
- switch (ptr[0] >> 6) {
- case 3:
*pkglength |= ptr[3] << 20;
offset++;
- case 2:
*pkglength |= ptr[2] << 12;
offset++;
- case 1:
*pkglength |= ptr[1] << 4;
*pkglength |= ptr[0] & 0x0f;
return offset;
- case 0:
- default:
*pkglength |= ptr[0] & 0x3f;
return 1;
- }
+}
+static int parse_pkg_common(u8 *ptr, const char *item, int *pkglength) +{
- int offset;
- offset = parse_pkglength(ptr, pkglength);
- offset += parse_namestring(ptr + offset, item);
- return offset;
+}
+static int parse_pkg_scope(u8 *ptr) +{
- int offset, pkglength;
- offset = parse_pkg_common(ptr, "skope", &pkglength);
skope?
- parse_termlist(ptr, offset, pkglength);
- return pkglength;
+}
+static int parse_pkg_device(u8 *ptr) +{
- int offset, pkglength;
- offset = parse_pkg_common(ptr, "device", &pkglength);
- parse_dev = malloc_high(sizeof(*parse_dev));
Shouldn't this be malloc_tmp() ?
- if (!parse_dev) {
warn_noalloc();
parse_error = 1;
return pkglength;
- }
- memset(parse_dev, 0, sizeof(*parse_dev));
- hlist_add_head(&parse_dev->node, &acpi_devices);
- strtcpy(parse_dev->name, parse_name, sizeof(parse_dev->name));
- parse_termlist(ptr, offset, pkglength);
- return pkglength;
+}
+static int parse_pkg_buffer(u8 *ptr) +{
- u64 blen;
- int pkglength, offset;
- offset = parse_pkglength(ptr, &pkglength);
- offset += parse_termarg_int(ptr + offset, &blen);
- if (strcmp(parse_name, "_CRS") == 0) {
parse_dev->crs_data = ptr + offset;
parse_dev->crs_size = blen;
- }
- return pkglength;
+}
+static int parse_pkg_skip(u8 *ptr, int op, int name) +{
- int pkglength, offset;
- char item[8];
- snprintf(item, sizeof(item), "op %x", op);
- offset = parse_pkglength(ptr, &pkglength);
- if (name) {
parse_namestring(ptr + offset, item);
- } else {
dprintf(5, "%s: %s (%d)\n", __func__, item, pkglength);
- }
- return pkglength;
+}
+static int parse_termobj(u8 *ptr) +{
- int offset = 1;
- switch (ptr[0]) {
- case 0x00: /* zero */
break;
- case 0x01: /* one */
break;
- case 0x08: /* name op */
offset += parse_namestring(ptr + offset, "name");
offset += parse_termobj(ptr + offset);
if (strcmp(parse_name, "_HID") == 0)
parse_dev->hid_aml = ptr;
if (strcmp(parse_name, "_STA") == 0)
parse_dev->sta_aml = ptr;
break;
- case 0x0a: /* byte prefix */
offset++;
break;
- case 0x0b: /* word prefix */
offset += 2;
break;
- case 0x0c: /* dword prefix */
offset += 4;
break;
- case 0x0d: /* string prefix */
while (ptr[offset])
offset++;
offset++;
break;
- case 0x10: /* scope op */
offset += parse_pkg_scope(ptr + offset);
break;
- case 0x11: /* buffer op */
offset += parse_pkg_buffer(ptr + offset);
break;
- case 0x12: /* package op */
- case 0x13: /* var package op */
offset += parse_pkg_skip(ptr + offset, ptr[0], 0);
break;
- case 0x14: /* method op */
offset += parse_pkg_skip(ptr + offset, ptr[0], 1);
if (strcmp(parse_name, "_STA") == 0)
parse_dev->sta_aml = ptr;
break;
- case 0x5b: /* ext op prefix */
offset++;
switch (ptr[1]) {
case 0x01: /* mutex op */
offset += parse_namestring(ptr + offset, "mutex");
offset++; /* sync flags */
break;
case 0x80: /* op region op */
offset += parse_namestring(ptr + offset, "op region");
offset++; /* region space */
offset += parse_termarg_int(ptr + offset, NULL);
offset += parse_termarg_int(ptr + offset, NULL);
break;
case 0x81: /* field op */
case 0x83: /* processor op */
case 0x84: /* power resource op */
case 0x85: /* thermal zone op */
offset += parse_pkg_skip(ptr + offset, 0x5b00 | ptr[1], 1);
break;
case 0x82: /* device op */
offset += parse_pkg_device(ptr + offset);
break;
default:
hex(ptr, 16, 3, __func__);
parse_error = 1;
break;
}
break;
- default:
hex(ptr, 16, 3, __func__);
parse_error = 1;
break;
- }
- return offset;
+}
+static void parse_termlist(u8 *ptr, int offset, int pkglength) +{
- for (;;) {
offset += parse_termobj(ptr + offset);
if (offset == pkglength)
return;
if (offset > pkglength) {
dprintf(1, "%s: overrun: %d/%d\n", __func__,
offset, pkglength);
parse_error = 1;
return;
}
if (parse_error) {
dprintf(1, "%s: parse error, skip from %d/%d\n", __func__,
offset, pkglength);
parse_error = 0;
return;
}
- }
+}
+static struct acpi_device *acpi_dsdt_find(struct acpi_device *prev, const u8 *aml, int size)
This code should be wrapped to 80 characters. (I know there's a bunch of places where I goofed at this in the past, but I think going forward we should try to keep to 80 characters.)
+{
- struct acpi_device *dev;
- struct hlist_node *node;
- if (!prev)
node = acpi_devices.first;
- else
node = prev->node.next;
- for (; node != NULL; node = dev->node.next) {
dev = container_of(node, struct acpi_device, node);
if (!aml)
return dev;
if (!dev->hid_aml)
continue;
if (memcmp(dev->hid_aml + 5, aml, size) == 0)
return dev;
- }
- return NULL;
+}
+static int acpi_dsdt_present(struct acpi_device *dev) +{
- if (!dev)
return 0; /* no */
- if (!dev->sta_aml)
return 1; /* yes */
- if (dev->sta_aml[0] == 0x14)
return -1; /* unknown (can't evaluate method) */
- if (dev->sta_aml[0] == 0x08) {
u64 value = 0;
parse_termarg_int(dev->sta_aml + 5, &value);
if (value == 0)
return 0; /* no */
else
return 1; /* yes */
- }
- return -1; /* unknown (should not happen) */
+}
+/****************************************************************
- DSDT parser, public interface
- ****************************************************************/
+struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev, const char *hid) +{
- if (!CONFIG_ACPI_PARSE)
return NULL;
- u8 aml[10];
- int len = snprintf((char*)aml, sizeof(aml), "\x0d%s", hid);
- return acpi_dsdt_find(prev, aml, len);
+}
+struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid) +{
- if (!CONFIG_ACPI_PARSE)
return NULL;
- u8 aml[] = {
0x0c, 0x41, 0xd0,
eisaid >> 8,
eisaid & 0xff
- };
- return acpi_dsdt_find(prev, aml, 5);
+}
+char *acpi_dsdt_name(struct acpi_device *dev) +{
- if (!CONFIG_ACPI_PARSE || !dev)
return NULL;
- return dev->name;
+}
+int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max) +{
- if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
return -1;
- return find_resource(dev->crs_data, dev->crs_size,
1 /* I/O */, min, max);
+}
+int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max) +{
- if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
return -1;
- return find_resource(dev->crs_data, dev->crs_size,
0 /* mem */, min, max);
+}
+int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq) +{
- u64 max;
- if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
return -1;
- return find_resource(dev->crs_data, dev->crs_size,
3 /* irq */, irq, &max);
+}
+int acpi_dsdt_present_eisaid(u16 eisaid) +{
- if (!CONFIG_ACPI_PARSE)
return -1; /* unknown */
- struct acpi_device *dev = acpi_dsdt_find_eisaid(NULL, eisaid);
- return acpi_dsdt_present(dev);
+}
+void acpi_dsdt_parse(void) +{
- if (!CONFIG_ACPI_PARSE)
return;
- struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
- if (!fadt)
return;
- u8 *dsdt = (void*)(fadt->dsdt);
- if (!dsdt)
return;
- u32 length = *(u32*)(dsdt + 4);
- u32 offset = 0x24;
- dprintf(1, "ACPI: parse DSDT at %p (len %d)\n", dsdt, length);
- parse_termlist(dsdt, offset, length);
- if (parse_dumptree) {
struct acpi_device *dev;
dprintf(1, "ACPI: dumping dsdt devices\n");
for (dev = acpi_dsdt_find(NULL, NULL, 0);
dev != NULL;
dev = acpi_dsdt_find(dev, NULL, 0)) {
dprintf(1, " %s", acpi_dsdt_name(dev));
if (dev->hid_aml)
dprintf(1, ", hid");
if (dev->sta_aml)
dprintf(1, ", sta (0x%x)", dev->sta_aml[0]);
if (dev->crs_data)
dprintf(1, ", crs");
dprintf(1, "\n");
if (dev->crs_data)
print_resources(" ", dev->crs_data, dev->crs_size);
}
- }
+} diff --git a/src/post.c b/src/post.c index f93106a1c9c9..febdc0859764 100644 --- a/src/post.c +++ b/src/post.c @@ -149,6 +149,8 @@ platform_hardware_setup(void) qemu_platform_setup(); coreboot_platform_setup();
- acpi_dsdt_parse();
Instead of adding this to post.c, could we add the call to find_acpi_features()? (And arrange for qemu_platform_setup() to call find_acpi_features() or directly call acpi_dsdt_parse().)
- // Setup timers and periodic clock interrupt timer_setup(); clock_setup();
diff --git a/src/Kconfig b/src/Kconfig index 6606ce4d46c9..ab36e676bf12 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -524,6 +524,13 @@ menu "BIOS Tables" This option can be disabled for QEMU 1.6 and older to save some space in the ROM file. If unsure, say Y.
- config ACPI_PARSE
bool "Include ACPI DSDT parser."
default n
help
Support parsing ACPI DSDT for device probing.
Needed to find virtio-mmio devices.
If unsure, say N.
If we're going to add a dsdt parser then I think it should default to enabled.
-Kevin
endmenu
source vgasrc/Kconfig
2.18.2 _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
Hi,
+/****************************************************************
- DSDT parser
- ****************************************************************/
I think this code is sufficiently large to demand it's own C file - for example src/fw/dsdt_parser.c .
Done.
+static struct hlist_head acpi_devices;
It would be good to add VARVERIFY32INIT to this global.
Done.
+static int parse_error = 0; +static int parse_dumptree = 0; +static char parse_name[32]; +static struct acpi_device *parse_dev;
I think it would be preferable to not use global variables for temporary state. I think the above could be moved into a new "struct dsdt_parsing_state" and passed between functions.
Done.
(I suspect the "u8 *ptr" could be moved into that struct as well.)
Not that easy with the current position/offset tracking, specifically the parse-something(ptr + offset, ...); calls are tricky.
+static void parse_termlist(u8 *ptr, int offset, int pkglength);
I'm a little concerned about the unbounded recursion in this parsing code. The main SeaBIOS execution stack is pretty large, but nothing stops the dsdt table from doing something goofy. I think a sanity check on recursion depth may be worthwhile.
Added.
+again:
- switch (ptr[offset]) {
- case 0: /* null name */
offset++;
*(dst++) = 0;
break;
[ ... ]
- case '^':
*(dst++) = '^';
offset++;
goto again;
I think this code would be more clear if it used "for (;;) {" and "continue" instead of a backwards goto.
Hmm, doesn't help that much due to for + switch nesting. I would need either an additional state variable or use goto to jump from inside switch out of the for loop. Both ways don't make things more clear compared to the current state ...
+static int parse_pkg_scope(u8 *ptr) +{
- int offset, pkglength;
- offset = parse_pkg_common(ptr, "skope", &pkglength);
skope?
Tyops. Fixed.
+static int parse_pkg_device(u8 *ptr) +{
- int offset, pkglength;
- offset = parse_pkg_common(ptr, "device", &pkglength);
- parse_dev = malloc_high(sizeof(*parse_dev));
Shouldn't this be malloc_tmp() ?
Yes, device list is not needed any more after post.
+static struct acpi_device *acpi_dsdt_find(struct acpi_device *prev, const u8 *aml, int size)
This code should be wrapped to 80 characters. (I know there's a bunch of places where I goofed at this in the past, but I think going forward we should try to keep to 80 characters.)
Done.
- acpi_dsdt_parse();
Instead of adding this to post.c, could we add the call to find_acpi_features()? (And arrange for qemu_platform_setup() to call find_acpi_features() or directly call acpi_dsdt_parse().)
Done (calling acpi_dsdt_parse directly, with qemu we don't need to search the tables for pm_timer).
- config ACPI_PARSE
bool "Include ACPI DSDT parser."
default n
help
Support parsing ACPI DSDT for device probing.
Needed to find virtio-mmio devices.
If unsure, say N.
If we're going to add a dsdt parser then I think it should default to enabled.
Done.
New versions should come later today.
take care, Gerd
On Wed, Apr 08, 2020 at 02:39:39PM +0200, Gerd Hoffmann wrote:
+again:
- switch (ptr[offset]) {
- case 0: /* null name */
offset++;
*(dst++) = 0;
break;
[ ... ]
- case '^':
*(dst++) = '^';
offset++;
goto again;
I think this code would be more clear if it used "for (;;) {" and "continue" instead of a backwards goto.
Hmm, doesn't help that much due to for + switch nesting. I would need either an additional state variable or use goto to jump from inside switch out of the for loop. Both ways don't make things more clear compared to the current state ...
static int parse_namestring(struct parse_state *s, u8 *ptr, const char *item) { char *dst = s->name; int offset = 0; int i, count;
for (;;) { switch (ptr[offset]) { case 0: /* null name */ offset++; *(dst++) = 0; break; case 0x2e: offset++; offset += parse_nameseg(ptr + offset, &dst); *(dst++) = '.'; offset += parse_nameseg(ptr + offset, &dst); break; case 0x2f: offset++; count = ptr[offset]; offset++; for (i = 0; i < count; i++) { if (i) *(dst++) = '.'; offset += parse_nameseg(ptr + offset, &dst); } break; case '\': *(dst++) = '\'; offset++; continue; case '^': *(dst++) = '^'; offset++; continue; case 'A' ... 'Z': case '_': offset += parse_nameseg(ptr, &dst); break; default: hex(ptr, 16, 3, __func__); s->error = 1; break; } break; } dprintf(5, "%s: %d %s '%s'\n", __func__, s->depth, item, s->name); return offset; }
-Kevin
Don't initialize the ps/2 keyboard in case the device is not listed in the ACPi DSDT table.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/ps2port.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/src/hw/ps2port.c b/src/hw/ps2port.c index 2c334c06b7eb..c82521b42e16 100644 --- a/src/hw/ps2port.c +++ b/src/hw/ps2port.c @@ -542,6 +542,10 @@ ps2port_setup(void) ASSERT32FLAT(); if (! CONFIG_PS2PORT) return; + if (acpi_dsdt_present_eisaid(0x0303) == 0) { + dprintf(1, "ACPI: no PS/2 keyboard present\n"); + return; + } dprintf(3, "init ps2port\n");
enable_hwirq(1, FUNC16(entry_09));
On Fri, Apr 03, 2020 at 10:31:20AM +0200, Gerd Hoffmann wrote:
Don't initialize the ps/2 keyboard in case the device is not listed in the ACPi DSDT table.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
src/hw/ps2port.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/src/hw/ps2port.c b/src/hw/ps2port.c index 2c334c06b7eb..c82521b42e16 100644 --- a/src/hw/ps2port.c +++ b/src/hw/ps2port.c @@ -542,6 +542,10 @@ ps2port_setup(void) ASSERT32FLAT(); if (! CONFIG_PS2PORT) return;
- if (acpi_dsdt_present_eisaid(0x0303) == 0) {
dprintf(1, "ACPI: no PS/2 keyboard present\n");
return;
- }
Unless I'm missing something, if the dsdt parser is enabled, but the dsdt is not actually found, this would turn off the keyboard. I think it should only disable the keyboard detection if the dsdt is actually found and doesn't contain a ps2 port entry.
-Kevin
dprintf(3, "init ps2port\n"); enable_hwirq(1, FUNC16(entry_09));
-- 2.18.2 _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
On Mon, Apr 06, 2020 at 07:26:07PM -0400, Kevin O'Connor wrote:
On Fri, Apr 03, 2020 at 10:31:20AM +0200, Gerd Hoffmann wrote:
Don't initialize the ps/2 keyboard in case the device is not listed in the ACPi DSDT table.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
src/hw/ps2port.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/src/hw/ps2port.c b/src/hw/ps2port.c index 2c334c06b7eb..c82521b42e16 100644 --- a/src/hw/ps2port.c +++ b/src/hw/ps2port.c @@ -542,6 +542,10 @@ ps2port_setup(void) ASSERT32FLAT(); if (! CONFIG_PS2PORT) return;
- if (acpi_dsdt_present_eisaid(0x0303) == 0) {
dprintf(1, "ACPI: no PS/2 keyboard present\n");
return;
- }
Unless I'm missing something, if the dsdt parser is enabled, but the dsdt is not actually found, this would turn off the keyboard.
Correct, didn't consider that corner case. Will fix.
take care, Gerd
Seach for virtio-mmio devices in the DSDT table, register the devices found.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-mmio.h | 1 + src/util.h | 1 + src/hw/virtio-mmio.c | 20 ++++++++++++++++++++ src/post.c | 2 ++ 4 files changed, 24 insertions(+)
diff --git a/src/hw/virtio-mmio.h b/src/hw/virtio-mmio.h index 5e24f692c1ae..349d744d9364 100644 --- a/src/hw/virtio-mmio.h +++ b/src/hw/virtio-mmio.h @@ -71,6 +71,7 @@ typedef struct virtio_mmio_cfg { } virtio_mmio_cfg;
void virtio_mmio_register(u64 mmio); +void virtio_mmio_acpi(void); void virtio_mmio_setup(void); void vp_init_mmio(struct vp_device *vp, void *mmio);
diff --git a/src/util.h b/src/util.h index 8ee0370492b8..76e1bfcd9897 100644 --- a/src/util.h +++ b/src/util.h @@ -100,6 +100,7 @@ struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev, const char * struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid); char *acpi_dsdt_name(struct acpi_device *dev); int acpi_dsdt_present_eisaid(u16 eisaid); +void acpi_dsdt_virtio(void); int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max); int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max); int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq); diff --git a/src/hw/virtio-mmio.c b/src/hw/virtio-mmio.c index b205ec6ef6ae..78618ab49e44 100644 --- a/src/hw/virtio-mmio.c +++ b/src/hw/virtio-mmio.c @@ -3,6 +3,7 @@ #include "output.h" // dprintf #include "stacks.h" // run_thread #include "string.h" // memset +#include "util.h" // acpi_dsdt_* #include "virtio-pci.h" #include "virtio-blk.h" #include "virtio-scsi.h" @@ -34,6 +35,25 @@ void virtio_mmio_register(u64 mmio) dprintf(1, "virtio-mmio: device list full\n"); }
+void virtio_mmio_acpi(void) +{ + static const char *virtio_hid = "LNRO0005"; + struct acpi_device *dev; + u64 mem, irq, unused; + + for (dev = acpi_dsdt_find_string(NULL, virtio_hid); + dev != NULL; + dev = acpi_dsdt_find_string(dev, virtio_hid)) { + if (acpi_dsdt_find_mem(dev, &mem, &unused) < 0) + continue; + if (acpi_dsdt_find_irq(dev, &irq) < 0) + continue; + dprintf(1, "ACPI: virtio-mmio device %s at 0x%llx, irq %lld\n", + acpi_dsdt_name(dev), mem, irq); + virtio_mmio_register(mem); + } +} + void virtio_mmio_setup(void) { u32 magic, version, devid; diff --git a/src/post.c b/src/post.c index febdc0859764..c4ab5b814a00 100644 --- a/src/post.c +++ b/src/post.c @@ -17,6 +17,7 @@ #include "hw/rtc.h" // rtc_write #include "hw/serialio.h" // serial_debug_preinit #include "hw/usb.h" // usb_setup +#include "hw/virtio-mmio.h" // virtio_mmio_acpi #include "malloc.h" // malloc_init #include "memmap.h" // SYMBOL #include "output.h" // dprintf @@ -150,6 +151,7 @@ platform_hardware_setup(void) coreboot_platform_setup();
acpi_dsdt_parse(); + virtio_mmio_acpi();
// Setup timers and periodic clock interrupt timer_setup();