调试amd_iommu时想看一下iommu的device table中存的数据,通过busybox 访问amd_iommu的BAR的0地址,然后可以获取device table的地址(0x6800 01FF),但是device table是个全局的数组,其地址是内存中预留的,这个地址devmem是没有权限访问的,正如图中那样,访问该地址话,busybox提示operation not permitted。那么怎么可能访问到内存的地址呢?
查看config文件中,CONFIG_STRICT_DEVMEM为,限制用户态通过dev/mem访问所有memroy地址。
If this option is disabled, you allow userspace (root) access to all of memory, including kernel and userspace memory. Accidental access to this is obviously disastrous, but specific access can be used by people debugging the kernel. Note that with PAT support enabled, even in this case there are restrictions on /dev/mem use due to the cache aliasing requirements.
If this option is switched on, the /dev/mem file only allows userspace access to PCI space and the BIOS code and data regions. This is sufficient for dosemu and X and all common users of /dev/mem.
If in doubt, say Y.
可以看到devmem访问时被限制的,需要重新编译内核打开限制
make menuconfig,然后输入/ 搜索STRICT_DEVMEM,可以看到在kernel hacking有对应配置
进入 kernel hacking把
[ ] Filter access to /dev/mem
[ ] Filter I/O access to /dev/mem (NEW)
前面的*点掉(上下键选中,然后敲空格)
vim .config查看CONFIG_STRICT_DEVMEM是没有set的,剩下的就是编译内核了
编译内核的方式
起来后访问内存还是不行,但是提示从没有权限变成了invalid argument
对应这个问题linux/lib/Kconfig.debug有说明
我们需要在配置kernel时,关闭CONFIG_X86_PAT=n,同时开启CONFIG_EXPERT
或者直接在cmdline加上nopat的配置 [X86] Disable PAT (page attribute table extension of pagetables) support.
重启系统后,可以正常访问内存的地址了
我们可以看到devmem读取的和直接hexdump /dev/mem是一样的,只是大小端显示不同。
还有一种不用重编内核的简单方法,就是利用kretprobe把devmem_is_allowed函数强制返回1
range_is_allowed
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#define MODULE_NAME "devmem_allowed"
static char func_name[NAME_MAX] = "devmem_is_allowed";
/* 每个实例的私有数据结构 */
struct my_data {
ktime_t entry_stamp; // 记录函数进入的时间戳
};
/* 入口处理函数:记录函数进入的时间戳 */
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct my_data *data;
if (!current->mm)
return 1; // 跳过内核线程
data = (struct my_data *)ri->data;
data->entry_stamp = ktime_get(); // 获取当前时间戳
return 0;
}
/* 返回处理函数:强制设置返回值为 1 */
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
regs_set_return_value(regs, 1); // 强制设置返回值为 1
return 0;
}
/* 定义 kretprobe */
static struct kretprobe my_kretprobe = {
.handler = ret_handler, // 返回时的处理函数
.entry_handler = entry_handler, // 入口时的处理函数
.data_size = sizeof(struct my_data), // 私有数据大小
.maxactive = 20, // 最大并发实例数
};
/* 初始化 kretprobe */
static int kretprobe_init(void)
{
int ret;
my_kretprobe.kp.symbol_name = func_name; // 要监控的函数名
ret = register_kretprobe(&my_kretprobe); // 注册 kretprobe
if (ret < 0) {
pr_err("register_kretprobe failed, returned %d\n", ret);
return ret;
}
pr_info("Planted return probe at %s: %p\n",
my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
return 0;
}
/* 卸载 kretprobe */
static void kretprobe_exit(void)
{
unregister_kretprobe(&my_kretprobe);
pr_info("kretprobe at %p unregistered\n", my_kretprobe.kp.addr);
}
/* 模块初始化函数 */
static int __init devmem_allowed_init(void)
{
return kretprobe_init();
}
/* 模块退出函数 */
static void __exit devmem_allowed_exit(void)
{
kretprobe_exit();
}
/* 模块信息 */
module_init(devmem_allowed_init);
module_exit(devmem_allowed_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Force devmem_is_allowed to return 1");
MODULE_AUTHOR("Your Name");
makefile
EXTRA_CFLAGS += -I$(PWD)
MODULE_NAME = devmem_is_allowed
ifeq ($(KERNELDIR), )
KERNELDIR = /lib/modules/$(shell uname -r)/build
endif
ifeq ($(Peek), y)
KDIR = /lib/modules/$(shell uname -r)/build
else
KDIR=$(KERNELDIR)
endif
EXTRA_CFLAGS += -Wno-date-time -g
obj-m += $(MODULE_NAME).o
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
dmesg相关打印
[2697892.742080] Planted return probe at devmem_is_allowed: 00000000ae19aa25
[2697911.150814] x86/PAT: busybox:1200663 map pfn RAM range req uncached-minus for [mem 0x00100000-0x00100fff], got write-back
[2697922.624486] x86/PAT: busybox:1200730 map pfn RAM range req uncached-minus for [mem 0x00100000-0x00100fff], got write-back
[2697924.020980] x86/PAT: busybox:1200743 map pfn RAM range req uncached-minus for [mem 0x00100000-0x00100fff], got write-back
[2698426.939187] kretprobe at 00000000ae19aa25 unregistered
参考
https://lwn.net/Articles/267427/
http://blog.sina.com.cn/s/blog_6f5b220601012xbc.html
https://blog.csdn.net/anzhuangguai/article/details/54316206
https://blog.csdn.net/linuxdrivers/article/details/8498204
https://cloud.tencent.com/developer/article/1543163
https://mp.weixin.qq.com/s?__biz=Mzg2OTc0ODAzMw==&mid=2247502373&idx=1&sn=29f55e365916eccda2e7d9b98b810889&source=41#wechat_redirect