1/**
2 * \file PCI bus
3 *
4 * Virtual PCI bus implementation.
5 */
6
7/*
8 * Copyright (c) 2009, ETH Zurich.
9 * All rights reserved.
10 *
11 * This file is distributed under the terms in the attached LICENSE file.
12 * If you do not find this file, copies can be found by writing to:
13 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14 */
15
16#include <stdlib.h>
17
18#include "vmkitmon.h"
19#include "pci.h"
20#include "pci_devices.h"
21
22#define INVALID         0xffffffff
23
24int pci_handle_pio_write(struct pci *pci, uint16_t port, enum opsize size,
25                         uint32_t val)
26{
27    assert(pci != NULL);
28
29    switch(port) {
30    case 0xcf8:         // PCI config address port
31        VMKIT_PCI_DEBUG("wrote %x to 0xcf8\n", val);
32        if(size == OPSIZE_32) {
33            pci->address.raw = val;
34        } else {
35            VMKIT_PCI_DEBUG("ignoring write (not 32bit opsize)\n");
36        }
37        break;
38
39    case 0xcfc:         // PCI config data port
40    case 0xcfd:
41    case 0xcfe:
42    case 0xcff:
43
44        if(port != 0xcfc) {
45            printf("!!!!!!!!!!!!!!!!! Unaligned write !!!!!!!!!!!!!!!\n");
46        }
47
48        VMKIT_PCI_DEBUG("wrote %x to 0x%x\n", val, port);
49        {
50            int busnr = pci->address.d.bus_nr;
51            int device = pci->address.d.dev_nr;
52            struct pci_bus *bus = pci->bus[busnr];
53
54            if(bus == NULL) {
55                break;
56            }
57
58            struct pci_device *dev = bus->device[device];
59
60            if(dev != NULL) {
61                dev->confspace_write(dev, pci->address, size, val);
62            }
63        }
64        break;
65
66    default:
67        VMKIT_PCI_DEBUG("write to invalid port\n");
68        break;
69    }
70
71    return 0;
72}
73
74int pci_handle_pio_read(struct pci *pci, uint16_t port, enum opsize size,
75                        uint32_t *val)
76{
77    assert(pci != NULL);
78
79    switch(port) {
80    case 0xcf8:         // PCI config address port
81        *val = pci->address.raw;
82        VMKIT_PCI_DEBUG("read from 0xcf8: %x\n", *val);
83        break;
84
85    case 0xcfc:         // PCI config data port
86    case 0xcfd:
87    case 0xcfe:
88    case 0xcff:
89        {
90            int busnr = pci->address.d.bus_nr;
91            int device = pci->address.d.dev_nr;
92            struct pci_bus *bus = pci->bus[busnr];
93
94            if(bus == NULL) {
95                *val = INVALID;
96                break;
97            }
98
99            struct pci_device *dev = bus->device[device];
100
101            if(dev != NULL) {
102                dev->confspace_read(dev, pci->address, size, val);
103            } else {
104                *val = INVALID;
105            }
106        }
107
108        // Shift on unaligned read (masking is done later)
109        *val >>= (port - 0xcfc) * 8;
110
111        VMKIT_PCI_DEBUG("read %x from 0x%x\n", *val, port);
112        break;
113
114    default:
115        VMKIT_PCI_DEBUG("read from invalid port\n");
116        break;
117    }
118
119    return 0;
120}
121
122static struct pci_bus *pci_new_bus(void)
123{
124    struct pci_bus *bus = calloc(1, sizeof(struct pci_bus));
125    return bus;
126}
127
128struct pci *pci_new(void)
129{
130    struct pci *pci = calloc(1, sizeof(struct pci));
131
132    pci->bus[0] = pci_new_bus();
133
134    // Put a host-bridge on the bus (Linux expects to find one)
135    struct pci_device *bridge = pci_hostbridge_new();
136    pci_attach_device(pci, 0, 0, bridge);
137
138    return pci;
139}
140
141int pci_attach_device(struct pci *pci, uint8_t busnr, uint8_t devnr,
142                      struct pci_device *device)
143{
144    struct pci_bus *bus = pci->bus[busnr];
145
146    if(bus == NULL) {
147        return -1;
148    }
149
150    if(bus->device[devnr] != NULL) {
151        return -2;
152    }
153
154    bus->device[devnr] = device;
155    return 0;
156}
157