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 <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