xref: /freebsd/usr.sbin/mpsutil/mps_flash.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
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$");
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 		return (1);
87 	}
88 
89 	close(fd);
90 	if (size > 0) {
91 		fd = open(firmware_file, O_CREAT | O_TRUNC | O_RDWR, 0644);
92 		if (fd <0) {
93 			error = errno;
94 			warn("open");
95 			free(firmware_buffer);
96 			return (error);
97 		}
98 		while (written != size) {
99 			if ((ret = write(fd, firmware_buffer + written, size - written)) <0) {
100 				error = errno;
101 				warn("write");
102 				free(firmware_buffer);
103 				return (error);
104 			}
105 			written += ret;
106 		}
107 		close(fd);
108 	}
109 	free(firmware_buffer);
110 	printf("%s successfully saved as %s\n", argv[1], firmware_file);
111 	return (0);
112 }
113 
114 MPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]",
115     "Save firmware/bios into a file");
116 
117 static int
118 flash_update(int argc, char **argv)
119 {
120 	int error, fd;
121 	unsigned char *mem = NULL;
122 	struct stat st;
123 	bool bios = false;
124 	MPI2_FW_IMAGE_HEADER *fwheader;
125 	MPI2_IOC_FACTS_REPLY *facts;
126 
127 	if (argc < 2) {
128 		warnx("missing argument: expecting 'firmware' or bios'");
129 		return (EINVAL);
130 	}
131 
132 	if (strcmp(argv[1], "bios") == 0) {
133 		bios = true;
134 	} else if (strcmp(argv[1], "firmware") != 0) {
135 		warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
136 		    argv[1]);
137 	}
138 
139 	if (argc > 4) {
140 		warnx("update firmware: extra arguments");
141 		return (EINVAL);
142 	}
143 
144 	if (argc != 3) {
145 		warnx("no firmware specified");
146 		return (EINVAL);
147 	}
148 
149 	if (stat(argv[2], &st) == -1) {
150 		error = errno;
151 		warn("stat");
152 		return (error);
153 	}
154 
155 	fd = open(argv[2], O_RDONLY);
156 	if (fd < 0) {
157 		error = errno;
158 		warn("open");
159 		return (error);
160 	}
161 
162 	mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
163 	if (mem == MAP_FAILED) {
164 		error = errno;
165 		warn("mmap");
166 		close(fd);
167 		return (error);
168 	}
169 	close(fd);
170 
171 	fd = mps_open(mps_unit);
172 	if (fd < 0) {
173 		error = errno;
174 		warn("mps_open");
175 		munmap(mem, st.st_size);
176 		return (error);
177 	}
178 
179 	if ((facts = mps_get_iocfacts(fd)) == NULL) {
180 		warnx("could not get controller IOCFacts\n");
181 		munmap(mem, st.st_size);
182 		close(fd);
183 		return (EINVAL);
184 	}
185 
186 	if (bios) {
187 		/* Check boot record magic number */
188 		if (((mem[0x01]<<8) + mem[0x00]) != 0xaa55) {
189 			warnx("Invalid bios: no boot record magic number");
190 			munmap(mem, st.st_size);
191 			close(fd);
192 			return (1);
193 		}
194 		if ((st.st_size % 512) != 0) {
195 			warnx("Invalid bios: size not a multiple of 512");
196 			munmap(mem, st.st_size);
197 			close(fd);
198 			return (1);
199 		}
200 	} else {
201 		fwheader = (MPI2_FW_IMAGE_HEADER *)mem;
202 		if (fwheader->VendorID != MPI2_MFGPAGE_VENDORID_LSI) {
203 			warnx("Invalid firmware:");
204 			warnx("  Expected Vendor ID: %04x",
205 			    MPI2_MFGPAGE_VENDORID_LSI);
206 			warnx("  Image Vendor ID: %04x", fwheader->VendorID);
207 			munmap(mem, st.st_size);
208 			close(fd);
209 			return (1);
210 		}
211 
212 		if (fwheader->ProductID != facts->ProductID) {
213 			warnx("Invalid image:");
214 			warnx("  Expected Product ID: %04x", facts->ProductID);
215 			warnx("  Image Product ID: %04x", fwheader->ProductID);
216 			munmap(mem, st.st_size);
217 			close(fd);
218 			return (1);
219 		}
220 	}
221 
222 	printf("Updating %s...\n", argv[1]);
223 	if (mps_firmware_send(fd, mem, st.st_size, bios) < 0) {
224 		warnx("Fail to update %s", argv[1]);
225 		munmap(mem, st.st_size);
226 		close(fd);
227 		return (1);
228 	}
229 
230 	munmap(mem, st.st_size);
231 	close(fd);
232 	printf("%s successfully updated\n", argv[1]);
233 	return (0);
234 }
235 
236 MPS_COMMAND(flash, update, flash_update, "[firmware|bios] file",
237     "Update firmware/bios");
238