static int battery_test1(fwts_framework *fw) { uint32_t count = 0; uint32_t i; fwts_log_info(fw, "This test reports which (if any) batteries there are in the system. " "In addition, for charging or discharging batteries, the test validates " "that the reported 'current capacity' properly increments/decrements " "in line with the charge/discharge state. " "This test also stresses the battery state reporting codepath " "in the ACPI BIOS, and any warnings given by the ACPI interpreter " "will be reported."); if (fwts_battery_get_count(fw, &count) != FWTS_OK) { fwts_log_info(fw, "No battery information present: cannot test."); return FWTS_OK; } fwts_log_info(fw, "Found %" PRIu32 " batteries.", count); for (i = 0; i < count; i++) do_battery_test(fw, i); return FWTS_OK; }
static int prd_restart(fwts_framework *fw) { int status = 0; char *command; char *output = NULL; command = "systemctl start opal-prd.service 2>&1"; status = fwts_exec2(command, &output); if (output) free(output); if (status) { fwts_log_info(fw, "OPAL PRD service (opal-prd.service)" " was restarted after stopping it to allow " "checks. Please re-check since processing " "was not able to be confirmed, " "\"sudo systemctl status opal-prd.service\""); } else { fwts_log_info(fw, "OPAL PRD service (opal-prd.service)" " was restarted after stopping it to allow " "checks. This is informational only, " "no action needed."); } return status; /* ignored by caller */ }
static int version_test3(fwts_framework *fw) { char *str; if ((str = fwts_get("/proc/cmdline")) == NULL) fwts_log_info(fw, "Cannot get version info from /proc/cmdline"); else { fwts_chop_newline(str); fwts_log_info(fw, "Kernel boot command line: %s", str); free(str); } fwts_infoonly(fw); return FWTS_OK; }
static void do_battery_test(fwts_framework *fw, const uint32_t index) { char name[PATH_MAX]; char state[1024]; *state = '\0'; fwts_battery_get_name(fw, index, name); fwts_log_info(fw, "Test battery '%s'.", name); fwts_printf(fw, "==== Please PLUG IN the AC power of the machine ====\n"); fwts_press_enter(fw); fwts_printf(fw, "==== Please now UNPLUG the AC power of the machine ====\n"); wait_for_acpi_event(fw, name); check_discharging(fw, index, name); fwts_printf(fw, "==== Please wait 30 seconds while the battery is discharged a little ====\n"); battery_discharge(fw, 30); fwts_printf(fw, "==== Please now PLUG IN the AC power of the machine ====\n"); wait_for_acpi_event(fw, name); check_charging(fw, index, name); check_battery_cycle_count(fw, index, name); check_battery_trip_point(fw, index, name); }
static int version_test1(fwts_framework *fw) { char *str; fwts_release *release; release = fwts_release_get(); if (release) { bool not_ubuntu = strcmp(release->distributor, "Ubuntu"); fwts_release_free(release); /* Following is Ubuntu specific, so don't fail */ if (not_ubuntu) { fwts_skipped(fw, "Information not available with this kernel."); return FWTS_OK; } } if ((str = fwts_get("/proc/version_signature")) == NULL) fwts_skipped(fw, "Cannot get version signature info from " "/proc/version_signature"); else { fwts_chop_newline(str); fwts_log_info(fw, "Signature: %s", str); free(str); } fwts_infoonly(fw); return FWTS_OK; }
static int prd_info_init(fwts_framework *fw) { if (fw->fdt) { #ifdef HAVE_LIBFDT int node; node = fdt_path_offset(fw->fdt, "/ibm,opal/diagnostics"); if (node >= 0) { if (!fdt_node_check_compatible(fw->fdt, node, "ibm,opal-prd")) { return FWTS_OK; } else { return FWTS_SKIP; } } #endif } else { fwts_log_info(fw, "The OPAL PRD device tree node is not" " able to be detected so skipping the prd_info" " test. There may be tools missing such as" " libfdt-dev or dtc, check that the packages" " are installed and re-build if needed." " If this condition persists try running the" " dt_base test to further diagnose. If dt_base" " test is not available this is probably a" " setup problem."); return FWTS_SKIP; } /* only run test when fdt node is confirmed */ return FWTS_SKIP; }
static int ebda_test1(fwts_framework *fw) { const char *memory_map_name = fwts_memory_map_name(fw->firmware_type); fwts_memory_map_entry *entry; if (memory_map == NULL) return FWTS_ERROR; fwts_log_info(fw, "The Extended BIOS Data Area (EBDA) is normally located at " "the end of the low 640K region and is typically 2-4K in " "size. It should be reserved in the %s table.", memory_map_name); entry = fwts_memory_map_info(memory_map, (uint64_t)ebda_addr); if ((entry != NULL) && (entry->type == FWTS_MEMORY_MAP_RESERVED || entry->type == FWTS_MEMORY_MAP_ACPI)) { fwts_passed(fw, "EBDA region mapped at 0x%lx and reserved as a %" PRId64 "K region in the %s table at 0x%" PRIx64 "..0x%" PRIx64 ".", ebda_addr, (entry->end_address - entry->start_address) / 1024, memory_map_name, entry->start_address, entry->end_address); } else fwts_failed(fw, LOG_LEVEL_MEDIUM, "EBDAMappedNotReserved", "EBDA region mapped at 0x%lx but not reserved in the %s table.", ebda_addr, memory_map_name); return FWTS_OK; }
static int prd_dev_query(fwts_framework *fw) { int fd = 0; struct opal_prd_info info; if ((fd = open(prd_devnode, O_RDWR)) < 0) { fwts_failed(fw, LOG_LEVEL_CRITICAL, "OPAL PRD Info", "Cannot get data from the OPAL PRD " " device interface," " check if opal-prd daemon may be in use " "or check your user privileges."); return FWTS_ERROR; } memset(&info, 0, sizeof(info)); if (ioctl(fd, OPAL_PRD_GET_INFO, &info)) { (void)close(fd); fwts_failed(fw, LOG_LEVEL_CRITICAL, "OPAL PRD Info", "Cannot get data from the" " OPAL PRD device interface."); return FWTS_ERROR; } else { fwts_log_info(fw, "OPAL PRD Version is %lu", info.version); (void)close(fd); return FWTS_OK; } }
static int version_test4(fwts_framework *fw) { char *str; if (((str = fwts_get("/sys/module/acpi/parameters/acpica_version")) == NULL) && ((str = fwts_get("/proc/acpi/info")) == NULL)) fwts_log_info(fw, "Cannot get ACPI version info from " "/sys/module/acpi/parameters/acpica_version " "or /proc/acpi/info, system does not have ACPI."); else { fwts_chop_newline(str); fwts_log_info(fw, "ACPI Version: %s", str); free(str); } fwts_infoonly(fw); return FWTS_OK; }
static void check_battery_trip_point( fwts_framework *fw, const uint32_t index, const char *name) { uint32_t trip_point; uint32_t trip_point_org; fwts_printf(fw, "==== Checking trip point of battery '%s' ====\n", name); if (!fwts_battery_check_trip_point_support(fw, index)) { fwts_printf(fw, "==== Not supported - skip test ====\n"); return; } if (fwts_battery_get_trip_point(fw, index, &trip_point) == FWTS_OK) trip_point_org = trip_point; else trip_point_org = 0; fwts_log_info(fw, "Test battery '%s' downward trip point.", name); fwts_printf(fw, "==== Please now UNPLUG the AC power of the machine ====\n"); fwts_press_enter(fw); sleep(1); trip_point = get_full(fw, index) - 5; fwts_battery_set_trip_point(fw, index, trip_point); fwts_cpu_consume_start(); wait_for_acpi_event(fw, name); fwts_cpu_consume_complete(); fwts_log_info(fw, "Test battery '%s' upwards trip point.", name); fwts_printf(fw, "==== Please PLUG IN the AC power of the machine ====\n"); fwts_press_enter(fw); sleep(1); trip_point = get_full(fw, index) + 3; fwts_battery_set_trip_point(fw, index, trip_point); wait_for_acpi_event(fw, name); if (trip_point_org != 0) fwts_battery_set_trip_point(fw, index, trip_point_org); }
static int romdump_test1(fwts_framework *fw) { uint8_t *mem; int i; if ((mem = fwts_mmap(BIOS_ROM_REGION_START, BIOS_ROM_REGION_SIZE)) == FWTS_MAP_FAILED) { fwts_log_error(fw, "Cannot mmap BIOS ROM region."); return FWTS_ERROR; } for (i = 0; i < BIOS_ROM_REGION_SIZE; i += 512) { /* Ensure we can safely read the memory */ if (fwts_safe_memread(mem + i, 512) != FWTS_OK) continue; if ((*(mem+i) == 0x55) && (*(mem+i+1) == 0xaa)) { int length = *(mem+i+2) << 9; fwts_log_info(fw, "Found ROM: %x..%x (%d bytes)", BIOS_ROM_REGION_START+i, BIOS_ROM_REGION_START+i+length, length); romdump_data(fw, mem+i, BIOS_ROM_REGION_START+i, length); fwts_log_nl(fw); } } fwts_log_info(fw, "BIOS ROM: %x..%x (%d bytes)", BIOS_ROM_START, BIOS_ROM_END, BIOS_ROM_SIZE); romdump_data(fw, mem+BIOS_ROM_OFFSET, BIOS_ROM_START, BIOS_ROM_SIZE); fwts_infoonly(fw); (void)fwts_munmap(mem, BIOS_ROM_REGION_SIZE); return FWTS_OK; }
static int uefivarinfo_init(fwts_framework *fw) { if (fwts_firmware_detect() != FWTS_FIRMWARE_UEFI) { fwts_log_info(fw, "Cannot detect any UEFI firmware. Aborted."); return FWTS_ABORTED; } if (fwts_lib_efi_runtime_load_module(fw) != FWTS_OK) { fwts_log_info(fw, "Cannot load efi_runtime module. Aborted."); return FWTS_ABORTED; } fd = fwts_lib_efi_runtime_open(); if (fd == -1) { fwts_log_info(fw, "Cannot open EFI test driver. Aborted."); return FWTS_ABORTED; } return FWTS_OK; }
static int uefirtauthvar_init(fwts_framework *fw) { if (fwts_firmware_detect() != FWTS_FIRMWARE_UEFI) { fwts_log_info(fw, "Cannot detect any UEFI firmware. Aborted."); return FWTS_ABORTED; } if (fwts_lib_efi_runtime_load_module(fw) != FWTS_OK) { fwts_log_info(fw, "Cannot load efi_runtime module. Aborted."); return FWTS_ABORTED; } fd = open("/dev/efi_runtime", O_WRONLY | O_RDWR); if (fd == -1) { fwts_log_info(fw, "Cannot open efi_runtime driver. Aborted."); return FWTS_ABORTED; } uefirtvariable_env_cleanup(); return FWTS_OK; }
static int clog_init(fwts_framework *fw) { if (!fwts_clog_available(fw)) { fwts_log_info(fw, "coreboot log not available, skipping test"); return FWTS_SKIP; } clog_list = fwts_clog_read(fw); if (clog_list == NULL) { fwts_log_error(fw, "Cannot read coreboot log."); return FWTS_ERROR; } return FWTS_OK; }
static int wrap_logind_do_s4(fwts_pm_method_vars *fwts_settings, const int percent, int *duration, const char *str) { FWTS_UNUSED(str); char *action = PM_HIBERNATE_LOGIND; fwts_progress_message(fwts_settings->fw, percent, "(Hibernating)"); /* This blocks by entering a glib mainloop */ *duration = fwts_logind_wait_for_resume_from_action(fwts_settings, action, s4_min_delay); fwts_log_info(fwts_settings->fw, "S4 duration = %d.", *duration); fwts_progress_message(fwts_settings->fw, percent, "(Resumed)"); return *duration > 0 ? 0 : 1; }
static bool machine_matches_reference_model(fwts_framework *fw, const char *compatible, int compat_len, const char *model) { bool compatible_is_reference = false, model_is_reference = false; struct reference_platform *plat; int i; for (i = 0; i < (int)FWTS_ARRAY_LEN(openpower_reference_platforms); i++) { plat = &openpower_reference_platforms[i]; if (dt_fdt_stringlist_contains_last(compatible, compat_len, plat->compatible)) { compatible_is_reference = true; break; } } /* Not a reference platform, nothing to check */ if (!compatible_is_reference) { fwts_log_info(fw, "Informational: no reference model found," " device tree \"compatible\" is \"%s\" and" " \"model\" is \"%s\"", compatible, model); return true; } /* Since we're on a reference platform, ensure that the model is also * one of the reference model numbers */ for (i = 0; i < plat->n_models; i++) { if (!strcmp(model, plat->models[i])) { model_is_reference = true; break; } } if (model_is_reference) { fwts_log_info_verbatim(fw, "Matched reference model, device tree " "\"compatible\" is \"%s\" and \"model\" is " "\"%s\"", plat->compatible, model); } return model_is_reference; }
static int lid_test3(fwts_framework *fw) { int i; fwts_log_info(fw, "Some machines may have EC or ACPI faults that cause detection of multiple open/close events to fail."); for (i = 1; i < 4; i++) { int ret; fwts_printf(fw, "==== %d of %d: Please close laptop lid for 2 seconds and then re-open. ====\n", i,3); if ((ret = lid_test_state(fw, FWTS_BUTTON_LID_CLOSED)) != FWTS_OK) return ret; if ((ret = lid_test_state(fw, FWTS_BUTTON_LID_OPENED)) != FWTS_OK) return ret; } return FWTS_OK; }
static int clog_test1(fwts_framework *fw) { int errors = 0; if (fwts_clog_firmware_check(fw, clog_progress, clog_list, &errors)) { fwts_log_error(fw, "Error parsing coreboot log."); return FWTS_ERROR; } if (errors > 0) /* Checks will log errors as failures automatically */ fwts_log_info(fw, "Found %d unique errors in coreboot log.", errors); else fwts_passed(fw, "Found no errors in coreboot log."); return FWTS_OK; }
static int ebda_init(fwts_framework *fw) { if (fw->firmware_type != FWTS_FIRMWARE_BIOS) { fwts_log_info(fw, "Machine is not using traditional BIOS firmware, skipping test."); return FWTS_SKIP; } if ((memory_map = fwts_memory_map_table_load(fw)) == NULL) { fwts_log_error(fw, "Failed to read memory map."); return FWTS_ERROR; } if ((ebda_addr = fwts_ebda_get()) == FWTS_NO_EBDA) { fwts_log_error(fw, "Failed to locate EBDA region."); return FWTS_ERROR; } return FWTS_OK; }
static void check_battery_cycle_count( fwts_framework *fw, const uint32_t index, const char *name) { uint32_t cycle_count; fwts_printf(fw, "==== Checking cycle count of battery '%s' ====\n", name); if (fwts_battery_get_cycle_count(fw, index, &cycle_count) == FWTS_OK) { if (cycle_count == 0) { fwts_log_info(fw, "Please ignore this error with a new battery"); fwts_failed(fw, LOG_LEVEL_LOW, "BatteryZeroCycleCount", "System firmware may not support cycle count interface " "or it reports it incorrectly for battery %s.", name); } } }
static int microcode_init(fwts_framework *fw) { bool intel; if (fwts_cpu_is_Intel(&intel) != FWTS_OK) { fwts_log_error(fw, "Cannot determine processor type."); return FWTS_ERROR; } if (!intel) { fwts_log_info(fw, "The microcode test currently only supports Intel processors."); return FWTS_SKIP; } klog = fwts_klog_read(); if (klog == NULL) { fwts_log_error(fw, "Cannot read kernel log."); return FWTS_ERROR; } return FWTS_OK; }
static int uefivarinfo_test1(fwts_framework *fw) { uint64_t status; uint64_t remvarstoragesize; uint64_t maxvariablesize; uint64_t maxvarstoragesize; uint64_t usedvars; uint64_t usedvarssize; if (do_queryvariableinfo(&status, &maxvarstoragesize, &remvarstoragesize, &maxvariablesize) == FWTS_ERROR) { if (status == EFI_UNSUPPORTED) { fwts_skipped(fw, "QueryVariableInfo UEFI runtime interface not supported: cannot test."); fwts_advice(fw, "Firmware also needs to check if the revision " "of system table is correct or not. Linux " "kernel returns EFI_UNSUPPORTED as well, if " "the FirmwareRevision of system table is less " "than EFI_2_00_SYSTEM_TABLE_REVISION."); return FWTS_SKIP; } else { fwts_log_info(fw, "Failed to query variable info with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } } fwts_log_info_verbatim(fw, "UEFI NVRAM storage:"); fwts_log_info_verbatim(fw, " Maximum storage: %8" PRIu64 " bytes", maxvarstoragesize); fwts_log_info_verbatim(fw, " Remaining storage: %8" PRIu64 " bytes", remvarstoragesize); fwts_log_info_verbatim(fw, " Maximum variable size: %8" PRIu64 " bytes", maxvariablesize); if (do_checkvariables(fw, &usedvars, &usedvarssize, maxvariablesize) == FWTS_OK) { fwts_log_info_verbatim(fw, "Currently used:"); fwts_log_info_verbatim(fw, " %" PRIu64 " variables, storage used: %" PRIu64 " bytes", usedvars, usedvarssize); } return FWTS_OK; }
static int hpet_check_test1(fwts_framework *fw) { fwts_list_link *item; if (klog == NULL) return FWTS_ERROR; fwts_log_info(fw, "This test checks the HPET PCI BAR for each timer block " "in the timer. The base address is passed by the firmware " "via an ACPI table. IRQ routing and initialization is also " "verified by the test."); fwts_list_foreach(item, klog) { char *text = fwts_text_list_text(item); /* Old format */ if (strstr(text, "ACPI: HPET id:") != NULL) { char *str = strstr(text, "base: "); if (str) { hpet_base_p = strtoul(str+6, NULL, 0x10); fwts_passed(fw, "Found HPET base 0x%" PRIx64 " in kernel log.", hpet_base_p); break; } } /* New format */ /* [ 0.277934] hpet0: at MMIO 0xfed00000, IRQs 2, 8, 0 */ if ((strstr(text, "hpet") != NULL) && (strstr(text, "IRQs") != NULL)) { char *str = strstr(text, "at MMIO "); if (str) { hpet_base_p = strtoul(str+8, NULL, 0x10); fwts_passed(fw, "Found HPET base 0x%" PRIx64 " in kernel log.", hpet_base_p); break; } } }
static int ebdadump_test1(fwts_framework *fw) { off_t ebda_addr; uint8_t *mem; size_t len; if ((ebda_addr = fwts_ebda_get()) == FWTS_NO_EBDA) { fwts_log_error(fw, "Failed to local EBDA region."); return FWTS_ERROR; } len = BIOS_ROM_START - ebda_addr; if (ebda_addr > BIOS_ROM_START) { fwts_log_error(fw, "EBDA start address is greater than the " "BIOS ROM start address."); return FWTS_ERROR; } if ((mem = fwts_mmap(ebda_addr, len)) == FWTS_MAP_FAILED) { fwts_log_error(fw, "Cannot mmap BIOS ROM region."); return FWTS_ERROR; } fwts_log_info(fw, "EBDA region: %" PRIx32 "..%x (%zd bytes)", (uint32_t)ebda_addr, BIOS_ROM_START, len); ebdadump_data(fw, mem, ebda_addr, len); (void)fwts_munmap(mem, len); fwts_infoonly(fw); return FWTS_OK; }
static int do_checkvariables( fwts_framework *fw, uint64_t *usedvars, uint64_t *usedvarssize, const uint64_t maxvarsize) { uint64_t status; struct efi_getnextvariablename getnextvariablename; uint64_t variablenamesize = MAX_VARNAME_LENGTH; uint16_t variablename[MAX_VARNAME_LENGTH]; EFI_GUID vendorguid; uint8_t *data; uint64_t getdatasize; uint32_t attributestest; struct efi_getvariable getvariable; getvariable.Attributes = &attributestest; getvariable.status = &status; getnextvariablename.VariableNameSize = &variablenamesize; getnextvariablename.VariableName = variablename; getnextvariablename.VendorGuid = &vendorguid; getnextvariablename.status = &status; *usedvars = 0; *usedvarssize = 0; /* * To start the search, need to pass a Null-terminated string * in VariableName */ variablename[0] = '\0'; while (true) { long ioret; status = ~0ULL; variablenamesize = MAX_VARNAME_LENGTH; ioret = ioctl(fd, EFI_RUNTIME_GET_NEXTVARIABLENAME, &getnextvariablename); if (ioret == -1) { /* no next variable was found*/ if (*getnextvariablename.status == EFI_NOT_FOUND) break; fwts_log_info(fw, "Failed to get next variable name with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } (*usedvars)++; data = malloc(maxvarsize); if (!data) { fwts_log_info(fw, "Failed to allocate memory for test."); return FWTS_ERROR; } getdatasize = maxvarsize; getvariable.VariableName = variablename; getvariable.VendorGuid = &vendorguid; getvariable.DataSize = &getdatasize; getvariable.Data = data; status = ~0ULL; ioret = ioctl(fd, EFI_RUNTIME_GET_VARIABLE, &getvariable); if (ioret == -1) { if (status != EFI_BUFFER_TOO_SMALL) { free(data); fwts_log_info(fw, "Failed to get variable with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } else if (getdatasize > maxvarsize) { free(data); fwts_log_info(fw, "Variable is larger than maximum variable length."); fwts_uefi_print_status_info(fw, status); /* * Although the variable is larger than maximum variable length, * still try to calculate the total sizes of the used variables. */ data = malloc(getdatasize); if (!data) { fwts_log_info(fw, "Failed to allocate memory for test."); return FWTS_ERROR; } getvariable.Data = data; status = ~0ULL; ioret = ioctl(fd, EFI_RUNTIME_GET_VARIABLE, &getvariable); if (ioret == -1) { fwts_log_info(fw, "Failed to get variable with variable larger than maximum variable length."); fwts_uefi_print_status_info(fw, status); free(data); return FWTS_ERROR; } } } free(data); (*usedvarssize) += getdatasize; }; 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; }
/* * 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 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; }
static int s4_hibernate(fwts_framework *fw, int *klog_errors, int *hw_errors, int *pm_errors, int *klog_oopses, int *klog_warn_ons, int *failed_alloc_image, int percent) { fwts_list *klog_pre, *klog_post, *klog_diff; fwts_hwinfo hwinfo1, hwinfo2; int status; int duration; int differences; int rc = FWTS_OK; char *command = NULL; char *quirks = NULL; fwts_pm_method_vars *fwts_settings; int (*do_s4)(fwts_pm_method_vars *, const int, int*, const char*); fwts_settings = calloc(1, sizeof(fwts_pm_method_vars)); if (fwts_settings == NULL) return FWTS_OUT_OF_MEMORY; fwts_settings->fw = fw; if (fw->pm_method == FWTS_PM_UNDEFINED) { /* Autodetection */ fwts_log_info(fw, "Detecting the power method."); detect_pm_method(fwts_settings); } switch (fw->pm_method) { #if FWTS_ENABLE_LOGIND case FWTS_PM_LOGIND: fwts_log_info(fw, "Using logind as the default power method."); if (fwts_logind_init_proxy(fwts_settings) != 0) { fwts_log_error(fw, "Failure to connect to Logind."); rc = FWTS_ERROR; goto tidy; } do_s4 = &wrap_logind_do_s4; break; #endif case FWTS_PM_PMUTILS: fwts_log_info(fw, "Using pm-utils as the default power method."); do_s4 = &wrap_pmutils_do_s4; break; case FWTS_PM_SYSFS: fwts_log_info(fw, "Using sysfs as the default power method."); do_s4 = &wrap_sysfs_do_s4; break; default: /* This should never happen */ fwts_log_info(fw, "Using sysfs as the default power method."); do_s4 = &wrap_sysfs_do_s4; break; } if (s4_device_check) fwts_hwinfo_get(fw, &hwinfo1); if (fw->pm_method == FWTS_PM_PMUTILS) { /* Format up pm-hibernate command with optional quirking arguments */ if ((command = fwts_realloc_strcat(NULL, PM_HIBERNATE)) == NULL) { rc = FWTS_OUT_OF_MEMORY; goto tidy; } /* For now we only support quirks with pm-utils */ if (s4_quirks) { if ((command = fwts_realloc_strcat(command, " ")) == NULL) { rc = FWTS_OUT_OF_MEMORY; goto tidy; } if ((quirks = fwts_args_comma_list(s4_quirks)) == NULL) { rc = FWTS_OUT_OF_MEMORY; goto tidy; } if ((command = fwts_realloc_strcat(command, quirks)) == NULL) { rc = FWTS_OUT_OF_MEMORY; goto tidy; } } } fwts_wakealarm_trigger(fw, s4_sleep_delay); /* Do s4 here */ if ((klog_pre = fwts_klog_read()) == NULL) fwts_log_error(fw, "S4: hibernate: Cannot read kernel log."); status = do_s4(fwts_settings, percent, &duration, command); if ((klog_post = fwts_klog_read()) == NULL) fwts_log_error(fw, "S4: hibernate: Cannot re-read kernel log."); if (s4_device_check) { int i; for (i = 0; i < s4_device_check_delay; i++) { char buffer[80]; snprintf(buffer, sizeof(buffer), "(Waiting %d/%d seconds)", i+1, s4_device_check_delay); fwts_progress_message(fw, percent, buffer); sleep(1); } fwts_progress_message(fw, percent, "(Checking devices)"); fwts_hwinfo_get(fw, &hwinfo2); fwts_hwinfo_compare(fw, &hwinfo1, &hwinfo2, &differences); fwts_hwinfo_free(&hwinfo1); fwts_hwinfo_free(&hwinfo2); if (differences > 0) { fwts_failed(fw, LOG_LEVEL_HIGH, "DevConfigDiffAfterS4", "Found %d differences in device configuation during S4 cycle.", differences); (*hw_errors)++; } } fwts_progress_message(fw, percent, "(Checking for errors)"); klog_diff = fwts_klog_find_changes(klog_pre, klog_post); s4_check_log(fw, klog_diff, klog_errors, klog_oopses, klog_warn_ons); fwts_progress_message(fw, percent, "(Checking for PM errors)"); /* Add in error check for pm-hibernate status */ if ((status > 0) && (status < 128)) { fwts_failed(fw, LOG_LEVEL_HIGH, "PMActionFailedPreS4", "pm-action failed before trying to put the system " "in the requested power saving state."); (*pm_errors)++; } else if (status == 128) { fwts_failed(fw, LOG_LEVEL_HIGH, "PMActionPowerStateS4", "pm-action tried to put the machine in the requested " "power state but failed."); (*pm_errors)++; } else if (status > 128) { fwts_failed(fw, LOG_LEVEL_HIGH, "PMActionFailedS4", "pm-action encountered an error and also failed to " "enter the requested power saving state."); (*pm_errors)++; } if (fwts_klog_regex_find(fw, klog_diff, "Freezing user space processes.*done") < 1) { fwts_failed(fw, LOG_LEVEL_HIGH, "UserSpaceTaskFreeze", "Failed to freeze user space processes."); (*pm_errors)++; } if (fwts_klog_regex_find(fw, klog_diff, "Freezing remaining freezable tasks.*done") < 1) { fwts_failed(fw, LOG_LEVEL_HIGH, "KernelTaskFreeze", "Failed to freeze remaining non-user space processes."); (*pm_errors)++; } if ((fwts_klog_regex_find(fw, klog_diff, "PM: freeze of devices complete") < 1) && (fwts_klog_regex_find(fw, klog_diff, "PM: late freeze of devices complete") < 1)) { fwts_failed(fw, LOG_LEVEL_HIGH, "DeviceFreeze", "Failed to freeze devices."); (*pm_errors)++; } if (fwts_klog_regex_find(fw, klog_diff, "PM: Allocated.*kbytes") < 1) { fwts_failed(fw, LOG_LEVEL_HIGH, "HibernateImageAlloc", "Failed to allocate memory for hibernate image."); *failed_alloc_image = 1; (*pm_errors)++; } if (fwts_klog_regex_find(fw, klog_diff, "PM: Image restored successfully") < 1) { fwts_failed(fw, LOG_LEVEL_HIGH, "HibernateImageRestore", "Failed to restore hibernate image."); (*pm_errors)++; } fwts_klog_free(klog_pre); fwts_klog_free(klog_post); fwts_list_free(klog_diff, NULL); tidy: free(command); free(quirks); free_pm_method_vars(fwts_settings); 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; }