/* * WSMT Windows Platform Binary Table */ static int wsmt_test1(fwts_framework *fw) { fwts_acpi_table_wsmt *wsmt = (fwts_acpi_table_wsmt*) table->data; bool passed = true; fwts_log_info_verbatim(fw, "WSMT Windows SMM Security Mitigations Table:"); fwts_log_info_verbatim(fw, " Protection Flags: 0x%8.8" PRIx32, wsmt->protection_flags); fwts_acpi_reserved_bits_check(fw, "WSMT", "Protection Flags", wsmt->protection_flags, sizeof(wsmt->protection_flags), 3, 31, &passed); if ((wsmt->protection_flags & 0x2) && !(wsmt->protection_flags & 0x1)) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "WSMTBadFlagsValue", "WSMT Protection Flags bit[1] must be " "set when bit[2] is set"); } fwts_log_nl(fw); if (passed) fwts_passed(fw, "No issues found in WSMT table."); return FWTS_OK; }
static void memory_length(fwts_framework *fw, uint8_t type, uint64_t memory_range, uint64_t min_length, bool *passed) { switch (type) { case 0 ... 2: fwts_log_info_verbatim(fw, " Length: 0x%16.16" PRIx64, memory_range); if (memory_range <= min_length) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "PCCTBadSubtypeMemoryLength", "PCCT Subspace Type %" PRId8 " must have memory length > 0x%16.16" PRIx64 ", got 0x%16.16" PRIx64 " instead", type, min_length, memory_range); } break; case 3 ... 4: fwts_log_info_verbatim(fw, " Length: 0x%8.8" PRIx32, (uint32_t)memory_range); if (memory_range <= min_length) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "PCCTBadSubtypeMemoryLength", "PCCT Subspace Type %" PRId8 " must have memory length > 0x%8.8" PRIx32 ", got 0x%8.8" PRIx32 " instead", type, (uint32_t)min_length, (uint32_t)memory_range); } break; } }
/* * 4.1.2.7 ASF_ADDR */ static void asf_check_addr( fwts_framework *fw, ssize_t record_length, ssize_t length, uint8_t *data, bool *passed, bool *abort) { ssize_t total_length; fwts_acpi_table_asf_addr *addr = (fwts_acpi_table_asf_addr *)data; #if ASF_DUMP uint8_t i; #else (void)data; #endif if (length < (ssize_t)sizeof(fwts_acpi_table_asf_addr)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AddrRecordTooShort", "ASF! ASF_ADDR Record too short, " "expecting %zu bytes, instead got %zu bytes", sizeof(fwts_acpi_table_asf_addr), length); *passed = false; *abort = true; return; } #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_ADDR Record:"); fwts_log_info_verbatim(fw, " SEEPROM Address: 0x%2.2" PRIx8, addr->seeprom_addr); fwts_log_info_verbatim(fw, " Number of Devices: 0x%2.2" PRIx8, addr->number_of_devices); #endif total_length = sizeof(fwts_acpi_table_asf_addr) + (addr->number_of_devices * sizeof(fwts_acpi_table_asf_addr_element)); if (total_length > record_length) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AddrArrayElementLengthInvalid", "ASF! ASF_ADDR Number of Devices makes the " "total ASF_ADDR record size to be %zu bytes, however the " "table is only %zu bytes long", total_length, record_length); *passed = false; *abort = true; return; } #if ASF_DUMP data += sizeof(fwts_acpi_table_asf_addr); for (i = 0; i < addr->number_of_devices; i++) { fwts_acpi_table_asf_addr_element *element = (fwts_acpi_table_asf_addr_element *)data; fwts_log_info_verbatim(fw, " Fixed SMBus Address 0x%2.2" PRIx8, element->fixed_smbus_addr); data += sizeof(fwts_acpi_table_asf_addr_element); } #endif if (*passed) fwts_passed(fw, "No issues found in ASF! ASF_ADDR record."); }
/* * Dump ACPI header in a form that matches IASL's header dump format */ static void acpidump_hdr( fwts_framework *fw, const fwts_acpi_table_header *hdr, const size_t length) { if (length < sizeof(fwts_acpi_table_header)) return; fwts_log_info_verbatim(fw, "[000h 0000 4] Signature : \"%4.4s\"", hdr->signature); fwts_log_info_verbatim(fw, "[004h 0004 4] Table Length : %8.8" PRIx32, hdr->length); fwts_log_info_verbatim(fw, "[008h 0008 1] Revision : %2.2" PRIx8, hdr->revision); fwts_log_info_verbatim(fw, "[009h 0009 1] Checksum : %2.2" PRIx8, hdr->checksum); fwts_log_info_verbatim(fw, "[00Ah 0010 6] Oem ID : \"%6.6s\"", hdr->oem_id); fwts_log_info_verbatim(fw, "[010h 0016 8] Oem Table ID : \"%8.8s\"", hdr->oem_tbl_id); fwts_log_info_verbatim(fw, "[018h 0024 4] Oem Revision : %8.8" PRIx32, hdr->oem_revision); fwts_log_info_verbatim(fw, "[01Ch 0028 4] Asl Compiler ID : \"%4.4s\"", hdr->creator_id); fwts_log_info_verbatim(fw, "[020h 0032 4] Asl Compiler Revision : %8.8" PRIx32, hdr->creator_revision); }
/* * Alas, RSDP is a special case */ static void acpidump_rsdp( fwts_framework *fw, const fwts_acpi_table_info *table) { fwts_acpi_table_rsdp *rsdp = (fwts_acpi_table_rsdp *)table->data; if (table->length < sizeof(fwts_acpi_table_rsdp)) return; fwts_log_info_verbatim(fw, "[000h 0000 8] Signature : \"%8.8s\"", rsdp->signature); fwts_log_info_verbatim(fw, "[008h 0008 1] Checksum : %1.1" PRIx8, rsdp->checksum); fwts_log_info_verbatim(fw, "[009h 0009 6] Oem ID : \"%6.6s\"", rsdp->oem_id); fwts_log_info_verbatim(fw, "[00fh 0015 1] Revision : %2.2" PRIx8, rsdp->revision); fwts_log_info_verbatim(fw, "[010h 0016 4] RSDT Address : %8.8" PRIx32, rsdp->rsdt_address); fwts_log_info_verbatim(fw, "[014h 0020 4] Table Length : %8.8" PRIx32, rsdp->length); fwts_log_info_verbatim(fw, "[018h 0024 8] XSDT Address : %16.16" PRIx64, rsdp->xsdt_address); fwts_log_info_verbatim(fw, "[020h 0032 1] Extended Checksum : %2.2" PRIx8, rsdp->extended_checksum); fwts_log_info_verbatim(fw, "[021h 0033 3] Reserved : %2.2" PRIx8 " %2.2" PRIx8 " %2.2" PRIx8, rsdp->reserved[0], rsdp->reserved[1], rsdp->reserved[2]); }
/* * TPM2 table * available @ https://trustedcomputinggroup.org/tcg-acpi-specification/ */ static int tpm2_test1(fwts_framework *fw) { fwts_acpi_table_tpm2 *tpm2 = (fwts_acpi_table_tpm2*) table->data; bool passed = true; fwts_log_info_verbatim(fw, "TPM2 Table:"); fwts_log_info_verbatim(fw, " Platform Class: 0x%4.4" PRIx16, tpm2->platform_class); fwts_log_info_verbatim(fw, " Reserved: 0x%4.4" PRIx32, tpm2->reserved); fwts_log_info_verbatim(fw, " Address of Control Area: 0x%16.16" PRIx64, tpm2->address_of_control_area); fwts_log_info_verbatim(fw, " Start Method: 0x%8.8" PRIx32, tpm2->start_method); if (tpm2->platform_class != 0 && tpm2->platform_class != 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "TPM2BadPlatformClass", "TPM2's platform class must be zero (client) or one (server), got 0x%" PRIx16, tpm2->platform_class); } fwts_acpi_reserved_zero_check(fw, "TPM2", "Reserved", tpm2->reserved, sizeof(tpm2->reserved), &passed); if (tpm2->start_method < 1 || tpm2->start_method >= 12) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "TPM2BadStartMethod", "TPM2's Start Method must be between one to eleven, got 0x%" PRIx16, tpm2->start_method); } if (tpm2->start_method == 2 && table->length != sizeof(fwts_acpi_table_tpm2) + 4) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "TPM2BadPlatformParameters", "Table length must be 0x%" PRIx32 " if Start method equals 2, got 0x%" PRIx32, (uint32_t) sizeof(fwts_acpi_table_tpm2) + 4, (uint32_t) table->length); } if (tpm2->start_method == 11 && table->length < sizeof(fwts_acpi_table_tpm2) + 12) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "TPM2BadPlatformParameters", "Table length must be atleast 0x%" PRIx32 " if Start method equals 11, got 0x%" PRIx32, (uint32_t) sizeof(fwts_acpi_table_tpm2) + 12, (uint32_t) table->length); } if (passed) fwts_passed(fw, "No issues found in TPM2 table."); return FWTS_OK; }
/* * 4.1.2.1 ASF_INFO */ static void asf_check_info( fwts_framework *fw, ssize_t length, uint8_t *data, bool *passed, bool *abort) { fwts_acpi_table_asf_info *info = (fwts_acpi_table_asf_info *)data; if (length < (ssize_t)sizeof(fwts_acpi_table_asf_info)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InfoRecordTooShort", "ASF! ASF_INFO Record too short, " "expecting %zu bytes, instead got %zu bytes", sizeof(fwts_acpi_table_asf_info), length); *passed = false; *abort = true; return; } #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_INFO Record:"); fwts_log_info_verbatim(fw, " Min Watchdog Reset Value: 0x%2.2" PRIx8, info->watchdog_reset_value); fwts_log_info_verbatim(fw, " Min Poll Wait Time: 0x%2.2" PRIx8, info->min_sensor_poll_wait_time); fwts_log_info_verbatim(fw, " System ID: 0x%2.2" PRIx8, info->id); fwts_log_info_verbatim(fw, " IANA Manufacturer ID: 0x%2.2" PRIx8, info->iana_id); fwts_log_info_verbatim(fw, " Feature Flags: 0x%2.2" PRIx8, info->flags); fwts_log_info_verbatim(fw, " Reserved: 0x%2.2" PRIx8, info->reserved1); fwts_log_info_verbatim(fw, " Reserved: 0x%2.2" PRIx8, info->reserved2); fwts_log_info_verbatim(fw, " Reserved: 0x%2.2" PRIx8, info->reserved3); #endif if (info->watchdog_reset_value == 0) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InfoMinWatchDogInvalid", "ASF! ASF_INFO Minimum Watchdog Reset Value is 0x00 and " "must be in the range 0x01..0xff"); } if (info->min_sensor_poll_wait_time < 2) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InfoMinPollWaitTimeInvalid", "ASF! ASF_INFO Minimum Poll Wait Time is 0x%" PRIx8 " and must be in the range 0x02..0xff", info->min_sensor_poll_wait_time); } fwts_acpi_reserved_bits_check(fw, "ASF!", "ASF_INFO Feature Flags", info->flags, sizeof(info->flags), 1, 7, passed); fwts_acpi_reserved_zero_check(fw, "ASF!", "ASF_INFO Reserved1", info->reserved1, sizeof(info->reserved1), passed); fwts_acpi_reserved_zero_check(fw, "ASF!", "ASF_INFO Reserved2", info->reserved2, sizeof(info->reserved2), passed); fwts_acpi_reserved_zero_check(fw, "ASF!", "ASF_INFO Reserved3", info->reserved3, sizeof(info->reserved3), passed); if (*passed) fwts_passed(fw, "No issues found in ASF! ASF_INFO record."); }
static void hw_reduced_comm_test_type1(fwts_framework *fw, fwts_acpi_table_pcct_subspace_type_1 *entry, bool *passed) { fwts_log_info_verbatim(fw, " Platform Interrupt: 0x%8.8" PRIx32, entry->platform_interrupt); fwts_log_info_verbatim(fw, " Platform Interrupt Flags: 0x%2.2" PRIx8, entry->platform_interrupt_flags); fwts_log_info_verbatim(fw, " Reserved: 0x%2.2" PRIx8, entry->reserved); fwts_log_info_verbatim(fw, " Base Address: 0x%16.16" PRIx64, entry->base_address); memory_length(fw, entry->header.type, entry->length, 8, passed); fwts_log_info_verbatim(fw, " Doorbell Register:"); gas_messages(fw, entry->header.type, &entry->doorbell_register, passed); fwts_log_info_verbatim(fw, " Doorbell Preserve: 0x%16.16" PRIx64, entry->doorbell_preserve); fwts_log_info_verbatim(fw, " Doorbell Write: 0x%16.16" PRIx64, entry->doorbell_write); fwts_log_info_verbatim(fw, " Nominal Latency: 0x%8.8" PRIx32, entry->nominal_latency); fwts_log_info_verbatim(fw, " Max Periodic Access Rate: 0x%8.8" PRIx32, entry->max_periodic_access_rate); fwts_log_info_verbatim(fw, " Min Request Turnaround Time: 0x%8.8" PRIx32, entry->min_request_turnaround_time); fwts_acpi_reserved_bits_check(fw, "PCCT", "Platform Interrupt Flags", entry->platform_interrupt_flags, sizeof(uint8_t), 2, 7, passed); }
/* * 4.1.2.6 ASF_RMCP */ static void asf_check_rmcp( fwts_framework *fw, ssize_t length, uint8_t *data, bool *passed, bool *abort) { fwts_acpi_table_asf_rmcp *rmcp = (fwts_acpi_table_asf_rmcp *)data; if (length < (ssize_t)sizeof(fwts_acpi_table_asf_rmcp)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!RmcpRecordTooShort", "ASF! ASF_RMCP Record too short, " "expecting %zu bytes, instead got %zu bytes", sizeof(fwts_acpi_table_asf_rmcp), length); *passed = false; *abort = true; return; } #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_RMCP Record:"); fwts_log_info_verbatim(fw, " Remote Control Cap.: " "0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8 " " "0x%2.2" PRIx8 " 0x%2.2" PRIx8 " 0x%2.2" PRIx8, rmcp->remote_control_capabilities[0], rmcp->remote_control_capabilities[1], rmcp->remote_control_capabilities[2], rmcp->remote_control_capabilities[3], rmcp->remote_control_capabilities[4], rmcp->remote_control_capabilities[5], rmcp->remote_control_capabilities[6]); fwts_log_info_verbatim(fw, " Boot Opt. Completion Code:0x%2.2" PRIx8, rmcp->completion_code); fwts_log_info_verbatim(fw, " IANA Enterprise ID: 0x%8.8" PRIx32, rmcp->iana); fwts_log_info_verbatim(fw, " Special Command: 0x%2.2" PRIx8, rmcp->special_command); fwts_log_info_verbatim(fw, " Special Command Parameter:0x%4.4" PRIx16, rmcp->special_command_param); fwts_log_info_verbatim(fw, " Boot Options: 0x%2.2" PRIx8 " 0x%2.2" PRIx8, rmcp->boot_options[0], rmcp->boot_options[1]); fwts_log_info_verbatim(fw, " OEM Parameters: 0x%4.4" PRIx16, rmcp->oem_parameters); #endif /* Specification, page 33-34 */ if (rmcp->iana == 0x4542) { /* Values 0x00..0x05 and 0xc0..0xff are allowed */ if ((rmcp->special_command > 0x05) && (rmcp->special_command < 0xc0)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!RmcpSpecialCommandInvalid", "ASF! ASF_RMCP Special Command is 0x%" PRIx8 "and should be 0x00..0x05 or 0xc0..0xff", rmcp->special_command); } } if (*passed) fwts_passed(fw, "No issues found in ASF! ASF_RMCP record."); }
/* * ASPT Table * (reverse engineered, table is common on AMD machines) */ static int aspt_test1(fwts_framework *fw) { bool passed = true; fwts_acpi_table_aspt *aspt = (fwts_acpi_table_aspt *)table->data; if (!fwts_acpi_table_length_check(fw, "ASPT", table->length, sizeof(fwts_acpi_table_aspt))) { passed = false; goto done; } fwts_log_info_verbatim(fw, "ASPT Table:"); fwts_log_info_verbatim(fw, " SPTT Start Address: 0x%8.8" PRIx32, aspt->sptt_addr_start); fwts_log_info_verbatim(fw, " SPTT End Address: 0x%8.8" PRIx32, aspt->sptt_addr_end); fwts_log_info_verbatim(fw, " AMRT Start Address: 0x%8.8" PRIx32, aspt->amrt_addr_start); fwts_log_info_verbatim(fw, " AMRT End Address: 0x%8.8" PRIx32, aspt->amrt_addr_end); fwts_log_nl(fw); /* * Without a specification to work with there is very * little we can do to validate this apart from the * simplest sanity check */ if (aspt->sptt_addr_end < aspt->sptt_addr_start) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASPTSpttEndError", "ASPT SPTT end address is less than the APTT start " "address."); passed = false; } if (aspt->amrt_addr_end < aspt->amrt_addr_start) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASPTAmrtEndError", "ASPT AMRT end address is less than the AMRT start " "address."); passed = false; } done: if (passed) fwts_passed(fw, "No issues found in ASPT table."); return FWTS_OK; }
/* * ACPI Section 18.3.2.2.1, IA-32 Architecture Non-Maskable Interrupt * - note, this should be a higher section number, the ACPI 6.0 * specification seems to have numbered this incorrectly. */ static void hest_check_acpi_table_hest_nmi_error( fwts_framework *fw, ssize_t *length, uint8_t **data, bool *passed) { fwts_acpi_table_hest_nmi_error *err = (fwts_acpi_table_hest_nmi_error *)*data; if (*length < (ssize_t)sizeof(fwts_acpi_table_hest_nmi_error)) { fwts_failed(fw, LOG_LEVEL_HIGH, "HESTIA-32ArchitectureNmiTooShort", "HEST IA-32 Architecture Non-Mastable Interrupt " "too short, expecting %zu bytes, " "instead got %zu bytes", sizeof(fwts_acpi_table_hest_nmi_error), *length); *passed = false; *length = 0; /* Forces an early abort */ return; } fwts_log_info_verbatim(fw, "HEST IA-32 Architecture Non-Maskable Interrupt:"); fwts_log_info_verbatim(fw, " Type: 0x%2.2" PRIx8, err->type); fwts_log_info_verbatim(fw, " Source ID: 0x%4.4" PRIx16, err->source_id); fwts_log_info_verbatim(fw, " Reserved: 0x%4.4" PRIx16, err->reserved1); fwts_log_info_verbatim(fw, " Number of Records: 0x%8.8" PRIx32, err->number_of_records_to_preallocate); fwts_log_info_verbatim(fw, " Max Sections Per Record: 0x%8.8" PRIx32, err->max_sections_per_record); fwts_log_info_verbatim(fw, " Max Raw Data Length: 0x%8.8" PRIx32, err->max_raw_data_length); fwts_log_nl(fw); if (err->reserved1) { *passed = false; fwts_failed(fw, LOG_LEVEL_LOW, "HESTInvalidRecordsToPreallocate", "HEST IA-32 Architecture NMI Reserved field " "at offset 4 must be zero, instead got 0x%" PRIx16, err->reserved1); } if (err->number_of_records_to_preallocate < 1) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTInvalidRecordsToPreallocate", "HEST IA-32 Architecture NMI Number of Records " "to Preallocate is 0x%" PRIx16 " and must be " "more than zero.", err->number_of_records_to_preallocate); } if (err->max_sections_per_record < 1) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTInvalidMaxSectionsPerRecord", "HEST A-32 Architecture NMI Max Sections Per " "Record is 0x%" PRIx16 " and must be " "more than zero.", err->max_sections_per_record); } *length -= sizeof(fwts_acpi_table_hest_nmi_error); *data += sizeof(fwts_acpi_table_hest_nmi_error); }
static void gas_messages(fwts_framework *fw, uint8_t type, fwts_acpi_gas *gas, bool *passed) { fwts_log_info_verbatim(fw, " Address Space ID 0x%2.2" PRIx8, gas->address_space_id); fwts_log_info_verbatim(fw, " Register Bit Width 0x%2.2" PRIx8, gas->register_bit_width); fwts_log_info_verbatim(fw, " Register Bit Offset 0x%2.2" PRIx8, gas->register_bit_offset); fwts_log_info_verbatim(fw, " Access Size 0x%2.2" PRIx8, gas->access_width); fwts_log_info_verbatim(fw, " Address 0x%16.16" PRIx64, gas->address); if ((gas->address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_IO) && (gas->address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_MEMORY) && (gas->address_space_id != FWTS_GAS_ADDR_SPACE_ID_FFH)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "PCCTSubspaceInvalidAddrSpaceID", "PCCT Subspace Type %" PRId8 " has space ID = %" PRId8 " which is not System I/O, Memory or FFH", type, gas->address_space_id); } }
static void romdump_data(fwts_framework *fw, uint8_t *data, int offset, int length) { char buffer[128]; int i; for (i = 0; i < length; i += 16) { fwts_dump_raw_data(buffer, sizeof(buffer), data+i, offset+i, 16); fwts_log_info_verbatim(fw, "%s", buffer); } }
static int pdtt_test1(fwts_framework *fw) { fwts_acpi_table_pdtt *pdtt = (fwts_acpi_table_pdtt*) table->data; fwts_acpi_table_pdtt_channel *entry; uint32_t offset, count, i; uint32_t reserved; bool passed = true; reserved = (uint32_t) pdtt->reserved[0] + ((uint32_t) pdtt->reserved[1] << 8) + ((uint32_t) pdtt->reserved[2] << 16); fwts_log_info_verbatim(fw, "PDTT Platform Debug Trigger Table:"); fwts_log_info_verbatim(fw, " Trigger Count: 0x%2.2" PRIx8, pdtt->trigger_count); fwts_log_info_verbatim(fw, " Reserved[3]: 0x%6.6" PRIx32, reserved); fwts_log_info_verbatim(fw, " Trigger ID Array Offset: 0x%2.2" PRIx8, pdtt->array_offset); fwts_acpi_reserved_zero_check(fw, "PDTT", "Reserved", reserved, sizeof(reserved), &passed); offset = pdtt->array_offset; entry = (fwts_acpi_table_pdtt_channel *) (table->data + offset); count = (pdtt->header.length - pdtt->array_offset) / sizeof(fwts_acpi_table_pdtt_channel); if (count != pdtt->trigger_count) { passed = false; fwts_failed(fw, LOG_LEVEL_CRITICAL, "PDTTBadIDCount", "PDTT should have %" PRId8 " ids, got %" PRId8, pdtt->trigger_count, count); return FWTS_OK; } fwts_log_info_verbatim(fw, " Platform Communication Channel IDs"); for (i = 0; i < pdtt->trigger_count; i++) { fwts_log_info_verbatim(fw, " Sub channel ID: 0x%2.2" PRIx8, entry->sub_channel_id); fwts_log_info_verbatim(fw, " Flags: 0x%2.2" PRIx8, entry->flags); fwts_acpi_reserved_bits_check(fw, "PDTT", "Flags", entry->flags, sizeof(entry->flags), 3, 7, &passed); if ((offset += sizeof(fwts_acpi_table_pdtt_channel)) > table->length) { passed = false; fwts_failed(fw, LOG_LEVEL_CRITICAL, "PDTTBadTableLength", "PDTT has more channel IDs than its size can handle"); break; } entry = (fwts_acpi_table_pdtt_channel *) (table->data + offset); } fwts_log_nl(fw); if (passed) fwts_passed(fw, "No issues found in PDTT table."); return FWTS_OK; }
static bool machine_matches_reference_model(fwts_framework *fw, const char *compatible, int compat_len, const char *model) { bool compatible_is_reference = false, model_is_reference = false; struct reference_platform *plat; int i; for (i = 0; i < (int)FWTS_ARRAY_LEN(openpower_reference_platforms); i++) { plat = &openpower_reference_platforms[i]; if (dt_fdt_stringlist_contains_last(compatible, compat_len, plat->compatible)) { compatible_is_reference = true; break; } } /* Not a reference platform, nothing to check */ if (!compatible_is_reference) { fwts_log_info(fw, "Informational: no reference model found," " device tree \"compatible\" is \"%s\" and" " \"model\" is \"%s\"", compatible, model); return true; } /* Since we're on a reference platform, ensure that the model is also * one of the reference model numbers */ for (i = 0; i < plat->n_models; i++) { if (!strcmp(model, plat->models[i])) { model_is_reference = true; break; } } if (model_is_reference) { fwts_log_info_verbatim(fw, "Matched reference model, device tree " "\"compatible\" is \"%s\" and \"model\" is " "\"%s\"", plat->compatible, model); } return model_is_reference; }
static void acpi_dump_raw_table( fwts_framework *fw, const fwts_acpi_table_info *table) { const uint8_t *data = (uint8_t *)table->data; const size_t length = table->length; size_t n; fwts_log_nl(fw); for (n = 0; n < length; n += 16) { int left = length - n; char buffer[128]; fwts_dump_raw_data(buffer, sizeof(buffer), data + n, n, left > 16 ? 16 : left); fwts_log_info_verbatim(fw, "%s", buffer); } }
static int uefivarinfo_test1(fwts_framework *fw) { uint64_t status; uint64_t remvarstoragesize; uint64_t maxvariablesize; uint64_t maxvarstoragesize; uint64_t usedvars; uint64_t usedvarssize; if (do_queryvariableinfo(&status, &maxvarstoragesize, &remvarstoragesize, &maxvariablesize) == FWTS_ERROR) { if (status == EFI_UNSUPPORTED) { fwts_skipped(fw, "QueryVariableInfo UEFI runtime interface not supported: cannot test."); fwts_advice(fw, "Firmware also needs to check if the revision " "of system table is correct or not. Linux " "kernel returns EFI_UNSUPPORTED as well, if " "the FirmwareRevision of system table is less " "than EFI_2_00_SYSTEM_TABLE_REVISION."); return FWTS_SKIP; } else { fwts_log_info(fw, "Failed to query variable info with UEFI runtime service."); fwts_uefi_print_status_info(fw, status); return FWTS_ERROR; } } fwts_log_info_verbatim(fw, "UEFI NVRAM storage:"); fwts_log_info_verbatim(fw, " Maximum storage: %8" PRIu64 " bytes", maxvarstoragesize); fwts_log_info_verbatim(fw, " Remaining storage: %8" PRIu64 " bytes", remvarstoragesize); fwts_log_info_verbatim(fw, " Maximum variable size: %8" PRIu64 " bytes", maxvariablesize); if (do_checkvariables(fw, &usedvars, &usedvarssize, maxvariablesize) == FWTS_OK) { fwts_log_info_verbatim(fw, "Currently used:"); fwts_log_info_verbatim(fw, " %" PRIu64 " variables, storage used: %" PRIu64 " bytes", usedvars, usedvarssize); } return FWTS_OK; }
static void generic_comm_test(fwts_framework *fw, fwts_acpi_table_pcct_subspace_type_0 *entry, bool *passed) { fwts_acpi_gas *gas = &entry->doorbell_register; uint64_t reserved; reserved = (uint64_t) entry->reserved[0] + ((uint64_t) entry->reserved[1] << 8) + ((uint64_t) entry->reserved[2] << 16) + ((uint64_t) entry->reserved[3] << 24) + ((uint64_t) entry->reserved[4] << 32) + ((uint64_t) entry->reserved[5] << 40); fwts_log_info_verbatim(fw, " Reserved: 0x%16.16" PRIx64, reserved); fwts_log_info_verbatim(fw, " Base Address: 0x%16.16" PRIx64, entry->base_address); memory_length(fw, entry->header.type, entry->length, 8, passed); fwts_log_info_verbatim(fw, " Doorbell Register:"); fwts_log_info_verbatim(fw, " Address Space ID 0x%2.2" PRIx8, gas->address_space_id); fwts_log_info_verbatim(fw, " Register Bit Width 0x%2.2" PRIx8, gas->register_bit_width); fwts_log_info_verbatim(fw, " Register Bit Offset 0x%2.2" PRIx8, gas->register_bit_offset); fwts_log_info_verbatim(fw, " Access Size 0x%2.2" PRIx8, gas->access_width); fwts_log_info_verbatim(fw, " Address 0x%16.16" PRIx64, gas->address); fwts_log_info_verbatim(fw, " Doorbell Preserve: 0x%16.16" PRIx64, entry->doorbell_preserve); fwts_log_info_verbatim(fw, " Doorbell Write: 0x%16.16" PRIx64, entry->doorbell_write); fwts_log_info_verbatim(fw, " Nominal Latency: 0x%8.8" PRIx32, entry->nominal_latency); fwts_log_info_verbatim(fw, " Max Periodic Access Rate: 0x%8.8" PRIx32, entry->max_periodic_access_rate); fwts_log_info_verbatim(fw, " Min Request Turnaround Time: 0x%8.8" PRIx32, entry->min_request_turnaround_time); if ((gas->address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_IO) && (gas->address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_MEMORY)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "PCCTSubspaceInvalidAddrSpaceID", "PCCT Subspace Type 0 has space ID = 0x%2.2" PRIx8 " which is not System I/O or Memory", gas->address_space_id); } }
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; }
/* * DRTM D-RTM Resources Table */ static int drtm_test1(fwts_framework *fw) { fwts_acpi_table_drtm *drtm = (fwts_acpi_table_drtm*) table->data; fwts_acpi_table_drtm_vtl *drtm_vtl; fwts_acpi_table_drtm_rtl *drtm_rtl; fwts_acpi_table_drtm_dps *drtm_dps; bool passed = true; uint32_t offset; uint32_t i; fwts_log_info_verbatim(fw, "DRTM D-RTM Resources Table:"); fwts_log_info_verbatim(fw, " DL_Entry_Base: 0x%16.16" PRIx64, drtm->entry_base_address); fwts_log_info_verbatim(fw, " DL_Entry_Length: 0x%16.16" PRIx64, drtm->entry_length); fwts_log_info_verbatim(fw, " DL_Entry32: 0x%8.8" PRIx32, drtm->entry_address32); fwts_log_info_verbatim(fw, " DL_Entry64: 0x%16.16" PRIx64, drtm->entry_address64); fwts_log_info_verbatim(fw, " DLME_Exit: 0x%16.16" PRIx64, drtm->exit_address); fwts_log_info_verbatim(fw, " Log_Area_Start: 0x%16.16" PRIx64, drtm->log_area_address); fwts_log_info_verbatim(fw, " Log_Area_Length: 0x%8.8" PRIx32, drtm->log_area_length); fwts_log_info_verbatim(fw, " Architecture_Dependent: 0x%16.16" PRIx64, drtm->arch_dependent_address); fwts_log_info_verbatim(fw, " DRT_Flags: 0x%8.8" PRIx32, drtm->flags); fwts_acpi_reserved_bits_check(fw, "DRTM", "DRT_Flags", drtm->flags, sizeof(drtm->flags), 4, 31, &passed); fwts_log_nl(fw); offset = sizeof(fwts_acpi_table_drtm); drtm_vtl = (fwts_acpi_table_drtm_vtl *) (table->data + offset); fwts_log_info_verbatim(fw, " VTL_Length: 0x%8.8" PRIx32, drtm_vtl->validated_table_count); offset += sizeof(drtm_vtl->validated_table_count); if (drtm->header.length < offset + sizeof(uint64_t) * drtm_vtl->validated_table_count) { fwts_failed(fw, LOG_LEVEL_HIGH, "DRTMOutOfBound", "DRTM's length is too small to contain all fields"); goto error; } for (i = 0; i < drtm_vtl->validated_table_count; i++) { fwts_log_info_verbatim(fw, " Validated_Tables: 0x%16.16" PRIx64, drtm_vtl->validated_tables[i]); offset += sizeof(drtm_vtl->validated_tables[i]); } fwts_log_nl(fw); drtm_rtl = (fwts_acpi_table_drtm_rtl *) (table->data + offset); fwts_log_info_verbatim(fw, " RL_Length: 0x%8.8" PRIx32, drtm_rtl->resource_count); offset += sizeof(drtm_rtl->resource_count); if (drtm->header.length < offset + sizeof(fwts_acpi_drtm_resource) * drtm_rtl->resource_count) { fwts_failed(fw, LOG_LEVEL_HIGH, "DRTMOutOfBound", "DRTM's length is too small to contain all fields"); goto error; } for (i = 0; i < drtm_rtl->resource_count; i++) { fwts_acpi_drtm_resource *resource = (fwts_acpi_drtm_resource *) (table->data + offset); uint64_t size; size = resource->size[0] + ((uint64_t) resource->size[1] << 8) + ((uint64_t) resource->size[2] << 16) + ((uint64_t) resource->size[3] << 24) + ((uint64_t) resource->size[4] << 32) + ((uint64_t) resource->size[5] << 40) + ((uint64_t) resource->size[6] << 48); fwts_log_info_verbatim(fw, " Resource Size: 0x%16.16" PRIx64, size); fwts_log_info_verbatim(fw, " Resource Type: 0x%2.2" PRIx8, resource->type); fwts_log_info_verbatim(fw, " Resource Address: 0x%16.16" PRIx64, resource->address); if (resource->type & 0x7C) { passed = false; fwts_failed(fw, LOG_LEVEL_MEDIUM, "DRTMBadResourceType", "DRTM Resource Type Bits [6:2] are reserved, got 0x%2.2" PRIx8 " instead", resource->type); } offset += sizeof(fwts_acpi_drtm_resource); fwts_log_nl(fw); } drtm_dps = (fwts_acpi_table_drtm_dps *) (table->data + offset); fwts_log_info_verbatim(fw, " DPS_Length: 0x%8.8" PRIx32, drtm_dps->dps_id_length); if (drtm->header.length < offset + sizeof(fwts_acpi_table_drtm_dps)) { fwts_failed(fw, LOG_LEVEL_HIGH, "DRTMOutOfBound", "DRTM's length is too small to contain all fields"); goto error; } for (i = 0; i < sizeof(drtm_dps->dps_id); i++) { fwts_log_info_verbatim(fw, " DLME Platform Id: 0x%2.2" PRIx8, drtm_dps->dps_id[i]); } fwts_log_nl(fw); if (passed) fwts_passed(fw, "No issues found in DRTM table."); error: return FWTS_OK; }
static int acpidump_test1(fwts_framework *fw) { int i; fwts_infoonly(fw); if (fwts_iasl_init(fw) != FWTS_OK) { fwts_aborted(fw, "Failure to initialise iasl, aborting."); return FWTS_ERROR; } for (i = 0; i < ACPI_MAX_TABLES; i++) { fwts_acpi_table_info *table; fwts_list *output; char *provenance; if (fwts_acpi_get_table(fw, i, &table) != FWTS_OK) break; if (table == NULL) break; switch (table->provenance) { case FWTS_ACPI_TABLE_FROM_FILE: provenance = " (loaded from file)"; break; case FWTS_ACPI_TABLE_FROM_FIXUP: provenance = " (generated by fwts)"; break; default: provenance = ""; break; } fwts_log_info_verbatim(fw, "%s @ %lx (%zd bytes)%s", table->name, (unsigned long)table->addr, table->length, provenance); fwts_log_info_verbatim(fw, "----"); if (!strcmp(table->name, "RSDP")) { /* RSDP is a special case */ acpidump_rsdp(fw, table); } else if (table->has_aml) { /* Disassembling the AML bloats the output, so ignore */ uint8_t *data = (uint8_t *)table->data; fwts_acpi_table_header hdr; fwts_acpi_table_get_header(&hdr, data); acpidump_hdr(fw, &hdr, table->length); fwts_log_info_verbatim(fw, "Contains AML Object Code."); } else if (fwts_iasl_disassemble(fw, table, true, &output) != FWTS_OK) { /* Cannot find, assume standard table header */ uint8_t *data = (uint8_t *)table->data; fwts_acpi_table_header hdr; fwts_acpi_table_get_header(&hdr, data); acpidump_hdr(fw, &hdr, table->length); acpi_dump_raw_table(fw, table); } else { /* Successfully disassembled, so parse */ fwts_list_link *line; bool skip = false; bool unknown = false; fwts_list_foreach(line, output) { char *text = fwts_text_list_text(line); bool ignore = (strstr(text, "Raw Table Data:") != NULL); /* We don't want to emit this line */ if (strstr(text, "Unknown ACPI table signature") != NULL) { unknown = true; ignore = true; } /* and we want to ignore text in comments */ if (!strncmp(text, "/*", 2)) skip = true; if (!(ignore | skip | unknown)) fwts_log_info_verbatim(fw, "%s", fwts_text_list_text(line)); if (!strncmp(text, " */", 3)) skip = false; } fwts_text_list_free(output); if (unknown) acpi_dump_raw_table(fw, table); } fwts_log_nl(fw); }
/* * ASF! Hardware Error Source Table test * http://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf */ static int asf_test1(fwts_framework *fw) { bool passed = true; bool abort = false; fwts_acpi_table_header *hdr = (fwts_acpi_table_header *)table->data; uint8_t *data = (uint8_t *)table->data; ssize_t length = (ssize_t)hdr->length; fwts_log_info_verbatim(fw, "ASF! Hardware Error Source Table"); data += sizeof(fwts_acpi_table_header); length -= sizeof(fwts_acpi_table_header); while (!abort && (length > 0)) { bool asf_passed = true; fwts_acpi_table_asf_header *asf_hdr = (fwts_acpi_table_asf_header *)data; /* Must have enough for a info header */ if (length < (ssize_t)sizeof(fwts_acpi_table_asf_header)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!TooShort", "ASF! table too short, expecting at least %zu bytes " "for an ASF! information record header, " "instead got %zu bytes", sizeof(fwts_acpi_table_asf_header), table->length); break; } #if ASF_DUMP fwts_log_info_verbatim(fw, "Type: 0x%2.2" PRIx8, asf_hdr->type); fwts_log_info_verbatim(fw, "Reserved: 0x%2.2" PRIx8, asf_hdr->reserved); fwts_log_info_verbatim(fw, "Length: 0x%4.4" PRIx16, asf_hdr->length); #endif fwts_acpi_reserved_zero_check(fw, "ASF!", "Information Record Reserved", asf_hdr->reserved, sizeof(asf_hdr->reserved), &passed); if (asf_hdr->length > (uint32_t)length) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InfoRecordLengthTooLong", "ASF! Information Record Reserved length is %" PRIu32 " and this is too long for the size given by " "the ASF! table. Expected at most %zu bytes.", asf_hdr->length, length); /* Since we can't trust the table, abort */ break; } switch (asf_hdr->type) { case 0x00: case 0x80: asf_check_info(fw, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x01: case 0x81: asf_check_alrt(fw, asf_hdr->length, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x02: case 0x82: asf_check_rctl(fw, asf_hdr->length, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x03: case 0x83: asf_check_rmcp(fw, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; case 0x04: case 0x84: asf_check_addr(fw, asf_hdr->length, length - sizeof(*asf_hdr), data + sizeof(*asf_hdr), &asf_passed, &abort); data += asf_hdr->length; length -= asf_hdr->length; break; default: fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!InvalidType", "ASF! Information Record Type 0x%4.4" PRIx16 " is invalid, aborting check", asf_hdr->type); passed = false; length = 0; break; } passed &= asf_passed; #if ASF_DUMP fwts_log_nl(fw); #endif } #if ASF_DUMP fwts_log_nl(fw); #endif if (passed) fwts_passed(fw, "No issues found in ASF! table."); return FWTS_OK; }
static int 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; }
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; }
/* * 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.1.2.2 ASF_ALRT */ static void asf_check_alrt( fwts_framework *fw, ssize_t record_length, ssize_t length, uint8_t *data, bool *passed, bool *abort) { fwts_acpi_table_asf_alrt *alrt = (fwts_acpi_table_asf_alrt *)data; uint8_t i; ssize_t total_length; if (length < (ssize_t)sizeof(fwts_acpi_table_asf_alrt)) { fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtRecordTooShort", "ASF! ASF_ALRT Record too short, " "expecting %zu bytes, instead got %zu bytes", sizeof(fwts_acpi_table_asf_alrt), length); *passed = false; *abort = true; return; } #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_ALRT Record:"); fwts_log_info_verbatim(fw, " Assertion Event Mask: 0x%2.2" PRIx8, alrt->assertion_mask); fwts_log_info_verbatim(fw, " De-Assertion Event Mask: 0x%2.2" PRIx8, alrt->deassertion_mask); fwts_log_info_verbatim(fw, " Number of Alerts: 0x%2.2" PRIx8, alrt->number_of_alerts); fwts_log_info_verbatim(fw, " Array Element Length: 0x%2.2" PRIx8, alrt->array_length); #endif if ((alrt->number_of_alerts < 1) || (alrt->number_of_alerts > 8)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtNumOfAlertsInvalid", "ASF! ASF_ALRT Number of Alerts field is 0x%" PRIx8 " and must be in the range 0x01..0x08", alrt->number_of_alerts); /* Don't trust the ALRT data, so abort */ return; } if (alrt->array_length != sizeof(fwts_acpi_table_asf_alrt_element)) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtArrayElementLengthInvalid", "ASF! ASF_ALRT Array Element Length is 0x%" PRIx8 " and must be instead 0x%" PRIx8, alrt->array_length, (uint8_t)sizeof(fwts_acpi_table_asf_alrt_element)); } total_length = sizeof(fwts_acpi_table_asf_alrt) + (alrt->number_of_alerts * sizeof(fwts_acpi_table_asf_alrt_element)); if (total_length > record_length) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtArrayElementLengthInvalid", "ASF! ASF_ALRT Array Element Length makes the " "total ASF_ALRT record size to be %zu bytes, however the " "table is only %zu bytes long", total_length, record_length); *passed = false; *abort = true; return; } data += sizeof(fwts_acpi_table_asf_alrt); for (i = 0; i < alrt->number_of_alerts; i++) { fwts_acpi_table_asf_alrt_element *element = (fwts_acpi_table_asf_alrt_element *)data; #if ASF_DUMP fwts_log_info_verbatim(fw, "ASF! ASF_ALRT Element %" PRIu8 ":", i); fwts_log_info_verbatim(fw, " Device Address: 0x%2.2" PRIx8, element->device_addr); fwts_log_info_verbatim(fw, " Alert Command: 0x%2.2" PRIx8, element->command); fwts_log_info_verbatim(fw, " Alert Data Mask: 0x%2.2" PRIx8, element->data_mask); fwts_log_info_verbatim(fw, " Alert Compare Value: 0x%2.2" PRIx8, element->compare_value); fwts_log_info_verbatim(fw, " Alert Event Sensor Type: 0x%2.2" PRIx8, element->sensor_type); fwts_log_info_verbatim(fw, " Alert Event Type: 0x%2.2" PRIx8, element->event_type); fwts_log_info_verbatim(fw, " Alert Event Offset: 0x%2.2" PRIx8, element->event_offset); fwts_log_info_verbatim(fw, " Alert Source Type: 0x%2.2" PRIx8, element->event_source_type); fwts_log_info_verbatim(fw, " Alert Event Severity: 0x%2.2" PRIx8, element->event_severity); fwts_log_info_verbatim(fw, " Alert Sensor Number: 0x%2.2" PRIx8, element->sensor_number); fwts_log_info_verbatim(fw, " Alert Entity: 0x%2.2" PRIx8, element->entity); fwts_log_info_verbatim(fw, " Alert Entity Instance: 0x%2.2" PRIx8, element->entity_instance); #endif if (element->event_offset & 0x80) { *passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "ASF!AlrtEventOffsetBit7Set", "ASF! ASF_ALRT Array Element %" PRIu8 " Event Offset Bit 7 is 1, " " and should be 0", i); } data += sizeof(fwts_acpi_table_asf_alrt_element); } if (*passed) fwts_passed(fw, "No issues found in ASF! ASF_ALRT record."); }
static void extended_pcc_test(fwts_framework *fw, fwts_acpi_table_pcct_subspace_type_3_4 *entry, bool *passed) { fwts_log_info_verbatim(fw, " Platform Interrupt: 0x%8.8" PRIx32, entry->platform_interrupt); fwts_log_info_verbatim(fw, " Platform Interrupt Flags: 0x%2.2" PRIx8, entry->platform_interrupt_flags); fwts_log_info_verbatim(fw, " Reserved: 0x%2.2" PRIx8, entry->reserved1); fwts_log_info_verbatim(fw, " Base Address: 0x%16.16" PRIx64, entry->base_address); memory_length(fw, entry->header.type, entry->length, 16, passed); fwts_log_info_verbatim(fw, " Doorbell Register:"); gas_messages(fw, entry->header.type, &entry->doorbell_register, passed); fwts_log_info_verbatim(fw, " Doorbell Preserve: 0x%16.16" PRIx64, entry->doorbell_preserve); fwts_log_info_verbatim(fw, " Doorbell Write: 0x%16.16" PRIx64, entry->doorbell_write); fwts_log_info_verbatim(fw, " Nominal Latency: 0x%8.8" PRIx32, entry->nominal_latency); fwts_log_info_verbatim(fw, " Max Periodic Access Rate: 0x%8.8" PRIx32, entry->max_periodic_access_rate); fwts_log_info_verbatim(fw, " Min Request Turnaround Time: 0x%8.8" PRIx32, entry->min_request_turnaround_time); fwts_log_info_verbatim(fw, " Command Complete Check Register:"); gas_messages(fw, entry->header.type, &entry->platform_ack_register, passed); fwts_log_info_verbatim(fw, " Doorbell Ack Preserve: 0x%16.16" PRIx64, entry->platform_ack_preserve); fwts_log_info_verbatim(fw, " Doorbell Ack Write: 0x%16.16" PRIx64, entry->platform_ack_write); fwts_log_info_verbatim(fw, " Reserved: 0x%16.16" PRIx64, entry->reserved2); fwts_log_info_verbatim(fw, " Cmd Complete Check Register:"); gas_messages(fw, entry->header.type, &entry->cmd_complete_register, passed); fwts_log_info_verbatim(fw, " Cmd Complete Check Mask: 0x%16.16" PRIx64, entry->cmd_complete_mask); fwts_log_info_verbatim(fw, " Cmd Complete Update Register:"); gas_messages(fw, entry->header.type, &entry->cmd_update_register, passed); fwts_log_info_verbatim(fw, " Cmd Complete Update Mask: 0x%16.16" PRIx64, entry->cmd_update_preserve_mask); fwts_log_info_verbatim(fw, " Cmd Complete Set Mask: 0x%16.16" PRIx64, entry->cmd_update_preserve_mask); fwts_log_info_verbatim(fw, " Error Status Register:"); gas_messages(fw, entry->header.type, &entry->error_status_register, passed); fwts_log_info_verbatim(fw, " Error Status Mask: 0x%16.16" PRIx64, entry->error_status_mask); fwts_acpi_reserved_bits_check(fw, "PCCT", "Platform Interrupt Flags", entry->platform_interrupt_flags, sizeof(uint8_t), 2, 7, passed); }
/* * 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; }
/* * HEST Hardware Error Source Table test * ACPI section 18.3.2 "ACPI Error Source" */ static int hest_test1(fwts_framework *fw) { bool passed = true; fwts_acpi_table_hest *hest = (fwts_acpi_table_hest *)table->data; uint8_t *data = (uint8_t *)table->data; ssize_t length = (ssize_t)hest->header.length; uint32_t hest_type_00_count = 0, hest_type_01_count = 0, hest_type_02_count = 0, hest_type_11_count = 0; if (table->length < sizeof(fwts_acpi_table_hest)) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooShort", "HEST table too short, expecting %zu bytes, " "instead got %zu bytes", sizeof(fwts_acpi_table_hest), table->length); goto done; } fwts_log_info_verbatim(fw, "HEST Hardware Error Source Table test"); fwts_log_info_verbatim(fw, " Error Source Count: 0x%2.2" PRIx8, hest->error_source_count); fwts_log_nl(fw); data += sizeof(fwts_acpi_table_hest); length -= sizeof(fwts_acpi_table_hest); while (length > 0) { uint16_t *type = (uint16_t *)data; switch (*type) { case 0: hest_check_ia32_arch_machine_check_exception(fw, &length, &data, &passed); hest_type_00_count++; break; case 1: hest_check_ia32_arch_corrected_machine_check(fw, &length, &data, &passed); hest_type_01_count++; break; case 2: hest_check_acpi_table_hest_nmi_error(fw, &length, &data, &passed); hest_type_02_count++; break; case 6: hest_check_pci_express_root_port_aer(fw, &length, &data, &passed); break; case 7: hest_check_pci_express_device_aer(fw, &length, &data, &passed); break; case 8: hest_heck_pci_express_bridge_aer(fw, &length, &data, &passed); break; case 9: hest_check_generic_error_source(fw, &length, &data, &passed); break; case 10: hest_check_generic_error_source_v2(fw, &length, &data, &passed); break; case 11: /* the structure of type 11 is the same as type 1 */ hest_check_ia32_arch_corrected_machine_check(fw, &length, &data, &passed); hest_type_11_count++; break; default: fwts_failed(fw, LOG_LEVEL_HIGH, "HESTInvalidType", "HEST Type 0x%4.4" PRIx16 " is invalid, aborting check", *type); passed = false; length = 0; break; } } if (hest_type_00_count > 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooManyIA32ArchMachineCheckExceptions", "HEST Contained %" PRIu32 " IA32 Architecture " "Machine Check Exception Entries, maximum allowed " "per HEST is just 1.", hest_type_00_count); } if (hest_type_01_count > 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooManyIA32CorrectedMachineChecks", "HEST Contained %" PRIu32 " IA32 Architecture " "Corrected Machine Check Exception Entries, maximum allowed " "per HEST is just 1.", hest_type_01_count); } if (hest_type_02_count > 1) { passed = false; fwts_failed(fw, LOG_LEVEL_HIGH, "HESTTooManyNmiErrors", "HEST Contained %" PRIu32 " NMI Error Entries, " "maximum allowed per HEST is just 1.", hest_type_02_count); } if (passed) fwts_passed(fw, "No issues found in HEST table."); done: return FWTS_OK; }
static int 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; }