/* * WAET Windows ACPI Emulated Devices Table * see http://msdn.microsoft.com/en-us/windows/hardware/gg487524.aspx */ static int waet_test1(fwts_framework *fw) { bool passed = true; fwts_acpi_table_waet *waet = (fwts_acpi_table_waet *)table->data; /* Enough length for the initial waet header? */ if (table->length < sizeof(fwts_acpi_table_waet)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "WAETTooShort", "WAET table too short, expecting %zu bytes, " "instead got %zu bytes", sizeof(fwts_acpi_table_waet), table->length); goto done; } fwts_log_info_verbatum(fw, "WAET Table:"); fwts_log_info_verbatum(fw, " Emulated Device Flags: 0x%8.8" PRIx32, waet->flags); fwts_log_info_verbatum(fw, " Bit [0] RTC Good: %1" PRIu32, waet->flags & 1); fwts_log_info_verbatum(fw, " Bit [1] PM Timer Good: %1" PRIu32, (waet->flags >> 1) & 1); fwts_log_nl(fw); if (waet->flags & ~3) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "WAETFlagReservedNotZero", "WAET Emulated Device Flags was 0x%" PRIx32 " and so some of reserved bits [31:2] are not zero " " as expected.", waet->flags); } done: if (passed) fwts_passed(fw, "No issues found in WAET table."); return FWTS_OK; }
static int lid_test_state(fwts_framework *fw, int button) { int gpe_count = 0; int fd; int matching = 0; int not_matching = 0; int events = 0; size_t len; char *state; int i; fwts_gpe *gpes_start; fwts_gpe *gpes_end; switch (button) { case FWTS_BUTTON_LID_OPENED: state = "open"; break; case FWTS_BUTTON_LID_CLOSED: state = "closed"; break; default: state = "unknown"; break; } if ((gpe_count = fwts_gpe_read(&gpes_start)) == FWTS_ERROR) { fwts_log_error(fw, "Cannot read GPEs."); return FWTS_ERROR; } if ((fd = fwts_acpi_event_open()) < 0) { fwts_log_error(fw, "Cannot connect to acpid."); fwts_gpe_free(gpes_start, gpe_count); return FWTS_ERROR; } for (i = 0; i <= 20; i++) { char *buffer; if ((buffer = fwts_acpi_event_read(fd, &len, 1)) != NULL) { if (strstr(buffer, "button/lid")) { events++; lid_check_field_poll(fw, button, &matching, ¬_matching); break; } free(buffer); } fwts_printf(fw, "Waiting %2.2d/20\r", 20-i); } fwts_acpi_event_close(fd); if ((gpe_count = fwts_gpe_read(&gpes_end)) == FWTS_ERROR) { fwts_log_error(fw, "Cannot read GPEs."); fwts_gpe_free(gpes_start, gpe_count); return FWTS_ERROR; } fwts_gpe_test(fw, gpes_start, gpes_end, gpe_count); fwts_gpe_free(gpes_start, gpe_count); fwts_gpe_free(gpes_end, gpe_count); if (events == 0) fwts_failed(fw, LOG_LEVEL_HIGH, "NoLidEvents", "Did not detect any ACPI LID events while waiting for to LID %s.", state); else { fwts_passed(fw, "Detected ACPI LID events while waiting for LID to %s.", state); if ((matching == 0) || (not_matching > 0)) fwts_failed(fw, LOG_LEVEL_HIGH, "NoLidState", "Could not detect lid %s state.", state); else fwts_passed(fw, "Detected lid %s state.", state); } return FWTS_OK; }
/* * 4.1.2.4 ASF_RCTL */ static void asf_check_rctl( fwts_framework *fw, ssize_t record_length, ssize_t length, uint8_t *data, bool *passed, bool *abort) { fwts_acpi_table_asf_rctl *rctl = (fwts_acpi_table_asf_rctl *)data; uint8_t i; ssize_t total_length; if (length < (ssize_t)sizeof(fwts_acpi_table_asf_rctl)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!RctlRecordTooShort", "ASF! ASF_RCTL Record too short, " "expecting %zu bytes, instead got %zu bytes", sizeof(fwts_acpi_table_asf_rctl), length); *passed = false; *abort = true; return; } #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_RCTL Record:"); fwts_log_info_verbatim(fw, " Number of Controls: 0x%2.2" PRIx8, rctl->number_of_controls); fwts_log_info_verbatim(fw, " Array Element Length: 0x%2.2" PRIx8, rctl->array_element_length); fwts_log_info_verbatim(fw, " Reserved: 0x%4.4" PRIx16, rctl->array_element_length); #endif if (rctl->array_element_length != sizeof(fwts_acpi_table_asf_rctl_element)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!RctlArrayElementLengthInvalid", "ASF! ASF_RCTL Array Element Length is 0x%" PRIx8 " and must be instead 0x%" PRIx8, rctl->array_element_length, (uint8_t)sizeof(fwts_acpi_table_asf_rctl_element)); } total_length = sizeof(fwts_acpi_table_asf_rctl) + (rctl->number_of_controls * sizeof(fwts_acpi_table_asf_rctl_element)); if (total_length > record_length) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!RctlArrayElementLengthInvalid", "ASF! ASF_RCTL Array Element Length makes the " "total ASF_RCTL record size to be %zu bytes, however the " "table is only %zu bytes long", total_length, record_length); *passed = false; *abort = true; return; } data += sizeof(fwts_acpi_table_asf_rctl); for (i = 0; i < rctl->number_of_controls; i++) { fwts_acpi_table_asf_rctl_element *element = (fwts_acpi_table_asf_rctl_element *)data; #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_RCTL Element %" PRIu8 ":", i); fwts_log_info_verbatim(fw, " Control Function: 0x%2.2" PRIx8, element->control_function); fwts_log_info_verbatim(fw, " Control Device Address: 0x%2.2" PRIx8, element->control_device_addr); fwts_log_info_verbatim(fw, " Control Command: 0x%2.2" PRIx8, element->control_command); fwts_log_info_verbatim(fw, " Control Value: 0x%2.2" PRIx8, element->control_value); #endif if (element->control_function > 0x03) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!RctlCtrlFuncInvalid", "ASF! ASF_RCTL Control Function is 0x%" PRIx8 " and must be in the range 0x00..0x03", element->control_function); } data += sizeof(fwts_acpi_table_asf_rctl_element); } if (*passed) fwts_passed(fw, "No issues found in ASF! ASF_RCTL record."); }
static int cpuidle_states_test(fwts_framework *fw) { int offset, len, test_len, ret; int latency_ns[CPUIDLE_STATE_MAX]; int residency_ns[CPUIDLE_STATE_MAX]; int flags[CPUIDLE_STATE_MAX]; uint64_t pm_cr[CPUIDLE_STATE_MAX]; uint64_t pm_cr_mask[CPUIDLE_STATE_MAX]; bool has_stop_inst = false; bool ok = true; char *control_prop, *mask_prop; switch (proc_gen) { case proc_gen_p8: has_stop_inst = false; control_prop = "ibm,cpu-idle-state-pmicr"; mask_prop = "ibm,cpu-idle-state-pmicr-mask"; break; case proc_gen_p9: has_stop_inst = true; control_prop = "ibm,cpu-idle-state-psscr"; mask_prop = "ibm,cpu-idle-state-psscr-mask"; break; default: fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip", "Unknown processor generation %d", proc_gen); return FWTS_ERROR; } offset = fdt_path_offset(fw->fdt, power_mgt_path); if (offset < 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing", "power management node %s is missing", power_mgt_path); return FWTS_ERROR; } /* In P9 ibm,enabled-stop-levels present in /ibm,opal/power-mgt/ */ if (has_stop_inst) { const int *buf; buf = fdt_getprop(fw->fdt, offset, "ibm,enabled-stop-levels", &len); if (!buf) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing", "ibm,enabled-stop-levels missing under %s", power_mgt_path); return FWTS_ERROR; } } /* Validate ibm,cpu-idle-state-flags property */ ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,cpu-idle-state-flags", flags, &len); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property ibm,cpu-idle-state-flags %s", fdt_strerror(len)); return FWTS_ERROR; } if (len < 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNoIdleStates", "No idle states found in DT"); return FWTS_ERROR; } if (len > CPUIDLE_STATE_MAX-1) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTMoreIdleStates", "More idle states found in DT than the expected"); return FWTS_ERROR; } /* Validate ibm,cpu-idle-state-latencies-ns property */ ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,cpu-idle-state-latencies-ns", latency_ns, &test_len); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property" " ibm,cpu-idle-state-latencies-ns %s", fdt_strerror(test_len)); return FWTS_ERROR; } if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len, "ibm,cpu-idle-state-latencies-ns", test_len) != FWTS_OK) ok = false; /* Validate ibm,cpu-idle-state-names property */ test_len = fwts_dt_stringlist_count(fw, fw->fdt, offset, "ibm,cpu-idle-state-names"); if (test_len > 0) { if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len, "ibm,cpu-idle-state-names", test_len) != FWTS_OK) ok = false; } /* Validate ibm,cpu-idle-state-residency-ns property */ ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,cpu-idle-state-residency-ns", residency_ns, &test_len); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property " "ibm,cpu-idle-state-residency-ns %s", fdt_strerror(test_len)); return FWTS_ERROR; } if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len, "ibm,cpu-idle-state-residency-ns", test_len) != FWTS_OK) ok = false; /* Validate pmicr and psscr value and mask bits */ ret = fwts_dt_property_read_u64_arr(fw->fdt, offset, control_prop, pm_cr, &test_len); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property %s rc: %s", control_prop, fdt_strerror(test_len)); return FWTS_ERROR; } if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len, control_prop, test_len) != FWTS_OK) ok = false; ret = fwts_dt_property_read_u64_arr(fw->fdt, offset, mask_prop, pm_cr_mask, &test_len); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property %s rc: %s", mask_prop, fdt_strerror(test_len)); return FWTS_ERROR; } if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len, mask_prop, test_len) != FWTS_OK) ok = false; if (!ok) { fwts_failed(fw, LOG_LEVEL_HIGH, "CPUIDLEStatesFail", "One or few CPU IDLE DT Validation tests failed"); return FWTS_ERROR; } fwts_passed(fw, "CPU IDLE States are validated"); return FWTS_OK; }
/* * For SLIT System Locality Distance Information refer to * section 5.2.17 of the ACPI specification version 6.0 */ static int slit_test1(fwts_framework *fw) { bool passed = true; uint64_t i, j, size, n, reserved = 0, bad_entry = 0; uint8_t *entry; fwts_acpi_table_slit *slit = (fwts_acpi_table_slit *)table->data; /* Size sanity check #1, got enough table to at least get matrix size */ if (table->length < sizeof(fwts_acpi_table_slit)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SLITTooShort", "SLIT table too short, must be at least %zu bytes, " "instead got %zu bytes", sizeof(fwts_acpi_table_spmi), table->length); goto done; } n = slit->num_of_system_localities; fwts_log_info_verbatum(fw, "SLIT System Locality Distance Information Table:"); fwts_log_info_verbatum(fw, " Number of Localities: 0x%" PRIx64, n); /* * ACPI table length is 32 bits, so maximum matrix of entries size is * is 2^32 - sizeof(fwts_acpi_table_slit) = 2^32 - 44 = 4294967252 * and table is a N x N matrix, so maxium number of localities is * limited to int(sqrt(4294967252)) = 65535. */ if (n > 0xffff) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SLITTooManySystemLocalities", "SLIT table size is %zu, however, the number of " "System Locality Entries is %" PRIu64 " which " "results in an ACPI table larger than the maximum " "32 bit ACPI table size of 4MB", table->length, n); goto done; } /* * Now that we are confident of no overflow, check that the matrix * + SLIT table header is not bigger than the actual table. */ size = (n * n) + sizeof(fwts_acpi_table_slit); if ((uint64_t)table->length < size) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SLITTooManySystemLocalities", "SLIT table size is %zu, however, the number of " "System Locality Entries is %" PRIu64 " and this " "results in a table of size %" PRIu64 " which " "is larger than the SLIT table size", table->length, size, n); goto done; } entry = (uint8_t *)table->data + sizeof(fwts_acpi_table_slit); /* * Now sanity check the entries.. */ if (entry[INDEX(0,0)] != 10) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SLITBadCornerEntry", "SLIT Entry[0][0] is 0x%" PRIx8 ", expecting value 0x0a.", entry[INDEX(0,0)]); } if (entry[INDEX(n - 1, n - 1)] != 10) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SLITBadCornerEntry", "SLIT Entry[%" PRIu64 "][%" PRIu64 "] is 0x%" PRIx8 ", expecting value 0x0a.", n - 1, n - 1, entry[INDEX(n - 1, n - 1)]); } for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { uint8_t val1 = entry[INDEX(i, j)], val2 = entry[INDEX(j, i)]; /* Check for distances less than 10 (reserved, no meaning) */ if (val1 < 10) { reserved++; /* Report first 16 errors */ if (reserved < 16) { fwts_failed(fw, LOG_LEVEL_HIGH, "SLITEntryReserved", "SLIT Entry[%" PRIu64 "][%" PRIu64 "]" " is 0x%" PRIx8 " which is a reserved value" " and has no defined meaning", i, j, val1); } } if (val1 != val2) { bad_entry++; /* Report first 16 bad entries */ if (bad_entry < 16) { fwts_failed(fw, LOG_LEVEL_HIGH, "SLITEntryReserved", "SLIT Entry[%" PRIu64 "][%" PRIu64 "]" " is 0x%" PRIx8 " and not the same as " "SLIT Entry[%" PRIu64 "][%" PRIu64 "]" " which is 0x%" PRIx8, i, j, val1, j, i, val2); } } } } if (reserved) fwts_log_info(fw, "Total of %" PRIu64 " entries were using reserved values", reserved); if (bad_entry) fwts_log_info(fw, "Total of %" PRIu64 " entries were not matching " "their diagonal parner element", bad_entry); done: if (passed) fwts_passed(fw, "No issues found in SLIT table."); return FWTS_OK; }
static int acpi_table_sbbr_check_test2(fwts_framework *fw) { int i; bool checked = false; bool dsdt_checked = false; bool ssdt_checked = false; for (i = 0; ; i++) { fwts_acpi_table_info *info; if (fwts_acpi_get_table(fw, i, &info) != FWTS_OK) break; if (info == NULL) continue; checked = true; if (!strcmp(info->name, "DSDT") || !strcmp(info->name, "SSDT")) { fwts_acpi_table_header *hdr; char name[TABLE_NAME_LEN]; bool passed = false; if (!strcmp(info->name, "DSDT")) { dsdt_checked = true; } if (!strcmp(info->name, "SSDT")) { ssdt_checked = true; } hdr = (fwts_acpi_table_header *)info->data; if (acpi_table_check_field(hdr->signature, MIN_SIG)) { snprintf(name, sizeof(name), "%4.4s", hdr->signature); } else { /* Table name not printable, so identify it by the address */ snprintf(name, sizeof(name), "at address 0x%" PRIx64, info->addr); } /* * Tables shouldn't be short, however, they do have at * least 4 bytes with their signature else they would not * have been loaded by this stage. */ if (hdr->length < sizeof(fwts_acpi_table_header)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ACPITableHdrShort", "ACPI Table %s is too short, only %d bytes long. Further " "header checks will be omitted.", name, hdr->length); continue; } /* Warn about empty tables */ if (hdr->length == sizeof(fwts_acpi_table_header)) { fwts_warning(fw, "ACPI Table %s is empty and just contains a table header. Further " "header checks will be omitted.", name); continue; } passed = acpi_table_check_field_test(fw, name, "Signature", hdr->signature, MIN_SIG) & acpi_table_check_field_test(fw, name, "OEM ID", hdr->oem_id, OEM_ID) & acpi_table_check_field_test(fw, name, "OEM Table ID", hdr->oem_tbl_id, OEM_TABLE_ID) & acpi_table_check_field_test(fw, name, "OEM Creator ID", hdr->creator_id, OEM_CREATOR_ID); if (passed) fwts_passed(fw, "Table %s has valid signature and ID strings.", name); } } if (!checked) { fwts_aborted(fw, "Cannot find any ACPI tables."); return FWTS_ABORTED; } if (!dsdt_checked) fwts_failed(fw, LOG_LEVEL_HIGH, "acpi_table_check_test4", "Test DSDT table is NOT implemented."); if (!ssdt_checked) fwts_warning(fw, "SSDT table is NOT implemented."); return FWTS_OK; }
/* * GTDT Generic Timer Description Table */ static int gtdt_test1(fwts_framework *fw) { bool passed = true; uint8_t *ptr, *end_ptr; uint32_t i = 0, n; const fwts_acpi_table_gtdt *gtdt = (const fwts_acpi_table_gtdt *)table->data; fwts_acpi_reserved_bits_check(fw, "GTDT", "Flags", gtdt->virtual_timer_flags, sizeof(gtdt->virtual_timer_flags), 3, 31, &passed); ptr = (uint8_t *)table->data + gtdt->platform_timer_offset; n = gtdt->platform_timer_count; end_ptr = (uint8_t *)table->data + table->length; while ((i < n) && (ptr < end_ptr)) { uint32_t len, j; fwts_acpi_table_gtdt_block *block; fwts_acpi_table_gtdt_block_timer *block_timer; fwts_acpi_table_gtdt_watchdog *watchdog; char field[80]; switch (*ptr) { case 0x00: /* GT Block Structure */ block = (fwts_acpi_table_gtdt_block *)ptr; if (ptr + 20 > end_ptr) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTShortBlock", "GTDT block is too short"); goto done; } if (block->length < 20) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidBlockLength", "GTDT block %" PRIu32 " length has in " "invalid length: %" PRIu32 " bytes", i, block->length); goto done; } if (block->reserved) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidBlockReserved", "GTDT block %" PRIu32 " reserved is " "non-zero, got 0x%" PRIx8, i, block->reserved); } if (block->block_timer_count > 8) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidBlockTimerCount", "GTDT block %" PRIu32 " timer count " "is too large, %" PRIu32 ", must be <= 8", i, block->block_timer_count); break; } len = (block->block_timer_count * 40) + 20; if (len != block->length) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidTimerCountOrLength", "GTDT block %" PRIu32 " timer count %" PRIu32 " and block length %" PRIu32 ", " "expected length of %" PRIu32, i, block->block_timer_count, block->length, len); /* length may be inconsistent, don't trust it so stop here */ goto done; } block_timer = &block->block_timers[0]; for (j = 0; j < block->block_timer_count; j++) { if (((uint8_t *)block_timer + sizeof(*block_timer)) > end_ptr) break; if (block_timer->frame_number > 7) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidGTFrameNumber", "GTDT block frame number is %" PRIu8 ", expecting 0..7", block_timer->frame_number); } if (block_timer->reserved[0] | block_timer->reserved[1] | block_timer->reserved[2]) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTBlockTimerReservedNotZero", "GTDT block %" PRIu32 " timer reserved " "space is not zero, got 0x" "%" PRIx8 "%" PRIx8 "%" PRIx8 " instead", i, block_timer->reserved[0], block_timer->reserved[1], block_timer->reserved[2]); } snprintf(field, sizeof(field), "block %" PRIu32 " physical timer flags", i); fwts_acpi_reserved_bits_check(fw, "GTDT", field, block_timer->phys_timer_flags, sizeof(block_timer->phys_timer_flags), 2, 31, &passed); snprintf(field, sizeof(field), "block %" PRIu32 " virtual timer flags", i); fwts_acpi_reserved_bits_check(fw, "GTDT", field, block_timer->virt_timer_flags, sizeof(block_timer->virt_timer_flags), 2, 31, &passed); snprintf(field, sizeof(field), "block %" PRIu32 " common flags", i); fwts_acpi_reserved_bits_check(fw, "GTDT", field, block_timer->common_flags, sizeof(block_timer->common_flags), 2, 31, &passed); } ptr += block->length; break; case 0x01: /* SBSA Generic Watchdog Timer Structure */ watchdog = (fwts_acpi_table_gtdt_watchdog *)ptr; if (ptr + 28 > end_ptr) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTShortWatchDogTimer", "GTDT SBSA generic watchdog timer %" PRIu32 " is too short", i); goto done; } if (watchdog->length != 28) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidWatchDogTimeLength", "GTDT SBSA generic watchdog timer %" PRIu32 " length has in invalid length: %" PRIu32 " bytes", i, watchdog->length); goto done; } if (watchdog->reserved) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidWatchDogReserved", "GTDT SBSA generic watchdog timer %" PRIu32 " reserved is non-zero, got 0x%" PRIx8, i, watchdog->reserved); } snprintf(field, sizeof(field), "SBSA generic watchdog timer %" PRIu32 " flags", i); fwts_acpi_reserved_bits_check(fw, "GTDT", field, watchdog->watchdog_timer_flags, sizeof(watchdog->watchdog_timer_flags), 3, 31, &passed); ptr += watchdog->length; break; default: passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "GTDTInvalidType", "GTDT platform timer strucuture %" PRIu32 " has an invalid type: 0x%" PRIx8, i, *ptr); /* Can't determine field length, so end of parsing */ goto done; } i++; } done: if (passed) fwts_passed(fw, "No issues found in GTDT table."); return FWTS_OK; }
static int compare_config_space( fwts_framework *fw, const fwts_acpi_mcfg_configuration *config) { uint8_t *mapped_config_space; uint8_t config_space[16]; size_t page_size, n; bool match; char path[PATH_MAX]; FILE *fp; int i; page_size = fwts_page_size(); /* * Sanity check on first config, this is enough to * see if MMIO base is OK or not */ snprintf(path, sizeof(path), "/sys/bus/pci/devices/%4.4" PRIx16 ":00:00.0/config", config->pci_segment_group_number); if ((fp = fopen(path, "r")) == NULL) { fwts_log_warning(fw, "Could not open %s.", path); return FWTS_ERROR; } n = fread(config_space, 1, sizeof(config_space), fp); (void)fclose(fp); if (n != sizeof(config_space)) { fwts_log_warning(fw, "Could only read %zd bytes from %s, expecting %zd.", n, path, sizeof(config_space)); return FWTS_ERROR; } if ((mapped_config_space = fwts_mmap(config->base_address, page_size)) == FWTS_MAP_FAILED) { fwts_log_error(fw, "Cannot mmap PCI config space at 0x%" PRIx64 ".", config->base_address); return FWTS_ERROR; } /* We only need to check if just the config space is readable */ if (fwts_safe_memread(mapped_config_space, sizeof(config_space)) != FWTS_OK) { fwts_log_error(fw, "Cannot read PCI config space at 0x%" PRIx64 ".", config->base_address); (void)fwts_munmap(mapped_config_space, page_size); return FWTS_ERROR; } /* * Need to explicitly do byte comparisons on region * memcmp() fails as this can do 64 bit reads */ for (match = true, i = 0; i < 16; i++) { if (config_space[i] != mapped_config_space[i]) { match = false; break; } } (void)fwts_munmap(mapped_config_space, page_size); if (match) fwts_passed(fw, "PCI config space verified."); else fwts_failed(fw, LOG_LEVEL_MEDIUM, "PCIConfigSpaceBad", "PCI config space appears to not work."); return FWTS_OK; }
/* * HEST Hardware Error Source Table test * ACPI section 18.3.2 "ACPI Error Source" */ static int hest_test1(fwts_framework *fw) { bool passed = true; fwts_acpi_table_hest *hest = (fwts_acpi_table_hest *)table->data; uint8_t *data = (uint8_t *)table->data; ssize_t length = (ssize_t)hest->header.length; uint32_t hest_type_00_count = 0, hest_type_01_count = 0, hest_type_02_count = 0, hest_type_11_count = 0; if (table->length < sizeof(fwts_acpi_table_hest)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooShort", "HEST table too short, expecting %zu bytes, " "instead got %zu bytes", sizeof(fwts_acpi_table_hest), table->length); goto done; } fwts_log_info_verbatim(fw, "HEST Hardware Error Source Table test"); fwts_log_info_verbatim(fw, " Error Source Count: 0x%2.2" PRIx8, hest->error_source_count); fwts_log_nl(fw); data += sizeof(fwts_acpi_table_hest); length -= sizeof(fwts_acpi_table_hest); while (length > 0) { uint16_t *type = (uint16_t *)data; switch (*type) { case 0: hest_check_ia32_arch_machine_check_exception(fw, &length, &data, &passed); hest_type_00_count++; break; case 1: hest_check_ia32_arch_corrected_machine_check(fw, &length, &data, &passed); hest_type_01_count++; break; case 2: hest_check_acpi_table_hest_nmi_error(fw, &length, &data, &passed); hest_type_02_count++; break; case 6: hest_check_pci_express_root_port_aer(fw, &length, &data, &passed); break; case 7: hest_check_pci_express_device_aer(fw, &length, &data, &passed); break; case 8: hest_heck_pci_express_bridge_aer(fw, &length, &data, &passed); break; case 9: hest_check_generic_error_source(fw, &length, &data, &passed); break; case 10: hest_check_generic_error_source_v2(fw, &length, &data, &passed); break; case 11: /* the structure of type 11 is the same as type 1 */ hest_check_ia32_arch_corrected_machine_check(fw, &length, &data, &passed); hest_type_11_count++; break; default: fwts_failed(fw, LOG_LEVEL_HIGH, "HESTInvalidType", "HEST Type 0x%4.4" PRIx16 " is invalid, aborting check", *type); passed = false; length = 0; break; } } if (hest_type_00_count > 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooManyIA32ArchMachineCheckExceptions", "HEST Contained %" PRIu32 " IA32 Architecture " "Machine Check Exception Entries, maximum allowed " "per HEST is just 1.", hest_type_00_count); } if (hest_type_01_count > 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooManyIA32CorrectedMachineChecks", "HEST Contained %" PRIu32 " IA32 Architecture " "Corrected Machine Check Exception Entries, maximum allowed " "per HEST is just 1.", hest_type_01_count); } if (hest_type_02_count > 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooManyNmiErrors", "HEST Contained %" PRIu32 " NMI Error Entries, " "maximum allowed per HEST is just 1.", hest_type_02_count); } if (passed) fwts_passed(fw, "No issues found in HEST table."); done: return FWTS_OK; }
static int uefirttime_test23(fwts_framework *fw) { long ioret; struct efi_setwakeuptime setwakeuptime; uint64_t status; EFI_TIME oldtime; EFI_TIME newtime; struct efi_gettime gettime; EFI_TIME_CAPABILITIES efi_time_cap; struct efi_getwakeuptime getwakeuptime; uint8_t enabled, pending; gettime.Capabilities = &efi_time_cap; gettime.Time = &oldtime; gettime.status = &status; ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime", "Failed to get time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } /* change the hour, add 1 hour*/ addonehour(&oldtime); setwakeuptime.Time = &oldtime; setwakeuptime.status = &status; setwakeuptime.Enabled = true; ioret = ioctl(fd, EFI_RUNTIME_SET_WAKETIME, &setwakeuptime); if (ioret == -1) { if (status == EFI_UNSUPPORTED) { fwts_skipped(fw, "Skipping test, GetWakeupTime runtime " "service is not supported on this platform."); return FWTS_OK; } fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetWakeupTime", "Failed to set wakeup time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } sleep(1); getwakeuptime.Enabled = &enabled; getwakeuptime.Pending = &pending; getwakeuptime.Time = &newtime; getwakeuptime.status = &status; ioret = ioctl(fd, EFI_RUNTIME_GET_WAKETIME, &getwakeuptime); if (ioret == -1) { if (status == EFI_UNSUPPORTED) { fwts_skipped(fw, "Skipping test, GetWakeupTime runtime " "service is not supported on this platform."); return FWTS_OK; } fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetWakeupTime", "Failed to get wakeup time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } if (*getwakeuptime.Enabled != true) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetWakeupTimeEnable", "Failed to set wakeup alarm clock, wakeup timer should be enabled."); return FWTS_ERROR; } if (*getwakeuptime.Pending != false) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetWakeupTimePending", "Get error alarm signle status."); return FWTS_ERROR; } if ((oldtime.Year != newtime.Year) || (oldtime.Month != newtime.Month) || (oldtime.Day != newtime.Day) || (oldtime.Hour != newtime.Hour)) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetWakeupTimeVerify", "Failed to verify wakeup time after change."); return FWTS_ERROR; } setwakeuptime.Enabled = false; ioret = ioctl(fd, EFI_RUNTIME_SET_WAKETIME, &setwakeuptime); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetWakeupTime", "Failed to set wakeup time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } sleep(1); ioret = ioctl(fd, EFI_RUNTIME_GET_WAKETIME, &getwakeuptime); if (ioret == -1) { if (status == EFI_UNSUPPORTED) { fwts_skipped(fw, "Skipping test, GetWakeupTime runtime " "service is not supported on this platform."); return FWTS_OK; } fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetWakeupTime", "Failed to get wakeup time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } if (*getwakeuptime.Enabled != false) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetWakeupTimeEnable", "Failed to set wakeup alarm clock, wakeup timer should be disabled."); return FWTS_ERROR; } fwts_passed(fw, "UEFI runtime service SetWakeupTime interface test passed."); return FWTS_OK; }
static int uefirttime_test4(fwts_framework *fw) { long ioret; struct efi_settime settime; uint64_t status; struct efi_gettime gettime; EFI_TIME oldtime; EFI_TIME newtime; EFI_TIME time; EFI_TIME_CAPABILITIES efi_time_cap; gettime.Capabilities = &efi_time_cap; gettime.Time = &oldtime; gettime.status = &status; ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime", "Failed to get time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } /* refer to UEFI SCT 2.3 test items */ /* change year */ time = oldtime; if (time.Year != 2012) time.Year = 2012; else time.Year = 2016; /* change month */ if (time.Month != 1) time.Month = 1; else time.Month = 12; /* Change daylight */ if (time.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT) time.Daylight &= ~FWTS_UEFI_TIME_ADJUST_DAYLIGHT; else time.Daylight |= FWTS_UEFI_TIME_ADJUST_DAYLIGHT; /* Change time zone */ if (time.TimeZone != 0) time.TimeZone = 0; else /* Unspecified timezone, local time */ time.TimeZone = 2047; settime.Time = &time; settime.status = &status; ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime", "Failed to set time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } sleep(1); gettime.Time = &newtime; ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime", "Failed to get time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } if (!((oldtime.Year == 2012) && (newtime.Year == 2016)) && !((oldtime.Year != 2012) && (newtime.Year == 2012))) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeYear", "Failed to set year with UEFI runtime service."); return FWTS_ERROR; } if (!((oldtime.Month == 1) && (newtime.Month == 12)) && !((oldtime.Month != 1) && (newtime.Month == 1))) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeMonth", "Failed to set month with UEFI runtime service."); return FWTS_ERROR; } if (!((oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT) && (!(newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) && !((!(oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)) && (newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeDaylight", "Failed to set daylight with UEFI runtime service."); return FWTS_ERROR; } if (!((oldtime.TimeZone == 0) && (newtime.TimeZone == 2047)) && !((oldtime.TimeZone != 0) && (newtime.TimeZone == 0))) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeTimezone", "Failed to set timezone with UEFI runtime service."); return FWTS_ERROR; } /* restore the previous time. */ settime.Time = &oldtime; ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime", "Failed to set time with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } fwts_passed(fw, "UEFI runtime service SetTime interface test passed."); return FWTS_OK; }
/* * MCHI Management Controller Host Interface Table * http://www.dmtf.org/sites/default/files/standards/documents/DSP0256_1.0.0.pdf */ static int mchi_test1(fwts_framework *fw) { bool passed = true; fwts_acpi_table_mchi *mchi = (fwts_acpi_table_mchi *)table->data; if (table->length < sizeof(fwts_acpi_table_mchi)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHITooShort", "MCHI table too short, expecting %zu bytes, " "instead got %zu bytes", sizeof(fwts_acpi_table_mchi), table->length); goto done; } #if DUMP_MCHI_TABLE fwts_log_info_verbatum(fw, "MCHI Table:"); fwts_log_info_verbatum(fw, " Interface Type: 0x%2.2" PRIx8, mchi->interface_type); fwts_log_info_verbatum(fw, " Protocol Identifier 0x%2.2" PRIx8, mchi->protocol_identifier); fwts_log_info_verbatum(fw, " Protocol Data: " "0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, mchi->protocol_data[0], mchi->protocol_data[1], mchi->protocol_data[2], mchi->protocol_data[3]); fwts_log_info_verbatum(fw, " " "0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, mchi->protocol_data[4], mchi->protocol_data[5], mchi->protocol_data[6], mchi->protocol_data[7]); fwts_log_info_verbatum(fw, " Interrupt Type: 0x%2.2" PRIx8, mchi->interrupt_type); fwts_log_info_verbatum(fw, " GPE: 0x%2.2" PRIx8, mchi->gpe); fwts_log_info_verbatum(fw, " PCI Device Flag: 0x%2.2" PRIx8, mchi->pci_device_flag); fwts_log_info_verbatum(fw, " Global System Interrupt: 0x%8.8" PRIx32, mchi->global_system_interrupt); fwts_log_info_verbatum(fw, " Base Address:"); fwts_log_info_verbatum(fw, " Address Space ID: 0x%2.2" PRIx8, mchi->base_address.address_space_id); fwts_log_info_verbatum(fw, " Register Bit Width 0x%2.2" PRIx8, mchi->base_address.register_bit_width); fwts_log_info_verbatum(fw, " Register Bit Offset 0x%2.2" PRIx8, mchi->base_address.register_bit_offset); fwts_log_info_verbatum(fw, " Access Size 0x%2.2" PRIx8, mchi->base_address.access_width); fwts_log_info_verbatum(fw, " Address 0x%16.16" PRIx64, mchi->base_address.address); if ((mchi->pci_device_flag & 1) == 1) { fwts_log_info_verbatum(fw, " PCI Segment Group: 0x%2.2" PRIx8, mchi->bytes[0]); fwts_log_info_verbatum(fw, " PCI Bus Number: 0x%2.2" PRIx8, mchi->bytes[1]); fwts_log_info_verbatum(fw, " PCI Device Number: 0x%2.2" PRIx8, mchi->bytes[2]); fwts_log_info_verbatum(fw, " PCI Function Number: 0x%2.2" PRIx8, mchi->bytes[3]); } else { /* Zero -> UIDS */ fwts_log_info_verbatum(fw, " UID Bytes 1-4: " "0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, mchi->bytes[0], mchi->bytes[1], mchi->bytes[2], mchi->bytes[3]); } fwts_log_nl(fw); #endif if ((mchi->interface_type < 2) || (mchi->interface_type > 8)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIInvalidInterfaceType", "MCHI Interface Type is 0x%2.2" PRIx8 " which is reserved, " "allowed values are 0x02..0x08", mchi->interface_type); } if ((mchi->protocol_identifier > 2) && (mchi->protocol_identifier < 255)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIInvalidProtocolIdentifier", "MCHI Protocol Identifier 0x%2.2" PRIx8 " which is reserved, " "allowed values are 0x00 (Unspecifier), 0x01 (MCTP), 0x02 (IPMI) or " "255 (OEM defined)", mchi->protocol_identifier); } if (mchi->interrupt_type & ~0x03) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIInterruptTypeReservedNonZero", "MCHI Interrupt Type 0x%2.2" PRIx8 " has some reserved " "bits [7:2] set, these should be all zero.", mchi->interrupt_type); } if (((mchi->interrupt_type & 0x01) == 0) && (mchi->gpe != 0)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIGpeNonZero", "MCHI GPE is 0x%2.2" PRIx8 " and should be zero " "when bit 0 of the Interrupt Type field is 0 " "(SCI triggered through GPE non-supported)", mchi->gpe); } if (mchi->pci_device_flag & ~0x01) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIPciDeviceFlagReservedNonZero", "MCHI PCI Device Flag 0x%2.2" PRIx8 " has some reserved " "bits [7:1] set, these should be all zero.", mchi->pci_device_flag); } if (((mchi->interrupt_type & 0x02) == 0) && (mchi->global_system_interrupt != 0)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIGsiNonZero", "MCHI Global System Interrupt is 0x%2.2" PRIx8 " and should be zero " "when bit 1 of the Interrupt Type field is 0", mchi->global_system_interrupt); } if ((mchi->base_address.address_space_id != 0x00) && (mchi->base_address.address_space_id != 0x01) && (mchi->base_address.address_space_id != 0x04)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIAddrSpaceIdInvalid", "MCHI Address Space ID is 0x%2.2" PRIx8 " and should be " "0x00 (System Memory), 0x01 (System I/O) or 0x04 (SMBus)", mchi->base_address.address_space_id); } /* SMBus specific checks */ if (mchi->base_address.address_space_id == 0x04) { if ((mchi->pci_device_flag & 0x01) == 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "MCHIPciDeviceFlagInvalid", "MCHI PCI Device Flag is 0x%2.2" PRIx8 " and bit [0] should be 0 for a SMBus Address Space ID", mchi->pci_device_flag); } if (mchi->base_address.register_bit_width != 0) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCHISmbusRegBitWidthNonZero", "MCHI Base Address Register Bit Width is 0x%2.2" PRIx8 " and should be zero for a SMBus Address Space ID", mchi->base_address.register_bit_width); } if (mchi->base_address.register_bit_offset != 0) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCHISmbusRegBitOffsetNonZero", "MCHI Base Address Register Bit Offset is 0x%2.2" PRIx8 " and should be zero for a SMBus Address Space ID", mchi->base_address.register_bit_offset); } if (mchi->base_address.access_width != 1) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCHISmbusRegAddressSizeInvalid", "MCHI Base Address Register Address Size is 0x%2.2" PRIx8 " and should be 1 (byte access) for a SMBus Address Space ID", mchi->base_address.access_width); } if (mchi->base_address.address & ~(0x7fULL)) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCHISmbusAddressInvalid", "MCHI Base Address is 0x%16.16" PRIx64 " and should be 0x00..0x7f a SMBus Address Space ID", mchi->base_address.address); } } /* PCI Device field specific checks */ if (mchi->pci_device_flag & 0x01) { if (mchi->bytes[2] & ~0x1f) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCHIPciDeviceNumberInvalid", "MCHI PCI Device Number is 0x%2.2" PRIx8 " and reserved bits [7:5] should be zero", mchi->bytes[2]); } if (mchi->bytes[3] & ~0x47) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCHIPciFunctionNumberInvalid", "MCHI PCI Function Number is 0x%2.2" PRIx8 " and reserved bits [7] and [5:3] should be zero", mchi->bytes[3]); } } done: if (passed) fwts_passed(fw, "No issues found in MCHI table."); return FWTS_OK; }
/* * Test with setting and deleting another authenticated variable, * after previous test authenticated variable was deleted. */ static int uefirtauthvar_test12(fwts_framework *fw) { long ioret; uint8_t data[getvar_buf_size]; uint64_t getdatasize = sizeof(data); uint64_t status; uint32_t attributestest; size_t i; ioret = setvar(>estguid, attributes, sizeof(AuthVarCreateDiff), AuthVarCreateDiff, &status); if (ioret == -1) { int supcheck = check_fw_support(fw, status); if (supcheck != FWTS_OK) return supcheck; fwts_failed(fw, LOG_LEVEL_HIGH, "UEFISetAuthVarDiff", "Failed to set authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } ioret = getvar(>estguid, &attributestest, &getdatasize, data, &status); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFISetAuthVarDiff", "Failed to get authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } if (getdatasize != sizeof(AuthVarCreateData)) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFISetAuthVarDiff", "Get authenticated variable data size is not the " "same as it set."); return FWTS_ERROR; } for (i = 0; i < sizeof(AuthVarCreateData); i++) { if (data[i] != AuthVarCreateData[i]) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFISetAuthVarDiff", "Get authenticated variable data are not the " "same as it set."); return FWTS_ERROR; } } fwts_passed(fw, "Set authenticated variable created by different key test passed."); ioret = setvar(>estguid, attributes, sizeof(AuthVarDelDiff), AuthVarDelDiff, &status); if (ioret == -1) { int supcheck = check_fw_support(fw, status); if (supcheck != FWTS_OK) return supcheck; fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIDelAuthVarDiff", "Failed to delete authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } ioret = getvar(>estguid, &attributestest, &getdatasize, data, &status); if (ioret == -1) { if (status == EFI_NOT_FOUND) { fwts_passed(fw, "Delete authenticated variable created by different key test passed."); return FWTS_OK; } fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIDelAuthVarDiff", "Failed to get authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIDelAuthVarDiff", "Failed to delete authenticated variable still get the test" "authenticated variable."); return FWTS_ERROR; }
/* * Update the new authenticated variable by using the same key but a new * timestamp and data. */ static int uefirtauthvar_test5(fwts_framework *fw) { long ioret; uint8_t data[getvar_buf_size]; uint64_t getdatasize = sizeof(data); uint64_t status; uint32_t attributestest; size_t i; if (!(data_exist & E_AUTHVARAPPEND)) { fwts_skipped(fw,"The test data, AuthVarAppend, doesn't exist, skip the test."); return FWTS_SKIP; } ioret = setvar(>estguid, attributes, sizeof(AuthVarUpdate), AuthVarUpdate, &status); if (ioret == -1) { int supcheck = check_fw_support(fw, status); if (supcheck != FWTS_OK) return supcheck; fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIUpdateAuthVar", "Failed to update authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } ioret = getvar(>estguid, &attributestest, &getdatasize, data, &status); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIUpdateAuthVar", "Failed to get authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } if (getdatasize != sizeof(AuthVarUpdateData)) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIUpdateAuthVar", "Get authenticated variable data size is not the " "same as it set."); return FWTS_ERROR; } for (i = 0; i < getdatasize; i++) { if (data[i] != AuthVarUpdateData[i]) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIUpdateAuthVar", "Get authenticated variable data are not the " "same as it set."); return FWTS_ERROR; } } data_exist |= E_AUTHVARUPDATE; fwts_passed(fw, "Update authenticated variable tests passed."); return FWTS_OK; }
/* * Execute the normal append operation. */ static int uefirtauthvar_test4(fwts_framework *fw) { long ioret; uint8_t data[getvar_buf_size]; uint64_t getdatasize = sizeof(data); uint64_t status; uint32_t attributestest; size_t i; uint32_t attribappend = attributes | FWTS_UEFI_VARIABLE_APPEND_WRITE; if (!(data_exist & E_AUTHVARCREATE)) { fwts_skipped(fw,"The test variable, AuthVarCreate, doesn't exist, skip the test."); return FWTS_SKIP; } ioret = setvar(>estguid, attribappend, sizeof(AuthVarAppend), AuthVarAppend, &status); if (ioret == -1) { int supcheck = check_fw_support(fw, status); if (supcheck != FWTS_OK) return supcheck; fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIAppendAuthVar", "Failed to append authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } ioret = getvar(>estguid, &attributestest, &getdatasize, data, &status); if (ioret == -1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIAppendAuthVar", "Failed to get authenticated variable with UEFI " "runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } if (getdatasize != (sizeof(AuthVarCreateData) + sizeof(AuthVarAppendData))) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIAppendAuthVar", "Get total authenticated variable data size is not the " "same as it set and appended."); return FWTS_ERROR; } for (i = 0; i < getdatasize; i++) { if (i < sizeof(AuthVarCreateData)) { if (data[i] != AuthVarCreateData[i]) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIAppendAuthVar", "Get authenticated variable data are not the " "same as it set."); return FWTS_ERROR; } } else { if (data[i] != AuthVarAppendData[i - sizeof(AuthVarCreateData)]) { fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIAppendAuthVar", "Get authenticated variable data are not the " "same as it set."); return FWTS_ERROR; } } } data_exist |= E_AUTHVARAPPEND; fwts_passed(fw, "Append authenticated variable tests passed."); return FWTS_OK; }
static int reserv_mem_limits_test(fwts_framework *fw) { bool ok = true; char *region_names; const uint64_t *ranges; reserve_region_t *regions; int offset, len, nr_regions, rc, j; plat_config_t configstruct = {0, 0, 0}; get_config(fw, CONFIG_FILENAME, &configstruct); offset = fdt_path_offset(fw->fdt, root_node); if (offset < 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing", "DT root node %s is missing", root_node); return FWTS_ERROR; } /* Get the number of memory reserved regions */ nr_regions = fwts_dt_stringlist_count(fw, fw->fdt, offset, "reserved-names"); /* Check for the reservd-names property */ region_names = (char *)fdt_getprop(fw->fdt, offset, "reserved-names", &len); if (!region_names) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing", "DT Property reserved-names is missing %s", fdt_strerror(len)); return FWTS_ERROR; } regions = malloc(nr_regions*sizeof(reserve_region_t)); if (!regions) { fwts_skipped(fw, "Unable to allocate memory " "for reserv_region_t structure"); return FWTS_SKIP; } for (j = 0; j < nr_regions; j++) { regions[j].name = strdup(region_names); region_names += strlen(regions[j].name) + 1; } /* Check for the reserved-ranges property */ ranges = fdt_getprop(fw->fdt, offset, "reserved-ranges", &len); if (!ranges) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing", "DT Property reserved-ranges is missing %s", fdt_strerror(len)); rc = FWTS_ERROR; goto out_free_regions; } for (j = 0; j < nr_regions; j++) { regions[j].start = (uint64_t)be64toh(ranges[2 * j]); regions[j].len = (uint64_t)be64toh(ranges[2 * j + 1]); fwts_log_info(fw, "Region name %80s" " start: 0x%08" PRIx64 ", len: 0x%08" PRIx64 "\n", regions[j].name, regions[j].start, regions[j].len); } offset = fdt_path_offset(fw->fdt, reserv_mem_node); if (offset < 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing", "reserve memory node %s is missing", reserv_mem_node); rc = FWTS_ERROR; goto out_free_regions; } /* Validate different cases */ for (j = 0; j < nr_regions; j++) { char *buf = NULL; /* Check for zero offset's */ if (regions[j].start == 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "ZeroStartAddress", "memory region got zero start address"); ok = false; } /* Check for zero region sizes */ if (regions[j].len == 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "ZeroRegionSize", "memory region got zero size"); ok = false; } /* Form the reserved-memory sub nodes for all the regions*/ if (!strstr(regions[j].name, "@")) buf = make_message("%s%s@%lx", reserv_mem_node, regions[j].name, regions[j].start); else buf = make_message("/%s/%s", reserv_mem_node, regions[j].name); if (!buf) { fwts_skipped(fw, "Unable to allocate memory for buffer"); rc = FWTS_SKIP; goto out_free_regions; } /* Check all nodes got created for all the sub regions */ offset = fdt_path_offset(fw->fdt, buf); if (offset < 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing", "reserve memory region node %s is missing", buf); ok = false; } if (skip) continue; /* Validate different Known image fixed sizes here */ if (strstr(regions[j].name, "homer-image")) { if (regions[j].len != configstruct.homer) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "ImageSizeMismatch", "Mismatch in homer-image size, " "expected: 0x%" PRIx64 ", actual: 0x%" PRIx64, configstruct.homer, regions[j].len); ok = false; } else fwts_log_info(fw, "homer-image size is validated"); } if (strstr(regions[j].name, "slw-image")) { if (regions[j].len != configstruct.slw) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "ImageSizeMismatch", "Mismatch in slw-image size, " "expected: 0x%" PRIx64 ", actual: 0x%" PRIx64, configstruct.slw, regions[j].len); ok = false; } else fwts_log_info(fw, "slw-image size is validated"); } if (strstr(regions[j].name, "occ-common-area")) { if (regions[j].len != configstruct.occ_common) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "ImageSizeMismatch", "Mismatch in occ-common-area size, " "expected: 0x%" PRIx64 ", actual: 0x%" PRIx64, configstruct.occ_common, regions[j].len); ok = false; } else fwts_log_info(fw, "occ-common-area size is validated"); } } if (ok) { rc = FWTS_OK; fwts_passed(fw, "Reserved memory validation tests passed"); } else { rc = FWTS_ERROR; fwts_failed(fw, LOG_LEVEL_HIGH, "ReservMemTestFail", "One or few Reserved Memory DT" " validation tests failed"); } out_free_regions: free(regions); return rc; }
static int mcfg_test1(fwts_framework *fw) { int nr, i; fwts_acpi_table_mcfg *mcfg = (fwts_acpi_table_mcfg*)mcfg_table->data; fwts_acpi_mcfg_configuration *config; bool failed = false; ssize_t mcfg_size; const char *memory_map_name; memory_map_name = fwts_memory_map_name(fw->firmware_type); fwts_log_info(fw, "This test tries to validate the MCFG table by comparing the first " "16 bytes in the MMIO mapped config space with the 'traditional' config " "space of the first PCI device (root bridge). The MCFG data is only " "trusted if it is marked reserved in the %s", memory_map_name); fwts_log_nl(fw); if ((memory_map_list = fwts_memory_map_table_load(fw)) == NULL) { /* Not fatal, just means test will be less comprehensive */ fwts_log_warning(fw, "No memory map table found"); } else { fwts_memory_map_table_dump(fw, memory_map_list); fwts_log_nl(fw); } mcfg_size = mcfg_table->length; mcfg_size -= sizeof(fwts_acpi_table_mcfg); if (mcfg_size < 0) { fwts_failed(fw, LOG_LEVEL_HIGH, "MCFGInvalidSize", "Invalid MCFG ACPI table size: got %zd bytes expecting more", mcfg_size + sizeof(fwts_acpi_table_mcfg)); fwts_advice(fw, "MCFG table must be least %zd bytes (header size) with " "multiples of %zd bytes for each MCFG entry.", sizeof(fwts_acpi_table_mcfg), sizeof(fwts_acpi_mcfg_configuration)); return FWTS_ERROR; } nr = mcfg_size / sizeof(fwts_acpi_mcfg_configuration); if (!nr) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "MCFGNoEntries", "No MCFG ACPI table entries"); return FWTS_ERROR; } if (mcfg_size != (ssize_t)(nr * sizeof(fwts_acpi_mcfg_configuration))) { fwts_failed(fw, LOG_LEVEL_HIGH, "MCFGInvalidSize2", "MCFG table is not a multiple of record size"); return FWTS_ERROR; } fwts_log_info(fw, "MCFG table found, size is %zd bytes (excluding header) (%i entries).", mcfg_size, nr); if (mcfg == NULL) { fwts_failed(fw, LOG_LEVEL_HIGH, "MCFGInvalidTable", "Invalid MCFG ACPI table"); return FWTS_ERROR; } if (memory_map_list == NULL) fwts_failed(fw, LOG_LEVEL_MEDIUM, "MMapUnreadable", "Cannot check MCFG MMIO space against memory map table: could not read memory map table."); config = &mcfg->configuration[0]; for (i = 0; i < nr; i++, config++) { fwts_log_info_verbatim(fw, "Configuration Entry #%d:", i); fwts_log_info_verbatim(fw, " Base Address : 0x%" PRIx64, config->base_address); fwts_log_info_verbatim(fw, " Segment : %" PRIu8, config->pci_segment_group_number); fwts_log_info_verbatim(fw, " Start bus : %" PRIu8, config->start_bus_number); fwts_log_info_verbatim(fw, " End bus : %" PRIu8, config->end_bus_number); if ((memory_map_list != NULL) && (!fwts_memory_map_is_reserved(memory_map_list, config->base_address))) { fwts_failed(fw, LOG_LEVEL_LOW, "MCFGMMIONotReserved", "MCFG MMIO config space at 0x%" PRIx64 " is not reserved in the memory map table", config->base_address); fwts_advice(fw, "The PCI Express specification states that the " "PCI Express configuration space should " "be defined in the MCFG table and *maybe* " "optionally defined in the %s if ACPI MCFG is " "present. Linux checks if the region is reserved " "in the memory map table and will reject the " "MMCONFIG if there is a discrepency between MCFG " "and the memory map table for the PCI Express region. " "[See arch/x86/pci/mmconfig-shared.c pci_mmcfg_reject_broken()]. " "It is recommended that this is defined in the " "%s table for Linux.", memory_map_name, memory_map_name); failed = true; } } if (!failed) fwts_passed(fw, "MCFG MMIO config space is reserved in memory map table."); return FWTS_OK; }
static int crs_test1(fwts_framework *fw) { fwts_list *klog; int day, mon, year; char *cmdline; if ((cmdline = fwts_get("/proc/cmdline")) == NULL) { fwts_log_error(fw, "Cannot read /proc/cmdline"); return FWTS_ERROR; } if (crs_get_bios_date(fw, &day, &mon, &year) != FWTS_OK) { fwts_log_error(fw, "Cannot determine age of BIOS."); free(cmdline); return FWTS_ERROR; } if ((klog = fwts_klog_read()) == NULL) { fwts_log_error(fw, "Cannot read kernel log."); free(cmdline); return FWTS_ERROR; } if (fwts_klog_regex_find(fw, klog, "PCI: Ignoring host bridge windows from ACPI;") > 0) { if (strstr(cmdline, "pci=nocrs") != NULL) { fwts_skipped(fw, "Kernel was booted with pci=nocrs, Ignoring host bridge windows _CRS settings from ACPI, skipping test."); } else { if (year == 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "BIOSTooOld", "The kernel could not determine the BIOS age " "and has assumed that your BIOS is too old to correctly " "specify the host bridge MMIO aperture using _CRS."); fwts_log_advice(fw, "You can override this by booting with \"pci=use_crs\"."); } else if (year < 2008) { fwts_passed(fw, "The kernel has detected an old BIOS (%d/%d/%d) " "and has assumed that your BIOS is too old to correctly " "specify the host bridge MMIO aperture using _CRS.", mon, day, year); fwts_log_advice(fw, "You can override this by booting with \"pci=use_crs\"."); } else { fwts_failed(fw, LOG_LEVEL_MEDIUM, "HostBridgeWindows", "The kernel is ignoring host bridge windows from ACPI for some unknown reason. " "pci=nocrs has not been used as a boot parameter and the BIOS may be recent enough " "to support this (%d/%d/%d)", mon, day, year); } } } else if (fwts_klog_regex_find(fw, klog, "PCI: Using host bridge windows from ACPI;") > 0) { if (strstr(cmdline, "pci=use_crs") != NULL) { if (year == 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "BIOSNoReleaseDate", "The BIOS does not seem to have release date, hence pci=use_crs was required."); } else if (year < 2008) { fwts_passed(fw, "The BIOS is relatively old (%d/%d/%d) and hence pci=use_crs was required to " "enable host bridge windows _CRS settings from ACPI.", mon, day, year); } else { fwts_failed(fw, LOG_LEVEL_LOW, "BIOSSupportBridgeWindows", "Kernel was booted with pci=use_crs but this may be uncessary as " "the BIOS is new enough to support automatic bridge windows configuring using _CRS from ACPI. " "However, the workaround may be necessary because _CRS is incorrect or not implemented in the " "DSDT."); } } else { fwts_passed(fw, "The kernel has detected a BIOS newer than the end of 2007 (%d/%d/%d) " "and has assumed that your BIOS can correctly " "specify the host bridge MMIO aperture using _CRS. If this does not work " "correctly you can override this by booting with \"pci=nocrs\".", mon, day, year); } } else { fwts_skipped(fw, "Cannot find host bridge message in kernel log, skipping test."); } fwts_list_free(klog, free); free(cmdline); return FWTS_OK; }
static int pcct_test1(fwts_framework *fw) { fwts_acpi_table_pcct *pcct = (fwts_acpi_table_pcct*) table->data; fwts_acpi_table_pcct_subspace_header *pcct_sub; size_t offset; bool passed = true; fwts_log_info_verbatim(fw, "PCC Table:"); fwts_log_info_verbatim(fw, " Flags: 0x%8.8" PRIx32, pcct->flags); fwts_log_info_verbatim(fw, " Reserved: 0x%16.16" PRIx64, pcct->reserved); fwts_log_nl(fw); fwts_acpi_reserved_bits_check(fw, "PCCT", "Flags", pcct->flags, sizeof(pcct->flags), 1, 31, &passed); fwts_acpi_reserved_zero_check(fw, "PCCT", "Reserved", pcct->reserved, sizeof(pcct->reserved), &passed); offset = sizeof(fwts_acpi_table_pcct); pcct_sub = (fwts_acpi_table_pcct_subspace_header *) (table->data + offset); while (offset < table->length) { fwts_log_info_verbatim(fw, " PCC Subspace Structure:"); fwts_log_info_verbatim(fw, " Type: 0x%2.2" PRIx8, pcct_sub->type); fwts_log_info_verbatim(fw, " Length: 0x%2.2" PRIx8, pcct_sub->length); if (pcct_sub->type == 0) { fwts_acpi_table_pcct_subspace_type_0 *subspace = (fwts_acpi_table_pcct_subspace_type_0 *) pcct_sub; if(!subspace_length_equal(fw, 0, sizeof(fwts_acpi_table_pcct_subspace_type_0), pcct_sub->length)) { passed = false; break; } generic_comm_test(fw, subspace, &passed); } else if (pcct_sub->type == 1) { fwts_acpi_table_pcct_subspace_type_1 *subspace = (fwts_acpi_table_pcct_subspace_type_1 *) pcct_sub; if(!subspace_length_equal(fw, 0, sizeof(fwts_acpi_table_pcct_subspace_type_1), pcct_sub->length)) { passed = false; break; } hw_reduced_comm_test_type1(fw, subspace, &passed); } else if (pcct_sub->type == 2) { fwts_acpi_table_pcct_subspace_type_2 *subspace = (fwts_acpi_table_pcct_subspace_type_2 *) pcct_sub; if(!subspace_length_equal(fw, 0, sizeof(fwts_acpi_table_pcct_subspace_type_2), pcct_sub->length)) { passed = false; break; } hw_reduced_comm_test_type2(fw, subspace, &passed); } else if (pcct_sub->type == 3) { fwts_acpi_table_pcct_subspace_type_3_4 *subspace = (fwts_acpi_table_pcct_subspace_type_3_4 *) pcct_sub; if(!subspace_length_equal(fw, 0, sizeof(fwts_acpi_table_pcct_subspace_type_3_4), pcct_sub->length)) { passed = false; break; } extended_pcc_test(fw, subspace, &passed); } else if (pcct_sub->type == 4) { fwts_acpi_table_pcct_subspace_type_3_4 *subspace = (fwts_acpi_table_pcct_subspace_type_3_4 *) pcct_sub; if(!subspace_length_equal(fw, 0, sizeof(fwts_acpi_table_pcct_subspace_type_3_4), pcct_sub->length)) { passed = false; break; } if (!(pcct->flags & 0x01)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "PCCTBadFlags", "PCCT Platform Interrupt in flags must be set when subspace " "type 4 is present, got 0x%8.8" PRIx32 " instead", pcct->flags); } extended_pcc_test(fw, subspace, &passed); } else { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "PCCTBadSubType", "PCCT Subspace Structure supports type 0..4, got " "0x%2.2" PRIx8 " instead", pcct_sub->type); break; } fwts_log_nl(fw); offset += pcct_sub->length; pcct_sub = (fwts_acpi_table_pcct_subspace_header *) (table->data + offset); } if (passed) fwts_passed(fw, "No issues found in PCC table."); return FWTS_OK; }
static int spcr_test1(fwts_framework *fw) { char *str; uint32_t reserved1; bool reserved = false; bool pci = true; bool passed = true; /* * Assuming revision 2, full list from * http://go.microsoft.com/fwlink/p/?LinkId=234837) */ switch (spcr->interface_type) { case 0x00: str = "16550 compatible"; break; case 0x01: str = "16450 compatible"; break; case 0x03: str = "ARM PL011 UART"; break; case 0x02: case 0x04 ... 0x0c: str = "Reserved (Do not Use)"; reserved = true; break; case 0x0d: str = "(deprecated) ARM SBSA"; break; case 0x0e: str = "ARM SBSA Generic UART"; break; case 0x0f: str = "ARM DCC"; break; case 0x10: str = "BCM2835"; break; default: str = "Reserved"; reserved = true; break; } fwts_log_info_verbatim(fw, "Serial Interface: %s", str); if (reserved) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRInterfaceReserved", "SPCR Serial interface type 0x%2.2" PRIx8 " is a reserved interface", spcr->interface_type); } reserved1 = spcr->reserved1[0] + (spcr->reserved1[1] << 8) + (spcr->reserved1[2] << 16); fwts_acpi_reserved_zero_check(fw, "SPCR", "Reserved1", reserved1, sizeof(reserved1), &passed); if (spcr->interrupt_type == 0) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRUnknownInterruptType", "SPCR interrupt type field is zero, expecting support bits to be set"); } if (spcr->interrupt_type & 0xf0) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRIllegalReservedInterruptType", "SPCR interrupt type reserved bits are non-zero zero, got 0x%" PRIx8, spcr->interrupt_type); } /* Check PC-AT compatible UART IRQs */ if (spcr->interrupt_type & 1) { switch (spcr->irq) { case 2 ... 7: case 9 ... 12: case 14 ... 15: break; default: passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRIllegalIRQ", "SPCR PC-AT compatible IRQ 0x%" PRIx8 " is invalid", spcr->irq); break; } } reserved = false; switch (spcr->baud_rate) { case 0x03: str = "9600"; break; case 0x04: str = "19200"; break; case 0x06: str = "57600"; break; case 0x07: str = "115200"; break; default: str = "Reserved"; reserved = true; } fwts_log_info_verbatim(fw, "Baud Rate: %s", str); if (reserved) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRBaudRateReserved", "SPCR Serial baud rate type 0x%2.2" PRIx8 " is a reserved baud rate", spcr->baud_rate); } if (spcr->parity != 0) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRReservedValueUsed", "SPCR Parity field must be zero, got 0x%2.2" PRIx8 " instead", spcr->parity); } if (spcr->stop_bits != 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRReservedValueUsed", "SPCR Stop field must be 1, got 0x%2.2" PRIx8 " instead", spcr->stop_bits); } fwts_acpi_reserved_bits_check(fw, "SPCR", "Flow control", spcr->flow_control, sizeof(spcr->flow_control), 3, 7, &passed); reserved = false; switch (spcr->terminal_type) { case 0x00: str = "VT100"; break; case 0x01: str = "VT100+"; break; case 0x02: str = "VT-UTF8"; break; case 0x03: str = "ANSI"; break; default: str = "Reserved"; reserved = true; } fwts_log_info_verbatim(fw, "Terminal Type: %s", str); if (reserved) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRTerminalTypeReserved", "SPCR terminal type type 0x%2.2" PRIx8 " is a reserved terminal type", spcr->terminal_type); } fwts_acpi_reserved_zero_check(fw, "SPCR", "Reserved2", spcr->reserved2, sizeof(spcr->reserved2), &passed); /* According to the spec, these values indicate NOT a PCI device */ if ((spcr->pci_device_id == 0xffff) && (spcr->pci_vendor_id == 0xffff) && (spcr->pci_bus_number == 0) && (spcr->pci_device_number == 0) && (spcr->pci_function_number == 0)) pci = false; /* Now validate all pci specific fields if not-PCI enabled */ if (pci) { if (spcr->pci_device_id == 0xffff) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRPciDeviceID", "SPCR PCI device ID is 0x%4.4" PRIx16 ", expecting non-0xffff for PCI device", spcr->pci_device_id); } if (spcr->pci_vendor_id == 0xffff) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRPciVendorID", "SPCR PCI vendor ID is 0x%4.4" PRIx16 ", expecting non-0xffff for non-PCI device", spcr->pci_vendor_id); } if ((spcr->pci_flags & 1) == 0) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "SPCRPciFlagsBit0", "SPCR PCI flags compatibility bit 0 is %" PRIx32 ", expecting 1 for PCI device", spcr->pci_flags & 1); } } fwts_acpi_reserved_bits_check(fw, "SPCR", "PCI Flags", spcr->pci_flags, sizeof(spcr->pci_flags), 1, 31, &passed); fwts_acpi_reserved_zero_check(fw, "SPCR", "Reserved3", spcr->reserved3, sizeof(spcr->reserved3), &passed); if (passed) fwts_passed(fw, "No issues found in SPCR table."); return FWTS_OK; }
/* * UEFI ACPI DATA Table * See UEFI specification Appendix O. */ static int uefi_test1(fwts_framework *fw) { fwts_acpi_table_uefi *uefi = (fwts_acpi_table_uefi *)table->data; bool passed = true; uint32_t i; char guid[37]; /* * GUID for SMM Communication ACPI Table * {0xc68ed8e2, 0x9dc6, 0x4cbd, 0x9d, 0x94, 0xdb, 0x65, 0xac, 0xc5, 0xc3, 0x32} */ static const uint8_t guid_smm[16] = { 0xe2, 0xd8, 0x8e, 0xc6, 0xc6, 0x9d, 0xbd, 0x4c, 0x9d, 0x94, 0xdb, 0x65, 0xac, 0xc5, 0xc3, 0x32 }; /* Enough length for the uefi table? */ if (!fwts_acpi_table_length_check(fw, "UEFI", table->length, sizeof(fwts_acpi_table_uefi))) { passed = false; goto done; } fwts_guid_buf_to_str(uefi->uuid, guid, sizeof(guid)); fwts_log_info_verbatim(fw, "UEFI ACPI Data Table:"); fwts_log_info_verbatim(fw, " Identifier: %s", guid); fwts_log_info_verbatim(fw, " DataOffset: 0x%4.4" PRIx16, uefi->dataoffset); /* Sanity check the dataoffset */ if (uefi->dataoffset > table->length) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIDataOffset", "Invalid UEFI DataOffset, exceed the whole table length " "%zu bytes, instead got %" PRIu16 " offset bytes" , table->length, uefi->dataoffset); } /* check the GUID for SMM Communication ACPI table */ if (memcmp(uefi->uuid, guid_smm, 16) == 0) { fwts_acpi_table_uefi_smmcomm *uefi_smmcomm = (fwts_acpi_table_uefi_smmcomm *)table->data; /* chekc the dataoffset for SMM Comm table */ if (uefi_smmcomm->boot.dataoffset != 54) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIDataOffset", "Invalid UEFI DataOffset for SMM Communication table, " "DataOffset should be 54, instead got %" PRIu16 " offset bytes" , uefi_smmcomm->boot.dataoffset); } fwts_log_info_verbatim(fw, " SW SMI Number: 0x%8.8" PRIx32, uefi_smmcomm->sw_smi_number); fwts_log_info_verbatim(fw, " Buffer Ptr Address: 0x%16.16" PRIx64, uefi_smmcomm->buf_ptr_addr); } else { /* dump the remaining data */ fwts_log_info_verbatim(fw, " Data:"); for (i = 0; i < (table->length - uefi->dataoffset) ; i += 16) { int left = table->length - uefi->dataoffset -i; char buffer[128]; fwts_dump_raw_data(buffer,sizeof(buffer), uefi->data + i, i, left > 16 ? 16 : left); fwts_log_info_verbatim(fw, "%s", buffer); } } done: if (passed) fwts_passed(fw, "No issues found in UEFI table."); return FWTS_OK; }
static int acpi_table_check_test1(fwts_framework *fw) { int i; bool checked = false; for (i = 0; ; i++) { fwts_acpi_table_info *info; fwts_acpi_table_header *hdr; char name[50]; bool passed; if (fwts_acpi_get_table(fw, i, &info) != FWTS_OK) break; if (info == NULL) continue; checked = true; /* RSDP and FACS are special cases, skip these */ if (!strcmp(info->name, "RSDP") || !strcmp(info->name, "FACS")) continue; hdr = (fwts_acpi_table_header *)info->data; if (acpi_table_check_field(hdr->signature, 4)) { snprintf(name, sizeof(name), "%4.4s", hdr->signature); } else { /* Table name not printable, so identify it by the address */ snprintf(name, sizeof(name), "at address 0x%" PRIx64, info->addr); } /* * Tables shouldn't be short, however, they do have at * least 4 bytes with their signature else they would not * have been loaded by this stage. */ if (hdr->length < sizeof(fwts_acpi_table_header)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ACPITableHdrShort", "ACPI Table %s is too short, only %d bytes long. Further " "header checks will be omitted.", name, hdr->length); continue; } /* Warn about empty tables */ if (hdr->length == sizeof(fwts_acpi_table_header)) { fwts_warning(fw, "ACPI Table %s is empty and just contains a table header. Further " "header checks will be omitted.", name); continue; } passed = acpi_table_check_field_test(fw, name, "Signature", hdr->signature, 4) & acpi_table_check_field_test(fw, name, "OEM ID", hdr->oem_id, 6) & acpi_table_check_field_test(fw, name, "OEM Table ID", hdr->oem_tbl_id, 8) & acpi_table_check_field_test(fw, name, "OEM Creator ID", hdr->creator_id, 4); if (passed) fwts_passed(fw, "Table %s has valid signature and ID strings.", name); } if (!checked) fwts_aborted(fw, "Cannot find any ACPI tables."); return FWTS_OK; }
static int s4_test_multiple(fwts_framework *fw) { int i; int klog_errors = 0; int hw_errors = 0; int pm_errors = 0; int klog_oopses = 0; int klog_warn_ons = 0; int awake_delay = s4_min_delay * 1000; int delta = (int)(s4_delay_delta * 1000.0); int tracing_buffer_size = -1; int ret = FWTS_OK; bool retried = false; #if FWTS_ENABLE_LOGIND #if !GLIB_CHECK_VERSION(2,35,0) /* This is for backward compatibility with old glib versions */ g_type_init(); #endif #endif if (s4_multiple == 1) fwts_log_info(fw, "Defaulted to run 1 test, run --s4-multiple=N to run more S4 cycles\n"); for (i = 0; i < s4_multiple; i++) { struct timeval tv; int failed_alloc_image = 0; int percent = (i * 100) / s4_multiple; fwts_log_info(fw, "S4 cycle %d of %d\n",i+1,s4_multiple); if (s4_hibernate(fw, &klog_errors, &hw_errors, &pm_errors, &klog_oopses, &klog_warn_ons, &failed_alloc_image, percent) != FWTS_OK) { fwts_log_error(fw, "Aborting S4 multiple tests."); return FWTS_ERROR; } /* Sometimes we just fail at the first S4 cycle, so shrink tracing buffer size and retry */ if (failed_alloc_image) { if (fwts_get_int(FWTS_TRACING_BUFFER_SIZE, &tracing_buffer_size) != FWTS_OK) { fwts_log_error(fw, "Could not get size from %s.", FWTS_TRACING_BUFFER_SIZE); } else { if ((!retried) && (tracing_buffer_size > 4096)) { retried = true; fwts_failed(fw, LOG_LEVEL_HIGH, "TracingBufferTooBig", "/sys/kernel/debug/tracing/buffer_size_kb is set to %d Kbytes which " "may cause hibernate to fail. Programs such as ureadahead may have " "set this enable fast boot and not freed up the tracing buffer.", tracing_buffer_size); fwts_log_info(fw, "Setting tracing buffer size to 1K for subsequent tests."); fwts_set("1", FWTS_TRACING_BUFFER_SIZE); failed_alloc_image = 0; if (s4_hibernate(fw, &klog_errors, &hw_errors, &pm_errors, &klog_oopses, &klog_warn_ons, &failed_alloc_image, percent) != FWTS_OK) { fwts_log_error(fw, "Aborting S4 multiple tests."); ret = FWTS_ABORTED; break; }; if (failed_alloc_image) { ret = FWTS_ABORTED; break; } } } } if (!s4_device_check) { char buffer[80]; int j; tv.tv_sec = 0; tv.tv_usec = (awake_delay % 1000)*1000; select(0, NULL, NULL, NULL, &tv); for (j = 0; j < awake_delay / 1000; j++) { snprintf(buffer, sizeof(buffer), "(Waiting %d/%d seconds)", j + 1, awake_delay / 1000); fwts_progress_message(fw, percent, buffer); sleep(1); } awake_delay += delta; if (awake_delay > (s4_max_delay * 1000)) awake_delay = s4_min_delay * 1000; } } if (tracing_buffer_size > 0) { char tmp[32]; /* Restore tracking buffer size */ snprintf(tmp, sizeof(tmp), "%d", tracing_buffer_size); fwts_set(tmp, FWTS_TRACING_BUFFER_SIZE); } if (klog_errors > 0) fwts_log_info(fw, "Found %d errors in kernel log.", klog_errors); else fwts_passed(fw, "No kernel log errors detected."); if (pm_errors > 0) fwts_log_info(fw, "Found %d PM related hibernate issues.", pm_errors); else fwts_passed(fw, "No PM related hibernate issues detected."); if (hw_errors > 0) fwts_log_info(fw, "Found %d device errors.", hw_errors); else fwts_passed(fw, "No device errors detected."); if (klog_oopses > 0) fwts_log_info(fw, "Found %d kernel oopses.", klog_oopses); else fwts_passed(fw, "No kernel oopses detected."); if (klog_warn_ons > 0) fwts_log_info(fw, "Found %d kernel WARN_ON warnings.", klog_warn_ons); else fwts_passed(fw, "No kernel WARN_ON warnings detected."); /* Really passed or failed? */ if ((klog_errors + pm_errors + hw_errors + klog_oopses) > 0) { fwts_log_info(fw, "Found %d errors and %d oopses doing %d hibernate/resume cycle(s).", klog_errors + pm_errors + hw_errors, klog_oopses, s4_multiple); } else fwts_passed(fw, "Found no errors and no oopses doing %d hibernate/resume cycle(s).", s4_multiple); return ret; }
/* * DRTM D-RTM Resources Table */ static int drtm_test1(fwts_framework *fw) { fwts_acpi_table_drtm *drtm = (fwts_acpi_table_drtm*) table->data; fwts_acpi_table_drtm_vtl *drtm_vtl; fwts_acpi_table_drtm_rtl *drtm_rtl; fwts_acpi_table_drtm_dps *drtm_dps; bool passed = true; uint32_t offset; uint32_t i; fwts_log_info_verbatim(fw, "DRTM D-RTM Resources Table:"); fwts_log_info_verbatim(fw, " DL_Entry_Base: 0x%16.16" PRIx64, drtm->entry_base_address); fwts_log_info_verbatim(fw, " DL_Entry_Length: 0x%16.16" PRIx64, drtm->entry_length); fwts_log_info_verbatim(fw, " DL_Entry32: 0x%8.8" PRIx32, drtm->entry_address32); fwts_log_info_verbatim(fw, " DL_Entry64: 0x%16.16" PRIx64, drtm->entry_address64); fwts_log_info_verbatim(fw, " DLME_Exit: 0x%16.16" PRIx64, drtm->exit_address); fwts_log_info_verbatim(fw, " Log_Area_Start: 0x%16.16" PRIx64, drtm->log_area_address); fwts_log_info_verbatim(fw, " Log_Area_Length: 0x%8.8" PRIx32, drtm->log_area_length); fwts_log_info_verbatim(fw, " Architecture_Dependent: 0x%16.16" PRIx64, drtm->arch_dependent_address); fwts_log_info_verbatim(fw, " DRT_Flags: 0x%8.8" PRIx32, drtm->flags); fwts_acpi_reserved_bits_check(fw, "DRTM", "DRT_Flags", drtm->flags, sizeof(drtm->flags), 4, 31, &passed); fwts_log_nl(fw); offset = sizeof(fwts_acpi_table_drtm); drtm_vtl = (fwts_acpi_table_drtm_vtl *) (table->data + offset); fwts_log_info_verbatim(fw, " VTL_Length: 0x%8.8" PRIx32, drtm_vtl->validated_table_count); offset += sizeof(drtm_vtl->validated_table_count); if (drtm->header.length < offset + sizeof(uint64_t) * drtm_vtl->validated_table_count) { fwts_failed(fw, LOG_LEVEL_HIGH, "DRTMOutOfBound", "DRTM's length is too small to contain all fields"); goto error; } for (i = 0; i < drtm_vtl->validated_table_count; i++) { fwts_log_info_verbatim(fw, " Validated_Tables: 0x%16.16" PRIx64, drtm_vtl->validated_tables[i]); offset += sizeof(drtm_vtl->validated_tables[i]); } fwts_log_nl(fw); drtm_rtl = (fwts_acpi_table_drtm_rtl *) (table->data + offset); fwts_log_info_verbatim(fw, " RL_Length: 0x%8.8" PRIx32, drtm_rtl->resource_count); offset += sizeof(drtm_rtl->resource_count); if (drtm->header.length < offset + sizeof(fwts_acpi_drtm_resource) * drtm_rtl->resource_count) { fwts_failed(fw, LOG_LEVEL_HIGH, "DRTMOutOfBound", "DRTM's length is too small to contain all fields"); goto error; } for (i = 0; i < drtm_rtl->resource_count; i++) { fwts_acpi_drtm_resource *resource = (fwts_acpi_drtm_resource *) (table->data + offset); uint64_t size; size = resource->size[0] + ((uint64_t) resource->size[1] << 8) + ((uint64_t) resource->size[2] << 16) + ((uint64_t) resource->size[3] << 24) + ((uint64_t) resource->size[4] << 32) + ((uint64_t) resource->size[5] << 40) + ((uint64_t) resource->size[6] << 48); fwts_log_info_verbatim(fw, " Resource Size: 0x%16.16" PRIx64, size); fwts_log_info_verbatim(fw, " Resource Type: 0x%2.2" PRIx8, resource->type); fwts_log_info_verbatim(fw, " Resource Address: 0x%16.16" PRIx64, resource->address); if (resource->type & 0x7C) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "DRTMBadResourceType", "DRTM Resource Type Bits [6:2] are reserved, got 0x%2.2" PRIx8 " instead", resource->type); } offset += sizeof(fwts_acpi_drtm_resource); fwts_log_nl(fw); } drtm_dps = (fwts_acpi_table_drtm_dps *) (table->data + offset); fwts_log_info_verbatim(fw, " DPS_Length: 0x%8.8" PRIx32, drtm_dps->dps_id_length); if (drtm->header.length < offset + sizeof(fwts_acpi_table_drtm_dps)) { fwts_failed(fw, LOG_LEVEL_HIGH, "DRTMOutOfBound", "DRTM's length is too small to contain all fields"); goto error; } for (i = 0; i < sizeof(drtm_dps->dps_id); i++) { fwts_log_info_verbatim(fw, " DLME Platform Id: 0x%2.2" PRIx8, drtm_dps->dps_id[i]); } fwts_log_nl(fw); if (passed) fwts_passed(fw, "No issues found in DRTM table."); error: return FWTS_OK; }
static int pstate_limits_test(fwts_framework *fw) { int pstate_min, pstate_max, pstates[MAX_PSTATES]; bool ok = true; int nr_pstates, offset, len, ret, i; switch (proc_gen) { case proc_gen_p8: cmp_pstates = cmp_negative_pstates; break; case proc_gen_p9: cmp_pstates = cmp_positive_pstates; break; default: fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip", "Unknown processor generation %d", proc_gen); return FWTS_ERROR; } offset = fdt_path_offset(fw->fdt, power_mgt_path); if (offset < 0) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing", "power management node %s is missing", power_mgt_path); return FWTS_ERROR; } ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-min", &pstate_min); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property ibm,pstate-min %s", fdt_strerror(pstate_min)); return FWTS_ERROR; } ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-max", &pstate_max); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property ibm,pstate-max %s", fdt_strerror(pstate_max)); return FWTS_ERROR; } ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,pstate-ids", pstates, &len); if (ret != FWTS_OK) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError", "Failed to read property ibm,pstate-ids %s", fdt_strerror(len)); return FWTS_ERROR; } nr_pstates = abs(pstate_max - pstate_min) + 1; fwts_log_info(fw, "Pstates info: " "Pstate min: %d " "Pstate max: %d " "nr_pstates: %d " "Pstate ID's: ", pstate_min, pstate_max, nr_pstates); for (i = 0; i < nr_pstates; i++) fwts_log_info(fw, " %d ", pstates[i]); if (nr_pstates <= 1) fwts_log_warning(fw, "Pstates range %d is not valid", nr_pstates); if (proc_gen == proc_gen_p8 && nr_pstates > 128) fwts_log_warning(fw, "More than 128 pstates found,nr_pstates = %d", nr_pstates); if (proc_gen == proc_gen_p9 && nr_pstates > 255) fwts_log_warning(fw, "More than 255 pstates found,nr_pstates = %d", nr_pstates); if (len != nr_pstates) fwts_log_warning(fw, "Wrong number of pstates." "Expected %d pstates, found %d pstates", nr_pstates, len); for (i = 0; i < nr_pstates; i++) { if (cmp_pstates(pstate_max, pstates[i]) < 0) { fwts_log_warning(fw, "Invalid Pstate id %d " "greater than max pstate %d", pstates[i], pstate_max); ok = false; } if (cmp_pstates(pstates[i], pstate_min) < 0) { fwts_log_warning(fw, "Invalid Pstate id %d " "lesser than min pstate %d", pstates[i], pstate_min); ok = false; } } /* Pstates should be in monotonic descending order */ for (i = 0; i < nr_pstates; i++) { if ((i == 0) && (cmp_pstates(pstates[i], pstate_max) != 0)) { fwts_log_warning(fw, "Pstates mismatch: " "Expected Pmax %d," "Actual Pmax %d", pstate_max, pstates[i]); ok = false; } else if ((i == nr_pstates - 1) && (cmp_pstates(pstates[i], pstate_min) != 0)) { fwts_log_warning(fw, "Pstates mismatch: " "Expected Pmin %d," "Actual Pmin %d", pstate_min, pstates[i]); ok = false; } else if (i != 0 && i != nr_pstates) { int previous_pstate; previous_pstate = pstates[i-1]; if (cmp_pstates(pstates[i], previous_pstate) > 0) { fwts_log_warning(fw, "Non monotonicity ...," "Pstate %d greater then" " previous Pstate %d", pstates[i], previous_pstate); ok = false; } } } if (!ok) { fwts_failed(fw, LOG_LEVEL_MEDIUM, "CPUPstateLimitsTestFail", "One or few CPU Pstates DT validation tests failed"); return FWTS_ERROR; } fwts_passed(fw, "CPU Frequency pstates are validated"); return FWTS_OK; }
static int dt_sysinfo_check_ref_plat_compatible(fwts_framework *fw) { int node, compat_len, model_len; node = fdt_path_offset(fw->fdt, "/"); if (node < 0) { fwts_failed(fw, LOG_LEVEL_HIGH, "DTRootNodeMissing", "root device tree node is missing"); return FWTS_ERROR; } if (fdt_node_check_compatible(fw->fdt, node, op_powernv)) { fwts_failed(fw, LOG_LEVEL_HIGH, "DTCompatibleMissing", "DeviceTree failed validation, could not find" " the \"compatible\" property of \"%s\" in the " "root of the device tree", "ibm,powernv"); return FWTS_ERROR; } else { const char *model_buf, *compat_buf; char *orig_model_buf, *tmp_model_buf; compat_buf = fdt_getprop(fw->fdt, node, "compatible", &compat_len); model_buf = fdt_getprop(fw->fdt, node, "model", &model_len); if (!model_buf || !compat_buf) { fwts_failed(fw,LOG_LEVEL_HIGH, "DTSysInfoCheck", "Cannot read the properties for OpenPOWER" " Reference Compatible check"); return FWTS_ERROR; } /* need modifiable memory */ /* save original ptr to free */ tmp_model_buf = orig_model_buf = strdup(model_buf); if (!tmp_model_buf) { fwts_failed(fw, LOG_LEVEL_HIGH, "DTSysInfoCheck", "Unable to get memory for model" " compare for OpenPOWER" " Reference Compatible check"); return FWTS_ERROR; } tmp_model_buf = hidewhitespace(tmp_model_buf); if (!(strcmp(model_buf, tmp_model_buf) == 0)) { fwts_warning(fw, "DTSysInfoCheck" " See further advice in the log."); fwts_log_nl(fw); fwts_log_info_verbatim(fw, "DTSysInfoCheck" " Check the root \"model\" property" " from the device tree %s \"%s\".", DT_FS_PATH, model_buf); fwts_advice(fw, "Check the root \"model\" property" " from the device tree %s, " "there are whitespace inconsistentencies" " between the \"model\" property and " " the trimmed value of \"%s\", report" " this as a possible bug." " Run \"hexdump -C model\"" " from the \"%s\" directory to view" " the raw contents of the property.", DT_FS_PATH, tmp_model_buf, DT_FS_PATH); } if (machine_matches_reference_model(fw, compat_buf, compat_len, tmp_model_buf)) { fwts_passed(fw, "OpenPOWER Reference " "Compatible passed"); } else { fwts_failed(fw, LOG_LEVEL_HIGH, "DTOpenPOWERReferenceFailed", "Unable to find an OpenPOWER supported" " match"); /* adding verbatim to show proper string */ fwts_log_info_verbatim(fw, "Unable to find an OpenPOWER reference" " match for \"%s\"", tmp_model_buf); free(orig_model_buf); return FWTS_ERROR; } free(orig_model_buf); } return FWTS_OK; }
/* * 4.1.2.2 ASF_ALRT */ static void asf_check_alrt( fwts_framework *fw, ssize_t record_length, ssize_t length, uint8_t *data, bool *passed, bool *abort) { fwts_acpi_table_asf_alrt *alrt = (fwts_acpi_table_asf_alrt *)data; uint8_t i; ssize_t total_length; if (length < (ssize_t)sizeof(fwts_acpi_table_asf_alrt)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtRecordTooShort", "ASF! ASF_ALRT Record too short, " "expecting %zu bytes, instead got %zu bytes", sizeof(fwts_acpi_table_asf_alrt), length); *passed = false; *abort = true; return; } #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_ALRT Record:"); fwts_log_info_verbatim(fw, " Assertion Event Mask: 0x%2.2" PRIx8, alrt->assertion_mask); fwts_log_info_verbatim(fw, " De-Assertion Event Mask: 0x%2.2" PRIx8, alrt->deassertion_mask); fwts_log_info_verbatim(fw, " Number of Alerts: 0x%2.2" PRIx8, alrt->number_of_alerts); fwts_log_info_verbatim(fw, " Array Element Length: 0x%2.2" PRIx8, alrt->array_length); #endif if ((alrt->number_of_alerts < 1) || (alrt->number_of_alerts > 8)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtNumOfAlertsInvalid", "ASF! ASF_ALRT Number of Alerts field is 0x%" PRIx8 " and must be in the range 0x01..0x08", alrt->number_of_alerts); /* Don't trust the ALRT data, so abort */ return; } if (alrt->array_length != sizeof(fwts_acpi_table_asf_alrt_element)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtArrayElementLengthInvalid", "ASF! ASF_ALRT Array Element Length is 0x%" PRIx8 " and must be instead 0x%" PRIx8, alrt->array_length, (uint8_t)sizeof(fwts_acpi_table_asf_alrt_element)); } total_length = sizeof(fwts_acpi_table_asf_alrt) + (alrt->number_of_alerts * sizeof(fwts_acpi_table_asf_alrt_element)); if (total_length > record_length) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtArrayElementLengthInvalid", "ASF! ASF_ALRT Array Element Length makes the " "total ASF_ALRT record size to be %zu bytes, however the " "table is only %zu bytes long", total_length, record_length); *passed = false; *abort = true; return; } data += sizeof(fwts_acpi_table_asf_alrt); for (i = 0; i < alrt->number_of_alerts; i++) { fwts_acpi_table_asf_alrt_element *element = (fwts_acpi_table_asf_alrt_element *)data; #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_ALRT Element %" PRIu8 ":", i); fwts_log_info_verbatim(fw, " Device Address: 0x%2.2" PRIx8, element->device_addr); fwts_log_info_verbatim(fw, " Alert Command: 0x%2.2" PRIx8, element->command); fwts_log_info_verbatim(fw, " Alert Data Mask: 0x%2.2" PRIx8, element->data_mask); fwts_log_info_verbatim(fw, " Alert Compare Value: 0x%2.2" PRIx8, element->compare_value); fwts_log_info_verbatim(fw, " Alert Event Sensor Type: 0x%2.2" PRIx8, element->sensor_type); fwts_log_info_verbatim(fw, " Alert Event Type: 0x%2.2" PRIx8, element->event_type); fwts_log_info_verbatim(fw, " Alert Event Offset: 0x%2.2" PRIx8, element->event_offset); fwts_log_info_verbatim(fw, " Alert Source Type: 0x%2.2" PRIx8, element->event_source_type); fwts_log_info_verbatim(fw, " Alert Event Severity: 0x%2.2" PRIx8, element->event_severity); fwts_log_info_verbatim(fw, " Alert Sensor Number: 0x%2.2" PRIx8, element->sensor_number); fwts_log_info_verbatim(fw, " Alert Entity: 0x%2.2" PRIx8, element->entity); fwts_log_info_verbatim(fw, " Alert Entity Instance: 0x%2.2" PRIx8, element->entity_instance); #endif if (element->event_offset & 0x80) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtEventOffsetBit7Set", "ASF! ASF_ALRT Array Element %" PRIu8 " Event Offset Bit 7 is 1, " " and should be 0", i); } data += sizeof(fwts_acpi_table_asf_alrt_element); } if (*passed) fwts_passed(fw, "No issues found in ASF! ASF_ALRT record."); }
static int dt_sysinfo_check_version(fwts_framework *fw) { if (!fw->fdt) { fwts_failed(fw, LOG_LEVEL_CRITICAL, "DTMissing", "Device tree is missing, check your installation."); return FWTS_ABORTED; } int node, version_len; fwts_log_info(fw, "OPAL base device tree path is %s.", DT_FS_PATH); node = fdt_path_offset(fw->fdt, opal_firmware); if (node >= 0) { const char *version_buf = fdt_getprop(fw->fdt, node, "version", &version_len); if (version_buf) { fwts_passed(fw, "OPAL Firmware version from device tree node" " \"%s\" is \"%s\".", opal_firmware, version_buf); } else { fwts_failed(fw, LOG_LEVEL_CRITICAL, "DTSysInfoCheck", "OPAL Firmware version from device tree node" " \"%s\" was not found," " check your installation for" " device tree node \"%s\" property" " \"version\".", opal_firmware, opal_firmware); } } else { fwts_failed(fw, LOG_LEVEL_CRITICAL, "DTMissing", "Device tree missing version property of \"%s\", " "check your installation for device tree node" " property \"version\".", opal_firmware); return FWTS_ERROR; } /* Now check for additional firmware versions */ node = fdt_path_offset(fw->fdt, platform_firmware); dt_sysinfo_get_version(fw, node, "occ"); dt_sysinfo_get_version(fw, node, "open-power"); dt_sysinfo_get_version(fw, node, "linux"); dt_sysinfo_get_version(fw, node, "skiboot"); dt_sysinfo_get_version(fw, node, "capp-ucode"); dt_sysinfo_get_version(fw, node, "hostboot-binaries"); dt_sysinfo_get_version(fw, node, "hostboot"); dt_sysinfo_get_version(fw, node, "petitboot"); dt_sysinfo_get_version(fw, node, "buildroot"); return FWTS_OK; }
/* * ASF! Hardware Error Source Table test * http://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf */ static int asf_test1(fwts_framework *fw) { bool passed = true; bool abort = false; fwts_acpi_table_header *hdr = (fwts_acpi_table_header *)table->data; uint8_t *data = (uint8_t *)table->data; ssize_t length = (ssize_t)hdr->length; fwts_log_info_verbatim(fw, "ASF! Hardware Error Source Table"); data += sizeof(fwts_acpi_table_header); length -= sizeof(fwts_acpi_table_header); while (!abort && (length > 0)) { bool asf_passed = true; fwts_acpi_table_asf_header *asf_hdr = (fwts_acpi_table_asf_header *)data; /* Must have enough for a info header */ if (length < (ssize_t)sizeof(fwts_acpi_table_asf_header)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!TooShort", "ASF! table too short, expecting at least %zu bytes " "for an ASF! information record header, " "instead got %zu bytes", sizeof(fwts_acpi_table_asf_header), table->length); break; } #if ASF_DUMP fwts_log_info_verbatim(fw, "Type: 0x%2.2" PRIx8, asf_hdr->type); fwts_log_info_verbatim(fw, "Reserved: 0x%2.2" PRIx8, asf_hdr->reserved); fwts_log_info_verbatim(fw, "Length: 0x%4.4" PRIx16, asf_hdr->length); #endif fwts_acpi_reserved_zero_check(fw, "ASF!", "Information Record Reserved", asf_hdr->reserved, sizeof(asf_hdr->reserved), &passed); if (asf_hdr->length > (uint32_t)length) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InfoRecordLengthTooLong", "ASF! Information Record Reserved length is %" PRIu32 " and this is too long for the size given by " "the ASF! table. Expected at most %zu bytes.", asf_hdr->length, length); /* Since we can't trust the table, abort */ break; } switch (asf_hdr->type) { case 0x00: case 0x80: asf_check_info(fw, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x01: case 0x81: asf_check_alrt(fw, asf_hdr->length, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x02: case 0x82: asf_check_rctl(fw, asf_hdr->length, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x03: case 0x83: asf_check_rmcp(fw, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x04: case 0x84: asf_check_addr(fw, asf_hdr->length, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; default: fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InvalidType", "ASF! Information Record Type 0x%4.4" PRIx16 " is invalid, aborting check", asf_hdr->type); passed = false; length = 0; break; } passed &= asf_passed; #if ASF_DUMP fwts_log_nl(fw); #endif } #if ASF_DUMP fwts_log_nl(fw); #endif if (passed) fwts_passed(fw, "No issues found in ASF! table."); return FWTS_OK; }
static int wait_for_acpi_event(fwts_framework *fw, const char *name) { int gpe_count = 0; int fd; int events = 0; int matching = 0; size_t len; int i; fwts_gpe *gpes_start; fwts_gpe *gpes_end; if ((gpe_count = fwts_gpe_read(&gpes_start)) == FWTS_ERROR) { fwts_log_error(fw, "Cannot read GPEs."); return FWTS_ERROR; } if ((fd = fwts_acpi_event_open()) < 0) { fwts_log_error(fw, "Cannot connect to acpid."); fwts_gpe_free(gpes_start, gpe_count); return FWTS_ERROR; } for (i = 0; i <= 20; i++) { char *buffer; if ((buffer = fwts_acpi_event_read(fd, &len, 1)) != NULL) { char *str; if ((str = strstr(buffer, "battery")) != NULL) { events++; if (strstr(str, name) != NULL) { matching++; free(buffer); break; } } free(buffer); } fwts_printf(fw, "Waiting %2.2d/20\r", 20-i); } fwts_acpi_event_close(fd); if ((gpe_count = fwts_gpe_read(&gpes_end)) == FWTS_ERROR) { fwts_log_error(fw, "Cannot read GPEs."); fwts_gpe_free(gpes_start, gpe_count); return FWTS_ERROR; } fwts_gpe_test(fw, gpes_start, gpes_end, gpe_count); fwts_gpe_free(gpes_start, gpe_count); fwts_gpe_free(gpes_end, gpe_count); if (events == 0) fwts_failed(fw, LOG_LEVEL_HIGH, "BatteryNoEvents", "Did not detect any ACPI battery events."); else { fwts_passed(fw, "Detected ACPI battery events."); if (matching == 0) fwts_failed(fw, LOG_LEVEL_HIGH, "BatteryNoEvents", "Could not detect ACPI events for battery %s.", name); else fwts_passed(fw, "Detected ACPI event for battery %s.", name); } return FWTS_OK; }