AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
pci.c
Go to the documentation of this file.
1
13
14#include <core/system.h>
15#include <core/module.h>
16#include <core/arch.h>
17
18#include <dev/pci.h>
19#include <cpu/io.h>
20#include <mm/mm.h>
21
22/* the platform's host bridge; without one, there are no devices */
23static struct pci_host *host;
24
28{
29 struct pci_dev d = {.bus = bus, .dev = dev, .func = func};
30 return host ? host->read(host, &d, reg) : 0xFFFFFFFF;
31}
32
35{
36 struct pci_dev d = {.bus = bus, .dev = dev, .func = func};
37
38 if (host)
39 host->write(host, &d, reg, val);
40}
41
44{
45 return pci_read_dword(bus, dev, func, 0x00) & 0xFFFF;
46}
47
50{
51 return (pci_read_dword(bus, dev, func, 0x00) >> 16) & 0xFFFF;
52}
53
56{
57 return (pci_read_dword(bus, dev, func, 0x08) >> 24) & 0xFF;
58}
59
62{
63 return (pci_read_dword(bus, dev, func, 0x08) >> 16) & 0xFF;
64}
65
69{
70 return (pci_read_dword(bus, dev, func, 0x0C) >> 16) & 0xFF;
71}
72
75{
76 return pci_read_dword(bus, dev, func, 0x10 + 4 * id);
77}
78
87int pci_device_scan(uint16_t vendor_id, uint16_t device_id, struct pci_dev *ref)
88{
89 uint8_t bus = 0;
90 for (uint8_t dev = 0; dev < 32; ++dev) {
91 for (uint8_t func = 0; func < 8; ++func) {
92 uint16_t _vendor_id = get_vendor_id(bus, dev, func);
93 if (_vendor_id != 0xFFFF) {
94 uint16_t _device_id = get_device_id(bus, dev, func);
95 if (_vendor_id == vendor_id && _device_id == device_id) {
96 if (ref) {
97 ref->bus = bus;
98 ref->dev = dev;
99 ref->func = func;
100 }
101
102 return 0;
103 }
104 }
105 }
106 }
107
108 return -1;
109}
110
119{
120 return (pci_read_dword(dev->bus, dev->dev, dev->func, off) >> ((off & 3) * 8)) & 0xFF;
121}
122
131{
132 return (pci_read_dword(dev->bus, dev->dev, dev->func, off) >> ((off & 2) * 8)) & 0xFFFF;
133}
134
143{
144 return pci_read_dword(dev->bus, dev->dev, dev->func, off);
145}
146
155void pci_reg8_write(struct pci_dev *dev, uint8_t off, uint8_t val)
156{
157 uint32_t oval = pci_read_dword(dev->bus, dev->dev, dev->func, off);
158 oval = (oval & ~(0xFFUL << (off & 3) * 8)) | ((uint32_t) val << (off & 3) * 8);
159 pci_write_dword(dev->bus, dev->dev, dev->func, off, oval);
160}
161
171{
172 uint32_t oval = pci_read_dword(dev->bus, dev->dev, dev->func, off);
173 oval = (oval & ~(0xFFFFUL << (off & 2) * 8)) | ((uint32_t) val << (off & 2) * 8);
174 pci_write_dword(dev->bus, dev->dev, dev->func, off, oval);
175}
176
185{
186 pci_write_dword(dev->bus, dev->dev, dev->func, off, val);
187}
188
191{
192 for (uint8_t func = 0; func < 8; ++func) {
193 uint16_t vendor_id = get_vendor_id(bus, dev, func);
194
195 if (vendor_id != 0xFFFF) {
196 uint32_t device_id = get_device_id(bus, dev, func);
197 uint32_t class_code = get_class_code(bus, dev, func);
198 uint32_t subclass_code = get_subclass_code(bus, dev, func);
199
200 printk("Bus: %d, Device: %d, Function %d\n", bus, dev, func);
201 printk(" -> Vendor ID: %x, Device ID: %x, Class: %x, Subclass: %x\n", vendor_id, device_id, class_code, subclass_code);
202 }
203 }
204}
205
207static void scan_bus(uint8_t bus)
208{
209 for (uint8_t dev = 0; dev < 32; ++dev) {
211 }
212}
213
221{
222 return 0;
223}
224
225struct dev pcidev = {
226 .probe = pci_prope,
227};
228
229
230
231/*
232 * The interface drivers use
233 */
234
244int pci_scan_device(uint8_t class, uint8_t subclass, struct pci_dev *_dev, size_t nr)
245{
246 size_t idx = 0;
247 uint8_t bus = 0;
248
249 /* no room for any */
250 if (!nr)
251 return 0;
252
253 for (uint8_t dev = 0; dev < 32; ++dev) {
254 for (uint8_t func = 0; func < 8; ++func) {
255 uint16_t vendor_id = get_vendor_id(bus, dev, func);
256
257 if (vendor_id != 0xFFFF) {
258 uint32_t class_code = get_class_code(bus, dev, func);
259 uint32_t subclass_code = get_subclass_code(bus, dev, func);
260
261 if (class_code == class && subclass_code == subclass) {
262 _dev[idx].bus = bus;
263 _dev[idx].dev = dev;
264 _dev[idx].func = func;
265 ++idx;
266 --nr;
267
268 if (!nr)
269 return idx;
270 }
271 }
272 }
273 }
274
275 return idx;
276}
277
286{
287 return pci_read_dword(dev->bus, dev->dev, dev->func, 0x10 + 4 * id);
288}
289
290#define PCI_COMMAND 0x04
291#define PCI_COMMAND_IO 0x0001
292#define PCI_COMMAND_MEMORY 0x0002
293#define PCI_COMMAND_MASTER 0x0004
294#define PCI_INTERRUPT_LINE 0x3C
295
307int pci_bar(struct pci_dev *dev, int id, struct pci_bar *bar)
308{
309 uint8_t off = 0x10 + 4 * id;
311 uint32_t lo = pci_reg32_read(dev, off), hi = 0, size_lo, size_hi = 0xFFFFFFFF;
312
314
315 pci_reg32_write(dev, off, 0xFFFFFFFF);
316 size_lo = pci_reg32_read(dev, off);
317 pci_reg32_write(dev, off, lo);
318
319 bar->io = lo & 1;
320
321 /* a 64-bit memory BAR */
322 if (!bar->io && (lo & 0x6) == 0x4) {
323 hi = pci_reg32_read(dev, off + 4);
324 pci_reg32_write(dev, off + 4, 0xFFFFFFFF);
325 size_hi = pci_reg32_read(dev, off + 4);
326 pci_reg32_write(dev, off + 4, hi);
327 }
328
330
331 uint64_t mask = bar->io ? ~(uint64_t) 0x3 : ~(uint64_t) 0xF;
332 uint64_t sized = ((uint64_t) size_hi << 32 | size_lo) & mask;
333
334 if (bar->io)
335 sized |= 0xFFFFFFFF00000000ULL | 0xFFFF0000; /* ports are 16 bits */
336
337 bar->addr = ((uint64_t) hi << 32 | lo) & mask;
338 bar->size = ~sized + 1;
339
340 return size_lo ? 0 : -1;
341}
342
355
356#define PCI_INTERRUPT_PIN 0x3D
357
367int pci_irq(struct pci_dev *dev)
368{
369 if (!host || !host->nirq)
371
373
374 if (pin < 1 || pin > 4)
375 return -1;
376
377 return host->irq[(dev->dev + pin - 1) % host->nirq];
378}
379
380#define ALIGN_TO(x, a) (((x) + (a) - 1) & ~((uint64_t) (a) - 1))
381
385static void pci_assign(void)
386{
387 uint64_t mem = host->mem_base, io = host->io_base;
388
389 for (uint8_t slot = 0; slot < 32; ++slot) {
390 for (uint8_t func = 0; func < 8; ++func) {
391 struct pci_dev dev = {.bus = 0, .dev = slot, .func = func};
392
393 /* skip a missing function, and a bridge (its BARs have another
394 * layout) */
395 if (get_vendor_id(0, slot, func) == 0xFFFF || (get_header_type(0, slot, func) & 0x7F))
396 continue;
397
398 for (int id = 0; id < 6; ++id) {
399 uint8_t off = 0x10 + 4 * id;
400 uint32_t lo = pci_reg32_read(&dev, off);
401 int wide = !(lo & 1) && (lo & 0x6) == 0x4;
402 struct pci_bar bar;
403
404 if (!pci_bar(&dev, id, &bar) && !bar.addr) {
405 uint64_t *next = bar.io ? &io : &mem;
406 uint64_t end = bar.io ? host->io_base + host->io_size
408 uint64_t addr = ALIGN_TO(*next, bar.size);
409
410 if (addr + bar.size <= end) {
411 pci_reg32_write(&dev, off, (uint32_t) addr | (lo & (bar.io ? 0x3 : 0xF)));
412
413 if (wide)
414 pci_reg32_write(&dev, off + 4, (uint32_t) (addr >> 32));
415
416 *next = addr + bar.size;
417 printk("pci: %d:%d.%d BAR %d: %s at %p, %ld bytes\n", 0, slot, func, id,
418 bar.io ? "ports" : "memory", (void *) (uintptr_t) addr, bar.size);
419 } else {
420 printk("pci: %d:%d.%d BAR %d: no room for %ld bytes\n", 0, slot, func, id, bar.size);
421 }
422 }
423
424 if (wide)
425 ++id;
426 }
427
428 /* a device with one function only: don't look for others */
429 if (!func && !(get_header_type(0, slot, 0) & 0x80))
430 break;
431 }
432 }
433}
434
443int pci_host_set(struct pci_host *h)
444{
445 if (h->config.type != IOADDR_PORT) {
446 void *config = arch_mmio_map(h->config.addr, h->size);
447
448 if (!config)
449 return -ENOMEM;
450
451 printk("pci: %s at %p\n", h->name, (void *) h->config.addr);
452 h->config.addr = (uintptr_t) config;
453 } else {
454 printk("pci: %s at port %p\n", h->name, (void *) h->config.addr);
455 }
456
457 host = h;
458
459 if (h->mem_size || h->io_size)
460 pci_assign();
461
462 return 0;
463}
464
465#define PCI_STATUS 0x06
466#define PCI_STATUS_CAP_LIST 0x10
467#define PCI_CAPABILITIES 0x34
468#define PCI_COMMAND_INTX_OFF 0x400
469
479{
481 return 0;
482
484
485 /* follow at most 48 of them: each is at least 4 bytes, after the
486 * header, so a longer list is a broken one */
487 for (int i = 0; off && i < 48; ++i) {
488 if (pci_reg8_read(dev, off) == id)
489 return off;
490
491 off = pci_reg8_read(dev, off + 1) & 0xfc;
492 }
493
494 return 0;
495}
496
497/* MSI's capability: its message control, the message's address (with its
498 * high half if it is 64-bit), then the message's data */
499#define MSI_CTRL 2
500#define MSI_ADDR 4
501#define MSI_CTRL_ENABLE 0x0001
502#define MSI_CTRL_MME 0x0070 /* how many messages: 0 means 1 */
503#define MSI_CTRL_64BIT 0x0080
504
518{
521 uint32_t data;
522 int irq;
523
524 if (!cap || !arch_msi_alloc)
525 return -ENODEV;
526
527 if ((irq = arch_msi_alloc(handler, arg, &addr, &data)) < 0)
528 return irq;
529
530 uint16_t ctrl = pci_reg16_read(dev, cap + MSI_CTRL);
531
534
535 if (ctrl & MSI_CTRL_64BIT) {
536 pci_reg32_write(dev, cap + MSI_ADDR + 4, (uint32_t) (addr >> 32));
537 pci_reg16_write(dev, cap + MSI_ADDR + 8, data);
538 } else {
539 pci_reg16_write(dev, cap + MSI_ADDR + 4, data);
540 }
541
544
545 return irq;
546}
547
int arch_msi_alloc(irq_handler_t handler, void *arg, uint64_t *addr, uint32_t *data)
Gets an IRQ for a device's MSI, and installs its handler.
Definition apic.c:271
#define IOADDR_PORT
Definition io.h:89
void(*) irq_handler_t(void *arg)
Definition irq.h:21
#define MODULE_INIT(name, i, f)
Definition module.h:10
int pci_irq(struct pci_dev *dev)
Finds the IRQ that a function's interrupt pin comes to.
Definition pci.c:367
int pci_prope()
The PCI module's initialization, which has nothing to do: drivers find their devices themselves.
Definition pci.c:220
static struct pci_host * host
Definition pci.c:23
static void pci_assign(void)
Assigns addresses to the BARs that no firmware assigned, on bus 0's devices: each BAR in the host's w...
Definition pci.c:385
#define PCI_STATUS
Definition pci.c:465
uint16_t pci_reg16_read(struct pci_dev *dev, uint8_t off)
Reads a 16-bit configuration register.
Definition pci.c:130
#define PCI_COMMAND_INTX_OFF
Definition pci.c:468
#define MSI_CTRL_64BIT
Definition pci.c:503
uint32_t pci_reg32_read(struct pci_dev *dev, uint8_t off)
Reads a 32-bit configuration register.
Definition pci.c:142
static void pci_write_dword(uint8_t bus, uint8_t dev, uint8_t func, uint16_t reg, uint32_t val)
Writes a 32-bit configuration register, if there is a host bridge.
Definition pci.c:34
void pci_reg32_write(struct pci_dev *dev, uint8_t off, uint32_t val)
Writes a 32-bit configuration register.
Definition pci.c:184
int pci_device_scan(uint16_t vendor_id, uint16_t device_id, struct pci_dev *ref)
Finds a device by its vendor and device IDs, on bus 0.
Definition pci.c:87
int pci_scan_device(uint8_t class, uint8_t subclass, struct pci_dev *_dev, size_t nr)
Finds the functions of a class and subclass, on bus 0.
Definition pci.c:244
#define ALIGN_TO(x, a)
Definition pci.c:380
int pci_msi_enable(struct pci_dev *dev, irq_handler_t handler, void *arg)
Has a function send its interrupts as messages (MSI) instead of on its interrupt line: one message,...
Definition pci.c:517
uint32_t pci_read_bar(struct pci_dev *dev, uint8_t id)
Reads a base address register as it is, flag bits included.
Definition pci.c:285
#define PCI_INTERRUPT_PIN
Definition pci.c:356
#define PCI_STATUS_CAP_LIST
Definition pci.c:466
static uint8_t get_class_code(uint8_t bus, uint8_t dev, uint8_t func)
Returns a function's class code.
Definition pci.c:55
#define MSI_CTRL
Definition pci.c:499
int pci_host_set(struct pci_host *h)
Sets the platform's host bridge, from platform_init().
Definition pci.c:443
#define PCI_INTERRUPT_LINE
Definition pci.c:294
#define PCI_CAPABILITIES
Definition pci.c:467
#define PCI_COMMAND_IO
Definition pci.c:291
#define PCI_COMMAND_MASTER
Definition pci.c:293
static uint32_t pci_read_dword(uint8_t bus, uint8_t dev, uint8_t func, uint16_t reg)
Reads a 32-bit configuration register, or returns all ones (no device) if there is no host bridge.
Definition pci.c:27
#define PCI_COMMAND_MEMORY
Definition pci.c:292
void pci_enable(struct pci_dev *dev)
Turns a function's memory and port decoding on, and bus mastering, so that it can do DMA.
Definition pci.c:349
#define MSI_CTRL_ENABLE
Definition pci.c:501
void pci_reg8_write(struct pci_dev *dev, uint8_t off, uint8_t val)
Writes an 8-bit configuration register: the 32-bit register it's in is read, changed and written back...
Definition pci.c:155
void pci_reg16_write(struct pci_dev *dev, uint8_t off, uint16_t val)
Writes a 16-bit configuration register: the 32-bit register it's in is read, changed and written back...
Definition pci.c:170
#define MSI_ADDR
Definition pci.c:500
static uint32_t get_bar(uint8_t bus, uint8_t dev, uint8_t func, uint8_t id)
Returns a function's base address register id, as it is.
Definition pci.c:74
struct dev pcidev
Definition pci.c:225
static uint8_t get_subclass_code(uint8_t bus, uint8_t dev, uint8_t func)
Returns a function's subclass code.
Definition pci.c:61
static void scan_device(uint8_t bus, uint8_t dev)
Prints the functions of a device, for debugging.
Definition pci.c:190
static uint16_t get_device_id(uint8_t bus, uint8_t dev, uint8_t func)
Returns a function's device ID.
Definition pci.c:49
#define PCI_COMMAND
Definition pci.c:290
static uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func)
Returns a function's vendor ID: all ones if there's no function.
Definition pci.c:43
uint8_t pci_cap_find(struct pci_dev *dev, uint8_t id)
Finds one of a function's capabilities.
Definition pci.c:478
uint8_t pci_reg8_read(struct pci_dev *dev, uint8_t off)
Reads an 8-bit configuration register.
Definition pci.c:118
static void scan_bus(uint8_t bus)
Prints the devices of a bus, for debugging (nothing calls it).
Definition pci.c:207
static uint8_t get_header_type(uint8_t bus, uint8_t dev, uint8_t func)
Returns a function's header type: bit 7 set for a device with several functions, the rest 0 for a dev...
Definition pci.c:68
#define MSI_CTRL_MME
Definition pci.c:502
#define PCI_CAP_MSI
Definition pci.h:94
void * arch_mmio_map(uintptr_t paddr, size_t size)
Maps a device's registers into the kernel's address space, uncached (memory-mapped I/O).
Definition pmap.c:508
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
unsigned char uint8_t
Definition stddef.h:20
unsigned long long int uint64_t
Definition stddef.h:23
unsigned short int uint16_t
Definition stddef.h:21
A device driver: its operations, any of which may be NULL.
Definition dev.h:28
An installed handler, on its interrupt's list.
Definition irq.c:19
uintptr_t addr
Definition io.h:97
char type
Definition io.h:96
A base address register (BAR): where the device's registers are, how big they are,...
Definition pci.h:81
uint64_t addr
Definition pci.h:82
uint64_t size
Definition pci.h:83
int io
Definition pci.h:84
A PCI function: its bus, device (slot) and function numbers.
Definition pci.h:18
uint8_t dev
Definition pci.h:20
uint8_t func
Definition pci.h:21
uint8_t bus
Definition pci.h:19
The host bridge, which the platform describes (platform_init() sets it): how its configuration space ...
Definition pci.h:32
const char * name
Definition pci.h:33
uint64_t io_size
Definition pci.h:53
uint64_t io_base
Definition pci.h:53
uint64_t mem_size
Definition pci.h:52
struct ioaddr config
Definition pci.h:38
uint32_t(*) read(struct pci_host *host, struct pci_dev *dev, uint16_t reg)
Definition pci.h:40
unsigned irq[4]
Definition pci.h:47
void(*) write(struct pci_host *host, struct pci_dev *dev, uint16_t reg, uint32_t val)
Definition pci.h:41
size_t size
Definition pci.h:39
int nirq
Definition pci.h:46
uint64_t mem_base
Definition pci.h:52