From db21424c64e229cfae205c886e34ece10410a27a Mon Sep 17 00:00:00 2001 From: hinus Date: Tue, 27 Jul 2021 22:11:10 +0800 Subject: [PATCH] Title: Handle no page error. Issue: https://gitee.com/hinus/linux_kernel_011/issues/I3W9AB Description: do_no_page. --- fs/buffer.c | 34 ++++++++++ include/linux/fs.h | 1 + include/linux/mm.h | 2 + include/linux/sched.h | 2 + kernel/fork.c | 1 + mm/Makefile | 2 +- mm/memory.c | 146 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 185 insertions(+), 3 deletions(-) diff --git a/fs/buffer.c b/fs/buffer.c index b0e54a9..3f12e16 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -238,3 +238,37 @@ void buffer_init(long buffer_end) { hash_table[i] = NULL; } +#define COPYBLK(from,to) \ +__asm__("cld\n\t" \ + "rep\n\t" \ + "movsl\n\t" \ + ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \ + :) + +void bread_page(unsigned long address,int dev,int b[4]) { + struct buffer_head * bh[4]; + int i; + + for (i = 0; i < 4; i++) { + if (b[i]) { + if ((bh[i] = getblk(dev,b[i]))) { + if (!bh[i]->b_uptodate) { + ll_rw_block(READ,bh[i]); + } + } + } + else { + bh[i] = NULL; + } + } + + for (i=0 ; i<4 ; i++,address += BLOCK_SIZE) { + if (bh[i]) { + wait_on_buffer(bh[i]); + if (bh[i]->b_uptodate) + COPYBLK((unsigned long) bh[i]->b_data,address); + brelse(bh[i]); + } + } +} + diff --git a/include/linux/fs.h b/include/linux/fs.h index 13d5db8..7f9044f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -147,6 +147,7 @@ extern struct buffer_head * getblk(int dev, int block); extern void ll_rw_block(int rw, struct buffer_head * bh); extern void brelse(struct buffer_head * buf); extern struct buffer_head * bread(int dev,int block); +extern void bread_page(unsigned long addr,int dev,int b[4]); extern struct m_inode * namei(const char * pathname); extern int open_namei(const char * pathname, int flag, int mode, diff --git a/include/linux/mm.h b/include/linux/mm.h index 7030a24..5e383c5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -8,6 +8,8 @@ extern unsigned long get_free_page(); extern void free_page(unsigned long addr); +extern inline void oom(); + #define invalidate() \ __asm__("movl %%eax,%%cr3"::"a" (0)) diff --git a/include/linux/sched.h b/include/linux/sched.h index 996df68..4196cda 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -79,6 +79,7 @@ struct task_struct { long counter; long priority; long pid; + unsigned long start_code,end_code,end_data,brk,start_stack; struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; @@ -103,6 +104,7 @@ struct task_struct { 15, \ 15, \ 0, \ +/* ec,brk... */ 0,0,0,0,0,0, \ &init_task.task, 0, 0, 0,\ 0,0,0,0,0,0, \ 0,0022,NULL,NULL,NULL,NULL,0, \ diff --git a/kernel/fork.c b/kernel/fork.c index 04f1f6e..732318d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -21,6 +21,7 @@ int copy_mem(int nr, struct task_struct* p) { panic("Bad data_limit"); new_data_base = new_code_base = nr * TASK_SIZE; + p->start_code = new_code_base; set_base(p->ldt[1],new_code_base); set_base(p->ldt[2],new_data_base); if (copy_page_tables(old_data_base,new_data_base,data_limit)) { diff --git a/mm/Makefile b/mm/Makefile index 19da3c7..9b4c38e 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -1,6 +1,6 @@ GCC := gcc LD := ld -CCFLAG := -I../include -nostdinc -ffreestanding -Wall -fomit-frame-pointer -fno-pic -fno-stack-protector -c -m32 +CCFLAG := -I../include -nostdinc -ffreestanding -Wall -fomit-frame-pointer -fno-pic -fno-stack-protector -std=gnu99 -c -m32 LDFLAG := -Ttext 0x0 -s --oformat binary -m elf_i386 INCDIR := ../include OBJS := swap.o memory.o page.o diff --git a/mm/memory.c b/mm/memory.c index a04a69d..7f9325e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1,4 +1,5 @@ #include +#include unsigned long HIGH_MEMORY = 0; @@ -7,6 +8,11 @@ unsigned char mem_map [ PAGING_PAGES ] = {0,}; #define copy_page(from,to) \ __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):) +extern inline void oom() { + printk("out of memory\n\r"); + //do_exit(SIGSEGV); +} + void free_page(unsigned long addr) { if (addr < LOW_MEM) return; if (addr >= HIGH_MEMORY) @@ -127,8 +133,145 @@ void do_wp_page(unsigned long error_code, unsigned long address) { *((unsigned long *) ((address>>20) &0xffc))))); } +static unsigned long put_page(unsigned long page,unsigned long address) { + unsigned long tmp, *page_table; + 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; + return page; +} + +void get_empty_page(unsigned long address) { + unsigned long tmp; + if (!(tmp = get_free_page()) || !put_page(tmp, address)) { + free_page(tmp); + oom(); + } +} + +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); + + from = *(unsigned long *) from_page; + if (!(from & 1)) + return 0; + from &= 0xfffff000; + from_page = from + ((address>>10) & 0xffc); + phys_addr = *(unsigned long *) from_page; + 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"); + + *(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; +} + +static int share_page(struct m_inode * inode, unsigned long address) { + struct task_struct ** p; + + if (inode->i_count < 2 || !inode) + return 0; + + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!*p) + continue; + if (current == *p) + continue; + if (inode != (*p)->executable) + continue; + if (try_to_share(address, *p)) + return; + } + + return 0; +} + void do_no_page(unsigned long error_code,unsigned long address) { - unsigned long a = error_code + address; + int nr[4]; + unsigned long tmp; + unsigned long page; + int block,i; + struct m_inode * inode; + + if (address < TASK_SIZE) + printk("\n\rBAD!! KERNEL PAGE MISSING\n\r"); + if (address - current->start_code > TASK_SIZE) { + printk("Bad things happen: nonexistent page error in do_no_page\n\r"); + //do_exit(); + } + + address &= 0xfffff000; + tmp = address - current->start_code; + + if (!current->executable || tmp >= current->end_code) { + get_empty_page(address); + return; + } + + if (!share_page(current->executable, tmp)) + return; + + if (!(page = get_free_page())) + oom(); + + block = 1 + tmp / BLOCK_SIZE; + + for (i = 0; i < 4; 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) { @@ -146,6 +289,5 @@ void mem_init(long start_mem, long end_mem) { while (end_mem--) { mem_map[i++] = 0; } - } -- Gitee