Problems with far jump and interrupts

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Programmdude
Posts: 3
Joined: Sun Jan 03, 2016 8:28 am

Problems with far jump and interrupts

Post by Programmdude »

I have two problems, I have searched for solutions to both without much success.
First off, I have two parts, a x64 uefi loader, and a x64 elf kernel. The loader essentially parses the kernel, sticks it in at 1MB and jumps to it.

The first problem is when I attempt to start the kernel in the loader. I disable interrupts, set up the gdt, and try to far jump to it, to segment 0x10 and the kernel start address. This causes VirtualBox to kill the VM, having an issue with the instruction jmp far %rcx. It does work fine when I jump to it normally, using the segment that uefi stuck me in by default (0x28).
This isn't a massive problem yet, since it works just jumping directly there.

The relevant code:

Code: Select all

UINT16 initialGdt[] =
{
	/* gdt[0]: dummy */
	0, 0, 0, 0, 
	
	/* gdt[1]: unused */
	0, 0, 0, 0,

	/* gdt[2]: code */
	0xFFFF,		/* 4Gb - (0x100000*0x1000 = 4Gb) */
	0x0000,		/* base address=0 */
	0x9A00,		/* code read/exec */
	0x00CF,		/* granularity=4096, 386 (+5th nibble of limit) */

	/* gdt[3]: data */
	0xFFFF,		/* 4Gb - (0x100000*0x1000 = 4Gb) */
	0x0000,		/* base address=0 */
	0x9200,		/* data read/write */
	0x00CF,		/* granularity=4096, 386 (+5th nibble of limit) */
};

void StartKernel(void* address, KernelInfo* kinfo)
{
	// Disable interrupts
	__asm__ volatile("cli");
	
	SetMem(gdtAddress.base, gdtAddress.limit, 0);
	CopyMem(gdtAddress.base, initialGdt, sizeof(initialGdt));

	__asm__ volatile("lidt %0" : : "m" (idtAddress));
	__asm__ volatile("lgdt %0" : : "m" (gdtAddress));
	
	__asm__ volatile("mov %0, %%rdi" : : "m" (kinfo));
	__asm__ volatile("mov %0, %%rcx" : : "m" (address));
	__asm__ volatile("jmp *%rcx");
}
The second problem is when I try to set up interrupts. I set up the descriptor table, copy it to 0x0, call lidt and sti, and then test it with int $0. I'm unsure exactly where I am going wrong, part of the problem is the lack of documentation about x64, most of it only being relevant with real or protected mode.

interrupt.c

Code: Select all

static void SetIDT(u8 number, void* asmHandler, u16 selector, u8 flags, InterruptHandler handler)
{
	idt[number].Offset1 = (u64)asmHandler & 0xFFFF;
	idt[number].Offset2 = ((u64)asmHandler >> 16) & 0xFFFF;
	idt[number].Offset3 = ((u64)asmHandler >> 32) & 0xFFFFFFFF;
	idt[number].SegmentSelector = selector;
	idt[number].Flags = flags;
	
	isrHandlers[number] = handler;
}
void InitIDT()
{
	SerialWriteText("Interrupts: Setting up interrupts\n");
	SetIDT(0, &IDT0, 0x28, 0x8E, DefaultHandler);
...
	SetIDT(31, &IDT31, 0x28, 0x8E, DefaultHandler);
	SetIDT(238, &IDT238, 0x28, 0x8E, DefaultHandler);
	
	DescriptorTableAddress idtAddress = { sizeof(IDTDescriptor) * 255, 0 };
	memcpy(idtAddress.Base, &idt, idtAddress.Limit);
	asm volatile("lidt %0" : : "m" (idtAddress));
	asm volatile("sti");
	SerialWriteText("Interrupts: Finished setting up interrupts\n");
}
interrupt.S

Code: Select all

IDT0:
	mov $0xDEADBABE, %rdx
	hlt
	iretq
I have tried it, as the sample above shows, with it just calling hlt, yet is still ends up triple faulting.
The 0xDEADBABE isn't in the rdx register either, so I can assume that the interrupt handler isn't even called.

I'm unsure exactly what state UEFI leaves me in after I leave it. I'm fairly certain paging is disabled since cr3 doesn't have bit 31 enabled.
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Problems with far jump and interrupts

Post by jnc100 »

UEFI on the x86_64 architecture leaves you in IA-32e mode by default, which implies paging is enabled (although all memory is identity-mapped). It is bit 31 of CR0 which determines if paging is enabled. Having a quick look at your code, the first thing is that your code descriptor in the GDT doesn't specify the segment is a 64-bit segment - the 0x00cf should be 0x00af.

I can't comment on the IDT issues. Are you using the appropriate 16-byte long IDT entries for 64-bit mode? Virtualbox has a built in debugger you can use to single-step and inspect the IDT contents prior to executing your int $0.

Regards,
John.
Programmdude
Posts: 3
Joined: Sun Jan 03, 2016 8:28 am

Re: Problems with far jump and interrupts

Post by Programmdude »

I have a feeling that uefi is using paging, looking over the MemoryMapDescription that uefi gives me shows different virtual and physical addresses. However, it appears that the same virtual address is bound to different physical pages, which I find strange. Therefore when I was copying my gdt, I was copying it to 0x0 in virtual memory, which isn't the same as 0x0 in physical memory. Unfortunately this makes me feel rather stuck, so I'm thinking it might be a better idea to create a multiboot x32 loader to set up the x64 kernel myself rather then attempting to go the uefi route.
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Problems with far jump and interrupts

Post by jnc100 »

If you ask a x86_64 UEFI firmware to give you some memory (e.g. with BS->AllocatePages()) it will give you a block of memory that it linearly mapped and so you could easily use it as a GDT address. It is possible that other memory regions (e.g. for devices) are not linearly mapped, but the specification guarantees that any memory you allocate will be.

Regards,
John.
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: Problems with far jump and interrupts

Post by ggodw000 »

i am doing similar stuff and trying to get IDT working but using 16-bit ASM.
here is my post:
http://forum.osdev.org/viewtopic.php?f=1&t=29936
i just chip in here so just in case if any solution here in this thread can be used in my situation.
if it can be figured out, this is tremendous step for me.
thx!
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Problems with far jump and interrupts

Post by Brendan »

Hi,
Programmdude wrote:I have a feeling that uefi is using paging, looking over the MemoryMapDescription that uefi gives me shows different virtual and physical addresses. However, it appears that the same virtual address is bound to different physical pages, which I find strange. Therefore when I was copying my gdt, I was copying it to 0x0 in virtual memory, which isn't the same as 0x0 in physical memory. Unfortunately this makes me feel rather stuck, so I'm thinking it might be a better idea to create a multiboot x32 loader to set up the x64 kernel myself rather then attempting to go the uefi route.
The UEFI specs say (in section "2.3.4 x64 Platforms"):
UEFI wrote:
  • Paging mode is enabled and any memory space defined by the UEFI memory map is identity mapped (virtual address equals physical address). The mappings to other regions are undefined and may vary form implementation to implementation.
The can be changed by calling the "SetVirtualAddressMap()" run-time service; which can't be used before calling "ExitBootServices()" (it returns an "EFI_UNSUPPORTED" error if you try).

This leads to multiple possibilities:
  • You're mis-interpreting UEFI's memory map (and it actually is identity mapped)
  • You called "ExitBootServices()" and then called "SetVirtualAddressMap()", and are getting the information that you gave UEFI
  • Some other code has hijacked the boot process and called "ExitBootServices()" and "SetVirtualAddressMap()" before loading/starting your code
  • You've accidentally corrupted RAM that firmware relies on
  • Your firmware is extremely buggy.
:)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Programmdude
Posts: 3
Joined: Sun Jan 03, 2016 8:28 am

Re: Problems with far jump and interrupts

Post by Programmdude »

I wasn't reading UEFI's memory map, assuming it was identity mapped. Once I realised that I could ask for identity mapped memory, everything else fell into place. I was originally loading the GDT and IDT into non-identity mapped memory, which resulted in lgdt and lidt reading garbage memory. This means I can now far jump to my kernel, and load an interrupt table.
Post Reply