NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: DiTBho on July 12, 2023, 09:57:05 AM
-
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.
[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
; -----------------------------------------------------------------------------
; 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
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
-
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
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.
-
by searching in this forum, I indirectly found this (https://forum.nasm.us/index.php?topic=3669.msg15518#msg15518) topic that has precious information
-
Since your code seems to assume you aren't using paging, I have some sample code in this Stackoverflow answer (https://stackoverflow.com/a/41731180/3857942) 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
-
Since your code seems to assume you aren't using paging, I have some sample code in this Stackoverflow answer (https://stackoverflow.com/a/41731180/3857942) that contains a do_vbe function that switches from 32-bit protected mode to real mode.
yup, no paging.
Thanks for your link, it's very interesting :D
-
Intel SDM, volume 3, topic 9.9: "Mode Switching".
Maybe you want to read Intel SDM, volume 3, chapter 20: "8086 emulation" (works in i386 mode).
-
Intel SDM, volume 3, topic 9.9: "Mode Switching".
Maybe you want to read Intel SDM, volume 3, chapter 20: "8086 emulation" (works in i386 mode).
I will for sure read it sooner or later.
At the moment I'd like to complete my mon/loader, so I am looking for examples that are known to work.
I have other priorities, as I have also to hack the Linux kernel and adapt it for my Geode router (amd x86 compatible).