mm/mprotect.c

  本模块只提供一个系统调用,而无其他接口。这个系统调用就是sys_mprotect.
系统调用sys_mprotect(unsigned long start, size_t len, unsigned long prot)
改变地址范围[start,addr+len-1]涉及到的物理页面的保护方式。对于i386就是
改变pte中的__pgprot。
  其中prot可用的参数有:
  PROT_WRITE/PROT_READ/PROT_EXEC
  PROT_NONE:此段内存不能被访问.
  新设置的属性会完全取代现有属性,而不是叠加到现有属性上.
  
  此系统调用实现起来也不复杂,思路也常见了:遍历涉及到的vma,根据属性的改
变进行fixup(这个东西看到好几次了),同时设置所涉及之pte的prot属性.

  不再进行更详尽的分析了,简单看看函数:
asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot)
{
	unsigned long nstart, end, tmp;
	struct vm_area_struct * vma, * next;
	int error = -EINVAL;

	if (start & ~PAGE_MASK) //起始地址必须位于页边界
		return -EINVAL;
	len = PAGE_ALIGN(len);//长度扩大到最后一个page的末尾(大于给定范围)
	end = start + len;
	if (end < start)
		return -EINVAL;
	if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))//仅容许使用这几位
		return -EINVAL;
	if (end == start)
		return 0;

	down(&current->mm->mmap_sem);

	vma = find_vma(current->mm, start);//start<vm_end
	error = -EFAULT;
	if (!vma || vma->vm_start > start)
		goto out;

	for (nstart = start ; ; ) {
		unsigned int newflags;

		/* Here we know that  vma->vm_start <= nstart < vma->vm_end. */

		newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC));
		if ((newflags & ~(newflags >> 4)) & 0xf) {
		//高四位代表内核所授予的权限,VM_MAYREAD,...
		//PROT_xxx分别等于VM_XXX,代表当前所拥有的权限
		//不能超越VM_MAYxxx,即高4位所代表的权限
			error = -EACCES;
			break;
		}

		if (vma->vm_end >= end) {//last vma 
			error = mprotect_fixup(vma, nstart, end, newflags);
			break;
		}

		tmp = vma->vm_end;
		next = vma->vm_next;
		error = mprotect_fixup(vma, nstart, tmp, newflags);
		//xxxx_fixup都要求 nstart,tep在vma_start到vma_end之间
		
		if (error)
			break;
		nstart = tmp;
		vma = next;
		if (!vma || vma->vm_start != nstart) {
			error = -EFAULT;
			break;
		}
	}
out:
	up(&current->mm->mmap_sem);
	return error;
}

   次函数使用的fixup函数,以及改变pte的prot的部分不再做详细分析,重点注意
下面几个问题:  
1)pmd_bad
static inline void change_pte_range(pmd_t * pmd, unsigned long address,
	unsigned long size, pgprot_t newprot)
{
	pte_t * pte;
	unsigned long end;

	if (pmd_none(*pmd))
		return;
	if (pmd_bad(*pmd)) { //以前分析过pmd_bad,如果用户页面出了问题
		pmd_ERROR(*pmd); //就作记录
		pmd_clear(pmd);  //清除pte
		return;          //当内核页面bad的时候,可以参考get_pte_slow
	}
.....
}


2.vm flags 到pte prot的转化表 
static int mprotect_fixup(struct vm_area_struct * vma, 
	unsigned long start, unsigned long end, unsigned int newflags)
{
	pgprot_t newprot;
	int error;

	if (newflags == vma->vm_flags)
		return 0;
	newprot = protection_map[newflags & 0xf];//2^4种组合见
	        //protection_map
	
	.....
}
}