1 /* Copyright 2020 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 /* For strdup */
7 #define _POSIX_C_SOURCE 200809L
8
9 #include <fcntl.h>
10 #include <limits.h>
11 #include <stdbool.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 "2api.h"
21 #include "2return_codes.h"
22 #include "host_misc.h"
23 #include "flashrom.h"
24 #include "subprocess.h"
25
26 #define FLASHROM_EXEC_NAME "flashrom"
27
28 /**
29 * Helper to create a temporary file, and optionally write some data
30 * into it.
31 *
32 * @param data If data needs to be written to the file, a
33 * pointer to the buffer. Pass NULL to just
34 * create an empty temporary file.
35 * @param data_size The size of the buffer to write, if applicable.
36 * @param path_out An output pointer for the filename. Caller
37 * should free.
38 *
39 * @return VB2_SUCCESS on success, or a relevant error.
40 */
write_temp_file(const uint8_t * data,uint32_t data_size,char ** path_out)41 static vb2_error_t write_temp_file(const uint8_t *data, uint32_t data_size,
42 char **path_out)
43 {
44 int fd;
45 ssize_t write_rv;
46 vb2_error_t rv;
47 char *path;
48 mode_t umask_save;
49
50 *path_out = NULL;
51
52 path = strdup(VBOOT_TMP_DIR "/vb2_flashrom.XXXXXX");
53
54 /* Set the umask before mkstemp for security considerations. */
55 umask_save = umask(077);
56 fd = mkstemp(path);
57 umask(umask_save);
58 if (fd < 0) {
59 rv = VB2_ERROR_WRITE_FILE_OPEN;
60 goto fail;
61 }
62
63 while (data && data_size > 0) {
64 write_rv = write(fd, data, data_size);
65 if (write_rv < 0) {
66 close(fd);
67 unlink(path);
68 rv = VB2_ERROR_WRITE_FILE_DATA;
69 goto fail;
70 }
71
72 data_size -= write_rv;
73 data += write_rv;
74 }
75
76 close(fd);
77 *path_out = path;
78 return VB2_SUCCESS;
79
80 fail:
81 free(path);
82 return rv;
83 }
84
run_flashrom(const char * const argv[])85 static vb2_error_t run_flashrom(const char *const argv[])
86 {
87 int status = subprocess_run(argv, &subprocess_null, &subprocess_null,
88 &subprocess_null);
89 if (status) {
90 fprintf(stderr, "Flashrom invocation failed (exit status %d):",
91 status);
92
93 for (const char *const *argp = argv; *argp; argp++)
94 fprintf(stderr, " %s", *argp);
95
96 fprintf(stderr, "\n");
97 return VB2_ERROR_FLASHROM;
98 }
99
100 return VB2_SUCCESS;
101 }
102
flashrom_read(struct firmware_image * image,const char * region)103 vb2_error_t flashrom_read(struct firmware_image *image, const char *region)
104 {
105 char *tmpfile;
106 char region_param[PATH_MAX];
107 vb2_error_t rv;
108
109 image->data = NULL;
110 image->size = 0;
111
112 VB2_TRY(write_temp_file(NULL, 0, &tmpfile));
113
114 if (region)
115 snprintf(region_param, sizeof(region_param), "%s:%s", region,
116 tmpfile);
117
118 const char *const argv[] = {
119 FLASHROM_EXEC_NAME,
120 "-p",
121 image->programmer,
122 "-r",
123 region ? "-i" : tmpfile,
124 region ? region_param : NULL,
125 NULL,
126 };
127
128 rv = run_flashrom(argv);
129 if (rv == VB2_SUCCESS)
130 rv = vb2_read_file(tmpfile, &image->data, &image->size);
131
132 unlink(tmpfile);
133 free(tmpfile);
134 return rv;
135 }
136
flashrom_write(struct firmware_image * image,const char * region)137 vb2_error_t flashrom_write(struct firmware_image *image, const char *region)
138 {
139 char *tmpfile;
140 char region_param[PATH_MAX];
141 vb2_error_t rv;
142
143 VB2_TRY(write_temp_file(image->data, image->size, &tmpfile));
144
145 if (region)
146 snprintf(region_param, sizeof(region_param), "%s:%s", region,
147 tmpfile);
148
149 const char *const argv[] = {
150 FLASHROM_EXEC_NAME,
151 "-p",
152 image->programmer,
153 "--noverify-all",
154 "-w",
155 region ? "-i" : tmpfile,
156 region ? region_param : NULL,
157 NULL,
158 };
159
160 rv = run_flashrom(argv);
161 unlink(tmpfile);
162 free(tmpfile);
163 return rv;
164 }
165