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:
-
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.
-
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.
-
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.
-
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.
-
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.
-
Ich lade einen Selector direkt per Wert in die Segmentregister und der Prozessor weiß nun, wie er mit virtuellen Adressen umgehen soll.
-
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 3Ok. 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...