Message ID | 1438343301-20431-2-git-send-email-colin.king@canonical.com |
---|---|
State | Accepted |
Headers | show |
On 2015年07月31日 19:48, Colin King wrote: > From: Colin Ian King <colin.king@canonical.com> > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > src/Makefile.am | 1 + > src/acpi/wdat/wdat.c | 227 ++++++++++++++++++++++++++++++++++++++++++++ > src/lib/include/fwts_acpi.h | 30 ++++++ > 3 files changed, 258 insertions(+) > create mode 100644 src/acpi/wdat/wdat.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index 11aa37b..9c8e35e 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -86,6 +86,7 @@ fwts_SOURCES = main.c \ > acpi/uefi/uefi.c \ > acpi/waet/waet.c \ > acpi/wakealarm/wakealarm.c \ > + acpi/wdat/wdat.c \ > acpi/wmi/wmi.c \ > acpi/xsdt/xsdt.c \ > acpi/xenv/xenv.c \ > diff --git a/src/acpi/wdat/wdat.c b/src/acpi/wdat/wdat.c > new file mode 100644 > index 0000000..fdea850 > --- /dev/null > +++ b/src/acpi/wdat/wdat.c > @@ -0,0 +1,227 @@ > +/* > + * Copyright (C) 2015 Canonical > + * > + * Portions of this code original from the Linux-ready Firmware Developer Kit > + * > + * 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 <stdlib.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <inttypes.h> > +#include <string.h> > + > +#include "fwts_acpi_object_eval.h" > + > +#define ACPI_DUMP (0) /* WDAT entries are long, so don't dump, too verbose */ > + > +static fwts_acpi_table_info *table; > + > +static int wdat_init(fwts_framework *fw) > +{ > + > + if (fwts_acpi_find_table(fw, "WDAT", 0, &table) != FWTS_OK) { > + fwts_log_error(fw, "Cannot read ACPI tables."); > + return FWTS_ERROR; > + } > + if (table == NULL || (table && table->length == 0)) { > + fwts_log_error(fw, "ACPI WDAT table does not exist, skipping test"); > + return FWTS_SKIP; > + } > + > + return FWTS_OK; > +} > + > +/* > + * ACPI WDAT (Watchdog Action Table) > + * https://msdn.microsoft.com/en-us/windows/hardware/gg463320.aspx > + */ > +static int wdat_test1(fwts_framework *fw) > +{ > + const fwts_acpi_table_wdat *wdat = > + (const fwts_acpi_table_wdat *)table->data; > + bool passed = true; > + bool entries_passed = true; > + size_t total_length; > + uint32_t i; > + > + if (wdat->header.length > (uint32_t)table->length) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_MEDIUM, > + "WDATBadLength", > + "WDAT header reports that the table is %" PRIu32 > + " bytes long, however this is longer than the ACPI " > + "table size of %zu bytes.", > + wdat->header.length, > + table->length); > + goto done; > + } > + > + /* Now we have got some sane data, dump the WDAT */ > +#if ACPI_DUMP > + fwts_log_info_verbatum(fw, "WDAT Microsoft Watchdog Action Table:"); > + fwts_log_info_verbatum(fw, " Watchdog Header Length: 0x%8.8" PRIx32, wdat->watchdog_header_length); > + fwts_log_info_verbatum(fw, " PCI Segment: 0x%4.4" PRIx16, wdat->pci_segment); > + fwts_log_info_verbatum(fw, " PCI Bus Number: 0x%2.2" PRIx8, wdat->pci_bus_number); > + fwts_log_info_verbatum(fw, " PCI Device Number: 0x%2.2" PRIx8, wdat->pci_device_number); > + fwts_log_info_verbatum(fw, " PCI Function Number: 0x%2.2" PRIx8, wdat->pci_function_number); > + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, > + wdat->reserved1[0], wdat->reserved1[1], wdat->reserved1[2]); > + fwts_log_info_verbatum(fw, " Timer Period: 0x%4.4" PRIx32, wdat->timer_period); > + fwts_log_info_verbatum(fw, " Maximum Count: 0x%4.4" PRIx32, wdat->maximum_count); > + fwts_log_info_verbatum(fw, " Minimum Count: 0x%4.4" PRIx32, wdat->minimum_count); > + fwts_log_info_verbatum(fw, " Watchdog Flags: 0x%4.4" PRIx32, wdat->watchdog_flags); > + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, > + wdat->reserved2[0], wdat->reserved2[1], wdat->reserved2[2]); > + fwts_log_info_verbatum(fw, " Watchdog Entries 0x%4.4" PRIx32, wdat->number_of_entries); > +#endif > + > + if (wdat->reserved1[0] | wdat->reserved1[1] | wdat->reserved1[2] | > + wdat->reserved2[0] | wdat->reserved2[1] | wdat->reserved2[2]) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_MEDIUM, > + "WDATReservedFieldsNonZero", > + "WDAT Reserved Fields contain a non-zero value, these " > + "all should be zero."); > + } > + > + if (wdat->minimum_count > wdat->maximum_count) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_MEDIUM, > + "WDATMinGreaterThanMax", > + "WDAT Minimum Count is 0x%" PRIx32 " and is greater " > + "than the Maximum Count of 0x%" PRIx32, > + wdat->minimum_count, wdat->maximum_count); > + } > + > + /* > + * Check if bits 6..1 are set, just bits 0 and 7 are used > + * so check if the undefined bits are set. The specification > + * does not state what these bits should be set as, but > + * this does sanity check that somebody has not set these > + * bits accidentally. This is a LOW issue. > + */ > + if (wdat->watchdog_flags & ~0x81) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_LOW, > + "WDATWatchdogFlagsUndefinedSet", > + "WDAT Watchdog Flags bits [6:1] are not all 0. These bits " > + "are not defined in the specification and should be " > + "set to 0. Watchdog flags are: 0x%" PRIx8, wdat->watchdog_flags); > + } > + > + total_length = sizeof(fwts_acpi_table_wdat) + > + (wdat->number_of_entries * sizeof(fwts_acpi_table_wdat_instr_entries)); > + if (total_length > wdat->header.length) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_HIGH, > + "WDATBadLength", > + "WDAT header reports that the table has %" PRIu32 > + " watchdog instruction entries making the table " > + "%zu bytes long, however, the WDAT table is only %" PRIu32 > + " bytes in size.", > + wdat->number_of_entries, total_length, > + wdat->header.length); > + goto done; > + } > + for (i = 0; i < wdat->number_of_entries; i++) { > + const fwts_acpi_table_wdat_instr_entries *entry = &wdat->entries[i]; > +#if ACPI_DUMP > + fwts_log_info_verbatum(fw, "Watchdog Instruction Entry %" PRIu32, i + 1); > + fwts_log_info_verbatum(fw, " Watchdog Action: 0x%2.2" PRIx8, entry->watchdog_action); > + fwts_log_info_verbatum(fw, " Instruction Flags: 0x%2.2" PRIx8, entry->instruction_flags); > + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8, > + entry->reserved[0], entry->reserved[1]); > + > + fwts_log_info_verbatum(fw, " Address Space ID: 0x%2.2" PRIx8, entry->register_region.address_space_id); > + fwts_log_info_verbatum(fw, " Register Bit Width 0x%2.2" PRIx8, entry->register_region.register_bit_width); > + fwts_log_info_verbatum(fw, " Register Bit Offset 0x%2.2" PRIx8, entry->register_region.register_bit_offset); > + fwts_log_info_verbatum(fw, " Access Size 0x%2.2" PRIx8, entry->register_region.access_width); > + fwts_log_info_verbatum(fw, " Address 0x%16.16" PRIx64, entry->register_region.address); > + fwts_log_info_verbatum(fw, " Value: 0x%8.8" PRIx32, entry->value); > + fwts_log_info_verbatum(fw, " Mask: 0x%8.8" PRIx32, entry->mask); > +#endif > + > + switch (entry->watchdog_action) { > + case 0x01: /* RESET */ > + case 0x04: /* QUERY_CURRENT_COUNTDOWN_PERIOD */ > + case 0x05: /* QUERY_COUNTDOWN_PERIOD */ > + case 0x06: /* SET_COUNTDOWN_PERIOD */ > + case 0x08: /* QUERY_RUNNING_STATE */ > + case 0x09: /* SET_RUNNING_STATE */ > + case 0x0a: /* QUERY_STOPPED_STATE */ > + case 0x0b: /* SET_STOPPED_STATE */ > + case 0x10: /* QUERY_REBOOT */ > + case 0x11: /* SET_REBOOT */ > + case 0x12: /* QUERY_SHUTDOWN */ > + case 0x13: /* SET_SHUTDOWN */ > + case 0x20: /* QUERY_WATCHDOG_STATUS */ > + case 0x21: /* SET_WATCHDOG_STATUS */ > + break; > + default: > + entries_passed = false; > + fwts_failed(fw, LOG_LEVEL_HIGH, > + "WDATWatchdogActionInvalid", > + "WDAT Watchdog Instruction Entry %" PRIu32 > + " Watchdog Action field is 0x%" PRIx8 > + " and should be one of 0x00, 0x04, 0x05, 0x06, " > + "0x08, 0x09, 0x0a, 0x0b, 0x10, 0x11, 0x12, 0x13, " > + "0x20 or 0x21", > + i + 1, entry->watchdog_action); > + break; > + } > + /* > + * Instruction flags can be 0x00, 0x01, 0x02, 0x03 with > + * bit 7 clear or set, so bits 7, 1, 0 are valid, (which > + * is 0x80 | 0x02 | 0x01) > + */ > + if (entry->instruction_flags & ~0x83) { > + entries_passed = false; > + fwts_failed(fw, LOG_LEVEL_HIGH, > + "WDATInstructionFlagsInvalid", > + "WDAT Watchdog Instruction Entry %" PRIu32 > + " Instruction Flags field is 0x%" PRIx8 > + " and should be one of 0x00, 0x01, 0x02, 0x03 or " > + " 0x80, 0x81, 0x82, 0x83", > + i + 1, entry->instruction_flags); > + } > + } > + if (entries_passed) > + fwts_passed(fw, "All %" PRIu32 " WDAT Watchdog Instruction Entries look sane.", > + wdat->number_of_entries); > + > +done: > + passed &= entries_passed; > + if (passed) > + fwts_passed(fw, "No issues found in WDAT table."); > + > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test wdat_tests[] = { > + { wdat_test1, "WDAT Microsoft Hardware Watchdog Action Table test." }, > + { NULL, NULL } > +}; > + > +static fwts_framework_ops wdat_ops = { > + .description = "WDAT Microsoft Hardware Watchdog Action Table test.", > + .init = wdat_init, > + .minor_tests = wdat_tests > +}; > + > +FWTS_REGISTER("wdat", &wdat_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI) > diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h > index 923a461..5cc4cb4 100644 > --- a/src/lib/include/fwts_acpi.h > +++ b/src/lib/include/fwts_acpi.h > @@ -1380,4 +1380,34 @@ typedef struct { > uint8_t namelist[0]; > } __attribute__ ((packed)) fwts_acpi_table_stao; > > +/* > + * ACPI WDAT (Watchdog Action Table) > + * https://msdn.microsoft.com/en-us/windows/hardware/gg463320.aspx > + */ > +typedef struct { > + uint8_t watchdog_action; > + uint8_t instruction_flags; > + uint8_t reserved[2]; > + fwts_acpi_gas register_region; > + uint32_t value; > + uint32_t mask; > +} __attribute__ ((packed)) fwts_acpi_table_wdat_instr_entries; > + > +typedef struct { > + fwts_acpi_table_header header; > + uint32_t watchdog_header_length; > + uint16_t pci_segment; > + uint8_t pci_bus_number; > + uint8_t pci_device_number; > + uint8_t pci_function_number; > + uint8_t reserved1[3]; > + uint32_t timer_period; > + uint32_t maximum_count; > + uint32_t minimum_count; > + uint8_t watchdog_flags; > + uint8_t reserved2[3]; > + uint32_t number_of_entries; > + fwts_acpi_table_wdat_instr_entries entries[0]; > +} __attribute__ ((packed)) fwts_acpi_table_wdat; > + > #endif Acked-by: Ivan Hu<ivan.hu@canonical.com>
On 07/31/2015 07:48 PM, Colin King wrote: > From: Colin Ian King <colin.king@canonical.com> > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > src/Makefile.am | 1 + > src/acpi/wdat/wdat.c | 227 ++++++++++++++++++++++++++++++++++++++++++++ > src/lib/include/fwts_acpi.h | 30 ++++++ > 3 files changed, 258 insertions(+) > create mode 100644 src/acpi/wdat/wdat.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index 11aa37b..9c8e35e 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -86,6 +86,7 @@ fwts_SOURCES = main.c \ > acpi/uefi/uefi.c \ > acpi/waet/waet.c \ > acpi/wakealarm/wakealarm.c \ > + acpi/wdat/wdat.c \ > acpi/wmi/wmi.c \ > acpi/xsdt/xsdt.c \ > acpi/xenv/xenv.c \ > diff --git a/src/acpi/wdat/wdat.c b/src/acpi/wdat/wdat.c > new file mode 100644 > index 0000000..fdea850 > --- /dev/null > +++ b/src/acpi/wdat/wdat.c > @@ -0,0 +1,227 @@ > +/* > + * Copyright (C) 2015 Canonical > + * > + * Portions of this code original from the Linux-ready Firmware Developer Kit > + * > + * 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 <stdlib.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <inttypes.h> > +#include <string.h> > + > +#include "fwts_acpi_object_eval.h" > + > +#define ACPI_DUMP (0) /* WDAT entries are long, so don't dump, too verbose */ > + > +static fwts_acpi_table_info *table; > + > +static int wdat_init(fwts_framework *fw) > +{ > + > + if (fwts_acpi_find_table(fw, "WDAT", 0, &table) != FWTS_OK) { > + fwts_log_error(fw, "Cannot read ACPI tables."); > + return FWTS_ERROR; > + } > + if (table == NULL || (table && table->length == 0)) { > + fwts_log_error(fw, "ACPI WDAT table does not exist, skipping test"); > + return FWTS_SKIP; > + } > + > + return FWTS_OK; > +} > + > +/* > + * ACPI WDAT (Watchdog Action Table) > + * https://msdn.microsoft.com/en-us/windows/hardware/gg463320.aspx > + */ > +static int wdat_test1(fwts_framework *fw) > +{ > + const fwts_acpi_table_wdat *wdat = > + (const fwts_acpi_table_wdat *)table->data; > + bool passed = true; > + bool entries_passed = true; > + size_t total_length; > + uint32_t i; > + > + if (wdat->header.length > (uint32_t)table->length) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_MEDIUM, > + "WDATBadLength", > + "WDAT header reports that the table is %" PRIu32 > + " bytes long, however this is longer than the ACPI " > + "table size of %zu bytes.", > + wdat->header.length, > + table->length); > + goto done; > + } > + > + /* Now we have got some sane data, dump the WDAT */ > +#if ACPI_DUMP > + fwts_log_info_verbatum(fw, "WDAT Microsoft Watchdog Action Table:"); > + fwts_log_info_verbatum(fw, " Watchdog Header Length: 0x%8.8" PRIx32, wdat->watchdog_header_length); > + fwts_log_info_verbatum(fw, " PCI Segment: 0x%4.4" PRIx16, wdat->pci_segment); > + fwts_log_info_verbatum(fw, " PCI Bus Number: 0x%2.2" PRIx8, wdat->pci_bus_number); > + fwts_log_info_verbatum(fw, " PCI Device Number: 0x%2.2" PRIx8, wdat->pci_device_number); > + fwts_log_info_verbatum(fw, " PCI Function Number: 0x%2.2" PRIx8, wdat->pci_function_number); > + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, > + wdat->reserved1[0], wdat->reserved1[1], wdat->reserved1[2]); > + fwts_log_info_verbatum(fw, " Timer Period: 0x%4.4" PRIx32, wdat->timer_period); > + fwts_log_info_verbatum(fw, " Maximum Count: 0x%4.4" PRIx32, wdat->maximum_count); > + fwts_log_info_verbatum(fw, " Minimum Count: 0x%4.4" PRIx32, wdat->minimum_count); > + fwts_log_info_verbatum(fw, " Watchdog Flags: 0x%4.4" PRIx32, wdat->watchdog_flags); > + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, > + wdat->reserved2[0], wdat->reserved2[1], wdat->reserved2[2]); > + fwts_log_info_verbatum(fw, " Watchdog Entries 0x%4.4" PRIx32, wdat->number_of_entries); > +#endif > + > + if (wdat->reserved1[0] | wdat->reserved1[1] | wdat->reserved1[2] | > + wdat->reserved2[0] | wdat->reserved2[1] | wdat->reserved2[2]) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_MEDIUM, > + "WDATReservedFieldsNonZero", > + "WDAT Reserved Fields contain a non-zero value, these " > + "all should be zero."); > + } > + > + if (wdat->minimum_count > wdat->maximum_count) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_MEDIUM, > + "WDATMinGreaterThanMax", > + "WDAT Minimum Count is 0x%" PRIx32 " and is greater " > + "than the Maximum Count of 0x%" PRIx32, > + wdat->minimum_count, wdat->maximum_count); > + } > + > + /* > + * Check if bits 6..1 are set, just bits 0 and 7 are used > + * so check if the undefined bits are set. The specification > + * does not state what these bits should be set as, but > + * this does sanity check that somebody has not set these > + * bits accidentally. This is a LOW issue. > + */ > + if (wdat->watchdog_flags & ~0x81) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_LOW, > + "WDATWatchdogFlagsUndefinedSet", > + "WDAT Watchdog Flags bits [6:1] are not all 0. These bits " > + "are not defined in the specification and should be " > + "set to 0. Watchdog flags are: 0x%" PRIx8, wdat->watchdog_flags); > + } > + > + total_length = sizeof(fwts_acpi_table_wdat) + > + (wdat->number_of_entries * sizeof(fwts_acpi_table_wdat_instr_entries)); > + if (total_length > wdat->header.length) { > + passed = false; > + fwts_failed(fw, LOG_LEVEL_HIGH, > + "WDATBadLength", > + "WDAT header reports that the table has %" PRIu32 > + " watchdog instruction entries making the table " > + "%zu bytes long, however, the WDAT table is only %" PRIu32 > + " bytes in size.", > + wdat->number_of_entries, total_length, > + wdat->header.length); > + goto done; > + } > + for (i = 0; i < wdat->number_of_entries; i++) { > + const fwts_acpi_table_wdat_instr_entries *entry = &wdat->entries[i]; > +#if ACPI_DUMP > + fwts_log_info_verbatum(fw, "Watchdog Instruction Entry %" PRIu32, i + 1); > + fwts_log_info_verbatum(fw, " Watchdog Action: 0x%2.2" PRIx8, entry->watchdog_action); > + fwts_log_info_verbatum(fw, " Instruction Flags: 0x%2.2" PRIx8, entry->instruction_flags); > + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8, > + entry->reserved[0], entry->reserved[1]); > + > + fwts_log_info_verbatum(fw, " Address Space ID: 0x%2.2" PRIx8, entry->register_region.address_space_id); > + fwts_log_info_verbatum(fw, " Register Bit Width 0x%2.2" PRIx8, entry->register_region.register_bit_width); > + fwts_log_info_verbatum(fw, " Register Bit Offset 0x%2.2" PRIx8, entry->register_region.register_bit_offset); > + fwts_log_info_verbatum(fw, " Access Size 0x%2.2" PRIx8, entry->register_region.access_width); > + fwts_log_info_verbatum(fw, " Address 0x%16.16" PRIx64, entry->register_region.address); > + fwts_log_info_verbatum(fw, " Value: 0x%8.8" PRIx32, entry->value); > + fwts_log_info_verbatum(fw, " Mask: 0x%8.8" PRIx32, entry->mask); > +#endif > + > + switch (entry->watchdog_action) { > + case 0x01: /* RESET */ > + case 0x04: /* QUERY_CURRENT_COUNTDOWN_PERIOD */ > + case 0x05: /* QUERY_COUNTDOWN_PERIOD */ > + case 0x06: /* SET_COUNTDOWN_PERIOD */ > + case 0x08: /* QUERY_RUNNING_STATE */ > + case 0x09: /* SET_RUNNING_STATE */ > + case 0x0a: /* QUERY_STOPPED_STATE */ > + case 0x0b: /* SET_STOPPED_STATE */ > + case 0x10: /* QUERY_REBOOT */ > + case 0x11: /* SET_REBOOT */ > + case 0x12: /* QUERY_SHUTDOWN */ > + case 0x13: /* SET_SHUTDOWN */ > + case 0x20: /* QUERY_WATCHDOG_STATUS */ > + case 0x21: /* SET_WATCHDOG_STATUS */ > + break; > + default: > + entries_passed = false; > + fwts_failed(fw, LOG_LEVEL_HIGH, > + "WDATWatchdogActionInvalid", > + "WDAT Watchdog Instruction Entry %" PRIu32 > + " Watchdog Action field is 0x%" PRIx8 > + " and should be one of 0x00, 0x04, 0x05, 0x06, " > + "0x08, 0x09, 0x0a, 0x0b, 0x10, 0x11, 0x12, 0x13, " > + "0x20 or 0x21", > + i + 1, entry->watchdog_action); > + break; > + } > + /* > + * Instruction flags can be 0x00, 0x01, 0x02, 0x03 with > + * bit 7 clear or set, so bits 7, 1, 0 are valid, (which > + * is 0x80 | 0x02 | 0x01) > + */ > + if (entry->instruction_flags & ~0x83) { > + entries_passed = false; > + fwts_failed(fw, LOG_LEVEL_HIGH, > + "WDATInstructionFlagsInvalid", > + "WDAT Watchdog Instruction Entry %" PRIu32 > + " Instruction Flags field is 0x%" PRIx8 > + " and should be one of 0x00, 0x01, 0x02, 0x03 or " > + " 0x80, 0x81, 0x82, 0x83", > + i + 1, entry->instruction_flags); > + } > + } > + if (entries_passed) > + fwts_passed(fw, "All %" PRIu32 " WDAT Watchdog Instruction Entries look sane.", > + wdat->number_of_entries); > + > +done: > + passed &= entries_passed; > + if (passed) > + fwts_passed(fw, "No issues found in WDAT table."); > + > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test wdat_tests[] = { > + { wdat_test1, "WDAT Microsoft Hardware Watchdog Action Table test." }, > + { NULL, NULL } > +}; > + > +static fwts_framework_ops wdat_ops = { > + .description = "WDAT Microsoft Hardware Watchdog Action Table test.", > + .init = wdat_init, > + .minor_tests = wdat_tests > +}; > + > +FWTS_REGISTER("wdat", &wdat_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI) > diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h > index 923a461..5cc4cb4 100644 > --- a/src/lib/include/fwts_acpi.h > +++ b/src/lib/include/fwts_acpi.h > @@ -1380,4 +1380,34 @@ typedef struct { > uint8_t namelist[0]; > } __attribute__ ((packed)) fwts_acpi_table_stao; > > +/* > + * ACPI WDAT (Watchdog Action Table) > + * https://msdn.microsoft.com/en-us/windows/hardware/gg463320.aspx > + */ > +typedef struct { > + uint8_t watchdog_action; > + uint8_t instruction_flags; > + uint8_t reserved[2]; > + fwts_acpi_gas register_region; > + uint32_t value; > + uint32_t mask; > +} __attribute__ ((packed)) fwts_acpi_table_wdat_instr_entries; > + > +typedef struct { > + fwts_acpi_table_header header; > + uint32_t watchdog_header_length; > + uint16_t pci_segment; > + uint8_t pci_bus_number; > + uint8_t pci_device_number; > + uint8_t pci_function_number; > + uint8_t reserved1[3]; > + uint32_t timer_period; > + uint32_t maximum_count; > + uint32_t minimum_count; > + uint8_t watchdog_flags; > + uint8_t reserved2[3]; > + uint32_t number_of_entries; > + fwts_acpi_table_wdat_instr_entries entries[0]; > +} __attribute__ ((packed)) fwts_acpi_table_wdat; > + > #endif > Acked-by: Alex Hung <alex.hung@canonical.com>
diff --git a/src/Makefile.am b/src/Makefile.am index 11aa37b..9c8e35e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -86,6 +86,7 @@ fwts_SOURCES = main.c \ acpi/uefi/uefi.c \ acpi/waet/waet.c \ acpi/wakealarm/wakealarm.c \ + acpi/wdat/wdat.c \ acpi/wmi/wmi.c \ acpi/xsdt/xsdt.c \ acpi/xenv/xenv.c \ diff --git a/src/acpi/wdat/wdat.c b/src/acpi/wdat/wdat.c new file mode 100644 index 0000000..fdea850 --- /dev/null +++ b/src/acpi/wdat/wdat.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2015 Canonical + * + * Portions of this code original from the Linux-ready Firmware Developer Kit + * + * 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <string.h> + +#include "fwts_acpi_object_eval.h" + +#define ACPI_DUMP (0) /* WDAT entries are long, so don't dump, too verbose */ + +static fwts_acpi_table_info *table; + +static int wdat_init(fwts_framework *fw) +{ + + if (fwts_acpi_find_table(fw, "WDAT", 0, &table) != FWTS_OK) { + fwts_log_error(fw, "Cannot read ACPI tables."); + return FWTS_ERROR; + } + if (table == NULL || (table && table->length == 0)) { + fwts_log_error(fw, "ACPI WDAT table does not exist, skipping test"); + return FWTS_SKIP; + } + + return FWTS_OK; +} + +/* + * ACPI WDAT (Watchdog Action Table) + * https://msdn.microsoft.com/en-us/windows/hardware/gg463320.aspx + */ +static int wdat_test1(fwts_framework *fw) +{ + const fwts_acpi_table_wdat *wdat = + (const fwts_acpi_table_wdat *)table->data; + bool passed = true; + bool entries_passed = true; + size_t total_length; + uint32_t i; + + if (wdat->header.length > (uint32_t)table->length) { + passed = false; + fwts_failed(fw, LOG_LEVEL_MEDIUM, + "WDATBadLength", + "WDAT header reports that the table is %" PRIu32 + " bytes long, however this is longer than the ACPI " + "table size of %zu bytes.", + wdat->header.length, + table->length); + goto done; + } + + /* Now we have got some sane data, dump the WDAT */ +#if ACPI_DUMP + fwts_log_info_verbatum(fw, "WDAT Microsoft Watchdog Action Table:"); + fwts_log_info_verbatum(fw, " Watchdog Header Length: 0x%8.8" PRIx32, wdat->watchdog_header_length); + fwts_log_info_verbatum(fw, " PCI Segment: 0x%4.4" PRIx16, wdat->pci_segment); + fwts_log_info_verbatum(fw, " PCI Bus Number: 0x%2.2" PRIx8, wdat->pci_bus_number); + fwts_log_info_verbatum(fw, " PCI Device Number: 0x%2.2" PRIx8, wdat->pci_device_number); + fwts_log_info_verbatum(fw, " PCI Function Number: 0x%2.2" PRIx8, wdat->pci_function_number); + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, + wdat->reserved1[0], wdat->reserved1[1], wdat->reserved1[2]); + fwts_log_info_verbatum(fw, " Timer Period: 0x%4.4" PRIx32, wdat->timer_period); + fwts_log_info_verbatum(fw, " Maximum Count: 0x%4.4" PRIx32, wdat->maximum_count); + fwts_log_info_verbatum(fw, " Minimum Count: 0x%4.4" PRIx32, wdat->minimum_count); + fwts_log_info_verbatum(fw, " Watchdog Flags: 0x%4.4" PRIx32, wdat->watchdog_flags); + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, + wdat->reserved2[0], wdat->reserved2[1], wdat->reserved2[2]); + fwts_log_info_verbatum(fw, " Watchdog Entries 0x%4.4" PRIx32, wdat->number_of_entries); +#endif + + if (wdat->reserved1[0] | wdat->reserved1[1] | wdat->reserved1[2] | + wdat->reserved2[0] | wdat->reserved2[1] | wdat->reserved2[2]) { + passed = false; + fwts_failed(fw, LOG_LEVEL_MEDIUM, + "WDATReservedFieldsNonZero", + "WDAT Reserved Fields contain a non-zero value, these " + "all should be zero."); + } + + if (wdat->minimum_count > wdat->maximum_count) { + passed = false; + fwts_failed(fw, LOG_LEVEL_MEDIUM, + "WDATMinGreaterThanMax", + "WDAT Minimum Count is 0x%" PRIx32 " and is greater " + "than the Maximum Count of 0x%" PRIx32, + wdat->minimum_count, wdat->maximum_count); + } + + /* + * Check if bits 6..1 are set, just bits 0 and 7 are used + * so check if the undefined bits are set. The specification + * does not state what these bits should be set as, but + * this does sanity check that somebody has not set these + * bits accidentally. This is a LOW issue. + */ + if (wdat->watchdog_flags & ~0x81) { + passed = false; + fwts_failed(fw, LOG_LEVEL_LOW, + "WDATWatchdogFlagsUndefinedSet", + "WDAT Watchdog Flags bits [6:1] are not all 0. These bits " + "are not defined in the specification and should be " + "set to 0. Watchdog flags are: 0x%" PRIx8, wdat->watchdog_flags); + } + + total_length = sizeof(fwts_acpi_table_wdat) + + (wdat->number_of_entries * sizeof(fwts_acpi_table_wdat_instr_entries)); + if (total_length > wdat->header.length) { + passed = false; + fwts_failed(fw, LOG_LEVEL_HIGH, + "WDATBadLength", + "WDAT header reports that the table has %" PRIu32 + " watchdog instruction entries making the table " + "%zu bytes long, however, the WDAT table is only %" PRIu32 + " bytes in size.", + wdat->number_of_entries, total_length, + wdat->header.length); + goto done; + } + for (i = 0; i < wdat->number_of_entries; i++) { + const fwts_acpi_table_wdat_instr_entries *entry = &wdat->entries[i]; +#if ACPI_DUMP + fwts_log_info_verbatum(fw, "Watchdog Instruction Entry %" PRIu32, i + 1); + fwts_log_info_verbatum(fw, " Watchdog Action: 0x%2.2" PRIx8, entry->watchdog_action); + fwts_log_info_verbatum(fw, " Instruction Flags: 0x%2.2" PRIx8, entry->instruction_flags); + fwts_log_info_verbatum(fw, " Reserved: 0x%2.2" PRIx8 " 0x%2.2" PRIx8, + entry->reserved[0], entry->reserved[1]); + + fwts_log_info_verbatum(fw, " Address Space ID: 0x%2.2" PRIx8, entry->register_region.address_space_id); + fwts_log_info_verbatum(fw, " Register Bit Width 0x%2.2" PRIx8, entry->register_region.register_bit_width); + fwts_log_info_verbatum(fw, " Register Bit Offset 0x%2.2" PRIx8, entry->register_region.register_bit_offset); + fwts_log_info_verbatum(fw, " Access Size 0x%2.2" PRIx8, entry->register_region.access_width); + fwts_log_info_verbatum(fw, " Address 0x%16.16" PRIx64, entry->register_region.address); + fwts_log_info_verbatum(fw, " Value: 0x%8.8" PRIx32, entry->value); + fwts_log_info_verbatum(fw, " Mask: 0x%8.8" PRIx32, entry->mask); +#endif + + switch (entry->watchdog_action) { + case 0x01: /* RESET */ + case 0x04: /* QUERY_CURRENT_COUNTDOWN_PERIOD */ + case 0x05: /* QUERY_COUNTDOWN_PERIOD */ + case 0x06: /* SET_COUNTDOWN_PERIOD */ + case 0x08: /* QUERY_RUNNING_STATE */ + case 0x09: /* SET_RUNNING_STATE */ + case 0x0a: /* QUERY_STOPPED_STATE */ + case 0x0b: /* SET_STOPPED_STATE */ + case 0x10: /* QUERY_REBOOT */ + case 0x11: /* SET_REBOOT */ + case 0x12: /* QUERY_SHUTDOWN */ + case 0x13: /* SET_SHUTDOWN */ + case 0x20: /* QUERY_WATCHDOG_STATUS */ + case 0x21: /* SET_WATCHDOG_STATUS */ + break; + default: + entries_passed = false; + fwts_failed(fw, LOG_LEVEL_HIGH, + "WDATWatchdogActionInvalid", + "WDAT Watchdog Instruction Entry %" PRIu32 + " Watchdog Action field is 0x%" PRIx8 + " and should be one of 0x00, 0x04, 0x05, 0x06, " + "0x08, 0x09, 0x0a, 0x0b, 0x10, 0x11, 0x12, 0x13, " + "0x20 or 0x21", + i + 1, entry->watchdog_action); + break; + } + /* + * Instruction flags can be 0x00, 0x01, 0x02, 0x03 with + * bit 7 clear or set, so bits 7, 1, 0 are valid, (which + * is 0x80 | 0x02 | 0x01) + */ + if (entry->instruction_flags & ~0x83) { + entries_passed = false; + fwts_failed(fw, LOG_LEVEL_HIGH, + "WDATInstructionFlagsInvalid", + "WDAT Watchdog Instruction Entry %" PRIu32 + " Instruction Flags field is 0x%" PRIx8 + " and should be one of 0x00, 0x01, 0x02, 0x03 or " + " 0x80, 0x81, 0x82, 0x83", + i + 1, entry->instruction_flags); + } + } + if (entries_passed) + fwts_passed(fw, "All %" PRIu32 " WDAT Watchdog Instruction Entries look sane.", + wdat->number_of_entries); + +done: + passed &= entries_passed; + if (passed) + fwts_passed(fw, "No issues found in WDAT table."); + + return FWTS_OK; +} + +static fwts_framework_minor_test wdat_tests[] = { + { wdat_test1, "WDAT Microsoft Hardware Watchdog Action Table test." }, + { NULL, NULL } +}; + +static fwts_framework_ops wdat_ops = { + .description = "WDAT Microsoft Hardware Watchdog Action Table test.", + .init = wdat_init, + .minor_tests = wdat_tests +}; + +FWTS_REGISTER("wdat", &wdat_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI) diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h index 923a461..5cc4cb4 100644 --- a/src/lib/include/fwts_acpi.h +++ b/src/lib/include/fwts_acpi.h @@ -1380,4 +1380,34 @@ typedef struct { uint8_t namelist[0]; } __attribute__ ((packed)) fwts_acpi_table_stao; +/* + * ACPI WDAT (Watchdog Action Table) + * https://msdn.microsoft.com/en-us/windows/hardware/gg463320.aspx + */ +typedef struct { + uint8_t watchdog_action; + uint8_t instruction_flags; + uint8_t reserved[2]; + fwts_acpi_gas register_region; + uint32_t value; + uint32_t mask; +} __attribute__ ((packed)) fwts_acpi_table_wdat_instr_entries; + +typedef struct { + fwts_acpi_table_header header; + uint32_t watchdog_header_length; + uint16_t pci_segment; + uint8_t pci_bus_number; + uint8_t pci_device_number; + uint8_t pci_function_number; + uint8_t reserved1[3]; + uint32_t timer_period; + uint32_t maximum_count; + uint32_t minimum_count; + uint8_t watchdog_flags; + uint8_t reserved2[3]; + uint32_t number_of_entries; + fwts_acpi_table_wdat_instr_entries entries[0]; +} __attribute__ ((packed)) fwts_acpi_table_wdat; + #endif