| /** |
| * Describes functions for converting VT-d specific DMAr CPER sections from binary and JSON format |
| * into an intermediate format. |
| * |
| * Author: Lawrence.Tang@arm.com |
| **/ |
| #include <stdio.h> |
| #include <string.h> |
| #include <json.h> |
| #include <libcper/base64.h> |
| #include <libcper/Cper.h> |
| #include <libcper/cper-utils.h> |
| #include <libcper/sections/cper-section-dmar-vtd.h> |
| |
| //Converts a single VT-d specific DMAr CPER section into JSON IR. |
| json_object *cper_section_dmar_vtd_to_ir(void *section) |
| { |
| EFI_DIRECTED_IO_DMAR_ERROR_DATA *vtd_error = |
| (EFI_DIRECTED_IO_DMAR_ERROR_DATA *)section; |
| json_object *section_ir = json_object_new_object(); |
| |
| //Version, revision and OEM ID, as defined in the VT-d architecture. |
| UINT64 oem_id = 0; |
| for (int i = 0; i < 6; i++) { |
| oem_id |= (UINT64)vtd_error->OemId[i] << (i * 8); |
| } |
| json_object_object_add(section_ir, "version", |
| json_object_new_int(vtd_error->Version)); |
| json_object_object_add(section_ir, "revision", |
| json_object_new_int(vtd_error->Revision)); |
| json_object_object_add(section_ir, "oemID", |
| json_object_new_uint64(oem_id)); |
| |
| //Registers. |
| json_object_object_add(section_ir, "capabilityRegister", |
| json_object_new_uint64(vtd_error->Capability)); |
| json_object_object_add(section_ir, "extendedCapabilityRegister", |
| json_object_new_uint64(vtd_error->CapabilityEx)); |
| json_object_object_add( |
| section_ir, "globalCommandRegister", |
| json_object_new_uint64(vtd_error->GlobalCommand)); |
| json_object_object_add(section_ir, "globalStatusRegister", |
| json_object_new_uint64(vtd_error->GlobalStatus)); |
| json_object_object_add(section_ir, "faultStatusRegister", |
| json_object_new_uint64(vtd_error->FaultStatus)); |
| |
| //Fault record basic fields. |
| json_object *fault_record_ir = json_object_new_object(); |
| EFI_VTD_FAULT_RECORD *fault_record = |
| (EFI_VTD_FAULT_RECORD *)vtd_error->FaultRecord; |
| json_object_object_add( |
| fault_record_ir, "faultInformation", |
| json_object_new_uint64(fault_record->FaultInformation)); |
| json_object_object_add( |
| fault_record_ir, "sourceIdentifier", |
| json_object_new_uint64(fault_record->SourceIdentifier)); |
| json_object_object_add( |
| fault_record_ir, "privelegeModeRequested", |
| json_object_new_boolean(fault_record->PrivelegeModeRequested)); |
| json_object_object_add( |
| fault_record_ir, "executePermissionRequested", |
| json_object_new_boolean( |
| fault_record->ExecutePermissionRequested)); |
| json_object_object_add( |
| fault_record_ir, "pasidPresent", |
| json_object_new_boolean(fault_record->PasidPresent)); |
| json_object_object_add( |
| fault_record_ir, "faultReason", |
| json_object_new_uint64(fault_record->FaultReason)); |
| json_object_object_add( |
| fault_record_ir, "pasidValue", |
| json_object_new_uint64(fault_record->PasidValue)); |
| json_object_object_add( |
| fault_record_ir, "addressType", |
| json_object_new_uint64(fault_record->AddressType)); |
| |
| //Fault record type. |
| json_object *fault_record_type = integer_to_readable_pair( |
| fault_record->Type, 2, VTD_FAULT_RECORD_TYPES_KEYS, |
| VTD_FAULT_RECORD_TYPES_VALUES, "Unknown"); |
| json_object_object_add(fault_record_ir, "type", fault_record_type); |
| json_object_object_add(section_ir, "faultRecord", fault_record_ir); |
| |
| //Root entry. |
| int32_t encoded_len = 0; |
| |
| char *encoded = |
| base64_encode((UINT8 *)vtd_error->RootEntry, 16, &encoded_len); |
| json_object_object_add(section_ir, "rootEntry", |
| json_object_new_string_len(encoded, |
| encoded_len)); |
| free(encoded); |
| |
| //Context entry. |
| encoded_len = 0; |
| encoded = base64_encode((UINT8 *)vtd_error->ContextEntry, 16, |
| &encoded_len); |
| if (encoded == NULL) { |
| printf("Failed to allocate encode output buffer. \n"); |
| } else { |
| json_object_object_add(section_ir, "contextEntry", |
| json_object_new_string_len(encoded, |
| encoded_len)); |
| free(encoded); |
| } |
| |
| //PTE entry for all page levels. |
| json_object_object_add(section_ir, "pageTableEntry_Level6", |
| json_object_new_uint64(vtd_error->PteL6)); |
| json_object_object_add(section_ir, "pageTableEntry_Level5", |
| json_object_new_uint64(vtd_error->PteL5)); |
| json_object_object_add(section_ir, "pageTableEntry_Level4", |
| json_object_new_uint64(vtd_error->PteL4)); |
| json_object_object_add(section_ir, "pageTableEntry_Level3", |
| json_object_new_uint64(vtd_error->PteL3)); |
| json_object_object_add(section_ir, "pageTableEntry_Level2", |
| json_object_new_uint64(vtd_error->PteL2)); |
| json_object_object_add(section_ir, "pageTableEntry_Level1", |
| json_object_new_uint64(vtd_error->PteL1)); |
| |
| return section_ir; |
| } |
| |
| //Converts a single VT-d DMAR CPER-JSON segment into CPER binary, outputting to the given stream. |
| void ir_section_dmar_vtd_to_cper(json_object *section, FILE *out) |
| { |
| EFI_DIRECTED_IO_DMAR_ERROR_DATA *section_cper = |
| (EFI_DIRECTED_IO_DMAR_ERROR_DATA *)calloc( |
| 1, sizeof(EFI_DIRECTED_IO_DMAR_ERROR_DATA)); |
| |
| //OEM ID. |
| UINT64 oem_id = json_object_get_uint64( |
| json_object_object_get(section, "oemID")); |
| for (int i = 0; i < 6; i++) { |
| section_cper->OemId[i] = (oem_id >> (i * 8)) & 0xFF; |
| } |
| |
| //Registers & basic numeric fields. |
| section_cper->Version = (UINT8)json_object_get_int( |
| json_object_object_get(section, "version")); |
| section_cper->Revision = (UINT8)json_object_get_int( |
| json_object_object_get(section, "revision")); |
| section_cper->Capability = json_object_get_uint64( |
| json_object_object_get(section, "capabilityRegister")); |
| section_cper->CapabilityEx = json_object_get_uint64( |
| json_object_object_get(section, "extendedCapabilityRegister")); |
| section_cper->GlobalCommand = json_object_get_uint64( |
| json_object_object_get(section, "globalCommandRegister")); |
| section_cper->GlobalStatus = json_object_get_uint64( |
| json_object_object_get(section, "globalStatusRegister")); |
| section_cper->FaultStatus = json_object_get_uint64( |
| json_object_object_get(section, "faultStatusRegister")); |
| |
| //Fault record. |
| json_object *fault_record = |
| json_object_object_get(section, "faultRecord"); |
| EFI_VTD_FAULT_RECORD *fault_record_cper = |
| (EFI_VTD_FAULT_RECORD *)section_cper->FaultRecord; |
| fault_record_cper->FaultInformation = json_object_get_uint64( |
| json_object_object_get(fault_record, "faultInformation")); |
| fault_record_cper->SourceIdentifier = json_object_get_uint64( |
| json_object_object_get(fault_record, "sourceIdentifier")); |
| fault_record_cper->PrivelegeModeRequested = json_object_get_boolean( |
| json_object_object_get(fault_record, "privelegeModeRequested")); |
| fault_record_cper->ExecutePermissionRequested = json_object_get_boolean( |
| json_object_object_get(fault_record, |
| "executePermissionRequested")); |
| fault_record_cper->PasidPresent = json_object_get_boolean( |
| json_object_object_get(fault_record, "pasidPresent")); |
| fault_record_cper->FaultReason = json_object_get_uint64( |
| json_object_object_get(fault_record, "faultReason")); |
| fault_record_cper->PasidValue = json_object_get_uint64( |
| json_object_object_get(fault_record, "pasidValue")); |
| fault_record_cper->AddressType = json_object_get_uint64( |
| json_object_object_get(fault_record, "addressType")); |
| fault_record_cper->Type = readable_pair_to_integer( |
| json_object_object_get(fault_record, "type")); |
| |
| //Root entry. |
| json_object *encoded = json_object_object_get(section, "rootEntry"); |
| int32_t decoded_len = 0; |
| |
| UINT8 *decoded = base64_decode(json_object_get_string(encoded), |
| json_object_get_string_len(encoded), |
| &decoded_len); |
| if (decoded == NULL) { |
| printf("Failed to allocate decode output buffer. \n"); |
| } else { |
| memcpy(section_cper->RootEntry, decoded, decoded_len); |
| free(decoded); |
| } |
| |
| //Context entry. |
| encoded = json_object_object_get(section, "contextEntry"); |
| decoded_len = 0; |
| |
| decoded = base64_decode(json_object_get_string(encoded), |
| json_object_get_string_len(encoded), |
| &decoded_len); |
| if (decoded == NULL) { |
| printf("Failed to allocate decode output buffer. \n"); |
| |
| } else { |
| memcpy(section_cper->ContextEntry, decoded, decoded_len); |
| free(decoded); |
| } |
| |
| //Page table entries. |
| section_cper->PteL1 = json_object_get_uint64( |
| json_object_object_get(section, "pageTableEntry_Level1")); |
| section_cper->PteL2 = json_object_get_uint64( |
| json_object_object_get(section, "pageTableEntry_Level2")); |
| section_cper->PteL3 = json_object_get_uint64( |
| json_object_object_get(section, "pageTableEntry_Level3")); |
| section_cper->PteL4 = json_object_get_uint64( |
| json_object_object_get(section, "pageTableEntry_Level4")); |
| section_cper->PteL5 = json_object_get_uint64( |
| json_object_object_get(section, "pageTableEntry_Level5")); |
| section_cper->PteL6 = json_object_get_uint64( |
| json_object_object_get(section, "pageTableEntry_Level6")); |
| |
| //Write to stream, free resources. |
| fwrite(section_cper, sizeof(EFI_DIRECTED_IO_DMAR_ERROR_DATA), 1, out); |
| fflush(out); |
| free(section_cper); |
| } |