diff --git a/kernel/Makefile b/kernel/Makefile index a1b142c2..901dbbe7 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,7 +6,6 @@ include ../build-aux/dirs.mak # Default values in case the user doesn't override these variables. OPTLEVEL?=$(DEFAULT_OPTLEVEL) -DISKWRITE?=1 CPPFLAGS?= CXXFLAGS?=$(OPTLEVEL) @@ -19,7 +18,6 @@ CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -ffreestanding -fbuiltin -std=gnu++11 \ ifeq ($(PANIC_SHORT),1) CPPFLAGS:=$(CPPFLAGS) -DPANIC_SHORT endif -CPPFLAGS:=$(CPPFLAGS) -DENABLE_DISKWRITE=$(DISKWRITE) ifdef VERSION CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\" endif @@ -81,7 +79,6 @@ OBJS=\ $(CPUOBJS) \ addralloc.o \ alarm.o \ -ata.o \ clock.o \ com.o \ copy.o \ @@ -89,6 +86,10 @@ $(CPUDIR)/kthread.o \ crc32.o \ debugger.o \ descriptor.o \ +disk/ata/ata.o \ +disk/ata/hba.o \ +disk/ata/port.o \ +disk/node.o \ dtable.o \ elf.o \ fcache.o \ diff --git a/kernel/ata.cpp b/kernel/ata.cpp deleted file mode 100644 index efa079f2..00000000 --- a/kernel/ata.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. - - 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 -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ata.h" - -// TODO: Use the PCI to detect ATA devices instead of relying on them being on -// standard locations. - -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 { - -class ATANode : public AbstractInode -{ -public: - ATANode(ATADrive* drive, uid_t owner, gid_t group, mode_t mode, dev_t dev, - ino_t ino); - virtual ~ATANode(); - virtual int sync(ioctx_t* ctx); - virtual int truncate(ioctx_t* ctx, off_t length); - virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); - virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, - off_t off); - virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, - off_t off); - -private: - kthread_mutex_t filelock; - ATADrive* drive; - -}; - -ATANode::ATANode(ATADrive* drive, uid_t owner, gid_t group, mode_t mode, - dev_t dev, ino_t /*ino*/) -{ - inode_type = INODE_TYPE_FILE; - filelock = KTHREAD_MUTEX_INITIALIZER; - this->dev = dev; - this->ino = (ino_t) this; - this->stat_uid = owner; - this->stat_gid = group; - this->type = S_IFBLK; - this->stat_size = (off_t) drive->GetSize(); - this->stat_mode = (mode & S_SETABLE) | this->type; - this->stat_blksize = (blksize_t) drive->GetSectorSize(); - this->stat_blocks = (blkcnt_t) drive->GetNumSectors(); - this->drive = drive; -} - -ATANode::~ATANode() -{ - delete drive; -} - -int ATANode::sync(ioctx_t* /*ctx*/) -{ - // TODO: Actually sync the device here! - return 0; -} - -int ATANode::truncate(ioctx_t* /*ctx*/, off_t length) -{ - ScopedLock lock(&filelock); - if ( length == drive->GetSize() ) { return 0; } - errno = EPERM; - return -1; -} - -off_t ATANode::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) -{ - ScopedLock lock(&filelock); - if ( whence == SEEK_SET ) - return offset; - if ( whence == SEEK_END ) - return (off_t) drive->GetSize() + offset; - errno = EINVAL; - return -1; -} - -ssize_t ATANode::pread(ioctx_t* /*ctx*/, uint8_t* buf, size_t count, off_t off) -{ - // TODO: SECURITY: Use ioctx copy functions to copy or we have a serious - // security hole if invoked from user-space! - size_t numbytes = drive->Read(off, buf, count); - if ( numbytes < count ) - return -1; - return (ssize_t) numbytes; -} - -ssize_t ATANode::pwrite(ioctx_t* /*ctx*/, const uint8_t* buf, size_t count, - off_t off) -{ - // TODO: SECURITY: Use ioctx copy functions to copy or we have a serious - // security hole if invoked from user-space! - size_t numbytes = drive->Write(off, buf, count); - if ( numbytes < count ) - return -1; - return (ssize_t) numbytes; -} - -void DetectDrive(const char* devpath, Ref slashdev, unsigned busid, - ATABus* bus, unsigned driveid) -{ - unsigned ataid = busid*2 + driveid; - ATADrive* drive = bus->Instatiate(driveid); - if ( !drive ) - return; - Ref node(new ATANode(drive, 0, 0, 0660, slashdev->dev, 0)); - if ( !node ) - Panic("Unable to allocate memory for ATA drive inode."); - const size_t NAMELEN = 64; - char name[NAMELEN]; - snprintf(name, NAMELEN, "ata%u", ataid); - ioctx_t ctx; SetupKernelIOCtx(&ctx); - if ( LinkInodeInDir(&ctx, slashdev, name, node) != 0 ) - PanicF("Unable to link %s/%s to ATA driver inode.", devpath, name); -} - -void DetectBus(const char* devpath, Ref slashdev, unsigned busid, - uint16_t ioport, uint16_t altio) -{ - ATABus* bus = ATA::CreateBus(ioport, altio); - if ( !bus ) - return; - DetectDrive(devpath, slashdev, busid, bus, 0); - DetectDrive(devpath, slashdev, busid, bus, 1); -} - -void Init(const char* devpath, Ref slashdev) -{ - DetectBus(devpath, slashdev, 0, 0x1F0, 0x3F6); - DetectBus(devpath, slashdev, 1, 0x170, 0x366); -} - -ATABus* CreateBus(uint16_t portoffset, uint16_t altport) -{ - unsigned status = inport8(portoffset + STATUS); - // Detect if there is no such bus. - if ( status == 0xFF ) - { - errno = ENODEV; - return NULL; - } - return new ATABus(portoffset, altport); -} - -} // namespace ATA - -void Wait400NSecs(uint16_t iobase) -{ - // Now wait 400 ns for the drive to be ready. - for ( unsigned i = 0; i < 4; i++ ) { inport8(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 ) - return errno = EINVAL, (ATADrive*) NULL; - curdriveid = 0; - - uint8_t drivemagic = 0xA0 | (driveid << 4); - outport8(iobase + DRIVE_SELECT, drivemagic); - outport8(iobase + SECTOR_COUNT, 0); - outport8(iobase + LBA_LOW, 0); - outport8(iobase + LBA_MID, 0); - outport8(iobase + LBA_HIGH, 0); - outport8(iobase + COMMAND, CMD_IDENTIFY); - uint8_t status; - while ( true ) - { - status = inport8(iobase + STATUS); - if ( !status || status == 0xFF ) - return errno = ENODEV, (ATADrive*) NULL; - if ( !(status & STATUS_BUSY) ) - break; - } - // Check for ATAPI device not following spec. - if ( inport8(iobase + LBA_MID) || inport8(iobase + LBA_MID) ) - return errno = ENODEV, (ATADrive*) NULL; - while ( (status & STATUS_BUSY) || (!(status & STATUS_DATAREADY) && !(status & STATUS_ERROR)) ) - status = inport8(iobase + STATUS); - if ( status & STATUS_ERROR ) - { - unsigned mid = inport8(iobase + LBA_MID); - unsigned high = inport8(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"); - } - return errno = EIO, (ATADrive*) NULL; - } - ATADrive* drive = new ATADrive(this, driveid, iobase, altport); - return drive; -} - -bool ATABus::SelectDrive(unsigned driveid) -{ - if ( driveid == curdriveid ) { return true; } - if ( 1 < driveid ) { errno = EINVAL; return false; } - - uint8_t drivemagic = 0xA0 | (driveid << 4); - outport8(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->atalock = KTHREAD_MUTEX_INITIALIZER; - this->bus = bus; - this->driveid = driveid; - this->iobase = portoffset; - this->altport = altport; - for ( size_t i = 0; i < 256; i++ ) - meta[i] = inport16(iobase + DATA); - lba48 = meta[META_FLAGS] & FLAG_LBA48; - numsectors = 0; - if ( lba48 ) - { - numsectors = (uint64_t) meta[META_LBA48 + 0] << 0 - | (uint64_t) meta[META_LBA48 + 1] << 16 - | (uint64_t) meta[META_LBA48 + 2] << 32 - | (uint64_t) meta[META_LBA48 + 3] << 48; - } - else - { - numsectors = meta[META_LBA28 + 0] << 0 - | meta[META_LBA28 + 1] << 16; - } - 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 ) { errno = EINVAL; return false; } - if ( write && !ENABLE_DISKWRITE ) - { - errno = EPERM; - return false; - } - bus->SelectDrive(driveid); - uint8_t mode = (lba48) ? 0x40 : 0xE0; - mode |= driveid << 4; - mode |= (lba48) ? 0 : (sector >> 24) & 0x0F; - outport8(iobase + DRIVE_SELECT, mode); - uint16_t sectorcount = 1; - uint8_t sectorcountlow = sectorcount & 0xFF; - uint8_t sectorcounthigh = (sectorcount >> 8) & 0xFF; - if ( lba48 ) - { - outport8(iobase + SECTOR_COUNT, sectorcounthigh); - outport8(iobase + LBA_LOW, (sector >> 24) & 0xFF); - outport8(iobase + LBA_MID, (sector >> 32) & 0xFF); - outport8(iobase + LBA_HIGH, (sector >> 40) & 0xFF); - } - outport8(iobase + SECTOR_COUNT, sectorcountlow); - outport8(iobase + LBA_LOW, sector & 0xFF); - outport8(iobase + LBA_MID, (sector >> 8) & 0xFF); - outport8(iobase + LBA_HIGH, (sector >> 16) & 0xFF); - uint8_t command = (write) ? CMD_WRITE : CMD_READ; - if ( lba48 ) { command = (write) ? CMD_WRITE_EXT : CMD_READ_EXT; } - outport8(iobase + COMMAND, command); - while ( true ) - { - uint8_t status = inport8(iobase + STATUS); - if ( status & STATUS_BUSY ) { continue; } - if ( status & STATUS_DATAREADY ) { break; } - if ( status & STATUS_ERROR ) { errno = EIO; return false; } - if ( status & STATUS_DRIVEFAULT ) { errno = EIO; return false; } - } - return true; -} - -bool ATADrive::ReadSector(off_t sector, uint8_t* dest) -{ - ScopedLock lock(&atalock); - if ( !PrepareIO(false, sector) ) { return false; } - uint16_t* destword = (uint16_t*) dest; - for ( size_t i = 0; i < sectorsize/2; i++ ) - { - destword[i] = inport16(iobase + DATA); - } - Wait400NSecs(iobase); - inport8(iobase + STATUS); - return true; -} - -bool ATADrive::WriteSector(off_t sector, const uint8_t* src) -{ - ScopedLock lock(&atalock); - if ( !PrepareIO(true, sector) ) { return false; } - const uint16_t* srcword = (const uint16_t*) src; - for ( size_t i = 0; i < sectorsize/2; i++ ) - { - outport16(iobase + DATA, srcword[i]); - } - Wait400NSecs(iobase); - outport8(iobase + COMMAND, CMD_FLUSH_CACHE); - while ( true ) - { - uint8_t status = inport8(iobase + STATUS); - if ( status & STATUS_ERROR ) { errno = EIO; return false; } - if ( status & STATUS_DRIVEFAULT ) { errno = 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; } - memcpy(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; } - memcpy(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); - outport8(iobase + COMMAND, CTL_NO_INTERRUPT); -} - -} // namespace Sortix diff --git a/kernel/ata.h b/kernel/ata.h deleted file mode 100644 index 2363030e..00000000 --- a/kernel/ata.h +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - 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.h - Allowes access to block devices over ATA PIO. - -*******************************************************************************/ - -#ifndef SORTIX_ATA_H -#define SORTIX_ATA_H - -#include -#include - -namespace Sortix { - -class Descriptor; -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: - kthread_mutex_t atalock; - 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(const char* devpath, Ref slashdev); -ATABus* CreateBus(uint16_t portoffset, uint16_t altport); - -} // namespace ATA - -} // namespace Sortix - -#endif diff --git a/kernel/disk/ata/ata.cpp b/kernel/disk/ata/ata.cpp new file mode 100644 index 00000000..79905eb8 --- /dev/null +++ b/kernel/disk/ata/ata.cpp @@ -0,0 +1,66 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/ata/ata.cpp + Driver for ATA. + +*******************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "ata.h" +#include "hba.h" + +namespace Sortix { +namespace ATA { + +static void InitializeDevice(Ref dev, const char* devpath, + uint32_t devaddr) +{ + HBA* hba = new HBA(devaddr); + if ( !hba ) + PanicF("Failed to allocate ATA driver for PCI device 0x%X", devaddr); + + if ( !hba->Initialize(dev, devpath) ) + return delete hba; +} + + +void Init(const char* devpath, Ref dev) +{ + uint32_t devaddr; + pcifind_t filter; + + memset(&filter, 255, sizeof(filter)); + filter.classid = 0x01; + filter.subclassid = 0x01; + devaddr = 0; + while ( (devaddr = PCI::SearchForDevices(filter, devaddr)) ) + InitializeDevice(dev, devpath, devaddr); +} + +} // namespace ATA +} // namespace Sortix diff --git a/kernel/disk/ata/ata.h b/kernel/disk/ata/ata.h new file mode 100644 index 00000000..95044641 --- /dev/null +++ b/kernel/disk/ata/ata.h @@ -0,0 +1,39 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2015. + + 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 . + + disk/ata/ata.h + Driver for ATA. + +*******************************************************************************/ + +#ifndef SORTIX_DISK_ATA_ATA_H +#define SORTIX_DISK_ATA_ATA_H + +#include +#include + +namespace Sortix { +namespace ATA { + +void Init(const char* devpath, Ref dev); + +} // namespace ATA +} // namespace Sortix + +#endif diff --git a/kernel/disk/ata/hba.cpp b/kernel/disk/ata/hba.cpp new file mode 100644 index 00000000..fc39a78c --- /dev/null +++ b/kernel/disk/ata/hba.cpp @@ -0,0 +1,337 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/ata/hba.cpp + Driver for ATA. + +*******************************************************************************/ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../node.h" + +#include "hba.h" +#include "port.h" +#include "registers.h" + +namespace Sortix { +namespace ATA { + +static unsigned long AllocateDiskNumber() +{ + static unsigned long next_disk_number = 0; + return InterlockedIncrement(&next_disk_number).o; +} + +static void sleep_400_nanoseconds() +{ + struct timespec delay = timespec_make(0, 400); + Clock* clock = Time::GetClock(CLOCK_BOOT); + clock->SleepDelay(delay); +} + +Channel::Channel() +{ + hw_lock = KTHREAD_MUTEX_INITIALIZER; + interrupt_registered = false; + drives[0] = NULL; + drives[1] = NULL; +} + +Channel::~Channel() +{ + if ( interrupt_registered ) + Interrupt::UnregisterHandler(interrupt_index, &interrupt_registration); + // TODO: Destroy all the ports? +} + +void Channel::LogF(const char* format, ...) +{ + // TODO: Print this line in an atomic manner. + const char* cdesc = channel_index == 0 ? "primary" : "secondary"; + Log::PrintF("ata: pci 0x%X: %s channel: ", devaddr, cdesc); + va_list ap; + va_start(ap, format); + Log::PrintFV(format, ap); + va_end(ap); + Log::PrintF("\n"); +} + +void Channel::SelectDrive(unsigned int drive_index) // hw_lock locked +{ + if ( current_drive == drive_index ) + return; + +#if 0 + // TODO: Perhaps not do this here. This appears to time out on boot on real + // hardware where there is no drive there. + if ( !wait_inport8_clear(port_base + REG_STATUS, + STATUS_BUSY | STATUS_DATAREADY, false, 10000 /*ms*/) ) + { + LogF("error: timed out waiting for idle"); + // TODO: This can fail! + //return errno = EIO, false; + return; + } +#endif + + uint8_t value = 0xA0 | (drive_index << 4); + outport8(port_base + REG_DRIVE_SELECT, value); + //outport8(port_control, value); // TODO: Or is it port_control we use? + + sleep_400_nanoseconds(); + + // TODO: Do we need to wait for non-busy now? Can this operation fail? + + current_drive = drive_index; +} + +void Channel::OnInterrupt() +{ + // Check whether the interrupt came from this channel. + uint8_t status = inport8(busmaster_base + BUSMASTER_REG_STATUS); + if ( !(status & BUSMASTER_STATUS_INTERRUPT_PENDING) ) + return; + + if ( status & BUSMASTER_STATUS_DMA_FAILURE ) + { + // TODO: What do we do here? + } + + // Clear interrupt flag. + // TODO: This filters away BUSMASTER_STATUS_DMA and + // BUSMASTER_STATUS_DMA_FAILURE and might be a bug? + //status &= 0xF8 | BUSMASTER_STATUS_DMA; + status |= BUSMASTER_STATUS_INTERRUPT_PENDING; + outport8(busmaster_base + BUSMASTER_REG_STATUS, status); + + if ( current_drive < 2 && drives[current_drive] ) + drives[current_drive]->OnInterrupt(); +} + +void Channel__OnInterrupt(struct interrupt_context*, void* context) +{ + ((Channel*) context)->OnInterrupt(); +} + +static +void FixDefaultDeviceBars(pcibar_t* basebar, pcibar_t* ctrlbar, uint8_t* irq, + unsigned int channel_index, uint8_t interface) +{ + bool compatibility = interface == 0x00 || interface == 0x02; + + if ( compatibility ) + *irq = channel_index == 0 ? 14 : 15; + + if ( compatibility || + basebar->addr_raw == 0 || + (basebar->is_iospace() && !ctrlbar->ioaddr()) ) + { + uint16_t ioport = channel_index == 0 ? 0x1F0 : 0x170; + basebar->addr_raw = ioport | PCIBAR_TYPE_IOSPACE; + basebar->size_raw = 8; + } + + if ( compatibility || + ctrlbar->addr_raw == 0 || + (ctrlbar->is_iospace() && !ctrlbar->ioaddr()) ) + { + uint16_t ioport = channel_index == 0 ? 0x3F4 : 0x374; + ctrlbar->addr_raw = ioport | PCIBAR_TYPE_IOSPACE; + ctrlbar->size_raw = 4; // TODO: This is just a guess. + } +} + +bool Channel::Initialize(Ref dev, const char* devpath) +{ + uint8_t prog_if = PCI::Read8(devaddr, PCIFIELD_PROG_IF); + uint8_t interface = (prog_if >> (channel_index * 2)) & 0x3; + pcibar_t basebar = PCI::GetBAR(devaddr, 2 * channel_index + 0); + pcibar_t ctrlbar = PCI::GetBAR(devaddr, 2 * channel_index + 1); + pcibar_t busmasterbar = PCI::GetBAR(devaddr, 4); + uint8_t irq = PCI::Read8(devaddr, PCIFIELD_INTERRUPT_LINE); + FixDefaultDeviceBars(&basebar, &ctrlbar, &irq, channel_index, interface); + + if ( !basebar.is_iospace() ) + { + LogF("ignoring: non-iospace base BAR"); + return errno = EINVAL, false; + } + + if ( basebar.size() < 8 ) + { + LogF("ignoring: too small base BAR"); + return errno = EINVAL, false; + } + + if ( !ctrlbar.is_iospace() ) + { + LogF("ignoring: non-iospace control BAR"); + return errno = EINVAL, false; + } + + if ( ctrlbar.size() < 4 ) + { + LogF("ignoring: too small control BAR"); + return errno = EINVAL, false; + } + + if ( !busmasterbar.is_iospace() ) + { + LogF("ignoring: non-iospace bus master BAR"); + return errno = EINVAL, false; + } + + if ( busmasterbar.size() < 16 ) + { + LogF("ignoring: too small bus master BAR"); + return errno = EINVAL, false; + } + + port_base = basebar.addr(); + if ( inport8(port_base + REG_STATUS) == 0xFF ) + return errno = ENODEV, false; // Non-existent. + + // TODO: Ensure this is the correct logic. + port_control = ctrlbar.addr() + 2; + + busmaster_base = busmasterbar.addr() + 8 * channel_index; + + current_drive = (unsigned int) -1; // We don't know. + + for ( unsigned int i = 0; i < 2; i++ ) + { + drives[i] = NULL; + + ScopedLock lock(&hw_lock); + + SelectDrive(i); + + // TODO: May we do this before sending an IDENTITY command? + uint8_t status = inport8(port_base + REG_STATUS); + if ( status == 0 ) + continue; // Non-existent. + + const char* name = i == 0 ? "master" : "slave"; + + drives[i] = new Port(this, i); + if ( !drives[i] ) + { + LogF("error: failed to allocate %s drive", name); + continue; + } + + if ( !drives[i]->Initialize() ) + { + delete drives[i]; + drives[i] = NULL; + continue; + } + } + + interrupt_index = Interrupt::IRQ0 + irq; + + interrupt_registration.handler = Channel__OnInterrupt; + interrupt_registration.context = this; + Interrupt::RegisterHandler(interrupt_index, &interrupt_registration); + interrupt_registered = true; + + for ( unsigned int i = 0; i < 2; i++ ) + { + if ( !drives[i] ) + continue; + if ( !drives[i]->FinishInitialize() ) + { + // TODO: Gracefully destroy the drive here? + // TODO: Unsafe with respect to interrupt handler. + delete drives[i]; + drives[i] = NULL; + continue; + } + } + + for ( unsigned int i = 0; i < 2; i++ ) + { + if ( !drives[i] ) + continue; + unsigned long number = AllocateDiskNumber(); + char name[3 + sizeof(unsigned long) * 3]; + snprintf(name, sizeof(name), "ata%lu", number); + Ref node(new PortNode(drives[i], 0, 0, 0660, dev->dev, 0)); + if ( !node ) + PanicF("Unable to allocate memory for %s/%s inode", devpath, name); + ioctx_t ctx; SetupKernelIOCtx(&ctx); + if ( LinkInodeInDir(&ctx, dev, name, node) != 0 ) + PanicF("Unable to link %s/%s to ATA driver inode", devpath, name); + } + + return true; +} + +HBA::HBA(uint32_t devaddr) +{ + this->devaddr = devaddr; +} + +HBA::~HBA() +{ +} + +bool HBA::InitializeChannel(Ref dev, const char* devpath, + unsigned int channel_index) +{ + channels[channel_index].devaddr = devaddr; + channels[channel_index].hba = this; + channels[channel_index].channel_index = channel_index; + return channels[channel_index].Initialize(dev, devpath); +} + +bool HBA::Initialize(Ref dev, const char* devpath) +{ + uint16_t dev_pci_command_cur = PCI::Read16(devaddr, PCIFIELD_COMMAND); + uint16_t dev_pci_command_new = dev_pci_command_cur; + dev_pci_command_new &= ~PCIFIELD_COMMAND_INTERRUPT_DISABLE; + dev_pci_command_new |= PCIFIELD_COMMAND_IO_SPACE; + dev_pci_command_new |= PCIFIELD_COMMAND_BUS_MASTER; + if ( dev_pci_command_cur != dev_pci_command_new ) + PCI::Write16(devaddr, PCIFIELD_COMMAND, dev_pci_command_new); + + InitializeChannel(dev, devpath, 0); + InitializeChannel(dev, devpath, 1); + + return true; +} + +} // namespace ATA +} // namespace Sortix diff --git a/kernel/disk/ata/hba.h b/kernel/disk/ata/hba.h new file mode 100644 index 00000000..42f32426 --- /dev/null +++ b/kernel/disk/ata/hba.h @@ -0,0 +1,98 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/ata/hba.h + Driver for ATA. + +*******************************************************************************/ + +#ifndef SORTIX_DISK_ATA_HBA_H +#define SORTIX_DISK_ATA_HBA_H + +#include + +#include +#include +#include + +namespace Sortix { +namespace ATA { + +class HBA; +class Port; + +class Channel +{ + friend class Port; + friend class HBA; + +public: + Channel(); + ~Channel(); + +public: + bool Initialize(Ref dev, const char* devpath); + void OnInterrupt(); + +private: + __attribute__((format(printf, 2, 3))) + void LogF(const char* format, ...); + void SelectDrive(unsigned int drive_index); + +private: + struct interrupt_handler interrupt_registration; + kthread_mutex_t hw_lock; + uint32_t devaddr; + HBA* hba; + unsigned int channel_index; + unsigned int current_drive; + uint16_t port_base; + uint16_t port_control; + uint16_t busmaster_base; + Port* drives[2]; + unsigned int interrupt_index; + bool interrupt_registered; + +}; + +class HBA +{ + friend class Port; + +public: + HBA(uint32_t devaddr); + ~HBA(); + +public: + bool Initialize(Ref dev, const char* devpath); + +private: + bool InitializeChannel(Ref dev, const char* devpath, + unsigned int channel_index); + +private: + uint32_t devaddr; + Channel channels[2]; + +}; + +} // namespace ATA +} // namespace Sortix + +#endif diff --git a/kernel/disk/ata/port.cpp b/kernel/disk/ata/port.cpp new file mode 100644 index 00000000..5379fa1a --- /dev/null +++ b/kernel/disk/ata/port.cpp @@ -0,0 +1,784 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/ata/port.cpp + Driver for ATA. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hba.h" +#include "port.h" +#include "registers.h" + +namespace Sortix { +namespace ATA { + +static void copy_ata_string(char* dest, const char* src, size_t length) +{ + for ( size_t i = 0; i < length; i += 2 ) + { + dest[i + 0] = src[i + 1]; + dest[i + 1] = src[i + 0]; + } + length = strnlen(dest, length); + while ( 0 < length && dest[length - 1] == ' ' ) + length--; + dest[length] = '\0'; +} + +static void sleep_400_nanoseconds() +{ + struct timespec delay = timespec_make(0, 400); + Clock* clock = Time::GetClock(CLOCK_BOOT); + clock->SleepDelay(delay); +} + +Port::Port(Channel* channel, unsigned int port_index) +{ + this->channel = channel; + this->port_index = port_index; + memset(&control_alloc, 0, sizeof(control_alloc)); + memset(&dma_alloc, 0, sizeof(dma_alloc)); + is_control_page_mapped = false; + is_dma_page_mapped = false; + interrupt_signaled = false; + transfer_in_progress = false; + control_physical_frame = 0; + dma_physical_frame = 0; +} + +Port::~Port() +{ + if ( transfer_in_progress ) + FinishTransferDMA(); + if ( is_control_page_mapped ) + { + Memory::Unmap(control_alloc.from); + Memory::Flush(); + } + if ( is_dma_page_mapped ) + { + Memory::Unmap(dma_alloc.from); + Memory::Flush(); + } + FreeKernelAddress(&control_alloc); + FreeKernelAddress(&dma_alloc); + if ( control_physical_frame ) + Page::Put(control_physical_frame, PAGE_USAGE_DRIVER); + if ( dma_physical_frame ) + Page::Put(dma_physical_frame, PAGE_USAGE_DRIVER); +} + +void Port::LogF(const char* format, ...) +{ + // TODO: Print this line in an atomic manner. + const char* cdesc = channel->channel_index == 0 ? "primary" : "secondary"; + const char* ddesc = port_index == 0 ? "master" : "slave"; + Log::PrintF("ata: pci 0x%X: %s %s: ", channel->devaddr, cdesc, ddesc); + va_list ap; + va_start(ap, format); + Log::PrintFV(format, ap); + va_end(ap); + Log::PrintF("\n"); +} + +bool Port::Initialize() +{ + if ( !(control_physical_frame = Page::Get32Bit(PAGE_USAGE_DRIVER)) ) + { + LogF("error: control page allocation failure"); + return false; + } + + if ( !(dma_physical_frame = Page::Get32Bit(PAGE_USAGE_DRIVER)) ) + { + LogF("error: dma page allocation failure"); + return false; + } + + if ( !AllocateKernelAddress(&control_alloc, Page::Size()) ) + { + LogF("error: control page virtual address allocation failure"); + return false; + } + + if ( !AllocateKernelAddress(&dma_alloc, Page::Size()) ) + { + LogF("error: dma page virtual address allocation failure"); + return false; + } + + int prot = PROT_KREAD | PROT_KWRITE; + + is_control_page_mapped = + Memory::Map(control_physical_frame, control_alloc.from, prot); + if ( !is_control_page_mapped ) + { + LogF("error: control page virtual address allocation failure"); + return false; + } + + Memory::Flush(); + + is_dma_page_mapped = Memory::Map(dma_physical_frame, dma_alloc.from, prot); + if ( !is_dma_page_mapped ) + { + LogF("error: dma page virtual address allocation failure"); + return false; + } + + Memory::Flush(); + + prdt = (volatile struct prd*) (control_alloc.from); + + return true; +} + +bool Port::FinishInitialize() +{ + ScopedLock lock(&channel->hw_lock); + + channel->SelectDrive(port_index); + + outport8(channel->port_base + REG_COMMAND, CMD_IDENTIFY); + + sleep_400_nanoseconds(); + + // TODO: The status polling logic should be double-checked against some + // formal specification telling how this should properly be done. + + uint8_t status = inport8(channel->port_base + REG_STATUS); + if ( status == 0 ) + { + // Non-existent. + return errno = ENODEV, false; + } + + // TODO: This failing might mean non-existent. + // TODO: What is a good timeout here? + if ( !wait_inport8_clear(channel->port_base + REG_STATUS, STATUS_BUSY, + false, 1000 /*ms*/) ) + { + // IDENTIFY timed out, still busy. + return errno = ETIMEDOUT, false; + } + + // TODO: This failing might mean non-existent. + // TODO: Should we actually wait here, or are the status set already? + // TODO: What is a good timeout here? + if ( !wait_inport8_set(channel->port_base + REG_STATUS, + STATUS_DATAREADY | STATUS_ERROR, true, 1000 /*ms*/) ) + { + // IDENTIFY timed out, status not set. + return errno = ETIMEDOUT, false; + } + + status = inport8(channel->port_base + REG_STATUS); + if ( status & STATUS_ERROR ) + { + uint8_t mid = inport8(channel->port_base + REG_LBA_MID); + uint8_t high = inport8(channel->port_base + REG_LBA_HIGH); + + if ( (mid == 0x14 && high == 0xEB) || (mid == 0x69 && high == 0x96) ) + { + // TODO: Add ATAPI support. + //LogF("ignoring: found ATAPI device instead"); + return errno = ENODRV, false; + } + else if ( mid == 0x3C && high == 0xC3 ) + { + // TODO: Does this actually happen and can we do something? + //LogF("ignoring: found SATA device instead"); + return errno = ENODRV, false; + } + else if ( (mid || high) && (mid != 0x7F && high != 0x7F) ) + { + //LogF("ignoring: found unknown device instead (0x%02X:0x%02X)", + // mid, high); + return errno = ENODRV, false; + } + else if ( mid == 0x7F && high == 0x7F ) + { + //LogF("ignoring: non-existent"); + return errno = EIO, false; + } + else + { + LogF("ignoring: IDENTIFY returned error status"); + return errno = EIO, false; + } + } + + for ( size_t i = 0; i < 256; i++ ) + { + uint16_t value = inport16(channel->port_base + REG_DATA); + identify_data[2*i + 0] = value >> 0 & 0xFF; + identify_data[2*i + 1] = value >> 8 & 0xFF; + } + + little_uint16_t* words = (little_uint16_t*) identify_data; + + if ( words[0] & (1 << 15) ) + return errno = EINVAL, false; // Skipping non-ATA device. + if ( !(words[49] & (1 << 9)) ) + return errno = EINVAL, false; // Skipping non-LBA device. + + this->is_lba48 = words[83] & (1 << 10); + + copy_ata_string(serial, (const char*) &words[10], sizeof(serial) - 1); + copy_ata_string(revision, (const char*) &words[23], sizeof(revision) - 1); + copy_ata_string(model, (const char*) &words[27], sizeof(model) - 1); + + uint64_t block_count; + if ( is_lba48 ) + { + block_count = (uint64_t) words[100] << 0 | + (uint64_t) words[101] << 16 | + (uint64_t) words[102] << 32 | + (uint64_t) words[103] << 48; + } + else + { + block_count = (uint64_t) words[60] << 0 | + (uint64_t) words[61] << 16; + } + + uint64_t block_size = 512; + if( (words[106] & (1 << 14)) && + !(words[106] & (1 << 15)) && + (words[106] & (1 << 12)) ) + { + block_size = 2 * ((uint64_t) words[117] << 0 | + (uint64_t) words[118] << 16); + } + + // TODO: Verify the block size is a power of two. + + cylinder_count = words[1]; + head_count = words[3]; + sector_count = words[6]; + + is_using_dma = true; + + // This is apparently the case on older hardware, there are additional + // restrictions on DMA usage, we could work around it, but instead we just + // don't use DMA at all, as such hardware should be irrelevant by now. Print + // a warning so we can look into this if it actually turns out to matter. + // TODO: Arg. This happens in VirtualBox! + // TODO: Alternative solution. If this is true, then it should lock the + // HBA hw_lock instead than the channel one. + uint8_t bm_status = inport8(channel->busmaster_base + BUSMASTER_REG_STATUS); + if ( bm_status & BUSMASTER_STATUS_SIMPLEX ) + { + LogF("warning: simplex silliness: no DMA"); + is_using_dma = false; + } + + // TODO: Why is this commented out? Due to the above? +#if 0 + if ( port_index == 0 && !(bm_status & BUSMASTER_STATUS_MASTER_DMA_INIT) ) + { + LogF("warning: no DMA support"); + is_using_dma = false; + } + + if ( port_index == 1 && !(bm_status & BUSMASTER_STATUS_SLAVE_DMA_INIT) ) + { + LogF("warning: no DMA support"); + is_using_dma = false; + } +#endif + + if ( __builtin_mul_overflow(block_count, block_size, &this->device_size) ) + { + LogF("error: device size overflows off_t"); + return errno = EOVERFLOW, false; + } + + this->block_count = (blkcnt_t) block_count; + this->block_size = (blkcnt_t) block_size; + + return true; +} + +void Port::Seek(blkcnt_t block_index, size_t count) +{ + uintmax_t lba = (uintmax_t) block_index; + uint8_t mode = (is_lba48 ? 0x40 : 0xE0) | port_index << 4; + outport8(channel->port_base + REG_DRIVE_SELECT, mode); + if ( is_lba48 ) + { + outport8(channel->port_base + REG_SECTOR_COUNT, count >> 8 & 0xFF); + outport8(channel->port_base + REG_LBA_LOW, lba >> 24 & 0xFF); + outport8(channel->port_base + REG_LBA_MID, lba >> 32 & 0xFF); + outport8(channel->port_base + REG_LBA_HIGH, lba >> 40 & 0xFF); + } + outport8(channel->port_base + REG_SECTOR_COUNT, count >> 0 & 0xFF); + outport8(channel->port_base + REG_LBA_LOW, lba >> 0 & 0xFF); + outport8(channel->port_base + REG_LBA_MID, lba >> 8 & 0xFF); + outport8(channel->port_base + REG_LBA_HIGH, lba >> 16 & 0xFF); +} + +void Port::CommandDMA(uint8_t cmd, size_t size, bool write) +{ + assert(size); + assert(size <= Page::Size()); + assert(size <= UINT16_MAX); + assert((size & 1) == 0); /* sizes and addresses must be 2-byte aligned */ + + // Store the DMA region in the first PRD. + prdt->physical = dma_physical_frame >> 0 & 0xFFFFFFFF; + prdt->count = size; + prdt->flags = PRD_FLAG_EOT; + + // Tell the hardware the location of the PRDT. + uint32_t bm_prdt = control_physical_frame >> 0 & 0xFFFFFFFF; + outport32(channel->busmaster_base + BUSMASTER_REG_PDRT, bm_prdt); + + // Clear the error and interrupt bits. + uint8_t bm_status = inport8(channel->busmaster_base + BUSMASTER_REG_STATUS); + bm_status |= BUSMASTER_STATUS_DMA_FAILURE | BUSMASTER_STATUS_INTERRUPT_PENDING; + outport8(channel->busmaster_base + BUSMASTER_REG_STATUS, bm_status); + + // Set the transfer direction + uint8_t bm_command = inport8(channel->busmaster_base + BUSMASTER_REG_COMMAND); + if ( write ) + bm_command &= ~BUSMASTER_COMMAND_READING; + else + bm_command |= BUSMASTER_COMMAND_READING; + outport8(channel->busmaster_base + BUSMASTER_REG_COMMAND, bm_command); + + // Anticipate we will be delivered an IRQ. + PrepareAwaitInterrupt(); + transfer_in_progress = true; + transfer_size = size; + transfer_is_write = write; + + // Execute the command. + outport8(channel->port_base + REG_COMMAND, cmd); + + // Start the DMA transfer. + bm_command = inport8(channel->busmaster_base + BUSMASTER_REG_COMMAND); + bm_command |= BUSMASTER_COMMAND_START; + outport8(channel->busmaster_base + BUSMASTER_REG_COMMAND, bm_command); +} + +bool Port::FinishTransferDMA() +{ + assert(transfer_in_progress); + + bool result = true; + + // Wait for an interrupt to arrive. + if ( !AwaitInterrupt(10000 /*ms*/) ) + { + const char* op = transfer_is_write ? "write" : "read"; + LogF("error: %s DMA timed out", op); + errno = EIO; + result = false; + // TODO: Is this a consistent state, do we need a reset, how to recover? + } + + // Stop the DMA transfer. + uint8_t bm_command = inport8(channel->busmaster_base + BUSMASTER_REG_COMMAND); + bm_command &= ~BUSMASTER_COMMAND_START; + outport8(channel->busmaster_base + BUSMASTER_REG_COMMAND, bm_command); + + // Clear the error and interrupt bits. + uint8_t bm_status = inport8(channel->busmaster_base + BUSMASTER_REG_STATUS); + uint8_t bm_set_flags = BUSMASTER_STATUS_DMA_FAILURE | + BUSMASTER_STATUS_INTERRUPT_PENDING; + outport8(channel->busmaster_base + BUSMASTER_REG_STATUS, + bm_status | bm_set_flags); + + // Check if the DMA transfer failed. + if ( bm_status & BUSMASTER_STATUS_DMA_FAILURE ) + { + const char* op = transfer_is_write ? "write" : "read"; + LogF("error: %s DMA error", op); + errno = EIO; + result = false; + // TODO: Is this a consistent state, do we need a reset, how to recover? + } + + transfer_in_progress = false; + return result; +} + +void Port::CommandPIO(uint8_t cmd, size_t size, bool write) +{ + assert(size); + assert(size <= Page::Size()); + assert((size & 1) == 0); /* sizes and addresses must be 2-byte aligned */ + + // Anticipate we will be delivered an IRQ. + PrepareAwaitInterrupt(); + + // Execute the command. + (void) write; + outport8(channel->port_base + REG_COMMAND, cmd); +} + +bool Port::TransferPIO(size_t size, bool write) +{ + const char* op = write ? "write" : "read"; + size_t i = 0; + while ( write || true ) + { + if ( !write && i < size && !AwaitInterrupt(10000 /*ms*/) ) + { + LogF("error: %s timed out, waiting for transfer start", op); + return errno = EIO, false; + } + + if ( !wait_inport8_clear(channel->port_base + REG_STATUS, + STATUS_BUSY, false, 10000 /*ms*/) ) + { + LogF("error: %s timed out waiting for transfer pre idle", op); + return errno = EIO, false; + } + + uint8_t status = inport8(channel->port_base + REG_STATUS); + + if ( status & STATUS_BUSY ) + { + LogF("error: %s unexpectedly still busy", op); + return errno = EIO, false; + } + + if ( status & (STATUS_ERROR | STATUS_DRIVEFAULT) ) + { + LogF("error: %s error", op); + return errno = EIO, false; + } + + if ( i == size ) + break; + + if ( !(status & STATUS_DATAREADY) ) + { + LogF("error: %s unexpectedly not ready", op); + return errno = EIO, false; + } + + // Anticipate another IRQ if we're not at the end. + size_t i_sector_end = i + block_size; + if ( i_sector_end != size ) + PrepareAwaitInterrupt(); + + uint8_t* dma_data = (uint8_t*) dma_alloc.from; + + if ( write ) + { + while ( i < size && i < i_sector_end ) + { + uint16_t value = dma_data[i + 0] << 0 | dma_data[i + 1] << 8; + outport16(channel->port_base + REG_DATA, value); + i += 2; + } + } + else + { + while ( i < size && i < i_sector_end ) + { + uint16_t value = inport16(channel->port_base + REG_DATA); + dma_data[i + 0] = (value >> 0) & 0xFF; + dma_data[i + 1] = (value >> 8) & 0xFF; + i += 2; + } + } + + if ( write && !AwaitInterrupt(10000 /*ms*/) ) + { + LogF("error: %s timed out, waiting for transfer end", op); + return errno = EIO, false; + } + } + + return true; +} + +off_t Port::GetSize() +{ + return device_size; +} + +blkcnt_t Port::GetBlockCount() +{ + return block_count; +} + +blksize_t Port::GetBlockSize() +{ + return block_size; +} + +uint16_t Port::GetCylinderCount() +{ + return cylinder_count; +} + +uint16_t Port::GetHeadCount() +{ + return head_count; +} + +uint16_t Port::GetSectorCount() +{ + return sector_count; +} + +const char* Port::GetDriver() +{ + return "ata"; +} + +const char* Port::GetModel() +{ + return model; +} + +const char* Port::GetSerial() +{ + return serial; +} + +const char* Port::GetRevision() +{ + return revision; +} + +const unsigned char* Port::GetATAIdentify(size_t* size_ptr) +{ + return *size_ptr = sizeof(identify_data), identify_data; +} + +int Port::sync(ioctx_t* ctx) +{ + (void) ctx; + ScopedLock lock(&channel->hw_lock); + channel->SelectDrive(port_index); + if ( transfer_in_progress && !FinishTransferDMA() ) + return -1; + PrepareAwaitInterrupt(); + uint8_t cmd = is_lba48 ? CMD_FLUSH_CACHE_EXT : CMD_FLUSH_CACHE; + outport8(channel->port_base + REG_COMMAND, cmd); + // TODO: This might take longer than 30 seconds according to the spec. But + // how long? Let's say twice that? + if ( !AwaitInterrupt(2 * 30000 /*ms*/) ) + { + LogF("error: cache flush timed out"); + transfer_in_progress = false; + return errno = EIO, -1; + } + transfer_in_progress = false; + uint8_t status = inport8(channel->port_base + REG_STATUS); + if ( status & (STATUS_ERROR | STATUS_DRIVEFAULT) ) + { + LogF("error: IO error"); + return errno = EIO, -1; + } + return 0; +} + +ssize_t Port::pread(ioctx_t* ctx, unsigned char* buf, size_t count, off_t off) +{ + ssize_t result = 0; + while ( count ) + { + ScopedLock lock(&channel->hw_lock); + channel->SelectDrive(port_index); + if ( device_size <= off ) + break; + if ( (uintmax_t) device_size - off < (uintmax_t) count ) + count = (size_t) device_size - off; + uintmax_t block_index = (uintmax_t) off / (uintmax_t) block_size; + uintmax_t block_offset = (uintmax_t) off % (uintmax_t) block_size; + uintmax_t amount = block_offset + count; + if ( Page::Size() < amount ) + amount = Page::Size(); + size_t num_blocks = (amount + block_size - 1) / block_size; + uintmax_t full_amount = num_blocks * block_size; + // If an asynchronous operation is in progress, let it finish. + if ( transfer_in_progress && !FinishTransferDMA() ) + return result ? result : -1; + unsigned char* dma_data = (unsigned char*) dma_alloc.from; + unsigned char* data = dma_data + block_offset; + size_t data_size = amount - block_offset; + Seek(block_index, num_blocks); + if ( is_using_dma ) + { + uint8_t cmd = is_lba48 ? CMD_READ_DMA_EXT : CMD_READ_DMA; + CommandDMA(cmd, (size_t) full_amount, false); + if ( !FinishTransferDMA() ) + return result ? result : -1; + } + else + { + uint8_t cmd = is_lba48 ? CMD_READ_EXT : CMD_READ; + CommandPIO(cmd, (size_t) full_amount, false); + if ( !TransferPIO((size_t) full_amount, false) ) + return result ? result : -1; + } + if ( !ctx->copy_to_dest(buf, data, data_size) ) + return result ? result : -1; + buf += data_size; + count -= data_size; + result += data_size; + off += data_size; + } + return result; +} + +ssize_t Port::pwrite(ioctx_t* ctx, const unsigned char* buf, size_t count, off_t off) +{ + ssize_t result = 0; + while ( count ) + { + ScopedLock lock(&channel->hw_lock); + channel->SelectDrive(port_index); + if ( device_size <= off ) + break; + if ( (uintmax_t) device_size - off < (uintmax_t) count ) + count = (size_t) device_size - off; + uintmax_t block_index = (uintmax_t) off / (uintmax_t) block_size; + uintmax_t block_offset = (uintmax_t) off % (uintmax_t) block_size; + uintmax_t amount = block_offset + count; + if ( Page::Size() < amount ) + amount = Page::Size(); + size_t num_blocks = (amount + block_size - 1) / block_size; + uintmax_t full_amount = num_blocks * block_size; + // If an asynchronous operation is in progress, let it finish. + if ( transfer_in_progress && !FinishTransferDMA() ) + return result ? result : -1; + unsigned char* dma_data = (unsigned char*) dma_alloc.from; + unsigned char* data = dma_data + block_offset; + size_t data_size = amount - block_offset; + if ( block_offset || amount < full_amount ) + { + if ( is_using_dma ) + { + uint8_t cmd = is_lba48 ? CMD_READ_DMA_EXT : CMD_READ_DMA; + Seek(block_index, num_blocks); + CommandDMA(cmd, (size_t) full_amount, false); + if ( !FinishTransferDMA() ) + return result ? result : -1; + } + else + { + uint8_t cmd = is_lba48 ? CMD_READ_EXT : CMD_READ; + Seek(block_index, num_blocks); + CommandPIO(cmd, (size_t) full_amount, false); + if ( !TransferPIO((size_t) full_amount, false) ) + return result ? result : -1; + } + } + if ( !ctx->copy_from_src(data, buf, data_size) ) + return result ? result : -1; + if ( is_using_dma ) + { + Seek(block_index, num_blocks); + uint8_t cmd = is_lba48 ? CMD_WRITE_DMA_EXT : CMD_WRITE_DMA; + CommandDMA(cmd, (size_t) full_amount, true); + // Let the transfer finish asynchronously so the caller can prepare + // the next write operation to keep the write pipeline busy. + } + else + { + Seek(block_index, num_blocks); + uint8_t cmd = is_lba48 ? CMD_WRITE_EXT : CMD_WRITE; + CommandPIO(cmd, (size_t) full_amount, true); + if ( !TransferPIO((size_t) full_amount, true) ) + return result ? result : -1; + } + buf += data_size; + count -= data_size; + result += data_size; + off += data_size; + } + return result; +} + +void Port::PrepareAwaitInterrupt() +{ + interrupt_signaled = false; +} + +bool Port::AwaitInterrupt(unsigned int msecs) +{ + struct timespec timeout = timespec_make(msecs / 1000, (msecs % 1000) * 1000000L); + Clock* clock = Time::GetClock(CLOCK_BOOT); + struct timespec begun; + clock->Get(&begun, NULL); + while ( true ) + { + struct timespec now; + clock->Get(&now, NULL); + if ( interrupt_signaled ) + return true; + struct timespec elapsed = timespec_sub(now, begun); + if ( timespec_le(timeout, elapsed) ) + return errno = ETIMEDOUT, false; + // TODO: Can't safely back out here unless the pending operation is + // is properly cancelled. + //if ( Signal::IsPending() ) + // return errno = EINTR, false; + kthread_yield(); + } +} + +void Port::OnInterrupt() +{ + // Clear INTRQ. + uint8_t status = inport8(channel->port_base + REG_STATUS); + + if ( status & STATUS_ERROR ) + { + uint8_t error_status = inport8(channel->port_base + REG_ERROR); + (void) error_status; // TODO: How to handle this exactly? + } + + if ( !interrupt_signaled ) + { + interrupt_signaled = true; + // TODO: Priority schedule the blocking thread now. + } +} + +} // namespace ATA +} // namespace Sortix diff --git a/kernel/disk/ata/port.h b/kernel/disk/ata/port.h new file mode 100644 index 00000000..6287eddd --- /dev/null +++ b/kernel/disk/ata/port.h @@ -0,0 +1,114 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/ata/port.h + Driver for ATA. + +*******************************************************************************/ + +#ifndef SORTIX_DISK_ATA_PORT_H +#define SORTIX_DISK_ATA_PORT_H + +#include + +#include + +#include +#include +#include +#include + +namespace Sortix { +namespace ATA { + +class Channel; + +class Port : public Harddisk +{ + friend class Channel; + +public: + Port(Channel* channel, unsigned int port_index); + virtual ~Port(); + +public: + virtual off_t GetSize(); + virtual blkcnt_t GetBlockCount(); + virtual blksize_t GetBlockSize(); + virtual uint16_t GetCylinderCount(); + virtual uint16_t GetHeadCount(); + virtual uint16_t GetSectorCount(); + virtual const char* GetDriver(); + virtual const char* GetModel(); + virtual const char* GetSerial(); + virtual const char* GetRevision(); + virtual const unsigned char* GetATAIdentify(size_t* size_ptr); + virtual int sync(ioctx_t* ctx); + virtual ssize_t pread(ioctx_t* ctx, unsigned char* buf, size_t count, off_t off); + virtual ssize_t pwrite(ioctx_t* ctx, const unsigned char* buf, size_t count, off_t off); + +public: + bool Initialize(); + bool FinishInitialize(); + +private: + __attribute__((format(printf, 2, 3))) + void LogF(const char* format, ...); + void Seek(blkcnt_t block_index, size_t count); + void CommandDMA(uint8_t cmd, size_t size, bool write); + void CommandPIO(uint8_t cmd, size_t size, bool write); + bool FinishTransferDMA(); + bool TransferPIO(size_t size, bool write); + void PrepareAwaitInterrupt(); + bool AwaitInterrupt(unsigned int msescs); + void OnInterrupt(); + +private: + unsigned char identify_data[512]; + char serial[20 + 1]; + char revision[8 + 1]; + char model[40 + 1]; + addralloc_t control_alloc; + addralloc_t dma_alloc; + Channel* channel; + volatile struct prd* prdt; + addr_t control_physical_frame; + addr_t dma_physical_frame; + unsigned int port_index; + bool is_control_page_mapped; + bool is_dma_page_mapped; + bool is_lba48; + bool is_using_dma; + off_t device_size; + blksize_t block_count; + blkcnt_t block_size; + uint16_t cylinder_count; + uint16_t head_count; + uint16_t sector_count; + volatile bool interrupt_signaled; + bool transfer_in_progress; + size_t transfer_size; + bool transfer_is_write; + +}; + +} // namespace ATA +} // namespace Sortix + +#endif diff --git a/kernel/disk/ata/registers.h b/kernel/disk/ata/registers.h new file mode 100644 index 00000000..b37dbf00 --- /dev/null +++ b/kernel/disk/ata/registers.h @@ -0,0 +1,88 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/ata/registers.h + Driver for ATA. + +*******************************************************************************/ + +#ifndef SORTIX_DISK_ATA_REGISTERS_H +#define SORTIX_DISK_ATA_REGISTERS_H + +#include +#include + +namespace Sortix { +namespace ATA { + +struct prd +{ + little_uint32_t physical; + little_uint16_t count; + little_uint16_t flags; +}; + +static const uint16_t PRD_FLAG_EOT = 1 << 15; + +static const uint16_t REG_DATA = 0x0; +static const uint16_t REG_FEATURE = 0x1; +static const uint16_t REG_ERROR = 0x1; +static const uint16_t REG_SECTOR_COUNT = 0x2; +static const uint16_t REG_LBA_LOW = 0x3; +static const uint16_t REG_LBA_MID = 0x4; +static const uint16_t REG_LBA_HIGH = 0x5; +static const uint16_t REG_DRIVE_SELECT = 0x6; +static const uint16_t REG_COMMAND = 0x7; +static const uint16_t REG_STATUS = 0x7; + +static const uint8_t CMD_READ = 0x20; +static const uint8_t CMD_READ_EXT = 0x24; +static const uint8_t CMD_READ_DMA = 0xC8; +static const uint8_t CMD_READ_DMA_EXT = 0x25; +static const uint8_t CMD_WRITE = 0x30; +static const uint8_t CMD_WRITE_EXT = 0x34; +static const uint8_t CMD_WRITE_DMA = 0xCA; +static const uint8_t CMD_WRITE_DMA_EXT = 0x35; +static const uint8_t CMD_FLUSH_CACHE = 0xE7; +static const uint8_t CMD_FLUSH_CACHE_EXT = 0xEA; +static const uint8_t CMD_IDENTIFY = 0xEC; + +static const uint8_t STATUS_ERROR = 1 << 0; +static const uint8_t STATUS_DATAREADY = 1 << 3; +static const uint8_t STATUS_DRIVEFAULT = 1 << 5; +static const uint8_t STATUS_BUSY = 1 << 7; + +static const uint16_t BUSMASTER_REG_COMMAND = 0x0; +static const uint16_t BUSMASTER_REG_STATUS = 0x2; +static const uint16_t BUSMASTER_REG_PDRT = 0x4; + +static const uint16_t BUSMASTER_COMMAND_START = 1 << 0; +static const uint16_t BUSMASTER_COMMAND_READING = 1 << 3; + +static const uint8_t BUSMASTER_STATUS_DMA = 1 << 0; +static const uint8_t BUSMASTER_STATUS_DMA_FAILURE = 1 << 1; +static const uint8_t BUSMASTER_STATUS_INTERRUPT_PENDING = 1 << 2; +static const uint8_t BUSMASTER_STATUS_MASTER_DMA_INIT = 1 << 5; +static const uint8_t BUSMASTER_STATUS_SLAVE_DMA_INIT = 1 << 6; +static const uint8_t BUSMASTER_STATUS_SIMPLEX = 1 << 7; + +} // namespace ATA +} // namespace Sortix + +#endif diff --git a/kernel/disk/node.cpp b/kernel/disk/node.cpp new file mode 100644 index 00000000..928cbeaa --- /dev/null +++ b/kernel/disk/node.cpp @@ -0,0 +1,200 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016. + + 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 . + + disk/node.cpp + Inode adapter for harddisks. + +*******************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "node.h" + +namespace Sortix { + +PortNode::PortNode(Harddisk* harddisk, uid_t owner, gid_t group, mode_t mode, + dev_t dev, ino_t /*ino*/) +{ + this->harddisk = harddisk; + inode_type = INODE_TYPE_FILE; + this->dev = dev; + this->ino = (ino_t) this; + this->stat_uid = owner; + this->stat_gid = group; + this->type = S_IFBLK; + this->stat_size = harddisk->GetSize(); + this->stat_mode = (mode & S_SETABLE) | this->type; + this->stat_blksize = harddisk->GetBlockSize(); + this->stat_blocks = harddisk->GetBlockCount(); +} + +PortNode::~PortNode() +{ + // TODO: Ownership of `port'. +} + +int PortNode::sync(ioctx_t* ctx) +{ + return harddisk->sync(ctx); +} + +int PortNode::truncate(ioctx_t* /*ctx*/, off_t length) +{ + if ( length != harddisk->GetSize() ) + return errno = EPERM, -1; + return 0; +} + +off_t PortNode::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) +{ + if ( whence == SEEK_SET ) + return offset; + if ( whence == SEEK_END ) + { + off_t result; + if ( __builtin_add_overflow(harddisk->GetSize(), offset, &result) ) + return errno = EOVERFLOW, -1; + return result; + } + return errno = EINVAL, -1; +} + +ssize_t PortNode::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) +{ + return harddisk->pread(ctx, (unsigned char*) buf, count, off); +} + +ssize_t PortNode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off) +{ + return harddisk->pwrite(ctx, (const unsigned char*) buf, count, off); +} + +ssize_t PortNode::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, + size_t count) +{ + const void* result_pointer = NULL; + size_t result_size = 0; + char string[sizeof(uintmax_t) * 3]; + static const char index[] = "harddisk-driver\0" + "harddisk-model\0" + "harddisk-serial\0" + "harddisk-revision\0" + "harddisk-size\0" + "harddisk-block-count\0" + "harddisk-block-size\0" + "harddisk-cylinders\0" + "harddisk-heads\0" + "harddisk-sectors\0" + "harddisk-ata-identify\0"; + + if ( !name ) + { + result_pointer = index; + result_size = sizeof(index) - 1; + } + else if ( !strcmp(name, "harddisk-driver") ) + { + result_pointer = harddisk->GetDriver(); + result_size = strlen((const char*) result_pointer); + } + else if ( !strcmp(name, "harddisk-model") ) + { + result_pointer = harddisk->GetModel(); + result_size = strlen((const char*) result_pointer); + } + else if ( !strcmp(name, "harddisk-serial") ) + { + result_pointer = harddisk->GetSerial(); + result_size = strlen((const char*) result_pointer); + } + else if ( !strcmp(name, "harddisk-revision") ) + { + result_pointer = harddisk->GetRevision(); + result_size = strlen((const char*) result_pointer); + } + else if ( !strcmp(name, "harddisk-size") ) + { + snprintf(string, sizeof(string), "%" PRIiOFF, harddisk->GetSize()); + result_pointer = string; + result_size = strlen(string); + } + else if ( !strcmp(name, "harddisk-block-count") ) + { + snprintf(string, sizeof(string), "%" PRIiBLKCNT, harddisk->GetBlockCount()); + result_pointer = string; + result_size = strlen(string); + } + else if ( !strcmp(name, "harddisk-block-size") ) + { + snprintf(string, sizeof(string), "%" PRIiBLKSIZE, harddisk->GetBlockSize()); + result_pointer = string; + result_size = strlen(string); + } + else if ( !strcmp(name, "harddisk-cylinders") ) + { + snprintf(string, sizeof(string), "%" PRIu16, harddisk->GetCylinderCount()); + result_pointer = string; + result_size = strlen(string); + } + else if ( !strcmp(name, "harddisk-heads") ) + { + snprintf(string, sizeof(string), "%" PRIu16, harddisk->GetHeadCount()); + result_pointer = string; + result_size = strlen(string); + } + else if ( !strcmp(name, "harddisk-sectors") ) + { + snprintf(string, sizeof(string), "%" PRIu16, harddisk->GetSectorCount()); + result_pointer = string; + result_size = strlen(string); + } + else if ( !strcmp(name, "harddisk-ata-identify") ) + { + if ( !(result_pointer = harddisk->GetATAIdentify(&result_size)) ) + return -1; + } + else + { + return errno = ENOENT, -1; + } + + if ( SSIZE_MAX < result_size ) + return errno = EOVERFLOW, -1; + if ( buffer && count < result_size ) + return errno = ERANGE, -1; + if ( buffer && !ctx->copy_to_dest(buffer, result_pointer, result_size) ) + return -1; + return (ssize_t) result_size; +} + +} // namespace Sortix diff --git a/kernel/disk/node.h b/kernel/disk/node.h new file mode 100644 index 00000000..e9684067 --- /dev/null +++ b/kernel/disk/node.h @@ -0,0 +1,58 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015. + + 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 . + + disk/node.h + Inode adapter for harddisks. + +*******************************************************************************/ + +#ifndef SORTIX_DISK_NODE_H +#define SORTIX_DISK_NODE_H + +#include + +#include + +#include +#include + +namespace Sortix { + +class Harddisk; + +class PortNode : public AbstractInode +{ +public: + PortNode(Harddisk* harddisk, uid_t owner, gid_t group, mode_t mode, dev_t dev, ino_t ino); + virtual ~PortNode(); + virtual int sync(ioctx_t* ctx); + virtual int truncate(ioctx_t* ctx, off_t length); + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off); + virtual ssize_t tcgetblob(ioctx_t* ctx, const char* name, void* buffer, size_t count); + +private: + Harddisk* harddisk; + +}; + +} // namespace Sortix + +#endif diff --git a/kernel/include/sortix/kernel/harddisk.h b/kernel/include/sortix/kernel/harddisk.h new file mode 100644 index 00000000..f254f8b8 --- /dev/null +++ b/kernel/include/sortix/kernel/harddisk.h @@ -0,0 +1,57 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2015. + + 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 . + + sortix/kernel/harddisk.h + Harddisk interface. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_HARDDISK_H +#define INCLUDE_SORTIX_KERNEL_HARDDISK_H + +#include + +#include + +namespace Sortix { + +class Harddisk +{ +public: + virtual ~Harddisk() { } + virtual off_t GetSize() = 0; + virtual blkcnt_t GetBlockCount() = 0; + virtual blksize_t GetBlockSize() = 0; + virtual uint16_t GetCylinderCount() = 0; + virtual uint16_t GetHeadCount() = 0; + virtual uint16_t GetSectorCount() = 0; + virtual const char* GetDriver() = 0; + virtual const char* GetModel() = 0; + virtual const char* GetSerial() = 0; + virtual const char* GetRevision() = 0; + virtual const unsigned char* GetATAIdentify(size_t* size_ptr) = 0; + virtual int sync(ioctx_t* ctx) = 0; + virtual ssize_t pread(ioctx_t* ctx, unsigned char* buf, size_t count, off_t off) = 0; + virtual ssize_t pwrite(ioctx_t* ctx, const unsigned char* buf, size_t count, off_t off) = 0; + +}; + +} // namespace Sortix + +#endif diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 2fcde2f3..c8e0e696 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -72,8 +72,8 @@ #include #include -#include "ata.h" #include "com.h" +#include "disk/ata/ata.h" #include "fs/full.h" #include "fs/kram.h" #include "fs/null.h"