Switch protcted mode (32bits) to real mode (16bits) in asm, low level

-1

I am creating an OS, and I need to call the BIOS in real mode, I tried this code but it has a bug: enter image description here The error on boch is

00002172825e[CPU0 ] check_cs: conforming code seg descriptor dpl > cpl

The code is :

    global go16
;______________________________________________________________________________________________________
;Switch to 16-bit real Mode
;IN/OUT:  nothing

go16:
    [BITS 32]

    cli                 ;Clear interrupts
    pop edx             ;save return location in edx
    jmp 0x20:PM16       ;Load CS with selector 0x20

;For go to 16-bit real mode, first we have to go to 16-bit protected mode
    [BITS 16]
PM16:
    mov ax, 0x28        ;0x28 is 16-bit protected mode selector.
    mov ss, ax  
    mov ds, ax
    mov es, ax
    mov gs, ax
    mov fs, ax
    mov sp, 0x7c00+0x200    ;Stack hase base at 0x7c00+0x200    

    
    mov eax, cr0
    and eax, 0xfffffffe ;Clear protected enable bit in cr0
    mov cr0, eax   
    jmp 0x50:realMode   ;Load CS and IP


realMode:
;Load segment registers with 16-bit Values.
    mov ax, 0x50
    mov ds, ax
    mov fs, ax
    mov gs, ax
    mov ax, 0
    mov ss, ax
    mov ax, 0
    mov es, ax
    hlt
    mov sp, 0x7c00+0x200    

    cli
    lidt[.idtR]     ;Load real mode interrupt vector table
    sti

    push 0x50       ;New CS
    push dx         ;New IP (saved in edx)
    retf            ;Load CS, IP and Start real mode

;Real mode interrupt vector table
.idtR: 
    dw 0xffff       ;Limit
    dd 0            ;Base

The program stops at jmp 0x20:PM16
because he don't like 0x20 I need to call the bios to switch graphic mode because I can't do that in my bootloader Thank you for your answer PS : My gdt is :

init_gdt_desc(0x0, 0x0, 0x0, 0x0, &kgdt[0]);
init_gdt_desc(0x0, 0xFFFFF, 0x9B, 0x0D, &kgdt[1]);      
init_gdt_desc(0x0, 0xFFFFF, 0x93, 0x0D, &kgdt[2]);    
init_gdt_desc(0x0, 0x0, 0x97, 0x0D, &kgdt[3]);          

//user segment
init_gdt_desc(0x0, 0xFFFFF, 0xFF, 0x0D, &kgdt[4]); /* ucode */
init_gdt_desc(0x0, 0xFFFFF, 0xF3, 0x0D, &kgdt[5]); /* udata */
init_gdt_desc(0x0, 0x0, 0xF7, 0x0D, &kgdt[6]); /* ustack */
assembly
operating-system
x86-16
bios
low-level
asked on Stack Overflow Jan 17, 2021 by Konect Team • edited Jan 24, 2021 by Konect Team

1 Answer

1

For a jmp or call to a different segment you need to obey one of the following priviledge rules:

  • target DPL is equal to CPL
  • target DPL is less or equal to CPL, but the target conforming bit is set

Looking at your selectors, the target DPL is 3 and the CPL is 0.

So to use jmp 0x20:PM16, change

init_gdt_desc(0x0, 0xFFFFF, 0xFF, 0x0D, &kgdt[4]); /* ucode */

into

init_gdt_desc(0x0, 0xFFFFF, 0x9F, 0x0D, &kgdt[4]); /* ucode */

;Real mode interrupt vector table
.idtR: 
    dw 0xffff       ;Limit
    dd 0            ;Base

The correct limit would be 0x03FF (1023 in decimal)

[edit]

Since the goal is to go to real mode the BIG bit in the descriptors should be off to select 16-bit, and the GRANULARITY bit should be off to limit memory access to just 1MB.

init_gdt_desc(0x0, 0xFFFF, 0x9E, 0x0, &kgdt[4]); /* ucode */
init_gdt_desc(0x0, 0xFFFF, 0x92, 0x0, &kgdt[5]); /* udata */

The order of things in your current program is different from what you can find here about switching from protected mode to real mode.

answered on Stack Overflow Jan 24, 2021 by Sep Roland • edited Jan 25, 2021 by Sep Roland

User contributions licensed under CC BY-SA 3.0