1

I'm trying to understand the difference between far JMP and far CALL instructions for the x86-64 CPU. If I'm following Intel documentation correctly for a 64-bit long mode:

A) Far CALL (opcode 48, FF, /3) or in asm:

call tbyte ptr [rcx]

can be used for a "code segment" or a "call gate" call to the same or different code segment, depending on the GDT or LDT of the segment selector used. Where:

  • "Code segment" can specify only the same privilege (CPL) level call. It can be a call to a 32-bit compatibility mode code, or to the same 64-bit long mode (depending on the L bit.)

  • "Call gate" can specify same privilege level (CPL) call or inter-privilege call. But it cannot jump to a 32-bit compatibility mode, and only to the same 64-bit long mode.

B) Far JMP (opcode 48, FF, /5) or in asm:

jmp tbyte ptr [rcx] 

I can't find much documentation for the 64-bit long mode. Is it pretty much the same specs as for a far CALL?

I can't seem to find any reference for inter-privilege "call gate" JMPs into a different CPL, or being able to jump to a 32-bit compatibility mode from a long 64-bit mode.

Can someone clarify this.

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • 1
    The Intel manuals should explain how this works. Did you have a look at them? – fuz May 31 '18 at 19:48
  • @fuz: yes....... – c00000fd May 31 '18 at 19:51
  • Do you have a specific use-case in mind for this? I'm not sure exactly what you can do with a call gate, but modern OSes use `syscall` to allow less-privileged code to call into the kernel. That's the most efficient mechanism on current hardware, so you should probably only look at call gates if you can't do what you want with `syscall`. – Peter Cordes May 31 '18 at 20:38
  • You can far `jmp` to a code segment without the L bit set, though, if you want to run some 32-bit code in your kernel (instead of just returning to 32-bit user-space.) User-space switching to 32-bit mode is possible if you know what CS value to use. But it's not usually useful, and the OS may return to user-space in 64-bit mode after system calls or something under Linux; [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/q/46087730) includes a brief mention of using an alternate CS. – Peter Cordes May 31 '18 at 20:42
  • @PeterCordes: No. I just want to better understand how far jumps work. There seems to be a lot in the Intel docs about a far call but then it's pretty brief on the far jmp for a 64-bit long mode. Have you dealt much with that instruction? (Note, it's not a syscall/sysenter. A far jmp supposedly can do more than just a specialized ring-0 call.) – c00000fd May 31 '18 at 20:43
  • Yeah, testing is kind sketchy. I'm on Windows and so far all I can do is break in my own test driver in a VM, modify the code on the fly and then run it. Then after the BSOD reboot and repeat. Takes a long time though. I wish there was an easier way to run tests .... – c00000fd May 31 '18 at 20:45
  • I meant if you were thinking about using a far call or jmp across privilege levels, `syscall` is probably a better choice. For other use-cases, sure you need a far `jmp` to change to 64-bit mode at bootup. See https://wiki.osdev.org/Segmentation#Operations_that_affect_segment_registers and https://wiki.osdev.org/Global_Descriptor_Table. – Peter Cordes May 31 '18 at 20:46
  • @c00000fd It might be useful if you could tell us what part of the Intel manual's descriptions you don't understand. Otherwise, it's kinda hard to know where to pick you up with an answer. – fuz May 31 '18 at 21:28

1 Answers1

3

An inter-privilege-level far call is possible in 64-bit through a call gate. The code segment descriptor specified by the target call gate must be non-conforming and its DPL must be smaller than the CPL. Then the new CPL is set to the DPL. On the other hand, inter-privilege-level control transfer is not possible with a far jump. That is, if the code segment descriptor specified by the call gate is non-conforming and DPL < CPL, then a general protection (GP) exception occurs.

You can't JMP to a non-64-bit segment from a 64-bit segment. Otherwise, a GP occurs.

Hadi Brais
  • 22,259
  • 3
  • 54
  • 95
  • Oh, thanks for clarifying it. Because all I was getting in my tests were #GPs. I was blaming myself for not setting up the call gate right. So now it makes sense. Also just from a curiosity, what are those "non-conforming things"? I keep seeing references to it all over the Intel pdf. – c00000fd May 31 '18 at 21:10
  • 1
    @c00000fd The term "non-conforming" refers to how the CPL changes. "Conforming" means that the CPL will remain the same even if the control transfer is to a more privileged segment. "Non-conforming" means that the CPL will change; it will be set to the DPL. BTW, both calls and jumps don't allow to transfer control to a less-privileged code segment. – Hadi Brais May 31 '18 at 21:17
  • Oh, that was easy. Thanks. Why wouldn't they just call it that. (Rhetorical question.) So yeah, to jump to a less privileged cs you need IRET or RETF instruction, right? – c00000fd May 31 '18 at 21:22
  • Also if I'm seeing it correctly, with a far JMP even higher CPL jumps are not allowed. Only to the segment with the same CPL. Is that true? – c00000fd May 31 '18 at 21:27
  • @c00000fd Yes and yes. – Hadi Brais May 31 '18 at 22:44
  • OK. Thanks for sharing this info. – c00000fd May 31 '18 at 22:59