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