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#include "nandpart.h"
5
6#include <assert.h>
7#include <inttypes.h>
8#include <stdio.h>
9#include <string.h>
10
11#include <ddk/binding.h>
12#include <ddk/debug.h>
13#include <ddk/driver.h>
14#include <ddk/metadata.h>
15#include <ddk/metadata/nand.h>
16#include <ddk/protocol/bad-block.h>
17
18#include <fbl/algorithm.h>
19#include <fbl/alloc_checker.h>
20#include <fbl/unique_ptr.h>
21#include <lib/sync/completion.h>
22#include <zircon/boot/image.h>
23#include <zircon/hw/gpt.h>
24#include <zircon/types.h>
25
26#include "nandpart-utils.h"
27
28namespace nand {
29namespace {
30
31constexpr uint8_t fvm_guid[] = GUID_FVM_VALUE;
32
33// Shim for calling sub-partition's callback.
34void CompletionCallback(nand_op_t* op, zx_status_t status) {
35    op = static_cast<nand_op_t*>(op->cookie);
36    op->completion_cb(op, status);
37}
38
39} // namespace
40
41zx_status_t NandPartDevice::Create(zx_device_t* parent) {
42    zxlogf(INFO, "NandPartDevice::Create: Starting...!\n");
43
44    nand_protocol_t nand_proto;
45    if (device_get_protocol(parent, ZX_PROTOCOL_NAND, &nand_proto) != ZX_OK) {
46        zxlogf(ERROR, "nandpart: parent device '%s': does not support nand protocol\n",
47               device_get_name(parent));
48        return ZX_ERR_NOT_SUPPORTED;
49    }
50
51    // Query parent to get its nand_info_t and size for nand_op_t.
52    nand_info_t nand_info;
53    size_t parent_op_size;
54    nand_proto.ops->query(nand_proto.ctx, &nand_info, &parent_op_size);
55    // Make sure parent_op_size is aligned, so we can safely add our data at the end.
56    parent_op_size = fbl::round_up(parent_op_size, 8u);
57
58    // Query parent for nand configuration info.
59    size_t actual;
60    nand_config_t nand_config;
61    zx_status_t status = device_get_metadata(parent, DEVICE_METADATA_PRIVATE, &nand_config,
62                                             sizeof(nand_config), &actual);
63    if (status != ZX_OK) {
64        zxlogf(ERROR, "nandpart: parent device '%s' has no device metadata\n",
65               device_get_name(parent));
66        return status;
67    }
68    if (actual < sizeof(nand_config_t)) {
69        zxlogf(ERROR, "nandpart: Expected metadata is of size %zu, needs to at least be %zu\n",
70               actual, sizeof(nand_config_t));
71        return ZX_ERR_INTERNAL;
72    }
73    // Create a bad block instance.
74    BadBlock::Config config = {
75        .bad_block_config = nand_config.bad_block_config,
76        .nand_proto = nand_proto,
77    };
78    fbl::RefPtr<BadBlock> bad_block;
79    status = BadBlock::Create(config, &bad_block);
80    if (status != ZX_OK) {
81        zxlogf(ERROR, "nandpart: Failed to create BadBlock object\n");
82        return status;
83    }
84
85    // Query parent for partition map.
86    uint8_t buffer[METADATA_PARTITION_MAP_MAX];
87    status = device_get_metadata(parent, DEVICE_METADATA_PARTITION_MAP, buffer, sizeof(buffer),
88                                 &actual);
89    if (status != ZX_OK) {
90        zxlogf(ERROR, "nandpart: parent device '%s' has no parititon map\n",
91               device_get_name(parent));
92        return status;
93    }
94    if (actual < sizeof(zbi_partition_map_t)) {
95        zxlogf(ERROR, "nandpart: Partition map is of size %zu, needs to at least be %zu\n", actual,
96               sizeof(zbi_partition_t));
97        return ZX_ERR_INTERNAL;
98    }
99
100    auto* pmap = reinterpret_cast<zbi_partition_map_t*>(buffer);
101
102    const size_t minimum_size =
103        sizeof(zbi_partition_map_t) + (sizeof(zbi_partition_t) * pmap->partition_count);
104    if (actual < minimum_size) {
105        zxlogf(ERROR, "nandpart: Partition map is of size %zu, needs to at least be %zu\n", actual,
106               minimum_size);
107        return ZX_ERR_INTERNAL;
108    }
109
110    // Sanity check partition map and transform into expected form.
111    status = SanitizePartitionMap(pmap, nand_info);
112    if (status != ZX_OK) {
113        return status;
114    }
115
116    // Create a device for each partition.
117    for (unsigned i = 0; i < pmap->partition_count; i++) {
118        const auto* part = &pmap->partitions[i];
119
120        nand_info.num_blocks = static_cast<uint32_t>(part->last_block - part->first_block + 1);
121        memcpy(&nand_info.partition_guid, &part->type_guid, sizeof(nand_info.partition_guid));
122        // We only use FTL for the FVM partition.
123        if (memcmp(part->type_guid, fvm_guid, sizeof(fvm_guid)) == 0) {
124            nand_info.nand_class = NAND_CLASS_FTL;
125        } else {
126            nand_info.nand_class = NAND_CLASS_BBS;
127        }
128
129        fbl::AllocChecker ac;
130        fbl::unique_ptr<NandPartDevice> device(new (&ac) NandPartDevice(
131            parent, nand_proto, bad_block, parent_op_size, nand_info,
132            static_cast<uint32_t>(part->first_block)));
133        if (!ac.check()) {
134            continue;
135        }
136        // Find optional partition_config information.
137        uint32_t copy_count = 1;
138        for (uint32_t i = 0; i < nand_config.extra_partition_config_count; i++) {
139            if (memcmp(nand_config.extra_partition_config[i].type_guid, part->type_guid,
140                       sizeof(part->type_guid)) == 0 &&
141                nand_config.extra_partition_config[i].copy_count > 0) {
142                copy_count = nand_config.extra_partition_config[i].copy_count;
143                break;
144            }
145        }
146        status = device->Bind(part->name, copy_count);
147        if (status != ZX_OK) {
148            zxlogf(ERROR, "Failed to bind %s with error %d\n", part->name, status);
149
150            continue;
151        }
152        // devmgr is now in charge of the device.
153        __UNUSED auto* dummy = device.release();
154    }
155
156    return ZX_OK;
157}
158
159zx_status_t NandPartDevice::Bind(const char* name, uint32_t copy_count) {
160    zxlogf(INFO, "nandpart: Binding %s to %s\n", name, device_get_name(parent()));
161
162    zx_device_prop_t props[] = {
163        {BIND_PROTOCOL, 0, ZX_PROTOCOL_NAND},
164        {BIND_NAND_CLASS, 0, nand_info_.nand_class},
165    };
166
167    zx_status_t status = DdkAdd(name, DEVICE_ADD_INVISIBLE, props, fbl::count_of(props));
168    if (status != ZX_OK) {
169        return status;
170    }
171
172    // Add empty partition map metadata to prevent this driver from binding to its child devices
173    status = DdkAddMetadata(DEVICE_METADATA_PARTITION_MAP, nullptr, 0);
174    if (status != ZX_OK) {
175        DdkRemove();
176        return status;
177    }
178
179    status = DdkAddMetadata(DEVICE_METADATA_PRIVATE, &copy_count, sizeof(copy_count));
180    if (status != ZX_OK) {
181        DdkRemove();
182        return status;
183    }
184
185    DdkMakeVisible();
186    return ZX_OK;
187}
188
189void NandPartDevice::Query(nand_info_t* info_out, size_t* nand_op_size_out) {
190    memcpy(info_out, &nand_info_, sizeof(*info_out));
191    // Add size of translated_op.
192    *nand_op_size_out = parent_op_size_ + sizeof(nand_op_t);
193}
194
195void NandPartDevice::Queue(nand_op_t* op) {
196    auto* translated_op =
197        reinterpret_cast<nand_op_t*>(reinterpret_cast<uintptr_t>(op) + parent_op_size_);
198    uint32_t command = op->command;
199
200    // Copy client's op to translated op
201    memcpy(translated_op, op, sizeof(*translated_op));
202
203    // Make offset relative to full underlying device
204    switch (command) {
205    case NAND_OP_READ:
206    case NAND_OP_WRITE:
207        translated_op->rw.offset_nand += (erase_block_start_ * nand_info_.pages_per_block);
208        break;
209    case NAND_OP_ERASE:
210        translated_op->erase.first_block += erase_block_start_;
211        break;
212    default:
213        op->completion_cb(op, ZX_ERR_NOT_SUPPORTED);
214        return;
215    }
216
217    translated_op->completion_cb = CompletionCallback;
218    translated_op->cookie = op;
219
220    // Call parent's queue
221    nand_.Queue(translated_op);
222}
223
224zx_status_t NandPartDevice::GetFactoryBadBlockList(uint32_t* bad_blocks, uint32_t bad_block_len,
225                                                   uint32_t* num_bad_blocks) {
226    // TODO implement this.
227    *num_bad_blocks = 0;
228    return ZX_ERR_NOT_SUPPORTED;
229}
230
231zx_status_t NandPartDevice::GetBadBlockList(uint32_t* bad_block_list, uint32_t bad_block_list_len,
232                                            uint32_t* bad_block_count) {
233
234    if (!bad_block_list_) {
235        const zx_status_t status = bad_block_->GetBadBlockList(
236            erase_block_start_, erase_block_start_ + nand_info_.num_blocks, &bad_block_list_);
237        if (status != ZX_OK) {
238            return status;
239        }
240        for (uint32_t i = 0; i < bad_block_list_.size(); i++) {
241            bad_block_list_[i] -= erase_block_start_;
242        }
243    }
244
245    *bad_block_count = static_cast<uint32_t>(bad_block_list_.size());
246    zxlogf(TRACE, "nandpart: %s: Bad block count: %u\n", name(), *bad_block_count);
247
248    if (bad_block_list_len == 0 || bad_block_list_.size() == 0) {
249        return ZX_OK;
250    }
251    if (bad_block_list == NULL) {
252        return ZX_ERR_INVALID_ARGS;
253    }
254
255    const size_t size = sizeof(uint32_t) * fbl::min(*bad_block_count, bad_block_list_len);
256    memcpy(bad_block_list, bad_block_list_.get(), size);
257    return ZX_OK;
258}
259
260zx_status_t NandPartDevice::MarkBlockBad(uint32_t block) {
261    if (block >= nand_info_.num_blocks) {
262        return ZX_ERR_OUT_OF_RANGE;
263    }
264
265    // First, invalidate our cached copy.
266    bad_block_list_.reset();
267
268    // Second, "write-through" to actually persist.
269    block += erase_block_start_;
270    return bad_block_->MarkBlockBad(block);
271}
272
273zx_status_t NandPartDevice::DdkGetProtocol(uint32_t proto_id, void* protocol) {
274    auto* proto = static_cast<ddk::AnyProtocol*>(protocol);
275    proto->ctx = this;
276    switch (proto_id) {
277    case ZX_PROTOCOL_NAND:
278        proto->ops = &nand_proto_ops_;
279        break;
280    case ZX_PROTOCOL_BAD_BLOCK:
281        proto->ops = &bad_block_proto_ops_;
282        break;
283    default:
284        return ZX_ERR_NOT_SUPPORTED;
285    }
286    return ZX_OK;
287}
288
289} // namespace nand
290
291extern "C" zx_status_t nandpart_bind(void* ctx, zx_device_t* parent) {
292    return nand::NandPartDevice::Create(parent);
293}
294