| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/storage/volume_image/ftl/ftl_test_helper.h" |
| |
| #include <lib/stdcompat/span.h> |
| |
| #include <iostream> |
| |
| #include <fbl/algorithm.h> |
| |
| namespace storage::volume_image { |
| |
| // |ftl_volume| (if provided) will be notified with the volume details. |
| // Returns an error string, or nullptr on success. |
| const char* InMemoryNdm::Attach(const ftl::Volume* ftl_volume) { |
| ftl::VolumeOptions options; |
| options.block_size = |
| static_cast<uint32_t>(raw_nand_->options.page_size * raw_nand_->options.pages_per_block); |
| options.eb_size = raw_nand_->options.oob_bytes_size; |
| options.max_bad_blocks = max_bad_blocks_; |
| if (raw_nand_->options.page_count % raw_nand_->options.pages_per_block != 0) { |
| return "InMemoryNdm::Attach page_count not divisble by pages_per_block."; |
| } |
| options.num_blocks = raw_nand_->options.page_count / raw_nand_->options.pages_per_block; |
| options.page_size = static_cast<uint32_t>(raw_nand_->options.page_size); |
| options.flags = 0; |
| return CreateNdmVolume(ftl_volume, options); |
| } |
| |
| // Reads |page_count| pages starting at |start_page|, placing the results on |
| // |page_buffer| and |oob_buffer|. Either pointer can be nullptr if that |
| // part is not desired. |
| // Returns kNdmOk, kNdmUncorrectableEcc, kNdmFatalError or kNdmUnsafeEcc. |
| int InMemoryNdm::NandRead(uint32_t start_page, uint32_t page_count, void* page_buffer, |
| void* oob_buffer) { |
| for (uint32_t i = 0; i < page_count; ++i) { |
| uint32_t page_number = start_page + i; |
| size_t page_offset = i * page_size_; |
| size_t oob_offset = i * oob_size_; |
| |
| if (raw_nand_->page_data.find(page_number) == raw_nand_->page_data.end()) { |
| if (page_buffer != nullptr) { |
| auto page_view = |
| cpp20::span<uint8_t>(reinterpret_cast<uint8_t*>(page_buffer) + page_offset, page_size_); |
| std::fill(page_view.begin(), page_view.end(), 0xFF); |
| } |
| if (oob_buffer != nullptr) { |
| auto oob_view = |
| cpp20::span<uint8_t>(reinterpret_cast<uint8_t*>(oob_buffer) + oob_offset, oob_size_); |
| std::fill(oob_view.begin(), oob_view.end(), 0xFF); |
| } |
| } else { |
| if (page_buffer != nullptr) { |
| auto page_view = |
| cpp20::span<uint8_t>(reinterpret_cast<uint8_t*>(page_buffer) + page_offset, page_size_); |
| memcpy(page_view.data(), raw_nand_->page_data.at(page_number).data(), page_view.size()); |
| } |
| |
| if (oob_buffer != nullptr) { |
| auto oob_view = |
| cpp20::span<uint8_t>(reinterpret_cast<uint8_t*>(oob_buffer) + oob_offset, oob_size_); |
| memcpy(oob_view.data(), raw_nand_->page_oob.at(page_number).data(), oob_view.size()); |
| } |
| } |
| } |
| return ftl::kNdmOk; |
| } |
| |
| // Writes |page_count| pages starting at |start_page|, using the data from |
| // |page_buffer| and |oob_buffer|. |
| // Returns kNdmOk, kNdmError or kNdmFatalError. kNdmError triggers marking |
| // the block as bad. |
| int InMemoryNdm::NandWrite(uint32_t start_page, uint32_t page_count, const void* page_buffer, |
| const void* oob_buffer) { |
| for (uint32_t i = 0; i < page_count; ++i) { |
| uint32_t page_number = start_page + i; |
| size_t page_offset = i * page_size_; |
| size_t oob_offset = i * oob_size_; |
| auto page_view = cpp20::span<const uint8_t>( |
| reinterpret_cast<const uint8_t*>(page_buffer) + page_offset, raw_nand_->options.page_size); |
| auto oob_view = |
| cpp20::span<const uint8_t>(reinterpret_cast<const uint8_t*>(oob_buffer) + oob_offset, |
| raw_nand_->options.oob_bytes_size); |
| if (page_buffer != nullptr) { |
| raw_nand_->page_data[page_number] = std::vector<uint8_t>(page_view.begin(), page_view.end()); |
| } |
| |
| if (oob_buffer != nullptr) { |
| raw_nand_->page_oob[page_number] = std::vector<uint8_t>(oob_view.begin(), oob_view.end()); |
| } |
| } |
| |
| return ftl::kNdmOk; |
| } |
| |
| // Erases the block containing |page_num|. |
| // Returns kNdmOk or kNdmError. kNdmError triggers marking the block as bad. |
| int InMemoryNdm::NandErase(uint32_t page_num) { |
| uint32_t page_start = fbl::round_down(page_num, raw_nand_->options.pages_per_block); |
| for (uint32_t i = 0; i < raw_nand_->options.pages_per_block; ++i) { |
| raw_nand_->page_data.erase(page_start + i); |
| raw_nand_->page_oob.erase(page_start + i); |
| } |
| return ftl::kNdmOk; |
| } |
| |
| // Returns whether a given page is empty or not. |data| and |spare| store |
| // the contents of the page. |
| bool InMemoryNdm::IsEmptyPage(uint32_t page_num, const uint8_t* data, const uint8_t* spare) const { |
| auto page_view = cpp20::span<const uint8_t>(reinterpret_cast<const uint8_t*>(data), page_size_); |
| auto oob_view = cpp20::span<const uint8_t>(reinterpret_cast<const uint8_t*>(spare), oob_size_); |
| |
| return std::all_of(oob_view.begin(), oob_view.end(), [](const auto& b) { return b == 0xFF; }) && |
| std::all_of(page_view.begin(), page_view.end(), [](const auto& b) { return b == 0xFF; }); |
| } |
| |
| } // namespace storage::volume_image |