首页 > 解决方案 > 从 GNU/Linux shell 使用 devmem 读取 Armv8-A 寄存器

问题描述

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

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

访问 ID_AA64ISAR0_EL1: MRS , 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

PS: 这非常有见地,再次感谢您!

更新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");

标签: cpu-registersmemory-mappingcortex-aarmv8base-address

解决方案


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

您无法读取ID_AA64ISAR0_EL1ID_ISAR5_EL1ID_ISAR5无法从运行 at 的用户模式应用程序中读取EL0_EL1后缀意味着至少运行 atEL1是允许读取这两个寄存器所必需的。

阅读此处此处的 arm 文档中的伪代码可能会对您有所帮助。以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,它完全能够读取这三个寄存器。

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

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

在 Aarch64 状态下读取这些寄存器所需的 C 代码片段将是(使用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 字段的哪些确切值与此寄存器相关联,则可以正确编码系统寄存器访问指令。

在 的情况下, Arm® 架构寄存器 Armv8 中为 Armv8-A 架构配置文件ID_AA64ISAR0_EL1指定的值

coproc=0b11, opc1=0b000, CRn=0b0000, CRm=0b0110,opc2=0b000

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


推荐阅读