1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include <platsupport/io.h>
12
13#include <sel4vmmplatsupport/drivers/virtio.h>
14#include <sel4vmmplatsupport/drivers/virtio_con.h>
15
16#include <pci/helper.h>
17#include <sel4vmmplatsupport/drivers/pci_helper.h>
18
19#define QUEUE_SIZE 128
20
21static ps_io_ops_t ops;
22
23static int virtio_con_io_in(void *cookie, unsigned int port_no, unsigned int size, unsigned int *result)
24{
25    virtio_con_t *con = (virtio_con_t *)cookie;
26    unsigned int offset = port_no - con->iobase;
27    unsigned int val;
28    int err = con->emul->io_in(con->emul, offset, size, &val);
29    if (err) {
30        return err;
31    }
32    *result = val;
33    return 0;
34}
35
36static int virtio_con_io_out(void *cookie, unsigned int port_no, unsigned int size, unsigned int value)
37{
38    int ret;
39    virtio_con_t *con = (virtio_con_t *)cookie;
40    unsigned int offset = port_no - con->iobase;
41    ret = con->emul->io_out(con->emul, offset, size, value);
42    return 0;
43}
44
45static int emul_con_driver_init(struct console_passthrough *driver, ps_io_ops_t io_ops, void *config)
46{
47    virtio_con_t *con = (virtio_con_t *)config;
48    *driver = con->emul_driver_funcs;
49    return 0;
50}
51
52static vmm_pci_entry_t vmm_virtio_console_pci_bar(unsigned int iobase,
53                                                  size_t iobase_size_bits, unsigned int interrupt_pin, unsigned int interrupt_line)
54{
55    vmm_pci_device_def_t *pci_config;
56    int err = ps_calloc(&ops.malloc_ops, 1, sizeof(*pci_config), (void **)&pci_config);
57    ZF_LOGF_IF(err, "Failed to allocate pci config");
58    *pci_config = (vmm_pci_device_def_t) {
59        .vendor_id = VIRTIO_PCI_VENDOR_ID,
60        .device_id = VIRTIO_CONSOLE_PCI_DEVICE_ID,
61        .command = PCI_COMMAND_IO | PCI_COMMAND_MEMORY,
62        .header_type = PCI_HEADER_TYPE_NORMAL,
63        .subsystem_vendor_id    = VIRTIO_PCI_SUBSYSTEM_VENDOR_ID,
64        .subsystem_id       = VIRTIO_ID_CONSOLE,
65        .interrupt_pin = interrupt_pin,
66        .interrupt_line = interrupt_line,
67        .bar0 = iobase | PCI_BASE_ADDRESS_SPACE_IO,
68        .cache_line_size = 64,
69        .latency_timer = 64,
70        .prog_if = VIRTIO_PCI_CLASS_CONSOLE & 0xff,
71        .subclass = (VIRTIO_PCI_CLASS_CONSOLE >> 8) & 0xff,
72        .class_code = (VIRTIO_PCI_CLASS_CONSOLE >> 16) & 0xff,
73    };
74    vmm_pci_entry_t entry = (vmm_pci_entry_t) {
75        .cookie = pci_config,
76        .ioread = vmm_pci_mem_device_read,
77        .iowrite = vmm_pci_mem_device_write
78    };
79
80    vmm_pci_bar_t bars[1] = {{
81            .mem_type = NON_MEM,
82            .address = iobase,
83            .size_bits = iobase_size_bits
84        }
85    };
86    return vmm_pci_create_passthrough_bar_emulation(entry, 1, bars);
87}
88
89virtio_con_t *common_make_virtio_con(vm_t *vm, vmm_pci_space_t *pci, vmm_io_port_list_t *ioport,
90                                     ioport_range_t ioport_range, ioport_type_t port_type, unsigned int interrupt_pin, unsigned int interrupt_line,
91                                     struct console_passthrough backend)
92{
93    int err = ps_new_stdlib_malloc_ops(&ops.malloc_ops);
94    ZF_LOGF_IF(err, "Failed to get malloc ops");
95
96    virtio_con_t *con;
97    err = ps_calloc(&ops.malloc_ops, 1, sizeof(*con), (void **)&con);
98    ZF_LOGF_IF(err, "Failed to allocate virtio con");
99
100    ioport_interface_t virtio_io_interface = {con, virtio_con_io_in, virtio_con_io_out, "VIRTIO CON"};
101    ioport_entry_t *io_entry = vmm_io_port_add_handler(ioport, ioport_range, virtio_io_interface, port_type);
102    if (!io_entry) {
103        ZF_LOGE("Failed to add vmm io port handler");
104        return NULL;
105    }
106
107    size_t iobase_size_bits = BYTES_TO_SIZE_BITS(io_entry->range.size);
108    con->iobase = io_entry->range.start;
109    vmm_pci_entry_t con_entry = vmm_virtio_console_pci_bar(io_entry->range.start, iobase_size_bits, interrupt_pin,
110                                                           interrupt_line);
111    vmm_pci_add_entry(pci, con_entry, NULL);
112
113    ps_io_ops_t ioops;
114    con->emul_driver_funcs = backend;
115    con->emul = virtio_emul_init(ioops, QUEUE_SIZE, vm, emul_con_driver_init, con, VIRTIO_CONSOLE);
116
117    assert(con->emul);
118    return con;
119}
120