/* * Copyright (C) 2019 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. */ #define LOG_TAG "bootcontrolhal" #include "BootControl.h" #include "GptUtils.h" #include #include #include namespace android { namespace hardware { namespace boot { namespace V1_0 { namespace implementation { namespace { #define BOOT_A_PATH "/dev/block/by-name/boot_a" #define BOOT_B_PATH "/dev/block/by-name/boot_b" // slot flags #define AB_ATTR_PRIORITY_SHIFT 52 #define AB_ATTR_PRIORITY_MASK (3UL << AB_ATTR_PRIORITY_SHIFT) #define AB_ATTR_ACTIVE_SHIFT 54 #define AB_ATTR_ACTIVE (1UL << AB_ATTR_ACTIVE_SHIFT) #define AB_ATTR_RETRY_COUNT_SHIFT (55) #define AB_ATTR_RETRY_COUNT_MASK (7UL << AB_ATTR_RETRY_COUNT_SHIFT) #define AB_ATTR_SUCCESSFUL (1UL << 58) #define AB_ATTR_UNBOOTABLE (1UL << 59) #define AB_ATTR_MAX_PRIORITY 3UL #define AB_ATTR_MAX_RETRY_COUNT 3UL static std::string getDevPath(uint32_t slot) { char real_path[PATH_MAX]; const char *path = slot == 0 ? BOOT_A_PATH : BOOT_B_PATH; int ret = readlink(path, real_path, sizeof real_path); if (ret < 0) { ALOGE("readlink failed for boot device %s\n", strerror(errno)); return std::string(); } std::string dp(real_path); // extract /dev/sda.. part return dp.substr(0, sizeof "/dev/block/sdX" - 1); } static bool isSlotFlagSet(uint32_t slot, uint64_t flag) { std::string dev_path = getDevPath(slot); if (dev_path.empty()) { ALOGI("Could not get device path for slot %d\n", slot); return false; } GptUtils gpt(dev_path); if (gpt.Load()) { ALOGI("failed to load gpt data\n"); return false; } gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a"); if (e == nullptr) { ALOGI("failed to get gpt entry\n"); return false; } return !!(e->attr & flag); } static int setSlotFlag(uint32_t slot, uint64_t flag) { std::string dev_path = getDevPath(slot); if (dev_path.empty()) { ALOGI("Could not get device path for slot %d\n", slot); return -1; } GptUtils gpt(dev_path); if (gpt.Load()) { ALOGI("failed to load gpt data\n"); return -1; } gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a"); if (e == nullptr) { ALOGI("failed to get gpt entry\n"); return -1; } e->attr |= flag; gpt.Sync(); return 0; } } // Methods from ::android::hardware::boot::V1_0::IBootControl follow. Return BootControl::getNumberSlots() { uint32_t slots = 0; if (access(BOOT_A_PATH, F_OK) == 0) slots++; if (access(BOOT_B_PATH, F_OK) == 0) slots++; return slots; } Return BootControl::getCurrentSlot() { char suffix[PROPERTY_VALUE_MAX]; property_get("ro.boot.slot_suffix", suffix, "_a"); return std::string(suffix) == "_b" ? 1 : 0; } Return BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) { if (getNumberSlots() == 0) { // no slots, just return true otherwise Android keeps trying _hidl_cb({true, ""}); return Void(); } int ret = setSlotFlag(getCurrentSlot(), AB_ATTR_SUCCESSFUL); ret ? _hidl_cb({false, "Failed to set successfull flag"}) : _hidl_cb({true, ""}); return Void(); } Return BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) { if (slot >= 2) { _hidl_cb({false, "Invalid slot"}); return Void(); } std::string dev_path = getDevPath(slot); if (dev_path.empty()) { _hidl_cb({false, "Could not get device path for slot"}); return Void(); } GptUtils gpt(dev_path); if (gpt.Load()) { _hidl_cb({false, "failed to load gpt data"}); return Void(); } gpt_entry *active_entry = gpt.GetPartitionEntry(slot == 0 ? "boot_a" : "boot_b"); gpt_entry *inactive_entry = gpt.GetPartitionEntry(slot == 0 ? "boot_b" : "boot_a"); if (active_entry == nullptr || inactive_entry == nullptr) { _hidl_cb({false, "failed to get entries for boot partitions"}); return Void(); } ALOGV("slot active attributes %lx\n", active_entry->attr); ALOGV("slot inactive attributes %lx\n", inactive_entry->attr); char boot_dev[PROPERTY_VALUE_MAX]; property_get("ro.boot.bootdevice", boot_dev, ""); if (boot_dev[0] == '\0') { _hidl_cb({false, "invalid ro.boot.bootdevice prop"}); return Void(); } std::string boot_lun_path = std::string("/sys/devices/platform/") + boot_dev + "/pixel/boot_lun_enabled"; int fd = open(boot_lun_path.c_str(), O_RDWR); if (fd < 0) { // Try old path for kernels < 5.4 // TODO: remove once kernel 4.19 support is deprecated std::string boot_lun_path = std::string("/sys/devices/platform/") + boot_dev + "/attributes/boot_lun_enabled"; fd = open(boot_lun_path.c_str(), O_RDWR); if (fd < 0) { _hidl_cb({false, "failed to open ufs attr boot_lun_enabled"}); return Void(); } } // update attributes for active and inactive inactive_entry->attr &= ~AB_ATTR_ACTIVE; active_entry->attr = AB_ATTR_ACTIVE | (AB_ATTR_MAX_PRIORITY << AB_ATTR_PRIORITY_SHIFT) | (AB_ATTR_MAX_RETRY_COUNT << AB_ATTR_RETRY_COUNT_SHIFT); // // bBootLunEn // 0x1 => Boot LU A = enabled, Boot LU B = disable // 0x2 => Boot LU A = disable, Boot LU B = enabled // int ret = android::base::WriteStringToFd(slot == 0 ? "1" : "2", fd); close(fd); if (ret < 0) { _hidl_cb({false, "faied to write boot_lun_enabled attribute"}); return Void(); } _hidl_cb({true, ""}); return Void(); } Return BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) { if (slot >= 2) { _hidl_cb({false, "Invalid slot"}); return Void(); } std::string dev_path = getDevPath(slot); if (dev_path.empty()) { _hidl_cb({false, "Could not get device path for slot"}); return Void(); } GptUtils gpt(dev_path); gpt.Load(); gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a"); e->attr |= AB_ATTR_UNBOOTABLE; gpt.Sync(); _hidl_cb({true, ""}); return Void(); } Return<::android::hardware::boot::V1_0::BoolResult> BootControl::isSlotBootable(uint32_t slot) { if (getNumberSlots() == 0) return BoolResult::FALSE; if (slot >= getNumberSlots()) return BoolResult::INVALID_SLOT; return isSlotFlagSet(slot, AB_ATTR_UNBOOTABLE) ? BoolResult::FALSE : BoolResult::TRUE; } Return<::android::hardware::boot::V1_0::BoolResult> BootControl::isSlotMarkedSuccessful(uint32_t slot) { if (getNumberSlots() == 0) { // just return true so that we don't we another call trying to mark it as successful // when there is no slots return BoolResult::TRUE; } if (slot >= getNumberSlots()) return BoolResult::INVALID_SLOT; return isSlotFlagSet(slot, AB_ATTR_SUCCESSFUL) ? BoolResult::TRUE : BoolResult::FALSE; } Return BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) { _hidl_cb(slot == 0 ? "_a" : slot == 1 ? "_b" : ""); return Void(); } extern "C" IBootControl* HIDL_FETCH_IBootControl(const char*) { return new BootControl(); } } // namespace implementation } // namespace V1_0 } // namespace boot } // namespace hardware } // namespace android