raw/ifpga: add FPGA RSU APIs
authorWei Huang <wei.huang@intel.com>
Wed, 3 Mar 2021 02:34:29 +0000 (21:34 -0500)
committerQi Zhang <qi.z.zhang@intel.com>
Fri, 5 Mar 2021 08:55:55 +0000 (09:55 +0100)
RSU (Remote System Update) depends on secure manager which may be
different on various implementations, so a new secure manager device
is implemented for adapting such difference.
There are five APIs added:
1. rte_pmd_ifpga_get_dev_id() get raw device ID of ifpga device from PCI
   address like 'Domain:Bus:Dev.Func'.
2. rte_pmd_ifpga_update_flash() update flash with specific image file.
3. rte_pmd_ifpga_stop_update() abort flash update process.
4. rte_pmd_ifpga_reboot_try() check current ifpga status and change it
   to reboot status if it is idle.
5. rte_pmd_ifpga_reload() trigger full reconfiguration of ifpga device.

Signed-off-by: Wei Huang <wei.huang@intel.com>
Acked-by: Tianfei Zhang <tianfei.zhang@intel.com>
Acked-by: Rosen Xu <rosen.xu@intel.com>
17 files changed:
doc/api/doxy-api-index.md
doc/api/doxy-api.conf.in
drivers/raw/ifpga/base/ifpga_api.c
drivers/raw/ifpga/base/ifpga_fme.c
drivers/raw/ifpga/base/ifpga_fme_rsu.c [new file with mode: 0644]
drivers/raw/ifpga/base/ifpga_hw.h
drivers/raw/ifpga/base/ifpga_sec_mgr.c [new file with mode: 0644]
drivers/raw/ifpga/base/ifpga_sec_mgr.h [new file with mode: 0644]
drivers/raw/ifpga/base/meson.build
drivers/raw/ifpga/base/opae_hw_api.c
drivers/raw/ifpga/base/opae_hw_api.h
drivers/raw/ifpga/base/opae_intel_max10.c
drivers/raw/ifpga/base/opae_intel_max10.h
drivers/raw/ifpga/meson.build
drivers/raw/ifpga/rte_pmd_ifpga.c [new file with mode: 0644]
drivers/raw/ifpga/rte_pmd_ifpga.h [new file with mode: 0644]
drivers/raw/ifpga/version.map

index 748514e..8a48af1 100644 (file)
@@ -56,7 +56,8 @@ The public API headers are grouped by topics:
   [dpaa2_qdma]         (@ref rte_pmd_dpaa2_qdma.h),
   [crypto_scheduler]   (@ref rte_cryptodev_scheduler.h),
   [dlb]                (@ref rte_pmd_dlb.h),
-  [dlb2]               (@ref rte_pmd_dlb2.h)
+  [dlb2]               (@ref rte_pmd_dlb2.h),
+  [ifpga]              (@ref rte_pmd_ifpga.h)
 
 - **memory**:
   [memseg]             (@ref rte_memory.h),
index 5c883b6..5eb3150 100644 (file)
@@ -23,6 +23,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/drivers/net/softnic \
                           @TOPDIR@/drivers/raw/dpaa2_cmdif \
                           @TOPDIR@/drivers/raw/dpaa2_qdma \
+                          @TOPDIR@/drivers/raw/ifpga \
                           @TOPDIR@/drivers/raw/ioat \
                           @TOPDIR@/lib/librte_eal/include \
                           @TOPDIR@/lib/librte_eal/include/generic \
index 1ff57fa..1aedf15 100644 (file)
@@ -5,6 +5,7 @@
 #include "ifpga_api.h"
 #include "ifpga_enumerate.h"
 #include "ifpga_feature_dev.h"
+#include "ifpga_sec_mgr.h"
 
 #include "opae_hw_api.h"
 
@@ -228,11 +229,36 @@ static int ifpga_mgr_get_board_info(struct opae_manager *mgr,
        return 0;
 }
 
+static int ifpga_mgr_update_flash(struct opae_manager *mgr, const char *image,
+       u64 *status)
+{
+       struct ifpga_fme_hw *fme = mgr->data;
+
+       return fpga_update_flash(fme, image, status);
+}
+
+static int ifpga_mgr_stop_flash_update(struct opae_manager *mgr, int force)
+{
+       struct ifpga_fme_hw *fme = mgr->data;
+
+       return fpga_stop_flash_update(fme, force);
+}
+
+static int ifpga_mgr_reload(struct opae_manager *mgr, int type, int page)
+{
+       struct ifpga_fme_hw *fme = mgr->data;
+
+       return fpga_reload(fme, type, page);
+}
+
 struct opae_manager_ops ifpga_mgr_ops = {
        .flash = ifpga_mgr_flash,
        .get_eth_group_region_info = ifpga_mgr_get_eth_group_region_info,
        .get_sensor_value = ifpga_mgr_get_sensor_value,
        .get_board_info = ifpga_mgr_get_board_info,
+       .update_flash = ifpga_mgr_update_flash,
+       .stop_flash_update = ifpga_mgr_stop_flash_update,
+       .reload = ifpga_mgr_reload,
 };
 
 static int ifpga_mgr_read_mac_rom(struct opae_manager *mgr, int offset,
index f29ff31..34fd9a8 100644 (file)
@@ -7,6 +7,7 @@
 #include "opae_intel_max10.h"
 #include "opae_i2c.h"
 #include "opae_at24_eeprom.h"
+#include "ifpga_sec_mgr.h"
 
 #define PWR_THRESHOLD_MAX       0x7F
 
@@ -1152,6 +1153,12 @@ static int fme_nios_spi_init(struct ifpga_feature *feature)
        if (spi_self_checking(max10))
                goto spi_fail;
 
+       ret = init_sec_mgr(fme);
+       if (ret) {
+               dev_err(fme, "security manager init fail\n");
+               goto spi_fail;
+       }
+
        return ret;
 
 spi_fail:
@@ -1165,6 +1172,7 @@ static void fme_nios_spi_uinit(struct ifpga_feature *feature)
 {
        struct ifpga_fme_hw *fme = (struct ifpga_fme_hw *)feature->parent;
 
+       release_sec_mgr(fme);
        if (fme->max10_dev)
                intel_max10_device_remove(fme->max10_dev);
 }
diff --git a/drivers/raw/ifpga/base/ifpga_fme_rsu.c b/drivers/raw/ifpga/base/ifpga_fme_rsu.c
new file mode 100644 (file)
index 0000000..28198ab
--- /dev/null
@@ -0,0 +1,428 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include "ifpga_sec_mgr.h"
+
+static struct ifpga_sec_mgr *sec_mgr;
+
+static void set_rsu_control(struct ifpga_sec_mgr *smgr, uint32_t ctrl)
+{
+       if (smgr && smgr->rsu_control)
+               *smgr->rsu_control = ctrl;
+}
+
+static uint32_t get_rsu_control(struct ifpga_sec_mgr *smgr)
+{
+       if (smgr && smgr->rsu_control)
+               return *smgr->rsu_control;
+       return 0;
+}
+
+static void set_rsu_status(struct ifpga_sec_mgr *smgr, uint32_t status,
+       uint32_t progress)
+{
+       if (smgr && smgr->rsu_status)
+               *smgr->rsu_status = IFPGA_RSU_STATUS(status, progress);
+}
+
+static void get_rsu_status(struct ifpga_sec_mgr *smgr, uint32_t *status,
+       uint32_t *progress)
+{
+       if (smgr && smgr->rsu_status) {
+               if (status)
+                       *status = IFPGA_RSU_GET_STAT(*smgr->rsu_status);
+               if (progress)
+                       *progress = IFPGA_RSU_GET_PROG(*smgr->rsu_status);
+       }
+}
+
+static void sig_handler(int sig, siginfo_t *info, void *data)
+{
+       (void)(info);
+       (void)(data);
+
+       switch (sig) {
+       case SIGINT:
+               if (sec_mgr) {
+                       dev_info(sec_mgr, "Interrupt secure flash update"
+                               " by keyboard\n");
+                       set_rsu_control(sec_mgr, IFPGA_RSU_ABORT);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void log_time(time_t t, const char *msg)
+{
+       uint32_t h = 0;
+       uint32_t m = 0;
+       uint32_t s = 0;
+
+       if (t < 60) {
+               s = (uint32_t)t;
+       } else if (t < 3600) {
+               s = (uint32_t)(t % 60);
+               m = (uint32_t)(t / 60);
+       } else {
+               s = (uint32_t)(t % 60);
+               m = (uint32_t)((t % 3600) / 60);
+               h = (uint32_t)(t / 3600);
+       }
+       printf("%s - %02u:%02u:%02u\n", msg, h, m, s);
+}
+
+static int start_flash_update(struct ifpga_sec_mgr *smgr)
+{
+       if (!smgr)
+               return -ENODEV;
+
+       if (!smgr->ops || !smgr->ops->prepare)
+               return -EINVAL;
+
+       return smgr->ops->prepare(smgr);
+}
+
+static int write_flash_image(struct ifpga_sec_mgr *smgr, const char *image,
+       uint32_t offset)
+{
+       void *buf = NULL;
+       int retry = 0;
+       uint32_t length = 0;
+       uint32_t to_transfer = 0;
+       uint32_t one_percent = 0;
+       uint32_t prog = 0;
+       uint32_t old_prog = -1;
+       ssize_t read_size = 0;
+       int fd = -1;
+       int ret = 0;
+
+       if (!smgr)
+               return -ENODEV;
+
+       if (!smgr->ops || !smgr->ops->write_blk)
+               return -EINVAL;
+
+       fd = open(image, O_RDONLY);
+       if (fd < 0) {
+               dev_err(smgr,
+                       "Failed to open \'%s\' for RD [e:%s]\n",
+                       image, strerror(errno));
+               return -EIO;
+       }
+
+       buf = malloc(IFPGA_RSU_DATA_BLK_SIZE);
+       if (!buf) {
+               dev_err(smgr, "Failed to allocate memory for flash update\n");
+               close(fd);
+               return -ENOMEM;
+       }
+
+       length = smgr->rsu_length;
+       one_percent = length / 100;
+       do {
+               to_transfer = (length > IFPGA_RSU_DATA_BLK_SIZE) ?
+                       IFPGA_RSU_DATA_BLK_SIZE : length;
+               lseek(fd, offset, SEEK_SET);
+               read_size = read(fd, buf, to_transfer);
+               if (read_size < 0) {
+                       dev_err(smgr, "Failed to read from \'%s\' [e:%s]\n",
+                               image, strerror(errno));
+                       ret = -EIO;
+                       goto end;
+               }
+               if ((uint32_t)read_size != to_transfer) {
+                       dev_err(smgr,
+                               "Read length %zd is not expected [e:%u]\n",
+                               read_size, to_transfer);
+                       ret = -EIO;
+                       goto end;
+               }
+
+               retry = 0;
+               do {
+                       if (get_rsu_control(smgr) == IFPGA_RSU_ABORT) {
+                               ret = -EAGAIN;
+                               goto end;
+                       }
+                       ret = smgr->ops->write_blk(smgr, buf, offset,
+                               to_transfer);
+                       if (ret == 0)
+                               break;
+                       sleep(1);
+               } while (++retry <= IFPGA_RSU_WRITE_RETRY);
+               if (retry > IFPGA_RSU_WRITE_RETRY) {
+                       dev_err(smgr, "Failed to write to staging area 0x%x\n",
+                               offset);
+                       ret = -EAGAIN;
+                       goto end;
+               }
+
+               length -= to_transfer;
+               offset += to_transfer;
+               prog = offset / one_percent;
+               if (prog != old_prog) {
+                       printf("\r%d%%", prog);
+                       fflush(stdout);
+                       set_rsu_status(smgr, IFPGA_RSU_READY, prog);
+                       old_prog = prog;
+               }
+       } while (length > 0);
+       set_rsu_status(smgr, IFPGA_RSU_READY, 100);
+       printf("\n");
+
+end:
+       free(buf);
+       close(fd);
+       return ret;
+}
+
+static int apply_flash_update(struct ifpga_sec_mgr *smgr)
+{
+       uint32_t one_percent = 0;
+       uint32_t one_percent_time = 0;
+       uint32_t prog = 0;
+       uint32_t old_prog = -1;
+       uint32_t copy_time = 0;
+       int ret = 0;
+
+       if (!smgr)
+               return -ENODEV;
+
+       if (!smgr->ops || !smgr->ops->write_done || !smgr->ops->check_complete)
+               return -EINVAL;
+
+       if (smgr->ops->write_done(smgr) < 0) {
+               dev_err(smgr, "Failed to apply flash update\n");
+               return -EAGAIN;
+       }
+
+       one_percent = (smgr->rsu_length + 99) / 100;
+       if (smgr->copy_speed == 0)   /* avoid zero divide fault */
+               smgr->copy_speed = 1;
+       one_percent_time = (one_percent + smgr->copy_speed - 1) /
+               smgr->copy_speed;
+       if (one_percent_time == 0)   /* avoid zero divide fault */
+               one_percent_time = 1;
+
+       do {
+               ret = smgr->ops->check_complete(smgr);
+               if (ret != -EAGAIN)
+                       break;
+               sleep(1);
+               copy_time += 1;
+               prog = copy_time / one_percent_time;
+               if (prog >= 100)
+                       prog = 99;
+               if (prog != old_prog) {
+                       printf("\r%d%%", prog);
+                       fflush(stdout);
+                       set_rsu_status(smgr, IFPGA_RSU_COPYING, prog);
+                       old_prog = prog;
+               }
+       } while (true);
+
+       if (ret < 0) {
+               printf("\n");
+               dev_err(smgr, "Failed to complete secure flash update\n");
+       } else {
+               printf("\r100%%\n");
+               set_rsu_status(smgr, IFPGA_RSU_COPYING, 100);
+       }
+
+       return ret;
+}
+
+static int secure_update_cancel(struct ifpga_sec_mgr *smgr)
+{
+       if (!smgr)
+               return -ENODEV;
+
+       if (!smgr->ops || !smgr->ops->cancel)
+               return -EINVAL;
+
+       return smgr->ops->cancel(smgr);
+}
+
+static int secure_update_status(struct ifpga_sec_mgr *smgr, uint64_t *status)
+{
+       if (!smgr)
+               return -ENODEV;
+
+       if (!smgr->ops || !smgr->ops->get_hw_errinfo)
+               return -EINVAL;
+
+       if (status)
+               *status = smgr->ops->get_hw_errinfo(smgr);
+
+       return 0;
+}
+
+int fpga_update_flash(struct ifpga_fme_hw *fme, const char *image,
+       uint64_t *status)
+{
+       struct ifpga_hw *hw = NULL;
+       struct ifpga_sec_mgr *smgr = NULL;
+       uint32_t rsu_stat = 0;
+       int fd = -1;
+       struct sigaction old_sigint_action;
+       struct sigaction sa;
+       time_t start;
+       int ret = 0;
+
+       if (!fme || !image || !status) {
+               dev_err(fme, "Input parameter of %s is invalid\n", __func__);
+               return -EINVAL;
+       }
+
+       hw = (struct ifpga_hw *)fme->parent;
+       if (!hw) {
+               dev_err(fme, "Parent of FME not found\n");
+               return -ENODEV;
+       }
+
+       smgr = (struct ifpga_sec_mgr *)fme->sec_mgr;
+       if (!smgr || !smgr->max10_dev) {
+               dev_err(smgr, "Security manager not initialized\n");
+               return -ENODEV;
+       }
+
+       opae_adapter_lock(hw->adapter, -1);
+       get_rsu_status(smgr, &rsu_stat, NULL);
+       if (rsu_stat != IFPGA_RSU_IDLE) {
+               opae_adapter_unlock(hw->adapter);
+               if (rsu_stat == IFPGA_RSU_REBOOT)
+                       dev_info(smgr, "Reboot is in progress\n");
+               else
+                       dev_info(smgr, "Update is in progress\n");
+               return -EAGAIN;
+       }
+       set_rsu_control(smgr, 0);
+       set_rsu_status(smgr, IFPGA_RSU_PREPARE, 0);
+       opae_adapter_unlock(hw->adapter);
+
+       fd = open(image, O_RDONLY);
+       if (fd < 0) {
+               dev_err(smgr,
+                       "Failed to open \'%s\' for RD [e:%s]\n",
+                       image, strerror(errno));
+               return -EIO;
+       }
+       smgr->rsu_length = lseek(fd, 0, SEEK_END);
+       close(fd);
+
+       if (smgr->max10_dev->staging_area_size < smgr->rsu_length) {
+               dev_err(dev, "Size of staging area is small than image length "
+                       "[%u<%u]\n", smgr->max10_dev->staging_area_size,
+                       smgr->rsu_length);
+               return -EINVAL;
+       }
+
+       printf("Updating from file \'%s\' with size %u\n",
+               image, smgr->rsu_length);
+
+       sec_mgr = smgr;
+       memset(&sa, 0, sizeof(struct sigaction));
+       sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
+       sa.sa_sigaction = sig_handler;
+       ret = sigaction(SIGINT, &sa, &old_sigint_action);
+       if (ret < 0) {
+               dev_warn(dev, "Failed to register signal handler"
+                       " [e:%d]\n", ret);
+               sec_mgr = NULL;
+       }
+
+       start = time(NULL);
+       log_time(time(NULL) - start, "Starting secure flash update");
+       ret = start_flash_update(smgr);
+       if (ret < 0)
+               goto end;
+
+       set_rsu_status(smgr, IFPGA_RSU_READY, 0);
+       log_time(time(NULL) - start, "Writing to staging area");
+       ret = write_flash_image(smgr, image, 0);
+       if (ret < 0)
+               goto end;
+
+       set_rsu_status(smgr, IFPGA_RSU_COPYING, 0);
+       log_time(time(NULL) - start, "Applying secure flash update");
+       ret = apply_flash_update(smgr);
+
+end:
+       if (sec_mgr) {
+               sec_mgr = NULL;
+               if (sigaction(SIGINT, &old_sigint_action, NULL) < 0)
+                       dev_err(smgr, "Failed to unregister signal handler\n");
+       }
+
+       secure_update_status(smgr, status);
+       if (ret < 0) {
+               log_time(time(NULL) - start, "Secure flash update ERROR");
+               if (ret == -EAGAIN)
+                       secure_update_cancel(smgr);
+       } else {
+               log_time(time(NULL) - start, "Secure flash update OK");
+       }
+       set_rsu_status(smgr, IFPGA_RSU_IDLE, 0);
+
+       return ret;
+}
+
+int fpga_stop_flash_update(struct ifpga_fme_hw *fme, int force)
+{
+       struct ifpga_sec_mgr *smgr = NULL;
+       uint32_t status = 0;
+       int retry = IFPGA_RSU_CANCEL_RETRY;
+       int ret = 0;
+
+       if (!fme) {
+               dev_err(fme, "Input parameter of %s is invalid\n", __func__);
+               return -EINVAL;
+       }
+       smgr = (struct ifpga_sec_mgr *)fme->sec_mgr;
+
+       get_rsu_status(smgr, &status, NULL);
+       if (status != IFPGA_RSU_IDLE) {
+               dev_info(smgr, "Cancel secure flash update\n");
+               set_rsu_control(smgr, IFPGA_RSU_ABORT);
+       }
+
+       if (force) {
+               sleep(2);
+               do {
+                       get_rsu_status(smgr, &status, NULL);
+                       if (status == IFPGA_RSU_IDLE)
+                               break;
+                       if (secure_update_cancel(smgr) == 0)
+                               set_rsu_status(smgr, IFPGA_RSU_IDLE, 0);
+                       sleep(1);
+               } while (--retry > 0);
+               if (retry <= 0) {
+                       dev_err(smgr, "Failed to stop flash update\n");
+                       ret = -EAGAIN;
+               }
+       }
+
+       return ret;
+}
+
+int fpga_reload(struct ifpga_fme_hw *fme, int type, int page)
+{
+       struct ifpga_sec_mgr *smgr = NULL;
+
+       if (!fme) {
+               dev_err(fme, "Input parameter of %s is invalid\n", __func__);
+               return -EINVAL;
+       }
+       smgr = (struct ifpga_sec_mgr *)fme->sec_mgr;
+
+       if (!smgr || !smgr->ops || !smgr->ops->reload)
+               return -EINVAL;
+
+       return smgr->ops->reload(smgr, type, page);
+}
index 7c3307f..ed5edc6 100644 (file)
@@ -91,6 +91,7 @@ struct ifpga_fme_hw {
        struct opae_board_info board_info;
        int nums_eth_dev;
        unsigned int nums_acc_region;
+       void *sec_mgr;
 };
 
 enum ifpga_port_state {
diff --git a/drivers/raw/ifpga/base/ifpga_sec_mgr.c b/drivers/raw/ifpga/base/ifpga_sec_mgr.c
new file mode 100644 (file)
index 0000000..4cf1db3
--- /dev/null
@@ -0,0 +1,639 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include "ifpga_sec_mgr.h"
+
+
+static const char * const rsu_prog[] = {"IDLE", "PREPARING", "SLEEPING",
+       "READY", "AUTHENTICATING", "COPYING", "CANCELLATION", "PROGRAMMING_KEY",
+       "DONE", "PKVL_DONE"};
+static const char * const rsu_statl[] = {"NORMAL", "TIMEOUT", "AUTH_FAIL",
+       "COPY_FAIL", "FATAL", "PKVL_REJECT", "NON_INCR", "ERASE_FAIL",
+       "WEAROUT"};
+static const char * const rsu_stath[] = {"NIOS_OK", "USER_OK", "FACTORY_OK",
+       "USER_FAIL", "FACTORY_FAIL", "NIOS_FLASH_ERR", "FPGA_FLASH_ERR"};
+
+static const char *rsu_progress_name(uint32_t prog)
+{
+       if (prog > SEC_PROGRESS_PKVL_PROM_DONE)
+               return "UNKNOWN";
+       else
+               return rsu_prog[prog];
+}
+
+static const char *rsu_status_name(uint32_t stat)
+{
+       if (stat >= SEC_STATUS_NIOS_OK) {
+               if (stat > SEC_STATUS_FPGA_FLASH_ERR)
+                       return "UNKNOWN";
+               else
+                       return rsu_stath[stat-SEC_STATUS_NIOS_OK];
+       } else {
+               if (stat > SEC_STATUS_WEAROUT)
+                       return "UNKNOWN";
+               else
+                       return rsu_statl[stat];
+       }
+}
+
+static bool secure_start_done(uint32_t doorbell)
+{
+       return (SEC_STATUS_G(doorbell) == SEC_STATUS_ERASE_FAIL ||
+               SEC_STATUS_G(doorbell) == SEC_STATUS_WEAROUT ||
+               (SEC_PROGRESS_G(doorbell) != SEC_PROGRESS_IDLE &&
+               SEC_PROGRESS_G(doorbell) != SEC_PROGRESS_RSU_DONE));
+}
+
+static bool secure_prog_ready(uint32_t doorbell)
+{
+       return (SEC_PROGRESS_G(doorbell) != SEC_PROGRESS_READY);
+}
+
+static int poll_timeout(struct intel_max10_device *dev, uint32_t offset,
+       bool (*cond)(uint32_t), uint32_t interval_ms, uint32_t timeout_ms)
+{
+       uint32_t val = 0;
+       int ret = 0;
+
+       for (;;) {
+               ret = max10_sys_read(dev, offset, &val);
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to read max10 register 0x%x [e:%d]\n",
+                               offset, ret);
+                       break;
+               }
+
+               if (cond(val)) {
+                       dev_debug(dev,
+                               "Read 0x%08x from max10 register 0x%x "
+                               "[poll success]\n", val, offset);
+                       ret = 0;
+                       break;
+               }
+               if (timeout_ms > interval_ms)
+                       timeout_ms -= interval_ms;
+               else
+                       timeout_ms = 0;
+               if (timeout_ms == 0) {
+                       dev_debug(dev,
+                               "Read 0x%08x from max10 register 0x%x "
+                               "[poll timeout]\n", val, offset);
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+               msleep(interval_ms);
+       }
+
+       return ret;
+}
+
+static int n3000_secure_update_start(struct intel_max10_device *dev)
+{
+       uint32_t doorbell = 0;
+       uint32_t prog = 0;
+       uint32_t status = 0;
+       int ret = 0;
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       prog = SEC_PROGRESS_G(doorbell);
+       if ((prog != SEC_PROGRESS_IDLE) && (prog != SEC_PROGRESS_RSU_DONE)) {
+               dev_debug(dev, "Current RSU progress is %s\n",
+                       rsu_progress_name(prog));
+               return -EBUSY;
+       }
+
+       ret = max10_sys_update_bits(dev, MAX10_DOORBELL,
+               RSU_REQUEST | HOST_STATUS, RSU_REQUEST);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to updt max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       ret = poll_timeout(dev, MAX10_DOORBELL, secure_start_done,
+               IFPGA_SEC_START_INTERVAL_MS, IFPGA_SEC_START_TIMEOUT_MS);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to poll max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       status = SEC_STATUS_G(doorbell);
+       if (status == SEC_STATUS_WEAROUT)
+               return -EAGAIN;
+
+       if (status == SEC_STATUS_ERASE_FAIL)
+               return -EIO;
+
+       return 0;
+}
+
+static int n3000_cancel(struct ifpga_sec_mgr *smgr)
+{
+       struct intel_max10_device *dev = NULL;
+       uint32_t doorbell = 0;
+       uint32_t prog = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+       dev = (struct intel_max10_device *)smgr->max10_dev;
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       prog = SEC_PROGRESS_G(doorbell);
+       if (prog == SEC_PROGRESS_IDLE)
+               return 0;
+       if (prog != SEC_PROGRESS_READY)
+               return -EBUSY;
+
+       return max10_sys_update_bits(dev, MAX10_DOORBELL, HOST_STATUS,
+               HOST_STATUS_S(HOST_STATUS_ABORT_RSU));
+}
+
+static int n3000_prepare(struct ifpga_sec_mgr *smgr)
+{
+       struct intel_max10_device *dev = NULL;
+       int retry = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+       dev = (struct intel_max10_device *)smgr->max10_dev;
+
+       ret = n3000_secure_update_start(dev);
+       if (ret == -EBUSY)
+               n3000_cancel(smgr);
+
+       while (ret) {
+               if (++retry > IFPGA_RSU_START_RETRY)
+                       break;
+               msleep(1000);
+               ret = n3000_secure_update_start(dev);
+       }
+       if (retry > IFPGA_RSU_START_RETRY) {
+               dev_err(dev, "Failed to start secure flash update\n");
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
+static int n3000_bulk_write(struct intel_max10_device *dev, uint32_t addr,
+       char *buf, uint32_t len)
+{
+       uint32_t i = 0;
+       uint32_t n = 0;
+       uint32_t v = 0;
+       uint32_t p = 0;
+       int ret = 0;
+
+       if (len & 0x3) {
+               dev_err(dev,
+                       "Length of data block is not 4 bytes aligned [e:%u]\n",
+                       len);
+               return -EINVAL;
+       }
+
+       n = len >> 2;
+       for (i = 0; i < n; i++) {
+               p = i << 2;
+               v = *(uint32_t *)(buf + p);
+               ret = max10_reg_write(dev, addr + p, v);
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to write to staging area 0x%08x [e:%d]\n",
+                               addr + p, ret);
+                       return ret;
+               }
+               usleep(1);
+       }
+
+       return 0;
+}
+
+static int n3000_write_blk(struct ifpga_sec_mgr *smgr, char *buf,
+       uint32_t offset, uint32_t len)
+{
+       struct intel_max10_device *dev = NULL;
+       uint32_t doorbell = 0;
+       uint32_t prog = 0;
+       uint32_t m = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+       dev = (struct intel_max10_device *)smgr->max10_dev;
+
+       if (offset + len > dev->staging_area_size) {
+               dev_err(dev,
+                       "Write position would be out of staging area [e:%u]\n",
+                       dev->staging_area_size);
+               return -ENOMEM;
+       }
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       prog = SEC_PROGRESS_G(doorbell);
+       if (prog == SEC_PROGRESS_PREPARE)
+               return -EAGAIN;
+       else if (prog != SEC_PROGRESS_READY)
+               return -EBUSY;
+
+       m = len & 0x3;
+       if (m != 0)
+               len += 4 - m;   /* make length to 4 bytes align */
+
+       return n3000_bulk_write(dev, dev->staging_area_base + offset, buf, len);
+}
+
+static int n3000_write_done(struct ifpga_sec_mgr *smgr)
+{
+       struct intel_max10_device *dev = NULL;
+       uint32_t doorbell = 0;
+       uint32_t prog = 0;
+       uint32_t status = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+       dev = (struct intel_max10_device *)smgr->max10_dev;
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       prog = SEC_PROGRESS_G(doorbell);
+       if (prog != SEC_PROGRESS_READY)
+               return -EBUSY;
+
+       ret = max10_sys_update_bits(dev, MAX10_DOORBELL, HOST_STATUS,
+               HOST_STATUS_S(HOST_STATUS_WRITE_DONE));
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to update max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       ret = poll_timeout(dev, MAX10_DOORBELL, secure_prog_ready,
+               IFPGA_NIOS_HANDSHAKE_INTERVAL_MS,
+               IFPGA_NIOS_HANDSHAKE_TIMEOUT_MS);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to poll max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       status = SEC_STATUS_G(doorbell);
+       switch (status) {
+       case SEC_STATUS_NORMAL:
+       case SEC_STATUS_NIOS_OK:
+       case SEC_STATUS_USER_OK:
+       case SEC_STATUS_FACTORY_OK:
+               ret = 0;
+               break;
+       default:
+               ret = -EIO;
+               break;
+       }
+
+       return ret;
+}
+
+static int n3000_check_complete(struct ifpga_sec_mgr *smgr)
+{
+       struct intel_max10_device *dev = NULL;
+       uint32_t doorbell = 0;
+       uint32_t status = 0;
+       uint32_t prog = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+       dev = (struct intel_max10_device *)smgr->max10_dev;
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return ret;
+       }
+
+       status = SEC_STATUS_G(doorbell);
+       switch (status) {
+       case SEC_STATUS_NORMAL:
+       case SEC_STATUS_NIOS_OK:
+       case SEC_STATUS_USER_OK:
+       case SEC_STATUS_FACTORY_OK:
+       case SEC_STATUS_WEAROUT:
+               break;
+       default:
+               return -EIO;
+       }
+
+       prog = SEC_PROGRESS_G(doorbell);
+       switch (prog) {
+       case SEC_PROGRESS_IDLE:
+       case SEC_PROGRESS_RSU_DONE:
+               return 0;
+       case SEC_PROGRESS_AUTHENTICATING:
+       case SEC_PROGRESS_COPYING:
+       case SEC_PROGRESS_UPDATE_CANCEL:
+       case SEC_PROGRESS_PROGRAM_KEY_HASH:
+               return -EAGAIN;
+       case SEC_PROGRESS_PREPARE:
+       case SEC_PROGRESS_READY:
+               return -EBUSY;
+       default:
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int n3000_reload_fpga(struct intel_max10_device *dev, int page)
+{
+       int ret = 0;
+
+       dev_info(dev, "Reload FPGA\n");
+
+       if (!dev || ((page != 0) && (page != 1))) {
+               dev_err(dev, "Input parameter of %s is invalid\n", __func__);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       if (dev->flags & MAX10_FLAGS_SECURE) {
+               ret = max10_sys_update_bits(dev, FPGA_RECONF_REG,
+                       SFPGA_RP_LOAD, 0);
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to update max10 reconfig register [e:%d]\n",
+                               ret);
+                       goto end;
+               }
+               ret = max10_sys_update_bits(dev, FPGA_RECONF_REG,
+                       SFPGA_RP_LOAD | SFPGA_RECONF_PAGE,
+                       SFPGA_RP_LOAD | SFPGA_PAGE(page));
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to update max10 reconfig register [e:%d]\n",
+                               ret);
+                       goto end;
+               }
+       } else {
+               ret = max10_sys_update_bits(dev, RSU_REG, FPGA_RP_LOAD, 0);
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to update max10 rsu register [e:%d]\n",
+                               ret);
+                       goto end;
+               }
+               ret = max10_sys_update_bits(dev, RSU_REG,
+                       FPGA_RP_LOAD | FPGA_RECONF_PAGE,
+                       FPGA_RP_LOAD | FPGA_PAGE(page));
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to update max10 rsu register [e:%d]\n",
+                               ret);
+                       goto end;
+               }
+       }
+
+       ret = max10_sys_update_bits(dev, FPGA_RECONF_REG, COUNTDOWN_START, 0);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to update max10 reconfig register [e:%d]\n",
+                       ret);
+               goto end;
+       }
+
+       ret = max10_sys_update_bits(dev, FPGA_RECONF_REG, COUNTDOWN_START,
+               COUNTDOWN_START);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to update max10 reconfig register [e:%d]\n",
+                       ret);
+       }
+end:
+       if (ret < 0)
+               dev_err(dev, "Failed to reload FPGA\n");
+
+       return ret;
+}
+
+static int n3000_reload_bmc(struct intel_max10_device *dev, int page)
+{
+       uint32_t val = 0;
+       int ret = 0;
+
+       dev_info(dev, "Reload BMC\n");
+
+       if (!dev || ((page != 0) && (page != 1))) {
+               dev_err(dev, "Input parameter of %s is invalid\n", __func__);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       if (dev->flags & MAX10_FLAGS_SECURE) {
+               ret = max10_sys_update_bits(dev, MAX10_DOORBELL,
+                       CONFIG_SEL | REBOOT_REQ,
+                       CONFIG_SEL_S(page) | REBOOT_REQ);
+       } else {
+               val = (page == 0) ? 0x1 : 0x3;
+               ret = max10_reg_write(dev, IFPGA_DUAL_CFG_CTRL1, val);
+               if (ret < 0) {
+                       dev_err(dev,
+                               "Failed to write to dual config1 register [e:%d]\n",
+                               ret);
+                       goto end;
+               }
+
+               ret = max10_reg_write(dev, IFPGA_DUAL_CFG_CTRL0, 0x1);
+               if (ret < 0) {
+                       if (ret == -EIO) {
+                               ret = 0;
+                               goto end;
+                       }
+                       dev_err(dev,
+                               "Failed to write to dual config0 register [e:%d]\n",
+                               ret);
+               }
+       }
+
+end:
+       if (ret < 0)
+               dev_err(dev, "Failed to reload BMC\n");
+
+       return ret;
+}
+
+static int n3000_reload(struct ifpga_sec_mgr *smgr, int type, int page)
+{
+       int psel = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+
+       if (type == IFPGA_BOOT_TYPE_FPGA) {
+               psel = (page == IFPGA_BOOT_PAGE_FACTORY ? 0 : 1);
+               ret = n3000_reload_fpga(smgr->max10_dev, psel);
+       } else if (type == IFPGA_BOOT_TYPE_BMC) {
+               psel = (page == IFPGA_BOOT_PAGE_FACTORY ? 1 : 0);
+               ret = n3000_reload_bmc(smgr->max10_dev, psel);
+       } else {
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static uint64_t n3000_get_hw_errinfo(struct ifpga_sec_mgr *smgr)
+{
+       struct intel_max10_device *dev = NULL;
+       uint32_t doorbell = 0;
+       uint32_t stat = 0;
+       uint32_t prog = 0;
+       uint32_t auth_result = 0;
+       int ret = 0;
+
+       if (!smgr || !smgr->max10_dev)
+               return -ENODEV;
+       dev = (struct intel_max10_device *)smgr->max10_dev;
+
+       ret = max10_sys_read(dev, MAX10_DOORBELL, &doorbell);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read max10 doorbell register [e:%d]\n",
+                       ret);
+               return -1;
+       }
+       stat = SEC_STATUS_G(doorbell);
+       prog = SEC_PROGRESS_G(doorbell);
+       dev_debug(dev, "Current RSU status is %s, progress is %s\n",
+               rsu_status_name(stat), rsu_progress_name(prog));
+
+       ret = max10_sys_read(dev, MAX10_AUTH_RESULT, &auth_result);
+       if (ret < 0) {
+               dev_err(dev,
+                       "Failed to read authenticate result register [e:%d]\n",
+                       ret);
+               return -1;
+       }
+
+       return (uint64_t)doorbell << 32 | (uint64_t)auth_result;
+}
+
+static const struct ifpga_sec_ops n3000_sec_ops = {
+       .prepare = n3000_prepare,
+       .write_blk = n3000_write_blk,
+       .write_done = n3000_write_done,
+       .check_complete = n3000_check_complete,
+       .reload = n3000_reload,
+       .cancel = n3000_cancel,
+       .cleanup = NULL,
+       .get_hw_errinfo = n3000_get_hw_errinfo,
+};
+
+int init_sec_mgr(struct ifpga_fme_hw *fme)
+{
+       struct ifpga_hw *hw = NULL;
+       opae_share_data *sd = NULL;
+       struct ifpga_sec_mgr *smgr = NULL;
+
+       if (!fme || !fme->max10_dev)
+               return -ENODEV;
+
+       smgr = (struct ifpga_sec_mgr *)malloc(sizeof(*smgr));
+       if (!smgr) {
+               dev_err(NULL, "Failed to allocate memory for security manager\n");
+               return -ENOMEM;
+       }
+       fme->sec_mgr = smgr;
+
+       hw = (struct ifpga_hw *)fme->parent;
+       if (hw && hw->adapter && hw->adapter->shm.ptr) {
+               sd = (opae_share_data *)hw->adapter->shm.ptr;
+               smgr->rsu_control = &sd->rsu_ctrl;
+               smgr->rsu_status = &sd->rsu_stat;
+       } else {
+               smgr->rsu_control = NULL;
+               smgr->rsu_status = NULL;
+       }
+
+       if ((hw->pci_data->device_id == IFPGA_N3000_DID) &&
+               (hw->pci_data->vendor_id == IFPGA_N3000_VID)) {
+               smgr->ops = &n3000_sec_ops;
+               smgr->copy_speed = IFPGA_N3000_COPY_SPEED;
+       } else {
+               dev_err(NULL, "No operation for security manager\n");
+               smgr->ops = NULL;
+       }
+
+       smgr->fme = fme;
+       smgr->max10_dev = fme->max10_dev;
+
+       return 0;
+}
+
+void release_sec_mgr(struct ifpga_fme_hw *fme)
+{
+       struct ifpga_sec_mgr *smgr = NULL;
+
+       if (fme) {
+               smgr = (struct ifpga_sec_mgr *)fme->sec_mgr;
+               if (smgr) {
+                       fme->sec_mgr = NULL;
+                       free(smgr);
+               }
+       }
+}
diff --git a/drivers/raw/ifpga/base/ifpga_sec_mgr.h b/drivers/raw/ifpga/base/ifpga_sec_mgr.h
new file mode 100644 (file)
index 0000000..fbeba56
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IFPGA_FME_RSU_H_
+#define _IFPGA_FME_RSU_H_
+
+
+#include "ifpga_hw.h"
+
+#define IFPGA_N3000_VID     0x8086
+#define IFPGA_N3000_DID     0x0b30
+
+#define IFPGA_BOOT_TYPE_FPGA     0
+#define IFPGA_BOOT_TYPE_BMC      1
+
+#define IFPGA_BOOT_PAGE_FACTORY  0
+#define IFPGA_BOOT_PAGE_USER     1
+
+#define IFPGA_RSU_DATA_BLK_SIZE  32768
+#define IFPGA_RSU_START_RETRY    120
+#define IFPGA_RSU_WRITE_RETRY    10
+#define IFPGA_RSU_CANCEL_RETRY   30
+
+#define IFPGA_N3000_COPY_SPEED   42700
+
+/* status */
+#define IFPGA_RSU_IDLE       0
+#define IFPGA_RSU_PREPARE    1
+#define IFPGA_RSU_READY      2
+#define IFPGA_RSU_COPYING    3
+#define IFPGA_RSU_REBOOT     4
+
+#define IFPGA_RSU_GET_STAT(v)  (((v) >> 16) & 0xffff)
+#define IFPGA_RSU_GET_PROG(v)  ((v) & 0xffff)
+#define IFPGA_RSU_STATUS(s, p) ((((s) << 16) & 0xffff0000) | ((p) & 0xffff))
+
+/* control */
+#define IFPGA_RSU_ABORT      1
+
+#define IFPGA_DUAL_CFG_CTRL0     0x200020
+#define IFPGA_DUAL_CFG_CTRL1     0x200024
+
+#define IFPGA_SEC_START_INTERVAL_MS       100
+#define IFPGA_SEC_START_TIMEOUT_MS        20000
+#define IFPGA_NIOS_HANDSHAKE_INTERVAL_MS  100
+#define IFPGA_NIOS_HANDSHAKE_TIMEOUT_MS   5000
+
+#define IFPGA_RSU_ERR_HW_ERROR         -1
+#define IFPGA_RSU_ERR_TIMEOUT          -2
+#define IFPGA_RSU_ERR_CANCELED         -3
+#define IFPGA_RSU_ERR_BUSY                     -4
+#define IFPGA_RSU_ERR_INVALID_SIZE     -5
+#define IFPGA_RSU_ERR_RW_ERROR         -6
+#define IFPGA_RSU_ERR_WEAROUT          -7
+#define IFPGA_RSU_ERR_FILE_READ                -8
+
+struct ifpga_sec_mgr;
+
+struct ifpga_sec_ops {
+       int (*prepare)(struct ifpga_sec_mgr *smgr);
+       int (*write_blk)(struct ifpga_sec_mgr *smgr, char *buf, uint32_t offset,
+               uint32_t size);
+       int (*write_done)(struct ifpga_sec_mgr *smgr);
+       int (*check_complete)(struct ifpga_sec_mgr *smgr);
+       int (*reload)(struct ifpga_sec_mgr *smgr, int type, int page);
+       int (*cancel)(struct ifpga_sec_mgr *smgr);
+       void (*cleanup)(struct ifpga_sec_mgr *smgr);
+       u64 (*get_hw_errinfo)(struct ifpga_sec_mgr *smgr);
+};
+
+struct ifpga_sec_mgr {
+       struct ifpga_fme_hw *fme;
+       struct intel_max10_device *max10_dev;
+       unsigned int rsu_length;
+       /* number of bytes that copied from staging area to working area
+        * in one second, which is calculated by experiment
+        */
+       unsigned int copy_speed;
+       unsigned int *rsu_control;
+       unsigned int *rsu_status;
+       const struct ifpga_sec_ops *ops;
+};
+
+int init_sec_mgr(struct ifpga_fme_hw *fme);
+void release_sec_mgr(struct ifpga_fme_hw *fme);
+int fpga_update_flash(struct ifpga_fme_hw *fme, const char *image,
+       uint64_t *status);
+int fpga_stop_flash_update(struct ifpga_fme_hw *fme, int force);
+int fpga_reload(struct ifpga_fme_hw *fme, int type, int page);
+
+
+#endif /* _IFPGA_FME_RSU_H_ */
index da2d6e3..3549afa 100644 (file)
@@ -12,6 +12,8 @@ sources = [
        'ifpga_port.c',
        'ifpga_port_error.c',
        'ifpga_fme_pr.c',
+       'ifpga_fme_rsu.c',
+       'ifpga_sec_mgr.c',
        'opae_hw_api.c',
        'opae_ifpga_hw_api.c',
        'opae_debug.c',
index d5cd5fe..86ad88f 100644 (file)
@@ -470,6 +470,8 @@ static void opae_adapter_shm_init(struct opae_adapter *adapter)
        opae_mutex_init(&sd->i2c_mutex);
        sd->ref_cnt = 0;
        sd->dtb_size = SHM_BLK_SIZE;
+       sd->rsu_ctrl = 0;
+       sd->rsu_stat = 0;
 }
 
 static void *opae_adapter_shm_alloc(struct opae_adapter *adapter)
@@ -964,3 +966,60 @@ opae_mgr_get_board_info(struct opae_manager *mgr,
 
        return -ENOENT;
 }
+
+/**
+ * opae_mgr_update_flash -  update image in flash.
+ * @mgr: targeted manager
+ * @image: name of image file
+ * @status: status of update
+ *
+ * Return: 0 on success, otherwise error code.
+ */
+int opae_mgr_update_flash(struct opae_manager *mgr, const char *image,
+       uint64_t *status)
+{
+       if (!mgr)
+               return -EINVAL;
+
+       if (mgr->ops && mgr->ops->update_flash)
+               return mgr->ops->update_flash(mgr, image, status);
+
+       return -ENOENT;
+}
+
+/**
+ * opae_stop_flash_update -  stop flash update.
+ * @mgr: targeted manager
+ * @force: make sure the update process is stopped
+ *
+ * Return: 0 on success, otherwise error code.
+ */
+int opae_mgr_stop_flash_update(struct opae_manager *mgr, int force)
+{
+       if (!mgr)
+               return -EINVAL;
+
+       if (mgr->ops && mgr->ops->stop_flash_update)
+               return mgr->ops->stop_flash_update(mgr, force);
+
+       return -ENOENT;
+}
+
+/**
+ * opae_mgr_reload -  reload FPGA.
+ * @mgr: targeted manager
+ * @type: FPGA type
+ * @page: reload from which page
+ *
+ * Return: 0 on success, otherwise error code.
+ */
+int opae_mgr_reload(struct opae_manager *mgr, int type, int page)
+{
+       if (!mgr)
+               return -EINVAL;
+
+       if (mgr->ops && mgr->ops->reload)
+               return mgr->ops->reload(mgr, type, page);
+
+       return -ENOENT;
+}
index e99ee45..91d26d9 100644 (file)
@@ -55,6 +55,10 @@ struct opae_manager_ops {
                        unsigned int *value);
        int (*get_board_info)(struct opae_manager *mgr,
                        struct opae_board_info **info);
+       int (*update_flash)(struct opae_manager *mgr, const char *image,
+                       u64 *status);
+       int (*stop_flash_update)(struct opae_manager *mgr, int force);
+       int (*reload)(struct opae_manager *mgr, int type, int page);
 };
 
 /* networking management ops in FME */
@@ -276,6 +280,8 @@ typedef struct {
                        pthread_mutex_t i2c_mutex;
                        u32 ref_cnt;    /* reference count of shared memory */
                        u32 dtb_size;   /* actual length of DTB data in byte */
+                       u32 rsu_ctrl;   /* used to control rsu */
+                       u32 rsu_stat;   /* used to report status for rsu */
                };
        };
        u8 dtb[SHM_BLK_SIZE];   /* DTB data */
@@ -354,4 +360,8 @@ int opae_manager_eth_group_read_reg(struct opae_manager *mgr, u8 group_id,
                u8 type, u8 index, u16 addr, u32 *data);
 int opae_mgr_get_board_info(struct opae_manager *mgr,
                struct opae_board_info **info);
+int opae_mgr_update_flash(struct opae_manager *mgr, const char *image,
+               uint64_t *status);
+int opae_mgr_stop_flash_update(struct opae_manager *mgr, int force);
+int opae_mgr_reload(struct opae_manager *mgr, int type, int page);
 #endif /* _OPAE_HW_API_H_*/
index 1a526ea..443e248 100644 (file)
@@ -51,6 +51,22 @@ int max10_sys_write(struct intel_max10_device *dev,
        return max10_reg_write(dev, dev->base + offset, val);
 }
 
+int max10_sys_update_bits(struct intel_max10_device *dev, unsigned int offset,
+                                       unsigned int msk, unsigned int val)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+
+       ret = max10_sys_read(dev, offset, &temp);
+       if (ret < 0)
+               return ret;
+
+       temp &= ~msk;
+       temp |= val & msk;
+
+       return max10_sys_write(dev, offset, temp);
+}
+
 static struct max10_compatible_id max10_id_table[] = {
        {.compatible = MAX10_PAC,},
        {.compatible = MAX10_PAC_N3000,},
@@ -557,6 +573,36 @@ static int check_max10_version(struct intel_max10_device *dev)
        return -ENODEV;
 }
 
+static int max10_staging_area_init(struct intel_max10_device *dev)
+{
+       char *fdt_root = dev->fdt_root;
+       int ret, offset = 0;
+       u64 start, size;
+
+       if (!fdt_root) {
+               dev_debug(dev,
+                       "skip staging area init as not find Device Tree\n");
+               return -ENODEV;
+       }
+
+       dev->staging_area_size = 0;
+
+       fdt_for_each_subnode(offset, fdt_root, 0) {
+               if (fdt_node_check_compatible(fdt_root, offset,
+                                             "ifpga-sec-mgr,staging-area"))
+                       continue;
+
+               ret = fdt_get_reg(fdt_root, offset, 0, &start, &size);
+               if (!ret) {
+                       dev->staging_area_base = start;
+                       dev->staging_area_size = size;
+               }
+               return ret;
+       }
+
+       return -ENODEV;
+}
+
 static int
 max10_secure_hw_init(struct intel_max10_device *dev)
 {
@@ -581,6 +627,8 @@ max10_secure_hw_init(struct intel_max10_device *dev)
 
        max10_sensor_init(dev, sysmgr_offset);
 
+       max10_staging_area_init(dev);
+
        return 0;
 }
 
index 123cdc4..670683f 100644 (file)
@@ -38,6 +38,8 @@ struct intel_max10_device {
        unsigned int base; /* max10 base address */
        u16 bus;
        struct opae_sensor_list opae_sensor_list;
+       u32 staging_area_base;
+       u32 staging_area_size;
 };
 
 /* retimer speed */
@@ -98,6 +100,7 @@ struct opae_retimer_status {
 #define   MAX10_MAC_COUNT      GENMASK(23, 16)
 #define RSU_REG                        0x2c
 #define   FPGA_RECONF_PAGE     GENMASK(2, 0)
+#define   FPGA_PAGE(p)         ((p) & 0x1)
 #define   FPGA_RP_LOAD         BIT(3)
 #define   NIOS2_PRERESET       BIT(4)
 #define   NIOS2_HANG           BIT(5)
@@ -106,6 +109,9 @@ struct opae_retimer_status {
 #define   NIOS2_I2C2_POLL_STOP BIT(13)
 #define   PKVL_EEPROM_LOAD     BIT(31)
 #define FPGA_RECONF_REG                0x30
+#define   SFPGA_RECONF_PAGE    GENMASK(22, 20)
+#define   SFPGA_PAGE(p)                (((p) & 0x1) << 20)
+#define   SFPGA_RP_LOAD                BIT(23)
 #define MAX10_TEST_REG         0x3c
 #define   COUNTDOWN_START      BIT(18)
 #define MAX10_BUILD_VER                0x68
@@ -118,8 +124,44 @@ struct opae_retimer_status {
 #define MAX10_DOORBELL         0x400
 #define   RSU_REQUEST          BIT(0)
 #define   SEC_PROGRESS         GENMASK(7, 4)
+#define   SEC_PROGRESS_G(v)    (((v) >> 4) & 0xf)
+#define   SEC_PROGRESS_IDLE                            0x0
+#define   SEC_PROGRESS_PREPARE                 0x1
+#define   SEC_PROGRESS_SLEEP                   0x2
+#define   SEC_PROGRESS_READY                   0x3
+#define   SEC_PROGRESS_AUTHENTICATING  0x4
+#define   SEC_PROGRESS_COPYING                 0x5
+#define   SEC_PROGRESS_UPDATE_CANCEL   0x6
+#define   SEC_PROGRESS_PROGRAM_KEY_HASH        0x7
+#define   SEC_PROGRESS_RSU_DONE                        0x8
+#define   SEC_PROGRESS_PKVL_PROM_DONE  0x9
 #define   HOST_STATUS          GENMASK(11, 8)
+#define   HOST_STATUS_S(v)     (((v) << 8) & 0xf00)
+#define   HOST_STATUS_IDLE                     0x0
+#define   HOST_STATUS_WRITE_DONE       0x1
+#define   HOST_STATUS_ABORT_RSU                0x2
 #define   SEC_STATUS           GENMASK(23, 16)
+#define   SEC_STATUS_G(v)      (((v) >> 16) & 0xff)
+#define   SEC_STATUS_NORMAL                    0x0
+#define   SEC_STATUS_TIMEOUT           0x1
+#define   SEC_STATUS_AUTH_FAIL         0x2
+#define   SEC_STATUS_COPY_FAIL         0x3
+#define   SEC_STATUS_FATAL                     0x4
+#define   SEC_STATUS_PKVL_REJECT       0x5
+#define   SEC_STATUS_NON_INC           0x6
+#define   SEC_STATUS_ERASE_FAIL                0x7
+#define   SEC_STATUS_WEAROUT           0x8
+#define   SEC_STATUS_NIOS_OK           0x80
+#define   SEC_STATUS_USER_OK           0x81
+#define   SEC_STATUS_FACTORY_OK                0x82
+#define   SEC_STATUS_USER_FAIL         0x83
+#define   SEC_STATUS_FACTORY_FAIL      0x84
+#define   SEC_STATUS_NIOS_FLASH_ERR    0x85
+#define   SEC_STATUS_FPGA_FLASH_ERR    0x86
+#define   CONFIG_SEL           BIT(28)
+#define   CONFIG_SEL_S(v)      (((v) & 0x1) << 28)
+#define   REBOOT_REQ           BIT(29)
+#define MAX10_AUTH_RESULT      0x404
 
 /* PKVL related registers, in system register region */
 #define PKVL_POLLING_CTRL              0x80
@@ -149,6 +191,8 @@ int max10_sys_read(struct intel_max10_device *dev,
        unsigned int offset, unsigned int *val);
 int max10_sys_write(struct intel_max10_device *dev,
        unsigned int offset, unsigned int val);
+int max10_sys_update_bits(struct intel_max10_device *dev,
+       unsigned int offset, unsigned int msk, unsigned int val);
 struct intel_max10_device *
 intel_max10_device_probe(struct altera_spi_device *spi,
                int chipselect);
index 027ff80..60ea59a 100644 (file)
@@ -13,8 +13,10 @@ objs = [base_objs]
 deps += ['ethdev', 'rawdev', 'pci', 'bus_pci', 'kvargs',
        'bus_vdev', 'bus_ifpga', 'net', 'net_i40e', 'net_ipn3ke']
 
-sources = files('ifpga_rawdev.c')
+sources = files('ifpga_rawdev.c', 'rte_pmd_ifpga.c')
 
 includes += include_directories('base')
 includes += include_directories('../../net/ipn3ke')
 includes += include_directories('../../net/i40e')
+
+headers = files('rte_pmd_ifpga.h')
diff --git a/drivers/raw/ifpga/rte_pmd_ifpga.c b/drivers/raw/ifpga/rte_pmd_ifpga.c
new file mode 100644 (file)
index 0000000..af6f175
--- /dev/null
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <rte_pci.h>
+#include <rte_bus_pci.h>
+#include <rte_rawdev.h>
+#include <rte_rawdev_pmd.h>
+#include "rte_pmd_ifpga.h"
+#include "ifpga_rawdev.h"
+#include "base/ifpga_sec_mgr.h"
+
+
+int
+rte_pmd_ifpga_get_dev_id(const char *pci_addr, uint16_t *dev_id)
+{
+       struct rte_pci_addr addr;
+       struct rte_rawdev *rdev = NULL;
+       char rdev_name[RTE_RAWDEV_NAME_MAX_LEN] = {0};
+
+       if (!pci_addr || !dev_id) {
+               IFPGA_RAWDEV_PMD_ERR("Input parameter is invalid.");
+               return -EINVAL;
+       }
+
+       if (strnlen(pci_addr, PCI_PRI_STR_SIZE) == PCI_PRI_STR_SIZE) {
+               IFPGA_RAWDEV_PMD_ERR("PCI address is too long.");
+               return -EINVAL;
+       }
+
+       if (rte_pci_addr_parse(pci_addr, &addr)) {
+               IFPGA_RAWDEV_PMD_ERR("PCI address %s is invalid.", pci_addr);
+               return -EINVAL;
+       }
+
+       snprintf(rdev_name, RTE_RAWDEV_NAME_MAX_LEN, "IFPGA:%02x:%02x.%x",
+               addr.bus, addr.devid, addr.function);
+       rdev = rte_rawdev_pmd_get_named_dev(rdev_name);
+       if (!rdev) {
+               IFPGA_RAWDEV_PMD_DEBUG("%s is not probed by ifpga driver.",
+                       pci_addr);
+               return -ENODEV;
+       }
+       *dev_id = rdev->dev_id;
+
+       return 0;
+}
+
+static struct rte_rawdev *
+get_rte_rawdev(uint16_t dev_id)
+{
+       struct rte_rawdev *dev = NULL;
+
+       if (dev_id >= RTE_RAWDEV_MAX_DEVS)
+               return NULL;
+
+       dev = &rte_rawdevs[dev_id];
+       if (dev->attached == RTE_RAWDEV_ATTACHED)
+               return dev;
+
+       return NULL;
+}
+
+static struct opae_adapter *
+get_opae_adapter(uint16_t dev_id)
+{
+       struct rte_rawdev *dev = NULL;
+       struct opae_adapter *adapter = NULL;
+
+       dev = get_rte_rawdev(dev_id);
+       if (!dev) {
+               IFPGA_RAWDEV_PMD_ERR("Device ID %u is invalid.", dev_id);
+               return NULL;
+       }
+
+       adapter = ifpga_rawdev_get_priv(dev);
+       if (!adapter) {
+               IFPGA_RAWDEV_PMD_ERR("Adapter is not registered.");
+               return NULL;
+       }
+
+       return adapter;
+}
+
+static opae_share_data *
+get_share_data(struct opae_adapter *adapter)
+{
+       opae_share_data *sd = NULL;
+
+       if (!adapter)
+               return NULL;
+
+       sd = (opae_share_data *)adapter->shm.ptr;
+       if (!sd) {
+               IFPGA_RAWDEV_PMD_ERR("Share data is not initialized.");
+               return NULL;
+       }
+
+       return sd;
+}
+
+int
+rte_pmd_ifpga_update_flash(uint16_t dev_id, const char *image,
+       uint64_t *status)
+{
+       struct opae_adapter *adapter = NULL;
+
+       adapter = get_opae_adapter(dev_id);
+       if (!adapter)
+               return -ENODEV;
+
+       return opae_mgr_update_flash(adapter->mgr, image, status);
+}
+
+int
+rte_pmd_ifpga_stop_update(uint16_t dev_id, int force)
+{
+       struct opae_adapter *adapter = NULL;
+
+       adapter = get_opae_adapter(dev_id);
+       if (!adapter)
+               return -ENODEV;
+
+       return opae_mgr_stop_flash_update(adapter->mgr, force);
+}
+
+int
+rte_pmd_ifpga_reboot_try(uint16_t dev_id)
+{
+       struct opae_adapter *adapter = NULL;
+       opae_share_data *sd = NULL;
+
+       adapter = get_opae_adapter(dev_id);
+       if (!adapter)
+               return -ENODEV;
+
+       sd = get_share_data(adapter);
+       if (!sd)
+               return -ENOMEM;
+
+       opae_adapter_lock(adapter, -1);
+       if (IFPGA_RSU_GET_STAT(sd->rsu_stat) != IFPGA_RSU_IDLE) {
+               opae_adapter_unlock(adapter);
+               IFPGA_RAWDEV_PMD_WARN("Update or reboot is in progress.");
+               return -EBUSY;
+       }
+       sd->rsu_stat = IFPGA_RSU_STATUS(IFPGA_RSU_REBOOT, 0);
+       opae_adapter_unlock(adapter);
+
+       return 0;
+}
+
+int
+rte_pmd_ifpga_reload(uint16_t dev_id, int type, int page)
+{
+       struct opae_adapter *adapter = NULL;
+
+       adapter = get_opae_adapter(dev_id);
+       if (!adapter)
+               return -ENODEV;
+
+       return opae_mgr_reload(adapter->mgr, type, page);
+}
diff --git a/drivers/raw/ifpga/rte_pmd_ifpga.h b/drivers/raw/ifpga/rte_pmd_ifpga.h
new file mode 100644 (file)
index 0000000..023a011
--- /dev/null
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#ifndef _RTE_PMD_IFPGA_H_
+#define _RTE_PMD_IFPGA_H_
+
+/**
+ * @file rte_pmd_ifpga.h
+ *
+ * ifpga PMD specific functions.
+ *
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Get raw device ID from PCI address string like 'Domain:Bus:Dev.Func'
+ *
+ * @param pci_addr
+ *    The PCI address of specified Intel FPGA device.
+ * @param dev_id
+ *    The buffer to output device ID.
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-ENODEV) if FPGA is not probed by ifpga driver.
+ */
+__rte_experimental
+int
+rte_pmd_ifpga_get_dev_id(const char *pci_addr, uint16_t *dev_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Update image flash of specified Intel FPGA device
+ *
+ * @param dev_id
+ *   The raw device ID of specified Intel FPGA device.
+ * @param image
+ *   The image file name string.
+ * @param status
+ *   The detailed update status for debug.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if dev_id is invalid.
+ *   - (-EINVAL) if bad parameter or staging area is not initialized.
+ *   - (-EBUSY) if FPGA is updating or rebooting.
+ *   - (-EIO) if failed to open image file.
+ */
+__rte_experimental
+int
+rte_pmd_ifpga_update_flash(uint16_t dev_id, const char *image,
+       uint64_t *status);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Stop flash update of specified Intel FPGA device
+ *
+ * @param dev_id
+ *   The raw device ID of specified Intel FPGA device.
+ * @param force
+ *   Abort the update process by writing register if set non-zero.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if dev_id is invalid.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EAGAIN) if failed with force.
+ */
+__rte_experimental
+int
+rte_pmd_ifpga_stop_update(uint16_t dev_id, int force);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Check current Intel FPGA status and change it to reboot status if it is idle
+ *
+ * @param dev_id
+ *    The raw device ID of specified Intel FPGA device.
+ * @return
+ *   - (0) if FPGA is ready to reboot.
+ *   - (-ENODEV) if dev_id is invalid.
+ *   - (-ENOMEM) if share data is not initialized.
+ *   - (-EBUSY) if FPGA is updating or rebooting.
+ */
+__rte_experimental
+int
+rte_pmd_ifpga_reboot_try(uint16_t dev_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Trigger full reconfiguration of specified Intel FPGA device
+ *
+ * @param dev_id
+ *    The raw device ID of specified Intel FPGA device.
+ * @param type
+ *    Select reconfiguration type.
+ *    0 - reconfigure FPGA only.
+ *    1 - reboot the whole card including FPGA.
+ * @param page
+ *    Select image from which flash partition.
+ *    0 - factory partition.
+ *    1 - user partition.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if dev_id is invalid.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if failed to access BMC register.
+ */
+__rte_experimental
+int
+rte_pmd_ifpga_reload(uint16_t dev_id, int type, int page);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_PMD_IFPGA_H_ */
index 4a76d1d..16584f7 100644 (file)
@@ -1,3 +1,14 @@
 DPDK_21 {
        local: *;
 };
+
+EXPERIMENTAL {
+       global:
+
+       # added in 21.05
+       rte_pmd_ifpga_get_dev_id;
+       rte_pmd_ifpga_update_flash;
+       rte_pmd_ifpga_stop_update;
+       rte_pmd_ifpga_reboot_try;
+       rte_pmd_ifpga_reload;
+};