26 Commits

Author SHA1 Message Date
837d69a44b ide rework again 2025-03-09 13:50:11 +00:00
f0d5eb21f1 DevOps Workflow Improvements
- `VirtualBox-Wrapper.ps1` now takes 'up' or 'down' as opposed to a machine name. This allows start/stop of a virtualmachine.
- `VirtualBox-Wrapper.ps1` now relies on a gitignored `localenv.json` to work.
- `VirtualBox-Wrapper.ps1` can also optionally monitor the log file generated from the serial adapter in VirtualBox.
- `readme.md` updated to provide instructions on how to populate the `localenv.json` file.
- `tasks.json` updated to have a "Clean" task to --remove-orphans, the Build task depends on this.
- `tasks.json` updated to have a "Close VirtualBox" task, this runs the `virtualbox-wrapper.ps1` in 'down' mode. The Build task depends on this.
- `launch.json` updated to run the `VirtualBox-Wrapper.ps1` with the "-Command up" argument, instead of machine name.
- .gitignore updated to ignore any instances of `localenv.json`.

# Conflicts:
#	.gitignore
2025-03-09 13:49:53 +00:00
07e9095582 VirtualBox 7 Compatability Changes
- Created a PowerShell script `virtualbox-wrapper.ps1` to wrap calls to vboxmanage and only exit once the virtual machine has been terminated.
- Updated launch.json to use the PowerShell launch type to launch `virtualbox-wrapper.ps1` with the machine name supplied as an argument.
- Updated `readme.md` to reflect these changes.

# Conflicts:
#	.vscode/launch.json
2025-03-08 21:09:13 +00:00
72769fbef7 fix 2022-09-28 16:15:45 +01:00
95599c0e7c fix 2022-09-28 16:14:06 +01:00
fcaafa0d12 Started aadding file handle stuff to volume manager 2022-09-28 16:04:51 +01:00
e490b95f1d comments 2022-09-27 17:23:42 +01:00
b601080960 Added delete files and directory functions to flatfs 2022-02-03 02:21:51 +00:00
27177979a2 Flatfs reading and writing files and directories done 2022-02-03 01:21:37 +00:00
f88a282893 Progress on flatfs 2022-02-02 23:19:30 +00:00
ff4c597ff3 Work on storage and flat file system 2022-02-01 05:07:52 +00:00
24c371cab1 Added new String functions 2022-01-31 00:34:38 +00:00
7588a4c9cb Revert "Updated Strings unit with new functions"
This reverts commit 5cb415c76b.
2022-01-31 00:31:01 +00:00
5cb415c76b Updated Strings unit with new functions 2022-01-31 00:27:02 +00:00
118d8b0c03 Merge branch 'feature/storage-system' of https://gitlab.spexeah.com/spexeah/asuro into feature/storage-system
# Conflicts:
#	src/driver/storage/drivemanager.pas
#	src/driver/storage/storagemanagement.pas
2022-01-30 00:42:37 +00:00
28caca3bcc commiting before rebase 2022-01-30 00:34:52 +00:00
6476a774dc More progress and fixes 2022-01-30 00:34:52 +00:00
e791edb02a Progress 2022-01-30 00:34:51 +00:00
da36f6fabb Continued new volume management stuff. 2022-01-30 00:34:51 +00:00
97e1a18253 Restructuring and expanding storage system. 2022-01-30 00:34:51 +00:00
88a850e8fc More progress and fixes 2022-01-29 22:21:10 +00:00
10c9194c5f Progress 2022-01-29 20:05:11 +00:00
346dc4e4c9 Merge branch 'feature/readme-update-1' into 'develop'
Update readme.md

See merge request spexeah/asuro!29
2022-01-29 12:47:56 +00:00
b2eee58df4 Update readme.md 2022-01-29 12:46:35 +00:00
94b477d8f7 Continued new volume management stuff. 2021-06-25 01:48:20 +01:00
ba38f4fd19 Restructuring and expanding storage system. 2021-06-23 21:22:06 +01:00
21 changed files with 3087 additions and 519 deletions

3
.gitignore vendored
View File

@ -10,5 +10,4 @@
/*.sh~
/*.img
src/include/asuro.pas
.vscode/launch.json
.vscode
localenv.json

13
.vscode/launch.json vendored
View File

@ -3,18 +3,11 @@
{
"name":"Run",
"request": "launch",
"type": "coreclr",
"type": "PowerShell",
"preLaunchTask": "Build",
"program": "VBoxSDL",
"args": [
"--comment",
"Asuro",
"--startvm",
"7d395c96-891c-4139-b77d-9b6b144b0b93"
],
"script": "${workspaceFolder}/virtualbox-wrapper.ps1",
"args": ["-Command", "up"],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"internalConsoleOptions": "neverOpen"
}
]
}

26
.vscode/tasks.json vendored
View File

@ -9,14 +9,18 @@
"command": "docker-compose",
"args": [
"run",
"builder"
"builder",
],
"type": "shell",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
"dependsOn": [
"Close VirtualBox",
"Clean"
]
},
{
"label": "Build (Builder)",
@ -26,6 +30,24 @@
"builder"
],
"type": "shell"
},
{
"label": "Clean",
"command": "docker-compose",
"args": [
"down",
"--remove-orphans"
],
"type": "shell"
},
{
"label": "Close VirtualBox",
"command": "./virtualbox-wrapper.ps1",
"args": [
"-Command",
"down"
],
"type": "shell"
}
]
}

32
doc/flatfs.md Normal file
View File

@ -0,0 +1,32 @@
#Flat filesystem
A super simple filesystem for asuro. Folders are emulated in filenames.
Starts with disk info sector, sector 0 of volume
---
#### disk info
jmp2boot : ubit24;
OEMName : array[0..7] of char;
version : uint16 // numerical version of filesystem
sectorCount : uint16;
fileCount : uint16
signature : uint32 = 0x0B00B1E5
the Rest of the sector is reserved
---
Starting from sector 1 is the file table. Table size is determined by entry size (64) * fileCount
---
####File entry
name : array[0..59] of char //file name max 60 chars
fileStart : 16bit // start sector of data
fileSize : 16bit // data size in sectors
---

View File

@ -1,4 +1,3 @@
version: "3.9"
services:
builder:
build: .

View File

@ -13,8 +13,8 @@ We welcome everyone to give building/breaking/fixing/shooting Asuro a go, feel f
I don't think this needs an explaination.
* [VSCode (Optional, but highly recommended)](https://code.visualstudio.com/)
Visual Studio code is our IDE of choice, and we have a number of recommended plugins.
* [C# Plugin by Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)
This plugin gives you the ability to use the 'coreclr' task type, allowing the automatic launching of virtualbox with the resulting image generated during compilation of Asuro.
* [PowerShell Plugin by Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-vscode.powershell)
This plugin gives you the ability to use the 'PowerShell' task type, allowing the automatic launching of virtualbox with the resulting image generated during compilation of Asuro.
* [VirtualBox](https://www.virtualbox.org/)
Virtualbox is our Virtualisation environment of choice, don't ask why, it just is.
@ -27,15 +27,15 @@ We welcome everyone to give building/breaking/fixing/shooting Asuro a go, feel f
3. Install Docker for Windows.
4. Install Git for Windows.
5. Install VSCode & the listed plugins.
6. Install VirtualBox.
6. Install VirtualBox (v7+).
7. Clone this repository.
8. Run the following command in the root of the repo to build the docker image:
```powershell
docker-compose build builder
docker compose build builder
```
9. Run the following command to compile Asuro:
```powershell
docker-compose run builder
docker compose run builder
```
10. Create a new virtual machine in Virtualbox and mount the `Asuro.iso` generated in step 9 as a boot image.
11. Add the virtualbox installation directory to your `%PATH%` environment variable, usually:
@ -50,32 +50,29 @@ We welcome everyone to give building/breaking/fixing/shooting Asuro a go, feel f
```xml
<Machine uuid="{7d395c96-891c-4139-b77d-9b6b144b0b93}" name="Asuro" OSType="Linux" snapshotFolder="Snapshots" lastStateChange="2021-06-20T20:33:07Z">
```
Copy the uuid, in our case `7d395c96-891c-4139-b77d-9b6b144b0b93` and replace the uuid found in `.vscode\launch.json` under `args`, so that it looks something like this:
Copy the uuid, in our case `7d395c96-891c-4139-b77d-9b6b144b0b93` & create a `localenv.json` file in the project root with the following content:
```json
{
"configurations": [
{
"name":"Run",
"request": "launch",
"type": "coreclr",
"preLaunchTask": "Build",
"program": "VBoxSDL",
"args": [
"--comment",
"Asuro",
"--startvm",
"<YOUR UUID HERE>"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"internalConsoleOptions": "neverOpen"
"VirtualBox":{
"MachineName":"<YOUR_UUID_OR_MACHINE_NAME>"
}
]
}
```
This will allow VSCode to automatically launch VirtualBox once Asuro has been compiled.
You can also enable the serial adapter "COM1" in mode "Raw File", give it a path, and provide this path in the `localenv.json` as follows:
```json
{
"VirtualBox" : {
"MachineName": "<YOUR_UUID_OR_MACHINE_NAME>",
"LogLocation": "Fully\\Qualified\\Path\\To\\Your\\Log\\File"
}
}
```
This will allow you to see the console output from Asuro in your host terminal.
13. Open your project folder in VSCode, use CTRL+SHIFT+B to build & F5 to build + run in VBox.
14. Congratulations! You can now play with Asuro!
### Gotchas
### Notes & Gotchas
- The above process has been updated to be compatible with VirtualBox 7+, in which VBoxSDL was removed and vboxmanage should be used in its place. A small wrapper powershell script is used to achieve this.
- It was noted that Windows builds above `20H2` seem to have issues installing WSL2. We may have to wait for a patch from Microsoft to fix this. Our devs are currently using build `20H2`.

View File

@ -1,17 +1,9 @@
{ ************************************************
* Asuro
* Unit: Drivers/storage/IDE
* Description: IDE ATA Driver
*
************************************************
* Author: Aaron Hance
* Contributors:
************************************************ }
unit IDE;
unit ide;
interface
uses
ata,
util,
drivertypes,
console,
@ -21,226 +13,326 @@ uses
lmemorymanager,
storagemanagement,
strings,
tracer;
tracer,
drivemanager,
storagetypes,
idetypes;
type
TPortMode = (P_READ, P_WRITE);
TIdentResponse = array[0..255] of uint16;
TIDE_Channel_Registers = record
base : uint16;
ctrl : uint16;
bmide : uint16;
noInter : uint8
end;
TIDE_Device = record
exists : boolean;
isPrimary : boolean;
isMaster : boolean;
isATAPI : boolean;
info : TIdentResponse;
end;
TIDE_Status = bitpacked record
Busy : Boolean;
Ready : Boolean;
Fault : Boolean;
Seek : Boolean;
DRQ : Boolean;
CORR : Boolean;
IDDEX : Boolean;
ERROR : Boolean;
end;
PIDE_Status = ^TIDE_Status;
const
ATA_SR_BUSY = $80; //BUSY
ATA_SR_DRDY = $40; //DRIVE READY
ATA_SR_DF = $20; //DRIVE WRITE FAULT
ATA_SR_DSC = $10; //DRIVE SEEK COMPLETE
ATA_SR_DRQ = $08; //DATA REQUEST READY
ATA_SR_CORR = $04; //CORRECTED DATA
ATA_SR_IDX = $02; //INLEX
ATA_SR_ERR = $01; //ERROR
ATA_ER_BBK = $80; //BAD SECTOR
ATA_ER_UNC = $40; //UNCORRECTABLE DATA
ATA_ER_MC = $20; //NO MEDIA
ATA_ER_IDNF = $10; //ID MARK NOT FOUND
ATA_ER_MCR = $08; //NO MEDIA
ATA_ER_ABRT = $04; //COMMAND ABORTED
ATA_ER_TK0NF = $02; //TRACK 0 NOT FOUND
ATA_ER_AMNF = $01; //NO ADDRESS MARK
ATA_CMD_READ_PIO = $20;
ATA_CMD_READ_PIO_EXT = $24;
ATA_CMD_READ_DMA = $C8;
ATA_CMD_READ_DMA_EXT = $25;
ATA_CMD_WRITE_PIO = $30;
ATA_CMD_WRITE_PIO_EXT = $34;
ATA_CMD_WRITE_DMA = $CA;
ATA_CMD_WRITE_DMA_EXT = $35;
ATA_CMD_CACHE_FLUSH = $E7;
ATA_CMD_CACHE_FLUSH_EXT = $EA;
ATA_CMD_PACKET = $A0;
ATA_CMD_IDENTIFY_PACKET = $A1;
ATA_CMD_IDENTIFY = $EC;
ATAPI_CMD_READ = $A8;
ATAPI_CMD_EJECT = $1B;
ATA_IDENT_DEVICETYPE = $0;
ATA_IDENT_CYLINDERS = $2;
ATA_IDENT_HEADS = $6;
ATA_IDENT_SECOTRS = $12;
ATA_IDENT_SERIAL = $20;
ATA_IDENT_MODEL = $54;
ATA_IDENT_CAPABILITIES = $98;
ATA_IDENT_FIELDVALID = $106;
ATA_IDENT_MAX_LBA = $120;
ATA_IDENT_COMMANDSETS = $164;
ATA_IDENT_MAX_LBA_EXT = $200;
ATA_REG_DATA = $00;
ATA_REG_ERROR = $01;
ATA_REG_FEATURES = $01;
ATA_REG_SECCOUNT = $02;
ATA_REG_LBA0 = $03;
ATA_REG_LBA1 = $04;
ATA_REG_LBA2 = $05;
ATA_REG_HDDEVSEL = $06;
ATA_REG_COMMAND = $07;
ATA_REG_STATUS = $07;
ATA_REG_SECCOUNT1 = $08;
ATA_REG_LBA3 = $09;
ATA_REG_LBA4 = $0A;
ATA_REG_LBA5 = $0B;
ATA_REG_CONTROL = $0C;
ATA_REG_ALTSTATUS = $0C;
ATA_REG_DEVADDRESS = $0D;
ATA_DEVICE_MASTER = $A0;
ATA_DEVICE_SLAVE = $B0;
ATA_PRIMARY_BASE = $1F0;
var
controller : PPCI_Device;
primaryDevices: array[0..1] of TIDE_Device = (
(exists: false; isPrimary: true; isMaster: true; isATAPI: false;
status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false);
base: ATA_PRIMARY_BASE; info: nil),
bar0 : uint32;
bar1 : uint32;
bar4 : uint32;
(exists: false; isPrimary: true; isMaster: false; isATAPI: false;
status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false);
base: ATA_PRIMARY_BASE; info: nil)
);
IDEDevices : array[0..3] of TIDE_Device;
secondaryDevices: array[0..1] of TIDE_Device = (
(exists: false; isPrimary: false; isMaster: true; isATAPI: false;
status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false);
base: ATA_SECONDARY_BASE; info: nil),
(exists: false; isPrimary: false; isMaster: false; isATAPI: false;
status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false);
base: ATA_SECONDARY_BASE; info: nil)
);
buffer : Puint32;
procedure init();
function load(ptr: void) : boolean;
function identify_device(bus : uint8; device : uint8) : TIdentResponse;
// procedure flush();
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8);
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8);
//read/write must be capable of reading/writting any amknt of data upto disk size
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32);
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32);
procedure init();
function get_status(var device : TIDE_Device) : TIDE_Status;
function wait_for_device(device : TIDE_Device) : boolean;
procedure no_interrupt(isPrimary : boolean);
procedure select_device(device : TIDE_Device);
implementation
function port_read(register : uint8) : uint8;
{
Disable interrupts on the IDE bus
@param(isPrimary The bus to disable interrupts on)
}
procedure no_interrupt(isPrimary : boolean);
begin
port_read:= inb(ATA_PRIMARY_BASE + register);
if isPrimary then begin
outb(ATA_INTERRUPT_PRIMARY, inb(ATA_INTERRUPT_PRIMARY) or (1 shl 1));
end else begin
outb(ATA_INTERRUPT_SECONDARY, inb(ATA_INTERRUPT_SECONDARY) or (1 shl 1));
end;
end;
procedure port_write(register : uint8; data : uint8);
{
Wait for the device to be ready on the IDE bus
@param(device The device to wait for)
@returns(@True if the device is ready, @False otherwise)
}
function wait_for_device(device : TIDE_Device) : boolean;
var
i : uint8;
begin
outb(ATA_PRIMARY_BASE + register, data);
util.psleep(1);
if register = ATA_REG_COMMAND then begin
for i:= 0 to 5 do begin
port_read(ATA_REG_STATUS);
end;
end;
end;
procedure no_interrupt(device : uint8);
begin
outb($3F6, inb($3f6) or (1 shl 1));
end;
procedure device_select(device : uint8);
begin
outb($1F6, device); //TODO clean
end;
function is_ready() : boolean;
var
status : uint8;
status : TIDE_Status;
i : uint32;
begin
//wait for drive to be ready
while true do begin
status := port_read(ATA_REG_COMMAND);
push_trace('ide.wait_for_device()');
if(status and ATA_SR_ERR) = ATA_SR_ERR then begin
console.writestringln('[IDE] (IDENTIFY_DEVICE) DRIVE ERROR!');
console.redrawWindows();
is_ready := false;
break;
i := 0;
while (i < 10000) do begin
status := get_status(device);
if (status.BUSY = false) then begin //todo test
wait_for_device := true;
pop_trace();
exit;
end;
if (status and ATA_SR_BUSY) <> ATA_SR_BUSY then begin
is_ready := true;
break;
end;
end;
i := i + 1;
end;
function validate_28bit_address(addr : uint32) : boolean;
begin
validate_28bit_address := (addr and $F0000000) = 0;
wait_for_device := false;
pop_trace();
end;
function identify_device(bus : uint8; device : uint8) : TIdentResponse;
{
Select the active device on the IDE bus
@param(device The device to select)
Function can set the device to either master or slave,
one device per bus/channel can be active at a time.
}
procedure select_device(device : TIDE_Device);
var
status : uint8;
identResponse : TIdentResponse;
dev : uint8;
begin
push_trace('ide.select_device()');
dev := ATA_DEVICE_SLAVE;
if device.isMaster then begin
dev := ATA_DEVICE_MASTER;
end;
outb(device.base + ATA_REG_HDDEVSEL, dev);
pop_trace();
end;
{
Get the status of the device on the IDE bus
@param(device The device to get the status of)
@returns(@TIDE_Device The status of the device on the IDE bus)
}
function get_status(var device : TIDE_Device) : TIDE_Status;
var
status : TIDE_Status;
begin
push_trace('ide.get_status()');
select_device(device);
status := TIDE_Status(inb(device.base + ATA_REG_STATUS));
device.status := status;
pop_trace();
get_status := status;
end;
{
Check if the device is present on the IDE bus
@param(device The device to check)
@returns(@True if the device is present, @False otherwise)
}
function is_device_present(var device : TIDE_Device) : boolean;
begin
push_trace('ide.is_device_present()');
get_status(device);
if (uInt8(device.status) = $FF) then begin //TODO make this more robust
is_device_present := false;
end else begin
is_device_present := true;
end;
end;
{
Check if the device is an ATAPI device
@param(device The device to check)
@returns(@True if the device is an ATAPI device, @False otherwise)
}
function check_device_type(var device : TIDE_Device) : boolean;
var
sec, lba0, lba1, lba2 : uint8;
begin
push_trace('ide.check_device_type()');
select_device(device);
//TODO make sure dvice signture is set at this time, else reset it
//read all bytes of LBA address
sec := inb(device.base + ATA_REG_SECCOUNT);
lba0 := inb(device.base + ATA_REG_LBA0);
lba1 := inb(device.base + ATA_REG_LBA1);
lba2 := inb(device.base + ATA_REG_LBA2);
//check if the device is an ATAPI device (01, 01, 14, EB)
if (lba1 = $14) and (lba2 = $EB) then begin
check_device_type := true;
end else begin
check_device_type := false;
end;
device.isATAPI := check_device_type;
end;
{
Load the device on the IDE bus
@param(device The device to load)
@returns(@True if the device is loaded, @False otherwise)
}
procedure load_device(var device : TIDE_Device);
var
i : uint8;
buffer : puint8;
storageDevice : PStorage_device;
success : boolean;
begin
push_trace('ide.load_device()');
if (is_device_present(device)) then begin
console.writestringln('[IDE] (load_device) Device is present');
if (check_device_type(device)) then begin
console.writestringln('[IDE] (load_device) Device is ATAPI');
//todo load device info
end else begin
console.writestringln('[IDE] (load_device) Device is ATA');
success:= ata.identify_device(device);
end;
if not success then begin
console.writestringln('[IDE] (load_device) Error identifying device'); //todo
device.exists := false;
exit;
end;
device.exists := true;
storageDevice := PStorage_Device(kalloc(sizeof(TStorage_Device)));
memset(uint32(storageDevice), 0, sizeof(TStorage_Device));
if (device.isATAPI) then begin
storageDevice^.controller := TControllerType.ControllerATAPI;
storageDevice^.writable := false; //TODO atapi
end else begin
storageDevice^.controller := TControllerType.ControllerATA;
storageDevice^.writable := true;
storageDevice^.maxSectorCount := (device.info^[61] shl 16) or device.info^[60];
storageDevice^.sectorSize := 512; //todo
if (device.isMaster) then begin
storageDevice^.controllerId0 := 0;
end else begin
storageDevice^.controllerId0 := 1;
end;
//if secotorcount is 0, then the device is not present
if (storageDevice^.maxSectorCount = 0) then begin
device.exists := false;
exit;
end;
//wrtie test, 1F2F1F2F repeated
buffer := puint8(kalloc(512));
memset(uint32(buffer), 0, 512);
for i:=0 to 200 do begin
if (i mod 4 = 0) then begin
buffer[i] := $1F;
end else begin
buffer[i] := $2F;
end;
end;
//write to the first sector
ata.write_pio28(device, 0, 1, puint16(buffer));
//read the first sector
memset(uint32(buffer), 0, 512);
ata.read_pio28(device, 0, 1, puint16(buffer));
//check if the data is the same
for i:=0 to 200 do begin
console.writehexln(buffer[i]);
end;
end;
//register the device
drivemanager.register_device(storageDevice);
end else begin
console.writestringln('[IDE] (load_device) Device is not present');
device.exists := false;
end;
pop_trace();
end;
function load(ptr: void) : boolean;
var
pciDevice : PPCI_Device;
buffer : puint8;
i : uint8;
begin
push_trace('IDE.Identify_Device');
device_select(device);
no_interrupt(device);
port_write(ATA_REG_CONTROL, 0);
push_trace('ide.load()');
console.writestringln('[IDE] (load) Loading IDE Devices');
//check if bus is floating
status := port_read(ATA_REG_COMMAND);
if status = $FF then exit;
pciDevice := PPCI_Device(ptr);
port_write(ATA_REG_SECCOUNT, 0);
port_write(ATA_REG_LBA0, 0);
port_write(ATA_REG_LBA1, 0);
port_write(ATA_REG_LBA2, 0);
load_device(primaryDevices[0]);
load_device(primaryDevices[1]);
load_device(secondaryDevices[0]);
load_device(secondaryDevices[1]);
port_write(ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
pop_trace();
load := true;
//check if drive is present
status := port_read(ATA_REG_COMMAND);
if status = $00 then exit;
console.writestringln('[IDE] (load) IDE Device Loading Finished');
if not is_ready() then exit;
i := 0;
for i:=0 to 255 do begin
identResponse[i] := inw(ATA_PRIMARY_BASE + ATA_REG_DATA);
if (primaryDevices[0].exists) then begin
console.writestringln('[IDE] (load) FOUND Primary Master Device 0');
i := i + 1;
end;
identify_device := identResponse;
if (primaryDevices[1].exists) then begin
console.writestringln('[IDE] (load) FOUND Primary Slave Device 1');
i := i + 1;
end;
if (secondaryDevices[0].exists) then begin
console.writestringln('[IDE] (load) FOUND Secondary Master Device 2');
i := i + 1;
end;
if (secondaryDevices[1].exists) then begin
console.writestringln('[IDE] (load) FOUND Secondary Slave Device 3');
i := i + 1;
end;
// console.writestringln('[IDE] (load) Found ' + i + ' IDE Devices');
end;
procedure init();
@ -256,193 +348,8 @@ begin
devID.id3:= idANY;
devID.id4:= idANY;
devID.ex:= nil;
drivermanagement.register_driver('IDE ATA Driver', @devID, @load);
drivermanagement.register_driver('IDE ATA/ATAPI Driver', @devID, @load);
console.writestringln('[IDE] (INIT) END');
end;
function load(ptr : void) : boolean;
var
controller : PPCI_Device;
masterDevice : TStorage_Device;
slaveDevice : TStorage_Device;
buffer : puint8;
i : uint8;
begin
push_trace('ide.load');
console.writestringln('[IDE] (LOAD) BEGIN');
controller := PPCI_Device(ptr);
console.writestringln('[IDE] (INIT) CHECK FLOATING BUS');
//check if bus is floating and identify device
if inb($1F7) <> $FF then begin
//outb($3F6, inb($3f6) or (1 shl 1)); // disable interrupts
IDEDevices[0].isMaster:= true;
IDEDevices[0].info := identify_device(0, ATA_DEVICE_MASTER);
masterDevice.controller := ControllerIDE;
masterDevice.controllerId0:= 0;
masterDevice.maxSectorCount:= (IDEDevices[0].info[60] or (IDEDevices[0].info[61] shl 16) ); //LBA28 SATA
if IDEDevices[0].info[1] = 0 then begin
console.writestringln('[IDE] (INIT) ERROR: DEVICE IDENT FAILED!');
exit;
end;
masterDevice.hpc:= uint32(IDEDevices[0].info[3] DIV IDEDevices[0].info[1]);
masterDevice.sectorSize:= 512;
if masterDevice.maxSectorCount <> 0 then begin
IDEDevices[0].exists:= true;
masterDevice.readCallback:= @dread;
masterDevice.writeCallback:= @dwrite;
storagemanagement.register_device(@masterDevice);
end;
end;
// buffer:= puint8(kalloc(512));
// buffer[0] := 1;
// buffer[1] := 2;
// buffer[2] := 3;
// buffer[3] := 4;
// buffer[4] := 5;
// buffer[5] := 6;
// writePIO28(0, 3, buffer);
// writePIO28(0, 3, buffer);
// writePIO28(0, 3, buffer);
// writePIO28(0, 4, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// psleep(1000);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// kfree(puint32(buffer));
console.writestringln('[IDE] (LOAD) END');
end;
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8);
var
status : uint8;
i: uint16;
device: uint8;
data: uint16;
begin
if not validate_28bit_address(LBA) then begin
console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!');
end;
//Add last 4 bits of LBA to device port
if IDEDevices[drive].isMaster then begin
device:= ATA_DEVICE_MASTER;
device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master
end
else begin
device:= ATA_DEVICE_SLAVE;
device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave
end;
no_interrupt(device);
port_write(ATA_REG_ERROR, 0);
//Write sector count and LBA
port_write(ATA_REG_SECCOUNT, 1);
port_write(ATA_REG_LBA0, (LBA and $000000FF));
port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8);
port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16);
//send read command
port_write(ATA_REG_COMMAND, ATA_CMD_READ_PIO);
if not is_ready() then exit;
i:=0;
while i < 512 do begin
// if not is_ready() then exit;
data:= inw(ATA_PRIMARY_BASE + ATA_REG_DATA);
buffer[i+1] := uint8($00ff and (data shr 8));
buffer[i] := uint8($00ff and data);
i:= i + 2;
end;
end;
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8);
var
status : uint8;
i: uint16;
device: uint8;
begin
push_trace('IDE.WritePIO28');
if not validate_28bit_address(LBA) then begin
console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!');
end;
console.writeintln(uint32(drive));
console.writeintln(LBA);
//Add last 4 bits of LBA to device port
if IDEDevices[drive].isMaster then begin
device:= ATA_DEVICE_MASTER;
device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master
end
else begin
device:= ATA_DEVICE_SLAVE;
device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave
end;
// no_interrupt(device);
port_write(ATA_REG_ERROR, 0);
port_write(ATA_REG_CONTROL, 0);
// check if bus is floating
status := port_read(ATA_REG_COMMAND);
if status = $FF then exit;
//Write sector count and LBA
port_write(ATA_REG_SECCOUNT, 1);
port_write(ATA_REG_LBA0, (LBA and $000000FF));
port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8);
port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16);
//send write command
port_write(ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
//write data
i:=0;
while i < 512 do begin
outw(ATA_PRIMARY_BASE + ATA_REG_DATA, uint16(buffer[i] or (buffer[i+1] shl 8)));
i:= i + 2;
end;
//flush drive cache
psleep(1);
port_write(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
psleep(1);
if not is_ready() then exit;
end;
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32);
var
i : uint16;
begin
for i:=0 to sectorCount-1 do begin
readPIO28(device^.controllerId0, LBA, puint8(@buffer[512*i]));
end;
end;
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32);
var
i : uint16;
begin
for i:=0 to sectorCount-1 do begin
writePIO28(device^.controllerId0, LBA, puint8(@buffer[512*i]));
end;
// writePIO28(device^.controllerId0, LBA, puint8(buffer));
end;
end.

View File

@ -0,0 +1,81 @@
{
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 : array[0..3] of 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.

221
src/driver/storage/ata.pas Normal file
View File

@ -0,0 +1,221 @@
unit ata;
interface
uses
util,
drivertypes,
console,
terminal,
drivermanagement,
vmemorymanager,
lmemorymanager,
storagemanagement,
strings,
tracer,
drivemanager,
storagetypes,
idetypes;
var
test : uint32;
function identify_device(var device : TIDE_Device) : Boolean;
function read_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
implementation
uses
ide;
{
ensure the address is a valid 28 bit address
@param(addr The address to validate)
@returns(@True if the address is valid, @False otherwise)
}
function validate_28bit_address(addr : uint32) : boolean;
begin
validate_28bit_address := (addr and $F0000000) = 0;
end;
{
Identify the device on the IDE bus
@param(device The device to identify)
@returns(@True if the device is identified, @False otherwise)
}
function identify_device(var device : TIDE_Device) : Boolean;
var
i : uint8;
status : TIDE_Status;
buffer : TIdentResponse;
ready : boolean;
begin
select_device(device);
no_interrupt(device.isPrimary);
status := get_status(device);
outb(device.base + ATA_REG_SECCOUNT, 0);
outb(device.base + ATA_REG_LBA0, 0);
outb(device.base + ATA_REG_LBA1, 0);
outb(device.base + ATA_REG_LBA2, 0);
outb(device.base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
status := get_status(device);
if status.ERROR then begin
console.writestringln('Error identifying device, maybe'); //todo
end;
ready := wait_for_device(device);
if not ready then begin
console.writestringln('Device not ready in time!');
//teapot time
// BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR IDENT');
exit(false);
end;
for i:=0 to 255 do begin
buffer[i] := inw(device.base + ATA_REG_DATA);
end;
device.info := @buffer;
identify_device := true;
end;
procedure set_lba_mode(device : TIDE_Device; lba : uint32);
begin
// if device.isPrimary then begin
if device.isMaster then begin
outb(device.base + ATA_REG_HDDEVSEL, $E0 or ((lba and $0F000000) shr 24));
end else begin
outb(device.base + ATA_REG_HDDEVSEL, $F0 or ((lba and $0F000000) shr 24));
end;
// end else begin //TODO
// if device.isMaster then begin
// outb(device.base + ATA_REG_HDDEVSEL, $A0);
// end else begin
// outb(device.base + ATA_REG_HDDEVSEL, $B0);
// end;
// end;
end;
function read_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
var
i : uint16;
ii : uint16;
status : TIDE_Status;
ready : boolean;
begin
push_trace('ata.read_pio28()');
if not validate_28bit_address(lba) then begin
console.writestringln('Invalid address for 28 bit read');
read_pio28 := false;
exit;
end;
select_device(device);
no_interrupt(device.isPrimary);
set_lba_mode(device, lba);
outb(device.base + ATA_REG_SECCOUNT, count);
outb(device.base + ATA_REG_LBA0, (lba and $000000FF));
outb(device.base + ATA_REG_LBA1, (lba and $0000FF00) shr 8);
outb(device.base + ATA_REG_LBA2, (lba and $00FF0000) shr 16);
outb(device.base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
ready := wait_for_device(device);
if not ready then begin
console.writestringln('Device not ready in time!');
BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR READ COMMAND');
end;
for i:=0 to count-1 do begin
for ii:=0 to 511 do begin //TODO needs to read sector size
buffer[i] := inw(device.base + ATA_REG_DATA);
ready := wait_for_device(device);
if not ready then begin
console.writestringln('Device not ready in time!');
BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR READ');
end;
end;
end;
read_pio28 := true;
pop_trace();
end;
function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
var
i : uint16;
ii : uint16;
status : TIDE_Status;
ready : boolean;
begin
push_trace('ata.write_pio28()');
if not validate_28bit_address(lba) then begin
console.writestringln('Invalid address for 28 bit write');
write_pio28 := false;
exit;
end;
select_device(device);
no_interrupt(device.isPrimary); //maybe not?
set_lba_mode(device, lba);
outb(device.base + ATA_REG_SECCOUNT, count);
outb(device.base + ATA_REG_LBA0, (lba and $000000FF));
outb(device.base + ATA_REG_LBA1, (lba and $0000FF00) shr 8);
outb(device.base + ATA_REG_LBA2, (lba and $00FF0000) shr 16);
outb(device.base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ready := wait_for_device(device);
if not ready then begin
console.writestringln('Device not ready in time!');
BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR WRITE COMMAND');
end;
for i:=0 to count-1 do begin
for ii:=0 to 511 do begin //TODO needs to read sector size
outw(device.base + ATA_REG_DATA, uint16(buffer[i]));
ready := wait_for_device(device);
if not ready then begin
console.writestringln('Device not ready in time!');
BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR WRITE');
end;
end;
end;
psleep(50); //todo check
outb(device.base + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
ready := wait_for_device(device);
// if not ready then begin
// console.writestringln('Device not ready in time!');
// BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR CACHE FLUSH');
// end;
write_pio28 := true;
pop_trace();
end;
end.

View File

View File

@ -0,0 +1,306 @@
// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
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,
volumemanager,
storagetypes;
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;
{ Disk subcommand for formatting drives }
procedure format_command(params : PParamList);
var
driveIndex : uint16;
drive : PStorage_Device;
filesystemString : pchar;
spc : puint32;
sectorCount : uint32;
volumeId : uint8;
mb : PMaster_Boot_Record;
i : uint32 = 0;
begin
push_trace('DriverManager.format_command');
//format <DRIVE_ID> <FILESYSTEM> <size>
spc:= puint32(kalloc(4));
spc^:= 1;
push_trace('DriverManager.format_command.0');
//get drive from index/id
driveIndex:= stringToInt( getParam(1, params) );
drive:= PStorage_Device(LL_Get(storageDevices, driveIndex));
filesystemString := getParam(2, params);
sectorCount := stringToInt( getParam(3, params) );
// if sector count is 0, use full drive
if sectorCount = 0 then begin
sectorCount := drive^.maxSectorCount - 10;
end;
//create MBR if none, and partition table
mb := PMaster_Boot_Record(kalloc(sizeof(TMaster_Boot_Record)*2));
memset(uint32(mb), 0, sizeof(TMaster_Boot_Record));
drive^.readCallback(drive, 0, 1, PuInt32(mb));
//check if MBR exists
if (mb^.boot_sector <> $55AA)then begin
writestringlnWND('Creating MBR', getTerminalHWND());
//create MBR
mb^.signature := $A570 + drive^.id;
mb^.boot_sector := $55AA;
//create partition table
mbr.setup_partition(@mb^.partition[0], 2, sectorCount);
//write MBR
drive^.writeCallback(drive, 0, 1, puint32(mb));
end else begin
writestringlnWND('MBR already exists', getTerminalHWND());
//write first partiton sectorStart and sectorCount
writeintlnWND(mb^.partition[0].LBA_start, getTerminalHWND());
writeintlnWND(mb^.partition[0].sector_count, getTerminalHWND());
//create partition table
mbr.setup_partition(@mb^.partition[0], 2, sectorCount);
//write MBR
drive^.writeCallback(drive, 0, 1, puint32(mb));
end;
kfree(puint32(mb));
//setup volume
volumemanager.create_volume(drive, filesystemString, 2, sectorCount);
writestringWnd('Drive ', getTerminalHWND);
writeintWnd(driveIndex, getTerminalHWND);
writestringlnWnd(' formatted.', getTerminalHWND);
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);
push_trace('DriverManager.drive_command.0');
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());
console.writestringlnWnd(' format [drive] [filesystem] [size (0 for max)] - destructive, formats a drive with MBR and volume', getTerminalHWND());
exit;
end;
push_trace('DriverManager.drive_command.1');
if( stringEquals(subcmd, 'ls') ) then begin
list_drive_command();
exit;
end;
push_trace('DriverManager.drive_command.2');
if( stringEquals(subcmd, 'info') ) then begin
console.writestringWnd('Disk: ', getTerminalHWND());
console.writeintlnWND(1, getTerminalHWND());
console.writestringWnd('Capacity: ', getTerminalHWND());
console.writeintlnWND(1, getTerminalHWND());
//TODO impliment
exit;
end;
if( stringEquals(subcmd, 'init') ) then begin
init_drive_command(stringToInt(getParam(1, params) ) );
exit;
end;
push_trace('DriverManager.drive_command.3');
if( stringEquals(subcmd, 'format') ) then begin
format_command(params);
exit;
end;
push_trace('DriverManager.drive_command.4');
end;
{ Initialise the drive manager }
procedure init();
begin
push_trace('DriveManager.init');
//setup drive manager
setworkingdirectory('.');
storageDevices:= ll_new(sizeof(TStorage_Device));
//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
memcpy(uint32(device), uint32(elm), SizeOf(TStorage_Device));
//set drive id in struct
PStorage_device(elm)^.id := LL_Size(storageDevices) - 1;
//create empty volume list for drive
PStorage_device(elm)^.volumes := LL_New(sizeof(TStorage_Volume));
//TODO register with volume manager
//volumemanager.check_for_volumes(PStorage_Device(elm));
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
ControllerATA : controller_type_2_string:= 'ATA';
ControllerATAPI : controller_type_2_string:= 'ATAPI';
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.

View File

@ -10,14 +10,16 @@ interface
uses
console,
storagemanagement,
util, terminal,
util,
terminal,
lmemorymanager,
strings,
lists,
tracer,
serial,
rtc;
rtc,
volumemanager,
storagetypes;
type
@ -89,7 +91,7 @@ var
filesystem : TFilesystem;
procedure init;
procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32);
procedure detect_volumes(disk : PStorage_Device);
//function writeDirectory(volume : PStorage_volume; directory : pchar; attributes : uint32) : uint8; // need to handle parent table cluster overflow, need to take attributes
//function readDirectory(volume : PStorage_volume; directory : pchar; listPtr : PLinkedListBase) : uint8; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error
@ -751,7 +753,7 @@ end;
//TODO check directory commands for errors with a clean disk
procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32);
var
buffer : puint32;
bt: puint32;
@ -772,9 +774,18 @@ var
programFileArray : byteArray8 = ('P','R','O','G','R','A','M','S');
rootCluster : uint32 = 1;
sysArray : byteArray8 = ('S','Y','S','T','E','M',' ',' ');
progArray : byteArray8 = ('P','R','O','G','R','A','M','S');
userArray : byteArray8 = ('U','S','E','R',' ',' ',' ',' ');
status : puint32;
disk : PStorage_device;
begin //maybe increase buffer size by one?
push_trace('fat32.create_volume()');
disk := volume^.device;
// zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 ));
// memset(uint32(zeroBuffer), 0, disk^.sectorSize * 4);
@ -811,7 +822,7 @@ begin //maybe increase buffer size by one?
bootRecord^.jmp2boot := $0; //TODO impliment boot jump
bootRecord^.OEMName := asuroArray;
bootRecord^.sectorSize := disk^.sectorsize;
bootRecord^.spc := config^;
bootRecord^.spc := 1;
bootRecord^.rsvSectors := 32; //32 is standard
bootRecord^.numFats := 1;
bootRecord^.mediaDescp := $F8;
@ -873,8 +884,16 @@ begin //maybe increase buffer size by one?
disk^.writecallback(disk, dataStart + (config^ * rootCluster), 1, buffer);
memset(uint32(buffer), 0, disk^.sectorsize);
status := puint32(kalloc(sizeof(uint32)));
writeDirectory(volume, '', sysArray, $10, status, '');
kfree(buffer);
end;
procedure detect_volumes(disk : PStorage_Device);
@ -903,11 +922,11 @@ begin
if (puint32(buffer)[127] = $55AA) and (PBootRecord(buffer)^.bsignature = $29) then begin //TODO partition table
console.writestringln('FAT32: volume found!');
volume^.device:= disk;
volume^.sectorStart:= 1;
volume^.sectorStart:= 1; //THIS MAKE SURE IF NOT FUCKY, cos im getting info from 2
volume^.sectorSize:= PBootRecord(buffer)^.sectorSize;
volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first
volume^.filesystem := @filesystem;
storagemanagement.register_volume(disk, volume);
// storagemanagement.register_volume(disk, volume); TODO repalce with new thing
end;
kfree(buffer);
@ -917,6 +936,7 @@ procedure init();
begin
push_trace('fat32.init()');
filesystem.sName:= 'FAT32';
filesystem.system_id:= $01;
filesystem.readDirCallback:= @readDirectoryGen;
filesystem.createDirCallback:= @writeDirectoryGen;
filesystem.createcallback:= @create_volume;
@ -924,7 +944,7 @@ begin
filesystem.writecallback:= @writeFile;
filesystem.readcallback := @readFile;
storagemanagement.register_filesystem(@filesystem);
volumemanager.register_filesystem(@filesystem);
end;
end.

View File

@ -0,0 +1,725 @@
// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
Driver->Storage->flatfs super simple flat filesystem
@author(Aaron Hance ah@aaronhance.me)
}
unit flatfs;
interface
uses
tracer,
strings,
volumemanager,
lists,
console,
terminal,
storagetypes,
lmemorymanager,
util;
type
TDisk_Info = bitpacked record
jmp2boot: ubit24;
OEMName: array[0..7] of char;
version: uint16;
sectorCount : uint16;
fileCount : uint16;
signature : uint32;
end;
PDisk_Info = ^TDisk_Info;
TFile_Entry = bitpacked record
attribues : uint8; // 0x00 = does not exsist, 0x01 = file, 0x02 = hidden, 0x04 = system/read only, 0x10 = directory, 0x20 = archive
name: array[0..53] of char; //max file name length is 54
rsv : uint8;
size: uint32;
start : uint32;
end;
PFile_Entry = ^TFile_Entry;
var
filesystem : TFilesystem;
procedure init;
procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32);
procedure detect_volumes(disk : PStorage_Device);
function read_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase;
procedure write_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32);
procedure write_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; size : uint32; status : PuInt32);
procedure read_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; status : PuInt32);
implementation
procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32);
var
info : PDisk_Info;
entryTable : PFile_Entry;
i : uint32;
directories : PLinkedListBase;
status : PuInt32;
data : pchar;
const
asuroArray : array[0..7] of char = 'ASURO';
systemArray : array[0..6] of char = '/system';
programsArray : array[0..8] of char = '/programs';
userArray : array[0..4] of char = '/user';
testArray : array[0..23] of char = 'ASURO TEST DATA ARRAY';
begin
info := PDisk_Info(Kalloc(512));
memset(uint32(info), 0, 512);
info^.jmp2boot := $0;
info^.OEMName := asuroArray;
info^.version := 1;
info^.sectorCount := sectors;
info^.fileCount := 1000;
info^.signature := $0B00B1E5;
if config^ <> 0 then begin
info^.fileCount := config^;
end;
volume^.device^.writeCallback(volume^.device, start, 1, PuInt32(info));// what happens if buffer is smaller than 512?
// create file table
entryTable := PFile_Entry(Kalloc(126 * 512));
memset(uint32(entryTable), 0, 126 * 512);
//create system folders
entryTable[0].attribues := $10;
memcpy(uint32(@systemArray), uint32(@entryTable[0].name), 7);
entryTable[0].size := 0;
entryTable[0].start := 0;
entryTable[1].attribues := $10;
memcpy(uint32(@programsArray), uint32(@entryTable[1].name), 9);
entryTable[1].size := 0;
entryTable[1].start := 0;
entryTable[2].attribues := $10;
memcpy(uint32(@userArray), uint32(@entryTable[2].name), 5);
entryTable[2].size := 0;
entryTable[2].start := 0;
//write file table
volume^.device^.writeCallback(volume^.device, start + 1, ((sizeof(TFile_Entry) * info^.fileCount) div 512), puint32(entryTable));
//volume^.device^.writeCallback(volume^.device, start + 1, 1, puint32(entryTable));
//temp read
status := PuInt32(Kalloc(sizeof(uint32)));
// directories := read_directory(volume, '/', status);
data := stringNew(1024);
memset(uint32(data), 0, 513);
memcpy(uint32(@testArray), uint32(data), 23);
write_directory(volume, '/logs', status);
write_directory(volume, '/logs/test', status);
write_file(volume, '/logs/log.txt', PuInt32(data), 1, status);
write_file(volume, '/logs/log2.txt', PuInt32(data), 1, status);
write_file(volume, '/logs/log3.txt', PuInt32(data), 1, status);
read_directory(volume, '/logs', status);
kfree(puint32(data));
read_file(volume, '/logs/log.txt', PuInt32(data), status);
writestringlnWND(pchar(data), getterminalhwnd());
end;
procedure write_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32);
var
directories : PLinkedListBase;
i : uint32;
fileEntries : PFile_Entry;
fileEntry : PFile_Entry;
buffer : PuInt32;
position : uint32;
offset : uint32;
const
fileCount : uint32 = 1000;
begin
status^ := 0;
//directories := read_directory(volume, directory, status);
fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount + 512);
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, (sizeof(TFile_Entry) * fileCount) div 512, PuInt32(fileEntries));
if status^ = 0 then begin
for i := 0 to fileCount - 1 do begin
push_trace('write_directory_2');
//fileEntry := PFile_Entry(STRLL_Get(directories, i));
fileEntry := @fileEntries[i];
if fileEntry^.attribues = 0 then begin
fileEntry^.attribues := $10;
fileEntry^.size := 0;
fileEntry^.start := 0;
memcpy(uint32(directory), uint32(@fileEntry^.name), stringSize(directory));
push_trace('write_directory_3');
//write file table
buffer := PuInt32(Kalloc(513));
memset(uint32(buffer), 0, 512);
push_trace('write_directory.b4_read');
//read file table cointaining entry of interest
position := (i * sizeof(TFile_Entry)) div 512;
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1 + position, 1, buffer);
push_trace('write_directory.after_read');
//calculate entry offset into sector
offset := (i * sizeof(TFile_Entry)) mod 512;
offset := offset div sizeof(TFile_Entry);
//set entry in buffer
PFile_Entry(buffer)[offset] := fileEntry^;
//write updated file table sector
volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 + position, 1, buffer);
Kfree(buffer);
push_trace('write_directory.after_write');
break;
end;
end;
end;
if fileEntry^.attribues = 0 then begin
status^ := 1;
end;
end;
function read_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase;
var
directories : PLinkedListBase;
fileEntrys : PFile_Entry;
i : uint32;
compString : PChar;
afterString : PChar;
slash : PChar;
compStringLength : uint32;
fileCount : uint32 = 1000;
slashArray : array[0..1] of char = '/';
begin
compString := pchar(kalloc(60));
memset(uint32(compString), 0, 60);
afterString := pchar(kalloc(60));
memset(uint32(afterString), 0, 60);
slash := stringNew(2);
memcpy(uint32(@slashArray), uint32(slash), 1);
directories := STRLL_New();
fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys));
for i := 0 to fileCount - 1 do begin
if uint32(fileEntrys[i].name[0]) = 0 then begin
break;
end;
compString := stringSub(fileEntrys[i].name, 0, stringSize(directory));
afterString := stringSub(fileEntrys[i].name, stringSize(directory), stringSize(fileEntrys[i].name)-1);
writestringlnWND(afterString, getterminalhwnd());
push_trace('read_directory_2');
if stringEquals(compString, directory) and
(not stringContains(afterString, slash)) then begin
writestringlnWND(fileEntrys[i].name, getterminalhwnd());
STRLL_Add(directories, afterString);
end;
end;
push_trace('read_directory_3');
Kfree(puint32(fileEntrys));
read_directory := directories;
end;
procedure quicksort_start(list : PFile_Entry; begining : uint32; stop : uint32);
var
pivot : uint32;
i : uint32;
j : uint32;
temp : PLinkedListBase;
tempFileEntry : PFile_Entry;
begin
tempFileEntry := PFile_Entry(Kalloc(sizeof(TFile_Entry)));
memset(uint32(tempFileEntry), 0, sizeof(TFile_Entry));
i:=begining;
j:=stop;
pivot := list[(begining + stop) div 2].start;
while i <= j do begin
while list[i].start < pivot do begin
i := i + 1;
end;
while list[j].start > pivot do begin
j := j - 1;
end;
if i <= j then begin
tempFileEntry^ := list[i];
list[i] := list[j];
list[j] := tempFileEntry^;
i := i + 1;
j := j - 1;
end;
end;
if begining < j then begin
quicksort_start(list, begining, j);
end;
if i < stop then begin
quicksort_start(list, i, stop);
end;
Kfree(puint32(tempFileEntry));
end;
function find_free_space(volume : PStorage_Volume; size : uint32; status : PuInt32) : uint32;
var
fileEntrys : PFile_Entry;
confirmedFileEntrys : PFile_Entry;
confirmedFileEntrysCount : uint32 = 0;
fileEntry : PFile_Entry;
i : uint32;
fileCount : uint32 = 1000;
baseAddress : uint32;
begin
fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
confirmedFileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
memset(uint32(fileEntrys), 0, sizeof(TFile_Entry) * fileCount + 512);
memset(uint32(confirmedFileEntrys), 0, sizeof(TFile_Entry) * fileCount + 512);
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys));
baseAddress := volume^.sectorStart + 2 + ( (fileCount * sizeof(TFile_Entry)) div 512);
for i := 0 to fileCount - 1 do begin
if fileEntrys[i].attribues <> 0 then begin
confirmedFileEntrys[confirmedFileEntrysCount] := fileEntrys[i];
confirmedFileEntrysCount := confirmedFileEntrysCount + 1;
end;
end;
//sort file table by start address using quicksort
quicksort_start(confirmedFileEntrys, 0, confirmedFileEntrysCount - 1);
//find free space big enough to fit size //TODO: make this work
for i := 0 to confirmedFileEntrysCount - 1 do begin
if confirmedFileEntrys[i+1].attribues = 0 then begin
if confirmedFileEntrys[i].size = 0 then begin
find_free_space := baseAddress;
break;
end else begin
find_free_space := confirmedFileEntrys[i].start + confirmedFileEntrys[i].size;
break;
end;
break;
end;
if confirmedFileEntrys[i].size <> 0 then begin
if confirmedFileEntrys[i+1].start - (confirmedFileEntrys[i].start + confirmedFileEntrys[i].size) > size+1 then begin
find_free_space := confirmedFileEntrys[i].start + confirmedFileEntrys[i].size;
status^ := 0;
break;
end;
end;
end;
Kfree(puint32(fileEntrys));
Kfree(puint32(confirmedFileEntrys));
end;
procedure write_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; size : uint32; status : PuInt32);
var
directories : PLinkedListBase;
i : uint32;
p : uint32;
fileEntries : PFile_Entry;
fileEntry : PFile_Entry;
buffer : PuInt32;
position : uint32;
offset : uint32;
fileCount : uint32 = 1000;
exsists : boolean;
elm : puint32;
begin
push_trace('flatfs.write_file');
//load file table
fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount);
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries));
//check if file exists else find free entry
for i := 0 to fileCount - 1 do begin
if stringEquals(fileEntries[i].name, fileName) then begin
exsists := true;
p:= i;
break;
end;
if fileEntries[i].attribues = 0 then begin
exsists := false;
p:= i;
break;
end;
end;
//if entry is directory then exit
if fileEntries[p].attribues and $10 = $10 then begin
status^ := 1;
exit;
end;
// //read file table cointaining entry of interest
position := (p * sizeof(TFile_Entry)) div 512;
//if file exists update entry else create new entry
if exsists then begin
//check if size is greater than current size
if size > fileEntries[p].size then begin
//find free space
fileEntries[p].start := find_free_space(volume, size, status);
fileEntries[p].size := size;
end else begin
//update size
fileEntries[p].size := size;
end;
end else begin
//find free space
fileEntries[p].start := find_free_space(volume, size, status);
fileEntries[p].size := size;
fileEntries[p].attribues := $01;
//add file name
memcpy(uint32(fileName), uint32(@fileEntries[p].name), 14);
end;
//write file table
volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position]));
//write file
volume^.device^.writeCallback(volume^.device, fileEntries[p].start, size, data);
//free buffer
Kfree(puint32(fileEntries));
end;
procedure read_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; status : PuInt32);
var
directories : PLinkedListBase;
i : uint32;
p : uint32;
fileEntries : PFile_Entry;
fileEntry : PFile_Entry;
fileCount : uint32 = 1000;
exsists : boolean = false;
size : uint32;
begin
//load file table
fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount);
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries));
//check if file exists else exit
for i := 0 to fileCount - 1 do begin
if stringEquals(fileEntries[i].name, fileName) then begin
exsists := true;
p:= i;
break;
end;
end;
//if entry is directory then exit
if fileEntries[p].attribues and $10 = $10 then begin
status^ := 1;
exit;
end;
//if entry is not found exit
if fileEntries[i].attribues = 0 then begin
exsists := false;
p:= i;
exit;
end;
//file size rounded up to nearest 512
size := (fileEntries[p].size + 511) and -512;
//read file
data := Kalloc(size);
volume^.device^.readCallback(volume^.device, fileEntries[p].start, size, data);
//free buffer
Kfree(puint32(fileEntries));
status^ := 0;
end;
procedure detect_volumes(disk : PStorage_Device);
var
buffer : puint32;
volume : PStorage_volume;
begin
push_trace('flatfs.detectVolumes()');
volume:= PStorage_volume(kalloc(sizeof(TStorage_Volume)));
//check first address for MBR
//if found then add volume and use info to see if there is another volume
buffer := puint32(kalloc(512));
memset(uint32(buffer), 0, 512);
disk^.readcallback(disk, 2, 1, buffer);
if (PDisk_Info(buffer)^.signature = $0B00B1E5) then begin
console.writestringln('FlatFS: volume found!');
volume^.device:= disk;
volume^.sectorStart:= 2;
volume^.sectorSize:= 512;
volume^.sectorCount:= PDisk_Info(buffer)^.sectorCount;
volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first
volume^.filesystem := @filesystem;
volume^.isBootDrive := false;
// storagemanagement.register_volume(disk, volume); TODO repalce with new thing
end;
kfree(buffer);
end;
procedure delete_file(volume : PStorage_Volume; fileName : pchar; status : PuInt32);
var
directories : PLinkedListBase;
i : uint32;
p : uint32;
fileEntries : PFile_Entry;
fileEntry : PFile_Entry;
zeroBuffer : PuInt32;
position : uint32;
offset : uint32;
fileCount : uint32 = 1000;
exsists : boolean;
begin
//load file table
fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount);
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries));
//check if file exists else exit
for i := 0 to fileCount - 1 do begin
if stringEquals(fileEntries[i].name, fileName) then begin
exsists := true;
p:= i;
break;
end;
end;
//set position of entry
position := (p * sizeof(TFile_Entry)) div 512;
//if entry is directory then exit
if fileEntries[p].attribues and $10 = $10 then begin
status^ := 1;
exit;
end;
//if entry is not found exit
if fileEntries[i].attribues = 0 then begin
exsists := false;
p:= i;
exit;
end;
//zero out file
zeroBuffer := Kalloc(fileEntries[p].size);
memset(uint32(zeroBuffer), 0, fileEntries[p].size);
//write file
volume^.device^.writeCallback(volume^.device, fileEntries[p].start, fileEntries[p].size, zeroBuffer);
//free buffer
Kfree(puint32(zeroBuffer));
//zero out file table
memset(uint32(@fileEntries[p]), 0, sizeof(TFile_Entry));
//write file table
volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position]));
//free buffer
Kfree(puint32(fileEntries));
status^ := 0;
end;
procedure delete_directory(volume : PStorage_Volume; directoryName : pchar; status : PuInt32);
var
directories : PLinkedListBase;
i : uint32;
p : uint32;
fileEntries : PFile_Entry;
fileEntry : PFile_Entry;
zeroBuffer : PuInt32;
position : uint32;
offset : uint32;
fileCount : uint32 = 1000;
exsists : boolean;
begin
//load file table
fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512));
memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount);
volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries));
//check if file exists else exit
for i := 0 to fileCount - 1 do begin
if stringEquals(fileEntries[i].name, directoryName) then begin
exsists := true;
p:= i;
break;
end;
end;
//if entry is not directory then exit
if fileEntries[i].attribues and $10 <> $10 then begin
status^ := 1;
exit;
end;
//calculate position of directory in file table
position := (p * sizeof(TFile_Entry)) div 512;
//zero file table
memset(uint32(@fileEntries[p]), 0, sizeof(TFile_Entry));
//write file table
volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position]));
//free buffer
Kfree(puint32(fileEntries));
status^ := 0;
end;
procedure init();
begin
push_trace('flatfs.init()');
filesystem.sName:= 'flatfs';
filesystem.system_id:= $02;
filesystem.readDirCallback:= @read_directory;
// filesystem.createDirCallback:= @writeDirectoryGen;
filesystem.createcallback:= @create_volume;
filesystem.detectcallback:= @detect_volumes;
// filesystem.writecallback:= @writeFile;
// filesystem.readcallback := @readFile;
// filesystem.formatVolumeCallback := @create_volume;
volumemanager.register_filesystem(@filesystem);
end;
end.

View File

@ -0,0 +1,140 @@
// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
Driver->Storage->IDE->idetypes - IDE device types and constants.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit idetypes;
interface
const
ATA_SR_BUSY = $80; //BUSY
ATA_SR_DRDY = $40; //DRIVE READY
ATA_SR_DF = $20; //DRIVE WRITE FAULT
ATA_SR_DSC = $10; //DRIVE SEEK COMPLETE
ATA_SR_DRQ = $08; //DATA REQUEST READY
ATA_SR_CORR = $04; //CORRECTED DATA
ATA_SR_IDX = $02; //INLEX
ATA_SR_ERR = $01; //ERROR
ATA_ER_BBK = $80; //BAD SECTOR
ATA_ER_UNC = $40; //UNCORRECTABLE DATA
ATA_ER_MC = $20; //NO MEDIA
ATA_ER_IDNF = $10; //ID MARK NOT FOUND
ATA_ER_MCR = $08; //NO MEDIA
ATA_ER_ABRT = $04; //COMMAND ABORTED
ATA_ER_TK0NF = $02; //TRACK 0 NOT FOUND
ATA_ER_AMNF = $01; //NO ADDRESS MARK
ATA_CMD_READ_PIO = $20;
ATA_CMD_READ_PIO_EXT = $24;
ATA_CMD_READ_DMA = $C8;
ATA_CMD_READ_DMA_EXT = $25;
ATA_CMD_WRITE_PIO = $30;
ATA_CMD_WRITE_PIO_EXT = $34;
ATA_CMD_WRITE_DMA = $CA;
ATA_CMD_WRITE_DMA_EXT = $35;
ATA_CMD_CACHE_FLUSH = $E7;
ATA_CMD_CACHE_FLUSH_EXT = $EA;
ATA_CMD_PACKET = $A0;
ATA_CMD_IDENTIFY_PACKET = $A1;
ATA_CMD_IDENTIFY = $EC;
ATAPI_CMD_READ = $A8;
ATAPI_CMD_EJECT = $1B;
ATA_IDENT_DEVICETYPE = $0;
ATA_IDENT_CYLINDERS = $2;
ATA_IDENT_HEADS = $6;
ATA_IDENT_SECOTRS = $12;
ATA_IDENT_SERIAL = $20;
ATA_IDENT_MODEL = $54;
ATA_IDENT_CAPABILITIES = $98;
ATA_IDENT_FIELDVALID = $106;
ATA_IDENT_MAX_LBA = $120;
ATA_IDENT_COMMANDSETS = $164;
ATA_IDENT_MAX_LBA_EXT = $200;
ATA_REG_DATA = $00;
ATA_REG_ERROR = $01;
ATA_REG_FEATURES = $01;
ATA_REG_SECCOUNT = $02;
ATA_REG_LBA0 = $03;
ATA_REG_LBA1 = $04;
ATA_REG_LBA2 = $05;
ATA_REG_HDDEVSEL = $06;
ATA_REG_COMMAND = $07;
ATA_REG_STATUS = $07;
ATA_REG_SECCOUNT1 = $08;
ATA_REG_LBA3 = $09;
ATA_REG_LBA4 = $0A;
ATA_REG_LBA5 = $0B;
ATA_REG_CONTROL = $0C;
ATA_REG_ALTSTATUS = $0C;
ATA_REG_DEVADDRESS = $0D;
ATA_DEVICE_MASTER = $A0;
ATA_DEVICE_SLAVE = $B0;
ATA_PRIMARY_BASE = $1F0;
ATA_PRIMARY_BASE1 = $3F6;
ATA_SECONDARY_BASE = $170; //todo check this
ATA_SECONDARY_BASE1 = $376; //todo check this
ATA_INTERRUPT_PRIMARY = $3F6;
ATA_INTERRUPT_SECONDARY = $376;
type
TPortMode = (P_READ, P_WRITE);
TIdentResponse = array[0..255] of uint16;
PIdentResponse = ^TIdentResponse;
TIDE_Channel_Registers = record
base : uint16;
ctrl : uint16;
bmide : uint16;
noInter : uint8
end;
TIDE_Status = bitpacked record
Busy : Boolean;
Ready : Boolean;
Fault : Boolean;
Seek : Boolean;
DRQ : Boolean;
CORR : Boolean;
IDDEX : Boolean;
ERROR : Boolean;
end;
PIDE_Status = ^TIDE_Status;
TIDE_Device = record
exists : boolean;
isPrimary : boolean;
isMaster : boolean;
isATAPI : boolean;
status : TIDE_Status;
base : uint16;
info : PIdentResponse;
end;
implementation
end.

View File

@ -0,0 +1,504 @@
{ ************************************************
* Asuro
* Unit: Drivers/storage/IDE
* Description: IDE ATA Driver
*
************************************************
* Author: Aaron Hance
* Contributors:
************************************************ }
unit IDE;
interface
uses
util,
drivertypes,
console,
terminal,
drivermanagement,
vmemorymanager,
lmemorymanager,
storagemanagement,
strings,
tracer,
drivemanager,
storagetypes;
type
TPortMode = (P_READ, P_WRITE);
TIdentResponse = array[0..255] of uint16;
TIDE_Channel_Registers = record
base : uint16;
ctrl : uint16;
bmide : uint16;
noInter : uint8
end;
TIDE_Device = record
exists : boolean;
isPrimary : boolean;
isMaster : boolean;
isATAPI : boolean;
info : TIdentResponse;
end;
TIDE_Status = bitpacked record
Busy : Boolean;
Ready : Boolean;
Fault : Boolean;
Seek : Boolean;
DRQ : Boolean;
CORR : Boolean;
IDDEX : Boolean;
ERROR : Boolean;
end;
PIDE_Status = ^TIDE_Status;
const
ATA_SR_BUSY = $80; //BUSY
ATA_SR_DRDY = $40; //DRIVE READY
ATA_SR_DF = $20; //DRIVE WRITE FAULT
ATA_SR_DSC = $10; //DRIVE SEEK COMPLETE
ATA_SR_DRQ = $08; //DATA REQUEST READY
ATA_SR_CORR = $04; //CORRECTED DATA
ATA_SR_IDX = $02; //INLEX
ATA_SR_ERR = $01; //ERROR
ATA_ER_BBK = $80; //BAD SECTOR
ATA_ER_UNC = $40; //UNCORRECTABLE DATA
ATA_ER_MC = $20; //NO MEDIA
ATA_ER_IDNF = $10; //ID MARK NOT FOUND
ATA_ER_MCR = $08; //NO MEDIA
ATA_ER_ABRT = $04; //COMMAND ABORTED
ATA_ER_TK0NF = $02; //TRACK 0 NOT FOUND
ATA_ER_AMNF = $01; //NO ADDRESS MARK
ATA_CMD_READ_PIO = $20;
ATA_CMD_READ_PIO_EXT = $24;
ATA_CMD_READ_DMA = $C8;
ATA_CMD_READ_DMA_EXT = $25;
ATA_CMD_WRITE_PIO = $30;
ATA_CMD_WRITE_PIO_EXT = $34;
ATA_CMD_WRITE_DMA = $CA;
ATA_CMD_WRITE_DMA_EXT = $35;
ATA_CMD_CACHE_FLUSH = $E7;
ATA_CMD_CACHE_FLUSH_EXT = $EA;
ATA_CMD_PACKET = $A0;
ATA_CMD_IDENTIFY_PACKET = $A1;
ATA_CMD_IDENTIFY = $EC;
ATAPI_CMD_READ = $A8;
ATAPI_CMD_EJECT = $1B;
ATA_IDENT_DEVICETYPE = $0;
ATA_IDENT_CYLINDERS = $2;
ATA_IDENT_HEADS = $6;
ATA_IDENT_SECOTRS = $12;
ATA_IDENT_SERIAL = $20;
ATA_IDENT_MODEL = $54;
ATA_IDENT_CAPABILITIES = $98;
ATA_IDENT_FIELDVALID = $106;
ATA_IDENT_MAX_LBA = $120;
ATA_IDENT_COMMANDSETS = $164;
ATA_IDENT_MAX_LBA_EXT = $200;
ATA_REG_DATA = $00;
ATA_REG_ERROR = $01;
ATA_REG_FEATURES = $01;
ATA_REG_SECCOUNT = $02;
ATA_REG_LBA0 = $03;
ATA_REG_LBA1 = $04;
ATA_REG_LBA2 = $05;
ATA_REG_HDDEVSEL = $06;
ATA_REG_COMMAND = $07;
ATA_REG_STATUS = $07;
ATA_REG_SECCOUNT1 = $08;
ATA_REG_LBA3 = $09;
ATA_REG_LBA4 = $0A;
ATA_REG_LBA5 = $0B;
ATA_REG_CONTROL = $0C;
ATA_REG_ALTSTATUS = $0C;
ATA_REG_DEVADDRESS = $0D;
ATA_DEVICE_MASTER = $A0;
ATA_DEVICE_SLAVE = $B0;
ATA_PRIMARY_BASE = $1F0;
var
controller : PPCI_Device;
bar0 : uint32;
bar1 : uint32;
bar4 : uint32;
IDEDevices : array[0..3] of TIDE_Device;
buffer : Puint32;
procedure init();
function load(ptr : void) : boolean;
function identify_device(bus : uint8; device : uint8) : TIdentResponse;
// procedure flush();
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8);
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8);
//read/write must be capable of reading/writting any amknt of data upto disk size
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32);
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32);
implementation
function port_read(register : uint8) : uint8;
begin
port_read:= inb(ATA_PRIMARY_BASE + register);
end;
procedure port_write(register : uint8; data : uint8);
var
i : uint8;
begin
outb(ATA_PRIMARY_BASE + register, data);
util.psleep(1);
if register = ATA_REG_COMMAND then begin
for i:= 0 to 5 do begin
port_read(ATA_REG_STATUS);
end;
end;
end;
procedure no_interrupt(device : uint8);
begin
outb($3F6, inb($3f6) or (1 shl 1));
end;
procedure device_select(device : uint8);
begin
outb($1F6, device); //TODO clean
end;
function is_ready() : boolean;
var
status : uint8;
i : uint32;
begin
//wait for drive to be ready
while true do begin
status := port_read(ATA_REG_COMMAND);
if(status and ATA_SR_ERR) = ATA_SR_ERR then begin
console.writestringln('[IDE] (IDENTIFY_DEVICE) DRIVE ERROR!');
console.redrawWindows();
is_ready := false;
break;
end;
if (status and ATA_SR_BUSY) <> ATA_SR_BUSY then begin
is_ready := true;
break;
end;
end;
end;
function validate_28bit_address(addr : uint32) : boolean;
begin
validate_28bit_address := (addr and $F0000000) = 0;
end;
function identify_device(bus : uint8; device : uint8) : TIdentResponse;
var
status : uint8;
identResponse : TIdentResponse;
i : uint8;
begin
push_trace('IDE.Identify_Device');
device_select(device);
no_interrupt(device);
port_write(ATA_REG_CONTROL, 0);
console.writestringln('[IDE] (IDENTIFY_DEVICE) CHECK FLOATING BUS');
//check if bus is floating
status := port_read(ATA_REG_COMMAND);
if status = $FF then exit;
port_write(ATA_REG_SECCOUNT, 0);
port_write(ATA_REG_LBA0, 0);
port_write(ATA_REG_LBA1, 0);
port_write(ATA_REG_LBA2, 0);
console.writestringln('[IDE] (IDENTIFY_DEVICE) SEND IDENTIFY COMMAND');
port_write(ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
console.writestringln('[IDE] (IDENTIFY_DEVICE) WAIT FOR DRIVE TO BE READY');
//check if drive is present
status := port_read(ATA_REG_COMMAND);
if status = $00 then exit;
console.writestringln('[IDE] (IDENTIFY_DEVICE) WAIT FOR DRIVE TO BE READY');
if not is_ready() then exit;
console.writestringln('[IDE] (IDENTIFY_DEVICE) READ IDENTIFY RESPONSE');
for i:=0 to 255 do begin
console.writeint(i);
identResponse[i] := inw(ATA_PRIMARY_BASE + ATA_REG_DATA);
end;
console.writestringln('[IDE] (IDENTIFY_DEVICE) IDENTIFY RESPONSE RECEIVED');
identify_device := identResponse;
end;
procedure init();
var
devID : TDeviceIdentifier;
begin
push_trace('ide.init');
console.writestringln('[IDE] (INIT) BEGIN');
devID.bus:= biPCI;
devID.id0:= idANY;
devID.id1:= $00000001;
devID.id2:= $00000001;
devID.id3:= idANY;
devID.id4:= idANY;
devID.ex:= nil;
drivermanagement.register_driver('IDE ATA Driver', @devID, @load);
console.writestringln('[IDE] (INIT) END');
end;
function load(ptr : void) : boolean;
var
controller : PPCI_Device;
masterDevice : TStorage_Device;
slaveDevice : TStorage_Device;
buffer : puint8;
i : uint8;
test : PStorage_device;
tbuf : puint8;
begin
push_trace('ide.load');
console.writestringln('[IDE] (LOAD) BEGIN');
controller := PPCI_Device(ptr);
console.writestringln('[IDE] (INIT) CHECK FLOATING BUS');
//check if bus is floating and identify device
if inb($1F7) <> $FF then begin
//outb($3F6, inb($3f6) or (1 shl 1)); // disable interrupts
IDEDevices[0].isMaster:= true;
IDEDevices[0].info := identify_device(0, ATA_DEVICE_MASTER);
e
mastrDevice.controller := ControllerIDE;
masterDevice.controllerId0:= 0;
masterDevice.maxSectorCount:= (IDEDevices[0].info[60] or (IDEDevices[0].info[61] shl 16) ); //LBA28 SATA
if IDEDevices[0].info[1] = 0 then begin
console.writestringln('[IDE] (INIT) ERROR: DEVICE IDENT FAILED!');
exit;
end;
// 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);
drivemanager.register_device(@masterDevice);
end;
end;
// buffer:= puint8(kalloc(512));
// buffer[0] := 1;
// buffer[1] := 2;
// buffer[2] := 3;
// buffer[3] := 4;
// buffer[4] := 5;
// buffer[5] := 6;
// writePIO28(0, 3, buffer);
// writePIO28(0, 3, buffer);
// writePIO28(0, 3, buffer);
// writePIO28(0, 4, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// psleep(1000);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// kfree(puint32(buffer));
console.writestringln('[IDE] (LOAD) END');
//read first 16 bytes of disk
tbuf:= puint8(kalloc(512));
//set buffer to 1F1F repeating
for i:=0 to 200 do begin
tbuf[i] := 31;
end;
//write buffer to disk
writePIO28(0, 0, tbuf);
memset(uint32(tbuf), 0, 512);
readPIO28(0, 0, puint8(tbuf));
console.writestringln('[IDE] (INIT) READ FIRST 4 BYTES OF DISK');
console.writehexln(tbuf[0]);
console.writehexln(tbuf[1]);
console.writehexln(tbuf[2]);
console.writehexln(tbuf[3]);
kfree(puint32(tbuf));
end;
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8);
var
status : uint8;
i: uint16;
device: uint8;
data: uint16;
begin
push_trace('IDE.readPIO28');
if not validate_28bit_address(LBA) then begin
console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!');
end;
// push_trace('IDE.readPIO28.2');
//Add last 4 bits of LBA to device port
if IDEDevices[drive].isMaster then begin
device:= ATA_DEVICE_MASTER;
device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master
end
else begin
device:= ATA_DEVICE_SLAVE;
device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave
end;
// push_trace('IDE.readPIO28.3');
no_interrupt(device);
port_write(ATA_REG_ERROR, 0);
//Write sector count and LBA
port_write(ATA_REG_SECCOUNT, 1);
port_write(ATA_REG_LBA0, (LBA and $000000FF));
port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8);
port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16);
// push_trace('IDE.readPIO28.4');
//send read command
port_write(ATA_REG_COMMAND, ATA_CMD_READ_PIO);
if not is_ready() then exit;
i:=0;
while i < 512 do begin
// if not is_ready() then exit;
data:= inw(ATA_PRIMARY_BASE + ATA_REG_DATA);
buffer[i+1] := uint8($00ff and (data shr 8));
buffer[i] := uint8($00ff and data);
i:= i + 2;
if not is_ready() then exit;
end;
// push_trace('IDE.readPIO28.5');
end;
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8);
var
status : uint8;
i: uint16;
device: uint8;
begin
push_trace('IDE.WritePIO28');
if not validate_28bit_address(LBA) then begin
console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!');
end;
console.writeintln(uint32(drive));
console.writeintln(LBA);
//Add last 4 bits of LBA to device port
if IDEDevices[drive].isMaster then begin
device:= ATA_DEVICE_MASTER;
device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master
end
else begin
device:= ATA_DEVICE_SLAVE;
device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave
end;
// no_interrupt(device);
port_write(ATA_REG_ERROR, 0);
port_write(ATA_REG_CONTROL, 0);
// check if bus is floating
status := port_read(ATA_REG_COMMAND);
if status = $FF then exit;
//Write sector count and LBA
port_write(ATA_REG_SECCOUNT, 1);
port_write(ATA_REG_LBA0, (LBA and $000000FF));
port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8);
port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16);
//send write command
port_write(ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
//write data
i:=0;
while i < 512 do begin
outw(ATA_PRIMARY_BASE + ATA_REG_DATA, uint16(buffer[i] or (buffer[i+1] shl 8)));
i:= i + 2;
end;
//flush drive cache
psleep(1);
port_write(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
psleep(1);
if not is_ready() then exit;
end;
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32);
var
i : uint16;
begin
push_trace('IDE.dread');
for i:=0 to sectorCount-1 do begin
readPIO28(device^.controllerId0, LBA + i, puint8(@buffer[512*i]));
psleep(100)
end;
end;
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32);
var
i : uint16;
begin
for i:=0 to sectorCount-1 do begin
writePIO28(device^.controllerId0, LBA + i, puint8(@buffer[512*i]));
psleep(100)
end;
// writePIO28(device^.controllerId0, LBA, puint8(buffer));
end;
end.

View File

@ -15,6 +15,8 @@
{
Driver->Storage->StorageManagment Storage Managment System
DEPRECATED unit
@author(Aaron Hance ah@aaronhance.me)
}
unit storagemanagement;
@ -31,14 +33,14 @@ uses
strings,
lists,
tracer,
rtc;
rtc,
MBR,
volumemanager,
storagetypes;
type
TControllerType = (ControllerIDE, ControllerUSB, ControllerAHCI, ControllerNET, ControllerRAM, rsvctr1, rsvctr2, rsvctr3);
TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry);
PStorage_volume = ^TStorage_Volume;
PStorage_device = ^TStorage_Device;
APStorage_Volume = array[0..10] of PStorage_volume;
byteArray8 = array[0..7] of char;
PByteArray8 = ^byteArray8;
@ -54,41 +56,6 @@ type
PPHIOHook_ = procedure;
TFilesystem = record
sName : pchar;
writeCallback : PPWriteHook;
readCallback : PPReadHook;
createCallback : PPCreateHook;
detectCallback : PPDetectHook;
createDirCallback : PPCreateDirHook;
readDirCallback : PPReadDirHook;
end;
PFileSystem = ^TFilesystem;
TStorage_Volume = record
device : PStorage_device;
sectorStart : uint32;
sectorSize : uint32;
freeSectors : uint32;
filesystem : PFilesystem;
{ True if this drive contains the loaded OS }
isBootDrive : boolean;
end;
{ Generic storage device }
TStorage_Device = record
idx : uint8;
controller : TControllerType;
controllerId0 : uint32;
maxSectorCount : uint32;
sectorSize : uint32;
writable : boolean;
volumes : PLinkedListBase;
writeCallback : PPHIOHook;
readCallback : PPHIOHook;
hpc : uint16;
spt : uint16;
end;
APStorage_Device = array[0..255] of PStorage_device;
{ Generic directory entry }
@ -110,20 +77,13 @@ procedure init();
procedure register_device(device : PStorage_Device);
function get_device_list() : PLinkedListBase;
procedure register_filesystem(filesystem : PFilesystem);
procedure register_volume(device : PStorage_Device; volume : PStorage_Volume);
//function writeNewFile(fileName : pchar; extension : pchar; buffer : puint32; size : uint32) : uint32;
//function readFile(fileName : pchar; extension : pchar; buffer : puint32; byteCount : puint32) : puint32;
//TODO write partition table
implementation
function controller_type_2_string(controllerType : TControllerType) : pchar;
begin
case controllerType of
ControllerIDE : controller_type_2_string:= 'IDE';
ControllerATA : controller_type_2_string:= 'ATA';
ControllerATAPI : controller_type_2_string:= 'ATAPI';
ControllerUSB : controller_type_2_string:= 'USB';
ControllerAHCI : controller_type_2_string:= 'ACHI (SATA)';
ControllerNET : controller_type_2_string:= 'Net';
@ -169,18 +129,57 @@ var
drive : PStorage_Device;
filesystemString : pchar;
filesystem : PFileSystem;
spc : puint32;
sectorCount : uint32;
volumeId : uint8;
mb : PMaster_Boot_Record;
i : uint32 = 0;
begin
//format <DRIVE_ID> <FILESYSTEM> <size>
spc:= puint32(kalloc(4));
spc^:= 4;
spc^:= 1;
driveIndex:= stringToInt( getParam(1, params) );
drive:= PStorage_Device(LL_Get(storageDevices, driveIndex));
//todo change b4 adding in aniother filesytem
PFilesystem(LL_Get(filesystems, 0))^.createCallback(drive, drive^.maxSectorCount-1, 1, spc);
filesystemString := getParam(2, params);
sectorCount := stringToInt( getParam(4, params) );
// if sector count is 0, use full drive
if sectorCount = 0 then begin
sectorCount := drive^.maxSectorCount - 10;
end;
//create MBR if none, and partition table
mb := PMaster_Boot_Record(kalloc(sizeof(TMaster_Boot_Record)));
drive^.readCallback(drive, 0, 1, PuInt32(mb));
//check if MBR exists
if not mb^.boot_sector = $55AA then begin
//create MBR
mb^.signature := $A570 + drive^.id;
mb^.boot_sector := $55AA;
//create partition table
mbr.setup_partition(@mb^.partition[0], 2, sectorCount);
//write MBR
drive^.writeCallback(drive, 0, 1, puint32(mb));
end;
kfree(puint32(mb));
//setup volume
volumemanager.create_volume(drive, filesystemString, 2, sectorCount);
writestringWnd('Drive ', getTerminalHWND);
writeintWnd(driveIndex, getTerminalHWND);
@ -217,10 +216,6 @@ begin
format_command(params);
end;
//lsfs command for listing filesystems
if stringEquals(subcmd, 'lsfs') then begin
end;
pop_trace();
end;
@ -231,10 +226,8 @@ begin
setworkingdirectory('.');
storageDevices:= ll_new(sizeof(TStorage_Device));
fileSystems:= ll_New(sizeof(TFilesystem));
terminal.registerCommand('DISK', @disk_command, 'Disk utility');
pop_trace();
end;
{ Register a new drive with the storage manager }
@ -253,9 +246,7 @@ begin
PStorage_Device(LL_Get(storageDevices, LL_Size(storageDevices) - 1))^.volumes := LL_New(sizeof(TStorage_Volume));
//check for volumes
for i:=0 to ll_size(filesystems) - 1 do begin
PFileSystem(LL_Get(filesystems, i))^.detectCallback(PStorage_Device(LL_Get(storageDevices, LL_Size(storageDevices) - 1)));
end;
pop_trace();
end;
@ -265,23 +256,4 @@ begin
get_device_list:= storageDevices;
end;
procedure register_filesystem(filesystem : PFilesystem);
var
elm : void;
begin
elm:= LL_Add(fileSystems);
PFileSystem(elm)^ := filesystem^;
end;
procedure register_volume(device : PStorage_Device; volume : PStorage_Volume);
var
elm : void;
begin
push_trace('storagemanagement.register_volume');
elm := LL_Add(device^.volumes);
PStorage_volume(elm)^:= volume^;
if rootVolume = PStorage_Volume(0) then rootVolume:= volume;
end;
end.

View File

@ -0,0 +1,64 @@
// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
Driver->Storage->Include->storagetypes - Structs & Data Shared Across Storage Drivers.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit storagetypes;
interface
uses
lists;
type
TControllerType = (ControllerATA, ControllerATAPI, ControllerUSB, ControllerAHCI,
ControllerNET, ControllerRAM, rsvctr1, rsvctr2, rsvctr3);
PStorage_device = ^TStorage_Device;
PDrive_Error = ^TDrive_Error;
PPHIOHook = procedure(drive : PStorage_device; addr : uint32; sectors : uint32; buffer : puint32);
PPHIOHook_ = procedure;
byteArray8 = array[0..7] of char;
PByteArray8 = ^byteArray8;
{ 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;
implementation
end.

View File

@ -0,0 +1,429 @@
// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
Driver->Storage->VolumeManager Drive volume manager
@author(Aaron Hance ah@aaronhance.me)
}
unit volumemanager;
interface
uses
util,
console,
terminal,
strings,
lists,
tracer,
lmemorymanager,
vfs,
MBR,
storagetypes;
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(volume : PStorage_volume; start : uint32; size : 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; //create volume
detectCallback : PPDetectHook; //detect volume
createDirCallback : PPCreateDirHook;
readDirCallback : PPReadDirHook;
end;
TStorage_Volume = record
volumeHandle : uint32;
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;
TFilehandleEntry = record
volume : PStorage_Volume;
fileName : pchar;
fileHandle : uint32;
openMode : TOpenMode;
writeMode : TWriteMode;
lock : Boolean;
closed : Boolean;
end;
PFilehandleEntry = ^TFilehandleEntry;
var
storageVolumes : PLinkedListBase;
filesystems : PlinkedListBase;
fileHandles : PLinkedListBase;
{ Returns the number of volumes }
procedure init();
procedure register_filesystem(filesystem : PFilesystem);
procedure check_for_volumes(drive : PStorage_device);
procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32);
//IO functions
implementation
procedure format_volume_command(params : PParamList);
var
volume : PStorage_Volume;
filesystem : PFilesystem;
config : puint32;
begin
push_trace('VolumeManager.format_volume_command');
console.writestringlnWND('Not implimented, use disk format', getTerminalHWND());
exit;
if paramCount(params) < 3 then begin
console.writestringlnWND('Invalid arguments', getTerminalHWND());
end;
push_trace('VolumeManager.format_volume_command.1');
volume := PStorage_volume(LL_get(storageVolumes, stringToInt(getParam(1, params))));
filesystem := PFilesystem(LL_get(filesystems, stringToInt(getParam(2, params))));
push_trace('VolumeManager.format_volume_command.2');
config := PuInt32(kalloc(sizeof(uInt32)));
config^ := 4;
push_trace('VolumeManager.format_volume_command.3');
//TODO check things exsist
console.writestringlnWND('Staring volume formatting...', getTerminalHWND());
// filesystem^.formatVolumeCallback(volume^.device, volume^.sectorSize, volume^.sectorStart, config);
console.writestringlnWND('Volume format finished.', getTerminalHWND());
end;
procedure list_filesystems_command();
var
i : uint32;
filesystem : PFilesystem;
begin
push_trace('VolumeManager.list_filesystems_command');
if LL_Size(filesystems) < 1 then begin
console.writestringlnWnd('ERROR: no filesystems found!', getTerminalHWND());
exit;
end;
console.writestringlnWnd('Filesystems: ', getTerminalHWND());
for i:=0 to LL_Size(filesystems) - 1 do begin
filesystem := PFilesystem(LL_Get(filesystems, i));
console.writestringWnd(' ', getTerminalHWND());
console.writestringWnd(filesystem^.sName, getTerminalHWND());
console.writestringWnd(' - ', getTerminalHWND());
console.writehexlnWND(filesystem^.system_id, getTerminalHWND());
end;
end;
procedure list_volume_command();
var
i : uint32;
volume : PStorage_Volume;
begin
push_trace('VolumeManager.list_volume_command');
if LL_Size(storageVolumes) < 1 then begin
console.writestringlnWnd('No volumes found.', getTerminalHWND());
exit;
end;
console.writestringlnWnd('Volumes: ', getTerminalHWND());
for i:=0 to LL_Size(storageVolumes) - 1 do begin
volume := PStorage_Volume(LL_Get(storageVolumes, i));
console.writestringWnd(' ', getTerminalHWND());
console.writeintWnd(i, getTerminalHWND());
console.writestringWnd(' - Capacity: ', getTerminalHWND());
console.writeintWnd(uint32((volume^.sectorSize * volume^.sectorCount) DIV 1024 DIV 1024), getTerminalHWND());
console.writestringWnd('MB, Filesystem: ', getTerminalHWND());
if volume^.filesystem <> nil then begin
console.writestringlnWnd(volume^.filesystem^.sName, getTerminalHWND());
end else begin
console.writestringlnWnd('None', getTerminalHWND());
end;
end;
end;
procedure volume_command(params : PParamList);
var
i : uint16;
subcmd : pchar;
begin
push_trace('VolumeManager.volume_command');
subcmd:= getParam(0, params);
if ((paramCount(params) = 0)) then begin
console.writestringlnWnd('Please provide valid arguments.', getTerminalHWND());
console.writestringlnWnd(' ls - for listing all volumes', getTerminalHWND());
console.writestringlnWnd(' lsfs - for listing all filesystems', getTerminalHWND());
console.writestringlnWnd(' info [volume] - display formation for specified volume', getTerminalHWND());
console.writestringlnWnd(' format [volume] [filesystem] - destructive, formats a volume with a specified filesystem', getTerminalHWND());
exit;
end;
if( stringEquals(subcmd, 'ls') ) then begin
list_volume_command();
exit;
end;
if( stringEquals(subcmd, 'lsfs') ) then begin
list_filesystems_command();
exit;
end;
if( stringEquals(subcmd, 'format') ) then begin
format_volume_command(params);
exit;
end;
end;
{ Initialise volume manager }
procedure init();
begin
push_trace('VolumeManager.init');
{ setup lists }
storageVolumes:= ll_New(sizeof(TFilesystem));
filesystems:= ll_New(sizeof(TFilesystem));
fileHandles:= ll_New(sizeof(TFilehandleEntry));
{ register commands }
terminal.registerCommand('volume', @volume_command, 'Volume utility');
end;
{ register a new type of filesystem }
procedure register_filesystem(filesystem : PFilesystem); //TODO on init create null filesystem for empty partition
var
elm : void;
begin
//add filesystem
elm := LL_add(filesystems);
memcpy(uint32(filesystem), uInt32(elm), SizeOf(TFilesystem));
//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
push_trace('VolumeManager.check_for_volumes');
bootrecord := PMaster_Boot_Record(kalloc(SizeOf(TMaster_Boot_Record)));
drive^.readCallback(drive, 0, 1, puint32(bootrecord)); //TODO multiple drives
//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.sectorSize := drive^.sectorSize;
storageVolume.freeSectors := 0; //TODO impliment
storageVolume.filesystem := nil;
if LL_Size(filesystems) < 1 then begin
console.writestringln('Failed to initalise storage system, no filesystems found, stopping!');
exit;
end;
//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;
push_trace('VolumeManager.init3');
//add volume to list
elm := LL_Add(storageVolumes);
memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume));
push_trace('VolumeManager.init4');
elm := LL_Add(drive^.volumes);
memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume));
end;
end;
procedure remove_volume(list : PLinkedListBase; volume : PStorage_Volume);
var
i : uint32;
elm : void;
begin
for i:=0 to LL_Size(list) - 1 do begin
elm := LL_Get(list, i);
if (PStorage_Volume(elm)^.device = volume^.device)
and (PStorage_Volume(elm)^.sectorStart = volume^.sectorStart) then begin
LL_Delete(list, i);
break;
end;
end;
end;
procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32);
var
volume : PStorage_Volume;
elm : void;
i : uint16;
config : PuInt32;
begin
push_trace('VolumeManager.create_volume');
volume := PStorage_Volume(kalloc(SizeOf(TStorage_Volume)));
volume^.device := disk;
volume^.sectorStart := sectorStart;
volume^.sectorCount := sectorCount - 10;
volume^.sectorSize := disk^.sectorSize;
volume^.freeSectors := 0; //setup by filesystem
//find filesystem
for i:=0 to LL_Size(filesystems) - 1 do begin
if stringEquals(filesystem, PFilesystem(LL_Get(filesystems, i))^.sName) then begin
volume^.filesystem := PFilesystem(LL_Get(filesystems, i));
end;
end;
//TODO CHECK FILESYSTEM EXSISTS
//format volume
volume^.filesystem^.createCallback(volume, sectorCount, sectorStart, config);
push_trace('VolumeManager.create_volume.2');
//remove volume from list of volumes
remove_volume(storageVolumes, volume);
//add volume to list
elm := LL_Add(storageVolumes);
memcpy(uint32(volume), uint32(elm), SizeOf(TStorage_Volume));
push_trace('VolumeManager.create_volume.3');
//remove volume from list of volumes on device
remove_volume(disk^.volumes, volume);
//add volume to device
elm := LL_Add(disk^.volumes);
memcpy(uint32(volume), uint32(elm), SizeOf(TStorage_Volume));
//free memory
kfree(puint32(volume));
end;
procedure register_volume(device : PStorage_Device; volume : PStorage_Volume);
var
elm : void;
begin
push_trace('storagemanagement.register_volume');
elm := LL_Add(device^.volumes);
PStorage_volume(elm)^:= volume^;
// if rootVolume = PStorage_Volume(0) then rootVolume:= volume;
end;
procedure openFile(Handle : uint32; Filename : PChar; OpenMode : TOpenMode; WriteMode : TWriteMode; lock : boolean; Error : PError);
var
fileHandleEntree : PFilehandleEntry;
fileHandle : uInt32;
i : uint32;
elm : void;
begin
push_trace('VolumeManager.openFile');
//check if file is already open and if it is locked, if openmode is not read only
if OpenMode <> omReadOnly then begin
for i:=0 to LL_Size(fileHandles) - 1 do begin
elm := LL_Get(fileHandles, i);
if stringEquals(PFilehandleEntry(elm)^.filename, Filename) then begin
if PFilehandleEntry(elm)^.lock and (not PFilehandleEntry(elm)^.closed) then begin
Error^ := eFileInUse;
exit;
end;
end;
end;
end;
//create filehandleentry
fileHandleEntree := PFilehandleEntry(kalloc(SizeOf(TFilehandleEntry)));
fileHandleEntree^.fileName := Filename;
fileHandleEntree^.openMode := OpenMode;
fileHandleEntree^.writeMode := WriteMode;
fileHandleEntree^.lock := lock;
fileHandleEntree^.closed := false;
//create filehandle
fileHandle := LL_Size(fileHandles);
end;
end.

View File

@ -16,6 +16,7 @@
Include->Strings - String Manipulation.
@author(Kieron Morris <kjm@kieronmorris.me>)
@author(Aaron Hance <ah@aaronhance.me>)
}
unit strings;
@ -33,6 +34,10 @@ function stringCopy(str : pchar) : pchar;
function stringNew(size : uint32) : pchar;
function stringSize(str : pchar) : uint32;
function stringConcat(str1, str2 : pchar) : pchar;
function stringTrim(str : pchar; length : uint32) : pchar;
function stringSub(str : pchar; start, size : uint32) : pchar;
function stringReplace(str, find, replace : pchar) : pchar;
function stringIndexOf(str, find : pchar) : sint32;
function stringContains(str : pchar; sub : pchar) : boolean;
function stringToInt(str : pchar) : uint32;
function hexStringToInt(str : pchar) : uint32;
@ -151,6 +156,94 @@ begin
stringConcat:= result;
end;
// Trim the string to the specified length.
function stringTrim(str : pchar; length : uInt32) : pchar;
var
result : pchar;
begin
result:= stringNew(length);
memcpy(uint32(str), uint32(result), length);
stringTrim:= result;
end;
// Return a substring of the string.
function stringSub(str : pchar; start, size : uint32) : pchar;
var
result : pchar;
begin
result:= stringNew(size);
memcpy(uint32(str)+start, uint32(result), size);
stringSub:= result;
end;
// Replace first instance of a string with another.
function stringReplace(str, find, replace : pchar) : pchar;
var
result : pchar;
i, j, k : uint32;
found : boolean;
begin
// Find the first instance of the find string.
i:= 0;
found:= false;
while (i < stringSize(str)) and (not found) do begin
if stringEquals(str+i, find) then begin
found:= true;
end else begin
inc(i);
end;
end;
// If we found the find string, replace it.
if found then begin
result:= stringNew(stringSize(str) - stringSize(find) + stringSize(replace));
j:= 0;
k:= 0;
while i < stringSize(str) do begin
if stringEquals(str+i, find) then begin
memcpy(uint32(replace), uint32(result+j), stringSize(replace));
j:= j + stringSize(replace);
inc(i, stringSize(find));
end else begin
result[j]:= str[i];
inc(j);
inc(i);
end;
end;
stringReplace:= result;
end else begin
stringReplace:= stringCopy(str);
end;
// Return the result.
stringReplace:= result;
end;
// Find the index of the first instance of a string.
function stringIndexOf(str, find : pchar) : sint32;
var
i : uint32;
found : boolean;
begin
i:= 0;
found:= false;
while (i < stringSize(str)) and (not found) do begin
if stringEquals(str+i, find) then begin
found:= true;
end else begin
inc(i);
end;
end;
if found then begin
stringIndexOf:= i;
end else begin
stringIndexOf:= -1;
end;
end;
function stringContains(str : pchar; sub : pchar) : boolean;
var
strEnd, subEnd, i, j, count : uint32;

View File

@ -41,9 +41,12 @@ uses
E1000,
IDE,
storagemanagement,
drivemanager,
volumemanager,
lists,
net,
fat32,
flatfs,
isrmanager,
faults,
fonts,
@ -205,7 +208,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');
@ -214,6 +219,7 @@ begin
{ Filsystems }
fat32.init();
flatfs.init();
{ Device Drivers }
tracer.push_trace('kmain.DEVDRV');

58
virtualbox-wrapper.ps1 Normal file
View File

@ -0,0 +1,58 @@
<#
You need a local git-ignored localenv.json file with the following content:
{
"virtualbox": {
"MachineName": "your-machine-name or guid"
}
}
#>
param (
[Parameter(Mandatory=$true)]
[ValidateSet('up', 'down')]
[String]$Command
)
$Config = Get-Content .\localenv.json | ConvertFrom-Json
$MachineName = $Config.virtualbox.MachineName
$LogLocation = $Config.virtualbox.LogLocation
$LogOutputEnabled = $LogLocation -ne $null
if ($Command -eq 'up') {
if($LogOutputEnabled) {
Clear-Content $LogLocation
}
$MonitorJob = Start-Job -ArgumentList $MachineName -ScriptBlock {
param($MachineName)
Write-Output "Starting $MachineName"
VBoxManage.exe startvm $MachineName
$running=$true
while($running) {
$status=(VBoxManage.exe list runningvms)
if($status) {
$running=$status.contains($MachineName)
} else {
$running=$false
}
}
}
if($LogOutputEnabled) {
$LogJob = Start-Job -ArgumentList $LogLocation -ScriptBlock {
param($LogLocation)
Get-Content -Path $LogLocation -Wait
}
}
while($MonitorJob.State -eq 'Running') {
if($LogOutputEnabled) {
Receive-Job $LogJob
}
Receive-Job $MonitorJob
}
} elseif ($Command -eq 'down') {
Write-Output "Stopping $MachineName"
VBoxManage.exe controlvm $MachineName poweroff
}