From e69328eb2d1c8b993f0be72f14b2b0b67287cb30 Mon Sep 17 00:00:00 2001 From: hinus Date: Mon, 21 Jun 2021 12:27:57 +0800 Subject: [PATCH] Title: Implementation of fork. Issue: https://gitee.com/hinus/linux_kernel_011/issues/I3WFD7 Description: Implementing fork, and we get into page fault. --- include/linux/mm.h | 6 +++ include/linux/sched.h | 55 +++++++++++++++++++- include/linux/sys.h | 8 +++ include/unistd.h | 30 +++++++++++ kernel/chr_drv/console.c | 2 +- kernel/fork.c | 107 +++++++++++++++++++++++++++++---------- kernel/main.c | 15 ++++-- kernel/sched.c | 85 +++++++------------------------ kernel/sys_call.S | 64 +++++++++++++---------- mm/memory.c | 94 ++++++++++++++++++++++++++++++++++ 10 files changed, 339 insertions(+), 127 deletions(-) create mode 100644 include/linux/sys.h create mode 100644 include/unistd.h diff --git a/include/linux/mm.h b/include/linux/mm.h index ade08d0..7030a24 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1,9 +1,15 @@ #ifndef _MM_H #define _MM_H +#include + #define PAGE_SIZE 4096 extern unsigned long get_free_page(); +extern void free_page(unsigned long addr); + +#define invalidate() \ +__asm__("movl %%eax,%%cr3"::"a" (0)) #define LOW_MEM 0x100000 extern unsigned long HIGH_MEMORY; diff --git a/include/linux/sched.h b/include/linux/sched.h index 2bd53a7..fc6d7ea 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -4,6 +4,15 @@ #define HZ 100 #define NR_TASKS 64 +#define TASK_SIZE 0x04000000 + +#if (TASK_SIZE & 0x3fffff) +#error "TASK_SIZE must be multiple of 4M" +#endif + +#if (((TASK_SIZE>>16)*NR_TASKS) != 0x10000) +#error "TASK_SIZE*NR_TASKS must be 4GB" +#endif #define FIRST_TASK task[0] #define LAST_TASK task[NR_TASKS-1] @@ -11,16 +20,29 @@ #include #include +#define TASK_RUNNING 0 +#define TASK_INTERRUPTIBLE 1 +#define TASK_UNINTERRUPTIBLE 2 +#define TASK_ZOMBIE 3 +#define TASK_STOPPED 4 + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +extern int copy_page_tables(unsigned long from, unsigned long to, long size); +extern int free_page_tables(unsigned long from, unsigned long size); void trap_init(); void sched_init(); void test_a(); void test_b(); -int create_second_process(); extern struct task_struct *task[NR_TASKS]; extern struct task_struct *current; +typedef int (*fn_ptr)(); + struct tss_struct { long back_link; long esp0; @@ -48,12 +70,18 @@ struct tss_struct { }; struct task_struct { + long state; + long pid; + struct task_struct *p_pptr; struct desc_struct ldt[3]; struct tss_struct tss; }; #define INIT_TASK \ { \ + 0, \ + 0, \ + &init_task.task,\ { \ {0, 0}, \ {0xfff, 0xc0fa00}, \ @@ -89,6 +117,31 @@ struct task_struct { "d" (_TSS(n)),"c" ((long) task[n])); \ } +#define _set_base(addr,base) \ +__asm__("movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %%dl,%1\n\t" \ + "movb %%dh,%2" \ + ::"m" (*((addr)+2)), \ + "m" (*((addr)+4)), \ + "m" (*((addr)+7)), \ + "d" (base) \ + :) + +#define _set_limit(addr,limit) \ +__asm__("movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %1,%%dh\n\t" \ + "andb $0xf0,%%dh\n\t" \ + "orb %%dh,%%dl\n\t" \ + "movb %%dl,%1" \ + ::"m" (*(addr)), \ + "m" (*((addr)+6)), \ + "d" (limit) \ + :"dx") + +#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base ) +#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 ) #define _get_base(addr) ({\ unsigned long __base; \ diff --git a/include/linux/sys.h b/include/linux/sys.h new file mode 100644 index 0000000..e5331c1 --- /dev/null +++ b/include/linux/sys.h @@ -0,0 +1,8 @@ +extern int sys_fork(); + +fn_ptr sys_call_table[] = { + sys_fork, +}; + +int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); + diff --git a/include/unistd.h b/include/unistd.h new file mode 100644 index 0000000..f953ba1 --- /dev/null +++ b/include/unistd.h @@ -0,0 +1,30 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifdef __LIBRARY__ + +#define __NR_fork 0 + +#define _syscall0(type, name) \ +type name() { \ + long __res; \ +__asm__ volatile("int $0x80\n\r"\ + : "=a"(__res) \ + : "a"(__NR_##name)); \ + if (__res >= 0) \ + return (type)__res; \ + errno = -__res; \ + return -1; \ +} + +#endif /* __LIBRARY__ */ + +extern int errno; + +static int fork(); + +#endif diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c index 749aa72..abbe7d7 100644 --- a/kernel/chr_drv/console.c +++ b/kernel/chr_drv/console.c @@ -149,7 +149,7 @@ void con_init() { display_ptr = ((char *)video_mem_base) + video_size_row - 8; while (*display_desc) { *display_ptr++ = *display_desc++; - *display_ptr++; + display_ptr++; } origin = video_mem_base; diff --git a/kernel/fork.c b/kernel/fork.c index 47dc817..82eeeb4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -3,51 +3,102 @@ #include #include -int find_empty_process() { - int i; - for (i = 1; i < NR_TASKS; i++) { - if (!task[i]) - return i; +long last_pid = 0; + +int copy_mem(int nr, struct task_struct* p) { + unsigned long old_data_base,new_data_base,data_limit; + unsigned long old_code_base,new_code_base,code_limit; + + code_limit = get_limit(0x0f); + data_limit = get_limit(0x17); + old_code_base = get_base(current->ldt[1]); + old_data_base = get_base(current->ldt[2]); + if (old_data_base != old_code_base) + printk("We don't support separate I&D"); + if (data_limit < code_limit) + printk("Bad data_limit"); + + new_data_base = new_code_base = nr * TASK_SIZE; + 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)) { + free_page_tables(new_data_base,data_limit); + return -ENOMEM; } - return -EAGAIN; + return 0; } -int create_second_process1() { - int nr; +int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, + long ebx,long ecx,long edx, long orig_eax, + long fs,long es,long ds, + long eip,long cs,long eflags,long esp,long ss) { struct task_struct *p; - //char* ustack; p = (struct task_struct *) get_free_page(); if (!p) return -EAGAIN; - /* - ustack = (char*) get_free_page(); - if (!ustack) - return -EAGAIN; - */ + task[nr] = p; + *p = *current; - //*p = *current; + p->pid = last_pid; + p->p_pptr = current; - p->tss.eip = (long)test_b; - p->tss.ldt = _LDT(nr); + p->ldt[0] = current->ldt[0]; + p->ldt[1] = current->ldt[1]; + p->ldt[2] = current->ldt[2]; + + p->tss.back_link = 0; + p->tss.esp0 = PAGE_SIZE + (long)p - 8; p->tss.ss0 = 0x10; - p->tss.esp0 = PAGE_SIZE + (long)p; - p->tss.ss = 0x10; - p->tss.ds = 0x10; - p->tss.es = 0x10; - p->tss.fs = 0x10; - p->tss.gs = 0x10; - p->tss.cs = 0x08; - p->tss.esp = PAGE_SIZE + (long)p; - p->tss.eflags = 0x602; + p->tss.cr3 = current->tss.cr3; + p->tss.eip = eip; + p->tss.eflags = eflags; + p->tss.eax = 0; + p->tss.ecx = ecx; + p->tss.edx = edx; + p->tss.ebx = ebx; + p->tss.esp = esp; + p->tss.ebp = ebp; + p->tss.esi = esi; + p->tss.edi = edi; + p->tss.es = es & 0xffff; + p->tss.cs = cs & 0xffff; + p->tss.ss = ss & 0xffff; + p->tss.ds = ds & 0xffff; + p->tss.fs = fs & 0xffff; + p->tss.gs = gs & 0xffff; + p->tss.ldt = _LDT(nr); + p->tss.trace_bitmap = 0x80000000; + + if (copy_mem(nr, p)) { + task[nr] = NULL; + free_page((long)p); + return -EAGAIN; + } set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); - task[nr] = p; + return last_pid; +} - return nr; +int find_empty_process() { + int i; +repeat: + if ((++last_pid)<0) last_pid=1; + + for(i=0 ; ipid == last_pid)) + goto repeat; + } + + for (i = 1; i < NR_TASKS; i++) { + if (!task[i]) + return i; + } + + return -EAGAIN; } diff --git a/kernel/main.c b/kernel/main.c index 7220d00..69fb04a 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -1,5 +1,11 @@ #define __LIBRARY__ +#include + +static inline _syscall0(int, fork); + +int errno; + #include #include @@ -44,14 +50,17 @@ void main(void) sched_init(); tty_init(); - - printk("%x, %x\n\r", task[0], task[1]); move_to_user_mode(); + + if (fork() == 0) { + test_b(); +// schedule(); + while(1) {} + } test_a(); __asm__ __volatile__( - "int $0x80\n\r" "loop:\n\r" "jmp loop" ::); diff --git a/kernel/sched.c b/kernel/sched.c index 9a876b6..4b3f0ef 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -33,33 +34,26 @@ struct int clock = COUNTER; -static int cnt = 0; -static int isFirst = 1; void do_timer(long cpl) { - cnt++; - - if (cnt > 2) - return; - if (clock >0 && clock <= COUNTER) { clock--; } else if (clock == 0) { - clock = COUNTER; - if (isFirst) { - isFirst = 0; - switch_to(1); - } - else { - isFirst = 1; - switch_to(0); - } + schedule(); } else { clock = COUNTER; } - cnt--; +} + +void schedule() { + if (current == task[0] && task[1]) { + switch_to(1); + } + else if (current == task[1]) { + switch_to(0); + } } void sched_init() { @@ -79,8 +73,6 @@ void sched_init() { p++; } - create_second_process(); - __asm__("pushfl; andl $0xffffbfff, (%esp); popfl"); ltr(0); lldt(0); @@ -95,65 +87,22 @@ void sched_init() { set_system_gate(0x80, &system_call); } -int create_second_process() { - struct task_struct *p; - int i, nr; - - nr = find_empty_process(); - if (nr < 0) - return -EAGAIN; - - //p = (struct task_struct*) &(second_task.task); - p = (struct task_struct*) get_free_page(); - *p = *current; - - set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); - - p->ldt[0] = current->ldt[0]; - p->ldt[1] = current->ldt[1]; - p->ldt[2] = current->ldt[2]; - - p->tss = current->tss; - - p->tss.eip = (long)test_b; - p->tss.ldt = _LDT(nr); - p->tss.ss0 = 0x10; - p->tss.esp0 = PAGE_SIZE + (long)p - 8; - p->tss.ss = 0x10; - p->tss.ds = 0x10; - p->tss.es = 0x10; - p->tss.cs = 0x8; - p->tss.fs = 0x10; - p->tss.esp = PAGE_SIZE + (long)p - 8; - p->tss.eflags = 0x602; - - task[nr] = p; - return nr; -} - void test_a(void) { -__asm__("movl $0, %edi\n\r" - "movl $0x17, %eax\n\t" - "movw %ax, %ds \n\t" - "movw %ax, %es \n\t" - "movw %ax, %fs \n\t" +__asm__("movl $0x0, %edi\n\r" "movw $0x18, %ax\n\t" "movw %ax, %gs \n\t" "movb $0x0c, %ah\n\r" "movb $'A', %al\n\r" - "loopa:\n\r" - "movw %ax, %gs:(%edi)\n\r" - "jmp loopa"); + "movw %ax, %gs:(%edi)\n\r"); } void test_b(void) { -__asm__("movl $0, %edi\n\r" +__asm__("pushl %ebp\n\r" + "movl %esp, %ebp\n\r" + "movl $0x30, %edi\n\r" "movw $0x18, %ax\n\t" "movw %ax, %gs \n\t" "movb $0x0f, %ah\n\r" "movb $'B', %al\n\r" - "loopb:\n\r" - "movw %ax, %gs:(%edi)\n\r" - "jmp loopb"); + "movw %ax, %gs:(%edi)\n\r"); } diff --git a/kernel/sys_call.S b/kernel/sys_call.S index a75406c..7ea146f 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -1,6 +1,6 @@ .code32 .text -.globl system_call, timer_interrupt +.globl system_call, timer_interrupt, sys_fork EAX = 0x00 EBX = 0x04 @@ -19,43 +19,40 @@ int_msg: .asciz "In kernel interrupt\n\r" system_call: + pushl %ds + pushl %es + pushl %fs pushl %eax - pushl %ecx pushl %edx - pushw %ds - pushw %es - pushw %fs - movl $0x10, %eax - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - /* call _printk */ - pushl $int_msg - call printk - popl %eax - popw %fs - popw %es - popw %ds - popl %edx - popl %ecx - popl %eax - iret + pushl %ecx + pushl %ebx + movl $0x10, %edx + movw %dx, %ds + movw %dx, %es + movl $0x17, %edx + movw %dx, %fs + + call sys_call_table(, %eax, 4) + pushl %eax + ret_from_sys_call: popl %eax popl %ebx popl %ecx popl %edx - popw %fs - popw %es - popw %ds + addl $4, %esp + popl %fs + popl %es + popl %ds iret .align 4 timer_interrupt: - pushw %ds - pushw %es - pushw %fs + pushl %ds + pushl %es + pushl %fs + pushl $-1 pushl %edx pushl %ecx pushl %ebx @@ -73,3 +70,18 @@ timer_interrupt: call do_timer addl $4, %esp jmp ret_from_sys_call + +.align 4 +sys_fork: + call find_empty_process + testl %eax, %eax + js 1f + pushl %gs + pushl %esi + pushl %edi + pushl %ebp + pushl %eax + call copy_process + addl $20, %esp +1: ret + diff --git a/mm/memory.c b/mm/memory.c index 26aa66b..ea9ddf0 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4,6 +4,100 @@ unsigned long HIGH_MEMORY = 0; unsigned char mem_map [ PAGING_PAGES ] = {0,}; +void free_page(unsigned long addr) { + if (addr < LOW_MEM) return; + if (addr >= HIGH_MEMORY) + printk("trying to free nonexistent page"); + + addr -= LOW_MEM; + addr >>= 12; + if (mem_map[addr]--) return; + mem_map[addr]=0; + printk("trying to free free page"); +} + +int free_page_tables(unsigned long from,unsigned long size) { + unsigned long *pg_table; + unsigned long * dir, nr; + + if (from & 0x3fffff) + printk("free_page_tables called with wrong alignment"); + if (!from) + printk("Trying to free up swapper memory space"); + size = (size + 0x3fffff) >> 22; + dir = (unsigned long *) ((from>>20) & 0xffc); + + for ( ; size-->0 ; dir++) { + if (!(1 & *dir)) + continue; + pg_table = (unsigned long *) (0xfffff000 & *dir); + for (nr=0 ; nr<1024 ; nr++) { + if (*pg_table) { + if (1 & *pg_table) + free_page(0xfffff000 & *pg_table); + *pg_table = 0; + } + pg_table++; + } + free_page(0xfffff000 & *dir); + *dir = 0; + } + invalidate(); + return 0; +} + +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)) { + printk("copy_page_tables called with wrong alignment"); + } + + /* Get high 10 bits. As PDE is 4 byts, so right shift 20.*/ + from_dir = (unsigned long *) ((from>>20) & 0xffc); + to_dir = (unsigned long *) ((to>>20) & 0xffc); + + size = ((unsigned) (size+0x3fffff)) >> 22; + for( ; size-->0 ; from_dir++,to_dir++) { + if (1 & *to_dir) + printk("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; + + *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 (!this_page) + continue; + 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; +} + + void mem_init(long start_mem, long end_mem) { int i; -- Gitee