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#include <sys/stat.h>
28#include <sys/param.h>
29#include <sys/mman.h>
30#include <sys/endian.h>
31
32#include <errno.h>
33#include <err.h>
34#include <fcntl.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include "mpsutil.h"
42
43MPS_TABLE(top, flash);
44
45static int
46flash_save(int argc, char **argv)
47{
48	const char *firmware_file;
49	unsigned char *firmware_buffer = NULL;
50	int error, fd, size;
51	bool bios = false;
52	ssize_t written = 0, ret = 0;
53
54	if (argc < 2) {
55		warnx("missing argument: expecting 'firmware' or bios'");
56		return (EINVAL);
57	}
58
59	if (strcmp(argv[1], "bios") == 0) {
60		bios = true;
61	} else if (strcmp(argv[1], "firmware") != 0) {
62		warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
63		    argv[1]);
64	}
65
66	if (argc > 4) {
67		warnx("save %s: extra arguments", argv[1]);
68		return (EINVAL);
69	}
70
71	firmware_file = argv[1];
72	if (argc == 3) {
73		firmware_file = argv[2];
74	}
75
76	fd = mps_open(mps_unit);
77	if (fd < 0) {
78		error = errno;
79		warn("mps_open");
80		return (error);
81	}
82
83	if ((size = mps_firmware_get(fd, &firmware_buffer, bios)) < 0) {
84		warnx("Fail to save %s", argv[1]);
85		close(fd);
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				close(fd);
104				return (error);
105			}
106			written += ret;
107		}
108		close(fd);
109	}
110	free(firmware_buffer);
111	printf("%s successfully saved as %s\n", argv[1], firmware_file);
112	return (0);
113}
114
115MPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]",
116    "Save firmware/bios into a file");
117
118static int
119flash_update(int argc, char **argv)
120{
121	int error, fd;
122	unsigned char *mem = NULL;
123	struct stat st;
124	bool bios = false;
125	MPI2_FW_IMAGE_HEADER *fwheader;
126	MPI2_IOC_FACTS_REPLY *facts;
127
128	if (argc < 2) {
129		warnx("missing argument: expecting 'firmware' or bios'");
130		return (EINVAL);
131	}
132
133	if (strcmp(argv[1], "bios") == 0) {
134		bios = true;
135	} else if (strcmp(argv[1], "firmware") != 0) {
136		warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
137		    argv[1]);
138	}
139
140	if (argc > 4) {
141		warnx("update firmware: extra arguments");
142		return (EINVAL);
143	}
144
145	if (argc != 3) {
146		warnx("no firmware specified");
147		return (EINVAL);
148	}
149
150	if (stat(argv[2], &st) == -1) {
151		error = errno;
152		warn("stat");
153		return (error);
154	}
155
156	fd = open(argv[2], O_RDONLY);
157	if (fd < 0) {
158		error = errno;
159		warn("open");
160		return (error);
161	}
162
163	mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
164	if (mem == MAP_FAILED) {
165		error = errno;
166		warn("mmap");
167		close(fd);
168		return (error);
169	}
170	close(fd);
171
172	fd = mps_open(mps_unit);
173	if (fd < 0) {
174		error = errno;
175		warn("mps_open");
176		munmap(mem, st.st_size);
177		return (error);
178	}
179
180	if ((facts = mps_get_iocfacts(fd)) == NULL) {
181		warnx("could not get controller IOCFacts\n");
182		munmap(mem, st.st_size);
183		close(fd);
184		return (EINVAL);
185	}
186
187	if (bios) {
188		/* Check boot record magic number */
189		if (((mem[0x01]<<8) + mem[0x00]) != 0xaa55) {
190			warnx("Invalid bios: no boot record magic number");
191			munmap(mem, st.st_size);
192			close(fd);
193			free(facts);
194			return (1);
195		}
196		if ((st.st_size % 512) != 0) {
197			warnx("Invalid bios: size not a multiple of 512");
198			munmap(mem, st.st_size);
199			close(fd);
200			free(facts);
201			return (1);
202		}
203	} else {
204		fwheader = (MPI2_FW_IMAGE_HEADER *)mem;
205		if (le16toh(fwheader->VendorID) != MPI2_MFGPAGE_VENDORID_LSI) {
206			warnx("Invalid firmware:");
207			warnx("  Expected Vendor ID: %04x",
208			    MPI2_MFGPAGE_VENDORID_LSI);
209			warnx("  Image Vendor ID: %04x", le16toh(fwheader->VendorID));
210			munmap(mem, st.st_size);
211			close(fd);
212			free(facts);
213			return (1);
214		}
215
216		if (le16toh(fwheader->ProductID) != facts->ProductID) {
217			warnx("Invalid image:");
218			warnx("  Expected Product ID: %04x", facts->ProductID);
219			warnx("  Image Product ID: %04x", le16toh(fwheader->ProductID));
220			munmap(mem, st.st_size);
221			close(fd);
222			free(facts);
223			return (1);
224		}
225	}
226
227	printf("Updating %s...\n", argv[1]);
228	if (mps_firmware_send(fd, mem, st.st_size, bios) < 0) {
229		warnx("Fail to update %s", argv[1]);
230		munmap(mem, st.st_size);
231		close(fd);
232		free(facts);
233		return (1);
234	}
235
236	munmap(mem, st.st_size);
237	close(fd);
238	free(facts);
239	printf("%s successfully updated\n", argv[1]);
240	return (0);
241}
242
243MPS_COMMAND(flash, update, flash_update, "[firmware|bios] file",
244    "Update firmware/bios");
245