blob: 7f934502f329832f37f70b6bd47a62f668c84df7 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// 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 <flasher/device.hpp>
#include <flasher/modutil.hpp>
#include <flasher/util.hpp>
#include <algorithm>
#include <cstring>
#include <format>
#include <stdexcept>
#include <utility>
namespace flasher
{
void Device::writeAtExact(std::span<const std::byte> data, size_t offset)
{
opAtExact("Device writeAtExact", &Device::writeAt, *this, data, offset);
}
Device::Type Device::toType(std::string_view str)
{
if (str == "nor")
{
return Type::Nor;
}
if (str == "simple")
{
return Type::Simple;
}
throw std::invalid_argument(std::format("Not a device type: {}", str));
}
Device::Device(DeviceInfo&& info) : info(std::move(info))
{
if (info.erase_size != 0 && info.size % info.erase_size != 0)
{
throw std::invalid_argument(
std::format("Flash size {} is not divisible by erase size {}",
info.size, info.erase_size));
}
switch (info.type)
{
case Type::Nor:
if (info.erase_size == 0)
{
throw std::invalid_argument(
"Nor flashes can't have 0 erase size");
}
break;
case Type::Simple:
if (info.erase_size != 0)
{
throw std::invalid_argument("Simple flashes can't erase");
}
break;
}
}
size_t Device::recommendedStride() const
{
return info.erase_size == 0 ? 8192 : info.erase_size;
}
size_t Device::getSize() const
{
return info.size;
}
size_t Device::eraseAlignStart(size_t offset) const
{
if (info.erase_size == 0)
return offset;
return offset / info.erase_size * info.erase_size;
}
size_t Device::eraseAlignEnd(size_t offset) const
{
if (info.erase_size == 0)
return offset;
return eraseAlignStart(offset + info.erase_size - 1);
}
bool Device::needsErase(std::span<const std::byte> flash_data,
std::span<const std::byte> new_data) const
{
if (new_data.size() > flash_data.size())
{
throw std::invalid_argument("New data bigger than MTD data");
}
switch (info.type)
{
case Type::Nor:
for (size_t i = 0; i < new_data.size(); ++i)
{
if ((flash_data[i] & new_data[i]) != new_data[i])
{
return true;
}
}
return false;
case Type::Simple:
return false;
}
throw std::invalid_argument("Don't know how to erase Device");
}
template <typename Iter>
static size_t shrinkWrite(Device::Type type, Iter flash_data_begin,
Iter new_data_begin, Iter new_data_end)
{
switch (type)
{
case Device::Type::Nor:
for (auto itn = new_data_begin, itf = flash_data_begin;
new_data_end != itn; itn++, itf++)
{
if ((*itn & *itf) != *itf)
{
return itn - new_data_begin;
}
}
return new_data_end - new_data_begin;
case Device::Type::Simple:
for (auto itn = new_data_begin, itf = flash_data_begin;
new_data_end != itn; itn++, itf++)
{
if (*itn != *itf)
{
return itn - new_data_begin;
}
}
return new_data_end - new_data_begin;
}
throw std::invalid_argument("Don't know how to shrink write data");
}
size_t Device::shrinkWritePre(std::span<const std::byte> flash_data,
std::span<const std::byte> new_data) const
{
return shrinkWrite(info.type, flash_data.begin(), new_data.begin(),
new_data.begin() +
std::min(flash_data.size(), new_data.size()));
}
size_t Device::shrinkWritePost(std::span<const std::byte> flash_data,
std::span<const std::byte> new_data) const
{
auto max = std::min(flash_data.size(), new_data.size());
return max -
shrinkWrite(info.type, flash_data.rbegin() + flash_data.size() - max,
new_data.rbegin() + new_data.size() - max,
new_data.rend());
}
void Device::mockErase(std::span<std::byte> data) const
{
switch (info.type)
{
case Type::Nor:
memset(data.data(), 0xff, data.size());
return;
case Type::Simple:
return;
}
throw std::invalid_argument("Don't know how to erase Device");
}
ModTypeMap<DeviceType>& getDeviceTypes() noexcept
{
static ModTypeMap<DeviceType> deviceTypes;
return deviceTypes;
}
std::unique_ptr<Device> openDevice(const ModArgs& args)
{
return openMod<Device>(getDeviceTypes(), args);
}
void registerDeviceType(std::string_view name,
std::unique_ptr<DeviceType>&& type) noexcept
{
getDeviceTypes().emplace(name, std::move(type));
}
void unregisterDeviceType(std::string_view name) noexcept
{
getDeviceTypes().erase(name);
}
} // namespace flasher