1 /* Copyright 2014 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
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 static const char usage[] = "\n"
24 "Usage: " MYNAME " %s [OPTIONS] FILE AREA:file [AREA:file ...]\n"
25 "\n"
26 "Replace the contents of specific FMAP areas. This is the complement\n"
27 "of " MYNAME " dump_fmap -x FILE AREA [AREA ...]\n"
28 "\n"
29 "Options:\n"
30 " -o OUTFILE Write the result to this file, instead of modifying\n"
31 " the input file. This is safer, since there are no\n"
32 " safeguards against doing something stupid.\n"
33 "\n"
34 "Example:\n"
35 "\n"
36 " This will clear the RO_VPD area, and scramble VBLOCK_B:\n"
37 "\n"
38 " " MYNAME " %s image.bin RO_VPD:/dev/zero VBLOCK_B:/dev/urandom\n"
39 "\n";
40
print_help(int argc,char * argv[])41 static void print_help(int argc, char *argv[])
42 {
43 printf(usage, argv[0], argv[0]);
44 }
45
46 enum {
47 OPT_HELP = 1000,
48 };
49 static const struct option long_opts[] = {
50 /* name hasarg *flag val */
51 {"help", 0, NULL, OPT_HELP},
52 {NULL, 0, NULL, 0},
53 };
54 static const char *short_opts = ":o:";
55
56
copy_to_area(const char * file,uint8_t * buf,const uint32_t len,const char * area)57 static int copy_to_area(const char *file, uint8_t *buf,
58 const uint32_t len, const char *area)
59 {
60 int retval = 0;
61
62 FILE *fp = fopen(file, "r");
63 if (!fp) {
64 ERROR("area %s: can't open %s for reading: %s\n",
65 area, file, strerror(errno));
66 return 1;
67 }
68
69 size_t n = fread(buf, 1, len, fp);
70 if (!n) {
71 if (feof(fp))
72 ERROR("area %s: unexpected EOF on %s\n", area, file);
73 if (ferror(fp))
74 ERROR("area %s: can't read from %s: %s\n",
75 area, file, strerror(errno));
76 retval = 1;
77 } else if (n < len) {
78 WARN("area %s: %s size (%zu) smaller than area size %u; "
79 "erasing remaining data to 0xff\n",
80 area, file, n, len);
81 memset(buf + n, 0xff, len - n);
82 }
83
84 if (fclose(fp)) {
85 ERROR("area %s: error closing %s: %s\n",
86 area, file, strerror(errno));
87 retval = 1;
88 }
89
90 return retval;
91 }
92
93
do_load_fmap(int argc,char * argv[])94 static int do_load_fmap(int argc, char *argv[])
95 {
96 char *outfile = NULL;
97 int errorcnt = 0;
98 int i;
99
100 opterr = 0; /* quiet, you */
101 while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
102 switch (i) {
103 case 'o':
104 outfile = optarg;
105 break;
106 case OPT_HELP:
107 print_help(argc, argv);
108 return !!errorcnt;
109 case '?':
110 if (optopt)
111 ERROR("Unrecognized option: -%c\n",
112 optopt);
113 else
114 ERROR("Unrecognized option\n");
115 errorcnt++;
116 break;
117 case ':':
118 ERROR("Missing argument to -%c\n", optopt);
119 errorcnt++;
120 break;
121 default:
122 FATAL("Unrecognized getopt output: %d\n", i);
123 }
124 }
125
126 if (errorcnt) {
127 print_help(argc, argv);
128 return 1;
129 }
130
131 if (argc - optind < 2) {
132 ERROR("You must specify an input file"
133 " and at least one AREA:file argument\n");
134 print_help(argc, argv);
135 return 1;
136 }
137
138 const char *infile = argv[optind++];
139
140 /* okay, let's do it ... */
141 if (!outfile)
142 outfile = (char *)infile;
143 else
144 if (futil_copy_file(infile, outfile) < 0)
145 exit(1);
146
147 int fd;
148 uint8_t *buf;
149 uint32_t len;
150 errorcnt |= futil_open_and_map_file(outfile, &fd, FILE_RW, &buf, &len);
151 if (errorcnt)
152 goto done;
153
154 FmapHeader *fmap = fmap_find(buf, len);
155 if (!fmap) {
156 ERROR("Can't find an FMAP in %s\n", infile);
157 errorcnt++;
158 goto done;
159 }
160
161 for (i = optind; i < argc; i++) {
162 char *a = argv[i];
163 char *f = strchr(a, ':');
164
165 if (!f || a == f || *(f+1) == '\0') {
166 ERROR("argument \"%s\" is bogus\n", a);
167 errorcnt++;
168 break;
169 }
170 *f++ = '\0';
171
172 FmapAreaHeader *ah;
173 uint8_t *area_buf = fmap_find_by_name(buf, len, fmap, a, &ah);
174 if (!area_buf) {
175 ERROR("Can't find area \"%s\" in FMAP\n", a);
176 errorcnt++;
177 break;
178 }
179
180 if (copy_to_area(f, area_buf, ah->area_size, a)) {
181 errorcnt++;
182 break;
183 }
184 }
185
186 done:
187 errorcnt |= futil_unmap_and_close_file(fd, FILE_RW, buf, len);
188 return !!errorcnt;
189 }
190
191 DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap, VBOOT_VERSION_ALL,
192 "Replace the contents of specified FMAP areas");
193