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