Transitioning from real to protected mode in the Linux kernel

I am currently studying the low-level organization of operating systems. To achieve this, I am trying to understand how the Linux kernel boots.

What I cannot understand is the transition from 16-bit (real mode) to 32-bit (protected mode). This happens in this file .

The protected_mode_jump function performs various auxiliary calculations for the 32-bit code, which is executed later, then turns on the PE bit in the CR0 registry

  movl %cr0, %edx orb $X86_CR0_PE, %dl # Protected mode movl %edx, %cr0 

and after that makes a long jump to 32-bit code:

  # Transition to 32-bit mode .byte 0x66, 0xea # ljmpl opcode 2: .long in_pm32 # offset .word __BOOT_CS # segment 

As far as I understand, in_pm32 is the address of the 32-bit function declared below protected_mode_jump :

  .code32 .section ".text32","ax" GLOBAL(in_pm32) # some code # ... # some code ENDPROC(in_pm32) 

The base of the __BOOT_CS sector is 0 (GDT is pre-set here ), so this means that the offset should be basically the absolute address of the in_pm32 function.

This is problem. During the generation of machine code, the assembler / linker does not need to know the absolute address of the in_pm32 function, since it does not know where it will be loaded into memory in real mode (different loaders can take up different amounts of space, and the kernel of the real mode loads immediately after the loader).

In addition, the script linker ( setup.ld in the same folder) sets the beginning of the code to 0, so it seems that in_pm32 address will be the offset from the beginning of the real mode kernel. It should work fine with 16-bit code, because the CS register is set correctly, but when a long transition occurs, the CPU is already in protected mode, so the relative offset should not work.

So my question is: Why .byte 0x66, 0xea long jump in protected mode ( .byte 0x66, 0xea ) set the correct code position if the offset ( .long in_pm32 ) is relative?

It seems like I'm missing something really important.

+5
source share
1 answer

It looks like your question is really about how the offset stored in the next line can work, since it refers to the beginning of a segment, not necessarily to the beginning of memory:

  2: .long in_pm32 # offset 

It is true that in_pm32 refers to the offset used by the script linker . In particular, the script linker has:

 . = 0; .bstext : { *(.bstext) } .bsdata : { *(.bsdata) } . = 495; .header : { *(.header) } .entrytext : { *(.entrytext) } .inittext : { *(.inittext) } .initdata : { *(.initdata) } __end_init = .; .text : { *(.text) } .text32 : { *(.text32) } 

The virtual memory address is set to zero (and subsequently 495), so you might think that something in the .text32 section should be fixed in small memory. This would be a good observation, if not for these instructions in protected_mode_jump :

  xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx addl %ebx, 2f [snip] # Transition to 32-bit mode .byte 0x66, 0xea # ljmpl opcode 2: .long in_pm32 # offset .word __BOOT_CS # segment 

At the end, there is a manually encoded JMP FAR that is used to set the CS selector to a 32-bit code descriptor to complete the transition to 32-bit protected mode. But the main thing to observe is specifically in these lines:

  xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx addl %ebx, 2f 

This takes a value in CS and shifts it by 4 bits (multiplied by 16), and then adds it to the value stored on the 2f label. Thus, you take the real segment mode: offset and convert it to a linear address (which in this case coincides with the physical address). The 2f shortcut is actually the in_pm32 offset in this line:

 2: .long in_pm32 # offset 

When this instruction is complete, the in_pm32 long word in_pm32 in the JMP FAR will be adjusted (at run time) by adding the linear address of the current real-mode code segment to the in_pm32 value. This .long (DWORD) value will be replaced with (CS <4) + in_pm32.

This code was designed to move to any segment of real mode. The final linear address is computed at runtime before the JMP FAR. This is really self-modifying code.

+5
source

Source: https://habr.com/ru/post/1263134/


All Articles