src/drivers/ipmi: Implement BMC Get Self Test Result function

According to IPMI SPEC, it is recommended that BIOS includes provisions
for checking and reporting on the basic health of BMC by executing
the Get Self Test Results command and checking the result.

TEST=Check the result in response data to confirm the BMC status is fine
or not.

Change-Id: I20349cec2e8e9420d177d725de2a5560d354fe47
Signed-off-by: Morgan Jang <Morgan_Jang@wiwynn.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36638
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: David Hendricks <david.hendricks@gmail.com>
diff --git a/src/drivers/ipmi/ipmi_kcs_ops.c b/src/drivers/ipmi/ipmi_kcs_ops.c
index 90f19dd..5cb8995 100644
--- a/src/drivers/ipmi/ipmi_kcs_ops.c
+++ b/src/drivers/ipmi/ipmi_kcs_ops.c
@@ -59,11 +59,34 @@
 	return 0;
 }
 
+static int ipmi_get_bmc_self_test_result(struct device *dev, struct ipmi_selftest_rsp *rsp)
+{
+	int ret;
+
+	ret = ipmi_kcs_message(dev->path.pnp.port, IPMI_NETFN_APPLICATION, 0,
+				 IPMI_BMC_GET_SELFTEST_RESULTS, NULL, 0, (u8 *)rsp,
+				 sizeof(*rsp));
+
+	if (ret < sizeof(struct ipmi_rsp) || rsp->resp.completion_code) {
+		printk(BIOS_ERR, "IPMI: %s command failed (ret=%d resp=0x%x)\n",
+		       __func__, ret, rsp->resp.completion_code);
+		return 1;
+	}
+	if (ret != sizeof(*rsp)) {
+		printk(BIOS_ERR, "IPMI: %s response truncated\n", __func__);
+		return 1;
+	}
+
+	return 0;
+}
+
 static void ipmi_kcs_init(struct device *dev)
 {
 	struct ipmi_devid_rsp rsp;
 	uint32_t man_id = 0, prod_id = 0;
 	struct drivers_ipmi_config *conf = NULL;
+	struct ipmi_selftest_rsp selftestrsp;
+	uint8_t retry_count;
 
 	if (!dev->enabled)
 		return;
@@ -92,6 +115,42 @@
 		}
 	}
 
+	printk(BIOS_INFO, "Get BMC self test result...");
+	for (retry_count = 0; retry_count < conf->bmc_boot_timeout; retry_count++) {
+		if (!ipmi_get_bmc_self_test_result(dev, &selftestrsp))
+			break;
+
+		mdelay(1000);
+	}
+
+	switch (selftestrsp.result) {
+	case IPMI_APP_SELFTEST_NO_ERROR: /* 0x55 */
+		printk(BIOS_DEBUG, "No Error\n");
+		break;
+	case IPMI_APP_SELFTEST_NOT_IMPLEMENTED: /* 0x56 */
+		printk(BIOS_DEBUG, "Function Not Implemented\n");
+		break;
+	case IPMI_APP_SELFTEST_ERROR: /* 0x57 */
+		printk(BIOS_ERR, "BMC: Corrupted or inaccessible data or device\n");
+		/* Don't write tables if communication failed */
+		dev->enabled = 0;
+		break;
+	case IPMI_APP_SELFTEST_FATAL_HW_ERROR: /* 0x58 */
+		printk(BIOS_ERR, "BMC: Fatal Hardware Error\n");
+		/* Don't write tables if communication failed */
+		dev->enabled = 0;
+		break;
+	case IPMI_APP_SELFTEST_RESERVED: /* 0xFF */
+		printk(BIOS_DEBUG, "Reserved\n");
+		break;
+
+	default: /* Other Device Specific Hardware Error */
+		printk(BIOS_ERR, "BMC: Device Specific Error\n");
+		/* Don't write tables if communication failed */
+		dev->enabled = 0;
+		break;
+	}
+
 	if (!ipmi_get_device_id(dev, &rsp)) {
 		/* Queried the IPMI revision from BMC */
 		ipmi_revision_minor = IPMI_IPMI_VERSION_MINOR(rsp.ipmi_version);