394 lines
8.8 KiB
NASM
394 lines
8.8 KiB
NASM
;******************************************************************************
|
|
;* *
|
|
;* S T A R T U P . A S M *
|
|
;* *
|
|
;*----------------------------------------------------------------------------*
|
|
;* Beschreibung: 'startup' ist der Eintrittspunkt des eigentlichen Systems.*
|
|
;* Die Umschaltung in den Protected-Mode ist bereits erfolgt.*
|
|
;* Es wird alles vorbereitet, damit so schnell wie moeglich *
|
|
;* die weitere Ausfuehrung durch C-Code erfolgen kann. *
|
|
;* *
|
|
;* Hier erweitert, um BIOS callund Paging-Aktivierung, *
|
|
;* Unterstuetzung des Bluescreens und preemptives *
|
|
;* Thread-Switching. *
|
|
;* *
|
|
;* Autor: Olaf Spinczyk, TU Dortmund *
|
|
;* Michael Schoettner, HHU, 3.7.2022 *
|
|
;******************************************************************************
|
|
|
|
|
|
; Multiboot-Konstanten
|
|
MULTIBOOT_PAGE_ALIGN equ 1<<0
|
|
MULTIBOOT_MEMORY_INFO equ 1<<1
|
|
|
|
; Magic-Number fuer Multiboot
|
|
MULTIBOOT_HEADER_MAGIC equ 0x1badb002
|
|
; Multiboot-Flags (ELF-spezifisch!)
|
|
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
|
|
MULTIBOOT_HEADER_CHKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
|
|
MULTIBOOT_EAX_MAGIC equ 0x2badb002
|
|
|
|
;
|
|
; System
|
|
;
|
|
|
|
[GLOBAL startup]
|
|
[GLOBAL idt]
|
|
[GLOBAL __cxa_pure_virtual]
|
|
[GLOBAL bios_call]
|
|
[GLOBAL invalidate_tlb_entry]
|
|
[GLOBAL paging_on]
|
|
[GLOBAL get_page_fault_address]
|
|
[GLOBAL get_int_esp]
|
|
|
|
|
|
; Michael Schoettner:
|
|
; Nachfolgender label steht fuer das 'delete', welches jetzt implementiert
|
|
; wird. Damit der Linker nicht wegen doppelter Definition "meckert"
|
|
; nun auskommentieren!
|
|
; [GLOBAL _ZdlPv]
|
|
|
|
[EXTERN main]
|
|
[EXTERN int_disp]
|
|
|
|
[EXTERN ___BSS_START__]
|
|
[EXTERN ___BSS_END__]
|
|
[EXTERN __init_array_start]
|
|
[EXTERN __init_array_end]
|
|
[EXTERN __fini_array_start]
|
|
[EXTERN __fini_array_end]
|
|
|
|
[SECTION .text]
|
|
|
|
startup:
|
|
jmp skip_multiboot_hdr
|
|
|
|
multiboot_header:
|
|
align 4
|
|
dd MULTIBOOT_HEADER_MAGIC
|
|
dd MULTIBOOT_HEADER_FLAGS
|
|
dd MULTIBOOT_HEADER_CHKSUM
|
|
|
|
skip_multiboot_hdr:
|
|
; GCC-kompilierter Code erwartet das so.
|
|
|
|
cld
|
|
|
|
cmp eax,MULTIBOOT_EAX_MAGIC
|
|
jne floppy_boot
|
|
;
|
|
; GDT setzen (notwendig, falls wir durch GRUB geladen wurden)
|
|
;
|
|
lgdt [gdt_48]
|
|
|
|
floppy_boot:
|
|
|
|
; Globales Datensegment
|
|
|
|
mov ax,0x10
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov fs,ax
|
|
mov gs,ax
|
|
|
|
; Stack festlegen
|
|
|
|
mov ss,ax
|
|
mov esp,init_stack+4096
|
|
|
|
; Unterbrechungsbehandlung sicherstellen
|
|
|
|
call setup_idt
|
|
call reprogram_pics
|
|
|
|
; BSS loeschen
|
|
mov edi, ___BSS_START__
|
|
clear_bss:
|
|
mov byte [edi], 0
|
|
inc edi
|
|
cmp edi, ___BSS_END__
|
|
jne clear_bss
|
|
|
|
; Aufruf des C-Codes
|
|
|
|
call _init ; Konstruktoren globaler Objekte ausfuehren
|
|
call main ; C/C++ Level System
|
|
call _fini ; Destruktoren
|
|
hlt
|
|
|
|
;; Ausfuehrung der Konstruktoren globaler Objekte
|
|
_init:
|
|
mov edi, __init_array_start
|
|
_init_loop:
|
|
cmp edi, __init_array_end
|
|
je _init_done
|
|
mov eax, [edi]
|
|
call eax
|
|
add edi, 4
|
|
ja _init_loop
|
|
_init_done:
|
|
ret
|
|
|
|
;; Ausfuehrung der Destruktoren globaler Objekte
|
|
_fini:
|
|
mov edi, __fini_array_start
|
|
_fini_loop:
|
|
cmp edi, __fini_array_end
|
|
je _fini_done
|
|
mov eax, [edi]
|
|
call eax
|
|
add edi, 4
|
|
ja _fini_loop
|
|
_fini_done:
|
|
ret
|
|
|
|
; Default Interrupt Behandlung
|
|
|
|
; Spezifischer Kopf der Unterbrechungsbehandlungsroutinen
|
|
|
|
%macro wrapper 1
|
|
wrapper_%1:
|
|
pushad ; alle Register sichern (fuer den Bluescreen)
|
|
mov ecx, int_esp ; Stack_zeiger sichern, fuer Zugriff im Bluescreen
|
|
mov [ecx], esp
|
|
mov al,%1
|
|
jmp wrapper_body
|
|
%endmacro
|
|
|
|
; ... wird automatisch erzeugt.
|
|
%assign i 0
|
|
%rep 256
|
|
wrapper i
|
|
%assign i i+1
|
|
%endrep
|
|
|
|
; Gemeinsamer Rumpf
|
|
wrapper_body:
|
|
cld ; das erwartet der gcc so.
|
|
push ecx ; Sichern der fluechtigen Register
|
|
push edx
|
|
and eax,0xff ; Der generierte Wrapper liefert nur 8 Bits
|
|
push eax ; Nummer der Unterbrechung uebergeben
|
|
call int_disp; Interrupt-Dispatcher aufrufen
|
|
add esp,4 ; Parameter vom Stack entfernen
|
|
pop edx ; fluechtige Register wieder herstellen
|
|
pop ecx
|
|
popad ; alle Register wiederherstellen
|
|
iret ; fertig!
|
|
|
|
;
|
|
; setup_idt
|
|
;
|
|
; Relokation der Eintraege in der IDT und Setzen des IDTR
|
|
|
|
setup_idt:
|
|
mov eax,wrapper_0 ; ax: niederwertige 16 Bit
|
|
mov ebx,eax
|
|
shr ebx,16 ; bx: hoeherwertige 16 Bit
|
|
mov ecx,255 ; Zaehler
|
|
.loop: add [idt+8*ecx+0],ax
|
|
adc [idt+8*ecx+6],bx
|
|
dec ecx
|
|
jge .loop
|
|
|
|
lidt [idt_descr]
|
|
ret
|
|
|
|
;
|
|
; reprogram_pics
|
|
;
|
|
; Neuprogrammierung der PICs (Programmierbare Interrupt-Controller), damit
|
|
; alle 15 Hardware-Interrupts nacheinander in der idt liegen.
|
|
|
|
reprogram_pics:
|
|
mov al,0x11 ; ICW1: 8086 Modus mit ICW4
|
|
out 0x20,al
|
|
call delay
|
|
out 0xa0,al
|
|
call delay
|
|
mov al,0x20 ; ICW2 Master: IRQ # Offset (32)
|
|
out 0x21,al
|
|
call delay
|
|
mov al,0x28 ; ICW2 Slave: IRQ # Offset (40)
|
|
out 0xa1,al
|
|
call delay
|
|
mov al,0x04 ; ICW3 Master: Slaves an IRQs
|
|
out 0x21,al
|
|
call delay
|
|
mov al,0x02 ; ICW3 Slave: Verbunden mit IRQ2 des Masters
|
|
out 0xa1,al
|
|
call delay
|
|
mov al,0x03 ; ICW4: 8086 Modus und automatischer EIO
|
|
out 0x21,al
|
|
call delay
|
|
out 0xa1,al
|
|
call delay
|
|
|
|
mov al,0xff ; Hardware-Interrupts durch PICs
|
|
out 0xa1,al ; ausmaskieren. Nur der Interrupt 2,
|
|
call delay ; der der Kaskadierung der beiden
|
|
mov al,0xfb ; PICs dient, ist erlaubt.
|
|
out 0x21,al
|
|
|
|
ret
|
|
|
|
; delay
|
|
;
|
|
; Kurze Verzoegerung fuer in/out Befehle.
|
|
|
|
delay:
|
|
jmp .L2
|
|
.L2: ret
|
|
|
|
; Die Funktion wird beim abarbeiten der globalen Konstruktoren aufgerufen
|
|
; (unter Linux). Das Label muss definiert sein (fuer den Linker). Die
|
|
; Funktion selbst kann aber leer sein, da bei StuBs keine Freigabe des
|
|
; Speichers erfolgen muss.
|
|
|
|
__cxa_pure_virtual:
|
|
_ZdlPv:
|
|
ret
|
|
|
|
;
|
|
; bios_call
|
|
;
|
|
; BIOS-Aufruf (siehe BIOS.cc)
|
|
;
|
|
bios_call:
|
|
lidt [idt16_descr]
|
|
pushf
|
|
pusha
|
|
call 0x18:0
|
|
popa
|
|
popf
|
|
lidt [idt_descr]
|
|
ret
|
|
|
|
; Paging aktivieren
|
|
; (siehe Paging.cc)
|
|
paging_on:
|
|
mov eax,[4+esp] ; Parameter Addr. Page-Dir. ins eax Register
|
|
mov ebx, cr4
|
|
or ebx, 0x10 ; 4 MB Pages aktivieren
|
|
mov cr4, ebx ; CR4 schreiben
|
|
mov cr3, eax ; Page-Directory laden
|
|
mov ebx, cr0
|
|
or ebx, 0x80010000 ; Paging aktivieren
|
|
mov cr0, ebx
|
|
ret
|
|
|
|
; Paging-Fault-Adresse holen
|
|
; (siehe Paging.cc)
|
|
get_page_fault_address:
|
|
mov eax,cr2
|
|
ret
|
|
|
|
; Invalidiert eine Seite im TLB. Dies notwendig, falls eine
|
|
; die Bits Present, R/W in einem Seitentabelleneintrag
|
|
; geaendert werden. Falls die Seite im TLB gespeichert ist
|
|
; wuerde die MMU nichts von diesen Aenderungen erkennen,
|
|
; da die MMU dann nicht auf die Seitentabellen zugreift.
|
|
; (siehe Paging.cc)
|
|
invalidate_tlb_entry:
|
|
mov eax, [esp+4]
|
|
invlpg [eax]
|
|
ret
|
|
|
|
; Auslesen von 'int_esp'
|
|
; wird im Bluescreen benoetigt, um den Stacks zuzugreifen
|
|
;
|
|
; C Prototyp: void get_int_esp (unsigned int** esp);
|
|
get_int_esp:
|
|
mov eax,[4+esp] ; esp
|
|
mov ecx, int_esp
|
|
mov [eax], ecx
|
|
ret
|
|
|
|
|
|
[SECTION .data]
|
|
|
|
; 'interrupt descriptor table' mit 256 Eintraegen.
|
|
|
|
idt:
|
|
|
|
%macro idt_entry 1
|
|
dw (wrapper_%1 - wrapper_0) & 0xffff
|
|
dw 0x0008
|
|
dw 0x8e00
|
|
dw ((wrapper_%1 - wrapper_0) & 0xffff0000) >> 16
|
|
%endmacro
|
|
|
|
; ... wird automatisch erzeugt.
|
|
|
|
%assign i 0
|
|
%rep 256
|
|
idt_entry i
|
|
%assign i i+1
|
|
%endrep
|
|
|
|
idt_descr:
|
|
dw 256*8-1 ; idt enthaelt 256 Eintraege
|
|
dd idt
|
|
|
|
; Stack und interrupt descriptor table im BSS Bereich
|
|
|
|
[SECTION .bss]
|
|
|
|
init_stack:
|
|
resb 4096
|
|
|
|
[SECTION .data]
|
|
;
|
|
; Descriptor-Tabellen
|
|
;
|
|
gdt:
|
|
dw 0,0,0,0 ; NULL Deskriptor
|
|
|
|
dw 0xFFFF ; 4Gb - (0x100000*0x1000 = 4Gb)
|
|
dw 0x0000 ; base address=0
|
|
dw 0x9A00 ; code read/exec
|
|
dw 0x00CF ; granularity=4096, 386 (+5th nibble of limit)
|
|
|
|
dw 0xFFFF ; 4Gb - (0x100000*0x1000 = 4Gb)
|
|
dw 0x0000 ; base address=0
|
|
dw 0x9200 ; data read/write
|
|
dw 0x00CF ; granularity=4096, 386 (+5th nibble of limit)
|
|
|
|
dw 0xFFFF ; 4Gb - (0x100000*0x1000 = 4Gb)
|
|
dw 0x4000 ; 0x4000 -> base address=0x24000 (siehe BIOS.cc)
|
|
dw 09A02h ; 0x2 -> base address =0x24000 (siehe BIOS.cc) und code read/exec;
|
|
dw 0008Fh ; granularity=4096, 16-bit code
|
|
|
|
gdt_48:
|
|
dw 0x20 ; GDT Limit=24, 3 GDT Eintraege
|
|
dd gdt ; Physikalische Adresse der GDT
|
|
|
|
|
|
;
|
|
; IDT des Realmode ;
|
|
; (Michael Schoettner)
|
|
;
|
|
idt16_descr:
|
|
dw 1024 ; idt enthaelt max. 1024 Eintraege
|
|
dd 0 ; Adresse 0
|
|
|
|
;
|
|
; Stack-Zeiger fuer Bluescreen
|
|
; (genauerer Stack-Aufbau siehe Bluescreen.cc)
|
|
;
|
|
; |-------------|
|
|
; | EFLAGS |
|
|
; |-------------|
|
|
; | CS |
|
|
; |-------------|
|
|
; | EIP |
|
|
; |-------------|
|
|
; | [ErrorCode] |
|
|
; |-------------|
|
|
; | alle Regs. |
|
|
; | (PUSHAD) |
|
|
; |-------------| <-- int_esp
|
|
int_esp:
|
|
db 0,0,0,0
|
|
|