【问题标题】:Reading Armv8-A registers with devmem from GNU/Linux shell从 GNU/Linux shell 使用 devmem 读取 Armv8-A 寄存器
【发布时间】:2020-05-21 02:51:57
【问题描述】:

我想读取一些Cortex-A53寄存器的值,比如

  • D_AA64ISAR0_EL1 (AArch64)
  • ID_ISAR5 (Aarch32)
  • ID_ISAR5_EL1 (Aarch64)

不幸的是,我缺乏一点嵌入式/组装经验。文档显示

要访问 ID_AA64ISAR0_EL1: 夫人,ID_AA64ISAR0_EL1;将 ID_AA64ISAR0_EL1 读入 Xt ID_AA64ISAR0_EL1[31:0] 可以通过内部存储器映射接口访问 和外部调试接口,偏移0xD30。

我决定在我的目标上使用 devmem2(因为 busybox 不包括 devmem 小程序)。以下程序读取寄存器是否正确?

devmem2 0xD30

我不确定的部分是使用“偏移量”作为直接物理地址。如果是实际地址,为什么要调用“偏移”而不是“地址”。如果是偏移量,基地址是什么?我 99% 确定这不是正确的程序,但我怎么知道要添加偏移量的基地址?我搜索了 Armv8 技术参考手册和 A53 MPCore 文档无济于事。详细解释寄存器内容,但似乎假设您使用标签 ID_AA64ISAR0_EL1 从 ASM 读取它们。

更新:

我发现了这个:

配置基地址寄存器,EL1 CBAR_EL1 的特征是: 用途 保存内存映射的 GIC CPU 的物理基地址 接口寄存器。

但它只是重复了我的问题,如何读取另一个寄存器?

更新 2: 第一次更新似乎只与 GIC 相关,而与我试图读取的配置寄存器无关(我误解了我认为的信息)。

对于手头的具体问题(检查加密扩展可用性),可以简单地 cat /proc/cpuinfo 并查找 aes/sha 等。

更新 3:

我现在正在调查http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0176c/ar01s04s01.html,以及特定于 SoC 的基地址,因此可以在 SoC 的参考手册中找到。

更新 4:

感谢出色的回答,我似乎能够通过我的内核模块读取数据:

[ 4943.461948] ID_AA64ISA_EL1 : 0x11120
[ 4943.465775] ID_ISAR5_EL1     : 0x11121

P.S.:这非常有见地,再次感谢您!

更新 5: 根据要求提供源代码:

/******************************************************************************
 *
 *   Copyright (C) 2011  Intel Corporation. All rights reserved.
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; version 2 of the License.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *****************************************************************************/

#include <linux/module.h>
#include <linux/types.h>

/*****************************************************************************/

// read system register value ID_AA64ISAR0_EL1 (s3_0_c0_c6_0).
static inline uint64_t system_read_ID_AA64ISAR0_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, ID_AA64ISAR0_EL1" : "=r" (val));
    return val;
}

// read system register value ID_ISAR5_EL1 (s3_0_c0_c2_5).
static inline uint64_t system_read_ID_ISAR5_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, s3_0_c0_c2_5" : "=r" (val));
    return val;
}

/*****************************************************************************/

int init_module(void)
{
    printk("ramdump Hello World!\n");
    printk("ID_AA64ISAR0_EL1 : 0x%llX\n", system_read_ID_AA64ISAR0_EL1());
    printk("ID_ISAR5_EL1     : 0x%llX\n", system_read_ID_ISAR5_EL1());
    return 0;
}

void cleanup_module(void)
{
    printk("ramdump Goodbye Cruel World!\n");
}

MODULE_LICENSE("GPL");

【问题讨论】:

  • 能分享一下代码吗?
  • @MichałLeon 它基本上只是一个“空”的 linux 内核模块,添加了答案中的两个函数,并在 main() 中调用它们。我稍后会尝试找到代码,必须在某个地方仍然存在...
  • 那太好了。
  • 我能找到的只有 C 文件 sn-p,不要再拘泥于手头的 bitbake 配方了。
  • 非常感谢,够了!私信我以获得信用(不需要真实姓名)。

标签: cpu-registers memory-mapping cortex-a armv8 base-address


【解决方案1】:

免责声明:我不是 Aarch64 专家,但我目前正在学习架构并阅读了一些内容。

您无法从在EL0 运行的用户模式应用程序中读取ID_AA64ISAR0_EL1ID_ISAR5_EL1ID_ISAR5_EL1 后缀意味着至少需要在EL1 运行才能被允许读取这两个寄存器。

阅读 arm 文档 herehere 中的伪代码可能会有所帮助。 以ID_ISAR5为例,伪代码非常明确:

if PSTATE.EL == EL0 then
    UNDEFINED;
elsif PSTATE.EL == EL1 then
    if EL2Enabled() && !ELUsingAArch32(EL2) && HSTR_EL2.T0 == '1' then
        AArch64.AArch32SystemAccessTrap(EL2, 0x03);
    elsif EL2Enabled() && ELUsingAArch32(EL2) && HSTR.T0 == '1' then
        AArch32.TakeHypTrapException(0x03);
    elsif EL2Enabled() && !ELUsingAArch32(EL2) && HCR_EL2.TID3 == '1' then
        AArch64.AArch32SystemAccessTrap(EL2, 0x03);
    elsif EL2Enabled() && ELUsingAArch32(EL2) && HCR.TID3 == '1' then
        AArch32.TakeHypTrapException(0x03);
    else
        return ID_ISAR5;
elsif PSTATE.EL == EL2 then
    return ID_ISAR5;
elsif PSTATE.EL == EL3 then
    return ID_ISAR5;

读取这些寄存器的一种简单方法是编写一个可从用户模式应用程序调用的微型可加载内核模块:由于 Linux 内核运行在 EL1,因此它完全能够读取这三个寄存器。

请参阅this 文章,了解 Linux 可加载内核模块的精彩介绍。

这很可能是在EL0 上运行的应用程序无法访问只能从EL1 访问的内存映射寄存器,因为这显然会破坏保护方案。

在 Aarch64 状态下读取这些寄存器所需的 C 代码 sn-ps 将是(使用 gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu 测试):

#include <stdint.h>

// read system register value ID_AA64ISAR0_EL1 (s3_0_c0_c6_0).
static inline uint64_t system_read_ID_AA64ISAR0_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, s3_0_c0_c6_0" : "=r" (val));
    return val;
}

// read system register value ID_ISAR5_EL1 (s3_0_c0_c2_5).
static inline uint64_t system_read_ID_ISAR5_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, s3_0_c0_c2_5" : "=r" (val));
    return val;
}

更新 #1: GCC 工具链不能理解所有 arm 系统寄存器名称,但如果指定 coproc、opc1、CRn、CRm 和 opc2 字段的哪些确切值与此寄存器相关联,则可以正确编码系统寄存器访问指令。

对于ID_AA64ISAR0_EL1,在Arm® Architecture Registers Armv8, for Armv8-A architecture profile文档are中指定的值:

coproc=0b11opc1=0b000CRn=0b0000CRm=0b0110opc2=0b000

系统寄存器别名将是s[coproc]_[opc1]_c[CRn]_c[CRm]_[opc2],在ID_AA64ISAR0_EL1 的情况下为s3_0_c0_c6_0

【讨论】:

  • devmem 使用 /dev/mem 设备映射物理地址,这可以通过 root 权限(我拥有)来实现。我已经有一个“hello world”最小内核模块(目前什么都不做)。但我需要弄清楚如何使用内联汇编(用于 MRS 指令),或者使用我不知道的现有包装器。
  • @smoothware:我明白了,我相应地扩充了我的答案。
  • @smoothware:您是否同意 arm 设计了 ​​Aarch64 架构,使得运行在 EL0 的用户应用程序无法读取 ID_AA64ISAR0_EL1ID_ISAR5_EL1,即使具有 root 权限,因此允许root 用户使用/dev/mem 访问他们会违反他们的保护方案吗?在 UID 0 上运行的 Linux 应用程序仍在 Aarch64 EL0 上运行(我猜)。
  • @smoothware:Exception model 文档非常有趣,恕我直言。
  • 谢谢,我会尽快尝试。您能解释一下为什么将“s3_0_c0_c6_0”指定为寄存器,而不是我的文档指定的“ID_AA64ISAR0_EL1”吗?你是怎么想出使用那个标签的?提前致谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-02
  • 2020-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多