CeDOS - kernel/pci.c

kernel/pci.c (view raw)

#include <stdint.h>

#include "assembly.h"

#include "core.h"
#include "list.h"
#include "memory.h"

#define CONFIG_ADDR_ENABLE (31)
#define CONFIG_ADDR_BUS (16)
#define CONFIG_ADDR_PORT (11)
#define CONFIG_ADDR_FUNC (8)
#define CONFIG_ADDR_REG (0)

#define VENDOR_DEVICE_ID_REGISTER (0x00)

#define VENDOR_ID_BIT_MASK (0x0000FFFF)
#define VENDOR_ID_BIT_SHIFT (0)

#define DEVICE_ID_BIT_MASK (0xFFFF0000)
#define DEVICE_ID_BIT_SHIFT (16)

#define CLASS_CODE_REGISTER (0x08)

#define CLASS_CODE_BIT_MASK (0xFF000000)
#define CLASS_CODE_BIT_SHIFT (24)

#define SUBCLASS_CODE_BIT_MASK (0x00FF0000)
#define SUBCLASS_CODE_BIT_SHIFT (16)

#define HEADER_TYPE_REGISTER (0x0C)

#define HEADER_TYPE_BIT_MASK (0x00FF0000)
#define HEADER_TYPE_BIT_SHIFT (16)

struct pci_device {
	uint8_t bus;
	uint8_t port;

	uint16_t vendor_id;
	uint16_t device_id;

	uint8_t class_code;
	uint8_t subclass_code;

	uint8_t header_type;

	struct list_node node;
};

struct list pci_devices = LIST_INIT();

static int pci_read_register(struct pci_device *dev, uint8_t func, uint8_t reg, uint32_t *res) {
	uint32_t config_addr = 0;

	config_addr |= (1U << CONFIG_ADDR_ENABLE);
	config_addr |= (uint32_t)(dev->bus) << CONFIG_ADDR_BUS;
	config_addr |= (uint32_t)(dev->port) << CONFIG_ADDR_PORT;
	config_addr |= (uint32_t)(func) << CONFIG_ADDR_FUNC;
	config_addr |= (uint32_t)(reg) << CONFIG_ADDR_REG;

	outl(config_addr, 0xCF8);

	*res = inl(0xCFC);

	return 0;
}

int pci_init(void) {
	int num_devices = 0;

	for (int bus = 0; bus < 256; bus++) {
		for (int port = 0; port < 256; port++) {
			struct pci_device dev = {
				.bus = (uint8_t)(bus),
				.port = (uint8_t)(port),
			};

			uint32_t reg_value = 0;
			pci_read_register(&dev, 0, VENDOR_DEVICE_ID_REGISTER, &reg_value);

			uint16_t vendor_id =
				(reg_value & VENDOR_ID_BIT_MASK) >> VENDOR_ID_BIT_SHIFT;
			uint16_t device_id =
				(reg_value & DEVICE_ID_BIT_MASK) >> DEVICE_ID_BIT_SHIFT;

			if (vendor_id == 0xFFFF) {
				continue;
			}

			struct pci_device *list_entry = malloc(sizeof(struct pci_device));

			list_entry->bus = dev.bus;
			list_entry->port = dev.port;

			list_entry->vendor_id = vendor_id;
			list_entry->device_id = device_id;

			pci_read_register(&dev, 0, CLASS_CODE_REGISTER, &reg_value);

			list_entry->class_code =
				(reg_value & CLASS_CODE_BIT_MASK) >> CLASS_CODE_BIT_SHIFT;
			list_entry->subclass_code =
				(reg_value & SUBCLASS_CODE_BIT_MASK) >> SUBCLASS_CODE_BIT_SHIFT;

			pci_read_register(&dev, 0, HEADER_TYPE_REGISTER, &reg_value);

			list_entry->header_type =
				(reg_value & HEADER_TYPE_BIT_MASK) >> HEADER_TYPE_BIT_SHIFT;

			list_append(&pci_devices, &(list_entry->node));
			num_devices++;
		}
	}

	printk("\nfound %i PCI devices:\n", num_devices);

	struct pci_device *entry;
	LIST_FOR_EACH_ENTRY(&pci_devices, struct pci_device, node, entry) {
		printk(
				"- bus:         %x\n"
				"  port:        %x\n"
				"  vendor id:   %x\n"
				"  device id:   %x\n"
				"  class:       %x\n"
				"  subclass:    %x\n"
				"  header type: %x\n",
				entry->bus, entry->port,
				entry->vendor_id, entry->device_id,
				entry->class_code, entry->subclass_code,
				entry->header_type);
	}

	return 0;
}