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 "nand_driver.h"
6
7 #include <memory>
8
9 #include <fbl/array.h>
10 #include <ddk/driver.h>
11 #include <ddktl/protocol/nand.h>
12 #include <ddktl/protocol/badblock.h>
13 #include <unittest/unittest.h>
14
15 namespace {
16
17 constexpr uint32_t kPageSize = 1024;
18 constexpr uint32_t kOobSize = 8;
19 constexpr uint32_t kBlockSize = 4;
20 constexpr uint32_t kNumBlocks = 3;
21 constexpr uint32_t kEccBits = 12;
22
23 // Fake for the nand protocol.
24 class FakeNand : public ddk::NandProtocol<FakeNand> {
25 public:
FakeNand()26 FakeNand() : proto_({&nand_protocol_ops_, this}) {
27 info_.page_size = kPageSize;
28 info_.oob_size = kOobSize;
29 info_.pages_per_block = kBlockSize;
30 info_.num_blocks = kNumBlocks;
31 info_.ecc_bits = kEccBits;
32 }
33
proto()34 nand_protocol_t* proto() { return &proto_; }
operation()35 nand_operation_t* operation() { return operation_; }
36
set_result(zx_status_t result)37 void set_result(zx_status_t result) { result_ = result; }
set_ecc_bits(uint32_t ecc_bits)38 void set_ecc_bits(uint32_t ecc_bits) { ecc_bits_ = ecc_bits; }
39
40 // Nand protocol:
NandQuery(fuchsia_hardware_nand_Info * out_info,size_t * out_nand_op_size)41 void NandQuery(fuchsia_hardware_nand_Info* out_info, size_t* out_nand_op_size) {
42 *out_info = info_;
43 *out_nand_op_size = sizeof(*operation_);
44 }
45
NandQueue(nand_operation_t * operation,nand_queue_callback callback,void * cookie)46 void NandQueue(nand_operation_t* operation, nand_queue_callback callback, void* cookie) {
47 operation_ = operation;
48 if (operation->rw.command == NAND_OP_READ) {
49 uint8_t data = 'd';
50 uint64_t vmo_addr = operation->rw.offset_data_vmo * kPageSize;
51 zx_vmo_write(operation->rw.data_vmo, &data, vmo_addr, sizeof(data));
52
53 data = 'o';
54 vmo_addr = operation->rw.offset_oob_vmo * kPageSize;
55 zx_vmo_write(operation->rw.oob_vmo, &data, vmo_addr, sizeof(data));
56 operation->rw.corrected_bit_flips = ecc_bits_;
57 } else if (operation->rw.command == NAND_OP_WRITE) {
58 uint8_t data;
59 uint64_t vmo_addr = operation->rw.offset_data_vmo * kPageSize;
60 zx_vmo_read(operation->rw.data_vmo, &data, vmo_addr, sizeof(data));
61 if (data != 'd') {
62 result_ = ZX_ERR_IO;
63 }
64
65 vmo_addr = operation->rw.offset_oob_vmo * kPageSize;
66 zx_vmo_read(operation->rw.oob_vmo, &data, vmo_addr, sizeof(data));
67 if (data != 'o') {
68 result_ = ZX_ERR_IO;
69 }
70 }
71 callback(cookie, result_, operation);
72 }
73
NandGetFactoryBadBlockList(uint32_t * out_bad_blocks_list,size_t bad_blocks_count,size_t * out_bad_blocks_actual)74 zx_status_t NandGetFactoryBadBlockList(uint32_t* out_bad_blocks_list, size_t bad_blocks_count,
75 size_t* out_bad_blocks_actual) {
76 return ZX_ERR_BAD_STATE;
77 }
78
79 private:
80 nand_protocol_t proto_;
81 fuchsia_hardware_nand_Info info_ = {};
82 nand_operation_t* operation_;
83 zx_status_t result_ = ZX_OK;
84 uint32_t ecc_bits_ = 0;
85 };
86
87 // Fake for the bad block protocol.
88 class FakeBadBlock : public ddk::BadBlockProtocol<FakeBadBlock> {
89 public:
FakeBadBlock()90 FakeBadBlock() : proto_({&bad_block_protocol_ops_, this}) {}
91
proto()92 bad_block_protocol_t* proto() { return &proto_; }
set_result(zx_status_t result)93 void set_result(zx_status_t result) { result_ = result; }
94
95 // Bad block protocol:
BadBlockGetBadBlockList(uint32_t * out_bad_blocks_list,size_t bad_blocks_count,size_t * out_bad_blocks_actual)96 zx_status_t BadBlockGetBadBlockList(uint32_t* out_bad_blocks_list, size_t bad_blocks_count,
97 size_t* out_bad_blocks_actual) {
98 *out_bad_blocks_actual = 0;
99 if (!bad_blocks_count) {
100 *out_bad_blocks_actual = 1;
101 } else if (bad_blocks_count == 1) {
102 ZX_ASSERT(out_bad_blocks_list);
103 *out_bad_blocks_list = 1; // Second block is bad.
104 *out_bad_blocks_actual = 1;
105 }
106 return result_;
107 }
108
BadBlockMarkBlockBad(uint32_t block)109 zx_status_t BadBlockMarkBlockBad(uint32_t block) {
110 return ZX_ERR_BAD_STATE;
111 }
112
113 private:
114 bad_block_protocol_t proto_;
115 zx_status_t result_ = ZX_OK;
116 };
117
118 class NandTester {
119 public:
NandTester()120 NandTester() {}
121
nand_proto()122 nand_protocol_t* nand_proto() { return nand_proto_.proto(); }
bad_block_proto()123 bad_block_protocol_t* bad_block_proto() { return bad_block_proto_.proto(); }
operation()124 nand_operation_t* operation() { return nand_proto_.operation(); }
nand()125 FakeNand* nand() { return &nand_proto_; }
bad_block()126 FakeBadBlock* bad_block() { return &bad_block_proto_; }
127
128 private:
129 FakeNand nand_proto_;
130 FakeBadBlock bad_block_proto_;
131 };
132
TrivialLifetimeTest()133 bool TrivialLifetimeTest() {
134 BEGIN_TEST;
135 NandTester tester;
136 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
137 END_TEST;
138 }
139
InitTest()140 bool InitTest() {
141 BEGIN_TEST;
142 NandTester tester;
143 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
144 ASSERT_EQ(nullptr, driver->Init());
145 END_TEST;
146 }
147
InitFailureTest()148 bool InitFailureTest() {
149 BEGIN_TEST;
150 NandTester tester;
151
152 tester.bad_block()->set_result(ZX_ERR_BAD_STATE);
153 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
154 ASSERT_NE(nullptr, driver->Init());
155 END_TEST;
156 }
157
ReadTest()158 bool ReadTest() {
159 BEGIN_TEST;
160 NandTester tester;
161 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
162 ASSERT_EQ(nullptr, driver->Init());
163
164 fbl::Array<uint8_t> data(new uint8_t[kPageSize * 2], kPageSize * 2);
165 fbl::Array<uint8_t> oob(new uint8_t[kOobSize * 2], kOobSize * 2);
166
167 ASSERT_EQ(ftl::kNdmOk, driver->NandRead(5, 2, data.get(), oob.get()));
168
169 nand_operation_t* operation = tester.operation();
170 EXPECT_EQ(NAND_OP_READ, operation->command);
171 EXPECT_EQ(2 * 2, operation->rw.length);
172 EXPECT_EQ(5 * 2, operation->rw.offset_nand);
173 EXPECT_EQ(0, operation->rw.offset_data_vmo);
174 EXPECT_EQ(2 * 2, operation->rw.offset_oob_vmo);
175 EXPECT_EQ('d', data[0]);
176 EXPECT_EQ('o', oob[0]);
177 END_TEST;
178 }
179
ReadFailureTest()180 bool ReadFailureTest() {
181 BEGIN_TEST;
182 NandTester tester;
183 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
184 ASSERT_EQ(nullptr, driver->Init());
185
186 fbl::Array<uint8_t> data(new uint8_t[kPageSize * 2], kPageSize * 2);
187 fbl::Array<uint8_t> oob(new uint8_t[kOobSize * 2], kOobSize * 2);
188
189 tester.nand()->set_result(ZX_ERR_BAD_STATE);
190 ASSERT_EQ(ftl::kNdmFatalError, driver->NandRead(5, 2, data.get(), oob.get()));
191 END_TEST;
192 }
193
ReadEccUnsafeTest()194 bool ReadEccUnsafeTest() {
195 BEGIN_TEST;
196 NandTester tester;
197 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
198 ASSERT_EQ(nullptr, driver->Init());
199
200 fbl::Array<uint8_t> data(new uint8_t[kPageSize * 2], kPageSize * 2);
201 fbl::Array<uint8_t> oob(new uint8_t[kOobSize * 2], kOobSize * 2);
202
203 tester.nand()->set_ecc_bits(kEccBits / 2 + 1);
204 ASSERT_EQ(ftl::kNdmUnsafeEcc, driver->NandRead(5, 2, data.get(), oob.get()));
205 END_TEST;
206 }
207
ReadEccFailureTest()208 bool ReadEccFailureTest() {
209 BEGIN_TEST;
210 NandTester tester;
211 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
212 ASSERT_EQ(nullptr, driver->Init());
213
214 fbl::Array<uint8_t> data(new uint8_t[kPageSize * 2], kPageSize * 2);
215 fbl::Array<uint8_t> oob(new uint8_t[kOobSize * 2], kOobSize * 2);
216
217 tester.nand()->set_ecc_bits(kEccBits + 1);
218 ASSERT_EQ(ftl::kNdmUncorrectableEcc, driver->NandRead(5, 2, data.get(), oob.get()));
219 END_TEST;
220 }
221
WriteTest()222 bool WriteTest() {
223 BEGIN_TEST;
224 NandTester tester;
225 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
226 ASSERT_EQ(nullptr, driver->Init());
227
228 fbl::Array<uint8_t> data(new uint8_t[kPageSize * 2], kPageSize * 2);
229 fbl::Array<uint8_t> oob(new uint8_t[kOobSize * 2], kOobSize * 2);
230 memset(data.get(), 'd', data.size());
231 memset(oob.get(), 'o', oob.size());
232
233 ASSERT_EQ(ftl::kNdmOk, driver->NandWrite(5, 2, data.get(), oob.get()));
234
235 nand_operation_t* operation = tester.operation();
236 EXPECT_EQ(NAND_OP_WRITE, operation->command);
237 EXPECT_EQ(2 * 2, operation->rw.length);
238 EXPECT_EQ(5 * 2, operation->rw.offset_nand);
239 EXPECT_EQ(0, operation->rw.offset_data_vmo);
240 EXPECT_EQ(2 * 2, operation->rw.offset_oob_vmo);
241 END_TEST;
242 }
243
WriteFailureTest()244 bool WriteFailureTest() {
245 BEGIN_TEST;
246 NandTester tester;
247 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
248 ASSERT_EQ(nullptr, driver->Init());
249
250 fbl::Array<uint8_t> data(new uint8_t[kPageSize * 2], kPageSize * 2);
251 fbl::Array<uint8_t> oob(new uint8_t[kOobSize * 2], kOobSize * 2);
252 memset(data.get(), 'd', data.size());
253 memset(oob.get(), 'e', oob.size()); // Unexpected value.
254
255 ASSERT_EQ(ftl::kNdmError, driver->NandWrite(5, 2, data.get(), oob.get()));
256 END_TEST;
257 }
258
EraseTest()259 bool EraseTest() {
260 BEGIN_TEST;
261 NandTester tester;
262 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
263 ASSERT_EQ(nullptr, driver->Init());
264
265 ASSERT_EQ(ftl::kNdmOk, driver->NandErase(5 * kBlockSize / 2));
266
267 nand_operation_t* operation = tester.operation();
268 EXPECT_EQ(NAND_OP_ERASE, operation->command);
269 EXPECT_EQ(1, operation->erase.num_blocks);
270 EXPECT_EQ(5, operation->erase.first_block);
271 END_TEST;
272 }
273
EraseFailureTest()274 bool EraseFailureTest() {
275 BEGIN_TEST;
276 NandTester tester;
277 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
278 ASSERT_EQ(nullptr, driver->Init());
279
280 tester.nand()->set_result(ZX_ERR_BAD_STATE);
281 ASSERT_EQ(ftl::kNdmError, driver->NandErase(5 * kBlockSize / 2));
282 END_TEST;
283 }
284
IsBadBlockTest()285 bool IsBadBlockTest() {
286 BEGIN_TEST;
287 NandTester tester;
288 auto driver = ftl::NandDriver::Create(tester.nand_proto(), tester.bad_block_proto());
289 ASSERT_EQ(nullptr, driver->Init());
290
291 ASSERT_FALSE(driver->IsBadBlock(0));
292 ASSERT_TRUE(driver->IsBadBlock(1 * kBlockSize / 2));
293 ASSERT_FALSE(driver->IsBadBlock(2 * kBlockSize / 2));
294 END_TEST;
295 }
296
297 } // namespace
298
299 BEGIN_TEST_CASE(NandDriverTests)
300 RUN_TEST_SMALL(TrivialLifetimeTest)
301 RUN_TEST_SMALL(InitTest)
302 RUN_TEST_SMALL(InitFailureTest)
303 RUN_TEST_SMALL(ReadTest)
304 RUN_TEST_SMALL(ReadFailureTest)
305 RUN_TEST_SMALL(ReadEccUnsafeTest)
306 RUN_TEST_SMALL(ReadEccFailureTest)
307 RUN_TEST_SMALL(WriteTest)
308 RUN_TEST_SMALL(WriteFailureTest)
309 RUN_TEST_SMALL(EraseTest)
310 RUN_TEST_SMALL(EraseFailureTest)
311 RUN_TEST_SMALL(IsBadBlockTest)
312 END_TEST_CASE(NandDriverTests)
313