From f9db006c2c394948352cabe5bd7f327b11181179 Mon Sep 17 00:00:00 2001 From: fan Date: Tue, 19 Apr 2022 16:27:02 +0800 Subject: [PATCH 01/62] change task & timer, simple malloc->kmalloc --- debug.gdb | 2 +- kernel/boot/start.S | 2 +- kernel/src/main.c | 9 +++++---- kernel/src/task.c | 7 +++++-- kernel/src/timer.c | 7 ++++++- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/debug.gdb b/debug.gdb index 6310a5621..9bdced160 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *0x81600 +b *0x841e8 target remote :1234 \ No newline at end of file diff --git a/kernel/boot/start.S b/kernel/boot/start.S index 179c95071..2b6d16d9f 100644 --- a/kernel/boot/start.S +++ b/kernel/boot/start.S @@ -122,7 +122,7 @@ exception_vector_table: b fail .align 7 - b fail + b sync_exception_entry .align 7 b irq_entry .align 7 diff --git a/kernel/src/main.c b/kernel/src/main.c index 73720272a..bf031390c 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -11,17 +11,18 @@ int main(unsigned long dtb_base){ uart_init(); + // uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); // buddy_debug(); // chunk_debug(); - kmalloc_debug(); + // kmalloc_debug(); - // uart_getc(); - // enable_timer_irq(); + + enable_timer_irq(); // enable_AUX_MU_IER_r(); - // enable_irq(); // DAIF set to 0b0000 + enable_irq(); // DAIF set to 0b0000 PrintWelcome(); diff --git a/kernel/src/task.c b/kernel/src/task.c index 2bdcaf85c..1b8915af3 100644 --- a/kernel/src/task.c +++ b/kernel/src/task.c @@ -7,7 +7,8 @@ unsigned int curr_poriority = 100; Task *task_head = NULL; void add_task(Handler handler, unsigned int priority){ - Task *task = (Task*)simple_malloc(sizeof(Task)); + Task *task = (Task*)kmalloc(sizeof(Task)); + memset((char *)task, 0, sizeof(Task)); task->handler = handler; task->priority = priority; task->next = NULL; @@ -88,8 +89,10 @@ void do_task(){ disable_irq(); // new_task should be the low priority task / me / null + Task *tmp = task_head; task_head = task_head->next; - + kfree(tmp); + tmp = NULL; /* ** if prev_poriority(curr_poriority now) is highest priority task ** it means the prev_task need to be continued to finish the task. diff --git a/kernel/src/timer.c b/kernel/src/timer.c index 712d08b39..3eb3bbb7d 100644 --- a/kernel/src/timer.c +++ b/kernel/src/timer.c @@ -16,7 +16,8 @@ void add_timer(TimerTask task, unsigned long long expired_time, void *args){ :"=r"(system_timer), "=r"(frq) ); - Timer *timer = (Timer*)simple_malloc(sizeof(Timer)); + Timer *timer = (Timer*)kmalloc(sizeof(Timer)); + memset((char *)timer, 0, sizeof(Timer)); timer->expired_time = system_timer + expired_time * frq; timer->task = task; timer->args = args; @@ -61,9 +62,12 @@ void timer_interrupt_handler(){ // } while(head != NULL){ if(head->next != NULL){ + Timer *tmp = head; head->task(head->args); head = head->next; head->prev = NULL; + kfree(tmp); + tmp = NULL; unsigned long long system_timer = 0; asm volatile("mrs %0, cntpct_el0\n\t" :"=r"(system_timer)); if(head->expired_time > system_timer){ @@ -73,6 +77,7 @@ void timer_interrupt_handler(){ } else{ head->task(head->args); + kfree(head); head = NULL; } } From 5d7bed624cb69f278aed585b2a503128cb440869 Mon Sep 17 00:00:00 2001 From: fan Date: Tue, 19 Apr 2022 18:10:52 +0800 Subject: [PATCH 02/62] fix sub sp bug -> rpi3 can do irq --- kernel/Makefile | 4 ++-- kernel/boot/start.S | 4 ++-- kernel/src/main.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index b6013b61b..1eea77138 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -18,10 +18,10 @@ CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles all: clean kernel8.img run $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g kernel8.img: $(ALLOFILES) $(LD) -nostdlib -nostartfiles $(ALLOFILES) -T boot/linker.ld -o kernel8.elf diff --git a/kernel/boot/start.S b/kernel/boot/start.S index 2b6d16d9f..aae381638 100644 --- a/kernel/boot/start.S +++ b/kernel/boot/start.S @@ -39,7 +39,7 @@ _start: // save general registers to stack .macro save_all - sub sp, sp, 33 * 8 + sub sp, sp, 32 * 9 stp x0, x1, [sp ,16 * 0] stp x2, x3, [sp ,16 * 1] stp x4, x5, [sp ,16 * 2] @@ -85,7 +85,7 @@ _start: ldp x28, x29, [sp ,16 * 14] ldr x30, [sp, 16 * 15] - add sp, sp, 33 * 8 + add sp, sp, 32 * 9 .endm sync_exception_entry: diff --git a/kernel/src/main.c b/kernel/src/main.c index bf031390c..1439efaae 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -17,7 +17,7 @@ int main(unsigned long dtb_base){ all_allocator_init(); // buddy_debug(); // chunk_debug(); - // kmalloc_debug(); + kmalloc_debug(); enable_timer_irq(); From cac303979cd5811777effa8ad08c0ebe96ff4b74 Mon Sep 17 00:00:00 2001 From: fan Date: Tue, 19 Apr 2022 20:02:44 +0800 Subject: [PATCH 03/62] change size --- kernel/src/malloc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernel/src/malloc.c b/kernel/src/malloc.c index 1cb567493..04375b897 100644 --- a/kernel/src/malloc.c +++ b/kernel/src/malloc.c @@ -159,11 +159,14 @@ void kmalloc_debug(){ // test buddy merge & split uart_puts("\n--------------------------------------TEST BUDDY MERGE & SPLIT--------------------------------------\n\n"); - void *addr3[30]; - for(int i = 0; i < 30 ; i++){ + unsigned int test_size = 30; + void *addr3[test_size]; + for(int i = 0; i < test_size ; i++){ addr3[i] = kmalloc(0x1000); } - for(int i = 0; i < 30 ;i++){ + print_buddy_list(); + + for(int i = 0; i < test_size ;i++){ kfree(addr3[i]); } print_buddy_list(); From c589d8e8e0961762de08000969bf785bc3097c31 Mon Sep 17 00:00:00 2001 From: fan Date: Wed, 20 Apr 2022 01:16:51 +0800 Subject: [PATCH 04/62] task queue change to linux kernel list --- kernel/include/task.h | 15 ++++++-- kernel/src/main.c | 7 ++-- kernel/src/task.c | 80 ++++++++++++++++--------------------------- kernel/src/timer.c | 6 ++-- 4 files changed, 48 insertions(+), 60 deletions(-) diff --git a/kernel/include/task.h b/kernel/include/task.h index a65ac867e..697565934 100644 --- a/kernel/include/task.h +++ b/kernel/include/task.h @@ -1,16 +1,25 @@ #ifndef TASK_H_ #define TASK_H_ #include - +#include typedef void (*Handler)(); +// typedef struct _Task{ +// Handler handler; +// unsigned int priority; +// struct _Task *next; +// struct _Task *prev; +// }Task; + +void init_task_head(); + typedef struct _Task{ + struct list_head list; Handler handler; unsigned int priority; - struct _Task *next; - struct _Task *prev; }Task; + void add_task(Handler, unsigned int); void do_task(); diff --git a/kernel/src/main.c b/kernel/src/main.c index 1439efaae..11e97de15 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -7,6 +7,7 @@ #include #include #include +#include int main(unsigned long dtb_base){ @@ -17,13 +18,13 @@ int main(unsigned long dtb_base){ all_allocator_init(); // buddy_debug(); // chunk_debug(); - kmalloc_debug(); - + // kmalloc_debug(); + init_task_head(); + // printf("wef\n"); enable_timer_irq(); // enable_AUX_MU_IER_r(); enable_irq(); // DAIF set to 0b0000 - PrintWelcome(); diff --git a/kernel/src/task.c b/kernel/src/task.c index 1b8915af3..7a02bf39a 100644 --- a/kernel/src/task.c +++ b/kernel/src/task.c @@ -3,45 +3,39 @@ #include #include #include +#include unsigned int curr_poriority = 100; -Task *task_head = NULL; + +Task *task_head; + +void init_task_head(){ + task_head = (Task*)kmalloc(sizeof(Task)); + INIT_LIST_HEAD(&task_head->list); +} + void add_task(Handler handler, unsigned int priority){ Task *task = (Task*)kmalloc(sizeof(Task)); memset((char *)task, 0, sizeof(Task)); + INIT_LIST_HEAD(&task->list); task->handler = handler; task->priority = priority; - task->next = NULL; - task->prev = NULL; - if(task_head == NULL){ - task_head = task; + if(list_empty(&task_head->list)){ + list_add_tail(&task->list, &task_head->list); return; } - - Task *tmp = task_head; - while(tmp->next != NULL && task->priority > tmp->priority){ - tmp = tmp->next; - } - if(task->priority <= tmp->priority){ - if(tmp == task_head){ - task_head = task; - task_head->next = tmp; - tmp->prev = task_head; - } - else{ - task->next = tmp; - task->prev = tmp->prev; - tmp->prev->next = task; - tmp->prev = task; + struct list_head *pos; + list_for_each(pos, &task_head->list){ + Task *tmp = (Task *)pos; + if(task->priority <= tmp->priority){ + list_add(&task->list, tmp->list.prev); } } - else{ - tmp->next = task; - task->prev = tmp; + if(list_is_head(pos, &task_head->list)){ + list_add_tail(&task->list, &task_head->list); } - } @@ -53,26 +47,10 @@ void do_task(){ // } // uart_sputs("\n"); - // NO PREEMPTION - // while(task_head != NULL){ - // if(task_head->next != NULL){ - // Handler handler = task_head->handler; - // task_head = task_head->next; - // task_head->prev = NULL; - // enable_irq(); - // handler(); - // } - // else{ - // Handler handler = task_head->handler; - // task_head = NULL; - // enable_irq(); - // handler(); - // } - // } - - while(task_head != NULL){ + while(!list_empty(&task_head->list)){ + Task *t_task = (Task *)task_head->list.next; // curr_task is highest priority task, return it and finish the curr_task. - if(curr_poriority <= task_head->priority){ + if(curr_poriority <= t_task->priority){ // uart_sputs("curr_task is highest priority task, return it.\n"); return; } @@ -82,17 +60,17 @@ void do_task(){ ** new(highest priority) -> prev(curr_poriority) -> low priority task... */ unsigned int prev_poriority = curr_poriority; // save current poriority - curr_poriority = task_head->priority; - Handler handler = task_head->handler; + curr_poriority = t_task->priority; + Handler handler = t_task->handler; + // new_task should be the low priority task / me / null + enable_irq(); handler(); disable_irq(); - // new_task should be the low priority task / me / null - Task *tmp = task_head; - task_head = task_head->next; - kfree(tmp); - tmp = NULL; + list_del(&t_task->list); + kfree(t_task); + t_task = NULL; /* ** if prev_poriority(curr_poriority now) is highest priority task ** it means the prev_task need to be continued to finish the task. diff --git a/kernel/src/timer.c b/kernel/src/timer.c index 3eb3bbb7d..6667b4c72 100644 --- a/kernel/src/timer.c +++ b/kernel/src/timer.c @@ -4,7 +4,7 @@ #include #include -int printAfter2Second = 1; +int printAfter2Second = 0; Timer *head = NULL; void add_timer(TimerTask task, unsigned long long expired_time, void *args){ unsigned long long system_timer = 0; @@ -82,9 +82,9 @@ void timer_interrupt_handler(){ } } if(head == NULL) set_long_timer_irq(); - if(printAfter2Second) { + if(printAfter2Second == 0) { add_timer(timeout_print, 2, "[*] After Two Second, Hello User\n# "); - printAfter2Second = 0; + printAfter2Second = 1; } enable_timer_irq(); } From 4350f41246b39d5c546cfba5f643a3ae87378b89 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 21 Apr 2022 02:46:37 +0800 Subject: [PATCH 05/62] thead init & create (? --- kernel/include/sched.h | 44 +++++++++++++++++++++++++++++++++++++++++ kernel/include/stddef.h | 1 + kernel/include/task.h | 12 ++++------- kernel/src/allocator.c | 3 ++- kernel/src/main.c | 15 ++++++++------ kernel/src/sched.S | 28 ++++++++++++++++++++++++++ kernel/src/sched.c | 44 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 kernel/include/sched.h create mode 100644 kernel/src/sched.S create mode 100644 kernel/src/sched.c diff --git a/kernel/include/sched.h b/kernel/include/sched.h new file mode 100644 index 000000000..b7f40be66 --- /dev/null +++ b/kernel/include/sched.h @@ -0,0 +1,44 @@ +#ifndef SCHED_H_ +#define SCHED_H_ +#include +#include + +#define MAX_THREAD 0x10 +#define STACT_SIZE 0x1000 + +enum thread_state{ + NOUSE, + WAIT, + RUNNING, + EXIT +}; + +typedef struct _cpu_context{ + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; // frame pointer, stack base address + unsigned long lr; // retrun address + unsigned long sp; // stact pointer +}CpuContext; + + +typedef struct _Thread{ + struct list_head list; + CpuContext ctx; + enum thread_state state; + int id; + void *addr; +}Thread; + +void init_thread_pool(); +Thread *thread_create(); + +#endif \ No newline at end of file diff --git a/kernel/include/stddef.h b/kernel/include/stddef.h index 1049a6775..7b4f2876d 100644 --- a/kernel/include/stddef.h +++ b/kernel/include/stddef.h @@ -3,6 +3,7 @@ #define NULL (void *)0x0 #define uint32_t unsigned int +#define unit64_t unsigned long long #endif \ No newline at end of file diff --git a/kernel/include/task.h b/kernel/include/task.h index 697565934..51ef27e10 100644 --- a/kernel/include/task.h +++ b/kernel/include/task.h @@ -4,22 +4,18 @@ #include typedef void (*Handler)(); -// typedef struct _Task{ -// Handler handler; -// unsigned int priority; -// struct _Task *next; -// struct _Task *prev; -// }Task; -void init_task_head(); + + typedef struct _Task{ struct list_head list; - Handler handler; unsigned int priority; + Handler handler; }Task; +void init_task_head(); void add_task(Handler, unsigned int); void do_task(); diff --git a/kernel/src/allocator.c b/kernel/src/allocator.c index 129f4ddf7..b38949aeb 100644 --- a/kernel/src/allocator.c +++ b/kernel/src/allocator.c @@ -52,8 +52,9 @@ void memory_init(){ memory_reserve((void *)0x0, (void *)0x1000); /* Kernel image in the physical memory*/ + unsigned long long stack = (unsigned long long)&_start - 0x10000; uart_puts("[*] Memory Reserve(Kernel image) -> "); - memory_reserve((void *)&_start, (void *)&_end); + memory_reserve((void *)stack, (void *)&_end); /* Kernel image in the physical memory*/ uart_puts("[*] Memory Reserve(Initramfs) -> "); diff --git a/kernel/src/main.c b/kernel/src/main.c index 11e97de15..822079c10 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -6,9 +6,10 @@ #include #include #include -#include #include +#include +extern Thread *thread_head; int main(unsigned long dtb_base){ uart_init(); @@ -16,18 +17,20 @@ int main(unsigned long dtb_base){ print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); - // buddy_debug(); - // chunk_debug(); - // kmalloc_debug(); + init_thread_pool(); init_task_head(); - // printf("wef\n"); enable_timer_irq(); // enable_AUX_MU_IER_r(); enable_irq(); // DAIF set to 0b0000 - PrintWelcome(); + Thread *new_thread = thread_create(print_string); + print_string(UITOHEX, "new_thread->stack: ", (unsigned long long )new_thread->ctx.sp, 1); + print_string(UITOHEX, "thead_head->next: ", (unsigned long long )thread_head->list.next, 1); + + + PrintWelcome(); ShellLoop(); return 0; diff --git a/kernel/src/sched.S b/kernel/src/sched.S new file mode 100644 index 000000000..163e4e3f0 --- /dev/null +++ b/kernel/src/sched.S @@ -0,0 +1,28 @@ +#include "sched.h" +.globl cpu_switch_to + +cpu_switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/kernel/src/sched.c b/kernel/src/sched.c new file mode 100644 index 000000000..acc3e3010 --- /dev/null +++ b/kernel/src/sched.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +Thread *thread_pool; +Thread *thread_head; + +void init_thread_pool(){ + thread_pool = (Thread*)kmalloc(sizeof(Thread) * MAX_THREAD); + memset((char *)thread_pool, 0, sizeof(Thread) * MAX_THREAD); + for(unsigned int i = 0; i < MAX_THREAD; i++){ + INIT_LIST_HEAD(&thread_pool[i].list); + thread_pool[i].state = NOUSE; + thread_pool[i].id = i; + thread_pool[i].addr = NULL; + } + + thread_head = (Thread *)kmalloc(sizeof(Thread)); + memset((char *)thread_head, 0, sizeof(Thread)); + INIT_LIST_HEAD(&thread_head->list); +} + +Thread *thread_create(void(*func)()){ + unsigned int idx; + for(idx = 0; idx < MAX_THREAD; idx++){ + if(thread_pool[idx].state == NOUSE) + break; + } + if(idx == MAX_THREAD) return NULL; + + Thread *new_thread = &thread_pool[idx]; + void *new_thread_stack = kmalloc(STACT_SIZE); + new_thread->state = WAIT; + new_thread->ctx.fp = (unsigned long)new_thread_stack + STACT_SIZE; + new_thread->ctx.sp = (unsigned long)new_thread_stack + STACT_SIZE; + new_thread->ctx.lr = (unsigned long)func; + + list_add_tail(&new_thread->list, &thread_head->list); + + return new_thread; +} \ No newline at end of file From 212338bcc9953aa5a2b043466085c7b5744b3175 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 21 Apr 2022 02:58:18 +0800 Subject: [PATCH 06/62] thead_create need user&kernel thread --- kernel/include/sched.h | 5 +++-- kernel/src/main.c | 3 ++- kernel/src/sched.c | 13 ++++++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/kernel/include/sched.h b/kernel/include/sched.h index b7f40be66..4f72f7a7d 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -35,10 +35,11 @@ typedef struct _Thread{ CpuContext ctx; enum thread_state state; int id; - void *addr; + void *ustack_addr; + void *kstack_addr; }Thread; -void init_thread_pool(); +void init_thread_pool_and_head(); Thread *thread_create(); #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 822079c10..9bc3bed7d 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -26,7 +26,8 @@ int main(unsigned long dtb_base){ Thread *new_thread = thread_create(print_string); - print_string(UITOHEX, "new_thread->stack: ", (unsigned long long )new_thread->ctx.sp, 1); + print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 1); + print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); print_string(UITOHEX, "thead_head->next: ", (unsigned long long )thread_head->list.next, 1); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index acc3e3010..04fff09ad 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -8,14 +8,15 @@ Thread *thread_pool; Thread *thread_head; -void init_thread_pool(){ +void init_thread_pool_and_head(){ thread_pool = (Thread*)kmalloc(sizeof(Thread) * MAX_THREAD); memset((char *)thread_pool, 0, sizeof(Thread) * MAX_THREAD); for(unsigned int i = 0; i < MAX_THREAD; i++){ INIT_LIST_HEAD(&thread_pool[i].list); thread_pool[i].state = NOUSE; thread_pool[i].id = i; - thread_pool[i].addr = NULL; + thread_pool[i].ustack_addr = NULL; + thread_pool[i].kstack_addr = NULL; } thread_head = (Thread *)kmalloc(sizeof(Thread)); @@ -32,12 +33,14 @@ Thread *thread_create(void(*func)()){ if(idx == MAX_THREAD) return NULL; Thread *new_thread = &thread_pool[idx]; - void *new_thread_stack = kmalloc(STACT_SIZE); new_thread->state = WAIT; - new_thread->ctx.fp = (unsigned long)new_thread_stack + STACT_SIZE; - new_thread->ctx.sp = (unsigned long)new_thread_stack + STACT_SIZE; + new_thread->ustack_addr = kmalloc(STACT_SIZE); + new_thread->kstack_addr = kmalloc(STACT_SIZE); + new_thread->ctx.fp = (unsigned long)new_thread->kstack_addr + STACT_SIZE; + new_thread->ctx.sp = (unsigned long)new_thread->kstack_addr + STACT_SIZE; new_thread->ctx.lr = (unsigned long)func; + list_add_tail(&new_thread->list, &thread_head->list); return new_thread; From 314d8d13383b31bf0501ed8f79ac46beb8a25b23 Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 3 May 2022 22:05:43 +0800 Subject: [PATCH 07/62] something --- kernel/src/main.c | 2 +- kernel/src/sched.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/src/main.c b/kernel/src/main.c index 9bc3bed7d..8e21778e4 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -17,7 +17,7 @@ int main(unsigned long dtb_base){ print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); - init_thread_pool(); + init_thread_pool_and_head(); init_task_head(); enable_timer_irq(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 04fff09ad..6f7f8faa1 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -14,7 +14,7 @@ void init_thread_pool_and_head(){ for(unsigned int i = 0; i < MAX_THREAD; i++){ INIT_LIST_HEAD(&thread_pool[i].list); thread_pool[i].state = NOUSE; - thread_pool[i].id = i; + thread_pool[i].id = i + 1; thread_pool[i].ustack_addr = NULL; thread_pool[i].kstack_addr = NULL; } @@ -44,4 +44,14 @@ Thread *thread_create(void(*func)()){ list_add_tail(&new_thread->list, &thread_head->list); return new_thread; +} + +void idle_thread(){ + while(1){ + for(unsigned int i = 0; i < MAX_THREAD; i++){ + if(thread_pool[i].state == EXIT && thread_pool[i].id != 0){ + + } + } + } } \ No newline at end of file From 579453ca379da11bf37c39772b0371549229a64f Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 4 May 2022 01:15:16 +0800 Subject: [PATCH 08/62] can run into idle thread --- kernel/Makefile | 7 +++- kernel/include/sched.h | 11 ++++++ kernel/src/allocator.c | 26 +++++++------- kernel/src/ctx_switch.S | 28 +++++++++++++++ kernel/src/main.c | 11 +++--- kernel/src/malloc.c | 8 ++--- kernel/src/sched.S | 28 --------------- kernel/src/sched.c | 78 ++++++++++++++++++++++++++++++++++++----- 8 files changed, 138 insertions(+), 59 deletions(-) create mode 100644 kernel/src/ctx_switch.S delete mode 100644 kernel/src/sched.S diff --git a/kernel/Makefile b/kernel/Makefile index 1eea77138..3dd51d235 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -7,11 +7,13 @@ SRC_DIR = src BUILD_DIR = build BOOT_DIR = boot +SFILES_ASM = $(wildcard src/*.S) +OFILES_ASM = $(patsubst src/%.S, build/%.o, $(SFILES_ASM)) # OFILES = $(SFILES:.c=.o) SFILES = $(wildcard src/*.c) OFILES = $(patsubst src/%.c, build/%.o, $(SFILES)) # OFILES = $(SFILES:.c=.o) BOOTFILES = $(wildcard boot/*.S) BOOTOFILES = $(patsubst boot/%.S, build/%.o, $(BOOTFILES)) # BOOTOFILES = $(BOOTFILES:.S=.o) -ALLOFILES = $(OFILES) $(BOOTOFILES) +ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles @@ -20,6 +22,9 @@ all: clean kernel8.img run $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.S + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g + $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 4f72f7a7d..8d8d9730a 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -39,7 +39,18 @@ typedef struct _Thread{ void *kstack_addr; }Thread; +extern struct Thread* get_current(); +extern void cpu_switch_to(Thread* prev, Thread* next); + void init_thread_pool_and_head(); Thread *thread_create(); +void idle_thread(); +void schedule(); +void kernel_main(); +void delay(); +void foo(); + +void print_run_thread(); + #endif \ No newline at end of file diff --git a/kernel/src/allocator.c b/kernel/src/allocator.c index b38949aeb..28c95bc20 100644 --- a/kernel/src/allocator.c +++ b/kernel/src/allocator.c @@ -138,7 +138,7 @@ void *buddy_alloc(unsigned int size){ if(!list_empty(&buddy_list[i].list)){ Frame *alloca_frame = (Frame *)buddy_pop(&buddy_list[i], use_order); unsigned long long alloca_addr = alloca_frame->idx * FRAME_SIZE + BUDDY_ADDR_START; - print_use_frame(size, alloca_frame->idx, use_frames, use_order); + // print_use_frame(size, alloca_frame->idx, use_frames, use_order); // print_buddy_list(); // memset((void *)alloca_addr, '\0', (1 << alloca_frame->order) * FRAME_SIZE); return (void *)alloca_addr; @@ -174,9 +174,9 @@ void *release_redundant(Frame *left_frame, int use_order){ right_frame->order = samll_order; left_frame->order = samll_order; list_add(&frames[right_frame->idx].list, &buddy_list[samll_order].list); - print_string(UITOHEX, "[-] Alloc Buddy -> Split: Left_Addr = 0x", left_frame->idx * FRAME_SIZE, 0); - print_string(UITOHEX, " | Right_Addr = 0x", right_frame->idx * FRAME_SIZE, 0); - print_string(UITOA, " | order = ", left_frame->order, 1); + // print_string(UITOHEX, "[-] Alloc Buddy -> Split: Left_Addr = 0x", left_frame->idx * FRAME_SIZE, 0); + // print_string(UITOHEX, " | Right_Addr = 0x", right_frame->idx * FRAME_SIZE, 0); + // print_string(UITOA, " | order = ", left_frame->order, 1); } left_frame->free = 0; return left_frame; @@ -200,8 +200,8 @@ void buddy_free(void *addr){ */ if(buddy_frame == NULL || buddy_frame->free == 0 || buddy_frame->order != target_frame->order){ - print_string(UITOHEX, "[*] Free Buddy -> Free Addr: 0x", (unsigned long long)addr, 0); - print_string(UITOA, " | order = ", target_frame->order, 1); + // print_string(UITOHEX, "[*] Free Buddy -> Free Addr: 0x", (unsigned long long)addr, 0); + // print_string(UITOA, " | order = ", target_frame->order, 1); target_frame->free = 1; @@ -214,24 +214,24 @@ void buddy_free(void *addr){ while(buddy_frame->free == 1 && buddy_frame->order == target_frame->order){ if(first){ - print_string(UITOHEX, "[*] Free Buddy -> Free Addr: 0x", (unsigned long long)addr, 1); + // print_string(UITOHEX, "[*] Free Buddy -> Free Addr: 0x", (unsigned long long)addr, 1); first = 0; } /* buddy cannot be allocated, it will be merged */ list_del(&frames[buddy_frame->idx].list); if(target_frame->idx > buddy_frame->idx){ - print_string(UITOHEX, "[+] Free Buddy -> Merge: Left_Addr = 0x", buddy_frame->idx * FRAME_SIZE, 0); - print_string(UITOHEX, " | Right_Addr = 0x", target_frame->idx * FRAME_SIZE, 0); - print_string(UITOA, " | order = ", buddy_frame->order+1, 1); + // print_string(UITOHEX, "[+] Free Buddy -> Merge: Left_Addr = 0x", buddy_frame->idx * FRAME_SIZE, 0); + // print_string(UITOHEX, " | Right_Addr = 0x", target_frame->idx * FRAME_SIZE, 0); + // print_string(UITOA, " | order = ", buddy_frame->order+1, 1); buddy_frame->order++; target_frame->order = -1; target_frame = buddy_frame; } else{ - print_string(UITOHEX, "[+] Free Buddy -> Merge: Left_Addr = 0x", target_frame->idx * FRAME_SIZE, 0); - print_string(UITOHEX, " | Right_Addr = 0x", buddy_frame->idx * FRAME_SIZE, 0); - print_string(UITOA, " | order = ", target_frame->order+1, 1); + // print_string(UITOHEX, "[+] Free Buddy -> Merge: Left_Addr = 0x", target_frame->idx * FRAME_SIZE, 0); + // print_string(UITOHEX, " | Right_Addr = 0x", buddy_frame->idx * FRAME_SIZE, 0); + // print_string(UITOA, " | order = ", target_frame->order+1, 1); target_frame->order++; buddy_frame->order = -1; } diff --git a/kernel/src/ctx_switch.S b/kernel/src/ctx_switch.S new file mode 100644 index 000000000..b884cb665 --- /dev/null +++ b/kernel/src/ctx_switch.S @@ -0,0 +1,28 @@ +.section ".text" + +.globl cpu_switch_to +cpu_switch_to: + stp x19, x20, [x0, 16 * 1] + stp x21, x22, [x0, 16 * 2] + stp x23, x24, [x0, 16 * 3] + stp x25, x26, [x0, 16 * 4] + stp x27, x28, [x0, 16 * 5] + stp fp, lr, [x0, 16 * 6] + mov x9, sp + str x9, [x0, 16 * 7] + + ldp x19, x20, [x1, 16 * 1] + ldp x21, x22, [x1, 16 * 2] + ldp x23, x24, [x1, 16 * 3] + ldp x25, x26, [x1, 16 * 4] + ldp x27, x28, [x1, 16 * 5] + ldp fp, lr, [x1, 16 * 6] + ldr x9, [x1, 16 * 7] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 8e21778e4..14a8dedb7 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -9,7 +9,7 @@ #include #include -extern Thread *thread_head; +extern Thread *run_thread_head; int main(unsigned long dtb_base){ uart_init(); @@ -25,11 +25,12 @@ int main(unsigned long dtb_base){ enable_irq(); // DAIF set to 0b0000 - Thread *new_thread = thread_create(print_string); - print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 1); - print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); - print_string(UITOHEX, "thead_head->next: ", (unsigned long long )thread_head->list.next, 1); + // Thread *new_thread = thread_create(print_string); + // print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 1); + // print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); + // print_string(UITOHEX, "thead_head->next: ", (unsigned long long )run_thread_head->list.next, 1); + kernel_main(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/malloc.c b/kernel/src/malloc.c index 04375b897..93cd15383 100644 --- a/kernel/src/malloc.c +++ b/kernel/src/malloc.c @@ -78,9 +78,9 @@ void *chunk_alloc(unsigned int size){ FreeChunkList *chunk_list = &freechunk_list[level]; Chunk *chunk = (Chunk *)chunk_list->list.next; list_del(&chunk->list); - print_string(UITOHEX, "[*] Alloc Chunk -> Allocate Size: ", size, 0); - print_string(UITOHEX, " | Chunk Size: ", chunk_size[level], 0); - print_string(UITOA, " | Level: ", level, 1); + // print_string(UITOHEX, "[*] Alloc Chunk -> Allocate Size: ", size, 0); + // print_string(UITOHEX, " | Chunk Size: ", chunk_size[level], 0); + // print_string(UITOA, " | Level: ", level, 1); // print_freechunk_list(); return (void *)chunk; } @@ -105,7 +105,7 @@ void chunk_free(void *addr){ Chunk *chunk = (Chunk *)addr; list_add(&chunk->list, &freechunk_list[target_frame->chunk_level].list); - print_string(UITOHEX, "[*] Free Chunk -> the addr: 0x", (unsigned long long)addr, 1); + // print_string(UITOHEX, "[*] Free Chunk -> the addr: 0x", (unsigned long long)addr, 1); // print_freechunk_list(); } diff --git a/kernel/src/sched.S b/kernel/src/sched.S deleted file mode 100644 index 163e4e3f0..000000000 --- a/kernel/src/sched.S +++ /dev/null @@ -1,28 +0,0 @@ -#include "sched.h" -.globl cpu_switch_to - -cpu_switch_to: - stp x19, x20, [x0, 16 * 0] - stp x21, x22, [x0, 16 * 1] - stp x23, x24, [x0, 16 * 2] - stp x25, x26, [x0, 16 * 3] - stp x27, x28, [x0, 16 * 4] - stp fp, lr, [x0, 16 * 5] - mov x9, sp - str x9, [x0, 16 * 6] - - ldp x19, x20, [x1, 16 * 0] - ldp x21, x22, [x1, 16 * 1] - ldp x23, x24, [x1, 16 * 2] - ldp x25, x26, [x1, 16 * 3] - ldp x27, x28, [x1, 16 * 4] - ldp fp, lr, [x1, 16 * 5] - ldr x9, [x1, 16 * 6] - mov sp, x9 - msr tpidr_el1, x1 - ret - -.global get_current -get_current: - mrs x0, tpidr_el1 - ret \ No newline at end of file diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 6f7f8faa1..c20f00248 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -4,9 +4,11 @@ #include #include #include +#include +#include Thread *thread_pool; -Thread *thread_head; +Thread *run_thread_head; void init_thread_pool_and_head(){ thread_pool = (Thread*)kmalloc(sizeof(Thread) * MAX_THREAD); @@ -14,14 +16,14 @@ void init_thread_pool_and_head(){ for(unsigned int i = 0; i < MAX_THREAD; i++){ INIT_LIST_HEAD(&thread_pool[i].list); thread_pool[i].state = NOUSE; - thread_pool[i].id = i + 1; + thread_pool[i].id = i; thread_pool[i].ustack_addr = NULL; thread_pool[i].kstack_addr = NULL; } - thread_head = (Thread *)kmalloc(sizeof(Thread)); - memset((char *)thread_head, 0, sizeof(Thread)); - INIT_LIST_HEAD(&thread_head->list); + run_thread_head = (Thread *)kmalloc(sizeof(Thread)); + memset((char *)run_thread_head, 0, sizeof(Thread)); + INIT_LIST_HEAD(&run_thread_head->list); } Thread *thread_create(void(*func)()){ @@ -41,17 +43,77 @@ Thread *thread_create(void(*func)()){ new_thread->ctx.lr = (unsigned long)func; - list_add_tail(&new_thread->list, &thread_head->list); + list_add_tail(&new_thread->list, &run_thread_head->list); return new_thread; } void idle_thread(){ while(1){ + // kill zombie for(unsigned int i = 0; i < MAX_THREAD; i++){ - if(thread_pool[i].state == EXIT && thread_pool[i].id != 0){ - + if(thread_pool[i].state == EXIT){ + kfree(thread_pool[i].ustack_addr); + kfree(thread_pool[i].kstack_addr); + thread_pool[i].state = NOUSE; + INIT_LIST_HEAD(&thread_pool[i].list); } } + + // call schedule + schedule(); + } +} + +void schedule(){ + +} + + + +void kernel_main() { + disable_irq(); + /* the first thread that is fake thread */ + Thread prev; + + /* create the idle thread first */ + thread_create(idle_thread); + for(int i = 0; i < 5; i++){ + thread_create(foo); + } + + + Thread *next = (Thread *)run_thread_head->list.next; + // idle don't need in run thread + list_del(&next->list); + + print_run_thread(); + enable_irq(); + cpu_switch_to(&prev, next); +} + +void delay(){ + +} + +void foo(){ + for(int i = 0; i < 10; i++) { + // printf("Thread id: %d %d\n", get_current()->task_id, i); + // delay(1); + //schedule(); + } +} + +void print_run_thread(){ + struct list_head *pos; + unsigned int first = 1; + list_for_each(pos, &run_thread_head->list){ + Thread *tmp = (Thread *)pos; + if(first){ + print_string(UITOA, "run_thread: pid", tmp->id, 0); + first = 0; + } + else print_string(UITOA, " -> pid", tmp->id, 0); } + uart_puts("\n"); } \ No newline at end of file From 3fd59f7a368fb3ccef2f447169e114784be3564e Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 5 May 2022 23:29:17 +0800 Subject: [PATCH 09/62] Thread: can use schedule change task --- debug.gdb | 2 +- kernel/include/sched.h | 5 +-- kernel/src/sched.c | 81 ++++++++++++++++++++++++++++++------------ 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/debug.gdb b/debug.gdb index 9bdced160..9d1dbc122 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *0x841e8 +b *0x825d8 target remote :1234 \ No newline at end of file diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 8d8d9730a..ba578ebd8 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -39,15 +39,16 @@ typedef struct _Thread{ void *kstack_addr; }Thread; -extern struct Thread* get_current(); +extern Thread* get_current(); extern void cpu_switch_to(Thread* prev, Thread* next); void init_thread_pool_and_head(); Thread *thread_create(); +void kill_zombie(); void idle_thread(); void schedule(); void kernel_main(); -void delay(); +void delay(unsigned int); void foo(); void print_run_thread(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index c20f00248..44b437b7d 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -24,6 +24,7 @@ void init_thread_pool_and_head(){ run_thread_head = (Thread *)kmalloc(sizeof(Thread)); memset((char *)run_thread_head, 0, sizeof(Thread)); INIT_LIST_HEAD(&run_thread_head->list); + run_thread_head->id = -1; } Thread *thread_create(void(*func)()){ @@ -43,6 +44,11 @@ Thread *thread_create(void(*func)()){ new_thread->ctx.lr = (unsigned long)func; + print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); + uart_puts(" | "); + print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); + + list_add_tail(&new_thread->list, &run_thread_head->list); return new_thread; @@ -51,22 +57,39 @@ Thread *thread_create(void(*func)()){ void idle_thread(){ while(1){ // kill zombie - for(unsigned int i = 0; i < MAX_THREAD; i++){ - if(thread_pool[i].state == EXIT){ - kfree(thread_pool[i].ustack_addr); - kfree(thread_pool[i].kstack_addr); - thread_pool[i].state = NOUSE; - INIT_LIST_HEAD(&thread_pool[i].list); - } - } - + kill_zombie(); // call schedule schedule(); } } +void kill_zombie(){ + for(unsigned int i = 0; i < MAX_THREAD; i++){ + if(thread_pool[i].state == EXIT){ + kfree(thread_pool[i].ustack_addr); + kfree(thread_pool[i].kstack_addr); + INIT_LIST_HEAD(&thread_pool[i].list); + thread_pool[i].state = NOUSE; + thread_pool[i].ustack_addr = NULL; + thread_pool[i].kstack_addr = NULL; + } + } +} + void schedule(){ - + // [TODO] + disable_irq(); + Thread *curr_thread = get_current(); + Thread *next_thread = (Thread *)run_thread_head->list.next; + list_del(&next_thread->list); + list_add_tail(&next_thread->list, &run_thread_head->list); + + // print_string(UITOHEX, "curr: ", curr_thread->id, 0); + // uart_puts(" | "); + // print_string(UITOHEX, "next: ", next_thread->id, 1); + + enable_irq(); + cpu_switch_to(curr_thread, next_thread); } @@ -74,7 +97,7 @@ void schedule(){ void kernel_main() { disable_irq(); /* the first thread that is fake thread */ - Thread prev; + Thread curr_thread; /* create the idle thread first */ thread_create(idle_thread); @@ -82,26 +105,40 @@ void kernel_main() { thread_create(foo); } - - Thread *next = (Thread *)run_thread_head->list.next; + Thread *next_thread = (Thread *)run_thread_head->list.next; // idle don't need in run thread - list_del(&next->list); + list_del(&next_thread->list); print_run_thread(); enable_irq(); - cpu_switch_to(&prev, next); + cpu_switch_to(&curr_thread, next_thread); } -void delay(){ - +void delay(unsigned int time){ + unsigned long long system_timer = 0; + unsigned long long frq = 0; + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + :"=r"(system_timer), "=r"(frq) + ); + unsigned long long expired_time = system_timer + frq * time; + while(system_timer <= expired_time){ + asm volatile( + "mrs %0, cntpct_el0\n\t" + :"=r"(system_timer) + ); + } } void foo(){ - for(int i = 0; i < 10; i++) { - // printf("Thread id: %d %d\n", get_current()->task_id, i); - // delay(1); - //schedule(); - } + for(int i = 0; i < 10; i++) { + print_string(UITOA, "Thread id: ", get_current()->id, 0); + print_string(UITOA, " ", i, 1); + // printf("Thread id: %d %d\n", get_current()->task_id, i); + delay(1); + schedule(); + } } void print_run_thread(){ From a75e6ed8ef3c628c8cf967d1521ca993645ce577 Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 7 May 2022 00:24:17 +0800 Subject: [PATCH 10/62] syscall: In development~~ --- kernel/boot/start.S | 31 ++++++++------ kernel/include/exc.h | 14 ++++--- kernel/include/syscall.h | 26 ++++++++++++ kernel/include/user_syscall.h | 26 ++++++++++++ kernel/src/allocator.c | 2 +- kernel/src/exc.c | 76 +++++++++++++++++++++++++---------- kernel/src/main.c | 2 +- kernel/src/malloc.c | 1 - kernel/src/sched.c | 1 - kernel/src/syscall.c | 67 ++++++++++++++++++++++++++++++ kernel/src/user_syscall.S | 51 +++++++++++++++++++++++ 11 files changed, 253 insertions(+), 44 deletions(-) create mode 100644 kernel/include/syscall.h create mode 100644 kernel/include/user_syscall.h create mode 100644 kernel/src/syscall.c create mode 100644 kernel/src/user_syscall.S diff --git a/kernel/boot/start.S b/kernel/boot/start.S index aae381638..9efc693be 100644 --- a/kernel/boot/start.S +++ b/kernel/boot/start.S @@ -39,7 +39,7 @@ _start: // save general registers to stack .macro save_all - sub sp, sp, 32 * 9 + sub sp, sp, 16 * 17 stp x0, x1, [sp ,16 * 0] stp x2, x3, [sp ,16 * 1] stp x4, x5, [sp ,16 * 2] @@ -55,18 +55,20 @@ _start: stp x24, x25, [sp ,16 * 12] stp x26, x27, [sp ,16 * 13] stp x28, x29, [sp ,16 * 14] - str x30, [sp, 16 * 15] - - mrs x5, spsr_el1 - mrs x6, elr_el1 - stp x5, x6, [sp ,16 * 16] + mrs x9, sp_el0 + stp x30, x9, [sp, 16 * 15] + mrs x9, spsr_el1 + mrs x10, elr_el1 + stp x9, x10, [sp ,16 * 16] .endm // load general registers from stack .macro load_all - ldp x5, x6, [sp ,16 * 16] - msr spsr_el1, x5 - msr elr_el1, x6 + ldp x9, x10, [sp , 16* 15] + msr sp_el0, x10 + ldp x9, x10, [sp ,16 * 16] + msr spsr_el1, x9 + msr elr_el1, x10 ldp x0, x1, [sp ,16 * 0] ldp x2, x3, [sp ,16 * 1] @@ -85,14 +87,17 @@ _start: ldp x28, x29, [sp ,16 * 14] ldr x30, [sp, 16 * 15] - add sp, sp, 32 * 9 + add sp, sp, 16 * 17 .endm sync_exception_entry: save_all - mrs x1, esr_el1 - mrs x2, elr_el1 - mrs x3, spsr_el1 + mrs x0, esr_el1 + mrs x1, elr_el1 + mrs x2, spsr_el1 + + // trap frame + mov x3, sp bl exception_handler load_all eret diff --git a/kernel/include/exc.h b/kernel/include/exc.h index 5798ce32e..5bf15b6f3 100644 --- a/kernel/include/exc.h +++ b/kernel/include/exc.h @@ -1,9 +1,13 @@ #ifndef EXC_H_ #define EXC_H_ +#include -void exception_handler(unsigned long long, - unsigned long long, - unsigned long long, - unsigned long long); +void exception_handler( unsigned long long, + unsigned long long, + unsigned long long, + TrapFrame *); + +void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame); + +#endif -#endif \ No newline at end of file diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h new file mode 100644 index 000000000..0d88745e1 --- /dev/null +++ b/kernel/include/syscall.h @@ -0,0 +1,26 @@ +#ifndef SYSCALL_H_ +#define SYSCALL_H_ + +typedef struct _TrapFrame{ + unsigned long long x[31]; + unsigned long long sp_el0; + unsigned long long elr_el1; + unsigned long long spsr_el1; +}TrapFrame; + + +void sys_getpid(TrapFrame *); +void sys_uart_read(TrapFrame *); +void sys_uart_write(TrapFrame *); +void sys_exec(TrapFrame *); +void sys_fork(TrapFrame *); +void sys_exit(TrapFrame *); +void sys_mbox_call(TrapFrame *); +void sys_kill(TrapFrame *); + +int do_getpid(); +void do_fork(); +void do_exit(); +void do_kill(); + +#endif \ No newline at end of file diff --git a/kernel/include/user_syscall.h b/kernel/include/user_syscall.h new file mode 100644 index 000000000..27c9dcb87 --- /dev/null +++ b/kernel/include/user_syscall.h @@ -0,0 +1,26 @@ +#ifndef USER_SYSCALL_H_ +#define USER_SYSCALL_H_ + +#define GET_PID 0 +#define UART_READ 1 +#define UART_WRITE 2 +#define EXEC 3 +#define FORK 4 +#define EXIT 5 +#define MBOX_CALL 6 +#define KILL 7 + +#endif + +#ifndef __ASSEMBLY__ + +extern int getpid(); +extern unsigned int uart_read(char buf[], unsigned int size); +extern unsigned int uart_write(const char buf[], unsigned int size); +extern int exec(const char *name, char *const argv[]); +extern int fork(); +extern void exit(); +extern int mbox_call(unsigned char ch, unsigned int *mbox); +extern void kill(int pid); + +#endif \ No newline at end of file diff --git a/kernel/src/allocator.c b/kernel/src/allocator.c index 28c95bc20..a6435b824 100644 --- a/kernel/src/allocator.c +++ b/kernel/src/allocator.c @@ -125,7 +125,7 @@ void buddy_init(){ list_add_tail(&frames[idx].list, &buddy_list[frames[idx].order].list); } - print_buddy_list(); + // print_buddy_list(); } diff --git a/kernel/src/exc.c b/kernel/src/exc.c index 03ee1db92..7dd9ad679 100644 --- a/kernel/src/exc.c +++ b/kernel/src/exc.c @@ -1,31 +1,63 @@ #include #include #include +#include +#include -void exception_handler(unsigned long long what, - unsigned long long esr, - unsigned long long elr, - unsigned long long spsr){ +void exception_handler( unsigned long long esr, + unsigned long long elr, + unsigned long long spsr, + TrapFrame *trapFrame){ - char buf[10]; - uart_puts("---------Exception Handler---------\n"); - uart_puts("[*] Exception type: "); - uart_puts("Synchronous"); + /* + * EC, bits [31:26] -> Exception Class. Indicates the reason for the exception that this register holds information about. + * ec = 0b010101: SVC instruction execution in AArch64 state. + * https://developer.arm.com/documentation/ddi0601/2021-12/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1-?lang=en + */ + unsigned int ec = esr >> 26; + if(ec == 0b010101){ + unsigned int syscall_id = trapFrame->x[8]; + syscall_handler(syscall_id, trapFrame); + } + else{ + uart_puts("---------Exception Handler---------\n[*] Exception type: Synchronous\n"); + print_string(UITOHEX, "[*] spsr_el1: 0x", spsr, 1); + print_string(UITOHEX, "[*] elr_el1: 0x", elr, 1); + print_string(UITOHEX, "[*] esr_el1: 0x", esr, 1); + } - uart_puts("\n"); - uart_puts("[*] spsr_el1: 0x"); - uitohex(buf, spsr); - uart_puts(buf); - uart_puts("\n"); +} - uart_puts("[*] elr_el1: 0x"); - uitohex(buf, elr); - uart_puts(buf); - uart_puts("\n"); - uart_puts("[*] esr_el1: 0x"); - uitohex(buf, esr); - uart_puts(buf); - uart_puts("\n"); +void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ + switch (syscall_id) + { + case GET_PID: + sys_getpid(trapFrame); + break; + case UART_READ: + sys_uart_read(trapFrame); + break; + case UART_WRITE: + sys_uart_write(trapFrame); + break; + case EXEC: + sys_exec(trapFrame); + break; + case FORK: + sys_fork(trapFrame); + break; + case EXIT: + sys_exit(trapFrame); + break; + case MBOX_CALL: + sys_mbox_call(trapFrame); + break; + case KILL: + sys_kill(trapFrame); + break; -} \ No newline at end of file + default: + break; + } +} diff --git a/kernel/src/main.c b/kernel/src/main.c index 14a8dedb7..397e77e06 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -30,7 +30,7 @@ int main(unsigned long dtb_base){ // print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); // print_string(UITOHEX, "thead_head->next: ", (unsigned long long )run_thread_head->list.next, 1); - kernel_main(); + //kernel_main(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/malloc.c b/kernel/src/malloc.c index 93cd15383..3527f02a1 100644 --- a/kernel/src/malloc.c +++ b/kernel/src/malloc.c @@ -6,7 +6,6 @@ extern Frame *frames; extern Buddy *buddy_list; FreeChunkList *freechunk_list; -// FreeChunkList freechunk_list[MAX_CHUNK_SIZE]; /* define 11 level common chunk size */ unsigned int chunk_size[] = {0x10, 0x20, 0x30, 0x40, 0x60, 0x80, diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 44b437b7d..8395dbe80 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -77,7 +77,6 @@ void kill_zombie(){ } void schedule(){ - // [TODO] disable_irq(); Thread *curr_thread = get_current(); Thread *next_thread = (Thread *)run_thread_head->list.next; diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c new file mode 100644 index 000000000..fc05a9ca7 --- /dev/null +++ b/kernel/src/syscall.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +extern Thread *run_thread_head; + +/* + * Return value is x0 + * https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard + */ + + +/* Get current process’s id. */ +void sys_getpid(TrapFrame *trapFrame){ + int pid = do_getpid(); + trapFrame->x[0] = pid; +} +int do_getpid(){ + return get_current()->id; +} + +/* Return the number of bytes read by reading size byte into the user-supplied buffer buf. */ +void sys_uart_read(TrapFrame *trapFrame){ + char *buf = (char *)trapFrame->x[0]; + unsigned int size = trapFrame->x[1]; + int idx = readnbyte(buf, size); + trapFrame->x[0] = idx; +} + +/* Return the number of bytes written after writing size byte from the user-supplied buffer buf. */ +void sys_uart_write(TrapFrame *trapFrame){ + const char *buf = (char *)trapFrame->x[0]; + unsigned int size = trapFrame->x[1]; + for(unsigned int i = 0; i < size; i++){ + uart_putc(buf[i]); + } + trapFrame->x[0] = size; +} +void sys_exec(TrapFrame *trapFrame){ + +} +void sys_fork(TrapFrame *trapFrame){ + +} +void sys_exit(TrapFrame *trapFrame){ + do_exit(); +} +/* Terminate the current process. */ +void do_exit(){ + Thread *exit_thread = get_current(); + exit_thread->state = EXIT; + list_del(&exit_thread->list); + Thread *next_thread = (Thread *)run_thread_head->list.next; + + enable_irq(); + cpu_switch_to(exit_thread, next_thread); +} + +void sys_mbox_call(TrapFrame *trapFrame){ + +} +void sys_kill(TrapFrame *trapFrame){ + +} \ No newline at end of file diff --git a/kernel/src/user_syscall.S b/kernel/src/user_syscall.S new file mode 100644 index 000000000..83fbfe3a2 --- /dev/null +++ b/kernel/src/user_syscall.S @@ -0,0 +1,51 @@ +#define __ASSEMBLY__ +#include "user_syscall.h" +.section ".text" + +.global getpid +getpid: + mov x8, GET_PID + svc #0 + ret + +.global uart_read +uart_read: + mov x8, UART_READ + svc #0 + ret + +.global uart_write +uart_write: + mov x8, UART_WRITE + svc #0 + ret + +.global exec +exec: + mov x8, EXEC + svc #0 + ret + +.global fork +fork: + mov x8, FORK + svc #0 + ret + +.global exit +exit: + mov x8, EXIT + svc #0 + ret + +.global mbox_call +mbox_call: + mov x8, MBOX_CALL + svc #0 + ret + +.global kill +kill: + mov x8, KILL + svc #0 + ret \ No newline at end of file From ea7bbc44935a2cb7c13592fe9a0342b7e381ee21 Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 7 May 2022 23:22:55 +0800 Subject: [PATCH 11/62] syscall: NO TEST QQ --- kernel/include/cpio.h | 3 ++- kernel/include/sched.h | 2 ++ kernel/include/syscall.h | 3 +++ kernel/src/cpio.c | 23 +++++++++++++++++-- kernel/src/main.c | 2 +- kernel/src/sched.c | 44 +++++++++++++++++++++++------------- kernel/src/syscall.c | 49 ++++++++++++++++++++++++++++++++++++---- 7 files changed, 102 insertions(+), 24 deletions(-) diff --git a/kernel/include/cpio.h b/kernel/include/cpio.h index cb1b2cd26..a20094107 100644 --- a/kernel/include/cpio.h +++ b/kernel/include/cpio.h @@ -43,7 +43,8 @@ unsigned int padding(unsigned int); void ls(); void cat(char [MAX_SIZE]); void run(unsigned long); -unsigned long findDataAddr(char [MAX_SIZE]); +unsigned long findDataAddr(const char *); +file_info cpio_find_file_info(const char *thefilename); void parse_cpio_header(cpio_newc_header *, file_info *); diff --git a/kernel/include/sched.h b/kernel/include/sched.h index ba578ebd8..7120d9202 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -37,6 +37,8 @@ typedef struct _Thread{ int id; void *ustack_addr; void *kstack_addr; + void *code_addr; // use in exec + unsigned int code_size; // use in exec }Thread; extern Thread* get_current(); diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 0d88745e1..3965da3ae 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -1,5 +1,6 @@ #ifndef SYSCALL_H_ #define SYSCALL_H_ +#include typedef struct _TrapFrame{ unsigned long long x[31]; @@ -19,6 +20,8 @@ void sys_mbox_call(TrapFrame *); void sys_kill(TrapFrame *); int do_getpid(); +int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); +void *load_program(file_info *fileInfo); void do_fork(); void do_exit(); void do_kill(); diff --git a/kernel/src/cpio.c b/kernel/src/cpio.c index 2ab853e34..e5c8275d4 100644 --- a/kernel/src/cpio.c +++ b/kernel/src/cpio.c @@ -3,6 +3,7 @@ #include #include #include +#include /* The pathname is followed by NUL bytes so that the total size of the fixed @@ -56,7 +57,7 @@ void cat(char *thefilename) { } -unsigned long findDataAddr(char *thefilename) { +unsigned long findDataAddr(const char *thefilename) { cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; while(1){ file_info info; @@ -72,8 +73,26 @@ unsigned long findDataAddr(char *thefilename) { return 0; } +file_info cpio_find_file_info(const char *thefilename) { + cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + file_info info; + while(1){ + parse_cpio_header(header, &info); + + if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + else if(strncmp(info.filename, thefilename, info.filename_size) == 0){ + return info; + } + + header = (cpio_newc_header *)(info.data + padding(info.datasize)); + } + info.filename = 0; + return info; + +} + void run(unsigned long runAddr){ - unsigned long *user_stack = (unsigned long*)simple_malloc(0x1000); + unsigned long *user_stack = (unsigned long *)kmalloc(0x1000); set_period_timer_irq(); asm volatile( "mov x0, 0x0\n\t" diff --git a/kernel/src/main.c b/kernel/src/main.c index 397e77e06..14a8dedb7 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -30,7 +30,7 @@ int main(unsigned long dtb_base){ // print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); // print_string(UITOHEX, "thead_head->next: ", (unsigned long long )run_thread_head->list.next, 1); - //kernel_main(); + kernel_main(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 8395dbe80..0da255619 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -6,6 +6,7 @@ #include #include #include +#include Thread *thread_pool; Thread *run_thread_head; @@ -19,6 +20,8 @@ void init_thread_pool_and_head(){ thread_pool[i].id = i; thread_pool[i].ustack_addr = NULL; thread_pool[i].kstack_addr = NULL; + thread_pool[i].code_addr = NULL; + thread_pool[i].code_size = 0; } run_thread_head = (Thread *)kmalloc(sizeof(Thread)); @@ -64,24 +67,34 @@ void idle_thread(){ } void kill_zombie(){ - for(unsigned int i = 0; i < MAX_THREAD; i++){ - if(thread_pool[i].state == EXIT){ - kfree(thread_pool[i].ustack_addr); - kfree(thread_pool[i].kstack_addr); - INIT_LIST_HEAD(&thread_pool[i].list); - thread_pool[i].state = NOUSE; - thread_pool[i].ustack_addr = NULL; - thread_pool[i].kstack_addr = NULL; + disable_irq(); + struct list_head *pos; + list_for_each(pos, &run_thread_head->list){ + Thread *tmp = (Thread *)pos; + if(tmp->state == EXIT){ + kfree(tmp->ustack_addr); + kfree(tmp->kstack_addr); + tmp->state = NOUSE; + tmp->ustack_addr = NULL; + tmp->kstack_addr = NULL; + if(tmp->code_addr != NULL){ + kfree(tmp->code_addr); + } + tmp->code_addr = NULL; + tmp->code_size = 0; + list_del_entry(&tmp->list); } } + enable_irq(); } void schedule(){ disable_irq(); Thread *curr_thread = get_current(); - Thread *next_thread = (Thread *)run_thread_head->list.next; - list_del(&next_thread->list); - list_add_tail(&next_thread->list, &run_thread_head->list); + Thread *next_thread = curr_thread; + do{ + next_thread = (Thread *)next_thread->list.next; + }while(list_is_head(&next_thread->list, &run_thread_head->list) || next_thread->state == EXIT); // print_string(UITOHEX, "curr: ", curr_thread->id, 0); // uart_puts(" | "); @@ -98,15 +111,13 @@ void kernel_main() { /* the first thread that is fake thread */ Thread curr_thread; - /* create the idle thread first */ thread_create(idle_thread); + /* create the idle thread first */ for(int i = 0; i < 5; i++){ thread_create(foo); } Thread *next_thread = (Thread *)run_thread_head->list.next; - // idle don't need in run thread - list_del(&next_thread->list); print_run_thread(); enable_irq(); @@ -121,7 +132,7 @@ void delay(unsigned int time){ "mrs %1, cntfrq_el0\n\t" :"=r"(system_timer), "=r"(frq) ); - unsigned long long expired_time = system_timer + frq * time; + unsigned long long expired_time = system_timer + 1000; while(system_timer <= expired_time){ asm volatile( "mrs %0, cntpct_el0\n\t" @@ -131,13 +142,14 @@ void delay(unsigned int time){ } void foo(){ - for(int i = 0; i < 10; i++) { + for(int i = 0; i < 2; i++) { print_string(UITOA, "Thread id: ", get_current()->id, 0); print_string(UITOA, " ", i, 1); // printf("Thread id: %d %d\n", get_current()->task_id, i); delay(1); schedule(); } + do_exit(); } void print_run_thread(){ diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index fc05a9ca7..9ef4ba38b 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include extern Thread *run_thread_head; @@ -40,23 +43,61 @@ void sys_uart_write(TrapFrame *trapFrame){ trapFrame->x[0] = size; } void sys_exec(TrapFrame *trapFrame){ + const char *name = (const char *)trapFrame->x[0]; + char **const argv = (char **const)trapFrame->x[1]; + int success = do_exec(trapFrame, name, argv); + trapFrame->x[0] = success; +} +int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ + /* check if the file info exist */ + file_info fileInfo = cpio_find_file_info(name); + if(fileInfo.filename == NULL) return -1; + + /* check if the file can create in new memory */ + void *thread_code_addr = load_program(&fileInfo); + if(thread_code_addr == NULL) return -1; + + /* current thread will change the pc to new code addr */ + Thread *curr_thread = get_current(); + curr_thread->code_addr = thread_code_addr; + curr_thread->code_size = fileInfo.filename_size; + + trapFrame->elr_el1 = (unsigned long long)curr_thread->code_addr; + trapFrame->sp_el0 = (unsigned long long)curr_thread->ustack_addr + STACT_SIZE; + return 0; } -void sys_fork(TrapFrame *trapFrame){ +void *load_program(file_info *fileInfo){ + void *thread_code_addr = kmalloc(fileInfo->datasize); + if(thread_code_addr == NULL) return NULL; + memcpy(thread_code_addr, fileInfo->data, fileInfo->filename_size); + + return thread_code_addr; } + + +void sys_fork(TrapFrame *trapFrame){ + do_fork(); +} +void do_fork(){ + // Thread *new_thread; + // TrapFrame *curr_tf, *new_tf; + // void *start; +} + void sys_exit(TrapFrame *trapFrame){ do_exit(); } /* Terminate the current process. */ void do_exit(){ + disable_irq(); Thread *exit_thread = get_current(); exit_thread->state = EXIT; - list_del(&exit_thread->list); - Thread *next_thread = (Thread *)run_thread_head->list.next; enable_irq(); - cpu_switch_to(exit_thread, next_thread); + schedule(); + // cpu_switch_to(exit_thread, next_thread); } void sys_mbox_call(TrapFrame *trapFrame){ From a988504bf1ed254a5c337cedf1ecc9da3ff535d0 Mon Sep 17 00:00:00 2001 From: Fan Date: Mon, 9 May 2022 02:13:03 +0800 Subject: [PATCH 12/62] kernel_exec: run a user program with a thread --- debug.gdb | 2 +- kernel/include/sched.h | 2 +- kernel/include/shell.h | 2 +- kernel/include/syscall.h | 2 ++ kernel/src/main.c | 2 +- kernel/src/sched.c | 8 ++++---- kernel/src/shell.c | 20 ++++++++------------ kernel/src/syscall.c | 39 ++++++++++++++++++++++++++++++++++++--- 8 files changed, 54 insertions(+), 23 deletions(-) diff --git a/debug.gdb b/debug.gdb index 9d1dbc122..8ac2590c8 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *0x825d8 +b *0x811e8 target remote :1234 \ No newline at end of file diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 7120d9202..a2c6464e9 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -4,7 +4,7 @@ #include #define MAX_THREAD 0x10 -#define STACT_SIZE 0x1000 +#define STACK_SIZE 0x1000 enum thread_state{ NOUSE, diff --git a/kernel/include/shell.h b/kernel/include/shell.h index 8e7e91454..f22dd1728 100644 --- a/kernel/include/shell.h +++ b/kernel/include/shell.h @@ -28,7 +28,7 @@ void ShellLoop(); void Ls(); void Cat(char [MAX_SIZE]); -void Run(char [MAX_SIZE]); +void Exec(char [MAX_SIZE]); void SetTimeOut(char [MAX_SIZE]); void TestTimeOut(char [MAX_SIZE]); diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 3965da3ae..9b6aa0612 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -26,4 +26,6 @@ void do_fork(); void do_exit(); void do_kill(); +int kernel_exec(char *name); + #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 14a8dedb7..f6b292c19 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -30,7 +30,7 @@ int main(unsigned long dtb_base){ // print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); // print_string(UITOHEX, "thead_head->next: ", (unsigned long long )run_thread_head->list.next, 1); - kernel_main(); + // kernel_main(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 0da255619..2513ba27a 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -40,10 +40,10 @@ Thread *thread_create(void(*func)()){ Thread *new_thread = &thread_pool[idx]; new_thread->state = WAIT; - new_thread->ustack_addr = kmalloc(STACT_SIZE); - new_thread->kstack_addr = kmalloc(STACT_SIZE); - new_thread->ctx.fp = (unsigned long)new_thread->kstack_addr + STACT_SIZE; - new_thread->ctx.sp = (unsigned long)new_thread->kstack_addr + STACT_SIZE; + new_thread->ustack_addr = kmalloc(STACK_SIZE); + new_thread->kstack_addr = kmalloc(STACK_SIZE); + new_thread->ctx.fp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; + new_thread->ctx.sp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; new_thread->ctx.lr = (unsigned long)func; diff --git a/kernel/src/shell.c b/kernel/src/shell.c index 789c75081..bb51db781 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -7,6 +7,7 @@ #include #include #include +#include /* print welcome message*/ void PrintWelcome(){ @@ -25,7 +26,7 @@ void PrintHelp(){ uart_puts("memory : print memory info\n"); uart_puts("reboot : reboot the device\n"); uart_puts("ls : list directory contents\n"); - uart_puts("cat : concatenate files and print on the standard output\n"); + uart_puts("exec : concatenate files and print on the standard output\n"); uart_puts("setTimeout : set timeout for read\n"); uart_puts("test_timeout : test timeout for read\n"); } @@ -105,23 +106,18 @@ void Cat(char buf[MAX_SIZE]){ cat(buf); } -void Run(char buf[MAX_SIZE]){ +void Exec(char buf[MAX_SIZE]){ uart_puts("Filename: "); unsigned int size = readline(buf, MAX_SIZE); if (size == 0){ return; } - unsigned long fileDataAddr = findDataAddr(buf); - uitohex(buf, (unsigned int)fileDataAddr); - if(!fileDataAddr){ - uart_puts("[x] Failed to find file data address\n"); + int state = kernel_exec(buf); + + if(state == -1){ + uart_puts("[x] Failed to exec the file\n"); return; } - - uart_puts("[*] File data address: 0x"); - uart_puts(buf); - uart_puts("\n"); - run(fileDataAddr); } void SetTimeOut(char buf[MAX_SIZE]){ @@ -169,7 +165,7 @@ void ShellLoop(){ else if(strcmp("bootimg", buf) == 0) Bootimg(buf); else if(strcmp("ls", buf) == 0) Ls(); else if(strcmp("cat", buf) == 0) Cat(buf); - else if(strcmp("run", buf) == 0) Run(buf); + else if(strcmp("exec", buf) == 0) Exec(buf); else if(strncmp("setTimeout", buf, strlen("setTimeout")) == 0) SetTimeOut(buf); else if(strcmp("test_timeout", buf) == 0) TestTimeOut(buf); else PrintUnknown(buf); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 9ef4ba38b..f388926af 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -61,17 +61,50 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ Thread *curr_thread = get_current(); curr_thread->code_addr = thread_code_addr; curr_thread->code_size = fileInfo.filename_size; - + trapFrame->elr_el1 = (unsigned long long)curr_thread->code_addr; - trapFrame->sp_el0 = (unsigned long long)curr_thread->ustack_addr + STACT_SIZE; + trapFrame->sp_el0 = (unsigned long long)curr_thread->ustack_addr + STACK_SIZE; return 0; } + +int kernel_exec(char *name){ + /* check if the file info exist */ + file_info fileInfo = cpio_find_file_info(name); + if(fileInfo.filename == NULL) return -1; + + /* check if the file can create in new memory */ + void *thread_code_addr = load_program(&fileInfo); + if(thread_code_addr == NULL) return -1; + + Thread *new_thread = thread_create(thread_code_addr); + new_thread->code_addr = thread_code_addr; + new_thread->code_size = fileInfo.filename_size; + print_string(UITOHEX, "new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); + + set_period_timer_irq(); + asm volatile( + "mov x0, 0x0\n\t" + "msr spsr_el1, x0\n\t" + "msr tpidr_el1, %0\n\t" + "msr elr_el1, %1\n\t" + "msr sp_el0, %2\n\t" + "mov sp, %3\n\t" + "eret\n\t" + ::"r"(new_thread), + "r"(new_thread->code_addr), + "r"(new_thread->ustack_addr + STACK_SIZE), + "r"(new_thread->kstack_addr + STACK_SIZE) + : "x0" + ); + return 0; +} + void *load_program(file_info *fileInfo){ void *thread_code_addr = kmalloc(fileInfo->datasize); if(thread_code_addr == NULL) return NULL; - memcpy(thread_code_addr, fileInfo->data, fileInfo->filename_size); + memcpy(thread_code_addr, fileInfo->data, fileInfo->datasize); return thread_code_addr; } From 460c23592a54d7dc575ac7aef9e0ca866a7149ce Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 10 May 2022 01:07:41 +0800 Subject: [PATCH 13/62] syscall: do_fork and exec a sample user program --- .gitignore | 4 ++++ kernel/Makefile | 3 +++ kernel/boot/start.S | 5 +++++ kernel/include/sched.h | 2 +- kernel/include/syscall.h | 6 +++-- kernel/initramfs.cpio | Bin 4096 -> 25600 bytes kernel/src/ctx_switch.S | 3 ++- kernel/src/sched.c | 2 ++ kernel/src/syscall.c | 47 ++++++++++++++++++++++++++++++++------- 9 files changed, 60 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 8f131326c..0b6edfe2f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,10 @@ bootloader/src/*.elf bootloader/src/*.img bootloader/src/*.o bootloader/boot/*.o + + +user/* + peda* .gdb* diff --git a/kernel/Makefile b/kernel/Makefile index 3dd51d235..98d00e96e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -40,3 +40,6 @@ run: pty: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial pty -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +cpio: + find . | cpio -o -H newc > ../initramfs.cpio diff --git a/kernel/boot/start.S b/kernel/boot/start.S index 9efc693be..60d5eabb1 100644 --- a/kernel/boot/start.S +++ b/kernel/boot/start.S @@ -90,6 +90,11 @@ _start: add sp, sp, 16 * 17 .endm +.global after_fork +after_fork: + load_all + eret + sync_exception_entry: save_all mrs x0, esr_el1 diff --git a/kernel/include/sched.h b/kernel/include/sched.h index a2c6464e9..47aaa0fbe 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -25,7 +25,7 @@ typedef struct _cpu_context{ unsigned long x27; unsigned long x28; unsigned long fp; // frame pointer, stack base address - unsigned long lr; // retrun address + unsigned long lr; // retrun address after context switch unsigned long sp; // stact pointer }CpuContext; diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 9b6aa0612..b02f9404d 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -14,7 +14,7 @@ void sys_getpid(TrapFrame *); void sys_uart_read(TrapFrame *); void sys_uart_write(TrapFrame *); void sys_exec(TrapFrame *); -void sys_fork(TrapFrame *); +int sys_fork(TrapFrame *); void sys_exit(TrapFrame *); void sys_mbox_call(TrapFrame *); void sys_kill(TrapFrame *); @@ -22,10 +22,12 @@ void sys_kill(TrapFrame *); int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); void *load_program(file_info *fileInfo); -void do_fork(); +int do_fork(TrapFrame *trapFrame); void do_exit(); void do_kill(); int kernel_exec(char *name); +extern void after_fork(); + #endif \ No newline at end of file diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index 649943150be956dd76de98ea7c4010066590f923..0ff7b857f1fccc6f4c4737e2f82e106e72ea046e 100644 GIT binary patch literal 25600 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 delta 435 zcmZoT!Pua{XJ8J5h6aY_CWbDKmJ|7u1(1XRrcC6No}I4I8gUaY1!BqvuO3u~6S@uqSFAdExz+fA zt^&%Lg5)g>-CQQyaZAY=yI2@mK>cHAflaHWC0G1F1oGXO!uB?cCTiHr;k zSL8()KCb@%KLjW!f)ZXX=uX4o89^RBHi*+E)+z%XBwb K1IZ3ZSOoyj8)_~9 diff --git a/kernel/src/ctx_switch.S b/kernel/src/ctx_switch.S index b884cb665..aa265491a 100644 --- a/kernel/src/ctx_switch.S +++ b/kernel/src/ctx_switch.S @@ -25,4 +25,5 @@ cpu_switch_to: .global get_current get_current: mrs x0, tpidr_el1 - ret \ No newline at end of file + ret + diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 2513ba27a..0495e12fe 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -28,6 +28,8 @@ void init_thread_pool_and_head(){ memset((char *)run_thread_head, 0, sizeof(Thread)); INIT_LIST_HEAD(&run_thread_head->list); run_thread_head->id = -1; + + thread_create(idle_thread); } Thread *thread_create(void(*func)()){ diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index f388926af..ee0c67457 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -105,18 +105,50 @@ void *load_program(file_info *fileInfo){ if(thread_code_addr == NULL) return NULL; memcpy(thread_code_addr, fileInfo->data, fileInfo->datasize); - + return thread_code_addr; } -void sys_fork(TrapFrame *trapFrame){ - do_fork(); +int sys_fork(TrapFrame *trapFrame){ + return do_fork(trapFrame); } -void do_fork(){ - // Thread *new_thread; - // TrapFrame *curr_tf, *new_tf; - // void *start; + +int do_fork(TrapFrame *trapFrame){ + Thread *curr_thread = get_current(); + + void *thread_code_addr = kmalloc(curr_thread->code_size); + if(thread_code_addr == NULL) return -1; + + Thread *new_thread = thread_create(curr_thread->code_addr); + new_thread->code_addr = thread_code_addr; + new_thread->code_size = curr_thread->code_size; + + + /* copy the code */ + memcpy((char *)new_thread->code_addr, (char *)curr_thread->code_addr, new_thread->code_size); + /* copy user stack */ + memcpy((char *)new_thread->ustack_addr, (char *)curr_thread->ustack_addr, STACK_SIZE); + /* copy trap frame (kernel stack) */ + TrapFrame *new_trapFrame = (TrapFrame *)((char *)new_thread->kstack_addr - sizeof(TrapFrame)); + memcpy((char*)new_trapFrame, (char *)trapFrame, sizeof(TrapFrame)); + /* copy context */ + memcpy((char *)&new_thread->ctx, (char *)&curr_thread->ctx, sizeof(CpuContext)); + + /* return pid = 0 (child) */ + new_trapFrame->x[0] = 0; + /* set new code return to after eret */ + new_trapFrame->elr_el1 = (unsigned long)new_thread->code_addr + + (trapFrame->elr_el1 - (unsigned long)curr_thread->code_addr); + /* set new code return to after eret */ + new_trapFrame->sp_el0 = ((unsigned long)new_thread->ustack_addr + STACK_SIZE) - + (((unsigned long)curr_thread->ustack_addr + STACK_SIZE) - trapFrame->sp_el0); + + /* after context switch, child proc will load all reg from kernel stack, and return to el0 */ + new_thread->ctx.lr = (unsigned long)after_fork; + new_thread->ctx.sp = (unsigned long)new_trapFrame; + + return 1; } void sys_exit(TrapFrame *trapFrame){ @@ -130,7 +162,6 @@ void do_exit(){ enable_irq(); schedule(); - // cpu_switch_to(exit_thread, next_thread); } void sys_mbox_call(TrapFrame *trapFrame){ From 0a7488aea022073abe376460371ebc72ee35b2d6 Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 10 May 2022 19:33:41 +0800 Subject: [PATCH 14/62] syscall: getpid, uart_read, uart_write, exec -> ok --- debug.gdb | 2 +- kernel/Makefile | 9 +++++++-- kernel/boot/start.S | 11 +++++------ kernel/include/read.h | 2 ++ kernel/include/syscall.h | 2 +- kernel/initramfs.cpio | Bin 25600 -> 26112 bytes kernel/src/irq.c | 3 +++ kernel/src/main.c | 7 ++----- kernel/src/read.c | 12 ++++++++++++ kernel/src/sched.c | 5 +++-- kernel/src/syscall.c | 39 ++++++++++++++++++++++++++++----------- kernel/src/timer.c | 6 ++++-- kernel/src/uart.c | 2 +- 13 files changed, 69 insertions(+), 31 deletions(-) diff --git a/debug.gdb b/debug.gdb index 8ac2590c8..e630ff794 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *0x811e8 +b *0x81920 target remote :1234 \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile index 98d00e96e..b0e763cca 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -41,5 +41,10 @@ run: pty: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial pty -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -cpio: - find . | cpio -o -H newc > ../initramfs.cpio +user: + cd ../user;\ + make;\ + cp kernel8.img ./initramfs/kernel8.img;\ + cd initramfs;\find . | cpio -o -H newc > ../initramfs.cpio;\ + cd ..;\ + cp ./initramfs.cpio ../kernel/initramfs.cpio diff --git a/kernel/boot/start.S b/kernel/boot/start.S index 60d5eabb1..7708491af 100644 --- a/kernel/boot/start.S +++ b/kernel/boot/start.S @@ -57,18 +57,18 @@ _start: stp x28, x29, [sp ,16 * 14] mrs x9, sp_el0 stp x30, x9, [sp, 16 * 15] - mrs x9, spsr_el1 - mrs x10, elr_el1 + mrs x9, elr_el1 + mrs x10, spsr_el1 stp x9, x10, [sp ,16 * 16] .endm // load general registers from stack .macro load_all - ldp x9, x10, [sp , 16* 15] + ldp x30, x10, [sp ,16 * 15] msr sp_el0, x10 ldp x9, x10, [sp ,16 * 16] - msr spsr_el1, x9 - msr elr_el1, x10 + msr elr_el1, x9 + msr spsr_el1, x10 ldp x0, x1, [sp ,16 * 0] ldp x2, x3, [sp ,16 * 1] @@ -85,7 +85,6 @@ _start: ldp x24, x25, [sp ,16 * 12] ldp x26, x27, [sp ,16 * 13] ldp x28, x29, [sp ,16 * 14] - ldr x30, [sp, 16 * 15] add sp, sp, 16 * 17 .endm diff --git a/kernel/include/read.h b/kernel/include/read.h index 3b782155d..7fc247a5f 100644 --- a/kernel/include/read.h +++ b/kernel/include/read.h @@ -9,6 +9,8 @@ int async_readline(char [MAX_SIZE], int); /* scan the input until get \r */ int readline(char [MAX_SIZE], int); + +int async_readnbyte(char buf[MAX_SIZE], int size); /* read n bytes from uart */ int readnbyte(char [MAX_SIZE], int); #endif \ No newline at end of file diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index b02f9404d..c66630403 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -14,7 +14,7 @@ void sys_getpid(TrapFrame *); void sys_uart_read(TrapFrame *); void sys_uart_write(TrapFrame *); void sys_exec(TrapFrame *); -int sys_fork(TrapFrame *); +void sys_fork(TrapFrame *); void sys_exit(TrapFrame *); void sys_mbox_call(TrapFrame *); void sys_kill(TrapFrame *); diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index 0ff7b857f1fccc6f4c4737e2f82e106e72ea046e..fad399f2fc9bb31cea9c3d9b45aa6c92f8876031 100644 GIT binary patch delta 5091 zcmZvf4N#QVxyR4@F0LTB62XOwitjGC;PSn|f`GD%gnE;VAwU!XW%-&Wqt`G(Cr-@B zYBQgJ<+!$g=nwS;6H&!!=6QyBO^zHxbhi;u7 z=H-3P@A;qSJ?A-J;5;Ka$0S{D5q|P=b1jy_T)Um?QfArnmfH)swL8@1xB%l=LtTDJ# zsri9q-Irm=xpe4-2aE``l-{1Nmz^g1%lsE*rlZEV*CUvYC)0+woS}mF4oT}wN#Zcj#Wv{8k?e}+4?owU5UKKSVfI`;{z~XkKkQeB3;)fNfV?^SRyKE30ve{iPV?ykZ71UJ5~D`CLC+f>#qSHo9_a^K zo3cdvT0D=>c#yWIY?U)+(f26_rBk#wwN^Syld0vBn~Y1J`cG#XchqS6H3;Y^VLpa; zdlmDoyQB6Yw;twA<40!HzSb(6@3LlrS@@A)kAbE0HPE0ly;E^EA3H-|EGZoe*>$JH>iojVbV^CL zh#6Uzz7c``C%sK(%V^=UEqYAt&048H4)NkK#a+f_e4nhNL(6u}54vt?*nn`uMc2)X zsoLBjBK@z~`0o`jg)riIZeC`59~x4|HQBL@dNTDruV!#B*4Fe-!CbgA`4nuyZsL72 z8skRG+uKhaX5K9R-A`)55=Har?d|XO`*X*#`E3p3S3~WU9R7bH+nvKFL$)KAe;u+} z9{)0E7co~J|2*WdS@?yJ?X~bvLN+Vl9@w|HpFHgUsH=b;&PtYTg|sOvNeus2Sti+4 zNN)q8b}maVGb??QWwki1ybmo!GA|X`j)`F*p*$7SE$m+Syqfrdy@4+cGFC*7W?Pb( zmG1z$S!?gVz5Rp3ZcX!r;D`sAqlgY=uhF}Tc$>d<+(1j9wTI!`nw)Hz6;pnWNmh!f zF{etrUdvf2JBsOY&K7BuYI0M0yv4lhy$YY`!m$XpPrMDxd0xh{uTYlr;UIe@#2m}{ z+d;N7#9YhyK#*ap1loF+^WO!TJ;am}?g}!46wsAdI7|3%g78vE=q}+e1=-0EW2Kx2 zndqEY4i&ai%FnY}jAi`CHcfvbZ~~shwleNZVZQl{#f#I*^=c{iA~8-4D-dgi zGs)g8I-O^hca_rjd3xDhO0zB5va^heEXlH~jMm}hE~BR{$@9Hs{AC1i;RyT>d|$1=-S7YAyJg z>|8-_7Oa$J=vu)NpgDy_KV#**6XAm^e9*tbUFH1!X*FRI{S~K0@FW;CK-+Oo8`upt z#!o;??t*waP`CiGj}+QlJO)MLtL|y<#ytRSVCVd)U+kEvM!8>)ops@qKh7r~}Ft0C$&iSL|N`?{~w4r`;V(a~S6#Su8j zBuXg4zA#czQE!ZAYE=}!g`qH~hCV4OnUDPBBVsG=75_F;X3>)3R*|S*7T;Op*80z~ zDUCcZ@}t@}saY@}u9&;0p?5Z~_X!^VuiO^NYy3QJK%AOl&lsGMbZ+??=?*2ABy7A; z;k$%Wi|L)`cgoDS7w3D|wF=)Auvc+5h9%l#a7XfXXwndH*Zn?66gPmkg5Uj+JBp~p zjNG3nS(jv+!$og@LS}ZYiLVtRF#>~g4&{^@A*v~TSa#2$L#0VQj%c2XQ9^SAxwv7t zRpDb}cx<+h-$MrjXIl(U3)kS5WjKa$IpR#T`9?xIR}5baoj9H(uj@Q>xz7I)7nz95 z#w&c$(0Vn|11(Yn7b^hVN5y4}rJ13%W#y8;^ClWuu@u(iit;EldDWBh0+z9Pqop0X#&?s@crU5OnGEktbS-o(p=blkoua&RGy}3gvNj1#n^m1lQh2aSmrCkd2H~OeoF8;v^L)oCS6~4N3DzeJN7@?;_e*Y zhd9nTwqxQv5Q|J)$+x$kKK%WZnjqpj(KgzQK!*?%V*0)kcS9^iKWNsuVsSFy7Q?

U zQp3zbdspXO#xtM`X-@q+jl;~1(8!2gQSLQkv9jJvuQAS)3_h8_d`Hp9%~(NTVL5BW z(IiGFt{*kb%WCRWV^CLfh1V}Ma$Oi=B*tq?af_J zKGP9!!TYcNr?)=Y-Ep6wV_QdCb9YBqw_nZJrjD&!IyM()Klxuj_Y>h)p4v1g=%(7$ zdQw(r4Xs&ij*^Fu(=(45<)g>x;A6@1;Bh*E+Q@O5e9SE0I8M46qZ~6rmKu{>H$n}l zIYwwljmhlAc>R9>BWf(3dL(r`jN#N{o59YE&_^}NYdv8uA;)@PqhSnpJsSo)8peS9 zp~t`vgGUO0Qh>(`c3^}mYOQkoJM>I#t9#$L+Ucv>6w0iNl)Wd&TDNj#?7OP! z7R^w^m(w+jjewQ;Nr07sT?Kn9$P~P*!Jc!|kvgN<<5tzoF!^q{z6EmynS%ELux%&l zPMuMnIZ4LH%{>a1lQXCgA7)6`9ply#|DjM$wjJYr10Wi_Q+^~Ry!GM)v zY!s~eKHUV^X0WC(-M3&z!8-3U5m}27<2te3g{Xtsz(R{g$kM?!zem4bXX%-MD|}iz z;VS!!s@8{vcn-`7)*Ht5gUx^`VeBoiNSs#Tli&th1hzU%Hx5<}_8epPrur)2C}4F^ zD0blt7<->K)+fuc@6(QYlid40y9>6t3;?f!CpL<)XFsH^4Mut5LwX6q)gRG`2Ft9mk5u(AootAs z$i_vo?HrjJt+MAFZEQ@oh|sAx-#Y&m-0OB-?#tSPvnnX!i8&X)`b5X<(UAB*@CbBg z&e7|Q$=Xhs-=3qhjY-;Gm~|ey209G$2@mO-Oj<9@w>(tRWL_JK0P!COM-v>Zf{^Lp zi@-4rA_>aN5eGGuB=3_N@ZE8pKe3NMzf5-}eS#8iO4geLYpSvMqW8TQind1}~T(q_W!9H-|uWVbqCHhp?; zHpL8ggKYy79S}vZ-C%pbge{6#G6%qRhuT&KX140$S32R(#WMo`{!dB2F>kN zV2S`Jf?WaI54PD)u+s3J2D>#uHO*FS0XkX#IqhvWCN;tIetvJ$Do}QU#e6|$o0GNs zVD^4N-!&)o48xrMLRIT7#MblzlgT&U~aI2V86sV-v)N&BAs^_wJw;I zU(-*HWbG)-C%&fSmJ)3QS|)mFV~ZuJ4CbKs-ZVUd_SIm&_0mAgLs2f6m6ud?f<9g! zN3pGm+HsJpmnge+%l!I=Ku^*O18+wI{k~OSdpL-z*9NdH_@1@~>Z^lwS5WsvNFRJ3 a4b|%d`oV@s`a|1d%4{1acbhHjQT_=?lWdv* delta 4834 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 E3s~9h$^ZZW diff --git a/kernel/src/irq.c b/kernel/src/irq.c index b1e283ca0..06b7f7c6d 100644 --- a/kernel/src/irq.c +++ b/kernel/src/irq.c @@ -2,6 +2,7 @@ #include #include #include +#include void irq_handler(unsigned long long what, unsigned long long spsr){ @@ -14,11 +15,13 @@ void irq_handler(unsigned long long what, unsigned long long spsr){ void Time_interrupt(unsigned long long spsr){ unsigned long long frq = 0; asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); + // print_string(UITOHEX, "spsr = ", (unsigned long long)spsr, 1); spsr &= 0b1111; if(spsr == 0x0){ disable_timer_irq(); add_task(timer_interrupt_handler_el0, 1); do_task(); + // timer_interrupt_handler_el0(); } else{ disable_timer_irq(); diff --git a/kernel/src/main.c b/kernel/src/main.c index f6b292c19..46d3a5d51 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -8,6 +8,7 @@ #include #include #include +#include extern Thread *run_thread_head; @@ -24,11 +25,7 @@ int main(unsigned long dtb_base){ // enable_AUX_MU_IER_r(); enable_irq(); // DAIF set to 0b0000 - - // Thread *new_thread = thread_create(print_string); - // print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 1); - // print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); - // print_string(UITOHEX, "thead_head->next: ", (unsigned long long )run_thread_head->list.next, 1); + kernel_exec("kernel8.img"); // kernel_main(); diff --git a/kernel/src/read.c b/kernel/src/read.c index e22f10391..66d9e5558 100644 --- a/kernel/src/read.c +++ b/kernel/src/read.c @@ -70,6 +70,18 @@ int readline(char buf[MAX_SIZE], int size){ return idx; } +/* read n bytes from uart */ +int async_readnbyte(char buf[MAX_SIZE], int size){ + unsigned int idx = 0; + char c; + while(idx < size && idx < MAX_SIZE){ + c = async_uart_getc(); + buf[idx++] = c; + } + buf[idx] = '\0'; + return idx; +} + /* read n bytes from uart */ int readnbyte(char buf[MAX_SIZE], int size){ unsigned int idx = 0; diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 0495e12fe..61d596a4f 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -49,9 +49,9 @@ Thread *thread_create(void(*func)()){ new_thread->ctx.lr = (unsigned long)func; - print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); + print_string(UITOHEX, "[*] new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); uart_puts(" | "); - print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); + print_string(UITOHEX, "[*] new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); list_add_tail(&new_thread->list, &run_thread_head->list); @@ -126,6 +126,7 @@ void kernel_main() { cpu_switch_to(&curr_thread, next_thread); } + void delay(unsigned int time){ unsigned long long system_timer = 0; unsigned long long frq = 0; diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index ee0c67457..4c581d80c 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -7,6 +7,7 @@ #include #include #include +#include extern Thread *run_thread_head; @@ -18,8 +19,10 @@ extern Thread *run_thread_head; /* Get current process’s id. */ void sys_getpid(TrapFrame *trapFrame){ + disable_irq(); int pid = do_getpid(); trapFrame->x[0] = pid; + enable_irq(); } int do_getpid(){ return get_current()->id; @@ -29,26 +32,36 @@ int do_getpid(){ void sys_uart_read(TrapFrame *trapFrame){ char *buf = (char *)trapFrame->x[0]; unsigned int size = trapFrame->x[1]; - int idx = readnbyte(buf, size); + enable_irq(); + int idx = async_readnbyte(buf, size); + disable_irq(); trapFrame->x[0] = idx; + enable_irq(); } /* Return the number of bytes written after writing size byte from the user-supplied buffer buf. */ void sys_uart_write(TrapFrame *trapFrame){ const char *buf = (char *)trapFrame->x[0]; unsigned int size = trapFrame->x[1]; - for(unsigned int i = 0; i < size; i++){ - uart_putc(buf[i]); + unsigned int i; + enable_irq(); + for(i = 0; i < size; i++){ + async_uart_putc(buf[i]); } - trapFrame->x[0] = size; + disable_irq(); + trapFrame->x[0] = i; + enable_irq(); } void sys_exec(TrapFrame *trapFrame){ + disable_irq(); const char *name = (const char *)trapFrame->x[0]; char **const argv = (char **const)trapFrame->x[1]; int success = do_exec(trapFrame, name, argv); trapFrame->x[0] = success; + enable_irq(); } int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ + /* check if the file info exist */ file_info fileInfo = cpio_find_file_info(name); if(fileInfo.filename == NULL) return -1; @@ -62,8 +75,9 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ curr_thread->code_addr = thread_code_addr; curr_thread->code_size = fileInfo.filename_size; - trapFrame->elr_el1 = (unsigned long long)curr_thread->code_addr; - trapFrame->sp_el0 = (unsigned long long)curr_thread->ustack_addr + STACK_SIZE; + /* set current trapFrame elr_el1(begin of code) and sp_el0(begin of user stack)*/ + trapFrame->elr_el1 = (unsigned long)curr_thread->code_addr; + trapFrame->sp_el0 = (unsigned long)curr_thread->ustack_addr + STACK_SIZE; return 0; } @@ -82,7 +96,9 @@ int kernel_exec(char *name){ new_thread->code_size = fileInfo.filename_size; print_string(UITOHEX, "new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); - set_period_timer_irq(); + // set_period_timer_irq(); + add_timer(timeout_print, 1, "[*] Time irq\n"); + enable_irq(); asm volatile( "mov x0, 0x0\n\t" "msr spsr_el1, x0\n\t" @@ -110,8 +126,9 @@ void *load_program(file_info *fileInfo){ } -int sys_fork(TrapFrame *trapFrame){ - return do_fork(trapFrame); +void sys_fork(TrapFrame *trapFrame){ + int child_pid = do_fork(trapFrame); + trapFrame->x[0] = child_pid; } int do_fork(TrapFrame *trapFrame){ @@ -130,7 +147,7 @@ int do_fork(TrapFrame *trapFrame){ /* copy user stack */ memcpy((char *)new_thread->ustack_addr, (char *)curr_thread->ustack_addr, STACK_SIZE); /* copy trap frame (kernel stack) */ - TrapFrame *new_trapFrame = (TrapFrame *)((char *)new_thread->kstack_addr - sizeof(TrapFrame)); + TrapFrame *new_trapFrame = (TrapFrame *)((char *)new_thread->kstack_addr + STACK_SIZE - sizeof(TrapFrame)); memcpy((char*)new_trapFrame, (char *)trapFrame, sizeof(TrapFrame)); /* copy context */ memcpy((char *)&new_thread->ctx, (char *)&curr_thread->ctx, sizeof(CpuContext)); @@ -148,7 +165,7 @@ int do_fork(TrapFrame *trapFrame){ new_thread->ctx.lr = (unsigned long)after_fork; new_thread->ctx.sp = (unsigned long)new_trapFrame; - return 1; + return new_thread->id; } void sys_exit(TrapFrame *trapFrame){ diff --git a/kernel/src/timer.c b/kernel/src/timer.c index 6667b4c72..0a067c324 100644 --- a/kernel/src/timer.c +++ b/kernel/src/timer.c @@ -83,7 +83,7 @@ void timer_interrupt_handler(){ } if(head == NULL) set_long_timer_irq(); if(printAfter2Second == 0) { - add_timer(timeout_print, 2, "[*] After Two Second, Hello User\n# "); + add_timer(timeout_print, 2, "[*] After Two Second, Hello User\n"); printAfter2Second = 1; } enable_timer_irq(); @@ -92,8 +92,10 @@ void timer_interrupt_handler(){ void timer_interrupt_handler_el0(){ head = NULL; uart_puts("Time interrupt\n"); - set_period_timer_irq(); + // set_period_timer_irq(); + add_timer(timeout_print, 1, "[*] Time irq\n"); enable_timer_irq(); + enable_irq(); } void timeout_print(void *args){ diff --git a/kernel/src/uart.c b/kernel/src/uart.c index 353923590..148bb22f6 100644 --- a/kernel/src/uart.c +++ b/kernel/src/uart.c @@ -73,7 +73,7 @@ void recv_interrupt_handler(){ } char async_uart_getc(){ - // enable_AUX_MU_IER_r(); + enable_AUX_MU_IER_r(); /* wait until something is in the read buffer (read_set_idx != read_get_idx) */ while(read_get_idx == read_set_idx) {asm volatile("nop");} From ca871d34c3d83341c8d9d537af4c1b8c6e74823c Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 11 May 2022 00:28:53 +0800 Subject: [PATCH 15/62] syscall: fork done, and don't copy code --- kernel/Makefile | 2 +- kernel/include/syscall.h | 2 +- kernel/initramfs.cpio | Bin 26112 -> 49664 bytes kernel/src/irq.c | 34 +++++++++++++------------- kernel/src/main.c | 2 +- kernel/src/sched.c | 2 +- kernel/src/syscall.c | 50 +++++++++++++++++++++++++-------------- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index b0e763cca..84e2d9bff 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -44,7 +44,7 @@ pty: user: cd ../user;\ make;\ - cp kernel8.img ./initramfs/kernel8.img;\ + cp user2.img ./initramfs/user2.img;\ cd initramfs;\find . | cpio -o -H newc > ../initramfs.cpio;\ cd ..;\ cp ./initramfs.cpio ../kernel/initramfs.cpio diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index c66630403..1b2eb9d13 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -23,7 +23,7 @@ int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); void *load_program(file_info *fileInfo); int do_fork(TrapFrame *trapFrame); -void do_exit(); +void do_exit(int status); void do_kill(); int kernel_exec(char *name); diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index fad399f2fc9bb31cea9c3d9b45aa6c92f8876031..39c8f118ab8781c69007019d3b7dbefd882376ba 100644 GIT binary patch delta 13969 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 diff --git a/kernel/src/irq.c b/kernel/src/irq.c index 06b7f7c6d..94db9ffaf 100644 --- a/kernel/src/irq.c +++ b/kernel/src/irq.c @@ -18,16 +18,16 @@ void Time_interrupt(unsigned long long spsr){ // print_string(UITOHEX, "spsr = ", (unsigned long long)spsr, 1); spsr &= 0b1111; if(spsr == 0x0){ - disable_timer_irq(); - add_task(timer_interrupt_handler_el0, 1); - do_task(); - // timer_interrupt_handler_el0(); + // disable_timer_irq(); + // add_task(timer_interrupt_handler_el0, 1); + // do_task(); + timer_interrupt_handler_el0(); } else{ - disable_timer_irq(); - add_task(timer_interrupt_handler, 1); - do_task(); - // timer_interrupt_handler(); + // disable_timer_irq(); + // add_task(timer_interrupt_handler, 1); + // do_task(); + timer_interrupt_handler(); } } @@ -50,17 +50,17 @@ void Time_interrupt(unsigned long long spsr){ void GPU_interrupt(){ // uart_sputs("GPU interrupt\n"); if(*AUX_MU_IIR & TRANSMIT_HOLDING){ // Transmit interrupt - disable_AUX_MU_IER_w(); - add_task(tran_interrupt_handler, 3); - do_task(); - // tran_interrupt_handler(); + // disable_AUX_MU_IER_w(); + // add_task(tran_interrupt_handler, 3); + // do_task(); + tran_interrupt_handler(); } else if(*AUX_MU_IIR & RECEIVE_VALID){ // Receive interrupt - disable_AUX_MU_IER_r(); - disable_AUX_MU_IER_w(); - add_task(recv_interrupt_handler, 2); - do_task(); - // recv_interrupt_handler(); + // disable_AUX_MU_IER_r(); + // disable_AUX_MU_IER_w(); + // add_task(recv_interrupt_handler, 2); + // do_task(); + recv_interrupt_handler(); } else{ uart_puts("[*] AUX_MU_IIR: No interrupts\n"); diff --git a/kernel/src/main.c b/kernel/src/main.c index 46d3a5d51..4a8b44f31 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -25,7 +25,7 @@ int main(unsigned long dtb_base){ // enable_AUX_MU_IER_r(); enable_irq(); // DAIF set to 0b0000 - kernel_exec("kernel8.img"); + kernel_exec("user2.img"); // kernel_main(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 61d596a4f..87ba17223 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -152,7 +152,7 @@ void foo(){ delay(1); schedule(); } - do_exit(); + do_exit(0); } void print_run_thread(){ diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 4c581d80c..7d75b7fe3 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -57,11 +57,12 @@ void sys_exec(TrapFrame *trapFrame){ const char *name = (const char *)trapFrame->x[0]; char **const argv = (char **const)trapFrame->x[1]; int success = do_exec(trapFrame, name, argv); + disable_irq(); trapFrame->x[0] = success; enable_irq(); } int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ - + disable_irq(); /* check if the file info exist */ file_info fileInfo = cpio_find_file_info(name); if(fileInfo.filename == NULL) return -1; @@ -73,16 +74,17 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ /* current thread will change the pc to new code addr */ Thread *curr_thread = get_current(); curr_thread->code_addr = thread_code_addr; - curr_thread->code_size = fileInfo.filename_size; + curr_thread->code_size = fileInfo.datasize; /* set current trapFrame elr_el1(begin of code) and sp_el0(begin of user stack)*/ trapFrame->elr_el1 = (unsigned long)curr_thread->code_addr; trapFrame->sp_el0 = (unsigned long)curr_thread->ustack_addr + STACK_SIZE; - + enable_irq(); return 0; } int kernel_exec(char *name){ + disable_irq(); /* check if the file info exist */ file_info fileInfo = cpio_find_file_info(name); if(fileInfo.filename == NULL) return -1; @@ -93,11 +95,11 @@ int kernel_exec(char *name){ Thread *new_thread = thread_create(thread_code_addr); new_thread->code_addr = thread_code_addr; - new_thread->code_size = fileInfo.filename_size; - print_string(UITOHEX, "new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); + new_thread->code_size = fileInfo.datasize; + print_string(UITOHEX, "[*] kernel_exec: new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); // set_period_timer_irq(); - add_timer(timeout_print, 1, "[*] Time irq\n"); + // add_timer(timeout_print, 1, "[*] Time irq\n"); enable_irq(); asm volatile( "mov x0, 0x0\n\t" @@ -119,61 +121,73 @@ int kernel_exec(char *name){ void *load_program(file_info *fileInfo){ void *thread_code_addr = kmalloc(fileInfo->datasize); if(thread_code_addr == NULL) return NULL; - memcpy(thread_code_addr, fileInfo->data, fileInfo->datasize); - return thread_code_addr; } void sys_fork(TrapFrame *trapFrame){ + disable_irq(); int child_pid = do_fork(trapFrame); + disable_irq(); trapFrame->x[0] = child_pid; + enable_irq(); } int do_fork(TrapFrame *trapFrame){ + disable_irq(); Thread *curr_thread = get_current(); - void *thread_code_addr = kmalloc(curr_thread->code_size); - if(thread_code_addr == NULL) return -1; + // void *thread_code_addr = kmalloc(curr_thread->code_size); + // if(thread_code_addr == NULL) return -1; Thread *new_thread = thread_create(curr_thread->code_addr); - new_thread->code_addr = thread_code_addr; + new_thread->code_addr = curr_thread->code_addr; new_thread->code_size = curr_thread->code_size; /* copy the code */ - memcpy((char *)new_thread->code_addr, (char *)curr_thread->code_addr, new_thread->code_size); + // memcpy((char *)new_thread->code_addr, (char *)curr_thread->code_addr, new_thread->code_size); + /* copy user stack */ memcpy((char *)new_thread->ustack_addr, (char *)curr_thread->ustack_addr, STACK_SIZE); /* copy trap frame (kernel stack) */ TrapFrame *new_trapFrame = (TrapFrame *)((char *)new_thread->kstack_addr + STACK_SIZE - sizeof(TrapFrame)); - memcpy((char*)new_trapFrame, (char *)trapFrame, sizeof(TrapFrame)); + memcpy((char *)new_trapFrame, (char *)trapFrame, sizeof(TrapFrame)); /* copy context */ memcpy((char *)&new_thread->ctx, (char *)&curr_thread->ctx, sizeof(CpuContext)); + + // print_string(UITOHEX, "(child)new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); + /* return pid = 0 (child) */ new_trapFrame->x[0] = 0; /* set new code return to after eret */ - new_trapFrame->elr_el1 = (unsigned long)new_thread->code_addr + - (trapFrame->elr_el1 - (unsigned long)curr_thread->code_addr); + // new_trapFrame->elr_el1 = (unsigned long)new_thread->code_addr + + // (trapFrame->elr_el1 - (unsigned long)curr_thread->code_addr); + new_trapFrame->elr_el1 = trapFrame->elr_el1; + // print_string(UITOHEX, "(child)new_trapFrame->elr_el1: 0x", (unsigned long long)trapFrame->elr_el1, 1); /* set new code return to after eret */ new_trapFrame->sp_el0 = ((unsigned long)new_thread->ustack_addr + STACK_SIZE) - (((unsigned long)curr_thread->ustack_addr + STACK_SIZE) - trapFrame->sp_el0); + /* after context switch, child proc will load all reg from kernel stack, and return to el0 */ new_thread->ctx.lr = (unsigned long)after_fork; new_thread->ctx.sp = (unsigned long)new_trapFrame; - + enable_irq(); return new_thread->id; } void sys_exit(TrapFrame *trapFrame){ - do_exit(); + disable_irq(); + int status = trapFrame->x[0]; + do_exit(status); } /* Terminate the current process. */ -void do_exit(){ +void do_exit(int status){ disable_irq(); + Thread *exit_thread = get_current(); exit_thread->state = EXIT; From 14b73351212474b4e98901afac3e6c8e98c1e831 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 12 May 2022 00:16:14 +0800 Subject: [PATCH 16/62] syscall: fork ok irq: time schedule ok --- kernel/include/sched.h | 2 +- kernel/include/timer.h | 3 ++- kernel/initramfs.cpio | Bin 49664 -> 49664 bytes kernel/src/irq.c | 42 ++++++++++++++++++++++++++--------------- kernel/src/main.c | 6 +++++- kernel/src/sched.c | 15 ++++++++------- kernel/src/shell.c | 24 ++++++++++++----------- kernel/src/syscall.c | 4 ++-- kernel/src/timer.c | 35 ++++++++++++++++++++++------------ 9 files changed, 81 insertions(+), 50 deletions(-) diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 47aaa0fbe..1c70be1b6 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -50,7 +50,7 @@ void kill_zombie(); void idle_thread(); void schedule(); void kernel_main(); -void delay(unsigned int); +void delay(unsigned long long); void foo(); void print_run_thread(); diff --git a/kernel/include/timer.h b/kernel/include/timer.h index 45ddd1dae..e2c37a4c8 100644 --- a/kernel/include/timer.h +++ b/kernel/include/timer.h @@ -29,8 +29,9 @@ typedef struct _Timer{ struct _Timer *prev; }Timer; -void add_timer(TimerTask, unsigned long long, void *); +void add_timer(TimerTask, unsigned long long, void *, unsigned int); void timeout_print(void *); +void sched_timeout(void *); void timer_interrupt_handler(); void timer_interrupt_handler_el0(); diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index 39c8f118ab8781c69007019d3b7dbefd882376ba..47b403cf3c7af592362ec2add1a211637b4ec220 100644 GIT binary patch delta 6172 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 V{mB #include #include +#include +extern Thread *run_thread_head; void irq_handler(unsigned long long what, unsigned long long spsr){ // char buf[10]; @@ -16,19 +18,25 @@ void Time_interrupt(unsigned long long spsr){ unsigned long long frq = 0; asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); // print_string(UITOHEX, "spsr = ", (unsigned long long)spsr, 1); - spsr &= 0b1111; - if(spsr == 0x0){ - // disable_timer_irq(); - // add_task(timer_interrupt_handler_el0, 1); - // do_task(); - timer_interrupt_handler_el0(); - } - else{ + // spsr &= 0b1111; + // if(spsr == 0x0){ + // // disable_timer_irq(); + // // add_task(timer_interrupt_handler_el0, 1); + // // do_task(); + // timer_interrupt_handler_el0(); + // } + // else{ // disable_timer_irq(); // add_task(timer_interrupt_handler, 1); // do_task(); timer_interrupt_handler(); - } + Thread *next = (Thread *)run_thread_head->list.next; + if((Thread *)next->list.next != run_thread_head){ + //uart_puts("go to schedule\n"); + schedule(); + } + + // } } @@ -54,6 +62,7 @@ void GPU_interrupt(){ // add_task(tran_interrupt_handler, 3); // do_task(); tran_interrupt_handler(); + enable_irq(); } else if(*AUX_MU_IIR & RECEIVE_VALID){ // Receive interrupt // disable_AUX_MU_IER_r(); @@ -61,6 +70,7 @@ void GPU_interrupt(){ // add_task(recv_interrupt_handler, 2); // do_task(); recv_interrupt_handler(); + enable_irq(); } else{ uart_puts("[*] AUX_MU_IIR: No interrupts\n"); @@ -92,18 +102,20 @@ void reset_timer_irq(unsigned long long expired_time){ } void set_long_timer_irq(){ - asm volatile( + asm volatile( "mov x1, 0xfffffffffffffff\n\t" // set a large second "msr cntp_cval_el0, x1\n\t" :::"x1" ); } -void set_period_timer_irq(){ - asm volatile( - "mrs x1, cntfrq_el0\n\t" - "msr cntp_tval_el0, x1\n\t" - :::"x1" +void set_period_timer_irq(unsigned long long peried){ + unsigned long long system_timer = 0; + asm volatile("mrs %0, cntpct_el0\n\t" :"=r"(system_timer)); + system_timer += peried; + asm volatile( + "msr cntp_tval_el0, %0\n\t" + ::"r"(system_timer) ); } diff --git a/kernel/src/main.c b/kernel/src/main.c index 4a8b44f31..5fbbf0a66 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -13,6 +13,10 @@ extern Thread *run_thread_head; int main(unsigned long dtb_base){ + unsigned long long tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); uart_init(); // uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); @@ -25,7 +29,7 @@ int main(unsigned long dtb_base){ // enable_AUX_MU_IER_r(); enable_irq(); // DAIF set to 0b0000 - kernel_exec("user2.img"); + // kernel_exec("user2.img"); // kernel_main(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 87ba17223..54820b10d 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -79,9 +79,10 @@ void kill_zombie(){ tmp->state = NOUSE; tmp->ustack_addr = NULL; tmp->kstack_addr = NULL; - if(tmp->code_addr != NULL){ - kfree(tmp->code_addr); - } + /* cannot remove code_addr beacuse fork process share the code?? */ + // if(tmp->code_addr != NULL){ + // kfree(tmp->code_addr); + // } tmp->code_addr = NULL; tmp->code_size = 0; list_del_entry(&tmp->list); @@ -101,7 +102,7 @@ void schedule(){ // print_string(UITOHEX, "curr: ", curr_thread->id, 0); // uart_puts(" | "); // print_string(UITOHEX, "next: ", next_thread->id, 1); - + // print_run_thread(); enable_irq(); cpu_switch_to(curr_thread, next_thread); } @@ -127,7 +128,7 @@ void kernel_main() { } -void delay(unsigned int time){ +void delay(unsigned long long time){ unsigned long long system_timer = 0; unsigned long long frq = 0; asm volatile( @@ -135,7 +136,7 @@ void delay(unsigned int time){ "mrs %1, cntfrq_el0\n\t" :"=r"(system_timer), "=r"(frq) ); - unsigned long long expired_time = system_timer + 1000; + unsigned long long expired_time = system_timer + time; while(system_timer <= expired_time){ asm volatile( "mrs %0, cntpct_el0\n\t" @@ -149,7 +150,7 @@ void foo(){ print_string(UITOA, "Thread id: ", get_current()->id, 0); print_string(UITOA, " ", i, 1); // printf("Thread id: %d %d\n", get_current()->task_id, i); - delay(1); + delay(1000000); schedule(); } do_exit(0); diff --git a/kernel/src/shell.c b/kernel/src/shell.c index bb51db781..e49b71963 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -8,6 +8,7 @@ #include #include #include +#include /* print welcome message*/ void PrintWelcome(){ @@ -113,6 +114,7 @@ void Exec(char buf[MAX_SIZE]){ return; } int state = kernel_exec(buf); + enable_irq(); if(state == -1){ uart_puts("[x] Failed to exec the file\n"); @@ -128,20 +130,20 @@ void SetTimeOut(char buf[MAX_SIZE]){ strcpy(message, message_tmp); unsigned int timeout = atoui(end_message + 1); - add_timer(timeout_print, timeout, message); + add_timer(timeout_print, timeout, message, 0); } void TestTimeOut(char buf[MAX_SIZE]){ - add_timer(timeout_print, 2, "[*] timeout: 2\n"); - add_timer(timeout_print, 1, "[*] timeout: 1\n"); - add_timer(timeout_print, 5, "[*] timeout: 5\n"); - add_timer(timeout_print, 4, "[*] timeout: 4\n"); - add_timer(timeout_print, 3, "[*] timeout: 3\n"); - add_timer(timeout_print, 2, "[*] timeout: 2.1\n"); // test short expired time - add_timer(timeout_print, 9, "[*] timeout: 9\n"); - add_timer(timeout_print, 7, "[*] timeout: 7\n"); - add_timer(timeout_print, 6, "[*] timeout: 6\n"); - add_timer(timeout_print, 8, "[*] timeout: 8\n"); + add_timer(timeout_print, 2, "[*] timeout: 2\n", 0); + add_timer(timeout_print, 1, "[*] timeout: 1\n", 0); + add_timer(timeout_print, 5, "[*] timeout: 5\n", 0); + add_timer(timeout_print, 4, "[*] timeout: 4\n", 0); + add_timer(timeout_print, 3, "[*] timeout: 3\n", 0); + add_timer(timeout_print, 2, "[*] timeout: 2.1\n", 0); // test short expired time + add_timer(timeout_print, 9, "[*] timeout: 9\n", 0); + add_timer(timeout_print, 7, "[*] timeout: 7\n", 0); + add_timer(timeout_print, 6, "[*] timeout: 6\n", 0); + add_timer(timeout_print, 8, "[*] timeout: 8\n", 0); } diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 7d75b7fe3..4d5ab43b7 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -99,7 +99,7 @@ int kernel_exec(char *name){ print_string(UITOHEX, "[*] kernel_exec: new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); // set_period_timer_irq(); - // add_timer(timeout_print, 1, "[*] Time irq\n"); + sched_timeout("omg"); enable_irq(); asm volatile( "mov x0, 0x0\n\t" @@ -166,7 +166,7 @@ int do_fork(TrapFrame *trapFrame){ // new_trapFrame->elr_el1 = (unsigned long)new_thread->code_addr + // (trapFrame->elr_el1 - (unsigned long)curr_thread->code_addr); new_trapFrame->elr_el1 = trapFrame->elr_el1; - // print_string(UITOHEX, "(child)new_trapFrame->elr_el1: 0x", (unsigned long long)trapFrame->elr_el1, 1); + /* set new code return to after eret */ new_trapFrame->sp_el0 = ((unsigned long)new_thread->ustack_addr + STACK_SIZE) - (((unsigned long)curr_thread->ustack_addr + STACK_SIZE) - trapFrame->sp_el0); diff --git a/kernel/src/timer.c b/kernel/src/timer.c index 0a067c324..52adc90b0 100644 --- a/kernel/src/timer.c +++ b/kernel/src/timer.c @@ -6,7 +6,7 @@ int printAfter2Second = 0; Timer *head = NULL; -void add_timer(TimerTask task, unsigned long long expired_time, void *args){ +void add_timer(TimerTask task, unsigned long long expired_time, void *args, unsigned int tick){ unsigned long long system_timer = 0; unsigned long long frq = 0; @@ -18,7 +18,10 @@ void add_timer(TimerTask task, unsigned long long expired_time, void *args){ Timer *timer = (Timer*)kmalloc(sizeof(Timer)); memset((char *)timer, 0, sizeof(Timer)); - timer->expired_time = system_timer + expired_time * frq; + if(tick) + timer->expired_time = system_timer + expired_time; + else + timer->expired_time = system_timer + expired_time * frq; timer->task = task; timer->args = args; timer->next = NULL; @@ -57,15 +60,12 @@ void add_timer(TimerTask task, unsigned long long expired_time, void *args){ void timer_interrupt_handler(){ - // while(1){ - // uart_puts("abcdefghijklmnopqrstuvwxyz\n"); - // } while(head != NULL){ if(head->next != NULL){ Timer *tmp = head; - head->task(head->args); head = head->next; head->prev = NULL; + tmp->task(tmp->args); kfree(tmp); tmp = NULL; unsigned long long system_timer = 0; @@ -76,14 +76,19 @@ void timer_interrupt_handler(){ } } else{ - head->task(head->args); - kfree(head); + Timer *tmp = head; head = NULL; + tmp->task(tmp->args); + kfree(tmp); + tmp = NULL; + break; } } - if(head == NULL) set_long_timer_irq(); + if(head == NULL){ + set_long_timer_irq(); + } if(printAfter2Second == 0) { - add_timer(timeout_print, 2, "[*] After Two Second, Hello User\n"); + add_timer(timeout_print, 2, "[*] After Two Second, Hello User\n", 0); printAfter2Second = 1; } enable_timer_irq(); @@ -93,11 +98,17 @@ void timer_interrupt_handler_el0(){ head = NULL; uart_puts("Time interrupt\n"); // set_period_timer_irq(); - add_timer(timeout_print, 1, "[*] Time irq\n"); + add_timer(timeout_print, 1, "[*] Time irq\n", 0); enable_timer_irq(); - enable_irq(); } void timeout_print(void *args){ uart_puts((char*)args); +} + +void sched_timeout(void *args){ + // uart_puts("omg\n"); + unsigned long long frq; + asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); + add_timer(sched_timeout, frq>>5 , "omg", 1); } \ No newline at end of file From 7f974acdea1e69f7fd2011827bb897bc0b5cb646 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 12 May 2022 23:28:11 +0800 Subject: [PATCH 17/62] syscall: mbox maybe ok & upload user program --- .gitignore | 4 +- kernel/include/mailbox.h | 6 +- kernel/include/sched.h | 3 +- kernel/include/syscall.h | 2 +- kernel/include/user_syscall.h | 4 +- kernel/initramfs.cpio | Bin 49664 -> 49664 bytes kernel/src/mailbox.c | 6 +- kernel/src/sched.c | 2 +- kernel/src/shell.c | 20 +-- kernel/src/syscall.c | 31 +++- kernel/src/user_syscall.S | 8 +- user/Makefile | 43 +++++ user/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 31790 bytes user/boot/linker.ld | 24 +++ user/boot/start.S | 5 + user/include/allocator.h | 51 ++++++ user/include/cpio.h | 51 ++++++ user/include/exc.h | 13 ++ user/include/fdt.h | 37 +++++ user/include/gpio.h | 25 +++ user/include/irq.h | 40 +++++ user/include/list.h | 287 ++++++++++++++++++++++++++++++++ user/include/mailbox.h | 43 +++++ user/include/malloc.h | 30 ++++ user/include/math.h | 7 + user/include/read.h | 14 ++ user/include/reboot.h | 12 ++ user/include/sched.h | 59 +++++++ user/include/shell.h | 36 ++++ user/include/stddef.h | 9 + user/include/stdint.h | 18 ++ user/include/string.h | 50 ++++++ user/include/syscall.h | 31 ++++ user/include/task.h | 22 +++ user/include/timer.h | 38 +++++ user/include/uart.h | 53 ++++++ user/include/user_syscall.h | 26 +++ user/initramfs.cpio | Bin 0 -> 49664 bytes user/initramfs/me | 27 +++ user/initramfs/test1 | 1 + user/initramfs/test2.txt | 2 + user/initramfs/test3.cc | 2 + user/initramfs/user1.S | 11 ++ user/src/allocator.c | 300 ++++++++++++++++++++++++++++++++++ user/src/cpio.c | 112 +++++++++++++ user/src/ctx_switch.S | 28 ++++ user/src/exc.c | 63 +++++++ user/src/fdt.c | 82 ++++++++++ user/src/irq.c | 114 +++++++++++++ user/src/mailbox.c | 61 +++++++ user/src/main.c | 148 +++++++++++++++++ user/src/malloc.c | 197 ++++++++++++++++++++++ user/src/math.c | 15 ++ user/src/read.c | 83 ++++++++++ user/src/reboot.c | 11 ++ user/src/sched.c | 167 +++++++++++++++++++ user/src/shell.c | 177 ++++++++++++++++++++ user/src/string.c | 212 ++++++++++++++++++++++++ user/src/syscall.c | 143 ++++++++++++++++ user/src/task.c | 82 ++++++++++ user/src/timer.c | 101 ++++++++++++ user/src/uart.c | 175 ++++++++++++++++++++ user/src/user_syscall.S | 51 ++++++ 63 files changed, 3442 insertions(+), 33 deletions(-) create mode 100644 user/Makefile create mode 100644 user/bcm2710-rpi-3-b-plus.dtb create mode 100644 user/boot/linker.ld create mode 100644 user/boot/start.S create mode 100644 user/include/allocator.h create mode 100644 user/include/cpio.h create mode 100644 user/include/exc.h create mode 100644 user/include/fdt.h create mode 100644 user/include/gpio.h create mode 100644 user/include/irq.h create mode 100644 user/include/list.h create mode 100644 user/include/mailbox.h create mode 100644 user/include/malloc.h create mode 100644 user/include/math.h create mode 100644 user/include/read.h create mode 100644 user/include/reboot.h create mode 100644 user/include/sched.h create mode 100644 user/include/shell.h create mode 100644 user/include/stddef.h create mode 100644 user/include/stdint.h create mode 100644 user/include/string.h create mode 100644 user/include/syscall.h create mode 100644 user/include/task.h create mode 100644 user/include/timer.h create mode 100644 user/include/uart.h create mode 100644 user/include/user_syscall.h create mode 100644 user/initramfs.cpio create mode 100644 user/initramfs/me create mode 100644 user/initramfs/test1 create mode 100644 user/initramfs/test2.txt create mode 100644 user/initramfs/test3.cc create mode 100644 user/initramfs/user1.S create mode 100644 user/src/allocator.c create mode 100644 user/src/cpio.c create mode 100644 user/src/ctx_switch.S create mode 100644 user/src/exc.c create mode 100644 user/src/fdt.c create mode 100644 user/src/irq.c create mode 100644 user/src/mailbox.c create mode 100644 user/src/main.c create mode 100644 user/src/malloc.c create mode 100644 user/src/math.c create mode 100644 user/src/read.c create mode 100644 user/src/reboot.c create mode 100644 user/src/sched.c create mode 100644 user/src/shell.c create mode 100644 user/src/string.c create mode 100644 user/src/syscall.c create mode 100644 user/src/task.c create mode 100644 user/src/timer.c create mode 100644 user/src/uart.c create mode 100644 user/src/user_syscall.S diff --git a/.gitignore b/.gitignore index 0b6edfe2f..66b03fa51 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,9 @@ bootloader/src/*.o bootloader/boot/*.o -user/* +user/*.elf +user/*.img +user/build/* peda* .gdb* diff --git a/kernel/include/mailbox.h b/kernel/include/mailbox.h index 1c42e1870..101457f79 100644 --- a/kernel/include/mailbox.h +++ b/kernel/include/mailbox.h @@ -36,8 +36,8 @@ #define MAILBOX_CH_COUNT 7 #define MAILBOX_CH_PROP 8 -unsigned int get_board_revision(volatile unsigned int [36]); -unsigned int get_arm_memory(volatile unsigned int [36]); -unsigned int mailbox_call(volatile unsigned int [36], unsigned char); +unsigned int get_board_revision(unsigned int [36]); +unsigned int get_arm_memory(unsigned int [36]); +unsigned int mailbox_call(unsigned int *, unsigned char); #endif diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 1c70be1b6..45d31c103 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -3,12 +3,11 @@ #include #include -#define MAX_THREAD 0x10 +#define MAX_THREAD 0x100 #define STACK_SIZE 0x1000 enum thread_state{ NOUSE, - WAIT, RUNNING, EXIT }; diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 1b2eb9d13..2e02d4de4 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -24,7 +24,7 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); void *load_program(file_info *fileInfo); int do_fork(TrapFrame *trapFrame); void do_exit(int status); -void do_kill(); +int do_kill(int pid); int kernel_exec(char *name); diff --git a/kernel/include/user_syscall.h b/kernel/include/user_syscall.h index 27c9dcb87..644f1bfa9 100644 --- a/kernel/include/user_syscall.h +++ b/kernel/include/user_syscall.h @@ -15,8 +15,8 @@ #ifndef __ASSEMBLY__ extern int getpid(); -extern unsigned int uart_read(char buf[], unsigned int size); -extern unsigned int uart_write(const char buf[], unsigned int size); +extern unsigned int uartread(char buf[], unsigned int size); +extern unsigned int uartwrite(const char buf[], unsigned int size); extern int exec(const char *name, char *const argv[]); extern int fork(); extern void exit(); diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index 47b403cf3c7af592362ec2add1a211637b4ec220..a8b57f1f106d5c45d46986432909de200dc36068 100644 GIT binary patch delta 10834 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 BkYxY> diff --git a/kernel/src/mailbox.c b/kernel/src/mailbox.c index 070c63c6c..984252555 100644 --- a/kernel/src/mailbox.c +++ b/kernel/src/mailbox.c @@ -2,7 +2,7 @@ #include #include -unsigned int get_board_revision(volatile unsigned int mbox[36]){ +unsigned int get_board_revision(unsigned int mbox[36]){ mbox[0] = 7 * 4; // buffer size in bytes mbox[1] = REQUEST_CODE; // tags begin @@ -16,7 +16,7 @@ unsigned int get_board_revision(volatile unsigned int mbox[36]){ return mailbox_call(mbox, MAILBOX_CH_PROP); } -unsigned int get_arm_memory(volatile unsigned int mbox[36]){ +unsigned int get_arm_memory(unsigned int mbox[36]){ mbox[0] = 8 * 4; // buffer size in bytes mbox[1] = REQUEST_CODE; // tags begin @@ -40,7 +40,7 @@ unsigned int get_arm_memory(volatile unsigned int mbox[36]){ 5. If not, then you can read from Mailbox 0 Read/Write register. 6. Check if the value is the same as you wrote in step 1. */ -unsigned int mailbox_call(volatile unsigned int mbox[36], unsigned char ch){ +unsigned int mailbox_call(unsigned int *mbox, unsigned char ch){ /* Combine the message address (upper 28 bits) with channel number (lower 4 bits) */ unsigned int req = (((unsigned int)((unsigned long)mbox) & (~0xF)) | (ch & 0xF)); /* wait until we can write to the mailbox */ diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 54820b10d..cbd0304e1 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -41,7 +41,7 @@ Thread *thread_create(void(*func)()){ if(idx == MAX_THREAD) return NULL; Thread *new_thread = &thread_pool[idx]; - new_thread->state = WAIT; + new_thread->state = RUNNING; new_thread->ustack_addr = kmalloc(STACK_SIZE); new_thread->kstack_addr = kmalloc(STACK_SIZE); new_thread->ctx.fp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; diff --git a/kernel/src/shell.c b/kernel/src/shell.c index e49b71963..535f5b5ea 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -41,13 +41,10 @@ void PrintUnknown(char buf[MAX_SIZE]){ /* print board revision*/ void PrintRevision(char buf[MAX_SIZE]){ - volatile unsigned int mbox[36]; + unsigned int mbox[36]; unsigned int success = get_board_revision(mbox); if (success){ - uart_puts("Board Revision: 0x"); - uitohex(buf, mbox[5]); - uart_puts(buf); - uart_puts("\n"); + print_string(UITOHEX, "Board Revision: 0x", mbox[5], 1); } else{ uart_puts("Failed to get board revision\n"); } @@ -55,18 +52,11 @@ void PrintRevision(char buf[MAX_SIZE]){ /* print memory info*/ void PrintMemory(char buf[MAX_SIZE]){ - volatile unsigned int mbox[36]; + unsigned int mbox[36]; unsigned int success = get_arm_memory(mbox); if (success){ - uart_puts("ARM Memory Base Address: 0x"); - uitohex(buf, mbox[5]); - uart_puts(buf); - uart_puts("\n"); - - uart_puts("ARM Memory Size: 0x"); - uitohex(buf, mbox[6]); - uart_puts(buf); - uart_puts("\n"); + print_string(UITOHEX, "ARM Memory Base Address: 0x", mbox[5], 1); + print_string(UITOHEX, "ARM Memory Size: 0x", mbox[6], 1); } else{ uart_puts("Failed to get board revision\n"); } diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 4d5ab43b7..80a8efc78 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -8,7 +8,9 @@ #include #include #include +#include +extern Thread *thread_pool; extern Thread *run_thread_head; /* @@ -196,8 +198,35 @@ void do_exit(int status){ } void sys_mbox_call(TrapFrame *trapFrame){ - + disable_irq(); + unsigned char ch = trapFrame->x[0]; + unsigned int *mbox = (unsigned int *)trapFrame->x[1]; + int status = mailbox_call(mbox, ch); + trapFrame->x[0] = status; + enable_irq(); } + + void sys_kill(TrapFrame *trapFrame){ + disable_irq(); + int pid = trapFrame->x[0]; + int status = do_kill(pid); + if(status == -1) + print_string(UITOA, "[x] kill fail, pid: ", pid, 1); + else + print_string(UITOA, "[*] kill success, pid: ", pid, 1); + enable_irq(); +} + +int do_kill(int pid){ + if(!(pid >= 0 && pid < MAX_THREAD)) + return -1; + if(thread_pool[pid].state != RUNNING) + return -1; + + thread_pool[pid].state = EXIT; + enable_irq(); + schedule(); + return 0; } \ No newline at end of file diff --git a/kernel/src/user_syscall.S b/kernel/src/user_syscall.S index 83fbfe3a2..7ac7d6465 100644 --- a/kernel/src/user_syscall.S +++ b/kernel/src/user_syscall.S @@ -8,14 +8,14 @@ getpid: svc #0 ret -.global uart_read -uart_read: +.global uartread +uartread: mov x8, UART_READ svc #0 ret -.global uart_write -uart_write: +.global uartwrite +uartwrite: mov x8, UART_WRITE svc #0 ret diff --git a/user/Makefile b/user/Makefile new file mode 100644 index 000000000..8906dfe84 --- /dev/null +++ b/user/Makefile @@ -0,0 +1,43 @@ +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +INCLUDE = -I ./include/ + +SRC_DIR = src +BUILD_DIR = build +BOOT_DIR = boot + +SFILES_ASM = $(wildcard src/*.S) +OFILES_ASM = $(patsubst src/%.S, build/%.o, $(SFILES_ASM)) # OFILES = $(SFILES:.c=.o) +SFILES = $(wildcard src/*.c) +OFILES = $(patsubst src/%.c, build/%.o, $(SFILES)) # OFILES = $(SFILES:.c=.o) +BOOTFILES = $(wildcard boot/*.S) +BOOTOFILES = $(patsubst boot/%.S, build/%.o, $(BOOTFILES)) # BOOTOFILES = $(BOOTFILES:.S=.o) +ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) + +CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles + +all: clean user2.img + +$(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.S + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g + +user2.img: $(ALLOFILES) + $(LD) -nostdlib -nostartfiles $(ALLOFILES) -T boot/linker.ld -o user2.elf + $(OBJCOPY) -O binary user2.elf user2.img + +clean: + rm *.elf *.img build/* >/dev/null 2>/dev/null || true + +run: + qemu-system-aarch64 -M raspi3 -kernel user2.img -display none -s -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +pty: + qemu-system-aarch64 -M raspi3 -kernel user2.img -display none -s -serial null -serial pty -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + diff --git a/user/bcm2710-rpi-3-b-plus.dtb b/user/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..3934b3a26eb82fd65dbcdfca6f6916da427a3fae GIT binary patch literal 31790 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 literal 0 HcmV?d00001 diff --git a/user/boot/linker.ld b/user/boot/linker.ld new file mode 100644 index 000000000..745a0d835 --- /dev/null +++ b/user/boot/linker.ld @@ -0,0 +1,24 @@ +/* MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} */ +/* _estack = ORIGIN(RAM) + LENGTH(RAM); */ + +SECTIONS +{ + . = 0x80000; + _start = .; + .text : { KEEP(*(.text.boot)) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + . = ALIGN(16); + __bss_start = .; + *(.bss) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; + diff --git a/user/boot/start.S b/user/boot/start.S new file mode 100644 index 000000000..0fd0dcb1d --- /dev/null +++ b/user/boot/start.S @@ -0,0 +1,5 @@ +.section ".text.boot" +.global _start + +_start: + bl main \ No newline at end of file diff --git a/user/include/allocator.h b/user/include/allocator.h new file mode 100644 index 000000000..9a50f396f --- /dev/null +++ b/user/include/allocator.h @@ -0,0 +1,51 @@ +#ifndef ALLOCATOR_H_ +#define ALLOCATOR_H_ +#include +#include + +#define BUDDY_ADDR_START 0x00000000 +#define BUDDY_ADDR_END 0x3C000000 +// #define BUDDY_ADDR_START 0x10000000 +// #define BUDDY_ADDR_END 0x20000000 +#define FRAME_SIZE 4096 +#define FRAME_NUM ((BUDDY_ADDR_END-BUDDY_ADDR_START) / FRAME_SIZE) +#define MAX_BUDDY_ORDER 15 + +typedef struct _Buddy { + struct list_head list; +}Buddy; + +typedef struct _Frame { + struct list_head list; + int order; + unsigned int idx; + unsigned int free; + + // chunk allocator + int chunk_level; +}Frame; + +void startup_alloc(); +void all_allocator_init(); +void frames_init(); +void memory_init(); +void memory_reserve(void *start, void *end); +void allocator_init(); +void buddy_init(); +void buddy_push(Frame *, Buddy *); +void *buddy_pop(Buddy *, int); +void *buddy_alloc(unsigned int); +void *release_redundant(Frame *, int); +void buddy_free(void *); +Frame *find_buddy_frame(Frame*, int); +int addr_to_frame_idx(void *); + + +void print_frame_info(Frame *); +void print_use_frame(unsigned int, unsigned int, unsigned int, int); +void print_frame_info(Frame *); +void print_buddy_list(); + +void buddy_debug(); + +#endif \ No newline at end of file diff --git a/user/include/cpio.h b/user/include/cpio.h new file mode 100644 index 000000000..a20094107 --- /dev/null +++ b/user/include/cpio.h @@ -0,0 +1,51 @@ +#ifndef CPIO_H_ +#define CPIO_H_ + +#include +// #define CPIO_BASE ((volatile unsigned long*)(0x8000000)) +unsigned long long CPIO_BASE_START; +unsigned long long CPIO_BASE_END; + +/* Magic identifiers for the "cpio" file format. */ +#define CPIO_HEADER_MAGIC "070701" +#define CPIO_FOOTER_MAGIC "TRAILER!!!" +#define CPIO_ALIGNMENT 4 + + +typedef struct _cpio_newc_header{ + char c_magic[6]; /* Magic header '070701'. */ + char c_ino[8]; /* "i-node" number. */ + char c_mode[8]; /* Permisions. */ + char c_uid[8]; /* User ID. */ + char c_gid[8]; /* Group ID. */ + char c_nlink[8]; /* Number of hard links. */ + char c_mtime[8]; /* Modification time. */ + char c_filesize[8]; /* File size. */ + char c_dev1major[8]; /* Major dev number. */ + char c_devminor[8]; /* Minor dev number. */ + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; /* Length of filename in bytes. */ + char c_check[8]; /* Checksum. */ +}cpio_newc_header; + + + +typedef struct _file_info{ + char *filename; + char *data; + unsigned int filename_size; + unsigned int datasize; +}file_info; + +void testprint(); +unsigned int padding(unsigned int); +void ls(); +void cat(char [MAX_SIZE]); +void run(unsigned long); +unsigned long findDataAddr(const char *); +file_info cpio_find_file_info(const char *thefilename); +void parse_cpio_header(cpio_newc_header *, file_info *); + + +#endif diff --git a/user/include/exc.h b/user/include/exc.h new file mode 100644 index 000000000..5bf15b6f3 --- /dev/null +++ b/user/include/exc.h @@ -0,0 +1,13 @@ +#ifndef EXC_H_ +#define EXC_H_ +#include + +void exception_handler( unsigned long long, + unsigned long long, + unsigned long long, + TrapFrame *); + +void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame); + +#endif + diff --git a/user/include/fdt.h b/user/include/fdt.h new file mode 100644 index 000000000..b3293a615 --- /dev/null +++ b/user/include/fdt.h @@ -0,0 +1,37 @@ +#ifndef FDT_H_ +#define FDT_H_ +#include + +#define FDT_MAGIC 0xd00dfeed +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +typedef struct _fdt_header { + uint32_t magic; + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +}fdt_header; + + + +typedef void (*dtb_callback)(char *, uint32_t, uint32_t, uint32_t*); + +void fdt_traverse(fdt_header *, dtb_callback); +void initramfs_callback(char *, uint32_t, uint32_t, uint32_t*); +void show_tree_callback(char *, char *, uint32_t, uint32_t); + +uint32_t big_to_little(uint32_t); + + +#endif + diff --git a/user/include/gpio.h b/user/include/gpio.h new file mode 100644 index 000000000..68b6a39ff --- /dev/null +++ b/user/include/gpio.h @@ -0,0 +1,25 @@ +#ifndef GPIO_H_ +#define GPIO_H_ + +// check https://bob.cs.sonoma.edu/IntroCompOrg-RPi/sec-gpio-pins.html +#define MMIO_BASE 0x3F000000 +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C)) + +#endif \ No newline at end of file diff --git a/user/include/irq.h b/user/include/irq.h new file mode 100644 index 000000000..bf9c92d98 --- /dev/null +++ b/user/include/irq.h @@ -0,0 +1,40 @@ +#ifndef IRQ_H_ +#define IRQ_H_ + +#include + +#define IRQ_BASIC_PENDING ((volatile unsigned int*)(MMIO_BASE+0x0000B200)) +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int*)(MMIO_BASE+0x0000B208)) +#define FIQ_CONTROL ((volatile unsigned int*)(MMIO_BASE+0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int*)(MMIO_BASE+0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(MMIO_BASE+0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(MMIO_BASE+0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int*)(MMIO_BASE+0x0000B224)) +#define CORE0_TIMER_IRQ_CTRL (volatile unsigned int*)0x40000040 +#define CORE0_IRQ_SOURCE (volatile unsigned int*)0x40000060 + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) +#define NON_SECURE_TIMER_IRQ (1 << 1) + +void irq_handler(unsigned long long, unsigned long long); +void Time_interrupt(unsigned long long); +void GPU_interrupt(); + +#define TRANSMIT_HOLDING 0b10 +#define RECEIVE_VALID 0b100 + +void enable_timer_irq(); +void disable_timer_irq(); +void reset_timer_irq(unsigned long long); +void set_long_timer_irq(); +void set_period_timer_irq(); +void enable_irq(); +void disable_irq(); + +#endif \ No newline at end of file diff --git a/user/include/list.h b/user/include/list.h new file mode 100644 index 000000000..6e507addd --- /dev/null +++ b/user/include/list.h @@ -0,0 +1,287 @@ +/* + * Circular doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + * linux kernel: https://github.com/torvalds/linux/blob/master/include/linux/list.h + */ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include +#define WRITE_ONCE(var, val) \ + (*((volatile typeof(val) *)(&(var))) = (val)) + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +struct list_head { + struct list_head *next, *prev; +}; + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + WRITE_ONCE(list->next, list); + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + // if (!__list_add_valid(new, prev, next)) + // return; + + next->prev = new; + new->next = next; + new->prev = prev; + WRITE_ONCE(prev->next, new); +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + WRITE_ONCE(prev->next, next); +} + +static inline void list_del(struct list_head *entry) +{ + // if (!__list_del_entry_valid(entry)) + // return; + + __list_del(entry->prev,entry->next); + INIT_LIST_HEAD(entry); +} + + +/* + * Delete a list entry and clear the 'prev' pointer. + * + * This is a special-purpose list clearing method used in the networking code + * for lists allocated as per-cpu, where we don't want to incur the extra + * WRITE_ONCE() overhead of a regular list_del_init(). The code that uses this + * needs to check the node 'prev' pointer instead of calling list_empty(). + */ +static inline void __list_del_clearprev(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->prev = NULL; +} + + +static inline void list_del_entry(struct list_head *entry) +{ + // if (!__list_del_entry_valid(entry)) + // return; + + __list_del(entry->prev, entry->next); +} + +static inline void _list_del(struct list_head *prev, + struct list_head *target, + struct list_head *next) +{ + // if (!__list_del_entry_valid(entry)) + // return; + + prev->next = next; + next->prev = prev; + INIT_LIST_HEAD(target); +} + +static inline void list_del_first(struct list_head *entry) +{ + // if (!__list_del_entry_valid(entry)) + // return; + + _list_del(entry, entry->next, entry->next->next); +} + + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +/** + * list_is_first -- tests whether @list is the first entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_first(const struct list_head *list, const struct list_head *head) +{ + return list->prev == head; +} + + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_is_head - tests whether @list is the list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next) + + + +/* -----------------------------list_for_each_entry--------------------------- */ + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + void *__mptr = (void *)(ptr); \ + static_assert(__same_type(*(ptr), ((type *)0)->member) || \ + __same_type(*(ptr), void), \ + "pointer type mismatch in container_of()"); \ + ((type *)(__mptr - offsetof(type, member))); }) + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_entry_is_head - test if the entry points to the head of the list + * @pos: the type * to cursor + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_entry_is_head(pos, head, member) \ + (&pos->member == (head)) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = list_next_entry(pos, member)) + + +#endif \ No newline at end of file diff --git a/user/include/mailbox.h b/user/include/mailbox.h new file mode 100644 index 000000000..1c42e1870 --- /dev/null +++ b/user/include/mailbox.h @@ -0,0 +1,43 @@ +#ifndef MAILBOX_H_ +#define MAILBOX_H_ +#include + +/* mailbox registers */ +#define MAILBOX_BASE (MMIO_BASE + 0xB880) // MMIO base address + MAILBOX offset +#define MAILBOX_READ ((volatile unsigned int*)(MAILBOX_BASE)) +#define MAILBOX_POLL ((volatile unsigned int*)(MAILBOX_BASE+0x10)) +#define MAILBOX_SENDER ((volatile unsigned int*)(MAILBOX_BASE+0x14)) +#define MAILBOX_STATUS0 ((volatile unsigned int*)(MAILBOX_BASE+0x18)) +#define MAILBOX_STATUS1 ((volatile unsigned int*)(MAILBOX_BASE+0x38)) +#define MAILBOX_CONFIG ((volatile unsigned int*)(MAILBOX_BASE+0x1C)) +#define MAILBOX_WRITE ((volatile unsigned int*)(MAILBOX_BASE+0x20)) + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 +#define MAILBOX_RESPONSE 0x80000000 + +/* tags */ +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +/* channels */ +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +unsigned int get_board_revision(volatile unsigned int [36]); +unsigned int get_arm_memory(volatile unsigned int [36]); +unsigned int mailbox_call(volatile unsigned int [36], unsigned char); + +#endif diff --git a/user/include/malloc.h b/user/include/malloc.h new file mode 100644 index 000000000..786cde229 --- /dev/null +++ b/user/include/malloc.h @@ -0,0 +1,30 @@ +#ifndef MALLOC_H_ +#define MALLOC_H_ +#include +#include +#define SIMPLE_MALLOC_BASE_START ((volatile unsigned long*)(0x5000000)) +#define SIMPLE_MALLOC_BASE_END ((volatile unsigned long*)(0x7000000)) + +#define MAX_CHUNK_SIZE 11 + +typedef struct { + struct list_head list; +}FreeChunkList; + +typedef struct { + struct list_head list; // size 0x10 +}Chunk; + +void *kmalloc(unsigned int size); +void kfree(void *addr); + +unsigned int find_level(unsigned int); +void *simple_malloc(unsigned long); +void freechunk_list_init(); +void *chunk_alloc(unsigned int); +void chunk_free(void *); +void kmalloc_debug(); + +void print_freechunk_list(); + +#endif \ No newline at end of file diff --git a/user/include/math.h b/user/include/math.h new file mode 100644 index 000000000..cc9fa6b43 --- /dev/null +++ b/user/include/math.h @@ -0,0 +1,7 @@ +#ifndef MATH_H_ +#define MATH_H_ + +unsigned int log(unsigned int ,unsigned int); +unsigned int log2(unsigned int); + +#endif \ No newline at end of file diff --git a/user/include/read.h b/user/include/read.h new file mode 100644 index 000000000..3b782155d --- /dev/null +++ b/user/include/read.h @@ -0,0 +1,14 @@ +#ifndef READ_H_ +#define READ_H_ + +#include + +/* async scan the input until get \r */ +int async_readline(char [MAX_SIZE], int); + +/* scan the input until get \r */ +int readline(char [MAX_SIZE], int); + +/* read n bytes from uart */ +int readnbyte(char [MAX_SIZE], int); +#endif \ No newline at end of file diff --git a/user/include/reboot.h b/user/include/reboot.h new file mode 100644 index 000000000..e18ad4b1e --- /dev/null +++ b/user/include/reboot.h @@ -0,0 +1,12 @@ +#ifndef REBOOT_H_ +#define REBOOT_H_ + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC ((volatile unsigned int*)(0x3F10001c)) +#define PM_WDOG ((volatile unsigned int*)(0x3F100024)) + +void set(long, unsigned int); +void reset(int); +void cancel_reset(); + +#endif \ No newline at end of file diff --git a/user/include/sched.h b/user/include/sched.h new file mode 100644 index 000000000..a2c6464e9 --- /dev/null +++ b/user/include/sched.h @@ -0,0 +1,59 @@ +#ifndef SCHED_H_ +#define SCHED_H_ +#include +#include + +#define MAX_THREAD 0x10 +#define STACK_SIZE 0x1000 + +enum thread_state{ + NOUSE, + WAIT, + RUNNING, + EXIT +}; + +typedef struct _cpu_context{ + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; // frame pointer, stack base address + unsigned long lr; // retrun address + unsigned long sp; // stact pointer +}CpuContext; + + +typedef struct _Thread{ + struct list_head list; + CpuContext ctx; + enum thread_state state; + int id; + void *ustack_addr; + void *kstack_addr; + void *code_addr; // use in exec + unsigned int code_size; // use in exec +}Thread; + +extern Thread* get_current(); +extern void cpu_switch_to(Thread* prev, Thread* next); + +void init_thread_pool_and_head(); +Thread *thread_create(); +void kill_zombie(); +void idle_thread(); +void schedule(); +void kernel_main(); +void delay(unsigned int); +void foo(); + +void print_run_thread(); + + +#endif \ No newline at end of file diff --git a/user/include/shell.h b/user/include/shell.h new file mode 100644 index 000000000..f22dd1728 --- /dev/null +++ b/user/include/shell.h @@ -0,0 +1,36 @@ +#ifndef SHELL_H_ +#define SHELL_H_ +#include + +/* print welcome message*/ +void PrintWelcome(); + +/* print help message*/ +void PrintHelp(); + +/* print unknown command message*/ +void PrintUnknown(char [MAX_SIZE]); + +/* print board revision*/ +void PrintRevision(char [MAX_SIZE]); + +/* print memory info*/ +void PrintMemory(char [MAX_SIZE]); + +/* uart booting */ +void Bootimg(char [MAX_SIZE]); + +/* reboot device */ +void Reboot(); + +/* Main Shell */ +void ShellLoop(); + +void Ls(); +void Cat(char [MAX_SIZE]); +void Exec(char [MAX_SIZE]); + +void SetTimeOut(char [MAX_SIZE]); +void TestTimeOut(char [MAX_SIZE]); + +#endif \ No newline at end of file diff --git a/user/include/stddef.h b/user/include/stddef.h new file mode 100644 index 000000000..7b4f2876d --- /dev/null +++ b/user/include/stddef.h @@ -0,0 +1,9 @@ +#ifndef STDDEF_H_ +#define STDDEF_H_ + +#define NULL (void *)0x0 +#define uint32_t unsigned int +#define unit64_t unsigned long long + + +#endif \ No newline at end of file diff --git a/user/include/stdint.h b/user/include/stdint.h new file mode 100644 index 000000000..81dfc7257 --- /dev/null +++ b/user/include/stdint.h @@ -0,0 +1,18 @@ +#ifndef FDT_H_ +#define FDT_H_ + +/* Unsigned. */ +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +#ifndef __uint32_t_defined +typedef unsigned int uint32_t; +# define __uint32_t_defined +#endif +#if __WORDSIZE == 64 +typedef unsigned long int uint64_t; +#else +__extension__ +typedef unsigned long long int uint64_t; +#endif + +#endif \ No newline at end of file diff --git a/user/include/string.h b/user/include/string.h new file mode 100644 index 000000000..4500c42c9 --- /dev/null +++ b/user/include/string.h @@ -0,0 +1,50 @@ +#ifndef STRING_H_ +#define STRING_H_ + +#define MAX_SIZE 512 + + +enum print_type{ + UITOHEX, + UITOA, + ITOA +}; + +/* Compare S1 and S2, returning less than, equal to or + greater than zero if S1 is lexicographically less than, + equal to or greater than S2. */ +int strcmp(const char *, const char *); +int strncmp(const char *, const char *, unsigned int); + +/* string length */ +unsigned int strlen(const char *); + +/* reverse string */ +void reverse_buf(char *); + +/* int to string */ +void itoa(char *, int); + +/* uint to string */ +void uitoa(char *, unsigned int); + +/* uint to hex string */ +void uitohex(char *, unsigned int); +void print_string(enum print_type type, char *text , unsigned long long num, int println); + +/* array to int */ +int atoi(const char *); + +/* array to uint */ +unsigned int atoui(const char *); + +/* hex string to unsigned int */ +unsigned int hextoui(const char *, unsigned int); + +void memcpy(char *, const char *, unsigned int); +void memset(char *, const char, unsigned int); +void strcpy(char *, const char *); +void strcat(char *, const char *); +char *strchr(const char *, int); + +#endif \ No newline at end of file diff --git a/user/include/syscall.h b/user/include/syscall.h new file mode 100644 index 000000000..9b6aa0612 --- /dev/null +++ b/user/include/syscall.h @@ -0,0 +1,31 @@ +#ifndef SYSCALL_H_ +#define SYSCALL_H_ +#include + +typedef struct _TrapFrame{ + unsigned long long x[31]; + unsigned long long sp_el0; + unsigned long long elr_el1; + unsigned long long spsr_el1; +}TrapFrame; + + +void sys_getpid(TrapFrame *); +void sys_uart_read(TrapFrame *); +void sys_uart_write(TrapFrame *); +void sys_exec(TrapFrame *); +void sys_fork(TrapFrame *); +void sys_exit(TrapFrame *); +void sys_mbox_call(TrapFrame *); +void sys_kill(TrapFrame *); + +int do_getpid(); +int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); +void *load_program(file_info *fileInfo); +void do_fork(); +void do_exit(); +void do_kill(); + +int kernel_exec(char *name); + +#endif \ No newline at end of file diff --git a/user/include/task.h b/user/include/task.h new file mode 100644 index 000000000..51ef27e10 --- /dev/null +++ b/user/include/task.h @@ -0,0 +1,22 @@ +#ifndef TASK_H_ +#define TASK_H_ +#include +#include +typedef void (*Handler)(); + + + + + +typedef struct _Task{ + struct list_head list; + unsigned int priority; + Handler handler; +}Task; + + +void init_task_head(); +void add_task(Handler, unsigned int); +void do_task(); + +#endif \ No newline at end of file diff --git a/user/include/timer.h b/user/include/timer.h new file mode 100644 index 000000000..2478c6ce6 --- /dev/null +++ b/user/include/timer.h @@ -0,0 +1,38 @@ +#ifndef TIMER_H_ +#define TIMER_H_ + +#include +#include + +// Timer interrupt +#define TIMER_CS ((volatile unsigned int*)(MMIO_BASE+0x00003000)) +#define TIMER_CLO ((volatile unsigned int*)(MMIO_BASE+0x00003004)) +#define TIMER_CHI ((volatile unsigned int*)(MMIO_BASE+0x00003008)) +#define TIMER_C0 ((volatile unsigned int*)(MMIO_BASE+0x0000300C)) +#define TIMER_C1 ((volatile unsigned int*)(MMIO_BASE+0x00003010)) +#define TIMER_C2 ((volatile unsigned int*)(MMIO_BASE+0x00003014)) +#define TIMER_C3 ((volatile unsigned int*)(MMIO_BASE+0x00003018)) + +#define TIMER_CS_M0 (1 << 0) +#define TIMER_CS_M1 (1 << 1) +#define TIMER_CS_M2 (1 << 2) +#define TIMER_CS_M3 (1 << 3) + + +typedef void (*TimerTask)(void *); + +typedef struct _Timer{ + TimerTask task; + unsigned long long expired_time; + void *args; + struct _Timer *next; + struct _Timer *prev; +}Timer; + +void add_timer(TimerTask, unsigned long long, void *); +void timeout_print(void *); +void sched_timeout(void *); +void timer_interrupt_handler(); +void timer_interrupt_handler_el0(); + +#endif \ No newline at end of file diff --git a/user/include/uart.h b/user/include/uart.h new file mode 100644 index 000000000..c3c5eae70 --- /dev/null +++ b/user/include/uart.h @@ -0,0 +1,53 @@ +#ifndef UART_H_ +#define UART_H_ +#include + +#define AUX_BASE (MMIO_BASE + 0x00215000) //MMIO base address + AUX offset +#define AUX_ENABLE ((volatile unsigned int*)(AUX_BASE+0x4)) +#define AUX_MU_IO ((volatile unsigned int*)(AUX_BASE+0x40)) +#define AUX_MU_IER ((volatile unsigned int*)(AUX_BASE+0x44)) +#define AUX_MU_IIR ((volatile unsigned int*)(AUX_BASE+0x48)) +#define AUX_MU_LCR ((volatile unsigned int*)(AUX_BASE+0x4C)) +#define AUX_MU_MCR ((volatile unsigned int*)(AUX_BASE+0x50)) +#define AUX_MU_LSR ((volatile unsigned int*)(AUX_BASE+0x54)) +#define AUX_MU_MSR ((volatile unsigned int*)(AUX_BASE+0x58)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(AUX_BASE+0x5C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(AUX_BASE+0x60)) +#define AUX_MU_STAT ((volatile unsigned int*)(AUX_BASE+0x64)) +#define AUX_MU_BAUD ((volatile unsigned int*)(AUX_BASE+0x68)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000B210)) + + +/* Display a char */ +void uart_init(); + +/* Send a character */ +void uart_putc(unsigned int); + +/* Receive a character */ +char uart_getc(); +/* Recv a new char in read buffer */ +void recv_interrupt_handler(); +/* Async receive a character */ +char async_uart_getc(); + +/* Display a string */ +void uart_puts(char*); + +/* Async display a string */ +void async_uart_puts(char *); + +/* Transmit a character */ +void tran_interrupt_handler(); + +/* Async send a character */ +void async_uart_putc(unsigned int); + +void uart_nbyte(char *, unsigned int); + +void enable_AUX_MU_IER_r(); +void enable_AUX_MU_IER_w(); +void disable_AUX_MU_IER_r(); +void disable_AUX_MU_IER_w(); + +#endif \ No newline at end of file diff --git a/user/include/user_syscall.h b/user/include/user_syscall.h new file mode 100644 index 000000000..644f1bfa9 --- /dev/null +++ b/user/include/user_syscall.h @@ -0,0 +1,26 @@ +#ifndef USER_SYSCALL_H_ +#define USER_SYSCALL_H_ + +#define GET_PID 0 +#define UART_READ 1 +#define UART_WRITE 2 +#define EXEC 3 +#define FORK 4 +#define EXIT 5 +#define MBOX_CALL 6 +#define KILL 7 + +#endif + +#ifndef __ASSEMBLY__ + +extern int getpid(); +extern unsigned int uartread(char buf[], unsigned int size); +extern unsigned int uartwrite(const char buf[], unsigned int size); +extern int exec(const char *name, char *const argv[]); +extern int fork(); +extern void exit(); +extern int mbox_call(unsigned char ch, unsigned int *mbox); +extern void kill(int pid); + +#endif \ No newline at end of file diff --git a/user/initramfs.cpio b/user/initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..a8b57f1f106d5c45d46986432909de200dc36068 GIT binary patch literal 49664 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 WHGNj57?~cE7X6olV`+4D+W!M72^C}j literal 0 HcmV?d00001 diff --git a/user/initramfs/me b/user/initramfs/me new file mode 100644 index 000000000..3e00fccbb --- /dev/null +++ b/user/initramfs/me @@ -0,0 +1,27 @@ + .^^^:^B#####################7:^^^: + .~~^?GGGGP^.....................JGGGGY^~^. + !JPPP? . .. ~PPP?! + ?YY! 7YJJ. + YP?7 7?5Y. + JP?7. !?PJ + .:P&^ :#G:. + 7#!.. .!#7 + ?@! .^^^^^^^^^^: :^^^^^^^^^~: !@? + ?@! !7!5@@@@@@@@@@P!: :7P@@@@@@@@@@G!7! !@? + ?@! .7Y&@@@@@@@@@@@@@@@7 7@@@@@@@@@@@@@@@@YJ. !@? + ?@! .B@@@@@@@@J77?&@@@@7 7@@@@@Y775@@@@@@@@@^ !@? + ?@! .B@@@@@@@@~ .&@@@@7 .. 7@@@@@! 7@@@@@@@@&^ !@? + .^P#^ :~B&#&@@@#BB#@@@5^. Y##Y .^5@@@#BB#@@@&##B~^ :GP^. + P&~. ...?&######@@&J .:G@@G:: J&@@&#####&Y... .^#P + ..B&^ ..:!777P5:. Y&&@@@&B. .:5GJ77~... B#:. + !?YJ: ^J~ 7YYJ. 5@@@@@@#. 7YY7 ^J~ ?YJ! + G@^ 7@#Y: ~??????7 7@#Y^ :&G + P@^ .Y55G@B?Y55Y. J55G@@@GY. :&G + .....G@~. .~!~Y@5.B&!!PPPGGGPPPGGPPPPPGGGPPPGG7J@@&P~~~. .^&&PPPPG7. + ^!GGGGP&@#B^ .:7G&#^:^^:Y@J^^^G&~^^^^J@Y:^:G@!7#5:. G##P^^^^:?P~: + :!BY.....5#@@!: .P#BG~^:J@! P#. 7@? :^G@#Y. ^^GG: .:^~PP. + .BB. ..YP&&?^ ...:Y55B@5!77B&?77775@57Y5PP^ ~755:. ^!5@GY?7 + !JJJ????J~ ?YYYJJ?! ~YYYYYYYYYYYYYYYY: :J??#@: :?G@@@P?YYJ? + 7Y?Y@@@@@BJJJJ?. ^YG@#JJJJ? !JJJ?P@@@P?. .?5@@@@@@@: JYJ? + 75&@@@@@@@@@@@@PY. :75@@@@@P555555555555555555555#@@@@B777. JP@@@@@@@@@PJ 7J5~ + \ No newline at end of file diff --git a/user/initramfs/test1 b/user/initramfs/test1 new file mode 100644 index 000000000..893ba5540 --- /dev/null +++ b/user/initramfs/test1 @@ -0,0 +1 @@ +this is text 1 \ No newline at end of file diff --git a/user/initramfs/test2.txt b/user/initramfs/test2.txt new file mode 100644 index 000000000..67a1781b3 --- /dev/null +++ b/user/initramfs/test2.txt @@ -0,0 +1,2 @@ +test2222 +test2222 \ No newline at end of file diff --git a/user/initramfs/test3.cc b/user/initramfs/test3.cc new file mode 100644 index 000000000..95b5c775a --- /dev/null +++ b/user/initramfs/test3.cc @@ -0,0 +1,2 @@ +test3333 +test3333 \ No newline at end of file diff --git a/user/initramfs/user1.S b/user/initramfs/user1.S new file mode 100644 index 000000000..838323763 --- /dev/null +++ b/user/initramfs/user1.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b \ No newline at end of file diff --git a/user/src/allocator.c b/user/src/allocator.c new file mode 100644 index 000000000..a6435b824 --- /dev/null +++ b/user/src/allocator.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include +#include +#include + +Frame *frames; +Buddy *buddy_list; +// Frame frames[FRAME_NUM]; +// Buddy buddy_list[MAX_BUDDY_ORDER+1]; + +extern unsigned long long _start; +extern unsigned long long _end; +extern unsigned long long CPIO_BASE_START; +extern unsigned long long CPIO_BASE_END; + +extern FreeChunkList *freechunk_list; + +void all_allocator_init(){ + startup_alloc(); + freechunk_list_init(); + frames_init(); + memory_init(); + buddy_init(); +} + +void startup_alloc(){ + frames = (Frame *)simple_malloc(sizeof(Frame) * FRAME_NUM); + buddy_list = (Buddy *)simple_malloc(sizeof(Buddy) * (MAX_BUDDY_ORDER+1)); + freechunk_list = (FreeChunkList *)simple_malloc(sizeof(FreeChunkList) * MAX_CHUNK_SIZE); + + print_string(UITOHEX, "[*] Startup Alloca -> frames addr = 0x", (unsigned long long)frames, 0); + print_string(UITOHEX, " | buddy_list addr = 0x", (unsigned long long)buddy_list, 0); + print_string(UITOHEX, " | freechunk_list addr = 0x", (unsigned long long)freechunk_list, 1); +} + +void frames_init(){ + for(unsigned int idx = 0; idx < FRAME_NUM; idx++){ + INIT_LIST_HEAD(&frames[idx].list); + frames[idx].idx = idx; + frames[idx].free = 1; + frames[idx].order = 0; + frames[idx].chunk_level = -1; + } +} + +void memory_init(){ + /* Spin tables for multicore boot (0x0000 - 0x1000) */ + uart_puts("[*] Memory Reserve(Spin tables) -> "); + memory_reserve((void *)0x0, (void *)0x1000); + + /* Kernel image in the physical memory*/ + unsigned long long stack = (unsigned long long)&_start - 0x10000; + uart_puts("[*] Memory Reserve(Kernel image) -> "); + memory_reserve((void *)stack, (void *)&_end); + + /* Kernel image in the physical memory*/ + uart_puts("[*] Memory Reserve(Initramfs) -> "); + memory_reserve((void *)CPIO_BASE_START, (void *)CPIO_BASE_END); + + /* Simple malloc in the physical memory*/ + uart_puts("[*] Memory Reserve(Simple malloc) -> "); + memory_reserve((void *)SIMPLE_MALLOC_BASE_START, (void *)SIMPLE_MALLOC_BASE_END); + +} + +void memory_reserve(void *start, void *end){ + unsigned int start_idx = addr_to_frame_idx(start); + unsigned int end_idx = addr_to_frame_idx(end); + + /* align end_idx */ + unsigned long long end_addr = (unsigned long long)end; + end_idx = (end_addr % FRAME_SIZE == 0) ? end_idx : end_idx + 1; + + print_string(UITOHEX, "start addr: 0x", start_idx * FRAME_SIZE, 0); + print_string(UITOHEX, " | end addr: 0x", end_idx * FRAME_SIZE, 1); + + + for(; start_idx < end_idx; start_idx++){ + frames[start_idx].free = 0; + frames[start_idx].order = -1; + } +} + +void buddy_init(){ + for(unsigned int i = 0; i <= MAX_BUDDY_ORDER; i++){ + INIT_LIST_HEAD(&buddy_list[i].list); + } + + for(unsigned int idx = 0; idx < FRAME_NUM; idx++){ + /* + * reserve frames -> don't do merge + * merge already -> don't merge + */ + if(frames[idx].free == 0 || frames[idx].order == -1) continue; + Frame *target_frame = &frames[idx]; + Frame *buddy_frame = find_buddy_frame(target_frame, target_frame->order); + + if( buddy_frame == NULL || + buddy_frame->free == 0 || + buddy_frame->order != target_frame->order){ + continue; + } + + while(buddy_frame->free == 1 && buddy_frame->order == target_frame->order){ + if(target_frame->idx > buddy_frame->idx){ + buddy_frame->order++; + target_frame->order = -1; + target_frame = buddy_frame; + } + else{ + target_frame->order++; + buddy_frame->order = -1; + } + + buddy_frame = find_buddy_frame(target_frame, target_frame->order); + if(buddy_frame == NULL) break; + } + } + + for(unsigned int idx = 0; idx < FRAME_NUM; idx++){ + if(frames[idx].free == 0 || frames[idx].order == -1) continue; + list_add_tail(&frames[idx].list, &buddy_list[frames[idx].order].list); + } + + // print_buddy_list(); +} + + + + +void *buddy_alloc(unsigned int size){ + unsigned int use_frames = (size % FRAME_SIZE == 0) ? (size / FRAME_SIZE) : (size / FRAME_SIZE) + 1; + int use_order = (int)log2(use_frames); + for(unsigned int i = use_order; i <= MAX_BUDDY_ORDER; i++){ + if(!list_empty(&buddy_list[i].list)){ + Frame *alloca_frame = (Frame *)buddy_pop(&buddy_list[i], use_order); + unsigned long long alloca_addr = alloca_frame->idx * FRAME_SIZE + BUDDY_ADDR_START; + // print_use_frame(size, alloca_frame->idx, use_frames, use_order); + // print_buddy_list(); + // memset((void *)alloca_addr, '\0', (1 << alloca_frame->order) * FRAME_SIZE); + return (void *)alloca_addr; + } + } + print_string(UITOHEX, "[x] Allocate Size: 0x", size, 1); + uart_puts("[x] No enough memory!!!!\n"); + // print_string(UITOHEX, "[*] No enough memory!!!!", left_frame->idx, 0); + return NULL; +} + + +void *buddy_pop(Buddy *buddy_list, int use_order){ + Frame *target_frame = (Frame *)buddy_list->list.next; + list_del(&target_frame->list); // pop the free entry + return release_redundant(target_frame, use_order); +} + +Frame *find_buddy_frame(Frame *me, int order){ + /* + * 0b11(left_frame->me) xor 0b10(order) = 0b01(buddy_frame) + * 0b01(right_frame->me) xor 0b10(order) = 0b11(buddy_frame) + */ + if(order == MAX_BUDDY_ORDER) + return NULL; + return &frames[me->idx ^ (1 << (unsigned int)order)]; +} + +void *release_redundant(Frame *left_frame, int use_order){ + while(left_frame->order > use_order){ + int samll_order = left_frame->order - 1; + Frame *right_frame = find_buddy_frame(left_frame, samll_order); + right_frame->order = samll_order; + left_frame->order = samll_order; + list_add(&frames[right_frame->idx].list, &buddy_list[samll_order].list); + // print_string(UITOHEX, "[-] Alloc Buddy -> Split: Left_Addr = 0x", left_frame->idx * FRAME_SIZE, 0); + // print_string(UITOHEX, " | Right_Addr = 0x", right_frame->idx * FRAME_SIZE, 0); + // print_string(UITOA, " | order = ", left_frame->order, 1); + } + left_frame->free = 0; + return left_frame; +} + +int addr_to_frame_idx(void *addr){ + unsigned long long offset = (unsigned long long)addr; + unsigned int idx = (unsigned int)(offset / FRAME_SIZE); + return idx; +} + +void buddy_free(void *addr){ + unsigned int idx = addr_to_frame_idx(addr); + Frame *target_frame = &frames[idx]; + Frame *buddy_frame = find_buddy_frame(target_frame, target_frame->order); + int first = 1; + /* + * buddy_frame is not free + * or buddy_frame's order is not same as target_frame's order + * then they are not buddy, cannot merge + */ + + if(buddy_frame == NULL || buddy_frame->free == 0 || buddy_frame->order != target_frame->order){ + // print_string(UITOHEX, "[*] Free Buddy -> Free Addr: 0x", (unsigned long long)addr, 0); + // print_string(UITOA, " | order = ", target_frame->order, 1); + + target_frame->free = 1; + + // no buddy_frame = the init status + if(buddy_frame != NULL) + list_add(&frames[target_frame->idx].list, &buddy_list[target_frame->order].list); + // print_buddy_list(); + return; + } + + while(buddy_frame->free == 1 && buddy_frame->order == target_frame->order){ + if(first){ + // print_string(UITOHEX, "[*] Free Buddy -> Free Addr: 0x", (unsigned long long)addr, 1); + first = 0; + } + /* buddy cannot be allocated, it will be merged */ + list_del(&frames[buddy_frame->idx].list); + + if(target_frame->idx > buddy_frame->idx){ + // print_string(UITOHEX, "[+] Free Buddy -> Merge: Left_Addr = 0x", buddy_frame->idx * FRAME_SIZE, 0); + // print_string(UITOHEX, " | Right_Addr = 0x", target_frame->idx * FRAME_SIZE, 0); + // print_string(UITOA, " | order = ", buddy_frame->order+1, 1); + buddy_frame->order++; + target_frame->order = -1; + target_frame = buddy_frame; + } + else{ + // print_string(UITOHEX, "[+] Free Buddy -> Merge: Left_Addr = 0x", target_frame->idx * FRAME_SIZE, 0); + // print_string(UITOHEX, " | Right_Addr = 0x", buddy_frame->idx * FRAME_SIZE, 0); + // print_string(UITOA, " | order = ", target_frame->order+1, 1); + target_frame->order++; + buddy_frame->order = -1; + } + + buddy_frame = find_buddy_frame(target_frame, target_frame->order); + if(buddy_frame == NULL) break; + + } + target_frame->free = 1; + list_add(&frames[target_frame->idx].list, &buddy_list[target_frame->order].list); + // print_buddy_list(); +} + + + +void print_use_frame(unsigned int size, unsigned int frame_idx, unsigned int use_frames,int use_order){ + print_string(UITOHEX, "[*] Alloc Buddy -> Allocate Size: 0x", size, 0); + print_string(UITOHEX, " | Frame Addr: 0x", frame_idx * FRAME_SIZE, 0); + print_string(UITOA, " | Use Order: ", use_order, 0); + print_string(UITOA, " | Use Frames: ", use_frames, 1); +} + +void print_frame_info(Frame *frame){ + print_string(UITOA, "idx: ", frame->idx, 0); + print_string(ITOA, " - order: ", frame->order, 1); +} + +void print_buddy_list(){ + struct list_head *pos; + for(unsigned int i = 0; i <= MAX_BUDDY_ORDER; i++){ + print_string(UITOA, "", i, 0); + uart_puts("\t:\t"); + unsigned int first = 1; + list_for_each(pos, &buddy_list[i].list){ + Frame *tmp = (Frame *)pos; + if(first){ + print_string(UITOHEX, "0x", tmp->idx * FRAME_SIZE, 0); + first = 0; + } + else print_string(UITOHEX, " -> 0x", tmp->idx * FRAME_SIZE, 0); + } + uart_puts("\n"); + } +} + +void buddy_debug(){ + void *addr1 = buddy_alloc(0x2000); + void *addr2 = buddy_alloc(0x2000); + void *addr3 = buddy_alloc(0x2000); + void *addr4 = buddy_alloc(0x2000); + void *addr5 = buddy_alloc(0x1000); + void *addr6 = buddy_alloc(0x1000); + void *addr7 = buddy_alloc(0x1000); + void *addr8 = buddy_alloc(0x1000); + + buddy_free(addr1); + buddy_free(addr2); + buddy_free(addr3); + buddy_free(addr4); + + buddy_free(addr5); + buddy_free(addr6); + buddy_free(addr7); + buddy_free(addr8); +} + diff --git a/user/src/cpio.c b/user/src/cpio.c new file mode 100644 index 000000000..e5c8275d4 --- /dev/null +++ b/user/src/cpio.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include + +/* +The pathname is followed by NUL bytes so that the total size of the fixed +header plus pathname is a multiple of four. Likewise, the file data is +padded to a multiple of four bytes. Note that this format supports only +4 gigabyte files (unlike the older ASCII format, which supports 8 giga- +byte files). +*/ +unsigned int padding(unsigned int size){ + unsigned int padding = size % 4; + if(padding == 0) return size; + else return size + 4 - padding; +} + +void parse_cpio_header(cpio_newc_header *header, file_info *info){ + if(strncmp(header->c_magic, "070701", 6) != 0) return; + info->filename = (char *)(header + 1); + info->filename_size = hextoui(header->c_namesize, 8); + info->datasize = hextoui(header->c_filesize, 8); + info->data = (char *)header + padding(sizeof(cpio_newc_header)+info->filename_size); +} + +void ls() { + cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + while(1){ + file_info info; + parse_cpio_header(header, &info); + if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + uart_nbyte(info.filename, info.filename_size); + uart_puts("\n"); + header = (cpio_newc_header *)(info.data + padding(info.datasize)); + } +} + + +void cat(char *thefilename) { + cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + while(1){ + file_info info; + parse_cpio_header(header, &info); + + if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + else if(strncmp(info.filename, thefilename, info.filename_size) == 0){ + uart_nbyte(info.data, info.datasize); + uart_puts("\n"); + return; + } + + header = (cpio_newc_header *)(info.data + padding(info.datasize)); + } +} + + +unsigned long findDataAddr(const char *thefilename) { + cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + while(1){ + file_info info; + parse_cpio_header(header, &info); + + if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + else if(strncmp(info.filename, thefilename, info.filename_size) == 0){ + return (unsigned long)info.data; + } + + header = (cpio_newc_header *)(info.data + padding(info.datasize)); + } + return 0; +} + +file_info cpio_find_file_info(const char *thefilename) { + cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + file_info info; + while(1){ + parse_cpio_header(header, &info); + + if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + else if(strncmp(info.filename, thefilename, info.filename_size) == 0){ + return info; + } + + header = (cpio_newc_header *)(info.data + padding(info.datasize)); + } + info.filename = 0; + return info; + +} + +void run(unsigned long runAddr){ + unsigned long *user_stack = (unsigned long *)kmalloc(0x1000); + set_period_timer_irq(); + asm volatile( + "mov x0, 0x0\n\t" + "msr spsr_el1, x0\n\t" + "msr elr_el1, %0\n\t" + "msr sp_el0, %1\n\t" + "eret\n\t" + ::"r"(runAddr), + "r"(user_stack) + : "x0" + ); + uart_puts("run done\n"); +} + + + + diff --git a/user/src/ctx_switch.S b/user/src/ctx_switch.S new file mode 100644 index 000000000..b884cb665 --- /dev/null +++ b/user/src/ctx_switch.S @@ -0,0 +1,28 @@ +.section ".text" + +.globl cpu_switch_to +cpu_switch_to: + stp x19, x20, [x0, 16 * 1] + stp x21, x22, [x0, 16 * 2] + stp x23, x24, [x0, 16 * 3] + stp x25, x26, [x0, 16 * 4] + stp x27, x28, [x0, 16 * 5] + stp fp, lr, [x0, 16 * 6] + mov x9, sp + str x9, [x0, 16 * 7] + + ldp x19, x20, [x1, 16 * 1] + ldp x21, x22, [x1, 16 * 2] + ldp x23, x24, [x1, 16 * 3] + ldp x25, x26, [x1, 16 * 4] + ldp x27, x28, [x1, 16 * 5] + ldp fp, lr, [x1, 16 * 6] + ldr x9, [x1, 16 * 7] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/user/src/exc.c b/user/src/exc.c new file mode 100644 index 000000000..7dd9ad679 --- /dev/null +++ b/user/src/exc.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +void exception_handler( unsigned long long esr, + unsigned long long elr, + unsigned long long spsr, + TrapFrame *trapFrame){ + + /* + * EC, bits [31:26] -> Exception Class. Indicates the reason for the exception that this register holds information about. + * ec = 0b010101: SVC instruction execution in AArch64 state. + * https://developer.arm.com/documentation/ddi0601/2021-12/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1-?lang=en + */ + unsigned int ec = esr >> 26; + if(ec == 0b010101){ + unsigned int syscall_id = trapFrame->x[8]; + syscall_handler(syscall_id, trapFrame); + } + else{ + uart_puts("---------Exception Handler---------\n[*] Exception type: Synchronous\n"); + print_string(UITOHEX, "[*] spsr_el1: 0x", spsr, 1); + print_string(UITOHEX, "[*] elr_el1: 0x", elr, 1); + print_string(UITOHEX, "[*] esr_el1: 0x", esr, 1); + } + +} + + +void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ + switch (syscall_id) + { + case GET_PID: + sys_getpid(trapFrame); + break; + case UART_READ: + sys_uart_read(trapFrame); + break; + case UART_WRITE: + sys_uart_write(trapFrame); + break; + case EXEC: + sys_exec(trapFrame); + break; + case FORK: + sys_fork(trapFrame); + break; + case EXIT: + sys_exit(trapFrame); + break; + case MBOX_CALL: + sys_mbox_call(trapFrame); + break; + case KILL: + sys_kill(trapFrame); + break; + + default: + break; + } +} diff --git a/user/src/fdt.c b/user/src/fdt.c new file mode 100644 index 000000000..6b1420aa7 --- /dev/null +++ b/user/src/fdt.c @@ -0,0 +1,82 @@ +#include +#include +#include + +extern unsigned long long CPIO_BASE_START; +extern unsigned long long CPIO_BASE_END; + + +uint32_t big_to_little(uint32_t big){ + uint32_t little = 0; + little |= (big & 0xFF000000) >> 24; + little |= (big & 0x00FF0000) >> 8; + little |= (big & 0x0000FF00) << 8; + little |= (big & 0x000000FF) << 24; + return little; +} + + + +// { +// chosen { +// linux,initrd-start = <0x82000000>; +// linux,initrd-end = <0x82800000>; +// }; +// }; + +void initramfs_callback(char * prop_name, uint32_t token_type, uint32_t len, uint32_t *struct_addr){ + if(token_type == FDT_PROP){ + if(strcmp("linux,initrd-start", prop_name) == 0){ + CPIO_BASE_START = (unsigned long long)big_to_little(*(struct_addr+2)); + print_string(UITOHEX, "[*] CPIO_BASE_START: 0x", CPIO_BASE_START, 1); + } + else if(strcmp("linux,initrd-end", prop_name) == 0){ + CPIO_BASE_END = (unsigned long)big_to_little(*(struct_addr+2)); + print_string(UITOHEX, "[*] CPIO_BASE_END: 0x", CPIO_BASE_END, 1); + } + } +} + +void fdt_traverse(fdt_header *header, dtb_callback the_callback){ + if(big_to_little(header->magic) != FDT_MAGIC){ + uart_puts("[x] fdt_traverse: wrong magic number\n"); + return; + } + uint32_t *struct_addr = (uint32_t *)((char *)header + big_to_little(header->off_dt_struct)); + char *strings_addr = (char *)header + big_to_little(header->off_dt_strings); + char *node_name; + + while(1){ + uint32_t token_type = big_to_little(*struct_addr); + if(token_type == FDT_END){ + break; + } + + struct_addr += 1; // skip token type + if(token_type == FDT_BEGIN_NODE){ + node_name = (char *)(struct_addr); // chosen + int node_name_size = strlen(node_name) + 1; // The node’s name as a '\0' string + struct_addr += padding(node_name_size) / 4; + } + else if(token_type == FDT_PROP){ + uint32_t len = big_to_little(*(uint32_t *)struct_addr); + uint32_t nameoff = big_to_little(*(uint32_t *)(struct_addr + 1)); + char *prop_name = strings_addr + nameoff; + the_callback(prop_name, token_type, len, struct_addr); + + struct_addr += 2; + struct_addr += padding(len) / 4; + } + else if(token_type == FDT_NOP || token_type == FDT_END_NODE){ + // do something + } + else{ + uart_puts("fdt_traverse: wrong token type\n"); + return; + } + } + +} + + + diff --git a/user/src/irq.c b/user/src/irq.c new file mode 100644 index 000000000..b1e283ca0 --- /dev/null +++ b/user/src/irq.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include + + +void irq_handler(unsigned long long what, unsigned long long spsr){ + // char buf[10]; + // uart_sputs("---------IRQ Handler---------\n"); + if(*CORE0_IRQ_SOURCE & 0x2) Time_interrupt(spsr); + else if(*CORE0_IRQ_SOURCE & 0x100) GPU_interrupt(); +} + +void Time_interrupt(unsigned long long spsr){ + unsigned long long frq = 0; + asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); + spsr &= 0b1111; + if(spsr == 0x0){ + disable_timer_irq(); + add_task(timer_interrupt_handler_el0, 1); + do_task(); + } + else{ + disable_timer_irq(); + add_task(timer_interrupt_handler, 1); + do_task(); + // timer_interrupt_handler(); + } +} + + +/* + AUX_MU_IIR: + On read this register shows the interrupt ID bit Interrupt ID + bit[2:1] + 00 : No interrupts + 01 : Transmit holding register empty + 10 : Receiver holds valid byte + 11 : + + AUX_MU_IER_REG: + bit 1: Enable Transmit interrupt + bit 0: Enable Receive interrupt +*/ + + +void GPU_interrupt(){ + // uart_sputs("GPU interrupt\n"); + if(*AUX_MU_IIR & TRANSMIT_HOLDING){ // Transmit interrupt + disable_AUX_MU_IER_w(); + add_task(tran_interrupt_handler, 3); + do_task(); + // tran_interrupt_handler(); + } + else if(*AUX_MU_IIR & RECEIVE_VALID){ // Receive interrupt + disable_AUX_MU_IER_r(); + disable_AUX_MU_IER_w(); + add_task(recv_interrupt_handler, 2); + do_task(); + // recv_interrupt_handler(); + } + else{ + uart_puts("[*] AUX_MU_IIR: No interrupts\n"); + } +} + + +void enable_timer_irq(){ + asm volatile( + "msr cntp_ctl_el0, %0\n\t" + ::"r"(1) + ); + *CORE0_TIMER_IRQ_CTRL |= 2; +} + +void disable_timer_irq(){ + asm volatile( + "msr cntp_ctl_el0, %0\n\t" + ::"r"(0) + ); + *CORE0_TIMER_IRQ_CTRL &= ~2; +} + +void reset_timer_irq(unsigned long long expired_time){ + asm volatile( + "msr cntp_cval_el0, %0\n\t" + ::"r"(expired_time) + ); +} + +void set_long_timer_irq(){ + asm volatile( + "mov x1, 0xfffffffffffffff\n\t" // set a large second + "msr cntp_cval_el0, x1\n\t" + :::"x1" + ); +} + +void set_period_timer_irq(){ + asm volatile( + "mrs x1, cntfrq_el0\n\t" + "msr cntp_tval_el0, x1\n\t" + :::"x1" + ); +} + +void enable_irq(){ + asm volatile("msr DAIFClr, 0xf"); +} + +void disable_irq(){ + asm volatile("msr DAIFSet, 0xf"); +} + diff --git a/user/src/mailbox.c b/user/src/mailbox.c new file mode 100644 index 000000000..070c63c6c --- /dev/null +++ b/user/src/mailbox.c @@ -0,0 +1,61 @@ +#include +#include +#include + +unsigned int get_board_revision(volatile unsigned int mbox[36]){ + mbox[0] = 7 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_BOARD_REVISION; // tag identifier + mbox[3] = 4; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // board revision + // tags end + mbox[6] = END_TAG; + + return mailbox_call(mbox, MAILBOX_CH_PROP); +} + +unsigned int get_arm_memory(volatile unsigned int mbox[36]){ + mbox[0] = 8 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_ARM_MEMORY; // tag identifier + mbox[3] = 8; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // base address in bytes + mbox[6] = 0; // size in bytes + // tags end + mbox[7] = END_TAG; + + return mailbox_call(mbox, MAILBOX_CH_PROP); + +} + +/* +1. Combine the message address (upper 28 bits) with channel number (lower 4 bits) +2. Check if Mailbox 0 status register’s full flag is set. +3. If not, then you can write to Mailbox 1 Read/Write register. +4. Check if Mailbox 0 status register’s empty flag is set. +5. If not, then you can read from Mailbox 0 Read/Write register. +6. Check if the value is the same as you wrote in step 1. +*/ +unsigned int mailbox_call(volatile unsigned int mbox[36], unsigned char ch){ + /* Combine the message address (upper 28 bits) with channel number (lower 4 bits) */ + unsigned int req = (((unsigned int)((unsigned long)mbox) & (~0xF)) | (ch & 0xF)); + /* wait until we can write to the mailbox */ + while(*MAILBOX_STATUS1 & MAILBOX_FULL){asm volatile("nop");} + *MAILBOX_WRITE = req; + + /* now wait for the response */ + while(1){ + /* wait the response signal */ + while(*MAILBOX_STATUS0 & MAILBOX_EMPTY){asm volatile("nop");} + + /* read the response to compare the our req and request_code */ + if(req == *MAILBOX_READ){ + return mbox[1] == MAILBOX_RESPONSE; + } + } + return 0; +} \ No newline at end of file diff --git a/user/src/main.c b/user/src/main.c new file mode 100644 index 000000000..466c35f3c --- /dev/null +++ b/user/src/main.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include + +int readline2(char buf[MAX_SIZE], int size){ + unsigned int idx = 0; + char u[3]; + do{ + uartread(u, 1); + /* After reboot, rpi3b+ will send non-ascii char, so we need to check it */ + if(u[0] < 0 || u[0] >= 128) continue; + /* if get newline, then print \r\n and break */ + if(u[0] == '\n'){ + uartwrite("\n", 1); + break; + } + /* check the backspace character */ + else if(u[0] == '\x7f' || u[0] == '\b'){ + if(idx > 0){ + uartwrite("\b", 1); + uartwrite(" ", 1); + uartwrite("\b", 1); + idx--; + } + } + /* otherwise, print and save the character */ + else{ + uartwrite(u, 1); // need to recv the echo back + if( idx < size){ + buf[idx++] = u[0]; + } + } + } while(1); + buf[idx] = '\0'; + return idx; +} + +void delay1(unsigned long long time){ + unsigned long long system_timer = 0; + asm volatile( + "mrs %0, cntpct_el0\n\t" + :"=r"(system_timer) + ); + unsigned long long expired_time = system_timer + time; + while(system_timer <= expired_time){ + asm volatile( + "mrs %0, cntpct_el0\n\t" + :"=r"(system_timer) + ); + } + +} + +int get_arm_memory1(unsigned int *mbox){ + mbox[0] = 8 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_ARM_MEMORY; // tag identifier + mbox[3] = 8; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // base address in bytes + mbox[6] = 0; // size in bytes + // tags end + mbox[7] = END_TAG; + + return mbox_call(MAILBOX_CH_PROP, mbox); +} + +int main(){ + uart_puts("----------------------------user program2----------------------------\n"); + print_string(UITOA, "[user] Fork Test, pid = ", getpid(), 1); + int cnt = 1; + int ret = 0; + unsigned int mbox[36]; + int success = get_arm_memory1(mbox); + + if (success){ + print_string(UITOHEX, "[user] ARM Memory Base Address: 0x", mbox[5], 1); + print_string(UITOHEX, "[user] ARM Memory Size: 0x", mbox[6], 1); + } else{ + uart_puts("[user] Failed to get board revision\n"); + } + + if((ret = fork()) == 0){ + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + print_string(UITOA, "[user] first child pid: ", getpid(), 0); + print_string(UITOA, " | cnt: ", cnt, 0); + print_string(UITOHEX, " | cnt_ptr: 0x", (unsigned long long)&cnt, 0); + print_string(UITOHEX, " | sp: 0x", cur_sp, 1); + + ++cnt; + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + print_string(UITOA, "[user] first child pid: ", getpid(), 0); + print_string(UITOA, " | cnt: ", cnt, 0); + print_string(UITOHEX, " | cnt_ptr: 0x", (unsigned long long)&cnt, 0); + print_string(UITOHEX, " | sp: 0x", cur_sp, 1); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + print_string(UITOA, "[user] second child pid: ", getpid(), 0); + print_string(UITOA, " | cnt: ", cnt, 0); + print_string(UITOHEX, " | cnt_ptr: 0x", (unsigned long long)&cnt, 0); + print_string(UITOHEX, " | sp: 0x", cur_sp, 1); + delay1(100000000); + ++cnt; + } + } + exit(0); + + }else{ + print_string(UITOA, "[user] parent here, pid: ", getpid(), 0); + print_string(UITOA, " | ret: ", ret, 1); + } + + exit(0); + + return 0; +} + +// int main(){ +// char buf[50]; +// memset(buf, 0, sizeof(buf)); +// // uart_init(); +// uartwrite("----------------------------user program----------------------------\n", 71); +// int pid = getpid(); +// print_string(UITOA, "[user] pid = ", pid, 1); + +// uartwrite("[user] please enter the user program: ", 40); +// unsigned int size = readline2(buf, 49); +// print_string(UITOA, "[user] uart_read_size = ", size, 1); + +// size = uartwrite(buf, size); +// print_string(UITOA, "[user] uart_write_size = ", size, 1); + + +// char* argv[] = {"exec_test"}; +// int a = exec(buf, argv); +// if(a == -1) +// uart_puts("[user] exec fail QQ\n"); + +// exit(); + +// return 0; +// } \ No newline at end of file diff --git a/user/src/malloc.c b/user/src/malloc.c new file mode 100644 index 000000000..3527f02a1 --- /dev/null +++ b/user/src/malloc.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include + +extern Frame *frames; +extern Buddy *buddy_list; +FreeChunkList *freechunk_list; + +/* define 11 level common chunk size */ +unsigned int chunk_size[] = {0x10, 0x20, 0x30, 0x40, 0x60, 0x80, + 0xa0, 0x100, 0x200, 0x400, 0x800}; + +unsigned int find_level(unsigned int size){ + int i; + for(i = 0; i < MAX_CHUNK_SIZE; i++){ + if(size <= chunk_size[i]) break; + } + return i; +} +void freechunk_list_init(){ + for(int i = 0; i < MAX_CHUNK_SIZE; i++){ + INIT_LIST_HEAD(&freechunk_list[i].list); + } +} + +void *kmalloc(unsigned int size){ + void *addr; + + if(size <= FRAME_SIZE / 2) + addr = chunk_alloc(size); + else + addr = buddy_alloc(size); + + return addr; +} + +void kfree(void *addr){ + int idx = addr_to_frame_idx(addr); + if(idx == -1){ + print_string(UITOHEX, "[x] kfree error -> the addr: 0x", (unsigned long long)addr, 0); + uart_puts(" is illegal allocated memory!!!\n"); + return; + } + Frame *target_frame = &frames[idx]; + if(target_frame->chunk_level >= 0) + chunk_free(addr); + else + buddy_free(addr); +} + + +void *chunk_alloc(unsigned int size){ + if(size >= FRAME_SIZE){ + uart_puts("[x] Alloc Chunk Error -> allocate size is over 4096KB, please use \"buddy_alloc\" function\n"); + return NULL; + } + unsigned int level = find_level(size); + + if(list_empty(&freechunk_list[level].list)){ + void *addr = buddy_alloc(0x1000); + int idx = addr_to_frame_idx(addr); + Frame *target_frame = &frames[idx]; + target_frame->chunk_level = (int)level; + + /* split the frame + * and add the chunk info(list_head) in the physical memory + */ + unsigned int offset = 0; + while(offset < FRAME_SIZE){ + Chunk *chunk = (Chunk *)((char *)addr + offset); + list_add_tail(&chunk->list, &freechunk_list[level].list); + offset += chunk_size[level]; + } + } + + FreeChunkList *chunk_list = &freechunk_list[level]; + Chunk *chunk = (Chunk *)chunk_list->list.next; + list_del(&chunk->list); + // print_string(UITOHEX, "[*] Alloc Chunk -> Allocate Size: ", size, 0); + // print_string(UITOHEX, " | Chunk Size: ", chunk_size[level], 0); + // print_string(UITOA, " | Level: ", level, 1); + // print_freechunk_list(); + return (void *)chunk; +} + + +void chunk_free(void *addr){ + int idx = addr_to_frame_idx(addr); + if(idx == -1){ + print_string(UITOHEX, "[x] Free Chunk error -> the addr: 0x", (unsigned long long)addr, 0); + uart_puts(" is illegal allocated memory!!!\n"); + return; + } + Frame *target_frame = &frames[idx]; + + if(target_frame->chunk_level == -1){ + print_string(UITOHEX, "[x] Free Chunk error -> the addr: 0x", (unsigned long long)addr, 0); + uart_puts(" is not a chunk, please use \"buddy_free\" function\n"); + return; + } + + /* add the chunk info(list_head) in the physical memory */ + Chunk *chunk = (Chunk *)addr; + list_add(&chunk->list, &freechunk_list[target_frame->chunk_level].list); + + // print_string(UITOHEX, "[*] Free Chunk -> the addr: 0x", (unsigned long long)addr, 1); + // print_freechunk_list(); + +} + + +void print_freechunk_list(){ + struct list_head *pos; + for(unsigned int i = 0; i < MAX_CHUNK_SIZE; i++){ + print_string(UITOHEX, "0x", chunk_size[i], 0); + uart_puts("\t:\t"); + unsigned int first = 1; + list_for_each(pos, &freechunk_list[i].list){ + void *tmp = (Chunk *)pos; + if(first){ + print_string(UITOHEX, "0x", (unsigned long long)tmp, 0); + first = 0; + } + else print_string(UITOHEX, " -> 0x", (unsigned long long)tmp, 0); + } + uart_puts("\n"); + } +} + + + + +void chunk_debug(){ + // void *addr1 = chunk_alloc(0x2000); + // void *addr2 = chunk_alloc(0x200); + // void *addr3 = chunk_alloc(0x200); + // chunk_free(addr2); +} + +void kmalloc_debug(){ + // test buddy alloc & free + uart_puts("\n--------------------------------------TEST BUDDY ALLOC & FREE--------------------------------------\n\n"); + unsigned long long size1[] = {0x900, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1500, 0x4000, 0x8000, 0x10000, 0x20000, 0x20001}; + void *addr1[sizeof(size1) / sizeof(unsigned long long)]; + + for(int i = 0; i < sizeof(size1) / sizeof(unsigned long long); i++){ + addr1[i] = kmalloc(size1[i]); + } + print_buddy_list(); + + + for(int i = 0; i < sizeof(size1) / sizeof(unsigned long long); i++){ + kfree(addr1[i]); + } + print_buddy_list(); + + // test buddy merge & split + uart_puts("\n--------------------------------------TEST BUDDY MERGE & SPLIT--------------------------------------\n\n"); + unsigned int test_size = 30; + void *addr3[test_size]; + for(int i = 0; i < test_size ; i++){ + addr3[i] = kmalloc(0x1000); + } + print_buddy_list(); + + for(int i = 0; i < test_size ;i++){ + kfree(addr3[i]); + } + print_buddy_list(); + + // test chunk alloc & free + uart_puts("\n--------------------------------------TEST CHUNK ALLOC & FREE--------------------------------------\n\n"); + void *addr2[sizeof(chunk_size) / sizeof(unsigned int)]; + for(int i = 0; i < sizeof(chunk_size)/ sizeof(unsigned int); i++){ + addr2[i] = kmalloc(chunk_size[i]); + } + print_freechunk_list(); + + for(int i = 0; i < sizeof(chunk_size) / sizeof(unsigned int); i++){ + kfree(addr2[i]); + } + print_freechunk_list(); + + + +} + +void *simple_malloc(unsigned long size) { + static void *head = (void *)SIMPLE_MALLOC_BASE_START; + void *ptr = head; + head = head + size; + return ptr; +} + + diff --git a/user/src/math.c b/user/src/math.c new file mode 100644 index 000000000..037db4291 --- /dev/null +++ b/user/src/math.c @@ -0,0 +1,15 @@ +#include + +unsigned int log(unsigned int base, unsigned int num){ + unsigned int val = 0; + unsigned int tmp = 1; + while(tmp < num){ + tmp *= base; + val++; + } + return val; +} + +unsigned int log2(unsigned int num){ + return log(2, num); +} \ No newline at end of file diff --git a/user/src/read.c b/user/src/read.c new file mode 100644 index 000000000..e22f10391 --- /dev/null +++ b/user/src/read.c @@ -0,0 +1,83 @@ +#include +#include +#include + +/* async scan the input until get \r */ +int async_readline(char buf[MAX_SIZE], int size){ + unsigned int idx = 0; + char c; + do{ + c = async_uart_getc(); + /* After reboot, rpi3b+ will send non-ascii char, so we need to check it */ + if(c < 0 || c >= 128) continue; + /* if get newline, then print \r\n and break */ + if(c == '\n'){ + async_uart_puts("\n"); + break; + } + /* check the backspace character */ + else if(c == '\x7f' || c == '\b'){ + if(idx > 0){ + async_uart_putc('\b'); + async_uart_putc(' '); + async_uart_putc('\b'); + idx--; + } + } + /* otherwise, print and save the character */ + else{ + async_uart_putc(c); // need to recv the echo back + if( idx < size){ + buf[idx++] = c; + } + } + } while(1); + buf[idx] = '\0'; + return idx; +} + +/* scan the input until get \r */ +int readline(char buf[MAX_SIZE], int size){ + unsigned int idx = 0; + char c; + do{ + c = uart_getc(); + /* After reboot, rpi3b+ will send non-ascii char, so we need to check it */ + if(c < 0 || c >= 128) continue; + /* if get newline, then print \r\n and break */ + if(c == '\n'){ + uart_puts("\n"); + break; + } + /* check the backspace character */ + else if(c == '\x7f' || c == '\b'){ + if(idx > 0){ + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + idx--; + } + } + /* otherwise, print and save the character */ + else{ + uart_putc(c); // need to recv the echo back + if( idx < size){ + buf[idx++] = c; + } + } + } while(1); + buf[idx] = '\0'; + return idx; +} + +/* read n bytes from uart */ +int readnbyte(char buf[MAX_SIZE], int size){ + unsigned int idx = 0; + char c; + while(idx < size && idx < MAX_SIZE){ + c = uart_getc(); + buf[idx++] = c; + } + buf[idx] = '\0'; + return idx; +} \ No newline at end of file diff --git a/user/src/reboot.c b/user/src/reboot.c new file mode 100644 index 000000000..83c439b95 --- /dev/null +++ b/user/src/reboot.c @@ -0,0 +1,11 @@ +#include + +void reset(int tick) { // reboot after watchdog timer expire + *PM_RSTC = PM_PASSWORD|0x20; // full reset + *PM_WDOG = PM_PASSWORD|tick; // number of watchdog tick +} + +void cancel_reset() { + *PM_RSTC = PM_PASSWORD|0; // full reset + *PM_WDOG = PM_PASSWORD|0; // number of watchdog tick +} \ No newline at end of file diff --git a/user/src/sched.c b/user/src/sched.c new file mode 100644 index 000000000..2513ba27a --- /dev/null +++ b/user/src/sched.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Thread *thread_pool; +Thread *run_thread_head; + +void init_thread_pool_and_head(){ + thread_pool = (Thread*)kmalloc(sizeof(Thread) * MAX_THREAD); + memset((char *)thread_pool, 0, sizeof(Thread) * MAX_THREAD); + for(unsigned int i = 0; i < MAX_THREAD; i++){ + INIT_LIST_HEAD(&thread_pool[i].list); + thread_pool[i].state = NOUSE; + thread_pool[i].id = i; + thread_pool[i].ustack_addr = NULL; + thread_pool[i].kstack_addr = NULL; + thread_pool[i].code_addr = NULL; + thread_pool[i].code_size = 0; + } + + run_thread_head = (Thread *)kmalloc(sizeof(Thread)); + memset((char *)run_thread_head, 0, sizeof(Thread)); + INIT_LIST_HEAD(&run_thread_head->list); + run_thread_head->id = -1; +} + +Thread *thread_create(void(*func)()){ + unsigned int idx; + for(idx = 0; idx < MAX_THREAD; idx++){ + if(thread_pool[idx].state == NOUSE) + break; + } + if(idx == MAX_THREAD) return NULL; + + Thread *new_thread = &thread_pool[idx]; + new_thread->state = WAIT; + new_thread->ustack_addr = kmalloc(STACK_SIZE); + new_thread->kstack_addr = kmalloc(STACK_SIZE); + new_thread->ctx.fp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; + new_thread->ctx.sp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; + new_thread->ctx.lr = (unsigned long)func; + + + print_string(UITOHEX, "new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); + uart_puts(" | "); + print_string(UITOHEX, "new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); + + + list_add_tail(&new_thread->list, &run_thread_head->list); + + return new_thread; +} + +void idle_thread(){ + while(1){ + // kill zombie + kill_zombie(); + // call schedule + schedule(); + } +} + +void kill_zombie(){ + disable_irq(); + struct list_head *pos; + list_for_each(pos, &run_thread_head->list){ + Thread *tmp = (Thread *)pos; + if(tmp->state == EXIT){ + kfree(tmp->ustack_addr); + kfree(tmp->kstack_addr); + tmp->state = NOUSE; + tmp->ustack_addr = NULL; + tmp->kstack_addr = NULL; + if(tmp->code_addr != NULL){ + kfree(tmp->code_addr); + } + tmp->code_addr = NULL; + tmp->code_size = 0; + list_del_entry(&tmp->list); + } + } + enable_irq(); +} + +void schedule(){ + disable_irq(); + Thread *curr_thread = get_current(); + Thread *next_thread = curr_thread; + do{ + next_thread = (Thread *)next_thread->list.next; + }while(list_is_head(&next_thread->list, &run_thread_head->list) || next_thread->state == EXIT); + + // print_string(UITOHEX, "curr: ", curr_thread->id, 0); + // uart_puts(" | "); + // print_string(UITOHEX, "next: ", next_thread->id, 1); + + enable_irq(); + cpu_switch_to(curr_thread, next_thread); +} + + + +void kernel_main() { + disable_irq(); + /* the first thread that is fake thread */ + Thread curr_thread; + + thread_create(idle_thread); + /* create the idle thread first */ + for(int i = 0; i < 5; i++){ + thread_create(foo); + } + + Thread *next_thread = (Thread *)run_thread_head->list.next; + + print_run_thread(); + enable_irq(); + cpu_switch_to(&curr_thread, next_thread); +} + +void delay(unsigned int time){ + unsigned long long system_timer = 0; + unsigned long long frq = 0; + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + :"=r"(system_timer), "=r"(frq) + ); + unsigned long long expired_time = system_timer + 1000; + while(system_timer <= expired_time){ + asm volatile( + "mrs %0, cntpct_el0\n\t" + :"=r"(system_timer) + ); + } +} + +void foo(){ + for(int i = 0; i < 2; i++) { + print_string(UITOA, "Thread id: ", get_current()->id, 0); + print_string(UITOA, " ", i, 1); + // printf("Thread id: %d %d\n", get_current()->task_id, i); + delay(1); + schedule(); + } + do_exit(); +} + +void print_run_thread(){ + struct list_head *pos; + unsigned int first = 1; + list_for_each(pos, &run_thread_head->list){ + Thread *tmp = (Thread *)pos; + if(first){ + print_string(UITOA, "run_thread: pid", tmp->id, 0); + first = 0; + } + else print_string(UITOA, " -> pid", tmp->id, 0); + } + uart_puts("\n"); +} \ No newline at end of file diff --git a/user/src/shell.c b/user/src/shell.c new file mode 100644 index 000000000..bb51db781 --- /dev/null +++ b/user/src/shell.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* print welcome message*/ +void PrintWelcome(){ + uart_puts("******************************************************************\n"); + uart_puts("********************* Welcome to FanFan's OS *********************\n"); + uart_puts("******************************************************************\n"); + uart_puts("# "); +} + +/* print help message*/ +void PrintHelp(){ + uart_puts("-------------------------- Help Message --------------------------\n"); + uart_puts("help : print this help menu\n"); + uart_puts("hello : print Hello World!\n"); + uart_puts("revision : print board_revision\n"); + uart_puts("memory : print memory info\n"); + uart_puts("reboot : reboot the device\n"); + uart_puts("ls : list directory contents\n"); + uart_puts("exec : concatenate files and print on the standard output\n"); + uart_puts("setTimeout : set timeout for read\n"); + uart_puts("test_timeout : test timeout for read\n"); +} + +/* print unknown command message*/ +void PrintUnknown(char buf[MAX_SIZE]){ + uart_puts("Unknown command: "); + uart_puts(buf); + uart_puts("\n"); +} + +/* print board revision*/ +void PrintRevision(char buf[MAX_SIZE]){ + volatile unsigned int mbox[36]; + unsigned int success = get_board_revision(mbox); + if (success){ + uart_puts("Board Revision: 0x"); + uitohex(buf, mbox[5]); + uart_puts(buf); + uart_puts("\n"); + } else{ + uart_puts("Failed to get board revision\n"); + } +} + +/* print memory info*/ +void PrintMemory(char buf[MAX_SIZE]){ + volatile unsigned int mbox[36]; + unsigned int success = get_arm_memory(mbox); + if (success){ + uart_puts("ARM Memory Base Address: 0x"); + uitohex(buf, mbox[5]); + uart_puts(buf); + uart_puts("\n"); + + uart_puts("ARM Memory Size: 0x"); + uitohex(buf, mbox[6]); + uart_puts(buf); + uart_puts("\n"); + } else{ + uart_puts("Failed to get board revision\n"); + } +} + +/* reboot device */ +void Reboot(){ + uart_puts("Rebooting...\n"); + reset(100); + while(1); +} + +/* uart booting */ +void Bootimg(char buf[MAX_SIZE]){ + memset(buf, '\0', MAX_SIZE); + readnbyte(buf, 10); + unsigned int size = atoi(buf); + uart_puts("kernal image size: "); + uart_puts(buf); + uart_puts("\n"); + + memset(buf, '\0', MAX_SIZE); + readnbyte(buf, size); + + uart_puts("done\n"); +} + +void Ls(){ + ls(); +} + +void Cat(char buf[MAX_SIZE]){ + uart_puts("Filename: "); + unsigned int size = readline(buf, MAX_SIZE); + if (size == 0){ + return; + } + cat(buf); +} + +void Exec(char buf[MAX_SIZE]){ + uart_puts("Filename: "); + unsigned int size = readline(buf, MAX_SIZE); + if (size == 0){ + return; + } + int state = kernel_exec(buf); + + if(state == -1){ + uart_puts("[x] Failed to exec the file\n"); + return; + } +} + +void SetTimeOut(char buf[MAX_SIZE]){ + char *message_tmp = strchr(buf, ' ') + 1; + char *end_message = strchr(message_tmp, ' '); + *end_message = '\0'; + char *message = (char *)simple_malloc(strlen(message_tmp) + 1); + strcpy(message, message_tmp); + unsigned int timeout = atoui(end_message + 1); + + add_timer(timeout_print, timeout, message); +} + +void TestTimeOut(char buf[MAX_SIZE]){ + add_timer(timeout_print, 2, "[*] timeout: 2\n"); + add_timer(timeout_print, 1, "[*] timeout: 1\n"); + add_timer(timeout_print, 5, "[*] timeout: 5\n"); + add_timer(timeout_print, 4, "[*] timeout: 4\n"); + add_timer(timeout_print, 3, "[*] timeout: 3\n"); + add_timer(timeout_print, 2, "[*] timeout: 2.1\n"); // test short expired time + add_timer(timeout_print, 9, "[*] timeout: 9\n"); + add_timer(timeout_print, 7, "[*] timeout: 7\n"); + add_timer(timeout_print, 6, "[*] timeout: 6\n"); + add_timer(timeout_print, 8, "[*] timeout: 8\n"); + +} + +/* Main Shell */ +void ShellLoop(){ + char buf[MAX_SIZE]; + + while(1){ + + memset(buf, '\0', MAX_SIZE); + unsigned int size = readline(buf, sizeof(buf)); + if (size == 0){ + uart_puts("# "); + continue; + } + if(strcmp("help", buf) == 0) PrintHelp(); + else if(strcmp("hello", buf) == 0) uart_puts("Hello World!\n"); + else if(strcmp("reboot", buf) == 0) Reboot(); + else if(strcmp("revision", buf) == 0) PrintRevision(buf); + else if(strcmp("memory", buf) == 0) PrintMemory(buf); + else if(strcmp("bootimg", buf) == 0) Bootimg(buf); + else if(strcmp("ls", buf) == 0) Ls(); + else if(strcmp("cat", buf) == 0) Cat(buf); + else if(strcmp("exec", buf) == 0) Exec(buf); + else if(strncmp("setTimeout", buf, strlen("setTimeout")) == 0) SetTimeOut(buf); + else if(strcmp("test_timeout", buf) == 0) TestTimeOut(buf); + else PrintUnknown(buf); + + uart_puts("# "); + } + +} + diff --git a/user/src/string.c b/user/src/string.c new file mode 100644 index 000000000..49f5c1e42 --- /dev/null +++ b/user/src/string.c @@ -0,0 +1,212 @@ +/* Copyright (C) 1991-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + + +/* Compare S1 and S2, returning less than, equal to or + greater than zero if S1 is lexicographically less than, + equal to or greater than S2. */ +int strcmp(const char *p1, const char *p2) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + + do{ + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + + return c1 - c2; +} + +int strncmp(const char *p1, const char *p2, unsigned int n) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + + if (n <= 0) return 0; + while(n--){ + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + } + + return c1 - c2; +} + +/* string length */ +unsigned int strlen(const char buf[MAX_SIZE]){ + unsigned int len; + for(len = 0; len < MAX_SIZE; len++){ + if(buf[len] == '\0') return(len); + } + return 0; +} + +/* reverse string */ +void reverse_string(char buf[MAX_SIZE]){ + unsigned int len = strlen(buf); + char tmp; + for(unsigned int i = 0; i < len/2; i++){ + tmp = buf[i]; + buf[i] = buf[len-i-1]; + buf[len-i-1] = tmp; + } +} + +/* int to string */ +void itoa(char buf[MAX_SIZE], int num){ + int i = 0; + int sign = 0; + if(num < 0){ + sign = 1; + num = -num; + } + do{ + buf[i++] = num % 10 + '0'; + }while((num /= 10) > 0); + if(sign) buf[i++] = '-'; + buf[i] = '\0'; + reverse_string(buf); +} + +/* uint to string */ +void uitoa(char buf[MAX_SIZE], unsigned int num){ + unsigned int i = 0; + do{ + buf[i++] = num % 10 + '0'; + }while((num /= 10) > 0); + buf[i] = '\0'; + reverse_string(buf); +} + +/* uint to hex string */ +void uitohex(char buf[MAX_SIZE], unsigned int d){ + unsigned int i = 0; + do{ + buf[i] = d % 16; + if(buf[i] < 10) buf[i] += '0'; + else buf[i] += 'a' - 10; + i++; + }while((d /= 16) > 0); + buf[i] = '\0'; + reverse_string(buf); +} + +void print_string(enum print_type type, char *text , unsigned long long num, int println){ + char buf[20]; + switch(type){ + case UITOHEX: + uart_puts(text); + uitohex(buf, (unsigned int)num); + uart_puts(buf); + break; + case UITOA: + uart_puts(text); + uitoa(buf, (unsigned int)num); + uart_puts(buf); + break; + case ITOA: + uart_puts(text); + itoa(buf, (int)num); + uart_puts(buf); + break; + } + if(println) uart_puts("\n"); +} + +/* array to int */ +int atoi(const char buf[MAX_SIZE]){ + int num = 0; + int sign = 1; + if(buf[0] == '-'){ + sign = -1; + buf++; + } + for(unsigned int i = 0; i < strlen(buf); i++){ + num = num * 10 + (buf[i] - '0'); + } + return num * sign; +} + +/* array to uint */ +unsigned int atoui(const char buf[MAX_SIZE]){ + unsigned int num = 0; + for(unsigned int i = 0; i < strlen(buf); i++){ + num = num * 10 + (buf[i] - '0'); + } + return num; +} + +/* hex string to unsigned int */ +unsigned int hextoui(const char buf[MAX_SIZE], unsigned int size){ + unsigned int num = 0; + for(unsigned int i = 0; i < size; i++){ + if(buf[i] >= '0' && buf[i] <= '9'){ + num = num * 16 + (buf[i] - '0'); + } + else if(buf[i] >= 'A' && buf[i] <= 'F'){ + num = num * 16 + (buf[i] - 'A' + 10); + } + else if(buf[i] >= 'a' && buf[i] <= 'f'){ + num = num * 16 + (buf[i] - 'a' + 10); + } + } + return num; +} + +void memcpy(char *d, const char *s, unsigned int len){ + for(unsigned int i = 0; i < len; i++){ + d[i] = s[i]; + } +} + + +void memset(char *d, const char s, unsigned int len){ + for(unsigned int i = 0; i < len; i++){ + d[i] = s; + } +} + +void strcpy(char *d, const char *s){ + unsigned int len = strlen(s); + memcpy(d, s, len); + d[len] = '\0'; +} + + +void strcat(char *d, const char *s){ + unsigned int len = strlen(d); + unsigned int slen = strlen(s); + memcpy(d + len, s, slen); + d[len + slen] = '\0'; +} + +char *strchr(const char *str, int c){ + for(unsigned int i = 0; i < strlen(str); i++){ + if(str[i] == c) return (char *)(str + i); + } + return 0; +} \ No newline at end of file diff --git a/user/src/syscall.c b/user/src/syscall.c new file mode 100644 index 000000000..e668464cd --- /dev/null +++ b/user/src/syscall.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern Thread *run_thread_head; + +/* + * Return value is x0 + * https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard + */ + + +/* Get current process’s id. */ +void sys_getpid(TrapFrame *trapFrame){ + int pid = do_getpid(); + trapFrame->x[0] = pid; +} +int do_getpid(){ + return get_current()->id; +} + +/* Return the number of bytes read by reading size byte into the user-supplied buffer buf. */ +void sys_uart_read(TrapFrame *trapFrame){ + char *buf = (char *)trapFrame->x[0]; + unsigned int size = trapFrame->x[1]; + int idx = readnbyte(buf, size); + trapFrame->x[0] = idx; +} + +/* Return the number of bytes written after writing size byte from the user-supplied buffer buf. */ +void sys_uart_write(TrapFrame *trapFrame){ + const char *buf = (char *)trapFrame->x[0]; + unsigned int size = trapFrame->x[1]; + for(unsigned int i = 0; i < size; i++){ + uart_putc(buf[i]); + } + trapFrame->x[0] = size; +} +void sys_exec(TrapFrame *trapFrame){ + const char *name = (const char *)trapFrame->x[0]; + char **const argv = (char **const)trapFrame->x[1]; + int success = do_exec(trapFrame, name, argv); + trapFrame->x[0] = success; +} +int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ + /* check if the file info exist */ + file_info fileInfo = cpio_find_file_info(name); + if(fileInfo.filename == NULL) return -1; + + /* check if the file can create in new memory */ + void *thread_code_addr = load_program(&fileInfo); + if(thread_code_addr == NULL) return -1; + + /* current thread will change the pc to new code addr */ + Thread *curr_thread = get_current(); + curr_thread->code_addr = thread_code_addr; + curr_thread->code_size = fileInfo.filename_size; + + trapFrame->elr_el1 = (unsigned long long)curr_thread->code_addr; + trapFrame->sp_el0 = (unsigned long long)curr_thread->ustack_addr + STACK_SIZE; + + return 0; +} + +int kernel_exec(char *name){ + /* check if the file info exist */ + file_info fileInfo = cpio_find_file_info(name); + if(fileInfo.filename == NULL) return -1; + + /* check if the file can create in new memory */ + void *thread_code_addr = load_program(&fileInfo); + if(thread_code_addr == NULL) return -1; + + Thread *new_thread = thread_create(thread_code_addr); + new_thread->code_addr = thread_code_addr; + new_thread->code_size = fileInfo.filename_size; + print_string(UITOHEX, "new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); + + set_period_timer_irq(); + asm volatile( + "mov x0, 0x0\n\t" + "msr spsr_el1, x0\n\t" + "msr tpidr_el1, %0\n\t" + "msr elr_el1, %1\n\t" + "msr sp_el0, %2\n\t" + "mov sp, %1\n\t" + "eret\n\t" + ::"r"(new_thread), + "r"(new_thread->code_addr), + "r"(new_thread->ustack_addr + STACK_SIZE), + "r"(new_thread->kstack_addr + STACK_SIZE) + : "x0" + ); + return 0; +} + +void *load_program(file_info *fileInfo){ + void *thread_code_addr = kmalloc(fileInfo->datasize); + if(thread_code_addr == NULL) return NULL; + + memcpy(thread_code_addr, fileInfo->data, fileInfo->datasize); + + return thread_code_addr; +} + + +void sys_fork(TrapFrame *trapFrame){ + do_fork(); +} +void do_fork(){ + // Thread *new_thread; + // TrapFrame *curr_tf, *new_tf; + // void *start; +} + +void sys_exit(TrapFrame *trapFrame){ + int status = trapFrame->x[0]; + do_exit(status); +} +/* Terminate the current process. */ +void do_exit(int status){ + disable_irq(); + + Thread *exit_thread = get_current(); + exit_thread->state = EXIT; + + enable_irq(); + schedule(); + // cpu_switch_to(exit_thread, next_thread); +} + +void sys_mbox_call(TrapFrame *trapFrame){ + +} +void sys_kill(TrapFrame *trapFrame){ + +} \ No newline at end of file diff --git a/user/src/task.c b/user/src/task.c new file mode 100644 index 000000000..7a02bf39a --- /dev/null +++ b/user/src/task.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +unsigned int curr_poriority = 100; + +Task *task_head; + +void init_task_head(){ + task_head = (Task*)kmalloc(sizeof(Task)); + INIT_LIST_HEAD(&task_head->list); +} + +void add_task(Handler handler, unsigned int priority){ + Task *task = (Task*)kmalloc(sizeof(Task)); + memset((char *)task, 0, sizeof(Task)); + INIT_LIST_HEAD(&task->list); + task->handler = handler; + task->priority = priority; + + if(list_empty(&task_head->list)){ + list_add_tail(&task->list, &task_head->list); + return; + } + + struct list_head *pos; + list_for_each(pos, &task_head->list){ + Task *tmp = (Task *)pos; + if(task->priority <= tmp->priority){ + list_add(&task->list, tmp->list.prev); + } + } + if(list_is_head(pos, &task_head->list)){ + list_add_tail(&task->list, &task_head->list); + } +} + + +void do_task(){ + // for(Task *tmp = task_head; tmp != NULL; tmp = tmp->next){ + // uart_sputs("->"); + // if(tmp->priority == 1) uart_sputs("time"); + // else uart_sputs("uart"); + // } + // uart_sputs("\n"); + + while(!list_empty(&task_head->list)){ + Task *t_task = (Task *)task_head->list.next; + // curr_task is highest priority task, return it and finish the curr_task. + if(curr_poriority <= t_task->priority){ + // uart_sputs("curr_task is highest priority task, return it.\n"); + return; + } + + /* + ** task_head is the highest priority task (highest priority task is the first task in the list) + ** new(highest priority) -> prev(curr_poriority) -> low priority task... + */ + unsigned int prev_poriority = curr_poriority; // save current poriority + curr_poriority = t_task->priority; + Handler handler = t_task->handler; + // new_task should be the low priority task / me / null + + enable_irq(); + handler(); + disable_irq(); + + list_del(&t_task->list); + kfree(t_task); + t_task = NULL; + /* + ** if prev_poriority(curr_poriority now) is highest priority task + ** it means the prev_task need to be continued to finish the task. + ** prev(curr_poriority) -> low priority task... + */ + curr_poriority = prev_poriority; + } + curr_poriority = 100; +} diff --git a/user/src/timer.c b/user/src/timer.c new file mode 100644 index 000000000..6667b4c72 --- /dev/null +++ b/user/src/timer.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include + +int printAfter2Second = 0; +Timer *head = NULL; +void add_timer(TimerTask task, unsigned long long expired_time, void *args){ + unsigned long long system_timer = 0; + unsigned long long frq = 0; + + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + :"=r"(system_timer), "=r"(frq) + ); + + Timer *timer = (Timer*)kmalloc(sizeof(Timer)); + memset((char *)timer, 0, sizeof(Timer)); + timer->expired_time = system_timer + expired_time * frq; + timer->task = task; + timer->args = args; + timer->next = NULL; + timer->prev = NULL; + + if(head == NULL){ + head = timer; + reset_timer_irq(head->expired_time); + return; + } + Timer *tmp = head; + while(tmp->next != NULL && timer->expired_time > tmp->expired_time){ + tmp = tmp->next; + } + + if(timer->expired_time <= tmp->expired_time){ + if(tmp == head){ + head = timer; + head->next = tmp; + tmp->prev = head; + reset_timer_irq(head->expired_time); + } + else{ + timer->next = tmp; + timer->prev = tmp->prev; + tmp->prev->next = timer; + tmp->prev = timer; + } + } + else{ + tmp->next = timer; + timer->prev = tmp; + } + +} + + +void timer_interrupt_handler(){ + // while(1){ + // uart_puts("abcdefghijklmnopqrstuvwxyz\n"); + // } + while(head != NULL){ + if(head->next != NULL){ + Timer *tmp = head; + head->task(head->args); + head = head->next; + head->prev = NULL; + kfree(tmp); + tmp = NULL; + unsigned long long system_timer = 0; + asm volatile("mrs %0, cntpct_el0\n\t" :"=r"(system_timer)); + if(head->expired_time > system_timer){ + reset_timer_irq(head->expired_time); + break; + } + } + else{ + head->task(head->args); + kfree(head); + head = NULL; + } + } + if(head == NULL) set_long_timer_irq(); + if(printAfter2Second == 0) { + add_timer(timeout_print, 2, "[*] After Two Second, Hello User\n# "); + printAfter2Second = 1; + } + enable_timer_irq(); +} + +void timer_interrupt_handler_el0(){ + head = NULL; + uart_puts("Time interrupt\n"); + set_period_timer_irq(); + enable_timer_irq(); +} + +void timeout_print(void *args){ + uart_puts((char*)args); +} \ No newline at end of file diff --git a/user/src/uart.c b/user/src/uart.c new file mode 100644 index 000000000..353923590 --- /dev/null +++ b/user/src/uart.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include + +char read_buf[MAX_SIZE]; +char write_buf[MAX_SIZE]; +static unsigned int read_set_idx = 0; +static unsigned int read_get_idx = 0; +static unsigned int write_set_idx = 0; +static unsigned int write_get_idx = 0; + + +void uart_init(){ + *AUX_ENABLE |= 1; // Enable mini UART. + *AUX_MU_CNTL = 0; // Disable transmitter and receiver during configuration. + *AUX_MU_IER = 0; // Disable interrupt because currently you don’t need interrupt. + *AUX_MU_LCR = 3; // Set the data size to 8 bit. + *AUX_MU_MCR = 0; // Don’t need auto flow control. + *AUX_MU_BAUD = 270; // Set baud rate and characteristics (115200 8N1) and map to GPIO + *AUX_MU_IIR = 6; // No FIFO + + /* map UART1 to GPIO pins */ + register unsigned int reg; + reg = *GPFSEL1; + reg &= ~((7<<12) | (7<<15)); // clear gpio14,15 alternate function, check uart.h + reg |= ((2<<12) | (2<<15)); // alternate function 5 (TXD1 and RXD1) + *GPFSEL1 = reg; + /* Enable pins 14 and 15 (disable pull-up/down, "floating" input pin with no pull-up or pull-down resistors) */ + *GPPUD = 0; + /* Wait 150 cycles – this provides the required set-up time for the control signal */ + for(reg = 0; reg > 150; reg--) {asm volatile("nop");} + /* Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to modify */ + *GPPUDCLK0 = (1<<14) | (1<<15); + /* Wait 150 cycles – this provides the required set-up time for the control signal */ + for(reg = 0; reg > 150; reg--) {asm volatile("nop");} + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx + + *ENABLE_IRQS_1 = 1 << 29; // enable UART1 IRQ + +} + +/* Receive a character */ +char uart_getc(){ + /* + wait until something is in the buffer + 0x01/bit_1 is set if there was a receiver overrun. That is: + one or more characters arrived whilst the receive + FIFO was full. The newly arrived charters have been + discarded. This bit is cleared each time this register is + read. To do a non-destructive read of this overrun bit + use the Mini Uart Extra Status register. + */ + while(!(*AUX_MU_LSR & 0x01)) {asm volatile("nop");} + /* read it and return */ + char r = (char)(*AUX_MU_IO); + /* convert carrige return to newline */ + return r == '\r'?'\n':r; +} + +void recv_interrupt_handler(){ + /* read buffer is full, discard the new char */ + if((read_set_idx + 1) % MAX_SIZE == read_get_idx){ + disable_AUX_MU_IER_r(); + return; + } + + read_buf[read_set_idx] = uart_getc(); + read_set_idx = (read_set_idx + 1) % MAX_SIZE; /* reset the index if it reaches the end */ + /* enable receive interrupt after set the new char */ + enable_AUX_MU_IER_r(); +} + +char async_uart_getc(){ + // enable_AUX_MU_IER_r(); + /* wait until something is in the read buffer (read_set_idx != read_get_idx) */ + while(read_get_idx == read_set_idx) {asm volatile("nop");} + + disable_irq(); + char r = read_buf[read_get_idx]; /* read the char that set in read buffer already*/ + read_get_idx = (read_get_idx + 1) % MAX_SIZE; /* reset the index if it reaches the end */ + enable_irq(); + + return r; +} + +/* Display a char */ +void uart_putc(unsigned int c){ + /* + wait until we can send + 0x20/bit_6 is set if the transmit FIFO is empty and the + transmitter is idle. (Finished shifting out the last bit). + */ + while(!(*AUX_MU_LSR & 0x20)) {asm volatile("nop");} + + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +void tran_interrupt_handler(){ + /* finished sending the last char */ + if(write_get_idx == write_set_idx){ + disable_AUX_MU_IER_w(); + return; + } + + char c = write_buf[write_get_idx]; + uart_putc(c); + write_get_idx = (write_get_idx + 1) % MAX_SIZE; /* reset the index if it reaches the end */ + + /* finished sending the last char */ + if(write_get_idx == write_set_idx){ + disable_AUX_MU_IER_w(); + return; + } + + /* enable transmit interrupt to expect print next char */ + enable_AUX_MU_IER_w(); +} + +void async_uart_putc(unsigned int c){ + /* buffer is full, wait the sending char */ + while((write_set_idx + 1) % MAX_SIZE == write_get_idx) {enable_AUX_MU_IER_w();} + + + disable_irq(); + write_buf[write_set_idx] = (char)c; + write_set_idx = (write_set_idx + 1) % MAX_SIZE; /* reset the index if it reaches the end */ + enable_irq(); + + /* enable transmit interrupt after set the new char */ + enable_AUX_MU_IER_w(); +} + +/* Async display a string */ +void async_uart_puts(char *s) { + while(*s) async_uart_putc(*s++); + /* convert newline to carrige return + newline */ + if(*(--s)=='\n') async_uart_putc('\r'); + +} + +/* Display a string */ +void uart_puts(char *s) { + while(*s) uart_putc(*s++); + /* convert newline to carrige return + newline */ + if(*(--s)=='\n') uart_putc('\r'); + +} + +void uart_nbyte(char *s, unsigned int len) { + while(len--) { + /* convert newline to carrige return + newline */ + if(*s=='\n') uart_putc('\r'); + uart_putc(*s++); + } +} + + +void enable_AUX_MU_IER_r(){ + *AUX_MU_IER |= 1; +} + +void enable_AUX_MU_IER_w(){ + *AUX_MU_IER |= 2; +} + +void disable_AUX_MU_IER_r(){ + *AUX_MU_IER &= ~1; +} + +void disable_AUX_MU_IER_w(){ + *AUX_MU_IER &= ~2; +} \ No newline at end of file diff --git a/user/src/user_syscall.S b/user/src/user_syscall.S new file mode 100644 index 000000000..7ac7d6465 --- /dev/null +++ b/user/src/user_syscall.S @@ -0,0 +1,51 @@ +#define __ASSEMBLY__ +#include "user_syscall.h" +.section ".text" + +.global getpid +getpid: + mov x8, GET_PID + svc #0 + ret + +.global uartread +uartread: + mov x8, UART_READ + svc #0 + ret + +.global uartwrite +uartwrite: + mov x8, UART_WRITE + svc #0 + ret + +.global exec +exec: + mov x8, EXEC + svc #0 + ret + +.global fork +fork: + mov x8, FORK + svc #0 + ret + +.global exit +exit: + mov x8, EXIT + svc #0 + ret + +.global mbox_call +mbox_call: + mov x8, MBOX_CALL + svc #0 + ret + +.global kill +kill: + mov x8, KILL + svc #0 + ret \ No newline at end of file From 960330792a4f5f0f26b351ad5e4d1ca5869108e8 Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 13 May 2022 23:55:12 +0800 Subject: [PATCH 18/62] QQ --- kernel/Makefile | 2 +- kernel/initramfs.cpio | Bin 49664 -> 247296 bytes user/initramfs.cpio | Bin 49664 -> 49664 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/Makefile b/kernel/Makefile index 84e2d9bff..4b0875f8a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -36,7 +36,7 @@ clean: rm *.elf *.img build/* >/dev/null 2>/dev/null || true run: - qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb pty: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial pty -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index a8b57f1f106d5c45d46986432909de200dc36068..0676fb1584617bc4d73624b3b212a12133e97d8e 100644 GIT binary patch literal 247296 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 WHGNj57?~cE7X6olV`+4D+W!M72^C}j diff --git a/user/initramfs.cpio b/user/initramfs.cpio index a8b57f1f106d5c45d46986432909de200dc36068..66ef026df3f7d58f192af4f99ea2f7b0b4eba93d 100644 GIT binary patch delta 48 zcmZo@VQy$)-f)+j*VWv`#Q+3M9i0p&%VnBumg7m1XJTgA+@yU=5=6(jE~)_lTT>1d delta 48 zcmZo@VQy$)-f)+j*Tu-p#nAu=OdXvJCd*}-Y?k9ml4ojW*xaOjOA!0A+3u A3IG5A From 6340252025ad42da04907d211d3e6e847055431d Mon Sep 17 00:00:00 2001 From: fan Date: Sat, 14 May 2022 01:05:53 +0800 Subject: [PATCH 19/62] video player ok --- kernel/Makefile | 5 ++++- kernel/initramfs.cpio | Bin 49664 -> 247296 bytes 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/Makefile b/kernel/Makefile index 84e2d9bff..2a607575e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -17,7 +17,7 @@ ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -all: clean kernel8.img run +all: clean kernel8.img run-display $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g @@ -38,6 +38,9 @@ clean: run: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb +run-display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -s -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + pty: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial pty -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index a8b57f1f106d5c45d46986432909de200dc36068..0676fb1584617bc4d73624b3b212a12133e97d8e 100644 GIT binary patch literal 247296 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 WHGNj57?~cE7X6olV`+4D+W!M72^C}j From c6535fe881019d486f54737dddfeab0fdbc81b46 Mon Sep 17 00:00:00 2001 From: fan Date: Mon, 16 May 2022 15:48:07 +0800 Subject: [PATCH 20/62] signal syscall setup --- kernel/include/user_syscall.h | 4 ++++ kernel/src/main.c | 3 +-- kernel/src/user_syscall.S | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/kernel/include/user_syscall.h b/kernel/include/user_syscall.h index 644f1bfa9..34824cbfa 100644 --- a/kernel/include/user_syscall.h +++ b/kernel/include/user_syscall.h @@ -9,6 +9,8 @@ #define EXIT 5 #define MBOX_CALL 6 #define KILL 7 +#define SIGNAL_ 8 +#define SIGKILL 9 #endif @@ -22,5 +24,7 @@ extern int fork(); extern void exit(); extern int mbox_call(unsigned char ch, unsigned int *mbox); extern void kill(int pid); +extern void singal(int SIGNAL, void (*handler)()); +extern void signal_kill(int pid, int SIGNAL); #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 5fbbf0a66..623309e2f 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -17,6 +17,7 @@ int main(unsigned long dtb_base){ asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); tmp |= 1; asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); + uart_init(); // uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); @@ -26,11 +27,9 @@ int main(unsigned long dtb_base){ init_task_head(); enable_timer_irq(); - // enable_AUX_MU_IER_r(); enable_irq(); // DAIF set to 0b0000 // kernel_exec("user2.img"); - // kernel_main(); PrintWelcome(); diff --git a/kernel/src/user_syscall.S b/kernel/src/user_syscall.S index 7ac7d6465..d298dbb2c 100644 --- a/kernel/src/user_syscall.S +++ b/kernel/src/user_syscall.S @@ -48,4 +48,16 @@ mbox_call: kill: mov x8, KILL svc #0 + ret + +.global signal +signal: + mov x8, SIGNAL_ + svc #0 + ret + +.global signal_kill +signal_kill: + mov x8, SIGKILL + svc #0 ret \ No newline at end of file From f7bbdd1e10a1d3698ded798b1e7d5a75320dd077 Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 20 May 2022 00:17:30 +0800 Subject: [PATCH 21/62] signal: signal kill ready --- kernel/include/sched.h | 10 +++++++++- kernel/include/signal.h | 7 +++++++ kernel/include/syscall.h | 3 +++ kernel/include/task.h | 3 --- kernel/src/exc.c | 6 ++++++ kernel/src/sched.c | 6 +++++- kernel/src/signal.c | 10 ++++++++++ kernel/src/syscall.c | 28 ++++++++++++++++++++++++++++ 8 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 kernel/include/signal.h create mode 100644 kernel/src/signal.c diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 45d31c103..f6a2ec933 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -4,8 +4,10 @@ #include #define MAX_THREAD 0x100 +#define MAX_SIG_HANDLER 0x20 #define STACK_SIZE 0x1000 + enum thread_state{ NOUSE, RUNNING, @@ -28,7 +30,7 @@ typedef struct _cpu_context{ unsigned long sp; // stact pointer }CpuContext; - +typedef void (*SigHandler)(); typedef struct _Thread{ struct list_head list; CpuContext ctx; @@ -38,6 +40,12 @@ typedef struct _Thread{ void *kstack_addr; void *code_addr; // use in exec unsigned int code_size; // use in exec + + /* signal */ + SigHandler signal_handler[MAX_SIG_HANDLER]; + unsigned int signal_count[MAX_SIG_HANDLER]; + + }Thread; extern Thread* get_current(); diff --git a/kernel/include/signal.h b/kernel/include/signal.h new file mode 100644 index 000000000..8c5602212 --- /dev/null +++ b/kernel/include/signal.h @@ -0,0 +1,7 @@ +#ifndef SIGNAL_H_ +#define SIGNAL_H_ + + +void sig_default_handler(); + +#endif \ No newline at end of file diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 2e02d4de4..5099ab20f 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -18,6 +18,8 @@ void sys_fork(TrapFrame *); void sys_exit(TrapFrame *); void sys_mbox_call(TrapFrame *); void sys_kill(TrapFrame *); +void sys_signal(TrapFrame *); +void sys_signal_kill(TrapFrame *); int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); @@ -25,6 +27,7 @@ void *load_program(file_info *fileInfo); int do_fork(TrapFrame *trapFrame); void do_exit(int status); int do_kill(int pid); +int do_signal_kill(int pid, int signal); int kernel_exec(char *name); diff --git a/kernel/include/task.h b/kernel/include/task.h index 51ef27e10..5e1fce7f4 100644 --- a/kernel/include/task.h +++ b/kernel/include/task.h @@ -5,9 +5,6 @@ typedef void (*Handler)(); - - - typedef struct _Task{ struct list_head list; unsigned int priority; diff --git a/kernel/src/exc.c b/kernel/src/exc.c index 7dd9ad679..ac9ca0134 100644 --- a/kernel/src/exc.c +++ b/kernel/src/exc.c @@ -56,6 +56,12 @@ void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ case KILL: sys_kill(trapFrame); break; + case SIGNAL_: + sys_signal(trapFrame); + break; + case SIGKILL: + sys_signal_kill(trapFrame); + break; default: break; diff --git a/kernel/src/sched.c b/kernel/src/sched.c index cbd0304e1..7c27e4e7c 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -7,6 +7,7 @@ #include #include #include +#include Thread *thread_pool; Thread *run_thread_head; @@ -47,7 +48,10 @@ Thread *thread_create(void(*func)()){ new_thread->ctx.fp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; new_thread->ctx.sp = (unsigned long)new_thread->kstack_addr + STACK_SIZE; new_thread->ctx.lr = (unsigned long)func; - + + for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ + new_thread->signal_handler[i] = sig_default_handler; + } print_string(UITOHEX, "[*] new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); uart_puts(" | "); diff --git a/kernel/src/signal.c b/kernel/src/signal.c new file mode 100644 index 000000000..0e33d778e --- /dev/null +++ b/kernel/src/signal.c @@ -0,0 +1,10 @@ +#include +#include +#include + + +void sig_default_handler(){ + TrapFrame fake_tf; + fake_tf.x[0] = get_current()->id; + sys_kill(&fake_tf); +} \ No newline at end of file diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 80a8efc78..611481262 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -229,4 +229,32 @@ int do_kill(int pid){ enable_irq(); schedule(); return 0; +} + +void sys_signal(TrapFrame *trapFrame){ + +} + +void sys_signal_kill(TrapFrame *trapFrame){ + disable_irq(); + int pid = trapFrame->x[0]; + int signal = trapFrame->x[1]; + int status = do_signal_kill(pid, signal); + + if(status == -1) + print_string(UITOA, "[x] signal_kill fail, pid: ", pid, 1); + else + print_string(UITOA, "[*] signal_kill ready, pid: ", pid, 1); + + enable_irq(); +} + +int do_signal_kill(int pid, int signal){ + if(!(pid >= 0 && pid < MAX_THREAD)) + return -1; + if(thread_pool[pid].state != RUNNING) + return -1; + + thread_pool[pid].signal_count[signal]++; + return 0; } \ No newline at end of file From 8939c783503fb12a218b7ef9c38bf36719589c24 Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 21 May 2022 03:03:18 +0800 Subject: [PATCH 22/62] signal: signal_kill done --- kernel/boot/start.S | 3 +- kernel/include/irq.h | 3 +- kernel/include/sched.h | 14 ++++++--- kernel/include/signal.h | 3 ++ kernel/include/syscall.h | 1 + kernel/include/user_syscall.h | 1 + kernel/src/exc.c | 3 ++ kernel/src/irq.c | 17 ++++++++--- kernel/src/sched.c | 9 +++++- kernel/src/signal.c | 53 +++++++++++++++++++++++++++++++++-- kernel/src/syscall.c | 32 +++++++++++++++++++-- 11 files changed, 123 insertions(+), 16 deletions(-) diff --git a/kernel/boot/start.S b/kernel/boot/start.S index 7708491af..8477b72d3 100644 --- a/kernel/boot/start.S +++ b/kernel/boot/start.S @@ -108,7 +108,8 @@ sync_exception_entry: irq_entry: save_all - mrs x1, spsr_el1 + mrs x0, spsr_el1 + mov x1, sp bl irq_handler load_all eret diff --git a/kernel/include/irq.h b/kernel/include/irq.h index bf9c92d98..49e8d5209 100644 --- a/kernel/include/irq.h +++ b/kernel/include/irq.h @@ -2,6 +2,7 @@ #define IRQ_H_ #include +#include #define IRQ_BASIC_PENDING ((volatile unsigned int*)(MMIO_BASE+0x0000B200)) #define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000B204)) @@ -22,7 +23,7 @@ #define SYSTEM_TIMER_IRQ_3 (1 << 3) #define NON_SECURE_TIMER_IRQ (1 << 1) -void irq_handler(unsigned long long, unsigned long long); +void irq_handler(unsigned long long, TrapFrame*); void Time_interrupt(unsigned long long); void GPU_interrupt(); diff --git a/kernel/include/sched.h b/kernel/include/sched.h index f6a2ec933..643f0372f 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -31,6 +31,12 @@ typedef struct _cpu_context{ }CpuContext; typedef void (*SigHandler)(); +typedef struct sig_list_{ + struct list_head list; + unsigned int ready; + SigHandler handler; +}SignalInfo; + typedef struct _Thread{ struct list_head list; CpuContext ctx; @@ -42,10 +48,10 @@ typedef struct _Thread{ unsigned int code_size; // use in exec /* signal */ - SigHandler signal_handler[MAX_SIG_HANDLER]; - unsigned int signal_count[MAX_SIG_HANDLER]; - - + SignalInfo sig_info_pool[MAX_SIG_HANDLER]; // all signal info + SignalInfo sig_queue_head; // ready queue + void *sig_stack_addr; + void *old_tp; }Thread; extern Thread* get_current(); diff --git a/kernel/include/signal.h b/kernel/include/signal.h index 8c5602212..44d152ad6 100644 --- a/kernel/include/signal.h +++ b/kernel/include/signal.h @@ -1,7 +1,10 @@ #ifndef SIGNAL_H_ #define SIGNAL_H_ +#include +void check_sig_queue(TrapFrame*); void sig_default_handler(); +void sig_register_handler(); #endif \ No newline at end of file diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 5099ab20f..d2c8bfdd6 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -20,6 +20,7 @@ void sys_mbox_call(TrapFrame *); void sys_kill(TrapFrame *); void sys_signal(TrapFrame *); void sys_signal_kill(TrapFrame *); +void sys_sigreturn(TrapFrame *); int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); diff --git a/kernel/include/user_syscall.h b/kernel/include/user_syscall.h index 34824cbfa..4de39573a 100644 --- a/kernel/include/user_syscall.h +++ b/kernel/include/user_syscall.h @@ -11,6 +11,7 @@ #define KILL 7 #define SIGNAL_ 8 #define SIGKILL 9 +#define SIGRETURN 20 #endif diff --git a/kernel/src/exc.c b/kernel/src/exc.c index ac9ca0134..97d4d0127 100644 --- a/kernel/src/exc.c +++ b/kernel/src/exc.c @@ -62,6 +62,9 @@ void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ case SIGKILL: sys_signal_kill(trapFrame); break; + case SIGRETURN: + sys_sigreturn(trapFrame); + break; default: break; diff --git a/kernel/src/irq.c b/kernel/src/irq.c index a9b51c89f..9ea4dfcb6 100644 --- a/kernel/src/irq.c +++ b/kernel/src/irq.c @@ -4,19 +4,28 @@ #include #include #include +#include +#include extern Thread *run_thread_head; -void irq_handler(unsigned long long what, unsigned long long spsr){ - // char buf[10]; +void irq_handler(unsigned long long spsr, TrapFrame *trapFrame){ // uart_sputs("---------IRQ Handler---------\n"); if(*CORE0_IRQ_SOURCE & 0x2) Time_interrupt(spsr); else if(*CORE0_IRQ_SOURCE & 0x100) GPU_interrupt(); + + // print_string(UITOHEX, "spsr = ", spsr, 0); + // print_string(UITOHEX, "pid = ", get_current()->id, 1); + /* check the spsr if it is from user mode */ + spsr &= 0b1111; + if(spsr == 0x0){ + check_sig_queue(trapFrame); + } } void Time_interrupt(unsigned long long spsr){ - unsigned long long frq = 0; - asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); + // unsigned long long frq = 0; + // asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); // print_string(UITOHEX, "spsr = ", (unsigned long long)spsr, 1); // spsr &= 0b1111; // if(spsr == 0x0){ diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 7c27e4e7c..512a8b541 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -23,6 +23,13 @@ void init_thread_pool_and_head(){ thread_pool[i].kstack_addr = NULL; thread_pool[i].code_addr = NULL; thread_pool[i].code_size = 0; + + for(unsigned int j = 0; j < MAX_SIG_HANDLER; j++){ + INIT_LIST_HEAD(&thread_pool[i].sig_info_pool[j].list); + thread_pool[i].sig_info_pool[j].ready = 0; + thread_pool[i].sig_info_pool[j].handler = NULL; + } + INIT_LIST_HEAD(&thread_pool[i].sig_queue_head.list); } run_thread_head = (Thread *)kmalloc(sizeof(Thread)); @@ -50,7 +57,7 @@ Thread *thread_create(void(*func)()){ new_thread->ctx.lr = (unsigned long)func; for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ - new_thread->signal_handler[i] = sig_default_handler; + new_thread->sig_info_pool[i].handler = sig_default_handler; } print_string(UITOHEX, "[*] new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); diff --git a/kernel/src/signal.c b/kernel/src/signal.c index 0e33d778e..a2bf5a72b 100644 --- a/kernel/src/signal.c +++ b/kernel/src/signal.c @@ -1,10 +1,57 @@ #include #include #include +#include +#include +#include + +extern Thread *thread_pool; +extern Thread *run_thread_head; + +void check_sig_queue(TrapFrame *trapFrame){ + disable_irq(); + Thread *current = get_current(); + + // if(current->running_signal == 1) + // goto ENABLE_IRQ; + // uart_puts("HI"); + if(list_empty(¤t->sig_queue_head.list)){ + goto ENABLE_IRQ; + } + + SignalInfo *sigInfo = (SignalInfo *)current->sig_queue_head.list.next; + if(sigInfo->ready > 0){ + sigInfo->ready--; + /* call the default handler(do_exit(0)) */ + if(sigInfo->handler == sig_default_handler){ + sigInfo->handler(); + } + /* if not default handler, call the user signal handler */ + else{ + current->sig_stack_addr = kmalloc(STACK_SIZE); + current->old_tp = kmalloc(sizeof(TrapFrame)); + /* save the trapFrame into old_ctx */ + memcpy((char*)current->old_tp, (char*)trapFrame, sizeof(TrapFrame)); + trapFrame->x[0] = (unsigned long)sigInfo->handler; + trapFrame->elr_el1 = (unsigned long)sig_register_handler; + trapFrame->sp_el0 = (unsigned long)current->sig_stack_addr + STACK_SIZE; + } + list_del(&sigInfo->list); + } +ENABLE_IRQ: + enable_irq(); +} + + +void sig_register_handler(SigHandler handler){ + handler(); + asm volatile( + "mov x8, 20\n\t" + "svc 0\n\t" + ); +} void sig_default_handler(){ - TrapFrame fake_tf; - fake_tf.x[0] = get_current()->id; - sys_kill(&fake_tf); + do_exit(0); } \ No newline at end of file diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 611481262..67331fd53 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -159,6 +159,14 @@ int do_fork(TrapFrame *trapFrame){ /* copy context */ memcpy((char *)&new_thread->ctx, (char *)&curr_thread->ctx, sizeof(CpuContext)); + /* copy signal */ + memcpy((char *)&new_thread->sig_info_pool, (char *)&curr_thread->sig_info_pool, MAX_SIG_HANDLER); + for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ + if(new_thread->sig_info_pool[i].ready > 0){ + list_add_tail(&new_thread->sig_info_pool[i].list, &new_thread->sig_queue_head.list); + } + } + // print_string(UITOHEX, "(child)new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); @@ -169,7 +177,7 @@ int do_fork(TrapFrame *trapFrame){ // (trapFrame->elr_el1 - (unsigned long)curr_thread->code_addr); new_trapFrame->elr_el1 = trapFrame->elr_el1; - /* set new code return to after eret */ + /* set new pc return to after eret */ new_trapFrame->sp_el0 = ((unsigned long)new_thread->ustack_addr + STACK_SIZE) - (((unsigned long)curr_thread->ustack_addr + STACK_SIZE) - trapFrame->sp_el0); @@ -255,6 +263,26 @@ int do_signal_kill(int pid, int signal){ if(thread_pool[pid].state != RUNNING) return -1; - thread_pool[pid].signal_count[signal]++; + /* + * add the signal to the thread's ready queue + * if ther signal isn't in ready queue, add it. + */ + if(thread_pool[pid].sig_info_pool[signal].ready == 0){ + list_add_tail(&thread_pool[pid].sig_info_pool[signal].list, &thread_pool[pid].sig_queue_head.list); + } + thread_pool[pid].sig_info_pool[signal].ready++; return 0; +} + +void sys_sigreturn(TrapFrame *trapFrame){ + disable_irq(); + Thread *current = get_current(); + /* load the old trap frame */ + memcpy((char *)¤t->old_tp, (char *)trapFrame, sizeof(trapFrame)); + kfree(current->sig_stack_addr); + kfree(current->old_tp); + current->sig_stack_addr = NULL; + current->old_tp = NULL; + + enable_irq(); } \ No newline at end of file From 09e326f7b033fea811dd6f24a26a31b20434606d Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 21 May 2022 21:47:18 +0800 Subject: [PATCH 23/62] signal: POSIX Signal done(qemu ok) --- debug.gdb | 2 +- kernel/include/syscall.h | 4 +++- kernel/src/exc.c | 2 +- kernel/src/main.c | 2 +- kernel/src/sched.c | 27 +++++++++++++++++++++--- kernel/src/signal.c | 6 +++--- kernel/src/syscall.c | 45 +++++++++++++++++++++++++++++++++++----- 7 files changed, 73 insertions(+), 15 deletions(-) diff --git a/debug.gdb b/debug.gdb index e630ff794..53c642965 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *0x81920 +b *sys_sigreturn target remote :1234 \ No newline at end of file diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index d2c8bfdd6..07615be27 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -1,6 +1,7 @@ #ifndef SYSCALL_H_ #define SYSCALL_H_ #include +#include typedef struct _TrapFrame{ unsigned long long x[31]; @@ -18,7 +19,7 @@ void sys_fork(TrapFrame *); void sys_exit(TrapFrame *); void sys_mbox_call(TrapFrame *); void sys_kill(TrapFrame *); -void sys_signal(TrapFrame *); +void sys_signal_register(TrapFrame *); void sys_signal_kill(TrapFrame *); void sys_sigreturn(TrapFrame *); @@ -28,6 +29,7 @@ void *load_program(file_info *fileInfo); int do_fork(TrapFrame *trapFrame); void do_exit(int status); int do_kill(int pid); +int do_signal_register(int signal, SigHandler handler); int do_signal_kill(int pid, int signal); int kernel_exec(char *name); diff --git a/kernel/src/exc.c b/kernel/src/exc.c index 97d4d0127..021dcfaf6 100644 --- a/kernel/src/exc.c +++ b/kernel/src/exc.c @@ -57,7 +57,7 @@ void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ sys_kill(trapFrame); break; case SIGNAL_: - sys_signal(trapFrame); + sys_signal_register(trapFrame); break; case SIGKILL: sys_signal_kill(trapFrame); diff --git a/kernel/src/main.c b/kernel/src/main.c index 623309e2f..2c07a9190 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -29,7 +29,7 @@ int main(unsigned long dtb_base){ enable_timer_irq(); enable_irq(); // DAIF set to 0b0000 - // kernel_exec("user2.img"); + // kernel_exec("syscall.img"); // kernel_main(); PrintWelcome(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 512a8b541..69cb3985a 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -24,12 +24,15 @@ void init_thread_pool_and_head(){ thread_pool[i].code_addr = NULL; thread_pool[i].code_size = 0; + /* init signal */ for(unsigned int j = 0; j < MAX_SIG_HANDLER; j++){ INIT_LIST_HEAD(&thread_pool[i].sig_info_pool[j].list); thread_pool[i].sig_info_pool[j].ready = 0; thread_pool[i].sig_info_pool[j].handler = NULL; } INIT_LIST_HEAD(&thread_pool[i].sig_queue_head.list); + thread_pool[i].sig_stack_addr = NULL; + thread_pool[i].old_tp = NULL; } run_thread_head = (Thread *)kmalloc(sizeof(Thread)); @@ -58,6 +61,7 @@ Thread *thread_create(void(*func)()){ for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ new_thread->sig_info_pool[i].handler = sig_default_handler; + INIT_LIST_HEAD(&new_thread->sig_info_pool[i].list); } print_string(UITOHEX, "[*] new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); @@ -87,15 +91,32 @@ void kill_zombie(){ if(tmp->state == EXIT){ kfree(tmp->ustack_addr); kfree(tmp->kstack_addr); - tmp->state = NOUSE; tmp->ustack_addr = NULL; tmp->kstack_addr = NULL; + tmp->code_addr = NULL; + tmp->code_size = 0; + tmp->state = NOUSE; /* cannot remove code_addr beacuse fork process share the code?? */ // if(tmp->code_addr != NULL){ // kfree(tmp->code_addr); // } - tmp->code_addr = NULL; - tmp->code_size = 0; + + + /* init signal */ + for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ + tmp->sig_info_pool[i].ready = 0; + tmp->sig_info_pool[i].handler = sig_default_handler; + INIT_LIST_HEAD(&tmp->sig_info_pool[i].list); + } + INIT_LIST_HEAD(&tmp->sig_queue_head.list); + if(tmp->sig_stack_addr != NULL) + kfree(tmp->sig_stack_addr); + if(tmp->old_tp != NULL) + kfree(tmp->old_tp); + tmp->sig_stack_addr = NULL; + tmp->old_tp = NULL; + + /* remove from run_thread_head */ list_del_entry(&tmp->list); } } diff --git a/kernel/src/signal.c b/kernel/src/signal.c index a2bf5a72b..e28a0cd9a 100644 --- a/kernel/src/signal.c +++ b/kernel/src/signal.c @@ -12,16 +12,16 @@ void check_sig_queue(TrapFrame *trapFrame){ disable_irq(); Thread *current = get_current(); + // if(current->running_signal == 1) // goto ENABLE_IRQ; - // uart_puts("HI"); if(list_empty(¤t->sig_queue_head.list)){ goto ENABLE_IRQ; } - + SignalInfo *sigInfo = (SignalInfo *)current->sig_queue_head.list.next; if(sigInfo->ready > 0){ - sigInfo->ready--; + sigInfo->ready = 0; /* call the default handler(do_exit(0)) */ if(sigInfo->handler == sig_default_handler){ sigInfo->handler(); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 67331fd53..f3ace92b1 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include extern Thread *thread_pool; extern Thread *run_thread_head; @@ -81,6 +83,22 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ /* set current trapFrame elr_el1(begin of code) and sp_el0(begin of user stack)*/ trapFrame->elr_el1 = (unsigned long)curr_thread->code_addr; trapFrame->sp_el0 = (unsigned long)curr_thread->ustack_addr + STACK_SIZE; + + + /* maybe need to reset the signal 0.0? */ + for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ + curr_thread->sig_info_pool[i].ready = 0; + curr_thread->sig_info_pool[i].handler = sig_default_handler; + INIT_LIST_HEAD(&curr_thread->sig_info_pool[i].list); + } + INIT_LIST_HEAD(&curr_thread->sig_queue_head.list); + if(curr_thread->sig_stack_addr != NULL) + kfree(curr_thread->sig_stack_addr); + if(curr_thread->old_tp != NULL) + kfree(curr_thread->old_tp); + curr_thread->sig_stack_addr = NULL; + curr_thread->old_tp = NULL; + enable_irq(); return 0; } @@ -160,7 +178,7 @@ int do_fork(TrapFrame *trapFrame){ memcpy((char *)&new_thread->ctx, (char *)&curr_thread->ctx, sizeof(CpuContext)); /* copy signal */ - memcpy((char *)&new_thread->sig_info_pool, (char *)&curr_thread->sig_info_pool, MAX_SIG_HANDLER); + memcpy((char *)&new_thread->sig_info_pool, (char *)&curr_thread->sig_info_pool, MAX_SIG_HANDLER * sizeof(SignalInfo)); for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ if(new_thread->sig_info_pool[i].ready > 0){ list_add_tail(&new_thread->sig_info_pool[i].list, &new_thread->sig_queue_head.list); @@ -239,8 +257,25 @@ int do_kill(int pid){ return 0; } -void sys_signal(TrapFrame *trapFrame){ +void sys_signal_register(TrapFrame *trapFrame){ + disable_irq(); + int signal = trapFrame->x[0]; + SigHandler handler = (SigHandler)trapFrame->x[1]; + int status = do_signal_register(signal, handler); + if(status == -1) + print_string(UITOA, "[x] signal register fail, signal: ", signal, 1); + // else + // print_string(UITOA, "[*] signal register success, signal: ", signal, 1); + enable_irq(); +} +int do_signal_register(int signal, SigHandler handler){ + Thread *curr_thread = get_current(); + if(!(signal >= 0 && signal < MAX_SIG_HANDLER)) + return -1; + + curr_thread->sig_info_pool[signal].handler = handler; + return 0; } void sys_signal_kill(TrapFrame *trapFrame){ @@ -251,8 +286,8 @@ void sys_signal_kill(TrapFrame *trapFrame){ if(status == -1) print_string(UITOA, "[x] signal_kill fail, pid: ", pid, 1); - else - print_string(UITOA, "[*] signal_kill ready, pid: ", pid, 1); + // else + // print_string(UITOA, "[*] signal_kill ready, pid: ", pid, 1); enable_irq(); } @@ -278,7 +313,7 @@ void sys_sigreturn(TrapFrame *trapFrame){ disable_irq(); Thread *current = get_current(); /* load the old trap frame */ - memcpy((char *)¤t->old_tp, (char *)trapFrame, sizeof(trapFrame)); + memcpy((char *)trapFrame, (char *)current->old_tp, sizeof(TrapFrame)); kfree(current->sig_stack_addr); kfree(current->old_tp); current->sig_stack_addr = NULL; From ec411d0f414b61ebae4101b0a63031f25f5d46dc Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 21 May 2022 22:37:13 +0800 Subject: [PATCH 24/62] ALL DONE!!! --- kernel/src/irq.c | 2 -- kernel/src/syscall.c | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/src/irq.c b/kernel/src/irq.c index 9ea4dfcb6..9ec559d4f 100644 --- a/kernel/src/irq.c +++ b/kernel/src/irq.c @@ -14,8 +14,6 @@ void irq_handler(unsigned long long spsr, TrapFrame *trapFrame){ if(*CORE0_IRQ_SOURCE & 0x2) Time_interrupt(spsr); else if(*CORE0_IRQ_SOURCE & 0x100) GPU_interrupt(); - // print_string(UITOHEX, "spsr = ", spsr, 0); - // print_string(UITOHEX, "pid = ", get_current()->id, 1); /* check the spsr if it is from user mode */ spsr &= 0b1111; if(spsr == 0x0){ diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index f3ace92b1..9e548e910 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -34,6 +34,7 @@ int do_getpid(){ /* Return the number of bytes read by reading size byte into the user-supplied buffer buf. */ void sys_uart_read(TrapFrame *trapFrame){ + disable_irq(); char *buf = (char *)trapFrame->x[0]; unsigned int size = trapFrame->x[1]; enable_irq(); @@ -45,12 +46,13 @@ void sys_uart_read(TrapFrame *trapFrame){ /* Return the number of bytes written after writing size byte from the user-supplied buffer buf. */ void sys_uart_write(TrapFrame *trapFrame){ + disable_irq(); const char *buf = (char *)trapFrame->x[0]; unsigned int size = trapFrame->x[1]; unsigned int i; enable_irq(); for(i = 0; i < size; i++){ - async_uart_putc(buf[i]); + uart_putc(buf[i]); } disable_irq(); trapFrame->x[0] = i; From 96a4c1f66b90bd5e13447820174fa2bfd1d796d5 Mon Sep 17 00:00:00 2001 From: Fan Date: Sun, 22 May 2022 20:34:37 +0800 Subject: [PATCH 25/62] update lab5 readme --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 157ceff87..395ed60ff 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,17 @@ sudo ../uartboot.py ┃ ┃ ┣ 📜math.h ┃ ┃ ┣ 📜read.h ┃ ┃ ┣ 📜reboot.h + ┃ ┃ ┣ 📜sched.h ┃ ┃ ┣ 📜shell.h + ┃ ┃ ┣ 📜signal.h ┃ ┃ ┣ 📜stddef.h ┃ ┃ ┣ 📜stdint.h ┃ ┃ ┣ 📜string.h + ┃ ┃ ┣ 📜syscall.h ┃ ┃ ┣ 📜task.h ┃ ┃ ┣ 📜timer.h - ┃ ┃ ┗ 📜uart.h + ┃ ┃ ┣ 📜uart.h + ┃ ┃ ┗ 📜user_syscall.h ┃ ┣ 📂initramfs ┃ ┃ ┣ 📜me ┃ ┃ ┣ 📜test1 @@ -94,20 +98,25 @@ sudo ../uartboot.py ┃ ┣ 📂src ┃ ┃ ┣ 📜allocator.c ┃ ┃ ┣ 📜cpio.c + ┃ ┃ ┣ 📜ctx_switch.S ┃ ┃ ┣ 📜exc.c ┃ ┃ ┣ 📜fdt.c ┃ ┃ ┣ 📜irq.c ┃ ┃ ┣ 📜mailbox.c ┃ ┃ ┣ 📜main.c ┃ ┃ ┣ 📜malloc.c - ┃ ┃ ┣ 📜math.c ┃ ┃ ┣ 📜read.c + ┃ ┃ ┣ 📜math.c ┃ ┃ ┣ 📜reboot.c + ┃ ┃ ┣ 📜sched.c ┃ ┃ ┣ 📜shell.c + ┃ ┃ ┣ 📜signal.c ┃ ┃ ┣ 📜string.c + ┃ ┃ ┣ 📜syscall.c ┃ ┃ ┣ 📜task.c ┃ ┃ ┣ 📜timer.c - ┃ ┃ ┗ 📜uart.c + ┃ ┃ ┣ 📜uart.c + ┃ ┃ ┗ 📜user_syscall.S ┃ ┣ 📜Makefile ┃ ┣ 📜bcm2710-rpi-3-b-plus.dtb ┃ ┗ 📜initramfs.cpio From b66d50667b26f28d954f30ddd8f301423f972b59 Mon Sep 17 00:00:00 2001 From: Fan Date: Mon, 23 May 2022 02:19:04 +0800 Subject: [PATCH 26/62] vfs: create rootfs done --- kernel/include/stddef.h | 4 +- kernel/include/tmpfs.h | 23 +++++++++++ kernel/include/vfs.h | 75 ++++++++++++++++++++++++++++++++++++ kernel/src/tmpfs.c | 84 +++++++++++++++++++++++++++++++++++++++++ kernel/src/vfs.c | 64 +++++++++++++++++++++++++++++++ 5 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 kernel/include/tmpfs.h create mode 100644 kernel/include/vfs.h create mode 100644 kernel/src/tmpfs.c create mode 100644 kernel/src/vfs.c diff --git a/kernel/include/stddef.h b/kernel/include/stddef.h index 7b4f2876d..ee12537bb 100644 --- a/kernel/include/stddef.h +++ b/kernel/include/stddef.h @@ -3,7 +3,7 @@ #define NULL (void *)0x0 #define uint32_t unsigned int -#define unit64_t unsigned long long - +#define uint64_t unsigned long long +#define size_t unsigned long #endif \ No newline at end of file diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h new file mode 100644 index 000000000..49cb0acec --- /dev/null +++ b/kernel/include/tmpfs.h @@ -0,0 +1,23 @@ +#ifndef TMPFS_H_ +#define TMPFS_H_ + +#include + + +int tmpfs_setup_mount(FileSystem *fs, Mount *mount); +Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type); +VNode *tmpfs_create_vnode(Dentry *dentry); +void tmpfs_set_ops(); + +int tmpfs_write(struct file* file, const void* buf, size_t len); +int tmpfs_read(struct file* file, void* buf, size_t len); +int tmpfs_open(struct vnode* file_node, struct file** target); +int tmpfs_close(struct file* file); +long tmpfs_lseek64(struct file* file, long offset, int whence); + +int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name); +int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name); + + +#endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h new file mode 100644 index 000000000..88063c989 --- /dev/null +++ b/kernel/include/vfs.h @@ -0,0 +1,75 @@ +#ifndef VFS_H_ +#define VFS_H_ + +#include +#include + +enum dentry_type { + D_DICT, + D_FILE +}; + +// file handle +typedef struct file { + struct vnode* vnode; + size_t f_pos; // RW position of this file handle + struct file_operations* f_ops; + int flags; +}File; + +typedef struct vnode { + struct dentry* dentry; + struct vnode_operations* v_ops; + struct file_operations* f_ops; + void* internal; +}VNode; + +typedef struct dentry { + struct list_head list; + struct mount* mount; + char* name; + enum dentry_type type; + struct vnode* vnode; +}Dentry; + +typedef struct mount { + struct filesystem* fs; + struct dentry* root; +}Mount; + +typedef struct filesystem { + char* name; + int (*setup_mount)(struct filesystem* fs, struct mount* mount); +}FileSystem; + +struct file_operations { + int (*write)(struct file* file, const void* buf, size_t len); + int (*read)(struct file* file, void* buf, size_t len); + int (*open)(struct vnode* file_node, struct file** target); + int (*close)(struct file* file); + long (*lseek64)(struct file* file, long offset, int whence); +}; + +struct vnode_operations { + int (*lookup)(struct vnode* dir_node, struct vnode** target, + const char* component_name); + int (*create)(struct vnode* dir_node, struct vnode** target, + const char* component_name); + int (*mkdir)(struct vnode* dir_node, struct vnode** target, + const char* component_name); +}; + + + + +void vfs_init(); +int register_filesystem(FileSystem* fs); +int vfs_open(const char* pathname, int flags, struct file** target); +int vfs_close(struct file* file); +int vfs_write(struct file* file, const void* buf, size_t len); +int vfs_read(struct file* file, void* buf, size_t len); +int vfs_mkdir(const char* pathname); +int vfs_mount(const char* target, const char* filesystem); +int vfs_lookup(const char* pathname, struct vnode** target); + +#endif \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c new file mode 100644 index 000000000..f1ce37708 --- /dev/null +++ b/kernel/src/tmpfs.c @@ -0,0 +1,84 @@ +#include +#include +#include + +struct file_operations* tmpfs_file_ops; +struct vnode_operations* tmpfs_vnode_ops; + +int tmpfs_setup_mount(FileSystem *fs, Mount *mount){ + mount->fs = fs; + mount->root = tmpfs_create_dentry("/", NULL, D_DICT); + return 0; +} + +Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type){ + Dentry *new_dentry = (Dentry *)kmalloc(sizeof(Dentry)); + new_dentry->name = (char *)kmalloc(sizeof(char) * strlen(name)); + strcpy(new_dentry->name, name); + if(parent != NULL){ + list_add(&parent->list, &new_dentry->list); + } + new_dentry->type = type; + new_dentry->mount = NULL; + new_dentry->vnode = tmpfs_create_vnode(new_dentry); + return new_dentry; +} + +VNode *tmpfs_create_vnode(Dentry *dentry){ + VNode *new_vnode = (VNode *)kmalloc(sizeof(VNode)); + new_vnode->dentry = dentry; + new_vnode->v_ops = tmpfs_vnode_ops; + new_vnode->f_ops = tmpfs_file_ops; + new_vnode->internal = NULL; + return new_vnode; +} + +void tmpfs_set_ops(){ + tmpfs_file_ops = (struct file_operations *)kmalloc(sizeof(struct file_operations)); + tmpfs_vnode_ops = (struct vnode_operations *)kmalloc(sizeof(struct vnode_operations)); + + tmpfs_file_ops->write = tmpfs_write; + tmpfs_file_ops->read = tmpfs_read; + tmpfs_file_ops->open = tmpfs_open; + tmpfs_file_ops->close = tmpfs_close; + tmpfs_file_ops->lseek64 = tmpfs_lseek64; + + tmpfs_vnode_ops->lookup = tmpfs_lookup; + tmpfs_vnode_ops->create = tmpfs_create; + tmpfs_vnode_ops->mkdir = tmpfs_mkdir; +} + +int tmpfs_write(struct file* file, const void* buf, size_t len){ + + return 0; +} +int tmpfs_read(struct file* file, void* buf, size_t len){ + + return 0; +} +int tmpfs_open(struct vnode* file_node, struct file** target){ + + return 0; +} +int tmpfs_close(struct file* file){ + + return 0; +} +long tmpfs_lseek64(struct file* file, long offset, int whence){ + + return 0; +} + + +int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name){ + + return 0; +} +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ + + return 0; +} +int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name){ + + return 0; +} \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c new file mode 100644 index 000000000..1262495fa --- /dev/null +++ b/kernel/src/vfs.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +Mount *rootfs; + +void rootfs_init(char *fs_name){ + if(strcmp(fs_name, "tmpfs") == 0){ + FileSystem *tmpfs = (FileSystem *)kmalloc(sizeof(FileSystem)); + tmpfs->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" + strcpy(tmpfs->name, "tmpfs"); + tmpfs->setup_mount = tmpfs_setup_mount; + int err = register_filesystem(tmpfs); + if(err){ + uart_puts("Failed to register tmpfs filesystem\n"); + } + } +} + + +int register_filesystem(FileSystem *fs) { + // register the file system to the kernel. + // you can also initialize memory pool of the file system here. + if(fs == NULL) return -1; + if(fs->name == NULL || fs->setup_mount == NULL) return -1; + if(strcmp(fs->name, "tmpfs") == 0){ + tmpfs_set_ops(); + } + fs->setup_mount(fs, rootfs); + + return 0; +} + +int vfs_open(const char* pathname, int flags, struct file** target) { + // 1. Lookup pathname + // 2. Create a new file handle for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags and vnode not found + // lookup error code shows if file exist or not or other error occurs + // 4. Return error code if fails + return 0; +} + +int vfs_close(struct file* file) { + // 1. release the file handle + // 2. Return error code if fails + return 0; + +} + +int vfs_write(struct file* file, const void* buf, size_t len) { + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return 0; +} + +int vfs_read(struct file* file, void* buf, size_t len) { + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 2. return read size or error code if an error occurs. + return 0; +} + From 66275b08941facbeb6a6c92456854f2128a4477c Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 24 May 2022 01:21:06 +0800 Subject: [PATCH 27/62] vfs_lookup: ok but no test --- kernel/Makefile | 2 +- kernel/include/vfs.h | 57 +++++++++++++++------------- kernel/src/main.c | 3 +- kernel/src/tmpfs.c | 43 ++++++++++++++++++--- kernel/src/vfs.c | 90 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 156 insertions(+), 39 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index 1fe20b383..97317b0b3 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -17,7 +17,7 @@ ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -all: clean kernel8.img run-display +all: clean kernel8.img run $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 88063c989..114353e39 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -5,27 +5,29 @@ #include enum dentry_type { - D_DICT, + D_DIR, D_FILE }; // file handle typedef struct file { - struct vnode* vnode; - size_t f_pos; // RW position of this file handle - struct file_operations* f_ops; - int flags; + struct vnode* vnode; + size_t f_pos; // RW position of this file handle + struct file_operations* f_ops; + int flags; }File; typedef struct vnode { - struct dentry* dentry; - struct vnode_operations* v_ops; - struct file_operations* f_ops; - void* internal; + struct dentry* dentry; + struct vnode_operations* v_ops; + struct file_operations* f_ops; + void* internal; }VNode; typedef struct dentry { struct list_head list; + struct list_head childs; + struct dentry *parent; struct mount* mount; char* name; enum dentry_type type; @@ -33,43 +35,46 @@ typedef struct dentry { }Dentry; typedef struct mount { - struct filesystem* fs; - struct dentry* root; + struct filesystem* fs; + struct dentry* root_dentry; }Mount; typedef struct filesystem { - char* name; - int (*setup_mount)(struct filesystem* fs, struct mount* mount); + char* name; + int (*setup_mount)(struct filesystem* fs, struct mount* mount); }FileSystem; struct file_operations { - int (*write)(struct file* file, const void* buf, size_t len); - int (*read)(struct file* file, void* buf, size_t len); - int (*open)(struct vnode* file_node, struct file** target); - int (*close)(struct file* file); - long (*lseek64)(struct file* file, long offset, int whence); + int (*write)(struct file* file, const void* buf, size_t len); + int (*read)(struct file* file, void* buf, size_t len); + int (*open)(struct vnode* file_node, struct file** target); + int (*close)(struct file* file); + long (*lseek64)(struct file* file, long offset, int whence); }; struct vnode_operations { - int (*lookup)(struct vnode* dir_node, struct vnode** target, - const char* component_name); - int (*create)(struct vnode* dir_node, struct vnode** target, - const char* component_name); - int (*mkdir)(struct vnode* dir_node, struct vnode** target, - const char* component_name); + int (*lookup)(struct vnode* dir_node, struct vnode** target, + const char* component_name); + int (*create)(struct vnode* dir_node, struct vnode** target, + const char* component_name); + int (*mkdir)(struct vnode* dir_node, struct vnode** target, + const char* component_name); }; -void vfs_init(); +void rootfs_init(); int register_filesystem(FileSystem* fs); int vfs_open(const char* pathname, int flags, struct file** target); +int vfs_lookup(const char* pathname, Dentry *target_dentry, VNode *target_vnode); +// int vfs_lookup(const char *pathname, Dentry *target_dentry, VNode *target_vnode); +void find_component_name(const char *pathname, char *target_name, char delimiter); + int vfs_close(struct file* file); int vfs_write(struct file* file, const void* buf, size_t len); int vfs_read(struct file* file, void* buf, size_t len); int vfs_mkdir(const char* pathname); int vfs_mount(const char* target, const char* filesystem); -int vfs_lookup(const char* pathname, struct vnode** target); #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 2c07a9190..da8997b99 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -9,6 +9,7 @@ #include #include #include +#include extern Thread *run_thread_head; @@ -31,7 +32,7 @@ int main(unsigned long dtb_base){ // kernel_exec("syscall.img"); // kernel_main(); - + rootfs_init("tmpfs"); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index f1ce37708..62851df85 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -7,7 +7,7 @@ struct vnode_operations* tmpfs_vnode_ops; int tmpfs_setup_mount(FileSystem *fs, Mount *mount){ mount->fs = fs; - mount->root = tmpfs_create_dentry("/", NULL, D_DICT); + mount->root_dentry = tmpfs_create_dentry("/", NULL, D_DIR); return 0; } @@ -15,8 +15,10 @@ Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type t Dentry *new_dentry = (Dentry *)kmalloc(sizeof(Dentry)); new_dentry->name = (char *)kmalloc(sizeof(char) * strlen(name)); strcpy(new_dentry->name, name); - if(parent != NULL){ - list_add(&parent->list, &new_dentry->list); + new_dentry->parent = parent; + if(new_dentry->parent != NULL){ + /* add to parent's child list */ + list_add_tail(&parent->childs, &new_dentry->list); } new_dentry->type = type; new_dentry->mount = NULL; @@ -71,8 +73,39 @@ long tmpfs_lseek64(struct file* file, long offset, int whence){ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name){ - - return 0; + // if(*component_name == '\0'){ + // /* if the target name is empty, it is end of the directory */ + // return 0; + // } + if(strcmp(component_name, ".")){ + /* if the target name is ".", it is the same directory */ + *target = dir_node; + return 0; + } + else if(strcmp(component_name, "..")){ + /* if dentry is root path, return it */ + if(dir_node->dentry->parent == NULL) return 0; + /* if dentry is not root path, find its parent */ + *target = dir_node->dentry->parent->vnode; + return 0; + } + else{ + /* need to find the child dentry */ + struct list_head *pos; + list_for_each(pos, &dir_node->dentry->childs){ + Dentry *tmp = (Dentry *)pos; + if(strcmp(tmp->name, component_name) == 0){ + // TODO: need to check the dir is other filesystem + + /* if the target is a file, return it */ + *target = tmp->vnode; + return 0; + } + } + } + /* if the target is not found, return error code */ + *target = NULL; + return -1; } int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 1262495fa..ff548f1cb 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -7,16 +7,17 @@ Mount *rootfs; void rootfs_init(char *fs_name){ + FileSystem *fs = NULL; if(strcmp(fs_name, "tmpfs") == 0){ FileSystem *tmpfs = (FileSystem *)kmalloc(sizeof(FileSystem)); tmpfs->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" strcpy(tmpfs->name, "tmpfs"); tmpfs->setup_mount = tmpfs_setup_mount; - int err = register_filesystem(tmpfs); - if(err){ - uart_puts("Failed to register tmpfs filesystem\n"); - } + fs = tmpfs; } + + int err = register_filesystem(fs); + if(err) uart_puts("[x] Failed to register filesystem\n"); } @@ -27,21 +28,98 @@ int register_filesystem(FileSystem *fs) { if(fs->name == NULL || fs->setup_mount == NULL) return -1; if(strcmp(fs->name, "tmpfs") == 0){ tmpfs_set_ops(); + uart_puts("[*] Registered tmpfs\n"); } fs->setup_mount(fs, rootfs); return 0; } -int vfs_open(const char* pathname, int flags, struct file** target) { - // 1. Lookup pathname +int vfs_open(const char* pathname, int flags, struct file** target_file) { + if(pathname == NULL || *target_file == NULL) return -1; + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + int err = vfs_lookup(pathname, target_path, target_vnode); + if(err) return -1; // 2. Create a new file handle for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags and vnode not found // lookup error code shows if file exist or not or other error occurs // 4. Return error code if fails return 0; } +int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode) { + char component_name[256]; + // 1. Lookup pathname + if(pathname[0] == '/'){ + int idx = 1; + target_path = rootfs->root_dentry; + find_component_name(pathname + idx, component_name, '/'); + while(component_name[0] != '\0'){ + /* find the next vnode */ + if(target_vnode != NULL) target_path = target_vnode->dentry; + int err = target_path->vnode->v_ops->lookup(target_path->vnode, &target_vnode, component_name); + if(err) return err; + + /* find next component name*/ + idx += strlen(component_name) + 1; + find_component_name(pathname + idx, component_name, '/'); + } + } + + // TODO: check the relative path + return 0; +} + +void find_component_name(const char *pathname, char *component_name, char delimiter){ + int i = 0; + while(pathname[i] != delimiter && pathname[i] != '\0'){ + component_name[i] = pathname[i]; + i++; + } + component_name[i] = '\0'; +} + +// int traversal_path(const char *pathname, Dentry *target_path, char *target_name){ +// find_target_name(pathname, target_name, '/'); +// if(*target_name == '\0'){ +// /* if the target name is empty, it is end of the directory */ +// return 0; +// } +// else if(strcmp(target_name, ".")){ +// /* if the target name is ".", it is the same directory */ +// return traversal_path(pathname + strlen(target_name) + 1, target_path, target_name); +// } +// else if(strcmp(target_name, "..")){ +// /* if dentry is root path, return it */ +// if(target_path->parent == NULL) return 0; +// /* if dentry is not root path, find its parent */ +// return traversal_path(pathname + strlen(target_name) + 1, target_path->parent, target_name); +// } +// else{ +// /* need to find the child dentry */ +// struct list_head *pos; +// list_for_each(pos, &target_path->childs){ +// Dentry *tmp = (Dentry *)pos; +// if(strcmp(tmp->name, target_name) == 0){ +// // TODO: need to check the dir is other filesystem + +// if(target_path->type == D_DIR){ +// return traversal_path(pathname + strlen(target_name) + 1, tmp, target_name); +// } + +// /* if the target is a file, break and return it */ +// return 0; +// } +// } +// } +// /* if the target is not found, return error code */ +// return -1; +// } + + + int vfs_close(struct file* file) { // 1. release the file handle // 2. Return error code if fails From 99a5562a8b7dc4b7abf69a4c53f248de43820448 Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 24 May 2022 04:26:52 +0800 Subject: [PATCH 28/62] vfs_open: limit the last name when O_CREAT --- kernel/include/vfs.h | 5 ++++- kernel/src/tmpfs.c | 4 +++- kernel/src/vfs.c | 48 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 114353e39..10c44e333 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -8,6 +8,8 @@ enum dentry_type { D_DIR, D_FILE }; +#define O_CREAT 1 +#define MAX_PATHNAME_LEN 256 // file handle typedef struct file { @@ -67,9 +69,10 @@ struct vnode_operations { void rootfs_init(); int register_filesystem(FileSystem* fs); int vfs_open(const char* pathname, int flags, struct file** target); -int vfs_lookup(const char* pathname, Dentry *target_dentry, VNode *target_vnode); +int vfs_lookup(const char* pathname, Dentry *target_dentry, VNode *target_vnode, char *component_name); // int vfs_lookup(const char *pathname, Dentry *target_dentry, VNode *target_vnode); void find_component_name(const char *pathname, char *target_name, char delimiter); +File *create_fd(VNode *target_vnode); int vfs_close(struct file* file); int vfs_write(struct file* file, const void* buf, size_t len); diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 62851df85..368833b5f 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -107,10 +107,12 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp *target = NULL; return -1; } -int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ + return 0; } + int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name){ return 0; diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index ff548f1cb..42b5f289a 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -39,33 +39,58 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { if(pathname == NULL || *target_file == NULL) return -1; Dentry *target_path = NULL; VNode *target_vnode = NULL; - int err = vfs_lookup(pathname, target_path, target_vnode); - if(err) return -1; + char component_name[MAX_PATHNAME_LEN]; + int err = vfs_lookup(pathname, target_path, target_vnode, component_name); + if(err == -1) return err; // worng pathname // 2. Create a new file handle for this vnode if found. - + if(target_vnode != NULL){ + *target_file = create_fd(target_vnode); + return 0; + } // 3. Create a new file if O_CREAT is specified in flags and vnode not found // lookup error code shows if file exist or not or other error occurs + else{ + if(flags & O_CREAT){ + // create a new file + int err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); + if(err) return err; + } + } + // 4. Return error code if fails return 0; } -int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode) { - char component_name[256]; +int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode, char *component_name) { + int ready_return = 0; // 1. Lookup pathname if(pathname[0] == '/'){ int idx = 1; + char tmp_buf[MAX_PATHNAME_LEN]; target_path = rootfs->root_dentry; find_component_name(pathname + idx, component_name, '/'); while(component_name[0] != '\0'){ + /* + * ready_return is 1 beacuse it couldn't find child vnode before + * The next component name is not '\0' now, it's not the last component name + * Therefore, it is a wrong pathname, even if the flag is O_CREAT, it also cannot create a new file + */ + if(ready_return) return -1; /* find the next vnode */ if(target_vnode != NULL) target_path = target_vnode->dentry; - int err = target_path->vnode->v_ops->lookup(target_path->vnode, &target_vnode, component_name); - if(err) return err; + int err = rootfs->root_dentry->vnode->v_ops->lookup(target_path->vnode, &target_vnode, component_name); + if(err){ + ready_return = 1; + /* save the component name in tmp_buf beacuse it will check the last name */ + strcpy(tmp_buf, component_name); + } /* find next component name*/ idx += strlen(component_name) + 1; find_component_name(pathname + idx, component_name, '/'); } + /* if the last component name is '\0', it is a file name */ + if(ready_return) strcpy(component_name, tmp_buf); } // TODO: check the relative path @@ -81,6 +106,15 @@ void find_component_name(const char *pathname, char *component_name, char delimi component_name[i] = '\0'; } +File *create_fd(VNode *target_vnode){ + File *new_file = (File *)kmalloc(sizeof(File)); + new_file->vnode = target_vnode; + new_file->f_pos = 0; + new_file->f_ops = target_vnode->f_ops; + new_file->flags = 0; + return new_file; +} + // int traversal_path(const char *pathname, Dentry *target_path, char *target_name){ // find_target_name(pathname, target_name, '/'); // if(*target_name == '\0'){ From 651866a48bf9c79014c61982cd0e0c186ff0c35b Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 24 May 2022 20:13:58 +0800 Subject: [PATCH 29/62] tmpfs create & open & close & read (no test) --- kernel/include/tmpfs.h | 11 +++++++++ kernel/include/vfs.h | 3 ++- kernel/src/tmpfs.c | 53 +++++++++++++++++++++++++++++++++++++++--- kernel/src/vfs.c | 27 +++++++++++---------- 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 49cb0acec..539d4235a 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -2,7 +2,18 @@ #define TMPFS_H_ #include +#include +typedef struct tmpfs_inode{ + struct list_head list; + /* + * inode head: means the num of blocks + * data inode: means the idx + */ + unsigned long idx; + char *data; // data block size = MAX_DATA_LEN + struct vnode* vnode; +}TmpfsInode; int tmpfs_setup_mount(FileSystem *fs, Mount *mount); Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type); diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 10c44e333..2437010d6 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -10,6 +10,8 @@ enum dentry_type { }; #define O_CREAT 1 #define MAX_PATHNAME_LEN 256 +#define MAX_DATA_LEN 512 +#define EOF -1 // file handle typedef struct file { @@ -72,7 +74,6 @@ int vfs_open(const char* pathname, int flags, struct file** target); int vfs_lookup(const char* pathname, Dentry *target_dentry, VNode *target_vnode, char *component_name); // int vfs_lookup(const char *pathname, Dentry *target_dentry, VNode *target_vnode); void find_component_name(const char *pathname, char *target_name, char delimiter); -File *create_fd(VNode *target_vnode); int vfs_close(struct file* file); int vfs_write(struct file* file, const void* buf, size_t len); diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 368833b5f..ba93ae5ec 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -55,15 +55,46 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ return 0; } int tmpfs_read(struct file* file, void* buf, size_t len){ + TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; + char *dest = (char *)buf; + + /* iterate through all data inodes */ + struct list_head *pos; + list_for_each(pos, &inode_head->list){ + TmpfsInode *block = (TmpfsInode *)pos; + if(file->f_pos + MAX_DATA_LEN > len){ + /* len < MAX_DATA_LEN, read len is ok */ + for(int i = 0; i < len && block->data[i] != EOF; i++){ + dest[file->f_pos + i] = block->data[i]; + } + break; + } + else if(file->f_pos + MAX_DATA_LEN <= len){ + /* len >= MAX_DATA_LEN, read MAX_DATA_LEN is ok */ + for(int i = 0; i < MAX_DATA_LEN && block->data[i] != EOF; i++){ + dest[file->f_pos + i] = block->data[i]; + } + file->f_pos += MAX_DATA_LEN; + } + } return 0; } int tmpfs_open(struct vnode* file_node, struct file** target){ - + if(file_node == NULL){ + return -1; + } + File *new_file = (File *)kmalloc(sizeof(File)); + new_file->vnode = file_node; + new_file->f_pos = 0; + new_file->f_ops = file_node->f_ops; + new_file->flags = 0; + *target = new_file; return 0; } int tmpfs_close(struct file* file){ - + kfree(file); + file = NULL; return 0; } long tmpfs_lseek64(struct file* file, long offset, int whence){ @@ -109,7 +140,23 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp } int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ - + /* create the inode list head */ + TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); + inode_head->idx = 1; + /* create the real data block */ + TmpfsInode *inode = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); + inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); + inode->idx = inode_head->idx; + + /* create the dict info */ + Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); + new_dentry->vnode->internal = inode_head; + + /* add the new data block to inode_head */ + list_add_tail(&inode_head->list, &inode->list); + + *target = new_dentry->vnode; + return 0; } diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 42b5f289a..ffad8ed8e 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -44,7 +44,7 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { if(err == -1) return err; // worng pathname // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ - *target_file = create_fd(target_vnode); + err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); return 0; } // 3. Create a new file if O_CREAT is specified in flags and vnode not found @@ -52,13 +52,16 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { else{ if(flags & O_CREAT){ // create a new file - int err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); + err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); if(err) return err; + err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); + if(err) return err; + return 0; } } // 4. Return error code if fails - return 0; + return -1; } int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode, char *component_name) { @@ -94,6 +97,7 @@ int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode, c } // TODO: check the relative path + // TODO: the path need to save in global variable and thread info return 0; } @@ -106,14 +110,6 @@ void find_component_name(const char *pathname, char *component_name, char delimi component_name[i] = '\0'; } -File *create_fd(VNode *target_vnode){ - File *new_file = (File *)kmalloc(sizeof(File)); - new_file->vnode = target_vnode; - new_file->f_pos = 0; - new_file->f_ops = target_vnode->f_ops; - new_file->flags = 0; - return new_file; -} // int traversal_path(const char *pathname, Dentry *target_path, char *target_name){ // find_target_name(pathname, target_name, '/'); @@ -157,20 +153,23 @@ File *create_fd(VNode *target_vnode){ int vfs_close(struct file* file) { // 1. release the file handle // 2. Return error code if fails - return 0; + if(file == NULL) return -1; + return file->f_ops->close(file); } int vfs_write(struct file* file, const void* buf, size_t len) { // 1. write len byte from buf to the opened file. // 2. return written size or error code if an error occurs. - return 0; + if(file == NULL) return -1; + return file->f_ops->write(file, buf, len); } int vfs_read(struct file* file, void* buf, size_t len) { // 1. read min(len, readable size) byte to buf from the opened file. // 2. block if nothing to read for FIFO type // 2. return read size or error code if an error occurs. - return 0; + if(file == NULL) return -1; + return file->f_ops->read(file, buf, len); } From 3c94bb65ea146cfa9ffddd28a4ddc5e888baee0f Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 24 May 2022 20:19:50 +0800 Subject: [PATCH 30/62] tmpfs_read: use block size to read --- kernel/include/tmpfs.h | 1 + kernel/src/tmpfs.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 539d4235a..4d55a371d 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -11,6 +11,7 @@ typedef struct tmpfs_inode{ * data inode: means the idx */ unsigned long idx; + unsigned long size; char *data; // data block size = MAX_DATA_LEN struct vnode* vnode; }TmpfsInode; diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index ba93ae5ec..815e1c798 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -51,6 +51,9 @@ void tmpfs_set_ops(){ } int tmpfs_write(struct file* file, const void* buf, size_t len){ + TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; + + return 0; } @@ -62,19 +65,19 @@ int tmpfs_read(struct file* file, void* buf, size_t len){ struct list_head *pos; list_for_each(pos, &inode_head->list){ TmpfsInode *block = (TmpfsInode *)pos; - if(file->f_pos + MAX_DATA_LEN > len){ + if(file->f_pos + block->size > len){ /* len < MAX_DATA_LEN, read len is ok */ for(int i = 0; i < len && block->data[i] != EOF; i++){ dest[file->f_pos + i] = block->data[i]; } break; } - else if(file->f_pos + MAX_DATA_LEN <= len){ + else if(file->f_pos + block->size <= len){ /* len >= MAX_DATA_LEN, read MAX_DATA_LEN is ok */ - for(int i = 0; i < MAX_DATA_LEN && block->data[i] != EOF; i++){ + for(int i = 0; i < block->size && block->data[i] != EOF; i++){ dest[file->f_pos + i] = block->data[i]; } - file->f_pos += MAX_DATA_LEN; + file->f_pos += block->size; } } @@ -147,6 +150,7 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp TmpfsInode *inode = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); inode->idx = inode_head->idx; + inode->size = 0; /* create the dict info */ Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); From 9d628ac3765ede9dad7870cba55175f6435e302b Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 24 May 2022 21:37:34 +0800 Subject: [PATCH 31/62] tmpfs_write: done (no test) --- kernel/src/tmpfs.c | 87 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 815e1c798..322225e67 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -52,35 +52,91 @@ void tmpfs_set_ops(){ int tmpfs_write(struct file* file, const void* buf, size_t len){ TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; + char *src = (char *)buf; + size_t write_len = len; + size_t write_idx = 0; + struct list_head *pos; + list_for_each(pos, &inode_head->list){ + TmpfsInode *block = (TmpfsInode *)pos; + if(file->f_pos >= block->idx * MAX_DATA_LEN){ + continue; + } + /* f_pos is in the block of some offset */ + size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); + + while(1){ + if(write_len <= block->size - offset){ + memcpy(block->data + offset, src + write_idx, write_len); + file->f_pos += write_len; + write_idx += write_len; + block->data[write_idx] = EOF; + + block->size += write_len; + goto DONE; + } + else if(write_len > block->size - offset){ + memcpy(block->data + offset, src + write_idx, block->size - offset); + file->f_pos += block->size - offset; + write_idx += block->size - offset; + write_len -= block->size - offset; + block->size = MAX_DATA_LEN; + + /* add a new block */ + TmpfsInode *new_block = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); + new_block->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); + new_block->idx = block->idx + 1; + new_block->size = 0; + new_block->vnode = inode_head->vnode; + list_add_tail(&inode_head->list, &new_block->list); + + /* next round will write the data in the new block */ + block = new_block; + } + } + } +DONE: return 0; } int tmpfs_read(struct file* file, void* buf, size_t len){ TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; char *dest = (char *)buf; + size_t read_len = len; + size_t read_idx = 0; /* iterate through all data inodes */ struct list_head *pos; list_for_each(pos, &inode_head->list){ TmpfsInode *block = (TmpfsInode *)pos; - if(file->f_pos + block->size > len){ - /* len < MAX_DATA_LEN, read len is ok */ - for(int i = 0; i < len && block->data[i] != EOF; i++){ - dest[file->f_pos + i] = block->data[i]; + /* first need to iterate to the right block beacuse of the f_pos */ + if(file->f_pos >= block->idx * MAX_DATA_LEN){ + continue; + } + /* f_pos is in the block of the offset */ + size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); + + if(read_len <= block->size - offset){ + /* len < block size, read len is ok */ + for(size_t i = 0; i < read_len && block->data[offset + i] != EOF; i++){ + dest[read_idx + i] = block->data[offset + i]; } - break; + file->f_pos += read_len; + goto DONE; } - else if(file->f_pos + block->size <= len){ - /* len >= MAX_DATA_LEN, read MAX_DATA_LEN is ok */ - for(int i = 0; i < block->size && block->data[i] != EOF; i++){ - dest[file->f_pos + i] = block->data[i]; + else if(read_len > block->size - offset){ + /* len >= block size, read MAX_DATA_LEN is ok */ + for(size_t i = 0; (i < (block->size - offset)) && (block->data[offset + i] != EOF); i++){ + dest[read_idx + i] = block->data[offset + i]; } - file->f_pos += block->size; + file->f_pos += block->size - offset; + read_idx += block->size - offset; + read_len -= block->size - offset; } } +DONE: return 0; } int tmpfs_open(struct vnode* file_node, struct file** target){ @@ -143,17 +199,20 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp } int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ + /* create the dict info */ + Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); /* create the inode list head */ TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); - inode_head->idx = 1; + inode_head->idx = 0; + inode_head->size = 0; + inode_head->vnode = new_dentry->vnode; /* create the real data block */ TmpfsInode *inode = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); - inode->idx = inode_head->idx; + inode->idx = 1; inode->size = 0; + inode->vnode = new_dentry->vnode; - /* create the dict info */ - Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); new_dentry->vnode->internal = inode_head; /* add the new data block to inode_head */ From 28937a9776ba5fe84884537780fcde23e25894d5 Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 24 May 2022 23:20:19 +0800 Subject: [PATCH 32/62] vfs_open: simple test ok --- kernel/include/test_fs.h | 7 +++++++ kernel/include/tmpfs.h | 2 +- kernel/include/vfs.h | 2 +- kernel/src/main.c | 6 ++++-- kernel/src/test_fs.c | 24 ++++++++++++++++++++++++ kernel/src/tmpfs.c | 16 ++++++++++++---- kernel/src/vfs.c | 31 +++++++++++++++++-------------- 7 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 kernel/include/test_fs.h create mode 100644 kernel/src/test_fs.c diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h new file mode 100644 index 000000000..87c07a522 --- /dev/null +++ b/kernel/include/test_fs.h @@ -0,0 +1,7 @@ +#ifndef TEST_FS_H_ +#define TEST_FS_H_ + +void fs_test1(); + + +#endif \ No newline at end of file diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 4d55a371d..050b0bc61 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -12,7 +12,7 @@ typedef struct tmpfs_inode{ */ unsigned long idx; unsigned long size; - char *data; // data block size = MAX_DATA_LEN + char *data; // data block size = MAX_DATA_LEN(512) struct vnode* vnode; }TmpfsInode; diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 2437010d6..a01d194d1 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -71,7 +71,7 @@ struct vnode_operations { void rootfs_init(); int register_filesystem(FileSystem* fs); int vfs_open(const char* pathname, int flags, struct file** target); -int vfs_lookup(const char* pathname, Dentry *target_dentry, VNode *target_vnode, char *component_name); +int vfs_lookup(const char* pathname, Dentry **target_dentry, VNode **target_vnode, char *component_name); // int vfs_lookup(const char *pathname, Dentry *target_dentry, VNode *target_vnode); void find_component_name(const char *pathname, char *target_name, char delimiter); diff --git a/kernel/src/main.c b/kernel/src/main.c index da8997b99..10adf80d9 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -10,6 +10,7 @@ #include #include #include +#include extern Thread *run_thread_head; @@ -30,9 +31,10 @@ int main(unsigned long dtb_base){ enable_timer_irq(); enable_irq(); // DAIF set to 0b0000 - // kernel_exec("syscall.img"); - // kernel_main(); rootfs_init("tmpfs"); + + fs_test1(); + PrintWelcome(); ShellLoop(); diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c new file mode 100644 index 000000000..48f28470c --- /dev/null +++ b/kernel/src/test_fs.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +void fs_test1(){ + File *file_a = NULL; + int err = vfs_open("/hello", 0, &file_a); + if(err){ + uart_puts("[x] Failed to open file_a 1\n"); + } + err = vfs_open("/hello", O_CREAT, &file_a); + if(err){ + uart_puts("[x] Failed to open file_a 2\n"); + } + vfs_close(file_a); + + File *file_b = NULL; + err = vfs_open("/hello", 0, &file_b); + if(err){ + uart_puts("[x] Failed to open file_b 1\n"); + } + vfs_close(file_b); +} \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 322225e67..3ebf1c25c 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include struct file_operations* tmpfs_file_ops; struct vnode_operations* tmpfs_vnode_ops; @@ -14,6 +16,8 @@ int tmpfs_setup_mount(FileSystem *fs, Mount *mount){ Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type){ Dentry *new_dentry = (Dentry *)kmalloc(sizeof(Dentry)); new_dentry->name = (char *)kmalloc(sizeof(char) * strlen(name)); + INIT_LIST_HEAD(&new_dentry->list); + INIT_LIST_HEAD(&new_dentry->childs); strcpy(new_dentry->name, name); new_dentry->parent = parent; if(new_dentry->parent != NULL){ @@ -91,7 +95,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ new_block->vnode = inode_head->vnode; list_add_tail(&inode_head->list, &new_block->list); - /* next round will write the data in the new block */ + /* next round will write the data into the new block */ block = new_block; } } @@ -152,6 +156,10 @@ int tmpfs_open(struct vnode* file_node, struct file** target){ return 0; } int tmpfs_close(struct file* file){ + uart_puts("[*] Closed file: "); + uart_puts(file->vnode->dentry->name); + uart_puts("\n"); + kfree(file); file = NULL; return 0; @@ -167,12 +175,12 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp // /* if the target name is empty, it is end of the directory */ // return 0; // } - if(strcmp(component_name, ".")){ + if(strcmp(component_name, ".") == 0){ /* if the target name is ".", it is the same directory */ *target = dir_node; return 0; } - else if(strcmp(component_name, "..")){ + else if(strcmp(component_name, "..") == 0){ /* if dentry is root path, return it */ if(dir_node->dentry->parent == NULL) return 0; /* if dentry is not root path, find its parent */ @@ -187,7 +195,7 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp if(strcmp(tmp->name, component_name) == 0){ // TODO: need to check the dir is other filesystem - /* if the target is a file, return it */ + /* if the target is a file/dir, return it */ *target = tmp->vnode; return 0; } diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index ffad8ed8e..574d2a7c4 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -36,41 +36,44 @@ int register_filesystem(FileSystem *fs) { } int vfs_open(const char* pathname, int flags, struct file** target_file) { - if(pathname == NULL || *target_file == NULL) return -1; + if(pathname == NULL) return -1; Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; - int err = vfs_lookup(pathname, target_path, target_vnode, component_name); - if(err == -1) return err; // worng pathname + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return err; // worng pathname // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ + if(target_vnode->dentry->type == D_DIR) return -1; err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); + if(err == -1) return err; return 0; } - // 3. Create a new file if O_CREAT is specified in flags and vnode not found - // lookup error code shows if file exist or not or other error occurs + // // 3. Create a new file if O_CREAT is specified in flags and vnode not found + // // lookup error code shows if file exist or not or other error occurs else{ if(flags & O_CREAT){ - // create a new file err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); if(err) return err; err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); if(err) return err; + uart_puts("[*] Created file: "); + uart_puts((*target_file)->vnode->dentry->name); + uart_puts("\n"); return 0; } } - // 4. Return error code if fails return -1; } -int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode, char *component_name) { +int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, char *component_name) { int ready_return = 0; // 1. Lookup pathname if(pathname[0] == '/'){ int idx = 1; char tmp_buf[MAX_PATHNAME_LEN]; - target_path = rootfs->root_dentry; + *target_path = rootfs->root_dentry; find_component_name(pathname + idx, component_name, '/'); while(component_name[0] != '\0'){ /* @@ -80,20 +83,20 @@ int vfs_lookup(const char* pathname, Dentry *target_path, VNode *target_vnode, c */ if(ready_return) return -1; /* find the next vnode */ - if(target_vnode != NULL) target_path = target_vnode->dentry; - int err = rootfs->root_dentry->vnode->v_ops->lookup(target_path->vnode, &target_vnode, component_name); + if(*target_vnode != NULL) *target_path = (*target_vnode)->dentry; + int err = rootfs->root_dentry->vnode->v_ops->lookup((*target_path)->vnode, target_vnode, component_name); if(err){ ready_return = 1; - /* save the component name in tmp_buf beacuse it will check the last name */ - strcpy(tmp_buf, component_name); } + /* save the component name in tmp_buf beacuse it will check the last name */ + strcpy(tmp_buf, component_name); /* find next component name*/ idx += strlen(component_name) + 1; find_component_name(pathname + idx, component_name, '/'); } /* if the last component name is '\0', it is a file name */ - if(ready_return) strcpy(component_name, tmp_buf); + strcpy(component_name, tmp_buf); } // TODO: check the relative path From 2f19f591c592ae0488ffaa649a29e86cc0290480 Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 25 May 2022 02:02:11 +0800 Subject: [PATCH 33/62] vps_read, vps_write: simple test ok --- kernel/include/test_fs.h | 2 +- kernel/include/vfs.h | 2 +- kernel/src/main.c | 1 + kernel/src/test_fs.c | 50 ++++++++++++++++++++++++++++++++-------- kernel/src/tmpfs.c | 43 ++++++++++++++++++++-------------- 5 files changed, 70 insertions(+), 28 deletions(-) diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h index 87c07a522..e3d437d59 100644 --- a/kernel/include/test_fs.h +++ b/kernel/include/test_fs.h @@ -2,6 +2,6 @@ #define TEST_FS_H_ void fs_test1(); - +void fs_test2(); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index a01d194d1..1ec9fe04a 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -11,7 +11,7 @@ enum dentry_type { #define O_CREAT 1 #define MAX_PATHNAME_LEN 256 #define MAX_DATA_LEN 512 -#define EOF -1 +#define EOF (-1) // file handle typedef struct file { diff --git a/kernel/src/main.c b/kernel/src/main.c index 10adf80d9..9cbe65211 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -34,6 +34,7 @@ int main(unsigned long dtb_base){ rootfs_init("tmpfs"); fs_test1(); + fs_test2(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index 48f28470c..cddeb2ed8 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -2,23 +2,55 @@ #include #include #include +#include void fs_test1(){ + uart_puts("--------------------TEST VFS_OPEN--------------------\n"); File *file_a = NULL; int err = vfs_open("/hello", 0, &file_a); - if(err){ - uart_puts("[x] Failed to open file_a 1\n"); - } + if(err) uart_puts("[x] Failed to open hello\n"); + err = vfs_open("/hello", O_CREAT, &file_a); - if(err){ - uart_puts("[x] Failed to open file_a 2\n"); - } + if(err) uart_puts("[x] Failed to open file_a 2\n"); vfs_close(file_a); File *file_b = NULL; err = vfs_open("/hello", 0, &file_b); - if(err){ - uart_puts("[x] Failed to open file_b 1\n"); - } + if(err) uart_puts("[x] Failed to open file_b 1\n"); vfs_close(file_b); +} + +void fs_test2(){ + uart_puts("--------------------TEST VFS_READ & VFS_WRITE--------------------\n"); + File *a = NULL; + int err = vfs_open("/hello", O_CREAT, &a); + if(err) uart_puts("[x] Failed to open \"hello\"\n"); + // uart_puts(a->vnode->dentry->parent->name); + + File *b = NULL; + err = vfs_open("/world", O_CREAT, &b); + if(err) uart_puts("[x] Failed to open \"world\"\n"); + + int len = vfs_write(a, "Hello ", 6); + len = vfs_write(a, "Hello ", 6); + if(len == -1) uart_puts("[x] Failed to write \"Hello\"\n"); + len = vfs_write(b, "World\n", 6); + + vfs_close(a); + vfs_close(b); + + err = vfs_open("/hello", O_CREAT, &b); + if(err) uart_puts("[x] Failed to open \"hello\"\n"); + err = vfs_open("/world", O_CREAT, &a); + if(err) uart_puts("[x] Failed to open \"world\"\n"); + + char buf[300]; + int sz; + sz = vfs_read(b, buf, 100); + sz += vfs_read(a, buf + sz, 100); + len = vfs_read(b, buf, 100); + if(len == -1) uart_puts("[x] Failed to read \"Hello\"\n"); + buf[sz] = '\0'; + uart_puts(buf); + // printf("%s\n", buf); // should be Hello World! } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 3ebf1c25c..e27268f86 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -22,7 +22,8 @@ Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type t new_dentry->parent = parent; if(new_dentry->parent != NULL){ /* add to parent's child list */ - list_add_tail(&parent->childs, &new_dentry->list); + list_add_tail(&new_dentry->list, &parent->childs); + // uart_puts("[*] Added to parent's child list\n"); } new_dentry->type = type; new_dentry->mount = NULL; @@ -60,6 +61,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ size_t write_len = len; size_t write_idx = 0; + struct list_head *pos; list_for_each(pos, &inode_head->list){ TmpfsInode *block = (TmpfsInode *)pos; @@ -67,11 +69,12 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ continue; } - /* f_pos is in the block of some offset */ - size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); - while(1){ - if(write_len <= block->size - offset){ + /* f_pos is in the block of some offset */ + size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); + if(block->data[offset] == (char)EOF) return -1; + + if(write_len <= MAX_DATA_LEN - offset){ memcpy(block->data + offset, src + write_idx, write_len); file->f_pos += write_len; write_idx += write_len; @@ -80,11 +83,11 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ block->size += write_len; goto DONE; } - else if(write_len > block->size - offset){ - memcpy(block->data + offset, src + write_idx, block->size - offset); - file->f_pos += block->size - offset; - write_idx += block->size - offset; - write_len -= block->size - offset; + else if(write_len > MAX_DATA_LEN - offset){ + memcpy(block->data + offset, src + write_idx, MAX_DATA_LEN - offset); + file->f_pos += MAX_DATA_LEN - offset; + write_idx += MAX_DATA_LEN - offset; + write_len -= MAX_DATA_LEN - offset; block->size = MAX_DATA_LEN; /* add a new block */ @@ -93,16 +96,17 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ new_block->idx = block->idx + 1; new_block->size = 0; new_block->vnode = inode_head->vnode; - list_add_tail(&inode_head->list, &new_block->list); + list_add_tail(&new_block->list, &inode_head->list); + uart_puts("[*] Tmpfs Write: Create new block\n"); /* next round will write the data into the new block */ - block = new_block; + block = new_block; } } } DONE: - return 0; + return write_idx; } int tmpfs_read(struct file* file, void* buf, size_t len){ TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; @@ -120,10 +124,11 @@ int tmpfs_read(struct file* file, void* buf, size_t len){ } /* f_pos is in the block of the offset */ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); + if(block->data[offset] == (char)EOF) return -1; if(read_len <= block->size - offset){ /* len < block size, read len is ok */ - for(size_t i = 0; i < read_len && block->data[offset + i] != EOF; i++){ + for(size_t i = 0; i < read_len && block->data[offset + i] != (char)EOF; i++){ dest[read_idx + i] = block->data[offset + i]; } file->f_pos += read_len; @@ -131,7 +136,7 @@ int tmpfs_read(struct file* file, void* buf, size_t len){ } else if(read_len > block->size - offset){ /* len >= block size, read MAX_DATA_LEN is ok */ - for(size_t i = 0; (i < (block->size - offset)) && (block->data[offset + i] != EOF); i++){ + for(size_t i = 0; (i < (block->size - offset)) && (block->data[offset + i] != (char)EOF); i++){ dest[read_idx + i] = block->data[offset + i]; } file->f_pos += block->size - offset; @@ -141,7 +146,7 @@ int tmpfs_read(struct file* file, void* buf, size_t len){ } DONE: - return 0; + return read_idx; } int tmpfs_open(struct vnode* file_node, struct file** target){ if(file_node == NULL){ @@ -208,6 +213,8 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ /* create the dict info */ + // uart_puts(component_name); + // uart_puts(((Dentry *)(dir_node->dentry->childs.next))->name); Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); /* create the inode list head */ TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); @@ -224,10 +231,12 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp new_dentry->vnode->internal = inode_head; /* add the new data block to inode_head */ - list_add_tail(&inode_head->list, &inode->list); + list_add_tail(&inode->list, &inode_head->list); *target = new_dentry->vnode; + // print_string(UITOA, "idx = ", ((TmpfsInode *)(((TmpfsInode *)((*target)->internal))->list.next))->idx, 1); + return 0; } From 6904cdf31b1fc44429f7d493aacc86ad8bdeab73 Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 25 May 2022 18:10:01 +0800 Subject: [PATCH 34/62] vfs_mkdir & vfs_ls: simple test ok --- kernel/include/test_fs.h | 1 + kernel/include/vfs.h | 1 + kernel/src/main.c | 1 + kernel/src/test_fs.c | 44 ++++++--- kernel/src/tmpfs.c | 7 +- kernel/src/vfs.c | 198 ++++++++++++++++++++++++++------------- 6 files changed, 170 insertions(+), 82 deletions(-) diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h index e3d437d59..03f7e50b9 100644 --- a/kernel/include/test_fs.h +++ b/kernel/include/test_fs.h @@ -3,5 +3,6 @@ void fs_test1(); void fs_test2(); +void fs_test3(); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 1ec9fe04a..3f7decd89 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -79,6 +79,7 @@ int vfs_close(struct file* file); int vfs_write(struct file* file, const void* buf, size_t len); int vfs_read(struct file* file, void* buf, size_t len); int vfs_mkdir(const char* pathname); +int vfs_ls(const char* pathname); int vfs_mount(const char* target, const char* filesystem); #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 9cbe65211..47d9ae96b 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -35,6 +35,7 @@ int main(unsigned long dtb_base){ fs_test1(); fs_test2(); + fs_test3(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index cddeb2ed8..63de8186b 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -5,36 +5,36 @@ #include void fs_test1(){ - uart_puts("--------------------TEST VFS_OPEN--------------------\n"); + uart_puts("--------------------------TEST VFS_OPEN--------------------------\n"); File *file_a = NULL; - int err = vfs_open("/hello", 0, &file_a); + int err = vfs_open("hello", 0, &file_a); if(err) uart_puts("[x] Failed to open hello\n"); - err = vfs_open("/hello", O_CREAT, &file_a); + err = vfs_open("hello", O_CREAT, &file_a); if(err) uart_puts("[x] Failed to open file_a 2\n"); vfs_close(file_a); File *file_b = NULL; - err = vfs_open("/hello", 0, &file_b); + err = vfs_open("hello", 0, &file_b); if(err) uart_puts("[x] Failed to open file_b 1\n"); vfs_close(file_b); } void fs_test2(){ - uart_puts("--------------------TEST VFS_READ & VFS_WRITE--------------------\n"); + uart_puts("--------------------TEST VFS_WRITE & VFS_READ--------------------\n"); File *a = NULL; - int err = vfs_open("/hello", O_CREAT, &a); + int err = vfs_open("hello", O_CREAT, &a); if(err) uart_puts("[x] Failed to open \"hello\"\n"); // uart_puts(a->vnode->dentry->parent->name); File *b = NULL; - err = vfs_open("/world", O_CREAT, &b); + err = vfs_open("world", O_CREAT, &b); if(err) uart_puts("[x] Failed to open \"world\"\n"); - int len = vfs_write(a, "Hello ", 6); - len = vfs_write(a, "Hello ", 6); - if(len == -1) uart_puts("[x] Failed to write \"Hello\"\n"); - len = vfs_write(b, "World\n", 6); + vfs_write(a, "Hello ", 6); + vfs_write(b, "World\n", 6); + // len = vfs_write(a, "Hello ", 6); + // if(len == -1) uart_puts("[x] Failed to write \"Hello\"\n"); vfs_close(a); vfs_close(b); @@ -48,9 +48,27 @@ void fs_test2(){ int sz; sz = vfs_read(b, buf, 100); sz += vfs_read(a, buf + sz, 100); - len = vfs_read(b, buf, 100); - if(len == -1) uart_puts("[x] Failed to read \"Hello\"\n"); buf[sz] = '\0'; uart_puts(buf); // printf("%s\n", buf); // should be Hello World! +} + +void fs_test3(){ + uart_puts("---------------------TEST VFS_MKDIR & VFS_LS---------------------\n"); + vfs_mkdir("/path1"); + int err = vfs_mkdir("/path1/path2/"); + if(err){ + switch (err) + { + case -2: + uart_puts("[x] \"path1\" is exist\n"); + break; + default: + uart_puts("[x] Failed to mkdir \"path1\"\n"); + break; + } + } + vfs_ls(NULL); + vfs_ls("path1/"); + vfs_ls("path1/path2"); } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index e27268f86..879d1a3ae 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -212,9 +212,9 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp } int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name){ - /* create the dict info */ // uart_puts(component_name); // uart_puts(((Dentry *)(dir_node->dentry->childs.next))->name); + /* create the dict info */ Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); /* create the inode list head */ TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); @@ -241,6 +241,9 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp } int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name){ - + /* create the dict info */ + Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_DIR); + *target = new_dentry->vnode; + return 0; } \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 574d2a7c4..952965707 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -4,13 +4,15 @@ #include #include +char *global_dir; +Dentry *global_dentry; Mount *rootfs; void rootfs_init(char *fs_name){ FileSystem *fs = NULL; if(strcmp(fs_name, "tmpfs") == 0){ FileSystem *tmpfs = (FileSystem *)kmalloc(sizeof(FileSystem)); - tmpfs->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" + tmpfs->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" strcpy(tmpfs->name, "tmpfs"); tmpfs->setup_mount = tmpfs_setup_mount; fs = tmpfs; @@ -18,6 +20,11 @@ void rootfs_init(char *fs_name){ int err = register_filesystem(fs); if(err) uart_puts("[x] Failed to register filesystem\n"); + + global_dir = (char *)kmalloc(sizeof(char) * 2046); + global_dir[0] = '/'; + global_dentry = rootfs->root_dentry; + } @@ -35,16 +42,68 @@ int register_filesystem(FileSystem *fs) { return 0; } +void find_component_name(const char *pathname, char *component_name, char delimiter){ + int i = 0; + while(pathname[i] != delimiter && pathname[i] != '\0'){ + component_name[i] = pathname[i]; + i++; + } + component_name[i] = '\0'; +} + +int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, char *component_name) { + int ready_return = 0; + int idx; + // 1. Lookup pathname + if(pathname[0] == '/'){ + idx = 1; + *target_path = rootfs->root_dentry; + } + // TODO: check the relative path + // TODO: the path need to save in global variable and thread info + else{ + idx = 0; + *target_path = global_dentry; + } + + char tmp_buf[MAX_PATHNAME_LEN]; + find_component_name(pathname + idx, component_name, '/'); + while(component_name[0] != '\0'){ + /* + * ready_return is 1 beacuse it couldn't find child vnode before + * The next component name is not '\0' now, it's not the last component name + * Therefore, it is a wrong pathname, even if the flag is O_CREAT, it also cannot create a new file + */ + if(ready_return) return -1; + /* find the next vnode */ + if(*target_vnode != NULL) *target_path = (*target_vnode)->dentry; + int err = rootfs->root_dentry->vnode->v_ops->lookup((*target_path)->vnode, target_vnode, component_name); + if(err){ + ready_return = 1; + } + /* save the component name in tmp_buf beacuse it will check the last name */ + strcpy(tmp_buf, component_name); + + /* find next component name*/ + idx += strlen(component_name) + 1; + find_component_name(pathname + idx, component_name, '/'); + } + /* if the last component name is '\0', it is a file name */ + strcpy(component_name, tmp_buf); + + return 0; +} + int vfs_open(const char* pathname, int flags, struct file** target_file) { if(pathname == NULL) return -1; Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); - if(err) return err; // worng pathname + if(err) return -1; // worng pathname // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ - if(target_vnode->dentry->type == D_DIR) return -1; + if(target_vnode->dentry->type == D_DIR) return -2; // cannot open a directory err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); if(err == -1) return err; return 0; @@ -63,57 +122,87 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { return 0; } } + // 4. Return error code if fails return -1; } -int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, char *component_name) { - int ready_return = 0; - // 1. Lookup pathname - if(pathname[0] == '/'){ - int idx = 1; - char tmp_buf[MAX_PATHNAME_LEN]; - *target_path = rootfs->root_dentry; - find_component_name(pathname + idx, component_name, '/'); - while(component_name[0] != '\0'){ - /* - * ready_return is 1 beacuse it couldn't find child vnode before - * The next component name is not '\0' now, it's not the last component name - * Therefore, it is a wrong pathname, even if the flag is O_CREAT, it also cannot create a new file - */ - if(ready_return) return -1; - /* find the next vnode */ - if(*target_vnode != NULL) *target_path = (*target_vnode)->dentry; - int err = rootfs->root_dentry->vnode->v_ops->lookup((*target_path)->vnode, target_vnode, component_name); - if(err){ - ready_return = 1; - } - /* save the component name in tmp_buf beacuse it will check the last name */ - strcpy(tmp_buf, component_name); - - /* find next component name*/ - idx += strlen(component_name) + 1; - find_component_name(pathname + idx, component_name, '/'); - } - /* if the last component name is '\0', it is a file name */ - strcpy(component_name, tmp_buf); - } - // TODO: check the relative path - // TODO: the path need to save in global variable and thread info +int vfs_mkdir(const char *pathname){ + if(pathname == NULL) return -1; + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + char component_name[MAX_PATHNAME_LEN]; + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + + if(target_vnode != NULL) return -2; // folder already exist + + err = rootfs->root_dentry->vnode->v_ops->mkdir(target_path->vnode, &target_vnode, component_name); + if(err) return -1; + return 0; } -void find_component_name(const char *pathname, char *component_name, char delimiter){ - int i = 0; - while(pathname[i] != delimiter && pathname[i] != '\0'){ - component_name[i] = pathname[i]; - i++; +int vfs_ls(const char *pathname){ + /* now path */ + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + char component_name[MAX_PATHNAME_LEN]; + + if(pathname == NULL){ + target_path = global_dentry; + target_vnode = global_dentry->vnode; } - component_name[i] = '\0'; + else{ + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + } + + /* file/folder not exist */ + if(target_vnode == NULL) return -2; + + /* print the file/folder name */ + struct list_head *pos; + list_for_each(pos, &target_vnode->dentry->childs){ + Dentry *child = (Dentry *)pos; + uart_puts(child->name); + uart_puts("["); + if(child->type == D_DIR) uart_puts("DIR"); + else uart_puts("FILE"); + uart_puts("]"); + uart_puts("\n"); + } + + return 0; +} + + +int vfs_close(struct file* file) { + // 1. release the file handle + // 2. Return error code if fails + if(file == NULL) return -1; + return file->f_ops->close(file); + +} + +int vfs_write(struct file* file, const void* buf, size_t len) { + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + if(file == NULL) return -1; + return file->f_ops->write(file, buf, len); +} + +int vfs_read(struct file* file, void* buf, size_t len) { + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 2. return read size or error code if an error occurs. + if(file == NULL) return -1; + return file->f_ops->read(file, buf, len); } + // int traversal_path(const char *pathname, Dentry *target_path, char *target_name){ // find_target_name(pathname, target_name, '/'); // if(*target_name == '\0'){ @@ -151,28 +240,3 @@ void find_component_name(const char *pathname, char *component_name, char delimi // return -1; // } - - -int vfs_close(struct file* file) { - // 1. release the file handle - // 2. Return error code if fails - if(file == NULL) return -1; - return file->f_ops->close(file); - -} - -int vfs_write(struct file* file, const void* buf, size_t len) { - // 1. write len byte from buf to the opened file. - // 2. return written size or error code if an error occurs. - if(file == NULL) return -1; - return file->f_ops->write(file, buf, len); -} - -int vfs_read(struct file* file, void* buf, size_t len) { - // 1. read min(len, readable size) byte to buf from the opened file. - // 2. block if nothing to read for FIFO type - // 2. return read size or error code if an error occurs. - if(file == NULL) return -1; - return file->f_ops->read(file, buf, len); -} - From b3cd1bf8705be960ee59788ca286454a6d849c7a Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 25 May 2022 18:31:37 +0800 Subject: [PATCH 35/62] vfs_ls: use tmpfs_ls --- kernel/include/tmpfs.h | 1 + kernel/include/vfs.h | 1 + kernel/src/test_fs.c | 5 ++++- kernel/src/tmpfs.c | 18 ++++++++++++++++++ kernel/src/vfs.c | 21 +++++---------------- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 050b0bc61..7d23ab063 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -30,6 +30,7 @@ long tmpfs_lseek64(struct file* file, long offset, int whence); int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name); int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name); +int tmpfs_ls(struct vnode* target); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 3f7decd89..727242921 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -63,6 +63,7 @@ struct vnode_operations { const char* component_name); int (*mkdir)(struct vnode* dir_node, struct vnode** target, const char* component_name); + int (*ls)(struct vnode* target); }; diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index 63de8186b..2fdc663f8 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -69,6 +69,9 @@ void fs_test3(){ } } vfs_ls(NULL); - vfs_ls("path1/"); + vfs_ls("./path1/"); vfs_ls("path1/path2"); + vfs_ls("path1/path2/../../"); + err = vfs_ls("path1/abc"); + if(err) uart_puts("[x] Failed to ls \"path1/abc\"\n"); } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 879d1a3ae..c958b83fe 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -53,6 +53,7 @@ void tmpfs_set_ops(){ tmpfs_vnode_ops->lookup = tmpfs_lookup; tmpfs_vnode_ops->create = tmpfs_create; tmpfs_vnode_ops->mkdir = tmpfs_mkdir; + tmpfs_vnode_ops->ls = tmpfs_ls; } int tmpfs_write(struct file* file, const void* buf, size_t len){ @@ -245,5 +246,22 @@ int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* compo Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_DIR); *target = new_dentry->vnode; + return 0; +} + +int tmpfs_ls(struct vnode* target){ + /* print the file/folder name */ + struct list_head *pos; + list_for_each(pos, &target->dentry->childs){ + Dentry *child = (Dentry *)pos; + uart_puts(child->name); + uart_puts("["); + if(child->type == D_DIR) uart_puts("DIR"); + else uart_puts("FILE"); + uart_puts("]"); + uart_puts("\t"); + } + uart_puts("\n"); + return 0; } \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 952965707..09fd52c6d 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -59,8 +59,6 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, idx = 1; *target_path = rootfs->root_dentry; } - // TODO: check the relative path - // TODO: the path need to save in global variable and thread info else{ idx = 0; *target_path = global_dentry; @@ -70,10 +68,10 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, find_component_name(pathname + idx, component_name, '/'); while(component_name[0] != '\0'){ /* - * ready_return is 1 beacuse it couldn't find child vnode before - * The next component name is not '\0' now, it's not the last component name - * Therefore, it is a wrong pathname, even if the flag is O_CREAT, it also cannot create a new file - */ + * ready_return is 1 beacuse it couldn't find child vnode before + * The next component name is not '\0' now, it's not the last component name + * Therefore, it is a wrong pathname, even if the flag is O_CREAT, it also cannot create a new file + */ if(ready_return) return -1; /* find the next vnode */ if(*target_vnode != NULL) *target_path = (*target_vnode)->dentry; @@ -163,16 +161,7 @@ int vfs_ls(const char *pathname){ if(target_vnode == NULL) return -2; /* print the file/folder name */ - struct list_head *pos; - list_for_each(pos, &target_vnode->dentry->childs){ - Dentry *child = (Dentry *)pos; - uart_puts(child->name); - uart_puts("["); - if(child->type == D_DIR) uart_puts("DIR"); - else uart_puts("FILE"); - uart_puts("]"); - uart_puts("\n"); - } + rootfs->root_dentry->vnode->v_ops->ls(target_vnode); return 0; } From a60c923c71264fa5676e3bcc5e775d3424ad369f Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 26 May 2022 02:46:15 +0800 Subject: [PATCH 36/62] vfs_mount: done (no test) --- kernel/include/tmpfs.h | 6 +- kernel/include/vfs.h | 13 +++-- kernel/src/tmpfs.c | 31 +++++++---- kernel/src/vfs.c | 123 +++++++++++++++++++++++++++++++++++------ 4 files changed, 139 insertions(+), 34 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 7d23ab063..50676b4fe 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -17,7 +17,7 @@ typedef struct tmpfs_inode{ }TmpfsInode; int tmpfs_setup_mount(FileSystem *fs, Mount *mount); -Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type); +Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type, Mount* mount); VNode *tmpfs_create_vnode(Dentry *dentry); void tmpfs_set_ops(); @@ -30,7 +30,7 @@ long tmpfs_lseek64(struct file* file, long offset, int whence); int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name); int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name); -int tmpfs_ls(struct vnode* target); - +int tmpfs_ls(struct dentry* target); +int tmpfs_chdir(struct dentry* target); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 727242921..40a9770bd 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -6,11 +6,13 @@ enum dentry_type { D_DIR, - D_FILE + D_FILE, + D_MOUNT }; #define O_CREAT 1 #define MAX_PATHNAME_LEN 256 #define MAX_DATA_LEN 512 +#define MAX_FS_NUM 32 #define EOF (-1) // file handle @@ -32,14 +34,15 @@ typedef struct dentry { struct list_head list; struct list_head childs; struct dentry *parent; - struct mount* mount; char* name; enum dentry_type type; struct vnode* vnode; + struct mount* mount; }Dentry; typedef struct mount { struct filesystem* fs; + struct mount* mount_parent; struct dentry* root_dentry; }Mount; @@ -63,7 +66,8 @@ struct vnode_operations { const char* component_name); int (*mkdir)(struct vnode* dir_node, struct vnode** target, const char* component_name); - int (*ls)(struct vnode* target); + int (*ls)(struct dentry* target); + int (*chdir)(struct dentry* target); }; @@ -81,6 +85,7 @@ int vfs_write(struct file* file, const void* buf, size_t len); int vfs_read(struct file* file, void* buf, size_t len); int vfs_mkdir(const char* pathname); int vfs_ls(const char* pathname); -int vfs_mount(const char* target, const char* filesystem); +int vfs_chdir(const char* pathname); +int vfs_mount(const char* pathname, const char* filesystem); #endif \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index c958b83fe..e1b8b108e 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -7,13 +7,16 @@ struct file_operations* tmpfs_file_ops; struct vnode_operations* tmpfs_vnode_ops; +extern char *global_dir; +extern Dentry *global_dentry; + int tmpfs_setup_mount(FileSystem *fs, Mount *mount){ mount->fs = fs; - mount->root_dentry = tmpfs_create_dentry("/", NULL, D_DIR); + mount->root_dentry = tmpfs_create_dentry("/", NULL, D_DIR, mount); return 0; } -Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type){ +Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type, Mount *mount){ Dentry *new_dentry = (Dentry *)kmalloc(sizeof(Dentry)); new_dentry->name = (char *)kmalloc(sizeof(char) * strlen(name)); INIT_LIST_HEAD(&new_dentry->list); @@ -26,7 +29,7 @@ Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type t // uart_puts("[*] Added to parent's child list\n"); } new_dentry->type = type; - new_dentry->mount = NULL; + new_dentry->mount = mount; new_dentry->vnode = tmpfs_create_vnode(new_dentry); return new_dentry; } @@ -199,9 +202,7 @@ int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* comp list_for_each(pos, &dir_node->dentry->childs){ Dentry *tmp = (Dentry *)pos; if(strcmp(tmp->name, component_name) == 0){ - // TODO: need to check the dir is other filesystem - - /* if the target is a file/dir, return it */ + /* if the target is a file/dir/mountpoint, return it */ *target = tmp->vnode; return 0; } @@ -216,7 +217,7 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp // uart_puts(component_name); // uart_puts(((Dentry *)(dir_node->dentry->childs.next))->name); /* create the dict info */ - Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE); + Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE, dir_node->dentry->mount); /* create the inode list head */ TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); inode_head->idx = 0; @@ -243,16 +244,16 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name){ /* create the dict info */ - Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_DIR); + Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_DIR, dir_node->dentry->mount); *target = new_dentry->vnode; return 0; } -int tmpfs_ls(struct vnode* target){ +int tmpfs_ls(struct dentry* target){ /* print the file/folder name */ struct list_head *pos; - list_for_each(pos, &target->dentry->childs){ + list_for_each(pos, &target->childs){ Dentry *child = (Dentry *)pos; uart_puts(child->name); uart_puts("["); @@ -262,6 +263,14 @@ int tmpfs_ls(struct vnode* target){ uart_puts("\t"); } uart_puts("\n"); + + return 0; +} + +int tmpfs_chdir(struct dentry *target){ + /* change the current working directory */ + + return 0; -} \ No newline at end of file +} diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 09fd52c6d..7f9ba896f 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -7,24 +7,31 @@ char *global_dir; Dentry *global_dentry; Mount *rootfs; +FileSystem *fs_pool[MAX_FS_NUM]; void rootfs_init(char *fs_name){ - FileSystem *fs = NULL; + for(unsigned int idx = 0; idx < MAX_FS_NUM; idx++){ + FileSystem *init_fs = (FileSystem *)kmalloc(sizeof(FileSystem)); + init_fs->name = NULL; + init_fs->setup_mount = NULL; + fs_pool[idx] = init_fs; + } + if(strcmp(fs_name, "tmpfs") == 0){ - FileSystem *tmpfs = (FileSystem *)kmalloc(sizeof(FileSystem)); - tmpfs->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" - strcpy(tmpfs->name, "tmpfs"); - tmpfs->setup_mount = tmpfs_setup_mount; - fs = tmpfs; + fs_pool[0]->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" + strcpy(fs_pool[0]->name, "tmpfs"); + fs_pool[0]->setup_mount = tmpfs_setup_mount; } - int err = register_filesystem(fs); + int err = register_filesystem(fs_pool[0]); if(err) uart_puts("[x] Failed to register filesystem\n"); + + rootfs = (Mount *)kmalloc(sizeof(Mount)); + fs_pool[0]->setup_mount(fs_pool[0], rootfs); global_dir = (char *)kmalloc(sizeof(char) * 2046); - global_dir[0] = '/'; + strcpy(global_dir, "/"); global_dentry = rootfs->root_dentry; - } @@ -37,7 +44,10 @@ int register_filesystem(FileSystem *fs) { tmpfs_set_ops(); uart_puts("[*] Registered tmpfs\n"); } - fs->setup_mount(fs, rootfs); + else{ + tmpfs_set_ops(); + uart_puts("[*] Registered another fs\n"); + } return 0; } @@ -73,8 +83,8 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, * Therefore, it is a wrong pathname, even if the flag is O_CREAT, it also cannot create a new file */ if(ready_return) return -1; - /* find the next vnode */ - if(*target_vnode != NULL) *target_path = (*target_vnode)->dentry; + + /* Try to find the child vnode */ int err = rootfs->root_dentry->vnode->v_ops->lookup((*target_path)->vnode, target_vnode, component_name); if(err){ ready_return = 1; @@ -85,6 +95,19 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, /* find next component name*/ idx += strlen(component_name) + 1; find_component_name(pathname + idx, component_name, '/'); + + /* find the next vnode */ + if(*target_vnode != NULL){ + /* check the dir is mountpoint or not */ + if((*target_vnode)->dentry->type == D_MOUNT){ + *target_path = (*target_vnode)->dentry->mount->root_dentry; + } + else{ + /* the next vnode is child vnode*/ + *target_path = (*target_vnode)->dentry; + } + + } } /* if the last component name is '\0', it is a file name */ strcpy(component_name, tmp_buf); @@ -101,7 +124,8 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { if(err) return -1; // worng pathname // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ - if(target_vnode->dentry->type == D_DIR) return -2; // cannot open a directory + if(target_vnode->dentry->type == D_DIR || + target_vnode->dentry->type == D_MOUNT) return -2; // cannot open a directory err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); if(err == -1) return err; return 0; @@ -161,18 +185,85 @@ int vfs_ls(const char *pathname){ if(target_vnode == NULL) return -2; /* print the file/folder name */ - rootfs->root_dentry->vnode->v_ops->ls(target_vnode); + return rootfs->root_dentry->vnode->v_ops->ls(target_vnode->dentry); +} - return 0; +int vfs_chdir(const char *pathname){ + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + char component_name[MAX_PATHNAME_LEN]; + if(pathname == NULL){ + global_dentry = rootfs->root_dentry; + strcpy(global_dir, "/"); + target_path = global_dentry; + target_vnode = global_dentry->vnode; + return 0; + } + else{ + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + if(target_vnode->dentry->type == D_FILE) return -2; // cannot change to a file + } + + /* file/folder not exist */ + if(target_vnode == NULL) return -2; + + /* change the global_dentry and global_dir */ + return rootfs->root_dentry->vnode->v_ops->chdir(target_vnode->dentry); } +int vfs_mount(const char *pathname, const char *filesystem){ + if(filesystem == NULL) return -1; + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + char component_name[MAX_PATHNAME_LEN]; + int err; + if(pathname == NULL){ + target_path = global_dentry; + target_vnode = global_dentry->vnode; + } + else{ + err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + } + + /* target vnode isn't exist, cannot mount it */ + if(target_vnode == NULL) return -2; + + /* target vnode is exist, can mount it */ + FileSystem *target_fs = NULL; + unsigned int idx = 0; + /* find the filesystem */ + for(; idx < MAX_FS_NUM; idx++){ + if(fs_pool[idx] == NULL) break; + if(strcmp(filesystem, fs_pool[idx]->name) == 0){ + target_fs = fs_pool[idx]; + goto MOUNT_FS; + } + } + /* cannot find the filesystem , use the empty fs and register it*/ + fs_pool[idx]->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" + strcpy(fs_pool[idx]->name, filesystem); + fs_pool[idx]->setup_mount = tmpfs_setup_mount; + target_fs = fs_pool[idx]; + err = register_filesystem(target_fs); + if(err) uart_puts("[x] Failed to register another filesystem\n"); + +MOUNT_FS:; + /* mount the filesystem */ + Mount *new_mount = (Mount *)kmalloc(sizeof(Mount)); + target_fs->setup_mount(target_fs, new_mount); + new_mount->mount_parent = target_path->mount; + target_vnode->dentry->type = D_MOUNT; + + return 0; +} int vfs_close(struct file* file) { // 1. release the file handle // 2. Return error code if fails if(file == NULL) return -1; return file->f_ops->close(file); - } int vfs_write(struct file* file, const void* buf, size_t len) { From bf06e69ef9645a8fdb6d04533eb5701aa487140f Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 27 May 2022 00:36:33 +0800 Subject: [PATCH 37/62] vfs_chdir: ok (no test) --- kernel/include/test_fs.h | 1 + kernel/include/tmpfs.h | 4 +-- kernel/include/vfs.h | 9 +++-- kernel/src/main.c | 1 + kernel/src/test_fs.c | 19 +++++++--- kernel/src/tmpfs.c | 29 ++-------------- kernel/src/vfs.c | 75 +++++++++++++++++++++++++++++++++++----- 7 files changed, 93 insertions(+), 45 deletions(-) diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h index 03f7e50b9..026d37283 100644 --- a/kernel/include/test_fs.h +++ b/kernel/include/test_fs.h @@ -4,5 +4,6 @@ void fs_test1(); void fs_test2(); void fs_test3(); +void fs_test4(); #endif \ No newline at end of file diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 50676b4fe..0b3911331 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -16,7 +16,7 @@ typedef struct tmpfs_inode{ struct vnode* vnode; }TmpfsInode; -int tmpfs_setup_mount(FileSystem *fs, Mount *mount); +int tmpfs_setup_mount(FileSystem *fs, Mount *mount, Mount *mount_parent); Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type, Mount* mount); VNode *tmpfs_create_vnode(Dentry *dentry); void tmpfs_set_ops(); @@ -30,7 +30,5 @@ long tmpfs_lseek64(struct file* file, long offset, int whence); int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name); int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* component_name); -int tmpfs_ls(struct dentry* target); -int tmpfs_chdir(struct dentry* target); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 40a9770bd..2b265959f 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -38,6 +38,9 @@ typedef struct dentry { enum dentry_type type; struct vnode* vnode; struct mount* mount; + + /* for the chdir find the mount_point's name */ + struct dentry* mount_point_dentry; }Dentry; typedef struct mount { @@ -48,7 +51,7 @@ typedef struct mount { typedef struct filesystem { char* name; - int (*setup_mount)(struct filesystem* fs, struct mount* mount); + int (*setup_mount)(struct filesystem* fs, struct mount* mount, struct mount* mount_parent); }FileSystem; struct file_operations { @@ -67,7 +70,6 @@ struct vnode_operations { int (*mkdir)(struct vnode* dir_node, struct vnode** target, const char* component_name); int (*ls)(struct dentry* target); - int (*chdir)(struct dentry* target); }; @@ -85,7 +87,10 @@ int vfs_write(struct file* file, const void* buf, size_t len); int vfs_read(struct file* file, void* buf, size_t len); int vfs_mkdir(const char* pathname); int vfs_ls(const char* pathname); +int print_childs(Dentry *target); int vfs_chdir(const char* pathname); +int change_global_path(Dentry *target); int vfs_mount(const char* pathname, const char* filesystem); + #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 47d9ae96b..9930ca5f6 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -36,6 +36,7 @@ int main(unsigned long dtb_base){ fs_test1(); fs_test2(); fs_test3(); + fs_test4(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index 2fdc663f8..9079e93c5 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -5,7 +5,7 @@ #include void fs_test1(){ - uart_puts("--------------------------TEST VFS_OPEN--------------------------\n"); + uart_puts("----------------------------TEST VFS_OPEN---------------------------\n"); File *file_a = NULL; int err = vfs_open("hello", 0, &file_a); if(err) uart_puts("[x] Failed to open hello\n"); @@ -21,7 +21,7 @@ void fs_test1(){ } void fs_test2(){ - uart_puts("--------------------TEST VFS_WRITE & VFS_READ--------------------\n"); + uart_puts("----------------------TEST VFS_WRITE & VFS_READ---------------------\n"); File *a = NULL; int err = vfs_open("hello", O_CREAT, &a); if(err) uart_puts("[x] Failed to open \"hello\"\n"); @@ -54,7 +54,7 @@ void fs_test2(){ } void fs_test3(){ - uart_puts("---------------------TEST VFS_MKDIR & VFS_LS---------------------\n"); + uart_puts("-----------------------TEST VFS_MKDIR & VFS_LS----------------------\n"); vfs_mkdir("/path1"); int err = vfs_mkdir("/path1/path2/"); if(err){ @@ -72,6 +72,15 @@ void fs_test3(){ vfs_ls("./path1/"); vfs_ls("path1/path2"); vfs_ls("path1/path2/../../"); - err = vfs_ls("path1/abc"); - if(err) uart_puts("[x] Failed to ls \"path1/abc\"\n"); + // err = vfs_ls("path1/abc"); + // if(err) uart_puts("[x] Failed to ls \"path1/abc\"\n"); +} + +void fs_test4(){ + uart_puts("---------------------TEST VFS_MOUNT & VFS_UMONT---------------------\n"); + vfs_chdir("path1"); + vfs_chdir("path2"); + vfs_chdir("../../"); + + } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index e1b8b108e..678c2fc0b 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -10,8 +10,9 @@ struct vnode_operations* tmpfs_vnode_ops; extern char *global_dir; extern Dentry *global_dentry; -int tmpfs_setup_mount(FileSystem *fs, Mount *mount){ +int tmpfs_setup_mount(FileSystem *fs, Mount *mount, Mount *mount_parent){ mount->fs = fs; + mount->mount_parent = mount_parent; mount->root_dentry = tmpfs_create_dentry("/", NULL, D_DIR, mount); return 0; } @@ -26,7 +27,6 @@ Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type t if(new_dentry->parent != NULL){ /* add to parent's child list */ list_add_tail(&new_dentry->list, &parent->childs); - // uart_puts("[*] Added to parent's child list\n"); } new_dentry->type = type; new_dentry->mount = mount; @@ -56,7 +56,6 @@ void tmpfs_set_ops(){ tmpfs_vnode_ops->lookup = tmpfs_lookup; tmpfs_vnode_ops->create = tmpfs_create; tmpfs_vnode_ops->mkdir = tmpfs_mkdir; - tmpfs_vnode_ops->ls = tmpfs_ls; } int tmpfs_write(struct file* file, const void* buf, size_t len){ @@ -250,27 +249,3 @@ int tmpfs_mkdir(struct vnode* dir_node, struct vnode** target, const char* compo return 0; } -int tmpfs_ls(struct dentry* target){ - /* print the file/folder name */ - struct list_head *pos; - list_for_each(pos, &target->childs){ - Dentry *child = (Dentry *)pos; - uart_puts(child->name); - uart_puts("["); - if(child->type == D_DIR) uart_puts("DIR"); - else uart_puts("FILE"); - uart_puts("]"); - uart_puts("\t"); - } - uart_puts("\n"); - - return 0; -} - -int tmpfs_chdir(struct dentry *target){ - /* change the current working directory */ - - - - return 0; -} diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 7f9ba896f..d84bae114 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -27,7 +27,7 @@ void rootfs_init(char *fs_name){ if(err) uart_puts("[x] Failed to register filesystem\n"); rootfs = (Mount *)kmalloc(sizeof(Mount)); - fs_pool[0]->setup_mount(fs_pool[0], rootfs); + fs_pool[0]->setup_mount(fs_pool[0], rootfs, NULL); // NULL: rootfs no parent global_dir = (char *)kmalloc(sizeof(char) * 2046); strcpy(global_dir, "/"); @@ -166,6 +166,22 @@ int vfs_mkdir(const char *pathname){ return 0; } +int print_childs(Dentry *target){ + struct list_head *pos; + list_for_each(pos, &target->childs){ + Dentry *child = (Dentry *)pos; + uart_puts(child->name); + uart_puts("["); + if(child->type == D_DIR) uart_puts("DIR"); + else if(child->type == D_MOUNT) uart_puts("MOUNT"); + else uart_puts("FILE"); + uart_puts("]"); + uart_puts("\t"); + } + uart_puts("\n"); + return 0; +} + int vfs_ls(const char *pathname){ /* now path */ Dentry *target_path = NULL; @@ -185,18 +201,51 @@ int vfs_ls(const char *pathname){ if(target_vnode == NULL) return -2; /* print the file/folder name */ - return rootfs->root_dentry->vnode->v_ops->ls(target_vnode->dentry); + return print_childs(target_path); +} + + +int change_global_path(Dentry *target){ + /* change the current working directory */ + if(target->type == D_DIR){ + /* if the target is a directory, change the current working directory */ + global_dentry = target; + } + else if(target->type == D_MOUNT){ + global_dentry = target->mount->root_dentry; + } + unsigned int idx = 0; + char *path_arr[50]; + while(target != NULL){ + /* it means the target is the another fs root path*/ + if(strcmp(target->name, "/") == 0 && target->mount != NULL) + path_arr[idx] = target->mount_point_dentry->name; + else + path_arr[idx] = target->name; + target = target->parent; + idx++; + } + strcpy(global_dir, "/"); + for(int i = idx - 2; i >= 0; i--){ + strcat(global_dir, path_arr[i]); + strcat(global_dir, "/"); + } + uart_puts(global_dir); + uart_puts("\n"); + return 0; } int vfs_chdir(const char *pathname){ Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; + + /* just cd is goto root path */ if(pathname == NULL){ global_dentry = rootfs->root_dentry; strcpy(global_dir, "/"); - target_path = global_dentry; - target_vnode = global_dentry->vnode; + uart_puts(global_dir); + uart_puts("\n"); return 0; } else{ @@ -209,9 +258,12 @@ int vfs_chdir(const char *pathname){ if(target_vnode == NULL) return -2; /* change the global_dentry and global_dir */ - return rootfs->root_dentry->vnode->v_ops->chdir(target_vnode->dentry); + return change_global_path(target_vnode->dentry); + } + + int vfs_mount(const char *pathname, const char *filesystem){ if(filesystem == NULL) return -1; Dentry *target_path = NULL; @@ -242,7 +294,7 @@ int vfs_mount(const char *pathname, const char *filesystem){ } } /* cannot find the filesystem , use the empty fs and register it*/ - fs_pool[idx]->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" + fs_pool[idx]->name = (char *)kmalloc(sizeof(char) * (strlen(filesystem)+1)); strcpy(fs_pool[idx]->name, filesystem); fs_pool[idx]->setup_mount = tmpfs_setup_mount; target_fs = fs_pool[idx]; @@ -252,13 +304,20 @@ int vfs_mount(const char *pathname, const char *filesystem){ MOUNT_FS:; /* mount the filesystem */ Mount *new_mount = (Mount *)kmalloc(sizeof(Mount)); - target_fs->setup_mount(target_fs, new_mount); - new_mount->mount_parent = target_path->mount; + target_fs->setup_mount(target_fs, new_mount, target_path->mount); + /* change the target vnode's mount and type */ target_vnode->dentry->type = D_MOUNT; + target_vnode->dentry->mount = new_mount; + + new_mount->root_dentry->parent = target_vnode->dentry->parent; + /* for the chdir find the mount_point name */ + new_mount->root_dentry->mount_point_dentry = target_vnode->dentry; return 0; } +// TODO: umount + int vfs_close(struct file* file) { // 1. release the file handle // 2. Return error code if fails From 411a6724f01f7eb8cd1241ed56a3577037031309 Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 27 May 2022 02:03:17 +0800 Subject: [PATCH 38/62] vfs_mount & vfs_umount & vfs_chdir: simple test ok --- kernel/include/tmpfs.h | 2 +- kernel/include/vfs.h | 4 ++- kernel/src/test_fs.c | 20 ++++++++++--- kernel/src/tmpfs.c | 4 +-- kernel/src/vfs.c | 64 ++++++++++++++++++++++++++++++++++-------- 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 0b3911331..1b3219035 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -16,7 +16,7 @@ typedef struct tmpfs_inode{ struct vnode* vnode; }TmpfsInode; -int tmpfs_setup_mount(FileSystem *fs, Mount *mount, Mount *mount_parent); +int tmpfs_setup_mount(FileSystem *fs, Mount *mount); Dentry *tmpfs_create_dentry(const char *name, Dentry *parent, enum dentry_type type, Mount* mount); VNode *tmpfs_create_vnode(Dentry *dentry); void tmpfs_set_ops(); diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 2b265959f..d0bf3cae0 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -51,7 +51,8 @@ typedef struct mount { typedef struct filesystem { char* name; - int (*setup_mount)(struct filesystem* fs, struct mount* mount, struct mount* mount_parent); + struct mount* mount; + int (*setup_mount)(struct filesystem* fs, struct mount* mount); }FileSystem; struct file_operations { @@ -91,6 +92,7 @@ int print_childs(Dentry *target); int vfs_chdir(const char* pathname); int change_global_path(Dentry *target); int vfs_mount(const char* pathname, const char* filesystem); +int vfs_umount(const char *pathname); #endif \ No newline at end of file diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index 9079e93c5..da7e3e0f9 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -78,9 +78,21 @@ void fs_test3(){ void fs_test4(){ uart_puts("---------------------TEST VFS_MOUNT & VFS_UMONT---------------------\n"); - vfs_chdir("path1"); - vfs_chdir("path2"); - vfs_chdir("../../"); - + vfs_chdir(NULL); + vfs_mkdir("mnt"); + vfs_mount("mnt", "abc"); + vfs_ls(NULL); + vfs_chdir("mnt"); + File *file_a = NULL; + vfs_open("mount_file", O_CREAT, &file_a); + vfs_ls(NULL); + vfs_chdir("../"); + vfs_ls(NULL); + + vfs_umount("mnt"); + vfs_ls(NULL); + vfs_mkdir("/path1/path2/fanfan"); + vfs_mount("/path1/path2/fanfan", "abc"); + vfs_ls("/path1/path2/fanfan"); } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 678c2fc0b..af940eee2 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -10,10 +10,10 @@ struct vnode_operations* tmpfs_vnode_ops; extern char *global_dir; extern Dentry *global_dentry; -int tmpfs_setup_mount(FileSystem *fs, Mount *mount, Mount *mount_parent){ +int tmpfs_setup_mount(FileSystem *fs, Mount *mount){ mount->fs = fs; - mount->mount_parent = mount_parent; mount->root_dentry = tmpfs_create_dentry("/", NULL, D_DIR, mount); + fs->mount = mount; return 0; } diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index d84bae114..0ba65d032 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -7,9 +7,10 @@ char *global_dir; Dentry *global_dentry; Mount *rootfs; -FileSystem *fs_pool[MAX_FS_NUM]; +FileSystem **fs_pool; void rootfs_init(char *fs_name){ + fs_pool = (FileSystem **)kmalloc(sizeof(FileSystem *) * MAX_FS_NUM); for(unsigned int idx = 0; idx < MAX_FS_NUM; idx++){ FileSystem *init_fs = (FileSystem *)kmalloc(sizeof(FileSystem)); init_fs->name = NULL; @@ -27,7 +28,7 @@ void rootfs_init(char *fs_name){ if(err) uart_puts("[x] Failed to register filesystem\n"); rootfs = (Mount *)kmalloc(sizeof(Mount)); - fs_pool[0]->setup_mount(fs_pool[0], rootfs, NULL); // NULL: rootfs no parent + fs_pool[0]->setup_mount(fs_pool[0], rootfs); // NULL: rootfs no parent global_dir = (char *)kmalloc(sizeof(char) * 2046); strcpy(global_dir, "/"); @@ -40,6 +41,7 @@ int register_filesystem(FileSystem *fs) { // you can also initialize memory pool of the file system here. if(fs == NULL) return -1; if(fs->name == NULL || fs->setup_mount == NULL) return -1; + if(strcmp(fs->name, "tmpfs") == 0){ tmpfs_set_ops(); uart_puts("[*] Registered tmpfs\n"); @@ -281,15 +283,20 @@ int vfs_mount(const char *pathname, const char *filesystem){ /* target vnode isn't exist, cannot mount it */ if(target_vnode == NULL) return -2; + + /* target is not a directory, cannot mount it */ + if(target_vnode->dentry->type != D_DIR) return -3; /* target vnode is exist, can mount it */ FileSystem *target_fs = NULL; + Mount *new_mount = NULL; unsigned int idx = 0; /* find the filesystem */ for(; idx < MAX_FS_NUM; idx++){ - if(fs_pool[idx] == NULL) break; + if(fs_pool[idx]->name == NULL) break; if(strcmp(filesystem, fs_pool[idx]->name) == 0){ target_fs = fs_pool[idx]; + new_mount = target_fs->mount; goto MOUNT_FS; } } @@ -300,23 +307,58 @@ int vfs_mount(const char *pathname, const char *filesystem){ target_fs = fs_pool[idx]; err = register_filesystem(target_fs); if(err) uart_puts("[x] Failed to register another filesystem\n"); - + new_mount = (Mount *)kmalloc(sizeof(Mount)); + target_fs->setup_mount(target_fs, new_mount); + +/* mount the filesystem */ MOUNT_FS:; - /* mount the filesystem */ - Mount *new_mount = (Mount *)kmalloc(sizeof(Mount)); - target_fs->setup_mount(target_fs, new_mount, target_path->mount); + /* for the chdir find the mount_point name */ + new_mount->root_dentry->mount_point_dentry = target_vnode->dentry; + new_mount->root_dentry->parent = target_vnode->dentry->parent; + /* set the mount parent */ + new_mount->mount_parent = target_path->mount; + /* change the target vnode's mount and type */ target_vnode->dentry->type = D_MOUNT; target_vnode->dentry->mount = new_mount; - new_mount->root_dentry->parent = target_vnode->dentry->parent; - /* for the chdir find the mount_point name */ - new_mount->root_dentry->mount_point_dentry = target_vnode->dentry; + char *mount_fs_name = target_fs->name; + uart_puts("[*] Mount: mount \""); + uart_puts(mount_fs_name); + uart_puts("\" file system success\n"); return 0; } -// TODO: umount +int vfs_umount(const char *pathname){ + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + char component_name[MAX_PATHNAME_LEN]; + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + + /* target vnode isn't exist, cannot umount it */ + if(target_vnode == NULL) return -2; + /* target isn't a mount point */ + if(target_vnode->dentry->type != D_MOUNT) return -3; + + /* umount the filesystem */ + target_vnode->dentry->mount->root_dentry->mount_point_dentry = NULL; + target_vnode->dentry->mount->root_dentry->parent = NULL; + /* set the mount parent */ + target_vnode->dentry->mount->mount_parent = NULL; + + // kfree(target_vnode->dentry->mount); + target_vnode->dentry->mount = target_vnode->dentry->parent->mount; + target_vnode->dentry->type = D_DIR; + + char *umount_fs_name = target_vnode->dentry->mount->fs->name; + uart_puts("[*] Umount: umount \""); + uart_puts(umount_fs_name); + uart_puts("\" file system success\n"); + + return 0; +} int vfs_close(struct file* file) { // 1. release the file handle From 4fff30adf84970d37b699891efeddbe2a1b48b7f Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 27 May 2022 20:03:37 +0800 Subject: [PATCH 39/62] shell: add the vfs_{mathod} --- kernel/include/shell.h | 6 ++++ kernel/include/string.h | 1 - kernel/include/vfs.h | 2 +- kernel/src/shell.c | 77 +++++++++++++++++++++++++++++++++++++++-- kernel/src/test_fs.c | 16 +++++++++ kernel/src/vfs.c | 40 ++++++++++----------- 6 files changed, 117 insertions(+), 25 deletions(-) diff --git a/kernel/include/shell.h b/kernel/include/shell.h index f22dd1728..53f576459 100644 --- a/kernel/include/shell.h +++ b/kernel/include/shell.h @@ -33,4 +33,10 @@ void Exec(char [MAX_SIZE]); void SetTimeOut(char [MAX_SIZE]); void TestTimeOut(char [MAX_SIZE]); +void ls_arg(char *); +void chdir_arg(char *); +void mkdir_arg(char *); +void mount_arg(char *); +void umount_arg(char *); + #endif \ No newline at end of file diff --git a/kernel/include/string.h b/kernel/include/string.h index 4500c42c9..7a16d1cff 100644 --- a/kernel/include/string.h +++ b/kernel/include/string.h @@ -3,7 +3,6 @@ #define MAX_SIZE 512 - enum print_type{ UITOHEX, UITOA, diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index d0bf3cae0..a074407b4 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -10,7 +10,7 @@ enum dentry_type { D_MOUNT }; #define O_CREAT 1 -#define MAX_PATHNAME_LEN 256 +#define MAX_PATHNAME_LEN 64 #define MAX_DATA_LEN 512 #define MAX_FS_NUM 32 #define EOF (-1) diff --git a/kernel/src/shell.c b/kernel/src/shell.c index 535f5b5ea..0e60f054c 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -9,12 +9,16 @@ #include #include #include +#include + +extern char *global_dir; /* print welcome message*/ void PrintWelcome(){ uart_puts("******************************************************************\n"); uart_puts("********************* Welcome to FanFan's OS *********************\n"); uart_puts("******************************************************************\n"); + uart_puts(global_dir); uart_puts("# "); } @@ -26,12 +30,18 @@ void PrintHelp(){ uart_puts("revision : print board_revision\n"); uart_puts("memory : print memory info\n"); uart_puts("reboot : reboot the device\n"); - uart_puts("ls : list directory contents\n"); + uart_puts("cpio_ls : list cpio directory contents\n"); uart_puts("exec : concatenate files and print on the standard output\n"); uart_puts("setTimeout : set timeout for read\n"); uart_puts("test_timeout : test timeout for read\n"); + uart_puts("ls : list directory contents\n"); + uart_puts("cd : change working directory\n"); + uart_puts("mkdir : make directories\n"); + uart_puts("mount : mount a filesystem\n"); + uart_puts("umount : umount a filesystem\n"); } + /* print unknown command message*/ void PrintUnknown(char buf[MAX_SIZE]){ uart_puts("Unknown command: "); @@ -137,6 +147,59 @@ void TestTimeOut(char buf[MAX_SIZE]){ } +void ls_arg(char *buf){ + char *path = strchr(buf, ' ') + 1; + if(strlen(path) == 0){ + vfs_ls(NULL); + } + else{ + vfs_ls(path); + } +} + +void chdir_arg(char *buf){ + char *path = strchr(buf, ' ') + 1; + if(strlen(path) == 0){ + vfs_chdir(NULL); + } + else{ + vfs_chdir(path); + } +} + +void mkdir_arg(char *buf){ + char *path = strchr(buf, ' ') + 1; + if(strlen(path) == 0){ + vfs_mkdir(NULL); + } + else{ + vfs_mkdir(path); + } +} + +void mount_arg(char *buf){ + char *path_ptr = strchr(buf, ' ') + 1; + char *fs_name_ptr = strchr(path_ptr, ' ') + 1; + path_ptr[strlen(path_ptr) - strlen(fs_name_ptr) - 1] = '\0'; + + char path[MAX_PATHNAME_LEN * 32]; + char fs_name[MAX_PATHNAME_LEN]; + strcpy(path, path_ptr); + strcpy(fs_name, fs_name_ptr); + + vfs_mount(path, fs_name); +} + +void umount_arg(char *buf){ + char *path = strchr(buf, ' ') + 1; + if(strlen(path) == 0){ + vfs_umount(NULL); + } + else{ + vfs_umount(path); + } +} + /* Main Shell */ void ShellLoop(){ char buf[MAX_SIZE]; @@ -146,6 +209,7 @@ void ShellLoop(){ memset(buf, '\0', MAX_SIZE); unsigned int size = readline(buf, sizeof(buf)); if (size == 0){ + uart_puts(global_dir); uart_puts("# "); continue; } @@ -155,13 +219,20 @@ void ShellLoop(){ else if(strcmp("revision", buf) == 0) PrintRevision(buf); else if(strcmp("memory", buf) == 0) PrintMemory(buf); else if(strcmp("bootimg", buf) == 0) Bootimg(buf); - else if(strcmp("ls", buf) == 0) Ls(); + else if(strcmp("cpio_ls", buf) == 0) Ls(); else if(strcmp("cat", buf) == 0) Cat(buf); else if(strcmp("exec", buf) == 0) Exec(buf); else if(strncmp("setTimeout", buf, strlen("setTimeout")) == 0) SetTimeOut(buf); else if(strcmp("test_timeout", buf) == 0) TestTimeOut(buf); + else if(strncmp("ls", buf, strlen("ls")) == 0) ls_arg(buf); + else if(strncmp("cd", buf, strlen("cd")) == 0) chdir_arg(buf); + else if(strncmp("mkdir", buf, strlen("mkdir")) == 0) mkdir_arg(buf); + else if(strncmp("mount", buf, strlen("mount")) == 0) mount_arg(buf); + else if(strncmp("umount", buf, strlen("umount")) == 0) umount_arg(buf); else PrintUnknown(buf); - + + + uart_puts(global_dir); uart_puts("# "); } diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index da7e3e0f9..e7cd16945 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -85,6 +85,9 @@ void fs_test4(){ vfs_chdir("mnt"); File *file_a = NULL; vfs_open("mount_file", O_CREAT, &file_a); + vfs_write(file_a, "Hello Mount\n", 12); + vfs_close(file_a); + vfs_ls(NULL); vfs_chdir("../"); vfs_ls(NULL); @@ -95,4 +98,17 @@ void fs_test4(){ vfs_mkdir("/path1/path2/fanfan"); vfs_mount("/path1/path2/fanfan", "abc"); vfs_ls("/path1/path2/fanfan"); + + vfs_open("/path1/path2/fanfan/mount_file", 0, &file_a); + char buf[300]; + int sz; + sz = vfs_read(file_a, buf, 100); + buf[sz] = '\0'; + vfs_close(file_a); + uart_puts(buf); + + // vfs_umount("/path1/path2/fanfan"); + + // vfs_chdir(NULL); + } \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 0ba65d032..267be962d 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -78,6 +78,7 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, char tmp_buf[MAX_PATHNAME_LEN]; find_component_name(pathname + idx, component_name, '/'); + while(component_name[0] != '\0'){ /* * ready_return is 1 beacuse it couldn't find child vnode before @@ -113,7 +114,6 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, } /* if the last component name is '\0', it is a file name */ strcpy(component_name, tmp_buf); - return 0; } @@ -232,8 +232,8 @@ int change_global_path(Dentry *target){ strcat(global_dir, path_arr[i]); strcat(global_dir, "/"); } - uart_puts(global_dir); - uart_puts("\n"); + // uart_puts(global_dir); + // uart_puts("\n"); return 0; } @@ -246,18 +246,21 @@ int vfs_chdir(const char *pathname){ if(pathname == NULL){ global_dentry = rootfs->root_dentry; strcpy(global_dir, "/"); - uart_puts(global_dir); - uart_puts("\n"); + // uart_puts(global_dir); + // uart_puts("\n"); return 0; } else{ int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); if(err) return -1; // worng pathname - if(target_vnode->dentry->type == D_FILE) return -2; // cannot change to a file } /* file/folder not exist */ if(target_vnode == NULL) return -2; + + /* cannot change to a file */ + if(target_vnode->dentry->type == D_FILE) return -3; + /* change the global_dentry and global_dir */ return change_global_path(target_vnode->dentry); @@ -268,19 +271,15 @@ int vfs_chdir(const char *pathname){ int vfs_mount(const char *pathname, const char *filesystem){ if(filesystem == NULL) return -1; + // print_string(UITOA, "path_len: ", strlen(pathname), 0); + // print_string(UITOA, " | filefs_len: ", strlen(filesystem), 1); Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; - int err; - if(pathname == NULL){ - target_path = global_dentry; - target_vnode = global_dentry->vnode; - } - else{ - err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); - if(err) return -1; // worng pathname - } + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + // uart_puts(target_vnode->dentry->name); /* target vnode isn't exist, cannot mount it */ if(target_vnode == NULL) return -2; @@ -325,7 +324,7 @@ MOUNT_FS:; char *mount_fs_name = target_fs->name; uart_puts("[*] Mount: mount \""); uart_puts(mount_fs_name); - uart_puts("\" file system success\n"); + uart_puts("\" filesystem success\n"); return 0; } @@ -348,14 +347,15 @@ int vfs_umount(const char *pathname){ /* set the mount parent */ target_vnode->dentry->mount->mount_parent = NULL; + char *umount_fs_name = target_vnode->dentry->mount->fs->name; + uart_puts("[*] Umount: umount \""); + uart_puts(umount_fs_name); + uart_puts("\" filesystem success\n"); + // kfree(target_vnode->dentry->mount); target_vnode->dentry->mount = target_vnode->dentry->parent->mount; target_vnode->dentry->type = D_DIR; - char *umount_fs_name = target_vnode->dentry->mount->fs->name; - uart_puts("[*] Umount: umount \""); - uart_puts(umount_fs_name); - uart_puts("\" file system success\n"); return 0; } From cad2e2d5e3b07b1855461270d5105c244f7f893d Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 27 May 2022 20:30:39 +0800 Subject: [PATCH 40/62] update O_CREAT --- kernel/include/vfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index a074407b4..1f772d63a 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -9,7 +9,7 @@ enum dentry_type { D_FILE, D_MOUNT }; -#define O_CREAT 1 +#define O_CREAT 0b100 #define MAX_PATHNAME_LEN 64 #define MAX_DATA_LEN 512 #define MAX_FS_NUM 32 From afc2b4da686bc9db16a2a47b7cd3365066a5d409 Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 28 May 2022 00:41:39 +0800 Subject: [PATCH 41/62] add vfs syscall call --- kernel/include/sched.h | 6 ++ kernel/include/syscall.h | 7 +++ kernel/include/timer.h | 1 + kernel/include/user_syscall.h | 17 +++++- kernel/include/vfs.h | 1 + kernel/src/exc.c | 21 +++++++ kernel/src/main.c | 8 +-- kernel/src/sched.c | 8 +++ kernel/src/shell.c | 4 +- kernel/src/syscall.c | 106 ++++++++++++++++++++++++++++++++++ kernel/src/timer.c | 7 +++ kernel/src/user_syscall.S | 48 +++++++++++++++ kernel/src/vfs.c | 41 +------------ 13 files changed, 227 insertions(+), 48 deletions(-) diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 643f0372f..04eff4e5e 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -2,12 +2,14 @@ #define SCHED_H_ #include #include +#include #define MAX_THREAD 0x100 #define MAX_SIG_HANDLER 0x20 #define STACK_SIZE 0x1000 + enum thread_state{ NOUSE, RUNNING, @@ -52,6 +54,10 @@ typedef struct _Thread{ SignalInfo sig_queue_head; // ready queue void *sig_stack_addr; void *old_tp; + + /* vfs */ + char dir[MAX_PATHNAME_LEN * 16]; + File *fd_table[MAX_FD_NUM]; // max 16 fd }Thread; extern Thread* get_current(); diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 07615be27..ae545d48d 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -22,6 +22,13 @@ void sys_kill(TrapFrame *); void sys_signal_register(TrapFrame *); void sys_signal_kill(TrapFrame *); void sys_sigreturn(TrapFrame *); +void sys_open(TrapFrame *); +void sys_close(TrapFrame *); +void sys_write(TrapFrame *); +void sys_read(TrapFrame *); +void sys_mkdir(TrapFrame *); +void sys_mount(TrapFrame *); +void sys_chdir(TrapFrame *); int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); diff --git a/kernel/include/timer.h b/kernel/include/timer.h index e2c37a4c8..8c5894260 100644 --- a/kernel/include/timer.h +++ b/kernel/include/timer.h @@ -34,5 +34,6 @@ void timeout_print(void *); void sched_timeout(void *); void timer_interrupt_handler(); void timer_interrupt_handler_el0(); +void enable_el0_get_timer(); #endif \ No newline at end of file diff --git a/kernel/include/user_syscall.h b/kernel/include/user_syscall.h index 4de39573a..820b60041 100644 --- a/kernel/include/user_syscall.h +++ b/kernel/include/user_syscall.h @@ -11,7 +11,14 @@ #define KILL 7 #define SIGNAL_ 8 #define SIGKILL 9 -#define SIGRETURN 20 +#define SIGRETURN 10 +#define OPEN 11 +#define CLOSE 12 +#define WRITE 13 +#define READ 14 +#define MKDIR 15 +#define MOUNT 16 +#define CHDIR 17 #endif @@ -27,5 +34,13 @@ extern int mbox_call(unsigned char ch, unsigned int *mbox); extern void kill(int pid); extern void singal(int SIGNAL, void (*handler)()); extern void signal_kill(int pid, int SIGNAL); +extern int open(const char *pathname, int flags); +extern int close(int fd); +extern long write(int fd, const void *buf, unsigned long count); +extern long read(int fd, void *buf, unsigned long count); +extern int mkdir(const char *pathname, unsigned mode); +extern int mount(const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); +extern int chdir(const char *path); + #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 1f772d63a..1014fdb33 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -13,6 +13,7 @@ enum dentry_type { #define MAX_PATHNAME_LEN 64 #define MAX_DATA_LEN 512 #define MAX_FS_NUM 32 +#define MAX_FD_NUM 16 #define EOF (-1) // file handle diff --git a/kernel/src/exc.c b/kernel/src/exc.c index 021dcfaf6..6704f07b3 100644 --- a/kernel/src/exc.c +++ b/kernel/src/exc.c @@ -65,6 +65,27 @@ void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ case SIGRETURN: sys_sigreturn(trapFrame); break; + case OPEN: + sys_open(trapFrame); + break; + case CLOSE: + sys_close(trapFrame); + break; + case WRITE: + sys_write(trapFrame); + break; + case READ: + sys_read(trapFrame); + break; + case MKDIR: + sys_mkdir(trapFrame); + break; + case MOUNT: + sys_mount(trapFrame); + break; + case CHDIR: + sys_chdir(trapFrame); + break; default: break; diff --git a/kernel/src/main.c b/kernel/src/main.c index 9930ca5f6..449ccb3f2 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -11,27 +11,25 @@ #include #include #include +#include extern Thread *run_thread_head; int main(unsigned long dtb_base){ - unsigned long long tmp; - asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); - tmp |= 1; - asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); uart_init(); + enable_el0_get_timer(); // uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); + rootfs_init("tmpfs"); init_thread_pool_and_head(); init_task_head(); enable_timer_irq(); enable_irq(); // DAIF set to 0b0000 - rootfs_init("tmpfs"); fs_test1(); fs_test2(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 69cb3985a..5f0e511e2 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -8,9 +8,11 @@ #include #include #include +#include Thread *thread_pool; Thread *run_thread_head; +extern char *global_dir; void init_thread_pool_and_head(){ thread_pool = (Thread*)kmalloc(sizeof(Thread) * MAX_THREAD); @@ -64,6 +66,8 @@ Thread *thread_create(void(*func)()){ INIT_LIST_HEAD(&new_thread->sig_info_pool[i].list); } + strcpy(new_thread->dir, global_dir); + print_string(UITOHEX, "[*] new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); uart_puts(" | "); print_string(UITOHEX, "[*] new_thread->kstack: ", (unsigned long long )new_thread->kstack_addr, 1); @@ -131,6 +135,10 @@ void schedule(){ next_thread = (Thread *)next_thread->list.next; }while(list_is_head(&next_thread->list, &run_thread_head->list) || next_thread->state == EXIT); + strcpy(global_dir, next_thread->dir); + // uart_puts(global_dir); + // uart_puts("\n"); + // print_string(UITOHEX, "curr: ", curr_thread->id, 0); // uart_puts(" | "); // print_string(UITOHEX, "next: ", next_thread->id, 1); diff --git a/kernel/src/shell.c b/kernel/src/shell.c index 0e60f054c..60b87c05a 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -182,8 +182,8 @@ void mount_arg(char *buf){ char *fs_name_ptr = strchr(path_ptr, ' ') + 1; path_ptr[strlen(path_ptr) - strlen(fs_name_ptr) - 1] = '\0'; - char path[MAX_PATHNAME_LEN * 32]; - char fs_name[MAX_PATHNAME_LEN]; + char path[MAX_PATHNAME_LEN * 16 + 1]; + char fs_name[MAX_PATHNAME_LEN + 1]; strcpy(path, path_ptr); strcpy(fs_name, fs_name_ptr); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 9e548e910..e957606f0 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include extern Thread *thread_pool; extern Thread *run_thread_head; @@ -321,5 +323,109 @@ void sys_sigreturn(TrapFrame *trapFrame){ current->sig_stack_addr = NULL; current->old_tp = NULL; + enable_irq(); +} + +void sys_open(TrapFrame *trapFrame){ + disable_irq(); + char *path = (char *)trapFrame->x[0]; + int flags = trapFrame->x[1]; + Thread *curr_thread = get_current(); + File *file = NULL; + int status = vfs_open(path, flags, &file); + unsigned int fd_idx = 0; + if(status == 0){ + for(;fd_idx < MAX_FD_NUM; fd_idx++){ + if(curr_thread->fd_table[fd_idx] == NULL){ + curr_thread->fd_table[fd_idx] = file; + trapFrame->x[0] = fd_idx; + return; + } + } + kfree(file); + file = NULL; + trapFrame->x[0] = -1; + } + else + trapFrame->x[0] = status; + enable_irq(); +} + +void sys_close(TrapFrame *trapFrame){ + disable_irq(); + int fd = trapFrame->x[0]; + + Thread *curr_thread = get_current(); + if(fd < 0 || fd >= MAX_FD_NUM) + trapFrame->x[0] = -1; + else if(curr_thread->fd_table[fd] == NULL) + trapFrame->x[0] = -1; + else{ + int status = vfs_close(curr_thread->fd_table[fd]); + if(status == 0) curr_thread->fd_table[fd] = NULL; + trapFrame->x[0] = status; + } + enable_irq(); +} + +void sys_write(TrapFrame *trapFrame){ + disable_irq(); + int fd = trapFrame->x[0]; + char *buf = (char *)trapFrame->x[1]; + int count = trapFrame->x[2]; + + Thread *curr_thread = get_current(); + if(fd < 0 || fd >= MAX_FD_NUM) + trapFrame->x[0] = -1; + else if(curr_thread->fd_table[fd] == NULL) + trapFrame->x[0] = -1; + else{ + int status = vfs_write(curr_thread->fd_table[fd], buf, count); + trapFrame->x[0] = status; + } + enable_irq(); +} + +void sys_read(TrapFrame *trapFrame){ + disable_irq(); + int fd = trapFrame->x[0]; + char *buf = (char *)trapFrame->x[1]; + int count = trapFrame->x[2]; + + Thread *curr_thread = get_current(); + if(fd < 0 || fd >= MAX_FD_NUM) + trapFrame->x[0] = -1; + else if(curr_thread->fd_table[fd] == NULL) + trapFrame->x[0] = -1; + else{ + int status = vfs_read(curr_thread->fd_table[fd], buf, count); + trapFrame->x[0] = status; + } + enable_irq(); +} + +void sys_mkdir(TrapFrame *trapFrame){ + disable_irq(); + char *path = (char *)trapFrame->x[0]; + // int mode = trapFrame->x[1]; + int status = vfs_mkdir(path); + trapFrame->x[0] = status; + enable_irq(); +} + +void sys_mount(TrapFrame *trapFrame){ + disable_irq(); + char *path = (char *)trapFrame->x[1]; + char *fsname = (char *)trapFrame->x[2]; + int status = vfs_mount(path, fsname); + trapFrame->x[0] = status; + enable_irq(); +} + +void sys_chdir(TrapFrame *trapFrame){ + disable_irq(); + char *path = (char *)trapFrame->x[0]; + int status = vfs_chdir(path); + trapFrame->x[0] = status; enable_irq(); } \ No newline at end of file diff --git a/kernel/src/timer.c b/kernel/src/timer.c index 52adc90b0..bcac4e3d5 100644 --- a/kernel/src/timer.c +++ b/kernel/src/timer.c @@ -111,4 +111,11 @@ void sched_timeout(void *args){ unsigned long long frq; asm volatile("mrs %0, cntfrq_el0\n\t" :"=r"(frq)); add_timer(sched_timeout, frq>>5 , "omg", 1); +} + +void enable_el0_get_timer(){ + unsigned long long tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); } \ No newline at end of file diff --git a/kernel/src/user_syscall.S b/kernel/src/user_syscall.S index d298dbb2c..79d27fe32 100644 --- a/kernel/src/user_syscall.S +++ b/kernel/src/user_syscall.S @@ -60,4 +60,52 @@ signal: signal_kill: mov x8, SIGKILL svc #0 + ret + +.global sigreturn +sigreturn: + mov x8, SIGRETURN + svc #0 + ret + +.global open +open: + mov x8, OPEN + svc #0 + ret + +.global close +close: + mov x8, CLOSE + svc #0 + ret + +.global write +write: + mov x8, WRITE + svc #0 + ret + +.global read +read: + mov x8, READ + svc #0 + ret + +.global mkdir +mkdir: + mov x8, MKDIR + svc #0 + ret + +.global mount +mount: + mov x8, MOUNT + svc #0 + ret + +.global chdir +chdir: + mov x8, CHDIR + svc #0 ret \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 267be962d..ae28ffaab 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -30,7 +30,7 @@ void rootfs_init(char *fs_name){ rootfs = (Mount *)kmalloc(sizeof(Mount)); fs_pool[0]->setup_mount(fs_pool[0], rootfs); // NULL: rootfs no parent - global_dir = (char *)kmalloc(sizeof(char) * 2046); + global_dir = (char *)kmalloc(sizeof(char) * 1024); strcpy(global_dir, "/"); global_dentry = rootfs->root_dentry; } @@ -382,42 +382,3 @@ int vfs_read(struct file* file, void* buf, size_t len) { return file->f_ops->read(file, buf, len); } - - -// int traversal_path(const char *pathname, Dentry *target_path, char *target_name){ -// find_target_name(pathname, target_name, '/'); -// if(*target_name == '\0'){ -// /* if the target name is empty, it is end of the directory */ -// return 0; -// } -// else if(strcmp(target_name, ".")){ -// /* if the target name is ".", it is the same directory */ -// return traversal_path(pathname + strlen(target_name) + 1, target_path, target_name); -// } -// else if(strcmp(target_name, "..")){ -// /* if dentry is root path, return it */ -// if(target_path->parent == NULL) return 0; -// /* if dentry is not root path, find its parent */ -// return traversal_path(pathname + strlen(target_name) + 1, target_path->parent, target_name); -// } -// else{ -// /* need to find the child dentry */ -// struct list_head *pos; -// list_for_each(pos, &target_path->childs){ -// Dentry *tmp = (Dentry *)pos; -// if(strcmp(tmp->name, target_name) == 0){ -// // TODO: need to check the dir is other filesystem - -// if(target_path->type == D_DIR){ -// return traversal_path(pathname + strlen(target_name) + 1, tmp, target_name); -// } - -// /* if the target is a file, break and return it */ -// return 0; -// } -// } -// } -// /* if the target is not found, return error code */ -// return -1; -// } - From 571704a21aff132e338d7fdecc67d7327a05966f Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 28 May 2022 20:19:02 +0800 Subject: [PATCH 42/62] find_component_name: return the null flag --- debug.gdb | 2 +- kernel/include/test_fs.h | 3 ++ kernel/include/vfs.h | 2 +- kernel/src/main.c | 11 +++-- kernel/src/test_fs.c | 94 ++++++++++++++++++++++++++++++++++++++++ kernel/src/vfs.c | 22 +++++++--- 6 files changed, 121 insertions(+), 13 deletions(-) diff --git a/debug.gdb b/debug.gdb index 53c642965..ef84b4fac 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *sys_sigreturn +b *vfs_lookup target remote :1234 \ No newline at end of file diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h index 026d37283..053226473 100644 --- a/kernel/include/test_fs.h +++ b/kernel/include/test_fs.h @@ -5,5 +5,8 @@ void fs_test1(); void fs_test2(); void fs_test3(); void fs_test4(); +void fs_test5(); +void fs_test6(); +void fs_test7(); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 1014fdb33..8410236bc 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -82,7 +82,7 @@ int register_filesystem(FileSystem* fs); int vfs_open(const char* pathname, int flags, struct file** target); int vfs_lookup(const char* pathname, Dentry **target_dentry, VNode **target_vnode, char *component_name); // int vfs_lookup(const char *pathname, Dentry *target_dentry, VNode *target_vnode); -void find_component_name(const char *pathname, char *target_name, char delimiter); +int find_component_name(const char *pathname, char *target_name, char delimiter); int vfs_close(struct file* file); int vfs_write(struct file* file, const void* buf, size_t len); diff --git a/kernel/src/main.c b/kernel/src/main.c index 449ccb3f2..8f305c9f6 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -31,10 +31,13 @@ int main(unsigned long dtb_base){ enable_irq(); // DAIF set to 0b0000 - fs_test1(); - fs_test2(); - fs_test3(); - fs_test4(); + // fs_test1(); + // fs_test2(); + // fs_test3(); + // fs_test4(); + // fs_test5(); + fs_test6(); + // fs_test7(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index e7cd16945..6bcd4a657 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -3,6 +3,7 @@ #include #include #include +#include void fs_test1(){ uart_puts("----------------------------TEST VFS_OPEN---------------------------\n"); @@ -111,4 +112,97 @@ void fs_test4(){ // vfs_chdir(NULL); +} + +void fs_test5(){ + int a = open("hello", O_CREAT); + int b = open("world", O_CREAT); + write(a, "Hello ", 6); + write(b, "World!", 6); + close(a); + close(b); + b = open("hello", 0); + a = open("world", 0); + print_string(ITOA, "b_fd = ", b, 0); + print_string(ITOA, " | a_fd = ", a, 1); + + char buf[300]; + int sz; + sz = read(b, buf, 100); + sz += read(a, buf + sz, 100); + buf[sz] = '\0'; + uart_puts(buf); // should be Hello World! +} + +void fs_test6(){ + char buf[8]; + mkdir("mnt", 0); + int fd = open("/mnt/a.txt", O_CREAT); + write(fd, "Hi", 2); + close(fd); + chdir("mnt"); + fd = open("./a.txt", 0); + // assert(fd >= 0); + read(fd, buf, 2); + uart_puts(buf); + uart_puts("\n"); + // assert(strncmp(buf, "Hi", 2) == 0); + + chdir(".."); + mount(NULL, "mnt", "abc", 0, NULL); + fd = open("mnt/a.txt", 0); + // assert(fd < 0); + if(fd < 0) uart_puts("[x] fd fail!!\n"); + + vfs_umount("/mnt"); + fd = open("/mnt/a.txt", 0); + if(fd < 0) uart_puts("[x] fd fail!!\n"); + + char buf2[8]; + // assert(fd >= 0); + read(fd, buf2, 2); + uart_puts(buf2); + uart_puts("\n"); + // assert(strncmp(buf, "Hi", 2) == 0); +} + +void fs_test7(){ + char buf[16]; + mkdir("proc", 0); + mount(NULL, "proc", "procfs", 0, NULL); + int fd = open("/proc/switch", 0); + write(fd, "0", 1); + close(fd); + + fd = open("/proc/hello", 0); + int sz = read(fd, buf, 16); + buf[sz] = '\0'; + uart_puts(buf); + uart_puts("\n"); + // printf("%s\n", buf); // should be hello + close(fd); + + fd = open("/proc/switch", 0); + write(fd, "1", 1); + close(fd); + + fd = open("/proc/hello", 0); + sz = read(fd, buf, 16); + buf[sz] = '\0'; + uart_puts(buf); + uart_puts("\n"); + // printf("%s\n", buf); //should be HELLO + close(fd); + + fd = open("/proc/1/status", 0); // choose a created task's id here + sz = read(fd, buf, 16); + buf[sz] = '\0'; + uart_puts(buf); + uart_puts("\n"); + // printf("%s\n", buf); // status of the task. + close(fd); + + fd = open("/proc/999/status", 0); // choose a non-existed task's id here + if(fd < 0) uart_puts("[x] fd fail!"); + // assert(fd < 0); } \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index ae28ffaab..c64c0f865 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -54,18 +54,21 @@ int register_filesystem(FileSystem *fs) { return 0; } -void find_component_name(const char *pathname, char *component_name, char delimiter){ +int find_component_name(const char *pathname, char *component_name, char delimiter){ int i = 0; - while(pathname[i] != delimiter && pathname[i] != '\0'){ + while(pathname[i] != delimiter && pathname[i] != '\0'){ component_name[i] = pathname[i]; i++; } component_name[i] = '\0'; + if(pathname[i] == '\0') return 1; + else return 0; } int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, char *component_name) { int ready_return = 0; int idx; + int idx_null = 0; // 1. Lookup pathname if(pathname[0] == '/'){ idx = 1; @@ -77,7 +80,7 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, } char tmp_buf[MAX_PATHNAME_LEN]; - find_component_name(pathname + idx, component_name, '/'); + idx_null = find_component_name(pathname + idx, component_name, '/'); while(component_name[0] != '\0'){ /* @@ -87,6 +90,7 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, */ if(ready_return) return -1; + /* Try to find the child vnode */ int err = rootfs->root_dentry->vnode->v_ops->lookup((*target_path)->vnode, target_vnode, component_name); if(err){ @@ -96,9 +100,12 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, strcpy(tmp_buf, component_name); /* find next component name*/ - idx += strlen(component_name) + 1; - find_component_name(pathname + idx, component_name, '/'); - + if(idx_null) + idx += strlen(component_name); + else + idx += strlen(component_name) + 1; + idx_null = find_component_name(pathname + idx, component_name, '/'); + /* find the next vnode */ if(*target_vnode != NULL){ /* check the dir is mountpoint or not */ @@ -111,6 +118,7 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, } } + } /* if the last component name is '\0', it is a file name */ strcpy(component_name, tmp_buf); @@ -126,6 +134,7 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { if(err) return -1; // worng pathname // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ + // uart_puts(target_vnode->dentry->name); if(target_vnode->dentry->type == D_DIR || target_vnode->dentry->type == D_MOUNT) return -2; // cannot open a directory err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); @@ -146,7 +155,6 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { return 0; } } - // 4. Return error code if fails return -1; } From 27d2ff33f112d7d3ba23ebb68b0f224127d0f0c1 Mon Sep 17 00:00:00 2001 From: Fan Date: Sat, 28 May 2022 23:19:26 +0800 Subject: [PATCH 43/62] save vfs info to thread --- kernel/include/sched.h | 1 + kernel/src/sched.c | 15 +++++++++++++-- kernel/src/syscall.c | 36 ++++++++++++++++++++++-------------- kernel/src/vfs.c | 2 ++ 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/kernel/include/sched.h b/kernel/include/sched.h index 04eff4e5e..7cfe0f20a 100644 --- a/kernel/include/sched.h +++ b/kernel/include/sched.h @@ -57,6 +57,7 @@ typedef struct _Thread{ /* vfs */ char dir[MAX_PATHNAME_LEN * 16]; + Dentry *dentry; File *fd_table[MAX_FD_NUM]; // max 16 fd }Thread; diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 5f0e511e2..f71de742f 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -13,6 +13,8 @@ Thread *thread_pool; Thread *run_thread_head; extern char *global_dir; +extern Dentry *global_dentry; +extern File **global_fd_table; void init_thread_pool_and_head(){ thread_pool = (Thread*)kmalloc(sizeof(Thread) * MAX_THREAD); @@ -35,6 +37,7 @@ void init_thread_pool_and_head(){ INIT_LIST_HEAD(&thread_pool[i].sig_queue_head.list); thread_pool[i].sig_stack_addr = NULL; thread_pool[i].old_tp = NULL; + } run_thread_head = (Thread *)kmalloc(sizeof(Thread)); @@ -66,7 +69,6 @@ Thread *thread_create(void(*func)()){ INIT_LIST_HEAD(&new_thread->sig_info_pool[i].list); } - strcpy(new_thread->dir, global_dir); print_string(UITOHEX, "[*] new_thread->ustack: ", (unsigned long long )new_thread->ustack_addr, 0); uart_puts(" | "); @@ -135,9 +137,18 @@ void schedule(){ next_thread = (Thread *)next_thread->list.next; }while(list_is_head(&next_thread->list, &run_thread_head->list) || next_thread->state == EXIT); + strcpy(global_dir, next_thread->dir); + global_dentry = next_thread->dentry; + global_fd_table = next_thread->fd_table; + + // print_string(UITOA, "[*] next_thread->id: ", next_thread->id, 0); + // uart_puts(" | "); // uart_puts(global_dir); - // uart_puts("\n"); + // uart_puts(" | "); + // uart_puts(global_dentry->name); + // uart_puts(" | "); + // print_string(UITOHEX, "global_fd_table: ", (unsigned long long )global_fd_table, 1); // print_string(UITOHEX, "curr: ", curr_thread->id, 0); // uart_puts(" | "); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index e957606f0..ec014a565 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -16,6 +16,9 @@ extern Thread *thread_pool; extern Thread *run_thread_head; +extern File **global_fd_table; +extern char *global_dir; +extern Dentry *global_dentry; /* * Return value is x0 @@ -122,7 +125,11 @@ int kernel_exec(char *name){ new_thread->code_size = fileInfo.datasize; print_string(UITOHEX, "[*] kernel_exec: new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); - // set_period_timer_irq(); + /* copy the golbal dir / dentry in the new_thread*/ + strcpy(new_thread->dir, global_dir); + new_thread->dentry = global_dentry; + + set_period_timer_irq(); sched_timeout("omg"); enable_irq(); asm volatile( @@ -189,6 +196,10 @@ int do_fork(TrapFrame *trapFrame){ } } + /* copy the golbal dir / dentry in the new_thread*/ + strcpy(new_thread->dir, global_dir); + new_thread->dentry = global_dentry; + // print_string(UITOHEX, "(child)new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); @@ -330,14 +341,14 @@ void sys_open(TrapFrame *trapFrame){ disable_irq(); char *path = (char *)trapFrame->x[0]; int flags = trapFrame->x[1]; - Thread *curr_thread = get_current(); + File *file = NULL; int status = vfs_open(path, flags, &file); unsigned int fd_idx = 0; if(status == 0){ for(;fd_idx < MAX_FD_NUM; fd_idx++){ - if(curr_thread->fd_table[fd_idx] == NULL){ - curr_thread->fd_table[fd_idx] = file; + if(global_fd_table[fd_idx] == NULL){ + global_fd_table[fd_idx] = file; trapFrame->x[0] = fd_idx; return; } @@ -355,14 +366,13 @@ void sys_close(TrapFrame *trapFrame){ disable_irq(); int fd = trapFrame->x[0]; - Thread *curr_thread = get_current(); if(fd < 0 || fd >= MAX_FD_NUM) trapFrame->x[0] = -1; - else if(curr_thread->fd_table[fd] == NULL) + else if(global_fd_table[fd] == NULL) trapFrame->x[0] = -1; else{ - int status = vfs_close(curr_thread->fd_table[fd]); - if(status == 0) curr_thread->fd_table[fd] = NULL; + int status = vfs_close(global_fd_table[fd]); + if(status == 0) global_fd_table[fd] = NULL; trapFrame->x[0] = status; } enable_irq(); @@ -374,13 +384,12 @@ void sys_write(TrapFrame *trapFrame){ char *buf = (char *)trapFrame->x[1]; int count = trapFrame->x[2]; - Thread *curr_thread = get_current(); if(fd < 0 || fd >= MAX_FD_NUM) trapFrame->x[0] = -1; - else if(curr_thread->fd_table[fd] == NULL) + else if(global_fd_table[fd] == NULL) trapFrame->x[0] = -1; else{ - int status = vfs_write(curr_thread->fd_table[fd], buf, count); + int status = vfs_write(global_fd_table[fd], buf, count); trapFrame->x[0] = status; } enable_irq(); @@ -392,13 +401,12 @@ void sys_read(TrapFrame *trapFrame){ char *buf = (char *)trapFrame->x[1]; int count = trapFrame->x[2]; - Thread *curr_thread = get_current(); if(fd < 0 || fd >= MAX_FD_NUM) trapFrame->x[0] = -1; - else if(curr_thread->fd_table[fd] == NULL) + else if(global_fd_table[fd] == NULL) trapFrame->x[0] = -1; else{ - int status = vfs_read(curr_thread->fd_table[fd], buf, count); + int status = vfs_read(global_fd_table[fd], buf, count); trapFrame->x[0] = status; } enable_irq(); diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index c64c0f865..f20728e21 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -6,6 +6,7 @@ char *global_dir; Dentry *global_dentry; +File **global_fd_table; Mount *rootfs; FileSystem **fs_pool; @@ -33,6 +34,7 @@ void rootfs_init(char *fs_name){ global_dir = (char *)kmalloc(sizeof(char) * 1024); strcpy(global_dir, "/"); global_dentry = rootfs->root_dentry; + global_fd_table = (File **)kmalloc(sizeof(File *) * MAX_FD_NUM); } From f1cea0339a2e88ca4e1bb4c11f4d9083460ce7bb Mon Sep 17 00:00:00 2001 From: Fan Date: Sun, 29 May 2022 02:10:22 +0800 Subject: [PATCH 44/62] cpio.c: add the cpio file_info list --- .gitignore | 1 + kernel/Makefile | 3 ++ kernel/include/cpio.h | 3 +- kernel/include/vfs.h | 1 + kernel/initramfs.cpio | Bin 247296 -> 408576 bytes kernel/initramfs/flag/fakeflag.txt | 1 + kernel/initramfs/flag/fffflag/flag | 1 + kernel/src/cpio.c | 65 +++++++++++++++++++++-------- kernel/src/main.c | 1 + kernel/src/sched.c | 17 ++++---- kernel/src/vfs.c | 8 ++++ 11 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 kernel/initramfs/flag/fakeflag.txt create mode 100644 kernel/initramfs/flag/fffflag/flag diff --git a/.gitignore b/.gitignore index 66b03fa51..81e183344 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.cpio *.elf *.img src/*.elf diff --git a/kernel/Makefile b/kernel/Makefile index 97317b0b3..da217bbf0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -44,6 +44,9 @@ run-display: pty: qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -s -serial null -serial pty -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb +cpio: + cd initramfs;\find . | cpio -o -H newc > ../initramfs.cpio; + user: cd ../user;\ make;\ diff --git a/kernel/include/cpio.h b/kernel/include/cpio.h index a20094107..e6a2c4ea4 100644 --- a/kernel/include/cpio.h +++ b/kernel/include/cpio.h @@ -10,6 +10,7 @@ unsigned long long CPIO_BASE_END; #define CPIO_HEADER_MAGIC "070701" #define CPIO_FOOTER_MAGIC "TRAILER!!!" #define CPIO_ALIGNMENT 4 +#define MAX_CPIO_FILE_NUM 1024 typedef struct _cpio_newc_header{ @@ -38,7 +39,7 @@ typedef struct _file_info{ unsigned int datasize; }file_info; -void testprint(); +void init_cpio_file_info(); unsigned int padding(unsigned int); void ls(); void cat(char [MAX_SIZE]); diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 8410236bc..694c08a0f 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -78,6 +78,7 @@ struct vnode_operations { void rootfs_init(); +void vfs_initramfs_init(); int register_filesystem(FileSystem* fs); int vfs_open(const char* pathname, int flags, struct file** target); int vfs_lookup(const char* pathname, Dentry **target_dentry, VNode **target_vnode, char *component_name); diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index 0676fb1584617bc4d73624b3b212a12133e97d8e..bb8c8f03c2f22438c4a1bbb54ac3757ab0da8d2a 100644 GIT binary patch literal 408576 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 literal 247296 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 #include +file_info **cpio_file_info_list; +// TODO: make functions change faster, beacuse has the cpio_file_info_list /* The pathname is followed by NUL bytes so that the total size of the fixed header plus pathname is a multiple of four. Likewise, the file data is @@ -19,41 +21,68 @@ unsigned int padding(unsigned int size){ } void parse_cpio_header(cpio_newc_header *header, file_info *info){ - if(strncmp(header->c_magic, "070701", 6) != 0) return; + if(strncmp(header->c_magic, CPIO_HEADER_MAGIC, 6) != 0) return; info->filename = (char *)(header + 1); info->filename_size = hextoui(header->c_namesize, 8); info->datasize = hextoui(header->c_filesize, 8); info->data = (char *)header + padding(sizeof(cpio_newc_header)+info->filename_size); } -void ls() { +void init_cpio_file_info(){ cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + cpio_file_info_list = (file_info **)kmalloc(sizeof(file_info *) * MAX_CPIO_FILE_NUM); + int i = 0; while(1){ - file_info info; - parse_cpio_header(header, &info); - if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; - uart_nbyte(info.filename, info.filename_size); - uart_puts("\n"); - header = (cpio_newc_header *)(info.data + padding(info.datasize)); + file_info *info = (file_info *)kmalloc(sizeof(file_info)); + parse_cpio_header(header, info); + if(strncmp(info->filename, CPIO_FOOTER_MAGIC, 10) == 0) break; + header = (cpio_newc_header *)(info->data + padding(info->datasize)); + cpio_file_info_list[i++] = info; } } +void ls() { + for(int i = 0; cpio_file_info_list[i] != NULL; i++){ + uart_puts(cpio_file_info_list[i]->filename); + print_string(UITOA, " | datasize = ", cpio_file_info_list[i]->datasize, 1); + // uart_puts("\n"); + } + // cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + // while(1){ + // file_info info; + // parse_cpio_header(header, &info); + // if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + // uart_nbyte(info.filename, info.filename_size); + // print_string(UITOA, " data_size: ", info.datasize, 1); + // uart_puts("\n"); + // header = (cpio_newc_header *)(info.data + padding(info.datasize)); + // } +} -void cat(char *thefilename) { - cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; - while(1){ - file_info info; - parse_cpio_header(header, &info); - if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; - else if(strncmp(info.filename, thefilename, info.filename_size) == 0){ - uart_nbyte(info.data, info.datasize); +void cat(char *thefilename) { + for(int i = 0; cpio_file_info_list[i] != NULL; i++){ + file_info *info = cpio_file_info_list[i]; + if(strncmp(info->filename, thefilename, info->filename_size) == 0){ + uart_nbyte(info->data, info->datasize); uart_puts("\n"); return; } - - header = (cpio_newc_header *)(info.data + padding(info.datasize)); } + // cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; + // while(1){ + // file_info info; + // parse_cpio_header(header, &info); + + // if(strncmp(info.filename, "TRAILER!!!", 10) == 0) break; + // else if(strncmp(info.filename, thefilename, info.filename_size) == 0){ + // uart_nbyte(info.data, info.datasize); + // uart_puts("\n"); + // return; + // } + + // header = (cpio_newc_header *)(info.data + padding(info.datasize)); + // } } diff --git a/kernel/src/main.c b/kernel/src/main.c index 8f305c9f6..78c482697 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -23,6 +23,7 @@ int main(unsigned long dtb_base){ print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); + init_cpio_file_info(); rootfs_init("tmpfs"); init_thread_pool_and_head(); init_task_head(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index f71de742f..8dd28a619 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -107,7 +107,6 @@ void kill_zombie(){ // kfree(tmp->code_addr); // } - /* init signal */ for(unsigned int i = 0; i < MAX_SIG_HANDLER; i++){ tmp->sig_info_pool[i].ready = 0; @@ -137,18 +136,20 @@ void schedule(){ next_thread = (Thread *)next_thread->list.next; }while(list_is_head(&next_thread->list, &run_thread_head->list) || next_thread->state == EXIT); + strcpy(curr_thread->dir, global_dir); + curr_thread->dentry = global_dentry; strcpy(global_dir, next_thread->dir); global_dentry = next_thread->dentry; global_fd_table = next_thread->fd_table; - // print_string(UITOA, "[*] next_thread->id: ", next_thread->id, 0); - // uart_puts(" | "); - // uart_puts(global_dir); - // uart_puts(" | "); - // uart_puts(global_dentry->name); - // uart_puts(" | "); - // print_string(UITOHEX, "global_fd_table: ", (unsigned long long )global_fd_table, 1); + print_string(UITOA, "[*] next_thread->id: ", next_thread->id, 0); + uart_puts(" | "); + uart_puts(global_dir); + uart_puts(" | "); + uart_puts(global_dentry->name); + uart_puts(" | "); + print_string(UITOHEX, "global_fd_table: ", (unsigned long long )global_fd_table, 1); // print_string(UITOHEX, "curr: ", curr_thread->id, 0); // uart_puts(" | "); diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index f20728e21..292d66203 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -3,6 +3,7 @@ #include #include #include +#include char *global_dir; Dentry *global_dentry; @@ -10,6 +11,8 @@ File **global_fd_table; Mount *rootfs; FileSystem **fs_pool; +extern file_info **cpio_file_info_list; + void rootfs_init(char *fs_name){ fs_pool = (FileSystem **)kmalloc(sizeof(FileSystem *) * MAX_FS_NUM); for(unsigned int idx = 0; idx < MAX_FS_NUM; idx++){ @@ -35,8 +38,13 @@ void rootfs_init(char *fs_name){ strcpy(global_dir, "/"); global_dentry = rootfs->root_dentry; global_fd_table = (File **)kmalloc(sizeof(File *) * MAX_FD_NUM); + + vfs_initramfs_init(); } +void vfs_initramfs_init(){ + +} int register_filesystem(FileSystem *fs) { // register the file system to the kernel. From bed8d9020772ce257497bf2d895ed65f1e19f290 Mon Sep 17 00:00:00 2001 From: Fan Date: Sun, 29 May 2022 04:24:55 +0800 Subject: [PATCH 45/62] what the bug== --- debug.gdb | 2 +- kernel/include/string.h | 2 +- kernel/include/tmpfs.h | 2 +- kernel/include/vfs.h | 2 +- kernel/initramfs.cpio | Bin 408576 -> 408576 bytes kernel/src/main.c | 4 ++-- kernel/src/sched.c | 14 +++++++------- kernel/src/string.c | 4 ++-- kernel/src/tmpfs.c | 6 ++++-- kernel/src/vfs.c | 19 ++++++++++++++++++- 10 files changed, 37 insertions(+), 18 deletions(-) diff --git a/debug.gdb b/debug.gdb index ef84b4fac..a7f3b91a9 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *vfs_lookup +b *vfs_chdir target remote :1234 \ No newline at end of file diff --git a/kernel/include/string.h b/kernel/include/string.h index 7a16d1cff..498c9b7de 100644 --- a/kernel/include/string.h +++ b/kernel/include/string.h @@ -40,7 +40,7 @@ unsigned int atoui(const char *); /* hex string to unsigned int */ unsigned int hextoui(const char *, unsigned int); -void memcpy(char *, const char *, unsigned int); +void memcpy(char *, const char *, unsigned long); void memset(char *, const char, unsigned int); void strcpy(char *, const char *); void strcat(char *, const char *); diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 1b3219035..7c05cb5ad 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -12,7 +12,7 @@ typedef struct tmpfs_inode{ */ unsigned long idx; unsigned long size; - char *data; // data block size = MAX_DATA_LEN(512) + char *data; // data block size = MAX_DATA_LEN(0x1000) struct vnode* vnode; }TmpfsInode; diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 694c08a0f..74a762f1d 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -11,7 +11,7 @@ enum dentry_type { }; #define O_CREAT 0b100 #define MAX_PATHNAME_LEN 64 -#define MAX_DATA_LEN 512 +#define MAX_DATA_LEN 0x2000 #define MAX_FS_NUM 32 #define MAX_FD_NUM 16 #define EOF (-1) diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index bb8c8f03c2f22438c4a1bbb54ac3757ab0da8d2a..d10b40b05aaee44d69f8b114ce37c1c5496122f5 100644 GIT binary patch delta 35 qcmZqpA<^(dVuBHixvQgdqgksNW2+fcs~K~v8B41f>sB+iQ`P|8mkUY& delta 35 qcmZqpA<^(dVuBHisi~<^qgksNW2+fcs~K~v8B41f>sB+iQ`P|4EDIX| diff --git a/kernel/src/main.c b/kernel/src/main.c index 78c482697..b99ba47ee 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -19,7 +19,7 @@ int main(unsigned long dtb_base){ uart_init(); enable_el0_get_timer(); - // uart_getc(); + uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); @@ -37,7 +37,7 @@ int main(unsigned long dtb_base){ // fs_test3(); // fs_test4(); // fs_test5(); - fs_test6(); + // fs_test6(); // fs_test7(); PrintWelcome(); diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 8dd28a619..757bf2aae 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -143,13 +143,13 @@ void schedule(){ global_dentry = next_thread->dentry; global_fd_table = next_thread->fd_table; - print_string(UITOA, "[*] next_thread->id: ", next_thread->id, 0); - uart_puts(" | "); - uart_puts(global_dir); - uart_puts(" | "); - uart_puts(global_dentry->name); - uart_puts(" | "); - print_string(UITOHEX, "global_fd_table: ", (unsigned long long )global_fd_table, 1); + // print_string(UITOA, "[*] next_thread->id: ", next_thread->id, 0); + // uart_puts(" | "); + // uart_puts(global_dir); + // uart_puts(" | "); + // uart_puts(global_dentry->name); + // uart_puts(" | "); + // print_string(UITOHEX, "global_fd_table: ", (unsigned long long )global_fd_table, 1); // print_string(UITOHEX, "curr: ", curr_thread->id, 0); // uart_puts(" | "); diff --git a/kernel/src/string.c b/kernel/src/string.c index 49f5c1e42..89343d933 100644 --- a/kernel/src/string.c +++ b/kernel/src/string.c @@ -177,8 +177,8 @@ unsigned int hextoui(const char buf[MAX_SIZE], unsigned int size){ return num; } -void memcpy(char *d, const char *s, unsigned int len){ - for(unsigned int i = 0; i < len; i++){ +void memcpy(char *d, const char *s, unsigned long len){ + for(unsigned long i = 0; i < len; i++){ d[i] = s[i]; } } diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index af940eee2..c93edd678 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -75,15 +75,17 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ while(1){ /* f_pos is in the block of some offset */ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); - if(block->data[offset] == (char)EOF) return -1; - + // if(block->data[offset] == (char)EOF) return -1; if(write_len <= MAX_DATA_LEN - offset){ memcpy(block->data + offset, src + write_idx, write_len); file->f_pos += write_len; write_idx += write_len; block->data[write_idx] = EOF; + // uart_puts(block->data); + block->size += write_len; + // print_string(UITOA, "f_pos: ", file->f_pos, 1); goto DONE; } else if(write_len > MAX_DATA_LEN - offset){ diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 292d66203..0951f13f0 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -43,7 +43,24 @@ void rootfs_init(char *fs_name){ } void vfs_initramfs_init(){ - + vfs_mkdir("./initramfs"); + vfs_mount("/initramfs", "initramfs"); + vfs_chdir("initramfs"); + for(int i = 0; cpio_file_info_list[i] != NULL; i++){ + file_info *info = cpio_file_info_list[i]; + if(info->filename_size == 0) continue; + char name[MAX_PATHNAME_LEN+1]; + memcpy(name, info->filename, info->filename_size); + if(info->datasize == 0){ + vfs_mkdir(name); + } + else{ + File *file = NULL; + vfs_open(name, O_CREAT, &file); + vfs_write(file, info->data, info->datasize); + vfs_close(file); + } + } } int register_filesystem(FileSystem *fs) { From 7eddb2224cd46dcb19cac0732249d45082b5848e Mon Sep 17 00:00:00 2001 From: Fan Date: Sun, 29 May 2022 05:16:05 +0800 Subject: [PATCH 46/62] fix the data_len bug --- kernel/include/tmpfs.h | 3 ++- kernel/include/vfs.h | 2 +- kernel/src/main.c | 2 +- kernel/src/tmpfs.c | 11 ++++------- kernel/src/vfs.c | 8 ++++++++ 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 7c05cb5ad..781487664 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -12,7 +12,8 @@ typedef struct tmpfs_inode{ */ unsigned long idx; unsigned long size; - char *data; // data block size = MAX_DATA_LEN(0x1000) + // char *data; + char data[MAX_DATA_LEN]; // data block size = MAX_DATA_LEN(512 * 8 = 4096) struct vnode* vnode; }TmpfsInode; diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 74a762f1d..694c08a0f 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -11,7 +11,7 @@ enum dentry_type { }; #define O_CREAT 0b100 #define MAX_PATHNAME_LEN 64 -#define MAX_DATA_LEN 0x2000 +#define MAX_DATA_LEN 512 #define MAX_FS_NUM 32 #define MAX_FD_NUM 16 #define EOF (-1) diff --git a/kernel/src/main.c b/kernel/src/main.c index b99ba47ee..290da7674 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -19,7 +19,7 @@ int main(unsigned long dtb_base){ uart_init(); enable_el0_get_timer(); - uart_getc(); + // uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index c93edd678..563e8c9da 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -76,19 +76,16 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ /* f_pos is in the block of some offset */ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); // if(block->data[offset] == (char)EOF) return -1; - if(write_len <= MAX_DATA_LEN - offset){ + if(write_len < MAX_DATA_LEN - offset){ memcpy(block->data + offset, src + write_idx, write_len); file->f_pos += write_len; write_idx += write_len; block->data[write_idx] = EOF; - // uart_puts(block->data); - block->size += write_len; - // print_string(UITOA, "f_pos: ", file->f_pos, 1); goto DONE; } - else if(write_len > MAX_DATA_LEN - offset){ + else if(write_len >= MAX_DATA_LEN - offset){ memcpy(block->data + offset, src + write_idx, MAX_DATA_LEN - offset); file->f_pos += MAX_DATA_LEN - offset; write_idx += MAX_DATA_LEN - offset; @@ -97,7 +94,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ /* add a new block */ TmpfsInode *new_block = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); - new_block->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); + // new_block->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 new_block->idx = block->idx + 1; new_block->size = 0; new_block->vnode = inode_head->vnode; @@ -226,7 +223,7 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp inode_head->vnode = new_dentry->vnode; /* create the real data block */ TmpfsInode *inode = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); - inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); + // inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); inode->idx = 1; inode->size = 0; inode->vnode = new_dentry->vnode; diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 0951f13f0..362417918 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -59,6 +59,14 @@ void vfs_initramfs_init(){ vfs_open(name, O_CREAT, &file); vfs_write(file, info->data, info->datasize); vfs_close(file); + + // char buf[40]; + // memset(buf, 0, 40); + // vfs_open(name, O_CREAT, &file); + // vfs_read(file, buf, 39); + // vfs_close(file); + // uart_puts(buf); + // uart_puts("\n"); } } } From c25a2400f65e8d43bad6908cc6a7bf854981d372 Mon Sep 17 00:00:00 2001 From: Fan Date: Mon, 30 May 2022 21:38:45 +0800 Subject: [PATCH 47/62] tmpfs_write: fixed bug? --- debug.gdb | 2 +- kernel/include/tmpfs.h | 2 +- kernel/include/vfs.h | 3 ++- kernel/initramfs.cpio | Bin 408576 -> 408576 bytes kernel/initramfs/me | 3 +-- kernel/src/tmpfs.c | 42 +++++++++++++++++++++++++++++------------ kernel/src/vfs.c | 23 ++++++++++++++-------- 7 files changed, 50 insertions(+), 25 deletions(-) diff --git a/debug.gdb b/debug.gdb index a7f3b91a9..6ab26a6f4 100755 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,3 @@ file kernel8.elf -b *vfs_chdir +b *tmpfs_read target remote :1234 \ No newline at end of file diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 781487664..df04bdc7d 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -6,6 +6,7 @@ typedef struct tmpfs_inode{ struct list_head list; + struct vnode* vnode; /* * inode head: means the num of blocks * data inode: means the idx @@ -14,7 +15,6 @@ typedef struct tmpfs_inode{ unsigned long size; // char *data; char data[MAX_DATA_LEN]; // data block size = MAX_DATA_LEN(512 * 8 = 4096) - struct vnode* vnode; }TmpfsInode; int tmpfs_setup_mount(FileSystem *fs, Mount *mount); diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 694c08a0f..a0bbfa093 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -11,7 +11,8 @@ enum dentry_type { }; #define O_CREAT 0b100 #define MAX_PATHNAME_LEN 64 -#define MAX_DATA_LEN 512 +#define MAX_DATA_LEN (0x1000 - 0x28) +// #define MAX_DATA_LEN 100 #define MAX_FS_NUM 32 #define MAX_FD_NUM 16 #define EOF (-1) diff --git a/kernel/initramfs.cpio b/kernel/initramfs.cpio index d10b40b05aaee44d69f8b114ce37c1c5496122f5..81d3e48dcc703160807306ce76eb532092fce26b 100644 GIT binary patch delta 2208 zcmaJ?YfKzf6h3!%!9oRESeDm5hKKHBc&xiZyDZ>rv9+~oB0<~6N0$n*N>j^2L{r1G z4VqMKxV=EtKdwd-#b8S}ifgJsVq$-&iA0SWn+!Gak1b9r;SBe5G{krmS$&mZmMV>Cya1xUn(3HOPOlaBSwCBe?{U-i>eANQlJ>s`DeE zrf@8RyG@~3EXY<0nscsN!R`^PU!{|=O&5tR?xp=KuTbAVw1hJg}qQznx}Z_lL!TnJ1kl3d{IbzDhtLldmuFd zV9$6Vy|f2a0nP!mQ2;4bc@!$t`KC5jV0SpAHYxx-r&J@98{kmfirU>4Xk+);8_BIn zYOf&V6EgUR7s56^AUy^f&?*p^fc!RPB;{bRW7S8{`gEH{qir zHcV_kq>dALk*^WON0t3j>Uq8{LYj|d923mQ;Pl(rsWM0YNfTti#_lS=@pMVf2d4iw5Y=LnbmO-6MemaSBbsYE88@Mkc#=M^n@|cEC#ZPbJ!O-Uc z!>15XeRi5m?8I^YstA&rHKQFSOjI|TJA!=l`T8|{9$4q|!Bw9D`m?$xFIqvXmqiX> z8?kQa8N0r?4P%*oZ{)Gjx5eh$CCsf60lj0;-+zWN|Ub7-Mid|p6Mg>1u7vepfs|iSTbUSLShD+4ZUNzha zQ@BIykMiIMI(=!K%!DBW_4Kqcmg;FGN-CIW6&{);D@^e#quUS^rL@Nezw(zyX~!E! zA$<~#f6rt}6w{toUq3yn#~ji8`spw(tev=eyiw$iZDw&n6E_47=>i7-pqa1KQwjN@ z|KZ=S@k>@;?Y+35i8s)D2s>3#;fOcTJNfDp$2$I6&YV$ql?FCf;l4u%(nZ`-9s;@( zJ98QT`8JV)YPW{gcPk#GHzWRQyi3-w5f$6Z{7&bFc!a)>fQzilT|^#WT~50byT-8O z7tEY_jdM4n&g!~nFv>LUE>*|0TUUwD7g6L*Gec-rqqMRmr{I$K#5(mcX2Y*KLarImGk#vff3wVn+@d=krRZ^7 zc|}S?q5zv9Meu9})Lx;;oV!XukNE9w)o!Y^ON7w-^s*AVvyt^>1 z5RUWAkp&I&nh7(BWmx+_w6KRgKHC^1S{U;jw4DPXz%rhXEJAB?P8cHjQY%%8AdTh_ z)0%9ra$SdHfMnc%p6uo$fPum8M-B_VBmF%C1A=$h>u(l}^~W9`)F&FQr@wz~p4-_o xFeq$4jI{+gF)BVgDiVAw_*n6=;gdBxDrP713lo8CRdQJWmjko6H)l7Ie*ki6ox}hD delta 2219 zcmZuyU2IfE6h5@m zUTX{*OzbcXm}p|F5@{m{Y$9$nviM*K2?VR)i^f}c@WBwl2SZI)JTv#+<)(I%n>#b- z`_A{BGjnIP2Zh>$!pVlT#^95Yc78p3Ba4Rl^I2I+q`B$**+ZGDsPW6BvC^zlPDjI5 zUYAvoy)D$z5~;7R-_#gsXk|}$xO;8xsv8RAl~U#A^;l@_72=$(WZQXKPL1n?0#o`% zH4aX2=^G*b0*~YrkauHybG*ctsAR4*jBkOszD13f0vx^y&HQpoEYhY1~cjK5~p%!O5Ka*dO z-DN}U;O7e(#*wKw=6?D2;Yrb|8LL&@)ojoKQ%-<9nL(>&{WzE7>1y^5j#Jse!=4gq zmP6`c#X<)i?z0?rd!$33P2Vjzk6RXzv_@L=*dS~95Bc@uf{PwMUBP2;na8G-M-Tf> z#=c8>04 zGs`#)g!&{&CNoR;7(sF$yM64YNMLP&=3`8jGb_@V=;M5LePCz8$2zc%+wEYqP{0!!#x5b8qr_73|0nakWfI4%HfnxWggH`j zx}T|1ZjVha!tWO0p{iyB%hDXM(x^4;WgMptFAd;#uXQwF5~Fbeb|2$9cB32*5}t`U zai=t6JoQOJEmLu=64%5XmOS$P>4egCD|zJ7Y*GO>E)%J>>=_~Y+8Z5JSf^Y|)UqA8 zW{@tMq|YTu#-WbQ3-MlH!^pXfZ?R5jo$S}l@_wmxtQqmQ@JoJ#4W>)*34X1(c&8d< zUm@a(9ias!;B7P8WxN-#tMax1>?+4De`Ci_8~8Ea;;yJYkE}BoU8sl{7h598-baPg zb_TRcg+Bh4o9>}vqRjodWNbDe&?ySU%ZFuPGTlSsx+fApin5cK8 z+|iPkZMNk{C?5(d7R?=Goy>#YncpW1H+(2%rVndB!* zYD&})+lmPN19cIm*WhNTAv_VrKZX|Kzm=3^Xd%{wkam8*IM<;hc~PmPq17q zqF(1Y(As#A_Bn3=c=;#vTL*+=y=AYG)iliXTv!xs11%i;z1;m+EW>|+jAxGa42=Zn z>w}|oxNmUu;C?zBjnd(f{jUrJ#A|RAqxg;v4Ix>8jbJ9u03?ojP3FFXgT4K}06jDm z9Ui99fsyD?8idx * MAX_DATA_LEN - file->f_pos); + // if(block->data[offset] == (char)EOF) return -1; if(write_len < MAX_DATA_LEN - offset){ memcpy(block->data + offset, src + write_idx, write_len); @@ -86,7 +87,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ goto DONE; } else if(write_len >= MAX_DATA_LEN - offset){ - memcpy(block->data + offset, src + write_idx, MAX_DATA_LEN - offset); + memcpy(block->data + offset, src + write_idx, MAX_DATA_LEN); file->f_pos += MAX_DATA_LEN - offset; write_idx += MAX_DATA_LEN - offset; write_len -= MAX_DATA_LEN - offset; @@ -94,12 +95,16 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ /* add a new block */ TmpfsInode *new_block = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); + INIT_LIST_HEAD(&new_block->list); // new_block->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 new_block->idx = block->idx + 1; new_block->size = 0; new_block->vnode = inode_head->vnode; list_add_tail(&new_block->list, &inode_head->list); + // uart_puts(block->vnode->dentry->name); + // uart_puts("\n"); + uart_puts("[*] Tmpfs Write: Create new block\n"); /* next round will write the data into the new block */ block = new_block; @@ -110,6 +115,8 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ DONE: return write_idx; } + +// TODO: read fail in user program because of EOF byte int tmpfs_read(struct file* file, void* buf, size_t len){ TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; char *dest = (char *)buf; @@ -126,24 +133,31 @@ int tmpfs_read(struct file* file, void* buf, size_t len){ } /* f_pos is in the block of the offset */ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); + // print_string(UITOA, "offset: ", offset, 1); if(block->data[offset] == (char)EOF) return -1; + if(block->size <= offset) return 0; + size_t quota = block->size - offset; - if(read_len <= block->size - offset){ - /* len < block size, read len is ok */ - for(size_t i = 0; i < read_len && block->data[offset + i] != (char)EOF; i++){ + if(read_len <= quota){ + /* len < quota, read len is ok */ + size_t i = 0; + for(i = 0; i < read_len && block->data[offset + i] != (char)EOF; i++){ dest[read_idx + i] = block->data[offset + i]; } - file->f_pos += read_len; + read_idx += i; + file->f_pos += i; goto DONE; } - else if(read_len > block->size - offset){ - /* len >= block size, read MAX_DATA_LEN is ok */ - for(size_t i = 0; (i < (block->size - offset)) && (block->data[offset + i] != (char)EOF); i++){ + else if(read_len > quota){ + /* len > quota, read quota*/ + size_t i = 0; + for(i = 0; (i < quota) && (block->data[offset + i] != (char)EOF); i++){ dest[read_idx + i] = block->data[offset + i]; } - file->f_pos += block->size - offset; - read_idx += block->size - offset; - read_len -= block->size - offset; + file->f_pos += i; + read_idx += i; + read_len -= i; + } } @@ -218,16 +232,20 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp Dentry *new_dentry = tmpfs_create_dentry(component_name, dir_node->dentry, D_FILE, dir_node->dentry->mount); /* create the inode list head */ TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); + INIT_LIST_HEAD(&inode_head->list); + // inode_head->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 inode_head->idx = 0; inode_head->size = 0; inode_head->vnode = new_dentry->vnode; /* create the real data block */ TmpfsInode *inode = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); - // inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); + INIT_LIST_HEAD(&inode->list); + // inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 inode->idx = 1; inode->size = 0; inode->vnode = new_dentry->vnode; + new_dentry->vnode->internal = inode_head; /* add the new data block to inode_head */ diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 362417918..2710dba17 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -59,14 +59,21 @@ void vfs_initramfs_init(){ vfs_open(name, O_CREAT, &file); vfs_write(file, info->data, info->datasize); vfs_close(file); - - // char buf[40]; - // memset(buf, 0, 40); - // vfs_open(name, O_CREAT, &file); - // vfs_read(file, buf, 39); - // vfs_close(file); - // uart_puts(buf); - // uart_puts("\n"); + + file = NULL; + vfs_open(name, 0, &file); + char buf[500]; + while(1){ + memset(buf, 0, 500); + int read_size = vfs_read(file, buf, 499); + // print_string(ITOA, "read_size: ", read_size, 1); + if(read_size <= 0){ + break; + } + uart_puts(buf); + } + // vfs_write(file, info->data, info->datasize); + vfs_close(file); } } } From b492505899a73b7c5de59e5b57a49878c493487e Mon Sep 17 00:00:00 2001 From: Fan Date: Mon, 30 May 2022 22:33:07 +0800 Subject: [PATCH 48/62] tmpfs_read: fixed bug? --- kernel/src/tmpfs.c | 42 +++++++++++++++++++----------------------- kernel/src/vfs.c | 2 ++ 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index f2801723a..44be26c58 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -75,22 +75,23 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ while(1){ /* f_pos is in the block of some offset */ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); + size_t quota = MAX_DATA_LEN - offset; // if(block->data[offset] == (char)EOF) return -1; - if(write_len < MAX_DATA_LEN - offset){ + if(write_len <= quota){ memcpy(block->data + offset, src + write_idx, write_len); file->f_pos += write_len; write_idx += write_len; - block->data[write_idx] = EOF; + // block->data[write_idx] = EOF; block->size += write_len; goto DONE; } - else if(write_len >= MAX_DATA_LEN - offset){ - memcpy(block->data + offset, src + write_idx, MAX_DATA_LEN); - file->f_pos += MAX_DATA_LEN - offset; - write_idx += MAX_DATA_LEN - offset; - write_len -= MAX_DATA_LEN - offset; + else if(write_len > quota){ + memcpy(block->data + offset, src + write_idx, quota); + file->f_pos += quota; + write_idx += quota; + write_len -= quota; block->size = MAX_DATA_LEN; /* add a new block */ @@ -134,30 +135,25 @@ int tmpfs_read(struct file* file, void* buf, size_t len){ /* f_pos is in the block of the offset */ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); // print_string(UITOA, "offset: ", offset, 1); - if(block->data[offset] == (char)EOF) return -1; + // if(block->data[offset] == (char)EOF) return -1; // no need EOF? + + /* if the offset is the end, it means end of the file */ if(block->size <= offset) return 0; size_t quota = block->size - offset; if(read_len <= quota){ /* len < quota, read len is ok */ - size_t i = 0; - for(i = 0; i < read_len && block->data[offset + i] != (char)EOF; i++){ - dest[read_idx + i] = block->data[offset + i]; - } - read_idx += i; - file->f_pos += i; + memcpy(dest + read_idx, block->data + offset, read_len); + read_idx += read_len; + file->f_pos += read_len; goto DONE; } else if(read_len > quota){ - /* len > quota, read quota*/ - size_t i = 0; - for(i = 0; (i < quota) && (block->data[offset + i] != (char)EOF); i++){ - dest[read_idx + i] = block->data[offset + i]; - } - file->f_pos += i; - read_idx += i; - read_len -= i; - + /* len >= quota, read quota*/ + memcpy(dest + read_idx, block->data + offset, quota); + file->f_pos += quota; + read_idx += quota; + read_len -= quota; } } diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 2710dba17..e85db0864 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -62,6 +62,7 @@ void vfs_initramfs_init(){ file = NULL; vfs_open(name, 0, &file); + size_t thesize = 0; char buf[500]; while(1){ memset(buf, 0, 500); @@ -70,6 +71,7 @@ void vfs_initramfs_init(){ if(read_size <= 0){ break; } + thesize += read_size; uart_puts(buf); } // vfs_write(file, info->data, info->datasize); From 1edbe0b2aa083c25b8dc6c102a39be063ad99eae Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 31 May 2022 00:20:57 +0800 Subject: [PATCH 49/62] read only fs --- kernel/include/vfs.h | 2 + kernel/src/vfs.c | 99 +++++++++++++++++++++++++++++++++----------- 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index a0bbfa093..98e0769d3 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -15,6 +15,7 @@ enum dentry_type { // #define MAX_DATA_LEN 100 #define MAX_FS_NUM 32 #define MAX_FD_NUM 16 +#define READ_ONLY 1 #define EOF (-1) // file handle @@ -53,6 +54,7 @@ typedef struct mount { typedef struct filesystem { char* name; + int read_only; struct mount* mount; int (*setup_mount)(struct filesystem* fs, struct mount* mount); }FileSystem; diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index e85db0864..e7520233a 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -17,6 +17,8 @@ void rootfs_init(char *fs_name){ fs_pool = (FileSystem **)kmalloc(sizeof(FileSystem *) * MAX_FS_NUM); for(unsigned int idx = 0; idx < MAX_FS_NUM; idx++){ FileSystem *init_fs = (FileSystem *)kmalloc(sizeof(FileSystem)); + init_fs->mount = NULL; + init_fs->read_only = 0; init_fs->name = NULL; init_fs->setup_mount = NULL; fs_pool[idx] = init_fs; @@ -60,24 +62,27 @@ void vfs_initramfs_init(){ vfs_write(file, info->data, info->datasize); vfs_close(file); - file = NULL; - vfs_open(name, 0, &file); - size_t thesize = 0; - char buf[500]; - while(1){ - memset(buf, 0, 500); - int read_size = vfs_read(file, buf, 499); - // print_string(ITOA, "read_size: ", read_size, 1); - if(read_size <= 0){ - break; - } - thesize += read_size; - uart_puts(buf); - } - // vfs_write(file, info->data, info->datasize); - vfs_close(file); + // file = NULL; + // vfs_open(name, 0, &file); + // size_t thesize = 0; + // char buf[500]; + // while(1){ + // memset(buf, 0, 500); + // int read_size = vfs_read(file, buf, 499); + // // print_string(ITOA, "read_size: ", read_size, 1); + // if(read_size <= 0){ + // break; + // } + // thesize += read_size; + // uart_puts(buf); + // } + // // vfs_write(file, info->data, info->datasize); + // vfs_close(file); } } + vfs_chdir("/initramfs"); + /* set the filesystem to read only */ + global_dentry->mount->fs->read_only = 1; } int register_filesystem(FileSystem *fs) { @@ -179,8 +184,12 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ // uart_puts(target_vnode->dentry->name); + // cannot open a directory if(target_vnode->dentry->type == D_DIR || - target_vnode->dentry->type == D_MOUNT) return -2; // cannot open a directory + target_vnode->dentry->type == D_MOUNT){ + uart_puts("[*] Open: Cannot open a directory\n"); + return -2; + } err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); if(err == -1) return err; return 0; @@ -189,6 +198,7 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { // // lookup error code shows if file exist or not or other error occurs else{ if(flags & O_CREAT){ + if(target_path->mount->fs->read_only) return -3; // cannot create a file in read only filesystem err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); if(err) return err; err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); @@ -206,13 +216,24 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { int vfs_mkdir(const char *pathname){ if(pathname == NULL) return -1; + Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); if(err) return -1; // worng pathname - if(target_vnode != NULL) return -2; // folder already exist + // folder already exist + if(target_vnode != NULL){ + uart_puts("[*] Mkdir: folder already exist\n"); + return -2; + } + + // cannot create a file in read only filesystem + if(target_path->mount->fs->read_only){ + uart_puts("[*] Mkdir: cannot create a folder in read-only filesystem\n"); + return -3; + } err = rootfs->root_dentry->vnode->v_ops->mkdir(target_path->vnode, &target_vnode, component_name); if(err) return -1; @@ -308,10 +329,16 @@ int vfs_chdir(const char *pathname){ } /* file/folder not exist */ - if(target_vnode == NULL) return -2; + if(target_vnode == NULL){ + uart_puts("[*] Chdir: No such file or directory\n"); + return -2; + } /* cannot change to a file */ - if(target_vnode->dentry->type == D_FILE) return -3; + if(target_vnode->dentry->type == D_FILE){ + uart_puts("[*] Chdir: Cannot change to a file\n"); + return -3; + } /* change the global_dentry and global_dir */ @@ -333,10 +360,22 @@ int vfs_mount(const char *pathname, const char *filesystem){ // uart_puts(target_vnode->dentry->name); /* target vnode isn't exist, cannot mount it */ - if(target_vnode == NULL) return -2; + if(target_vnode == NULL){ + uart_puts("[*] Mount: No such file or directory\n"); + return -2; + } /* target is not a directory, cannot mount it */ - if(target_vnode->dentry->type != D_DIR) return -3; + if(target_vnode->dentry->type != D_DIR){ + uart_puts("[*] Mount: Target is not a directory, connot mount it\n"); + return -3; + } + + /* cannot mount in read only filesystem */ + if(target_path->mount->fs->read_only){ + uart_puts("[*] Mount: Cannot mount in a read-only filesystem\n"); + return -3; + } /* target vnode is exist, can mount it */ FileSystem *target_fs = NULL; @@ -354,12 +393,14 @@ int vfs_mount(const char *pathname, const char *filesystem){ /* cannot find the filesystem , use the empty fs and register it*/ fs_pool[idx]->name = (char *)kmalloc(sizeof(char) * (strlen(filesystem)+1)); strcpy(fs_pool[idx]->name, filesystem); + fs_pool[idx]->read_only = 0; fs_pool[idx]->setup_mount = tmpfs_setup_mount; target_fs = fs_pool[idx]; err = register_filesystem(target_fs); if(err) uart_puts("[x] Failed to register another filesystem\n"); new_mount = (Mount *)kmalloc(sizeof(Mount)); target_fs->setup_mount(target_fs, new_mount); + /* mount the filesystem */ MOUNT_FS:; @@ -389,9 +430,15 @@ int vfs_umount(const char *pathname){ if(err) return -1; // worng pathname /* target vnode isn't exist, cannot umount it */ - if(target_vnode == NULL) return -2; + if(target_vnode == NULL){ + uart_puts("[*] Umount: No such file or directory\n"); + return -2; + } /* target isn't a mount point */ - if(target_vnode->dentry->type != D_MOUNT) return -3; + if(target_vnode->dentry->type != D_MOUNT){ + uart_puts("[*] Umount: Target is not a mount point, connot umount it\n"); + return -3; + } /* umount the filesystem */ target_vnode->dentry->mount->root_dentry->mount_point_dentry = NULL; @@ -423,6 +470,10 @@ int vfs_write(struct file* file, const void* buf, size_t len) { // 1. write len byte from buf to the opened file. // 2. return written size or error code if an error occurs. if(file == NULL) return -1; + if(file->vnode->dentry->mount->fs->read_only == 1){ + uart_puts("[*] Write: Cannot write to a read-only filesystem\n"); + return -2; + } return file->f_ops->write(file, buf, len); } From ed21be565508fbd0b6017a96dbb0416c6717381f Mon Sep 17 00:00:00 2001 From: Fan Date: Tue, 31 May 2022 23:10:28 +0800 Subject: [PATCH 50/62] user program: basic1234 ok --- kernel/include/test_fs.h | 1 + kernel/include/vfs.h | 2 +- kernel/src/main.c | 5 ++-- kernel/src/test_fs.c | 26 +++++++++++++++++++ kernel/src/vfs.c | 55 ++++++++++++++++++++++++++++++++++------ 5 files changed, 78 insertions(+), 11 deletions(-) diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h index 053226473..fd8a23310 100644 --- a/kernel/include/test_fs.h +++ b/kernel/include/test_fs.h @@ -8,5 +8,6 @@ void fs_test4(); void fs_test5(); void fs_test6(); void fs_test7(); +void user_basic1(); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 98e0769d3..99ae409c4 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -9,7 +9,7 @@ enum dentry_type { D_FILE, D_MOUNT }; -#define O_CREAT 0b100 +#define O_CREAT 00000100 #define MAX_PATHNAME_LEN 64 #define MAX_DATA_LEN (0x1000 - 0x28) // #define MAX_DATA_LEN 100 diff --git a/kernel/src/main.c b/kernel/src/main.c index 290da7674..26ef75fb9 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -24,14 +24,14 @@ int main(unsigned long dtb_base){ fdt_traverse((fdt_header *)dtb_base, initramfs_callback); all_allocator_init(); init_cpio_file_info(); - rootfs_init("tmpfs"); + rootfs_init("rootfs"); init_thread_pool_and_head(); init_task_head(); enable_timer_irq(); enable_irq(); // DAIF set to 0b0000 - + // kernel_exec("vfs1.img"); // fs_test1(); // fs_test2(); // fs_test3(); @@ -39,6 +39,7 @@ int main(unsigned long dtb_base){ // fs_test5(); // fs_test6(); // fs_test7(); + // user_basic1(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index 6bcd4a657..a1698480c 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -205,4 +205,30 @@ void fs_test7(){ fd = open("/proc/999/status", 0); // choose a non-existed task's id here if(fd < 0) uart_puts("[x] fd fail!"); // assert(fd < 0); +} + +void user_basic1(){ + mkdir("/tmp", 0); + int fd = open("/tmp/tmpfile", O_CREAT); + write(fd, "Hello World!", 12); + close(fd); + fd = open("/tmp/tmpfile", 0); + char buf[100]; + int sz = read(fd, buf, 100); + buf[sz] = '\0'; + uart_puts(buf); + uart_puts("\n"); + close(fd); + + mount(NULL, "/tmp", "tmpfs", 0, NULL); + fd = open("/tmp/tmpfile", O_CREAT); + write(fd, "Hello World!", 12); + close(fd); + fd = open("/tmp/tmpfile", 0); + sz = read(fd, buf, 100); + buf[sz] = '\0'; + uart_puts(buf); + uart_puts("\n"); + close(fd); + } \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index e7520233a..4b2b2060c 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -24,9 +24,9 @@ void rootfs_init(char *fs_name){ fs_pool[idx] = init_fs; } - if(strcmp(fs_name, "tmpfs") == 0){ + if(strcmp(fs_name, "rootfs") == 0){ fs_pool[0]->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" - strcpy(fs_pool[0]->name, "tmpfs"); + strcpy(fs_pool[0]->name, "rootfs"); fs_pool[0]->setup_mount = tmpfs_setup_mount; } @@ -175,15 +175,22 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, } int vfs_open(const char* pathname, int flags, struct file** target_file) { + uart_puts("[*] vfs_open: "); + uart_puts((char *)pathname); + print_string(ITOA, " | flag: ", flags, 1); + if(pathname == NULL) return -1; Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); - if(err) return -1; // worng pathname + if(err){ + uart_puts("[*] Open: cannot find the pathname\n"); + return -1; // worng pathname + } // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ - // uart_puts(target_vnode->dentry->name); + uart_puts(target_vnode->dentry->name); // cannot open a directory if(target_vnode->dentry->type == D_DIR || target_vnode->dentry->type == D_MOUNT){ @@ -191,30 +198,46 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { return -2; } err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); - if(err == -1) return err; + if(err == -1){ + uart_puts("[*] Open: Cannot open a file\n"); + return -1; + } return 0; } // // 3. Create a new file if O_CREAT is specified in flags and vnode not found // // lookup error code shows if file exist or not or other error occurs else{ if(flags & O_CREAT){ - if(target_path->mount->fs->read_only) return -3; // cannot create a file in read only filesystem + if(target_path->mount->fs->read_only){ + uart_puts("[*] Open: Cannot create a file in read only filesystem\n"); + return -3; + } err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); - if(err) return err; + if(err){ + uart_puts("[*] Open: Cannot create a file\n"); + return err; + } err = rootfs->root_dentry->vnode->f_ops->open(target_vnode, target_file); - if(err) return err; + if(err){ + uart_puts("[*] Open: Cannot open a file\n"); + return err; + } uart_puts("[*] Created file: "); uart_puts((*target_file)->vnode->dentry->name); uart_puts("\n"); return 0; } } + uart_puts("[*] Open: Cannot open a file\n"); // 4. Return error code if fails return -1; } int vfs_mkdir(const char *pathname){ + uart_puts("[*] vfs_mkdir: "); + uart_puts((char *)pathname); + uart_puts("\n"); if(pathname == NULL) return -1; Dentry *target_path = NULL; @@ -314,6 +337,9 @@ int vfs_chdir(const char *pathname){ Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; + uart_puts("[*] vfs_chdir: "); + uart_puts((char *)pathname); + uart_puts("\n"); /* just cd is goto root path */ if(pathname == NULL){ @@ -323,6 +349,11 @@ int vfs_chdir(const char *pathname){ // uart_puts("\n"); return 0; } + else if(strcmp(pathname, "/") == 0){ + global_dentry = rootfs->root_dentry; + strcpy(global_dir, "/"); + return 0; + } else{ int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); if(err) return -1; // worng pathname @@ -350,6 +381,9 @@ int vfs_chdir(const char *pathname){ int vfs_mount(const char *pathname, const char *filesystem){ if(filesystem == NULL) return -1; + uart_puts("[*] vfs_mount: "); + uart_puts((char *)pathname); + uart_puts("\n"); // print_string(UITOA, "path_len: ", strlen(pathname), 0); // print_string(UITOA, " | filefs_len: ", strlen(filesystem), 1); Dentry *target_path = NULL; @@ -474,6 +508,9 @@ int vfs_write(struct file* file, const void* buf, size_t len) { uart_puts("[*] Write: Cannot write to a read-only filesystem\n"); return -2; } + uart_puts("[*] vfs_write \""); + uart_puts((char *)buf); + uart_puts("\"\n"); return file->f_ops->write(file, buf, len); } @@ -482,6 +519,8 @@ int vfs_read(struct file* file, void* buf, size_t len) { // 2. block if nothing to read for FIFO type // 2. return read size or error code if an error occurs. if(file == NULL) return -1; + uart_puts("[*] vfs_read | "); + print_string(UITOA, "len : ", len, 1); return file->f_ops->read(file, buf, len); } From 38679e22a9ddb9d36e80577aac6b5a89c116ca97 Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 1 Jun 2022 17:00:31 +0800 Subject: [PATCH 51/62] /dev/uart: pass the test --- kernel/include/test_fs.h | 1 + kernel/include/vfs.h | 3 ++ kernel/src/main.c | 1 + kernel/src/syscall.c | 75 +++++++++++++++++++++++++++++++++++++--- kernel/src/test_fs.c | 8 ++++- kernel/src/tmpfs.c | 3 +- kernel/src/vfs.c | 52 +++++++++++++++++++++++++--- 7 files changed, 130 insertions(+), 13 deletions(-) diff --git a/kernel/include/test_fs.h b/kernel/include/test_fs.h index fd8a23310..3d3fceeff 100644 --- a/kernel/include/test_fs.h +++ b/kernel/include/test_fs.h @@ -9,5 +9,6 @@ void fs_test5(); void fs_test6(); void fs_test7(); void user_basic1(); +void user_advance1(); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 99ae409c4..17ca67fb4 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -82,6 +82,7 @@ struct vnode_operations { void rootfs_init(); void vfs_initramfs_init(); +void vfs_dev_init(); int register_filesystem(FileSystem* fs); int vfs_open(const char* pathname, int flags, struct file** target); int vfs_lookup(const char* pathname, Dentry **target_dentry, VNode **target_vnode, char *component_name); @@ -99,5 +100,7 @@ int change_global_path(Dentry *target); int vfs_mount(const char* pathname, const char* filesystem); int vfs_umount(const char *pathname); +int vfs_mknod(const char* pathname); + #endif \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index 26ef75fb9..b1949ea3c 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -40,6 +40,7 @@ int main(unsigned long dtb_base){ // fs_test6(); // fs_test7(); // user_basic1(); + // user_advance1(); PrintWelcome(); ShellLoop(); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index ec014a565..7468ac870 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -128,6 +128,10 @@ int kernel_exec(char *name){ /* copy the golbal dir / dentry in the new_thread*/ strcpy(new_thread->dir, global_dir); new_thread->dentry = global_dentry; + /* copy fd table */ + for(int i = 0; i < MAX_FD_NUM; i++){ + new_thread->fd_table[i] = global_fd_table[i]; + } set_period_timer_irq(); sched_timeout("omg"); @@ -200,6 +204,21 @@ int do_fork(TrapFrame *trapFrame){ strcpy(new_thread->dir, global_dir); new_thread->dentry = global_dentry; + //TODO: mabye wrong? need to check + /* copy fd table */ + for(int i = 0; i < MAX_FD_NUM; i++){ + File *tmp = global_fd_table[i]; + if(tmp != NULL){ + File *new_file = kmalloc(sizeof(File)); + new_file->f_ops = tmp->f_ops; + new_file->f_pos = tmp->f_pos; + new_file->vnode = tmp->vnode; + new_file->flags = tmp->flags; + new_thread->fd_table[i] = new_file; + } + } + + // print_string(UITOHEX, "(child)new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); @@ -383,15 +402,52 @@ void sys_write(TrapFrame *trapFrame){ int fd = trapFrame->x[0]; char *buf = (char *)trapFrame->x[1]; int count = trapFrame->x[2]; + int status; - if(fd < 0 || fd >= MAX_FD_NUM) + if(fd < 0 || fd >= MAX_FD_NUM){ trapFrame->x[0] = -1; - else if(global_fd_table[fd] == NULL) + goto DONE; + } + + if(global_fd_table[fd] == NULL){ trapFrame->x[0] = -1; - else{ - int status = vfs_write(global_fd_table[fd], buf, count); - trapFrame->x[0] = status; + goto DONE; + } + + /* FIFO: uart file */ + if(fd == 1 || fd == 2){ + /* stdout, write the data in uart file */ + status = vfs_write(global_fd_table[fd], buf, count); + if(status < 0){ + trapFrame->x[0] = status; + goto DONE; + } + /* reset the pos */ + global_fd_table[fd]->f_pos = 0; + + /* stdin, read the data to the terminal */ + size_t thesize = 0; + char buf[501]; + int read_size; + while(1){ + memset(buf, 0, 501); + read_size = vfs_read(global_fd_table[0], buf, 500); + if(read_size <= 0){ + break; + } + thesize += read_size; + uart_puts(buf); + } + global_fd_table[0]->f_pos = 0; + trapFrame->x[0] = thesize; + goto DONE; } + + /* normal file */ + status = vfs_write(global_fd_table[fd], buf, count); + trapFrame->x[0] = status; + +DONE: enable_irq(); } @@ -406,9 +462,18 @@ void sys_read(TrapFrame *trapFrame){ else if(global_fd_table[fd] == NULL) trapFrame->x[0] = -1; else{ + /* stdin */ + if(fd == 0){ + enable_irq(); + int idx = async_readnbyte(buf, count); + disable_irq(); + trapFrame->x[0] = idx; + goto DONE; + } int status = vfs_read(global_fd_table[fd], buf, count); trapFrame->x[0] = status; } +DONE: enable_irq(); } diff --git a/kernel/src/test_fs.c b/kernel/src/test_fs.c index a1698480c..0ee00f4e9 100644 --- a/kernel/src/test_fs.c +++ b/kernel/src/test_fs.c @@ -207,7 +207,7 @@ void fs_test7(){ // assert(fd < 0); } -void user_basic1(){ +void user_basic2(){ mkdir("/tmp", 0); int fd = open("/tmp/tmpfile", O_CREAT); write(fd, "Hello World!", 12); @@ -231,4 +231,10 @@ void user_basic1(){ uart_puts("\n"); close(fd); +} + +void user_advance1(){ + write(1, "Hello World!\n", 13); + write(1, "JJ\n", 3); + } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 44be26c58..6f9cd66ef 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -84,7 +84,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ write_idx += write_len; // block->data[write_idx] = EOF; - block->size += write_len; + block->size = offset + write_len; goto DONE; } else if(write_len > quota){ @@ -117,7 +117,6 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ return write_idx; } -// TODO: read fail in user program because of EOF byte int tmpfs_read(struct file* file, void* buf, size_t len){ TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; char *dest = (char *)buf; diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 4b2b2060c..fb45cc7c9 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -25,7 +25,7 @@ void rootfs_init(char *fs_name){ } if(strcmp(fs_name, "rootfs") == 0){ - fs_pool[0]->name = (char *)kmalloc(sizeof(char) * 6); // 6 is the length of "tmpfs" + fs_pool[0]->name = (char *)kmalloc(sizeof(char) * 7); // 7 is the length of "rootfs" strcpy(fs_pool[0]->name, "rootfs"); fs_pool[0]->setup_mount = tmpfs_setup_mount; } @@ -42,6 +42,22 @@ void rootfs_init(char *fs_name){ global_fd_table = (File **)kmalloc(sizeof(File *) * MAX_FD_NUM); vfs_initramfs_init(); + vfs_dev_init(); +} + +void vfs_dev_init(){ + vfs_mkdir("/dev"); + vfs_mknod("/dev/uart"); + File *uart_stdin = kmalloc(sizeof(File)); + File *uart_stdout = kmalloc(sizeof(File)); + File *uart_stderr = kmalloc(sizeof(File)); + int stdin = vfs_open("/dev/uart", 0, &uart_stdin); + int stdout = vfs_open("/dev/uart", 0, &uart_stdout); + int stderr = vfs_open("/dev/uart", 0, &uart_stderr); + global_fd_table[0] = uart_stdin; + global_fd_table[1] = uart_stdout; + global_fd_table[2] = uart_stderr; + vfs_mknod("/dev/framebuffer"); } void vfs_initramfs_init(){ @@ -80,7 +96,6 @@ void vfs_initramfs_init(){ // vfs_close(file); } } - vfs_chdir("/initramfs"); /* set the filesystem to read only */ global_dentry->mount->fs->read_only = 1; } @@ -175,11 +190,10 @@ int vfs_lookup(const char* pathname, Dentry **target_path, VNode **target_vnode, } int vfs_open(const char* pathname, int flags, struct file** target_file) { + if(pathname == NULL) return -1; uart_puts("[*] vfs_open: "); uart_puts((char *)pathname); print_string(ITOA, " | flag: ", flags, 1); - - if(pathname == NULL) return -1; Dentry *target_path = NULL; VNode *target_vnode = NULL; char component_name[MAX_PATHNAME_LEN]; @@ -190,7 +204,6 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { } // 2. Create a new file handle for this vnode if found. if(target_vnode != NULL){ - uart_puts(target_vnode->dentry->name); // cannot open a directory if(target_vnode->dentry->type == D_DIR || target_vnode->dentry->type == D_MOUNT){ @@ -233,6 +246,35 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { return -1; } +int vfs_mknod(const char *pathname){ + if(pathname == NULL) return -1; + uart_puts("[*] vfs_mknod: "); + uart_puts((char *)pathname); + uart_puts("\n"); + Dentry *target_path = NULL; + VNode *target_vnode = NULL; + char component_name[MAX_PATHNAME_LEN]; + int err = vfs_lookup(pathname, &target_path, &target_vnode, component_name); + if(err) return -1; // worng pathname + + /* target vnode is exist, cannot create it */ + if(target_vnode != NULL){ + uart_puts("[*] Mknod: Target is already exist\n"); + return -2; + } + + /* create the special file */ + err = rootfs->root_dentry->vnode->v_ops->create(target_path->vnode, &target_vnode, component_name); + if(err){ + uart_puts("[*] Open: Cannot create a file\n"); + return err; + } + uart_puts("[*] Created file: "); + uart_puts(target_vnode->dentry->name); + uart_puts("\n"); + + return 0; +} int vfs_mkdir(const char *pathname){ uart_puts("[*] vfs_mkdir: "); From 355c79e18a32f17a8c37e7cd7f40f3f307f71ca8 Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 1 Jun 2022 18:44:04 +0800 Subject: [PATCH 52/62] Use vfs method to exec file --- kernel/include/shell.h | 7 +++-- kernel/include/syscall.h | 3 +- kernel/include/tmpfs.h | 6 +--- kernel/src/main.c | 1 + kernel/src/shell.c | 28 ++++++++++++----- kernel/src/syscall.c | 68 +++++++++++++++++++++++++++++++++------- kernel/src/tmpfs.c | 10 ++++-- kernel/src/vfs.c | 5 ++- 8 files changed, 94 insertions(+), 34 deletions(-) diff --git a/kernel/include/shell.h b/kernel/include/shell.h index 53f576459..c6d044255 100644 --- a/kernel/include/shell.h +++ b/kernel/include/shell.h @@ -26,9 +26,9 @@ void Reboot(); /* Main Shell */ void ShellLoop(); -void Ls(); -void Cat(char [MAX_SIZE]); -void Exec(char [MAX_SIZE]); +void cpio_ls(); +void cpio_cat(char [MAX_SIZE]); +void cpio_exec(char [MAX_SIZE]); void SetTimeOut(char [MAX_SIZE]); void TestTimeOut(char [MAX_SIZE]); @@ -38,5 +38,6 @@ void chdir_arg(char *); void mkdir_arg(char *); void mount_arg(char *); void umount_arg(char *); +void exec_arg(char *); #endif \ No newline at end of file diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index ae545d48d..815012443 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -32,7 +32,8 @@ void sys_chdir(TrapFrame *); int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); -void *load_program(file_info *fileInfo); +void *cpio_load_program(file_info *fileInfo); +void *vfs_load_program(const char *pathname, unsigned long *size); int do_fork(TrapFrame *trapFrame); void do_exit(int status); int do_kill(int pid); diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index df04bdc7d..34ec68ffd 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -7,12 +7,8 @@ typedef struct tmpfs_inode{ struct list_head list; struct vnode* vnode; - /* - * inode head: means the num of blocks - * data inode: means the idx - */ unsigned long idx; - unsigned long size; + unsigned long size; // incode head save the all size // char *data; char data[MAX_DATA_LEN]; // data block size = MAX_DATA_LEN(512 * 8 = 4096) }TmpfsInode; diff --git a/kernel/src/main.c b/kernel/src/main.c index b1949ea3c..b97cdbf70 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -32,6 +32,7 @@ int main(unsigned long dtb_base){ enable_irq(); // DAIF set to 0b0000 // kernel_exec("vfs1.img"); + // vfs_load_program("vfs1.img"); // fs_test1(); // fs_test2(); // fs_test3(); diff --git a/kernel/src/shell.c b/kernel/src/shell.c index 60b87c05a..dea41029f 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -31,7 +31,8 @@ void PrintHelp(){ uart_puts("memory : print memory info\n"); uart_puts("reboot : reboot the device\n"); uart_puts("cpio_ls : list cpio directory contents\n"); - uart_puts("exec : concatenate files and print on the standard output\n"); + uart_puts("cpio_cat : print cpio file's data\n"); + uart_puts("cpio_exec : exec file in cpio\n"); uart_puts("setTimeout : set timeout for read\n"); uart_puts("test_timeout : test timeout for read\n"); uart_puts("ls : list directory contents\n"); @@ -39,6 +40,7 @@ void PrintHelp(){ uart_puts("mkdir : make directories\n"); uart_puts("mount : mount a filesystem\n"); uart_puts("umount : umount a filesystem\n"); + uart_puts("exec : exec a file in filesystem\n"); } @@ -94,11 +96,11 @@ void Bootimg(char buf[MAX_SIZE]){ uart_puts("done\n"); } -void Ls(){ +void cpio_ls(){ ls(); } -void Cat(char buf[MAX_SIZE]){ +void cpio_cat(char buf[MAX_SIZE]){ uart_puts("Filename: "); unsigned int size = readline(buf, MAX_SIZE); if (size == 0){ @@ -107,7 +109,7 @@ void Cat(char buf[MAX_SIZE]){ cat(buf); } -void Exec(char buf[MAX_SIZE]){ +void cpio_exec(char buf[MAX_SIZE]){ uart_puts("Filename: "); unsigned int size = readline(buf, MAX_SIZE); if (size == 0){ @@ -200,6 +202,17 @@ void umount_arg(char *buf){ } } +void exec_arg(char *buf){ + char *path = strchr(buf, ' ') + 1; + int state = kernel_exec(path); + enable_irq(); + + if(state < 0){ + uart_puts("[x] Failed to exec the file\n"); + return; + } +} + /* Main Shell */ void ShellLoop(){ char buf[MAX_SIZE]; @@ -219,9 +232,9 @@ void ShellLoop(){ else if(strcmp("revision", buf) == 0) PrintRevision(buf); else if(strcmp("memory", buf) == 0) PrintMemory(buf); else if(strcmp("bootimg", buf) == 0) Bootimg(buf); - else if(strcmp("cpio_ls", buf) == 0) Ls(); - else if(strcmp("cat", buf) == 0) Cat(buf); - else if(strcmp("exec", buf) == 0) Exec(buf); + else if(strcmp("cpio_ls", buf) == 0) cpio_ls(); + else if(strcmp("cpio_cat", buf) == 0) cpio_cat(buf); + else if(strcmp("cpio_exec", buf) == 0) cpio_exec(buf); else if(strncmp("setTimeout", buf, strlen("setTimeout")) == 0) SetTimeOut(buf); else if(strcmp("test_timeout", buf) == 0) TestTimeOut(buf); else if(strncmp("ls", buf, strlen("ls")) == 0) ls_arg(buf); @@ -229,6 +242,7 @@ void ShellLoop(){ else if(strncmp("mkdir", buf, strlen("mkdir")) == 0) mkdir_arg(buf); else if(strncmp("mount", buf, strlen("mount")) == 0) mount_arg(buf); else if(strncmp("umount", buf, strlen("umount")) == 0) umount_arg(buf); + else if(strncmp("exec", buf, strlen("exec")) == 0) exec_arg(buf); else PrintUnknown(buf); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 7468ac870..26d6f0545 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -72,20 +72,23 @@ void sys_exec(TrapFrame *trapFrame){ trapFrame->x[0] = success; enable_irq(); } + + int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ disable_irq(); /* check if the file info exist */ - file_info fileInfo = cpio_find_file_info(name); - if(fileInfo.filename == NULL) return -1; + // file_info fileInfo = cpio_find_file_info(name); + // if(fileInfo.filename == NULL) return -1; /* check if the file can create in new memory */ - void *thread_code_addr = load_program(&fileInfo); + unsigned long file_size = 0; + void *thread_code_addr = vfs_load_program(name, &file_size); if(thread_code_addr == NULL) return -1; /* current thread will change the pc to new code addr */ Thread *curr_thread = get_current(); curr_thread->code_addr = thread_code_addr; - curr_thread->code_size = fileInfo.datasize; + curr_thread->code_size = file_size; /* set current trapFrame elr_el1(begin of code) and sp_el0(begin of user stack)*/ trapFrame->elr_el1 = (unsigned long)curr_thread->code_addr; @@ -106,6 +109,14 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ curr_thread->sig_stack_addr = NULL; curr_thread->old_tp = NULL; + /* reset the vfs info, except stdin, stdout, stderr */ + for(int i = 3; i < MAX_FD_NUM; i++){ + if(curr_thread->fd_table[i] != NULL){ + vfs_close(curr_thread->fd_table[i]); + curr_thread->fd_table[i] = NULL; + } + } + enable_irq(); return 0; } @@ -113,16 +124,17 @@ int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]){ int kernel_exec(char *name){ disable_irq(); /* check if the file info exist */ - file_info fileInfo = cpio_find_file_info(name); - if(fileInfo.filename == NULL) return -1; + // file_info fileInfo = cpio_find_file_info(name); + // if(fileInfo.filename == NULL) return -1; /* check if the file can create in new memory */ - void *thread_code_addr = load_program(&fileInfo); + unsigned long file_size; + void *thread_code_addr = vfs_load_program(name, &file_size); if(thread_code_addr == NULL) return -1; Thread *new_thread = thread_create(thread_code_addr); new_thread->code_addr = thread_code_addr; - new_thread->code_size = fileInfo.datasize; + new_thread->code_size = file_size; print_string(UITOHEX, "[*] kernel_exec: new_thread->code_addr: 0x", (unsigned long long)new_thread->code_addr, 1); /* copy the golbal dir / dentry in the new_thread*/ @@ -153,13 +165,45 @@ int kernel_exec(char *name){ return 0; } -void *load_program(file_info *fileInfo){ +void *cpio_load_program(file_info *fileInfo){ void *thread_code_addr = kmalloc(fileInfo->datasize); if(thread_code_addr == NULL) return NULL; memcpy(thread_code_addr, fileInfo->data, fileInfo->datasize); return thread_code_addr; } +void *vfs_load_program(const char *pathname, unsigned long *size){ + File *file; + int err = vfs_open(pathname, 0, &file); + if(err < 0) return NULL; + TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; + void *thread_code_addr = kmalloc(inode_head->size); + if(thread_code_addr == NULL) return NULL; + + /* read the inode data */ + size_t offset = 0; + char buf[MAX_SIZE]; + int read_size; + while(1){ + read_size = vfs_read(file, buf, MAX_SIZE); + if(read_size <= 0){ + break; + } + memcpy(thread_code_addr + offset, buf, read_size); + offset += read_size; + } + if(read_size < 0){ + kfree(thread_code_addr); + thread_code_addr = NULL; + uart_puts("[x] vfs_load_program: load program error\n"); + return NULL; + } + print_string(UITOA, "[*] exec file size: ", inode_head->size, 1); + *size = inode_head->size; + vfs_close(file); + return thread_code_addr; +} + void sys_fork(TrapFrame *trapFrame){ disable_irq(); @@ -427,11 +471,11 @@ void sys_write(TrapFrame *trapFrame){ /* stdin, read the data to the terminal */ size_t thesize = 0; - char buf[501]; + char buf[MAX_SIZE]; int read_size; while(1){ - memset(buf, 0, 501); - read_size = vfs_read(global_fd_table[0], buf, 500); + memset(buf, 0, MAX_SIZE); + read_size = vfs_read(global_fd_table[0], buf, MAX_SIZE - 1); if(read_size <= 0){ break; } diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 6f9cd66ef..2b73cdac2 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -77,14 +77,15 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ size_t offset = MAX_DATA_LEN - (block->idx * MAX_DATA_LEN - file->f_pos); size_t quota = MAX_DATA_LEN - offset; - // if(block->data[offset] == (char)EOF) return -1; if(write_len <= quota){ memcpy(block->data + offset, src + write_idx, write_len); file->f_pos += write_len; write_idx += write_len; - // block->data[write_idx] = EOF; - block->size = offset + write_len; + + /* if offset != 0, it may not work */ + inode_head->size += write_len; + print_string(UITOA, "[*] File size: ", inode_head->size, 1); goto DONE; } else if(write_len > quota){ @@ -94,6 +95,9 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ write_len -= quota; block->size = MAX_DATA_LEN; + /* if offset != 0, it may not work */ + inode_head->size += quota; + /* add a new block */ TmpfsInode *new_block = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); INIT_LIST_HEAD(&new_block->list); diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index fb45cc7c9..1a5353e28 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -550,9 +550,8 @@ int vfs_write(struct file* file, const void* buf, size_t len) { uart_puts("[*] Write: Cannot write to a read-only filesystem\n"); return -2; } - uart_puts("[*] vfs_write \""); - uart_puts((char *)buf); - uart_puts("\"\n"); + uart_puts("[*] vfs_write | "); + print_string(UITOA, "len : ", len, 1); return file->f_ops->write(file, buf, len); } From c823c0d822f7d9ad7687d74a53d96ef5d472c6fb Mon Sep 17 00:00:00 2001 From: Fan Date: Wed, 1 Jun 2022 22:56:40 +0800 Subject: [PATCH 53/62] cpio check file type use c_nlink --- kernel/include/cpio.h | 1 + kernel/src/cpio.c | 7 +++++-- kernel/src/vfs.c | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/include/cpio.h b/kernel/include/cpio.h index e6a2c4ea4..691810a7c 100644 --- a/kernel/include/cpio.h +++ b/kernel/include/cpio.h @@ -37,6 +37,7 @@ typedef struct _file_info{ char *data; unsigned int filename_size; unsigned int datasize; + char *c_nlink; }file_info; void init_cpio_file_info(); diff --git a/kernel/src/cpio.c b/kernel/src/cpio.c index c8dfe5234..528d82140 100644 --- a/kernel/src/cpio.c +++ b/kernel/src/cpio.c @@ -26,6 +26,7 @@ void parse_cpio_header(cpio_newc_header *header, file_info *info){ info->filename_size = hextoui(header->c_namesize, 8); info->datasize = hextoui(header->c_filesize, 8); info->data = (char *)header + padding(sizeof(cpio_newc_header)+info->filename_size); + info->c_nlink = header->c_nlink; } void init_cpio_file_info(){ @@ -44,8 +45,10 @@ void init_cpio_file_info(){ void ls() { for(int i = 0; cpio_file_info_list[i] != NULL; i++){ uart_puts(cpio_file_info_list[i]->filename); - print_string(UITOA, " | datasize = ", cpio_file_info_list[i]->datasize, 1); - // uart_puts("\n"); + print_string(UITOA, " | datasize = ", cpio_file_info_list[i]->datasize, 0); + uart_puts(" | "); + uart_nbyte(cpio_file_info_list[i]->c_nlink, 8); + uart_puts("\n"); } // cpio_newc_header *header = (cpio_newc_header *)CPIO_BASE_START; // while(1){ diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 1a5353e28..111b40c97 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -69,7 +69,7 @@ void vfs_initramfs_init(){ if(info->filename_size == 0) continue; char name[MAX_PATHNAME_LEN+1]; memcpy(name, info->filename, info->filename_size); - if(info->datasize == 0){ + if(strncmp(info->c_nlink, "00000001", 8) != 0){ vfs_mkdir(name); } else{ From ac6a6347b0f9482158b097d7da99a23780623a18 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 00:57:08 +0800 Subject: [PATCH 54/62] lseek64 --- kernel/Makefile | 2 +- kernel/include/syscall.h | 2 ++ kernel/include/user_syscall.h | 5 ++++- kernel/include/vfs.h | 4 ++-- kernel/src/exc.c | 6 ++++++ kernel/src/syscall.c | 23 ++++++++++++++++++++--- kernel/src/tmpfs.c | 15 ++++++++++++++- kernel/src/user_syscall.S | 13 ++++++++++++- kernel/src/vfs.c | 20 +++++++++++++------- 9 files changed, 74 insertions(+), 16 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index da217bbf0..f4da9c104 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -17,7 +17,7 @@ ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -all: clean kernel8.img run +all: clean kernel8.img run-display $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 815012443..f55960632 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -29,6 +29,8 @@ void sys_read(TrapFrame *); void sys_mkdir(TrapFrame *); void sys_mount(TrapFrame *); void sys_chdir(TrapFrame *); +void sys_lseek64(TrapFrame *); +void sys_ioctl(TrapFrame *); int do_getpid(); int do_exec(TrapFrame *trapFrame, const char *name, char *const argv[]); diff --git a/kernel/include/user_syscall.h b/kernel/include/user_syscall.h index 820b60041..a670ab141 100644 --- a/kernel/include/user_syscall.h +++ b/kernel/include/user_syscall.h @@ -19,6 +19,8 @@ #define MKDIR 15 #define MOUNT 16 #define CHDIR 17 +#define LSEEK64 18 +#define IOCTL 19 #endif @@ -41,6 +43,7 @@ extern long read(int fd, void *buf, unsigned long count); extern int mkdir(const char *pathname, unsigned mode); extern int mount(const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); extern int chdir(const char *path); - +extern long lseek64(int fd, long offset, int whence); +extern int ioctl(int fd, unsigned long request, ...); #endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index 17ca67fb4..cc73322dc 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -17,6 +17,7 @@ enum dentry_type { #define MAX_FD_NUM 16 #define READ_ONLY 1 #define EOF (-1) +#define SEEK_SET 0 // file handle typedef struct file { @@ -99,8 +100,7 @@ int vfs_chdir(const char* pathname); int change_global_path(Dentry *target); int vfs_mount(const char* pathname, const char* filesystem); int vfs_umount(const char *pathname); - int vfs_mknod(const char* pathname); - +int vfs_lseek64(struct file* file, long offset, int whence); #endif \ No newline at end of file diff --git a/kernel/src/exc.c b/kernel/src/exc.c index 6704f07b3..954ed5211 100644 --- a/kernel/src/exc.c +++ b/kernel/src/exc.c @@ -86,6 +86,12 @@ void syscall_handler(unsigned int syscall_id, TrapFrame *trapFrame){ case CHDIR: sys_chdir(trapFrame); break; + case LSEEK64: + sys_lseek64(trapFrame); + break; + case IOCTL: + sys_ioctl(trapFrame); + break; default: break; diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 26d6f0545..e97d013fd 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -248,7 +248,7 @@ int do_fork(TrapFrame *trapFrame){ strcpy(new_thread->dir, global_dir); new_thread->dentry = global_dentry; - //TODO: mabye wrong? need to check + /* copy fd table */ for(int i = 0; i < MAX_FD_NUM; i++){ File *tmp = global_fd_table[i]; @@ -467,7 +467,7 @@ void sys_write(TrapFrame *trapFrame){ goto DONE; } /* reset the pos */ - global_fd_table[fd]->f_pos = 0; + vfs_lseek64(global_fd_table[fd], 0, SEEK_SET); /* stdin, read the data to the terminal */ size_t thesize = 0; @@ -482,7 +482,7 @@ void sys_write(TrapFrame *trapFrame){ thesize += read_size; uart_puts(buf); } - global_fd_table[0]->f_pos = 0; + vfs_lseek64(global_fd_table[0], 0, SEEK_SET); trapFrame->x[0] = thesize; goto DONE; } @@ -544,5 +544,22 @@ void sys_chdir(TrapFrame *trapFrame){ char *path = (char *)trapFrame->x[0]; int status = vfs_chdir(path); trapFrame->x[0] = status; + enable_irq(); +} + +void sys_lseek64(TrapFrame *trapFrame){ + disable_irq(); + int fd = trapFrame->x[0]; + int offset = trapFrame->x[1]; + int whence = trapFrame->x[2]; + // uart_puts("lseek64\n"); + int status = vfs_lseek64(global_fd_table[fd], offset, whence); + trapFrame->x[0] = status; + enable_irq(); +} + +void sys_ioctl(TrapFrame *trapFrame){ + disable_irq(); + enable_irq(); } \ No newline at end of file diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 2b73cdac2..1456481ab 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -185,7 +185,20 @@ int tmpfs_close(struct file* file){ return 0; } long tmpfs_lseek64(struct file* file, long offset, int whence){ - + switch(whence){ + case SEEK_SET: + if(offset < 0) return -1; + file->f_pos = offset; + break; + // case SEEK_CUR: + // file->f_pos += offset; + // break; + // case SEEK_END: + // file->f_pos = file->vnode->internal->size + offset; + // break; + default: + return -1; + } return 0; } diff --git a/kernel/src/user_syscall.S b/kernel/src/user_syscall.S index 79d27fe32..f32e83356 100644 --- a/kernel/src/user_syscall.S +++ b/kernel/src/user_syscall.S @@ -108,4 +108,15 @@ mount: chdir: mov x8, CHDIR svc #0 - ret \ No newline at end of file + ret + +.global lseek64 +lseek64: + mov x8, LSEEK64 + svc #0 + ret + +.global ioctl + mov x8, IOCTL + svc #0 + ret diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 111b40c97..ec8f3155d 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -51,9 +51,9 @@ void vfs_dev_init(){ File *uart_stdin = kmalloc(sizeof(File)); File *uart_stdout = kmalloc(sizeof(File)); File *uart_stderr = kmalloc(sizeof(File)); - int stdin = vfs_open("/dev/uart", 0, &uart_stdin); - int stdout = vfs_open("/dev/uart", 0, &uart_stdout); - int stderr = vfs_open("/dev/uart", 0, &uart_stderr); + vfs_open("/dev/uart", 0, &uart_stdin); + vfs_open("/dev/uart", 0, &uart_stdout); + vfs_open("/dev/uart", 0, &uart_stderr); global_fd_table[0] = uart_stdin; global_fd_table[1] = uart_stdout; global_fd_table[2] = uart_stderr; @@ -550,8 +550,8 @@ int vfs_write(struct file* file, const void* buf, size_t len) { uart_puts("[*] Write: Cannot write to a read-only filesystem\n"); return -2; } - uart_puts("[*] vfs_write | "); - print_string(UITOA, "len : ", len, 1); + // uart_puts("[*] vfs_write | "); + // print_string(UITOA, "len : ", len, 1); return file->f_ops->write(file, buf, len); } @@ -560,8 +560,14 @@ int vfs_read(struct file* file, void* buf, size_t len) { // 2. block if nothing to read for FIFO type // 2. return read size or error code if an error occurs. if(file == NULL) return -1; - uart_puts("[*] vfs_read | "); - print_string(UITOA, "len : ", len, 1); + // uart_puts("[*] vfs_read | "); + // print_string(UITOA, "len : ", len, 1); return file->f_ops->read(file, buf, len); } +int vfs_lseek64(struct file* file, long offset, int whence) { + // 1. change the file offset according to whence + // 2. return the new offset or error code if an error occurs. + if(file == NULL) return -1; + return file->f_ops->lseek64(file, offset, whence); +} \ No newline at end of file From 6f2a6663b80e20a820ac407d15ec42f85bf723b0 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 01:46:29 +0800 Subject: [PATCH 55/62] vfs_write: resize block size everytime --- kernel/src/tmpfs.c | 3 ++- kernel/src/vfs.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 1456481ab..dc4a9edfa 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -63,7 +63,8 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ char *src = (char *)buf; size_t write_len = len; size_t write_idx = 0; - + /* if offset != 0, it may not work */ + inode_head->size = 0; struct list_head *pos; list_for_each(pos, &inode_head->list){ diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index ec8f3155d..017d15495 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -550,8 +550,8 @@ int vfs_write(struct file* file, const void* buf, size_t len) { uart_puts("[*] Write: Cannot write to a read-only filesystem\n"); return -2; } - // uart_puts("[*] vfs_write | "); - // print_string(UITOA, "len : ", len, 1); + uart_puts("[*] vfs_write | "); + print_string(UITOA, "len : ", len, 1); return file->f_ops->write(file, buf, len); } @@ -560,8 +560,8 @@ int vfs_read(struct file* file, void* buf, size_t len) { // 2. block if nothing to read for FIFO type // 2. return read size or error code if an error occurs. if(file == NULL) return -1; - // uart_puts("[*] vfs_read | "); - // print_string(UITOA, "len : ", len, 1); + uart_puts("[*] vfs_read | "); + print_string(UITOA, "len : ", len, 1); return file->f_ops->read(file, buf, len); } From d3651eca7b1513fad63dbfbff07a091b8911835b Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 02:30:18 +0800 Subject: [PATCH 56/62] kill_zombie: init vfs --- kernel/src/sched.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/src/sched.c b/kernel/src/sched.c index 757bf2aae..496a5cc1b 100644 --- a/kernel/src/sched.c +++ b/kernel/src/sched.c @@ -121,6 +121,16 @@ void kill_zombie(){ tmp->sig_stack_addr = NULL; tmp->old_tp = NULL; + /* init vfs */ + tmp->dentry = NULL; + memset(tmp->dir, 0, MAX_PATHNAME_LEN * 16); + for(int i = 0; i < MAX_FD_NUM; i++){ + if(tmp->fd_table[i] != NULL){ + vfs_close(tmp->fd_table[i]); + tmp->fd_table[i] = NULL; + } + } + /* remove from run_thread_head */ list_del_entry(&tmp->list); } From 4c057709e0b3a79c1159157d9560f528568611fd Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 15:45:13 +0800 Subject: [PATCH 57/62] change typo --- kernel/include/tmpfs.h | 2 +- kernel/src/tmpfs.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/include/tmpfs.h b/kernel/include/tmpfs.h index 34ec68ffd..3289b1757 100644 --- a/kernel/include/tmpfs.h +++ b/kernel/include/tmpfs.h @@ -10,7 +10,7 @@ typedef struct tmpfs_inode{ unsigned long idx; unsigned long size; // incode head save the all size // char *data; - char data[MAX_DATA_LEN]; // data block size = MAX_DATA_LEN(512 * 8 = 4096) + char data[MAX_DATA_LEN]; // data block size = MAX_DATA_LEN }TmpfsInode; int tmpfs_setup_mount(FileSystem *fs, Mount *mount); diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index dc4a9edfa..ddef908c0 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -102,7 +102,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ /* add a new block */ TmpfsInode *new_block = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); INIT_LIST_HEAD(&new_block->list); - // new_block->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 + // new_block->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); new_block->idx = block->idx + 1; new_block->size = 0; new_block->vnode = inode_head->vnode; @@ -246,14 +246,14 @@ int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* comp /* create the inode list head */ TmpfsInode *inode_head = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); INIT_LIST_HEAD(&inode_head->list); - // inode_head->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 + // inode_head->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); inode_head->idx = 0; inode_head->size = 0; inode_head->vnode = new_dentry->vnode; /* create the real data block */ TmpfsInode *inode = (TmpfsInode *)kmalloc(sizeof(TmpfsInode)); INIT_LIST_HEAD(&inode->list); - // inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); // 512 * 8 = 4096 + // inode->data = (char *)kmalloc(sizeof(char) * MAX_DATA_LEN); inode->idx = 1; inode->size = 0; inode->vnode = new_dentry->vnode; From accdad33d4936f6fd68362535d53f82eba73aaf4 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 17:13:46 +0800 Subject: [PATCH 58/62] sys_read: add vfs_write after uart_read? --- kernel/src/syscall.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index e97d013fd..c2a08c442 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -471,18 +471,18 @@ void sys_write(TrapFrame *trapFrame){ /* stdin, read the data to the terminal */ size_t thesize = 0; - char buf[MAX_SIZE]; + char buf2[MAX_SIZE]; int read_size; while(1){ - memset(buf, 0, MAX_SIZE); - read_size = vfs_read(global_fd_table[0], buf, MAX_SIZE - 1); + memset(buf2, 0, MAX_SIZE); + read_size = vfs_read(global_fd_table[fd], buf2, MAX_SIZE - 1); if(read_size <= 0){ break; } thesize += read_size; - uart_puts(buf); + uart_puts(buf2); } - vfs_lseek64(global_fd_table[0], 0, SEEK_SET); + vfs_lseek64(global_fd_table[fd], 0, SEEK_SET); trapFrame->x[0] = thesize; goto DONE; } @@ -511,8 +511,13 @@ void sys_read(TrapFrame *trapFrame){ enable_irq(); int idx = async_readnbyte(buf, count); disable_irq(); - trapFrame->x[0] = idx; + int status = vfs_write(global_fd_table[0], buf, idx); + vfs_lseek64(global_fd_table[0], 0, SEEK_SET); + trapFrame->x[0] = status; goto DONE; + + // trapFrame->x[0] = idx; + // goto DONE; } int status = vfs_read(global_fd_table[fd], buf, count); trapFrame->x[0] = status; From cde8eb5fa646bb1d4062906d94c168a99b34225a Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 18:39:44 +0800 Subject: [PATCH 59/62] exec read one time --- kernel/Makefile | 2 +- kernel/src/syscall.c | 36 +++++++++++++++++++++--------------- kernel/src/tmpfs.c | 1 + 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index f4da9c104..da217bbf0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -17,7 +17,7 @@ ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -all: clean kernel8.img run-display +all: clean kernel8.img run $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index c2a08c442..94fa3cb57 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -179,26 +179,32 @@ void *vfs_load_program(const char *pathname, unsigned long *size){ TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; void *thread_code_addr = kmalloc(inode_head->size); if(thread_code_addr == NULL) return NULL; - - /* read the inode data */ - size_t offset = 0; - char buf[MAX_SIZE]; - int read_size; - while(1){ - read_size = vfs_read(file, buf, MAX_SIZE); - if(read_size <= 0){ - break; - } - memcpy(thread_code_addr + offset, buf, read_size); - offset += read_size; - } + int read_size = vfs_read(file, thread_code_addr, inode_head->size); if(read_size < 0){ kfree(thread_code_addr); thread_code_addr = NULL; uart_puts("[x] vfs_load_program: load program error\n"); return NULL; - } - print_string(UITOA, "[*] exec file size: ", inode_head->size, 1); + } + + /* read the inode data */ + // size_t offset = 0; + // char buf[MAX_SIZE]; + // int read_size; + // while(1){ + // read_size = vfs_read(file, buf, MAX_SIZE); + // if(read_size <= 0){ + // break; + // } + // memcpy(thread_code_addr + offset, buf, read_size); + // offset += read_size; + // } + // if(read_size < 0){ + // kfree(thread_code_addr); + // thread_code_addr = NULL; + // uart_puts("[x] vfs_load_program: load program error\n"); + // return NULL; + // } *size = inode_head->size; vfs_close(file); return thread_code_addr; diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index ddef908c0..3f4410157 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -70,6 +70,7 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ list_for_each(pos, &inode_head->list){ TmpfsInode *block = (TmpfsInode *)pos; if(file->f_pos >= block->idx * MAX_DATA_LEN){ + inode_head->size += MAX_DATA_LEN; continue; } From 9c9a25c15b63c9e635c7f6fc034e69063cda2377 Mon Sep 17 00:00:00 2001 From: Fan Date: Thu, 2 Jun 2022 22:37:44 +0800 Subject: [PATCH 60/62] /dev/uart; change f_ops --- kernel/include/uartfs.h | 13 +++++++ kernel/src/syscall.c | 76 ++++++++++++++++++++--------------------- kernel/src/uartfs.c | 13 +++++++ kernel/src/vfs.c | 9 +++++ 4 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 kernel/include/uartfs.h create mode 100644 kernel/src/uartfs.c diff --git a/kernel/include/uartfs.h b/kernel/include/uartfs.h new file mode 100644 index 000000000..87569ff20 --- /dev/null +++ b/kernel/include/uartfs.h @@ -0,0 +1,13 @@ +#ifndef UARTFS_H_ +#define UARTFS_H_ + +#include +#include +#include + +int uartfs_write(struct file* file, const void* buf, size_t len); +int uartfs_read(struct file* file, void* buf, size_t len); + + + +#endif \ No newline at end of file diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 94fa3cb57..0a1269c24 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -465,33 +465,33 @@ void sys_write(TrapFrame *trapFrame){ } /* FIFO: uart file */ - if(fd == 1 || fd == 2){ - /* stdout, write the data in uart file */ - status = vfs_write(global_fd_table[fd], buf, count); - if(status < 0){ - trapFrame->x[0] = status; - goto DONE; - } - /* reset the pos */ - vfs_lseek64(global_fd_table[fd], 0, SEEK_SET); - - /* stdin, read the data to the terminal */ - size_t thesize = 0; - char buf2[MAX_SIZE]; - int read_size; - while(1){ - memset(buf2, 0, MAX_SIZE); - read_size = vfs_read(global_fd_table[fd], buf2, MAX_SIZE - 1); - if(read_size <= 0){ - break; - } - thesize += read_size; - uart_puts(buf2); - } - vfs_lseek64(global_fd_table[fd], 0, SEEK_SET); - trapFrame->x[0] = thesize; - goto DONE; - } + // if(fd == 1 || fd == 2){ + // /* stdout, write the data in uart file */ + // status = vfs_write(global_fd_table[fd], buf, count); + // if(status < 0){ + // trapFrame->x[0] = status; + // goto DONE; + // } + // /* reset the pos */ + // vfs_lseek64(global_fd_table[fd], 0, SEEK_SET); + + // /* stdin, read the data to the terminal */ + // size_t thesize = 0; + // char buf2[MAX_SIZE]; + // int read_size; + // while(1){ + // memset(buf2, 0, MAX_SIZE); + // read_size = vfs_read(global_fd_table[fd], buf2, MAX_SIZE - 1); + // if(read_size <= 0){ + // break; + // } + // thesize += read_size; + // uart_puts(buf2); + // } + // vfs_lseek64(global_fd_table[fd], 0, SEEK_SET); + // trapFrame->x[0] = thesize; + // goto DONE; + // } /* normal file */ status = vfs_write(global_fd_table[fd], buf, count); @@ -513,18 +513,18 @@ void sys_read(TrapFrame *trapFrame){ trapFrame->x[0] = -1; else{ /* stdin */ - if(fd == 0){ - enable_irq(); - int idx = async_readnbyte(buf, count); - disable_irq(); - int status = vfs_write(global_fd_table[0], buf, idx); - vfs_lseek64(global_fd_table[0], 0, SEEK_SET); - trapFrame->x[0] = status; - goto DONE; + // if(fd == 0){ + // enable_irq(); + // int idx = async_readnbyte(buf, count); + // disable_irq(); + // int status = vfs_write(global_fd_table[0], buf, idx); + // vfs_lseek64(global_fd_table[0], 0, SEEK_SET); + // trapFrame->x[0] = status; + // goto DONE; - // trapFrame->x[0] = idx; - // goto DONE; - } + // // trapFrame->x[0] = idx; + // // goto DONE; + // } int status = vfs_read(global_fd_table[fd], buf, count); trapFrame->x[0] = status; } diff --git a/kernel/src/uartfs.c b/kernel/src/uartfs.c new file mode 100644 index 000000000..640000f70 --- /dev/null +++ b/kernel/src/uartfs.c @@ -0,0 +1,13 @@ +#include +#include +#include + +int uartfs_write(struct file* file, const void* buf, size_t len){ + uart_nbyte((char *)buf, len); + return len; +} + + +int uartfs_read(struct file* file, void* buf, size_t len){ + return readnbyte((char *)buf, len); +} \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index 017d15495..d31137d51 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -4,6 +4,7 @@ #include #include #include +#include char *global_dir; Dentry *global_dentry; @@ -57,6 +58,14 @@ void vfs_dev_init(){ global_fd_table[0] = uart_stdin; global_fd_table[1] = uart_stdout; global_fd_table[2] = uart_stderr; + + struct file_operations* uartfs_file_ops = (struct file_operations *)kmalloc(sizeof(struct file_operations)); + uartfs_file_ops->read = uartfs_read; + uartfs_file_ops->write = uartfs_write; + + global_fd_table[0]->f_ops = uartfs_file_ops; + global_fd_table[1]->f_ops = uartfs_file_ops; + global_fd_table[2]->f_ops = uartfs_file_ops; vfs_mknod("/dev/framebuffer"); } From a7cea80bbf1ea64e72664f3cd8a002bd3344b25a Mon Sep 17 00:00:00 2001 From: Fan Date: Fri, 3 Jun 2022 02:11:45 +0800 Subject: [PATCH 61/62] framebuffer: done --- kernel/Makefile | 2 +- kernel/include/dev_ops.h | 14 +++++++++ kernel/include/mailbox.h | 6 ++++ kernel/include/uartfs.h | 13 --------- kernel/include/vfs.h | 7 ++++- kernel/src/dev_ops.c | 45 ++++++++++++++++++++++++++++ kernel/src/mailbox.c | 63 ++++++++++++++++++++++++++++++++++++++++ kernel/src/main.c | 2 ++ kernel/src/shell.c | 4 +-- kernel/src/syscall.c | 2 +- kernel/src/tmpfs.c | 4 +++ kernel/src/uartfs.c | 13 --------- kernel/src/vfs.c | 56 +++++++++++++++-------------------- 13 files changed, 168 insertions(+), 63 deletions(-) create mode 100644 kernel/include/dev_ops.h delete mode 100644 kernel/include/uartfs.h create mode 100644 kernel/src/dev_ops.c delete mode 100644 kernel/src/uartfs.c diff --git a/kernel/Makefile b/kernel/Makefile index da217bbf0..f4da9c104 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -17,7 +17,7 @@ ALLOFILES = $(OFILES) $(BOOTOFILES) $(OFILES_ASM) CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -all: clean kernel8.img run +all: clean kernel8.img run-display $(BUILD_DIR)/%.o: $(BOOT_DIR)/%.S $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ -g diff --git a/kernel/include/dev_ops.h b/kernel/include/dev_ops.h new file mode 100644 index 000000000..fa7638b23 --- /dev/null +++ b/kernel/include/dev_ops.h @@ -0,0 +1,14 @@ +#ifndef DRIVER_OPS_H_ +#define DRIVER_OPS_H_ + +#include +#include +#include + +void dev_ops_init(); +int uart_dev_write(struct file* file, const void* buf, size_t len); +int uart_dev_read(struct file* file, void* buf, size_t len); +int framebuf_dev_write(struct file* file, const void* buf, size_t len); + + +#endif \ No newline at end of file diff --git a/kernel/include/mailbox.h b/kernel/include/mailbox.h index 101457f79..b488b1392 100644 --- a/kernel/include/mailbox.h +++ b/kernel/include/mailbox.h @@ -36,8 +36,14 @@ #define MAILBOX_CH_COUNT 7 #define MAILBOX_CH_PROP 8 +#define MBOX_REQUEST 0 +#define MBOX_CH_PROP 8 +#define MBOX_TAG_LAST 0 + + unsigned int get_board_revision(unsigned int [36]); unsigned int get_arm_memory(unsigned int [36]); +void framebuffer_init(); unsigned int mailbox_call(unsigned int *, unsigned char); #endif diff --git a/kernel/include/uartfs.h b/kernel/include/uartfs.h deleted file mode 100644 index 87569ff20..000000000 --- a/kernel/include/uartfs.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef UARTFS_H_ -#define UARTFS_H_ - -#include -#include -#include - -int uartfs_write(struct file* file, const void* buf, size_t len); -int uartfs_read(struct file* file, void* buf, size_t len); - - - -#endif \ No newline at end of file diff --git a/kernel/include/vfs.h b/kernel/include/vfs.h index cc73322dc..0979039f2 100644 --- a/kernel/include/vfs.h +++ b/kernel/include/vfs.h @@ -4,6 +4,11 @@ #include #include +enum file_type { + UART, + FRAME_BUFFER +}; + enum dentry_type { D_DIR, D_FILE, @@ -100,7 +105,7 @@ int vfs_chdir(const char* pathname); int change_global_path(Dentry *target); int vfs_mount(const char* pathname, const char* filesystem); int vfs_umount(const char *pathname); -int vfs_mknod(const char* pathname); +int vfs_mknod(const char* pathname, enum file_type type); int vfs_lseek64(struct file* file, long offset, int whence); #endif \ No newline at end of file diff --git a/kernel/src/dev_ops.c b/kernel/src/dev_ops.c new file mode 100644 index 000000000..1d9b3e298 --- /dev/null +++ b/kernel/src/dev_ops.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +struct file_operations* uart_file_ops; +struct file_operations* framebuffer_file_ops; + +extern unsigned int width, height, pitch, isrgb; /* dimensions and channel order */ +extern unsigned char *lfb; /* raw frame buffer address */ + +void dev_ops_init(){ + uart_file_ops = (struct file_operations*)kmalloc(sizeof(struct file_operations)); + uart_file_ops->read = uart_dev_read; + uart_file_ops->write = uart_dev_write; + uart_file_ops->open = tmpfs_open; + uart_file_ops->lseek64 = tmpfs_lseek64; + uart_file_ops->close = tmpfs_close; + + framebuffer_file_ops = (struct file_operations*)kmalloc(sizeof(struct file_operations)); + framebuffer_file_ops->write = framebuf_dev_write; + framebuffer_file_ops->open = tmpfs_open; + framebuffer_file_ops->lseek64 = tmpfs_lseek64; + framebuffer_file_ops->close = tmpfs_close; +} + + + +int uart_dev_read(struct file* file, void* buf, size_t len){ + return readnbyte((char *)buf, len); +} + +int uart_dev_write(struct file* file, const void* buf, size_t len){ + uart_nbyte((char *)buf, len); + return len; +} + +int framebuf_dev_write(struct file* file, const void* buf, size_t len){ + size_t pos = file->f_pos; + memcpy((char *)lfb + pos, buf, len); + file->f_pos += len; + return len; +} diff --git a/kernel/src/mailbox.c b/kernel/src/mailbox.c index 984252555..cb6743468 100644 --- a/kernel/src/mailbox.c +++ b/kernel/src/mailbox.c @@ -2,6 +2,10 @@ #include #include +unsigned int __attribute__((aligned(16))) framebuf_mbox[36]; +unsigned int width, height, pitch, isrgb; /* dimensions and channel order */ +unsigned char *lfb; /* raw frame buffer address */ + unsigned int get_board_revision(unsigned int mbox[36]){ mbox[0] = 7 * 4; // buffer size in bytes mbox[1] = REQUEST_CODE; @@ -32,6 +36,65 @@ unsigned int get_arm_memory(unsigned int mbox[36]){ } +void framebuffer_init(){ + framebuf_mbox[0] = 35 * 4; + framebuf_mbox[1] = MBOX_REQUEST; + + framebuf_mbox[2] = 0x48003; // set phy wh + framebuf_mbox[3] = 8; + framebuf_mbox[4] = 8; + framebuf_mbox[5] = 1024; // FrameBufferInfo.width + framebuf_mbox[6] = 768; // FrameBufferInfo.height + + framebuf_mbox[7] = 0x48004; // set virt wh + framebuf_mbox[8] = 8; + framebuf_mbox[9] = 8; + framebuf_mbox[10] = 1024; // FrameBufferInfo.virtual_width + framebuf_mbox[11] = 768; // FrameBufferInfo.virtual_height + + framebuf_mbox[12] = 0x48009; // set virt offset + framebuf_mbox[13] = 8; + framebuf_mbox[14] = 8; + framebuf_mbox[15] = 0; // FrameBufferInfo.x_offset + framebuf_mbox[16] = 0; // FrameBufferInfo.y.offset + + framebuf_mbox[17] = 0x48005; // set depth + framebuf_mbox[18] = 4; + framebuf_mbox[19] = 4; + framebuf_mbox[20] = 32; // FrameBufferInfo.depth + + framebuf_mbox[21] = 0x48006; // set pixel order + framebuf_mbox[22] = 4; + framebuf_mbox[23] = 4; + framebuf_mbox[24] = 1; // RGB, not BGR preferably + + framebuf_mbox[25] = 0x40001; // get framebuffer, gets alignment on request + framebuf_mbox[26] = 8; + framebuf_mbox[27] = 8; + framebuf_mbox[28] = 4096; // FrameBufferInfo.pointer + framebuf_mbox[29] = 0; // FrameBufferInfo.size + + framebuf_mbox[30] = 0x40008; // get pitch + framebuf_mbox[31] = 4; + framebuf_mbox[32] = 4; + framebuf_mbox[33] = 0; // FrameBufferInfo.pitch + + framebuf_mbox[34] = MBOX_TAG_LAST; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mailbox_call(framebuf_mbox, MBOX_CH_PROP) && framebuf_mbox[20] == 32 && framebuf_mbox[28] != 0) { + framebuf_mbox[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + width = framebuf_mbox[5]; // get actual physical width + height = framebuf_mbox[6]; // get actual physical height + pitch = framebuf_mbox[33]; // get number of bytes per line + isrgb = framebuf_mbox[24]; // get the actual channel order + lfb = (void *)((unsigned long)framebuf_mbox[28]); + } else { + uart_puts("Unable to set screen resolution to 1024x768x32\n"); + } +} + /* 1. Combine the message address (upper 28 bits) with channel number (lower 4 bits) 2. Check if Mailbox 0 status register’s full flag is set. diff --git a/kernel/src/main.c b/kernel/src/main.c index b97cdbf70..c10808e53 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -12,6 +12,7 @@ #include #include #include +#include extern Thread *run_thread_head; @@ -22,6 +23,7 @@ int main(unsigned long dtb_base){ // uart_getc(); print_string(UITOHEX, "[*] DTB_BASE: 0x", dtb_base, 1); fdt_traverse((fdt_header *)dtb_base, initramfs_callback); + framebuffer_init(); all_allocator_init(); init_cpio_file_info(); rootfs_init("rootfs"); diff --git a/kernel/src/shell.c b/kernel/src/shell.c index dea41029f..e9334e7af 100644 --- a/kernel/src/shell.c +++ b/kernel/src/shell.c @@ -53,7 +53,7 @@ void PrintUnknown(char buf[MAX_SIZE]){ /* print board revision*/ void PrintRevision(char buf[MAX_SIZE]){ - unsigned int mbox[36]; + unsigned int __attribute__((aligned(16))) mbox[36]; unsigned int success = get_board_revision(mbox); if (success){ print_string(UITOHEX, "Board Revision: 0x", mbox[5], 1); @@ -64,7 +64,7 @@ void PrintRevision(char buf[MAX_SIZE]){ /* print memory info*/ void PrintMemory(char buf[MAX_SIZE]){ - unsigned int mbox[36]; + unsigned int __attribute__((aligned(16))) mbox[36]; unsigned int success = get_arm_memory(mbox); if (success){ print_string(UITOHEX, "ARM Memory Base Address: 0x", mbox[5], 1); diff --git a/kernel/src/syscall.c b/kernel/src/syscall.c index 0a1269c24..4b01dd361 100644 --- a/kernel/src/syscall.c +++ b/kernel/src/syscall.c @@ -528,7 +528,7 @@ void sys_read(TrapFrame *trapFrame){ int status = vfs_read(global_fd_table[fd], buf, count); trapFrame->x[0] = status; } -DONE: + enable_irq(); } diff --git a/kernel/src/tmpfs.c b/kernel/src/tmpfs.c index 3f4410157..e7830234b 100644 --- a/kernel/src/tmpfs.c +++ b/kernel/src/tmpfs.c @@ -59,6 +59,8 @@ void tmpfs_set_ops(){ } int tmpfs_write(struct file* file, const void* buf, size_t len){ + uart_puts("[*] tmpfs_write | "); + print_string(UITOA, "len : ", len, 1); TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; char *src = (char *)buf; size_t write_len = len; @@ -124,6 +126,8 @@ int tmpfs_write(struct file* file, const void* buf, size_t len){ } int tmpfs_read(struct file* file, void* buf, size_t len){ + uart_puts("[*] tmpfs_read | "); + print_string(UITOA, "len : ", len, 1); TmpfsInode *inode_head = (TmpfsInode *)file->vnode->internal; char *dest = (char *)buf; size_t read_len = len; diff --git a/kernel/src/uartfs.c b/kernel/src/uartfs.c deleted file mode 100644 index 640000f70..000000000 --- a/kernel/src/uartfs.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include - -int uartfs_write(struct file* file, const void* buf, size_t len){ - uart_nbyte((char *)buf, len); - return len; -} - - -int uartfs_read(struct file* file, void* buf, size_t len){ - return readnbyte((char *)buf, len); -} \ No newline at end of file diff --git a/kernel/src/vfs.c b/kernel/src/vfs.c index d31137d51..edd2090bf 100644 --- a/kernel/src/vfs.c +++ b/kernel/src/vfs.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include char *global_dir; Dentry *global_dentry; @@ -13,6 +13,8 @@ Mount *rootfs; FileSystem **fs_pool; extern file_info **cpio_file_info_list; +extern struct file_operations* uart_file_ops; +extern struct file_operations* framebuffer_file_ops; void rootfs_init(char *fs_name){ fs_pool = (FileSystem **)kmalloc(sizeof(FileSystem *) * MAX_FS_NUM); @@ -47,8 +49,9 @@ void rootfs_init(char *fs_name){ } void vfs_dev_init(){ + dev_ops_init(); vfs_mkdir("/dev"); - vfs_mknod("/dev/uart"); + vfs_mknod("/dev/uart", UART); File *uart_stdin = kmalloc(sizeof(File)); File *uart_stdout = kmalloc(sizeof(File)); File *uart_stderr = kmalloc(sizeof(File)); @@ -59,14 +62,7 @@ void vfs_dev_init(){ global_fd_table[1] = uart_stdout; global_fd_table[2] = uart_stderr; - struct file_operations* uartfs_file_ops = (struct file_operations *)kmalloc(sizeof(struct file_operations)); - uartfs_file_ops->read = uartfs_read; - uartfs_file_ops->write = uartfs_write; - - global_fd_table[0]->f_ops = uartfs_file_ops; - global_fd_table[1]->f_ops = uartfs_file_ops; - global_fd_table[2]->f_ops = uartfs_file_ops; - vfs_mknod("/dev/framebuffer"); + vfs_mknod("/dev/framebuffer", FRAME_BUFFER); } void vfs_initramfs_init(){ @@ -86,23 +82,6 @@ void vfs_initramfs_init(){ vfs_open(name, O_CREAT, &file); vfs_write(file, info->data, info->datasize); vfs_close(file); - - // file = NULL; - // vfs_open(name, 0, &file); - // size_t thesize = 0; - // char buf[500]; - // while(1){ - // memset(buf, 0, 500); - // int read_size = vfs_read(file, buf, 499); - // // print_string(ITOA, "read_size: ", read_size, 1); - // if(read_size <= 0){ - // break; - // } - // thesize += read_size; - // uart_puts(buf); - // } - // // vfs_write(file, info->data, info->datasize); - // vfs_close(file); } } /* set the filesystem to read only */ @@ -255,7 +234,7 @@ int vfs_open(const char* pathname, int flags, struct file** target_file) { return -1; } -int vfs_mknod(const char *pathname){ +int vfs_mknod(const char *pathname, enum file_type filetype){ if(pathname == NULL) return -1; uart_puts("[*] vfs_mknod: "); uart_puts((char *)pathname); @@ -282,6 +261,19 @@ int vfs_mknod(const char *pathname){ uart_puts(target_vnode->dentry->name); uart_puts("\n"); + /* set the file type */ + switch (filetype) + { + case UART: + target_vnode->f_ops = uart_file_ops; + break; + case FRAME_BUFFER: + target_vnode->f_ops = framebuffer_file_ops; + break; + default: + break; + } + return 0; } @@ -559,8 +551,8 @@ int vfs_write(struct file* file, const void* buf, size_t len) { uart_puts("[*] Write: Cannot write to a read-only filesystem\n"); return -2; } - uart_puts("[*] vfs_write | "); - print_string(UITOA, "len : ", len, 1); + // uart_puts("[*] vfs_write | "); + // print_string(UITOA, "len : ", len, 1); return file->f_ops->write(file, buf, len); } @@ -569,8 +561,8 @@ int vfs_read(struct file* file, void* buf, size_t len) { // 2. block if nothing to read for FIFO type // 2. return read size or error code if an error occurs. if(file == NULL) return -1; - uart_puts("[*] vfs_read | "); - print_string(UITOA, "len : ", len, 1); + // uart_puts("[*] vfs_read | "); + // print_string(UITOA, "len : ", len, 1); return file->f_ops->read(file, buf, len); } From 9d2497a31e8bbe627a3ed4c650665c432b96f41f Mon Sep 17 00:00:00 2001 From: fan Date: Mon, 6 Jun 2022 21:12:30 +0800 Subject: [PATCH 62/62] update readme --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 395ed60ff..75e4add4c 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ sudo ../uartboot.py ┃ ┣ 📂include ┃ ┃ ┣ 📜allocator.h ┃ ┃ ┣ 📜cpio.h + ┃ ┃ ┣ 📜dev_ops.h ┃ ┃ ┣ 📜exc.h ┃ ┃ ┣ 📜fdt.h ┃ ┃ ┣ 📜gpio.h @@ -86,10 +87,17 @@ sudo ../uartboot.py ┃ ┃ ┣ 📜string.h ┃ ┃ ┣ 📜syscall.h ┃ ┃ ┣ 📜task.h + ┃ ┃ ┣ 📜test_fs.h ┃ ┃ ┣ 📜timer.h + ┃ ┃ ┣ 📜tmpfs.h ┃ ┃ ┣ 📜uart.h - ┃ ┃ ┗ 📜user_syscall.h + ┃ ┃ ┣ 📜user_syscall.h + ┃ ┃ ┗ 📜vfs.h ┃ ┣ 📂initramfs + ┃ ┃ ┣ 📂flag + ┃ ┃ ┃ ┣ 📂fffflag + ┃ ┃ ┃ ┃ ┗ 📜flag + ┃ ┃ ┃ ┗ 📜fakeflag.txt ┃ ┃ ┣ 📜me ┃ ┃ ┣ 📜test1 ┃ ┃ ┣ 📜test2.txt @@ -99,14 +107,15 @@ sudo ../uartboot.py ┃ ┃ ┣ 📜allocator.c ┃ ┃ ┣ 📜cpio.c ┃ ┃ ┣ 📜ctx_switch.S + ┃ ┃ ┣ 📜dev_ops.c ┃ ┃ ┣ 📜exc.c ┃ ┃ ┣ 📜fdt.c ┃ ┃ ┣ 📜irq.c ┃ ┃ ┣ 📜mailbox.c ┃ ┃ ┣ 📜main.c ┃ ┃ ┣ 📜malloc.c - ┃ ┃ ┣ 📜read.c ┃ ┃ ┣ 📜math.c + ┃ ┃ ┣ 📜read.c ┃ ┃ ┣ 📜reboot.c ┃ ┃ ┣ 📜sched.c ┃ ┃ ┣ 📜shell.c @@ -114,9 +123,12 @@ sudo ../uartboot.py ┃ ┃ ┣ 📜string.c ┃ ┃ ┣ 📜syscall.c ┃ ┃ ┣ 📜task.c + ┃ ┃ ┣ 📜test_fs.c ┃ ┃ ┣ 📜timer.c + ┃ ┃ ┣ 📜tmpfs.c ┃ ┃ ┣ 📜uart.c - ┃ ┃ ┗ 📜user_syscall.S + ┃ ┃ ┣ 📜user_syscall.S + ┃ ┃ ┗ 📜vfs.c ┃ ┣ 📜Makefile ┃ ┣ 📜bcm2710-rpi-3-b-plus.dtb ┃ ┗ 📜initramfs.cpio