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
30 /* user facing routines */
pci_get_last_bus()31 int pci_get_last_bus() {
32 if (!pcib) {
33 return ERR_NOT_CONFIGURED;
34 }
35
36 return pcib->get_last_bus();
37 }
38
pci_get_last_segment()39 int pci_get_last_segment() {
40 // currently hard coded to 1 segment
41 return 0;
42 }
43
pci_read_config_byte(const pci_location_t state,uint32_t reg,uint8_t * value)44 status_t pci_read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
45 if (!pcib) return ERR_NOT_CONFIGURED;
46
47 AutoSpinLock guard(&lock);
48
49 int res = pcib->read_config_byte(state, reg, value);
50
51 return res;
52 }
pci_read_config_half(const pci_location_t state,uint32_t reg,uint16_t * value)53 status_t pci_read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
54 if (!pcib) return ERR_NOT_CONFIGURED;
55
56 AutoSpinLock guard(&lock);
57
58 int res = pcib->read_config_half(state, reg, value);
59
60 return res;
61 }
62
pci_read_config_word(const pci_location_t state,uint32_t reg,uint32_t * value)63 status_t pci_read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
64 if (!pcib) return ERR_NOT_CONFIGURED;
65
66 AutoSpinLock guard(&lock);
67
68 int res = pcib->read_config_word(state, reg, value);
69
70 return res;
71 }
72
pci_write_config_byte(const pci_location_t state,uint32_t reg,uint8_t value)73 status_t pci_write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
74 if (!pcib) return ERR_NOT_CONFIGURED;
75
76 AutoSpinLock guard(&lock);
77
78 int res = pcib->write_config_byte(state, reg, value);
79
80 return res;
81 }
82
pci_write_config_half(const pci_location_t state,uint32_t reg,uint16_t value)83 status_t pci_write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
84 if (!pcib) return ERR_NOT_CONFIGURED;
85
86 AutoSpinLock guard(&lock);
87
88 int res = pcib->write_config_half(state, reg, value);
89
90 return res;
91 }
92
pci_write_config_word(const pci_location_t state,uint32_t reg,uint32_t value)93 status_t pci_write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
94 if (!pcib) return ERR_NOT_CONFIGURED;
95
96 AutoSpinLock guard(&lock);
97
98 int res = pcib->write_config_word(state, reg, value);
99
100 return res;
101 }
102
pci_read_config(const pci_location_t loc,pci_config_t * config)103 status_t pci_read_config(const pci_location_t loc, pci_config_t *config) {
104 status_t err;
105
106 *config = {};
107
108 // TODO: handle endian swapping (if necessary)
109
110 // define some helper routines to read config offsets in the proper unit
111 size_t next_index;
112 auto read_byte = [&]() -> uint8_t {
113 uint8_t val;
114 err = pci_read_config_byte(loc, next_index, &val);
115 next_index++;
116 if (err < 0) return 0;
117 return val;
118 };
119
120 auto read_half = [&]() -> uint16_t {
121 uint16_t val;
122 err = pci_read_config_half(loc, next_index, &val);
123 next_index += 2;
124 if (err < 0) return 0;
125 return val;
126 };
127
128 auto read_word = [&]() -> uint32_t {
129 uint32_t val;
130 err = pci_read_config_word(loc, next_index, &val);
131 next_index += 4;
132 if (err < 0) return 0;
133 return val;
134 };
135
136 // shared, standard part of the pci config space
137 /*
138 uint16_t vendor_id;
139 uint16_t device_id;
140 uint16_t command;
141 uint16_t status;
142 uint8_t revision_id_0;
143 uint8_t program_interface;
144 uint8_t sub_class;
145 uint8_t base_class;
146 uint8_t cache_line_size;
147 uint8_t latency_timer;
148 uint8_t header_type;
149 uint8_t bist;
150 */
151 next_index = 0;
152 config->vendor_id = read_half(); if (err < 0) return err;
153 config->device_id = read_half(); if (err < 0) return err;
154 config->command = read_half(); if (err < 0) return err;
155 config->status = read_half(); if (err < 0) return err;
156 config->revision_id_0 = read_byte(); if (err < 0) return err;
157 config->program_interface = read_byte(); if (err < 0) return err;
158 config->sub_class = read_byte(); if (err < 0) return err;
159 config->base_class = read_byte(); if (err < 0) return err;
160 config->cache_line_size = read_byte(); if (err < 0) return err;
161 config->latency_timer = read_byte(); if (err < 0) return err;
162 config->header_type = read_byte(); if (err < 0) return err;
163 config->bist = read_byte(); if (err < 0) return err;
164 DEBUG_ASSERT(next_index == 0x10); // should have read this many bytes at this point
165
166 // based on the type field, read two different types
167 const uint8_t type = config->header_type & 0x7f;
168 if (type == 0) {
169 /*
170 uint32_t base_addresses[6];
171 uint32_t cardbus_cis_ptr;
172 uint16_t subsystem_vendor_id;
173 uint16_t subsystem_id;
174 uint32_t expansion_rom_address;
175 uint8_t capabilities_ptr;
176 uint8_t reserved_0[3];
177 uint32_t reserved_1;
178 uint8_t interrupt_line;
179 uint8_t interrupt_pin;
180 uint8_t min_grant;
181 uint8_t max_latency;
182 */
183 config->type0.base_addresses[0] = read_word(); if (err < 0) return err;
184 config->type0.base_addresses[1] = read_word(); if (err < 0) return err;
185 config->type0.base_addresses[2] = read_word(); if (err < 0) return err;
186 config->type0.base_addresses[3] = read_word(); if (err < 0) return err;
187 config->type0.base_addresses[4] = read_word(); if (err < 0) return err;
188 config->type0.base_addresses[5] = read_word(); if (err < 0) return err;
189 config->type0.cardbus_cis_ptr = read_word(); if (err < 0) return err;
190 config->type0.subsystem_vendor_id = read_half(); if (err < 0) return err;
191 config->type0.subsystem_id = read_half(); if (err < 0) return err;
192 config->type0.expansion_rom_address = read_word(); if (err < 0) return err;
193 config->type0.capabilities_ptr = read_byte(); if (err < 0) return err;
194 next_index += 3 + 4; // 7 bytes of reserved space
195 config->type0.interrupt_line = read_byte(); if (err < 0) return err;
196 config->type0.interrupt_pin = read_byte(); if (err < 0) return err;
197 config->type0.min_grant = read_byte(); if (err < 0) return err;
198 config->type0.max_latency = read_byte(); if (err < 0) return err;
199 DEBUG_ASSERT(next_index == 0x40); // should have read this many bytes at this point
200 } else if (type == 1) {
201 /*
202 uint32_t base_addresses[2];
203 uint8_t primary_bus;
204 uint8_t secondary_bus;
205 uint8_t subordinate_bus;
206 uint8_t secondary_latency_timer;
207 uint8_t io_base;
208 uint8_t io_limit;
209 uint16_t secondary_status;
210 uint16_t memory_base;
211 uint16_t memory_limit;
212 uint16_t prefetchable_memory_base;
213 uint16_t prefetchable_memory_limit;
214 uint32_t prefetchable_base_upper;
215 uint32_t prefetchable_limit_upper;
216 uint16_t io_base_upper;
217 uint16_t io_limit_upper;
218 uint8_t capabilities_ptr;
219 uint8_t reserved_0[3];
220 uint32_t expansion_rom_address;
221 uint8_t interrupt_line;
222 uint8_t interrupt_pin;
223 uint16_t bridge_control;
224 */
225 config->type1.base_addresses[0] = read_word(); if (err < 0) return err;
226 config->type1.base_addresses[1] = read_word(); if (err < 0) return err;
227 config->type1.primary_bus = read_byte(); if (err < 0) return err;
228 config->type1.secondary_bus = read_byte(); if (err < 0) return err;
229 config->type1.subordinate_bus = read_byte(); if (err < 0) return err;
230 config->type1.secondary_latency_timer = read_byte(); if (err < 0) return err;
231 config->type1.io_base = read_byte(); if (err < 0) return err;
232 config->type1.io_limit = read_byte(); if (err < 0) return err;
233 config->type1.secondary_status = read_half(); if (err < 0) return err;
234 config->type1.memory_base = read_half(); if (err < 0) return err;
235 config->type1.memory_limit = read_half(); if (err < 0) return err;
236 config->type1.prefetchable_memory_base = read_half(); if (err < 0) return err;
237 config->type1.prefetchable_memory_limit = read_half(); if (err < 0) return err;
238 config->type1.prefetchable_base_upper = read_word(); if (err < 0) return err;
239 config->type1.prefetchable_limit_upper = read_word(); if (err < 0) return err;
240 config->type1.io_base_upper = read_half(); if (err < 0) return err;
241 config->type1.io_limit_upper = read_half(); if (err < 0) return err;
242 config->type1.capabilities_ptr = read_byte(); if (err < 0) return err;
243 next_index += 3; // 3 reserved bytes
244 config->type1.expansion_rom_address = read_word(); if (err < 0) return err;
245 config->type1.interrupt_line = read_byte(); if (err < 0) return err;
246 config->type1.interrupt_pin = read_byte(); if (err < 0) return err;
247 config->type1.bridge_control = read_half(); if (err < 0) return err;
248
249 DEBUG_ASSERT(next_index == 0x40); // should have read this many bytes at this point
250 } else {
251 // cant handle other types
252 return ERR_NOT_VALID;
253 }
254
255 return NO_ERROR;
256 }
257
pci_init_legacy()258 status_t pci_init_legacy() {
259 LTRACE_ENTRY;
260
261 DEBUG_ASSERT(pcib == nullptr);
262
263 // try a series of detection mechanisms based on legacy PCI access on x86 PCs
264
265 // try to BIOS32 access first, if present
266 if ((pcib = pci_bios32::detect())) {
267 dprintf(INFO, "PCI: pci bios functions installed\n");
268 dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
269 return NO_ERROR;
270 }
271
272 // try type 1 access
273 if ((pcib = pci_type1::detect())) {
274 dprintf(INFO, "PCI: pci type1 functions installed\n");
275 dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
276 return NO_ERROR;
277 }
278
279 // if we couldn't find anything, leave pcib null
280 return ERR_NOT_FOUND;
281 }
282
pci_init_ecam(paddr_t ecam_base,uint16_t segment,uint8_t start_bus,uint8_t end_bus)283 status_t pci_init_ecam(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) {
284 LTRACEF("base %#lx, segment %hu, bus [%hhu...%hhu]\n", ecam_base, segment, start_bus, end_bus);
285
286 DEBUG_ASSERT(pcib == nullptr);
287
288 if ((pcib = pci_ecam::detect(ecam_base, segment, start_bus, end_bus))) {
289 dprintf(INFO, "PCI: pci ecam functions installed\n");
290 dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
291 return NO_ERROR;
292 }
293
294 // if we couldn't find anything, leave pcib null
295 return ERR_NOT_FOUND;
296 }
297
298