You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
7.7 KiB
292 lines
7.7 KiB
/* |
|
* 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 <sys/sysmacros.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; |
|
} |
|
|
|
static bool get_dev_t_for_partition(const char *name, dev_t *out_device) |
|
{ |
|
int fd; |
|
struct stat statbuf; |
|
|
|
fd = boot_info_open_partition(name, NULL, O_RDONLY); |
|
if (fd == -1) |
|
return false; |
|
if (fstat(fd, &statbuf) != 0) { |
|
fprintf(stderr, "WARNING: Error getting information about part %s: %s\n", |
|
name, strerror(errno)); |
|
close(fd); |
|
return false; |
|
} |
|
close(fd); |
|
*out_device = statbuf.st_rdev; |
|
return true; |
|
} |
|
|
|
unsigned module_getCurrentSlot(boot_control_module_t *module) |
|
{ |
|
struct stat statbuf; |
|
dev_t system_a_dev, system_b_dev; |
|
|
|
if (stat("/system", &statbuf) != 0) { |
|
fprintf(stderr, "WARNING: Error getting information about /system: %s\n", |
|
strerror(errno)); |
|
return 0; |
|
} |
|
|
|
if (!get_dev_t_for_partition("system_a", &system_a_dev) || |
|
!get_dev_t_for_partition("system_b", &system_b_dev)) |
|
return 0; |
|
|
|
if (statbuf.st_dev == system_a_dev) { |
|
return 0; |
|
} else if (statbuf.st_dev == system_b_dev) { |
|
return 1; |
|
} else { |
|
fprintf(stderr, "WARNING: Error determining current slot " |
|
"(/system dev_t of %d:%d does not match a=%d:%d or b=%d:%d)\n", |
|
major(statbuf.st_dev), minor(statbuf.st_dev), |
|
major(system_a_dev), minor(system_a_dev), |
|
major(system_b_dev), minor(system_b_dev)); |
|
return 0; |
|
} |
|
} |
|
|
|
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, |
|
};
|
|
|