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