无法写入 Qemu-Arm 上的 Arm 协处理器寄存器
Can't Write to Arm Coprocessor Register on Qemu-Arm
我在 qemu-system-arm 上模拟 arm1176jzf-s。因为我正在尝试为 raspberry pi 零开发一个简单的准系统操作系统,为此我正在使用来自 wiki.osdev.org 的引导代码来跳转到 c 代码。在其中,我试图将一些数据写入协处理器寄存器,准确地说是“协处理器控制访问寄存器”。我可以很好地阅读,但当我尝试写作时,价值不会改变。代码是这样的:
uint32_t dest = 0, tmp = 0;
/* Read the coprocessor access control register. */
__asm ("mrc p15, 0, %[dst], c1, c0, 2"
: [dst] "=r" (dst));
log_uint(dst, 'h');
log_uint(dst, 'b');
/* Enable access for domain control access register.
Modifies dst to be 0xC0000040. */
SETBIT(dst, 6);
log_uint(dst, 'h');
log_uint(dst, 'b');
/* Write the changed value. */
__asm ("mcr p15, 0, %[dst], c1, c0, 2"
:
: [dst] "r" (dst));
/* Read the coprocessor access control register. */
__asm ("mrc p15, 0, %[tmp], c1, c0, 2"
: [tmp] "=r" (tmp));
log_uint(tmp, 'h');
log_uint(tmp, 'b');
log_uint,使用 uart 打印二进制或十六进制形式的整数。
输出如下:
C0000000
11000000000000000000000000000000
C0000040
11000000000000000000000001000000
C0000000
11000000000000000000000000000000
根据处理器的技术参考手册:您必须在更新协处理器访问控制寄存器后立即执行指令内存屏障 (IMB) 序列。
我已经尝试了 Data Synchronization Barrier 操作和 Flush 操作 Prefetch Buffer 的许多组合。但这没有用,所以我不确定如何执行此 IMB 序列,而且我找不到它。
但另一件让我烦恼的事情是,根据处理器手册,协处理器访问控制寄存器必须具有重置值 0x00000000,但我读到 0xC0000000.
在内核中,在这段代码之前,我只是初始化了 uart,没有别的,所以一切都应该是重置值。
这个问题已被来自 osdev subreddit 的 klange 澄清,解释在 comments.
您正在尝试设置对应于协处理器 4 的 CPACR 位。QEMU 的 1176 CPU 中不存在该位,因此它的 CPACR 位是 RAZ/WI。我不确定您要这样做的目的是什么——评论中提到了“域控制访问寄存器”,但是 1176 域访问控制寄存器 (DACR) 在 cp15 中,而不是 cp4 中。如果您正在复制的代码确实需要访问“协处理器 4”,那么您需要修改它以不这样做,因为无论它是什么,QEMU 都不会实现它。
高位中始终为 1 的位严格来说是 QEMU 错误,但它们是无害的——我们正在实施“如果 CPU 没有 Neon,ASEDIS 和 D32DIS 是 RAO或 D16-D31" 适用于所有 v7 内核,但较早的内核如 1176 没有这些字段,这些位实际上应该读为零。
我在 qemu-system-arm 上模拟 arm1176jzf-s。因为我正在尝试为 raspberry pi 零开发一个简单的准系统操作系统,为此我正在使用来自 wiki.osdev.org 的引导代码来跳转到 c 代码。在其中,我试图将一些数据写入协处理器寄存器,准确地说是“协处理器控制访问寄存器”。我可以很好地阅读,但当我尝试写作时,价值不会改变。代码是这样的:
uint32_t dest = 0, tmp = 0;
/* Read the coprocessor access control register. */
__asm ("mrc p15, 0, %[dst], c1, c0, 2"
: [dst] "=r" (dst));
log_uint(dst, 'h');
log_uint(dst, 'b');
/* Enable access for domain control access register.
Modifies dst to be 0xC0000040. */
SETBIT(dst, 6);
log_uint(dst, 'h');
log_uint(dst, 'b');
/* Write the changed value. */
__asm ("mcr p15, 0, %[dst], c1, c0, 2"
:
: [dst] "r" (dst));
/* Read the coprocessor access control register. */
__asm ("mrc p15, 0, %[tmp], c1, c0, 2"
: [tmp] "=r" (tmp));
log_uint(tmp, 'h');
log_uint(tmp, 'b');
log_uint,使用 uart 打印二进制或十六进制形式的整数。
输出如下:
C0000000
11000000000000000000000000000000
C0000040
11000000000000000000000001000000
C0000000
11000000000000000000000000000000
根据处理器的技术参考手册:您必须在更新协处理器访问控制寄存器后立即执行指令内存屏障 (IMB) 序列。 我已经尝试了 Data Synchronization Barrier 操作和 Flush 操作 Prefetch Buffer 的许多组合。但这没有用,所以我不确定如何执行此 IMB 序列,而且我找不到它。
但另一件让我烦恼的事情是,根据处理器手册,协处理器访问控制寄存器必须具有重置值 0x00000000,但我读到 0xC0000000.
在内核中,在这段代码之前,我只是初始化了 uart,没有别的,所以一切都应该是重置值。
这个问题已被来自 osdev subreddit 的 klange 澄清,解释在 comments.
您正在尝试设置对应于协处理器 4 的 CPACR 位。QEMU 的 1176 CPU 中不存在该位,因此它的 CPACR 位是 RAZ/WI。我不确定您要这样做的目的是什么——评论中提到了“域控制访问寄存器”,但是 1176 域访问控制寄存器 (DACR) 在 cp15 中,而不是 cp4 中。如果您正在复制的代码确实需要访问“协处理器 4”,那么您需要修改它以不这样做,因为无论它是什么,QEMU 都不会实现它。
高位中始终为 1 的位严格来说是 QEMU 错误,但它们是无害的——我们正在实施“如果 CPU 没有 Neon,ASEDIS 和 D32DIS 是 RAO或 D16-D31" 适用于所有 v7 内核,但较早的内核如 1176 没有这些字段,这些位实际上应该读为零。