forked from linux-nvme/nvme-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmicron-nvme.c
154 lines (125 loc) · 3.53 KB
/
micron-nvme.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include <sys/ioctl.h>
#define CREATE_CMD
#include "micron-nvme.h"
#define min(x, y) ((x) > (y) ? (y) : (x))
static int micron_fw_commit(int fd, int select)
{
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_activate_fw,
.cdw10 = 8,
.cdw12 = select,
};
return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
}
static int micron_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "This performs a selective firmware download, which allows the user to " \
"select which firmware binary to update. This requires a power cycle once the " \
"update completes. The options available are: \n\n" \
"OOB - This updates the OOB and main firmware\n" \
"EEP - This updates the eeprom and main firmware\n" \
"ALL - This updates the eeprom, OOB, and main firmware";
const char *fw = "firmware file (required)";
const char *select = "FW Select (e.g., --select=ALL)";
int xfer = 4096;
void *fw_buf;
int fd, selectNo,fw_fd,fw_size,err,offset = 0;
struct stat sb;
struct config {
char *fw;
char *select;
};
struct config cfg = {
.fw = "",
.select = "\0",
};
const struct argconfig_commandline_options command_line_options[] = {
{"fw", 'f', "FILE", CFG_STRING, &cfg.fw, required_argument, fw},
{"select", 's', "flag", CFG_STRING, &cfg.select, required_argument, select},
{NULL}
};
fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
if (fd < 0)
return fd;
if(strlen(cfg.select) != 3) {
fprintf(stderr, "Invalid select flag\n");
err = EINVAL;
goto out;
}
for(int i=0;i<3;i++)
{
cfg.select[i] = toupper(cfg.select[i]);
}
if(strncmp(cfg.select,"OOB", 3) == 0) {
selectNo = 18;
}
else if(strncmp(cfg.select,"EEP", 3) == 0) {
selectNo = 10;
}
else if(strncmp(cfg.select,"ALL", 3) == 0) {
selectNo = 26;
}
else {
fprintf(stderr, "Invalid select flag\n");
err = EINVAL;
goto out;
}
fw_fd = open(cfg.fw, O_RDONLY);
if (fw_fd < 0) {
fprintf(stderr, "no firmware file provided\n");
err = EINVAL;
goto out;
}
err = fstat(fw_fd, &sb);
if (err < 0) {
perror("fstat");
err = errno;
}
fw_size = sb.st_size;
if (fw_size & 0x3) {
fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size);
err = EINVAL;
goto out;
}
if (posix_memalign(&fw_buf, getpagesize(), fw_size)) {
fprintf(stderr, "No memory for f/w size:%d\n", fw_size);
err = ENOMEM;
goto out;
}
if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size)))
return EIO;
while (fw_size > 0)
{
xfer = min(xfer, fw_size);
err = nvme_fw_download(fd, offset, xfer, fw_buf);
if (err < 0) {
perror("fw-download");
goto out;
} else if (err != 0) {
fprintf(stderr, "NVME Admin command error:%s(%x)\n",
nvme_status_to_string(err), err);
goto out;
}
fw_buf += xfer;
fw_size -= xfer;
offset += xfer;
}
err = micron_fw_commit(fd,selectNo);
if(err == 0x10B || err == 0x20B) {
err = 0;
fprintf(stderr, "Update successful! Please power cycle for changes to take effect\n");
}
out:
return err;
}