// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "broker.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // Wrapper for a nand_op_t. class Operation { public: explicit Operation(size_t op_size) { raw_buffer_.reset(new uint8_t[op_size]); memset(raw_buffer_.get(), 0, op_size); nand_op_t* op = reinterpret_cast(raw_buffer_.get()); op->completion_cb = OnCompletion; op->cookie = this; } ~Operation() {} nand_op_t* GetOperation() { return reinterpret_cast(raw_buffer_.get()); } // Waits for the operation to complete and returns the operation's status. zx_status_t Wait() { zx_status_t status = sync_completion_wait(&event_, ZX_TIME_INFINITE); sync_completion_reset(&event_); return status != ZX_OK ? status : status_; } private: static void OnCompletion(nand_op_t* op, zx_status_t status) { Operation* operation = reinterpret_cast(op->cookie); operation->status_ = status; sync_completion_signal(&operation->event_); } sync_completion_t event_; zx_status_t status_ = ZX_ERR_INTERNAL; fbl::unique_ptr raw_buffer_; }; class Broker; using DeviceType = ddk::Device; // Exposes a control device (nand-broker) for a nand protocol device. class Broker : public DeviceType { public: explicit Broker(zx_device_t* parent) : DeviceType(parent) {} ~Broker() {} zx_status_t Bind(); void DdkRelease() { delete this; } // Device protocol implementation. void DdkUnbind() { DdkRemove(); } zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf, size_t out_len, size_t* out_actual); private: zx_status_t Query(nand_info_t* info); zx_status_t Queue(uint32_t command, const nand_broker_request_t& request, nand_broker_response_t* response); nand_protocol_t nand_protocol_; size_t op_size_ = 0; }; zx_status_t Broker::Bind() { if (device_get_protocol(parent(), ZX_PROTOCOL_NAND, &nand_protocol_) != ZX_OK) { zxlogf(ERROR, "nand-broker: device '%s' does not support nand protocol\n", device_get_name(parent())); return ZX_ERR_NOT_SUPPORTED; } nand_info_t info; Query(&info); if (!op_size_) { zxlogf(ERROR, "nand-broker: unable to query the nand driver\n"); return ZX_ERR_NOT_SUPPORTED; } zxlogf(INFO, "nand-broker: %d blocks of %d pages each. Page size: %d\n", info.num_blocks, info.pages_per_block, info.page_size); return DdkAdd("broker"); } zx_status_t Broker::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf, size_t out_len, size_t* out_actual) { switch (op) { case IOCTL_NAND_BROKER_UNLINK: DdkUnbind(); return ZX_OK; case IOCTL_NAND_BROKER_GET_INFO: if (out_len < sizeof(nand_info_t)) { return ZX_ERR_INVALID_ARGS; } *out_actual = sizeof(nand_info_t); return Query(reinterpret_cast(out_buf)); case IOCTL_NAND_BROKER_READ: case IOCTL_NAND_BROKER_WRITE: case IOCTL_NAND_BROKER_ERASE: if (in_len != sizeof(nand_broker_request_t)) { return ZX_ERR_INVALID_ARGS; } if (out_len < sizeof(nand_broker_response_t)) { return ZX_ERR_INVALID_ARGS; } *out_actual = sizeof(nand_broker_response_t); return Queue(op, *reinterpret_cast(in_buf), reinterpret_cast(out_buf)); default: return ZX_ERR_NOT_SUPPORTED; } } zx_status_t Broker::Query(nand_info_t* info) { ddk::NandProtocolProxy proxy(&nand_protocol_); proxy.Query(info, &op_size_); return ZX_OK; } zx_status_t Broker::Queue(uint32_t command, const nand_broker_request_t& request, nand_broker_response_t* response) { Operation operation(op_size_); nand_op_t* op = operation.GetOperation(); *response = {}; switch (command) { case IOCTL_NAND_BROKER_READ: case IOCTL_NAND_BROKER_WRITE: op->rw.command = (command == IOCTL_NAND_BROKER_READ) ? NAND_OP_READ : NAND_OP_WRITE; op->rw.length = request.length; op->rw.offset_nand = request.offset_nand; op->rw.offset_data_vmo = request.offset_data_vmo; op->rw.offset_oob_vmo = request.offset_oob_vmo; op->rw.data_vmo = request.data_vmo ? request.vmo : ZX_HANDLE_INVALID; op->rw.oob_vmo = request.oob_vmo ? request.vmo : ZX_HANDLE_INVALID; break; case IOCTL_NAND_BROKER_ERASE: op->erase.command = NAND_OP_ERASE; op->erase.first_block = request.offset_nand; op->erase.num_blocks = request.length; break; default: ZX_DEBUG_ASSERT(false); } ddk::NandProtocolProxy proxy(&nand_protocol_); proxy.Queue(op); response->status = operation.Wait(); if (command == IOCTL_NAND_BROKER_READ) { response->corrected_bit_flips = op->rw.corrected_bit_flips; } if ((command == IOCTL_NAND_BROKER_READ || command == IOCTL_NAND_BROKER_WRITE) && request.vmo != ZX_HANDLE_INVALID) { zx_handle_close(request.vmo); } return ZX_OK; } } // namespace zx_status_t nand_broker_bind(void* ctx, zx_device_t* parent) { zxlogf(INFO, "nand-broker: binding\n"); fbl::AllocChecker checker; fbl::unique_ptr device(new (&checker) Broker(parent)); if (!checker.check()) { return ZX_ERR_NO_MEMORY; } zx_status_t status = device->Bind(); if (status == ZX_OK) { // devmgr is now in charge of the device. __UNUSED Broker* dummy = device.release(); } return status; }