// SPDX-License-Identifier: Apache-2.0 // Copyright (C) 2018 IBM Corp. #include "pnor_partition.hpp" #include "pnor_partition_table.hpp" #include "config.h" #include "mboxd_flash.h" #include "mboxd_pnor_partition_table.h" #include "xyz/openbmc_project/Common/error.hpp" #include #include #include #include #include #include #include #include #include "common.h" #include #include #include #include namespace openpower { namespace virtual_pnor { namespace fs = std::experimental::filesystem; fs::path Request::getPartitionFilePath(int flags) { // Check if partition exists in patch location auto dst = fs::path(ctx->paths.patch_loc) / partition.data.name; if (fs::is_regular_file(dst)) { return dst; } switch (partition.data.user.data[1] & (PARTITION_PRESERVED | PARTITION_READONLY)) { case PARTITION_PRESERVED: dst = ctx->paths.prsv_loc; break; case PARTITION_READONLY: dst = ctx->paths.ro_loc; break; default: dst = ctx->paths.rw_loc; } dst /= partition.data.name; if (fs::exists(dst)) { return dst; } if (flags == O_RDONLY) { dst = fs::path(ctx->paths.ro_loc) / partition.data.name; assert(fs::exists(dst)); return dst; } assert(flags == O_RDWR); auto src = fs::path(ctx->paths.ro_loc) / partition.data.name; assert(fs::exists(src)); MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n", partition.data.name, dst.c_str(), src.c_str()); dst = ctx->paths.rw_loc; if (partition.data.user.data[1] & PARTITION_PRESERVED) { dst = ctx->paths.prsv_loc; } dst /= partition.data.name; fs::copy_file(src, dst); return dst; } ssize_t Request::fulfil(void *buf, size_t len, int flags) { if (!(flags == O_RDONLY || flags == O_RDWR)) { std::stringstream err; err << "Require O_RDONLY (0x" << std::hex << O_RDONLY << " or O_RDWR " << std::hex << O_RDWR << " for flags, got: 0x" << std::hex << flags; throw std::invalid_argument(err.str()); } fs::path path = getPartitionFilePath(flags); int fd = ::open(path.c_str(), flags); if (fd == -1) { MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(), errno); throw std::system_error(errno, std::system_category()); } int mprot = PROT_READ | ((flags == O_RDWR) ? PROT_WRITE : 0); auto map = mmap(NULL, partition.data.actual, mprot, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { close(fd); MSG_ERR("Failed to map backing file '%s' for %d bytes: %d\n", path.c_str(), partition.data.actual, errno); throw std::system_error(errno, std::system_category()); } // copy to the reserved memory area if (flags == O_RDONLY) { len = std::min(partition.data.actual - offset, len); memcpy(buf, (char *)map + offset, len); } else { // if the asked offset + no of bytes to read is greater // then size of the partition file then throw error. // // FIXME: Don't use .actual, use (.size << ctx->block_size_shift), // otherwise we can't grow the size of the data to fill the partition if ((base + offset + len) > (base + partition.data.actual)) { munmap(map, partition.data.actual); close(fd); /* FIXME: offset calculation */ std::stringstream err; err << "Request size 0x" << std::hex << len << " from offset 0x" << std::hex << offset << " exceeds the partition size 0x" << std::hex << partition.data.actual; throw OutOfBoundsOffset(err.str()); } memcpy((char *)map + offset, buf, len); set_flash_bytemap(ctx, base + offset, len, FLASH_DIRTY); } munmap(map, partition.data.actual); close(fd); return len; } } // namespace virtual_pnor } // namespace openpower