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
44MPS_TABLE(top, flash);
45
46static int
47flash_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
116MPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]",
117    "Save firmware/bios into a file");
118
119static int
120flash_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
244MPS_COMMAND(flash, update, flash_update, "[firmware|bios] file",
245    "Update firmware/bios");
246