Compare commits

...

37 Commits

Author SHA1 Message Date
569b2f0bca More cleanup 2025-03-23 10:20:55 +00:00
ed0f8d9c08 volume manager begginings 2025-03-22 18:22:22 +00:00
a2f6d7e76d Cleanup 2025-03-22 10:20:26 +00:00
babe4290e4 ahci reading 2025-03-20 22:12:42 +00:00
05eab23956 starting to work 2025-03-19 22:15:32 +00:00
f04f8aa411 there you go ki-ear-ron 2025-03-16 21:39:47 +00:00
6a3ebf1387 ahci progress 2025-03-15 20:13:33 +00:00
b2ecbe58da Added licensing 2025-03-15 10:43:42 +00:00
2014ce4f9c AHCI Progress 2025-03-15 10:42:18 +00:00
6cd9ae4f46 Dynamic lists fixes and additions
# Conflicts:
#	src/include/lists.pas
2025-03-14 19:52:41 +00:00
0855abfd41 ATA, (PATAPI dead), AHCI New Begginings 2025-03-14 19:49:56 +00:00
fd0de9f1d2 atapi progress 2025-03-10 22:38:50 +00:00
920aca5bbf New working ATA PIO driver 2025-03-10 20:09:50 +00:00
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 5cb415c76b898143ddcc4ea63d600254b2cc30e4.
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
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
34 changed files with 5793 additions and 1145 deletions

3
.gitignore vendored
View File

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

13
.vscode/launch.json vendored
View File

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

26
.vscode/tasks.json vendored
View File

@ -9,14 +9,18 @@
"command": "docker-compose", "command": "docker-compose",
"args": [ "args": [
"run", "run",
"builder" "builder",
], ],
"type": "shell", "type": "shell",
"problemMatcher": [], "problemMatcher": [],
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
} },
"dependsOn": [
"Close VirtualBox",
"Clean"
]
}, },
{ {
"label": "Build (Builder)", "label": "Build (Builder)",
@ -26,6 +30,24 @@
"builder" "builder"
], ],
"type": "shell" "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"
} }
] ]
} }

View File

@ -4,4 +4,4 @@ echo "======================="
echo " " echo " "
echo "Compiling FPC Sources..." echo "Compiling FPC Sources..."
echo " " echo " "
fpc -Aelf -gw -n -va -O3 -Op3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/* -Fusrc/driver/net/* src/kernel.pas fpc -Aelf -gw -n -O3 -Op3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/storage/* -Fusrc/driver/storage/AHCI/* -Fusrc/driver/* -Fusrc/driver/net/* src/kernel.pas

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: services:
builder: builder:
build: . 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. I don't think this needs an explaination.
* [VSCode (Optional, but highly recommended)](https://code.visualstudio.com/) * [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. 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) * [PowerShell Plugin by Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-vscode.powershell)
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. 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](https://www.virtualbox.org/)
Virtualbox is our Virtualisation environment of choice, don't ask why, it just is. Virtualbox is our Virtualisation environment of choice, don't ask why, it just is.
@ -27,7 +27,7 @@ We welcome everyone to give building/breaking/fixing/shooting Asuro a go, feel f
3. Install Docker for Windows. 3. Install Docker for Windows.
4. Install Git for Windows. 4. Install Git for Windows.
5. Install VSCode & the listed plugins. 5. Install VSCode & the listed plugins.
6. Install VirtualBox. 6. Install VirtualBox (v7+).
7. Clone this repository. 7. Clone this repository.
8. Run the following command in the root of the repo to build the docker image: 8. Run the following command in the root of the repo to build the docker image:
```powershell ```powershell
@ -50,32 +50,29 @@ We welcome everyone to give building/breaking/fixing/shooting Asuro a go, feel f
```xml ```xml
<Machine uuid="{7d395c96-891c-4139-b77d-9b6b144b0b93}" name="Asuro" OSType="Linux" snapshotFolder="Snapshots" lastStateChange="2021-06-20T20:33:07Z"> <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 ```json
{ {
"configurations": [ "VirtualBox":{
{ "MachineName":"<YOUR_UUID_OR_MACHINE_NAME>"
"name":"Run",
"request": "launch",
"type": "coreclr",
"preLaunchTask": "Build",
"program": "VBoxSDL",
"args": [
"--comment",
"Asuro",
"--startvm",
"<YOUR UUID HERE>"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"internalConsoleOptions": "neverOpen"
} }
]
} }
``` ```
This will allow VSCode to automatically launch VirtualBox once Asuro has been compiled. 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. 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! 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`. - 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

@ -91,6 +91,7 @@ function getDeviceInfo(class_code : uint8; subclass_code : uint8; prog_if : uint
procedure requestConfig(bus : uint8; slot : uint8; func : uint8; row : uint8); procedure requestConfig(bus : uint8; slot : uint8; func : uint8; row : uint8);
procedure writeConfig(bus: uint8; slot : uint8; func : uint8; row : uint8; val : uint32); procedure writeConfig(bus: uint8; slot : uint8; func : uint8; row : uint8; val : uint32);
procedure setBusMaster(bus : uint8; slot : uint8; func : uint8; master : boolean); procedure setBusMaster(bus : uint8; slot : uint8; func : uint8; master : boolean);
procedure enableDevice(bus : uint8; slot : uint8; func : uint8);
implementation implementation
@ -442,4 +443,34 @@ begin
pop_trace; pop_trace;
end; end;
//Enable device inturrupts and set bus master
procedure enableDevice(bus : uint8; slot : uint8; func : uint8);
var
addr : uint32;
cmd : uint32;
begin
push_trace('PCI.enableDevice');
addr := ($1 shl 31);
addr := addr or (bus shl 16);
addr := addr or ((slot) shl 11);
addr := addr or ((func) shl 8);
addr := addr or ($04 and $FC);
outl(PCI_CONFIG_ADDRESS_PORT, addr);
cmd := inl(PCI_CONFIG_DATA_PORT);
cmd := cmd or PCI_COMMAND_MEM_SPACE;
cmd := cmd or PCI_COMMAND_BUS_MASTER;
cmd := cmd or (3 shl 1);
//enable interrupts, remove disable interrupt bit maybe
cmd := cmd and not PCI_COMMAND_INT_DISABLE;
// cmd := cmd and not (1 shl 9);
outl(PCI_CONFIG_ADDRESS_PORT, addr);
outl(PCI_CONFIG_DATA_PORT, cmd);
pop_trace;
end;
end. end.

View File

@ -21,6 +21,29 @@ unit drivertypes;
interface interface
const
PCI_CONFIG_ADDRESS_PORT = $0CF8;
PCI_CONFIG_DATA_PORT = $0CFC;
PCI_COMMAND_IO_SPACE = $0001;
PCI_COMMAND_MEM_SPACE = $0002;
PCI_COMMAND_BUS_MASTER = $0004;
PCI_COMMAND_SPECIAL_CYC = $0008;
PCI_COMMAND_MEM_WRITE = $0010;
PCI_COMMAND_VGA_PALETTE = $0020;
PCI_COMMAND_PARITY = $0040;
PCI_COMMAND_WAIT = $0080;
PCI_COMMAND_SERR = $0100;
PCI_COMMAND_FAST_BACK = $0200;
PCI_COMMAND_INT_DISABLE = $0400;
PCI_COMMAND_SERR_ENABLE = $8000;
PCI_CAP_ID_MSI = $05;
// Bits in the MSI Control register (16-bit):
MSI_CONTROL_ENABLE = 1 shl 0; // Bit 0
MSI_CONTROL_64BIT = 1 shl 7; // Bit 7
MSI_CONTROL_PVMASK = 1 shl 8; // Bit 8 (optional: per-vector masking)
type type
PPCI_Device = ^TPCI_Device; PPCI_Device = ^TPCI_Device;
@ -44,6 +67,7 @@ type
address1 : uint32; address1 : uint32;
address2 : uint32; address2 : uint32;
address3 : uint32; address3 : uint32;
address4 : uint32; address4 : uint32;
address5 : uint32; address5 : uint32;
CIS_pointer : uint32; CIS_pointer : uint32;

View File

@ -1,296 +0,0 @@
// 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.
{
Drivers->Storage->AHCI - AHCI SATA Driver.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit AHCI;
interface
uses
util,
PCI,
drivertypes,
drivermanagement,
lmemorymanager,
console,
vmemorymanager;
type
//Struct hell
TFIS_Type = (
REG_H2D = $27,
REG_D2H = $34,
DMA_ACT = $39,
DMA_SETUP = $41,
DATA = $46,
BIST = $58,
PIO_SETUP = $5F,
DEV_BITS = $A0
);
PFIS_REG_H2D = ^TFIS_REG_H2D;
TFIS_REG_H2D = bitpacked record
fis_type : uint8;
port_mult : UBit4;
rsv0 : UBit3;
coc : boolean;
command : uint8;
feature_low : uint8;
lba0 : uint8;
lba1 : uint8;
lba2 : uint8;
device : uint8;
lba3 : uint8;
lba4 : uint8;
lba5 : uint8;
feature_high : uint8;
count_low : uint8;
count_high : uint8;
icc : uint8;
control : uint8;
rsvl : uint32;
end;
TFIS_REG_D2H = bitpacked record
fis_type : uint8;
port_mult : UBit4;
rsv0 : UBit2;
i : boolean;
rsvl : boolean;
status : uint8;
error : uint8;
lba0 : uint8;
lba1 : uint8;
lba2 : uint8;
device : uint8;
lba3 : uint8;
lba4 : uint8;
lba5 : uint8;
rsv2 : uint8;
count_low : uint8;
count_high : uint8;
rsv3 : uint16;
rsv4 : uint32;
end;
TFIS_Data = bitpacked record
fis_type : uint8;
port_mult : UBit4;
rsv0 : UBit4;
rsv1 : uint16;
data : ^uint32;
end;
TFIS_PIO_Setup = bitpacked record
fis_type : uint8;
pmport : UBit4;
rsv0 : boolean;
d : boolean;
i : boolean;
rsv1 : boolean;
status : uint8;
error : uint8;
lba0 : uint8;
lba1 : uint8;
lba2 : uint8;
device : uint8;
lba3 : uint8;
lba4 : uint8;
lba5 : uint8;
rsv2 : uint8;
countl : uint8;
counth : uint8;
rsv3 : uint8;
e_status : uint8;
tc : uint16;
rsv4 : uint16;
end;
// TFIS_DMA_Setup = bitpacked record
// end;
// THBA_Memory = bitpacked record
// end;
// THBA_Port = bitpacked record
// end;
// THBA_FIS = bitpacked record
// end;
PHBA_PORT = ^THBA_PORT;
THBA_PORT = bitpacked record
clb : uint32;
clbu : uint32;
fb : uint32;
fbu : uint32;
istat : uint32;
ie : uint32;
cmd : uint32;
rsv0 : uint32;
tfd : uint32;
sig : uint32;
ssts : uint32;
sctl : uint32;
serr : uint32;
sact : uint32;
ci : uint32;
sntf : uint32;
fbs : uint32;
rsv1 : array[0..11] of uint32;
vendor : array[0..4] of uint32;
end;
THBA_MEM = bitpacked record
cap : uint32; //0
global_host_control : uint32; //4
interrupt_status : uint32; //8
port_implemented : uint32; //c
version : uint32; //10
ccc_control : uint32; //14
ccc_ports : uint32; //18
em_location : uint32; //1c
em_Control : uint32; //20
hcap2 : uint32; //24
bohc : uint32; //28
rsv0 : array[0..210] of boolean;
ports : array[0..31] of THBA_Port;
end;
PHBA = ^THBA_MEM;
PCMDHeader = ^ TCommand_Header;
TCommand_Header = bitpacked record
cfl : ubit5;
a : boolean;
w : boolean;
p : boolean;
r : boolean;
b : boolean;
c : boolean;
rsv0 : boolean;
pmp : ubit4;
PRDTL : uint16;
PRDTBC : uint32;
CTBA : uint32;
CTBAU : uint32;
rsv1 : array[0..3] of uint32;
end;
TPRD_Entry = bitpacked record
data_base_address : uint32;
data_bade_address_U : uint32;
rsv0 : uint32;
data_byte_count : ubit22;
rsv1 : ubit9;
interrupt_oc : boolean;
end;
PCommand_Table = ^TCommand_Table;
TCommand_Table = bitpacked record
cfis : array[0..64] of uint8;
acmd : array[0..16] of uint8;
rsv : array[0..48] of uint8;
prdt : array[0..7] of TPRD_Entry;
end;
TSataDevice = record
controller : uint8;
port : uint8;
end;
var
//constants
//SATA_SIG_ATA := $101;
//SATA_SIG_ATAPI := $EB140101;
//STA_SIG_SEMB := $C33C0101;
//STAT_SIG_PM := $96690101;
AHCI_BASE: uint32 = $400000; //irrelivent
//other
ahciControllers : array[0.16] of PuInt32;
ahciControllerCount : uint8 = 0;
hba : array[0..16] of PHBA;
sataDevices : array[0..127] of TSataDevice;
sataDeviceCount : uint8;
procedure init();
procedure check_ports(controller : uint8);
procedure enable_cmd(port : uint8);
procedure disable_cmd(port : uint8);
procedure port_rebase(port : uint8);
function load(ptr:void): boolean;
function read(port : uint8; startl : uint32; starth : uint32; count : uint32; buf : PuInt32) : boolean;
function write(port : uint8; startl : uint32; starth : uint32; count : uint32; buf : PuInt32) : boolean;
function find_cmd_slot(port : uint8) : uint32;
implementation
procedure init();
var
devID : TDeviceIdentifier;
begin
console.writestringln('AHCI: Registering driver');
devID.bus:= biPCI;
devID.id0:= idANY;
devID.id1:= $00000001;
devID.id2:= $00000006;
devID.id3:= $00000001;
devID.id4:= idANY;
devID.ex:= nil;
drivermanagement.register_driver('AHCI Driver', @devID, @load);
end;
procedure load(ptr : void);
begin
console.writestringln('AHCI: initilizing a new controller');
ahciControllers[ahciControllerCount] := ptr;
hba[ahciControllerCount] := PPCI_Device(ptr)^.address5;
new_page_at_address(hba[ahciControllerCount]);
//here would be any controller setup needed
check_ports(ahciControllerCount);
ahciControllerCount += 1;
exit(true);
end;
procedure check_ports(controller : uint8);
var
d : uint32 = 1;
i : uint32;
begin
for i:=0 to 31 do begin
if (d > 0) and (hba[controller]^.port_implemented shr i) = 1 then begin
if (hba[controller]^.ports[i].ssts shr 8) <> 1 and (hba[controller]^.ports[i].ssts and $0F) <> 3 then begin
if hba[controller]^.ports[i].sig = 1 then begin
//device is sata
sataDevices[sataDeviceCount].controller := controller;
sataDevices[sataDeviceCount].port := i;
sataDeviceCount += 1;
end;
//TODO implement ATAPI
end;
end;
end;
end;

View File

@ -0,0 +1,899 @@
// 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.
{
Drivers->Storage->AHCI->AHCI - AHCI Driver.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit AHCI;
interface
uses
AHCITypes,
console,
drivermanagement,
drivertypes,
idetypes,
isrmanager,
lists,
lmemorymanager,
PCI,
util,
vmemorymanager;
var
ahciControllers : PDList;
page_base : puint32;
procedure init();
function load(ptr : void) : boolean;
procedure check_ports(controller : PAHCI_Controller);
procedure identify_device(controller : PAHCI_Controller; portIndex : uint32; isATAPI : boolean);
procedure ahci_isr();
function find_cmd_slot(device : PAHCI_Device) : uint32;
function send_read_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
// function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
function read_atapi(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
implementation
procedure init();
var
devID : TDeviceIdentifier;
begin
console.writestringln('AHCI: Registering driver');
devID.bus:= biPCI;
devID.id0:= idANY;
devID.id1:= $00000001;
devID.id2:= $00000006;
devID.id3:= $00000001;
devID.id4:= idANY;
devID.ex:= nil;
drivermanagement.register_driver('ATA/PI AHCI Driver', @devID, @load);
//TODO check IDE devices in ide for sata devices
end;
procedure stop_port(port : PHBA_Port);
begin
port^.cmd := port^.cmd and not $1;
port^.cmd := port^.cmd and not $100;
while ((port^.cmd and $4000) or (port^.cmd and $8000)) <> 0 do begin
//wait for the port to stop
end;
end;
procedure start_port(port : PHBA_Port);
begin
while (port^.cmd and $8000) <> 0 do begin
//wait for port to not be busy
end;
port^.cmd := port^.cmd or $100;
port^.cmd := port^.cmd or $1;
end;
procedure reset_port(port : PHBA_Port);
var
ssts, serr, timeout : uint32;
begin
console.writestringln('AHCI: Performing a full port reset.');
port^.cmd := port^.cmd and not $1;
// Wait until CR (bit 15) is cleared.
timeout := 0;
while (port^.cmd and $8000) <> 0 do begin
timeout := timeout + 1;
if timeout > 1000000 then begin
console.writestringln('AHCI: Port reset timeout.');
break;
end;
end;
port^.sata_ctrl := port^.sata_ctrl or $FFFF0000;
while (port^.sata_status and $F) <> 3 do begin
//wait for the port to be ready
end;
// Clear the SERR register by writing 1s to it.
port^.sata_error := $FFFFFFFF;
start_port(port);
end;
// procedure reset_port(port : PHBA_Port);
// var
// ssts, serr, timeout : uint32;
// begin
// console.writestringln('AHCI: Performing a full port reset.');
// port^.cmd := port^.cmd and not $1;
// // Wait until CR (bit 15) is cleared.
// timeout := 0;
// while (port^.cmd and $8000) <> 0 do begin
// timeout := timeout + 1;
// if timeout > 1000000 then begin
// console.writestringln('AHCI: Port reset timeout.');
// break;
// end;
// end;
// port^.sata_ctrl := port^.sata_ctrl and $FFFF0000;
// port^.sata_ctrl := port^.sata_ctrl or $1;
// psleep(10);
// port^.sata_ctrl := port^.sata_ctrl and $FFFF0000;
// while (port^.sata_status and $F) <> $3 do begin
// //wait for the port to be ready
// end;
// // Clear the SERR register by writing 1s to it.
// port^.sata_error := $FFFFFFFF;
// // start_port(port);
// end;
{
Check the ports on the controller and setup the command list, FIS, and command table
}
procedure check_ports(controller : PAHCI_Controller);
var
i : uint32;
ii : uint32;
port : PHBA_Port;
device : PAHCI_Device;
cmd_list_base : puint32;
fis_base : puint32;
cmd_table_base : puint32;
command_header : PHBA_CMD_HEADER;
begin
console.writestring('AHCI: active ports: ');
console.writehexln(controller^.mio^.ports_implimented);
for i:=0 to 31 do begin
port := @controller^.mio^.ports[i];
console.writehex(port^.signature);
console.writestring(' ');
end;
for i:=0 to 31 do begin
if ((controller^.mio^.ports_implimented shr i) and 1) = 1 then begin
port := @controller^.mio^.ports[i];
console.writestring('AHCI: Port ');
console.writeint(i);
console.writestring(' implemented on controller ');
console.writehexln(uint32(controller^.pci_device^.address5));
device := PAHCI_Device(@controller^.devices[i]);
device^.port := port;
//check if the port is active TODO
console.writestring('AHCI: signature: ');
console.writehexln(port^.signature);
//check device type
case port^.signature of
SATA_SIG_ATA: begin
console.writestringln('AHCI: Device is SATA');
device^.device_type := SATA;
end;
SATA_SIG_ATAPI: begin
console.writestringln('AHCI: Device is ATAPI');
device^.device_type := ATAPI;
end;
SATA_SIG_SEMB: begin
console.writestringln('AHCI: Device is SEMB');
device^.device_type := SEMB;
end;
SATA_SIG_PM: begin
console.writestringln('AHCI: Device is PM');
device^.device_type := PM;
end;
end;
//NEEED TO STOP the port before doing anything
stop_port(port);
console.writestringln('AHCI: Port stopped');
//allocate memory for the command list and ensure it is aligned to 1024 bytes
cmd_list_base := kalloc(sizeof(THBA_CMD_HEADER) * 64);
cmd_list_base := puint32((uint32(cmd_list_base) + 1023) and $FFFFFC00);
memset(uint32(cmd_list_base), 0, sizeof(THBA_CMD_HEADER) * 32);
//set the command list base address
port^.cmdl_basel := vtop(uint32(cmd_list_base)); //todo set virtual address in device
port^.cmdl_baseu := 0;
device^.command_list := PHBA_CMD_HEADER(cmd_list_base);
//print the command list base address
console.writestring('AHCI: Command list base address: ');
console.writehexln(uint32(cmd_list_base));
//allocate memory for the FIS and ensure it is aligned to 256 bytes
fis_base := kalloc(sizeof(THBA_FIS) * 8);
fis_base := puint32((uint32(fis_base) + 255) and $FFFFFF00);
//set the FIS base address
port^.fis_basel := vtop(uint32(fis_base));
port^.fis_baseu := 0;
memset(uint32(fis_base), 0, sizeof(THBA_FIS));
device^.fis := PHBA_FIS(fis_base);
//todo check how many simultaneous commands are supported
//allocate memory for the command table and ensure it is aligned to 128 bytes
cmd_table_base := kalloc(sizeof(THBA_CMD_TABLE) * 64);
cmd_table_base := puint32((uint32(cmd_table_base) + 127) and $FFFFFF80);
memset(uint32(cmd_table_base), 0, sizeof(THBA_CMD_TABLE) * 32);
device^.command_table := PHBA_CMD_TABLE(cmd_table_base);
//set the command table base address and setup command table
for ii:=0 to 31 do begin
//set command header locations
command_header := PHBA_CMD_HEADER(uint32(cmd_list_base) + (ii * sizeof(THBA_CMD_HEADER)));
command_header^.prdtl := 32;
command_header^.cmd_table_base := vtop(uint32(cmd_table_base)) + (ii * sizeof(THBA_CMD_TABLE));
command_header^.cmd_table_baseu := 0;
end;
//reset the port
reset_port(port);
//print sata status
console.writestring('AHCI: sata status: ');
console.writehexln(port^.sata_status);
// //pass devices count as second param
if (device^.device_type = SATA) then begin
identify_device(controller, i, false);
end else if (device^.device_type = ATAPI) then begin
identify_device(controller, i, true);
end else begin
console.writestringln('AHCI: Unsupported device type');
end;
controller^.mio^.int_status := $FFFFFFFF;
end;
end;
end;
procedure identify_device(controller : PAHCI_Controller; portIndex : uint32; isATAPI : boolean);
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
buffer : puint32;
device : PAHCI_Device;
tfd : uint32;
sec_count : uint32;
b8 : puint16;
begin
console.writestring('AHCI: Identifying device ');
console.writeintln(portIndex);
device := PAHCI_Device(@controller^.devices[portIndex]);
// (Optional) Print the device type if you store it:
console.writestring('AHCI: Device type: ');
// For example: if device^.device_type = SATA then ...
console.writestring('AHCI: sata status: ');
console.writehexln(device^.port^.sata_status);
console.writestring('AHCI: signature: ');
console.writehexln(device^.port^.signature);
console.writestring('AHCI: device type: ');
console.writeintln(uint32(device^.device_type));
//clear any pending interrupts
device^.port^.int_status := $FFFFFFFF;
device^.port^.int_enable := $0;
// Allocate a 512-byte DMA buffer for the IDENTIFY data
buffer := kalloc(512);
memset(uint32(buffer), 0, 512);
// Use command slot 0 for the IDENTIFY command.
cmd_header := device^.command_list; // Assuming slot 0 is at the beginning.
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 0;
cmd_header^.prdtl := 1;
cmd_header^.clear_busy := 1;
if isATAPI then begin
cmd_header^.atapi := 1;
end;
// Setup the command table (using slot 0)
cmd_table := device^.command_table;
cmd_table^.prdt[0].dba := vtop(uint32(buffer));
cmd_table^.prdt[0].dbc := 511; // 512 bytes (0-based count)
cmd_table^.prdt[0].int := 1; // Interrupt on completion
// if isATAPI then begin
// // ATAPI Identify Device command
// buffer[0] := $A1; // ATAPI Identify Device
// end;
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@device^.command_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
if isATAPI then begin
fis^.command := ATA_CMD_IDENTIFY_PACKET;
end else begin
fis^.command := ATA_CMD_IDENTIFY;
end;
fis^.device := $A0; // LBA mode 40?
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or $1;
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent identify command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
device^.port^.int_status := $FFFFFFFF; // Clear any pending interrupts
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: IDENTIFY command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and $1) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and $2) <> 0 then begin
console.writestringln('AHCI: Error sending identify command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
// Dump the 512-byte IDENTIFY data for debugging (display 64 DWORDs)
for i := 0 to 63 do begin
console.writehex(uint32(buffer[i]));
console.writestring(' ');
end;
console.writestringln('');
b8 := puint16(buffer);
sec_count := (b8[61] shl 16) or b8[60];
console.writestring('AHCI: Sector count: ');
console.writeintln(sec_count);
console.writestring('AHCI: Device size: ');
console.writeint(sec_count * 512 div 1024 div 1024);
console.writestringln(' MB');
console.writestringln('');
buffer := puint32(kalloc(2048));
memset(uint32(buffer), 0, 2048);
//send a read DMA command to the device
if isATAPI then begin
read_atapi(device, 0, 2048, buffer);
end else begin
memset(uint32(buffer), $77, 2048);
send_write_dma(device, 22, 512, buffer);
memset(uint32(buffer), 0, 2048);
send_read_dma(device, 22, 512, buffer);
end;
//print the buffer
for i:=0 to 63 do begin
console.writehex(puInt8(buffer)[i]);
console.writestring(' ');
end;
console.writestringln('');
end;
function load(ptr : void) : boolean;
var
device : PPCI_Device;
controller : PAHCI_Controller;
i : uint32;
base : PHBA_Memory;
cmd_list_base : puint32;
fis_base : puint32;
cmd_table_base : puint32;
int_no : uInt8;
timeout : uint32;
bohc : uint32;
begin
console.writestringln('AHCI: initilizing a new controller');
if ahciControllers = nil then begin
console.writestringln('AHCI: Initializing controller list');
ahciControllers := DL_New(SizeOf(TAHCI_Controller));
end;
device := PPCI_Device(ptr);
pci.enableDevice(device^.bus, device^.slot, device^.func);
psleep(5);
int_no := device^.interrupt_line + 32;
//print interrupt number
console.writestring('AHCI: Interrupt number: ');
console.writehexln(int_no);
controller := PAHCI_Controller(DL_Add(ahciControllers));
controller^.pci_device := PPCI_Device(device);
// Perform BIOS/OS handoff (if the bit in the extended capabilities is set)
// Reset controller
//get the base address of the controller
page_base := kpalloc(device^.address5); // TODO MEM memory manager need to be improved
base := PHBA_Memory(device^.address5);
controller^.mio := base;
if (controller^.mio^.capabilites2 and $28) <> 0 then begin
console.writestringln('AHCI: Controller needs to be handed off to the OS');
end;
console.writestring('AHCI: Controller at: ');
console.writehexln(device^.address5);
console.writehexln(uint32(page_base));
bohc := controller^.mio^.bohc;
if (bohc and $1) <> 0 or (bohc and not (1 shl 1)) then begin
console.writestringln('AHCI: BIOS Owned Semaphore is set, initiating handoff.');
// Set the OS Owned Semaphore (typically bit 1).
bohc := bohc or (1 shl 1);
controller^.mio^.bohc := bohc;
// Wait until the BIOS Owned Semaphore (bit 0) is cleared.
timeout := 0;
while ((controller^.mio^.bohc and $1) <> 0) and (timeout < 1000000) do begin
timeout := timeout + 1;
end;
if timeout = 1000000 then begin
console.writestringln('AHCI: BIOS/OS handoff timed out.');
end else begin
console.writestringln('AHCI: BIOS/OS handoff successful.');
end;
end else begin
console.writestringln('AHCI: BIOS not holding controller or handoff already complete.');
end;
base^.global_ctrl := base^.global_ctrl or 1;
while (base^.global_ctrl and 1) <> 0 do begin
end;
registerISR(int_no, @ahci_isr);
//enable AHCI mode, TODO check if is not already in AHCI mode and enable it, also perhaps remove if loaded as IDE
base^.global_ctrl := base^.global_ctrl or AHCI_CONTROLLER_MODE;
// base^.global_ctrl := base^.global_ctrl or $2; //enable interrupts
//disable interrupts
base^.global_ctrl := base^.global_ctrl or $2;
//clear any pending interrupts
base^.int_status := $FFFFFFFF;
check_ports(controller);
end;
procedure ahci_isr();
begin
console.writestringln('AHCI: ISR');
console.redrawWindows();
//welp there is no way to know what port caused the interrupt or even if it was the controller
//so we will just have to check all ports, and figure the operation that caused the interrupt
end;
function find_cmd_slot(device : PAHCI_Device) : uint32;
var
i : uint32;
begin
// for i:=0 to 31 do begin
// if (device^.port^.cmd_issue[i] and $80000000) = 0 then begin
// exit(i);
// end;
// end;
exit(0);
end;
function send_read_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
tfd : uint32;
sec_count : uint32;
prdt_count : uint32;
b8 : puint16;
slot : uint32;
begin
console.writestringln('AHCI: Sending read DMA command');
reset_port(device^.port);
//prdt count is 22 bits
prdt_count := (count div 4194304) + 1;
if prdt_count > 32 then begin
send_read_dma := false;
exit;
end;
// Use command slot 0 for the IDENTIFY command.
slot := find_cmd_slot(device);
cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER)));
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 0;
cmd_header^.prdtl := prdt_count;
cmd_header^.clear_busy := 1;
// Setup the command table (using slot 0)
cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE)));
for i:=0 to prdt_count - 1 do begin
cmd_table^.prdt[i].dba := vtop(uint32(buffer) + (i * 4194304));
if i = prdt_count - 1 then begin
cmd_table^.prdt[i].dbc := count - (i * 4194304) - 1; // 512 bytes (0-based count)
end else begin
cmd_table^.prdt[i].dbc := 4194304 - 1; // 512 bytes (0-based count)
end;
cmd_table^.prdt[i].int := 1; // Interrupt on completion
end;
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
fis^.command := ATA_CMD_READ_DMA_EXT;
fis^.device := $40; // LBA mode 48?
fis^.lba0 := lba and $FF;
fis^.lba1 := (lba shr 8) and $FF;
fis^.lba2 := (lba shr 16) and $FF;
fis^.lba3 := (lba shr 24) and $FF;
fis^.lba4 := (lba shr 32) and $FF;
fis^.countl := count and $FF;
fis^.counth := (count shr 8) and $FF;
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or (1 shl slot);
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent read DMA command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: Read DMA command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and (1 shl slot)) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and (1 shl slot)) <> 0 then begin
console.writestringln('AHCI: Error sending read DMA command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
send_read_dma := true;
end;
function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
tfd : uint32;
sec_count : uint32;
prdt_count : uint32;
b8 : puint16;
slot : uint32;
begin
console.writestringln('AHCI: Sending write DMA command');
reset_port(device^.port);
//prdt count is 22 bits
prdt_count := (count div 4194304) + 1;
if prdt_count > 32 then begin
send_write_dma := false;
exit;
end;
// Use command slot 0 for the IDENTIFY command.
slot := find_cmd_slot(device);
cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER)));
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 1;
cmd_header^.prdtl := prdt_count;
cmd_header^.clear_busy := 1;
// Setup the command table (using slot 0)
cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE)));
for i:=0 to prdt_count - 1 do begin
cmd_table^.prdt[i].dba := vtop(uint32(buffer) + (i * 4194304));
if i = prdt_count - 1 then begin
cmd_table^.prdt[i].dbc := count - (i * 4194304) - 1; // 512 bytes (0-based count)
end else begin
cmd_table^.prdt[i].dbc := 4194304 - 1; // 512 bytes (0-based count)
end;
cmd_table^.prdt[i].int := 1; // Interrupt on completion
end;
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
fis^.command := ATA_CMD_WRITE_DMA_EXT;
fis^.device := $40; // LBA mode 48?
count:= count div 512;
fis^.lba0 := lba and $FF;
fis^.lba1 := (
lba shr 8) and $FF;
fis^.lba2 := (
lba shr 16) and $FF;
fis^.lba3 := (
lba shr 24) and $FF;
fis^.lba4 := (
lba shr 32) and $FF;
fis^.countl := count and $FF;
fis^.counth := (
count shr 8) and $FF;
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or (1 shl slot);
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent write DMA command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: Write DMA command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and (1 shl slot)) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and (1 shl slot)) <> 0 then begin
console.writestringln('AHCI: Error sending write DMA command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
send_write_dma := true;
end;
//read atapi
function read_atapi(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
tfd : uint32;
sec_count : uint32;
prdt_count : uint32;
b8 : puint16;
slot : uint32;
begin
console.writestringln('AHCI: Sending read ATAPI command');
reset_port(device^.port);
//prdt count is 22 bits
prdt_count := (count div 4194304) + 1;
if prdt_count > 32 then begin
read_atapi := false;
exit;
end;
// Use command slot 0 for the IDENTIFY command.
slot := find_cmd_slot(device);
cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER)));
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 0;
cmd_header^.prdtl := 1;
cmd_header^.atapi := 1;
cmd_header^.clear_busy := 1;
// Setup the command table (using slot 0)
cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE)));
cmd_table^.prdt[0].dba := vtop(uint32(buffer));
cmd_table^.prdt[0].dbc := 2048 - 1; // 512 bytes (0-based count
cmd_table^.prdt[0].int := 1; // Interrupt on completion
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
fis^.command := ATA_CMD_PACKET;
fis^.device := $A0;
fis^.featurel := fis^.featurel or 1; //setting both of these might not work on real hardware
fis^.featurel := fis^.featurel or (1 shl 2);
cmd_table^.acmd[0] := $A8; //ATAPI command
cmd_table^.acmd[1] := $0; //ATAPI command
cmd_table^.acmd[2] := uInt8((lba shr 24) and $FF);
cmd_table^.acmd[3] := uInt8((lba shr 16) and $FF);
cmd_table^.acmd[4] := uInt8((lba shr 8) and $FF);
cmd_table^.acmd[5] := uInt8(lba and $FF);
cmd_table^.acmd[9] := uInt8((1) and $FF);
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or (1 shl slot);
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent read ATAPI command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: Read ATAPI command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and (1 shl slot)) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and (1 shl slot)) <> 0 then begin
console.writestringln('AHCI: Error sending read ATAPI command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
read_atapi := true;
end;
end.

View File

@ -0,0 +1,320 @@
// 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.
{
Drivers->Storage->AHCI->AHCITypes - AHCI Driver Types.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit AHCITypes;
interface
uses
util,
PCI,
drivertypes,
drivermanagement,
lmemorymanager,
console,
vmemorymanager,
idetypes,
lists;
const
AHCI_CONTROLLER_MODE = $80000000;
CMD_LIST_ALIGN = 1024;
CMD_TBL_ALIGN = 128;
NUM_CMD_ENTRIES = 32;
//device type signatures
SATA_SIG_ATA = $00000101;
SATA_SIG_ATAPI = $EB140101;
SATA_SIG_SEMB = $C33C0101;
SATA_SIG_PM = $96690101;
type
{
AHCI Host Bus Adapter (HBA) Memory-Mapped Register Interface
This structure is used to access the AHCI HBA's memory-mapped registers.
The AHCI HBA's memory-mapped registers are used to configure the HBA and
to issue commands to the SATA devices connected to the HBA.
The AHCI HBA's memory-mapped registers are accessed by reading and writing
to the HBA's memory-mapped I/O space. The HBA's memory-mapped I/O space is
typically mapped to a region of physical memory by the system's BIOS or
UEFI firmware. The HBA's memory-mapped I/O space is typically mapped to a
region of physical memory that is accessible to the system's CPU(s) and
other devices.
}
THBA_Port = bitpacked record
cmdl_basel: uint32; // Command List Base Address Lower 32-bits
cmdl_baseu: uint32; // Command List Base Address Upper 32-bits
fis_basel: uint32; // FIS Base Address Lower 32-bits
fis_baseu: uint32; // FIS Base Address Upper 32-bits
int_status: uint32; // Interrupt Status
int_enable: uint32; // Interrupt Enable
cmd: uint32; // Command and Status
rsv0: uint32; // Reserved
tfd: uint32; // Task File Data
signature: uint32; // Signature
sata_status: uint32; // SATA Status (SCR0:SStatus)
sata_ctrl: uint32; // SATA Control (SCR2:SControl)
sata_error: uint32; // SATA Error (SCR1:SError)
sata_active: uint32; // SATA Active
cmd_issue: uint32; // Command Issue
sata_noti: uint32; // SATA Notification
fis_switch_ctrl: uint32;// FIS-based Switch Control
rsv1: array[0..10] of uint32;
vendor: array[0..3] of uint32;
end;
PHBA_Port = ^THBA_Port;
{
AHCI Host Bus Adapter (HBA) Memory-Mapped Register Interface
This structure is used to access the AHCI's memory-mapped registers.
The AHCI's memory-mapped registers are used to configure the HBA and to
issue commands to the SATA devices connected to the HBA.
}
THBA_Memory = bitpacked record
capabilites: uint32; // Host Capabilities
global_ctrl: uint32; // Global Host Control
int_status: uint32; // Interrupt Status
ports_implimented: uint32;// Ports Implemented
version: uint32; // Version
ccc_control: uint32; // Command Completion Coalescing Control
ccc_ports: uint32; // Command Completion Coalescing Ports
em_location: uint32; // Enclosure Management Location
em_control: uint32; // Enclosure Management Control
capabilites2: uint32; // Host Capabilities Extended
bohc: uint32; // BIOS/OS Handoff Control and Status
//0x2c to 0x9f reserved
rsv0: array[0..115] of uint8;
//vendor specific
vendor: array[0..95] of uint8;
//port registers
ports: array[0..31] of THBA_Port;
end;
PHBA_Memory = ^THBA_Memory;
{
AHCI Host Bus Adapter (HBA) FIS (Frame Information Structure) Interface
This structure is used to access the AHCI HBA's FIS (Frame Information Structure)
memory-mapped registers. RX
}
THBA_FIS = bitpacked record
dsfis: array[0..$1F] of uint32; // DMA Setup FIS
rsv0: array[0..$1F] of uint32;
psfis: array[0..$1F] of uint32; // PIO Setup FIS
rsv1: array[0..$1F] of uint32;
rfis: array[0..$1F] of uint32; // D2H Register FIS
rsv2: array[0..$1F] of uint32;
sdbfis: array[0..$F] of uint32; // Set Device Bits FIS
ufis: array[0..$1F] of uint32; // Unknown FIS
rsv3: array[0..$67] of uint32;
end;
PHBA_FIS = ^THBA_FIS;
//enum fis type
TFISType = (
FIS_TYPE_REG_H2D = $27, // Register FIS - Host to Device
FIS_TYPE_REG_D2H = $34, // Register FIS - Device to Host
FIS_TYPE_DMA_ACT = $39, // DMA Activate FIS - Device to Host
FIS_TYPE_DMA_SETUP = $41, // DMA Setup FIS - Bidirectional
FIS_TYPE_DATA = $46, // Data FIS - Bidirectional
FIS_TYPE_BIST = $58, // BIST Activate FIS - Bidirectional
FIS_TYPE_PIO_SETUP = $5F, // PIO Setup FIS - Device to Host
FIS_TYPE_DEV_BITS = $A1 // Set Device Bits FIS - Device to Host
);
{
AHCI Host Bus Adapter (HBA) FIS (Frame Information Structure) Interface
This structure is used to access the AHCI HBA's FIS (Frame Information Structure)
}
THBA_FIS_REG_H2D = bitpacked record
fis_type: uint8; // FIS Type
// pmport: uint8; // Port Multiplier Port is pmport:4 and rsv0:3 and i:1
pmport: ubit4; // Port Multiplier Port
rsv: ubit3; // Reserved
c: ubit1; // command or control
command: uint8; // Command
featurel: uint8; // Feature Lower 8-bits
lba0: uint8; // LBA0
lba1: uint8; // LBA1
lba2: uint8; // LBA2
device: uint8; // Device
lba3: uint8; // LBA3
lba4: uint8; // LBA4
lba5: uint8; // LBA5
featureh: uint8; // Feature Upper 8-bits
countl: uint8; // Count Lower 8-bits
counth: uint8; // Count Upper 8-bits
icc: uint8; // Isochronous Command Completion
control: uint8; // Control
rsv0: array[0..3] of uint8;
end;
PHBA_FIS_REG_H2D = ^THBA_FIS_REG_H2D;
{
AHCI Host Bus Adapter (HBA) Command Header Interface
}
THBA_CMD_HEADER = bitpacked record
cmd_fis_length: UBit5; // Command FIS Length
atapi: UBit1; // ATAPI
wrt: UBit1; // Write
prefetchable: UBit1; // Prefetchable
reset: UBit1; // Reset
bist: UBit1; // BIST
clear_busy: UBit1; // Clear Busy
reserved0: UBit1; // Reserved
port_multiplier: UBit4; // Port Multiplier Port
prdtl: uint16; // Physical Region Descriptor Table Length
prdbc: uint32; // Physical Region Descriptor Byte Count
cmd_table_base: uint32; // Command Table Base Address
cmd_table_baseu: uint32; // Command Table Base Address Upper 32-bits
rsv0: array[0..3] of uint32;
end;
PHBA_CMD_HEADER = ^THBA_CMD_HEADER;
{
AHCI Host Bus Adapter (HBA) Command Table Interface
}
THBA_PRD = bitpacked record
dba: uint32; // Data Base Address
dbau: uint32; // Data Base Address Upper 32-bits
rsv0: uint32; // Reserved
dbc: ubit22; // Data Byte Count
reserved: ubit9; // Reserved
int: ubit1; // Interrupt
// dbc: uint32; // Data Byte Count, bit 1 is Interrupt, then 22 bits of Byte Count, then 9 bits of Reserved
end;
TPRDT = array[0..31] of THBA_PRD;
PPRDT = ^TPRDT;
{
AHCI Host Bus Adapter (HBA) Command Table Interface
}
THBA_CMD_TABLE = bitpacked record
cmd_fis: array[0..63] of uint8; // Command FIS
acmd: array[0..15] of uint8; // ATAPI Command
rsv0: array[0..47] of uint8;
prdt: array[0..31] of THBA_PRD; // Physical Region Descriptor Table
end;
PHBA_CMD_TABLE = ^THBA_CMD_TABLE;
TCMD_LIST = array[0..255] of THBA_CMD_HEADER;
PCMD_LIST = ^TCMD_LIST;
//////////////////////////////////////////
//////////// Asuro AHCI types ////////////
//////////////////////////////////////////
{
Device type enum
}
TDeviceType = (
SATA = 1,
ATAPI = 2,
SEMB,
PM
);
{
AHCI device reference
}
TAHCI_Device = bitpacked record
port : PHBA_Port;
device_type : TDeviceType;
ata_info : TIdentResponse;
command_list : PHBA_CMD_HEADER;
fis : PHBA_FIS;
command_table : PHBA_CMD_TABLE;
end;
PAHCI_Device = ^TAHCI_Device;
{
controller reference
}
TAHCI_Controller = bitpacked record
pci_device : PPCI_Device;
mio: PHBA_Memory;
ata_info : TIdentResponse;
devices : array[0..31] of TAHCI_Device;
end;
PAHCI_Controller = ^TAHCI_Controller;
function get_device_type(sig : uint32) : TDeviceType;
// function get_device_type_string(deive_type : TDeviceType) : string;
implementation
function get_device_type(sig : uint32) : TDeviceType;
begin
case sig of
SATA_SIG_ATA: begin
get_device_type := SATA;
end;
SATA_SIG_ATAPI: begin
get_device_type := ATAPI;
end;
SATA_SIG_SEMB: begin
get_device_type := SEMB;
end;
SATA_SIG_PM: begin
get_device_type := PM;
end;
end;
end;
// function get_device_type_string(deive_type : TDeviceType) : string;
// begin
// case deive_type of
// SATA: begin
// get_device_type_string := 'SATA';
// end;
// ATAPI: begin
// get_device_type_string := 'ATAPI';
// end;
// SEMB: begin
// get_device_type_string := 'SEMB';
// end;
// PM: begin
// get_device_type_string := 'PM';
// end;
// end;
// end;
end.

View File

@ -1,85 +0,0 @@
// 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.
{
Drivers->Storage->ATA_ISR - Primary ATA IRQ.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit ATA_ISR;
interface
uses
util,
console,
isr_types,
isrmanager,
IDT;
procedure register();
procedure hook(hook_method : uint32);
procedure unhook(hook_method : uint32);
implementation
var
Hooks : Array[1..MAX_HOOKS] of pp_hook_method;
procedure Main(); interrupt;
var
i : integer;
begin
CLI;
for i:=0 to MAX_HOOKS-1 do begin
if uint32(Hooks[i]) <> 0 then Hooks[i](void($00000000));
end;
console.writestringln('Disk Operation Complete');
end;
procedure register();
begin
memset(uint32(@Hooks[0]), 0, sizeof(pp_hook_method)*MAX_HOOKS);
//isrmanager.registerISR(76, @Main);
//IDT.set_gate(76, uint32(@Main), $08, ISR_RING_0);
end;
procedure hook(hook_method : uint32);
var
i : uint32;
begin
for i:=0 to MAX_HOOKS-1 do begin
if uint32(Hooks[i]) = hook_method then exit;
end;
for i:=0 to MAX_HOOKS-1 do begin
if uint32(Hooks[i]) = 0 then begin
Hooks[i]:= pp_hook_method(hook_method);
exit;
end;
end;
end;
procedure unhook(hook_method : uint32);
var
i : uint32;
begin
for i:=0 to MAX_HOOKS-1 do begin
If uint32(Hooks[i]) = hook_method then Hooks[i]:= nil;
exit;
end;
end;
end.

View File

@ -1,246 +1,506 @@
{ ************************************************ // Copyright 2021 Aaron Hance
* Asuro //
* Unit: Drivers/storage/IDE // Licensed under the Apache License, Version 2.0 (the "License");
* Description: IDE ATA Driver // you may not use this file except in compliance with the License.
* // You may obtain a copy of the License at
************************************************ //
* Author: Aaron Hance // http://www.apache.org/licenses/LICENSE-2.0
* Contributors: //
************************************************ } // Unless required by applicable law or agreed to in writing, software
unit IDE; // 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.
{
Drivers->Storage->ide - IDE Driver.
NOT FINSIHED NEEDS WORK
@author(Aaron Hance <ah@aaronhance.me>)
}
unit ide;
interface interface
uses uses
util, ata,
drivertypes, atapi,
console, console,
terminal,
drivermanagement, drivermanagement,
vmemorymanager, drivertypes,
idetypes,
isrmanager,
lmemorymanager, lmemorymanager,
storagemanagement, storagetypes,
strings, strings,
tracer; terminal,
tracer,
util,
vmemorymanager;
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 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; blockSize: 0; info: nil),
bar0 : uint32; (exists: false; isPrimary: true; isMaster: false; isATAPI: false;
bar1 : uint32; status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false);
bar4 : uint32; base: ATA_PRIMARY_BASE; blockSize: 0; 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; blockSize: 0; 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; blockSize: 0; info: nil)
);
buffer : Puint32;
procedure init();
function load(ptr: void) : boolean; function load(ptr: void) : boolean;
function identify_device(bus : uint8; device : uint8) : TIdentResponse; procedure init();
function get_status(var device : TIDE_Device) : TIDE_Status;
// procedure flush(); function wait_for_device(device : TIDE_Device; ioop : boolean) : boolean;
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8); procedure no_interrupt(isPrimary : boolean);
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8); procedure enable_interrupt(isPrimary : boolean);
//read/write must be capable of reading/writting any amknt of data upto disk size procedure reset_device(device : TIDE_Device);
procedure select_device(device : TIDE_Device);
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32);
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32);
implementation 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 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; end;
procedure port_write(register : uint8; data : uint8); procedure enable_interrupt(isPrimary : boolean);
var var
i : uint8; reg : uint8;
begin begin
outb(ATA_PRIMARY_BASE + register, data); if isPrimary then begin
util.psleep(1); reg := inb(ATA_INTERRUPT_PRIMARY);
if register = ATA_REG_COMMAND then begin reg := reg and not (1 shl 1);
for i:= 0 to 5 do begin outb(ATA_INTERRUPT_PRIMARY, reg);
port_read(ATA_REG_STATUS); end else begin
end; reg := inb(ATA_INTERRUPT_SECONDARY);
reg := reg and not (1 shl 1);
outb(ATA_INTERRUPT_SECONDARY, reg);
end; end;
end; end;
procedure no_interrupt(device : uint8); //soft reset
begin procedure reset_device(device : TIDE_Device);
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 var
status : uint8; reg : uint8;
begin
if device.isPrimary then begin
reg := inb(ATA_INTERRUPT_PRIMARY);
reg := reg and (1 shl 2);
outb(ATA_INTERRUPT_PRIMARY, reg);
end else begin
reg := inb(ATA_INTERRUPT_SECONDARY);
reg := reg and (1 shl 2);
outb(ATA_INTERRUPT_SECONDARY, reg);
end;
sleep(20);
if device.isPrimary then begin
reg := inb(ATA_INTERRUPT_PRIMARY);
reg := reg and not (1 shl 2);
outb(ATA_INTERRUPT_PRIMARY, reg);
end else begin
reg := inb(ATA_INTERRUPT_SECONDARY);
reg := reg and not (1 shl 2);
outb(ATA_INTERRUPT_SECONDARY, reg);
end;
end;
{
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; ioop : boolean) : boolean;
var
status : TIDE_Status;
i : uint32; i : uint32;
begin begin
//wait for drive to be ready push_trace('ide.wait_for_device()');
while true do begin
status := port_read(ATA_REG_COMMAND);
if(status and ATA_SR_ERR) = ATA_SR_ERR then begin i := 0;
console.writestringln('[IDE] (IDENTIFY_DEVICE) DRIVE ERROR!');
console.redrawWindows(); while (i < 50000) do begin
is_ready := false; status := get_status(device);
break;
if (status.BUSY = false) and (status.DRQ or (not ioop)) then begin //todo test
wait_for_device := true;
pop_trace();
exit;
end; end;
if (status and ATA_SR_BUSY) <> ATA_SR_BUSY then begin i := i + 1;
is_ready := true;
break;
end;
end;
end; end;
function validate_28bit_address(addr : uint32) : boolean; wait_for_device := false;
begin pop_trace();
validate_28bit_address := (addr and $F0000000) = 0;
end; 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 var
status : uint8; dev : uint8;
identResponse : TIdentResponse; reg : uint8;
begin
push_trace('ide.select_device()');
dev := ATA_DEVICE_SLAVE;
if device.isMaster then begin
dev := ATA_DEVICE_MASTER;
end;
reg := inb(device.base + ATA_REG_HDDEVSEL);
reg := (reg and $F0) or dev;
outb(device.base + ATA_REG_HDDEVSEL, reg);
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;
errorReg : uint8;
begin
push_trace('ide.get_status()');
// select_device(device);
status := TIDE_Status(inb(device.base + ATA_REG_STATUS));
device.status := status;
if status.ERROR then begin
errorReg := inb(device.base + ATA_REG_ERROR);
console.writestringln('[IDE] ERROR detected!');
console.writestring('[IDE] ERROR REGISTER: ');
console.writebin8ln(errorReg);
if (errorReg and $04) <> 0 then console.writestringln('[IDE] ERROR: Aborted Command');
if (errorReg and $10) <> 0 then console.writestringln('[IDE] ERROR: ID Not Found');
if (errorReg and $40) <> 0 then console.writestringln('[IDE] ERROR: Uncorrectable Data');
if (errorReg and $80) <> 0 then console.writestringln('[IDE] ERROR: Bad Block');
console.redrawWindows();
end;
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
//reset any device signature
{
Write 0x04 to the Control Register (0x3F6 for primary bus, 0x376 for secondary).
Wait ~5 microseconds.
Write 0x00 back to Control Register to complete reset.
// }
outb(ATA_PRIMARY_BASE1, $04);
outb(ATA_SECONDARY_BASE1, $04);
sleep(1);
// outb(ATA_PRIMARY_BASE1, $00);
// outb(ATA_SECONDARY_BASE1, $00);
select_device(device);
sleep(1);
//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);
console.writestring('[IDE] (check_device_type) SEC: ');
console.writehexln(sec);
console.writestring('[IDE] (check_device_type) LBA0: ');
console.writehexln(lba0);
console.writestring('[IDE] (check_device_type) LBA1: ');
console.writehexln(lba1);
console.writestring('[IDE] (check_device_type) LBA2: ');
console.writehexln(lba2);
//check if the device is an ATAPI device
if ((sec = 3) or (lba2 = $EB)) then begin
check_device_type := true;
device.isATAPI := true;
end else if sec = 1 then begin
check_device_type := true;
device.isATAPI := false;
end else begin
check_device_type := false;
end;
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;
size : uint32;
begin
push_trace('ide.load_device()');
success := check_device_type(device);
if (is_device_present(device) and success) then begin
console.writestringln('[IDE] (load_device) Device is present');
if (device.isATAPI) then begin
console.writestringln('[IDE] (load_device) Device is ATAPI');
//todo load device info
success := atapi.identify_device(device);
device.exists := true;
device.isATAPI := true;
end else begin
console.writestringln('[IDE] (load_device) Device is ATA');
success:= ata.identify_device(device);
//check if atapi device using info from identify_device
console.writestringln('[IDE] (load_device) Device info: ');
console.writecharln(char(puint8(device.info)[252]));
console.writecharln(char(puint8(device.info)[253]));
if (puint8(device.info)[253] = $FF) then begin
console.writestringln('[IDE] (load_device) Device is ATAPI');
device.isATAPI := true;
device.exists := true;
success := atapi.identify_device(device);
end;
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
size := atapi.get_device_size(device);
storageDevice^.maxSectorCount := size;
storageDevice^.sectorSize := device.blockSize;
if (device.isMaster) then begin
storageDevice^.controllerId0 := 0;
end else begin
storageDevice^.controllerId0 := 1;
end;
//read and print first sector
buffer := puint8(kalloc(2048));
memset(uint32(buffer), 11, 2048);
atapi.read_pio28(device, 11, 1, puint16(buffer));
console.writestringln('[IDE] (load_device) First sector of ATAPI device: ');
for i:=0 to 16 do begin
console.writehexln(buffer[i]);
end;
memset(uint32(buffer), 0, 2048);
atapi.read_pio28(device, 5200, 1, puint16(buffer));
console.writestringln('[IDE] (load_device) First sector of ATAPI device: ');
for i:=0 to 16 do begin
console.writehexln(buffer[i]);
end;
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(1024));
// memset(uint32(buffer), $02, 1024);
// uint8(buffer[0]) := $1F;
// uint8(buffer[1]) := $2F;
// uint8(buffer[2]) := $3F;
// uint8(buffer[3]) := $4F;
// uint8(buffer[4]) := $5F;
// uint8(buffer[5]) := $6F;
// uint8(buffer[6]) := $7F;
// uint8(buffer[7]) := $8F;
// //write to the first sector
// ata.write_pio28(device, 10, 2, puint16(buffer));
// //read the first sector
// memset(uint32(buffer), 0, 512);
// ata.read_pio28(device, 10, 1, puint16(buffer));
// //check if the data is the same
// for i:=0 to 10 do begin
// console.writehexln(buffer[i]);
// end;
end;
//register the device TODO
// 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; i : uint8;
begin begin
push_trace('IDE.Identify_Device'); push_trace('ide.load()');
device_select(device); console.writestringln('[IDE] (load) Loading IDE Devices');
no_interrupt(device); console.redrawWindows();
port_write(ATA_REG_CONTROL, 0); registerISR(14, @atapi.ide_irq);
registerISR(15, @atapi.ide_irq);
console.writestringln('[IDE] (load) Loading IDE Devices 2');
console.redrawWindows();
pciDevice := PPCI_Device(ptr);
//check if bus is floating load_device(primaryDevices[0]);
status := port_read(ATA_REG_COMMAND); load_device(primaryDevices[1]);
if status = $FF then exit; load_device(secondaryDevices[0]);
load_device(secondaryDevices[1]);
port_write(ATA_REG_SECCOUNT, 0); pop_trace();
port_write(ATA_REG_LBA0, 0); load := true;
port_write(ATA_REG_LBA1, 0);
port_write(ATA_REG_LBA2, 0);
port_write(ATA_REG_COMMAND, ATA_CMD_IDENTIFY); console.writestringln('[IDE] (load) IDE Device Loading Finished');
//check if drive is present i := 0;
status := port_read(ATA_REG_COMMAND);
if status = $00 then exit;
if not is_ready() then exit; if (primaryDevices[0].exists) then begin
console.writestringln('[IDE] (load) FOUND Primary Master Device 0');
for i:=0 to 255 do begin i := i + 1;
identResponse[i] := inw(ATA_PRIMARY_BASE + ATA_REG_DATA);
end; 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; end;
procedure init(); procedure init();
@ -256,193 +516,8 @@ begin
devID.id3:= idANY; devID.id3:= idANY;
devID.id4:= idANY; devID.id4:= idANY;
devID.ex:= nil; 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'); console.writestringln('[IDE] (INIT) END');
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. 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.

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

@ -15,6 +15,8 @@
{ {
Driver->Storage->StorageManagment Storage Managment System Driver->Storage->StorageManagment Storage Managment System
DEPRECATED unit
@author(Aaron Hance ah@aaronhance.me) @author(Aaron Hance ah@aaronhance.me)
} }
unit storagemanagement; unit storagemanagement;
@ -31,14 +33,14 @@ uses
strings, strings,
lists, lists,
tracer, tracer,
rtc; rtc,
MBR,
volumemanager,
storagetypes;
type type
TControllerType = (ControllerIDE, ControllerUSB, ControllerAHCI, ControllerNET, ControllerRAM, rsvctr1, rsvctr2, rsvctr3);
TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry); TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry);
PStorage_volume = ^TStorage_Volume;
PStorage_device = ^TStorage_Device;
APStorage_Volume = array[0..10] of PStorage_volume; APStorage_Volume = array[0..10] of PStorage_volume;
byteArray8 = array[0..7] of char; byteArray8 = array[0..7] of char;
PByteArray8 = ^byteArray8; PByteArray8 = ^byteArray8;
@ -54,41 +56,6 @@ type
PPHIOHook_ = procedure; 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; APStorage_Device = array[0..255] of PStorage_device;
{ Generic directory entry } { Generic directory entry }
@ -110,20 +77,13 @@ procedure init();
procedure register_device(device : PStorage_Device); procedure register_device(device : PStorage_Device);
function get_device_list() : PLinkedListBase; 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 implementation
function controller_type_2_string(controllerType : TControllerType) : pchar; function controller_type_2_string(controllerType : TControllerType) : pchar;
begin begin
case controllerType of 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'; ControllerUSB : controller_type_2_string:= 'USB';
ControllerAHCI : controller_type_2_string:= 'ACHI (SATA)'; ControllerAHCI : controller_type_2_string:= 'ACHI (SATA)';
ControllerNET : controller_type_2_string:= 'Net'; ControllerNET : controller_type_2_string:= 'Net';
@ -169,18 +129,57 @@ var
drive : PStorage_Device; drive : PStorage_Device;
filesystemString : pchar; filesystemString : pchar;
filesystem : PFileSystem;
spc : puint32; spc : puint32;
sectorCount : uint32;
volumeId : uint8;
mb : PMaster_Boot_Record;
i : uint32 = 0;
begin begin
//format <DRIVE_ID> <FILESYSTEM> <size>
spc:= puint32(kalloc(4)); spc:= puint32(kalloc(4));
spc^:= 4; spc^:= 1;
driveIndex:= stringToInt( getParam(1, params) ); driveIndex:= stringToInt( getParam(1, params) );
drive:= PStorage_Device(LL_Get(storageDevices, driveIndex)); drive:= PStorage_Device(LL_Get(storageDevices, driveIndex));
//todo change b4 adding in aniother filesytem filesystemString := getParam(2, params);
PFilesystem(LL_Get(filesystems, 0))^.createCallback(drive, drive^.maxSectorCount-1, 1, spc);
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); writestringWnd('Drive ', getTerminalHWND);
writeintWnd(driveIndex, getTerminalHWND); writeintWnd(driveIndex, getTerminalHWND);
@ -217,10 +216,6 @@ begin
format_command(params); format_command(params);
end; end;
//lsfs command for listing filesystems
if stringEquals(subcmd, 'lsfs') then begin
end;
pop_trace(); pop_trace();
end; end;
@ -231,10 +226,8 @@ begin
setworkingdirectory('.'); setworkingdirectory('.');
storageDevices:= ll_new(sizeof(TStorage_Device)); storageDevices:= ll_new(sizeof(TStorage_Device));
fileSystems:= ll_New(sizeof(TFilesystem));
terminal.registerCommand('DISK', @disk_command, 'Disk utility'); terminal.registerCommand('DISK', @disk_command, 'Disk utility');
pop_trace();
end; end;
{ Register a new drive with the storage manager } { 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)); PStorage_Device(LL_Get(storageDevices, LL_Size(storageDevices) - 1))^.volumes := LL_New(sizeof(TStorage_Volume));
//check for volumes //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(); pop_trace();
end; end;
@ -265,23 +256,4 @@ begin
get_device_list:= storageDevices; get_device_list:= storageDevices;
end; 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. 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.

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

@ -0,0 +1,251 @@
// 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.
{
Drivers->Storage->ATA - ATA Driver.
@author(Aaron Hance <ah@aaronhance.me>)
}
unit ata;
interface
uses
console,
drivermanagement,
drivertypes,
idetypes,
lmemorymanager,
storagetypes,
strings,
terminal,
tracer,
util,
vmemorymanager;
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;
procedure outb(port : uint16; value : uint8);
begin
util.outb(port, value);
psleep(1);
// get_status(primaryDevices[0]);
end;
{
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, false);
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);
psleep(50);
no_interrupt(device.isPrimary);
psleep(50);
set_lba_mode(device, lba);
psleep(50);
outb(device.base + ATA_REG_SECCOUNT, count);
psleep(50);
outb(device.base + ATA_REG_LBA0, (lba and $000000FF));
psleep(50);
outb(device.base + ATA_REG_LBA1, (lba and $0000FF00) shr 8);
psleep(50);
outb(device.base + ATA_REG_LBA2, (lba and $00FF0000) shr 16);
psleep(50);
outb(device.base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
psleep(50);
ready := wait_for_device(device, false);
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
ii:=0;
while ii < 256 do begin
buffer[ii+(i*256)] := inw(device.base + ATA_REG_DATA);
ii := ii + 1;
psleep(50);
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, false);
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
ii:=0;
while ii < 256 do begin
outw(ATA_PRIMARY_BASE + ATA_REG_DATA, buffer[ii+(i*256)]);
ii := ii + 1;
psleep(50);
end;
psleep(50); //todo check
outb(device.base + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
ready := wait_for_device(device, false);
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;
// 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

@ -0,0 +1,448 @@
// 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.
{
Drivers->Storage->ATAPI - ATAPI Driver.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!! CURRENTLY NOT FUNCTIONAL !!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@author(Aaron Hance <ah@aaronhance.me>)
}
unit atapi;
interface
uses
console,
drivermanagement,
drivertypes,
idetypes,
lmemorymanager,
storagetypes,
strings,
terminal,
tracer,
util,
vmemorymanager;
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;
function get_device_size(var device : TIDE_Device) : uint32;
procedure ide_irq();
implementation
uses
ata,
ide;
procedure ide_irq();
begin
console.writestringln('IDE IRQ Handler');
console.redrawWindows();
end;
//print status of the device, alt status register
procedure print_status(device : TIDE_Device);
begin
get_status(device);
console.writestring('Status: ');
// console.writeintln(uint32(device.status));
console.writestring('Busy: ');
console.writeintln(uint32(device.status.Busy));
console.writestring('Ready: ');
console.writeintln(uint32(device.status.Ready));
console.writestring('Fault: ');
console.writeintln(uint32(device.status.Fault));
console.writestring('Seek: ');
console.writeintln(uint32(device.status.Seek));
console.writestring('DRQ: ');
console.writeintln(uint32(device.status.DRQ));
console.writestring('CORR: ');
console.writeintln(uint32(device.status.CORR));
console.writestring('IDDEX: ');
console.writeintln(uint32(device.status.IDDEX));
console.writestring('ERROR: ');
console.writeintln(uint32(device.status.ERROR));
end;
procedure outb(port : uint16; value : uint8);
begin
util.outb(port, value);
psleep(1);
// get_status(primaryDevices[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);
// outb(ATA_PRIMARY_BASE1, $04);
// outb(ATA_SECONDARY_BASE1, $04);
// sleep(1);
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);
psleep(1);
outb(device.base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
status := get_status(device);
if status.ERROR then begin
console.writestringln('Error identifying device, maybe'); //todo
end;
ready := wait_for_device(device, false);
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;
console.writestringln('ATAPI Device identified: ');
for i:=0 to 255 do begin
buffer[i] := inw(device.base + ATA_REG_DATA);
console.writestring('[');
console.writehexpair(buffer[i]);
console.writestring('] ');
if (i mod 4) = 0 then begin
console.writestringln('');
end;
end;
//bits 14 and 15 of word 0
{
00h
01h
Direct-access device
Sequential-access device
02h
03h
Printer device
Processor device
04h
05h
Write-once device
CD-ROM device
06h
07h
Scanner device
Optical memory device
08h
09h
Medium changer device
Communications device
0A-0Bh
0Ch
Reserved for ACS IT8 (Graphic arts pre-press devices)
Array controller device
0Dh
0Eh
Enclosure services device
Reduced block command devices
0Fh
10-1Eh
Optical card reader/writer device
Reserved
1Fh
Unknown or no device type}
//print device type
console.writestring('Device Type: ');
case (buffer[0] shr 8) and $3 of
0: console.writestringln('Direct-access device');
1: console.writestringln('Sequential-access device');
2: console.writestringln('Printer device');
3: console.writestringln('Processor device');
4: console.writestringln('Write-once device');
5: console.writestringln('CD-ROM device');
6: console.writestringln('Scanner device');
7: console.writestringln('Optical memory device');
8: console.writestringln('Medium changer device');
9: console.writestringln('Communications device');
10: console.writestringln('Reserved for ACS IT8 (Graphic arts pre-press devices)');
11: console.writestringln('Array controller device');
12: console.writestringln('Reserved');
13: console.writestringln('Enclosure services device');
14: console.writestringln('Reduced block command devices');
15: console.writestringln('Unknown or no device type');
end;
outb(ATA_PRIMARY_BASE1, $04);
outb(ATA_SECONDARY_BASE1, $04);
sleep(10);
device.info := @buffer;
identify_device := true;
end;
function read_PIO28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
var
i, j : uint16;
status : TIDE_Status;
ready : boolean;
packet : array[0..11] of uint8;
begin
push_trace('read_PIO28()');
if not device.isATAPI then begin
console.writestringln('[ATAPI] Device is not an ATAPI device!');
read_PIO28 := false;
exit;
end;
select_device(device);
no_interrupt(device.isPrimary);
// Step 1: Send PACKET command
outb(device.base + ATA_REG_FEATURES, 0); // No special features
outb(device.base + ATA_REG_SECCOUNT, 0); // ATAPI requires this to be 0
outb(device.base + ATA_REG_LBA0, (2048 shr 0) and $FF); // Byte size of transfer (2048 for CDs)
outb(device.base + ATA_REG_LBA1, (2048 shr 8) and $FF);
outb(device.base + ATA_REG_LBA2, 0); // Reserved
outb(device.base + ATA_REG_COMMAND, ATA_CMD_PACKET);
// Step 2: Wait for DRQ=1 (drive wants a command packet)
ready := wait_for_device(device, false);
if not ready then begin
console.writestringln('[ATAPI] Timeout waiting for PACKET DRQ!');
read_PIO28 := false;
exit;
end;
// Step 3: Prepare 12-byte SCSI CDB (Read Sectors Command 0xA8)
packet[0] := $A8; // ATAPI READ (10) command
packet[1] := 0; // Reserved
packet[2] := (lba shr 24) and $FF; // LBA high byte
packet[3] := (lba shr 16) and $FF;
packet[4] := (lba shr 8) and $FF;
packet[5] := (lba shr 0) and $FF;
packet[6] := 0; // Reserved
packet[7] := count; // Number of sectors to read
packet[8] := 0; // Reserved
packet[9] := 0; // Control
packet[10] := 0; // Reserved
packet[11] := 0; // Reserved
// Step 4: Send the command packet
for i := 0 to 5 do
outw(device.base + ATA_REG_DATA, (packet[i*2] or (packet[i*2+1] shl 8)));
// Step 6: Read the data (count * 2048 bytes)
for i := 0 to (count) - 1 do begin
for j := 0 to 1023 do begin
ready := wait_for_device(device, false);
if not ready then begin
console.writestringln('[ATAPI] Timeout waiting for data DRQ!');
read_PIO28 := false;
exit;
end;
buffer[i * 256 + j] := inw(device.base + ATA_REG_DATA);
end;
end;
// Success
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
write_pio28 := false;
end;
function get_device_size(var device: TIDE_Device): uint32;
var
i: uint8;
ready: boolean;
packet: puint16; // Will hold our 10-byte CDB (as 5 words)
response: puint16; // Will hold the 8-byte capacity response
xferCount: uint16;
temp: uInt32;
data: uint16;
begin
get_device_size := 0;
// 1) Select the device (Master/Slave) - presumably your function
select_device(device);
enable_interrupt(device.isPrimary);
// 2) We set "no special features" for ATAPI
outb(device.base + ATA_REG_FEATURES, 0);
// 3) Indicate how many bytes we expect from the drive.
// Usually we place "MaxByteCount" in LBA1:LBA2.
// We need at least 8 bytes for READ CAPACITY(10).
// We'll do 8 => low byte in LBA1, high byte in LBA2.
outb(device.base + ATA_REG_LBA1, $08);
outb(device.base + ATA_REG_LBA2, $00);
// 4) Send the "PACKET" command (0xA0) to start an ATAPI packet transfer
outb(device.base + ATA_REG_COMMAND, ATA_CMD_PACKET);
// 5) Build our 10-byte SCSI "READ CAPACITY(10)" command descriptor block (CDB)
// We'll store it in 5 words = 10 bytes
packet := puint16(kalloc(12)); // 5 * 2 = 10 bytes
memset(uint32(packet), 0, 12);
// READ CAPACITY(10) = opcode 0x25
// The rest can be 0 for a basic capacity read
// We'll store it as little-endian 16-bit words:
// packet[0] = 0x0025 means low byte=0x25 (opcode), high byte=0x00
packet[0] := $0025; // cdb[0..1]
packet[1] := 0; // cdb[2..3]
packet[2] := 0; // cdb[4..5]
packet[3] := 0; // cdb[6..7]
packet[4] := 0; // cdb[8..9]
packet[5] := 0; // cdb[10..11]
// 6) Wait for drive to be ready to receive the CDB
ready := wait_for_device(device, false);
if not ready then
begin
console.writestringln('Device not ready to receive READ CAPACITY(10) CDB.');
exit;
end;
// 7) Write our 5 words (10 bytes) of CDB to the drive
for i := 0 to 5 do
begin
outw(device.base + ATA_REG_DATA, packet[i]);
psleep(1); // short delay
end;
// Free the CDB memory
// kfree(uint32(packet));
// 8) Wait for next phase (the data phase). The device should assert DRQ
// if it has data to send. We'll poll again.
ready := wait_for_device(device, false);
psleep(1);
if not ready then
begin
console.writestringln('Drive not ready after sending the CDB.');
exit;
end;
// 9) The drive indicates how many bytes its about to send in LBA1:LBA2
// xferCount := (inb(device.base + ATA_REG_LBA2) shl 8) or inb(device.base + ATA_REG_LBA1);
// if xferCount < 8 then
// begin
// console.writestringln('Drive is returning less than 8 bytes for capacity!');
// exit;
// end;
// 10) Allocate space for the 8-byte result
response := puint16(kalloc(8));
memset(uint32(response), 0, 8);
// We'll read 4 words = 8 bytes from the data register
for i := 0 to 3 do
begin
ready := wait_for_device(device, false);
if not ready then
begin
console.writestringln('Device got stuck while reading capacity data!');
exit;
end;
psleep(3);
data := inw(device.base + ATA_REG_DATA);
response[i] := data;
psleep(1);
end;
// //fix the order of the bytes of each number
// endiness of device sector count
temp := puint32(response)[0];
puint32(response)[0] := 0;
for i := 0 to 31 do begin
puint32(response)[0] := puint32(response)[0] or ((temp shr i) and 1) shl (31 - i);
end;
//endiness of device sector size
temp := puint32(response)[1];
puint32(response)[1] := 0;
for i := 0 to 31 do begin
puint32(response)[1] := puint32(response)[1] or ((temp shr i) and 1) shl (31 - i);
end;
// 11) Now parse the returned 8 bytes:
// - first 4 bytes = last LBA
// - next 4 bytes = block size
console.writestring('Device sector count: ');
console.writeintln(puint32(response)[0]); // last LBA
console.writebin32ln(puint32(response)[0]);
console.writestring('Device sector size: ');
console.writeintln(puint32(response)[1] ); // block size
console.writebin32ln(puint32(response)[1]);
device.blockSize := puint32(response)[1];
console.writestringln('Device size: ');
console.writeintln(puint32(response)[0] * (puint32(response)[1]) );
// Return the LBA as "size" (some drivers do lastLBA+1).
get_device_size := puint32(response)[0];
end;
end.

View File

@ -10,14 +10,16 @@ interface
uses uses
console, console,
storagemanagement, util,
util, terminal, terminal,
lmemorymanager, lmemorymanager,
strings, strings,
lists, lists,
tracer, tracer,
serial, serial,
rtc; rtc,
volumemanager,
storagetypes;
type type
@ -89,7 +91,7 @@ var
filesystem : TFilesystem; filesystem : TFilesystem;
procedure init; 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); 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 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 //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 //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 var
buffer : puint32; buffer : puint32;
bt: puint32; bt: puint32;
@ -772,9 +774,18 @@ var
programFileArray : byteArray8 = ('P','R','O','G','R','A','M','S'); programFileArray : byteArray8 = ('P','R','O','G','R','A','M','S');
rootCluster : uint32 = 1; 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? begin //maybe increase buffer size by one?
push_trace('fat32.create_volume()'); push_trace('fat32.create_volume()');
disk := volume^.device;
// zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 )); // zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 ));
// memset(uint32(zeroBuffer), 0, 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^.jmp2boot := $0; //TODO impliment boot jump
bootRecord^.OEMName := asuroArray; bootRecord^.OEMName := asuroArray;
bootRecord^.sectorSize := disk^.sectorsize; bootRecord^.sectorSize := disk^.sectorsize;
bootRecord^.spc := config^; bootRecord^.spc := 1;
bootRecord^.rsvSectors := 32; //32 is standard bootRecord^.rsvSectors := 32; //32 is standard
bootRecord^.numFats := 1; bootRecord^.numFats := 1;
bootRecord^.mediaDescp := $F8; bootRecord^.mediaDescp := $F8;
@ -873,8 +884,16 @@ begin //maybe increase buffer size by one?
disk^.writecallback(disk, dataStart + (config^ * rootCluster), 1, buffer); 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); kfree(buffer);
end; end;
procedure detect_volumes(disk : PStorage_Device); 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 if (puint32(buffer)[127] = $55AA) and (PBootRecord(buffer)^.bsignature = $29) then begin //TODO partition table
console.writestringln('FAT32: volume found!'); console.writestringln('FAT32: volume found!');
volume^.device:= disk; 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^.sectorSize:= PBootRecord(buffer)^.sectorSize;
volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first
volume^.filesystem := @filesystem; volume^.filesystem := @filesystem;
storagemanagement.register_volume(disk, volume); // storagemanagement.register_volume(disk, volume); TODO repalce with new thing
end; end;
kfree(buffer); kfree(buffer);
@ -917,6 +936,7 @@ procedure init();
begin begin
push_trace('fat32.init()'); push_trace('fat32.init()');
filesystem.sName:= 'FAT32'; filesystem.sName:= 'FAT32';
filesystem.system_id:= $01;
filesystem.readDirCallback:= @readDirectoryGen; filesystem.readDirCallback:= @readDirectoryGen;
filesystem.createDirCallback:= @writeDirectoryGen; filesystem.createDirCallback:= @writeDirectoryGen;
filesystem.createcallback:= @create_volume; filesystem.createcallback:= @create_volume;
@ -924,7 +944,7 @@ begin
filesystem.writecallback:= @writeFile; filesystem.writecallback:= @writeFile;
filesystem.readcallback := @readFile; filesystem.readcallback := @readFile;
storagemanagement.register_filesystem(@filesystem); volumemanager.register_filesystem(@filesystem);
end; end;
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,148 @@
// 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; //1FC on some systems, 3F6 on all
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;
blockSize : uint32;
info : PIdentResponse;
end;
TIDE_PACKET = record
command : uint8;
lba : uint32;
count : uint8;
buffer : puint16;
end;
implementation
end.

View File

@ -0,0 +1,95 @@
// 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,
ControllerAHCI_ATAPI,
ControllerNVMe,
ControllerNET,
ControllerRAM,
ControllerSCSI
);
TFileSystemType = (
FileSystemFAT32,
FileSystemExFAT,
FileSystemEXT,
FileSystemEXT2,
FileSystemCDFS,
FileSystemOther
);
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 : PDList;
writeCallback : PPHIOHook;
readCallback : PPHIOHook;
maxSectorCount : uint32;
sectorSize : uint32; //in bytes
start : uint32; //start of device in sectors
end;
{ Generic storage volume }
TStorage_Volume = record
id : uint32;
device : PStorage_device;
name : pchar;
fileSystem : TFileSystemType;
size : uint32; //size of volume in sectors
start : uint32; //start of volume in sectors
end;
PStorage_Volume = ^TStorage_Volume;
TDrive_Error = record
code : uint16;
description : pchar;
recoverable : boolean;
end;
implementation
end.

View File

@ -0,0 +1,108 @@
// 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 - Volume manager for storage devices.
@author(Aaron Hance ah@aaronhance.me)
}
unit volumemanager;
interface
uses
console,
drivermanagement,
drivertypes,
lists,
lmemorymanager,
MBR,
rtc,
serial,
storagetypes,
strings,
terminal,
tracer,
util;
var
storageDevices : PDList; //index in this array is global drive id
volumes : PDList;
procedure init();
procedure register_device(device : PStorage_Device);
function discover_volumes(device : PStorage_device) : PDList;
function get_volume_list() : PDList;
implementation
procedure init();
begin
push_trace('VolumeManager.init');
storageDevices := DL_New(sizeof(PStorage_Device));
volumes := DL_New(sizeof(PStorage_Volume));
//TODO register commnads
pop_trace();
end;
procedure register_device(device : PStorage_Device);
var
diskVolumes : PDList;
begin
push_trace('VolumeManager.register_device');
DL_Add(storageDevices);
DL_Set(storageDevices, DL_Size(storageDevices) - 1, puint32(device));
diskVolumes := discover_volumes(device);
if volumes <> nil then begin
volumes := DL_Concat(volumes, diskVolumes);
end;
pop_trace();
end;
function discover_volumes(device : PStorage_device) : PDList;
begin
push_trace('VolumeManager.discover_volumes');
discover_volumes := nil;
//TODO implement
pop_trace();
end;
function get_volume_list() : PDList;
var
newList : PDList;
i : uint32;
begin
push_trace('VolumeManager.get_volume_list');
//get copy of volume list
newList := DL_New(sizeof(PStorage_Volume)); //TODO need to add DL_Copy function
for i := 0 to DL_Size(volumes) - 1 do begin
DL_Add(newList);
DL_Set(newList, i, DL_Get(volumes, i));
end;
end;
end.

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
Include->Strings - String Manipulation. Include->Strings - String Manipulation.
@author(Kieron Morris <kjm@kieronmorris.me>) @author(Kieron Morris <kjm@kieronmorris.me>)
@author(Aaron Hance <ah@aaronhance.me>)
} }
unit strings; unit strings;
@ -33,6 +34,10 @@ function stringCopy(str : pchar) : pchar;
function stringNew(size : uint32) : pchar; function stringNew(size : uint32) : pchar;
function stringSize(str : pchar) : uint32; function stringSize(str : pchar) : uint32;
function stringConcat(str1, str2 : pchar) : pchar; 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 stringContains(str : pchar; sub : pchar) : boolean;
function stringToInt(str : pchar) : uint32; function stringToInt(str : pchar) : uint32;
function hexStringToInt(str : pchar) : uint32; function hexStringToInt(str : pchar) : uint32;
@ -151,6 +156,94 @@ begin
stringConcat:= result; stringConcat:= result;
end; 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; function stringContains(str : pchar; sub : pchar) : boolean;
var var
strEnd, subEnd, i, j, count : uint32; strEnd, subEnd, i, j, count : uint32;

View File

@ -335,6 +335,7 @@ begin
//serial.sendString('[outb]'); //serial.sendString('[outb]');
//serial.sendHex(port); //serial.sendHex(port);
//serial.sendHex(val); //serial.sendHex(val);
psleep(1);
asm asm
PUSH EAX PUSH EAX
PUSH EDX PUSH EDX

View File

@ -33,6 +33,8 @@ procedure init;
procedure registerISR(INT_N : uint8; callback : TISRHook); procedure registerISR(INT_N : uint8; callback : TISRHook);
implementation implementation
uses
console;
var var
Hooks : TISRHookArray; Hooks : TISRHookArray;
@ -56,6 +58,13 @@ var
i : uint8; i : uint8;
begin begin
// if int_n <> $20 then begin
// console.writestring('ISR: ');
// console.writehexpair(Int_N);
// console.writestringln(' called');
// end;
for i:=0 to MAX_HOOKS do begin for i:=0 to MAX_HOOKS do begin
if Hooks[INT_N][i] <> nil then Hooks[INT_N][i](); if Hooks[INT_N][i] <> nil then Hooks[INT_N][i]();
end; end;

View File

@ -40,10 +40,13 @@ uses
testdriver, testdriver,
E1000, E1000,
IDE, IDE,
storagemanagement, // storagemanagement,
// drivemanager,
volumemanager,
lists, lists,
net, net,
fat32, // fat32,
// flatfs,
isrmanager, isrmanager,
faults, faults,
fonts, fonts,
@ -54,7 +57,8 @@ uses
base64, base64,
rand, rand,
terminal, terminal,
hashmap, vfs; hashmap, vfs,
AHCI;
procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall; procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall;
@ -205,7 +209,9 @@ begin
tracer.push_trace('kmain.DRVMGMT'); tracer.push_trace('kmain.DRVMGMT');
drivermanagement.init(); drivermanagement.init();
tracer.push_trace('kmain.STRMGMT'); tracer.push_trace('kmain.STRMGMT');
storagemanagement.init(); // storagemanagement.init();
// drivemanager.init();
// volumemanager.init();
{ Hook Timer for Ticks } { Hook Timer for Ticks }
tracer.push_trace('kmain.TMR'); tracer.push_trace('kmain.TMR');
@ -213,7 +219,8 @@ begin
TMR_0_ISR.hook(uint32(@bios_data_area.tick_update)); TMR_0_ISR.hook(uint32(@bios_data_area.tick_update));
{ Filsystems } { Filsystems }
fat32.init(); // fat32.init();
// flatfs.init();
{ Device Drivers } { Device Drivers }
tracer.push_trace('kmain.DEVDRV'); tracer.push_trace('kmain.DEVDRV');
@ -222,7 +229,8 @@ begin
mouse.init(); mouse.init();
testdriver.init(); testdriver.init();
E1000.init(); E1000.init();
IDE.init(); // IDE.init();
AHCI.init();
console.outputln('KERNEL', 'DEVICE DRIVERS: INIT END.'); console.outputln('KERNEL', 'DEVICE DRIVERS: INIT END.');
{ Bus Drivers } { Bus Drivers }

View File

@ -57,6 +57,7 @@ var
procedure init; procedure init;
function kalloc(size : uint32) : void; function kalloc(size : uint32) : void;
function kalloc(size : uint32; isPersistent : boolean) : void;
function klalloc(size : uint32) : void; function klalloc(size : uint32) : void;
procedure klfree(size : uint32); //Todo ??? Profit? procedure klfree(size : uint32); //Todo ??? Profit?
function kpalloc(address : uint32) : void; function kpalloc(address : uint32) : void;
@ -217,6 +218,11 @@ begin
//pop_trace; //pop_trace;
end; end;
function kalloc(size : uint32; isPersistent : boolean) : void;
begin
kalloc:= kalloc(size);
end;
procedure kfree(area : void); procedure kfree(area : void);
var var
hp : PHeapPage; hp : PHeapPage;

View File

@ -22,7 +22,15 @@ unit edit;
interface interface
uses uses
console, terminal, keyboard, shell, strings, tracer, storagemanagement, lmemorymanager, util; console,
keyboard,
lmemorymanager,
shell,
storagemanagement,
strings,
terminal,
tracer,
util;
procedure init(); procedure init();

View File

@ -25,7 +25,7 @@ uses
tracer, console, tracer, console,
//progs //progs
base64_prog, md5sum, shell, terminal, base64_prog, md5sum, shell, terminal,
edit, netlog, themer, netlog, themer,
memview, udpcat, dhclient, vbeinfo; memview, udpcat, dhclient, vbeinfo;
{ Initialize all baked-in programs } { Initialize all baked-in programs }
@ -43,8 +43,8 @@ begin
themer.init(); themer.init();
tracer.push_trace('progmanager.netlog.init'); tracer.push_trace('progmanager.netlog.init');
netlog.init(); netlog.init();
tracer.push_trace('progmanager.edit.init'); // tracer.push_trace('progmanager.edit.init');
edit.init(); // edit.init();
tracer.push_trace('progmanager.udpcat.init'); tracer.push_trace('progmanager.udpcat.init');
udpcat.init(); udpcat.init();
tracer.push_trace('progmanager.md5sum.init'); tracer.push_trace('progmanager.md5sum.init');

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
}