以下用于将Linux内核中的虚拟地址转换为物理地址。但它是什么意思?
我对装配知识非常有限
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))
答案 0 :(得分:3)
这不是真正的“汇编”,因为这个宏本身没有指令。
它只是一个插入instr
(传递给宏的指令)的宏,它有一个输入操作数from
,一个立即(常量)输入操作数type
和一个输出操作数{ {1}}。
to
和pushsection
之间还有一个部分,它在特定的二进制部分popsection
中记录了该指令的地址。这允许内核在其代码中找到这些位置。
最后一部分是asm约束和操作数。它列出了编译器将替换pv_table
,%0
和%1
的内容。 %2
是第一个列出的(%0
),这意味着%0将是任何通用寄存器,即输出操作数将存储在宏参数"=r"(to)
中。除了它们是输入操作数之外,其他两个是相似的:to
是一个寄存器,因此得到from
但"r"
是立即的,所以type
有关详细信息,请参阅http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm
所以请考虑内核中的代码(http://lxr.linux.no/linux+v3.9.4/arch/arm/include/asm/memory.h#L172)
"i"
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
(t = x + __PV_BITS_31_24
== instr
,add
== from
,x
= = to
,t
== type
)
所以你可能想知道为什么有人会做这么复杂的事情而不只是在代码中写__PV_BITS_31_24
。
原因是我上面提到的pv_table。所有这些陈述的地址都记录在特定的精灵部分。在某些情况下,内核会在运行时修补这些指令(因此需要能够轻松找到所有这些指令),因此需要一个表。
ARM端口就是这样做的:http://lxr.linux.no/linux+v3.9.4/arch/arm/kernel/head.S#L541
仅在使用CONFIG_ARM_PATCH_PHYS_VIRT编译内核时才使用它:
t = x + __PV_BITS_31_24