blob: 4b57691ed4b356c51bad9d197b1c2d9328880241 [file] [log] [blame]
// Copyright 2018 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.
#pragma once
#include <stdint.h>
struct ndm;
namespace ftl {
class Volume;
// Return values expected by NDM from the nand driver.
// See ndm-man-20150.pdf for the complete low level API specification:
constexpr int kNdmOk = 0;
constexpr int kNdmError = -1;
constexpr int kNdmUncorrectableEcc = -1;
constexpr int kNdmFatalError = -2;
constexpr int kNdmUnsafeEcc = 1;
constexpr int kTrue = 1;
constexpr int kFalse = 0;
// Initialization should not alter the contents of the volume.
constexpr uint32_t kReadOnlyInit = (1 << 8); // Matches FSF_READ_ONLY_INIT.
// Options for a device to be created. All sizes are in bytes.
struct VolumeOptions {
uint32_t num_blocks;
uint32_t max_bad_blocks;
uint32_t block_size;
uint32_t page_size;
uint32_t eb_size; // Extra bytes, a.k.a. OOB.
uint32_t flags;
};
// Encapsulates the lower layer TargetFtl-Ndm driver.
class NdmDriver {
public:
virtual ~NdmDriver() {}
// Performs driver initialization. Returns an error string, or nullptr on
// success.
virtual const char* Init() = 0;
// Creates a new volume. Note that multiple volumes are not supported.
// |ftl_volume| (if provided) will be notified with the volume details.
// Returns an error string, or nullptr on success.
virtual const char* Attach(const Volume* ftl_volume) = 0;
// Destroy the volume created with Attach(). Returns true on success.
virtual bool Detach() = 0;
// 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.
virtual int NandRead(uint32_t start_page, uint32_t page_count, void* page_buffer,
void* oob_buffer) = 0;
// 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.
virtual int NandWrite(uint32_t start_page, uint32_t page_count, const void* page_buffer,
const void* oob_buffer) = 0;
// Erases the block containing |page_num|.
// Returns kNdmOk or kNdmError. kNdmError triggers marking the block as bad.
virtual int NandErase(uint32_t page_num) = 0;
// Returns whether the block containing |page_num| was factory-marked as bad.
// Returns kTrue, kFalse or kNdmError.
virtual int IsBadBlock(uint32_t page_num) = 0;
// Returns whether a given page is empty or not. |data| and |spare| store
// the contents of the page.
virtual bool IsEmptyPage(uint32_t page_num, const uint8_t* data, const uint8_t* spare) = 0;
};
// Base functionality for a driver implementation.
class NdmBaseDriver : public NdmDriver {
public:
NdmBaseDriver() {}
virtual ~NdmBaseDriver();
// Returns true if known data appears to be present on the device. This does
// not imply that creating a volume will not find errors, just that calling
// CreateNdmVolume after this method returns false will result in a freshly
// minted (aka empty) volume.
// This method should be called after Init(), but before CreateNdmVolume(),
// for the result to be meaningful, but calling this is not required.
bool IsNdmDataPresent(const VolumeOptions& options);
// Returns true if the size of the bad block reservation cannot be used.
// The size to use (options.max_bad_blocks) may be too small to hold the
// current known bad blocks, or some internal value may be inconsistent if
// this size is used.
// This only makes sense when comparing the desired options with the data
// already stored on a volume, and in general should only be used to attempt
// to reduce the reserved space (increasing it would reduce the size of the
// visible volume, and that is not supported).
// This method should only be called right after calling IsNdmDataPresent(),
// before calling CreateNdmVolume().
bool BadBbtReservation() const;
// Creates the underlying NDM volume, with the provided parameters.
const char* CreateNdmVolume(const Volume* ftl_volume, const VolumeOptions& options);
// Deletes the underlying NDM volume.
bool RemoveNdmVolume();
// Saves and restores bad block data for volume extension.
bool SaveBadBlockData();
bool RestoreBadBlockData();
// Inspects |data_len| bytes from |data| and |spare_len| bytes from |spare|
// looking for a typical empty (erased) page. Returns true if all bits are 1.
bool IsEmptyPageImpl(const uint8_t* data, uint32_t data_len, const uint8_t* spare,
uint32_t spare_len) const;
private:
ndm* ndm_ = nullptr;
};
// Performs global module initialization. This is exposed to support unit tests,
// and while calling this function multiple times is supported, racing calls are
// not (or more generally, calling this from multiple threads).
// If multiple simultaneous tests from the same test instance ever becomes a
// thing, this should be called from testing::Environment and not from each
// test case.
bool InitModules();
} // namespace ftl