NASM Forum > Programming with NASM
how to swtich into protected mode/32bit and then back to real-mode/16-bit?
DiTBho:
hi
I am writing a monitor able to load and start a Linux kernel.
The application is started by the BIOS at 0x7c0 in real-mode and it switches into protected mode.
The whole monitor is 32bit and works in protected mode.
--- Code: ---[bits 16]
...
switch_to_32bit:
cli ; 1. disable interrupts
lgdt [ gdt_descriptor ] ; 2. load the GDT descriptor
mov eax, cr0
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
mov cr0, eax
jmp code_seg:init_32bit ; 4. far jump by using a different segment
[bits 32]
init_32bit: ; now using 32-bit instructions
mov ax, data_seg ; 5. update the segment registers
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
jmp app32_start
--- End code ---
--- Code: ---
; -----------------------------------------------------------------------------
; boot/gdt
;
; labels, are needed to compute sizes and jumps
;
; GDT for code segment. base = 0x00000000, length = 0xfffff
; -----------------------------------------------------------------------------
gdt_start:
; the GDT starts with a null 8-byte
dd 0x0 ; 4 byte
dd 0x0 ; 4 byte
gdt_code:
dw 0xffff ; segment length, bits 00-15
dw 0x0 ; segment base , bits 00-15
db 0x0 ; segment base , bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x0 ; segment base , bits 24-31
; GDT for data segment. base and length identical to code segment
; some flags changed, again, refer to os-dev.pdf
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
dd gdt_start ; address (32 bit)
; define some constants for later use
code_seg equ gdt_code - gdt_start
data_seg equ gdt_data - gdt_start
--- End code ---
This part works.
At some point the monitor loads from disk the kernel as bz-image, and places it in ram.
bz-image is composed by two parts:
* the real-mode kernel setup, loaded to 0x10000
* the kernel body, loaded to 0x100000
So, I need to switch back to real-mode in order to run the kernel-setup.
edit:
split into two posts
DiTBho:
--- Code: ---switch_to_rm:
mov eax,cr0 ; get CPU control register
; 286 will resume execution @ 0xF000:FFF0
; 386 will fall through.
lidt [reset_IDTR]
; restore real-mode compatible value
; * * * mandatory * * *
; real-mode interrupts are relocatable
; on the 386 via the IDT register!
and eax,0xfffffffe ; clear protected mode bit
mov cr0,eax ; now, the cpu is out of protected-mode
;----------------------------------------------------------------------------
xor eax,eax ; a convenient zero
mov cr3,eax ; Flush the TLB
;----------------------------------------------------------------------------
mov eax, 0x0000 ; zero segment for app16
;mov eax, 0x1000 ; zero segment for kernel
mov ds, ax
mov gs, ax
mov es, ax
mov cs, ax
nop ; flush prefetch queue
jmp app16_addr ; jump into real-mode application !!!!!!DOES NOT WORK!!!!!!!
nop ;
;----------------------------------------------------------------------------
reset_IDTR:
dw 0
dw 0
--- End code ---
This part doesn't work properly as I always get a "triple fault", which resets the CPU but doesn't force the InstructionPointer to point to app16_addr, instead it points to the BIOS, which loads the MBR again at 0x7c00.
DiTBho:
by searching in this forum, I indirectly found this topic that has precious information
Michael Petch:
Since your code seems to assume you aren't using paging, I have some sample code in this Stackoverflow answer that contains a do_vbe function that switches from 32-bit protected mode to real mode. This code assumes you didn't remap the PICs (which would also potentially have to be handled). The do_vbe function drops to real mode, does some video mode work with the BIOS and then returns back to 32-bit protected mode and then back to the calling function. The code of interest to you that you could draw inspiration from will be from the line starting from the CLI instruction down to the CLD instruction in do_vbe
DiTBho:
--- Quote from: Michael Petch on July 12, 2023, 09:28:47 PM ---Since your code seems to assume you aren't using paging, I have some sample code in this Stackoverflow answer that contains a do_vbe function that switches from 32-bit protected mode to real mode.
--- End quote ---
yup, no paging.
Thanks for your link, it's very interesting :D
Navigation
[0] Message Index
[#] Next page
Go to full version