diff --git a/lab6/.gitignore b/lab6/.gitignore new file mode 100644 index 000000000..1d0691eb3 --- /dev/null +++ b/lab6/.gitignore @@ -0,0 +1,3 @@ +.vscode +build +*.img \ No newline at end of file diff --git a/lab6/Makefile b/lab6/Makefile new file mode 100644 index 000000000..144854087 --- /dev/null +++ b/lab6/Makefile @@ -0,0 +1,61 @@ +# debug use CFLAGS='-g3' ASMFLAGS='-g3' +# Modify from +# https://github.com/s-matyukevich/raspberry-pi-os/blob/master/src/lesson01/Makefile +ARMGNU ?= aarch64-linux-gnu + +COPS = $(CFLAGS) -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude #-ggdb #-D__FS_DEBUG #-D__DEBUG #-D__DEBUG_MM #-D__DEBUG_MM_ALLOC #-D__DEBUG_MM_SCHED +ASMOPS = $(ASMFLAGS) -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader/bootloader.img + +debug: COPS += -DDEBUG +debug: ASMOPS += -DDEBUG +debug: all + +clean : + rm -rf $(BUILD_DIR) *.img + make -C bootloader clean + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -g -T linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -g $(BUILD_DIR)/kernel8.elf -O binary kernel8.img + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +run_display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +run_display_debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +run_debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -serial null -serial stdio -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +connect_raspi: + sudo screen /dev/ttyUSB0 115200 + +bootloader/bootloader.img: + make -C bootloader + +filesystem: + cd rootfs;find . | cpio -o -H newc > ../initramfs.cpio + diff --git a/lab6/bcm2710-rpi-3-b-plus.dtb b/lab6/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..3934b3a26 Binary files /dev/null and b/lab6/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab6/bootloader/Makefile b/lab6/bootloader/Makefile new file mode 100644 index 000000000..6b7bb6b60 --- /dev/null +++ b/lab6/bootloader/Makefile @@ -0,0 +1,50 @@ +# debug use CFLAGS='-g' ASMFLAGS='-g' +# Modify from +# https://github.com/s-matyukevich/raspberry-pi-os/blob/master/src/lesson01/Makefile +ARMGNU ?= aarch64-linux-gnu + +COPS = $(CFLAGS) -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude #-ggdb #-D__FS_DEBUG #-D__DEBUG #-D__DEBUG_MM #-D__DEBUG_MM_ALLOC #-D__DEBUG_MM_SCHED +ASMOPS = $(ASMFLAGS) -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : bootloader.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +bootloader.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -g -T linker.ld -o $(BUILD_DIR)/bootloader.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -g $(BUILD_DIR)/bootloader.elf -O binary bootloader.img + +run: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial stdio + +run_pty: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial pty -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb + +run_debug: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial stdio -dtb ../bcm2710-rpi-3-b-plus.dtb -s -S + +run_debug_pty: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial pty -s -S + +connect_raspi: + sudo screen /dev/ttyUSB0 115200 + diff --git a/lab6/bootloader/include/gpio.h b/lab6/bootloader/include/gpio.h new file mode 100644 index 000000000..399ae80f2 --- /dev/null +++ b/lab6/bootloader/include/gpio.h @@ -0,0 +1,25 @@ +#ifndef GPIO_H +#define GPIO_H + +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C)) + +#endif diff --git a/lab6/bootloader/include/mbox.h b/lab6/bootloader/include/mbox.h new file mode 100644 index 000000000..46160f821 --- /dev/null +++ b/lab6/bootloader/include/mbox.h @@ -0,0 +1,26 @@ +/* a properly aligned buffer */ +/* use this buffer(global variable) directly and the mbox_call will use it after call*/ +/* mbox format https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ +/* mbox address need to be aligned to 16 bytes */ +extern volatile unsigned int mbox[36]; + +#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 + +/* tags */ +#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); \ No newline at end of file diff --git a/lab6/bootloader/include/shell.h b/lab6/bootloader/include/shell.h new file mode 100644 index 000000000..74bd0aa42 --- /dev/null +++ b/lab6/bootloader/include/shell.h @@ -0,0 +1,8 @@ +#ifndef SHELL_H +#define SHELL_H + +void shell(); +void do_cmd(char* cmd); +void print_system_messages(); + +#endif \ No newline at end of file diff --git a/lab6/bootloader/include/string.h b/lab6/bootloader/include/string.h new file mode 100644 index 000000000..8123bc2d4 --- /dev/null +++ b/lab6/bootloader/include/string.h @@ -0,0 +1,9 @@ +#ifndef STRING_H +#define STRING_H + +int strcmp (const char * s1, const char * s2 ); +char* strcat (char *dest, const char *src); +unsigned long long strlen(const char *str); +char* strcpy (char *dest, const char *src); +char* memcpy (void *dest, const void *src, unsigned long long len); +#endif \ No newline at end of file diff --git a/lab6/bootloader/include/system.h b/lab6/bootloader/include/system.h new file mode 100644 index 000000000..79410c43e --- /dev/null +++ b/lab6/bootloader/include/system.h @@ -0,0 +1,14 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +extern char* _dtb; + +int get_board_revision(unsigned int* board_revision); +int get_arm_memory_info(unsigned int* base_addr,unsigned int* size); +void set(long addr, unsigned int value); +void reboot(); +void reset(int tick); +void cancel_reset(); +void load_kernel(); + +#endif \ No newline at end of file diff --git a/lab6/bootloader/include/uart.h b/lab6/bootloader/include/uart.h new file mode 100644 index 000000000..e226d69aa --- /dev/null +++ b/lab6/bootloader/include/uart.h @@ -0,0 +1,16 @@ +#ifndef UART_H +#define UART_H + +#define MAX_BUF_SIZE 0x100 + +void uart_init(); +void uart_putc(char c); +char uart_getc(); +int uart_puts(char *s); +char* uart_gets(char *buf); +int uart_printf(char *s); +void uart_hex(unsigned int d); +void disable_uart(); +char uart_getc_pure(); + +#endif diff --git a/lab6/bootloader/linker.ld b/lab6/bootloader/linker.ld new file mode 100644 index 000000000..3d0a0f5d1 --- /dev/null +++ b/lab6/bootloader/linker.ld @@ -0,0 +1,22 @@ +SECTIONS +{ + . = 0x70000; + _code_relocate_place = .; + /*bootloader on 0x80000, kernel on 0x80000 need to self relocate bootloader to other place*/ + . = 0x80000; + PROVIDE(_start = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start)>>3; +__code_size = (_end - _start); diff --git a/lab6/bootloader/src/main.c b/lab6/bootloader/src/main.c new file mode 100644 index 000000000..b56ee1741 --- /dev/null +++ b/lab6/bootloader/src/main.c @@ -0,0 +1,44 @@ +#include "uart.h" +#include "mbox.h" +#include "shell.h" +#include "string.h" +#include "system.h" + +extern char* _code_relocate_place; +extern unsigned long long __code_size; +extern unsigned long long _start; +extern char* _dtb; + +void code_relocate(char * addr); + +int relocate=1; + +void main(char* arg) +{ + _dtb = arg; + char* reloc_place = (char*)&_code_relocate_place; + + if(relocate) // only do relocate once + { + relocate = 0; + code_relocate(reloc_place); + } + + // set up serial console + uart_init(); + + shell(); +} + +// relocate code and jump to there +void code_relocate(char * addr) +{ + unsigned long long size = (unsigned long long)&__code_size; + char* start = (char *)&_start; + for(unsigned long long i=0;i request successful +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +/** + * 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 int r = (((unsigned int)((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 == *MBOX_READ) + /* is it a valid successful response? */ + return mbox[1]==MBOX_RESPONSE; + } + return 0; +} \ No newline at end of file diff --git a/lab6/bootloader/src/shell.c b/lab6/bootloader/src/shell.c new file mode 100644 index 000000000..e0f1766d1 --- /dev/null +++ b/lab6/bootloader/src/shell.c @@ -0,0 +1,63 @@ +#include "uart.h" +#include "string.h" +#include "shell.h" +#include "mbox.h" +#include "system.h" + +void shell() +{ + char cmd[MAX_BUF_SIZE]; + print_system_messages(); + uart_puts("Welcome, this is bootloader. Try to load kernel with uart with protocol in system.c(load_kernel)"); + while(1) + { + uart_printf("# "); + uart_gets(cmd); + do_cmd(cmd); + } +} + +void do_cmd(char* cmd) +{ + if(strcmp(cmd,"help")==0) + { + uart_puts("help : print this help menu"); + uart_puts("hello : print Hello World!"); + uart_puts("reboot : reboot the device"); + uart_puts("load_kernel : load kernel code from uart to 0x80000 and jump to it!"); + } + else if(strcmp(cmd,"hello")==0) + { + uart_puts("Hello World!"); + } + else if(strcmp(cmd,"reboot")==0) + { + reboot(); + }else if(strcmp(cmd,"load_kernel")==0) + { + load_kernel(); + }else + { + uart_puts("Unknown command!"); + } +} + +void print_system_messages() +{ + unsigned int board_revision; + get_board_revision(&board_revision); + uart_printf("Board revision is : 0x"); + uart_hex(board_revision); + uart_puts(""); + + unsigned int arm_mem_base_addr; + unsigned int arm_mem_size; + + get_arm_memory_info(&arm_mem_base_addr,&arm_mem_size); + uart_printf("ARM memory base address in bytes : 0x"); + uart_hex(arm_mem_base_addr); + uart_puts(""); + uart_printf("ARM memory size in bytes : 0x"); + uart_hex(arm_mem_size); + uart_puts(""); +} \ No newline at end of file diff --git a/lab6/bootloader/src/start.S b/lab6/bootloader/src/start.S new file mode 100644 index 000000000..446a11244 --- /dev/null +++ b/lab6/bootloader/src/start.S @@ -0,0 +1,33 @@ +.section ".text.boot" + +.global _start + +//avoid use x0 which stored dtb place +_start: + + // read cpu id, stop slave cores + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + // cpu id > 0, stop +1: wfe + b 1b +2: // cpu id == 0 + + // set top of stack just before our code (stack grows to a lower address per AAPCS64) + ldr x1, =_start + mov sp, x1 + + // clear bss + ldr x1, =__bss_start + ldr w2, =__bss_size +3: cbz w2, 4f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b + +4: + // jump to C code, should not return + bl main + // for failsafe, halt this core too + b 1b diff --git a/lab6/bootloader/src/string.c b/lab6/bootloader/src/string.c new file mode 100644 index 000000000..de4783cae --- /dev/null +++ b/lab6/bootloader/src/string.c @@ -0,0 +1,45 @@ +#include "string.h" +#include + +int strcmp (const char *p1, const char *p2) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + return c1 - c2; +} + +char* strcat (char *dest, const char *src) +{ + strcpy (dest + strlen (dest), src); + return dest; +} + +char* strcpy (char *dest, const char *src) +{ + return memcpy (dest, src, strlen (src) + 1); +} + +unsigned long long strlen(const char *str) +{ + size_t count = 0; + while((unsigned char)*str++)count++; + return count; +} + +char* memcpy (void *dest, const void *src, unsigned long long len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} \ No newline at end of file diff --git a/lab6/bootloader/src/system.c b/lab6/bootloader/src/system.c new file mode 100644 index 000000000..286d0d676 --- /dev/null +++ b/lab6/bootloader/src/system.c @@ -0,0 +1,121 @@ +#include "system.h" +#include "uart.h" +#include "mbox.h" + +extern char _start[]; //bootloader load kernel to here +char* _dtb; + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +/* For all return 0 -> success , -1 failure*/ + +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; + } +} + +void set(long addr, unsigned int value) +{ + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reboot() +{ + //disable_uart(); + reset(1); // timeout = 1/16th of a second? (whatever) +} + +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 + while(1); // wati for clock +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} + + +//編譯器不要優化這段 +#pragma GCC push_options +#pragma GCC optimize ("O0") + +void load_kernel() +{ + // prevent dtb been rewrited by kernel + char* temp_dtb = _dtb; + char c; + unsigned long long kernel_size=0; + char* kernel_start = (char*) (&_start); + + uart_puts("kernel size:"); + for(int i=0;i<8;i++) //protocol : use little endian to get kernel size + { + c = uart_getc_pure(); + kernel_size += c<<(i*8); + } + + + for(int i=0;i get del + { + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + }else + { + uart_putc(r); + } + /* convert carrige return to newline */ + return r=='\r'?'\n':r; +} + +/** + * Display a string with newline + */ +int uart_puts(char *s) { + int i=0; + + while(*s) { + uart_putc(*s++); + i++; + } + uart_putc('\r'); + uart_putc('\n'); + + return i+2; +} + +/** + * get a string + */ +char* uart_gets(char *buf) +{ + int count; + char c; + char *s; + for (s = buf,count = 0; (c = uart_getc()) != '\n' && count!=MAX_BUF_SIZE-1 ;count++) + { + *s = c; + if(*s=='\x7f') + { + count--; + if(count==-1) + { + uart_putc(' '); // prevent back over command line # + continue; + } + s--; + count--; + continue; + } + s++; + } + *s = '\0'; + return buf; +} + +/** + * printf (TODO) + */ +int uart_printf(char *s) { + int i = 0; + while(*s) { + uart_putc(*s++); + i++; + } + return i; +} + +/** + * Display a binary value in hexadecimal + */ +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_putc(n); + } +} + + +/** + * Receive a character without echo and any translation + */ +char uart_getc_pure() { + char r; + /* wait until something is in the buffer */ + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + return r; +} diff --git a/lab6/config.txt b/lab6/config.txt new file mode 100644 index 000000000..49fc25695 --- /dev/null +++ b/lab6/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x20000000 diff --git a/lab6/include/cpio.h b/lab6/include/cpio.h new file mode 100644 index 000000000..d92d441c4 --- /dev/null +++ b/lab6/include/cpio.h @@ -0,0 +1,40 @@ +#ifndef CPIO_H +#define CPIO_H + + +/* + cpio format : https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 + header,file path,file data,header ...... + header+file path (padding 4 bytes) + file data (padding 4 bytes) (max size 4gb) +*/ + +#define CPIO_NEWC_HEADER_MAGIC "070701" // big endian + +void* CPIO_DEFAULT_PLACE; // init in main +void *CPIO_DEFAULT_END; + +struct cpio_newc_header +{ + char c_magic[6]; //magic The string "070701". + 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]; //check This field is always set to zero by writers and ignored by readers. +}; + +/* 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); + +#endif \ No newline at end of file diff --git a/lab6/include/dtb.h b/lab6/include/dtb.h new file mode 100644 index 000000000..17c387b6c --- /dev/null +++ b/lab6/include/dtb.h @@ -0,0 +1,24 @@ +#ifndef DTB_H +#define DTB_H + +#define uint32_t unsigned int +#define uint64_t unsigned long long + +// manipulate device tree with dtb file format +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +char* dtb_place; +extern void* CPIO_DEFAULT_PLACE; // initialize by callback dtb_callback_initramfs in main 本來是寫死的 +typedef void (*dtb_callback)(uint32_t node_type, char *name, void *value, uint32_t name_size); + +uint32_t uint32_endian_big2lttle(uint32_t data); +void traverse_device_tree(void *base,dtb_callback callback); //traverse dtb tree +void dtb_callback_show_tree(uint32_t node_type, char *name, void *value, uint32_t name_size); +void dtb_callback_initramfs(uint32_t node_type, char *name, void *value, uint32_t name_size); +void reserve_memory_block_with_dtb(); + +#endif \ No newline at end of file diff --git a/lab6/include/exception.h b/lab6/include/exception.h new file mode 100644 index 000000000..2d9818a2d --- /dev/null +++ b/lab6/include/exception.h @@ -0,0 +1,98 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include "stddef.h" +// https://github.com/Tekki/raspberrypi-documentation/blob/master/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf p16 +#define CORE0_INTERRUPT_SOURCE ((volatile unsigned int *)(PHYS_TO_VIRT(0x40000060))) + +/* +The basic pending register shows which interrupt are pending. To speed up interrupts processing, a +number of 'normal' interrupt status bits have been added to this register. This makes the 'IRQ +pending base' register different from the other 'base' interrupt registers +p112-115 https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf +*/ +#define PBASE PHYS_TO_VIRT(0x3F000000) +#define IRQ_BASIC_PENDING ((volatile unsigned int*)(PBASE+0x0000B200)) +#define IRQ_PENDING_1 ((volatile unsigned int*)(PBASE+0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int*)(PBASE+0x0000B208)) +#define FIQ_CONTROL ((volatile unsigned int*)(PBASE+0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(PBASE+0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int*)(PBASE+0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(PBASE+0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(PBASE+0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(PBASE+0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int*)(PBASE+0x0000B224)) + +#define IRQ_PENDING_1_AUX_INT (1<<29) +#define INTERRUPT_SOURCE_GPU (1<<8) +#define INTERRUPT_SOURCE_CNTPNSIRQ (1<<1) +//#define SYSTEM_TIMER_MATCH_1 (1<<1) +//#define SYSTEM_TIMER_MATCH_3 (1<<3) +//#define UART_INT (1ULL<<57) + +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 sync_64_router(trapframe_t *tpf, unsigned long esr_el1); +void irq_router(trapframe_t *tpf); +void invalid_exception_router(); + +static inline void enable_interrupt() +{ + __asm__ __volatile__("msr daifclr, 0xf"); +} + +static inline void disable_interrupt() +{ + __asm__ __volatile__("msr daifset, 0xf"); +} + +unsigned long long is_disable_interrupt(); +void lock(); +void unlock(); + +#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 + +#endif \ No newline at end of file diff --git a/lab6/include/filesystem.h b/lab6/include/filesystem.h new file mode 100644 index 000000000..3ea204a03 --- /dev/null +++ b/lab6/include/filesystem.h @@ -0,0 +1,12 @@ +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +extern void* CPIO_DEFAULT_PLACE; + +int ls(char* working_dir); +int cat(char* thefilepath); +int execfile(char* thefilepath); +unsigned int get_file_size(char *thefilepath); +char *get_file_start(char *thefilepath); + +#endif \ No newline at end of file diff --git a/lab6/include/gpio.h b/lab6/include/gpio.h new file mode 100644 index 000000000..bb286555a --- /dev/null +++ b/lab6/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 diff --git a/lab6/include/irqtask.h b/lab6/include/irqtask.h new file mode 100644 index 000000000..1aaccc86a --- /dev/null +++ b/lab6/include/irqtask.h @@ -0,0 +1,25 @@ +#ifndef IRQTASK_H +#define IRQTASK_H + +#include "list.h" + +// set uart irq priority = 1, timer = 0 +#define UART_IRQ_PRIORITY 1 +#define TIMER_IRQ_PRIORITY 0 + +//like timer_event +typedef struct irq_task +{ + struct list_head listhead; + + unsigned long long priority; //store priority (smaller number is more preemptive) + + void *task_function; // task function pointer +} irq_task_t; + +void add_task(void *task_function, unsigned long long priority); +void run_task(irq_task_t *the_task); +void run_preemptive_tasks(); +void task_list_init(); + +#endif \ No newline at end of file diff --git a/lab6/include/list.h b/lab6/include/list.h new file mode 100644 index 000000000..5794e63c1 --- /dev/null +++ b/lab6/include/list.h @@ -0,0 +1,137 @@ +#ifndef LIST_H +#define LIST_H + +/* + * Circular doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + * + * https://github.com/torvalds/linux/blob/master/include/linux/list.h + * https://elixir.bootlin.com/linux/latest/source/scripts/kconfig/list.h#L24 + */ + +typedef struct list_head { + struct list_head *next, *prev; +}list_head_t; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + + +/** + * list_is_head - tests whether @list is the list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next) + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_size(const struct list_head *head) +{ + list_head_t *pos; + int i= 0; + list_for_each(pos, head) + { + i++; + } + return i; +} + +#endif \ No newline at end of file diff --git a/lab6/include/malloc.h b/lab6/include/malloc.h new file mode 100644 index 000000000..7047568de --- /dev/null +++ b/lab6/include/malloc.h @@ -0,0 +1,49 @@ +#ifndef MALLOC_H +#define MALLOC_H + +#include "list.h" +#include "uart.h" +#include "exception.h" +#include "dtb.h" + +#define MAXORDER 6 +#define MAXCACHEORDER 4 // 32, 64, 128, 256, 512 (for every 32bytes) + +// simple_malloc +void *simple_malloc(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 *kmalloc(unsigned int size); +void kfree(void *ptr); + +typedef struct frame +{ + struct list_head 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(); +#endif \ No newline at end of file diff --git a/lab6/include/mbox.h b/lab6/include/mbox.h new file mode 100644 index 000000000..c53870dee --- /dev/null +++ b/lab6/include/mbox.h @@ -0,0 +1,44 @@ +#ifndef MBOX_H +#define MBOX_H + +#include "gpio.h" + +/* a properly aligned buffer */ +/* use this buffer(global variable) directly and the mbox_call will use it after call*/ +/* mbox format https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ +/* mbox address need to be aligned to 16 bytes */ +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 + +/* tags */ +#define GET_BOARD_REVISION 0x10002 +#define MBOX_TAG_GETSERIAL 0x10004 +#define GET_ARM_MEMORY 0x10005 +#define MBOX_TAG_LAST 0 + +#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 + +int mbox_call(unsigned char ch); + +#endif \ No newline at end of file diff --git a/lab6/include/mmu.h b/lab6/include/mmu.h new file mode 100644 index 000000000..301a40f16 --- /dev/null +++ b/lab6/include/mmu.h @@ -0,0 +1,58 @@ +#ifndef MMU_H +#define MMU_H + +#include "stddef.h" +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) +#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 + +#define PD_TABLE 0b11L +#define PD_BLOCK 0b01L +#define PD_UNX (1L << 54) +#define PD_KNX (1L << 53) +#define PD_ACCESS (1L << 10) +#define PD_UK_ACCESS (1L << 6) +#define PD_RDONLY (1L << 7) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + +#define PHYS_TO_VIRT(x) (x + 0xffff000000000000) +#define VIRT_TO_PHYS(x) (x - 0xffff000000000000) +#define ENTRY_ADDR_MASK 0xfffffffff000L + +#ifndef __ASSEMBLER__ + +#define kernel_pgd_addr 0x1000 +#define kernel_pud_addr 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 mappages(size_t *pgd_p, size_t va, size_t size, size_t pa, size_t flag); +void add_vma(struct thread *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; + +} vm_area_struct_t; + +#endif //__ASSEMBLER__ + +#endif \ No newline at end of file diff --git a/lab6/include/registers.h b/lab6/include/registers.h new file mode 100644 index 000000000..41886d8d6 --- /dev/null +++ b/lab6/include/registers.h @@ -0,0 +1,12 @@ +#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))) + +#define STR(x) #x +#define XSTR(s) STR(s) + +#endif \ No newline at end of file diff --git a/lab6/include/sched.h b/lab6/include/sched.h new file mode 100644 index 000000000..6a1143e64 --- /dev/null +++ b/lab6/include/sched.h @@ -0,0 +1,68 @@ +#ifndef SCHED_H +#define SCHED_H + +#include "list.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x4000 +#define KSTACK_SIZE 0x4000 +#define SIGNAL_MAX 64 + +extern void switch_to(void *curr_context, void *next_context); +extern void store_context(void *curr_context); +extern void load_context(void *curr_context); +extern void *get_current(); + +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; // use for MMU mapping (user space) +} thread_context_t; + +typedef struct thread +{ + list_head_t listhead; + thread_context_t context; + char *data; + unsigned int datasize; + int iszombie; + int pid; + int isused; + char* stack_alloced_ptr; + char *kernel_stack_alloced_ptr; + void (*singal_handler[SIGNAL_MAX+1])(); + int sigcount[SIGNAL_MAX + 1]; + void (*curr_signal_handler)(); + int signal_is_checking; + list_head_t vma_list; + thread_context_t signal_saved_context; +} thread_t; + +thread_t *curr_thread; +list_head_t *run_queue; +list_head_t *wait_queue; + +thread_t threads[PIDMAX + 1]; + +void schedule_timer(char *notuse); +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/include/shell.h b/lab6/include/shell.h new file mode 100644 index 000000000..74bd0aa42 --- /dev/null +++ b/lab6/include/shell.h @@ -0,0 +1,8 @@ +#ifndef SHELL_H +#define SHELL_H + +void shell(); +void do_cmd(char* cmd); +void print_system_messages(); + +#endif \ No newline at end of file diff --git a/lab6/include/signal.h b/lab6/include/signal.h new file mode 100644 index 000000000..9e6c2cbca --- /dev/null +++ b/lab6/include/signal.h @@ -0,0 +1,16 @@ +#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/include/sprintf.h b/lab6/include/sprintf.h new file mode 100644 index 000000000..101e564e3 --- /dev/null +++ b/lab6/include/sprintf.h @@ -0,0 +1,27 @@ +/* + * 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. + * + */ + +unsigned int sprintf(char *dst, char* fmt, ...); +unsigned int vsprintf(char *dst,char* fmt, __builtin_va_list args); \ No newline at end of file diff --git a/lab6/include/stddef.h b/lab6/include/stddef.h new file mode 100644 index 000000000..d3f418ebb --- /dev/null +++ b/lab6/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/include/string.h b/lab6/include/string.h new file mode 100644 index 000000000..e8c814ed4 --- /dev/null +++ b/lab6/include/string.h @@ -0,0 +1,16 @@ +#include "stddef.h" + +#ifndef STRING_H +#define STRING_H + +int strcmp (const char * s1, const char * s2 ); +int strncmp (const char *s1, const char *s2, unsigned long long n); +char* strcat (char *dest, const char *src); +unsigned long long strlen(const char *str); +char* strcpy (char *dest, const char *src); +char* memcpy (void *dest, const void *src, unsigned long long len); +char* strchr (register const char *s, int c); +int atoi(char* str); +void *memset(void *s, int c, size_t n); + +#endif \ No newline at end of file diff --git a/lab6/include/syscall.h b/lab6/include/syscall.h new file mode 100644 index 000000000..1d0a60aa5 --- /dev/null +++ b/lab6/include/syscall.h @@ -0,0 +1,20 @@ +#ifndef SYSCALL_H +#define SYSCALL_H + +#include "stddef.h" +#include "exception.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 diff --git a/lab6/include/system.h b/lab6/include/system.h new file mode 100644 index 000000000..34bee2bd0 --- /dev/null +++ b/lab6/include/system.h @@ -0,0 +1,11 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +int get_board_revision(unsigned int* board_revision); +int get_arm_memory_info(unsigned int* base_addr,unsigned int* size); +void set(long addr, unsigned int value); +void reboot(); +void reset(int tick); +void cancel_reset(); + +#endif \ No newline at end of file diff --git a/lab6/include/timer.h b/lab6/include/timer.h new file mode 100644 index 000000000..2b84b854a --- /dev/null +++ b/lab6/include/timer.h @@ -0,0 +1,34 @@ +#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) + +typedef struct timer_event +{ + struct list_head listhead; + + unsigned long long interrupt_time; //store as tick time after cpu start + + void *callback; // interrupt -> timer_callback -> callback(args) + + char *args; // need to free the string by event callback function +} timer_event_t; + +struct list_head *timer_event_list; // first head has nothing, store timer_event_t after it + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_handler(); + +//now the callback only support "funcion(char *)", char* in args (defualt by second) +void add_timer(void *callback, unsigned long long timeout, char *args, int bytick); +unsigned long long get_tick_plus_s(unsigned long long second); +void set_core_timer_interrupt(unsigned long long expired_time); +void set_core_timer_interrupt_by_tick(unsigned long long tick); +void two_second_alert(char *str); +void timer_list_init(); +int timer_list_get_size(); + +#endif \ No newline at end of file diff --git a/lab6/include/uart.h b/lab6/include/uart.h new file mode 100644 index 000000000..08949d221 --- /dev/null +++ b/lab6/include/uart.h @@ -0,0 +1,51 @@ +#ifndef UART_H +#define UART_H + +#include "gpio.h" +#include "uart.h" +#include "sprintf.h" +#include "registers.h" + +#define MAX_BUF_SIZE 0x10000 + +/* 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)) + +void uart_init(); +void uart_disable_echo(); +void uart_enable_echo(); +void uart_putc(char c); +char uart_getc(); +int uart_puts(char *s); +int uart_async_puts(char *s); +char* uart_gets(char *buf); +char* uart_async_gets(char *buf); +int uart_printf(char *fmt, ...); +int uart_async_printf(char *fmt, ...); +void disable_uart(); +void uart_interrupt_r_handler(); +void uart_interrupt_w_handler(); +void enable_mini_uart_interrupt(); +void enable_mini_uart_w_interrupt(); +void enable_mini_uart_r_interrupt(); +void disable_mini_uart_interrupt(); +void disable_mini_uart_w_interrupt(); +void disable_mini_uart_r_interrupt(); +int mini_uart_r_interrupt_is_enable(); +int mini_uart_w_interrupt_is_enable(); + +char uart_async_getc(); +void uart_async_putc(char c); + +#endif diff --git a/lab6/linker.ld b/lab6/linker.ld new file mode 100644 index 000000000..16ef10f6d --- /dev/null +++ b/lab6/linker.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _heap_start = .; + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/lab6/rootfs/get_simpleexec.sh b/lab6/rootfs/get_simpleexec.sh new file mode 100644 index 000000000..782bd723b --- /dev/null +++ b/lab6/rootfs/get_simpleexec.sh @@ -0,0 +1,5 @@ +#!bin/bash + +aarch64-linux-gnu-gcc -c user_exception_test.S +aarch64-linux-gnu-ld -T linker.ld -o user_exception_test.elf user_exception_test.o +aarch64-linux-gnu-objcopy -O binary user_exception_test.elf user_exception_test.img \ No newline at end of file diff --git a/lab6/rootfs/linker.ld b/lab6/rootfs/linker.ld new file mode 100644 index 000000000..783ebe038 --- /dev/null +++ b/lab6/rootfs/linker.ld @@ -0,0 +1,5 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text) } +} \ No newline at end of file diff --git a/lab6/rootfs/testfile1 b/lab6/rootfs/testfile1 new file mode 100644 index 000000000..734d5d5db --- /dev/null +++ b/lab6/rootfs/testfile1 @@ -0,0 +1,2 @@ +1 +1111111111111111111111111111 diff --git a/lab6/rootfs/testfile112345 b/lab6/rootfs/testfile112345 new file mode 100644 index 000000000..539a7773b --- /dev/null +++ b/lab6/rootfs/testfile112345 @@ -0,0 +1 @@ +1111111111111111111111 diff --git a/lab6/rootfs/testfile2222 b/lab6/rootfs/testfile2222 new file mode 100644 index 000000000..876c79950 --- /dev/null +++ b/lab6/rootfs/testfile2222 @@ -0,0 +1 @@ +222222 diff --git a/lab6/rootfs/user_exception_test.S b/lab6/rootfs/user_exception_test.S new file mode 100644 index 000000000..838323763 --- /dev/null +++ b/lab6/rootfs/user_exception_test.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b \ No newline at end of file diff --git a/lab6/send_kernel_to_bootloader.py b/lab6/send_kernel_to_bootloader.py new file mode 100644 index 000000000..f25abfcb6 --- /dev/null +++ b/lab6/send_kernel_to_bootloader.py @@ -0,0 +1,31 @@ +from serial import Serial +from pwn import * +import argparse + +parser = argparse.ArgumentParser(description='NYCU OSDI kernel sender') +parser.add_argument('--filename', metavar='PATH', default='kernel8.img', type=str, help='path to kernel8.img') +parser.add_argument('--device', metavar='TTY',default='/dev/ttyUSB0', type=str, help='path to UART device') +parser.add_argument('--baud', metavar='Hz',default=115200, type=int, help='baud rate') +args = parser.parse_args() + +with open(args.filename,'rb') as fd: + with Serial(args.device, args.baud) as ser: + + kernel_raw = fd.read() + length = len(kernel_raw) + + print("Kernel image size : ", hex(length)) + for i in range(8): + ser.write(p64(length)[i:i+1]) + ser.flush() + + print("Start sending kernel img by uart...") + for i in range(length): + # Use kernel_raw[i: i+1] is byte type. Instead of using kernel_raw[i] it will retrieve int type then cause error + ser.write(kernel_raw[i: i+1]) + ser.flush() + if i % 100 == 0: + print("{:>6}/{:>6} bytes".format(i, length)) + print("{:>6}/{:>6} bytes".format(length, length)) + print("Transfer finished!") + \ No newline at end of file diff --git a/lab6/src/cpio.c b/lab6/src/cpio.c new file mode 100644 index 000000000..0f354b5fd --- /dev/null +++ b/lab6/src/cpio.c @@ -0,0 +1,64 @@ +#include "cpio.h" +#include "string.h" +#include "uart.h" + + +/* Parse an ASCII hex string into an integer. (big endian)*/ +static unsigned int parse_hex_str(char *s, unsigned int max_len) +{ + unsigned int r = 0; + + for (unsigned int i = 0; i < max_len; i++) { + r *= 16; + if (s[i] >= '0' && s[i] <= '9') { + r += s[i] - '0'; + } else if (s[i] >= 'a' && s[i] <= 'f') { + r += s[i] - 'a' + 10; + } else if (s[i] >= 'A' && s[i] <= 'F') { + r += s[i] - 'A' + 10; + } else { + return r; + } + } + return r; +} + + +/* 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 exists. */ + 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 and store into *filesize + *filesize = parse_hex_str(this_header_pointer->c_filesize,8); + + // end of header is the pathname + *pathname = ((char *)this_header_pointer) + sizeof(struct cpio_newc_header); + + // get file data, file data is just after pathname + unsigned int pathname_length = parse_hex_str(this_header_pointer->c_namesize,8); + unsigned int offset = pathname_length+sizeof(struct cpio_newc_header); + offset = offset%4==0?offset:(offset+4-offset%4); //padding + *data = (char *)this_header_pointer+offset; + + //get next header pointer + if(*filesize==0) + { + *next_header_pointer = (struct cpio_newc_header*)*data; + }else + { + offset = *filesize; + *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. + if(strncmp(*pathname,"TRAILER!!!",sizeof("TRAILER!!!"))==0) + { + *next_header_pointer = 0; + } + + return 0; +} \ No newline at end of file diff --git a/lab6/src/dtb.c b/lab6/src/dtb.c new file mode 100644 index 000000000..1bec30680 --- /dev/null +++ b/lab6/src/dtb.c @@ -0,0 +1,146 @@ +#include "dtb.h" +#include "uart.h" +#include "string.h" +#include "cpio.h" +#include "malloc.h" + +//stored as big endian +struct fdt_header { + uint32_t magic; + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +}; + +struct fdt_reserve_entry +{ + uint64_t address; + uint64_t size; +}; + +uint32_t uint32_endian_big2lttle(uint32_t data) +{ + char* r = (char*)&data; + return (r[3]<<0) | (r[2]<<8) | (r[1]<<16) | (r[0]<<24); +} + +uint64_t uint64_endian_big2lttle(uint64_t data) +{ + char *r = (char *)&data; + return ((unsigned long long)r[7] << 0) | ((unsigned long long)r[6] << 8) | ((unsigned long long)r[5] << 16) | ((unsigned long long)r[4] << 24) | ((unsigned long long)r[3] << 32) | ((unsigned long long)r[2] << 40) | ((unsigned long long)r[1] << 48) | ((unsigned long long)r[0] << 56); +} + +void traverse_device_tree(void *dtb_ptr,dtb_callback callback) +{ + struct fdt_header* header = dtb_ptr; + if(uint32_endian_big2lttle(header->magic) != 0xD00DFEED) + { + uart_puts("traverse_device_tree : wrong magic in traverse_device_tree"); + return; + } + + uint32_t struct_size = uint32_endian_big2lttle(header->size_dt_struct); + char* dt_struct_ptr = (char*)((char*)header + uint32_endian_big2lttle(header->off_dt_struct)); + char* dt_strings_ptr = (char*)((char*)header + uint32_endian_big2lttle(header->off_dt_strings)); + + char* end = (char*)dt_struct_ptr + struct_size; + char* pointer = dt_struct_ptr; + + while(pointer < end) + { + uint32_t token_type = uint32_endian_big2lttle(*(uint32_t*)pointer); + + pointer += 4; + if(token_type == FDT_BEGIN_NODE) + { + callback(token_type,pointer,0,0); + pointer += strlen(pointer); + pointer += 4 - (unsigned long long)pointer%4; //alignment 4 byte + }else if(token_type == FDT_END_NODE) + { + callback(token_type,0,0,0); + }else if(token_type == FDT_PROP) + { + uint32_t len = uint32_endian_big2lttle(*(uint32_t*)pointer); + pointer += 4; + char* name = (char*)dt_strings_ptr + uint32_endian_big2lttle(*(uint32_t*)pointer); + pointer += 4; + callback(token_type,name,pointer,len); + pointer += len; + if((unsigned long long)pointer % 4 !=0)pointer += 4 - (unsigned long long)pointer%4; //alignment 4 byte + }else if(token_type == FDT_NOP) + { + callback(token_type,0,0,0); + }else if(token_type == FDT_END) + { + callback(token_type,0,0,0); + }else + { + uart_printf("error type:%x\r\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;imagic) != 0xD00DFEED) + { + uart_puts("traverse_device_tree : wrong magic in traverse_device_tree"); + return; + } + + char *dt_mem_rsvmap_ptr = (char *)((char *)header + uint32_endian_big2lttle(header->off_mem_rsvmap)); + struct fdt_reserve_entry *reverse_entry = (struct fdt_reserve_entry *)dt_mem_rsvmap_ptr; + + while (reverse_entry->address != 0 || reverse_entry->size != 0) + { + unsigned long long start = PHYS_TO_VIRT(uint64_endian_big2lttle(reverse_entry->address)); + unsigned long long end = uint64_endian_big2lttle(reverse_entry->size) + start; + memory_reserve(start, end); + reverse_entry++; + } + + //also reserve device tree + memory_reserve((unsigned long long)dtb_place, (unsigned long long)dtb_place + uint32_endian_big2lttle(header->totalsize)); +} diff --git a/lab6/src/entry.S b/lab6/src/entry.S new file mode 100644 index 000000000..e6fc28690 --- /dev/null +++ b/lab6/src/entry.S @@ -0,0 +1,208 @@ +// save general registers to stack +.global save_all +.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 +.global load_all +.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_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry sync_el1h // Synchronous EL1h + ventry irq_el1h // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_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_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + + + +sync_invalid_el1t: + //save_all + mov x0,0 + bl invalid_exception_router + //load_all + eret +irq_invalid_el1t: + save_all + mov x0,1 + bl invalid_exception_router + load_all + eret +fiq_invalid_el1t: + save_all + mov x0,2 + bl invalid_exception_router + load_all + eret +error_invalid_el1t: + save_all + mov x0,3 + bl invalid_exception_router + load_all + eret + + + +sync_el1h: + //save_all + mov x0,4 + bl invalid_exception_router + //load_all + eret +irq_el1h: + save_all + mov x0, sp // trap_frame + bl irq_router + load_all + eret +fiq_invalid_el1h: + save_all + mov x0,6 + bl invalid_exception_router + load_all + eret +error_invalid_el1h: + save_all + mov x0,7 + bl invalid_exception_router + load_all + eret + + + +sync_el0_64: + save_all + mov x0, sp // trap_frame + mrs x1, esr_el1 + bl sync_64_router + load_all + eret +irq_el0_64: + save_all + mov x0, sp // trap_frame + bl irq_router + load_all + eret +fiq_invalid_el0_64: + save_all + mov x0,10 + bl invalid_exception_router + load_all + eret +error_invalid_el0_64: + save_all + mov x0,11 + bl invalid_exception_router + load_all + eret + + + +sync_invalid_el0_32: + save_all + mov x0,12 + bl invalid_exception_router + load_all + eret +irq_invalid_el0_32: + save_all + mov x0,13 + bl invalid_exception_router + load_all + eret +fiq_invalid_el0_32: + save_all + mov x0,14 + bl invalid_exception_router + load_all + eret +error_invalid_el0_32: + save_all + mov x0,15 + bl invalid_exception_router + load_all + eret \ No newline at end of file diff --git a/lab6/src/exception.c b/lab6/src/exception.c new file mode 100644 index 000000000..3c24fc4d0 --- /dev/null +++ b/lab6/src/exception.c @@ -0,0 +1,204 @@ +#include "uart.h" +#include "exception.h" +#include "timer.h" +#include "irqtask.h" +#include "syscall.h" +#include "sched.h" +#include "signal.h" +#include "mmu.h" +#include "stddef.h" + +// For svc 0 (supervisor call) +void sync_64_router(trapframe_t *tpf, unsigned long x1) +{ + //uart_printf("syscall no : %d\r\n", tpf->x8); + //uart_printf("elr_el1 : 0x%x\r\n", tpf->elr_el1); + //uart_printf("sp_el0 : 0x%x\r\n", tpf->sp_el0); + //uart_printf("spsr_el1 : 0x%x\r\n", tpf->spsr_el1); + //while(1); + + //For MMU page fault + esr_el1_t *esr; + esr = (esr_el1_t *)&x1; + if (esr->ec == DATA_ABORT_LOWER || esr->ec == INS_ABORT_LOWER) + { + handle_abort(esr); + return; + } + + enable_interrupt(); + unsigned long long syscall_no = tpf->x8; + + if (syscall_no == 0) + { + getpid(tpf); + } + else if(syscall_no == 1) + { + uartread(tpf,(char *) tpf->x0, tpf->x1); + } + else if (syscall_no == 2) + { + uartwrite(tpf,(char *) tpf->x0, tpf->x1); + } + else if (syscall_no == 3) + { + exec(tpf,(char *) tpf->x0, (char **)tpf->x1); + } + else if (syscall_no == 4) + { + fork(tpf); + } + else if (syscall_no == 5) + { + exit(tpf,tpf->x0); + } + else if (syscall_no == 6) + { + syscall_mbox_call(tpf,(unsigned char)tpf->x0, (unsigned int *)tpf->x1); + } + else if (syscall_no == 7) + { + kill(tpf, (int)tpf->x0); + } + else if (syscall_no == 8) + { + signal_register(tpf->x0, (void (*)())tpf->x1); + } + else if (syscall_no == 9) + { + signal_kill(tpf->x0, tpf->x1); + }else if(syscall_no == 10) + { + sys_mmap(tpf,(void *)tpf->x0,tpf->x1,tpf->x2,tpf->x3,tpf->x4,tpf->x5); + } + else if (syscall_no == 50) + { + sigreturn(tpf); + }else + { + uart_printf("unknown syscall\r\n"); + while(1); + } + + /* + unsigned long long spsr_el1; + __asm__ __volatile__("mrs %0, SPSR_EL1\n\t" : "=r" (spsr_el1)); + + unsigned long long elr_el1; + __asm__ __volatile__("mrs %0, ELR_EL1\n\t" : "=r" (elr_el1)); + + unsigned long long esr_el1; + __asm__ __volatile__("mrs %0, ESR_EL1\n\t" : "=r" (esr_el1));*/ + + //uart_printf("exception sync_el0_64_router -> spsr_el1 : 0x%x, elr_el1 : 0x%x, esr_el1 : 0x%x\r\n",spsr_el1,elr_el1,esr_el1); +} + +void irq_router(trapframe_t* tpf) +{ + //uart_printf("ena : %d\r\n", is_disable_interrupt()); + //uart_printf("irq_basic_pending: %x\n",*IRQ_BASIC_PENDING); + //uart_printf("irq_pending_1: %x\n",*IRQ_PENDING_1); + //uart_printf("irq_pending_2: %x\n",*IRQ_PENDING_2); + //uart_printf("source : %x\n",*CORE0_INTERRUPT_SOURCE); + + //目前實測能從pending_1 AUX_INT, CORE0_INTERRUPT_SOURCE=GPU 辨別其他都是0(或再找) + + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + //https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p13 + /* + AUX_MU_IIR + on read bits[2:1] : + 00 : No interrupts + 01 : Transmit holding register empty + 10 : Receiver holds valid byte + 11: + */ + + // buffer read, write + if (*AUX_MU_IIR & (0b01 << 1)) //can write + { + disable_mini_uart_w_interrupt(); // lab 3 : advanced 2 -> mask device line (enable by handler) + add_task(uart_interrupt_w_handler, UART_IRQ_PRIORITY); + run_preemptive_tasks(); + } + else if (*AUX_MU_IIR & (0b10 << 1)) // can read + { + //不知道為啥關了還會進來 這種時候清除FIFO就可以關掉 (會有最多8個FIFO裡的byte消失) + if (!mini_uart_r_interrupt_is_enable()) + { + *AUX_MU_IIR = 0xC2; + return; + } + + //uart_printf("dd %d %d \r\n", mini_uart_r_interrupt_is_enable(), mini_uart_w_interrupt_is_enable()); + disable_mini_uart_r_interrupt(); // lab 3 : advanced 2 -> mask device line (enable by handler) + add_task(uart_interrupt_r_handler, UART_IRQ_PRIORITY); + run_preemptive_tasks(); + } + else + { + uart_printf("uart handler error\r\n"); + } + + }else if(*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) //from CNTPNS (core_timer) + { + core_timer_disable(); // lab 3 : advanced 2 -> mask device line + add_task(core_timer_handler, TIMER_IRQ_PRIORITY); + run_preemptive_tasks(); + core_timer_enable(); // lab 3 : advanced 2 -> unmask device line + + //at least two thread running -> schedule for any timer irq + if (run_queue->next->next != run_queue)schedule(); + } + + //only do signal handler when return to user mode + if ((tpf->spsr_el1 & 0b1100) == 0) + { + check_signal(tpf); + } + +} + +void invalid_exception_router(unsigned long long x0){ + unsigned long long elr_el1; + __asm__ __volatile__("mrs %0, ELR_EL1\n\t" + : "=r"(elr_el1)); + uart_printf("invalid exception : 0x%x\r\n", elr_el1); + uart_printf("invalid exception : x0 : %x\r\n",x0); + while(1); +} + +//https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/DAIF--Interrupt-Mask-Bits +//https://www.twblogs.net/a/5b7c4ca52b71770a43da534e +//zero -> enable +//others -> disable +unsigned long long is_disable_interrupt() +{ + unsigned long long daif; + __asm__ __volatile__("mrs %0, daif\n\t" + : "=r"(daif)); + + return daif; //enable -> daif == 0 (no mask) +} + +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/src/filesystem.c b/lab6/src/filesystem.c new file mode 100644 index 000000000..1ce6fef26 --- /dev/null +++ b/lab6/src/filesystem.c @@ -0,0 +1,148 @@ +#include "cpio.h" +#include "filesystem.h" +#include "uart.h" +#include "string.h" +#include "sched.h" + +int cat(char* thefilepath) +{ + char* filepath; + char* filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = CPIO_DEFAULT_PLACE; + + 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) + { + for(unsigned int i=0;ipriority = priority; // store interrupt time into timer_event + the_task->task_function = task_function; + + // add the timer_event into timer_event_list (sorted) + // if the same priority FIFO + struct list_head *curr; + + lock(); + list_for_each(curr, task_list) + { + if (((irq_task_t *)curr)->priority > the_task->priority) + { + list_add(&the_task->listhead, curr->prev); // add this timer at the place just before the bigger one (sorted) + break; + } + } + + if (list_is_head(curr, task_list)) + { + list_add_tail(&(the_task->listhead), task_list); // for the time is the biggest + } + + unlock(); +} + +void run_preemptive_tasks(){ + + while (1) + { + lock(); + if (list_empty(task_list)) + { + unlock(); + break; + } + + irq_task_t *the_task = (irq_task_t *)task_list->next; + // just run task when priority is lower than the task preempted + if (curr_task_priority <= the_task->priority) + { + unlock(); + break; + } + list_del_entry((struct list_head *)the_task); + int prev_task_priority = curr_task_priority; + curr_task_priority = the_task->priority; + + unlock(); + run_task(the_task); + + curr_task_priority = prev_task_priority; + kfree(the_task); + + } +} + +void run_task(irq_task_t *the_task) +{ + ((void (*)())the_task->task_function)(); +} \ No newline at end of file diff --git a/lab6/src/main.c b/lab6/src/main.c new file mode 100644 index 000000000..d60954273 --- /dev/null +++ b/lab6/src/main.c @@ -0,0 +1,39 @@ +#include "uart.h" +#include "mbox.h" +#include "shell.h" +#include "string.h" +#include "malloc.h" +#include "dtb.h" +#include "cpio.h" +#include "exception.h" +#include "irqtask.h" +#include "mmu.h" + +void init_cpio_default_place(); + +extern char* dtb_place; + +void main(char* dtb) +{ + //stroe dtb pointer to global (device tree) + dtb_place = PHYS_TO_VIRT(dtb); + + init_cpio_default_place(); //store cpio pointer to global (file system) + init_allocator(); + alloctest(); + + //cannot use original input series after interrupt start (input is going to the buffer), use async input instead. + //output series are not affected. (order is affected) + task_list_init(); + //enable_mini_uart_interrupt(); + enable_interrupt(); // enable interrupt in EL1 -> EL1 + + uart_printf("dtb : 0x%x\r\n",dtb); + + shell(); +} + +void init_cpio_default_place() +{ + traverse_device_tree(dtb_place,dtb_callback_initramfs); +} diff --git a/lab6/src/malloc.c b/lab6/src/malloc.c new file mode 100644 index 000000000..40ca1960d --- /dev/null +++ b/lab6/src/malloc.c @@ -0,0 +1,410 @@ +#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_place; + +// simple_malloc +void *simple_malloc(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 = simple_malloc(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 + */ + reserve_memory_block_with_dtb(); // spin tables can be find + 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_DEFAULT_PLACE, (unsigned long long)CPIO_DEFAULT_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 kmalloc!!!!\r\n"); + while (1); + return simple_malloc(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("kmalloc ERROR (all lists are empty?)!!!!\r\n"); + while (1); + return simple_malloc(size); + } + + //get the frame + frame_t *target_frame_ptr = (frame_t *)freelist[target_list_val].next; + list_del_entry((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_entry((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 +} + +void *kmalloc(unsigned int size) +{ + lock(); +#ifdef DEBUG + uart_printf("kmalloc 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("kmalloc ret 0x%x\r\n", r); +#endif + //For cache + unlock(); + return r; +} + +void kfree(void *ptr) +{ + lock(); +#ifdef DEBUG + uart_printf("kfree 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_entry(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_entry(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 = kmalloc(0x10); + char *b = kmalloc(0x100); + char *c = kmalloc(0x1000); + + kfree(a); + kfree(b); + kfree(c); + + a = kmalloc(32); + char *aa = kmalloc(50); + b = kmalloc(64); + char *bb = kmalloc(64); + c = kmalloc(128); + char *cc = kmalloc(129); + char *d = kmalloc(256); + char *dd = kmalloc(256); + char *e = kmalloc(512); + char *ee = kmalloc(999); + + kfree(a); + kfree(aa); + kfree(b); + kfree(bb); + kfree(c); + kfree(cc); + kfree(dd); + kfree(d); + kfree(e); + kfree(ee); +} \ No newline at end of file diff --git a/lab6/src/mbox.c b/lab6/src/mbox.c new file mode 100644 index 000000000..d4532fdb8 --- /dev/null +++ b/lab6/src/mbox.c @@ -0,0 +1,26 @@ +#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; +} \ No newline at end of file diff --git a/lab6/src/mmu.c b/lab6/src/mmu.c new file mode 100644 index 000000000..4a120decd --- /dev/null +++ b/lab6/src/mmu.c @@ -0,0 +1,165 @@ +#include "mmu.h" +#include "malloc.h" +#include "string.h" +#include "threads.h" +#include "exception.h" +// PGD's page frame at 0x1000 +// PUD's page frame at 0x2000 +// two-level translation (1G) -> three-level translation (2MB) +void* set_2M_kernel_mmu(void* x0) +{ + //unsigned long *pud_table = kmalloc(0x1000); + unsigned long *pud_table = (unsigned long *)0x3000; + for (int i = 0; i < 512; i++) + { + // 0x3F000000 to 0x3FFFFFFF for peripherals + if ((0x00000000 + (0x200000L) * i) >= 0x3F000000L) + { + pud_table[i] = PD_ACCESS | PD_BLOCK | (0x00000000 + (0x200000L) * i) | (MAIR_DEVICE_nGnRnE << 2) | PD_UK_ACCESS | PD_UNX | PD_KNX; + } + else + { + pud_table[i] = PD_ACCESS | PD_BLOCK | (0x00000000 | (0x200000L) * i) | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + } + + //unsigned long *pud_table2 = kmalloc(0x1000); + unsigned long *pud_table2 = (unsigned long *)0x4000; + for (int i = 0; i < 512; i++) + { + pud_table2[i] = PD_ACCESS | PD_BLOCK | (0x40000000L | (0x200000L) * i) | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + // set PUD + *(unsigned long *)(kernel_pud_addr) = PD_ACCESS | PD_TABLE | (unsigned long)pud_table; + *(unsigned long *)(kernel_pud_addr + 0x8) = PD_ACCESS | PD_TABLE | (unsigned long)pud_table2; + + 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; + for (int level = 0; level < 4; level++) + { + unsigned int idx = (va >> (39 - level * 9)) & 0x1ff; + + if (level == 3) + { + 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 =kmalloc(0x1000); + memset(newtable_p, 0, 0x1000); + table_p[idx] = VIRT_TO_PHYS((size_t)newtable_p); + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + table_p = (size_t*)PHYS_TO_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); + } +} + +//give +void mappages(size_t *virt_pgd_p, size_t va, size_t size, size_t pa, size_t flag) +{ + pa = pa - (pa % 0x1000); // align + for (size_t s = 0; s < size; s+=0x1000) + { + map_one_page(virt_pgd_p, va + s, pa + s, flag); + } +} + +void add_vma(thread_t *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced) +{ + // alignment + size = size % 0x1000 ? size + (0x1000 - size%0x1000): size; + + vm_area_struct_t *new_area = kmalloc(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; + 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); + 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) + { + if(level!=2)free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + kfree(PHYS_TO_VIRT((char *)next_table)); + } + } + } +} + +void handle_abort(esr_el1_t* esr_el1) +{ + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t": "=r"(far_el1)); + + 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 <= far_el1 && ((vm_area_struct_t *)pos)->virt_addr + ((vm_area_struct_t *)pos)->area_size >= far_el1) + { + the_area_ptr = (vm_area_struct_t *)pos; + break; + } + } + + if (!the_area_ptr) + { + seg_fault(); + } + + // For translation fault + 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) + { + uart_printf("[Translation fault]: 0x%x\r\n",far_el1); + + size_t addr_offset = (far_el1 - the_area_ptr->virt_addr); + 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); + }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; + + //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/src/sched.S b/lab6/src/sched.S new file mode 100644 index 000000000..de22ee0ef --- /dev/null +++ b/lab6/src/sched.S @@ -0,0 +1,60 @@ +.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] + //mrs x9, ttbr0_el1 + //str x9, [x0, 16 * 6 + 8] + + 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] + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + + msr tpidr_el1, x1 + + 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 + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/lab6/src/sched.c b/lab6/src/sched.c new file mode 100644 index 000000000..8b67897c6 --- /dev/null +++ b/lab6/src/sched.c @@ -0,0 +1,177 @@ +#include "sched.h" +#include "exception.h" +#include "malloc.h" +#include "timer.h" +#include "uart.h" +#include "signal.h" +#include "mmu.h" +#include "string.h" + +void init_thread_sched() +{ + lock(); + run_queue = kmalloc(sizeof(list_head_t)); + wait_queue = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + INIT_LIST_HEAD(wait_queue); + + //init pids + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].isused = 0; + threads[i].pid = i; + threads[i].iszombie = 0; + } + + asm volatile("msr tpidr_el1, %0" ::"r"(kmalloc(sizeof(thread_t)))); /// malloc a space for current kernel thread to prevent crash + + thread_t* idlethread = thread_create(idle,0x1000); + curr_thread = idlethread; + unlock(); +} + +void idle(){ + while(1) + { + kill_zombies(); //reclaim threads marked as DEAD + schedule(); //switch to next thread in run queue + } +} + +void schedule(){ + lock(); + do{ + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue) || curr_thread->iszombie); + + unlock(); + switch_to(get_current(), &curr_thread->context); +} + +void kill_zombies(){ + lock(); + list_head_t *curr; + list_for_each(curr,run_queue) + { + if (((thread_t *)curr)->iszombie) + { + list_del_entry(curr); + kfree(((thread_t *)curr)->kernel_stack_alloced_ptr); // free kstack + free_page_tables(((thread_t *)curr)->context.ttbr0_el1,0); + + // free alloced area and vma struct + list_head_t *pos = ((thread_t *)curr)->vma_list.next; + while (pos != &((thread_t *)curr)->vma_list) + { + if (((vm_area_struct_t *)pos)->is_alloced) + kfree((void*)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr)); + + list_head_t* next_pos = pos->next; + kfree(pos); + pos = next_pos; + } + + kfree(PHYS_TO_VIRT(((thread_t *)curr)->context.ttbr0_el1)); // free PGD + ((thread_t *)curr)->iszombie = 0; + ((thread_t *)curr)->isused = 0; + } + } + 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->stack_alloced_ptr), 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; + + //copy file into data + for (int i = 0; i < filesize;i++) + { + t->data[i] = data[i]; + } + + //disable echo when going to userspace + uart_disable_echo(); + curr_thread = t; + add_timer(schedule_timer, 1, "", 0); + // eret to exception level 0 + asm("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" + "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_stack_alloced_ptr + KSTACK_SIZE), "r"(t->context.ttbr0_el1)); + + return 0; +} + + +//malloc a kstack and a userstack +thread_t *thread_create(void *start, unsigned int filesize) +{ + lock(); + + thread_t *r; + for (int i = 0; i <= PIDMAX; i++) + { + if (!threads[i].isused) + { + r = &threads[i]; + break; + } + } + INIT_LIST_HEAD(&r->vma_list); + r->iszombie = 0; + r->isused = 1; + r->context.lr = (unsigned long long)start; + r->stack_alloced_ptr = kmalloc(USTACK_SIZE); + r->kernel_stack_alloced_ptr = kmalloc(KSTACK_SIZE); + r->signal_is_checking = 0; + r->data = kmalloc(filesize); + r->datasize = filesize; + r->context.sp = (unsigned long long)r->kernel_stack_alloced_ptr + KSTACK_SIZE; + r->context.fp = r->context.sp; + + r->context.ttbr0_el1 = kmalloc(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->singal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add(&r->listhead, run_queue); + unlock(); + return r; +} + +void thread_exit(){ + lock(); + curr_thread->iszombie = 1; + unlock(); + schedule(); +} + +void schedule_timer(char* notuse){ + unsigned long long cntfrq_el0; + __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/src/shell.c b/lab6/src/shell.c new file mode 100644 index 000000000..470020895 --- /dev/null +++ b/lab6/src/shell.c @@ -0,0 +1,113 @@ +#include "uart.h" +#include "string.h" +#include "shell.h" +#include "mbox.h" +#include "system.h" +#include "filesystem.h" +#include "dtb.h" +#include "timer.h" +#include "malloc.h" +#include "sched.h" + +void shell() +{ + char cmd[MAX_BUF_SIZE]; + print_system_messages(); + init_thread_sched(); + timer_list_init(); + core_timer_enable(); + while (1) + { + uart_printf("# "); + uart_gets(cmd); + do_cmd(cmd); + } +} + +void do_cmd(char *cmd) +{ + if (strcmp(cmd, "help") == 0) + { + uart_printf("help : print this help menu\r\n"); + uart_printf("hello : print Hello World!\r\n"); + uart_printf("reboot : reboot the device\r\n"); + uart_printf("ls : list current directory\r\n"); + uart_printf("cat : print content of a file\r\n"); + uart_printf("show_device_tree : show device tree\r\n"); + uart_printf("exec : load a user program in the initramfs and run it in EL0\r\n"); + uart_printf("setTimeout [MESSAGE] [SECONDS] : print message after [SECONDS] seconds (non-blocking)\r\n"); + uart_printf("clockAlert : alert every two seconds\r\n"); + } + else if (strcmp(cmd, "hello") == 0) + { + uart_printf("Hello World!\r\n"); + } + else if (strcmp(cmd, "reboot") == 0) + { + reboot(); + } + else if (strcmp(cmd, "cat") == 0) + { + uart_printf("Filename: "); + char filepath[MAX_BUF_SIZE]; + uart_gets(filepath); + cat(filepath); + } + else if (strcmp(cmd, "ls") == 0) + { + ls("."); + } + else if (strcmp(cmd, "show_device_tree") == 0) + { + traverse_device_tree(dtb_place, dtb_callback_show_tree); + } + else if (strcmp(cmd, "exec") == 0) // in filesystem.c + { + uart_printf("Filename: "); + char filepath[MAX_BUF_SIZE]; + uart_gets(filepath); + execfile(filepath); + } + else if (strncmp(cmd, "setTimeout", sizeof("setTimeout") - 1) == 0) + { + strchr(cmd, ' '); + char *message = strchr(cmd, ' '); + if (!message) + { + uart_printf("setTimeout wrong format"); + return; + } + message += 1; + char *end_message = strchr(message, ' '); + if (!end_message) + { + uart_printf("setTimeout wrong format"); + return; + } + *end_message = '\0'; + char *seconds = end_message + 1; + add_timer(uart_puts, atoi(seconds), message,0); + } + else if (strcmp(cmd, "clockAlert") == 0) + { + add_timer(two_second_alert, 2, "two_second_alert",0); + } + else + { + uart_printf("Unknown command!: %s\r\n", cmd); + } +} + +void print_system_messages() +{ + unsigned int board_revision; + get_board_revision(&board_revision); + uart_printf("Board revision is : 0x%x\r\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_printf("ARM memory base address in bytes : 0x%x\r\n", arm_mem_base_addr); + uart_printf("ARM memory size in bytes : 0x%x\r\n", arm_mem_size); +} \ No newline at end of file diff --git a/lab6/src/signal.c b/lab6/src/signal.c new file mode 100644 index 000000000..9475bf27c --- /dev/null +++ b/lab6/src/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->singal_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/src/sprintf.c b/lab6/src/sprintf.c new file mode 100644 index 000000000..63533303f --- /dev/null +++ b/lab6/src/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/src/start.S b/lab6/src/start.S new file mode 100644 index 000000000..8399d3324 --- /dev/null +++ b/lab6/src/start.S @@ -0,0 +1,92 @@ +#include "registers.h" +#include "mmu.h" + +.section ".text.boot" + +.global _start + +_start: + // read cpu id, stop slave cores + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + // cpu id > 0, stop +1: wfe + b 1b +2: // cpu id == 0 + + // Switch from EL2 to EL1 . + bl from_el2_to_el1 + + // set paging configuration (up : 0xffff000000000000 low : 0x0000000000000000) + 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 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x4] + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x4 // load PGD to the bottom translation-based register. + msr ttbr1_el1, x4 // also load PGD to the upper translation based register. + + mov sp, 0x3c000000 + bl set_2M_kernel_mmu + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + + // indirect branch to the upper virtual address + ldr x2, =set_exception_vector_table + br x2 + +set_exception_vector_table: + adr x1, exception_vector_table + msr vbar_el1, x1 + + // set top of stack at 0xffff00003c000000 (last usable memory) + movz x3, 0x0000 + movk x3, 0x3c00, lsl 16 + movk x3, 0xffff, lsl 48 + mov sp, x3 + + // clear bss + ldr x1, =__bss_start + ldr w2, =__bss_size +3: cbz w2, 4f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b + + // jump to C code, should not return +4: bl main + // for failsafe, halt this core too + b 1b + +from_el2_to_el1: + ldr x1, =CPACR_EL1_VALUE // Make el0, el1 can use Floating point and Advanced SIMD + msr CPACR_EL1, x1 + mov x1, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x1 + mov x1, 0x3c5 // EL1h (SPSel = 1) with interrupt disabled + msr spsr_el2, x1 + msr elr_el2, lr + eret // return to EL1 + + diff --git a/lab6/src/string.c b/lab6/src/string.c new file mode 100644 index 000000000..97b28b435 --- /dev/null +++ b/lab6/src/string.c @@ -0,0 +1,131 @@ +#include "string.h" +#include +#include "uart.h" + +int strcmp (const char *p1, const char *p2) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + return c1 - c2; +} + +int strncmp (const char *s1, const char *s2, unsigned long long n) +{ + unsigned char c1 = '\0'; + unsigned char c2 = '\0'; + if (n >= 4) + { + size_t n4 = n >> 2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + } while (--n4 > 0); + n &= 3; + } + while (n > 0) + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + n--; + } + return c1 - c2; +} + +char* strcat (char *dest, const char *src) +{ + strcpy (dest + strlen (dest), src); + return dest; +} + +char* strcpy (char *dest, const char *src) +{ + return memcpy (dest, src, strlen (src) + 1); +} + +unsigned long long strlen(const char *str) +{ + size_t count = 0; + while((unsigned char)*str++)count++; + return count; +} + +char* memcpy (void *dest, const void *src, unsigned long long len) +{ + char *d = dest; + const char *s = src; + while (len--) + { + *d++ = *s++; + } + return dest; +} + +char* strchr (register const char *s, int c) +{ + do { + if (*s == c) + { + return (char*)s; + } + } while (*s++); + return (0); +} + +// A simple atoi() function +int atoi(char* str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + { + if(str[i] > '9' || str[i] < '0')return res; + res = res * 10 + str[i] - '0'; + } + + // return result. + return res; +} + +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/src/syscall.c b/lab6/src/syscall.c new file mode 100644 index 000000000..b9598ca9b --- /dev/null +++ b/lab6/src/syscall.c @@ -0,0 +1,241 @@ +#include "syscall.h" +#include "sched.h" +#include "stddef.h" +#include "uart.h" +#include "filesystem.h" +#include "exception.h" +#include "malloc.h" +#include "mbox.h" +#include "signal.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_putc(buf[i]); // TODO : debug -> some unknown bugs occur when uart_async_putc (only in qemu) + } + 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 + // free alloced area and vma struct + list_head_t *pos = curr_thread->vma_list.next; + while (pos != &curr_thread->vma_list) + { + if (((vm_area_struct_t *)pos)->is_alloced) + kfree((void *)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr)); + + list_head_t *next_pos = pos->next; + kfree(pos); + pos = next_pos; + } + + 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 = kmalloc(curr_thread->datasize); + curr_thread->stack_alloced_ptr = kmalloc(USTACK_SIZE); + + asm("dsb ish\n\t"); // ensure write has completed + free_page_tables(curr_thread->context.ttbr0_el1, 0); + memset(PHYS_TO_VIRT(curr_thread->context.ttbr0_el1), 0, 0x1000); + asm("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->stack_alloced_ptr), 7, 1); + // device + 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->singal_handler[i] = signal_default_handler; + } + + tpf->elr_el1 = 0; + tpf->sp_el0 = 0xfffffffff000; + tpf->x0 = 0; + return 0; +} + +int fork(trapframe_t *tpf) +{ + lock(); + thread_t *newt = thread_create(curr_thread->data,curr_thread->datasize); + + //copy signal handler + for (int i = 0; i <= SIGNAL_MAX;i++) + { + newt->singal_handler[i] = curr_thread->singal_handler[i]; + } + + list_head_t *pos; + list_for_each(pos, &curr_thread->vma_list){ + + // ignore device and signal wrapper + if (((vm_area_struct_t *)pos)->virt_addr == USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED || ((vm_area_struct_t *)pos)->virt_addr == 0x3C000000L) + { + continue; + } + + char *new_alloc = kmalloc(((vm_area_struct_t *)pos)->area_size); + add_vma(newt, ((vm_area_struct_t *)pos)->virt_addr, ((vm_area_struct_t *)pos)->area_size, (size_t)VIRT_TO_PHYS(new_alloc), ((vm_area_struct_t *)pos)->rwx, 1); + + memcpy(new_alloc, (void*)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr), ((vm_area_struct_t *)pos)->area_size); + } + + // device + add_vma(newt, 0x3C000000L, 0x3000000L, 0x3C000000L, 3, 0); + // for signal wrapper + add_vma(newt, 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 + for (int i = 0; i < KSTACK_SIZE; i++) + { + newt->kernel_stack_alloced_ptr[i] = curr_thread->kernel_stack_alloced_ptr[i]; + } + + store_context(get_current()); + //for child + if( parent_pid != curr_thread->pid) + { + goto child; + } + + void *temp_ttbr0_el1 = newt->context.ttbr0_el1; + newt->context = curr_thread->context; + newt->context.ttbr0_el1 = VIRT_TO_PHYS(temp_ttbr0_el1); + newt->context.fp += newt->kernel_stack_alloced_ptr - curr_thread->kernel_stack_alloced_ptr; // move fp + newt->context.sp += newt->kernel_stack_alloced_ptr - curr_thread->kernel_stack_alloced_ptr; // move kernel sp + + unlock(); + + tpf->x0 = newt->pid; + return newt->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) +{ + //mbox_user = (unsigned int *)(curr_thread->stack_alloced_ptr + USTACK_SIZE - (0xfffffffff000 - (size_t)mbox)); + lock(); + + 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].isused) + { + unlock(); + return; + } + threads[pid].iszombie = 1; + unlock(); + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0)return; + + curr_thread->singal_handler[signal] = handler; +} + +void signal_kill(int pid, int signal) +{ + if (pid > PIDMAX || pid < 0 || !threads[pid].isused)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; + + len = len % 0x1000 ? len + (0x1000 - len % 0x1000) : len; // rounds up + addr = (unsigned long)addr%0x1000?addr + (0x1000 - (unsigned long)addr % 0x1000):addr; + + // check if overlap + 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; + } + } + + // test the end of the area as addr + 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; + } + + add_vma(curr_thread, (unsigned long)addr, len, VIRT_TO_PHYS((unsigned long)kmalloc(len)), prot, 1); + tpf->x0 = (unsigned long)addr; + return (void*)tpf->x0; +} diff --git a/lab6/src/system.c b/lab6/src/system.c new file mode 100644 index 000000000..c8cd8bc38 --- /dev/null +++ b/lab6/src/system.c @@ -0,0 +1,82 @@ +#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) + +/* For all return 0 -> success , -1 failure*/ + +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; + } +} + +void set(long addr, unsigned int value) +{ + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reboot() +{ + //disable_uart(); + reset(1); // timeout = 1/16th of a second? (whatever) +} + +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 + while(1); +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/lab6/src/timer.c b/lab6/src/timer.c new file mode 100644 index 000000000..34b71ba24 --- /dev/null +++ b/lab6/src/timer.c @@ -0,0 +1,177 @@ +#include "timer.h" +#include "registers.h" +#include "uart.h" +#include "malloc.h" +#include "string.h" + +void timer_list_init() +{ + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1": "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0":: "r"(tmp)); + + timer_event_list = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(timer_event_list); +} + +void core_timer_enable() +{ + __asm__ __volatile__( + "mov x1, 1\n\t" + "msr cntp_ctl_el0, x1\n\t" // enable + + //"mrs x1, cntfrq_el0\n\t" + //"mov x2, 0x100000\n\t" + //"mul x1, x1, x2\n\t" //set a big value prevent interrupt immediately + //"msr cntp_tval_el0, x1\n\t" // set expired time + + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // unmask timer interrupt + :::"x1","x2"); +} + +void core_timer_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"); +} + +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_entry((struct list_head *)timer_event); // delete the event + kfree(timer_event->args); // kfree the arg space + kfree(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)->interrupt_time); + } + else + { + set_core_timer_interrupt(10000); // disable timer interrupt (set a very big value) + } +} + +void two_second_alert(char *str) +{ + unsigned long long cntpct_el0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" + : "=r"(cntpct_el0)); //tick now + + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" + : "=r"(cntfrq_el0)); //tick frequency + uart_printf("alert '%s': \r\nexception irq_router -> seconds after booting : %d\r\n", str, cntpct_el0 / cntfrq_el0); + + add_timer(two_second_alert, 2, "two_second_alert",0); +} + +void core_timer_handler() +{ + lock(); + //disable_interrupt(); + if (list_empty(timer_event_list)) + { + set_core_timer_interrupt(10000); // disable timer interrupt (set a very big value) + unlock(); + return; + } + + timer_event_callback((timer_event_t *)timer_event_list->next); // do callback and set new interrupt + unlock(); +} + +// give a string argument to callback timeout after seconds +void add_timer(void *callback, unsigned long long timeout, char *args, int bytick) +{ + timer_event_t *the_timer_event = kmalloc(sizeof(timer_event_t)); //need to kfree by event handler + + // store argument string into timer_event + the_timer_event->args = kmalloc(strlen(args) + 1); + strcpy(the_timer_event->args, args); + + if(bytick == 0) + { + the_timer_event->interrupt_time = get_tick_plus_s(timeout); // store interrupt time into timer_event + }else + { + the_timer_event->interrupt_time = get_tick_plus_s(0) + timeout; + } + + the_timer_event->callback = callback; + + // add the timer_event into timer_event_list (sorted) + struct list_head *curr; + + lock(); + list_for_each(curr, timer_event_list) + { + if (((timer_event_t *)curr)->interrupt_time > the_timer_event->interrupt_time) + { + list_add(&the_timer_event->listhead, curr->prev); // add this timer at the place just before the bigger one (sorted) + break; + } + } + + if (list_is_head(curr, timer_event_list)) + { + list_add_tail(&the_timer_event->listhead, timer_event_list); // for the time is the biggest + } + + + // set interrupt to first event + set_core_timer_interrupt_by_tick(((timer_event_t *)timer_event_list->next)->interrupt_time); + unlock(); +} + + +// get cpu tick add some second +unsigned long long get_tick_plus_s(unsigned long long second) +{ + + unsigned long long cntpct_el0 = 0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" + : "=r"(cntpct_el0)); //tick now + + unsigned long long cntfrq_el0 = 0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" + : "=r"(cntfrq_el0)); //tick frequency + + return (cntpct_el0 + cntfrq_el0 * second); +} + +// set timer interrupt time to [expired_time] seconds after now (relatively) +void set_core_timer_interrupt(unsigned long long expired_time) +{ + __asm__ __volatile__( + "mrs x1, cntfrq_el0\n\t" //cntfrq_el0 -> relative time + "mul x1, x1, %0\n\t" + "msr cntp_tval_el0, x1\n\t" // set expired time + :: "r"(expired_time):"x1"); +} + +// directly set timer interrupt time to a cpu tick (directly) +void set_core_timer_interrupt_by_tick(unsigned long long tick) +{ + __asm__ __volatile__( + "msr cntp_cval_el0, %0\n\t" //cntp_cval_el0 -> absolute time + :: "r"(tick)); +} + +int timer_list_get_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/src/uart.c b/lab6/src/uart.c new file mode 100644 index 000000000..5c9d5024c --- /dev/null +++ b/lab6/src/uart.c @@ -0,0 +1,414 @@ +#include "gpio.h" +#include "uart.h" +#include "sprintf.h" +#include "registers.h" +#include "exception.h" + +// get address from linker +extern volatile unsigned char _end; + +int echoflag = 1; + +//implement first in first out buffer with a read index and a write index +static char uart_tx_buffer[MAX_BUF_SIZE] = {}; +unsigned int uart_tx_buffer_widx = 0; //write index +unsigned int uart_tx_buffer_ridx = 0; //read index +static char uart_rx_buffer[MAX_BUF_SIZE] = {}; +unsigned int uart_rx_buffer_widx = 0; +unsigned int uart_rx_buffer_ridx = 0; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + register unsigned int r; + + /* initialize UART */ + *AUX_ENABLE |= 1; // Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + *AUX_MU_CNTL = 0; // Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + *AUX_MU_IER = 0; // Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + *AUX_MU_LCR = 3; // Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + *AUX_MU_MCR = 0; // Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + *AUX_MU_IIR = 0x6; // disable interrupts + *AUX_MU_BAUD = 270; // 115200 baud + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 (disable pull up/down) + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx + + while ((*AUX_MU_LSR & 0x01)) + *AUX_MU_IO; //clean rx data +} + +// maybe don't do so many step +void disable_uart() +{ + register unsigned int r; + *AUX_ENABLE &= ~(unsigned int)1; + *AUX_MU_CNTL = 0; + r = *GPFSEL1; + r |= ((7 << 12) | (7 << 15)); // gpio14, gpio15 + r &= ~(2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 2; // enable pins 14 and 15 (pull down) + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; // flush GPIO setup +} + +/** + * Send a character (only replace uart_put with uart_async_putc when non-echo output) + */ +void uart_putc(char c) +{ + unsigned int intc = c; + /* wait until we can send */ + do + { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); + /* write the character to the buffer */ + *AUX_MU_IO = intc; +} + +/** + * Receive a character + */ +char uart_getc() +{ + char r; + /* wait until something is in the buffer */ + do + { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); + /* read it and return */ + r = (char)(*AUX_MU_IO); + + /* + echo back (disable it when going to userspace) + */ + if(echoflag) + { + if (r == '\r') + { + uart_printf("\r\r\n"); + do + { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x40)); //wait for output success Transmitter idle + } + else if (r == '\x7f') // backspace -> get del + { + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + } + else + { + uart_putc(r); + } + } + /* convert carrige return to newline */ + return r == '\r' ? '\n' : r; +} + +void uart_disable_echo() +{ + echoflag = 0; +} + +void uart_enable_echo() +{ + echoflag = 1; +} + +/** + * Display a string with newline + */ +int uart_puts(char *s) +{ + int i = 0; + + while (*s) + { + uart_putc(*s++); + i++; + } + uart_putc('\r'); + uart_putc('\n'); + + return i + 2; +} + +/** + * Display a string with newline + */ +int uart_async_puts(char *s) +{ + int i = 0; + + while (*s) + { + uart_async_putc(*s++); + i++; + } + uart_async_putc('\r'); + uart_async_putc('\n'); + + return i + 2; +} + +/** + * get a string (use async getc) + */ +char *uart_async_gets(char *buf) +{ + int count; + char c; + char *s; + for (s = buf, count = 0; (c = uart_async_getc()) != '\n' && count != MAX_BUF_SIZE - 100; count++) + { + *s = c; + if (*s == '\x7f') //delete -> backspace + { + count--; + if (count == -1) + { + uart_putc(' '); // prevent back over command line # + continue; + } + s--; + count--; + continue; + } + s++; + } + *s = '\0'; + return buf; +} + +/** + * get a string (use async getc) + */ +char *uart_gets(char *buf) +{ + int count; + char c; + char *s; + for (s = buf, count = 0; (c = uart_getc()) != '\n' && count != MAX_BUF_SIZE - 100; count++) + { + *s = c; + if (*s == '\x7f') + { + count--; + if (count == -1) + { + uart_putc(' '); // prevent back over command line # + continue; + } + s--; + count--; + continue; + } + s++; + } + *s = '\0'; + return buf; +} + +/** + * printf (from https://github.com/bztsrc/raspi3-tutorial/tree/master/12_printf) + initial printf from github dont use any va_end, and it is also can run. (in assembly there is nothing compiled from __builtin_va_end) + */ +int uart_printf(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[MAX_BUF_SIZE]; + // we don't have memory allocation yet, so we + // simply place our string after our code + char *s = (char *)buf; + // use sprintf to format our string + int count = vsprintf(s, fmt, args); + // print out as usual + while (*s) + { + uart_putc(*s++); + } + __builtin_va_end(args); + return count; +} + +int uart_async_printf(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[MAX_BUF_SIZE]; + // we don't have memory allocation yet, so we + // simply place our string after our code + char *s = (char *)buf; + // use sprintf to format our string + int count = vsprintf(s, fmt, args); + // print out as usual + while (*s) + { + uart_async_putc(*s++); + } + __builtin_va_end(args); + return count; +} + +void uart_interrupt_r_handler() +{ + //read buffer full + lock(); + if ((uart_rx_buffer_widx + 1) % MAX_BUF_SIZE == uart_rx_buffer_ridx) + { + *AUX_MU_IIR = 0xC2; /* clear the fifos */ // I dont know why need this but it can prevent big input to cause infinite run here (disable_r_interrupt never work) + disable_mini_uart_r_interrupt(); //disable read interrupt when read buffer full + unlock(); + return; + } + //lock(); + uart_rx_buffer[uart_rx_buffer_widx++] = uart_getc(); + if (uart_rx_buffer_widx >= MAX_BUF_SIZE) + uart_rx_buffer_widx = 0; + + enable_mini_uart_r_interrupt(); // lab 3 : advanced 2 -> unmask device line + unlock(); +} + +void uart_interrupt_w_handler() //can write +{ + // buffer empty + lock(); + if (uart_tx_buffer_ridx == uart_tx_buffer_widx) + { + *AUX_MU_IIR = 0xC4; + disable_mini_uart_w_interrupt(); // disable w_interrupt to prevent interruption without any async output + unlock(); + return; + } + + uart_putc(uart_tx_buffer[uart_tx_buffer_ridx++]); + if (uart_tx_buffer_ridx >= MAX_BUF_SIZE) + uart_tx_buffer_ridx = 0; // cycle pointer + + enable_mini_uart_w_interrupt(); // lab 3 : advanced 2 -> unmask device line + unlock(); +} + +void uart_async_putc(char c) +{ + + // full buffer wait + lock(); + while ((uart_tx_buffer_widx + 1) % MAX_BUF_SIZE == uart_tx_buffer_ridx) + { + unlock(); + // start asynchronous transfer + enable_mini_uart_w_interrupt(); + lock(); + } + + uart_tx_buffer[uart_tx_buffer_widx++] = c; + if (uart_tx_buffer_widx >= MAX_BUF_SIZE) + uart_tx_buffer_widx = 0; // cycle pointer + + // start asynchronous transfer + // enable interrupt to transfer + enable_mini_uart_w_interrupt(); + unlock(); +} + +char uart_async_getc() +{ + enable_mini_uart_r_interrupt(); + // while buffer empty + // enable read interrupt to get some input into buffer + lock(); + while (uart_rx_buffer_ridx == uart_rx_buffer_widx) + { + unlock(); + enable_mini_uart_r_interrupt(); + lock(); + } + + char r = uart_rx_buffer[uart_rx_buffer_ridx++]; + + if (uart_rx_buffer_ridx >= MAX_BUF_SIZE) + uart_rx_buffer_ridx = 0; + + unlock(); + + return r; +} + +void enable_mini_uart_interrupt() +{ + enable_mini_uart_r_interrupt(); + enable_mini_uart_w_interrupt(); + *IRQS1 |= 1 << 29; +} + +void disable_mini_uart_interrupt() +{ + disable_mini_uart_r_interrupt(); + disable_mini_uart_w_interrupt(); +} + +void enable_mini_uart_r_interrupt() +{ + *AUX_MU_IER |= 1; // read interrupt +} + +void enable_mini_uart_w_interrupt() +{ + *AUX_MU_IER |= 2; // write interrupt +} + +void disable_mini_uart_r_interrupt() +{ + *AUX_MU_IER &= ~(1); +} + +void disable_mini_uart_w_interrupt() +{ + *AUX_MU_IER &= ~(2); +} + +int mini_uart_r_interrupt_is_enable() +{ + return *AUX_MU_IER & 1; +} + +int mini_uart_w_interrupt_is_enable() +{ + return *AUX_MU_IER & 2; +} diff --git a/lab7/.gitignore b/lab7/.gitignore new file mode 100644 index 000000000..1d0691eb3 --- /dev/null +++ b/lab7/.gitignore @@ -0,0 +1,3 @@ +.vscode +build +*.img \ No newline at end of file diff --git a/lab7/Makefile b/lab7/Makefile new file mode 100644 index 000000000..b3440d99f --- /dev/null +++ b/lab7/Makefile @@ -0,0 +1,65 @@ +# debug use CFLAGS='-g3' ASMFLAGS='-g3' +# Modify from +# https://github.com/s-matyukevich/raspberry-pi-os/blob/master/src/lesson01/Makefile +ARMGNU ?= aarch64-linux-gnu + +COPS = $(CFLAGS) -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude #-ggdb #-D__FS_DEBUG #-D__DEBUG #-D__DEBUG_MM #-D__DEBUG_MM_ALLOC #-D__DEBUG_MM_SCHED +ASMOPS = $(ASMFLAGS) -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader/bootloader.img + +debug: COPS += -DDEBUG +debug: ASMOPS += -DDEBUG +debug: all + +clean : + rm -rf $(BUILD_DIR) *.img + make -C bootloader clean + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/fs/%_c.o: $(SRC_DIR)/fs/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -g -T linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -g $(BUILD_DIR)/kernel8.elf -O binary kernel8.img + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +run_display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +run_display_debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +run_debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -serial null -serial stdio -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +connect_raspi: + sudo screen /dev/ttyUSB0 115200 + +bootloader/bootloader.img: + make -C bootloader + +filesystem: + cd rootfs;find . | cpio -o -H newc > ../initramfs.cpio + diff --git a/lab7/bcm2710-rpi-3-b-plus.dtb b/lab7/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..3934b3a26 Binary files /dev/null and b/lab7/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab7/bootloader/Makefile b/lab7/bootloader/Makefile new file mode 100644 index 000000000..6b7bb6b60 --- /dev/null +++ b/lab7/bootloader/Makefile @@ -0,0 +1,50 @@ +# debug use CFLAGS='-g' ASMFLAGS='-g' +# Modify from +# https://github.com/s-matyukevich/raspberry-pi-os/blob/master/src/lesson01/Makefile +ARMGNU ?= aarch64-linux-gnu + +COPS = $(CFLAGS) -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude #-ggdb #-D__FS_DEBUG #-D__DEBUG #-D__DEBUG_MM #-D__DEBUG_MM_ALLOC #-D__DEBUG_MM_SCHED +ASMOPS = $(ASMFLAGS) -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : bootloader.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +bootloader.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -g -T linker.ld -o $(BUILD_DIR)/bootloader.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -g $(BUILD_DIR)/bootloader.elf -O binary bootloader.img + +run: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial stdio + +run_pty: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial pty -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb + +run_debug: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial stdio -dtb ../bcm2710-rpi-3-b-plus.dtb -s -S + +run_debug_pty: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial pty -s -S + +connect_raspi: + sudo screen /dev/ttyUSB0 115200 + diff --git a/lab7/bootloader/include/gpio.h b/lab7/bootloader/include/gpio.h new file mode 100644 index 000000000..399ae80f2 --- /dev/null +++ b/lab7/bootloader/include/gpio.h @@ -0,0 +1,25 @@ +#ifndef GPIO_H +#define GPIO_H + +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C)) + +#endif diff --git a/lab7/bootloader/include/mbox.h b/lab7/bootloader/include/mbox.h new file mode 100644 index 000000000..46160f821 --- /dev/null +++ b/lab7/bootloader/include/mbox.h @@ -0,0 +1,26 @@ +/* a properly aligned buffer */ +/* use this buffer(global variable) directly and the mbox_call will use it after call*/ +/* mbox format https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ +/* mbox address need to be aligned to 16 bytes */ +extern volatile unsigned int mbox[36]; + +#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 + +/* tags */ +#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); \ No newline at end of file diff --git a/lab7/bootloader/include/shell.h b/lab7/bootloader/include/shell.h new file mode 100644 index 000000000..74bd0aa42 --- /dev/null +++ b/lab7/bootloader/include/shell.h @@ -0,0 +1,8 @@ +#ifndef SHELL_H +#define SHELL_H + +void shell(); +void do_cmd(char* cmd); +void print_system_messages(); + +#endif \ No newline at end of file diff --git a/lab7/bootloader/include/string.h b/lab7/bootloader/include/string.h new file mode 100644 index 000000000..8123bc2d4 --- /dev/null +++ b/lab7/bootloader/include/string.h @@ -0,0 +1,9 @@ +#ifndef STRING_H +#define STRING_H + +int strcmp (const char * s1, const char * s2 ); +char* strcat (char *dest, const char *src); +unsigned long long strlen(const char *str); +char* strcpy (char *dest, const char *src); +char* memcpy (void *dest, const void *src, unsigned long long len); +#endif \ No newline at end of file diff --git a/lab7/bootloader/include/system.h b/lab7/bootloader/include/system.h new file mode 100644 index 000000000..79410c43e --- /dev/null +++ b/lab7/bootloader/include/system.h @@ -0,0 +1,14 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +extern char* _dtb; + +int get_board_revision(unsigned int* board_revision); +int get_arm_memory_info(unsigned int* base_addr,unsigned int* size); +void set(long addr, unsigned int value); +void reboot(); +void reset(int tick); +void cancel_reset(); +void load_kernel(); + +#endif \ No newline at end of file diff --git a/lab7/bootloader/include/uart.h b/lab7/bootloader/include/uart.h new file mode 100644 index 000000000..e226d69aa --- /dev/null +++ b/lab7/bootloader/include/uart.h @@ -0,0 +1,16 @@ +#ifndef UART_H +#define UART_H + +#define MAX_BUF_SIZE 0x100 + +void uart_init(); +void uart_putc(char c); +char uart_getc(); +int uart_puts(char *s); +char* uart_gets(char *buf); +int uart_printf(char *s); +void uart_hex(unsigned int d); +void disable_uart(); +char uart_getc_pure(); + +#endif diff --git a/lab7/bootloader/linker.ld b/lab7/bootloader/linker.ld new file mode 100644 index 000000000..3d0a0f5d1 --- /dev/null +++ b/lab7/bootloader/linker.ld @@ -0,0 +1,22 @@ +SECTIONS +{ + . = 0x70000; + _code_relocate_place = .; + /*bootloader on 0x80000, kernel on 0x80000 need to self relocate bootloader to other place*/ + . = 0x80000; + PROVIDE(_start = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start)>>3; +__code_size = (_end - _start); diff --git a/lab7/bootloader/src/main.c b/lab7/bootloader/src/main.c new file mode 100644 index 000000000..b56ee1741 --- /dev/null +++ b/lab7/bootloader/src/main.c @@ -0,0 +1,44 @@ +#include "uart.h" +#include "mbox.h" +#include "shell.h" +#include "string.h" +#include "system.h" + +extern char* _code_relocate_place; +extern unsigned long long __code_size; +extern unsigned long long _start; +extern char* _dtb; + +void code_relocate(char * addr); + +int relocate=1; + +void main(char* arg) +{ + _dtb = arg; + char* reloc_place = (char*)&_code_relocate_place; + + if(relocate) // only do relocate once + { + relocate = 0; + code_relocate(reloc_place); + } + + // set up serial console + uart_init(); + + shell(); +} + +// relocate code and jump to there +void code_relocate(char * addr) +{ + unsigned long long size = (unsigned long long)&__code_size; + char* start = (char *)&_start; + for(unsigned long long i=0;i request successful +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +/** + * 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 int r = (((unsigned int)((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 == *MBOX_READ) + /* is it a valid successful response? */ + return mbox[1]==MBOX_RESPONSE; + } + return 0; +} \ No newline at end of file diff --git a/lab7/bootloader/src/shell.c b/lab7/bootloader/src/shell.c new file mode 100644 index 000000000..e0f1766d1 --- /dev/null +++ b/lab7/bootloader/src/shell.c @@ -0,0 +1,63 @@ +#include "uart.h" +#include "string.h" +#include "shell.h" +#include "mbox.h" +#include "system.h" + +void shell() +{ + char cmd[MAX_BUF_SIZE]; + print_system_messages(); + uart_puts("Welcome, this is bootloader. Try to load kernel with uart with protocol in system.c(load_kernel)"); + while(1) + { + uart_printf("# "); + uart_gets(cmd); + do_cmd(cmd); + } +} + +void do_cmd(char* cmd) +{ + if(strcmp(cmd,"help")==0) + { + uart_puts("help : print this help menu"); + uart_puts("hello : print Hello World!"); + uart_puts("reboot : reboot the device"); + uart_puts("load_kernel : load kernel code from uart to 0x80000 and jump to it!"); + } + else if(strcmp(cmd,"hello")==0) + { + uart_puts("Hello World!"); + } + else if(strcmp(cmd,"reboot")==0) + { + reboot(); + }else if(strcmp(cmd,"load_kernel")==0) + { + load_kernel(); + }else + { + uart_puts("Unknown command!"); + } +} + +void print_system_messages() +{ + unsigned int board_revision; + get_board_revision(&board_revision); + uart_printf("Board revision is : 0x"); + uart_hex(board_revision); + uart_puts(""); + + unsigned int arm_mem_base_addr; + unsigned int arm_mem_size; + + get_arm_memory_info(&arm_mem_base_addr,&arm_mem_size); + uart_printf("ARM memory base address in bytes : 0x"); + uart_hex(arm_mem_base_addr); + uart_puts(""); + uart_printf("ARM memory size in bytes : 0x"); + uart_hex(arm_mem_size); + uart_puts(""); +} \ No newline at end of file diff --git a/lab7/bootloader/src/start.S b/lab7/bootloader/src/start.S new file mode 100644 index 000000000..446a11244 --- /dev/null +++ b/lab7/bootloader/src/start.S @@ -0,0 +1,33 @@ +.section ".text.boot" + +.global _start + +//avoid use x0 which stored dtb place +_start: + + // read cpu id, stop slave cores + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + // cpu id > 0, stop +1: wfe + b 1b +2: // cpu id == 0 + + // set top of stack just before our code (stack grows to a lower address per AAPCS64) + ldr x1, =_start + mov sp, x1 + + // clear bss + ldr x1, =__bss_start + ldr w2, =__bss_size +3: cbz w2, 4f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b + +4: + // jump to C code, should not return + bl main + // for failsafe, halt this core too + b 1b diff --git a/lab7/bootloader/src/string.c b/lab7/bootloader/src/string.c new file mode 100644 index 000000000..de4783cae --- /dev/null +++ b/lab7/bootloader/src/string.c @@ -0,0 +1,45 @@ +#include "string.h" +#include + +int strcmp (const char *p1, const char *p2) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + return c1 - c2; +} + +char* strcat (char *dest, const char *src) +{ + strcpy (dest + strlen (dest), src); + return dest; +} + +char* strcpy (char *dest, const char *src) +{ + return memcpy (dest, src, strlen (src) + 1); +} + +unsigned long long strlen(const char *str) +{ + size_t count = 0; + while((unsigned char)*str++)count++; + return count; +} + +char* memcpy (void *dest, const void *src, unsigned long long len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} \ No newline at end of file diff --git a/lab7/bootloader/src/system.c b/lab7/bootloader/src/system.c new file mode 100644 index 000000000..286d0d676 --- /dev/null +++ b/lab7/bootloader/src/system.c @@ -0,0 +1,121 @@ +#include "system.h" +#include "uart.h" +#include "mbox.h" + +extern char _start[]; //bootloader load kernel to here +char* _dtb; + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +/* For all return 0 -> success , -1 failure*/ + +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; + } +} + +void set(long addr, unsigned int value) +{ + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reboot() +{ + //disable_uart(); + reset(1); // timeout = 1/16th of a second? (whatever) +} + +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 + while(1); // wati for clock +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} + + +//編譯器不要優化這段 +#pragma GCC push_options +#pragma GCC optimize ("O0") + +void load_kernel() +{ + // prevent dtb been rewrited by kernel + char* temp_dtb = _dtb; + char c; + unsigned long long kernel_size=0; + char* kernel_start = (char*) (&_start); + + uart_puts("kernel size:"); + for(int i=0;i<8;i++) //protocol : use little endian to get kernel size + { + c = uart_getc_pure(); + kernel_size += c<<(i*8); + } + + + for(int i=0;i get del + { + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + }else + { + uart_putc(r); + } + /* convert carrige return to newline */ + return r=='\r'?'\n':r; +} + +/** + * Display a string with newline + */ +int uart_puts(char *s) { + int i=0; + + while(*s) { + uart_putc(*s++); + i++; + } + uart_putc('\r'); + uart_putc('\n'); + + return i+2; +} + +/** + * get a string + */ +char* uart_gets(char *buf) +{ + int count; + char c; + char *s; + for (s = buf,count = 0; (c = uart_getc()) != '\n' && count!=MAX_BUF_SIZE-1 ;count++) + { + *s = c; + if(*s=='\x7f') + { + count--; + if(count==-1) + { + uart_putc(' '); // prevent back over command line # + continue; + } + s--; + count--; + continue; + } + s++; + } + *s = '\0'; + return buf; +} + +/** + * printf (TODO) + */ +int uart_printf(char *s) { + int i = 0; + while(*s) { + uart_putc(*s++); + i++; + } + return i; +} + +/** + * Display a binary value in hexadecimal + */ +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_putc(n); + } +} + + +/** + * Receive a character without echo and any translation + */ +char uart_getc_pure() { + char r; + /* wait until something is in the buffer */ + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + return r; +} diff --git a/lab7/config.txt b/lab7/config.txt new file mode 100644 index 000000000..49fc25695 --- /dev/null +++ b/lab7/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x20000000 diff --git a/lab7/include/cpio.h b/lab7/include/cpio.h new file mode 100644 index 000000000..d92d441c4 --- /dev/null +++ b/lab7/include/cpio.h @@ -0,0 +1,40 @@ +#ifndef CPIO_H +#define CPIO_H + + +/* + cpio format : https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 + header,file path,file data,header ...... + header+file path (padding 4 bytes) + file data (padding 4 bytes) (max size 4gb) +*/ + +#define CPIO_NEWC_HEADER_MAGIC "070701" // big endian + +void* CPIO_DEFAULT_PLACE; // init in main +void *CPIO_DEFAULT_END; + +struct cpio_newc_header +{ + char c_magic[6]; //magic The string "070701". + 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]; //check This field is always set to zero by writers and ignored by readers. +}; + +/* 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); + +#endif \ No newline at end of file diff --git a/lab7/include/dtb.h b/lab7/include/dtb.h new file mode 100644 index 000000000..17c387b6c --- /dev/null +++ b/lab7/include/dtb.h @@ -0,0 +1,24 @@ +#ifndef DTB_H +#define DTB_H + +#define uint32_t unsigned int +#define uint64_t unsigned long long + +// manipulate device tree with dtb file format +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +char* dtb_place; +extern void* CPIO_DEFAULT_PLACE; // initialize by callback dtb_callback_initramfs in main 本來是寫死的 +typedef void (*dtb_callback)(uint32_t node_type, char *name, void *value, uint32_t name_size); + +uint32_t uint32_endian_big2lttle(uint32_t data); +void traverse_device_tree(void *base,dtb_callback callback); //traverse dtb tree +void dtb_callback_show_tree(uint32_t node_type, char *name, void *value, uint32_t name_size); +void dtb_callback_initramfs(uint32_t node_type, char *name, void *value, uint32_t name_size); +void reserve_memory_block_with_dtb(); + +#endif \ No newline at end of file diff --git a/lab7/include/exception.h b/lab7/include/exception.h new file mode 100644 index 000000000..2d9818a2d --- /dev/null +++ b/lab7/include/exception.h @@ -0,0 +1,98 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include "stddef.h" +// https://github.com/Tekki/raspberrypi-documentation/blob/master/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf p16 +#define CORE0_INTERRUPT_SOURCE ((volatile unsigned int *)(PHYS_TO_VIRT(0x40000060))) + +/* +The basic pending register shows which interrupt are pending. To speed up interrupts processing, a +number of 'normal' interrupt status bits have been added to this register. This makes the 'IRQ +pending base' register different from the other 'base' interrupt registers +p112-115 https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf +*/ +#define PBASE PHYS_TO_VIRT(0x3F000000) +#define IRQ_BASIC_PENDING ((volatile unsigned int*)(PBASE+0x0000B200)) +#define IRQ_PENDING_1 ((volatile unsigned int*)(PBASE+0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int*)(PBASE+0x0000B208)) +#define FIQ_CONTROL ((volatile unsigned int*)(PBASE+0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(PBASE+0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int*)(PBASE+0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(PBASE+0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(PBASE+0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(PBASE+0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int*)(PBASE+0x0000B224)) + +#define IRQ_PENDING_1_AUX_INT (1<<29) +#define INTERRUPT_SOURCE_GPU (1<<8) +#define INTERRUPT_SOURCE_CNTPNSIRQ (1<<1) +//#define SYSTEM_TIMER_MATCH_1 (1<<1) +//#define SYSTEM_TIMER_MATCH_3 (1<<3) +//#define UART_INT (1ULL<<57) + +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 sync_64_router(trapframe_t *tpf, unsigned long esr_el1); +void irq_router(trapframe_t *tpf); +void invalid_exception_router(); + +static inline void enable_interrupt() +{ + __asm__ __volatile__("msr daifclr, 0xf"); +} + +static inline void disable_interrupt() +{ + __asm__ __volatile__("msr daifset, 0xf"); +} + +unsigned long long is_disable_interrupt(); +void lock(); +void unlock(); + +#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 + +#endif \ No newline at end of file diff --git a/lab7/include/filesystem.h b/lab7/include/filesystem.h new file mode 100644 index 000000000..3ea204a03 --- /dev/null +++ b/lab7/include/filesystem.h @@ -0,0 +1,12 @@ +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +extern void* CPIO_DEFAULT_PLACE; + +int ls(char* working_dir); +int cat(char* thefilepath); +int execfile(char* thefilepath); +unsigned int get_file_size(char *thefilepath); +char *get_file_start(char *thefilepath); + +#endif \ No newline at end of file diff --git a/lab7/include/fs/dev_framebuffer.h b/lab7/include/fs/dev_framebuffer.h new file mode 100644 index 000000000..edc77f0a2 --- /dev/null +++ b/lab7/include/fs/dev_framebuffer.h @@ -0,0 +1,22 @@ +#ifndef DEV_FRAMEBUFFER_H +#define DEV_FRAMEBUFFER_H + +#include "stddef.h" +#include "fs/vfs.h" + +struct framebuffer_info +{ + unsigned int width; + unsigned int height; + unsigned int pitch; + unsigned int isrgb; +}; + +int init_dev_framebuffer(); + +int dev_framebuffer_write(struct file *file, const void *buf, size_t len); +int dev_framebuffer_read(struct file *file, void *buf, size_t len); +int dev_framebuffer_open(struct vnode *file_node, struct file **target); +int dev_framebuffer_close(struct file *file); + +#endif \ No newline at end of file diff --git a/lab7/include/fs/dev_uart.h b/lab7/include/fs/dev_uart.h new file mode 100644 index 000000000..3051efdd8 --- /dev/null +++ b/lab7/include/fs/dev_uart.h @@ -0,0 +1,14 @@ +#ifndef DEV_UART_H +#define DEV_UART_H + +#include "stddef.h" +#include "fs/vfs.h" + +int init_dev_uart(); + +int dev_uart_write(struct file *file, const void *buf, size_t len); +int dev_uart_read(struct file *file, void *buf, size_t len); +int dev_uart_open(struct vnode *file_node, struct file **target); +int dev_uart_close(struct file *file); + +#endif \ No newline at end of file diff --git a/lab7/include/fs/initramfs.h b/lab7/include/fs/initramfs.h new file mode 100644 index 000000000..3d40db811 --- /dev/null +++ b/lab7/include/fs/initramfs.h @@ -0,0 +1,33 @@ +#ifndef INITRAMFS_H +#define INITRAMFS_H + +#include "stddef.h" +#include "fs/vfs.h" + +#define INITRAMFS_MAX_DIR_ENTRY 100 + +struct initramfs_inode +{ + enum node_type type; + char* name; + struct vnode *entry[INITRAMFS_MAX_DIR_ENTRY]; + char *data; + size_t datasize; +}; + +int register_initramfs(); +int initramfs_setup_mount(struct filesystem *fs, struct mount *_mount); + +int initramfs_write(struct file *file, const void *buf, size_t len); +int initramfs_read(struct file *file, void *buf, size_t len); +int initramfs_open(struct vnode *file_node, struct file **target); +int initramfs_close(struct file *file); +long initramfs_getsize(struct vnode *vd); + +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum node_type type); + +#endif \ No newline at end of file diff --git a/lab7/include/fs/tmpfs.h b/lab7/include/fs/tmpfs.h new file mode 100644 index 000000000..5298f3a54 --- /dev/null +++ b/lab7/include/fs/tmpfs.h @@ -0,0 +1,35 @@ +#ifndef TMPFS_H +#define TMPFS_H + +#include "stddef.h" +#include "fs/vfs.h" + +#define FILE_NAME_MAX 16 +#define MAX_DIR_ENTRY 17 +#define MAX_FILE_SIZE 4096 + +struct tmpfs_inode +{ + enum node_type type; + char name[FILE_NAME_MAX]; + struct vnode *entry[MAX_DIR_ENTRY]; + char *data; + size_t datasize; +}; + +int register_tmpfs(); +int tmpfs_setup_mount(struct filesystem *fs, struct mount *_mount); + +int tmpfs_write(struct file *file, const void *buf, size_t len); +int tmpfs_read(struct file *file, void *buf, size_t len); +int tmpfs_open(struct vnode *file_node, struct file **target); +int tmpfs_close(struct file *file); +long tmpfs_getsize(struct vnode *vd); + +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); + +struct vnode *tmpfs_create_vnode(struct mount *_mount, enum node_type type); + +#endif \ No newline at end of file diff --git a/lab7/include/fs/vfs.h b/lab7/include/fs/vfs.h new file mode 100644 index 000000000..e0c5a502e --- /dev/null +++ b/lab7/include/fs/vfs.h @@ -0,0 +1,90 @@ +#ifndef VFS_H +#define VFS_H + +#include "stddef.h" + +#define MAX_PATH_NAME 255 +#define O_CREAT 00000100 +#define SEEK_SET 0 + +enum node_type +{ + dir_t, + file_t +}; + +struct vnode +{ + struct mount *mount; + struct vnode_operations *v_ops; + struct file_operations *f_ops; + void *internal; +}; + +// file handle +struct file +{ + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +}; + +struct mount +{ + struct vnode *root; + struct filesystem *fs; +}; + +struct filesystem +{ + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +}; + +struct file_operations +{ + int (*write)(struct file *file, const void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + long (*lseek64)(struct file *file, long offset, int whence); + long (*getsize)(struct vnode *vd); +}; + +struct vnode_operations +{ + int (*lookup)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, + const char *component_name); +}; + +struct mount *rootfs; + +int register_filesystem(struct filesystem *fs); +int register_dev(struct file_operations* fo); +struct filesystem *find_filesystem(const char *fs_name); +int vfs_open(const char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, const void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); +int vfs_mkdir(const char *pathname); +int vfs_mount(const char *target, const char *filesystem); +int vfs_lookup(const char *pathname, struct vnode **target); +long vfs_lseek64(struct file *file, long offset, int whence); + +#define MAX_FS_REG 0x50 +#define MAX_DEV_REG 0x10 + + struct filesystem reg_fs[MAX_FS_REG]; +struct file_operations reg_dev[MAX_DEV_REG]; + +void init_rootfs(); +void vfs_test(); +char* path_to_absolute(char* path,char* curr_working_dir); +int op_deny(); + +#endif \ No newline at end of file diff --git a/lab7/include/gpio.h b/lab7/include/gpio.h new file mode 100644 index 000000000..bb286555a --- /dev/null +++ b/lab7/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 diff --git a/lab7/include/irqtask.h b/lab7/include/irqtask.h new file mode 100644 index 000000000..1aaccc86a --- /dev/null +++ b/lab7/include/irqtask.h @@ -0,0 +1,25 @@ +#ifndef IRQTASK_H +#define IRQTASK_H + +#include "list.h" + +// set uart irq priority = 1, timer = 0 +#define UART_IRQ_PRIORITY 1 +#define TIMER_IRQ_PRIORITY 0 + +//like timer_event +typedef struct irq_task +{ + struct list_head listhead; + + unsigned long long priority; //store priority (smaller number is more preemptive) + + void *task_function; // task function pointer +} irq_task_t; + +void add_task(void *task_function, unsigned long long priority); +void run_task(irq_task_t *the_task); +void run_preemptive_tasks(); +void task_list_init(); + +#endif \ No newline at end of file diff --git a/lab7/include/list.h b/lab7/include/list.h new file mode 100644 index 000000000..5794e63c1 --- /dev/null +++ b/lab7/include/list.h @@ -0,0 +1,137 @@ +#ifndef LIST_H +#define LIST_H + +/* + * Circular doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + * + * https://github.com/torvalds/linux/blob/master/include/linux/list.h + * https://elixir.bootlin.com/linux/latest/source/scripts/kconfig/list.h#L24 + */ + +typedef struct list_head { + struct list_head *next, *prev; +}list_head_t; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + + +/** + * list_is_head - tests whether @list is the list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next) + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_size(const struct list_head *head) +{ + list_head_t *pos; + int i= 0; + list_for_each(pos, head) + { + i++; + } + return i; +} + +#endif \ No newline at end of file diff --git a/lab7/include/malloc.h b/lab7/include/malloc.h new file mode 100644 index 000000000..4388d8395 --- /dev/null +++ b/lab7/include/malloc.h @@ -0,0 +1,49 @@ +#ifndef MALLOC_H +#define MALLOC_H + +#include "list.h" +#include "uart.h" +#include "exception.h" +#include "dtb.h" + +#define MAXORDER 7 +#define MAXCACHEORDER 4 // 32, 64, 128, 256, 512 (for every 32bytes) + +// simple_malloc +void *simple_malloc(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 *kmalloc(unsigned int size); +void kfree(void *ptr); + +typedef struct frame +{ + struct list_head 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(); +#endif \ No newline at end of file diff --git a/lab7/include/mbox.h b/lab7/include/mbox.h new file mode 100644 index 000000000..6af1ae633 --- /dev/null +++ b/lab7/include/mbox.h @@ -0,0 +1,44 @@ +#ifndef MBOX_H +#define MBOX_H + +#include "gpio.h" + +/* a properly aligned buffer */ +/* use this buffer(global variable) directly and the mbox_call will use it after call*/ +/* mbox format https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ +/* mbox address need to be aligned to 16 bytes */ +extern volatile unsigned int mbox[36]; + +#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 + +/* tags */ +#define GET_BOARD_REVISION 0x10002 +#define MBOX_TAG_GETSERIAL 0x10004 +#define GET_ARM_MEMORY 0x10005 +#define MBOX_TAG_LAST 0 + +#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 + +int mbox_call(unsigned char ch); + +#endif \ No newline at end of file diff --git a/lab7/include/mmu.h b/lab7/include/mmu.h new file mode 100644 index 000000000..301a40f16 --- /dev/null +++ b/lab7/include/mmu.h @@ -0,0 +1,58 @@ +#ifndef MMU_H +#define MMU_H + +#include "stddef.h" +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) +#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 + +#define PD_TABLE 0b11L +#define PD_BLOCK 0b01L +#define PD_UNX (1L << 54) +#define PD_KNX (1L << 53) +#define PD_ACCESS (1L << 10) +#define PD_UK_ACCESS (1L << 6) +#define PD_RDONLY (1L << 7) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + +#define PHYS_TO_VIRT(x) (x + 0xffff000000000000) +#define VIRT_TO_PHYS(x) (x - 0xffff000000000000) +#define ENTRY_ADDR_MASK 0xfffffffff000L + +#ifndef __ASSEMBLER__ + +#define kernel_pgd_addr 0x1000 +#define kernel_pud_addr 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 mappages(size_t *pgd_p, size_t va, size_t size, size_t pa, size_t flag); +void add_vma(struct thread *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; + +} vm_area_struct_t; + +#endif //__ASSEMBLER__ + +#endif \ No newline at end of file diff --git a/lab7/include/registers.h b/lab7/include/registers.h new file mode 100644 index 000000000..41886d8d6 --- /dev/null +++ b/lab7/include/registers.h @@ -0,0 +1,12 @@ +#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))) + +#define STR(x) #x +#define XSTR(s) STR(s) + +#endif \ No newline at end of file diff --git a/lab7/include/sched.h b/lab7/include/sched.h new file mode 100644 index 000000000..373316444 --- /dev/null +++ b/lab7/include/sched.h @@ -0,0 +1,72 @@ +#ifndef SCHED_H +#define SCHED_H + +#include "list.h" +#include "fs/vfs.h" +#include "syscall.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x4000 +#define KSTACK_SIZE 0x4000 +#define SIGNAL_MAX 64 + +extern void switch_to(void *curr_context, void *next_context); +extern void store_context(void *curr_context); +extern void load_context(void *curr_context); +extern void *get_current(); + +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; // use for MMU mapping (user space) +} thread_context_t; + +typedef struct thread +{ + list_head_t listhead; + thread_context_t context; + char *data; + unsigned int datasize; + int iszombie; + int pid; + int isused; + char* stack_alloced_ptr; + char *kernel_stack_alloced_ptr; + void (*singal_handler[SIGNAL_MAX+1])(); + int sigcount[SIGNAL_MAX + 1]; + void (*curr_signal_handler)(); + int signal_is_checking; + list_head_t vma_list; + thread_context_t signal_saved_context; + char curr_working_dir[MAX_PATH_NAME+1]; + struct file* file_descriptors_table[MAX_FD+1]; +} thread_t; + +thread_t *curr_thread; +list_head_t *run_queue; +list_head_t *wait_queue; + +thread_t threads[PIDMAX + 1]; + +void schedule_timer(char *notuse); +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/lab7/include/shell.h b/lab7/include/shell.h new file mode 100644 index 000000000..74bd0aa42 --- /dev/null +++ b/lab7/include/shell.h @@ -0,0 +1,8 @@ +#ifndef SHELL_H +#define SHELL_H + +void shell(); +void do_cmd(char* cmd); +void print_system_messages(); + +#endif \ No newline at end of file diff --git a/lab7/include/signal.h b/lab7/include/signal.h new file mode 100644 index 000000000..9e6c2cbca --- /dev/null +++ b/lab7/include/signal.h @@ -0,0 +1,16 @@ +#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/lab7/include/sprintf.h b/lab7/include/sprintf.h new file mode 100644 index 000000000..101e564e3 --- /dev/null +++ b/lab7/include/sprintf.h @@ -0,0 +1,27 @@ +/* + * 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. + * + */ + +unsigned int sprintf(char *dst, char* fmt, ...); +unsigned int vsprintf(char *dst,char* fmt, __builtin_va_list args); \ No newline at end of file diff --git a/lab7/include/stddef.h b/lab7/include/stddef.h new file mode 100644 index 000000000..d3f418ebb --- /dev/null +++ b/lab7/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/lab7/include/string.h b/lab7/include/string.h new file mode 100644 index 000000000..e8c814ed4 --- /dev/null +++ b/lab7/include/string.h @@ -0,0 +1,16 @@ +#include "stddef.h" + +#ifndef STRING_H +#define STRING_H + +int strcmp (const char * s1, const char * s2 ); +int strncmp (const char *s1, const char *s2, unsigned long long n); +char* strcat (char *dest, const char *src); +unsigned long long strlen(const char *str); +char* strcpy (char *dest, const char *src); +char* memcpy (void *dest, const void *src, unsigned long long len); +char* strchr (register const char *s, int c); +int atoi(char* str); +void *memset(void *s, int c, size_t n); + +#endif \ No newline at end of file diff --git a/lab7/include/syscall.h b/lab7/include/syscall.h new file mode 100644 index 000000000..7ab02b5f0 --- /dev/null +++ b/lab7/include/syscall.h @@ -0,0 +1,54 @@ +#ifndef SYSCALL_H +#define SYSCALL_H + +#include "stddef.h" +#include "exception.h" + +#define MAX_FD 16 + +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); + +// syscall number : 11 +int sys_open(trapframe_t *tpf, const char *pathname, int flags); + +// syscall number : 12 +int sys_close(trapframe_t *tpf, int fd); + +// syscall number : 13 +// remember to return read size or error code +long sys_write(trapframe_t *tpf, int fd, const void *buf, unsigned long count); + +// syscall number : 14 +// remember to return read size or error code +long sys_read(trapframe_t *tpf, int fd, void *buf, unsigned long count); + +// syscall number : 15 +// you can ignore mode, since there is no access control +int sys_mkdir(trapframe_t *tpf, const char *pathname, unsigned mode); + +// syscall number : 16 +// you can ignore arguments other than target (where to mount) and filesystem (fs name) +int sys_mount(trapframe_t *tpf, const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); + +// syscall number : 17 +int sys_chdir(trapframe_t *tpf, const char *path); + +// syscall number : 18 +// you only need to implement seek set +long sys_lseek64(trapframe_t *tpf, int fd, long offset, int whence); + +// syscall number : 19 +int sys_ioctl(trapframe_t *tpf, int fb, unsigned long request, void *info); + +#endif diff --git a/lab7/include/system.h b/lab7/include/system.h new file mode 100644 index 000000000..34bee2bd0 --- /dev/null +++ b/lab7/include/system.h @@ -0,0 +1,11 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +int get_board_revision(unsigned int* board_revision); +int get_arm_memory_info(unsigned int* base_addr,unsigned int* size); +void set(long addr, unsigned int value); +void reboot(); +void reset(int tick); +void cancel_reset(); + +#endif \ No newline at end of file diff --git a/lab7/include/timer.h b/lab7/include/timer.h new file mode 100644 index 000000000..2b84b854a --- /dev/null +++ b/lab7/include/timer.h @@ -0,0 +1,34 @@ +#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) + +typedef struct timer_event +{ + struct list_head listhead; + + unsigned long long interrupt_time; //store as tick time after cpu start + + void *callback; // interrupt -> timer_callback -> callback(args) + + char *args; // need to free the string by event callback function +} timer_event_t; + +struct list_head *timer_event_list; // first head has nothing, store timer_event_t after it + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_handler(); + +//now the callback only support "funcion(char *)", char* in args (defualt by second) +void add_timer(void *callback, unsigned long long timeout, char *args, int bytick); +unsigned long long get_tick_plus_s(unsigned long long second); +void set_core_timer_interrupt(unsigned long long expired_time); +void set_core_timer_interrupt_by_tick(unsigned long long tick); +void two_second_alert(char *str); +void timer_list_init(); +int timer_list_get_size(); + +#endif \ No newline at end of file diff --git a/lab7/include/uart.h b/lab7/include/uart.h new file mode 100644 index 000000000..adef0cf64 --- /dev/null +++ b/lab7/include/uart.h @@ -0,0 +1,51 @@ +#ifndef UART_H +#define UART_H + +#include "gpio.h" +#include "uart.h" +#include "sprintf.h" +#include "registers.h" + +#define MAX_BUF_SIZE 0x1000 + +/* 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)) + +void uart_init(); +void uart_disable_echo(); +void uart_enable_echo(); +void uart_putc(char c); +char uart_getc(); +int uart_puts(char *s); +int uart_async_puts(char *s); +char* uart_gets(char *buf); +char* uart_async_gets(char *buf); +int uart_printf(char *fmt, ...); +int uart_async_printf(char *fmt, ...); +void disable_uart(); +void uart_interrupt_r_handler(); +void uart_interrupt_w_handler(); +void enable_mini_uart_interrupt(); +void enable_mini_uart_w_interrupt(); +void enable_mini_uart_r_interrupt(); +void disable_mini_uart_interrupt(); +void disable_mini_uart_w_interrupt(); +void disable_mini_uart_r_interrupt(); +int mini_uart_r_interrupt_is_enable(); +int mini_uart_w_interrupt_is_enable(); + +char uart_async_getc(); +void uart_async_putc(char c); + +#endif diff --git a/lab7/linker.ld b/lab7/linker.ld new file mode 100644 index 000000000..16ef10f6d --- /dev/null +++ b/lab7/linker.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _heap_start = .; + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/lab7/rootfs/get_simpleexec.sh b/lab7/rootfs/get_simpleexec.sh new file mode 100644 index 000000000..782bd723b --- /dev/null +++ b/lab7/rootfs/get_simpleexec.sh @@ -0,0 +1,5 @@ +#!bin/bash + +aarch64-linux-gnu-gcc -c user_exception_test.S +aarch64-linux-gnu-ld -T linker.ld -o user_exception_test.elf user_exception_test.o +aarch64-linux-gnu-objcopy -O binary user_exception_test.elf user_exception_test.img \ No newline at end of file diff --git a/lab7/rootfs/linker.ld b/lab7/rootfs/linker.ld new file mode 100644 index 000000000..783ebe038 --- /dev/null +++ b/lab7/rootfs/linker.ld @@ -0,0 +1,5 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text) } +} \ No newline at end of file diff --git a/lab7/rootfs/testfile1 b/lab7/rootfs/testfile1 new file mode 100644 index 000000000..734d5d5db --- /dev/null +++ b/lab7/rootfs/testfile1 @@ -0,0 +1,2 @@ +1 +1111111111111111111111111111 diff --git a/lab7/rootfs/testfile112345 b/lab7/rootfs/testfile112345 new file mode 100644 index 000000000..539a7773b --- /dev/null +++ b/lab7/rootfs/testfile112345 @@ -0,0 +1 @@ +1111111111111111111111 diff --git a/lab7/rootfs/testfile2222 b/lab7/rootfs/testfile2222 new file mode 100644 index 000000000..876c79950 --- /dev/null +++ b/lab7/rootfs/testfile2222 @@ -0,0 +1 @@ +222222 diff --git a/lab7/rootfs/user_exception_test.S b/lab7/rootfs/user_exception_test.S new file mode 100644 index 000000000..838323763 --- /dev/null +++ b/lab7/rootfs/user_exception_test.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b \ No newline at end of file diff --git a/lab7/send_kernel_to_bootloader.py b/lab7/send_kernel_to_bootloader.py new file mode 100644 index 000000000..f25abfcb6 --- /dev/null +++ b/lab7/send_kernel_to_bootloader.py @@ -0,0 +1,31 @@ +from serial import Serial +from pwn import * +import argparse + +parser = argparse.ArgumentParser(description='NYCU OSDI kernel sender') +parser.add_argument('--filename', metavar='PATH', default='kernel8.img', type=str, help='path to kernel8.img') +parser.add_argument('--device', metavar='TTY',default='/dev/ttyUSB0', type=str, help='path to UART device') +parser.add_argument('--baud', metavar='Hz',default=115200, type=int, help='baud rate') +args = parser.parse_args() + +with open(args.filename,'rb') as fd: + with Serial(args.device, args.baud) as ser: + + kernel_raw = fd.read() + length = len(kernel_raw) + + print("Kernel image size : ", hex(length)) + for i in range(8): + ser.write(p64(length)[i:i+1]) + ser.flush() + + print("Start sending kernel img by uart...") + for i in range(length): + # Use kernel_raw[i: i+1] is byte type. Instead of using kernel_raw[i] it will retrieve int type then cause error + ser.write(kernel_raw[i: i+1]) + ser.flush() + if i % 100 == 0: + print("{:>6}/{:>6} bytes".format(i, length)) + print("{:>6}/{:>6} bytes".format(length, length)) + print("Transfer finished!") + \ No newline at end of file diff --git a/lab7/src/cpio.c b/lab7/src/cpio.c new file mode 100644 index 000000000..0f354b5fd --- /dev/null +++ b/lab7/src/cpio.c @@ -0,0 +1,64 @@ +#include "cpio.h" +#include "string.h" +#include "uart.h" + + +/* Parse an ASCII hex string into an integer. (big endian)*/ +static unsigned int parse_hex_str(char *s, unsigned int max_len) +{ + unsigned int r = 0; + + for (unsigned int i = 0; i < max_len; i++) { + r *= 16; + if (s[i] >= '0' && s[i] <= '9') { + r += s[i] - '0'; + } else if (s[i] >= 'a' && s[i] <= 'f') { + r += s[i] - 'a' + 10; + } else if (s[i] >= 'A' && s[i] <= 'F') { + r += s[i] - 'A' + 10; + } else { + return r; + } + } + return r; +} + + +/* 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 exists. */ + 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 and store into *filesize + *filesize = parse_hex_str(this_header_pointer->c_filesize,8); + + // end of header is the pathname + *pathname = ((char *)this_header_pointer) + sizeof(struct cpio_newc_header); + + // get file data, file data is just after pathname + unsigned int pathname_length = parse_hex_str(this_header_pointer->c_namesize,8); + unsigned int offset = pathname_length+sizeof(struct cpio_newc_header); + offset = offset%4==0?offset:(offset+4-offset%4); //padding + *data = (char *)this_header_pointer+offset; + + //get next header pointer + if(*filesize==0) + { + *next_header_pointer = (struct cpio_newc_header*)*data; + }else + { + offset = *filesize; + *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. + if(strncmp(*pathname,"TRAILER!!!",sizeof("TRAILER!!!"))==0) + { + *next_header_pointer = 0; + } + + return 0; +} \ No newline at end of file diff --git a/lab7/src/dtb.c b/lab7/src/dtb.c new file mode 100644 index 000000000..1bec30680 --- /dev/null +++ b/lab7/src/dtb.c @@ -0,0 +1,146 @@ +#include "dtb.h" +#include "uart.h" +#include "string.h" +#include "cpio.h" +#include "malloc.h" + +//stored as big endian +struct fdt_header { + uint32_t magic; + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +}; + +struct fdt_reserve_entry +{ + uint64_t address; + uint64_t size; +}; + +uint32_t uint32_endian_big2lttle(uint32_t data) +{ + char* r = (char*)&data; + return (r[3]<<0) | (r[2]<<8) | (r[1]<<16) | (r[0]<<24); +} + +uint64_t uint64_endian_big2lttle(uint64_t data) +{ + char *r = (char *)&data; + return ((unsigned long long)r[7] << 0) | ((unsigned long long)r[6] << 8) | ((unsigned long long)r[5] << 16) | ((unsigned long long)r[4] << 24) | ((unsigned long long)r[3] << 32) | ((unsigned long long)r[2] << 40) | ((unsigned long long)r[1] << 48) | ((unsigned long long)r[0] << 56); +} + +void traverse_device_tree(void *dtb_ptr,dtb_callback callback) +{ + struct fdt_header* header = dtb_ptr; + if(uint32_endian_big2lttle(header->magic) != 0xD00DFEED) + { + uart_puts("traverse_device_tree : wrong magic in traverse_device_tree"); + return; + } + + uint32_t struct_size = uint32_endian_big2lttle(header->size_dt_struct); + char* dt_struct_ptr = (char*)((char*)header + uint32_endian_big2lttle(header->off_dt_struct)); + char* dt_strings_ptr = (char*)((char*)header + uint32_endian_big2lttle(header->off_dt_strings)); + + char* end = (char*)dt_struct_ptr + struct_size; + char* pointer = dt_struct_ptr; + + while(pointer < end) + { + uint32_t token_type = uint32_endian_big2lttle(*(uint32_t*)pointer); + + pointer += 4; + if(token_type == FDT_BEGIN_NODE) + { + callback(token_type,pointer,0,0); + pointer += strlen(pointer); + pointer += 4 - (unsigned long long)pointer%4; //alignment 4 byte + }else if(token_type == FDT_END_NODE) + { + callback(token_type,0,0,0); + }else if(token_type == FDT_PROP) + { + uint32_t len = uint32_endian_big2lttle(*(uint32_t*)pointer); + pointer += 4; + char* name = (char*)dt_strings_ptr + uint32_endian_big2lttle(*(uint32_t*)pointer); + pointer += 4; + callback(token_type,name,pointer,len); + pointer += len; + if((unsigned long long)pointer % 4 !=0)pointer += 4 - (unsigned long long)pointer%4; //alignment 4 byte + }else if(token_type == FDT_NOP) + { + callback(token_type,0,0,0); + }else if(token_type == FDT_END) + { + callback(token_type,0,0,0); + }else + { + uart_printf("error type:%x\r\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;imagic) != 0xD00DFEED) + { + uart_puts("traverse_device_tree : wrong magic in traverse_device_tree"); + return; + } + + char *dt_mem_rsvmap_ptr = (char *)((char *)header + uint32_endian_big2lttle(header->off_mem_rsvmap)); + struct fdt_reserve_entry *reverse_entry = (struct fdt_reserve_entry *)dt_mem_rsvmap_ptr; + + while (reverse_entry->address != 0 || reverse_entry->size != 0) + { + unsigned long long start = PHYS_TO_VIRT(uint64_endian_big2lttle(reverse_entry->address)); + unsigned long long end = uint64_endian_big2lttle(reverse_entry->size) + start; + memory_reserve(start, end); + reverse_entry++; + } + + //also reserve device tree + memory_reserve((unsigned long long)dtb_place, (unsigned long long)dtb_place + uint32_endian_big2lttle(header->totalsize)); +} diff --git a/lab7/src/entry.S b/lab7/src/entry.S new file mode 100644 index 000000000..e6fc28690 --- /dev/null +++ b/lab7/src/entry.S @@ -0,0 +1,208 @@ +// save general registers to stack +.global save_all +.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 +.global load_all +.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_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry sync_el1h // Synchronous EL1h + ventry irq_el1h // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_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_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + + + +sync_invalid_el1t: + //save_all + mov x0,0 + bl invalid_exception_router + //load_all + eret +irq_invalid_el1t: + save_all + mov x0,1 + bl invalid_exception_router + load_all + eret +fiq_invalid_el1t: + save_all + mov x0,2 + bl invalid_exception_router + load_all + eret +error_invalid_el1t: + save_all + mov x0,3 + bl invalid_exception_router + load_all + eret + + + +sync_el1h: + //save_all + mov x0,4 + bl invalid_exception_router + //load_all + eret +irq_el1h: + save_all + mov x0, sp // trap_frame + bl irq_router + load_all + eret +fiq_invalid_el1h: + save_all + mov x0,6 + bl invalid_exception_router + load_all + eret +error_invalid_el1h: + save_all + mov x0,7 + bl invalid_exception_router + load_all + eret + + + +sync_el0_64: + save_all + mov x0, sp // trap_frame + mrs x1, esr_el1 + bl sync_64_router + load_all + eret +irq_el0_64: + save_all + mov x0, sp // trap_frame + bl irq_router + load_all + eret +fiq_invalid_el0_64: + save_all + mov x0,10 + bl invalid_exception_router + load_all + eret +error_invalid_el0_64: + save_all + mov x0,11 + bl invalid_exception_router + load_all + eret + + + +sync_invalid_el0_32: + save_all + mov x0,12 + bl invalid_exception_router + load_all + eret +irq_invalid_el0_32: + save_all + mov x0,13 + bl invalid_exception_router + load_all + eret +fiq_invalid_el0_32: + save_all + mov x0,14 + bl invalid_exception_router + load_all + eret +error_invalid_el0_32: + save_all + mov x0,15 + bl invalid_exception_router + load_all + eret \ No newline at end of file diff --git a/lab7/src/exception.c b/lab7/src/exception.c new file mode 100644 index 000000000..119f52b08 --- /dev/null +++ b/lab7/src/exception.c @@ -0,0 +1,260 @@ +#include "uart.h" +#include "exception.h" +#include "timer.h" +#include "irqtask.h" +#include "syscall.h" +#include "sched.h" +#include "signal.h" +#include "mmu.h" +#include "stddef.h" + +// For svc 0 (supervisor call) +void sync_64_router(trapframe_t *tpf, unsigned long x1) +{ + //uart_printf("syscall no : %d\r\n", tpf->x8); + //uart_printf("elr_el1 : 0x%x\r\n", tpf->elr_el1); + //uart_printf("sp_el0 : 0x%x\r\n", tpf->sp_el0); + //uart_printf("spsr_el1 : 0x%x\r\n", tpf->spsr_el1); + //while(1); + + //For MMU page fault + esr_el1_t *esr; + esr = (esr_el1_t *)&x1; + if (esr->ec == DATA_ABORT_LOWER || esr->ec == INS_ABORT_LOWER) + { + handle_abort(esr); + return; + } + + enable_interrupt(); + unsigned long long syscall_no = tpf->x8; + + if (syscall_no == 0) + { + getpid(tpf); + } + else if(syscall_no == 1) + { + uartread(tpf,(char *) tpf->x0, tpf->x1); + } + else if (syscall_no == 2) + { + uartwrite(tpf,(char *) tpf->x0, tpf->x1); + } + else if (syscall_no == 3) + { + exec(tpf,(char *) tpf->x0, (char **)tpf->x1); + } + else if (syscall_no == 4) + { + fork(tpf); + } + else if (syscall_no == 5) + { + exit(tpf,tpf->x0); + } + else if (syscall_no == 6) + { + syscall_mbox_call(tpf,(unsigned char)tpf->x0, (unsigned int *)tpf->x1); + } + else if (syscall_no == 7) + { + kill(tpf, (int)tpf->x0); + } + else if (syscall_no == 8) + { + signal_register(tpf->x0, (void (*)())tpf->x1); + } + else if (syscall_no == 9) + { + signal_kill(tpf->x0, tpf->x1); + }else if(syscall_no == 10) + { + sys_mmap(tpf,(void *)tpf->x0,tpf->x1,tpf->x2,tpf->x3,tpf->x4,tpf->x5); + } + else if (syscall_no == 11) + { + sys_open(tpf, (char*)tpf->x0, tpf->x1); + } + else if (syscall_no == 12) + { + sys_close(tpf, tpf->x0); + } + else if (syscall_no == 13) + { + sys_write(tpf, tpf->x0, (char *)tpf->x1, tpf->x2); + } + else if (syscall_no == 14) + { + sys_read(tpf, tpf->x0, (char *)tpf->x1, tpf->x2); + } + else if (syscall_no == 15) + { + sys_mkdir(tpf, (char *)tpf->x0, tpf->x1); + } + else if (syscall_no == 16) + { + sys_mount(tpf, (char *)tpf->x0, (char *)tpf->x1, (char *)tpf->x2, tpf->x3, (void*)tpf->x4); + } + else if (syscall_no == 17) + { + sys_chdir(tpf, (char *)tpf->x0); + } + else if(syscall_no == 18) + { + sys_lseek64(tpf, tpf->x0, tpf->x1, tpf->x2); + } + else if(syscall_no == 19) + { + // ioctl 0 will be use to get info + // there will be default value in info + // if it works with default value, you can ignore this syscall + sys_ioctl(tpf, tpf->x0, tpf->x1, (void*)tpf->x2); + tpf->x0 = 0; + } + else if (syscall_no == 50) + { + sigreturn(tpf); + }else + { + uart_printf("unknown syscall\r\n"); + while(1); + } + + // load all shouldn't be irq + disable_interrupt(); + /* + unsigned long long spsr_el1; + __asm__ __volatile__("mrs %0, SPSR_EL1\n\t" : "=r" (spsr_el1)); + + unsigned long long elr_el1; + __asm__ __volatile__("mrs %0, ELR_EL1\n\t" : "=r" (elr_el1)); + + unsigned long long esr_el1; + __asm__ __volatile__("mrs %0, ESR_EL1\n\t" : "=r" (esr_el1));*/ + + //uart_printf("exception sync_el0_64_router -> spsr_el1 : 0x%x, elr_el1 : 0x%x, esr_el1 : 0x%x\r\n",spsr_el1,elr_el1,esr_el1); +} + +void irq_router(trapframe_t* tpf) +{ + //uart_printf("ena : %d\r\n", is_disable_interrupt()); + //uart_printf("irq_basic_pending: %x\n",*IRQ_BASIC_PENDING); + //uart_printf("irq_pending_1: %x\n",*IRQ_PENDING_1); + //uart_printf("irq_pending_2: %x\n",*IRQ_PENDING_2); + //uart_printf("source : %x\n",*CORE0_INTERRUPT_SOURCE); + + //目前實測能從pending_1 AUX_INT, CORE0_INTERRUPT_SOURCE=GPU 辨別其他都是0(或再找) + + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + //https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p13 + /* + AUX_MU_IIR + on read bits[2:1] : + 00 : No interrupts + 01 : Transmit holding register empty + 10 : Receiver holds valid byte + 11: + */ + + // buffer read, write + if (*AUX_MU_IIR & (0b01 << 1)) //can write + { + disable_mini_uart_w_interrupt(); // lab 3 : advanced 2 -> mask device line (enable by handler) + add_task(uart_interrupt_w_handler, UART_IRQ_PRIORITY); + run_preemptive_tasks(); + } + else if (*AUX_MU_IIR & (0b10 << 1)) // can read + { + //不知道為啥關了還會進來 這種時候清除FIFO就可以關掉 (會有最多8個FIFO裡的byte消失) + if (!mini_uart_r_interrupt_is_enable()) + { + *AUX_MU_IIR = 0xC2; + return; + } + + //uart_printf("dd %d %d \r\n", mini_uart_r_interrupt_is_enable(), mini_uart_w_interrupt_is_enable()); + disable_mini_uart_r_interrupt(); // lab 3 : advanced 2 -> mask device line (enable by handler) + add_task(uart_interrupt_r_handler, UART_IRQ_PRIORITY); + run_preemptive_tasks(); + } + else + { + uart_printf("uart handler error\r\n"); + } + + }else if(*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) //from CNTPNS (core_timer) + { + core_timer_disable(); // lab 3 : advanced 2 -> mask device line + add_task(core_timer_handler, TIMER_IRQ_PRIORITY); + run_preemptive_tasks(); + core_timer_enable(); // lab 3 : advanced 2 -> unmask device line + + disable_interrupt(); + //at least two thread running -> schedule for any timer irq + if (run_queue->next->next != run_queue)schedule(); + } + + //only do signal handler when return to user mode + if ((tpf->spsr_el1 & 0b1100) == 0) + { + check_signal(tpf); + } + + // load all shouldn't be irq + disable_interrupt(); +} + +void invalid_exception_router(unsigned long long x0){ + unsigned long long elr_el1; + __asm__ __volatile__("mrs %0, ELR_EL1\n\t" + : "=r"(elr_el1)); + + unsigned long long spsr_el1; + __asm__ __volatile__("mrs %0, SPSR_EL1\n\t" + : "=r"(spsr_el1)); + + unsigned long long lr; + __asm__ __volatile__("mov %0, lr\n\t" + : "=r"(lr)); + + uart_printf("invalid exception : 0x%x\r\n", elr_el1); + uart_printf("invalid exception : 0x%x\r\n", spsr_el1); + uart_printf("invalid exception : 0x%x\r\n", lr); + uart_printf("invalid exception : x0 : %x\r\n",x0); + while(1); +} + +//https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/DAIF--Interrupt-Mask-Bits +//https://www.twblogs.net/a/5b7c4ca52b71770a43da534e +//zero -> enable +//others -> disable +unsigned long long is_disable_interrupt() +{ + unsigned long long daif; + __asm__ __volatile__("mrs %0, daif\n\t" + : "=r"(daif)); + + return daif; //enable -> daif == 0 (no mask) +} + +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/lab7/src/filesystem.c b/lab7/src/filesystem.c new file mode 100644 index 000000000..1ce6fef26 --- /dev/null +++ b/lab7/src/filesystem.c @@ -0,0 +1,148 @@ +#include "cpio.h" +#include "filesystem.h" +#include "uart.h" +#include "string.h" +#include "sched.h" + +int cat(char* thefilepath) +{ + char* filepath; + char* filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = CPIO_DEFAULT_PLACE; + + 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) + { + for(unsigned int i=0;if_pos > pitch * height) + { + uart_printf("????\r\n"); + len = pitch * height - file->f_pos; + } + memcpy(lfb + file->f_pos, buf, len); + file->f_pos += len; + unlock(); + return len; +} + +int dev_framebuffer_open(struct vnode *file_node, struct file **target) +{ + (*target)->f_pos = 0; + (*target)->vnode = file_node; + (*target)->f_ops = &dev_framebuffer_operations; + return 0; +} + +int dev_framebuffer_close(struct file *file) +{ + kfree(file); + return 0; +} \ No newline at end of file diff --git a/lab7/src/fs/dev_uart.c b/lab7/src/fs/dev_uart.c new file mode 100644 index 000000000..f5d5746f2 --- /dev/null +++ b/lab7/src/fs/dev_uart.c @@ -0,0 +1,44 @@ +#include "fs/vfs.h" +#include "fs/dev_uart.h" +#include "uart.h" +#include "malloc.h" + +struct file_operations dev_file_operations = {dev_uart_write, dev_uart_read, dev_uart_open, dev_uart_close, (void *)op_deny, (void *)op_deny}; + +int init_dev_uart() +{ + return register_dev(&dev_file_operations); +} + +int dev_uart_write(struct file *file, const void *buf, size_t len) +{ + const char *cbuf = buf; + for (int i = 0; i < len;i++) + { + uart_async_putc(cbuf[i]); + } + return len; +} + +int dev_uart_read(struct file *file, void *buf, size_t len) +{ + char *cbuf = buf; + for (int i = 0; i < len; i++) + { + cbuf[i] = uart_async_getc(); + } + return len; +} + +int dev_uart_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = &dev_file_operations; + return 0; +} + +int dev_uart_close(struct file *file) +{ + kfree(file); + return 0; +} \ No newline at end of file diff --git a/lab7/src/fs/initramfs.c b/lab7/src/fs/initramfs.c new file mode 100644 index 000000000..75f8d0474 --- /dev/null +++ b/lab7/src/fs/initramfs.c @@ -0,0 +1,145 @@ +#include "fs/initramfs.h" +#include "fs/vfs.h" +#include "string.h" +#include "malloc.h" +#include "cpio.h" + +struct file_operations initramfs_file_operations = {initramfs_write, initramfs_read, initramfs_open, initramfs_close, vfs_lseek64, initramfs_getsize}; +struct vnode_operations initramfs_vnode_operations = {initramfs_lookup, initramfs_create, initramfs_mkdir}; + +int register_initramfs() +{ + struct filesystem fs; + fs.name = "initramfs"; + fs.setup_mount = initramfs_setup_mount; + return register_filesystem(&fs); +} + +int initramfs_setup_mount(struct filesystem *fs, struct mount *_mount) +{ + _mount->fs = fs; + _mount->root = initramfs_create_vnode(0, dir_t); + struct initramfs_inode *ramdir_inode = _mount->root->internal; + + // add all file in initramfs to filesystem + char *filepath; + char *filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = CPIO_DEFAULT_PLACE; + int idx = 0; + + 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("%s", "error\r\n"); + break; + } + + //if this is not TRAILER!!! (last of file) + if (header_pointer != 0) + { + // only support file (no dir) + struct vnode * filevnode = initramfs_create_vnode(0, file_t); + struct initramfs_inode *fileinode = filevnode->internal; + fileinode->data = filedata; + fileinode->datasize = filesize; + fileinode->name = filepath; + ramdir_inode->entry[idx++] = filevnode; + } + } + + return 0; +} + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum node_type type) +{ + struct vnode *v = kmalloc(sizeof(struct vnode)); + v->f_ops = &initramfs_file_operations; + v->v_ops = &initramfs_vnode_operations; + v->mount = _mount; + struct initramfs_inode *inode = kmalloc(sizeof(struct initramfs_inode)); + memset(inode, 0, sizeof(struct initramfs_inode)); + inode->type = type; + inode->data = kmalloc(0x1000); + v->internal = inode; + return v; +} + +// file operations +int initramfs_write(struct file *file, const void *buf, size_t len) +{ + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) +{ + struct initramfs_inode *inode = file->vnode->internal; + + if (len + file->f_pos > inode->datasize) + { + memcpy(buf, inode->data + file->f_pos, inode->datasize - file->f_pos); + file->f_pos += inode->datasize - file->f_pos; + return inode->datasize - file->f_pos; + } + else + { + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += len; + return len; + } + return -1; +} + +int initramfs_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +} + +int initramfs_close(struct file *file) +{ + kfree(file); + return 0; +} + +// vnode operations +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct initramfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < INITRAMFS_MAX_DIR_ENTRY; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode)break; + struct initramfs_inode *inode = vnode->internal; + if (strcmp(component_name, inode->name) == 0) + { + *target = vnode; + return 0; + } + } + + uart_printf("initramfs lookup not found\r\n"); + return -1; +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + return -1; +} + +long initramfs_getsize(struct vnode *vd) +{ + struct initramfs_inode *inode = vd->internal; + return inode->datasize; +} \ No newline at end of file diff --git a/lab7/src/fs/tmpfs.c b/lab7/src/fs/tmpfs.c new file mode 100644 index 000000000..2b199b9cb --- /dev/null +++ b/lab7/src/fs/tmpfs.c @@ -0,0 +1,193 @@ +#include "fs/tmpfs.h" +#include "fs/vfs.h" +#include "string.h" +#include "malloc.h" + +struct file_operations tmpfs_file_operations = {tmpfs_write, tmpfs_read, tmpfs_open, tmpfs_close, vfs_lseek64, tmpfs_getsize}; +struct vnode_operations tmpfs_vnode_operations = {tmpfs_lookup,tmpfs_create,tmpfs_mkdir}; + +int register_tmpfs() +{ + struct filesystem fs; + fs.name = "tmpfs"; + fs.setup_mount = tmpfs_setup_mount; + return register_filesystem(&fs); +} + +int tmpfs_setup_mount(struct filesystem *fs, struct mount *_mount) +{ + _mount->fs = fs; + _mount->root = tmpfs_create_vnode(0,dir_t); + return 0; +} + +struct vnode* tmpfs_create_vnode(struct mount* _mount, enum node_type type) +{ + struct vnode *v = kmalloc(sizeof(struct vnode)); + v->f_ops = &tmpfs_file_operations; + v->v_ops = &tmpfs_vnode_operations; + v->mount = 0; + struct tmpfs_inode* inode = kmalloc(sizeof(struct tmpfs_inode)); + memset(inode, 0, sizeof(struct tmpfs_inode)); + inode->type = type; + inode->data = kmalloc(0x1000); + v->internal = inode; + return v; +} + +// file operations +int tmpfs_write(struct file *file, const void *buf, size_t len) +{ + struct tmpfs_inode *inode = file->vnode->internal; + + memcpy(inode->data + file->f_pos, buf, len); + file->f_pos += len; + if(inode->datasizef_pos)inode->datasize = file->f_pos; + return len; +} + +int tmpfs_read(struct file *file, void *buf, size_t len) +{ + struct tmpfs_inode *inode = file->vnode->internal; + + if(len+file->f_pos > inode->datasize) + { + len = inode->datasize - file->f_pos; + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += inode->datasize - file->f_pos; + return len; + } + else + { + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += len; + return len; + } + return -1; +} + +int tmpfs_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +} + +int tmpfs_close(struct file *file) +{ + kfree(file); + return 0; +} + +// vnode operations +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct tmpfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < MAX_DIR_ENTRY; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if(!vnode)break; + struct tmpfs_inode *inode = vnode->internal; + if (strcmp(component_name, inode->name) == 0) + { + *target = vnode; + return 0; + } + } + + uart_printf("tmpfs lookup not found\r\n"); + return -1; +} + + +int tmpfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct tmpfs_inode *inode = dir_node->internal; + if(inode->type!=dir_t) + { + uart_printf("tmpfs create not dir_t\r\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx < MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx])break; + + struct tmpfs_inode *child_inode = inode->entry[child_idx]->internal; + if (strcmp(child_inode->name,component_name)==0) + { + uart_printf("tmpfs create file exists\r\n"); + return -1; + } + } + + if (child_idx == MAX_DIR_ENTRY) + { + uart_printf("DIR ENTRY FULL\r\n"); + return -1; + } + + struct vnode *_vnode = tmpfs_create_vnode(0, file_t); + inode->entry[child_idx] = _vnode; + if (strlen(component_name) > FILE_NAME_MAX) + { + uart_printf("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct tmpfs_inode *newinode = _vnode->internal; + strcpy(newinode->name, component_name); + + *target = _vnode; + return 0; +} + +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct tmpfs_inode *inode = dir_node->internal; + + if (inode->type != dir_t) + { + uart_printf("tmpfs mkdir not dir_t\r\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx < MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + } + + if(child_idx == MAX_DIR_ENTRY) + { + uart_printf("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > FILE_NAME_MAX) + { + uart_printf("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode* _vnode = tmpfs_create_vnode(0, dir_t); + inode->entry[child_idx] = _vnode; + + struct tmpfs_inode *newinode = _vnode->internal; + strcpy(newinode->name, component_name); + + *target = _vnode; + return 0; +} + +long tmpfs_getsize(struct vnode* vd) +{ + struct tmpfs_inode *inode = vd->internal; + return inode->datasize; +} \ No newline at end of file diff --git a/lab7/src/fs/vfs.c b/lab7/src/fs/vfs.c new file mode 100644 index 000000000..03dfa18da --- /dev/null +++ b/lab7/src/fs/vfs.c @@ -0,0 +1,323 @@ +#include "fs/vfs.h" +#include "fs/tmpfs.h" +#include "malloc.h" +#include "string.h" +#include "fs/initramfs.h" +#include "fs/dev_uart.h" +#include "fs/dev_framebuffer.h" + +int register_filesystem(struct filesystem *fs) +{ + for (int i = 0; i < MAX_FS_REG;i++) + { + if(!reg_fs[i].name) + { + reg_fs[i].name = fs->name; + reg_fs[i].setup_mount = fs->setup_mount; + return i; + } + } + return -1; +} + +int register_dev(struct file_operations *fo) +{ + for (int i = 0; i < MAX_FS_REG; i++) + { + if (!reg_dev[i].open) + { + reg_dev[i] = *fo; + return i; + } + } + return -1; +} + +struct filesystem* find_filesystem(const char* fs_name) +{ + for (int i = 0; i < MAX_FS_REG; i++) + { + if (strcmp(reg_fs[i].name,fs_name)==0) + { + return ®_fs[i]; + } + } + return 0; +} + +int vfs_open(const char *pathname, int flags, struct file **target) +{ + // 1. Lookup pathname + // 3. Create a new file if O_CREAT is specified in flags and vnode not found + struct vnode *node; + if (vfs_lookup(pathname, &node) != 0 && (flags&O_CREAT)) + { + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) + { + if(pathname[i]=='/') + { + last_slash_idx = i; + } + } + + char dirname[MAX_PATH_NAME+1]; + strcpy(dirname, pathname); + dirname[last_slash_idx] = 0; + if (vfs_lookup(dirname,&node)!=0) + { + uart_printf("cannot ocreate no dir name\r\n"); + return -1; + } + node->v_ops->create(node, &node, pathname+last_slash_idx+1); + *target = kmalloc(sizeof(struct file)); + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + else // 2. Create a new file handle for this vnode if found. + { + *target = kmalloc(sizeof(struct file)); + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + + // lookup error code shows if file exist or not or other error occurs + // 4. Return error code if fails + return -1; +} + +int vfs_close(struct file *file) +{ + // 1. release the file handle + // 2. Return error code if fails + file->f_ops->close(file); + return 0; +} + +int vfs_write(struct file *file, const void *buf, size_t len) +{ + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file,buf,len); +} + +int vfs_read(struct file *file, void *buf, size_t len) +{ + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 2. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +int vfs_mkdir(const char *pathname) +{ + char dirname[MAX_PATH_NAME] = {}; + char newdirname[MAX_PATH_NAME] = {}; + + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) + { + if (pathname[i] == '/') + { + last_slash_idx = i; + } + } + + memcpy(dirname, pathname, last_slash_idx); + strcpy(newdirname, pathname + last_slash_idx + 1); + + struct vnode *node; + if(vfs_lookup(dirname,&node)==0) + { + node->v_ops->mkdir(node,&node,newdirname); + return 0; + } + + uart_printf("vfs_mkdir cannot find pathname"); + return -1; +} + +int vfs_mount(const char *target, const char *filesystem) +{ + struct vnode *dirnode; + struct filesystem *fs = find_filesystem(filesystem); + if(!fs) + { + uart_printf("vfs_mount cannot find filesystem\r\n"); + return -1; + } + + if(vfs_lookup(target, &dirnode)==-1) + { + uart_printf("vfs_mount cannot find dir\r\n"); + return -1; + }else + { + dirnode->mount = kmalloc(sizeof(struct mount)); + fs->setup_mount(fs, dirnode->mount); + } + return 0; +} + +int vfs_lookup(const char *pathname, struct vnode **target) +{ + if(strlen(pathname)==0) + { + *target = rootfs->root; + return 0; + } + + struct vnode *dirnode = rootfs->root; + char component_name[FILE_NAME_MAX+1] = {}; + int c_idx = 0; + for (int i = 1; i < strlen(pathname); i++) + { + if (pathname[i] == '/') + { + component_name[c_idx++] = 0; + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0)return -1; + // redirect to new mounted filesystem + + while (dirnode->mount) + { + dirnode = dirnode->mount->root; + } + c_idx = 0; + } + else + { + component_name[c_idx++] = pathname[i]; + } + } + + component_name[c_idx++] = 0; + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0)return -1; + // redirect to new mounted filesystem + while (dirnode->mount) + { + dirnode = dirnode->mount->root; + } + + *target = dirnode; + + return 0; +} + +int vfs_mknod(char* pathname, int id) +{ + struct file* f = kmalloc(sizeof(struct file)); + //create file + vfs_open(pathname, O_CREAT, &f); + f->vnode->f_ops = ®_dev[id]; + vfs_close(f); + return 0; +} + +long vfs_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +void init_rootfs() +{ + int idx = register_tmpfs(); + rootfs = kmalloc(sizeof(struct mount)); + reg_fs[idx].setup_mount(®_fs[idx], rootfs); + + vfs_mkdir("/initramfs"); + register_initramfs(); + vfs_mount("/initramfs","initramfs"); + + // for dev + vfs_mkdir("/dev"); + int uart_id = init_dev_uart(); + vfs_mknod("/dev/uart", uart_id); + int framebuffer_id = init_dev_framebuffer(); + vfs_mknod("/dev/framebuffer", framebuffer_id); + + //vfs_test(); +} + +/* +void vfs_test() +{ + // test read/write + vfs_mkdir("/lll"); + vfs_mkdir("/lll/ddd"); + // test mount + vfs_mount("/lll/ddd", "tmpfs"); + + struct file* testfilew; + struct file *testfiler; + char testbufw[0x30] = "ABCDEABBBBBBDDDDDDDDDDD"; + char testbufr[0x30] = {}; + vfs_open("/lll/ddd/ggg", O_CREAT, &testfilew); + vfs_open("/lll/ddd/ggg", O_CREAT, &testfiler); + vfs_write(testfilew, testbufw, 10); + vfs_read(testfiler, testbufr, 10); + uart_printf("%s",testbufr); + + struct file *testfile_initramfs; + vfs_open("/initramfs/get_simpleexec.sh", O_CREAT, &testfile_initramfs); + vfs_read(testfile_initramfs, testbufr, 30); + uart_printf("%s", testbufr); +}*/ + +char *path_to_absolute(char *path, char *curr_working_dir) +{ + //relative path + if(path[0] != '/') + { + char tmp[MAX_PATH_NAME]; + strcpy(tmp, curr_working_dir); + if(strcmp(curr_working_dir,"/")!=0)strcat(tmp, "/"); + strcat(tmp, path); + strcpy(path, tmp); + } + + char absolute_path[MAX_PATH_NAME+1] = {}; + int idx = 0; + for (int i = 0; i < strlen(path); i++) + { + // meet /.. + if (path[i] == '/' && path[i+1] == '.' && path[i+2] == '.') + { + for (int j = idx; j >= 0;j--) + { + if(absolute_path[j] == '/') + { + absolute_path[j] = 0; + idx = j; + } + } + i += 2; + continue; + } + + // ignore /. + if (path[i] == '/' && path[i+1] == '.') + { + i++; + continue; + } + + + absolute_path[idx++] = path[i]; + } + + absolute_path[idx] = 0; + + return strcpy(path, absolute_path); +} + +int op_deny() +{ + return -1; +} \ No newline at end of file diff --git a/lab7/src/irqtask.c b/lab7/src/irqtask.c new file mode 100644 index 000000000..60140eccc --- /dev/null +++ b/lab7/src/irqtask.c @@ -0,0 +1,86 @@ +#include "irqtask.h" +#include "exception.h" +#include "malloc.h" +#include "uart.h" + +/* +Preemption +Now, any interrupt handler can preempt the task’s execution, but the newly enqueued task still needs to wait for the currently running task’s completion. It’d be better if the newly enqueued task with a higher priority can preempt the currently running task. + +To achieve the preemption, the kernel can check the last executing task’s priority before returning to the previous interrupt handler. If there are higher priority tasks, execute the highest priority task. +*/ +int curr_task_priority = 9999; // init a very low priority + +struct list_head *task_list; + +void task_list_init() +{ + task_list = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(task_list); +} + +//like add_timer +void add_task(void *task_function,unsigned long long priority){ + + irq_task_t *the_task = kmalloc(sizeof(irq_task_t)); //need to kfree by task runner + + the_task->priority = priority; // store interrupt time into timer_event + the_task->task_function = task_function; + + // add the timer_event into timer_event_list (sorted) + // if the same priority FIFO + struct list_head *curr; + + lock(); + list_for_each(curr, task_list) + { + if (((irq_task_t *)curr)->priority > the_task->priority) + { + list_add(&the_task->listhead, curr->prev); // add this timer at the place just before the bigger one (sorted) + break; + } + } + + if (list_is_head(curr, task_list)) + { + list_add_tail(&(the_task->listhead), task_list); // for the time is the biggest + } + + unlock(); +} + +void run_preemptive_tasks(){ + + while (1) + { + lock(); + if (list_empty(task_list)) + { + unlock(); + break; + } + + irq_task_t *the_task = (irq_task_t *)task_list->next; + // just run task when priority is lower than the task preempted + if (curr_task_priority <= the_task->priority) + { + unlock(); + break; + } + list_del_entry((struct list_head *)the_task); + int prev_task_priority = curr_task_priority; + curr_task_priority = the_task->priority; + + unlock(); + run_task(the_task); + + curr_task_priority = prev_task_priority; + kfree(the_task); + + } +} + +void run_task(irq_task_t *the_task) +{ + ((void (*)())the_task->task_function)(); +} \ No newline at end of file diff --git a/lab7/src/main.c b/lab7/src/main.c new file mode 100644 index 000000000..dc249532f --- /dev/null +++ b/lab7/src/main.c @@ -0,0 +1,41 @@ +#include "uart.h" +#include "mbox.h" +#include "shell.h" +#include "string.h" +#include "malloc.h" +#include "dtb.h" +#include "cpio.h" +#include "exception.h" +#include "irqtask.h" +#include "mmu.h" +#include "fs/vfs.h" + +void init_cpio_default_place(); + +extern char* dtb_place; + +void main(char* dtb) +{ + //stroe dtb pointer to global (device tree) + dtb_place = PHYS_TO_VIRT(dtb); + + init_cpio_default_place(); //store cpio pointer to global (file system) + init_allocator(); + //alloctest(); + init_rootfs(); + + //cannot use original input series after interrupt start (input is going to the buffer), use async input instead. + //output series are not affected. (order is affected) + task_list_init(); + enable_mini_uart_interrupt(); + enable_interrupt(); // enable interrupt in EL1 -> EL1 + + uart_printf("dtb : 0x%x\r\n",dtb); + + shell(); +} + +void init_cpio_default_place() +{ + traverse_device_tree(dtb_place,dtb_callback_initramfs); +} diff --git a/lab7/src/malloc.c b/lab7/src/malloc.c new file mode 100644 index 000000000..1222d3a62 --- /dev/null +++ b/lab7/src/malloc.c @@ -0,0 +1,410 @@ +#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_place; + +// simple_malloc +void *simple_malloc(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 ~ 7) +>=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 = simple_malloc(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 = MAXORDER; + } + } + + //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 + */ + reserve_memory_block_with_dtb(); // spin tables can be find + 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_DEFAULT_PLACE, (unsigned long long)CPIO_DEFAULT_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 kmalloc!!!!\r\n"); + while (1); + return simple_malloc(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("kmalloc ERROR (all lists are empty?)!!!!\r\n"); + while (1); + return simple_malloc(size); + } + + //get the frame + frame_t *target_frame_ptr = (frame_t *)freelist[target_list_val].next; + list_del_entry((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_entry((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 +} + +void *kmalloc(unsigned int size) +{ + lock(); +#ifdef DEBUG + uart_printf("kmalloc 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("kmalloc ret 0x%x\r\n", r); +#endif + //For cache + unlock(); + return r; +} + +void kfree(void *ptr) +{ + lock(); +#ifdef DEBUG + uart_printf("kfree 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_entry(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_entry(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 = kmalloc(0x10); + char *b = kmalloc(0x100); + char *c = kmalloc(0x1000); + + kfree(a); + kfree(b); + kfree(c); + + a = kmalloc(32); + char *aa = kmalloc(50); + b = kmalloc(64); + char *bb = kmalloc(64); + c = kmalloc(128); + char *cc = kmalloc(129); + char *d = kmalloc(256); + char *dd = kmalloc(256); + char *e = kmalloc(512); + char *ee = kmalloc(999); + + kfree(a); + kfree(aa); + kfree(b); + kfree(bb); + kfree(c); + kfree(cc); + kfree(dd); + kfree(d); + kfree(e); + kfree(ee); +} \ No newline at end of file diff --git a/lab7/src/mbox.c b/lab7/src/mbox.c new file mode 100644 index 000000000..262b718f1 --- /dev/null +++ b/lab7/src/mbox.c @@ -0,0 +1,26 @@ +#include "mbox.h" +#include "uart.h" +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * 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; +} \ No newline at end of file diff --git a/lab7/src/mmu.c b/lab7/src/mmu.c new file mode 100644 index 000000000..9078840f6 --- /dev/null +++ b/lab7/src/mmu.c @@ -0,0 +1,165 @@ +#include "mmu.h" +#include "malloc.h" +#include "string.h" +#include "threads.h" +#include "exception.h" +// PGD's page frame at 0x1000 +// PUD's page frame at 0x2000 +// two-level translation (1G) -> three-level translation (2MB) +void* set_2M_kernel_mmu(void* x0) +{ + //unsigned long *pud_table = kmalloc(0x1000); + unsigned long *pud_table = (unsigned long *)0x3000; + for (int i = 0; i < 512; i++) + { + // 0x3F000000 to 0x3FFFFFFF for peripherals + if ((0x00000000 + (0x200000L) * i) >= 0x3F000000L) + { + pud_table[i] = PD_ACCESS | PD_BLOCK | (0x00000000 + (0x200000L) * i) | (MAIR_DEVICE_nGnRnE << 2) | PD_UK_ACCESS | PD_UNX | PD_KNX; + } + else + { + pud_table[i] = PD_ACCESS | PD_BLOCK | (0x00000000 | (0x200000L) * i) | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + } + + //unsigned long *pud_table2 = kmalloc(0x1000); + unsigned long *pud_table2 = (unsigned long *)0x4000; + for (int i = 0; i < 512; i++) + { + pud_table2[i] = PD_ACCESS | PD_BLOCK | (0x40000000L | (0x200000L) * i) | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + // set PUD + *(unsigned long *)(kernel_pud_addr) = PD_ACCESS | PD_TABLE | (unsigned long)pud_table; + *(unsigned long *)(kernel_pud_addr + 0x8) = PD_ACCESS | PD_TABLE | (unsigned long)pud_table2; + + 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; + for (int level = 0; level < 4; level++) + { + unsigned int idx = (va >> (39 - level * 9)) & 0x1ff; + + if (level == 3) + { + 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 =kmalloc(0x1000); + memset(newtable_p, 0, 0x1000); + table_p[idx] = VIRT_TO_PHYS((size_t)newtable_p); + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + table_p = (size_t*)PHYS_TO_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); + } +} + +//give +void mappages(size_t *virt_pgd_p, size_t va, size_t size, size_t pa, size_t flag) +{ + pa = pa - (pa % 0x1000); // align + for (size_t s = 0; s < size; s+=0x1000) + { + map_one_page(virt_pgd_p, va + s, pa + s, flag); + } +} + +void add_vma(thread_t *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced) +{ + // alignment + size = size % 0x1000 ? size + (0x1000 - size%0x1000): size; + + vm_area_struct_t *new_area = kmalloc(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; + 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); + 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) + { + if(level!=2)free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + kfree(PHYS_TO_VIRT((char *)next_table)); + } + } + } +} + +void handle_abort(esr_el1_t* esr_el1) +{ + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t": "=r"(far_el1)); + + 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 <= far_el1 && ((vm_area_struct_t *)pos)->virt_addr + ((vm_area_struct_t *)pos)->area_size >= far_el1) + { + the_area_ptr = (vm_area_struct_t *)pos; + break; + } + } + + if (!the_area_ptr) + { + seg_fault(); + } + + // For translation fault + 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) + { + //uart_printf("[Translation fault]: 0x%x\r\n",far_el1); + + size_t addr_offset = (far_el1 - the_area_ptr->virt_addr); + 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); + }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; + + //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/lab7/src/sched.S b/lab7/src/sched.S new file mode 100644 index 000000000..de22ee0ef --- /dev/null +++ b/lab7/src/sched.S @@ -0,0 +1,60 @@ +.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] + //mrs x9, ttbr0_el1 + //str x9, [x0, 16 * 6 + 8] + + 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] + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + + msr tpidr_el1, x1 + + 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 + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/lab7/src/sched.c b/lab7/src/sched.c new file mode 100644 index 000000000..06c2ebfe4 --- /dev/null +++ b/lab7/src/sched.c @@ -0,0 +1,187 @@ +#include "sched.h" +#include "exception.h" +#include "malloc.h" +#include "timer.h" +#include "uart.h" +#include "signal.h" +#include "mmu.h" +#include "string.h" + +void init_thread_sched() +{ + lock(); + run_queue = kmalloc(sizeof(list_head_t)); + wait_queue = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + INIT_LIST_HEAD(wait_queue); + + //init pids + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].isused = 0; + threads[i].pid = i; + threads[i].iszombie = 0; + } + + asm volatile("msr tpidr_el1, %0" ::"r"(kmalloc(sizeof(thread_t)))); /// malloc a space for current kernel thread to prevent crash + + thread_t* idlethread = thread_create(idle,0x1000); + curr_thread = idlethread; + unlock(); +} + +void idle(){ + while(1) + { + kill_zombies(); //reclaim threads marked as DEAD + schedule(); //switch to next thread in run queue + } +} + +void schedule(){ + lock(); + do{ + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue) || curr_thread->iszombie); + + unlock(); + switch_to(get_current(), &curr_thread->context); +} + +void kill_zombies(){ + lock(); + list_head_t *curr; + list_for_each(curr,run_queue) + { + if (((thread_t *)curr)->iszombie) + { + list_del_entry(curr); + kfree(((thread_t *)curr)->kernel_stack_alloced_ptr); // free kstack + free_page_tables(((thread_t *)curr)->context.ttbr0_el1,0); + + // free alloced area and vma struct + list_head_t *pos = ((thread_t *)curr)->vma_list.next; + while (pos != &((thread_t *)curr)->vma_list) + { + if (((vm_area_struct_t *)pos)->is_alloced) + kfree((void*)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr)); + + list_head_t* next_pos = pos->next; + kfree(pos); + pos = next_pos; + } + + for(int i = 0; i < MAX_FD;i++) + { + if (((thread_t *)curr)->file_descriptors_table[i]) + vfs_close(((thread_t *)curr)->file_descriptors_table[i]); + } + + kfree(PHYS_TO_VIRT(((thread_t *)curr)->context.ttbr0_el1)); // free PGD + ((thread_t *)curr)->iszombie = 0; + ((thread_t *)curr)->isused = 0; + } + } + 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->stack_alloced_ptr), 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; + + //copy file into data + for (int i = 0; i < filesize;i++) + { + t->data[i] = data[i]; + } + + //disable echo when going to userspace + uart_disable_echo(); + curr_thread = t; + vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[0]); + vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[1]); + vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[2]); + add_timer(schedule_timer, 1, "", 0); + // eret to exception level 0 + asm("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" + "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_stack_alloced_ptr + KSTACK_SIZE), "r"(t->context.ttbr0_el1)); + + return 0; +} + + +//malloc a kstack and a userstack +thread_t *thread_create(void *start, unsigned int filesize) +{ + lock(); + + thread_t *r; + for (int i = 0; i <= PIDMAX; i++) + { + if (!threads[i].isused) + { + r = &threads[i]; + break; + } + } + INIT_LIST_HEAD(&r->vma_list); + r->iszombie = 0; + r->isused = 1; + r->context.lr = (unsigned long long)start; + r->stack_alloced_ptr = kmalloc(USTACK_SIZE); + r->kernel_stack_alloced_ptr = kmalloc(KSTACK_SIZE); + r->signal_is_checking = 0; + r->data = kmalloc(filesize); + r->datasize = filesize; + r->context.sp = (unsigned long long)r->kernel_stack_alloced_ptr + KSTACK_SIZE; + r->context.fp = r->context.sp; + strcpy(r->curr_working_dir, "/"); + + r->context.ttbr0_el1 = kmalloc(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->singal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add(&r->listhead, run_queue); + unlock(); + return r; +} + +void thread_exit(){ + lock(); + curr_thread->iszombie = 1; + unlock(); + schedule(); +} + +void schedule_timer(char* notuse){ + unsigned long long cntfrq_el0; + __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/lab7/src/shell.c b/lab7/src/shell.c new file mode 100644 index 000000000..2a2bb3c7c --- /dev/null +++ b/lab7/src/shell.c @@ -0,0 +1,113 @@ +#include "uart.h" +#include "string.h" +#include "shell.h" +#include "mbox.h" +#include "system.h" +#include "filesystem.h" +#include "dtb.h" +#include "timer.h" +#include "malloc.h" +#include "sched.h" + +void shell() +{ + char cmd[MAX_BUF_SIZE]; + print_system_messages(); + init_thread_sched(); + timer_list_init(); + core_timer_enable(); + while (1) + { + uart_async_printf("# "); + uart_async_gets(cmd); + do_cmd(cmd); + } +} + +void do_cmd(char *cmd) +{ + if (strcmp(cmd, "help") == 0) + { + uart_async_printf("help : print this help menu\r\n"); + uart_async_printf("hello : print Hello World!\r\n"); + uart_async_printf("reboot : reboot the device\r\n"); + uart_async_printf("ls : list current directory\r\n"); + uart_async_printf("cat : print content of a file\r\n"); + uart_async_printf("show_device_tree : show device tree\r\n"); + uart_async_printf("exec : load a user program in the initramfs and run it in EL0\r\n"); + uart_async_printf("setTimeout [MESSAGE] [SECONDS] : print message after [SECONDS] seconds (non-blocking)\r\n"); + uart_async_printf("clockAlert : alert every two seconds\r\n"); + } + else if (strcmp(cmd, "hello") == 0) + { + uart_async_printf("Hello World!\r\n"); + } + else if (strcmp(cmd, "reboot") == 0) + { + reboot(); + } + else if (strcmp(cmd, "cat") == 0) + { + uart_async_printf("Filename: "); + char filepath[MAX_BUF_SIZE]; + uart_async_gets(filepath); + cat(filepath); + } + else if (strcmp(cmd, "ls") == 0) + { + ls("."); + } + else if (strcmp(cmd, "show_device_tree") == 0) + { + traverse_device_tree(dtb_place, dtb_callback_show_tree); + } + else if (strcmp(cmd, "exec") == 0) // in filesystem.c + { + uart_async_printf("Filename: "); + char filepath[MAX_BUF_SIZE]; + uart_async_gets(filepath); + execfile(filepath); + } + else if (strncmp(cmd, "setTimeout", sizeof("setTimeout") - 1) == 0) + { + strchr(cmd, ' '); + char *message = strchr(cmd, ' '); + if (!message) + { + uart_async_printf("setTimeout wrong format"); + return; + } + message += 1; + char *end_message = strchr(message, ' '); + if (!end_message) + { + uart_async_printf("setTimeout wrong format"); + return; + } + *end_message = '\0'; + char *seconds = end_message + 1; + add_timer(uart_puts, atoi(seconds), message,0); + } + else if (strcmp(cmd, "clockAlert") == 0) + { + add_timer(two_second_alert, 2, "two_second_alert",0); + } + else + { + uart_async_printf("Unknown command!: %s\r\n", cmd); + } +} + +void print_system_messages() +{ + unsigned int board_revision; + get_board_revision(&board_revision); + uart_async_printf("Board revision is : 0x%x\r\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\r\n", arm_mem_base_addr); + uart_async_printf("ARM memory size in bytes : 0x%x\r\n", arm_mem_size); +} \ No newline at end of file diff --git a/lab7/src/signal.c b/lab7/src/signal.c new file mode 100644 index 000000000..9475bf27c --- /dev/null +++ b/lab7/src/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->singal_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/lab7/src/sprintf.c b/lab7/src/sprintf.c new file mode 100644 index 000000000..63533303f --- /dev/null +++ b/lab7/src/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/lab7/src/start.S b/lab7/src/start.S new file mode 100644 index 000000000..8399d3324 --- /dev/null +++ b/lab7/src/start.S @@ -0,0 +1,92 @@ +#include "registers.h" +#include "mmu.h" + +.section ".text.boot" + +.global _start + +_start: + // read cpu id, stop slave cores + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + // cpu id > 0, stop +1: wfe + b 1b +2: // cpu id == 0 + + // Switch from EL2 to EL1 . + bl from_el2_to_el1 + + // set paging configuration (up : 0xffff000000000000 low : 0x0000000000000000) + 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 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x4] + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x4 // load PGD to the bottom translation-based register. + msr ttbr1_el1, x4 // also load PGD to the upper translation based register. + + mov sp, 0x3c000000 + bl set_2M_kernel_mmu + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + + // indirect branch to the upper virtual address + ldr x2, =set_exception_vector_table + br x2 + +set_exception_vector_table: + adr x1, exception_vector_table + msr vbar_el1, x1 + + // set top of stack at 0xffff00003c000000 (last usable memory) + movz x3, 0x0000 + movk x3, 0x3c00, lsl 16 + movk x3, 0xffff, lsl 48 + mov sp, x3 + + // clear bss + ldr x1, =__bss_start + ldr w2, =__bss_size +3: cbz w2, 4f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b + + // jump to C code, should not return +4: bl main + // for failsafe, halt this core too + b 1b + +from_el2_to_el1: + ldr x1, =CPACR_EL1_VALUE // Make el0, el1 can use Floating point and Advanced SIMD + msr CPACR_EL1, x1 + mov x1, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x1 + mov x1, 0x3c5 // EL1h (SPSel = 1) with interrupt disabled + msr spsr_el2, x1 + msr elr_el2, lr + eret // return to EL1 + + diff --git a/lab7/src/string.c b/lab7/src/string.c new file mode 100644 index 000000000..97b28b435 --- /dev/null +++ b/lab7/src/string.c @@ -0,0 +1,131 @@ +#include "string.h" +#include +#include "uart.h" + +int strcmp (const char *p1, const char *p2) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + return c1 - c2; +} + +int strncmp (const char *s1, const char *s2, unsigned long long n) +{ + unsigned char c1 = '\0'; + unsigned char c2 = '\0'; + if (n >= 4) + { + size_t n4 = n >> 2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + } while (--n4 > 0); + n &= 3; + } + while (n > 0) + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + n--; + } + return c1 - c2; +} + +char* strcat (char *dest, const char *src) +{ + strcpy (dest + strlen (dest), src); + return dest; +} + +char* strcpy (char *dest, const char *src) +{ + return memcpy (dest, src, strlen (src) + 1); +} + +unsigned long long strlen(const char *str) +{ + size_t count = 0; + while((unsigned char)*str++)count++; + return count; +} + +char* memcpy (void *dest, const void *src, unsigned long long len) +{ + char *d = dest; + const char *s = src; + while (len--) + { + *d++ = *s++; + } + return dest; +} + +char* strchr (register const char *s, int c) +{ + do { + if (*s == c) + { + return (char*)s; + } + } while (*s++); + return (0); +} + +// A simple atoi() function +int atoi(char* str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + { + if(str[i] > '9' || str[i] < '0')return res; + res = res * 10 + str[i] - '0'; + } + + // return result. + return res; +} + +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/lab7/src/syscall.c b/lab7/src/syscall.c new file mode 100644 index 000000000..2f08818df --- /dev/null +++ b/lab7/src/syscall.c @@ -0,0 +1,391 @@ +#include "syscall.h" +#include "sched.h" +#include "stddef.h" +#include "uart.h" +#include "filesystem.h" +#include "exception.h" +#include "malloc.h" +#include "mbox.h" +#include "signal.h" +#include "mmu.h" +#include "string.h" +#include "fs/vfs.h" +#include "fs/dev_framebuffer.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_async_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_async_putc(buf[i]); // TODO : debug -> some unknown bugs occur when uart_async_putc (only in qemu) + } + 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 + // free alloced area and vma struct + list_head_t *pos = curr_thread->vma_list.next; + while (pos != &curr_thread->vma_list) + { + if (((vm_area_struct_t *)pos)->is_alloced) + kfree((void *)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr)); + + list_head_t *next_pos = pos->next; + kfree(pos); + pos = next_pos; + } + + INIT_LIST_HEAD(&curr_thread->vma_list); + + // use virtual file system + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, name); + path_to_absolute(abs_path, curr_thread->curr_working_dir); + + struct vnode *target_file; + vfs_lookup(abs_path,&target_file); + curr_thread->datasize = target_file->f_ops->getsize(target_file); + curr_thread->data = kmalloc(curr_thread->datasize); + curr_thread->stack_alloced_ptr = kmalloc(USTACK_SIZE); + + asm("dsb ish\n\t"); // ensure write has completed + free_page_tables(curr_thread->context.ttbr0_el1, 0); + memset(PHYS_TO_VIRT(curr_thread->context.ttbr0_el1), 0, 0x1000); + asm("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->stack_alloced_ptr), 7, 1); + // device + 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 + struct file *f; + vfs_open(abs_path, 0, &f); + vfs_read(f, curr_thread->data, curr_thread->datasize); + vfs_close(f); + + //clear signal handler + for (int i = 0; i <= SIGNAL_MAX; i++) + { + curr_thread->singal_handler[i] = signal_default_handler; + } + + tpf->elr_el1 = 0; + tpf->sp_el0 = 0xfffffffff000; + tpf->x0 = 0; + return 0; +} + +int fork(trapframe_t *tpf) +{ + lock(); + thread_t *newt = thread_create(curr_thread->data,curr_thread->datasize); + + //copy signal handler + for (int i = 0; i <= SIGNAL_MAX;i++) + { + newt->singal_handler[i] = curr_thread->singal_handler[i]; + } + + //copy signal handler + for (int i = 0; i <= MAX_FD; i++) + { + if (curr_thread->file_descriptors_table[i]) + { + newt->file_descriptors_table[i] = kmalloc(sizeof(struct file)); + *newt->file_descriptors_table[i] = *curr_thread->file_descriptors_table[i]; + } + } + + list_head_t *pos; + list_for_each(pos, &curr_thread->vma_list){ + + // ignore device and signal wrapper + if (((vm_area_struct_t *)pos)->virt_addr == USER_SIG_WRAPPER_VIRT_ADDR_ALIGNED || ((vm_area_struct_t *)pos)->virt_addr == 0x3C000000L) + { + continue; + } + + char *new_alloc = kmalloc(((vm_area_struct_t *)pos)->area_size); + add_vma(newt, ((vm_area_struct_t *)pos)->virt_addr, ((vm_area_struct_t *)pos)->area_size, (size_t)VIRT_TO_PHYS(new_alloc), ((vm_area_struct_t *)pos)->rwx, 1); + + memcpy(new_alloc, (void*)PHYS_TO_VIRT(((vm_area_struct_t *)pos)->phys_addr), ((vm_area_struct_t *)pos)->area_size); + } + + // device + add_vma(newt, 0x3C000000L, 0x3000000L, 0x3C000000L, 3, 0); + // for signal wrapper + add_vma(newt, 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 + for (int i = 0; i < KSTACK_SIZE; i++) + { + newt->kernel_stack_alloced_ptr[i] = curr_thread->kernel_stack_alloced_ptr[i]; + } + + store_context(get_current()); + //for child + if( parent_pid != curr_thread->pid) + { + goto child; + } + + void *temp_ttbr0_el1 = newt->context.ttbr0_el1; + newt->context = curr_thread->context; + newt->context.ttbr0_el1 = VIRT_TO_PHYS(temp_ttbr0_el1); + newt->context.fp += newt->kernel_stack_alloced_ptr - curr_thread->kernel_stack_alloced_ptr; // move fp + newt->context.sp += newt->kernel_stack_alloced_ptr - curr_thread->kernel_stack_alloced_ptr; // move kernel sp + + unlock(); + + tpf->x0 = newt->pid; + return newt->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) +{ + //mbox_user = (unsigned int *)(curr_thread->stack_alloced_ptr + USTACK_SIZE - (0xfffffffff000 - (size_t)mbox)); + lock(); + + 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].isused) + { + unlock(); + return; + } + threads[pid].iszombie = 1; + unlock(); + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0)return; + + curr_thread->singal_handler[signal] = handler; +} + +void signal_kill(int pid, int signal) +{ + if (pid > PIDMAX || pid < 0 || !threads[pid].isused)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; + + len = len % 0x1000 ? len + (0x1000 - len % 0x1000) : len; // rounds up + addr = (unsigned long)addr%0x1000?addr + (0x1000 - (unsigned long)addr % 0x1000):addr; + + // check if overlap + 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; + } + } + + // test the end of the area as addr + 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; + } + + add_vma(curr_thread, (unsigned long)addr, len, VIRT_TO_PHYS((unsigned long)kmalloc(len)), prot, 1); + tpf->x0 = (unsigned long)addr; + return (void*)tpf->x0; +} + +// syscall number : 11 +int sys_open(trapframe_t *tpf, const char *pathname, int flags) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + path_to_absolute(abs_path, curr_thread->curr_working_dir); + for (int i = 0; i < MAX_FD; i++) + { + if(!curr_thread->file_descriptors_table[i]) + { + if(vfs_open(abs_path, flags, &curr_thread->file_descriptors_table[i])!=0) + { + break; + } + + tpf->x0 = i; + return i; + } + } + + tpf->x0 = -1; + return -1; +} + +// syscall number : 12 +int sys_close(trapframe_t *tpf, int fd) +{ + if(curr_thread->file_descriptors_table[fd]) + { + vfs_close(curr_thread->file_descriptors_table[fd]); + curr_thread->file_descriptors_table[fd] = 0; + tpf->x0 = 0; + return 0; + } + + tpf->x0 = -1; + return -1; +} + +// syscall number : 13 +// remember to return read size or error code +long sys_write(trapframe_t *tpf, int fd, const void *buf, unsigned long count) +{ + if (curr_thread->file_descriptors_table[fd]) + { + tpf->x0 = vfs_write(curr_thread->file_descriptors_table[fd], buf, count); + return tpf->x0; + } + + tpf->x0 = -1; + return tpf->x0; +} + +// syscall number : 14 +// remember to return read size or error code +long sys_read(trapframe_t *tpf, int fd, void *buf, unsigned long count) +{ + if (curr_thread->file_descriptors_table[fd]) + { + tpf->x0 = vfs_read(curr_thread->file_descriptors_table[fd], buf, count); + return tpf->x0; + } + + tpf->x0 = -1; + return tpf->x0; +} + +// syscall number : 15 +// you can ignore mode, since there is no access control +int sys_mkdir(trapframe_t *tpf, const char *pathname, unsigned mode) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + path_to_absolute(abs_path, curr_thread->curr_working_dir); + tpf->x0 = vfs_mkdir(abs_path); + return tpf->x0; +} + +// syscall number : 16 +// you can ignore arguments other than target (where to mount) and filesystem (fs name) +int sys_mount(trapframe_t *tpf, const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, target); + path_to_absolute(abs_path, curr_thread->curr_working_dir); + + tpf->x0 = vfs_mount(abs_path,filesystem); + return tpf->x0; +} + +// syscall number : 17 +int sys_chdir(trapframe_t *tpf, const char *path) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, path); + path_to_absolute(abs_path, curr_thread->curr_working_dir); + strcpy(curr_thread->curr_working_dir, abs_path); + + return 0; +} + +// syscall number : 18 +// you only need to implement seek set +long sys_lseek64(trapframe_t *tpf,int fd, long offset, int whence) +{ + tpf->x0 = vfs_lseek64(curr_thread->file_descriptors_table[fd],offset,whence); + return tpf->x0; +} + +extern unsigned int height; +extern unsigned int isrgb; +extern unsigned int pitch; +extern unsigned int width; +int sys_ioctl(trapframe_t *tpf, int fb, unsigned long request, void *info) +{ + if(request == 0) + { + struct framebuffer_info *fb_info = info; + fb_info->height = height; + fb_info->isrgb = isrgb; + fb_info->pitch = pitch; + fb_info->width = width; + } + + tpf->x0 = 0; + return tpf->x0; +} diff --git a/lab7/src/system.c b/lab7/src/system.c new file mode 100644 index 000000000..c8cd8bc38 --- /dev/null +++ b/lab7/src/system.c @@ -0,0 +1,82 @@ +#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) + +/* For all return 0 -> success , -1 failure*/ + +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; + } +} + +void set(long addr, unsigned int value) +{ + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reboot() +{ + //disable_uart(); + reset(1); // timeout = 1/16th of a second? (whatever) +} + +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 + while(1); +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/lab7/src/timer.c b/lab7/src/timer.c new file mode 100644 index 000000000..34b71ba24 --- /dev/null +++ b/lab7/src/timer.c @@ -0,0 +1,177 @@ +#include "timer.h" +#include "registers.h" +#include "uart.h" +#include "malloc.h" +#include "string.h" + +void timer_list_init() +{ + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1": "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0":: "r"(tmp)); + + timer_event_list = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(timer_event_list); +} + +void core_timer_enable() +{ + __asm__ __volatile__( + "mov x1, 1\n\t" + "msr cntp_ctl_el0, x1\n\t" // enable + + //"mrs x1, cntfrq_el0\n\t" + //"mov x2, 0x100000\n\t" + //"mul x1, x1, x2\n\t" //set a big value prevent interrupt immediately + //"msr cntp_tval_el0, x1\n\t" // set expired time + + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // unmask timer interrupt + :::"x1","x2"); +} + +void core_timer_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"); +} + +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_entry((struct list_head *)timer_event); // delete the event + kfree(timer_event->args); // kfree the arg space + kfree(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)->interrupt_time); + } + else + { + set_core_timer_interrupt(10000); // disable timer interrupt (set a very big value) + } +} + +void two_second_alert(char *str) +{ + unsigned long long cntpct_el0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" + : "=r"(cntpct_el0)); //tick now + + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" + : "=r"(cntfrq_el0)); //tick frequency + uart_printf("alert '%s': \r\nexception irq_router -> seconds after booting : %d\r\n", str, cntpct_el0 / cntfrq_el0); + + add_timer(two_second_alert, 2, "two_second_alert",0); +} + +void core_timer_handler() +{ + lock(); + //disable_interrupt(); + if (list_empty(timer_event_list)) + { + set_core_timer_interrupt(10000); // disable timer interrupt (set a very big value) + unlock(); + return; + } + + timer_event_callback((timer_event_t *)timer_event_list->next); // do callback and set new interrupt + unlock(); +} + +// give a string argument to callback timeout after seconds +void add_timer(void *callback, unsigned long long timeout, char *args, int bytick) +{ + timer_event_t *the_timer_event = kmalloc(sizeof(timer_event_t)); //need to kfree by event handler + + // store argument string into timer_event + the_timer_event->args = kmalloc(strlen(args) + 1); + strcpy(the_timer_event->args, args); + + if(bytick == 0) + { + the_timer_event->interrupt_time = get_tick_plus_s(timeout); // store interrupt time into timer_event + }else + { + the_timer_event->interrupt_time = get_tick_plus_s(0) + timeout; + } + + the_timer_event->callback = callback; + + // add the timer_event into timer_event_list (sorted) + struct list_head *curr; + + lock(); + list_for_each(curr, timer_event_list) + { + if (((timer_event_t *)curr)->interrupt_time > the_timer_event->interrupt_time) + { + list_add(&the_timer_event->listhead, curr->prev); // add this timer at the place just before the bigger one (sorted) + break; + } + } + + if (list_is_head(curr, timer_event_list)) + { + list_add_tail(&the_timer_event->listhead, timer_event_list); // for the time is the biggest + } + + + // set interrupt to first event + set_core_timer_interrupt_by_tick(((timer_event_t *)timer_event_list->next)->interrupt_time); + unlock(); +} + + +// get cpu tick add some second +unsigned long long get_tick_plus_s(unsigned long long second) +{ + + unsigned long long cntpct_el0 = 0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" + : "=r"(cntpct_el0)); //tick now + + unsigned long long cntfrq_el0 = 0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" + : "=r"(cntfrq_el0)); //tick frequency + + return (cntpct_el0 + cntfrq_el0 * second); +} + +// set timer interrupt time to [expired_time] seconds after now (relatively) +void set_core_timer_interrupt(unsigned long long expired_time) +{ + __asm__ __volatile__( + "mrs x1, cntfrq_el0\n\t" //cntfrq_el0 -> relative time + "mul x1, x1, %0\n\t" + "msr cntp_tval_el0, x1\n\t" // set expired time + :: "r"(expired_time):"x1"); +} + +// directly set timer interrupt time to a cpu tick (directly) +void set_core_timer_interrupt_by_tick(unsigned long long tick) +{ + __asm__ __volatile__( + "msr cntp_cval_el0, %0\n\t" //cntp_cval_el0 -> absolute time + :: "r"(tick)); +} + +int timer_list_get_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/lab7/src/uart.c b/lab7/src/uart.c new file mode 100644 index 000000000..5c9d5024c --- /dev/null +++ b/lab7/src/uart.c @@ -0,0 +1,414 @@ +#include "gpio.h" +#include "uart.h" +#include "sprintf.h" +#include "registers.h" +#include "exception.h" + +// get address from linker +extern volatile unsigned char _end; + +int echoflag = 1; + +//implement first in first out buffer with a read index and a write index +static char uart_tx_buffer[MAX_BUF_SIZE] = {}; +unsigned int uart_tx_buffer_widx = 0; //write index +unsigned int uart_tx_buffer_ridx = 0; //read index +static char uart_rx_buffer[MAX_BUF_SIZE] = {}; +unsigned int uart_rx_buffer_widx = 0; +unsigned int uart_rx_buffer_ridx = 0; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + register unsigned int r; + + /* initialize UART */ + *AUX_ENABLE |= 1; // Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + *AUX_MU_CNTL = 0; // Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + *AUX_MU_IER = 0; // Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + *AUX_MU_LCR = 3; // Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + *AUX_MU_MCR = 0; // Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + *AUX_MU_IIR = 0x6; // disable interrupts + *AUX_MU_BAUD = 270; // 115200 baud + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 (disable pull up/down) + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx + + while ((*AUX_MU_LSR & 0x01)) + *AUX_MU_IO; //clean rx data +} + +// maybe don't do so many step +void disable_uart() +{ + register unsigned int r; + *AUX_ENABLE &= ~(unsigned int)1; + *AUX_MU_CNTL = 0; + r = *GPFSEL1; + r |= ((7 << 12) | (7 << 15)); // gpio14, gpio15 + r &= ~(2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 2; // enable pins 14 and 15 (pull down) + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; // flush GPIO setup +} + +/** + * Send a character (only replace uart_put with uart_async_putc when non-echo output) + */ +void uart_putc(char c) +{ + unsigned int intc = c; + /* wait until we can send */ + do + { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); + /* write the character to the buffer */ + *AUX_MU_IO = intc; +} + +/** + * Receive a character + */ +char uart_getc() +{ + char r; + /* wait until something is in the buffer */ + do + { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); + /* read it and return */ + r = (char)(*AUX_MU_IO); + + /* + echo back (disable it when going to userspace) + */ + if(echoflag) + { + if (r == '\r') + { + uart_printf("\r\r\n"); + do + { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x40)); //wait for output success Transmitter idle + } + else if (r == '\x7f') // backspace -> get del + { + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + } + else + { + uart_putc(r); + } + } + /* convert carrige return to newline */ + return r == '\r' ? '\n' : r; +} + +void uart_disable_echo() +{ + echoflag = 0; +} + +void uart_enable_echo() +{ + echoflag = 1; +} + +/** + * Display a string with newline + */ +int uart_puts(char *s) +{ + int i = 0; + + while (*s) + { + uart_putc(*s++); + i++; + } + uart_putc('\r'); + uart_putc('\n'); + + return i + 2; +} + +/** + * Display a string with newline + */ +int uart_async_puts(char *s) +{ + int i = 0; + + while (*s) + { + uart_async_putc(*s++); + i++; + } + uart_async_putc('\r'); + uart_async_putc('\n'); + + return i + 2; +} + +/** + * get a string (use async getc) + */ +char *uart_async_gets(char *buf) +{ + int count; + char c; + char *s; + for (s = buf, count = 0; (c = uart_async_getc()) != '\n' && count != MAX_BUF_SIZE - 100; count++) + { + *s = c; + if (*s == '\x7f') //delete -> backspace + { + count--; + if (count == -1) + { + uart_putc(' '); // prevent back over command line # + continue; + } + s--; + count--; + continue; + } + s++; + } + *s = '\0'; + return buf; +} + +/** + * get a string (use async getc) + */ +char *uart_gets(char *buf) +{ + int count; + char c; + char *s; + for (s = buf, count = 0; (c = uart_getc()) != '\n' && count != MAX_BUF_SIZE - 100; count++) + { + *s = c; + if (*s == '\x7f') + { + count--; + if (count == -1) + { + uart_putc(' '); // prevent back over command line # + continue; + } + s--; + count--; + continue; + } + s++; + } + *s = '\0'; + return buf; +} + +/** + * printf (from https://github.com/bztsrc/raspi3-tutorial/tree/master/12_printf) + initial printf from github dont use any va_end, and it is also can run. (in assembly there is nothing compiled from __builtin_va_end) + */ +int uart_printf(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[MAX_BUF_SIZE]; + // we don't have memory allocation yet, so we + // simply place our string after our code + char *s = (char *)buf; + // use sprintf to format our string + int count = vsprintf(s, fmt, args); + // print out as usual + while (*s) + { + uart_putc(*s++); + } + __builtin_va_end(args); + return count; +} + +int uart_async_printf(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[MAX_BUF_SIZE]; + // we don't have memory allocation yet, so we + // simply place our string after our code + char *s = (char *)buf; + // use sprintf to format our string + int count = vsprintf(s, fmt, args); + // print out as usual + while (*s) + { + uart_async_putc(*s++); + } + __builtin_va_end(args); + return count; +} + +void uart_interrupt_r_handler() +{ + //read buffer full + lock(); + if ((uart_rx_buffer_widx + 1) % MAX_BUF_SIZE == uart_rx_buffer_ridx) + { + *AUX_MU_IIR = 0xC2; /* clear the fifos */ // I dont know why need this but it can prevent big input to cause infinite run here (disable_r_interrupt never work) + disable_mini_uart_r_interrupt(); //disable read interrupt when read buffer full + unlock(); + return; + } + //lock(); + uart_rx_buffer[uart_rx_buffer_widx++] = uart_getc(); + if (uart_rx_buffer_widx >= MAX_BUF_SIZE) + uart_rx_buffer_widx = 0; + + enable_mini_uart_r_interrupt(); // lab 3 : advanced 2 -> unmask device line + unlock(); +} + +void uart_interrupt_w_handler() //can write +{ + // buffer empty + lock(); + if (uart_tx_buffer_ridx == uart_tx_buffer_widx) + { + *AUX_MU_IIR = 0xC4; + disable_mini_uart_w_interrupt(); // disable w_interrupt to prevent interruption without any async output + unlock(); + return; + } + + uart_putc(uart_tx_buffer[uart_tx_buffer_ridx++]); + if (uart_tx_buffer_ridx >= MAX_BUF_SIZE) + uart_tx_buffer_ridx = 0; // cycle pointer + + enable_mini_uart_w_interrupt(); // lab 3 : advanced 2 -> unmask device line + unlock(); +} + +void uart_async_putc(char c) +{ + + // full buffer wait + lock(); + while ((uart_tx_buffer_widx + 1) % MAX_BUF_SIZE == uart_tx_buffer_ridx) + { + unlock(); + // start asynchronous transfer + enable_mini_uart_w_interrupt(); + lock(); + } + + uart_tx_buffer[uart_tx_buffer_widx++] = c; + if (uart_tx_buffer_widx >= MAX_BUF_SIZE) + uart_tx_buffer_widx = 0; // cycle pointer + + // start asynchronous transfer + // enable interrupt to transfer + enable_mini_uart_w_interrupt(); + unlock(); +} + +char uart_async_getc() +{ + enable_mini_uart_r_interrupt(); + // while buffer empty + // enable read interrupt to get some input into buffer + lock(); + while (uart_rx_buffer_ridx == uart_rx_buffer_widx) + { + unlock(); + enable_mini_uart_r_interrupt(); + lock(); + } + + char r = uart_rx_buffer[uart_rx_buffer_ridx++]; + + if (uart_rx_buffer_ridx >= MAX_BUF_SIZE) + uart_rx_buffer_ridx = 0; + + unlock(); + + return r; +} + +void enable_mini_uart_interrupt() +{ + enable_mini_uart_r_interrupt(); + enable_mini_uart_w_interrupt(); + *IRQS1 |= 1 << 29; +} + +void disable_mini_uart_interrupt() +{ + disable_mini_uart_r_interrupt(); + disable_mini_uart_w_interrupt(); +} + +void enable_mini_uart_r_interrupt() +{ + *AUX_MU_IER |= 1; // read interrupt +} + +void enable_mini_uart_w_interrupt() +{ + *AUX_MU_IER |= 2; // write interrupt +} + +void disable_mini_uart_r_interrupt() +{ + *AUX_MU_IER &= ~(1); +} + +void disable_mini_uart_w_interrupt() +{ + *AUX_MU_IER &= ~(2); +} + +int mini_uart_r_interrupt_is_enable() +{ + return *AUX_MU_IER & 1; +} + +int mini_uart_w_interrupt_is_enable() +{ + return *AUX_MU_IER & 2; +} diff --git a/sdcard/bcm2710-rpi-3-b-plus.dtb b/sdcard/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..3934b3a26 Binary files /dev/null and b/sdcard/bcm2710-rpi-3-b-plus.dtb differ diff --git a/sdcard/bootcode.bin b/sdcard/bootcode.bin new file mode 100644 index 000000000..944263a65 Binary files /dev/null and b/sdcard/bootcode.bin differ diff --git a/sdcard/config.txt b/sdcard/config.txt new file mode 100644 index 000000000..49fc25695 --- /dev/null +++ b/sdcard/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x20000000 diff --git a/sdcard/fixup.dat b/sdcard/fixup.dat new file mode 100644 index 000000000..e928ee3fc Binary files /dev/null and b/sdcard/fixup.dat differ