15 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
29 changed files with 4346 additions and 1442 deletions

1
.gitignore vendored
View File

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

2
.vscode/launch.json vendored
View File

@ -6,7 +6,7 @@
"type": "PowerShell", "type": "PowerShell",
"preLaunchTask": "Build", "preLaunchTask": "Build",
"script": "${workspaceFolder}/virtualbox-wrapper.ps1", "script": "${workspaceFolder}/virtualbox-wrapper.ps1",
"args": ["-MachineName", "7d395c96-891c-4139-b77d-9b6b144b0b93"], "args": ["-Command", "up"],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
} }
] ]

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

View File

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

View File

@ -50,23 +50,26 @@ 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": "PowerShell",
"preLaunchTask": "Build",
"script": "${workspaceFolder}/virtualbox-wrapper.ps1",
"args": ["-MachineName", "7d395c96-891c-4139-b77d-9b6b144b0b93"],
"cwd": "${workspaceFolder}",
}
]
} }
``` ```
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!

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,248 +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,
terminal,
tracer, tracer,
drivemanager, util,
storagetypes; 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),
buffer : Puint32; (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)
);
procedure init();
function load(ptr : void) : boolean;
function identify_device(bus : uint8; device : uint8) : TIdentResponse;
// procedure flush(); function load(ptr: void) : boolean;
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8); procedure init();
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8); function get_status(var device : TIDE_Device) : TIDE_Status;
//read/write must be capable of reading/writting any amknt of data upto disk size function wait_for_device(device : TIDE_Device; ioop : boolean) : boolean;
procedure no_interrupt(isPrimary : boolean);
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32); procedure enable_interrupt(isPrimary : boolean);
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32); procedure reset_device(device : TIDE_Device);
procedure select_device(device : TIDE_Device);
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
end; outb(ATA_INTERRUPT_PRIMARY, inb(ATA_INTERRUPT_PRIMARY) or (1 shl 1));
end else begin
procedure port_write(register : uint8; data : uint8); outb(ATA_INTERRUPT_SECONDARY, inb(ATA_INTERRUPT_SECONDARY) or (1 shl 1));
var
i : uint8;
begin
outb(ATA_PRIMARY_BASE + register, data);
util.psleep(1);
if register = ATA_REG_COMMAND then begin
for i:= 0 to 5 do begin
port_read(ATA_REG_STATUS);
end;
end; end;
end; end;
procedure no_interrupt(device : uint8); procedure enable_interrupt(isPrimary : boolean);
begin
outb($3F6, inb($3f6) or (1 shl 1));
end;
procedure device_select(device : uint8);
begin
outb($1F6, device); //TODO clean
end;
function is_ready() : boolean;
var var
status : uint8; reg : uint8;
begin
if isPrimary then begin
reg := inb(ATA_INTERRUPT_PRIMARY);
reg := reg and not (1 shl 1);
outb(ATA_INTERRUPT_PRIMARY, reg);
end else begin
reg := inb(ATA_INTERRUPT_SECONDARY);
reg := reg and not (1 shl 1);
outb(ATA_INTERRUPT_SECONDARY, reg);
end;
end;
//soft reset
procedure reset_device(device : TIDE_Device);
var
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; end;
break;
end; wait_for_device := false;
pop_trace();
end;
{
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
dev : uint8;
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;
end; end;
function validate_28bit_address(addr : uint32) : boolean; {
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 begin
validate_28bit_address := (addr and $F0000000) = 0; 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; end;
function identify_device(bus : uint8; device : uint8) : TIdentResponse;
function load(ptr: void) : boolean;
var var
status : uint8; pciDevice : PPCI_Device;
identResponse : TIdentResponse; 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();
@ -258,208 +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;
test : PStorage_device;
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]); //TODO wtf is hpc
masterDevice.sectorSize:= 512;
if masterDevice.maxSectorCount <> 0 then begin
IDEDevices[0].exists:= true;
masterDevice.readCallback:= @dread;
masterDevice.writeCallback:= @dwrite;
// storagemanagement.register_device(@masterDevice);
drivemanager.register_device(@masterDevice);
end;
end;
// buffer:= puint8(kalloc(512));
// buffer[0] := 1;
// buffer[1] := 2;
// buffer[2] := 3;
// buffer[3] := 4;
// buffer[4] := 5;
// buffer[5] := 6;
// writePIO28(0, 3, buffer);
// writePIO28(0, 3, buffer);
// writePIO28(0, 3, buffer);
// writePIO28(0, 4, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// psleep(1000);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// writePIO28(0, 5, buffer);
// kfree(puint32(buffer));
console.writestringln('[IDE] (LOAD) END');
end;
procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8);
var
status : uint8;
i: uint16;
device: uint8;
data: uint16;
begin
push_trace('IDE.readPIO28');
if not validate_28bit_address(LBA) then begin
console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!');
end;
// push_trace('IDE.readPIO28.2');
//Add last 4 bits of LBA to device port
if IDEDevices[drive].isMaster then begin
device:= ATA_DEVICE_MASTER;
device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master
end
else begin
device:= ATA_DEVICE_SLAVE;
device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave
end;
// push_trace('IDE.readPIO28.3');
no_interrupt(device);
port_write(ATA_REG_ERROR, 0);
//Write sector count and LBA
port_write(ATA_REG_SECCOUNT, 1);
port_write(ATA_REG_LBA0, (LBA and $000000FF));
port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8);
port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16);
// push_trace('IDE.readPIO28.4');
//send read command
port_write(ATA_REG_COMMAND, ATA_CMD_READ_PIO);
if not is_ready() then exit;
i:=0;
while i < 512 do begin
// if not is_ready() then exit;
data:= inw(ATA_PRIMARY_BASE + ATA_REG_DATA);
buffer[i+1] := uint8($00ff and (data shr 8));
buffer[i] := uint8($00ff and data);
i:= i + 2;
end;
// push_trace('IDE.readPIO28.5');
end;
procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8);
var
status : uint8;
i: uint16;
device: uint8;
begin
push_trace('IDE.WritePIO28');
if not validate_28bit_address(LBA) then begin
console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!');
end;
console.writeintln(uint32(drive));
console.writeintln(LBA);
//Add last 4 bits of LBA to device port
if IDEDevices[drive].isMaster then begin
device:= ATA_DEVICE_MASTER;
device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master
end
else begin
device:= ATA_DEVICE_SLAVE;
device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave
end;
// no_interrupt(device);
port_write(ATA_REG_ERROR, 0);
port_write(ATA_REG_CONTROL, 0);
// check if bus is floating
status := port_read(ATA_REG_COMMAND);
if status = $FF then exit;
//Write sector count and LBA
port_write(ATA_REG_SECCOUNT, 1);
port_write(ATA_REG_LBA0, (LBA and $000000FF));
port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8);
port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16);
//send write command
port_write(ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
//write data
i:=0;
while i < 512 do begin
outw(ATA_PRIMARY_BASE + ATA_REG_DATA, uint16(buffer[i] or (buffer[i+1] shl 8)));
i:= i + 2;
end;
//flush drive cache
psleep(1);
port_write(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
psleep(1);
if not is_ready() then exit;
end;
procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32);
var
i : uint16;
begin
push_trace('IDE.dread');
for i:=0 to sectorCount-1 do begin
readPIO28(device^.controllerId0, LBA + i, puint8(@buffer[512*i]));
psleep(100)
end;
end;
procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32);
var
i : uint16;
begin
for i:=0 to sectorCount-1 do begin
writePIO28(device^.controllerId0, LBA + i, puint8(@buffer[512*i]));
psleep(100)
end;
// writePIO28(device^.controllerId0, LBA, puint8(buffer));
end;
end. end.

View File

@ -271,7 +271,7 @@ begin
PStorage_device(elm)^.volumes := LL_New(sizeof(TStorage_Volume)); PStorage_device(elm)^.volumes := LL_New(sizeof(TStorage_Volume));
//TODO register with volume manager //TODO register with volume manager
volumemanager.check_for_volumes(PStorage_Device(elm)); //volumemanager.check_for_volumes(PStorage_Device(elm));
end; end;
{ Get the drive list } { Get the drive list }
@ -290,7 +290,8 @@ begin
//return string of controller type //return string of controller type
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';

View File

@ -82,7 +82,8 @@ 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';

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

@ -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

@ -26,8 +26,26 @@ uses
type type
TControllerType = (ControllerIDE, ControllerUSB, ControllerAHCI, TControllerType = (
ControllerNET, ControllerRAM, rsvctr1, rsvctr2, rsvctr3); ControllerATA,
ControllerATAPI,
ControllerUSB,
ControllerAHCI,
ControllerAHCI_ATAPI,
ControllerNVMe,
ControllerNET,
ControllerRAM,
ControllerSCSI
);
TFileSystemType = (
FileSystemFAT32,
FileSystemExFAT,
FileSystemEXT,
FileSystemEXT2,
FileSystemCDFS,
FileSystemOther
);
PStorage_device = ^TStorage_Device; PStorage_device = ^TStorage_Device;
PDrive_Error = ^TDrive_Error; PDrive_Error = ^TDrive_Error;
@ -44,13 +62,26 @@ type
controller : TControllerType; controller : TControllerType;
controllerId0 : uint32; controllerId0 : uint32;
writable : boolean; writable : boolean;
volumes : PLinkedListBase; volumes : PDList;
writeCallback : PPHIOHook; writeCallback : PPHIOHook;
readCallback : PPHIOHook; readCallback : PPHIOHook;
maxSectorCount : uint32; maxSectorCount : uint32;
sectorSize : uint32; sectorSize : uint32; //in bytes
start : uint32; //start of device in sectors
end; 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 TDrive_Error = record
code : uint16; code : uint16;
description : pchar; description : pchar;

View File

@ -13,417 +13,96 @@
// limitations under the License. // limitations under the License.
{ {
Driver->Storage->VolumeManager Drive volume manager Driver->Storage->VolumeManager - Volume manager for storage devices.
@author(Aaron Hance ah@aaronhance.me) @author(Aaron Hance ah@aaronhance.me)
} }
unit volumemanager; unit volumemanager;
interface interface
uses uses
util,
console, console,
terminal, drivermanagement,
strings, drivertypes,
lists, lists,
tracer,
lmemorymanager, lmemorymanager,
vfs,
MBR, MBR,
storagetypes; rtc,
serial,
type storagetypes,
strings,
TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry); terminal,
tracer,
PDirectory_Entry = ^TDirectory_Entry; util;
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 var
storageDevices : PDList; //index in this array is global drive id
storageVolumes : PLinkedListBase; volumes : PDList;
filesystems : PlinkedListBase;
fileHandles : PLinkedListBase;
{ Returns the number of volumes }
procedure init(); procedure init();
procedure register_device(device : PStorage_Device);
function discover_volumes(device : PStorage_device) : PDList;
procedure register_filesystem(filesystem : PFilesystem); function get_volume_list() : PDList;
procedure check_for_volumes(drive : PStorage_device);
procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32);
//IO functions
implementation 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(); procedure init();
begin begin
push_trace('VolumeManager.init'); push_trace('VolumeManager.init');
{ setup lists } storageDevices := DL_New(sizeof(PStorage_Device));
storageVolumes:= ll_New(sizeof(TFilesystem)); volumes := DL_New(sizeof(PStorage_Volume));
filesystems:= ll_New(sizeof(TFilesystem));
fileHandles:= ll_New(sizeof(TFilehandleEntry));
{ register commands } //TODO register commnads
pop_trace();
terminal.registerCommand('volume', @volume_command, 'Volume utility');
end; end;
{ register a new type of filesystem } procedure register_device(device : PStorage_Device);
procedure register_filesystem(filesystem : PFilesystem); //TODO on init create null filesystem for empty partition
var var
elm : void; diskVolumes : PDList;
begin begin
//add filesystem push_trace('VolumeManager.register_device');
elm := LL_add(filesystems);
memcpy(uint32(filesystem), uInt32(elm), SizeOf(TFilesystem));
//check drives for volumes of new type DL_Add(storageDevices);
end; DL_Set(storageDevices, DL_Size(storageDevices) - 1, puint32(device));
{ Check for volumes on a physical device } diskVolumes := discover_volumes(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));
if volumes <> nil then begin
volumes := DL_Concat(volumes, diskVolumes);
end; end;
pop_trace();
end; end;
procedure remove_volume(list : PLinkedListBase; volume : PStorage_Volume); 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 var
newList : PDList;
i : uint32; i : uint32;
elm : void;
begin begin
push_trace('VolumeManager.get_volume_list');
for i:=0 to LL_Size(list) - 1 do begin //get copy of volume list
elm := LL_Get(list, i); newList := DL_New(sizeof(PStorage_Volume)); //TODO need to add DL_Copy function
if (PStorage_Volume(elm)^.device = volume^.device)
and (PStorage_Volume(elm)^.sectorStart = volume^.sectorStart) then begin for i := 0 to DL_Size(volumes) - 1 do begin
LL_Delete(list, i); DL_Add(newList);
break; DL_Set(newList, i, DL_Get(volumes, i));
end;
end; 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. end.

File diff suppressed because it is too large Load Diff

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,13 +40,13 @@ uses
testdriver, testdriver,
E1000, E1000,
IDE, IDE,
storagemanagement, // storagemanagement,
drivemanager, // drivemanager,
volumemanager, volumemanager,
lists, lists,
net, net,
fat32, // fat32,
flatfs, // flatfs,
isrmanager, isrmanager,
faults, faults,
fonts, fonts,
@ -57,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;
@ -209,8 +210,8 @@ begin
drivermanagement.init(); drivermanagement.init();
tracer.push_trace('kmain.STRMGMT'); tracer.push_trace('kmain.STRMGMT');
// storagemanagement.init(); // storagemanagement.init();
drivemanager.init(); // drivemanager.init();
volumemanager.init(); // volumemanager.init();
{ Hook Timer for Ticks } { Hook Timer for Ticks }
tracer.push_trace('kmain.TMR'); tracer.push_trace('kmain.TMR');
@ -218,8 +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(); // flatfs.init();
{ Device Drivers } { Device Drivers }
tracer.push_trace('kmain.DEVDRV'); tracer.push_trace('kmain.DEVDRV');
@ -228,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');

View File

@ -1,16 +1,58 @@
param ( <#
$MachineName You need a local git-ignored localenv.json file with the following content:
) {
"virtualbox": {
VBoxManage.exe startvm $MachineName "MachineName": "your-machine-name or guid"
$running=$true
while($running) {
Start-Sleep -Seconds 1
$status=(VBoxManage.exe list runningvms)
if($status) {
$running=$status.contains($MachineName)
} else {
$running=$false
} }
} }
#>
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
}