diff options
author | David Zeuthen <zeuthen@google.com> | 2015-09-01 14:58:36 -0400 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2015-09-03 13:00:22 -0400 |
commit | aaa6282361272d3f5d10867245e1e489bd9d19f6 (patch) | |
tree | de2346d9d65471a44c224a897d5a9205939168bd /boot_control_copy | |
parent | 32802ecf57a6047f7008f9be8ba5072b9d458e57 (diff) | |
download | extras-aaa6282361272d3f5d10867245e1e489bd9d19f6.tar.gz |
boot_control_copy: A boot_control HAL implementation that works by copying.
This boot_control HAL implementation emulates A/B by copying the
contents of the boot partition of the requested slot to the boot
partition. It hence works with bootloaders that are not yet aware of
A/B. This code is only intended to be used for development.
Change-Id: Ifcc1ff19530f9e6db1de882807a25c785e1959de
Diffstat (limited to 'boot_control_copy')
-rw-r--r-- | boot_control_copy/Android.mk | 16 | ||||
-rw-r--r-- | boot_control_copy/boot_control_copy.c | 260 | ||||
-rw-r--r-- | boot_control_copy/bootinfo.c | 188 | ||||
-rw-r--r-- | boot_control_copy/bootinfo.h | 65 |
4 files changed, 529 insertions, 0 deletions
diff --git a/boot_control_copy/Android.mk b/boot_control_copy/Android.mk new file mode 100644 index 00000000..0027c106 --- /dev/null +++ b/boot_control_copy/Android.mk @@ -0,0 +1,16 @@ +# Copyright 2015 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := boot_control_copy.c bootinfo.h bootinfo.c +LOCAL_CFLAGS := -Wall -Wno-missing-field-initializers +LOCAL_C_INCLUDES := system/core/mkbootimg bootable/recovery +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_STATIC_LIBRARIES := libfs_mgr + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE:= bootctrl.default +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) + diff --git a/boot_control_copy/boot_control_copy.c b/boot_control_copy/boot_control_copy.c new file mode 100644 index 00000000..a5deb5a6 --- /dev/null +++ b/boot_control_copy/boot_control_copy.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> + +#include <fs_mgr.h> +#include <hardware/hardware.h> +#include <hardware/boot_control.h> + +#include "bootinfo.h" + +void module_init(boot_control_module_t *module) +{ +} + +unsigned module_getNumberSlots(boot_control_module_t *module) +{ + return 2; +} + +unsigned module_getCurrentSlot(boot_control_module_t *module) +{ + BrilloBootInfo info; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + return info.active_slot; +} + +int module_markBootSuccessful(boot_control_module_t *module) +{ + return 0; +} + +#define COPY_BUF_SIZE 1024*1024 + +static bool copy_data(int src_fd, int dst_fd, size_t num_bytes) +{ + char copy_buf[COPY_BUF_SIZE]; + size_t remaining; + + remaining = num_bytes; + while (remaining > 0) { + size_t num_to_read = remaining > COPY_BUF_SIZE ? COPY_BUF_SIZE : remaining; + ssize_t num_read; + do { + num_read = read(src_fd, copy_buf, num_to_read); + } while (num_read == -1 && errno == EINTR); + if (num_read <= 0) { + fprintf(stderr, "Error reading %zd bytes from source: %s\n", + num_to_read, strerror(errno)); + return false; + } + size_t num_to_write = num_read; + while (num_to_write > 0) { + size_t offset = num_read - num_to_write; + ssize_t num_written; + do { + num_written = write(dst_fd, copy_buf + offset, num_to_write); + } while (num_written == -1 && errno == EINTR); + if (num_written <= 0) { + fprintf(stderr, "Error writing %zd bytes to destination: %s\n", + num_to_write, strerror(errno)); + return false; + } + num_to_write -= num_written; + } + remaining -= num_read; + } + + return true; +} + +int module_setActiveBootSlot(boot_control_module_t *module, unsigned slot) +{ + BrilloBootInfo info; + int src_fd, dst_fd; + uint64_t src_size, dst_size; + char src_name[32]; + + if (slot >= 2) + return -EINVAL; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + info.active_slot = slot; + info.slot_info[slot].bootable = true; + snprintf(info.bootctrl_suffix, + sizeof(info.bootctrl_suffix), + "_%c", slot + 'a'); + + if (!boot_info_save(&info)) { + fprintf(stderr, "Error saving boot-info.\n"); + return -errno; + } + + // Finally copy the contents of boot_X into boot. + snprintf(src_name, sizeof(src_name), "boot_%c", slot + 'a'); + src_fd = boot_info_open_partition(src_name, &src_size, O_RDONLY); + if (src_fd == -1) { + fprintf(stderr, "Error opening \"%s\" partition.\n", src_name); + return -errno; + } + + dst_fd = boot_info_open_partition("boot", &dst_size, O_RDWR); + if (dst_fd == -1) { + fprintf(stderr, "Error opening \"boot\" partition.\n"); + close(src_fd); + return -errno; + } + + if (src_size != dst_size) { + fprintf(stderr, + "src (%" PRIu64 " bytes) and dst (%" PRIu64 " bytes) " + "have different sizes.\n", + src_size, dst_size); + close(src_fd); + close(dst_fd); + return -EINVAL; + } + + if (!copy_data(src_fd, dst_fd, src_size)) { + close(src_fd); + close(dst_fd); + return -errno; + } + + if (fsync(dst_fd) != 0) { + fprintf(stderr, "Error calling fsync on destination: %s\n", + strerror(errno)); + return -errno; + } + + close(src_fd); + close(dst_fd); + return 0; +} + +int module_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot) +{ + BrilloBootInfo info; + + if (slot >= 2) + return -EINVAL; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + info.slot_info[slot].bootable = false; + + if (!boot_info_save(&info)) { + fprintf(stderr, "Error saving boot-info.\n"); + return -errno; + } + + return 0; +} + +int module_isSlotBootable(struct boot_control_module *module, unsigned slot) +{ + BrilloBootInfo info; + + if (slot >= 2) + return -EINVAL; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + return info.slot_info[slot].bootable; +} + +const char* module_getSuffix(boot_control_module_t *module, unsigned slot) +{ + static const char* suffix[2] = {"_a", "_b"}; + if (slot >= 2) + return NULL; + return suffix[slot]; +} + +static struct hw_module_methods_t module_methods = { + .open = NULL, +}; + + +/* This boot_control HAL implementation emulates A/B by copying the + * contents of the boot partition of the requested slot to the boot + * partition. It hence works with bootloaders that are not yet aware + * of A/B. This code is only intended to be used for development. + */ + +boot_control_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = BOOT_CONTROL_HARDWARE_MODULE_ID, + .name = "Copy Implementation of boot_control HAL", + .author = "The Android Open Source Project", + .methods = &module_methods, + }, + .init = module_init, + .getNumberSlots = module_getNumberSlots, + .getCurrentSlot = module_getCurrentSlot, + .markBootSuccessful = module_markBootSuccessful, + .setActiveBootSlot = module_setActiveBootSlot, + .setSlotAsUnbootable = module_setSlotAsUnbootable, + .isSlotBootable = module_isSlotBootable, + .getSuffix = module_getSuffix, +}; diff --git a/boot_control_copy/bootinfo.c b/boot_control_copy/bootinfo.c new file mode 100644 index 00000000..e3c24127 --- /dev/null +++ b/boot_control_copy/bootinfo.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <errno.h> +#include <fcntl.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <cutils/properties.h> + +#include <bootloader.h> +#include <fs_mgr.h> + +#include "bootinfo.h" + +// Open the appropriate fstab file and fallback to /fstab.device if +// that's what's being used. +static struct fstab *open_fstab(void) +{ + char propbuf[PROPERTY_VALUE_MAX]; + char fstab_name[PROPERTY_VALUE_MAX + 32]; + struct fstab *fstab; + + property_get("ro.hardware", propbuf, ""); + snprintf(fstab_name, sizeof(fstab_name), "/fstab.%s", propbuf); + fstab = fs_mgr_read_fstab(fstab_name); + if (fstab != NULL) + return fstab; + + fstab = fs_mgr_read_fstab("/fstab.device"); + return fstab; +} + +int boot_info_open_partition(const char *name, uint64_t *out_size, int flags) +{ + char *path; + int fd; + struct fstab *fstab; + struct fstab_rec *record; + + // We can't use fs_mgr to look up |name| because fstab doesn't list + // every slot partition (it uses the slotselect option to mask the + // suffix) and |slot| is expected to be of that form, e.g. boot_a. + // + // We can however assume that there's an entry for the /misc mount + // point and use that to get the device file for the misc + // partition. From there we'll assume that a by-name scheme is used + // so we can just replace the trailing "misc" by the given |name|, + // e.g. + // + // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc -> + // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a + // + // If needed, it's possible to relax this assumption in the future + // by trawling /sys/block looking for the appropriate sibling of + // misc and then finding an entry in /dev matching the sysfs entry. + + fstab = open_fstab(); + if (fstab == NULL) + return -1; + record = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); + if (record == NULL) { + fs_mgr_free_fstab(fstab); + return -1; + } + if (strcmp(name, "misc") == 0) { + path = strdup(record->blk_device); + } else { + size_t trimmed_len, name_len; + const char *end_slash = strrchr(record->blk_device, '/'); + if (end_slash == NULL) { + fs_mgr_free_fstab(fstab); + return -1; + } + trimmed_len = end_slash - record->blk_device + 1; + name_len = strlen(name); + path = calloc(trimmed_len + name_len + 1, 1); + strncpy(path, record->blk_device, trimmed_len); + strncpy(path + trimmed_len, name, name_len); + } + fs_mgr_free_fstab(fstab); + + fd = open(path, flags); + free(path); + + // If we successfully opened the device, get size if requested. + if (fd != -1 && out_size != NULL) { + if (ioctl(fd, BLKGETSIZE64, out_size) != 0) { + close(fd); + return -1; + } + } + + return fd; +} + +// As per struct bootloader_message which is defined in +// bootable/recovery/bootloader.h we can use the 32 bytes in the +// bootctrl_suffix field provided that they start with the active slot +// suffix terminated by NUL. It just so happens that BrilloBootInfo is +// laid out this way. +#define BOOTINFO_OFFSET offsetof(struct bootloader_message, slot_suffix) + +bool boot_info_load(BrilloBootInfo *out_info) +{ + int fd; + + memset(out_info, '\0', sizeof(BrilloBootInfo)); + + fd = boot_info_open_partition("misc", NULL, O_RDONLY); + if (fd == -1) + return false; + if (lseek(fd, BOOTINFO_OFFSET, SEEK_SET) != BOOTINFO_OFFSET) { + close(fd); + return false; + } + ssize_t num_read; + do { + num_read = read(fd, (void*) out_info, sizeof(BrilloBootInfo)); + } while (num_read == -1 && errno == EINTR); + close(fd); + if (num_read != sizeof(BrilloBootInfo)) + return false; + return true; +} + +bool boot_info_save(BrilloBootInfo *info) +{ + int fd; + + fd = boot_info_open_partition("misc", NULL, O_RDWR); + if (fd == -1) + return false; + if (lseek(fd, BOOTINFO_OFFSET, SEEK_SET) != BOOTINFO_OFFSET) { + close(fd); + return false; + } + ssize_t num_written; + do { + num_written = write(fd, (void*) info, sizeof(BrilloBootInfo)); + } while (num_written == -1 && errno == EINTR); + close(fd); + if (num_written != sizeof(BrilloBootInfo)) + return false; + return true; +} + +bool boot_info_validate(BrilloBootInfo* info) +{ + if (info->magic[0] != 'B' || + info->magic[1] != 'C' || + info->magic[2] != 'c') + return false; + if (info->active_slot >= 2) + return false; + return true; +} + +void boot_info_reset(BrilloBootInfo* info) +{ + size_t n; + memset(info, '\0', sizeof(BrilloBootInfo)); + info->magic[0] = 'B'; + info->magic[1] = 'C'; + info->magic[2] = 'c'; +} diff --git a/boot_control_copy/bootinfo.h b/boot_control_copy/bootinfo.h new file mode 100644 index 00000000..4b36b2cd --- /dev/null +++ b/boot_control_copy/bootinfo.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BOOTINFO_H_ +#define BOOTINFO_H_ + +#include <stdint.h> +#include <stdbool.h> + +typedef struct BrilloSlotInfo { + uint8_t bootable : 1; + uint8_t reserved[3]; +} BrilloSlotInfo; + +typedef struct BrilloBootInfo { + // Used by fs_mgr. Must be NUL terminated. + char bootctrl_suffix[4]; + + // Magic for identification - must be 'B', 'C', 'c' (short for + // "boot_control copy" implementation). + uint8_t magic[3]; + + // Version of BrilloBootInfo struct, must be 0 or larger. + uint8_t version; + + // Currently active slot. + uint8_t active_slot; + + // Information about each slot. + BrilloSlotInfo slot_info[2]; + + uint8_t reserved[15]; +} BrilloBootInfo; + +// Loading and saving BrillBootInfo instances. +bool boot_info_load(BrilloBootInfo *out_info); +bool boot_info_save(BrilloBootInfo *info); + +// Returns non-zero if valid. +bool boot_info_validate(BrilloBootInfo* info); +void boot_info_reset(BrilloBootInfo* info); + +// Opens partition by |name|, e.g. "misc" or "boot_a" with |flags| +// (e.g. O_RDONLY or O_RDWR) passed directly to open(2). Returns fd on +// success and -1 on error. +int boot_info_open_partition(const char *name, uint64_t *out_size, int flags); + +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +_Static_assert(sizeof(BrilloBootInfo) == 32, "BrilloBootInfo has wrong size"); +#endif + +#endif // BOOTINFO_H |