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
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include <ddk/binding.h>
10#include <ddk/debug.h>
11#include <ddk/device.h>
12#include <ddk/driver.h>
13#include <ddk/protocol/pci.h>
14
15#include <fbl/alloc_checker.h>
16#include <fbl/unique_ptr.h>
17
18#include <zircon/compiler.h>
19#include <zircon/types.h>
20
21#include "backends/pci.h"
22#include "block.h"
23#include "console.h"
24#include "device.h"
25#include "ethernet.h"
26#include "gpu.h"
27#include "input.h"
28#include "rng.h"
29
30extern "C" zx_status_t virtio_pci_bind(void* ctx, zx_device_t* bus_device, void** cookie) {
31    zx_status_t status;
32    pci_protocol_t pci;
33
34    // grab the pci device and configuration to pass to the backend
35    if (device_get_protocol(bus_device, ZX_PROTOCOL_PCI, &pci)) {
36        return ZX_ERR_INVALID_ARGS;
37    }
38
39    zx_pcie_device_info_t info;
40    status = pci_get_device_info(&pci, &info);
41    if (status != ZX_OK) {
42        return status;
43    }
44
45    zx::bti bti;
46    status = pci_get_bti(&pci, 0, bti.reset_and_get_address());
47    if (status != ZX_OK) {
48        return status;
49    }
50
51    // Due to the similarity between Virtio 0.9.5 legacy devices and Virtio 1.0
52    // transitional devices we need to check whether modern capabilities exist.
53    // If no vendor capabilities are found then we will default to the legacy
54    // interface.
55    fbl::unique_ptr<virtio::Backend> backend = nullptr;
56    if (pci_get_first_capability(&pci, kPciCapIdVendor) != 0) {
57        zxlogf(SPEW, "virtio %02x:%02x.%1x using modern PCI backend\n", info.bus_id, info.dev_id, info.func_id);
58        backend.reset(new virtio::PciModernBackend(pci, info));
59    } else {
60        zxlogf(SPEW, "virtio %02x:%02x.%1x using legacy PCI backend\n", info.bus_id, info.dev_id, info.func_id);
61        backend.reset(new virtio::PciLegacyBackend(pci, info));
62    }
63
64    status = backend->Bind();
65    if (status != ZX_OK) {
66        return status;
67    }
68
69    // Now that the backend for this device has been initialized we can
70    // compose a device based on the PCI device id
71    fbl::unique_ptr<virtio::Device> virtio_device = nullptr;
72    switch (info.device_id) {
73    case VIRTIO_DEV_TYPE_NETWORK:
74    case VIRTIO_DEV_TYPE_T_NETWORK:
75        virtio_device.reset(new virtio::EthernetDevice(bus_device, fbl::move(bti),
76                                                       fbl::move(backend)));
77        break;
78    case VIRTIO_DEV_TYPE_BLOCK:
79    case VIRTIO_DEV_TYPE_T_BLOCK:
80        virtio_device.reset(new virtio::BlockDevice(bus_device, fbl::move(bti),
81                                                    fbl::move(backend)));
82        break;
83    case VIRTIO_DEV_TYPE_CONSOLE:
84    case VIRTIO_DEV_TYPE_T_CONSOLE:
85        virtio_device.reset(new virtio::ConsoleDevice(bus_device, fbl::move(bti),
86                                                      fbl::move(backend)));
87        break;
88    case VIRTIO_DEV_TYPE_GPU:
89        virtio_device.reset(new virtio::GpuDevice(bus_device, fbl::move(bti), fbl::move(backend)));
90        break;
91    case VIRTIO_DEV_TYPE_ENTROPY:
92    case VIRTIO_DEV_TYPE_T_ENTROPY:
93        virtio_device.reset(new virtio::RngDevice(bus_device, fbl::move(bti), fbl::move(backend)));
94        break;
95    case VIRTIO_DEV_TYPE_INPUT:
96        virtio_device.reset(new virtio::InputDevice(bus_device, fbl::move(bti),
97                                                    fbl::move(backend)));
98        break;
99    default:
100        return ZX_ERR_NOT_SUPPORTED;
101    }
102
103    status = virtio_device->Init();
104    if (status != ZX_OK) {
105        return status;
106    }
107
108    // if we're here, we're successful so drop the unique ptr ref to the object and let it live on
109    __UNUSED auto ptr = virtio_device.release();
110    return ZX_OK;
111}
112