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