blob: 2ec578964c65959c5d98a24f2db2f3c2bf11849e [file] [log] [blame]
// Copyright 2020 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_image_internal.h"
#include <lib/stdcompat/span.h>
#include <iostream>
#include <limits>
#include <vector>
#include <fbl/algorithm.h>
#include <safemath/safe_conversions.h>
#include "src/storage/volume_image/ftl/options.h"
#include "src/storage/volume_image/ftl/raw_nand_image_utils.h"
namespace storage::volume_image::ftl_image_internal {
namespace {
// Writes |value| into |sink|, in little endian, as expected by the FTL.
template <typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = true>
void WriteValue(const T value, cpp20::span<uint8_t> sink) {
for (size_t i = 0; i < sink.size(); ++i) {
uint8_t byte = (value >> (i * 8)) & 0xFF;
sink[i] = byte;
}
}
// Fills |oob_bytes| with the expected FTL data.
void FillOutOfBandBytes(uint32_t logical_page_number, uint32_t generation_number,
cpp20::span<uint8_t> oob_bytes) {
// Reset the contents of |oob| to unprogrammed state.
std::fill(oob_bytes.begin(), oob_bytes.end(), 0xFF);
// Mark the block as not bad. This only matters for the first page in the block,
// but is innocuous in the other pages.
// Note: Here for clarification.
oob_bytes[0] = 0xFF;
// Fill the logical page number.
WriteValue(logical_page_number, oob_bytes.subspan(1, 4));
WriteValue(generation_number, oob_bytes.subspan(5, 4));
// Write the wear count, that has a length of 28 bits.
// We write the first 3 bytes as is, then write the following most significant 4 bits in the 12-th
// byte.
WriteValue(kFtlPageWearCount, oob_bytes.subspan(9, 3));
oob_bytes[12] = (oob_bytes[12] & 0xF) | ((kFtlPageWearCount >> 20) & 0xF0);
// Add the NDM marking this page as a valid volume page.
oob_bytes[15] = kNdmVolumePageMark;
}
} // namespace
template <>
void WriteOutOfBandBytes<PageType::kVolumePage>(uint32_t logical_page_number,
cpp20::span<uint8_t> oob_bytes) {
// Volume pages have the generation number ''unprogrammed'' where all bits are set.
FillOutOfBandBytes(logical_page_number, std::numeric_limits<uint32_t>::max(), oob_bytes);
}
template <>
void WriteOutOfBandBytes<PageType::kMapPage>(uint32_t logical_page_number,
cpp20::span<uint8_t> oob_bytes) {
// Generated images have the first version of the map pages, with generation number 0.
FillOutOfBandBytes(logical_page_number, 0, oob_bytes);
}
fpromise::result<void, std::string> WriteMapBlock(
const std::map<uint32_t, uint32_t>& logical_to_physical_pages,
const RawNandOptions& ftl_options, uint64_t offset, Writer* writer) {
if (ftl_options.page_size < 4) {
return fpromise::error("Page Size must be greater than 4 bytes.");
}
if (ftl_options.oob_bytes_size < 16) {
return fpromise::error("OOB Size must be greater or equal to 16 bytes per page.");
}
uint32_t mappings_per_page =
safemath::checked_cast<uint32_t>(ftl_options.page_size / sizeof(uint32_t));
uint32_t total_map_pages =
fbl::round_up(ftl_options.page_count, mappings_per_page) / mappings_per_page;
std::vector<uint8_t> page_buffer(ftl_options.page_size, 0xFF);
std::vector<uint8_t> oob_bytes_buffer(ftl_options.oob_bytes_size, 0xFF);
uint64_t written_pages = 0;
for (uint32_t map_page_number = 0; map_page_number < total_map_pages; ++map_page_number) {
std::fill(page_buffer.begin(), page_buffer.end(), 0xFF);
uint32_t lower_bound = map_page_number * mappings_per_page;
uint32_t upper_bound = lower_bound + mappings_per_page - 1;
auto begin = logical_to_physical_pages.lower_bound(lower_bound);
auto end = logical_to_physical_pages.upper_bound(upper_bound);
// Fill mappings in this page range.
for (auto it = begin; it != end; ++it) {
auto [logical_page, physical_page] = *it;
size_t mapping_page_offset = logical_page - lower_bound;
size_t map_page_offset = (mapping_page_offset % mappings_per_page) * sizeof(uint32_t);
WriteValue(physical_page,
cpp20::span<uint8_t>(page_buffer.data() + map_page_offset, sizeof(uint32_t)));
}
// Only write map pages that have mappings.
if (begin == end) {
continue;
}
WriteOutOfBandBytes<PageType::kMapPage>(map_page_number, oob_bytes_buffer);
auto write_result = RawNandImageWritePage(
page_buffer, oob_bytes_buffer,
offset + RawNandImageGetPageOffset(written_pages, ftl_options), writer);
if (write_result.is_error()) {
return write_result.take_error_result();
}
written_pages++;
}
return fpromise::ok();
}
} // namespace storage::volume_image::ftl_image_internal