1 /*
2  * Copyright (c) 2009 Corey Tabaka
3  * Copyright (c) 2020 Travis Geiselbrecht
4  *
5  * Use of this source code is governed by a MIT-style
6  * license that can be found in the LICENSE file or at
7  * https://opensource.org/licenses/MIT
8  */
9 #include <dev/bus/pci.h>
10 
11 #include <lk/debug.h>
12 #include <lk/err.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <kernel/thread.h>
16 #include <kernel/spinlock.h>
17 #include <lk/trace.h>
18 
19 #include "pci_priv.h"
20 
21 #define LOCAL_TRACE 0
22 
23 // Largely C level api for the PCI bus manager
24 
25 namespace {
26 SpinLock lock;
27 pci_backend *pcib = nullptr;
28 } // namespace
29 
pci_get_last_bus()30 int pci_get_last_bus() {
31     if (!pcib) {
32         return ERR_NOT_CONFIGURED;
33     }
34 
35     return pcib->get_last_bus();
36 }
37 
38 /* user facing routines */
pci_find_pci_device(pci_location_t * state,uint16_t device_id,uint16_t vendor_id,uint16_t index)39 status_t pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
40     LTRACEF("device_id dev %#hx vendor %#hx index %#hx\n", device_id, vendor_id, index);
41 
42     if (!pcib) return ERR_NOT_CONFIGURED;
43 
44     AutoSpinLock guard(&lock);
45 
46     int res = pcib->find_pci_device(state, device_id, vendor_id, index);
47 
48     return res;
49 }
50 
pci_find_pci_class_code(pci_location_t * state,uint32_t class_code,uint16_t index)51 status_t pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
52     LTRACEF("device_id class %#x index %#hx\n", class_code, index);
53 
54     if (!pcib) return ERR_NOT_CONFIGURED;
55 
56     AutoSpinLock guard(&lock);
57 
58     int res = pcib->find_pci_class_code(state, class_code, index);
59 
60     return res;
61 }
62 
pci_read_config_byte(const pci_location_t * state,uint32_t reg,uint8_t * value)63 status_t pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
64     if (!pcib) return ERR_NOT_CONFIGURED;
65 
66     AutoSpinLock guard(&lock);
67 
68     int res = pcib->read_config_byte(state, reg, value);
69 
70     return res;
71 }
pci_read_config_half(const pci_location_t * state,uint32_t reg,uint16_t * value)72 status_t pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
73     if (!pcib) return ERR_NOT_CONFIGURED;
74 
75     AutoSpinLock guard(&lock);
76 
77     int res = pcib->read_config_half(state, reg, value);
78 
79     return res;
80 }
81 
pci_read_config_word(const pci_location_t * state,uint32_t reg,uint32_t * value)82 status_t pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
83     if (!pcib) return ERR_NOT_CONFIGURED;
84 
85     AutoSpinLock guard(&lock);
86 
87     int res = pcib->read_config_word(state, reg, value);
88 
89     return res;
90 }
91 
pci_write_config_byte(const pci_location_t * state,uint32_t reg,uint8_t value)92 status_t pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
93     if (!pcib) return ERR_NOT_CONFIGURED;
94 
95     AutoSpinLock guard(&lock);
96 
97     int res = pcib->write_config_byte(state, reg, value);
98 
99     return res;
100 }
101 
pci_write_config_half(const pci_location_t * state,uint32_t reg,uint16_t value)102 status_t pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
103     if (!pcib) return ERR_NOT_CONFIGURED;
104 
105     AutoSpinLock guard(&lock);
106 
107     int res = pcib->write_config_half(state, reg, value);
108 
109     return res;
110 }
111 
pci_write_config_word(const pci_location_t * state,uint32_t reg,uint32_t value)112 status_t pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
113     if (!pcib) return ERR_NOT_CONFIGURED;
114 
115     AutoSpinLock guard(&lock);
116 
117     int res = pcib->write_config_word(state, reg, value);
118 
119     return res;
120 }
121 
pci_get_irq_routing_options(irq_routing_entry * entries,uint16_t * count,uint16_t * pci_irqs)122 status_t pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs) {
123     if (!pcib) return ERR_NOT_CONFIGURED;
124 
125     // TODO: highly bios32 specific, abstract this differently
126     pci_backend::irq_routing_options_t options;
127     options.size = sizeof(irq_routing_entry) * *count;
128     options.selector = 0x10; // XXX actually DATA_SELECTOR
129     options.offset = entries;
130 
131     int res;
132     {
133         AutoSpinLock guard(&lock);
134 
135         res = pcib->get_irq_routing_options(&options, pci_irqs);
136     }
137 
138     *count = options.size / sizeof(irq_routing_entry);
139 
140     return res;
141 }
142 
pci_set_irq_hw_int(const pci_location_t * state,uint8_t int_pin,uint8_t irq)143 status_t pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
144     if (!pcib) return ERR_NOT_CONFIGURED;
145 
146     AutoSpinLock guard(&lock);
147 
148     int res = pcib->set_irq_hw_int(state, int_pin, irq);
149 
150     return res;
151 }
152 
pci_init_legacy()153 status_t pci_init_legacy() {
154     LTRACE_ENTRY;
155 
156     DEBUG_ASSERT(pcib == nullptr);
157 
158     // try a series of detection mechanisms based on legacy PCI access on x86 PCs
159 
160     // try to BIOS32 access first, if present
161     if ((pcib = pci_bios32::detect())) {
162         dprintf(INFO, "PCI: pci bios functions installed\n");
163         dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
164         return NO_ERROR;
165     }
166 
167     // try type 1 access
168     if ((pcib = pci_type1::detect())) {
169         dprintf(INFO, "PCI: pci type1 functions installed\n");
170         dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
171         return NO_ERROR;
172     }
173 
174     // if we couldn't find anything, leave pcib null
175     return ERR_NOT_FOUND;
176 }
177 
pci_init_ecam(paddr_t ecam_base,uint16_t segment,uint8_t start_bus,uint8_t end_bus)178 status_t pci_init_ecam(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) {
179     LTRACEF("base %#lx, segment %hu, bus [%hhu...%hhu]\n", ecam_base, segment, start_bus, end_bus);
180 
181     DEBUG_ASSERT(pcib == nullptr);
182 
183     if ((pcib = pci_ecam::detect(ecam_base, segment, start_bus, end_bus))) {
184         dprintf(INFO, "PCI: pci ecam functions installed\n");
185         dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
186         return NO_ERROR;
187     }
188 
189     // if we couldn't find anything, leave pcib null
190     return ERR_NOT_FOUND;
191 }
192 
193