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