commit 2f5bf10269ca32b1c967da333d05f151938aeaf7 Author: Caleb Connolly Date: Sat Oct 9 23:16:20 2021 +0100 initial commit This is a port of the Android bootctrl HAL and Qualcomms GPT based implementation for musl/glibc based systems running mainline linux Currently it doesn't work because the gpt hdr which is read doesn't seem to have valid data. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e524d79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +build/ diff --git a/BootControl.cpp b/BootControl.cpp new file mode 100644 index 0000000..e1081c1 --- /dev/null +++ b/BootControl.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 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 + +#include "boot_control.h" +#include "BootControl.h" + +BootControl::BootControl(boot_control_module *module) : mModule(module) { +} + +// Methods from ::android::hardware::boot::V1_0::IBootControl follow. +unsigned int BootControl::getNumberSlots() { + return mModule->getNumberSlots(mModule); +} + +unsigned int BootControl::getCurrentSlot() { + return mModule->getCurrentSlot(mModule); +} + +int BootControl::markBootSuccessful() { + int ret = mModule->markBootSuccessful(mModule); + return ret; +} + +int BootControl::setActiveBootSlot(unsigned int slot) { + int ret = mModule->setActiveBootSlot(mModule, slot); + return ret; +} + +int BootControl::setSlotAsUnbootable(unsigned int slot) { + int ret = mModule->setSlotAsUnbootable(mModule, slot); + return ret; +} + +BoolResult BootControl::isSlotBootable(unsigned int slot) { + int32_t ret = mModule->isSlotBootable(mModule, slot); + if (ret < 0) { + return BoolResult::INVALID_SLOT; + } + return ret ? BoolResult::TRUE : BoolResult::FALSE; +} + +BoolResult BootControl::isSlotMarkedSuccessful(unsigned int slot) { + int32_t ret = mModule->isSlotMarkedSuccessful(mModule, slot); + if (ret < 0) { + return BoolResult::INVALID_SLOT; + } + return ret ? BoolResult::TRUE : BoolResult::FALSE; +} + +std::string BootControl::getSuffix(unsigned int slot) { + std::string ans; + const char *suffix = mModule->getSuffix(mModule, slot); + if (suffix) { + ans = std::string(suffix); + } + return ans; +} + +BootControl* BootControlInit() { + boot_control_module* module; + + // For devices that don't build a standalone libhardware bootctrl impl for recovery, + // we simulate the hw_get_module() by accessing it from the current process directly. + const boot_control_module* hw_module = &HAL_MODULE_INFO_SYM; + module = reinterpret_cast(const_cast(hw_module)); + module->init(module); + return new BootControl(module); +} + +int main (int argc, char * argv []) { + BootControl *bootctl = BootControlInit(); + printf("======= Current slot: %d\n", bootctl->getCurrentSlot()); + printf("======= isslotbootable: a = %d, b = %d\n", bootctl->isSlotBootable(0), + bootctl->isSlotBootable(1)); + printf("======= isSlotMarkedSuccessful: a = %d, b = %d\n", bootctl->isSlotMarkedSuccessful(0), + bootctl->isSlotMarkedSuccessful(1)); +} diff --git a/BootControl.h b/BootControl.h new file mode 100644 index 0000000..bc77e3c --- /dev/null +++ b/BootControl.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 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 ANDROID_HARDWARE_BOOT_V1_0_BOOTCONTROL_H +#define ANDROID_HARDWARE_BOOT_V1_0_BOOTCONTROL_H + +/** + * A result encapsulating whether a function returned true, false or + * failed due to an invalid slot number + */ +enum class BoolResult : int { + FALSE = 0, + TRUE = 1, + INVALID_SLOT = -1 /* -1 */, +}; + +struct BootControl { + BootControl(boot_control_module* module); + // Methods from ::android::hardware::boot::V1_0::IBootControl follow. + unsigned int getNumberSlots(); + unsigned int getCurrentSlot(); + int markBootSuccessful(); + int setActiveBootSlot(unsigned int slot); + int setSlotAsUnbootable(unsigned int slot); + BoolResult isSlotBootable(unsigned int slot); + BoolResult isSlotMarkedSuccessful(unsigned int slot); + std::string getSuffix(unsigned int slot); +private: + boot_control_module* mModule; +}; + + +#endif // ANDROID_HARDWARE_BOOT_V1_0_BOOTCONTROL_H diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0fc516d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(qbootctl) + +set(BootCtrl_Files BootControl.cpp + boot_control_impl_qcom.cpp + gpt-utils.cpp) + +include_directories(include) + +# add the executable +add_executable(qbootctl ${BootCtrl_Files}) + +target_link_libraries(qbootctl libz.so) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d594938 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +Qualcomm bootctl HAL for Linux + +This HAL was pulled from AOSP source code and bastardised to build and run on a musl/glibc system. This may or may not render any hardware you run it on unusable, you have been warned. + +# Dependencies + +* zlib-dev \ No newline at end of file diff --git a/boot_control.h b/boot_control.h new file mode 100644 index 0000000..b506505 --- /dev/null +++ b/boot_control.h @@ -0,0 +1,136 @@ +/* + * 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 ANDROID_INCLUDE_HARDWARE_BOOT_CONTROL_H +#define ANDROID_INCLUDE_HARDWARE_BOOT_CONTROL_H + +#define BOOT_CONTROL_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) + +/** + * The id of this module + */ +#define BOOT_CONTROL_HARDWARE_MODULE_ID "bootctrl" + +/* + * The Boot Control HAL is designed to allow for managing sets of redundant + * partitions, called slots, that can be booted from independantly. Slots + * are sets of partitions whose names differ only by a given suffix. + * They are identified here by a 0 indexed number, and associated with their + * suffix, which can be appended to the base name for any particular partition + * to find the one associated with that slot. The bootloader must pass the suffix + * of the currently active slot either through a kernel command line property at + * androidboot.slot_suffix, or the device tree at /firmware/android/slot_suffix. + * The primary use of this set up is to allow for background updates while the + * device is running, and to provide a fallback in the event that the update fails. + */ + + +/** + * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM + * and the fields of this data structure must begin with hw_module_t + * followed by module specific information. + */ +struct boot_control_module { + + /* + * (*init)() perform any initialization tasks needed for the HAL. + * This is called only once. + */ + void (*init)(struct boot_control_module *module); + + /* + * (*getNumberSlots)() returns the number of available slots. + * For instance, a system with a single set of partitions would return + * 1, a system with A/B would return 2, A/B/C -> 3... + */ + unsigned (*getNumberSlots)(struct boot_control_module *module); + + /* + * (*getCurrentSlot)() returns the value letting the system know + * whether the current slot is A or B. The meaning of A and B is + * left up to the implementer. It is assumed that if the current slot + * is A, then the block devices underlying B can be accessed directly + * without any risk of corruption. + * The returned value is always guaranteed to be strictly less than the + * value returned by getNumberSlots. Slots start at 0 and + * finish at getNumberSlots() - 1 + */ + unsigned (*getCurrentSlot)(struct boot_control_module *module); + + /* + * (*markBootSuccessful)() marks the current slot + * as having booted successfully + * + * Returns 0 on success, -errno on error. + */ + int (*markBootSuccessful)(struct boot_control_module *module); + + /* + * (*setActiveBootSlot)() marks the slot passed in parameter as + * the active boot slot (see getCurrentSlot for an explanation + * of the "slot" parameter). This overrides any previous call to + * setSlotAsUnbootable. + * Returns 0 on success, -errno on error. + */ + int (*setActiveBootSlot)(struct boot_control_module *module, unsigned slot); + + /* + * (*setSlotAsUnbootable)() marks the slot passed in parameter as + * an unbootable. This can be used while updating the contents of the slot's + * partitions, so that the system will not attempt to boot a known bad set up. + * Returns 0 on success, -errno on error. + */ + int (*setSlotAsUnbootable)(struct boot_control_module *module, unsigned slot); + + /* + * (*isSlotBootable)() returns if the slot passed in parameter is + * bootable. Note that slots can be made unbootable by both the + * bootloader and by the OS using setSlotAsUnbootable. + * Returns 1 if the slot is bootable, 0 if it's not, and -errno on + * error. + */ + int (*isSlotBootable)(struct boot_control_module *module, unsigned slot); + + /* + * (*getSuffix)() returns the string suffix used by partitions that + * correspond to the slot number passed in parameter. The returned string + * is expected to be statically allocated and not need to be freed. + * Returns NULL if slot does not match an existing slot. + */ + const char* (*getSuffix)(struct boot_control_module *module, unsigned slot); + + /* + * (*isSlotMarkedSucessful)() returns if the slot passed in parameter has + * been marked as successful using markBootSuccessful. + * Returns 1 if the slot has been marked as successful, 0 if it's + * not the case, and -errno on error. + */ + int (*isSlotMarkedSuccessful)(struct boot_control_module *module, unsigned slot); + + /** + * Returns the active slot to boot into on the next boot. If + * setActiveBootSlot() has been called, the getter function should return + * the same slot as the one provided in the last setActiveBootSlot() call. + */ + unsigned (*getActiveBootSlot)(struct boot_control_module *module); + + void* reserved[30]; +}; + +extern const struct boot_control_module HAL_MODULE_INFO_SYM; + + +#endif // ANDROID_INCLUDE_HARDWARE_BOOT_CONTROL_H diff --git a/boot_control_impl_qcom.cpp b/boot_control_impl_qcom.cpp new file mode 100644 index 0000000..011c05d --- /dev/null +++ b/boot_control_impl_qcom.cpp @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "boot_control.h" +#include "gpt-utils.h" + +#define BOOTDEV_DIR "/dev/disk/by-partlabel" +#define BOOT_IMG_PTN_NAME "boot_" +#define LUN_NAME_END_LOC 14 +#define BOOT_SLOT_PROP "slot_suffix" + +#define MAX_CMDLINE_SIZE 4096 + +#define SLOT_ACTIVE 1 +#define SLOT_INACTIVE 2 +#define UPDATE_SLOT(pentry, guid, slot_state) ({ \ + memcpy(pentry, guid, TYPE_GUID_SIZE); \ + if (slot_state == SLOT_ACTIVE)\ + *(pentry + AB_FLAG_OFFSET) = AB_SLOT_ACTIVE_VAL; \ + else if (slot_state == SLOT_INACTIVE) \ + *(pentry + AB_FLAG_OFFSET) = (*(pentry + AB_FLAG_OFFSET)& \ + ~AB_PARTITION_ATTR_SLOT_ACTIVE); \ + }) + +using namespace std; +const char *slot_suffix_arr[] = { + AB_SLOT_A_SUFFIX, + AB_SLOT_B_SUFFIX, + NULL}; + +enum part_attr_type { + ATTR_SLOT_ACTIVE = 0, + ATTR_BOOT_SUCCESSFUL, + ATTR_UNBOOTABLE, +}; + +void get_kernel_cmdline_arg(const char * arg, const char* buf, const char* def) +{ + // int fd = open("/proc/cmdline\n", O_RDONLY); + // char buf[MAX_CMDLINE_SIZE]; + // int rc = read(fd, &buf, MAX_CMDLINE_SIZE); + // close(fd); + // std::string cmdline(buf); + // std::regex rgx(std::string(arg).append("=.+")) + printf("%s\n", __func__); + buf = def; +} + +void boot_control_init(struct boot_control_module *module) +{ + printf("%s\n", __func__); + if (!module) { + fprintf(stderr, "Invalid argument passed to %s\n", __func__); + return; + } + return; +} + +//Get the value of one of the attribute fields for a partition. +static int get_partition_attribute(char *partname, + enum part_attr_type part_attr) +{ + printf("%s\n", __func__); + struct gpt_disk *disk = NULL; + uint8_t *pentry = NULL; + int retval = -1; + uint8_t *attr = NULL; + if (!partname) + goto error; + disk = gpt_disk_alloc(); + if (!disk) { + fprintf(stderr, "%s: Failed to alloc disk struct\n", __func__); + goto error; + } + printf("gpt_disk_get_disk_info\n"); + if (gpt_disk_get_disk_info(partname, disk)) { + fprintf(stderr, "%s: Failed to get disk info\n", __func__); + goto error; + } + pentry = gpt_disk_get_pentry(disk, partname, PRIMARY_GPT); + + if (!pentry) { + fprintf(stderr, "%s: pentry does not exist in disk struct\n", + __func__); + goto error; + } + attr = pentry + AB_FLAG_OFFSET; + printf("gpt_disk_get_pentry: 0x%x", *attr); + if (part_attr == ATTR_SLOT_ACTIVE) + retval = !!(*attr & AB_PARTITION_ATTR_SLOT_ACTIVE); + else if (part_attr == ATTR_BOOT_SUCCESSFUL) + retval = !!(*attr & AB_PARTITION_ATTR_BOOT_SUCCESSFUL); + else if (part_attr == ATTR_UNBOOTABLE) + retval = !!(*attr & AB_PARTITION_ATTR_UNBOOTABLE); + else + retval = -1; + gpt_disk_free(disk); + return retval; +error: + if (disk) + gpt_disk_free(disk); + return retval; +} + +//Set a particular attribute for all the partitions in a +//slot +static int update_slot_attribute(const char *slot, + enum part_attr_type ab_attr) +{ + unsigned int i = 0; + char buf[PATH_MAX]; + struct stat st; + struct gpt_disk *disk = NULL; + uint8_t *pentry = NULL; + uint8_t *pentry_bak = NULL; + int rc = -1; + uint8_t *attr = NULL; + uint8_t *attr_bak = NULL; + char partName[MAX_GPT_NAME_SIZE + 1] = {0}; + const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; + int slot_name_valid = 0; + printf("%s\n", __func__); + if (!slot) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + goto error; + } + for (i = 0; slot_suffix_arr[i] != NULL; i++) + { + if (!strncmp(slot, slot_suffix_arr[i], + strlen(slot_suffix_arr[i]))) + slot_name_valid = 1; + } + if (!slot_name_valid) { + fprintf(stderr, "%s: Invalid slot name\n", __func__); + goto error; + } + for (i=0; i < ARRAY_SIZE(ptn_list); i++) { + memset(buf, '\0', sizeof(buf)); + //Check if A/B versions of this ptn exist + snprintf(buf, sizeof(buf) - 1, + "%s/%s%s\n", + BOOT_DEV_DIR, + ptn_list[i], + AB_SLOT_A_SUFFIX + ); + if (stat(buf, &st)) { + //partition does not have _a version + continue; + } + memset(buf, '\0', sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, + "%s/%s%s\n", + BOOT_DEV_DIR, + ptn_list[i], + AB_SLOT_B_SUFFIX + ); + if (stat(buf, &st)) { + //partition does not have _a version + continue; + } + memset(partName, '\0', sizeof(partName)); + snprintf(partName, + sizeof(partName) - 1, + "%s%s\n", + ptn_list[i], + slot); + disk = gpt_disk_alloc(); + if (!disk) { + fprintf(stderr, "%s: Failed to alloc disk struct\n", + __func__); + goto error; + } + rc = gpt_disk_get_disk_info(partName, disk); + if (rc != 0) { + fprintf(stderr, "%s: Failed to get disk info for %s\n", + __func__, + partName); + goto error; + } + pentry = gpt_disk_get_pentry(disk, partName, PRIMARY_GPT); + pentry_bak = gpt_disk_get_pentry(disk, partName, SECONDARY_GPT); + if (!pentry || !pentry_bak) { + fprintf(stderr, "%s: Failed to get pentry/pentry_bak for %s\n", + __func__, + partName); + goto error; + } + attr = pentry + AB_FLAG_OFFSET; + attr_bak = pentry_bak + AB_FLAG_OFFSET; + if (ab_attr == ATTR_BOOT_SUCCESSFUL) { + *attr = (*attr) | AB_PARTITION_ATTR_BOOT_SUCCESSFUL; + *attr_bak = (*attr_bak) | + AB_PARTITION_ATTR_BOOT_SUCCESSFUL; + } else if (ab_attr == ATTR_UNBOOTABLE) { + *attr = (*attr) | AB_PARTITION_ATTR_UNBOOTABLE; + *attr_bak = (*attr_bak) | AB_PARTITION_ATTR_UNBOOTABLE; + } else if (ab_attr == ATTR_SLOT_ACTIVE) { + *attr = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE; + *attr_bak = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE; + } else { + fprintf(stderr, "%s: Unrecognized attr\n", __func__); + goto error; + } + if (gpt_disk_update_crc(disk)) { + fprintf(stderr, "%s: Failed to update crc for %s\n", + __func__, + partName); + goto error; + } + if (gpt_disk_commit(disk)) { + fprintf(stderr, "%s: Failed to write back entry for %s\n", + __func__, + partName); + goto error; + } + gpt_disk_free(disk); + disk = NULL; + } + return 0; +error: + if (disk) + gpt_disk_free(disk); + return -1; +} + +unsigned get_number_slots(struct boot_control_module *module) +{ + struct dirent *de = NULL; + DIR *dir_bootdev = NULL; + unsigned slot_count = 0; + if (!module) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + goto error; + } + dir_bootdev = opendir(BOOTDEV_DIR); + if (!dir_bootdev) { + fprintf(stderr, "%s: Failed to open bootdev dir (%s)\n", + __func__, + strerror(errno)); + goto error; + } + while ((de = readdir(dir_bootdev))) { + if (de->d_name[0] == '.') + continue; + static_assert(AB_SLOT_A_SUFFIX[0] == '_', "Breaking change to slot A suffix"); + static_assert(AB_SLOT_B_SUFFIX[0] == '_', "Breaking change to slot B suffix"); + if (!strncmp(de->d_name, BOOT_IMG_PTN_NAME, + strlen(BOOT_IMG_PTN_NAME)) && !!strncmp(de->d_name, "boot_aging\n", strlen("boot_aging"))) { + slot_count++; + } + } + closedir(dir_bootdev); + return slot_count; +error: + if (dir_bootdev) + closedir(dir_bootdev); + return 0; +} + +static unsigned int get_current_slot(struct boot_control_module *module) +{ + uint32_t num_slots = 0; + char bootSlotProp[MAX_CMDLINE_SIZE] = {'\0'}; + unsigned i = 0; + if (!module) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + goto error; + } + num_slots = get_number_slots(module); + if (num_slots <= 1) { + //Slot 0 is the only slot around. + return 0; + } + get_kernel_cmdline_arg(BOOT_SLOT_PROP, bootSlotProp, "_a"); + if (!strncmp(bootSlotProp, "N/A\n", strlen("N/A"))) { + fprintf(stderr, "%s: Unable to read boot slot property\n", + __func__); + goto error; + } + //Iterate through a list of partitons named as boot+suffix + //and see which one is currently active. + for (i = 0; slot_suffix_arr[i] != NULL ; i++) { + if (!strncmp(bootSlotProp, + slot_suffix_arr[i], + strlen(slot_suffix_arr[i]))) { + printf("%s current_slot = %d\n", __func__, i); + return i; + } + } +error: + //The HAL spec requires that we return a number between + //0 to num_slots - 1. Since something went wrong here we + //are just going to return the default slot. + return 0; +} + +static int boot_control_check_slot_sanity(struct boot_control_module *module, + unsigned slot) +{ + printf("%s\n", __func__); + if (!module) + return -1; + uint32_t num_slots = get_number_slots(module); + if ((num_slots < 1) || (slot > num_slots - 1)) { + fprintf(stderr, "Invalid slot number"); + return -1; + } + return 0; + +} + +int mark_boot_successful(struct boot_control_module *module) +{ + printf("%s\n", __func__); + unsigned cur_slot = 0; + if (!module) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + goto error; + } + cur_slot = get_current_slot(module); + if (update_slot_attribute(slot_suffix_arr[cur_slot], + ATTR_BOOT_SUCCESSFUL)) { + goto error; + } + return 0; +error: + fprintf(stderr, "%s: Failed to mark boot successful\n", __func__); + return -1; +} + +const char *get_suffix(struct boot_control_module *module, unsigned slot) +{ + printf("%s\n", __func__); + if (boot_control_check_slot_sanity(module, slot) != 0) + return NULL; + else + return slot_suffix_arr[slot]; +} + + +//Return a gpt disk structure representing the disk that holds +//partition. +static struct gpt_disk* boot_ctl_get_disk_info(char *partition) +{ + printf("%s\n", __func__); + struct gpt_disk *disk = NULL; + if (!partition) + return NULL; + disk = gpt_disk_alloc(); + if (!disk) { + fprintf(stderr, "%s: Failed to alloc disk\n", + __func__); + goto error; + } + if (gpt_disk_get_disk_info(partition, disk)) { + fprintf(stderr, "failed to get disk info for %s\n", + partition); + goto error; + } + return disk; +error: + if (disk) + gpt_disk_free(disk); + return NULL; +} + +//The argument here is a vector of partition names(including the slot suffix) +//that lie on a single disk +static int boot_ctl_set_active_slot_for_partitions(vector part_list, + unsigned slot) +{ + printf("%s\n", __func__); + char buf[PATH_MAX] = {0}; + struct gpt_disk *disk = NULL; + char slotA[MAX_GPT_NAME_SIZE + 1] = {0}; + char slotB[MAX_GPT_NAME_SIZE + 1] = {0}; + char active_guid[TYPE_GUID_SIZE + 1] = {0}; + char inactive_guid[TYPE_GUID_SIZE + 1] = {0}; + //Pointer to the partition entry of current 'A' partition + uint8_t *pentryA = NULL; + uint8_t *pentryA_bak = NULL; + //Pointer to partition entry of current 'B' partition + uint8_t *pentryB = NULL; + uint8_t *pentryB_bak = NULL; + struct stat st; + vector::iterator partition_iterator; + + for (partition_iterator = part_list.begin(); + partition_iterator != part_list.end(); + partition_iterator++) { + //Chop off the slot suffix from the partition name to + //make the string easier to work with. + string prefix = *partition_iterator; + if (prefix.size() < (strlen(AB_SLOT_A_SUFFIX) + 1)) { + fprintf(stderr, "Invalid partition name: %s\n", prefix.c_str()); + goto error; + } + prefix.resize(prefix.size() - strlen(AB_SLOT_A_SUFFIX)); + //Check if A/B versions of this ptn exist + snprintf(buf, sizeof(buf) - 1, "%s/%s%s\n", BOOT_DEV_DIR, + prefix.c_str(), + AB_SLOT_A_SUFFIX); + if (stat(buf, &st)) + continue; + memset(buf, '\0', sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%s/%s%s\n", BOOT_DEV_DIR, + prefix.c_str(), + AB_SLOT_B_SUFFIX); + if (stat(buf, &st)) + continue; + memset(slotA, 0, sizeof(slotA)); + memset(slotB, 0, sizeof(slotA)); + snprintf(slotA, sizeof(slotA) - 1, "%s%s\n", prefix.c_str(), + AB_SLOT_A_SUFFIX); + snprintf(slotB, sizeof(slotB) - 1,"%s%s\n", prefix.c_str(), + AB_SLOT_B_SUFFIX); + //Get the disk containing the partitions that were passed in. + //All partitions passed in must lie on the same disk. + if (!disk) { + disk = boot_ctl_get_disk_info(slotA); + if (!disk) + goto error; + } + //Get partition entry for slot A & B from the primary + //and backup tables. + pentryA = gpt_disk_get_pentry(disk, slotA, PRIMARY_GPT); + pentryA_bak = gpt_disk_get_pentry(disk, slotA, SECONDARY_GPT); + pentryB = gpt_disk_get_pentry(disk, slotB, PRIMARY_GPT); + pentryB_bak = gpt_disk_get_pentry(disk, slotB, SECONDARY_GPT); + if ( !pentryA || !pentryA_bak || !pentryB || !pentryB_bak) { + //None of these should be NULL since we have already + //checked for A & B versions earlier. + fprintf(stderr, "Slot pentries for %s not found.\n", + prefix.c_str()); + goto error; + } + memset(active_guid, '\0', sizeof(active_guid)); + memset(inactive_guid, '\0', sizeof(inactive_guid)); + if (get_partition_attribute(slotA, ATTR_SLOT_ACTIVE) == 1) { + //A is the current active slot + memcpy((void*)active_guid, (const void*)pentryA, + TYPE_GUID_SIZE); + memcpy((void*)inactive_guid,(const void*)pentryB, + TYPE_GUID_SIZE); + } else if (get_partition_attribute(slotB, + ATTR_SLOT_ACTIVE) == 1) { + //B is the current active slot + memcpy((void*)active_guid, (const void*)pentryB, + TYPE_GUID_SIZE); + memcpy((void*)inactive_guid, (const void*)pentryA, + TYPE_GUID_SIZE); + } else { + fprintf(stderr, "Both A & B are inactive..Aborting"); + goto error; + } + if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX, + strlen(AB_SLOT_A_SUFFIX))){ + //Mark A as active in primary table + UPDATE_SLOT(pentryA, active_guid, SLOT_ACTIVE); + //Mark A as active in backup table + UPDATE_SLOT(pentryA_bak, active_guid, SLOT_ACTIVE); + //Mark B as inactive in primary table + UPDATE_SLOT(pentryB, inactive_guid, SLOT_INACTIVE); + //Mark B as inactive in backup table + UPDATE_SLOT(pentryB_bak, inactive_guid, SLOT_INACTIVE); + } else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX, + strlen(AB_SLOT_B_SUFFIX))){ + //Mark B as active in primary table + UPDATE_SLOT(pentryB, active_guid, SLOT_ACTIVE); + //Mark B as active in backup table + UPDATE_SLOT(pentryB_bak, active_guid, SLOT_ACTIVE); + //Mark A as inavtive in primary table + UPDATE_SLOT(pentryA, inactive_guid, SLOT_INACTIVE); + //Mark A as inactive in backup table + UPDATE_SLOT(pentryA_bak, inactive_guid, SLOT_INACTIVE); + } else { + //Something has gone terribly terribly wrong + fprintf(stderr, "%s: Unknown slot suffix!\n", __func__); + goto error; + } + if (disk) { + if (gpt_disk_update_crc(disk) != 0) { + fprintf(stderr, "%s: Failed to update gpt_disk crc\n", + __func__); + goto error; + } + } + } + //write updated content to disk + if (disk) { + if (gpt_disk_commit(disk)) { + fprintf(stderr, "Failed to commit disk entry"); + goto error; + } + gpt_disk_free(disk); + } + return 0; + +error: + if (disk) + gpt_disk_free(disk); + return -1; +} + +unsigned get_active_boot_slot(struct boot_control_module *module) +{ + printf("%s\n", __func__); + if (!module) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + // The HAL spec requires that we return a number between + // 0 to num_slots - 1. Since something went wrong here we + // are just going to return the default slot. + return 0; + } + + uint32_t num_slots = get_number_slots(module); + if (num_slots <= 1) { + //Slot 0 is the only slot around. + return 0; + } + + for (uint32_t i = 0; i < num_slots; i++) { + char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; + snprintf(bootPartition, sizeof(bootPartition) - 1, "boot%s", + slot_suffix_arr[i]); + if (get_partition_attribute(bootPartition, ATTR_SLOT_ACTIVE) == 1) { + return i; + } + } + + fprintf(stderr, "%s: Failed to find the active boot slot\n", __func__); + return 0; +} + +int set_active_boot_slot(struct boot_control_module *module, unsigned slot) +{ + printf("%s\n", __func__); + map> ptn_map; + vector ptn_vec; + const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; + uint32_t i; + int rc = -1; + int is_ufs = gpt_utils_is_ufs_device(); + map>::iterator map_iter; + + if (boot_control_check_slot_sanity(module, slot)) { + fprintf(stderr, "%s: Bad arguments\n", __func__); + goto error; + } + //The partition list just contains prefixes(without the _a/_b) of the + //partitions that support A/B. In order to get the layout we need the + //actual names. To do this we append the slot suffix to every member + //in the list. + for (i = 0; i < ARRAY_SIZE(ptn_list); i++) { + //XBL is handled differrently for ufs devices so ignore it + if (is_ufs && !strncmp(ptn_list[i], PTN_XBL, strlen(PTN_XBL))) + continue; + //The partition list will be the list of _a partitions + string cur_ptn = ptn_list[i]; + cur_ptn.append(AB_SLOT_A_SUFFIX); + ptn_vec.push_back(cur_ptn); + + } + //The partition map gives us info in the following format: + // [path_to_block_device_1]--> + // [path_to_block_device_2]--> + // ... + // ... + // eg: + // [/dev/block/sdb]---> + if (gpt_utils_get_partition_map(ptn_vec, ptn_map)) { + fprintf(stderr, "%s: Failed to get partition map\n", + __func__); + goto error; + } + for (map_iter = ptn_map.begin(); map_iter != ptn_map.end(); map_iter++){ + if (map_iter->second.size() < 1) + continue; + if (boot_ctl_set_active_slot_for_partitions(map_iter->second, slot)) { + fprintf(stderr, "%s: Failed to set active slot for partitions \n", __func__);; + goto error; + } + } + if (is_ufs) { + if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX, + strlen(AB_SLOT_A_SUFFIX))){ + //Set xbl_a as the boot lun + rc = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT); + } else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX, + strlen(AB_SLOT_B_SUFFIX))){ + //Set xbl_b as the boot lun + rc = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT); + } else { + //Something has gone terribly terribly wrong + fprintf(stderr, "%s: Unknown slot suffix!\n", __func__); + goto error; + } + if (rc) { + fprintf(stderr, "%s: Failed to switch xbl boot partition\n", + __func__); + goto error; + } + } + return 0; +error: + return -1; +} + +int set_slot_as_unbootable(struct boot_control_module *module, unsigned slot) +{ + printf("%s\n", __func__); + if (boot_control_check_slot_sanity(module, slot) != 0) { + fprintf(stderr, "%s: Argument check failed\n", __func__); + goto error; + } + if (update_slot_attribute(slot_suffix_arr[slot], + ATTR_UNBOOTABLE)) { + goto error; + } + return 0; +error: + fprintf(stderr, "%s: Failed to mark slot unbootable\n", __func__); + return -1; +} + +int is_slot_bootable(struct boot_control_module *module, unsigned slot) +{ + printf("%s\n", __func__); + int attr = 0; + char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; + + if (boot_control_check_slot_sanity(module, slot) != 0) { + fprintf(stderr, "%s: Argument check failed\n", __func__); + goto error; + } + snprintf(bootPartition, + sizeof(bootPartition) - 1, "boot%s", + slot_suffix_arr[slot]); + attr = get_partition_attribute(bootPartition, ATTR_UNBOOTABLE); + if (attr >= 0) + return !attr; +error: + return -1; +} + +int is_slot_marked_successful(struct boot_control_module *module, unsigned slot) +{ + printf("%s\n", __func__); + int attr = 0; + char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; + + if (boot_control_check_slot_sanity(module, slot) != 0) { + fprintf(stderr, "%s: Argument check failed\n", __func__); + goto error; + } + snprintf(bootPartition, + sizeof(bootPartition) - 1, + "boot%s", slot_suffix_arr[slot]); + attr = get_partition_attribute(bootPartition, ATTR_BOOT_SUCCESSFUL); + if (attr >= 0) + return attr; +error: + return -1; +} + +const struct boot_control_module HAL_MODULE_INFO_SYM = { + .init = boot_control_init, + .getNumberSlots = get_number_slots, + .getCurrentSlot = get_current_slot, + .markBootSuccessful = mark_boot_successful, + .setActiveBootSlot = set_active_boot_slot, + .setSlotAsUnbootable = set_slot_as_unbootable, + .isSlotBootable = is_slot_bootable, + .getSuffix = get_suffix, + .isSlotMarkedSuccessful = is_slot_marked_successful, + .getActiveBootSlot = get_active_boot_slot, +}; diff --git a/gpt-utils.cpp b/gpt-utils.cpp new file mode 100644 index 0000000..643a5ef --- /dev/null +++ b/gpt-utils.cpp @@ -0,0 +1,1557 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define _LARGEFILE64_SOURCE /* enable lseek64() */ + +/****************************************************************************** + * INCLUDE SECTION + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpt-utils.h" + + +/****************************************************************************** + * DEFINE SECTION + ******************************************************************************/ +#define BLK_DEV_FILE "/dev/disk/mmcblk0" +/* list the names of the backed-up partitions to be swapped */ +/* extension used for the backup partitions - tzbak, abootbak, etc. */ +#define BAK_PTN_NAME_EXT "bak" +#define XBL_PRIMARY "/dev/disk/by-partlabel/xbl_a" // FIXME +#define XBL_BACKUP "/dev/disk/by-partlabel/xblbak" +#define XBL_AB_PRIMARY "/dev/disk/by-partlabel/xbl_a" +#define XBL_AB_SECONDARY "/dev/disk/by-partlabel/xbl_b" +/* GPT defines */ +#define MAX_LUNS 26 +//Size of the buffer that needs to be passed to the UFS ioctl +#define UFS_ATTR_DATA_SIZE 32 +//This will allow us to get the root lun path from the path to the partition. +//i.e: from /dev/disk/sdaXXX get /dev/disk/sda. The assumption here is that +//the boot critical luns lie between sda to sdz which is acceptable because +//only user added external disks,etc would lie beyond that limit which do not +//contain partitions that interest us here. +#define PATH_TRUNCATE_LOC (sizeof("/dev/disk/sda") - 1) + +//From /dev/disk/sda get just sda +#define LUN_NAME_START_LOC (sizeof("/dev/disk/") - 1) +#define BOOT_LUN_A_ID 1 +#define BOOT_LUN_B_ID 2 +/****************************************************************************** + * MACROS + ******************************************************************************/ + + +#define GET_4_BYTES(ptr) ((uint32_t) *((uint8_t *)(ptr)) | \ + ((uint32_t) *((uint8_t *)(ptr) + 1) << 8) | \ + ((uint32_t) *((uint8_t *)(ptr) + 2) << 16) | \ + ((uint32_t) *((uint8_t *)(ptr) + 3) << 24)) + +#define GET_8_BYTES(ptr) ((uint64_t) *((uint8_t *)(ptr)) | \ + ((uint64_t) *((uint8_t *)(ptr) + 1) << 8) | \ + ((uint64_t) *((uint8_t *)(ptr) + 2) << 16) | \ + ((uint64_t) *((uint8_t *)(ptr) + 3) << 24) | \ + ((uint64_t) *((uint8_t *)(ptr) + 4) << 32) | \ + ((uint64_t) *((uint8_t *)(ptr) + 5) << 40) | \ + ((uint64_t) *((uint8_t *)(ptr) + 6) << 48) | \ + ((uint64_t) *((uint8_t *)(ptr) + 7) << 56)) + +#define PUT_4_BYTES(ptr, y) *((uint8_t *)(ptr)) = (y) & 0xff; \ + *((uint8_t *)(ptr) + 1) = ((y) >> 8) & 0xff; \ + *((uint8_t *)(ptr) + 2) = ((y) >> 16) & 0xff; \ + *((uint8_t *)(ptr) + 3) = ((y) >> 24) & 0xff; + +/****************************************************************************** + * TYPES + ******************************************************************************/ +using namespace std; +enum gpt_state { + GPT_OK = 0, + GPT_BAD_SIGNATURE, + GPT_BAD_CRC +}; +//List of LUN's containing boot critical images. +//Required in the case of UFS devices +struct update_data { + char lun_list[MAX_LUNS][PATH_MAX]; + uint32_t num_valid_entries; +}; + +/****************************************************************************** + * FUNCTIONS + ******************************************************************************/ +/** + * ========================================================================== + * + * \brief Read/Write len bytes from/to block dev + * + * \param [in] fd block dev file descriptor (returned from open) + * \param [in] rw RW flag: 0 - read, != 0; - write + * \param [in] offset block dev offset [bytes] - RW start position + * \param [in] buf Pointer to the buffer containing the data + * \param [in] len RW size in bytes. Buf must be at least that big + * + * \return 0 on success + * + * ========================================================================== + */ +static int blk_rw(int fd, int rw, uint64_t offset, uint8_t *buf, unsigned len) +{ + int r; + + if (lseek64(fd, offset, SEEK_SET) < 0) { + fprintf(stderr, "block dev lseek64 %" PRIu64 " failed: %s\n", offset, + strerror(errno)); + return -1; + } + + if (rw) + r = write(fd, buf, len); + else + r = read(fd, buf, len); + + if (r < 0) { + fprintf(stderr, "block dev %s failed: %s\n", rw ? "write" : "read\n", + strerror(errno)); + } else { + if (rw) { + r = fsync(fd); + if (r < 0) + fprintf(stderr, "fsync failed: %s\n", strerror(errno)); + } else { + r = 0; + } + } + + return r; +} + + + +/** + * ========================================================================== + * + * \brief Search within GPT for partition entry with the given name + * or it's backup twin (name-bak). + * + * \param [in] ptn_name Partition name to seek + * \param [in] pentries_start Partition entries array start pointer + * \param [in] pentries_end Partition entries array end pointer + * \param [in] pentry_size Single partition entry size [bytes] + * + * \return First partition entry pointer that matches the name or NULL + * + * ========================================================================== + */ +static uint8_t *gpt_pentry_seek(const char *ptn_name, + const uint8_t *pentries_start, + const uint8_t *pentries_end, + uint32_t pentry_size) +{ + char *pentry_name; + unsigned len = strlen(ptn_name); + + for (pentry_name = (char *) (pentries_start + PARTITION_NAME_OFFSET); + pentry_name < (char *) pentries_end; pentry_name += pentry_size) { + char name8[MAX_GPT_NAME_SIZE / 2]; + unsigned i; + + /* Partition names in GPT are UTF-16 - ignoring UTF-16 2nd byte */ + for (i = 0; i < sizeof(name8); i++) + name8[i] = pentry_name[i * 2]; + if (!strncmp(ptn_name, name8, len)) + if (name8[len] == 0; || !strcmp(&name8[len], BAK_PTN_NAME_EXT)) + return (uint8_t *) (pentry_name - PARTITION_NAME_OFFSET); + } + + return NULL; +} + + + +/** + * ========================================================================== + * + * \brief Swaps boot chain in GPT partition entries array + * + * \param [in] pentries_start Partition entries array start + * \param [in] pentries_end Partition entries array end + * \param [in] pentry_size Single partition entry size + * + * \return 0 on success, 1 if no backup partitions found + * + * ========================================================================== + */ +static int gpt_boot_chain_swap(const uint8_t *pentries_start, + const uint8_t *pentries_end, + uint32_t pentry_size) +{ + const char ptn_swap_list[][MAX_GPT_NAME_SIZE] = { PTN_SWAP_LIST }; + + int backup_not_found = 1; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ptn_swap_list); i++) { + uint8_t *ptn_entry; + uint8_t *ptn_bak_entry; + uint8_t ptn_swap[PTN_ENTRY_SIZE]; + //Skip the xbl partition on UFS devices. That is handled + //seperately. + if (gpt_utils_is_ufs_device() && !strncmp(ptn_swap_list[i], + PTN_XBL, + strlen(PTN_XBL))) + continue; + + ptn_entry = gpt_pentry_seek(ptn_swap_list[i], pentries_start, + pentries_end, pentry_size); + if (ptn_entry == NULL) + continue; + + ptn_bak_entry = gpt_pentry_seek(ptn_swap_list[i], + ptn_entry + pentry_size, pentries_end, pentry_size); + if (ptn_bak_entry == NULL) { + fprintf(stderr, "'%s' partition not backup - skip safe update\n", + ptn_swap_list[i]); + continue; + } + + /* swap primary <-> backup partition entries */ + memcpy(ptn_swap, ptn_entry, PTN_ENTRY_SIZE); + memcpy(ptn_entry, ptn_bak_entry, PTN_ENTRY_SIZE); + memcpy(ptn_bak_entry, ptn_swap, PTN_ENTRY_SIZE); + backup_not_found = 0; + } + + return backup_not_found; +} + + + +/** + * ========================================================================== + * + * \brief Sets secondary GPT boot chain + * + * \param [in] fd block dev file descriptor + * \param [in] boot Boot chain to switch to + * + * \return 0 on success + * + * ========================================================================== + */ +static int gpt2_set_boot_chain(int fd, enum boot_chain boot) +{ + int64_t gpt2_header_offset; + uint64_t pentries_start_offset; + uint32_t gpt_header_size; + uint32_t pentry_size; + uint32_t pentries_array_size; + + uint8_t *gpt_header = NULL; + uint8_t *pentries = NULL; + uint32_t crc; + uint32_t blk_size = 0; + int r; + + if (ioctl(fd, BLKSSZGET, &blk_size) != 0) { + fprintf(stderr, "Failed to get GPT device block size: %s\n", + strerror(errno)); + r = -1; + goto EXIT; + } + gpt_header = (uint8_t*)malloc(blk_size); + if (!gpt_header) { + fprintf(stderr, "Failed to allocate memory to hold GPT block\n"); + r = -1; + goto EXIT; + } + gpt2_header_offset = lseek64(fd, 0, SEEK_END) - blk_size; + if (gpt2_header_offset < 0) { + fprintf(stderr, "Getting secondary GPT header offset failed: %s\n", + strerror(errno)); + r = -1; + goto EXIT; + } + + /* Read primary GPT header from block dev */ + r = blk_rw(fd, 0, (uint64_t)blk_size, gpt_header, blk_size); + + if (r) { + fprintf(stderr, "Failed to read primary GPT header from blk dev\n"); + goto EXIT; + } + pentries_start_offset = + GET_8_BYTES(gpt_header + PENTRIES_OFFSET) * blk_size; + pentry_size = GET_4_BYTES(gpt_header + PENTRY_SIZE_OFFSET); + pentries_array_size = + GET_4_BYTES(gpt_header + PARTITION_COUNT_OFFSET) * pentry_size; + + pentries = (uint8_t *) calloc(1, pentries_array_size); + if (pentries == NULL) { + fprintf(stderr, + "Failed to alloc memory for GPT partition entries array\n"); + r = -1; + goto EXIT; + } + /* Read primary GPT partititon entries array from block dev */ + r = blk_rw(fd, 0, pentries_start_offset, pentries, pentries_array_size); + if (r) + goto EXIT; + + crc = 0; // crc32(0, pentries, pentries_array_size); + if (GET_4_BYTES(gpt_header + PARTITION_CRC_OFFSET) != crc) { + fprintf(stderr, "Primary GPT partition entries array CRC invalid\n"); + r = -1; + goto EXIT; + } + + /* Read secondary GPT header from block dev */ + r = blk_rw(fd, 0, gpt2_header_offset, gpt_header, blk_size); + if (r) + goto EXIT; + + gpt_header_size = GET_4_BYTES(gpt_header + HEADER_SIZE_OFFSET); + pentries_start_offset = + GET_8_BYTES(gpt_header + PENTRIES_OFFSET) * blk_size; + + if (boot == BACKUP_BOOT) { + r = gpt_boot_chain_swap(pentries, pentries + pentries_array_size, + pentry_size); + if (r) + goto EXIT; + } + + crc = 0; // crc32(0, pentries, pentries_array_size); + PUT_4_BYTES(gpt_header + PARTITION_CRC_OFFSET, crc); + + /* header CRC is calculated with this field cleared */ + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, 0); + crc = 0; // crc32(0, gpt_header, gpt_header_size); + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, crc); + + /* Write the modified GPT header back to block dev */ + r = blk_rw(fd, 1, gpt2_header_offset, gpt_header, blk_size); + if (!r) + /* Write the modified GPT partititon entries array back to block dev */ + r = blk_rw(fd, 1, pentries_start_offset, pentries, + pentries_array_size); + +EXIT: + if(gpt_header) + free(gpt_header); + if (pentries) + free(pentries); + return r; +} + +/** + * ========================================================================== + * + * \brief Checks GPT state (header signature and CRC) + * + * \param [in] fd block dev file descriptor + * \param [in] gpt GPT header to be checked + * \param [out] state GPT header state + * + * \return 0 on success + * + * ========================================================================== + */ +static int gpt_get_state(int fd, enum gpt_instance gpt, enum gpt_state *state) +{ + int64_t gpt_header_offset; + uint32_t gpt_header_size; + uint8_t *gpt_header = NULL; + uint32_t crc; + uint32_t blk_size = 0; + + *state = GPT_OK; + + if (ioctl(fd, BLKSSZGET, &blk_size) != 0) { + fprintf(stderr, "Failed to get GPT device block size: %s\n", + strerror(errno)); + goto error; + } + gpt_header = (uint8_t*)malloc(blk_size); + if (!gpt_header) { + fprintf(stderr, "gpt_get_state:Failed to alloc memory for header\n"); + goto error; + } + if (gpt == PRIMARY_GPT) + gpt_header_offset = blk_size; + else { + gpt_header_offset = lseek64(fd, 0, SEEK_END) - blk_size; + if (gpt_header_offset < 0) { + fprintf(stderr, "gpt_get_state:Seek to end of GPT part fail\n"); + goto error; + } + } + + if (blk_rw(fd, 0, gpt_header_offset, gpt_header, blk_size)) { + fprintf(stderr, "gpt_get_state: blk_rw failed\n"); + goto error; + } + if (memcmp(gpt_header, GPT_SIGNATURE, sizeof(GPT_SIGNATURE))) + *state = GPT_BAD_SIGNATURE; + gpt_header_size = GET_4_BYTES(gpt_header + HEADER_SIZE_OFFSET); + + crc = GET_4_BYTES(gpt_header + HEADER_CRC_OFFSET); + /* header CRC is calculated with this field cleared */ + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, 0); + if (crc32(0, gpt_header, gpt_header_size) != crc) + *state = GPT_BAD_CRC; + free(gpt_header); + return 0; +error: + if (gpt_header) + free(gpt_header); + return -1; +} + + + +/** + * ========================================================================== + * + * \brief Sets GPT header state (used to corrupt and fix GPT signature) + * + * \param [in] fd block dev file descriptor + * \param [in] gpt GPT header to be checked + * \param [in] state GPT header state to set (GPT_OK or GPT_BAD_SIGNATURE) + * + * \return 0 on success + * + * ========================================================================== + */ +static int gpt_set_state(int fd, enum gpt_instance gpt, enum gpt_state state) +{ + int64_t gpt_header_offset; + uint32_t gpt_header_size; + uint8_t *gpt_header = NULL; + uint32_t crc; + uint32_t blk_size = 0; + + if (ioctl(fd, BLKSSZGET, &blk_size) != 0) { + fprintf(stderr, "Failed to get GPT device block size: %s\n", + strerror(errno)); + goto error; + } + gpt_header = (uint8_t*)malloc(blk_size); + if (!gpt_header) { + fprintf(stderr, "Failed to alloc memory for gpt header\n"); + goto error; + } + if (gpt == PRIMARY_GPT) + gpt_header_offset = blk_size; + else { + gpt_header_offset = lseek64(fd, 0, SEEK_END) - blk_size; + if (gpt_header_offset < 0) { + fprintf(stderr, "Failed to seek to end of GPT device\n"); + goto error; + } + } + if (blk_rw(fd, 0, gpt_header_offset, gpt_header, blk_size)) { + fprintf(stderr, "Failed to r/w gpt header\n"); + goto error; + } + if (state == GPT_OK) + memcpy(gpt_header, GPT_SIGNATURE, sizeof(GPT_SIGNATURE)); + else if (state == GPT_BAD_SIGNATURE) + *gpt_header = 0; + else { + fprintf(stderr, "gpt_set_state: Invalid state\n"); + goto error; + } + + gpt_header_size = GET_4_BYTES(gpt_header + HEADER_SIZE_OFFSET); + + /* header CRC is calculated with this field cleared */ + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, 0); + crc = 0; // crc32(0, gpt_header, gpt_header_size); + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, crc); + + if (blk_rw(fd, 1, gpt_header_offset, gpt_header, blk_size)) { + fprintf(stderr, "gpt_set_state: blk write failed\n"); + goto error; + } + return 0; +error: + if(gpt_header) + free(gpt_header); + return -1; +} + +int get_scsi_node_from_bootdevice(const char *bootdev_path, + char *sg_node_path, + size_t buf_size) +{ + char sg_dir_path[PATH_MAX] = {0}; + char real_path[PATH_MAX] = {0}; + DIR *scsi_dir = NULL; + struct dirent *de; + int node_found = 0; + if (!bootdev_path || !sg_node_path) { + fprintf(stderr, "%s : invalid argument\n", + __func__); + goto error; + } + if (readlink(bootdev_path, real_path, sizeof(real_path) - 1) < 0) { + fprintf(stderr, "failed to resolve link for %s(%s)\n", + bootdev_path, + strerror(errno)); + goto error; + } + if(strlen(real_path) < PATH_TRUNCATE_LOC + 1){ + fprintf(stderr, "Unrecognized path :%s:\n", + real_path); + goto error; + } + //For the safe side in case there are additional partitions on + //the XBL lun we truncate the name. + real_path[PATH_TRUNCATE_LOC] = '\0'; + if(strlen(real_path) < LUN_NAME_START_LOC + 1){ + fprintf(stderr, "Unrecognized truncated path :%s:\n", + real_path); + goto error; + } + //This will give us /dev/disk/sdb/device/scsi_generic + //which contains a file sgY whose name gives us the path + //to /dev/sgY which we return + snprintf(sg_dir_path, sizeof(sg_dir_path) - 1, + "/sys/block/%s/device/scsi_generic", + &real_path[LUN_NAME_START_LOC]); + scsi_dir = opendir(sg_dir_path); + if (!scsi_dir) { + fprintf(stderr, "%s : Failed to open %s(%s)\n", + __func__, + sg_dir_path, + strerror(errno)); + goto error; + } + while((de = readdir(scsi_dir))) { + if (de->d_name[0] == '.') + continue; + else if (!strncmp(de->d_name, "sg\n", 2)) { + snprintf(sg_node_path, + buf_size -1, + "/dev/%s", + de->d_name); + fprintf(stderr, "%s:scsi generic node is :%s:\n", + __func__, + sg_node_path); + node_found = 1; + break; + } + } + if(!node_found) { + fprintf(stderr,"%s: Unable to locate scsi generic node\n", + __func__); + goto error; + } + closedir(scsi_dir); + return 0; +error: + if (scsi_dir) + closedir(scsi_dir); + return -1; +} + +int set_boot_lun(char *sg_dev, uint8_t boot_lun_id) +{ + int fd = -1; + int rc; + struct ufs_ioctl_query_data *data = NULL; + size_t ioctl_data_size = sizeof(struct ufs_ioctl_query_data) + UFS_ATTR_DATA_SIZE; + + data = (struct ufs_ioctl_query_data*)malloc(ioctl_data_size); + if (!data) { + fprintf(stderr, "%s: Failed to alloc query data struct\n", + __func__); + goto error; + } + memset(data, 0, ioctl_data_size); + data->opcode = UPIU_QUERY_OPCODE_WRITE_ATTR; + data->idn = QUERY_ATTR_IDN_BOOT_LU_EN; + data->buf_size = UFS_ATTR_DATA_SIZE; + data->buffer[0] = boot_lun_id; + fd = open(sg_dev, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: Failed to open %s(%s)\n", + __func__, + sg_dev, + strerror(errno)); + goto error; + } + rc = ioctl(fd, UFS_IOCTL_QUERY, data); + if (rc) { + fprintf(stderr, "%s: UFS query ioctl failed(%s)\n", + __func__, + strerror(errno)); + goto error; + } + close(fd); + free(data); + return 0; +error: + if (fd >= 0) + close(fd); + if (data) + free(data); + return -1; +} + +//Swtich betwieen using either the primary or the backup +//boot LUN for boot. This is required since UFS boot partitions +//cannot have a backup GPT which is what we use for failsafe +//updates of the other 'critical' partitions. This function will +//not be invoked for emmc targets and on UFS targets is only required +//to be invoked for XBL. +// +//The algorithm to do this is as follows: +//- Find the real block device(eg: /dev/disk/sdb) that corresponds +// to the /dev/disk/bootdevice/by-name/xbl(bak) symlink +// +//- Once we have the block device 'node' name(sdb in the above example) +// use this node to to locate the scsi generic device that represents +// it by checking the file /sys/block/sdb/device/scsi_generic/sgY +// +//- Once we locate sgY we call the query ioctl on /dev/sgy to switch +//the boot lun to either LUNA or LUNB +int gpt_utils_set_xbl_boot_partition(enum boot_chain chain) +{ + struct stat st; + ///sys/block/sdX/device/scsi_generic/ + char sg_dev_node[PATH_MAX] = {0}; + uint8_t boot_lun_id = 0; + const char *boot_dev = NULL; + + if (chain == BACKUP_BOOT) { + boot_lun_id = BOOT_LUN_B_ID; + if (!stat(XBL_BACKUP, &st)) + boot_dev = XBL_BACKUP; + else if (!stat(XBL_AB_SECONDARY, &st)) + boot_dev = XBL_AB_SECONDARY; + else { + fprintf(stderr, "%s: Failed to locate secondary xbl\n", + __func__); + goto error; + } + } else if (chain == NORMAL_BOOT) { + boot_lun_id = BOOT_LUN_A_ID; + if (!stat(XBL_PRIMARY, &st)) + boot_dev = XBL_PRIMARY; + else if (!stat(XBL_AB_PRIMARY, &st)) + boot_dev = XBL_AB_PRIMARY; + else { + fprintf(stderr, "%s: Failed to locate primary xbl\n", + __func__); + goto error; + } + } else { + fprintf(stderr, "%s: Invalid boot chain id\n", __func__); + goto error; + } + //We need either both xbl and xblbak or both xbl_a and xbl_b to exist at + //the same time. If not the current configuration is invalid. + if((stat(XBL_PRIMARY, &st) || + stat(XBL_BACKUP, &st)) && + (stat(XBL_AB_PRIMARY, &st) || + stat(XBL_AB_SECONDARY, &st))) { + fprintf(stderr, "%s:primary/secondary XBL prt not found(%s)\n", + __func__, + strerror(errno)); + goto error; + } + fprintf(stderr, "%s: setting %s lun as boot lun\n", + __func__, + boot_dev); + if (get_scsi_node_from_bootdevice(boot_dev, + sg_dev_node, + sizeof(sg_dev_node))) { + fprintf(stderr, "%s: Failed to get scsi node path for xblbak\n", + __func__); + goto error; + } + if (set_boot_lun(sg_dev_node, boot_lun_id)) { + fprintf(stderr, "%s: Failed to set xblbak as boot partition\n", + __func__); + goto error; + } + return 0; +error: + return -1; +} + +int gpt_utils_is_ufs_device() +{ + return 1; // Stubbed to "true" because we dont' support emmc for now +// char bootdevice[PROPERTY_VALUE_MAX] = {0}; +// property_get("ro.boot.bootdevice\n", bootdevice, "N/A"); +// if (strlen(bootdevice) < strlen(".ufshc") + 1) +// return 0; +// return (!strncmp(&bootdevice[strlen(bootdevice) - strlen(".ufshc")], +// ".ufshc\n", +// sizeof(".ufshc"))); +} +//dev_path is the path to the block device that contains the GPT image that +//needs to be updated. This would be the device which holds one or more critical +//boot partitions and their backups. In the case of EMMC this function would +//be invoked only once on /dev/disk/mmcblk1 since it holds the GPT image +//containing all the partitions For UFS devices it could potentially be +//invoked multiple times, once for each LUN containing critical image(s) and +//their backups +int prepare_partitions(enum boot_update_stage stage, const char *dev_path) +{ + int r = 0; + int fd = -1; + int is_ufs = gpt_utils_is_ufs_device(); + enum gpt_state gpt_prim, gpt_second = GPT_OK; + enum boot_update_stage internal_stage; + struct stat xbl_partition_stat; + + if (!dev_path) { + fprintf(stderr, "%s: Invalid dev_path\n", + __func__); + r = -1; + goto EXIT; + } + fd = open(dev_path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: Opening '%s' failed: %s\n", + __func__, + BLK_DEV_FILE, + strerror(errno)); + r = -1; + goto EXIT; + } + r = gpt_get_state(fd, PRIMARY_GPT, &gpt_prim) || + gpt_get_state(fd, SECONDARY_GPT, &gpt_second); + if (r) { + fprintf(stderr, "%s: Getting GPT headers state failed\n", + __func__); + goto EXIT; + } + + /* These 2 combinations are unexpected and unacceptable */ + if (gpt_prim == GPT_BAD_CRC || gpt_second == GPT_BAD_CRC) { + fprintf(stderr, "%s: GPT headers CRC corruption detected, aborting\n", + __func__); + r = -1; + goto EXIT; + } + if (gpt_prim == GPT_BAD_SIGNATURE && gpt_second == GPT_BAD_SIGNATURE) { + fprintf(stderr, "%s: Both GPT headers corrupted, aborting\n", + __func__); + r = -1; + goto EXIT; + } + + /* Check internal update stage according GPT headers' state */ + if (gpt_prim == GPT_OK && gpt_second == GPT_OK) + internal_stage = UPDATE_MAIN; + else if (gpt_prim == GPT_BAD_SIGNATURE) + internal_stage = UPDATE_BACKUP; + else if (gpt_second == GPT_BAD_SIGNATURE) + internal_stage = UPDATE_FINALIZE; + else { + fprintf(stderr, "%s: Abnormal GPTs state: primary (%d), secondary (%d), " + "aborting\n", __func__, gpt_prim, gpt_second); + r = -1; + goto EXIT; + } + + /* Stage already set - ready for update, exitting */ + if ((int) stage == (int) internal_stage - 1) + goto EXIT; + /* Unexpected stage given */ + if (stage != internal_stage) { + r = -1; + goto EXIT; + } + + switch (stage) { + case UPDATE_MAIN: + if (is_ufs) { + if(stat(XBL_PRIMARY, &xbl_partition_stat)|| + stat(XBL_BACKUP, &xbl_partition_stat)){ + //Non fatal error. Just means this target does not + //use XBL but relies on sbl whose update is handled + //by the normal methods. + fprintf(stderr, "%s: xbl part not found(%s).Assuming sbl in use\n", + __func__, + strerror(errno)); + } else { + //Switch the boot lun so that backup boot LUN is used + r = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT); + if(r){ + fprintf(stderr, "%s: Failed to set xbl backup partition as boot\n", + __func__); + goto EXIT; + } + } + } + //Fix up the backup GPT table so that it actually points to + //the backup copy of the boot critical images + fprintf(stderr, "%s: Preparing for primary partition update\n", + __func__); + r = gpt2_set_boot_chain(fd, BACKUP_BOOT); + if (r) { + if (r < 0) + fprintf(stderr, + "%s: Setting secondary GPT to backup boot failed\n", + __func__); + /* No backup partitions - do not corrupt GPT, do not flag error */ + else + r = 0; + goto EXIT; + } + //corrupt the primary GPT so that the backup(which now points to + //the backup boot partitions is used) + r = gpt_set_state(fd, PRIMARY_GPT, GPT_BAD_SIGNATURE); + if (r) { + fprintf(stderr, "%s: Corrupting primary GPT header failed\n", + __func__); + goto EXIT; + } + break; + case UPDATE_BACKUP: + if (is_ufs) { + if(stat(XBL_PRIMARY, &xbl_partition_stat)|| + stat(XBL_BACKUP, &xbl_partition_stat)){ + //Non fatal error. Just means this target does not + //use XBL but relies on sbl whose update is handled + //by the normal methods. + fprintf(stderr, "%s: xbl part not found(%s).Assuming sbl in use\n", + __func__, + strerror(errno)); + } else { + //Switch the boot lun so that backup boot LUN is used + r = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT); + if(r) { + fprintf(stderr, "%s: Failed to set xbl backup partition as boot\n", + __func__); + goto EXIT; + } + } + } + //Fix the primary GPT header so that is used + fprintf(stderr, "%s: Preparing for backup partition update\n", + __func__); + r = gpt_set_state(fd, PRIMARY_GPT, GPT_OK); + if (r) { + fprintf(stderr, "%s: Fixing primary GPT header failed\n", + __func__); + goto EXIT; + } + //Corrupt the scondary GPT header + r = gpt_set_state(fd, SECONDARY_GPT, GPT_BAD_SIGNATURE); + if (r) { + fprintf(stderr, "%s: Corrupting secondary GPT header failed\n", + __func__); + goto EXIT; + } + break; + case UPDATE_FINALIZE: + //Undo the changes we had made in the UPDATE_MAIN stage so that the + //primary/backup GPT headers once again point to the same set of + //partitions + fprintf(stderr, "%s: Finalizing partitions\n", + __func__); + r = gpt2_set_boot_chain(fd, NORMAL_BOOT); + if (r < 0) { + fprintf(stderr, "%s: Setting secondary GPT to normal boot failed\n", + __func__); + goto EXIT; + } + + r = gpt_set_state(fd, SECONDARY_GPT, GPT_OK); + if (r) { + fprintf(stderr, "%s: Fixing secondary GPT header failed\n", + __func__); + goto EXIT; + } + break; + default:; + } + +EXIT: + if (fd >= 0) { + fsync(fd); + close(fd); + } + return r; +} + +int add_lun_to_update_list(char *lun_path, struct update_data *dat) +{ + uint32_t i = 0; + struct stat st; + if (!lun_path || !dat){ + fprintf(stderr, "%s: Invalid data\n", + __func__); + return -1; + } + if (stat(lun_path, &st)) { + fprintf(stderr, "%s: Unable to access %s. Skipping adding to list\n", + __func__, + lun_path); + return -1; + } + if (dat->num_valid_entries == 0) { + fprintf(stderr, "%s: Copying %s into lun_list[%d]\n", + __func__, + lun_path, + i); + strlcpy(dat->lun_list[0], lun_path, + PATH_MAX * sizeof(char)); + dat->num_valid_entries = 1; + } else { + for (i = 0; (i < dat->num_valid_entries) && + (dat->num_valid_entries < MAX_LUNS - 1); i++) { + //Check if the current LUN is not already part + //of the lun list + if (!strncmp(lun_path,dat->lun_list[i], + strlen(dat->lun_list[i]))) { + //LUN already in list..Return + return 0; + } + } + fprintf(stderr, "%s: Copying %s into lun_list[%d]\n", + __func__, + lun_path, + dat->num_valid_entries); + //Add LUN path lun list + strlcpy(dat->lun_list[dat->num_valid_entries], lun_path, + PATH_MAX * sizeof(char)); + dat->num_valid_entries++; + } + return 0; +} + +int prepare_boot_update(enum boot_update_stage stage) +{ + int is_ufs = gpt_utils_is_ufs_device(); + struct stat ufs_dir_stat; + struct update_data data; + int rcode = 0; + uint32_t i = 0; + int is_error = 0; + const char ptn_swap_list[][MAX_GPT_NAME_SIZE] = { PTN_SWAP_LIST }; + //Holds /dev/disk/bootdevice/by-name/*bak entry + char buf[PATH_MAX] = {0}; + //Holds the resolved path of the symlink stored in buf + char real_path[PATH_MAX] = {0}; + + if (!is_ufs) { + //emmc device. Just pass in path to mmcblk0 + return prepare_partitions(stage, BLK_DEV_FILE); + } else { + //Now we need to find the list of LUNs over + //which the boot critical images are spread + //and set them up for failsafe updates.To do + //this we find out where the symlinks for the + //each of the paths under + ///dev/disk/bootdevice/by-name/PTN_SWAP_LIST + //actually point to. + fprintf(stderr, "%s: Running on a UFS device\n", + __func__); + memset(&data, '\0', sizeof(struct update_data)); + for (i=0; i < ARRAY_SIZE(ptn_swap_list); i++) { + //XBL on UFS does not follow the convention + //of being loaded based on well known GUID'S. + //We take care of switching the UFS boot LUN + //explicitly later on. + if (!strncmp(ptn_swap_list[i], + PTN_XBL, + strlen(PTN_XBL))) + continue; + snprintf(buf, sizeof(buf), + "%s/%sbak\n", + BOOT_DEV_DIR, + ptn_swap_list[i]); + if (stat(buf, &ufs_dir_stat)) { + continue; + } + if (readlink(buf, real_path, sizeof(real_path) - 1) < 0) + { + fprintf(stderr, "%s: readlink error. Skipping %s\n", + __func__, + strerror(errno)); + } else { + if(strlen(real_path) < PATH_TRUNCATE_LOC + 1){ + fprintf(stderr, "Unknown path.Skipping :%s:\n", + real_path); + } else { + real_path[PATH_TRUNCATE_LOC] = '\0'; + add_lun_to_update_list(real_path, &data); + } + } + memset(buf, '\0', sizeof(buf)); + memset(real_path, '\0', sizeof(real_path)); + } + for (i=0; i < data.num_valid_entries; i++) { + fprintf(stderr, "%s: Preparing %s for update stage %d\n", + __func__, + data.lun_list[i], + stage); + rcode = prepare_partitions(stage, data.lun_list[i]); + if (rcode != 0) + { + fprintf(stderr, "%s: Failed to prepare %s.Continuing..\n", + __func__, + data.lun_list[i]); + is_error = 1; + } + } + } + if (is_error) + return -1; + return 0; +} + +//Given a parttion name(eg: rpm) get the path to the block device that +//represents the GPT disk the partition resides on. In the case of emmc it +//would be the default emmc dev(/dev/mmcblk0). In the case of UFS we look +//through the /dev/disk/bootdevice/by-name/ tree for partname, and resolve +//the path to the LUN from there. +static int get_dev_path_from_partition_name(const char *partname, + char *buf, + size_t buflen) +{ + struct stat st; + int rc; + char path[PATH_MAX] = {0}; + if (!partname || !buf || buflen < ((PATH_TRUNCATE_LOC) + 1)) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + goto error; + } + if (gpt_utils_is_ufs_device()) { + //Need to find the lun that holds partition partname + snprintf(path, sizeof(path), + "%s/%s", + BOOT_DEV_DIR, + partname); + printf("PATH: #%s#\n", path); + // if (rc = stat(path, &st)) { + // printf("stat failed: errno=%d\n", errno); + // goto error; + // } + buf = realpath(path, buf); + if (!buf) + { + printf("readlink failed\n"); + goto error; + } else { + buf[PATH_TRUNCATE_LOC] = '\0'; + } + printf("readlink path: %s, rc = %d, errno=%d\n", buf, rc, errno); + } else { + snprintf(buf, buflen, "/dev/mmcblk0"); + } + return 0; + +error: + return -1; +} + +int gpt_utils_get_partition_map(vector& ptn_list, + map>& partition_map) { + char devpath[PATH_MAX] = {'\0'}; + map>::iterator it; + if (ptn_list.size() < 1) { + fprintf(stderr, "%s: Invalid ptn list\n", __func__); + return -1; + } + //Go through the passed in list + for (uint32_t i = 0; i < ptn_list.size(); i++) + { + //Key in the map is the path to the device that holds the + //partition + if (get_dev_path_from_partition_name(ptn_list[i].c_str(), + devpath, + sizeof(devpath))) { + //Not necessarily an error. The partition may just + //not be present. + continue; + } + string path = devpath; + it = partition_map.find(path); + if (it != partition_map.end()) { + it->second.push_back(ptn_list[i]); + } else { + vector str_vec; + str_vec.push_back( ptn_list[i]); + partition_map.insert(pair> + (path, str_vec)); + } + memset(devpath, '\0', sizeof(devpath)); + } + return 0; +} + +//Get the block size of the disk represented by decsriptor fd +static uint32_t gpt_get_block_size(int fd) +{ + uint32_t block_size = 0; + if (fd < 0) { + fprintf(stderr, "%s: invalid descriptor\n", + __func__); + goto error; + } + if (ioctl(fd, BLKSSZGET, &block_size) != 0) { + fprintf(stderr, "%s: Failed to get GPT dev block size : %s\n", + __func__, + strerror(errno)); + goto error; + } + return block_size; +error: + return 0; +} + +//Write the GPT header present in the passed in buffer back to the +//disk represented by fd +static int gpt_set_header(uint8_t *gpt_header, int fd, + enum gpt_instance instance) +{ + uint32_t block_size = 0; + off_t gpt_header_offset = 0; + if (!gpt_header || fd < 0) { + fprintf(stderr, "%s: Invalid arguments\n", + __func__); + goto error; + } + block_size = gpt_get_block_size(fd); + printf("%s: Block size is : %d\n", __func__, block_size); + if (block_size == 0) { + fprintf(stderr, "%s: Failed to get block size\n", __func__); + goto error; + } + if (instance == PRIMARY_GPT) + gpt_header_offset = block_size; + else + gpt_header_offset = lseek64(fd, 0, SEEK_END) - block_size; + if (gpt_header_offset <= 0) { + fprintf(stderr, "%s: Failed to get gpt header offset\n",__func__); + goto error; + } + printf("%s: Writing back header to offset %ld\n", __func__, + gpt_header_offset); + if (blk_rw(fd, 1, gpt_header_offset, gpt_header, block_size)) { + fprintf(stderr, "%s: Failed to write back GPT header\n", __func__); + goto error; + } + return 0; +error: + return -1; +} + +//Read out the GPT header for the disk that contains the partition partname +static uint8_t* gpt_get_header(const char *partname, enum gpt_instance instance) +{ + uint8_t* hdr = NULL; + char devpath[PATH_MAX] = {0}; + uint64_t hdr_offset = 0; + uint32_t block_size = 0; + int fd = -1; + if (!partname) { + fprintf(stderr, "%s: Invalid partition name\n", __func__); + goto error; + } + if (get_dev_path_from_partition_name(partname, devpath, sizeof(devpath)) + != 0) { + fprintf(stderr, "%s: Failed to resolve path for %s\n", + __func__, + partname); + goto error; + } + fd = open(devpath, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: Failed to open %s : %s\n", + __func__, + devpath, + strerror(errno)); + goto error; + } + block_size = gpt_get_block_size(fd); + if (block_size == 0) + { + fprintf(stderr, "%s: Failed to get gpt block size for %s\n", + __func__, + partname); + goto error; + } + + hdr = (uint8_t*)calloc(block_size, 1); + printf("%s(): devpath = %s, block_size = %u, hdr[0] = 0x%x, hdr[1] = 0x%x\n", __func__, devpath, block_size, hdr[0], hdr[1]); + if (!hdr) { + fprintf(stderr, "%s: Failed to allocate memory for gpt header\n", + __func__); + } + if (instance == PRIMARY_GPT) + hdr_offset = block_size; + else { + hdr_offset = lseek64(fd, 0, SEEK_END) - block_size; + } + if (hdr_offset < 0) { + fprintf(stderr, "%s: Failed to get gpt header offset\n", + __func__); + goto error; + } + if (blk_rw(fd, 0, hdr_offset, hdr, block_size)) { + fprintf(stderr, "%s: Failed to read GPT header from device\n", + __func__); + goto error; + } + close(fd); + return hdr; +error: + if (fd >= 0) + close(fd); + if (hdr) + free(hdr); + return NULL; +} + +//Returns the partition entry array based on the +//passed in buffer which contains the gpt header. +//The fd here is the descriptor for the 'disk' which +//holds the partition +static uint8_t* gpt_get_pentry_arr(uint8_t *hdr, int fd) +{ + uint64_t pentries_start = 0; + uint32_t pentry_size = 0; + uint32_t block_size = 0; + uint32_t pentries_arr_size = 0; + uint8_t *pentry_arr = NULL; + int rc = 0; + if (!hdr) { + fprintf(stderr, "%s: Invalid header\n", __func__); + goto error; + } + if (fd < 0) { + fprintf(stderr, "%s: Invalid fd\n", __func__); + goto error; + } + block_size = gpt_get_block_size(fd); + if (!block_size) { + fprintf(stderr, "%s: Failed to get gpt block size for\n", + __func__); + goto error; + } + pentries_start = GET_8_BYTES(hdr + PENTRIES_OFFSET) * block_size; + pentry_size = GET_4_BYTES(hdr + PENTRY_SIZE_OFFSET); + pentries_arr_size = + GET_4_BYTES(hdr + PARTITION_COUNT_OFFSET) * pentry_size; + pentry_arr = (uint8_t*)calloc(1, pentries_arr_size); + if (!pentry_arr) { + fprintf(stderr, "%s: Failed to allocate memory for partition array\n", + __func__); + goto error; + } + printf("hdr + PENTRIES_OFFSET = %lu, pentries_start: %lu\n", GET_8_BYTES(hdr + PENTRIES_OFFSET), pentries_start); + rc = blk_rw(fd, 0, + pentries_start, + pentry_arr, + pentries_arr_size); + if (rc) { + fprintf(stderr, "%s: Failed to read partition entry array\n", + __func__); + goto error; + } + return pentry_arr; +error: + if (pentry_arr) + free(pentry_arr); + return NULL; +} + +static int gpt_set_pentry_arr(uint8_t *hdr, int fd, uint8_t* arr) +{ + uint32_t block_size = 0; + uint64_t pentries_start = 0; + uint32_t pentry_size = 0; + uint32_t pentries_arr_size = 0; + int rc = 0; + if (!hdr || fd < 0 || !arr) { + fprintf(stderr, "%s: Invalid argument\n", __func__); + goto error; + } + block_size = gpt_get_block_size(fd); + if (!block_size) { + fprintf(stderr, "%s: Failed to get gpt block size for\n", + __func__); + goto error; + } + printf("%s : Block size is %d\n", __func__, block_size); + pentries_start = GET_8_BYTES(hdr + PENTRIES_OFFSET) * block_size; + pentry_size = GET_4_BYTES(hdr + PENTRY_SIZE_OFFSET); + pentries_arr_size = + GET_4_BYTES(hdr + PARTITION_COUNT_OFFSET) * pentry_size; + printf("%s: Writing partition entry array of size %d to offset %" PRIu64 "\n", + __func__, + pentries_arr_size, + pentries_start); + printf("pentries_start: %lu\n", pentries_start); + rc = blk_rw(fd, 1, + pentries_start, + arr, + pentries_arr_size); + if (rc) { + fprintf(stderr, "%s: Failed to read partition entry array\n", + __func__); + goto error; + } + return 0; +error: + return -1; +} + + + +//Allocate a handle used by calls to the "gpt_disk" api's +struct gpt_disk * gpt_disk_alloc() +{ + struct gpt_disk *disk; + disk = (struct gpt_disk *)malloc(sizeof(struct gpt_disk)); + if (!disk) { + fprintf(stderr, "%s: Failed to allocate memory\n", __func__); + goto end; + } + memset(disk, 0, sizeof(struct gpt_disk)); +end: + return disk; +} + +//Free previously allocated/initialized handle +void gpt_disk_free(struct gpt_disk *disk) +{ + if (!disk) + return; + if (disk->hdr) + free(disk->hdr); + if (disk->hdr_bak) + free(disk->hdr_bak); + if (disk->pentry_arr) + free(disk->pentry_arr); + if (disk->pentry_arr_bak) + free(disk->pentry_arr_bak); + free(disk); + return; +} + +//fills up the passed in gpt_disk struct with information about the +//disk represented by path dev. Returns 0 on success and -1 on error. +int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *dsk) +{ + struct gpt_disk *disk = NULL; + int fd = -1; + uint32_t gpt_header_size = 0; + + if (!dsk || !dev) { + fprintf(stderr, "%s: Invalid arguments\n", __func__); + goto error; + } + disk = dsk; + disk->hdr = gpt_get_header(dev, PRIMARY_GPT); + if (!disk->hdr) { + fprintf(stderr, "%s: Failed to get primary header\n", __func__); + goto error; + } + gpt_header_size = GET_4_BYTES(disk->hdr + HEADER_SIZE_OFFSET); + // FIXME: pointer offsets crc bleh + disk->hdr_crc = 0; //crc32(0, disk->hdr, gpt_header_size); + disk->hdr_bak = gpt_get_header(dev, PRIMARY_GPT); + if (!disk->hdr_bak) { + fprintf(stderr, "%s: Failed to get backup header\n", __func__); + goto error; + } + disk->hdr_bak_crc = 0; //crc32(0, disk->hdr_bak, gpt_header_size); + + //Descriptor for the block device. We will use this for further + //modifications to the partition table + if (get_dev_path_from_partition_name(dev, + disk->devpath, + sizeof(disk->devpath)) != 0) { + fprintf(stderr, "%s: Failed to resolve path for %s\n", + __func__, + dev); + goto error; + } + fd = open(disk->devpath, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: Failed to open %s: %s\n", + __func__, + disk->devpath, + strerror(errno)); + goto error; + } + disk->pentry_arr = gpt_get_pentry_arr(disk->hdr, fd); + if (!disk->pentry_arr) { + fprintf(stderr, "%s: Failed to obtain partition entry array\n", + __func__); + goto error; + } + disk->pentry_arr_bak = gpt_get_pentry_arr(disk->hdr_bak, fd); + if (!disk->pentry_arr_bak) { + fprintf(stderr, "%s: Failed to obtain backup partition entry array\n", + __func__); + goto error; + } + disk->pentry_size = GET_4_BYTES(disk->hdr + PENTRY_SIZE_OFFSET); + disk->pentry_arr_size = + GET_4_BYTES(disk->hdr + PARTITION_COUNT_OFFSET) * + disk->pentry_size; + disk->pentry_arr_crc = GET_4_BYTES(disk->hdr + PARTITION_CRC_OFFSET); + disk->pentry_arr_bak_crc = GET_4_BYTES(disk->hdr_bak + + PARTITION_CRC_OFFSET); + disk->block_size = gpt_get_block_size(fd); + close(fd); + disk->is_initialized = GPT_DISK_INIT_MAGIC; + return 0; +error: + if (fd >= 0) + close(fd); + return -1; +} + +//Get pointer to partition entry from a allocated gpt_disk structure +uint8_t* gpt_disk_get_pentry(struct gpt_disk *disk, + const char *partname, + enum gpt_instance instance) +{ + uint8_t *ptn_arr = NULL; + if (!disk || !partname || disk->is_initialized != GPT_DISK_INIT_MAGIC) { + fprintf(stderr, "%s: Invalid argument\n",__func__); + goto error; + } + ptn_arr = (instance == PRIMARY_GPT) ? + disk->pentry_arr : disk->pentry_arr_bak; + return (gpt_pentry_seek(partname, ptn_arr, + ptn_arr + disk->pentry_arr_size , + disk->pentry_size)); +error: + return NULL; +} + +//Update CRC values for the various components of the gpt_disk +//structure. This function should be called after any of the fields +//have been updated before the structure contents are written back to +//disk. +int gpt_disk_update_crc(struct gpt_disk *disk) +{ + uint32_t gpt_header_size = 0; + if (!disk || (disk->is_initialized != GPT_DISK_INIT_MAGIC)) { + fprintf(stderr, "%s: invalid argument\n", __func__); + goto error; + } + //Recalculate the CRC of the primary partiton array + disk->pentry_arr_crc = 0; // crc32(0, + //disk->pentry_arr, + //disk->pentry_arr_size); + //Recalculate the CRC of the backup partition array + disk->pentry_arr_bak_crc = 0; // crc32(0, + //disk->pentry_arr_bak, + //disk->pentry_arr_size); + //Update the partition CRC value in the primary GPT header + PUT_4_BYTES(disk->hdr + PARTITION_CRC_OFFSET, disk->pentry_arr_crc); + //Update the partition CRC value in the backup GPT header + PUT_4_BYTES(disk->hdr_bak + PARTITION_CRC_OFFSET, + disk->pentry_arr_bak_crc); + //Update the CRC value of the primary header + gpt_header_size = GET_4_BYTES(disk->hdr + HEADER_SIZE_OFFSET); + //Header CRC is calculated with its own CRC field set to 0 + PUT_4_BYTES(disk->hdr + HEADER_CRC_OFFSET, 0); + PUT_4_BYTES(disk->hdr_bak + HEADER_CRC_OFFSET, 0); + disk->hdr_crc = 0; // crc32(0, disk->hdr, gpt_header_size); + disk->hdr_bak_crc = 0; // crc32(0, disk->hdr_bak, gpt_header_size); + PUT_4_BYTES(disk->hdr + HEADER_CRC_OFFSET, disk->hdr_crc); + PUT_4_BYTES(disk->hdr_bak + HEADER_CRC_OFFSET, disk->hdr_bak_crc); + return 0; +error: + return -1; +} + +//Write the contents of struct gpt_disk back to the actual disk +int gpt_disk_commit(struct gpt_disk *disk) +{ + int fd = -1; + if (!disk || (disk->is_initialized != GPT_DISK_INIT_MAGIC)){ + fprintf(stderr, "%s: Invalid args\n", __func__); + goto error; + } + fd = open(disk->devpath, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: Failed to open %s: %s\n", + __func__, + disk->devpath, + strerror(errno)); + goto error; + } + printf("%s: Writing back primary GPT header\n", __func__); + //Write the primary header + if(gpt_set_header(disk->hdr, fd, PRIMARY_GPT) != 0) { + fprintf(stderr, "%s: Failed to update primary GPT header\n", + __func__); + goto error; + } + printf("%s: Writing back primary partition array\n", __func__); + //Write back the primary partition array + if (gpt_set_pentry_arr(disk->hdr, fd, disk->pentry_arr)) { + fprintf(stderr, "%s: Failed to write primary GPT partition arr\n", + __func__); + goto error; + } + fsync(fd); + close(fd); + return 0; +error: + if (fd >= 0) + close(fd); + return -1; +} diff --git a/gpt-utils.h b/gpt-utils.h new file mode 100644 index 0000000..7f296a6 --- /dev/null +++ b/gpt-utils.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __GPT_UTILS_H__ +#define __GPT_UTILS_H__ +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +/****************************************************************************** + * GPT HEADER DEFINES + ******************************************************************************/ +#define GPT_SIGNATURE "EFI PART" +#define HEADER_SIZE_OFFSET 12 +#define HEADER_CRC_OFFSET 16 +#define PRIMARY_HEADER_OFFSET 24 +#define BACKUP_HEADER_OFFSET 32 +#define FIRST_USABLE_LBA_OFFSET 40 +#define LAST_USABLE_LBA_OFFSET 48 +#define PENTRIES_OFFSET 72 +#define PARTITION_COUNT_OFFSET 80 +#define PENTRY_SIZE_OFFSET 84 +#define PARTITION_CRC_OFFSET 88 + +#define TYPE_GUID_OFFSET 0 +#define TYPE_GUID_SIZE 16 +#define PTN_ENTRY_SIZE 128 +#define UNIQUE_GUID_OFFSET 16 +#define FIRST_LBA_OFFSET 32 +#define LAST_LBA_OFFSET 40 +#define ATTRIBUTE_FLAG_OFFSET 48 +#define PARTITION_NAME_OFFSET 56 +#define MAX_GPT_NAME_SIZE 72 + +/****************************************************************************** + * AB RELATED DEFINES + ******************************************************************************/ +//Bit 48 onwords in the attribute field are the ones where we are allowed to +//store our AB attributes. +#define AB_FLAG_OFFSET (ATTRIBUTE_FLAG_OFFSET + 6) +#define GPT_DISK_INIT_MAGIC 0xABCD +#define AB_PARTITION_ATTR_SLOT_ACTIVE (0x1<<2) +#define AB_PARTITION_ATTR_BOOT_SUCCESSFUL (0x1<<6) +#define AB_PARTITION_ATTR_UNBOOTABLE (0x1<<7) +#define AB_SLOT_ACTIVE_VAL 0xF +#define AB_SLOT_INACTIVE_VAL 0x0 +#define AB_SLOT_ACTIVE 1 +#define AB_SLOT_INACTIVE 0 +#define AB_SLOT_A_SUFFIX "_a" +#define AB_SLOT_B_SUFFIX "_b" +#define PTN_XBL "xbl" +#define PTN_SWAP_LIST PTN_XBL, \ + "abl", "aop", "apdp", "cmnlib", "cmnlib64", \ + "devcfg", "dtbo", "hyp", "keymaster", "msadp", \ + "qupfw", "storsec", "tz", "vbmeta", "vbmeta_system", "xbl_config" + +#define AB_PTN_LIST PTN_SWAP_LIST, "boot", "system", "vendor", "modem", "system_ext", "product" +#define BOOT_DEV_DIR "/dev/disk/by-partlabel" + +/****************************************************************************** + * HELPER MACROS + ******************************************************************************/ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +/****************************************************************************** + * TYPES + ******************************************************************************/ +enum boot_update_stage { + UPDATE_MAIN = 1, + UPDATE_BACKUP, + UPDATE_FINALIZE +}; + +enum gpt_instance { + PRIMARY_GPT = 0, + SECONDARY_GPT +}; + +enum boot_chain { + NORMAL_BOOT = 0, + BACKUP_BOOT +}; + +struct gpt_disk { + //GPT primary header + uint8_t *hdr; + //primary header crc + uint32_t hdr_crc; + //GPT backup header + uint8_t *hdr_bak; + //backup header crc + uint32_t hdr_bak_crc; + //Partition entries array + uint8_t *pentry_arr; + //Partition entries array for backup table + uint8_t *pentry_arr_bak; + //Size of the pentry array + uint32_t pentry_arr_size; + //Size of each element in the pentry array + uint32_t pentry_size; + //CRC of the partition entry array + uint32_t pentry_arr_crc; + //CRC of the backup partition entry array + uint32_t pentry_arr_bak_crc; + //Path to block dev representing the disk + char devpath[PATH_MAX]; + //Block size of disk + uint32_t block_size; + uint32_t is_initialized; +}; + +/****************************************************************************** + * FUNCTION PROTOTYPES + ******************************************************************************/ +int prepare_boot_update(enum boot_update_stage stage); +//GPT disk methods +struct gpt_disk* gpt_disk_alloc(); +//Free previously allocated gpt_disk struct +void gpt_disk_free(struct gpt_disk *disk); +//Get the details of the disk holding the partition whose name +//is passed in via dev +int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk); + +//Get pointer to partition entry from a allocated gpt_disk structure +uint8_t* gpt_disk_get_pentry(struct gpt_disk *disk, + const char *partname, + enum gpt_instance instance); + +//Update the crc fields of the modified disk structure +int gpt_disk_update_crc(struct gpt_disk *disk); + +//Write the contents of struct gpt_disk back to the actual disk +int gpt_disk_commit(struct gpt_disk *disk); + +//Return if the current device is UFS based or not +int gpt_utils_is_ufs_device(); + +//Swtich betwieen using either the primary or the backup +//boot LUN for boot. This is required since UFS boot partitions +//cannot have a backup GPT which is what we use for failsafe +//updates of the other 'critical' partitions. This function will +//not be invoked for emmc targets and on UFS targets is only required +//to be invoked for XBL. +// +//The algorithm to do this is as follows: +//- Find the real block device(eg: /dev/block/sdb) that corresponds +// to the /dev/block/bootdevice/by-name/xbl(bak) symlink +// +//- Once we have the block device 'node' name(sdb in the above example) +// use this node to to locate the scsi generic device that represents +// it by checking the file /sys/block/sdb/device/scsi_generic/sgY +// +//- Once we locate sgY we call the query ioctl on /dev/sgy to switch +//the boot lun to either LUNA or LUNB +int gpt_utils_set_xbl_boot_partition(enum boot_chain chain); + +//Given a vector of partition names as a input and a reference to a map, +//populate the map to indicate which physical disk each of the partitions +//sits on. The key in the map is the path to the block device where the +//partition lies and the value is a vector of strings indicating which of +//the passed in partition names sits on that device. +int gpt_utils_get_partition_map(std::vector& partition_list, + std::map>& partition_map); +#ifdef __cplusplus +} +#endif +#endif /* __GPT_UTILS_H__ */ diff --git a/include/scsi/ufs/ioctl.h b/include/scsi/ufs/ioctl.h new file mode 100644 index 0000000..524ca86 --- /dev/null +++ b/include/scsi/ufs/ioctl.h @@ -0,0 +1,30 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef UAPI_UFS_IOCTL_H_ +#define UAPI_UFS_IOCTL_H_ +#include +#define UFS_IOCTL_QUERY 0x5388 +struct ufs_ioctl_query_data { + __u32 opcode; + __u8 idn; + __u16 buf_size; + __u8 buffer[0]; +}; +#endif + diff --git a/include/scsi/ufs/ufs.h b/include/scsi/ufs/ufs.h new file mode 100644 index 0000000..d15229d --- /dev/null +++ b/include/scsi/ufs/ufs.h @@ -0,0 +1,86 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef UAPI_UFS_H_ +#define UAPI_UFS_H_ +#define MAX_QUERY_IDN 0x18 +enum flag_idn { + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_PERMANENT_WPE = 0x02, + QUERY_FLAG_IDN_PWR_ON_WPE = 0x03, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, + QUERY_FLAG_IDN_RESERVED1 = 0x05, + QUERY_FLAG_IDN_PURGE_ENABLE = 0x06, + QUERY_FLAG_IDN_RESERVED2 = 0x07, + QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08, + QUERY_FLAG_IDN_BUSY_RTC = 0x09, + QUERY_FLAG_IDN_MANUAL_GC_CONT = 0x0E, +}; +enum attr_idn { + QUERY_ATTR_IDN_BOOT_LU_EN = 0x00, + QUERY_ATTR_IDN_RESERVED = 0x01, + QUERY_ATTR_IDN_POWER_MODE = 0x02, + QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, + QUERY_ATTR_IDN_OOO_DATA_EN = 0x04, + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_PURGE_STATUS = 0x06, + QUERY_ATTR_IDN_MAX_DATA_IN = 0x07, + QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08, + QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09, + QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A, + QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B, + QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS = 0x0E, + QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F, + QUERY_ATTR_IDN_CNTX_CONF = 0x10, + QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11, + QUERY_ATTR_IDN_MANUAL_GC_STATUS = 0x17, +}; +#define QUERY_ATTR_IDN_BOOT_LU_EN_MAX 0x02 +enum desc_idn { + QUERY_DESC_IDN_DEVICE = 0x0, + QUERY_DESC_IDN_CONFIGURAION = 0x1, + QUERY_DESC_IDN_UNIT = 0x2, + QUERY_DESC_IDN_RFU_0 = 0x3, + QUERY_DESC_IDN_INTERCONNECT = 0x4, + QUERY_DESC_IDN_STRING = 0x5, + QUERY_DESC_IDN_RFU_1 = 0x6, + QUERY_DESC_IDN_GEOMETRY = 0x7, + QUERY_DESC_IDN_POWER = 0x8, + QUERY_DESC_IDN_HEALTH = 0x9, + QUERY_DESC_IDN_RFU_2 = 0xA, + QUERY_DESC_IDN_MAX, +}; +enum query_opcode { + UPIU_QUERY_OPCODE_NOP = 0x0, + UPIU_QUERY_OPCODE_READ_DESC = 0x1, + UPIU_QUERY_OPCODE_WRITE_DESC = 0x2, + UPIU_QUERY_OPCODE_READ_ATTR = 0x3, + UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4, + UPIU_QUERY_OPCODE_READ_FLAG = 0x5, + UPIU_QUERY_OPCODE_SET_FLAG = 0x6, + UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7, + UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, + UPIU_QUERY_OPCODE_MAX, +}; +#define UPIU_QUERY_OPCODE_HIGH_HPB 0x5500 +#define UPIU_QUERY_OPCODE_HIGH(opcode) ((opcode) >> 16) +#define UPIU_QUERY_OPCODE_LOW(opcode) ((opcode) & 0xffff) +#endif +