1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/sysmacros.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22
23 #include <errno.h>
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include <fs_mgr.h>
29 #include <hardware/hardware.h>
30 #include <hardware/boot_control.h>
31
32 #include "bootinfo.h"
33
module_init(boot_control_module_t * module)34 void module_init(boot_control_module_t *module)
35 {
36 }
37
module_getNumberSlots(boot_control_module_t * module)38 unsigned module_getNumberSlots(boot_control_module_t *module)
39 {
40 return 2;
41 }
42
get_dev_t_for_partition(const char * name,dev_t * out_device)43 static bool get_dev_t_for_partition(const char *name, dev_t *out_device)
44 {
45 int fd;
46 struct stat statbuf;
47
48 fd = boot_info_open_partition(name, NULL, O_RDONLY);
49 if (fd == -1)
50 return false;
51 if (fstat(fd, &statbuf) != 0) {
52 fprintf(stderr, "WARNING: Error getting information about part %s: %s\n",
53 name, strerror(errno));
54 close(fd);
55 return false;
56 }
57 close(fd);
58 *out_device = statbuf.st_rdev;
59 return true;
60 }
61
module_getCurrentSlot(boot_control_module_t * module)62 unsigned module_getCurrentSlot(boot_control_module_t *module)
63 {
64 struct stat statbuf;
65 dev_t system_a_dev, system_b_dev;
66
67 if (stat("/system", &statbuf) != 0) {
68 fprintf(stderr, "WARNING: Error getting information about /system: %s\n",
69 strerror(errno));
70 return 0;
71 }
72
73 if (!get_dev_t_for_partition("system_a", &system_a_dev) ||
74 !get_dev_t_for_partition("system_b", &system_b_dev))
75 return 0;
76
77 if (statbuf.st_dev == system_a_dev) {
78 return 0;
79 } else if (statbuf.st_dev == system_b_dev) {
80 return 1;
81 } else {
82 fprintf(stderr, "WARNING: Error determining current slot "
83 "(/system dev_t of %d:%d does not match a=%d:%d or b=%d:%d)\n",
84 major(statbuf.st_dev), minor(statbuf.st_dev),
85 major(system_a_dev), minor(system_a_dev),
86 major(system_b_dev), minor(system_b_dev));
87 return 0;
88 }
89 }
90
module_markBootSuccessful(boot_control_module_t * module)91 int module_markBootSuccessful(boot_control_module_t *module)
92 {
93 return 0;
94 }
95
96 #define COPY_BUF_SIZE (1024*1024)
97
copy_data(int src_fd,int dst_fd,size_t num_bytes)98 static bool copy_data(int src_fd, int dst_fd, size_t num_bytes)
99 {
100 char copy_buf[COPY_BUF_SIZE];
101 size_t remaining;
102
103 remaining = num_bytes;
104 while (remaining > 0) {
105 size_t num_to_read = remaining > COPY_BUF_SIZE ? COPY_BUF_SIZE : remaining;
106 ssize_t num_read;
107 do {
108 num_read = read(src_fd, copy_buf, num_to_read);
109 } while (num_read == -1 && errno == EINTR);
110 if (num_read <= 0) {
111 fprintf(stderr, "Error reading %zd bytes from source: %s\n",
112 num_to_read, strerror(errno));
113 return false;
114 }
115 size_t num_to_write = num_read;
116 while (num_to_write > 0) {
117 size_t offset = num_read - num_to_write;
118 ssize_t num_written;
119 do {
120 num_written = write(dst_fd, copy_buf + offset, num_to_write);
121 } while (num_written == -1 && errno == EINTR);
122 if (num_written <= 0) {
123 fprintf(stderr, "Error writing %zd bytes to destination: %s\n",
124 num_to_write, strerror(errno));
125 return false;
126 }
127 num_to_write -= num_written;
128 }
129 remaining -= num_read;
130 }
131
132 return true;
133 }
134
module_setActiveBootSlot(boot_control_module_t * module,unsigned slot)135 int module_setActiveBootSlot(boot_control_module_t *module, unsigned slot)
136 {
137 BrilloBootInfo info;
138 int src_fd, dst_fd;
139 uint64_t src_size, dst_size;
140 char src_name[32];
141
142 if (slot >= 2)
143 return -EINVAL;
144
145 if (!boot_info_load(&info)) {
146 fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n");
147 boot_info_reset(&info);
148 } else {
149 if (!boot_info_validate(&info)) {
150 fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n");
151 boot_info_reset(&info);
152 }
153 }
154
155 info.active_slot = slot;
156 info.slot_info[slot].bootable = true;
157 snprintf(info.bootctrl_suffix,
158 sizeof(info.bootctrl_suffix),
159 "_%c", slot + 'a');
160
161 if (!boot_info_save(&info)) {
162 fprintf(stderr, "Error saving boot-info.\n");
163 return -errno;
164 }
165
166 // Finally copy the contents of boot_X into boot.
167 snprintf(src_name, sizeof(src_name), "boot_%c", slot + 'a');
168 src_fd = boot_info_open_partition(src_name, &src_size, O_RDONLY);
169 if (src_fd == -1) {
170 fprintf(stderr, "Error opening \"%s\" partition.\n", src_name);
171 return -errno;
172 }
173
174 dst_fd = boot_info_open_partition("boot", &dst_size, O_RDWR);
175 if (dst_fd == -1) {
176 fprintf(stderr, "Error opening \"boot\" partition.\n");
177 close(src_fd);
178 return -errno;
179 }
180
181 if (src_size != dst_size) {
182 fprintf(stderr,
183 "src (%" PRIu64 " bytes) and dst (%" PRIu64 " bytes) "
184 "have different sizes.\n",
185 src_size, dst_size);
186 close(src_fd);
187 close(dst_fd);
188 return -EINVAL;
189 }
190
191 if (!copy_data(src_fd, dst_fd, src_size)) {
192 close(src_fd);
193 close(dst_fd);
194 return -errno;
195 }
196
197 if (fsync(dst_fd) != 0) {
198 fprintf(stderr, "Error calling fsync on destination: %s\n",
199 strerror(errno));
200 return -errno;
201 }
202
203 close(src_fd);
204 close(dst_fd);
205 return 0;
206 }
207
module_setSlotAsUnbootable(struct boot_control_module * module,unsigned slot)208 int module_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot)
209 {
210 BrilloBootInfo info;
211
212 if (slot >= 2)
213 return -EINVAL;
214
215 if (!boot_info_load(&info)) {
216 fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n");
217 boot_info_reset(&info);
218 } else {
219 if (!boot_info_validate(&info)) {
220 fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n");
221 boot_info_reset(&info);
222 }
223 }
224
225 info.slot_info[slot].bootable = false;
226
227 if (!boot_info_save(&info)) {
228 fprintf(stderr, "Error saving boot-info.\n");
229 return -errno;
230 }
231
232 return 0;
233 }
234
module_isSlotBootable(struct boot_control_module * module,unsigned slot)235 int module_isSlotBootable(struct boot_control_module *module, unsigned slot)
236 {
237 BrilloBootInfo info;
238
239 if (slot >= 2)
240 return -EINVAL;
241
242 if (!boot_info_load(&info)) {
243 fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n");
244 boot_info_reset(&info);
245 } else {
246 if (!boot_info_validate(&info)) {
247 fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n");
248 boot_info_reset(&info);
249 }
250 }
251
252 return info.slot_info[slot].bootable;
253 }
254
module_getSuffix(boot_control_module_t * module,unsigned slot)255 const char* module_getSuffix(boot_control_module_t *module, unsigned slot)
256 {
257 static const char* suffix[2] = {"_a", "_b"};
258 if (slot >= 2)
259 return NULL;
260 return suffix[slot];
261 }
262
263 static struct hw_module_methods_t module_methods = {
264 .open = NULL,
265 };
266
267
268 /* This boot_control HAL implementation emulates A/B by copying the
269 * contents of the boot partition of the requested slot to the boot
270 * partition. It hence works with bootloaders that are not yet aware
271 * of A/B. This code is only intended to be used for development.
272 */
273
274 boot_control_module_t HAL_MODULE_INFO_SYM = {
275 .common = {
276 .tag = HARDWARE_MODULE_TAG,
277 .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
278 .hal_api_version = HARDWARE_HAL_API_VERSION,
279 .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
280 .name = "Copy Implementation of boot_control HAL",
281 .author = "The Android Open Source Project",
282 .methods = &module_methods,
283 },
284 .init = module_init,
285 .getNumberSlots = module_getNumberSlots,
286 .getCurrentSlot = module_getCurrentSlot,
287 .markBootSuccessful = module_markBootSuccessful,
288 .setActiveBootSlot = module_setActiveBootSlot,
289 .setSlotAsUnbootable = module_setSlotAsUnbootable,
290 .isSlotBootable = module_isSlotBootable,
291 .getSuffix = module_getSuffix,
292 };
293