Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

virtual to physical address conversion in linux kernel

The following is used to translate virtual address to physical address in linux kernel. But what does it mean?

I have very limited knowledge of assembly

163 #define __pv_stub(from,to,instr,type)                   \
164         __asm__("@ __pv_stub\n"                         \
165         "1:     " instr "       %0, %1, %2\n"           \
166         "       .pushsection .pv_table,\"a\"\n"         \
167         "       .long   1b\n"                           \
168         "       .popsection\n"                          \
169         : "=r" (to)                                     \
170         : "r" (from), "I" (type))
like image 248
jiawen Avatar asked Nov 20 '25 04:11

jiawen


1 Answers

It's not really "assembly" as there is no instruction in this macro per se.

It's just a macro which inserts instr (an instruction passed to the macro) which has one input operand from, one immediate (constant) input operand type and a output operand to.

There is also the part between pushsection and popsection which records in a specific binary section pv_table the address of this instruction. That allows the kernel to find these places in its code if it wishes to.

The last part is the asm constraints and operands. It lists what the compiler will replace %0, %1 and %2 with. %0 is the first listed ("=r"(to)), it means that %0 will be any general purpose register, that is an output operand that will be stored in the macro argument to. The other 2 are similar except they're input operands: from is a register so gets "r" but type is an immediate so is "i"

See http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm for details

So consider this code from the kernel (http://lxr.linux.no/linux+v3.9.4/arch/arm/include/asm/memory.h#L172)

static inline unsigned long __virt_to_phys(unsigned long x)
{       unsigned long t;
        __pv_stub(x, t, "add", __PV_BITS_31_24);
        return t;
}

__pv_stub will be equivalent to t = x + __PV_BITS_31_24 (instr == add, from == x, to == t, type == __PV_BITS_31_24)

So you might wonder why anybody would do such a complicated thing instead of just writing t = x + __PV_BITS_31_24 in the code.

The reason is the pv_table I mentioned above. The address of all these statements is recorded in a specific elf section. Under some circumstances, the kernel patches these instructions at runtime (so needs to be able to easily find all of them) hence the need for a table.

The ARM port does exactly that here: http://lxr.linux.no/linux+v3.9.4/arch/arm/kernel/head.S#L541

It's used only if CONFIG_ARM_PATCH_PHYS_VIRT is used to compile the kernel:

CONFIG_ARM_PATCH_PHYS_VIRT:

Patch phys-to-virt and virt-to-phys translation functions at
boot and module load time according to the position of the
kernel in system memory.

This can only be used with non-XIP MMU kernels where the base
of physical memory is at a 16MB boundary, or theoretically 64K
for the MSM machine class.
like image 122
Guillaume Avatar answered Nov 24 '25 12:11

Guillaume



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!