PC stürzt nach Übergang in den Protected Mode ab



  • Hi!

    Ich versuch mir grad nen Kernel in C zu schreiben, da das aber nicht von Anfang an möglich ist, muss halt der Anfang in Assembler sein. Leider hab ich davon nur wenig Ahnung und weiß einfach nicht, warum der PC rummuckt, wenn ich in den Protected Mode gehen will...

    Er startet einfach immer wieder neu und nichts passiert, keine Fehlermeldung, keine Ausgabe.

    [BITS 16]
    
    ; Global declaration of star
    global start
    
    ; Jump over the GDT
    jmp		start
    
    ; Null-Desc
    NULL_Desc:
    	dd		0
    	dd		0
    
    ; Code-Desc	
    CODE_Desc:
    	dw		0xFFFF		;Segmentgröße Byte 0/1
    	dw		0			;Segmentbasisadresse Byte 0/1
    	db		0			;Segmentbasisadresse Byte 2
    	db		10011010b	;Zugriffsberechtigungen
    	db		11001111b	;Zusatz + Segmentgröße Bits 16 - 19
    	db		0			;Segmentbasisadresse Byte 3
    
    ; Data-Desc
    DATA_Desc:
    	dw		0xFFFF
    	dw		0
    	db		0
    	db		0x92
    	db		0xCF
    	db		0
    
    ; GDT
    gdt:
    Limit	dw	0
    Base	dd	0
    
    ; Start
    start:
    
    ; Turn off any interrupts
    cli
    
    ; Set EAX and DS to the code segment
    mov		eax, cs
    mov		ds, ax
    
    ; Work with EAX to reach the linear address of the segment
    shl		eax, 4
    mov		[CODE_Desc+2], ax	;Lineare Adresse des Codesegmentes als 
    mov		[DATA_Desc+2], ax	;Startadresse des Code- und Datendeskriptors
    shr		eax, 16				;eintragen
    mov		[CODE_Desc+4], al
    mov		[DATA_Desc+4], al
    
    ; Calculate the starting-address of the GDT
    mov		eax, cs
    shl		eax, 4
    add		eax, NULL_Desc
    
    mov		[Base], eax
    mov		[Limit], WORD gdt - NULL_Desc -1
    

    Bis hier hin geht das ganze noch...

    ; Change into the Protected Mode
    mov		eax, cr0
    or		eax, 1
    mov		cr0, eax
    
    db		0xea
    dw		PMODE
    dw		0x8
    
    [BITS 32]
    
    PMODE:
    
    PMODE:
    mov		WORD [CODE_Desc+2], 0	;Code Segmentstartaddresse auf 0 setzen
    mov		WORD [DATA_Desc+2], 0	;Daten Segmentstartadresse auf 0 setzen
    mov		BYTE [CODE_Desc+4], 0	;Code Segmentstartaddresse auf 0 setzen
    mov		BYTE [DATA_Desc+4], 0	;Daten Segmentstartadresse auf 0 setzen
    
    mov		eax, 2
    shl		eax, 3
    
    mov		ds, ax				;Daten- Stack- und Extrasegment mit
    mov		ss, ax				;Datensegmentdeskriptor laden
    mov		es, ax
    mov		eax, 0				;FS und GS mit Null-Deskriptor laden
    mov		fs, ax
    mov		gs, ax
    mov		esp, 0x1FFFFF		;Stack auf unterhalb der 2 MB Grenze setzen
    
    ; Print Boot-Messages
    mov si, msg
    call putstr
    
    mov si, msg_boot
    call putstr
    
    call getkey
    jmp reboot
    
    stop:
    
    jmp stop
    
    msg db "Welcome to SampleOS",13,10,0
    msg_boot db "Press any key...",10,0
    
    ; Print a string
    putstr:
    lodsb            ; Byte laden
    or al,al
    jz short putstrd ; 0-Byte? -> Ende!
    mov ah,0x0E      ; Funktion 0x0E
    mov bx,0x0007    ; Atrribut-Byte
    int 0x10         ; schreiben
    jmp putstr       ; naechstes Byte
    putstrd:
    retn
    
    ; Waiting for key pressing
    getkey:
    mov ah, 0 ; Funktion 0
    int 016h  ; Ausfuehren
    ret
    
    ; Reboot
    reboot:
    db 0EAh
    dw 0000h
    dw 0FFFFh
    
    times 512-($-$$)	db	0
    

    PS: Zum Booten benutzte ich nur den VMware Player, dürfte aber keinen Unterschied zum richtigen Boot darstellen.

    Und bitte fangt jetzt nicht damit an: wenn du noch nicht mal Assembler kannst, dann brauchst du auch nicht versuchen ein Kernel zu proggen. Ich weiß sehrwohl wie schwierig das ist, da man die komplette Laufzeitbibliothek neuschreiben muss. Und es soll ja auch nur als Hobby gedacht seint 😉 .

    Und schon man Thx im voraus.



  • Aehm... "wenn du noch nicht mal Assembler kannst, dann brauchst du auch nicht versuchen ein Kernel zu proggen." SCNR 😃
    Ne, grundsaetzlich nicht schlecht (und sogar noch was gelernt: mir war vorher nicht praesent, dass man die 16Bit Segmentregister offenbar auch direkt in 32Bit-Register schieben kann .😉

    Keine Ahnung von VMWare, aber erstmal ist dein Code kein gueltiger Bootsektor (falls es das denn darstellen sollte).
    Dann funktionieren die meisten BIOS-Interrupts (inklusive der, die du in deinem Code verwendest ;)) im PM nicht.
    Und zuletzt denke ich, dass du deinen Code sicher vereinfachen koenntest, wenn du deine Deskriptoren nicht erst zur Laufzeit berechnest, sondern die Werte schon vorher festlegst. Sollte bei einem selbst geschriebenen Kernel schliesslich drin sein.



  • Wie gesagt, ich hab noch nicht viel Ahnung von Assembler. 😃 Der Code ist aus nem Tutorial gezogen. Könntest du mir vlt. ein Bespiel zeigen, mit dem man mindestens in den Protected Mode kommt und den Kernel in C proggen kann? Das Linken funzt ja schon: Bootloader+Kompilierter Kernel in ASM+Kompilierter Kernel in C. Nur stürzt er halt immer ab.



  • Ne, habe kein Beispiel zur Hand (FAQ darfst du selbst durchstoebern), aber falls du dich nicht naeher mit Asm beschaeftigen willst, nimm doch zB. Grub. Der kann dir AFAIK die meiste Arbeit schon abnehmen, so dass du gleich in einer Hochsprache loslegen kannst. 🙄



  • Klingt gut, das mit dem grub und gleich hochsprache. Hast du - Nubou-T - dafür ein Dewdurial? Danke



  • Hm, es macht mit Sicherheit einen Unterschied ob man unter
    VMWare versucht in den Protected Mode zu Wechseln, oder
    ob man von einem echten DOS gebootet hat.



  • GAST158 schrieb:

    Hm, es macht mit Sicherheit einen Unterschied ob man unter
    VMWare versucht in den Protected Mode zu Wechseln, oder
    ob man von einem echten DOS gebootet hat.

    Und der wäre? Windows und Linux kann man doch auch booten und da befindet sich der virtuelle Prozessor auch erst im Real-Mode.



  • Dr. C++ schrieb:

    GAST158 schrieb:

    Hm, es macht mit Sicherheit einen Unterschied ob man unter
    VMWare versucht in den Protected Mode zu Wechseln, oder
    ob man von einem echten DOS gebootet hat.

    Und der wäre? Windows und Linux kann man doch auch booten und da befindet sich der virtuelle Prozessor auch erst im Real-Mode.

    Der Unterschied ist meiner Meinung nach der, daß unter VMWare nicht alle Befehle des Prozessors erlaubt sind bzw. evtl. die nicht erlaubten nur emuliert werden. LMSW (Load Machine Status Word) ist möglicherweise z.B. so ein Kandidat. Was spricht dagegen mal ein echtes DOS zu booten und ES auszuprobieren ? Es gibt natürlich noch viele andere Gründe, die für den
    Absturz in Frage kommen. Ich sehe z.B. nirgends die Interrupt Descriptorentabelle. Du sperrst zwar die Interrupts, aber ich weiß nicht ob das reicht. Ich glaube ich habe hier noch irgendwo ein uraltes Programm rumfliegen, daß in den Protected Mode wechselt und NICHT abstürzt 🙂
    Da müßte ich mal ein paar alte Disketten durchforsten ...



  • So, Meister, ich hab das Beispiel gefunden (auf einer Diskette die satte 16 Jahre alt ist). Mit der Routine "START" kommst du in den Protected Mode
    und auch wieder zurück:

    .286p

    LOC_DES_TAB1 SEGMENT
    DB 0, 0, 0, 0, 0, 0, 0, 0 ; NULLSELEKTOR
    DB 0, 0, 0, 0, 0, 0, 0, 0
    DB 0, 0, 0, 0, 0, 0, 0, 0
    DB 0, 0, 0, 0, 0, 0, 0, 0
    LOC_DES_TAB1 ENDS

    GLOB_DES_TAB SEGMENT
    DB 0, 0, 0, 0, 0, 0, 0, 0 ; NULLSELEKTOR
    DB 0, 0, 0, 0, 0, 0, 0, 0
    DB 255,255,0, 0, 10,147, 0, 0
    DB 255,255,0,128, 11,147, 0, 0
    DB 0, 0, 0, 0, 0, 0, 0, 0
    DB 0, 0, 0, 0, 0, 0, 0, 0
    GLOB_DES_TAB ENDS

    INTR_DES_TAB SEGMENT
    DB 0, 0, 0, 0, 0, 0, 0, 0 ; NULLSELEKTOR
    DB 0, 0, 0, 0, 0, 0, 0, 0
    DB 0, 0, 0, 0, 0, 0, 0, 0
    DB 0, 0, 0, 0, 0, 0, 0, 0
    INTR_DES_TAB ENDS

    FEHLER SEGMENT
    I0 DB 'DivisionsfehlerI1DBEinzelschritt' I1 DB 'Einzelschritt'
    I2 DB 'NMI$'
    FEHLER ENDS

    STAPEL SEGMENT STACK
    DW 256 DUP (0)
    STAPEL ENDS

    PROCODE SEGMENT
    ASSUME CS: PROCODE

    TABLE DW ?, ?, ?

    DOSRETURN: MOV AX,4C00H
    INT 21H

    START: CLI
    MOV AH,159+64 ; A20 FREISCHALTEN
    CALL WAIT8042
    JNZ WEITER
    MOV AL,0D1H
    OUT [64H],AL
    CALL WAIT8042
    JNZ WEITER
    MOV AL,AH
    OUT [60H],AL
    CALL WAIT8042
    WEITER: MOV AL,08FH ; CMOS-BYTE IN UHR SETZEN
    OUT [70H],AL
    AAD ; 14 TAKTE WARTEN
    MOV AL,9
    OUT [71H],AL

    PUSHF
    PUSH CS
    PUSH OFFSET DOSRETURN ; FšR RETURN NACH EINEM INT 15H
    SUB SP,20
    MOV AX,0 ; REAL-MODE-STACKZEIGER MERKEN
    MOV DS,AX
    MOV DS:[0469H],SS
    MOV DS:[0467H],SP

    PUSHF
    MOV DI,SP
    AND SS:[DI],1111110011111111b ; CLI UND TRAPFLAG L™SCHEN
    POPF

    MOV AX,GLOB_DES_TAB
    MOV ES,AX
    MOV SI,0
    MOV BP,SI ; OFFSET IN GDTSEG = 0
    LEA DI,[SI+8]
    MOV DX,6*8
    MOV BL,0
    CALL DESKRIP
    LGDT QWORD PTR ES:[SI+08H]

    MOV AX,SS
    MOV BP,0
    LEA DI,[SI+28H]
    MOV DX,0FFFFH
    MOV BL,93H
    CALL DESKRIP

    MOV WORD PTR CS:TABLE,2048 ; 6 BYTES FUER DIE
    MOV AX,INTR_DES_TAB ; INITIALISIERUNG DES IDTR
    MOV BX,10 ; VORBEREITEN
    MUL BX
    MOV WORD PTR CS:TABLE+2,AX
    MOV BYTE PTR CS:TABLE+3,DL
    LIDT QWORD PTR CS:TABLE

    MOV AX,PROCODE ; PM-CODE-SEGMENT
    MOV BP,0
    MOV DX,0FFFFH
    MOV BL,9BH
    LEA DI,[SI+20H]
    CALL DESKRIP

    SMSW AX
    OR AL,1
    LMSW AX
    DB 0EAH
    DW OFFSET PM_START
    DW 0020H ; JMP 0020:PM_START

    PM_START: MOV AX,28H
    MOV SS,AX
    MOV AX,18H
    MOV ES,AX
    MOV AX,10H
    MOV DS,AX

    MOV ax,65+16*256;
    MOV cx,1000
    XOR di,di
    REP stosw

    MOV WORD PTR CS:TABLE,0 ;
    LIDT QWORD PTR CS:TABLE ; IDT-LŽNGE AUF 0 UND INT 8
    INT 8

    DESKRIP PROC NEAR
    MOV WORD PTR ES:[DI+6],0
    MOV ES:[DI+5],BL
    MOV ES:[DI],DX
    MOV BX,10H
    MUL BX
    ADD AX,BP
    ADC DX,0
    MOV ES:[DI+2],AX
    MOV ES:[DI+4],DL
    RET
    DESKRIP ENDP

    WAIT8042 PROC NEAR
    XOR CX,CX
    INPUT: IN AL,[64H]
    AND AL,02
    JZ OK8042
    LOOP INPUT
    INC CX
    OK8042: RET
    WAIT8042 ENDP

    PROCODE ENDS
    END START


Anmelden zum Antworten