summaryrefslogtreecommitdiff
path: root/boot_control_copy
diff options
context:
space:
mode:
authorDavid Zeuthen <zeuthen@google.com>2015-09-01 14:58:36 -0400
committerDavid Zeuthen <zeuthen@google.com>2015-09-03 13:00:22 -0400
commitaaa6282361272d3f5d10867245e1e489bd9d19f6 (patch)
treede2346d9d65471a44c224a897d5a9205939168bd /boot_control_copy
parent32802ecf57a6047f7008f9be8ba5072b9d458e57 (diff)
downloadextras-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.mk16
-rw-r--r--boot_control_copy/boot_control_copy.c260
-rw-r--r--boot_control_copy/bootinfo.c188
-rw-r--r--boot_control_copy/bootinfo.h65
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