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: ");