In den Protected Mode springen die 6745897.



  • Ringding schrieb:

    Wenn du Code an die (real mode)-Adresse 1000h:0000h geladen hast, dann kannst du ihn nachher unter <cs sel>:00100000h erreichen.

    Verstehe ich das richtig, dass man also die entsprechenden Selektoren in die Register schiebt und dann kann man tun und lassen was man will? Es wäre also möglich mehrere Segmente zu 4GB zu benutzen? (Wenn man eben immer einen anderen Selektor wählt).

    Mir fehlt immernoch der Bezug zwischen alter 16-bit Adresse und neuer PMode-Adresse. (Ich glaube ich fahre mich langsam aber sicher fest 😞 )



  • Ringding schrieb:

    Wieso das nicht? Wenn man ein 4GB großes Segment erzeugt, dann hat man ja faktisch die Segmentierung eliminiert und hat eine flache und durchgehende 32-bit-Adressierung.

    So sehe ich das auch.

    Das ist zwar faktisch so, aber dass heißt noch lange nicht das die Segmentieren in irgendeiner Weise "deaktivierbar" wäre.

    Der Selektor in den Registern zeigt auf einen Deskriptor in der GDT. Wenn du nun einen Speicherzugriff über diesen Selektor - direkt (mov eax, [es:0xDEAD]) oder indirekt (indem der Prozessor auf cs zugreift und versucht den nächsten Befehl zu laden) - ausführst, dann gibst du dem Prozessor dabei ein 32Bit-Offset [=lineare Adresse] (also zb. 0xDEAD oder auch eip beim Laden des nächsten Befehls). Der Prozessor liest dann den Eintrag in der GDT auf den der Selektor zeigt aus und führt einige Sicherheitschecks (ob das Präsenz-Bit gesetzt ist, das CPL ausreicht für den Zugriff, ob das Offset kleiner als das Limit des Deskriptors ist) aus. Anschließend Berechnet er die physikalische Adresse = DeskriptorBasis + Offset.

    Du hast im 32Bit pmode keine "automatische" Unterteilung in 64kib Segmente wie im realmode. Du bestimmst selbst wo ein Segment losgeht, wo es aufhört und wer darauf Zugriff hat.

    Um Paging nutzen zu können muss man es erst aktivieren (setzen des 31bits in cr0). Paging ist zwischen Segmentierung (linearer Adresse) und physikalischer Adresse anzusiedeln und es bringt noch mal einiges an Verwirrung/Komplexität hinzu :p



  • 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?


Anmelden zum Antworten