Merge branch 'vendor/DHCPCD'
[dragonfly.git] / usr.sbin / mpsutil / mps_flash.c
1 /*-
2  * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __RCSID("$FreeBSD: head/usr.sbin/mpsutil/mps_flash.c 298374 2016-04-20 21:11:49Z bapt $");
28
29 #include <sys/stat.h>
30 #include <sys/param.h>
31 #include <sys/mman.h>
32
33 #include <errno.h>
34 #include <err.h>
35 #include <fcntl.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #include "mpsutil.h"
43
44 MPS_TABLE(top, flash);
45
46 static int
47 flash_save(int argc, char **argv)
48 {
49         const char *firmware_file;
50         unsigned char *firmware_buffer = NULL;
51         int error, fd, size;
52         bool bios = false;
53         ssize_t written = 0, ret = 0;
54
55         if (argc < 2) {
56                 warnx("missing argument: expecting 'firmware' or bios'");
57                 return (EINVAL);
58         }
59
60         if (strcmp(argv[1], "bios") == 0) {
61                 bios = true;
62         } else if (strcmp(argv[1], "firmware") != 0) {
63                 warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
64                     argv[1]);
65         }
66
67         if (argc > 4) {
68                 warnx("save %s: extra arguments", argv[1]);
69                 return (EINVAL);
70         }
71
72         firmware_file = argv[1];
73         if (argc == 3) {
74                 firmware_file = argv[2];
75         }
76
77         fd = mps_open(mps_unit);
78         if (fd < 0) {
79                 error = errno;
80                 warn("mps_open");
81                 return (error);
82         }
83
84         if ((size = mps_firmware_get(fd, &firmware_buffer, bios)) < 0) {
85                 warnx("Fail to save %s", argv[1]);
86                 close(fd);
87                 return (1);
88         }
89
90         close(fd);
91         if (size > 0) {
92                 fd = open(firmware_file, O_CREAT | O_TRUNC | O_RDWR, 0644);
93                 if (fd <0) {
94                         error = errno;
95                         warn("open");
96                         free(firmware_buffer);
97                         return (error);
98                 }
99                 while (written != size) {
100                         if ((ret = write(fd, firmware_buffer + written, size - written)) <0) {
101                                 error = errno;
102                                 warn("write");
103                                 free(firmware_buffer);
104                                 close(fd);
105                                 return (error);
106                         }
107                         written += ret;
108                 }
109                 close(fd);
110         }
111         free(firmware_buffer);
112         printf("%s successfully saved as %s\n", argv[1], firmware_file);
113         return (0);
114 }
115
116 MPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]",
117     "Save firmware/bios into a file");
118
119 static int
120 flash_update(int argc, char **argv)
121 {
122         int error, fd;
123         unsigned char *mem = NULL;
124         struct stat st;
125         bool bios = false;
126         MPI2_FW_IMAGE_HEADER *fwheader;
127         MPI2_IOC_FACTS_REPLY *facts;
128
129         if (argc < 2) {
130                 warnx("missing argument: expecting 'firmware' or bios'");
131                 return (EINVAL);
132         }
133
134         if (strcmp(argv[1], "bios") == 0) {
135                 bios = true;
136         } else if (strcmp(argv[1], "firmware") != 0) {
137                 warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
138                     argv[1]);
139         }
140
141         if (argc > 4) {
142                 warnx("update firmware: extra arguments");
143                 return (EINVAL);
144         }
145
146         if (argc != 3) {
147                 warnx("no firmware specified");
148                 return (EINVAL);
149         }
150
151         if (stat(argv[2], &st) == -1) {
152                 error = errno;
153                 warn("stat");
154                 return (error);
155         }
156
157         fd = open(argv[2], O_RDONLY);
158         if (fd < 0) {
159                 error = errno;
160                 warn("open");
161                 return (error);
162         }
163
164         mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
165         if (mem == MAP_FAILED) {
166                 error = errno;
167                 warn("mmap");
168                 close(fd);
169                 return (error);
170         }
171         close(fd);
172
173         fd = mps_open(mps_unit);
174         if (fd < 0) {
175                 error = errno;
176                 warn("mps_open");
177                 munmap(mem, st.st_size);
178                 return (error);
179         }
180
181         if ((facts = mps_get_iocfacts(fd)) == NULL) {
182                 warnx("could not get controller IOCFacts\n");
183                 munmap(mem, st.st_size);
184                 close(fd);
185                 return (EINVAL);
186         }
187
188         if (bios) {
189                 /* Check boot record magic number */
190                 if (((mem[0x01]<<8) + mem[0x00]) != 0xaa55) {
191                         warnx("Invalid bios: no boot record magic number");
192                         munmap(mem, st.st_size);
193                         close(fd);
194                         free(facts);
195                         return (1);
196                 }
197                 if ((st.st_size % 512) != 0) {
198                         warnx("Invalid bios: size not a multiple of 512");
199                         munmap(mem, st.st_size);
200                         close(fd);
201                         free(facts);
202                         return (1);
203                 }
204         } else {
205                 fwheader = (MPI2_FW_IMAGE_HEADER *)mem;
206                 if (fwheader->VendorID != MPI2_MFGPAGE_VENDORID_LSI) {
207                         warnx("Invalid firmware:");
208                         warnx("  Expected Vendor ID: %04x",
209                             MPI2_MFGPAGE_VENDORID_LSI);
210                         warnx("  Image Vendor ID: %04x", fwheader->VendorID);
211                         munmap(mem, st.st_size);
212                         close(fd);
213                         free(facts);
214                         return (1);
215                 }
216
217                 if (fwheader->ProductID != facts->ProductID) {
218                         warnx("Invalid image:");
219                         warnx("  Expected Product ID: %04x", facts->ProductID);
220                         warnx("  Image Product ID: %04x", fwheader->ProductID);
221                         munmap(mem, st.st_size);
222                         close(fd);
223                         free(facts);
224                         return (1);
225                 }
226         }
227
228         printf("Updating %s...\n", argv[1]);
229         if (mps_firmware_send(fd, mem, st.st_size, bios) < 0) {
230                 warnx("Fail to update %s", argv[1]);
231                 munmap(mem, st.st_size);
232                 close(fd);
233                 free(facts);
234                 return (1);
235         }
236
237         munmap(mem, st.st_size);
238         close(fd);
239         free(facts);
240         printf("%s successfully updated\n", argv[1]);
241         return (0);
242 }
243
244 MPS_COMMAND(flash, update, flash_update, "[firmware|bios] file",
245     "Update firmware/bios");