From ba38f4fd198fc42b17ec2cdf825c468237891e03 Mon Sep 17 00:00:00 2001
From: Aaron Hance <ah@aaronhance.me>
Date: Wed, 23 Jun 2021 21:22:06 +0100
Subject: [PATCH] Restructuring and expanding storage system.

---
 src/driver/storage/IDE.pas               |   9 +-
 src/driver/storage/MBR.pas               |  84 +++++++++
 src/driver/storage/drivemanager.pas      | 226 +++++++++++++++++++++++
 src/driver/storage/storagemanagement.pas |   3 +-
 src/driver/storage/volumemanager.pas     | 137 ++++++++++++++
 src/kernel.pas                           |   8 +-
 6 files changed, 461 insertions(+), 6 deletions(-)
 create mode 100644 src/driver/storage/MBR.pas
 create mode 100644 src/driver/storage/drivemanager.pas
 create mode 100644 src/driver/storage/volumemanager.pas

diff --git a/src/driver/storage/IDE.pas b/src/driver/storage/IDE.pas
index d9d10b32..3e6fc6ec 100644
--- a/src/driver/storage/IDE.pas
+++ b/src/driver/storage/IDE.pas
@@ -21,7 +21,8 @@ uses
     lmemorymanager,
     storagemanagement,
     strings,
-    tracer;
+    tracer,
+    drivemanager;
 
 type 
     TPortMode = (P_READ, P_WRITE);
@@ -267,6 +268,7 @@ var
     slaveDevice : TStorage_Device;
     buffer : puint8;
     i : uint8;
+    test : PStorage_device;
 begin
     push_trace('ide.load');
     console.writestringln('[IDE] (LOAD) BEGIN');
@@ -288,14 +290,15 @@ begin
             exit;
         end;
 
-        masterDevice.hpc:= uint32(IDEDevices[0].info[3] DIV IDEDevices[0].info[1]);
+        // masterDevice.hpc:= uint32(IDEDevices[0].info[3] DIV IDEDevices[0].info[1]); //TODO wtf is hpc
 
         masterDevice.sectorSize:= 512;
         if masterDevice.maxSectorCount <> 0 then begin
             IDEDevices[0].exists:= true;
             masterDevice.readCallback:= @dread;
             masterDevice.writeCallback:= @dwrite;
-            storagemanagement.register_device(@masterDevice);
+            // storagemanagement.register_device(@masterDevice);
+            drivemanager.register_device(@masterDevice);
         end;
 
     end;
diff --git a/src/driver/storage/MBR.pas b/src/driver/storage/MBR.pas
new file mode 100644
index 00000000..37e5d544
--- /dev/null
+++ b/src/driver/storage/MBR.pas
@@ -0,0 +1,84 @@
+{
+    Driver->Storage->MBR Master boot record 
+
+    @author(Aaron Hance ah@aaronhance.me)
+}
+unit mbr;
+
+interface
+
+uses
+    tracer;
+
+type
+
+    PMaster_Boot_Record = ^TMaster_Boot_Record;
+    PPartition_table = ^TPartition_table;
+
+    TPartition_table = bitpacked record 
+        attributes     : uint8;
+        CHS_start      : array[0..2] of uint8;
+        system_id      : uint8;
+        CHS_end        : array[0..2] of uint8;
+        LBA_start      : uInt32;
+        sector_count   : uInt32;
+    end;
+
+    TMaster_Boot_Record = bitpacked record 
+        bootstrap   : array[0..439] of uint8;
+        signature   : uint32;
+        rsv         : uint16;
+        partition_0 :  TPartition_table;
+        partition_1 :  TPartition_table;
+        partition_2 :  TPartition_table;
+        partition_3 :  TPartition_table;
+        boot_sector : uint16;
+    end;
+
+    T24bit = array[0..2] of uint8;
+
+    function get_bootable(partition_table : PPartition_table) : boolean;
+    procedure set_bootable(partition_table : PPartition_table);
+    procedure setup_partition(partition_table : PPartition_table; address : uint32; sectorSize : uint32);
+
+implementation
+
+{ convert LBA address to CHS address}
+function LBA_2_CHS(lba : uint32) : T24bit;
+var
+    dat : T24bit;
+begin
+  //TODO impliment procedure
+  LBA_2_CHS := dat;
+end;
+
+{ Set a partition struct to be bootable}
+procedure set_bootable(partition_table : PPartition_table);
+begin
+    push_trace('MBR.set_bootable');
+    //set the bootble bit in attributes
+    partition_table^.attributes := (partition_table^.attributes and $80);
+end;
+
+{ Check a partitions bootable bit }
+function get_bootable(partition_table : PPartition_table) : boolean;
+begin
+    push_trace('MBR.get_bootable');
+    //get the bootble bit in attributes
+    get_bootable := (partition_table^.attributes and $80) = $80;
+end;
+
+{ Setup a partition table struct }
+procedure setup_partition(partition_table : PPartition_table; address : uint32; sectorSize : uint32);
+begin
+    push_trace('MBR.setup_partition');
+    //set values in both LBA and CHS addressing schemes
+    partition_table^.LBA_start := address;
+    partition_table^.sector_count := sectorSize;
+    partition_table^.CHS_start := LBA_2_CHS(address);
+    partition_table^.CHS_start := LBA_2_CHS(address + sectorSize);  
+    push_trace('MBR.setup_partition.end');
+end;
+
+
+end.
\ No newline at end of file
diff --git a/src/driver/storage/drivemanager.pas b/src/driver/storage/drivemanager.pas
new file mode 100644
index 00000000..d5bc7619
--- /dev/null
+++ b/src/driver/storage/drivemanager.pas
@@ -0,0 +1,226 @@
+{
+    Driver->Storage->DriveManager Physical storage device manager
+
+    @author(Aaron Hance ah@aaronhance.me)
+}
+unit drivemanager;
+
+interface
+
+uses
+    util,
+    drivertypes,
+    console,
+    terminal,
+    drivermanagement,
+    lmemorymanager,
+    strings,
+    lists,
+    tracer,
+    rtc,
+    MBR,
+    serial;
+
+type 
+    TControllerType = (ControllerIDE, ControllerUSB, ControllerAHCI, 
+        ControllerNET, ControllerRAM, rsvctr1, rsvctr2, rsvctr3);
+
+    PStorage_device = ^TStorage_Device;
+    PDrive_Error    = ^TDrive_Error;
+
+    // PPWriteHook = procedure(volume : PStorage_volume; directory : pchar; entry : PDirectory_Entry; byteCount : uint32; buffer : puint32; statusOut : puint32);
+    // PPReadHook = function(volume : PStorage_Volume; directory : pchar; fileName : pchar; fileExtension : pchar; buffer : puint32; bytecount : puint32) : uint32;
+    // PPCreateHook = procedure(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
+    // PPDetectHook = procedure(disk : PStorage_Device);
+    // PPCreateDirHook = procedure(volume : PStorage_volume; directory : pchar; dirname : pchar; attributes : uint32; status : puint32);
+    // PPReadDirHook = function(volume : PStorage_volume; directory : pchar; status : puint32) : PLinkedListBase; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error
+    PPHIOHook = procedure(drive : PStorage_device; addr : uint32; sectors : uint32; buffer : puint32);
+
+    PPHIOHook_ = procedure;
+
+
+    { Generic storage device }
+    TStorage_Device = record
+        id               : uint8;
+        controller       : TControllerType;
+        controllerId0    : uint32;
+        writable         : boolean;
+        volumes          : PLinkedListBase;
+        writeCallback    : PPHIOHook;
+        readCallback     : PPHIOHook;
+        maxSectorCount   : uint32;
+        sectorSize       : uint32;
+    end;
+
+    TDrive_Error = record 
+        code : uint16;
+        description : pchar;
+        recoverable : boolean;
+    end;
+
+var 
+    storageDevices : PLinkedListBase; //index in this array is global drive id
+
+    procedure init();
+
+    procedure register_device(device : PStorage_Device);
+    function get_drive_list() : PLinkedListBase;
+    function controller_type_2_string(controllerType : TControllerType) : pchar; 
+
+implementation 
+
+procedure list_drive_command();
+var 
+    i : uint32 = 0;
+begin
+    push_trace('DriverManager.list_drive_command');
+    //if no storage device print none found
+    if LL_Size(storageDevices) < 1 then begin
+        console.writestringlnWnd('No storage devices found.', getTerminalHWND());
+        exit;
+    end;
+
+    //print number of storage devices
+    console.writeintwnd(LL_Size(storageDevices), getTerminalHWND());
+    console.writestringlnWnd(' devices found', getTerminalHWND());
+
+    for i:=0 to LL_Size(storageDevices)-1 do begin
+
+        //print device id and type
+        console.writeintwnd(i, getTerminalHWND());
+        console.writestringwnd(' - Device type: ', getTerminalHWND());
+        console.writestringwnd(controller_type_2_string(PStorage_Device(LL_Get(storageDevices, i))^.controller), getTerminalHWND());
+        console.writestringwnd(', ', getTerminalHWND());
+
+        //print device capcity
+        console.writestringwnd(' Capacity: ', getTerminalHWND());
+        console.writeintwnd((( PStorage_Device(LL_Get(storageDevices, i))^.maxSectorCount * PStorage_Device(LL_Get(storageDevices, i))^.sectorSize) DIV 1024) DIV 1024, getTerminalHWND());
+        console.writestringlnWnd('MB', getTerminalHWND());
+    end;
+
+end;
+
+{ initalise a drive with a MBR - TODO needs to be expanded for partitioning }
+procedure init_drive_command(driveIndex : uint32);
+var
+    bootrecord : PMaster_Boot_Record;
+    partition : PPartition_table;
+    drive : PStorage_device;
+begin
+    push_trace('DriverManager.init_drive_command');
+    //setup MBR and partition table
+    bootrecord := PMaster_Boot_record(kalloc(SizeOf(TMaster_boot_record)));
+    partition := PPartition_table(kalloc(SizeOf(TPartition_table)));
+
+    drive := PStorage_device(LL_Get(storageDevices, driveIndex));
+
+    MBR.setup_partition(partition, 100, drive^.maxSectorCount-300);
+    bootrecord^.partition_0 := partition^;
+
+    //write MBR and partition table to disk
+    drive^.writeCallback(drive, 0, 1, puint32(bootrecord));
+
+    //volume_manager reload partition
+    //TODO
+
+end;
+
+procedure drive_command(params : PParamList);
+var
+    i : uint16;
+    drive : uint16;
+    subcmd : pchar;
+begin
+    push_trace('DriverManager.drive_command');
+    subcmd:= getParam(0, params);
+
+    if ((paramCount(params) = 0)) then begin
+        console.writestringlnWnd('Please provide valid arguments.', getTerminalHWND());
+        console.writestringlnWnd('  ls - for listing all drives', getTerminalHWND());
+        console.writestringlnWnd('  info [drive] - display formation for specified drive', getTerminalHWND());
+        console.writestringlnWnd('  init [drive] - destructive, formats a drive with a blank partition', getTerminalHWND());
+        exit;
+    end;
+
+    if( stringEquals(subcmd, 'ls') ) then begin
+      list_drive_command();
+      exit;
+    end;
+
+    if( stringEquals(subcmd, 'info') ) then begin
+      //TODO unimplimented
+      exit
+    end;
+
+    if( stringEquals(subcmd, 'init') ) then begin
+      init_drive_command(stringToInt(getParam(1, params) ) );
+      exit
+    end;
+end;
+
+{ Initialise the drive manager }
+procedure init();
+begin
+    push_trace('DriveManager.init');
+    
+    //setup drive manager
+    setworkingdirectory('.');
+    storageDevices:= ll_new(sizeof(TStorage_Device));
+
+    serial.sendString('HERE I DID THE INIT WAH DU HEK BRUH!!!!!!');
+
+    //register DISK command 
+    terminal.registerCommand('DISK', @drive_command, 'Disk utility');
+end;
+
+{ Register a new drive with the drive manager }
+procedure register_device(device : PStorage_device);
+var 
+    i   : uint8;
+    elm : void;
+begin
+    push_trace('DriveManager.register_device');
+
+    //add the drive to the list of storage devices.
+    elm:= LL_Add(storageDevices); 
+    PStorage_device(elm)^ := device^; //TODO memcopy
+
+    //set drive id in struct
+    PStorage_device(elm)^.id := LL_Size(storageDevices) - 1;
+
+    //create empty volume list for drive
+    // PStorage_Device(LL_Get(storageDevices, LL_Size(storageDevices) - 1))^.volumes := LL_New(sizeof(TStorage_Volume));
+    
+    //TODO register with volume manager
+
+end;
+
+{ Get the drive list }
+function get_drive_list() : PLinkedListBase;
+begin
+    push_trace('DriverManager.get_drive_list');
+
+    //return the device list
+    get_drive_list:= storageDevices;
+end;
+
+{ Get string name for controller type }
+function controller_type_2_string(controllerType : TControllerType) : pchar; 
+begin
+    push_trace('DriverManager.controller_type_2_string');
+
+    //return string of controller type
+    case controllerType of
+        ControllerIDE : controller_type_2_string:= 'IDE';
+        ControllerUSB :  controller_type_2_string:= 'USB';
+        ControllerAHCI : controller_type_2_string:= 'ACHI (SATA)';
+        ControllerNET : controller_type_2_string:= 'Net';
+    end;
+end;
+
+function write_2_drive(drive : uint32; address : uint32; byteSize : uint32; buffer : puint32) : PDrive_Error;
+begin
+  
+end;
+
+end.
\ No newline at end of file
diff --git a/src/driver/storage/storagemanagement.pas b/src/driver/storage/storagemanagement.pas
index e315a61b..84371725 100644
--- a/src/driver/storage/storagemanagement.pas
+++ b/src/driver/storage/storagemanagement.pas
@@ -234,7 +234,8 @@ begin
     fileSystems:= ll_New(sizeof(TFilesystem));
     terminal.registerCommand('DISK', @disk_command, 'Disk utility');
 
-    pop_trace();
+    
+
 end; 
 
 { Register a new drive with the storage manager }
diff --git a/src/driver/storage/volumemanager.pas b/src/driver/storage/volumemanager.pas
new file mode 100644
index 00000000..859478ba
--- /dev/null
+++ b/src/driver/storage/volumemanager.pas
@@ -0,0 +1,137 @@
+{
+    Driver->Storage->VolumeManager Drive volume manager
+
+    @author(Aaron Hance ah@aaronhance.me)
+}
+unit volumemanager;
+
+interface
+
+uses
+    util, 
+    console,
+    drivemanager,
+    strings,
+    lists,
+    tracer,
+    vfs,
+    MBR;
+
+type
+    
+    TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry);
+
+    PDirectory_Entry = ^TDirectory_Entry;
+    PFilesystem = ^TFilesystem;
+    PStorage_Volume = ^TStorage_Volume;
+
+    PPWriteHook = procedure(volume : PStorage_volume; directory : pchar; entry : PDirectory_Entry; byteCount : uint32; buffer : puint32; statusOut : puint32);
+    PPReadHook = function(volume : PStorage_Volume; directory : pchar; fileName : pchar; fileExtension : pchar; buffer : puint32; bytecount : puint32) : uint32;
+    PPHIOHook = procedure(volume : PStorage_device; addr : uint32; sectors : uint32; buffer : puint32);
+    PPCreateHook = procedure(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
+    PPDetectHook = procedure(disk : PStorage_Device);
+    PPCreateDirHook = procedure(volume : PStorage_volume; directory : pchar; dirname : pchar; attributes : uint32; status : puint32);
+    PPReadDirHook = function(volume : PStorage_volume; directory : pchar; status : puint32) : PLinkedListBase; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error
+
+    PPHIOHook_ = procedure;
+
+    TFilesystem = record
+        sName : pchar;
+        system_id : uint8;
+        writeCallback     : PPWriteHook;
+        readCallback      : PPReadHook;
+        createCallback    : PPCreateHook;
+        detectCallback    : PPDetectHook;
+        createDirCallback : PPCreateDirHook;
+        readDirCallback   : PPReadDirHook;
+    end;
+
+    TStorage_Volume = record
+        device          : PStorage_device;
+        sectorStart     : uint32;
+        sectorSize      : uint32;
+        sectorCount     : uint32;
+        freeSectors     : uint32;
+        filesystem      : PFilesystem; 
+        { True if this drive contains the loaded OS }
+        isBootDrive     : boolean;
+    end;
+
+    { Generic directory entry }
+    TDirectory_Entry = record
+        { Contains filename and optionally file extension, seperated by the last period.}
+        fileName  : pchar; 
+        entryType : TDirectory_Entry_Type;
+        // creationT : TDateTime;
+        // modifiedT : TDateTime;
+    end;
+  
+var
+
+    storageVolumes : PLinkedListBase;
+    filesystems : PlinkedListBase;
+
+    procedure init();
+    
+    procedure register_filesystem(filesystem : PFilesystem);
+    procedure check_for_volumes(drive : PStorage_device);
+
+    //IO functions
+
+implementation
+
+{ Initialise volume manager }
+procedure init();
+begin
+    push_trace('VolumeManager.init');
+
+    { setup lists }
+    storageVolumes:= ll_New(sizeof(TFilesystem));
+    filesystems:= ll_New(sizeof(TFilesystem));      
+end;
+
+{ register a new type of filesystem }
+procedure register_filesystem(filesystem : PFilesystem); //TODO on init create null filesystem for empty partition
+begin
+    //add filesystem
+
+    //check drives for volumes of new type
+end;
+
+{ Check for volumes on a physical device }
+procedure check_for_volumes(drive : PStorage_device);
+var 
+    bootrecord : PMaster_Boot_Record;
+    storageVolume : TStorage_Volume;
+    i : uint32 = 0;
+    elm : void;
+begin
+
+    drive^.readCallback(drive, 0, 1, puint32(bootrecord));
+
+    //TODO multipe partition entries
+    if bootrecord^.partition_0.LBA_start <> 0 then
+    begin
+      //set volume information
+      storageVolume.device := drive;
+      storageVolume.sectorStart := bootrecord^.partition_0.LBA_start;
+      storageVolume.sectorCount := bootrecord^.partition_0.sector_count;
+      storageVolume.freeSectors := 0; //TODO impliment
+
+      //check for filesystem type
+      for i:=0 to LL_Size(filesystems) - 1 do begin
+        if bootrecord^.partition_0.system_id = PFilesystem(LL_Get(filesystems, i))^.system_id then
+        begin
+          storageVolume.filesystem := PFilesystem(LL_Get(filesystems, i));
+        end;
+      end;
+
+      //add volume to list
+      elm := LL_Add(storageVolumes);
+      memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume));
+        
+    end;
+    
+end;
+
+end.
\ No newline at end of file
diff --git a/src/kernel.pas b/src/kernel.pas
index 9b40accc..fd382f0c 100644
--- a/src/kernel.pas
+++ b/src/kernel.pas
@@ -41,6 +41,8 @@ uses
      E1000,
      IDE,
      storagemanagement,
+     drivemanager,
+     volumemanager,
      lists,
      net,
      fat32,
@@ -205,7 +207,9 @@ begin
      tracer.push_trace('kmain.DRVMGMT');
      drivermanagement.init();
      tracer.push_trace('kmain.STRMGMT');
-     storagemanagement.init();
+     //  storagemanagement.init();
+     drivemanager.init();
+     volumemanager.init();
 
      { Hook Timer for Ticks }
      tracer.push_trace('kmain.TMR');
@@ -213,7 +217,7 @@ begin
      TMR_0_ISR.hook(uint32(@bios_data_area.tick_update));
 
      { Filsystems }
-     fat32.init();
+    //  fat32.init();
 
      { Device Drivers }
      tracer.push_trace('kmain.DEVDRV');