blob: 69629ee46dc310685f40da171ed3cd807a66e5bb [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/logging.hpp>
#include <flasher/ops.hpp>
#include <algorithm>
#include <cstddef>
#include <format>
#include <stdexcept>
#include <vector>
namespace flasher
{
namespace ops
{
static void readAtExact(Device& dev, std::span<std::byte> buf, size_t off)
{
dev.readAtExact(buf, off);
LOG(LogLevel::Info, " RD@{}#{}", off, buf.size());
}
static void writeAtExact(Device& dev, std::span<const std::byte> data,
size_t off)
{
dev.writeAtExact(data, off);
LOG(LogLevel::Info, " WD@{}#{}", off, data.size());
}
static void eraseBlocks(Device& dev, size_t off, size_t size)
{
dev.eraseBlocks(off, size);
LOG(LogLevel::Info, " ED@{}#{}", dev.getEraseSize() * off,
dev.getEraseSize() * size);
}
void erase(Device& dev, size_t dev_offset, size_t max_size,
std::optional<size_t> stride_size, bool noread)
{
size_t stride = stride_size ? *stride_size : dev.recommendedStride();
if (stride == 0)
{
throw std::invalid_argument("Stride cannot be 0");
}
stride = dev.eraseAlignEnd(stride);
if (dev_offset > dev.getSize())
{
throw std::invalid_argument(std::format(
"Device smaller than offset, {} < {}", dev.getSize(), dev_offset));
}
max_size = std::min(max_size, dev.getSize() - dev_offset);
if (max_size == 0)
{
LOG(LogLevel::Info, "\n");
return;
}
std::vector<std::byte> buf_v(stride * 2), erased_v(stride * 2);
dev.mockErase(erased_v);
const std::span<std::byte> buf(buf_v), erased(erased_v);
const auto erase_size = dev.getEraseSize();
const auto start = dev.eraseAlignStart(dev_offset);
if (start != dev_offset)
{
const auto start_size = dev_offset - start;
max_size += start_size;
auto erase = true;
if (noread)
{
readAtExact(dev, buf.subspan(0, start_size), start);
if (max_size < erase_size)
{
readAtExact(dev, buf.subspan(max_size, erase_size - max_size),
start + max_size);
}
}
else
{
readAtExact(dev, buf.subspan(0, erase_size), start);
const auto cmp_size = std::min(max_size, erase_size) - start_size;
if (!dev.needsErase(buf.subspan(start_size, cmp_size),
erased.subspan(start_size, cmp_size)))
{
erase = false;
}
}
if (erase)
{
eraseBlocks(dev, start / erase_size, 1);
writeAtExact(dev, buf.subspan(0, start_size), start);
if (max_size < erase_size)
{
writeAtExact(dev, buf.subspan(max_size, erase_size - max_size),
start + max_size);
}
}
if (max_size < erase_size)
{
LOG(LogLevel::Info, "\n");
return;
}
dev_offset = start + erase_size;
max_size -= erase_size;
}
const auto uend = dev_offset + max_size;
const auto end = dev.eraseAlignEnd(uend);
if (end != uend)
{
const auto end_size = end - uend;
auto erase = true;
if (noread)
{
readAtExact(dev, buf.subspan(erase_size - end_size, end_size),
uend);
}
else
{
readAtExact(dev, buf.subspan(0, erase_size), end - erase_size);
if (!dev.needsErase(buf.subspan(0, erase_size - end_size),
erased.subspan(0, erase_size - end_size)))
{
erase = false;
}
}
if (erase)
{
eraseBlocks(dev, end / erase_size - 1, 1);
writeAtExact(dev, buf.subspan(erase_size - end_size, end_size),
uend);
}
max_size -= erase_size - end_size;
}
for (size_t i = dev_offset; i < max_size + dev_offset; i += stride)
{
const size_t bytes = std::min(stride, max_size + dev_offset - i);
if (!noread)
{
readAtExact(dev, buf.subspan(0, bytes), i);
if (!dev.needsErase(buf.subspan(0, bytes),
erased.subspan(0, bytes)))
{
continue;
}
}
eraseBlocks(dev, i / erase_size, bytes / erase_size);
}
LOG(LogLevel::Info, "\n");
}
} // namespace ops
} // namespace flasher