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 33 void module_init(boot_control_module_t *module) 34 { 35 } 36 37 unsigned module_getNumberSlots(boot_control_module_t *module) 38 { 39 return 2; 40 } 41 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 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 90 int module_markBootSuccessful(boot_control_module_t *module) 91 { 92 return 0; 93 } 94 95 #define COPY_BUF_SIZE 1024*1024 96 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 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 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 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 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