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