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