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 
24 static 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 
help_and_quit(const char * prog)42 static void help_and_quit(const char *prog)
43 {
44 	printf(usage, prog, prog);
45 }
46 
47 static const struct option long_opts[] = {
48 	/* name    hasarg *flag  val */
49 	{NULL,          0, NULL, 0},
50 };
51 static char *short_opts = ":o:";
52 
53 
copy_to_area(char * file,uint8_t * buf,uint32_t len,char * area)54 static 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 
do_load_fmap(int argc,char * argv[])91 static 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 
187 done_map:
188 	errorcnt |= futil_unmap_file(fd, 1, buf, len);
189 
190 done_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 
201 DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap,
202 		      VBOOT_VERSION_ALL,
203 		      "Replace the contents of specified FMAP areas",
204 		      help_and_quit);
205