i tried to code this but it's not as easy as i thought
i try to execute bios code from protected mode to see if that can work, but i've encountered some little issue with it
first some area need to be accessed both as code and data segment, as i use segment selector mapped to an address it's not possible to map the same address as code and data in the same time, i solve this for now by giving a special mapping to code segment it should be ok as long as the 16 bit code doesn't do a far jump using a segment selector that need to be used also as data segment
bios code also has to be executed from a 16 bit code segment, so it make the managment of the stack rather tricky regarding interupt and exeptions, as the instruction int/iret behave differently depending if it's 16 bit or 32 bit
i looked into intel manuals and various sources, it seem the behavior of int/iret instruction is rather complex, in the intel manual the pseudo code of the instruction is several page long and it depend on lot of parameters, depending if the cpu is in real mode or protected mode, and there are implicit 16 bit and 32 bit version of the opcode that the assembler choose depending on the [bits ] directive
as i have to make the call to the 16 bit code that is supposed to be an interuption handler from the 32 bit code, the stack must be prepared as such as the 16 bit version of the iret opcode can return, and so the call/jmp/int cannot be made using the 32 bit opcode
the stack also need to be setup to be used with the 16 stack pointer (sp), so the stack pointer also need to be prepared to be able to be used with the 16 bit ofset register
for now it seems to work a bit, i tried to set video mode, and it seem to works to a degree, in the sense it seem to switch the mode, and it make use of the segment register properly, but it throw a page fault at some point that i'm trying to debug
i made some special interuption handler that lookup for the entry in the ivt to make a jump to it
Code: Select all
;--------------------------
%macro interupt_hndl 1
interupt_%1:
[.....]
movzx ebx , word [0x0+(%1)*4] ; ofset part of the ivt vector
movzx edx , word [0x0+(%1)*4+2] ; segment part of the ivt vector
shr edx , 4 ; trick to match code segment selector to address >> 4
mov ax ,0x48 ;prepare the stack for 16 bit code
mov ss ,ax
mov esp ,0x8000
pushf
push word cs ;push value in the stack for the iret, as it's a 16 bit iret, the stack cannot be prepared using a regular int call
push word end_of_real_interupt_%1
mov ax,13h ;set vga mode 13h
push word dx ;push the segment selector/address of the ivt vector code segment
push ebx ;push the ofset of the of the ivt vector
db 0xCB ;32 bit far ret opcode , which load the ivt vector into cs:eip
the gdt is setup like this
Code: Select all
seg_access =GDT_ACCESS_PRESENT|GDT_ACCESS_WRITE|GDT_ACCESS_SYSTEM;
seg_code_ax =GDT_ACCESS_PRESENT|GDT_ACCESS_WRITE|GDT_ACCESS_SYSTEM|GDT_ACCESS_CODE;
flags =GDT_ENTRY_AVL;//|GDT_ENTRY_32bit;
//set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x0,0x0,0xFFFF,seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x0C00,0x000C0000,0xFFFF,seg_code_ax,flags); ;code segment selectors for bios aera
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x0F00,0x000F0000,0xFFFF,seg_code_ax,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x0040,0x00000400,0xFFFF,seg_access,flags); ;segment selectors for bios base address (0x40:XX)
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x9FC0,0x0009FC00,0xFFFF,seg_access,flags); ;segment selectors for bios edba
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xA000,0x000A0000,0xFFFF,seg_access,flags); ;data segment selectors for bios aera
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xB000,0x000B0000,0xFFFF,seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xB800,0x000B8000,0xFFFF,seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xC000,0x000C0000,0xFFFF, seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xD000,0x000D0000,0xFFFF,seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xE000,0x000E0000,0xFFFF,seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xF000,0x000F0000,0xFFFF,seg_access,flags);
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x1F90,0x00001F90,0xFFFF,seg_access,flags); ; entered manually, pointer to stack stored in segment by the bios
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x1490,0x00014900,0xFFFF,seg_access,flags); ;pointer to output data stack stored in segment by the bios
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x0100,0x00001000,0xFFFF,seg_access,flags); ; space used for 16 bit stack
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0x1000,0x00010000,0xFFFF,seg_access,flags); ; space used for 16 bit ofset from kernel base address (0x10000)
set_gdt_ptr_c(new_gdt->limit,new_gdt->addr);
/*
set_gdt_entry(new_gdt,&new_gdt_entries_ref,0xA000,0x000A0000,0xFFFF,seg_access,flags);
0xA000 => segment selector ofset in the gdt
0x000A0000 => physical memory
0xFFFF => limit
seg_access => gdt access
flags => gdt flags
*/
so i can just call int 10h from protected mode, and getting the 16 bit bios coded executed from this interupt handler, it would need to save registers to be passed to the interuption, but i'll do this latter, it seem to execute and change the mode
but it end with a page fault exception, from what i gather from the stack, it seem to be fired from within the bios segment, as cs and ds are set properly, but the calling stack seem to be setup as for a 32 bit exeption, albeit it's supposed to be fired from a 16 bit code segment, i couldn't really understand clearly if what change the behavior of int/iret regarding the stack is determined from the 16/32 bit attribute of the code segment, or if it's only cpu flags regarding real mode/protected mode, so i need to dig that more, or if in the absolute any version of the opcode could be used from any type of segment, as a 16 bit opcode could be present in the 32 bit segment, and a 32 bit opcode from a 16 bit code segment, or if the cpu determine which version of iret will be used in the exception handler only from general protected mode flag
i will debug this more to see if i can determine clearly the behavior that is expected from an interupt/exeption handler and an interupt call regarding the setup of the calling stack when the call have to go accross 16/32 bits protected mode segments
with an exception throw, the page fault exception could be caugth and the execution could still be resumed after the fault and apparently the video mode is set, but i'll try to figure was is going wrong
maybe i should put the interupt handler in [bits 16] section with nasm, the protected mode interupt controller should be able to switch the segment selector to the 16 bit code segment, and prepare the interuption/exeption calling stack for a 16 bit iret, which could make the calling context more compatible with the 16 bit code to be called, or i need to setup all the calling stack properly from the 32 bit code for the calling, and dealing with handler that can potentially be called from a 16 bit code segment