| // 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 |