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		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
114MPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]",
115    "Save firmware/bios into a file");
116
117static int
118flash_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
236MPS_COMMAND(flash, update, flash_update, "[firmware|bios] file",
237    "Update firmware/bios");
238