1// Copyright 2016 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#pragma once
5
6#include "device.h"
7#include "ring.h"
8
9#include <stdlib.h>
10#include <zircon/compiler.h>
11
12#include "backends/backend.h"
13#include <virtio/block.h>
14#include <zircon/device/block.h>
15#include <ddk/protocol/block.h>
16
17#include <lib/sync/completion.h>
18
19namespace virtio {
20
21struct block_txn_t {
22    block_op_t op;
23    struct vring_desc* desc;
24    size_t index;
25    list_node_t node;
26    zx_handle_t pmt;
27};
28
29class Ring;
30
31class BlockDevice : public Device {
32public:
33    BlockDevice(zx_device_t* device, zx::bti bti, fbl::unique_ptr<Backend> backend);
34    virtual ~BlockDevice();
35
36    virtual zx_status_t Init() override;
37
38    virtual void IrqRingUpdate() override;
39    virtual void IrqConfigChange() override;
40
41    uint64_t GetSize() const { return config_.capacity * config_.blk_size; }
42    uint32_t GetBlockSize() const { return config_.blk_size; }
43    uint64_t GetBlockCount() const { return config_.capacity; }
44    const char* tag() const override { return "virtio-blk"; }
45
46private:
47    // DDK driver hooks
48    static zx_off_t virtio_block_get_size(void* ctx);
49    static zx_status_t virtio_block_ioctl(void* ctx, uint32_t op, const void* in_buf, size_t in_len,
50                                          void* out_buf, size_t out_len, size_t* out_actual);
51
52    static void virtio_block_query(void* ctx, block_info_t* bi, size_t* bopsz);
53    static void virtio_block_queue(void* ctx, block_op_t* bop);
54
55    void GetInfo(block_info_t* info);
56
57    zx_status_t QueueTxn(block_txn_t* txn, bool write, size_t bytes,
58                         uint64_t* pages, size_t pagecount, uint16_t* idx);
59    void QueueReadWriteTxn(block_txn_t* txn, bool write);
60
61    void txn_complete(block_txn_t* txn, zx_status_t status);
62
63    // the main virtio ring
64    Ring vring_ = {this};
65
66    // lock to be used around Ring::AllocDescChain and FreeDesc
67    // TODO: move this into Ring class once it's certain that other
68    // users of the class are okay with it.
69    fbl::Mutex ring_lock_;
70
71    static const uint16_t ring_size = 128; // 128 matches legacy pci
72
73    // saved block device configuration out of the pci config BAR
74    virtio_blk_config_t config_ = {};
75
76    // a queue of block request/responses
77    static const size_t blk_req_count = 32;
78
79    io_buffer_t blk_req_buf_;
80    virtio_blk_req_t* blk_req_ = nullptr;
81
82    zx_paddr_t blk_res_pa_ = 0;
83    uint8_t* blk_res_ = nullptr;
84
85    uint32_t blk_req_bitmap_ = 0;
86    static_assert(blk_req_count <= sizeof(blk_req_bitmap_) * CHAR_BIT, "");
87
88    size_t alloc_blk_req() {
89        size_t i = 0;
90        if (blk_req_bitmap_ != 0)
91            i = sizeof(blk_req_bitmap_) * CHAR_BIT - __builtin_clz(blk_req_bitmap_);
92        blk_req_bitmap_ |= (1 << i);
93        return i;
94    }
95
96    void free_blk_req(size_t i) {
97        blk_req_bitmap_ &= ~(1 << i);
98    }
99
100    // pending iotxns and waiter state
101    fbl::Mutex txn_lock_;
102    list_node txn_list_ = LIST_INITIAL_VALUE(txn_list_);
103    bool txn_wait_ = false;
104    sync_completion_t txn_signal_;
105
106    block_protocol_ops_t block_ops_ = {};
107};
108
109} // namespace virtio
110