Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: streetrunner am 17. March 2014, 20:17
-
Guten Abend,
ich bin gerade dabei einen Startup-Code zu schreiben um in den Long-Mode zu kommen. Dabei bin ich auf einen Fehler gestoßen zu dem mir überhaubt nicht einfällt wie dieser zu stande kommen könnte. Es geht sich um folgendes: So bald ich diesen Code
asm volatile("movl %cr0, %eax");
asm volatile("or $0x80000000, %eax");
asm volatile("movl %eax, %cr0");
ausführe bricht mir qemu mit einem GP-Fault ab. Hier mal die interessanten Zeilen des Logs:
0: v=0d e=0010 i=0 cpl=0 IP=0008:0000000000100ba2 pc=0000000000100ba2 SP=0010:00000000433385e6 env->regs[R_EAX]=000000002bb00fe3
EAX=2bb00fe3 EBX=00009500 ECX=c000007b EDX=a30c3147
ESI=93416461 EDI=00129000 EBP=00104fc0 ESP=433385e6
EIP=00100ba2 EFL=00000003 [------C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00104fa8 00000017
IDT= 00000000 000003ff
CR0=80000011 CR2=00000000 CR3=08000000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000001 CCD=2bb00fe3 CCO=INCL
EFER=0000000000000000
So bald ich aber "asm volatile("movl %eax, %cr0")" auskommentiere läuft alles wie geschmiert (außer dass das Paging natürlich nicht aktiviert wird). Ein objdump hat mir bis jetzt auch nicht weitergeholfen, denn die Speicheraddresse in EIP liegt nicht mehr in meinem Code, sondern irgendwo dahinter (aber immer noch im .text Segment).
Hat irgendjemand eine Ahnung woran dass liegen könnte?
Gruß,
Streetrunner
PS: Der Fehler ist natürlich reproduzierbar
-
Nach dem Ende des asm-Blocks garantiert dir kein Mensch, welchen Inhalt die Register haben. Du hast dem Compiler außerdem nicht mitgeteilt, dass du eax benutzt, d.h. er könnte darin eigentlich noch andere Daten gespeichert haben. Mach einen einzigen asm-Block draus (als einen einzigen langen C-String mit Zeilenumbrüchen drin) und benutz die Clobber-Liste:
asm volatile(
"movl %cr0, %eax\n"
"or $0x80000000, %eax\n"
"movl %eax, %cr0"
: : : "eax");
-
Danke für deine Hilfe,
leider ergibt sich damit der gleiche Fehler. Nach objdump gibt es keinen Unterschied zwischen den beiden Versionen.
Gruß,
Streetrunner
-
Ist es gewollt, dass sich das Page Directory an Adresse 0x08000000 (CR3) befindet? Das ist nämlich 128 MB und soviel hat QEMU per default nicht. Wenn es nicht gewollt ist, liegt die Schuld vermutlich an der physischen Speicherverwaltung.
-
Ja ist es. An dieser Stelle liegt die PML4T. Ich starte qemu mit "-m 2048", das sollte reichen. Außerdem teste ich ob der Speicher auch wirklich frei ist. Wo ich mir auch keinen Reim drauf machen kann ist, warum ESP so verdammt hoch ist. Bei der aktuellen Version ist ESP = 0xa680895f, also über 2GB. Vllt. hilft die Gegenüberstellung von Log und objdump weiter:
0: v=0d e=0010 i=0 cpl=0 IP=0008:0000000000100ba2 pc=0000000000100ba2 SP=0010:00000000a680895f env->regs[R_EAX]=000000006d3b16ab
EAX=6d3b16ab EBX=00009500 ECX=bffffffd EDX=193cb72b
ESI=9bbf37e6 EDI=0012b000 EBP=00106fc0 ESP=a680895f
EIP=00100ba2 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00127000 00000017
IDT= 00000000 000003ff
CR0=80000011 CR2=00000000 CR3=08000000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=6d3b16ab CCO=INCL
EFER=0000000000000000
00100691 <StartImage>:
100691: 55 push %ebp
100692: 89 e5 mov %esp,%ebp
100694: 83 ec 28 sub $0x28,%esp
100697: b8 10 00 00 00 mov $0x10,%eax
10069c: 8e d8 mov %eax,%ds
10069e: 8e c0 mov %eax,%es
1006a0: 8e e0 mov %eax,%fs
1006a2: 8e e8 mov %eax,%gs
1006a4: 0f 20 e0 mov %cr4,%eax
1006a7: 83 c8 20 or $0x20,%eax
1006aa: 0f 22 e0 mov %eax,%cr4
1006ad: b9 80 00 00 c0 mov $0xc0000080,%ecx
1006b2: 0f 32 rdmsr
1006b4: 0d 00 00 00 80 or $0x80000000,%eax
1006b9: 0f 30 wrmsr
1006bb: b8 00 00 00 08 mov $0x8000000,%eax
1006c0: 0f 22 d8 mov %eax,%cr3
1006c3: 0f 20 c0 mov %cr0,%eax
1006c6: 0d 00 00 00 80 or $0x80000000,%eax
1006cb: 0f 22 c0 mov %eax,%cr0
1006ce: a1 18 70 12 00 mov 0x127018,%eax
1006d3: 89 45 f0 mov %eax,-0x10(%ebp)
1006d6: c7 45 f4 08 00 00 00 movl $0x8,-0xc(%ebp)
1006dd: f4 hlt
1006de: c7 04 24 8f 21 10 00 movl $0x10218f,(%esp)
1006e5: e8 a3 00 00 00 call 10078d <print>
1006ea: 90 nop
1006eb: c9 leave
1006ec: c3 ret
1006ed: 66 90 xchg %ax,%ax
1006ef: 90 nop
Wie man aus den Daten entnehmen kann, ist CR3 = 0x8000000. Der Code läuft also mindestens bis Zeile 0x1006c0. Gleichzeitig bis maximal 0x1006dd, denn da sollte Schluss sein. Das letzte mal wird EBP in Zeile 0x100692 geändert, da war ESP also noch normal. Also muss irgendwo dazwischen ESP um einen sehr großen Wert geändert worden sein, nur wo?
Gruß,
Streetrunner
-
Also zwei Sachen fallen mir noch auf: Der Errorcode 0010 besagt, dass was mit dem GDT-Deskriptor 0x10 nicht stimmt. Möglicherweise ist was in der GDT nicht korrekt. Allerdings erklärt das nicht das übrige Verhalten (Wert in ESP).
Ich habe deswegen mal in meinen 64-Bit-Code geschaut, und mir ist aufgefallen, dass du in dem MSR Bit 31 (0x80000000) setzt. Korrekt wäre glaube ich (http://www.lowlevel.eu/wiki/Long_Mode) Bit 8 (0x100). Möglicherweise erklärt das das komische Verhalten.
Wenn das funktioniert, landest du mit deinem Code übrigens im Compatibility Mode. (Falls dir das nicht bewusst ist.)
-
Korrekt wäre glaube ich (http://www.lowlevel.eu/wiki/Long_Mode) Bit 8 (0x100). Möglicherweise erklärt das das komische Verhalten.
Da hat Jidder recht. Und mit nicht eingeschaltetem LM-Bit wird natürlich eine ganz andere Paging Tabelle erwartet. Womit man dann keiner Adresse mehr trauen kann.
Nach dem "movl eax, cr0" wird also vermutlich nichts mehr von dem ausgeführt was da in deinem Listing steht, sondern irgend ein teil deiner Pagingtabelle als Code interpretiert.
Was dann auch erklärt warum er nicht bei 0x1006dd anhält sondern bis 0x100ba2 weiter läuft.
-
Wenn das funktioniert, landest du mit deinem Code übrigens im Compatibility Mode.
Ja, ich weiß. Von da aus geht es dann weiter in den Long Mode. Der Code dafür ist aber auskommentiert, da er nur unnötig verwirrung stiften würde und auch erst nach der Zeile mit dem Fehler ausgeführt werden würde.
Die Idee mit dem falschen Wert im MSR trifft genau ins Schwarze, das behebt den Fehler komplett. Danke an dieser Stelle. Ich weiß nicht was mich da geritten hat (vllt. Verwechslung von 8tes Bit und 0x80000000??). Werde auch gleich mal auf die Suche gehen was ich denn da fälschlicherweise aktiviert habe.
Gruß,
Streetrunner