1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/ftl/ndm-driver.h>
6 
7 #include <zircon/assert.h>
8 
9 #include "kprivate/ndm.h"
10 #include "posix.h"
11 #include "utils/modules.h"
12 
13 namespace ftl {
14 
15 namespace {
16 
17 bool g_init_performed = false;
18 
19 // Implementation of the driver interface:
20 
21 // Returns kNdmOk, kNdmUncorrectableEcc, kNdmFatalError or kNdmUnsafeEcc.
ReadPagesImpl(uint32_t page,uint32_t count,uint8_t * data,uint8_t * spare,void * dev)22 int ReadPagesImpl(uint32_t page, uint32_t count, uint8_t* data, uint8_t* spare, void* dev) {
23     NdmDriver* device = reinterpret_cast<NdmDriver*>(dev);
24     return device->NandRead(page, count, data, spare);
25 }
26 
27 // Returns kNdmOk, kNdmUncorrectableEcc, kNdmFatalError or kNdmUnsafeEcc.
ReadPages(uint32_t page,uint32_t count,uint8_t * data,uint8_t * spare,void * dev)28 int ReadPages(uint32_t page, uint32_t count, uint8_t* data, uint8_t* spare, void* dev) {
29    return ReadPagesImpl(page, count, data, nullptr, dev);
30 }
31 
32 // Returns kNdmOk, kNdmUncorrectableEcc, kNdmFatalError or kNdmUnsafeEcc.
ReadPage(uint32_t page,uint8_t * data,uint8_t * spare,void * dev)33 int ReadPage(uint32_t page, uint8_t* data, uint8_t* spare, void* dev) {
34     return ReadPagesImpl(page, 1, data, nullptr, dev);
35 }
36 
37 // Returns kNdmOk or kNdmError on ECC decode failure.
ReadSpare(uint32_t page,uint8_t * spare,void * dev)38 int ReadSpare(uint32_t page, uint8_t* spare, void* dev) {
39     int result = ReadPagesImpl(page, 1, nullptr, spare, dev);
40     if (result == kNdmFatalError || result == kNdmUncorrectableEcc) {
41         return kNdmError;
42     }
43 
44     // kNdmUnsafeEcc is also OK as the data is still correct.
45     return kNdmOk;
46 }
47 
48 // Returns kNdmOk or kNdmError.
ReadSpareNoEcc(uint32_t page,uint8_t * spare,void * dev)49 int ReadSpareNoEcc(uint32_t page, uint8_t* spare, void* dev) {
50     int result = ReadPagesImpl(page, 1, nullptr, spare, dev);
51     return result == kNdmFatalError ? kNdmError : kNdmOk;
52 }
53 
54 // Returns kNdmOk, kNdmError or kNdmFatalError. kNdmError triggers marking the block as bad.
WritePages(uint32_t page,uint32_t count,const uint8_t * data,uint8_t * spare,int action,void * dev)55 int WritePages(uint32_t page, uint32_t count, const uint8_t* data, uint8_t* spare, int action,
56                void* dev) {
57     NdmDriver* device = reinterpret_cast<NdmDriver*>(dev);
58     return device->NandWrite(page, count, data, spare);
59 }
60 
61 // Returns kNdmOk, kNdmError or kNdmFatalError. kNdmError triggers marking the block as bad.
WritePage(uint32_t page,const uint8_t * data,uint8_t * spare,int action,void * dev)62 int WritePage(uint32_t page, const uint8_t* data, uint8_t* spare, int action, void* dev) {
63     return WritePages(page, 1, data, spare, action, dev);
64 }
65 
66 // Returns kNdmOk or kNdmError. kNdmError triggers marking the block as bad.
EraseBlock(uint32_t page,void * dev)67 int EraseBlock(uint32_t page, void* dev) {
68     NdmDriver* device = reinterpret_cast<NdmDriver*>(dev);
69     return device->NandErase(page);
70 }
71 
72 // Returns kTrue, kFalse or kNdmError.
IsBadBlockImpl(uint32_t page,void * dev)73 int IsBadBlockImpl(uint32_t page, void* dev) {
74     NdmDriver* device = reinterpret_cast<NdmDriver*>(dev);
75     return device->IsBadBlock(page);
76 }
77 
78 // Returns kTrue or kFalse (kFalse on error).
IsEmpty(uint32_t page,uint8_t * data,uint8_t * spare,void * dev)79 int IsEmpty(uint32_t page, uint8_t* data, uint8_t* spare, void* dev) {
80     int result = ReadPagesImpl(page, 1, data, spare, dev);
81 
82     // kNdmUncorrectableEcc and kNdmUnsafeEcc are ok.
83     if (result == kNdmFatalError) {
84         return kFalse;
85     }
86 
87     NdmDriver* device = reinterpret_cast<NdmDriver*>(dev);
88     return device->IsEmptyPage(page, data, spare) ? kTrue : kFalse;
89 }
90 
91 // Returns kNdmOk or kNdmError.
CheckPage(uint32_t page,uint8_t * data,uint8_t * spare,int * status,void * dev)92 int CheckPage(uint32_t page, uint8_t* data, uint8_t* spare, int* status, void* dev) {
93     *status = IsEmpty(page, data, spare, dev) ? NDM_PAGE_ERASED : NDM_PAGE_VALID;
94     return kNdmOk;
95 }
96 
97 }  // namespace
98 
~NdmBaseDriver()99 NdmBaseDriver::~NdmBaseDriver() {
100     RemoveNdmVolume();
101 }
102 
CreateNdmVolume(const Volume * ftl_volume,const VolumeOptions & options)103 const char* NdmBaseDriver::CreateNdmVolume(const Volume* ftl_volume, const VolumeOptions& options) {
104     NDMDrvr driver = {};
105     driver.num_blocks = options.num_blocks;
106     driver.max_bad_blocks = options.max_bad_blocks;
107     driver.block_size = options.block_size;
108     driver.page_size = options.page_size;
109     driver.eb_size = options.eb_size;
110     driver.flags = FSF_MULTI_ACCESS | FSF_FREE_SPARE_ECC | options.flags;
111     driver.dev = this;
112     driver.type = NDM_SLC;
113     driver.read_pages = ReadPages;
114     driver.write_pages = WritePages;
115     driver.write_data_and_spare = WritePage;
116     driver.read_decode_data = ReadPage;
117     driver.read_decode_spare = ReadSpare;
118     driver.read_spare = ReadSpareNoEcc;
119     driver.data_and_spare_erased = IsEmpty;
120     driver.data_and_spare_check = CheckPage;
121     driver.erase_block = EraseBlock;
122     driver.is_block_bad = IsBadBlockImpl;
123 
124     ndm_ = ndmAddDev(&driver);
125     if (!ndm_) {
126         return "FTL: ndmAddDev failed";
127     }
128 
129     if (ndmSetNumPartitions(ndm_, 1)) {
130         return "FTL: ndmSetNumPartitions failed";
131     }
132 
133     NDMPartition partition = {};
134     partition.num_blocks = ndmGetNumVBlocks(ndm_) - partition.first_block;
135     partition.type = XFS_VOL;
136 
137     if (ndmWritePartition(ndm_, &partition, 0, "ftl")) {
138         return "FTL: ndmWritePartition failed";
139     }
140 
141     FtlNdmVol ftl = {};
142     XfsVol xfs = {};
143 
144     ftl.flags = FSF_EXTRA_FREE;
145     ftl.cached_map_pages = options.num_blocks * (options.block_size / options.page_size);
146     ftl.extra_free = 6;  // Over-provision 6% of the device.
147     xfs.ftl_volume = const_cast<Volume*>(ftl_volume);
148 
149     if (ndmAddVolXfsFTL(ndm_, 0, &ftl, &xfs)) {
150         return "FTL: ndmAddVolXfsFTL failed";
151     }
152 
153     return nullptr;
154 }
155 
RemoveNdmVolume()156 bool NdmBaseDriver::RemoveNdmVolume() {
157     if (ndm_ && ndmDelDev(ndm_) == 0) {
158         ndm_ = nullptr;
159         return true;
160     }
161     return false;
162 }
163 
IsEmptyPageImpl(const uint8_t * data,uint32_t data_len,const uint8_t * spare,uint32_t spare_len) const164 bool NdmBaseDriver::IsEmptyPageImpl(const uint8_t* data, uint32_t data_len, const uint8_t* spare,
165                                     uint32_t spare_len) const {
166     const int64_t* pointer = reinterpret_cast<const int64_t*>(data);
167     ZX_DEBUG_ASSERT(data_len % sizeof(*pointer) == 0);
168     for (size_t i = 0; i < data_len / sizeof(*pointer); i++) {
169         if (pointer[i] != -1) {
170           return false;
171         }
172     }
173 
174     ZX_DEBUG_ASSERT(spare_len % sizeof(*pointer) == 0);
175     pointer = reinterpret_cast<const int64_t*>(spare);
176     for (size_t i = 0; i < spare_len / sizeof(*pointer); i++) {
177         if (pointer[i] != -1) {
178           return false;
179         }
180     }
181     return true;
182 }
183 
InitModules()184 bool InitModules() {
185     if (!g_init_performed) {
186         // Unfortunately, module initialization is a global affair, and there is
187         // no cleanup. At least, make sure no re-initialization takes place.
188         if (NdmModule(kInitMod) != nullptr || FsModule(kInitMod) != nullptr) {
189             return false;
190         }
191         g_init_performed = true;
192     }
193     return true;
194 }
195 
196 }  // namespace ftl
197