mem.c

来自「linux 内核源代码」· C语言 代码 · 共 915 行 · 第 1/2 页

C
915
字号
		realp += sz;		count -= sz;		written += sz;	}#endif	while (count > 0) {		char *ptr;		/*		 * Handle first page in case it's not aligned		 */		if (-realp & (PAGE_SIZE - 1))			sz = -realp & (PAGE_SIZE - 1);		else			sz = PAGE_SIZE;		sz = min_t(unsigned long, sz, count);		/*		 * On ia64 if a page has been mapped somewhere as		 * uncached, then it must also be accessed uncached		 * by the kernel or data corruption may occur		 */		ptr = xlate_dev_kmem_ptr(p);		copied = copy_from_user(ptr, buf, sz);		if (copied) {			written += sz - copied;			if (written)				break;			return -EFAULT;		}		buf += sz;		p += sz;		realp += sz;		count -= sz;		written += sz;	}	*ppos += written;	return written;}/* * This function writes to the *virtual* memory as seen by the kernel. */static ssize_t write_kmem(struct file * file, const char __user * buf, 			  size_t count, loff_t *ppos){	unsigned long p = *ppos;	ssize_t wrote = 0;	ssize_t virtr = 0;	ssize_t written;	char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */	if (p < (unsigned long) high_memory) {		wrote = count;		if (count > (unsigned long) high_memory - p)			wrote = (unsigned long) high_memory - p;		written = do_write_kmem((void*)p, p, buf, wrote, ppos);		if (written != wrote)			return written;		wrote = written;		p += wrote;		buf += wrote;		count -= wrote;	}	if (count > 0) {		kbuf = (char *)__get_free_page(GFP_KERNEL);		if (!kbuf)			return wrote ? wrote : -ENOMEM;		while (count > 0) {			int len = count;			if (len > PAGE_SIZE)				len = PAGE_SIZE;			if (len) {				written = copy_from_user(kbuf, buf, len);				if (written) {					if (wrote + virtr)						break;					free_page((unsigned long)kbuf);					return -EFAULT;				}			}			len = vwrite(kbuf, (char *)p, len);			count -= len;			buf += len;			virtr += len;			p += len;		}		free_page((unsigned long)kbuf);	} 	*ppos = p; 	return virtr + wrote;}#ifdef CONFIG_DEVPORTstatic ssize_t read_port(struct file * file, char __user * buf,			 size_t count, loff_t *ppos){	unsigned long i = *ppos;	char __user *tmp = buf;	if (!access_ok(VERIFY_WRITE, buf, count))		return -EFAULT; 	while (count-- > 0 && i < 65536) {		if (__put_user(inb(i),tmp) < 0) 			return -EFAULT;  		i++;		tmp++;	}	*ppos = i;	return tmp-buf;}static ssize_t write_port(struct file * file, const char __user * buf,			  size_t count, loff_t *ppos){	unsigned long i = *ppos;	const char __user * tmp = buf;	if (!access_ok(VERIFY_READ,buf,count))		return -EFAULT;	while (count-- > 0 && i < 65536) {		char c;		if (__get_user(c, tmp)) {			if (tmp > buf)				break;			return -EFAULT; 		}		outb(c,i);		i++;		tmp++;	}	*ppos = i;	return tmp-buf;}#endifstatic ssize_t read_null(struct file * file, char __user * buf,			 size_t count, loff_t *ppos){	return 0;}static ssize_t write_null(struct file * file, const char __user * buf,			  size_t count, loff_t *ppos){	return count;}static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,			struct splice_desc *sd){	return sd->len;}static ssize_t splice_write_null(struct pipe_inode_info *pipe,struct file *out,				 loff_t *ppos, size_t len, unsigned int flags){	return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);}static ssize_t read_zero(struct file * file, char __user * buf, 			 size_t count, loff_t *ppos){	size_t written;	if (!count)		return 0;	if (!access_ok(VERIFY_WRITE, buf, count))		return -EFAULT;	written = 0;	while (count) {		unsigned long unwritten;		size_t chunk = count;		if (chunk > PAGE_SIZE)			chunk = PAGE_SIZE;	/* Just for latency reasons */		unwritten = clear_user(buf, chunk);		written += chunk - unwritten;		if (unwritten)			break;		buf += chunk;		count -= chunk;		cond_resched();	}	return written ? written : -EFAULT;}static int mmap_zero(struct file * file, struct vm_area_struct * vma){#ifndef CONFIG_MMU	return -ENOSYS;#endif	if (vma->vm_flags & VM_SHARED)		return shmem_zero_setup(vma);	return 0;}static ssize_t write_full(struct file * file, const char __user * buf,			  size_t count, loff_t *ppos){	return -ENOSPC;}/* * Special lseek() function for /dev/null and /dev/zero.  Most notably, you * can fopen() both devices with "a" now.  This was previously impossible. * -- SRB. */static loff_t null_lseek(struct file * file, loff_t offset, int orig){	return file->f_pos = 0;}/* * The memory devices use the full 32/64 bits of the offset, and so we cannot * check against negative addresses: they are ok. The return value is weird, * though, in that case (0). * * also note that seeking relative to the "end of file" isn't supported: * it has no meaning, so it returns -EINVAL. */static loff_t memory_lseek(struct file * file, loff_t offset, int orig){	loff_t ret;	mutex_lock(&file->f_path.dentry->d_inode->i_mutex);	switch (orig) {		case 0:			file->f_pos = offset;			ret = file->f_pos;			force_successful_syscall_return();			break;		case 1:			file->f_pos += offset;			ret = file->f_pos;			force_successful_syscall_return();			break;		default:			ret = -EINVAL;	}	mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);	return ret;}static int open_port(struct inode * inode, struct file * filp){	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;}#define zero_lseek	null_lseek#define full_lseek      null_lseek#define write_zero	write_null#define read_full       read_zero#define open_mem	open_port#define open_kmem	open_mem#define open_oldmem	open_memstatic const struct file_operations mem_fops = {	.llseek		= memory_lseek,	.read		= read_mem,	.write		= write_mem,	.mmap		= mmap_mem,	.open		= open_mem,	.get_unmapped_area = get_unmapped_area_mem,};static const struct file_operations kmem_fops = {	.llseek		= memory_lseek,	.read		= read_kmem,	.write		= write_kmem,	.mmap		= mmap_kmem,	.open		= open_kmem,	.get_unmapped_area = get_unmapped_area_mem,};static const struct file_operations null_fops = {	.llseek		= null_lseek,	.read		= read_null,	.write		= write_null,	.splice_write	= splice_write_null,};#ifdef CONFIG_DEVPORTstatic const struct file_operations port_fops = {	.llseek		= memory_lseek,	.read		= read_port,	.write		= write_port,	.open		= open_port,};#endifstatic const struct file_operations zero_fops = {	.llseek		= zero_lseek,	.read		= read_zero,	.write		= write_zero,	.mmap		= mmap_zero,};/* * capabilities for /dev/zero * - permits private mappings, "copies" are taken of the source of zeros */static struct backing_dev_info zero_bdi = {	.capabilities	= BDI_CAP_MAP_COPY,};static const struct file_operations full_fops = {	.llseek		= full_lseek,	.read		= read_full,	.write		= write_full,};#ifdef CONFIG_CRASH_DUMPstatic const struct file_operations oldmem_fops = {	.read	= read_oldmem,	.open	= open_oldmem,};#endifstatic ssize_t kmsg_write(struct file * file, const char __user * buf,			  size_t count, loff_t *ppos){	char *tmp;	ssize_t ret;	tmp = kmalloc(count + 1, GFP_KERNEL);	if (tmp == NULL)		return -ENOMEM;	ret = -EFAULT;	if (!copy_from_user(tmp, buf, count)) {		tmp[count] = 0;		ret = printk("%s", tmp);		if (ret > count)			/* printk can add a prefix */			ret = count;	}	kfree(tmp);	return ret;}static const struct file_operations kmsg_fops = {	.write =	kmsg_write,};static int memory_open(struct inode * inode, struct file * filp){	switch (iminor(inode)) {		case 1:			filp->f_op = &mem_fops;			filp->f_mapping->backing_dev_info =				&directly_mappable_cdev_bdi;			break;		case 2:			filp->f_op = &kmem_fops;			filp->f_mapping->backing_dev_info =				&directly_mappable_cdev_bdi;			break;		case 3:			filp->f_op = &null_fops;			break;#ifdef CONFIG_DEVPORT		case 4:			filp->f_op = &port_fops;			break;#endif		case 5:			filp->f_mapping->backing_dev_info = &zero_bdi;			filp->f_op = &zero_fops;			break;		case 7:			filp->f_op = &full_fops;			break;		case 8:			filp->f_op = &random_fops;			break;		case 9:			filp->f_op = &urandom_fops;			break;		case 11:			filp->f_op = &kmsg_fops;			break;#ifdef CONFIG_CRASH_DUMP		case 12:			filp->f_op = &oldmem_fops;			break;#endif		default:			return -ENXIO;	}	if (filp->f_op && filp->f_op->open)		return filp->f_op->open(inode,filp);	return 0;}static const struct file_operations memory_fops = {	.open		= memory_open,	/* just a selector for the real open */};static const struct {	unsigned int		minor;	char			*name;	umode_t			mode;	const struct file_operations	*fops;} devlist[] = { /* list of minor devices */	{1, "mem",     S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},	{2, "kmem",    S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},	{3, "null",    S_IRUGO | S_IWUGO,           &null_fops},#ifdef CONFIG_DEVPORT	{4, "port",    S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},#endif	{5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},	{7, "full",    S_IRUGO | S_IWUGO,           &full_fops},	{8, "random",  S_IRUGO | S_IWUSR,           &random_fops},	{9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops},	{11,"kmsg",    S_IRUGO | S_IWUSR,           &kmsg_fops},#ifdef CONFIG_CRASH_DUMP	{12,"oldmem",    S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops},#endif};static struct class *mem_class;static int __init chr_dev_init(void){	int i;	int err;	err = bdi_init(&zero_bdi);	if (err)		return err;	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))		printk("unable to get major %d for memory devs\n", MEM_MAJOR);	mem_class = class_create(THIS_MODULE, "mem");	for (i = 0; i < ARRAY_SIZE(devlist); i++)		device_create(mem_class, NULL,			      MKDEV(MEM_MAJOR, devlist[i].minor),			      devlist[i].name);	return 0;}fs_initcall(chr_dev_init);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?