Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: bscreator am 03. July 2007, 11:54
-
Hi OS-Coder,
hab jetzt mal mein OS, wenn man das so nennen kann, so umgeschrieben, dass es im PM starten sollte. Dazu hab ich den Bootloader aus Lowlevel Ausgabe 1 genommen, ein paar unnötige Dinge entfernt und den kurzen PMode-Code von Stefan Marcik verwendet, dann beides kombiniert.
Jedoch wird jetzt gar keine Meldung mehr ausgegeben.
Könnt ihr mir sagen, wo der Fehler im Bootloader liegt ?
Bootcode:
[BITS 16]
ORG 0x7C00 ;Startadresse
start:
cli ;Keine Interrupts!
mov [bootdrv],dl ;Bootlaufwerk aus DL speichern
call load ;Lade unseren Kernel
lgdt [gdtr]
mov eax,cr0
or al,1
mov cr0,eax
jmp codesel:PMode
;Springe zu diesem Kernel
mov ax,0x1000 ;Die Adresse des Programms
mov es,ax ;Segmentregister updaten
mov ds,ax
push ax
mov ax,0
push ax
retf
[BITS 32]
PMode:
mov ax,datasel
mov ds,ax
mov ss,ax
mov esp,0x90000
jmp $
gdtr:
dw gdt_end-gdt-1
dd gdt
gdt:
dd 0,0
codesel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x9A
db 0xCF
db 0x00
datasel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x92
db 0xCF
db 0x00
gdt_end:
;--------------------------
;Funktionen und Variablen
;--------------------------
bootdrv db 0
loadmsg db "Loading...",13,10,0
;Einen String ausgeben:
putstr:
lodsb ;Byte laden
or al,al
jz short putstrd ;0-Byte? -> Ende!
mov ah,0x0E ;Funktion 0x0E
mov bx,0x0007 ;Attribut-Byte (wird nicht bentigt)
int 0x10 ;schreiben
jmp putstr ;Nchstes Byte
putstrd:
retn
;Lade den Kernel vom Bootlaufwerk
load:
;Diskdrive reset (Interrupt 13h, 0)
push ds ;Sichere DS
mov ax,0 ;Die gewnschte Funktion (reset)
mov dl,[bootdrv] ;Dieses Laufwerk ist gewnscht
int 13h ;den Interrupt ausfhren
pop ds ;DS wiederherstellen
jc load ;Geht nicht? -> Nochmal!
load1:
mov ax,0x1000 ;ES:BX=10000
mov es,ax
mov bx,0
;Sektoren lesen (Interrupt 13h, 2)
mov ah,2 ;Funktion 2 (Lesen)
mov al,5 ;Lese 5 Sektoren
mov cx,2 ;Zylinder=0, Sektor=2
mov dx,0 ;Kopf=0, Laufwerk=0
int 13h ;ES:BX=Daten vom Laufwerk
jc load1 ;Fehler? Nochmal!
mov si,loadmsg
call putstr ;Meldung ausgeben
retn
times 512-($-$$)-2 db 0 ;Dateilnge: 512 Bytes
dw 0AA55h ;Bootsignatur
Ich schätz mal, dass der Fehler bei dem jmp $ liegt, jedoch hab ich
keine Ahnung, wo ich sonst hinspringen soll.
Beim gdt_end hat im Code ein Doppelpunkt gefehlt, oder ?
(Da sonst von NASM eine Fehlermeldung "Label alone" erscheint)
Kernel-Code
mov ax,1000h ;Segmentregister updaten
mov ds,ax
mov es,ax
START:
;--------------------------
;Funktionen und Variablen
;--------------------------
mov BYTE [DS:0B8000h],'P' ;Leuchtendes P ausgeben
mov BYTE [DS:0B8001h],1Bh
Traumszenario:
Der Bootloader ladet den Kernel, ähnlich wie in Lowlevel-Ausgabe 1, der Kernel gibt lediglich ein P aus, um zu zeigen, dass der Vorgang erfolgreich war.
Vielen Dank im Vorraus
-
Ich habe mir jetzt nicht alles genau angeschaut. Aber der Kernel soll nach 1000h:0000h geladen werden, oder? Dann müsstst du nach 10000h springen (Seg*16+Offet). Aber du solltest ganz oben ds und es setzen, da die nicht bei allen PC-Starts gleich sind. Dazwischen sind vielleicht auch noch Fehler, aber keine Lust das mir jetzt alles genau anzugucken. ;-)
bitmaster
-
Also dieser Teil kommt mir etwas seltsam vor, der "Sprung" wird ja garnie ausgeführt?
jmp codesel:PMode
;Springe zu diesem Kernel
mov ax,0x1000 ;Die Adresse des Programms
mov es,ax ;Segmentregister updaten
mov ds,ax
push ax
mov ax,0
push ax
retf
[BITS 32]
PMode:
mov ax,datasel
mov ds,ax
mov ss,ax
mov esp,0x90000
jmp $
Und beim jmp$ dort bleibt er ja hängen. Das müsste eher ein jmp 10000h sein.
Und ein hlt / jmp$ im "Kernel" könnte auch nicht schaden, denn sonst rennt er direkt ins Verderben, wenn er nicht aufgehalten wird ;-)
-
Sorry, glaub ich sollte meine Fragen konkreter stellen.
Ich hab irgendwie keine Ahnung, wie ich meinen Kernel mit diesem Bootloader laden soll.
Mein Kernel wird ja mit dem Code
;Sektoren lesen (Interrupt 13h, 2)
mov ah,2 ;Funktion 2 (Lesen)
mov al,5 ;Lese 5 Sektoren
mov cx,2 ;Zylinder=0, Sektor=2
mov dx,0 ;Kopf=0, Laufwerk=0
int 13h ;ES:BX=Daten vom Laufwerk
jc load1 ;Fehler? Nochmal!
mov si,loadmsg
call putstr ;Meldung ausgeben
retn
geladen (von LowLevel Ausgabe 1). Wie allerdings jeder sieht, verwendet dieser Interrupts. Allerdings kann ich ja den Kernel im oberen 16-Bit-Code ( [BITS 16] )nicht laden, da ja dann der darauffolgende 32-Bit-Code ( BITS 32] ) nicht mehr ausgeführt wird, weil der Compiler dann gleich zum Kernel-Code springt, oder ?
Und im unteren 32-Bit-Code kann ich den Kernel ja auch nicht laden, weil ja 32-Bit-Code Protected Mode bedeutet und dort keine Interrupts erlaubt sind, oder ?
Also kann ich den Kernel
a) im 16-Bit-Teil nicht laden, weil dann der Protected Mode nicht aktiviert (bzw. übersprungen) wird und
b) im 32-Bit-Teil nicht laden, weil im PMode keine Interrupts erlaubt sind
Oder bring ich da was durcheinander ?
-
PS : Was meinst du mit
Rennt direkt ins Verderben ?
Wenn kein Code mehr folgt, wird doch nichts mehr ausgeführt, oder ?
-
Wenn nichts mehr folgt, was du als Code ansehen würdest, wird eben das als Code ausgeführt, was im Speicher mehr oder weniger zufällig dahinter steht.
-
OK, is klar. Aber wie kann ich meine Hauptprobleme a bzw. b (siehe oben) lösen ?
Irgendwie muss ich ja den Kernel ohne Verzicht auf Protected Mode laden können.
-
Oder bring ich da was durcheinander ?
Genau ;-)
Also nach dem Booten läuft der Prozessor im Realmode (16-Bit). Das tut er so lange, bis du den Protected mode durch setzen des PM-Bits im CR0 aktivierst. Aber soweit sind wir noch nicht.
Du musst den Kernel ja jetzt nun aus dem Realmode heraus von der Diskette laden. Da du dort nix >1MB adressieren kannst, musst du ihn ja nun irgendwo im ersten MB ablegen. Das dürfte kein Problem sein. Wenn du das geschafft hast, setzt du das PM-Bit, lädst du die GDT (Ich würde dir zu einem flachen Speichermodell, mit 2 deskriptoren von 0-4GB raten), und machst einen FAR-Jump ins Code-Segment.Wenn das klappt ist der kritische Teil überstanden ;-) Jetzt musst du noch den Datenselektor in ds, es und ss laden. Danach nur noch die Adresse anspringen, an die du den kernel vorhin geladen hast. Dafür müsste eigentlich ein normaler Jump tun. Dieses Gefrickel mit dem ret brauchts da wmnat nicht.
Und wie gesagt, ein jmp $ am Ende ;-)
-
Vielen Dank, das hat mir sehr geholfen :-D. Mit
Datenselektor in ds, es und ss laden
meinst du
mov ax,datasel
mov ds,ax
mov es,ax
mov ss,ax
,oder?
-
Genau das meine ich :-D
-
Also, Ich hab jetzt mal alles noch mals versucht und seltsamerweise wird nicht mal die Message 'Loading...' mit der Fkt. putstr ausgegeben.
Der Code ist von mir jetzt gegliedert und ziemlich übersichtlich.
Desweiteren ist er relativ leicht verständlich aber wie gesagt der Teufel steckt im Detail.
Liegt der Fehler vielleicht bei
a) den für mich unverständlichen Befehlen
codesel equ $-gdt und datasel equ $-gdt ?
b) dem komischen Label am Ende des Codes gdt_end: ?
[BITS 16] ;Zuerst der 16-Bit-Code
ORG 0x7C00 ;Startadresse
start:
mov [bootdrv],dl ;Bootlaufwerk aus DL speichern
call load ;Kernel von Diskette laden
mov ax, 0x1000 ;Die Adresse des Kernels
mov es, ax
mov ds, ax
push ax
mov ax, 0
push ax
;retf
cli
lgdt [gdtr]
mov eax, cr0
or al, 1
mov cr0, eax ;Setzen des PM-Bits
jmp codesel:PMode ;FAR Jump ins Codesegment
[BITS 32]
PMode:
mov ax, datasel ;Datenselektor in ds,es und ss laden
mov ds, ax
mov ss, ax
mov esp, 0x90000
jmp 0x10000 ;Sprung zur Adresse des Kernel
;Variablen
bootdrv db 0
loadmsg db 'Loading...',13,10,0
;------------------------
;Funktion String ausgeben
putstr:
lodsb
or al,al
jz short putstrd
mov ah,0x0E
mov bx,0x0007
int 0x10
jmp putstr
putstrd:
retn
;Ende Funktion String ausgeben
;---------------------
;Funktion Kernel laden
load:
;Zuerst mit Interrupt 13h, Funktion 0 ein Diskdrive-Reset
push ds
mov ax, 0
mov dl, [bootdrv]
int 13h
pop ds
jc load
loadl:
mov ax, 0x1000 ;ES:BX=10000
mov es, ax
mov bx, 0
;Sektoren lesen (Interrupt 13h, 2)
mov ah, 2
mov al, 5
mov cx, 2
mov dx, 0
int 13h
jc loadl
mov si, loadmsg
call putstr ;Meldung 'Loading...' ausgeben
retn
;Ende Funktion Kernel laden
gdtr:
dw gdt_end-gdt-1
dd gdt
gdt:
dd 0,0
codesel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x9A
db 0xCF
db 0x00
datasel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x92
db 0xCF
db 0x00
gdt_end:
times 512-($-$$)-2 db 0
dw 0AA55h
Vielen Dank für jeden einzelnen Blick, den ihr darauf werft
-
Ich schätze mal, dass das Problem ist, das pushstr als 32-Bit assembliert wird aber noch im 16-Bit RM aufgerufen wird.
Du müsstest also nur den teil
[BITS 32]
PMode:
mov ax, datasel ;Datenselektor in ds,es und ss laden
mov ds, ax
mov ss, ax
mov esp, 0x90000
jmp 0x10000 ;Sprung zur Adresse des Kernel
hinter deine pushstr(und load) setzen oder vor pushstr wieder auf 16-Bit umstellen( [BITS 16] ).
Edit:
Als FreakyPenguin von dem "Gefrickel mit dem ret" geredet hat, hat er nicht nur das retf selbst gemeint sondern den kompletten Part:
mov ax, 0x1000 ;Die Adresse des Kernels
mov es, ax
mov ds, ax
push ax
mov ax, 0
push ax
retf
der ohne ret nicht nur nutzlos ist sondern auch Fehler verursacht weil das DS & ES Register mit unpassenden werten belegt werden.
-
[BITS 16] ;Zuerst der 16-Bit-Code
ORG 0x7C00 ;Startadresse
start:
xor ax,ax
mov ds,ax ;da ds und es nicht auf allen PCs standardmäßig Null sind
mov es,ax
mov ax,0100h
mov ss,ax
mov sp,6BFEh ;viele machen den Stack nach 2000h:0000h aber ich glaube das mag Virtual PC nicht
mov [bootdrv],dl ;Bootlaufwerk aus DL speichern
call load ;Kernel von Diskette laden
cli
lgdt [gdtr]
mov eax, cr0
or al, 1
mov cr0, eax ;Setzen des PM-Bits
jmp codesel:PMode ;FAR Jump ins Codesegment
;Variablen
bootdrv db 0
loadmsg db 'Loading...',13,10,0
;------------------------
;Funktion String ausgeben
putstr:
lodsb
or al,al
jz short putstrd
mov ah,0x0E
mov bx,0x0007
int 0x10
jmp putstr
putstrd:
retn
;Ende Funktion String ausgeben
;---------------------
;Funktion Kernel laden
load:
;Zuerst mit Interrupt 13h, Funktion 0 ein Diskdrive-Reset
push ds
mov ax, 0
mov dl, [bootdrv]
int 13h
pop ds
jc load
loadl:
mov ax, 0x1000 ;ES:BX=10000
mov es, ax
mov bx, 0
;Sektoren lesen (Interrupt 13h, 2)
mov ah, 2
mov al, 5
mov cx, 2
mov dx, 0
int 13h
jc loadl
mov si, loadmsg
call putstr ;Meldung 'Loading...' ausgeben
retn
;Ende Funktion Kernel laden
[BITS 32]
PMode:
mov ax, datasel ;Datenselektor in ds,es und ss laden
mov ds, ax
mov ss, ax
mov esp, 0x90000
jmp 0x10000 ;Sprung zur Adresse des Kernel
gdtr:
dw gdt_end-gdt-1
dd gdt
gdt:
dd 0,0
codesel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x9A
db 0xCF
db 0x00
datasel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x92
db 0xCF
db 0x00
gdt_end:
times 512-($-$$)-2 db 0
dw 0AA55h
So müsste es richtig sein. Den Code:
mov ax, 0x1000 ;Die Adresse des Kernels
mov es, ax
mov ds, ax
push ax
mov ax, 0
push ax
retf
brauchst du ja nicht, sonst würdest du ja vor den Protected-Mode Aufruf zum Kernel springen.
bitmaster
-
Vielen Dank, das war schon mal ein großer Schritt nach vorn. Die Message "Loading..." wird jetzt schon mal ausgegeben.
Jedoch erscheint kurz nach der Message bei Virtual PC 2007 die Meldung
"Nicht behebbarer Prozessorfehler, der Prozessor wird zurückgesetzt"
und mit VMWare die Meldung
"Virtual machine kernel stack fault (hardware reset) ... a bug in the operating system"
Sorry, aber ich werd das Gefühl nicht los, dass etwas mit dem gdt_end:
nicht stimmt...
-
ich hab den code ma getestet und bei mir gibt es keine fehler(zu mindest mit nem "jmp $" kernel nicht). deshalb meine frage:
wie sieht denn dein kernel aus? hast du immer noch das kernel vom Anfang?(ich frage weil ich mir da keinen Stack-Fault vorstellen kann)
da fehlt nämlich ein "jmp $" am ende und die Segmentregister werden noch mit den RM-Werten geladen.
das müsste gehen:
kernel_start:
mov BYTE [DS:0B8000h],'P' ;Leuchtendes P ausgeben
mov BYTE [DS:0B8001h],1Bh
jmp $ ; Aufhängen/ Anhalten
Sorry, aber ich werd das Gefühl nicht los, dass etwas mit dem gdt_end:
nicht stimmt...
das stimmt schon so. Das label wird benoetigt um die groesse der GDT zu berechnen(addr[ende]-addr[anfang]).
-
Du solltest irgendwann mal einen Stack aufsetzen, oder hab ich das jetzt übersehen?
-
Du solltest irgendwann mal einen Stack aufsetzen, oder hab ich das jetzt übersehen?
mov ss, ax
mov esp, 0x90000
Damit wird doch ein Stack eingerichtet, oder was meinst du?
bitmaster
-
Also meinen Kernel hab ich jetzt umgeschrieben :
kernel_start:
mov BYTE [DS:0B8000h],'P'
mov BYTE [DS:0B8001h],1Bh
jmp $
Aber so gehts auch nicht. Kann an der Behauptung mit dem Stack nicht doch was dran sein, weil der Stack ja schließlich erst im Protected Mode eingerichtet wird und ich ja schon den Stack im Real Mode verwende ?
-
Achso, ihr meint den Stack im Rm, den habe ich ja ganz vergessen. So, ich habe den Code oben editiert. Jetzt müsste es aber stimmen.
bitmaster
-
So, ich war mal so fleißig und habe es ausprobiert. Zumindest unter VMware funktioniert es. Hier der Code:
boot.asm:
[BITS 16] ;Zuerst der 16-Bit-Code
ORG 0x7C00 ;Startadresse
start:
xor ax,ax
mov ds,ax ;da ds und es nicht auf allen PCs standardmäßig Null sind
mov es,ax
mov ax,0100h
mov ss,ax
mov sp,6BFEh ;viele machen den Stack nach 2000h:0000h aber ich glaube das mag Virtual PC nicht
mov [bootdrv],dl ;Bootlaufwerk aus DL speichern
call load ;Kernel von Diskette laden
cli
lgdt [gdtr]
mov eax, cr0
or al, 1
mov cr0, eax ;Setzen des PM-Bits
jmp codesel:PMode ;FAR Jump ins Codesegment
;Variablen
bootdrv db 0
loadmsg db 'Loading...',13,10,0
;------------------------
;Funktion String ausgeben
putstr:
lodsb
or al,al
jz short putstrd
mov ah,0x0E
mov bx,0x0007
int 0x10
jmp putstr
putstrd:
retn
;Ende Funktion String ausgeben
;---------------------
;Funktion Kernel laden
load:
;Zuerst mit Interrupt 13h, Funktion 0 ein Diskdrive-Reset
push ds
mov ax, 0
mov dl, [bootdrv]
int 13h
pop ds
jc load
loadl:
mov ax, 0x1000 ;ES:BX=10000
mov es, ax
mov bx, 0
;Sektoren lesen (Interrupt 13h, 2)
mov ah, 2
mov al, 5
mov cx, 2
mov dx, 0
int 13h
jc loadl
mov si, loadmsg
call putstr ;Meldung 'Loading...' ausgeben
retn
;Ende Funktion Kernel laden
[BITS 32]
PMode:
mov ax, datasel ;Datenselektor in ds,es und ss laden
mov ds, ax
mov es, ax
mov ss, ax
mov esp, 0x90000
jmp 0x10000 ;Sprung zur Adresse des Kernel
gdtr:
dw gdt_end-gdt-1
dd gdt
gdt:
dd 0,0
codesel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x9A
db 0xCF
db 0x00
datasel equ $-gdt
dw 0xFFFF
dw 0x0000
db 0x00
db 0x92
db 0xCF
db 0x00
gdt_end:
times 512-($-$$)-2 db 0
dw 0AA55h
kernel.asm
[BITS 32]
org 10000h
Start:
mov edi,0B8000h+80*2
mov esi,Msg
.2:
lodsb
or al,al
jz .1
stosb
mov al,07h
stosb
jmp .2
.1:
jmp $ ;Endlosschleife
Msg db "Ich bin im Protected-Mode :-)",0
Und dann:
nasmw boot.asm -o boot.bin
nasmw kernel.asm -o kernel.bin
copy boot.bin+kernel.bin=image.img
Dann die image.img mittels VMware laden und staunen. ^^
bitmaster