xref: /aosp_15_r20/external/vboot_reference/cgpt/cgpt_nor.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1*8617a60dSAndroid Build Coastguard Worker /* Copyright 2015 The ChromiumOS Authors
2*8617a60dSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
3*8617a60dSAndroid Build Coastguard Worker  * found in the LICENSE file.
4*8617a60dSAndroid Build Coastguard Worker  */
5*8617a60dSAndroid Build Coastguard Worker 
6*8617a60dSAndroid Build Coastguard Worker #include <err.h>
7*8617a60dSAndroid Build Coastguard Worker #include <errno.h>
8*8617a60dSAndroid Build Coastguard Worker #include <fcntl.h>
9*8617a60dSAndroid Build Coastguard Worker #include <ftw.h>
10*8617a60dSAndroid Build Coastguard Worker #include <inttypes.h>
11*8617a60dSAndroid Build Coastguard Worker #if !defined(__FreeBSD__)
12*8617a60dSAndroid Build Coastguard Worker #include <linux/major.h>
13*8617a60dSAndroid Build Coastguard Worker #endif
14*8617a60dSAndroid Build Coastguard Worker #include <stdbool.h>
15*8617a60dSAndroid Build Coastguard Worker #include <stdarg.h>
16*8617a60dSAndroid Build Coastguard Worker #include <stdlib.h>
17*8617a60dSAndroid Build Coastguard Worker #include <stdint.h>
18*8617a60dSAndroid Build Coastguard Worker #include <stdio.h>
19*8617a60dSAndroid Build Coastguard Worker #include <string.h>
20*8617a60dSAndroid Build Coastguard Worker #include <sys/stat.h>
21*8617a60dSAndroid Build Coastguard Worker #include <sys/types.h>
22*8617a60dSAndroid Build Coastguard Worker #include <sys/wait.h>
23*8617a60dSAndroid Build Coastguard Worker #include <unistd.h>
24*8617a60dSAndroid Build Coastguard Worker 
25*8617a60dSAndroid Build Coastguard Worker #include "cgpt.h"
26*8617a60dSAndroid Build Coastguard Worker #include "cgpt_nor.h"
27*8617a60dSAndroid Build Coastguard Worker #include "subprocess.h"
28*8617a60dSAndroid Build Coastguard Worker 
29*8617a60dSAndroid Build Coastguard Worker static const char FLASHROM_PATH[] = "/usr/sbin/flashrom";
30*8617a60dSAndroid Build Coastguard Worker 
31*8617a60dSAndroid Build Coastguard Worker // Obtain the MTD size from its sysfs node.
GetMtdSize(const char * mtd_device,uint64_t * size)32*8617a60dSAndroid Build Coastguard Worker int GetMtdSize(const char *mtd_device, uint64_t *size) {
33*8617a60dSAndroid Build Coastguard Worker   mtd_device = strrchr(mtd_device, '/');
34*8617a60dSAndroid Build Coastguard Worker   if (mtd_device == NULL) {
35*8617a60dSAndroid Build Coastguard Worker     errno = EINVAL;
36*8617a60dSAndroid Build Coastguard Worker     return 1;
37*8617a60dSAndroid Build Coastguard Worker   }
38*8617a60dSAndroid Build Coastguard Worker   char *sysfs_name;
39*8617a60dSAndroid Build Coastguard Worker   if (asprintf(&sysfs_name, "/sys/class/mtd%s/size", mtd_device) == -1) {
40*8617a60dSAndroid Build Coastguard Worker     return 1;
41*8617a60dSAndroid Build Coastguard Worker   }
42*8617a60dSAndroid Build Coastguard Worker   FILE *fp = fopen(sysfs_name, "r");
43*8617a60dSAndroid Build Coastguard Worker   free(sysfs_name);
44*8617a60dSAndroid Build Coastguard Worker   if (fp == NULL) {
45*8617a60dSAndroid Build Coastguard Worker     return 1;
46*8617a60dSAndroid Build Coastguard Worker   }
47*8617a60dSAndroid Build Coastguard Worker   int ret = (fscanf(fp, "%" PRIu64 "\n", size) != 1);
48*8617a60dSAndroid Build Coastguard Worker   fclose(fp);
49*8617a60dSAndroid Build Coastguard Worker   return ret;
50*8617a60dSAndroid Build Coastguard Worker }
51*8617a60dSAndroid Build Coastguard Worker 
52*8617a60dSAndroid Build Coastguard Worker // TODO(b:184812319): Remove these functions and use subprocess_run everywhere.
ForkExecV(const char * cwd,const char * const argv[])53*8617a60dSAndroid Build Coastguard Worker int ForkExecV(const char *cwd, const char *const argv[]) {
54*8617a60dSAndroid Build Coastguard Worker   pid_t pid = fork();
55*8617a60dSAndroid Build Coastguard Worker   if (pid == -1) {
56*8617a60dSAndroid Build Coastguard Worker     return -1;
57*8617a60dSAndroid Build Coastguard Worker   }
58*8617a60dSAndroid Build Coastguard Worker   int status = -1;
59*8617a60dSAndroid Build Coastguard Worker   if (pid == 0) {
60*8617a60dSAndroid Build Coastguard Worker     if (cwd && chdir(cwd) != 0) {
61*8617a60dSAndroid Build Coastguard Worker       return -1;
62*8617a60dSAndroid Build Coastguard Worker     }
63*8617a60dSAndroid Build Coastguard Worker     execv(argv[0], (char *const *)argv);
64*8617a60dSAndroid Build Coastguard Worker     // If this is reached, execv fails.
65*8617a60dSAndroid Build Coastguard Worker     err(-1, "Cannot exec %s in %s.", argv[0], cwd);
66*8617a60dSAndroid Build Coastguard Worker   } else {
67*8617a60dSAndroid Build Coastguard Worker     if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status))
68*8617a60dSAndroid Build Coastguard Worker       return WEXITSTATUS(status);
69*8617a60dSAndroid Build Coastguard Worker   }
70*8617a60dSAndroid Build Coastguard Worker   return status;
71*8617a60dSAndroid Build Coastguard Worker }
72*8617a60dSAndroid Build Coastguard Worker 
ForkExecL(const char * cwd,const char * cmd,...)73*8617a60dSAndroid Build Coastguard Worker static int ForkExecL(const char *cwd, const char *cmd, ...) {
74*8617a60dSAndroid Build Coastguard Worker   int argc;
75*8617a60dSAndroid Build Coastguard Worker   va_list ap;
76*8617a60dSAndroid Build Coastguard Worker   va_start(ap, cmd);
77*8617a60dSAndroid Build Coastguard Worker   for (argc = 1; va_arg(ap, char *) != NULL; ++argc);
78*8617a60dSAndroid Build Coastguard Worker   va_end(ap);
79*8617a60dSAndroid Build Coastguard Worker 
80*8617a60dSAndroid Build Coastguard Worker   va_start(ap, cmd);
81*8617a60dSAndroid Build Coastguard Worker   const char **argv = calloc(argc + 1, sizeof(char *));
82*8617a60dSAndroid Build Coastguard Worker   if (argv == NULL) {
83*8617a60dSAndroid Build Coastguard Worker     errno = ENOMEM;
84*8617a60dSAndroid Build Coastguard Worker     va_end(ap);
85*8617a60dSAndroid Build Coastguard Worker     return -1;
86*8617a60dSAndroid Build Coastguard Worker   }
87*8617a60dSAndroid Build Coastguard Worker   argv[0] = cmd;
88*8617a60dSAndroid Build Coastguard Worker   int i;
89*8617a60dSAndroid Build Coastguard Worker   for (i = 1; i < argc; ++i) {
90*8617a60dSAndroid Build Coastguard Worker     argv[i] = va_arg(ap, char *);
91*8617a60dSAndroid Build Coastguard Worker   }
92*8617a60dSAndroid Build Coastguard Worker   va_end(ap);
93*8617a60dSAndroid Build Coastguard Worker 
94*8617a60dSAndroid Build Coastguard Worker   int ret = ForkExecV(cwd, argv);
95*8617a60dSAndroid Build Coastguard Worker   free(argv);
96*8617a60dSAndroid Build Coastguard Worker   return ret;
97*8617a60dSAndroid Build Coastguard Worker }
98*8617a60dSAndroid Build Coastguard Worker 
read_write(int source_fd,uint64_t size,const char * src_name,int idx)99*8617a60dSAndroid Build Coastguard Worker static int read_write(int source_fd,
100*8617a60dSAndroid Build Coastguard Worker                       uint64_t size,
101*8617a60dSAndroid Build Coastguard Worker                       const char *src_name,
102*8617a60dSAndroid Build Coastguard Worker                       int idx) {
103*8617a60dSAndroid Build Coastguard Worker   int ret = 1;
104*8617a60dSAndroid Build Coastguard Worker   const int bufsize = 4096;
105*8617a60dSAndroid Build Coastguard Worker   char *buf = malloc(bufsize);
106*8617a60dSAndroid Build Coastguard Worker   if (buf == NULL) {
107*8617a60dSAndroid Build Coastguard Worker     goto clean_exit;
108*8617a60dSAndroid Build Coastguard Worker   }
109*8617a60dSAndroid Build Coastguard Worker 
110*8617a60dSAndroid Build Coastguard Worker   ret++;
111*8617a60dSAndroid Build Coastguard Worker   char *dest;
112*8617a60dSAndroid Build Coastguard Worker   if (asprintf(&dest, "%s_%d", src_name, idx) == -1) {
113*8617a60dSAndroid Build Coastguard Worker     goto free_buf;
114*8617a60dSAndroid Build Coastguard Worker   }
115*8617a60dSAndroid Build Coastguard Worker 
116*8617a60dSAndroid Build Coastguard Worker   ret++;
117*8617a60dSAndroid Build Coastguard Worker   int dest_fd = open(dest, O_WRONLY | O_CLOEXEC | O_CREAT, 0600);
118*8617a60dSAndroid Build Coastguard Worker   if (dest_fd < 0) {
119*8617a60dSAndroid Build Coastguard Worker     goto free_dest;
120*8617a60dSAndroid Build Coastguard Worker   }
121*8617a60dSAndroid Build Coastguard Worker 
122*8617a60dSAndroid Build Coastguard Worker   ret++;
123*8617a60dSAndroid Build Coastguard Worker   uint64_t copied = 0;
124*8617a60dSAndroid Build Coastguard Worker   ssize_t nr_read;
125*8617a60dSAndroid Build Coastguard Worker   ssize_t nr_write;
126*8617a60dSAndroid Build Coastguard Worker   while (copied < size) {
127*8617a60dSAndroid Build Coastguard Worker     size_t to_read = size - copied;
128*8617a60dSAndroid Build Coastguard Worker     if (to_read > bufsize) {
129*8617a60dSAndroid Build Coastguard Worker       to_read = bufsize;
130*8617a60dSAndroid Build Coastguard Worker     }
131*8617a60dSAndroid Build Coastguard Worker     nr_read = read(source_fd, buf, to_read);
132*8617a60dSAndroid Build Coastguard Worker     if (nr_read < 0) {
133*8617a60dSAndroid Build Coastguard Worker       goto close_dest_fd;
134*8617a60dSAndroid Build Coastguard Worker     }
135*8617a60dSAndroid Build Coastguard Worker     nr_write = 0;
136*8617a60dSAndroid Build Coastguard Worker     while (nr_write < nr_read) {
137*8617a60dSAndroid Build Coastguard Worker       ssize_t s = write(dest_fd, buf + nr_write, nr_read - nr_write);
138*8617a60dSAndroid Build Coastguard Worker       if (s < 0) {
139*8617a60dSAndroid Build Coastguard Worker         goto close_dest_fd;
140*8617a60dSAndroid Build Coastguard Worker       }
141*8617a60dSAndroid Build Coastguard Worker       nr_write += s;
142*8617a60dSAndroid Build Coastguard Worker     }
143*8617a60dSAndroid Build Coastguard Worker     copied += nr_read;
144*8617a60dSAndroid Build Coastguard Worker   }
145*8617a60dSAndroid Build Coastguard Worker 
146*8617a60dSAndroid Build Coastguard Worker   ret = 0;
147*8617a60dSAndroid Build Coastguard Worker 
148*8617a60dSAndroid Build Coastguard Worker close_dest_fd:
149*8617a60dSAndroid Build Coastguard Worker   close(dest_fd);
150*8617a60dSAndroid Build Coastguard Worker free_dest:
151*8617a60dSAndroid Build Coastguard Worker   free(dest);
152*8617a60dSAndroid Build Coastguard Worker free_buf:
153*8617a60dSAndroid Build Coastguard Worker   free(buf);
154*8617a60dSAndroid Build Coastguard Worker clean_exit:
155*8617a60dSAndroid Build Coastguard Worker   return ret;
156*8617a60dSAndroid Build Coastguard Worker }
157*8617a60dSAndroid Build Coastguard Worker 
split_gpt(const char * dir_name,const char * file_name)158*8617a60dSAndroid Build Coastguard Worker static int split_gpt(const char *dir_name, const char *file_name) {
159*8617a60dSAndroid Build Coastguard Worker   int ret = 1;
160*8617a60dSAndroid Build Coastguard Worker   char *source;
161*8617a60dSAndroid Build Coastguard Worker   if (asprintf(&source, "%s/%s", dir_name, file_name) == -1) {
162*8617a60dSAndroid Build Coastguard Worker     goto clean_exit;
163*8617a60dSAndroid Build Coastguard Worker   }
164*8617a60dSAndroid Build Coastguard Worker 
165*8617a60dSAndroid Build Coastguard Worker   ret++;
166*8617a60dSAndroid Build Coastguard Worker   int fd = open(source, O_RDONLY | O_CLOEXEC);
167*8617a60dSAndroid Build Coastguard Worker   if (fd < 0) {
168*8617a60dSAndroid Build Coastguard Worker     goto free_source;
169*8617a60dSAndroid Build Coastguard Worker   }
170*8617a60dSAndroid Build Coastguard Worker 
171*8617a60dSAndroid Build Coastguard Worker   ret++;
172*8617a60dSAndroid Build Coastguard Worker   struct stat stat;
173*8617a60dSAndroid Build Coastguard Worker   if (fstat(fd, &stat) != 0 || (stat.st_size & 1) != 0) {
174*8617a60dSAndroid Build Coastguard Worker     goto close_fd;
175*8617a60dSAndroid Build Coastguard Worker   }
176*8617a60dSAndroid Build Coastguard Worker   uint64_t half_size = stat.st_size / 2;
177*8617a60dSAndroid Build Coastguard Worker 
178*8617a60dSAndroid Build Coastguard Worker   ret++;
179*8617a60dSAndroid Build Coastguard Worker   if (read_write(fd, half_size, source, 1) != 0 ||
180*8617a60dSAndroid Build Coastguard Worker       read_write(fd, half_size, source, 2) != 0) {
181*8617a60dSAndroid Build Coastguard Worker     goto close_fd;
182*8617a60dSAndroid Build Coastguard Worker   }
183*8617a60dSAndroid Build Coastguard Worker 
184*8617a60dSAndroid Build Coastguard Worker   ret = 0;
185*8617a60dSAndroid Build Coastguard Worker close_fd:
186*8617a60dSAndroid Build Coastguard Worker   close(fd);
187*8617a60dSAndroid Build Coastguard Worker free_source:
188*8617a60dSAndroid Build Coastguard Worker   free(source);
189*8617a60dSAndroid Build Coastguard Worker clean_exit:
190*8617a60dSAndroid Build Coastguard Worker   return ret;
191*8617a60dSAndroid Build Coastguard Worker }
192*8617a60dSAndroid Build Coastguard Worker 
remove_file_or_dir(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)193*8617a60dSAndroid Build Coastguard Worker static int remove_file_or_dir(const char *fpath, const struct stat *sb,
194*8617a60dSAndroid Build Coastguard Worker                               int typeflag, struct FTW *ftwbuf) {
195*8617a60dSAndroid Build Coastguard Worker   return remove(fpath);
196*8617a60dSAndroid Build Coastguard Worker }
197*8617a60dSAndroid Build Coastguard Worker 
RemoveDir(const char * dir)198*8617a60dSAndroid Build Coastguard Worker int RemoveDir(const char *dir) {
199*8617a60dSAndroid Build Coastguard Worker   return nftw(dir, remove_file_or_dir, 20, FTW_DEPTH | FTW_PHYS);
200*8617a60dSAndroid Build Coastguard Worker }
201*8617a60dSAndroid Build Coastguard Worker 
202*8617a60dSAndroid Build Coastguard Worker #define FLASHROM_RW_GPT_PRI "RW_GPT_PRIMARY:rw_gpt_1",
203*8617a60dSAndroid Build Coastguard Worker #define FLASHROM_RW_GPT_SEC "RW_GPT_SECONDARY:rw_gpt_2"
204*8617a60dSAndroid Build Coastguard Worker #define FLASHROM_RW_GPT "RW_GPT:rw_gpt"
205*8617a60dSAndroid Build Coastguard Worker 
206*8617a60dSAndroid Build Coastguard Worker // Read RW_GPT from NOR flash to "rw_gpt" in a dir.
207*8617a60dSAndroid Build Coastguard Worker // TODO(b:184812319): Replace this function with flashrom_read.
ReadNorFlash(const char * dir)208*8617a60dSAndroid Build Coastguard Worker int ReadNorFlash(const char *dir) {
209*8617a60dSAndroid Build Coastguard Worker   int ret = 0;
210*8617a60dSAndroid Build Coastguard Worker 
211*8617a60dSAndroid Build Coastguard Worker   // Read RW_GPT section from NOR flash to "rw_gpt".
212*8617a60dSAndroid Build Coastguard Worker   ret++;
213*8617a60dSAndroid Build Coastguard Worker 
214*8617a60dSAndroid Build Coastguard Worker   char *cwd = getcwd(NULL, 0);
215*8617a60dSAndroid Build Coastguard Worker   if (!cwd) {
216*8617a60dSAndroid Build Coastguard Worker     Error("Cannot get current directory.\n");
217*8617a60dSAndroid Build Coastguard Worker     return ret;
218*8617a60dSAndroid Build Coastguard Worker   }
219*8617a60dSAndroid Build Coastguard Worker   if (chdir(dir) < 0) {
220*8617a60dSAndroid Build Coastguard Worker     Error("Cannot change directory.\n");
221*8617a60dSAndroid Build Coastguard Worker     goto out_free;
222*8617a60dSAndroid Build Coastguard Worker   }
223*8617a60dSAndroid Build Coastguard Worker   const char *const argv[] = {FLASHROM_PATH, "-i", FLASHROM_RW_GPT, "-r"};
224*8617a60dSAndroid Build Coastguard Worker   // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
225*8617a60dSAndroid Build Coastguard Worker   // output.
226*8617a60dSAndroid Build Coastguard Worker   if (subprocess_run(argv, &subprocess_null, &subprocess_null, NULL) != 0) {
227*8617a60dSAndroid Build Coastguard Worker     Error("Cannot exec flashrom to read from RW_GPT section.\n");
228*8617a60dSAndroid Build Coastguard Worker   } else {
229*8617a60dSAndroid Build Coastguard Worker     ret = 0;
230*8617a60dSAndroid Build Coastguard Worker   }
231*8617a60dSAndroid Build Coastguard Worker   if (chdir(cwd) < 0) {
232*8617a60dSAndroid Build Coastguard Worker     Error("Cannot change directory back to original.\n");
233*8617a60dSAndroid Build Coastguard Worker     goto out_free;
234*8617a60dSAndroid Build Coastguard Worker   }
235*8617a60dSAndroid Build Coastguard Worker 
236*8617a60dSAndroid Build Coastguard Worker out_free:
237*8617a60dSAndroid Build Coastguard Worker   free(cwd);
238*8617a60dSAndroid Build Coastguard Worker   return ret;
239*8617a60dSAndroid Build Coastguard Worker }
240*8617a60dSAndroid Build Coastguard Worker 
FlashromWriteRegion(const char * region)241*8617a60dSAndroid Build Coastguard Worker static int FlashromWriteRegion(const char *region)
242*8617a60dSAndroid Build Coastguard Worker {
243*8617a60dSAndroid Build Coastguard Worker   const char *const argv[] = {FLASHROM_PATH, "-i", region, "-w", "--noverify-all"};
244*8617a60dSAndroid Build Coastguard Worker   // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
245*8617a60dSAndroid Build Coastguard Worker   // output.
246*8617a60dSAndroid Build Coastguard Worker   if (subprocess_run(argv, &subprocess_null, &subprocess_null, NULL) != 0) {
247*8617a60dSAndroid Build Coastguard Worker     Warning("Cannot write '%s' back with flashrom.\n", region);
248*8617a60dSAndroid Build Coastguard Worker     return 1;
249*8617a60dSAndroid Build Coastguard Worker   }
250*8617a60dSAndroid Build Coastguard Worker   return 0;
251*8617a60dSAndroid Build Coastguard Worker }
252*8617a60dSAndroid Build Coastguard Worker 
253*8617a60dSAndroid Build Coastguard Worker // Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
254*8617a60dSAndroid Build Coastguard Worker // TODO(b:184812319): Replace this function with flashrom_write.
WriteNorFlash(const char * dir)255*8617a60dSAndroid Build Coastguard Worker int WriteNorFlash(const char *dir) {
256*8617a60dSAndroid Build Coastguard Worker   int ret = 0;
257*8617a60dSAndroid Build Coastguard Worker 
258*8617a60dSAndroid Build Coastguard Worker   ret++;
259*8617a60dSAndroid Build Coastguard Worker   if (split_gpt(dir, "rw_gpt") != 0) {
260*8617a60dSAndroid Build Coastguard Worker     Error("Cannot split rw_gpt in two.\n");
261*8617a60dSAndroid Build Coastguard Worker     return ret;
262*8617a60dSAndroid Build Coastguard Worker   }
263*8617a60dSAndroid Build Coastguard Worker   ret++;
264*8617a60dSAndroid Build Coastguard Worker   int nr_fails = 0;
265*8617a60dSAndroid Build Coastguard Worker 
266*8617a60dSAndroid Build Coastguard Worker   char *cwd = getcwd(NULL, 0);
267*8617a60dSAndroid Build Coastguard Worker   if (!cwd) {
268*8617a60dSAndroid Build Coastguard Worker     Error("Cannot get current directory.\n");
269*8617a60dSAndroid Build Coastguard Worker     return ret;
270*8617a60dSAndroid Build Coastguard Worker   }
271*8617a60dSAndroid Build Coastguard Worker   if (chdir(dir) < 0) {
272*8617a60dSAndroid Build Coastguard Worker     Error("Cannot change directory.\n");
273*8617a60dSAndroid Build Coastguard Worker     goto out_free;
274*8617a60dSAndroid Build Coastguard Worker   }
275*8617a60dSAndroid Build Coastguard Worker   if (FlashromWriteRegion(FLASHROM_RW_GPT_PRI))
276*8617a60dSAndroid Build Coastguard Worker     nr_fails++;
277*8617a60dSAndroid Build Coastguard Worker   if (FlashromWriteRegion(FLASHROM_RW_GPT_SEC))
278*8617a60dSAndroid Build Coastguard Worker     nr_fails++;
279*8617a60dSAndroid Build Coastguard Worker 
280*8617a60dSAndroid Build Coastguard Worker   if (chdir(cwd) < 0) {
281*8617a60dSAndroid Build Coastguard Worker     Error("Cannot change directory back to original.\n");
282*8617a60dSAndroid Build Coastguard Worker     goto out_free;
283*8617a60dSAndroid Build Coastguard Worker   }
284*8617a60dSAndroid Build Coastguard Worker   switch (nr_fails) {
285*8617a60dSAndroid Build Coastguard Worker     case 0: ret = 0; break;
286*8617a60dSAndroid Build Coastguard Worker     case 1: Warning("It might still be okay.\n"); break;
287*8617a60dSAndroid Build Coastguard Worker     case 2: Error("Cannot write both parts back with flashrom.\n"); break;
288*8617a60dSAndroid Build Coastguard Worker   }
289*8617a60dSAndroid Build Coastguard Worker 
290*8617a60dSAndroid Build Coastguard Worker out_free:
291*8617a60dSAndroid Build Coastguard Worker   free(cwd);
292*8617a60dSAndroid Build Coastguard Worker   return ret;
293*8617a60dSAndroid Build Coastguard Worker }
294