;****************************************************************************** ;* * ;* 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