1/*
2 * Copyright 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6#include <errno.h>
7#include <fcntl.h>
8#include <getopt.h>
9#include <inttypes.h>
10#include <limits.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18#include <unistd.h>
19
20#include "fmap.h"
21#include "futility.h"
22
23
24static const char usage[] = "\n"
25	"Usage:  " MYNAME " %s [OPTIONS] FILE AREA:file [AREA:file ...]\n"
26	"\n"
27	"Replace the contents of specific FMAP areas. This is the complement\n"
28	"of " MYNAME " dump_fmap -x FILE AREA [AREA ...]\n"
29	"\n"
30	"Options:\n"
31	"  -o OUTFILE     Write the result to this file, instead of modifying\n"
32	"                   the input file. This is safer, since there are no\n"
33	"                   safeguards against doing something stupid.\n"
34	"\n"
35	"Example:\n"
36	"\n"
37	"  This will clear the RO_VPD area, and scramble VBLOCK_B:\n"
38	"\n"
39	"  " MYNAME " %s bios.bin RO_VPD:/dev/zero VBLOCK_B:/dev/urandom\n"
40	"\n";
41
42static void help_and_quit(const char *prog)
43{
44	printf(usage, prog, prog);
45}
46
47static const struct option long_opts[] = {
48	/* name    hasarg *flag  val */
49	{NULL,          0, NULL, 0},
50};
51static char *short_opts = ":o:";
52
53
54static int copy_to_area(char *file, uint8_t *buf, uint32_t len, char *area)
55{
56	FILE *fp;
57	int retval = 0;
58	int n;
59
60	fp = fopen(file, "r");
61	if (!fp) {
62		fprintf(stderr, "area %s: can't open %s for reading: %s\n",
63			area, file, strerror(errno));
64		return 1;
65	}
66
67	n = fread(buf, 1, len, fp);
68	if (n == 0) {
69		if (feof(fp))
70			fprintf(stderr, "area %s: unexpected EOF on %s\n",
71				area, file);
72		if (ferror(fp))
73			fprintf(stderr, "area %s: can't read from %s: %s\n",
74				area, file, strerror(errno));
75		retval = 1;
76	} else if (n < len) {
77		fprintf(stderr, "Warning on area %s: only read %d "
78			"(not %d) from %s\n", area, n, len, file);
79	}
80
81	if (0 != fclose(fp)) {
82		fprintf(stderr, "area %s: error closing %s: %s\n",
83			area, file, strerror(errno));
84		retval = 1;
85	}
86
87	return retval;
88}
89
90
91static int do_load_fmap(int argc, char *argv[])
92{
93	char *infile = 0;
94	char *outfile = 0;
95	uint8_t *buf;
96	uint32_t len;
97	FmapHeader *fmap;
98	FmapAreaHeader *ah;
99	int errorcnt = 0;
100	int fd, i;
101
102	opterr = 0;		/* quiet, you */
103	while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
104		switch (i) {
105		case 'o':
106			outfile = optarg;
107			break;
108		case '?':
109			if (optopt)
110				fprintf(stderr, "Unrecognized option: -%c\n",
111					optopt);
112			else
113				fprintf(stderr, "Unrecognized option\n");
114			errorcnt++;
115			break;
116		case ':':
117			fprintf(stderr, "Missing argument to -%c\n", optopt);
118			errorcnt++;
119			break;
120		default:
121			DIE;
122		}
123	}
124
125	if (errorcnt) {
126		help_and_quit(argv[0]);
127		return 1;
128	}
129
130	if (argc - optind < 2) {
131		fprintf(stderr,
132			"You must specify an input file"
133			" and at least one AREA:file argument\n");
134		help_and_quit(argv[0]);
135		return 1;
136	}
137
138	infile = argv[optind++];
139
140	/* okay, let's do it ... */
141	if (outfile)
142		futil_copy_file_or_die(infile, outfile);
143	else
144		outfile = infile;
145
146	fd = open(outfile, O_RDWR);
147	if (fd < 0) {
148		fprintf(stderr, "Can't open %s: %s\n",
149			outfile, strerror(errno));
150		return 1;
151	}
152
153	errorcnt |= futil_map_file(fd, MAP_RW, &buf, &len);
154	if (errorcnt)
155		goto done_file;
156
157	fmap = fmap_find(buf, len);
158	if (!fmap) {
159		fprintf(stderr, "Can't find an FMAP in %s\n", infile);
160		errorcnt++;
161		goto done_map;
162	}
163
164	for (i = optind; i < argc; i++) {
165		char *a = argv[i];
166		char *f = strchr(a, ':');
167
168		if (!f || a == f || *(f+1) == '\0') {
169			fprintf(stderr, "argument \"%s\" is bogus\n", a);
170			errorcnt++;
171			break;
172		}
173		*f++ = '\0';
174		uint8_t *area_buf = fmap_find_by_name(buf, len, fmap, a, &ah);
175		if (!area_buf) {
176			fprintf(stderr, "Can't find area \"%s\" in FMAP\n", a);
177			errorcnt++;
178			break;
179		}
180
181		if (0 != copy_to_area(f, area_buf, ah->area_size, a)) {
182			errorcnt++;
183			break;
184		}
185	}
186
187done_map:
188	errorcnt |= futil_unmap_file(fd, 1, buf, len);
189
190done_file:
191
192	if (0 != close(fd)) {
193		fprintf(stderr, "Error closing %s: %s\n",
194			outfile, strerror(errno));
195		errorcnt++;
196	}
197
198	return !!errorcnt;
199}
200
201DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap,
202		      VBOOT_VERSION_ALL,
203		      "Replace the contents of specified FMAP areas",
204		      help_and_quit);
205