diff --git a/sortix/Makefile b/sortix/Makefile index d196e2d2..39f59523 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -52,6 +52,11 @@ endif ifeq ($(PANIC_SHORT),1) DEFINES:=$(DEFINES) -DPANIC_SHORT endif +ifeq ($(DISKWRITE),1) + DEFINES:=$(DEFINES) -DENABLE_DISKWRITE=1 +else + DEFINES:=$(DEFINES) -DENABLE_DISKWRITE=0 +endif CPPFLAGSRELEASE=-s $(O) CPPFLAGSDEBUG= CPPFLAGS=-I.. -I. $(CPUDEFINES) $(CPUFLAGS) -std=gnu++0x -Wall -Wextra -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector $(DEFINES) $(CPPFLAGSRELEASE) @@ -86,6 +91,7 @@ signal.o \ fs/devfs.o \ fs/initfs.o \ fs/ramfs.o \ +ata.o \ ../libmaxsi/libmaxsi-sortix.a \ end.o # Must be last diff --git a/sortix/ata.cpp b/sortix/ata.cpp new file mode 100644 index 00000000..769a37b1 --- /dev/null +++ b/sortix/ata.cpp @@ -0,0 +1,359 @@ +/****************************************************************************** + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + ata.cpp + Allowes access to block devices over ATA PIO. + +******************************************************************************/ + +#include "platform.h" +#include +#include +#include "ata.h" + +// TODO: Use the PCI to detect ATA devices instead of relying on them being on +// standard locations. + +using namespace Maxsi; + +namespace Sortix +{ + const uint16_t PRIMARY_BUS_OFFSET = 0x1F0; + const uint16_t SECONDARY_BUS_OFFSET = 0x170; + const uint16_t DATA = 0x0; + const uint16_t FEATURE = 0x1; + const uint16_t ERROR = 0x1; + const uint16_t SECTOR_COUNT = 0x2; + const uint16_t LBA_LOW = 0x3; + const uint16_t LBA_MID = 0x4; + const uint16_t LBA_HIGH = 0x5; + const uint16_t DRIVE_SELECT = 0x6; + const uint16_t COMMAND = 0x7; + const uint16_t STATUS = 0x7; + const uint8_t CMD_READ = 0x20; + const uint8_t CMD_READ_EXT = 0x24; + const uint8_t CMD_WRITE = 0x30; + const uint8_t CMD_WRITE_EXT = 0x34; + const uint8_t CMD_FLUSH_CACHE = 0xE7; + const uint8_t CMD_IDENTIFY = 0xEC; + const uint8_t STATUS_ERROR = (1<<0); + const uint8_t STATUS_DATAREADY = (1<<3); + const uint8_t STATUS_DRIVEFAULT = (1<<5); + const uint8_t STATUS_BUSY = (1<<7); + const uint8_t CTL_NO_INTERRUPT = (1<<1); + const uint8_t CTL_RESET = (1<<2); + + namespace ATA + { + void DetectDrive(unsigned busid, ATABus* bus, unsigned driveid) + { + unsigned ataid = busid*2 + driveid; + ATADrive* drive = bus->Instatiate(driveid); + if ( !drive ) { return; } + // TODO: Actually use the drive somewhere. + } + + void DetectBus(unsigned busid, uint16_t ioport, uint16_t altio) + { + ATABus* bus = ATA::CreateBus(ioport, altio); + DetectDrive(busid, bus, 0); + DetectDrive(busid, bus, 1); + } + + void Init() + { + DetectBus(0, 0x1F0, 0x3F6); + DetectBus(1, 0x170, 0x366); + } + + ATABus* CreateBus(uint16_t portoffset, uint16_t altport) + { + unsigned status = CPU::InPortB(portoffset + STATUS); + // Detect if there is no such bus. + if ( status == 0xFF ) + { + Error::Set(ENODEV); + return NULL; + } + return new ATABus(portoffset, altport); + } + } + + void Wait400NSecs(uint16_t iobase) + { + // Now wait 400 ns for the drive to be ready. + for ( unsigned i = 0; i < 4; i++ ) { CPU::InPortB(iobase + STATUS); } + } + + ATABus::ATABus(uint16_t portoffset, uint16_t altport) + { + this->iobase = portoffset; + this->altport = altport; + this->curdriveid = 0; + } + + ATABus::~ATABus() + { + } + + ATADrive* ATABus::Instatiate(unsigned driveid) + { + if ( 1 < driveid ) { Error::Set(EINVAL); return false; } + curdriveid = 0; + + uint8_t drivemagic = 0xA0 | (driveid << 4); + CPU::OutPortB(iobase + DRIVE_SELECT, drivemagic); + CPU::OutPortB(iobase + SECTOR_COUNT, 0); + CPU::OutPortB(iobase + LBA_LOW, 0); + CPU::OutPortB(iobase + LBA_MID, 0); + CPU::OutPortB(iobase + LBA_HIGH, 0); + CPU::OutPortB(iobase + COMMAND, CMD_IDENTIFY); + uint8_t status; + while ( true ) + { + status = CPU::InPortB(iobase + STATUS); + if ( !status || status == 0xFF ) { Error::Set(ENODEV); return false; } + if ( !(status & STATUS_BUSY) ) { break; } + } + if ( CPU::InPortB(iobase + LBA_MID) || CPU::InPortB(iobase + LBA_MID) ) + { + Error::Set(ENODEV); return false; // ATAPI device not following spec. + } + while ( !(status & STATUS_DATAREADY) && !(status & STATUS_ERROR) ) + { + status = CPU::InPortB(iobase + STATUS); + } + if ( status & STATUS_ERROR ) + { + unsigned mid = CPU::InPortB(iobase + LBA_MID); + unsigned high = CPU::InPortB(iobase + LBA_HIGH); + if ( mid == 0x14 && high == 0xEB ) + { + //Log::PrintF("Found ATAPI device instead of ATA\n"); + } + else if ( mid == 0x3C && high == 0xC3 ) + { + //Log::PrintF("Found SATA device instead of ATA\n"); + } + else if ( mid || high ) + { + //Log::PrintF("Found unknown device instead of ATA\n"); + } + else + { + //Log::PrintF("Error status during identify\n"); + } + Error::Set(EIO); + return false; + } + ATADrive* drive = new ATADrive(this, driveid, iobase, altport); + return drive; + } + + bool ATABus::SelectDrive(unsigned driveid) + { + if ( driveid == curdriveid ) { return true; } + if ( 1 < driveid ) { Error::Set(EINVAL); return false; } + + uint8_t drivemagic = 0xA0 | (driveid << 4); + CPU::OutPortB(iobase + DRIVE_SELECT, drivemagic); + Wait400NSecs(iobase); + return true; + } + + const size_t META_LBA28 = 60; + const size_t META_FLAGS = 83; + const size_t META_LBA48 = 100; + const uint16_t FLAG_LBA48 = (1<<10); + + ATADrive::ATADrive(ATABus* bus, unsigned driveid, uint16_t portoffset, uint16_t altport) + { + this->bus = bus; + this->driveid = driveid; + this->iobase = portoffset; + this->altport = altport; + for ( size_t i = 0; i < 256; i++ ) + { + meta[i] = CPU::InPortW(iobase + DATA); + } + lba48 = meta[META_FLAGS] & FLAG_LBA48; + if ( lba48 ) + { + numsectors = *((uint64_t*) (meta + META_LBA48)); + } + else + { + numsectors = *((uint32_t*) (meta + META_LBA28)); + } + sectorsize = 512; // TODO: Detect this! + Initialize(); + } + + ATADrive::~ATADrive() + { + } + + off_t ATADrive::GetSectorSize() + { + return sectorsize; + } + + off_t ATADrive::GetNumSectors() + { + return numsectors; + } + + bool ATADrive::PrepareIO(bool write, off_t sector) + { + if ( numsectors <= sector ) { Error::Set(EINVAL); return false; } + if ( write && !ENABLE_DISKWRITE ) + { + Error::Set(EPERM); + return false; + } + bus->SelectDrive(driveid); + uint8_t mode = (lba48) ? 0x40 : 0xE0; + mode |= driveid << 4; + mode |= (lba48) ? 0 : (sector >> 24) & 0x0F; + CPU::OutPortB(iobase + DRIVE_SELECT, mode); + uint16_t sectorcount = 1; + uint8_t sectorcountlow = sectorcount & 0xFF; + uint8_t sectorcounthigh = (sectorcount >> 8) & 0xFF; + if ( lba48 ) + { + CPU::OutPortB(iobase + SECTOR_COUNT, sectorcounthigh); + CPU::OutPortB(iobase + LBA_LOW, (sector >> 24) & 0xFF); + CPU::OutPortB(iobase + LBA_MID, (sector >> 32) & 0xFF); + CPU::OutPortB(iobase + LBA_HIGH, (sector >> 40) & 0xFF); + } + CPU::OutPortB(iobase + SECTOR_COUNT, sectorcountlow); + CPU::OutPortB(iobase + LBA_LOW, sector & 0xFF); + CPU::OutPortB(iobase + LBA_MID, (sector >> 8) & 0xFF); + CPU::OutPortB(iobase + LBA_HIGH, (sector >> 16) & 0xFF); + uint8_t command = (write) ? CMD_WRITE : CMD_READ; + if ( lba48 ) { command = (write) ? CMD_WRITE_EXT : CMD_READ_EXT; } + CPU::OutPortB(iobase + COMMAND, command); + while ( true ) + { + uint8_t status = CPU::InPortB(iobase + STATUS); + if ( status & STATUS_BUSY ) { continue; } + if ( status & STATUS_DATAREADY ) { break; } + if ( status & STATUS_ERROR ) { Error::Set(EIO); return false; } + if ( status & STATUS_DRIVEFAULT ) { Error::Set(EIO); return false; } + } + return true; + } + + bool ATADrive::ReadSector(off_t sector, uint8_t* dest) + { + if ( !PrepareIO(false, sector) ) { return false; } + uint16_t* destword = (uint16_t*) dest; + for ( size_t i = 0; i < sectorsize/2; i++ ) + { + destword[i] = CPU::InPortW(iobase + DATA); + } + Wait400NSecs(iobase); + uint8_t status = CPU::InPortB(iobase + STATUS); + return true; + } + + bool ATADrive::WriteSector(off_t sector, const uint8_t* src) + { + if ( !PrepareIO(true, sector) ) { return false; } + const uint16_t* srcword = (const uint16_t*) src; + for ( size_t i = 0; i < sectorsize/2; i++ ) + { + CPU::OutPortW(iobase + DATA, srcword[i]); + } + Wait400NSecs(iobase); + CPU::OutPortB(iobase + COMMAND, CMD_FLUSH_CACHE); + while ( true ) + { + uint8_t status = CPU::InPortB(iobase + STATUS); + if ( status & STATUS_ERROR ) { Error::Set(EIO); return false; } + if ( status & STATUS_DRIVEFAULT ) { Error::Set(EIO); return false; } + if ( !(status & STATUS_BUSY) ) { break; } + } + return true; + } + + size_t ATADrive::Read(off_t byteoffset, uint8_t* dest, size_t numbytes) + { + size_t sofar = 0; + size_t leadingbytes = byteoffset % sectorsize; + if ( leadingbytes || numbytes < sectorsize ) + { + size_t wanted = sectorsize - leadingbytes; + if ( numbytes < wanted ) { wanted = numbytes; } + uint8_t temp[512 /*sectorsize*/]; + if ( !ReadSector(byteoffset/sectorsize, temp) ) { return sofar; } + Memory::Copy(dest + sofar, temp + leadingbytes, wanted); + sofar += wanted; + numbytes -= wanted; + byteoffset += wanted; + } + + while ( sectorsize <= numbytes ) + { + if ( !ReadSector(byteoffset/sectorsize, dest + sofar) ) { return sofar; } + sofar += sectorsize; + numbytes -= sectorsize; + byteoffset += sectorsize; + } + + if ( numbytes ) { return sofar + Read(byteoffset, dest + sofar, numbytes); } + return sofar; + } + + size_t ATADrive::Write(off_t byteoffset, const uint8_t* src, size_t numbytes) + { + size_t sofar = 0; + size_t leadingbytes = byteoffset % sectorsize; + if ( leadingbytes || numbytes < sectorsize ) + { + size_t wanted = sectorsize - leadingbytes; + if ( numbytes < wanted ) { wanted = numbytes; } + uint8_t temp[512 /*sectorsize*/]; + if ( !ReadSector(byteoffset/sectorsize, temp) ) { return sofar; } + Memory::Copy(temp + leadingbytes, src + sofar, wanted); + if ( !WriteSector(byteoffset/sectorsize, temp) ) { return sofar; } + sofar += wanted; + numbytes -= wanted; + byteoffset += wanted; + } + + while ( sectorsize <= numbytes ) + { + if ( !WriteSector(byteoffset/sectorsize, src + sofar) ) { return sofar; } + sofar += sectorsize; + numbytes -= sectorsize; + byteoffset += sectorsize; + } + + if ( numbytes ) { return sofar + Write(byteoffset, src + sofar, numbytes); } + return sofar; + } + + void ATADrive::Initialize() + { + bus->SelectDrive(driveid); + CPU::OutPortB(iobase + COMMAND, CTL_NO_INTERRUPT); + } +} + diff --git a/sortix/ata.h b/sortix/ata.h new file mode 100644 index 00000000..3357ae49 --- /dev/null +++ b/sortix/ata.h @@ -0,0 +1,88 @@ +/****************************************************************************** + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + ata.cpp + Allowes access to block devices over ATA PIO. + +******************************************************************************/ + +#ifndef SORTIX_ATA_H +#define SORTIX_ATA_H + +namespace Sortix +{ + class ATABus; + class ATADrive; + + class ATABus + { + public: + ATABus(uint16_t portoffset, uint16_t altport); + ~ATABus(); + + public: + ATADrive* Instatiate(unsigned driveid); + bool SelectDrive(unsigned driveid); + + private: + unsigned curdriveid; + uint16_t iobase; + uint16_t altport; + + }; + + class ATADrive + { + public: + off_t GetSectorSize(); + off_t GetNumSectors(); + off_t GetSize() { return GetSectorSize() * GetNumSectors(); } + bool ReadSector(off_t sector, uint8_t* dest); + bool WriteSector(off_t sector, const uint8_t* src); + size_t Read(off_t byteoffset, uint8_t* dest, size_t numbytes); + size_t Write(off_t byteoffset, const uint8_t* src, size_t numbytes); + + public: + ATADrive(ATABus* bus, unsigned driveid, uint16_t portoffset, uint16_t altport); + ~ATADrive(); + + private: + void Initialize(); + bool PrepareIO(bool write, off_t sector); + + private: + unsigned driveid; + uint16_t meta[256]; + uint16_t iobase; + uint16_t altport; + ATABus* bus; + bool lba48; + size_t sectorsize; + off_t numsectors; + + }; + + namespace ATA + { + void Init(); + ATABus* CreateBus(uint16_t portoffset, uint16_t altport); + } +} + +#endif diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 95914283..23fa416c 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -204,12 +204,7 @@ namespace Sortix initrdsize = 0x280000; // 2 MiB 512 KiB #endif - Memory::RegisterInitRDSize(initrdsize); - -#ifndef JSSORTIX - // Search for PCI devices and load their drivers. - PCI::Init(); -#endif + Memory::RegisterInitRDSize(initrdsize);; // Initialize the paging and virtual memory. Memory::Init(BootInfo); @@ -262,6 +257,11 @@ namespace Sortix // Set up the initial ram disk. InitRD::Init(initrd, initrdsize); +#ifndef JSSORTIX + // Search for PCI devices and load their drivers. + PCI::Init(); +#endif + // Alright, now the system's drivers are loaded and initialized. It is // time to load the initial user-space programs and start execution of // the actual operating system. diff --git a/sortix/pci.cpp b/sortix/pci.cpp index 527cca2f..91315dd9 100644 --- a/sortix/pci.cpp +++ b/sortix/pci.cpp @@ -23,8 +23,12 @@ ******************************************************************************/ #include "platform.h" +#include #include "pci.h" #include "log.h" +#include "ata.h" + +using namespace Maxsi; namespace Sortix { @@ -231,6 +235,7 @@ namespace Sortix void Init() { + ATA::Init(); #if 0 Log::Print("PCI Devices: ");