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