Skip to content

Commit ac5f00a

Browse files
committed
mi: Add firmware download and commit commands
This change adds MI implementations for the Firmware Download and Firmware Commit admin commands, as well as a couple of tests. As usual, these are designed to match the ioctl API. Signed-off-by: Jeremy Kerr <[email protected]>
1 parent 8b7378a commit ac5f00a

4 files changed

Lines changed: 273 additions & 0 deletions

File tree

src/libnvme-mi.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ LIBNVME_MI_1_2 {
66
nvme_mi_admin_ns_attach;
77
nvme_mi_admin_format_nvm;
88
nvme_mi_admin_sanitize_nvm;
9+
nvme_mi_admin_fw_download;
10+
nvme_mi_admin_fw_commit;
911
};
1012

1113
LIBNVME_MI_1_1 {

src/nvme/mi.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,78 @@ int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl,
831831
return nvme_mi_admin_parse_status(&resp, args->result);
832832
}
833833

834+
int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl,
835+
struct nvme_fw_download_args *args)
836+
{
837+
struct nvme_mi_admin_resp_hdr resp_hdr;
838+
struct nvme_mi_admin_req_hdr req_hdr;
839+
struct nvme_mi_resp resp;
840+
struct nvme_mi_req req;
841+
int rc;
842+
843+
if (args->args_size < sizeof(*args))
844+
return -EINVAL;
845+
846+
if (args->data_len & 0x3)
847+
return -EINVAL;
848+
849+
if (args->offset & 0x3)
850+
return -EINVAL;
851+
852+
if (!args->data_len)
853+
return -EINVAL;
854+
855+
nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
856+
nvme_admin_fw_download);
857+
858+
req_hdr.cdw10 = cpu_to_le32((args->data_len >> 2) - 1);
859+
req_hdr.cdw11 = cpu_to_le32(args->offset >> 2);
860+
req.data = args->data;
861+
req.data_len = args->data_len;
862+
req_hdr.dlen = cpu_to_le32(args->data_len);
863+
req_hdr.flags = 0x1;
864+
865+
nvme_mi_calc_req_mic(&req);
866+
867+
nvme_mi_admin_init_resp(&resp, &resp_hdr);
868+
869+
rc = nvme_mi_submit(ctrl->ep, &req, &resp);
870+
if (rc)
871+
return rc;
872+
873+
return nvme_mi_admin_parse_status(&resp, NULL);
874+
}
875+
876+
int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl,
877+
struct nvme_fw_commit_args *args)
878+
{
879+
struct nvme_mi_admin_resp_hdr resp_hdr;
880+
struct nvme_mi_admin_req_hdr req_hdr;
881+
struct nvme_mi_resp resp;
882+
struct nvme_mi_req req;
883+
int rc;
884+
885+
if (args->args_size < sizeof(*args))
886+
return -EINVAL;
887+
888+
nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
889+
nvme_admin_fw_commit);
890+
891+
req_hdr.cdw10 = cpu_to_le32(((args->bpid & 0x1) << 31) |
892+
((args->action & 0x7) << 3) |
893+
((args->slot & 0x7) << 0));
894+
895+
nvme_mi_calc_req_mic(&req);
896+
897+
nvme_mi_admin_init_resp(&resp, &resp_hdr);
898+
899+
rc = nvme_mi_submit(ctrl->ep, &req, &resp);
900+
if (rc)
901+
return rc;
902+
903+
return nvme_mi_admin_parse_status(&resp, NULL);
904+
}
905+
834906
int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl,
835907
struct nvme_format_nvm_args *args)
836908
{

src/nvme/mi.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,42 @@ static inline int nvme_mi_admin_ns_detach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid,
23442344
return nvme_mi_admin_ns_attach(ctrl, &args);
23452345
}
23462346

2347+
/**
2348+
* nvme_mi_admin_fw_download() - Download part or all of a firmware image to
2349+
* the controller
2350+
* @ctrl: Controller to send firmware data to
2351+
* @args: &struct nvme_fw_download_args argument structure
2352+
*
2353+
* The Firmware Image Download command downloads all or a portion of an image
2354+
* for a future update to the controller. The Firmware Image Download command
2355+
* downloads a new image (in whole or in part) to the controller.
2356+
*
2357+
* The image may be constructed of multiple pieces that are individually
2358+
* downloaded with separate Firmware Image Download commands. Each Firmware
2359+
* Image Download command includes a Dword Offset and Number of Dwords that
2360+
* specify a dword range.
2361+
*
2362+
* The new firmware image is not activated as part of the Firmware Image
2363+
* Download command. Use the nvme_mi_admin_fw_commit() to activate a newly
2364+
* downloaded image.
2365+
*
2366+
* Return: 0 on success, non-zero on failure
2367+
*/
2368+
int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl,
2369+
struct nvme_fw_download_args *args);
2370+
2371+
/**
2372+
* nvme_mi_admin_fw_commit() - Commit firmware using the specified action
2373+
* @ctrl: Controller to send firmware data to
2374+
* @args: &struct nvme_fw_download_args argument structure
2375+
*
2376+
* The Firmware Commit command modifies the firmware image or Boot Partitions.
2377+
*
2378+
* Return: 0 on success, non-zero on failure
2379+
*/
2380+
int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl,
2381+
struct nvme_fw_commit_args *args);
2382+
23472383
/**
23482384
* nvme_mi_admin_format_nvm() - Format NVMe namespace
23492385
* @ctrl: Controller to send command to

test/mi.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,167 @@ static void test_admin_ns_detach(struct nvme_mi_ep *ep)
13961396
assert(!rc);
13971397
}
13981398

1399+
struct fw_download_info {
1400+
uint32_t offset;
1401+
uint32_t len;
1402+
void *data;
1403+
};
1404+
1405+
static int test_admin_fw_download_cb(struct nvme_mi_ep *ep,
1406+
struct nvme_mi_req *req,
1407+
struct nvme_mi_resp *resp,
1408+
void *data)
1409+
{
1410+
struct fw_download_info *info = data;
1411+
__u32 len, off;
1412+
__u8 *rq_hdr;
1413+
1414+
rq_hdr = (__u8 *)req->hdr;
1415+
assert(rq_hdr[4] == nvme_admin_fw_download);
1416+
1417+
len = rq_hdr[47] << 24 | rq_hdr[46] << 16 | rq_hdr[45] << 8 | rq_hdr[44];
1418+
off = rq_hdr[51] << 24 | rq_hdr[50] << 16 | rq_hdr[49] << 8 | rq_hdr[48];
1419+
1420+
assert(off << 2 == info->offset);
1421+
assert(((len+1) << 2) == info->len);
1422+
1423+
/* ensure that the request len matches too */
1424+
assert(req->data_len == info->len);
1425+
1426+
assert(!memcmp(req->data, info->data, len));
1427+
1428+
test_transport_resp_calc_mic(resp);
1429+
1430+
return 0;
1431+
}
1432+
1433+
static void test_admin_fw_download(struct nvme_mi_ep *ep)
1434+
{
1435+
struct nvme_fw_download_args args;
1436+
struct fw_download_info info;
1437+
unsigned char fw[4096];
1438+
nvme_mi_ctrl_t ctrl;
1439+
int rc, i;
1440+
1441+
for (i = 0; i < sizeof(fw); i++)
1442+
fw[i] = i % 0xff;
1443+
1444+
info.offset = 0;
1445+
info.len = 0;
1446+
info.data = fw;
1447+
args.data = fw;
1448+
args.args_size = sizeof(args);
1449+
1450+
test_set_transport_callback(ep, test_admin_fw_download_cb, &info);
1451+
1452+
ctrl = nvme_mi_init_ctrl(ep, 5);
1453+
assert(ctrl);
1454+
1455+
/* invalid (zero) len */
1456+
args.data_len = info.len = 1;
1457+
args.offset = info.offset = 0;
1458+
rc = nvme_mi_admin_fw_download(ctrl, &args);
1459+
assert(rc);
1460+
1461+
/* invalid (unaligned) len */
1462+
args.data_len = info.len = 1;
1463+
args.offset = info.offset = 0;
1464+
rc = nvme_mi_admin_fw_download(ctrl, &args);
1465+
assert(rc);
1466+
1467+
/* invalid offset */
1468+
args.data_len = info.len = 4;
1469+
args.offset = info.offset = 1;
1470+
rc = nvme_mi_admin_fw_download(ctrl, &args);
1471+
assert(rc);
1472+
1473+
/* smallest len */
1474+
args.data_len = info.len = 4;
1475+
args.offset = info.offset = 0;
1476+
rc = nvme_mi_admin_fw_download(ctrl, &args);
1477+
assert(!rc);
1478+
1479+
/* largest len */
1480+
args.data_len = info.len = 4096;
1481+
args.offset = info.offset = 0;
1482+
rc = nvme_mi_admin_fw_download(ctrl, &args);
1483+
assert(!rc);
1484+
1485+
/* offset value */
1486+
args.data_len = info.len = 4096;
1487+
args.offset = info.offset = 4096;
1488+
rc = nvme_mi_admin_fw_download(ctrl, &args);
1489+
assert(!rc);
1490+
}
1491+
1492+
struct fw_commit_info {
1493+
__u8 bpid;
1494+
__u8 action;
1495+
__u8 slot;
1496+
};
1497+
1498+
static int test_admin_fw_commit_cb(struct nvme_mi_ep *ep,
1499+
struct nvme_mi_req *req,
1500+
struct nvme_mi_resp *resp,
1501+
void *data)
1502+
{
1503+
struct fw_commit_info *info = data;
1504+
__u8 bpid, action, slot;
1505+
__u8 *rq_hdr;
1506+
1507+
rq_hdr = (__u8 *)req->hdr;
1508+
assert(rq_hdr[4] == nvme_admin_fw_commit);
1509+
1510+
bpid = (rq_hdr[47] >> 7) & 0x1;
1511+
slot = rq_hdr[44] & 0x7;
1512+
action = (rq_hdr[44] >> 3) & 0x7;
1513+
1514+
assert(!!bpid == !!info->bpid);
1515+
assert(slot == info->slot);
1516+
assert(action == info->action);
1517+
1518+
test_transport_resp_calc_mic(resp);
1519+
1520+
return 0;
1521+
}
1522+
1523+
static void test_admin_fw_commit(struct nvme_mi_ep *ep)
1524+
{
1525+
struct nvme_fw_commit_args args;
1526+
struct fw_commit_info info;
1527+
nvme_mi_ctrl_t ctrl;
1528+
int rc;
1529+
1530+
args.args_size = sizeof(args);
1531+
info.bpid = args.bpid = 0;
1532+
1533+
test_set_transport_callback(ep, test_admin_fw_commit_cb, &info);
1534+
1535+
ctrl = nvme_mi_init_ctrl(ep, 5);
1536+
assert(ctrl);
1537+
1538+
/* all zeros */
1539+
info.bpid = args.bpid = 0;
1540+
info.slot = args.slot = 0;
1541+
info.action = args.action = 0;
1542+
rc = nvme_mi_admin_fw_commit(ctrl, &args);
1543+
assert(!rc);
1544+
1545+
/* all ones */
1546+
info.bpid = args.bpid = 1;
1547+
info.slot = args.slot = 0x7;
1548+
info.action = args.action = 0x7;
1549+
rc = nvme_mi_admin_fw_commit(ctrl, &args);
1550+
assert(!rc);
1551+
1552+
/* correct fields */
1553+
info.bpid = args.bpid = 1;
1554+
info.slot = args.slot = 2;
1555+
info.action = args.action = 3;
1556+
rc = nvme_mi_admin_fw_commit(ctrl, &args);
1557+
assert(!rc);
1558+
}
1559+
13991560
struct format_data {
14001561
__u32 nsid;
14011562
__u8 lbafu;
@@ -1573,6 +1734,8 @@ struct test {
15731734
DEFINE_TEST(admin_ns_mgmt_delete),
15741735
DEFINE_TEST(admin_ns_attach),
15751736
DEFINE_TEST(admin_ns_detach),
1737+
DEFINE_TEST(admin_fw_download),
1738+
DEFINE_TEST(admin_fw_commit),
15761739
DEFINE_TEST(admin_format_nvm),
15771740
DEFINE_TEST(admin_sanitize_nvm),
15781741
};

0 commit comments

Comments
 (0)