Bootable kernel skeleton

This commit is contained in:
Jeremy Soller 2016-08-13 18:21:46 -06:00
parent 8f4aff05d5
commit d5902c5a20
27 changed files with 1702 additions and 3 deletions

View file

@ -1,3 +1,12 @@
[package] [package]
name = "kernel" name = "kernel"
version = "0.1.0" version = "0.1.0"
[lib]
crate-type = ["staticlib"]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"

24
Makefile Normal file
View file

@ -0,0 +1,24 @@
ARCH?=x86_64
run: qemu
bochs: build/harddrive.bin
bochs -f bochs.$(ARCH)
qemu: build/harddrive.bin
qemu-system-$(ARCH) -drive file=$<,format=raw,index=0,media=disk
FORCE:
build/libkernel.a: FORCE
rustc --crate-type staticlib src/lib.rs -o $@
#--target $(ARCH)-unknown-none.json
build/kernel.bin: build/libkernel.a
ld -m elf_$(ARCH) -o $@ -T bootloader/x86/kernel.ld -z max-page-size=0x1000 $<
build/harddrive.bin: build/kernel.bin
nasm -f bin -o $@ -D ARCH_$(ARCH) -ibootloader/x86/ -ibuild/ bootloader/x86/harddrive.asm
clean:
rm -rf build/*

14
bochs.x86_64 Normal file
View file

@ -0,0 +1,14 @@
ata0-master: type=disk, path="build/harddrive.bin", mode=flat
boot: disk
com1: enabled=1, mode=file, dev=build/serial.log
megs: 1024
magic_break: enabled=1
display_library: x, options="gui_debug"
log: -
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
debugger_log: -

View file

@ -0,0 +1,131 @@
ORG 0x7C00
SECTION .text
USE16
boot: ; dl comes with disk
; initialize segment registers
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
; initialize stack
mov sp, 0x7C00
mov [disk], dl
mov si, name
call print
call print_line
mov bh, 0
mov bl, [disk]
call print_num
call print_line
mov ax, (startup_start - boot) / 512
mov bx, startup_start
mov cx, (startup_end - startup_start) / 512
xor dx, dx
call load
mov si, finished
call print
call print_line
jmp startup
; load some sectors from disk to a buffer in memory
; buffer has to be below 1MiB
; IN
; ax: start sector
; bx: offset of buffer
; cx: number of sectors (512 Bytes each)
; dx: segment of buffer
; CLOBBER
; ax, bx, cx, dx, si
; TODO rewrite to (eventually) move larger parts at once
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
load:
cmp cx, 64
jbe .good_size
pusha
mov cx, 64
call load
popa
add ax, 64
add dx, 64 * 512 / 16
sub cx, 64
jmp load
.good_size:
mov [DAPACK.addr], ax
mov [DAPACK.buf], bx
mov [DAPACK.count], cx
mov [DAPACK.seg], dx
mov si, loading
call print
mov bx, [DAPACK.addr]
call print_num
mov al, '#'
call print_char
mov bx, [DAPACK.count]
call print_num
mov al, ' '
call print_char
mov bx, [DAPACK.seg]
call print_num
mov al, ':'
call print_char
mov bx, [DAPACK.buf]
call print_num
call print_line
mov dl, [disk]
mov si, DAPACK
mov ah, 0x42
int 0x13
jc error
ret
error:
mov si, errored
call print
call print_line
.halt:
cli
hlt
jmp .halt
%include "print16.asm"
name: db "Redox Loader",0
loading: db "Load ",0
errored: db "Could not read disk",0
finished: db "Finished Loading",0
line: db 13,10,0
disk: db 0
DAPACK:
db 0x10
db 0
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
.buf: dw 0 ; memory buffer destination address (0:7c00)
.seg: dw 0 ; in memory page zero
.addr: dd 0 ; put the lba to read in this spot
dd 0 ; more storage bytes only for big lba's ( > 4 bytes )
times 510-($-$$) db 0
db 0x55
db 0xaa

View file

@ -0,0 +1,46 @@
attrib:
.present equ 1 << 7
.ring1 equ 1 << 5
.ring2 equ 1 << 6
.ring3 equ 1 << 5 | 1 << 6
.user equ 1 << 4
;user
.code equ 1 << 3
; code
.conforming equ 1 << 2
.readable equ 1 << 1
; data
.expand_down equ 1 << 2
.writable equ 1 << 1
.accessed equ 1 << 0
;system
; legacy
.tssAvailabe16 equ 0x1
.ldt equ 0x2
.tssBusy16 equ 0x3
.call16 equ 0x4
.task equ 0x5
.interrupt16 equ 0x6
.trap16 equ 0x7
.tssAvailabe32 equ 0x9
.tssBusy32 equ 0xB
.call32 equ 0xC
.interrupt32 equ 0xE
.trap32 equ 0xF
; long mode
.ldt32 equ 0x2
.tssAvailabe64 equ 0x9
.tssBusy64 equ 0xB
.call64 equ 0xC
.interrupt64 equ 0xE
.trap64 equ 0xF
flags:
.granularity equ 1 << 7
.available equ 1 << 4
;user
.default_operand_size equ 1 << 6
; code
.long_mode equ 1 << 5
; data
.reserved equ 1 << 5

View file

@ -0,0 +1,8 @@
struc GDTEntry
.limitl resw 1
.basel resw 1
.basem resb 1
.attribute resb 1
.flags__limith resb 1
.baseh resb 1
endstruc

View file

@ -0,0 +1,21 @@
%include "bootsector.asm"
startup_start:
%ifdef ARCH_i386
%include "startup-i386.asm"
%endif
%ifdef ARCH_x86_64
%include "startup-x86_64.asm"
%endif
align 512, db 0
startup_end:
kernel_file:
incbin "kernel.bin"
align 512, db 0
.end:
.length equ kernel_file.end - kernel_file
.length_sectors equ .length / 512
times 1024*1024-($-$$) db 0

View file

@ -0,0 +1,78 @@
SECTION .text
USE16
initialize:
.fpu: ;enable fpu
mov eax, cr0
and al, 11110011b
or al, 00100010b
mov cr0, eax
mov eax, cr4
or eax, 0x200
mov cr4, eax
fninit
ret
.sse: ;enable sse
mov eax, cr4
or ax, 0000011000000000b
mov cr4, eax
ret
;PIT Frequency
;If using nanoseconds, to minimize drift, one should find a frequency as close to an integer nanosecond value in wavelength
;Divider Hz Nanoseconds Properties
;2685 444.38795779019242706393 2250286.00003631746492922946 Best For Context Switching
;5370 222.19397889509621353196 4500572.00007263492985856020
;21029 56.73981961418358774390 17624306.99991199998882825455
;23714 50.31549576902532962244 19874592.99994831745375667118
;26399 45.19798729749864262535 22124878.99998463491868476373
;29084 41.02536331545408701233 24375165.00002095238361424615
;31769 37.55804925136663623868 26625451.00005726984854313455
;34454 34.63115071302799868423 28875737.00009358731347639618
;50113 23.80982313305263437963 41999471.99993295237244784676
;52798 22.59899364874932131267 44249757.99996926983737931766
;55483 21.50535599492937776736 46500044.00000558730230583335 Lowest Drift
;58168 20.51268165772704350616 48750330.00004190476724037528
;60853 19.60760630809765610021 51000616.00007822223218031738
.pit:
;initialize the PIT
mov ax, 5370 ;this is the divider for the PIT
out 0x40, al
rol ax, 8
out 0x40, al
;DISABLED ;enable rtc interrupt
;mov al, 0xB
;out 0x70, al
;rol ax, 8
;in al, 0x71
;rol ax, 8
;out 0x70, al
;rol ax, 8
;or al, 0x40
;out 0x71, al
ret
.pic: ;sets up IRQs at int 20-2F
mov al, 0x11
out 0x20, al
out 0xA0, al
mov al, 0x20 ;IRQ0 vector
out 0x21, al
mov al, 0x28 ;IRQ8 vector
out 0xA1, al
mov al, 4
out 0x21, al
mov al, 2
out 0xA1, al
mov al, 1
out 0x21, al
out 0xA1, al
xor al, al ;no IRQ masks
out 0x21, al
out 0xA1, al
mov al, 0x20 ;reset PIC's
out 0xA0, al
out 0x20, al
ret

View file

@ -0,0 +1,105 @@
struc IDTEntry
.offsetl resw 1
.selector resw 1
.zero resb 1
.attribute resb 1
.offseth resw 1
endstruc
SECTION .text
USE32
interrupts:
.first:
mov [.entry], byte 0
jmp dword .handle
.second:
%assign i 1
%rep 255
mov [.entry], byte i
jmp dword .handle
%assign i i+1
%endrep
.handle:
push ebp
push esi
push edi
push edx
push ecx
push ebx
push eax
push esp
push dword [.entry]
mov eax, gdt.kernel_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
call dword [.handler]
mov eax, gdt.user_data | 3
mov ds, eax
mov es, eax
mov fs, eax
mov eax, gdt.user_tls | 3
mov gs, eax
add esp, 8 ; Skip interrupt code and reg pointer
pop eax
pop ebx
pop ecx
pop edx
pop edi
pop esi
pop ebp
iretd
.handler: dd 0
.entry: dd 0
idtr:
dw (idt.end - idt) + 1
dd idt
idt:
%assign i 0
;Below system call
%rep 128
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.zero, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt32
at IDTEntry.offseth, dw 0
iend
%assign i i+1
%endrep
;System call
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.zero, db 0
at IDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.interrupt32
at IDTEntry.offseth, dw 0
iend
%assign i i+1
;Above system call
%rep 127
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.zero, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt32
at IDTEntry.offseth, dw 0
iend
%assign i i+1
%endrep
.end:

View file

@ -0,0 +1,130 @@
struc IDTEntry
.offsetl resw 1
.selector resw 1
.ist resb 1
.attribute resb 1
.offsetm resw 1
.offseth resd 1
.reserved resd 1
endstruc
SECTION .text
USE64
interrupts:
.first:
mov [.entry], byte 0
jmp qword .handle
.second:
%assign i 1
%rep 255
mov [.entry], byte i
jmp qword .handle
%assign i i+1
%endrep
.handle:
push rbp
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push rsi
push rdi
push rdx
push rcx
push rbx
push rax
mov rsi, rsp
push rsi
mov rdi, qword [.entry]
push rdi
mov rax, gdt.kernel_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
call qword [.handler]
mov rax, gdt.user_data | 3
mov ds, rax
mov es, rax
mov gs, rax
mov rax, gdt.user_tls | 3
mov fs, rax
add rsp, 16 ; Skip interrupt code and reg pointer
pop rax
pop rbx
pop rcx
pop rdx
pop rdi
pop rsi
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
pop rbp
iretq
.handler: dq 0
.entry: dq 0
idtr:
dw (idt.end - idt) + 1
dq idt
idt:
%assign i 0
;Below syscall
%rep 128
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.ist, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt64
at IDTEntry.offsetm, dw 0
at IDTEntry.offseth, dd 0
at IDTEntry.reserved, dd 0
iend
%assign i i+1
%endrep
;Syscall
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.ist, db 0
at IDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.interrupt64
at IDTEntry.offsetm, dw 0
at IDTEntry.offseth, dd 0
at IDTEntry.reserved, dd 0
iend
%assign i i+1
;Above syscall
%rep 127
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.ist, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt64
at IDTEntry.offsetm, dw 0
at IDTEntry.offseth, dd 0
at IDTEntry.reserved, dd 0
iend
%assign i i+1
%endrep
.end:

40
bootloader/x86/kernel.ld Normal file
View file

@ -0,0 +1,40 @@
ENTRY(kmain)
SECTIONS {
kernel_base = 0x101000;
. = kernel_base;
.text : AT(ADDR(.text) - kernel_base) {
__text_start = .;
*(.text*)
. = ALIGN(4096);
__text_end = .;
}
.rodata : AT(ADDR(.rodata) - kernel_base) {
__rodata_start = .;
*(.rodata*)
. = ALIGN(4096);
__rodata_end = .;
}
.data : AT(ADDR(.data) - kernel_base) {
__data_start = .;
*(.data*)
. = ALIGN(4096);
__data_end = .;
}
.bss : AT(ADDR(.bss) - kernel_base) {
__bss_start = .;
*(.bss*)
. = ALIGN(4096);
__bss_end = .;
}
/DISCARD/ : {
*(.comment)
*(.eh_frame)
*(.rel.eh_frame)
}
}

View file

@ -0,0 +1,32 @@
SECTION .text
USE16
;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader)
memory_map:
.start equ 0x0500
.end equ 0x5000
.length equ .end - .start
xor eax, eax
mov di, .start
mov ecx, .length / 4 ; moving 4 Bytes at once
cld
rep stosd
mov di, .start
mov edx, 0x534D4150
xor ebx, ebx
.lp:
mov eax, 0xE820
mov ecx, 24
int 0x15
jc .done ; Error or finished
cmp ebx, 0
je .done ; Finished
add di, 24
cmp di, .end
jb .lp ; Still have buffer space
.done:
ret

View file

@ -0,0 +1,66 @@
SECTION .text
USE16
; provide function for printing in x86 real mode
; a newline
newline: db 0xD, 0xA, 0
; print a string and a newline
; IN
; si: points at zero-terminated String
; CLOBBER
; ax
print_line:
mov si, newline
call print
ret
; print a string
; IN
; si: points at zero-terminated String
; CLOBBER
; ax
print:
lodsb
test al, al
jz .done
call print_char
jmp print
.done:
ret
; print a character
; IN
; al: character to print
; CLOBBER
; ah
print_char:
mov ah, 0x0e
int 0x10
ret
; print a number in hex
; IN
; bx: the number
; CLOBBER
; cx, ax
print_num:
mov cx, 4
.lp:
mov al, bh
shr al, 4
cmp al, 0xA
jb .below_0xA
add al, 'A' - 0xA - '0'
.below_0xA:
add al, '0'
call print_char
shl bx, 4
loop .lp
ret

View file

@ -0,0 +1,89 @@
SECTION .text
USE16
startup:
; enable A20-Line via IO-Port 92, might not work on all motherboards
in al, 0x92
or al, 2
out 0x92, al
; loading kernel to 1MiB
; move part of kernel to startup_end via bootsector#load and then copy it up
; repeat until all of the kernel is loaded
; buffersize in multiple of sectors (512 Bytes)
; min 1
; max (0x70000 - startup_end) / 512
buffer_size_sectors equ 1
; buffer size in Bytes
buffer_size_bytes equ buffer_size_sectors * 512
kernel_base equ 0x100000
; how often do we need to call load and move memory
mov ecx, kernel_file.length_sectors / buffer_size_sectors
mov ax, (kernel_file - boot) / 512
mov edi, kernel_base
cld
.lp:
; saving counter
push cx
; populating buffer
mov cx, buffer_size_sectors
mov bx, startup_end
mov dx, 0x0
push ax
call load
; moving buffer
call unreal
pop ax
mov esi, startup_end
mov ecx, buffer_size_bytes / 4
a32 rep movsd
; preparing next iteration
add ax, buffer_size_sectors
pop cx
loop .lp
; load the part of the kernel that does not fill the buffer completely
mov cx, kernel_file.length_sectors % buffer_size_sectors
test cx, cx
jz finished_loading ; if cx = 0 => skip
mov bx, startup_end
mov dx, 0x0
call load
; moving remnants of kernel
call unreal
mov esi, startup_end
mov ecx, (kernel_file.length_sectors % buffer_size_bytes) / 4
a32 rep movsd
finished_loading:
call memory_map
call vesa
call initialize.fpu
call initialize.sse
call initialize.pit
call initialize.pic
jmp startup_arch
%include "descriptor_flags.inc"
%include "gdt_entry.inc"
%include "unreal.asm"
%include "memory_map.asm"
%include "vesa.asm"
%include "initialize.asm"

View file

@ -0,0 +1,150 @@
%include "startup-common.asm"
startup_arch:
; load protected mode GDT and IDT
cli
lgdt [gdtr]
lidt [idtr]
; set protected mode bit of cr0
mov eax, cr0
or eax, 1
mov cr0, eax
; far jump to load CS with 32 bit segment
jmp gdt.kernel_code:protected_mode
USE32
protected_mode:
; load all the other segments with 32 bit data segments
mov eax, gdt.kernel_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
mov esp, 0x800000 - 128
mov eax, gdt.tss
ltr ax
;rust init
mov eax, [kernel_base + 0x18]
mov [interrupts.handler], eax
mov eax, gdtr
mov ebx, idtr
mov ecx, tss
int 255
.lp:
sti
hlt
jmp .lp
gdtr:
dw gdt.end + 1 ; size
dd gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.kernel_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code | attrib.readable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.kernel_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.user_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.code | attrib.readable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.user_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.user_tls equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.tss equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw (tss.end - tss) & 0xFFFF
at GDTEntry.basel, dw (tss-$$+0x7C00) & 0xFFFF
at GDTEntry.basem, db ((tss-$$+0x7C00) >> 16) & 0xFF
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.tssAvailabe32
at GDTEntry.flags__limith, db ((tss.end - tss) >> 16) & 0xF
at GDTEntry.baseh, db ((tss-$$+0x7C00) >> 24) & 0xFF
iend
.end equ $ - gdt
struc TSS
.prev_tss resd 1 ;The previous TSS - if we used hardware task switching this would form a linked list.
.esp0 resd 1 ;The stack pointer to load when we change to kernel mode.
.ss0 resd 1 ;The stack segment to load when we change to kernel mode.
.esp1 resd 1 ;everything below here is unused now..
.ss1 resd 1
.esp2 resd 1
.ss2 resd 1
.cr3 resd 1
.eip resd 1
.eflags resd 1
.eax resd 1
.ecx resd 1
.edx resd 1
.ebx resd 1
.esp resd 1
.ebp resd 1
.esi resd 1
.edi resd 1
.es resd 1
.cs resd 1
.ss resd 1
.ds resd 1
.fs resd 1
.gs resd 1
.ldt resd 1
.trap resw 1
.iomap_base resw 1
endstruc
tss:
istruc TSS
at TSS.esp0, dd 0x800000 - 128
at TSS.ss0, dd gdt.kernel_data
at TSS.iomap_base, dw 0xFFFF
iend
.end:
%include "interrupts-i386.asm"

View file

@ -0,0 +1,191 @@
%include "startup-common.asm"
startup_arch:
cli
; setting up Page Tables
; Identity Mapping first GB
mov ax, 0x7000
mov es, ax
xor edi, edi
xor eax, eax
mov ecx, 3 * 4096 / 4 ;PML4, PDP, PD / moves 4 Bytes at once
cld
rep stosd
xor edi, edi
;Link first PML4 to PDP
mov DWORD [es:edi], 0x71000 | 1 << 1 | 1
add edi, 0x1000
;Link first PDP to PD
mov DWORD [es:edi], 0x72000 | 1 << 1 | 1
add edi, 0x1000
;Link all PD's (512 per PDP, 2MB each)y
mov ebx, 1 << 7 | 1 << 1 | 1
mov ecx, 512
.setpd:
mov [es:edi], ebx
add ebx, 0x200000
add edi, 8
loop .setpd
xor ax, ax
mov es, ax
;cr3 holds pointer to PML4
mov edi, 0x70000
mov cr3, edi
;enable Page Address Extension and Page Size Extension
mov eax, cr4
or eax, 1 << 5 | 1 << 4
mov cr4, eax
; load protected mode GDT
lgdt [gdtr]
mov ecx, 0xC0000080 ; Read from the EFER MSR.
rdmsr
or eax, 0x00000100 ; Set the Long-Mode-Enable bit.
wrmsr
;enabling paging and protection simultaneously
mov ebx, cr0
or ebx, 0x80000001 ;Bit 31: Paging, Bit 0: Protected Mode
mov cr0, ebx
; far jump to enable Long Mode and load CS with 64 bit segment
jmp gdt.kernel_code:long_mode
USE64
long_mode:
; load all the other segments with 64 bit data segments
mov rax, gdt.kernel_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
mov ss, rax
; load long mode IDT
lidt [idtr]
mov rsp, 0x800000 - 128
mov rax, gdt.tss
ltr ax
;rust init
mov eax, [kernel_base + 0x18]
mov [interrupts.handler], rax
mov rax, gdtr
mov rbx, idtr
mov rcx, tss
int 0xFF
.lp:
sti
hlt
jmp .lp
gdtr:
dw gdt.end + 1 ; size
dq gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.kernel_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code
at GDTEntry.flags__limith, db flags.long_mode
at GDTEntry.baseh, db 0
iend
.kernel_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0
at GDTEntry.baseh, db 0
iend
.user_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.code
at GDTEntry.flags__limith, db flags.long_mode
at GDTEntry.baseh, db 0
iend
.user_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0
at GDTEntry.baseh, db 0
iend
.user_tls equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0
at GDTEntry.baseh, db 0
iend
.tss equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw (tss.end - tss) & 0xFFFF
at GDTEntry.basel, dw (tss-$$+0x7C00) & 0xFFFF
at GDTEntry.basem, db ((tss-$$+0x7C00) >> 16) & 0xFF
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.tssAvailabe64
at GDTEntry.flags__limith, db ((tss.end - tss) >> 16) & 0xF
at GDTEntry.baseh, db ((tss-$$+0x7C00) >> 24) & 0xFF
iend
dq 0 ;tss descriptors are extended to 16 Bytes
.end equ $ - gdt
struc TSS
.reserved1 resd 1 ;The previous TSS - if we used hardware task switching this would form a linked list.
.rsp0 resq 1 ;The stack pointer to load when we change to kernel mode.
.rsp1 resq 1 ;everything below here is unused now..
.rsp2 resq 1
.reserved2 resd 1
.reserved3 resd 1
.ist1 resq 1
.ist2 resq 1
.ist3 resq 1
.ist4 resq 1
.ist5 resq 1
.ist6 resq 1
.ist7 resq 1
.reserved4 resd 1
.reserved5 resd 1
.reserved6 resw 1
.iomap_base resw 1
endstruc
tss:
istruc TSS
at TSS.rsp0, dd 0x800000 - 128
at TSS.iomap_base, dw 0xFFFF
iend
.end:
%include "interrupts-x86_64.asm"

54
bootloader/x86/unreal.asm Normal file
View file

@ -0,0 +1,54 @@
SECTION .text
USE16
; switch to unreal mode; ds and es can address up to 4GiB
unreal:
cli
lgdt [unreal_gdtr]
push es
push ds
mov eax, cr0 ; switch to pmode by
or al,1 ; set pmode bit
mov cr0, eax
jmp $+2
; http://wiki.osdev.org/Babystep7
; When this register given a "selector", a "segment descriptor cache register"
; is filled with the descriptor values, including the size (or limit). After
; the switch back to real mode, these values are not modified, regardless of
; what value is in the 16-bit segment register. So the 64k limit is no longer
; valid and 32-bit offsets can be used with the real-mode addressing rules
mov bx, unreal_gdt.data
mov es, bx
mov ds, bx
and al,0xFE ; back to realmode
mov cr0, eax ; by toggling bit again
pop ds
pop es
sti
ret
unreal_gdtr:
dw unreal_gdt.end + 1 ; size
dd unreal_gdt ; offset
unreal_gdt:
.null equ $ - unreal_gdt
dq 0
.data equ $ - unreal_gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0x0
at GDTEntry.basem, db 0x0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0x0
iend
.end equ $ - unreal_gdt

304
bootloader/x86/vesa.asm Normal file
View file

@ -0,0 +1,304 @@
%include "vesa.inc"
SECTION .text
USE16
vesa:
.getcardinfo:
mov ax, 0x4F00
mov di, VBECardInfo
int 0x10
cmp ax, 0x4F
je .edid
mov eax, 1
ret
.edid:
cmp dword [.required], 0 ;if both required x and required y are set, forget this
jne near .findmode
mov ax, 0x4F15
mov bx, 1
xor cx, cx
xor dx, dx
mov di, VBEEDID
int 0x10
cmp ax, 0x4F
jne near .noedid
xor di, di
.lp:
xor cx, cx
mov cl, [di+VBEEDID.standardtiming]
shl cx, 3
add cx, 248
push ecx
call decshowrm
mov al, 'x'
call charrm
pop ecx
mov bx, cx
inc di
mov al, [di+VBEEDID.standardtiming]
and al, 11000000b
cmp al, VBEEDID.aspect.4.3
jne .not43
mov ax, 3
mul cx
mov cx, ax
shr cx, 2
jmp .gotres
.not43:
cmp al, VBEEDID.aspect.5.4
jne .not54
shl cx, 2
mov ax, cx
mov cx, 5
xor dx, dx
div cx
mov cx, ax
jmp .gotres
.not54:
cmp al, VBEEDID.aspect.16.10
jne .not1610
mov ax, 10
mul cx
mov cx, ax
shr cx, 4
jmp .gotres
.not1610:
mov ax, 9
mul cx
mov cx, ax
shr cx, 4
.gotres:
call decshowrm
mov si, .edidmsg
call printrm
inc di
cmp di, 8
jb .lp
jmp .findmode
.noedid:
mov si, .noedidmsg
call printrm
jmp .findmode
.resetlist:
;if needed, reset mins/maxes/stuff
xor cx, cx
mov [.minx], cx
mov [.miny], cx
mov [.requiredx], cx
mov [.requiredy], cx
mov [.requiredmode], cx
.findmode:
mov si, [VBECardInfo.videomodeptr]
mov ax, [VBECardInfo.videomodeptr+2]
mov fs, ax
sub si, 2
mov cx, [.requiredmode]
test cx, cx
jnz .getmodeinfo
.searchmodes:
add si, 2
mov cx, [fs:si]
cmp cx, 0xFFFF
jne .getmodeinfo
cmp word [.goodmode], 0
je .resetlist
jmp .findmode
.getmodeinfo:
push esi
mov [.currentmode], cx
mov ax, 0x4F01
mov di, VBEModeInfo
int 0x10
pop esi
cmp ax, 0x4F
je .foundmode
mov eax, 1
ret
.foundmode:
;check minimum values, really not minimums from an OS perspective but ugly for users
cmp byte [VBEModeInfo.bitsperpixel], 32
jb .searchmodes
.testx:
mov cx, [VBEModeInfo.xresolution]
cmp word [.requiredx], 0
je .notrequiredx
cmp cx, [.requiredx]
je .testy
jmp .searchmodes
.notrequiredx:
cmp cx, [.minx]
jb .searchmodes
.testy:
mov cx, [VBEModeInfo.yresolution]
cmp word [.requiredy], 0
je .notrequiredy
cmp cx, [.requiredy]
jne .searchmodes ;as if there weren't enough warnings, USE WITH CAUTION
cmp word [.requiredx], 0
jnz .setmode
jmp .testgood
.notrequiredy:
cmp cx, [.miny]
jb .searchmodes
.testgood:
mov cx, [.currentmode]
mov [.goodmode], cx
push esi
call decshowrm
mov al, ':'
call charrm
mov cx, [VBEModeInfo.xresolution]
call decshowrm
mov al, 'x'
call charrm
mov cx, [VBEModeInfo.yresolution]
call decshowrm
mov al, '@'
call charrm
xor ch, ch
mov cl, [VBEModeInfo.bitsperpixel]
call decshowrm
mov si, .modeok
call printrm
xor ax, ax
int 0x16
pop esi
cmp al, 'y'
jne .searchmodes
.setmode:
mov bx, [.currentmode]
cmp bx, 0
je .nomode
or bx, 0x4000
mov ax, 0x4F02
int 0x10
.nomode:
cmp ax, 0x4F
je .returngood
mov eax, 1
ret
.returngood:
xor eax, eax
ret
.minx dw 640
.miny dw 480
.required:
.requiredx dw 0 ;1024 ;USE THESE WITH CAUTION
.requiredy dw 0 ;768
.requiredmode dw 0
.noedidmsg db "EDID not supported.",10,13,0
.edidmsg db " is supported.",10,13,0
.modeok db ": Is this OK?(y/n)",10,13,0
.goodmode dw 0
.currentmode dw 0
;useful functions
decshowrm:
mov si, .number
.clear:
mov al, "0"
mov [si], al
inc si
cmp si, .numberend
jb .clear
dec si
call convertrm
mov si, .number
.lp:
lodsb
cmp si, .numberend
jae .end
cmp al, "0"
jbe .lp
.end:
dec si
call printrm
ret
.number times 7 db 0
.numberend db 0
convertrm:
dec si
mov bx, si ;place to convert into must be in si, number to convert must be in cx
.cnvrt:
mov si, bx
sub si, 4
.ten4: inc si
cmp cx, 10000
jb .ten3
sub cx, 10000
inc byte [si]
jmp .cnvrt
.ten3: inc si
cmp cx, 1000
jb .ten2
sub cx, 1000
inc byte [si]
jmp .cnvrt
.ten2: inc si
cmp cx, 100
jb .ten1
sub cx, 100
inc byte [si]
jmp .cnvrt
.ten1: inc si
cmp cx, 10
jb .ten0
sub cx, 10
inc byte [si]
jmp .cnvrt
.ten0: inc si
cmp cx, 1
jb .return
sub cx, 1
inc byte [si]
jmp .cnvrt
.return:
ret
printrm:
mov al, [si]
test al, al
jz .return
call charrm
inc si
jmp printrm
.return:
ret
charrm: ;char must be in al
mov bx, 7
mov ah, 0xE
int 10h
ret
; .bestmode: ;preference is width > height > color
; mov bx, [VBEModeInfo.xresolution]
; cmp bx, [.width]
; ja .switchmode
; jb .searchmodes
; mov bx, [VBEModeInfo.yresolution]
; cmp bx, [.height]
; ja .switchmode
; jb .searchmodes
; mov bl, [VBEModeInfo.bitsperpixel]
; cmp bl, [.color]
; jb .searchmodes
; .switchmode:
; mov cx, [.currentmode]
; mov [.mode], cx
; mov bx, [VBEModeInfo.xresolution]
; mov [.width], bx
; mov bx, [VBEModeInfo.yresolution]
; mov [.height], bx
; mov bl, [VBEModeInfo.bitsperpixel]
; mov [.color], bl
; jmp .searchmodes
; .mode dw 0
; .color db 0
; .height dw 0
; .width dw 0

90
bootloader/x86/vesa.inc Normal file
View file

@ -0,0 +1,90 @@
ABSOLUTE 0x5000
VBECardInfo:
.signature resb 4
.version resw 1
.oemstring resd 1
.capabilities resd 1
.videomodeptr resd 1
.totalmemory resw 1
.oemsoftwarerev resw 1
.oemvendornameptr resd 1
.oemproductnameptr resd 1
.oemproductrevptr resd 1
.reserved resb 222
.oemdata resb 256
ABSOLUTE 0x5200
VBEModeInfo:
.attributes resw 1
.winA resb 1
.winB resb 1
.granularity resw 1
.winsize resw 1
.segmentA resw 1
.segmentB resw 1
.winfuncptr resd 1
.bytesperscanline resw 1
.xresolution resw 1
.yresolution resw 1
.xcharsize resb 1
.ycharsize resb 1
.numberofplanes resb 1
.bitsperpixel resb 1
.numberofbanks resb 1
.memorymodel resb 1
.banksize resb 1
.numberofimagepages resb 1
.unused resb 1
.redmasksize resb 1
.redfieldposition resb 1
.greenmasksize resb 1
.greenfieldposition resb 1
.bluemasksize resb 1
.bluefieldposition resb 1
.rsvdmasksize resb 1
.rsvdfieldposition resb 1
.directcolormodeinfo resb 1
.physbaseptr resd 1
.offscreenmemoryoffset resd 1
.offscreenmemsize resw 1
.reserved resb 206
VBE.ModeAttributes:
.available equ 1 << 0
.bios equ 1 << 2
.color equ 1 << 3
.graphics equ 1 << 4
.vgacompatible equ 1 << 5
.notbankable equ 1 << 6
.linearframebuffer equ 1 << 7
ABSOLUTE 0x5400
VBEEDID:
.header resb 8
.manufacturer resw 1
.productid resw 1
.serial resd 1
.manufactureweek resb 1
.manufactureyear resb 1
.version resb 1
.revision resb 1
.input resb 1
.horizontalsize resb 1
.verticalsize resb 1
.gamma resb 1
.displaytype resb 1
.chromaticity resb 10
.timingI resb 1
.timingII resb 1
.timingreserved resb 1
.standardtiming: resw 8 ;format: db (horizontal-248)/8, aspectratio | verticalfrequency - 60
.aspect.16.10 equ 0 ;mul horizontal by 10, shr 4 to get vertical resolution
.aspect.4.3 equ 1 << 6 ;mul horizontal by 3, shr 2 to get vertical resolution
.aspect.5.4 equ 2 << 6 ;shl horizontal by 2, div by 5 to get vertical resolution
.aspect.16.9 equ 3 << 6 ;mul horizontal by 9, shr by 4 to get vertical resolution
.descriptorblock1 resb 18
.descriptorblock2 resb 18
.descriptorblock3 resb 18
.descriptorblock4 resb 18
.extensionflag resb 1
.checksum resb 1

4
src/arch/mod.rs Normal file
View file

@ -0,0 +1,4 @@
//! Architecture specific items
#[cfg(target_arch = "x86_64")]
pub mod x86_64;

8
src/arch/x86_64/irq.rs Normal file
View file

@ -0,0 +1,8 @@
//! # IRQ handling
//! This module defines IRQ handling functions. These functions should all be #[naked],
//! unsafe, extern, and end in `iretq`
#[naked]
pub unsafe extern fn irq() {
}

11
src/arch/x86_64/main.rs Normal file
View file

@ -0,0 +1,11 @@
/// This function is where the kernel sets up IRQ handlers
/// It is increcibly unsafe, and should be minimal in nature
/// It must create the IDT with the correct entries, those entries are
/// defined in other files inside of the `arch` module
#[naked]
#[no_mangle]
pub unsafe extern fn kmain() {
asm!("xchg bx, bx" : : : : "intel", "volatile");
loop{}
}

5
src/arch/x86_64/mod.rs Normal file
View file

@ -0,0 +1,5 @@
/// IRQ Handling
pub mod irq;
/// Initialization and main function
pub mod main;

View file

@ -11,31 +11,37 @@
//! `open(path: &str, flags: usize) -> Result<file_descriptor: usize>` //! `open(path: &str, flags: usize) -> Result<file_descriptor: usize>`
//! //!
//! Open a file, providing a path as a `&str` and flags, defined elsewhere. //! Open a file, providing a path as a `&str` and flags, defined elsewhere.
//!
//! Returns a number, known as a file descriptor, that is passed to other syscalls //! Returns a number, known as a file descriptor, that is passed to other syscalls
//! //!
//! ### Close //! ### Close
//! `close(file_descriptor: usize) -> Result<()>` //! `close(file_descriptor: usize) -> Result<()>`
//! //!
//! Close a file descriptor, providing the file descriptor from `open` //! Close a file descriptor, providing the file descriptor from `open`
//!
//! Returns an error, `EBADF`, if the file descriptor was not found. //! Returns an error, `EBADF`, if the file descriptor was not found.
//!
//! This potential error is often ignored by userspace //! This potential error is often ignored by userspace
//! //!
//! ### Duplicate //! ### Duplicate
//! `dup(file_descriptor: usize) -> Result<file_descriptor: usize>` //! `dup(file_descriptor: usize) -> Result<file_descriptor: usize>`
//! //!
//! Duplicate a file descriptor, providing the file descriptor from `open` //! Duplicate a file descriptor, providing the file descriptor from `open`
//!
//! Returns a new file descriptor, or an error //! Returns a new file descriptor, or an error
//! //!
//! ### Read //! ### Read
//! `read(file_descriptor: usize, buffer: &mut [u8]) -> Result<count: usize>` //! `read(file_descriptor: usize, buffer: &mut [u8]) -> Result<count: usize>`
//! //!
//! Read from a file descriptor, providing the file descriptor from `open` and a mutable buffer //! Read from a file descriptor, providing the file descriptor from `open` and a mutable buffer
//!
//! Returns the number of bytes actually read, or an error //! Returns the number of bytes actually read, or an error
//! //!
//! ### Write //! ### Write
//! `write(file_descriptor: usize, buffer: &[u8]) -> Result<count: usize>` //! `write(file_descriptor: usize, buffer: &[u8]) -> Result<count: usize>`
//! //!
//! Write to a file descriptor, providing the file descriptor from `open` and a const buffer //! Write to a file descriptor, providing the file descriptor from `open` and a const buffer
//!
//! Returns the number of bytes actually written, or an error //! Returns the number of bytes actually written, or an error
//! //!
//! ### Stat //! ### Stat
@ -43,12 +49,34 @@
//! //!
//! Get information from a file descriptor, providing the file descriptor from `open` //! Get information from a file descriptor, providing the file descriptor from `open`
//! and a mutable Stat struct, defined elsewhere. //! and a mutable Stat struct, defined elsewhere.
//!
//! Returns an error if the operation failed //! Returns an error if the operation failed
//! //!
//! ### Path //! ### Path
//! `fpath(file_descriptor: usize, buffer: &mut [u8]) -> Result<count: usize>` //! `fpath(file_descriptor: usize, buffer: &mut [u8]) -> Result<count: usize>`
//! //!
//! Read the path of a file descriptor, providing the file descriptor from `open` //! Read the path of a file descriptor, providing the file descriptor from `open` and
//! and a mutable buffer. The buffer should be 4096 bytes, to ensure that the //! a mutable buffer.
//! entire path will fit. //!
//! Returns the number of bytes actually read, or an error //! Returns the number of bytes actually read, or an error
//!
//! The buffer should be 4096 bytes, to ensure that the entire path will fit.
//! An error will be returned, `ENOBUFS`, if the buffer is not long enough for the name.
//! In this case, it is recommended to add one page, 4096 bytes, to the buffer and retry.
#![feature(asm)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![no_std]
/// Architecture specific items
pub mod arch;
#[cfg(not(test))]
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[cfg(not(test))]
/// Required to handle panics
#[lang = "panic_fmt"]
extern "C" fn panic_fmt() -> ! {loop{}}

11
src/scheme/mod.rs Normal file
View file

@ -0,0 +1,11 @@
/// A scheme is a primitive for handling filesystem syscalls in Redox.
/// Schemes accept paths from the kernel for `open`, and file descriptors that they generate
/// are then passed for operations like `close`, `read`, `write`, etc.
///
/// The kernel validates paths and file descriptors before they are passed to schemes,
/// also stripping the scheme identifier of paths if necessary.
pub trait Scheme {
/// Open the file at `path` with `flags`.
/// Returns a file descriptor or an error
fn open(path: &str, flags: usize) -> Result<usize>;
}

25
x86_64-unknown-none.json Normal file
View file

@ -0,0 +1,25 @@
{
"llvm-target": "x86_64-unknown-none",
"target-endian": "little",
"target-pointer-width": "64",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"os": "none",
"env": "",
"vendor": "unknown",
"target-family": "redox",
"pre-link-args": ["-m64", "-nostdlib", "-static"],
"features": "-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2",
"dynamic-linking": false,
"executables": false,
"relocation-model": "static",
"code-model": "kernel",
"disable-redzone": true,
"eliminate-frame-pointer": true,
"exe-suffix": "",
"has-rpath": false,
"no-compiler-rt": true,
"no-default-libraries": true,
"position-independent-executables": false,
"has-elf-tls": false
}

25
x86_64-unknown-redox.json Normal file
View file

@ -0,0 +1,25 @@
{
"llvm-target": "x86_64-unknown-redox",
"target-endian": "little",
"target-pointer-width": "64",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"os": "redox",
"env": "",
"vendor": "unknown",
"target-family": "redox",
"pre-link-args": ["-m64", "-nostdlib", "-static"],
"features": "-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2",
"dynamic-linking": false,
"executables": true,
"relocation-model": "static",
"code-model": "kernel",
"disable-redzone": true,
"eliminate-frame-pointer": true,
"exe-suffix": ".bin",
"has-rpath": false,
"no-compiler-rt": true,
"no-default-libraries": true,
"position-independent-executables": false,
"has-elf-tls": false
}