diff --git a/lab6/initramfs.cpio b/lab6/initramfs.cpio new file mode 100644 index 000000000..0ef5cf90b Binary files /dev/null and b/lab6/initramfs.cpio differ diff --git a/lab6/kernel/Makefile b/lab6/kernel/Makefile new file mode 100644 index 000000000..07b0d0a5f --- /dev/null +++ b/lab6/kernel/Makefile @@ -0,0 +1,57 @@ +OS := $(shell uname) + +ifeq ($(OS), Linux) + ARMGNU = aarch64-linux-gnu + MACHINE = raspi3b +endif + +ifeq ($(OS), Darwin) + ARMGNU = aarch64-unknown-linux-gnu + MACHINE = raspi3b +endif + +CC = $(ARMGNU)-gcc +LK = $(ARMGNU)-ld +OBJCPY = $(ARMGNU)-objcopy +QEMU = qemu-system-aarch64 + +A_SRCS = $(wildcard lib/*.S) +C_SRCS = $(wildcard lib/*.c) +SRC_DIR = lib +OBJ_DIR = build +OBJS = $(A_SRCS:$(SRC_DIR)/%.S=$(OBJ_DIR)/%_s.o) $(C_SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%_c.o) +INCLUDE = include +CFLAGS = -ggdb -Wno-implicit -Wno-int-conversion -ffreestanding -nostdlib -nostartfiles -I$(INCLUDE) +CPIO_FILE = ../initramfs.cpio +DTB_FILE = ../bcm2710-rpi-3-b-plus.dtb + +IMAGE = kernel8 + +all: $(IMAGE).img + +$(IMAGE).img: $(IMAGE).elf + $(OBJCPY) -O binary $(IMAGE).elf $(IMAGE).img + +$(IMAGE).elf: $(OBJS) + $(LK) $(OBJS) -T linker.ld -o $(IMAGE).elf + +$(OBJ_DIR)/%_s.o: lib/%.S + @if [ ! -d "$(OBJ_DIR)" ]; then mkdir $(OBJ_DIR); fi + $(CC) -c $< $(CFLAGS) -o $@ + +$(OBJ_DIR)/%_c.o: lib/%.c + @if [ ! -d "$(OBJ_DIR)" ]; then mkdir $(OBJ_DIR); fi + $(CC) -c $< $(CFLAGS) -o $@ + +run: $(IMAGE).img + $(QEMU) -machine $(MACHINE) -kernel $(IMAGE).img -initrd $(CPIO_FILE) -dtb $(DTB_FILE) -display none -serial null -serial stdio + +run_display: $(IMAGE).img + $(QEMU) -machine $(MACHINE) -kernel $(IMAGE).img -initrd $(CPIO_FILE) -dtb $(DTB_FILE) -serial null -serial stdio + +dbg: $(IMAGE).img + $(QEMU) -machine $(MACHINE) -kernel $(IMAGE).img -initrd $(CPIO_FILE) -dtb $(DTB_FILE) -display none -serial null -serial stdio -s -S + +.PHONY: clean +clean: + rm -rf $(OBJ_DIR) *.img *.elf */*.elf \ No newline at end of file diff --git a/lab6/kernel/include/cpio.h b/lab6/kernel/include/cpio.h new file mode 100644 index 000000000..002692b92 --- /dev/null +++ b/lab6/kernel/include/cpio.h @@ -0,0 +1,34 @@ +#ifndef CPIO_H +#define CPIO_H + +#define CPIO_NEWC_HEADER_MAGIC "070701" // big endian + +struct cpio_newc_header +{ + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +}; + +/* write pathname,data,next header into corresponding parameter*/ +int cpio_newc_parse_header(struct cpio_newc_header *this_header_pointer, + char **pathname, unsigned int *filesize, char **data, + struct cpio_newc_header **next_header_pointer); + +int ls(char* working_dir); +int cat(char* thefilepath); +int execfile(char *thefilepath); +char *get_file_start(char *thefilepath); +unsigned int get_file_size(char *thefilepath); +#endif \ No newline at end of file diff --git a/lab6/kernel/include/current.h b/lab6/kernel/include/current.h new file mode 100644 index 000000000..ab97d777b --- /dev/null +++ b/lab6/kernel/include/current.h @@ -0,0 +1,27 @@ +#ifndef CURRENT_H +#define CURRENT_H + +#include "sched.h" + +// tpidr_el1 : 64 bit system register +// get current thread data structure +// msb 2 bit : thread id +static inline thread_context_t *get_current_ctx(void) +{ + thread_context_t *cur; + __asm__ __volatile__( + "mrs %0, tpidr_el1\n\t" + : "=r"(cur)); + return cur; +} + +static inline void set_current_ctx(thread_context_t *thread_context) +{ + __asm__ __volatile__( + "msr tpidr_el1, %0\n\t" + ::"r"(thread_context)); +} + +#define current_ctx get_current_ctx() + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/dtb.h b/lab6/kernel/include/dtb.h new file mode 100644 index 000000000..212a818d5 --- /dev/null +++ b/lab6/kernel/include/dtb.h @@ -0,0 +1,33 @@ +#ifndef DTB_H +#define DTB_H +#include "uint.h" + + +struct fdt_header { + unsigned int magic; + unsigned int totalsize; + unsigned int off_dt_struct; + unsigned int off_dt_strings; + unsigned int off_mem_rsvmap; + unsigned int version; + unsigned int last_comp_version; + unsigned int boot_cpuid_phys; + unsigned int size_dt_strings; + unsigned int size_dt_struct; +}; +extern char* cpio_start; +extern char* cpio_end; + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 +typedef void (*dtb_callback)(unsigned int node_type, char *name, void *value, unsigned int name_size); + +unsigned int endian_big2little(unsigned int x); +void fdt_traverse(dtb_callback callback); +void dtb_callback_show_tree(uint32_t node_type, char *name, void *value, uint32_t name_size); +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/gpio.h b/lab6/kernel/include/gpio.h new file mode 100644 index 000000000..42e1cc5c1 --- /dev/null +++ b/lab6/kernel/include/gpio.h @@ -0,0 +1,27 @@ +#include + +#ifndef GPIO_H +#define GPIO_H + +#define MMIO_BASE PHYS_TO_VIRT(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/lab6/kernel/include/irq.h b/lab6/kernel/include/irq.h new file mode 100644 index 000000000..2f5f400a3 --- /dev/null +++ b/lab6/kernel/include/irq.h @@ -0,0 +1,94 @@ +#ifndef IRQ_H +#define IRQ_H + + +#include "mmu.h" + +#define CORE0_INTERRUPT_SOURCE ((volatile unsigned int *)(PHYS_TO_VIRT(0x40000060))) + +#define PERIPHERAL_INTERRUPT_BASE PHYS_TO_VIRT(0x3F000000) +#define IRQ_BASIC_PENDING ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B200)) + +#define INTERRUPT_SOURCE_TIMER (1 << 1) +#define INTERRUPT_SOURCE_CNTPNSIRQ 1<<1 +#define INTERRUPT_SOURCE_GPU (1 << 8) +#define IRQ_PENDING_1_AUX_INT (1 << 29) + +#define IRQ_PENDING_1 ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B208)) + + +#define FIQ_CONTROL ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int *)(PERIPHERAL_INTERRUPT_BASE + 0x0000B224)) + + + #define DATA_ABORT_LOWER 0b100100 + #define INS_ABORT_LOWER 0b100000 + + #define TF_LEVEL0 0b000100 + #define TF_LEVEL1 0b000101 + #define TF_LEVEL2 0b000110 + #define TF_LEVEL3 0b000111 + + + + +// trapframe register +typedef struct trapframe +{ + unsigned long x0; + unsigned long x1; + unsigned long x2; + unsigned long x3; + unsigned long x4; + unsigned long x5; + unsigned long x6; + unsigned long x7; + unsigned long x8; + unsigned long x9; + unsigned long x10; + unsigned long x11; + unsigned long x12; + unsigned long x13; + unsigned long x14; + unsigned long x15; + unsigned long x16; + unsigned long x17; + unsigned long x18; + 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 x29; + unsigned long x30; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; + +} trapframe_t; + +void enable_interrupt(); +void disable_interrupt(); +void irq_handler(); +void invalid_exception_handler(unsigned long long x0); +void cpacr_el1_off(); +void sync_el0_64_handler(trapframe_t *tpf, unsigned long x1); + +void highp(); +void lowp(); +void test_preemption(); + +void lock(); +void unlock(); +#endif \ No newline at end of file diff --git a/lab6/kernel/include/list.h b/lab6/kernel/include/list.h new file mode 100644 index 000000000..94abf2b9a --- /dev/null +++ b/lab6/kernel/include/list.h @@ -0,0 +1,46 @@ +#ifndef LIST_H +#define LIST_H + + + +typedef struct list_head { + struct list_head *next, *prev; +} list_head_t; + +extern list_head_t task_list; +extern list_head_t *timer_event_list; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } +#define LIST_HEAD(name) list_head_t name = LIST_HEAD_INIT(name) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); pos = pos->prev) + +#define offsetof(TYPE, MEMBER) ((unsigned int) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define list_entry(ptr,type,member) \ + container_of(ptr, type, member) + +void INIT_LIST_HEAD(list_head_t *head); +int list_empty(const list_head_t *head); +static void __list_add(list_head_t *new_lst, list_head_t *prev, list_head_t *next); +void list_add(list_head_t *new_lst, list_head_t *head); +void list_add_tail( list_head_t *new_lst, list_head_t *head); +void list_insert(list_head_t *new_lst, list_head_t *prev, list_head_t *next); +static void __list_del(list_head_t * prev, list_head_t * next); +void list_del(list_head_t * entry); +int list_size(const list_head_t *head); + +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/malloc.h b/lab6/kernel/include/malloc.h new file mode 100644 index 000000000..5f1d9359f --- /dev/null +++ b/lab6/kernel/include/malloc.h @@ -0,0 +1,52 @@ +#ifndef MALLOC_H +#define MALLOC_H + +#include "list.h" +#include "uart.h" +#include "irq.h" +#include "dtb.h" + +#define MAXORDER 6 +#define MAXCACHEORDER 4 // 32, 64, 128, 256, 512 (for every 32bytes) + +// simple_malloc +void *smalloc(unsigned int size); + +#define BUDDYSYSTEM_START PHYS_TO_VIRT(0x0) //0x10000000L +#define BUDDYSYSTEM_PAGE_COUNT 0x3C000 +//buddy system (for >= 4K pages) +void *allocpage(unsigned int size); +void freepage(void *ptr); + +//Basic Exercise 2 - Dynamic Memory Allocator - 30% +//For (< 4K) +//small memory allocation +//store listhead in cache first 16 bytes +void *alloccache(unsigned int size); +void freecache(void *ptr); +void page2caches(int order); + +void *malloc(unsigned int size); +void free(void *ptr); + +typedef struct frame +{ + list_head_t listhead; + int val; // val is order + int isused; + int cacheorder; // -1 means isn't used for cache + unsigned int idx; +} frame_t; + +void init_allocator(); +frame_t *release_redundant(frame_t *frame); +frame_t *get_buddy(frame_t *frame); +frame_t *coalesce(frame_t* f); +void dump_freelist_info(); +void dump_cachelist_info(); +void memory_reserve(unsigned long long start, unsigned long long end); +void alloctest(); +void *memcpy(void *dest, const void *src, unsigned long long n); +#endif + + diff --git a/lab6/kernel/include/mbox.h b/lab6/kernel/include/mbox.h new file mode 100644 index 000000000..8e4dc9772 --- /dev/null +++ b/lab6/kernel/include/mbox.h @@ -0,0 +1,39 @@ +#ifndef MAILBOX_H +#define MAILBOX_H +#include "gpio.h" + +extern volatile unsigned int mbox[72]; + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +#define VIDEOCORE_MBOX (MMIO_BASE+0x0000B880) +#define MBOX_READ ((volatile unsigned int*)(VIDEOCORE_MBOX+0x0)) +#define MBOX_POLL ((volatile unsigned int*)(VIDEOCORE_MBOX+0x10)) +#define MBOX_SENDER ((volatile unsigned int*)(VIDEOCORE_MBOX+0x14)) +#define MBOX_STATUS ((volatile unsigned int*)(VIDEOCORE_MBOX+0x18)) +#define MBOX_CONFIG ((volatile unsigned int*)(VIDEOCORE_MBOX+0x1C)) +#define MBOX_WRITE ((volatile unsigned int*)(VIDEOCORE_MBOX+0x20)) +#define MBOX_RESPONSE 0x80000000 // mbox[1] = 0x80000000 -> request successful +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define GET_BOARD_REVISION 0x10002 +#define MBOX_TAG_GETSERIAL 0x10004 +#define GET_ARM_MEMORY 0x10005 +#define MBOX_TAG_LAST 0 + +int mbox_call(unsigned char ch); + + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/mmu.h b/lab6/kernel/include/mmu.h new file mode 100644 index 000000000..c465f3381 --- /dev/null +++ b/lab6/kernel/include/mmu.h @@ -0,0 +1,68 @@ +#ifndef MMU_H +#define MMU_H + +#include "stddef.h" +// user address translation [0 : 5] +// kernel address translation [16 : 21] +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) +// user space 0b00 [14 : 15] +// kernel space 0b10 [30 : 31] +#define TCR_CONFIG_DEFAULT (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB) + +#define MAIR_DEVICE_nGnRnE 0b00000000 +#define MAIR_NORMAL_NOCACHE 0b01000100 +#define MAIR_IDX_DEVICE_nGnRnE 0 +#define MAIR_IDX_NORMAL_NOCACHE 1 + +// next level is block/page/invalid +#define PD_TABLE 0b11L +#define PD_BLOCK 0b01L +#define PD_UNX (1L << 54) +#define PD_KNX (1L << 53) +// access flag (page fault generated if not set) +#define PD_ACCESS (1L << 10) +// only kernel access +#define PD_UK_ACCESS (1L << 6) +// 0 for read-write 1 for read-only +#define PD_RDONLY (1L << 7) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + +// linear mapping to simplify +#define PHYS_TO_VIRT(x) (x + 0xffff000000000000) +#define VIRT_TO_PHYS(x) (x - 0xffff000000000000) +#define ENTRY_ADDR_MASK 0x0000fffffffff000L + +#ifndef __ASSEMBLER__ + +//#define kernel_pgd_addr 0x1000 +//#define kernel_pud_addr 0x2000 + +#define PGD 0x1000 +#define PUD 0x2000 + +#include "sched.h" + +void *set_2M_kernel_mmu(void *x0); +void map_one_page(size_t *pgd_p, size_t va, size_t pa, size_t flag); +void add_vma(thread_t *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced); +void free_page_tables(size_t *page_table, int level); +void handle_abort(esr_el1_t* esr_el1); +void seg_fault(); +void map_one_page_rwx(size_t *pgd_p, size_t va, size_t pa, size_t rwxflag); + +typedef struct vm_area_struct +{ + list_head_t listhead; + unsigned long virt_addr; + unsigned long phys_addr; + unsigned long area_size; + unsigned long rwx; // 1, 2, 4 + int is_alloced; // malloc or not + +} vm_area_struct_t; + +#endif //__ASSEMBLER__ + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/registers.h b/lab6/kernel/include/registers.h new file mode 100644 index 000000000..4e6634222 --- /dev/null +++ b/lab6/kernel/include/registers.h @@ -0,0 +1,11 @@ +#ifndef REGISTERS_H +#define REGISTERS_H + +// CPACR_EL1, Architectural Feature Access Control Register +#define CPACR_EL1_FPEN (0b11 << 20) +#define CPACR_EL1_VALUE (CPACR_EL1_FPEN) +#define IRQS1 ((volatile unsigned int *)(PHYS_TO_VIRT(0x3f00b210))) + + + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/sched.h b/lab6/kernel/include/sched.h new file mode 100644 index 000000000..18b3f5db4 --- /dev/null +++ b/lab6/kernel/include/sched.h @@ -0,0 +1,98 @@ +#ifndef SCHED_H +#define SCHED_H + +#include "list.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x10000 +#define KSTACK_SIZE 0x10000 +#define SIGNAL_MAX 64 + +typedef void (*signal_handler_t)(void); + +typedef enum thread_status { + NEW, + RUNNING, + DEAD, +} thread_status_t; + +// callee-saved reg +// other reg are already on stack +typedef struct thread_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; + unsigned long lr; + unsigned long sp; + void *ttbr0_el1; +} thread_context_t; + +typedef struct thread +{ + /* Run queue, wait queue*/ + list_head_t listhead; + + /* Context*/ + // el0 sp -> user_sp + // el1 sp -> kernel_sp + // context sp : sp of this thread + // context switch may use context sp so it stores kernel_sp + thread_context_t context; + + /* Data info */ + // function or program location + char *data; + unsigned int datasize; + + /* Thread status*/ + int pid; + thread_status_t status; + + /* Stack pointers*/ + // el0 call function + char *user_sp; + // el1 call function, load all | save all + char *kernel_sp; + + /* Signal */ + // registered signal call back function + signal_handler_t signal_handler[SIGNAL_MAX + 1]; + // registered signal number + int sigcount[SIGNAL_MAX + 1]; + signal_handler_t curr_signal_handler; + // prevent nested running signal handler + // set true when kernel checking signal + int signal_is_checking; + // before running handler + // save origin context + thread_context_t signal_saved_context; + + /* VMA */ + list_head_t vma_list; +} thread_t; + +// track curr_thread +extern thread_t *curr_thread; +extern list_head_t *run_queue; +extern list_head_t *wait_queue; +extern thread_t threads[PIDMAX + 1]; + +void schedule_timer(char *s); +void init_thread_sched(); +void idle(); +void schedule(); +void kill_zombies(); +void thread_exit(); +thread_t *thread_create(void *start, unsigned int filesize); +int exec_thread(char *data, unsigned int filesize); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/shell.h b/lab6/kernel/include/shell.h new file mode 100644 index 000000000..1e25f3cc0 --- /dev/null +++ b/lab6/kernel/include/shell.h @@ -0,0 +1,9 @@ +#ifndef SHELL_H +#define SHELL_H + + +extern char *dtb_base; +void cmd(char *s1); +void shell(); +void print_system_messages(); +#endif \ No newline at end of file diff --git a/lab6/kernel/include/signal.h b/lab6/kernel/include/signal.h new file mode 100644 index 000000000..47a3418c7 --- /dev/null +++ b/lab6/kernel/include/signal.h @@ -0,0 +1,17 @@ +#ifndef SIGNAL_H +#define SIGNAL_H + +#define SIGKILL_NO 9 + +#define USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED 0xffffffff9000L + +#include "syscall.h" +#include "sched.h" +#include "malloc.h" + +void signal_default_handler(); +void check_signal(trapframe_t *tpf); +void run_signal(trapframe_t* tpf,int signal); +void signal_handler_wrapper(); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/sprintf.h b/lab6/kernel/include/sprintf.h new file mode 100644 index 000000000..042148da0 --- /dev/null +++ b/lab6/kernel/include/sprintf.h @@ -0,0 +1,8 @@ + +#ifndef SPRINTF_H +#define SPRINTF_H + +unsigned int sprintf(char *dst, char* fmt, ...); +unsigned int vsprintf(char *dst,char* fmt, __builtin_va_list args); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/stddef.h b/lab6/kernel/include/stddef.h new file mode 100644 index 000000000..d3f418ebb --- /dev/null +++ b/lab6/kernel/include/stddef.h @@ -0,0 +1,16 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define size_t unsigned long + +#ifndef __ASSEMBLER__ + +typedef struct{ + unsigned int iss : 25, // Instruction specific syndrome + il : 1, // Instruction length bit + ec : 6; // Exception class +} esr_el1_t; + +#endif //__ASSEMBLER__ + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/string.h b/lab6/kernel/include/string.h new file mode 100644 index 000000000..a0cd764f8 --- /dev/null +++ b/lab6/kernel/include/string.h @@ -0,0 +1,21 @@ +#ifndef STRING_H +#define STRING_H +#include "uint.h" +#include "stddef.h" +unsigned long long strlen(const char *str); +char *strcpy(char *dest, const char *src); +void _strcpy(char *dest, char *src); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, int n); +char* strtok(char* str, const char* delimiters); + +int isalpha(char c); +int isdigit(int c); +int toupper(int c); +int isxdigit(int c); +long long atoi(char *str); +unsigned int parse_hex_str(char *arr, int size); +char* strchr (register const char *s, int c); +void *memset(void *s, int c, size_t n); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/syscall.h b/lab6/kernel/include/syscall.h new file mode 100644 index 000000000..6cd4c13c3 --- /dev/null +++ b/lab6/kernel/include/syscall.h @@ -0,0 +1,20 @@ +#ifndef SYSCALL_H +#define SYSCALL_H + +#include "stddef.h" +#include "irq.h" + +int getpid(trapframe_t *tpf); +size_t uartread(trapframe_t *tpf,char buf[], size_t size); +size_t uartwrite(trapframe_t *tpf,const char buf[], size_t size); +int exec(trapframe_t *tpf,const char *name, char *const argv[]); +int fork(trapframe_t *tpf); +void exit(trapframe_t *tpf,int status); +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox); +void kill(trapframe_t *tpf,int pid); +void signal_register(int signal, void (*handler)()); +void signal_kill(int pid, int signal); +void sigreturn(trapframe_t *tpf); +void *sys_mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/system.h b/lab6/kernel/include/system.h new file mode 100644 index 000000000..48064121a --- /dev/null +++ b/lab6/kernel/include/system.h @@ -0,0 +1,11 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); +void reboot(); +int get_board_revision(unsigned int* board_revision); +int get_arm_memory_info(unsigned int* base_addr,unsigned int* size); +#endif \ No newline at end of file diff --git a/lab6/kernel/include/task.h b/lab6/kernel/include/task.h new file mode 100644 index 000000000..f289360bc --- /dev/null +++ b/lab6/kernel/include/task.h @@ -0,0 +1,21 @@ + +#ifndef TASK_H +#define TASK_H + +#include "list.h" + +typedef void (*task_callback_t)(void); + +typedef struct task { + list_head_t listhead; + int priority; + task_callback_t callback; +} task_t; + +extern list_head_t task_list; +void task_list_init() ; +void add_task(task_callback_t callback, int priority); +void pop_task(); + + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/timer.h b/lab6/kernel/include/timer.h new file mode 100644 index 000000000..52ae473eb --- /dev/null +++ b/lab6/kernel/include/timer.h @@ -0,0 +1,48 @@ +#ifndef TIMER_H +#define TIMER_H + +#include "list.h" + +//https://github.com/Tekki/raspberrypi-documentation/blob/master/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf p13 +#define CORE0_TIMER_IRQ_CTRL PHYS_TO_VIRT(0x40000040) +#define STR(x) #x +#define XSTR(x) STR(x) + +typedef void (*timer_callback_t)(char *); + +typedef struct timer_event +{ + list_head_t listhead; + unsigned long long expire_time; + timer_callback_t callback; + char *args; +} timer_event_t; + + + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_interrupt_enable(); +void core_timer_interrupt_disable(); +void cpu_timer_enable(); +void set_core_timer_interrupt(unsigned long long expire_time); +void set_core_timer_interrupt_by_tick(unsigned long long tick); +void set_core_timer_interrupt_first(); +void add_Node_timer(list_head_t *head, timer_event_t *entry); +void add_timer(timer_callback_t callback,void* arg, unsigned long long expire_time, int bytick); +void pop_timer() ; +int timer_list_size(); + +unsigned long long get_clock_tick(); +unsigned long long get_clock_freq(); +unsigned long long get_clock_time(); + +void two_second_alert(const char *str); +void core_timer_interrupt_disable_alternative(); +void core_timer_handler(); +void timer_event_callback(timer_event_t *timer_event); +void timer_list_init(); +unsigned long long get_tick_plus_s(unsigned long long second); +void print_timer(); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/uart.h b/lab6/kernel/include/uart.h new file mode 100644 index 000000000..42eec3b8f --- /dev/null +++ b/lab6/kernel/include/uart.h @@ -0,0 +1,52 @@ +#ifndef UART_H +#define UART_H + +#include "gpio.h" +#include "irq.h" +#include "sprintf.h" + +#define MAX_BUF_SIZE 0x1000 + +#define INT_SRC_0 +#define INT_AUX_RECV 0b00000100 +#define INT_AUX_TRAN 0b00000010 +#define INT_AUX_MASK 0b00000110 + +/* Auxilary mini UART registers */ +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +extern int echo; + +void init_idx(); +void uart_init(); +void uart_send(unsigned int c); +char uart_getc(); +void uart_puts(char *s); +void uart_hex(unsigned int d); +void uart_dec(int d); +int uart_printf(char *fmt, ...); + +void uart_async_putc(char c); +char uart_async_getc(); +int uart_async_printf(char *fmt, ...); + +void enable_mini_uart_interrupt(); +void enable_mini_uart_rx_interrupt(); +void disable_mini_uart_rx_interrupt(); +void enable_mini_uart_tx_interrupt(); +void disable_mini_uart_tx_interrupt(); + +void uart_tx_interrupt_handler(); +void uart_rx_interrupt_handler(); +#endif \ No newline at end of file diff --git a/lab6/kernel/include/uint.h b/lab6/kernel/include/uint.h new file mode 100644 index 000000000..4912f1a92 --- /dev/null +++ b/lab6/kernel/include/uint.h @@ -0,0 +1,26 @@ +#include "stddef.h" +#ifndef __int8_t_defined +# define __int8_t_defined +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +# if __WORDSIZE == 64 +typedef long int int64_t; +# else +__extension__ +typedef long long int int64_t; +# endif +#endif + +/* 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 +typedef unsigned long long int uint64_t; +#endif \ No newline at end of file diff --git a/lab6/kernel/include/utils.h b/lab6/kernel/include/utils.h new file mode 100644 index 000000000..f363c397f --- /dev/null +++ b/lab6/kernel/include/utils.h @@ -0,0 +1,9 @@ +#ifndef UTILS_H +#define UTILS_H + +#define STR(x) #x +#define XSTR(x) STR(x) + + + +#endif \ No newline at end of file diff --git a/lab6/kernel/kernel8.elf b/lab6/kernel/kernel8.elf new file mode 100644 index 000000000..a80bde8a2 Binary files /dev/null and b/lab6/kernel/kernel8.elf differ diff --git a/lab6/kernel/kernel8.img b/lab6/kernel/kernel8.img new file mode 100644 index 000000000..6ff8cd872 Binary files /dev/null and b/lab6/kernel/kernel8.img differ diff --git a/lab6/kernel/lib/boot.S b/lab6/kernel/lib/boot.S new file mode 100644 index 000000000..e95b57f22 --- /dev/null +++ b/lab6/kernel/lib/boot.S @@ -0,0 +1,103 @@ +#include "mmu.h" + +.section ".text.boot" + +.global _start + +_start: + // read cpu id, stop slave cores + // system register need use mrs + mrs x1, mpidr_el1 + and x1, x1, #3 + // switch to el1 before master + cbz x1, from_el2_to_el1 + // cpu id > 0, stop + +busyloop: + wfe + b busyloop + +from_el2_to_el1: + bl _from_el2_to_el1 + + +setup_kernel_vm: + // set paging configuration (up : 0xffffxxxxxxxxxxxx low : 0x0000xxxxxxxxxxxx) + ldr x4, = TCR_CONFIG_DEFAULT + + msr tcr_el1, x4 + + // set Used Memory Attributes + ldr x4, =((MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8))) + msr mair_el1, x4 + + // set and enable MMU + mov x4, 0x1000 // PGD's page frame at 0x1000 + mov x1, 0x2000 // PUD's page frame at 0x2000 + + // PGD 12 ~ 2 ignore + // set up PGD_attribute, pd table , 0b11 + ldr x2, = BOOT_PGD_ATTR + // physical | address | attribute | + orr x2, x1, x2 // combine the physical address of next level page with attribute. + // make first entry in PGD points to PUD + str x2, [x4] + + // set stack pointer location + mov sp, 0x3c000000 // startup stack + bl set_2M_kernel_mmu + + // load PGD to the bottom (user-space) translation-based register. + msr ttbr0_el1, x4 + // load PGD to the upper (kernel-space) translation based register. + msr ttbr1_el1, x4 + + // MMU enable for EL1 & 0 stage 1 address translation + // cache remains disable + // bits 0 : M + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 + + ldr x2, =master + br x2 + +master: + ldr x1, =(3 << 20) // close advanced SIMD exception + msr CPACR_EL1, x1 // printf builtin_va might use + adr x1, exception_vector_table // set exception_vector_table + msr vbar_el1, x1 + + // set top of stack at 0xffff00003c000000 (last usable memory) + movz x1, 0x0000 + movk x1, 0x3c00, lsl 16 + movk x1, 0xffff, lsl 48 + mov sp, x1 + + ldr x1, =__bss_start + ldr w2, =__bss_size + +bssinitloop: + cbz w2, kernel + // save 0 and plus 8 + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bssinitloop + +kernel: + // bl save next command to lr register + bl main + b busyloop + +_from_el2_to_el1: + // enable RW + mov x1, (1 << 31) // EL1 uses aarch64 + // decide execution state of el1 and el0 + msr hcr_el2, x1 + // 3c daif disabled + // 5 exception level + mov x1, 0x3c5 + // EL1h (SPSel = 1) with interrupt disabled + msr spsr_el2, x1 + msr elr_el2, lr // eret location + eret // return to EL1 ,str in elr_el2 \ No newline at end of file diff --git a/lab6/kernel/lib/cpio.c b/lab6/kernel/lib/cpio.c new file mode 100644 index 000000000..a6ca192ff --- /dev/null +++ b/lab6/kernel/lib/cpio.c @@ -0,0 +1,217 @@ +#include "cpio.h" +#include "uart.h" +#include "string.h" +#include "utils.h" +#include "sched.h" + +char *cpio_start; +char *cpio_end; + +// ubuntu man page cpio +/* Parse one file system object */ +/* write pathname,data,next header into corresponding parameter */ +/* if no next header, next_header_pointer = 0 */ +/* return -1 if parse error*/ +int cpio_newc_parse_header(struct cpio_newc_header *this_header_pointer, char **pathname, unsigned int *filesize, char **data, struct cpio_newc_header **next_header_pointer) +{ + // Ensure magic header 070701 + // new ascii format + if (strncmp(this_header_pointer->c_magic, CPIO_NEWC_HEADER_MAGIC, sizeof(this_header_pointer->c_magic)) != 0) + return -1; + + // transfer big endian 8 byte hex string to unsinged int + // data size + *filesize = parse_hex_str(this_header_pointer->c_filesize, 8); + + // end of header is the pathname + // header | pathname str | data + *pathname = ((char *)this_header_pointer) + sizeof(struct cpio_newc_header); + + // get file data, file data is just after pathname + // header | pathname str | data + // check picture on hackmd note + unsigned int pathname_length = parse_hex_str(this_header_pointer->c_namesize, 8); + // get the offset to start of data + // | offset | data + unsigned int offset = pathname_length + sizeof(struct cpio_newc_header); + // pathname and data might be zero padding + // section % 4 ==0 + offset = offset % 4 == 0 ? offset : (offset + 4 - offset % 4); // padding + // header pointer + offset = start of data + // h| offset | data + *data = (char *)this_header_pointer + offset; + + // get next header pointer + if (*filesize == 0) + // hardlinked files handeld by setting filesize to zero + *next_header_pointer = (struct cpio_newc_header *)*data; + else + { + // data size + offset = *filesize; + // move pointer to the end of data + *next_header_pointer = (struct cpio_newc_header *)(*data + (offset % 4 == 0 ? offset : (offset + 4 - offset % 4))); + } + + // if filepath is TRAILER!!! means there is no more files. + // end of archieve + // empty filename : TRAILER!!! + if (strncmp(*pathname, "TRAILER!!!", sizeof("TRAILER!!!")) == 0) + *next_header_pointer = 0; + + return 0; +} + +int execfile(char *thefilepath) +{ + char* filepath; + char* filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + while(header_pointer!=0) + { + int error = cpio_newc_parse_header(header_pointer,&filepath,&filesize,&filedata,&header_pointer); + //if parse header error + if(error) + { + uart_printf("error"); + break; + } + + // traverse until find the filepath + // program start address is data + // header | pathname str | data + if(!strcmp(thefilepath,filepath)) + { + // + exec_thread(filedata, filesize); + break; + } + + //if this is TRAILER!!! (last of file) + if (header_pointer == 0) + { + uart_printf("execfile: %s: No such file or directory\n", thefilepath); + return -1; + } + } + return 0; +} + +int cat(char *thefilepath) +{ + char *filepath; + char *filedata; + unsigned int filesize; + // current header pointer, cpio start + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + while (header_pointer) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // if parse header error + if (error) + { + uart_printf("error\n"); + break; + } + // parse until filepath is same as cat input + // print the content of input file + if (!strcmp(thefilepath, filepath)) + { + for (unsigned int i = 0; i < filesize; i++) + uart_printf("%c", filedata[i]); + uart_printf("\n"); + break; + } + // end of cpio, cannot find input file + if (header_pointer == 0) + uart_printf("cat: %s: No such file or directory\r\n", thefilepath); + } + return 0; +} + +int ls(char *working_dir) +{ + char *filepath; + char *filedata; + unsigned int filesize; + // current pointer + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + // print every cpio pathname + while (header_pointer) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // if parse header error + if (error) + { + uart_printf("error\n"); + break; + } + + // if this is not TRAILER!!! (last of file) + if (header_pointer != 0) + uart_printf("%s\n", filepath); + } + return 0; +} + +char* get_file_start(char *thefilepath) +{ + char *filepath; + char *filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + while (header_pointer != 0) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + //if parse header error + if (error) + { + uart_puts("error"); + break; + } + + if (strcmp(thefilepath, filepath) == 0) + { + return filedata; + } + + //if this is TRAILER!!! (last of file) + if (header_pointer == 0) + uart_printf("execfile: %s: No such file or directory\r\n", thefilepath); + } + return 0; +} + +unsigned int get_file_size(char *thefilepath) +{ + char *filepath; + char *filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + while (header_pointer != 0) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + //if parse header error + if (error) + { + uart_puts("error"); + break; + } + + if (strcmp(thefilepath, filepath) == 0) + { + return filesize; + } + + //if this is TRAILER!!! (last of file) + if (header_pointer == 0) + uart_printf("execfile: %s: No such file or directory\r\n", thefilepath); + } + return 0; +} \ No newline at end of file diff --git a/lab6/kernel/lib/dtb.c b/lab6/kernel/lib/dtb.c new file mode 100644 index 000000000..695faed83 --- /dev/null +++ b/lab6/kernel/lib/dtb.c @@ -0,0 +1,142 @@ +#include "dtb.h" +#include "uart.h" +#include "string.h" +#include "uart.h" +#include "string.h" +#include "mmu.h" + +char *dtb_base; +extern char *cpio_start; +extern char *cpio_end; +// arm is little endian, we need to change big to little +unsigned int endian_big2little(unsigned int x) { + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +} +// a tree data structure which indicating what devices are on a computer system. +// only find out node of initramfs and get the address +void fdt_traverse(dtb_callback callback) +{ + struct fdt_header *header = (struct fdt_header *)dtb_base; + // fdt header magic 0xD00DFEED (big-endian) + if (endian_big2little(header->magic) != 0xD00DFEED) + { + uart_puts("fdt_traverse: wrong magic in fdt_traverse\n"); + uart_printf("expect: 0XD00DFEED, get: %x\n", endian_big2little(header->magic)); + + return; + } + + // length in bytes of structure block section of dtb + unsigned int struct_size = endian_big2little(header->size_dt_struct); + + // check hackmd notes about the picture of DTB structure + // header is address of fdt_header, so we need (char *) + // offset in bytes of the structure block from beginning of header + // to locate struct start + char *dt_struct_ptr = (char *)((char *)header + endian_big2little(header->off_dt_struct)); + // offset in bytes of strings block from beginning of header + // to locate string start + // fdt_prop use string_ptr + nameoff to get the pathname + char *dt_strings_ptr = (char *)((char *)header + endian_big2little(header->off_dt_strings)); + + // parse from struct begin to end + char *end = (char *)dt_struct_ptr + struct_size; + char *pointer = dt_struct_ptr; + + // according to lexical structure + while (pointer < end) + { + // lexical big-endian-32-bit integer + // all tokens shall be alligned on 32-bit boundary + unsigned int token_type = endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // lexical structure + switch (token_type) + { + // begin of node's representation + case FDT_BEGIN_NODE: + // move node's unit name + callback(token_type, pointer, 0, 0); + pointer += strlen(pointer); + // node name is followed by zeroed padding bytes + // allign + pointer += (4 - (unsigned long long)pointer % 4); + break; + + // end of node's representation + case FDT_END_NODE: + //callback(token_type, 0, 0, 0); + break; + + case FDT_PROP: + + // len | name offset | address + // uint32_t + // length of prop values in byte + unsigned int len = endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // nameoff save offset of string blocks + // strings_ptr + nameoff get the name + char *name = (char *)dt_strings_ptr + endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // check node is initrd-start/end and set cpio_start/end address + callback(token_type, name, pointer, len); + // address, byte string of length len + pointer += len; + // followed by zeroed padding bytes + if ((unsigned long long)pointer % 4 != 0) + pointer += 4 - (unsigned long long)pointer % 4; // alignment 4 byte + break; + // ignore NOP + case FDT_NOP: + //callback(token_type, 0, 0, 0); + break; + // marks end of structures block + case FDT_END: + //callback(token_type, 0, 0, 0); + break; + default: + uart_printf("error type:%x\n", token_type); + return; + } + } +} + +void dtb_callback_show_tree(uint32_t node_type, char *name, void *data, uint32_t name_size) +{ + static int level = 0; + if (node_type == FDT_BEGIN_NODE) + { + for (int i = 0; i < level; i++) + uart_async_printf(" "); + uart_async_printf("%s{\n", name); + level++; + } + else if (node_type == FDT_END_NODE) + { + level--; + for (int i = 0; i < level; i++) + uart_async_printf(" "); + uart_async_printf("}\n"); + } + else if (node_type == FDT_PROP) + { + for (int i = 0; i < level; i++) + uart_async_printf(" "); + uart_async_printf("%s\n", name); + } +} + + +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size) +{ + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-start") == 0) + cpio_start = (void *)PHYS_TO_VIRT((unsigned long long)endian_big2little(*(uint32_t *)value)); + + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-end") == 0) + cpio_end = (void *)PHYS_TO_VIRT((unsigned long long)endian_big2little(*(uint32_t *)value)); + +} \ No newline at end of file diff --git a/lab6/kernel/lib/evt.S b/lab6/kernel/lib/evt.S new file mode 100644 index 000000000..8128c7bd0 --- /dev/null +++ b/lab6/kernel/lib/evt.S @@ -0,0 +1,208 @@ +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp ,16 * 0] + stp x2, x3, [sp ,16 * 1] + stp x4, x5, [sp ,16 * 2] + stp x6, x7, [sp ,16 * 3] + stp x8, x9, [sp ,16 * 4] + stp x10, x11, [sp ,16 * 5] + stp x12, x13, [sp ,16 * 6] + stp x14, x15, [sp ,16 * 7] + stp x16, x17, [sp ,16 * 8] + stp x18, x19, [sp ,16 * 9] + stp x20, x21, [sp ,16 * 10] + stp x22, x23, [sp ,16 * 11] + stp x24, x25, [sp ,16 * 12] + stp x26, x27, [sp ,16 * 13] + stp x28, x29, [sp ,16 * 14] + str x30, [sp, 16 * 15] + + //using for nested interrupt + mrs x0, spsr_el1 + str x0, [sp, 16 * 15 + 8] + mrs x0, elr_el1 + str x0, [sp, 16 * 16] + mrs x0, sp_el0 + str x0, [sp, 16 * 16 + 8] + + ldp x0, x1, [sp ,16 * 0] // restore x0 +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + + //using for nested interrupt + ldr x0, [sp, 16 * 15 + 8] + msr spsr_el1,x0 + ldr x0, [sp, 16 * 16] + msr elr_el1, x0 + ldr x0, [sp, 16 * 16 + 8] + msr sp_el0, x0 + + ldp x0, x1, [sp ,16 * 0] // restore x0 + + add sp, sp, 32 * 9 +.endm + +.macro ventry label + .align 7 + b \label +.endm + +.align 11 // vector table should be aligned to 0x800 +.global exception_vector_table +exception_vector_table: + + //Exception from the current EL while using SP_EL0 + ventry sync_el1t // Synchronous EL1t + ventry irq_el1t // IRQ EL1t + ventry fiq_el1t // FIQ EL1t + ventry error_el1t // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry sync_el1h // Synchronous EL1h + ventry irq_el1h // IRQ EL1h + ventry fiq_el1h // FIQ EL1h + ventry error_el1h // Error EL1h + + //Exception from a lower EL and at least one lower EL is AArch64 + ventry sync_el0_64 // Synchronous 64-bit EL0 + ventry irq_el0_64 // IRQ 64-bit EL0 + ventry fiq_el0_64 // FIQ 64-bit EL0 + ventry error_el0_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry sync_el0_32 // Synchronous 32-bit EL0 + ventry irq_el0_32 // IRQ 32-bit EL0 + ventry fiq_el0_32 // FIQ 32-bit EL0 + ventry error_el0_32 // Error 32-bit EL0 + +// Exception handlers +sync_el1t: + save_all + mov x0,0 // for debug + bl invalid_exception_handler + load_all + eret +irq_el1t: + save_all + mov x0,1 // for debug + bl invalid_exception_handler + load_all + eret +fiq_el1t: + save_all + mov x0,2 // for debug + bl invalid_exception_handler + load_all + eret +error_el1t: + save_all + mov x0,3 // for debug + bl invalid_exception_handler + load_all + eret + + + +sync_el1h: + save_all + mov x0,4 // for debug + bl invalid_exception_handler + load_all + eret +irq_el1h: + save_all + mov x0,sp + bl irq_handler + load_all + eret +fiq_el1h: + save_all + mov x0,6 // for debug + bl invalid_exception_handler + load_all + eret +error_el1h: + save_all + mov x0,7 // for debug + bl invalid_exception_handler + load_all + eret + + + +sync_el0_64: + save_all + mov x0,sp + mrs x1, esr_el1 + bl sync_el0_64_handler + + mov x0,sp // check signal when going back to user mode (el0) + bl check_signal + load_all + eret +irq_el0_64: + save_all + bl irq_handler + + mov x0,sp // check signal when going back to user mode (el0) + bl check_signal + load_all + eret +fiq_el0_64: + save_all + mov x0,10 // for debug + bl invalid_exception_handler + load_all + eret +error_el0_64: + save_all + mov x0,11 // for debug + bl invalid_exception_handler + load_all + eret + + + +sync_el0_32: + save_all + mov x0,12 // for debug + bl invalid_exception_handler + load_all + eret +irq_el0_32: + save_all + mov x0,13 // for debug + bl invalid_exception_handler + load_all + eret +fiq_el0_32: + save_all + mov x0,14 // for debug + bl invalid_exception_handler + load_all + eret +error_el0_32: + save_all + mov x0,15 // for debug + bl invalid_exception_handler + load_all + eret \ No newline at end of file diff --git a/lab6/kernel/lib/irq.c b/lab6/kernel/lib/irq.c new file mode 100644 index 000000000..87f73d921 --- /dev/null +++ b/lab6/kernel/lib/irq.c @@ -0,0 +1,184 @@ + +#include "irq.h" +#include "uart.h" +#include "sched.h" +#include "timer.h" +#include "task.h" +#include "signal.h" +#include "syscall.h" +#include "mmu.h" +#include "stddef.h" + + +#define UART_IRQ_PRIORITY 4 +#define TIMER_IRQ_PRIORITY 3 + +// unmask specific interrupt +void enable_interrupt() { + //uart_puts("ei daif\n"); + asm volatile("msr daifclr, 0xf"); + //uart_puts("eiii daif\n"); +} +// mask specific interrupt +void disable_interrupt() { + asm volatile("msr daifset, 0xf"); +} + +void irq_handler() +{ + // core0_int_src : 0x40000060 + // from aux && from GPU0 -> uart exception + // determine GPU interrupt : bit 8 + // arm peripherals interrupt table + // bit 29 AUX interrupt + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) + { + // bit[2:1] 01 tx + if (*AUX_MU_IIR & (0b01 << 1)) // TX can get data from tx buffer + { + disable_mini_uart_tx_interrupt(); + add_task(uart_tx_interrupt_handler, UART_IRQ_PRIORITY); + pop_task(); + } + // bit[2:1] 10 rx + else if (*AUX_MU_IIR & (0b10 << 1)) // kernel can get data from rx buffer + { + disable_mini_uart_rx_interrupt(); + add_task(uart_rx_interrupt_handler, UART_IRQ_PRIORITY); + pop_task(); + } + else + uart_printf("uart handler error\n"); + } + // check bit 1 to determine timer interrupt + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) + { + core_timer_interrupt_disable(); + //uart_printf("a1\n"); + add_task(core_timer_handler, TIMER_IRQ_PRIORITY); + //uart_printf("a2\n"); + pop_task(); + //uart_printf("a3\n"); + core_timer_enable(); + + // timer interrupt to be round robin + if (run_queue->next->next != run_queue) // runqueue size > 1 + schedule(); + } +} +void invalid_exception_handler(unsigned long long x0) { + uart_printf("invalid exception : 0x%x\n", x0); + uart_getc(); +} + +void cpacr_el1_off(){ + // bit 21 20 set 11 + asm volatile( + "mov x20, (3 << 20)\n\t" + "msr CPACR_EL1, x20\n\t" + ); +} + +void sync_el0_64_handler(trapframe_t *tpf, unsigned long x1) { + + esr_el1_t *esr = (esr_el1_t *)&x1; + // ec 0x20(instruction abort) 0x24(data abort) + // iss [5 : 0] 0b001LL (translation fault) + // ec : reason of exception + // iss : detailed reason of exception + if (esr->ec == DATA_ABORT_LOWER || esr->ec == INS_ABORT_LOWER) + { + handle_abort(esr); + return; + } + + + enable_interrupt(); + // get trapframe x8 + // which is system call number + unsigned long long syscall_no = tpf->x8; + + // by lab given spec + // arguments store in x0 x1 x2 ... + switch (syscall_no) + { + case 0: + getpid(tpf); + break; + case 1: + uartread(tpf, (char *)tpf->x0, tpf->x1); + break; + case 2: + uartwrite(tpf, (char *)tpf->x0, tpf->x1); + break; + case 3: + exec(tpf, (char *)tpf->x0, (char **)tpf->x1); + break; + case 4: + fork(tpf); + break; + case 5: + exit(tpf, tpf->x0); + break; + case 6: + syscall_mbox_call(tpf, (unsigned char)tpf->x0, (unsigned int *)tpf->x1); + break; + case 7: + kill(tpf, (int)tpf->x0); + break; + case 8: + signal_register(tpf->x0, (signal_handler_t)tpf->x1); + break; + case 9: + signal_kill(tpf->x0, tpf->x1); + break; + case 10: + sys_mmap(tpf, (void *)tpf->x0, tpf->x1, tpf->x2, tpf->x3, tpf->x4, tpf->x5); + break; + case 50: + // self-defined signal return + sigreturn(tpf); + break; + } +} + + +void highp() { + uart_async_printf("high prior start\n"); + uart_async_printf("high prior end\n"); + +} + +void lowp() { + uart_async_printf("low prior start\n"); + add_task(highp, 0); + uart_async_putc('\r'); // to trigger pop_task + for(int i = 0; i < 100000; ++i); + uart_async_printf("low prior end\n"); + for(int i = 0; i < 100000; ++i); +} + +void test_preemption() { + uart_async_printf("Starting test :\n"); + add_task(lowp, 9); + uart_async_putc('\r'); // to trigger pop_task +} + +static unsigned long long lock_count = 0; +void lock() +{ + disable_interrupt(); + lock_count++; +} + +void unlock() +{ + lock_count--; + if (lock_count<0) + { + uart_printf("lock error !!!\r\n"); + while(1); + } + if (lock_count == 0) + enable_interrupt(); +} \ No newline at end of file diff --git a/lab6/kernel/lib/list.c b/lab6/kernel/lib/list.c new file mode 100644 index 000000000..5acb8d1dd --- /dev/null +++ b/lab6/kernel/lib/list.c @@ -0,0 +1,64 @@ +#include "list.h" + +//list_head_t *list = (list_head_t*)smalloc(sizeof(list_head_t)); + + +void INIT_LIST_HEAD(list_head_t *head) +{ + head->next = head; + head->prev = head; +} + +int list_empty(const list_head_t *head) +{ + return head->next == head; +} + +static void __list_add(list_head_t *new_lst, list_head_t *prev, list_head_t *next) +{ + + next->prev = new_lst; + new_lst->next = next; + new_lst->prev = prev; + prev->next = new_lst; +} + +void list_insert(list_head_t *new_lst, list_head_t *prev, list_head_t *next) +{ + next->prev = new_lst; + new_lst->next = next; + new_lst->prev = prev; + prev->next = new_lst; +} + +void list_add(list_head_t *new_lst, list_head_t *head) +{ + __list_add(new_lst, head, head->next); +} + +void list_add_tail( list_head_t *new_lst, list_head_t *head) +{ + __list_add(new_lst, head->prev, head); +} + +static void __list_del(list_head_t * prev, list_head_t * next ) +{ + next->prev = prev; + prev->next = next; +} + +void list_del(list_head_t * entry) +{ + __list_del(entry->prev, entry->next); + +} + + +int list_size(const list_head_t *head) { + list_head_t *pos; + int i= 0; + list_for_each(pos, head) { + i++; + } + return i; +} diff --git a/lab6/kernel/lib/main.c b/lab6/kernel/lib/main.c new file mode 100644 index 000000000..fabef0695 --- /dev/null +++ b/lab6/kernel/lib/main.c @@ -0,0 +1,43 @@ +#include "dtb.h" +#include "uart.h" +#include "irq.h" +#include "cpio.h" +#include "list.h" +#include "timer.h" +#include "task.h" +#include "sched.h" +#include "mmu.h" +#include "mbox.h" + + +extern char *dtb_base; + + + +void main(char *arg) +{ + dtb_base = PHYS_TO_VIRT(arg); + // init list + init_idx(); + uart_init(); + task_list_init(); + fdt_traverse(initramfs_callback); + init_allocator(); + timer_list_init(); + + enable_interrupt(); + //enable_mini_uart_interrupt(); + + init_thread_sched(); + + core_timer_enable(); + + + + + uart_printf("Lab6 :\n"); + //fork_test(); + //kernel_main(); + execfile("vm.img"); + //shell(); +} \ No newline at end of file diff --git a/lab6/kernel/lib/malloc.c b/lab6/kernel/lib/malloc.c new file mode 100644 index 000000000..ee54d9487 --- /dev/null +++ b/lab6/kernel/lib/malloc.c @@ -0,0 +1,423 @@ +#include "malloc.h" +#include "cpio.h" +#include "mmu.h" +extern char _heap_start; +extern char _end; +static char *simple_top = &_heap_start; +static char *kernel_end = &_end; +extern char *dtb_base; + +// simple_malloc +void *smalloc(unsigned int size) +{ + char *r = simple_top + 0x10; + if (size < 0x18) + size = 0x18; // minimum size 0x20 //like ptmalloc + size = size + 0x7; + size = 0x10 + size - size % 0x10; + *(unsigned int *)(r - 0x8) = size; + simple_top += size; + return r; +} + +//buddy system allocator +//https://oscapstone.github.io/labs/lab4. +// use val(size) and isused with freelist is enough +//page size = 4K +/* For val (-2 ~ 6) +>=0 -> There is an allocable, contiguous memory that starts from the idx’th frame with size = 2**val * 4kb. +-1 -> allocated (deprecated) +-2 -> free, but it belongs to a larger contiguous memory block (deprecated) +*/ + +static frame_t* framearray; +static list_head_t freelist[MAXORDER + 1]; // 4K * (idx**ORDER) (for every 4K) (page) +static list_head_t cachelist[MAXCACHEORDER + 1]; // 32, 64, 128, 256, 512 (for every 32bytes) + +void init_allocator() +{ + // The usable memory region is from 0x00 to 0x3C000000, you can get this information from the memory node in devicetree. + // Advanced Exercise 3 - Startup Allocation - 20% + framearray = smalloc(BUDDYSYSTEM_PAGE_COUNT * sizeof(frame_t)); + + // init framearray + for (int i = 0; i < BUDDYSYSTEM_PAGE_COUNT; i++) + { + if (i % (1 << MAXORDER) == 0) + { + framearray[i].isused = 0; + framearray[i].val = 6; + } + } + + //init frame freelist + for (int i = 0; i <= MAXORDER; i++) + { + INIT_LIST_HEAD(&freelist[i]); + } + + for (int i = 0; i < BUDDYSYSTEM_PAGE_COUNT; i++) + { + //init listhead for each frame + INIT_LIST_HEAD(&framearray[i].listhead); + framearray[i].idx = i; + framearray[i].cacheorder = -1; + + //add init frame into freelist + if (i % (1 << MAXORDER) == 0) + { + list_add(&framearray[i].listhead, &freelist[MAXORDER]); + } + } + + //init cachelist + for (int i = 0; i <= MAXCACHEORDER; i++) + { + INIT_LIST_HEAD(&cachelist[i]); + } + + + /* should reserve these memory region + Spin tables for multicore boot (0x0000 - 0x1000) + Kernel image in the physical memory + Initramfs + Devicetree (Optional, if you have implement it) + Your simple allocator (startup allocator) + stack + */ + memory_reserve(PHYS_TO_VIRT(0x0000), PHYS_TO_VIRT(0x1000)); + memory_reserve(PHYS_TO_VIRT(0x1000), PHYS_TO_VIRT(0x5000)); // // PGD's page frame at 0x1000 // PUD's page frame at 0x2000 PMD 0x3000-0x5000 + memory_reserve(PHYS_TO_VIRT(0x80000), (unsigned long long)kernel_end); //kernel + memory_reserve((unsigned long long)&_heap_start, (unsigned long long)simple_top); //simple + memory_reserve((unsigned long long)cpio_start, (unsigned long long)cpio_end); + memory_reserve(PHYS_TO_VIRT(0x2c000000), PHYS_TO_VIRT(0x3c000000)); //0x2c000000L - 0x3c000000L (stack) +} + +//smallest 4K +void *allocpage(unsigned int size) +{ + // get real val size + //int allocsize; + int val; + for (int i = 0; i <= MAXORDER; i++) + { + + if (size <= (0x1000 << i)) + { + val = i; + break; + } + + if (i == MAXORDER) + { + uart_puts("Too large size for malloc!!!!\r\n"); + while (1); + return smalloc(size); + } + } + + // find the smallest larger frame in freelist + int target_list_val; + for (target_list_val = val; target_list_val <= MAXORDER; target_list_val++) + { + if (!list_empty(&freelist[target_list_val])) + break; + } + + if (target_list_val > MAXORDER) + { + uart_puts("malloc ERROR (all lists are empty?)!!!!\r\n"); + while (1); + return smalloc(size); + } + + //get the frame + frame_t *target_frame_ptr = (frame_t *)freelist[target_list_val].next; + list_del((struct list_head *)target_frame_ptr); + + // Release redundant memory block + for (int j = target_list_val; j > val; j--) + { + release_redundant(target_frame_ptr); + } + target_frame_ptr->isused = 1; +#ifdef DEBUG + uart_printf("allocpage ret : 0x%x, val : %d\r\n", BUDDYSYSTEM_START + (0x1000 * (target_frame_ptr->idx)), target_frame_ptr->val); +#endif + return (void *)BUDDYSYSTEM_START + (0x1000 * (target_frame_ptr->idx)); +} + +void freepage(void *ptr) +{ + frame_t *target_frame_ptr = &framearray[((unsigned long long)ptr - BUDDYSYSTEM_START) >> 12]; + +#ifdef DEBUG + uart_printf("freepage 0x%x, val = %d\r\n", ptr, target_frame_ptr->val); +#endif + + target_frame_ptr->isused = 0; + frame_t* temp; + while ((temp = coalesce(target_frame_ptr)) != (frame_t *)-1)target_frame_ptr = temp; + + list_add(&target_frame_ptr->listhead, &freelist[target_frame_ptr->val]); +} + +frame_t *release_redundant(frame_t *frame_ptr) +{ + frame_ptr->val -= 1; + frame_t *buddyptr = get_buddy(frame_ptr); + buddyptr->val = frame_ptr->val; + buddyptr->isused = 0; + list_add(&buddyptr->listhead, &freelist[buddyptr->val]); +#ifdef DEBUG + uart_printf("release_redundant idx = %d,%d\r\n", frame_ptr->idx, buddyptr->idx); +#endif + return frame_ptr; +} + +frame_t *get_buddy(frame_t *frame) +{ + return &framearray[frame->idx ^ (1 << frame->val)]; +} + +//return 0 -> success +//return -1 -> cannot coalesce +frame_t* coalesce(frame_t *frame_ptr) +{ + frame_t *buddy = get_buddy(frame_ptr); + + // MAXORDER + if (frame_ptr->val == MAXORDER) + return (frame_t*)-1; + + // val not the same (there is some chunks in the buddy used) + if (frame_ptr->val != buddy->val) + return (frame_t *)-1; + + //buddy is used + if (buddy->isused == 1) + return (frame_t *)-1; + + list_del((struct list_head *)buddy); + frame_ptr->val += 1; + buddy->val += 1; + +#ifdef DEBUG + uart_printf("coalesce idx = %d,%d\r\n", frame_ptr->idx, buddy->idx); +#endif + return buddy> 12]; + pageframe_ptr->cacheorder = order; + + // split page into a lot of caches and push them into cachelist + int cachesize = (32 << order); + for (int i = 0; i < 0x1000; i += cachesize) + { + list_head_t *c = (list_head_t *)(page + i); + list_add(c, &cachelist[order]); + } +} + +void freecache(void *ptr) +{ + list_head_t *c = (list_head_t *)ptr; + frame_t *pageframe_ptr = &framearray[((unsigned long long)ptr - BUDDYSYSTEM_START) >> 12]; + list_add(c, &cachelist[pageframe_ptr->cacheorder]); +#ifdef DEBUG + uart_printf("freecache 0x%x, order : %d\r\n", ptr, pageframe_ptr->cacheorder); +#endif +} +// malloc -> virtual -> 0xffff... +// because of linker script +void *malloc(unsigned int size) +{ + lock(); +#ifdef DEBUG + uart_printf("malloc size: %d\r\n", size); +#endif + //For page + if (size > (32 << MAXCACHEORDER)) + { + void *r = allocpage(size); +#ifdef DEBUG + dump_freelist_info(); + dump_cachelist_info(); +#endif + unlock(); + return r; + } + + void *r = alloccache(size); + +#ifdef DEBUG + dump_freelist_info(); + dump_cachelist_info(); + uart_printf("malloc ret 0x%x\r\n", r); +#endif + //For cache + unlock(); + return r; +} + +void free(void *ptr) +{ + lock(); +#ifdef DEBUG + uart_printf("free 0x%x\r\n", ptr); +#endif + //For page + if ((unsigned long long)ptr % 0x1000 == 0 && framearray[((unsigned long long)ptr - BUDDYSYSTEM_START) >> 12].cacheorder == -1) + { + freepage(ptr); +#ifdef DEBUG + dump_freelist_info(); + dump_cachelist_info(); +#endif + unlock(); + return; + } + + //For cache + freecache(ptr); +#ifdef DEBUG + dump_freelist_info(); + dump_cachelist_info(); +#endif + unlock(); +} + +void dump_freelist_info() +{ + for (int i = 0; i <= MAXORDER; i++) + uart_printf("freelist %d : %d\r\n", i, list_size(&freelist[i])); +} + +void dump_cachelist_info() +{ + for (int i = 0; i <= MAXCACHEORDER; i++) + uart_printf("cachelist %d : %d\r\n", i, list_size(&cachelist[i])); +} + +void memory_reserve(unsigned long long start, unsigned long long end) +{ + uart_printf("reserve -> start : 0x%x end : 0x%x\r\n", start, end); + start -= start % 0x1000; // floor (align 0x1000) + end = end % 0x1000 ? end + 0x1000 - (end % 0x1000) : end; // ceiling (align 0x1000) + + //delete page from freelist + for (int order = MAXORDER; order >= 0; order--) + { + list_head_t *pos; + list_for_each(pos, &freelist[order]) + { + unsigned long long pagestart = ((frame_t *)pos)->idx * 0x1000L + BUDDYSYSTEM_START; + unsigned long long pageend = pagestart + (0x1000L << order); + + if (start <= pagestart && end >= pageend) // if page all in reserved memory -> delete it from freelist + { + ((frame_t *)pos)->isused = 1; + list_del(pos); +#ifdef DEBUG + uart_printf("del order %d\r\n",order); + //dump_freelist_info(); +#endif + } + else if (start >= pageend || end <= pagestart) // no intersection + { + continue; + } + else // partial intersection (or reversed memory all in the page) + { + list_del(pos); + list_head_t *temppos = pos -> prev; + list_add(&release_redundant((frame_t *)pos)->listhead, &freelist[order - 1]); + pos = temppos; +#ifdef DEBUG + //dump_freelist_info(); +#endif + } + } + } +} + +void alloctest() +{ + uart_printf("alloc test\r\n"); + + //memory_reserve(0x1FFFAddb, 0x1FFFFdda); + + char *a = malloc(0x10); + char *b = malloc(0x100); + char *c = malloc(0x1000); + + free(a); + free(b); + free(c); + + a = malloc(32); + char *aa = malloc(50); + b = malloc(64); + char *bb = malloc(64); + c = malloc(128); + char *cc = malloc(129); + char *d = malloc(256); + char *dd = malloc(256); + char *e = malloc(512); + char *ee = malloc(999); + + free(a); + free(aa); + free(b); + free(bb); + free(c); + free(cc); + free(dd); + free(d); + free(e); + free(ee); +} + +void *memcpy(void *dest, const void *src, unsigned long long n) { + char *cdest = dest; + const char *csrc = src; + + while (n--) + { + *cdest++ = *csrc++; + } + + return dest; +} \ No newline at end of file diff --git a/lab6/kernel/lib/mbox.c b/lab6/kernel/lib/mbox.c new file mode 100644 index 000000000..03aa76daa --- /dev/null +++ b/lab6/kernel/lib/mbox.c @@ -0,0 +1,27 @@ +#include "mbox.h" +#include "uart.h" +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[72]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) // Mailbox 0 define several channels, but we only use channel 8 (CPU->GPU) for communication. +{ + unsigned long r = (((unsigned long)((unsigned long)mbox) & ~0xF) | (ch & 0xF)); + /* wait until we can write to the mailbox */ + do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_FULL); + /* write the address of our message to the mailbox with channel identifier */ + *MBOX_WRITE = r; + /* now wait for the response */ + while(1) { + /* is there a response? */ + do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_EMPTY); + /* is it a response to our message? */ + if(r == PHYS_TO_VIRT(*MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1]==MBOX_RESPONSE; + } + return 0; +} + diff --git a/lab6/kernel/lib/mmu.c b/lab6/kernel/lib/mmu.c new file mode 100644 index 000000000..906574738 --- /dev/null +++ b/lab6/kernel/lib/mmu.c @@ -0,0 +1,191 @@ +#include "mmu.h" +#include "malloc.h" +#include "sched.h" +#include "string.h" +#include "irq.h" +// PGD's page frame at 0x1000 +// PUD's page frame at 0x2000 +// PMD1's page frame at 0x3000 +// PMD2's page frame at 0x4000 +// two-level translation (1G) -> three-level translation (2MB) + +void *set_2M_kernel_mmu(void *x0) +{ + // setting up first PMD which maps to 0x0000000 ~ 0x3fffffff + + unsigned long *PMD = (unsigned long *)0x3000; + // 512 entries per page table + // 0x80000000 / 2MB = 1024 = 512 * 2, -> 2 PMD + for (int i = 0; i < 512; i++) + { + // 0x00000000 ~ 0x3F000000 normal RAM -> without cache + // normal RAM doesnt need PD_UKAccess or UNX or KNX + // gathering, reordering , speculative execution are possible + // if RAM set them, then el0 el1 cannot execute + // 0x3F000000 to 0x3FFFFFFF for GPU peripherals -> Device memory nGnRnE + // nG no gathering : memory access cannot merge + // nR no Reordering : strictly accroding to program order + // nE transaction ack must from end point + // every 2MB -> 3 level + if ((0x00000000 + (0x200000L) * i) >= 0x3F000000L) + PMD[i] = PD_ACCESS | PD_BLOCK | (0x00000000 + (0x200000L) * i) | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_UK_ACCESS | PD_UNX | PD_KNX; + else + PMD[i] = PD_ACCESS | PD_BLOCK | (0x00000000 + (0x200000L) * i) | (MAIR_IDX_NORMAL_NOCACHE << 2); + // 0x3c0000000 ~ 0x3f0000000 is GPU (mailbox ..etc) memory + } + + // setting up second PMD which maps to 0x40000000 ~ 0x7fffffff + unsigned long *PMD2 = (unsigned long *)0x4000; + for (int i = 0; i < 512; i++) + // 0x40000000 ~ 0x7fffffff ARM local peripherals -> Device memory nGnRnE + PMD2[i] = PD_ACCESS | PD_BLOCK | (0x40000000 + (0x200000L) * i) | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_UK_ACCESS | PD_UNX | PD_KNX; + + // PUD first entry point to first PMD + *(unsigned long *)(PUD) = PD_ACCESS | PD_TABLE | (unsigned long)PMD; + // PUD second entry point to second PMD + *(unsigned long *)(PUD + 0x8) = PD_ACCESS | PD_TABLE | (unsigned long)PMD2; + + return x0; +} + +// pa,va aligned to 4K +void map_one_page(size_t *virt_pgd_p, size_t va, size_t pa, size_t flag) +{ + size_t *table_p = virt_pgd_p; + // 4 level page table + for (int level = 0; level < 4; level++) + { + //get table index according descriptor + // 0x1ff first 9 bits + // shift right va to get to bit corresponding to current table + unsigned int idx = (va >> (39 - level * 9)) & 0x1ff; + // page level + if (level == 3) + { + // setting page + table_p[idx] = pa; + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_KNX | flag; + return; + } + + if (!table_p[idx]) + { + size_t *newtable_p = malloc(0x1000); + memset(newtable_p, 0, 0x1000); + // arm walk get virtual address + // change virtual newtable_p to physical + table_p[idx] = VIRT_TO_PHYS((size_t)newtable_p); + // normal memory no cache + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + // page table is set on linear mapped kernel space + table_p = (size_t *)PHYS_TO_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); + } +} + +void add_vma(thread_t *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced) +{ + // alignment 4k + size = size % 0x1000 ? size + (0x1000 - size % 0x1000) : size; + // malloc this page + // but the page table of user doesnt have an entry corresponding this page + vm_area_struct_t *new_area = malloc(sizeof(vm_area_struct_t)); + new_area->rwx = rwx; + new_area->area_size = size; + new_area->virt_addr = va; + new_area->phys_addr = pa; + new_area->is_alloced = is_alloced; + // memorize this vma, demand paging -> when use this vma, map to memory space + list_add_tail((list_head_t *)new_area, &t->vma_list); +} + +void free_page_tables(size_t *page_table, int level) +{ + size_t *table_virt = (size_t *)PHYS_TO_VIRT((char *)page_table); + // traverse page tables + for (int i = 0; i < 512; i++) + { + if (table_virt[i] != 0) + { + size_t *next_table = (size_t *)(table_virt[i] & ENTRY_ADDR_MASK); + if (table_virt[i] & PD_TABLE) + { + // recursion free + if (level != 2) + free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + free(PHYS_TO_VIRT((char *)next_table)); + } + } + } +} + +void handle_abort(esr_el1_t *esr_el1) +{ + /* + Holds the faulting Virtual Address for all synchronous Instruction or Data Abort, + PC alignment fault and Watchpoint exceptions that are taken to EL1. + get current fault vma + */ + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t" + : "=r"(far_el1)); + + // check this page fault page is in vma or not + list_head_t *pos; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + vm_area_struct_t *vma_tmp = (vm_area_struct_t *)pos; + if (vma_tmp->virt_addr <= far_el1 && (vma_tmp->virt_addr + vma_tmp->area_size) >= far_el1) + { + the_area_ptr = vma_tmp; + break; + } + } + + // if not in vma -> seg_fault + if (!the_area_ptr) + seg_fault(); + + // is in vma and iss is translation fault + // -> map this page + if ((esr_el1->iss & 0x3f) == TF_LEVEL0 || (esr_el1->iss & 0x3f) == TF_LEVEL1 || (esr_el1->iss & 0x3f) == TF_LEVEL2 || (esr_el1->iss & 0x3f) == TF_LEVEL3) + { + // current address + uart_printf("[Translation fault]: 0x%x\n", far_el1); + + size_t addr_offset = (far_el1 - the_area_ptr->virt_addr); + // allign + addr_offset = (addr_offset % 0x1000) == 0 ? addr_offset : addr_offset - (addr_offset % 0x1000); + map_one_page_rwx(PHYS_TO_VIRT(curr_thread->context.ttbr0_el1), the_area_ptr->virt_addr + addr_offset, the_area_ptr->phys_addr + addr_offset, the_area_ptr->rwx); + } + // iss is other fault -> seg_fault + else // For other Fault (permisson ...etc) + seg_fault(); +} + +void map_one_page_rwx(size_t *pgd_p, size_t va, size_t pa, size_t rwxflag) +{ + size_t flag = 0; + //set rwx and then map + // execute + if (!(rwxflag & 0b100)) + flag |= PD_UNX; + + // write + if (!(rwxflag & 0b10)) + flag |= PD_RDONLY; + + // read + if (rwxflag & 0b1) + flag |= PD_UK_ACCESS; + + map_one_page(pgd_p, va, pa, flag); +} + +void seg_fault() +{ + uart_printf("[Segmentation fault]: Kill Process\r\n"); + thread_exit(); +} \ No newline at end of file diff --git a/lab6/kernel/lib/sched.S b/lab6/kernel/lib/sched.S new file mode 100644 index 000000000..2513e1890 --- /dev/null +++ b/lab6/kernel/lib/sched.S @@ -0,0 +1,62 @@ + +// tpidr_el1 : per cpu offset +// MSB 2 bit : thread id + +.global switch_to +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] + // get translation based address + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + + msr tpidr_el1, x1 + + // remember to switch ttbr0_el1 + // need memory barriers + // gurantee previos instructions are finished + // TLB invalidation -> old value are staled + dsb ish // ensure write has completed + msr ttbr0_el1, x0 // switch translation based address. + tlbi vmalle1is // invalidate all TLB entries + dsb ish // ensure completion of TLB invalidatation + isb // clear pipeline + + ret + +.global store_context +store_context: + 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] + ret + +.global load_context +load_context: + ldp x19, x20, [x0, 16 * 0] + ldp x21, x22, [x0, 16 * 1] + ldp x23, x24, [x0, 16 * 2] + ldp x25, x26, [x0, 16 * 3] + ldp x27, x28, [x0, 16 * 4] + ldp fp, lr, [x0, 16 * 5] + ldr x9, [x0, 16 * 6] + mov sp, x9 + ret \ No newline at end of file diff --git a/lab6/kernel/lib/sched.c b/lab6/kernel/lib/sched.c new file mode 100644 index 000000000..58cb3c8ba --- /dev/null +++ b/lab6/kernel/lib/sched.c @@ -0,0 +1,197 @@ +#include "sched.h" +#include "irq.h" +#include "malloc.h" +#include "timer.h" +#include "uart.h" +#include "mmu.h" +#include "current.h" +#include "signal.h" +#include "string.h" + +thread_t *curr_thread; +list_head_t *run_queue; +list_head_t *wait_queue; +thread_t threads[PIDMAX + 1]; + +void init_thread_sched() +{ + lock(); + // malloc run & wait queue + run_queue = malloc(sizeof(list_head_t)); + wait_queue = malloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + INIT_LIST_HEAD(wait_queue); + + // init pids + // 類似 thread pool + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].pid = i; + threads[i].status = NEW; + } + // set current tmp thread + thread_t *tmp = malloc(sizeof(thread_t)); + // set tpidr_el1 + set_current_ctx(&tmp->context); + curr_thread = tmp; + + // create idle + thread_create(idle, 0x1000); + unlock(); +} + +void idle() { + while (1) { + // reclaim threads marked as DEAD + kill_zombies(); + // switch to next thread in run_queue + schedule(); + } +} + +void schedule() { + lock(); + do { + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue) || curr_thread->status == DEAD); + // switch to next thread + + unlock(); + switch_to(current_ctx, &curr_thread->context); +} + +void kill_zombies() +{ + lock(); + list_head_t *tmp; + list_for_each(tmp, run_queue) + { + thread_t *tmp_thread = (thread_t *)tmp; + if (tmp_thread->status == DEAD) + { + list_del(tmp); + // free(tmp_thread->user_sp); // free stack + free(tmp_thread->kernel_sp); // free stack + free_page_tables(tmp_thread->context.ttbr0_el1, 0); + // free(tmp_thread->data); // free data + + list_head_t *vma_tmp = tmp_thread->vma_list.next; + while (vma_tmp != &tmp_thread->vma_list) + { + // if is_alloced free malloced vma + if (((vm_area_struct_t *)vma_tmp)->is_alloced) + free((void *)PHYS_TO_VIRT(((vm_area_struct_t *)vma_tmp)->phys_addr)); + + list_head_t *next_vma = vma_tmp->next; + free(vma_tmp); + vma_tmp = next_vma; + } + + free(PHYS_TO_VIRT(tmp_thread->context.ttbr0_el1)); // free PGD + + tmp_thread->status = NEW; + } + } + unlock(); +} + +int exec_thread(char *data, unsigned int filesize) +{ + thread_t *t = thread_create(data, filesize); + + add_vma(t, 0x3C000000L, 0x3000000L, 0x3C000000L, 3, 0); // device + add_vma(t, 0xffffffffb000, 0x4000, (size_t)VIRT_TO_PHYS(t->user_sp), 7, 1); // stack + add_vma(t, 0x0, filesize, (size_t)VIRT_TO_PHYS(t->data), 7, 1); // text + add_vma(t, USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED, 0x2000, (size_t)VIRT_TO_PHYS(signal_handler_wrapper), 5, 0); // for signal wrapper + + t->context.ttbr0_el1 = VIRT_TO_PHYS(t->context.ttbr0_el1); + t->context.sp = 0xfffffffff000; + t->context.fp = 0xfffffffff000; + t->context.lr = 0L; + + memcpy(t->data, data, filesize); + + // disable echo when going to userspace + curr_thread = t; + // different timer + add_timer(schedule_timer, "", 1, 0); + // eret to exception level 0 + __asm__ __volatile__("msr tpidr_el1, %0\n\t" + "msr elr_el1, %1\n\t" + "msr spsr_el1, xzr\n\t" // enable interrupt in EL0. You can do it by setting spsr_el1 to 0 before returning to EL0. + "msr sp_el0, %2\n\t" + "mov sp, %3\n\t" + "mov fp, sp\n\t" + "dsb ish\n\t" // ensure write has completed + "msr ttbr0_el1, %4\n\t" + // need memory barriers + // gurantee previos instructions are finished + // TLB invalidation -> old value are staled + "tlbi vmalle1is\n\t" // invalidate all TLB entries + "dsb ish\n\t" // ensure completion of TLB invalidatation + "isb\n\t" // clear pipeline" + "eret\n\t" ::"r"(&t->context), + "r"(t->context.lr), "r"(t->context.sp), "r"(t->kernel_sp + KSTACK_SIZE), "r"(t->context.ttbr0_el1)); + + return 0; +} + +thread_t *thread_create(void *start, unsigned int filesize) +{ + lock(); + + thread_t *r; + for (int i = 0; i <= PIDMAX; i++) + { + if (threads[i].status == NEW) + { + r = &threads[i]; + break; + } + } + INIT_LIST_HEAD(&r->vma_list); + r->status = RUNNING; + r->context.lr = (unsigned long long)start; + r->user_sp = malloc(USTACK_SIZE); + r->kernel_sp = malloc(KSTACK_SIZE); + r->signal_is_checking = 0; + r->data = malloc(filesize); + r->datasize = filesize; + r->context.sp = (unsigned long long)r->kernel_sp + KSTACK_SIZE; + r->context.fp = r->context.sp; + // create one PGD (page table for this process) + // set PGD to 0 + // context.ttbr0_el1 point to this PGD + r->context.ttbr0_el1 = malloc(0x1000); + memset(r->context.ttbr0_el1, 0, 0x1000); + + // initial signal handler with signal_default_handler (kill thread) + for (int i = 0; i < SIGNAL_MAX; i++) + { + r->signal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add(&r->listhead, run_queue); + unlock(); + return r; +} + +void thread_exit() { + // set dead + lock(); + curr_thread->status = DEAD; + unlock(); + // move on + schedule(); +} + +// timer +// set expired time +void schedule_timer(char *notuse) { + unsigned long long cntfrq_el0; + // Set the expired time as core timer frequency shift right 5 bits. + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" + : "=r"(cntfrq_el0)); // tick frequency + add_timer(schedule_timer, "", cntfrq_el0 >> 5, 1); +} \ No newline at end of file diff --git a/lab6/kernel/lib/shell.c b/lab6/kernel/lib/shell.c new file mode 100644 index 000000000..9965ef5c0 --- /dev/null +++ b/lab6/kernel/lib/shell.c @@ -0,0 +1,162 @@ +#include "uart.h" +#include "string.h" +#include "shell.h" +#include "mbox.h" +#include "system.h" +#include "cpio.h" +#include "dtb.h" +#include "timer.h" +#include "task.h" + +void cmd(char *s1) { + + char *token; + char arg[20][50]; + + for(int i = 0; i < 20; ++i) + for(int j = 0; j < 50; ++j) + arg[i][j] = '\0'; + + int i = 0; + token = strtok(s1, " "); + + while(token != 0) { + strcpy(arg[i], token); + //uart_puts(arg[i]); + ++i; + //uart_send('0'+i); + token = strtok(0, " "); + } + + if(!strcmp(arg[0], "help") && i == 1) { + uart_async_printf("cat \t\t\t: cat\n"); + uart_async_printf("clear \t\t\t: clear all\n"); + uart_async_printf("exec \t\t\t: execute test file\n"); + uart_async_printf("help \t\t\t: print this help menu\n"); + uart_async_printf("hello \t\t\t: print Hello World!\n"); + uart_async_printf("ls \t\t\t: ls\n"); + uart_async_printf("mailbox\t\t\t: show infos of board revision and ARM memory\n"); + uart_async_printf("malloc \t\t\t: allocate string abc\n"); + uart_async_printf("reboot \t\t\t: reboot the device\n"); + uart_async_printf("sto [MESSAGE] [SECONDS]\t: print message after [SECONDS] seconds\n"); + uart_async_printf("tsa [MESSAGE] \t\t: set an alarm that alert every two seconds\n"); + uart_async_printf("pfa \t\t\t: test page frame allocator\n"); + uart_async_printf("csa \t\t\t: test chunk slot allocator\n"); + } + else if(!strcmp(arg[0], "hello") && i == 1) { + uart_puts("Hello World!\n"); + } + else if(!strcmp(arg[0], "mailbox") && i == 1) { + print_system_messages(); + } + else if(!strcmp(arg[0], "reboot") && i == 1) { + reset(1); + } + else if(!strcmp(arg[0], "cat") && i == 2) { + + cat(arg[1]); + //uart_puts(arg[1]); + } + else if(!strcmp(arg[0], "clear") && i == 1) { + uart_puts("\x1b[2J\x1b[H"); + } + else if(!strcmp(arg[0], "ls") && i == 1) { + ls("."); + //uart_puts(arg[1]); + } + else if(!strcmp(arg[0], "malloc")) { + uart_puts("Allocating string abc\n"); + char *st = (char*)malloc(16); + // uart_puts("test1\n"); + st[0] = 'a'; + st[1] = 'b'; + st[2] = 'c'; + st[3] = '\0'; + uart_printf("%s\n", st); + } + else if(!strcmp(arg[0], "exec") && i == 1) { + execfile(arg[1]); + } + else if (!strcmp(arg[0], "sto") && i == 3) { + // setTimeout MESSAGE SECONDS + + uart_async_printf("'%s' Set Timeout : %d\n", arg[1], atoi(arg[2]) * get_clock_freq() + get_clock_tick()); + add_timer(uart_puts, arg[1], atoi(arg[2]), 0); + } + else if (!strcmp(arg[0], "tsa") && i == 2) { + // tsa MESSAGE + + add_timer(two_second_alert, arg[1], 2, 0); + + } + else if (!strcmp(arg[0], "async") && i == 1) { + char c = ' '; + uart_async_printf("Press enter to exit\n"); + while(c != '\n' && c != '\r') { + c = uart_async_getc(); + uart_async_putc(c); + } + uart_async_printf("\n"); + + } + else if(!strcmp(arg[0], "preempt") && i == 1) { + test_preemption(); + } + else if(!strcmp(arg[0], "pfa") && i == 1) { + alloctest(); + } + else if(!strcmp(arg[0], "csa") && i == 1) { + alloctest(); + } + else { + uart_async_printf("Unknown command: %s\n", s1); + } +} + +void shell() { + + while(1) { + uart_puts("# "); + + int i = 0; + char str[50] = {}; + char c = ' '; + + while( c != '\n') { + c = uart_getc(); + + if(c == '\n') { + uart_puts("\n"); + } + else { + uart_send(c); + } + if(c == 0x08 || c == 0x7f && i > 0) { + uart_send('\b'); + uart_send(' '); + uart_send('\b'); + str[strlen(str) - 1] = '\0'; + --i; + } + + if(c != '\n' && c != 0x08 && c != 0x7f) + str[i++] = c; + + } + cmd(str); + + } +} +void print_system_messages() +{ + unsigned int board_revision; + get_board_revision(&board_revision); + uart_async_printf("Board revision is : 0x%x\n", board_revision); + + unsigned int arm_mem_base_addr; + unsigned int arm_mem_size; + + get_arm_memory_info(&arm_mem_base_addr, &arm_mem_size); + uart_async_printf("ARM memory base address in bytes : 0x%x\n", arm_mem_base_addr); + uart_async_printf("ARM memory size in bytes : 0x%x\n", arm_mem_size); +} \ No newline at end of file diff --git a/lab6/kernel/lib/signal.c b/lab6/kernel/lib/signal.c new file mode 100644 index 000000000..ce92a64d9 --- /dev/null +++ b/lab6/kernel/lib/signal.c @@ -0,0 +1,61 @@ +#include "signal.h" + +void check_signal(trapframe_t *tpf) +{ + lock(); + if (curr_thread->signal_is_checking) + { + unlock(); + return; + } + // prevent nested running signal handler + curr_thread->signal_is_checking = 1; + unlock(); + for (int i = 0; i <= SIGNAL_MAX; i++) + { + store_context(&curr_thread->signal_saved_context); + if (curr_thread->sigcount[i] > 0) + { + lock(); + curr_thread->sigcount[i]--; + unlock(); + run_signal(tpf, i); + } + } + lock(); + curr_thread->signal_is_checking = 0; + unlock(); +} + +void run_signal(trapframe_t *tpf, int signal) +{ + curr_thread->curr_signal_handler = curr_thread->signal_handler[signal]; + + // run default handler in kernel + if (curr_thread->curr_signal_handler == signal_default_handler) + { + signal_default_handler(); + return; + } + + asm("msr elr_el1, %0\n\t" + "msr sp_el0, %1\n\t" + "msr spsr_el1, %2\n\t" + "mov x0, %3\n\t" + "eret\n\t" ::"r"(USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED + ((size_t)signal_handler_wrapper % 0x1000)), + "r"(tpf->sp_el0), + "r"(tpf->spsr_el1), "r"(curr_thread->curr_signal_handler)); +} + +void signal_handler_wrapper() +{ + // call function and system call sigreturn + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void signal_default_handler() +{ + kill(0, curr_thread->pid); +} \ No newline at end of file diff --git a/lab6/kernel/lib/sprintf.c b/lab6/kernel/lib/sprintf.c new file mode 100644 index 000000000..63533303f --- /dev/null +++ b/lab6/kernel/lib/sprintf.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/** + * minimal sprintf implementation + */ + +#include "uart.h" +#include "string.h" + +unsigned int vsprintf(char *dst, char* fmt, __builtin_va_list args) +{ + long int arg; + int len, sign, i; + char *p, *orig=dst, tmpstr[19]; + + // failsafes + if(dst==(void*)0 || fmt==(void*)0) { + return 0; + } + + // main loop + arg = 0; + while(*fmt) { + if (strlen(fmt) > MAX_BUF_SIZE - 0x10 || dst - orig > MAX_BUF_SIZE - 0x10) + { + uart_puts("Error!!! format string too long!!!!"); + *dst = 0; + return dst - orig; + } + // argument access + if(*fmt=='%') { + fmt++; + // literal % + if(*fmt=='%') { + goto put; + } + len=0; + // size modifier + while(*fmt>='0' && *fmt<='9') { + len *= 10; + len += *fmt-'0'; + fmt++; + } + // skip long modifier + if(*fmt=='l') { + fmt++; + } + // character + if(*fmt=='c') { + arg = __builtin_va_arg(args, int); + *dst++ = (char)arg; + fmt++; + continue; + } else + // decimal number + if(*fmt=='d') { + arg = __builtin_va_arg(args, int); + // check input + sign=0; + if((int)arg<0) { + arg*=-1; + sign++; + } + if(arg>99999999999999999L) { + arg=99999999999999999L; + } + // convert to string + i=18; + tmpstr[i]=0; + do { + tmpstr[--i]='0'+(arg%10); + arg/=10; + } while(arg!=0 && i>0); + if(sign) { + tmpstr[--i]='-'; + } + // padding, only space + if(len>0 && len<18) { + while(i>18-len) { + tmpstr[--i]=' '; + } + } + p=&tmpstr[i]; + goto copystring; + } else + // hex number + if(*fmt=='x') { + arg = __builtin_va_arg(args, long int); + // convert to string + i=16; + tmpstr[i]=0; + do { + char n=arg & 0xf; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + tmpstr[--i]=n+(n>9?0x37:0x30); + arg>>=4; + } while(arg!=0 && i>0); + // padding, only leading zeros + if(len>0 && len<=16) { + while(i>16-len) { + tmpstr[--i]='0'; + } + } + p=&tmpstr[i]; + goto copystring; + } else + // string + if(*fmt=='s') { + p = __builtin_va_arg(args, char*); +copystring: if(p==(void*)0) { + p="(null)"; + } + while(*p) { + *dst++ = *p++; + } + } + } else { +put: *dst++ = *fmt; + } + fmt++; + } + *dst=0; + // number of bytes written + return dst-orig; +} + +/** + * Variable length arguments + */ +unsigned int sprintf(char *dst, char* fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + unsigned int r = vsprintf(dst,fmt,args); + __builtin_va_end(args); + return r; +} \ No newline at end of file diff --git a/lab6/kernel/lib/string.c b/lab6/kernel/lib/string.c new file mode 100644 index 000000000..47ba7548f --- /dev/null +++ b/lab6/kernel/lib/string.c @@ -0,0 +1,161 @@ +#include +#define NULL ((void *)0) + + +int isalpha(char c){ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +int isdigit(int c) { + return c >= '0' && c <= '9'; +} + +int toupper(int c) { + if (c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } else { + return c; + } +} + +int isxdigit(int c) { + return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F'); +} + +unsigned long long strlen(const char *str) { + + unsigned long long len = 0; + while((unsigned char)*str++) { + ++len; + } + + return len; +} + + +char *strcpy(char *dest, const char *src) { + char *ret = dest; + while ((*dest++ = *src++)); + return ret; +} + +void _strcpy(char *dest, char *src){ + if (dest==NULL||src==NULL) + return; + + while(*src!=NULL){ + *dest = *src; + dest++; + src++; + } +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && *s2 && (*s1 == *s2)) { + s1++; + s2++; + } + return *s1 - *s2; +} + +int strncmp(const char *s1, const char *s2, int n) { + while (n-- && *s1 && (*s1 == *s2)) { + s1++; + s2++; + } + if (n == (int) -1) { + return 0; + } + return *(const unsigned char *) s1 - *(const unsigned char *) s2; +} + +char* strtok(char* str, const char* delimiters) { + static char* buffer = 0; + if (str != 0) { + buffer = str; + } + if (buffer == 0) { + return 0; + } + char* start = buffer; + while (*buffer != '\0') { + const char* delim = delimiters; + while (*delim != '\0') { + if (*buffer == *delim) { + *buffer = '\0'; + buffer++; + if (start != buffer) { + return start; + } else { + start++; + break; + } + } + delim++; + } + if (*delim == '\0') { + buffer++; + } + } + if (start == buffer) { + return 0; + } else { + return start; + } +} + +long long atoi(char* str) { + long long result = 0; + long long sign = 1; + long long i = 0; + + if (str[0] == '-') { + sign = -1; + i++; + } + + for (; str[i] != '\0'; ++i) { + if (str[i] >= '0' && str[i] <= '9') { + result = result * 10 + (str[i] - '0'); + } + else { + + return 0; + } + } + + return sign * result; +} + +unsigned int parse_hex_str(char *arr, int size) { + unsigned int result = 0; + + for (int i = 0; i < size; i++) { + char c = arr[i]; + if (isxdigit(c)) { + int val = isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; + result = (result << 4) + val; + } + } + + return result; +} + +char* strchr (register const char *s, int c) { + do { + if (*s == c) { + return (char*)s; + } + } while (*s++); + return (0); +} + + +void *memset(void *s, int c, size_t n) { + char *start = s; + for (size_t i = 0; i < n; i++) { + start[i] = c; + } + + return s; +} \ No newline at end of file diff --git a/lab6/kernel/lib/syscall.c b/lab6/kernel/lib/syscall.c new file mode 100644 index 000000000..92fefbb63 --- /dev/null +++ b/lab6/kernel/lib/syscall.c @@ -0,0 +1,253 @@ +#include "syscall.h" +#include "sched.h" +#include "stddef.h" +#include "uart.h" +#include "cpio.h" +#include "irq.h" +#include "malloc.h" +#include "mbox.h" +#include "signal.h" +#include "current.h" +#include "mmu.h" +#include "string.h" + + +int getpid(trapframe_t *tpf) +{ + tpf->x0 = curr_thread->pid; + return curr_thread->pid; +} + +size_t uartread(trapframe_t *tpf, char buf[], size_t size) +{ + int i = 0; + for (int i = 0; i < size; i++) + buf[i] = uart_getc(); + + tpf->x0 = i; + return i; +} + +size_t uartwrite(trapframe_t *tpf, const char buf[], size_t size) +{ + int i = 0; + for (int i = 0; i < size; i++) + uart_send(buf[i]); + + tpf->x0 = i; + return i; +} + +// In this lab, you won’t have to deal with argument passing +int exec(trapframe_t *tpf, const char *name, char *const argv[]) +{ + // free alloced area and vma struct + list_head_t *pos = curr_thread->vma_list.next; + while (pos != &curr_thread->vma_list) + { + // is_alloced -> malloc + if (((vm_area_struct_t *)pos)->is_alloced) + free((void *)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr)); + + list_head_t *next_pos = pos->next; + free(pos); + pos = next_pos; + } + + // exec program + INIT_LIST_HEAD(&curr_thread->vma_list); + curr_thread->datasize = get_file_size((char *)name); + char *new_data = get_file_start((char *)name); + curr_thread->data = malloc(curr_thread->datasize); + curr_thread->user_sp = malloc(USTACK_SIZE); + + // flush tlb + // malloc new ttbr0_el1 + __asm__ __volatile__("dsb ish\n\t"); // ensure write has completed + free_page_tables(curr_thread->context.ttbr0_el1, 0); + // new ttbr0_el1 + memset(PHYS_TO_VIRT(curr_thread->context.ttbr0_el1), 0, 0x1000); + __asm__ __volatile__("tlbi vmalle1is\n\t" // invalidate all TLB entries + "dsb ish\n\t" // ensure completion of TLB invalidatation + "isb\n\t"); // clear pipeline + + // remap code + add_vma(curr_thread, 0, curr_thread->datasize, (size_t)VIRT_TO_PHYS(curr_thread->data), 7, 1); + // remap stack + add_vma(curr_thread, 0xffffffffb000, 0x4000, (size_t)VIRT_TO_PHYS(curr_thread->user_sp), 7, 1); + // videocore memory (mailbox) + add_vma(curr_thread, 0x3C000000L, 0x3000000L, 0x3C000000L, 3, 0); + // for signal wrapper + add_vma(curr_thread, USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED, 0x2000, (size_t)VIRT_TO_PHYS(signal_handler_wrapper), 5, 0); + + // copy file into data + memcpy(curr_thread->data, new_data, curr_thread->datasize); + + // clear signal handler + for (int i = 0; i <= SIGNAL_MAX; i++) + curr_thread->signal_handler[i] = signal_default_handler; + + // set elr sp in user space + tpf->elr_el1 = 0; + tpf->sp_el0 = 0xfffffffff000; + tpf->x0 = 0; + return 0; +} + +int fork(trapframe_t *tpf) +{ + // addvma : thread + // code : 0x0 + // user stack : 0xffffffffb000 + lock(); + thread_t *child_thread = thread_create(curr_thread->data, curr_thread->datasize); + + // copy signal handler + for (int i = 0; i <= SIGNAL_MAX; i++) + child_thread->signal_handler[i] = curr_thread->signal_handler[i]; + + // copy parent's vma except signal wrapper and video core memory + list_head_t *pos; + list_for_each(pos, &curr_thread->vma_list) + { + vm_area_struct_t *cur_vma = (vm_area_struct_t *)pos; + if (cur_vma->virt_addr == USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED || cur_vma->virt_addr == 0x3C000000) + continue; + + char *new_alloc = malloc(cur_vma->area_size); // original buddy system buggy. + add_vma(child_thread, cur_vma->virt_addr, cur_vma->area_size, (size_t)VIRT_TO_PHYS(new_alloc), cur_vma->rwx, 1); + + memcpy(new_alloc, (void *)PHYS_TO_VIRT(cur_vma->phys_addr), cur_vma->area_size); + } + // video core and signal wrapper are not malloc + // every video core memory and signal wrapper of thread point to the same memory + // videocore memory (mailbox) + add_vma(child_thread, 0x3C000000L, 0x3000000L, 0x3C000000L, 3, 0); + // for signal wrapper + add_vma(child_thread, USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED, 0x2000, (size_t)VIRT_TO_PHYS(signal_handler_wrapper), 5, 0); // for signal wrapper + + int parent_pid = curr_thread->pid; + + // copy stack into new process + memcpy(child_thread->kernel_sp, curr_thread->kernel_sp, KSTACK_SIZE); + + store_context(current_ctx); + // for child + if (parent_pid != curr_thread->pid) + goto child; + + child_thread->context.x19 = curr_thread->context.x19; + child_thread->context.x20 = curr_thread->context.x20; + child_thread->context.x21 = curr_thread->context.x21; + child_thread->context.x22 = curr_thread->context.x22; + child_thread->context.x23 = curr_thread->context.x23; + child_thread->context.x24 = curr_thread->context.x24; + child_thread->context.x25 = curr_thread->context.x25; + child_thread->context.x26 = curr_thread->context.x26; + child_thread->context.x27 = curr_thread->context.x28; + child_thread->context.x28 = curr_thread->context.x28; + child_thread->context.fp = child_thread->kernel_sp + curr_thread->context.fp - curr_thread->kernel_sp; // move fp + child_thread->context.lr = curr_thread->context.lr; + // set up child thread (not copy from parent) + child_thread->context.sp = child_thread->kernel_sp + curr_thread->context.sp - curr_thread->kernel_sp; // move kernel sp + child_thread->context.ttbr0_el1 = VIRT_TO_PHYS(child_thread->context.ttbr0_el1); + + unlock(); + + tpf->x0 = child_thread->pid; + return child_thread->pid; + +child: + tpf->x0 = 0; + return 0; +} + +void exit(trapframe_t *tpf, int status) +{ + thread_exit(); +} + +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox_user) +{ + lock(); + // move user space mbox message to kernel space + unsigned int size_of_mbox = mbox_user[0]; + memcpy((char *)mbox, mbox_user, size_of_mbox); + mbox_call(ch); + memcpy(mbox_user, (char *)mbox, size_of_mbox); + + tpf->x0 = 8; + unlock(); + return 0; +} + +void kill(trapframe_t *tpf, int pid) +{ + lock(); + if (pid >= PIDMAX || pid < 0 || threads[pid].status == NEW) + { + unlock(); + return; + } + threads[pid].status = DEAD; + unlock(); + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0) + return; + + curr_thread->signal_handler[signal] = handler; +} + +void signal_kill(int pid, int signal) +{ + if (pid > PIDMAX || pid < 0 || threads[pid].status == NEW) + return; + + lock(); + threads[pid].sigcount[signal]++; + unlock(); +} + +void sigreturn(trapframe_t *tpf) +{ + load_context(&curr_thread->signal_saved_context); +} + +// only need to implement the anonymous page mapping in this Lab. +void *sys_mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset) +{ + // relocate to zero + if (len + (unsigned long)addr >= 0xfffffffff000L) + addr = 0L; + + // allign + len = len % 0x1000 ? len + (0x1000 - len % 0x1000) : len; // rounds up + addr = (unsigned long)addr % 0x1000 ? addr + (0x1000 - (unsigned long)addr % 0x1000) : addr; + + // check if any vma overlap the page + list_head_t *pos; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + if (!(((vm_area_struct_t *)pos)->virt_addr >= (unsigned long)(addr + len) || ((vm_area_struct_t *)pos)->virt_addr + ((vm_area_struct_t *)pos)->area_size <= (unsigned long)addr)) + { + the_area_ptr = (vm_area_struct_t *)pos; + break; + } + } + + // overlapped -> make the end as start + if (the_area_ptr) + { + tpf->x0 = (unsigned long)sys_mmap(tpf, (void *)(the_area_ptr->virt_addr + the_area_ptr->area_size), len, prot, flags, fd, file_offset); + return (void *)tpf->x0; + } + // if not overlapped add this page to vma + add_vma(curr_thread, (unsigned long)addr, len, VIRT_TO_PHYS((unsigned long)malloc(len)), prot, 1); + tpf->x0 = (unsigned long)addr; + return (void *)tpf->x0; +} \ No newline at end of file diff --git a/lab6/kernel/lib/system.c b/lab6/kernel/lib/system.c new file mode 100644 index 000000000..55efb6401 --- /dev/null +++ b/lab6/kernel/lib/system.c @@ -0,0 +1,74 @@ +#include "system.h" +#include "uart.h" +#include "mbox.h" + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC PHYS_TO_VIRT(0x3F10001c) +#define PM_WDOG PHYS_TO_VIRT(0x3F100024) + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} +void reboot() +{ + //disable_uart(); + reset(1); // timeout = 1/16th of a second? (whatever) +} +int get_board_revision(unsigned int* board_revision) +{ + /* + GET_BOARD_REVISION + */ + mbox[0] = 7*4; // length of the message + mbox[1] = MBOX_REQUEST; // request code + mbox[2] = GET_BOARD_REVISION; // tag identifier + mbox[3] = 4; // value buffer size in bytes + mbox[4] = 0; // request codes : b31 clear, b30-b0 reversed + mbox[5] = 0; // clear output buffer + mbox[6] = MBOX_TAG_LAST; // end tag + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) { + *board_revision = mbox[5]; + return 0; + } else { + uart_puts("Unable to query serial!"); + *board_revision = mbox[5] = -1; + return -1; + } +} + +int get_arm_memory_info(unsigned int* base_addr,unsigned int* size) +{ + /* + GET arm_memory address and size + */ + mbox[0] = 8*4; // length of the message + mbox[1] = MBOX_REQUEST; // request code + mbox[2] = GET_ARM_MEMORY; // tag identifier + mbox[3] = 8; // value buffer size in bytes + mbox[4] = 0; // request codes : b31 clear, b30-b0 reversed + mbox[5] = 0; // clear output buffer ( u32: base address in bytes ) + mbox[6] = 0; // clear output buffer ( u32: size in bytes ) + mbox[7] = MBOX_TAG_LAST; // end tag + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) { + *base_addr = mbox[5]; + *size = mbox[6]; + return 0; + } else { + uart_puts("Unable to query serial!"); + return -1; + } +} diff --git a/lab6/kernel/lib/task.c b/lab6/kernel/lib/task.c new file mode 100644 index 000000000..ea3c1526b --- /dev/null +++ b/lab6/kernel/lib/task.c @@ -0,0 +1,75 @@ +#include "task.h" +#include "irq.h" +#include "string.h" +#include "malloc.h" +#include "uart.h" +#include "timer.h" + +list_head_t task_list; +int cur_priority = 10; + +void task_list_init() { + INIT_LIST_HEAD(&task_list); +} +void add_task(task_callback_t callback, int priority){ + // init task + //uart_puts("t1\n"); + task_t *t = malloc(sizeof(task_t)); + //uart_puts("t2\n"); + t->priority = priority; + + //uart_puts("t3\n"); + t->callback = callback; + //uart_puts("t4\n"); + + INIT_LIST_HEAD(&t->listhead); + + disable_interrupt(); + // insert based on priority + if(list_empty(&task_list)) { + //uart_puts("f\n"); + list_add_tail(&t->listhead, &task_list); + } + else { + task_t *now; + list_head_t *listptr; + int inst = 0; + list_for_each(listptr, &task_list) { + now = list_entry(listptr, task_t, listhead); + + if(priority < now->priority) { + list_insert(&t->listhead, listptr->prev, listptr); + inst = 1; + break; + } + } + if(!inst) { + list_add_tail(&t->listhead, &task_list); + } + } + + enable_interrupt(); +} + +void pop_task() { + while (!list_empty(&task_list)) { + disable_interrupt(); + task_t *first = (task_t *)list_entry(task_list.next, task_t, listhead); + if(first->priority > cur_priority) { + enable_interrupt(); + return; + } + + list_del(&first->listhead); + int tmp_priority = cur_priority; + cur_priority = first->priority; + enable_interrupt(); + + first->callback(); + + disable_interrupt(); + cur_priority = tmp_priority; + enable_interrupt(); + } +} + diff --git a/lab6/kernel/lib/timer.c b/lab6/kernel/lib/timer.c new file mode 100644 index 000000000..06c6db231 --- /dev/null +++ b/lab6/kernel/lib/timer.c @@ -0,0 +1,268 @@ +#include "timer.h" +#include "uart.h" +#include "uint.h" +#include "irq.h" +#include "malloc.h" +#include "registers.h" +#include "string.h" +#include "utils.h" + + +struct list_head *timer_event_list; + + +/* + Enable timer interrupt: + 1. cntp_ctl_el0 to 1 (bit 0) + 2. CORE0_TIMER_IRQ_CTRL set to 2 + 3. enable cpu core interrupt + +*/ + +void timer_list_init() +{ + cpu_timer_enable(); + timer_event_list = malloc(sizeof(list_head_t)); + INIT_LIST_HEAD(timer_event_list); +} + +void cpu_timer_enable() { + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" ::"r"(tmp)); +} + +void core_timer_enable(){ + // according to given assembly + + // set register cntp_ctl_el1 to 1 + asm volatile("msr cntp_ctl_el0, %0"::"r"(1)); // enable timer + + + // exception 04 + core_timer_interrupt_enable(); + + +} + +void core_timer_disable(){ + // set register cntp_ctl_el0 to zero + // asm volatile("msr cntp_ctl_el0, %0"::"r"(0)); // disable + core_timer_interrupt_disable(); +} + +void core_timer_interrupt_enable(){ + + asm volatile ( + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // unmask timer interrupt + :::"x1","x2"); + + //*CORE0_TIMER_IRQ_CTRL = 2; +} +void core_timer_interrupt_disable(){ + + __asm__ __volatile__( + "mov x2, 0\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // unmask timer interrupt + :::"x1","x2"); + +} + + +// relative time from now (tval) +void set_core_timer_interrupt(unsigned long long expired_time) +{ + __asm__ __volatile__( + "mrs x1, cntfrq_el0\n\t" + "mul x1, x1, %0\n\t" + "msr cntp_tval_el0, x1\n\t" + ::"r"(expired_time) + : "x1"); +} + +// absolute time (cval) +void set_core_timer_interrupt_by_tick(unsigned long long tick) +{ + __asm__ __volatile__( + "msr cntp_cval_el0, %0\n\t" + ::"r"(tick)); +} + + +void set_core_timer_interrupt_first() { + + + timer_event_t *first = list_entry(timer_event_list->next, timer_event_t, listhead); + + asm volatile("msr cntp_cval_el0, %0"::"r"(first->expire_time)); + +} + +void timer_event_callback(timer_event_t *timer_event) { + ((void (*)(char *))timer_event->callback)(timer_event->args); // call the callback store in event + list_del((struct list_head *)timer_event); // delete the event + free(timer_event->args); // free the arg space + free(timer_event); + + // set interrupt to next time_event if existing + if (!list_empty(timer_event_list)) + set_core_timer_interrupt_by_tick(((timer_event_t *)timer_event_list->next)->expire_time); + else + set_core_timer_interrupt(10000); // alternative disable (large value) +} + +void add_Node_timer(list_head_t *head, timer_event_t *entry){ + + struct list_head *listptr; + + list_for_each(listptr, head) { + timer_event_t *now; + now = list_entry(listptr, timer_event_t, listhead); + if(entry->expire_time < now->expire_time) { + list_insert(&entry->listhead, listptr->prev, listptr); + return; + } + } + list_add_tail(&entry->listhead, head); + +} + +void print_timer(){ + uart_async_printf("Now time event list :\n"); + list_head_t *listptr; + list_for_each(listptr, timer_event_list) { + timer_event_t *now; + now = list_entry(listptr, timer_event_t, listhead); + uart_async_printf("address: %x, expire time:%d : %s\n",&now->listhead, now->expire_time, now->args); + } +} + +void add_timer(timer_callback_t callback, void* arg, unsigned long long expire_time, int bytick){ + + // disable_interrupt(); + struct timer_event *entry = malloc(sizeof(struct timer_event)); + + //uart_printf("size : %d\n",n); + entry->args = malloc(strlen(arg) + 1); + strcpy(entry->args, (char *)arg); + + // argument expire time is tick or second + if (bytick == 0) + entry->expire_time = get_tick_plus_s(expire_time); // store interrupt time into timer_event + else + entry->expire_time = get_tick_plus_s(0) + expire_time; + + entry->callback = callback; + entry->listhead.next = &(entry->listhead); + entry->listhead.prev = &(entry->listhead); + + + + + lock(); + + if(list_empty(timer_event_list)) { + list_add_tail(&entry->listhead, timer_event_list); + } + else { + add_Node_timer(timer_event_list, entry); + } + + set_core_timer_interrupt_first(); + + unlock(); + + +} + + + +void pop_timer() { + + + timer_event_t *first = list_entry(timer_event_list->next, timer_event_t, listhead); + + disable_interrupt(); + list_del(&first->listhead); // del first node + enable_interrupt(); + + first->callback(first->args); // exec callback func + + // check empty + disable_interrupt(); + + if(list_empty(timer_event_list)) { + core_timer_interrupt_disable_alternative(); // turn off timer interrupt + } + else { + set_core_timer_interrupt_first(); + } + enable_interrupt(); +} + + +unsigned long long get_tick_plus_s(unsigned long long second) +{ + return (get_clock_tick() + get_clock_freq() * second); +} + +unsigned long long get_clock_tick() +{ + unsigned long long cntpct_el0; + __asm__ __volatile__( + "mrs %0, cntpct_el0\n\t" + : "=r"(cntpct_el0)); // tick now + + return cntpct_el0; +} + +unsigned long long get_clock_freq() +{ + unsigned long long cntfrq_el0; + __asm__ __volatile__( + "mrs %0, cntfrq_el0\n\t" + : "=r"(cntfrq_el0)); // tick frequency + return cntfrq_el0; +} + +unsigned long long get_clock_time() +{ + return get_clock_tick() / get_clock_freq(); +} + + +void core_timer_interrupt_disable_alternative() { + set_core_timer_interrupt(10000); +} + +void core_timer_handler() { + lock(); + if(list_empty(timer_event_list)) { + set_core_timer_interrupt(10000); + unlock(); + return; + } + timer_event_callback((timer_event_t *)timer_event_list->next); // do callback and set new interrupt + //uart_puts("core_timer_handler...\n"); + unlock(); +} + +void two_second_alert(const char *str) { + //int n = strlen(str); + //uart_printf("tsa : size : %d\n",n); + uart_printf("'%s': seconds after booting : %d\n", str, get_clock_time()); + add_timer(two_second_alert, str, 2, 0); +} +int timer_list_size() +{ + int r = 0; + struct list_head *curr; + list_for_each(curr, timer_event_list) { + r++; + } + return r; +} \ No newline at end of file diff --git a/lab6/kernel/lib/uart.c b/lab6/kernel/lib/uart.c new file mode 100644 index 000000000..192da10df --- /dev/null +++ b/lab6/kernel/lib/uart.c @@ -0,0 +1,274 @@ +#include "uart.h" + + +char uart_tx_buffer[MAX_BUF_SIZE] = {}; +char uart_rx_buffer[MAX_BUF_SIZE] = {}; + + +static unsigned int uart_tx_buffer_r_idx; +static unsigned int uart_tx_buffer_w_idx; +static unsigned int uart_rx_buffer_r_idx; +static unsigned int uart_rx_buffer_w_idx; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void init_idx() { + uart_tx_buffer_r_idx = 0; + uart_tx_buffer_w_idx = 0; + uart_rx_buffer_r_idx = 0; + uart_rx_buffer_w_idx = 0; +} +void uart_init() +{ + register unsigned int r; + + r = *GPFSEL1; + //gpio15 can be both used for mini UART and PL011 UART + r &= ~((7<<12)|(7<<15)); // gpio14 least bit is 12, gpio15 least bit is 15 + r |= (2<<12)|(2<<15); // set alt5 for gpio14 and gpio15 + *GPFSEL1 = r; // control gpio pin 10~19 + *GPPUD = 0; // enable pins 14 and 15 + r=150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = (1<<14)|(1<<15); + r=150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = 0; // flush GPIO setup + + /* initialize UART */ + *AUX_ENABLE |= 1; // enable UART1, AUX mini uart + *AUX_MU_CNTL = 0; // disable tx,rx during configuration + *AUX_MU_IER = 0; // disable tx/rx interrupts + *AUX_MU_LCR = 3; // set data size 8 bits + *AUX_MU_MCR = 0; // don't need auto flow control + *AUX_MU_BAUD = 270; // 115200 baud, system clock 250MHz + *AUX_MU_IIR = 0x6; // clear FIFO + *AUX_MU_CNTL = 3; // enable Tx, Rx + + /* + for(int i = 0; i < MAX_BUF_SIZE; ++i) { + uart_tx_buffer[i] = '\0'; + uart_rx_buffer[i] = '\0'; + }*/ +} + +/** + * Send a character + */ +void uart_send(unsigned int c) { + /* wait until we can send */ + do{ + asm volatile("nop"); + }while(!(*AUX_MU_LSR & 0x20)); + /* write the character to the buffer */ + *AUX_MU_IO = (unsigned int)c; +} + + +char uart_getc() { + char r; + /* wait until something is in the buffer */ + //uart_puts("getc\n"); + do{ + asm volatile("nop"); + }while(!(*AUX_MU_LSR & 0x01)); + //uart_puts("read\n"); + /* read it and return */ + r = (char)(*AUX_MU_IO); + /* convert carriage return to newline */ + return r=='\r'?'\n':r; +} + + +void uart_puts(char *s) { + while(*s) { + /* convert newline to carriage return + newline */ + if(*s=='\n') + uart_send('\r'); + uart_send(*s++); + } +} + +void uart_hex(unsigned int d) { + unsigned int n; + int c; + for(c=28;c>=0;c-=4) { + // get highest tetrad + n=(d>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + uart_send(n); + } +} + +void uart_dec(int d) { + if (d < 0) { + uart_send('-'); + d = -d; + } + int divisor = 1; + while (divisor <= d / 10) { + divisor *= 10; + } + while (divisor > 0) { + uart_send((d / divisor) + '0'); + d %= divisor; + divisor /= 10; + } +} + +extern char _end; + +int uart_printf(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[MAX_BUF_SIZE]; + char *s = (char *)buf; + int count = vsprintf(s, fmt, args); + while (*s) + { + if (*s == '\n') + uart_send('\r'); + uart_send(*s++); + } + __builtin_va_end(args); + return count; +} + +void uart_async_putc(char c) { + // is full + // send a byte to transmit fifo + while((uart_tx_buffer_w_idx + 1) % MAX_BUF_SIZE == uart_tx_buffer_r_idx) + enable_mini_uart_tx_interrupt(); + + disable_interrupt(); + + // put a byte to tx buffer + //if(c == '\n') + // uart_tx_buffer[uart_tx_buffer_w_idx++] = '\r'; + uart_tx_buffer[uart_tx_buffer_w_idx++] = c; + uart_tx_buffer_w_idx %= MAX_BUF_SIZE; + + enable_interrupt(); + + enable_mini_uart_tx_interrupt(); +} + +char uart_async_getc() { + // is empty + // if empty rx buffer get a byte from IO + //uart_puts("call rx\n"); + // fix test + enable_mini_uart_rx_interrupt(); + while((uart_rx_buffer_w_idx == uart_rx_buffer_r_idx)) + enable_mini_uart_rx_interrupt(); + + //uart_puts("agetc\n"); + disable_interrupt(); + // get a byte from rx buffer + char c = uart_rx_buffer[uart_rx_buffer_r_idx++]; + uart_rx_buffer_r_idx %= MAX_BUF_SIZE; + + enable_interrupt(); + + + return c; +} + +// char sync uart to async +int uart_async_printf(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[MAX_BUF_SIZE]; + char *s = (char *)buf; + int count = vsprintf(s, fmt, args); + while (*s) + { + if (*s == '\n') + uart_async_putc('\r'); + uart_async_putc(*s++); + } + __builtin_va_end(args); + return count; +} + +void enable_mini_uart_interrupt() { + //uart_puts("a1\n"); + enable_mini_uart_rx_interrupt(); + //uart_puts("a2\n"); + enable_mini_uart_tx_interrupt(); + + // second level interrupt controller + // might be block + //uart_puts("a5\n"); + *ENABLE_IRQS_1 |= 1 << 29; + uart_puts("a4\n"); +} + +// rx interrupt bit 1 +void enable_mini_uart_rx_interrupt() { + *AUX_MU_IER |= 1; +} +// clear bit 1 +void disable_mini_uart_rx_interrupt(){ + // 11111...10 + *AUX_MU_IER &= ~1; +} + +// tx interrupt bit 2 +void enable_mini_uart_tx_interrupt() { + *AUX_MU_IER |= 1 << 1; +} +// clear bit 2 +void disable_mini_uart_tx_interrupt(){ + // 111111...01 + *AUX_MU_IER &= ~(1<<1); +} + +void unmask_aux_interrupt(){ + *ENABLE_IRQS_1 |= IRQ_PENDING_1_AUX_INT; +} + +void mask_aux_int(){ + *ENABLE_IRQS_1 &= ~IRQ_PENDING_1_AUX_INT; +} + +void uart_tx_interrupt_handler() { + // check empty + // empty then return + if(uart_tx_buffer_r_idx == uart_tx_buffer_w_idx) { + disable_mini_uart_tx_interrupt(); + return; + } + + // sent a byte to transmit fifo + disable_interrupt(); + // *AUX_MU_IO = (unsigned int)uart_tx_buffer[uart_tx_buffer_r_idx++]; + uart_send(uart_tx_buffer[uart_tx_buffer_r_idx++]); + uart_tx_buffer_r_idx %= MAX_BUF_SIZE; + enable_interrupt(); + + enable_mini_uart_tx_interrupt(); +} + +void uart_rx_interrupt_handler() { + // is holding byte + //uart_puts("rx handler 111\n"); + if((uart_rx_buffer_w_idx + 1) % MAX_BUF_SIZE == uart_rx_buffer_r_idx) { + disable_mini_uart_rx_interrupt(); + return; + } + + // get a byte from receive fifo + //uart_puts("rx handler\n"); + disable_interrupt(); + uart_rx_buffer[uart_rx_buffer_w_idx++] = (char)(*AUX_MU_IO); + //uart_send(uart_rx_buffer[uart_rx_buffer_w_idx++]) + uart_rx_buffer_w_idx %= MAX_BUF_SIZE; + enable_interrupt(); + + // enable_mini_uart_rx_interrupt(); +} + + diff --git a/lab6/kernel/linker.ld b/lab6/kernel/linker.ld new file mode 100644 index 000000000..d3b29f730 --- /dev/null +++ b/lab6/kernel/linker.ld @@ -0,0 +1,25 @@ +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + /* gnu linkonce : the linker will only keep one copy and discard the + other. */ + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + /* read only data */ + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + /* avoid redefinition */ + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _heap_start = .; + _end = .; + /* input section不會被輸出成output section */ + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/lab6/kernel/send.py b/lab6/kernel/send.py new file mode 100644 index 000000000..2e73fd246 --- /dev/null +++ b/lab6/kernel/send.py @@ -0,0 +1,25 @@ +import serial +import os,sys +import time + + +# tty.usbserial-0001 +# /dev/ttys002 +tty = serial.Serial("/dev/ttys003", 115200, timeout=0.5) +file_stats = os.stat("kernel8.img") + +# issue request and tell the size of img to rec +tty.write(str(file_stats.st_size).encode('utf-8')) +# size sended +tty.write("\n") + + +# send img byte-by-byte +# delay to ensure no loss +with open("kernel8.img", "rb") as fp: + byte = fp.read(1) + while byte: + tty.write(byte) + byte = fp.read(1) + #print(byte) + time.sleep(0.00001) \ No newline at end of file