【发布时间】:2018-10-02 09:51:35
【问题描述】:
我正在尝试从用户空间(通过自定义驱动程序)对内存寄存器执行一些写入操作。我想写三个 64 位整数并将变量“value_1, value_2 and value_3”初始化为 uint64_t 类型。
我必须使用 gcc inline mov 指令,并且我正在为嵌入式系统定制版本的 linux 上的 ARM 64 位架构。
这是我的代码:
asm ( "MOV %[reg1], %[val1]\t\n"
"MOV %[reg2], %[val2]\t\n"
"MOV %[reg3], %[val3]\t\n"
:[reg1] "=&r" (*register_1),[arg2] "=&r" (*register_2), [arg3] "=&r" (*register_3)
:[val1] "r"(value_1),[val2] "r" (value_2), [val3] "r" (value_3)
);
问题很奇怪... 如果我只执行两个 MOV,则代码有效。 如果我执行所有三个 MOV,整个系统崩溃,我必须重新启动整个系统。
更陌生... 如果我在第二个和第三个 MOV 之间放置一个“printf”,甚至是一个 0 纳秒的 nanosleep,代码就可以工作!
我环顾四周试图找到一个解决方案,我也使用了内存的破坏:
asm ( "MOV %[reg1], %[val1]\t\n"
"MOV %[reg2], %[val2]\t\n"
"MOV %[reg3], %[val3]\t\n"
:[reg1] "=&r" (*register_1),[arg2] "=&r" (*register_2), [arg3] "=&r" (*register_3)
:[val1] "r"(value_1),[val2] "r" (value_2), [val3] "r" (value_3)
:"memory"
);
...不起作用!
我还在第二个和第三个 MOV 之间或三个 MOV 的末尾使用了内存屏障宏:
asm volatile("": : :"memory")
..不起作用!
另外,我尝试使用指针直接写入寄存器,我有同样的行为:第二次写入后系统崩溃......
任何人都可以建议我一个解决方案..或者告诉我是否以错误的方式使用 gcc 内联 MOV 或内存屏障?
---->更多详情
这是我的主要内容:
int main()
{
int dev_fd;
volatile void * base_addr = NULL;
volatile uint64_t * reg1_addr = NULL;
volatile uint32_t * reg2_addr = NULL;
volatile uint32_t * reg3_addr = NULL;
dev_fd = open(MY_DEVICE, O_RDWR);
if (dev_fd < 0)
{
perror("Open call failed");
return -1;
}
base_addr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, xsmll_dev_fd, 0);
if (base_addr == MAP_FAILED)
{
perror("mmap operation failed");
return -1;
}
printf("BASE ADDRESS VIRT: 0x%p\n", base_addr);
/* Preparing the registers */
reg1_addr = base_addr + REG1_OFF;
reg2_addr = base_addr + REG2_OFF;
reg3_addr = base_addr + REG3_OFF;
uint64_t val_1 = 0xEEEEEEEE;
uint64_t val_2 = 0x00030010;
uint64_t val_3 = 0x01;
asm ( "str %[val1], %[reg1]\t\n"
"str %[val2], %[reg2]\t\n"
"str %[val3], %[reg3]\t\n"
:[reg1] "=&m" (*reg1_addr),[reg2] "=&m" (*reg2_addr), [reg3] "=&m" (*reg3_addr)
:[val1] "r"(val_1),[val2] "r" (val_2), [val3] "r" (val_3)
);
printf("--- END ---\n");
close(dev_fd);
return 0;
}
这是编译器关于 asm 语句的输出(linaro..I cross compile):
400bfc: f90013a0 str x0, [x29,#32]
400c00: f94027a3 ldr x3, [x29,#72]
400c04: f94023a4 ldr x4, [x29,#64]
400c08: f9402ba5 ldr x5, [x29,#80]
400c0c: f9401ba0 ldr x0, [x29,#48]
400c10: f94017a1 ldr x1, [x29,#40]
400c14: f94013a2 ldr x2, [x29,#32]
400c18: f9000060 str x0, [x3]
400c1c: f9000081 str x1, [x4]
400c20: f90000a2 str x2, [x5]
谢谢!
【问题讨论】:
-
哦,您应该使用
asm volatile,因为您需要在正常编译(使用-O2或-O3)而不是调试版本时不优化它。除此之外,IDK 看起来您的 asm 正在执行您指定的操作,因此请使用调试器单步执行它,和/或调试支持您 mmap 的设备文件的任何内核驱动程序。 -
看起来你不需要内联汇编;如果您只是
*reg1_addr = val_1;等等,编译器将为您发出那些str指令。 -
asm volatile 不能解决问题...我会尝试使用 GDB... 非常感谢您的帮助
-
我试过 *reg1_addr = val_1;我也有同样的问题...
-
那么这段代码不是问题。避免
asm只是获得等效机器代码的一种更简洁的方法,而无需使用内联汇编。您的问题可能是您选择的寄存器和值,或内核驱动程序。 (或者您是否需要在写入第一个映射位置之前将这些值放在 CPU 寄存器中,以避免从存储之间的堆栈中加载任何内容?这是您需要内联 asm 的唯一原因)
标签: gcc linux-device-driver embedded-linux inline-assembly