diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/src/include/hardDrive.h b/src/include/hardDrive.h index e8a04d3..554c61c 100644 --- a/src/include/hardDrive.h +++ b/src/include/hardDrive.h @@ -5,6 +5,20 @@ #include #include +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + typedef struct HardDrive { char *model; uint32_t size; diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/src/include/hardDrive.h b/src/include/hardDrive.h index e8a04d3..554c61c 100644 --- a/src/include/hardDrive.h +++ b/src/include/hardDrive.h @@ -5,6 +5,20 @@ #include #include +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + typedef struct HardDrive { char *model; uint32_t size; diff --git a/src/include/list.h b/src/include/list.h index 5a6a0ff..f61708e 100644 --- a/src/include/list.h +++ b/src/include/list.h @@ -16,4 +16,6 @@ extern void *popBeginning(ListElement **list); +extern void memset(void *buffer, uint8_t byte, uint32_t count); + #endif diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/src/include/hardDrive.h b/src/include/hardDrive.h index e8a04d3..554c61c 100644 --- a/src/include/hardDrive.h +++ b/src/include/hardDrive.h @@ -5,6 +5,20 @@ #include #include +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + typedef struct HardDrive { char *model; uint32_t size; diff --git a/src/include/list.h b/src/include/list.h index 5a6a0ff..f61708e 100644 --- a/src/include/list.h +++ b/src/include/list.h @@ -16,4 +16,6 @@ extern void *popBeginning(ListElement **list); +extern void memset(void *buffer, uint8_t byte, uint32_t count); + #endif diff --git a/src/kernel/drivers/pci/hardDrive/sataController.c b/src/kernel/drivers/pci/hardDrive/sataController.c index c5ebe18..f9e5e9f 100644 --- a/src/kernel/drivers/pci/hardDrive/sataController.c +++ b/src/kernel/drivers/pci/hardDrive/sataController.c @@ -1,22 +1,338 @@ #include <_stdio.h> +#include #include // information source: // https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisLower, fisUpper; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + typedef struct { uint32_t hostCapabilities, globalHostControl, interruptStatus, implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; } HbaMemoryRegisters; +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer); + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + sataCommand(interface->port, ATA_CMD_READ_DMA_EXT, buffer); + return; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (interface->port->commandIssue & slotMask) { + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= ~(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + disableCommands(port); + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + uint64_t fis = (uint64_t)mallocAligned(sizeof(CommandListStructure), 10); + port->fisLower = fis; + port->fisUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 32; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 8); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer) { + port->interruptStatus = -1; + CommandListStructure *commandList = + getPointer(port->commandListLower, port->commandListUpper); + int8_t slot = findFreeCommandHeader(port); + if (slot == -1) { + printf("sata access failed: no free command header slot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; + commandHeader->regionDescriptorCount = 1; + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + commandTable->regionDescriptor[0].dataBaseLower = (uint64_t)buffer; + commandTable->regionDescriptor[0].dataBaseUpper = 0; + commandTable->regionDescriptor[0].byteCount = 511; + commandTable->regionDescriptor[0].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + fis->type = FIS_TO_DEVICE_TYPE; + fis->command = command; + fis->device = 0; + fis->commandControl = 1; + fis->device = 1 << 6; + fis->countLow = 1; + insertLba(fis, buffer); + while (port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + port->commandIssue = 1; + while (port->commandIssue == 1) { + yields(); + } +} + +void sataDeviceInitialize(PortControlRegisters *port, ListElement **drives, + HbaMemoryRegisters *registers) { + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = port; + relocatePort(interface->port); + uint8_t *buffer = malloc(512); + sataCommand(port, ATA_CMD_IDENTIFY, buffer); + char *model = malloc(41); + for (int i = 0; i < 40; i += 2) { + *((uint16_t *)&model[i]) = buffer[54 + i + 1]; + *((uint16_t *)&model[i + 1]) = buffer[54 + i]; + } + model[41] = 0; + printf("model: %s\n", model); + hardDrive->model = model; + free(buffer); + listAdd(drives, hardDrive); +} + void initializeSataController(PciDevice *pciDevice, ListElement **drives) { if (pciDevice->programmingInterface != 1) { // todo return; } HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; - printf("hostCapabilities: %x\nimplementedPorts: %x\nversion: %x\n", - registers->hostCapabilities, registers->implementedPorts, - registers->version); + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + sataDeviceInitialize(®isters->ports[i], drives, registers); + } } diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/src/include/hardDrive.h b/src/include/hardDrive.h index e8a04d3..554c61c 100644 --- a/src/include/hardDrive.h +++ b/src/include/hardDrive.h @@ -5,6 +5,20 @@ #include #include +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + typedef struct HardDrive { char *model; uint32_t size; diff --git a/src/include/list.h b/src/include/list.h index 5a6a0ff..f61708e 100644 --- a/src/include/list.h +++ b/src/include/list.h @@ -16,4 +16,6 @@ extern void *popBeginning(ListElement **list); +extern void memset(void *buffer, uint8_t byte, uint32_t count); + #endif diff --git a/src/kernel/drivers/pci/hardDrive/sataController.c b/src/kernel/drivers/pci/hardDrive/sataController.c index c5ebe18..f9e5e9f 100644 --- a/src/kernel/drivers/pci/hardDrive/sataController.c +++ b/src/kernel/drivers/pci/hardDrive/sataController.c @@ -1,22 +1,338 @@ #include <_stdio.h> +#include #include // information source: // https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisLower, fisUpper; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + typedef struct { uint32_t hostCapabilities, globalHostControl, interruptStatus, implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; } HbaMemoryRegisters; +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer); + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + sataCommand(interface->port, ATA_CMD_READ_DMA_EXT, buffer); + return; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (interface->port->commandIssue & slotMask) { + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= ~(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + disableCommands(port); + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + uint64_t fis = (uint64_t)mallocAligned(sizeof(CommandListStructure), 10); + port->fisLower = fis; + port->fisUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 32; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 8); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer) { + port->interruptStatus = -1; + CommandListStructure *commandList = + getPointer(port->commandListLower, port->commandListUpper); + int8_t slot = findFreeCommandHeader(port); + if (slot == -1) { + printf("sata access failed: no free command header slot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; + commandHeader->regionDescriptorCount = 1; + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + commandTable->regionDescriptor[0].dataBaseLower = (uint64_t)buffer; + commandTable->regionDescriptor[0].dataBaseUpper = 0; + commandTable->regionDescriptor[0].byteCount = 511; + commandTable->regionDescriptor[0].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + fis->type = FIS_TO_DEVICE_TYPE; + fis->command = command; + fis->device = 0; + fis->commandControl = 1; + fis->device = 1 << 6; + fis->countLow = 1; + insertLba(fis, buffer); + while (port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + port->commandIssue = 1; + while (port->commandIssue == 1) { + yields(); + } +} + +void sataDeviceInitialize(PortControlRegisters *port, ListElement **drives, + HbaMemoryRegisters *registers) { + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = port; + relocatePort(interface->port); + uint8_t *buffer = malloc(512); + sataCommand(port, ATA_CMD_IDENTIFY, buffer); + char *model = malloc(41); + for (int i = 0; i < 40; i += 2) { + *((uint16_t *)&model[i]) = buffer[54 + i + 1]; + *((uint16_t *)&model[i + 1]) = buffer[54 + i]; + } + model[41] = 0; + printf("model: %s\n", model); + hardDrive->model = model; + free(buffer); + listAdd(drives, hardDrive); +} + void initializeSataController(PciDevice *pciDevice, ListElement **drives) { if (pciDevice->programmingInterface != 1) { // todo return; } HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; - printf("hostCapabilities: %x\nimplementedPorts: %x\nversion: %x\n", - registers->hostCapabilities, registers->implementedPorts, - registers->version); + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + sataDeviceInitialize(®isters->ports[i], drives, registers); + } } diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 7375f67..c7c1d50 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -11,12 +11,10 @@ #include #include -extern uint32_t _kernel_end; +extern uint32_t kernelEnd; void kernelMain() { - initMemoryAllocation( - 0x1000000); // initializing stacks after the kernel seems not to work :( - // otherwise, _kernel_end should be passed here + initMemoryAllocation(kernelEnd); initOSTasks(); getCurrentTask()->timerTicks = 1000; getCurrentTask()->ticksLeft = 1000; diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/src/include/hardDrive.h b/src/include/hardDrive.h index e8a04d3..554c61c 100644 --- a/src/include/hardDrive.h +++ b/src/include/hardDrive.h @@ -5,6 +5,20 @@ #include #include +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + typedef struct HardDrive { char *model; uint32_t size; diff --git a/src/include/list.h b/src/include/list.h index 5a6a0ff..f61708e 100644 --- a/src/include/list.h +++ b/src/include/list.h @@ -16,4 +16,6 @@ extern void *popBeginning(ListElement **list); +extern void memset(void *buffer, uint8_t byte, uint32_t count); + #endif diff --git a/src/kernel/drivers/pci/hardDrive/sataController.c b/src/kernel/drivers/pci/hardDrive/sataController.c index c5ebe18..f9e5e9f 100644 --- a/src/kernel/drivers/pci/hardDrive/sataController.c +++ b/src/kernel/drivers/pci/hardDrive/sataController.c @@ -1,22 +1,338 @@ #include <_stdio.h> +#include #include // information source: // https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisLower, fisUpper; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + typedef struct { uint32_t hostCapabilities, globalHostControl, interruptStatus, implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; } HbaMemoryRegisters; +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer); + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + sataCommand(interface->port, ATA_CMD_READ_DMA_EXT, buffer); + return; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (interface->port->commandIssue & slotMask) { + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= ~(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + disableCommands(port); + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + uint64_t fis = (uint64_t)mallocAligned(sizeof(CommandListStructure), 10); + port->fisLower = fis; + port->fisUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 32; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 8); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer) { + port->interruptStatus = -1; + CommandListStructure *commandList = + getPointer(port->commandListLower, port->commandListUpper); + int8_t slot = findFreeCommandHeader(port); + if (slot == -1) { + printf("sata access failed: no free command header slot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; + commandHeader->regionDescriptorCount = 1; + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + commandTable->regionDescriptor[0].dataBaseLower = (uint64_t)buffer; + commandTable->regionDescriptor[0].dataBaseUpper = 0; + commandTable->regionDescriptor[0].byteCount = 511; + commandTable->regionDescriptor[0].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + fis->type = FIS_TO_DEVICE_TYPE; + fis->command = command; + fis->device = 0; + fis->commandControl = 1; + fis->device = 1 << 6; + fis->countLow = 1; + insertLba(fis, buffer); + while (port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + port->commandIssue = 1; + while (port->commandIssue == 1) { + yields(); + } +} + +void sataDeviceInitialize(PortControlRegisters *port, ListElement **drives, + HbaMemoryRegisters *registers) { + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = port; + relocatePort(interface->port); + uint8_t *buffer = malloc(512); + sataCommand(port, ATA_CMD_IDENTIFY, buffer); + char *model = malloc(41); + for (int i = 0; i < 40; i += 2) { + *((uint16_t *)&model[i]) = buffer[54 + i + 1]; + *((uint16_t *)&model[i + 1]) = buffer[54 + i]; + } + model[41] = 0; + printf("model: %s\n", model); + hardDrive->model = model; + free(buffer); + listAdd(drives, hardDrive); +} + void initializeSataController(PciDevice *pciDevice, ListElement **drives) { if (pciDevice->programmingInterface != 1) { // todo return; } HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; - printf("hostCapabilities: %x\nimplementedPorts: %x\nversion: %x\n", - registers->hostCapabilities, registers->implementedPorts, - registers->version); + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + sataDeviceInitialize(®isters->ports[i], drives, registers); + } } diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 7375f67..c7c1d50 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -11,12 +11,10 @@ #include #include -extern uint32_t _kernel_end; +extern uint32_t kernelEnd; void kernelMain() { - initMemoryAllocation( - 0x1000000); // initializing stacks after the kernel seems not to work :( - // otherwise, _kernel_end should be passed here + initMemoryAllocation(kernelEnd); initOSTasks(); getCurrentTask()->timerTicks = 1000; getCurrentTask()->ticksLeft = 1000; diff --git a/src/kernel/memory/alloc.c b/src/kernel/memory/alloc.c index 868b8b6..8fb6975 100644 --- a/src/kernel/memory/alloc.c +++ b/src/kernel/memory/alloc.c @@ -51,6 +51,14 @@ return mallocTask(size, (Task *)getCurrentTask()); } +void *mallocAligned(uint32_t size, uint8_t alignment) { + uint64_t buffer = (uint64_t)malloc(size + (1 << alignment)); + buffer >>= alignment - 1; + buffer <<= alignment - 1; + buffer += 1 << alignment; + return (void *)buffer; +} + void mergeNextIfPossible(MemoryBlock *current) { if (current->next != 0x00 && current->next->state == FREE) { current->size += sizeof(MemoryBlock) + current->next->size; diff --git a/:w b/:w new file mode 100644 index 0000000..31145b6 --- /dev/null +++ b/:w @@ -0,0 +1,284 @@ +#include <_stdio.h> +#include +#include + +// information source: +// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf + +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisBaseLower, fisBaseHigher; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + +typedef struct { + uint32_t hostCapabilities, globalHostControl, interruptStatus, + implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, + enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; +} HbaMemoryRegisters; + +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + printf("interface: %x\ncommand header: %x\ncommandList: %x\n", interface, + commandHeader, commandList); + yields(); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + printf("size: %i\n", commandHeader->fisLength); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (1) { + if (!(interface->port->commandIssue & slotMask)) { + break; + } + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= !(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + disableCommands(port); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 2; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 7); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void initializeSataController(PciDevice *pciDevice, ListElement **drives) { + if (pciDevice->programmingInterface != 1) { + // todo + return; + } + HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = ®isters->ports[i]; + relocatePort(interface->port); + listAdd(drives, hardDrive); + printf("device on port %i has type %x\n", i, type); + } +} diff --git a/linker.ld b/linker.ld index 07ad149..902dd6f 100644 --- a/linker.ld +++ b/linker.ld @@ -25,4 +25,12 @@ .rodata : { *(.rodata) } -} \ No newline at end of file + + .data : { + *(.data) + kernelEnd = .; + LONG(endOfKernel) + } + + endOfKernel = .; +} diff --git a/src/include/alloc.h b/src/include/alloc.h index 0e40e82..93229c1 100644 --- a/src/include/alloc.h +++ b/src/include/alloc.h @@ -7,15 +7,16 @@ typedef enum MemoryState { FREE = 0, IN_USE = 1 } MemoryState; typedef struct MemoryBlock { - struct MemoryBlock *last; - struct MemoryBlock *next; - MemoryState state; - uint32_t size; - Task *task; + struct MemoryBlock *last; + struct MemoryBlock *next; + MemoryState state; + uint32_t size; + Task *task; } MemoryBlock; extern void initMemoryAllocation(uint32_t kernelEnd); extern void *malloc(uint32_t size); +extern void *mallocAligned(uint32_t size, uint8_t alignment); extern void *mallocTask(uint32_t size, Task *task); extern void free(void *location); extern void printMemoryStack(); diff --git a/src/include/hardDrive.h b/src/include/hardDrive.h index e8a04d3..554c61c 100644 --- a/src/include/hardDrive.h +++ b/src/include/hardDrive.h @@ -5,6 +5,20 @@ #include #include +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + typedef struct HardDrive { char *model; uint32_t size; diff --git a/src/include/list.h b/src/include/list.h index 5a6a0ff..f61708e 100644 --- a/src/include/list.h +++ b/src/include/list.h @@ -16,4 +16,6 @@ extern void *popBeginning(ListElement **list); +extern void memset(void *buffer, uint8_t byte, uint32_t count); + #endif diff --git a/src/kernel/drivers/pci/hardDrive/sataController.c b/src/kernel/drivers/pci/hardDrive/sataController.c index c5ebe18..f9e5e9f 100644 --- a/src/kernel/drivers/pci/hardDrive/sataController.c +++ b/src/kernel/drivers/pci/hardDrive/sataController.c @@ -1,22 +1,338 @@ #include <_stdio.h> +#include #include // information source: // https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf +#define SATA_ATA 0x00000101 +#define SATA_ATAPI 0xEB140101 +#define SATA_ENCLOSURE_MANAGEMENT_BRIDGE 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define AHCI_DEV_NULL 0 +#define AHCI_SATA_DEVICE 1 +#define AHCI_SEMB_DEVICE 2 +#define AHCI_DEV_PM 3 +#define AHCI_DEV_SATAPI 4 + +#define POWER_ACTIVE 1 +#define DEVICE_PRESENT 3 + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 + +typedef struct { + uint32_t commandListLower, commandListUpper; + uint32_t fisLower, fisUpper; + uint32_t interruptStatus, interruptEnable; + uint32_t command; + uint32_t reserved; + uint32_t taskFileData; + uint32_t signature; + uint32_t sataStatus, sataControl, sataError, sataActive; + uint32_t commandIssue; + uint32_t sataNotification; + uint32_t fisSwitchingControl; + uint32_t deviceSleep; + uint8_t reserved_[50]; + uint32_t vendorSpecific; +} PortControlRegisters; + typedef struct { uint32_t hostCapabilities, globalHostControl, interruptStatus, implementedPorts, version, cccCtl, cccPts, enclosureManagementLocation, enclosureManagementControl, hostCapabilitiesExtended, handoffStatus; + uint8_t reserved[116]; + uint8_t vendor[96]; + PortControlRegisters ports[32]; } HbaMemoryRegisters; +typedef struct { + uint8_t fisLength : 5, ATAPI : 1, write : 1, prefetchable : 1; + + uint8_t reset : 1, bist : 1, clearBusy : 1, reserved : 1, + portMultiplierPort : 4; + + uint16_t regionDescriptorCount; + + volatile uint32_t transferedBytes; + + uint32_t commandTableLower, commandTableUpper; + + uint32_t reserved_[4]; +} CommandHeader; + +typedef struct { + CommandHeader commandHeaders[32]; +} CommandListStructure; + +typedef struct { + uint32_t dataBaseLower, dataBaseUpper; + uint32_t reserved; + uint32_t byteCount : 22, reserved_ : 9, interrupt : 1; +} PhysicalRegionDescriptor; + +typedef struct { + uint8_t fis[64]; + uint8_t atapi[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor regionDescriptor[8]; +} CommandTable; + +typedef struct { + HbaMemoryRegisters *registers; + PortControlRegisters *port; +} SataInterface; + +#define FIS_TO_DEVICE_TYPE 0x27 + +typedef struct { + uint8_t type; + uint8_t portMultiplierPort : 4, reserved : 3, commandControl : 1; + + uint8_t command, featureLow; + + uint8_t lba0, lba1, lba2, device; + + uint8_t lba3, lba4, lba5, featureHigh; + + uint8_t countLow, countHigh, isochronousCommandCompletion, control; + + uint8_t reserved_[4]; +} FisToDevice; + +void *getPointer(uint32_t lower, uint32_t upper) { + return (void *)(lower | (uint64_t)upper << 32); +} + +uint32_t getType(PortControlRegisters *registers) { + uint32_t status = registers->sataStatus; + uint8_t detectionStatus = status & 0xF; + uint8_t interfacePowerManagement = (status >> 8) & 0xF; + if (detectionStatus != DEVICE_PRESENT || + interfacePowerManagement != POWER_ACTIVE) { + return AHCI_DEV_NULL; + } + switch (registers->signature) { + case SATA_ATAPI: + return AHCI_DEV_SATAPI; + case SATA_ENCLOSURE_MANAGEMENT_BRIDGE: + return AHCI_SEMB_DEVICE; + case SATA_SIG_PM: + return AHCI_DEV_PM; + default: + return AHCI_SATA_DEVICE; + } +} + +int8_t findFreeCommandHeader(PortControlRegisters *port) { + uint32_t filledSlots = port->sataControl | port->commandIssue; + for (uint8_t i = 0; i < 32; i++) { + if (!(filledSlots & 1 >> i)) { + return i; + } + } + return -1; +} + +void insertLba(FisToDevice *fis, void *buffer) { + uint64_t bufferPosition = (uint64_t)buffer; + fis->lba0 = (uint8_t)bufferPosition; + fis->lba1 = (uint8_t)(bufferPosition >> 8); + fis->lba2 = (uint8_t)(bufferPosition >> 16); + fis->lba3 = (uint8_t)(bufferPosition >> 24); + fis->lba4 = (uint8_t)(bufferPosition >> 32); + fis->lba5 = (uint8_t)(bufferPosition >> 40); +} + +void setupFisToRead(FisToDevice *fis, void *buffer, uint8_t sectorCount) { + fis->type = FIS_TO_DEVICE_TYPE; + fis->commandControl = 1; + fis->command = ATA_CMD_READ_DMA_EXT; + fis->device = 1 << 6; + fis->countLow = (uint8_t)sectorCount; + fis->countHigh = (uint8_t)(sectorCount >> 8); + insertLba(fis, buffer); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer); + +void sataDriveAccess(HardDrive *hardDrive, uint32_t lba, void *buffer, + uint8_t sectorCount, uint8_t direction) { + SataInterface *interface = hardDrive->interface; + sataCommand(interface->port, ATA_CMD_READ_DMA_EXT, buffer); + return; + CommandListStructure *commandList = getPointer( + interface->port->commandListLower, interface->port->commandListUpper); + interface->port->interruptStatus = -1; // clear pending interrupts + int8_t slot = findFreeCommandHeader(interface->port); + if (slot == -1) { + printf("sata access failed: no free command header spot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; // read + commandHeader->regionDescriptorCount = ((sectorCount - 1) >> 4) + 1; + + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + uint32_t i; + void *currentPosition = buffer; + for (i = 0; i < commandHeader->regionDescriptorCount - 1; i++) { + commandTable->regionDescriptor[i].dataBaseLower = currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + ((uint64_t)currentPosition) >> 32; + commandTable->regionDescriptor[i].byteCount = 8 * 1024 - 1; + commandTable->regionDescriptor[i].interrupt = 1; + currentPosition += 8 * 1024; + sectorCount -= 16; + } + commandTable->regionDescriptor[i].dataBaseLower = (uint64_t)currentPosition; + commandTable->regionDescriptor[i].dataBaseUpper = + (uint64_t)currentPosition >> 32; + commandTable->regionDescriptor[i].byteCount = (sectorCount << 9) - 1; + commandTable->regionDescriptor[i].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + setupFisToRead(fis, buffer, sectorCount); + printf("waiting."); + while (interface->port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + printf(".."); + yields(); + + uint32_t slotMask = 1 << slot; + interface->port->commandIssue |= slotMask; + while (interface->port->commandIssue & slotMask) { + } + printf("\n"); +} + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +void disableCommands(PortControlRegisters *port) { + port->command &= ~(HBA_PxCMD_ST | HBA_PxCMD_FRE); + while (port->command & HBA_PxCMD_FR && port->command + HBA_PxCMD_CR) { + } +} + +void enableCommands(PortControlRegisters *port) { + while (port->command & HBA_PxCMD_CR) { + } + port->command |= HBA_PxCMD_ST | HBA_PxCMD_FRE; +} + +void relocatePort(PortControlRegisters *port) { + // call only once for each port, nothing is being freed here + disableCommands(port); + CommandListStructure *commandList = + mallocAligned(sizeof(CommandListStructure), 10); + port->commandListLower = commandList; + port->commandListUpper = (uint64_t)commandList >> 32; + uint64_t fis = (uint64_t)mallocAligned(sizeof(CommandListStructure), 10); + port->fisLower = fis; + port->fisUpper = (uint64_t)commandList >> 32; + memset(commandList, 0, sizeof(CommandListStructure)); + for (uint8_t i = 0; i < 32; i++) { + uint16_t size = 256; // sizeof(CommandTable) + commandList->commandHeaders[i].regionDescriptorCount = 8; + uint64_t commandTable = (uint64_t)mallocAligned(size, 8); + commandList->commandHeaders[i].commandTableLower = commandTable; + commandList->commandHeaders[i].commandTableUpper = commandTable >> 32; + memset((void *)commandTable, 0, size); + } + enableCommands(port); +} + +void sataCommand(PortControlRegisters *port, uint8_t command, void *buffer) { + port->interruptStatus = -1; + CommandListStructure *commandList = + getPointer(port->commandListLower, port->commandListUpper); + int8_t slot = findFreeCommandHeader(port); + if (slot == -1) { + printf("sata access failed: no free command header slot found!\n"); + return; + } + CommandHeader *commandHeader = &(commandList->commandHeaders[slot]); + commandHeader->fisLength = sizeof(FisToDevice) / sizeof(uint32_t); + commandHeader->write = 0; + commandHeader->regionDescriptorCount = 1; + CommandTable *commandTable = getPointer(commandHeader->commandTableLower, + commandHeader->commandTableUpper); + memset(commandTable, 0, + sizeof(CommandTable) + (commandHeader->regionDescriptorCount - 1) * + sizeof(PhysicalRegionDescriptor)); + commandTable->regionDescriptor[0].dataBaseLower = (uint64_t)buffer; + commandTable->regionDescriptor[0].dataBaseUpper = 0; + commandTable->regionDescriptor[0].byteCount = 511; + commandTable->regionDescriptor[0].interrupt = 1; + FisToDevice *fis = (void *)&(commandTable->fis); + fis->type = FIS_TO_DEVICE_TYPE; + fis->command = command; + fis->device = 0; + fis->commandControl = 1; + fis->device = 1 << 6; + fis->countLow = 1; + insertLba(fis, buffer); + while (port->taskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) { + yields(); + } + port->commandIssue = 1; + while (port->commandIssue == 1) { + yields(); + } +} + +void sataDeviceInitialize(PortControlRegisters *port, ListElement **drives, + HbaMemoryRegisters *registers) { + HardDrive *hardDrive = malloc(sizeof(HardDrive)); + SataInterface *interface = malloc(sizeof(SataInterface)); + hardDrive->access = sataDriveAccess; + hardDrive->interface = interface; + interface->registers = registers; + interface->port = port; + relocatePort(interface->port); + uint8_t *buffer = malloc(512); + sataCommand(port, ATA_CMD_IDENTIFY, buffer); + char *model = malloc(41); + for (int i = 0; i < 40; i += 2) { + *((uint16_t *)&model[i]) = buffer[54 + i + 1]; + *((uint16_t *)&model[i + 1]) = buffer[54 + i]; + } + model[41] = 0; + printf("model: %s\n", model); + hardDrive->model = model; + free(buffer); + listAdd(drives, hardDrive); +} + void initializeSataController(PciDevice *pciDevice, ListElement **drives) { if (pciDevice->programmingInterface != 1) { // todo return; } HbaMemoryRegisters *registers = (HbaMemoryRegisters *)pciDevice->bar5; - printf("hostCapabilities: %x\nimplementedPorts: %x\nversion: %x\n", - registers->hostCapabilities, registers->implementedPorts, - registers->version); + for (uint8_t i = 0; i < 32; i++) { + if (!(registers->implementedPorts & (1 << i))) { + continue; + } + uint32_t type = getType(®isters->ports[i]); + if (type == AHCI_DEV_NULL) { + continue; + } + if (type != AHCI_SATA_DEVICE) { + printf("type %i not implemented yet...\n", type); + // todo + continue; + } + sataDeviceInitialize(®isters->ports[i], drives, registers); + } } diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 7375f67..c7c1d50 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -11,12 +11,10 @@ #include #include -extern uint32_t _kernel_end; +extern uint32_t kernelEnd; void kernelMain() { - initMemoryAllocation( - 0x1000000); // initializing stacks after the kernel seems not to work :( - // otherwise, _kernel_end should be passed here + initMemoryAllocation(kernelEnd); initOSTasks(); getCurrentTask()->timerTicks = 1000; getCurrentTask()->ticksLeft = 1000; diff --git a/src/kernel/memory/alloc.c b/src/kernel/memory/alloc.c index 868b8b6..8fb6975 100644 --- a/src/kernel/memory/alloc.c +++ b/src/kernel/memory/alloc.c @@ -51,6 +51,14 @@ return mallocTask(size, (Task *)getCurrentTask()); } +void *mallocAligned(uint32_t size, uint8_t alignment) { + uint64_t buffer = (uint64_t)malloc(size + (1 << alignment)); + buffer >>= alignment - 1; + buffer <<= alignment - 1; + buffer += 1 << alignment; + return (void *)buffer; +} + void mergeNextIfPossible(MemoryBlock *current) { if (current->next != 0x00 && current->next->state == FREE) { current->size += sizeof(MemoryBlock) + current->next->size; diff --git a/src/kernel/util/list.c b/src/kernel/util/list.c index e5ee306..76110c4 100644 --- a/src/kernel/util/list.c +++ b/src/kernel/util/list.c @@ -1,6 +1,13 @@ #include #include +void memset(void *buffer, uint8_t byte, uint32_t count) { + uint8_t *byteBuffer = buffer; + for (uint32_t i = 0; i < count; i++) { + byteBuffer[i] = byte; + } +} + void listAdd(ListElement **list, void *data) { ListElement *element = *list; if (element == NULL) {