1297590Ssbruno/*-
2297590Ssbruno * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
3297590Ssbruno *
4297590Ssbruno * Redistribution and use in source and binary forms, with or without
5297590Ssbruno * modification, are permitted provided that the following conditions
6297590Ssbruno * are met:
7297590Ssbruno * 1. Redistributions of source code must retain the above copyright
8297590Ssbruno *    notice, this list of conditions and the following disclaimer.
9297590Ssbruno * 2. Redistributions in binary form must reproduce the above copyright
10297590Ssbruno *    notice, this list of conditions and the following disclaimer in the
11297590Ssbruno *    documentation and/or other materials provided with the distribution.
12297590Ssbruno *
13297590Ssbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14297590Ssbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15297590Ssbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16297590Ssbruno * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17297590Ssbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18297590Ssbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19297590Ssbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20297590Ssbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21297590Ssbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22297590Ssbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23297590Ssbruno * SUCH DAMAGE.
24297590Ssbruno */
25297590Ssbruno
26297590Ssbruno#include <sys/cdefs.h>
27297590Ssbruno__RCSID("$FreeBSD$");
28297590Ssbruno
29297590Ssbruno#include <sys/stat.h>
30297590Ssbruno#include <sys/param.h>
31297590Ssbruno#include <sys/mman.h>
32297590Ssbruno
33297590Ssbruno#include <errno.h>
34297590Ssbruno#include <err.h>
35297590Ssbruno#include <fcntl.h>
36297590Ssbruno#include <stdbool.h>
37297590Ssbruno#include <stdio.h>
38297590Ssbruno#include <stdlib.h>
39297590Ssbruno#include <string.h>
40297590Ssbruno#include <unistd.h>
41297590Ssbruno
42297590Ssbruno#include "mpsutil.h"
43297590Ssbruno
44297590SsbrunoMPS_TABLE(top, flash);
45297590Ssbruno
46297590Ssbrunostatic int
47297590Ssbrunoflash_save(int argc, char **argv)
48297590Ssbruno{
49297590Ssbruno	const char *firmware_file;
50297590Ssbruno	unsigned char *firmware_buffer = NULL;
51297590Ssbruno	int error, fd, size;
52297590Ssbruno	bool bios = false;
53297590Ssbruno	ssize_t written = 0, ret = 0;
54297590Ssbruno
55297590Ssbruno	if (argc < 2) {
56297590Ssbruno		warnx("missing argument: expecting 'firmware' or bios'");
57297590Ssbruno		return (EINVAL);
58297590Ssbruno	}
59297590Ssbruno
60297590Ssbruno	if (strcmp(argv[1], "bios") == 0) {
61297590Ssbruno		bios = true;
62297590Ssbruno	} else if (strcmp(argv[1], "firmware") != 0) {
63297590Ssbruno		warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
64297590Ssbruno		    argv[1]);
65297590Ssbruno	}
66297590Ssbruno
67297590Ssbruno	if (argc > 4) {
68297590Ssbruno		warnx("save %s: extra arguments", argv[1]);
69297590Ssbruno		return (EINVAL);
70297590Ssbruno	}
71297590Ssbruno
72297590Ssbruno	firmware_file = argv[1];
73297590Ssbruno	if (argc == 3) {
74297590Ssbruno		firmware_file = argv[2];
75297590Ssbruno	}
76297590Ssbruno
77297590Ssbruno	fd = mps_open(mps_unit);
78297590Ssbruno	if (fd < 0) {
79297590Ssbruno		error = errno;
80297590Ssbruno		warn("mps_open");
81297590Ssbruno		return (error);
82297590Ssbruno	}
83297590Ssbruno
84297590Ssbruno	if ((size = mps_firmware_get(fd, &firmware_buffer, bios)) < 0) {
85297590Ssbruno		warnx("Fail to save %s", argv[1]);
86297590Ssbruno		return (1);
87297590Ssbruno	}
88297590Ssbruno
89297590Ssbruno	close(fd);
90297590Ssbruno	if (size > 0) {
91297590Ssbruno		fd = open(firmware_file, O_CREAT | O_TRUNC | O_RDWR, 0644);
92297590Ssbruno		if (fd <0) {
93297590Ssbruno			error = errno;
94297590Ssbruno			warn("open");
95297590Ssbruno			free(firmware_buffer);
96297590Ssbruno			return (error);
97297590Ssbruno		}
98297590Ssbruno		while (written != size) {
99297590Ssbruno			if ((ret = write(fd, firmware_buffer + written, size - written)) <0) {
100297590Ssbruno				error = errno;
101297590Ssbruno				warn("write");
102297590Ssbruno				free(firmware_buffer);
103297590Ssbruno				return (error);
104297590Ssbruno			}
105297590Ssbruno			written += ret;
106297590Ssbruno		}
107297590Ssbruno		close(fd);
108297590Ssbruno	}
109297590Ssbruno	free(firmware_buffer);
110297590Ssbruno	printf("%s successfully saved as %s\n", argv[1], firmware_file);
111297590Ssbruno	return (0);
112297590Ssbruno}
113297590Ssbruno
114297590SsbrunoMPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]",
115297590Ssbruno    "Save firmware/bios into a file");
116297590Ssbruno
117297590Ssbrunostatic int
118297590Ssbrunoflash_update(int argc, char **argv)
119297590Ssbruno{
120297590Ssbruno	int error, fd;
121297590Ssbruno	unsigned char *mem = NULL;
122297590Ssbruno	struct stat st;
123297590Ssbruno	bool bios = false;
124297590Ssbruno	MPI2_FW_IMAGE_HEADER *fwheader;
125297590Ssbruno	MPI2_IOC_FACTS_REPLY *facts;
126297590Ssbruno
127297590Ssbruno	if (argc < 2) {
128297590Ssbruno		warnx("missing argument: expecting 'firmware' or bios'");
129297590Ssbruno		return (EINVAL);
130297590Ssbruno	}
131297590Ssbruno
132297590Ssbruno	if (strcmp(argv[1], "bios") == 0) {
133297590Ssbruno		bios = true;
134297590Ssbruno	} else if (strcmp(argv[1], "firmware") != 0) {
135297590Ssbruno		warnx("Invalid argument '%s', expecting 'firmware' or 'bios'",
136297590Ssbruno		    argv[1]);
137297590Ssbruno	}
138297590Ssbruno
139297590Ssbruno	if (argc > 4) {
140297590Ssbruno		warnx("update firmware: extra arguments");
141297590Ssbruno		return (EINVAL);
142297590Ssbruno	}
143297590Ssbruno
144297590Ssbruno	if (argc != 3) {
145297590Ssbruno		warnx("no firmware specified");
146297590Ssbruno		return (EINVAL);
147297590Ssbruno	}
148297590Ssbruno
149297590Ssbruno	if (stat(argv[2], &st) == -1) {
150297590Ssbruno		error = errno;
151297590Ssbruno		warn("stat");
152297590Ssbruno		return (error);
153297590Ssbruno	}
154297590Ssbruno
155297590Ssbruno	fd = open(argv[2], O_RDONLY);
156297590Ssbruno	if (fd < 0) {
157297590Ssbruno		error = errno;
158297590Ssbruno		warn("open");
159297590Ssbruno		return (error);
160297590Ssbruno	}
161297590Ssbruno
162297590Ssbruno	mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
163297590Ssbruno	if (mem == MAP_FAILED) {
164297590Ssbruno		error = errno;
165297590Ssbruno		warn("mmap");
166297590Ssbruno		close(fd);
167297590Ssbruno		return (error);
168297590Ssbruno	}
169297590Ssbruno	close(fd);
170297590Ssbruno
171297590Ssbruno	fd = mps_open(mps_unit);
172297590Ssbruno	if (fd < 0) {
173297590Ssbruno		error = errno;
174297590Ssbruno		warn("mps_open");
175297590Ssbruno		munmap(mem, st.st_size);
176297590Ssbruno		return (error);
177297590Ssbruno	}
178297590Ssbruno
179297590Ssbruno	if ((facts = mps_get_iocfacts(fd)) == NULL) {
180297590Ssbruno		warnx("could not get controller IOCFacts\n");
181297590Ssbruno		munmap(mem, st.st_size);
182297590Ssbruno		close(fd);
183297590Ssbruno		return (EINVAL);
184297590Ssbruno	}
185297590Ssbruno
186297590Ssbruno	if (bios) {
187297590Ssbruno		/* Check boot record magic number */
188297590Ssbruno		if (((mem[0x01]<<8) + mem[0x00]) != 0xaa55) {
189297590Ssbruno			warnx("Invalid bios: no boot record magic number");
190297590Ssbruno			munmap(mem, st.st_size);
191297590Ssbruno			close(fd);
192297590Ssbruno			return (1);
193297590Ssbruno		}
194297590Ssbruno		if ((st.st_size % 512) != 0) {
195297590Ssbruno			warnx("Invalid bios: size not a multiple of 512");
196297590Ssbruno			munmap(mem, st.st_size);
197297590Ssbruno			close(fd);
198297590Ssbruno			return (1);
199297590Ssbruno		}
200297590Ssbruno	} else {
201297590Ssbruno		fwheader = (MPI2_FW_IMAGE_HEADER *)mem;
202297590Ssbruno		if (fwheader->VendorID != MPI2_MFGPAGE_VENDORID_LSI) {
203297590Ssbruno			warnx("Invalid firmware:");
204297590Ssbruno			warnx("  Expected Vendor ID: %04x",
205297590Ssbruno			    MPI2_MFGPAGE_VENDORID_LSI);
206297590Ssbruno			warnx("  Image Vendor ID: %04x", fwheader->VendorID);
207297590Ssbruno			munmap(mem, st.st_size);
208297590Ssbruno			close(fd);
209297590Ssbruno			return (1);
210297590Ssbruno		}
211297590Ssbruno
212297590Ssbruno		if (fwheader->ProductID != facts->ProductID) {
213297590Ssbruno			warnx("Invalid image:");
214297590Ssbruno			warnx("  Expected Product ID: %04x", facts->ProductID);
215297590Ssbruno			warnx("  Image Product ID: %04x", fwheader->ProductID);
216297590Ssbruno			munmap(mem, st.st_size);
217297590Ssbruno			close(fd);
218297590Ssbruno			return (1);
219297590Ssbruno		}
220297590Ssbruno	}
221297590Ssbruno
222297590Ssbruno	printf("Updating %s...\n", argv[1]);
223297590Ssbruno	if (mps_firmware_send(fd, mem, st.st_size, bios) < 0) {
224297590Ssbruno		warnx("Fail to update %s", argv[1]);
225297590Ssbruno		munmap(mem, st.st_size);
226297590Ssbruno		close(fd);
227297590Ssbruno		return (1);
228297590Ssbruno	}
229297590Ssbruno
230297590Ssbruno	munmap(mem, st.st_size);
231297590Ssbruno	close(fd);
232297590Ssbruno	printf("%s successfully updated\n", argv[1]);
233297590Ssbruno	return (0);
234297590Ssbruno}
235297590Ssbruno
236297590SsbrunoMPS_COMMAND(flash, update, flash_update, "[firmware|bios] file",
237297590Ssbruno    "Update firmware/bios");
238