我正在尝试直接访问嵌入式Linux项目的物理内存,但我不确定如何最好地为我的内容指定内存.
如果我定期启动我的设备,并访问/ dev/mem,我可以轻松地读取和写入我想要的任何地方.但是,在这里,我正在访问可以轻松分配给任何进程的内存; 这是我不想做的
我的/ dev/mem代码是(所有错误检查等删除):
mem_fd = open("/dev/mem", O_RDWR)); mem_p = malloc(SIZE + (PAGE_SIZE - 1)); if ((unsigned long) mem_p % PAGE_SIZE) { mem_p += PAGE_SIZE - ((unsigned long) mem_p % PAGE_SIZE); } mem_p = (unsigned char *) mmap(mem_p, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, mem_fd, BASE_ADDRESS);
这很有效.但是,我想使用其他人无法触及的记忆.我已经尝试通过使用mem = XXXm启动来限制内核看到的内存量,然后将BASE_ADDRESS设置为高于该值(但低于物理内存),但它似乎并不是一致地访问相同的内存.
根据我在网上看到的内容,我怀疑我可能需要一个使用ioremap()或remap_pfn_range()(或两者兼而有之)的内核模块(可以),但我完全不知道如何; 有人可以帮忙吗?
编辑:我想要的是一种总是访问相同物理内存(比如,1.5MB)的方法,并将该内存放在一边,以便内核不会将其分配给任何其他进程.
我正在尝试重现我们在其他操作系统中的系统(没有内存管理),我可以通过链接器在内存中分配一个空间,并使用类似的方式访问它
*(unsigned char *)0x12345678
EDIT2:我想我应该提供更多细节.此内存空间将用于RAM缓冲区,以用于嵌入式应用程序的高性能日志记录解决方案.在我们拥有的系统中,在软重启期间没有什么可以清除或扰乱物理内存.因此,如果我向物理地址X写入一个位并重新启动系统,则重新启动后仍将设置相同的位.这已经在运行VxWorks的完全相同的硬件上进行了测试(这种逻辑在不同平台上的Nucleus RTOS和OS20上也很好用,FWIW).我的想法是通过直接解决物理内存在Linux中尝试相同的事情; 因此,每次启动时都必须获得相同的地址.
我应该澄清这是针对内核2.6.12和更新的.
EDIT3:这是我的代码,首先是内核模块,然后是用户空间应用程序.
要使用它,我用mem = 95m启动,然后insmod foo-module.ko,然后mknod mknod/dev/foo c 32 0,然后运行foo-user,它会死掉.在gdb下运行表明它在赋值时死了,虽然在gdb中,我无法取消引用从mmap获得的地址(虽然printf可以)
FOO-的module.c
#include#include #include #include #include #include #define VERSION_STR "1.0.0" #define FOO_BUFFER_SIZE (1u*1024u*1024u) #define FOO_BUFFER_OFFSET (95u*1024u*1024u) #define FOO_MAJOR 32 #define FOO_NAME "foo" static const char *foo_version = "@(#) foo Support version " VERSION_STR " " __DATE__ " " __TIME__; static void *pt = NULL; static int foo_release(struct inode *inode, struct file *file); static int foo_open(struct inode *inode, struct file *file); static int foo_mmap(struct file *filp, struct vm_area_struct *vma); struct file_operations foo_fops = { .owner = THIS_MODULE, .llseek = NULL, .read = NULL, .write = NULL, .readdir = NULL, .poll = NULL, .ioctl = NULL, .mmap = foo_mmap, .open = foo_open, .flush = NULL, .release = foo_release, .fsync = NULL, .fasync = NULL, .lock = NULL, .readv = NULL, .writev = NULL, }; static int __init foo_init(void) { int i; printk(KERN_NOTICE "Loading foo support module\n"); printk(KERN_INFO "Version %s\n", foo_version); printk(KERN_INFO "Preparing device /dev/foo\n"); i = register_chrdev(FOO_MAJOR, FOO_NAME, &foo_fops); if (i != 0) { return -EIO; printk(KERN_ERR "Device couldn't be registered!"); } printk(KERN_NOTICE "Device ready.\n"); printk(KERN_NOTICE "Make sure to run mknod /dev/foo c %d 0\n", FOO_MAJOR); printk(KERN_INFO "Allocating memory\n"); pt = ioremap(FOO_BUFFER_OFFSET, FOO_BUFFER_SIZE); if (pt == NULL) { printk(KERN_ERR "Unable to remap memory\n"); return 1; } printk(KERN_INFO "ioremap returned %p\n", pt); return 0; } static void __exit foo_exit(void) { printk(KERN_NOTICE "Unloading foo support module\n"); unregister_chrdev(FOO_MAJOR, FOO_NAME); if (pt != NULL) { printk(KERN_INFO "Unmapping memory at %p\n", pt); iounmap(pt); } else { printk(KERN_WARNING "No memory to unmap!\n"); } return; } static int foo_open(struct inode *inode, struct file *file) { printk("foo_open\n"); return 0; } static int foo_release(struct inode *inode, struct file *file) { printk("foo_release\n"); return 0; } static int foo_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; if (pt == NULL) { printk(KERN_ERR "Memory not mapped!\n"); return -EAGAIN; } if ((vma->vm_end - vma->vm_start) != FOO_BUFFER_SIZE) { printk(KERN_ERR "Error: sizes don't match (buffer size = %d, requested size = %lu)\n", FOO_BUFFER_SIZE, vma->vm_end - vma->vm_start); return -EAGAIN; } ret = remap_pfn_range(vma, vma->vm_start, (unsigned long) pt, vma->vm_end - vma->vm_start, PAGE_SHARED); if (ret != 0) { printk(KERN_ERR "Error in calling remap_pfn_range: returned %d\n", ret); return -EAGAIN; } return 0; } module_init(foo_init); module_exit(foo_exit); MODULE_AUTHOR("Mike Miller"); MODULE_LICENSE("NONE"); MODULE_VERSION(VERSION_STR); MODULE_DESCRIPTION("Provides support for foo to access direct memory");
FOO-user.c的
#include#include #include #include #include int main(void) { int fd; char *mptr; fd = open("/dev/foo", O_RDWR | O_SYNC); if (fd == -1) { printf("open error...\n"); return 1; } mptr = mmap(0, 1 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 4096); printf("On start, mptr points to 0x%lX.\n",(unsigned long) mptr); printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, *mptr); mptr[0] = 'a'; mptr[1] = 'b'; printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, *mptr); close(fd); return 0; }
shodanex.. 16
我想你可以找到很多关于kmalloc + mmap部分的文档.但是,我不确定你能以连续的方式获得如此多的记忆,并且总是在同一个地方.当然,如果一切都是一样的话,那么你可能会得到一个恒定的地址.但是,每次更改内核代码时,您将获得不同的地址,因此我不会使用kmalloc解决方案.
我认为你应该在启动时保留一些内存,即保留一些物理内存,这样内核就不会触及它.然后你可以ioremap这个内存,它会给你一个内核虚拟地址,然后你可以mmap它并编写一个很好的设备驱动程序.
这将我们带回PDF格式的linux设备驱动程序.看一下第15章,它正在描述第443页的这种技术
编辑:ioremap和mmap.我认为这可能更容易分两步调试:首先获取正确的ioremap,然后使用字符设备操作测试它,即读/写.一旦你知道你可以安全地使用读/写访问整个ioremapped内存,那么你尝试mmap整个ioremapped范围.
如果你遇到麻烦可能会发布另一个关于mmaping的问题
编辑:remap_pfn_range ioremap返回一个virtual_adress,您必须将其转换为pfn以获取remap_pfn_ranges.现在,我并不完全明白pfn(Page Frame Number)是什么,但我认为你可以得到一个电话
virt_to_phys(pt) >> PAGE_SHIFT
这可能不是正确的方法(tm),但你应该尝试一下
您还应该检查FOO_MEM_OFFSET是RAM块的物理地址.即,在mmu发生任何事情之前,您的内存在处理器的内存映射中为0.
我想你可以找到很多关于kmalloc + mmap部分的文档.但是,我不确定你能以连续的方式获得如此多的记忆,并且总是在同一个地方.当然,如果一切都是一样的话,那么你可能会得到一个恒定的地址.但是,每次更改内核代码时,您将获得不同的地址,因此我不会使用kmalloc解决方案.
我认为你应该在启动时保留一些内存,即保留一些物理内存,这样内核就不会触及它.然后你可以ioremap这个内存,它会给你一个内核虚拟地址,然后你可以mmap它并编写一个很好的设备驱动程序.
这将我们带回PDF格式的linux设备驱动程序.看一下第15章,它正在描述第443页的这种技术
编辑:ioremap和mmap.我认为这可能更容易分两步调试:首先获取正确的ioremap,然后使用字符设备操作测试它,即读/写.一旦你知道你可以安全地使用读/写访问整个ioremapped内存,那么你尝试mmap整个ioremapped范围.
如果你遇到麻烦可能会发布另一个关于mmaping的问题
编辑:remap_pfn_range ioremap返回一个virtual_adress,您必须将其转换为pfn以获取remap_pfn_ranges.现在,我并不完全明白pfn(Page Frame Number)是什么,但我认为你可以得到一个电话
virt_to_phys(pt) >> PAGE_SHIFT
这可能不是正确的方法(tm),但你应该尝试一下
您还应该检查FOO_MEM_OFFSET是RAM块的物理地址.即,在mmu发生任何事情之前,您的内存在处理器的内存映射中为0.
很抱歉回答但不完全回答,我注意到你已经编辑了这个问题.请注意,在您编辑问题时,SO不会通知我们.我在这里给出一个通用答案,当你更新问题时请发表评论,然后我会编辑我的答案.
是的,你需要编写一个模块.它归结为使用kmalloc()
(在内核空间中vmalloc()
分配区域)或(在用户空间中分配区域).
揭露先前是容易的,暴露后者可能是后方的痛苦与您根据需要描述的那种界面.你注意到1.5 MB是你实际需要预留多少的粗略估计,那是铁板吗?你是否觉得从内核空间中获取它?你能从用户空间(甚至磁盘睡眠)充分处理ENOMEM或EIO吗?IOW,这个地区会发生什么?
此外,并发会成为一个问题吗?如果是这样,你会使用futex吗?如果两者的答案都是"是"(特别是后者),那么你可能需要咬紧牙关并继续vmalloc()
(或者从内部冒犯内核腐烂).此外,如果你甚至想到关于ioctl()
char设备的接口(特别是对于一些临时锁定的想法),你真的想要使用vmalloc()
.
另外,你读过这个吗?另外,我们甚至没有涉及到grsec/selinux会想到什么(如果在使用中).