Compare commits

..

44 Commits

Author SHA1 Message Date
Lucia Ceionia
5fa8e0efa3 add emulator demo 2023-10-29 19:37:03 -05:00
Lucia Ceionia
e688617286 Support for multiple volumes, filesystem detection (currently FAT) 2023-02-17 06:21:43 -06:00
Lucia Ceionia
d0a5eb4c6f Usermode now loads at 0x800000, Virtual 8086 loads consistent DS&ES for data access 2023-02-17 01:41:30 -06:00
Lucia Ceionia
5fe565b6f7 Filesystem now uses an abstract representation 2023-02-14 23:48:11 -06:00
Lucia Ceionia
1b2184fe52 Kernel now is loaded from a partition, allowing a separate MBR bootloader if desired 2023-02-13 18:23:33 -06:00
Lucia Ceionia
e9c4e993f4 Lots of little things, Changed test output, Usermode test build 2023-02-12 00:42:14 -06:00
Lucia Ceionia
36e66600f5 Can view large count directories, Names trimmed in viewers 2023-02-10 22:17:03 -06:00
Lucia Ceionia
0953fe31ec Improved Linker script & Makefile 2023-02-10 19:20:17 -06:00
Lucia Ceionia
cec3b93c83 File Selector uses a very simple double buffer 2023-02-10 04:45:47 -06:00
Lucia Ceionia
168816cb1e More fun color :3 2023-02-10 04:24:29 -06:00
Lucia Ceionia
1ac79d005b Can return from fun recovery :3 2023-02-10 03:57:44 -06:00
Lucia Ceionia
8c5247c317 Hex editor has separate BSS, Converted some consts to defines 2023-02-10 03:42:53 -06:00
Lucia Ceionia
d1024d7793 Disk does very basic read caching 2023-02-10 01:06:42 -06:00
Lucia Ceionia
89c0f95219 Hex viewer is now a hex editor! 2023-02-09 23:32:51 -06:00
Lucia Ceionia
094e278212 Moved INT 13H disk operations outside DOSFS driver 2023-02-09 13:19:50 -06:00
Lucia Ceionia
c7b69675bb Can navigate directories 2023-02-08 12:48:01 -06:00
Lucia Ceionia
7799813a30 Check for CMOV support on boot 2023-02-08 11:38:35 -06:00
Lucia Ceionia
6a4c1908bb Preliminary support for CHS disks. 2023-02-08 10:33:49 -06:00
Lucia Ceionia
d1f1bfa974 Program loader sets text mode on exit. 2023-02-08 03:41:13 -06:00
Lucia Ceionia
de2edf1404 Improved fault handling 2023-02-07 23:53:13 -06:00
Lucia Ceionia
7f0a94352d Added example disk, Made some minor changes to error output 2023-02-07 18:52:17 -06:00
Lucia Ceionia
0e3ae9c4e3 Can create usermode children, Can load programs from disk 2023-02-06 05:49:03 -06:00
Lucia Ceionia
6e66cb9bbe Can pass args to child via varargs 2023-02-06 03:55:13 -06:00
Lucia Ceionia
5729c6c893 Can spawn a child task in C, Fault handler can return to previous 2023-02-06 03:07:15 -06:00
Lucia Ceionia
b41d65bfce Lots of general cleanup, Call to specified V86 int (from Kernel and Usermode) 2023-02-06 02:27:33 -06:00
Lucia Ceionia
d63430bb4d Bootloader can load more from disk, Text file viewer added 2023-02-06 00:01:48 -06:00
Lucia Ceionia
d0fbc7df56 Added enum for scancodes, More control in hex viewer, hex viewer can switch between 25/50 lines 2023-02-05 19:57:06 -06:00
Lucia Ceionia
7107c0ef8b Fixed the hex viewer formatting lol 2023-02-04 20:09:42 -06:00
Lucia Ceionia
ddadeed70c Added a hex viewer, Fixed a bug in dosfs 2023-02-04 18:51:09 -06:00
Lucia Ceionia
964cbcd68d Added a file selection screen (that does nothing), Moved tests to their own file, Fixed a race condition with the keyboard handler 2023-02-02 21:40:39 -06:00
Lucia Ceionia
2114741766 Might? have fixed one race condition bug. Still crashing for unknown reasons on my laptop rarely 2023-02-02 18:15:14 -06:00
Lucia Ceionia
750b1edc16 Register arguments can be passed to v86 code 2023-02-02 14:52:26 -06:00
Lucia Ceionia
7937de6ef0 Added fun fault recovery :3 (I think I spotted a race condition in exception handling during testing, but whatever, that can wait) 2023-02-02 01:44:35 -06:00
Lucia Ceionia
ce771b19bb Fixed small mistake in last commit 2023-02-02 01:34:49 -06:00
Lucia Ceionia
02f03d2224 Usermode can now call system functions (currently get key, gfx mode) with INT 21H, Test switch to graphics mode is done from Usermode 2023-02-02 00:50:16 -06:00
Lucia Ceionia
679eb8cf57 More advanced Fault handling (recovers to text mode), Keyboard handler converted to C and improved (shifting, simple get_key added) 2023-02-01 20:42:05 -06:00
Lucia Ceionia
0faa2eb553 Adds more fault 'hanlders', Sets A20 for some hardware, Fixes some other minor stuff 2023-02-01 14:18:27 -06:00
Lucia Ceionia
afaf5e1a03 Implemented Paging! Moved Kernel to 0x100000, Moved TSS above 1M, Moved V86 to 0x8000, Moved Usermode test to 0x400000, Moved lots of things! 2023-01-31 21:26:43 -06:00
Lucia Ceionia
43e902e83c Fixed disk handling 2023-01-04 19:36:29 -06:00
Lucia Ceionia
9aa56cdce2 Actually read the dir 2022-09-22 10:08:56 -05:00
Lucia Ceionia
9216b3359a Fixed some Task stuff, added DOSFS filesystem 2022-09-21 17:14:11 -05:00
Lucia Ceionia
6ebee28032 Fixed problem loading null segs on real hardware 2022-09-20 13:13:42 -05:00
Lucia Ceionia
8c309c6d9f Added kernel disk reading test 2022-09-19 19:46:26 -05:00
Lucia Ceionia
606fc37e37 'Better' task management, needs more testing 2022-09-19 16:06:01 -05:00
48 changed files with 5990 additions and 346 deletions

2
.gitignore vendored
View File

@ -1,4 +1,6 @@
*.o *.o
*.bin *.bin
*.img
*.lock *.lock
*.com
bx_enh_dbg.ini bx_enh_dbg.ini

View File

@ -1,19 +1,57 @@
objects = entry.o kernel.o handler.o interrupt.o v86.o print.o objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o gdt.o\
CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding -march=pentium-m -fno-stack-protector -nostdlib -c paging.o fault.o tests.o kbd.o helper.o disk.o file.o fs.o dosfs/dosfs.o fs_dos.o\
progs.o hexedit.o textedit.o
CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding\
-march=i686 -fno-stack-protector -Wno-int-conversion -nostdlib -c
LFLAGS = -Wl,--gc-sections -Wl,--print-gc-sections -m32 -nostartfiles -nostdlib
ifeq ($(OUTFILE),)
OUTFILE = virtdisk.bin
endif
ifeq ($(PARTSTART),)
PARTSTART = 2048
endif
PARTVBR=$(shell echo "$(PARTSTART) * (2^9)" | bc)
PARTVBRBOOT=$(shell echo "$(PARTVBR) + 90" | bc)
KERNSEC=$(shell echo "$(PARTSTART) + 4" | bc)
.PHONY: $(OUTFILE)
all: $(OUTFILE)
$(OUTFILE): boot.bin boot_partition.bin kernel.bin
# Copy system bootloader to boot sector, don't overwrite MBR
dd bs=400 count=1 conv=notrunc if=boot.bin of=$@
# Ensure partition VBR contains EB 5A
echo -n -e '\xeb\x5a' | dd bs=1 seek=$(PARTVBR) count=2 conv=notrunc of=$@
# Copy kernel bootloader to partition VBR
dd bs=1 count=420 seek=$(PARTVBRBOOT) conv=notrunc if=boot_partition.bin of=$@
# TODO Check that disk has enough reserved sectors,
# currently this will overwrite the disk if too few
# Write kernel beyond boot sector, maximum 64K (128 sectors)
dd bs=512 count=128 seek=$(KERNSEC) conv=notrunc if=kernel.bin of=$@
kernel.bin: out.o link.ld usermode.o
clang $(LFLAGS) -Wl,-M -Tlink.ld -ffreestanding -o $@ out.o usermode.o
usermode.o: usermode.bin
objcopy -I binary -O elf32-i386 -B i386 $< $@
%.bin: %.nasm
nasm -o $@ $<
out.o: $(objects)
clang $(LFLAGS) -e entry -r -o $@ $^
%.o: %.nasm %.o: %.nasm
nasm -f elf32 -o $@ $< nasm -f elf32 -o $@ $<
%.o: %.c %.o: %.c
clang $(CFLAGS) -O2 $< clang $(CFLAGS) -ffunction-sections -fdata-sections -Os -o $@ $<
all: $(objects)
nasm boot.nasm -o boot.bin
gcc -Tlink.ld -m32 -ffreestanding -nostartfiles -nostdlib -o kernel.bin\
$(objects)
dd bs=256 count=1 conv=notrunc if=boot.bin of=virtdisk.bin
dd bs=512 seek=1 conv=notrunc if=kernel.bin of=virtdisk.bin
virtdisk: virtdisk:
dd bs=1M count=32 if=/dev/zero of=virtdisk.bin cp virtdisk.bin.ex virtdisk.bin
echo -n -e '\x55\xaa' | dd bs=1 seek=510 conv=notrunc of=virtdisk.bin
clean:
rm -f $(objects) out.o kernel.bin boot.bin usermode.bin

View File

@ -1,2 +1,2 @@
# luciaos # luciaos
<https://ceionia.com/resources/v86.html>

View File

@ -1,6 +1,5 @@
# configuration file generated by Bochs # configuration file generated by Bochs
display_library: x, options="gui_debug" display_library: x, options="gui_debug"
#magic_break: enabled=1
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true, pcidev=false, usb_uhci=false plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true, pcidev=false, usb_uhci=false
config_interface: textconfig config_interface: textconfig
display_library: x display_library: x
@ -12,7 +11,7 @@ floppy_bootsig_check: disabled=0
floppya: type=none floppya: type=none
# no floppyb # no floppyb
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="virtdisk.bin", mode=flat, cylinders=2, heads=16, spt=63, sect_size=512, model="Generic 1234", biosdetect=auto, translation=auto ata0-master: type=disk, path="virtdisk.bin", mode=flat, cylinders=8, heads=16, spt=63, sect_size=512, model="Generic 1234", biosdetect=auto, translation=auto
ata0-slave: type=none ata0-slave: type=none
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata1-master: type=none ata1-master: type=none
@ -32,7 +31,7 @@ vga: extension=vbe, update_freq=5, realtime=1, ddc=builtin
cpu: count=1:1:1, ips=4000000, quantum=16, model=core_duo_t2400_yonah, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 cpu: count=1:1:1, ips=4000000, quantum=16, model=core_duo_t2400_yonah, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
print_timestamps: enabled=0 print_timestamps: enabled=0
debugger_log: - debugger_log: -
magic_break: enabled=0 magic_break: enabled=1
port_e9_hack: enabled=0 port_e9_hack: enabled=0
private_colormap: enabled=0 private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0 clock: sync=none, time0=local, rtc_sync=0

View File

@ -1,18 +1,67 @@
[ORG 0x7c00] ; This boots the active partition.
; Relocates self to 0x7E00, loads the
; first sector of active partition
; to 0x7C00 and jumps
[ORG 0x7C00]
[BITS 16] [BITS 16]
xor ax, ax xor ax, ax
mov ds, ax mov ds, ax
mov es, ax mov es, ax
mov ax, 0x8000
mov ss, ax
mov sp, 0xFF00
; Relocate self
mov di, 0x7E00
mov si, 0x7C00
mov cx, 512
rep movsw
jmp 0:relocated
; TODO Make this calculated, somehow
[SECTION RELOC vstart=0x7E20]
relocated:
mov ah, 0x41 ; int 13 extensions check
mov bx, 0x55AA
int 0x13
jc no_int13
; Find active partition
xor cx, cx
mov si, 0x7E00+0x1BE
.find_active:
lodsb
bt ax, 7
jc read
add si, 15
inc cl
cmp cl, 4
jl .find_active
jmp err
read:
; Put partition start LBA in disk address packet
add si, 7
mov di, addr_packet_start_block
movsw
movsw
; Load the first sector of the partition
xor ax, ax
mov ah, 0x42 mov ah, 0x42
mov si, addr_packet mov si, addr_packet
int 0x13 int 0x13
jnc 0x8000 jc err
; Jump to partition boot
jmp 0:0x7C00
no_int13:
mov si, no_exten_str
mov cx, no_exten_str_end - no_exten_str
jmp print
err:
mov bx, ax
mov si, string
mov cx, string_end - string
print:
push 0xb800 push 0xb800
pop es pop es
xor di, di xor di, di
mov si, string
mov cx, 10
mov ah, 0x7 mov ah, 0x7
err_print: err_print:
lodsb lodsb
@ -21,11 +70,14 @@ loop err_print
hlt_loop: hlt_loop:
hlt hlt
jmp hlt_loop jmp hlt_loop
string: db 'DISK ERROR' string: db 'DISK ERROR'
string_end:
no_exten_str: db 'NO INT13 EXTEN'
no_exten_str_end:
addr_packet: addr_packet:
db 0x10, 0x00 ; size, reserved db 0x10, 0x00 ; size, reserved
dw 0x20 ; blocks dw 1 ; blocks
dd 0x8000 ; transfer buffer addr_packet_transfer_buff_off: dw 0x7C00 ; transfer buffer offset
dq 1 ; start block addr_packet_transfer_buff_seg: dw 0x0000 ; transfer buffer segment
addr_packet_start_block: dq 0 ; start block

136
boot_partition.nasm Normal file
View File

@ -0,0 +1,136 @@
; This boots *from a FAT partition*
; So it starts at 0x7C00+90, the offset of
; the boot code in a FAT VBR
secreadcnt equ 0x38
kernelreads equ 0x10000/(512*secreadcnt) ; 64K / Sector Size FIXME This underestimates when kernel size is not divisible by bytes per read
[ORG 0x7c00+90]
[BITS 16]
xor ax, ax
mov ds, ax
mov es, ax
mov ax, 0x8000
mov ss, ax
mov sp, 0xFF00
; FIXME Assumes INT 13 Extension support
mov ah, 0x42
mov si, addr_packet
int 0x13
jc err
xor cx, cx
mov si, 0x7E00+0x1BE
.find_active:
lodsb
bt ax, 7
jc read
add si, 15
inc cl
cmp cl, 4
jl .find_active
jmp err
read:
mov dh, cl ; Store active partition
add si, 7
mov di, addr_packet_start_block
lodsw
; Offset of kernel in partition
add ax, 4
stosw
lodsw
adc ax, 0
stosw
mov cx, kernelreads
read_loop:
xor ax, ax
mov ah, 0x42
mov si, addr_packet
int 0x13
jc err
add word [addr_packet_transfer_buff_seg], (secreadcnt*512)/16
add word [addr_packet_start_block], secreadcnt
loop read_loop
entry:
cli ; no interrupts
xor ax,ax
mov ds, ax
lgdt [gdt_desc] ; load gdt register
mov eax, cr0 ; set pmode bit
or al, 1
mov cr0, eax
jmp 08h:Pmode
err:
mov si, string
mov cx, string_end - string
print:
push 0xb800
pop es
xor di, di
mov ah, 0x7
err_print:
lodsb
stosw
loop err_print
hlt_loop:
hlt
jmp hlt_loop
[BITS 32]
Pmode:
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; check A20
.a20:
mov edi, 0x107C00
mov esi, 0x007C00
mov [esi], esi
mov [edi], edi
cmpsd
jne .kernel
; FIXME It's annoying that fast A20
; will crash old systems, but it's
; just so small...
in al, 0x92 ; fast A20
test al, 2
jnz .fa20_end
or al, 2
and al, 0xFE
out 0x92, al
.fa20_end:
jmp .a20
.kernel:
mov esi, 0x7E00
mov edi, 0x100000
mov ecx, 0x10000
rep movsb
xchg bx,bx
jmp 08h:0x100000
gdt_desc:
dw gdt_end - gdt
dd gdt
gdt:
gdt_null: dq 0
gdt_code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 10011010b ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 10010010b ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_end:
string: db 'DISK ERROR'
string_end:
addr_packet:
db 0x10, 0x00 ; size, reserved
dw secreadcnt ; blocks
addr_packet_transfer_buff_off: dw 0x7E00 ; transfer buffer offset
addr_packet_transfer_buff_seg: dw 0x0000 ; transfer buffer segment
addr_packet_start_block: dq 0 ; start block

196
disk.c Normal file
View File

@ -0,0 +1,196 @@
#include "disk.h"
#include "v86defs.h"
#include "print.h"
#include "stdint.h"
extern void *memcpy(void *restrict dest, const void *restrict src, uintptr_t n);
#define SECTOR_SIZE 512
#define DISKCACHEBLOCKSIZE 0x1000 // 512 * 4
#define DISKCACHESECTORMASK 7
#define DISKCACHESECTORSIZE 8
#define DISKCACHEBLOCKCOUNT 128
uint8_t (*const DiskCache)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE] = (uint8_t (* const)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE])0x280000;
uint32_t DiskCacheLastRead[DISKCACHEBLOCKCOUNT];
uint32_t DiskCacheSector[DISKCACHEBLOCKCOUNT];
extern uint32_t _gpf_eax_save;
extern uint32_t _gpf_eflags_save;
extern uint16_t error_screen[80*50]; // defined in kernel.c
__attribute__((__no_caller_saved_registers__))
__attribute__((__noreturn__))
extern void error_environment(); // defined in kernel.c
uint32_t numHead;
uint32_t secPerTrack;
uint32_t maxCylinder;
char useCHS = -1;
// TODO This function also inits cache, make that go somewhere else
void Disk_SetupCHS() {
union V86Regs_t regs;
// Check for INT 13 Extensions support
regs.h.ah = 0x41;
regs.w.bx = 0x55AA;
regs.h.dl = 0x80;
V8086Int(0x13, &regs);
// LBA supported if CF clear
if (!(_gpf_eflags_save & 0x1)) {
useCHS = 0;
} else {
FARPTR v86_entry = i386LinearToFp(v86DiskGetGeometry);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
// check CF for error
if (_gpf_eflags_save & 0x1) {
uint16_t *vga = error_screen;
vga += printStr("Could not get Disk Geometry.", vga);
error_environment();
for(;;);
}
numHead = ((_gpf_eax_save & 0xff00) >> 8) + 1;
secPerTrack = _gpf_eax_save & 0x3f;
maxCylinder = ((_gpf_eax_save & 0xff0000) >> 16) | ((_gpf_eax_save & 0xc0) << 2);
useCHS = 1;
}
}
extern uint32_t TIMERVAL;
uint8_t *FindInCache(uint32_t sector) {
uint32_t maskedSector = sector & ~DISKCACHESECTORMASK;
for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) {
// Found
if (DiskCacheSector[i] == maskedSector) {
DiskCacheLastRead[i] = TIMERVAL;
return &(*DiskCache)[i][(sector & DISKCACHESECTORMASK) << 9];
}
}
// Not Found
return 0;
}
void AddToCache(uint32_t sector, uint8_t *buffer) {
uintptr_t lowestFoundTime = DiskCacheLastRead[0];
uintptr_t lowestFoundIdx = 0;
// TODO We should really use something more fast and less lazy
for (int i = 1; i < DISKCACHEBLOCKCOUNT; i++) {
if (DiskCacheLastRead[i] < lowestFoundTime) {
lowestFoundTime = DiskCacheLastRead[i];
lowestFoundIdx = i;
}
}
for (int i = 0; i < DISKCACHEBLOCKSIZE / sizeof(uint32_t); i++)
((uint32_t *)((*DiskCache)[lowestFoundIdx]))[i] = ((uint32_t*)buffer)[i];
DiskCacheLastRead[lowestFoundIdx] = TIMERVAL;
DiskCacheSector[lowestFoundIdx] = sector;
}
// NOTE Only updates one sector (512B)
void UpdateCache(uint32_t sector, uint8_t *buffer) {
uint8_t *cache = FindInCache(sector);
// Not in cache, nothing to update
if (!cache) return;
for (int i = 0; i < SECTOR_SIZE/sizeof(uint32_t); i++)
((uint32_t*)cache)[i] = ((uint32_t*)buffer)[i];
}
void InitCache() {
for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) {
DiskCacheLastRead[i] = 0;
DiskCacheSector[i] = -1;
}
}
// NOTE Must be run before reading from disk
void InitDisk() {
InitCache();
Disk_SetupCHS();
}
uint32_t Disk_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
uint8_t *cache = FindInCache(sector);
if (cache) {
memcpy(buffer, cache, count * SECTOR_SIZE);
return 0;
}
// NOTE If the buffer provided is outside the 0x20000-0x2F000 range,
// the function will use 0x20000 for the Virtual 8086 process
// and copy to the other buffer after
uint8_t *v86buf = (uintptr_t)buffer >= 0x20000 && (uintptr_t)buffer <= 0x2F000 ?
buffer :
(uint8_t*)0x20000;
// TODO Do error handling
if (!useCHS) {
// LBA Read
v86disk_addr_packet.start_block = sector & ~DISKCACHESECTORMASK;
v86disk_addr_packet.blocks = DISKCACHESECTORSIZE;
v86disk_addr_packet.transfer_buffer =
(uintptr_t)v86buf & 0x000F |
(((uintptr_t)v86buf & 0xFFFF0) << 12);
union V86Regs_t regs;
regs.h.ah = 0x42;
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
if (v86disk_addr_packet.blocks != DISKCACHESECTORSIZE) {
uint16_t *vga = error_screen;
vga += printStr("INT13 Read Secs: ", vga);
vga += printDec(v86disk_addr_packet.blocks, vga);
error_environment();
for(;;);
}
AddToCache(sector & ~DISKCACHESECTORMASK, v86buf);
v86buf = &v86buf[(sector & DISKCACHESECTORMASK) << 9];
} else {
uint32_t tmp = sector / secPerTrack;
uint32_t sec = (sector % (secPerTrack)) + 1;
uint32_t head = tmp % numHead;
uint32_t cyl = tmp / numHead;
union V86Regs_t regs;
regs.w.ax = 0x0201;
regs.h.ch = cyl & 0xff;
regs.h.cl = sec | ((cyl >> 2) & 0xc0);
regs.h.dh = head;
regs.h.dl = 0x80;
regs.w.bx = (uintptr_t)v86buf & 0xFFFF;
FARPTR v86_entry = i386LinearToFp(v86DiskReadCHS);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
}
if (v86buf != buffer)
memcpy(buffer, v86buf, count * SECTOR_SIZE);
return 0;
}
uint32_t Disk_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
// NOTE If the buffer provided is outside the 0x20000-0x2FE00 range,
// the function will use copy that buffer into the Virtual 8086 disk range
uint8_t *v86buf = buffer;
if ((uintptr_t)v86buf < 0x20000 || (uintptr_t)v86buf > 0x2FE00) {
v86buf = (uint8_t *)0x20000;
memcpy(v86buf, buffer, count * SECTOR_SIZE);
}
// TODO This check should probably happen at the kernel level
if (useCHS == -1) {
Disk_SetupCHS();
}
// TODO Do error handling
if (!useCHS) {
// LBA Write
v86disk_addr_packet.start_block = sector;
v86disk_addr_packet.blocks = count;
v86disk_addr_packet.transfer_buffer =
(uintptr_t)v86buf & 0x000F |
(((uintptr_t)v86buf & 0xFFFF0) << 12);
union V86Regs_t regs;
regs.w.ax = 0x4300;
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
UpdateCache(sector, buffer);
} else {
uint16_t *vga = error_screen;
vga += printStr("CHS Write Unimplemented", vga);
error_environment();
for(;;);
}
return 0;
}

7
disk.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
void InitDisk();
uint32_t Disk_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
uint32_t Disk_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);

1249
dosfs/dosfs.c Executable file

File diff suppressed because it is too large Load Diff

374
dosfs/dosfs.h Executable file
View File

@ -0,0 +1,374 @@
/*
DOSFS Embedded FAT-Compatible Filesystem
(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
*/
#ifndef _DOSFS_H
#define _DOSFS_H
#include <stdint.h>
//===================================================================
// User-supplied functions
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
//===================================================================
// Configurable items
#define MAX_PATH 64 // Maximum path length (increasing this will
// GREATLY increase stack requirements!)
#define DIR_SEPARATOR '/' // character separating directory components
// End of configurable items
//===================================================================
//===================================================================
// 32-bit error codes
#define DFS_OK 0 // no error
#define DFS_EOF 1 // end of file (not an error)
#define DFS_WRITEPROT 2 // volume is write protected
#define DFS_NOTFOUND 3 // path or file not found
#define DFS_PATHLEN 4 // path too long
#define DFS_ALLOCNEW 5 // must allocate new directory cluster
#define DFS_ERRMISC 0xffffffff // generic error
//===================================================================
// File access modes
#define DFS_READ 1 // read-only
#define DFS_WRITE 2 // write-only
//===================================================================
// Miscellaneous constants
#define SECTOR_SIZE 512 // sector size in bytes
//===================================================================
// Internal subformat identifiers
#define FAT12 0
#define FAT16 1
#define FAT32 2
//===================================================================
// DOS attribute bits
#define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02
#define ATTR_SYSTEM 0x04
#define ATTR_VOLUME_ID 0x08
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20
#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
/*
Directory entry structure
note: if name[0] == 0xe5, this is a free dir entry
if name[0] == 0x00, this is a free entry and all subsequent entries are free
if name[0] == 0x05, the first character of the name is 0xe5 [a kanji nicety]
Date format: bit 0-4 = day of month (1-31)
bit 5-8 = month, 1=Jan..12=Dec
bit 9-15 = count of years since 1980 (0-127)
Time format: bit 0-4 = 2-second count, (0-29)
bit 5-10 = minutes (0-59)
bit 11-15= hours (0-23)
*/
typedef struct _tagDIRENT {
uint8_t name[11]; // filename
uint8_t attr; // attributes (see ATTR_* constant definitions)
uint8_t reserved; // reserved, must be 0
uint8_t crttimetenth; // create time, 10ths of a second (0-199 are valid)
uint8_t crttime_l; // creation time low byte
uint8_t crttime_h; // creation time high byte
uint8_t crtdate_l; // creation date low byte
uint8_t crtdate_h; // creation date high byte
uint8_t lstaccdate_l; // last access date low byte
uint8_t lstaccdate_h; // last access date high byte
uint8_t startclus_h_l; // high word of first cluster, low byte (FAT32)
uint8_t startclus_h_h; // high word of first cluster, high byte (FAT32)
uint8_t wrttime_l; // last write time low byte
uint8_t wrttime_h; // last write time high byte
uint8_t wrtdate_l; // last write date low byte
uint8_t wrtdate_h; // last write date high byte
uint8_t startclus_l_l; // low word of first cluster, low byte
uint8_t startclus_l_h; // low word of first cluster, high byte
uint8_t filesize_0; // file size, low byte
uint8_t filesize_1; //
uint8_t filesize_2; //
uint8_t filesize_3; // file size, high byte
} DIRENT, *PDIRENT;
/*
Partition table entry structure
*/
typedef struct _tagPTINFO {
uint8_t active; // 0x80 if partition active
uint8_t start_h; // starting head
uint8_t start_cs_l; // starting cylinder and sector (low byte)
uint8_t start_cs_h; // starting cylinder and sector (high byte)
uint8_t type; // type ID byte
uint8_t end_h; // ending head
uint8_t end_cs_l; // ending cylinder and sector (low byte)
uint8_t end_cs_h; // ending cylinder and sector (high byte)
uint8_t start_0; // starting sector# (low byte)
uint8_t start_1; //
uint8_t start_2; //
uint8_t start_3; // starting sector# (high byte)
uint8_t size_0; // size of partition (low byte)
uint8_t size_1; //
uint8_t size_2; //
uint8_t size_3; // size of partition (high byte)
} PTINFO, *PPTINFO;
/*
Master Boot Record structure
*/
typedef struct _tagMBR {
uint8_t bootcode[0x1be]; // boot sector
PTINFO ptable[4]; // four partition table structures
uint8_t sig_55; // 0x55 signature byte
uint8_t sig_aa; // 0xaa signature byte
} MBR, *PMBR;
/*
BIOS Parameter Block structure (FAT12/16)
*/
typedef struct _tagBPB {
uint8_t bytepersec_l; // bytes per sector low byte (0x00)
uint8_t bytepersec_h; // bytes per sector high byte (0x02)
uint8_t secperclus; // sectors per cluster (1,2,4,8,16,32,64,128 are valid)
uint8_t reserved_l; // reserved sectors low byte
uint8_t reserved_h; // reserved sectors high byte
uint8_t numfats; // number of FAT copies (2)
uint8_t rootentries_l; // number of root dir entries low byte (0x00 normally)
uint8_t rootentries_h; // number of root dir entries high byte (0x02 normally)
uint8_t sectors_s_l; // small num sectors low byte
uint8_t sectors_s_h; // small num sectors high byte
uint8_t mediatype; // media descriptor byte
uint8_t secperfat_l; // sectors per FAT low byte
uint8_t secperfat_h; // sectors per FAT high byte
uint8_t secpertrk_l; // sectors per track low byte
uint8_t secpertrk_h; // sectors per track high byte
uint8_t heads_l; // heads low byte
uint8_t heads_h; // heads high byte
uint8_t hidden_0; // hidden sectors low byte
uint8_t hidden_1; // (note - this is the number of MEDIA sectors before
uint8_t hidden_2; // first sector of VOLUME - we rely on the MBR instead)
uint8_t hidden_3; // hidden sectors high byte
uint8_t sectors_l_0; // large num sectors low byte
uint8_t sectors_l_1; //
uint8_t sectors_l_2; //
uint8_t sectors_l_3; // large num sectors high byte
} BPB, *PBPB;
/*
Extended BIOS Parameter Block structure (FAT12/16)
*/
typedef struct _tagEBPB {
uint8_t unit; // int 13h drive#
uint8_t head; // archaic, used by Windows NT-class OSes for flags
uint8_t signature; // 0x28 or 0x29
uint8_t serial_0; // serial#
uint8_t serial_1; // serial#
uint8_t serial_2; // serial#
uint8_t serial_3; // serial#
uint8_t label[11]; // volume label
uint8_t system[8]; // filesystem ID
} EBPB, *PEBPB;
/*
Extended BIOS Parameter Block structure (FAT32)
*/
typedef struct _tagEBPB32 {
uint8_t fatsize_0; // big FAT size in sectors low byte
uint8_t fatsize_1; //
uint8_t fatsize_2; //
uint8_t fatsize_3; // big FAT size in sectors high byte
uint8_t extflags_l; // extended flags low byte
uint8_t extflags_h; // extended flags high byte
uint8_t fsver_l; // filesystem version (0x00) low byte
uint8_t fsver_h; // filesystem version (0x00) high byte
uint8_t root_0; // cluster of root dir, low byte
uint8_t root_1; //
uint8_t root_2; //
uint8_t root_3; // cluster of root dir, high byte
uint8_t fsinfo_l; // sector pointer to FSINFO within reserved area, low byte (2)
uint8_t fsinfo_h; // sector pointer to FSINFO within reserved area, high byte (0)
uint8_t bkboot_l; // sector pointer to backup boot sector within reserved area, low byte (6)
uint8_t bkboot_h; // sector pointer to backup boot sector within reserved area, high byte (0)
uint8_t reserved[12]; // reserved, should be 0
uint8_t unit; // int 13h drive#
uint8_t head; // archaic, used by Windows NT-class OSes for flags
uint8_t signature; // 0x28 or 0x29
uint8_t serial_0; // serial#
uint8_t serial_1; // serial#
uint8_t serial_2; // serial#
uint8_t serial_3; // serial#
uint8_t label[11]; // volume label
uint8_t system[8]; // filesystem ID
} EBPB32, *PEBPB32;
/*
Logical Boot Record structure (volume boot sector)
*/
typedef struct _tagLBR {
uint8_t jump[3]; // JMP instruction
uint8_t oemid[8]; // OEM ID, space-padded
BPB bpb; // BIOS Parameter Block
union {
EBPB ebpb; // FAT12/16 Extended BIOS Parameter Block
EBPB32 ebpb32; // FAT32 Extended BIOS Parameter Block
} ebpb;
uint8_t code[420]; // boot sector code
uint8_t sig_55; // 0x55 signature byte
uint8_t sig_aa; // 0xaa signature byte
} LBR, *PLBR;
/*
Volume information structure (Internal to DOSFS)
*/
typedef struct _tagVOLINFO {
uint8_t unit; // unit on which this volume resides
uint8_t filesystem; // formatted filesystem
// These two fields aren't very useful, so support for them has been commented out to
// save memory. (Note that the "system" tag is not actually used by DOS to determine
// filesystem type - that decision is made entirely on the basis of how many clusters
// the drive contains. DOSFS works the same way).
// See tag: OEMID in dosfs.c
// uint8_t oemid[9]; // OEM ID ASCIIZ
// uint8_t system[9]; // system ID ASCIIZ
uint8_t label[12]; // volume label ASCIIZ
uint32_t startsector; // starting sector of filesystem
uint8_t secperclus; // sectors per cluster
uint16_t reservedsecs; // reserved sectors
uint32_t numsecs; // number of sectors in volume
uint32_t secperfat; // sectors per FAT
uint16_t rootentries; // number of root dir entries
uint32_t numclusters; // number of clusters on drive
// The fields below are PHYSICAL SECTOR NUMBERS.
uint32_t fat1; // starting sector# of FAT copy 1
uint32_t rootdir; // starting sector# of root directory (FAT12/FAT16) or cluster (FAT32)
uint32_t dataarea; // starting sector# of data area (cluster #2)
} VOLINFO, *PVOLINFO;
/*
Flags in DIRINFO.flags
*/
#define DFS_DI_BLANKENT 0x01 // Searching for blank entry
/*
Directory search structure (Internal to DOSFS)
*/
typedef struct _tagDIRINFO {
uint32_t currentcluster; // current cluster in dir
uint8_t currentsector; // current sector in cluster
uint8_t currententry; // current dir entry in sector
uint8_t *scratch; // ptr to user-supplied scratch buffer (one sector)
uint8_t flags; // internal DOSFS flags
} DIRINFO, *PDIRINFO;
/*
File handle structure (Internal to DOSFS)
*/
typedef struct _tagFILEINFO {
PVOLINFO volinfo; // VOLINFO used to open this file
uint32_t dirsector; // physical sector containing dir entry of this file
uint8_t diroffset; // # of this entry within the dir sector
uint8_t mode; // mode in which this file was opened
uint32_t firstcluster; // first cluster of file
uint32_t filelen; // byte length of file
uint32_t cluster; // current cluster
uint32_t pointer; // current (BYTE) pointer
} FILEINFO, *PFILEINFO;
/*
Get starting sector# of specified partition on drive #unit
NOTE: This code ASSUMES an MBR on the disk.
scratchsector should point to a SECTOR_SIZE scratch area
Returns 0xffffffff for any error.
If pactive is non-NULL, this function also returns the partition active flag.
If pptype is non-NULL, this function also returns the partition type.
If psize is non-NULL, this function also returns the partition size.
*/
uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize);
/*
Retrieve volume info from BPB and store it in a VOLINFO structure
You must provide the unit and starting sector of the filesystem, and
a pointer to a sector buffer for scratch
Attempts to read BPB and glean information about the FS from that.
Returns 0 OK, nonzero for any error.
*/
uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo);
/*
Open a directory for enumeration by DFS_GetNextDirEnt
You must supply a populated VOLINFO (see DFS_GetVolInfo)
The empty string or a string containing only the directory separator are
considered to be the root directory.
Returns 0 OK, nonzero for any error.
*/
uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo);
/*
Get next entry in opened directory structure. Copies fields into the dirent
structure, updates dirinfo. Note that it is the _caller's_ responsibility to
handle the '.' and '..' entries.
A deleted file will be returned as a NULL entry (first char of filename=0)
by this code. Filenames beginning with 0x05 will be translated to 0xE5
automatically. Long file name entries will be returned as NULL.
returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
or DFS_ERRMISC for a media error
*/
uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent);
/*
Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
provide a pointer to a sector-sized scratch buffer.
Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
to access the file from this point on.
*/
uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo);
/*
Read an open file
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
pointer to a SECTOR_SIZE scratch buffer.
Note that returning DFS_EOF is not an error condition. This function updates the
successcount field with the number of bytes actually read.
*/
uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
/*
Write an open file
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
pointer to a SECTOR_SIZE scratch buffer.
This function updates the successcount field with the number of bytes actually written.
*/
uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
/*
Seek file pointer to a given position
This function does not return status - refer to the fileinfo->pointer value
to see where the pointer wound up.
Requires a SECTOR_SIZE scratch buffer
*/
void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch);
/*
Delete a file
scratch must point to a sector-sized buffer
*/
uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch);
// If we are building a host-emulation version, include host support
#ifdef HOSTVER
#include "hostemu.h"
#endif
#endif // _DOSFS_H

366
dosfs/readme.txt Executable file
View File

@ -0,0 +1,366 @@
README.TXT (C) Copyright 2006
DOSFS Level 1 Version 1.02 Lewin A.R.W. Edwards (sysadm@zws.com)
=====================================================================
Abstract
========
DOSFS is a FAT-compatible filesystem intended for fairly low-end
embedded applications. It is not the leanest possible implementation
(the leanest FAT implementations operate in << 512 bytes of RAM, with
heavy restrictions). This code strikes a good balance between size
and functionality, with an emphasis on RAM footprint.
Intended target systems would be in the ballpark of 1K RAM, 4K ROM
or more.
Features:
* Supports FAT12, FAT16 and FAT32 volumes
* Supports storage devices up to 2048Gbytes in size (LBA32)
* Supports devices with or without MBRs (hard disks vs. floppy disks
or ZIP drives formatted as "big floppies")
* Supports multiple partitions on disks with MBRs
* Supports subdirectories
* Can be operated with a single global 512-byte sector buffer
* Fully reentrant code (assuming the underlying physical device driver
is reentrant and global sector buffers are not used). There are no
global variables in the filesystem
* Does not perform any memory allocation
* Partial support for random-access files
Applications:
* Firmware upgrades
* Failsafe IPL
* Media playback
* Data logging
* Configuration storage
There is no technical support for this free product; however, if you
have questions or suggestions, you are encouraged to email Lewin
Edwards at sysadm@zws.com. If you need custom additions to the code,
or if you have other projects for which you need engineering
assistance, please feel free to email or call (646) 549-3715.
License
=======
The license for DOSFS is very simple but verbose to state.
1. DOSFS is (C) Copyright 2006 by Lewin A.R.W. Edwards ("Author").
All rights not explicitly granted herein are reserved. The DOSFS
code is the permanent property of the Author and no transfer of
ownership is implied by this license.
2. DOSFS is an educational project, provided as-is. No guarantee of
performance or suitability for any application is stated or
implied. You use this product entirely at your own risk. Use of
this product in any manner automatically waives any right to seek
compensation or damages of any sort from the Author. Since the
products you might make are entirely out of the Author's control,
use of this product also constitutes an agreement by you to take
full responsibility for and indemnify the Author against any
action for any loss or damage (including economic loss of any
type, and specifically including patent litigation) that arises
from a product made by you that incorporates any portion of
the DOSFS code.
3. If you live under the jurisdiction of any legislation that would
prohibit or limit any condition in this license, you cannot be
licensed to use this product.
4. If you do not fall into the excluded category in point 3, you are
hereby licensed to use the DOSFS code in any application that you
see fit. You are not required to pay any fee or notify the Author
that you are using DOSFS. Any modifications made by you to the
DOSFS code are your property and you may distribute the modified
version in any manner that you wish. You are not required to
disclose sourcecode to such modifications, either to the Author or
to any third party. Any such disclosure made to the Author will
irrevocably become the property of the Author in the absence of a
formal agreement to the contrary, established prior to such
disclosure being made.
To summarize the intent of the above: DOSFS is free. You can do what
you want with it. Anything that happens as a result is entirely your
responsibility. You can't take ownership of my code and stop me from
doing whatever I want with it. If you do something nifty with DOSFS
and send me the sourcecode, I may include your changes in the next
distribution and it will be released to the world as free software.
If someone sues you because your DOSFS-containing product causes
any sort of legal, financial or other problem, it's your lawsuit,
not mine, and you'll exclude me from the proceedings.
User-Supplied Functions
=======================
You must provide functions to read sectors into memory and write
them back to the target media. The demo suite includes an emulation
module that reads/writes a disk image file (#define HOSTVER pulls
in hostemu.h which wraps the prototypes for these functions).
There are various tools for UNIX, DOS, Windows et al, to create
images from storage media; my preferred utility is dd.
The functions you must supply in your embedded app are:
DFS_ReadSector(unit,buffer,sector,count)
DFS_WriteSector(unit,buffer,sector,count)
These two functions read and write, respectively, "count" sectors of
size SECTOR_SIZE (512 bytes; see below) from/to physical sector
#"sector" of device "unit", to/from the scratch buffer "buffer". They
should return 0 for success or nonzero for failure. In the current
implementation of DOSFS, count will always be 1.
The "unit" argument is designed to permit implementation of multiple
storage devices, for example multiple media slots on a single device,
or to differentiate between master and slave devices on an ATAPI bus.
This code is designed for 512-byte sectors. Although the sector size
is a #define, you should not tinker with it because the vast majority
of FAT filesystems use 512-byte sectors, and the DOSFS code doesn't
support runtime determination of sector size. This will not affect the
vast majority of users.
Example Code
============
Refer to the tests in main.c to see how to call DOSFS functions.
(These tests are all commented out). Note that the only two files
you need to add to your project are dosfs.c and dosfs.h.
Mounting Volumes
================
--If the device has a partition table (practically all removable flash
media are formatted this way), call DFS_GetPtnStart to get the
starting sector# of the desired partition. You can optionally also
retrieve the active state, partition type byte and partition size
in this step. The reason this step is broken out separately is so
you can support devices that are formatted like a floppy disk, i.e.
the volume starts directly at physical sector 0 of the media.
--Call DFS_GetVolInfo to read filesystem info into a VOLINFO structure.
DFS_GetVolInfo needs to know the unit number and partition starting
sector (as returned by DFS_GetPtnStart, or 0 if this is a "floppy-
format" volume without an MBR).
From this point on, the VOLINFO structure is all you'll need - you can
forget the unit and partition start sector numbers.
Enumerating Directory Contents
==============================
--Call DFS_Opendir and supply a path, populated VOLINFO and a
DIRINFO structure to receive the results. Note - you must PREPOPULATE
the DIRINFO.scratch field with a pointer to a sector scratch buffer.
This buffer must remain unmolested while you have the directory open
for searching.
--Call DFS_GetNext to receive the DIRENT contents for the next directory
item. This function returns DFS_OK for no error, and DFS_EOF if there
are no more entries in the directory being searched.
Before using the DIRENT, check the first character of the name. If it
is NULL, then this is an unusable entry - call DFS_GetNext again to
keep searching. LFN directory entries are automatically tagged this way
so your application will not be pestered by them.
Note: A designed side-effect of this code is that when you locate the
file of interest, the DIRINFO.currentcluster, DIRINFO.currentsector
and DIRINFO.currententry-1 fields will identify the directory entry of
interest.
Reading a File
==============
--Call DFS_OpenFile with mode = DFS_READ and supply a path and the relevant
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used
to refer to the file.
--Optionally call DFS_Seek to set the file pointer. If you attempt to set
the file pointer past the end of file, the file will NOT be extended. Check
the FILEINFO.pointer value after DFS_Seek to verify that the pointer is
where you expect it to be.
--Observe that functionality similar to the "whence" parameter of fseek() can
be obtained by using simple arithmetic on the FILEINFO.pointer and
FILEINFO.filelen members.
--Call DFS_ReadFile with the FILEINFO you obtained from OpenFile, and a
pointer to a buffer plus the desired number of bytes to read, and a
pointer to a sector-sized scratch buffer. The reason a scratch sector is
required is because the underlying sector read function doesn't know
about partial reads.
--Note that a file opened for reading cannot be written. If you need r/w
access, open with mode = DFS_WRITE (see below).
Writing a file
==============
--Call DFS_OpenFile with mode = DFS_WRITE and supply a path and the relevant
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used to
refer to the file.
--Optionally call DFS_Seek to set the file pointer. Refer to the notes on
this topic in the section on reading files, above.
--Call DFS_WriteFile with the FILEINFO you obtained from OpenFile, and a
pointer to the source buffer, and a pointer to a sector-sized scratch
buffer.
--Note that a file open for writing can also be read.
--Files are created automatically if they do not exist. Subdirectories are
NOT automatically created.
--If you open an existing file for writing, the file pointer will start at
the beginning of the data; if you want to append, seek to the end before
writing new data.
--If you perform random-access writes to a file, the length will NOT change
unless you exceed the file's original length. There is currently no
function to truncate a file at the current pointer position.
--On-disk consistency is guaranteed when DFS_WriteFile exits, unless your
physical layer has a writeback cache in it.
Deleting a file
===============
--Call DFS_UnlinkFile
--WARNING: This call will delete a subdirectory (correctly) but will NOT
first recurse the directory to delete the contents - so you will end up
with lost clusters.
Notes
=====
Some platforms may require explicit pragmas or attributes to the structures
and unions. For example, arm-gcc will require __attribute__ ((__packed__))
otherwise it will try to be "smart" and place the uint8_t members on 4-byte
boundaries. There is no truly elegant compiler-independent method to get
around this sort of problem.
The code assumes either a von Neumann architecture, or a compiler that
is smart enough to understand where your pointers are aimed and emit
the right kind of memory read and write instructions. The implications
of this statement depend on your target processor and the compiler you
are using. Be very careful not to straddle bank boundaries on bank-
switched memory systems.
Physical 32-bit sector numbers are used throughout. Therefore, the
CHS geometry (if any) of the storage media is not known to DOSFS. Your
sector r/w functions may need to query the CHS geometry and perform
mapping.
File timestamps set by DOSFS are always 1:01:00am on Jan 1, 2006. If
your system has a concept of real time, you can enhance this.
FILEINFO structures contain a pointer to the corresponding VOLINFO
used to open the file, mainly in order to avoid mixups but also to
obviate the need for an extra parameter to every file read/write. DOSFS
assumes that the VOLINFO won't move around. If you need to move or
destroy VOLINFOs pertaining to open files, you'll have to fix up the
pointer in the FILEINFO structure yourself.
The subdirectory delimiter is a forward slash ( '/' ) by default. The
reason for this is to avoid the common programming error of forgetting
that backslash is an escape character in C strings; i.e. "\MYDIR\FILE"
is NOT what you want; "\\MYDIR\\FILE" is what you wanted to type. If you
are porting DOS code into an embedded environment, feel free to change
this #define.
DOSFS does not have a concept of "current directory". A current directory
is owned by a process, and a process is an operating system concept.
DOSFS is a filesystem library, not an operating system. Therefore, any
path you provide to a DOSFS call is assumed to be relative to the root of
the volume.
There is no call to close a file or directory that is open for reading or
writing. You can simply destroy or reuse the data structures allocated for
that operation; there is no internal state in DOSFS so no cleanup is
necessary. Similarly, there is no call to close a file that is open for
writing. (Observe that dosfs.c has no global variables. All state information
is stored in data structures provided by the caller).
MAX_PATH is defined as 64. MS-type DOS filesystems support 128 characters
or more in paths. You can increase this define, but it may GREATLY
increase memory requirements.
VFAT long filenames are not supported. There is a certain amount of
patent controversy about them, but more importantly they don't really
belong in the scope of a "minimalist embedded filesystem".
Improving Performance
=====================
Read performance is fairly good, but can be improved by implementing read
caching on the FAT (see below) and, depending on your hardware platform,
possibly by implementing multi-sector reads.
Write performance may benefit ENORMOUSLY from platform-specific
optimization, especially if you are working with a flash media type that
has a large erase block size. While it is not possible to offer detailed
platform-independent advice, my general advice is to implement writeback
caching on the FAT area. One method for doing this would be to have a
cache system that lives in the DFS_ReadSector/WriteSector functions (on
top of the physical sector r/w functions) and is initially switched off.
Once you have called DFS_GetVolInfo, you then extract the VOLINFO.fat1
and VOLINFO.rootdir parameters and pass them to your caching layer.
Sectors >= fat1 and < rootdir should be cached. The cache strategy is
determined by the physical storage medium underlying the filesystem.
CACHING HINT:
Observe that there will be numerous read-modify-write operations in the
region from VOLINFO.fat1 through VOLINFO.fat1+VOLINFO.secperfat-1, but
in the region from VOLINFO.fat1+VOLINFO.secperfat through VOLINFO.rootdir
there will ONLY be write operations.
Platform Compatibility
======================
DOSFS was derived from code originally written for ARM7TDMI but
designed to be portable. It has been tested on AVR (using avrgcc),
MSP430 (using Rowley's CrossWorks) and PPC603e (using gcc); the host
test suite has also been validated on x86 using gcc under both Cygwin
and 32-bit Fedora Core 4 Linux.
TODO list
=========
* Add function to create subdirectory
* Make DFS_UnlinkFile recognize non-empty subdirectories
* Support "fast write" files where the FAT is not updated, for
logging applications where latency is important.
Test cases for V1.02
====================
Version 1.02 has NOT been through full regression testing. However the
bugs fixed in this version are important, and people have been asking
about them.
Test cases for V1.01
====================
See below.
Test cases for V1.00
====================
These are the test cases that were used to validate the correct
functionality of the DOSFS suite. Each test was performed on FAT12,
FAT16 and FAT32 volumes. P=Pass, F=Fail.
Case F12 F16 F32
---------------------------------------------------------------------
Get volume information P P P
Open root directory P P P
List contents of root directory (fully populated) P P P
Open subdirectory P P P
List contents of subdirectory (<= 1 cluster) P P P
List contents of large subdirectory (> 1 cluster) P P P
Open 5-level nested subdirectory P P P
Open existing file for reading P P P
Open nonexistent file for reading P P P
Seek past EOF, file open for reading P P P
Seek to cluster boundary P P P
Seek past cluster boundary P P P
Seek backwards to nonzero offset, pointer > cluster size P P P
Block-read entire file >1 cluster in size, odd size P P P
Seek to odd location in file P P P
Perform <1 sector reads from random file locations P P P
Open nonexistent file for writing in root dir P P P
Open nonexistent file for writing in subdir P P P
Repeat prev. 2 tests on volume with 0 free clusters P P P
Seek past EOF, file open for writing P P P
Open existing file for writing in root dir P P P
Write random-length records to file, 20 clusters total P P P
MS-DOS 6.0 SCANDISK cross-check P P P
Revision History
================
Jan-06-2005 larwe Initial release (1.0)
Jan-29-2006 larwe Bugfix release (1.01)
- Fixed error in FAT12 FAT read on boundary of sector
- Improved compilability under avrgcc
Sep-16-2006 larwe Bugfix release (1.02)
- DFS_Seek would not correctly rewind to start of file
- DFS_Seek would not correctly seek to a position not on a cluster
boundary
- DFS_OpenFile fencepost error caused memory access at [start of
string-1] with a local variable
- DFS_OpenFile could not open a file in the root directory

99
dosfs/tmpstring.c Normal file
View File

@ -0,0 +1,99 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
for (; n; n--) *d++ = *s++;
return dest;
}
void *memset(void *dest, int c, size_t n)
{
unsigned char *s = dest;
size_t k;
/* Fill head and tail with minimal branching. Each
* conditional ensures that all the subsequently used
* offsets are well-defined and in the dest region. */
if (!n) return dest;
s[0] = c;
s[n-1] = c;
if (n <= 2) return dest;
s[1] = c;
s[2] = c;
s[n-2] = c;
s[n-3] = c;
if (n <= 6) return dest;
s[3] = c;
s[n-4] = c;
if (n <= 8) return dest;
/* Advance pointer to align it at a 4-byte boundary,
* and truncate n to a multiple of 4. The previous code
* already took care of any head/tail that get cut off
* by the alignment. */
k = -(uintptr_t)s & 3;
s += k;
n -= k;
n &= -4;
/* Pure C fallback with no aliasing violations. */
for (; n; n--, s++) *s = c;
return dest;
}
size_t strlen(const char *s)
{
const char *a = s;
for (; *s; s++);
return s-a;
}
int memcmp(const void *vl, const void *vr, size_t n)
{
const unsigned char *l=vl, *r=vr;
for (; n && *l == *r; n--, l++, r++);
return n ? *l-*r : 0;
}
char *strncpy(char *restrict d, const char *restrict s, size_t n)
{
for (; n && (*d=*s); n--, s++, d++);
memset(d, 0, n);
return d;
}
char *strcpy(char *restrict dest, const char *restrict src)
{
char *restrict d = dest;
const char *restrict s = src;
for (; (*d=*s); s++, d++);
return d;
}
int strcmp(const char *l, const char *r)
{
for (; *l==*r && *l; l++, r++);
return *(unsigned char *)l - *(unsigned char *)r;
}
/* STDLIB DIV FUNCTIONS */
typedef struct { int32_t quot, rem; } div_t;
typedef struct { int64_t quot, rem; } ldiv_t;
div_t div(int num, int den)
{
return (div_t){ num/den, num%den };
}
ldiv_t ldiv(long num, long den)
{
return (ldiv_t){ num/den, num%den };
}

View File

@ -1,143 +1,40 @@
[BITS 16]
global entry global entry
entry: entry:
cli ; no interrupts mov dword [0xb8000], 0x07000700 | 'E' | 'N' << 16
xor ax,ax mov dword [0xb8004], 0x07000700 | 'T' | 'R' << 16
mov ds, ax mov dword [0xb8008], 0x07000700 | 'Y' | ' ' << 16
lgdt [gdt_desc] ; load gdt register lgdt [gdt_desc] ; load gdt register
mov eax, cr0 ; set pmode bit
or al, 1
mov cr0, eax
jmp 08h:Pmodecode jmp 08h:Pmodecode
extern gdt_desc
[BITS 32] [BITS 32]
Pmodecode: Pmodecode:
mov ax, 0x10 ; set up segments mov ax, 0x10 ; set up segments
mov ds, ax mov ds, ax
mov es, ax mov es, ax
mov ss, ax mov ss, ax
mov ebp, 0x400000 mov fs, ax
mov gs, ax
mov ebp, 0x800000
mov esp, ebp mov esp, ebp
mov eax, 0x1f001f00 mov eax, 0x1f001f00
mov ecx, (80*25)/2 mov ecx, (80*25)/2
mov edi, 0xb8000 mov edi, 0xb8000
rep stosd ; clear screen rep stosd ; clear screen
mov eax, dword [kernel_check]
cmp eax, 0x12345678
jne err
call start call start
hlt_loop: hlt_loop:
hlt hlt
jmp hlt_loop jmp hlt_loop
err:
mov dword [0xb8000], 0x07000700 | 'L' | 'O' << 16
mov dword [0xb8004], 0x07000700 | 'A' | 'D' << 16
mov dword [0xb8008], 0x07000700 | 'F' | 'A' << 16
mov dword [0xb800C], 0x07000700 | 'I' | 'L' << 16
jmp hlt_loop
extern start extern start
extern kernel_check
; currently unused first 8MB identity paging
; taken from Linux 0.01
setup_paging:
mov ecx, 1024*3 ; 3K?
xor eax, eax
mov edi, 0x1000
rep stosd ; zero first 3K for some reason
mov edi, 0x4000
mov eax, 0x800007 ; 8MB + 7
std ; fill backwards
.fill:
stosd
sub eax, 0x1000
jge .fill
cld ; fix direction
mov dword [0x0000], 0x2000 + 7
mov dword [0x0004], 0x3000 + 7
mov eax, 0x1000
mov cr3, eax ; page dir start 0x1000
mov eax, cr0
or eax, 0x80000000
mov cr0, eax ; set paging bit
ret ; flushes pre-fetch queue
user_test:
mov dword [0xb8000], 0x0f000f00 | 'U' | 's' << 16
mov dword [0xb8004], 0x0f000f00 | 'e' | 'r' << 16
mov dword [0xb8008], 0x0f000f00 | 'm' | 'o' << 16
mov dword [0xb800C], 0x0f000f00 | 'd' | 'e' << 16
mov word [0xb8010], 0x0f00 | '!'
mov edi, 0xA0000
xor eax, eax
.loop:
mov ecx, 320
rep stosb
inc al
cmp eax, 200
jl .loop
mov eax, 0xA0000
int 0x30 ; Exit
xor ebx, ebx
div bl ; Unhandled DIV0 exception
global jmp_usermode_test
jmp_usermode_test:
pop eax ; return address
mov ebp, esp ; return stack
push ss
push ebp
pushfd
push cs
push eax ; return address
push ds ; other segs, pop
push es ; before iret
push fs ; in exit handler
push gs
mov dword [0x20004], esp ; tss ESP0
mov ax, 0x20 | 3
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push 0x20 | 3
push eax
pushfd
push 0x18 | 3
push user_test
iret
global flushTSS
flushTSS:
mov ax, 0x28
ltr ax
ret
extern tss_data
global ivt
ivt: dd 0x00000000
gdt_desc:
dw gdt_end - gdt
dd gdt
gdt:
gdt_null: dq 0
gdt_code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 10011010b ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 10010010b ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_r3code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 0xFA ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_r3data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 0xF2 ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_tss: dw 0x2080, 0x0000;26*4, tss_data ; bits 0-15 limit (4GB), bits 0-15 base address
db 0x2 ; bits 16-23 base address
db 0x89 ; access byte
db 00000000b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_end:

275
fault.nasm Normal file
View File

@ -0,0 +1,275 @@
extern error_screen ; 80x50 words
; Switches to text mode and shit
extern error_environment
_fault_coda:
xchg bx,bx
mov ax, 0x10
mov es, ax
; move to 'safe' location
mov ebp, 0x318000
; copy stack a bit
mov ecx, 0
.copy:
mov eax, [esp+ecx*4]
mov [ebp+ecx*4], eax
inc ecx
cmp ecx, 6
jl .copy
mov esp, ebp
call error_environment
.hlt:
hlt
jmp .hlt
global _gpf_eax_save
_gpf_eax_save: dd 0
global _gpf_eflags_save
_gpf_eflags_save: dd 0
extern gpf_handler_v86
global gpfHandler
gpfHandler:
cli ; make sure we're in a 'friendly' env
push eax
push ebx
push ecx
; save old ds
mov bx, ds
mov ax, 0x10
mov ds, ax
mov word [_gpf_old_ds], bx
; relocate stack so other interrupts don't fuck us over
; not sure if this is necessary, it doesn't seem to fix our race conditions...
mov ebx, esp
sub esp, 0x1000
xor ecx, ecx
.l:
mov eax, [ebx]
mov [esp+ecx], eax
add ebx, 4
add ecx, 4
cmp ebx, 0x320000 ; tss esp0
jl .l
pop ecx
pop ebx
sti ; we shouldn't crash now?
mov eax, dword [esp+16] ; EFLAGS
mov dword [_gpf_eflags_save], eax ; save
and eax, 1 << 17 ; VM flag
test eax, eax
pop eax
mov dword [_gpf_eax_save], eax
jnz gpf_handler_v86
jmp gpf_handler_32
gpf_unhandled:
mov dword [error_screen+0x00], 0x0f000f00 | 'G' | 'P' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'F' | '!' << 16
xchg bx,bx
jmp _fault_coda
_gpf_old_ds: dw 0
extern get_key
extern get_scancode
extern task_ptr
extern _enter_v86_internal_no_task
extern return_prev_task
extern v86Interrupt
gpf_handler_32:
push eax
mov eax, dword [esp+8] ; EIP
movzx eax, word [eax]
cmp eax, 0x30CD ; int 0x30
je .int30
cmp eax, 0x21CD ; int 0x21
je .int21
jmp gpf_unhandled
.int21:
pop eax ; command
cmp al, 0x00 ; get key
jne .s1
call get_key
jmp .return_to_offender
.s1: cmp al, 0x01 ; get scancode
jne .s86
call get_scancode
jmp .return_to_offender
.s86: cmp al, 0x86 ; v86 interrupt call
je .v86int
cmp ax, 0x86D8 ; get v86 data pointer
jne .return_to_offender
mov eax, 0x30000 ; V86 Data
jmp .return_to_offender
.v86int:
add esp, 4
add dword [esp+0], 2
; add a new task
call _gpf_create_return_task
; now enter v86 mode
; get int & regs from return stack
mov eax, [esp+12] ; return esp
mov eax, [eax] ; interrupt
and eax, 0xff ; ensure 1 byte
shl eax, 8
or eax, 0x30CD00CD ; command
mov dword [v86Interrupt], eax
mov eax, [esp+12] ; return esp
mov eax, [eax+4] ; regs
push eax ; regs
mov eax, v86Interrupt
and eax, 0xffff
push eax ; ip
mov eax, v86Interrupt
shr eax, 4
and eax, 0xf000
push eax ; cs
push 0xFF00 ; sp
push 0x8000 ; ss
jmp _enter_v86_internal_no_task ; NOT a function call
.int30:
pop eax ; return value
jmp return_prev_task
.return_to_offender:
add dword [esp+4], 2
push eax
mov ax, word [_gpf_old_ds]
mov ds, ax
pop eax
add esp, 4 ; error code
iret
_gpf_create_return_task:
; handler stack stored in edx
mov edx, esp
mov esp, dword [task_ptr]
mov eax, [edx+20] ; ss
push eax
mov eax, [edx+16] ; esp
push eax
mov eax, [edx+12] ; eflags
push eax
mov eax, [edx+8] ; cs
push eax
mov eax, [edx+4] ; eip
push eax
mov ax, word [_gpf_old_ds] ; restore old ds
mov ds, ax
push ds
push es
push fs
push gs
push ebp
push ebx
push esi
push edi
mov ax, 0x10
mov ds, ax
mov dword [task_ptr], esp ; save new task pointer
mov esp, edx ; restore handler stack
ret
global unhandled_handler
unhandled_handler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | 'E' | 'R' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'R' | 'O' << 16
mov dword [error_screen+0x08], 0x0f000f00 | 'R' | '!' << 16
jmp _fault_coda
global pageFaultHandler
pageFaultHandler:
mov ax, 0x10
mov ds, ax
pop eax ; error code
mov ebx, 0x0f000f00 | '0' | '!' << 16
and eax, 0x7 ; U/S,R/W,P
add ebx, eax
mov dword [error_screen+0x00], 0x0f000f00 | 'P' | 'G' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'F' | 'L' << 16
mov dword [error_screen+0x08], 0x0f000f00 | 'T' | ':' << 16
mov dword [error_screen+0x0C], ebx
jmp _fault_coda
global divisionErrorHandler
divisionErrorHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'D' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'E' | '!' << 16
jmp _fault_coda
global boundRangeHandler
boundRangeHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'B' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'R' | '!' << 16
jmp _fault_coda
global invalidOpcodeHandler
invalidOpcodeHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'U' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'D' | '!' << 16
jmp _fault_coda
global deviceNotAvailableHandler
deviceNotAvailableHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'N' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'D' | '!' << 16
jmp _fault_coda
global doubleFaultHandler
doubleFaultHandler:
mov ax, 0x10
mov ds, ax
mov dword [0xb8000+0x00], 0x0f000f00 | '#' | 'D' << 16
mov dword [0xb8000+0x04], 0x0f000f00 | 'F' | '!' << 16
; double faults simply abort right then
.hlt: hlt
jmp .hlt
global invalidTSSHandler
invalidTSSHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'T' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'S' | '!' << 16
jmp _fault_coda
global segmentNotPresentHandler
segmentNotPresentHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'N' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'P' | '!' << 16
jmp _fault_coda
global stackSegmentHandler
stackSegmentHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'S' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'S' | '!' << 16
jmp _fault_coda
global x87FloatingHandler
x87FloatingHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'M' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'F' | '!' << 16
jmp _fault_coda
global alignmentCheckHandler
alignmentCheckHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'A' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'C' | '!' << 16
jmp _fault_coda
global controlProtectionHandler
controlProtectionHandler:
mov ax, 0x10
mov ds, ax
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'C' << 16
mov dword [error_screen+0x04], 0x0f000f00 | 'P' | '!' << 16
jmp _fault_coda

113
file.c Normal file
View File

@ -0,0 +1,113 @@
#include "file.h"
#include "fs.h"
// TODO Replace with something better
extern uint8_t ActiveFsId;
// Sets adjusted_path to the path without filesystem info,
// returns the filesystem calculated
uint8_t GetFsFromPath(char *path, char **adjusted_path) {
// On active filsystem
if (*path == '/') {
*adjusted_path = path;
return GetActiveFilesystemId();
}
// Read the filesystem ID
uint8_t id = 0;
char *tmp_path = path;
// Find first /
for (;*tmp_path != '/';tmp_path++);
*adjusted_path = tmp_path;
// Read octal num
tmp_path--;
for (uint8_t bit_off = 0; tmp_path >= path; tmp_path--, bit_off+=3) {
// Outside of octal range, error
if (*tmp_path < '0' || *tmp_path > '8') return 0;
id += (*tmp_path - '0') << bit_off;
}
// Return filesystem
return id;
}
// Returns 0 on success, non-zero error code on error. Fills provided struct FILE
int file_open(FILE *file, char *path, char mode) {
char *adj_path;
uint8_t fsid = GetFsFromPath(path, &adj_path);
filesystem *fs = GetFilesystem(fsid);
int err = fs->ops.file_open(fs->fs_data, file, adj_path, mode);
file->filesystem_id = fsid;
return err;
}
// Returns 0 on success, non-zero error code on error.
int file_seek(FILE *file, uint32_t offset) {
filesystem *fs = GetFilesystem(file->filesystem_id);
return fs->ops.file_seek(fs->fs_data, file, offset);
}
// Returns 0 on error, bytes read on success.
int file_read(FILE *file, uint8_t *dest, uint32_t len) {
filesystem *fs = GetFilesystem(file->filesystem_id);
return fs->ops.file_read(fs->fs_data, file, dest, len);
}
// Returns 0 on error, bytes written on success.
int file_write(FILE *file, uint8_t *src, uint32_t len) {
filesystem *fs = GetFilesystem(file->filesystem_id);
return fs->ops.file_write(fs->fs_data, file, src, len);
}
void file_close(FILE *file) {
filesystem *fs = GetFilesystem(file->filesystem_id);
return fs->ops.file_close(fs->fs_data, file);
}
// Returns 0 on success, non-zero error code on error. Fills provided struct DIR
int dir_open(DIR *dir, char *path) {
char *adj_path;
uint8_t fsid = GetFsFromPath(path, &adj_path);
filesystem *fs = GetFilesystem(fsid);
int err = fs->ops.dir_open(fs->fs_data, dir, adj_path);
dir->filesystem_id = fsid;
return err;
}
// Return 0 on success, non-zero error code on error. Fills provided struct dirent.
int dir_nextentry(DIR *dir, dirent *ent) {
filesystem *fs = GetFilesystem(dir->filesystem_id);
return fs->ops.dir_nextentry(fs->fs_data, dir, ent);
}
void dir_close(DIR *dir) {
filesystem *fs = GetFilesystem(dir->filesystem_id);
return fs->ops.dir_close(fs->fs_data, dir);
}
// Returns 0 on success, non-zero error code on error. Fills provided struct dirent.
int path_getinfo(char *path, dirent *ent) {
char *adj_path;
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
return fs->ops.path_getinfo(fs->fs_data, adj_path, ent);
}
// Returns 0 on success, non-zero error code on error.
int path_mkdir(char *path) {
char *adj_path;
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
return fs->ops.path_mkdir(fs->fs_data, adj_path);
}
// Returns 0 on success, non-zero error code on error.
int path_rmdir(char *path) {
char *adj_path;
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
return fs->ops.path_rmdir(fs->fs_data, adj_path);
}
// Returns 0 on success, non-zero error code on error.
int path_rmfile(char *path) {
char *adj_path;
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
return fs->ops.path_rmfile(fs->fs_data, adj_path);
}

36
file.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include "file_s.h"
// Returns 0 on success, non-zero error code on error. Fills provided struct FILE
int file_open(FILE *file, char *path, char mode);
// Returns 0 on success, non-zero error code on error.
int file_seek(FILE *file, uint32_t offset);
// Returns 0 on error, bytes read on success.
int file_read(FILE *file, uint8_t *dest, uint32_t len);
// Returns 0 on error, bytes written on success.
int file_write(FILE *file, uint8_t *src, uint32_t len);
void file_close(FILE *file);
// Returns 0 on success, non-zero error code on error. Fills provided struct DIR
int dir_open(DIR *dir, char *path);
// Return 0 on success, non-zero error code on error. Fills provided struct dirent.
int dir_nextentry(DIR *dir, dirent *ent);
void dir_close(DIR *dir);
// Returns 0 on success, non-zero error code on error. Fills provided struct dirent.
int path_getinfo(char *path, dirent *ent);
// Returns 0 on success, non-zero error code on error.
int path_mkdir(char *path);
// Returns 0 on success, non-zero error code on error.
int path_rmdir(char *path);
// Returns 0 on success, non-zero error code on error.
int path_rmfile(char *path);

31
file_s.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <stdint.h>
typedef struct FILE {
uint8_t filesystem_id;
uint8_t bytes[0x3F];
} __attribute__((__packed__)) FILE;
typedef struct DIR {
uint8_t filesystem_id;
uint8_t bytes[0x3F];
} __attribute__((__packed__)) DIR;
typedef enum filetype {
FT_UNKNOWN,
FT_REG,
FT_DIR
} filetype;
typedef struct dirent {
filetype type;
uint32_t size;
uint32_t last_modified;
uint32_t last_accessed;
uint32_t created;
uint8_t namelen;
char name[255];
} dirent;
#define OPENREAD 1
#define OPENWRITE 2

130
fs.c Normal file
View File

@ -0,0 +1,130 @@
#include "fs.h"
#include "disk.h"
struct FsType {
uint32_t type_id;
int (*init_func)(filesystem *, uint32_t);
// Not yet decided
char (*detect_func)(uint32_t);
};
// TODO Get these dynamically somehow
int InitDosFs(filesystem *fs, uint32_t start_sector);
char DetectDosPart(uint32_t start_sector);
struct FsType SupportedFilesystems[] = {
{
.type_id = 0xD05,
.init_func = InitDosFs,
.detect_func = DetectDosPart
}
};
#define MAXFS 255
filesystem *ActiveFilesystems = (filesystem *)0x240000;
uint8_t ActiveFsIdx;
filesystem *GetFilesystem(uint8_t idx) {
if (idx >= MAXFS) return 0;
filesystem *fs = &ActiveFilesystems[idx];
return fs->type != 0 ? fs : 0;
}
filesystem *GetActiveFilesystem() {
return &ActiveFilesystems[ActiveFsIdx];
}
uint8_t GetActiveFilesystemId() {
return ActiveFsIdx;
}
filesystem *SetActiveFilesystem(uint8_t idx) {
if (idx >= MAXFS) return 0;
filesystem *fs = &ActiveFilesystems[idx];
if (fs->type == 0) return 0;
ActiveFsIdx = idx;
return fs;
}
void ActiveFilesystemBitmap(char *bitmap) {
for (int i = 0; i < 256; i++)
bitmap[i] = ActiveFilesystems[i].type != 0;
}
struct PartTableEntry_t {
uint8_t attr;
uint8_t start_chs0;
uint8_t start_chs1;
uint8_t start_chs2;
uint8_t type;
uint8_t end_chs0;
uint8_t end_chs1;
uint8_t end_chs2;
uint32_t start_lba;
uint32_t num_sectors;
};
typedef struct MBR_t {
char boot_code[0x1B8];
uint32_t signature;
uint16_t reserved;
struct PartTableEntry_t partTable[4];
uint16_t boot_sig;
} __attribute__((__packed__)) MBR_t;
// TODO Just check for this
uint8_t BootPartition;
uint8_t NextAvailableFilesystem;
MBR_t SysMbr;
void MakeMBRPartitions() {
// Get MBR
MBR_t *mbr = &SysMbr;
Disk_ReadSector(0, (uint8_t*)mbr, 0, 1);
// Scan partitions
for (int p = 0; p < 4; p++) {
if (p == BootPartition) continue;
if (mbr->partTable[p].type == 0) continue;
uint32_t partStart = mbr->partTable[p].start_lba;
if (partStart == 0) continue;
// Scan supported filesystems
filesystem *sys = &ActiveFilesystems[NextAvailableFilesystem];
for (int i = 0; i < sizeof(SupportedFilesystems)/sizeof(struct FsType); i++) {
if (!SupportedFilesystems[i].detect_func(partStart)) continue;
SupportedFilesystems[i].init_func(sys, partStart);
sys->type = SupportedFilesystems[i].type_id;
ActiveFsIdx = NextAvailableFilesystem;
NextAvailableFilesystem++;
break;
}
}
}
int MakeSystemVolume(uint8_t bootPartition) {
// Clear out filesystem area
for (int i = 0; i < (sizeof(filesystem)*MAXFS)/sizeof(uint32_t);i++)
((uint32_t*)ActiveFilesystems)[i] = 0;
BootPartition = bootPartition;
NextAvailableFilesystem = 1;
// Get MBR
MBR_t *mbr = &SysMbr;
Disk_ReadSector(0, (uint8_t*)mbr, 0, 1);
// Get boot partition sector
uint32_t sys_sector = mbr->partTable[bootPartition].start_lba;
// Scan supported filesystems
filesystem *sys = &ActiveFilesystems[0];
for (int i = 0; i < sizeof(SupportedFilesystems)/sizeof(struct FsType); i++) {
asm volatile("xchg %bx,%bx");
if (!SupportedFilesystems[i].detect_func(sys_sector)) continue;
SupportedFilesystems[i].init_func(sys, sys_sector);
sys->type = SupportedFilesystems[i].type_id;
ActiveFsIdx = 0;
return 0;
}
// Init Failed
return -1;
}

32
fs.h Normal file
View File

@ -0,0 +1,32 @@
#include <stdint.h>
#include "file_s.h"
typedef struct filesystem {
uint32_t resv0;
uint32_t type;
struct fs_operations {
int (*file_open)(uint8_t *, FILE *, char *, char);
int (*file_seek)(uint8_t *, FILE *, uint32_t);
int (*file_read)(uint8_t *, FILE *, uint8_t *, uint32_t);
int (*file_write)(uint8_t *, FILE *, uint8_t *, uint32_t);
void (*file_close)(uint8_t *, FILE *);
int (*dir_open)(uint8_t *, DIR *, char *);
int (*dir_nextentry)(uint8_t *, DIR *, dirent *);
void (*dir_close)(uint8_t *, DIR *);
int (*path_getinfo)(uint8_t *, char *, dirent *);
int (*path_mkdir)(uint8_t *, char *);
int (*path_rmdir)(uint8_t *, char *);
int (*path_rmfile)(uint8_t *, char *);
void (*endfs)(uint8_t *);
} ops;
uint8_t labellen;
char label[255];
uint8_t fs_data[2048-4-4-44-256];
} __attribute__((packed)) filesystem;
filesystem *GetFilesystem(uint8_t idx);
filesystem *GetActiveFilesystem();
uint8_t GetActiveFilesystemId();
filesystem *SetActiveFilesystem(uint8_t idx);
void ActiveFilesystemBitmap(char *bitmap);

239
fs_dos.c Normal file
View File

@ -0,0 +1,239 @@
#include "disk.h"
#include "fs.h"
#include "dosfs/dosfs.h"
char *strncpy(char *restrict d, const char *restrict s, uintptr_t n);
int strcmp(const char *l, const char *r);
void *memcpy(void *restrict dest, const void *restrict src, uintptr_t n);
// Implementations for DOSFS
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
return Disk_ReadSector(unit, buffer, sector, count);
}
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
return Disk_WriteSector(unit, buffer, sector, count);
}
// System Implementations
typedef struct fsdat {
VOLINFO vi;
} fsdat;
int file83ToPath(uint8_t *src, char *path) {
uint8_t tmp, trailingSpace;
for (trailingSpace=0, tmp = 0; tmp < 8 && src[tmp]; tmp++) {
path[tmp] = src[tmp];
if (src[tmp] == ' ') trailingSpace++;
else trailingSpace = 0;
}
tmp -= trailingSpace;
path[tmp++] = '.';
trailingSpace = 0;
for (int i = 8; i < 11 && src[i]; i++, tmp++) {
path[tmp] = src[i];
if (src[i] == ' ') trailingSpace++;
else trailingSpace = 0;
}
tmp -= trailingSpace;
if (trailingSpace == 3) tmp--;
path[tmp] = 0;
return tmp;
}
uintptr_t stripToDir(char *path) {
int i = 0;
// find end of string
for (;path[i];i++);
// find last /
for (;path[i] != '/' && i >= 0;i--);
// path[i] == '/'
// set next to end, return split location
path[i+1] = 0;
return i + 1;
}
int dos_file_open(uint8_t *dat, FILE *f, char *path, char mode) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
uint8_t dfs_mode =
(mode & OPENREAD ? DFS_READ : 0) |
(mode & OPENWRITE ? DFS_WRITE : 0);
return DFS_OpenFile(&fs->vi, (uint8_t *)path, dfs_mode, scratch, (FILEINFO *)f->bytes);
}
int dos_file_seek(uint8_t *dat, FILE *f, uint32_t offset) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
DFS_Seek((FILEINFO *)f->bytes, offset, scratch);
if (((FILEINFO *)f->bytes)->pointer != offset) return -1;
return 0;
}
int dos_file_read(uint8_t *dat, FILE *f, uint8_t *dest, uint32_t len) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
uint32_t successcount;
uint32_t err = DFS_ReadFile((FILEINFO *)f->bytes, scratch, dest, &successcount, len);
// Error
if (err != 0 && err != DFS_EOF)
return 0;
// Success or EOF
return successcount;
}
int dos_file_write(uint8_t *dat, FILE *f, uint8_t *src, uint32_t len) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
uint32_t successcount;
uint32_t err = DFS_WriteFile((FILEINFO *)f->bytes, scratch, src, &successcount, len);
// Error
if (err != 0) return 0;
// Success
return successcount;
}
// DOSFS doesn't have anything to clean up
void dos_file_close(uint8_t *dat, FILE *f) { return; }
int dos_dir_open(uint8_t *dat, DIR *d, char *path) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
((DIRINFO *)d->bytes)->scratch = scratch;
return DFS_OpenDir(&fs->vi, (uint8_t *)path, (DIRINFO *)d->bytes);
}
int dos_dir_nextentry(uint8_t *dat, DIR *d, dirent *ent) {
fsdat *fs = (fsdat *)dat;
DIRENT de;
for (;;) {
uint32_t code = DFS_GetNext(&fs->vi, (DIRINFO *)d->bytes, &de);
if (code == DFS_EOF) return 1;
if (code != DFS_OK) return -1;
// Deleted file, continue to next entry
if (de.name[0] == 0) continue;
break;
}
// Copy info
ent->type = de.attr & ATTR_DIRECTORY ? FT_DIR : FT_REG;
ent->size = (uint32_t)de.filesize_0 +
((uint32_t)de.filesize_1 << 8) +
((uint32_t)de.filesize_2 << 16) +
((uint32_t)de.filesize_3 << 24);
// Haven't decided format on these yet
ent->last_modified = 0;
ent->last_accessed = 0;
ent->created = 0;
ent->namelen = file83ToPath(de.name, ent->name);
return 0;
}
// DOSFS doesn't have anything to clean up
void dos_dir_close(uint8_t *dat, DIR *d) { return; }
// TODO Make this less expensive -> Use DOSFS directly?
int dos_path_getinfo(uint8_t *dat, char *path, dirent *d) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
// Get directory path is in
uint8_t tmppath[MAX_PATH];
strncpy((char*)tmppath,path,MAX_PATH);
tmppath[MAX_PATH-1]=0;
uintptr_t nameidx = stripToDir((char*)tmppath);
char *name = &path[nameidx];
// Open directory
DIR dir;
dos_dir_open(dat, &dir, (char*)tmppath);
dirent de;
// Enumerate info
for (;dos_dir_nextentry(dat, &dir, &de) == 0;) {
// Check if correct entry
if (strcmp(de.name, name) == 0) {
// Copy to caller dirent
for (int i = 0; i < sizeof(dirent); i++)
((uint8_t*)d)[i] = ((uint8_t*)&de)[i];
return 0;
}
}
// Did not find or error
return -1;
}
// TODO Unimplemented
int dos_path_mkdir(uint8_t *dat, char *path) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
return -1;
}
// TODO Unimplemented
int dos_path_rmdir(uint8_t *dat, char *path) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
return -1;
}
// TODO Unimplemented
int dos_path_rmfile(uint8_t *dat, char *path) {
fsdat *fs = (fsdat *)dat;
uint8_t *scratch = (uint8_t *)0x20000;
return -1;
}
// DOSFS doesn't have anything to clean up
void dos_endfs(uint8_t *dat) { return; }
// Try to detect if partition is a valid DOS partition
char DetectDosPart(uint32_t start_sector) {
// Read sector
//uint8_t *scratch = (uint8_t *)0x20000;
//Disk_ReadSector(0, scratch, start_sector, 1);
//// Check for start bytes EBXX90
//if (((*(uint32_t*)&scratch[0]) & 0x00FF00FF) != 0x9000EB) return 0;
//// Check for bytes per sector == 512 (We don't support other values anyway)
//if (*(uint16_t*)&scratch[0xB] != 512) return 0;
// TODO Check more, so we *know* it's FAT
// We're probably FAT
return 1;
}
int InitDosFs(filesystem *fs, uint32_t start_sector) {
uint8_t *diskReadBuf = (uint8_t *)0x20000;
VOLINFO *vi = (VOLINFO *)fs->fs_data;
if (DFS_GetVolInfo(0, diskReadBuf, start_sector, (VOLINFO *)fs->fs_data)) {
return -1;
}
int i;
for (i = 0; vi->label[i] && i < sizeof(vi->label); i++)
fs->label[i] = vi->label[i];
fs->labellen = i;
fs->ops.file_open = dos_file_open;
fs->ops.file_seek = dos_file_seek;
fs->ops.file_read = dos_file_read;
fs->ops.file_write = dos_file_write;
fs->ops.file_close = dos_file_close;
fs->ops.dir_open = dos_dir_open;
fs->ops.dir_nextentry = dos_dir_nextentry;
fs->ops.dir_close = dos_dir_close;
fs->ops.path_getinfo = dos_path_getinfo;
fs->ops.path_mkdir = dos_path_mkdir;
fs->ops.path_rmdir = dos_path_rmdir;
fs->ops.path_rmfile = dos_path_rmfile;
fs->ops.endfs = dos_endfs;
return 0;
}

35
gdt.nasm Normal file
View File

@ -0,0 +1,35 @@
global ivt
ivt: dd 0x00000000
global gdt_desc
gdt_desc:
dw gdt_end - gdt
dd gdt
gdt:
gdt_null: dq 0
gdt_code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 10011010b ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 10010010b ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_r3code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 0xFA ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_r3data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
db 0 ; bits 16-23 base address
db 0xF2 ; access byte
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_tss: dw 0x2080, 0x0000;26*4, tss_data ; bits 0-15 limit (4GB), bits 0-15 base address
db 0x20 ; bits 16-23 base address
db 0x89 ; access byte
db 00000000b ; bits 16-19 limit (4GB), 4 bits flags
db 0 ; bits 24-31 base address
gdt_end:

View File

@ -1,48 +1,4 @@
extern unhandled_handler oldscancodesToAscii: db 0, 0 ; 0x00 - 0x01
unhandled_handler:
mov ax, 0x10
mov ds, ax
mov dword [0xb8000], 0x0f000f00 | 'E' | 'R' << 16
mov dword [0xb8004], 0x0f000f00 | 'R' | 'O' << 16
mov dword [0xb8008], 0x0f000f00 | 'R' | '!' << 16
.hlt:
hlt
jmp .hlt
extern gpf_handler_v86
global gpfHandler
gpfHandler:
push eax
mov ax, 0x10
mov ds, ax
mov eax, dword [esp+16] ; EFLAGS
and eax, 1 << 17 ; VM flag
test eax, eax
pop eax
jnz gpf_handler_v86
jmp gpf_handler_32
gpf_unhandled:
mov dword [0xb8000], 0x0f000f00 | 'G' | 'P' << 16
mov dword [0xb8004], 0x0f000f00 | 'F' | '!' << 16
.hlt:
hlt
jmp .hlt
gpf_handler_32:
push eax
mov eax, dword [esp+8] ; EIP
movzx eax, word [eax]
cmp eax, 0x30CD ; int 0x30
jne gpf_unhandled
pop eax ; return value
mov esp, dword [0x20004] ; return info
pop gs
pop fs
pop es
pop ds
iret ; return to original caller
scancodesToAscii: db 0, 0 ; 0x00 - 0x01
db "1234567890" ; 0x02 - 0x0B db "1234567890" ; 0x02 - 0x0B
db "-=" ; 0x0C - 0x0D db "-=" ; 0x0C - 0x0D
db 0, 0 ; 0x0E - 0x0F db 0, 0 ; 0x0E - 0x0F
@ -58,8 +14,8 @@ db ' ' ; 0x39
db 'C' db 'C'
scancodesToAsciiEnd: scancodesToAsciiEnd:
cursorCurrent: dd 0xb8000 + (80*6*2) cursorCurrent: dd 0xb8000 + (80*6*2)
global keyboardHandler global oldkeyboardHandler
keyboardHandler: oldkeyboardHandler:
push eax push eax
push ebx push ebx
push ds push ds
@ -69,7 +25,7 @@ xor eax, eax
in al, 0x60 in al, 0x60
cmp eax, 0x3A cmp eax, 0x3A
jg .done jg .done
mov al, [scancodesToAscii+eax] mov al, [oldscancodesToAscii+eax]
test al, al test al, al
jz .done jz .done
mov ebx, [cursorCurrent] mov ebx, [cursorCurrent]
@ -85,8 +41,7 @@ pop eax
iret iret
KBDWAIT: db 0 KBDWAIT: db 0
global kbd_wait oldkbd_wait:
kbd_wait:
mov byte [KBDWAIT], 0 mov byte [KBDWAIT], 0
.loop: .loop:
hlt hlt
@ -95,13 +50,16 @@ test eax, eax
jz .loop jz .loop
ret ret
global TIMERVAL
TIMERVAL: dd 0
global timerHandler global timerHandler
timerHandler: timerHandler:
push eax push eax
push ds push ds
mov ax, 0x10 mov ax, 0x10
mov ds, ax mov ds, ax
inc byte [(0xb8000 + (80*8*2))] ;inc byte [(0xb8000 + (80*8*2))]
inc dword [TIMERVAL]
mov al, 0x20 mov al, 0x20
out 0x20, al out 0x20, al
pop ds pop ds

53
helper.c Normal file
View File

@ -0,0 +1,53 @@
#include "helper.h"
uint16_t *nextLine(uint16_t *p, uint16_t *b) {
uintptr_t v = (uintptr_t)p;
return (uint16_t *)(v + (160 - ((v - (uintptr_t)b) % 160)));
}
void trimPath(char *path, char *buff, uint32_t maxLen) {
int pathLen = 0;
for (;path[pathLen];pathLen++);
pathLen++;
if (pathLen < maxLen) {
for(int i = 0; i < pathLen; i++)
buff[i] = path[i];
return;
}
for (int i = 0; i < 3; i++)
buff[i] = '.';
for (int i = 3; i < maxLen; i++) {
buff[i] = path[pathLen-maxLen+i];
}
}
void V8086Int(uint8_t interrupt, union V86Regs_t *regs) {
// Edit the v8086 code with the interrupt
// Writing 4 bytes to ensure proper code
*(uint32_t*)v86Interrupt = 0x30CD00CD | (interrupt << 8);
FARPTR v86_entry = i386LinearToFp(v86Interrupt);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), regs);
}
void SetVideo25Lines() {
union V86Regs_t regs;
regs.w.ax = 0x1114; // 80x25 mode
regs.w.bx = 0x0000;
V8086Int(0x10, &regs);
}
void SetVideo50Lines() {
union V86Regs_t regs;
regs.w.ax = 0x1112; // 80x50 mode
regs.w.bx = 0x0000;
V8086Int(0x10, &regs);
}
void SetCursorDisabled() {
union V86Regs_t regs;
regs.w.ax = 0x0100; // set cursor
regs.w.cx = 0x3F00; // disabled
V8086Int(0x10, &regs);
}
void GetFileList(DIR *dir, dirent *entries, int32_t *entCount, int32_t maxEntries) {
for ((*entCount) = 0; *entCount < maxEntries && !dir_nextentry(dir, &entries[*entCount]); (*entCount)++);
}

17
helper.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
#include "interrupt.h"
#include "v86defs.h"
#include "file.h"
void V8086Int(uint8_t interrupt, union V86Regs_t *regs);
void SetVideo25Lines();
void SetVideo50Lines();
void SetCursorDisabled();
uint16_t *nextLine(uint16_t *p, uint16_t *b);
void trimPath(char *path, char *buff, uint32_t maxLen);
void GetFileList(DIR *dir, dirent *entries, int32_t *entCount, int32_t maxEntries);

454
hexedit.c Normal file
View File

@ -0,0 +1,454 @@
#include "file.h"
#include "progs.h"
#define BLOCKSIZE 0x10000 // 64K
#define BLOCKMASK ~0xffff // block = (offset & blockMask)
#define BLOCKSHIFT 16 // blockSize = 1 << blockShift
#define MAXFILESIZE 0x80000000 // 2GB
#define TOTALBLOCKS (MAXFILESIZE/BLOCKSIZE)
uint16_t writtenMap[TOTALBLOCKS] __attribute__((section(".hexbss")));;
uint32_t blockLenMap[TOTALBLOCKS] __attribute__((section(".hexbss")));;
// NOTE This is linked at the end of program BSS section,
// so that it can be expanded without telling C how much
// it actually needs
uint8_t writeStoreBase[BLOCKSIZE] __attribute__((section(".hexlatebss")));
void HexEditor(char *path) {
uint32_t err;
uint16_t *vga_text = (uint16_t *)0xb8000;
uint32_t screenSize = 80*25;
uint8_t (*writeStore)[BLOCKSIZE] = &writeStoreBase;
for (int i = 0; i < TOTALBLOCKS; i++)
writtenMap[i] = 0;
uint8_t *screenBuff = *writeStore;
// First two blocks are screen buffer
uint32_t nextFreeBlock = 2;
vga_text = (uint16_t *)0xb8000;
for (int i = 0; i < 80*50; i++)
vga_text[i] = 0x0f00;
dirent de;
err = path_getinfo(path, &de);
if (err) {
vga_text += printStr("Error getting file info.", vga_text);
kbd_wait();
return;
}
uint32_t filelen = de.size;
if (filelen == 0) {
vga_text += printStr("File ", vga_text);
vga_text += printStr((char*)path, vga_text);
vga_text += printStr(" has no data.", vga_text);
kbd_wait();
return;
}
if (filelen > MAXFILESIZE) {
vga_text += printStr("File ", vga_text);
vga_text += printStr((char*)path, vga_text);
vga_text += printStr(" is too large (> 2GB).", vga_text);
kbd_wait();
return;
}
FILE file;
err = file_open(&file, path, OPENREAD|OPENWRITE);
if (err) {
vga_text += printStr("Open Error: ", vga_text);
printDword(err, vga_text);
kbd_wait();
return;
}
uint32_t drawOffset = 0, lastDrawOffset = -1;
char cont = 1;
uint32_t byteCount = 16*24, lastByteCount = 0;
// Cursor offset from *drawOff*
int32_t cursorScreenOff = 0, lastCursorScreenOff = -1;
// Pointer to last cursor in *VGA mem*
uint8_t *lastCursorScreenPtr = (uint8_t*)0xb8000;
char cursorNibble = 1, lastCursorNibble = 0;
char cursorRedraw = 1;
char reread = 1, redraw = 1;
uint8_t *currBuff = 0, *nextBuff = 0;
uint32_t currBuffLength, nextBuffLength, totalBuffLength;
uint16_t currLoadedBlock = -1, nextLoadedBlock = -1;
char currInMap = 0, nextInMap = 0;
uint32_t changeOffset = -1, lastChangeOffset = -1;
char fileChanged = 0;
for (;cont;) {
// Check if we need to scroll screen for cursor
// Should never be more than 16 away, if it is,
// things will be caught by sanity checks.
// Scroll Back
if (cursorScreenOff < 0) {
if (drawOffset - 16 < filelen)
drawOffset -= 16;
cursorScreenOff += 16;
}
// Scroll Forward
if (cursorScreenOff >= byteCount) {
if (drawOffset + 16 < filelen)
drawOffset += 16;
cursorScreenOff -= 16;
}
// Sanity checks
if (cursorScreenOff >= byteCount)
cursorScreenOff = byteCount - 1;
if (cursorScreenOff + drawOffset >= filelen)
cursorScreenOff = filelen - drawOffset - 1;
if (cursorScreenOff < 0) cursorScreenOff = 0;
if (cursorNibble != lastCursorNibble)
cursorRedraw = 1;
if (cursorScreenOff != lastCursorScreenOff)
cursorRedraw = 1;
if (cursorRedraw) {
const uint32_t hexViewScreenOff = 10 * 2;
const uint32_t asciiViewScreenOff = 61 * 2;
uint16_t *screen = (uint16_t*)0xb8000;
// Byte draw starts on first line
uint16_t *line = &screen[80 * (1 + (cursorScreenOff >> 4))];
uint8_t *cursorPtr = (uint8_t*)line;
if (cursorNibble == 0) {
// Each byte takes 3 chars on screen
cursorPtr += hexViewScreenOff + (cursorScreenOff & 0xF) * 3 * 2;
if ((cursorScreenOff & 0xF) > 7)
cursorPtr += 2;
// Low nibble
cursorPtr += 2;
} else if (cursorNibble == 1) {
// Each byte takes 3 chars on screen
cursorPtr += hexViewScreenOff + (cursorScreenOff & 0xF) * 3 * 2;
if ((cursorScreenOff & 0xF) > 7)
cursorPtr += 2;
// ASCII area
} else {
// Each byte takes 1 char on screen
cursorPtr += asciiViewScreenOff + (cursorScreenOff & 0xF) * 2;
}
// We want the color byte, not char
cursorPtr++;
if (cursorPtr != lastCursorScreenPtr) {
*lastCursorScreenPtr = 0x0f;
*cursorPtr = 0xf0;
lastCursorScreenPtr = cursorPtr;
}
lastCursorScreenOff = cursorScreenOff;
lastCursorNibble = cursorNibble;
cursorRedraw = 0;
}
if ((drawOffset & BLOCKMASK) != (lastDrawOffset & BLOCKMASK)) {
lastDrawOffset = (drawOffset & BLOCKMASK);
reread = 1;
redraw = 1;
}
if (drawOffset != lastDrawOffset) {
lastDrawOffset = drawOffset;
redraw = 1;
}
if (byteCount != lastByteCount) {
lastByteCount = byteCount;
redraw = 1;
}
if (changeOffset != lastChangeOffset) {
redraw = 1;
lastChangeOffset = changeOffset;
}
// Check for changes to block
if (changeOffset != -1) {
char isCurr = changeOffset < BLOCKSIZE;
char changeInMap = isCurr ? currInMap : nextInMap;
uint8_t *changedBlock = isCurr ? currBuff : nextBuff;
// If changes are in map we don't need to do anything
if (!changeInMap) {
uint16_t loadedBlock = isCurr ? currLoadedBlock : nextLoadedBlock;
uint16_t storeBlockIdx = writtenMap[loadedBlock];
// Add a new map entry
if (!storeBlockIdx) {
writtenMap[loadedBlock] = storeBlockIdx = nextFreeBlock++;
blockLenMap[loadedBlock] = isCurr ? currBuffLength : nextBuffLength;
}
uint8_t *storeBlock = writeStore[storeBlockIdx];
for (int i = 0; i < BLOCKSIZE; i++)
storeBlock[i] = changedBlock[i];
// Move the current buffer to map
if (isCurr) { currBuff = storeBlock; currInMap = 1; }
else { nextBuff = storeBlock; nextInMap = 1; }
}
changeOffset = -1;
fileChanged = 1;
redraw = 1;
}
if (reread) {
// TODO We should probably check if we already have
// the block to read in nextBuff from previous stuff
// We are in a modified block
uint16_t newBlock = drawOffset >> BLOCKSHIFT;
if (writtenMap[newBlock]) {
currBuff = writeStore[writtenMap[newBlock]];
currBuffLength = blockLenMap[newBlock];
totalBuffLength = currBuffLength;
currInMap = 1;
// We are in an unmodified block
} else {
uint32_t blockOffset = drawOffset & BLOCKMASK;
vga_text = &((uint16_t*)0xb8000)[80];
if (file_seek(&file, blockOffset)) {
vga_text += printStr("Seek Error", vga_text);
kbd_wait();
return;
}
currBuffLength = file_read(&file, screenBuff, BLOCKSIZE);
if (!currBuffLength && blockOffset != filelen) {
vga_text += printStr("Read Error", vga_text);
kbd_wait();
return;
}
currBuff = screenBuff;
totalBuffLength = currBuffLength;
currInMap = 0;
}
currLoadedBlock = newBlock;
reread = 0;
nextInMap = 0;
}
// We will be drawing stuff in the next block
// TODO This would be better served with nice functions!
if (currBuffLength == BLOCKSIZE && (drawOffset & ~BLOCKMASK) > (BLOCKSIZE - byteCount)) {
// TODO We should probably check if we already have
// the next block in currBuff from previous stuff
// Next is a modified block
uint16_t newBlock = (drawOffset >> BLOCKSHIFT) + 1;
if (nextLoadedBlock != newBlock) {
if (writtenMap[newBlock]) {
nextBuffLength = blockLenMap[newBlock];
nextBuff = writeStore[writtenMap[newBlock]];
nextInMap = 1;
// Next is an unmodified block
} else {
uint32_t blockOffset = (drawOffset & BLOCKMASK) + BLOCKSIZE;
vga_text = &((uint16_t*)0xb8000)[80];
if (file_seek(&file, blockOffset)) {
vga_text += printStr("Seek Error", vga_text);
kbd_wait();
return;
}
nextBuffLength = file_read(&file, &screenBuff[BLOCKSIZE], BLOCKSIZE);
if (!nextBuffLength && blockOffset != filelen) {
vga_text += printStr("Read Error", vga_text);
kbd_wait();
return;
}
nextBuff = &screenBuff[BLOCKSIZE];
nextInMap = 0;
}
nextLoadedBlock = newBlock;
}
totalBuffLength = currBuffLength + nextBuffLength;
}
if (redraw) {
vga_text = (uint16_t *)0xb8000;
{
const char prnt[] = "Scroll: Up/Down PgUp/PgDown Home/End Exit: F1";
vga_text = &((uint16_t*)0xb8000)[0];
char pathBuff[22];
trimPath((char*)path, pathBuff, sizeof(pathBuff));
vga_text += printStr(pathBuff, vga_text);
vga_text += printChar(fileChanged ? '*' : ' ', vga_text);
for (;vga_text < &((uint16_t*)0xb8000)[80-sizeof(prnt)];)
vga_text += printChar(' ', vga_text);
vga_text += printStr((char*)prnt, vga_text);
}
vga_text = &((uint16_t*)0xb8000)[80];
uint32_t offsetInBuff = drawOffset & ~BLOCKMASK;
uint8_t *drawFromBuff = &currBuff[offsetInBuff];
uint32_t maxDrawLen = totalBuffLength - (drawOffset & ~BLOCKMASK);
for (int i = 0; i < maxDrawLen && i < byteCount; i += 16) {
// Blocks are BLOCKSIZE aligned, so 16-byte alignment should work
if (i + offsetInBuff >= BLOCKSIZE) {
drawFromBuff = &nextBuff[-i];
offsetInBuff = 0;
}
vga_text += printDword(i + drawOffset, vga_text);
vga_text += printChar(' ', vga_text);
vga_text += printChar(' ', vga_text);
for (uint32_t j = 0; j < 16; j++) {
if (i + j < maxDrawLen)
vga_text += printByte(drawFromBuff[i + j], vga_text);
else {
vga_text += printChar(' ', vga_text);
vga_text += printChar(' ', vga_text);
}
vga_text += printChar(' ', vga_text);
if (j == 7)
vga_text += printChar(' ', vga_text);
}
vga_text += printChar(' ', vga_text);
vga_text += printChar('|', vga_text);
for (uint32_t j = 0; j < 16; j++) {
if (i + j < maxDrawLen)
vga_text += printChar(drawFromBuff[i + j], vga_text);
else vga_text += printChar(' ', vga_text);
}
vga_text += printChar('|', vga_text);
vga_text = nextLine(vga_text,(uint16_t*)0xb8000);
}
// Clear remainder of screen
for (;vga_text < &((uint16_t*)0xb8000)[screenSize]; vga_text++)
*vga_text = 0x0f00;
redraw = 0;
}
uint16_t key = get_scancode();
union V86Regs_t regs;
FARPTR v86_entry;
char try_edit = 0;
switch (key & 0xff) {
case KEY_DOWN:
// Stay in file
if ((cursorScreenOff + 16 + drawOffset) < filelen)
cursorScreenOff += 16;
break;
case KEY_UP:
// Stay in file
if ((uint32_t)(cursorScreenOff - 16 + drawOffset) < filelen)
cursorScreenOff -= 16;
break;
case KEY_LEFT:
if (cursorNibble == 0) { cursorNibble = 1; break; }
// Exiting text area
else if (cursorNibble == 2 && (cursorScreenOff & 0xF) == 0) {
cursorNibble = 0;
cursorScreenOff |= 0xF;
// Stay in file
} else if ((cursorScreenOff - 1 + drawOffset) < filelen) {
cursorScreenOff--;
if (cursorNibble == 1) cursorNibble = 0;
}
break;
case KEY_RIGHT:
if (cursorNibble == 1) { cursorNibble = 0; break; }
// Entering text area
else if (cursorNibble == 0 && (cursorScreenOff & 0xF) == 0xF) {
cursorNibble = 2;
cursorScreenOff &= ~0xF;
// Stay in file
} else if ((cursorScreenOff + 1 + drawOffset) < filelen) {
cursorScreenOff++;
if (cursorNibble == 0) cursorNibble = 1;
}
break;
case KEY_PGDOWN:
if (drawOffset + byteCount < filelen)
drawOffset += byteCount;
else if ((filelen / byteCount) * byteCount > drawOffset)
drawOffset = (filelen / byteCount) * byteCount;
break;
case KEY_PGUP:
if (drawOffset - byteCount < filelen)
drawOffset -= byteCount;
else drawOffset = 0;
break;
case KEY_HOME:
cursorScreenOff &= ~0xF;
if (cursorNibble == 0) cursorNibble = 1;
break;
case KEY_END:
cursorScreenOff |= 0xF;
if (cursorNibble == 1) cursorNibble = 0;
break;
case KEY_F1:
cont = 0;
break;
case KEY_F3: // start of file
drawOffset = 0;
break;
case KEY_F4: // end of file
if ((filelen / byteCount) * byteCount > drawOffset)
drawOffset = (filelen / byteCount) * byteCount;
break;
case KEY_F6: // TODO write file
break;
case KEY_F2:
if (byteCount != 16*24) {
SetVideo25Lines();
SetCursorDisabled();
screenSize = 80*25;
byteCount = 16*24;
}
break;
case KEY_F5:
if (byteCount != 16*49) {
SetVideo50Lines();
SetCursorDisabled();
screenSize = 80*50;
byteCount = 16*49;
}
break;
default:
try_edit = 1;
break;
}
if (!try_edit) continue;
// Check if in range
char k = key >> 8; // ASCII portion
char v;
if (cursorNibble == 2 && k >= 0x20 && k <= 0x7E) v = k;
else if (k >= '0' && k <= '9')
v = k - '0';
else if (k >= 'a' && k <= 'f')
v = k - 'a' + 0xA;
else if (k >= 'A' && k <= 'F')
v = k - 'A' + 0xA;
else continue;
// Change buffer at cursor
uint32_t screenOffsetInBuff = drawOffset & ~BLOCKMASK;
changeOffset = screenOffsetInBuff + cursorScreenOff;
char isCurr = changeOffset < BLOCKSIZE;
uint8_t *changeBuff = isCurr ? currBuff : nextBuff;
uint32_t changeOffsetInBuff = isCurr ? changeOffset : changeOffset - BLOCKSIZE;
if (cursorNibble == 0)
changeBuff[changeOffsetInBuff] = (changeBuff[changeOffsetInBuff] & 0xF0) | v;
else if (cursorNibble == 1)
changeBuff[changeOffsetInBuff] = (changeBuff[changeOffsetInBuff] & 0x0F) | (v << 4);
else changeBuff[changeOffsetInBuff] = v;
redraw = 1;
}
if (!fileChanged) return;
vga_text = (uint16_t*)0xb8000;
{
char pathBuff[23];
trimPath((char*)path, pathBuff, sizeof(pathBuff));
vga_text += printStr(pathBuff, vga_text);
vga_text += printChar(fileChanged ? '*' : ' ', vga_text);
}
vga_text += printChar(' ', vga_text);
vga_text += printStr("Save changes to file? (Y/N)", vga_text);
for (;vga_text < &((uint16_t*)0xb8000)[80];vga_text++)
*vga_text = 0x0f00;
uint16_t key=0;
for(;(key & 0xff) != KEY_N && (key & 0xff) != KEY_Y;key = get_scancode());
if ((key & 0xff) != KEY_Y) return;
// Write changes
for (int i = 0; i < TOTALBLOCKS && (i << BLOCKSHIFT) < filelen; i++) {
// No change in current block
uint16_t blockIdx = writtenMap[i];
uint32_t blockLen = blockLenMap[i];
if (!blockIdx) continue;
// Write block to file
uint32_t blockOff = i << BLOCKSHIFT;
if (file_seek(&file, blockOff)) {
vga_text = (uint16_t*)0xb8000;
vga_text += printStr("Seek Error ", vga_text);
kbd_wait();
return;
}
uint32_t successcount = file_write(&file, writeStore[blockIdx], blockLen);
if (successcount < blockLen) {
vga_text = (uint16_t*)0xb8000;
vga_text += printStr("Write Error ", vga_text);
kbd_wait();
return;
}
}
}

View File

@ -2,6 +2,8 @@
#include <stddef.h> #include <stddef.h>
#include "interrupt.h" #include "interrupt.h"
#include "kbd.h"
#include "v86defs.h"
char int_nibbleToHex(uint8_t n) { char int_nibbleToHex(uint8_t n) {
return n > 9 ? (n - 10) + 'A' : n + '0'; return n > 9 ? (n - 10) + 'A' : n + '0';
@ -20,6 +22,13 @@ void int_printDword(uint32_t v, uint16_t *buff) {
int_printWord(v >> 16, buff); int_printWord(v >> 16, buff);
int_printWord(v, &buff[4]); int_printWord(v, &buff[4]);
} }
__attribute((__no_caller_saved_registers__))
uintptr_t int_printStr(char *v, uint16_t *buff) {
char *s;
for (s = v;*s;s++,buff++)
*(char*)buff = *s;
return s - v;
}
struct __attribute__((__packed__)) IDTR_t { struct __attribute__((__packed__)) IDTR_t {
uint16_t size; uint16_t size;
@ -51,7 +60,7 @@ FARPTR i386LinearToFp(void *ptr)
{ {
unsigned seg, off; unsigned seg, off;
off = (uintptr_t) ptr & 0xffff; off = (uintptr_t) ptr & 0xffff;
seg = ((uintptr_t) ptr >> 16); seg = ((uintptr_t) ptr >> 4) & 0xf000;
return MK_FP(seg, off); return MK_FP(seg, off);
} }
@ -82,11 +91,17 @@ void IRQ_clear_mask(char IRQline) {
} }
char v86_if = 0; char v86_if = 0;
extern uint16_t error_screen[80*50]; // defined in kernel.c
extern uint16_t *ivt; extern uint16_t *ivt;
extern void real_test(); extern void real_test();
__attribute((__no_caller_saved_registers__))
extern void kbd_wait();
extern void jmp_usermode_test(); extern void jmp_usermode_test();
__attribute__((__no_caller_saved_registers__))
__attribute__((__noreturn__))
extern void return_prev_task();
__attribute__((__no_caller_saved_registers__))
__attribute__((__noreturn__))
extern void error_environment(); // defined in kernel.c
extern uint32_t _gpf_eax_save;
#define VALID_FLAGS 0xDFF #define VALID_FLAGS 0xDFF
__attribute__ ((interrupt)) __attribute__ ((interrupt))
void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) { void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
@ -99,14 +114,14 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
stack = FP_TO_LINEAR(frame->ss, frame->esp); stack = FP_TO_LINEAR(frame->ss, frame->esp);
stack32 = (uint32_t*)stack; stack32 = (uint32_t*)stack;
char *vga = (char*)0xb8000 + (160 * 10); char *vga = (char*)error_screen + (160 * 10);
vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14;
vga = (char*)0xb8000 + (160 * 11); vga = (char*)error_screen + (160 * 11);
vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14;
vga = (char*)0xb8000 + (160 * 12); vga = (char*)error_screen + (160 * 12);
vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14;
@ -127,8 +142,8 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
// printByte(ip[i], vga); // printByte(ip[i], vga);
// vga += (sizeof(uint8_t)*2)*2; // vga += (sizeof(uint8_t)*2)*2;
//} //}
vga = (char*)0xb8000 + (160*3);
uint32_t *tss_esp0 = (uint32_t*)0x20004; vga = (char*)error_screen + (160*3);
for(;;) { for(;;) {
switch (ip[0]) { switch (ip[0]) {
case 0x66: // O32 case 0x66: // O32
@ -174,13 +189,15 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
frame->eip = (uint16_t)(frame->eip + 1); frame->eip = (uint16_t)(frame->eip + 1);
goto done; goto done;
case 0xCD: // INT n case 0xCD: // INT n
vga[0] = 'I'; vga[2]++; if (vga[2] < '0') vga[2] = '0'; //vga[0] = 'I'; vga[2]++; if (vga[2] < '0') vga[2] = '0';
switch (ip[1]) { switch (ip[1]) {
case 0x30: case 0x30:
asm ("mov %%eax, %%esp\nret"::"a"(*tss_esp0)); return_prev_task();
for(;;); for(;;);
case 0x3: case 0x3:
kbd_wait(); kbd_wait();
frame->eip = (uint16_t) (frame->eip + 2);
break;
default: default:
stack = &stack[-3]; stack = &stack[-3];
frame->esp = ((frame->esp & 0xffff) - 6) & 0xffff; frame->esp = ((frame->esp & 0xffff) - 6) & 0xffff;
@ -215,18 +232,38 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
frame->eip = (uint16_t) (frame->eip + 1); frame->eip = (uint16_t) (frame->eip + 1);
goto done; goto done;
default: default:
{
uint16_t *e = error_screen;
for (int i = 0; i < 80; i++)
e[i] = 0x0f00;
e += int_printStr("Unknown instruction caused V86 GPF(", e);
int_printWord(error_code, e);
e += 4;
e += int_printStr("):", e);
*(uint32_t*)e = 0x7f007f00;
int_printDword(*(uint32_t*)ip, e);
e += 9;
*(uint8_t*)e = '@';
e += 1;
int_printWord(frame->cs, e);
e += 4;
*(uint8_t*)e = ':';
e += 1;
int_printWord(frame->eip, e);
error_environment();
}
for(;;); for(;;);
} }
} }
done:; done:;
vga = (char*)0xb8000 + (160 * 13); vga = (char*)error_screen + (160 * 13);
vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14;
vga = (char*)0xb8000 + (160 * 14); vga = (char*)error_screen + (160 * 14);
vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14;
vga = (char*)0xb8000 + (160 * 15); vga = (char*)error_screen + (160 * 15);
vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14;
vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14; vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14;
@ -234,9 +271,20 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
} }
extern void timerHandler(); extern void timerHandler();
extern void keyboardHandler();
extern void gpfHandler(); extern void gpfHandler();
extern void pageFaultHandler();
extern void unhandled_handler(); extern void unhandled_handler();
extern void divisionErrorHandler();
extern void boundRangeHandler();
extern void invalidOpcodeHandler();
extern void deviceNotAvailableHandler();
extern void doubleFaultHandler();
extern void invalidTSSHandler();
extern void segmentNotPresentHandler();
extern void stackSegmentHandler();
extern void x87FloatingHandler();
extern void alignmentCheckHandler();
extern void controlProtectionHandler();
extern void picInit(); extern void picInit();
void set_system_gate(uint8_t gate, void (*handler)()) { void set_system_gate(uint8_t gate, void (*handler)()) {
IDT[gate].offset_1 = (uint32_t)(size_t)handler & 0xFFFF; IDT[gate].offset_1 = (uint32_t)(size_t)handler & 0xFFFF;
@ -251,19 +299,14 @@ void set_trap_gate(uint8_t gate, void (*handler)()) {
IDT[gate].type_attributes = 0x8F; IDT[gate].type_attributes = 0x8F;
} }
void setup_interrupts() { void setup_interrupts() {
asm volatile("cli");
IDTR.size = 256*8 - 1; IDTR.size = 256*8 - 1;
IDTR.offset = (uint32_t)(size_t)IDT; IDTR.offset = (uint32_t)(size_t)IDT;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
*(uint64_t*)&IDT[i] = 0; *(uint64_t*)&IDT[i] = 0;
} }
for (int i = 0; i < 9; i++) { for (int i = 0; i < 256; i++) {
set_trap_gate(i, unhandled_handler);
}
for (int i = 10; i < 15; i++) {
set_trap_gate(i, unhandled_handler);
}
for (int i = 16; i < 22; i++) {
set_trap_gate(i, unhandled_handler); set_trap_gate(i, unhandled_handler);
} }
@ -271,6 +314,19 @@ void setup_interrupts() {
set_system_gate(0x21, keyboardHandler); set_system_gate(0x21, keyboardHandler);
//set_trap_gate(13, gpf_handler_v86); //set_trap_gate(13, gpf_handler_v86);
set_trap_gate(13, gpfHandler); set_trap_gate(13, gpfHandler);
set_trap_gate(14, pageFaultHandler);
set_trap_gate(0, divisionErrorHandler);
set_trap_gate(5, boundRangeHandler);
set_trap_gate(6, invalidOpcodeHandler);
set_trap_gate(7, deviceNotAvailableHandler);
set_trap_gate(8, doubleFaultHandler);
set_trap_gate(10, invalidTSSHandler);
set_trap_gate(11, segmentNotPresentHandler);
set_trap_gate(12, stackSegmentHandler);
set_trap_gate(16, x87FloatingHandler);
set_trap_gate(17, alignmentCheckHandler);
set_trap_gate(21, controlProtectionHandler);
asm volatile("lidt %0": : "m"(IDTR)); asm volatile("lidt %0": : "m"(IDTR));
picInit(); picInit();
@ -279,3 +335,11 @@ void setup_interrupts() {
asm volatile("sti"); asm volatile("sti");
} }
__attribute__((__noreturn__))
void triple_fault() {
IDTR.size = 0;
asm volatile("lidt %0": : "m"(IDTR));
asm volatile("sti");
asm volatile("int $1");
for(;;);
}

View File

@ -7,28 +7,6 @@ struct interrupt_frame {
uint32_t esp, ss; uint32_t esp, ss;
uint32_t es, ds, fs, gs; uint32_t es, ds, fs, gs;
}; };
/* Real Mode helper macros */
/* segment:offset pair */
typedef uint32_t FARPTR;
/* Make a FARPTR from a segment and an offset */
#define MK_FP(seg, off) ((FARPTR) (((uint32_t) (seg) << 16) | (uint16_t) (off)))
/* Extract the segment part of a FARPTR */
#define FP_SEG(fp) (((FARPTR) fp) >> 16)
/* Extract the offset part of a FARPTR */
#define FP_OFF(fp) (((FARPTR) fp) & 0xffff)
/* Convert a segment:offset pair to a linear address */
#define FP_TO_LINEAR(seg, off) ((void*)(uintptr_t)((((uint32_t)seg) << 4) + ((uint32_t)off)))
#define EFLAG_IF ((uint32_t)1 << 9)
#define EFLAG_VM ((uint32_t)1 << 17)
__attribute__ ((interrupt)) __attribute__ ((interrupt))
void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code); void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code);

108
kbd.c Normal file
View File

@ -0,0 +1,108 @@
#include "interrupt.h"
#include "kbd.h"
uint8_t scancodesToAscii[0x3B] =
"\0\0" // 0x00 - 0x01
"1234567890" // 0x02 - 0x0B
"-=" // 0x0C - 0x0D
"\0\0" // 0x0E - 0x0F
"qwertyuiop[]" // 0x10 - 0x1B
"\0\0" // 0x1C - 0x1D
"asdfghjkl;'`" // 0x1E - 0x29
"\0" // 0x2A
"\\zxcvbnm,./" // 0x2B - 0x35
"\0" // 0x36
"*" // 0x37
"\0" // 0x38
" " // 0x39
"\0"; // 0x3A
uint8_t scancodesToAsciiShift[0x3B] =
"\0\0" // 0x00 - 0x01
"!@#$%^&*()" // 0x02 - 0x0B
"_+" // 0x0C - 0x0D
"\0\0" // 0x0E - 0x0F
"QWERTYUIOP{}" // 0x10 - 0x1B
"\0\0" // 0x1C - 0x1D
"ASDFGHJKL:\"~" // 0x1E - 0x29
"\0" // 0x2A
"|ZXCVBNM<>?" // 0x2B - 0x35
"\0" // 0x36
"*" // 0x37
"\0" // 0x38
" " // 0x39
"\0"; // 0x3A
uint8_t _KBDWAIT;
uint8_t _KEYCAPS = 0, _KEYSHIFT = 0;
uint8_t _LSTKEY_ASCII = 0, _LSTKEY_SCAN = 0;
__attribute__ ((interrupt))
void keyboardHandler(struct interrupt_frame *frame) {
uint16_t old_ds;
asm volatile(
"mov %%ds, %%bx\n"
"mov $0x10, %%ax\n"
"mov %%ax, %%ds\n"
:"=b"(old_ds)::"%ax"
);
uint8_t key;
asm volatile("inb $0x60, %%al":"=a"(key));
if (key == 0x3A) { // caps lock press
_KEYCAPS = !_KEYCAPS;
} else if (key == 0x2A || key == 0x36) { // left and right shift press
_KEYSHIFT = 1;
} else if (key == 0xAA || key == 0xB6) { // left and right shift release
_KEYSHIFT = 0;
} else if (key < 0x3B) {
uint8_t ascii = _KEYCAPS != _KEYSHIFT ?
scancodesToAsciiShift[key] :
scancodesToAscii[key];
if (ascii) {
_LSTKEY_ASCII = ascii;
_LSTKEY_SCAN = key;
_KBDWAIT = 1;
}
} else {
_LSTKEY_ASCII = 0;
_LSTKEY_SCAN = key;
}
asm volatile("outb %%al, $0x20"::"a"(0x20));
asm volatile(
"mov %%ax, %%ds\n"
::"a"(old_ds)
);
}
__attribute((__no_caller_saved_registers__))
void kbd_clear() {
_KBDWAIT = 0;
_LSTKEY_ASCII = 0;
_LSTKEY_SCAN = 0;
}
__attribute((__no_caller_saved_registers__))
void kbd_wait() {
_KBDWAIT = 0;
while(!_KBDWAIT) {
asm volatile("hlt");
}
}
__attribute((__no_caller_saved_registers__))
uint8_t get_key() {
while(!_LSTKEY_ASCII) {
asm volatile("hlt");
}
uint8_t k = _LSTKEY_ASCII;
_LSTKEY_ASCII = 0;
return k;
}
__attribute((__no_caller_saved_registers__))
uint16_t get_scancode() {
while(!_LSTKEY_SCAN) {
asm volatile("hlt");
}
uint16_t k = _LSTKEY_SCAN | (_LSTKEY_ASCII << 8);
_LSTKEY_SCAN = 0;
_LSTKEY_ASCII = 0;
return k;
}

48
kbd.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
#include "interrupt.h"
__attribute((__no_caller_saved_registers__))
void kbd_wait();
__attribute((__no_caller_saved_registers__))
void kbd_clear();
__attribute((__no_caller_saved_registers__))
uint8_t get_key();
__attribute((__no_caller_saved_registers__))
uint16_t get_scancode();
__attribute__ ((interrupt))
void keyboardHandler(struct interrupt_frame *frame);
typedef enum {
KEY_ESCAPE=0x01, KEY_1=0x02, KEY_2=0x03,
KEY_3=0x04, KEY_4=0x05, KEY_5=0x06, KEY_6=0x07,
KEY_7=0x08, KEY_8=0x09, KEY_9=0x0A, KEY_0=0x0B,
KEY_HYPHEN=0x0C, KEY_EQUALS=0x0D, KEY_BACKSPACE=0x0E, KEY_TAB=0x0F,
KEY_Q=0x10, KEY_W=0x11, KEY_E=0x12, KEY_R=0x13,
KEY_T=0x14, KEY_Y=0x15, KEY_U=0x16, KEY_I=0x17,
KEY_O=0x18, KEY_P=0x19, KEY_SQBRKLEFT=0x1A, KEY_SQBRKRIGHT=0x1B,
KEY_ENTER=0x1C, KEY_CTRL=0x1D, KEY_A=0x1E, KEY_S=0x1F,
KEY_D=0x20, KEY_F=0x21, KEY_G=0x22, KEY_H=0x23,
KEY_J=0x24, KEY_K=0x25, KEY_L=0x26, KEY_SEMICOLON=0x27,
KEY_QUOTE=0x28, KEY_BACKTICK=0x29, KEY_SHIFTLEFT=0x2A, KEY_BACKSTROKE=0x2B,
KEY_Z=0x2C, KEY_X=0x2D, KEY_C=0x2E, KEY_V=0x2F,
KEY_B=0x30, KEY_N=0x31, KEY_M=0x32, KEY_COMMA=0x33,
KEY_PERIOD=0x34, KEY_SLASH=0x35, KEY_SHIFTRIGHT=0x36, KEY_KPDSTAR=0x37,
KEY_ALT=0x38, KEY_SPACE=0x39, KEY_CAPSLOCK=0x3A, KEY_F1=0x3B,
KEY_F2=0x3C, KEY_F3=0x3D, KEY_F4=0x3E, KEY_F5=0x3F,
KEY_F6=0x40, KEY_F7=0x41, KEY_F8=0x42, KEY_F9=0x43,
KEY_F10=0x44, KEY_NUMLOCK=0x45, KEY_SCROLLLOCK=0x46, KEY_KPD7=0x47,
KEY_KPD8=0x48, KEY_KPD9=0x49, KEY_KPDMINUS=0x4A, KEY_KPD4=0x4B,
KEY_KPD5=0x4C, KEY_KPD6=0x4D, KEY_KPDPLUS=0x4E, KEY_KPD1=0x4F,
KEY_KPD2=0x50, KEY_KPD3=0x51, KEY_KPD0=0x52, KEY_KPDPERIOD=0x53,
KEY_F11=0x57, KEY_F12=0x58,
KEY_DOWN=KEY_KPD2,KEY_UP=KEY_KPD8,
KEY_LEFT=KEY_KPD4,KEY_RIGHT=KEY_KPD6,
KEY_PGUP=KEY_KPD9,KEY_PGDOWN=KEY_KPD3,
KEY_HOME=KEY_KPD7,KEY_END=KEY_KPD1,
KEY_INSERT=KEY_KPD0, KEY_DELETE=KEY_KPDPERIOD
} SCANCODE;

544
kernel.c
View File

@ -1,9 +1,17 @@
#include <stdint.h> #include <stdint.h>
#include "file.h"
#include "fs.h"
#include "print.h" #include "print.h"
#include "interrupt.h" #include "interrupt.h"
#include "kbd.h"
#include "tss.c" #include "tss.h"
#include "paging.h"
#include "v86defs.h"
#include "tests.h"
#include "progs.h"
#include "helper.h"
#include "disk.h"
typedef unsigned short word; typedef unsigned short word;
@ -17,6 +25,11 @@ char check_sse() {
asm("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); asm("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
return (edx & (1 << 25)) != 0; return (edx & (1 << 25)) != 0;
} }
char check_cmov() {
uint32_t eax, ebx, ecx, edx;
asm("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
return (edx & (1 << 15)) != 0;
}
void enable_sse() { void enable_sse() {
asm volatile( asm volatile(
@ -31,59 +44,490 @@ void enable_sse() {
); );
} }
void print_flags() { uint32_t get_flags() {
uint32_t flags; uint32_t flags;
asm volatile("pushfd\npop %%eax":"=a"(flags)); asm volatile("pushfd\npop %%eax":"=a"(flags));
printDword(flags, 0xB8000 + (160*4) + 50); return flags;
} }
void print_cr0() { uint32_t get_cr0() {
uint32_t reg; uint32_t reg;
asm volatile("mov %%cr0, %%eax":"=a"(reg)); asm volatile("mov %%cr0, %%eax":"=a"(reg));
printDword(reg, 0xB8000 + (160*5) + 50); return reg;
} }
void print_cr3() { uint32_t get_cr3() {
uint32_t reg; uint32_t reg;
asm volatile("mov %%cr3, %%eax":"=a"(reg)); asm volatile("mov %%cr3, %%eax":"=a"(reg));
printDword(reg, 0xB8000 + (160*5) + 50 + 8*2 + 2); return reg;
} }
void print_cr4() { uint32_t get_cr4() {
uint32_t reg; uint32_t reg;
asm volatile("mov %%cr4, %%eax":"=a"(reg)); asm volatile("mov %%cr4, %%eax":"=a"(reg));
printDword(reg, 0xB8000 + (160*5) + 50 + 8*4 + 4); return reg;
} }
__attribute((__no_caller_saved_registers__)) extern char _edata, _v86code, _ev86code, _bstart, _bend;
extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip); void setup_binary() {
extern void v86Code(); // Put V86 code in proper place based on linker
__attribute((__no_caller_saved_registers__)) char *s = &_edata;
extern char *jmp_usermode_test(); char *d = &_v86code;
while (d < &_ev86code)
*d++ = *s++;
// Clear BSS area
for (d = &_bstart; d < &_bend; d++)
*d = 0;
}
uint16_t error_screen[80*50]; // 50-line VGA screen of error content
extern uint16_t *ivt;
uint16_t ivt_bkup[0x200];
uint8_t bios_bkup[0x40000];
void backup_ivtbios() {
for (int i = 0; i < 0x200; i++)
ivt_bkup[i] = ivt[i];
for (int i = 0; i < 0x40000; i++)
bios_bkup[i] = ((uint8_t*)0xC0000)[i];
}
// This should only be used during fault handling, as it is expensive
void ensure_v86env() {
for (int i = 0; i < 0x200; i++)
ivt[i] = ivt_bkup[i];
for (int i = 0; i < 0x40000; i++)
((uint8_t*)0xC0000)[i] = bios_bkup[i];
char *s = &_edata;
char *d = &_v86code;
while (d < &_ev86code)
*d++ = *s++;
}
uint32_t _ERRORCODE = 0;
__attribute__((__no_caller_saved_registers__))
uint32_t check_error_code() {
uint32_t v = _ERRORCODE;
_ERRORCODE = 0;
return v;
}
__attribute__((__no_caller_saved_registers__))
__attribute__((__noreturn__))
extern void return_prev_task();
__attribute__((__no_caller_saved_registers__))
__attribute__((__noreturn__))
void error_environment(uint32_t stack0, uint32_t stack1, uint32_t stack2, uint32_t stack3, uint32_t stack4, uint32_t stack5) {
ensure_v86env();
setup_interrupts(); // just in case
for (int i = 0; i < 80*50; i++)
if (!(error_screen[i] & 0xFF00))
error_screen[i] = 0x0f00 | (error_screen[i] & 0x00FF);
union V86Regs_t regs;
FARPTR v86_entry = i386LinearToFp(v86TextMode);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
char str0[] = "Oh noes!!! System error! ;c STKDMP:";
printStr(str0, &error_screen[80]);
uint16_t *tmp = &error_screen[80+sizeof(str0)-1];
tmp += printDword(stack0, tmp);
tmp++;
tmp += printDword(stack1, tmp);
tmp++;
tmp += printDword(stack2, tmp);
tmp = &error_screen[80*2+sizeof(str0)-1];
tmp += printDword(stack3, tmp);
tmp++;
tmp += printDword(stack4, tmp);
tmp++;
tmp += printDword(stack5, tmp);
printStr("Press E for a fun recovery :3", &error_screen[80*2]);
printStr("Press R to return to previous task", &error_screen[80*3]);
uint16_t *vga_text = ((uint16_t*)0xB8000);
for (int i = 0; i < 80*50; i++)
vga_text[i] = error_screen[i];
for(;;) {
uint8_t key = get_scancode() & 0xff;
if (key == KEY_E) {
v86_entry = i386LinearToFp(v86TransFlag);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
}
if (key == KEY_R) break;
}
// reset error screen
for (int i = 0; i < (80*50)/2; i++)
((uint32_t*)error_screen)[i] = 0x0f000f00;
_ERRORCODE = -1;
return_prev_task();
}
uint32_t GetFreeStack() {
uint32_t stack;
asm volatile("mov %%esp,%%eax":"=a"(stack));
stack = ((stack - 0x2000) / 0x1000) * 0x1000;
return stack;
}
/* /*
Real Mode Accessible (First MB) Real Mode Accessible (First MB)
00000 - 02000 IVT 00000 - 00400 IVT (1kB)
01000 - 04000 Paging 00400 - 01000 Unused (3kB)
04000 - 07C00 Free 01000 - 04000 Free (12kB)
07C00 - 08000 Boot 04000 - 07C00 V86 Code (15kB)
08000 - 20000 Kernel Code 07C00 - 08000 Boot & V86 Code (512B)
20000 - 22080 TSS 08000 - 20000 Free (96kB)
80000 - 90000 Real Mode Stack 20000 - 30000 Disk Buffer (64kB)
90000 - A0000 Free 30000 - 40000 V86 Data (64kB)
A0000 - FFFFF BIOS Area 40000 - 80000 Free (256kB)
80000 - 90000 V86 Stack (64kB)
90000 - A0000 Free (64kB)
A0000 - C0000 VGA (128kB)
C0000 - FFFFF BIOS Area (256kB)
Protected Only (1MB+) Protected Only (1MB+)
100000 - Free 100000 - 200000 Kernel Code (1mB)
200000 - 200080 TSS (128B)
200080 - 202080 TSS IOMAP (8kB)
202080 - 208000 Free (~24kB)
208000 - 240000 Kernel File Stack (224kB)
240000 - 280000 Active Filesystems (128kB)
280000 - 300000 Disk Cache (512kB)
300000 - 310000 Task Stack (64kB)
310000 - 320000 Interrupt Stack (64kB)
320000 - 400000 Kernel Stack (896kB)
400000 - 700000 Usermode Code (3mB)
700000 - 800000 Usermode Stack (1mB)
*/ */
void DrawScreen(uint16_t *vga) {
uint16_t *vga_text = vga;
// clear screen
for (int i = 0; i < 80*25; i++)
vga_text[i] = 0x1f00;
// draw border
for (int c = 1; c < 79; c++) {
vga_text[c] = 0x1fc4; // top line
vga_text[(2*80)+c] = 0x1fc4; // 3rd line
vga_text[(24*80)+c] = 0x1fc4; // bottom line
}
for (int l = 1; l < 24; l++) {
vga_text[80*l] = 0x1fb3;
vga_text[(80*l)+79] = 0x1fb3;
}
vga_text[0] = 0x1fda;
vga_text[79] = 0x1fbf;
vga_text[2*80] = 0x1fc3;
vga_text[2*80+79] = 0x1fb4;
vga_text[24*80] = 0x1fc0;
vga_text[24*80+79] = 0x1fd9;
// name
vga_text[80+34] = 0x1f00 | '-';
vga_text[80+35] = 0x1f00 | ' ';
vga_text[80+36] = 0x1f00 | 'L';
vga_text[80+37] = 0x1f00 | 'u';
vga_text[80+38] = 0x1f00 | 'c';
vga_text[80+39] = 0x1f00 | 'i';
vga_text[80+40] = 0x1f00 | 'a';
vga_text[80+41] = 0x1f00 | 'O';
vga_text[80+42] = 0x1f00 | 'S';
vga_text[80+43] = 0x1f00 | ' ';
vga_text[80+44] = 0x1f00 | '-';
}
void SetPalette() {
union V86Regs_t regs;
regs.w.bx = 1; // index
regs.h.dh = 0x3D;
regs.h.ch = 0x2A;
regs.h.cl = 0x2E;
regs.w.ax = 0x1010; // set DAC color register
V8086Int(0x10, &regs);
}
// NOTE Does not set text mode
void RestoreVGA() {
SetVideo25Lines();
SetCursorDisabled();
SetPalette();
}
int32_t fileCount, fileOffset;
// We store dir entries in usermode space,
// which is nice because there's nothing after,
// but it does mean we need to reload the dir
// after every task called. This might be fine,
// since the task might have modified the directory.
extern char _USERMODE;
dirent *const DirEntries = (dirent*)&_USERMODE;
#define MAXDISPFILES 16
void PrintFileList(uint16_t *vga) {
uint16_t *vga_text = &((uint16_t *)vga)[80*6+3];
for (int i = 0; (i + fileOffset) < fileCount && i < MAXDISPFILES; i++) {
dirent *de = &DirEntries[i + fileOffset];
de->name[de->namelen < 20 ? de->namelen : 20] = 0;
vga_text += printStr(de->name, vga_text);
vga_text += printStr(" ", vga_text);
vga_text += printDec(de->size, vga_text);
*(uint8_t*)vga_text++ = 'B';
vga_text = nextLine(vga_text, vga) + 3;
}
}
char IsDir(dirent *de) {
return de->type == FT_DIR;
}
void ScancodeTest() {
uint16_t *vga = (uint16_t*)0xb8000;
uint16_t scancode;
while(((scancode = get_scancode()) & 0xff) != KEY_F1) {
vga += printWord(scancode, vga);
vga++;
if ((uintptr_t)vga >= 0xb8000+80*25*2)
vga = (uint16_t*)0xb8000;
}
}
extern void create_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
uint16_t FileSelectScreen[80*25];
char ValidFilesystems[256];
void FileSelect() {
ActiveFilesystemBitmap(ValidFilesystems);
uint8_t currentFsId = 0;
char current_path[80];
uintptr_t current_path_end;
for (int i = 0; i < sizeof(current_path); i++)
current_path[i] = 0;
current_path[0] = '0';
current_path[1] = '/';
current_path_end = 2;
fileCount = 5;
uint16_t *vga_text = (uint16_t *)FileSelectScreen;
int32_t fileHovered = 0;
fileOffset = 0;
for (char reload = 1;;) {
DrawScreen(vga_text);
// Info line (4)
{
uint16_t *vga = &vga_text[80*4 + 79 - 24];
printStr("X to view in hex", vga);
vga += 80;
printStr("T to view as text", vga);
vga += 80;
printStr("P to load as program", vga);
vga += 80;
printStr("O to open directory", vga);
vga += 80;
printStr("S to switch volume", vga);
vga += 80;
printStr("F4 to run tests", vga);
}
printStr(current_path, &vga_text[80*4 + 2]);
for (int i = 2; i < 15; i++)
*(uint8_t*)&vga_text[80*5 + i] = '-';
DIR dir;
if (reload) {
current_path[current_path_end] = 0;
dir_open(&dir, current_path);
GetFileList(&dir, DirEntries, &fileCount, INT32_MAX);
reload = 0;
}
if (fileHovered >= fileCount) {
fileOffset = fileCount - MAXDISPFILES;
fileHovered = fileCount - 1;
}
if ((fileHovered - fileOffset) >= MAXDISPFILES)
fileOffset = fileHovered - MAXDISPFILES + 1;
else if ((fileHovered - fileOffset) < 0)
fileOffset = fileHovered;
PrintFileList(vga_text);
for (int i = 6; i < 24; i++) {
*(uint8_t*)&vga_text[80*i+2] = ' ';
}
*(uint8_t*)&vga_text[80*(6+(fileHovered-fileOffset))+2] = '>';
// Copy to real VGA
for (int i = 0; i < 80*25; i++)
((uint16_t*)0xb8000)[i] = vga_text[i];
uint16_t key = get_scancode();
uint8_t path[13];
switch (key & 0xff) { // scancode component
case KEY_DOWN: // down
fileHovered++;
if (fileHovered >= fileCount) { fileHovered = 0; fileOffset = 0; }
break;
case KEY_UP: // up
fileHovered--;
if (fileHovered < 0) fileHovered = fileCount - 1;
break;
case KEY_F4:
create_child(GetFreeStack(), (uintptr_t)RunTests, 0);
// Set Text mode, tests might return in gfx
{
union V86Regs_t regs;
regs.w.ax = 3;
V8086Int(0x10, &regs);
}
RestoreVGA();
reload = 1;
break;
case KEY_P:
if (IsDir(&DirEntries[fileHovered])) break;
for (int i = 0; i < DirEntries[fileHovered].namelen; i++)
current_path[current_path_end + i] = DirEntries[fileHovered].name[i];
current_path[current_path_end + DirEntries[fileHovered].namelen] = 0;
create_child(GetFreeStack(), (uintptr_t)ProgramLoadTest, 1, current_path);
current_path[current_path_end] = 0;
RestoreVGA();
reload = 1;
break;
case KEY_X:
if (IsDir(&DirEntries[fileHovered])) break;
for (int i = 0; i < DirEntries[fileHovered].namelen; i++)
current_path[current_path_end + i] = DirEntries[fileHovered].name[i];
current_path[current_path_end + DirEntries[fileHovered].namelen] = 0;
create_child(GetFreeStack(), (uintptr_t)HexEditor, 1, current_path);
current_path[current_path_end] = 0;
RestoreVGA();
reload = 1;
break;
case KEY_T:
if (IsDir(&DirEntries[fileHovered])) break;
//TextViewTest(path, &vi);
for (int i = 0; i < DirEntries[fileHovered].namelen; i++)
current_path[current_path_end + i] = DirEntries[fileHovered].name[i];
current_path[current_path_end + DirEntries[fileHovered].namelen] = 0;
create_child(GetFreeStack(), (uintptr_t)TextViewTest, 1, current_path);
current_path[current_path_end] = 0;
RestoreVGA();
reload = 1;
break;
case KEY_O:
case 0x9C: // enter release
if (IsDir(&DirEntries[fileHovered])) {
uint8_t tmp_path[80];
{
int i;
for (i = 0; i < DirEntries[fileHovered].namelen && i < sizeof(tmp_path)-1; i++)
tmp_path[i] = DirEntries[fileHovered].name[i];
tmp_path[i] = 0;
}
if ((*(uint32_t*)tmp_path & 0xffff) == ('.' | 0x0000)) {
// Current dir, do nothing
break;
} else if ((*(uint32_t*)tmp_path & 0xffffff) == ('.' | '.' << 8 | 0x000000)) {
// Up one directory
for (current_path_end--;current_path_end >= 1 && current_path[current_path_end - 1] != '/'; current_path_end--);
current_path[current_path_end] = 0;
reload = 1;
fileHovered = 0;
fileOffset = 0;
break;
}
for (int i = 0; (i + current_path_end) < sizeof(current_path); i++)
current_path[current_path_end + i] = tmp_path[i];
for (;current_path_end < sizeof(current_path) - 2 && current_path[current_path_end]; current_path_end++);
current_path[current_path_end] = '/';
current_path_end++;
current_path[current_path_end] = 0;
reload = 1;
fileHovered = 0;
fileOffset = 0;
}
break;
case KEY_S:
// Next filesystem TODO Support over 077 I'm so lazy right now
for (currentFsId = (currentFsId + 1) % 64; !ValidFilesystems[currentFsId]; currentFsId = (currentFsId + 1) % 64);
if (currentFsId < 8) {
current_path[0] = '0' + currentFsId;
current_path[1] = '/';
current_path[2] = 0;
current_path_end = 2;
} else {
current_path[0] = '0' + (currentFsId >> 3);
current_path[1] = '0' + (currentFsId & 7);
current_path[2] = '/';
current_path[3] = 0;
current_path_end = 3;
}
reload = 1;
fileHovered = 0;
fileOffset = 0;
break;
case KEY_F6:
ScancodeTest();
reload = 1;
break;
default:
break;
}
}
}
int MakeSystemVolume(uint8_t sysPartition);
void MakeMBRPartitions();
void SystemRun(uint8_t sysPartition) {
uint16_t *vga_text = (word *)0xb8000;
RestoreVGA();
DrawScreen((uint16_t*)0xb8000);
InitDisk();
// Check for FAT partition
{
while (1) {
create_child(GetFreeStack(), (uintptr_t)MakeSystemVolume, 1, sysPartition);
if (!check_error_code()) break;
vga_text = &((word*)0xb8000)[80*4 + 2];
vga_text += printStr("Error loading file select. Ensure the disk has a valid MBR and FAT partition.", vga_text);
vga_text = &((word*)0xb8000)[80*5 + 2];
vga_text += printStr("Press R to retry.", vga_text);
for (;(get_scancode() & 0xff) != KEY_R;);
}
create_child(GetFreeStack(), (uintptr_t)MakeMBRPartitions, 0);
}
for (;;) {
create_child(GetFreeStack(), (uintptr_t)FileSelect, 0);
// should never return, so if it does,
// we have an error
{
union V86Regs_t regs;
FARPTR v86_entry = i386LinearToFp(v86TextMode);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
}
RestoreVGA();
DrawScreen((uint16_t*)0xb8000);
vga_text = &((word*)0xb8000)[80*4 + 2];
vga_text += printStr("Error loading file select. Ensure the disk has a valid MBR and FAT partition.", vga_text);
vga_text = &((word*)0xb8000)[80*5 + 2];
vga_text += printStr("Press R to retry.", vga_text);
for (;(get_scancode() & 0xff) != KEY_R;);
}
}
__attribute__((__noreturn__))
extern void triple_fault();
uint32_t kernel_check = 0x12345678;
void start() { void start() {
word *vga_text = (word *)0xb8000; word *vga_text = (word *)0xb8000;
char h[] = "LuciaOS"; char h[] = "LuciaOS";
for (int i = 0; i < sizeof(h); i++) for (int i = 0; i < sizeof(h); i++)
*(char *)&vga_text[i] = h[i]; *(char *)&vga_text[i] = h[i];
vga_text = &vga_text[80];
// DL *should* be preserved
uint16_t boot_dx;
asm volatile("nop":"=d"(boot_dx));
if (!check_cmov()) {
char cmov_err[] = "NO CMOV";
for (int i = 0; i < sizeof(cmov_err); i++)
*(char *)&vga_text[i] = cmov_err[i];
for (;;) asm volatile("hlt");
}
vga_text += printWord(boot_dx, vga_text);
vga_text++;
uint32_t o; uint32_t o;
asm("mov %%esp, %%eax" : "=a"(o) : :); asm("mov %%esp, %%eax" : "=a"(o) : :);
printDword(o, (uint16_t *)&vga_text[80]); vga_text += printDword(o, vga_text);
vga_text++;
asm("mov %%ebp, %%eax" : "=a"(o) : :); asm("mov %%ebp, %%eax" : "=a"(o) : :);
printDword(o, (uint16_t *)&vga_text[160]); vga_text += printDword(o, vga_text);
vga_text++;
//char c[] = "APIC support: "; //char c[] = "APIC support: ";
//char apic; //char apic;
@ -92,27 +536,37 @@ void start() {
// *(char *)&vga_text[i+(80*3)] = c[i]; // *(char *)&vga_text[i+(80*3)] = c[i];
//if (!apic) return; //if (!apic) return;
char sse_str[] = "SSE support: "; //char sse_str[] = "SSE support: ";
char sse; //vga_text += printStr("SSE: ", vga_text);
printByte(sse = check_sse(), (uint16_t*)&vga_text[80*4 + sizeof(sse_str) - 1]); //char sse = check_sse();
for (int i = 0; i < sizeof(sse_str); i++) //if (!sse) {
*(char *)&vga_text[i+(80*4)] = sse_str[i]; // *vga_text = 'N';
if (!sse) return; //}
enable_sse(); //vga_text += printStr("Y ", vga_text);
//enable_sse();
// edit //print_flags();
vga_text += printStr("CR0:", vga_text);
vga_text += printDword(get_cr0(), vga_text);
vga_text++;
//print_cr3();
//print_cr4();
// Setup system
setup_binary();
setup_interrupts(); setup_interrupts();
setup_tss(); setup_tss();
print_flags(); init_paging();
print_cr0(); backup_ivtbios();
print_cr3();
print_cr4();
FARPTR v86_entry = i386LinearToFp(v86Code);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
char *vga = jmp_usermode_test();
for (int i = 0; i < 320; i++) { // DL contained disk number, DH contained active partition
vga[i] = i; uint8_t SystemPartition = boot_dx >> 8;
}
create_child(GetFreeStack(), (uintptr_t)SystemRun, 1, SystemPartition);
// If this returns, something is *very* wrong, reboot the system
// TODO Maybe try to recover?
// Triple fault
triple_fault();
} }

49
link.ld
View File

@ -2,18 +2,51 @@ OUTPUT_FORMAT(binary)
ENTRY(entry) ENTRY(entry)
SECTIONS { SECTIONS {
. = 0x8000; . = 0x100000;
_USERMODE = 0x800000;
_USERMODE_END = 0x1000000;
.text : ALIGN(0x1000) { .text : {
*(.text) *(.text);
} }
.data : ALIGN(0x1000) { .data : {
*(.data) *(.data*);
*(.rodata) *(.data);
*(.rodata);
*(.rodata*);
_edata = .;
} }
.bss : ALIGN(0x1000) { .realmode 0x4000 :
*(.bss) AT ( _edata )
{ _v86code = .; *(.v86); _ev86code = .; }
. = _edata + SIZEOF(.realmode);
.bss : ALIGN(0x1000)
{
_bstart = .; *(.bss); *(.bss*) _bend = .;
}
.bss _USERMODE (NOLOAD) : AT(_bend) {
_bhexstart = .;
*(.hexbss);
*(.hexbss*);
_bhexend = .;
*(.hexlatebss);
*(.hexlatebss*);
}
.bss _USERMODE (NOLOAD) : AT(_bend) {
_btextstart = .;
*(.textbss);
*(.textbss*);
_btextend = .;
*(.textlatebss);
*(.textlatebss*);
}
/DISCARD/ : {
*(.note*)
*(.comment*)
} }
} }

63
paging.c Normal file
View File

@ -0,0 +1,63 @@
#include "paging.h"
uint32_t page_directory[1024] __attribute__((aligned(4096)));
uint32_t page_tables[4][1024] __attribute__((aligned(4096)));
void enable_paging() {
asm(
"mov %%eax, %%cr3\n"
"mov %%cr0, %%eax\n"
"or $0x80000001, %%eax\n"
"mov %%eax, %%cr0\n"
::"a"(page_directory));
}
// TODO Add a function which can dynamically add and remove pages,
// that keeps track internally of what's already used?
// NOTE This function cannot cross 4MB (1024 page) boundaries
void map_pages(uintptr_t page, uintptr_t num_pages, uintptr_t phy_address, char access_flags) {
uintptr_t access = access_flags & 0x7;
// Each page is 4K, Each page table is 4M
uintptr_t page_dir_entry = page >> 10;
if (page_dir_entry >= 4) return;
uintptr_t page_index = page & 0x3ff;
if (page_index + num_pages > 1024) return;
// Address must be 4K aligned
if (phy_address & 0x3ff) return;
for (int i = 0; i < num_pages; i++)
page_tables[page_dir_entry][page_index + i] = phy_address + (i * 0x1000) | access_flags;
}
// User, R/W, Present
#define USERACCESS (4|2|1)
// Supervisor, R/W, Present
#define SUPERACCESS (2|1)
void init_paging() {
for (int i = 0; i < 1024; i++)
// Supervisor, R/W, Not Present
page_directory[i] = 2;
// 1024 Pages = 1MB
// First MB: Real Mode
// TODO Make some of this not accessible to usermode?
map_pages(0x00000000 >> 12, 256, 0x00000000, USERACCESS);
// Next 3MB: Kernel
map_pages(0x00100000 >> 12, 768, 0x00100000, SUPERACCESS);
// Next 4MB: Kernel
map_pages(0x00400000 >> 12, 1024, 0x00400000, SUPERACCESS);
// Next 8MB: Usermode
map_pages(0x00800000 >> 12, 1024, 0x00800000, USERACCESS);
map_pages(0x00C00000 >> 12, 1024, 0x00C00000, USERACCESS);
// We aren't using page directory permissions
for (int i = 0; i < 4; i++)
page_directory[i] = ((uintptr_t)&page_tables[i]) | USERACCESS;
enable_paging();
}

3
paging.h Normal file
View File

@ -0,0 +1,3 @@
#include <stdint.h>
void init_paging();

34
print.c
View File

@ -3,15 +3,43 @@
char nibbleToHex(uint8_t n) { char nibbleToHex(uint8_t n) {
return n > 9 ? (n - 10) + 'A' : n + '0'; return n > 9 ? (n - 10) + 'A' : n + '0';
} }
void printByte(uint8_t v, uint16_t *buff) { uintptr_t printByte(uint8_t v, uint16_t *buff) {
*(char *)&buff[0] = nibbleToHex((v >> 4) & 0xF); *(char *)&buff[0] = nibbleToHex((v >> 4) & 0xF);
*(char *)&buff[1] = nibbleToHex(v & 0xF); *(char *)&buff[1] = nibbleToHex(v & 0xF);
return 2;
} }
void printWord(uint16_t v, uint16_t *buff) { uintptr_t printWord(uint16_t v, uint16_t *buff) {
printByte(v >> 8, buff); printByte(v >> 8, buff);
printByte(v, &buff[2]); printByte(v, &buff[2]);
return 4;
} }
void printDword(uint32_t v, uint16_t *buff) { uintptr_t printDword(uint32_t v, uint16_t *buff) {
printWord(v >> 16, buff); printWord(v >> 16, buff);
printWord(v, &buff[4]); printWord(v, &buff[4]);
return 8;
}
uintptr_t printStr(char *v, uint16_t *buff) {
char *s;
for (s = v;*s;s++,buff++)
*(char*)buff = *s;
return s - v;
}
uintptr_t printChar(char v, uint16_t *buff) {
*(char*)buff = v;
return 1;
}
uintptr_t printDec(uint32_t v, uint16_t *buff) {
char b[12];
char *s = &b[11];
if (!v) {
*(uint16_t*)&b[10] = '0'; // '0',0x00
s = &b[10];
} else {
*s = 0;
for (;v;v/=10) *--s = '0' + v%10;
}
return printStr(s, buff);
} }

10
print.h
View File

@ -1,6 +1,8 @@
#pragma once
#include <stdint.h> #include <stdint.h>
void printByte(uint8_t v, uint16_t *buff); uintptr_t printByte(uint8_t v, uint16_t *buff);
void printWord(uint16_t v, uint16_t *buff); uintptr_t printWord(uint16_t v, uint16_t *buff);
void printDword(uint32_t v, uint16_t *buff); uintptr_t printDword(uint32_t v, uint16_t *buff);
uintptr_t printStr(char *v, uint16_t *buff);
uintptr_t printDec(uint32_t v, uint16_t *buff);
uintptr_t printChar(char v, uint16_t *buff);

61
progs.c Normal file
View File

@ -0,0 +1,61 @@
#include "progs.h"
#include "file.h"
extern char _USERMODE, _USERMODE_END;
extern uint32_t create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
void ProgramLoadTest(char *path) {
uint16_t *vga_text = (uint16_t *)0xb8000;
for (int i = 0; i < 80*25; i++)
vga_text[i] = 0x0f00;
uint32_t successcount;
uint8_t *diskReadBuf = (uint8_t *)&_USERMODE;
{
uint32_t err;
dirent de;
err = path_getinfo(path, &de);
if (de.size > 0x300000 || err) {
vga_text += printStr("File too large or error.", vga_text);
kbd_wait();
return;
}
FILE file;
err = file_open(&file, path, OPENREAD);
if (err) {
vga_text += printStr("Open Error: ", vga_text);
printDword(err, vga_text);
return;
}
err = file_seek(&file, 0);
if (err) {
vga_text += printStr("Seek Error", vga_text);
return;
}
successcount = err = file_read(&file, diskReadBuf, de.size);
if (!err && de.size > 0) {
vga_text += printStr("Read Error", vga_text);
printDword(err, vga_text);
return;
}
}
vga_text += printStr("Successfully loaded program \"", vga_text);
vga_text += printStr((char*)path, vga_text);
vga_text += printStr("\", ", vga_text);
vga_text += printDec(successcount, vga_text);
vga_text += printStr(" Bytes.", vga_text);
vga_text = nextLine(vga_text,(uint16_t*)0xb8000);
vga_text += printStr("Press any key to run.", vga_text);
kbd_wait();
uint32_t res = create_user_child((uintptr_t)&_USERMODE_END, (uintptr_t)&_USERMODE, 0);
union V86Regs_t regs;
regs.w.ax = 3; // text mode
V8086Int(0x10, &regs);
vga_text = (uint16_t *)0xb8000;
for (int i = 0; i < 80*25; i++)
vga_text[i] = 0x0f00;
vga_text += printStr("Program returned code: ", vga_text);
vga_text += printDec(res, vga_text);
vga_text += printStr(" (0x", vga_text);
vga_text += printDword(res, vga_text);
vga_text += printStr("). Press any key to exit.", vga_text);
kbd_wait();
}

12
progs.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
#include "print.h"
#include "kbd.h"
#include "v86defs.h"
#include "helper.h"
#include "file.h"
void HexEditor(char *path);
void TextViewTest(char *path);
void ProgramLoadTest(char *path);

204
task.nasm Normal file
View File

@ -0,0 +1,204 @@
global flushTSS
flushTSS:
mov ax, 0x28
ltr ax
ret
global task_ptr
task_ptr: equ (0x310000-4)
; TODO Is varargs too compiler dependent?
; extern void create_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
global create_child
create_child:
mov eax, [esp] ; return address
lea ecx, [esp+4] ; return stack, minus address
call save_current_task
mov eax, [esp+8] ; new eip
mov ecx, [esp+12] ; argc
mov esi, esp ; old esp
mov esp, [esp+4] ; new esp
lea esi, [esi+16] ; args
mov edx, ecx
neg edx
lea esp, [esp+edx*4] ; adjust for args
mov edi, esp
; copy varargs to new stack
rep movsd
push return_prev_task ; if child returns, return to prev task
push eax
ret
; extern void create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
global create_user_child
create_user_child:
mov eax, [esp] ; return address
lea ecx, [esp+4] ; return stack, minus address
call save_current_task
mov eax, [esp+8] ; new eip
mov ecx, [esp+12] ; argc
mov esi, esp ; old esp
mov esp, [esp+4] ; new esp
lea esi, [esi+16] ; args
mov edx, ecx
neg edx
lea esp, [esp+edx*4] ; adjust for args
mov edi, esp
; copy varargs to new stack
rep movsd
push 0x00000000 ; if child returns, return to null, crashing
mov ecx, 0x20 | 3
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
mov ecx, esp
push 0x20 | 3
push ecx
pushfd
push 0x18 | 3
push eax
iret
; return address in EAX
; return stack in ECX
; we can modify EAX, ECX, EDX
; i.e. save all others in task
global save_current_task
save_current_task:
push edx
mov edx, esp ; EDX holds our tmp stack, unsaved
mov esp, dword [task_ptr] ; load current task pointer
push ss
push ecx ; return stack
pushfd
push cs
push eax ; return address
push ds ; other segs, pop
push es ; before iret
push fs ; in exit handler
push gs
push ebp ; saved
push ebx ; saved
push esi ; saved
push edi ; saved
mov dword [task_ptr], esp ; save new task pointer
mov esp, edx
pop edx
ret
; FIXME System will crash if last task is invalid
global return_prev_task
return_prev_task:
mov ecx, eax ; save return value for later
mov edx, dword [task_ptr] ; load current task pointer
add dword [task_ptr], 52 ; adjust to last task pointer
mov edi, [edx+0]
mov esi, [edx+4]
mov ebx, [edx+8]
mov ebp, [edx+12]
mov eax, [edx+0+16] ; gs
mov gs, ax
mov eax, [edx+4+16] ; fs
mov fs, ax
mov eax, [edx+8+16] ; es
mov es, ax
; SS:ESP <- return stack
push ecx
mov eax, [edx+32+16] ; ss
mov ecx, [edx+20+16] ; cs
and eax, 3
and ecx, 3
or eax, ecx
pop ecx
cmp eax, 3
je .ret_stack
.ret_no_stack:
mov esp, [edx+28+16] ; esp
mov eax, [edx+32+16] ; ss
mov ss, ax
mov eax, [edx+24+16] ; eflags
push eax
mov eax, [edx+20+16] ; cs
push eax
mov eax, [edx+16+16] ; eip
push eax
mov eax, [edx+12+16] ; ds
mov ds, ax
mov eax, ecx ; restore return value
iret
.ret_stack:
mov eax, [edx+32+16] ; ss
push eax
mov eax, [edx+28+16] ; esp
push eax
mov eax, [edx+24+16] ; eflags
push eax
mov eax, [edx+20+16] ; cs
push eax
mov eax, [edx+16+16] ; eip
push eax
mov eax, [edx+12+16] ; ds
mov ds, ax
mov eax, ecx ; restore return value
iret
; extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip, union V86Regs_t *regs);
global enter_v86
global _enter_v86_internal_no_task
enter_v86:
pop eax ; return address
mov ecx, esp ; return stack
call save_current_task
_enter_v86_internal_no_task:
mov ebp, esp ; save stack pointer
; push v86 stuff for iret
mov eax, 0x3000
push eax ; gs
push eax ; fs
push eax ; ds
push eax ; es
push dword [ebp+0] ; ss
push dword [ebp+4] ; esp
pushfd ; eflags
or dword [esp], (1 << 17) ; set VM flags
;or dword [esp], (3 << 12) ; IOPL 3
push dword [ebp+8] ; cs
push dword [ebp+12] ; eip
; check if we have regs
mov eax, dword [ebp+16] ; regs
test eax, eax
jz .no_regs
; load regs: ebp, edi, esi, ebx, edx, ecx, eax
mov ebp, dword [eax+0]
mov edi, dword [eax+4]
mov esi, dword [eax+8]
mov ebx, dword [eax+12]
mov edx, dword [eax+16]
mov ecx, dword [eax+20]
mov eax, dword [eax+24]
.no_regs:
iret
; return address in eax, return stack in ebp
;extern save_current_task
;extern user_test
;global jmp_usermode_test
;jmp_usermode_test:
;pop eax ; return address
;mov ecx, esp ; return stack
;call save_current_task
;mov esp, 0x800000 ; usermode stack
;mov eax, 0x20 | 3
;mov ds, ax
;mov es, ax
;mov fs, ax
;mov gs, ax
;mov eax, esp
;push 0x20 | 3
;push eax
;pushfd
;push 0x18 | 3
;push user_test
;iret

36
testpgrm.nasm Normal file
View File

@ -0,0 +1,36 @@
[BITS 32]
[ORG 0x800000]
xchg bx,bx
mov edi, 0xB8000
mov ecx, 80*25
mov eax, 0x2f00
rep stosw
mov edi, 0xB8000
mov esi, str0
mov ecx, str0_end - str0
mov eax, 0x2f00
.loop:
lodsb
stosw
loop .loop
mov edi, 0xB8000 + 160 - 2
.key_loop:
mov eax, 1 ; command = 01h, get scancode
int 0x21 ; OS call
cmp al, 0x3B ; F1
je .exit
cmp al, 0x3C ; F2
je .crash
cmp ah, 0x00
je .key_loop
mov byte [edi], ah ; ASCII value
add edi, 2
jmp .key_loop
.exit:
mov eax, 0x1337 ; Exit code/Return value
int 0x30 ; Exit
.crash:
jmp 0x00000000
str0: db "Running usermode program from disk! Press F1 to exit. Press F2 to crash."
str0_end:

271
tests.c Normal file
View File

@ -0,0 +1,271 @@
#include "tests.h"
extern char *user_test();
extern uint32_t create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
void TestV86() {
union V86Regs_t regs;
regs.d.edi = 0x11111111;
regs.d.esi = 0x22222222;
regs.d.ebx = 0x33333333;
regs.d.edx = 0x44444444;
regs.d.ecx = 0x55555555;
regs.d.eax = 0x66666666;
FARPTR v86_entry = i386LinearToFp(v86Test);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
uint16_t *vga_text = (uint16_t *)0xb8000 + (80*2);
vga_text += printStr("Done.", vga_text);
}
extern char _USERMODE, _USERMODE_END;
extern char _binary_usermode_bin_start, _binary_usermode_bin_end;
void ReloadUser() {
// Put Usermode code in proper place based on linker
char *s = &_binary_usermode_bin_start;
char *d = (char *)&_USERMODE;
while (s < &_binary_usermode_bin_end)
*d++ = *s++;
}
char TestUser() {
ReloadUser();
char *vga = (char *)(uintptr_t)create_user_child((uintptr_t)&_USERMODE_END, (uintptr_t)&_USERMODE, 0);
if ((uintptr_t)vga != 0xA0000) {
return 1;
}
for (int i = 0; i < 320; i++) {
vga[i] = i;
}
return 0;
}
void TestDiskRead() {
char *diskReadBuf = (char *)0x20000;
v86disk_addr_packet.transfer_buffer =
(uintptr_t)diskReadBuf & 0x000F |
(((uintptr_t)diskReadBuf & 0xFFFF0) << 12);
v86disk_addr_packet.start_block = 0;
v86disk_addr_packet.blocks = 2;
union V86Regs_t regs;
kbd_clear();
for (;;) {
regs.h.ah = 0x42;
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
enter_v86(0x0000, 0x8000, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
uint16_t *vga_text = (uint16_t *)0xb8000;
for (int i = 0; i < 25; i++) {
uint16_t *vga_line = &vga_text[i*80];
for (int j = 0; j < 26; j++) {
printByte(diskReadBuf[i*26 + j], &vga_line[j*2]);
*(uint8_t*)&vga_line[j + (26*2)+1] = diskReadBuf[i*26 + j];
}
}
uint16_t scan = get_scancode();
if ((scan & 0xff) == KEY_PGUP) {
if(v86disk_addr_packet.start_block > 0) v86disk_addr_packet.start_block--;
} else if ((scan & 0xff) == KEY_PGDOWN)
v86disk_addr_packet.start_block++;
else if (scan & 0xff00) break;
}
}
extern uint32_t _gpf_eax_save;
extern uint32_t _gpf_eflags_save;
void TestCHS() {
uint16_t *vga_text = (uint16_t*)0xb8000;
for (int i = 0; i < 80*25; i++)
vga_text[i] = 0x0f00;
printStr("CHS Test ", vga_text);
// CHS Read
union V86Regs_t regs;
FARPTR v86_entry = i386LinearToFp(v86DiskGetGeometry);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
// check CF for error
if (_gpf_eflags_save & 0x1) {
uint16_t *vga = &((uint16_t*)0xb8000)[80];
vga += printStr("Could not get Disk Geometry.", vga);
return;
}
uint32_t numHead = ((_gpf_eax_save & 0xff00) >> 8) + 1;
uint32_t secPerTrack = _gpf_eax_save & 0x3f;
uint32_t maxCyl = ((_gpf_eax_save & 0xff0000) >> 16) | ((_gpf_eax_save & 0xc0) << 2);
uint32_t maxLBA = numHead * secPerTrack * (maxCyl + 1);
for (uint32_t sector = 0; sector < maxLBA && sector < 0x8000 /* don't bother reading huge disks */; sector += (sector + secPerTrack < maxLBA) ? secPerTrack : 1) {
uint16_t *vga = &((uint16_t*)0xb8000)[9];
vga += printWord(sector, vga); vga++;
// LBA -> CHS
uint32_t tmp = sector / secPerTrack;
uint32_t sec = (sector % (secPerTrack)) + 1;
uint32_t head = tmp % numHead;
uint32_t cyl = tmp / numHead;
vga += printWord(cyl, vga); vga += printChar(':', vga);
vga += printByte(head, vga); vga += printChar(':', vga);
vga += printByte(sec, vga); vga += printChar(' ', vga);
vga += printStr("/ ", vga);
vga += printWord(maxCyl, vga); vga += printChar(':', vga);
vga += printByte(numHead - 1, vga); vga += printChar(':', vga);
vga += printByte(secPerTrack, vga); vga += printChar(' ', vga);
regs.w.ax = 0x0201;
regs.h.ch = cyl & 0xff;
regs.h.cl = sec | ((cyl >> 2) & 0xc0);
regs.h.dh = head;
regs.h.dl = 0x80;
regs.w.bx = 0x0000;
v86_entry = i386LinearToFp(v86DiskReadCHS);
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), &regs);
// if CF was set, we have error
if (_gpf_eflags_save & 0x1) {
uint16_t *vga = &((uint16_t*)0xb8000)[80];
vga += printStr("Disk Error: ", vga);
vga += printByte(_gpf_eax_save & 0xff, vga);
vga += printStr(" CHS=", vga);
vga += printWord(cyl, vga); vga += printChar(':', vga);
vga += printByte(head, vga); vga += printChar(':', vga);
vga += printByte(sec, vga);
return;
}
}
}
//void TestFAT() {
// uint16_t *vga_text = (uint16_t *)0xb8000;
// uint8_t *diskReadBuf = (uint8_t *)0x22400;
// for (int i = 0; i < 80*25; i++)
// vga_text[i] = 0x0f00;
// VOLINFO vi;
//
// uint8_t pactive, ptype;
// uint32_t pstart, psize;
// pstart = DFS_GetPtnStart(0, diskReadBuf, SystemPartition, &pactive, &ptype, &psize);
// vga_text = (uint16_t *)0xb8000;
// vga_text += printStr("PartStart: ", vga_text);
// vga_text += printDword(pstart, vga_text);
// vga_text += 2;
// vga_text += printStr("PartSize: ", vga_text);
// vga_text += printDword(psize, vga_text);
// vga_text += 2;
// vga_text += printStr("PartActive: ", vga_text);
// vga_text += printByte(pactive, vga_text);
// vga_text += 2;
// vga_text += printStr("PartType: ", vga_text);
// vga_text += printByte(ptype, vga_text);
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
// //asm ("xchgw %bx, %bx");
//
// DFS_GetVolInfo(0, diskReadBuf, pstart, &vi);
// vga_text += printStr("Label: ", vga_text);
// vga_text += printStr((char*)vi.label, vga_text);
// vga_text += 2;
// vga_text += printStr("Sec/Clus: ", vga_text);
// vga_text += printByte(vi.secperclus, vga_text);
// vga_text += 2;
// vga_text += printStr("ResrvSec: ", vga_text);
// vga_text += printWord(vi.reservedsecs, vga_text);
// vga_text += 2;
// vga_text += printStr("NumSec: ", vga_text);
// vga_text += printDword(vi.numsecs, vga_text);
// vga_text += 2;
// vga_text += printStr("Sec/FAT: ", vga_text);
// vga_text += printDword(vi.secperfat, vga_text);
// vga_text += 2;
// vga_text += printStr("FAT1@: ", vga_text);
// vga_text += printDword(vi.fat1, vga_text);
// vga_text += 2;
// vga_text += printStr("ROOT@: ", vga_text);
// vga_text += printDword(vi.rootdir, vga_text);
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
// //asm ("xchgw %bx, %bx");
//
// vga_text += printStr("Files in root:", vga_text);
// DIRINFO di;
// di.scratch = diskReadBuf;
// DFS_OpenDir(&vi, (uint8_t*)"", &di);
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
// DIRENT de;
// while (!DFS_GetNext(&vi, &di, &de)) {
// if (de.name[0]) {
// for (int i = 0; i < 11 && de.name[i]; i++) {
// if (i == 8) { *(uint8_t*)vga_text = ' '; vga_text++; } // space for 8.3
// *(uint8_t *)vga_text = de.name[i];
// vga_text++;
// }
// vga_text += printStr(" ", vga_text);
// vga_text += printDec((uint32_t)de.filesize_0 + ((uint32_t)de.filesize_1 << 8) + ((uint32_t)de.filesize_2 << 16) + ((uint32_t)de.filesize_3 << 24), vga_text);
// *(uint8_t*)vga_text++ = 'B';
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
// }
// //asm ("xchgw %bx, %bx");
// }
//}
void RunTests() {
char doTests = 1;
uint16_t *vga_text = (uint16_t*)0xb8000;
uint8_t key;
for (char l = 1;l;) {
if (doTests) {
vga_text = (uint16_t*)0xb8000;
for (int i = 0; i < 80*25; i++)
vga_text[i] = 0x1f00;
vga_text += printStr("V86 Test... ", vga_text);
//asm ("xchgw %bx, %bx");
TestV86(); // has int 3 wait in v86
vga_text = (uint16_t *)0xb8000 + (80*3);
vga_text += printStr("Press 'N' for next test.", vga_text);
for(;;) {
key = get_key();
if (key == 'N' || key == 'n') break;
*vga_text = (*vga_text & 0xFF00) | key;
vga_text++;
}
if (TestUser()) {
union V86Regs_t regs;
regs.w.ax = 3; // text mode
V8086Int(0x10, &regs);
vga_text = (uint16_t *)0xb8000 + (80*5);
printStr("Usermode test failed! Press any key to continue.", vga_text);
kbd_wait();
} else {
kbd_wait();
union V86Regs_t regs;
regs.w.ax = 3; // text mode
V8086Int(0x10, &regs);
}
doTests = 0;
}
vga_text = &((uint16_t*)0xB8000)[80*14];
vga_text += printStr("Press E for a flagrant system error. ", vga_text);
vga_text = &((uint16_t*)0xB8000)[80*15];
vga_text += printStr("Press D for disk tests. ", vga_text);
vga_text = &((uint16_t*)0xB8000)[80*16];
vga_text += printStr("Press R to repeat tests. ", vga_text);
vga_text = &((uint16_t*)0xB8000)[80*17];
vga_text += printStr("Press C to continue... ", vga_text);
switch (key = get_key()) {
case 'e':
case 'E':
// flagrant system error
*((uint8_t*)0x1000000) = 0;
break;
case 'c':
case 'C':
// continue
l = 0;
break;
case 'd':
case 'D':
// disk tests
TestCHS();
kbd_wait();
TestDiskRead();
//TestFAT();
break;
case 'r':
case 'R':
doTests = 1;
break;
default:
*vga_text = (*vga_text & 0xFF00) | key;
vga_text++;
break;
}
}
}

8
tests.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "print.h"
#include "interrupt.h"
#include "v86defs.h"
#include "kbd.h"
#include "helper.h"
void RunTests();

244
textedit.c Normal file
View File

@ -0,0 +1,244 @@
#include "progs.h"
#define MAXFILESIZE 0x300000 // 3MB
#define MAXLINES 100000
#define BLOCKSIZE 0x1000
#define BLOCKMASK 0xfff
#define BLOCKSHIFT 12
#define TOTALBLOCKS (MAXFILESIZE/BLOCKSIZE)
uintptr_t lineOffsets[MAXLINES] __attribute__((section(".textbss")));;
uintptr_t lineLengths[MAXLINES] __attribute__((section(".textbss")));;
uint8_t editedBlocks[TOTALBLOCKS] __attribute__((section(".textbss")));;
uint8_t fileBuffer[MAXFILESIZE]
__attribute__((aligned(0x1000)))
__attribute__((section(".textlatebss")));
void TextViewTest(char *path) {
uint16_t *vga_text = (uint16_t *)0xb8000;
uint32_t fileLen;
{
uint32_t err;
dirent de;
err = path_getinfo(path, &de);
if (err) {
vga_text += printStr("Error getting file info.", vga_text);
kbd_wait();
return;
}
fileLen = de.size;
FILE file;
err = file_open(&file, path, OPENREAD);
if (err) {
vga_text += printStr("Open Error: ", vga_text);
printDword(err, vga_text);
kbd_wait();
return;
}
// file too large
if (fileLen > MAXFILESIZE) {
vga_text += printStr("File too large.", vga_text);
kbd_wait();
return;
}
if (file_seek(&file, 0)) {
vga_text += printStr("Seek Error", vga_text);
kbd_wait();
return;
}
uint32_t bytesRead = file_read(&file, fileBuffer, fileLen);
if (bytesRead < fileLen) {
vga_text += printStr("Read Error: ", vga_text);
printDword(err, vga_text);
kbd_wait();
return;
}
}
uint32_t lastLine;
{
char nl;
uint8_t c = 0x0A; // start with a pretend newline
uint32_t line = -1; // start a pretend line behind
uint32_t lineLen = 0;
for (int32_t o = -1; o < (int32_t)fileLen; lineLen++, c = fileBuffer[++o]) {
// newline
if (c == 0x0A) {
lineOffsets[++line] = o;
lineLengths[line] = lineLen > 0 ? lineLen : 1;
lineLen = 0;
}
// file too large
if (line >= MAXLINES) {
vga_text += printStr("File too large.", vga_text);
kbd_wait();
return;
}
}
lineLengths[line] = lineLen;
lastLine = line;
}
uint32_t currLine = 0;
char cont = 1;
uint32_t screenSize = 80*25;
char redraw = 1;
uint32_t linesOnScreen = 0;
uint32_t cursorLine = 0;
uint32_t cursorLineOffset = 0;
char cursorChange = 0;
for (;cont;) {
if (cursorLine > lastLine) {
cursorLine = lastLine;
cursorLineOffset = lineLengths[lastLine] - 2;
cursorChange = 1;
}
if (lineLengths[cursorLine+1] == 1 && cursorLineOffset == 0){}
else if (cursorLineOffset >= lineLengths[cursorLine+1] - 1) {
cursorLineOffset = 0;
cursorLine++;
cursorChange = 1;
}
if (redraw || cursorChange) {
vga_text = (uint16_t *)0xb8000;
for (int i = 0; i < screenSize; i++)
vga_text[i] = 0x0f00;
char pathBuff[22];
trimPath((char*)path, pathBuff, sizeof(pathBuff));
vga_text += printStr(pathBuff, vga_text);
vga_text += 2;
vga_text += printDec(currLine, vga_text);
vga_text += printChar('/', vga_text);
vga_text += printDec(lastLine, vga_text);
vga_text += printStr(" Scroll: Up/Down PgUp/PgDown Home/End", vga_text);
{
const char prnt[] = "Exit: F1";
vga_text = &((uint16_t*)0xb8000)[80-sizeof(prnt)+1];
vga_text += printStr((char*)prnt, vga_text);
}
for (vga_text = &((uint16_t*)0xb8000)[84]; vga_text < &((uint16_t*)0xb8000)[screenSize]; vga_text += 80)
*(uint8_t*)vga_text = '|';
vga_text = &((uint16_t*)0xb8000)[0];
uint32_t screenLineOff = 6;
uint32_t lineOffset = 0;
uint8_t c = 0x0A; // start with a pretend newline
uint32_t line = currLine - 1; // start a pretend line behind
int32_t o = lineOffsets[currLine]; // the real or fake newline on previous line
linesOnScreen = screenSize/80;
for (; o < (int32_t)fileLen && vga_text < &((uint16_t*)0xb8000)[screenSize]; c = fileBuffer[++o]) {
// newline
if (c == 0x0A) {
vga_text = nextLine(vga_text,(uint16_t*)0xb8000);
line++;
lineOffset = 0;
{
uint16_t *vga_tmp = vga_text;
uint16_t decTmp[11];
char cnt = printDec(line, decTmp);
char off = cnt <= 4 ? 0 : cnt - 4;
vga_tmp += 4 - (cnt - off);
for (int i = off; i < cnt; i++, vga_tmp++)
*(uint8_t*)vga_tmp = (uint8_t)decTmp[i];
}
vga_text += 6;
screenLineOff = 6;
if (cursorLine == line && cursorLineOffset == lineOffset) {
((uint8_t*)vga_text)[1] = 0xf0;
}
continue;
}
*(uint8_t*)vga_text = c;
if (cursorLine == line && cursorLineOffset == lineOffset) {
((uint8_t*)vga_text)[1] = 0xf0;
}
lineOffset++;
vga_text++;
screenLineOff++;
if (screenLineOff == 80) { // last char
vga_text += 6;
screenLineOff = 6;
linesOnScreen--;
}
}
redraw = 0;
}
uint16_t key = get_scancode();
union V86Regs_t regs;
FARPTR v86_entry;
switch (key & 0xff) {
/*case KEY_DOWN: // down
if (currLine < lastLine && lineOffsets[currLine+1] < fileLen) {
currLine++;
redraw = 1;
}
break;
case KEY_UP: // up
if ((currLine > 0 && lineOffsets[currLine-1] < fileLen) || currLine == 1) {
currLine--;
redraw = 1;
}
break;*/
case KEY_DOWN:
cursorLine++;
cursorChange = 1;
break;
case KEY_UP:
if (cursorLine > 0) cursorLine--;
cursorChange = 1;
break;
case KEY_LEFT:
if (cursorLineOffset > 0) cursorLineOffset--;
cursorChange = 1;
break;
case KEY_RIGHT:
cursorLineOffset++;
cursorChange = 1;
break;
case KEY_PGDOWN:
if (currLine+(linesOnScreen/2) <= lastLine && lineOffsets[currLine+(linesOnScreen/2)] < fileLen) {
currLine += (linesOnScreen/2);
redraw = 1;
} else goto end;
break;
case KEY_PGUP:
if (currLine > (linesOnScreen/2) && lineOffsets[currLine-(linesOnScreen/2)] < fileLen) {
currLine -= (linesOnScreen/2);
redraw = 1;
} else if (currLine <= (linesOnScreen/2)) {
currLine = 0;
redraw = 1;
}
break;
case KEY_HOME: home:
if (currLine != 0) {
currLine = 0;
redraw = 1;
}
break;
case KEY_END: end:
if (currLine != lastLine) {
currLine = lastLine;
redraw = 1;
}
break;
case KEY_F1:
cont = 0;
break;
case KEY_F2:
if (screenSize != 80*25) {
SetVideo25Lines();
SetCursorDisabled();
screenSize = 80*25;
redraw = 1;
}
break;
case KEY_F5:
if (screenSize != 80*50) {
SetVideo50Lines();
SetCursorDisabled();
screenSize = 80*50;
redraw = 1;
}
break;
default:
break;
}
}
}

11
tss.c
View File

@ -1,5 +1,7 @@
#include <stdint.h> #include <stdint.h>
#include "tss.h"
struct __attribute__((__packed__)) tss_entry_struct { struct __attribute__((__packed__)) tss_entry_struct {
uint32_t prev_tss; uint32_t prev_tss;
uint32_t esp0; uint32_t esp0;
@ -31,12 +33,17 @@ struct __attribute__((__packed__)) tss_entry_struct {
}; };
struct tss_entry_struct *tss_data; struct tss_entry_struct *tss_data;
void write_tss() { void write_tss() {
tss_data = (struct tss_entry_struct *)0x20000; tss_data = (struct tss_entry_struct *)0x200000;
for (int i = 0; i < 0x2080; i++) for (int i = 0; i < 0x2080; i++)
((uint8_t*)tss_data)[i] = 0; ((uint8_t*)tss_data)[i] = 0;
tss_data->ss0 = 0x10; tss_data->ss0 = 0x10;
tss_data->esp0 = 0x400000; tss_data->esp0 = 0x320000;
tss_data->iomap_base = 0x80; tss_data->iomap_base = 0x80;
// not technically TSS but set up task pointer
uint32_t *current_task_ptr = (uint32_t*)(0x310000-4);
*current_task_ptr = 0x310000-(20*4); // each task is 12 dwords, plus 1 for pointer
/* TODO setup null recovery task at start */
} }
extern void flushTSS(); extern void flushTSS();

1
tss.h Normal file
View File

@ -0,0 +1 @@
void setup_tss();

71
usermode.nasm Normal file
View File

@ -0,0 +1,71 @@
[BITS 32]
[ORG 0x800000]
global user_test
user_test:
mov dword [0xb8000], 0x0f000f00 | 'U' | 's' << 16
mov dword [0xb8004], 0x0f000f00 | 'e' | 'r' << 16
mov dword [0xb8008], 0x0f000f00 | 'm' | 'o' << 16
mov dword [0xb800C], 0x0f000f00 | 'd' | 'e' << 16
mov dword [0xb8010], 0x0f000f00 | '!'
mov dword [0xb8000+160], 0x0f000f00 | 'F' | 'a' << 16
mov dword [0xb8004+160], 0x0f000f00 | 'u' | 'l' << 16
mov dword [0xb8008+160], 0x0f000f00 | 't' | 's' << 16
mov dword [0xb800C+160], 0x0f000f00 | ':' | ' ' << 16
mov dword [0xb8000+320], 0x0f000f00 | 'd' | ':' << 16
mov dword [0xb8004+320], 0x0f000f00 | ' ' | '#' << 16
mov dword [0xb8008+320], 0x0f000f00 | 'D' | 'I' << 16
mov dword [0xb800C+320], 0x0f000f00 | 'V' | ' ' << 16
mov dword [0xb8000+480], 0x0f000f00 | 'p' | ':' << 16
mov dword [0xb8004+480], 0x0f000f00 | ' ' | 'P' << 16
mov dword [0xb8008+480], 0x0f000f00 | 'G' | 'F' << 16
mov dword [0xb800C+480], 0x0f000f00 | 'L' | 'T' << 16
mov dword [0xb8000+640], 0x0f000f00 | 'u' | ':' << 16
mov dword [0xb8004+640], 0x0f000f00 | ' ' | '#' << 16
mov dword [0xb8008+640], 0x0f000f00 | 'U' | 'D' << 16
mov dword [0xb800C+640], 0x0f000f00 | '2' | ' ' << 16
mov dword [0xb8000+800], 0x0f000f00 | 'g' | ':' << 16
mov dword [0xb8004+800], 0x0f000f00 | ' ' | '#' << 16
mov dword [0xb8008+800], 0x0f000f00 | 'G' | 'P' << 16
mov dword [0xb800C+800], 0x0f000f00 | 'F' | ' ' << 16
mov eax, 0 ; command = 00h, get key
int 0x21 ; OS call
cmp al, 'd'
je .div_err
cmp al, 'p'
je .page_err
cmp al, 'u'
je .undefined_err
cmp al, 'g'
je .gpf_err
push 0x00000013 ; eax AH=0,AL=3 set video mode 3
push 0x00000000 ; ecx
push 0x00000000 ; edx
push 0x00000000 ; ebx
push 0x00000000 ; esi
push 0x00000000 ; edi
push 0x00000000 ; ebp
push esp ; regs
push 0x10 ; interrupt
mov eax, 0x86 ; command = 86h, virtual 8086 call
int 0x21 ; OS call
mov edi, 0xA0000
xor eax, eax
.loop:
mov ecx, 320
rep stosb
inc al
cmp eax, 200
jl .loop
mov eax, 0xA0000
int 0x30 ; Exit
.page_err:
mov edx, 0x105000 ; somewhere in kernel mem
mov edx, [edx] ; should page fault
.div_err:
xor ebx, ebx
div bl ; Unhandled DIV0 exception
.undefined_err:
ud2 ; undefined instruction
.gpf_err:
mov ax, 0x10
mov ds, ax ; load ds with bad value

166
v86.nasm
View File

@ -1,4 +1,87 @@
[BITS 16] [BITS 16]
[SECTION .v86]
global v86Interrupt
v86Interrupt:
int 0x00
int 0x30
ud2
global v86TransFlag
v86TransFlag:
push cs
pop es
mov ax, 0x13
int 0x10
mov ax,0x1012
xor bx,bx
mov cx,5
mov dx,.c
int 0x10
push 0xa000
pop es
xor di,di
xor ax,ax
.loop:
mov cx, 12800
rep stosb
inc ax
cmp ax,5
jl .loop
int 0x30
ud2
.c: db `\0263>=*.\?\?\?=*.\0263>`
global v86VideoInt
v86VideoInt:
int 0x10
int 0x30
ud2
global v86DiskOp
v86DiskOp:
xor bx, bx ; TODO fix assuming we're in first 64k
mov ds, bx
mov dl, 0x80 ; TODO get this from BIOS or something
mov si, v86disk_addr_packet ; ds:si
int 0x13
jc .err
int 0x30
.err:
ud2
global v86disk_addr_packet
v86disk_addr_packet:
db 0x10, 0x00 ; size, reserved
dw 0x1 ; blocks
dd 0x23000000 ; transfer buffer 0x23000
dq 0x1 ; start block
global v86DiskGetGeometry
v86DiskGetGeometry:
mov ah, 8
mov dl, 0x80
int 0x13
movzx eax, ch
shl eax, 16
mov al, cl
mov ah, dh
int 0x30
ud2
global v86DiskReadCHS
v86DiskReadCHS:
push 0x2000
pop es
int 0x13
int 0x30
ud2
global v86TextMode
v86TextMode:
mov ax, 0x3
int 0x10
int 0x30
ud2
real_hexprint: real_hexprint:
xor cx, cx xor cx, cx
mov bl, al mov bl, al
@ -26,58 +109,51 @@ call real_hexprint
mov ax, dx mov ax, dx
call real_hexprint call real_hexprint
ret ret
extern v86Code
v86Code: global v86Test
mov ax, 0xb814 v86Test:
mov ax, (0xB8000 + (80*2)) >> 4
mov es, ax mov es, ax
mov di, 20 mov di, 0
mov word es:[di+0], 0x1f00 | 'S'
mov word es:[di+2], 0x1f00 | 'S'
mov word es:[di+4], 0x1f00 | ':'
add di, 6
mov ax, ss
call real_printword
add di, 2
mov word es:[di+0], 0x1f00 | 'S'
mov word es:[di+2], 0x1f00 | 'P'
mov word es:[di+4], 0x1f00 | ':'
add di, 6
mov ax, sp mov ax, sp
call real_printword call real_printword
add di, 2 add di, 2
mov word es:[di+0], 0x1f00 | 'D'
mov word es:[di+2], 0x1f00 | 'S'
mov word es:[di+4], 0x1f00 | ':'
add di, 6
mov ax, ds mov ax, ds
call real_printword call real_printword
add di, 2 add di, 2
mov word es:[di+0], 0x1f00 | 'C'
mov word es:[di+2], 0x1f00 | 'S'
mov word es:[di+4], 0x1f00 | ':'
add di, 6
mov ax, cs mov ax, cs
call real_printword call real_printword
.loop: add di, 2
inc byte [0] mov ax, cs
;mov ax, 0x1111 mov ds, ax
;mov ds, ax mov si, .testStr
;mov ax, 0x2222 mov ah, 0x1f
;mov es, ax mov cx, .testStr_end - .testStr
;mov ax, 0x3333 .print:
;mov fs, ax lodsb
;mov ax, 0x4444 stosw
;mov gs, ax loop .print
;mov ax, 0x5555 int 3 ; wait for key
;mov ss, ax
;mov ax, 0x6666
;mov sp, ax
int 3
int 3
;jmp .loop
mov ax, 0x13
int 0x10
int 0x30 ; exit int 0x30 ; exit
jmp $ ud2
extern real_test .testStr: db "PRESS ANY KEY"
real_test: .testStr_end:
nop
nop
nop
jmp $
[BITS 32]
; extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
global enter_v86
enter_v86:
mov ebp, esp ; save stack pointer
mov dword [0x20004], ebp ; tss ESP0
push dword [ebp+4] ; ss
push dword [ebp+8] ; esp
pushfd ; eflags
or dword [esp], (1 << 17) ; set VM flags
;or dword [esp], (3 << 12) ; IOPL 3
push dword [ebp+12] ; cs
push dword [ebp+16] ; eip
iret

84
v86defs.h Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include <stdint.h>
// Labels of v8086 programs
// TODO Remove these and use
// a single define for location?
extern void v86Test();
extern void v86TransFlag();
extern void v86Interrupt();
extern void v86TextMode();
extern void v86DiskOp();
extern void v86DiskGetGeometry();
extern void v86DiskReadCHS();
union __attribute((__packed__)) V86Regs_t {
struct dword_regs {
uint32_t ebp;
uint32_t edi;
uint32_t esi;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
} d;
struct word_regs {
uint16_t bp, _upper_bp;
uint16_t di, _upper_di;
uint16_t si, _upper_si;
uint16_t bx, _upper_bx;
uint16_t dx, _upper_dx;
uint16_t cx, _upper_cx;
uint16_t ax, _upper_ax;
} w;
struct byte_regs {
uint32_t ebp;
uint32_t edi;
uint32_t esi;
uint8_t bl, bh;
uint16_t _upper_bx;
uint8_t dl, dh;
uint16_t _upper_dx;
uint8_t cl, ch;
uint16_t _upper_cx;
uint8_t al, ah;
uint16_t _upper_ax;
} h;
};
extern uint32_t enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip, union V86Regs_t *regs);
void V8086Int(uint8_t interrupt, union V86Regs_t *regs);
struct __attribute((__packed__)) Int13DiskPacket_t {
uint8_t size; // 0x10
uint8_t reserved; // 0x00
uint16_t blocks;
uint32_t transfer_buffer; // 0x2300:0000
uint64_t start_block;
};
extern struct Int13DiskPacket_t v86disk_addr_packet;
/* Real Mode helper macros */
/* segment:offset pair */
typedef uint32_t FARPTR;
/* Make a FARPTR from a segment and an offset */
#define MK_FP(seg, off) ((FARPTR) (((uint32_t) (seg) << 16) | (uint16_t) (off)))
/* Extract the segment part of a FARPTR */
#define FP_SEG(fp) (((FARPTR) fp) >> 16)
/* Extract the offset part of a FARPTR */
#define FP_OFF(fp) (((FARPTR) fp) & 0xffff)
/* Convert a segment:offset pair to a linear address */
#define FP_TO_LINEAR(seg, off) ((void*)(uintptr_t)((((uint32_t)seg) << 4) + ((uint32_t)off)))
#define EFLAG_IF ((uint32_t)1 << 9)
#define EFLAG_VM ((uint32_t)1 << 17)
FARPTR i386LinearToFp(void *ptr);
void ensure_v86env();

BIN
virtdisk.bin.ex Normal file

Binary file not shown.