Пример #1
0
/*
 *  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;
}
Пример #2
0
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, &not_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;
}
Пример #3
0
/*
 *  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.");
}
Пример #4
0
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;
}
Пример #5
0
/*
 *  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;
}
Пример #6
0
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;
}
Пример #7
0
/*
 *  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;
}
Пример #8
0
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;
}
Пример #9
0
/*
 *  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;
}
Пример #10
0
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;
}
Пример #11
0
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;
}
Пример #12
0
/*
 *  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;
}
Пример #13
0
/*
 * 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(&gtestguid, 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(&gtestguid, &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(&gtestguid, 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(&gtestguid, &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;
}
Пример #14
0
/*
 * 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(&gtestguid, 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(&gtestguid, &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;
}
Пример #15
0
/*
 * 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(&gtestguid, 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(&gtestguid, &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;
}
Пример #16
0
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;
}
Пример #17
0
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;
}
Пример #18
0
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;
}
Пример #19
0
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;
}
Пример #20
0
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;
}
Пример #21
0
/*
 *  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;
}
Пример #22
0
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;
}
Пример #23
0
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;
}
Пример #24
0
/*
 *  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;
}
Пример #25
0
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;

}
Пример #26
0
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;
}
Пример #27
0
/*
 *  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.");
}
Пример #28
0
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;
}
Пример #29
0
/*
 *  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;
}
Пример #30
0
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;
}