blob: 390c4d55572184ad44fdb586691945062e206016 [file] [log] [blame]
// 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 <phosphor-logging/log.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "common.h"
#include <string>
#include <exception>
#include <stdexcept>
#include <iostream>
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