// SPDX-License-Identifier: Apache-2.0 // Copyright (C) 2018 IBM Corp. #include "mboxd_pnor_partition_table.h" #include "pnor_partition_table.hpp" #include "common.h" #include "mbox.h" #include "mboxd_flash.h" #include "pnor_partition_table.hpp" #include "config.h" #include "xyz/openbmc_project/Common/error.hpp" #include #include int init_vpnor(struct mbox_context *context) { if (context && !context->vpnor) { int rc; strncpy(context->paths.ro_loc, PARTITION_FILES_RO_LOC, PATH_MAX); context->paths.ro_loc[PATH_MAX - 1] = '\0'; strncpy(context->paths.rw_loc, PARTITION_FILES_RW_LOC, PATH_MAX); context->paths.rw_loc[PATH_MAX - 1] = '\0'; strncpy(context->paths.prsv_loc, PARTITION_FILES_PRSV_LOC, PATH_MAX); context->paths.prsv_loc[PATH_MAX - 1] = '\0'; strncpy(context->paths.patch_loc, PARTITION_FILES_PATCH_LOC, PATH_MAX); context->paths.prsv_loc[PATH_MAX - 1] = '\0'; rc = vpnor_create_partition_table_from_path(context, PARTITION_FILES_RO_LOC); if (rc < 0) return rc; } return 0; } int vpnor_create_partition_table_from_path(struct mbox_context *context, const char *path) { namespace err = sdbusplus::xyz::openbmc_project::Common::Error; namespace fs = std::experimental::filesystem; namespace vpnor = openpower::virtual_pnor; if (context && !context->vpnor) { fs::path dir(path); try { context->vpnor = new vpnor_partition_table; context->vpnor->table = new openpower::virtual_pnor::partition::Table( std::move(dir), 1 << context->erase_size_shift, context->flash_size); } catch (vpnor::TocEntryError &e) { MSG_ERR("%s\n", e.what()); phosphor::logging::commit(); return -MBOX_R_SYSTEM_ERROR; } } return 0; } size_t vpnor_get_partition_table_size(const struct mbox_context *context) { return context && context->vpnor ? context->vpnor->table->blocks() : 0; } const struct pnor_partition_table * vpnor_get_partition_table(const struct mbox_context *context) { return context && context->vpnor ? &(context->vpnor->table->getHostTable()) : nullptr; } const struct pnor_partition * vpnor_get_partition(const struct mbox_context *context, const size_t offset) { return context && context->vpnor ? &(context->vpnor->table->partition(offset)) : nullptr; } int vpnor_copy_bootloader_partition(const struct mbox_context *context) { // The hostboot bootloader has certain size/offset assumptions, so // we need a special partition table here. // It assumes the PNOR is 64M, the TOC size is 32K, the erase block is // 4K, the page size is 4K. // It also assumes the TOC is at the 'end of pnor - toc size - 1 page size' // offset, and first looks for the TOC here, before proceeding to move up // page by page looking for the TOC. So it is optimal to place the TOC at // this offset. constexpr size_t eraseSize = 0x1000; constexpr size_t pageSize = 0x1000; constexpr size_t pnorSize = 0x4000000; constexpr size_t tocMaxSize = 0x8000; constexpr size_t tocStart = pnorSize - tocMaxSize - pageSize; constexpr auto blPartitionName = "HBB"; namespace err = sdbusplus::xyz::openbmc_project::Common::Error; namespace fs = std::experimental::filesystem; namespace vpnor = openpower::virtual_pnor; try { openpower::virtual_pnor::partition::Table blTable( fs::path{PARTITION_FILES_RO_LOC}, eraseSize, pnorSize); vpnor_partition_table vtbl{}; vtbl.table = &blTable; struct mbox_context local { }; local.vpnor = &vtbl; local.block_size_shift = log_2(eraseSize); memcpy(&local.paths, &context->paths, sizeof(local.paths)); size_t tocOffset = 0; // Copy TOC copy_flash(&local, tocOffset, static_cast(context->mem) + tocStart, blTable.capacity()); const pnor_partition &partition = blTable.partition(blPartitionName); size_t hbbOffset = partition.data.base * eraseSize; uint32_t hbbSize = partition.data.actual; // Copy HBB copy_flash(&local, hbbOffset, static_cast(context->mem) + hbbOffset, hbbSize); } catch (err::InternalFailure &e) { phosphor::logging::commit(); return -MBOX_R_SYSTEM_ERROR; } catch (vpnor::TocEntryError &e) { MSG_ERR("%s\n", e.what()); phosphor::logging::commit(); return -MBOX_R_SYSTEM_ERROR; } return 0; } void destroy_vpnor(struct mbox_context *context) { if (context && context->vpnor) { delete context->vpnor->table; delete context->vpnor; context->vpnor = nullptr; } }