Home | History | Annotate | Download | only in boot_control_copy
      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