⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mm.c

📁 os arm os arm os arm os arm os arm os arm os arm os arm os arm os arm os arm os arm os arm os arm
💻 C
字号:
/* *	ApOS (Another Project software for s3c2410) *	 *	This program is free software; you can redistribute it and/or modify *	it under the terms of the GNU General Public License version 2 as *	published by the Free Software Foundation. *			 *						Copyright caiyuqing * */#include "../include/mm/mm.h"#include "../include/kernel/task.h"extern struct task_struct* current;//#define _MEM_DEBUG/* *	很重要的一个数组,ram_map是对主内存区域(用户空间)0x30a00000~0x33ffffff *	的一个映射,该内存区域被分成13824(RAM_PAGES)个页,每个页对应ram_map中 *	的一个元素,当该页是干净的(未被映射的),则对应的ram_map数组 *	元素的值为0 */#define MAP_INDEX(mv_addr) (((mv_addr)-USER_RAM_BASE)/PAGE_SIZE)unsigned char ram_map[RAM_PAGES];int memcpy(unsigned char* dest,unsigned char* src,int n){	for(;n>0;n--)	{		*dest++=*src++;	}}/* *	将ram_map数组清0 */void clear_ram_map(){	unsigned char *ram_map_base=ram_map;	unsigned int pages=RAM_PAGES;		asm volatile(		"ldr r0,%0 \n\t"		"ldr r1,%1 \n\t"		"add r1,r0,r1 \n\t"		"ldrb r2,=0x00 \n\t"		"1: strb r2,[r0],#1 \n\t"		"cmp r0,r1 \n\t"		"bne 1b \n\t"		:		:"m"(ram_map_base),"m"(pages)		:"r0","r1","r2","memory"	);}void ram_init(){	init_kmalloc();	clear_ram_map();}void oom(){	panic("error:out of memory.\n");}/* *	获得"干净"的物理页地址,并将该页对应的ram_map元素增1 */unsigned int get_free_physical_page(){	unsigned int ram_page_index;	unsigned int page_address;		for(ram_page_index=0;		ram_page_index<RAM_PAGES&&ram_map[ram_page_index];		ram_page_index++);		if(ram_page_index==RAM_PAGES)	{		return 0;	}		ram_map[ram_page_index]++;	page_address=USER_RAM_BASE+ram_page_index*PAGE_SIZE;	/*	 *	将新获得的页面清零。	 */ 	 	asm volatile(		"ldr r0,%0 \n\t"		"ldr r1,=4096 \n\t"		"add r1,r0,r1 \n\t"		"ldr r2,=0x00000000 \n\t"		"1: str r2,[r0],#4 \n\t"		"cmp r0,r1 \n\t"		"bne 1b \n\t"		:		:"m"(page_address)		:"r0","r1","r2"	);		return page_address;}/* *	释放物理地址 p_page 指定的一页内存 */int free_physical_page(unsigned int p_page){	unsigned int ram_page_index;	//页面号		/*	 *	若该页地址属于内核地址范围内则不进行处理,只打印警告信息并返回-1,	 */	if(p_page>=SYS_RAM_BASE && p_page<SYS_RAM_LIMIT)	{#ifdef _MEM_DEBUG			printk("warning: free_physical_page trying to free the kernel's page\n");#endif 				return -1;	}	/*	 *	若该页地址大于系统所含物理内存的最高端(0x34000000)则显示出错信息	 *	并死机	 */	 	if(p_page>=(SDRAM_LIMIT))	{		panic("error: free_physical_page trying to free nonexistent page\n");	}	/*	 *	否则将物理页地址转换成页面号(ram_map数组中的索引号),根据该页面号对	 *	ram_map数组中的元素进行递减操作,页面号 = (page_addr-USER_RAM_BASE)/PAGE_SIZE	 *	在递减操作之间先判断该元素是否为0,若该元素不为0,则递减并返回该页面号,	 *	若该元素为0,则说明我们要释放一个空闲页,则报错死机	 */	else	{		ram_page_index=(p_page-USER_RAM_BASE)/PAGE_SIZE;		if(ram_map[ram_page_index])		{			ram_map[ram_page_index]--;						return ram_page_index;		}		else		{			panic("error: free_physical_page tryint to free free page\n");		}	}}int free_virtual_page(unsigned int v_page){	unsigned int *dir_entry;	unsigned int *page_entry;		//若v_page不是4KB的边界,说明该地址不是页地址,报错死机	if(v_page&0xfff)	{		panic("error: free_virtual_page called with wrong alignment\n");	}	/*	 *	若v_page为EXCEPTION_BASE或为内核使用的地址范围则警告并返回-1	 */	if( (v_page>=SYS_VADDR_BASE&&v_page<SYS_VADDR_LIMIT)||		v_page==EXCEPTION_BASE)	{		printk("warning: free_virtual_page trying to free kernel's page\n");		return -1;	}		dir_entry=(unsigned int *)DIRECTORY_BASE+(v_page>>20);	if(DIR_VALID(*dir_entry))	{		page_entry= *dir_entry&0xfffffc00;		if(PAGE_VALID(*page_entry))		{			free_physical_page(*page_entry&0xFFFFF000);			*page_entry=0;		}		//刷新 TLB		invalidate_TLBs();		return v_page;	}		return 0;}/* *	free_page_tables 用于释放连续的内存区域。 *	注意	这个函数只能够处理1MB为单位的内存块,也就是说若size<1MB *		也按1MB处理。 *		mv_addr必须处于1MB的边界 * */void free_page_tables(unsigned int mv_addr,unsigned int size){	unsigned int *dir_entry;	unsigned int *page_entry;	unsigned int nr;		//检查mv_addr是否处于1MB的边界	if(mv_addr&0xfffff)	{		panic("error: free_page_table called with wrong alignment\n");	}		/*	 *	size用于表明总共有多少MB(有多少个页目录项)。或许你会感到奇怪,为什么	 *	size必须先加上0xfffff?	 *	size+0xfffff的原因是因为会发生这种情况,若size=1.1MB,则 size>>20=1,	 *	也就是说余数部分被去掉了。但size=1.1MB的话,我们要按2MB进行处理,所以	 *	必须加上0xfffff	 */	size=(size+0xfffff)>>20;		dir_entry=(unsigned int *)DIRECTORY_BASE+(mv_addr>>20);	#ifdef _MEM_DEBUG		printk("free page_table\n");		printk("@virtual address:0x%0x\n",mv_addr);	#endif	for(;size-->0;dir_entry++)	{		if(DIR_INVALID(*dir_entry))		{			panic("error: free_page_table trying to free unmap virtual address\n");		}		page_entry=(*dir_entry&0xFFFFFC00);						for(nr=0;nr<256;nr++)		{			if(*page_entry&0x00000003)			{				//若p_addr为0xfff00000或为SYS_RAM地址范围,报错死机				if(((*page_entry&0xFFFFF000)>=SYS_RAM_BASE&&(*page_entry&0xFFFFF000)<SYS_RAM_LIMIT ))				{					panic("error: free_page_table trying to free kernel memory space\n");				}#ifdef _MEM_DEBUG					printk("	#physic address:0x%0x \n",*page_entry&0xFFFFF000);#endif				free_physical_page(*page_entry&0xFFFFF000);				*page_entry=0;			}			else 			{				panic("error: free_page_table trying to free unmap virtual address\n");			}			page_entry++;		}		*dir_entry=0;	}	//刷新 TLB	invalidate_TLBs();}/* *	内存复制函数,实际上是复制页表 */void copy_page_tables(unsigned int mv_addr_src,	unsigned int mv_addr_dest,	unsigned int size){	unsigned int *dir_entry_src=(unsigned int *)DIRECTORY_BASE+(mv_addr_src>>20);	unsigned int *dir_entry_dest=(unsigned int *)DIRECTORY_BASE+(mv_addr_dest>>20);	unsigned int *page_entry_src;	unsigned int *page_entry_desc;	unsigned int tmp;	unsigned int page_count;			//源地址和目标地址必须从1MB边界开始	if((mv_addr_src&0xfffff)||(mv_addr_dest&0xfffff))	{		panic("error: copy_page_tables called with wrong alignment\n");	}		//size的单位由byte变为MB	size=(size+0xfffff)>>20;			//每次至少复制1MB(一个目录项)	for(;size--;dir_entry_src++,dir_entry_dest++)	{		//源目录没有使用,不用复制		if(DIR_INVALID(*dir_entry_src))			continue;				if(DIR_INVALID(*dir_entry_dest))//目标地址页目录对应的页表不存在		{			//分配一页内存给目标地址存放页表			if((tmp=get_free_physical_page())==0)				oom();			*dir_entry_dest=tmp|COARSE_PAGE_DESC|CLIENT;		}						page_entry_src=(unsigned int *)(*dir_entry_src&0xFFFFFC00);		page_entry_desc=(unsigned int *)(*dir_entry_dest&0xFFFFFC00);				for(page_count=0;page_count<256;page_count++)		{			/*			 *	该页面被父进程和子进程共项,我们要从新设置该内存页面			 *	的访问权限。User模式只读,System模式允许读写,这样当			 *	两个进程中的某一个要对其进行写操作时将引起permission fault			 */						*page_entry_src=(*page_entry_src&~(0xfff))|USER_SMALL_PAGE_DESC_R;			*page_entry_desc=*page_entry_src;						//若该页地址属于用户空间,则将内存引用数组的对应项增一			if((*page_entry_src&0xfffff000)>=USER_RAM_BASE)				ram_map[MAP_INDEX(*page_entry_src&0xfffff000)]++;						page_entry_desc++;			page_entry_src++;					}	}		//刷新 TLB	invalidate_TLBs();}void see_addr_map(unsigned int mv_addr){	unsigned int *dir_entry=(unsigned int *)DIRECTORY_BASE+(mv_addr>>20);	unsigned int *page_entry=(unsigned int *)(*dir_entry&0xFFFFFC00)+((mv_addr>>12)&0xff);	unsigned int phy_page_index=(((*page_entry&~(0xfff))-USER_RAM_BASE)/PAGE_SIZE);		printf("TLB index  :0x%0x  [0x%0x]\n",(mv_addr>>20),*dir_entry);	printf("page_entry :0x%0x  [0x%0x]\n",page_entry,*page_entry);	printf("physic page index: 0x%0x\n",phy_page_index);	printf("reference count: 0x%0x\n",ram_map[phy_page_index]);	printf("page type: 0x%0x.\n",*page_entry&0x3);	printf("C bit: 0x%0x.\n",(*page_entry>>3)&1);	printf("B bit: 0x%0x.\n",(*page_entry>>2)&1);	printf("AP0:0x%0x  AP1:0x%0x  AP2:0x%0x  AP3:0x%0x.\n",	(*page_entry>>4)&3,(*page_entry>>6)&3,(*page_entry>>8)&3,(*page_entry>>10)&3);}/* *	缺页处理函数 */void do_no_page(unsigned int mv_addr){	unsigned int *dir_entry=(unsigned int *)DIRECTORY_BASE+(mv_addr>>20);	unsigned int *page_entry;	unsigned int tmp;		//该页的页目录不存在	if(DIR_INVALID(*dir_entry))	{		//请求一页内存作为页目录		if((tmp=get_free_physical_page())==0)			oom();		/*		 *	CLIENT 将使分配的内存在被访问的时候进行权限检测		 */		*dir_entry=tmp|COARSE_PAGE_DESC|CLIENT;		page_entry=(unsigned int *)(*dir_entry&0xFFFFFC00)+((mv_addr>>12)&0xff);		//请求一页内存作为页表		if((tmp=get_free_physical_page())==0)			oom();				/*		 *	USER_SMALL_PAGE_DESC_RW 将使分配的内存在内核		 *	级和用户级都具有读写的访问许可		 */		*page_entry=tmp|USER_SMALL_PAGE_DESC_RW;	}	else//该页的页目录存在	{		//请求一页内存作为页表		page_entry=(unsigned int *)(*dir_entry&0xFFFFFC00)+((mv_addr>>12)&0xff);		if(PAGE_INVALID(*page_entry))		{			if((tmp=get_free_physical_page())==0)				oom();			*page_entry=tmp|USER_SMALL_PAGE_DESC_RW;		}	}}void un_wp_page(unsigned int mv_addr){	unsigned int *dir_entry=(unsigned int *)DIRECTORY_BASE+(mv_addr>>20);	unsigned int *page_entry;	unsigned int tmp;	unsigned int old_page,new_page;	//获得该页页表项	page_entry=(unsigned int *)(*dir_entry&0xFFFFFC00)+((mv_addr>>12)&0xff);		/*	 *	若该地址对应的物理页面仅被一个进程使用,则将该页面的访问权限标志为R/W	 *	 *	我们知道当父进程创建子进程之后他们的内存地址是共享的,此时该内存空间的	 *	访问权限由原来的R/W被修改为R,内存空间对应的内存页引用次数将增一.当其中	 *	的某个进程要对内存进行写操作时则发生permission fault,这样系统会为该进	 *	程分配一页内存,并将原来的内存页引用次数减1,当原来共享的内存页对应的物	 *	理页引用次数递减到1时,说明此时只有一个进程在使用该页面,于是我们只需把	 *	该页的访问权限修改回R/W即可	 */	if(ram_map[MAP_INDEX(*page_entry&(0xfffff000))]==1)	{		*page_entry=(*page_entry&(0xfffff000))|USER_SMALL_PAGE_DESC_RW;	}	else	{		/*		 *	该页被多于一个进程共项,所以必须为当前进程分配新的一个内存页供		 *	其进行写操作,同时将原来的页面引用次数减一		 *		 */				//保存原页面地址用于新旧页面的数据拷贝		old_page=(*page_entry&(0xfffff000));		//将原共享的物理内存页引用数减1		ram_map[MAP_INDEX(old_page)]--;				//申请一页内存给当前进程		if((tmp=get_free_physical_page())==0)			oom();		*page_entry=tmp|USER_SMALL_PAGE_DESC_RW;				//获得新页面地址		new_page=(*page_entry&(0xfffff000));		//页面间的数据拷贝		memcpy((char *)new_page,(char *)old_page,4096);		}	//刷新 TLB	invalidate_TLBs();}void do_wp_page(unsigned int mv_addr){	un_wp_page(mv_addr);}/* *	本系统会产生permission_fault的原因只有一个,那 *	就是在用户模式下对一个只有读权限的内存页执行了, *	写操作,其他的permission_fault都将引起系统死机 */void do_permission_fault(unsigned int mv_addr){	/*	printk("permission fault.\n");	printk("	pid: %d\n",current->pid);	printk("	fault address :0x%0x\n",mv_addr);	*/	//取消写保护	do_wp_page(mv_addr);	}

⌨️ 快捷键说明

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