xref: /dragonfly/usr.sbin/mpsutil/mps_flash.c (revision 2b3f93ea)
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");
246