In den Protected Mode springen die 6745897.



  • Ich versuche mal wiede rverzweifelt den Protected Mode zu aktivieren und scheitere grandios. Im Moment schreibe ich das Zeug in NASM, also Intel-Syntax und habe ein paar Problemchen.

    Als erstes habe ich einen Funktionierenden Bootloader, der im Bootsektor einer Diskette sitzt und die nachfolgenden zwei Sektoren an Adresse 0x1000:0000 lädt. Dann springt der Bootloader (immernoch im Real-Mode) nach 0x1000:0000. Das klappt auch soweit. Dort soll dann in den PM gesprungen werden. und auch hier tun sich die Probleme erst auf. Erstmal der Quellcode:

    [BITS 16]
    
    jmp 1000h:hang
    nop
    
    hang:
    	mov si, pre_starting
    	call putstr2
    
    	;calculate the physical address of in_pm
    	mov eax, 0x1000
    	shl eax, 4
    	add eax, in_pm
    	;physical address of in_pm is now in eax
    
    	;copy that adress into kerndeldescriptor
    	mov word [ksegbase1], ax
    	shr ebx, 16
    	mov byte [ksegbase2], al
    	mov byte [ksegbase3], ah
    
    	;calculate the physical address of gdt_start
    	mov eax, 0x1000
    	shl eax, 4
    	add eax, gdt_start
    
    	;copy the address into the gdt
    	mov dword [gdtbase], eax
    
    	;Load the gdt
    	lgdt [mygdt]
    
    	;activate PM
    	mov eax, cr0
    	or eax, 1
    	mov cr0, eax
    
    	;calculate the physical address of in_pm (once again)
    	mov ebx, 0x1000
    	shl ebx, 4
    	add ebx, in_pm
    
    	;create the selector in ax (we want to select the first entry of the gdt
    	mov ax, 0000000000001000b
    	mov cs, ax
    	mov ds, ax
    
    	;jump to "in_pm"
    	jmp dword ebx
    
    putstr2:
    	lodsb
    	or al, al
    	jz short putstrd2
    	mov ah, 0x0E
    	mov bx, 0x0007
    	int 0x10
    	jmp putstr2
    
    	putstrd2:
    	retn
    
    pre_starting db "[HANG!...]", 13, 10, 0
    
    mygdt:
    	gdtlimit	dw 16
    	gdtbase		dd 0
    
    gdt_start:
    	nulldescriptor		times 8 db 0
    	kerneldescriptor: 	
    				ksegsize 	dw 0xFFFF		;4GB segsize, see also the first 4 bits of ksegmisc
    				ksegbase1 	dw 0x1000		;
    				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 10011010b
    				;-------------------_________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
    
    ; HERE PM STARTS!!!
    in_pm:
    	mov byte [0xB800], 0x07
    	mov byte [0xB801], 0x41
    	jmp in_pm
    

    Ich hoffe die Kommentare sind lesbar. Im Codeteil bei "in_pm" sollte eigentlich das Zeichen 'a' mit dem Attribut 0x07 (Farbe: weiß) auf die Konsole geschrieben werden. Leider passiert nichts.

    Ich habe das Gefühl, dass ich das mit den ganzen Adressen usw. immernoch nicht gecheckt hab. Erbarmt sich jemand? 😃

    Danke schonmal im Voraus für alle Antworten!



  • Ich weiß jetzt nicht auswendig, welches das 32Bit-Flag ist, aber wahrscheinlich hast du das gesetzt und möchtest damit 16bittigen Code ausführen.



  • hi,

    du kannst nicht einfach

    mov cs, ax
    

    machen. Du musst einen far jump ausführen:

    jmp 0x08:pmode
    

    Addresse des VRAM ist 0xB8000 nicht 0xB800. Und du musst zuerst das Zeichen (gerade Address) und dann die Farbe (ungerade Addresse) schreiben.
    Außerdem ist der Deskriptor 1 ein Codesegmentdeskriptor, d.h. du kannst nicht ds damit laden, da ds einen Datensegmentdeskriptor erwartet.
    Wie bereits Ringding sagte, erstellst du ein 32Bit Deskriptor, benutzt aber nicht das [BITS 32] Direktiv.



  • Ich habe anscheinend eine ganze Menge Verständnisprobleme bezüglich der Speicheradressierung. Ich stelle das mal so dar, wie ich es verstanden habe. Bitte korrigieren, wenn ich falsch liege:

    1. Man schaltet den Rechner ein und befindet sich im 16-bit Modus. Hier gelten Adressierungen im Format: 16-Bit-Segment:16-Bit-Offset mit einer maximal ansprechbaren Größe des RAMs von 1MiB.

    2. Aus der Segmentadresse im 16-bit-Modus wird intern eine 20-bit physikalische Adresse gebildet. Dazu wird das angegebene Segment mit 16 multipliziert (bzw. um 4 nach links geshiftet) und der Offset addiert.

    3. Es gibt einen GDT, der benötigt wird, um in den protected Mode zu springen. Die Adresse des GDT wird als physikalische 20-bit-Adresse beim Laden mittels lgdt angegeben.

    4. Die in der GDT angegebene Basisadresse wird ebenfalls als physikalische 20-bit-Adresse angegeben. Somit findet der Prozessor die Desktiptortabelle im RAM mittels physikalischer Adresse.

    5. Ich will die Segmentierung umgehen indem ich ein 4GiB Segment erzeuge. Da dieses Segment 4GiB groß ist und der maximal adressierbare Speicher 4GiB (ohne Erweiterungen) beträgt müssten alle alten 20-bit Adressen nun automatisch auch gleich mit den neuen Virtuellen Adressen sein.

    6. Ich lade einen Selector direkt per Wert in die Segmentregister und der Prozessor weiß nun, wie er mit virtuellen Adressen umgehen soll.

    7. Ich springe als nächstes an eine virtuelle Adresse und befinde mich im Protected mode.

    Mein Hauptproblem ist eigentlich, dass ich mir nicht 100%ig sicher bin, wann welche Adressierungsart vonnöten ist.

    Danke im Voraus für mögliche Aufklärungsversuche.



  • 1/2: ja
    3: Physikalische Adressen sind ab dem i386 32Bit groß d.h. die Adresse der GDT ist 32Bit groß.
    4: siehe Punkt 3
    5: siehe Punkt 4 😉 (2^20 = 1MiB, 2^32 = GiB) btw. die Segmentierung kann man nicht umgehen.
    6: Ja du lädst einen Selektor in ein Segmentregister. Dann weiß der Prozessor unter welchem Index in der GDT er nachsehen muss um aus der linearen die physikalische Adresse zu berechnen. Aber z.B. das Codesegmentregister cs erwartet, dass es auf einen Codesegmentdeskriptor zeigt, sonst gibts irgendeine Exception. Das Datensegmentregister ds (und alle anderen segmentregister es, fs, gs, ss) erwarten hingegen einen Datensegmentdeskriptor.
    7: Du bist im Protected Mode sobald du das 0te bit in cr0 setzt. Anschließend soltest du durch einen far jump das Codesegmentregister neu laden und danach die anderen Segmentregister neu laden.

    [Edit: Meine Ausführungen beziehen sich auf den 32Bit Protected-Mode ab dem i386, falls du den 16Bit Protected-Mode des i286 meinst, solltest du es sagen]



  • Danke erstmal für die aufklärende Antwort.

    bluecode schrieb:

    1/2: ja
    3: Physikalische Adressen sind ab dem i386 32Bit groß d.h. die Adresse der GDT ist 32Bit groß.
    4: siehe Punkt 3

    Ok. Heißt das, dass die Umwandlung von der Segmentierten Adresse nicht mehr nach dem "16*Segment+Ofsset"-Prinzip erfolgt? Also eigentlich eine 32-bit lineare Adresse mit 64 KB großen Segmenten erfolgt?

    bluecode schrieb:

    5: siehe Punkt 4 😉 (2^20 = 1MiB, 2^32 = GiB) btw. die Segmentierung kann man nicht umgehen.

    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. Welche Rolle spielt das Paging dabei?

    bluecode schrieb:

    6: Ja du lädst einen Selektor in ein Segmentregister. Dann weiß der Prozessor unter welchem Index in der GDT er nachsehen muss um aus der linearen die physikalische Adresse zu berechnen. Aber z.B. das Codesegmentregister cs erwartet, dass es auf einen Codesegmentdeskriptor zeigt, sonst gibts irgendeine Exception. Das Datensegmentregister ds (und alle anderen segmentregister es, fs, gs, ss) erwarten hingegen einen Datensegmentdeskriptor.

    gut. Noch eine Frage dazu: Am Anfang befinde ich mich im 16-bit-Modus mit Segmenten. Nun lade ich ein Codestück an, sagen wir mal, Adresse 0x1000:0000 (so wie oben). Angenommen ich habe da wirklich nur Code drinnestehen. Wie muss der zugehörige Descriptor aussehen? Mein Problem ist, dass ich nicht ganz verstehe, wie genau der Descriptor die Adressierung regelt. Du hast im Descriptor eine Basisadresse und die Größe drinne. Die Basisadresse bezieht sich auf eine 32-bit-Adresse. Soweit so gut. Aber jetzt kommt die virtuelle Adresse ins Spiel. Woher weiß ich, welche zugehörige Virtuelle Adresse dazu gehört? Oder muss ich sowieso der Reihe nach den kompletten Speicher mit Descriptoren versehen und es werden damit virtuelle Adressen ab 0x0000000000000000 abgedeckt?

    bluecode schrieb:

    7: Du bist im Protected Mode sobald du das 0te bit in cr0 setzt. Anschließend soltest du durch einen far jump das Codesegmentregister neu laden und danach die anderen Segmentregister neu laden.

    bluecode schrieb:

    [Edit: Meine Ausführungen beziehen sich auf den 32Bit Protected-Mode ab dem i386, falls du den 16Bit Protected-Mode des i286 meinst, solltest du es sagen]

    Nein. Ich meine den 32-bit protected mode. Meinen 286er habe ich (leider) vor ein paar Jahren entsorgt.

    Sorry, falls ich mich zu blöd anstelle, aber irgendwie finde ich diese ganzen Adressierungsarten extrem verwirrend.



  • Ok. Heißt das, dass die Umwandlung von der Segmentierten Adresse nicht mehr nach dem "16*Segment+Ofsset"-Prinzip erfolgt? Also eigentlich eine 32-bit lineare Adresse mit 64 KB großen Segmenten erfolgt?

    Nein. Du lädtst einfach einen 32 Bit-Wert rein, und das ist eben die Adresse des GDT.

    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.

    Welche Rolle spielt das Paging dabei?

    Gar keine, solange du es nicht einschaltest.

    Den Deskriptor für das Codesegment kannst du genau so machen wie den fürs Datensegment, nur musst du halt ein paar Flags anders setzen. Wenn du Code an die (real mode)-Adresse 1000h:0000h geladen hast, dann kannst du ihn nachher unter <cs sel>:00100000h erreichen.



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


Anmelden zum Antworten