Why does this fail, once Masm reaches jmp?
struct gdt_entry
{
unsigned short limit_low;
unsigned short base_low;
unsigned char base_middle;
unsigned char access;
unsigned char granularity;
unsigned char base_high;
};
struct gdt_ptr
{
unsigned short limit;
unsigned int base;
};
struct gdt_entry gdt[3];
struct gdt_ptr gp;
void gdt_flush()
{
__asm{
lgdt [gp]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; push the address on the stack
push 0x08
mov eax, offset flush2
push eax
; ret use the previous pushed address
_emit 0xCB ; far return
flush2:
;ret
}
}
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}
void gdt_install()
{
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = (int)&gdt;
gdt_set_gate(0, 0, 0, 0, 0);
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
gdt_flush();
}
`
New answer:
I've already encounter this problem some time ago and the only way I found to update the GDT with MASM inline assembly is to use a far return instruction, instead of the far jump instruction.
struct gdt_entry gdt[3];
struct gdt_ptr gp;
void gdt_flush(){
__asm{
lgdt [gp]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; push the address on the stack
push 0x08
mov eax, offset flush2
push eax
; ret use the previous pushed address
_emit 0xCB ; far return
flush2:
;ret
}
}
As far as I remember, there are two problems:
- the 32 bits MASM inline assembly cannot compile far instructions, so you have to emit the opcode.
- the
jmp
instruction does not do the right stuff and you should use instead the ret
instruction to jump to the next line of code.
Also, don't call the ret instruction from the inline assembly, otherwise you'll skip the epilog code that the compiler puts at the end of the function to clean the stack.
My first answer below:
Maybe your GDT descriptor (gp) is badly initialized.
When you the jump instruction is executed, the processor try to switch to protected mode and the GDT is then required. If the GDT is not set correctly, it crashed.
The first 16 bits of gp are the size of the gdt (here 3*8 = 24 bytes) and the following 32 bytes are the address of the gdt (here &gdt[0]).
Also, make sure the ds register is null before calling lgdt: this register is used by the instruction.
Try to put the following pragma before and after the structure definitions:
#pragma pack(push,1)
struct gdt_entry
{
unsigned short limit_low;
unsigned short base_low;
unsigned char base_middle;
unsigned char access;
unsigned char granularity;
unsigned char base_high;
};
struct gdt_ptr
{
unsigned short limit;
unsigned int base;
};
#pragma pack(pop)
Although it has no effect on gdt_entry
, these instructions change the memory layout of the gdt_ptr
structure. The default behavior of the compiler is to align structures elements on 32 bits. Hence, the previous definition would be equivalent to :
struct gdt_ptr
{
unsigned short limit;
unsigned short unused;
unsigned int base;
};
which is invalid from the processor point of view.
You shifted the stack right out from under it - the ip used by ret is now pointing somewhere really wild
[edit]
You still clobber the stack - the same one used by VC. VC pushes more stuff onto the stack than just the return IP. Do a assembler-listing of the source & you'll see.
A possibility is to copy the return-address off the stack before you make the changes, and at end to just jump to where it points.
create a labeled dw to store the address:
_asm {
oldip dd ? ;this is in cs
pop eax ;eip into eax
push eax ;leave stack as found
mov oldip,eax
.
..your stuff
.
jmp far cs:[oldip]
}
I may be missing something here, but by the looks of your code you are clobbering all the segment values except cs, thereby destroying all access to previously declared variables everywhere, as well as any return address etc placed on the stack by your program ... maybe that's what you want to do, jumping off to code somewhere else, orphaning your current program ...
The above fragment should put you back at the instruction following the call to the function with the _asm stuff, but lord knows what's going to happen then.