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
--
2.18.2
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
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
_______________________________________________
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
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
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
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
© 2016 - 2025 Red Hat, Inc.