@@ -534,6 +534,84 @@ int acpi_ghes_record_errors(enum AcpiGhesNotifyType notify,
NotifierList acpi_generic_error_notifiers =
NOTIFIER_LIST_INITIALIZER(error_device_notifiers);
+void ghes_record_cper_errors(AcpiGhesCper *cper, Error **errp,
+ enum AcpiGhesNotifyType notify)
+{
+ uint64_t cper_addr, read_ack_start_addr;
+ uint64_t read_ack = 0;
+ uint32_t data_length;
+ GArray *block;
+ uint32_t i;
+
+ if (ghes_get_hardware_errors_address(notify, NULL, &read_ack_start_addr,
+ &cper_addr, NULL)) {
+ error_setg(errp,
+ "GHES: Invalid error block/ack address(es) for notify %d",
+ notify);
+ return;
+ }
+
+ cpu_physical_memory_read(read_ack_start_addr,
+ &read_ack, sizeof(uint64_t));
+
+ /* zero means OSPM does not acknowledge the error */
+ if (!read_ack) {
+ error_setg(errp,
+ "Last CPER record was not acknowledged yet");
+ read_ack = 1;
+ cpu_physical_memory_write(read_ack_start_addr,
+ &read_ack, sizeof(uint64_t));
+ return;
+ }
+
+ read_ack = cpu_to_le64(0);
+ cpu_physical_memory_write(read_ack_start_addr,
+ &read_ack, sizeof(uint64_t));
+
+ /* Build CPER record */
+
+ /*
+ * Invalid fru id: ACPI 4.0: 17.3.2.6.1 Generic Error Data,
+ * Table 17-13 Generic Error Data Entry
+ */
+ QemuUUID fru_id = {};
+
+ block = g_array_new(false, true /* clear */, 1);
+ data_length = ACPI_GHES_DATA_LENGTH + cper->data_len;
+
+ /*
+ * It should not run out of the preallocated memory if
+ * adding a new generic error data entry
+ */
+ if ((data_length + ACPI_GHES_GESB_SIZE) >
+ ACPI_GHES_MAX_RAW_DATA_LENGTH) {
+ error_setg(errp, "GHES CPER record is too big: %d",
+ data_length);
+ }
+
+ /* Build the new generic error status block header */
+ acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE,
+ 0, 0, data_length,
+ ACPI_CPER_SEV_RECOVERABLE);
+
+ /* Build this new generic error data entry header */
+ acpi_ghes_generic_error_data(block, cper->guid,
+ ACPI_CPER_SEV_RECOVERABLE, 0, 0,
+ cper->data_len, fru_id, 0);
+
+ /* Add CPER data */
+ for (i = 0; i < cper->data_len; i++) {
+ build_append_int_noprefix(block, cper->data[i], 1);
+ }
+
+ /* Write the generic error data entry into guest memory */
+ cpu_physical_memory_write(cper_addr, block->data, block->len);
+
+ g_array_free(block, true);
+
+ notifier_list_notify(&acpi_generic_error_notifiers, NULL);
+}
+
bool acpi_ghes_present(void)
{
AcpiGedState *acpi_ged_state;
@@ -39,7 +39,7 @@ void qmp_ghes_cper(CommonPlatformErrorRecord *qmp_cper,
return;
}
- /* TODO: call a function at ghes */
+ ghes_record_cper_errors(&cper, errp, ACPI_GHES_NOTIFY_GPIO);
g_free(cper.data);
}
@@ -79,6 +79,9 @@ typedef struct AcpiGhesCper {
size_t data_len;
} AcpiGhesCper;
+void ghes_record_cper_errors(AcpiGhesCper *cper, Error **errp,
+ enum AcpiGhesNotifyType notify);
+
/**
* acpi_ghes_present: Report whether ACPI GHES table is present
*