查看“Inline Assembly/Examples”的源代码
←
Inline Assembly/Examples
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
以下是一些[[Inline Assembly|内联汇编]]函数的集合,这些函数非常普遍,以至于它们对于使用GCC的大多数操作系统开发人员来说应该是有用的。 其它编译器可能有内在的替代方案(参见参考资料)。 请注意这些函数是如何使用[[C]]语言的GNU扩展实现的,如果禁用GNU扩展,特定的关键字可能会给你带来麻烦。 如果你在保留的命名空间中使用替代关键字,例如 <tt>__asm__</tt>,你仍然可以使用禁用的关键字,例如 <tt>asm</tt>。 小心内联汇编:编译器不理解它发出的汇编代码,如果你对编译器乱写,可能会导致罕见的严重错误。 '''警告!''' 请注意,<tt>asm("");</tt> <small>(无constants,称为 [https://gcc.gnu.org/onlinedocs/gcc/Basic-Asm.html 基本 <tt>asm</tt>])</small> 和 <tt>asm("":);</tt> <small>(空constraints,[https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html 扩展 <tt>asm</tt>] 的形式)</small> 是不一样的! 除其它差异外,在basic<tt>asm</tt>中,必须使用单个百分号“%”作为寄存器前缀,在扩展<tt>asm</tt>中(即使constraints列表为空),也必须在汇编模板字符串中使用“%”作为前缀。 如果你收到类似“Error: bad register name '%%eax'”之类的GCC错误消息,那么你应该在右括号前插入冒号(‘:’)。 扩展 <tt>asm</tt> 通常是首选的,尽管在 [https://gcc.gnu.org/onlinedocs/gcc/Basic-Asm.html#Remarks 有些情况下,基本 <tt>asm</tt> 是必需的]。 还要注意,这个寄存器前缀只适用于AT&T语法汇编,这是gcc的默认值。 ==内存访问== === FAR_PEEKx === 使用默认C data segment以外的另一段读取给定内存位置的8/16/32位值。 不幸的是,直接操作段寄存器没有constraint, 因此,需要手动发布<tt>mov<reg>,<segmentreg></tt>。 <source lang="c"> static inline uint32_t farpeekl(uint16_t sel, void* off) { uint32_t ret; asm ( "push %%fs\n\t" "mov %1, %%fs\n\t" "mov %%fs:(%2), %0\n\t" "pop %%fs" : "=r"(ret) : "g"(sel), "r"(off) ); return ret; } </source> === FAR_POKEx === 将8/16/32位值写入segment:offset地址。 请注意,与farpeek非常相似,farpoke的这个版本保存并恢复用于访问的段寄存器。 <source lang="c"> static inline void farpokeb(uint16_t sel, void* off, uint8_t v) { asm ( "push %%fs\n\t" "mov %0, %%fs\n\t" "movb %2, %%fs:(%1)\n\t" "pop %%fs" : : "g"(sel), "r"(off), "r"(v) ); /* TODO: Should "memory" be in the clobber list here? */ } </source> == I/O access == === OUTx === 在I/O位置发送8/16/32位值。 传统命名为<tt>outb</tt>、<tt>outu</tt>和<tt>outl</tt>。 <tt>a</tt>修饰符强制在发出ASM命令之前将val放在eax寄存器中,而<tt>Nd</tt>允许将一个字节的常量值组装为常量,从而释放edX寄存器以供其它情况使用。 <source lang="c"> static inline void outb(uint16_t port, uint8_t val) { asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). * The outb %al, %dx encoding is the only option for all other cases. * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ } </source> === INx === 从I/O位置接收8/16/32位值。 传统命名为<tt>inb</tt>、<tt>inw</tt>和<tt>inl</tt>。 <source lang="c"> static inline uint8_t inb(uint16_t port) { uint8_t ret; asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) ); return ret; } </source> === IO_WAIT === 等待很短的时间(通常为1到4微秒)。 用于在旧硬件上实现PIC重新映射的小延迟,或通常作为简单但不精确的等待。 你可以在任何未使用的端口上执行IO操作:Linux内核默认使用端口0x80,该端口通常在POST(上电自检)期间用于在主板的十六进制显示上记录信息,但在引导后几乎不会再使用。 <source lang="c"> static inline void io_wait(void) { outb(0x80, 0); } </source> == 中断相关函数 == ===启用关闭=== 如果为CPU启用irq,则返回一个<tt>true</tt>布尔值。 <source lang="c"> static inline bool are_interrupts_enabled() { unsigned long flags; asm volatile ( "pushf\n\t" "pop %0" : "=g"(flags) ); return flags & (1 << 9); } </source> === Push/pop出中断标志 === 有时,禁用中断,然后仅当中断以前被禁用时才重新启用它们是有帮助的。 尽管上述函数也可用于此目的,但以下函数无论IF的状态设置如何,都可以用相同的方式进行: <source lang="c"> static inline unsigned long save_irqdisable(void) { unsigned long flags; asm volatile ("pushf\n\tcli\n\tpop %0" : "=r"(flags) : : "memory"); return flags; } static inline void irqrestore(unsigned long flags) { asm ("push %0\n\tpopf" : : "rm"(flags) : "memory","cc"); } static void intended_usage(void) { unsigned long f = save_irqdisable(); do_whatever_without_irqs(); irqrestore(f); } </source> Memory clobber强制排序,cc clobber,因为在第二个示例中,所有条件代码都被覆盖。 在第二种情况下没有volatile,因为它没有输出,因此总是volatile的。 === LIDT === 定义一个新的中断表。 <source lang="c"> static inline void lidt(void* base, uint16_t size) { // This function works in 32 and 64bit mode struct { uint16_t length; void* base; } __attribute__((packed)) IDTR = { size, base }; asm ( "lidt %0" : : "m"(IDTR) ); // let the compiler choose an addressing mode } </source> == CPU相关函数 == === CPUID === 请求CPU标识。 有关更多信息,请参见[[CPUID]]。 <source lang="c"> /* GCC has a <cpuid.h> header you should use instead of this. */ static inline void cpuid(int code, uint32_t* a, uint32_t* d) { asm volatile ( "cpuid" : "=a"(*a), "=d"(*d) : "0"(code) : "ebx", "ecx" ); } </source> === RDTSC === 读取CPU时间戳计数器的当前值并存储到EDX:EAX中。 时间戳计数器包含自上次CPU重置以来经过的时钟滴答数。 该值存储在64位MSR中,并在每个时钟周期后递增。 <source lang="c"> static inline uint64_t rdtsc() { uint64_t ret; asm volatile ( "rdtsc" : "=A"(ret) ); return ret; } </source> 这可以用来找出执行某些功能所需的时间,对于测试/基准测试等非常有用。 注意:这只是一个近似值。 在x86_64上,“A”constraint期望写入“rdx:rax”寄存器,而不是“edx:eax”。 因此,GCC实际上可以通过根本不设置“rdx”来优化上述代码。 相反,你需要使用位移位手动执行此操作: <source lang="c"> uint64_t rdtsc(void) { uint32_t low, high; asm volatile("rdtsc":"=a"(low),"=d"(high)); return ((uint64_t)high << 32) | low; } </source> 有关更多详细信息,请参阅[https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints GCC Inline Assembly Machine Constraints]。 === READ_CRx === 读取控制寄存器中的值。 <source lang="c"> static inline unsigned long read_cr0(void) { unsigned long val; asm volatile ( "mov %%cr0, %0" : "=r"(val) ); return val; } </source> === INVLPG === 使一个特定虚拟地址的TLB(转换查找缓冲区)无效。 页面的下一个内存引用将被强制从主内存重新读取PDE和PTE。 必须在每次更新其中一个表时发出。 <tt>m</tt>指针指向逻辑地址,而不是物理或虚拟地址:ds段的偏移量。 <source lang="c"> static inline void invlpg(void* m) { /* Clobber memory to avoid optimizer re-ordering access before invlpg, which may cause nasty bugs. */ asm volatile ( "invlpg (%0)" : : "b"(m) : "memory" ); } </source> === WRMSR === 将64位值写入MSR。 <tt>A</tt>constraint代表寄存器EAX和EDX的串联。 <source lang="c"> static inline void wrmsr(uint32_t msr_id, uint64_t msr_value) { asm volatile ( "wrmsr" : : "c" (msr_id), "A" (msr_value) ); } </source> 在x86_64上,这需要是: <source lang="c"> static inline void wrmsr(uint64_t msr, uint64_t value) { uint32_t low = value & 0xFFFFFFFF; uint32_t high = value >> 32; asm volatile ( "wrmsr" : : "c"(msr), "a"(low), "d"(high) ); } </source> === RDMSR === 从MSR读取64位值。 <tt>A</tt>constraint表示寄存器EAX和EDX的串联。 <source lang="c"> static inline uint64_t rdmsr(uint32_t msr_id) { uint64_t msr_value; asm volatile ( "rdmsr" : "=A" (msr_value) : "c" (msr_id) ); return msr_value; } </source> 在x86_64上,这需要: <source lang="c"> static inline uint64_t rdmsr(uint64_t msr) { uint32_t low, high; asm volatile ( "rdmsr" : "=a"(low), "=d"(high) : "c"(msr) ); return ((uint64_t)high << 32) | low; } </source> [[Category:Assembly]]
返回至“
Inline Assembly/Examples
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息