Das Paging: Stellen wir uns eine Zahlenmenge vor mit 12 Zahlen und zwar von 0 bis 11. {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} Stellen wir uns nun vor, die 12 Zahlen sind nicht in der richtigen Reihenfolge, also sie sind zwar in einer, in einer Reihenfolge, aber in irgendeiner Reihenfolge und nicht aufsteigend sortiert. {10, 6, 1, 0, 11, 3, 7, 9, 5, 8, 2, 4} Nun lassen sich diese Zahlen auch durchnummerieren, entsprechend ihrer Lageïn der Zahlenmenge: {0 10, 1 6, 2 1, 3 0, 4 11, 5 3, 6 7, 7 9, 8 5, 9 8, 10 2, 11 4} Damit lsst sich eine Tabelle aufstellen: f(0) := 10 f(1) := 6 f(2) := 1 f(3) := 0 f(4) := 11 f(5) := 3 f(6) := 7 f(7) := 9 f(8) := 5 f(9) := 8 f(10) := 2 f(11) := 4 (0, 10) (1, 6) (2, 1) (3, 0) (4, 11) (5, 3) (6, 7) (7, 9) (8, 5) (9, 8) (10, 2) (11, 4) Das ist im Prinzip schon der eine mathematische Teil des Pagings. Nun kommt 1
der zweite. Wir nehmen jetzt keine Dezimalzahlen, sondern Binrzahlen. (0000, 1010) (0001, 0110) (0010, 0001) (0011, 0000) (0100, 1011) (0101, 0011) (0110, 0111) (0111, 1001) (1000, 0101) (1001, 1000) (1010, 0010) (1011, 0100) Eine Binrzahl hier hat 4 Ziffern. Eine Binrzahl kann auch 8 oder 9 oder 16 oder beliebig viele Ziffern haben. Nehmen wir nun die Binrzahl 10011100, dann sind die ersten vier Ziffern 1001. Oder bei 11101111, davon sind die ersten vier Ziffern 1110. Das ist im Prinzip schon eine Zahl und damit lsst sich das selbe veranstalten, wie oben beschrieben. Das macht man dann aber nur mit den vorderen zum Beispiel 4 Ziffern, der hintere Teil bleibt erhalten und die Zahl wird so wieder zusammengesetzt. Nun teilen wir die Adresse, eine beliebige Binärzahl, in zwei Teile auf. Die Adresse ist eine Binärzahl. Eine Binärzahl wie 1010010111. Wir teilen die Adresse in einen vorderen und einen hinteren Teil auf. Der vordere Teil ist zum Beispiel 1010 und der hintere: 010111. Mit dem vorderen Teil, machen wir nun eine Zuordnung wie oben, in unserem Beispiel: (1010, 0010). Nun hängen wir aber an unserem hinteren Teil an die zweite Koordinate in dem geordneten Paar, den hinteren Teil der Binärzahl an: 0010010111. Wir haben nun zwei geordnete Paare, von denen wir jeweils wieder ein geordnetes Paar bilden: (1010, 010111) und (0010, 010111). Davon bilden wir ein weiteres geordnetes Paar: ((1010, 010111), (0010, 010111)). 2
Also: Wir haben ein geordnetes Paar (a, b), wobei gilt: f(a) := b. Nun haben wir aber noch die geordneten Paare, (a, c) und (b, c). wobei wir noch ((a, c), (b, c)) haben. Nun bezeichnen wir: a: Seitennummer b: Basisadresse = Seitenrahmennummer c: Offset (a, c): Logische Adresse = virtuelle Adresse (b, c): Physische Adresse = Reale Adresse Nun bezeichnen wir, erweitert: a: Seitennummer 3
b: Basisadresse = Seitenrahmennummer c: Offset (a, c): Logische Adresse = virtuelle Adresse (b, c): Physische Adresse = Reale Adresse Die Menge {(a, c)}, mit c = {0000, 0001, 0010, 0011,..., 1111}, also {(a, 0000), (a, 0001), (a, 0010), (a, 0011),..., (a, 1111)} aus Sicht der Seitennummer wird als Seite/Speicherseite/Page/Memory Page bezeichnet. Die Menge {(b, c)}, mit c = {0000, 0001, 0010, 0011,..., 1111}, also {(b, 0000), (b, 0001), (b, 0010), (b, 0011),..., (b, 1111)} aus Sicht der Seitennummer wird als Seitenrahmen/Frame/Pageframe/Kachel bezeichnet. Die Zuordnung (a, b) übernimmt die MMU (Memory Management Unit/Speicherverwaltungseinheit), anhand einer Tabelle (Seitentabelle/Pagetable). Die MMU (Memory Management Unit/Speicherverwaltungseinheit) mitsamt der Tabelle (Seitentabelle/Pagetable) stellt die Funktion f(a) := b dar. Die Menge {(a, c)}, mit und a = {0000, 0001, 0010, 0011,..., 1111} c = {0000, 0001, 0010, 0011,..., 1111} wird als virtueller Speicher/logischer Speicher/virtueller Adressraum/physischer Adressraum bezeichnet Die Menge {(b, c)}, mit und b = {0000, 0001, 0010, 0011,..., 1111} c = {0000, 0001, 0010, 0011,..., 1111} wird als physischer Speicher/physischer Adressraum bezeichnet Einen Eintrag in der Page Table wird als Page Table Entry bezeichnet 4
Ein Experiment (Programmierquelltext) zum Paging: ; Ein Teil des hier enthaltenen Codes habe ich aus dem Internet ; Ich habe ihn dazu weiterverwendet um das Paging zu demonstrieren ; Da mir die original Datei verloren ging, ich diesen Code aber aus- ; gedruckt hatte, habe ich ihn nochmals von dem Druck abgeschrieben ; (C) David Vajda ; Der "Rahmen" stammt von einem unbekannten User aus dem Web ; Die eigentliche Demonstration von mir ; Zu beachten ; - Der Bootloader muss viel mehr als 10 Sektoren einlesen, weil die Seiten mit Daten ; die hier am Schluss stehen, auch geladen werden muessen ; - org 0x8000 muss beim Paging bedacht werden. Wir fangen nicht bei 0 an! ; - Die Pagetable muss mit 4 Skaliert werden. org 0x8000 [BITS 16] ;;;;;;;;;;;;; ; Real Mode ; ;;;;;;;;;;;;; RealMode: xor ax, ax mov es, ax mov ds, ax mov ss, ax mov sp, ax add sp, -0x40 je.pm.pm: cli lgdt [gdtr] in al, 0x92 cmp al, 0xff je.no_fast_a20 or al, 2 and al, ~1 out 0x92, al jmp.a20_done.no_fast_a20: call empty_8042 mov al, 0xD1 out 0x64, al call empty_8042 5
mov al, 0xDF out 0x60, al call empty_8042.a20_done: mov eax, cr0 or eax, 1 mov cr0, eax jmp 0x8:ProtectedMode empty_8042: call Waitingloop in al, 0x64 cmp al, 0xff je.done test al, 1 jz.no_output call Waitingloop in al, 0x60 jmp empty_8042.no_output: test al, 2 jnz empty_8042.done: ret ;;;;;;;;;;;;;;;;;; ; Protected Mode ; ;;;;;;;;;;;;;;;;;; [Bits 32] ProtectedMode: mov ax, 0x10 mov ds, ax mov ss, ax mov es, ax xor eax, eax mov fs, ax mov gs, ax mov esp, 0x200000 call clrscr32bitmode ;--------------------------------------------------------------------- ;PAGING ;--------------------------------------------------------------------- 6
Paging_starts: ; Wir initialisieren zunaechst PD und PT "linear" mov eax, pd mov ebx, pt+3 mov edi, pt mov eax, 3 mov ecx, 1024 initpt: stosd add eax, 1000h loop initpt mov al, 1 mov [seite4], al mov al, 2 mov [seite5], al mov al, 3 mov [seite6], al mov al, 4 mov [seite7], al mov al, 5 mov [seite8], al mov al, 6 mov [seite9], al ; Die "lineare" Initialisierung von PT waere abgeschlossen ; Wir vertauschen die entsprechenden/ausgewaehlten Seiten mov eax, pt+8*4+3*4 mov ebx, page2_msg+3 mov eax, pt+8*4+4*4 mov ebx, page4_msg+3 mov eax, pt+8*4+5*4 mov ebx, page3_msg+3 mov eax, pt+8*4+6*4 mov ebx, page1_msg+3 7
mov eax, pt+8*4+7*4 mov ebx, page6_msg+3 mov eax, pt+8*4+8*4 mov ebx, page5_msg+3 ; Wir laden die Basisadresse des PD nach cr3 mov eax, pd mov cr3, eax ; Wir aktivieren das Paging mov eax, cr0 or eax, 80000000h mov cr0, eax jmp $+2 ; Hiermit waere das Paging mit Tabellen fertig eingerichtet ; Wir loeschen den Bildschirm und entfernen dern Curser ; Die Seiten die wir vertauscht hatten, geben wir dementsprechend aus mov al, [page1_msg] mov al, [page2_msg] mov al, [page3_msg] mov al, [page4_msg] mov al, [page5_msg] mov al, [page6_msg] mov al, ; Wir vertauschen die Seiten wieder, um den Effekt des Vertauschens ; richtig demonstrieren zu koennen mov eax, pt+8*4+8*4 mov ebx, page6_msg+3 mov eax, pt+8*4+7*4 mov ebx, page5_msg+3 8
mov eax, pt+8*4+6*4 mov ebx, page4_msg+3 mov eax, pt+8*4+5*4 mov ebx, page3_msg+3 mov eax, pt+8*4+4*4 mov ebx, page2_msg+3 mov eax, pt+8*4+3*4 mov ebx, page1_msg+3 ; Wir muessen den TLB aktualisieren mov eax, page1_msg invlpg [eax] mov eax, page2_msg invlpg [eax] mov eax, page3_msg invlpg [eax] mov eax, page4_msg invlpg [eax] mov eax, page5_msg invlpg [eax] mov eax, page6_msg invlpg [eax] ; Wir geben die eben vertauschten Seiten aus mov al, [page1_msg] mov al, [page2_msg] mov al, [page3_msg] mov al, [page4_msg] mov al, [page5_msg] mov al, [page6_msg] mov al,.endlessloop: jmp.endlessloop ;;;;;;;;;;;;;;;; ; Prozeduren ;;;;;;;;;;;;;;;; 9
Waitingloop: pushad mov ebx, 0x9FFFF.loop_start: dec ebx jnz.loop_start popad ret PutChar32BitMode: pushad mov edx, [VideoBufferPtr] mov ah, 15 mov [edx], ax inc edx inc edx mov [VideoBufferPtr], edx popad ret clrscr32bitmode: pushad mov edi, 0xb8000 mov [VideoBufferPtr], edi mov ecx, 80 * 25 mov eax, 0x07200720 rep stosd popad ret VideoBufferPtr dd 0xb8000 ;;;;;;;;;;; ; Strings ; ;;;;;;;;;;; %include "gdt.inc" ; Die Pagetable und das PD times 4096-($-$$) db 0 pd times 1024 dd 0 pt times 1024 dd 0 ; Zu guter Letzt, die wir verwendet haben Seiten, zur Demonstrationszwecken seite4: page1_msg db 1 times 4095 db 0 10
seite5: page2_msg db 2 times 4095 db 0 seite6: page3_msg db 3 times 4095 db 0 seite7: page4_msg db 4 times 4095 db 0 seite8: page5_msg db 5 times 4095 db 0 seite9: page6_msg db 6 times 4095 db 0 ; Zu beachten ; - Der Bootloader muss viel mehr als 10 Sektoren einlesen, weil die Seiten mit Daten ; die hier am Schluss stehen, auch geladen werden muessen ; - org 0x8000 muss beim Paging bedacht werden. Wir fangen nicht bei 0 an! ; - Die Pagetable muss mit 4 skaliert werden. ; Das bedeutet, wenn wir auf die Seite 4 zugreifen wollen muessen wir diese referenzieren ; pt+8*4+3*4 ; Seite 5: ; pt+8*4+4*4 ; Denn 0x8000 ist die 9 Seite, skaliert mit 4 = 8 * 4: ; 0x0000: Seite 1: 0*4 ; 0x1000: Seite 2: 1*4 ; 0x2000: Seite 3: 2*4 ; 0x3000: Seite 4: 3*4 ; 0x4000: Seite 5: 4*4 ; 0x5000: Seite 6: 5*4 ; 0x6000: Seite 7: 6*4 ; 0x7000: Seite 8: 7*4 ; 0x8000: Seite 9: 8*4 (Hier beginnt der Kernel) ; Dazu addieren wir den Unterschied im Kernel-Code ; Wir haben ; 1. Den Code: Laenge 1000h: Basis: 0x8000: 8*4 ; 2. PD: Laenge 1000h: Basis: 0x9000: 8*4+1*4 ; 3. PT: Laenge 1000h: Basis: 0x10000: 8*4+2*4 ; 4. Seite4: Laenge 1000h: Basis 0x11000: 8*4+3*4 ; 5. Seite5: Laenge 1000h: Basis 0x12000: 8*4+4*4 11
Ein weiterer wichtiger Quelltext: org 0x7C00 ; set up start address of bootloader ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; setup a stack and segment regs ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; xor ax, ax mov ds, ax mov es, ax mov ss, ax mov sp, ax ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; read kernel from floppy disks ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov [bootdrive], dl ; boot drive stored by BIOS in DL. ; we save it to [bootdrive] to play for safety load_kernel: xor ax, ax ; mov ax, 0 => function "reset" int 0x13 jc load_kernel ; trouble? try again mov bx, 0x8000 ; set up start address of kernel ; set parameters for reading function ; 8-bit-wise for better overview mov dl, [bootdrive] ; select boot drive mov al, 308 ; read 10 sectors mov ch, 0 ; cylinder = 0 mov cl, 2 ; sector = 2 mov dh, 0 ; head = 0 mov ah, 2 ; function "read" int 0x13 jc load_kernel ; trouble? try again ; show loading message mov si, loadmsg call print_string ;;;;;;;;;;;;;;;;;; ; jump to kernel ; ;;;;;;;;;;;;;;;;;; jmp 0x0000:0x8000 ; address of kernel. "jmp bx" might also work ;;;;;;;;;;;;;;;;;;;;;;; 12
; call "print_string" ; ;;;;;;;;;;;;;;;;;;;;;;; print_string: mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype.loop: lodsb ; grab a byte from SI test al, al ; NUL? jz.done ; if the result is zero, get out int 0x10 ; otherwise, print out the character! jmp.loop.done: ret ;;;;;;;; ; data ; ;;;;;;;; bootdrive db 0 ; boot drive loadmsg db "bootloader message: loading kernel...", 13, 10, 0 times 510-($-$$) hlt db 0x55 db 0xAA Und: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Global Descriptor Table (GDT) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NULL_Desc: dd 0 dd 0 CODE_Desc: dw 0xFFFF ; segment length bits 0-15 ("limit") dw 0 ; segment base byte 0,1 db 0 ; segment base byte 2 db 10011010b ; access rights db 11001111b ; bits 7-4: 4 flag bits: granularity, default operation size bit, ; bit 3-0: segment length bits 16-19 db 0 ; segment base byte 3 DATA_Desc: dw 0xFFFF ; segment length bits 0-15 dw 0 ; segment base byte 0,1 db 0 ; segment base byte 2 db 10010010b ; access rights db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE 13
; bit 3-0: segment length bits 16-19 db 0 ; segment base byte 3 gdtr: Limit dw 24 ; length of GDT Base dd NULL_Desc ; base of GDT ( linear address: RM Offset + Seg<<4 ) Und die Anleitung zum Übersetzen: ; Die ntigen Schritte ; ; DOSBox installieren ; DOSBox fr Windows 8.1 ; Version von DOSBox: DOSBox version 0.74 ; Nasm fr Windows 32 entpacken ; Aktuelle Version von Nasm: ; nasm-2.11.06 ; zip-datei von nasm herunterladen, von www.nasm.us, nasm-2.11.06-dos.zip ; Unbedingt unter DOS gucken und bereits compilierte Version verwenden, nicht selber berse ; Entpacken nach C:\nasm-2.11.06 ; In das Verzeichnis von C:\nasm-2.11.06 die Sources Laden die fr das eigene "Betriebssyst ;... von Bedeutung sind ; - KERNEL.ASM ; - BOOT.ASM ; - GDT.INC ; DOSBox installieren ; DOSBox aufrufen ; Innerhalb der DOSBox aufrufen:... ;... "mount C C:\nasm-2.11.06 ; Nun ist unter "C:\" in der DOSBox das Verzeichnis "C:\nasm-2.11.06" unter Windows eingeb ; Nun die Befehle:... ; nasm boot.asm -f bin -o boot.bin ; nasm kernel.asm -f bin -o kernel.bin ; copy /b boot.bin+kernel.bin MyOS.bin ; Und dies auf die Diskette schaffen, zum Beispiel mit ; partcopy MyOS.bin 0 400 -f0 ; Oder entsprechend in Linux mit den Kommandos ; cat: die Dateien aneinander dranhngen ; dd if=... of=/dev/fd0 ; Die Dateien auf die Diskette kopieren ; Oder eben mit der VirtualBox das "Betriebssystem" starten. 14