BigOS/kernel/pci.c
2023-01-15 16:10:09 +08:00

174 lines
5.2 KiB
C

#include "type.h"
#include "assert.h"
#include "console.h"
#include "const.h"
#include "keyboard.h"
#include "memman.h"
#include "proc.h"
#include "protect.h"
#include "proto.h"
#include "stdio.h"
#include "string.h"
#include "x86.h"
#include "global.h"
#include "tty.h"
#include "pci.h"
/*
* Copyright (C) 2020-2022 The opuntiaOS Project Authors.
* + Contributed by Nikita Melekhin <nimelehin@gmail.com>
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#define DEBUG_PCI
pci_dev_t pci_devs[NR_PCI_DEV];
static uint32_t _pci_do_read_bar(uint8_t bus, uint8_t device, uint8_t function,
uint8_t bar_id) {
uint32_t header_type = pci_read(bus, device, function, 0x0e) & 0x7f;
uint8_t max_bars = 6 - (header_type * 4);
if (bar_id >= max_bars) return 0;
uint32_t bar_val = pci_read(bus, device, function, 0x10 + 4 * bar_id);
return bar_val;
}
static bar_t _pci_get_bar(uint8_t bus, uint8_t device, uint8_t function,
uint8_t bar_id) {
bar_t result;
uint32_t bar_val = _pci_do_read_bar(bus, device, function, bar_id);
result.type = (bar_val & 0x1) ? INPUT_OUTPUT : MEMORY_MAPPED;
if (result.type == MEMORY_MAPPED) {
} else {
result.address = (uint32_t)((bar_val >> 2) << 2);
result.prefetchable = 0;
}
return result;
}
uint32_t pci_read(uint16_t bus, uint16_t device, uint16_t function,
uint32_t offset) {
uint32_t id = (0x1 << 31) | ((bus & 0xFF) << 16) | ((device & 0x1F) << 11) |
((function & 0x07) << 8) | (offset & 0xFC);
outl(0xCF8, id);
uint32_t tmp = (uint32_t)(inl(0xCFC) >> (8 * (offset % 4)));
return tmp;
}
void pci_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset,
uint32_t data) {
uint32_t bus32 = bus;
uint32_t device32 = device;
uint16_t function16 = function;
uint32_t address = (1 << 31) | (bus32 << 16) | (device32 << 11) |
(function16 << 8) | (offset & 0xFC);
outl(0xCF8, address);
outl(0xCFC, data);
}
static int dev_type_by_class(pci_dev_t* dd) {
switch (dd->class_id) {
case 0x1:
switch (dd->subclass_id) {
case 0x1:
case 0x3:
case 0x4:
return DEVICE_BUS_CONTROLLER;
default:
return DEVICE_STORAGE;
}
case 0x2:
return DEVICE_NETWORK;
case 0x3:
return DEVICE_DISPLAY;
case 0x6:
return DEVICE_BRIDGE;
default:
#ifdef DEBUG_PCI
kprintf("PCI: DEVICE_UNKNOWN: Class %d subclass %d", dd->class_id,
dd->subclass_id);
#endif
return DEVICE_UNKNOWN;
}
}
static char pci_has_device_functions(uint8_t bus, uint8_t device) {
return pci_read(bus, device, 0, 0x0e) & (1 << 7);
}
int pci_find_devices() {
#ifdef DEBUG_PCI
kprintf("PCI: scanning\n");
#endif
uint8_t bus, device, function;
int count = 0;
for (bus = 0; bus < 8; bus++) {
for (device = 0; device < 32; device++) {
uint8_t functions_count =
pci_has_device_functions(bus, device) == 0 ? 8 : 1;
for (function = 0; function < functions_count; function++) {
pci_dev_t dev =
pci_get_device_desriptor(bus, device, function);
if (dev.vendor_id == 0x0000 || dev.vendor_id == 0xffff) {
continue;
}
for (uint8_t bar_id = 0; bar_id < 6; bar_id++) {
bar_t bar = _pci_get_bar(bus, device, function, bar_id);
if (bar.address && (bar.type == INPUT_OUTPUT)) {
dev.port_base = (uint32_t)bar.address;
}
}
dev.type = dev_type_by_class(&dev);
pci_devs[count ++] = dev;
#ifdef DEBUG_PCI
kprintf("PCI: Vendor %x, devID %x, cl %x scl %x\n",
dev.vendor_id, dev.device_id, dev.class_id,
dev.subclass_id);
#endif
}
}
}
return 0;
}
pci_dev_t pci_get_device_desriptor(uint8_t bus, uint8_t device,
uint8_t function) {
pci_dev_t new_device = {0};
new_device.bus = bus;
new_device.device = device;
new_device.function = function;
new_device.vendor_id = pci_read(bus, device, function, 0x00);
new_device.device_id = pci_read(bus, device, function, 0x02);
new_device.class_id = pci_read(bus, device, function, 0x0b);
new_device.subclass_id = pci_read(bus, device, function, 0x0a);
new_device.interface_id = pci_read(bus, device, function, 0x09);
new_device.revision_id = pci_read(bus, device, function, 0x08);
new_device.interrupt = pci_read(bus, device, function, 0x3c);
return new_device;
}
uint32_t pci_read_bar(pci_dev_t* pci_dev, int bar_id) {
return _pci_do_read_bar(pci_dev->bus, pci_dev->device, pci_dev->function,
bar_id);
}
void init_pci() { pci_find_devices(); }
pci_dev_t* get_pci_bga()
{
for (int i = 0; i < NR_PCI_DEV; ++ i) {
if (pci_devs[i].type == DEVICE_DISPLAY) return &pci_devs[i];
}
return NULL;
}