Message ID | 1358967544-20174-1-git-send-email-colin.king@canonical.com |
---|---|
State | Accepted |
Headers | show |
On 01/24/2013 02:59 AM, Colin King wrote: > From: Colin Ian King <colin.king@canonical.com> > > Add a new utility that scans for all the _CRS buffers and dumps the > buffer contents in an annotated form. Tested against my data base > of ACPI tables. > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > src/Makefile.am | 1 + > src/acpi/crsdump/crsdump.c | 892 +++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 893 insertions(+) > create mode 100644 src/acpi/crsdump/crsdump.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index d62574b..f80936d 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -27,6 +27,7 @@ fwts_SOURCES = main.c \ > acpi/battery/battery.c \ > acpi/brightness/brightness.c \ > acpi/checksum/checksum.c \ > + acpi/crsdump/crsdump.c \ > acpi/cstates/cstates.c \ > acpi/dmar/dmar.c \ > acpi/fadt/fadt.c \ > diff --git a/src/acpi/crsdump/crsdump.c b/src/acpi/crsdump/crsdump.c > new file mode 100644 > index 0000000..d6dd6df > --- /dev/null > +++ b/src/acpi/crsdump/crsdump.c > @@ -0,0 +1,892 @@ > +/* > + * Copyright (C) 2013 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + */ > +#include "fwts.h" > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <inttypes.h> > + > +/* acpica headers */ > +#include "acpi.h" > +#include "fwts_acpi_object_eval.h" > + > +typedef struct { > + const char *label; /* Field label */ > + size_t offset; /* Offset into _CRS buffer */ > + size_t bitlength; /* Size of field in bits */ > + uint64_t bitmask; /* Bit mask, 0 = use all bits */ > + uint8_t shift; /* Value shift */ > + const char **annotation; /* Annotations */ > + const char *(*callback)(const uint64_t val); /* val -> string mapping callback for CRS_UINTX */ > +} crsdump_info; > + > +#define CRS_UINT(label, offset, bitlength) { label, offset, bitlength, 0, 0, NULL, NULL } > +#define CRS_UINT24(label, offset, bitlength) { label, offset, bitlength, 0, 8, NULL, NULL } > +#define CRS_UINTX(label, offset, bitlength, callback) { label, offset, bitlength, 0, 0, NULL, callback } > + > +#define CRS_BITS(label, offset, bitmask) { label, offset, 8, bitmask, 0, NULL, NULL } > +#define CRS_BITX(label, offset, bitmask, annotation) { label, offset, 8, bitmask, 0, annotation, NULL } > +#define CRS_END { NULL, 0, 0, 0, 0, 0, NULL } > + > +static void crsdump_show_header( > + fwts_framework *fw, > + const char *objname, > + const char *crs_name) > +{ > + fwts_log_info_verbatum(fw, "%s (%s):", objname, crs_name); > +} > + > +static void crsdump_show_info( > + fwts_framework *fw, > + const uint8_t *data, > + const size_t length, > + const crsdump_info *info) > +{ > + /* > + * Walk through fields and dump data according to the mapping > + */ > + for ( ; info->label; info++) { > + uint64_t val; > + uint64_t mask = info->bitmask; > + int hexdigits = info->bitlength >> 2; > + > + if (info->offset + (info->bitlength >> 3) > length) > + continue; > + > + if (info->bitmask) { > + /* > + * CRS_BIT*() data > + */ > + val = (uint64_t)*(uint8_t*)(data + info->offset); > + while (mask && ((mask & 1) == 0)) { > + val >>= 1; > + mask >>= 1; > + } > + val &= mask; > + hexdigits = 2; > + } else { > + /* > + * CRS_UINT*() data > + */ > + switch (info->bitlength) { > + case 8: > + val = (uint64_t)*(uint8_t*)(data + info->offset); > + break; > + case 16: > + val = (uint64_t)*(uint16_t*)(data + info->offset); > + break; > + case 32: > + val = (uint64_t)*(uint32_t*)(data + info->offset); > + break; > + case 64: > + val = (uint64_t)*(uint64_t*)(data + info->offset); > + break; > + default: > + val = ~0; > + break; > + } > + } > + val = val << info->shift; > + > + if (info->annotation) { > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64 " (%s)", > + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val, > + info->annotation[val]); > + } else if (info->callback) { > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64 " (%s)", > + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val, > + info->callback(val)); > + } else { > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64, > + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val); > + } > + } > +} > + > +static void crsdump_show( > + fwts_framework *fw, > + const char *objname, > + const char *crs_name, > + const uint8_t *data, > + const size_t length, > + const crsdump_info *header, > + const crsdump_info *info) > +{ > + crsdump_show_header(fw, objname, crs_name); > + crsdump_show_info(fw, data, length, header); > + crsdump_show_info(fw, data, length, info); > +} > + > +static void crsdump_data( > + fwts_framework *fw, > + const uint8_t *data, > + const size_t from, > + const size_t to) > +{ > + size_t i; > + char buffer[120]; > + > + for (i = from; i < to; i+= 16) { > + size_t n = to - i; > + > + fwts_dump_raw_data(buffer, sizeof(buffer), data + i, i, n > 16 ? 16 : n); > + buffer[56] = '\0'; /* Truncate off text version of hex dump */ > + > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: %s", > + (uint16_t)i, "Hex Dump", buffer + 8); > + } > +} > + > +/* > + * crsdump_init() > + * initialize ACPI > + */ > +static int crsdump_init(fwts_framework *fw) > +{ > + if (fwts_acpi_init(fw) != FWTS_OK) { > + fwts_log_error(fw, "Cannot initialise ACPI."); > + return FWTS_ERROR; > + } > + > + return FWTS_OK; > +} > + > +/* > + * crsdump_deinit > + * de-intialize ACPI > + */ > +static int crsdump_deinit(fwts_framework *fw) > +{ > + return fwts_acpi_deinit(fw); > +} > + > +/* > + * See section 6.4.2.7 Fixed DMA Descriptor, DMA transfer width > + */ > +static const char *crs_dma_transfer_width(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "8 bit"; > + case 0x01: > + return "16 bit"; > + case 0x02: > + return "32 bit"; > + case 0x03: > + return "64 bit"; > + case 0x04: > + return "128 bit"; > + case 0x05: > + return "256 bit"; > + default: > + return "reserved"; > + } > +} > + > +/* > + * See section 6.4.3.5.2 DWord Address Space Descriptor Resource Type > + */ > +static const char *crs_resource_type(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Memory range"; > + case 0x01: > + return "I/O range"; > + case 0x02: > + return "Bus number range"; > + case 0xc0 ... 0xff: > + return "Hardware Vendor Defined"; > + default: > + return "Reserved"; > + } > +} > + > +/* > + * See section 6.4.8.1. Generic Register Descriptor Address Space ID > + */ > +static const char *crs_address_space_id(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "System Memory"; > + case 0x01: > + return "System I/O"; > + case 0x02: > + return "PCI Configuration Space"; > + case 0x03: > + return "Embedded Controller"; > + case 0x04: > + return "SMBus"; > + case 0x0a: > + return "PCC"; > + case 0x7f: > + return "Functional Fixed Hardware"; > + default: > + return "Uknown"; > + } > +} > + > +/* > + * See section 6.4.8.1. Generic Register Descriptor Address Size > + */ > +static const char *crs_address_size(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Undefined (legacy)"; > + case 0x01: > + return "Byte Access"; > + case 0x02: > + return "Word Access"; > + case 0x04: > + return "Dword Access"; > + case 0x08: > + return "Qword Access"; > + default: > + return "Unknown"; > + } > +} > + > +/* > + * See section 6.4.3.8.1 GPIO Connection Description > + */ > +static const char *crs_gpio_connection_type(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Interrupt Connection"; > + case 0x01: > + return "I/O Connection"; > + default: > + return "Reserved"; > + } > +} > + > +/* > + * See section 6.4.3.8.1 GPIO Connection Description > + */ > +static const char *crs_pin_configuration(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Default Configuration"; > + case 0x01: > + return "Pull-Up"; > + case 0x02: > + return "Pull-Down"; > + case 0x03: > + return "No Pull"; > + case 0x80 ... 0xff: > + return "Vendor Defined"; > + default: > + return "Reserved"; > + } > +} > + > +/* > + * CRS small resource checks, simple checking > + */ > +static void crsdump_small_resource_items( > + fwts_framework *fw, > + const char *objname, > + const uint8_t *data, > + const size_t length) > +{ > + uint8_t tag_item = (data[0] >> 3) & 0xf; > + size_t crs_length = 1 + (data[0] & 7); > + > + static const crsdump_info header[] = { > + CRS_BITS("Tag Type", 0, 128), > + CRS_BITS("Tag Item ID", 0, 64 | 32 | 16 | 8), > + CRS_BITS("Tag Length", 0, 4 | 2 | 1), > + }; > + > + /* Ensure we just dump minimum _CRS buffer length */ > + if (crs_length > length) > + crs_length = length; > + > + switch (tag_item) { > + case 0x4: /* 6.4.2.1 IRQ Descriptor */ > + { > + static const char *sharing[] = { > + "Exclusive", > + "Shared", > + "Exclusive And Wake", > + "Shared And Wake" > + }; > + > + static const char *polarity[] = { > + "Active-High", > + "Active-Low" > + }; > + > + static const char *mode[] = { > + "Level-Triggered", > + "Edge-Triggered" > + }; > + > + static const crsdump_info info[] = { > + CRS_UINT("IRQ Mask", 1, 16), > + CRS_BITS("Reserved", 3, 128 | 64), > + CRS_BITX("Interrupt Sharing", 3, 32 | 16, sharing), > + CRS_BITX("Interrupt Polarity", 3, 8, polarity), > + CRS_BITS("Ignored", 3, 4 | 2), > + CRS_BITX("Interrupt Mode", 3, 1, mode), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "IRQ Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x5: /* 6.4.2.2 DMA Descriptor */ > + { > + static const char *dma_speed[] = { > + "Compatibility Mode", > + "Type A DMA", > + "Type B DMA", > + "Type F DMA" > + }; > + > + static const char *bus_master[] = { > + "Not a bus master", > + "Is a bus master" > + }; > + > + static const char *dma_size[] = { > + "8 bit only", > + "8 and 16 bit", > + "16 bit only", > + "Reserved" > + }; > + > + static const crsdump_info info[] = { > + CRS_UINT("DMA channel mask", 1, 16), > + CRS_BITS("Reserved", 2, 128), > + CRS_BITX("DMA channel speed", 2, 64 | 32, dma_speed), > + CRS_BITS("Ignored", 2, 16 | 8), > + CRS_BITX("Logical device bus master", 2, 4, bus_master), > + CRS_BITX("DMA transfer type preference",2, 2 | 1, dma_size), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "DMA Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x6: /* 6.4.2.3 Start Dependent Functions Descriptor */ > + { > + static const char *config[] = { > + "Good configurarion", > + "Acceptable configuration", > + "Sub-optimal configuration", > + "Reserved" > + }; > + > + static const crsdump_info info[] = { > + CRS_BITS("Reserved", 1, 0xf0), > + CRS_BITX("Performance/robustness", 1, 8 | 4, config), > + CRS_BITX("Compatibility priority", 1, 2 | 1, config), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Start Dependent Functions Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x7: /* 6.4.2.4 End Dependent Functions Descriptor */ > + crsdump_show_header(fw, objname, "End Dependent Functions Descriptor"); > + break; > + case 0x8: /* 6.4.2.5 I/O Port Descriptor */ > + { > + static const char *decodes[] = { > + "16 bit addresses", > + "10 bit addresses" > + }; > + static const crsdump_info info[] = { > + CRS_BITS("Reserved", 1, 0xfe), > + CRS_BITX("Logical Device Decode", 1, 1, decodes), > + CRS_UINT("Minimum Base Address", 2, 16), > + CRS_UINT("Maximum Base Address", 4, 16), > + CRS_UINT("Base Alignment", 6, 8), > + CRS_UINT("Range Length", 7, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "I/O Port Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x9: /* 6.4.2.6 Fixed Location I/O Port Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINT("Range Base Address", 1, 16), > + CRS_UINT("Range Length", 3, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Fixed Location I/O Port Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xa: /* 6.4.2.7 Fixed DMA Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINT("DMA Request Line", 1, 16), > + CRS_UINT("DMA Channel", 3, 16), > + CRS_UINTX("DMA Transfer Width", 5, 1, crs_dma_transfer_width), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Fixed DMA Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xe: /* 6.4.2.8 Vendor-Defined Descriptor */ > + crsdump_show_header(fw, objname, "Vendor-Defined Descriptor"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + case 0xf: /* 6.4.2.9 End Tag */ > + { > + static const crsdump_info info[] = { > + CRS_UINT("Checksum", 1, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "End Tag", > + data, crs_length, header, info); > + } > + break; > + default: > + crsdump_show_header(fw, objname, "Unknown type"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 1, crs_length); > + break; > + } > + > + fwts_log_nl(fw); > +} > + > +/* > + * CRS large resource checks, simple checking > + */ > +static void crsdump_large_resource_items( > + fwts_framework *fw, > + const char *objname, > + const uint8_t *data, > + const uint64_t length) > +{ > + uint8_t tag_item = data[0] & 0x7f; > + size_t crs_length = data[1]; > + > + static const crsdump_info header[] = { > + CRS_BITS("Tag Type", 0, 128), > + CRS_BITS("Tag Item ID", 0, 0x7f), > + CRS_UINT("Length", 1, 16), > + CRS_END > + }; > + > + static const char *write_status[] = { > + "non-writeable, read-only", > + "writeable, read/write" > + }; > + > + static const char *mifmaf[] = { > + "Not fixed", > + "Fixed" > + }; > + > + static const char *decode_type[] = { > + "Bridge Positively decodes this address", > + "Bridge Subtractively decode this address" > + }; > + > + static const char *sharing[] = { > + "Exclusive", > + "Shared", > + "Exclusive and Wake", > + "Shared and Wake" > + }; > + > + static const char *polarity[] = { > + "Active-High", > + "Active-Low", > + "Active-Both", > + "Reserved" > + }; > + > + static const char *mode[] = { > + "Level-Triggered", > + "Edge-Triggered" > + }; > + > + static const char *consumer[] = { > + "producer and consumer", > + "consumer" > + }; > + > + /* Ensure we just dump minimum _CRS buffer length */ > + if (crs_length > length) > + crs_length = length; > + > + switch (tag_item) { > + case 0x1: /* 6.4.3.1 24-Bit Memory Range Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITX("Write Status", 3, 1, write_status), > + CRS_UINT24("Range Minimum Base", 4, 16), > + CRS_UINT24("Range Maximum Base", 6, 16), > + CRS_UINT("Base Alignment", 8, 16), > + CRS_UINT("Range Length", 10, 16), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "24-Bit Memory Range Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x2: /* 6.4.3.7 Generic Register Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Address Space ID", 3, 8, crs_address_space_id), > + CRS_UINT("Address Bit Width", 4, 8), > + CRS_UINT("Register Bit Offset", 5, 8), > + CRS_UINTX("Address Size", 6, 8, crs_address_size), > + CRS_UINT("Register Address", 7, 64), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Generic Register Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x4: /* 6.4.3.2 Vendor-Defined Descriptor */ > + crsdump_show_header(fw, objname, "Vendor-Defined Descriptor"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + case 0x5: /* 6.4.3.3 32-Bit Memory Range Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITX("Write Status", 3, 1, write_status), > + CRS_UINT("Range Minimum Address", 4, 32), > + CRS_UINT("Range Maximum Address", 8, 32), > + CRS_UINT("Base Alignment", 12, 32), > + CRS_UINT("Range Length", 16, 32), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "32-Bit Memory Range Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x6: /* 6.4.3.4 32-Bit Fixed Memory Range Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITX("Write Status", 3, 1, write_status), > + CRS_UINT("Range Base Address", 4, 32), > + CRS_UINT("Range Length", 8, 32), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "32-Bit Fixed Memory Range Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x7: /* 6.4.3.5.2 DWord Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITS("Ignored", 4, 1), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Address Space Granularity", 6, 32), > + CRS_UINT("Address Range Minimum", 10, 32), > + CRS_UINT("Address Range Maximum", 14, 32), > + CRS_UINT("Address Translation Offset", 18, 32), > + CRS_UINT("Address Length", 22, 32), > + CRS_UINT("Resource Source Index", 26, 1), > + /* Skip Resource Source String */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "DWord Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x8: /* 6.4.3.5.3 Word Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITS("Ignored", 4, 1), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Address Space Granularity", 6, 16), > + CRS_UINT("Address Range Minimum", 8, 16), > + CRS_UINT("Address Range Maximum", 10, 16), > + CRS_UINT("Address Translation Offset", 12, 16), > + CRS_UINT("Address Length", 14, 16), > + CRS_UINT("Resource Source Index", 16, 1), > + /* Skip Resource Source String */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Word Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x9: /* 6.4.3.6 Extended Interrupt Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITS("Reserved", 3, 128 | 64 | 32), > + CRS_BITX("Interrupt Sharing", 3, 16 | 8, sharing), > + CRS_BITX("Interrupt Polarity", 7, 4, polarity), > + CRS_BITX("Interrupt Mode", 7, 2, mode), > + CRS_BITX("Interrupt Consumer/Producer", 7, 1, consumer), > + CRS_UINT("Interrupt Table Length", 8, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Extended Interrupt Descriptor", > + data, crs_length, header, info); > + crsdump_data(fw, data, 5, crs_length); > + } > + break; > + case 0xa: /* 6.4.3.5.1 QWord Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITS("Ignored", 4, 1), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Address Space Granularity", 6, 64), > + CRS_UINT("Address Range Minimum", 14, 64), > + CRS_UINT("Address Range Maximum", 22, 64), > + CRS_UINT("Address Translation Offset", 30, 64), > + CRS_UINT("Address Length", 38, 64), > + CRS_UINT("Resource Source Index", 46, 1), > + /* Skip Resource Source String */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "QWord Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xb: /* 6.4.3.5.4 Extended Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITX("Consumer/Producer", 4, 1, consumer), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Revision ID", 6, 8), > + CRS_UINT("Reserved", 7, 8), > + CRS_UINT("Address Space Granularity", 8, 64), > + CRS_UINT("Address Range Minimum", 16, 64), > + CRS_UINT("Address Range Maximum", 24, 64), > + CRS_UINT("Address Translation Offset", 32, 64), > + CRS_UINT("Address Length", 40, 64), > + CRS_UINT("Type Specific Attribute", 48, 64), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Extended Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xc: /* 6.4.3.8.1 GPIO Connection Descriptor */ > + { > + if (crs_length < 4) > + break; > + > + if (data[4] == 0) { > + > + /* Interrupt connection */ > + static const crsdump_info info[] = { > + CRS_UINT("Revision ID", 3, 8), > + CRS_UINTX("GPIO Connection Type", 4, 8, crs_gpio_connection_type), > + CRS_BITS("Reserved", 5, 0xfe), > + CRS_BITX("Consumer/Producer", 5, 1, consumer), > + CRS_UINT("Reserved", 6, 8), > + CRS_BITS("Reserved", 7, 128 | 64 | 32), > + CRS_BITX("Interrupt Sharing and Wake", 7, 16 | 8, sharing), > + CRS_BITX("Interrupt Polarity", 7, 4 | 2, polarity), > + CRS_BITX("Interrupt Mode", 7, 1, mode), > + CRS_UINT("Interrupt and I/O Flags", 8, 8), > + CRS_UINTX("Pin Configuration", 9, 8, crs_pin_configuration), > + CRS_UINT("Output Driver Strength", 10, 16), > + CRS_UINT("Debounce Timeout (bits)", 12, 16), > + CRS_UINT("Pin Table Offset", 14, 16), > + CRS_UINT("Resource Source Index", 16, 8), > + CRS_UINT("Resource Name Offset", 17, 16), > + CRS_UINT("Vendor Data Offset", 19, 16), > + CRS_UINT("Vendor Data Length", 21, 16), > + /* Skip pin table */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "GPIO Connection Descriptor", > + data, crs_length, header, info); > + } else if (data[4] == 1) { > + static const char *sharing[] = { > + "Exclusive", > + "Shared", > + }; > + > + static const char *restriction[] = { > + "Input or Output", > + "Input Only", > + "Output Only", > + "Input or Ouput, config must be preserved" > + }; > + > + /* I/O connection */ > + static const crsdump_info info[] = { > + CRS_UINT("Revision ID", 3, 8), > + CRS_UINTX("GPIO Connection Type", 4, 8, crs_gpio_connection_type), > + CRS_BITS("Reserved", 5, 0xfe), > + CRS_BITX("Consumer/Producer", 5, 1, consumer), > + CRS_UINT("Reserved", 6, 8), > + CRS_BITS("Reserved", 7, 128 | 64 | 32 | 16), > + CRS_BITX("Interrupt Sharing", 7, 8, sharing), > + CRS_BITS("Reserved", 7, 4), > + CRS_BITX("I/O Restriction", 7, 2 | 1, restriction), > + CRS_UINT("Interrupt and I/O Flags", 8, 8), > + CRS_UINTX("Pin Configuration", 9, 8, crs_pin_configuration), > + CRS_UINT("Output Driver Strength", 10, 16), > + CRS_UINT("Debounce Timeout (bits)", 12, 16), > + CRS_UINT("Pin Table Offset", 14, 16), > + CRS_UINT("Resource Source Index", 16, 8), > + CRS_UINT("Resource Name Offset", 17, 16), > + CRS_UINT("Vendor Data Offset", 19, 16), > + CRS_UINT("Vendor Data Length", 21, 16), > + /* Skip pin table */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "GPIO Connection Descriptor", > + data, crs_length, header, info); > + } else { > + /* No idea of the connection type */ > + static const crsdump_info info[] = { > + CRS_UINT("Revision ID", 3, 8), > + CRS_UINT("GPIO Connection Type", 4, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "GPIO Connection Descriptor", > + data, crs_length, header, info); > + } > + > + } > + break; > + case 0xe: /* 6.4.3.8.2 Serial Bus Connection Descriptors */ > + /* This is not frequently used, deferring implementation to later */ > + crsdump_show_header(fw, objname, "Serial Bus Connection Descriptor"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + default: > + crsdump_show_header(fw, objname, "Unknown type"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + } > + > + fwts_log_nl(fw); > +} > + > +static int crsdump_test1(fwts_framework *fw) > +{ > + fwts_list_link *item; > + fwts_list *objects; > + const size_t name_len = 4; > + > + if ((objects = fwts_acpi_object_get_names()) == NULL) { > + fwts_log_info(fw, "Cannot find any ACPI objects"); > + return FWTS_ERROR; > + } > + > + fwts_list_foreach(item, objects) { > + char *name = fwts_list_data(char*, item); > + const size_t len = strlen(name); > + if (strncmp("_CRS", name + len - name_len, name_len) == 0) { > + ACPI_OBJECT_LIST arg_list; > + ACPI_BUFFER buf; > + ACPI_OBJECT *obj; > + uint8_t *data; > + int ret; > + > + arg_list.Count = 0; > + arg_list.Pointer = NULL; > + > + ret = fwts_acpi_object_evaluate(fw, name, &arg_list, &buf); > + if ((ACPI_FAILURE(ret) != AE_OK) || (buf.Pointer == NULL)) > + continue; > + > + /* Do we have a valid _CRS buffer to dump? */ > + obj = buf.Pointer; > + if ((obj->Type == ACPI_TYPE_BUFFER) && > + (obj->Buffer.Pointer != NULL) && > + (obj->Buffer.Length > 0)) { > + data = (uint8_t*)obj->Buffer.Pointer; > + > + if (data[0] & 128) > + crsdump_large_resource_items(fw, name, data, obj->Buffer.Length); > + else > + crsdump_small_resource_items(fw, name, data, obj->Buffer.Length); > + } > + > + if (buf.Length && buf.Pointer) > + free(buf.Pointer); > + } > + } > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test crsdump_tests[] = { > + { crsdump_test1, "Dump ACPI _CRS buffers." }, > + { NULL, NULL } > +}; > + > +static fwts_framework_ops crsdump_ops = { > + .description = "Dump ACPI _CRS buffers.", > + .init = crsdump_init, > + .deinit = crsdump_deinit, > + .minor_tests = crsdump_tests > +}; > + > +FWTS_REGISTER("crsdump", &crsdump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS); > Acked-by: Ivan Hu <ivan.hu@canonical.com>
On Thu, Jan 24, 2013 at 2:59 AM, Colin King <colin.king@canonical.com> wrote: > From: Colin Ian King <colin.king@canonical.com> > > Add a new utility that scans for all the _CRS buffers and dumps the > buffer contents in an annotated form. Tested against my data base > of ACPI tables. > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > src/Makefile.am | 1 + > src/acpi/crsdump/crsdump.c | 892 +++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 893 insertions(+) > create mode 100644 src/acpi/crsdump/crsdump.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index d62574b..f80936d 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -27,6 +27,7 @@ fwts_SOURCES = main.c \ > acpi/battery/battery.c \ > acpi/brightness/brightness.c \ > acpi/checksum/checksum.c \ > + acpi/crsdump/crsdump.c \ > acpi/cstates/cstates.c \ > acpi/dmar/dmar.c \ > acpi/fadt/fadt.c \ > diff --git a/src/acpi/crsdump/crsdump.c b/src/acpi/crsdump/crsdump.c > new file mode 100644 > index 0000000..d6dd6df > --- /dev/null > +++ b/src/acpi/crsdump/crsdump.c > @@ -0,0 +1,892 @@ > +/* > + * Copyright (C) 2013 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + */ > +#include "fwts.h" > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <inttypes.h> > + > +/* acpica headers */ > +#include "acpi.h" > +#include "fwts_acpi_object_eval.h" > + > +typedef struct { > + const char *label; /* Field label */ > + size_t offset; /* Offset into _CRS buffer */ > + size_t bitlength; /* Size of field in bits */ > + uint64_t bitmask; /* Bit mask, 0 = use all bits */ > + uint8_t shift; /* Value shift */ > + const char **annotation; /* Annotations */ > + const char *(*callback)(const uint64_t val); /* val -> string mapping callback for CRS_UINTX */ > +} crsdump_info; > + > +#define CRS_UINT(label, offset, bitlength) { label, offset, bitlength, 0, 0, NULL, NULL } > +#define CRS_UINT24(label, offset, bitlength) { label, offset, bitlength, 0, 8, NULL, NULL } > +#define CRS_UINTX(label, offset, bitlength, callback) { label, offset, bitlength, 0, 0, NULL, callback } > + > +#define CRS_BITS(label, offset, bitmask) { label, offset, 8, bitmask, 0, NULL, NULL } > +#define CRS_BITX(label, offset, bitmask, annotation) { label, offset, 8, bitmask, 0, annotation, NULL } > +#define CRS_END { NULL, 0, 0, 0, 0, 0, NULL } > + > +static void crsdump_show_header( > + fwts_framework *fw, > + const char *objname, > + const char *crs_name) > +{ > + fwts_log_info_verbatum(fw, "%s (%s):", objname, crs_name); > +} > + > +static void crsdump_show_info( > + fwts_framework *fw, > + const uint8_t *data, > + const size_t length, > + const crsdump_info *info) > +{ > + /* > + * Walk through fields and dump data according to the mapping > + */ > + for ( ; info->label; info++) { > + uint64_t val; > + uint64_t mask = info->bitmask; > + int hexdigits = info->bitlength >> 2; > + > + if (info->offset + (info->bitlength >> 3) > length) > + continue; > + > + if (info->bitmask) { > + /* > + * CRS_BIT*() data > + */ > + val = (uint64_t)*(uint8_t*)(data + info->offset); > + while (mask && ((mask & 1) == 0)) { > + val >>= 1; > + mask >>= 1; > + } > + val &= mask; > + hexdigits = 2; > + } else { > + /* > + * CRS_UINT*() data > + */ > + switch (info->bitlength) { > + case 8: > + val = (uint64_t)*(uint8_t*)(data + info->offset); > + break; > + case 16: > + val = (uint64_t)*(uint16_t*)(data + info->offset); > + break; > + case 32: > + val = (uint64_t)*(uint32_t*)(data + info->offset); > + break; > + case 64: > + val = (uint64_t)*(uint64_t*)(data + info->offset); > + break; > + default: > + val = ~0; > + break; > + } > + } > + val = val << info->shift; > + > + if (info->annotation) { > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64 " (%s)", > + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val, > + info->annotation[val]); > + } else if (info->callback) { > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64 " (%s)", > + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val, > + info->callback(val)); > + } else { > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64, > + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val); > + } > + } > +} > + > +static void crsdump_show( > + fwts_framework *fw, > + const char *objname, > + const char *crs_name, > + const uint8_t *data, > + const size_t length, > + const crsdump_info *header, > + const crsdump_info *info) > +{ > + crsdump_show_header(fw, objname, crs_name); > + crsdump_show_info(fw, data, length, header); > + crsdump_show_info(fw, data, length, info); > +} > + > +static void crsdump_data( > + fwts_framework *fw, > + const uint8_t *data, > + const size_t from, > + const size_t to) > +{ > + size_t i; > + char buffer[120]; > + > + for (i = from; i < to; i+= 16) { > + size_t n = to - i; > + > + fwts_dump_raw_data(buffer, sizeof(buffer), data + i, i, n > 16 ? 16 : n); > + buffer[56] = '\0'; /* Truncate off text version of hex dump */ > + > + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: %s", > + (uint16_t)i, "Hex Dump", buffer + 8); > + } > +} > + > +/* > + * crsdump_init() > + * initialize ACPI > + */ > +static int crsdump_init(fwts_framework *fw) > +{ > + if (fwts_acpi_init(fw) != FWTS_OK) { > + fwts_log_error(fw, "Cannot initialise ACPI."); > + return FWTS_ERROR; > + } > + > + return FWTS_OK; > +} > + > +/* > + * crsdump_deinit > + * de-intialize ACPI > + */ > +static int crsdump_deinit(fwts_framework *fw) > +{ > + return fwts_acpi_deinit(fw); > +} > + > +/* > + * See section 6.4.2.7 Fixed DMA Descriptor, DMA transfer width > + */ > +static const char *crs_dma_transfer_width(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "8 bit"; > + case 0x01: > + return "16 bit"; > + case 0x02: > + return "32 bit"; > + case 0x03: > + return "64 bit"; > + case 0x04: > + return "128 bit"; > + case 0x05: > + return "256 bit"; > + default: > + return "reserved"; > + } > +} > + > +/* > + * See section 6.4.3.5.2 DWord Address Space Descriptor Resource Type > + */ > +static const char *crs_resource_type(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Memory range"; > + case 0x01: > + return "I/O range"; > + case 0x02: > + return "Bus number range"; > + case 0xc0 ... 0xff: > + return "Hardware Vendor Defined"; > + default: > + return "Reserved"; > + } > +} > + > +/* > + * See section 6.4.8.1. Generic Register Descriptor Address Space ID > + */ > +static const char *crs_address_space_id(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "System Memory"; > + case 0x01: > + return "System I/O"; > + case 0x02: > + return "PCI Configuration Space"; > + case 0x03: > + return "Embedded Controller"; > + case 0x04: > + return "SMBus"; > + case 0x0a: > + return "PCC"; > + case 0x7f: > + return "Functional Fixed Hardware"; > + default: > + return "Uknown"; > + } > +} > + > +/* > + * See section 6.4.8.1. Generic Register Descriptor Address Size > + */ > +static const char *crs_address_size(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Undefined (legacy)"; > + case 0x01: > + return "Byte Access"; > + case 0x02: > + return "Word Access"; > + case 0x04: > + return "Dword Access"; > + case 0x08: > + return "Qword Access"; > + default: > + return "Unknown"; > + } > +} > + > +/* > + * See section 6.4.3.8.1 GPIO Connection Description > + */ > +static const char *crs_gpio_connection_type(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Interrupt Connection"; > + case 0x01: > + return "I/O Connection"; > + default: > + return "Reserved"; > + } > +} > + > +/* > + * See section 6.4.3.8.1 GPIO Connection Description > + */ > +static const char *crs_pin_configuration(const uint64_t val) > +{ > + switch (val) { > + case 0x00: > + return "Default Configuration"; > + case 0x01: > + return "Pull-Up"; > + case 0x02: > + return "Pull-Down"; > + case 0x03: > + return "No Pull"; > + case 0x80 ... 0xff: > + return "Vendor Defined"; > + default: > + return "Reserved"; > + } > +} > + > +/* > + * CRS small resource checks, simple checking > + */ > +static void crsdump_small_resource_items( > + fwts_framework *fw, > + const char *objname, > + const uint8_t *data, > + const size_t length) > +{ > + uint8_t tag_item = (data[0] >> 3) & 0xf; > + size_t crs_length = 1 + (data[0] & 7); > + > + static const crsdump_info header[] = { > + CRS_BITS("Tag Type", 0, 128), > + CRS_BITS("Tag Item ID", 0, 64 | 32 | 16 | 8), > + CRS_BITS("Tag Length", 0, 4 | 2 | 1), > + }; > + > + /* Ensure we just dump minimum _CRS buffer length */ > + if (crs_length > length) > + crs_length = length; > + > + switch (tag_item) { > + case 0x4: /* 6.4.2.1 IRQ Descriptor */ > + { > + static const char *sharing[] = { > + "Exclusive", > + "Shared", > + "Exclusive And Wake", > + "Shared And Wake" > + }; > + > + static const char *polarity[] = { > + "Active-High", > + "Active-Low" > + }; > + > + static const char *mode[] = { > + "Level-Triggered", > + "Edge-Triggered" > + }; > + > + static const crsdump_info info[] = { > + CRS_UINT("IRQ Mask", 1, 16), > + CRS_BITS("Reserved", 3, 128 | 64), > + CRS_BITX("Interrupt Sharing", 3, 32 | 16, sharing), > + CRS_BITX("Interrupt Polarity", 3, 8, polarity), > + CRS_BITS("Ignored", 3, 4 | 2), > + CRS_BITX("Interrupt Mode", 3, 1, mode), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "IRQ Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x5: /* 6.4.2.2 DMA Descriptor */ > + { > + static const char *dma_speed[] = { > + "Compatibility Mode", > + "Type A DMA", > + "Type B DMA", > + "Type F DMA" > + }; > + > + static const char *bus_master[] = { > + "Not a bus master", > + "Is a bus master" > + }; > + > + static const char *dma_size[] = { > + "8 bit only", > + "8 and 16 bit", > + "16 bit only", > + "Reserved" > + }; > + > + static const crsdump_info info[] = { > + CRS_UINT("DMA channel mask", 1, 16), > + CRS_BITS("Reserved", 2, 128), > + CRS_BITX("DMA channel speed", 2, 64 | 32, dma_speed), > + CRS_BITS("Ignored", 2, 16 | 8), > + CRS_BITX("Logical device bus master", 2, 4, bus_master), > + CRS_BITX("DMA transfer type preference",2, 2 | 1, dma_size), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "DMA Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x6: /* 6.4.2.3 Start Dependent Functions Descriptor */ > + { > + static const char *config[] = { > + "Good configurarion", > + "Acceptable configuration", > + "Sub-optimal configuration", > + "Reserved" > + }; > + > + static const crsdump_info info[] = { > + CRS_BITS("Reserved", 1, 0xf0), > + CRS_BITX("Performance/robustness", 1, 8 | 4, config), > + CRS_BITX("Compatibility priority", 1, 2 | 1, config), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Start Dependent Functions Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x7: /* 6.4.2.4 End Dependent Functions Descriptor */ > + crsdump_show_header(fw, objname, "End Dependent Functions Descriptor"); > + break; > + case 0x8: /* 6.4.2.5 I/O Port Descriptor */ > + { > + static const char *decodes[] = { > + "16 bit addresses", > + "10 bit addresses" > + }; > + static const crsdump_info info[] = { > + CRS_BITS("Reserved", 1, 0xfe), > + CRS_BITX("Logical Device Decode", 1, 1, decodes), > + CRS_UINT("Minimum Base Address", 2, 16), > + CRS_UINT("Maximum Base Address", 4, 16), > + CRS_UINT("Base Alignment", 6, 8), > + CRS_UINT("Range Length", 7, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "I/O Port Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x9: /* 6.4.2.6 Fixed Location I/O Port Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINT("Range Base Address", 1, 16), > + CRS_UINT("Range Length", 3, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Fixed Location I/O Port Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xa: /* 6.4.2.7 Fixed DMA Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINT("DMA Request Line", 1, 16), > + CRS_UINT("DMA Channel", 3, 16), > + CRS_UINTX("DMA Transfer Width", 5, 1, crs_dma_transfer_width), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Fixed DMA Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xe: /* 6.4.2.8 Vendor-Defined Descriptor */ > + crsdump_show_header(fw, objname, "Vendor-Defined Descriptor"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + case 0xf: /* 6.4.2.9 End Tag */ > + { > + static const crsdump_info info[] = { > + CRS_UINT("Checksum", 1, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "End Tag", > + data, crs_length, header, info); > + } > + break; > + default: > + crsdump_show_header(fw, objname, "Unknown type"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 1, crs_length); > + break; > + } > + > + fwts_log_nl(fw); > +} > + > +/* > + * CRS large resource checks, simple checking > + */ > +static void crsdump_large_resource_items( > + fwts_framework *fw, > + const char *objname, > + const uint8_t *data, > + const uint64_t length) > +{ > + uint8_t tag_item = data[0] & 0x7f; > + size_t crs_length = data[1]; > + > + static const crsdump_info header[] = { > + CRS_BITS("Tag Type", 0, 128), > + CRS_BITS("Tag Item ID", 0, 0x7f), > + CRS_UINT("Length", 1, 16), > + CRS_END > + }; > + > + static const char *write_status[] = { > + "non-writeable, read-only", > + "writeable, read/write" > + }; > + > + static const char *mifmaf[] = { > + "Not fixed", > + "Fixed" > + }; > + > + static const char *decode_type[] = { > + "Bridge Positively decodes this address", > + "Bridge Subtractively decode this address" > + }; > + > + static const char *sharing[] = { > + "Exclusive", > + "Shared", > + "Exclusive and Wake", > + "Shared and Wake" > + }; > + > + static const char *polarity[] = { > + "Active-High", > + "Active-Low", > + "Active-Both", > + "Reserved" > + }; > + > + static const char *mode[] = { > + "Level-Triggered", > + "Edge-Triggered" > + }; > + > + static const char *consumer[] = { > + "producer and consumer", > + "consumer" > + }; > + > + /* Ensure we just dump minimum _CRS buffer length */ > + if (crs_length > length) > + crs_length = length; > + > + switch (tag_item) { > + case 0x1: /* 6.4.3.1 24-Bit Memory Range Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITX("Write Status", 3, 1, write_status), > + CRS_UINT24("Range Minimum Base", 4, 16), > + CRS_UINT24("Range Maximum Base", 6, 16), > + CRS_UINT("Base Alignment", 8, 16), > + CRS_UINT("Range Length", 10, 16), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "24-Bit Memory Range Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x2: /* 6.4.3.7 Generic Register Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Address Space ID", 3, 8, crs_address_space_id), > + CRS_UINT("Address Bit Width", 4, 8), > + CRS_UINT("Register Bit Offset", 5, 8), > + CRS_UINTX("Address Size", 6, 8, crs_address_size), > + CRS_UINT("Register Address", 7, 64), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Generic Register Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x4: /* 6.4.3.2 Vendor-Defined Descriptor */ > + crsdump_show_header(fw, objname, "Vendor-Defined Descriptor"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + case 0x5: /* 6.4.3.3 32-Bit Memory Range Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITX("Write Status", 3, 1, write_status), > + CRS_UINT("Range Minimum Address", 4, 32), > + CRS_UINT("Range Maximum Address", 8, 32), > + CRS_UINT("Base Alignment", 12, 32), > + CRS_UINT("Range Length", 16, 32), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "32-Bit Memory Range Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x6: /* 6.4.3.4 32-Bit Fixed Memory Range Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITX("Write Status", 3, 1, write_status), > + CRS_UINT("Range Base Address", 4, 32), > + CRS_UINT("Range Length", 8, 32), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "32-Bit Fixed Memory Range Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x7: /* 6.4.3.5.2 DWord Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITS("Ignored", 4, 1), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Address Space Granularity", 6, 32), > + CRS_UINT("Address Range Minimum", 10, 32), > + CRS_UINT("Address Range Maximum", 14, 32), > + CRS_UINT("Address Translation Offset", 18, 32), > + CRS_UINT("Address Length", 22, 32), > + CRS_UINT("Resource Source Index", 26, 1), > + /* Skip Resource Source String */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "DWord Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x8: /* 6.4.3.5.3 Word Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITS("Ignored", 4, 1), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Address Space Granularity", 6, 16), > + CRS_UINT("Address Range Minimum", 8, 16), > + CRS_UINT("Address Range Maximum", 10, 16), > + CRS_UINT("Address Translation Offset", 12, 16), > + CRS_UINT("Address Length", 14, 16), > + CRS_UINT("Resource Source Index", 16, 1), > + /* Skip Resource Source String */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Word Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0x9: /* 6.4.3.6 Extended Interrupt Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_BITS("Reserved", 3, 128 | 64 | 32), > + CRS_BITX("Interrupt Sharing", 3, 16 | 8, sharing), > + CRS_BITX("Interrupt Polarity", 7, 4, polarity), > + CRS_BITX("Interrupt Mode", 7, 2, mode), > + CRS_BITX("Interrupt Consumer/Producer", 7, 1, consumer), > + CRS_UINT("Interrupt Table Length", 8, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Extended Interrupt Descriptor", > + data, crs_length, header, info); > + crsdump_data(fw, data, 5, crs_length); > + } > + break; > + case 0xa: /* 6.4.3.5.1 QWord Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITS("Ignored", 4, 1), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Address Space Granularity", 6, 64), > + CRS_UINT("Address Range Minimum", 14, 64), > + CRS_UINT("Address Range Maximum", 22, 64), > + CRS_UINT("Address Translation Offset", 30, 64), > + CRS_UINT("Address Length", 38, 64), > + CRS_UINT("Resource Source Index", 46, 1), > + /* Skip Resource Source String */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "QWord Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xb: /* 6.4.3.5.4 Extended Address Space Descriptor */ > + { > + static const crsdump_info info[] = { > + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), > + CRS_BITS("Reserved", 4, 0xf0), > + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), > + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), > + CRS_BITX("Decode Type", 4, 2, decode_type), > + CRS_BITX("Consumer/Producer", 4, 1, consumer), > + CRS_UINT("Type Specific Flags", 5, 8), > + CRS_UINT("Revision ID", 6, 8), > + CRS_UINT("Reserved", 7, 8), > + CRS_UINT("Address Space Granularity", 8, 64), > + CRS_UINT("Address Range Minimum", 16, 64), > + CRS_UINT("Address Range Maximum", 24, 64), > + CRS_UINT("Address Translation Offset", 32, 64), > + CRS_UINT("Address Length", 40, 64), > + CRS_UINT("Type Specific Attribute", 48, 64), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "Extended Address Space Descriptor", > + data, crs_length, header, info); > + } > + break; > + case 0xc: /* 6.4.3.8.1 GPIO Connection Descriptor */ > + { > + if (crs_length < 4) > + break; > + > + if (data[4] == 0) { > + > + /* Interrupt connection */ > + static const crsdump_info info[] = { > + CRS_UINT("Revision ID", 3, 8), > + CRS_UINTX("GPIO Connection Type", 4, 8, crs_gpio_connection_type), > + CRS_BITS("Reserved", 5, 0xfe), > + CRS_BITX("Consumer/Producer", 5, 1, consumer), > + CRS_UINT("Reserved", 6, 8), > + CRS_BITS("Reserved", 7, 128 | 64 | 32), > + CRS_BITX("Interrupt Sharing and Wake", 7, 16 | 8, sharing), > + CRS_BITX("Interrupt Polarity", 7, 4 | 2, polarity), > + CRS_BITX("Interrupt Mode", 7, 1, mode), > + CRS_UINT("Interrupt and I/O Flags", 8, 8), > + CRS_UINTX("Pin Configuration", 9, 8, crs_pin_configuration), > + CRS_UINT("Output Driver Strength", 10, 16), > + CRS_UINT("Debounce Timeout (bits)", 12, 16), > + CRS_UINT("Pin Table Offset", 14, 16), > + CRS_UINT("Resource Source Index", 16, 8), > + CRS_UINT("Resource Name Offset", 17, 16), > + CRS_UINT("Vendor Data Offset", 19, 16), > + CRS_UINT("Vendor Data Length", 21, 16), > + /* Skip pin table */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "GPIO Connection Descriptor", > + data, crs_length, header, info); > + } else if (data[4] == 1) { > + static const char *sharing[] = { > + "Exclusive", > + "Shared", > + }; > + > + static const char *restriction[] = { > + "Input or Output", > + "Input Only", > + "Output Only", > + "Input or Ouput, config must be preserved" > + }; > + > + /* I/O connection */ > + static const crsdump_info info[] = { > + CRS_UINT("Revision ID", 3, 8), > + CRS_UINTX("GPIO Connection Type", 4, 8, crs_gpio_connection_type), > + CRS_BITS("Reserved", 5, 0xfe), > + CRS_BITX("Consumer/Producer", 5, 1, consumer), > + CRS_UINT("Reserved", 6, 8), > + CRS_BITS("Reserved", 7, 128 | 64 | 32 | 16), > + CRS_BITX("Interrupt Sharing", 7, 8, sharing), > + CRS_BITS("Reserved", 7, 4), > + CRS_BITX("I/O Restriction", 7, 2 | 1, restriction), > + CRS_UINT("Interrupt and I/O Flags", 8, 8), > + CRS_UINTX("Pin Configuration", 9, 8, crs_pin_configuration), > + CRS_UINT("Output Driver Strength", 10, 16), > + CRS_UINT("Debounce Timeout (bits)", 12, 16), > + CRS_UINT("Pin Table Offset", 14, 16), > + CRS_UINT("Resource Source Index", 16, 8), > + CRS_UINT("Resource Name Offset", 17, 16), > + CRS_UINT("Vendor Data Offset", 19, 16), > + CRS_UINT("Vendor Data Length", 21, 16), > + /* Skip pin table */ > + CRS_END > + }; > + > + crsdump_show(fw, objname, "GPIO Connection Descriptor", > + data, crs_length, header, info); > + } else { > + /* No idea of the connection type */ > + static const crsdump_info info[] = { > + CRS_UINT("Revision ID", 3, 8), > + CRS_UINT("GPIO Connection Type", 4, 8), > + CRS_END > + }; > + > + crsdump_show(fw, objname, "GPIO Connection Descriptor", > + data, crs_length, header, info); > + } > + > + } > + break; > + case 0xe: /* 6.4.3.8.2 Serial Bus Connection Descriptors */ > + /* This is not frequently used, deferring implementation to later */ > + crsdump_show_header(fw, objname, "Serial Bus Connection Descriptor"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + default: > + crsdump_show_header(fw, objname, "Unknown type"); > + crsdump_show_info(fw, data, crs_length, header); > + crsdump_data(fw, data, 3, crs_length); > + break; > + } > + > + fwts_log_nl(fw); > +} > + > +static int crsdump_test1(fwts_framework *fw) > +{ > + fwts_list_link *item; > + fwts_list *objects; > + const size_t name_len = 4; > + > + if ((objects = fwts_acpi_object_get_names()) == NULL) { > + fwts_log_info(fw, "Cannot find any ACPI objects"); > + return FWTS_ERROR; > + } > + > + fwts_list_foreach(item, objects) { > + char *name = fwts_list_data(char*, item); > + const size_t len = strlen(name); > + if (strncmp("_CRS", name + len - name_len, name_len) == 0) { > + ACPI_OBJECT_LIST arg_list; > + ACPI_BUFFER buf; > + ACPI_OBJECT *obj; > + uint8_t *data; > + int ret; > + > + arg_list.Count = 0; > + arg_list.Pointer = NULL; > + > + ret = fwts_acpi_object_evaluate(fw, name, &arg_list, &buf); > + if ((ACPI_FAILURE(ret) != AE_OK) || (buf.Pointer == NULL)) > + continue; > + > + /* Do we have a valid _CRS buffer to dump? */ > + obj = buf.Pointer; > + if ((obj->Type == ACPI_TYPE_BUFFER) && > + (obj->Buffer.Pointer != NULL) && > + (obj->Buffer.Length > 0)) { > + data = (uint8_t*)obj->Buffer.Pointer; > + > + if (data[0] & 128) > + crsdump_large_resource_items(fw, name, data, obj->Buffer.Length); > + else > + crsdump_small_resource_items(fw, name, data, obj->Buffer.Length); > + } > + > + if (buf.Length && buf.Pointer) > + free(buf.Pointer); > + } > + } > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test crsdump_tests[] = { > + { crsdump_test1, "Dump ACPI _CRS buffers." }, > + { NULL, NULL } > +}; > + > +static fwts_framework_ops crsdump_ops = { > + .description = "Dump ACPI _CRS buffers.", > + .init = crsdump_init, > + .deinit = crsdump_deinit, > + .minor_tests = crsdump_tests > +}; > + > +FWTS_REGISTER("crsdump", &crsdump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS); > -- > 1.8.0 > Acked-by: Keng-Yu Lin <kengyu@canonical.com>
diff --git a/src/Makefile.am b/src/Makefile.am index d62574b..f80936d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ fwts_SOURCES = main.c \ acpi/battery/battery.c \ acpi/brightness/brightness.c \ acpi/checksum/checksum.c \ + acpi/crsdump/crsdump.c \ acpi/cstates/cstates.c \ acpi/dmar/dmar.c \ acpi/fadt/fadt.c \ diff --git a/src/acpi/crsdump/crsdump.c b/src/acpi/crsdump/crsdump.c new file mode 100644 index 0000000..d6dd6df --- /dev/null +++ b/src/acpi/crsdump/crsdump.c @@ -0,0 +1,892 @@ +/* + * Copyright (C) 2013 Canonical + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "fwts.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> + +/* acpica headers */ +#include "acpi.h" +#include "fwts_acpi_object_eval.h" + +typedef struct { + const char *label; /* Field label */ + size_t offset; /* Offset into _CRS buffer */ + size_t bitlength; /* Size of field in bits */ + uint64_t bitmask; /* Bit mask, 0 = use all bits */ + uint8_t shift; /* Value shift */ + const char **annotation; /* Annotations */ + const char *(*callback)(const uint64_t val); /* val -> string mapping callback for CRS_UINTX */ +} crsdump_info; + +#define CRS_UINT(label, offset, bitlength) { label, offset, bitlength, 0, 0, NULL, NULL } +#define CRS_UINT24(label, offset, bitlength) { label, offset, bitlength, 0, 8, NULL, NULL } +#define CRS_UINTX(label, offset, bitlength, callback) { label, offset, bitlength, 0, 0, NULL, callback } + +#define CRS_BITS(label, offset, bitmask) { label, offset, 8, bitmask, 0, NULL, NULL } +#define CRS_BITX(label, offset, bitmask, annotation) { label, offset, 8, bitmask, 0, annotation, NULL } +#define CRS_END { NULL, 0, 0, 0, 0, 0, NULL } + +static void crsdump_show_header( + fwts_framework *fw, + const char *objname, + const char *crs_name) +{ + fwts_log_info_verbatum(fw, "%s (%s):", objname, crs_name); +} + +static void crsdump_show_info( + fwts_framework *fw, + const uint8_t *data, + const size_t length, + const crsdump_info *info) +{ + /* + * Walk through fields and dump data according to the mapping + */ + for ( ; info->label; info++) { + uint64_t val; + uint64_t mask = info->bitmask; + int hexdigits = info->bitlength >> 2; + + if (info->offset + (info->bitlength >> 3) > length) + continue; + + if (info->bitmask) { + /* + * CRS_BIT*() data + */ + val = (uint64_t)*(uint8_t*)(data + info->offset); + while (mask && ((mask & 1) == 0)) { + val >>= 1; + mask >>= 1; + } + val &= mask; + hexdigits = 2; + } else { + /* + * CRS_UINT*() data + */ + switch (info->bitlength) { + case 8: + val = (uint64_t)*(uint8_t*)(data + info->offset); + break; + case 16: + val = (uint64_t)*(uint16_t*)(data + info->offset); + break; + case 32: + val = (uint64_t)*(uint32_t*)(data + info->offset); + break; + case 64: + val = (uint64_t)*(uint64_t*)(data + info->offset); + break; + default: + val = ~0; + break; + } + } + val = val << info->shift; + + if (info->annotation) { + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64 " (%s)", + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val, + info->annotation[val]); + } else if (info->callback) { + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64 " (%s)", + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val, + info->callback(val)); + } else { + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: 0x%-*.*" PRIx64, + (uint16_t)info->offset, info->label, hexdigits, hexdigits, val); + } + } +} + +static void crsdump_show( + fwts_framework *fw, + const char *objname, + const char *crs_name, + const uint8_t *data, + const size_t length, + const crsdump_info *header, + const crsdump_info *info) +{ + crsdump_show_header(fw, objname, crs_name); + crsdump_show_info(fw, data, length, header); + crsdump_show_info(fw, data, length, info); +} + +static void crsdump_data( + fwts_framework *fw, + const uint8_t *data, + const size_t from, + const size_t to) +{ + size_t i; + char buffer[120]; + + for (i = from; i < to; i+= 16) { + size_t n = to - i; + + fwts_dump_raw_data(buffer, sizeof(buffer), data + i, i, n > 16 ? 16 : n); + buffer[56] = '\0'; /* Truncate off text version of hex dump */ + + fwts_log_info_verbatum(fw, " 0x%4.4" PRIx16 ": %-30.30s: %s", + (uint16_t)i, "Hex Dump", buffer + 8); + } +} + +/* + * crsdump_init() + * initialize ACPI + */ +static int crsdump_init(fwts_framework *fw) +{ + if (fwts_acpi_init(fw) != FWTS_OK) { + fwts_log_error(fw, "Cannot initialise ACPI."); + return FWTS_ERROR; + } + + return FWTS_OK; +} + +/* + * crsdump_deinit + * de-intialize ACPI + */ +static int crsdump_deinit(fwts_framework *fw) +{ + return fwts_acpi_deinit(fw); +} + +/* + * See section 6.4.2.7 Fixed DMA Descriptor, DMA transfer width + */ +static const char *crs_dma_transfer_width(const uint64_t val) +{ + switch (val) { + case 0x00: + return "8 bit"; + case 0x01: + return "16 bit"; + case 0x02: + return "32 bit"; + case 0x03: + return "64 bit"; + case 0x04: + return "128 bit"; + case 0x05: + return "256 bit"; + default: + return "reserved"; + } +} + +/* + * See section 6.4.3.5.2 DWord Address Space Descriptor Resource Type + */ +static const char *crs_resource_type(const uint64_t val) +{ + switch (val) { + case 0x00: + return "Memory range"; + case 0x01: + return "I/O range"; + case 0x02: + return "Bus number range"; + case 0xc0 ... 0xff: + return "Hardware Vendor Defined"; + default: + return "Reserved"; + } +} + +/* + * See section 6.4.8.1. Generic Register Descriptor Address Space ID + */ +static const char *crs_address_space_id(const uint64_t val) +{ + switch (val) { + case 0x00: + return "System Memory"; + case 0x01: + return "System I/O"; + case 0x02: + return "PCI Configuration Space"; + case 0x03: + return "Embedded Controller"; + case 0x04: + return "SMBus"; + case 0x0a: + return "PCC"; + case 0x7f: + return "Functional Fixed Hardware"; + default: + return "Uknown"; + } +} + +/* + * See section 6.4.8.1. Generic Register Descriptor Address Size + */ +static const char *crs_address_size(const uint64_t val) +{ + switch (val) { + case 0x00: + return "Undefined (legacy)"; + case 0x01: + return "Byte Access"; + case 0x02: + return "Word Access"; + case 0x04: + return "Dword Access"; + case 0x08: + return "Qword Access"; + default: + return "Unknown"; + } +} + +/* + * See section 6.4.3.8.1 GPIO Connection Description + */ +static const char *crs_gpio_connection_type(const uint64_t val) +{ + switch (val) { + case 0x00: + return "Interrupt Connection"; + case 0x01: + return "I/O Connection"; + default: + return "Reserved"; + } +} + +/* + * See section 6.4.3.8.1 GPIO Connection Description + */ +static const char *crs_pin_configuration(const uint64_t val) +{ + switch (val) { + case 0x00: + return "Default Configuration"; + case 0x01: + return "Pull-Up"; + case 0x02: + return "Pull-Down"; + case 0x03: + return "No Pull"; + case 0x80 ... 0xff: + return "Vendor Defined"; + default: + return "Reserved"; + } +} + +/* + * CRS small resource checks, simple checking + */ +static void crsdump_small_resource_items( + fwts_framework *fw, + const char *objname, + const uint8_t *data, + const size_t length) +{ + uint8_t tag_item = (data[0] >> 3) & 0xf; + size_t crs_length = 1 + (data[0] & 7); + + static const crsdump_info header[] = { + CRS_BITS("Tag Type", 0, 128), + CRS_BITS("Tag Item ID", 0, 64 | 32 | 16 | 8), + CRS_BITS("Tag Length", 0, 4 | 2 | 1), + }; + + /* Ensure we just dump minimum _CRS buffer length */ + if (crs_length > length) + crs_length = length; + + switch (tag_item) { + case 0x4: /* 6.4.2.1 IRQ Descriptor */ + { + static const char *sharing[] = { + "Exclusive", + "Shared", + "Exclusive And Wake", + "Shared And Wake" + }; + + static const char *polarity[] = { + "Active-High", + "Active-Low" + }; + + static const char *mode[] = { + "Level-Triggered", + "Edge-Triggered" + }; + + static const crsdump_info info[] = { + CRS_UINT("IRQ Mask", 1, 16), + CRS_BITS("Reserved", 3, 128 | 64), + CRS_BITX("Interrupt Sharing", 3, 32 | 16, sharing), + CRS_BITX("Interrupt Polarity", 3, 8, polarity), + CRS_BITS("Ignored", 3, 4 | 2), + CRS_BITX("Interrupt Mode", 3, 1, mode), + CRS_END + }; + + crsdump_show(fw, objname, "IRQ Descriptor", + data, crs_length, header, info); + } + break; + case 0x5: /* 6.4.2.2 DMA Descriptor */ + { + static const char *dma_speed[] = { + "Compatibility Mode", + "Type A DMA", + "Type B DMA", + "Type F DMA" + }; + + static const char *bus_master[] = { + "Not a bus master", + "Is a bus master" + }; + + static const char *dma_size[] = { + "8 bit only", + "8 and 16 bit", + "16 bit only", + "Reserved" + }; + + static const crsdump_info info[] = { + CRS_UINT("DMA channel mask", 1, 16), + CRS_BITS("Reserved", 2, 128), + CRS_BITX("DMA channel speed", 2, 64 | 32, dma_speed), + CRS_BITS("Ignored", 2, 16 | 8), + CRS_BITX("Logical device bus master", 2, 4, bus_master), + CRS_BITX("DMA transfer type preference",2, 2 | 1, dma_size), + CRS_END + }; + + crsdump_show(fw, objname, "DMA Descriptor", + data, crs_length, header, info); + } + break; + case 0x6: /* 6.4.2.3 Start Dependent Functions Descriptor */ + { + static const char *config[] = { + "Good configurarion", + "Acceptable configuration", + "Sub-optimal configuration", + "Reserved" + }; + + static const crsdump_info info[] = { + CRS_BITS("Reserved", 1, 0xf0), + CRS_BITX("Performance/robustness", 1, 8 | 4, config), + CRS_BITX("Compatibility priority", 1, 2 | 1, config), + CRS_END + }; + + crsdump_show(fw, objname, "Start Dependent Functions Descriptor", + data, crs_length, header, info); + } + break; + case 0x7: /* 6.4.2.4 End Dependent Functions Descriptor */ + crsdump_show_header(fw, objname, "End Dependent Functions Descriptor"); + break; + case 0x8: /* 6.4.2.5 I/O Port Descriptor */ + { + static const char *decodes[] = { + "16 bit addresses", + "10 bit addresses" + }; + static const crsdump_info info[] = { + CRS_BITS("Reserved", 1, 0xfe), + CRS_BITX("Logical Device Decode", 1, 1, decodes), + CRS_UINT("Minimum Base Address", 2, 16), + CRS_UINT("Maximum Base Address", 4, 16), + CRS_UINT("Base Alignment", 6, 8), + CRS_UINT("Range Length", 7, 8), + CRS_END + }; + + crsdump_show(fw, objname, "I/O Port Descriptor", + data, crs_length, header, info); + } + break; + case 0x9: /* 6.4.2.6 Fixed Location I/O Port Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINT("Range Base Address", 1, 16), + CRS_UINT("Range Length", 3, 8), + CRS_END + }; + + crsdump_show(fw, objname, "Fixed Location I/O Port Descriptor", + data, crs_length, header, info); + } + break; + case 0xa: /* 6.4.2.7 Fixed DMA Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINT("DMA Request Line", 1, 16), + CRS_UINT("DMA Channel", 3, 16), + CRS_UINTX("DMA Transfer Width", 5, 1, crs_dma_transfer_width), + CRS_END + }; + + crsdump_show(fw, objname, "Fixed DMA Descriptor", + data, crs_length, header, info); + } + break; + case 0xe: /* 6.4.2.8 Vendor-Defined Descriptor */ + crsdump_show_header(fw, objname, "Vendor-Defined Descriptor"); + crsdump_show_info(fw, data, crs_length, header); + crsdump_data(fw, data, 3, crs_length); + break; + case 0xf: /* 6.4.2.9 End Tag */ + { + static const crsdump_info info[] = { + CRS_UINT("Checksum", 1, 8), + CRS_END + }; + + crsdump_show(fw, objname, "End Tag", + data, crs_length, header, info); + } + break; + default: + crsdump_show_header(fw, objname, "Unknown type"); + crsdump_show_info(fw, data, crs_length, header); + crsdump_data(fw, data, 1, crs_length); + break; + } + + fwts_log_nl(fw); +} + +/* + * CRS large resource checks, simple checking + */ +static void crsdump_large_resource_items( + fwts_framework *fw, + const char *objname, + const uint8_t *data, + const uint64_t length) +{ + uint8_t tag_item = data[0] & 0x7f; + size_t crs_length = data[1]; + + static const crsdump_info header[] = { + CRS_BITS("Tag Type", 0, 128), + CRS_BITS("Tag Item ID", 0, 0x7f), + CRS_UINT("Length", 1, 16), + CRS_END + }; + + static const char *write_status[] = { + "non-writeable, read-only", + "writeable, read/write" + }; + + static const char *mifmaf[] = { + "Not fixed", + "Fixed" + }; + + static const char *decode_type[] = { + "Bridge Positively decodes this address", + "Bridge Subtractively decode this address" + }; + + static const char *sharing[] = { + "Exclusive", + "Shared", + "Exclusive and Wake", + "Shared and Wake" + }; + + static const char *polarity[] = { + "Active-High", + "Active-Low", + "Active-Both", + "Reserved" + }; + + static const char *mode[] = { + "Level-Triggered", + "Edge-Triggered" + }; + + static const char *consumer[] = { + "producer and consumer", + "consumer" + }; + + /* Ensure we just dump minimum _CRS buffer length */ + if (crs_length > length) + crs_length = length; + + switch (tag_item) { + case 0x1: /* 6.4.3.1 24-Bit Memory Range Descriptor */ + { + static const crsdump_info info[] = { + CRS_BITX("Write Status", 3, 1, write_status), + CRS_UINT24("Range Minimum Base", 4, 16), + CRS_UINT24("Range Maximum Base", 6, 16), + CRS_UINT("Base Alignment", 8, 16), + CRS_UINT("Range Length", 10, 16), + CRS_END + }; + + crsdump_show(fw, objname, "24-Bit Memory Range Descriptor", + data, crs_length, header, info); + } + break; + case 0x2: /* 6.4.3.7 Generic Register Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINTX("Address Space ID", 3, 8, crs_address_space_id), + CRS_UINT("Address Bit Width", 4, 8), + CRS_UINT("Register Bit Offset", 5, 8), + CRS_UINTX("Address Size", 6, 8, crs_address_size), + CRS_UINT("Register Address", 7, 64), + CRS_END + }; + + crsdump_show(fw, objname, "Generic Register Descriptor", + data, crs_length, header, info); + } + break; + case 0x4: /* 6.4.3.2 Vendor-Defined Descriptor */ + crsdump_show_header(fw, objname, "Vendor-Defined Descriptor"); + crsdump_show_info(fw, data, crs_length, header); + crsdump_data(fw, data, 3, crs_length); + break; + case 0x5: /* 6.4.3.3 32-Bit Memory Range Descriptor */ + { + static const crsdump_info info[] = { + CRS_BITX("Write Status", 3, 1, write_status), + CRS_UINT("Range Minimum Address", 4, 32), + CRS_UINT("Range Maximum Address", 8, 32), + CRS_UINT("Base Alignment", 12, 32), + CRS_UINT("Range Length", 16, 32), + CRS_END + }; + + crsdump_show(fw, objname, "32-Bit Memory Range Descriptor", + data, crs_length, header, info); + } + break; + case 0x6: /* 6.4.3.4 32-Bit Fixed Memory Range Descriptor */ + { + static const crsdump_info info[] = { + CRS_BITX("Write Status", 3, 1, write_status), + CRS_UINT("Range Base Address", 4, 32), + CRS_UINT("Range Length", 8, 32), + CRS_END + }; + + crsdump_show(fw, objname, "32-Bit Fixed Memory Range Descriptor", + data, crs_length, header, info); + } + break; + case 0x7: /* 6.4.3.5.2 DWord Address Space Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), + CRS_BITS("Reserved", 4, 0xf0), + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), + CRS_BITX("Decode Type", 4, 2, decode_type), + CRS_BITS("Ignored", 4, 1), + CRS_UINT("Type Specific Flags", 5, 8), + CRS_UINT("Address Space Granularity", 6, 32), + CRS_UINT("Address Range Minimum", 10, 32), + CRS_UINT("Address Range Maximum", 14, 32), + CRS_UINT("Address Translation Offset", 18, 32), + CRS_UINT("Address Length", 22, 32), + CRS_UINT("Resource Source Index", 26, 1), + /* Skip Resource Source String */ + CRS_END + }; + + crsdump_show(fw, objname, "DWord Address Space Descriptor", + data, crs_length, header, info); + } + break; + case 0x8: /* 6.4.3.5.3 Word Address Space Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), + CRS_BITS("Reserved", 4, 0xf0), + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), + CRS_BITX("Decode Type", 4, 2, decode_type), + CRS_BITS("Ignored", 4, 1), + CRS_UINT("Type Specific Flags", 5, 8), + CRS_UINT("Address Space Granularity", 6, 16), + CRS_UINT("Address Range Minimum", 8, 16), + CRS_UINT("Address Range Maximum", 10, 16), + CRS_UINT("Address Translation Offset", 12, 16), + CRS_UINT("Address Length", 14, 16), + CRS_UINT("Resource Source Index", 16, 1), + /* Skip Resource Source String */ + CRS_END + }; + + crsdump_show(fw, objname, "Word Address Space Descriptor", + data, crs_length, header, info); + } + break; + case 0x9: /* 6.4.3.6 Extended Interrupt Descriptor */ + { + static const crsdump_info info[] = { + CRS_BITS("Reserved", 3, 128 | 64 | 32), + CRS_BITX("Interrupt Sharing", 3, 16 | 8, sharing), + CRS_BITX("Interrupt Polarity", 7, 4, polarity), + CRS_BITX("Interrupt Mode", 7, 2, mode), + CRS_BITX("Interrupt Consumer/Producer", 7, 1, consumer), + CRS_UINT("Interrupt Table Length", 8, 8), + CRS_END + }; + + crsdump_show(fw, objname, "Extended Interrupt Descriptor", + data, crs_length, header, info); + crsdump_data(fw, data, 5, crs_length); + } + break; + case 0xa: /* 6.4.3.5.1 QWord Address Space Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), + CRS_BITS("Reserved", 4, 0xf0), + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), + CRS_BITX("Decode Type", 4, 2, decode_type), + CRS_BITS("Ignored", 4, 1), + CRS_UINT("Type Specific Flags", 5, 8), + CRS_UINT("Address Space Granularity", 6, 64), + CRS_UINT("Address Range Minimum", 14, 64), + CRS_UINT("Address Range Maximum", 22, 64), + CRS_UINT("Address Translation Offset", 30, 64), + CRS_UINT("Address Length", 38, 64), + CRS_UINT("Resource Source Index", 46, 1), + /* Skip Resource Source String */ + CRS_END + }; + + crsdump_show(fw, objname, "QWord Address Space Descriptor", + data, crs_length, header, info); + } + break; + case 0xb: /* 6.4.3.5.4 Extended Address Space Descriptor */ + { + static const crsdump_info info[] = { + CRS_UINTX("Resource Type", 3, 8, crs_resource_type), + CRS_BITS("Reserved", 4, 0xf0), + CRS_BITX("Max Address Fixed", 4, 8, mifmaf), + CRS_BITX("Min Address Fixed", 4, 4, mifmaf), + CRS_BITX("Decode Type", 4, 2, decode_type), + CRS_BITX("Consumer/Producer", 4, 1, consumer), + CRS_UINT("Type Specific Flags", 5, 8), + CRS_UINT("Revision ID", 6, 8), + CRS_UINT("Reserved", 7, 8), + CRS_UINT("Address Space Granularity", 8, 64), + CRS_UINT("Address Range Minimum", 16, 64), + CRS_UINT("Address Range Maximum", 24, 64), + CRS_UINT("Address Translation Offset", 32, 64), + CRS_UINT("Address Length", 40, 64), + CRS_UINT("Type Specific Attribute", 48, 64), + CRS_END + }; + + crsdump_show(fw, objname, "Extended Address Space Descriptor", + data, crs_length, header, info); + } + break; + case 0xc: /* 6.4.3.8.1 GPIO Connection Descriptor */ + { + if (crs_length < 4) + break; + + if (data[4] == 0) { + + /* Interrupt connection */ + static const crsdump_info info[] = { + CRS_UINT("Revision ID", 3, 8), + CRS_UINTX("GPIO Connection Type", 4, 8, crs_gpio_connection_type), + CRS_BITS("Reserved", 5, 0xfe), + CRS_BITX("Consumer/Producer", 5, 1, consumer), + CRS_UINT("Reserved", 6, 8), + CRS_BITS("Reserved", 7, 128 | 64 | 32), + CRS_BITX("Interrupt Sharing and Wake", 7, 16 | 8, sharing), + CRS_BITX("Interrupt Polarity", 7, 4 | 2, polarity), + CRS_BITX("Interrupt Mode", 7, 1, mode), + CRS_UINT("Interrupt and I/O Flags", 8, 8), + CRS_UINTX("Pin Configuration", 9, 8, crs_pin_configuration), + CRS_UINT("Output Driver Strength", 10, 16), + CRS_UINT("Debounce Timeout (bits)", 12, 16), + CRS_UINT("Pin Table Offset", 14, 16), + CRS_UINT("Resource Source Index", 16, 8), + CRS_UINT("Resource Name Offset", 17, 16), + CRS_UINT("Vendor Data Offset", 19, 16), + CRS_UINT("Vendor Data Length", 21, 16), + /* Skip pin table */ + CRS_END + }; + + crsdump_show(fw, objname, "GPIO Connection Descriptor", + data, crs_length, header, info); + } else if (data[4] == 1) { + static const char *sharing[] = { + "Exclusive", + "Shared", + }; + + static const char *restriction[] = { + "Input or Output", + "Input Only", + "Output Only", + "Input or Ouput, config must be preserved" + }; + + /* I/O connection */ + static const crsdump_info info[] = { + CRS_UINT("Revision ID", 3, 8), + CRS_UINTX("GPIO Connection Type", 4, 8, crs_gpio_connection_type), + CRS_BITS("Reserved", 5, 0xfe), + CRS_BITX("Consumer/Producer", 5, 1, consumer), + CRS_UINT("Reserved", 6, 8), + CRS_BITS("Reserved", 7, 128 | 64 | 32 | 16), + CRS_BITX("Interrupt Sharing", 7, 8, sharing), + CRS_BITS("Reserved", 7, 4), + CRS_BITX("I/O Restriction", 7, 2 | 1, restriction), + CRS_UINT("Interrupt and I/O Flags", 8, 8), + CRS_UINTX("Pin Configuration", 9, 8, crs_pin_configuration), + CRS_UINT("Output Driver Strength", 10, 16), + CRS_UINT("Debounce Timeout (bits)", 12, 16), + CRS_UINT("Pin Table Offset", 14, 16), + CRS_UINT("Resource Source Index", 16, 8), + CRS_UINT("Resource Name Offset", 17, 16), + CRS_UINT("Vendor Data Offset", 19, 16), + CRS_UINT("Vendor Data Length", 21, 16), + /* Skip pin table */ + CRS_END + }; + + crsdump_show(fw, objname, "GPIO Connection Descriptor", + data, crs_length, header, info); + } else { + /* No idea of the connection type */ + static const crsdump_info info[] = { + CRS_UINT("Revision ID", 3, 8), + CRS_UINT("GPIO Connection Type", 4, 8), + CRS_END + }; + + crsdump_show(fw, objname, "GPIO Connection Descriptor", + data, crs_length, header, info); + } + + } + break; + case 0xe: /* 6.4.3.8.2 Serial Bus Connection Descriptors */ + /* This is not frequently used, deferring implementation to later */ + crsdump_show_header(fw, objname, "Serial Bus Connection Descriptor"); + crsdump_show_info(fw, data, crs_length, header); + crsdump_data(fw, data, 3, crs_length); + break; + default: + crsdump_show_header(fw, objname, "Unknown type"); + crsdump_show_info(fw, data, crs_length, header); + crsdump_data(fw, data, 3, crs_length); + break; + } + + fwts_log_nl(fw); +} + +static int crsdump_test1(fwts_framework *fw) +{ + fwts_list_link *item; + fwts_list *objects; + const size_t name_len = 4; + + if ((objects = fwts_acpi_object_get_names()) == NULL) { + fwts_log_info(fw, "Cannot find any ACPI objects"); + return FWTS_ERROR; + } + + fwts_list_foreach(item, objects) { + char *name = fwts_list_data(char*, item); + const size_t len = strlen(name); + if (strncmp("_CRS", name + len - name_len, name_len) == 0) { + ACPI_OBJECT_LIST arg_list; + ACPI_BUFFER buf; + ACPI_OBJECT *obj; + uint8_t *data; + int ret; + + arg_list.Count = 0; + arg_list.Pointer = NULL; + + ret = fwts_acpi_object_evaluate(fw, name, &arg_list, &buf); + if ((ACPI_FAILURE(ret) != AE_OK) || (buf.Pointer == NULL)) + continue; + + /* Do we have a valid _CRS buffer to dump? */ + obj = buf.Pointer; + if ((obj->Type == ACPI_TYPE_BUFFER) && + (obj->Buffer.Pointer != NULL) && + (obj->Buffer.Length > 0)) { + data = (uint8_t*)obj->Buffer.Pointer; + + if (data[0] & 128) + crsdump_large_resource_items(fw, name, data, obj->Buffer.Length); + else + crsdump_small_resource_items(fw, name, data, obj->Buffer.Length); + } + + if (buf.Length && buf.Pointer) + free(buf.Pointer); + } + } + return FWTS_OK; +} + +static fwts_framework_minor_test crsdump_tests[] = { + { crsdump_test1, "Dump ACPI _CRS buffers." }, + { NULL, NULL } +}; + +static fwts_framework_ops crsdump_ops = { + .description = "Dump ACPI _CRS buffers.", + .init = crsdump_init, + .deinit = crsdump_deinit, + .minor_tests = crsdump_tests +}; + +FWTS_REGISTER("crsdump", &crsdump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS);