Autor Thema: Higher half kernel, general protection fault  (Gelesen 6046 mal)

Thalhammer

  • Beiträge: 8
    • Profil anzeigen
Gespeichert
« am: 09. May 2014, 23:46 »
Hallo,
Ich hab ein Problem:
Mein Kernel läuft und bin auch schon recht weit (PMM,VMM usw läuft).
Allerdings momentan noch als Lowerhalf kernel.
Jetzt wollte ich ihn so umschreiben das er ein Higherhalf kernel ist, weil mir das besser gefällt und ja auch ne menge Vorteile hat.
Ich hab mir dazu ein neues Projekt erstellt, das mir mit dem GDT Trick meinen Kernel an 0xC0000000 mapped und in die Init funktion springt.
Siehe auch hier:http://wiki.osdev.org/Higher_Half_With_GDT
Meine C init ist noch leer (dauerschleife) aber das ändert sich noch.
Nutze ich den ASM Code 1:1 funktioniert das auch perfekt.
Der Kernel startet, übernimmt die GDT und springt in meine Mainfunktion, wo er in einer endlos schleife stoppt (wie beabsichtigt).
Das Problem ist nun das der ASM teil in NASM Syntax ist, der Rest meines Kernels aber in GNU AS syntax.
Versuche ich nun den code in gas syntax zu ändern stürzt das program mit einem Triple fault ab.
Original Code:
[BITS 32]       ; 32 bit code
[section .text] ; keep NASM happy
[global start]  ; make 'start' function global
[extern init]  ; our C kernel main
 
; Multiboot constants
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
 
; Multiboot header (needed to boot from GRUB)
ALIGN 4
multiboot_header:
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM
 
; the kernel entry point
start:
; here's the trick: we load a GDT with a base address
; of 0x40000000 for the code (0x08) and data (0x10) segments
lgdt [trickgdt]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
 
; jump to the higher half kernel
jmp 0x08:higherhalf
 
higherhalf:
; from now the CPU will translate automatically every address
; by adding the base 0x40000000
 
mov esp, sys_stack ; set up a new stack for our kernel
 
call init ; jump to our C kernel ;)
 
; just a simple protection...
jmp $
 
[global gdt_flush] ; make 'gdt_flush' accessible from C code
[extern gp]        ; tells the assembler to look at C code for 'gp'
 
; this function does the same thing of the 'start' one, this time with
; the real GDT
gdt_flush:
lgdt [gp]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:flush2
 
flush2:
ret
 
[section .setup] ; tells the assembler to include this data in the '.setup' section
 
trickgdt:
dw gdt_end - gdt - 1 ; size of the GDT
dd gdt ; linear address of GDT
 
gdt:
dd 0, 0 ; null gate
db 0xFF, 0xFF, 0, 0, 0, 10011010b, 11001111b, 0x40 ; code selector 0x08: base 0x40000000, limit 0xFFFFFFFF, type 0x9A, granularity 0xCF
db 0xFF, 0xFF, 0, 0, 0, 10010010b, 11001111b, 0x40 ; data selector 0x10: base 0x40000000, limit 0xFFFFFFFF, type 0x92, granularity 0xCF
 
gdt_end:
 
[section .bss]
 
resb 0x1000
sys_stack:
; our kernel stack
Mein übersetzungsversuch:
.section multiboot
#define MB_MAGIC 0x1badb002
#define MB_FLAGS 0x0
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
 
// Der Multiboot-Header
.align 4
.int    MB_MAGIC
.int    MB_FLAGS
.int    MB_CHECKSUM

.section .text
.global start
.extern init

start:
cli
lgdt trickgdt
mov 0x10,%ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss

jmp higherhalf

higherhalf:
mov sys_stack, %esp
call init;
protection:
cli
hlt
jmp protection

.global gdt_flush
.extern gp

gdt_flush:
lgdt gp
mov 0x10,%ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
jmp flush2

flush2:
ret

.section .setup
trickgdt:
.short (gdt_end - gdt -1)
.int gdt

gdt:
# Null gate
.quad 0
# Code segment
.byte 0xff
.byte 0xff
.byte 0x00
.byte 0x00
.byte 0x00
.byte 0b10011010
.byte 0b11001111
.byte 0x40
# Data segment
.byte 0xff
.byte 0xff
.byte 0x00
.byte 0x00
.byte 0x00
.byte 0b10010010
.byte 0b11001111
.byte 0x40
gdt_end:

.section .bss
# Kernel stack
.space 0x1000
sys_stack:
Der Code kompiliert problemlos, allerdings stürzt das Program beim neuladen des ds registers in start (mov %ax, %ds) mit einem General protection fault ab (der wiederum nicht abgefangen wird, da die IDT ja nicht eingerichtet is -->Double fault ---> Triple fault).
Kann vielleicht mal einer drüberschauen was ich falsch mache ?
Meiner Meinung nach liegt das Problem entweder beim definieren der gdt oder beim laden der gdt (lgdt trickgdt), aber weiter weis ich nicht.
Ich hab auch schon gegoogelt, konnte aber nichts finden und bin auch nach 2 abenden probieren nicht auf eine lösung gestoßen, weshalb ich mich nun an euch wende.
Vielleicht kann ja mal einer der bei syntaxe kennt drüber schauen und mir sagen was ich falsch mache.

Mit freundlichen Grüßen,
Thalhammer

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 10. May 2014, 00:11 »
mov 0x10,%ax muss mov $0x10,%ax heißen, sonst liest er von Speicheradresse 0x10. Selbe Geschichte bei mov sys_stack, %esp. Der Sprung zum Neuladen von CS muss außerdem ein Far Jump sein, also ljmpl $0x08, $higherhalf.
Dieser Text wird unter jedem Beitrag angezeigt.

Thalhammer

  • Beiträge: 8
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 10. May 2014, 10:14 »
Schon mal danke, ich hab das gesagte jetzt geändert, hat aber nichts gebracht, er stürzt immernoch mit dem gleichen fehler ab.
Mein jetziger Stand:
.section multiboot
#define MB_MAGIC 0x1badb002
#define MB_FLAGS 0x0
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
 
// Der Multiboot-Header
.align 4
.int    MB_MAGIC
.int    MB_FLAGS
.int    MB_CHECKSUM

.section .text
.global start
.extern init

start:
cli
lgdt trickgdt
mov $0x10,%ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
ljmpl $0x08,$higherhalf

higherhalf:
mov $sys_stack, %esp
call init;
protection:
cli
hlt
jmp protection

.global gdt_flush
.extern gp

gdt_flush:
lgdt gp
mov $0x10,%ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
jmp $0x08,$flush2

flush2:
ret

.section .setup
trickgdt:
.short (gdt_end - gdt -1)
.int gdt

gdt:
# Null gate
.quad 0
# Code segment
.byte 0xff
.byte 0xff
.byte 0x00
.byte 0x00
.byte 0x00
.byte 0b10011010
.byte 0b11001111
.byte 0x40
# Data segment
.byte 0xff
.byte 0xff
.byte 0x00
.byte 0x00
.byte 0x00
.byte 0b10010010
.byte 0b11001111
.byte 0x40
gdt_end:

.section .bss
# Kernel stack
.space 0x1000
sys_stack:
Wie schon zuvor stürzt er beim neuladen von ds in der start routine ab.

MFG Thalhammer

// EDIT:
Also ich bin jetzt ein klein wenig weiter:
Es liegt definitiv an der zeile lgdt trickgdt.
Ich hab jetzt folgendes gemacht:
Ich hab sowohl in nasm als auch im gas source nach der Zeile ne dauerschleife eingebaut damit das program dort hält.
Jetzt hab ich mir die CPU Register angesehen.
Bei der Nasm steht der Wert für gdt schön braf auf 0x100006 (was der adresse der gdt entspricht, ein dump der adressen 0x100006,0x10000e,0x100010 und 100014 bestätigen das), bei gas hingegen steht er auf 0x0 was eindeutig nicht stimmt.
Ein Dump der region 0x100000 bis 0x10001d was laut dem disassembly des kernels ja die gdt sein sollte gibt hingegen lauter null bytes zurück weshalb vermutlich auch das laden schief geht.
Warum die auf null stehen kann ich mir allerdings nicht erklären.
Ich hab mal die beiden sources gekürzt und zusammen mit einem objdump der binary in ein zip gepackt:
http://dothal.de/files/higherhalf.zip
Vielleicht fällt dir ja was auf das ich übersehe ?

MFG Thalhammer
« Letzte Änderung: 10. May 2014, 16:15 von Thalhammer »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 10. May 2014, 16:41 »
Laut objdump ist die .setup-Section in der GAS-start.o nicht allocatable. Versuch mal .section .setup,"a" (oder ggf. .section .setup,"a",@progbits).
Dieser Text wird unter jedem Beitrag angezeigt.

Thalhammer

  • Beiträge: 8
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 10. May 2014, 17:01 »
Danke Jidder, das war das Problem.
Da wär ich nie drauf gekommen.
Ne erklärung warum das so ist und wieso es in nasm ohne funktioniert hast du nicht zufällig parat oder ?

MFG Thalhammer

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 10. May 2014, 17:05 »
NASM und GAS haben unterschiedliche defaults, wenn die Sektion keine der Standardsektionen (.text, ...) ist. Keine Ahnung aus welchem Grund die sich da unterscheiden. Vermutlich ist das einfach nicht spezifiziert.
Dieser Text wird unter jedem Beitrag angezeigt.

Thalhammer

  • Beiträge: 8
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 10. May 2014, 17:40 »
OK das wusste ich nicht :-D
Wieder was gelernt :-D
Ne kleine Frage hab ich noch, wo ich jetzt nicht weis wieso das so ist:
Im tutorial zu dem GDT Trick (http://wiki.osdev.org/Higher_Half_With_GDT) wird weiter unten noch das erstellen der Pagetable für den Higherhalf erklärt.
Das ist jetzt für mich eher weniger interessant, weil ich VMM ja schon implementiert hab, allerdings frage ich mich wieso die die ersten 4 MB sowohl in lower (also an die physischen adressen) als auch ins higher half (wo er ja hin soll). Wenn ich den Kernel anschliesend wieder ins lower half mappe mach ich mir doch alle vorteile eines Higher half kernels zunichte oder ?

Danke für deine Hilfe :-D

MFG Thalhammer

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 10. May 2014, 17:57 »
Vielleicht war die Intention des Autors dadurch den Videospeicher und die Multibootinfo zu mappen. Möglicherweise will er auch GDT (trickgdt) kurzzeitig nochmal mappen.
Dieser Text wird unter jedem Beitrag angezeigt.

Thalhammer

  • Beiträge: 8
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 12. May 2014, 13:26 »
Also nachdem ich nun weiter fortgeschritten bin hab ich festgestellt das es einen sehr guten grund hat warum der Autor dies gemacht hat:
Offenbar beieinflussen sich GDT und Paging gegenseitig. Das bedeutet Die addresse wurde so wohl von der GDT und der Pagetable verbogen.
Heist also eines davon muss ein 1:1 Mapping enthalten sonst kommt die CPU durch einander. Gleichzeitig muss aber auch vor dem anschalten des Pagings  die adresse stimmen. Aus diesem grund wird alles doppelt gemappt: einmal ins higher half und einmal nicht. Sobald die GDT wieder auf ein normales 1:1 Mapping geändert wurde kann man dieses Mapping im Lowerhalf wieder entfernen. Für die Zeit zwischen Einschalten des Pagings und ausschalten der Trickgdt ist es aber zwingen erforderlich. D.h. der Autor hat alles richtig gemacht, auch wenn ein wenig erklärung warum er das so tut wie er es tut schon nicht schlecht gewesen wäre  :-D.
Naja jetzt läuft mein Kernel jedenfalls auch im HH.

MFG Thalhammer

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 12. May 2014, 13:30 »
Heist also eines davon muss ein 1:1 Mapping enthalten sonst kommt die CPU durch einander.
Das ist der CPU gegenüber eine unfaire Unterstellung. Wer eventuell durcheinanderkommt ist der Programmierer, aber prinzipiell spricht nichts dagegen, Segmentierung (mit Basis != 0) und Paging (ohne Identity Mapping) gleichzeitig zu verwenden. Man muss nur wissen, wie eine Adresse genau übersetzt wird: Eine virtuelle Adresse aus Segment und Offset wird zuerst (mit GDT oder LDT) in eine lineare Adresse übersetzt, die dann wiederum (mit Paging) in eine physische Adresse übersetzt wird.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Thalhammer

  • Beiträge: 8
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 12. May 2014, 14:23 »
Ja ok, stimmt schon aber wie heist es so schön: Es sind immer erstmal die andere schuld :D
Ne im ernst, warum sollte man sowas machen ?
Mir fällt jetzt nichts ein was man damit erreichen kann, das nicht mit Paging alleine auf geht.

MFG Thalhammer

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 12. May 2014, 14:27 »
Segmentierung gab es früher und hat sich aus verschiedenen Gründen nicht durchgesetzt. Wegen der Kompatiblität ist sie aber auf x86-Prozessoren (im 16- und 32-Bit-Modus) voll funktionsfähig. Außerdem kann man damit Speicher auch auf Prozessoren ohne NX-Bit als "nicht ausführbar" markieren.

Oder sie halt für Higher-Half-ohne-Lower-Half-Loader missbrauchen. :-D

 

Einloggen