1

add setup/boot code

This commit is contained in:
churl
2022-04-16 14:44:49 +02:00
parent 3e28f3b49d
commit d106ef85fa
10 changed files with 1289 additions and 0 deletions

254
Makefile Executable file
View File

@ -0,0 +1,254 @@
# -----------------------------------------------------------------------------
#
# M A K E F I L E
#
# zum HHUos der Lehrveranstaltung 'Betriebssystem-Entwicklung'.
# -----------------------------------------------------------------------------
#
# Compilieren: make'
# Ausfuehren mit Qemu: make qemu
# GDB mit Qemu: make qemu-gdb & und danach make gdb
# DDD mit Qemu: make qemu-gdb & und danach make ddd
#
# Aufraeumen mit: make clean
#
# Achtung: Erzeugen von bootfaehigen Medien erfordert das Anpassen folgender
# Variablen: DRIVE_FD und DRIVE_HD. Aufpassen, ansonsten wir evt.
# die System-Disk zerstoert.
#
# Erzeugen einer bootfaehigen Diskette mit: make bootdisk
# Erzeugen eines bootfaehigen USB-Sticks mit: make bootdisk-usb
# Erzeugen eines bootfaehigen Festplatte mit: make bootdisk-hd
#
# -----------------------------------------------------------------------------
# Autor: Olaf Spinzcyk, TU Dortmund
#
# -----------------------------------------------------------------------------
# Liste der Quelltexte:
STARTUP_SOURCE = ./startup.asm
CC_SOURCES = $(shell find . -name "*.cc")
C_SOURCES = $(shell find . -name "*.c")
ASM_SOURCES = $(shell find ./kernel -name "*.asm")
# Einstellungen in Abhaengigkeit vom Generierungssystem:
#
# ASMOBJFORMAT: Ausgabeformat fuer den Assembler. Das Format muss dem
# jeweiligen Format des verwendeten C++ Compilers angepasst
# werde, damit gemischte Objektdateien gelinkt werden koennen.
# OBJDIR: Verzeichnis, in dem die Objektdateien abgelegt werden
# sollen.
# DEPDIR: Verzeichnis, in dem die Abhaengigkeitsdateien abgelegt werden
# sollen
# DRIVE_FD: Diskettenlaufwerk, auf das das System-Image geschrieben wird
# (Target: bootdisk)
# DRIVE_HD: Festplatten-/USB-Device, auf das das System-Image
# geschrieben wird (Target: bootdisk-hd)
# DELETE: Name des Kommandos zum Loeschen von Dateien
# ASM: Zu benutzender Assembler
# CC/CXX: Zu benutzender C/C++-Compiler
# CFLAGS: Flags fuer den C-Compileraufruf
# CXXFLAGS: Flags fuer den C++-Compileraufruf
# LD: Zu benutzender Linker
# LDFLAGS: Flags fuer den Linkeraufruf
# LDLIBS: Dazuzulinkende Bibliotheken
# LDHEAD: Am Anfang zu linkende Dateien
# LDTAIL: Am Ende zu linkende Dateien
# BOOT: Das Verzeichnis zu Bootsektor und Setup-Code
# TOOLS: Das Verzeichnis mit dem Build-Tool
VERBOSE = @
ASMOBJFORMAT = elf
OBJDIR = ./build
DEPDIR = ./dep
DRIVE_FD = /dev/sdb
# ACHTUNG: ein falsch angegebenes Laufwerk kann dazu fuehren, dass Daten auf dem
# spezifizierten Laufwerk verloren gehen! Nicht mit root-Rechten ausfuehren!
DRIVE_HD = /dev/sdb
DELETE = rm
ASM = nasm
CC ?= gcc
CXX ?= g++
CFLAGS := $(CFLAGS) -m32 -march=i486 -Wall -fno-stack-protector -nostdlib -I. -g -ffreestanding -fno-pie -fno-pic -mpreferred-stack-boundary=2 -Wno-write-strings -mno-sse -mno-sse2 -mmanual-endbr
CXXFLAGS := $(CFLAGS) -Wno-non-virtual-dtor -fno-threadsafe-statics -fno-use-cxa-atexit -fno-rtti -fno-exceptions
BOOT = ../c_boot
TOOLS = ../c_tools
# BIOS-dev.code:total-tracks:-heads:-sectors:start-track:-head:-sector
# Default-Werte fuer Boot von Floppy (USB/HD erkennt bootsect.asm selbst):
BOOTDEVICE = 0:80:2:18:0:0:1
# -------------------------------------------------------------------------
# Namen der Unterverzeichnisse mit den Quelltexten
VPATH = $(sort $(dir $(STARTUP_SOURCE) $(CC_SOURCES) $(C_SOURCES) $(ASM_SOURCES)))
# -------------------------------------------------------------------------
# Listen mit den Objektdateien, die beim Kompilieren entstehen:
FIRST_OBJECT = $(addprefix $(OBJDIR)/,$(patsubst %.asm,_%.o, $(notdir $(STARTUP_SOURCE))))
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
CC_OBJECTS = $(notdir $(CC_SOURCES:.cc=.o))
DEP_FILES = $(patsubst %.o,$(DEPDIR)/%.d,$(C_OBJECTS))
DEP_FILES += $(patsubst %.o,$(DEPDIR)/%.d,$(CC_OBJECTS))
ASM_OBJECTS = $(patsubst %.asm,_%.o, $(notdir $(ASM_SOURCES)))
OBJPRE = $(addprefix $(OBJDIR)/,$(ASM_OBJECTS) $(C_OBJECTS) $(CC_OBJECTS))
# --------------------------------------------------------------------------
# Default targets (einfaches Image, Image fuer USB Sticks, Image fuer VMWare
# und Boot CDs)
all: $(OBJDIR)/bootdisk.img $(OBJDIR)/bootdisk.vmi $(OBJDIR)/bootdisk-hd.vmi
# --------------------------------------------------------------------------
# Regeln zur Erzeugung der Abhaengigkeitsdateien
$(DEPDIR)/%.d : %.c
@echo "DEP $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(CC) $(CFLAGS) -MM -MT $(OBJDIR)/$*.o -MF $@ $<
$(DEPDIR)/%.d : %.cc
@echo "DEP $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(CXX) $(CXXFLAGS) -MM -MT $(OBJDIR)/$*.o -MF $@ $<
# --------------------------------------------------------------------------
# Regeln zur Erzeugung der Objektdateien
$(OBJDIR)/%.o : %.c
@echo "CC $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(CC) -c $(CFLAGS) -o $@ $<
$(OBJDIR)/%.o : %.cc
@echo "CXX $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(CXX) -c $(CXXFLAGS) -o $@ $<
$(OBJDIR)/_%.o : %.asm
@echo "ASM $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(ASM) -f $(ASMOBJFORMAT) -o $@ $<
# --------------------------------------------------------------------------
# Gelinktes System
$(OBJDIR)/system: $(FIRST_OBJECT) $(OBJPRE)
@echo "LD $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(CXX) $(CXXFLAGS) -static -e startup -T sections -o $(OBJDIR)/system $(FIRST_OBJECT) $(OBJPRE)
# --------------------------------------------------------------------------
# 'system.img' enthaelt die zu ladenden Sections des eigentlichen Systems
$(OBJDIR)/system.img : $(OBJDIR)/system
@echo "OBJCOPY $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) objcopy -O binary $< $@
# --------------------------------------------------------------------------
# 'bootdisk.img' besteht aus dem 'system.img', das um den (parametrisierten)
# Bootblock und den Setup-Code erweitert wurde.
$(OBJDIR)/bootdisk.img : $(OBJDIR)/system.img $(TOOLS)/build $(BOOT)/bootsect $(BOOT)/setup
@echo "BUILD $@"
@if test \( ! \( -d $(@D) \) \) ;then mkdir -p $(@D);fi
$(VERBOSE) $(TOOLS)/build $(BOOT)/bootsect $(BOOT)/setup $< $(BOOTDEVICE) $@
# 'bootdisk.vmi' ist eine bootdisk, die auf 1.44 MB mit 0 aufgefuellt ist,
# damit VMware damit klarkommt. Des weiteren wird dieses Image benoetigt, um
# bootfaehige CDs zu erstellen.
# bootdisk-hd.img wird auf etwa 1M mit 0 aufgefuellt, da qemu diese Groesse
# auf eine Festplatten-Laufwerkscharakteristik zurueckrechnen kann.
$(OBJDIR)/bootdisk.vmi: $(OBJDIR)/bootdisk.img
dd if=$< of=$@ bs=1474560 conv=sync
$(OBJDIR)/bootdisk-hd.vmi: $(OBJDIR)/bootdisk.img
dd if=$< of=$@ bs=$$((2016*512)) conv=sync
# --------------------------------------------------------------------------
# 'clean' loescht das generierte System, die Objektdateien und die
# Abhaengigkeitsdateien
clean:
@echo "RM $(OBJDIR)"
$(VERBOSE) rm -rf $(OBJDIR)
@echo "RM $(DEPDIR)"
$(VERBOSE) rm -rf $(DEPDIR)
$(MAKE) -C $(BOOT) clean
# --------------------------------------------------------------------------
# 'bootdisk' erzeugt zunaechst das System, falls das noch nicht geschehen ist.
# Danach wird eine Bootdiskette mit dem System erzeugt.
bootdisk: $(OBJDIR)/bootdisk.img
@echo "CP $<"
$(VERBOSE) cp $< $(DRIVE_FD)
# --------------------------------------------------------------------------
# 'bootdisk-hd' erzeugt zunaechst das System, falls das noch nicht geschehen
# ist. Danach wird das System auf das spezifizierte Laufwerk geschrieben,
# welches sowohl eine Festplatte, als auch ein USB-Stick sein kann.
# ACHTUNG: ein falsch angegebenes Laufwerk kann dazu fuehren, dass Daten auf dem
# spezifizierten Laufwerk verloren gehen! Nicht mit root-Rechten ausfuehren!
bootdisk-usb: bootdisk-hd
bootdisk-hd: $(OBJDIR)/bootdisk.img
@echo "CP $<"
$(VERBOSE) cp $< $(DRIVE_HD)
# --------------------------------------------------------------------------
# 'qemu' ruft den qemu-Emulator mit dem System auf.
qemu: $(OBJDIR)/bootdisk.vmi
qemu-system-i386 -fda $(OBJDIR)/bootdisk.vmi -boot a -k en-us -soundhw pcspk -vga std -cpu 486
# --------------------------------------------------------------------------
# 'qemu-gdb' ruft den qemu-Emulator mit aktiviertem GDB-Stub mit dem System
# auf, sodass es per GDB oder DDD inspiziert werden kann.
qemu-gdb: $(OBJDIR)/bootdisk.vmi
$(VERBOSE) echo "break main" > /tmp/gdbcommands.$(shell id -u)
$(VERBOSE) echo "target remote 127.0.0.1:1234" >> /tmp/gdbcommands.$(shell id -u)
$(VERBOSE) echo "continue" >> /tmp/gdbcommands.$(shell id -u)
qemu-system-i386 -fda $(OBJDIR)/bootdisk.vmi -boot a -k en-us -s -S -soundhw pcspk -vga std -cpu 486
# --------------------------------------------------------------------------
# 'gdb' startet den GDB-Debugger und verbindet sich mit dem GDB-Stub des vorher
# gestarteten Qemu.
gdb:
gdb -x /tmp/gdbcommands.$(shell id -u) $(OBJDIR)/system
# --------------------------------------------------------------------------
# 'ddd' startet den DDD-Debugger und verbindet sich mit dem GDB-Stub des vorher
# gestarteten Qemu.
ddd:
ddd --gdb -x /tmp/gdbcommands.$(shell id -u) $(OBJDIR)/system
# --------------------------------------------------------------------------
# 'build'-Tool kompilieren.
$(TOOLS)/build: $(TOOLS)/build.c
cd $(TOOLS) && $(CC) -o $@ $<
# --------------------------------------------------------------------------
# Bootsektor und Protected-Mode-Setup-Code kompilieren.
$(BOOT)/bootsect $(BOOT)/setup: $(BOOT)/bootsect.asm $(BOOT)/setup.asm
$(MAKE) -C $(BOOT)
# --------------------------------------------------------------------------
# Einbindung der Abhaengigkeitsdateien
ifneq ($(MAKECMDGOALS),clean)
-include $(DEP_FILES)
endif
.PHONY: clean bootdisk bootdisk-hd bootdisk-usb gdb ddd

9
c_boot/Makefile Executable file
View File

@ -0,0 +1,9 @@
# $Id: Makefile 956 2008-10-19 22:24:23Z hsc $
all: bootsect setup
% : %.asm
nasm -f bin $<
clean:
rm -f *~ bootsect setup

BIN
c_boot/bootsect Normal file

Binary file not shown.

324
c_boot/bootsect.asm Executable file
View File

@ -0,0 +1,324 @@
; $Id: bootsect.asm 5001 2012-10-12 11:34:05Z os $
;******************************************************************************
;* Betriebssysteme *
;*----------------------------------------------------------------------------*
;* *
;* B O O T S E C T *
;* *
;*----------------------------------------------------------------------------*
;* Code fuer den Disketten-Bootblock des System-Images. Das BIOS laedt den *
;* ersten Block einer Diskette (den Bootblock) beim Starten des Rechner in *
;* den Hauptspeicher und fuehrt ihn aus. Der Programmcode des Bootblocks *
;* laedt nun das restliche System und fuehrt es aus. *
;******************************************************************************
;
; Konstanten
;
BIOSSEG equ 0x07c0 ; Hierher wird der Bootsector
; vom BIOS geladen
BOOTSEG equ 0x0060 ; Hierher verschiebt sich der
; Boot-Code
SETUPSEG equ 0x9000 ; Hierher laedt der Boot-Code den
; Setup-Code (max. 64K inkl. Stack)
SYSTEMSEG equ 0x1000 ; System-Code (max. 512K)
SECTORSZ equ 512 ; Groesse eines Sektors in Bytes
[SECTION .text]
;
; Boot-Code
;
bootsector:
jmp skip_data
;------------------------------------------------------------------------------
;
; Datenbereich, der von 'build' beim Erzeugen der Boot-Diskette
; gefuellt wird.
;
pad:
times 4+bootsector-$ db 0 ; Bytes zum Auffuellen, damit 'total_tracks' an einer
; geraden und tools/build.c bekannten Adresse liegt
total_tracks:
dw 0 ; Anzahl der Tracks der Diskette
total_heads:
dw 0 ; Anzahl der Seiten der Diskette
total_sectors:
dw 0 ; Anzahl der Sektoren pro Track
setup_sectors:
dw 0 ; Anzahl der Sektoren, die der Setup-Code einnimmt
system_sectors:
dw 0 ; Anzahl der Sektoren, die das System einnimmt
bootdevice:
db 0 ; BIOS Geraetecode: 00: Disk A, 01: Disk B, ..., 0x80 HD0, ...
curr_track:
db 0 ; Track, bei dem die Diskette/Partition beginnt
curr_head:
db 0 ; Head, bei dem die Diskette/Partition beginnt
curr_sector:
db 0 ; Sector, bei dem die Diskette/Partition beginnt
;-----------------------------------------------------------------------
;
; Kopieren des Bootsectors
;
skip_data:
mov bl,dl ; vom BIOS uebergebenes Boot-Device sichern
mov ax,BIOSSEG
mov ds,ax
xor si,si
mov ax,BOOTSEG
mov es,ax
xor di,di
mov cx,SECTORSZ/2
rep movsw
;
; Ausfuehrung durch die Kopie fortsetzen
;
jmp BOOTSEG:start
;
; Segmentregister initialisieren und Platz fuer den Stack schaffen
;
start:
mov ax,cs ; Daten-, Stack- und Codesegment sollen
mov ds,ax ; hierher zeigen.
mov ss,ax
mov sp,4*SECTORSZ ; Drei Sektoren als Stack freilassen
mov [bootdevice],bl ; zuvor gesichertes Boot-Device permanent ablegen
;
; Ausgabe einer Meldung mit Hilfe eines BIOS-Aufrufs
;
mov ah,0x03 ; Feststellen der Cursor-Position
xor bh,bh
int 0x10
mov cx,13
mov bx,0x0007 ; page 0, attribute 7 (normal)
mov ax,ds
mov es,ax
mov bp,bootmessage
mov ax,0x1301 ; Ausgabe des Textes, Cursor bewegen
int 0x10
;
; Nachladen des Setup-Codes und des Systems selbst.
;
xor ah,ah ; Reset des Disketten-/Plattencontrollers
mov dl,[bootdevice]
int 0x13
;
; Informationen ueber die Laufwerksgeometrie holen
;
hdd_probe:
mov dl,[bootdevice]
test dl,0x80
jz load_setup ; Floppy mit den Standardparametern laden
mov ah,0x8
int 0x13
jc load_setup ; CF bei Fehler gesetzt
mov [total_heads],dh
mov ax,cx ; CX sichern
and ax,0x3f
mov [total_sectors],ax
mov ax,cx
shr ax,6
mov [total_tracks],ax
;
; Weiterstellen der Disketten-/Plattenposition um 1 (Bootblock)
;
load_setup:
call step_disk
;
; Laden des Setup-Codes
;
mov word [curr_segment],SETUPSEG
mov word [curr_offset],0
mov ax,[setup_sectors]
call load
;
; Laden des Kernels
;
mov word [curr_segment],SYSTEMSEG
mov word [curr_offset],0
mov ax,[system_sectors]
call load
;
; Floppy wieder abschalten
;
call stop_floppy_motor
;
; Start des Setup-Codes
;
mov ax, [system_sectors] ; Speichere Anzahl an System-Sektoren in AX.
jmp SETUPSEG:0
;
; load
;
; Die 'ax' Sektoren von der Diskette in den Hauptspeicher. Die Position auf
; der Diskette muss vorher in curr_head/curr_track/curr_sector und die
; Position im Hauptspeicher in curr_segment/curr_offset stehen. Die Positionen
; werden entsprechend der geladenen Sektoren weitergestellt.
;
load:
mov [to_load],ax
l_next_part:
mov al,[curr_head]
mov [last_head],al
mov al,[curr_track]
mov [last_track],al
mov al,[curr_sector]
mov [last_sector],al
mov ax,[curr_segment]
mov [last_segment],ax
mov ax,[curr_offset]
mov [last_offset],ax
mov al,0
l_loop: call step
cmp byte [curr_sector],0x01
je l_now
cmp word [curr_offset],0x0000
je l_now
cmp al,[to_load]
jne l_loop
l_now:
push ax
mov dl,[bootdevice]
mov dh,[last_head]
mov ch,[last_track]
mov cl,[last_sector]
mov bx,[last_segment]
mov es,bx
mov bx,[last_offset]
mov ah,0x02 ; Funktionscode fuer 'Lesen'
int 0x13
pop ax
push ax
call print_dot
pop ax
mov ah,0
sub [to_load],ax
jne l_next_part
ret
;
; step
;
; Stellt die aktuelle Position im Hauptspeicher und auf der Diskette
; um einen Sektor (512 Byte) weiter.
;
step: add al,1
call step_disk
call step_memory
ret
step_disk:
mov bl,[curr_sector]
add bl,1
mov [curr_sector],bl
cmp bl,[total_sectors]
jle l_1
mov byte [curr_sector],1
mov bl,[curr_head]
add bl,1
mov [curr_head],bl
cmp bl,[total_heads]
jne l_1
mov byte [curr_head],0
mov bl,[curr_track]
add bl,1
mov [curr_track],bl
l_1: ret
step_memory:
mov bx,[curr_offset]
add bx,SECTORSZ
mov [curr_offset],bx
test bx,0xffff
jne l_2
mov bx,[curr_segment]
add bx,0x1000 ; 64 KByte weiterstellen
mov [curr_segment],bx
l_2 ret
;
; Ausgabe eines Stern ('*') mit Hilfe eines BIOS-Aufrufs
;
print_dot:
mov ah,0x03 ; Feststellen der Cursor-Position
xor bh,bh
int 0x10
mov cx,1
mov bx,0x0007 ; page 0, attribute 7 (normal)
mov ax,ds
mov es,ax
mov bp,dot
mov ax,0x1301 ; Ausgabe des Textes, Cursor bewegen
int 0x10
ret
;
; stop_floppy_motor
;
; Stopt den Motor der Floppy, da das BIOS dazu in Kuerze nicht mehr in
; der Lage sein wird. Egal, ob von Floppy oder Platte gebootet wurde.
;
stop_floppy_motor:
mov dx,0x3f2
xor al,al
out dx,al
ret
;
; Datenbereich
;
bootmessage:
db 13,10
db 'booting ... '
dot:
db '*'
to_load:
dw 0
curr_segment:
dw 0
curr_offset:
dw 0
last_head:
db 0
last_track:
db 0
last_sector:
db 0
last_segment:
dw 0
last_offset:
dw 0
unused:
times bootsector+510-$ db 0
mark:
dw 0xaa55

BIN
c_boot/setup Normal file

Binary file not shown.

195
c_boot/setup.asm Executable file
View File

@ -0,0 +1,195 @@
; $Id: setup.asm 1484 2009-02-11 21:03:19Z hsc $
;******************************************************************************
;* Betriebssysteme *
;*----------------------------------------------------------------------------*
;* *
;* S E T U P *
;* *
;*----------------------------------------------------------------------------*
;* Der Setup-Code liegt im System-Image direkt hinter dem Bootsektor und wird *
;* von diesem direkt nach dem Laden aktiviert. Der Code wird noch im *
;* 'Real-Mode' gestartet, so dass zu Beginn auch noch BIOS-Aufrufe erlaubt *
;* sind. Dann werden jedoch alle Interrupts verboten, die Adressleitung A20 *
;* aktiviert und die Umschaltung in den 'Protected-Mode' vorgenommen. Alles *
;* weitere uebernimmt der Startup-Code des Systems. *
;******************************************************************************
;
; Konstanten
;
SETUPSEG equ 0x9000 ; Setup-Code (max. 64K inkl. Stack)
SYSTEMSEG equ 0x1000 ; System-Code (max. 512K)
SECTORSZ equ 512 ; Groesse eines Sektors in Bytes
SYSTEMSTART equ 0x100000 ; Hierhin wird das System nach Umschalten in den
; Protected Mode kopiert, da GRUB das auch tut
; (und GRUB kann nur an Adressen >1M laden).
[SECTION .text]
[BITS 16]
;
; Segmentregister initialisieren
;
start:
mov dx, ax ; Anzahl Systemsektoren in DX sichern.
mov ax,cs ; Daten-, Code- und Stacksegment sollen
mov ds,ax ; hierher zeigen.
mov ss,ax ; Alle drei Segment duerfen nicht mehr
mov sp,0xfffe ; als 64 KByte einnehmen (zusammen).
mov [system_sectors], dx ; Anzahl der Systemsektoren im Speicher ablegen
;
; Ausgabe einer Meldung mit Hilfe eines BIOS-Aufrufs
;
mov ah,0x03 ; Feststellen der Cursor-Position
xor bh,bh
int 0x10
mov cx,14
mov bx,0x0007 ; page 0, attribute 7 (normal)
mov ax,ds
mov es,ax
mov bp,setupmessage
mov ax,0x1301 ; Ausgabe des Textes, Cursor bewegen
int 0x10
;
; So, jetzt werden die Interrupts abgeschaltet
;
cli ; Maskierbare Interrupts verbieten
mov al,0x80 ; NMI verbieten
out 0x70,al
;
; IDT und GDT setzen
;
lidt [idt_48]
lgdt [gdt_48]
;
; Aktivieren der Adressleitung A20
;
call empty_8042
mov al,0xd1
out 0x64,al
call empty_8042
mov al,0xdf
out 0x60,al
call empty_8042
mov al,0xff
out 0x64,al
call empty_8042
;
; Moeglichen Koprozessor zuruecksetzen
;
xor ax,ax
out 0xf0,al
call delay
out 0xf1,al
call delay
;
; Umschalten in den Protected Mode
;
mov eax,cr0 ; Setze PM-Bit im Kontrollregister 1
or eax,1
mov cr0,eax
jmp dword 0x08:SETUPSEG*0x10+copy_system ; Far-Jump, um
; a) fetch Pipeline zu leeren
; b) CS Register sinnvoll zu belegen
[BITS 32]
; Arbeite jetzt im Protected Mode
copy_system:
;
; Systemcode von 0x10000 nach 0x100000 kopieren.
;
mov ax, 0x10 ; 0x10 entspricht dem Data-Eintrag in der GDT.
mov ds, ax ; DS und ES werden von movsd benoetigt.
mov es, ax
xor ecx, ecx ; Anzahl Systemsektoren laden
mov cx, [SETUPSEG*0x10+system_sectors]
imul ecx, SECTORSZ/4
mov esi, SYSTEMSEG*0x10 ; Hier liegt der Systemcode noch ...
mov edi, SYSTEMSTART ; ... und hierhin moechten wir ihn verschieben
cld ; Nach jedem movsb ESI,EDI inkrementieren
rep movsd ; Kopiere 4 Byte von [ESI] nach [EDI] ecx male
;
; Sprung in den Startup-Code des Systems
;
jmp dword 0x08:SYSTEMSTART
error:
hlt
[BITS 16]
; Ab hier wieder Real-Mode Code fuer die Codeteile
; vor der Umschaltung in den Protected Mode
;
; empty_8042
;
; Ein- und Ausgabepuffer des Tastaturcontrollers leeren
;
empty_8042:
call delay
in al,0x64 ; 8042 Status Port
test al,1 ; Ausgabepuffer voll?
jz no_output
call delay
in al,0x60 ; wenn ja: ueberlesen
jmp empty_8042
no_output:
test al,2 ; Eingabepuffer voll?
jnz empty_8042 ; wenn ja, noch mal testen, irgendwann
ret ; muss es weg sein.
;
; delay:
;
; Kurze Verzoegerung fuer in/out-Befehle
;
delay:
out 0x80,al
ret
;
; Datenbereich
;
[SECTION .data]
; Meldung
system_sectors:
dw 0
setupmessage:
db 13,10
db 'setup active'
;
; 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)
idt_48:
dw 0 ; idt limit=0
dw 0,0 ; idt base=0L
gdt_48:
dw 0x18 ; GDT Limit=24, 3 GDT Eintraege
dd SETUPSEG*0x10+gdt; Physikalische Adresse der GDT

BIN
c_tools/build Executable file

Binary file not shown.

153
c_tools/build.c Executable file
View File

@ -0,0 +1,153 @@
#include <stdio.h> /* fprintf */
#include <string.h>
#include <stdlib.h> /* contains exit */
#include <unistd.h> /* contains read/write */
#include <fcntl.h>
#include <sys/stat.h>
#if !defined(DOS) && !defined(Win32)
#define O_BINARY 0
#endif
#define SECTOR 512
void die (const char* str)
{
fprintf(stderr,"%s\n",str);
exit(1);
}
int main (int argc, char** argv)
{
int fd, fd_out;
char bootsector[SECTOR];
char setupsector[SECTOR];
struct stat info;
unsigned short setup_size;
int bytes_read, to_read;
unsigned int dc, st, sh, ss, tt, th, ts;
unsigned char bios_device_code, start_track, start_head, start_sector;
unsigned short total_tracks, total_heads, total_sectors;
unsigned short system_sectors;
if (argc != 6)
die ("usage: build bootsector setup-code system-image dev.info bootdisk-image\n\n"
"dev.info: BIOS-dev.code:total-tracks:-heads:-sectors:start-track:-head:-sector\n"
"BIOS-devicecode: 0=fd0, 1=fd1, ..., 128=hd0, 129=hd1, ...\n"
"Example: to boot from a 3.5\" floppy disk use \"0:80:2:18:0:0:1\"\n");
sscanf (argv[4], "%u:%u:%u:%u:%u:%u:%u", &dc, &tt, &th, &ts, &st, &sh, &ss);
bios_device_code = (unsigned char)dc;
total_tracks = (unsigned short)tt;
total_sectors = (unsigned short)ts;
total_heads = (unsigned short)th;
start_track = (unsigned char)st;
start_head = (unsigned char)sh;
start_sector = (unsigned char)ss;
printf ("BIOS-devicecode: 0x%x\n", bios_device_code);
printf ("Total T/H/S: (%d/%d/%d)\n", total_tracks, total_heads,
total_sectors);
printf ("Start T/H/S: (%d/%d/%d)\n\n", start_track, start_head,
start_sector);
if ((fd_out = open (argv[5], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) < 0)
die ("Unable to open output file.");
if ((fd = open (argv[1], O_RDONLY|O_BINARY, 0)) < 0)
die ("Unable to open input file.");
if (read (fd, bootsector, SECTOR) < 0)
die ("Unable to read bootsector.");
close (fd);
if (stat (argv[2], &info) != 0)
die ("Can't stat setup file.");
setup_size = (unsigned short)
((info.st_size + SECTOR - 1) / SECTOR);
printf ("Setup size is %d sectors.\n", (int)setup_size);
if (stat (argv[3], &info) != 0)
die ("Can't stat system file.");
to_read = info.st_size;
printf ("System size is %d bytes.\n", to_read);
system_sectors = (to_read + SECTOR - 1) / SECTOR;
printf ("# System sectors is %d.\n", system_sectors);
#if 0
*(unsigned short*)(bootsector+4) = total_tracks;
*(unsigned short*)(bootsector+6) = total_heads;
*(unsigned short*)(bootsector+8) = total_sectors;
*(unsigned short*)(bootsector+10) = setup_size;
*(unsigned short*)(bootsector+12) =
(unsigned short)((to_read + SECTOR - 1) / SECTOR);
*(unsigned char*)(bootsector+14) = bios_device_code;
*(unsigned char*)(bootsector+15) = start_track;
*(unsigned char*)(bootsector+16) = start_head;
*(unsigned char*)(bootsector+17) = start_sector;
#else
*(unsigned char*)(bootsector+4) = (total_tracks) & 0xff;
*(unsigned char*)(bootsector+5) = (total_tracks >> 8) & 0xff;
*(unsigned char*)(bootsector+6) = (total_heads) & 0xff;
*(unsigned char*)(bootsector+7) = (total_heads >> 8) & 0xff;
*(unsigned char*)(bootsector+8) = (total_sectors) & 0xff;
*(unsigned char*)(bootsector+9) = (total_sectors >> 8) & 0xff;
*(unsigned char*)(bootsector+10) = (setup_size) & 0xff;
*(unsigned char*)(bootsector+11) = (setup_size >> 8) & 0xff;
*(unsigned char*)(bootsector+12) = system_sectors & 0xff;
*(unsigned char*)(bootsector+13) = (system_sectors >> 8) & 0xff;
*(unsigned char*)(bootsector+14) = bios_device_code;
*(unsigned char*)(bootsector+15) = start_track;
*(unsigned char*)(bootsector+16) = start_head;
*(unsigned char*)(bootsector+17) = start_sector;
#endif
write (fd_out, bootsector, SECTOR);
if ((fd = open (argv[2], O_RDONLY|O_BINARY, 0)) < 0)
die ("Unable to open setup file.");
do
{
if ((bytes_read = read (fd, setupsector, SECTOR)) < 0)
die ("Unable to read setup.");
if (bytes_read > 0)
write (fd_out, setupsector, SECTOR);
} while (bytes_read > 0);
close (fd);
if ((fd = open (argv[3], O_RDONLY|O_BINARY, 0)) < 0)
die ("Unable to open system file.");
while (to_read > 0)
{
int l;
l = (to_read < SECTOR)?to_read:SECTOR;
if ((bytes_read = read (fd, setupsector, l)) != l)
die ("Unable to read system.");
write (fd_out, setupsector, l);
to_read -= l;
}
close (fd);
close (fd_out);
return 0;
}

70
sections Executable file
View File

@ -0,0 +1,70 @@
/* $Id: sections 5834 2013-10-08 17:04:08Z os $ */
SECTIONS
{
. = 0x100000; /* Startadresse des Systems */
.text :
{
*(".text")
*(".text.*")
*(".text$")
*(".init")
*(".fini")
*(".gnu.linkonce.*")
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
KEEP (*(".ctors"))
KEEP (*(".ctor"))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array))
KEEP (*(".dtors"))
KEEP (*(".dtor"))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.data :
{
*(".data")
*(".data$")
*(".rodata")
*(".rodata.*")
*(".got")
*(".got.plt")
*(".eh_frame")
*(".eh_fram")
*(".jcr")
*(".note.*")
}
.bss :
{
___BSS_START__ = .;
*(".bss")
*(".bss.*")
___BSS_END__ = .;
}
/*
/DISCARD/ :
{
*(".note")
*(".comment")
*(".debug_line")
*(".debug_info")
*(".debug_abbrev")
*(".debug_aranges")
}
*/
}

284
startup.asm Executable file
View File

@ -0,0 +1,284 @@
;******************************************************************************
;* *
;* 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. *
;* *
;* Autor: Olaf Spinczyk, TU Dortmund *
;******************************************************************************
; 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 _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:
push eax
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
add esp,4 ; Parameter vom Stack entfernen
pop edx ; fluechtige Register wieder herstellen
pop ecx
pop eax
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
[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)
gdt_48:
dw 0x18 ; GDT Limit=24, 3 GDT Eintraege
dd gdt ; Physikalische Adresse der GDT