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