@@ -248,6 +248,7 @@ config FPGA_MGR_VERSAL_FPGA
config FPGA_M10_BMC_SEC_UPDATE
tristate "Intel MAX10 BMC Secure Update driver"
depends on MFD_INTEL_M10_BMC
+ depends on HOTPLUG_PCI_FPGA
select FW_LOADER
select FW_UPLOAD
help
@@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/fpga/fpgahp_manager.h>
#include <linux/mfd/intel-m10-bmc.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
@@ -19,10 +20,21 @@ struct m10bmc_sec {
struct intel_m10bmc *m10bmc;
struct fw_upload *fwl;
char *fw_name;
+ struct image_load *image_load;
+ struct fpgahp_bmc_device *fpgahp_bmc;
u32 fw_name_id;
bool cancel_request;
};
+struct image_load {
+ const char *name;
+ int (*load_image)(struct m10bmc_sec *sec);
+ u32 wait_time_msec;
+};
+
+/* default wait 10 seconds for FPGA/BMC image reload */
+#define RELOAD_DEFAULT_WAIT_MSECS (10 * MSEC_PER_SEC)
+
static DEFINE_XARRAY_ALLOC(fw_upload_xa);
/* Root Entry Hash (REH) support */
@@ -137,6 +149,43 @@ DEVICE_ATTR_SEC_CSK_RO(pr, PR_PROG_ADDR + CSK_VEC_OFFSET);
#define FLASH_COUNT_SIZE 4096 /* count stored as inverted bit vector */
+static ssize_t m10bmc_available_images(struct fpgahp_bmc_device *bmc, char *buf)
+{
+ struct m10bmc_sec *sec = bmc->priv;
+ const struct image_load *hndlr;
+ ssize_t count = 0;
+
+ for (hndlr = sec->image_load; hndlr->name; hndlr++)
+ count += scnprintf(buf + count, PAGE_SIZE - count, "%s ", hndlr->name);
+
+ buf[count - 1] = '\n';
+
+ return count;
+}
+
+static int m10bmc_image_trigger(struct fpgahp_bmc_device *bmc, const char *buf,
+ u32 *wait_time_msec)
+{
+ struct m10bmc_sec *sec = bmc->priv;
+ const struct image_load *hndlr;
+ int ret = -EINVAL;
+
+ for (hndlr = sec->image_load; hndlr->name; hndlr++) {
+ if (sysfs_streq(buf, hndlr->name)) {
+ ret = hndlr->load_image(sec);
+ *wait_time_msec = hndlr->wait_time_msec;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct fpgahp_bmc_ops fpgahp_bmc_ops = {
+ .image_trigger = m10bmc_image_trigger,
+ .available_images = m10bmc_available_images,
+};
+
static ssize_t flash_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -208,6 +257,54 @@ static void log_error_regs(struct m10bmc_sec *sec, u32 doorbell)
dev_err(sec->dev, "RSU auth result: 0x%08x\n", auth_result);
}
+static int m10bmc_sec_bmc_image_load(struct m10bmc_sec *sec,
+ unsigned int val)
+{
+ u32 doorbell;
+ int ret;
+
+ if (val > 1) {
+ dev_err(sec->dev, "invalid reload val = %u\n", val);
+ return -EINVAL;
+ }
+
+ ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell);
+ if (ret)
+ return ret;
+
+ if (doorbell & DRBL_REBOOT_DISABLED)
+ return -EBUSY;
+
+ return regmap_update_bits(sec->m10bmc->regmap,
+ M10BMC_SYS_BASE + M10BMC_DOORBELL,
+ DRBL_CONFIG_SEL | DRBL_REBOOT_REQ,
+ FIELD_PREP(DRBL_CONFIG_SEL, val) | DRBL_REBOOT_REQ);
+}
+
+static int m10bmc_sec_bmc_image_load_0(struct m10bmc_sec *sec)
+{
+ return m10bmc_sec_bmc_image_load(sec, 0);
+}
+
+static int m10bmc_sec_bmc_image_load_1(struct m10bmc_sec *sec)
+{
+ return m10bmc_sec_bmc_image_load(sec, 1);
+}
+
+static struct image_load m10bmc_image_load_hndlrs[] = {
+ {
+ .name = "bmc_factory",
+ .load_image = m10bmc_sec_bmc_image_load_1,
+ .wait_time_msec = RELOAD_DEFAULT_WAIT_MSECS,
+ },
+ {
+ .name = "bmc_user",
+ .load_image = m10bmc_sec_bmc_image_load_0,
+ .wait_time_msec = RELOAD_DEFAULT_WAIT_MSECS,
+ },
+ {}
+};
+
static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec)
{
u32 doorbell;
@@ -565,6 +662,7 @@ static int m10bmc_sec_probe(struct platform_device *pdev)
sec->dev = &pdev->dev;
sec->m10bmc = dev_get_drvdata(pdev->dev.parent);
dev_set_drvdata(&pdev->dev, sec);
+ sec->image_load = m10bmc_image_load_hndlrs;
ret = xa_alloc(&fw_upload_xa, &sec->fw_name_id, sec,
xa_limit_32b, GFP_KERNEL);
@@ -587,6 +685,16 @@ static int m10bmc_sec_probe(struct platform_device *pdev)
}
sec->fwl = fwl;
+
+ sec->fpgahp_bmc = fpgahp_bmc_device_register(&fpgahp_bmc_ops, sec->dev, sec);
+ if (IS_ERR(sec->fpgahp_bmc)) {
+ dev_err(sec->dev, "register hotplug bmc failed\n");
+ kfree(sec->fw_name);
+ xa_erase(&fw_upload_xa, sec->fw_name_id);
+ firmware_upload_unregister(sec->fwl);
+ return PTR_ERR(sec->fpgahp_bmc);
+ }
+
return 0;
}
@@ -594,6 +702,7 @@ static int m10bmc_sec_remove(struct platform_device *pdev)
{
struct m10bmc_sec *sec = dev_get_drvdata(&pdev->dev);
+ fpgahp_bmc_device_unregister(sec->fpgahp_bmc);
firmware_upload_unregister(sec->fwl);
kfree(sec->fw_name);
xa_erase(&fw_upload_xa, sec->fw_name_id);
@@ -626,3 +735,4 @@ module_platform_driver(intel_m10bmc_sec_driver);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel MAX10 BMC Secure Update");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(FPGAHP);