In den Protected Mode springen die 6745897.
-
So langsam glaube ich den PM zu verstehen
Was passiert aber, wenn ich das hier mache (ist jetzt nicht mehr wirklich von Belang, aber es interessiert mich mal):Ich habe einen Descriptor, dessen physikalische Startadresse 0x20000 ist. Ich bestimme, dass das Segment, dass dadurch beschrieben wird 4GiB groß ist. Was passiert dann? Schließlich könnte man so bytes außerhalb des Hauptspeichers adressieren: Segmentstart 0x20000 + Frei gewählter Offset 0xFFFFFFFFFFFFFFFF > 4 GiB. Wird dann eine Exception beim Adressieren ausgelöst?
-
hackbert schrieb:
Ich habe einen Descriptor, dessen physikalische Startadresse 0x20000 ist. Ich bestimme, dass das Segment, dass dadurch beschrieben wird 4GiB groß ist. Was passiert dann? Schließlich könnte man so bytes außerhalb des Hauptspeichers adressieren: Segmentstart 0x20000 + Frei gewählter Offset 0xFFFFFFFFFFFFFFFF > 4 GiB. Wird dann eine Exception beim Adressieren ausgelöst?
Es wird keine Exception ausgeführt.
Versuchst du beispielsweise den RAM an der Adresse 0x100000010 (4GiB + 10Byte) auszulesen, wird der Prozessor die Bits, die oberhalb der 32Bit sind ignorieren, d.h. in diesem Fall werden die Daten an der Adresse 0x10 gelesen.
-
So. Ich habe meinen Code jetzt wesentlich geändert. Leider erfolgt immernoch nicht das gewünschte Ergebnis.
Das Speicherlayout sieht so aus:
Erster Bootloader wird vom BIOS an 0x7C0 kopiert (Code hier nicht aufgeführt, da diese Arbeit erledigt wird und es keine Probleme gibt)
Zweiter Codeteil wird vom ersten Bootloader an 0x1000 kopiert. Dort erfolgt der Sprung in den PM.
Der dritte Teil befindet sich genau 512 bytes hinter dem zweiten Teil und enthält eine Routine, die 100 mal 'P' auf den Schirm schreiben soll.zweiter Teil
[BITS 16] jmp 1000h:start nop start: mov ax, cs mov ds, ax mov es, ax ;disable pic mov byte al, 0xFF out byte 0x21, al nop out byte 0xA1, al ;Load the gdt cli lgdt [mygdt] ;activate PM mov eax, cr0 or eax, 1 mov cr0, eax jmp to_pmode to_pmode: ;jump to "in_pmode.asm" cli db 0x66 db 0xEA ;Jmp to: dd 0x1200 ;<offset> dw 0000000000001000b ;<selector> alignb 2 mygdt: gdtlimit dw 32 gdtbase1 dw gdt_start gdtbase2 dw 0x1 alignb 2 gdt_start: nulldescriptor times 8 db 0 kerneldescriptor: dw 0xFFFF dw 0 dw 0x9A00 dw 0x00CF vramdescriptor: 0xFFFF dw 0 dw 0x9200 dw 0x00CF stackdescriptor: stacksegsize dw 0xFFFF ;4 stacksegbase1 dw 0x0000 ; stacksegbase2 db 0x09 ; stacksegmisc db 11001111b stacksegbase3 db 0x00 times 512-($-$$) db 0
dritter Teil
[BITS 32] jmp in_pm nop in_pm: sti ;create the selector in ax (we want to select the second entry of the gdt as ds) mov ax, 0000000000010000b mov ds, ax ;create the selector in ax (we want to select the third entry of the gdt as ss) mov ax, 0000000000011000b mov ss, ax mov sp, 0 mov eax, 0xB8000 mov ecx, 100 writechar: mov byte [eax], 'P' ;0x07 inc eax mov byte [eax], 0x41 inc eax loop writechar hang: jmp hang times 4096 db 0
-
Hi.
Also erstmal muss ich eins klarstellen:
Lass bitte das mov cs, ax weg. Mag dein Assembler vielleicht sogar uebersetzen, ist aber in 99,9% aller Faelle absolut sinnfrei (wenn man weiss, was man tut), oder jedoch, wenn wie in deinem Fall falsch eingesetzt, sogar schaedlich.
In dem Moment, wo du CS setzt, liest die CPU im RM bei 0008:xxxx (ab ins Nirvana... ;)) statt bei 0100:xxxx weiter, also Obacht!
cs wird deshalb ueblicherweise nur bei far jumps und calls gesetzt.hackbert schrieb:
Zweiter Codeteil wird vom ersten Bootloader an 0x1000 kopiert. Dort erfolgt der Sprung in den PM.
Der dritte Teil befindet sich genau 512 bytes hinter dem zweiten Teil [...]zweiter Teil
[BITS 16] jmp 1000h:start ; was soll das hier eigentlich? nop start: [...] ;UHHH, wegmachen - bitte :) ;create the selector in ax (we want to select the first entry of the gdt as cs) mov ax, 0000000000001000b mov cs, ax [...] ;create the selector in ax (we want to select the third entry of the gdt as ss) mov ax, 0000000000011000b mov ss, ax mov sp, 0 ; dir ist aber schon klar, dass im 32Bit PM-Mode 32Bit-Register verwendet werden und der Stack nach unten waechst? ;BTW: Die Selektoren musst du _IM PM_ laden. Vorher bringt IMHO nichts. ;jump to "in_pmode.asm" db 0xEA ;Jmp to: dw 0x00000 ;<offset> dw 0000000000001000b ;<selector> ;Preisfrage: Wo willst du eigentlich hin? ;Wenn ich das richtig verstanden habe, liegt Teil 3, wo es eigentlich weitergehen soll, bei 1000h + 512d = 1200h! ;Du springst hier aber nach 2000h ;) [...]
dritter Teil
[BITS 32] jmp in_pm ;Schon wieder dieser Unsinn?? nop in_pm: mov eax, 0xB8000 ;weshalb eax? edi wuerde mal mehr bringen => stosx mov ecx, 100 writechar: mov byte [eax], 'P' inc eax mov byte [eax], 0x41 inc eax loop writechar ; hier koenntest du dann zB. auch einfach rep stosw schreiben... ;) hang: jmp hang times 4096 db 0
@bluecode:
Es gibt auch 32Bit-CPUs, die ueber 4GB adressieren koennen (48Bit). Habe aber ehrlichgesagt keine Ahnung, wie das genau funktioniert.
-
inzwischen habe ich das Problem mit Hilfe eines sehr angagierten Bekannten lösen können. Der Code muss so aussehen:
[BITS 16] jmp 1000h:start nop start: mov ax, cs mov ds, ax mov es, ax ;disable pic mov byte al, 0xFF out byte 0x21, al nop out byte 0xA1, al ;Load the gdt cli lgdt [mygdt] ;activate PM mov eax, cr0 or eax, 1 mov cr0, eax jmp to_pmode to_pmode: ;jump to "in_pmode.asm" db 0x66 db 0xEA ;Jmp to: dd 0x10200 ;<offset> dw 0000000000001000b ;<selector> alignb 2 mygdt: gdtlimit dw 32 gdtbase1 dw gdt_start gdtbase2 dw 0x1 alignb 2 gdt_start: nulldescriptor times 8 db 0 ;kerneldescriptor: ; ksegsize dw 0xFFFF ;4 GiB segsize, see also the first 4 bits of ksegmisc ; ksegbase1 dw 0x0000 ; ; ksegbase2 db 0x00 ; ; ; ;-------------------_________Present Flag 1: present, 0: non-present ; ;------------------/ ________Descriptor Privileg Level, 00, 01, 10, 11 ; ;------------------|/ ______Segment-bit 1: Memory Segment, 0: other segment type ; ;------------------|| / _____Segment type 000: data-r, 001: data-rw, 010 reserved, 011: expand-down, 100 code-x, 101: code-rx, 110: code-x, 111: code-ax ; ;------------------|| |/ __Access bit, set by the CPU ; ;------------------|| || / ; ;------------------|| || | ; ;------------------|| ||\ | ; ;------------------||\||\\| ; ksegacctype db 10011000b ; ;-------------------_________Granularity bit 0: max. segsize=1MB, 1: max. segsize=4GB ; ;------------------/ ________D/B-bit. 0: 286 segment, 1: 386+ segment ; ;------------------|/ _______reserved ; ;------------------||/ ______user-defined ; ;------------------|||/ _____size: bits 16-19 ; ;------------------||||/ ; ;------------------|||||\ ; ;------------------|||||\\ ; ;------------------|||||\\\ ; ksegmisc db 11001111b ; ksegbase3 db 0x00 ; kerneldescriptor: dw 0xFFFF dw 0 dw 0x9A00 dw 0x00CF vramdescriptor: dw 0xFFFF dw 0 dw 0x9200 dw 0x00CF ;vramdescriptor: ; vrsegsize dw 0xFFFF ;4 GiB segsize, see also the first 4 bits of ksegmisc ; vrsegbase1 dw 0x0000 ; ; vrsegbase2 db 0x00 ; ; ; ;-------------------_________Present Flag 1: present, 0: non-present ; ;------------------/ ________Descriptor Privileg Level, 00, 01, 10, 11 ; ;------------------|/ ______Segment-bit 1: Memory Segment, 0: other segment type ; ;------------------|| / _____Segment type 000: data-r, 001: data-rw, 010 reserved, 011: expand-down, 100 code-x, 101: code-rx, 110: code-x, 111: code-ax ; ;------------------|| |/ __Access bit, set by the CPU ; ;------------------|| || / ; ;------------------|| || | ; ;------------------|| ||\ | ; ;------------------||\||\\| ; vrsegacctype db 10010010b ; ;-------------------_________Granularity bit 0: max. segsize=1MB, 1: max. segsize=4GB ; ;------------------/ ________D/B-bit. 0: 286 segment, 1: 386+ segment ; ;------------------|/ _______reserved ; ;------------------||/ ______user-defined ; ;------------------|||/ _____size: bits 16-19 ; ;------------------||||/ ; ;------------------|||||\ ; ;------------------|||||\\ ; ;------------------|||||\\\ ; vrsegmisc db 11001111b ; vrsegbase3 db 0x00 stackdescriptor: stacksegsize dw 0xFFFF ;4 GiB segsize, see also the first 4 bits of ksegmisc stacksegbase1 dw 0x0000 ; stacksegbase2 db 0x09 ; ;-------------------_________Present Flag 1: present, 0: non-present ;------------------/ ________Descriptor Privileg Level, 00, 01, 10, 11 ;------------------|/ ______Segment-bit 1: Memory Segment, 0: other segment type ;------------------|| / _____Segment type 000: data-r, 001: data-rw, 010 reserved, 011: expand-down, 100 code-x, 101: code-rx, 110: code-x, 111: code-ax ;------------------|| |/ __Access bit, set by the CPU ;------------------|| || / ;------------------|| || | ;------------------|| ||\ | ;------------------||\||\\| stacksegacctype db 10010010b ;-------------------_________Granularity bit 0: max. segsize=1MB, 1: max. segsize=4GB ;------------------/ ________D/B-bit. 0: 286 segment, 1: 386+ segment ;------------------|/ _______reserved ;------------------||/ ______user-defined ;------------------|||/ _____size: bits 16-19 ;------------------||||/ ;------------------|||||\ ;------------------|||||\\ ;------------------|||||\\\ stacksegmisc db 11001111b stacksegbase3 db 0x00 times 512-($-$$) db 0
[BITS 32] jmp in_pm nop in_pm: ;create the selector in ax (we want to select the second entry of the gdt as ds) mov ax, 0000000000010000b mov ds, ax ;create the selector in ax (we want to select the third entry of the gdt as ss) mov ax, 0000000000011000b mov ss, ax mov sp, 0 mov eax, 0xB8000 mov ecx, 100 writechar: mov byte [eax], 'P' ;0x07 inc eax mov byte [eax], 0x41 inc eax loop writechar hang: jmp hang times 4096 db 0
-
Nobuo T schrieb:
@bluecode:
Es gibt auch 32Bit-CPUs, die ueber 4GB adressieren koennen (48Bit). Habe aber ehrlichgesagt keine Ahnung, wie das genau funktioniert.Der virtuelle Adressspace ist immer 2^32 bei 32Bit cpu's. Durch die PAE (Physical Address Extension) kann man den physikalische Adressraum auf 64GiB (2^36) erweitern. Dabei wird das imho über Paging gelöst (bei 4MiB pages ist im pagedir nochn paar bits frei), d.h. es geschieht ohne den virtuellen Adressraum zu erweitern.
[edit: wie kommst du auf 48Bit bzw. welcher Prozessor? Meine Antwort bezog sich auf Pentium+]
-
@hackbert:
schau dir lieber nochmal Nobuo T's Kommentare an. Das ist ein wirklich ernst gemeinter Rat...
-
bluecode schrieb:
Nobuo T schrieb:
@bluecode:
Es gibt auch 32Bit-CPUs, die ueber 4GB adressieren koennen (48Bit). Habe aber ehrlichgesagt keine Ahnung, wie das genau funktioniert.Der virtuelle Adressspace ist immer 2^32 bei 32Bit cpu's. Durch die PAE (Physical Address Extension) kann man den physikalische Adressraum auf 64GiB (2^36) erweitern. Dabei wird das imho über Paging gelöst (bei 4MiB pages ist im pagedir nochn paar bits frei), d.h. es geschieht ohne den virtuellen Adressraum zu erweitern.
[edit: wie kommst du auf 48Bit bzw. welcher Prozessor? Meine Antwort bezog sich auf Pentium+]
Ahja, danke.
Mir war so, als haette ich etwas derartiges in einer intel CPU-History gelesen.
KA, ob das tatsaechlich 48Bit waren - kommt mir im Nachhinein betrachtet etwas heftig vor.
AFAIR tauchte dieses feature aber schon vor dem P6 auf... irgendwo zwischen 386DX und 486.whatever...
bluecode schrieb:
Das ist ein wirklich ernst gemeinter Rat...
Was soll das denn heissen?
-
Nobuo T schrieb:
Ahja, danke.
Mir war so, als haette ich etwas derartiges in einer intel CPU-History gelesen.
KA, ob das tatsaechlich 48Bit waren - kommt mir im Nachhinein betrachtet etwas heftig vor.aus http://de.wikipedia.org/wiki/AMD64:
Bei AMD64 ist die Breite einer virtuellen Adresse 48 Bit, das heißt ein Task kann 256 TiB adressieren. Die AMD64-Prozessoren haben zur Zeit 40 Adresspins, physisch können sie also 1 TiB Speicher adressieren. Die physische Adressgrenze beträgt somit 1 TiB pro Task und für alle Tasks zusammen. Spätere Prozessoren können über mehr Adresspins einen größeren ansteuerbaren Arbeitsspeicher bieten, ohne dass Veränderungen an Betriebssystem oder gar Programmen notwendig wären.Nobuo T schrieb:
AFAIR tauchte dieses feature aber schon vor dem P6 auf... irgendwo zwischen 386DX und 486.
whatever...
Laut http://de.wikipedia.org/wiki/PAE erst ab pentium pro.
Nobuo T schrieb:
bluecode schrieb:
Das ist ein wirklich ernst gemeinter Rat...
Was soll das denn heissen?
[edit: Ich drücks lieber doch anders aus]: Naja, er scheint ja meine Posts nicht (wirklich) zu lesen/beachten/verstehen. Und dein Post erklärt doch sehr gut was falsch/schlecht ist.
@hackbert: Du musst im 32bit pmode esp nehmen, nicht sp. Du kannst für den Stack auch das Datensegment hernehmen, musst halt nur esp anpassen.
-
So. Das
mov ax, 0000000000001000b mov cs, ax
ist nun endgültig auf die rote Liste gekommen und wird auch nicht ausgeführt.
mein dritter Teil (also der im PM) sieht nun so aus:
[BITS 32] jmp in_pm nop in_pm: ;create the selector in ax (we want to select the second entry of the gdt as ds) mov ax, 0000000000010000b mov ds, ax ;mov ax, 0000000000010000b ;mov bp, ax ;mov ax, 0000000000010000b ;mov es, ax ;create the selector in ax (we want to select the third entry of the gdt as ss) mov ax, 0000000000011000b mov ss, ax mov esp, 0 mov eax, 0xB8000 mov ecx, 100 writechar: mov byte [eax], 'P' ;0x07 inc eax mov byte [eax], 0x41 inc eax loop writechar call start_c times 512-($-$$) db 0 start_c:
Danach kommt ein vierter Teil. Es ist eine C-Datei im Binärformat kompiliert:
char c = 'A'; int main() { *((char*)0xB8000) = c; //0xEC; hang: goto hang; return 0; }
Die Datei kompiliere und linke ich so:
gcc -c main.c ld -o main -Ttext 0x10400 -Tdata 0x10800 -e main main.o objcopy -S -O binary main ../../bin/cmain.bin
Die Datei wird dann hinten ans Diskettenimage angehängt. Der Sprung in den C-Code funktioniert! Leider passiert hier gar nichts. Nach ein Bisschen Forschung habe ich herausgefunden, dass das ganze funktioniert, wenn ich die Variable nicht global, sondern lokal auf dem Stack erzeuge. Auch eine direkte Zuweisung ohne Umweg über eine Variable funktioniert. Anscheinend stimmt hier was mit dem Datensegment nicht. Ganz merkwürdig. Hier nochmal die Descriptoren:
gdt_start: nulldescriptor times 8 db 0 kerneldescriptor: dw 0xFFFF dw 0 dw 0x9A00 dw 0x00CF datadescriptor: dw 0xFFFF dw 0 dw 0x9200 dw 0x00CF stackdescriptor: dw 0xFFFF dw 0 dw 0x9609 dw 0x00CF
Irgendwas scheint hier nicht mit dem Datensegmentdescriptor zu stimmen oder mit dem Selektor. Habe schon ein paar mal nachgerechnet und es müsste eigentlich klappen. Habe auch die C-binär-Datei disassembliert:
00000000 55 push ebp 00000001 89E5 mov ebp,esp 00000003 83EC08 sub esp,byte +0x8 00000006 83E4F0 and esp,byte -0x10 00000009 B800000000 mov eax,0x0 0000000E 29C4 sub esp,eax 00000010 0FB60500080100 movzx eax,byte [0x10800] 00000017 A200800B00 mov [0xb8000],al 0000001C EBFE jmp short 0x1c ...............Bereich mit Nullen...................... 00000400 41 inc ecx
Im Teil mit
00000010 0FB60500080100 movzx eax,byte [0x10800] 00000017 A200800B00 mov [0xb8000],al
scheint er ja tatsächlich richtig zu schieben. Rein rechnerisch ist das auch ok. Schließlich ergibt: Textstart bei 0x10400 + 0x400 = 0x10800. Und dort liegt 0x41, also ein 'A'.
-
hi,
du solltest esp (also den Stack) nicht mit 0x0 initialisieren, da sonst dein Stack vom Ende des RAM (4GiB) abwärts wächst und ich nehme einfach mal an, dass bei 0xFFFFFFFC (4GiB - 4Byte) kein RAM mehr ist. Dies wäre aber die Adresse an die der Prozessor beim nächsten push seine Daten schiebt.
Das ist mir nur aufgefallen, wird aber dein momentanes Problem leider nicht lösen...
-
So langsam zweifle ich, dass mein Datensegment korrekt geladen wird. Gibt es bei irgendeinem Freien 386+-Emulator (BOCHS, QEmu & Co) die Möglichkeit einen Dump des RAMs zu machen?