在内核中处理未定义的指令

问题描述 投票:6回答:1

因此,我正在研究内核中的系统寄存器,最近遇到了一些障碍。

[在ARM64中,某些系统寄存器(例如OSECCR_EL1)并非总是实现。如果实现了它们,那么尝试使用mrs指令就可以了-没有不好的事情发生。但是,如果未实现它们,则由于未定义的指令,内核将引发“糟糕”。

然而,这并不是没有道理的,因为我在运行此mrs指令时处于内核模块中,所以我看不到从该oop中恢复的简便方法,甚至没有意识到读取了特定的系统寄存器首先将失败。

是否有任何简便的方法可以预先确定系统寄存器是否有效,或者至少以一种不会立即停止我的内核模块函数执行的方式来处理内核操作?

c linux-kernel kernel kernel-module arm64
1个回答
2
投票
用于ARM的Linux内核具有处理未定义指令以仿真它们的方式,这是通过在arch/arm64/include/asm/traps.h中定义的简单“未定义指令钩子”完成的:

struct undef_hook { struct list_head node; u32 instr_mask; u32 instr_val; u64 pstate_mask; u64 pstate_val; int (*fn)(struct pt_regs *regs, u32 instr); };

这些钩子是通过(不幸的是未导出)函数register_undef_hook()添加的,而通过register_undef_hook()删除的。

为了解决您的问题,您可以通过修改unregister_undef_hook()并添加以下两行代码来导出这两个函数:

unregister_undef_hook()

现在重新编译内核,函数将被导出并可以在模块中使用。现在,您可以轻松地按自己的意愿处理未定义的指令。

这里是一个示例模块,正是这样做的:

arch/arm64/kernel/traps.c

这里是// after register_undef_hook EXPORT_SYMBOL(register_undef_hook); // after unregister_undef_hook EXPORT_SYMBOL(unregister_undef_hook); 输出:

// SPDX-License-Identifier: GPL-3.0
#include <linux/init.h>   // module_{init,exit}()
#include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
#include <linux/kernel.h>
#include <asm/traps.h>    // struct undef_hook, register_undef_hook()
#include <asm/ptrace.h>   // struct pt_regs

#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

static void whoops(void)
{
    // Execute a known invalid instruction.
    asm volatile (".word 0xf7f0a000");
}

static int undef_instr_handler(struct pt_regs *regs, u32 instr)
{
    pr_info("*gotcha*\n");

    // Just skip over to the next instruction.
    regs->pc += 4;

    return 0; // All fine!
}

static struct undef_hook uh = {
    .instr_mask  = 0x0, // any instruction
    .instr_val   = 0x0, // any instruction
    .pstate_mask = 0x0, // any pstate
    .pstate_val  = 0x0, // any pstate
    .fn          = undef_instr_handler
};

static int __init modinit(void)
{
    register_undef_hook(&uh);

    pr_info("Jumping off a cliff...\n");
    whoops();
    pr_info("Woah, I survived!\n");

    return 0;
}

static void __exit modexit(void)
{
    unregister_undef_hook(&uf);
}

module_init(modinit);
module_exit(modexit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Test undefined instruction handling on arm64.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");

一些笔记

    上面的示例将dmesg指令/ pstate掩码/值设置为[ 1.508253] testmod: Jumping off a cliff... [ 1.508781] testmod: *gotcha* [ 1.509207] testmod: Woah, I survived! ,这意味着将为执行的[[any未定义指令调用该挂钩。您可能希望将其限制为undef_hook,并且应该可以这样做:
  • 0x0

msr XX,YY匹配除操作数以外的所有内容(根据// didn't test these, you might want to double-check .instr_mask = 0xfff00000, .instr_val = 0xd5100000, ,PDF的第779页)。您可以0xfff00000查看如何检查这些值以确定是否调用该挂钩,这非常简单。您还可以检查传递给挂钩的the manual值。

您可以查看look at the source code以了解其定义。您可能只想跳过说明并打印一些内容,在这种情况下,我在上面的示例中所做的就足够了。如果需要,您可以更改任何寄存器值。

  • 在Linux内核v5.6,instr上测试。
  • © www.soinside.com 2019 - 2024. All rights reserved.