You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

994 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* linux/mm/memory.c
*
* (C) 1991 Linus Torvalds
*/
/*
* demand-loading started 01.12.91 - seems it is high on the list of
* things wanted, and it should be easy to implement. - Linus
*/
/*
* Ok, demand-loading was easy, shared pages a little bit tricker. Shared
* pages started 02.12.91, seems to work. - Linus.
*
* Tested sharing by executing about 30 /bin/sh: under the old kernel it
* would have taken more than the 6M I have free, but it worked well as
* far as I could see.
*
* Also corrected some "invalidate()"s - I wasn't doing enough of them.
*/
#include <stdarg.h>
#include <signal.h>
#include <asm/system.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <new.h>
#include <asm/segment.h>
volatile void do_exit(long code);
static inline volatile void oom(void)
{
printk("out of memory\n\r");
do_exit(SIGSEGV);
}
#define invalidate() \
__asm__("movl %%eax,%%cr3"::"a" (0))
/* these are not to be changed without changing head.s etc */
#define LOW_MEM 0x100000
#define PAGING_MEMORY (15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
current->start_code + current->end_code)
static long HIGH_MEMORY = 0;
#define copy_page(from,to) \
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024))
static unsigned char mem_map [ PAGING_PAGES ] = {0,};
/*
* Get physical address of first (actually last :-) free page, and mark it
* used. If no free pages left, return 0.
*/
unsigned long get_free_page(void)
{
register unsigned long __res asm("ax");
__asm__("std ; repne ; scasb\n\t"
"jne 1f\n\t"
"movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t"
"movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t"
"movl %%edx,%%eax\n\t"
"1:"
"cld\n\t" /* by wyj */
:"=a" (__res)
:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
"D" (mem_map+PAGING_PAGES-1)
);
return __res;
}
/*
* Free a page of memory at physical address 'addr'. Used by
* 'free_page_tables()'
*/
void free_page(unsigned long addr)
{
if (addr < LOW_MEM) return;
if (addr >= HIGH_MEMORY)
panic("trying to free nonexistent page");
addr -= LOW_MEM;
addr >>= 12;
if (mem_map[addr]--) return;
mem_map[addr]=0;
panic("trying to free free page");
}
/*
* This function frees a continuos block of page tables, as needed
* by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
*/
int free_page_tables(unsigned long from,unsigned long size)
{
unsigned long *pg_table;
unsigned long * dir, nr;
if (from & 0x3fffff)
panic("free_page_tables called with wrong alignment");
if (!from)
panic("Trying to free up swapper memory space");
size = (size + 0x3fffff) >> 22;
dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
for ( ; size-->0 ; dir++) {
if (!(1 & *dir))
continue;
pg_table = (unsigned long *) (0xfffff000 & *dir);
for (nr=0 ; nr<1024 ; nr++) {
if (1 & *pg_table)
free_page(0xfffff000 & *pg_table);
*pg_table = 0;
pg_table++;
}
free_page(0xfffff000 & *dir);
*dir = 0;
}
invalidate();
return 0;
}
/*
* Well, here is one of the most complicated functions in mm. It
* copies a range of linerar addresses by copying only the pages.
* Let's hope this is bug-free, 'cause this one I don't want to debug :-)
*
* Note! We don't copy just any chunks of memory - addresses have to
* be divisible by 4Mb (one page-directory entry), as this makes the
* function easier. It's used only by fork anyway.
*
* NOTE 2!! When from==0 we are copying kernel space for the first
* fork(). Then we DONT want to copy a full page-directory entry, as
* that would lead to some serious memory waste - we just copy the
* first 160 pages - 640kB. Even that is more than we need, but it
* doesn't take any more memory - we don't copy-on-write in the low
* 1 Mb-range, so the pages can be shared with the kernel. Thus the
* special case for nr=xxxx.
*/
int copy_page_tables(unsigned long from,unsigned long to,long size )
{
unsigned long * from_page_table;
unsigned long * to_page_table;
unsigned long this_page;
unsigned long * from_dir, * to_dir;
unsigned long nr;
if ((from&0x3fffff) || (to&0x3fffff))
panic("copy_page_tables called with wrong alignment");
from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
to_dir = (unsigned long *) ((to>>20) & 0xffc);
size = ((unsigned) (size+0x3fffff)) >> 22;
for( ; size-->0 ; from_dir++,to_dir++) {
if (1 & *to_dir)
panic("copy_page_tables: already exist");
if (!(1 & *from_dir))
continue;
from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
if (!(to_page_table = (unsigned long *) get_free_page()))
return -1; /* Out of memory, see freeing */
*to_dir = ((unsigned long) to_page_table) | 7;
nr = (from==0)?0xA0:1024;
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table;
if (!(1 & this_page))
continue;
this_page &= ~2;
*to_page_table = this_page;
if (this_page > LOW_MEM) {
*from_page_table = this_page;
this_page -= LOW_MEM;
this_page >>= 12;
mem_map[this_page]++;
}
}
}
invalidate();
return 0;
}
/*
* This function puts a page in memory at the wanted address.
* It returns the physical address of the page gotten, 0 if
* out of memory (either when trying to access page-table or
* page.)
*/
unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
if (page < LOW_MEM || page >= HIGH_MEMORY)
printk("Trying to put page %p at %p\n",page,address);
if (mem_map[(page-LOW_MEM)>>12] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
page_table = (unsigned long *) ((address>>20) & 0xffc);
if ((*page_table)&1)
page_table = (unsigned long *) (0xfffff000 & *page_table);
else {
if (!(tmp=get_free_page()))
return 0;
*page_table = tmp|7;
page_table = (unsigned long *) tmp;
}
page_table[(address>>12) & 0x3ff] = page | 7;
/* no need for invalidate */
return page;
}
void un_wp_page(unsigned long * table_entry)
{
unsigned long old_page,new_page;
old_page = 0xfffff000 & *table_entry;
if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
*table_entry |= 2;
invalidate();
return;
}
if (!(new_page=get_free_page()))
oom();
if (old_page >= LOW_MEM)
mem_map[MAP_NR(old_page)]--;
*table_entry = new_page | 7;
invalidate();
copy_page(old_page,new_page);
}
/*
* This routine handles present pages, when users try to write
* to a shared page. It is done by copying the page to a new address
* and decrementing the shared-page counter for the old page.
*
* If it's in code space we exit with a segment error.
*/
void do_wp_page(unsigned long error_code,unsigned long address)
{
#if 0
/* we cannot do this yet: the estdio library writes to code space */
/* stupid, stupid. I really want the libc.a from GNU */
if (CODE_SPACE(address))
do_exit(SIGSEGV);
#endif
un_wp_page((unsigned long *)
(((address>>10) & 0xffc) + (0xfffff000 &
*((unsigned long *) ((address>>20) &0xffc)))));
}
void write_verify(unsigned long address)
{
unsigned long page;
if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
return;
page &= 0xfffff000;
page += ((address>>10) & 0xffc);
if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */
un_wp_page((unsigned long *) page);
return;
}
void get_empty_page(unsigned long address)
{
unsigned long tmp;
if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
free_page(tmp); /* 0 is ok - ignored */
oom();
}
}
/*
* try_to_share() checks the page at address "address" in the task "p",
* to see if it exists, and if it is clean. If so, share it with the current
* task.
*
* NOTE! This assumes we have checked that p != current, and that they
* share the same executable.
*/
static int try_to_share(unsigned long address, struct task_struct * p)
{
unsigned long from;
unsigned long to;
unsigned long from_page;
unsigned long to_page;
unsigned long phys_addr;
from_page = to_page = ((address>>20) & 0xffc);
from_page += ((p->start_code>>20) & 0xffc);
to_page += ((current->start_code>>20) & 0xffc);
/* is there a page-directory at from? */
from = *(unsigned long *) from_page;
if (!(from & 1))
return 0;
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
phys_addr = *(unsigned long *) from_page;
/* is the page clean and present? */
if ((phys_addr & 0x41) != 0x01)
return 0;
phys_addr &= 0xfffff000;
if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
return 0;
to = *(unsigned long *) to_page;
if (!(to & 1))
if (to = get_free_page())
*(unsigned long *) to_page = to | 7;
else
oom();
to &= 0xfffff000;
to_page = to + ((address>>10) & 0xffc);
if (1 & *(unsigned long *) to_page)
panic("try_to_share: to_page already exists");
/* share them: write-protect */
*(unsigned long *) from_page &= ~2;
*(unsigned long *) to_page = *(unsigned long *) from_page;
invalidate();
phys_addr -= LOW_MEM;
phys_addr >>= 12;
mem_map[phys_addr]++;
return 1;
}
/*
* share_page() tries to find a process that could share a page with
* the current one. Address is the address of the wanted page relative
* to the current data space.
*
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
static int share_page(unsigned long address)
{
struct task_struct ** p;
if (!current->executable)
return 0;
if (current->executable->i_count < 2)
return 0;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!*p)
continue;
if (current == *p)
continue;
if ((*p)->executable != current->executable)
continue;
if (try_to_share(address,*p))
return 1;
}
return 0;
}
void do_no_page(unsigned long error_code,unsigned long address)
{
int nr[4];
unsigned long tmp;
unsigned long page;
int block,i;
/* if (current->pid > 5)
printk(" --do_no_page: address=%x, pid=%d\n", address, current->pid); */
address &= 0xfffff000;
tmp = address - current->start_code;
if (!current->executable || tmp >= current->end_data) {
get_empty_page(address);
return;
}
if (share_page(tmp))
return;
if (!(page = get_free_page()))
oom();
/* remember that 1 block is used for header */
block = 1 + tmp/BLOCK_SIZE;
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(current->executable,block);
bread_page(page,current->executable->i_dev,nr);
i = tmp + 4096 - current->end_data;
tmp = page + 4096;
while (i-- > 0) {
tmp--;
*(char *)tmp = 0;
}
if (put_page(page,address))
return;
free_page(page);
oom();
}
void mem_init(long start_mem, long end_mem)
{
int i;
HIGH_MEMORY = end_mem;
for (i=0 ; i<PAGING_PAGES ; i++)
mem_map[i] = USED;
i = MAP_NR(start_mem);
end_mem -= start_mem;
end_mem >>= 12;
while (end_mem-->0)
mem_map[i++]=0;
}
int do_execve2(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
{
int ans=0;
if((ans=do_execve(eip,tmp,filename,argv,envp)))
return ans;
int nr[4];
unsigned long temp;
unsigned long page;
int block,i;
unsigned long address=current->start_code;
//this part comes from do_no_page as achievement of immediate loading of cs and ds
while(address<=current->brk+current->start_code)
{
address &= 0xfffff000;
temp = address - current->start_code;
if (share_page(temp))
return -1;
if (!(page = get_free_page()))
oom();
block = 1 + temp/BLOCK_SIZE;
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(current->executable,block);
bread_page(page,current->executable->i_dev,nr);
if (!put_page(page,address))
{
free_page(page);
oom();
}
address+=PAGE_SIZE;
}
return 0;
}
void calc_mem(void)
{
int i,j,k,free=0;
long * pg_tbl;
for(i=0 ; i<PAGING_PAGES ; i++)
if (!mem_map[i]) free++;
printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
for(i=2 ; i<1024 ; i++) {
if (1&pg_dir[i]) {
pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
for(j=k=0 ; j<1024 ; j++)
if (pg_tbl[j]&1)
k++;
printk("Pg-dir[%d] uses %d pages\n",i,k);
}
}
}
static int my_share(unsigned long address, struct task_struct * p)
{
unsigned long from,to,from_page,to_page,phys_addr; //from address to p
//从页目录 获取页表项地址
from_page = to_page = ((address>>20) & 0xffc);
from_page += ((current->start_code>>20) & 0xffc);
to_page += ((p->start_code>>20) & 0xffc);
//从页表项获取页地址
from = *(unsigned long *) from_page;
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
phys_addr = *(unsigned long *) from_page;
to = *(unsigned long *) to_page;
if (!(to & 1)) //是否存在
if (to = get_free_page())
*(unsigned long *) to_page = to | 7; //权限修改
else
return 0;
to &= 0xfffff000;
to_page = to + ((address>>10) & 0xffc);
if (1 & *(unsigned long *) to_page)
return 0;
/* share them: write-protect */
*(unsigned long *) to_page = *(unsigned long *) from_page; //复制到p进程
phys_addr -= LOW_MEM;
phys_addr >>= 12;
mem_map[phys_addr]++; //物理页映射数组,记录该物理页的使用者数量
return 1;
}
// rememeber to sync when munmap exit manual
long sys_mmap(void * start,size_t len,unsigned long * argv)// int prot,int flags,int fd,off_t off
{
unsigned long a,b,c,d;
a=get_fs_long(argv);
b=get_fs_long(argv+1);
c=get_fs_long(argv+2);
d=get_fs_long(argv+3);
//printk("a,b,c,d:%ld,%ld,%ld,%ld\n",a,b,c,d);
int prot=a,flags=b,fd=c;
off_t off=d;
int block;
size_t l=len;
struct buffer_head * bh;
char *temp,*p1,*p2;
void * buf=start;
int tempprot=0;
//因为new.h和namei.c定义不一样要修改
if(prot &PROT_READ) tempprot |=4;
if(prot &PROT_EXEC) tempprot |=1;
if(prot &PROT_WRITE) tempprot |=2;
//特殊量len>0 off%4096==0 prot至少有read权限
if( (flags & MAP_SHARED)&& (flags &MAP_PRIVATE ))
{
printk("error with private and shared request.\n");
return -1;
}
if(len<=0 || off<0 || (off%PAGE_SIZE))
{
if(len<=0)
printk("the len of the wanting bytes must be positive.\n");
else
printk("offset must be the mul of 4096.\n");
return MAP_FAILED;
}
if(!(prot& PROT_READ)) return MAP_FAILED;
//permissions of the file ,参考函数namei.c::permissions
struct m_inode *inode=current->filp[fd]->f_inode;
int mode = inode->i_mode;
if (inode->i_dev && !inode->i_nlinks)
return MAP_FAILED;
else if (current->euid==inode->i_uid)
mode >>= 6;
else if (current->egid==inode->i_gid)
mode >>= 3;
if (!(((mode & tempprot & 0007) == tempprot) || suser()))
{
printk("error permission.");
return MAP_FAILED;
}
//end of permission check
//设置用户空间的虚拟地址初始为32MB处这样写在brk增长至32MB时会有bug,但是这种情况不会出现,
//因为物理页一共16MBlinux-011没有页替换策略即linux011内核默认虚拟内存利用不会超过16MB
//同时,当栈向下增长到一定程度时,也会和当前地址冲突,这种情况也不可能出现
if(start<=current->brk || start>=current->start_stack-0x8000 || start==NULL)
{
if(!current->mmap_tail)
buf=0x2000000;
else
buf=current->mmap_tail->endaddr;
}
//申请mmap
struct mmap_struct *mmap=(struct mmap_struct *)malloc(sizeof(struct mmap_struct));
mmap->address=buf;
mmap->size=len;
mmap->endaddr=(unsigned long)buf+(len + 0xfff)&0xfffff000;
mmap->rw=prot;
mmap->ps=flags;
mmap->filenode=(struct m_inode*)malloc(sizeof(struct m_inode));
memcpy(mmap->filenode,inode,sizeof(struct m_inode));
if(!mmap->filenode->i_count)mmap->filenode->i_count=1;
mmap->off=off;
mmap->next=NULL;
//加入current->mmap
if(!current->mmap)
{current->mmap=current->mmap_tail=mmap;}
else {
current->mmap_tail->next=mmap;
current->mmap_tail=mmap;
}
//分配物理页
unsigned long from,to,from_page,to_page,phys_addr,tpage,address=mmap->address;
while(address<mmap->endaddr)
{
from_page = ((address>>20) & 0xffc);
from_page += ((current->start_code>>20) & 0xffc);//页目录
if(!(*(unsigned long *)from_page&1)) //页表项不存在,申请页面
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return MAP_FAILED;
}
else
{
*(unsigned long *)from_page= tpage | 7;
mem_map[(tpage-LOW_MEM)>>12]++;
}
from=*(unsigned long *)from_page; //页表项
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
if(!(*(unsigned long *)from_page & 1)) //物理页不存在
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return MAP_FAILED;
}
else
{
*(unsigned long *)from_page= tpage | 7;
mem_map[(tpage-LOW_MEM)>>12]++;
}
*(unsigned long *)from_page &= (prot&PROT_WRITE)?0xffffffff:0xfffffffd; //分配读写权限
//设置物理页共享
if(flags & MAP_SHARED)
{
//printk("i am doing sharing.");
struct task_struct ** p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!*p)
continue;
if (current == *p)
continue;
//在这一部分,我将current->address处的物理页复制给
//所有进程的该区域
if(!my_share(address,*p))
printk("share failed at task:pid=%d",(*p)->pid);
}
}
address+=PAGE_SIZE;
}
//将文件内容写入共享内存
if (!(block = inode->i_zone[0]))
return NULL;
if (!(bh = bread(inode->i_dev,block)))
return NULL;
temp = (char *) bh->b_data;
p1=temp+off;p2=buf;
while(l--)
put_fs_byte(*(p1++),p2++);
return buf;
}
static int my_sync(off_t pos, struct m_inode * inode, char * buf, int count)
{
int block,c;
struct buffer_head * bh;
char * p;
int i=0;
while (i<count) {//将count字节写入磁盘
if (!(block = create_block(inode,pos/BLOCK_SIZE)))
break;
if (!(bh=bread(inode->i_dev,block)))
break;
c = pos % BLOCK_SIZE;//从偏移里那个开始写
p = c + bh->b_data;
bh->b_dirt = 1;
c = BLOCK_SIZE-c;//本块要写的数量
if (c > count-i) c = count-i;
pos += c;//已经读了c字节更新偏移量
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
i += c;
while (c-->0) //暂存到p里面即bh里
*(p++) = get_fs_byte(buf++);
brelse(bh);//释放bh同时同步到磁盘
}
inode->i_mtime = CURRENT_TIME;//文件最后修改时间
inode->i_ctime = CURRENT_TIME;//i节点修改时间
return (i?i:-1);
}
int sys_munmap(void * start ,size_t size)
{
struct mmap_struct *temp=current->mmap;
struct mmap_struct *pre=NULL;
while(temp!=NULL) //遍历链表
{
if(temp->address==(unsigned long)start)
break;
else temp=temp->next;
pre=temp;
}
if(!temp) //未找到
{
printk("invalid address.\n");
return -1;
}
if(!pre) //为头节点
{
if(current->mmap_tail==current->mmap)current->mmap_tail=NULL;
current->mmap=current->mmap->next;
}
else if(temp->next=NULL) //中间节点
{
current->mmap_tail=pre;
pre->next=NULL;
}
else{ //尾节点
pre->next=temp->next;
}
unsigned long from,to,from_page,to_page,phys_addr,tpage,address=temp->address;
//printk("%lx\n",address);
if(!(temp->ps & MAP_SHARED))
goto mytag;
//若为共享,执行下面的代码
while (address < temp ->endaddr)
{
//printk("addr: %lx\n",address);
from_page = ((address>>20) & 0xffc);
from_page += ((current->start_code>>20) & 0xffc);//页目录
from=*(unsigned long *)from_page; //页表项
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
phys_addr = (*(unsigned long *)from_page & 0xfffff000);
//printk("addr: %lx",*(unsigned long *)from_page);
if(!(*(unsigned long *)from_page & 1))//当前进程必须有该映射区物理页
{
printk("error of deleting page at %x.\n",*(unsigned long *)from_page);
printk("current address:%x\n",address);
return -1;
}
if(temp->ps & MAP_SHARED)
{
struct task_struct ** p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { //遍历所有进程
if (!*p) //空进程
continue;
if (current == *p) //排除当前进程
continue;
to_page = ((address>>20) & 0xffc)+(((*p)->start_code>>20) & 0xffc);
to = *(unsigned long *) to_page;
if(!(to & 1)) //不存在,肯定未共享过
continue;
to &= 0xfffff000;
to_page = to + ((address>>10) & 0xffc);
if(!(*(unsigned long *)to_page &1)) //不存在,肯定未共享过
continue;
to = (*(unsigned long *)to_page & 0xfffff000);
if(phys_addr == to)
{
*(unsigned long *)to_page &= ~1; //删除页
mem_map[(phys_addr-LOW_MEM)>>12]--; //物理页使用者--
}
}
}
address+=PAGE_SIZE;
}
if(temp->ps & MAP_SHARED)
{
if(my_sync(temp->off,temp->filenode,(char*)(temp->address),temp->size)==-1)
return -1;
}
mytag:
//取消删去当前进程映射区物理页
address=temp->address;
while (address < temp ->endaddr)
{
//printk("%lx\n",address);
from_page = ((address>>20) & 0xffc);
from_page += ((current->start_code>>20) & 0xffc);//页目录
from=*(unsigned long *)from_page; //页表项
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
phys_addr = (*(unsigned long *)from_page & 0xfffff000);
//printk("addr: %lx",*(unsigned long *)from_page);
if(!(*(unsigned long *)from_page & 1)) //存在
{
printk("error of deleting page at %x.\n",*(unsigned long *)from_page);
printk("current address:%x\n",address);
return -1;
}
else{
*(unsigned long *)from_page &= ~1; //删除物理页
}
address+=PAGE_SIZE;
}
free(temp->filenode); //清空节点 mmap->filename
free(temp); //清空 mmap
return 0;
}
/*
直接让esp子进程指向另一个进程当前进程的地址
修改eip对应的指令改为ret 0xec8353c3 ps:应该只有0xc3有用
由于eip只执行一条指令我们将eip指向栈将ret压入栈即可
在这里我们只实现了int (*fn)(void *) 一种函数类型并且要求fn内以exit结尾
于是我们只需修改八字节即可;
下面写的不对,不能指向另一个进程的地址,只能修改物理页了。
*/
/* int do_clone(int nr,long tmp,int (*fn)(void *), void *child_stack, int flag)
{
//设置压栈表,方便操作;
long stack[1024];
unsigned long len=0;
task[nr]->tss.esp=(unsigned long) child_stack - 8;
stack[0] = (unsigned long) fn;
task[nr]->tss.eip = (unsigned long) child_stack - 4;
stack[1]= 0xec8353c3;
//栈区赋值;
len = 8;
char *p1=stack ,*p2=(char *)((unsigned long)child_stack-len);
while(len--)
put_fs_byte(*(p1++),p2++);
return 0;
} */
/* child_stack-4 00 00 00 00 */
/* child_stack-8 00 00 00 00 */
/* child_stack-12 arg */
/* child_stack-16 fn */ //esp
/* child_stack-20 00 00 00 00 */
/* child_stack-24 00 00 00 00 */
/* child_stack-28 00 00 00 00 */
/* child_stack-32 0x000000c3 */
/*
int do_clone(int nr,long tmp,int (*fn)(void *), void *child_stack, int flag)
{
void * argv =NULL;
//共享物理页写法有缺陷可能child——stack跨页
//子进程task[nr]的页表项获取目的是更新成child——stacK的物理页
unsigned long nrfrom_page,nrfrom,from_page,from,tpage,address;
address = task[nr]->tss.esp;
nrfrom_page = ((address>>20) & 0xffc);
nrfrom_page += ((task[nr]->start_code>>20) & 0xffc);//页目录
nrfrom=*(unsigned long *)nrfrom_page; //页表项
if(!(nrfrom&1)) //无页表项分配一个
{
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return -1;
}
else
{
*(unsigned long *)nrfrom_page= tpage | 7;
mem_map[(tpage-LOW_MEM)>>12]++;
}
}
nrfrom &= 0xfffff000;
nrfrom_page = nrfrom + ((address>>10) & 0xffc);
//获取child_stack的物理页,并将其复制给task[nr];
address=(unsigned long) child_stack;
from_page = ((address>>20) & 0xffc);
from_page += ((current->start_code>>20) & 0xffc);//页目录
if(!(*(unsigned long *)from_page&1)) //页表项不存在,申请页面
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return -1;
}
else
{
*(unsigned long *)from_page= tpage | 7;
mem_map[(tpage-LOW_MEM)>>12]++;
}
from=*(unsigned long *)from_page; //页表项
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
if(!(*(unsigned long *)from_page & 1)) //物理页不存在
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return -1;
}
else
{
*(unsigned long *)from_page= tpage | 7;
mem_map[(tpage-LOW_MEM)>>12]++;
}
//复制给nr
*(unsigned long *)nrfrom_page = *(unsigned long *)from_page;
unsigned long stack[128];
unsigned long off=(unsigned long)child_stack & 0xfff;
if(off < 32)
{
printk("地址跨页,我也没办法.\n");
return -1;
}
//更新esp,让其指向child_stack同样偏移的地点;
//注意这里child_stack 指向的是高地址要向下面push.
task[nr]->tss.esp &=0xfffff000;
task[nr]->tss.esp |=off - 16;
//eip 只有一条ret //所以传进来的fn必须以exit结尾不能return ,
//因为我不会return这个玩意push ebp 又pop 啥的
task[nr]->tss.eip &=0xfffff000;
task[nr]->tss.eip |=off - 32;
//ret eip
stack[0]=0x000000c3;
stack[4]=(unsigned long)fn;
stack[5]=(unsigned long)argv;
char * p1=stack, *p2=(unsigned long) child_stack - 32,len=32;
while(len--)
put_fs_byte(*(p1++),p2++);
printk("nr:%d::fn:%x\n",nr,(unsigned long )fn);
return 0;
}
*/
int copy_page_tables_clone(unsigned long from,unsigned long to,long size,int flag)
{
unsigned long * from_page_table;
unsigned long * to_page_table;
unsigned long this_page;
unsigned long * from_dir, * to_dir;
unsigned long nr;
//获取栈区域的物理页
/* child_stack= child_stack?child_stack:(0x2000000-4096);
printk("chid:%lx\n",(unsigned long ) child_stack);
unsigned long sfrom,sfrom_page,tpage;
sfrom_page = ((child_stack>>20) & 0xffc);
sfrom_page += ((current->start_code>>20) & 0xffc);//页目录
printk("11111222222222222221.");
if(!(*(unsigned long *)sfrom_page&1)) //页表项不存在,申请页面
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return -1;
}
else
{
*(unsigned long *)sfrom_page= tpage | 7;
mem_map[(tpage-LOW_MEM)>>12]++;
printk("111555555555511.");
}
printk("111111111111111111."); */
/* sfrom=*(unsigned long *)sfrom_page; //页表项
sfrom &= 0xfffff000;
sfrom_page = sfrom + ((child_stack>>10) & 0xffc);
printk("1111133333333.");
if(!(*(unsigned long *)sfrom_page & 1)) //物理页不存在
if(!(tpage=get_free_page()))
{
printk("out of memory.\n");
return -1;
}
else
{
*(unsigned long *)sfrom_page= tpage | 7;
printk("111114444466666664111.");
mem_map[(tpage-LOW_MEM)>>12]++;
printk("11111444444444444111.");
}
printk("11111111133333333333311."); */
//遍历进程size个页目录复制到子进程中
if ((from&0x3fffff) || (to&0x3fffff))
panic("copy_page_tables called with wrong alignment");
from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
to_dir = (unsigned long *) ((to>>20) & 0xffc);
size = ((unsigned) (size+0x3fffff)) >> 22;
for( ; size-->0 ; from_dir++,to_dir++) {
if (1 & *to_dir)
panic("copy_page_tables: already exist");
if (!(1 & *from_dir))
continue;
from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
if (!(to_page_table = (unsigned long *) get_free_page()))
return -1; /* Out of memory, see freeing */
*to_dir = ((unsigned long) to_page_table) | 7;
nr = (from==0)?0xA0:1024;
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table;
if (!(1 & this_page))
continue;
//如果flag==0 表示创建进程 而非线程
//如果当前物理页是栈区物理页,父子进程都有写权限
if(!flag)// && !(this_page == *(unsigned long *)sfrom_page))
this_page &= ~2;
*to_page_table = this_page;
if (this_page > LOW_MEM) {
*from_page_table = this_page;
this_page -= LOW_MEM;
this_page >>= 12;
mem_map[this_page]++;
}
}
}
invalidate();
return 0;
}