Backport more old code

This commit is contained in:
Mark
2020-01-31 22:18:29 +02:00
parent 93c8fc0b77
commit 4e845d5d5e
12 changed files with 1844 additions and 1 deletions
+4 -1
View File
@@ -46,7 +46,9 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \
$(O)/sys/amd64/syscall_s.o \
$(O)/sys/amd64/syscall.o \
$(O)/sys/amd64/binfmt_elf.o \
$(O)/sys/amd64/hw/con.o
$(O)/sys/amd64/hw/con.o \
$(O)/sys/amd64/hw/pci/pci.o \
$(O)/sys/amd64/hw/pci/ahci.o
kernel_LINKER=sys/amd64/link.ld
kernel_LDFLAGS=-nostdlib \
@@ -88,6 +90,7 @@ kernel_OBJS=$(O)/sys/amd64/crti.o \
$(O)/sys/amd64/crtn.o
DIRS+=$(O)/sys/amd64/image/boot/grub \
$(O)/sys/amd64/hw/pci \
$(O)/sys/amd64/hw \
$(O)/sys/amd64/sys \
$(O)/sys/amd64/mm \
+32
View File
@@ -0,0 +1,32 @@
#pragma once
struct ahci_registers;
struct pci_device;
struct ahci_fis_reg_h2d {
uint8_t type;
uint8_t cmd_port;
uint8_t cmd;
uint8_t feature_low;
uint8_t lba0;
uint8_t lba1;
uint8_t lba2;
uint8_t device;
uint8_t lba3;
uint8_t lba4;
uint8_t lba5;
uint8_t feature_high;
uint16_t count;
uint8_t icc;
uint8_t control;
uint32_t __res0;
};
struct ahci_controller {
struct pci_device *pci_dev;
uintptr_t abar_phys;
struct ahci_registers *regs;
};
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_READ_DMA_EX 0x25
#define ATA_CMD_WRITE_DMA_EX 0x35
#define ATA_CMD_PACKET_IDENTIFY 0xA1
#define ATAPI_CMD_READ_SECTORS 0xA8
#define ATA_SR_BUSY (1 << 7)
#define ATA_SR_DRQ (1 << 3)
// Response to ATA_CMD_IDENTIFY
#define ATA_IDENT_DEVICE_TYPE 0x00
#define ATA_IDENT_SERIAL 0x14
#define ATA_IDENT_MODEL 0x36
#define ATA_IDENT_CAPS 0x62
#define ATA_IDENT_MAX_LBA 0x78
#define ATA_IDENT_CMD_SETS 0xA4
#define ATA_IDENT_MAX_LBAEXT 0xC8
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "sys/amd64/hw/irq.h"
#include "sys/types.h"
#define PCI_PORT_CONFIG_ADDR 0xCF8
#define PCI_PORT_CONFIG_DATA 0xCFC
#define PCI_CONFIG_ID 0x00
#define PCI_CONFIG_CMD 0x04
#define PCI_CONFIG_CLASS 0x08
#define PCI_CONFIG_INFO 0x0C
#define PCI_CONFIG_BAR(n) (0x10 + (n) * 4)
#define PCI_CONFIG_SUBSYSTEM 0x2C
#define PCI_CONFIG_CAPABILITIES 0x34
#define PCI_CONFIG_IRQ 0x3C
#define PCI_CONFIG_BRIDGE 0x18
#define PCI_ID(vnd, dev) (((uint32_t) (vnd)) | ((uint32_t) (dev) << 16))
struct pci_device;
typedef void (*pci_driver_func_t)(struct pci_device *dev);
void pci_init(void);
void pci_add_root_bus(uint8_t n);
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off);
void pci_add_irq(struct pci_device *dev, irq_handler_func_t handler, void *ctx);
void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func);
// pcidb.c
const char *pci_class_string(uint16_t full_class);
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include "sys/amd64/hw/pci/pci.h"
//void pci_usb_uhci_init(pci_addr_t addr);
+604
View File
@@ -0,0 +1,604 @@
#include "sys/amd64/hw/pci/pci.h"
#include "sys/amd64/hw/ata.h"
#include "sys/amd64/mm/phys.h"
#include "sys/amd64/hw/ahci.h"
#include "sys/dev/blk.h"
#include "sys/dev/dev.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/errno.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/attr.h"
#include "sys/mm.h"
#define AHCI_SPIN_WAIT_MAX 10000000
#define AHCI_PRD_MAX_SIZE 8192
#define AHCI_PORT_SIG_SATA 0x00000101
#define AHCI_PORT_SIG_SATAPI 0xEB140101
#define AHCI_MAX_PORTS 32
#define AHCI_CMD_LIST_SIZE 32
#define AHCI_CMD_TABLE_ENTSZ 256
#define AHCI_PORT_IS_TFES (1 << 30)
#define AHCI_PORT_SSTS_IPM_ACTIVE 0x01
#define AHCI_PORT_SSTS_DET_CONNECTED 0x03
#define AHCI_FIS_REG_H2D 0x27
// Tells the controller it's a command FIS
#define AHCI_FIS_REG_H2D_COMMAND (1 << 7)
#define AHCI_PORT_CMD_LIST(port) \
((struct ahci_cmd_list *) MM_VIRTUALIZE((port)->clb | (((uintptr_t) (port)->clbu) << 32)))
#define AHCI_PORT_TABLE_ENTRY(list, n) \
((struct ahci_cmd_table *) MM_VIRTUALIZE((list)[n].ctba))
// Start
#define AHCI_PORT_CMD_ST (1 << 0)
// FIS receive enable
#define AHCI_PORT_CMD_FRE (1 << 4)
// FIS receive running
#define AHCI_PORT_CMD_FR (1 << 14)
// Command list running
#define AHCI_PORT_CMD_CR (1 << 15)
struct ahci_port {
uint32_t clb; // 0x00
uint32_t clbu; // 0x04
uint32_t fb; // 0x08
uint32_t fbu; // 0x0C
uint32_t is; // 0x10
uint32_t ie; // 0x14
uint32_t cmd; // 0x18
uint32_t __res0; // 0x1C
uint32_t tfd; // 0x20
uint32_t sig; // 0x24
uint32_t ssts; // 0x28
uint32_t sctl; // 0x2C
uint32_t serr; // 0x30
uint32_t sact; // 0x34
uint32_t ci; // 0x38
uint32_t __res1[13];// 0x3C
uint32_t __vnd[4];
};
struct ahci_registers {
// 0x00 - 0x1F - Generic Host Control
uint32_t cap; // 0x00
uint32_t ghc; // 0x04
uint32_t is; // 0x08
uint32_t pi; // 0x0C
uint32_t vs; // 0x10
uint32_t __unspec[3];
// 0x20 - 0x9F - Reserved
uint32_t __res[32];
// 0xA0 - 0xFF - Vendor specific registers
uint32_t __vnd[24];
struct ahci_port port[32];
};
// Command list entry
struct ahci_cmd_list {
uint16_t attr;
// PRD table length in entries
uint16_t prdtl;
// Byte count transferred
volatile uint32_t prdbc;
// Command table base address
uint64_t ctba;
uint32_t __res0[4];
};
// Physical region descriptor entry
struct ahci_prd {
// Buffer address
uint64_t dba;
uint32_t __res0;
uint32_t dbc;
};
// Command table entry
struct ahci_cmd_table {
// Buffer for FIS to be sent to the device
union {
struct ahci_fis_reg_h2d fis_reg_h2d;
uint8_t __cmd_fis[64];
};
uint8_t acmd[16];
uint8_t __res0[48];
// Any number of entries can follow
struct ahci_prd prdt[0];
};
//// Generic AHCI
static void ahci_port_stop(struct ahci_port *port) {
// Disable command engine and FIS receive
port->cmd &= ~(AHCI_PORT_CMD_ST | AHCI_PORT_CMD_FRE);
// Wait for controller to change status
while (port->cmd & (AHCI_PORT_CMD_CR | AHCI_PORT_CMD_FR)) {
asm ("pause");
}
}
static void ahci_port_start(struct ahci_port *port) {
// Wait while port may be processing any commands
while (port->cmd & AHCI_PORT_CMD_CR) {
asm ("pause");
}
// Enable command engine and FIS receive
port->cmd |= AHCI_PORT_CMD_FRE | AHCI_PORT_CMD_ST;
}
static int ahci_port_cmd_alloc(struct ahci_port *port) {
uint32_t slots = (port->sact | port->ci);
for (int i = 0; i < AHCI_CMD_LIST_SIZE; ++i) {
if (!(slots & (1 << i))) {
return i;
}
}
return -1;
}
static int ahci_port_alloc(struct ahci_port *port) {
// Disable port command engine first
ahci_port_stop(port);
// AHCI port memory:
// * 1024 for command list
// * 256 - FIS receive buffer
// * 8192 - command table
// Total:
// 12288 bytes (page-aligned), 3 pages
// Command list and FIS buffer
uintptr_t page0 = amd64_phys_alloc_page();
if (page0 == MM_NADDR) {
kerror("Failed to allocate a page\n");
return -ENOMEM;
}
// Command table
uintptr_t page1 = amd64_phys_alloc_contiguous(2);
if (page1 == MM_NADDR) {
kerror("Failed to allocate 2 pages\n");
amd64_phys_free(page0);
return -ENOMEM;
}
// Cleanup the pages
memset((void *) MM_VIRTUALIZE(page0), 0, 0x1000);
memset((void *) MM_VIRTUALIZE(page1), 0, 0x2000);
// Setup an empty command list with proper addresses
struct ahci_cmd_list *cmd_list = (struct ahci_cmd_list *) MM_VIRTUALIZE(page0);
for (size_t i = 0; i < AHCI_CMD_LIST_SIZE; ++i) {
cmd_list[i].prdtl = 8;
cmd_list[i].ctba = page1 + i * AHCI_CMD_TABLE_ENTSZ;
}
// Configure the port to use allocated pages
port->clb = page0 & 0xFFFFFFFF;
port->clbu = page0 >> 32;
port->fb = (page0 + 0x400) & 0xFFFFFFFF;
port->fbu = (page0 + 0x400) >> 32;
// Enable it again
ahci_port_start(port);
return 0;
}
static int ahci_port_ata_cmd(struct ahci_port *port, uint8_t ata, uintptr_t lba, void *data, size_t len) {
// At least should be sector-aligned
_assert(len % 512 == 0);
int cmd = ahci_port_cmd_alloc(port);
if (cmd < 0) {
return -EBUSY;
}
struct ahci_cmd_list *list = AHCI_PORT_CMD_LIST(port);
struct ahci_cmd_list *list_entry = &list[cmd];
struct ahci_cmd_table *table_entry = AHCI_PORT_TABLE_ENTRY(list, cmd);
uint32_t spin = 0;
// TODO: support devices with different sector sizes
size_t nsect = (len + 511) / 512;
_assert((nsect & ~0xFFFF) == 0);
size_t prd_count = (len + AHCI_PRD_MAX_SIZE - 1) / AHCI_PRD_MAX_SIZE;
kdebug("Command requires %d PRDs\n", prd_count);
// Setup command table entry
memset(table_entry, 0, sizeof(struct ahci_cmd_table) + sizeof(struct ahci_prd) * prd_count);
for (size_t i = 0, bytes_left = len; i < prd_count; ++i) {
size_t prd_size = MIN(AHCI_PRD_MAX_SIZE, len);
_assert(prd_size);
uintptr_t prd_buf_phys = MM_PHYS(data) + i * AHCI_PRD_MAX_SIZE;
table_entry->prdt[i].dba = prd_buf_phys;
table_entry->prdt[i].dbc = ((prd_size - 1) << 1) | 1;
kdebug("PRD%d: %S at %p\n", i, prd_size, prd_buf_phys);
if (i == prd_count - 1) {
// Mark last PRDT entry
table_entry->prdt[i].dbc |= 1 << 31;
}
bytes_left -= prd_size;
}
// Setup command list entry
// attr = FIS size in dwords
list_entry->attr = sizeof(struct ahci_fis_reg_h2d) / sizeof(uint32_t);
list_entry->prdtl = prd_count;
list_entry->prdbc = 0;
// Setup FIS packet
struct ahci_fis_reg_h2d *fis = &table_entry->fis_reg_h2d;
memset(fis, 0, sizeof(struct ahci_fis_reg_h2d));
fis->type = AHCI_FIS_REG_H2D;
fis->cmd = ata;
fis->cmd_port = AHCI_FIS_REG_H2D_COMMAND;
if (ata != ATA_CMD_IDENTIFY) {
fis->device = 1 << 6; // LBA mode
fis->lba0 = lba & 0xFF;
fis->lba1 = (lba >> 8) & 0xFF;
fis->lba2 = (lba >> 16) & 0xFF;
fis->lba3 = (lba >> 24) & 0xFF;
fis->lba4 = (lba >> 32) & 0xFF;
fis->lba5 = (lba >> 40) & 0xFF;
kdebug("nsect = %d\n", nsect);
fis->count = nsect & 0xFFFF;
}
// Wait for port to be free for commands
while ((port->tfd & (ATA_SR_BUSY | ATA_SR_DRQ)) && spin < AHCI_SPIN_WAIT_MAX) {
++spin;
}
if (spin == AHCI_SPIN_WAIT_MAX) {
kerror("AHCI device hang\n");
return -EIO;
}
// Request HBA to execute the command
port->ci |= 1 << cmd;
while (1) {
if (!(port->ci & (1 << cmd))) {
break;
}
if (port->is & AHCI_PORT_IS_TFES) {
kerror("Device signalled TFE error\n");
return -EIO;
}
}
if (port->is & AHCI_PORT_IS_TFES) {
kerror("Device signalled TFE error\n");
return -EIO;
}
return 0;
}
static int ahci_port_atapi_read(struct ahci_port *port, uint64_t lba, void *buf, size_t nsect) {
int cmd = ahci_port_cmd_alloc(port);
if (cmd < 0) {
return -EBUSY;
}
struct ahci_cmd_list *list = AHCI_PORT_CMD_LIST(port);
struct ahci_cmd_list *list_entry = &list[cmd];
struct ahci_cmd_table *table_entry = AHCI_PORT_TABLE_ENTRY(list, cmd);
uint32_t spin = 0;
// TODO: support devices with different sector sizes
size_t len = nsect * 2048;
_assert((nsect & ~0xFFFF) == 0);
size_t prd_count = (len + AHCI_PRD_MAX_SIZE - 1) / AHCI_PRD_MAX_SIZE;
kdebug("Command requires %d PRDs\n", prd_count);
// Setup command table entry
memset(table_entry, 0, sizeof(struct ahci_cmd_table) + sizeof(struct ahci_prd) * prd_count);
table_entry->acmd[0] = 0xA8;
table_entry->acmd[9] = nsect;
table_entry->acmd[5] = lba & 0xFF;
table_entry->acmd[4] = (lba >> 8) & 0xFF;
table_entry->acmd[3] = (lba >> 16) & 0xFF;
table_entry->acmd[2] = (lba >> 24) & 0xFF;
for (size_t i = 0, bytes_left = len; i < prd_count; ++i) {
size_t prd_size = MIN(AHCI_PRD_MAX_SIZE, len);
_assert(prd_size);
uintptr_t prd_buf_phys = MM_PHYS(buf) + i * AHCI_PRD_MAX_SIZE;
table_entry->prdt[i].dba = prd_buf_phys;
table_entry->prdt[i].dbc = ((prd_size - 1) << 1) | 1;
kdebug("PRD%d: %S at %p\n", i, prd_size, prd_buf_phys);
if (i == prd_count - 1) {
// Mark last PRDT entry
table_entry->prdt[i].dbc |= 1 << 31;
}
bytes_left -= prd_size;
}
// Setup command list entry
// attr = FIS size in dwords
list_entry->attr = sizeof(struct ahci_fis_reg_h2d) / sizeof(uint32_t);
// This is ATAPI command
list_entry->attr |= (1 << 5);
list_entry->prdtl = 1;
list_entry->prdbc = 0;
// Setup FIS packet
struct ahci_fis_reg_h2d *fis = &table_entry->fis_reg_h2d;
memset(fis, 0, sizeof(struct ahci_fis_reg_h2d));
fis->type = AHCI_FIS_REG_H2D;
fis->cmd = ATA_CMD_PACKET;
fis->cmd_port = AHCI_FIS_REG_H2D_COMMAND;
fis->device = 1 << 6; // LBA mode
fis->feature_low = 1;
fis->lba0 = lba & 0xFF;
fis->lba1 = (lba >> 8) & 0xFF;
fis->lba2 = (lba >> 16) & 0xFF;
fis->lba3 = (lba >> 24) & 0xFF;
fis->lba4 = (lba >> 32) & 0xFF;
fis->lba5 = (lba >> 40) & 0xFF;
fis->count = nsect & 0xFFFF;
// Wait for port to be free for commands
while ((port->tfd & (ATA_SR_BUSY | ATA_SR_DRQ)) && spin < AHCI_SPIN_WAIT_MAX) {
++spin;
}
if (spin == AHCI_SPIN_WAIT_MAX) {
kerror("AHCI device hang\n");
return -EIO;
}
// Request HBA to execute the command
port->ci |= 1 << cmd;
while (1) {
if (!(port->ci & (1 << cmd))) {
break;
}
if (port->is & AHCI_PORT_IS_TFES) {
kerror("Device signalled TFE error\n");
return -EIO;
}
}
if (port->is & AHCI_PORT_IS_TFES) {
kerror("Device signalled TFE error\n");
return -EIO;
}
return 0;
}
static int ahci_port_identify(struct ahci_port *port) {
char ident_buf[1024];
size_t disk_size_lba;
char model_string[41];
char serial_string[11];
uint32_t cmd_sets;
int res, id_cmd;
if (port->sig == AHCI_PORT_SIG_SATA) {
id_cmd = ATA_CMD_IDENTIFY;
} else {
id_cmd = ATA_CMD_PACKET_IDENTIFY;
}
if ((res = ahci_port_ata_cmd(port, id_cmd, 0, ident_buf, sizeof(ident_buf))) != 0) {
kwarn("Disk identify failed: %s\n", kstrerror(res));
return res;
}
model_string[40] = 0;
serial_string[10] = 0;
cmd_sets = *(uint32_t *) (ident_buf + ATA_IDENT_CMD_SETS);
for (size_t i = 0; i < 40; i += 2) {
model_string[i] = ident_buf[ATA_IDENT_MODEL + i + 1];
model_string[i + 1] = ident_buf[ATA_IDENT_MODEL + i];
}
for (size_t i = 39; i > 0; --i) {
if (model_string[i] != ' ') {
break;
}
model_string[i] = 0;
}
for (size_t i = 0; i < 10; i += 2) {
serial_string[i] = ident_buf[ATA_IDENT_SERIAL + i + 1];
serial_string[i + 1] = ident_buf[ATA_IDENT_SERIAL + i];
}
for (size_t i = 9; i > 0; --i) {
if (serial_string[i] != ' ') {
break;
}
serial_string[i] = 0;
}
if (cmd_sets & (1 << 26)) {
disk_size_lba = *(uint32_t *) (ident_buf + ATA_IDENT_MAX_LBAEXT);
} else {
disk_size_lba = *(uint32_t *) (ident_buf + ATA_IDENT_MAX_LBA);
}
kdebug("%s drive: \"%s\", Serial: \"%s\", Capacity: %S\n",
id_cmd == ATA_CMD_IDENTIFY ? "SATA" : "SATAPI",
model_string, serial_string, disk_size_lba * 512);
return 0;
}
static ssize_t ahci_blk_write(struct blkdev *blk, const void *buf, size_t off, size_t len) {
if ((off % blk->block_size) != 0) {
panic("Misaligned AHCI device write: offset of %lu, block size is %lu\n", off, blk->block_size);
}
if ((len % blk->block_size) != 0) {
panic("Misaligned AHCI device write: size of %lu\n", len);
}
kdebug("AHCI write %S at %p\n", len, off);
struct ahci_port *port = blk->dev_data;
_assert(port);
uintptr_t lba = (off / blk->block_size);
size_t n_blocks = len / blk->block_size;
int res;
switch (port->sig) {
case AHCI_PORT_SIG_SATA:
if ((res = ahci_port_ata_cmd(port, ATA_CMD_WRITE_DMA_EX, lba, (void *) buf, len)) != 0) {
return res;
}
return len;
case AHCI_PORT_SIG_SATAPI:
return -EROFS;
default:
return -EIO;
}
}
static ssize_t ahci_blk_read(struct blkdev *blk, void *buf, size_t off, size_t len) {
if ((off % blk->block_size) != 0) {
panic("Misaligned AHCI device read: offset of %lu, block size is %lu\n", off, blk->block_size);
}
if ((len % blk->block_size) != 0) {
panic("Misaligned AHCI device read: size of %lu\n", len);
}
kdebug("AHCI read %S at %p\n", len, off);
struct ahci_port *port = blk->dev_data;
_assert(port);
uintptr_t lba = (off / blk->block_size);
size_t n_blocks = len / blk->block_size;
int res;
switch (port->sig) {
case AHCI_PORT_SIG_SATA:
if ((res = ahci_port_ata_cmd(port, ATA_CMD_READ_DMA_EX, lba, buf, len)) != 0) {
return res;
}
return len;
case AHCI_PORT_SIG_SATAPI:
if ((res = ahci_port_atapi_read(port, lba, buf, n_blocks)) != 0) {
return res;
}
return len;
default:
return -EIO;
}
}
static void ahci_port_add(struct ahci_port *port) {
struct blkdev *blk = kmalloc(sizeof(struct blkdev));
_assert(blk);
uint32_t subclass;
switch (port->sig) {
case AHCI_PORT_SIG_SATA:
blk->block_size = 512;
subclass = DEV_BLOCK_SDx;
break;
case AHCI_PORT_SIG_SATAPI:
blk->block_size = 2048;
subclass = DEV_BLOCK_CDx;
break;
default:
panic("Unhandled AHCI device signature: %08x\n", port->sig);
}
blk->dev_data = port;
blk->read = ahci_blk_read;
blk->write = ahci_blk_write;
blk->flags = 0;
_assert(dev_add(DEV_CLASS_BLOCK, subclass, blk, NULL) == 0);
}
static void ahci_port_init(struct ahci_controller *ahci, struct ahci_port *port, int no) {
kinfo("Initializing port %d\n", no);
// Allocate all the data structures required for port control
if (ahci_port_alloc(port) != 0) {
kwarn("Failed to allocate port data %d\n", no);
return;
}
if (ahci_port_identify(port) != 0) {
kwarn("Failed to identify device at port %d\n", no);
return;
}
// Register new block device
ahci_port_add(port);
}
static void ahci_controller_init(struct ahci_controller *ahci) {
// Check controller version
kinfo("AHCI controller version is %02x.%02x\n", (ahci->regs->vs >> 16), (ahci->regs->vs & 0xFFFF));
// Check which ports are supported by AHCI controller
for (size_t i = 0; i < AHCI_MAX_PORTS; ++i) {
if (ahci->regs->pi & (1 << i)) {
// Get SATA connnection status
struct ahci_port *port = &ahci->regs->port[i];
uint32_t ssts = port->ssts;
uint8_t ipm = (ssts >> 8) & 0x0F;
uint8_t spd = (ssts >> 4) & 0x0F;
uint8_t det = ssts & 0x0F;
if ((ipm != AHCI_PORT_SSTS_IPM_ACTIVE) || (det != AHCI_PORT_SSTS_DET_CONNECTED)) {
// Skip not connected or inactive ports
continue;
}
if (port->sig == AHCI_PORT_SIG_SATA || port->sig == AHCI_PORT_SIG_SATAPI) {
ahci_port_init(ahci, port, i);
}
}
}
}
//// PCI-specific
static void pci_ahci_init(struct pci_device *pci_dev) {
// TODO: change pcie -> pci
uint32_t abar_phys = pci_config_read_dword(pci_dev, PCI_CONFIG_BAR(5));
if (abar_phys & 1) {
kwarn("AHCI controller has ABAR in I/O space\n");
return;
}
struct ahci_controller *obj = kmalloc(sizeof(struct ahci_controller));
obj->pci_dev = pci_dev;
obj->abar_phys = abar_phys;
obj->regs = (void *) MM_VIRTUALIZE(abar_phys);
ahci_controller_init(obj);
}
static __init void ahci_register_class(void) {
pci_add_class_driver(0x010601, pci_ahci_init);
}
+275
View File
@@ -0,0 +1,275 @@
#include "sys/amd64/hw/pci/pci.h"
#include "sys/amd64/hw/ioapic.h"
#include "sys/amd64/hw/acpi.h"
#include "sys/amd64/hw/io.h"
#include "sys/assert.h"
#include "sys/panic.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/mm.h"
#define PCI_MAX_DRIVERS 64
#define pcie_config_read_dword(dev, off) \
(*(uint32_t *) ((dev)->pcie_config + (off)))
#define PCI_CAP_MSI_64 (1 << 7)
#define PCI_CAP_MSI_EN (1 << 0)
struct pci_cap_msi {
uint8_t cap_id;
uint8_t cap_link;
uint16_t message_control;
union {
struct {
uint32_t message_address;
uint16_t message_data;
} __attribute__((packed)) msi32;
struct {
uint64_t message_address;
uint16_t message_data;
} __attribute__((packed)) msi64;
};
} __attribute__((packed));
struct pci_device {
// PCI address
uint8_t bus;
uint8_t dev;
uint8_t func;
// PCIe addressing: segment group number and configuration space pointer
uint16_t pcie_segment_group;
void *pcie_config;
// Interrupt resources
struct pci_cap_msi *msi;
int irq_pin;
// For list
struct pci_device *next;
};
struct pci_driver {
pci_driver_func_t init_func;
uint32_t type;
uint32_t match;
};
static struct pci_device *g_pci_devices = NULL;
static struct pci_driver g_pci_drivers[PCI_MAX_DRIVERS] = {0};
static size_t g_pci_driver_count;
#define PCI_DRIVER_CLASS 1
#define PCI_DRIVER_DEV 2
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off) {
// TODO: check if device is really PCIe
return pcie_config_read_dword(dev, off);
}
void pci_add_irq(struct pci_device *dev, irq_handler_func_t handler, void *ctx) {
if (dev->msi) {
uint8_t vector;
if (irq_add_msi_handler(handler, ctx, &vector) != 0) {
panic("Failed to add MSI handler\n");
}
if (dev->msi->message_control & PCI_CAP_MSI_64) {
dev->msi->msi64.message_address = 0xFEE00000;
dev->msi->msi64.message_data = vector;
} else {
dev->msi->msi32.message_address = 0xFEE00000;
dev->msi->msi32.message_data = vector;
}
dev->msi->message_control |= PCI_CAP_MSI_EN;
}
}
void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func) {
if (g_pci_driver_count == PCI_MAX_DRIVERS) {
panic("Too many PCI drivers loaded\n");
}
struct pci_driver *driver = &g_pci_drivers[g_pci_driver_count++];
driver->init_func = func;
driver->type = PCI_DRIVER_CLASS;
driver->match = full_class & ~0xFF000000;
}
void pci_add_root_bus(uint8_t n) {
kwarn("%s: %02x\n", __func__, n);
}
static void pci_pick_driver(uint32_t device_id, uint32_t class_id, pci_driver_func_t *class_driver, pci_driver_func_t *dev_driver) {
pci_driver_func_t rclass = NULL, rdev = NULL;
for (size_t i = 0; i < g_pci_driver_count; ++i) {
if (rclass && rdev) {
break;
}
if (g_pci_drivers[i].type == PCI_DRIVER_CLASS && !rclass) {
uint32_t match = g_pci_drivers[i].match;
// Check if prog_if has to be matched
// 0xFF means (match all prog. IF)
if ((match & 0xFF) == 0xFF) {
match &= ~0xFF;
class_id &= ~0xFF;
}
if (match == class_id) {
rclass = g_pci_drivers[i].init_func;
continue;
}
} else if (g_pci_drivers[i].type == PCI_DRIVER_DEV && !rdev) {
if (device_id == g_pci_drivers[i].match) {
rdev = g_pci_drivers[i].init_func;
continue;
}
}
}
*class_driver = rclass;
*dev_driver = rdev;
}
static void pci_device_add(struct pci_device *dev) {
dev->next = g_pci_devices;
g_pci_devices = dev;
}
static int pcie_device_setup(struct pci_device *dev) {
uint32_t class, caps_offset, irq_info;
uint32_t id;
uint8_t irq_pin;
class = pcie_config_read_dword(dev, PCI_CONFIG_CLASS);
caps_offset = pcie_config_read_dword(dev, PCI_CONFIG_CAPABILITIES) & 0xFF;
irq_info = pcie_config_read_dword(dev, PCI_CONFIG_IRQ);
id = pcie_config_read_dword(dev, PCI_CONFIG_ID);
kinfo("%02x:%02x:%02x:\n", dev->bus, dev->dev, dev->func);
kinfo(" Class %02x:%02x:%02x\n", (class >> 24), (class >> 16) & 0xFF, (class >> 8) & 0xFF);
kinfo(" Device %04x:%04x\n", id & 0xFFFF, (id >> 16) & 0xFFFF);
irq_pin = (irq_info >> 8) & 0xFF;
if (irq_pin) {
dev->irq_pin = irq_pin - 1;
kinfo(" IRQ pin INT%c#\n", dev->irq_pin + 'A');
}
while (caps_offset) {
uint8_t *link = (uint8_t *) (dev->pcie_config + caps_offset);
switch (link[0]) {
case 0x05:
kinfo(" * MSI capability\n");
dev->msi = (struct pci_cap_msi *) link;
break;
case 0x10:
kinfo(" * PCIe capability\n");
break;
default:
// Unknown capability
kinfo(" * Device capability: %02x\n", link[0]);
break;
}
caps_offset = link[1];
}
pci_driver_func_t driver_class, driver_dev;
pci_pick_driver(id, class >> 8, &driver_class, &driver_dev);
if (driver_dev) {
driver_dev(dev);
return 0;
}
if (driver_class) {
driver_class(dev);
return 0;
}
return -1;
}
static void pcie_enumerate_device(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus, uint8_t dev_no) {
void *cfg;
uint32_t id;
for (uint8_t func = 0; func < 8; ++func) {
uint32_t d = ((uint32_t) (bus - start_bus) << 20) | ((uint32_t) dev_no << 15) | ((uint32_t) func << 12);
cfg = (void *) MM_VIRTUALIZE(base_address + d);
id = *(uint32_t *) cfg;
if ((id & 0xFFFF) == 0xFFFF) {
continue;
}
struct pci_device *dev = kmalloc(sizeof(struct pci_device));
_assert(dev);
dev->bus = bus;
dev->dev = dev_no;
dev->func = func;
dev->pcie_segment_group = seg;
dev->pcie_config = cfg;
dev->msi = NULL;
dev->irq_pin = -1;
pcie_device_setup(dev);
pci_device_add(dev);
}
}
static void pcie_enumerate_bus(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus) {
for (uint8_t dev = 0; dev < 32; ++dev) {
// Check if function 0 is present
void *cfg = (void *) MM_VIRTUALIZE(base_address + (((bus - start_bus) << 20) | (dev << 15)));
if (((*(uint32_t *) cfg) & 0xFFFF) != 0xFFFF) {
uint32_t header = *(uint32_t *) (cfg + 0x0C);
header >>= 16;
header &= 0x7F;
switch (header) {
case 0x00:
pcie_enumerate_device(base_address, seg, start_bus, bus, dev);
break;
default:
kwarn("Skipping unsupported header type: %02x\n", header);
break;
}
}
}
}
static void pcie_enumerate_segment(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t end_bus) {
for (uint16_t bus = start_bus; bus < end_bus; ++bus) {
pcie_enumerate_bus(base_address, seg, start_bus, bus);
}
}
void pci_init(void) {
if (!acpi_mcfg) {
panic("TODO: legacy PCI\n");
}
uint32_t mcfg_entry_count = (acpi_mcfg->hdr.length - sizeof(struct acpi_header) - 8) / sizeof(struct acpi_mcfg_entry);
kinfo("MCFG has %u entries:\n", mcfg_entry_count);
for (uint32_t i = 0; i < mcfg_entry_count; ++i) {
kinfo("%u:\n", i);
kinfo(" Base address: %p\n", acpi_mcfg->entry[i].base_address);
kinfo(" Segment group #%u\n", acpi_mcfg->entry[i].pci_segment_group);
kinfo(" PCI buses: %02x-%02x\n", acpi_mcfg->entry[i].start_pci_bus, acpi_mcfg->entry[i].end_pci_bus);
}
// Start enumerating buses specified in MCFG
for (uint32_t i = 0; i < mcfg_entry_count; ++i) {
pcie_enumerate_segment(acpi_mcfg->entry[i].base_address,
acpi_mcfg->entry[i].pci_segment_group,
acpi_mcfg->entry[i].start_pci_bus,
acpi_mcfg->entry[i].end_pci_bus);
}
}
+65
View File
@@ -0,0 +1,65 @@
#include "sys/amd64/hw/pci/pci.h"
static const char *pci_class_code_strings[] = {
"Unclassified",
"Mass storage controller",
"Network controller",
"Display controller",
"Multimedia controller",
"Memory controller",
"Bridge device",
"Simple communication controller",
"Base system peripheral",
"Input device controller",
"Docking station",
"Processor",
"Serial bus controller",
"Wireless controller",
"Intelligent controller",
"Satellite communication controller",
"Encryption controller",
"Signal processing controller",
"Processing accelerator",
"Non-essential instrumentation"
};
const char *pci_class_string(uint16_t full_class) {
// Better descriptions for well-known classes
switch (full_class) {
// Mass storage
case 0x0101:
return "IDE controller";
case 0x0106:
return "SATA controller";
// Display controllers
case 0x0300:
return "VGA display controller";
// Network controllers
case 0x200:
return "Ethernet controller";
// Bridge devices
case 0x0600:
return "Host bridge";
case 0x0601:
return "ISA bridge";
// Serial bus controllers
case 0x0C03:
return "USB controller";
case 0x0C05:
return "SMBus";
default:
break;
}
uint8_t class_code = full_class >> 8;
if (class_code == 0xFF) {
return "Unknown device";
}
if (class_code >= sizeof(pci_class_code_strings) / sizeof(pci_class_code_strings[0])) {
return "Reserved";
}
return pci_class_code_strings[class_code];
}
+277
View File
@@ -0,0 +1,277 @@
#include "sys/amd64/hw/pci/pci.h"
#include "sys/amd64/mm/phys.h"
#include "sys/amd64/hw/irq.h"
#include "sys/amd64/hw/io.h"
#include "sys/net/eth.h"
#include "sys/net/netdev.h"
#include "sys/net/net.h"
#include "sys/assert.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/attr.h"
#include "sys/mm.h"
#define RTL8139_IDR0 0x00
#define RTL8139_MAR0 0x08
#define RTL8139_TSD(n) (0x10 + (n) * 4)
#define RTL8139_TSAD(n) (0x20 + (n) * 4)
#define RTL8139_RBSTART 0x30
#define RTL8139_ERBCR 0x34
#define RTL8139_ERSR 0x36
#define RTL8139_CR 0x37
#define RTL8139_CAPR 0x38
#define RTL8139_CBR 0x3A
#define RTL8139_IMR 0x3C
#define RTL8139_ISR 0x3E
#define RTL8139_TCR 0x40
#define RTL8139_RCR 0x44
#define RTL8139_TCTR 0x48
#define RTL8139_MPC 0x4C
#define RTL8139_CONFIG0 0x51
#define RTL8139_CONFIG1 0x52
#define RTL8139_MSR 0x58
#define RTL8139_TSD_OWN (1 << 13)
#define RTL8139_CR_RST (1 << 4)
#define RTL8139_CR_RE (1 << 3)
#define RTL8139_CR_TE (1 << 2)
#define RTL8139_RCR_WRAP (1 << 7)
#define RTL8139_RCR_AB (1 << 0)
#define RTL8139_RCR_AM (1 << 1)
#define RTL8139_RCR_APM (1 << 2)
#define RTL8139_RCR_AAP (1 << 3)
#define RTL8139_RCR_ALL (RTL8139_RCR_AB | RTL8139_RCR_AM | RTL8139_RCR_APM | RTL8139_RCR_AAP)
#define RTL8139_ISR_TER (1 << 3)
#define RTL8139_ISR_TOK (1 << 2)
#define RTL8139_ISR_RER (1 << 1)
#define RTL8139_ISR_ROK (1 << 0)
#define RTL8139_IMR_TER (1 << 3)
#define RTL8139_IMR_TOK (1 << 2)
#define RTL8139_IMR_RER (1 << 1)
#define RTL8139_IMR_ROK (1 << 0)
struct rtl8139 {
struct netdev net;
uint32_t bar0;
// Receive buffer: 3 pages
uint32_t page0;
uint32_t tx_pages[4];
size_t tx_sizes[4];
// Bits:
// 0..3: TX_BUSY Set by _tx(), cleared by _handle_tx(), means that
// the descriptor cannot be allocated
uint32_t tx_flag;
uint32_t cbr_prev;
uint32_t tx_number;
};
static void rtl8139_handle_rx(struct rtl8139 *rtl);
static void rtl8139_handle_tx(struct rtl8139 *rtl, int succ);
static uint32_t rtl8139_irq(void *ctx) {
struct rtl8139 *rtl = ctx;
uint16_t isr = inw(rtl->bar0 + RTL8139_ISR);
if (isr) {
if (isr & RTL8139_ISR_ROK) {
// Got a packet
rtl8139_handle_rx(rtl);
}
if (isr & (RTL8139_ISR_TOK | RTL8139_ISR_TER)) {
rtl8139_handle_tx(rtl, isr & RTL8139_ISR_TOK);
}
// Implementation of RTL8139 in qemu differs
// from the datasheet specification:
// we need to WRITE to the register to clear the interrupts
outw(rtl->bar0 + RTL8139_ISR, isr);
return IRQ_HANDLED;
}
return IRQ_UNHANDLED;
}
static void rtl8139_handle_rx(struct rtl8139 *rtl) {
kdebug("Received a packet\n");
uint16_t cbr = inw(rtl->bar0 + RTL8139_CBR);
size_t packet_size;
uint16_t packet_offset = rtl->cbr_prev;
if (cbr > rtl->cbr_prev) {
packet_size = cbr - rtl->cbr_prev;
} else {
panic("TODO\n");
}
rtl->cbr_prev = cbr;
kdebug("Packet size: %u\n", packet_size);
char *pack = (char *) MM_VIRTUALIZE(rtl->page0) + packet_offset;
struct eth_frame *eth_frame = (struct eth_frame *) &pack[4];
// First 4 octets - recv status and packet length
uint16_t recv_status = ((uint16_t *) pack)[0];
uint16_t recv_length = ((uint16_t *) pack)[1];
kdebug("Recv status: %u\n", recv_status);
//kdebug("Recv length = %u\n", recv_length);
//kdebug("Packet size = %u\n", packet_size);
//_assert(recv_length == (packet_size - 4));
eth_handle_frame(&rtl->net, eth_frame, recv_length);
}
static void rtl8139_cmd_tx(struct rtl8139 *rtl) {
kdebug("HW TX %d\n", rtl->tx_number);
uint8_t n = rtl->tx_number;
++rtl->tx_number;
if (rtl->tx_number == 4) {
rtl->tx_number = 0;
}
outl(rtl->bar0 + RTL8139_TSD(n), rtl->tx_sizes[n]);
}
static void rtl8139_handle_tx(struct rtl8139 *rtl, int s) {
uint8_t prev = 3;
if (rtl->tx_number != 0) {
prev = rtl->tx_number - 1;
}
kdebug("%s Tx on %d\n", s ? "Successful" : "Failed", prev);
kdebug("Clearing TxD%d\n", prev);
rtl->tx_flag &= ~(1 << prev);
rtl->tx_sizes[prev] = 0;
kdebug("TX Done!\n");
// If after transmission we reached a new entry and it's
// awaiting transmission - emit a Tx command
while (rtl->tx_flag & (1 << rtl->tx_number)) {
rtl8139_cmd_tx(rtl);
}
}
static uint8_t rtl8139_alloc_tx(struct rtl8139 *rtl) {
for (uint8_t i = rtl->tx_number; i < 4; ++i) {
if (!(rtl->tx_flag & (1 << i))) {
return i;
}
}
for (uint8_t i = 0; i < rtl->tx_number; ++i) {
if (!(rtl->tx_flag & (1 << i))) {
return i;
}
}
return 0xFF;
}
static int rtl8139_tx(struct netdev *net, const void *packet, size_t size) {
kdebug("Requested Tx\n");
struct rtl8139 *rtl = (struct rtl8139 *) net;
uint8_t index = rtl8139_alloc_tx(rtl);
// TODO: transmit queue for this?
assert(index != 0xFF, "No free TSADx\n");
void *page = (void *) MM_VIRTUALIZE(rtl->tx_pages[index]);
memcpy(page, packet, size);
rtl->tx_sizes[index] = size;
if (index == rtl->tx_number) {
// Can tx right now
rtl8139_cmd_tx(rtl);
}
return 0;
}
void pci_rtl8139_init(pci_addr_t addr) {
kdebug("Initializing RTL8139 at " PCI_FMTADDR "\n", PCI_VAADDR(addr));
struct rtl8139 *rtl = (struct rtl8139 *) kmalloc(sizeof(struct rtl8139));
uint32_t irq_config = pci_config_read_dword(addr, PCI_CONFIG_IRQ);
// Enable PCI busmastering
uint32_t cmd = pci_config_read_dword(addr, PCI_CONFIG_CMD);
cmd |= 1 << 2;
pci_config_write_dword(addr, PCI_CONFIG_CMD, cmd);
rtl->bar0 = pci_config_read_dword(addr, PCI_CONFIG_BAR(0));
assert(rtl->bar0 & 1, "RTL8139 BAR0 is not in I/O space\n");
rtl->bar0 &= ~0x3;
// Board init
// Set LWAKE + LWPTN to active high
outb(rtl->bar0 + RTL8139_CONFIG1, 0);
// Software reset
outb(rtl->bar0 + RTL8139_CR, RTL8139_CR_RST);
// Wait for the board to clear RST bit
while (inb(rtl->bar0 + RTL8139_CR) & RTL8139_CR_RST);
// Initialize rx buffer
rtl->page0 = amd64_phys_alloc_contiguous(3);
rtl->cbr_prev = 0;
assert(rtl->page0 != MM_NADDR, "Failed to allocate RxBuf\n");
outl(rtl->bar0 + RTL8139_RBSTART, rtl->page0);
// Initialize tx buffers
for (size_t i = 0; i < 4; ++i) {
rtl->tx_pages[i] = amd64_phys_alloc_page();
rtl->tx_sizes[i] = 0;
assert(rtl->tx_pages[i] != MM_NADDR, "Failed to allocate TxBuf\n");
outl(rtl->bar0 + RTL8139_TSAD(i), rtl->tx_pages[i]);
}
rtl->tx_flag = 0;
rtl->tx_number = 0;
// Accept all
outl(rtl->bar0 + RTL8139_RCR, RTL8139_RCR_APM | RTL8139_RCR_AB | RTL8139_RCR_WRAP);
// Enable Rx/Tx
outb(rtl->bar0 + RTL8139_CR, RTL8139_CR_TE | RTL8139_CR_RE);
uint8_t irq_pin = (irq_config >> 8) & 0xFF;
if (irq_pin) {
kdebug("Uses INT%c# IRQ pin\n\n", 'A' + irq_pin - 1);
irq_add_pci_handler(addr, irq_pin - 1, rtl8139_irq, rtl);
}
// Configure interrupt mask
outw(rtl->bar0 + RTL8139_IMR, RTL8139_IMR_ROK | RTL8139_IMR_RER |
RTL8139_IMR_TOK | RTL8139_IMR_TER);
// Clear ISR
outw(rtl->bar0 + RTL8139_ISR, 0);
// After the reset, controller uses Tx pair 0
rtl->tx_number = 0;
// Setup a system network device
for (size_t i = 0; i < 6; ++i) {
rtl->net.hwaddr[i] = inb(rtl->bar0 + RTL8139_IDR0 + i);
}
kdebug("RTL8139 MAC: " MAC_FMT "\n", MAC_VA(rtl->net.hwaddr));
rtl->net.tx = rtl8139_tx;
// TODO: call some kind of netdev_add to bind a name to the device
// XXX: for testing
link_add_route_in(&rtl->net, 0x0002000A, 0x00FFFFFF, 0x0102000A);
}
static __init void pci_rtl8139_register(void) {
pci_add_device_driver(PCI_ID(0x10EC, 0x8139), pci_rtl8139_init);
}
+24
View File
@@ -0,0 +1,24 @@
#include "sys/amd64/hw/pci/pci.h"
#include "sys/amd64/hw/pci/usb_uhci.h"
#include "sys/debug.h"
#include "sys/attr.h"
//static void pci_usb_init(pci_addr_t addr) {
// uint32_t class = pci_config_read_dword(addr, PCI_CONFIG_CLASS);
// uint8_t prog_if = (class >> 8) & 0xFF;
//
// switch (prog_if) {
//#if defined(PCI_UHCI_ENABLE)
// case 0x00:
// pci_usb_uhci_init(addr);
// break;
//#endif
// default:
// kwarn("Unsupported USB controller type: %02x\n", prog_if);
// break;
// }
//}
//
//static __init void pci_register_usb(void) {
// pci_add_class_driver(0x0C03, pci_usb_init);
//}
+494
View File
@@ -0,0 +1,494 @@
#include "sys/amd64/hw/pci/usb_uhci.h"
#include "sys/amd64/mm/phys.h"
#include "sys/amd64/hw/io.h"
#include "sys/usb/request.h"
#include "sys/usb/device.h"
#include "sys/usb/usb.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/mm.h"
#define IO_USBCMD 0x00
#define IO_USBSTS 0x02
#define IO_USBINTR 0x04
#define IO_FRNUM 0x06
#define IO_FRBASEADD 0x08
#define IO_SOFMOD 0x0C
#define IO_PORTSC1 0x10
#define IO_PORTSC2 0x12
#define USBCMD_RUN (1 << 0)
#define USBCMD_HCRST (1 << 1)
#define USBCMD_GRST (1 << 2)
#define USBCMD_GSUS (1 << 3)
#define USBCMD_GRES (1 << 4)
#define USBCMD_SDBG (1 << 5)
#define USBCMD_CONF (1 << 6)
#define USBCMD_PMAX (1 << 7)
#define USBSTS_INTR (1 << 0)
#define USBSTS_EINT (1 << 1)
#define USBSTS_RESD (1 << 2)
#define USBSTS_ESYS (1 << 3)
#define USBSTS_EPROC (1 << 4)
#define USBSTS_HALT (1 << 5)
#define USBINTR_TCRC (1 << 0)
#define USBINTR_RES (1 << 1)
#define USBINTR_DONE (1 << 2)
#define USBINTR_SHORT (1 << 3)
#define USBPORT_CONN (1 << 0)
#define USBPORT_CNCH (1 << 1)
#define USBPORT_PEN (1 << 2)
#define USBPORT_PENCH (1 << 3)
#define USBPORT_RESD (1 << 6)
#define USBPORT_LSPD (1 << 8)
#define USBPORT_RST (1 << 9)
#define USBPORT_SUSP (1 << 12)
#define USB_FRAME_EMPTY (1 << 0)
#define USB_FRAME_QUEUE (1 << 1)
#define USB_FRAME_FULLQ (1 << 2)
#define UHCI_MAX_QH 8
#define UHCI_MAX_TD 32
#define UHCI_TD_USED (1 << 0)
struct uhci_td {
uint32_t next_td;
uint32_t status;
uint32_t header;
uint32_t buffer;
union {
uint32_t __sys0[4];
struct {
struct uhci_td *next;
uint32_t flags;
} system;
};
} __attribute__((packed));
struct uhci_qh {
uint32_t head_td;
uint32_t element_td;
// System
uint32_t list_index;
struct usb_transfer *transfer;
struct uhci_qh *prev, *next;
struct uhci_td *head_real;
uint32_t used;
uintptr_t pad;
};
struct uhci {
struct usb_controller hc;
uint32_t iobase;
uint32_t *frame_list;
uintptr_t pool_page;
struct uhci_td *td_pool;
struct uhci_qh *qh_pool;
struct uhci_qh *qh_async;
};
static struct uhci_td *uhci_alloc_td(struct uhci *hc) {
for (size_t i = 0; i < UHCI_MAX_TD; ++i) {
if (!(hc->td_pool[i].system.flags & UHCI_TD_USED)) {
_assert(!(((uintptr_t) &hc->td_pool[i]) & 0xF));
hc->td_pool[i].system.flags |= UHCI_TD_USED;
return &hc->td_pool[i];
}
}
return NULL;
}
static struct uhci_qh *uhci_alloc_qh(struct uhci *hc) {
for (size_t i = 0; i < UHCI_MAX_QH; ++i) {
if (!hc->qh_pool[i].used) {
_assert(!(((uintptr_t) &hc->qh_pool[i]) & 0xF));
hc->qh_pool[i].used = 1;
return &hc->qh_pool[i];
}
}
return NULL;
}
/* static */ void uhci_data_init(struct uhci *data, uint32_t bar4) {
uintptr_t frame_list_page = amd64_phys_alloc_contiguous(2);
_assert(frame_list_page != MM_NADDR && frame_list_page < 0x100000000);
uintptr_t pool_page = amd64_phys_alloc_page();
_assert(pool_page != MM_NADDR && pool_page < 0x100000000);
data->iobase = bar4 & ~3;
data->frame_list = (uint32_t *) MM_VIRTUALIZE(frame_list_page);
data->pool_page = pool_page;
data->td_pool = (struct uhci_td *) MM_VIRTUALIZE(pool_page);
data->qh_pool = (struct uhci_qh *) (MM_VIRTUALIZE(pool_page) + sizeof(struct uhci_td) * UHCI_MAX_TD);
memset(data->td_pool, 0, sizeof(struct uhci_td) * UHCI_MAX_TD);
memset(data->qh_pool, 0, sizeof(struct uhci_qh) * UHCI_MAX_QH);
data->qh_async = uhci_alloc_qh(data);
_assert(data->qh_async);
data->qh_async->head_td = USB_FRAME_EMPTY;
data->qh_async->element_td = USB_FRAME_EMPTY;
data->qh_async->transfer = NULL;
data->qh_async->prev = data->qh_async;
data->qh_async->next = data->qh_async;
for (uint32_t i = 0; i < 2048; ++i) {
data->frame_list[i] = USB_FRAME_QUEUE | MM_PHYS(data->qh_async);
}
}
static uint16_t uhci_port_reset(struct uhci *uhci, int port) {
uint16_t r;
uint16_t reg = uhci->iobase + port * 2 + IO_PORTSC1;
r = inw(reg);
r |= USBPORT_RST;
outw(reg, r);
for (size_t i = 0; i < 10000000; ++i);
r = inw(reg);
r &= ~USBPORT_RST;
outw(reg, r);
for (size_t i = 0; i < 10; ++i) {
for (size_t _ = 0; _ < 10000000; ++_);
r = inw(reg);
// Port is not connected
if (!(r & USBPORT_CONN)) {
break;
}
if (r & (USBPORT_PENCH | USBPORT_CNCH)) {
r &= ~(USBPORT_PENCH | USBPORT_CNCH);
outw(reg, r);
continue;
}
// Check if device is enabled
if (r & USBPORT_PEN) {
break;
}
// Try to enable the port
r |= USBPORT_PEN;
outw(reg, r);
}
return r;
}
static void uhci_start_qh(struct uhci *hc, struct uhci_qh *qh) {
//kdebug("Assign QH: %p\n", qh);
struct uhci_qh *list = hc->qh_async;
_assert(list);
struct uhci_qh *end = hc->qh_async->prev;
_assert(end);
qh->head_td = USB_FRAME_EMPTY;
end->head_td = (uint32_t) MM_PHYS(qh) | USB_FRAME_QUEUE;
qh->prev = end;
end->next = qh;
list->prev = qh;
qh->next = list;
//kdebug("---- After add ----\n");
//uhci_dumpq(hc);
//kdebug("---- After add ----\n");
}
static void uhci_remove_qh(struct uhci *hc, struct uhci_qh *qh) {
//kdebug("Unassign QH: %p\n", qh);
struct uhci_qh *list = hc->qh_async;
_assert(list);
struct uhci_qh *prev = qh->prev;
_assert(prev);
prev->head_td = qh->head_td;
prev->next = list;
list->prev = prev;
qh->next = NULL;
qh->prev = NULL;
//kdebug("---- After remove ----\n");
//uhci_dumpq(hc);
//kdebug("---- After remove ----\n");
}
static void uhci_free_td(struct uhci_td *td) {
//kdebug("Free TD: %p\n", td);
td->system.flags = 0;
td->next_td = USB_FRAME_EMPTY;
td->buffer = 0;
}
static void uhci_free_qh(struct uhci_qh *qh) {
//kdebug("Free QH: %p\n", qh);
qh->transfer = 0;
qh->used = 0;
qh->head_real = NULL;
qh->head_td = 0;
qh->element_td = 0;
}
static void uhci_process_qh(struct uhci *hc, struct uhci_qh *qh) {
struct usb_transfer *t = qh->transfer;
uint32_t elem_phys = qh->element_td & ~0xF;
if (!elem_phys) {
t->flags = USB_TRANSFER_COMPLETE | USB_TRANSFER_SUCCESS;
} else {
struct uhci_td *td = (struct uhci_td *) MM_VIRTUALIZE(elem_phys);
if (td->status & (1 << 22)) {
kwarn("Transfer descriptor is stalled\n");
t->flags = USB_TRANSFER_COMPLETE;
}
}
if (t->flags & USB_TRANSFER_COMPLETE) {
//kdebug("Transfer is complete\n");
uhci_remove_qh(hc, qh);
(void) uhci_free_td;
// Release TDs
uint32_t td_ptr = 1234;
struct uhci_td *td = qh->head_real;
_assert(td);
while (1) {
td_ptr = td->next_td;
uhci_free_td(td);
if (!(td_ptr & ~0xF) || (td_ptr & USB_FRAME_EMPTY)) {
break;
}
td = (struct uhci_td *) MM_VIRTUALIZE(td_ptr & ~0xF);
}
uhci_free_qh(qh);
}
}
static void uhci_wait_qh(struct uhci *hc, struct uhci_qh *qh) {
//kdebug("Wait for QH: %p\n", qh);
struct usb_transfer *t = qh->transfer;
while (!(t->flags & USB_TRANSFER_COMPLETE)) {
uhci_process_qh(hc, qh);
}
kdebug("Transfer is complete\n");
}
static void uhci_init_td(struct uhci_td *td, struct uhci_td *prev, uint8_t speed, uint8_t addr, uint8_t endp, uint16_t len, uint8_t packet_type, uint8_t toggle, void *buf) {
// Packet header:
// 0 .. 7 Packet type
// 0x69 IN
// 0xE1 OUT
// 0x2D SETUP
// 8 .. 14 Device address
// 15 .. 18 Endpoint address
// 19 Data toggle
// 20 Reserved (0)
// 21 .. 31 Maximum payload length - 1
_assert((((uintptr_t) td) & 0xF) == 0);
len = (len - 1) & 0x7FF;
if (prev) {
prev->next_td = (uint32_t) MM_PHYS(td) | USB_FRAME_FULLQ;
}
td->header = packet_type |
(len << 21) |
(toggle << 19) |
(addr << 8) |
(endp << 15);
// Status:
// 23 Active
// 26 Low speed
// 27 .. 28 Error counter
td->status = (1 << 23) |
(3 << 27);
if (speed == USB_SPEED_LOW) {
td->status |= 1 << 26;
}
td->buffer = MM_PHYS(buf);
}
static void uhci_device_interrupt(struct usb_device *dev, struct usb_transfer *t) {
struct uhci *hc = dev->hc;
uint8_t speed = dev->speed;
uint8_t addr = dev->address;
uint8_t endp = dev->desc_endpoint.address & 0xF;
struct uhci_td *td = uhci_alloc_td(hc);
_assert(td);
struct uhci_td *head = td;
struct uhci_td *prev = NULL;
uint8_t toggle = dev->endpoint_toggle;
uint8_t packet_type = 0x69;
uint8_t packet_size = t->length;
uhci_init_td(td, prev, speed, addr, endp, toggle, packet_type, packet_size, t->data);
struct uhci_qh *qh = uhci_alloc_qh(hc);
_assert(qh);
qh->head_real = head;
qh->head_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
qh->element_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
qh->transfer = t;
uhci_start_qh(hc, qh);
}
static void uhci_device_control(struct usb_device *dev, struct usb_transfer *t) {
struct uhci *hc = dev->hc;
struct usb_request *req = t->request;
uint8_t max_len = sizeof(struct usb_request);
struct uhci_td *td, *head, *prev;
struct uhci_qh *qh;
td = uhci_alloc_td(hc);
_assert(td);
head = td;
uint8_t addr = dev->address;
uint8_t endp = 0;
uint8_t speed = dev->speed;
uint16_t packet_size = sizeof(struct usb_request);
uint8_t packet_type = 0x2D;
uint8_t toggle = 0;
// Setup first packet (SETUP)
uhci_init_td(td, NULL, speed, addr, endp, packet_size, packet_type, toggle, req);
prev = td;
// Middle packets (IN)
size_t off = 0;
size_t rem = req->length;
packet_type = (req->type & USB_REQUEST_TYPE_D2H) ? 0x69 : 0xE1;
while (rem) {
packet_size = MIN(rem, dev->max_packet);
td = uhci_alloc_td(hc);
_assert(td);
toggle ^= 1;
uhci_init_td(td, prev, speed, addr, endp, packet_size, packet_type, toggle, t->data + off);
prev = td;
off += packet_size;
rem -= packet_size;
}
// Ending packet (OUT)
td = uhci_alloc_td(hc);
_assert(td);
packet_type = (req->type & USB_REQUEST_TYPE_D2H) ? 0xE1 : 0x69;
uhci_init_td(td, prev, speed, addr, endp, 0, packet_type, 1, NULL);
qh = uhci_alloc_qh(hc);
_assert(qh);
qh->head_real = head;
qh->head_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
qh->element_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
qh->transfer = t;
uhci_start_qh(hc, qh);
uhci_wait_qh(hc, qh);
}
/* static */ void uhci_probe(struct uhci *uhci) {
uint16_t portsc;
for (int port = 0; port < 2; ++port) {
portsc = uhci_port_reset(uhci, port);
if (portsc & USBPORT_PEN) {
kdebug("Port %d is enabled\n", port);
// Setup a new USB device
struct usb_device *dev = usb_device_create();
dev->hc = uhci;
dev->max_packet = 8;
dev->hc_control = uhci_device_control;
dev->hc_interrupt = uhci_device_interrupt;
dev->address = 0;
dev->endpoint_toggle = 0;
if (portsc & USBPORT_LSPD) {
dev->speed = USB_SPEED_LOW;
} else {
dev->speed = USB_SPEED_FULL;
}
usb_device_init(dev);
}
}
}
/* static */ void uhci_poll(struct usb_controller *_hc) {
struct uhci *hc = (struct uhci *) _hc;
struct uhci_qh *qh = hc->qh_async;
struct uhci_qh *next;
while (qh) {
next = qh->next;
if (qh->transfer) {
uhci_process_qh(hc, qh);
break;
}
qh = next;
}
}
//void pci_usb_uhci_init(pci_addr_t addr) {
//}
// uint32_t bar4;
// struct uhci *hc = kmalloc(sizeof(struct uhci));
// _assert(hc);
//
// kdebug("Initializing USB UHCI at " PCI_FMTADDR "\n", PCI_VAADDR(addr));
//
// bar4 = pci_config_read_dword(addr, PCI_CONFIG_BAR(4));
// _assert(bar4 & 1);
// uhci_data_init(hc, bar4);
// kdebug("Base addr %p\n", hc->frame_list);
//
// // Disable IRQs
// outw(hc->iobase + IO_USBINTR, 0);
// // Setup frame lists
// outl(hc->iobase + IO_FRBASEADD, (uint32_t) MM_PHYS(hc->frame_list));
// outw(hc->iobase + IO_FRNUM, 0);
// outw(hc->iobase + IO_SOFMOD, 0x40);
// // Clear status
// outw(hc->iobase + IO_USBSTS, 0xFFFF);
// // Enable controller
// outw(hc->iobase + IO_USBCMD, USBCMD_RUN);
//
// uhci_probe(hc);
//
// hc->hc.spec = USB_SPEC_UHCI;
// hc->hc.hc_poll = uhci_poll;
//
// usb_controller_add((struct usb_controller *) hc);
//}
+11
View File
@@ -1,5 +1,6 @@
#include "sys/amd64/loader/multiboot.h"
#include "sys/amd64/asm/asm_irq.h"
#include "sys/amd64/hw/pci/pci.h"
#include "sys/amd64/hw/rs232.h"
#include "sys/amd64/smp/smp.h"
#include "sys/amd64/syscall.h"
@@ -124,6 +125,16 @@ void kernel_main(struct amd64_loader_data *data) {
kinfo("yggdrasil " KERNEL_VERSION_STR "\n");
amd64_apic_init();
rtc_init();
// Setup system time
struct tm t;
rtc_read(&t);
system_boot_time = mktime(&t);
kinfo("Boot time: %04u-%02u-%02u %02u:%02u:%02u\n",
t.tm_year, t.tm_mon, t.tm_mday,
t.tm_hour, t.tm_min, t.tm_sec);
pci_init();
vfs_init();
tty_init();