• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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