Protected Mode -> Real Mode -> Protected Mode
Protected Mode -> Real Mode -> Protected Mode
Hi,
I've written some asm to allow me to run code in real mode (for vesa, etc.). It is compiled for 16 bits with nasm to a .bin file, using a base address of 0x7C00. This .bin file is then appended to the kernel's main .bin file before being mounted to the virtual floppy.
I'm using grub to load the kernel at boot time. I intend to copy the appended code to address 0x7C00 where I should be able to call it in order to do the mode changes. The problem is that when the kernel is running it seems as though there is no code appended to the kernel. (I just read null bytes). The kernel is loaded at 1MB and the kernel itself is exactly 36kb, so the appended code should be at address 0x109000 right? But there is nothing at this address.
Can anyone see a reason why this should not/does not work?
Thanks
Diggsey
I've written some asm to allow me to run code in real mode (for vesa, etc.). It is compiled for 16 bits with nasm to a .bin file, using a base address of 0x7C00. This .bin file is then appended to the kernel's main .bin file before being mounted to the virtual floppy.
I'm using grub to load the kernel at boot time. I intend to copy the appended code to address 0x7C00 where I should be able to call it in order to do the mode changes. The problem is that when the kernel is running it seems as though there is no code appended to the kernel. (I just read null bytes). The kernel is loaded at 1MB and the kernel itself is exactly 36kb, so the appended code should be at address 0x109000 right? But there is nothing at this address.
Can anyone see a reason why this should not/does not work?
Thanks
Diggsey
Re: Protected Mode -> Real Mode -> Protected Mode
Likely your offsets 12-18 in the kernel's multiboot header (the AOUT kludge) doesn't include the extra data. Therefore, GRUB doesn't load it. You'd have to include it in the effective length you tell GRUB the kernel's text/data section is (ie set load_end_addr to the end of the extra code).
It would be easier in the long run to load that extra code as a module rather than to cat it to the kernel.
It would be easier in the long run to load that extra code as a module rather than to cat it to the kernel.
- Love4Boobies
- Member
- Posts: 2111
- Joined: Fri Mar 07, 2008 5:36 pm
- Location: Bucharest, Romania
Re: Protected Mode -> Real Mode -> Protected Mode
Diggsey wrote:I've written some asm to allow me to run code in real mode (for vesa, etc.). It is compiled for 16 bits with nasm to a .bin file, using a base address of 0x7C00.
- NASM doesn't compile, it assembles.
- It can't be 16-bit only, you need to mix it with 32-bit because you're doing a jump between 16-bit and 32-bit modes.
Hangin10 is correct and I'm not sure how you overlooked this. What you're trying to do is sort of a hack too and an awful programming practice. Why don't you try linking the code into the kernel instead of appending it? Just to be sure you don't try anything funny, I mention that the file needs to be ELF as well.This .bin file is then appended to the kernel's main .bin file before being mounted to the virtual floppy.
The problem is that when the kernel is running it seems as though there is no code appended to the kernel. (I just read null bytes). The kernel is loaded at 1MB and the kernel itself is exactly 36kb, so the appended code should be at address 0x109000 right? But there is nothing at this address.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
[ Project UDI ]
Re: Protected Mode -> Real Mode -> Protected Mode
@Hangin10
Thanks, that was the problem. I can't believe I forgot about that
I would definitely prefer it if I didn't have to do this 'hack' but I'm unsure how to make it so that this one asm file thinks that it begins at 0x7C00 when the other files need to be linked from 1MB.
@Love4Boobies
Thanks, that was the problem. I can't believe I forgot about that
I would definitely prefer it if I didn't have to do this 'hack' but I'm unsure how to make it so that this one asm file thinks that it begins at 0x7C00 when the other files need to be linked from 1MB.
@Love4Boobies
I'm sure you understood what I meant...NASM doesn't compile, it assembles.
Yes, I simply meant that it was assembled using the [BITS 16] directive.It can't be 16-bit only, you need to mix it with 32-bit because you're doing a jump between 16-bit and 32-bit modes.
Re: Protected Mode -> Real Mode -> Protected Mode
I tried to get the real mode code to link correctly by using this linker script instead of the previous hack:
The problem is that I get some errors from the linker:
To make sure it wasn't a problem with the ASM code, I'm testing it with some code from these forums:
Code: Select all
OUTPUT_FORMAT("binary")
OUTPUT_ARCH("i386")
ENTRY(start)
phys = 0x00100000;
real_phys = 0x7C00;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(EXCLUDE_FILE(obj/real.asm.o) .text)
*(EXCLUDE_FILE(obj/real.asm.o) .rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
.text real_phys : AT(real_phys)
{
real_code = .;
obj/real.asm.o(.text)
obj/real.asm.o(.rodata)
. = ALIGN(4096);
}
.data : AT(real_phys + (real_data - real_code))
{
real_data = .;
obj/real.asm.o(.data)
. = ALIGN(4096);
}
end = .;
}
There are more errors like the second error, but they are identical except for the offset.ld: section .data loaded at [000000000000ac00,000000000000cbff] overlaps section .text loaded at [0000000000007c00,000000000000fbff]
obj/real.asm.o:obj/real.asm.o:(.text+0x37): relocation truncated to fit: 16 against `.data'
To make sure it wasn't a problem with the ASM code, I'm testing it with some code from these forums:
Code: Select all
; Exported Function
[GLOBAL _set_vesa]
section .text
; Implementation of function
; set_vesa: C-Prototyp: void set_vesa ();
[BITS 32]
_set_vesa:
sgdt [saved_gdt]
sidt [saved_idt]
mov [saved_esp], esp
lgdt [gdtr] ; Load the GDT descriptor
;Comments from http://download.intel.com/design/PentiumII/manuals/24319202.pdf
;1. Disable interrupts. A CLI instruction disables maskable hardware interrupts. NMI
;interrupts can be disabled with external circuitry.
cli
;2. Transfer program control to a readable segment that has a limit of 64 KBytes (FFFFH).
;This operation loads the CS register with the segment limit required in real-address mode.
jmp RM_Code_Sel:pmode_16bit
;3. Load segment registers SS, DS, ES, FS, and GS with a selector for a descriptor containing
;the following values, which are appropriate for real-address mode:
;— Limit = 64 KBytes (0FFFFH)
;— Byte granular (G = 0)
;— Expand up (E = 0)
;— Writable (W = 1)
;— Present (P = 1)
;— Base = any value
;The segment registers must be loaded with nonnull segment selectors or the segment
;registers will be unusable in real-address mode. Note that if the segment registers are not
;reloaded, execution continues using the descriptor attributes loaded during protected
;mode.
[BITS 16]
pmode_16bit:
mov ax,RM_Data_Sel
mov ss,ax
mov ds,ax
;4. Execute an LIDT instruction to point to a real-address mode interrupt table that is within
;the 1-MByte real-address mode address range.
;lidt [ridtr]
;5. Clear the PE flag in the CR0 register to switch to real-address mode.
mov eax, cr0
and al,0xfe
mov cr0, eax
;6. Execute a far JMP instruction to jump to a real-address mode program. This operation
;flushes the instruction queue and loads the appropriate base and access rights values in the
;CS register.
mov sp,0xFFFE
mov bx,[RealModeCS]
push bx
lea bx,[do_rm]
push bx
retf
;7. Load the SS, DS, ES, FS, and GS registers as needed by the real-address mode code. If any
;of the registers are not going to be used in real-address mode, write 0s to them.
do_rm:
mov ax,cs
mov ds,ax
mov ss,ax
nop
mov es,ax
mov fs,ax
mov gs,ax
; point to real-mode IDTR
lidt [ridtr]
;8. Execute the STI instruction to enable maskable hardware interrupts and perform the
;necessary hardware operation to enable NMI interrupts.
sti
;We are in Real Mode, let's set 1024 x 768 x (16/32) or 800 x 600 x (16/32) or
;go back to CGA and hope for VGA
;Get VBE Controller Information - 512 byte
mov ax,0x700 ;put struct to 0x7000
mov es,ax
mov di,0x0 ;Put to 0x7000 with offset 0x0
mov eax,'VBE2' ;tell VESA-BIOS that we want VBE 2 rather than VBE 1
mov [es:di],eax
mov ax,0x4F00 ;Function to get VBE Controller Information
int 0x10
cmp al,0x4F ;Is Function supported?
jne continue_cga
cmp ah,0x00 ;successful?
jne continue_cga
mov ax,0x800 ;position for VBE Mode Information struct
mov es,ax
mov ax,0x4F01 ;function call for VBE Mode Information
mov cx,[0x8200] ;getting VBE Mode number
mov di,0x0
int 0x10
cmp al,0x4F
jne continue_cga
cmp ah,0x00
jne continue_cga
;jmp continue_cga
;Set desired modes
mov ax,0x4F02 ;loading VBE mode-setting-function
mov bx,[0x8200] ;getting VBE Mode number
or bx,0x4000 ;enable linear Buffering
int 0x10 ;go for it
jmp activate_pm
continue_cga:
activate_pm:
;1. Disable interrupts. A CLI instruction disables maskable hardware interrupts.
;NMI interrupts can be disabled with external circuitry. (Software must guarantee
;that no exceptions or interrupts are generated during the mode switching
;operation.)
cli
;2. Execute the LGDT instruction to load the GDTR register with the base
;address of the GDT.
lgdt [gdtr]
;3. Execute a MOV CR0 instruction that sets the PE flag (and optionally
;the PG flag) in control register CR0.
mov eax,cr0
or al,0x01
mov cr0,eax
;4. Immediately following the MOV CR0 instruction, execute a far JMP
;or far CALL instruction. (This operation is typically a far jump or call to
;the next instruction in the instruction stream.)
;
;The JMP or CALL instruction immediately after the MOV CR0 instruction
;changes the flow of execution and serializes the processor.
;
;If paging is enabled, the code for the MOV CR0 instruction and the JMP or
;CALL instruction must come from a page that is identity mapped (that is, the
;linear address before the jump is the same as the physical address
;after paging and protected mode is enabled). The target instruction for the JMP or CALL
;instruction does not need to be identity mapped.
jmp PM_Code_Sel:do_pm
;5. If a local descriptor table is going to be used, execute the LLDT instruction
;to load the segment selector for the LDT in the
;LDTR register.
;6. Execute the LTR instruction to load the task register with a segment
;selector to the initial protected-mode task or to a writable area of memory that can be
;used to store TSS information on a task switch.
do_pm:
;7. After entering protected mode, the segment registers conntinue to hold the contents
;they had in real-address mode. The JMP or CALL instruction in step 4 resets the CS register.
;Perform one of the following operations to update the contents of the remaining segment
;registers.
;— Reload segment registers DS, SS, ES, FS, and GS. If the ES, FS, and/or
;GS registers are not going to be used, load them with a null selector.
;— Perform a JMP or CALL instruction to a new task, which automatically resets
;the values of the segment registers and branches to a new code segment.
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov ds,ax
;8. Execute the LIDT instruction to load the IDTR register with the
;address and limit of the protected-mode IDT.
[BITS 32]
mov esp,[saved_esp]
lidt [saved_idt]
lgdt [saved_gdt]
;9. Execute the STI instruction to enable maskable hardware
;interrupts and perform the necessary hardware operation to enable NMI interrupts.
ret
section .data
;variables for "Real Mode <-> Protected Mode"-Switching
ridtr:
dw 0xFFFF ; limit=0xFFFF
dd 0 ; base=0
RealModeCS:
dw 0x0
gdtr:
dw gdt_end-1
dd gdt
gdt:
dw 0,0,0,0 ;null desrciptor
PM_Code_Sel equ $-gdt
dw 0xFFFF
dw 0x0000
dw 0x9A00
dw 0x00CF
PM_Data_Sel equ $-gdt
dw 0xFFFF
dw 0x0000
dw 0x9200
dw 0x00CF
RM_Code_Sel equ $-gdt
dw 0xFFFF
dw 0x0000
dw 0x9A00
dw 0x0000
RM_Data_Sel equ $-gdt
dw 0xFFFF
dw 0x0000
dw 0x9200
dw 0x0000
gdt_end:
saved_gdt:
dw 0
dd 0
saved_idt:
dw 0
dd 0
saved_esp:
dw 0
saved_gdtr:
dw 0
- Love4Boobies
- Member
- Posts: 2111
- Joined: Fri Mar 07, 2008 5:36 pm
- Location: Bucharest, Romania
Re: Protected Mode -> Real Mode -> Protected Mode
Exactly. And I simply meant you were wrong to do so. You need to use both [BITS 16] and [BITS 32] in the same file. I don't know if you did or not, that's how I interpreted your text.Diggsey wrote:Yes, I simply meant that it was assembled using the [BITS 16] directive.It can't be 16-bit only, you need to mix it with 32-bit because you're doing a jump between 16-bit and 32-bit modes.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
[ Project UDI ]
Re: Protected Mode -> Real Mode -> Protected Mode
@ Love4Boobies
OK, thanks. I was using a modified version of the protected mode -> real mode switch code from the wiki, which used just 16 bit mode for the whole file.
I've managed to get rid of all the errors except for:
I'm using this for the linker script:
OK, thanks. I was using a modified version of the protected mode -> real mode switch code from the wiki, which used just 16 bit mode for the whole file.
I've managed to get rid of all the errors except for:
real.asm contains the code shown in my last post.obj/real.asm.o:obj/real.asm.o:(.data+0x8): relocation truncated to fit: 16 against `.data'
obj/real.asm.o:obj/real.asm.o:(.text+0x36): relocation truncated to fit: 16 against `.data'
obj/real.asm.o:obj/real.asm.o:(.text+0x51): relocation truncated to fit: 16 against `.data'
obj/real.asm.o:obj/real.asm.o:(.text+0xa2): relocation truncated to fit: 16 against `.data'
I'm using this for the linker script:
Code: Select all
OUTPUT_FORMAT("binary")
OUTPUT_ARCH("i386")
ENTRY(start)
phys = 0x00100000;
real_phys = 0x7C00;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(EXCLUDE_FILE(obj/real.asm.o) .text)
*(EXCLUDE_FILE(obj/real.asm.o) .rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
.real_text real_phys : AT(real_phys)
{
real_code = .;
obj/real.asm.o(.text)
obj/real.asm.o(.rodata)
. = ALIGN(4096);
}
.real_data : AT(real_phys + (real_data - real_code))
{
real_data = .;
obj/real.asm.o(.data)
. = ALIGN(4096);
}
end = .;
}
- Firestryke31
- Member
- Posts: 550
- Joined: Sat Nov 29, 2008 1:07 pm
- Location: Throw a dart at central Texas
- Contact:
Re: Protected Mode -> Real Mode -> Protected Mode
Since this little bit of code is the only thing that's fixed at 0x7C00 I don't know why it's such a bad idea to assemble it as a separate binary. I would, except instead of appending it to the end of the kernel file I would convert it to an array (most likely with bin2c or some variation thereof), then in the kernel copy that to 0x7C00 to be run.
What goes on in the assembly file is a whole separate matter.
What goes on in the assembly file is a whole separate matter.
Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Protected Mode -> Real Mode -> Protected Mode
nasm/yasm's incbin directive would probably work the fastest - you can attach the symbols to the locations and values you want that way and you don't need yet another tool for the job.
Re: Protected Mode -> Real Mode -> Protected Mode
Thanks everyone. Using incbin works perfectly!
I've got one more problem though
When using the code above to get to real mode, it never returns.
In this part of the code:
I know it's working up until the 'mov ss,ax' instruction, but anything I do after that instruction seems to have no effect! I haven't even been able to make it crash. It's as though it just stops running once it reaches that part of the code.
I've got one more problem though
When using the code above to get to real mode, it never returns.
In this part of the code:
Code: Select all
[BITS 16]
pmode_16bit:
mov ax,RM_Data_Sel
mov ss,ax
mov ds,ax
- Firestryke31
- Member
- Posts: 550
- Joined: Sat Nov 29, 2008 1:07 pm
- Location: Throw a dart at central Texas
- Contact:
Re: Protected Mode -> Real Mode -> Protected Mode
If you're running in Bochs, you might consider using the debugger and setting a breakpoint. YASM (and I'm pretty sure NASM as well) allows a -l (listfile).lst which gives you the instruction offsets to help set breakpoints (note that they're offset from the beginning of the file, not the segment). Once you have set the breakpoint, type 'c' to run until you reach it(be sure you're not in the bootloader since that might also run at your breakpoint), then 's' and/or 'regs' to check out what's going on.
You might also want to be sure you load sp after 'mov ss,ax' since the cpu is designed to disable interrupts to let you finish setting up the stack in peace (don't want interrupts firing during that transition period where you don't have a quite-valid stack), though I don't know if that's the real problem.
You might also want to be sure you load sp after 'mov ss,ax' since the cpu is designed to disable interrupts to let you finish setting up the stack in peace (don't want interrupts firing during that transition period where you don't have a quite-valid stack), though I don't know if that's the real problem.
Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
Re: Protected Mode -> Real Mode -> Protected Mode
I tracked down the error to a problem with interrupts.
If I call 'int xx' in real mode, interrupts stop working after returning to protected mode. The code saves and restores IDT and GDT, and my kernel definitely calls 'sti' after the asm code returns.
Oddly enough it works fine under bochs, but fails under both virtualbox and qemu.
If I call 'int xx' in real mode, interrupts stop working after returning to protected mode. The code saves and restores IDT and GDT, and my kernel definitely calls 'sti' after the asm code returns.
Oddly enough it works fine under bochs, but fails under both virtualbox and qemu.
Re: Protected Mode -> Real Mode -> Protected Mode
I solved it by re-remapping the PIC after the real mode call. (Why does nothing mention this??? )
Re: Protected Mode -> Real Mode -> Protected Mode
That should be mentioned in the Wiki page about Real Mode. It may have been overlooked as 'obvious', but yeah, you need to restore the original PIC settings before making BIOS calls and set yours back up afterwards. Another reason to try to avoid it where possibleDiggsey wrote:I solved it by re-remapping the PIC after the real mode call. (Why does nothing mention this??? )