blob: 1868ed757dc1924133659cb1f6e31482eb2b4652 [file] [log] [blame]
/*
* Mailbox Daemon Implementation
*
* Copyright 2018 IBM
*
* 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 "pnor_partition.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 <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
{
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
using namespace std::string_literals;
ReturnCode Request::open(const std::string& path, int mode)
{
if (mode == O_RDWR && partition->data.user.data[1] & PARTITION_READONLY)
{
MSG_ERR("Can't open RO partition '%s' for write\n", path.c_str());
return ReturnCode::PARTITION_READ_ONLY;
}
fs::path partitionFilePath = path;
if (!fs::exists(partitionFilePath))
{
return ReturnCode::FILE_NOT_FOUND;
}
auto descriptor = ::open(partitionFilePath.c_str(), mode);
if (descriptor < 0)
{
return ReturnCode::FILE_OPEN_FAILURE;
}
fd.set(descriptor);
descriptor = -1;
return ReturnCode::SUCCESS;
}
std::string Request::getPartitionFilePath(struct mbox_context* context,
uint32_t offset)
{
partition = vpnor_get_partition(context, offset);
if (!partition)
{
MSG_ERR("Couldn't get the partition info for offset 0x%" PRIx32 "\n",
offset);
elog<InternalFailure>();
}
fs::path partitionFilePath;
// Check if partition exists in patch location
partitionFilePath = context->paths.patch_loc;
partitionFilePath /= partition->data.name;
if (fs::is_regular_file(partitionFilePath))
{
return partitionFilePath.string();
}
switch (partition->data.user.data[1] &
(PARTITION_PRESERVED | PARTITION_READONLY))
{
case PARTITION_PRESERVED:
partitionFilePath = context->paths.prsv_loc;
break;
case PARTITION_READONLY:
partitionFilePath = context->paths.ro_loc;
break;
default:
partitionFilePath = context->paths.rw_loc;
}
partitionFilePath /= partition->data.name;
return partitionFilePath.string();
}
const pnor_partition* RORequest::getPartitionInfo(struct mbox_context* context,
uint32_t offset)
{
std::string path = getPartitionFilePath(context, offset);
ReturnCode rc = Request::open(path, O_RDONLY);
if (rc == ReturnCode::SUCCESS)
{
return partition;
}
// not interested in any other error except FILE_NOT_FOUND
if (rc != ReturnCode::FILE_NOT_FOUND)
{
MSG_ERR(
"RORequest: Error opening partition file '%s' (offset 0x%" PRIx32
"): %u\n",
path.c_str(), offset, static_cast<uint8_t>(rc));
elog<InternalFailure>();
}
// if the offset lies in read only partition then throw error.
if (partition->data.user.data[1] & PARTITION_READONLY)
{
MSG_ERR("RORequest: Requested offset 0x%" PRIx32
" is in a read-only partition (%s)\n",
offset, path.c_str());
elog<InternalFailure>();
}
// we don't get the file in the respective partition(RW/PSRV)
// try to open it from RO location.
fs::path partitionFilePath = context->paths.ro_loc;
partitionFilePath /= partition->data.name;
rc = Request::open(partitionFilePath, O_RDONLY);
if (rc != ReturnCode::SUCCESS)
{
MSG_ERR("RORequest: Failed to open partition file '%s' at RO fallback "
"(offset 0x%" PRIx32 "): %u\n",
partitionFilePath.c_str(), offset, static_cast<uint8_t>(rc));
elog<InternalFailure>();
}
return partition;
}
const pnor_partition* RWRequest::getPartitionInfo(struct mbox_context* context,
uint32_t offset)
{
std::string path = getPartitionFilePath(context, offset);
std::error_code ec;
ReturnCode rc = Request::open(path, O_RDWR);
if (rc == ReturnCode::SUCCESS)
{
return partition;
}
// not interested in any other error except FILE_NOT_FOUND
if (rc != ReturnCode::FILE_NOT_FOUND)
{
MSG_ERR(
"RWRequest: Error opening partition file '%s' (offset 0x%" PRIx32
"): %d\n",
path.c_str(), offset, static_cast<uint8_t>(rc));
elog<InternalFailure>();
}
// if the file is not available in the respective(RW/PSRV) partition
// then copy the file from RO to the respective(RW/PSRV) partition
// and open it for writing.
fs::path fromPath = context->paths.ro_loc;
fromPath /= partition->data.name;
if (!fs::exists(fromPath))
{
MSG_ERR("RWRequest: Partition '%s' for offset 0x%" PRIx32
" not found in RO directory '%s'\n",
partition->data.name, offset, context->paths.ro_loc);
elog<InternalFailure>();
}
// copy the file from ro to respective partition
fs::path toPath = context->paths.rw_loc;
if (partition->data.user.data[1] & PARTITION_PRESERVED)
{
toPath = context->paths.prsv_loc;
}
MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n",
partition->data.name, toPath.c_str(), fromPath.c_str());
toPath /= partition->data.name;
if (!fs::copy_file(fromPath, toPath, ec))
{
MSG_ERR("RWRequest: Failed to copy file from '%s' to '%s': %d\n",
fromPath.c_str(), toPath.c_str(), ec.value());
elog<InternalFailure>();
}
rc = Request::open(toPath.c_str(), O_RDWR);
if (rc != ReturnCode::SUCCESS)
{
MSG_ERR("RWRequest: Failed to open file at '%s': %d\n", toPath.c_str(),
static_cast<uint8_t>(rc));
elog<InternalFailure>();
}
return partition;
}
} // namespace virtual_pnor
} // namespace openpower