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