diff --git a/lab6/bcm2710-rpi-3-b-plus.dtb b/lab6/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..38395a23f Binary files /dev/null and b/lab6/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab6/config.txt b/lab6/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/lab6/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/lab6/create_cpio.sh b/lab6/create_cpio.sh new file mode 100755 index 000000000..dc42635e1 --- /dev/null +++ b/lab6/create_cpio.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Make rootfs folder +# Select all files and put into archives +# '-o, --create' Run in copy-out mode +# '-H FORMAT' archive format, +# newc: SVR4 portable format + +mkdir -p rootfs + +cat < rootfs/Hello +Hello +EOL + +cat < rootfs/World +W +o +r +l +d +EOL + +cd rootfs +find . | cpio -o -H newc > ../initramfs.cpio +cd .. + +rm -rf rootfs \ No newline at end of file diff --git a/lab6/create_fs/FromTA/initramfs.cpio b/lab6/create_fs/FromTA/initramfs.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/lab6/create_fs/FromTA/initramfs.cpio differ diff --git a/lab6/create_fs/create_cpio.sh b/lab6/create_fs/create_cpio.sh new file mode 100755 index 000000000..f1270ba2c --- /dev/null +++ b/lab6/create_fs/create_cpio.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Go to rootfs folder +# Select all files and put into archives +# '-o, --create' Run in copy-out mode +# '-H FORMAT' archive format, +# newc: SVR4 portable format + +cd rootfs +find . | cpio -o -H newc > ../initramfs.cpio +cd .. diff --git a/lab6/create_fs/initramfs.cpio b/lab6/create_fs/initramfs.cpio new file mode 100644 index 000000000..abc766ad4 Binary files /dev/null and b/lab6/create_fs/initramfs.cpio differ diff --git a/lab6/create_fs/lab3_user_proc/linker.ld b/lab6/create_fs/lab3_user_proc/linker.ld new file mode 100644 index 000000000..faee5d7b1 --- /dev/null +++ b/lab6/create_fs/lab3_user_proc/linker.ld @@ -0,0 +1,5 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text) } +} diff --git a/lab6/create_fs/lab3_user_proc/makefile b/lab6/create_fs/lab3_user_proc/makefile new file mode 100644 index 000000000..590bdf420 --- /dev/null +++ b/lab6/create_fs/lab3_user_proc/makefile @@ -0,0 +1,23 @@ +ARMGNU ?= aarch64-linux-gnu + +BUILD_DIR = build +#--------------------------------------------------------------------------------------- + +ASM_FILES = $(wildcard *.S) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_s.o: %.S + @mkdir -p $(@D) + $(ARMGNU)-gcc -c $< -o $@ + +user_proc.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/user_proc.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/user_proc.elf -O binary user_proc.img + +all: user_proc.img + +clean: + rm -rf $(BUILD_DIR) *.img diff --git a/lab6/create_fs/lab3_user_proc/user_proc.S b/lab6/create_fs/lab3_user_proc/user_proc.S new file mode 100644 index 000000000..2d9837651 --- /dev/null +++ b/lab6/create_fs/lab3_user_proc/user_proc.S @@ -0,0 +1,12 @@ +// Run `svc 0` 5 times; svc (el0 -> el1) system calls +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/lab6/create_fs/lab5_user_fork/fork_test.S b/lab6/create_fs/lab5_user_fork/fork_test.S new file mode 100644 index 000000000..463d19529 --- /dev/null +++ b/lab6/create_fs/lab5_user_fork/fork_test.S @@ -0,0 +1,28 @@ +void fork_test(){ + printf("\nFork Test, pid %d\n", get_pid()); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + ++cnt; + + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + delay(1000000); + ++cnt; + } + } + exit(); + } + else { + printf("parent here, pid %d, child %d\n", get_pid(), ret); + } +} diff --git a/lab6/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio b/lab6/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/lab6/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio differ diff --git a/lab6/create_fs/lab5_user_fork/makefile b/lab6/create_fs/lab5_user_fork/makefile new file mode 100644 index 000000000..160b9729a --- /dev/null +++ b/lab6/create_fs/lab5_user_fork/makefile @@ -0,0 +1,23 @@ +ARMGNU ?= aarch64-linux-gnu + +BUILD_DIR = build +#--------------------------------------------------------------------------------------- + +ASM_FILES = $(wildcard *.S) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_s.o: %.S + @mkdir -p $(@D) + $(ARMGNU)-gcc -c $< -o $@ + +user_proc.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/user_proc.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/user_proc.elf -O binary user_proc.img + +all: fork_test.img + +clean: + rm -rf $(BUILD_DIR) *.img diff --git a/lab6/create_fs/rootfs/file0.txt b/lab6/create_fs/rootfs/file0.txt new file mode 100644 index 000000000..c2e1803c9 --- /dev/null +++ b/lab6/create_fs/rootfs/file0.txt @@ -0,0 +1,2 @@ +[This is file 0] +[Hello World!] diff --git a/lab6/create_fs/rootfs/file1.txt b/lab6/create_fs/rootfs/file1.txt new file mode 100644 index 000000000..5d585c1ac --- /dev/null +++ b/lab6/create_fs/rootfs/file1.txt @@ -0,0 +1,11 @@ +{this is file1} +. +.F +.I +.L +.E +. +.1 +. + + diff --git a/lab6/initramfs.cpio b/lab6/initramfs.cpio new file mode 100644 index 000000000..abc766ad4 Binary files /dev/null and b/lab6/initramfs.cpio differ diff --git a/lab6/kernel/Makefile b/lab6/kernel/Makefile new file mode 100644 index 000000000..7914a0c1d --- /dev/null +++ b/lab6/kernel/Makefile @@ -0,0 +1,89 @@ +CC := aarch64-linux-gnu-gcc +LD := aarch64-linux-gnu-ld +OBJCOPY := aarch64-linux-gnu-objcopy +GDB := gdb-multiarch +QEMU := qemu-system-aarch64 +KILL_QEMU := killall $(QEMU) + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -mgeneral-regs-only $(cops) +ASMOPS = -Iinclude +QOPS = -M raspi3b -kernel $(NAME).img -dtb ../bcm2710-rpi-3-b-plus.dtb -initrd ../initramfs.cpio +GDBOPS = --init-eval-command="file $(BUILD_DIR)/$(NAME).elf" --init-eval-command="target remote :1234" + +RED=\033[0;31m +GREEN=\033[0;32m +YELLOW=\033[0;33m +BLUE=\033[0;34m +PURPLE=\033[0;35m +CYAN=\033[0;36m +RESET=\033[0m + +# Set the TARGET_PLATFORM and _DEBUG macro to be passed to the GCC definition later. +ifdef DEBUG +COPS+=-D_DEBUG=$(DEBUG) +endif + +ifneq (,$(findstring pi,$(MAKECMDGOALS))) +COPS+=-DRPI +FINISH_STR = "\r\n-------------------------------------------------------------\r\n The $(CYAN)kernel8.img$(RESET) intended for the $(BLUE)Raspberry Pi 3B+$(RESET) is ready.\r\n-------------------------------------------------------------" +else # Default to QEMU +COPS += -DQEMU +FINISH_STR = "\r\n-------------------------------------------------\r\n The $(CYAN)kernel8.img$(RESET) intended for the $(BLUE)QEMU$(RESET) is ready.\r\n-------------------------------------------------" +endif + +NAME = kernel8 +BUILD_DIR = build +SRC_DIR = src +LK_SCRIPT = linker.ld +SESSION_NAME := my-os + +.PHONY: all +all : $(NAME).img + +.PHONY: clean +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(CC) $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(CC) $(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) + +$(NAME).img: $(SRC_DIR)/$(LK_SCRIPT) $(OBJ_FILES) + $(LD) -T $(SRC_DIR)/$(LK_SCRIPT) -o $(BUILD_DIR)/$(NAME).elf $(OBJ_FILES) + $(OBJCOPY) $(BUILD_DIR)/$(NAME).elf -O binary $(NAME).img + @echo -e $(FINISH_STR) + +#This target compiles and runs the kernel.img of the QEMU platform by default. +# You can use "make pi run" to run this image in QEMU. +.PHONY: run +run: clean $(NAME).img + $(QEMU) $(QOPS) -serial null -serial stdio + +# This target compiles kernel.img to run on a QEMU. +.PHONY: qemu +qemu: clean $(NAME).img + +# This target compiles kernel.img to run on a Raspberry 3B. +.PHONY: pi +pi: clean $(NAME).img + +# This target will run kernel in QEMU with GDB attached in a tmux session. +.PHONY: debug +debug: clean $(NAME).img + tmux new-session -d -s $(SESSION_NAME) '$(QEMU) $(QOPS) -S -s -serial null -serial stdio' \; split-window -h '$(GDB) $(GDBOPS)' \; attach -t $(SESSION_NAME) + +# This target will kill the QEMU session. +.PHONY: kill +kill: + $(KILL_QEMU) diff --git a/lab6/kernel/include/bcm2837/rpi_base.h b/lab6/kernel/include/bcm2837/rpi_base.h new file mode 100644 index 000000000..f640aaf4d --- /dev/null +++ b/lab6/kernel/include/bcm2837/rpi_base.h @@ -0,0 +1,7 @@ +#ifndef _RPI_BASE_H_ +#define _RPI_BASE_H_ + +#include "bcm2837/rpi_mmu.h" +#define PERIPHERAL_BASE PHYS_TO_KERNEL_VIRT(0x3F000000) + +#endif /*_RPI_BASE_H_ */ diff --git a/lab6/kernel/include/bcm2837/rpi_gpio.h b/lab6/kernel/include/bcm2837/rpi_gpio.h new file mode 100644 index 000000000..e5133708a --- /dev/null +++ b/lab6/kernel/include/bcm2837/rpi_gpio.h @@ -0,0 +1,25 @@ +#ifndef _RPI_GPIO_H_ +#define _RPI_GPIO_H_ + +#include "bcm2837/rpi_base.h" + +#define GPFSEL0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020009C)) + +#endif /*_RPI_GPIO_H_*/ diff --git a/lab6/kernel/include/bcm2837/rpi_irq.h b/lab6/kernel/include/bcm2837/rpi_irq.h new file mode 100644 index 000000000..8423dfea0 --- /dev/null +++ b/lab6/kernel/include/bcm2837/rpi_irq.h @@ -0,0 +1,24 @@ +#ifndef _RPI_IRQ_H_ +#define _RPI_IRQ_H_ + +#include "bcm2837/rpi_base.h" + +/* +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 IRQ_BASIC_PENDING ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B200)) +#define IRQ_PENDING_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B208)) +#define FIQ_CONTROL ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B224)) + +#endif /*_RPI_IRQ_H_*/ diff --git a/lab6/kernel/include/bcm2837/rpi_mbox.h b/lab6/kernel/include/bcm2837/rpi_mbox.h new file mode 100644 index 000000000..ba1001472 --- /dev/null +++ b/lab6/kernel/include/bcm2837/rpi_mbox.h @@ -0,0 +1,17 @@ +#ifndef _RPI_MBOX_H_ +#define _RPI_MBOX_H_ + +#include "bcm2837/rpi_base.h" + +#define MBOX_BASE (PERIPHERAL_BASE+0x0000B880) + +// The register access to a mailbox +// https://jsandler18.github.io/extra/mailbox.html +#define MBOX_READ ((volatile unsigned int*)(MBOX_BASE+0x00)) +#define MBOX_POLL ((volatile unsigned int*)(MBOX_BASE+0x10)) +#define MBOX_SENDER ((volatile unsigned int*)(MBOX_BASE+0x14)) +#define MBOX_STATUS ((volatile unsigned int*)(MBOX_BASE+0x18)) +#define MBOX_CONFIG ((volatile unsigned int*)(MBOX_BASE+0x1C)) +#define MBOX_WRITE ((volatile unsigned int*)(MBOX_BASE+0x20)) + +#endif /*_RPI_MBOX_H_ */ diff --git a/lab6/kernel/include/bcm2837/rpi_mmu.h b/lab6/kernel/include/bcm2837/rpi_mmu.h new file mode 100644 index 000000000..e0d82c7dd --- /dev/null +++ b/lab6/kernel/include/bcm2837/rpi_mmu.h @@ -0,0 +1,18 @@ +#ifndef _RPI_MMU_H_ +#define _RPI_MMU_H_ + +#define PHYS_TO_VIRT(x) (x + 0xffff000000000000) +#define VIRT_TO_PHYS(x) (x - 0xffff000000000000) +#define ENTRY_ADDR_MASK 0x0000fffffffff000L + +#define PHYS_TO_KERNEL_VIRT(x) (((unsigned long)(x)) | 0xffff000000000000) +#define KERNEL_VIRT_TO_PHYS(x) (((unsigned long)(x)) & ~0xffff000000000000) + +// e.g. size=0x13200, alignment=0x1000 -> 0x14000 +#define ALIGN_UP(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1)) +// e.g. size=0x13200, alignment=0x1000 -> 0x13000 +#define ALIGN_DOWN(size, alignment) ((size) & ~((alignment) - 1)) + +#define IS_NOT_ALIGN(ptr, alignment) (((unsigned long)ptr & ((alignment) - 1)) != 0) + +#endif /*_RPI_MMU_H_ */ diff --git a/lab6/kernel/include/bcm2837/rpi_uart1.h b/lab6/kernel/include/bcm2837/rpi_uart1.h new file mode 100644 index 000000000..959130656 --- /dev/null +++ b/lab6/kernel/include/bcm2837/rpi_uart1.h @@ -0,0 +1,19 @@ +#ifndef _RPI_UART1_H_ +#define _RPI_UART1_H_ + +#include "bcm2837/rpi_base.h" + +#define AUX_ENABLES ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215004)) +#define AUX_MU_IO_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215040)) +#define AUX_MU_IER_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215044)) +#define AUX_MU_IIR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215048)) +#define AUX_MU_LCR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x0021504C)) +#define AUX_MU_MCR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215050)) +#define AUX_MU_LSR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215054)) +#define AUX_MU_MSR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(PERIPHERAL_BASE+0x0021505C)) +#define AUX_MU_CNTL_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215060)) +#define AUX_MU_STAT_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215064)) +#define AUX_MU_BAUD_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215068)) + +#endif /*_RPI_UART1_H_ */ diff --git a/lab6/kernel/include/cpio.h b/lab6/kernel/include/cpio.h new file mode 100644 index 000000000..bfe2c7044 --- /dev/null +++ b/lab6/kernel/include/cpio.h @@ -0,0 +1,47 @@ +#ifndef _CPIO_H_ +#define _CPIO_H_ + +/* + cpio format : https://manpages.ubuntu.com/manpages/bionic/en/man5/cpio.5.html + We are using "newc" format + 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 constant, to check whether it is big endian or little endian + +// Using newc archive format +struct cpio_newc_header +{ + char c_magic[6]; // fixed, "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]; +}; + +/* 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); + +unsigned int padding_4byte(unsigned int size); + +extern void *CPIO_DEFAULT_START; + +#define CPIO_for_each(c_filepath, c_filesize, c_filedata) \ + struct cpio_newc_header *header_ptr = CPIO_DEFAULT_START; \ + int err = cpio_newc_parse_header(header_ptr, c_filepath, c_filesize, c_filedata, &header_ptr); \ + for (; header_ptr&&!err; cpio_newc_parse_header(header_ptr, c_filepath, c_filesize, c_filedata, &header_ptr)) + +#endif /* _CPIO_H_ */ \ No newline at end of file diff --git a/lab6/kernel/include/debug.h b/lab6/kernel/include/debug.h new file mode 100644 index 000000000..2e7d97904 --- /dev/null +++ b/lab6/kernel/include/debug.h @@ -0,0 +1,19 @@ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include "uart1.h" + +#define CYAN "\e[0;36m" // 青色 +#define HRED "\e[0;91m" +#define CRESET "\e[0m" + +#define WARING(fmt, ...) PRINT_MESSAGE(LEVEL_ERROR, "WARING", CYAN, fmt, ##__VA_ARGS__) +#define ERROR(fmt, ...) PRINT_MESSAGE(LEVEL_ERROR, "ERROR", HRED, fmt, ##__VA_ARGS__) +#define PRINT_MESSAGE(level, prefix, color, fmt, ...) \ + do \ + { \ + uart_sendlinek(color "[" prefix "] " CRESET fmt, ##__VA_ARGS__); \ + } while (0) + +#endif /* _DEBUG_H_ */ \ No newline at end of file diff --git a/lab6/kernel/include/dtb.h b/lab6/kernel/include/dtb.h new file mode 100644 index 000000000..7076d8683 --- /dev/null +++ b/lab6/kernel/include/dtb.h @@ -0,0 +1,26 @@ +#ifndef _DTB_H_ +#define _DTB_H_ + +#define uint32_t unsigned int +#define uint64_t unsigned long long + +// manipulate device tree with dtb file format +// linux kernel fdt.h +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +typedef void (*dtb_callback)(uint32_t node_type, char *name, void *value, uint32_t name_size); + +uint32_t uint32_endian_big2little(uint32_t data); +uint64_t uint64_endian_big2little(uint64_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 dtb_find_and_store_reserved_memory(); + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/exception.h b/lab6/kernel/include/exception.h new file mode 100644 index 000000000..eb975a1aa --- /dev/null +++ b/lab6/kernel/include/exception.h @@ -0,0 +1,92 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" + +#define SYSCALL(number, function) \ + case number: \ + function; \ + break; + +// 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_KERNEL_VIRT(0x40000060))) +#define INTERRUPT_SOURCE_CNTPNSIRQ (1 << 1) +#define INTERRUPT_SOURCE_GPU (1 << 8) +#define IRQ_PENDING_1_AUX_INT (1 << 29) + +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; + +#define MEMFAIL_DATA_ABORT_LOWER 0b100100 // esr_el1 +#define MEMFAIL_INST_ABORT_LOWER 0b100000 // EC, bits [31:26] + +#define TF_LEVEL0 0b000100 // iss IFSC, bits [5:0] +#define TF_LEVEL1 0b000101 +#define TF_LEVEL2 0b000110 +#define TF_LEVEL3 0b000111 + +#define ESR_EL1_EC_SHIFT 26 +#define ESR_EL1_EC_MASK 0x3F +#define ESR_EL1_EC_SVC64 0x15 + +typedef struct +{ + unsigned int iss : 25, // Instruction specific syndrome + il : 1, // Instruction length bit + ec : 6; // Exception class +} esr_el1_t; + +void el1_interrupt_enable(); +void el1_interrupt_disable(); + +void lock(); +void unlock(); + +void el1h_sync_router(trapframe_t *tpf); +void el1h_irq_router(trapframe_t *tpf); +void el0_sync_router(trapframe_t *tpf); +void el0_irq_router(trapframe_t *tpf); + +void invalid_exception_router(unsigned long long x0); // exception_handler.S + +const char *get_exception_name(unsigned long esr_el1); + + +#endif /*_EXCEPTION_H_*/ diff --git a/lab6/kernel/include/irqtask.h b/lab6/kernel/include/irqtask.h new file mode 100644 index 000000000..52783e26b --- /dev/null +++ b/lab6/kernel/include/irqtask.h @@ -0,0 +1,22 @@ +#ifndef _IRQTASK_H_ +#define _IRQTASK_H_ + +#include "u_list.h" + +// smaller is more preemptive +#define UART_IRQ_PRIORITY 1 +#define TIMER_IRQ_PRIORITY 0 + +typedef struct irqtask +{ + struct list_head listhead; + unsigned long long priority; // store priority (smaller number is more preemptive) + void *task_function; // task function pointer +} irqtask_t; + +void irqtask_add(void *task_function, unsigned long long priority); +void irqtask_run(irqtask_t *the_task); +void irqtask_run_preemptive(); +void irqtask_list_init(); + +#endif diff --git a/lab6/kernel/include/mbox.h b/lab6/kernel/include/mbox.h new file mode 100644 index 000000000..39270518e --- /dev/null +++ b/lab6/kernel/include/mbox.h @@ -0,0 +1,66 @@ +#ifndef _MBOX_H_ +#define _MBOX_H_ + +extern volatile unsigned int pt[64]; + +// Mailbox Register MMIO +// https://jsandler18.github.io/extra/mailbox.html +// include/bcm2837/rpi_mbox.h + +// Mailbox Channels +// https://github.com/raspberrypi/firmware/wiki/Mailboxes +typedef enum +{ + MBOX_POWER_MANAGEMENT = 0, + MBOX_FRAMEBUFFER, + MBOX_VIRTUAL_UART, + MBOX_VCHIQ, + MBOX_LEDS, + MBOX_BUTTONS, + MBOX_TOUCHSCREEN, + MBOX_UNUSED, + MBOX_TAGS_ARM_TO_VC, + MBOX_TAGS_VC_TO_ARM, +} mbox_channel_type; + +// Status Code from Broadcom Videocode Driver +// brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/arm_control.h +enum mbox_status_reg_bits +{ + BCM_ARM_VC_MS_FULL = 0x80000000, + BCM_ARM_VC_MS_EMPTY = 0x40000000, + BCM_ARM_VC_MS_LEVEL = 0x400000FF, +}; + +enum mbox_buffer_status_code +{ + MBOX_REQUEST_PROCESS = 0x00000000, + MBOX_REQUEST_SUCCEED = 0x80000000, + MBOX_REQUEST_FAILED = 0x80000001, +}; + +// Tag +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +// Included partition only +typedef enum +{ + /* Videocore */ + MBOX_TAG_GET_FIRMWARE_VERSION = 0x1, + + /* Hardware */ + MBOX_TAG_GET_BOARD_MODEL = 0x10001, + MBOX_TAG_GET_BOARD_REVISION, + MBOX_TAG_GET_BOARD_MAC_ADDRESS, + MBOX_TAG_GET_BOARD_SERIAL, + MBOX_TAG_GET_ARM_MEMORY, + MBOX_TAG_GET_VC_MEMORY, + MBOX_TAG_GET_CLOCKS, + +} mbox_tag_type; + +#define MBOX_TAG_REQUEST_CODE 0x00000000 +#define MBOX_TAG_LAST_BYTE 0x00000000 + +int mbox_call(mbox_channel_type, unsigned int); + +#endif /*_MBOX_H_*/ diff --git a/lab6/kernel/include/memory.h b/lab6/kernel/include/memory.h new file mode 100644 index 000000000..35f7ddb16 --- /dev/null +++ b/lab6/kernel/include/memory.h @@ -0,0 +1,80 @@ +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" +/* Lab2 */ +void *allocator(unsigned int size); +// void free(void* ptr); + +/* Lab4 */ +#define BUDDY_MEMORY_BASE PHYS_TO_KERNEL_VIRT(0x0) // 0x10000000 - 0x20000000 (SPEC) -> Advanced #3 for all memory region +#define BUDDY_MEMORY_PAGE_COUNT 0x3C000 // let BUDDY_MEMORY use 0x0 ~ 0x3C000000 (SPEC) +#define PAGESIZE 0x1000 // 4KB + +#define CACHE_SEG 0x8 +#define CACHE_offset 3 +#define CACHE_record_num 8 + +// #define MAX_PAGES 0x10000 // 65536 (Entries), PAGESIZE * MAX_PAGES = 0x10000000 (SPEC) + +typedef enum +{ + FRAME_FREE = -2, + FRAME_ALLOCATED = -1, + FRAME_IDX_0 = 0, // 0x1000 + FRAME_IDX_8 = 8, + FRAME_IDX_FINAL = 17, // 0x20 000 000 + FRAME_MAX_IDX = 18 +} frame_value_type; + +typedef enum +{ + CACHE_IDX_0 = 0, // 0x20 + CACHE_IDX_FINAL = 6, // 0x800 + CACHE_MAX_IDX = 7 // 0x1000 +} cache_value_type; + +typedef struct frame +{ + struct list_head listhead; // store freelist + int val; // store order + int used; + unsigned int idx; +} frame_t; + +typedef struct cache +{ + struct list_head listhead; // store freelist + void *data_base; + int cache_order; + int max_available; + int available; + unsigned long long cache_record[CACHE_record_num]; +} cache_t; + +void allocator_init(); +frame_t *release_redundant(frame_t *frame); +frame_t *get_buddy(frame_t *frame); +int coalesce(frame_t *frame_ptr); + +void dump_page_info(); +void dump_cache_info(); + +// buddy system +void *page_malloc(unsigned int size); +void page_free(frame_t *ptr); +void page2caches(int order); +void *cache_malloc(unsigned int size); +void cache_free(void *ptr); + +void *kmalloc(unsigned int size); +void kfree(void *ptr); +void memory_reserve(unsigned long long start, unsigned long long end); + +void freelist_init(); +void page2caches(int c_val); +void *find_CACHE(cache_t *ptr); +frame_t *find_free_page(int val); + +#endif /* _MEMORY_H_ */ \ No newline at end of file diff --git a/lab6/kernel/include/mmu.h b/lab6/kernel/include/mmu.h new file mode 100644 index 000000000..bb58ee390 --- /dev/null +++ b/lab6/kernel/include/mmu.h @@ -0,0 +1,101 @@ +#ifndef _MMU_H_ +#define _MMU_H_ + +#include "stddef.h" +// tcr_el1: The control register for stage 1 of the EL1&0 translation regime. +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) // T0SZ 和 T1SZ 設為 16 +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) // TG0 和 TG1 設為 4KB +#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 // Table Entry Armv8_a_address_translation p.14 +#define PD_BLOCK 0b01L // Block Entry +#define PD_UNX (1L << 54) // non-executable page frame for EL0 if set +#define PD_KNX (1L << 53) // non-executable page frame for EL1 if set +#define PD_ACCESS (1L << 10) // a page fault is generated if not set +#define PD_RDONLY (1L << 7) // 0 for read-write, 1 for read-only. +#define PD_UK_ACCESS (1L << 6) // 0 for only kernel access, 1 for user/kernel access. + +#define PERIPHERAL_START 0x3C000000L +#define PERIPHERAL_END 0x3F000000L +#define USER_DATA_BASE 0x00000000L +#define USER_STACK_BASE 0x0000fffffffff000L +#define USER_SIGNAL_WRAPPER_VA 0x0000FFFFFFFAF000L //<------------------------------------- +#define USER_EXEC_WRAPPER_VA 0x0000FFFFFFFBF000L //<------------------------------------- + +#define MMU_PGD_BASE 0x1000L +#define MMU_PGD_ADDR (MMU_PGD_BASE + 0x0000L) +#define MMU_PUD_ADDR (MMU_PGD_BASE + 0x1000L) +#define MMU_PTE_ADDR (MMU_PGD_BASE + 0x2000L) + +// Used for EL1 +#define BOOT_PGD_ATTR (PD_TABLE) +#define BOOT_PUD_ATTR (PD_TABLE | PD_ACCESS) +#define BOOT_PTE_ATTR_nGnRnE (PD_BLOCK | PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_UNX | PD_KNX | PD_UK_ACCESS) // p.17 +#define BOOT_PTE_ATTR_NOCACHE (PD_BLOCK | PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2)) + +#ifndef __ASSEMBLER__ + +#include "sched.h" +#include "exception.h" +#include "u_list.h" +#include "stddef.h" + + +#define PERMISSION_INVAILD(userId,VMA_Permission) (userId&~VMA_Permission) +#define DUMP_NAME(number, name) \ + case number: \ + uart_sendlinek(name); \ + uart_sendlinek("\n"); \ + break; + +typedef enum +{ + UNKNOW_AREA = -1, + USER_DATA, + USER_STACK, + PERIPHERAL, + USER_SIGNAL_WRAPPER, + USER_EXEC_WRAPPER +} vma_name_type; + +typedef enum +{ + PGD, + PUD, + PMD, + PTE, +} pagetable_type; + +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; + vma_name_type name; +} vm_area_struct_t; + +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 mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced, vma_name_type name); +void mmu_del_vma(struct thread *t); +void mmu_free_page_tables(size_t *page_table, int level); +// void mmu_set_PTE_readonly(size_t *page_table, int level); +// void mmu_pagetable_copy(unsigned long *dst_page_table, unsigned long *src_page_table, int level); +void mmu_memfail_abort_handle(esr_el1_t *esr_el1); + +vm_area_struct_t *check_vma_overlap(thread_t *t,unsigned long user_va, unsigned long size); +int check_permission(int userId, int requiredPermission); +void dump_vma(); +void dump_pagetable(unsigned long user_va, unsigned long pa); + +#endif //__ASSEMBLER__ + +#endif /* _MMU_H_ */ diff --git a/lab6/kernel/include/power.h b/lab6/kernel/include/power.h new file mode 100644 index 000000000..07da61847 --- /dev/null +++ b/lab6/kernel/include/power.h @@ -0,0 +1,9 @@ +#ifndef _POWER_H_ +#define _POWER_H_ + +#include "bcm2837/rpi_mmu.h" +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC PHYS_TO_KERNEL_VIRT(0x3F10001c) +#define PM_WDOG PHYS_TO_KERNEL_VIRT(0x3F100024) + +#endif /*_POWER_H_*/ diff --git a/lab6/kernel/include/sched.h b/lab6/kernel/include/sched.h new file mode 100644 index 000000000..b4b8bf34c --- /dev/null +++ b/lab6/kernel/include/sched.h @@ -0,0 +1,75 @@ +#ifndef _SCHED_H_ +#define _SCHED_H_ + +#include "u_list.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x10000 +#define KSTACK_SIZE 0x10000 +#define SIGNAL_MAX 64 +#define STACK_BASE_OFFSET 0x00 + +extern void switch_to(void *curr_context, void *next_context); +extern void *get_current(); +extern void store_context(void *curr_context); +extern void load_context(void *curr_context); + +// arch/arm64/include/asm/processor.h - cpu_context +typedef struct thread_context +{ + unsigned long x19; // callee saved registers: the called function will preserve them and restore them before returning + 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; // base pointer for local variable in stack + unsigned long lr; // store return address + unsigned long sp; // stack pointer, varys from function calls + void* pgd; // use for MMU mapping (user space) +} thread_context_t; + +typedef struct thread +{ + list_head_t listhead; // Freelist node + thread_context_t context; // Thread registers + char *data; // Process itself + unsigned int datasize; // Process size + int iszombie; // Process statement + int pid; // Process ID + int isused; // Freelist node statement + char *stack_alloced_ptr; // Process Stack (Process itself) + char *kernel_stack_alloced_ptr; // Process Stack (Kernel syscall) + void (*signal_handler[SIGNAL_MAX + 1])(); // Signal handlers for different signal + int sigcount[SIGNAL_MAX + 1]; // Signal Pending buffer + void (*curr_signal_handler)(); // Allow Signal handler overwritten by others + int signal_is_checking; // Signal Processing Lock + thread_context_t signal_savedContext; // Store registers before signal handler involving + list_head_t vma_list; +} thread_t; + +typedef struct execfile +{ + char *data; + unsigned int filesize; +} execfile; + + +void schedule_timer(char *notuse); +void thread_sched_init(); +void idle(); +void schedule(); +void kill_zombies(); +void thread_exit(); +thread_t *thread_create(void *start); +int exec_thread(); +void exec_proc(); +void exec_wrapper(); + +void foo(); + +#endif /* _SCHED_H_ */ diff --git a/lab6/kernel/include/shell.h b/lab6/kernel/include/shell.h new file mode 100644 index 000000000..0ab060fe2 --- /dev/null +++ b/lab6/kernel/include/shell.h @@ -0,0 +1,39 @@ +#ifndef _SHELL_H_ +#define _SHELL_H_ + +#define CLI_MAX_CMD 14 +#define CMD_MAX_LEN 32 +#define CMD_MAX_PARAM 10 +#define MSG_MAX_LEN 128 + +typedef struct CLI_CMDS +{ + char command[CMD_MAX_LEN]; + char help[MSG_MAX_LEN]; + int (*func)(int, char **); +} CLI_CMDS; + +int _parse_args(char *buffer, int *argc, char **argv); + +void start_shell(); + +void cli_flush_buffer(char *, int); +void cli_cmd_read(char *); +void cli_cmd_exec(char *); +void cli_print_banner(); + +int do_cmd_help(int argc, char **argv); +int do_cmd_hello(int argc, char **argv); +int do_cmd_info(int argc, char **argv); +int do_cmd_reboot(int argc, char **argv); +int do_cmd_ls(int argc, char **argv); +int do_cmd_cat(int argc, char **argv); +int do_cmd_malloc(int argc, char **argv); +int do_cmd_dtb(int argc, char **argv); +int do_cmd_exec(int argc, char **argv); +int do_cmd_setTimeout(int argc, char **argv); +int do_cmd_set2sAlert(int argc, char **argv); +int do_cmd_mtest(int argc, char **argv); +int do_cmd_ttest(int argc, char **argv); +int do_cmd_ftest(int argc, char **argv); +#endif /* _SHELL_H_ */ diff --git a/lab6/kernel/include/signal.h b/lab6/kernel/include/signal.h new file mode 100644 index 000000000..afe2c1617 --- /dev/null +++ b/lab6/kernel/include/signal.h @@ -0,0 +1,49 @@ +#ifndef _SIGNAL_H_ +#define _SIGNAL_H_ + +#include "exception.h" +#include "memory.h" + +#define SIGHUP 1 /* Hangup (POSIX). */ +#define SIGINT 2 /* Interrupt (ANSI). */ +#define SIGQUIT 3 /* Quit (POSIX). */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ +#define SIGTRAP 5 /* Trace trap (POSIX). */ +#define SIGABRT 6 /* Abort (ANSI). */ +#define SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define SIGBUS 7 /* BUS error (4.2 BSD). */ +#define SIGFPE 8 /* Floating-point exception (ANSI). */ +#define SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define SIGPIPE 13 /* Broken pipe (POSIX). */ +#define SIGALRM 14 /* Alarm clock (POSIX). */ +#define SIGTERM 15 /* Termination (ANSI). */ +#define SIGSTKFLT 16 /* Stack fault. */ +#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define SIGCHLD 17 /* Child status has changed (POSIX). */ +#define SIGCONT 18 /* Continue (POSIX). */ +#define SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define SIGTTIN 21 /* Background read from tty (POSIX). */ +#define SIGTTOU 22 /* Background write to tty (POSIX). */ +#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define SIGPWR 30 /* Power failure restart (System V). */ +#define SIGSYS 31 /* Bad system call. */ +#define SIGUNUSED 31 + +void signal_default_handler(); +void check_signal(trapframe_t *tpf); +void run_signal(trapframe_t *tpf, int signal); +//void __attribute__((aligned(PAGESIZE))) signal_handler_wrapper(); +void signal_handler_wrapper(); + +#endif diff --git a/lab6/kernel/include/stddef.h b/lab6/kernel/include/stddef.h new file mode 100644 index 000000000..61b71d842 --- /dev/null +++ b/lab6/kernel/include/stddef.h @@ -0,0 +1,12 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define size_t unsigned long +#define NULL ((void *)0) + +// typedef enum { +// false = 0, +// true = 1 +// } boolean; + +#endif \ No newline at end of file diff --git a/lab6/kernel/include/stdio.h b/lab6/kernel/include/stdio.h new file mode 100644 index 000000000..17eaf0d49 --- /dev/null +++ b/lab6/kernel/include/stdio.h @@ -0,0 +1,10 @@ + +char getchar(); +void putchar(char c); +void put_int(int num); +void puts(const char *s); +void put_hex(unsigned int num); +int atoi(char* str); +void Readfile(char *str,int size); +int fake_log2(unsigned long long n); +void delay(int s); \ No newline at end of file diff --git a/lab6/kernel/include/string.h b/lab6/kernel/include/string.h new file mode 100644 index 000000000..4a0fd9198 --- /dev/null +++ b/lab6/kernel/include/string.h @@ -0,0 +1,10 @@ +#include "stddef.h" +#define VSPRINT_MAX_BUF_SIZE 0x100 + +size_t strlen(const char *str); +int strcmp(const char *p1, const char *p2); +int strncmp(const char *s1, const char *s2, unsigned long long n); +char *strcpy(char *dest, const char *src); +unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); +void *memset(void *s, int c, size_t n); +char* memcpy(void *dest, const void *src, unsigned long long len); diff --git a/lab6/kernel/include/syscall.h b/lab6/kernel/include/syscall.h new file mode 100644 index 000000000..72de04223 --- /dev/null +++ b/lab6/kernel/include/syscall.h @@ -0,0 +1,28 @@ +#ifndef _SYSCALL_H_ +#define _SYSCALL_H_ + +#include "exception.h" +#include + +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 *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset); + +void syscall_lock(trapframe_t *tpf); +void syscall_unlock(trapframe_t *tpf); + +unsigned int get_file_size(char *thefilepath); +char *get_file_start(char *thefilepath); + +#endif /* _SYSCALL_H_*/ diff --git a/lab6/kernel/include/timer.h b/lab6/kernel/include/timer.h new file mode 100644 index 000000000..8b02b6b8d --- /dev/null +++ b/lab6/kernel/include/timer.h @@ -0,0 +1,36 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" +#define CORE0_TIMER_IRQ_CTRL PHYS_TO_VIRT(0x40000040) //-------------------------------------------------------------------------- + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_handler(); + +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; + +typedef enum +{ + setSecond = 0, + setTick = 1 +} timer_type_t; + +// now the callback only support "funcion(char *)", char* in args +void timer_event_callback(timer_event_t *timer_event); +void add_timer(void *callback, unsigned long long timeout, char *args, int inTickFormat); +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 timer_set2sAlert(char *str); +void timer_list_init(); +int timer_list_get_size(); + +#endif /* _TIMER_H_ */ diff --git a/lab6/kernel/include/u_list.h b/lab6/kernel/include/u_list.h new file mode 100644 index 000000000..79767f194 --- /dev/null +++ b/lab6/kernel/include/u_list.h @@ -0,0 +1,150 @@ +#ifndef _U_LIST_H_ +#define _U_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. + * 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_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; \ + !list_is_head(pos, (head)); \ + pos = n, n = 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 /* _U_LIST_H_ */ diff --git a/lab6/kernel/include/uart1.h b/lab6/kernel/include/uart1.h new file mode 100644 index 000000000..7b6bd70ce --- /dev/null +++ b/lab6/kernel/include/uart1.h @@ -0,0 +1,18 @@ +#ifndef _UART1_H_ +#define _UART1_H_ + +void uart_init(); +void uart_flush_FIFO(); +char uart_recv(); +void uart_send(unsigned int c); + +void uart_r_irq_handler(); +void uart_w_irq_handler(); +char uart_async_recv(); +void uart_async_send(char c); + +int uart_sendlinek(char* fmt, ...); +void uart_interrupt_enable(); +void uart_interrupt_disable(); + +#endif /*_UART1_H_*/ diff --git a/lab6/kernel/src/boot.S b/lab6/kernel/src/boot.S new file mode 100644 index 000000000..456dfeb7d --- /dev/null +++ b/lab6/kernel/src/boot.S @@ -0,0 +1,82 @@ +#include "mmu.h" + +.section ".text.boot" + +.global _kernel_start + +_kernel_start: + bl from_el2_to_el1 + +set_mmu_configuration: + // 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 + ldr x4, = MMU_PGD_ADDR // PGD's page frame at 0x1000 + ldr x1, = MMU_PUD_ADDR // 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] // [MMU_PGD_ADDR] = MMU_PUD_ADDR | BOOT_PGD_ATTR + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 // [MMU_PUD_ADDR] = 0x00000000 | BOOT_PUD_ATTR + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 // [MMU_PUD_ADDR + 8] = 0x40000000 | BOOT_PUD_ATTR + 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 // temp stack + bl set_2M_kernel_mmu + + mrs x2, sctlr_el1 // sctlr_el1: Provides top level control of the system, including its memory system, at EL1 and EL0. + orr x2 , x2, 1 // sctlr_el1[0]: EL1&0 stage 1 address translation enabled/disabled. + msr sctlr_el1, x2 + + // indirect branch to the upper virtual address + ldr x2, =set_exception_vector_table + br x2 + +set_exception_vector_table: + adr x1, exception_vector_table // vbar_el1: Vector Base Address Register (EL1) + msr vbar_el1, x1 // Holds the exception base address for any exception that is taken to EL1. + +setup_stack: + ldr x1, =_stack_top + mov sp, x1 + +setup_bss: + ldr x1, =_bss_start + ldr w2, =_bss_size + +init_bss: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, init_bss + +run_main: + ldr x1, =dtb_ptr + str x0, [x1], #8 + bl main + +proc_hang: + wfe + b proc_hang + +from_el2_to_el1: + 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 \ No newline at end of file diff --git a/lab6/kernel/src/cpio.c b/lab6/kernel/src/cpio.c new file mode 100644 index 000000000..56fdfc4e8 --- /dev/null +++ b/lab6/kernel/src/cpio.c @@ -0,0 +1,82 @@ +#include "string.h" +#include "cpio.h" +#include "stdio.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) + { + *next_header_pointer = 0; + puts("cpio parse error\r\n"); + return -1; + } + + // transfer big endian 8 byte hex string to unsigned 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); + // The file data is padded to a multiple of four bytes + offset = padding_4byte(offset); + *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 + padding_4byte(offset)); + } + + // if filepath is TRAILER!!! means there is no more files. + if (strncmp(*pathname, "TRAILER!!!", sizeof("TRAILER!!!")) == 0) + { + *next_header_pointer = 0; + } + + return 0; +} + +unsigned int padding_4byte(unsigned int size) +{ + return size + (4 - size % 4) % 4; +} \ No newline at end of file diff --git a/lab6/kernel/src/dtb.c b/lab6/kernel/src/dtb.c new file mode 100644 index 000000000..df97d64fc --- /dev/null +++ b/lab6/kernel/src/dtb.c @@ -0,0 +1,173 @@ +#include "dtb.h" +#include "uart1.h" +#include "cpio.h" +#include "string.h" +#include "stdio.h" +#include "memory.h" +#include "bcm2837/rpi_mmu.h" +#include "debug.h" + +void *CPIO_DEFAULT_START; +void *CPIO_DEFAULT_END; +char *dtb_ptr; + +// 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_big2little(uint32_t data) +{ + char *r = (char *)&data; + return (r[3] << 0) | (r[2] << 8) | (r[1] << 16) | (r[0] << 24); +} + +uint64_t uint64_endian_big2little(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_big2little(header->magic) != 0xD00DFEED) + { + ERROR("traverse_device_tree : wrong magic in traverse_device_tree\n"); + return; + } + // https://abcamus.github.io/2016/12/28/uboot%E8%AE%BE%E5%A4%87%E6%A0%91-%E8%A7%A3%E6%9E%90%E8%BF%87%E7%A8%8B/ + // https://blog.csdn.net/wangdapao12138/article/details/82934127 + uint32_t struct_size = uint32_endian_big2little(header->size_dt_struct); + char *dt_struct_ptr = (char *)((char *)header + uint32_endian_big2little(header->off_dt_struct)); + char *dt_strings_ptr = (char *)((char *)header + uint32_endian_big2little(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_big2little(*(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_big2little(*(uint32_t *)pointer); + pointer += 4; + char *name = (char *)dt_strings_ptr + uint32_endian_big2little(*(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 + { + puts("error type:"); + put_hex(token_type); + puts("\n"); + return; + } + } +} + +void dtb_callback_show_tree(uint32_t node_type, char *name, void *data, uint32_t name_size) +{ + static int level = 0; + if (node_type == FDT_BEGIN_NODE) + { + for (int i = 0; i < level; i++) + puts(" "); + puts(name); + puts("{\n"); + level++; + } + else if (node_type == FDT_END_NODE) + { + level--; + for (int i = 0; i < level; i++) + puts(" "); + puts("}\n"); + } + else if (node_type == FDT_PROP) + { + for (int i = 0; i < level; i++) + puts(" "); + puts(name); + puts("\n"); + } +} + +void dtb_callback_initramfs(uint32_t node_type, char *name, void *value, uint32_t name_size) +{ + // https://github.com/stweil/raspberrypi-documentation/blob/master/configuration/device-tree.md + // linux,initrd-start will be assigned by start.elf based on config.txt + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-start") == 0) + { + CPIO_DEFAULT_START = (void *)(unsigned long long)PHYS_TO_KERNEL_VIRT(uint32_endian_big2little(*(uint32_t *)value)); + } + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-end") == 0) + { + CPIO_DEFAULT_END = (void *)(unsigned long long)PHYS_TO_KERNEL_VIRT(uint32_endian_big2little(*(uint32_t *)value)); + } +} + +void dtb_find_and_store_reserved_memory() +{ + struct fdt_header *header = (struct fdt_header *)dtb_ptr; + if (uint32_endian_big2little(header->magic) != 0xD00DFEED) + { + ERROR("traverse_device_tree : wrong magic in traverse_device_tree\n"); + return; + } + + // off_mem_rsvmap stores all of reserve memory map with address and size + char *dt_mem_rsvmap_ptr = (char *)((char *)header + uint32_endian_big2little(header->off_mem_rsvmap)); + struct fdt_reserve_entry *reverse_entry = (struct fdt_reserve_entry *)dt_mem_rsvmap_ptr; + + // reserve memory which is defined by dtb + while (reverse_entry->address != 0 || reverse_entry->size != 0) + { + unsigned long long start = PHYS_TO_KERNEL_VIRT(uint64_endian_big2little(reverse_entry->address)); + unsigned long long end = uint64_endian_big2little(reverse_entry->size) + start; + memory_reserve(start, end); + reverse_entry++; + } + + // reserve device tree itself + memory_reserve((unsigned long long)dtb_ptr, (unsigned long long)dtb_ptr + uint32_endian_big2little(header->totalsize)); +} \ No newline at end of file diff --git a/lab6/kernel/src/entry.S b/lab6/kernel/src/entry.S new file mode 100644 index 000000000..284a07e73 --- /dev/null +++ b/lab6/kernel/src/entry.S @@ -0,0 +1,195 @@ +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp ,16 * 0] // store pair of registers + 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] + // information 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] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + // information 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 instead of elr_el1 + add sp, sp, 32 * 9 +.endm + +.macro ventry label + .align 7 // entry should be aligned to 0x80 (2^7) + b \label +.endm + +.align 11 // vector table should be aligned to 0x800 (2^11) +.global exception_vector_table + +// exception_vector_table definition +// https://github.com/torvalds/linux/blob/v4.14/arch/arm64/kernel/entry.S#L367 +exception_vector_table: + //Exception from the current EL while using SP_EL0 + ventry el1t_sync_invalid // Synchronous EL1t + ventry el1t_irq_invalid // IRQ EL1t + ventry el1t_fiq_invalid // FIQ EL1t + ventry el1t_error_invalid // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry el1h_sync // Synchronous EL1h + ventry el1h_irq // IRQ EL1h + ventry el1h_fiq_invalid // FIQ EL1h + ventry el1h_error_invalid // Error EL1h + + //Exception from a lower EL and at least one lower EL is AArch64 + ventry el0_sync_64 // Synchronous 64-bit EL0 + ventry el0_irq_64 // IRQ 64-bit EL0 + ventry el0_fiq_invalid_64 // FIQ 64-bit EL0 + ventry el0_error_invalid_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry el0_sync_invalid_32 // Synchronous 32-bit EL0 + ventry el0_irq_invalid_32 // IRQ 32-bit EL0 + ventry el0_fiq_invalid_32 // FIQ 32-bit EL0 + ventry el0_error_invalid_32 // Error 32-bit EL0 + + +el1t_sync_invalid: + save_all + mov x0,0 + bl invalid_exception_router + load_all + eret +el1t_irq_invalid: + save_all + mov x0,1 + bl invalid_exception_router + load_all + eret +el1t_fiq_invalid: + save_all + mov x0,2 + bl invalid_exception_router + load_all + eret +el1t_error_invalid: + save_all + mov x0,3 + bl invalid_exception_router + load_all + eret + + +el1h_sync: + save_all + mov x0, sp + bl el1h_sync_router + load_all + eret +el1h_irq: + save_all + mov x0, sp + bl el1h_irq_router + load_all + eret +el1h_fiq_invalid: + save_all + mov x0,6 + bl invalid_exception_router + load_all + eret +el1h_error_invalid: + save_all + mov x0,7 + bl invalid_exception_router + load_all + eret + +el0_sync_64: + save_all + mov x0, sp + bl el0_sync_router + load_all + eret +el0_irq_64: + save_all + mov x0, sp + bl el0_irq_router // ----------------------------------- + load_all + eret +el0_fiq_invalid_64: + save_all + mov x0,10 + bl invalid_exception_router + load_all + eret +el0_error_invalid_64: + save_all + mov x0,11 + bl invalid_exception_router + load_all + eret + + +el0_sync_invalid_32: + save_all + mov x0,12 + bl invalid_exception_router + load_all + eret +el0_irq_invalid_32: + save_all + mov x0,13 + bl invalid_exception_router + load_all + eret +el0_fiq_invalid_32: + save_all + mov x0,14 + bl invalid_exception_router + load_all + eret +el0_error_invalid_32: + save_all + mov x0,15 + bl invalid_exception_router + load_all + eret diff --git a/lab6/kernel/src/exception.c b/lab6/kernel/src/exception.c new file mode 100644 index 000000000..3fabcd772 --- /dev/null +++ b/lab6/kernel/src/exception.c @@ -0,0 +1,367 @@ +#include "bcm2837/rpi_irq.h" +#include "bcm2837/rpi_uart1.h" +#include "uart1.h" +#include "exception.h" +#include "timer.h" +#include "syscall.h" +#include "signal.h" +#include "sched.h" +#include "memory.h" +#include "irqtask.h" +#include "mmu.h" +#include "debug.h" + +// 讀取ESR_EL1暫存器的值 +static inline unsigned long read_esr_el1(void) +{ + unsigned long value; + asm volatile("mrs %0, esr_el1" : "=r"(value)); + return value; +} + +// 判斷異常是否由EL0觸發的syscall +static inline int is_el0_syscall() +{ + unsigned long esr_el1 = read_esr_el1(); + unsigned long ec = (esr_el1 >> ESR_EL1_EC_SHIFT) & ESR_EL1_EC_MASK; + if (ec == ESR_EL1_EC_SVC64) + { + return 1; + } + return 0; +} + +// 定義異常類型名稱 +const char *exception_type[] = { + "Unknown reason", + "Trapped WFI or WFE instruction execution", + "Trapped MCR or MRC access with (coproc==0b1111) (AArch32)", + "Trapped MCRR or MRRC access with (coproc==0b1111) (AArch32)", + "Trapped MCR or MRC access with (coproc==0b1110) (AArch32)", + "Trapped LDC or STC access (AArch32)", + "Trapped FP access", + "Trapped VMRS access", + "Trapped PSTATE (AArch32)", + "Instruction Abort from a lower Exception level", + "Instruction Abort taken without a change in Exception level", + "PC alignment fault", + "Data Abort from a lower Exception level", + "Data Abort taken without a change in Exception level", + "SP alignment fault", + "Trapped floating-point exception", + "SError interrupt", + "Breakpoint from a lower Exception level", + "Breakpoint taken without a change in Exception level", + "Software Step from a lower Exception level", + "Software Step taken without a change in Exception level", + "Watchpoint from a lower Exception level", + "Watchpoint taken without a change in Exception level", + "BKPT instruction execution (AArch32)", + "Vector Catch exception (AArch32)", + "BRK instruction execution (AArch64)"}; + +extern list_head_t *run_queue; + +// DAIF, Interrupt Mask Bits +void el1_interrupt_enable() +{ + __asm__ __volatile__("msr daifclr, 0xf"); // umask all DAIF +} + +void el1_interrupt_disable() +{ + __asm__ __volatile__("msr daifset, 0xf"); // mask all DAIF +} + +unsigned long long int lock_counter = 0; + +void lock() +{ + el1_interrupt_disable(); + lock_counter++; +} + +void unlock() +{ + // uart_sendlinek("This is unlock\n"); + lock_counter--; + if (lock_counter < 0) + { + while (1) + ; + } + else if (lock_counter == 0) + { + el1_interrupt_enable(); + } +} + +void el1h_sync_router(trapframe_t *tpf) +{ + uart_sendlinek("\n"); + uart_sendlinek("spsr_el1 : %x\n ", tpf->spsr_el1); + uart_sendlinek("elr_el1 : %x\n ", tpf->elr_el1); + uart_sendlinek("sp_el0 : %x\n ", tpf->sp_el0); + //dump_vma(); + while (1) + ; +} + +void el1h_irq_router(trapframe_t *tpf) +{ + lock(); + // decouple the handler into irqtask queue + // (1) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.113 + // (2) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.16 + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + if (*AUX_MU_IER_REG & 2) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); // run the queued task before returning to the program. + } + else if (*AUX_MU_IER_REG & 1) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) // from CNTPNS (core_timer) // A1 - setTimeout run in el1 + { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + core_timer_enable(); + + if (list_size(run_queue) > 1) + { + // uart_sendlinek("el1h_irq_router\n"); + schedule(); + } + } + else + { + unlock(); + uart_sendlinek("Hello World el1 64 router other interrupt!\r\n"); + } + + // only do signal handler when return to user mode + if ((tpf->spsr_el1 & 0b1100) == 0) + { + check_signal(tpf); + } +} + +void el0_sync_router(trapframe_t *tpf) +{ + static int count = 0; + unsigned long long esr_el1 = read_esr_el1(); + + // esr_el1: Holds syndrome information for an exception taken to EL1. + esr_el1_t *esr = (esr_el1_t *)&esr_el1; + if (esr->ec == MEMFAIL_DATA_ABORT_LOWER || esr->ec == MEMFAIL_INST_ABORT_LOWER) + { + mmu_memfail_abort_handle(esr); + return; + } + + if (!is_el0_syscall()) + { + const char *exception_name = get_exception_name(esr_el1); + if (count == 0) + ERROR("el0_sync_router: exception occurred - %s\r\n", exception_name); + count++; + return; + } + + el1_interrupt_enable(); // Allow UART input during exception + unsigned long long syscall_no = tpf->x8; + if (syscall_no > 2) + { + uart_sendlinek("SYSCALL No: %d\n", syscall_no); + } + // uart_sendlinek("tpf->x8 : %d\n",tpf->x8); + switch (syscall_no) + { + SYSCALL(0, getpid(tpf)) + SYSCALL(1, uartread(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(2, uartwrite(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(3, exec(tpf, (char *)tpf->x0, (char **)tpf->x1)) + SYSCALL(4, fork(tpf)) + SYSCALL(5, exit(tpf, tpf->x0)) + SYSCALL(6, syscall_mbox_call(tpf, (unsigned char)tpf->x0, (unsigned int *)tpf->x1)) + SYSCALL(7, kill(tpf, (int)tpf->x0)) + SYSCALL(8, signal_register(tpf->x0, (void (*)())tpf->x1)) + SYSCALL(9, signal_kill(tpf->x0, tpf->x1)) + SYSCALL(10, mmap(tpf,(void *)tpf->x0,tpf->x1,tpf->x2,tpf->x3,tpf->x4,tpf->x5);) + SYSCALL(50, sigreturn(tpf)) + SYSCALL(114, syscall_lock(tpf)) + SYSCALL(514, syscall_unlock(tpf)) + default: + uart_sendlinek("el0_sync_router other syscall, syscall_no: %d\r\n", syscall_no); + break; + } +} + +void el0_irq_router(trapframe_t *tpf) +{ + lock(); + // decouple the handler into irqtask queue + // (1) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.113 + // (2) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.16 + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + if (*AUX_MU_IER_REG & 2) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); // run the queued task before returning to the program. + } + else if (*AUX_MU_IER_REG & 1) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) // from CNTPNS (core_timer) // A1 - setTimeout run in el1 + { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + core_timer_enable(); + + if (list_size(run_queue) > 1) + { + // uart_sendlinek("el0_irq_router\n"); + schedule(); + } + } + else + { + unlock(); + uart_sendlinek("Hello World el0 64 router other interrupt!\r\n"); + } + + // 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) +{ + uart_sendlinek("\n invalid exception %x \n", x0); + while (1) + ; +} + +// 獲取異常類型名稱 +const char *get_exception_name(unsigned long esr_el1) +{ + unsigned long ec = (esr_el1 >> ESR_EL1_EC_SHIFT) & ESR_EL1_EC_MASK; + if (ec < sizeof(exception_type) / sizeof(exception_type[0])) + { + return exception_type[ec]; + } + return "Unknown exception"; +} + +// ------------------------------------------------------------------------------------------ + +/* +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; // Small number has higher priority + +struct list_head *task_list; +void irqtask_list_init() +{ + task_list = allocator(sizeof(list_head_t)); + INIT_LIST_HEAD(task_list); +} + +void irqtask_add(void *task_function, unsigned long long priority) +{ + irqtask_t *the_task = kmalloc(sizeof(irqtask_t)); // free by irq_tasl_run_preemptive() + + // store all the related information into irqtask node + // manually copy the device's buffer + the_task->priority = priority; + the_task->task_function = task_function; + INIT_LIST_HEAD(&(the_task->listhead)); + + // add the timer_event into timer_event_list (sorted) + // if the priorities are the same -> FIFO + struct list_head *curr; + + // mask the device's interrupt line + // el1_interrupt_disable(); + // enqueue the processing task to the event queue with sorting. + list_for_each(curr, task_list) + { + if (((irqtask_t *)curr)->priority > the_task->priority) + { + list_add(&(the_task->listhead), curr->prev); + break; + } + } + // if the priority is lowest + if (list_is_head(curr, task_list)) + { + list_add_tail(&(the_task->listhead), task_list); + } + // unmask the interrupt line + // el1_interrupt_enable(); +} + +void irqtask_run_preemptive() +{ + // el1_interrupt_enable(); + while (!list_empty(task_list)) + { + // critical section protects new coming node + + lock(); + irqtask_t *the_task = (irqtask_t *)task_list->next; + // Run new task (early return) if its priority is lower than the scheduled task. + if (curr_task_priority <= the_task->priority) + { + unlock(); + break; + } + // get the scheduled task and run it. + list_del_entry((struct list_head *)the_task); + int prev_task_priority = curr_task_priority; + curr_task_priority = the_task->priority; + // uart_sendlinek("preemptive curr_task_priority: %d\r\n", curr_task_priority); + unlock(); + + irqtask_run(the_task); + + lock(); + curr_task_priority = prev_task_priority; + unlock(); + // free(the_task); + } +} + +void irqtask_run(irqtask_t *the_task) +{ + ((void (*)())the_task->task_function)(); + kfree(the_task); +} diff --git a/lab6/kernel/src/linker.ld b/lab6/kernel/src/linker.ld new file mode 100644 index 000000000..45b64f607 --- /dev/null +++ b/lab6/kernel/src/linker.ld @@ -0,0 +1,44 @@ +_heap_stack_size = 50M; + +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + _kernel_start = .; + + .text : { + *(.text.boot) + *(.text) + } + + .rodata : { + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + . = ALIGN(8); + _bss_start = .; + *(.bss) + *(COMMON) + } + _bss_end = .; + _bss_size = (_bss_end - _bss_start)>>3; + + .heap : { + . = ALIGN(8); + _heap_top = .; + . += _heap_stack_size; + _heap_end = .; + } + + _kernel_end = .; + + . = 0xffff00002c000000; + _stack_end = .; + . = 0xffff00003c000000; + _stack_top = .; +} \ No newline at end of file diff --git a/lab6/kernel/src/main.c b/lab6/kernel/src/main.c new file mode 100644 index 000000000..427fa1eab --- /dev/null +++ b/lab6/kernel/src/main.c @@ -0,0 +1,43 @@ +#include "uart1.h" +#include "shell.h" +#include "memory.h" +#include "dtb.h" +#include "exception.h" +#include "timer.h" +#include "sched.h" +#include "uart1.h" +#include "irqtask.h" +//#include "bcm2837/rpi_mmu.h" + +extern list_head_t *run_queue; +extern char *dtb_ptr; +int Set_dtb = 0; + +void main(char *arg) +{ + // uart_sendlinek("in main\n"); + dtb_ptr = (char *)PHYS_TO_KERNEL_VIRT(arg); + if (!Set_dtb) + { + traverse_device_tree(dtb_ptr, dtb_callback_initramfs); + Set_dtb = 1; + } + uart_init(); + allocator_init(); + irqtask_list_init(); + timer_list_init(); + thread_sched_init(); + //uart_sendlinek("in main\n"); + + core_timer_enable(); + uart_interrupt_enable(); + uart_flush_FIFO(); + + el1_interrupt_enable(); // enable interrupt in EL1 -> EL1 + + //start_shell(); + // uart_sendlinek("RQ size : %d \n",list_size(run_queue)); + + //schedule(); + load_context(&((((thread_t*)run_queue->next))->context)); +} diff --git a/lab6/kernel/src/mbox.c b/lab6/kernel/src/mbox.c new file mode 100644 index 000000000..e59dee6ee --- /dev/null +++ b/lab6/kernel/src/mbox.c @@ -0,0 +1,23 @@ +#include "bcm2837/rpi_mbox.h" +#include "mbox.h" + +/* Aligned to 16-byte boundary while we have 28-bits for VC */ +volatile unsigned int __attribute__((aligned(16))) pt[64]; + +int mbox_call( mbox_channel_type channel, unsigned int value ) +{ + // Add channel to lower 4 bit + value &= ~(0xF); + value |= channel; + while ( (*MBOX_STATUS & BCM_ARM_VC_MS_FULL) != 0 ) {} + // Write to Register + *MBOX_WRITE = value; + while(1) { + while ( *MBOX_STATUS & BCM_ARM_VC_MS_EMPTY ) {} + // Read from Register + if (value == *MBOX_READ) + return pt[1] == MBOX_REQUEST_SUCCEED; + } + return 0; +} + diff --git a/lab6/kernel/src/memory.c b/lab6/kernel/src/memory.c new file mode 100644 index 000000000..a80a1219a --- /dev/null +++ b/lab6/kernel/src/memory.c @@ -0,0 +1,491 @@ +#include "memory.h" +#include "u_list.h" +#include "uart1.h" +#include "exception.h" +#include "dtb.h" +#include "stdio.h" +#include "mmu.h" +#include "bcm2837/rpi_mmu.h" + +extern char _heap_top; +static char *htop_ptr = &_heap_top; + +extern char _kernel_start; +extern char _kernel_end; +extern char *CPIO_DEFAULT_START; +extern char *CPIO_DEFAULT_END; +extern char _stack_end; +extern char _stack_top; + +void *allocator(unsigned int size) +{ + lock(); + // -> htop_ptr + // htop_ptr + 0x02: heap_block size + // htop_ptr + 0x10 ~ htop_ptr + 0x10 * k: + // { heap_block } + // -> htop_ptr + // header 0x10 bytes block + // |--------------------------------------------------------------| + // | fill zero 0x8 bytes | size 0x8 bytes | size padding to 0x16 | + // |--------------------------------------------------------------| + + // 0x10 for heap_block header + char *r = htop_ptr + 0x10; + // size paddling to multiple of 0x10 + size = 0x10 + size - size % 0x10; + *(unsigned int *)(r - 0x8) = size; + htop_ptr += size; + unlock(); + return r; +} + +// void free(void* ptr) { +// // TBD +// } + +// ------------------------------------------------------------ + +static frame_t *frame_array; // store memory's statement and page's corresponding index +static list_head_t frame_freelist[FRAME_MAX_IDX]; // store available block for page +static list_head_t cache_list[CACHE_MAX_IDX]; // store available block for cache + +void allocator_init() +{ + frame_array = allocator(BUDDY_MEMORY_PAGE_COUNT * sizeof(frame_t)); + + // init frame freelist + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&frame_freelist[i]); + } + + // init cache list + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&cache_list[i]); + } + + for (int i = 0; i < BUDDY_MEMORY_PAGE_COUNT; i++) + { + // init listhead for each frame + INIT_LIST_HEAD(&(frame_array[i].listhead)); + frame_array[i].idx = i; + } + freelist_init(); + dump_page_info(); + + /* Startup reserving the following region: + Spin tables for multicore boot (0x0000 - 0x1000) + Devicetree (Optional, if you have implement it) + Kernel image in the physical memory + Your simple allocator (startup allocator) (Stack + Heap in my case) + Initramfs + */ + uart_sendlinek("\r\n* Startup Allocation *\r\n"); + uart_sendlinek("buddy system: usable memory region: 0x%x ~ 0x%x\n", BUDDY_MEMORY_BASE, BUDDY_MEMORY_BASE + BUDDY_MEMORY_PAGE_COUNT * PAGESIZE); + dtb_find_and_store_reserved_memory(); // find spin tables in dtb + memory_reserve(PHYS_TO_KERNEL_VIRT(MMU_PGD_ADDR), PHYS_TO_KERNEL_VIRT(MMU_PTE_ADDR + 0x2000)); // // PGD's page frame at 0x1000 // PUD's page frame at 0x2000 PMD 0x3000-0x5000 + memory_reserve((unsigned long long)&_kernel_start, (unsigned long long)&_kernel_end); // kernel + memory_reserve((unsigned long long)&_stack_end, (unsigned long long)&_stack_top); + memory_reserve((unsigned long long)CPIO_DEFAULT_START, (unsigned long long)CPIO_DEFAULT_END); +} + +void freelist_init() +{ + int PAGE_COUNT = BUDDY_MEMORY_PAGE_COUNT; + for (int i = 0; i <= FRAME_IDX_FINAL; i++) + { + // uart_sendlinek("\n %ld,%ld \n",PAGE_COUNT,(1 << i)); + if ((PAGE_COUNT & (1 << i))) + { + PAGE_COUNT -= (1 << i); + frame_array[PAGE_COUNT].val = i; + frame_array[PAGE_COUNT].used = FRAME_FREE; + list_add(&(frame_array[PAGE_COUNT].listhead), &frame_freelist[i]); + } + if (i == FRAME_IDX_FINAL && PAGE_COUNT) + { + while (PAGE_COUNT) + { + PAGE_COUNT -= (1 << i); + frame_array[PAGE_COUNT].val = FRAME_IDX_FINAL; + frame_array[PAGE_COUNT].used = FRAME_FREE; + list_add(&(frame_array[PAGE_COUNT].listhead), &frame_freelist[FRAME_IDX_FINAL]); + } + } + } +} + +frame_t *release_redundant(frame_t *frame) +{ + // order -1 -> add its buddy to free list (frame itself will be used in master function) + frame->val -= 1; + frame_t *buddyptr = get_buddy(frame); + buddyptr->val = frame->val; + buddyptr->used = FRAME_FREE; + list_add(&(buddyptr->listhead), &frame_freelist[buddyptr->val]); + return frame; +} + +frame_t *get_buddy(frame_t *frame) +{ + // XOR(idx, order) + if ((frame->idx ^ (1 << frame->val)) > BUDDY_MEMORY_PAGE_COUNT) + { + uart_sendlinek("[!] BUDDY of Page: 0x%x at level: %d Does not exit", frame->idx, frame->val); + // return -1; + } + return &frame_array[frame->idx ^ (1 << frame->val)]; +} + +void dump_page_info() +{ + // unsigned int exp2 = 1; + // uart_sendlinek(" ┌───────────────────── [ Number of Available Page Blocks ] ─────────────────────┐\r\n │ "); + // for (int i = FRAME_IDX_0; i < FRAME_IDX_8; i++) + // { + // uart_sendlinek("%4dKB(%1d) ", 4 * exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │ "); + // for (int i = FRAME_IDX_0; i < FRAME_IDX_8; i++) + // uart_sendlinek(" %4d ", list_size(&frame_freelist[i])); + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└────────────────────────────────────────"); + // uart_sendlinek("─────────────────────────────────────────┘\r\n"); + + // exp2 = 1; + // uart_sendlinek(" ┌──────────────────────────────────── "); + // uart_sendlinek("[ Number of Available Page Blocks ]"); + // uart_sendlinek(" ───────────────────────────────────┐\r\n │"); + // for (int i = FRAME_IDX_8; i < FRAME_MAX_IDX; i++) + // { + // uart_sendlinek("%4dMB(%2d) ", exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │"); + // for (int i = FRAME_IDX_8; i < FRAME_MAX_IDX; i++) + // uart_sendlinek(" %4d ", list_size(&frame_freelist[i])); + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└──────────────────────────────────────────────────────"); + // uart_sendlinek("────────────────────────────────────────────────────────┘\r\n"); +} + +void dump_cache_info() +{ + // unsigned int exp2 = 1; + // uart_sendlinek(" ┌──────────────── [ Number of Available Cache Blocks ] ────────────────┐\r\n │ "); + // for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + // { + // uart_sendlinek("%4dB(%1d) ", CACHE_SEG * exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │ "); + // ; + // for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + // { + // if (!list_empty(&cache_list[i])) + // { + // // uart_sendlinek("I free !!!!!!!!!!!!"); + // int num = 0; + // list_head_t *pos; + // list_for_each(pos, &cache_list[i]) + // { + // num += ((cache_t *)pos)->available; + // } + // uart_sendlinek("%5d ", num); + // } + // else + // { + // uart_sendlinek("%5d ", 0); + // } + // } + + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└───────────────────────────────────"); + // uart_sendlinek("────────────────────────────────────┘\r\n"); +} + +void memory_reserve(unsigned long long start, unsigned long long end) +{ + start -= start % PAGESIZE; // floor (align 0x1000) + end = end % PAGESIZE ? end + PAGESIZE - (end % PAGESIZE) : end; // ceiling (align 0x1000) + + // uart_sendlinek("Reserved Memory: "); + // uart_sendlinek("start 0x%x ~ ", start); + // uart_sendlinek("end 0x%x\r\n", end); + + // delete page from free list + for (int order = FRAME_IDX_FINAL; order >= 0; order--) + { + list_head_t *pos; + // uart_sendlinek("\n use %d level page to match \n",order); + list_for_each(pos, &frame_freelist[order]) + { + unsigned long long pagestart = ((frame_t *)pos)->idx * PAGESIZE + BUDDY_MEMORY_BASE; + unsigned long long pageend = pagestart + (PAGESIZE << order); + // uart_sendlinek("\n from 0x%x to 0x%x\n",pagestart,pageend); + + if (start <= pagestart && end >= pageend) // if page all in reserved memory -> delete it from freelist + { + ((frame_t *)pos)->used = FRAME_ALLOCATED; + // uart_sendlinek(" [!] Reserved page in 0x%x - 0x%x\n", pagestart, pageend); + // uart_sendlinek(" Before\n"); + // dump_page_info(); + list_del_entry(pos); + // uart_sendlinek(" Remove usable block for reserved memory: order %d\r\n", order); + // uart_sendlinek(" After\n"); + // dump_page_info(); + } + else if (start >= pageend || end <= pagestart) // no intersection + { + continue; + } + else // partial intersection, separate the page into smaller size. + { + // dump_page_info(); + list_del_entry(pos); + list_head_t *temppos = pos->prev; + list_add(&release_redundant((frame_t *)pos)->listhead, &frame_freelist[order - 1]); + pos = temppos; + // dump_page_info(); + } + } + } +} + +void *kmalloc(unsigned int size) +{ + lock(); + + // uart_sendlinek("\n\n"); + // uart_sendlinek("================================\r\n"); + // uart_sendlinek("[+] Request kmalloc size: %d\r\n", size); + // uart_sendlinek("================================\r\n"); + // if size is larger than cache size, go for page + if (size > (CACHE_SEG << CACHE_IDX_FINAL)) + { + void *r = page_malloc(size); + unlock(); + return r; + } + // go for cache + void *r = cache_malloc(size); + unlock(); + return r; +}; + +void *page_malloc(unsigned int size) +{ + // uart_sendlinek("this is page_malloc \r\n"); + int val; + int find_PageSize = 0; + frame_t *target_frame_ptr; + // turn size into minimum 4KB * 2**val + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + + if (size <= (PAGESIZE << i) && !find_PageSize) + { + val = i; + // uart_sendlinek(" block size = 0x%x\n", PAGESIZE << i); + find_PageSize = 1; + } + + if (find_PageSize && !list_empty(&frame_freelist[i])) + { + // uart_sendlinek(" free page at level : %d\n", i); + target_frame_ptr = (frame_t *)(frame_freelist[i].next); + for (int min_FreePageLevel = i; min_FreePageLevel > val; min_FreePageLevel--) // ex: 10000 -> 01111 + { + target_frame_ptr = release_redundant(target_frame_ptr); + } + break; + } + + if (i == FRAME_IDX_FINAL) + { + // uart_sendlinek("[!] request size exceeded for page_malloc!!!!\r\n"); + return (void *)0; + } + } + // get the available frame from freelist + target_frame_ptr->used = FRAME_ALLOCATED; + + // uart_sendlinek(" [+] Allocate page - size : %d(0x%x)\r\n", size, size); + // uart_sendlinek(" Before\r\n"); + // dump_page_info(); + list_del_entry((struct list_head *)target_frame_ptr); + // uart_sendlinek(" physical address : 0x%x\n", BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx))); + // uart_sendlinek(" After\r\n"); + // dump_page_info(); + + return (void *)BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx)); +}; + +void *cache_malloc(unsigned int size) +{ + int c_val; + // uart_sendlinek("this is cache_malloc \r\n"); + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + if (size <= (CACHE_SEG << i)) + { + c_val = i; + break; + } + } + + if (list_empty(&cache_list[c_val])) + { + // uart_sendlinek("[!] No free size for cache \r\n"); //--------------------------------------------------- + page2caches(c_val); + } + + // cache_t *ptr = (cache_t *)(cache_list[c_val].prev); + + // uart_sendlinek("this is cache_malloc \r\n"); + // uart_sendlinek("[+] Allocate cache - size : %d(0x%x)\r\n", size, size); + // uart_sendlinek(" Before\r\n"); + // dump_cache_info(); + + void *ptr = find_CACHE((cache_t *)cache_list[c_val].next); + // uart_sendlinek("ptr : %x\n",ptr); + // uart_sendlinek(" After\r\n"); + // dump_cache_info(); + + return ptr; +}; + +void *find_CACHE(cache_t *ptr) +{ + // uart_sendlinek("ptr : %x\n",ptr); + unsigned long long num; + int record_num; + for (record_num = 0; ptr->cache_record[record_num] + 1 == 0 && record_num < CACHE_record_num; record_num++) + ; + + num = ptr->cache_record[record_num] + 1; + num = ((~num) & (ptr->cache_record[record_num])) + 1; + + // uart_sendlinek("before cache_record[%d] : %x\n", record_num, ptr->cache_record[record_num]); + ptr->cache_record[record_num] += num; + ptr->available--; + // uart_sendlinek("after cache_record[%d] : %x\n", record_num, ptr->cache_record[record_num]); + + int val = fake_log2(num) + record_num * 64; + void *target = ptr->data_base + val * (CACHE_SEG << (ptr->cache_order)); + // uart_sendlinek("data_base addr : 0x%x\n", ptr->data_base); + // uart_sendlinek("target addr : 0x%x\n", target); + + if (!(ptr->available)) + { + list_del_entry((list_head_t *)ptr); + } + + return target; +} + +void page2caches(int c_val) +{ + // uart_sendlinek("[!] Split Page for cache\r\n"); + void *ptr = page_malloc(PAGESIZE); + // uart_sendlinek("ptr : %x\n",ptr); + cache_t *Pageinfo = (cache_t *)ptr; + Pageinfo->data_base = ptr; + Pageinfo->cache_order = c_val; + Pageinfo->max_available = (PAGESIZE >> (CACHE_offset + c_val)); + + while (sizeof(*Pageinfo) >= Pageinfo->data_base - ptr) + { + Pageinfo->data_base += (CACHE_SEG << c_val); + (Pageinfo->max_available)--; + } + Pageinfo->available = Pageinfo->max_available; + + for (int i = 0; i < CACHE_record_num; i++) + Pageinfo->cache_record[i] = 0; + + // uart_sendlinek(" max_available : %d\n", Pageinfo->max_available); + // uart_sendlinek(" Pageinfo size : 0x%x\n", sizeof(*Pageinfo)); + // uart_sendlinek(" offset size : 0x%x\n", (CACHE_SEG << c_val)); + + list_add(&(Pageinfo->listhead), &cache_list[c_val]); + // return Pageinfo; +} + +//------------------------------------------------------------------------------------------------------------------------------- kfree + +void kfree(void *ptr) +{ + // uart_sendlinek("\r\n"); + // uart_sendlinek("==========================\r\n"); + //uart_sendlinek("[+] Request kfree 0x%x\r\n", ptr); + // uart_sendlinek("==========================\r\n"); + + // If no cache assigned, go for page + lock(); + frame_t *target_frame_ptr = &frame_array[((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12]; + if ((unsigned long long)ptr % PAGESIZE == 0) + { + page_free(target_frame_ptr); + unlock(); + return; + } + // go for cache + cache_free(ptr); + unlock(); +}; + +void page_free(frame_t *target_frame_ptr) +{ + frame_t *buddyptr = get_buddy(target_frame_ptr); + target_frame_ptr->used = FRAME_FREE; + while (buddyptr->used == FRAME_FREE && buddyptr->val == target_frame_ptr->val) + { + list_del_entry((list_head_t *)buddyptr); + target_frame_ptr = &frame_array[(target_frame_ptr->idx) & (buddyptr->idx)]; + target_frame_ptr->val++; + buddyptr = get_buddy(target_frame_ptr); + if (buddyptr < 0) + break; + } + + // uart_sendlinek(" Before\r\n"); + // dump_page_info(); + list_add(&(target_frame_ptr->listhead), &frame_freelist[target_frame_ptr->val]); + // uart_sendlinek(" After\r\n"); + // dump_page_info(); +} + +void cache_free(void *ptr) +{ + // uart_sendlinek("ptr : %x\n",ptr); + int idx = ((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12; + frame_t *target_frame_ptr = &frame_array[idx]; + cache_t *cache_ptr = (cache_t *)((unsigned long long)(idx * PAGESIZE) + BUDDY_MEMORY_BASE); //<------- + // uart_sendlinek("target_frame_ptr : %x\n",target_frame_ptr); + //uart_sendlinek("cache_ptr : %x\n",cache_ptr); + int num = (ptr - cache_ptr->data_base) / (CACHE_SEG << cache_ptr->cache_order); + //uart_sendlinek("num : %d\n",num); + int record_num = (num >> 6); + num = num - (record_num << 6); + cache_ptr->cache_record[record_num] -= (1 << num); + + // uart_sendlinek("[+] Free cache: 0x%x, val = %d\r\n", ptr, cache_ptr->cache_order); + // uart_sendlinek(" Before\r\n"); + // dump_cache_info(); + if (cache_ptr->available == 0) + { + list_add(&(cache_ptr->listhead), &cache_list[cache_ptr->cache_order]); + } + cache_ptr->available++; + if (cache_ptr->available == cache_ptr->max_available) + { + list_del_entry((list_head_t *)cache_ptr); + page_free(target_frame_ptr); + } + // uart_sendlinek(" After\r\n"); + // dump_cache_info(); +} \ No newline at end of file diff --git a/lab6/kernel/src/mmu.c b/lab6/kernel/src/mmu.c new file mode 100644 index 000000000..769948ae6 --- /dev/null +++ b/lab6/kernel/src/mmu.c @@ -0,0 +1,376 @@ +#include "bcm2837/rpi_mmu.h" +#include "mmu.h" +#include "memory.h" +#include "string.h" +#include "uart1.h" +#include "debug.h" + +extern thread_t *curr_thread; + +void *set_2M_kernel_mmu(void *x0) +{ + // Turn + // Two-level Translation (1GB) - in boot.S + // to + // Three-level Translation (2MB) - set PUD point to new table + unsigned long *pud_table = (unsigned long *)MMU_PUD_ADDR; + + unsigned long *pte_table1 = (unsigned long *)MMU_PTE_ADDR; + unsigned long *pte_table2 = (unsigned long *)(MMU_PTE_ADDR + 0x1000L); + for (int i = 0; i < 512; i++) + { + unsigned long addr = 0x200000L * i; + if (addr >= PERIPHERAL_END) + { + pte_table1[i] = (0x00000000 + addr) + BOOT_PTE_ATTR_nGnRnE; + continue; + } + pte_table1[i] = (0x00000000 + addr) | BOOT_PTE_ATTR_NOCACHE; // 0 * 2MB // No definition for 3-level attribute, use nocache. + pte_table2[i] = (0x40000000 + addr) | BOOT_PTE_ATTR_NOCACHE; // 512 * 2MB + } + + // set PUD + pud_table[0] = (unsigned long)pte_table1 | BOOT_PUD_ATTR; + pud_table[1] = (unsigned long)pte_table2 | BOOT_PUD_ATTR; + + return x0; +} + +void map_one_page(size_t *virt_pgd_p, size_t user_va, size_t pa, size_t flag) +{ + size_t *table_p = virt_pgd_p; + for (int level = 0; level < 4; level++) + { + unsigned int idx = (user_va >> (39 - level * 9)) & 0x1ff; // p.14, 9-bit only + + if (level == 3) + { + table_p[idx] = pa; + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_KNX | flag; // el0 only + return; + } + + if (!table_p[idx]) + { + size_t *newtable_p = kmalloc(0x1000); // create a table + memset(newtable_p, 0, 0x1000); + table_p[idx] = KERNEL_VIRT_TO_PHYS((size_t)newtable_p); // point to that table + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + table_p = (size_t *)PHYS_TO_KERNEL_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); // PAGE_SIZE + } +} + +void mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced, vma_name_type name) +{ + if (IS_NOT_ALIGN(pa, PAGESIZE) || IS_NOT_ALIGN(va, PAGESIZE)) + { + // ERROR("CHECK_ALIGN : 0x%x\n",CHECK_ALIGN(pa,PAGESIZE)); + uart_sendlinek("\n\n"); + ERROR("Input User Vitural Address or Physical Address Should be Aliged to PAGESIZE\n"); + ERROR("Input User Vitural Address : 0x%x\n", va); + ERROR("Input Physical Address : 0x%x\n", pa); + return; + } + vm_area_struct_t *the_area_ptr = check_vma_overlap(t, va, (unsigned long)size); + if (the_area_ptr != 0) + { + uart_sendlinek("\n\n"); + ERROR("check_vma_overlap : 0x%x\n", the_area_ptr); + ERROR("Vitural Memory Area is Overlap !!\n"); + // dump_vma(); + return; + } + + size = ALIGN_UP(size, PAGESIZE); + 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; + new_area->name = name; + list_add_tail((list_head_t *)new_area, &t->vma_list); +} + +void mmu_del_vma(struct thread *t) +{ + list_head_t *curr = &t->vma_list; + list_head_t *n; + list_for_each_safe(curr, n, &t->vma_list) + { + vm_area_struct_t *vma = (vm_area_struct_t *)curr; + if (vma->is_alloced) + { + kfree((void *)PHYS_TO_KERNEL_VIRT(vma->phys_addr)); + } + list_del_entry(curr); + kfree(curr); + } +} + +void mmu_free_page_tables(size_t *page_table, int level) +{ + size_t *table_virt = (size_t *)PHYS_TO_KERNEL_VIRT(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) == PD_TABLE) + { + if (level < PMD) + mmu_free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + kfree((void *)PHYS_TO_KERNEL_VIRT(next_table)); + } + } + } +} + +// void mmu_set_PTE_readonly(size_t *page_table, int level) +// { +// size_t *table_virt = (size_t *)PHYS_TO_KERNEL_VIRT(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 == PD_TABLE) +// { +// if (level < PMD) +// { +// mmu_set_PTE_readonly(next_table, level + 1); +// } +// else +// { +// table_virt[i] &= PD_RDONLY; +// } +// } +// } +// } +// } + +// mmu_pagetable_copy(size_t *dst_page_table, size_t *src_page_table, int level) +// { +// size_t *dst_page_table_va = (size_t *)PHYS_TO_KERNEL_VIRT(dst_page_table); +// size_t *src_page_table_va = (size_t *)PHYS_TO_KERNEL_VIRT(src_page_table); +// for (int i = 0; i < 512; i++) +// { +// if (src_page_table_va[i] != 0) +// { +// size_t *next_src_table = (size_t)(src_page_table_va[i] & ENTRY_ADDR_MASK); +// size_t *next_dst_table = (size_t)(dst_page_table_va[i] & ENTRY_ADDR_MASK); +// if (src_page_table_va[i] & PD_TABLE == PD_TABLE) +// { +// if (level < PMD) +// { +// mmu_pagetable_copy(next_dst_table,next_src_table,level); +// } +// else +// { +// //-------- +// } +// } +// } +// } +// } + +void mmu_memfail_abort_handle(esr_el1_t *esr_el1) +{ + lock(); + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t" : "=r"(far_el1)); + + list_head_t *pos; + vm_area_struct_t *vma; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + vma = (vm_area_struct_t *)pos; + if (vma->virt_addr <= far_el1 && vma->virt_addr + vma->area_size >= far_el1) + { + the_area_ptr = vma; + break; + } + } + // area is not part of process's address space + if (!the_area_ptr) + { + uart_sendlinek("\n\n"); + ERROR("[Segmentation fault]: Kill Process\r\n"); + ERROR("Invilad Vitural Address Access: %x\n", far_el1); + thread_exit(); + // dump_vma(); + unlock(); + while (1) + schedule(); + + return; + } + + // For translation fault, only map one page frame for the fault address + 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_sendlinek("\n"); + WARING("[Translation fault]: 0x%x\r\n", far_el1); // far_el1: Fault address register. + // Holds the faulting Virtual Address for all synchronous Instruction or Data Abort, PC alignment fault and Watchpoint exceptions that are taken to 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); + addr_offset = ALIGN_DOWN(addr_offset, 0x1000); + + size_t flag = 0; + if (!(the_area_ptr->rwx & (0b1 << 2))) + flag |= PD_UNX; // 4: executable + if (!(the_area_ptr->rwx & (0b1 << 1))) + flag |= PD_RDONLY; // 2: writable + if (the_area_ptr->rwx & (0b1 << 0)) + flag |= PD_UK_ACCESS; // 1: readable / accessible + map_one_page((size_t *)PHYS_TO_KERNEL_VIRT(curr_thread->context.pgd), the_area_ptr->virt_addr + addr_offset, the_area_ptr->phys_addr + addr_offset, flag); + // dump_pagetable(the_area_ptr->virt_addr + addr_offset,the_area_ptr->phys_addr + addr_offset); + } + else + { + // For other Fault (permisson ...etc) + // uart_sendlinek("[Other Fault]: Kill Process\r\n"); + // uart_sendlinek("esr_el1: 0x%x\r\n", esr_el1); + uart_sendlinek("\n\n"); + if ((unsigned long)esr_el1 & (1 << 10)) + { + ERROR("[Permission Fault] due to a write of an Allocation Tag to Canonically Tagged memory.\r\n"); + } + if ((unsigned long)esr_el1 & (1 << 9)) + { + ERROR("[Permission Fault] due to the NoTagAccess memory attribute..\r\n"); + } + // check_permission(,the_area_ptr->rwx); + thread_exit(); + // dump_vma(); + unlock(); + while (1) + schedule(); + } + unlock(); +} + +vm_area_struct_t *check_vma_overlap(thread_t *t, unsigned long user_va, unsigned long size) +{ + list_head_t *pos; + vm_area_struct_t *vma; + list_for_each(pos, &t->vma_list) + { + vma = (vm_area_struct_t *)pos; + // Detect existing vma overlapped + if (!(vma->virt_addr >= (unsigned long)(user_va + size) || vma->virt_addr + vma->area_size <= (unsigned long)user_va)) + { + return vma; + } + } + return 0; +} + +int check_permission(int userId, int VMA_Permission) +{ + switch (PERMISSION_INVAILD(userId, VMA_Permission)) + { + DUMP_NAME(1, "Read is Invaild in This Vitural Memory Area") + DUMP_NAME(2, "Write is Invaild in This Vitural Memory Area") + DUMP_NAME(3, "Write & Read is Invaild in This Vitural Memory Area") + DUMP_NAME(4, "Exec is Invaild in This Vitural Memory Area") + DUMP_NAME(5, "Exec & Read is Invaild in This Vitural Memory Area") + DUMP_NAME(6, "Exec & Write is Invaild in This Vitural Memory Area") + DUMP_NAME(7, "Exec & Write & Read is Invaild in This Vitural Memory Area") + default: + uart_sendlinek("[other Fault]: UNKNOW FAULT"); + break; + } + return PERMISSION_INVAILD(userId, VMA_Permission); +} + +void dump_vma() +{ + uart_sendlinek(" +--------------------------+\n"); + uart_sendlinek(" | DUMP Vitural Memory Area |\n"); + uart_sendlinek(" +--------------------------+\n"); + list_head_t *pos; + list_for_each(pos, &curr_thread->vma_list) + { + uart_sendlinek("===============================================\n"); + vm_area_struct_t *vma_ptr = (vm_area_struct_t *)pos; + + uart_sendlinek("Vitural Memory Area Name : "); + switch (vma_ptr->name) + { + DUMP_NAME(UNKNOW_AREA, "UNKNOW_AREA") + DUMP_NAME(USER_DATA, "USER_DATA") + DUMP_NAME(USER_STACK, "USER_STACK") + DUMP_NAME(PERIPHERAL, "PERIPHERAL") + DUMP_NAME(USER_SIGNAL_WRAPPER, "USER_SIGNAL_WRAPPER") + DUMP_NAME(USER_EXEC_WRAPPER, "USER_EXEC_WRAPPER") + default: + uart_sendlinek("unnamed: %d\n", vma_ptr->name); + break; + } + uart_sendlinek("Base Vitural Address : 0x%x\n", ((vm_area_struct_t *)pos)->virt_addr); + uart_sendlinek("Base Physical Address : 0x%x\n", ((vm_area_struct_t *)pos)->phys_addr); + uart_sendlinek("Area Size : 0x%x\n", ((vm_area_struct_t *)pos)->area_size); + uart_sendlinek("Exec, Write, Read : 0x%x\n", ((vm_area_struct_t *)pos)->rwx); + uart_sendlinek("===============================================\n\n"); + } +} + +void dump_pagetable(unsigned long user_va, unsigned long pa) +{ + uart_sendlinek(" +---------------------------+\n"); + uart_sendlinek(" | DUMP PAGE TABLE & ADDRESS |\n"); + uart_sendlinek(" +---------------------------+\n"); + unsigned long *pagetable_pa = curr_thread->context.pgd; + unsigned long *pagetable_kernel_va = (unsigned long *)PHYS_TO_KERNEL_VIRT(pagetable_pa); + + uart_sendlinek("===============================================\n"); + uart_sendlinek("User Physical Address : 0x%x\n", pa); + uart_sendlinek("User Vitural Address : 0x%x\n", user_va); + unsigned long offset = user_va & 0xFFF; + uart_sendlinek("Vitural Address offset: 0x%x\n", offset); + for (int level = 0; level < 4; level++) + { + uart_sendlinek("-----------------------------------------------\n"); + uart_sendlinek("PAGE TABLE : "); + switch (level) + { + DUMP_NAME(PGD, "PGD") + DUMP_NAME(PUD, "PUD") + DUMP_NAME(PMD, "PMD") + DUMP_NAME(PTE, "PTE") + + default: + uart_sendlinek("unnamed: %d\n", level); + break; + } + uart_sendlinek("Pagetable Physical Address: 0x%x\n", pagetable_pa); + uart_sendlinek("Pagetable Vitural Address: 0x%x\n", pagetable_kernel_va); + + unsigned int idx = (user_va >> (39 - level * 9)) & 0x1FF; + uart_sendlinek("Index of Pagetable: 0x%x\n", idx); + uart_sendlinek("Entry %x of Pagetable: 0x%x\n", idx, pagetable_kernel_va[idx]); + if (level == PTE) + { + pagetable_pa = (unsigned long *)(pagetable_kernel_va[idx] & ENTRY_ADDR_MASK); + uart_sendlinek("The Page Base Physical Address: 0x%x\n", pagetable_pa); + uart_sendlinek("The Physical Address: 0x%x\n", (unsigned long)pagetable_pa | offset); + } + else + { + pagetable_pa = (unsigned long *)(pagetable_kernel_va[idx] & ENTRY_ADDR_MASK); + uart_sendlinek("next Pagetable Physical Address: 0x%x\n", pagetable_pa); + pagetable_kernel_va = (unsigned long *)PHYS_TO_KERNEL_VIRT(pagetable_pa); + uart_sendlinek("next Pagetable Vitural Address: 0x%x\n", pagetable_kernel_va); + } + uart_sendlinek("-----------------------------------------------\n"); + } +} \ No newline at end of file diff --git a/lab6/kernel/src/sched.S b/lab6/kernel/src/sched.S new file mode 100644 index 000000000..7d0f93cd2 --- /dev/null +++ b/lab6/kernel/src/sched.S @@ -0,0 +1,57 @@ +.global switch_to +switch_to: // (prev, next) = (x0, x1) + stp x19, x20, [x0, 16 * 0] // store callee saved register + 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] // store sp, fp, lr + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] // load callee saved register + 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] // load sp, fp, lr + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 // update current thread id + + dsb ish // ensure write has completed, lock for context switch + msr ttbr0_el1, x0 // switch translation based address. + tlbi vmalle1is // invalidate all TLB entries in stage 1, el1 and inner shareable + dsb ish // ensure completion of TLB invalidatation + isb // clear pipeline + + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 // get the current thread id + 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 diff --git a/lab6/kernel/src/sched.c b/lab6/kernel/src/sched.c new file mode 100644 index 000000000..effebb8aa --- /dev/null +++ b/lab6/kernel/src/sched.c @@ -0,0 +1,271 @@ +#include "sched.h" +#include "uart1.h" +#include "exception.h" +#include "memory.h" +#include "timer.h" +#include "shell.h" +#include "signal.h" +#include "stdio.h" +#include "mmu.h" +#include "string.h" + +list_head_t *run_queue; +execfile c_execfile; + +thread_t threads[PIDMAX + 1]; +thread_t *curr_thread; + +int pid_history = 0; +int timer_sched_flag = 0; +int shell_flag = 0; + +void thread_sched_init() +{ + lock(); + // init thread freelist and run_queue + run_queue = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + + // init pids + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].isused = 0; + threads[i].pid = i; + threads[i].iszombie = 0; + } + + thread_t *idlethread = thread_create(start_shell); + curr_thread = idlethread; + asm volatile("msr tpidr_el1, %0" ::"r"(&curr_thread->context)); // Don't let thread structure NULL as we enable the functionality + + // thread_create(idle); + unlock(); +} + +thread_t *thread_create(void *start) +{ + lock(); + thread_t *r; + // find usable PID, don't use the previous one + if (pid_history > PIDMAX) + { + unlock(); + return 0; + } + + if (!threads[pid_history].isused) + { + r = &threads[pid_history]; + pid_history += 1; + } + else + { + unlock(); + return 0; + } + + INIT_LIST_HEAD(&r->vma_list); + r->iszombie = 0; + r->isused = 1; + r->stack_alloced_ptr = kmalloc(USTACK_SIZE); + r->kernel_stack_alloced_ptr = kmalloc(KSTACK_SIZE); + r->context.lr = (unsigned long long)start; + r->context.sp = (unsigned long long)r->kernel_stack_alloced_ptr + KSTACK_SIZE - STACK_BASE_OFFSET; + r->context.fp = r->context.sp; // frame pointer for local variable, which is also in stack. + + // new <--------------------------------------------------------------------------------------------------------------- + // r->data = kmalloc(c_execfile.filesize); + // r->datasize = c_execfile.filesize; + r->context.pgd = kmalloc(0x1000); + // uart_sendlinek("r->context.pgd: %x\n", r->context.pgd); + memset(r->context.pgd, 0, 0x1000); + r->context.pgd = (void *)KERNEL_VIRT_TO_PHYS(r->context.pgd); + // new <--------------------------------------------------------------------------------------------------------------- + + r->signal_is_checking = 0; + // initial all signal handler with signal_default_handler (kill thread) + + // uart_sendlinek("signal_default_handler : 0x%x\n", signal_default_handler); + for (int i = 0; i < SIGNAL_MAX; i++) + { + r->signal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add_tail(&r->listhead, run_queue); + unlock(); + return r; +} + +void schedule() +{ + lock(); + // uart_sendlinek("Run queue size :%d \n", list_size(run_queue)); + do + { + // uart_sendlinek("Run queue size :%d \n", list_size(run_queue)); + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue)); // find a runnable thread + unlock(); + // uart_sendlinek("curr_thread :%d \n",curr_thread->pid); + // uart_sendlinek("curr_thread->context.lr :0x%x \n",curr_thread->context.lr); + switch_to(get_current(), &curr_thread->context); +} + +void idle() +{ + // uart_sendlinek("This is idle\n"); + while (shell_flag > 0) + // while (list_size(run_queue) > 1) + { + // uart_sendlinek("This is idle\n"); + kill_zombies(); // reclaim threads marked as DEAD + schedule(); // switch to next thread in run queue + // delay(10000); + } + kill_zombies(); +} + +void thread_exit() +{ + // thread cannot deallocate the stack while still using it, wait for someone to recycle it. + // In this lab, idle thread handles this task, instead of parent thread. + lock(); + curr_thread->iszombie = 1; + shell_flag--; + unlock(); + // delay(10000); + // schedule(); +} + +void kill_zombies() +{ + lock(); + list_head_t *curr; + thread_t *t; + list_for_each(curr, run_queue) + { + t = (thread_t *)curr; + if (t->iszombie) + { + list_del_entry(curr); + mmu_free_page_tables(t->context.pgd, 0); + mmu_del_vma(t); + kfree(t->kernel_stack_alloced_ptr); + kfree((void*)PHYS_TO_KERNEL_VIRT(t->context.pgd)); + t->iszombie = 0; + t->isused = 0; + } + } + unlock(); +} + +void foo() +{ + // Lab5 Basic 1 Test function + for (int i = 0; i < 10; ++i) + { + uart_sendlinek("Thread id: %d %d\n", curr_thread->pid, i); + int r = 1000000; + while (r--) + { + asm volatile("nop"); + } + schedule(); + } + // uart_sendlinek("exit\n"); + thread_exit(); +} + +int exec_thread() +{ + lock(); + shell_flag++; + thread_t *t = thread_create(exec_proc); + curr_thread = t; + // uart_sendlinek("timer_sched_flag : %d\n", timer_sched_flag); + if (!timer_sched_flag) + { + timer_sched_flag = 1; + add_timer(schedule_timer, 1, "", setSecond); // start scheduler --------------------------------------------------------- + } + + unlock(); + schedule(); + + return 0; +} + +void exec_proc() +{ + //lock(); + el1_interrupt_disable(); // daif 會在 eret 時更改。 + char *data = c_execfile.data; + unsigned int filesize = c_execfile.filesize; + thread_t *t = curr_thread; + // uart_sendlinek("filesize : %d\n", filesize); + t->data = kmalloc(filesize); + t->datasize = filesize; + // t->context.lr = (unsigned long)t->data; // set return address to program if function call completes + + mmu_add_vma(t, USER_DATA_BASE, t->datasize, (size_t)KERNEL_VIRT_TO_PHYS(t->data), 0b111, 1, USER_DATA); + mmu_add_vma(t, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(t->stack_alloced_ptr), 0b111, 1, USER_STACK); + mmu_add_vma(t, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(t, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), PAGESIZE), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(t, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + // t->context.pgd = KERNEL_VIRT_TO_PHYS(t->context.pgd); + t->context.sp = USER_STACK_BASE - STACK_BASE_OFFSET; + t->context.fp = USER_STACK_BASE - STACK_BASE_OFFSET; + t->context.lr = USER_DATA_BASE; + + // copy file into data + for (int i = 0; i < filesize; i++) + { + t->data[i] = data[i]; + } + // dump_vma(); + // eret to exception level 0 + // lock(); + + // asm("dsb ish\n\t" // ensure write has completed + // "msr ttbr0_el1, %0\n\t" + // "tlbi vmalle1is\n\t" // invalidate all TLB entries + // "dsb ish\n\t" // ensure completion of TLB invalidatation + // "isb\n\t" // clear pipeline" + // ::"r"(t->context.pgd)); + + asm("msr tpidr_el1, %0\n\t" // Hold the "kernel(el1)" thread structure information + "msr elr_el1, %1\n\t" // When el0 -> el1, store return address for el1 -> el0 + "msr spsr_el1, xzr\n\t" // EL1h (SPSel = 1) with interrupt disabled + "msr sp_el0, %2\n\t" // el0 stack pointer for el1 process + "mov sp, %3\n\t" // sp is reference for the same el process. For example, el2 cannot use sp_el2, it has to use sp to find its own stack. + "mov x0, %4\n\t" ::"r"(&t->context), + "r"(exec_wrapper), "r"(t->context.sp), "r"(t->kernel_stack_alloced_ptr + KSTACK_SIZE - STACK_BASE_OFFSET), "r"(t->context.lr)); + + //unlock(); + asm("eret\n\t"); +} + +void exec_wrapper() +{ + // unlock + // uart_sendlinek("exec_handler : %x\n", exec_addr); + // uart_sendlinek("curr_thread : %x\n", curr_thread); + // uart_sendlinek(curr_thread->context.lr)); + // asm("mov x8,514\n\t" + // "svc 0\n\t"); + + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void schedule_timer(char *notuse) +{ + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq_el0)); + add_timer(schedule_timer, cntfrq_el0 >> 5, "", setTick); + // schedule(); + // 32 * default timer -> trigger next schedule timer +} diff --git a/lab6/kernel/src/shell.c b/lab6/kernel/src/shell.c new file mode 100644 index 000000000..bd8a3f5b0 --- /dev/null +++ b/lab6/kernel/src/shell.c @@ -0,0 +1,506 @@ +#include "shell.h" +#include "mbox.h" +#include "power.h" +#include "stdio.h" +#include "string.h" +#include "cpio.h" +#include "memory.h" +#include "dtb.h" +#include "timer.h" +#include "sched.h" +#include "signal.h" +#include "syscall.h" + +#include "uart1.h" + +#define USTACK_SIZE 0x10000 + +extern unsigned long long int lock_counter; +extern list_head_t *run_queue; +extern execfile c_execfile; + +struct CLI_CMDS cmd_list[CLI_MAX_CMD] = { + {.command = "cat", .help = "concatenate files and print on the standard output", .func = do_cmd_cat}, + {.command = "dtb", .help = "show device tree", .func = do_cmd_dtb}, + {.command = "hello", .help = "print Hello World!", .func = do_cmd_hello}, + {.command = "help", .help = "print all available commands", .func = do_cmd_help}, + {.command = "info", .help = "get device information via mailbox", .func = do_cmd_info}, + {.command = "ls", .help = "list directory contents", .func = do_cmd_ls}, + {.command = "malloc", .help = "test malloc", .func = do_cmd_malloc}, + {.command = "reboot", .help = "reboot the device", .func = do_cmd_reboot}, + {.command = "exec", .help = "execute user programs ", .func = do_cmd_exec}, + {.command = "setTime", .help = "setTime [MESSAGE] [SECONDS] ", .func = do_cmd_setTimeout}, + {.command = "2sAlert", .help = "set core timer interrupt every 2 second ", .func = do_cmd_set2sAlert}, + {.command = "mtest", .help = "memory testcase generator, allocate and free", .func = do_cmd_mtest}, + {.command = "ttest", .help = "thread tester with dummy function - foo()", .func = do_cmd_ttest}, + {.command = "ftest", .help = "get run_queue size", .func = do_cmd_ftest}}; + +extern char *dtb_ptr; + +extern void *CPIO_DEFAULT_START; + +void start_shell() +{ + // uart_sendlinek("In start_shell\n"); + char input_buffer[CMD_MAX_LEN]; + cli_print_banner(); + while (1) + { + cli_flush_buffer(input_buffer, CMD_MAX_LEN); + // uart_sendlinek("lock_counter : %d\n",lock_counter); + puts("【 Ciallo~(∠・ω< )⌒★ 】 # "); + cli_cmd_read(input_buffer); + cli_cmd_exec(input_buffer); + idle(); + // uart_sendlinek("idle finish\n"); + } + // return 0; +} + +void cli_flush_buffer(char *buffer, int length) +{ + for (int i = 0; i < length; i++) + { + buffer[i] = '\0'; + } +}; + +void cli_cmd_read(char *buffer) +{ + char c = '\0'; + int idx = 0; + while (idx < CMD_MAX_LEN - 1) + { + c = getchar(); + if (c == 127) // backspace + { + if (idx != 0) + { + puts("\b \b"); + idx--; + } + } + else if (c == '\n') + { + break; + } + else if (c <= 16 || c >= 32 || c < 127) + { + putchar(c); + buffer[idx++] = c; + } + } + buffer[idx] = '\0'; + puts("\r\n"); +} + +int _parse_args(char *buffer, int *argc, char **argv) +{ + char get_cmd = 0; + for (int i = 0; buffer[i] != '\0'; i++) + { + if (!get_cmd) + { + if (buffer[i] == ' ') + { + buffer[i] = '\0'; + get_cmd = 1; + } + } + else + { + if (buffer[i - 1] == '\0' && buffer[i] != ' ' && buffer[i] != '\0') + { + if (*argc >= CMD_MAX_PARAM) + { + return -1; + } + argv[*argc] = buffer + i; + (*argc)++; + } + else if (buffer[i] == ' ') + { + buffer[i] = '\0'; + } + } + } + return 0; +} + +void print_args(int argc, char **argv) +{ + puts("argc: "); + put_int(argc); + puts("\r\n"); + for (int i = 0; i < argc; i++) + { + puts("argv["); + put_int(i); + puts("]: "); + puts(argv[i]); + puts("\r\n"); + } +} + +void cli_cmd_exec(char *buffer) +{ + char *cmd = buffer; + int argc = 0; + char *argv[CMD_MAX_PARAM]; + if (_parse_args(buffer, &argc, argv) == -1) + { + puts("Too many arguments\r\n"); + return; + } + // print_args(argc, argv); + + for (int i = 0; i < CLI_MAX_CMD; i++) + { + if (strcmp(cmd, cmd_list[i].command) == 0) + { + cmd_list[i].func(argc, argv); + return; + } + } + if (*buffer) + { + puts(buffer); + puts(": command not found\r\n"); + } +} + +void cli_print_banner() +{ + // uart_sendlinek("In cli_print_banner\n"); + puts("\r\n"); + puts("============================================================================================\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@- %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% *@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#%@@@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@% *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:=@@@@@@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@- :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* #@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@#- :#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+ +%@@@@@@@@@@@@@@+ -%@@@@@@@@@@@@\r\n"); + puts("@@@@*=: :=#@@@@@@@@#=: =*@@@@@@@@@@\r\n"); + puts("@@@@@#= =*%@@@@@@@@@@*+- :=*%@@@@@@@@@@\r\n"); + puts("@@@@@@@- @@@@@@@@@@@# -@@@@@@@@@@@@@@@@@@%: -%@%@@@@@@@@@@@@@@%= :#@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@= @%++*%@@@@@%: +@@@@#==#@@@@@@@@@@@% %* -%@@#-=+%@@@@@@@- %@@@@@+::=*%@@@@\r\n"); + puts("@@@@@@@= @% =%@+ #@+ =%@@\r\n"); + puts("@@@@@@@= @% ************- =@ #+ :%@@\r\n"); + puts("@@@@@@@= @% @@@@@@@@@@@@+ @@% #= += %@@@* =@@@@- :@@@@\r\n"); + puts("@@@@@@@= @% ##########%@+ :@@% #= +@@@@@@= %@#= -#@@= -@@@@\r\n"); + puts("@@@@@@@= @% :::::::::::@+ :@@% #= +#+=++@= -: -: -@@@@\r\n"); + puts("@@@@@@@= @% @@@@@@@@@@@@+ :@@% #= ++ @= - : -@@@@\r\n"); + puts("@@@@@@@= :@% @@@@@@@@@@@@+ :@@% #= ++ @= %@#- :*%@- -@@@@\r\n"); + puts("@@@@@@@= =@% :@@% #= ++ @= %@@@+ -@@@@- -@@@@\r\n"); + puts("@@@@@@@- #@% ****: +**= +@@% #= ++ @= *####: #####: -@@@@\r\n"); + puts("@@@@@@@ :@@%-=*%@@@@@- #@@#+*#%@@@@% #= ++ =@+ -@@@@\r\n"); + puts("@@@@@@* %@@@@#*=--@@@- #@@#-=+*%@@@% %= *+ =@@+ +@@@@@= :@@@@@# -@@@@\r\n"); + puts("@@@@@@: *@%+- =@@- #@#: :=*+ :@= +*=%@@@+ =%@@@@@* -@@@@@@+: -@@@@\r\n"); + puts("@@@@@= *#- *- #- *@= +@@@@@@#+*@@@@@@*- :+%@@@@@#+*@@@@\r\n"); + puts("@@@@+ -%@: -=**%@@@- #@@%#*+=: *@@= *@@@@@@@@@@@@=: :-%@@@@@@@@@\r\n"); + puts("@@@+ -#@@@%- =@@@@@@@@@- #@@@@@@@@% =%@@@= *@@@@@@@@@@@@@#- :*%@@@@@@@@@@\r\n"); + puts("@@=:+%@@@@@@@%+--=*%@@@@@- :@@@@@@#*=-=*@@@@@@= #@@@@@@@@@@@@@@@* =@@@@@@@@@@@@@\r\n"); + puts("@%%@@@@@@@@@@@@@@@%##@@@@- #@@@@%#%@@@@@@@@@@@= +@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@- =%@@@@@@@@@@@@@@@@@@@= *@@@@@@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@++%@@@@@@@@@@@@@@@@@@@@@*+%@@@@@@@@@@@@@@@@@@@@+ -@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% +@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-%@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@@@@@@@@@@@@\r\n"); + puts("============================================================================================\r\n"); +} + +int do_cmd_help(int argc, char **argv) +{ + for (int i = 0; i < CLI_MAX_CMD; i++) + { + puts(cmd_list[i].command); + puts("\t\t\t: "); + puts(cmd_list[i].help); + puts("\r\n"); + } + return 0; +} + +int do_cmd_hello(int argc, char **argv) +{ + puts("Hello World!\r\n"); + return 0; +} + +int do_cmd_info(int argc, char **argv) +{ + // print hw revision + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_BOARD_REVISION; + pt[3] = 4; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt))) + { + puts("Hardware Revision\t: 0x"); + // put_hex(pt[6]); + put_hex(pt[5]); + puts("\r\n"); + } + // print arm memory + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_ARM_MEMORY; + pt[3] = 8; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt))) + { + puts("ARM Memory Base Address\t: 0x"); + put_hex(pt[5]); + puts("\r\n"); + puts("ARM Memory Size\t\t: 0x"); + put_hex(pt[6]); + puts("\r\n"); + } + return 0; +} + +int do_cmd_reboot(int argc, char **argv) +{ + if (argc == 0) + { + + puts("Reboot in 10 seconds ...\r\n\r\n"); + volatile unsigned int *rst_addr = (unsigned int *)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x20; + volatile unsigned int *wdg_addr = (unsigned int *)PM_WDOG; + *wdg_addr = PM_PASSWORD | 0x70000; + } + else if (argc == 1 && strcmp(argv[0], "-c") == 0) + { + puts("Cancel reboot...\r\n"); + volatile unsigned int *rst_addr = (unsigned int *)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x0; + volatile unsigned int *wdg_addr = (unsigned int *)PM_WDOG; + *wdg_addr = PM_PASSWORD | 0x0; + } + return 0; +} + +int do_cmd_ls(int argc, char **argv) +{ + char *c_filepath; + char *c_filedata; + unsigned int c_filesize; + + CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + { + if (header_ptr != 0) + { + puts(c_filepath); + puts("\r\n"); + } + } + return 0; +} + +int do_cmd_cat(int argc, char **argv) +{ + int FLAG_getfile = 0; + char *filepath; + char *c_filepath; + char *c_filedata; + unsigned int c_filesize; + + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + + CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + { + if (strcmp(c_filepath, filepath) == 0) + { + FLAG_getfile = 1; + Readfile(c_filedata, c_filesize); + break; + } + } + + if (!FLAG_getfile) + { + puts("cat: "); + puts(filepath); + puts(": No such file or directory\r\n"); + } + return 0; +} + +int do_cmd_malloc(int argc, char **argv) +{ + // test malloc + char *test1 = allocator(0x18); + strcpy(test1, "test malloc1"); + puts(test1); + puts("\r\n"); + + char *test2 = allocator(0x20); + strcpy(test2, "test malloc2"); + puts(test2); + puts("\r\n"); + + char *test3 = allocator(0x28); + strcpy(test3, "test malloc3"); + puts(test3); + puts("\r\n"); + return 0; +} + +int do_cmd_dtb(int argc, char **argv) +{ + traverse_device_tree(dtb_ptr, dtb_callback_show_tree); + return 0; +} + +int do_cmd_exec(int argc, char **argv) +{ + int FLAG_getfile = 0; + char *filepath; + char *c_filepath; + char *c_filedata; + unsigned int c_filesize; + + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + + CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + { + if (strcmp(c_filepath, filepath) == 0) + { + // exec c_filedata + FLAG_getfile = 1; + // char *ustack = allocator(USTACK_SIZE); + // asm("msr elr_el1, %0\n\t" // elr_el1: Set the address to return to: c_filedata + // "mov x1, 0x3c0\n\t" + // "msr spsr_el1, x1\n\t" // enable interrupt (PSTATE.DAIF) -> spsr_el1[9:6]=4b0. In Basic#1 sample, EL1 interrupt is disabled. + // "msr sp_el0, %1\n\t" // user program stack pointer set to new stack. + // "eret\n\t" // Perform exception return. EL1 -> EL0 + // ::"r"(c_filedata), + // "r"(ustack + USTACK_SIZE)); + // free(ustack); + // break; + + // thread_create((exec_thread)(c_filedata, c_filesize)); + // schedule(); + c_execfile.data = c_filedata; + c_execfile.filesize = c_filesize; + exec_thread(); + } + } + + if (!FLAG_getfile) // header_ptr + { + puts("cat: "); + puts(filepath); + puts(": No such file or directory\r\n"); + } + return 0; +} + +int do_cmd_setTimeout(int argc, char **argv) +{ + char *msg; + int sec; + if (argc == 2) + { + msg = argv[0]; + sec = atoi(argv[1]); + } + else + { + puts("setTimeout [MESSAGE] [SECONDS]\r\n"); + return -1; + } + add_timer(puts, sec, msg, setSecond); + return 0; +} + +int do_cmd_set2sAlert(int argc, char **argv) +{ + add_timer(timer_set2sAlert, 2, "2sAlert", setSecond); + return 0; +} + +int do_cmd_mtest(int argc, char **argv) +{ + // char *a = kmalloc(513); + // uart_sendlinek("a : %x\n", a); + // //kfree(a); + + // char *b = kmalloc(512); + // uart_sendlinek("b : %x\n", b); + // //kfree(b); + + // char *c = kmalloc(8); + // //kfree(c); + + return 0; +} + +int do_cmd_ttest(int argc, char **argv) +{ + // for (int i = 0; i < 5; ++i) + // { // N should > 2 + // thread_create(foo); + // } + // // idle(); + // // schedule(); + uart_sendlinek("getcurrent thread : %x\n", get_current()); + uart_sendlinek("signal_handler_wrapper : 0x%x\n", signal_handler_wrapper); + return 0; +} + +int do_cmd_ftest(int argc, char **argv) +{ + uart_sendlinek("run_queue size : %d\n", list_size(run_queue)); + list_head_t *pos; + list_for_each(pos, run_queue) + { + uart_sendlinek("pid : %d\n", ((thread_t *)pos)->pid); + for (int i = 0; i < SIGNAL_MAX; i++) + { + uart_sendlinek("signal_handler : 0x%x\n", ((thread_t *)pos)->signal_handler[i]); + } + } + return 0; +} \ No newline at end of file diff --git a/lab6/kernel/src/signal.c b/lab6/kernel/src/signal.c new file mode 100644 index 000000000..454315f6e --- /dev/null +++ b/lab6/kernel/src/signal.c @@ -0,0 +1,90 @@ +#include "signal.h" +#include "syscall.h" +#include "sched.h" +#include "memory.h" +#include "mmu.h" + +extern thread_t *curr_thread; + +/** + ( ) + [Signal] [UserProcess] ( [ Registered Signal Handler ] ) [UserProcess] + | \ ( / \ ) / + | \ ( / \ ) / +-------|--------------------[SystemCall]-------------------------------------(-----------------/---------------------------------\----------------------)--/--------------- + | \ [Signal Check] (-> \ ) / + V \ / ^ ( \ \ )/ + [Job Pending] [Exception Handler] | ( \ [ Default Signal Handler ]-------- [Exception Handler]) + | | ( ) + --------------------- ( CONTENT SWITCHING FOR SIGNAL HANDLER ) + +**/ + +void check_signal(trapframe_t *tpf) +{ + if (curr_thread->signal_is_checking) + return; + lock(); + // Prevent nested running signal handler. No need to handle + curr_thread->signal_is_checking = 1; + unlock(); + for (int i = 0; i <= SIGNAL_MAX; i++) + { + store_context(&curr_thread->signal_savedContext); + if (curr_thread->sigcount[i] > 0) + { + lock(); + curr_thread->sigcount[i]--; + unlock(); + run_signal(tpf, i); + } + } + lock(); + curr_thread->signal_is_checking = 0; + unlock(); +} + +void run_signal(trapframe_t *tpf, int signal) +{ + curr_thread->curr_signal_handler = curr_thread->signal_handler[signal]; + + // run default handler in kernel + if (curr_thread->curr_signal_handler == signal_default_handler) + { + signal_default_handler(); + return; + } + + // run registered handler in userspace + // char *temp_signal_userstack = kmalloc(USTACK_SIZE); + // asm("msr elr_el1, %0\n\t" + // "msr sp_el0, %1\n\t" + // "msr spsr_el1, %2\n\t" + // "eret\n\t" ::"r"(signal_handler_wrapper), + // "r"(temp_signal_userstack + USTACK_SIZE), + // "r"(tpf->spsr_el1)); + + 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_SIGNAL_WRAPPER_VA + ((size_t)signal_handler_wrapper % 0x1000)), + "r"(tpf->sp_el0), + "r"(tpf->spsr_el1), + "r"(curr_thread->curr_signal_handler)); +} + +void signal_handler_wrapper() +{ + //(curr_thread->curr_signal_handler)(); + // system call sigreturn + // uart_sendlinek("signal_handler_wrapper\n"); + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void signal_default_handler() +{ + kill(0, curr_thread->pid); +} diff --git a/lab6/kernel/src/stdio.c b/lab6/kernel/src/stdio.c new file mode 100644 index 000000000..62aec7591 --- /dev/null +++ b/lab6/kernel/src/stdio.c @@ -0,0 +1,114 @@ +#include "uart1.h" + +char getchar() +{ + char c = uart_async_recv(); + return c == '\r' ? '\n' : c; +} + +void putchar(char c) +{ + uart_async_send(c); +} + +void puts(const char *s) +{ + while (*s) + putchar(*s++); +} + +void Readfile(char *str, int size) +{ + while (size--) + { + putchar(*str++); + } +} + +// Function to print an integer to the UART +void put_int(int num) +{ + // Handle the case when the number is 0 + if (num == 0) + { + putchar('0'); + return; + } + + // Temporary array to store the reversed digits as characters + char temp[12]; // Assuming int can have at most 10 digits + int idx = 0; + + // Handle negative numbers + if (num < 0) + { + putchar('-'); + num = -num; + } + + // Convert the number to characters and store in the temporary array in reverse order + while (num > 0) + { + temp[idx++] = (char)(num % 10 + '0'); + num /= 10; + } + + // Reverse output the character digits + while (idx > 0) + { + putchar(temp[--idx]); + } +} + +void put_hex(unsigned int num) +{ + unsigned int hex; + int index = 28; + puts("0x"); + while (index >= 0) + { + hex = (num >> index) & 0xF; + hex += hex > 9 ? 0x37 : 0x30; + putchar(hex); + index -= 4; + } +} + +// 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; +} + +int fake_log2(unsigned long long n) +{ + int val = 0; + while (n >>= 1) + val++; + return val; +} + +void delay(int r) +{ + while (r--) + { + asm volatile("nop"); + } +} diff --git a/lab6/kernel/src/string.c b/lab6/kernel/src/string.c new file mode 100644 index 000000000..e47fd4d39 --- /dev/null +++ b/lab6/kernel/src/string.c @@ -0,0 +1,199 @@ +#include "string.h" + +size_t strlen(const char *str) +{ + size_t count = 0; + while ((unsigned char)*str++) + count++; + return count; +} + +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 *strcpy(char *dest, const char *src) +{ + while ((*dest++ = *src++)) + ; + return dest; +} + +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(dst-orig > VSPRINT_MAX_BUF_SIZE-0x10) + { + return -1; + } + // 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]='-'; + } + if(len>0 && len<18) { + while(i>18-len) { + tmpstr[--i]=' '; + } + } + p=&tmpstr[i]; + goto copystring; + } else + if(*fmt=='x') { + arg = __builtin_va_arg(args, long int); + i=16; + tmpstr[i]=0; + do { + char n=arg & 0xf; + tmpstr[--i]=n+(n>9?0x37:0x30); + arg>>=4; + } while(arg!=0 && i>0); + if(len>0 && len<=16) { + while(i>16-len) { + tmpstr[--i]='0'; + } + } + p=&tmpstr[i]; + goto copystring; + } else + 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; + return dst-orig; +} + +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; +} + +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/kernel/src/syscall.c b/lab6/kernel/src/syscall.c new file mode 100644 index 000000000..6f9705f32 --- /dev/null +++ b/lab6/kernel/src/syscall.c @@ -0,0 +1,328 @@ +#include "bcm2837/rpi_mbox.h" +#include "syscall.h" +#include "sched.h" +#include "uart1.h" +#include "stdio.h" +#include "exception.h" +#include "memory.h" +#include "mbox.h" +#include "signal.h" +#include "string.h" + +#include "debug.h" +#include "cpio.h" +#include "dtb.h" +#include "mmu.h" + +extern void *CPIO_DEFAULT_START; +extern thread_t *curr_thread; +extern thread_t threads[PIDMAX + 1]; + +// trap is like a shared buffer for user space and kernel space +// Because general-purpose registers are used for both arguments and return value, +// We may receive the arguments we need, and overwrite them with return value. + +int getpid(trapframe_t *tpf) +{ + // uart_sendlinek("this is getpid"); + 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_recv(); + // buf[i] = uart_recv(); + } + tpf->x0 = i; + return i; +} + +size_t uartwrite(trapframe_t *tpf, const char buf[], size_t size) +{ + int i = 0; + // uart_sendlinek("buf[i]: %s\n",buf); + // uart_sendlinek("buf[i] size: %d\n",size); + for (int i = 0; i < size; i++) + { + uart_async_send(buf[i]); + // uart_send(buf[i]); + } + tpf->x0 = i; + return i; +} + +// In this lab, you won’t have to deal with argument passing +int exec(trapframe_t *tpf, const char *name, char *const argv[]) +{ + mmu_del_vma(curr_thread); + 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 + mmu_free_page_tables(curr_thread->context.pgd, 0); + memset(PHYS_TO_VIRT(curr_thread->context.pgd), 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 + mmu_del_vma(curr_thread); + + memcpy(curr_thread->signal_handler, curr_thread->signal_handler, SIGNAL_MAX * 8); + memcpy(curr_thread->data, new_data, curr_thread->datasize); + mmu_add_vma(curr_thread, USER_DATA_BASE, curr_thread->datasize, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->data), 0b111, 1, USER_DATA); + mmu_add_vma(curr_thread, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->stack_alloced_ptr), 0b011, 1, USER_STACK); + mmu_add_vma(curr_thread, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(curr_thread, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), 0x1000), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(curr_thread, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + tpf->elr_el1 = USER_DATA_BASE; + tpf->sp_el0 = USER_STACK_BASE-STACK_BASE_OFFSET; + tpf->x0 = 0; + return 0; +} + +// extern unsigned long long int lock_counter; +int fork(trapframe_t *tpf) +{ + lock(); + thread_t *newt = thread_create(curr_thread->data); + + // mmu_set_PTE_readonly(curr_thread->context.pgd,0); + // mmu_pagetable_copy(newt->context.pgd,curr_thread->context.pgd,0); + //uart_sendlinek("fork\n"); + memcpy(newt->signal_handler, curr_thread->signal_handler, SIGNAL_MAX * 8); + memcpy(newt->stack_alloced_ptr, curr_thread->kernel_stack_alloced_ptr, USTACK_SIZE); + memcpy(newt->kernel_stack_alloced_ptr, curr_thread->kernel_stack_alloced_ptr, KSTACK_SIZE); + mmu_add_vma(newt, USER_DATA_BASE, curr_thread->datasize, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->data), 0b111, 1, USER_DATA); + mmu_add_vma(newt, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(newt->stack_alloced_ptr), 0b011, 1, USER_STACK); + mmu_add_vma(newt, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(newt, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), 0x1000), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(newt, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + int parent_pid = curr_thread->pid; + + store_context(get_current()); + // for child + if (parent_pid != curr_thread->pid) + { + goto child; + } + + // 除了PGD以外的context都複製。 + void *temp_pgd = newt->context.pgd; + newt->context = curr_thread->context; + newt->context.pgd = temp_pgd; + + 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(); + while (1) + schedule(); +} + +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox) +{ + lock(); + + unsigned int size_of_mbox = mbox[0]; + memcpy((char *)pt, mbox, size_of_mbox); + mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)); + memcpy(mbox, (char *)pt, size_of_mbox); + + // tpf->x0 = 8; + unlock(); + return 0; +} + +// only need to implement the anonymous page mapping in this Lab. +void *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset) +{ + len = ALIGN_UP(len, PAGESIZE); + unsigned long base_user_va = ALIGN_DOWN((unsigned long)addr, PAGESIZE); + uart_sendlinek("+\n"); + uart_sendlinek("| User request new vma base vitural address: 0x%x\n", (unsigned long)addr); + uart_sendlinek("| Aligned to PAGESIZE: 0x%x\n", base_user_va); + uart_sendlinek("| User request new vma size: 0x%x\n", len); + uart_sendlinek("| Exec, Write, Read : 0x%d\n", prot); + uart_sendlinek("+\n"); + + // Req #2 check if overlap + vm_area_struct_t *the_area_ptr = check_vma_overlap(curr_thread, base_user_va, (unsigned long)len); + // take as a hint to decide new region's start address + if (the_area_ptr) + { + WARING("Vitural Memory Area Overlap\n"); + WARING("Find another vma base vitural address\n"); + tpf->x0 = (unsigned long)mmap(tpf, (void *)(the_area_ptr->virt_addr + the_area_ptr->area_size), len, prot, flags, fd, file_offset); + return (void *)tpf->x0; + } + // create new valid region, map and set the page attributes (prot) + mmu_add_vma(curr_thread, base_user_va, len, KERNEL_VIRT_TO_PHYS((unsigned long)kmalloc(len)), prot, 1, UNKNOW_AREA); + tpf->x0 = base_user_va; + return (void *)tpf->x0; +} + +void kill(trapframe_t *tpf, int pid) +{ + if (pid < 0 || pid >= PIDMAX || !threads[pid].isused) + return; + + lock(); + + if (pid == curr_thread->pid) + { + uart_sendlinek("[!] you kill youself !! \n"); + thread_exit(); + unlock(); + while (1) + schedule(); + } + else + { + threads[pid].iszombie = 1; + unlock(); + } + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0) + return; + // uart_sendlinek("handler : 0x%x\n", handler); + curr_thread->signal_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) +{ + // unsigned long signal_ustack = tpf->sp_el0 % USTACK_SIZE == 0 ? tpf->sp_el0 - USTACK_SIZE : tpf->sp_el0 & (~(USTACK_SIZE - 1)); + // kfree((char *)signal_ustack); + load_context(&curr_thread->signal_savedContext); +} + +void syscall_unlock(trapframe_t *tpf) +{ + unlock(); +} + +void syscall_lock(trapframe_t *tpf) +{ + lock(); +} + +char *get_file_start(char *thefilepath) +{ + int FLAG_getfile = 0; + char *filepath; + char *filedata; + unsigned int filesize; + // struct cpio_newc_header *header_pointer = CPIO_DEFAULT_START; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + if (strcmp(thefilepath, filepath) == 0) + { + FLAG_getfile = 1; + return filedata; + } + } + + if (!FLAG_getfile) + { + uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + } + + // while (header_pointer != 0) + // { + // int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // //if parse header error + // if (error) + // { + // uart_sendlinek("error"); + // break; + // } + + // if (strcmp(thefilepath, filepath) == 0) + // { + // return filedata; + // } + + // //if this is TRAILER!!! (last of file) + // if (header_pointer == 0) + // uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + // } + + return 0; +} + +unsigned int get_file_size(char *thefilepath) +{ + int FLAG_getfile = 0; + char *filepath; + char *filedata; + unsigned int filesize; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + if (strcmp(thefilepath, filepath) == 0) + { + FLAG_getfile = 1; + return filesize; + } + } + + if (!FLAG_getfile) + { + uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + } + + // while (header_pointer != 0) + // { + // int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // // if parse header error + // if (error) + // { + // uart_sendlinek("error"); + // break; + // } + + // if (strcmp(thefilepath, filepath) == 0) + // { + // return filesize; + // } + + // // if this is TRAILER!!! (last of file) + // if (header_pointer == 0) + // uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + // } + return 0; +} diff --git a/lab6/kernel/src/timer.c b/lab6/kernel/src/timer.c new file mode 100644 index 000000000..061bbb05d --- /dev/null +++ b/lab6/kernel/src/timer.c @@ -0,0 +1,177 @@ +#include "timer.h" +#include "uart1.h" +#include "memory.h" +#include "string.h" +#include "exception.h" +#include + +#define STR(x) #x +#define XSTR(s) STR(s) + +struct list_head *timer_event_list; // first head has nothing, store timer_event_t after it + +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 = allocator(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" // cntp_ctl_el0[0]: enable, Control register for the EL1 physical timer. + // cntp_tval_el0: Holds the timer value for the EL1 physical timer + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // QA7_rev3.4.pdf: Core0 Timer IRQ allows Non-secure physical timer(nCNTPNSIRQ) + ); +} + +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" // QA7_rev3.4.pdf: Mask all timer interrupt + ); +} + +void core_timer_handler() +{ + lock(); + //uart_sendlinek("\nThis is core_timer_handler\n"); + if (list_empty(timer_event_list)) + { + uart_sendlinek("\ntimer_event_list is empty\n"); + 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(); +} + +void timer_event_callback(timer_event_t *timer_event) +{ + list_del_entry((struct list_head *)timer_event); // delete the event in queue + // free(timer_event->args); // free the event's space + // free(timer_event); + ((void (*)(char *))timer_event->callback)(timer_event->args); // call the event + + kfree(timer_event->args); + kfree(timer_event); + + // set queue linked list to next time event if it exists + 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 timer_set2sAlert(char *str) +{ + unsigned long long cntpct_el0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" : "=r"(cntpct_el0)); // tick auchor + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq_el0)); // tick frequency + + uart_sendlinek("\n"); + uart_sendlinek("[Interrupt start]\n"); + for (int i = 0; i < 1000000000; i++) + ; + uart_sendlinek("[Interrupt finish]\n"); + + // uart_sendlinek("[Interrupt][el1_irq][%s] %d seconds after booting\n", str, cntpct_el0 / cntfrq_el0); + + add_timer(timer_set2sAlert, 2, "2sAlert",setSecond); +} + +void add_timer(void *callback, unsigned long long timeout, char *args, int inTickFormat) +{ + timer_event_t *the_timer_event = kmalloc(sizeof(timer_event_t)); // free by timer_event_callback + // store all the related information in timer_event + the_timer_event->args = kmalloc(strlen(args) + 1); + strcpy(the_timer_event->args, args); + + if (inTickFormat == 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; + INIT_LIST_HEAD(&the_timer_event->listhead); + + // 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 the timer_event is the biggest, run this code block + if (list_is_head(curr, timer_event_list)) + { + list_add_tail(&the_timer_event->listhead, timer_event_list); + } + // 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 auchor + 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 -> frequency of the timer + "mul x1, x1, %0\n\t" // cntpct_el0 = cntfrq_el0 * seconds: relative timer to cntfrq_el0 + "msr cntp_tval_el0, x1\n\t" // Set expired time to cntp_tval_el0, which stores time value of EL1 physical timer. + : "=r"(expired_time)); +} + +// 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 timer + : "=r"(tick)); +} + +// get timer pending queue size +int timer_list_get_size() +{ + int r = 0; + struct list_head *curr; + list_for_each(curr, timer_event_list) + { + r++; + } + return r; +} diff --git a/lab6/kernel/src/uart1.c b/lab6/kernel/src/uart1.c new file mode 100644 index 000000000..b3eea3222 --- /dev/null +++ b/lab6/kernel/src/uart1.c @@ -0,0 +1,175 @@ +#include "bcm2837/rpi_gpio.h" +#include "bcm2837/rpi_uart1.h" +#include "bcm2837/rpi_irq.h" +#include "uart1.h" +#include "string.h" +#include "exception.h" + +// implement first in first out buffer with a read index and a write index +static char uart_tx_buffer[VSPRINT_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[VSPRINT_MAX_BUF_SIZE]; +unsigned int uart_rx_buffer_widx = 0; +unsigned int uart_rx_buffer_ridx = 0; + +void uart_init() +{ + register unsigned int selector; + + /* initialize UART */ + *AUX_ENABLES = 1; // enable UART1 + *AUX_MU_CNTL_REG = 0; // disable TX/RX + + /* configure UART */ + *AUX_MU_IER_REG = 0; // disable interrupt + *AUX_MU_LCR_REG = 3; // 8 bit data size + *AUX_MU_MCR_REG = 0; // disable flow control + *AUX_MU_BAUD_REG = 270; // 115200 baud rate + + /* map UART1 to GPIO pins */ + selector = *GPFSEL1; + selector &= ~(7 << 12); // clean gpio14, and (11 111 111 111 111 111 000 111 111 111 111)2 + selector |= 2 << 12; // set gpio14 to alt5 + selector &= ~(7 << 15); // clean gpio15, and (11 111 111 111 111 000 111 111 111 111 111)2 + selector |= 2 << 15; // set gpio15 to alt5 + *GPFSEL1 = selector; + + /* enable pin 14, 15 - ref: Page 101 */ + *GPPUD = 0; + selector = 150; + while (selector--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + selector = 150; + while (selector--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; + + *AUX_MU_CNTL_REG = 3; +} + +void uart_flush_FIFO() +{ + // On write: + // Writing with bit 1 set will clear the receive FIFO + // Writing with bit 2 set will clear the transmit FIFOF + *AUX_MU_IIR_REG |= 6; +} + +char uart_recv() +{ + char r; + while (!(*AUX_MU_LSR_REG & 0x01)) + ; + r = (char)(*AUX_MU_IO_REG); + return r; +} + +void uart_send(unsigned int c) +{ + while (!(*AUX_MU_LSR_REG & 0x20)) + ; + *AUX_MU_IO_REG = c; +} + +// AUX_MU_IER_REG -> BCM2837-ARM-Peripherals.pdf - Pg.12 +void uart_interrupt_enable() +{ + *AUX_MU_IER_REG |= 1; // enable read interrupt + *AUX_MU_IER_REG |= 2; // enable write interrupt + *ENABLE_IRQS_1 |= 1 << 29; // Pg.112 +} + +void uart_interrupt_disable() +{ + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + *AUX_MU_IER_REG &= ~(2); // disable write interrupt +} + +// scanf +void uart_r_irq_handler() +{ + if ((uart_rx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_rx_buffer_ridx) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + return; + } + uart_rx_buffer[uart_rx_buffer_widx++] = uart_recv(); + if (uart_rx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) + uart_rx_buffer_widx = 0; + *AUX_MU_IER_REG |= 1; +} + +// printf +void uart_w_irq_handler() +{ + if (uart_tx_buffer_ridx == uart_tx_buffer_widx) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + return; // buffer empty + } + uart_send(uart_tx_buffer[uart_tx_buffer_ridx++]); + if (uart_tx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) + uart_tx_buffer_ridx = 0; + *AUX_MU_IER_REG |= 2; // enable write interrupt +} + +// uart_async_getc read from buffer +// uart_r_irq_handler write to buffer then output +char uart_async_recv() +{ + *AUX_MU_IER_REG |= 1; // enable read interrupt + // do while if buffer empty + while (uart_rx_buffer_ridx == uart_rx_buffer_widx) + { + *AUX_MU_IER_REG |= 1; // enable read interrupt + } + lock(); + char r = uart_rx_buffer[uart_rx_buffer_ridx++]; + if (uart_rx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) + uart_rx_buffer_ridx = 0; + unlock(); + return r; +} + +// uart_async_putc writes to buffer +// uart_w_irq_handler read from buffer then output +void uart_async_send(char c) +{ + // if buffer full, wait for uart_w_irq_handler + while ((uart_tx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_tx_buffer_ridx) + { + // uart_puts("buffer full\r\n"); + *AUX_MU_IER_REG |= 2; // enable write interrupt + } + lock(); + uart_tx_buffer[uart_tx_buffer_widx++] = c; + if (uart_tx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) + uart_tx_buffer_widx = 0; // cycle pointer + unlock(); + *AUX_MU_IER_REG |= 2; // enable write interrupt +} + +int uart_sendlinek(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[VSPRINT_MAX_BUF_SIZE]; + + char *str = (char *)buf; + int count = vsprintf(str, fmt, args); + + while (*str) + { + if (*str == '\n') + uart_send('\r'); + uart_send(*str++); + } + __builtin_va_end(args); + return count; +} \ No newline at end of file diff --git a/lab6/send_img_to_bootloader.py b/lab6/send_img_to_bootloader.py new file mode 100644 index 000000000..29d43b781 --- /dev/null +++ b/lab6/send_img_to_bootloader.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +from serial import Serial +from pwn import * +import argparse +from sys import platform + +if platform == "linux" or platform == "linux2": + 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 image by uart1...") + 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!") + +else: + 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='COM',default='COM3', type=str, help='COM# 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 image by uart1...") + 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/bcm2710-rpi-3-b-plus.dtb b/lab7/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..38395a23f Binary files /dev/null and b/lab7/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab7/config.txt b/lab7/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/lab7/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/lab7/create_fs/FromTA/initramfs.cpio b/lab7/create_fs/FromTA/initramfs.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/lab7/create_fs/FromTA/initramfs.cpio differ diff --git a/lab7/create_fs/create_cpio.sh b/lab7/create_fs/create_cpio.sh new file mode 100755 index 000000000..f1270ba2c --- /dev/null +++ b/lab7/create_fs/create_cpio.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Go to rootfs folder +# Select all files and put into archives +# '-o, --create' Run in copy-out mode +# '-H FORMAT' archive format, +# newc: SVR4 portable format + +cd rootfs +find . | cpio -o -H newc > ../initramfs.cpio +cd .. diff --git a/lab7/create_fs/lab3_user_proc/linker.ld b/lab7/create_fs/lab3_user_proc/linker.ld new file mode 100644 index 000000000..faee5d7b1 --- /dev/null +++ b/lab7/create_fs/lab3_user_proc/linker.ld @@ -0,0 +1,5 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text) } +} diff --git a/lab7/create_fs/lab3_user_proc/makefile b/lab7/create_fs/lab3_user_proc/makefile new file mode 100644 index 000000000..590bdf420 --- /dev/null +++ b/lab7/create_fs/lab3_user_proc/makefile @@ -0,0 +1,23 @@ +ARMGNU ?= aarch64-linux-gnu + +BUILD_DIR = build +#--------------------------------------------------------------------------------------- + +ASM_FILES = $(wildcard *.S) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_s.o: %.S + @mkdir -p $(@D) + $(ARMGNU)-gcc -c $< -o $@ + +user_proc.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/user_proc.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/user_proc.elf -O binary user_proc.img + +all: user_proc.img + +clean: + rm -rf $(BUILD_DIR) *.img diff --git a/lab7/create_fs/lab3_user_proc/user_proc.S b/lab7/create_fs/lab3_user_proc/user_proc.S new file mode 100644 index 000000000..2d9837651 --- /dev/null +++ b/lab7/create_fs/lab3_user_proc/user_proc.S @@ -0,0 +1,12 @@ +// Run `svc 0` 5 times; svc (el0 -> el1) system calls +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/lab7/create_fs/lab5_user_fork/fork_test.S b/lab7/create_fs/lab5_user_fork/fork_test.S new file mode 100644 index 000000000..463d19529 --- /dev/null +++ b/lab7/create_fs/lab5_user_fork/fork_test.S @@ -0,0 +1,28 @@ +void fork_test(){ + printf("\nFork Test, pid %d\n", get_pid()); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + ++cnt; + + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + delay(1000000); + ++cnt; + } + } + exit(); + } + else { + printf("parent here, pid %d, child %d\n", get_pid(), ret); + } +} diff --git a/lab7/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio b/lab7/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/lab7/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio differ diff --git a/lab7/create_fs/lab5_user_fork/makefile b/lab7/create_fs/lab5_user_fork/makefile new file mode 100644 index 000000000..160b9729a --- /dev/null +++ b/lab7/create_fs/lab5_user_fork/makefile @@ -0,0 +1,23 @@ +ARMGNU ?= aarch64-linux-gnu + +BUILD_DIR = build +#--------------------------------------------------------------------------------------- + +ASM_FILES = $(wildcard *.S) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_s.o: %.S + @mkdir -p $(@D) + $(ARMGNU)-gcc -c $< -o $@ + +user_proc.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/user_proc.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/user_proc.elf -O binary user_proc.img + +all: fork_test.img + +clean: + rm -rf $(BUILD_DIR) *.img diff --git a/lab7/create_fs/rootfs/file0.txt b/lab7/create_fs/rootfs/file0.txt new file mode 100644 index 000000000..c2e1803c9 --- /dev/null +++ b/lab7/create_fs/rootfs/file0.txt @@ -0,0 +1,2 @@ +[This is file 0] +[Hello World!] diff --git a/lab7/create_fs/rootfs/file1.txt b/lab7/create_fs/rootfs/file1.txt new file mode 100644 index 000000000..5d585c1ac --- /dev/null +++ b/lab7/create_fs/rootfs/file1.txt @@ -0,0 +1,11 @@ +{this is file1} +. +.F +.I +.L +.E +. +.1 +. + + diff --git a/lab7/initramfs.cpio b/lab7/initramfs.cpio new file mode 100644 index 000000000..60760a922 Binary files /dev/null and b/lab7/initramfs.cpio differ diff --git a/lab7/kernel/Makefile b/lab7/kernel/Makefile new file mode 100644 index 000000000..7914a0c1d --- /dev/null +++ b/lab7/kernel/Makefile @@ -0,0 +1,89 @@ +CC := aarch64-linux-gnu-gcc +LD := aarch64-linux-gnu-ld +OBJCOPY := aarch64-linux-gnu-objcopy +GDB := gdb-multiarch +QEMU := qemu-system-aarch64 +KILL_QEMU := killall $(QEMU) + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -mgeneral-regs-only $(cops) +ASMOPS = -Iinclude +QOPS = -M raspi3b -kernel $(NAME).img -dtb ../bcm2710-rpi-3-b-plus.dtb -initrd ../initramfs.cpio +GDBOPS = --init-eval-command="file $(BUILD_DIR)/$(NAME).elf" --init-eval-command="target remote :1234" + +RED=\033[0;31m +GREEN=\033[0;32m +YELLOW=\033[0;33m +BLUE=\033[0;34m +PURPLE=\033[0;35m +CYAN=\033[0;36m +RESET=\033[0m + +# Set the TARGET_PLATFORM and _DEBUG macro to be passed to the GCC definition later. +ifdef DEBUG +COPS+=-D_DEBUG=$(DEBUG) +endif + +ifneq (,$(findstring pi,$(MAKECMDGOALS))) +COPS+=-DRPI +FINISH_STR = "\r\n-------------------------------------------------------------\r\n The $(CYAN)kernel8.img$(RESET) intended for the $(BLUE)Raspberry Pi 3B+$(RESET) is ready.\r\n-------------------------------------------------------------" +else # Default to QEMU +COPS += -DQEMU +FINISH_STR = "\r\n-------------------------------------------------\r\n The $(CYAN)kernel8.img$(RESET) intended for the $(BLUE)QEMU$(RESET) is ready.\r\n-------------------------------------------------" +endif + +NAME = kernel8 +BUILD_DIR = build +SRC_DIR = src +LK_SCRIPT = linker.ld +SESSION_NAME := my-os + +.PHONY: all +all : $(NAME).img + +.PHONY: clean +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(CC) $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(CC) $(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) + +$(NAME).img: $(SRC_DIR)/$(LK_SCRIPT) $(OBJ_FILES) + $(LD) -T $(SRC_DIR)/$(LK_SCRIPT) -o $(BUILD_DIR)/$(NAME).elf $(OBJ_FILES) + $(OBJCOPY) $(BUILD_DIR)/$(NAME).elf -O binary $(NAME).img + @echo -e $(FINISH_STR) + +#This target compiles and runs the kernel.img of the QEMU platform by default. +# You can use "make pi run" to run this image in QEMU. +.PHONY: run +run: clean $(NAME).img + $(QEMU) $(QOPS) -serial null -serial stdio + +# This target compiles kernel.img to run on a QEMU. +.PHONY: qemu +qemu: clean $(NAME).img + +# This target compiles kernel.img to run on a Raspberry 3B. +.PHONY: pi +pi: clean $(NAME).img + +# This target will run kernel in QEMU with GDB attached in a tmux session. +.PHONY: debug +debug: clean $(NAME).img + tmux new-session -d -s $(SESSION_NAME) '$(QEMU) $(QOPS) -S -s -serial null -serial stdio' \; split-window -h '$(GDB) $(GDBOPS)' \; attach -t $(SESSION_NAME) + +# This target will kill the QEMU session. +.PHONY: kill +kill: + $(KILL_QEMU) diff --git a/lab7/kernel/include/bcm2837/rpi_base.h b/lab7/kernel/include/bcm2837/rpi_base.h new file mode 100644 index 000000000..f640aaf4d --- /dev/null +++ b/lab7/kernel/include/bcm2837/rpi_base.h @@ -0,0 +1,7 @@ +#ifndef _RPI_BASE_H_ +#define _RPI_BASE_H_ + +#include "bcm2837/rpi_mmu.h" +#define PERIPHERAL_BASE PHYS_TO_KERNEL_VIRT(0x3F000000) + +#endif /*_RPI_BASE_H_ */ diff --git a/lab7/kernel/include/bcm2837/rpi_gpio.h b/lab7/kernel/include/bcm2837/rpi_gpio.h new file mode 100644 index 000000000..e5133708a --- /dev/null +++ b/lab7/kernel/include/bcm2837/rpi_gpio.h @@ -0,0 +1,25 @@ +#ifndef _RPI_GPIO_H_ +#define _RPI_GPIO_H_ + +#include "bcm2837/rpi_base.h" + +#define GPFSEL0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020009C)) + +#endif /*_RPI_GPIO_H_*/ diff --git a/lab7/kernel/include/bcm2837/rpi_irq.h b/lab7/kernel/include/bcm2837/rpi_irq.h new file mode 100644 index 000000000..8423dfea0 --- /dev/null +++ b/lab7/kernel/include/bcm2837/rpi_irq.h @@ -0,0 +1,24 @@ +#ifndef _RPI_IRQ_H_ +#define _RPI_IRQ_H_ + +#include "bcm2837/rpi_base.h" + +/* +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 IRQ_BASIC_PENDING ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B200)) +#define IRQ_PENDING_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B208)) +#define FIQ_CONTROL ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B224)) + +#endif /*_RPI_IRQ_H_*/ diff --git a/lab7/kernel/include/bcm2837/rpi_mbox.h b/lab7/kernel/include/bcm2837/rpi_mbox.h new file mode 100644 index 000000000..ba1001472 --- /dev/null +++ b/lab7/kernel/include/bcm2837/rpi_mbox.h @@ -0,0 +1,17 @@ +#ifndef _RPI_MBOX_H_ +#define _RPI_MBOX_H_ + +#include "bcm2837/rpi_base.h" + +#define MBOX_BASE (PERIPHERAL_BASE+0x0000B880) + +// The register access to a mailbox +// https://jsandler18.github.io/extra/mailbox.html +#define MBOX_READ ((volatile unsigned int*)(MBOX_BASE+0x00)) +#define MBOX_POLL ((volatile unsigned int*)(MBOX_BASE+0x10)) +#define MBOX_SENDER ((volatile unsigned int*)(MBOX_BASE+0x14)) +#define MBOX_STATUS ((volatile unsigned int*)(MBOX_BASE+0x18)) +#define MBOX_CONFIG ((volatile unsigned int*)(MBOX_BASE+0x1C)) +#define MBOX_WRITE ((volatile unsigned int*)(MBOX_BASE+0x20)) + +#endif /*_RPI_MBOX_H_ */ diff --git a/lab7/kernel/include/bcm2837/rpi_mmu.h b/lab7/kernel/include/bcm2837/rpi_mmu.h new file mode 100644 index 000000000..e0d82c7dd --- /dev/null +++ b/lab7/kernel/include/bcm2837/rpi_mmu.h @@ -0,0 +1,18 @@ +#ifndef _RPI_MMU_H_ +#define _RPI_MMU_H_ + +#define PHYS_TO_VIRT(x) (x + 0xffff000000000000) +#define VIRT_TO_PHYS(x) (x - 0xffff000000000000) +#define ENTRY_ADDR_MASK 0x0000fffffffff000L + +#define PHYS_TO_KERNEL_VIRT(x) (((unsigned long)(x)) | 0xffff000000000000) +#define KERNEL_VIRT_TO_PHYS(x) (((unsigned long)(x)) & ~0xffff000000000000) + +// e.g. size=0x13200, alignment=0x1000 -> 0x14000 +#define ALIGN_UP(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1)) +// e.g. size=0x13200, alignment=0x1000 -> 0x13000 +#define ALIGN_DOWN(size, alignment) ((size) & ~((alignment) - 1)) + +#define IS_NOT_ALIGN(ptr, alignment) (((unsigned long)ptr & ((alignment) - 1)) != 0) + +#endif /*_RPI_MMU_H_ */ diff --git a/lab7/kernel/include/bcm2837/rpi_uart1.h b/lab7/kernel/include/bcm2837/rpi_uart1.h new file mode 100644 index 000000000..959130656 --- /dev/null +++ b/lab7/kernel/include/bcm2837/rpi_uart1.h @@ -0,0 +1,19 @@ +#ifndef _RPI_UART1_H_ +#define _RPI_UART1_H_ + +#include "bcm2837/rpi_base.h" + +#define AUX_ENABLES ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215004)) +#define AUX_MU_IO_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215040)) +#define AUX_MU_IER_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215044)) +#define AUX_MU_IIR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215048)) +#define AUX_MU_LCR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x0021504C)) +#define AUX_MU_MCR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215050)) +#define AUX_MU_LSR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215054)) +#define AUX_MU_MSR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(PERIPHERAL_BASE+0x0021505C)) +#define AUX_MU_CNTL_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215060)) +#define AUX_MU_STAT_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215064)) +#define AUX_MU_BAUD_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215068)) + +#endif /*_RPI_UART1_H_ */ diff --git a/lab7/kernel/include/cpio.h b/lab7/kernel/include/cpio.h new file mode 100644 index 000000000..bfe2c7044 --- /dev/null +++ b/lab7/kernel/include/cpio.h @@ -0,0 +1,47 @@ +#ifndef _CPIO_H_ +#define _CPIO_H_ + +/* + cpio format : https://manpages.ubuntu.com/manpages/bionic/en/man5/cpio.5.html + We are using "newc" format + 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 constant, to check whether it is big endian or little endian + +// Using newc archive format +struct cpio_newc_header +{ + char c_magic[6]; // fixed, "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]; +}; + +/* 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); + +unsigned int padding_4byte(unsigned int size); + +extern void *CPIO_DEFAULT_START; + +#define CPIO_for_each(c_filepath, c_filesize, c_filedata) \ + struct cpio_newc_header *header_ptr = CPIO_DEFAULT_START; \ + int err = cpio_newc_parse_header(header_ptr, c_filepath, c_filesize, c_filedata, &header_ptr); \ + for (; header_ptr&&!err; cpio_newc_parse_header(header_ptr, c_filepath, c_filesize, c_filedata, &header_ptr)) + +#endif /* _CPIO_H_ */ \ No newline at end of file diff --git a/lab7/kernel/include/debug.h b/lab7/kernel/include/debug.h new file mode 100644 index 000000000..2e7d97904 --- /dev/null +++ b/lab7/kernel/include/debug.h @@ -0,0 +1,19 @@ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include "uart1.h" + +#define CYAN "\e[0;36m" // 青色 +#define HRED "\e[0;91m" +#define CRESET "\e[0m" + +#define WARING(fmt, ...) PRINT_MESSAGE(LEVEL_ERROR, "WARING", CYAN, fmt, ##__VA_ARGS__) +#define ERROR(fmt, ...) PRINT_MESSAGE(LEVEL_ERROR, "ERROR", HRED, fmt, ##__VA_ARGS__) +#define PRINT_MESSAGE(level, prefix, color, fmt, ...) \ + do \ + { \ + uart_sendlinek(color "[" prefix "] " CRESET fmt, ##__VA_ARGS__); \ + } while (0) + +#endif /* _DEBUG_H_ */ \ No newline at end of file diff --git a/lab7/kernel/include/dtb.h b/lab7/kernel/include/dtb.h new file mode 100644 index 000000000..7076d8683 --- /dev/null +++ b/lab7/kernel/include/dtb.h @@ -0,0 +1,26 @@ +#ifndef _DTB_H_ +#define _DTB_H_ + +#define uint32_t unsigned int +#define uint64_t unsigned long long + +// manipulate device tree with dtb file format +// linux kernel fdt.h +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +typedef void (*dtb_callback)(uint32_t node_type, char *name, void *value, uint32_t name_size); + +uint32_t uint32_endian_big2little(uint32_t data); +uint64_t uint64_endian_big2little(uint64_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 dtb_find_and_store_reserved_memory(); + +#endif \ No newline at end of file diff --git a/lab7/kernel/include/exception.h b/lab7/kernel/include/exception.h new file mode 100644 index 000000000..eb975a1aa --- /dev/null +++ b/lab7/kernel/include/exception.h @@ -0,0 +1,92 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" + +#define SYSCALL(number, function) \ + case number: \ + function; \ + break; + +// 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_KERNEL_VIRT(0x40000060))) +#define INTERRUPT_SOURCE_CNTPNSIRQ (1 << 1) +#define INTERRUPT_SOURCE_GPU (1 << 8) +#define IRQ_PENDING_1_AUX_INT (1 << 29) + +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; + +#define MEMFAIL_DATA_ABORT_LOWER 0b100100 // esr_el1 +#define MEMFAIL_INST_ABORT_LOWER 0b100000 // EC, bits [31:26] + +#define TF_LEVEL0 0b000100 // iss IFSC, bits [5:0] +#define TF_LEVEL1 0b000101 +#define TF_LEVEL2 0b000110 +#define TF_LEVEL3 0b000111 + +#define ESR_EL1_EC_SHIFT 26 +#define ESR_EL1_EC_MASK 0x3F +#define ESR_EL1_EC_SVC64 0x15 + +typedef struct +{ + unsigned int iss : 25, // Instruction specific syndrome + il : 1, // Instruction length bit + ec : 6; // Exception class +} esr_el1_t; + +void el1_interrupt_enable(); +void el1_interrupt_disable(); + +void lock(); +void unlock(); + +void el1h_sync_router(trapframe_t *tpf); +void el1h_irq_router(trapframe_t *tpf); +void el0_sync_router(trapframe_t *tpf); +void el0_irq_router(trapframe_t *tpf); + +void invalid_exception_router(unsigned long long x0); // exception_handler.S + +const char *get_exception_name(unsigned long esr_el1); + + +#endif /*_EXCEPTION_H_*/ diff --git a/lab7/kernel/include/irqtask.h b/lab7/kernel/include/irqtask.h new file mode 100644 index 000000000..52783e26b --- /dev/null +++ b/lab7/kernel/include/irqtask.h @@ -0,0 +1,22 @@ +#ifndef _IRQTASK_H_ +#define _IRQTASK_H_ + +#include "u_list.h" + +// smaller is more preemptive +#define UART_IRQ_PRIORITY 1 +#define TIMER_IRQ_PRIORITY 0 + +typedef struct irqtask +{ + struct list_head listhead; + unsigned long long priority; // store priority (smaller number is more preemptive) + void *task_function; // task function pointer +} irqtask_t; + +void irqtask_add(void *task_function, unsigned long long priority); +void irqtask_run(irqtask_t *the_task); +void irqtask_run_preemptive(); +void irqtask_list_init(); + +#endif diff --git a/lab7/kernel/include/mbox.h b/lab7/kernel/include/mbox.h new file mode 100644 index 000000000..25b712383 --- /dev/null +++ b/lab7/kernel/include/mbox.h @@ -0,0 +1,66 @@ +#ifndef _MBOX_H_ +#define _MBOX_H_ + +extern volatile unsigned int pt[36]; + +// Mailbox Register MMIO +// https://jsandler18.github.io/extra/mailbox.html +// include/bcm2837/rpi_mbox.h + +// Mailbox Channels +// https://github.com/raspberrypi/firmware/wiki/Mailboxes +typedef enum +{ + MBOX_POWER_MANAGEMENT = 0, + MBOX_FRAMEBUFFER, + MBOX_VIRTUAL_UART, + MBOX_VCHIQ, + MBOX_LEDS, + MBOX_BUTTONS, + MBOX_TOUCHSCREEN, + MBOX_UNUSED, + MBOX_TAGS_ARM_TO_VC, + MBOX_TAGS_VC_TO_ARM, +} mbox_channel_type; + +// Status Code from Broadcom Videocode Driver +// brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/arm_control.h +enum mbox_status_reg_bits +{ + BCM_ARM_VC_MS_FULL = 0x80000000, + BCM_ARM_VC_MS_EMPTY = 0x40000000, + BCM_ARM_VC_MS_LEVEL = 0x400000FF, +}; + +enum mbox_buffer_status_code +{ + MBOX_REQUEST_PROCESS = 0x00000000, + MBOX_REQUEST_SUCCEED = 0x80000000, + MBOX_REQUEST_FAILED = 0x80000001, +}; + +// Tag +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +// Included partition only +typedef enum +{ + /* Videocore */ + MBOX_TAG_GET_FIRMWARE_VERSION = 0x1, + + /* Hardware */ + MBOX_TAG_GET_BOARD_MODEL = 0x10001, + MBOX_TAG_GET_BOARD_REVISION, + MBOX_TAG_GET_BOARD_MAC_ADDRESS, + MBOX_TAG_GET_BOARD_SERIAL, + MBOX_TAG_GET_ARM_MEMORY, + MBOX_TAG_GET_VC_MEMORY, + MBOX_TAG_GET_CLOCKS, + +} mbox_tag_type; + +#define MBOX_TAG_REQUEST_CODE 0x00000000 +#define MBOX_TAG_LAST_BYTE 0x00000000 + +int mbox_call(mbox_channel_type, unsigned int); + +#endif /*_MBOX_H_*/ diff --git a/lab7/kernel/include/memory.h b/lab7/kernel/include/memory.h new file mode 100644 index 000000000..35f7ddb16 --- /dev/null +++ b/lab7/kernel/include/memory.h @@ -0,0 +1,80 @@ +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" +/* Lab2 */ +void *allocator(unsigned int size); +// void free(void* ptr); + +/* Lab4 */ +#define BUDDY_MEMORY_BASE PHYS_TO_KERNEL_VIRT(0x0) // 0x10000000 - 0x20000000 (SPEC) -> Advanced #3 for all memory region +#define BUDDY_MEMORY_PAGE_COUNT 0x3C000 // let BUDDY_MEMORY use 0x0 ~ 0x3C000000 (SPEC) +#define PAGESIZE 0x1000 // 4KB + +#define CACHE_SEG 0x8 +#define CACHE_offset 3 +#define CACHE_record_num 8 + +// #define MAX_PAGES 0x10000 // 65536 (Entries), PAGESIZE * MAX_PAGES = 0x10000000 (SPEC) + +typedef enum +{ + FRAME_FREE = -2, + FRAME_ALLOCATED = -1, + FRAME_IDX_0 = 0, // 0x1000 + FRAME_IDX_8 = 8, + FRAME_IDX_FINAL = 17, // 0x20 000 000 + FRAME_MAX_IDX = 18 +} frame_value_type; + +typedef enum +{ + CACHE_IDX_0 = 0, // 0x20 + CACHE_IDX_FINAL = 6, // 0x800 + CACHE_MAX_IDX = 7 // 0x1000 +} cache_value_type; + +typedef struct frame +{ + struct list_head listhead; // store freelist + int val; // store order + int used; + unsigned int idx; +} frame_t; + +typedef struct cache +{ + struct list_head listhead; // store freelist + void *data_base; + int cache_order; + int max_available; + int available; + unsigned long long cache_record[CACHE_record_num]; +} cache_t; + +void allocator_init(); +frame_t *release_redundant(frame_t *frame); +frame_t *get_buddy(frame_t *frame); +int coalesce(frame_t *frame_ptr); + +void dump_page_info(); +void dump_cache_info(); + +// buddy system +void *page_malloc(unsigned int size); +void page_free(frame_t *ptr); +void page2caches(int order); +void *cache_malloc(unsigned int size); +void cache_free(void *ptr); + +void *kmalloc(unsigned int size); +void kfree(void *ptr); +void memory_reserve(unsigned long long start, unsigned long long end); + +void freelist_init(); +void page2caches(int c_val); +void *find_CACHE(cache_t *ptr); +frame_t *find_free_page(int val); + +#endif /* _MEMORY_H_ */ \ No newline at end of file diff --git a/lab7/kernel/include/mmu.h b/lab7/kernel/include/mmu.h new file mode 100644 index 000000000..bb58ee390 --- /dev/null +++ b/lab7/kernel/include/mmu.h @@ -0,0 +1,101 @@ +#ifndef _MMU_H_ +#define _MMU_H_ + +#include "stddef.h" +// tcr_el1: The control register for stage 1 of the EL1&0 translation regime. +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) // T0SZ 和 T1SZ 設為 16 +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) // TG0 和 TG1 設為 4KB +#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 // Table Entry Armv8_a_address_translation p.14 +#define PD_BLOCK 0b01L // Block Entry +#define PD_UNX (1L << 54) // non-executable page frame for EL0 if set +#define PD_KNX (1L << 53) // non-executable page frame for EL1 if set +#define PD_ACCESS (1L << 10) // a page fault is generated if not set +#define PD_RDONLY (1L << 7) // 0 for read-write, 1 for read-only. +#define PD_UK_ACCESS (1L << 6) // 0 for only kernel access, 1 for user/kernel access. + +#define PERIPHERAL_START 0x3C000000L +#define PERIPHERAL_END 0x3F000000L +#define USER_DATA_BASE 0x00000000L +#define USER_STACK_BASE 0x0000fffffffff000L +#define USER_SIGNAL_WRAPPER_VA 0x0000FFFFFFFAF000L //<------------------------------------- +#define USER_EXEC_WRAPPER_VA 0x0000FFFFFFFBF000L //<------------------------------------- + +#define MMU_PGD_BASE 0x1000L +#define MMU_PGD_ADDR (MMU_PGD_BASE + 0x0000L) +#define MMU_PUD_ADDR (MMU_PGD_BASE + 0x1000L) +#define MMU_PTE_ADDR (MMU_PGD_BASE + 0x2000L) + +// Used for EL1 +#define BOOT_PGD_ATTR (PD_TABLE) +#define BOOT_PUD_ATTR (PD_TABLE | PD_ACCESS) +#define BOOT_PTE_ATTR_nGnRnE (PD_BLOCK | PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_UNX | PD_KNX | PD_UK_ACCESS) // p.17 +#define BOOT_PTE_ATTR_NOCACHE (PD_BLOCK | PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2)) + +#ifndef __ASSEMBLER__ + +#include "sched.h" +#include "exception.h" +#include "u_list.h" +#include "stddef.h" + + +#define PERMISSION_INVAILD(userId,VMA_Permission) (userId&~VMA_Permission) +#define DUMP_NAME(number, name) \ + case number: \ + uart_sendlinek(name); \ + uart_sendlinek("\n"); \ + break; + +typedef enum +{ + UNKNOW_AREA = -1, + USER_DATA, + USER_STACK, + PERIPHERAL, + USER_SIGNAL_WRAPPER, + USER_EXEC_WRAPPER +} vma_name_type; + +typedef enum +{ + PGD, + PUD, + PMD, + PTE, +} pagetable_type; + +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; + vma_name_type name; +} vm_area_struct_t; + +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 mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced, vma_name_type name); +void mmu_del_vma(struct thread *t); +void mmu_free_page_tables(size_t *page_table, int level); +// void mmu_set_PTE_readonly(size_t *page_table, int level); +// void mmu_pagetable_copy(unsigned long *dst_page_table, unsigned long *src_page_table, int level); +void mmu_memfail_abort_handle(esr_el1_t *esr_el1); + +vm_area_struct_t *check_vma_overlap(thread_t *t,unsigned long user_va, unsigned long size); +int check_permission(int userId, int requiredPermission); +void dump_vma(); +void dump_pagetable(unsigned long user_va, unsigned long pa); + +#endif //__ASSEMBLER__ + +#endif /* _MMU_H_ */ diff --git a/lab7/kernel/include/power.h b/lab7/kernel/include/power.h new file mode 100644 index 000000000..07da61847 --- /dev/null +++ b/lab7/kernel/include/power.h @@ -0,0 +1,9 @@ +#ifndef _POWER_H_ +#define _POWER_H_ + +#include "bcm2837/rpi_mmu.h" +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC PHYS_TO_KERNEL_VIRT(0x3F10001c) +#define PM_WDOG PHYS_TO_KERNEL_VIRT(0x3F100024) + +#endif /*_POWER_H_*/ diff --git a/lab7/kernel/include/sched.h b/lab7/kernel/include/sched.h new file mode 100644 index 000000000..475bc27e7 --- /dev/null +++ b/lab7/kernel/include/sched.h @@ -0,0 +1,79 @@ +#ifndef _SCHED_H_ +#define _SCHED_H_ + +#include "u_list.h" +#include "vfs.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x10000 +#define KSTACK_SIZE 0x10000 +#define SIGNAL_MAX 64 +#define STACK_BASE_OFFSET 0x00 + +extern void switch_to(void *curr_context, void *next_context); +extern void *get_current(); +extern void store_context(void *curr_context); +extern void load_context(void *curr_context); + +// arch/arm64/include/asm/processor.h - cpu_context +typedef struct thread_context +{ + unsigned long x19; // callee saved registers: the called function will preserve them and restore them before returning + 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; // base pointer for local variable in stack + unsigned long lr; // store return address + unsigned long sp; // stack pointer, varys from function calls + void *pgd; // use for MMU mapping (user space) +} thread_context_t; + +typedef struct thread +{ + list_head_t listhead; // Freelist node + thread_context_t context; // Thread registers + char *data; // Process itself + unsigned int datasize; // Process size + int iszombie; // Process statement + int pid; // Process ID + int isused; // Freelist node statement + char *stack_alloced_ptr; // Process Stack (Process itself) + char *kernel_stack_alloced_ptr; // Process Stack (Kernel syscall) + void (*signal_handler[SIGNAL_MAX + 1])(); // Signal handlers for different signal + int sigcount[SIGNAL_MAX + 1]; // Signal Pending buffer + void (*curr_signal_handler)(); // Allow Signal handler overwritten by others + int signal_is_checking; // Signal Processing Lock + thread_context_t signal_savedContext; // Store registers before signal handler involving + list_head_t vma_list; + char curr_working_dir[MAX_PATH_NAME + 1]; + struct file *file_descriptors_table[MAX_FD + 1]; +} thread_t; + +typedef struct execfile +{ + struct vnode *vnode; + char * pathname; + char *data; + unsigned int filesize; +} execfile; + +void schedule_timer(char *notuse); +void thread_sched_init(); +void idle(); +void schedule(); +void kill_zombies(); +void thread_exit(); +thread_t *thread_create(void *start); +int exec_thread(); +void exec_proc(); +void exec_wrapper(); + +void foo(); + +#endif /* _SCHED_H_ */ diff --git a/lab7/kernel/include/shell.h b/lab7/kernel/include/shell.h new file mode 100644 index 000000000..276d085d9 --- /dev/null +++ b/lab7/kernel/include/shell.h @@ -0,0 +1,40 @@ +#ifndef _SHELL_H_ +#define _SHELL_H_ + +#define CLI_MAX_CMD 15 +#define CMD_MAX_LEN 32 +#define CMD_MAX_PARAM 10 +#define MSG_MAX_LEN 128 + +typedef struct CLI_CMDS +{ + char command[CMD_MAX_LEN]; + char help[MSG_MAX_LEN]; + int (*func)(int, char **); +} CLI_CMDS; + +int _parse_args(char *buffer, int *argc, char **argv); + +void start_shell(); + +void cli_flush_buffer(char *, int); +void cli_cmd_read(char *); +void cli_cmd_exec(char *); +void cli_print_banner(); + +int do_cmd_help(int argc, char **argv); +int do_cmd_hello(int argc, char **argv); +int do_cmd_info(int argc, char **argv); +int do_cmd_reboot(int argc, char **argv); +int do_cmd_ls(int argc, char **argv); +int do_cmd_cd(int argc, char **argv); +int do_cmd_cat(int argc, char **argv); +int do_cmd_malloc(int argc, char **argv); +int do_cmd_dtb(int argc, char **argv); +int do_cmd_exec(int argc, char **argv); +int do_cmd_setTimeout(int argc, char **argv); +int do_cmd_set2sAlert(int argc, char **argv); +int do_cmd_mtest(int argc, char **argv); +int do_cmd_ttest(int argc, char **argv); +int do_cmd_ftest(int argc, char **argv); +#endif /* _SHELL_H_ */ diff --git a/lab7/kernel/include/signal.h b/lab7/kernel/include/signal.h new file mode 100644 index 000000000..afe2c1617 --- /dev/null +++ b/lab7/kernel/include/signal.h @@ -0,0 +1,49 @@ +#ifndef _SIGNAL_H_ +#define _SIGNAL_H_ + +#include "exception.h" +#include "memory.h" + +#define SIGHUP 1 /* Hangup (POSIX). */ +#define SIGINT 2 /* Interrupt (ANSI). */ +#define SIGQUIT 3 /* Quit (POSIX). */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ +#define SIGTRAP 5 /* Trace trap (POSIX). */ +#define SIGABRT 6 /* Abort (ANSI). */ +#define SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define SIGBUS 7 /* BUS error (4.2 BSD). */ +#define SIGFPE 8 /* Floating-point exception (ANSI). */ +#define SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define SIGPIPE 13 /* Broken pipe (POSIX). */ +#define SIGALRM 14 /* Alarm clock (POSIX). */ +#define SIGTERM 15 /* Termination (ANSI). */ +#define SIGSTKFLT 16 /* Stack fault. */ +#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define SIGCHLD 17 /* Child status has changed (POSIX). */ +#define SIGCONT 18 /* Continue (POSIX). */ +#define SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define SIGTTIN 21 /* Background read from tty (POSIX). */ +#define SIGTTOU 22 /* Background write to tty (POSIX). */ +#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define SIGPWR 30 /* Power failure restart (System V). */ +#define SIGSYS 31 /* Bad system call. */ +#define SIGUNUSED 31 + +void signal_default_handler(); +void check_signal(trapframe_t *tpf); +void run_signal(trapframe_t *tpf, int signal); +//void __attribute__((aligned(PAGESIZE))) signal_handler_wrapper(); +void signal_handler_wrapper(); + +#endif diff --git a/lab7/kernel/include/stddef.h b/lab7/kernel/include/stddef.h new file mode 100644 index 000000000..61b71d842 --- /dev/null +++ b/lab7/kernel/include/stddef.h @@ -0,0 +1,12 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define size_t unsigned long +#define NULL ((void *)0) + +// typedef enum { +// false = 0, +// true = 1 +// } boolean; + +#endif \ No newline at end of file diff --git a/lab7/kernel/include/stdio.h b/lab7/kernel/include/stdio.h new file mode 100644 index 000000000..5ee646448 --- /dev/null +++ b/lab7/kernel/include/stdio.h @@ -0,0 +1,29 @@ +#include "sched.h" +#include "vfs.h" +extern thread_t *curr_thread; + +#define stdio_op(fd, c, len) \ + switch (fd) \ + { \ + case (stdin): \ + (vfs_read(curr_thread->file_descriptors_table[fd], c, len)); \ + break; \ + case (stdout): \ + (vfs_write(curr_thread->file_descriptors_table[fd], c, len)); \ + break; \ + case (stderr): \ + (vfs_write(curr_thread->file_descriptors_table[fd], c, len)); \ + break; \ + default: \ + break; \ + } + +char getchar(); +void putchar(char c); +void put_int(int num); +void puts(const char *s); +void put_hex(unsigned int num); +int atoi(char *str); +void Readfile(char *str, int size); +int fake_log2(unsigned long long n); +void delay(int s); \ No newline at end of file diff --git a/lab7/kernel/include/string.h b/lab7/kernel/include/string.h new file mode 100644 index 000000000..8c46ec5c0 --- /dev/null +++ b/lab7/kernel/include/string.h @@ -0,0 +1,12 @@ +#include "stddef.h" +#define VSPRINT_MAX_BUF_SIZE 0x100 + +size_t strlen(const char *str); +int strcmp(const char *p1, const char *p2); +char* strcat (char *dest, const char *src); +int strncmp(const char *s1, const char *s2, unsigned long long n); +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, unsigned long long n); +unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); +void *memset(void *s, int c, size_t n); +char* memcpy(void *dest, const void *src, unsigned long long len); diff --git a/lab7/kernel/include/syscall.h b/lab7/kernel/include/syscall.h new file mode 100644 index 000000000..a1ba07a31 --- /dev/null +++ b/lab7/kernel/include/syscall.h @@ -0,0 +1,38 @@ +#ifndef _SYSCALL_H_ +#define _SYSCALL_H_ + +#include "exception.h" +#include + +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 *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset); + +int open(trapframe_t *tpf, const char *pathname, int flags); +int close(trapframe_t *tpf, int fd); +long write(trapframe_t *tpf, int fd, const void *buf, unsigned long count); +long read(trapframe_t *tpf, int fd, void *buf, unsigned long count); +int mkdir(trapframe_t *tpf, const char *pathname, unsigned mode); +int mount(trapframe_t *tpf, const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); +int chdir(trapframe_t *tpf, const char *path); +long lseek64(trapframe_t *tpf, int fd, long offset, int whence); +int ioctl(trapframe_t *tpf, int fd, unsigned long request, void *info); + +void syscall_lock(trapframe_t *tpf); +void syscall_unlock(trapframe_t *tpf); + +unsigned int get_file_size(char *thefilepath); +char *get_file_start(char *thefilepath); + +#endif /* _SYSCALL_H_*/ diff --git a/lab7/kernel/include/timer.h b/lab7/kernel/include/timer.h new file mode 100644 index 000000000..8b02b6b8d --- /dev/null +++ b/lab7/kernel/include/timer.h @@ -0,0 +1,36 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" +#define CORE0_TIMER_IRQ_CTRL PHYS_TO_VIRT(0x40000040) //-------------------------------------------------------------------------- + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_handler(); + +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; + +typedef enum +{ + setSecond = 0, + setTick = 1 +} timer_type_t; + +// now the callback only support "funcion(char *)", char* in args +void timer_event_callback(timer_event_t *timer_event); +void add_timer(void *callback, unsigned long long timeout, char *args, int inTickFormat); +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 timer_set2sAlert(char *str); +void timer_list_init(); +int timer_list_get_size(); + +#endif /* _TIMER_H_ */ diff --git a/lab7/kernel/include/u_list.h b/lab7/kernel/include/u_list.h new file mode 100644 index 000000000..79767f194 --- /dev/null +++ b/lab7/kernel/include/u_list.h @@ -0,0 +1,150 @@ +#ifndef _U_LIST_H_ +#define _U_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. + * 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_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; \ + !list_is_head(pos, (head)); \ + pos = n, n = 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 /* _U_LIST_H_ */ diff --git a/lab7/kernel/include/uart1.h b/lab7/kernel/include/uart1.h new file mode 100644 index 000000000..7b6bd70ce --- /dev/null +++ b/lab7/kernel/include/uart1.h @@ -0,0 +1,18 @@ +#ifndef _UART1_H_ +#define _UART1_H_ + +void uart_init(); +void uart_flush_FIFO(); +char uart_recv(); +void uart_send(unsigned int c); + +void uart_r_irq_handler(); +void uart_w_irq_handler(); +char uart_async_recv(); +void uart_async_send(char c); + +int uart_sendlinek(char* fmt, ...); +void uart_interrupt_enable(); +void uart_interrupt_disable(); + +#endif /*_UART1_H_*/ diff --git a/lab7/kernel/include/vfs.h b/lab7/kernel/include/vfs.h new file mode 100644 index 000000000..c19162a8c --- /dev/null +++ b/lab7/kernel/include/vfs.h @@ -0,0 +1,103 @@ +#ifndef _VFS_H_ +#define _VFS_H_ + +#include "stddef.h" + +#define MAX_PATH_NAME 255 +#define MAX_FD 16 +#define O_CREAT 00000100 +#define SEEK_SET 0 +#define MAX_FS_REG 0x50 +#define MAX_DEV_REG 0x10 + +enum stdio_fd +{ + stdin, + stdout, + stderr +}; + +enum fsnode_type +{ + dir_t, + file_t +}; + +struct vnode +{ + struct mount *mount; // Superblock : represents mounted fs + struct vnode_operations *v_ops; // inode & dentry Ops: represents kernel methods for vnode + struct file_operations *f_ops; // file Ops : represents process methods for opened file + void *internal; // vnode itself : directly point to fs's vnode +}; + +struct mount +{ + struct vnode *root; + struct filesystem *fs; +}; + +struct filesystem +{ + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +}; + +// 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 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); + void (*ls)(struct vnode *dir_node); + void (*dump)(struct vnode *dir_node, int level); +}; + +struct dir_path +{ + char *dir; + int size; +}; + +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); +int vfs_mknod(char *pathname, int id); + +void rootfs_init(); +char *get_absolute_path(char *path, char *curr_working_dir); + +void vfs_cd(char *filepath); +void vfs_dump(struct vnode *_vnode, int level); +void vfs_ls(); +void displaylayer(int level); + +#endif /* _VFS_H_ */ diff --git a/lab7/kernel/include/vfs_dev_framebuffer.h b/lab7/kernel/include/vfs_dev_framebuffer.h new file mode 100644 index 000000000..b996ee8e3 --- /dev/null +++ b/lab7/kernel/include/vfs_dev_framebuffer.h @@ -0,0 +1,24 @@ +#ifndef _DEV_FRAMEBUFFER_H_ +#define _DEV_FRAMEBUFFER_H_ + +#include "stddef.h" +#include "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); +long dev_framebuffer_lseek64(struct file *file, long offset, int whence); +int dev_framebuffer_op_deny(); + +#endif diff --git a/lab7/kernel/include/vfs_dev_uart.h b/lab7/kernel/include/vfs_dev_uart.h new file mode 100644 index 000000000..b46dceed4 --- /dev/null +++ b/lab7/kernel/include/vfs_dev_uart.h @@ -0,0 +1,15 @@ +#ifndef _DEV_UART_H_ +#define _DEV_UART_H_ + +#include "stddef.h" +#include "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); +int dev_uart_op_deny(); + +#endif diff --git a/lab7/kernel/include/vfs_initramfs.h b/lab7/kernel/include/vfs_initramfs.h new file mode 100644 index 000000000..824fbdff5 --- /dev/null +++ b/lab7/kernel/include/vfs_initramfs.h @@ -0,0 +1,39 @@ +#ifndef _INITRAMFS_H_ +#define _INITRAMFS_H_ + +#include "stddef.h" +#include "vfs.h" + +#define INITRAMFS_MAX_DIR_ENTRY 100 + +// initramfs basically is same as tmpfs, but it is read-only + +struct initramfs_inode +{ + enum fsnode_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_lseek64(struct file *file, long offset, int whence); +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 fsnode_type type); + +void initramfs_dump(struct vnode *dir_node , int level); +void initramfs_ls(struct vnode *dir_node); + +#endif /* _INITRAMFS_H_ */ diff --git a/lab7/kernel/include/vfs_tmpfs.h b/lab7/kernel/include/vfs_tmpfs.h new file mode 100644 index 000000000..1ff4c4864 --- /dev/null +++ b/lab7/kernel/include/vfs_tmpfs.h @@ -0,0 +1,41 @@ +#ifndef _TMPFS_H_ +#define _TMPFS_H_ + +#include "stddef.h" +#include "vfs.h" + +// SPEC basic Note #3 +#define MAX_FILE_NAME 15 +#define MAX_DIR_ENTRY 16 +#define MAX_FILE_SIZE 4096 + +struct tmpfs_inode +{ + enum fsnode_type type; + char name[MAX_FILE_NAME]; + 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_lseek64(struct file *file, long offset, int whence); +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); + +void tmpfs_dump(struct vnode *dir_node , int level); +void tmpfs_ls(struct vnode *dir_node); + + +struct vnode *tmpfs_create_vnode(struct mount *_mount, enum fsnode_type type); + +#endif /* _TMPFS_H_ */ diff --git a/lab7/kernel/src/boot.S b/lab7/kernel/src/boot.S new file mode 100644 index 000000000..456dfeb7d --- /dev/null +++ b/lab7/kernel/src/boot.S @@ -0,0 +1,82 @@ +#include "mmu.h" + +.section ".text.boot" + +.global _kernel_start + +_kernel_start: + bl from_el2_to_el1 + +set_mmu_configuration: + // 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 + ldr x4, = MMU_PGD_ADDR // PGD's page frame at 0x1000 + ldr x1, = MMU_PUD_ADDR // 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] // [MMU_PGD_ADDR] = MMU_PUD_ADDR | BOOT_PGD_ATTR + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 // [MMU_PUD_ADDR] = 0x00000000 | BOOT_PUD_ATTR + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 // [MMU_PUD_ADDR + 8] = 0x40000000 | BOOT_PUD_ATTR + 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 // temp stack + bl set_2M_kernel_mmu + + mrs x2, sctlr_el1 // sctlr_el1: Provides top level control of the system, including its memory system, at EL1 and EL0. + orr x2 , x2, 1 // sctlr_el1[0]: EL1&0 stage 1 address translation enabled/disabled. + msr sctlr_el1, x2 + + // indirect branch to the upper virtual address + ldr x2, =set_exception_vector_table + br x2 + +set_exception_vector_table: + adr x1, exception_vector_table // vbar_el1: Vector Base Address Register (EL1) + msr vbar_el1, x1 // Holds the exception base address for any exception that is taken to EL1. + +setup_stack: + ldr x1, =_stack_top + mov sp, x1 + +setup_bss: + ldr x1, =_bss_start + ldr w2, =_bss_size + +init_bss: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, init_bss + +run_main: + ldr x1, =dtb_ptr + str x0, [x1], #8 + bl main + +proc_hang: + wfe + b proc_hang + +from_el2_to_el1: + 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 \ No newline at end of file diff --git a/lab7/kernel/src/cpio.c b/lab7/kernel/src/cpio.c new file mode 100644 index 000000000..56fdfc4e8 --- /dev/null +++ b/lab7/kernel/src/cpio.c @@ -0,0 +1,82 @@ +#include "string.h" +#include "cpio.h" +#include "stdio.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) + { + *next_header_pointer = 0; + puts("cpio parse error\r\n"); + return -1; + } + + // transfer big endian 8 byte hex string to unsigned 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); + // The file data is padded to a multiple of four bytes + offset = padding_4byte(offset); + *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 + padding_4byte(offset)); + } + + // if filepath is TRAILER!!! means there is no more files. + if (strncmp(*pathname, "TRAILER!!!", sizeof("TRAILER!!!")) == 0) + { + *next_header_pointer = 0; + } + + return 0; +} + +unsigned int padding_4byte(unsigned int size) +{ + return size + (4 - size % 4) % 4; +} \ No newline at end of file diff --git a/lab7/kernel/src/dtb.c b/lab7/kernel/src/dtb.c new file mode 100644 index 000000000..df97d64fc --- /dev/null +++ b/lab7/kernel/src/dtb.c @@ -0,0 +1,173 @@ +#include "dtb.h" +#include "uart1.h" +#include "cpio.h" +#include "string.h" +#include "stdio.h" +#include "memory.h" +#include "bcm2837/rpi_mmu.h" +#include "debug.h" + +void *CPIO_DEFAULT_START; +void *CPIO_DEFAULT_END; +char *dtb_ptr; + +// 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_big2little(uint32_t data) +{ + char *r = (char *)&data; + return (r[3] << 0) | (r[2] << 8) | (r[1] << 16) | (r[0] << 24); +} + +uint64_t uint64_endian_big2little(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_big2little(header->magic) != 0xD00DFEED) + { + ERROR("traverse_device_tree : wrong magic in traverse_device_tree\n"); + return; + } + // https://abcamus.github.io/2016/12/28/uboot%E8%AE%BE%E5%A4%87%E6%A0%91-%E8%A7%A3%E6%9E%90%E8%BF%87%E7%A8%8B/ + // https://blog.csdn.net/wangdapao12138/article/details/82934127 + uint32_t struct_size = uint32_endian_big2little(header->size_dt_struct); + char *dt_struct_ptr = (char *)((char *)header + uint32_endian_big2little(header->off_dt_struct)); + char *dt_strings_ptr = (char *)((char *)header + uint32_endian_big2little(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_big2little(*(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_big2little(*(uint32_t *)pointer); + pointer += 4; + char *name = (char *)dt_strings_ptr + uint32_endian_big2little(*(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 + { + puts("error type:"); + put_hex(token_type); + puts("\n"); + return; + } + } +} + +void dtb_callback_show_tree(uint32_t node_type, char *name, void *data, uint32_t name_size) +{ + static int level = 0; + if (node_type == FDT_BEGIN_NODE) + { + for (int i = 0; i < level; i++) + puts(" "); + puts(name); + puts("{\n"); + level++; + } + else if (node_type == FDT_END_NODE) + { + level--; + for (int i = 0; i < level; i++) + puts(" "); + puts("}\n"); + } + else if (node_type == FDT_PROP) + { + for (int i = 0; i < level; i++) + puts(" "); + puts(name); + puts("\n"); + } +} + +void dtb_callback_initramfs(uint32_t node_type, char *name, void *value, uint32_t name_size) +{ + // https://github.com/stweil/raspberrypi-documentation/blob/master/configuration/device-tree.md + // linux,initrd-start will be assigned by start.elf based on config.txt + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-start") == 0) + { + CPIO_DEFAULT_START = (void *)(unsigned long long)PHYS_TO_KERNEL_VIRT(uint32_endian_big2little(*(uint32_t *)value)); + } + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-end") == 0) + { + CPIO_DEFAULT_END = (void *)(unsigned long long)PHYS_TO_KERNEL_VIRT(uint32_endian_big2little(*(uint32_t *)value)); + } +} + +void dtb_find_and_store_reserved_memory() +{ + struct fdt_header *header = (struct fdt_header *)dtb_ptr; + if (uint32_endian_big2little(header->magic) != 0xD00DFEED) + { + ERROR("traverse_device_tree : wrong magic in traverse_device_tree\n"); + return; + } + + // off_mem_rsvmap stores all of reserve memory map with address and size + char *dt_mem_rsvmap_ptr = (char *)((char *)header + uint32_endian_big2little(header->off_mem_rsvmap)); + struct fdt_reserve_entry *reverse_entry = (struct fdt_reserve_entry *)dt_mem_rsvmap_ptr; + + // reserve memory which is defined by dtb + while (reverse_entry->address != 0 || reverse_entry->size != 0) + { + unsigned long long start = PHYS_TO_KERNEL_VIRT(uint64_endian_big2little(reverse_entry->address)); + unsigned long long end = uint64_endian_big2little(reverse_entry->size) + start; + memory_reserve(start, end); + reverse_entry++; + } + + // reserve device tree itself + memory_reserve((unsigned long long)dtb_ptr, (unsigned long long)dtb_ptr + uint32_endian_big2little(header->totalsize)); +} \ No newline at end of file diff --git a/lab7/kernel/src/entry.S b/lab7/kernel/src/entry.S new file mode 100644 index 000000000..74e3bcb53 --- /dev/null +++ b/lab7/kernel/src/entry.S @@ -0,0 +1,199 @@ +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp ,16 * 0] // store pair of registers + 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] + // information 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] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + // information 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 instead of elr_el1 + add sp, sp, 32 * 9 +.endm + +.macro ventry label + .align 7 // entry should be aligned to 0x80 (2^7) + b \label +.endm + +.align 11 // vector table should be aligned to 0x800 (2^11) +.global exception_vector_table + +// exception_vector_table definition +// https://github.com/torvalds/linux/blob/v4.14/arch/arm64/kernel/entry.S#L367 +exception_vector_table: + //Exception from the current EL while using SP_EL0 + ventry el1t_sync_invalid // Synchronous EL1t + ventry el1t_irq_invalid // IRQ EL1t + ventry el1t_fiq_invalid // FIQ EL1t + ventry el1t_error_invalid // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry el1h_sync // Synchronous EL1h + ventry el1h_irq // IRQ EL1h + ventry el1h_fiq_invalid // FIQ EL1h + ventry el1h_error_invalid // Error EL1h + + //Exception from a lower EL and at least one lower EL is AArch64 + ventry el0_sync_64 // Synchronous 64-bit EL0 + ventry el0_irq_64 // IRQ 64-bit EL0 + ventry el0_fiq_invalid_64 // FIQ 64-bit EL0 + ventry el0_error_invalid_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry el0_sync_invalid_32 // Synchronous 32-bit EL0 + ventry el0_irq_invalid_32 // IRQ 32-bit EL0 + ventry el0_fiq_invalid_32 // FIQ 32-bit EL0 + ventry el0_error_invalid_32 // Error 32-bit EL0 + + +el1t_sync_invalid: + save_all + mov x0,0 + bl invalid_exception_router + load_all + eret +el1t_irq_invalid: + save_all + mov x0,1 + bl invalid_exception_router + load_all + eret +el1t_fiq_invalid: + save_all + mov x0,2 + bl invalid_exception_router + load_all + eret +el1t_error_invalid: + save_all + mov x0,3 + bl invalid_exception_router + load_all + eret + + +el1h_sync: + save_all + mov x0, sp + bl el1h_sync_router + bl el1_interrupt_disable + load_all + eret +el1h_irq: + save_all + mov x0, sp + bl el1h_irq_router + bl el1_interrupt_disable + load_all + eret +el1h_fiq_invalid: + save_all + mov x0,6 + bl invalid_exception_router + load_all + eret +el1h_error_invalid: + save_all + mov x0,7 + bl invalid_exception_router + load_all + eret + +el0_sync_64: + save_all + mov x0, sp + bl el0_sync_router + bl el1_interrupt_disable + load_all + eret +el0_irq_64: + save_all + mov x0, sp + bl el0_irq_router // ----------------------------------- + bl el1_interrupt_disable + load_all + eret +el0_fiq_invalid_64: + save_all + mov x0,10 + bl invalid_exception_router + load_all + eret +el0_error_invalid_64: + save_all + mov x0,11 + bl invalid_exception_router + load_all + eret + + +el0_sync_invalid_32: + save_all + mov x0,12 + bl invalid_exception_router + load_all + eret +el0_irq_invalid_32: + save_all + mov x0,13 + bl invalid_exception_router + load_all + eret +el0_fiq_invalid_32: + save_all + mov x0,14 + bl invalid_exception_router + load_all + eret +el0_error_invalid_32: + save_all + mov x0,15 + bl invalid_exception_router + load_all + eret diff --git a/lab7/kernel/src/exception.c b/lab7/kernel/src/exception.c new file mode 100644 index 000000000..2224915ab --- /dev/null +++ b/lab7/kernel/src/exception.c @@ -0,0 +1,376 @@ +#include "bcm2837/rpi_irq.h" +#include "bcm2837/rpi_uart1.h" +#include "uart1.h" +#include "exception.h" +#include "timer.h" +#include "syscall.h" +#include "signal.h" +#include "sched.h" +#include "memory.h" +#include "irqtask.h" +#include "mmu.h" +#include "debug.h" + +// 讀取ESR_EL1暫存器的值 +static inline unsigned long read_esr_el1(void) +{ + unsigned long value; + asm volatile("mrs %0, esr_el1" : "=r"(value)); + return value; +} + +// 判斷異常是否由EL0觸發的syscall +static inline int is_el0_syscall() +{ + unsigned long esr_el1 = read_esr_el1(); + unsigned long ec = (esr_el1 >> ESR_EL1_EC_SHIFT) & ESR_EL1_EC_MASK; + if (ec == ESR_EL1_EC_SVC64) + { + return 1; + } + return 0; +} + +// 定義異常類型名稱 +const char *exception_type[] = { + "Unknown reason", + "Trapped WFI or WFE instruction execution", + "Trapped MCR or MRC access with (coproc==0b1111) (AArch32)", + "Trapped MCRR or MRRC access with (coproc==0b1111) (AArch32)", + "Trapped MCR or MRC access with (coproc==0b1110) (AArch32)", + "Trapped LDC or STC access (AArch32)", + "Trapped FP access", + "Trapped VMRS access", + "Trapped PSTATE (AArch32)", + "Instruction Abort from a lower Exception level", + "Instruction Abort taken without a change in Exception level", + "PC alignment fault", + "Data Abort from a lower Exception level", + "Data Abort taken without a change in Exception level", + "SP alignment fault", + "Trapped floating-point exception", + "SError interrupt", + "Breakpoint from a lower Exception level", + "Breakpoint taken without a change in Exception level", + "Software Step from a lower Exception level", + "Software Step taken without a change in Exception level", + "Watchpoint from a lower Exception level", + "Watchpoint taken without a change in Exception level", + "BKPT instruction execution (AArch32)", + "Vector Catch exception (AArch32)", + "BRK instruction execution (AArch64)"}; + +extern list_head_t *run_queue; + +// DAIF, Interrupt Mask Bits +void el1_interrupt_enable() +{ + __asm__ __volatile__("msr daifclr, 0xf"); // umask all DAIF +} + +void el1_interrupt_disable() +{ + __asm__ __volatile__("msr daifset, 0xf"); // mask all DAIF +} + +unsigned long long int lock_counter = 0; + +void lock() +{ + el1_interrupt_disable(); + lock_counter++; +} + +void unlock() +{ + // uart_sendlinek("This is unlock\n"); + lock_counter--; + if (lock_counter < 0) + { + while (1) + ; + } + else if (lock_counter == 0) + { + el1_interrupt_enable(); + } +} + +void el1h_sync_router(trapframe_t *tpf) +{ + uart_sendlinek("\n"); + uart_sendlinek("spsr_el1 : %x\n ", tpf->spsr_el1); + uart_sendlinek("elr_el1 : %x\n ", tpf->elr_el1); + uart_sendlinek("sp_el0 : %x\n ", tpf->sp_el0); + // dump_vma(); + while (1) + ; +} + +void el1h_irq_router(trapframe_t *tpf) +{ + lock(); + // decouple the handler into irqtask queue + // (1) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.113 + // (2) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.16 + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + if (*AUX_MU_IER_REG & 2) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); // run the queued task before returning to the program. + } + else if (*AUX_MU_IER_REG & 1) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) // from CNTPNS (core_timer) // A1 - setTimeout run in el1 + { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + core_timer_enable(); + + if (list_size(run_queue) > 1) + { + // uart_sendlinek("el1h_irq_router\n"); + schedule(); + } + } + else + { + unlock(); + uart_sendlinek("Hello World el1 64 router other interrupt!\r\n"); + } + + // only do signal handler when return to user mode + if ((tpf->spsr_el1 & 0b1100) == 0) + { + check_signal(tpf); + } +} + +void el0_sync_router(trapframe_t *tpf) +{ + static int count = 0; + unsigned long long esr_el1 = read_esr_el1(); + + // esr_el1: Holds syndrome information for an exception taken to EL1. + esr_el1_t *esr = (esr_el1_t *)&esr_el1; + if (esr->ec == MEMFAIL_DATA_ABORT_LOWER || esr->ec == MEMFAIL_INST_ABORT_LOWER) + { + mmu_memfail_abort_handle(esr); + return; + } + + if (!is_el0_syscall()) + { + const char *exception_name = get_exception_name(esr_el1); + if (count == 0) + ERROR("el0_sync_router: exception occurred - %s\r\n", exception_name); + count++; + return; + } + + el1_interrupt_enable(); // Allow UART input during exception + unsigned long long syscall_no = tpf->x8; + if (syscall_no > 18) + { + uart_sendlinek("SYSCALL No: %d\n", syscall_no); + } + // uart_sendlinek("tpf->x8 : %d\n",tpf->x8); + switch (syscall_no) + { + SYSCALL(0, getpid(tpf)) + SYSCALL(1, uartread(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(2, uartwrite(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(3, exec(tpf, (char *)tpf->x0, (char **)tpf->x1)) + SYSCALL(4, fork(tpf)) + SYSCALL(5, exit(tpf, tpf->x0)) + SYSCALL(6, syscall_mbox_call(tpf, (unsigned char)tpf->x0, (unsigned int *)tpf->x1)) + SYSCALL(7, kill(tpf, (int)tpf->x0)) + SYSCALL(8, signal_register(tpf->x0, (void (*)())tpf->x1)) + SYSCALL(9, signal_kill(tpf->x0, tpf->x1)) + SYSCALL(10, mmap(tpf, (void *)tpf->x0, tpf->x1, tpf->x2, tpf->x3, tpf->x4, tpf->x5);) + SYSCALL(11, open(tpf, (char *)tpf->x0, tpf->x1);) + SYSCALL(12, close(tpf, tpf->x0);) + SYSCALL(13, write(tpf, tpf->x0, (char *)tpf->x1, tpf->x2);) + SYSCALL(14, read(tpf, tpf->x0, (char *)tpf->x1, tpf->x2);) + SYSCALL(15, mkdir(tpf, (char *)tpf->x0, tpf->x1);) + SYSCALL(16, mount(tpf, (char *)tpf->x0, (char *)tpf->x1, (char *)tpf->x2, tpf->x3, (void *)tpf->x4);) + SYSCALL(17, chdir(tpf, (char *)tpf->x0);) + SYSCALL(18, lseek64(tpf, tpf->x0, tpf->x1, tpf->x2);) + SYSCALL(19, ioctl(tpf, tpf->x0, tpf->x1, (void *)tpf->x2);) + SYSCALL(50, sigreturn(tpf)) + SYSCALL(114, syscall_lock(tpf)) + SYSCALL(514, syscall_unlock(tpf)) + default: + uart_sendlinek("el0_sync_router other syscall, syscall_no: %d\r\n", syscall_no); + break; + } +} + +void el0_irq_router(trapframe_t *tpf) +{ + lock(); + // decouple the handler into irqtask queue + // (1) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.113 + // (2) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.16 + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + if (*AUX_MU_IER_REG & 2) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); // run the queued task before returning to the program. + } + else if (*AUX_MU_IER_REG & 1) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) // from CNTPNS (core_timer) // A1 - setTimeout run in el1 + { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + core_timer_enable(); + + if (list_size(run_queue) > 1) + { + // uart_sendlinek("el0_irq_router\n"); + schedule(); + } + } + else + { + unlock(); + uart_sendlinek("Hello World el0 64 router other interrupt!\r\n"); + } + + // 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) +{ + uart_sendlinek("\n invalid exception %x \n", x0); + while (1) + ; +} + +// 獲取異常類型名稱 +const char *get_exception_name(unsigned long esr_el1) +{ + unsigned long ec = (esr_el1 >> ESR_EL1_EC_SHIFT) & ESR_EL1_EC_MASK; + if (ec < sizeof(exception_type) / sizeof(exception_type[0])) + { + return exception_type[ec]; + } + return "Unknown exception"; +} + +// ------------------------------------------------------------------------------------------ + +/* +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; // Small number has higher priority + +struct list_head *task_list; +void irqtask_list_init() +{ + task_list = allocator(sizeof(list_head_t)); + INIT_LIST_HEAD(task_list); +} + +void irqtask_add(void *task_function, unsigned long long priority) +{ + irqtask_t *the_task = kmalloc(sizeof(irqtask_t)); // free by irq_tasl_run_preemptive() + + // store all the related information into irqtask node + // manually copy the device's buffer + the_task->priority = priority; + the_task->task_function = task_function; + INIT_LIST_HEAD(&(the_task->listhead)); + + // add the timer_event into timer_event_list (sorted) + // if the priorities are the same -> FIFO + struct list_head *curr; + + // mask the device's interrupt line + // el1_interrupt_disable(); + // enqueue the processing task to the event queue with sorting. + list_for_each(curr, task_list) + { + if (((irqtask_t *)curr)->priority > the_task->priority) + { + list_add(&(the_task->listhead), curr->prev); + break; + } + } + // if the priority is lowest + if (list_is_head(curr, task_list)) + { + list_add_tail(&(the_task->listhead), task_list); + } + // unmask the interrupt line + // el1_interrupt_enable(); +} + +void irqtask_run_preemptive() +{ + // el1_interrupt_enable(); + while (!list_empty(task_list)) + { + // critical section protects new coming node + + lock(); + irqtask_t *the_task = (irqtask_t *)task_list->next; + // Run new task (early return) if its priority is lower than the scheduled task. + if (curr_task_priority <= the_task->priority) + { + unlock(); + break; + } + // get the scheduled task and run it. + list_del_entry((struct list_head *)the_task); + int prev_task_priority = curr_task_priority; + curr_task_priority = the_task->priority; + // uart_sendlinek("preemptive curr_task_priority: %d\r\n", curr_task_priority); + unlock(); + + irqtask_run(the_task); + + lock(); + curr_task_priority = prev_task_priority; + unlock(); + // free(the_task); + } +} + +void irqtask_run(irqtask_t *the_task) +{ + ((void (*)())the_task->task_function)(); + kfree(the_task); +} diff --git a/lab7/kernel/src/linker.ld b/lab7/kernel/src/linker.ld new file mode 100644 index 000000000..45b64f607 --- /dev/null +++ b/lab7/kernel/src/linker.ld @@ -0,0 +1,44 @@ +_heap_stack_size = 50M; + +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + _kernel_start = .; + + .text : { + *(.text.boot) + *(.text) + } + + .rodata : { + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + . = ALIGN(8); + _bss_start = .; + *(.bss) + *(COMMON) + } + _bss_end = .; + _bss_size = (_bss_end - _bss_start)>>3; + + .heap : { + . = ALIGN(8); + _heap_top = .; + . += _heap_stack_size; + _heap_end = .; + } + + _kernel_end = .; + + . = 0xffff00002c000000; + _stack_end = .; + . = 0xffff00003c000000; + _stack_top = .; +} \ No newline at end of file diff --git a/lab7/kernel/src/main.c b/lab7/kernel/src/main.c new file mode 100644 index 000000000..582faa2ff --- /dev/null +++ b/lab7/kernel/src/main.c @@ -0,0 +1,45 @@ +#include "uart1.h" +#include "shell.h" +#include "memory.h" +#include "dtb.h" +#include "exception.h" +#include "timer.h" +#include "sched.h" +#include "uart1.h" +#include "irqtask.h" +#include "vfs.h" +//#include "bcm2837/rpi_mmu.h" + +extern list_head_t *run_queue; +extern char *dtb_ptr; +int Set_dtb = 0; + +void main(char *arg) +{ + // uart_sendlinek("in main\n"); + dtb_ptr = (char *)PHYS_TO_KERNEL_VIRT(arg); + if (!Set_dtb) + { + traverse_device_tree(dtb_ptr, dtb_callback_initramfs); + Set_dtb = 1; + } + uart_init(); + allocator_init(); + rootfs_init(); + irqtask_list_init(); + timer_list_init(); + thread_sched_init(); + //uart_sendlinek("in main\n"); + + core_timer_enable(); + uart_interrupt_enable(); + uart_flush_FIFO(); + + el1_interrupt_enable(); // enable interrupt in EL1 -> EL1 + + //start_shell(); + // uart_sendlinek("RQ size : %d \n",list_size(run_queue)); + + //schedule(); + load_context(&((((thread_t*)run_queue->next))->context)); +} diff --git a/lab7/kernel/src/mbox.c b/lab7/kernel/src/mbox.c new file mode 100644 index 000000000..530412d28 --- /dev/null +++ b/lab7/kernel/src/mbox.c @@ -0,0 +1,23 @@ +#include "bcm2837/rpi_mbox.h" +#include "mbox.h" + +/* Aligned to 16-byte boundary while we have 28-bits for VC */ +volatile unsigned int __attribute__((aligned(16))) pt[36]; + +int mbox_call( mbox_channel_type channel, unsigned int value ) +{ + // Add channel to lower 4 bit + value &= ~(0xF); + value |= channel; + while ( (*MBOX_STATUS & BCM_ARM_VC_MS_FULL) != 0 ) {} + // Write to Register + *MBOX_WRITE = value; + while(1) { + while ( *MBOX_STATUS & BCM_ARM_VC_MS_EMPTY ) {} + // Read from Register + if (value == *MBOX_READ) + return pt[1] == MBOX_REQUEST_SUCCEED; + } + return 0; +} + diff --git a/lab7/kernel/src/memory.c b/lab7/kernel/src/memory.c new file mode 100644 index 000000000..a80a1219a --- /dev/null +++ b/lab7/kernel/src/memory.c @@ -0,0 +1,491 @@ +#include "memory.h" +#include "u_list.h" +#include "uart1.h" +#include "exception.h" +#include "dtb.h" +#include "stdio.h" +#include "mmu.h" +#include "bcm2837/rpi_mmu.h" + +extern char _heap_top; +static char *htop_ptr = &_heap_top; + +extern char _kernel_start; +extern char _kernel_end; +extern char *CPIO_DEFAULT_START; +extern char *CPIO_DEFAULT_END; +extern char _stack_end; +extern char _stack_top; + +void *allocator(unsigned int size) +{ + lock(); + // -> htop_ptr + // htop_ptr + 0x02: heap_block size + // htop_ptr + 0x10 ~ htop_ptr + 0x10 * k: + // { heap_block } + // -> htop_ptr + // header 0x10 bytes block + // |--------------------------------------------------------------| + // | fill zero 0x8 bytes | size 0x8 bytes | size padding to 0x16 | + // |--------------------------------------------------------------| + + // 0x10 for heap_block header + char *r = htop_ptr + 0x10; + // size paddling to multiple of 0x10 + size = 0x10 + size - size % 0x10; + *(unsigned int *)(r - 0x8) = size; + htop_ptr += size; + unlock(); + return r; +} + +// void free(void* ptr) { +// // TBD +// } + +// ------------------------------------------------------------ + +static frame_t *frame_array; // store memory's statement and page's corresponding index +static list_head_t frame_freelist[FRAME_MAX_IDX]; // store available block for page +static list_head_t cache_list[CACHE_MAX_IDX]; // store available block for cache + +void allocator_init() +{ + frame_array = allocator(BUDDY_MEMORY_PAGE_COUNT * sizeof(frame_t)); + + // init frame freelist + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&frame_freelist[i]); + } + + // init cache list + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&cache_list[i]); + } + + for (int i = 0; i < BUDDY_MEMORY_PAGE_COUNT; i++) + { + // init listhead for each frame + INIT_LIST_HEAD(&(frame_array[i].listhead)); + frame_array[i].idx = i; + } + freelist_init(); + dump_page_info(); + + /* Startup reserving the following region: + Spin tables for multicore boot (0x0000 - 0x1000) + Devicetree (Optional, if you have implement it) + Kernel image in the physical memory + Your simple allocator (startup allocator) (Stack + Heap in my case) + Initramfs + */ + uart_sendlinek("\r\n* Startup Allocation *\r\n"); + uart_sendlinek("buddy system: usable memory region: 0x%x ~ 0x%x\n", BUDDY_MEMORY_BASE, BUDDY_MEMORY_BASE + BUDDY_MEMORY_PAGE_COUNT * PAGESIZE); + dtb_find_and_store_reserved_memory(); // find spin tables in dtb + memory_reserve(PHYS_TO_KERNEL_VIRT(MMU_PGD_ADDR), PHYS_TO_KERNEL_VIRT(MMU_PTE_ADDR + 0x2000)); // // PGD's page frame at 0x1000 // PUD's page frame at 0x2000 PMD 0x3000-0x5000 + memory_reserve((unsigned long long)&_kernel_start, (unsigned long long)&_kernel_end); // kernel + memory_reserve((unsigned long long)&_stack_end, (unsigned long long)&_stack_top); + memory_reserve((unsigned long long)CPIO_DEFAULT_START, (unsigned long long)CPIO_DEFAULT_END); +} + +void freelist_init() +{ + int PAGE_COUNT = BUDDY_MEMORY_PAGE_COUNT; + for (int i = 0; i <= FRAME_IDX_FINAL; i++) + { + // uart_sendlinek("\n %ld,%ld \n",PAGE_COUNT,(1 << i)); + if ((PAGE_COUNT & (1 << i))) + { + PAGE_COUNT -= (1 << i); + frame_array[PAGE_COUNT].val = i; + frame_array[PAGE_COUNT].used = FRAME_FREE; + list_add(&(frame_array[PAGE_COUNT].listhead), &frame_freelist[i]); + } + if (i == FRAME_IDX_FINAL && PAGE_COUNT) + { + while (PAGE_COUNT) + { + PAGE_COUNT -= (1 << i); + frame_array[PAGE_COUNT].val = FRAME_IDX_FINAL; + frame_array[PAGE_COUNT].used = FRAME_FREE; + list_add(&(frame_array[PAGE_COUNT].listhead), &frame_freelist[FRAME_IDX_FINAL]); + } + } + } +} + +frame_t *release_redundant(frame_t *frame) +{ + // order -1 -> add its buddy to free list (frame itself will be used in master function) + frame->val -= 1; + frame_t *buddyptr = get_buddy(frame); + buddyptr->val = frame->val; + buddyptr->used = FRAME_FREE; + list_add(&(buddyptr->listhead), &frame_freelist[buddyptr->val]); + return frame; +} + +frame_t *get_buddy(frame_t *frame) +{ + // XOR(idx, order) + if ((frame->idx ^ (1 << frame->val)) > BUDDY_MEMORY_PAGE_COUNT) + { + uart_sendlinek("[!] BUDDY of Page: 0x%x at level: %d Does not exit", frame->idx, frame->val); + // return -1; + } + return &frame_array[frame->idx ^ (1 << frame->val)]; +} + +void dump_page_info() +{ + // unsigned int exp2 = 1; + // uart_sendlinek(" ┌───────────────────── [ Number of Available Page Blocks ] ─────────────────────┐\r\n │ "); + // for (int i = FRAME_IDX_0; i < FRAME_IDX_8; i++) + // { + // uart_sendlinek("%4dKB(%1d) ", 4 * exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │ "); + // for (int i = FRAME_IDX_0; i < FRAME_IDX_8; i++) + // uart_sendlinek(" %4d ", list_size(&frame_freelist[i])); + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└────────────────────────────────────────"); + // uart_sendlinek("─────────────────────────────────────────┘\r\n"); + + // exp2 = 1; + // uart_sendlinek(" ┌──────────────────────────────────── "); + // uart_sendlinek("[ Number of Available Page Blocks ]"); + // uart_sendlinek(" ───────────────────────────────────┐\r\n │"); + // for (int i = FRAME_IDX_8; i < FRAME_MAX_IDX; i++) + // { + // uart_sendlinek("%4dMB(%2d) ", exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │"); + // for (int i = FRAME_IDX_8; i < FRAME_MAX_IDX; i++) + // uart_sendlinek(" %4d ", list_size(&frame_freelist[i])); + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└──────────────────────────────────────────────────────"); + // uart_sendlinek("────────────────────────────────────────────────────────┘\r\n"); +} + +void dump_cache_info() +{ + // unsigned int exp2 = 1; + // uart_sendlinek(" ┌──────────────── [ Number of Available Cache Blocks ] ────────────────┐\r\n │ "); + // for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + // { + // uart_sendlinek("%4dB(%1d) ", CACHE_SEG * exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │ "); + // ; + // for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + // { + // if (!list_empty(&cache_list[i])) + // { + // // uart_sendlinek("I free !!!!!!!!!!!!"); + // int num = 0; + // list_head_t *pos; + // list_for_each(pos, &cache_list[i]) + // { + // num += ((cache_t *)pos)->available; + // } + // uart_sendlinek("%5d ", num); + // } + // else + // { + // uart_sendlinek("%5d ", 0); + // } + // } + + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└───────────────────────────────────"); + // uart_sendlinek("────────────────────────────────────┘\r\n"); +} + +void memory_reserve(unsigned long long start, unsigned long long end) +{ + start -= start % PAGESIZE; // floor (align 0x1000) + end = end % PAGESIZE ? end + PAGESIZE - (end % PAGESIZE) : end; // ceiling (align 0x1000) + + // uart_sendlinek("Reserved Memory: "); + // uart_sendlinek("start 0x%x ~ ", start); + // uart_sendlinek("end 0x%x\r\n", end); + + // delete page from free list + for (int order = FRAME_IDX_FINAL; order >= 0; order--) + { + list_head_t *pos; + // uart_sendlinek("\n use %d level page to match \n",order); + list_for_each(pos, &frame_freelist[order]) + { + unsigned long long pagestart = ((frame_t *)pos)->idx * PAGESIZE + BUDDY_MEMORY_BASE; + unsigned long long pageend = pagestart + (PAGESIZE << order); + // uart_sendlinek("\n from 0x%x to 0x%x\n",pagestart,pageend); + + if (start <= pagestart && end >= pageend) // if page all in reserved memory -> delete it from freelist + { + ((frame_t *)pos)->used = FRAME_ALLOCATED; + // uart_sendlinek(" [!] Reserved page in 0x%x - 0x%x\n", pagestart, pageend); + // uart_sendlinek(" Before\n"); + // dump_page_info(); + list_del_entry(pos); + // uart_sendlinek(" Remove usable block for reserved memory: order %d\r\n", order); + // uart_sendlinek(" After\n"); + // dump_page_info(); + } + else if (start >= pageend || end <= pagestart) // no intersection + { + continue; + } + else // partial intersection, separate the page into smaller size. + { + // dump_page_info(); + list_del_entry(pos); + list_head_t *temppos = pos->prev; + list_add(&release_redundant((frame_t *)pos)->listhead, &frame_freelist[order - 1]); + pos = temppos; + // dump_page_info(); + } + } + } +} + +void *kmalloc(unsigned int size) +{ + lock(); + + // uart_sendlinek("\n\n"); + // uart_sendlinek("================================\r\n"); + // uart_sendlinek("[+] Request kmalloc size: %d\r\n", size); + // uart_sendlinek("================================\r\n"); + // if size is larger than cache size, go for page + if (size > (CACHE_SEG << CACHE_IDX_FINAL)) + { + void *r = page_malloc(size); + unlock(); + return r; + } + // go for cache + void *r = cache_malloc(size); + unlock(); + return r; +}; + +void *page_malloc(unsigned int size) +{ + // uart_sendlinek("this is page_malloc \r\n"); + int val; + int find_PageSize = 0; + frame_t *target_frame_ptr; + // turn size into minimum 4KB * 2**val + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + + if (size <= (PAGESIZE << i) && !find_PageSize) + { + val = i; + // uart_sendlinek(" block size = 0x%x\n", PAGESIZE << i); + find_PageSize = 1; + } + + if (find_PageSize && !list_empty(&frame_freelist[i])) + { + // uart_sendlinek(" free page at level : %d\n", i); + target_frame_ptr = (frame_t *)(frame_freelist[i].next); + for (int min_FreePageLevel = i; min_FreePageLevel > val; min_FreePageLevel--) // ex: 10000 -> 01111 + { + target_frame_ptr = release_redundant(target_frame_ptr); + } + break; + } + + if (i == FRAME_IDX_FINAL) + { + // uart_sendlinek("[!] request size exceeded for page_malloc!!!!\r\n"); + return (void *)0; + } + } + // get the available frame from freelist + target_frame_ptr->used = FRAME_ALLOCATED; + + // uart_sendlinek(" [+] Allocate page - size : %d(0x%x)\r\n", size, size); + // uart_sendlinek(" Before\r\n"); + // dump_page_info(); + list_del_entry((struct list_head *)target_frame_ptr); + // uart_sendlinek(" physical address : 0x%x\n", BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx))); + // uart_sendlinek(" After\r\n"); + // dump_page_info(); + + return (void *)BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx)); +}; + +void *cache_malloc(unsigned int size) +{ + int c_val; + // uart_sendlinek("this is cache_malloc \r\n"); + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + if (size <= (CACHE_SEG << i)) + { + c_val = i; + break; + } + } + + if (list_empty(&cache_list[c_val])) + { + // uart_sendlinek("[!] No free size for cache \r\n"); //--------------------------------------------------- + page2caches(c_val); + } + + // cache_t *ptr = (cache_t *)(cache_list[c_val].prev); + + // uart_sendlinek("this is cache_malloc \r\n"); + // uart_sendlinek("[+] Allocate cache - size : %d(0x%x)\r\n", size, size); + // uart_sendlinek(" Before\r\n"); + // dump_cache_info(); + + void *ptr = find_CACHE((cache_t *)cache_list[c_val].next); + // uart_sendlinek("ptr : %x\n",ptr); + // uart_sendlinek(" After\r\n"); + // dump_cache_info(); + + return ptr; +}; + +void *find_CACHE(cache_t *ptr) +{ + // uart_sendlinek("ptr : %x\n",ptr); + unsigned long long num; + int record_num; + for (record_num = 0; ptr->cache_record[record_num] + 1 == 0 && record_num < CACHE_record_num; record_num++) + ; + + num = ptr->cache_record[record_num] + 1; + num = ((~num) & (ptr->cache_record[record_num])) + 1; + + // uart_sendlinek("before cache_record[%d] : %x\n", record_num, ptr->cache_record[record_num]); + ptr->cache_record[record_num] += num; + ptr->available--; + // uart_sendlinek("after cache_record[%d] : %x\n", record_num, ptr->cache_record[record_num]); + + int val = fake_log2(num) + record_num * 64; + void *target = ptr->data_base + val * (CACHE_SEG << (ptr->cache_order)); + // uart_sendlinek("data_base addr : 0x%x\n", ptr->data_base); + // uart_sendlinek("target addr : 0x%x\n", target); + + if (!(ptr->available)) + { + list_del_entry((list_head_t *)ptr); + } + + return target; +} + +void page2caches(int c_val) +{ + // uart_sendlinek("[!] Split Page for cache\r\n"); + void *ptr = page_malloc(PAGESIZE); + // uart_sendlinek("ptr : %x\n",ptr); + cache_t *Pageinfo = (cache_t *)ptr; + Pageinfo->data_base = ptr; + Pageinfo->cache_order = c_val; + Pageinfo->max_available = (PAGESIZE >> (CACHE_offset + c_val)); + + while (sizeof(*Pageinfo) >= Pageinfo->data_base - ptr) + { + Pageinfo->data_base += (CACHE_SEG << c_val); + (Pageinfo->max_available)--; + } + Pageinfo->available = Pageinfo->max_available; + + for (int i = 0; i < CACHE_record_num; i++) + Pageinfo->cache_record[i] = 0; + + // uart_sendlinek(" max_available : %d\n", Pageinfo->max_available); + // uart_sendlinek(" Pageinfo size : 0x%x\n", sizeof(*Pageinfo)); + // uart_sendlinek(" offset size : 0x%x\n", (CACHE_SEG << c_val)); + + list_add(&(Pageinfo->listhead), &cache_list[c_val]); + // return Pageinfo; +} + +//------------------------------------------------------------------------------------------------------------------------------- kfree + +void kfree(void *ptr) +{ + // uart_sendlinek("\r\n"); + // uart_sendlinek("==========================\r\n"); + //uart_sendlinek("[+] Request kfree 0x%x\r\n", ptr); + // uart_sendlinek("==========================\r\n"); + + // If no cache assigned, go for page + lock(); + frame_t *target_frame_ptr = &frame_array[((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12]; + if ((unsigned long long)ptr % PAGESIZE == 0) + { + page_free(target_frame_ptr); + unlock(); + return; + } + // go for cache + cache_free(ptr); + unlock(); +}; + +void page_free(frame_t *target_frame_ptr) +{ + frame_t *buddyptr = get_buddy(target_frame_ptr); + target_frame_ptr->used = FRAME_FREE; + while (buddyptr->used == FRAME_FREE && buddyptr->val == target_frame_ptr->val) + { + list_del_entry((list_head_t *)buddyptr); + target_frame_ptr = &frame_array[(target_frame_ptr->idx) & (buddyptr->idx)]; + target_frame_ptr->val++; + buddyptr = get_buddy(target_frame_ptr); + if (buddyptr < 0) + break; + } + + // uart_sendlinek(" Before\r\n"); + // dump_page_info(); + list_add(&(target_frame_ptr->listhead), &frame_freelist[target_frame_ptr->val]); + // uart_sendlinek(" After\r\n"); + // dump_page_info(); +} + +void cache_free(void *ptr) +{ + // uart_sendlinek("ptr : %x\n",ptr); + int idx = ((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12; + frame_t *target_frame_ptr = &frame_array[idx]; + cache_t *cache_ptr = (cache_t *)((unsigned long long)(idx * PAGESIZE) + BUDDY_MEMORY_BASE); //<------- + // uart_sendlinek("target_frame_ptr : %x\n",target_frame_ptr); + //uart_sendlinek("cache_ptr : %x\n",cache_ptr); + int num = (ptr - cache_ptr->data_base) / (CACHE_SEG << cache_ptr->cache_order); + //uart_sendlinek("num : %d\n",num); + int record_num = (num >> 6); + num = num - (record_num << 6); + cache_ptr->cache_record[record_num] -= (1 << num); + + // uart_sendlinek("[+] Free cache: 0x%x, val = %d\r\n", ptr, cache_ptr->cache_order); + // uart_sendlinek(" Before\r\n"); + // dump_cache_info(); + if (cache_ptr->available == 0) + { + list_add(&(cache_ptr->listhead), &cache_list[cache_ptr->cache_order]); + } + cache_ptr->available++; + if (cache_ptr->available == cache_ptr->max_available) + { + list_del_entry((list_head_t *)cache_ptr); + page_free(target_frame_ptr); + } + // uart_sendlinek(" After\r\n"); + // dump_cache_info(); +} \ No newline at end of file diff --git a/lab7/kernel/src/mmu.c b/lab7/kernel/src/mmu.c new file mode 100644 index 000000000..95e3bde7f --- /dev/null +++ b/lab7/kernel/src/mmu.c @@ -0,0 +1,380 @@ +#include "bcm2837/rpi_mmu.h" +#include "mmu.h" +#include "memory.h" +#include "string.h" +#include "uart1.h" +#include "debug.h" + +extern thread_t *curr_thread; + +void *set_2M_kernel_mmu(void *x0) +{ + // Turn + // Two-level Translation (1GB) - in boot.S + // to + // Three-level Translation (2MB) - set PUD point to new table + unsigned long *pud_table = (unsigned long *)MMU_PUD_ADDR; + + unsigned long *pte_table1 = (unsigned long *)MMU_PTE_ADDR; + unsigned long *pte_table2 = (unsigned long *)(MMU_PTE_ADDR + 0x1000L); + for (int i = 0; i < 512; i++) + { + unsigned long addr = 0x200000L * i; + if (addr >= PERIPHERAL_END) + { + pte_table1[i] = (0x00000000 + addr) + BOOT_PTE_ATTR_nGnRnE; + continue; + } + pte_table1[i] = (0x00000000 + addr) | BOOT_PTE_ATTR_NOCACHE; // 0 * 2MB // No definition for 3-level attribute, use nocache. + pte_table2[i] = (0x40000000 + addr) | BOOT_PTE_ATTR_NOCACHE; // 512 * 2MB + } + + // set PUD + pud_table[0] = (unsigned long)pte_table1 | BOOT_PUD_ATTR; + pud_table[1] = (unsigned long)pte_table2 | BOOT_PUD_ATTR; + + return x0; +} + +void map_one_page(size_t *virt_pgd_p, size_t user_va, size_t pa, size_t flag) +{ + size_t *table_p = virt_pgd_p; + for (int level = 0; level < 4; level++) + { + unsigned int idx = (user_va >> (39 - level * 9)) & 0x1ff; // p.14, 9-bit only + + if (level == 3) + { + table_p[idx] = pa; + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_KNX | flag; // el0 only + return; + } + + if (!table_p[idx]) + { + size_t *newtable_p = kmalloc(0x1000); // create a table + memset(newtable_p, 0, 0x1000); + table_p[idx] = KERNEL_VIRT_TO_PHYS((size_t)newtable_p); // point to that table + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + table_p = (size_t *)PHYS_TO_KERNEL_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); // PAGE_SIZE + } +} + +void mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced, vma_name_type name) +{ + if (IS_NOT_ALIGN(pa, PAGESIZE) || IS_NOT_ALIGN(va, PAGESIZE)) + { + // ERROR("CHECK_ALIGN : 0x%x\n",CHECK_ALIGN(pa,PAGESIZE)); + uart_sendlinek("\n\n"); + ERROR("Input User Vitural Address or Physical Address Should be Aliged to PAGESIZE\n"); + ERROR("Input User Vitural Address : 0x%x\n", va); + ERROR("Input Physical Address : 0x%x\n", pa); + return; + } + vm_area_struct_t *the_area_ptr = check_vma_overlap(t, va, (unsigned long)size); + if (the_area_ptr != 0) + { + uart_sendlinek("\n\n"); + ERROR("check_vma_overlap : 0x%x\n", the_area_ptr); + ERROR("Vitural Memory Area is Overlap !!\n"); + // dump_vma(); + return; + } + + size = ALIGN_UP(size, PAGESIZE); + 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; + new_area->name = name; + list_add_tail((list_head_t *)new_area, &t->vma_list); +} + +void mmu_del_vma(struct thread *t) +{ + list_head_t *curr = &t->vma_list; + list_head_t *n; + list_for_each_safe(curr, n, &t->vma_list) + { + vm_area_struct_t *vma = (vm_area_struct_t *)curr; + if (vma->is_alloced) + { + kfree((void *)PHYS_TO_KERNEL_VIRT(vma->phys_addr)); + } + list_del_entry(curr); + kfree(curr); + } +} + +void mmu_free_page_tables(size_t *page_table, int level) +{ + size_t *table_virt = (size_t *)PHYS_TO_KERNEL_VIRT(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) == PD_TABLE) + { + if (level < PMD) + mmu_free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + kfree((void *)PHYS_TO_KERNEL_VIRT(next_table)); + } + } + } +} + +// void mmu_set_PTE_readonly(size_t *page_table, int level) +// { +// size_t *table_virt = (size_t *)PHYS_TO_KERNEL_VIRT(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 == PD_TABLE) +// { +// if (level < PMD) +// { +// mmu_set_PTE_readonly(next_table, level + 1); +// } +// else +// { +// table_virt[i] &= PD_RDONLY; +// } +// } +// } +// } +// } + +// void mmu_pagetable_copy(size_t *dst_page_table, size_t *src_page_table, int level) +// { +// size_t *dst_page_table_va = (size_t *)PHYS_TO_KERNEL_VIRT(dst_page_table); +// size_t *src_page_table_va = (size_t *)PHYS_TO_KERNEL_VIRT(src_page_table); +// for (int i = 0; i < 512; i++) +// { +// if (src_page_table_va[i] != 0) +// { +// size_t *next_src_table = (size_t)(src_page_table_va[i] & ENTRY_ADDR_MASK); +// size_t *next_dst_table = kmalloc(PAGESIZE); +// memset(next_dst_table, 0, 0x1000); +// dst_page_table_va[i] = KERNEL_VIRT_TO_PHYS((size_t)next_dst_table); // point to that table +// dst_page_table_va[i] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); +// // size_t *next_dst_table = (size_t)(dst_page_table_va[i] & ENTRY_ADDR_MASK); +// if (src_page_table_va[i] & PD_TABLE == PD_TABLE) +// { +// if (level < PTE) +// { +// mmu_pagetable_copy(next_dst_table,next_src_table,level); +// } +// else +// { +// //-------- +// } +// } +// } +// } +// } + +void mmu_memfail_abort_handle(esr_el1_t *esr_el1) +{ + lock(); + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t" : "=r"(far_el1)); + + list_head_t *pos; + vm_area_struct_t *vma; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + vma = (vm_area_struct_t *)pos; + if (vma->virt_addr <= far_el1 && vma->virt_addr + vma->area_size >= far_el1) + { + the_area_ptr = vma; + break; + } + } + // area is not part of process's address space + if (!the_area_ptr) + { + uart_sendlinek("\n\n"); + ERROR("[Segmentation fault]: Kill Process\r\n"); + ERROR("Invilad Vitural Address Access: %x\n", far_el1); + thread_exit(); + // dump_vma(); + unlock(); + while (1) + schedule(); + + return; + } + + // For translation fault, only map one page frame for the fault address + 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_sendlinek("\n"); + // WARING("[Translation fault]: 0x%x\r\n", far_el1); // far_el1: Fault address register. + // Holds the faulting Virtual Address for all synchronous Instruction or Data Abort, PC alignment fault and Watchpoint exceptions that are taken to 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); + addr_offset = ALIGN_DOWN(addr_offset, 0x1000); + + size_t flag = 0; + if (!(the_area_ptr->rwx & (0b1 << 2))) + flag |= PD_UNX; // 4: executable + if (!(the_area_ptr->rwx & (0b1 << 1))) + flag |= PD_RDONLY; // 2: writable + if (the_area_ptr->rwx & (0b1 << 0)) + flag |= PD_UK_ACCESS; // 1: readable / accessible + map_one_page((size_t *)PHYS_TO_KERNEL_VIRT(curr_thread->context.pgd), the_area_ptr->virt_addr + addr_offset, the_area_ptr->phys_addr + addr_offset, flag); + // dump_pagetable(the_area_ptr->virt_addr + addr_offset,the_area_ptr->phys_addr + addr_offset); + } + else + { + // For other Fault (permisson ...etc) + // uart_sendlinek("[Other Fault]: Kill Process\r\n"); + // uart_sendlinek("esr_el1: 0x%x\r\n", esr_el1); + uart_sendlinek("\n\n"); + if ((unsigned long)esr_el1 & (1 << 10)) + { + ERROR("[Permission Fault] due to a write of an Allocation Tag to Canonically Tagged memory.\r\n"); + } + if ((unsigned long)esr_el1 & (1 << 9)) + { + ERROR("[Permission Fault] due to the NoTagAccess memory attribute..\r\n"); + } + // check_permission(,the_area_ptr->rwx); + thread_exit(); + // dump_vma(); + unlock(); + while (1) + schedule(); + } + unlock(); +} + +vm_area_struct_t *check_vma_overlap(thread_t *t, unsigned long user_va, unsigned long size) +{ + list_head_t *pos; + vm_area_struct_t *vma; + list_for_each(pos, &t->vma_list) + { + vma = (vm_area_struct_t *)pos; + // Detect existing vma overlapped + if (!(vma->virt_addr >= (unsigned long)(user_va + size) || vma->virt_addr + vma->area_size <= (unsigned long)user_va)) + { + return vma; + } + } + return 0; +} + +int check_permission(int userId, int VMA_Permission) +{ + switch (PERMISSION_INVAILD(userId, VMA_Permission)) + { + DUMP_NAME(1, "Read is Invaild in This Vitural Memory Area") + DUMP_NAME(2, "Write is Invaild in This Vitural Memory Area") + DUMP_NAME(3, "Write & Read is Invaild in This Vitural Memory Area") + DUMP_NAME(4, "Exec is Invaild in This Vitural Memory Area") + DUMP_NAME(5, "Exec & Read is Invaild in This Vitural Memory Area") + DUMP_NAME(6, "Exec & Write is Invaild in This Vitural Memory Area") + DUMP_NAME(7, "Exec & Write & Read is Invaild in This Vitural Memory Area") + default: + uart_sendlinek("[other Fault]: UNKNOW FAULT"); + break; + } + return PERMISSION_INVAILD(userId, VMA_Permission); +} + +void dump_vma() +{ + uart_sendlinek(" +--------------------------+\n"); + uart_sendlinek(" | DUMP Vitural Memory Area |\n"); + uart_sendlinek(" +--------------------------+\n"); + list_head_t *pos; + list_for_each(pos, &curr_thread->vma_list) + { + uart_sendlinek("===============================================\n"); + vm_area_struct_t *vma_ptr = (vm_area_struct_t *)pos; + + uart_sendlinek("Vitural Memory Area Name : "); + switch (vma_ptr->name) + { + DUMP_NAME(UNKNOW_AREA, "UNKNOW_AREA") + DUMP_NAME(USER_DATA, "USER_DATA") + DUMP_NAME(USER_STACK, "USER_STACK") + DUMP_NAME(PERIPHERAL, "PERIPHERAL") + DUMP_NAME(USER_SIGNAL_WRAPPER, "USER_SIGNAL_WRAPPER") + DUMP_NAME(USER_EXEC_WRAPPER, "USER_EXEC_WRAPPER") + default: + uart_sendlinek("unnamed: %d\n", vma_ptr->name); + break; + } + uart_sendlinek("Base Vitural Address : 0x%x\n", ((vm_area_struct_t *)pos)->virt_addr); + uart_sendlinek("Base Physical Address : 0x%x\n", ((vm_area_struct_t *)pos)->phys_addr); + uart_sendlinek("Area Size : 0x%x\n", ((vm_area_struct_t *)pos)->area_size); + uart_sendlinek("Exec, Write, Read : 0x%x\n", ((vm_area_struct_t *)pos)->rwx); + uart_sendlinek("===============================================\n\n"); + } +} + +void dump_pagetable(unsigned long user_va, unsigned long pa) +{ + uart_sendlinek(" +---------------------------+\n"); + uart_sendlinek(" | DUMP PAGE TABLE & ADDRESS |\n"); + uart_sendlinek(" +---------------------------+\n"); + unsigned long *pagetable_pa = curr_thread->context.pgd; + unsigned long *pagetable_kernel_va = (unsigned long *)PHYS_TO_KERNEL_VIRT(pagetable_pa); + + uart_sendlinek("===============================================\n"); + uart_sendlinek("User Physical Address : 0x%x\n", pa); + uart_sendlinek("User Vitural Address : 0x%x\n", user_va); + unsigned long offset = user_va & 0xFFF; + uart_sendlinek("Vitural Address offset: 0x%x\n", offset); + for (int level = 0; level < 4; level++) + { + uart_sendlinek("-----------------------------------------------\n"); + uart_sendlinek("PAGE TABLE : "); + switch (level) + { + DUMP_NAME(PGD, "PGD") + DUMP_NAME(PUD, "PUD") + DUMP_NAME(PMD, "PMD") + DUMP_NAME(PTE, "PTE") + + default: + uart_sendlinek("unnamed: %d\n", level); + break; + } + uart_sendlinek("Pagetable Physical Address: 0x%x\n", pagetable_pa); + uart_sendlinek("Pagetable Vitural Address: 0x%x\n", pagetable_kernel_va); + + unsigned int idx = (user_va >> (39 - level * 9)) & 0x1FF; + uart_sendlinek("Index of Pagetable: 0x%x\n", idx); + uart_sendlinek("Entry %x of Pagetable: 0x%x\n", idx, pagetable_kernel_va[idx]); + if (level == PTE) + { + pagetable_pa = (unsigned long *)(pagetable_kernel_va[idx] & ENTRY_ADDR_MASK); + uart_sendlinek("The Page Base Physical Address: 0x%x\n", pagetable_pa); + uart_sendlinek("The Physical Address: 0x%x\n", (unsigned long)pagetable_pa | offset); + } + else + { + pagetable_pa = (unsigned long *)(pagetable_kernel_va[idx] & ENTRY_ADDR_MASK); + uart_sendlinek("next Pagetable Physical Address: 0x%x\n", pagetable_pa); + pagetable_kernel_va = (unsigned long *)PHYS_TO_KERNEL_VIRT(pagetable_pa); + uart_sendlinek("next Pagetable Vitural Address: 0x%x\n", pagetable_kernel_va); + } + uart_sendlinek("-----------------------------------------------\n"); + } +} \ No newline at end of file diff --git a/lab7/kernel/src/sched.S b/lab7/kernel/src/sched.S new file mode 100644 index 000000000..7d0f93cd2 --- /dev/null +++ b/lab7/kernel/src/sched.S @@ -0,0 +1,57 @@ +.global switch_to +switch_to: // (prev, next) = (x0, x1) + stp x19, x20, [x0, 16 * 0] // store callee saved register + 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] // store sp, fp, lr + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] // load callee saved register + 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] // load sp, fp, lr + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 // update current thread id + + dsb ish // ensure write has completed, lock for context switch + msr ttbr0_el1, x0 // switch translation based address. + tlbi vmalle1is // invalidate all TLB entries in stage 1, el1 and inner shareable + dsb ish // ensure completion of TLB invalidatation + isb // clear pipeline + + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 // get the current thread id + 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 diff --git a/lab7/kernel/src/sched.c b/lab7/kernel/src/sched.c new file mode 100644 index 000000000..09bbbcd17 --- /dev/null +++ b/lab7/kernel/src/sched.c @@ -0,0 +1,270 @@ +#include "sched.h" +#include "uart1.h" +#include "exception.h" +#include "memory.h" +#include "timer.h" +#include "shell.h" +#include "signal.h" +#include "stdio.h" +#include "mmu.h" +#include "string.h" + +list_head_t *run_queue; +execfile c_execfile; + +thread_t threads[PIDMAX + 1]; +thread_t *curr_thread; + +int pid_history = 0; +int timer_sched_flag = 0; +int shell_flag = 0; + +void thread_sched_init() +{ + lock(); + // init thread freelist and run_queue + run_queue = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + + // init pids + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].isused = 0; + threads[i].pid = i; + threads[i].iszombie = 0; + } + + thread_t *idlethread = thread_create(start_shell); + curr_thread = idlethread; + asm volatile("msr tpidr_el1, %0" ::"r"(&curr_thread->context)); // Don't let thread structure NULL as we enable the functionality + + // thread_create(idle); + unlock(); +} + +thread_t *thread_create(void *start) +{ + lock(); + thread_t *r; + // find usable PID, don't use the previous one + if (pid_history > PIDMAX) + { + unlock(); + return 0; + } + + if (!threads[pid_history].isused) + { + r = &threads[pid_history]; + pid_history += 1; + } + else + { + unlock(); + return 0; + } + + INIT_LIST_HEAD(&r->vma_list); + r->iszombie = 0; + r->isused = 1; + r->stack_alloced_ptr = kmalloc(USTACK_SIZE); + r->kernel_stack_alloced_ptr = kmalloc(KSTACK_SIZE); + r->context.lr = (unsigned long long)start; + r->context.sp = (unsigned long long)r->kernel_stack_alloced_ptr + KSTACK_SIZE - STACK_BASE_OFFSET; + r->context.fp = r->context.sp; // frame pointer for local variable, which is also in stack. + + strcpy(r->curr_working_dir, "/"); // new <----------------------------------------------------------------------- + vfs_open("/dev/uart", 0, &r->file_descriptors_table[stdin]); + vfs_open("/dev/uart", 0, &r->file_descriptors_table[stdout]); + vfs_open("/dev/uart", 0, &r->file_descriptors_table[stderr]); + + r->context.pgd = kmalloc(0x1000); + memset(r->context.pgd, 0, 0x1000); + r->context.pgd = (void *)KERNEL_VIRT_TO_PHYS(r->context.pgd); + + r->signal_is_checking = 0; + // initial all signal handler with signal_default_handler (kill thread) + + // uart_sendlinek("signal_default_handler : 0x%x\n", signal_default_handler); + for (int i = 0; i < SIGNAL_MAX; i++) + { + r->signal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add_tail(&r->listhead, run_queue); + unlock(); + return r; +} + +void schedule() +{ + lock(); + // uart_sendlinek("Run queue size :%d \n", list_size(run_queue)); + do + { + // uart_sendlinek("Run queue size :%d \n", list_size(run_queue)); + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue)); // find a runnable thread + unlock(); + // uart_sendlinek("curr_thread :%d \n",curr_thread->pid); + // uart_sendlinek("curr_thread->context.lr :0x%x \n",curr_thread->context.lr); + switch_to(get_current(), &curr_thread->context); +} + +void idle() +{ + // uart_sendlinek("This is idle\n"); + while (shell_flag > 0) + // while (list_size(run_queue) > 1) + { + // uart_sendlinek("This is idle\n"); + kill_zombies(); // reclaim threads marked as DEAD + schedule(); // switch to next thread in run queue + // delay(10000); + } + kill_zombies(); +} + +void thread_exit() +{ + // thread cannot deallocate the stack while still using it, wait for someone to recycle it. + // In this lab, idle thread handles this task, instead of parent thread. + lock(); + curr_thread->iszombie = 1; + shell_flag--; + unlock(); + // delay(10000); + // schedule(); +} + +void kill_zombies() +{ + lock(); + list_head_t *curr; + thread_t *t; + list_for_each(curr, run_queue) + { + t = (thread_t *)curr; + if (t->iszombie) + { + list_del_entry(curr); + mmu_free_page_tables(t->context.pgd, 0); + mmu_del_vma(t); + for (int i = 0; i < MAX_FD; i++) + { + if (t->file_descriptors_table[i]) + vfs_close(t->file_descriptors_table[i]); + } + kfree(t->kernel_stack_alloced_ptr); + kfree((void *)PHYS_TO_KERNEL_VIRT(t->context.pgd)); + t->iszombie = 0; + t->isused = 0; + } + } + unlock(); +} + +void foo() +{ + // Lab5 Basic 1 Test function + for (int i = 0; i < 10; ++i) + { + uart_sendlinek("Thread id: %d %d\n", curr_thread->pid, i); + int r = 1000000; + while (r--) + { + asm volatile("nop"); + } + schedule(); + } + // uart_sendlinek("exit\n"); + thread_exit(); +} + +int exec_thread() +{ + lock(); + shell_flag++; + thread_t *t = thread_create(exec_proc); + curr_thread = t; + // uart_sendlinek("timer_sched_flag : %d\n", timer_sched_flag); + if (!timer_sched_flag) + { + timer_sched_flag = 1; + add_timer(schedule_timer, 1, "", setSecond); // start scheduler --------------------------------------------------------- + } + + unlock(); + schedule(); + + return 0; +} + +void exec_proc() +{ + el1_interrupt_disable(); // daif 會在 eret 時更改。 + // char *data = c_execfile.data; + // unsigned int filesize = c_execfile.filesize; + thread_t *t = curr_thread; + + // char *data = target_file->f_ops; + unsigned int filesize = (c_execfile.vnode)->f_ops->getsize(c_execfile.vnode); + + t->data = kmalloc(filesize); + t->datasize = filesize; + + // copy file into data + struct file *f; + vfs_open(c_execfile.pathname, 0, &f); + vfs_read(f, t->data, t->datasize); + vfs_close(f); + + //memcpy(t->data, data, t->datasize); + + mmu_add_vma(t, USER_DATA_BASE, t->datasize, (size_t)KERNEL_VIRT_TO_PHYS(t->data), 0b111, 1, USER_DATA); + mmu_add_vma(t, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(t->stack_alloced_ptr), 0b111, 1, USER_STACK); + mmu_add_vma(t, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(t, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), PAGESIZE), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(t, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + t->context.sp = USER_STACK_BASE - STACK_BASE_OFFSET; + t->context.fp = USER_STACK_BASE - STACK_BASE_OFFSET; + t->context.lr = USER_DATA_BASE; + + // for (int i = 0; i < filesize; i++) + // { + // t->data[i] = data[i]; + // } + // dump_vma(); + + // vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[0]); // stdin + // vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[1]); // stdout + // vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[2]); // stderr + + asm("msr tpidr_el1, %0\n\t" // Hold the "kernel(el1)" thread structure information + "msr elr_el1, %1\n\t" // When el0 -> el1, store return address for el1 -> el0 + "msr spsr_el1, xzr\n\t" // EL1h (SPSel = 1) with interrupt disabled + "msr sp_el0, %2\n\t" // el0 stack pointer for el1 process + "mov sp, %3\n\t" // sp is reference for the same el process. For example, el2 cannot use sp_el2, it has to use sp to find its own stack. + "mov x0, %4\n\t" ::"r"(&t->context), + "r"(exec_wrapper), "r"(t->context.sp), "r"(t->kernel_stack_alloced_ptr + KSTACK_SIZE - STACK_BASE_OFFSET), "r"(t->context.lr)); + + asm("eret\n\t"); +} + +void exec_wrapper() +{ + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void schedule_timer(char *notuse) +{ + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq_el0)); + add_timer(schedule_timer, cntfrq_el0 >> 5, "", setTick); + // schedule(); + // 32 * default timer -> trigger next schedule timer +} diff --git a/lab7/kernel/src/shell.c b/lab7/kernel/src/shell.c new file mode 100644 index 000000000..a373a9868 --- /dev/null +++ b/lab7/kernel/src/shell.c @@ -0,0 +1,588 @@ +#include "shell.h" +#include "mbox.h" +#include "power.h" +#include "stdio.h" +#include "string.h" +#include "cpio.h" +#include "memory.h" +#include "dtb.h" +#include "timer.h" +#include "sched.h" +#include "signal.h" +#include "syscall.h" +#include "vfs.h" +#include "vfs_tmpfs.h" +#include "debug.h" + +#include "uart1.h" + +#define USTACK_SIZE 0x10000 + +extern unsigned long long int lock_counter; +extern list_head_t *run_queue; +extern execfile c_execfile; +extern struct mount *rootfs; + +struct CLI_CMDS cmd_list[CLI_MAX_CMD] = { + {.command = "cat", .help = "concatenate files and print on the standard output", .func = do_cmd_cat}, + {.command = "dtb", .help = "show device tree", .func = do_cmd_dtb}, + {.command = "hello", .help = "print Hello World!", .func = do_cmd_hello}, + {.command = "help", .help = "print all available commands", .func = do_cmd_help}, + {.command = "info", .help = "get device information via mailbox", .func = do_cmd_info}, + {.command = "malloc", .help = "test malloc", .func = do_cmd_malloc}, + {.command = "reboot", .help = "reboot the device", .func = do_cmd_reboot}, + {.command = "exec", .help = "execute user programs ", .func = do_cmd_exec}, + {.command = "setTime", .help = "setTime [MESSAGE] [SECONDS] ", .func = do_cmd_setTimeout}, + {.command = "2sAlert", .help = "set core timer interrupt every 2 second ", .func = do_cmd_set2sAlert}, + {.command = "mtest", .help = "memory testcase generator, allocate and free", .func = do_cmd_mtest}, + {.command = "ttest", .help = "thread tester with dummy function - foo()", .func = do_cmd_ttest}, + {.command = "ls", .help = "list directory contents", .func = do_cmd_ls}, + {.command = "cd", .help = "change directory", .func = do_cmd_cd}, + {.command = "ftest", .help = "get run_queue size", .func = do_cmd_ftest}}; + +extern char *dtb_ptr; +extern thread_t *curr_thread; +extern void *CPIO_DEFAULT_START; + +void start_shell() +{ + // uart_sendlinek("In start_shell\n"); + char input_buffer[CMD_MAX_LEN]; + + cli_print_banner(); + while (1) + { + cli_flush_buffer(input_buffer, CMD_MAX_LEN); + // uart_sendlinek("lock_counter : %d\n",lock_counter); + puts("【 Ciallo~(∠・ω< )⌒★ 】 # "); + cli_cmd_read(input_buffer); + cli_cmd_exec(input_buffer); + idle(); + // uart_sendlinek("idle finish\n"); + } + // return 0; +} + +void cli_flush_buffer(char *buffer, int length) +{ + for (int i = 0; i < length; i++) + { + buffer[i] = '\0'; + } +}; + +void cli_cmd_read(char *buffer) +{ + char c = '\0'; + int idx = 0; + + while (idx < CMD_MAX_LEN - 1) + { + c = getchar(); + // vfs_read(uart, c, 0); + + if (c == 127) // backspace + { + if (idx != 0) + { + puts("\b \b"); + // vfs_write("/dev/uart","\b \b",0); + idx--; + } + } + else if (c == '\n') + { + break; + } + else if (c <= 16 || c >= 32 || c < 127) + { + putchar(c); + // vfs_write("/dev/uart","\b \b",0); + buffer[idx++] = c; + } + } + buffer[idx] = '\0'; + puts("\r\n"); + // vfs_write("/dev/uart","\r\n",0); +} + +int _parse_args(char *buffer, int *argc, char **argv) +{ + char get_cmd = 0; + for (int i = 0; buffer[i] != '\0'; i++) + { + if (!get_cmd) + { + if (buffer[i] == ' ') + { + buffer[i] = '\0'; + get_cmd = 1; + } + } + else + { + if (buffer[i - 1] == '\0' && buffer[i] != ' ' && buffer[i] != '\0') + { + if (*argc >= CMD_MAX_PARAM) + { + return -1; + } + argv[*argc] = buffer + i; + (*argc)++; + } + else if (buffer[i] == ' ') + { + buffer[i] = '\0'; + } + } + } + return 0; +} + +void print_args(int argc, char **argv) +{ + puts("argc: "); + put_int(argc); + puts("\r\n"); + for (int i = 0; i < argc; i++) + { + puts("argv["); + put_int(i); + puts("]: "); + puts(argv[i]); + puts("\r\n"); + } +} + +void cli_cmd_exec(char *buffer) +{ + char *cmd = buffer; + int argc = 0; + char *argv[CMD_MAX_PARAM]; + if (_parse_args(buffer, &argc, argv) == -1) + { + puts("Too many arguments\r\n"); + return; + } + // print_args(argc, argv); + + for (int i = 0; i < CLI_MAX_CMD; i++) + { + if (strcmp(cmd, cmd_list[i].command) == 0) + { + cmd_list[i].func(argc, argv); + return; + } + } + if (*buffer) + { + puts(buffer); + puts(": command not found\r\n"); + } +} + +void cli_print_banner() +{ + // uart_sendlinek("In cli_print_banner\n"); + puts("\r\n"); + puts("============================================================================================\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@- %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% *@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#%@@@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@% *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:=@@@@@@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@- :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* #@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@#- :#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+ +%@@@@@@@@@@@@@@+ -%@@@@@@@@@@@@\r\n"); + puts("@@@@*=: :=#@@@@@@@@#=: =*@@@@@@@@@@\r\n"); + puts("@@@@@#= =*%@@@@@@@@@@*+- :=*%@@@@@@@@@@\r\n"); + puts("@@@@@@@- @@@@@@@@@@@# -@@@@@@@@@@@@@@@@@@%: -%@%@@@@@@@@@@@@@@%= :#@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@= @%++*%@@@@@%: +@@@@#==#@@@@@@@@@@@% %* -%@@#-=+%@@@@@@@- %@@@@@+::=*%@@@@\r\n"); + puts("@@@@@@@= @% =%@+ #@+ =%@@\r\n"); + puts("@@@@@@@= @% ************- =@ #+ :%@@\r\n"); + puts("@@@@@@@= @% @@@@@@@@@@@@+ @@% #= += %@@@* =@@@@- :@@@@\r\n"); + puts("@@@@@@@= @% ##########%@+ :@@% #= +@@@@@@= %@#= -#@@= -@@@@\r\n"); + puts("@@@@@@@= @% :::::::::::@+ :@@% #= +#+=++@= -: -: -@@@@\r\n"); + puts("@@@@@@@= @% @@@@@@@@@@@@+ :@@% #= ++ @= - : -@@@@\r\n"); + puts("@@@@@@@= :@% @@@@@@@@@@@@+ :@@% #= ++ @= %@#- :*%@- -@@@@\r\n"); + puts("@@@@@@@= =@% :@@% #= ++ @= %@@@+ -@@@@- -@@@@\r\n"); + puts("@@@@@@@- #@% ****: +**= +@@% #= ++ @= *####: #####: -@@@@\r\n"); + puts("@@@@@@@ :@@%-=*%@@@@@- #@@#+*#%@@@@% #= ++ =@+ -@@@@\r\n"); + puts("@@@@@@* %@@@@#*=--@@@- #@@#-=+*%@@@% %= *+ =@@+ +@@@@@= :@@@@@# -@@@@\r\n"); + puts("@@@@@@: *@%+- =@@- #@#: :=*+ :@= +*=%@@@+ =%@@@@@* -@@@@@@+: -@@@@\r\n"); + puts("@@@@@= *#- *- #- *@= +@@@@@@#+*@@@@@@*- :+%@@@@@#+*@@@@\r\n"); + puts("@@@@+ -%@: -=**%@@@- #@@%#*+=: *@@= *@@@@@@@@@@@@=: :-%@@@@@@@@@\r\n"); + puts("@@@+ -#@@@%- =@@@@@@@@@- #@@@@@@@@% =%@@@= *@@@@@@@@@@@@@#- :*%@@@@@@@@@@\r\n"); + puts("@@=:+%@@@@@@@%+--=*%@@@@@- :@@@@@@#*=-=*@@@@@@= #@@@@@@@@@@@@@@@* =@@@@@@@@@@@@@\r\n"); + puts("@%%@@@@@@@@@@@@@@@%##@@@@- #@@@@%#%@@@@@@@@@@@= +@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@- =%@@@@@@@@@@@@@@@@@@@= *@@@@@@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@++%@@@@@@@@@@@@@@@@@@@@@*+%@@@@@@@@@@@@@@@@@@@@+ -@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% +@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-%@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@@@@@@@@@@@@\r\n"); + puts("============================================================================================\r\n"); +} + +int do_cmd_help(int argc, char **argv) +{ + for (int i = 0; i < CLI_MAX_CMD; i++) + { + puts(cmd_list[i].command); + puts("\t\t\t: "); + puts(cmd_list[i].help); + puts("\r\n"); + } + return 0; +} + +int do_cmd_hello(int argc, char **argv) +{ + puts("Hello World!\r\n"); + return 0; +} + +int do_cmd_info(int argc, char **argv) +{ + // print hw revision + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_BOARD_REVISION; + pt[3] = 4; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt))) + { + puts("Hardware Revision\t: 0x"); + // put_hex(pt[6]); + put_hex(pt[5]); + puts("\r\n"); + } + // print arm memory + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_ARM_MEMORY; + pt[3] = 8; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt))) + { + puts("ARM Memory Base Address\t: 0x"); + put_hex(pt[5]); + puts("\r\n"); + puts("ARM Memory Size\t\t: 0x"); + put_hex(pt[6]); + puts("\r\n"); + } + return 0; +} + +int do_cmd_reboot(int argc, char **argv) +{ + if (argc == 0) + { + + puts("Reboot in 10 seconds ...\r\n\r\n"); + volatile unsigned int *rst_addr = (unsigned int *)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x20; + volatile unsigned int *wdg_addr = (unsigned int *)PM_WDOG; + *wdg_addr = PM_PASSWORD | 0x70000; + } + else if (argc == 1 && strcmp(argv[0], "-c") == 0) + { + puts("Cancel reboot...\r\n"); + volatile unsigned int *rst_addr = (unsigned int *)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x0; + volatile unsigned int *wdg_addr = (unsigned int *)PM_WDOG; + *wdg_addr = PM_PASSWORD | 0x0; + } + return 0; +} + +int do_cmd_ls(int argc, char **argv) +{ + // char *c_filepath; + // char *c_filedata; + // unsigned int c_filesize; + + // CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + // { + // if (header_ptr != 0) + // { + // puts(c_filepath); + // puts("\r\n"); + // } + // } + vfs_ls(); + return 0; +} + +int do_cmd_cat(int argc, char **argv) +{ + int FLAG_getfile = 0; + char *filepath; + char *c_filepath; + char *c_filedata; + unsigned int c_filesize; + + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + + CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + { + if (strcmp(c_filepath, filepath) == 0) + { + FLAG_getfile = 1; + Readfile(c_filedata, c_filesize); + break; + } + } + + if (!FLAG_getfile) + { + puts("cat: "); + puts(filepath); + puts(": No such file or directory\r\n"); + } + return 0; +} + +int do_cmd_malloc(int argc, char **argv) +{ + // test malloc + char *test1 = allocator(0x18); + strcpy(test1, "test malloc1"); + puts(test1); + puts("\r\n"); + + char *test2 = allocator(0x20); + strcpy(test2, "test malloc2"); + puts(test2); + puts("\r\n"); + + char *test3 = allocator(0x28); + strcpy(test3, "test malloc3"); + puts(test3); + puts("\r\n"); + return 0; +} + +int do_cmd_dtb(int argc, char **argv) +{ + traverse_device_tree(dtb_ptr, dtb_callback_show_tree); + return 0; +} + +int do_cmd_exec(int argc, char **argv) +{ + char *filepath; + + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + + char abs_path[MAX_PATH_NAME]; + struct vnode *target_file; + strcpy(abs_path, filepath); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + strcpy(abs_path,"/initramfs/vfs1.img");// + if (vfs_lookup(abs_path, &target_file) != 0) + { + WARING("File : %s Does not Exit!!\n", abs_path); + return 0; + }; + + c_execfile.vnode = target_file; + c_execfile.pathname = abs_path; + exec_thread(); + + // c_execfile.data = target_file->f_ops; + // c_execfile.filesize = target_file->f_ops->getsize(target_file);; + + // CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + // { + // if (strcmp(c_filepath, filepath) == 0) + // { + // // exec c_filedata + // FLAG_getfile = 1; + // c_execfile.data = c_filedata; + // c_execfile.filesize = c_filesize; + // exec_thread(); + // } + // } + + // if (!FLAG_getfile) // header_ptr + // { + // puts("cat: "); + // puts(filepath); + // puts(": No such file or directory\r\n"); + // } + return 0; +} + +int do_cmd_setTimeout(int argc, char **argv) +{ + char *msg; + int sec; + if (argc == 2) + { + msg = argv[0]; + sec = atoi(argv[1]); + } + else + { + puts("setTimeout [MESSAGE] [SECONDS]\r\n"); + return -1; + } + add_timer(puts, sec, msg, setSecond); + return 0; +} + +int do_cmd_set2sAlert(int argc, char **argv) +{ + add_timer(timer_set2sAlert, 2, "2sAlert", setSecond); + return 0; +} + +int do_cmd_mtest(int argc, char **argv) +{ + // char *a = kmalloc(513); + // uart_sendlinek("a : %x\n", a); + // //kfree(a); + + // char *b = kmalloc(512); + // uart_sendlinek("b : %x\n", b); + // //kfree(b); + + // char *c = kmalloc(8); + // //kfree(c); + + return 0; +} + +int do_cmd_ttest(int argc, char **argv) +{ + uart_sendlinek("run_queue size : %d\n", list_size(run_queue)); + list_head_t *pos; + list_for_each(pos, run_queue) + { + uart_sendlinek("pid : %d\n", ((thread_t *)pos)->pid); + for (int i = 0; i < SIGNAL_MAX; i++) + { + uart_sendlinek("signal_handler : 0x%x\n", ((thread_t *)pos)->signal_handler[i]); + } + } + return 0; +} + +int do_cmd_ftest(int argc, char **argv) +{ + // ============================== test for file operation start================================== + uart_sendlinek("\n\n"); + uart_sendlinek("+-----------------------------+\n"); + uart_sendlinek("| mkdir,mount test |\n"); + uart_sendlinek("+-----------------------------+\n"); + + vfs_mkdir("/lll"); + vfs_mkdir("/aaa"); + vfs_mkdir("/bbb"); + vfs_mkdir("/lll/ddd"); + vfs_mount("/lll/ddd", "tmpfs"); + vfs_mkdir("/lll/ddd/fff"); + vfs_dump(rootfs->root, 0); + + uart_sendlinek("\n\n"); + uart_sendlinek("+-----------------------------+\n"); + uart_sendlinek("| open,write,read test |\n"); + uart_sendlinek("+-----------------------------+\n"); + + struct file *testfilew; + struct file *testfiler; + char testbufw[0x30] = "ABCDEABBBBBBDDDDDDDDDDD\n"; + char testbufr[0x30] = {}; + + vfs_open("/lll/ddd/ggg.file", O_CREAT, &testfilew); + vfs_open("/lll/ddd/ggg.file", O_CREAT, &testfiler); + vfs_write(testfilew, testbufw, 25); + vfs_read(testfiler, testbufr, 25); + + uart_sendlinek("%s", testbufr); + vfs_dump(rootfs->root, 0); + // ============================== test for file operation end ================================== + + // ============================== test for get_absolute_path start================================== + // char *currt_path = "/root"; + // uart_sendlinek("currt_path : %s\n", currt_path); + // char *path1 = "/"; + // uart_sendlinek("path1 rel_path : %s\n", path1); + // get_absolute_path(path1, currt_path); + // // uart_sendlinek("path1 abs_path : %s\n", path1); + // uart_sendlinek("---------------------------------------\n"); + // char *path2 = "desktop"; + // uart_sendlinek("path2 rel_path : %s\n", path2); + // get_absolute_path(path2, currt_path); + // // uart_sendlinek("path2 abs_path : %s\n", path2); + // uart_sendlinek("---------------------------------------\n"); + // char *path3 = ".///////"; + // uart_sendlinek("path3 rel_path : %s\n", path3); + // get_absolute_path(path3, currt_path); + // // uart_sendlinek("path3 abs_path : %s\n", path3); + // ============================== test for get_absolute_path end================================== + + // char test[11] = "abcdefghijk"; + // char *t2 = &test[3]; + // uart_sendlinek("abs_path : %s\n", test); + // uart_sendlinek("abs_path : %s\n", t2); + // t2 = &test[5]; + // uart_sendlinek("abs_path : %s\n", t2); + + return 0; +} + +int do_cmd_cd(int argc, char **argv) +{ + char *filepath; + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + vfs_cd(filepath); + return 0; +} \ No newline at end of file diff --git a/lab7/kernel/src/signal.c b/lab7/kernel/src/signal.c new file mode 100644 index 000000000..454315f6e --- /dev/null +++ b/lab7/kernel/src/signal.c @@ -0,0 +1,90 @@ +#include "signal.h" +#include "syscall.h" +#include "sched.h" +#include "memory.h" +#include "mmu.h" + +extern thread_t *curr_thread; + +/** + ( ) + [Signal] [UserProcess] ( [ Registered Signal Handler ] ) [UserProcess] + | \ ( / \ ) / + | \ ( / \ ) / +-------|--------------------[SystemCall]-------------------------------------(-----------------/---------------------------------\----------------------)--/--------------- + | \ [Signal Check] (-> \ ) / + V \ / ^ ( \ \ )/ + [Job Pending] [Exception Handler] | ( \ [ Default Signal Handler ]-------- [Exception Handler]) + | | ( ) + --------------------- ( CONTENT SWITCHING FOR SIGNAL HANDLER ) + +**/ + +void check_signal(trapframe_t *tpf) +{ + if (curr_thread->signal_is_checking) + return; + lock(); + // Prevent nested running signal handler. No need to handle + curr_thread->signal_is_checking = 1; + unlock(); + for (int i = 0; i <= SIGNAL_MAX; i++) + { + store_context(&curr_thread->signal_savedContext); + if (curr_thread->sigcount[i] > 0) + { + lock(); + curr_thread->sigcount[i]--; + unlock(); + run_signal(tpf, i); + } + } + lock(); + curr_thread->signal_is_checking = 0; + unlock(); +} + +void run_signal(trapframe_t *tpf, int signal) +{ + curr_thread->curr_signal_handler = curr_thread->signal_handler[signal]; + + // run default handler in kernel + if (curr_thread->curr_signal_handler == signal_default_handler) + { + signal_default_handler(); + return; + } + + // run registered handler in userspace + // char *temp_signal_userstack = kmalloc(USTACK_SIZE); + // asm("msr elr_el1, %0\n\t" + // "msr sp_el0, %1\n\t" + // "msr spsr_el1, %2\n\t" + // "eret\n\t" ::"r"(signal_handler_wrapper), + // "r"(temp_signal_userstack + USTACK_SIZE), + // "r"(tpf->spsr_el1)); + + 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_SIGNAL_WRAPPER_VA + ((size_t)signal_handler_wrapper % 0x1000)), + "r"(tpf->sp_el0), + "r"(tpf->spsr_el1), + "r"(curr_thread->curr_signal_handler)); +} + +void signal_handler_wrapper() +{ + //(curr_thread->curr_signal_handler)(); + // system call sigreturn + // uart_sendlinek("signal_handler_wrapper\n"); + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void signal_default_handler() +{ + kill(0, curr_thread->pid); +} diff --git a/lab7/kernel/src/stdio.c b/lab7/kernel/src/stdio.c new file mode 100644 index 000000000..a1909d4b7 --- /dev/null +++ b/lab7/kernel/src/stdio.c @@ -0,0 +1,121 @@ +#include "stdio.h" +#include "uart1.h" +#include "vfs.h" +#include "sched.h" + +extern thread_t *curr_thread; + +char getchar() +{ + char c[1]; + stdio_op(stdin, c, 1); + return c[0] == '\r' ? '\n' : c[0]; +} + +void putchar(char c) +{ + char cp[2] = {c,'\0'}; + stdio_op(stdout, cp, 1); +} + +void puts(const char *s) +{ + while (*s) + putchar(*s++); +} + +void Readfile(char *str, int size) +{ + while (size--) + { + putchar(*str++); + } +} + +// Function to print an integer to the UART +void put_int(int num) +{ + // Handle the case when the number is 0 + if (num == 0) + { + putchar('0'); + return; + } + + // Temporary array to store the reversed digits as characters + char temp[12]; // Assuming int can have at most 10 digits + int idx = 0; + + // Handle negative numbers + if (num < 0) + { + putchar('-'); + num = -num; + } + + // Convert the number to characters and store in the temporary array in reverse order + while (num > 0) + { + temp[idx++] = (char)(num % 10 + '0'); + num /= 10; + } + + // Reverse output the character digits + while (idx > 0) + { + putchar(temp[--idx]); + } +} + +void put_hex(unsigned int num) +{ + unsigned int hex; + int index = 28; + puts("0x"); + while (index >= 0) + { + hex = (num >> index) & 0xF; + hex += hex > 9 ? 0x37 : 0x30; + putchar(hex); + index -= 4; + } +} + +// 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; +} + +int fake_log2(unsigned long long n) +{ + int val = 0; + while (n >>= 1) + val++; + return val; +} + +void delay(int r) +{ + while (r--) + { + asm volatile("nop"); + } +} diff --git a/lab7/kernel/src/string.c b/lab7/kernel/src/string.c new file mode 100644 index 000000000..0118ebf28 --- /dev/null +++ b/lab7/kernel/src/string.c @@ -0,0 +1,242 @@ +#include "string.h" + +size_t strlen(const char *str) +{ + size_t count = 0; + while ((unsigned char)*str++) + count++; + return count; +} + +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 *strcpy(char *dest, const char *src) +{ + while ((*dest++ = *src++)) + ; + return dest; +} + +char *strncpy(char *dest, const char *src, unsigned long long n) +{ + // for (int i = 0; i < n ; i++) { + // dest[i] = src[i]; + // } + + while (n-- && (*dest++ = *src++) ) + ; + return dest; +} + +char *strcat(char *dest, const char *src) +{ + strcpy(dest + strlen(dest), src); + return dest; +} + +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 (dst - orig > VSPRINT_MAX_BUF_SIZE - 0x10) + { + return -1; + } + // 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] = '-'; + } + if (len > 0 && len < 18) + { + while (i > 18 - len) + { + tmpstr[--i] = ' '; + } + } + p = &tmpstr[i]; + goto copystring; + } + else if (*fmt == 'x') + { + arg = __builtin_va_arg(args, long int); + i = 16; + tmpstr[i] = 0; + do + { + char n = arg & 0xf; + tmpstr[--i] = n + (n > 9 ? 0x37 : 0x30); + arg >>= 4; + } while (arg != 0 && i > 0); + if (len > 0 && len <= 16) + { + while (i > 16 - len) + { + tmpstr[--i] = '0'; + } + } + p = &tmpstr[i]; + goto copystring; + } + else 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; + return dst - orig; +} + +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; +} + +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/kernel/src/syscall.c b/lab7/kernel/src/syscall.c new file mode 100644 index 000000000..71f4d67a6 --- /dev/null +++ b/lab7/kernel/src/syscall.c @@ -0,0 +1,352 @@ +#include "bcm2837/rpi_mbox.h" +#include "syscall.h" +#include "sched.h" +#include "uart1.h" +#include "stdio.h" +#include "exception.h" +#include "memory.h" +#include "mbox.h" +#include "signal.h" +#include "string.h" + +#include "debug.h" +#include "cpio.h" +#include "dtb.h" +#include "mmu.h" + +extern void *CPIO_DEFAULT_START; +extern thread_t *curr_thread; +extern thread_t threads[PIDMAX + 1]; + +// trap is like a shared buffer for user space and kernel space +// Because general-purpose registers are used for both arguments and return value, +// We may receive the arguments we need, and overwrite them with return value. + +int getpid(trapframe_t *tpf) +{ + // uart_sendlinek("this is getpid"); + tpf->x0 = curr_thread->pid; + return curr_thread->pid; +} + +size_t uartread(trapframe_t *tpf, char buf[], size_t size) +{ + int i = 0; + stdio_op(stdin, buf, size); + tpf->x0 = i; + return i; +} + +size_t uartwrite(trapframe_t *tpf, const char buf[], size_t size) +{ + int i = 0; + char *cptr = buf; + stdio_op(stdout, buf, size); + 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[]) +{ + lock(); + mmu_del_vma(curr_thread); + INIT_LIST_HEAD(&curr_thread->vma_list); + + // use virtual file system + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, name); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + // uart_sendlinek("file name : %s\n", name); + // uart_sendlinek("curr_working_dir : %s\n", curr_thread->curr_working_dir); + // uart_sendlinek("abs_path : %s\n", abs_path); + + struct vnode *target_file; + if (vfs_lookup(abs_path, &target_file) != 0) + { + WARING("File : %s Does not Exit!!", abs_path); + return 0; + }; + curr_thread->datasize = target_file->f_ops->getsize(target_file); + uart_sendlinek("datasize : %d\n", curr_thread->datasize); + + // curr_thread->data = kmalloc(curr_thread->datasize > PAGESIZE ? curr_thread->datasize : PAGESIZE); + curr_thread->data = kmalloc(curr_thread->datasize); + curr_thread->stack_alloced_ptr = kmalloc(USTACK_SIZE); + + // data copy + memcpy(curr_thread->signal_handler, curr_thread->signal_handler, SIGNAL_MAX * 8); + struct file *f; + vfs_open(abs_path, 0, &f); + vfs_read(f, curr_thread->data, curr_thread->datasize); + vfs_close(f); + + // clean vma & page_tables + asm("dsb ish\n\t"); // ensure write has completed + mmu_free_page_tables(curr_thread->context.pgd, 0); + memset((void *)PHYS_TO_KERNEL_VIRT(curr_thread->context.pgd), 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 + mmu_del_vma(curr_thread); + + // new vma + mmu_add_vma(curr_thread, USER_DATA_BASE, curr_thread->datasize, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->data), 0b111, 1, USER_DATA); + mmu_add_vma(curr_thread, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->stack_alloced_ptr), 0b011, 1, USER_STACK); + mmu_add_vma(curr_thread, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(curr_thread, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), 0x1000), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(curr_thread, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + tpf->elr_el1 = USER_DATA_BASE; + tpf->sp_el0 = USER_STACK_BASE - STACK_BASE_OFFSET; + tpf->x0 = 0; + unlock(); + return 0; +} + +// extern unsigned long long int lock_counter; +int fork(trapframe_t *tpf) +{ + lock(); + thread_t *newt = thread_create(curr_thread->data); + // mmu_set_PTE_readonly(curr_thread->context.pgd,0); + // mmu_pagetable_copy(newt->context.pgd,curr_thread->context.pgd,0); + // uart_sendlinek("fork\n"); + memcpy(newt->signal_handler, curr_thread->signal_handler, SIGNAL_MAX * 8); + memcpy(newt->stack_alloced_ptr, curr_thread->kernel_stack_alloced_ptr, USTACK_SIZE); + memcpy(newt->kernel_stack_alloced_ptr, curr_thread->kernel_stack_alloced_ptr, KSTACK_SIZE); + memcpy(newt->curr_working_dir, curr_thread->curr_working_dir,MAX_PATH_NAME+1); + + newt->datasize = curr_thread->datasize; + newt->data = kmalloc(newt->datasize); + memcpy(newt->data,curr_thread->data,newt->datasize); + //newt->curr_working_dir = curr_thread->curr_working_dir; + // memcpy(newt->file_descriptors_table, curr_thread->file_descriptors_table, MAX_FD * 8); // <------------------------------------------------ + mmu_add_vma(newt, USER_DATA_BASE, newt->datasize, (size_t)KERNEL_VIRT_TO_PHYS(newt->data), 0b111, 1, USER_DATA); + mmu_add_vma(newt, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(newt->stack_alloced_ptr), 0b011, 1, USER_STACK); + mmu_add_vma(newt, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(newt, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), 0x1000), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(newt, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + int parent_pid = curr_thread->pid; + + store_context(get_current()); + // for child + if (parent_pid != curr_thread->pid) + { + goto child; + } + + // 除了PGD以外的context都複製。 + void *temp_pgd = newt->context.pgd; + newt->context = curr_thread->context; + newt->context.pgd = temp_pgd; + //memcpy(newt->context.pgd,curr_thread->context.pgd,PAGESIZE); + + 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(); + while (1) + schedule(); +} + +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox) +{ + lock(); + + unsigned int size_of_mbox = mbox[0]; + memcpy((char *)pt, mbox, size_of_mbox); + mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)); + memcpy(mbox, (char *)pt, size_of_mbox); + + // tpf->x0 = 8; + unlock(); + return 0; +} + +// only need to implement the anonymous page mapping in this Lab. +void *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset) +{ + len = ALIGN_UP(len, PAGESIZE); + unsigned long base_user_va = ALIGN_DOWN((unsigned long)addr, PAGESIZE); + uart_sendlinek("+\n"); + uart_sendlinek("| User request new vma base vitural address: 0x%x\n", (unsigned long)addr); + uart_sendlinek("| Aligned to PAGESIZE: 0x%x\n", base_user_va); + uart_sendlinek("| User request new vma size: 0x%x\n", len); + uart_sendlinek("| Exec, Write, Read : 0x%d\n", prot); + uart_sendlinek("+\n"); + + // Req #2 check if overlap + vm_area_struct_t *the_area_ptr = check_vma_overlap(curr_thread, base_user_va, (unsigned long)len); + // take as a hint to decide new region's start address + if (the_area_ptr) + { + WARING("Vitural Memory Area Overlap\n"); + WARING("Find another vma base vitural address\n"); + tpf->x0 = (unsigned long)mmap(tpf, (void *)(the_area_ptr->virt_addr + the_area_ptr->area_size), len, prot, flags, fd, file_offset); + return (void *)tpf->x0; + } + // create new valid region, map and set the page attributes (prot) + mmu_add_vma(curr_thread, base_user_va, len, KERNEL_VIRT_TO_PHYS((unsigned long)kmalloc(len)), prot, 1, UNKNOW_AREA); + tpf->x0 = base_user_va; + return (void *)tpf->x0; +} + +void kill(trapframe_t *tpf, int pid) +{ + if (pid < 0 || pid >= PIDMAX || !threads[pid].isused) + return; + + lock(); + + if (pid == curr_thread->pid) + { + uart_sendlinek("[!] you kill youself !! \n"); + thread_exit(); + unlock(); + while (1) + schedule(); + } + else + { + threads[pid].iszombie = 1; + unlock(); + } + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0) + return; + // uart_sendlinek("handler : 0x%x\n", handler); + curr_thread->signal_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) +{ + // unsigned long signal_ustack = tpf->sp_el0 % USTACK_SIZE == 0 ? tpf->sp_el0 - USTACK_SIZE : tpf->sp_el0 & (~(USTACK_SIZE - 1)); + // kfree((char *)signal_ustack); + load_context(&curr_thread->signal_savedContext); +} + +void syscall_unlock(trapframe_t *tpf) +{ + unlock(); +} + +void syscall_lock(trapframe_t *tpf) +{ + lock(); +} + +char *get_file_start(char *thefilepath) +{ + int FLAG_getfile = 0; + char *filepath; + char *filedata; + unsigned int filesize; + // struct cpio_newc_header *header_pointer = CPIO_DEFAULT_START; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + if (strcmp(thefilepath, filepath) == 0) + { + FLAG_getfile = 1; + return filedata; + } + } + + if (!FLAG_getfile) + { + uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + } + + // while (header_pointer != 0) + // { + // int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // //if parse header error + // if (error) + // { + // uart_sendlinek("error"); + // break; + // } + + // if (strcmp(thefilepath, filepath) == 0) + // { + // return filedata; + // } + + // //if this is TRAILER!!! (last of file) + // if (header_pointer == 0) + // uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + // } + + return 0; +} + +unsigned int get_file_size(char *thefilepath) +{ + int FLAG_getfile = 0; + char *filepath; + char *filedata; + unsigned int filesize; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + if (strcmp(thefilepath, filepath) == 0) + { + FLAG_getfile = 1; + return filesize; + } + } + + if (!FLAG_getfile) + { + uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + } + + // while (header_pointer != 0) + // { + // int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // // if parse header error + // if (error) + // { + // uart_sendlinek("error"); + // break; + // } + + // if (strcmp(thefilepath, filepath) == 0) + // { + // return filesize; + // } + + // // if this is TRAILER!!! (last of file) + // if (header_pointer == 0) + // uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + // } + return 0; +} diff --git a/lab7/kernel/src/syscall_fs.c b/lab7/kernel/src/syscall_fs.c new file mode 100644 index 000000000..0bce29aa0 --- /dev/null +++ b/lab7/kernel/src/syscall_fs.c @@ -0,0 +1,146 @@ +#include "bcm2837/rpi_mbox.h" +#include "vfs.h" +#include "string.h" +#include "sched.h" +#include "exception.h" +#include "uart1.h" +#include "vfs_dev_framebuffer.h" +#include "stdio.h" + +extern void *CPIO_DEFAULT_START; +extern thread_t *curr_thread; +extern thread_t threads[PIDMAX + 1]; + +int open(trapframe_t *tpf, const char *pathname, int flags) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + + // uart_sendlinek("file name : %s\n", pathname); + // uart_sendlinek("curr_working_dir : %s\n", curr_thread->curr_working_dir); + // uart_sendlinek("abs_path : %s\n", abs_path); + + // update abs_path + get_absolute_path(abs_path, curr_thread->curr_working_dir); + for (int i = 0; i < MAX_FD; i++) + { + // find a usable fd + if (!curr_thread->file_descriptors_table[i]) + { + // uart_sendlinek("i : %d\n", i); + // while (1) + // ; + + if (vfs_open(abs_path, flags, &curr_thread->file_descriptors_table[i]) != 0) + { + break; + } + + tpf->x0 = i; + return i; + } + } + + tpf->x0 = -1; + return -1; +} + +int close(trapframe_t *tpf, int fd) +{ + // find an opened 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; +} + +long write(trapframe_t *tpf, int fd, const void *buf, unsigned long count) +{ + // uart_sendlinek("fd : %d\n", fd); + 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; +} + +long 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; +} + +int mkdir(trapframe_t *tpf, const char *pathname, unsigned mode) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + tpf->x0 = vfs_mkdir(abs_path); + return tpf->x0; +} + +int 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); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + tpf->x0 = vfs_mount(abs_path, filesystem); + return tpf->x0; +} + +int chdir(trapframe_t *tpf, const char *path) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, path); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + strcpy(curr_thread->curr_working_dir, abs_path); + + return 0; +} + +long lseek64(trapframe_t *tpf, int fd, long offset, int whence) +{ + if (whence == SEEK_SET) // used for dev_framebuffer + { + curr_thread->file_descriptors_table[fd]->f_pos = offset; + tpf->x0 = offset; + } + else // other is not supported + { + tpf->x0 = -1; + } + + return tpf->x0; +} +extern unsigned int height; +extern unsigned int isrgb; +extern unsigned int pitch; +extern unsigned int width; +int ioctl(trapframe_t *tpf, int fb, unsigned long request, void *info) +{ + if (request == 0) // used for get info (SPEC) + { + 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; +} \ No newline at end of file diff --git a/lab7/kernel/src/timer.c b/lab7/kernel/src/timer.c new file mode 100644 index 000000000..061bbb05d --- /dev/null +++ b/lab7/kernel/src/timer.c @@ -0,0 +1,177 @@ +#include "timer.h" +#include "uart1.h" +#include "memory.h" +#include "string.h" +#include "exception.h" +#include + +#define STR(x) #x +#define XSTR(s) STR(s) + +struct list_head *timer_event_list; // first head has nothing, store timer_event_t after it + +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 = allocator(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" // cntp_ctl_el0[0]: enable, Control register for the EL1 physical timer. + // cntp_tval_el0: Holds the timer value for the EL1 physical timer + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // QA7_rev3.4.pdf: Core0 Timer IRQ allows Non-secure physical timer(nCNTPNSIRQ) + ); +} + +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" // QA7_rev3.4.pdf: Mask all timer interrupt + ); +} + +void core_timer_handler() +{ + lock(); + //uart_sendlinek("\nThis is core_timer_handler\n"); + if (list_empty(timer_event_list)) + { + uart_sendlinek("\ntimer_event_list is empty\n"); + 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(); +} + +void timer_event_callback(timer_event_t *timer_event) +{ + list_del_entry((struct list_head *)timer_event); // delete the event in queue + // free(timer_event->args); // free the event's space + // free(timer_event); + ((void (*)(char *))timer_event->callback)(timer_event->args); // call the event + + kfree(timer_event->args); + kfree(timer_event); + + // set queue linked list to next time event if it exists + 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 timer_set2sAlert(char *str) +{ + unsigned long long cntpct_el0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" : "=r"(cntpct_el0)); // tick auchor + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq_el0)); // tick frequency + + uart_sendlinek("\n"); + uart_sendlinek("[Interrupt start]\n"); + for (int i = 0; i < 1000000000; i++) + ; + uart_sendlinek("[Interrupt finish]\n"); + + // uart_sendlinek("[Interrupt][el1_irq][%s] %d seconds after booting\n", str, cntpct_el0 / cntfrq_el0); + + add_timer(timer_set2sAlert, 2, "2sAlert",setSecond); +} + +void add_timer(void *callback, unsigned long long timeout, char *args, int inTickFormat) +{ + timer_event_t *the_timer_event = kmalloc(sizeof(timer_event_t)); // free by timer_event_callback + // store all the related information in timer_event + the_timer_event->args = kmalloc(strlen(args) + 1); + strcpy(the_timer_event->args, args); + + if (inTickFormat == 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; + INIT_LIST_HEAD(&the_timer_event->listhead); + + // 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 the timer_event is the biggest, run this code block + if (list_is_head(curr, timer_event_list)) + { + list_add_tail(&the_timer_event->listhead, timer_event_list); + } + // 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 auchor + 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 -> frequency of the timer + "mul x1, x1, %0\n\t" // cntpct_el0 = cntfrq_el0 * seconds: relative timer to cntfrq_el0 + "msr cntp_tval_el0, x1\n\t" // Set expired time to cntp_tval_el0, which stores time value of EL1 physical timer. + : "=r"(expired_time)); +} + +// 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 timer + : "=r"(tick)); +} + +// get timer pending queue size +int timer_list_get_size() +{ + int r = 0; + struct list_head *curr; + list_for_each(curr, timer_event_list) + { + r++; + } + return r; +} diff --git a/lab7/kernel/src/uart1.c b/lab7/kernel/src/uart1.c new file mode 100644 index 000000000..30e7bae36 --- /dev/null +++ b/lab7/kernel/src/uart1.c @@ -0,0 +1,177 @@ +#include "bcm2837/rpi_gpio.h" +#include "bcm2837/rpi_uart1.h" +#include "bcm2837/rpi_irq.h" +#include "uart1.h" +#include "string.h" +#include "exception.h" +#include "vfs.h" + +// implement first in first out buffer with a read index and a write index +static char uart_tx_buffer[VSPRINT_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[VSPRINT_MAX_BUF_SIZE]; +unsigned int uart_rx_buffer_widx = 0; +unsigned int uart_rx_buffer_ridx = 0; + +void uart_init() +{ + register unsigned int selector; + + /* initialize UART */ + *AUX_ENABLES = 1; // enable UART1 + *AUX_MU_CNTL_REG = 0; // disable TX/RX + + /* configure UART */ + *AUX_MU_IER_REG = 0; // disable interrupt + *AUX_MU_LCR_REG = 3; // 8 bit data size + *AUX_MU_MCR_REG = 0; // disable flow control + *AUX_MU_BAUD_REG = 270; // 115200 baud rate + + /* map UART1 to GPIO pins */ + selector = *GPFSEL1; + selector &= ~(7 << 12); // clean gpio14, and (11 111 111 111 111 111 000 111 111 111 111)2 + selector |= 2 << 12; // set gpio14 to alt5 + selector &= ~(7 << 15); // clean gpio15, and (11 111 111 111 111 000 111 111 111 111 111)2 + selector |= 2 << 15; // set gpio15 to alt5 + *GPFSEL1 = selector; + + /* enable pin 14, 15 - ref: Page 101 */ + *GPPUD = 0; + selector = 150; + while (selector--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + selector = 150; + while (selector--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; + + *AUX_MU_CNTL_REG = 3; +} + +void uart_flush_FIFO() +{ + // On write: + // Writing with bit 1 set will clear the receive FIFO + // Writing with bit 2 set will clear the transmit FIFOF + *AUX_MU_IIR_REG |= 6; +} + +char uart_recv() +{ + char r; + while (!(*AUX_MU_LSR_REG & 0x01)) + ; + r = (char)(*AUX_MU_IO_REG); + return r; +} + +void uart_send(unsigned int c) +{ + while (!(*AUX_MU_LSR_REG & 0x20)) + ; + *AUX_MU_IO_REG = c; +} + +// AUX_MU_IER_REG -> BCM2837-ARM-Peripherals.pdf - Pg.12 +void uart_interrupt_enable() +{ + *AUX_MU_IER_REG |= 1; // enable read interrupt + *AUX_MU_IER_REG |= 2; // enable write interrupt + *ENABLE_IRQS_1 |= 1 << 29; // Pg.112 +} + +void uart_interrupt_disable() +{ + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + *AUX_MU_IER_REG &= ~(2); // disable write interrupt +} + +// scanf +void uart_r_irq_handler() +{ + if ((uart_rx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_rx_buffer_ridx) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + return; + } + uart_rx_buffer[uart_rx_buffer_widx++] = uart_recv(); + if (uart_rx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) + uart_rx_buffer_widx = 0; + *AUX_MU_IER_REG |= 1; +} + +// printf +void uart_w_irq_handler() +{ + if (uart_tx_buffer_ridx == uart_tx_buffer_widx) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + return; // buffer empty + } + uart_send(uart_tx_buffer[uart_tx_buffer_ridx++]); + if (uart_tx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) + uart_tx_buffer_ridx = 0; + *AUX_MU_IER_REG |= 2; // enable write interrupt +} + +// uart_async_getc read from buffer +// uart_r_irq_handler write to buffer then output +char uart_async_recv() +{ + *AUX_MU_IER_REG |= 1; // enable read interrupt + // do while if buffer empty + while (uart_rx_buffer_ridx == uart_rx_buffer_widx) + { + *AUX_MU_IER_REG |= 1; // enable read interrupt + } + lock(); + char r = uart_rx_buffer[uart_rx_buffer_ridx++]; + + if (uart_rx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) + uart_rx_buffer_ridx = 0; + unlock(); + return r; +} + +// uart_async_putc writes to buffer +// uart_w_irq_handler read from buffer then output +void uart_async_send(char c) +{ + // if buffer full, wait for uart_w_irq_handler + while ((uart_tx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_tx_buffer_ridx) + { + // uart_puts("buffer full\r\n"); + *AUX_MU_IER_REG |= 2; // enable write interrupt + } + lock(); + uart_tx_buffer[uart_tx_buffer_widx++] = c; + if (uart_tx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) + uart_tx_buffer_widx = 0; // cycle pointer + unlock(); + *AUX_MU_IER_REG |= 2; // enable write interrupt +} + +int uart_sendlinek(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[VSPRINT_MAX_BUF_SIZE]; + + char *str = (char *)buf; + int count = vsprintf(str, fmt, args); + + while (*str) + { + if (*str == '\n') + uart_send('\r'); + uart_send(*str++); + } + __builtin_va_end(args); + return count; +} \ No newline at end of file diff --git a/lab7/kernel/src/vfs.c b/lab7/kernel/src/vfs.c new file mode 100644 index 000000000..832a88ab3 --- /dev/null +++ b/lab7/kernel/src/vfs.c @@ -0,0 +1,433 @@ +#include "vfs.h" +#include "vfs_tmpfs.h" +#include "vfs_initramfs.h" +#include "vfs_dev_uart.h" +#include "memory.h" +#include "string.h" +#include "uart1.h" +#include "sched.h" +#include "debug.h" +// #include "dev_framebuffer.h" + +struct mount *rootfs; +struct filesystem reg_fs[MAX_FS_REG]; +struct file_operations reg_dev[MAX_DEV_REG]; +extern thread_t *curr_thread; + +void rootfs_init() +{ + // tmpfs + int idx = register_tmpfs(); + rootfs = kmalloc(sizeof(struct mount)); + reg_fs[idx].setup_mount(®_fs[idx], rootfs); + + // initramfs + vfs_mkdir("/initramfs"); + register_initramfs(); + vfs_mount("/initramfs", "initramfs"); + + // // dev_fs + 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); +} + +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 vfs_lookup(const char *pathname, struct vnode **target) +{ + // if no path input, return root + if (strlen(pathname) == 0 || (!strcmp(&pathname[0], "/") && strlen(pathname) == 1)) + { + *target = rootfs->root; + // uart_sendlinek("path : %s is root \n", pathname); + return 0; + } + struct vnode *dirnode = rootfs->root; + char component_name[MAX_FILE_NAME + 1] = {}; + int c_idx = 0; + // deal with directory + for (int i = 1; i < strlen(pathname); i++) + { + if (pathname[i] == '/') + { + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) + return -1; + // redirect to mounted filesystem + while (dirnode->mount) + { + dirnode = dirnode->mount->root; + } + c_idx = 0; + } + else + { + component_name[c_idx++] = pathname[i]; + } + } + + // deal with file + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) + return -1; + // redirect to mounted filesystem + while (dirnode->mount) + { + dirnode = dirnode->mount->root; + } + // return file's vnode + *target = dirnode; + + return 0; +} + +int register_dev(struct file_operations *fo) +{ + for (int i = 0; i < MAX_FS_REG; i++) + { + if (!reg_dev[i].open) + { + // return unique id for the assigned device + 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_mount(const char *target, const char *filesystem) +{ + struct vnode *dirnode; + // search for the target filesystem + struct filesystem *fs = find_filesystem(filesystem); + if (!fs) + { + uart_sendlinek("vfs_mount cannot find filesystem\r\n"); + return -1; + } + + if (vfs_lookup(target, &dirnode) == -1) + { + uart_sendlinek("vfs_mount cannot find dir\r\n"); + return -1; + } + else + { + // mount fs on dirnode + dirnode->mount = kmalloc(sizeof(struct mount)); + fs->setup_mount(fs, dirnode->mount); + } + return 0; +} + +// file ops +int vfs_mkdir(const char *pathname) +{ + struct vnode *node; + if (vfs_lookup(pathname, &node) == 0) + { + WARING("Directory Exit!! : %s\n", pathname); + return 0; + } + + char dirname[MAX_PATH_NAME] = {}; // before add folder + char newdirname[MAX_PATH_NAME] = {}; // after add folder + + // search for last directory + 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); + + // create new directory if upper directory is found + // struct vnode *node; + if (vfs_lookup(dirname, &node) == 0) + { + // node is the old dir, &node is new dir + node->v_ops->mkdir(node, &node, newdirname); + return 0; + } + + uart_sendlinek("vfs_mkdir cannot find pathname"); + return -1; +} +// file ops +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 + // uart_sendlinek("\nhere!!!!!\n"); + struct vnode *node; + if (vfs_lookup(pathname, &node) != 0 && (flags & O_CREAT)) + { + // grep all of the directory path + 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; + // update dirname to node + if (vfs_lookup(dirname, &node) != 0) + { + uart_sendlinek("cannot ocreate no dir name\r\n"); + return -1; + } + // create a new file node on node, &node is new file, 3rd arg is filename + node->v_ops->create(node, &node, pathname + last_slash_idx + 1); + *target = kmalloc(sizeof(struct file)); + // attach opened file on the new node + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + else // 2. Create a new file handle for this vnode if found. + { + // attach opened file on the node + + *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; +} + +// file ops +int vfs_close(struct file *file) +{ + // 1. release the file handle + // 2. Return error code if fails + file->f_ops->close(file); + return 0; +} + +// file ops +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); +} + +// file ops +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); +} + +// for device operations only +int vfs_mknod(char* pathname, int id) +{ + struct file* f; + // create leaf and its file operations + vfs_open(pathname, O_CREAT, &f); + f->vnode->f_ops = ®_dev[id]; + vfs_close(f); + return 0; +} + +// 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_sendline("%s",testbufr); + +// struct file *testfile_initramfs; +// vfs_open("/initramfs/get_simpleexec.sh", O_CREAT, &testfile_initramfs); +// vfs_read(testfile_initramfs, testbufr, 30); +// uart_sendline("%s", testbufr); +// } + +char *get_absolute_path(char *path, char *curr_working_dir) +{ + char absolute_path[MAX_PATH_NAME + 1] = {}; + int max_pathdeep = 10; + struct dir_path *ctmp = kmalloc(sizeof(struct dir_path) * max_pathdeep); + int deep = 0; + + if (path[0] == '/') + { + // uart_sendlinek("Input path is absolute_path: %s\n", path); + strcpy(absolute_path, path); + } + else + { + // uart_sendlinek("Input path is relative_path: %s\n", path); + strcpy(absolute_path, curr_working_dir); + if (strcmp(curr_working_dir, "/") != 0) + { + strcat(absolute_path, "/"); + } + strcat(absolute_path, path); + } + // uart_sendlinek("absolute_path: %s\n", absolute_path); + // uart_sendlinek("strlen of bsolute_path: %d\n", strlen(absolute_path)); + + int dir_namesize = 0; + for (int i = 0; i < strlen(absolute_path); i++) + { + dir_namesize++; + if (absolute_path[i + 1] == '/' || i + 1 == strlen(absolute_path)) + { + ctmp[deep].size = dir_namesize; + ctmp[deep].dir = &absolute_path[i + 1 - dir_namesize]; + dir_namesize = 0; + // deep ++; + // uart_sendlinek("ctmp[%d].dir : %s\n", deep, ctmp[deep].dir); + // uart_sendlinek("size : %d\n", ctmp[deep].size); + + if (!strncmp(ctmp[deep].dir, "/..", 3)) + { + // uart_sendlinek("Go back!!\n"); + deep > 0 ? deep-- : deep; + } + else if (!strncmp(ctmp[deep].dir, "/.", 2) || ctmp[deep].size <= 1) + { + // uart_sendlinek("Do Nothing!!\n"); + } + else + { + deep++; + } + } + } + + // uart_sendlinek("deep : %d\n", deep); + int n = 0; + char *cpath = &path[n]; + if (deep == 0) + { + // memset(path,0,MAX_PATH_NAME + 1); + strncpy(cpath, "/\0", 2); + kfree(ctmp); + // uart_sendlinek("absolute_path: %s\n", path); + return path; + } + + for (int i = 0; i < deep; i++) + { + strncpy(cpath, ctmp[i].dir, ctmp[i].size); + n += ctmp[i].size; + cpath = &path[n]; + strncpy(cpath, "\0", 1); + } + + // uart_sendlinek("absolute_path: %s\n", path); + kfree(ctmp); + return path; +} + +void vfs_dump(struct vnode *_vnode, int level) +{ + // uart_sendlinek("In vfs_dump\n"); + displaylayer(level); + if (_vnode->mount != 0) + { + // tmpfs_dump(_vnode->internal, level); + _vnode->v_ops->dump(_vnode, level); + while (_vnode->mount) + { + displaylayer(level); + uart_sendlinek(" !!mount!!\n"); + _vnode = _vnode->mount->root; + } + vfs_dump(_vnode, level); + } + else + { + // tmpfs_dump(_vnode->internal, level); + _vnode->v_ops->dump(_vnode, level); + } +} + +void vfs_ls() +{ + struct vnode *node; + uart_sendlinek("In directory : %s\n",curr_thread->curr_working_dir); + if (vfs_lookup(curr_thread->curr_working_dir, &node) == 0) + { + node->v_ops->ls(node); + } +} + +void vfs_cd(char *filepath) +{ + uart_sendlinek("Before change directory : %s\n",curr_thread->curr_working_dir); + + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, filepath); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + // uart_sendlinek("abs_path : %s\n",abs_path); + strcpy(curr_thread->curr_working_dir, abs_path); + + uart_sendlinek("In directory : %s\n",curr_thread->curr_working_dir); +} + +void displaylayer(int level) +{ + for (int i = 0; i < level; i++) + { + uart_sendlinek(" "); + } +} \ No newline at end of file diff --git a/lab7/kernel/src/vfs_dev_framebuffer.c b/lab7/kernel/src/vfs_dev_framebuffer.c new file mode 100644 index 000000000..3ddff0935 --- /dev/null +++ b/lab7/kernel/src/vfs_dev_framebuffer.c @@ -0,0 +1,131 @@ +#include "vfs.h" +#include "vfs_dev_framebuffer.h" +#include "uart1.h" +#include "memory.h" +#include "stdio.h" +#include "mbox.h" +#include "string.h" +#include "exception.h" +#include "debug.h" + +// #define MBOX_CH_PROP 8 + +// //The following code is for mailbox initialize used in lab7. +unsigned int width, height, pitch, isrgb; /* dimensions and channel order */ +unsigned char *lfb; /* raw frame buffer address */ + +struct file_operations dev_framebuffer_operations = {dev_framebuffer_write, (void *)dev_framebuffer_op_deny, dev_framebuffer_open, dev_framebuffer_close, dev_framebuffer_lseek64, (void *)dev_framebuffer_op_deny}; + +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +int init_dev_framebuffer() +{ + // The following code is for mailbox initialize used in lab7. + pt[0] = 35 * 4; + pt[1] = MBOX_TAG_REQUEST_CODE; + + pt[2] = 0x48003; // set phy wh + pt[3] = 8; + pt[4] = 8; + pt[5] = 1024; // FrameBufferInfo.width + pt[6] = 768; // FrameBufferInfo.height + + pt[7] = 0x48004; // set virt wh + pt[8] = 8; + pt[9] = 8; + pt[10] = 1024; // FrameBufferInfo.virtual_width + pt[11] = 768; // FrameBufferInfo.virtual_height + + pt[12] = 0x48009; // set virt offset + pt[13] = 8; + pt[14] = 8; + pt[15] = 0; // FrameBufferInfo.x_offset + pt[16] = 0; // FrameBufferInfo.y.offset + + pt[17] = 0x48005; // set depth + pt[18] = 4; + pt[19] = 4; + pt[20] = 32; // FrameBufferInfo.depth + + pt[21] = 0x48006; // set pixel order + pt[22] = 4; + pt[23] = 4; + pt[24] = 1; // RGB, not BGR preferably + + pt[25] = 0x40001; // get framebuffer, gets alignment on request + pt[26] = 8; + pt[27] = 8; + pt[28] = 4096; // FrameBufferInfo.pointer + pt[29] = 0; // FrameBufferInfo.size + + pt[30] = 0x40008; // get pitch + pt[31] = 4; + pt[32] = 4; + pt[33] = 0; // FrameBufferInfo.pitch + + pt[34] = MBOX_TAG_LAST_BYTE; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)) && pt[20] == 32 && pt[28] != 0) + { + pt[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + width = pt[5]; // get actual physical width + height = pt[6]; // get actual physical height + pitch = pt[33]; // get number of bytes per line + isrgb = pt[24]; // get the actual channel order + lfb = (void *)PHYS_TO_KERNEL_VIRT(((unsigned long)pt[28])); // raw frame buffer address + } + else + { + ERROR("Unable to set screen resolution to 1024x768x32\n"); + } + + return register_dev(&dev_framebuffer_operations); +} + +int dev_framebuffer_write(struct file *file, const void *buf, size_t len) +{ + lock(); + // uart_sendlinek + if (len + file->f_pos > pitch * height) + { + ERROR("How come? dev_framebuffer_write to no where!\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; +} + +long dev_framebuffer_lseek64(struct file *file, long offset, int whence) +{ + lock(); + if (whence == SEEK_SET) + { + file->f_pos = offset; + unlock(); + return file->f_pos; + } + unlock(); + return -1; +} + +int dev_framebuffer_op_deny() +{ + return -1; +} diff --git a/lab7/kernel/src/vfs_dev_uart.c b/lab7/kernel/src/vfs_dev_uart.c new file mode 100644 index 000000000..459c9444c --- /dev/null +++ b/lab7/kernel/src/vfs_dev_uart.c @@ -0,0 +1,53 @@ +#include "vfs.h" +#include "vfs_dev_uart.h" +#include "uart1.h" +#include "memory.h" +#include "string.h" + +struct file_operations dev_file_operations = {dev_uart_write, dev_uart_read, dev_uart_open, dev_uart_close, (void *)dev_uart_op_deny, (void *)dev_uart_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) +{ + char *cbuf = buf; + int i = len; + while (i--) + { + uart_async_send(*(cbuf++)); + } + return len; +} + +int dev_uart_read(struct file *file, void *buf, size_t len) +{ + char *cbuf = buf; + int i = len; + while (i--) + { + *cbuf = uart_async_recv(); + cbuf++; + } + 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; +} + +int dev_uart_op_deny() +{ + return -1; +} diff --git a/lab7/kernel/src/vfs_initramfs.c b/lab7/kernel/src/vfs_initramfs.c new file mode 100644 index 000000000..2915c2b0d --- /dev/null +++ b/lab7/kernel/src/vfs_initramfs.c @@ -0,0 +1,190 @@ +#include "vfs_initramfs.h" +#include "vfs.h" +#include "string.h" +#include "memory.h" +#include "cpio.h" +#include "uart1.h" +#include "debug.h" + +struct file_operations initramfs_file_operations = {initramfs_write, initramfs_read, initramfs_open, initramfs_close, initramfs_lseek64, initramfs_getsize}; +struct vnode_operations initramfs_vnode_operations = {initramfs_lookup, initramfs_create, initramfs_mkdir, initramfs_ls, initramfs_dump}; + +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); + // create entry under _mount, cpio files should be attached on it + 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_START; + int idx = 0; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + struct vnode *filevnode = initramfs_create_vnode(0, file_t); + struct initramfs_inode *fileinode = filevnode->internal; + fileinode->data = filedata; + fileinode->datasize = filesize; + fileinode->name = filepath; + // uart_sendlinek("file name: %s\n",filepath); + ramdir_inode->entry[idx++] = filevnode; + } + + return 0; +} + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum fsnode_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) +{ + // read-only + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) +{ + struct initramfs_inode *inode = file->vnode->internal; + // overflow, shrink size + 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; +} + +long initramfs_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// 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; + } + } + return -1; +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // read-only + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // read-only + return -1; +} + +long initramfs_getsize(struct vnode *vd) +{ + struct initramfs_inode *inode = vd->internal; + return inode->datasize; +} + +void initramfs_dump(struct vnode *vnode, int level) +{ + struct initramfs_inode *inode = (struct initramfs_inode *)vnode->internal; + int child_idx = 0; + + uart_sendlinek("+----------------+\n"); + + for (; child_idx <= INITRAMFS_MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + // uart_sendlinek("0x%x\n",inode->entry[child_idx]); + displaylayer(level+1); + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct initramfs_inode *)vnode->internal)->name); + + // displaylayer(level+2); + // uart_sendlinek("datasize size : %d\n", ((struct initramfs_inode *)vnode->internal)->datasize); + displaylayer(level+2); + uart_sendlinek("datasize size : %d\n", initramfs_getsize(vnode)); + } + displaylayer(level); + uart_sendlinek("+----------------+\n"); +} + +void initramfs_ls(struct vnode *vnode) +{ + struct initramfs_inode *inode = (struct initramfs_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= INITRAMFS_MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct initramfs_inode *)vnode->internal)->name); + } +} diff --git a/lab7/kernel/src/vfs_tmpfs.c b/lab7/kernel/src/vfs_tmpfs.c new file mode 100644 index 000000000..11e939243 --- /dev/null +++ b/lab7/kernel/src/vfs_tmpfs.c @@ -0,0 +1,253 @@ +#include "vfs_tmpfs.h" +#include "vfs.h" +#include "string.h" +#include "memory.h" +#include "uart1.h" + +struct file_operations tmpfs_file_operations = {tmpfs_write, tmpfs_read, tmpfs_open, tmpfs_close, tmpfs_lseek64, tmpfs_getsize}; +struct vnode_operations tmpfs_vnode_operations = {tmpfs_lookup, tmpfs_create, tmpfs_mkdir, tmpfs_ls, tmpfs_dump}; + +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 fsnode_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; +} + +// 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; + // BFS search tree + 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; + } + } + return -1; +} + +// dir ops +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_sendlinek("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_sendlinek("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) + { + uart_sendlinek("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; +} + +// file operations +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; +} + +// file operations +int tmpfs_write(struct file *file, const void *buf, size_t len) +{ + struct tmpfs_inode *inode = file->vnode->internal; + // write from f_pos + memcpy(inode->data + file->f_pos, buf, len); + // update f_pos and size + file->f_pos += len; + if (inode->datasize < file->f_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 buffer overflow, shrink the request read length + // read from f_pos + 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_close(struct file *file) +{ + kfree(file); + return 0; +} + +long tmpfs_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// file ops +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_sendlinek("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_sendlinek("tmpfs create file exists\r\n"); + return -1; + } + } + + if (child_idx > MAX_DIR_ENTRY) + { + uart_sendlinek("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) + { + uart_sendlinek("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode *_vnode = tmpfs_create_vnode(0, file_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; +} + +void tmpfs_dump(struct vnode *vnode, int level) +{ + struct tmpfs_inode *inode = (struct tmpfs_inode *)vnode->internal; + + uart_sendlinek("+-%s", inode->name); + switch (inode->type) + { + case dir_t: + uart_sendlinek("(dir)\n"); + break; + case file_t: + uart_sendlinek("(file)\n"); + break; + + default: + break; + } + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vfs_dump(inode->entry[child_idx], level + 1); + } +} + +void tmpfs_ls(struct vnode *vnode) +{ + struct tmpfs_inode *inode = (struct tmpfs_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct tmpfs_inode *)vnode->internal)->name); + } +} diff --git a/lab7/send_img_to_bootloader.py b/lab7/send_img_to_bootloader.py new file mode 100644 index 000000000..29d43b781 --- /dev/null +++ b/lab7/send_img_to_bootloader.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +from serial import Serial +from pwn import * +import argparse +from sys import platform + +if platform == "linux" or platform == "linux2": + 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 image by uart1...") + 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!") + +else: + 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='COM',default='COM3', type=str, help='COM# 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 image by uart1...") + 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/lab8/bcm2710-rpi-3-b-plus.dtb b/lab8/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..38395a23f Binary files /dev/null and b/lab8/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab8/config.txt b/lab8/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/lab8/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/lab8/create_diskimg/FAT_R.TXT b/lab8/create_diskimg/FAT_R.TXT new file mode 100644 index 000000000..5d2252cd1 --- /dev/null +++ b/lab8/create_diskimg/FAT_R.TXT @@ -0,0 +1 @@ +fat_r test \ No newline at end of file diff --git a/lab8/create_diskimg/create_img.sh b/lab8/create_diskimg/create_img.sh new file mode 100755 index 000000000..cb8363e64 --- /dev/null +++ b/lab8/create_diskimg/create_img.sh @@ -0,0 +1,50 @@ +#!/bin/sh + + +IMG_NAME=sdcard.img + +truncate -s 64M $IMG_NAME + +( +echo o # Create a new empty DOS partition table +echo n # Add a new partition +echo p # Primary partition +echo 1 # Partition number +echo # First sector (Accept default: 1) +echo # Last sector (Accept default: varies) +echo a # Toggle bootable flag +echo t # Change partition type +echo b # W95 FAT32 +echo w # Write changes +) | sudo fdisk $IMG_NAME + +LOOPBACK=$(sudo losetup --partscan --show --find $IMG_NAME) + +echo ${LOOPBACK} | grep --quiet "/dev/loop" + +if [ $? = 1 ]; then + echo "[!] losetup failed!" + exit 1 +fi + +sudo mkfs.msdos -F 32 ${LOOPBACK}p1 + +mkdir -p mnt + +sudo mount -t msdos ${LOOPBACK}p1 mnt + +sudo cp ../kernel/kernel8.img mnt/KERNEL8.IMG +sudo cp ./initramfs.cpio mnt/INITRD +sudo cp ../config.txt mnt/CONFIT.TXT +sudo cp ../bcm2710-rpi-3-b-plus.dtb mnt/BCM2710.DTB +sudo cp ./FAT_R.TXT mnt/FAT_R.TXT +sudo bash -c "echo "Hello" > mnt/HELLO" +sudo bash -c 'echo -e "W\no\nr\nl\nd" > mnt/WORLD' + +sudo umount -l mnt +sudo losetup -d ${LOOPBACK} +sudo rm -rf mnt + +#mv sdcard.img ../ # 1fc400 + +# dd if=./sdcard.img of=/dev/ diff --git a/lab8/create_diskimg/initramfs.cpio b/lab8/create_diskimg/initramfs.cpio new file mode 100644 index 000000000..08fa91452 Binary files /dev/null and b/lab8/create_diskimg/initramfs.cpio differ diff --git a/lab8/create_diskimg/sdcard/bcm2710-rpi-3-b-plus.dtb b/lab8/create_diskimg/sdcard/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..38395a23f Binary files /dev/null and b/lab8/create_diskimg/sdcard/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab8/create_diskimg/sdcard/bootcode.bin b/lab8/create_diskimg/sdcard/bootcode.bin new file mode 100644 index 000000000..23429a14d --- /dev/null +++ b/lab8/create_diskimg/sdcard/bootcode.bin @@ -0,0 +1,1652 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + firmware/bootcode.bin at master · raspberrypi/firmware · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ Skip to content + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+ + + + + + + +Permalink + +
+ +
+
+ + + master + + + + +
+
+
+ Switch branches/tags + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+ +
+ +
+ + +
+ +
+
+
+

Name already in use

+
+
+ +
+
+
+
+ +
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch? +
+ +
+
+ + +
+
+ + + + Go to file + +
+ + + + +
+
+
+ + + + + + + + + +
+ +
+
+ + @popcornmix + + +
+ + Latest commit + bfbd42e + Oct 14, 2022 + + + + + + History + + +
+
+
firmware: arm_dt: Export the bootloader EEPROM RSA public key via device-tree
+
+firmware: Add tryboot A_B mode
+ +
+ +
+
+ + + 1 + + contributor + + +
+ +

+ Users who have contributed to this file +

+
+ + + + + + +
+
+
+
+ + + + + + + + + +
+ +
+ + +
+ + 51.2 KB +
+ +
+ + + + +
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ View raw +
+
+ +
+ + + + +
+ + +
+ + +
+
+ + + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + diff --git a/lab8/create_diskimg/sdcard/config.txt b/lab8/create_diskimg/sdcard/config.txt new file mode 100644 index 000000000..15654a481 --- /dev/null +++ b/lab8/create_diskimg/sdcard/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 diff --git a/lab8/create_diskimg/sdcard/fixup.dat b/lab8/create_diskimg/sdcard/fixup.dat new file mode 100644 index 000000000..e928ee3fc Binary files /dev/null and b/lab8/create_diskimg/sdcard/fixup.dat differ diff --git a/lab8/create_fs/FromTA/initramfs.cpio b/lab8/create_fs/FromTA/initramfs.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/lab8/create_fs/FromTA/initramfs.cpio differ diff --git a/lab8/create_fs/create_cpio.sh b/lab8/create_fs/create_cpio.sh new file mode 100755 index 000000000..f1270ba2c --- /dev/null +++ b/lab8/create_fs/create_cpio.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Go to rootfs folder +# Select all files and put into archives +# '-o, --create' Run in copy-out mode +# '-H FORMAT' archive format, +# newc: SVR4 portable format + +cd rootfs +find . | cpio -o -H newc > ../initramfs.cpio +cd .. diff --git a/lab8/create_fs/lab3_user_proc/linker.ld b/lab8/create_fs/lab3_user_proc/linker.ld new file mode 100644 index 000000000..faee5d7b1 --- /dev/null +++ b/lab8/create_fs/lab3_user_proc/linker.ld @@ -0,0 +1,5 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text) } +} diff --git a/lab8/create_fs/lab3_user_proc/makefile b/lab8/create_fs/lab3_user_proc/makefile new file mode 100644 index 000000000..590bdf420 --- /dev/null +++ b/lab8/create_fs/lab3_user_proc/makefile @@ -0,0 +1,23 @@ +ARMGNU ?= aarch64-linux-gnu + +BUILD_DIR = build +#--------------------------------------------------------------------------------------- + +ASM_FILES = $(wildcard *.S) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_s.o: %.S + @mkdir -p $(@D) + $(ARMGNU)-gcc -c $< -o $@ + +user_proc.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/user_proc.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/user_proc.elf -O binary user_proc.img + +all: user_proc.img + +clean: + rm -rf $(BUILD_DIR) *.img diff --git a/lab8/create_fs/lab3_user_proc/user_proc.S b/lab8/create_fs/lab3_user_proc/user_proc.S new file mode 100644 index 000000000..2d9837651 --- /dev/null +++ b/lab8/create_fs/lab3_user_proc/user_proc.S @@ -0,0 +1,12 @@ +// Run `svc 0` 5 times; svc (el0 -> el1) system calls +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/lab8/create_fs/lab5_user_fork/fork_test.S b/lab8/create_fs/lab5_user_fork/fork_test.S new file mode 100644 index 000000000..463d19529 --- /dev/null +++ b/lab8/create_fs/lab5_user_fork/fork_test.S @@ -0,0 +1,28 @@ +void fork_test(){ + printf("\nFork Test, pid %d\n", get_pid()); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + ++cnt; + + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); + delay(1000000); + ++cnt; + } + } + exit(); + } + else { + printf("parent here, pid %d, child %d\n", get_pid(), ret); + } +} diff --git a/lab8/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio b/lab8/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/lab8/create_fs/lab5_user_fork/initramfs_providedbyTA.cpio differ diff --git a/lab8/create_fs/lab5_user_fork/makefile b/lab8/create_fs/lab5_user_fork/makefile new file mode 100644 index 000000000..160b9729a --- /dev/null +++ b/lab8/create_fs/lab5_user_fork/makefile @@ -0,0 +1,23 @@ +ARMGNU ?= aarch64-linux-gnu + +BUILD_DIR = build +#--------------------------------------------------------------------------------------- + +ASM_FILES = $(wildcard *.S) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_s.o: %.S + @mkdir -p $(@D) + $(ARMGNU)-gcc -c $< -o $@ + +user_proc.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/user_proc.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/user_proc.elf -O binary user_proc.img + +all: fork_test.img + +clean: + rm -rf $(BUILD_DIR) *.img diff --git a/lab8/create_fs/rootfs/file0.txt b/lab8/create_fs/rootfs/file0.txt new file mode 100644 index 000000000..c2e1803c9 --- /dev/null +++ b/lab8/create_fs/rootfs/file0.txt @@ -0,0 +1,2 @@ +[This is file 0] +[Hello World!] diff --git a/lab8/create_fs/rootfs/file1.txt b/lab8/create_fs/rootfs/file1.txt new file mode 100644 index 000000000..5d585c1ac --- /dev/null +++ b/lab8/create_fs/rootfs/file1.txt @@ -0,0 +1,11 @@ +{this is file1} +. +.F +.I +.L +.E +. +.1 +. + + diff --git a/lab8/initramfs.cpio b/lab8/initramfs.cpio new file mode 100644 index 000000000..62ebfcb30 Binary files /dev/null and b/lab8/initramfs.cpio differ diff --git a/lab8/kernel/Makefile b/lab8/kernel/Makefile new file mode 100644 index 000000000..f14f16b85 --- /dev/null +++ b/lab8/kernel/Makefile @@ -0,0 +1,93 @@ +CC := aarch64-linux-gnu-gcc +LD := aarch64-linux-gnu-ld +OBJCOPY := aarch64-linux-gnu-objcopy +GDB := gdb-multiarch +QEMU := qemu-system-aarch64 +KILL_QEMU := killall $(QEMU) + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -mgeneral-regs-only $(cops) +ASMOPS = -Iinclude +QOPS = -M raspi3b \ + -kernel $(NAME).img \ + -dtb ../bcm2710-rpi-3-b-plus.dtb \ + -initrd ../initramfs.cpio \ + -drive if=sd,file=../create_diskimg/sdcard.img,format=raw +GDBOPS = --init-eval-command="file $(BUILD_DIR)/$(NAME).elf" --init-eval-command="target remote :1234" + +RED=\033[0;31m +GREEN=\033[0;32m +YELLOW=\033[0;33m +BLUE=\033[0;34m +PURPLE=\033[0;35m +CYAN=\033[0;36m +RESET=\033[0m + +# Set the TARGET_PLATFORM and _DEBUG macro to be passed to the GCC definition later. +ifdef DEBUG +COPS+=-D_DEBUG=$(DEBUG) +endif + +ifneq (,$(findstring pi,$(MAKECMDGOALS))) +COPS+=-DRPI +FINISH_STR = "\r\n-------------------------------------------------------------\r\n The $(CYAN)kernel8.img$(RESET) intended for the $(BLUE)Raspberry Pi 3B+$(RESET) is ready.\r\n-------------------------------------------------------------" +else # Default to QEMU +COPS += -DQEMU +FINISH_STR = "\r\n-------------------------------------------------\r\n The $(CYAN)kernel8.img$(RESET) intended for the $(BLUE)QEMU$(RESET) is ready.\r\n-------------------------------------------------" +endif + +NAME = kernel8 +BUILD_DIR = build +SRC_DIR = src +LK_SCRIPT = linker.ld +SESSION_NAME := my-os + +.PHONY: all +all : $(NAME).img + +.PHONY: clean +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(CC) $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(CC) $(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) + +$(NAME).img: $(SRC_DIR)/$(LK_SCRIPT) $(OBJ_FILES) + $(LD) -T $(SRC_DIR)/$(LK_SCRIPT) -o $(BUILD_DIR)/$(NAME).elf $(OBJ_FILES) + $(OBJCOPY) $(BUILD_DIR)/$(NAME).elf -O binary $(NAME).img + @echo -e $(FINISH_STR) + +#This target compiles and runs the kernel.img of the QEMU platform by default. +# You can use "make pi run" to run this image in QEMU. +.PHONY: run +run: clean $(NAME).img + $(QEMU) $(QOPS) -serial null -serial stdio + +# This target compiles kernel.img to run on a QEMU. +.PHONY: qemu +qemu: clean $(NAME).img + +# This target compiles kernel.img to run on a Raspberry 3B. +.PHONY: pi +pi: clean $(NAME).img + +# This target will run kernel in QEMU with GDB attached in a tmux session. +.PHONY: debug +debug: clean $(NAME).img + tmux new-session -d -s $(SESSION_NAME) '$(QEMU) $(QOPS) -S -s -serial null -serial stdio' \; split-window -h '$(GDB) $(GDBOPS)' \; attach -t $(SESSION_NAME) + +# This target will kill the QEMU session. +.PHONY: kill +kill: + $(KILL_QEMU) diff --git a/lab8/kernel/include/bcm2837/rpi_base.h b/lab8/kernel/include/bcm2837/rpi_base.h new file mode 100644 index 000000000..f640aaf4d --- /dev/null +++ b/lab8/kernel/include/bcm2837/rpi_base.h @@ -0,0 +1,7 @@ +#ifndef _RPI_BASE_H_ +#define _RPI_BASE_H_ + +#include "bcm2837/rpi_mmu.h" +#define PERIPHERAL_BASE PHYS_TO_KERNEL_VIRT(0x3F000000) + +#endif /*_RPI_BASE_H_ */ diff --git a/lab8/kernel/include/bcm2837/rpi_gpio.h b/lab8/kernel/include/bcm2837/rpi_gpio.h new file mode 100644 index 000000000..e5133708a --- /dev/null +++ b/lab8/kernel/include/bcm2837/rpi_gpio.h @@ -0,0 +1,25 @@ +#ifndef _RPI_GPIO_H_ +#define _RPI_GPIO_H_ + +#include "bcm2837/rpi_base.h" + +#define GPFSEL0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(PERIPHERAL_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0020009C)) + +#endif /*_RPI_GPIO_H_*/ diff --git a/lab8/kernel/include/bcm2837/rpi_irq.h b/lab8/kernel/include/bcm2837/rpi_irq.h new file mode 100644 index 000000000..8423dfea0 --- /dev/null +++ b/lab8/kernel/include/bcm2837/rpi_irq.h @@ -0,0 +1,24 @@ +#ifndef _RPI_IRQ_H_ +#define _RPI_IRQ_H_ + +#include "bcm2837/rpi_base.h" + +/* +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 IRQ_BASIC_PENDING ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B200)) +#define IRQ_PENDING_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B204)) +#define IRQ_PENDING_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B208)) +#define FIQ_CONTROL ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B20C)) +#define ENABLE_IRQS_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B210)) +#define ENABLE_IRQS_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B214)) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B220)) +#define DISABLE_BASIC_IRQS ((volatile unsigned int*)(PERIPHERAL_BASE+0x0000B224)) + +#endif /*_RPI_IRQ_H_*/ diff --git a/lab8/kernel/include/bcm2837/rpi_mbox.h b/lab8/kernel/include/bcm2837/rpi_mbox.h new file mode 100644 index 000000000..ba1001472 --- /dev/null +++ b/lab8/kernel/include/bcm2837/rpi_mbox.h @@ -0,0 +1,17 @@ +#ifndef _RPI_MBOX_H_ +#define _RPI_MBOX_H_ + +#include "bcm2837/rpi_base.h" + +#define MBOX_BASE (PERIPHERAL_BASE+0x0000B880) + +// The register access to a mailbox +// https://jsandler18.github.io/extra/mailbox.html +#define MBOX_READ ((volatile unsigned int*)(MBOX_BASE+0x00)) +#define MBOX_POLL ((volatile unsigned int*)(MBOX_BASE+0x10)) +#define MBOX_SENDER ((volatile unsigned int*)(MBOX_BASE+0x14)) +#define MBOX_STATUS ((volatile unsigned int*)(MBOX_BASE+0x18)) +#define MBOX_CONFIG ((volatile unsigned int*)(MBOX_BASE+0x1C)) +#define MBOX_WRITE ((volatile unsigned int*)(MBOX_BASE+0x20)) + +#endif /*_RPI_MBOX_H_ */ diff --git a/lab8/kernel/include/bcm2837/rpi_mmu.h b/lab8/kernel/include/bcm2837/rpi_mmu.h new file mode 100644 index 000000000..e0d82c7dd --- /dev/null +++ b/lab8/kernel/include/bcm2837/rpi_mmu.h @@ -0,0 +1,18 @@ +#ifndef _RPI_MMU_H_ +#define _RPI_MMU_H_ + +#define PHYS_TO_VIRT(x) (x + 0xffff000000000000) +#define VIRT_TO_PHYS(x) (x - 0xffff000000000000) +#define ENTRY_ADDR_MASK 0x0000fffffffff000L + +#define PHYS_TO_KERNEL_VIRT(x) (((unsigned long)(x)) | 0xffff000000000000) +#define KERNEL_VIRT_TO_PHYS(x) (((unsigned long)(x)) & ~0xffff000000000000) + +// e.g. size=0x13200, alignment=0x1000 -> 0x14000 +#define ALIGN_UP(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1)) +// e.g. size=0x13200, alignment=0x1000 -> 0x13000 +#define ALIGN_DOWN(size, alignment) ((size) & ~((alignment) - 1)) + +#define IS_NOT_ALIGN(ptr, alignment) (((unsigned long)ptr & ((alignment) - 1)) != 0) + +#endif /*_RPI_MMU_H_ */ diff --git a/lab8/kernel/include/bcm2837/rpi_uart1.h b/lab8/kernel/include/bcm2837/rpi_uart1.h new file mode 100644 index 000000000..959130656 --- /dev/null +++ b/lab8/kernel/include/bcm2837/rpi_uart1.h @@ -0,0 +1,19 @@ +#ifndef _RPI_UART1_H_ +#define _RPI_UART1_H_ + +#include "bcm2837/rpi_base.h" + +#define AUX_ENABLES ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215004)) +#define AUX_MU_IO_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215040)) +#define AUX_MU_IER_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215044)) +#define AUX_MU_IIR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215048)) +#define AUX_MU_LCR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x0021504C)) +#define AUX_MU_MCR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215050)) +#define AUX_MU_LSR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215054)) +#define AUX_MU_MSR_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(PERIPHERAL_BASE+0x0021505C)) +#define AUX_MU_CNTL_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215060)) +#define AUX_MU_STAT_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215064)) +#define AUX_MU_BAUD_REG ((volatile unsigned int*)(PERIPHERAL_BASE+0x00215068)) + +#endif /*_RPI_UART1_H_ */ diff --git a/lab8/kernel/include/cpio.h b/lab8/kernel/include/cpio.h new file mode 100644 index 000000000..bfe2c7044 --- /dev/null +++ b/lab8/kernel/include/cpio.h @@ -0,0 +1,47 @@ +#ifndef _CPIO_H_ +#define _CPIO_H_ + +/* + cpio format : https://manpages.ubuntu.com/manpages/bionic/en/man5/cpio.5.html + We are using "newc" format + 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 constant, to check whether it is big endian or little endian + +// Using newc archive format +struct cpio_newc_header +{ + char c_magic[6]; // fixed, "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]; +}; + +/* 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); + +unsigned int padding_4byte(unsigned int size); + +extern void *CPIO_DEFAULT_START; + +#define CPIO_for_each(c_filepath, c_filesize, c_filedata) \ + struct cpio_newc_header *header_ptr = CPIO_DEFAULT_START; \ + int err = cpio_newc_parse_header(header_ptr, c_filepath, c_filesize, c_filedata, &header_ptr); \ + for (; header_ptr&&!err; cpio_newc_parse_header(header_ptr, c_filepath, c_filesize, c_filedata, &header_ptr)) + +#endif /* _CPIO_H_ */ \ No newline at end of file diff --git a/lab8/kernel/include/debug.h b/lab8/kernel/include/debug.h new file mode 100644 index 000000000..2e7d97904 --- /dev/null +++ b/lab8/kernel/include/debug.h @@ -0,0 +1,19 @@ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include "uart1.h" + +#define CYAN "\e[0;36m" // 青色 +#define HRED "\e[0;91m" +#define CRESET "\e[0m" + +#define WARING(fmt, ...) PRINT_MESSAGE(LEVEL_ERROR, "WARING", CYAN, fmt, ##__VA_ARGS__) +#define ERROR(fmt, ...) PRINT_MESSAGE(LEVEL_ERROR, "ERROR", HRED, fmt, ##__VA_ARGS__) +#define PRINT_MESSAGE(level, prefix, color, fmt, ...) \ + do \ + { \ + uart_sendlinek(color "[" prefix "] " CRESET fmt, ##__VA_ARGS__); \ + } while (0) + +#endif /* _DEBUG_H_ */ \ No newline at end of file diff --git a/lab8/kernel/include/dtb.h b/lab8/kernel/include/dtb.h new file mode 100644 index 000000000..7076d8683 --- /dev/null +++ b/lab8/kernel/include/dtb.h @@ -0,0 +1,26 @@ +#ifndef _DTB_H_ +#define _DTB_H_ + +#define uint32_t unsigned int +#define uint64_t unsigned long long + +// manipulate device tree with dtb file format +// linux kernel fdt.h +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +typedef void (*dtb_callback)(uint32_t node_type, char *name, void *value, uint32_t name_size); + +uint32_t uint32_endian_big2little(uint32_t data); +uint64_t uint64_endian_big2little(uint64_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 dtb_find_and_store_reserved_memory(); + +#endif \ No newline at end of file diff --git a/lab8/kernel/include/exception.h b/lab8/kernel/include/exception.h new file mode 100644 index 000000000..eb975a1aa --- /dev/null +++ b/lab8/kernel/include/exception.h @@ -0,0 +1,92 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" + +#define SYSCALL(number, function) \ + case number: \ + function; \ + break; + +// 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_KERNEL_VIRT(0x40000060))) +#define INTERRUPT_SOURCE_CNTPNSIRQ (1 << 1) +#define INTERRUPT_SOURCE_GPU (1 << 8) +#define IRQ_PENDING_1_AUX_INT (1 << 29) + +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; + +#define MEMFAIL_DATA_ABORT_LOWER 0b100100 // esr_el1 +#define MEMFAIL_INST_ABORT_LOWER 0b100000 // EC, bits [31:26] + +#define TF_LEVEL0 0b000100 // iss IFSC, bits [5:0] +#define TF_LEVEL1 0b000101 +#define TF_LEVEL2 0b000110 +#define TF_LEVEL3 0b000111 + +#define ESR_EL1_EC_SHIFT 26 +#define ESR_EL1_EC_MASK 0x3F +#define ESR_EL1_EC_SVC64 0x15 + +typedef struct +{ + unsigned int iss : 25, // Instruction specific syndrome + il : 1, // Instruction length bit + ec : 6; // Exception class +} esr_el1_t; + +void el1_interrupt_enable(); +void el1_interrupt_disable(); + +void lock(); +void unlock(); + +void el1h_sync_router(trapframe_t *tpf); +void el1h_irq_router(trapframe_t *tpf); +void el0_sync_router(trapframe_t *tpf); +void el0_irq_router(trapframe_t *tpf); + +void invalid_exception_router(unsigned long long x0); // exception_handler.S + +const char *get_exception_name(unsigned long esr_el1); + + +#endif /*_EXCEPTION_H_*/ diff --git a/lab8/kernel/include/irqtask.h b/lab8/kernel/include/irqtask.h new file mode 100644 index 000000000..52783e26b --- /dev/null +++ b/lab8/kernel/include/irqtask.h @@ -0,0 +1,22 @@ +#ifndef _IRQTASK_H_ +#define _IRQTASK_H_ + +#include "u_list.h" + +// smaller is more preemptive +#define UART_IRQ_PRIORITY 1 +#define TIMER_IRQ_PRIORITY 0 + +typedef struct irqtask +{ + struct list_head listhead; + unsigned long long priority; // store priority (smaller number is more preemptive) + void *task_function; // task function pointer +} irqtask_t; + +void irqtask_add(void *task_function, unsigned long long priority); +void irqtask_run(irqtask_t *the_task); +void irqtask_run_preemptive(); +void irqtask_list_init(); + +#endif diff --git a/lab8/kernel/include/mbox.h b/lab8/kernel/include/mbox.h new file mode 100644 index 000000000..25b712383 --- /dev/null +++ b/lab8/kernel/include/mbox.h @@ -0,0 +1,66 @@ +#ifndef _MBOX_H_ +#define _MBOX_H_ + +extern volatile unsigned int pt[36]; + +// Mailbox Register MMIO +// https://jsandler18.github.io/extra/mailbox.html +// include/bcm2837/rpi_mbox.h + +// Mailbox Channels +// https://github.com/raspberrypi/firmware/wiki/Mailboxes +typedef enum +{ + MBOX_POWER_MANAGEMENT = 0, + MBOX_FRAMEBUFFER, + MBOX_VIRTUAL_UART, + MBOX_VCHIQ, + MBOX_LEDS, + MBOX_BUTTONS, + MBOX_TOUCHSCREEN, + MBOX_UNUSED, + MBOX_TAGS_ARM_TO_VC, + MBOX_TAGS_VC_TO_ARM, +} mbox_channel_type; + +// Status Code from Broadcom Videocode Driver +// brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/arm_control.h +enum mbox_status_reg_bits +{ + BCM_ARM_VC_MS_FULL = 0x80000000, + BCM_ARM_VC_MS_EMPTY = 0x40000000, + BCM_ARM_VC_MS_LEVEL = 0x400000FF, +}; + +enum mbox_buffer_status_code +{ + MBOX_REQUEST_PROCESS = 0x00000000, + MBOX_REQUEST_SUCCEED = 0x80000000, + MBOX_REQUEST_FAILED = 0x80000001, +}; + +// Tag +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +// Included partition only +typedef enum +{ + /* Videocore */ + MBOX_TAG_GET_FIRMWARE_VERSION = 0x1, + + /* Hardware */ + MBOX_TAG_GET_BOARD_MODEL = 0x10001, + MBOX_TAG_GET_BOARD_REVISION, + MBOX_TAG_GET_BOARD_MAC_ADDRESS, + MBOX_TAG_GET_BOARD_SERIAL, + MBOX_TAG_GET_ARM_MEMORY, + MBOX_TAG_GET_VC_MEMORY, + MBOX_TAG_GET_CLOCKS, + +} mbox_tag_type; + +#define MBOX_TAG_REQUEST_CODE 0x00000000 +#define MBOX_TAG_LAST_BYTE 0x00000000 + +int mbox_call(mbox_channel_type, unsigned int); + +#endif /*_MBOX_H_*/ diff --git a/lab8/kernel/include/memory.h b/lab8/kernel/include/memory.h new file mode 100644 index 000000000..35f7ddb16 --- /dev/null +++ b/lab8/kernel/include/memory.h @@ -0,0 +1,80 @@ +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" +/* Lab2 */ +void *allocator(unsigned int size); +// void free(void* ptr); + +/* Lab4 */ +#define BUDDY_MEMORY_BASE PHYS_TO_KERNEL_VIRT(0x0) // 0x10000000 - 0x20000000 (SPEC) -> Advanced #3 for all memory region +#define BUDDY_MEMORY_PAGE_COUNT 0x3C000 // let BUDDY_MEMORY use 0x0 ~ 0x3C000000 (SPEC) +#define PAGESIZE 0x1000 // 4KB + +#define CACHE_SEG 0x8 +#define CACHE_offset 3 +#define CACHE_record_num 8 + +// #define MAX_PAGES 0x10000 // 65536 (Entries), PAGESIZE * MAX_PAGES = 0x10000000 (SPEC) + +typedef enum +{ + FRAME_FREE = -2, + FRAME_ALLOCATED = -1, + FRAME_IDX_0 = 0, // 0x1000 + FRAME_IDX_8 = 8, + FRAME_IDX_FINAL = 17, // 0x20 000 000 + FRAME_MAX_IDX = 18 +} frame_value_type; + +typedef enum +{ + CACHE_IDX_0 = 0, // 0x20 + CACHE_IDX_FINAL = 6, // 0x800 + CACHE_MAX_IDX = 7 // 0x1000 +} cache_value_type; + +typedef struct frame +{ + struct list_head listhead; // store freelist + int val; // store order + int used; + unsigned int idx; +} frame_t; + +typedef struct cache +{ + struct list_head listhead; // store freelist + void *data_base; + int cache_order; + int max_available; + int available; + unsigned long long cache_record[CACHE_record_num]; +} cache_t; + +void allocator_init(); +frame_t *release_redundant(frame_t *frame); +frame_t *get_buddy(frame_t *frame); +int coalesce(frame_t *frame_ptr); + +void dump_page_info(); +void dump_cache_info(); + +// buddy system +void *page_malloc(unsigned int size); +void page_free(frame_t *ptr); +void page2caches(int order); +void *cache_malloc(unsigned int size); +void cache_free(void *ptr); + +void *kmalloc(unsigned int size); +void kfree(void *ptr); +void memory_reserve(unsigned long long start, unsigned long long end); + +void freelist_init(); +void page2caches(int c_val); +void *find_CACHE(cache_t *ptr); +frame_t *find_free_page(int val); + +#endif /* _MEMORY_H_ */ \ No newline at end of file diff --git a/lab8/kernel/include/mmu.h b/lab8/kernel/include/mmu.h new file mode 100644 index 000000000..bb58ee390 --- /dev/null +++ b/lab8/kernel/include/mmu.h @@ -0,0 +1,101 @@ +#ifndef _MMU_H_ +#define _MMU_H_ + +#include "stddef.h" +// tcr_el1: The control register for stage 1 of the EL1&0 translation regime. +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) // T0SZ 和 T1SZ 設為 16 +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) // TG0 和 TG1 設為 4KB +#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 // Table Entry Armv8_a_address_translation p.14 +#define PD_BLOCK 0b01L // Block Entry +#define PD_UNX (1L << 54) // non-executable page frame for EL0 if set +#define PD_KNX (1L << 53) // non-executable page frame for EL1 if set +#define PD_ACCESS (1L << 10) // a page fault is generated if not set +#define PD_RDONLY (1L << 7) // 0 for read-write, 1 for read-only. +#define PD_UK_ACCESS (1L << 6) // 0 for only kernel access, 1 for user/kernel access. + +#define PERIPHERAL_START 0x3C000000L +#define PERIPHERAL_END 0x3F000000L +#define USER_DATA_BASE 0x00000000L +#define USER_STACK_BASE 0x0000fffffffff000L +#define USER_SIGNAL_WRAPPER_VA 0x0000FFFFFFFAF000L //<------------------------------------- +#define USER_EXEC_WRAPPER_VA 0x0000FFFFFFFBF000L //<------------------------------------- + +#define MMU_PGD_BASE 0x1000L +#define MMU_PGD_ADDR (MMU_PGD_BASE + 0x0000L) +#define MMU_PUD_ADDR (MMU_PGD_BASE + 0x1000L) +#define MMU_PTE_ADDR (MMU_PGD_BASE + 0x2000L) + +// Used for EL1 +#define BOOT_PGD_ATTR (PD_TABLE) +#define BOOT_PUD_ATTR (PD_TABLE | PD_ACCESS) +#define BOOT_PTE_ATTR_nGnRnE (PD_BLOCK | PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_UNX | PD_KNX | PD_UK_ACCESS) // p.17 +#define BOOT_PTE_ATTR_NOCACHE (PD_BLOCK | PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2)) + +#ifndef __ASSEMBLER__ + +#include "sched.h" +#include "exception.h" +#include "u_list.h" +#include "stddef.h" + + +#define PERMISSION_INVAILD(userId,VMA_Permission) (userId&~VMA_Permission) +#define DUMP_NAME(number, name) \ + case number: \ + uart_sendlinek(name); \ + uart_sendlinek("\n"); \ + break; + +typedef enum +{ + UNKNOW_AREA = -1, + USER_DATA, + USER_STACK, + PERIPHERAL, + USER_SIGNAL_WRAPPER, + USER_EXEC_WRAPPER +} vma_name_type; + +typedef enum +{ + PGD, + PUD, + PMD, + PTE, +} pagetable_type; + +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; + vma_name_type name; +} vm_area_struct_t; + +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 mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced, vma_name_type name); +void mmu_del_vma(struct thread *t); +void mmu_free_page_tables(size_t *page_table, int level); +// void mmu_set_PTE_readonly(size_t *page_table, int level); +// void mmu_pagetable_copy(unsigned long *dst_page_table, unsigned long *src_page_table, int level); +void mmu_memfail_abort_handle(esr_el1_t *esr_el1); + +vm_area_struct_t *check_vma_overlap(thread_t *t,unsigned long user_va, unsigned long size); +int check_permission(int userId, int requiredPermission); +void dump_vma(); +void dump_pagetable(unsigned long user_va, unsigned long pa); + +#endif //__ASSEMBLER__ + +#endif /* _MMU_H_ */ diff --git a/lab8/kernel/include/power.h b/lab8/kernel/include/power.h new file mode 100644 index 000000000..07da61847 --- /dev/null +++ b/lab8/kernel/include/power.h @@ -0,0 +1,9 @@ +#ifndef _POWER_H_ +#define _POWER_H_ + +#include "bcm2837/rpi_mmu.h" +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC PHYS_TO_KERNEL_VIRT(0x3F10001c) +#define PM_WDOG PHYS_TO_KERNEL_VIRT(0x3F100024) + +#endif /*_POWER_H_*/ diff --git a/lab8/kernel/include/sched.h b/lab8/kernel/include/sched.h new file mode 100644 index 000000000..475bc27e7 --- /dev/null +++ b/lab8/kernel/include/sched.h @@ -0,0 +1,79 @@ +#ifndef _SCHED_H_ +#define _SCHED_H_ + +#include "u_list.h" +#include "vfs.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x10000 +#define KSTACK_SIZE 0x10000 +#define SIGNAL_MAX 64 +#define STACK_BASE_OFFSET 0x00 + +extern void switch_to(void *curr_context, void *next_context); +extern void *get_current(); +extern void store_context(void *curr_context); +extern void load_context(void *curr_context); + +// arch/arm64/include/asm/processor.h - cpu_context +typedef struct thread_context +{ + unsigned long x19; // callee saved registers: the called function will preserve them and restore them before returning + 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; // base pointer for local variable in stack + unsigned long lr; // store return address + unsigned long sp; // stack pointer, varys from function calls + void *pgd; // use for MMU mapping (user space) +} thread_context_t; + +typedef struct thread +{ + list_head_t listhead; // Freelist node + thread_context_t context; // Thread registers + char *data; // Process itself + unsigned int datasize; // Process size + int iszombie; // Process statement + int pid; // Process ID + int isused; // Freelist node statement + char *stack_alloced_ptr; // Process Stack (Process itself) + char *kernel_stack_alloced_ptr; // Process Stack (Kernel syscall) + void (*signal_handler[SIGNAL_MAX + 1])(); // Signal handlers for different signal + int sigcount[SIGNAL_MAX + 1]; // Signal Pending buffer + void (*curr_signal_handler)(); // Allow Signal handler overwritten by others + int signal_is_checking; // Signal Processing Lock + thread_context_t signal_savedContext; // Store registers before signal handler involving + list_head_t vma_list; + char curr_working_dir[MAX_PATH_NAME + 1]; + struct file *file_descriptors_table[MAX_FD + 1]; +} thread_t; + +typedef struct execfile +{ + struct vnode *vnode; + char * pathname; + char *data; + unsigned int filesize; +} execfile; + +void schedule_timer(char *notuse); +void thread_sched_init(); +void idle(); +void schedule(); +void kill_zombies(); +void thread_exit(); +thread_t *thread_create(void *start); +int exec_thread(); +void exec_proc(); +void exec_wrapper(); + +void foo(); + +#endif /* _SCHED_H_ */ diff --git a/lab8/kernel/include/sdhost.h b/lab8/kernel/include/sdhost.h new file mode 100644 index 000000000..ed9639b32 --- /dev/null +++ b/lab8/kernel/include/sdhost.h @@ -0,0 +1,69 @@ +// mmio +#define KVA 0xffff000000000000 +#define MMIO_BASE (KVA + 0x3f000000) + +// SD card command +#define GO_IDLE_STATE 0 +#define SEND_OP_CMD 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 + #define VOLTAGE_CHECK_PATTERN 0x1aa +#define STOP_TRANSMISSION 12 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define WRITE_SINGLE_BLOCK 24 +#define SD_APP_OP_COND 41 + #define SDCARD_3_3V (1 << 21) + #define SDCARD_ISHCS (1 << 30) + #define SDCARD_READY (1 << 31) +#define APP_CMD 55 + +// gpio +#define GPIO_BASE (MMIO_BASE + 0x200000) +#define GPIO_GPFSEL4 (GPIO_BASE + 0x10) +#define GPIO_GPFSEL5 (GPIO_BASE + 0x14) +#define GPIO_GPPUD (GPIO_BASE + 0x94) +#define GPIO_GPPUDCLK1 (GPIO_BASE + 0x9c) + +// sdhost +#define SDHOST_BASE (MMIO_BASE + 0x202000) +#define SDHOST_CMD (SDHOST_BASE + 0) + #define SDHOST_READ 0x40 + #define SDHOST_WRITE 0x80 + #define SDHOST_LONG_RESPONSE 0x200 + #define SDHOST_NO_REPONSE 0x400 + #define SDHOST_BUSY 0x800 + #define SDHOST_NEW_CMD 0x8000 +#define SDHOST_ARG (SDHOST_BASE + 0x4) +#define SDHOST_TOUT (SDHOST_BASE + 0x8) + #define SDHOST_TOUT_DEFAULT 0xf00000 +#define SDHOST_CDIV (SDHOST_BASE + 0xc) + #define SDHOST_CDIV_MAXDIV 0x7ff + #define SDHOST_CDIV_DEFAULT 0x148 +#define SDHOST_RESP0 (SDHOST_BASE + 0x10) +#define SDHOST_RESP1 (SDHOST_BASE + 0x14) +#define SDHOST_RESP2 (SDHOST_BASE + 0x18) +#define SDHOST_RESP3 (SDHOST_BASE + 0x1c) +#define SDHOST_HSTS (SDHOST_BASE + 0x20) + #define SDHOST_HSTS_MASK (0x7f8) + #define SDHOST_HSTS_ERR_MASK (0xf8) + #define SDHOST_HSTS_DATA (1 << 0) +#define SDHOST_PWR (SDHOST_BASE + 0x30) +#define SDHOST_DBG (SDHOST_BASE + 0x34) + #define SDHOST_DBG_FSM_DATA 1 + #define SDHOST_DBG_FSM_MASK 0xf + #define SDHOST_DBG_MASK (0x1f << 14 | 0x1f << 9) + #define SDHOST_DBG_FIFO (0x4 << 14 | 0x4 << 9) +#define SDHOST_CFG (SDHOST_BASE + 0x38) + #define SDHOST_CFG_DATA_EN (1 << 4) + #define SDHOST_CFG_SLOW (1 << 3) + #define SDHOST_CFG_INTBUS (1 << 1) +#define SDHOST_SIZE (SDHOST_BASE + 0x3c) +#define SDHOST_DATA (SDHOST_BASE + 0x40) +#define SDHOST_CNT (SDHOST_BASE + 0x50) + +void sd_init(); +void writeblock(int block_idx, void* buf); +void readblock(int block_idx, void* buf); \ No newline at end of file diff --git a/lab8/kernel/include/shell.h b/lab8/kernel/include/shell.h new file mode 100644 index 000000000..276d085d9 --- /dev/null +++ b/lab8/kernel/include/shell.h @@ -0,0 +1,40 @@ +#ifndef _SHELL_H_ +#define _SHELL_H_ + +#define CLI_MAX_CMD 15 +#define CMD_MAX_LEN 32 +#define CMD_MAX_PARAM 10 +#define MSG_MAX_LEN 128 + +typedef struct CLI_CMDS +{ + char command[CMD_MAX_LEN]; + char help[MSG_MAX_LEN]; + int (*func)(int, char **); +} CLI_CMDS; + +int _parse_args(char *buffer, int *argc, char **argv); + +void start_shell(); + +void cli_flush_buffer(char *, int); +void cli_cmd_read(char *); +void cli_cmd_exec(char *); +void cli_print_banner(); + +int do_cmd_help(int argc, char **argv); +int do_cmd_hello(int argc, char **argv); +int do_cmd_info(int argc, char **argv); +int do_cmd_reboot(int argc, char **argv); +int do_cmd_ls(int argc, char **argv); +int do_cmd_cd(int argc, char **argv); +int do_cmd_cat(int argc, char **argv); +int do_cmd_malloc(int argc, char **argv); +int do_cmd_dtb(int argc, char **argv); +int do_cmd_exec(int argc, char **argv); +int do_cmd_setTimeout(int argc, char **argv); +int do_cmd_set2sAlert(int argc, char **argv); +int do_cmd_mtest(int argc, char **argv); +int do_cmd_ttest(int argc, char **argv); +int do_cmd_ftest(int argc, char **argv); +#endif /* _SHELL_H_ */ diff --git a/lab8/kernel/include/signal.h b/lab8/kernel/include/signal.h new file mode 100644 index 000000000..afe2c1617 --- /dev/null +++ b/lab8/kernel/include/signal.h @@ -0,0 +1,49 @@ +#ifndef _SIGNAL_H_ +#define _SIGNAL_H_ + +#include "exception.h" +#include "memory.h" + +#define SIGHUP 1 /* Hangup (POSIX). */ +#define SIGINT 2 /* Interrupt (ANSI). */ +#define SIGQUIT 3 /* Quit (POSIX). */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ +#define SIGTRAP 5 /* Trace trap (POSIX). */ +#define SIGABRT 6 /* Abort (ANSI). */ +#define SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define SIGBUS 7 /* BUS error (4.2 BSD). */ +#define SIGFPE 8 /* Floating-point exception (ANSI). */ +#define SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define SIGPIPE 13 /* Broken pipe (POSIX). */ +#define SIGALRM 14 /* Alarm clock (POSIX). */ +#define SIGTERM 15 /* Termination (ANSI). */ +#define SIGSTKFLT 16 /* Stack fault. */ +#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define SIGCHLD 17 /* Child status has changed (POSIX). */ +#define SIGCONT 18 /* Continue (POSIX). */ +#define SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define SIGTTIN 21 /* Background read from tty (POSIX). */ +#define SIGTTOU 22 /* Background write to tty (POSIX). */ +#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define SIGPWR 30 /* Power failure restart (System V). */ +#define SIGSYS 31 /* Bad system call. */ +#define SIGUNUSED 31 + +void signal_default_handler(); +void check_signal(trapframe_t *tpf); +void run_signal(trapframe_t *tpf, int signal); +//void __attribute__((aligned(PAGESIZE))) signal_handler_wrapper(); +void signal_handler_wrapper(); + +#endif diff --git a/lab8/kernel/include/stddef.h b/lab8/kernel/include/stddef.h new file mode 100644 index 000000000..61b71d842 --- /dev/null +++ b/lab8/kernel/include/stddef.h @@ -0,0 +1,12 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define size_t unsigned long +#define NULL ((void *)0) + +// typedef enum { +// false = 0, +// true = 1 +// } boolean; + +#endif \ No newline at end of file diff --git a/lab8/kernel/include/stdio.h b/lab8/kernel/include/stdio.h new file mode 100644 index 000000000..5ee646448 --- /dev/null +++ b/lab8/kernel/include/stdio.h @@ -0,0 +1,29 @@ +#include "sched.h" +#include "vfs.h" +extern thread_t *curr_thread; + +#define stdio_op(fd, c, len) \ + switch (fd) \ + { \ + case (stdin): \ + (vfs_read(curr_thread->file_descriptors_table[fd], c, len)); \ + break; \ + case (stdout): \ + (vfs_write(curr_thread->file_descriptors_table[fd], c, len)); \ + break; \ + case (stderr): \ + (vfs_write(curr_thread->file_descriptors_table[fd], c, len)); \ + break; \ + default: \ + break; \ + } + +char getchar(); +void putchar(char c); +void put_int(int num); +void puts(const char *s); +void put_hex(unsigned int num); +int atoi(char *str); +void Readfile(char *str, int size); +int fake_log2(unsigned long long n); +void delay(int s); \ No newline at end of file diff --git a/lab8/kernel/include/string.h b/lab8/kernel/include/string.h new file mode 100644 index 000000000..f190cdc83 --- /dev/null +++ b/lab8/kernel/include/string.h @@ -0,0 +1,15 @@ +#include "stddef.h" +#define VSPRINT_MAX_BUF_SIZE 0x100 + +size_t strlen(const char *str); + +int strcmp(const char *p1, const char *p2); +int strncmp(const char *s1, const char *s2, unsigned long long n); +int strcasecmp(const char *s1, const char *s2); +char *strcat(char *dest, const char *src); +char *strncat(char *dest, const char *src, int n); +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, unsigned long long n); +unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); +void *memset(void *s, int c, size_t n); +char *memcpy(void *dest, const void *src, unsigned long long len); diff --git a/lab8/kernel/include/syscall.h b/lab8/kernel/include/syscall.h new file mode 100644 index 000000000..42464df2a --- /dev/null +++ b/lab8/kernel/include/syscall.h @@ -0,0 +1,39 @@ +#ifndef _SYSCALL_H_ +#define _SYSCALL_H_ + +#include "exception.h" +#include + +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 *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset); + +int open(trapframe_t *tpf, const char *pathname, int flags); +int close(trapframe_t *tpf, int fd); +long write(trapframe_t *tpf, int fd, const void *buf, unsigned long count); +long read(trapframe_t *tpf, int fd, void *buf, unsigned long count); +int mkdir(trapframe_t *tpf, const char *pathname, unsigned mode); +int mount(trapframe_t *tpf, const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); +int chdir(trapframe_t *tpf, const char *path); +long lseek64(trapframe_t *tpf, int fd, long offset, int whence); +int ioctl(trapframe_t *tpf, int fd, unsigned long request, void *info); +int sync(trapframe_t *tpf); + +void syscall_lock(trapframe_t *tpf); +void syscall_unlock(trapframe_t *tpf); + +unsigned int get_file_size(char *thefilepath); +char *get_file_start(char *thefilepath); + +#endif /* _SYSCALL_H_*/ diff --git a/lab8/kernel/include/timer.h b/lab8/kernel/include/timer.h new file mode 100644 index 000000000..8b02b6b8d --- /dev/null +++ b/lab8/kernel/include/timer.h @@ -0,0 +1,36 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "u_list.h" +#include "bcm2837/rpi_mmu.h" +#define CORE0_TIMER_IRQ_CTRL PHYS_TO_VIRT(0x40000040) //-------------------------------------------------------------------------- + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_handler(); + +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; + +typedef enum +{ + setSecond = 0, + setTick = 1 +} timer_type_t; + +// now the callback only support "funcion(char *)", char* in args +void timer_event_callback(timer_event_t *timer_event); +void add_timer(void *callback, unsigned long long timeout, char *args, int inTickFormat); +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 timer_set2sAlert(char *str); +void timer_list_init(); +int timer_list_get_size(); + +#endif /* _TIMER_H_ */ diff --git a/lab8/kernel/include/u_list.h b/lab8/kernel/include/u_list.h new file mode 100644 index 000000000..a4a293954 --- /dev/null +++ b/lab8/kernel/include/u_list.h @@ -0,0 +1,213 @@ +#ifndef _U_LIST_H_ +#define _U_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. + * 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_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; \ + !list_is_head(pos, (head)); \ + pos = n, n = 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; +} +// ===================================================================================================================== +#define __LIST_HAVE_TYPEOF 1 +/* Offset of member MEMBER in a struct of type TYPE. */ +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *)0)->member) *__pmember = (ptr); \ + (type *)((char *)__pmember - offsetof(type, member)); \ + }) +#else /* __LIST_HAVE_TYPEOF */ +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) +#endif /* __LIST_HAVE_TYPEOF */ +#endif /* container_of */ +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif /* __LIST_HAVE_TYPEOF */ + +#endif /* _U_LIST_H_ */ diff --git a/lab8/kernel/include/uart1.h b/lab8/kernel/include/uart1.h new file mode 100644 index 000000000..7b6bd70ce --- /dev/null +++ b/lab8/kernel/include/uart1.h @@ -0,0 +1,18 @@ +#ifndef _UART1_H_ +#define _UART1_H_ + +void uart_init(); +void uart_flush_FIFO(); +char uart_recv(); +void uart_send(unsigned int c); + +void uart_r_irq_handler(); +void uart_w_irq_handler(); +char uart_async_recv(); +void uart_async_send(char c); + +int uart_sendlinek(char* fmt, ...); +void uart_interrupt_enable(); +void uart_interrupt_disable(); + +#endif /*_UART1_H_*/ diff --git a/lab8/kernel/include/vfs.h b/lab8/kernel/include/vfs.h new file mode 100644 index 000000000..8fd66b49c --- /dev/null +++ b/lab8/kernel/include/vfs.h @@ -0,0 +1,110 @@ +#ifndef _VFS_H_ +#define _VFS_H_ + +#include "stddef.h" + +#define MAX_PATH_NAME 255 +#define MAX_FD 16 +#define O_CREAT 00000100 +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#define MAX_FS_REG 0x50 +#define MAX_DEV_REG 0x10 + +enum stdio_fd +{ + stdin, + stdout, + stderr +}; + +enum fsnode_type +{ + dir_t, + file_t +}; + +struct vnode +{ + struct mount *mount; // Superblock : represents mounted fs + struct vnode_operations *v_ops; // inode & dentry Ops: represents kernel methods for vnode + struct file_operations *f_ops; // file Ops : represents process methods for opened file + void *internal; // vnode itself : directly point to fs's vnode + struct vnode *parent; +}; + +struct mount +{ + struct vnode *root; + struct filesystem *fs; +}; + +struct filesystem +{ + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); + int (*sync)(struct filesystem *fs); +}; + +// 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 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); + int (*isdir)(struct vnode *dir_node); + int (*getname)(struct vnode *dir_node, const char **name); + int (*getsize)(struct vnode *dir_node); + void (*ls)(struct vnode *dir_node); + void (*dump)(struct vnode *dir_node, int level); +}; + +struct dir_path +{ + char *dir; + int size; +}; + +extern struct filesystem reg_fs[MAX_FS_REG]; + +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); +int vfs_mknod(char *pathname, int id); +int vfs_sync(struct filesystem *fs); + +void rootfs_init(); +char *get_absolute_path(char *path, char *curr_working_dir); + +void vfs_cd(char *filepath); +void vfs_dump(struct vnode *_vnode, int level); +void vfs_ls(); +void displaylayer(int level); + +#endif /* _VFS_H_ */ diff --git a/lab8/kernel/include/vfs_dev_framebuffer.h b/lab8/kernel/include/vfs_dev_framebuffer.h new file mode 100644 index 000000000..b996ee8e3 --- /dev/null +++ b/lab8/kernel/include/vfs_dev_framebuffer.h @@ -0,0 +1,24 @@ +#ifndef _DEV_FRAMEBUFFER_H_ +#define _DEV_FRAMEBUFFER_H_ + +#include "stddef.h" +#include "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); +long dev_framebuffer_lseek64(struct file *file, long offset, int whence); +int dev_framebuffer_op_deny(); + +#endif diff --git a/lab8/kernel/include/vfs_dev_uart.h b/lab8/kernel/include/vfs_dev_uart.h new file mode 100644 index 000000000..b46dceed4 --- /dev/null +++ b/lab8/kernel/include/vfs_dev_uart.h @@ -0,0 +1,15 @@ +#ifndef _DEV_UART_H_ +#define _DEV_UART_H_ + +#include "stddef.h" +#include "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); +int dev_uart_op_deny(); + +#endif diff --git a/lab8/kernel/include/vfs_fat32.h b/lab8/kernel/include/vfs_fat32.h new file mode 100644 index 000000000..60824c2e3 --- /dev/null +++ b/lab8/kernel/include/vfs_fat32.h @@ -0,0 +1,177 @@ +#ifndef FAT32_H +#define FAT32_H + +#include "u_list.h" +#include "vfs.h" + +#define BLOCK_SIZE 512 +#define FAT32_MAX_FILENAME 8 +#define FAT32_MAX_EXTENSION 3 + +#define FAT32_FREE_CLUSTER 0x00000000 +#define FAT32_END_OF_CHAIN 0x0FFFFFF8 + +// #define CLUSTER_ENTRY_PER_BLOCK (BLOCK_SIZE / sizeof(struct cluster_entry_t)) +// #define DIR_PER_BLOCK (BLOCK_SIZE / sizeof(struct dir_t)) +// #define INVALID_CID 0x0ffffff8 // Last cluster in file (EOC), reserved by FAT32 + +static unsigned int *file_allocation_table; +static struct fs_info *fs_info; +struct partition_t +{ + unsigned char status; + unsigned char chss_head; + unsigned char chss_sector; + unsigned char chss_cylinder; + unsigned char type; + unsigned char chse_head; + unsigned char chse_sector; + unsigned char chse_cylinder; + unsigned int lba; + unsigned int sectors; +} __attribute__((packed)); + +struct MBR +{ + char bootstrap_code[0x1BE]; // 引導代碼區塊 + struct partition_t partition_table[4]; // 分區表,共4個分區 + unsigned short signature; // MBR簽名,0x55AA +} __attribute__((packed)); + +struct boot_sector_t +{ + unsigned char jmpboot[3]; + unsigned char oemname[8]; + unsigned short bytes_per_sector; + unsigned char sector_per_cluster; // 每簇扇區數 + unsigned short reserved_sector_cnt; // 保留扇區數 + unsigned char fat_cnt; // FAT 表數量,通常為 2 + unsigned short root_entry_cnt; + unsigned short old_sector_cnt; + unsigned char media; + unsigned short sector_per_fat16; + unsigned short sector_per_track; + unsigned short head_cnt; + unsigned int hidden_sector_cnt; // 隱藏扇區數,文件系統開始前的扇區數 + unsigned int sector_cnt; // FAT32 definition + unsigned int sector_per_fat32; // 每個 FAT 表的扇區數 + unsigned short extflags; + unsigned short ver; + unsigned int root_cluster; + unsigned short info; // 文件系統信息扇區號 + unsigned short bkbooksec; + unsigned char reserved[12]; + unsigned char drvnum; + unsigned char reserved1; + unsigned char bootsig; + unsigned int volid; + unsigned char vollab[11]; + unsigned char fstype[8]; +} __attribute__((packed)); + +// FAT32 FSINFO structure +struct fs_info +{ + unsigned int lead_signature; // Should be 0x41615252 + unsigned char reserved1[480]; // Must be zero + unsigned int structure_signature; // Should be 0x61417272 + unsigned int free_count; // Number of free clusters; -1 if unknown + unsigned int next_free; // Next free cluster; 0xFFFFFFFF if unknown + unsigned char reserved2[12]; // Must be zero + unsigned int trail_signature; // Should be 0xAA550000 +} __attribute__((packed)); + +// attr of dir_t +#define ATTR_READ_ONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME_ID 0x08 +#define ATTR_LFN 0x0f +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_FILE_DIR_MASK (ATTR_DIRECTORY | ATTR_ARCHIVE) + +// type of struct fat32_inode +#define FAT_DIR 1 +#define FAT_FILE 2 + +struct fat32_inode +{ + struct boot_sector_t *boot_sector; + struct sfn_file *sfn; + char name[16]; + struct vnode *entry[16]; + // char *data; + int data_block_cnt; + size_t datasize; +}; + +struct sfn_file +{ + char name[FAT32_MAX_FILENAME]; // 文件名,最多8個字節,不足部分使用空格填充 + char extension[FAT32_MAX_EXTENSION]; // 文件擴展名,最多3個字節,不足部分使用空格填充 + char attribute; // 屬性 + char reserved; // 保留位 + char creation_time_tenth_seconds; // 創建時間的1/10秒計數 + unsigned short creation_time; // 創建時間 + unsigned short creation_date; // 創建日期 + unsigned short last_access_date; // 最後訪問日期 + unsigned short first_cluster_high; // 文件起始簇高16位 + unsigned short last_write_time; // 最後修改時間 + unsigned short last_write_date; // 最後修改日期 + unsigned short first_cluster_low; // 文件起始簇低16位 + unsigned int file_size; // 文件大小(字節) +}; + +int register_fat32(); + +int fat32_mount(struct filesystem *fs, struct mount *mount); +int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int fat32_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int fat32_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); +int fat32_isdir(struct vnode *dir_node); +int fat32_getname(struct vnode *dir_node, const char **name); +int fat32_getsize(struct vnode *dir_node); + +int fat32_write(struct file *file, const void *buf, size_t len); +int fat32_read(struct file *file, void *buf, size_t len); +int fat32_open(struct vnode *file_node, struct file **target); +int fat32_close(struct file *file); +long fat32_lseek64(struct file *file, long offset, int whence); +int fat32_sync(struct filesystem *fs); + +void fat32_dump(struct vnode *vnode, int level); +void fat32_ls(struct vnode *vnode); +int fat32_op_deny(); + +static inline unsigned int get_first_cluster(struct sfn_file *entry) +{ + return (entry->first_cluster_high << 16) | entry->first_cluster_low; +} + +static inline void fat32_get_file_name(char *name_array, struct sfn_file *entry) +{ + int idx = 0; + for (int i = 0; i < FAT32_MAX_FILENAME; i++, idx++) + { + if (entry->name[i] == ' ') + { + break; + } + name_array[idx] = entry->name[i]; + } + for (int i = 0; i < FAT32_MAX_EXTENSION; i++, idx++) + { + if (entry->extension[i] == ' ') + { + break; + } + else if (i == 0) + { + name_array[idx++] = '.'; + } + name_array[idx] = entry->extension[i]; + } + name_array[idx] = '\0'; +} +#endif diff --git a/lab8/kernel/include/vfs_initramfs.h b/lab8/kernel/include/vfs_initramfs.h new file mode 100644 index 000000000..d7b267e8c --- /dev/null +++ b/lab8/kernel/include/vfs_initramfs.h @@ -0,0 +1,41 @@ +#ifndef _INITRAMFS_H_ +#define _INITRAMFS_H_ + +#include "stddef.h" +#include "vfs.h" + +#define INITRAMFS_MAX_DIR_ENTRY 100 + +// initramfs basically is same as tmpfs, but it is read-only + +struct initramfs_inode +{ + enum fsnode_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_lseek64(struct file *file, long offset, int whence); +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 fsnode_type type); + +void initramfs_dump(struct vnode *dir_node , int level); +void initramfs_ls(struct vnode *dir_node); +int initramfs_op_deny(); +int initramfs_sync(struct filesystem *fs); + +#endif /* _INITRAMFS_H_ */ diff --git a/lab8/kernel/include/vfs_tmpfs.h b/lab8/kernel/include/vfs_tmpfs.h new file mode 100644 index 000000000..e0415d95d --- /dev/null +++ b/lab8/kernel/include/vfs_tmpfs.h @@ -0,0 +1,42 @@ +#ifndef _TMPFS_H_ +#define _TMPFS_H_ + +#include "stddef.h" +#include "vfs.h" + +// SPEC basic Note #3 +#define MAX_FILE_NAME 15 +#define MAX_DIR_ENTRY 16 +#define MAX_FILE_SIZE 4096 + +struct tmpfs_inode +{ + enum fsnode_type type; + char name[MAX_FILE_NAME]; + 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_lseek64(struct file *file, long offset, int whence); +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); + +void tmpfs_dump(struct vnode *dir_node , int level); +void tmpfs_ls(struct vnode *dir_node); +int tmpfs_op_deny(); +int tmpfs_sync(struct filesystem *fs); + +struct vnode *tmpfs_create_vnode(struct mount *_mount, enum fsnode_type type); + +#endif /* _TMPFS_H_ */ diff --git a/lab8/kernel/initramfs.cpio b/lab8/kernel/initramfs.cpio new file mode 100644 index 000000000..08fa91452 Binary files /dev/null and b/lab8/kernel/initramfs.cpio differ diff --git a/lab8/kernel/src/boot.S b/lab8/kernel/src/boot.S new file mode 100644 index 000000000..456dfeb7d --- /dev/null +++ b/lab8/kernel/src/boot.S @@ -0,0 +1,82 @@ +#include "mmu.h" + +.section ".text.boot" + +.global _kernel_start + +_kernel_start: + bl from_el2_to_el1 + +set_mmu_configuration: + // 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 + ldr x4, = MMU_PGD_ADDR // PGD's page frame at 0x1000 + ldr x1, = MMU_PUD_ADDR // 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] // [MMU_PGD_ADDR] = MMU_PUD_ADDR | BOOT_PGD_ATTR + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 // [MMU_PUD_ADDR] = 0x00000000 | BOOT_PUD_ATTR + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 // [MMU_PUD_ADDR + 8] = 0x40000000 | BOOT_PUD_ATTR + 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 // temp stack + bl set_2M_kernel_mmu + + mrs x2, sctlr_el1 // sctlr_el1: Provides top level control of the system, including its memory system, at EL1 and EL0. + orr x2 , x2, 1 // sctlr_el1[0]: EL1&0 stage 1 address translation enabled/disabled. + msr sctlr_el1, x2 + + // indirect branch to the upper virtual address + ldr x2, =set_exception_vector_table + br x2 + +set_exception_vector_table: + adr x1, exception_vector_table // vbar_el1: Vector Base Address Register (EL1) + msr vbar_el1, x1 // Holds the exception base address for any exception that is taken to EL1. + +setup_stack: + ldr x1, =_stack_top + mov sp, x1 + +setup_bss: + ldr x1, =_bss_start + ldr w2, =_bss_size + +init_bss: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, init_bss + +run_main: + ldr x1, =dtb_ptr + str x0, [x1], #8 + bl main + +proc_hang: + wfe + b proc_hang + +from_el2_to_el1: + 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 \ No newline at end of file diff --git a/lab8/kernel/src/cpio.c b/lab8/kernel/src/cpio.c new file mode 100644 index 000000000..56fdfc4e8 --- /dev/null +++ b/lab8/kernel/src/cpio.c @@ -0,0 +1,82 @@ +#include "string.h" +#include "cpio.h" +#include "stdio.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) + { + *next_header_pointer = 0; + puts("cpio parse error\r\n"); + return -1; + } + + // transfer big endian 8 byte hex string to unsigned 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); + // The file data is padded to a multiple of four bytes + offset = padding_4byte(offset); + *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 + padding_4byte(offset)); + } + + // if filepath is TRAILER!!! means there is no more files. + if (strncmp(*pathname, "TRAILER!!!", sizeof("TRAILER!!!")) == 0) + { + *next_header_pointer = 0; + } + + return 0; +} + +unsigned int padding_4byte(unsigned int size) +{ + return size + (4 - size % 4) % 4; +} \ No newline at end of file diff --git a/lab8/kernel/src/dtb.c b/lab8/kernel/src/dtb.c new file mode 100644 index 000000000..df97d64fc --- /dev/null +++ b/lab8/kernel/src/dtb.c @@ -0,0 +1,173 @@ +#include "dtb.h" +#include "uart1.h" +#include "cpio.h" +#include "string.h" +#include "stdio.h" +#include "memory.h" +#include "bcm2837/rpi_mmu.h" +#include "debug.h" + +void *CPIO_DEFAULT_START; +void *CPIO_DEFAULT_END; +char *dtb_ptr; + +// 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_big2little(uint32_t data) +{ + char *r = (char *)&data; + return (r[3] << 0) | (r[2] << 8) | (r[1] << 16) | (r[0] << 24); +} + +uint64_t uint64_endian_big2little(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_big2little(header->magic) != 0xD00DFEED) + { + ERROR("traverse_device_tree : wrong magic in traverse_device_tree\n"); + return; + } + // https://abcamus.github.io/2016/12/28/uboot%E8%AE%BE%E5%A4%87%E6%A0%91-%E8%A7%A3%E6%9E%90%E8%BF%87%E7%A8%8B/ + // https://blog.csdn.net/wangdapao12138/article/details/82934127 + uint32_t struct_size = uint32_endian_big2little(header->size_dt_struct); + char *dt_struct_ptr = (char *)((char *)header + uint32_endian_big2little(header->off_dt_struct)); + char *dt_strings_ptr = (char *)((char *)header + uint32_endian_big2little(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_big2little(*(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_big2little(*(uint32_t *)pointer); + pointer += 4; + char *name = (char *)dt_strings_ptr + uint32_endian_big2little(*(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 + { + puts("error type:"); + put_hex(token_type); + puts("\n"); + return; + } + } +} + +void dtb_callback_show_tree(uint32_t node_type, char *name, void *data, uint32_t name_size) +{ + static int level = 0; + if (node_type == FDT_BEGIN_NODE) + { + for (int i = 0; i < level; i++) + puts(" "); + puts(name); + puts("{\n"); + level++; + } + else if (node_type == FDT_END_NODE) + { + level--; + for (int i = 0; i < level; i++) + puts(" "); + puts("}\n"); + } + else if (node_type == FDT_PROP) + { + for (int i = 0; i < level; i++) + puts(" "); + puts(name); + puts("\n"); + } +} + +void dtb_callback_initramfs(uint32_t node_type, char *name, void *value, uint32_t name_size) +{ + // https://github.com/stweil/raspberrypi-documentation/blob/master/configuration/device-tree.md + // linux,initrd-start will be assigned by start.elf based on config.txt + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-start") == 0) + { + CPIO_DEFAULT_START = (void *)(unsigned long long)PHYS_TO_KERNEL_VIRT(uint32_endian_big2little(*(uint32_t *)value)); + } + if (node_type == FDT_PROP && strcmp(name, "linux,initrd-end") == 0) + { + CPIO_DEFAULT_END = (void *)(unsigned long long)PHYS_TO_KERNEL_VIRT(uint32_endian_big2little(*(uint32_t *)value)); + } +} + +void dtb_find_and_store_reserved_memory() +{ + struct fdt_header *header = (struct fdt_header *)dtb_ptr; + if (uint32_endian_big2little(header->magic) != 0xD00DFEED) + { + ERROR("traverse_device_tree : wrong magic in traverse_device_tree\n"); + return; + } + + // off_mem_rsvmap stores all of reserve memory map with address and size + char *dt_mem_rsvmap_ptr = (char *)((char *)header + uint32_endian_big2little(header->off_mem_rsvmap)); + struct fdt_reserve_entry *reverse_entry = (struct fdt_reserve_entry *)dt_mem_rsvmap_ptr; + + // reserve memory which is defined by dtb + while (reverse_entry->address != 0 || reverse_entry->size != 0) + { + unsigned long long start = PHYS_TO_KERNEL_VIRT(uint64_endian_big2little(reverse_entry->address)); + unsigned long long end = uint64_endian_big2little(reverse_entry->size) + start; + memory_reserve(start, end); + reverse_entry++; + } + + // reserve device tree itself + memory_reserve((unsigned long long)dtb_ptr, (unsigned long long)dtb_ptr + uint32_endian_big2little(header->totalsize)); +} \ No newline at end of file diff --git a/lab8/kernel/src/entry.S b/lab8/kernel/src/entry.S new file mode 100644 index 000000000..74e3bcb53 --- /dev/null +++ b/lab8/kernel/src/entry.S @@ -0,0 +1,199 @@ +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp ,16 * 0] // store pair of registers + 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] + // information 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] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + // information 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 instead of elr_el1 + add sp, sp, 32 * 9 +.endm + +.macro ventry label + .align 7 // entry should be aligned to 0x80 (2^7) + b \label +.endm + +.align 11 // vector table should be aligned to 0x800 (2^11) +.global exception_vector_table + +// exception_vector_table definition +// https://github.com/torvalds/linux/blob/v4.14/arch/arm64/kernel/entry.S#L367 +exception_vector_table: + //Exception from the current EL while using SP_EL0 + ventry el1t_sync_invalid // Synchronous EL1t + ventry el1t_irq_invalid // IRQ EL1t + ventry el1t_fiq_invalid // FIQ EL1t + ventry el1t_error_invalid // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry el1h_sync // Synchronous EL1h + ventry el1h_irq // IRQ EL1h + ventry el1h_fiq_invalid // FIQ EL1h + ventry el1h_error_invalid // Error EL1h + + //Exception from a lower EL and at least one lower EL is AArch64 + ventry el0_sync_64 // Synchronous 64-bit EL0 + ventry el0_irq_64 // IRQ 64-bit EL0 + ventry el0_fiq_invalid_64 // FIQ 64-bit EL0 + ventry el0_error_invalid_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry el0_sync_invalid_32 // Synchronous 32-bit EL0 + ventry el0_irq_invalid_32 // IRQ 32-bit EL0 + ventry el0_fiq_invalid_32 // FIQ 32-bit EL0 + ventry el0_error_invalid_32 // Error 32-bit EL0 + + +el1t_sync_invalid: + save_all + mov x0,0 + bl invalid_exception_router + load_all + eret +el1t_irq_invalid: + save_all + mov x0,1 + bl invalid_exception_router + load_all + eret +el1t_fiq_invalid: + save_all + mov x0,2 + bl invalid_exception_router + load_all + eret +el1t_error_invalid: + save_all + mov x0,3 + bl invalid_exception_router + load_all + eret + + +el1h_sync: + save_all + mov x0, sp + bl el1h_sync_router + bl el1_interrupt_disable + load_all + eret +el1h_irq: + save_all + mov x0, sp + bl el1h_irq_router + bl el1_interrupt_disable + load_all + eret +el1h_fiq_invalid: + save_all + mov x0,6 + bl invalid_exception_router + load_all + eret +el1h_error_invalid: + save_all + mov x0,7 + bl invalid_exception_router + load_all + eret + +el0_sync_64: + save_all + mov x0, sp + bl el0_sync_router + bl el1_interrupt_disable + load_all + eret +el0_irq_64: + save_all + mov x0, sp + bl el0_irq_router // ----------------------------------- + bl el1_interrupt_disable + load_all + eret +el0_fiq_invalid_64: + save_all + mov x0,10 + bl invalid_exception_router + load_all + eret +el0_error_invalid_64: + save_all + mov x0,11 + bl invalid_exception_router + load_all + eret + + +el0_sync_invalid_32: + save_all + mov x0,12 + bl invalid_exception_router + load_all + eret +el0_irq_invalid_32: + save_all + mov x0,13 + bl invalid_exception_router + load_all + eret +el0_fiq_invalid_32: + save_all + mov x0,14 + bl invalid_exception_router + load_all + eret +el0_error_invalid_32: + save_all + mov x0,15 + bl invalid_exception_router + load_all + eret diff --git a/lab8/kernel/src/exception.c b/lab8/kernel/src/exception.c new file mode 100644 index 000000000..1b6a72bce --- /dev/null +++ b/lab8/kernel/src/exception.c @@ -0,0 +1,377 @@ +#include "bcm2837/rpi_irq.h" +#include "bcm2837/rpi_uart1.h" +#include "uart1.h" +#include "exception.h" +#include "timer.h" +#include "syscall.h" +#include "signal.h" +#include "sched.h" +#include "memory.h" +#include "irqtask.h" +#include "mmu.h" +#include "debug.h" + +// 讀取ESR_EL1暫存器的值 +static inline unsigned long read_esr_el1(void) +{ + unsigned long value; + asm volatile("mrs %0, esr_el1" : "=r"(value)); + return value; +} + +// 判斷異常是否由EL0觸發的syscall +static inline int is_el0_syscall() +{ + unsigned long esr_el1 = read_esr_el1(); + unsigned long ec = (esr_el1 >> ESR_EL1_EC_SHIFT) & ESR_EL1_EC_MASK; + if (ec == ESR_EL1_EC_SVC64) + { + return 1; + } + return 0; +} + +// 定義異常類型名稱 +const char *exception_type[] = { + "Unknown reason", + "Trapped WFI or WFE instruction execution", + "Trapped MCR or MRC access with (coproc==0b1111) (AArch32)", + "Trapped MCRR or MRRC access with (coproc==0b1111) (AArch32)", + "Trapped MCR or MRC access with (coproc==0b1110) (AArch32)", + "Trapped LDC or STC access (AArch32)", + "Trapped FP access", + "Trapped VMRS access", + "Trapped PSTATE (AArch32)", + "Instruction Abort from a lower Exception level", + "Instruction Abort taken without a change in Exception level", + "PC alignment fault", + "Data Abort from a lower Exception level", + "Data Abort taken without a change in Exception level", + "SP alignment fault", + "Trapped floating-point exception", + "SError interrupt", + "Breakpoint from a lower Exception level", + "Breakpoint taken without a change in Exception level", + "Software Step from a lower Exception level", + "Software Step taken without a change in Exception level", + "Watchpoint from a lower Exception level", + "Watchpoint taken without a change in Exception level", + "BKPT instruction execution (AArch32)", + "Vector Catch exception (AArch32)", + "BRK instruction execution (AArch64)"}; + +extern list_head_t *run_queue; + +// DAIF, Interrupt Mask Bits +void el1_interrupt_enable() +{ + __asm__ __volatile__("msr daifclr, 0xf"); // umask all DAIF +} + +void el1_interrupt_disable() +{ + __asm__ __volatile__("msr daifset, 0xf"); // mask all DAIF +} + +unsigned long long int lock_counter = 0; + +void lock() +{ + el1_interrupt_disable(); + lock_counter++; +} + +void unlock() +{ + // uart_sendlinek("This is unlock\n"); + lock_counter--; + if (lock_counter < 0) + { + while (1) + ; + } + else if (lock_counter == 0) + { + el1_interrupt_enable(); + } +} + +void el1h_sync_router(trapframe_t *tpf) +{ + uart_sendlinek("\n"); + uart_sendlinek("spsr_el1 : %x\n ", tpf->spsr_el1); + uart_sendlinek("elr_el1 : %x\n ", tpf->elr_el1); + uart_sendlinek("sp_el0 : %x\n ", tpf->sp_el0); + // dump_vma(); + while (1) + ; +} + +void el1h_irq_router(trapframe_t *tpf) +{ + lock(); + // decouple the handler into irqtask queue + // (1) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.113 + // (2) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.16 + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + if (*AUX_MU_IER_REG & 2) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); // run the queued task before returning to the program. + } + else if (*AUX_MU_IER_REG & 1) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) // from CNTPNS (core_timer) // A1 - setTimeout run in el1 + { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + core_timer_enable(); + + if (list_size(run_queue) > 1) + { + // uart_sendlinek("el1h_irq_router\n"); + schedule(); + } + } + else + { + unlock(); + uart_sendlinek("Hello World el1 64 router other interrupt!\r\n"); + } + + // only do signal handler when return to user mode + if ((tpf->spsr_el1 & 0b1100) == 0) + { + check_signal(tpf); + } +} + +void el0_sync_router(trapframe_t *tpf) +{ + static int count = 0; + unsigned long long esr_el1 = read_esr_el1(); + + // esr_el1: Holds syndrome information for an exception taken to EL1. + esr_el1_t *esr = (esr_el1_t *)&esr_el1; + if (esr->ec == MEMFAIL_DATA_ABORT_LOWER || esr->ec == MEMFAIL_INST_ABORT_LOWER) + { + mmu_memfail_abort_handle(esr); + return; + } + + if (!is_el0_syscall()) + { + const char *exception_name = get_exception_name(esr_el1); + if (count == 0) + ERROR("el0_sync_router: exception occurred - %s\r\n", exception_name); + count++; + return; + } + + el1_interrupt_enable(); // Allow UART input during exception + unsigned long long syscall_no = tpf->x8; + if (syscall_no > 18) + { + uart_sendlinek("SYSCALL No: %d\n", syscall_no); + } + // uart_sendlinek("tpf->x8 : %d\n",tpf->x8); + switch (syscall_no) + { + SYSCALL(0, getpid(tpf)) + SYSCALL(1, uartread(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(2, uartwrite(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(3, exec(tpf, (char *)tpf->x0, (char **)tpf->x1)) + SYSCALL(4, fork(tpf)) + SYSCALL(5, exit(tpf, tpf->x0)) + SYSCALL(6, syscall_mbox_call(tpf, (unsigned char)tpf->x0, (unsigned int *)tpf->x1)) + SYSCALL(7, kill(tpf, (int)tpf->x0)) + SYSCALL(8, signal_register(tpf->x0, (void (*)())tpf->x1)) + SYSCALL(9, signal_kill(tpf->x0, tpf->x1)) + SYSCALL(10, mmap(tpf, (void *)tpf->x0, tpf->x1, tpf->x2, tpf->x3, tpf->x4, tpf->x5)) + SYSCALL(11, open(tpf, (char *)tpf->x0, tpf->x1);) + SYSCALL(12, close(tpf, tpf->x0)) + SYSCALL(13, write(tpf, tpf->x0, (char *)tpf->x1, tpf->x2)) + SYSCALL(14, read(tpf, tpf->x0, (char *)tpf->x1, tpf->x2)) + SYSCALL(15, mkdir(tpf, (char *)tpf->x0, tpf->x1)) + SYSCALL(16, mount(tpf, (char *)tpf->x0, (char *)tpf->x1, (char *)tpf->x2, tpf->x3, (void *)tpf->x4)) + SYSCALL(17, chdir(tpf, (char *)tpf->x0)) + SYSCALL(18, lseek64(tpf, tpf->x0, tpf->x1, tpf->x2)) + SYSCALL(19, ioctl(tpf, tpf->x0, tpf->x1, (void *)tpf->x2)) + SYSCALL(20, sync(tpf)) + SYSCALL(50, sigreturn(tpf)) + SYSCALL(114, syscall_lock(tpf)) + SYSCALL(514, syscall_unlock(tpf)) + default: + uart_sendlinek("el0_sync_router other syscall, syscall_no: %d\r\n", syscall_no); + break; + } +} + +void el0_irq_router(trapframe_t *tpf) +{ + lock(); + // decouple the handler into irqtask queue + // (1) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.113 + // (2) https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf - Pg.16 + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) // from aux && from GPU0 -> uart exception + { + if (*AUX_MU_IER_REG & 2) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); // run the queued task before returning to the program. + } + else if (*AUX_MU_IER_REG & 1) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) // from CNTPNS (core_timer) // A1 - setTimeout run in el1 + { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + unlock(); + irqtask_run_preemptive(); + core_timer_enable(); + + if (list_size(run_queue) > 1) + { + // uart_sendlinek("el0_irq_router\n"); + schedule(); + } + } + else + { + unlock(); + uart_sendlinek("Hello World el0 64 router other interrupt!\r\n"); + } + + // 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) +{ + uart_sendlinek("\n invalid exception %x \n", x0); + while (1) + ; +} + +// 獲取異常類型名稱 +const char *get_exception_name(unsigned long esr_el1) +{ + unsigned long ec = (esr_el1 >> ESR_EL1_EC_SHIFT) & ESR_EL1_EC_MASK; + if (ec < sizeof(exception_type) / sizeof(exception_type[0])) + { + return exception_type[ec]; + } + return "Unknown exception"; +} + +// ------------------------------------------------------------------------------------------ + +/* +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; // Small number has higher priority + +struct list_head *task_list; +void irqtask_list_init() +{ + task_list = allocator(sizeof(list_head_t)); + INIT_LIST_HEAD(task_list); +} + +void irqtask_add(void *task_function, unsigned long long priority) +{ + irqtask_t *the_task = kmalloc(sizeof(irqtask_t)); // free by irq_tasl_run_preemptive() + + // store all the related information into irqtask node + // manually copy the device's buffer + the_task->priority = priority; + the_task->task_function = task_function; + INIT_LIST_HEAD(&(the_task->listhead)); + + // add the timer_event into timer_event_list (sorted) + // if the priorities are the same -> FIFO + struct list_head *curr; + + // mask the device's interrupt line + // el1_interrupt_disable(); + // enqueue the processing task to the event queue with sorting. + list_for_each(curr, task_list) + { + if (((irqtask_t *)curr)->priority > the_task->priority) + { + list_add(&(the_task->listhead), curr->prev); + break; + } + } + // if the priority is lowest + if (list_is_head(curr, task_list)) + { + list_add_tail(&(the_task->listhead), task_list); + } + // unmask the interrupt line + // el1_interrupt_enable(); +} + +void irqtask_run_preemptive() +{ + // el1_interrupt_enable(); + while (!list_empty(task_list)) + { + // critical section protects new coming node + + lock(); + irqtask_t *the_task = (irqtask_t *)task_list->next; + // Run new task (early return) if its priority is lower than the scheduled task. + if (curr_task_priority <= the_task->priority) + { + unlock(); + break; + } + // get the scheduled task and run it. + list_del_entry((struct list_head *)the_task); + int prev_task_priority = curr_task_priority; + curr_task_priority = the_task->priority; + // uart_sendlinek("preemptive curr_task_priority: %d\r\n", curr_task_priority); + unlock(); + + irqtask_run(the_task); + + lock(); + curr_task_priority = prev_task_priority; + unlock(); + // free(the_task); + } +} + +void irqtask_run(irqtask_t *the_task) +{ + ((void (*)())the_task->task_function)(); + kfree(the_task); +} diff --git a/lab8/kernel/src/linker.ld b/lab8/kernel/src/linker.ld new file mode 100644 index 000000000..45b64f607 --- /dev/null +++ b/lab8/kernel/src/linker.ld @@ -0,0 +1,44 @@ +_heap_stack_size = 50M; + +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + _kernel_start = .; + + .text : { + *(.text.boot) + *(.text) + } + + .rodata : { + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + . = ALIGN(8); + _bss_start = .; + *(.bss) + *(COMMON) + } + _bss_end = .; + _bss_size = (_bss_end - _bss_start)>>3; + + .heap : { + . = ALIGN(8); + _heap_top = .; + . += _heap_stack_size; + _heap_end = .; + } + + _kernel_end = .; + + . = 0xffff00002c000000; + _stack_end = .; + . = 0xffff00003c000000; + _stack_top = .; +} \ No newline at end of file diff --git a/lab8/kernel/src/main.c b/lab8/kernel/src/main.c new file mode 100644 index 000000000..582faa2ff --- /dev/null +++ b/lab8/kernel/src/main.c @@ -0,0 +1,45 @@ +#include "uart1.h" +#include "shell.h" +#include "memory.h" +#include "dtb.h" +#include "exception.h" +#include "timer.h" +#include "sched.h" +#include "uart1.h" +#include "irqtask.h" +#include "vfs.h" +//#include "bcm2837/rpi_mmu.h" + +extern list_head_t *run_queue; +extern char *dtb_ptr; +int Set_dtb = 0; + +void main(char *arg) +{ + // uart_sendlinek("in main\n"); + dtb_ptr = (char *)PHYS_TO_KERNEL_VIRT(arg); + if (!Set_dtb) + { + traverse_device_tree(dtb_ptr, dtb_callback_initramfs); + Set_dtb = 1; + } + uart_init(); + allocator_init(); + rootfs_init(); + irqtask_list_init(); + timer_list_init(); + thread_sched_init(); + //uart_sendlinek("in main\n"); + + core_timer_enable(); + uart_interrupt_enable(); + uart_flush_FIFO(); + + el1_interrupt_enable(); // enable interrupt in EL1 -> EL1 + + //start_shell(); + // uart_sendlinek("RQ size : %d \n",list_size(run_queue)); + + //schedule(); + load_context(&((((thread_t*)run_queue->next))->context)); +} diff --git a/lab8/kernel/src/mbox.c b/lab8/kernel/src/mbox.c new file mode 100644 index 000000000..530412d28 --- /dev/null +++ b/lab8/kernel/src/mbox.c @@ -0,0 +1,23 @@ +#include "bcm2837/rpi_mbox.h" +#include "mbox.h" + +/* Aligned to 16-byte boundary while we have 28-bits for VC */ +volatile unsigned int __attribute__((aligned(16))) pt[36]; + +int mbox_call( mbox_channel_type channel, unsigned int value ) +{ + // Add channel to lower 4 bit + value &= ~(0xF); + value |= channel; + while ( (*MBOX_STATUS & BCM_ARM_VC_MS_FULL) != 0 ) {} + // Write to Register + *MBOX_WRITE = value; + while(1) { + while ( *MBOX_STATUS & BCM_ARM_VC_MS_EMPTY ) {} + // Read from Register + if (value == *MBOX_READ) + return pt[1] == MBOX_REQUEST_SUCCEED; + } + return 0; +} + diff --git a/lab8/kernel/src/memory.c b/lab8/kernel/src/memory.c new file mode 100644 index 000000000..a80a1219a --- /dev/null +++ b/lab8/kernel/src/memory.c @@ -0,0 +1,491 @@ +#include "memory.h" +#include "u_list.h" +#include "uart1.h" +#include "exception.h" +#include "dtb.h" +#include "stdio.h" +#include "mmu.h" +#include "bcm2837/rpi_mmu.h" + +extern char _heap_top; +static char *htop_ptr = &_heap_top; + +extern char _kernel_start; +extern char _kernel_end; +extern char *CPIO_DEFAULT_START; +extern char *CPIO_DEFAULT_END; +extern char _stack_end; +extern char _stack_top; + +void *allocator(unsigned int size) +{ + lock(); + // -> htop_ptr + // htop_ptr + 0x02: heap_block size + // htop_ptr + 0x10 ~ htop_ptr + 0x10 * k: + // { heap_block } + // -> htop_ptr + // header 0x10 bytes block + // |--------------------------------------------------------------| + // | fill zero 0x8 bytes | size 0x8 bytes | size padding to 0x16 | + // |--------------------------------------------------------------| + + // 0x10 for heap_block header + char *r = htop_ptr + 0x10; + // size paddling to multiple of 0x10 + size = 0x10 + size - size % 0x10; + *(unsigned int *)(r - 0x8) = size; + htop_ptr += size; + unlock(); + return r; +} + +// void free(void* ptr) { +// // TBD +// } + +// ------------------------------------------------------------ + +static frame_t *frame_array; // store memory's statement and page's corresponding index +static list_head_t frame_freelist[FRAME_MAX_IDX]; // store available block for page +static list_head_t cache_list[CACHE_MAX_IDX]; // store available block for cache + +void allocator_init() +{ + frame_array = allocator(BUDDY_MEMORY_PAGE_COUNT * sizeof(frame_t)); + + // init frame freelist + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&frame_freelist[i]); + } + + // init cache list + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&cache_list[i]); + } + + for (int i = 0; i < BUDDY_MEMORY_PAGE_COUNT; i++) + { + // init listhead for each frame + INIT_LIST_HEAD(&(frame_array[i].listhead)); + frame_array[i].idx = i; + } + freelist_init(); + dump_page_info(); + + /* Startup reserving the following region: + Spin tables for multicore boot (0x0000 - 0x1000) + Devicetree (Optional, if you have implement it) + Kernel image in the physical memory + Your simple allocator (startup allocator) (Stack + Heap in my case) + Initramfs + */ + uart_sendlinek("\r\n* Startup Allocation *\r\n"); + uart_sendlinek("buddy system: usable memory region: 0x%x ~ 0x%x\n", BUDDY_MEMORY_BASE, BUDDY_MEMORY_BASE + BUDDY_MEMORY_PAGE_COUNT * PAGESIZE); + dtb_find_and_store_reserved_memory(); // find spin tables in dtb + memory_reserve(PHYS_TO_KERNEL_VIRT(MMU_PGD_ADDR), PHYS_TO_KERNEL_VIRT(MMU_PTE_ADDR + 0x2000)); // // PGD's page frame at 0x1000 // PUD's page frame at 0x2000 PMD 0x3000-0x5000 + memory_reserve((unsigned long long)&_kernel_start, (unsigned long long)&_kernel_end); // kernel + memory_reserve((unsigned long long)&_stack_end, (unsigned long long)&_stack_top); + memory_reserve((unsigned long long)CPIO_DEFAULT_START, (unsigned long long)CPIO_DEFAULT_END); +} + +void freelist_init() +{ + int PAGE_COUNT = BUDDY_MEMORY_PAGE_COUNT; + for (int i = 0; i <= FRAME_IDX_FINAL; i++) + { + // uart_sendlinek("\n %ld,%ld \n",PAGE_COUNT,(1 << i)); + if ((PAGE_COUNT & (1 << i))) + { + PAGE_COUNT -= (1 << i); + frame_array[PAGE_COUNT].val = i; + frame_array[PAGE_COUNT].used = FRAME_FREE; + list_add(&(frame_array[PAGE_COUNT].listhead), &frame_freelist[i]); + } + if (i == FRAME_IDX_FINAL && PAGE_COUNT) + { + while (PAGE_COUNT) + { + PAGE_COUNT -= (1 << i); + frame_array[PAGE_COUNT].val = FRAME_IDX_FINAL; + frame_array[PAGE_COUNT].used = FRAME_FREE; + list_add(&(frame_array[PAGE_COUNT].listhead), &frame_freelist[FRAME_IDX_FINAL]); + } + } + } +} + +frame_t *release_redundant(frame_t *frame) +{ + // order -1 -> add its buddy to free list (frame itself will be used in master function) + frame->val -= 1; + frame_t *buddyptr = get_buddy(frame); + buddyptr->val = frame->val; + buddyptr->used = FRAME_FREE; + list_add(&(buddyptr->listhead), &frame_freelist[buddyptr->val]); + return frame; +} + +frame_t *get_buddy(frame_t *frame) +{ + // XOR(idx, order) + if ((frame->idx ^ (1 << frame->val)) > BUDDY_MEMORY_PAGE_COUNT) + { + uart_sendlinek("[!] BUDDY of Page: 0x%x at level: %d Does not exit", frame->idx, frame->val); + // return -1; + } + return &frame_array[frame->idx ^ (1 << frame->val)]; +} + +void dump_page_info() +{ + // unsigned int exp2 = 1; + // uart_sendlinek(" ┌───────────────────── [ Number of Available Page Blocks ] ─────────────────────┐\r\n │ "); + // for (int i = FRAME_IDX_0; i < FRAME_IDX_8; i++) + // { + // uart_sendlinek("%4dKB(%1d) ", 4 * exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │ "); + // for (int i = FRAME_IDX_0; i < FRAME_IDX_8; i++) + // uart_sendlinek(" %4d ", list_size(&frame_freelist[i])); + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└────────────────────────────────────────"); + // uart_sendlinek("─────────────────────────────────────────┘\r\n"); + + // exp2 = 1; + // uart_sendlinek(" ┌──────────────────────────────────── "); + // uart_sendlinek("[ Number of Available Page Blocks ]"); + // uart_sendlinek(" ───────────────────────────────────┐\r\n │"); + // for (int i = FRAME_IDX_8; i < FRAME_MAX_IDX; i++) + // { + // uart_sendlinek("%4dMB(%2d) ", exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │"); + // for (int i = FRAME_IDX_8; i < FRAME_MAX_IDX; i++) + // uart_sendlinek(" %4d ", list_size(&frame_freelist[i])); + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└──────────────────────────────────────────────────────"); + // uart_sendlinek("────────────────────────────────────────────────────────┘\r\n"); +} + +void dump_cache_info() +{ + // unsigned int exp2 = 1; + // uart_sendlinek(" ┌──────────────── [ Number of Available Cache Blocks ] ────────────────┐\r\n │ "); + // for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + // { + // uart_sendlinek("%4dB(%1d) ", CACHE_SEG * exp2, i); + // exp2 *= 2; + // } + // uart_sendlinek("│\r\n │ "); + // ; + // for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + // { + // if (!list_empty(&cache_list[i])) + // { + // // uart_sendlinek("I free !!!!!!!!!!!!"); + // int num = 0; + // list_head_t *pos; + // list_for_each(pos, &cache_list[i]) + // { + // num += ((cache_t *)pos)->available; + // } + // uart_sendlinek("%5d ", num); + // } + // else + // { + // uart_sendlinek("%5d ", 0); + // } + // } + + // uart_sendlinek("│\r\n "); + // uart_sendlinek("└───────────────────────────────────"); + // uart_sendlinek("────────────────────────────────────┘\r\n"); +} + +void memory_reserve(unsigned long long start, unsigned long long end) +{ + start -= start % PAGESIZE; // floor (align 0x1000) + end = end % PAGESIZE ? end + PAGESIZE - (end % PAGESIZE) : end; // ceiling (align 0x1000) + + // uart_sendlinek("Reserved Memory: "); + // uart_sendlinek("start 0x%x ~ ", start); + // uart_sendlinek("end 0x%x\r\n", end); + + // delete page from free list + for (int order = FRAME_IDX_FINAL; order >= 0; order--) + { + list_head_t *pos; + // uart_sendlinek("\n use %d level page to match \n",order); + list_for_each(pos, &frame_freelist[order]) + { + unsigned long long pagestart = ((frame_t *)pos)->idx * PAGESIZE + BUDDY_MEMORY_BASE; + unsigned long long pageend = pagestart + (PAGESIZE << order); + // uart_sendlinek("\n from 0x%x to 0x%x\n",pagestart,pageend); + + if (start <= pagestart && end >= pageend) // if page all in reserved memory -> delete it from freelist + { + ((frame_t *)pos)->used = FRAME_ALLOCATED; + // uart_sendlinek(" [!] Reserved page in 0x%x - 0x%x\n", pagestart, pageend); + // uart_sendlinek(" Before\n"); + // dump_page_info(); + list_del_entry(pos); + // uart_sendlinek(" Remove usable block for reserved memory: order %d\r\n", order); + // uart_sendlinek(" After\n"); + // dump_page_info(); + } + else if (start >= pageend || end <= pagestart) // no intersection + { + continue; + } + else // partial intersection, separate the page into smaller size. + { + // dump_page_info(); + list_del_entry(pos); + list_head_t *temppos = pos->prev; + list_add(&release_redundant((frame_t *)pos)->listhead, &frame_freelist[order - 1]); + pos = temppos; + // dump_page_info(); + } + } + } +} + +void *kmalloc(unsigned int size) +{ + lock(); + + // uart_sendlinek("\n\n"); + // uart_sendlinek("================================\r\n"); + // uart_sendlinek("[+] Request kmalloc size: %d\r\n", size); + // uart_sendlinek("================================\r\n"); + // if size is larger than cache size, go for page + if (size > (CACHE_SEG << CACHE_IDX_FINAL)) + { + void *r = page_malloc(size); + unlock(); + return r; + } + // go for cache + void *r = cache_malloc(size); + unlock(); + return r; +}; + +void *page_malloc(unsigned int size) +{ + // uart_sendlinek("this is page_malloc \r\n"); + int val; + int find_PageSize = 0; + frame_t *target_frame_ptr; + // turn size into minimum 4KB * 2**val + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + + if (size <= (PAGESIZE << i) && !find_PageSize) + { + val = i; + // uart_sendlinek(" block size = 0x%x\n", PAGESIZE << i); + find_PageSize = 1; + } + + if (find_PageSize && !list_empty(&frame_freelist[i])) + { + // uart_sendlinek(" free page at level : %d\n", i); + target_frame_ptr = (frame_t *)(frame_freelist[i].next); + for (int min_FreePageLevel = i; min_FreePageLevel > val; min_FreePageLevel--) // ex: 10000 -> 01111 + { + target_frame_ptr = release_redundant(target_frame_ptr); + } + break; + } + + if (i == FRAME_IDX_FINAL) + { + // uart_sendlinek("[!] request size exceeded for page_malloc!!!!\r\n"); + return (void *)0; + } + } + // get the available frame from freelist + target_frame_ptr->used = FRAME_ALLOCATED; + + // uart_sendlinek(" [+] Allocate page - size : %d(0x%x)\r\n", size, size); + // uart_sendlinek(" Before\r\n"); + // dump_page_info(); + list_del_entry((struct list_head *)target_frame_ptr); + // uart_sendlinek(" physical address : 0x%x\n", BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx))); + // uart_sendlinek(" After\r\n"); + // dump_page_info(); + + return (void *)BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx)); +}; + +void *cache_malloc(unsigned int size) +{ + int c_val; + // uart_sendlinek("this is cache_malloc \r\n"); + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + if (size <= (CACHE_SEG << i)) + { + c_val = i; + break; + } + } + + if (list_empty(&cache_list[c_val])) + { + // uart_sendlinek("[!] No free size for cache \r\n"); //--------------------------------------------------- + page2caches(c_val); + } + + // cache_t *ptr = (cache_t *)(cache_list[c_val].prev); + + // uart_sendlinek("this is cache_malloc \r\n"); + // uart_sendlinek("[+] Allocate cache - size : %d(0x%x)\r\n", size, size); + // uart_sendlinek(" Before\r\n"); + // dump_cache_info(); + + void *ptr = find_CACHE((cache_t *)cache_list[c_val].next); + // uart_sendlinek("ptr : %x\n",ptr); + // uart_sendlinek(" After\r\n"); + // dump_cache_info(); + + return ptr; +}; + +void *find_CACHE(cache_t *ptr) +{ + // uart_sendlinek("ptr : %x\n",ptr); + unsigned long long num; + int record_num; + for (record_num = 0; ptr->cache_record[record_num] + 1 == 0 && record_num < CACHE_record_num; record_num++) + ; + + num = ptr->cache_record[record_num] + 1; + num = ((~num) & (ptr->cache_record[record_num])) + 1; + + // uart_sendlinek("before cache_record[%d] : %x\n", record_num, ptr->cache_record[record_num]); + ptr->cache_record[record_num] += num; + ptr->available--; + // uart_sendlinek("after cache_record[%d] : %x\n", record_num, ptr->cache_record[record_num]); + + int val = fake_log2(num) + record_num * 64; + void *target = ptr->data_base + val * (CACHE_SEG << (ptr->cache_order)); + // uart_sendlinek("data_base addr : 0x%x\n", ptr->data_base); + // uart_sendlinek("target addr : 0x%x\n", target); + + if (!(ptr->available)) + { + list_del_entry((list_head_t *)ptr); + } + + return target; +} + +void page2caches(int c_val) +{ + // uart_sendlinek("[!] Split Page for cache\r\n"); + void *ptr = page_malloc(PAGESIZE); + // uart_sendlinek("ptr : %x\n",ptr); + cache_t *Pageinfo = (cache_t *)ptr; + Pageinfo->data_base = ptr; + Pageinfo->cache_order = c_val; + Pageinfo->max_available = (PAGESIZE >> (CACHE_offset + c_val)); + + while (sizeof(*Pageinfo) >= Pageinfo->data_base - ptr) + { + Pageinfo->data_base += (CACHE_SEG << c_val); + (Pageinfo->max_available)--; + } + Pageinfo->available = Pageinfo->max_available; + + for (int i = 0; i < CACHE_record_num; i++) + Pageinfo->cache_record[i] = 0; + + // uart_sendlinek(" max_available : %d\n", Pageinfo->max_available); + // uart_sendlinek(" Pageinfo size : 0x%x\n", sizeof(*Pageinfo)); + // uart_sendlinek(" offset size : 0x%x\n", (CACHE_SEG << c_val)); + + list_add(&(Pageinfo->listhead), &cache_list[c_val]); + // return Pageinfo; +} + +//------------------------------------------------------------------------------------------------------------------------------- kfree + +void kfree(void *ptr) +{ + // uart_sendlinek("\r\n"); + // uart_sendlinek("==========================\r\n"); + //uart_sendlinek("[+] Request kfree 0x%x\r\n", ptr); + // uart_sendlinek("==========================\r\n"); + + // If no cache assigned, go for page + lock(); + frame_t *target_frame_ptr = &frame_array[((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12]; + if ((unsigned long long)ptr % PAGESIZE == 0) + { + page_free(target_frame_ptr); + unlock(); + return; + } + // go for cache + cache_free(ptr); + unlock(); +}; + +void page_free(frame_t *target_frame_ptr) +{ + frame_t *buddyptr = get_buddy(target_frame_ptr); + target_frame_ptr->used = FRAME_FREE; + while (buddyptr->used == FRAME_FREE && buddyptr->val == target_frame_ptr->val) + { + list_del_entry((list_head_t *)buddyptr); + target_frame_ptr = &frame_array[(target_frame_ptr->idx) & (buddyptr->idx)]; + target_frame_ptr->val++; + buddyptr = get_buddy(target_frame_ptr); + if (buddyptr < 0) + break; + } + + // uart_sendlinek(" Before\r\n"); + // dump_page_info(); + list_add(&(target_frame_ptr->listhead), &frame_freelist[target_frame_ptr->val]); + // uart_sendlinek(" After\r\n"); + // dump_page_info(); +} + +void cache_free(void *ptr) +{ + // uart_sendlinek("ptr : %x\n",ptr); + int idx = ((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12; + frame_t *target_frame_ptr = &frame_array[idx]; + cache_t *cache_ptr = (cache_t *)((unsigned long long)(idx * PAGESIZE) + BUDDY_MEMORY_BASE); //<------- + // uart_sendlinek("target_frame_ptr : %x\n",target_frame_ptr); + //uart_sendlinek("cache_ptr : %x\n",cache_ptr); + int num = (ptr - cache_ptr->data_base) / (CACHE_SEG << cache_ptr->cache_order); + //uart_sendlinek("num : %d\n",num); + int record_num = (num >> 6); + num = num - (record_num << 6); + cache_ptr->cache_record[record_num] -= (1 << num); + + // uart_sendlinek("[+] Free cache: 0x%x, val = %d\r\n", ptr, cache_ptr->cache_order); + // uart_sendlinek(" Before\r\n"); + // dump_cache_info(); + if (cache_ptr->available == 0) + { + list_add(&(cache_ptr->listhead), &cache_list[cache_ptr->cache_order]); + } + cache_ptr->available++; + if (cache_ptr->available == cache_ptr->max_available) + { + list_del_entry((list_head_t *)cache_ptr); + page_free(target_frame_ptr); + } + // uart_sendlinek(" After\r\n"); + // dump_cache_info(); +} \ No newline at end of file diff --git a/lab8/kernel/src/mmu.c b/lab8/kernel/src/mmu.c new file mode 100644 index 000000000..95e3bde7f --- /dev/null +++ b/lab8/kernel/src/mmu.c @@ -0,0 +1,380 @@ +#include "bcm2837/rpi_mmu.h" +#include "mmu.h" +#include "memory.h" +#include "string.h" +#include "uart1.h" +#include "debug.h" + +extern thread_t *curr_thread; + +void *set_2M_kernel_mmu(void *x0) +{ + // Turn + // Two-level Translation (1GB) - in boot.S + // to + // Three-level Translation (2MB) - set PUD point to new table + unsigned long *pud_table = (unsigned long *)MMU_PUD_ADDR; + + unsigned long *pte_table1 = (unsigned long *)MMU_PTE_ADDR; + unsigned long *pte_table2 = (unsigned long *)(MMU_PTE_ADDR + 0x1000L); + for (int i = 0; i < 512; i++) + { + unsigned long addr = 0x200000L * i; + if (addr >= PERIPHERAL_END) + { + pte_table1[i] = (0x00000000 + addr) + BOOT_PTE_ATTR_nGnRnE; + continue; + } + pte_table1[i] = (0x00000000 + addr) | BOOT_PTE_ATTR_NOCACHE; // 0 * 2MB // No definition for 3-level attribute, use nocache. + pte_table2[i] = (0x40000000 + addr) | BOOT_PTE_ATTR_NOCACHE; // 512 * 2MB + } + + // set PUD + pud_table[0] = (unsigned long)pte_table1 | BOOT_PUD_ATTR; + pud_table[1] = (unsigned long)pte_table2 | BOOT_PUD_ATTR; + + return x0; +} + +void map_one_page(size_t *virt_pgd_p, size_t user_va, size_t pa, size_t flag) +{ + size_t *table_p = virt_pgd_p; + for (int level = 0; level < 4; level++) + { + unsigned int idx = (user_va >> (39 - level * 9)) & 0x1ff; // p.14, 9-bit only + + if (level == 3) + { + table_p[idx] = pa; + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_KNX | flag; // el0 only + return; + } + + if (!table_p[idx]) + { + size_t *newtable_p = kmalloc(0x1000); // create a table + memset(newtable_p, 0, 0x1000); + table_p[idx] = KERNEL_VIRT_TO_PHYS((size_t)newtable_p); // point to that table + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + table_p = (size_t *)PHYS_TO_KERNEL_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); // PAGE_SIZE + } +} + +void mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced, vma_name_type name) +{ + if (IS_NOT_ALIGN(pa, PAGESIZE) || IS_NOT_ALIGN(va, PAGESIZE)) + { + // ERROR("CHECK_ALIGN : 0x%x\n",CHECK_ALIGN(pa,PAGESIZE)); + uart_sendlinek("\n\n"); + ERROR("Input User Vitural Address or Physical Address Should be Aliged to PAGESIZE\n"); + ERROR("Input User Vitural Address : 0x%x\n", va); + ERROR("Input Physical Address : 0x%x\n", pa); + return; + } + vm_area_struct_t *the_area_ptr = check_vma_overlap(t, va, (unsigned long)size); + if (the_area_ptr != 0) + { + uart_sendlinek("\n\n"); + ERROR("check_vma_overlap : 0x%x\n", the_area_ptr); + ERROR("Vitural Memory Area is Overlap !!\n"); + // dump_vma(); + return; + } + + size = ALIGN_UP(size, PAGESIZE); + 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; + new_area->name = name; + list_add_tail((list_head_t *)new_area, &t->vma_list); +} + +void mmu_del_vma(struct thread *t) +{ + list_head_t *curr = &t->vma_list; + list_head_t *n; + list_for_each_safe(curr, n, &t->vma_list) + { + vm_area_struct_t *vma = (vm_area_struct_t *)curr; + if (vma->is_alloced) + { + kfree((void *)PHYS_TO_KERNEL_VIRT(vma->phys_addr)); + } + list_del_entry(curr); + kfree(curr); + } +} + +void mmu_free_page_tables(size_t *page_table, int level) +{ + size_t *table_virt = (size_t *)PHYS_TO_KERNEL_VIRT(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) == PD_TABLE) + { + if (level < PMD) + mmu_free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + kfree((void *)PHYS_TO_KERNEL_VIRT(next_table)); + } + } + } +} + +// void mmu_set_PTE_readonly(size_t *page_table, int level) +// { +// size_t *table_virt = (size_t *)PHYS_TO_KERNEL_VIRT(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 == PD_TABLE) +// { +// if (level < PMD) +// { +// mmu_set_PTE_readonly(next_table, level + 1); +// } +// else +// { +// table_virt[i] &= PD_RDONLY; +// } +// } +// } +// } +// } + +// void mmu_pagetable_copy(size_t *dst_page_table, size_t *src_page_table, int level) +// { +// size_t *dst_page_table_va = (size_t *)PHYS_TO_KERNEL_VIRT(dst_page_table); +// size_t *src_page_table_va = (size_t *)PHYS_TO_KERNEL_VIRT(src_page_table); +// for (int i = 0; i < 512; i++) +// { +// if (src_page_table_va[i] != 0) +// { +// size_t *next_src_table = (size_t)(src_page_table_va[i] & ENTRY_ADDR_MASK); +// size_t *next_dst_table = kmalloc(PAGESIZE); +// memset(next_dst_table, 0, 0x1000); +// dst_page_table_va[i] = KERNEL_VIRT_TO_PHYS((size_t)next_dst_table); // point to that table +// dst_page_table_va[i] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); +// // size_t *next_dst_table = (size_t)(dst_page_table_va[i] & ENTRY_ADDR_MASK); +// if (src_page_table_va[i] & PD_TABLE == PD_TABLE) +// { +// if (level < PTE) +// { +// mmu_pagetable_copy(next_dst_table,next_src_table,level); +// } +// else +// { +// //-------- +// } +// } +// } +// } +// } + +void mmu_memfail_abort_handle(esr_el1_t *esr_el1) +{ + lock(); + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t" : "=r"(far_el1)); + + list_head_t *pos; + vm_area_struct_t *vma; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + vma = (vm_area_struct_t *)pos; + if (vma->virt_addr <= far_el1 && vma->virt_addr + vma->area_size >= far_el1) + { + the_area_ptr = vma; + break; + } + } + // area is not part of process's address space + if (!the_area_ptr) + { + uart_sendlinek("\n\n"); + ERROR("[Segmentation fault]: Kill Process\r\n"); + ERROR("Invilad Vitural Address Access: %x\n", far_el1); + thread_exit(); + // dump_vma(); + unlock(); + while (1) + schedule(); + + return; + } + + // For translation fault, only map one page frame for the fault address + 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_sendlinek("\n"); + // WARING("[Translation fault]: 0x%x\r\n", far_el1); // far_el1: Fault address register. + // Holds the faulting Virtual Address for all synchronous Instruction or Data Abort, PC alignment fault and Watchpoint exceptions that are taken to 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); + addr_offset = ALIGN_DOWN(addr_offset, 0x1000); + + size_t flag = 0; + if (!(the_area_ptr->rwx & (0b1 << 2))) + flag |= PD_UNX; // 4: executable + if (!(the_area_ptr->rwx & (0b1 << 1))) + flag |= PD_RDONLY; // 2: writable + if (the_area_ptr->rwx & (0b1 << 0)) + flag |= PD_UK_ACCESS; // 1: readable / accessible + map_one_page((size_t *)PHYS_TO_KERNEL_VIRT(curr_thread->context.pgd), the_area_ptr->virt_addr + addr_offset, the_area_ptr->phys_addr + addr_offset, flag); + // dump_pagetable(the_area_ptr->virt_addr + addr_offset,the_area_ptr->phys_addr + addr_offset); + } + else + { + // For other Fault (permisson ...etc) + // uart_sendlinek("[Other Fault]: Kill Process\r\n"); + // uart_sendlinek("esr_el1: 0x%x\r\n", esr_el1); + uart_sendlinek("\n\n"); + if ((unsigned long)esr_el1 & (1 << 10)) + { + ERROR("[Permission Fault] due to a write of an Allocation Tag to Canonically Tagged memory.\r\n"); + } + if ((unsigned long)esr_el1 & (1 << 9)) + { + ERROR("[Permission Fault] due to the NoTagAccess memory attribute..\r\n"); + } + // check_permission(,the_area_ptr->rwx); + thread_exit(); + // dump_vma(); + unlock(); + while (1) + schedule(); + } + unlock(); +} + +vm_area_struct_t *check_vma_overlap(thread_t *t, unsigned long user_va, unsigned long size) +{ + list_head_t *pos; + vm_area_struct_t *vma; + list_for_each(pos, &t->vma_list) + { + vma = (vm_area_struct_t *)pos; + // Detect existing vma overlapped + if (!(vma->virt_addr >= (unsigned long)(user_va + size) || vma->virt_addr + vma->area_size <= (unsigned long)user_va)) + { + return vma; + } + } + return 0; +} + +int check_permission(int userId, int VMA_Permission) +{ + switch (PERMISSION_INVAILD(userId, VMA_Permission)) + { + DUMP_NAME(1, "Read is Invaild in This Vitural Memory Area") + DUMP_NAME(2, "Write is Invaild in This Vitural Memory Area") + DUMP_NAME(3, "Write & Read is Invaild in This Vitural Memory Area") + DUMP_NAME(4, "Exec is Invaild in This Vitural Memory Area") + DUMP_NAME(5, "Exec & Read is Invaild in This Vitural Memory Area") + DUMP_NAME(6, "Exec & Write is Invaild in This Vitural Memory Area") + DUMP_NAME(7, "Exec & Write & Read is Invaild in This Vitural Memory Area") + default: + uart_sendlinek("[other Fault]: UNKNOW FAULT"); + break; + } + return PERMISSION_INVAILD(userId, VMA_Permission); +} + +void dump_vma() +{ + uart_sendlinek(" +--------------------------+\n"); + uart_sendlinek(" | DUMP Vitural Memory Area |\n"); + uart_sendlinek(" +--------------------------+\n"); + list_head_t *pos; + list_for_each(pos, &curr_thread->vma_list) + { + uart_sendlinek("===============================================\n"); + vm_area_struct_t *vma_ptr = (vm_area_struct_t *)pos; + + uart_sendlinek("Vitural Memory Area Name : "); + switch (vma_ptr->name) + { + DUMP_NAME(UNKNOW_AREA, "UNKNOW_AREA") + DUMP_NAME(USER_DATA, "USER_DATA") + DUMP_NAME(USER_STACK, "USER_STACK") + DUMP_NAME(PERIPHERAL, "PERIPHERAL") + DUMP_NAME(USER_SIGNAL_WRAPPER, "USER_SIGNAL_WRAPPER") + DUMP_NAME(USER_EXEC_WRAPPER, "USER_EXEC_WRAPPER") + default: + uart_sendlinek("unnamed: %d\n", vma_ptr->name); + break; + } + uart_sendlinek("Base Vitural Address : 0x%x\n", ((vm_area_struct_t *)pos)->virt_addr); + uart_sendlinek("Base Physical Address : 0x%x\n", ((vm_area_struct_t *)pos)->phys_addr); + uart_sendlinek("Area Size : 0x%x\n", ((vm_area_struct_t *)pos)->area_size); + uart_sendlinek("Exec, Write, Read : 0x%x\n", ((vm_area_struct_t *)pos)->rwx); + uart_sendlinek("===============================================\n\n"); + } +} + +void dump_pagetable(unsigned long user_va, unsigned long pa) +{ + uart_sendlinek(" +---------------------------+\n"); + uart_sendlinek(" | DUMP PAGE TABLE & ADDRESS |\n"); + uart_sendlinek(" +---------------------------+\n"); + unsigned long *pagetable_pa = curr_thread->context.pgd; + unsigned long *pagetable_kernel_va = (unsigned long *)PHYS_TO_KERNEL_VIRT(pagetable_pa); + + uart_sendlinek("===============================================\n"); + uart_sendlinek("User Physical Address : 0x%x\n", pa); + uart_sendlinek("User Vitural Address : 0x%x\n", user_va); + unsigned long offset = user_va & 0xFFF; + uart_sendlinek("Vitural Address offset: 0x%x\n", offset); + for (int level = 0; level < 4; level++) + { + uart_sendlinek("-----------------------------------------------\n"); + uart_sendlinek("PAGE TABLE : "); + switch (level) + { + DUMP_NAME(PGD, "PGD") + DUMP_NAME(PUD, "PUD") + DUMP_NAME(PMD, "PMD") + DUMP_NAME(PTE, "PTE") + + default: + uart_sendlinek("unnamed: %d\n", level); + break; + } + uart_sendlinek("Pagetable Physical Address: 0x%x\n", pagetable_pa); + uart_sendlinek("Pagetable Vitural Address: 0x%x\n", pagetable_kernel_va); + + unsigned int idx = (user_va >> (39 - level * 9)) & 0x1FF; + uart_sendlinek("Index of Pagetable: 0x%x\n", idx); + uart_sendlinek("Entry %x of Pagetable: 0x%x\n", idx, pagetable_kernel_va[idx]); + if (level == PTE) + { + pagetable_pa = (unsigned long *)(pagetable_kernel_va[idx] & ENTRY_ADDR_MASK); + uart_sendlinek("The Page Base Physical Address: 0x%x\n", pagetable_pa); + uart_sendlinek("The Physical Address: 0x%x\n", (unsigned long)pagetable_pa | offset); + } + else + { + pagetable_pa = (unsigned long *)(pagetable_kernel_va[idx] & ENTRY_ADDR_MASK); + uart_sendlinek("next Pagetable Physical Address: 0x%x\n", pagetable_pa); + pagetable_kernel_va = (unsigned long *)PHYS_TO_KERNEL_VIRT(pagetable_pa); + uart_sendlinek("next Pagetable Vitural Address: 0x%x\n", pagetable_kernel_va); + } + uart_sendlinek("-----------------------------------------------\n"); + } +} \ No newline at end of file diff --git a/lab8/kernel/src/sched.S b/lab8/kernel/src/sched.S new file mode 100644 index 000000000..7d0f93cd2 --- /dev/null +++ b/lab8/kernel/src/sched.S @@ -0,0 +1,57 @@ +.global switch_to +switch_to: // (prev, next) = (x0, x1) + stp x19, x20, [x0, 16 * 0] // store callee saved register + 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] // store sp, fp, lr + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] // load callee saved register + 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] // load sp, fp, lr + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 // update current thread id + + dsb ish // ensure write has completed, lock for context switch + msr ttbr0_el1, x0 // switch translation based address. + tlbi vmalle1is // invalidate all TLB entries in stage 1, el1 and inner shareable + dsb ish // ensure completion of TLB invalidatation + isb // clear pipeline + + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 // get the current thread id + 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 diff --git a/lab8/kernel/src/sched.c b/lab8/kernel/src/sched.c new file mode 100644 index 000000000..09bbbcd17 --- /dev/null +++ b/lab8/kernel/src/sched.c @@ -0,0 +1,270 @@ +#include "sched.h" +#include "uart1.h" +#include "exception.h" +#include "memory.h" +#include "timer.h" +#include "shell.h" +#include "signal.h" +#include "stdio.h" +#include "mmu.h" +#include "string.h" + +list_head_t *run_queue; +execfile c_execfile; + +thread_t threads[PIDMAX + 1]; +thread_t *curr_thread; + +int pid_history = 0; +int timer_sched_flag = 0; +int shell_flag = 0; + +void thread_sched_init() +{ + lock(); + // init thread freelist and run_queue + run_queue = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + + // init pids + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].isused = 0; + threads[i].pid = i; + threads[i].iszombie = 0; + } + + thread_t *idlethread = thread_create(start_shell); + curr_thread = idlethread; + asm volatile("msr tpidr_el1, %0" ::"r"(&curr_thread->context)); // Don't let thread structure NULL as we enable the functionality + + // thread_create(idle); + unlock(); +} + +thread_t *thread_create(void *start) +{ + lock(); + thread_t *r; + // find usable PID, don't use the previous one + if (pid_history > PIDMAX) + { + unlock(); + return 0; + } + + if (!threads[pid_history].isused) + { + r = &threads[pid_history]; + pid_history += 1; + } + else + { + unlock(); + return 0; + } + + INIT_LIST_HEAD(&r->vma_list); + r->iszombie = 0; + r->isused = 1; + r->stack_alloced_ptr = kmalloc(USTACK_SIZE); + r->kernel_stack_alloced_ptr = kmalloc(KSTACK_SIZE); + r->context.lr = (unsigned long long)start; + r->context.sp = (unsigned long long)r->kernel_stack_alloced_ptr + KSTACK_SIZE - STACK_BASE_OFFSET; + r->context.fp = r->context.sp; // frame pointer for local variable, which is also in stack. + + strcpy(r->curr_working_dir, "/"); // new <----------------------------------------------------------------------- + vfs_open("/dev/uart", 0, &r->file_descriptors_table[stdin]); + vfs_open("/dev/uart", 0, &r->file_descriptors_table[stdout]); + vfs_open("/dev/uart", 0, &r->file_descriptors_table[stderr]); + + r->context.pgd = kmalloc(0x1000); + memset(r->context.pgd, 0, 0x1000); + r->context.pgd = (void *)KERNEL_VIRT_TO_PHYS(r->context.pgd); + + r->signal_is_checking = 0; + // initial all signal handler with signal_default_handler (kill thread) + + // uart_sendlinek("signal_default_handler : 0x%x\n", signal_default_handler); + for (int i = 0; i < SIGNAL_MAX; i++) + { + r->signal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add_tail(&r->listhead, run_queue); + unlock(); + return r; +} + +void schedule() +{ + lock(); + // uart_sendlinek("Run queue size :%d \n", list_size(run_queue)); + do + { + // uart_sendlinek("Run queue size :%d \n", list_size(run_queue)); + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue)); // find a runnable thread + unlock(); + // uart_sendlinek("curr_thread :%d \n",curr_thread->pid); + // uart_sendlinek("curr_thread->context.lr :0x%x \n",curr_thread->context.lr); + switch_to(get_current(), &curr_thread->context); +} + +void idle() +{ + // uart_sendlinek("This is idle\n"); + while (shell_flag > 0) + // while (list_size(run_queue) > 1) + { + // uart_sendlinek("This is idle\n"); + kill_zombies(); // reclaim threads marked as DEAD + schedule(); // switch to next thread in run queue + // delay(10000); + } + kill_zombies(); +} + +void thread_exit() +{ + // thread cannot deallocate the stack while still using it, wait for someone to recycle it. + // In this lab, idle thread handles this task, instead of parent thread. + lock(); + curr_thread->iszombie = 1; + shell_flag--; + unlock(); + // delay(10000); + // schedule(); +} + +void kill_zombies() +{ + lock(); + list_head_t *curr; + thread_t *t; + list_for_each(curr, run_queue) + { + t = (thread_t *)curr; + if (t->iszombie) + { + list_del_entry(curr); + mmu_free_page_tables(t->context.pgd, 0); + mmu_del_vma(t); + for (int i = 0; i < MAX_FD; i++) + { + if (t->file_descriptors_table[i]) + vfs_close(t->file_descriptors_table[i]); + } + kfree(t->kernel_stack_alloced_ptr); + kfree((void *)PHYS_TO_KERNEL_VIRT(t->context.pgd)); + t->iszombie = 0; + t->isused = 0; + } + } + unlock(); +} + +void foo() +{ + // Lab5 Basic 1 Test function + for (int i = 0; i < 10; ++i) + { + uart_sendlinek("Thread id: %d %d\n", curr_thread->pid, i); + int r = 1000000; + while (r--) + { + asm volatile("nop"); + } + schedule(); + } + // uart_sendlinek("exit\n"); + thread_exit(); +} + +int exec_thread() +{ + lock(); + shell_flag++; + thread_t *t = thread_create(exec_proc); + curr_thread = t; + // uart_sendlinek("timer_sched_flag : %d\n", timer_sched_flag); + if (!timer_sched_flag) + { + timer_sched_flag = 1; + add_timer(schedule_timer, 1, "", setSecond); // start scheduler --------------------------------------------------------- + } + + unlock(); + schedule(); + + return 0; +} + +void exec_proc() +{ + el1_interrupt_disable(); // daif 會在 eret 時更改。 + // char *data = c_execfile.data; + // unsigned int filesize = c_execfile.filesize; + thread_t *t = curr_thread; + + // char *data = target_file->f_ops; + unsigned int filesize = (c_execfile.vnode)->f_ops->getsize(c_execfile.vnode); + + t->data = kmalloc(filesize); + t->datasize = filesize; + + // copy file into data + struct file *f; + vfs_open(c_execfile.pathname, 0, &f); + vfs_read(f, t->data, t->datasize); + vfs_close(f); + + //memcpy(t->data, data, t->datasize); + + mmu_add_vma(t, USER_DATA_BASE, t->datasize, (size_t)KERNEL_VIRT_TO_PHYS(t->data), 0b111, 1, USER_DATA); + mmu_add_vma(t, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(t->stack_alloced_ptr), 0b111, 1, USER_STACK); + mmu_add_vma(t, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(t, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), PAGESIZE), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(t, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + t->context.sp = USER_STACK_BASE - STACK_BASE_OFFSET; + t->context.fp = USER_STACK_BASE - STACK_BASE_OFFSET; + t->context.lr = USER_DATA_BASE; + + // for (int i = 0; i < filesize; i++) + // { + // t->data[i] = data[i]; + // } + // dump_vma(); + + // vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[0]); // stdin + // vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[1]); // stdout + // vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[2]); // stderr + + asm("msr tpidr_el1, %0\n\t" // Hold the "kernel(el1)" thread structure information + "msr elr_el1, %1\n\t" // When el0 -> el1, store return address for el1 -> el0 + "msr spsr_el1, xzr\n\t" // EL1h (SPSel = 1) with interrupt disabled + "msr sp_el0, %2\n\t" // el0 stack pointer for el1 process + "mov sp, %3\n\t" // sp is reference for the same el process. For example, el2 cannot use sp_el2, it has to use sp to find its own stack. + "mov x0, %4\n\t" ::"r"(&t->context), + "r"(exec_wrapper), "r"(t->context.sp), "r"(t->kernel_stack_alloced_ptr + KSTACK_SIZE - STACK_BASE_OFFSET), "r"(t->context.lr)); + + asm("eret\n\t"); +} + +void exec_wrapper() +{ + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void schedule_timer(char *notuse) +{ + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq_el0)); + add_timer(schedule_timer, cntfrq_el0 >> 5, "", setTick); + // schedule(); + // 32 * default timer -> trigger next schedule timer +} diff --git a/lab8/kernel/src/sdhost.c b/lab8/kernel/src/sdhost.c new file mode 100644 index 000000000..81e139178 --- /dev/null +++ b/lab8/kernel/src/sdhost.c @@ -0,0 +1,179 @@ +#include "sdhost.h" + +// helper +#define set(io_addr, val) \ + asm volatile("str %w1, [%0]" ::"r"(io_addr), "r"(val) : "memory"); + +#define get(io_addr, val) \ + asm volatile("ldr %w0, [%1]" : "=r"(val) : "r"(io_addr) : "memory"); + +static inline void delay(unsigned long tick) { + while (tick--) { + asm volatile("nop"); + } +} + +static int is_hcs; // high capcacity(SDHC) + +static void pin_setup() { + set(GPIO_GPFSEL4, 0x24000000); + set(GPIO_GPFSEL5, 0x924); + set(GPIO_GPPUD, 0); + delay(15000); + set(GPIO_GPPUDCLK1, 0xffffffff); + delay(15000); + set(GPIO_GPPUDCLK1, 0); +} + +static void sdhost_setup() { + unsigned int tmp; + set(SDHOST_PWR, 0); + set(SDHOST_CMD, 0); + set(SDHOST_ARG, 0); + set(SDHOST_TOUT, SDHOST_TOUT_DEFAULT); + set(SDHOST_CDIV, 0); + set(SDHOST_HSTS, SDHOST_HSTS_MASK); + set(SDHOST_CFG, 0); + set(SDHOST_CNT, 0); + set(SDHOST_SIZE, 0); + get(SDHOST_DBG, tmp); + tmp &= ~SDHOST_DBG_MASK; + tmp |= SDHOST_DBG_FIFO; + set(SDHOST_DBG, tmp); + delay(250000); + set(SDHOST_PWR, 1); + delay(250000); + set(SDHOST_CFG, SDHOST_CFG_SLOW | SDHOST_CFG_INTBUS | SDHOST_CFG_DATA_EN); + set(SDHOST_CDIV, SDHOST_CDIV_DEFAULT); +} + +static int wait_sd() { + int cnt = 1000000; + unsigned int cmd; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_CMD, cmd); + --cnt; + } while (cmd & SDHOST_NEW_CMD); + return 0; +} + +static int sd_cmd(unsigned cmd, unsigned int arg) { + set(SDHOST_ARG, arg); + set(SDHOST_CMD, cmd | SDHOST_NEW_CMD); + return wait_sd(); +} + +static int sdcard_setup() { + unsigned int tmp; + sd_cmd(GO_IDLE_STATE | SDHOST_NO_REPONSE, 0); + sd_cmd(SEND_IF_COND, VOLTAGE_CHECK_PATTERN); + get(SDHOST_RESP0, tmp); + if (tmp != VOLTAGE_CHECK_PATTERN) { + return -1; + } + while (1) { + if (sd_cmd(APP_CMD, 0) == -1) { + // MMC card or invalid card status + // currently not support + continue; + } + sd_cmd(SD_APP_OP_COND, SDCARD_3_3V | SDCARD_ISHCS); + get(SDHOST_RESP0, tmp); + if (tmp & SDCARD_READY) { + break; + } + delay(1000000); + } + + is_hcs = tmp & SDCARD_ISHCS; + sd_cmd(ALL_SEND_CID | SDHOST_LONG_RESPONSE, 0); + sd_cmd(SEND_RELATIVE_ADDR, 0); + get(SDHOST_RESP0, tmp); + sd_cmd(SELECT_CARD, tmp); + sd_cmd(SET_BLOCKLEN, 512); + return 0; +} + +static int wait_fifo() { + int cnt = 1000000; + unsigned int hsts; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_HSTS, hsts); + --cnt; + } while ((hsts & SDHOST_HSTS_DATA) == 0); + return 0; +} + +static void set_block(int size, int cnt) { + set(SDHOST_SIZE, size); + set(SDHOST_CNT, cnt); +} + +static void wait_finish() { + unsigned int dbg; + do { + get(SDHOST_DBG, dbg); + } while ((dbg & SDHOST_DBG_FSM_MASK) != SDHOST_HSTS_DATA); +} + +void readblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do{ + set_block(512, 1); + sd_cmd(READ_SINGLE_BLOCK | SDHOST_READ, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + get(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while(!succ); + wait_finish(); +} + +void writeblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do{ + set_block(512, 1); + sd_cmd(WRITE_SINGLE_BLOCK | SDHOST_WRITE, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + set(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while(!succ); + wait_finish(); +} + +void sd_init() { + pin_setup(); + sdhost_setup(); + sdcard_setup(); +} diff --git a/lab8/kernel/src/shell.c b/lab8/kernel/src/shell.c new file mode 100644 index 000000000..6f23eb049 --- /dev/null +++ b/lab8/kernel/src/shell.c @@ -0,0 +1,597 @@ +#include "shell.h" +#include "mbox.h" +#include "power.h" +#include "stdio.h" +#include "string.h" +#include "cpio.h" +#include "memory.h" +#include "dtb.h" +#include "timer.h" +#include "sched.h" +#include "signal.h" +#include "syscall.h" +#include "vfs.h" +#include "vfs_tmpfs.h" +#include "debug.h" + +#include "uart1.h" + +#define USTACK_SIZE 0x10000 + +extern unsigned long long int lock_counter; +extern list_head_t *run_queue; +extern execfile c_execfile; +extern struct mount *rootfs; + +struct CLI_CMDS cmd_list[CLI_MAX_CMD] = { + {.command = "cat", .help = "concatenate files and print on the standard output", .func = do_cmd_cat}, + {.command = "dtb", .help = "show device tree", .func = do_cmd_dtb}, + {.command = "hello", .help = "print Hello World!", .func = do_cmd_hello}, + {.command = "help", .help = "print all available commands", .func = do_cmd_help}, + {.command = "info", .help = "get device information via mailbox", .func = do_cmd_info}, + {.command = "malloc", .help = "test malloc", .func = do_cmd_malloc}, + {.command = "reboot", .help = "reboot the device", .func = do_cmd_reboot}, + {.command = "exec", .help = "execute user programs ", .func = do_cmd_exec}, + {.command = "setTime", .help = "setTime [MESSAGE] [SECONDS] ", .func = do_cmd_setTimeout}, + {.command = "2sAlert", .help = "set core timer interrupt every 2 second ", .func = do_cmd_set2sAlert}, + {.command = "mtest", .help = "memory testcase generator, allocate and free", .func = do_cmd_mtest}, + {.command = "ttest", .help = "thread tester with dummy function - foo()", .func = do_cmd_ttest}, + {.command = "ls", .help = "list directory contents", .func = do_cmd_ls}, + {.command = "cd", .help = "change directory", .func = do_cmd_cd}, + {.command = "ftest", .help = "get run_queue size", .func = do_cmd_ftest}}; + +extern char *dtb_ptr; +extern thread_t *curr_thread; +extern void *CPIO_DEFAULT_START; + +void start_shell() +{ + // uart_sendlinek("In start_shell\n"); + char input_buffer[CMD_MAX_LEN]; + + cli_print_banner(); + while (1) + { + cli_flush_buffer(input_buffer, CMD_MAX_LEN); + // uart_sendlinek("lock_counter : %d\n",lock_counter); + puts("【 Ciallo~(∠・ω< )⌒★ 】 # "); + cli_cmd_read(input_buffer); + cli_cmd_exec(input_buffer); + idle(); + // uart_sendlinek("idle finish\n"); + } + // return 0; +} + +void cli_flush_buffer(char *buffer, int length) +{ + for (int i = 0; i < length; i++) + { + buffer[i] = '\0'; + } +}; + +void cli_cmd_read(char *buffer) +{ + char c = '\0'; + int idx = 0; + + while (idx < CMD_MAX_LEN - 1) + { + c = getchar(); + // vfs_read(uart, c, 0); + + if (c == 127) // backspace + { + if (idx != 0) + { + puts("\b \b"); + // vfs_write("/dev/uart","\b \b",0); + idx--; + } + } + else if (c == '\n') + { + break; + } + else if (c <= 16 || c >= 32 || c < 127) + { + putchar(c); + // vfs_write("/dev/uart","\b \b",0); + buffer[idx++] = c; + } + } + buffer[idx] = '\0'; + puts("\r\n"); + // vfs_write("/dev/uart","\r\n",0); +} + +int _parse_args(char *buffer, int *argc, char **argv) +{ + char get_cmd = 0; + for (int i = 0; buffer[i] != '\0'; i++) + { + if (!get_cmd) + { + if (buffer[i] == ' ') + { + buffer[i] = '\0'; + get_cmd = 1; + } + } + else + { + if (buffer[i - 1] == '\0' && buffer[i] != ' ' && buffer[i] != '\0') + { + if (*argc >= CMD_MAX_PARAM) + { + return -1; + } + argv[*argc] = buffer + i; + (*argc)++; + } + else if (buffer[i] == ' ') + { + buffer[i] = '\0'; + } + } + } + return 0; +} + +void print_args(int argc, char **argv) +{ + puts("argc: "); + put_int(argc); + puts("\r\n"); + for (int i = 0; i < argc; i++) + { + puts("argv["); + put_int(i); + puts("]: "); + puts(argv[i]); + puts("\r\n"); + } +} + +void cli_cmd_exec(char *buffer) +{ + char *cmd = buffer; + int argc = 0; + char *argv[CMD_MAX_PARAM]; + if (_parse_args(buffer, &argc, argv) == -1) + { + puts("Too many arguments\r\n"); + return; + } + // print_args(argc, argv); + + for (int i = 0; i < CLI_MAX_CMD; i++) + { + if (strcmp(cmd, cmd_list[i].command) == 0) + { + cmd_list[i].func(argc, argv); + return; + } + } + if (*buffer) + { + puts(buffer); + puts(": command not found\r\n"); + } +} + +void cli_print_banner() +{ + // uart_sendlinek("In cli_print_banner\n"); + puts("\r\n"); + puts("============================================================================================\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@- %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% *@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#%@@@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@% *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:=@@@@@@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@- :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* #@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@#- :#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+ +%@@@@@@@@@@@@@@+ -%@@@@@@@@@@@@\r\n"); + puts("@@@@*=: :=#@@@@@@@@#=: =*@@@@@@@@@@\r\n"); + puts("@@@@@#= =*%@@@@@@@@@@*+- :=*%@@@@@@@@@@\r\n"); + puts("@@@@@@@- @@@@@@@@@@@# -@@@@@@@@@@@@@@@@@@%: -%@%@@@@@@@@@@@@@@%= :#@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@= @%++*%@@@@@%: +@@@@#==#@@@@@@@@@@@% %* -%@@#-=+%@@@@@@@- %@@@@@+::=*%@@@@\r\n"); + puts("@@@@@@@= @% =%@+ #@+ =%@@\r\n"); + puts("@@@@@@@= @% ************- =@ #+ :%@@\r\n"); + puts("@@@@@@@= @% @@@@@@@@@@@@+ @@% #= += %@@@* =@@@@- :@@@@\r\n"); + puts("@@@@@@@= @% ##########%@+ :@@% #= +@@@@@@= %@#= -#@@= -@@@@\r\n"); + puts("@@@@@@@= @% :::::::::::@+ :@@% #= +#+=++@= -: -: -@@@@\r\n"); + puts("@@@@@@@= @% @@@@@@@@@@@@+ :@@% #= ++ @= - : -@@@@\r\n"); + puts("@@@@@@@= :@% @@@@@@@@@@@@+ :@@% #= ++ @= %@#- :*%@- -@@@@\r\n"); + puts("@@@@@@@= =@% :@@% #= ++ @= %@@@+ -@@@@- -@@@@\r\n"); + puts("@@@@@@@- #@% ****: +**= +@@% #= ++ @= *####: #####: -@@@@\r\n"); + puts("@@@@@@@ :@@%-=*%@@@@@- #@@#+*#%@@@@% #= ++ =@+ -@@@@\r\n"); + puts("@@@@@@* %@@@@#*=--@@@- #@@#-=+*%@@@% %= *+ =@@+ +@@@@@= :@@@@@# -@@@@\r\n"); + puts("@@@@@@: *@%+- =@@- #@#: :=*+ :@= +*=%@@@+ =%@@@@@* -@@@@@@+: -@@@@\r\n"); + puts("@@@@@= *#- *- #- *@= +@@@@@@#+*@@@@@@*- :+%@@@@@#+*@@@@\r\n"); + puts("@@@@+ -%@: -=**%@@@- #@@%#*+=: *@@= *@@@@@@@@@@@@=: :-%@@@@@@@@@\r\n"); + puts("@@@+ -#@@@%- =@@@@@@@@@- #@@@@@@@@% =%@@@= *@@@@@@@@@@@@@#- :*%@@@@@@@@@@\r\n"); + puts("@@=:+%@@@@@@@%+--=*%@@@@@- :@@@@@@#*=-=*@@@@@@= #@@@@@@@@@@@@@@@* =@@@@@@@@@@@@@\r\n"); + puts("@%%@@@@@@@@@@@@@@@%##@@@@- #@@@@%#%@@@@@@@@@@@= +@@@@@@@@@@@@@@@@@* -@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@- =%@@@@@@@@@@@@@@@@@@@= *@@@@@@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@++%@@@@@@@@@@@@@@@@@@@@@*+%@@@@@@@@@@@@@@@@@@@@+ -@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% +@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= %@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% =@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-%@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=@@@@@@@@@@@@@@@@@\r\n"); + puts("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@@@@@@@@@@@@@@@\r\n"); + puts("============================================================================================\r\n"); +} + +int do_cmd_help(int argc, char **argv) +{ + for (int i = 0; i < CLI_MAX_CMD; i++) + { + puts(cmd_list[i].command); + puts("\t\t\t: "); + puts(cmd_list[i].help); + puts("\r\n"); + } + return 0; +} + +int do_cmd_hello(int argc, char **argv) +{ + puts("Hello World!\r\n"); + return 0; +} + +int do_cmd_info(int argc, char **argv) +{ + // print hw revision + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_BOARD_REVISION; + pt[3] = 4; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt))) + { + puts("Hardware Revision\t: 0x"); + // put_hex(pt[6]); + put_hex(pt[5]); + puts("\r\n"); + } + // print arm memory + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_ARM_MEMORY; + pt[3] = 8; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt))) + { + puts("ARM Memory Base Address\t: 0x"); + put_hex(pt[5]); + puts("\r\n"); + puts("ARM Memory Size\t\t: 0x"); + put_hex(pt[6]); + puts("\r\n"); + } + return 0; +} + +int do_cmd_reboot(int argc, char **argv) +{ + if (argc == 0) + { + + puts("Reboot in 10 seconds ...\r\n\r\n"); + volatile unsigned int *rst_addr = (unsigned int *)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x20; + volatile unsigned int *wdg_addr = (unsigned int *)PM_WDOG; + *wdg_addr = PM_PASSWORD | 0x70000; + } + else if (argc == 1 && strcmp(argv[0], "-c") == 0) + { + puts("Cancel reboot...\r\n"); + volatile unsigned int *rst_addr = (unsigned int *)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x0; + volatile unsigned int *wdg_addr = (unsigned int *)PM_WDOG; + *wdg_addr = PM_PASSWORD | 0x0; + } + return 0; +} + +int do_cmd_ls(int argc, char **argv) +{ + // char *c_filepath; + // char *c_filedata; + // unsigned int c_filesize; + + // CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + // { + // if (header_ptr != 0) + // { + // puts(c_filepath); + // puts("\r\n"); + // } + // } + vfs_ls(); + return 0; +} + +int do_cmd_cat(int argc, char **argv) +{ + int FLAG_getfile = 0; + char *filepath; + char *c_filepath; + char *c_filedata; + unsigned int c_filesize; + + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + + CPIO_for_each(&c_filepath, &c_filesize, &c_filedata) + { + if (strcmp(c_filepath, filepath) == 0) + { + FLAG_getfile = 1; + Readfile(c_filedata, c_filesize); + break; + } + } + + if (!FLAG_getfile) + { + puts("cat: "); + puts(filepath); + puts(": No such file or directory\r\n"); + } + return 0; +} + +int do_cmd_malloc(int argc, char **argv) +{ + // test malloc + char *test1 = allocator(0x18); + strcpy(test1, "test malloc1"); + puts(test1); + puts("\r\n"); + + char *test2 = allocator(0x20); + strcpy(test2, "test malloc2"); + puts(test2); + puts("\r\n"); + + char *test3 = allocator(0x28); + strcpy(test3, "test malloc3"); + puts(test3); + puts("\r\n"); + return 0; +} + +int do_cmd_dtb(int argc, char **argv) +{ + traverse_device_tree(dtb_ptr, dtb_callback_show_tree); + return 0; +} + +int do_cmd_exec(int argc, char **argv) +{ + char *filepath; + + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + + char abs_path[MAX_PATH_NAME]; + struct vnode *target_file; + strcpy(abs_path, filepath); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + strcpy(abs_path, "/initramfs/vfs2.img"); // + if (vfs_lookup(abs_path, &target_file) != 0) + { + WARING("File : %s Does not Exit!!\n", abs_path); + return 0; + }; + + c_execfile.vnode = target_file; + c_execfile.pathname = abs_path; + exec_thread(); + return 0; +} + +int do_cmd_setTimeout(int argc, char **argv) +{ + char *msg; + int sec; + if (argc == 2) + { + msg = argv[0]; + sec = atoi(argv[1]); + } + else + { + puts("setTimeout [MESSAGE] [SECONDS]\r\n"); + return -1; + } + add_timer(puts, sec, msg, setSecond); + return 0; +} + +int do_cmd_set2sAlert(int argc, char **argv) +{ + add_timer(timer_set2sAlert, 2, "2sAlert", setSecond); + return 0; +} + +int do_cmd_mtest(int argc, char **argv) +{ + // char *a = kmalloc(513); + // uart_sendlinek("a : %x\n", a); + // //kfree(a); + + // char *b = kmalloc(512); + // uart_sendlinek("b : %x\n", b); + // //kfree(b); + + // char *c = kmalloc(8); + // //kfree(c); + + return 0; +} + +int do_cmd_ttest(int argc, char **argv) +{ + uart_sendlinek("run_queue size : %d\n", list_size(run_queue)); + list_head_t *pos; + list_for_each(pos, run_queue) + { + uart_sendlinek("pid : %d\n", ((thread_t *)pos)->pid); + for (int i = 0; i < SIGNAL_MAX; i++) + { + uart_sendlinek("signal_handler : 0x%x\n", ((thread_t *)pos)->signal_handler[i]); + } + } + return 0; +} + +int do_cmd_ftest(int argc, char **argv) +{ + // ============================== test for fat file read&write start================================== + struct file *testfile; + char testbuf[512] = {}; + vfs_open("/boot/FAT_R.TXT", 0, &testfile); + vfs_read(testfile, testbuf, 25); + uart_sendlinek("%s", testbuf); + vfs_close(testfile); + + struct file *testfilew; + char testbufw[512] = "abcdefg"; + vfs_open("/boot/FAT_R.TXT", 0, &testfilew); + vfs_write(testfilew, testbufw, 8); + // uart_sendlinek("%s", testbufw); + vfs_close(testfilew); + + struct file *testfiler; + char testbufr[512] = {}; + vfs_open("/boot/FAT_R.TXT", 0, &testfiler); + vfs_read(testfiler, testbufr, 25); + uart_sendlinek("%s", testbufr); + vfs_close(testfiler); + + // char test[512] = "" + // char testbufw[512] = "abcdefghijk\n"; + // uart_sendlinek("%s", testbufw); + + // char *t = &testbufw[10]; + // memcpy(t,"abcdefg",7); + // uart_sendlinek("%s", testbufw); + // ============================== test for fat file read&write end ================================== + + // ============================== test for file operation start================================== + // uart_sendlinek("\n\n"); + // uart_sendlinek("+-----------------------------+\n"); + // uart_sendlinek("| mkdir,mount test |\n"); + // uart_sendlinek("+-----------------------------+\n"); + + // vfs_mkdir("/lll"); + // vfs_mkdir("/aaa"); + // vfs_mkdir("/bbb"); + // vfs_mkdir("/lll/ddd"); + // vfs_mount("/lll/ddd", "tmpfs"); + // vfs_mkdir("/lll/ddd/fff"); + // vfs_dump(rootfs->root, 0); + + // uart_sendlinek("\n\n"); + // uart_sendlinek("+-----------------------------+\n"); + // uart_sendlinek("| open,write,read test |\n"); + // uart_sendlinek("+-----------------------------+\n"); + + // struct file *testfilew; + // struct file *testfiler; + // char testbufw[0x30] = "ABCDEABBBBBBDDDDDDDDDDD\n"; + // char testbufr[0x30] = {}; + + // vfs_open("/lll/ddd/ggg.file", O_CREAT, &testfilew); + // vfs_open("/lll/ddd/ggg.file", O_CREAT, &testfiler); + // vfs_write(testfilew, testbufw, 25); + // vfs_read(testfiler, testbufr, 25); + + // uart_sendlinek("%s", testbufr); + // vfs_dump(rootfs->root, 0); + // ============================== test for file operation end ================================== + + // ============================== test for get_absolute_path start================================== + // char *currt_path = "/root"; + // uart_sendlinek("currt_path : %s\n", currt_path); + // char *path1 = "/"; + // uart_sendlinek("path1 rel_path : %s\n", path1); + // get_absolute_path(path1, currt_path); + // // uart_sendlinek("path1 abs_path : %s\n", path1); + // uart_sendlinek("---------------------------------------\n"); + // char *path2 = "desktop"; + // uart_sendlinek("path2 rel_path : %s\n", path2); + // get_absolute_path(path2, currt_path); + // // uart_sendlinek("path2 abs_path : %s\n", path2); + // uart_sendlinek("---------------------------------------\n"); + // char *path3 = ".///////"; + // uart_sendlinek("path3 rel_path : %s\n", path3); + // get_absolute_path(path3, currt_path); + // // uart_sendlinek("path3 abs_path : %s\n", path3); + // ============================== test for get_absolute_path end================================== + + // char test[11] = "abcdefghijk"; + // char *t2 = &test[3]; + // uart_sendlinek("abs_path : %s\n", test); + // uart_sendlinek("abs_path : %s\n", t2); + // t2 = &test[5]; + // uart_sendlinek("abs_path : %s\n", t2); + + return 0; +} + +int do_cmd_cd(int argc, char **argv) +{ + char *filepath; + if (argc == 1) + { + filepath = argv[0]; + } + else + { + puts("Too many arguments\r\n"); + return -1; + } + vfs_cd(filepath); + return 0; +} \ No newline at end of file diff --git a/lab8/kernel/src/signal.c b/lab8/kernel/src/signal.c new file mode 100644 index 000000000..454315f6e --- /dev/null +++ b/lab8/kernel/src/signal.c @@ -0,0 +1,90 @@ +#include "signal.h" +#include "syscall.h" +#include "sched.h" +#include "memory.h" +#include "mmu.h" + +extern thread_t *curr_thread; + +/** + ( ) + [Signal] [UserProcess] ( [ Registered Signal Handler ] ) [UserProcess] + | \ ( / \ ) / + | \ ( / \ ) / +-------|--------------------[SystemCall]-------------------------------------(-----------------/---------------------------------\----------------------)--/--------------- + | \ [Signal Check] (-> \ ) / + V \ / ^ ( \ \ )/ + [Job Pending] [Exception Handler] | ( \ [ Default Signal Handler ]-------- [Exception Handler]) + | | ( ) + --------------------- ( CONTENT SWITCHING FOR SIGNAL HANDLER ) + +**/ + +void check_signal(trapframe_t *tpf) +{ + if (curr_thread->signal_is_checking) + return; + lock(); + // Prevent nested running signal handler. No need to handle + curr_thread->signal_is_checking = 1; + unlock(); + for (int i = 0; i <= SIGNAL_MAX; i++) + { + store_context(&curr_thread->signal_savedContext); + if (curr_thread->sigcount[i] > 0) + { + lock(); + curr_thread->sigcount[i]--; + unlock(); + run_signal(tpf, i); + } + } + lock(); + curr_thread->signal_is_checking = 0; + unlock(); +} + +void run_signal(trapframe_t *tpf, int signal) +{ + curr_thread->curr_signal_handler = curr_thread->signal_handler[signal]; + + // run default handler in kernel + if (curr_thread->curr_signal_handler == signal_default_handler) + { + signal_default_handler(); + return; + } + + // run registered handler in userspace + // char *temp_signal_userstack = kmalloc(USTACK_SIZE); + // asm("msr elr_el1, %0\n\t" + // "msr sp_el0, %1\n\t" + // "msr spsr_el1, %2\n\t" + // "eret\n\t" ::"r"(signal_handler_wrapper), + // "r"(temp_signal_userstack + USTACK_SIZE), + // "r"(tpf->spsr_el1)); + + 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_SIGNAL_WRAPPER_VA + ((size_t)signal_handler_wrapper % 0x1000)), + "r"(tpf->sp_el0), + "r"(tpf->spsr_el1), + "r"(curr_thread->curr_signal_handler)); +} + +void signal_handler_wrapper() +{ + //(curr_thread->curr_signal_handler)(); + // system call sigreturn + // uart_sendlinek("signal_handler_wrapper\n"); + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void signal_default_handler() +{ + kill(0, curr_thread->pid); +} diff --git a/lab8/kernel/src/stdio.c b/lab8/kernel/src/stdio.c new file mode 100644 index 000000000..a1909d4b7 --- /dev/null +++ b/lab8/kernel/src/stdio.c @@ -0,0 +1,121 @@ +#include "stdio.h" +#include "uart1.h" +#include "vfs.h" +#include "sched.h" + +extern thread_t *curr_thread; + +char getchar() +{ + char c[1]; + stdio_op(stdin, c, 1); + return c[0] == '\r' ? '\n' : c[0]; +} + +void putchar(char c) +{ + char cp[2] = {c,'\0'}; + stdio_op(stdout, cp, 1); +} + +void puts(const char *s) +{ + while (*s) + putchar(*s++); +} + +void Readfile(char *str, int size) +{ + while (size--) + { + putchar(*str++); + } +} + +// Function to print an integer to the UART +void put_int(int num) +{ + // Handle the case when the number is 0 + if (num == 0) + { + putchar('0'); + return; + } + + // Temporary array to store the reversed digits as characters + char temp[12]; // Assuming int can have at most 10 digits + int idx = 0; + + // Handle negative numbers + if (num < 0) + { + putchar('-'); + num = -num; + } + + // Convert the number to characters and store in the temporary array in reverse order + while (num > 0) + { + temp[idx++] = (char)(num % 10 + '0'); + num /= 10; + } + + // Reverse output the character digits + while (idx > 0) + { + putchar(temp[--idx]); + } +} + +void put_hex(unsigned int num) +{ + unsigned int hex; + int index = 28; + puts("0x"); + while (index >= 0) + { + hex = (num >> index) & 0xF; + hex += hex > 9 ? 0x37 : 0x30; + putchar(hex); + index -= 4; + } +} + +// 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; +} + +int fake_log2(unsigned long long n) +{ + int val = 0; + while (n >>= 1) + val++; + return val; +} + +void delay(int r) +{ + while (r--) + { + asm volatile("nop"); + } +} diff --git a/lab8/kernel/src/string.c b/lab8/kernel/src/string.c new file mode 100644 index 000000000..27e2c76a2 --- /dev/null +++ b/lab8/kernel/src/string.c @@ -0,0 +1,292 @@ +#include "string.h" + +size_t strlen(const char *str) +{ + size_t count = 0; + while ((unsigned char)*str++) + count++; + return count; +} + +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 strcasecmp(const char *s1, const char *s2) +{ + char c1, c2; + + while (1) { + c1 = *s1++; + c2 = *s2++; + + if (!c1 || !c2) { + break; + } + + if ('A' <= c1 && c1 <= 'Z') { + c1 |= 0x20; + } + + if ('A' <= c2 && c2 <= 'Z') { + c2 |= 0x20; + } + + if (c1 != c2) { + break; + } + } + + 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 *strcpy(char *dest, const char *src) +{ + while ((*dest++ = *src++)) + ; + return dest; +} + +char *strncpy(char *dest, const char *src, unsigned long long n) +{ + // for (int i = 0; i < n ; i++) { + // dest[i] = src[i]; + // } + + while (n-- && (*dest++ = *src++) ) + ; + return dest; +} + +char *strcat(char *dest, const char *src) +{ + strcpy(dest + strlen(dest), src); + return dest; +} + +char *strncat(char *dest, const char *src, int n) +{ + char *t; + + t = dest; + + while (*t) { + t++; + } + + while (n > 0 && *src) { + *t = *src; + t++; + src++; + n--; + } + + *t = '\0'; + + return dest; +} + +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 (dst - orig > VSPRINT_MAX_BUF_SIZE - 0x10) + { + return -1; + } + // 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] = '-'; + } + if (len > 0 && len < 18) + { + while (i > 18 - len) + { + tmpstr[--i] = ' '; + } + } + p = &tmpstr[i]; + goto copystring; + } + else if (*fmt == 'x') + { + arg = __builtin_va_arg(args, long int); + i = 16; + tmpstr[i] = 0; + do + { + char n = arg & 0xf; + tmpstr[--i] = n + (n > 9 ? 0x37 : 0x30); + arg >>= 4; + } while (arg != 0 && i > 0); + if (len > 0 && len <= 16) + { + while (i > 16 - len) + { + tmpstr[--i] = '0'; + } + } + p = &tmpstr[i]; + goto copystring; + } + else 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; + return dst - orig; +} + +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; +} + +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/lab8/kernel/src/syscall.c b/lab8/kernel/src/syscall.c new file mode 100644 index 000000000..71f4d67a6 --- /dev/null +++ b/lab8/kernel/src/syscall.c @@ -0,0 +1,352 @@ +#include "bcm2837/rpi_mbox.h" +#include "syscall.h" +#include "sched.h" +#include "uart1.h" +#include "stdio.h" +#include "exception.h" +#include "memory.h" +#include "mbox.h" +#include "signal.h" +#include "string.h" + +#include "debug.h" +#include "cpio.h" +#include "dtb.h" +#include "mmu.h" + +extern void *CPIO_DEFAULT_START; +extern thread_t *curr_thread; +extern thread_t threads[PIDMAX + 1]; + +// trap is like a shared buffer for user space and kernel space +// Because general-purpose registers are used for both arguments and return value, +// We may receive the arguments we need, and overwrite them with return value. + +int getpid(trapframe_t *tpf) +{ + // uart_sendlinek("this is getpid"); + tpf->x0 = curr_thread->pid; + return curr_thread->pid; +} + +size_t uartread(trapframe_t *tpf, char buf[], size_t size) +{ + int i = 0; + stdio_op(stdin, buf, size); + tpf->x0 = i; + return i; +} + +size_t uartwrite(trapframe_t *tpf, const char buf[], size_t size) +{ + int i = 0; + char *cptr = buf; + stdio_op(stdout, buf, size); + 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[]) +{ + lock(); + mmu_del_vma(curr_thread); + INIT_LIST_HEAD(&curr_thread->vma_list); + + // use virtual file system + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, name); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + // uart_sendlinek("file name : %s\n", name); + // uart_sendlinek("curr_working_dir : %s\n", curr_thread->curr_working_dir); + // uart_sendlinek("abs_path : %s\n", abs_path); + + struct vnode *target_file; + if (vfs_lookup(abs_path, &target_file) != 0) + { + WARING("File : %s Does not Exit!!", abs_path); + return 0; + }; + curr_thread->datasize = target_file->f_ops->getsize(target_file); + uart_sendlinek("datasize : %d\n", curr_thread->datasize); + + // curr_thread->data = kmalloc(curr_thread->datasize > PAGESIZE ? curr_thread->datasize : PAGESIZE); + curr_thread->data = kmalloc(curr_thread->datasize); + curr_thread->stack_alloced_ptr = kmalloc(USTACK_SIZE); + + // data copy + memcpy(curr_thread->signal_handler, curr_thread->signal_handler, SIGNAL_MAX * 8); + struct file *f; + vfs_open(abs_path, 0, &f); + vfs_read(f, curr_thread->data, curr_thread->datasize); + vfs_close(f); + + // clean vma & page_tables + asm("dsb ish\n\t"); // ensure write has completed + mmu_free_page_tables(curr_thread->context.pgd, 0); + memset((void *)PHYS_TO_KERNEL_VIRT(curr_thread->context.pgd), 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 + mmu_del_vma(curr_thread); + + // new vma + mmu_add_vma(curr_thread, USER_DATA_BASE, curr_thread->datasize, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->data), 0b111, 1, USER_DATA); + mmu_add_vma(curr_thread, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(curr_thread->stack_alloced_ptr), 0b011, 1, USER_STACK); + mmu_add_vma(curr_thread, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(curr_thread, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), 0x1000), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(curr_thread, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + tpf->elr_el1 = USER_DATA_BASE; + tpf->sp_el0 = USER_STACK_BASE - STACK_BASE_OFFSET; + tpf->x0 = 0; + unlock(); + return 0; +} + +// extern unsigned long long int lock_counter; +int fork(trapframe_t *tpf) +{ + lock(); + thread_t *newt = thread_create(curr_thread->data); + // mmu_set_PTE_readonly(curr_thread->context.pgd,0); + // mmu_pagetable_copy(newt->context.pgd,curr_thread->context.pgd,0); + // uart_sendlinek("fork\n"); + memcpy(newt->signal_handler, curr_thread->signal_handler, SIGNAL_MAX * 8); + memcpy(newt->stack_alloced_ptr, curr_thread->kernel_stack_alloced_ptr, USTACK_SIZE); + memcpy(newt->kernel_stack_alloced_ptr, curr_thread->kernel_stack_alloced_ptr, KSTACK_SIZE); + memcpy(newt->curr_working_dir, curr_thread->curr_working_dir,MAX_PATH_NAME+1); + + newt->datasize = curr_thread->datasize; + newt->data = kmalloc(newt->datasize); + memcpy(newt->data,curr_thread->data,newt->datasize); + //newt->curr_working_dir = curr_thread->curr_working_dir; + // memcpy(newt->file_descriptors_table, curr_thread->file_descriptors_table, MAX_FD * 8); // <------------------------------------------------ + mmu_add_vma(newt, USER_DATA_BASE, newt->datasize, (size_t)KERNEL_VIRT_TO_PHYS(newt->data), 0b111, 1, USER_DATA); + mmu_add_vma(newt, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)KERNEL_VIRT_TO_PHYS(newt->stack_alloced_ptr), 0b011, 1, USER_STACK); + mmu_add_vma(newt, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0, PERIPHERAL); + mmu_add_vma(newt, USER_SIGNAL_WRAPPER_VA, 0x1000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(signal_handler_wrapper), 0x1000), 0b101, 0, USER_SIGNAL_WRAPPER); + mmu_add_vma(newt, USER_EXEC_WRAPPER_VA, 0x2000, ALIGN_DOWN((size_t)KERNEL_VIRT_TO_PHYS(exec_wrapper), PAGESIZE), 0b101, 0, USER_EXEC_WRAPPER); + + int parent_pid = curr_thread->pid; + + store_context(get_current()); + // for child + if (parent_pid != curr_thread->pid) + { + goto child; + } + + // 除了PGD以外的context都複製。 + void *temp_pgd = newt->context.pgd; + newt->context = curr_thread->context; + newt->context.pgd = temp_pgd; + //memcpy(newt->context.pgd,curr_thread->context.pgd,PAGESIZE); + + 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(); + while (1) + schedule(); +} + +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox) +{ + lock(); + + unsigned int size_of_mbox = mbox[0]; + memcpy((char *)pt, mbox, size_of_mbox); + mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)); + memcpy(mbox, (char *)pt, size_of_mbox); + + // tpf->x0 = 8; + unlock(); + return 0; +} + +// only need to implement the anonymous page mapping in this Lab. +void *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset) +{ + len = ALIGN_UP(len, PAGESIZE); + unsigned long base_user_va = ALIGN_DOWN((unsigned long)addr, PAGESIZE); + uart_sendlinek("+\n"); + uart_sendlinek("| User request new vma base vitural address: 0x%x\n", (unsigned long)addr); + uart_sendlinek("| Aligned to PAGESIZE: 0x%x\n", base_user_va); + uart_sendlinek("| User request new vma size: 0x%x\n", len); + uart_sendlinek("| Exec, Write, Read : 0x%d\n", prot); + uart_sendlinek("+\n"); + + // Req #2 check if overlap + vm_area_struct_t *the_area_ptr = check_vma_overlap(curr_thread, base_user_va, (unsigned long)len); + // take as a hint to decide new region's start address + if (the_area_ptr) + { + WARING("Vitural Memory Area Overlap\n"); + WARING("Find another vma base vitural address\n"); + tpf->x0 = (unsigned long)mmap(tpf, (void *)(the_area_ptr->virt_addr + the_area_ptr->area_size), len, prot, flags, fd, file_offset); + return (void *)tpf->x0; + } + // create new valid region, map and set the page attributes (prot) + mmu_add_vma(curr_thread, base_user_va, len, KERNEL_VIRT_TO_PHYS((unsigned long)kmalloc(len)), prot, 1, UNKNOW_AREA); + tpf->x0 = base_user_va; + return (void *)tpf->x0; +} + +void kill(trapframe_t *tpf, int pid) +{ + if (pid < 0 || pid >= PIDMAX || !threads[pid].isused) + return; + + lock(); + + if (pid == curr_thread->pid) + { + uart_sendlinek("[!] you kill youself !! \n"); + thread_exit(); + unlock(); + while (1) + schedule(); + } + else + { + threads[pid].iszombie = 1; + unlock(); + } + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0) + return; + // uart_sendlinek("handler : 0x%x\n", handler); + curr_thread->signal_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) +{ + // unsigned long signal_ustack = tpf->sp_el0 % USTACK_SIZE == 0 ? tpf->sp_el0 - USTACK_SIZE : tpf->sp_el0 & (~(USTACK_SIZE - 1)); + // kfree((char *)signal_ustack); + load_context(&curr_thread->signal_savedContext); +} + +void syscall_unlock(trapframe_t *tpf) +{ + unlock(); +} + +void syscall_lock(trapframe_t *tpf) +{ + lock(); +} + +char *get_file_start(char *thefilepath) +{ + int FLAG_getfile = 0; + char *filepath; + char *filedata; + unsigned int filesize; + // struct cpio_newc_header *header_pointer = CPIO_DEFAULT_START; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + if (strcmp(thefilepath, filepath) == 0) + { + FLAG_getfile = 1; + return filedata; + } + } + + if (!FLAG_getfile) + { + uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + } + + // while (header_pointer != 0) + // { + // int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // //if parse header error + // if (error) + // { + // uart_sendlinek("error"); + // break; + // } + + // if (strcmp(thefilepath, filepath) == 0) + // { + // return filedata; + // } + + // //if this is TRAILER!!! (last of file) + // if (header_pointer == 0) + // uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + // } + + return 0; +} + +unsigned int get_file_size(char *thefilepath) +{ + int FLAG_getfile = 0; + char *filepath; + char *filedata; + unsigned int filesize; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + if (strcmp(thefilepath, filepath) == 0) + { + FLAG_getfile = 1; + return filesize; + } + } + + if (!FLAG_getfile) + { + uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + } + + // while (header_pointer != 0) + // { + // int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // // if parse header error + // if (error) + // { + // uart_sendlinek("error"); + // break; + // } + + // if (strcmp(thefilepath, filepath) == 0) + // { + // return filesize; + // } + + // // if this is TRAILER!!! (last of file) + // if (header_pointer == 0) + // uart_sendlinek("execfile: %s: No such file or directory\r\n", thefilepath); + // } + return 0; +} diff --git a/lab8/kernel/src/syscall_fs.c b/lab8/kernel/src/syscall_fs.c new file mode 100644 index 000000000..3f6e95391 --- /dev/null +++ b/lab8/kernel/src/syscall_fs.c @@ -0,0 +1,157 @@ +#include "bcm2837/rpi_mbox.h" +#include "vfs.h" +#include "string.h" +#include "sched.h" +#include "exception.h" +#include "uart1.h" +#include "vfs_dev_framebuffer.h" +#include "stdio.h" + +extern void *CPIO_DEFAULT_START; +extern thread_t *curr_thread; +extern thread_t threads[PIDMAX + 1]; + +int open(trapframe_t *tpf, const char *pathname, int flags) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + + uart_sendlinek("file name : %s\n", pathname); + uart_sendlinek("curr_working_dir : %s\n", curr_thread->curr_working_dir); + uart_sendlinek("abs_path : %s\n", abs_path); + + // update abs_path + get_absolute_path(abs_path, curr_thread->curr_working_dir); + for (int i = 0; i < MAX_FD; i++) + { + // find a usable fd + if (!curr_thread->file_descriptors_table[i]) + { + // uart_sendlinek("i : %d\n", i); + // while (1) + // ; + + if (vfs_open(abs_path, flags, &curr_thread->file_descriptors_table[i]) != 0) + { + break; + } + + tpf->x0 = i; + return i; + } + } + + tpf->x0 = -1; + return -1; +} + +int close(trapframe_t *tpf, int fd) +{ + // find an opened 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; +} + +long write(trapframe_t *tpf, int fd, const void *buf, unsigned long count) +{ + // uart_sendlinek("fd : %d\n", fd); + 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; +} + +long 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; +} + +int mkdir(trapframe_t *tpf, const char *pathname, unsigned mode) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + tpf->x0 = vfs_mkdir(abs_path); + return tpf->x0; +} + +int 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); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + tpf->x0 = vfs_mount(abs_path, filesystem); + return tpf->x0; +} + +int chdir(trapframe_t *tpf, const char *path) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, path); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + strcpy(curr_thread->curr_working_dir, abs_path); + + return 0; +} + +long lseek64(trapframe_t *tpf, int fd, long offset, int whence) +{ + if (whence == SEEK_SET) // used for dev_framebuffer + { + curr_thread->file_descriptors_table[fd]->f_pos = offset; + tpf->x0 = offset; + } + else // other is not supported + { + tpf->x0 = -1; + } + + return tpf->x0; +} +extern unsigned int height; +extern unsigned int isrgb; +extern unsigned int pitch; +extern unsigned int width; +int ioctl(trapframe_t *tpf, int fb, unsigned long request, void *info) +{ + if (request == 0) // used for get info (SPEC) + { + 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; +} + +int sync(trapframe_t *tpf) +{ + for (int i = 0; i < MAX_FS_REG;i++) + { + if(!reg_fs[i].name) continue; + vfs_sync(®_fs[i]); + } + tpf->x0 = 0; + return tpf->x0; +} \ No newline at end of file diff --git a/lab8/kernel/src/timer.c b/lab8/kernel/src/timer.c new file mode 100644 index 000000000..061bbb05d --- /dev/null +++ b/lab8/kernel/src/timer.c @@ -0,0 +1,177 @@ +#include "timer.h" +#include "uart1.h" +#include "memory.h" +#include "string.h" +#include "exception.h" +#include + +#define STR(x) #x +#define XSTR(s) STR(s) + +struct list_head *timer_event_list; // first head has nothing, store timer_event_t after it + +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 = allocator(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" // cntp_ctl_el0[0]: enable, Control register for the EL1 physical timer. + // cntp_tval_el0: Holds the timer value for the EL1 physical timer + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // QA7_rev3.4.pdf: Core0 Timer IRQ allows Non-secure physical timer(nCNTPNSIRQ) + ); +} + +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" // QA7_rev3.4.pdf: Mask all timer interrupt + ); +} + +void core_timer_handler() +{ + lock(); + //uart_sendlinek("\nThis is core_timer_handler\n"); + if (list_empty(timer_event_list)) + { + uart_sendlinek("\ntimer_event_list is empty\n"); + 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(); +} + +void timer_event_callback(timer_event_t *timer_event) +{ + list_del_entry((struct list_head *)timer_event); // delete the event in queue + // free(timer_event->args); // free the event's space + // free(timer_event); + ((void (*)(char *))timer_event->callback)(timer_event->args); // call the event + + kfree(timer_event->args); + kfree(timer_event); + + // set queue linked list to next time event if it exists + 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 timer_set2sAlert(char *str) +{ + unsigned long long cntpct_el0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t" : "=r"(cntpct_el0)); // tick auchor + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq_el0)); // tick frequency + + uart_sendlinek("\n"); + uart_sendlinek("[Interrupt start]\n"); + for (int i = 0; i < 1000000000; i++) + ; + uart_sendlinek("[Interrupt finish]\n"); + + // uart_sendlinek("[Interrupt][el1_irq][%s] %d seconds after booting\n", str, cntpct_el0 / cntfrq_el0); + + add_timer(timer_set2sAlert, 2, "2sAlert",setSecond); +} + +void add_timer(void *callback, unsigned long long timeout, char *args, int inTickFormat) +{ + timer_event_t *the_timer_event = kmalloc(sizeof(timer_event_t)); // free by timer_event_callback + // store all the related information in timer_event + the_timer_event->args = kmalloc(strlen(args) + 1); + strcpy(the_timer_event->args, args); + + if (inTickFormat == 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; + INIT_LIST_HEAD(&the_timer_event->listhead); + + // 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 the timer_event is the biggest, run this code block + if (list_is_head(curr, timer_event_list)) + { + list_add_tail(&the_timer_event->listhead, timer_event_list); + } + // 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 auchor + 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 -> frequency of the timer + "mul x1, x1, %0\n\t" // cntpct_el0 = cntfrq_el0 * seconds: relative timer to cntfrq_el0 + "msr cntp_tval_el0, x1\n\t" // Set expired time to cntp_tval_el0, which stores time value of EL1 physical timer. + : "=r"(expired_time)); +} + +// 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 timer + : "=r"(tick)); +} + +// get timer pending queue size +int timer_list_get_size() +{ + int r = 0; + struct list_head *curr; + list_for_each(curr, timer_event_list) + { + r++; + } + return r; +} diff --git a/lab8/kernel/src/uart1.c b/lab8/kernel/src/uart1.c new file mode 100644 index 000000000..30e7bae36 --- /dev/null +++ b/lab8/kernel/src/uart1.c @@ -0,0 +1,177 @@ +#include "bcm2837/rpi_gpio.h" +#include "bcm2837/rpi_uart1.h" +#include "bcm2837/rpi_irq.h" +#include "uart1.h" +#include "string.h" +#include "exception.h" +#include "vfs.h" + +// implement first in first out buffer with a read index and a write index +static char uart_tx_buffer[VSPRINT_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[VSPRINT_MAX_BUF_SIZE]; +unsigned int uart_rx_buffer_widx = 0; +unsigned int uart_rx_buffer_ridx = 0; + +void uart_init() +{ + register unsigned int selector; + + /* initialize UART */ + *AUX_ENABLES = 1; // enable UART1 + *AUX_MU_CNTL_REG = 0; // disable TX/RX + + /* configure UART */ + *AUX_MU_IER_REG = 0; // disable interrupt + *AUX_MU_LCR_REG = 3; // 8 bit data size + *AUX_MU_MCR_REG = 0; // disable flow control + *AUX_MU_BAUD_REG = 270; // 115200 baud rate + + /* map UART1 to GPIO pins */ + selector = *GPFSEL1; + selector &= ~(7 << 12); // clean gpio14, and (11 111 111 111 111 111 000 111 111 111 111)2 + selector |= 2 << 12; // set gpio14 to alt5 + selector &= ~(7 << 15); // clean gpio15, and (11 111 111 111 111 000 111 111 111 111 111)2 + selector |= 2 << 15; // set gpio15 to alt5 + *GPFSEL1 = selector; + + /* enable pin 14, 15 - ref: Page 101 */ + *GPPUD = 0; + selector = 150; + while (selector--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + selector = 150; + while (selector--) + { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; + + *AUX_MU_CNTL_REG = 3; +} + +void uart_flush_FIFO() +{ + // On write: + // Writing with bit 1 set will clear the receive FIFO + // Writing with bit 2 set will clear the transmit FIFOF + *AUX_MU_IIR_REG |= 6; +} + +char uart_recv() +{ + char r; + while (!(*AUX_MU_LSR_REG & 0x01)) + ; + r = (char)(*AUX_MU_IO_REG); + return r; +} + +void uart_send(unsigned int c) +{ + while (!(*AUX_MU_LSR_REG & 0x20)) + ; + *AUX_MU_IO_REG = c; +} + +// AUX_MU_IER_REG -> BCM2837-ARM-Peripherals.pdf - Pg.12 +void uart_interrupt_enable() +{ + *AUX_MU_IER_REG |= 1; // enable read interrupt + *AUX_MU_IER_REG |= 2; // enable write interrupt + *ENABLE_IRQS_1 |= 1 << 29; // Pg.112 +} + +void uart_interrupt_disable() +{ + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + *AUX_MU_IER_REG &= ~(2); // disable write interrupt +} + +// scanf +void uart_r_irq_handler() +{ + if ((uart_rx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_rx_buffer_ridx) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + return; + } + uart_rx_buffer[uart_rx_buffer_widx++] = uart_recv(); + if (uart_rx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) + uart_rx_buffer_widx = 0; + *AUX_MU_IER_REG |= 1; +} + +// printf +void uart_w_irq_handler() +{ + if (uart_tx_buffer_ridx == uart_tx_buffer_widx) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + return; // buffer empty + } + uart_send(uart_tx_buffer[uart_tx_buffer_ridx++]); + if (uart_tx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) + uart_tx_buffer_ridx = 0; + *AUX_MU_IER_REG |= 2; // enable write interrupt +} + +// uart_async_getc read from buffer +// uart_r_irq_handler write to buffer then output +char uart_async_recv() +{ + *AUX_MU_IER_REG |= 1; // enable read interrupt + // do while if buffer empty + while (uart_rx_buffer_ridx == uart_rx_buffer_widx) + { + *AUX_MU_IER_REG |= 1; // enable read interrupt + } + lock(); + char r = uart_rx_buffer[uart_rx_buffer_ridx++]; + + if (uart_rx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) + uart_rx_buffer_ridx = 0; + unlock(); + return r; +} + +// uart_async_putc writes to buffer +// uart_w_irq_handler read from buffer then output +void uart_async_send(char c) +{ + // if buffer full, wait for uart_w_irq_handler + while ((uart_tx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_tx_buffer_ridx) + { + // uart_puts("buffer full\r\n"); + *AUX_MU_IER_REG |= 2; // enable write interrupt + } + lock(); + uart_tx_buffer[uart_tx_buffer_widx++] = c; + if (uart_tx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) + uart_tx_buffer_widx = 0; // cycle pointer + unlock(); + *AUX_MU_IER_REG |= 2; // enable write interrupt +} + +int uart_sendlinek(char *fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[VSPRINT_MAX_BUF_SIZE]; + + char *str = (char *)buf; + int count = vsprintf(str, fmt, args); + + while (*str) + { + if (*str == '\n') + uart_send('\r'); + uart_send(*str++); + } + __builtin_va_end(args); + return count; +} \ No newline at end of file diff --git a/lab8/kernel/src/vfs.c b/lab8/kernel/src/vfs.c new file mode 100644 index 000000000..e352acee2 --- /dev/null +++ b/lab8/kernel/src/vfs.c @@ -0,0 +1,472 @@ +#include "sdhost.h" + +#include "vfs.h" +#include "vfs_dev_framebuffer.h" +#include "vfs_dev_uart.h" +#include "vfs_initramfs.h" +#include "vfs_tmpfs.h" + +#include "memory.h" +#include "string.h" +#include "uart1.h" +#include "sched.h" +#include "debug.h" + +struct mount *rootfs; +struct filesystem reg_fs[MAX_FS_REG]; +struct file_operations reg_dev[MAX_DEV_REG]; +extern thread_t *curr_thread; + +void rootfs_init() +{ + // sd_init + sd_init(); + + // tmpfs + int idx = register_tmpfs(); + rootfs = kmalloc(sizeof(struct mount)); + reg_fs[idx].setup_mount(®_fs[idx], rootfs); + + // initramfs + vfs_mkdir("/initramfs"); + register_initramfs(); + vfs_mount("/initramfs", "initramfs"); + + // fat32 + vfs_mkdir("/boot"); + register_fat32(); + vfs_mount("/boot", "fat32"); + + // // dev_fs + 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); +} + +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; + reg_fs[i].sync = fs->sync; // =================================================== + return i; + } + } + return -1; +} + +int vfs_lookup(const char *pathname, struct vnode **target) +{ + // int is_fat = 0; + // if (!strcmp(pathname, "/boot")) + // { + // is_fat = 1; + // uart_sendlinek("is boot...\n"); + // } + + // if no path input, return root + if (strlen(pathname) == 0 || (!strcmp(&pathname[0], "/") && strlen(pathname) == 1)) + { + *target = rootfs->root; + // uart_sendlinek("path : %s is root \n", pathname); + return 0; + } + struct vnode *dirnode = rootfs->root; + char component_name[MAX_FILE_NAME + 1] = {}; + int c_idx = 0; + // deal with directory + for (int i = 1; i < strlen(pathname); i++) + { + if (pathname[i] == '/') + { + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) + return -1; + // redirect to mounted filesystem + while (dirnode->mount) + { + dirnode = dirnode->mount->root; + // if (is_fat) + // break; + } + c_idx = 0; + } + else + { + component_name[c_idx++] = pathname[i]; + } + } + + // deal with file + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) + return -1; + // redirect to mounted filesystem + while (dirnode->mount) + { + // if (is_fat) + // break; + dirnode = dirnode->mount->root; + } + // return file's vnode + *target = dirnode; + + return 0; +} + +int register_dev(struct file_operations *fo) +{ + for (int i = 0; i < MAX_FS_REG; i++) + { + if (!reg_dev[i].open) + { + // return unique id for the assigned device + 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_mount(const char *target, const char *filesystem) +{ + struct vnode *dirnode; + // search for the target filesystem + struct filesystem *fs = find_filesystem(filesystem); + if (!fs) + { + uart_sendlinek("vfs_mount cannot find filesystem\r\n"); + return -1; + } + + if (vfs_lookup(target, &dirnode) == -1) + { + uart_sendlinek("vfs_mount cannot find dir\r\n"); + return -1; + } + else + { + // mount fs on dirnode + dirnode->mount = kmalloc(sizeof(struct mount)); + // uart_sendlinek("fs name : %s\n",fs->name); + fs->setup_mount(fs, dirnode->mount); + } + return 0; +} + +// file ops +int vfs_mkdir(const char *pathname) +{ + struct vnode *node; + if (vfs_lookup(pathname, &node) == 0) + { + WARING("Directory Exit!! : %s\n", pathname); + return 0; + } + + char dirname[MAX_PATH_NAME] = {}; // before add folder + char newdirname[MAX_PATH_NAME] = {}; // after add folder + + // search for last directory + 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); + + // create new directory if upper directory is found + // struct vnode *node; + if (vfs_lookup(dirname, &node) == 0) + { + // node is the old dir, &node is new dir + node->v_ops->mkdir(node, &node, newdirname); + return 0; + } + + uart_sendlinek("vfs_mkdir cannot find pathname"); + return -1; +} +// file ops +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 + // uart_sendlinek("\nhere!!!!!\n"); + struct vnode *node; + if (vfs_lookup(pathname, &node) != 0 && (flags & O_CREAT)) + { + // grep all of the directory path + 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; + // update dirname to node + if (vfs_lookup(dirname, &node) != 0) + { + uart_sendlinek("cannot ocreate no dir name\r\n"); + return -1; + } + // create a new file node on node, &node is new file, 3rd arg is filename + node->v_ops->create(node, &node, pathname + last_slash_idx + 1); + *target = kmalloc(sizeof(struct file)); + // attach opened file on the new node + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + else // 2. Create a new file handle for this vnode if found. + { + // attach opened file on the node + + *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; +} + +// file ops +int vfs_close(struct file *file) +{ + // 1. release the file handle + // 2. Return error code if fails + file->f_ops->close(file); + return 0; +} + +// file ops +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); +} + +// file ops +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); +} + +// for device operations only +int vfs_mknod(char *pathname, int id) +{ + struct file *f; + // create leaf and its file operations + vfs_open(pathname, O_CREAT, &f); + f->vnode->f_ops = ®_dev[id]; + vfs_close(f); + return 0; +} + + +int vfs_sync(struct filesystem *fs) +{ + return fs->sync(fs); +} + +// 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_sendline("%s",testbufr); + +// struct file *testfile_initramfs; +// vfs_open("/initramfs/get_simpleexec.sh", O_CREAT, &testfile_initramfs); +// vfs_read(testfile_initramfs, testbufr, 30); +// uart_sendline("%s", testbufr); +// } + +char *get_absolute_path(char *path, char *curr_working_dir) +{ + char absolute_path[MAX_PATH_NAME + 1] = {}; + int max_pathdeep = 10; + struct dir_path *ctmp = kmalloc(sizeof(struct dir_path) * max_pathdeep); + int deep = 0; + + if (path[0] == '/') + { + // uart_sendlinek("Input path is absolute_path: %s\n", path); + strcpy(absolute_path, path); + } + else + { + // uart_sendlinek("Input path is relative_path: %s\n", path); + strcpy(absolute_path, curr_working_dir); + if (strcmp(curr_working_dir, "/") != 0) + { + strcat(absolute_path, "/"); + } + strcat(absolute_path, path); + } + // uart_sendlinek("absolute_path: %s\n", absolute_path); + // uart_sendlinek("strlen of bsolute_path: %d\n", strlen(absolute_path)); + + int dir_namesize = 0; + for (int i = 0; i < strlen(absolute_path); i++) + { + dir_namesize++; + if (absolute_path[i + 1] == '/' || i + 1 == strlen(absolute_path)) + { + ctmp[deep].size = dir_namesize; + ctmp[deep].dir = &absolute_path[i + 1 - dir_namesize]; + dir_namesize = 0; + // deep ++; + // uart_sendlinek("ctmp[%d].dir : %s\n", deep, ctmp[deep].dir); + // uart_sendlinek("size : %d\n", ctmp[deep].size); + + if (!strncmp(ctmp[deep].dir, "/..", 3)) + { + // uart_sendlinek("Go back!!\n"); + deep > 0 ? deep-- : deep; + } + else if (!strncmp(ctmp[deep].dir, "/.", 2) || ctmp[deep].size <= 1) + { + // uart_sendlinek("Do Nothing!!\n"); + } + else + { + deep++; + } + } + } + + // uart_sendlinek("deep : %d\n", deep); + int n = 0; + char *cpath = &path[n]; + if (deep == 0) + { + // memset(path,0,MAX_PATH_NAME + 1); + strncpy(cpath, "/\0", 2); + kfree(ctmp); + // uart_sendlinek("absolute_path: %s\n", path); + return path; + } + + for (int i = 0; i < deep; i++) + { + strncpy(cpath, ctmp[i].dir, ctmp[i].size); + n += ctmp[i].size; + cpath = &path[n]; + strncpy(cpath, "\0", 1); + } + + // uart_sendlinek("absolute_path: %s\n", path); + kfree(ctmp); + return path; +} + +void vfs_dump(struct vnode *_vnode, int level) +{ + // uart_sendlinek("In vfs_dump\n"); + displaylayer(level); + if (_vnode->mount != 0) + { + // tmpfs_dump(_vnode->internal, level); + _vnode->v_ops->dump(_vnode, level); + while (_vnode->mount) + { + displaylayer(level); + uart_sendlinek(" !!mount!!\n"); + _vnode = _vnode->mount->root; + } + vfs_dump(_vnode, level); + } + else + { + // tmpfs_dump(_vnode->internal, level); + _vnode->v_ops->dump(_vnode, level); + } +} + +void vfs_ls() +{ + struct vnode *node; + uart_sendlinek("In directory : %s\n", curr_thread->curr_working_dir); + if (vfs_lookup(curr_thread->curr_working_dir, &node) == 0) + { + //uart_sendlinek("find!!\n"); + node->v_ops->ls(node); + } +} + +void vfs_cd(char *filepath) +{ + uart_sendlinek("Before change directory : %s\n", curr_thread->curr_working_dir); + + char abs_path[MAX_PATH_NAME]; + struct vnode *tmp; + + strcpy(abs_path, filepath); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + // uart_sendlinek("abs_path : %s\n",abs_path); + if (vfs_lookup(abs_path, tmp) == 0) + { + strcpy(curr_thread->curr_working_dir, abs_path); + uart_sendlinek("In directory : %s\n", curr_thread->curr_working_dir); + } + else + { + uart_sendlinek("No such directory : %s\n", abs_path); + } +} + +void displaylayer(int level) +{ + for (int i = 0; i < level; i++) + { + uart_sendlinek(" "); + } +} \ No newline at end of file diff --git a/lab8/kernel/src/vfs_dev_framebuffer.c b/lab8/kernel/src/vfs_dev_framebuffer.c new file mode 100644 index 000000000..44cd9833a --- /dev/null +++ b/lab8/kernel/src/vfs_dev_framebuffer.c @@ -0,0 +1,133 @@ +#include "vfs.h" +#include "vfs_dev_framebuffer.h" +#include "uart1.h" +#include "memory.h" +#include "stdio.h" +#include "mbox.h" +#include "string.h" +#include "exception.h" +#include "debug.h" + +// #define MBOX_CH_PROP 8 + +// //The following code is for mailbox initialize used in lab7. +unsigned int width, height, pitch, isrgb; /* dimensions and channel order */ +unsigned char *lfb; /* raw frame buffer address */ + +struct file_operations dev_framebuffer_operations = { + dev_framebuffer_write, (void *)dev_framebuffer_op_deny, dev_framebuffer_open, + dev_framebuffer_close, dev_framebuffer_lseek64, (void *)dev_framebuffer_op_deny}; + +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +int init_dev_framebuffer() +{ + // The following code is for mailbox initialize used in lab7. + pt[0] = 35 * 4; + pt[1] = MBOX_TAG_REQUEST_CODE; + + pt[2] = 0x48003; // set phy wh + pt[3] = 8; + pt[4] = 8; + pt[5] = 1024; // FrameBufferInfo.width + pt[6] = 768; // FrameBufferInfo.height + + pt[7] = 0x48004; // set virt wh + pt[8] = 8; + pt[9] = 8; + pt[10] = 1024; // FrameBufferInfo.virtual_width + pt[11] = 768; // FrameBufferInfo.virtual_height + + pt[12] = 0x48009; // set virt offset + pt[13] = 8; + pt[14] = 8; + pt[15] = 0; // FrameBufferInfo.x_offset + pt[16] = 0; // FrameBufferInfo.y.offset + + pt[17] = 0x48005; // set depth + pt[18] = 4; + pt[19] = 4; + pt[20] = 32; // FrameBufferInfo.depth + + pt[21] = 0x48006; // set pixel order + pt[22] = 4; + pt[23] = 4; + pt[24] = 1; // RGB, not BGR preferably + + pt[25] = 0x40001; // get framebuffer, gets alignment on request + pt[26] = 8; + pt[27] = 8; + pt[28] = 4096; // FrameBufferInfo.pointer + pt[29] = 0; // FrameBufferInfo.size + + pt[30] = 0x40008; // get pitch + pt[31] = 4; + pt[32] = 4; + pt[33] = 0; // FrameBufferInfo.pitch + + pt[34] = MBOX_TAG_LAST_BYTE; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)) && pt[20] == 32 && pt[28] != 0) + { + pt[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + width = pt[5]; // get actual physical width + height = pt[6]; // get actual physical height + pitch = pt[33]; // get number of bytes per line + isrgb = pt[24]; // get the actual channel order + lfb = (void *)PHYS_TO_KERNEL_VIRT(((unsigned long)pt[28])); // raw frame buffer address + } + else + { + ERROR("Unable to set screen resolution to 1024x768x32\n"); + } + + return register_dev(&dev_framebuffer_operations); +} + +int dev_framebuffer_write(struct file *file, const void *buf, size_t len) +{ + lock(); + // uart_sendlinek + if (len + file->f_pos > pitch * height) + { + ERROR("How come? dev_framebuffer_write to no where!\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; +} + +long dev_framebuffer_lseek64(struct file *file, long offset, int whence) +{ + lock(); + if (whence == SEEK_SET) + { + file->f_pos = offset; + unlock(); + return file->f_pos; + } + unlock(); + return -1; +} + +int dev_framebuffer_op_deny() +{ + return -1; +} diff --git a/lab8/kernel/src/vfs_dev_uart.c b/lab8/kernel/src/vfs_dev_uart.c new file mode 100644 index 000000000..d1f1bb590 --- /dev/null +++ b/lab8/kernel/src/vfs_dev_uart.c @@ -0,0 +1,55 @@ +#include "vfs.h" +#include "vfs_dev_uart.h" +#include "uart1.h" +#include "memory.h" +#include "string.h" + +struct file_operations dev_file_operations = { + dev_uart_write, dev_uart_read, dev_uart_open, dev_uart_close, + (void *)dev_uart_op_deny, (void *)dev_uart_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) +{ + char *cbuf = buf; + int i = len; + while (i--) + { + uart_async_send(*(cbuf++)); + } + return len; +} + +int dev_uart_read(struct file *file, void *buf, size_t len) +{ + char *cbuf = buf; + int i = len; + while (i--) + { + *cbuf = uart_async_recv(); + cbuf++; + } + 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; +} + +int dev_uart_op_deny() +{ + return -1; +} diff --git a/lab8/kernel/src/vfs_fat32.c b/lab8/kernel/src/vfs_fat32.c new file mode 100644 index 000000000..d5c9a6d84 --- /dev/null +++ b/lab8/kernel/src/vfs_fat32.c @@ -0,0 +1,389 @@ +#include "vfs_fat32.h" +#include "sdhost.h" +#include "memory.h" +#include "string.h" +#include "u_list.h" +#include "debug.h" +#include "uart1.h" +#include "exception.h" + +struct list_head mounts; // Store all new mountpoints that probably need to be sync'd + +struct vnode_operations fat32_v_ops = { + fat32_lookup, fat32_create, fat32_mkdir, + fat32_isdir, fat32_getname, fat32_getsize, + fat32_ls, fat32_dump}; + +struct file_operations fat32_f_ops = { + fat32_write, + fat32_read, + fat32_open, + fat32_close, + fat32_lseek64, + fat32_getsize}; + +int register_fat32() +{ + struct filesystem fs; + fs.name = "fat32"; + fs.setup_mount = fat32_mount; + fs.sync = fat32_sync; + return register_filesystem(&fs); +} + +int fat32_mount(struct filesystem *fs, struct mount *mount) +{ + struct partition_t *partition; + struct fat32_inode *data; + struct vnode *node; + unsigned int lba; + unsigned char buf[BLOCK_SIZE]; + unsigned char filedata_buf[BLOCK_SIZE]; + + // Read partition table from MBR, which is at first block + readblock(0, buf); + struct MBR *mbr = kmalloc(sizeof(struct MBR)); + memcpy(mbr, buf, sizeof(struct MBR)); + // MBR format + // 000 ~ 1BD: Code area + // 1BE ~ 1FD: Master Partition Table + // 1FE ~ 1FF: Boot Record Signature + + // https://lexra.pixnet.net/blog/post/303910876 + partition = &(mbr->partition_table[0]); + // check Boot Record Signature, constants + if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) + { + return -1; + } + // https://en.wikipedia.org/wiki/Partition_type + if (partition[0].type != 0xb && partition[0].type != 0xc) // check Partition type, only FAT32 + { + return -1; + } + lba = partition[0].lba; + readblock(partition[0].lba, buf); + + data = kmalloc(sizeof(struct fat32_inode)); + data->boot_sector = kmalloc(sizeof(struct boot_sector_t)); + memcpy((void *)data->boot_sector, (void *)buf, sizeof(struct boot_sector_t)); + + // oldnode should attach mount the fs + mount->root = kmalloc(sizeof(struct vnode)); + node = mount->root; + node->mount = 0; + node->v_ops = &fat32_v_ops; + node->f_ops = &fat32_f_ops; + node->internal = data; + + struct boot_sector_t *bs = data->boot_sector; + // int file_block = lba + bs->hidden_sector_cnt + bs->reserved_sector_cnt + bs->fat_cnt * bs->sector_per_fat32; + int file_block = bs->hidden_sector_cnt + bs->reserved_sector_cnt + bs->fat_cnt * bs->sector_per_fat32; + + uart_sendlinek("bs->hidden_sector_cnt: 0x%x\n", bs->hidden_sector_cnt); + uart_sendlinek("bs->reserved_sector_cnt: 0x%x\n", bs->reserved_sector_cnt); + uart_sendlinek("bs->fat_cnt: 0x%x\n", bs->fat_cnt); + uart_sendlinek("bs->sector_per_fat32: 0x%x\n", bs->sector_per_fat32); + uart_sendlinek("file_block: 0x%x\n", file_block); + + readblock(file_block, buf); + + // char *n = buf; + struct sfn_file *file_ptr = buf; + + // int high_block; + // int low_block; + int block; + unsigned long file_size; + struct fat32_inode *tmp; + char name_array[16]; + for (int i = 0; i < 16; i++) + { + // high_block = file_ptr->first_cluster_high; + // low_block = file_ptr->first_cluster_low; + // block = file_block + high_block * 256 + low_block - 2; + block = file_block + get_first_cluster(file_ptr) - 2; + file_size = file_ptr->file_size; + if (file_ptr->name[0] == (char *)0) + break; + uart_sendlinek("name : %s\n", file_ptr->name); + // uart_sendlinek("high_block: %d\n", high_block); + // uart_sendlinek("low_block: %d\n", low_block); + uart_sendlinek("file_size: 0x%x\n", file_size); + uart_sendlinek("fat32_f_ops addr: 0x%x\n", &fat32_f_ops); + + data->entry[i] = kmalloc(sizeof(struct vnode)); + data->entry[i]->f_ops = &fat32_f_ops; + data->entry[i]->v_ops = &fat32_v_ops; + data->entry[i]->internal = kmalloc(sizeof(struct fat32_inode)); + tmp = (struct fat32_inode *)(data->entry[i]->internal); + fat32_get_file_name(tmp->name, file_ptr); + uart_sendlinek("name_array : %s\n", tmp->name); + tmp->sfn = file_ptr; + tmp->datasize = file_size; + + // tmp->data = kmalloc(tmp->datasize); + // for (int j = 0; j < tmp->datasize / BLOCK_SIZE; j++) + // { + // readblock(block + j, tmp->data + j * BLOCK_SIZE); + // } + tmp->data_block_cnt = block; + // tmp->data = kmalloc(BLOCK_SIZE); + // readblock(block, filedata_buf); + // uart_sendlinek("%s\n", filedata_buf); + // memcpy(tmp->data, filedata_buf, BLOCK_SIZE); + + file_ptr++; + } + + for (int i = 0; i < 16; i++) + { + if (data->entry[i] != 0) + { + uart_sendlinek("%d\n", i); + uart_sendlinek("%s\n", ((struct fat32_inode *)data->entry[i]->internal)->name); + } + } + + int max_cluster_num = (bs->sector_per_fat32 * bs->bytes_per_sector / sizeof(unsigned int)); + uart_sendlinek("sector_per_fat32 : %d\n", bs->sector_per_fat32); + uart_sendlinek("bytes_per_sector : %d\n", bs->bytes_per_sector); + uart_sendlinek("max_cluster_num : %d\n", max_cluster_num); + file_allocation_table = kmalloc(max_cluster_num); + + for (int i = 0; i < bs->sector_per_fat32; i++) + readblock(bs->hidden_sector_cnt + bs->reserved_sector_cnt + i, file_allocation_table + i * bs->bytes_per_sector / sizeof(unsigned int)); + + readblock(bs->hidden_sector_cnt + bs->info, buf); + fs_info = kmalloc(sizeof(struct fs_info)); + memcpy(fs_info, buf, sizeof(struct fs_info)); + return 0; +} + +int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct fat32_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < 16; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode) + break; + struct fat32_inode *inode = vnode->internal; + // uart_sendlinek("vnode name : %s\n", inode->Name); + // uart_sendlinek("vnode name size: %d\n", strlen(inode->Name)); + // uart_sendlinek("component_name %s\n", component_name); + // uart_sendlinek("component_name size: %d\n", strlen(component_name)); + if (strcmp(component_name, inode->name) == 0) + { + // uart_sendlinek("GET %s\n", component_name); + *target = vnode; + return 0; + } + } + return -1; +} + +int fat32_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + uart_sendlinek("component_name : %s\n", component_name); + + unsigned char buf[BLOCK_SIZE]; + unsigned char name[FAT32_MAX_FILENAME] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; + unsigned char extension[FAT32_MAX_EXTENSION] = {0x20, 0x20, 0x20}; + struct fat32_inode *inode = dir_node->internal; + struct boot_sector_t *bs = inode->boot_sector; + + int file_block = bs->hidden_sector_cnt + bs->reserved_sector_cnt + bs->fat_cnt * bs->sector_per_fat32; + readblock(file_block, buf); + struct sfn_file *file_ptr = buf; + for (int i = 0; i < 16; i++, file_ptr++) + { + uart_sendlinek("%s\n", file_ptr->name); + if (file_ptr->name[0] == (char *)0) + { + uart_sendlinek("%d\n", i); + break; + } + } + int name_cnt = 0; + for (; name_cnt < strlen(component_name); name_cnt++) + { + if (component_name[name_cnt] == '.' || component_name[name_cnt] == '\0') + { + break; + } + if (name_cnt < FAT32_MAX_FILENAME) + { + name[name_cnt] = component_name[name_cnt]; + } + } + if (component_name[name_cnt] == '.') + { + name_cnt++; + for (int i = 0; name_cnt < strlen(component_name); i++, name_cnt++) + { + if (component_name[name_cnt] == '\0') + { + break; + } + if (i < FAT32_MAX_EXTENSION) + { + extension[i] = component_name[name_cnt]; + } + } + } + // uart_sendlinek("new file name : %s\n", name); + // uart_sendlinek("new file extension : %s\n", extension); + memcpy(file_ptr->name, name, FAT32_MAX_FILENAME); + memcpy(file_ptr->extension, extension, FAT32_MAX_EXTENSION); + // =============================================================================================== + + int max_cluster_num = (bs->sector_per_fat32 * bs->bytes_per_sector / sizeof(unsigned int)); + // find_free_cluster + int free_cluster = fs_info->next_free; + for (; free_cluster < max_cluster_num; free_cluster++) + { + if (file_allocation_table[free_cluster] == FAT32_FREE_CLUSTER) + { + break; + } + } + uart_sendlinek("free_cluster : %d\n", free_cluster); + file_ptr->attribute = 0x20; + file_ptr->creation_time_tenth_seconds = 0; + file_ptr->creation_time = 0; + file_ptr->creation_date = 0; + file_ptr->last_access_date = 0; + file_ptr->first_cluster_high = (free_cluster & 0xFFFF0000) >> 16; + file_ptr->last_write_time = 0; + file_ptr->last_write_date = 0; + file_ptr->first_cluster_low = free_cluster & 0x0000FFFF; + file_ptr->file_size = 512; + file_allocation_table[free_cluster] = FAT32_END_OF_CHAIN; + + writeblock(file_block, buf); + + struct vnode *_vnode = kmalloc(sizeof(struct vnode)); + + _vnode->f_ops = &fat32_f_ops; + _vnode->v_ops = &fat32_v_ops; + _vnode->internal = kmalloc(sizeof(struct fat32_inode)); + struct fat32_inode *_inode = (struct fat32_inode *)(_vnode->internal); + fat32_get_file_name(_inode->name, file_ptr); + _inode->sfn = file_ptr; + _inode->datasize = 512; + *target = _vnode; + + int find_entry = 0; + for (; find_entry < 16; find_entry++) + { + if (!inode->entry[find_entry]) + { + uart_sendlinek("%d\n", find_entry); + break; + } + } + inode->entry[find_entry] = _vnode; + + return 0; +}; + +int fat32_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + return 0; +}; +int fat32_isdir(struct vnode *dir_node) +{ + return 0; +}; +int fat32_getname(struct vnode *dir_node, const char **name) +{ + return 0; +}; +int fat32_getsize(struct vnode *dir_node) +{ + return 0; +}; +int fat32_write(struct file *file, const void *buf, size_t len) +{ + unsigned char filedata_buf[BLOCK_SIZE]; + // char *pt = buf; + struct fat32_inode *inode = file->vnode->internal; + // memcpy(filedata_buf,buf,BLOCK_SIZE); + // uart_sendlinek("IN fat32_write\n"); + // uart_sendlinek("old file->f_pos : %d\n",file->f_pos); + uart_sendlinek("fat32_write : %s\n", buf); + readblock(inode->data_block_cnt, filedata_buf); + // uart_sendlinek("old filedata_buf : %s\n",filedata_buf); + // char *tmp = &filedata_buf[file->f_pos]; + memcpy(filedata_buf + file->f_pos, buf, len); + uart_sendlinek("filedata_buf : %s\n", filedata_buf); + writeblock(inode->data_block_cnt, filedata_buf); + + inode->datasize = len + file->f_pos > inode->datasize ? len + file->f_pos : inode->datasize; + // uart_sendlinek("new file->f_pos : %d\n",file->f_pos); + // uart_sendlinek("new filedata_buf : %s\n",filedata_buf); + // uart_sendlinek("new filedata_buf size: %d\n",sizeof(filedata_buf)); + return len; +}; +int fat32_read(struct file *file, void *buf, size_t len) +{ + // uart_sendlinek("in fat32_read\n"); + unsigned char filedata_buf[BLOCK_SIZE]; + + struct fat32_inode *inode = file->vnode->internal; + readblock(inode->data_block_cnt, filedata_buf); + len = len > inode->datasize ? inode->datasize : len; + memcpy(buf, filedata_buf, len); + return len; +}; +int fat32_open(struct vnode *file_node, struct file **target) +{ + // uart_sendlinek("fat32_f_ops addr: 0x%x\n", file_node->f_ops); + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +}; +int fat32_close(struct file *file) +{ + kfree(file); + return 0; +}; +long fat32_lseek64(struct file *file, long offset, int whence) +{ + return 0; +}; +int fat32_sync(struct filesystem *fs) +{ + return 0; +}; + +int fat32_op_deny() +{ + return -1; +} + +void fat32_dump(struct vnode *vnode, int level) +{ +} + +void fat32_ls(struct vnode *vnode) +{ + // uart_sendlinek("in fat32_ls\n"); + char name_array[16]; + struct fat32_inode *inode = (struct fat32_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= 16; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vnode = inode->entry[child_idx]; + // fat32_get_file_name(name_array,((struct fat32_inode *)vnode->internal)->sfn); + // uart_sendlinek("%s\n", name_array); + uart_sendlinek("%s\n", ((struct fat32_inode *)vnode->internal)->name); + } +} diff --git a/lab8/kernel/src/vfs_initramfs.c b/lab8/kernel/src/vfs_initramfs.c new file mode 100644 index 000000000..11ea52eb2 --- /dev/null +++ b/lab8/kernel/src/vfs_initramfs.c @@ -0,0 +1,206 @@ +#include "vfs_initramfs.h" +#include "vfs.h" +#include "string.h" +#include "memory.h" +#include "cpio.h" +#include "uart1.h" +#include "debug.h" + +struct file_operations initramfs_file_operations = { + initramfs_write, initramfs_read, initramfs_open, + initramfs_close, initramfs_lseek64, initramfs_getsize}; + +struct vnode_operations initramfs_vnode_operations = { + initramfs_lookup, initramfs_create, initramfs_mkdir, + (void *)initramfs_op_deny,(void *)initramfs_op_deny,(void *)initramfs_op_deny, + initramfs_ls, initramfs_dump}; + +int register_initramfs() +{ + struct filesystem fs; + fs.name = "initramfs"; + fs.setup_mount = initramfs_setup_mount; + fs.sync = initramfs_sync; + 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); + // create entry under _mount, cpio files should be attached on it + 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_START; + int idx = 0; + + CPIO_for_each(&filepath, &filesize, &filedata) + { + struct vnode *filevnode = initramfs_create_vnode(0, file_t); + struct initramfs_inode *fileinode = filevnode->internal; + fileinode->data = filedata; + fileinode->datasize = filesize; + fileinode->name = filepath; + // uart_sendlinek("file name: %s\n",filepath); + ramdir_inode->entry[idx++] = filevnode; + } + + return 0; +} + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum fsnode_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) +{ + // read-only + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) +{ + struct initramfs_inode *inode = file->vnode->internal; + // overflow, shrink size + 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; +} + +long initramfs_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// 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; + } + } + return -1; +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // read-only + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // read-only + return -1; +} + +long initramfs_getsize(struct vnode *vd) +{ + struct initramfs_inode *inode = vd->internal; + return inode->datasize; +} + +void initramfs_dump(struct vnode *vnode, int level) +{ + struct initramfs_inode *inode = (struct initramfs_inode *)vnode->internal; + int child_idx = 0; + + uart_sendlinek("+----------------+\n"); + + for (; child_idx <= INITRAMFS_MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + // uart_sendlinek("0x%x\n",inode->entry[child_idx]); + displaylayer(level+1); + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct initramfs_inode *)vnode->internal)->name); + + // displaylayer(level+2); + // uart_sendlinek("datasize size : %d\n", ((struct initramfs_inode *)vnode->internal)->datasize); + displaylayer(level+2); + uart_sendlinek("datasize size : %d\n", initramfs_getsize(vnode)); + } + displaylayer(level); + uart_sendlinek("+----------------+\n"); +} + +void initramfs_ls(struct vnode *vnode) +{ + struct initramfs_inode *inode = (struct initramfs_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= INITRAMFS_MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct initramfs_inode *)vnode->internal)->name); + } +} + +int initramfs_op_deny(){ + return -1; +} + +int initramfs_sync(struct filesystem *fs) +{ + return 0; +} \ No newline at end of file diff --git a/lab8/kernel/src/vfs_tmpfs.c b/lab8/kernel/src/vfs_tmpfs.c new file mode 100644 index 000000000..0c3c601ae --- /dev/null +++ b/lab8/kernel/src/vfs_tmpfs.c @@ -0,0 +1,270 @@ +#include "vfs_tmpfs.h" +#include "vfs.h" +#include "string.h" +#include "memory.h" +#include "uart1.h" + +struct file_operations tmpfs_file_operations = { + tmpfs_write, tmpfs_read, tmpfs_open, + tmpfs_close, tmpfs_lseek64, tmpfs_getsize}; + +struct vnode_operations tmpfs_vnode_operations = { + tmpfs_lookup, tmpfs_create, tmpfs_mkdir, + (void *)tmpfs_op_deny,(void *)tmpfs_op_deny,(void *)tmpfs_op_deny, + tmpfs_ls, tmpfs_dump}; + +int register_tmpfs() +{ + struct filesystem fs; + fs.name = "tmpfs"; + fs.setup_mount = tmpfs_setup_mount; + fs.sync = tmpfs_sync; + 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 fsnode_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; +} + +// 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; + // BFS search tree + 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; + } + } + return -1; +} + +// dir ops +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_sendlinek("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_sendlinek("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) + { + uart_sendlinek("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; +} + +// file operations +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; +} + +// file operations +int tmpfs_write(struct file *file, const void *buf, size_t len) +{ + struct tmpfs_inode *inode = file->vnode->internal; + // write from f_pos + memcpy(inode->data + file->f_pos, buf, len); + // update f_pos and size + file->f_pos += len; + if (inode->datasize < file->f_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 buffer overflow, shrink the request read length + // read from f_pos + 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_close(struct file *file) +{ + kfree(file); + return 0; +} + +long tmpfs_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// file ops +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_sendlinek("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_sendlinek("tmpfs create file exists\r\n"); + return -1; + } + } + + if (child_idx > MAX_DIR_ENTRY) + { + uart_sendlinek("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) + { + uart_sendlinek("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode *_vnode = tmpfs_create_vnode(0, file_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; +} + +void tmpfs_dump(struct vnode *vnode, int level) +{ + struct tmpfs_inode *inode = (struct tmpfs_inode *)vnode->internal; + + uart_sendlinek("+-%s", inode->name); + switch (inode->type) + { + case dir_t: + uart_sendlinek("(dir)\n"); + break; + case file_t: + uart_sendlinek("(file)\n"); + break; + + default: + break; + } + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vfs_dump(inode->entry[child_idx], level + 1); + } +} + +void tmpfs_ls(struct vnode *vnode) +{ + struct tmpfs_inode *inode = (struct tmpfs_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct tmpfs_inode *)vnode->internal)->name); + } +} + +int tmpfs_op_deny() +{ + return -1; +} + +int tmpfs_sync(struct filesystem *fs) +{ + return 0; +} diff --git a/lab8/send_img_to_bootloader.py b/lab8/send_img_to_bootloader.py new file mode 100644 index 000000000..29d43b781 --- /dev/null +++ b/lab8/send_img_to_bootloader.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +from serial import Serial +from pwn import * +import argparse +from sys import platform + +if platform == "linux" or platform == "linux2": + 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 image by uart1...") + 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!") + +else: + 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='COM',default='COM3', type=str, help='COM# 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 image by uart1...") + 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/lab8/tmp_file/vfs_fat32.c b/lab8/tmp_file/vfs_fat32.c new file mode 100644 index 000000000..9adeb7b4f --- /dev/null +++ b/lab8/tmp_file/vfs_fat32.c @@ -0,0 +1,242 @@ +#include "vfs_fat32.h" +#include "sdhost.h" +#include "memory.h" +#include "string.h" +#include "u_list.h" +#include +#include "uart1.h" +#include "exception.h" + +struct list_head mounts; // Store all new mountpoints that probably need to be sync'd + +static inline unsigned int get_first_cluster(struct sfn_file *entry) +{ + return (entry->first_cluster_high << 16) | entry->first_cluster_low; +} + +struct vnode_operations fat32_v_ops = { + fat32_lookup, fat32_create, fat32_mkdir, + fat32_isdir, fat32_getname, fat32_getsize, + fat32_ls, fat32_dump}; + +struct file_operations fat32_f_ops = { + fat32_write, + fat32_read, + fat32_open, + fat32_close, + fat32_lseek64, +}; + +int register_fat32() +{ + struct filesystem fs; + fs.name = "fat32"; + fs.setup_mount = fat32_mount; + fs.sync = fat32_sync; + return register_filesystem(&fs); +} + +int fat32_mount(struct filesystem *fs, struct mount *mount) +{ + struct partition_t *partition; + struct fat32_inode *data; + struct vnode *node; + unsigned int lba; + unsigned char buf[BLOCK_SIZE]; + + // Read partition table from MBR, which is at first block + readblock(0, buf); + // MBR format + // 000 ~ 1BD: Code area + // 1BE ~ 1FD: Master Partition Table + // 1FE ~ 1FF: Boot Record Signature + + // https://lexra.pixnet.net/blog/post/303910876 + partition = (struct partition_t *)&buf[0x1be]; + // check Boot Record Signature, constants + if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) + { + return -1; + } + // https://en.wikipedia.org/wiki/Partition_type + if (partition[0].type != 0xb && partition[0].type != 0xc) // check Partition type, only FAT32 + { + return -1; + } + lba = partition[0].lba; + readblock(partition[0].lba, buf); + + data = kmalloc(sizeof(struct fat32_inode)); + data->boot_sector = kmalloc(sizeof(struct boot_sector_t)); + memcpy((void *)data->boot_sector, (void *)buf, sizeof(struct boot_sector_t)); + + // oldnode should attach mount the fs + mount->root = kmalloc(sizeof(struct vnode)); + node = mount->root; + node->mount = 0; + node->v_ops = &fat32_v_ops; + node->f_ops = &fat32_f_ops; + node->internal = data; + + struct boot_sector_t *bs = data->boot_sector; + // int file_block = lba + bs->hidden_sector_cnt + bs->reserved_sector_cnt + bs->fat_cnt * bs->sector_per_fat32; + int file_block = bs->hidden_sector_cnt + bs->reserved_sector_cnt + bs->fat_cnt * bs->sector_per_fat32; + + uart_sendlinek("bs->hidden_sector_cnt: 0x%x\n", bs->hidden_sector_cnt); + uart_sendlinek("bs->reserved_sector_cnt: 0x%x\n", bs->reserved_sector_cnt); + uart_sendlinek("bs->fat_cnt: 0x%x\n", bs->fat_cnt); + uart_sendlinek("bs->sector_per_fat32: 0x%x\n", bs->sector_per_fat32); + uart_sendlinek("file_block: 0x%x\n", file_block); + + readblock(file_block, buf); + + // char *n = buf; + struct sfn_file *file_ptr = buf; + + int high_block; + int low_block; + int block; + unsigned long file_size; + struct fat32_inode *tmp; + for (int i = 0; i < 16; i++) + { + high_block = file_ptr->first_cluster_high; + low_block = file_ptr->first_cluster_low; + // block = file_block + high_block * 256 + low_block - 2; + block = get_first_cluster(file_ptr) + file_block + 2; + file_size = file_ptr->file_size; + if (!file_size) + break; + uart_sendlinek("name : %s\n", file_ptr->name); + uart_sendlinek("high_block: %d\n", high_block); + uart_sendlinek("low_block: %d\n", low_block); + uart_sendlinek("file_size: 0x%x\n", file_size); + + data->entry[i] = kmalloc(sizeof(struct vnode)); + data->entry[i]->f_ops = &fat32_f_ops; + data->entry[i]->v_ops = &fat32_v_ops; + data->entry[i]->internal = kmalloc(sizeof(struct fat32_inode)); + tmp = (struct fat32_inode *)(data->entry[i]->internal); + memcpy(tmp->Name, file_ptr->name, 16); + tmp->sfn = file_ptr; + tmp->datasize = ALIGN_UP(file_size, BLOCK_SIZE); + tmp->data = kmalloc(tmp->datasize); + for (int j = 0; j < tmp->datasize / BLOCK_SIZE; j++) + { + readblock(block + j, tmp->data + j * BLOCK_SIZE); + } + uart_sendlinek("%s\n", *((char *)tmp->data)); + file_ptr++; + } + + for (int i = 0; i < 16; i++) + { + if (data->entry[i] != 0) + { + uart_sendlinek("%d\n", i); + uart_sendlinek("%s\n", ((struct fat32_inode *)data->entry[i]->internal)->Name); + } + } + + return 0; +} + +int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct fat32_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < 16; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode) + break; + struct fat32_inode *inode = vnode->internal; + if (strcasecmp(component_name, inode->name) == 0) + { + *target = vnode; + return 0; + } + } + return -1; +} + +int fat32_create(struct vnode *dir_node, struct vnode **target, const char *component_name) { + +}; +int fat32_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) { + +}; +int fat32_isdir(struct vnode *dir_node) { + +}; +int fat32_getname(struct vnode *dir_node, const char **name) { + +}; +int fat32_getsize(struct vnode *dir_node) { + +}; +int fat32_write(struct file *file, const void *buf, size_t len) { + +}; +int fat32_read(struct file *file, void *buf, size_t len) +{ + struct fat32_inode *inode = file->vnode->internal; + // overflow, shrink size + 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 fat32_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 fat32_close(struct file *file) +{ + kfree(file); + return 0; +}; +long fat32_lseek64(struct file *file, long offset, int whence) { + +}; +int fat32_sync(struct filesystem *fs) { + +}; + +int fat32_op_deny() +{ + return -1; +} + +void fat32_dump(struct vnode *vnode, int level) +{ +} + +void fat32_ls(struct vnode *vnode) +{ + // uart_sendlinek("in fat32_ls\n"); + struct fat32_inode *inode = (struct fat32_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= 16; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct fat32_inode *)vnode->internal)->Name); + // uart_sendlinek("0x%x\n", ((struct fat32_inode *)vnode->internal)->datasize); + } +} \ No newline at end of file diff --git a/vfs_fat32.c b/vfs_fat32.c new file mode 100644 index 000000000..2d30979a4 --- /dev/null +++ b/vfs_fat32.c @@ -0,0 +1,1689 @@ +#include "vfs_fat32.h" +#include "sdhost.h" +#include "memory.h" +#include "string.h" +#include "u_list.h" +#include +#include "uart1.h" +#include "exception.h" + +struct list_head mounts; // Store all new mountpoints that probably need to be sync'd + +struct vnode_operations fat32_v_ops = { + fat32_lookup, fat32_create, fat32_mkdir, + fat32_isdir, fat32_getname, fat32_getsize, + fat32_ls, fat32_dump}; + +struct file_operations fat32_f_ops = { + fat32_write, + fat32_read, + fat32_open, + fat32_close, + fat32_lseek64, +}; + +int register_fat32() +{ + struct filesystem fs; + fs.name = "fat32"; + fs.setup_mount = fat32_mount; + fs.sync = fat32_sync; + return register_filesystem(&fs); +} + +int fat32_mount(struct filesystem *fs, struct mount *mount) +{ + struct partition_t *partition; + struct fat_info_t *fat; + struct fat_dir_t *dir; + struct fat32_inode *data; + struct vnode *oldnode, *node; + struct fat_mount_t *newmount; + unsigned int lba; + unsigned char buf[BLOCK_SIZE]; + + // Read partition table from MBR, which is at first block + readblock(0, buf); + // MBR format + // 000 ~ 1BD: Code area + // 1BE ~ 1FD: Master Partition Table + // 1FE ~ 1FF: Boot Record Signature + + // https://lexra.pixnet.net/blog/post/303910876 + partition = (struct partition_t *)&buf[0x1be]; + // check Boot Record Signature, constants + if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) + { + return -1; + } + // https://en.wikipedia.org/wiki/Partition_type + if (partition[0].type != 0xb && partition[0].type != 0xc) // check Partition type, only FAT32 + { + return -1; + } + lba = partition[0].lba; + // uart_sendlinek("lba: 0x%x\n", lba); + + readblock(partition[0].lba, buf); + + node = kmalloc(sizeof(struct vnode)); + data = kmalloc(sizeof(struct fat32_inode)); + fat = kmalloc(sizeof(struct fat_info_t)); + dir = kmalloc(sizeof(struct fat_dir_t)); + newmount = kmalloc(sizeof(struct fat_mount_t)); + + memcpy((void *)&fat->bs, (void *)buf, sizeof(fat->bs)); + + // According to FAT fs design, FAT Region is after reserved sectors + // https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system + + fat->fat_lba = lba + fat->bs.reserved_sector_cnt; + fat->cluster_lba = fat->fat_lba + fat->bs.fat_cnt * fat->bs.sector_per_fat32; + // uart_sendlinek("fat->cluster_lba: 0x%x\n", fat->cluster_lba); + + INIT_LIST_HEAD(&dir->list); + + // oldnode should attach mount the fs + mount->root = kmalloc(sizeof(struct vnode)); + oldnode = mount->root; + oldnode->mount = 0; + oldnode->v_ops = &fat32_v_ops; + oldnode->f_ops = &fat32_f_ops; + oldnode->internal = data; + + data->boot_sector = kmalloc(sizeof(struct boot_sector_t)); + memcpy((void *)data->boot_sector, (void *)buf, sizeof(struct boot_sector_t)); + + struct boot_sector_t *bs = data->boot_sector; + int file_block = bs->hidden_sector_cnt + bs->reserved_sector_cnt + bs->fat_cnt * bs->sector_per_fat32; + + uart_sendlinek("bs->hidden_sector_cnt: 0x%x\n", bs->hidden_sector_cnt); + uart_sendlinek("bs->reserved_sector_cnt: 0x%x\n", bs->reserved_sector_cnt); + uart_sendlinek("bs->fat_cnt: 0x%x\n", bs->fat_cnt); + uart_sendlinek("bs->sector_per_fat32: 0x%x\n", bs->sector_per_fat32); + uart_sendlinek("file_block: 0x%x\n", file_block); + + readblock(fat->cluster_lba, buf); + + // char *n = buf; + struct sfn_file *file_ptr = buf; + + int high_block; + int low_block; + int block; + unsigned long file_size; + struct fat32_inode *tmp; + for (int i = 0; i < 16; i++) + { + high_block = file_ptr->first_cluster_high; + low_block = file_ptr->first_cluster_low; + block = fat->cluster_lba + high_block * 256 + low_block - 2; + file_size = file_ptr->file_size; + if (!file_size) + break; + uart_sendlinek("name : %s\n", file_ptr->name); + uart_sendlinek("high_block: %d\n", high_block); + uart_sendlinek("low_block: %d\n", low_block); + uart_sendlinek("file_size: 0x%x\n", file_size); + + data->entry[i] = kmalloc(sizeof(struct vnode)); + data->entry[i]->f_ops = &fat32_f_ops; + data->entry[i]->v_ops = &fat32_v_ops; + oldnode->internal = kmalloc(sizeof(struct fat32_inode)); + tmp = (struct fat32_inode *)oldnode->internal; + memcpy(tmp->Name, file_ptr->name, 11); + tmp->sfn = file_ptr; + tmp->datasize = ALIGN_UP(file_size, BLOCK_SIZE); + tmp->data = kmalloc(tmp->datasize); + for (int j = 0; j < tmp->datasize / BLOCK_SIZE; j++) + { + readblock(block + j, tmp->data + j * BLOCK_SIZE); + } + + file_ptr++; + } + + // oldnode = mount->root; + // uart_sendlinek("mount->root: 0x%x",oldnode); + + // node->mount = oldnode->mount; + // node->v_ops = oldnode->v_ops; + // node->f_ops = oldnode->f_ops; + // node->parent = oldnode->parent; + // node->internal = oldnode->internal; + + // data->node = node; + // data->fat = fat; + // data->cid = 2; // In FAT32, Root Directory Table starts from cluster #2 + // data->type = FAT_DIR; + // data->dir = dir; + + // oldnode->mount = mount; + // oldnode->v_ops = &fat32_v_ops; + // oldnode->f_ops = &fat32_f_ops; + // oldnode->internal = data; + + lock(); + list_add(&newmount->list, &mounts); + unlock(); + newmount->mount = mount; + + return 0; +} + +int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) + // return -1; + // if (!_lookup_cache(dir_node, target, component_name)) + // return 0; + // return _lookup_fat32(dir_node, target, component_name); + struct fat32_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < 16; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode) + break; + struct fat32_inode *inode = vnode->internal; + if (strcasecmp(component_name, inode->name) == 0) + { + *target = vnode; + return 0; + } + } + return -1; +} + +// ======================================================================================== + +// struct vnode_operations fat32_v_ops = { +// fat32_lookup, fat32_create, fat32_mkdir, +// fat32_isdir, fat32_getname, fat32_getsize, +// fat32_ls,fat32_dump}; + +// struct file_operations fat32_f_ops = { +// fat32_write, +// fat32_read, +// fat32_open, +// fat32_close, +// fat32_lseek64, +// }; + +// int register_fat32() +// { +// struct filesystem fs; +// fs.name = "fat32"; +// fs.setup_mount = fat32_mount; +// fs.sync = fat32_sync; +// INIT_LIST_HEAD(&mounts); +// return register_filesystem(&fs); +// } + +// /* filesystem methods */ +// int fat32_mount(struct filesystem *fs, struct mount *mount) +// { +// struct partition_t *partition; +// struct fat_info_t *fat; +// struct fat_dir_t *dir; +// struct fat32_inode *data; +// struct vnode *oldnode, *node; +// struct fat_mount_t *newmount; +// unsigned int lba; +// unsigned char buf[BLOCK_SIZE]; + +// // Read partition table from MBR, which is at first block +// readblock(0, buf); +// // MBR format +// // 000 ~ 1BD: Code area +// // 1BE ~ 1FD: Master Partition Table +// // 1FE ~ 1FF: Boot Record Signature + +// // https://lexra.pixnet.net/blog/post/303910876 +// partition = (struct partition_t *)&buf[0x1be]; +// // check Boot Record Signature, constants +// if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) +// { +// return -1; +// } +// // https://en.wikipedia.org/wiki/Partition_type +// if (partition[0].type != 0xb && partition[0].type != 0xc) // check Partition type, only FAT32 +// { +// return -1; +// } +// lba = partition[0].lba; +// readblock(partition[0].lba, buf); + +// node = kmalloc(sizeof(struct vnode)); +// data = kmalloc(sizeof(struct fat32_inode)); +// fat = kmalloc(sizeof(struct fat_info_t)); +// dir = kmalloc(sizeof(struct fat_dir_t)); +// newmount = kmalloc(sizeof(struct fat_mount_t)); + +// memcpy((void *)&fat->bs, (void *)buf, sizeof(fat->bs)); + +// // According to FAT fs design, FAT Region is after reserved sectors +// // https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system +// fat->fat_lba = lba + fat->bs.reserved_sector_cnt; +// fat->cluster_lba = fat->fat_lba + fat->bs.fat_cnt * fat->bs.sector_per_fat32; + +// INIT_LIST_HEAD(&dir->list); + +// // oldnode should attach mount the fs +// mount->root = kmalloc(sizeof(struct vnode)); +// oldnode = mount->root; +// oldnode->mount = 0; +// oldnode->v_ops = &fat32_v_ops; +// oldnode->f_ops = &fat32_f_ops; +// oldnode->internal = data; + +// data->node = oldnode; +// data->fat = fat; +// data->cid = 2; // In FAT32, Root Directory Table starts from cluster #2 +// data->type = FAT_DIR; +// data->dir = dir; + +// // oldnode = mount->root; +// // uart_sendlinek("mount->root: 0x%x",oldnode); + +// // node->mount = oldnode->mount; +// // node->v_ops = oldnode->v_ops; +// // node->f_ops = oldnode->f_ops; +// // node->parent = oldnode->parent; +// // node->internal = oldnode->internal; + +// // data->node = node; +// // data->fat = fat; +// // data->cid = 2; // In FAT32, Root Directory Table starts from cluster #2 +// // data->type = FAT_DIR; +// // data->dir = dir; + +// // oldnode->mount = mount; +// // oldnode->v_ops = &fat32_v_ops; +// // oldnode->f_ops = &fat32_f_ops; +// // oldnode->internal = data; + +// lock(); +// list_add(&newmount->list, &mounts); +// unlock(); +// newmount->mount = mount; + +// return 0; +// } + +// int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +// { +// if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) +// return -1; +// if (!_lookup_cache(dir_node, target, component_name)) +// return 0; +// return _lookup_fat32(dir_node, target, component_name); +// } + +int fat32_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) + return -1; + if (!fat32_lookup(dir_node, target, component_name)) + return -1; + *target = _create_vnode(dir_node, component_name, FAT_FILE, -1, 0); + return 0; +} + +int fat32_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) + return -1; + if (!fat32_lookup(dir_node, target, component_name)) + return -1; + *target = _create_vnode(dir_node, component_name, FAT_DIR, -1, 0); + return 0; +} + +int fat32_isdir(struct vnode *dir_node) +{ + return (((struct fat32_inode *)dir_node->internal)->type == FAT_DIR) ? 1 : 0; +} + +int fat32_getname(struct vnode *dir_node, const char **name) +{ + *name = ((struct fat32_inode *)dir_node->internal)->name; + return 0; +} + +int fat32_getsize(struct vnode *dir_node) +{ + if (((struct fat32_inode *)dir_node->internal)->type == FAT_DIR) + return 0; + return ((struct fat32_inode *)dir_node->internal)->file->size; +} + +/* file_operations methods */ +int fat32_write(struct file *file, const void *buf, size_t len) +{ + struct fat32_inode *data; + int filesize; + int ret; + + if (fat32_isdir(file->vnode)) + return -1; + if (!len) + return len; + + filesize = fat32_getsize(file->vnode); + data = file->vnode->internal; + + ret = _writefile(buf, data, file->f_pos, len); + if (ret <= 0) + { + return ret; + } + + file->f_pos += ret; + if (file->f_pos > filesize) + { + data->file->size = file->f_pos; + } + + return ret; +} + +int fat32_read(struct file *file, void *buf, size_t len) +{ + struct fat32_inode *data; + int filesize; + int ret; + + if (fat32_isdir(file->vnode)) + return -1; + + filesize = fat32_getsize(file->vnode); + data = file->vnode->internal; + + if (file->f_pos + len > filesize) + { + len = filesize - file->f_pos; + } + if (!len) + return len; + + ret = _readfile(buf, data, file->f_pos, len); + if (ret <= 0) + return ret; + + file->f_pos += ret; + return ret; +} + +int fat32_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_pos = 0; + (*target)->f_ops = file_node->f_ops; + return 0; +} + +int fat32_close(struct file *file) +{ + file->vnode = NULL; + file->f_pos = 0; + file->f_ops = NULL; + return 0; +} + +long fat32_lseek64(struct file *file, long offset, int whence) +{ + int filesize; + int base; + + if (!fat32_isdir(file->vnode)) + return -1; + + filesize = fat32_getsize(file->vnode); + + if (filesize < 0) + return -1; + + switch (whence) + { + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = file->f_pos; + break; + case SEEK_END: + base = filesize; + break; + default: + return -1; + } + + if (base + offset > filesize) + { + return -1; + } + file->f_pos = base + offset; + + return 0; +} + +int fat32_sync(struct filesystem *fs) +{ + struct fat_mount_t *entry; + + list_for_each_entry(entry, &mounts, list) + { + _sync_dir(entry->mount->root); + } + + return 0; +} + +// ------------------------------------------------------------------------ +// Utils +// + +unsigned int get_next_cluster(unsigned int fat_lba, unsigned int cluster_id) +{ + struct cluster_entry_t *ce; + unsigned int idx; + unsigned char buf[BLOCK_SIZE]; + + if (cluster_id >= INVALID_CID) + { + return cluster_id; + } + + // Get fat allocation table and its cluster entry in Data region + fat_lba += cluster_id / CLUSTER_ENTRY_PER_BLOCK; + idx = cluster_id % CLUSTER_ENTRY_PER_BLOCK; + + readblock(fat_lba, buf); + ce = &(((struct cluster_entry_t *)buf)[idx]); + + return ce->val; +} + +unsigned int alloc_cluster(struct fat_info_t *fat, unsigned int prev_cid) +{ + struct cluster_entry_t *ce; + unsigned int fat_lba; + unsigned int cid; + int found; + unsigned char buf[BLOCK_SIZE]; + + fat_lba = fat->fat_lba; + cid = 0; + // find unused cluster by clusters -> fats + while (fat_lba < fat->cluster_lba) + { + found = 0; + readblock(fat_lba, buf); + + for (int i = 0; i < CLUSTER_ENTRY_PER_BLOCK; ++i) + { + ce = &(((struct cluster_entry_t *)buf)[i]); + + if (!ce->val) + { + found = 1; + break; + } + + ++cid; + } + + if (found) + { + break; + } + fat_lba += 1; + } + // Only called if prev_cid is larger than 0xfffffff8, realloc ce; + if (found && prev_cid) + { + unsigned int target_lba; + unsigned int target_idx; + + target_lba = fat_lba + prev_cid / CLUSTER_ENTRY_PER_BLOCK; + target_idx = prev_cid % CLUSTER_ENTRY_PER_BLOCK; + readblock(target_lba, buf); + ce = &(((struct cluster_entry_t *)buf)[target_idx]); + ce->val = cid; + writeblock(target_lba, buf); + } + + if (!found) + { + uart_sendlinek("fat32 alloc_cluster: No space!"); + return -1; + } + + return cid; +} + +/* vnode_operations methods */ +struct vnode *_create_vnode(struct vnode *parent, const char *name, unsigned int type, unsigned int cid, unsigned int size) +{ + struct vnode *node; + struct fat32_inode *info, *data; + char *buf; + int len; + + info = parent->internal; + len = strlen(name); + + buf = kmalloc(len + 1); + node = kmalloc(sizeof(struct vnode)); + data = kmalloc(sizeof(struct fat32_inode)); + + strcpy(buf, name); + + data->name = buf; + data->node = node; + data->fat = info->fat; + data->cid = cid; + data->type = type; + + if (type == FAT_DIR) + { + struct fat_dir_t *dir; + dir = kmalloc(sizeof(struct fat_dir_t)); + INIT_LIST_HEAD(&dir->list); + data->dir = dir; + } + else + { + struct fat_file_t *file; + file = kmalloc(sizeof(struct fat_file_t)); + INIT_LIST_HEAD(&file->list); + file->size = size; + data->file = file; + } + + node->mount = parent->mount; + node->v_ops = &fat32_v_ops; + node->f_ops = &fat32_f_ops; + node->parent = parent; + node->internal = data; + + // attach to parent's dir_t or file_t list + list_add(&data->list, &info->dir->list); + + return node; +} + +int _lookup_cache(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct fat32_inode *data, *entry; + struct fat_dir_t *dir; + int found; + + data = dir_node->internal; + dir = data->dir; + found = 0; + + list_for_each_entry(entry, &dir->list, list) + { + if (!strcasecmp(component_name, entry->name)) + { // same as strcmp, but tr [:lower:] + found = 1; + break; + } + } + if (!found) + { + return -1; + } + *target = entry->node; + return 0; +} + +struct dir_t *__lookup_fat32(struct vnode *dir_node, const char *component_name, unsigned char *buf, int *buflba) +{ + struct dir_t *dir; + struct fat32_inode *data; + struct fat_info_t *fat; + struct filename_t name; + unsigned int cid; + int found, dirend, lfn; + + data = dir_node->internal; + fat = data->fat; + cid = data->cid; + + found = 0; + dirend = 0; + + memset(&name, 0, sizeof(struct filename_t)); + + while (1) + { + int lba; + + lba = fat->cluster_lba + (cid - 2) * fat->bs.sector_per_cluster; // cluster start from #2 + readblock(lba, buf); + + // only called by sync, to update buffer + if (buflba) + { + *buflba = lba; + } + + for (int i = 0; i < DIR_PER_BLOCK; ++i) + { + unsigned char len; + dir = (struct dir_t *)(&buf[sizeof(struct dir_t) * i]); + + if (dir->name[0] == 0) + { + dirend = 1; + break; + } + + if ((dir->attr & ATTR_LFN) == ATTR_LFN) + { + struct long_dir_t *ldir; + int n; + + lfn = 1; + + ldir = (struct long_dir_t *)dir; + n = (dir->name[0] & 0x3f) - 1; // every lfn entry has 16 bytes + // update filename_t if it is LFN + for (int i = 0; ldir->name1[i] != 0xff && i < 10; i += 2) + { + name.part[n].name[i / 2] = ldir->name1[i]; // LDIR_Name1 1th ~ 5th + } + for (int i = 0; ldir->name2[i] != 0xff && i < 12; i += 2) + { + name.part[n].name[5 + i / 2] = ldir->name2[i]; // LDIR_Name2 6th ~ 11th + } + for (int i = 0; ldir->name3[i] != 0xff && i < 4; i += 2) + { + name.part[n].name[11 + i / 2] = ldir->name3[i]; // LDIR_Name3 12th ~ 13th + } + + continue; + } + + if (lfn == 1) + { + if (!strcasecmp(component_name, (void *)name.fullname)) + { + found = 1; + break; + } + + lfn = 0; + memset(&name, 0, sizeof(struct filename_t)); + continue; + } + + lfn = 0; + len = 8; + + // SFN lookup + while (len) + { + if (dir->name[len - 1] != 0x20) + { + break; + } + len -= 1; + } + + memcpy((void *)name.fullname, (void *)dir->name, len); + name.fullname[len] = 0; + + len = 3; + + while (len) + { + if (dir->name[8 + len - 1] != 0x20) + { + break; + } + len -= 1; + } + + if (len >= 0) + { + strcat((void *)name.fullname, "."); + strncat((void *)name.fullname, (void *)&dir->name[8], len); + } + + if (!strcasecmp(component_name, (void *)name.fullname)) + { + found = 1; + break; + } + + memset(&name, 0, sizeof(struct filename_t)); + } + + if (found) + { + break; + } + + if (dirend) + { + break; + } + + // try next cluster and lookup again + cid = get_next_cluster(fat->fat_lba, cid); + + if (cid >= INVALID_CID) + { + break; + } + } + + if (!found) + { + return NULL; + } + + return dir; +} + +int _lookup_fat32(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct vnode *node; + struct dir_t *dir; + unsigned int type, cid; + unsigned char buf[BLOCK_SIZE]; + + dir = __lookup_fat32(dir_node, component_name, buf, NULL); + + if (!dir) + { + return -1; + } + + if (!(dir->attr & ATTR_FILE_DIR_MASK)) + { + return -1; + } + + cid = (dir->ch << 16) | dir->cl; // upper part of cluster id + lower part of cluster id + + if (dir->attr & ATTR_ARCHIVE) + { + type = FAT_FILE; + } + else + { + type = FAT_DIR; + } + + node = _create_vnode(dir_node, component_name, type, cid, dir->size); + + *target = node; + + return 0; +} + +int _writefile_seek_cache(struct fat32_inode *data, unsigned int foid, struct fat_file_block_t **block) +{ + struct fat_file_block_t *entry; + struct list_head *head; + + head = &data->file->list; + + if (list_empty(head)) + { + return -1; + } + + list_for_each_entry(entry, head, list) + { + *block = entry; + if (foid == entry->oid) + { // offset match + return 0; + } + } + + return -1; +} + +int _writefile_seek_fat32(struct fat32_inode *data, unsigned int foid, unsigned int fcid, struct fat_file_block_t **block) +{ + struct fat_info_t *info; + unsigned int curoid, curcid; + + info = data->fat; + + // block is from cache + if (*block) + { + curoid = (*block)->oid; + curcid = (*block)->cid; + + if (curoid == foid) + { // offset match + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + } + else + { + curoid = 0; + curcid = fcid; + } + + // create new block in cache + while (1) + { + struct fat_file_block_t *newblock; + + newblock = kmalloc(sizeof(struct fat_file_block_t)); + + newblock->oid = curoid; + newblock->cid = curcid; + newblock->bufIsUpdated = 0; + newblock->isDirty = 1; + + list_add_tail(&newblock->list, &data->file->list); + + *block = newblock; + + if (curoid == foid) + { + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + } +} + +int _writefile_cache(struct fat32_inode *data, unsigned long long bckoff, const unsigned char *buf, unsigned long long bufoff, unsigned int size, struct fat_file_block_t *block) +{ + int wsize; + + // if cache block is not updated yet, read from sdcard + if (!block->bufIsUpdated) + { + // read the data from sdcard + struct fat_info_t *info; + int lba; + + info = data->fat; + lba = info->cluster_lba + (block->cid - 2) * info->bs.sector_per_cluster; + + readblock(lba, block->buf); + + block->bufIsUpdated = 1; + } + + wsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + memcpy((void *)&block->buf[bckoff], (void *)&buf[bufoff], wsize); + + return wsize; +} + +int _writefile_fat32(struct fat32_inode *data, unsigned long long bckoff, const unsigned char *buf, unsigned int bufoff, unsigned int size, unsigned int oid, unsigned int cid) +{ + struct list_head *head; + struct fat_info_t *info; + struct fat_file_block_t *block; + int lba; + int wsize; + + head = &data->file->list; + info = data->fat; + + block = kmalloc(sizeof(struct fat_file_block_t)); + + wsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + // read the address from sdcard and write it + if (cid >= INVALID_CID) + { + memset(block->buf, 0, BLOCK_SIZE); + } + else + { + lba = info->cluster_lba + (cid - 2) * info->bs.sector_per_cluster; + + readblock(lba, block->buf); + } + + memcpy((void *)&block->buf[bckoff], (void *)&buf[bufoff], wsize); + + block->oid = oid; + block->cid = cid; + block->bufIsUpdated = 1; + block->isDirty = 1; // wait for sync + + list_add_tail(&block->list, head); + + return wsize; +} + +int _writefile(const void *buf, struct fat32_inode *data, unsigned long long fileoff, unsigned long long len) +{ + struct fat_file_block_t *block; + struct list_head *head; + unsigned int foid; // first block id + unsigned int coid; // current block id + unsigned int cid; // target cluster id + unsigned long long bufoff, result; + int ret; + + block = NULL; + head = &data->file->list; + foid = fileoff / BLOCK_SIZE; + coid = 0; + cid = data->cid; + bufoff = 0; + result = 0; + + // Seek, find the particular offset + ret = _writefile_seek_cache(data, foid, &block); + + if (ret < 0) + { + ret = _writefile_seek_fat32(data, foid, cid, &block); + } + + if (ret < 0) + { + return 0; + } + + // Write + while (len) + { + unsigned long long bckoff; + + bckoff = (fileoff + result) % BLOCK_SIZE; + + if (&block->list != head) + { + // cache has the blocks to be written + ret = _writefile_cache(data, bckoff, buf, bufoff, len, block); + cid = block->cid; + block = list_first_entry(&block->list, struct fat_file_block_t, list); + } + else + { + // Read block from sdcard, create cache, then write it + cid = get_next_cluster(data->fat->fat_lba, cid); + ret = _writefile_fat32(data, bckoff, buf, bufoff, len, coid, cid); + } + + if (ret < 0) + { + break; + } + + bufoff += ret; + result += ret; + coid += 1; + len -= ret; + } + + return result; +} + +int _readfile_seek_cache(struct fat32_inode *data, unsigned int foid, struct fat_file_block_t **block) +{ + struct fat_file_block_t *entry; + struct list_head *head; + + // check the cache list + head = &data->file->list; + + // if cache is none, return not found + if (list_empty(head)) + { + return -1; + } + + // find that offset in cache + list_for_each_entry(entry, head, list) + { + *block = entry; + if (foid == entry->oid) + { + return 0; + } + } + + return -1; +} + +int _readfile_seek_fat32(struct fat32_inode *data, unsigned int foid, unsigned int fcid, struct fat_file_block_t **block) +{ + struct fat_info_t *info; + unsigned int curoid, curcid; + + info = data->fat; + + // if block found in cache, change to that offset in fat32 + if (*block) + { + curoid = (*block)->oid; + curcid = (*block)->cid; + + if (curoid == foid) + { + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + + if (curcid >= INVALID_CID) + { + return -1; + } + } + else + { + curoid = 0; + curcid = fcid; + } + + while (1) + { + // add new offset block to cache + struct fat_file_block_t *newblock; + + newblock = kmalloc(sizeof(struct fat_file_block_t)); + + newblock->oid = curoid; + newblock->cid = curcid; + newblock->bufIsUpdated = 0; + newblock->isDirty = 0; + + list_add_tail(&newblock->list, &data->file->list); + + *block = newblock; + + if (curoid == foid) + { + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + + if (curcid >= INVALID_CID) + { + return -1; + } + } +} + +int _readfile_cache(struct fat32_inode *data, unsigned long long bckoff, unsigned char *buf, unsigned long long bufoff, unsigned int size, struct fat_file_block_t *block) +{ + int rsize; + + if (!block->bufIsUpdated) + { + // read the data from sdcard + struct fat_info_t *info; + int lba; + + info = data->fat; + lba = info->cluster_lba + (block->cid - 2) * info->bs.sector_per_cluster; + readblock(lba, block->buf); + + block->bufIsUpdated = 1; + } + + // if the size is overflow, resize it + rsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + + memcpy((void *)&buf[bufoff], (void *)&block->buf[bckoff], rsize); + + return rsize; +} + +int _readfile_fat32(struct fat32_inode *data, unsigned long long bckoff, unsigned char *buf, unsigned int bufoff, unsigned int size, unsigned int oid, unsigned int cid) +{ + struct list_head *head; + struct fat_info_t *info; + struct fat_file_block_t *block; + int lba; + int rsize; + + head = &data->file->list; + info = data->fat; + + block = kmalloc(sizeof(struct fat_file_block_t)); + + rsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + lba = info->cluster_lba + (cid - 2) * info->bs.sector_per_cluster; + + // read from fat32 + readblock(lba, block->buf); + + memcpy((void *)&buf[bufoff], (void *)&block->buf[bckoff], rsize); + + block->oid = oid; + block->cid = cid; + block->bufIsUpdated = 1; + + list_add_tail(&block->list, head); + + return rsize; +} + +int _readfile(void *buf, struct fat32_inode *data, unsigned long long fileoff, unsigned long long len) +{ + struct fat_file_block_t *block; + struct list_head *head; + unsigned int foid; // first block id + unsigned int coid; // current block id + unsigned int cid; // target cluster id + unsigned long long bufoff, result; + int ret; + + block = NULL; + head = &data->file->list; + foid = fileoff / BLOCK_SIZE; + coid = 0; + cid = data->cid; + bufoff = 0; + result = 0; + + // Seek, find the particular offset + ret = _readfile_seek_cache(data, foid, &block); + if (ret < 0) + { + ret = _readfile_seek_fat32(data, foid, cid, &block); + } + + if (ret < 0) + { + return 0; + } + + // Read + while (len) + { + unsigned long long bckoff; + + bckoff = (fileoff + result) % BLOCK_SIZE; + + if (&block->list != head) + { + // cache found + ret = _readfile_cache(data, bckoff, buf, bufoff, len, block); + + cid = block->cid; + block = list_first_entry(&block->list, struct fat_file_block_t, list); + } + else + { + // Read block from sdcard, create cache + cid = get_next_cluster(data->fat->fat_lba, cid); + + if (cid >= INVALID_CID) + { + break; + } + + ret = _readfile_fat32(data, bckoff, buf, bufoff, len, coid, cid); + } + + if (ret < 0) + { + break; + } + + bufoff += ret; + result += ret; + coid += 1; + len -= ret; + } + + return result; +} + +void _do_sync_dir(struct vnode *dirnode) +{ + struct fat32_inode *data, *entry; + struct list_head *head; + struct dir_t *dir; + struct long_dir_t *ldir; + unsigned int cid; + int lba, idx, lfnidx; + unsigned char buf[BLOCK_SIZE]; + + data = dirnode->internal; + head = &data->dir->list; + cid = data->cid; + idx = 0; + lfnidx = 1; + + if (cid >= INVALID_CID) + { + uart_sendlinek("fat32 _do_sync_dir: invalid dirnode->data->cid"); + } + + // get the buf from dirnode's offset + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + readblock(lba, buf); + + list_for_each_entry(entry, head, list) + { + struct dir_t *origindir; + const char *name; + const char *ext; + int lfn, namelen, extpos, i, buflba; + unsigned char lookupbuf[BLOCK_SIZE]; + + name = entry->name; + + // If entry is a old file, update its size + origindir = __lookup_fat32(dirnode, name, lookupbuf, &buflba); + + if (origindir) + { + if (entry->type == FAT_FILE) + { + origindir->size = entry->file->size; + writeblock(buflba, lookupbuf); + } + continue; + } + + // Else if entry is a new file + ext = NULL; + extpos = -1; + + do + { + + namelen = strlen(name); + if (namelen >= 13) + { + lfn = 1; + break; + } + for (i = 0; i < namelen; ++i) + { + // get idx of extension name + if (name[namelen - 1 - i] == '.') + { + break; + } + } + if (i < namelen) + { + ext = &name[namelen - i]; + extpos = namelen - 1 - i; + } + if (i >= 4) + { + lfn = 1; + break; + } + if (namelen - 1 - i > 8) + { + // SFN: 8.3 + lfn = 1; + break; + } + + lfn = 0; + } while (0); + + // Seek idx to the end of dir + while (1) + { + dir = (struct dir_t *)(&buf[sizeof(struct dir_t) * idx]); + + if (dir->name[0] == 0) + { + break; + } + + idx += 1; + + if (idx >= 16) + { // if idx is over Directory Entry, create a new one + unsigned int newcid; + + writeblock(lba, buf); + + newcid = get_next_cluster(data->fat->fat_lba, cid); + if (newcid >= INVALID_CID) + { + newcid = alloc_cluster(data->fat, cid); + } + + cid = newcid; + + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + + readblock(lba, buf); + idx = 0; + } + } + + // Write LFN + if (lfn) + { + int ord; + int first; + + ord = ((namelen - 1) / 13) + 1; + first = 0x40; // LAST_LONG_ENTRY flag in LFN LDIR_Ord + + for (; ord > 0; --ord) + { + int end; + ldir = (struct long_dir_t *)(&buf[sizeof(struct long_dir_t) * idx]); + + ldir->order = first | ord; + ldir->attr = ATTR_LFN; + ldir->type = 0; + // TODO: Calculate checksum, SFN + LFN + ldir->checksum = 0; + ldir->fstcluslo = 0; + + first = 0; + end = 0; + + for (i = 0; i < 10; i += 2) + { + if (end) + { // padding 0xff if filename end + ldir->name1[i] = 0xff; + ldir->name1[i + 1] = 0xff; + } + else + { + ldir->name1[i] = name[(ord - 1) * 13 + i / 2]; + ldir->name1[i + 1] = 0; + if (ldir->name1[i] == 0) + { + end = 1; + } + } + } + for (i = 0; i < 12; i += 2) + { + if (end) + { + ldir->name2[i] = 0xff; + ldir->name2[i + 1] = 0xff; + } + else + { + ldir->name2[i] = name[(ord - 1) * 13 + 5 + i / 2]; + ldir->name2[i + 1] = 0; + if (ldir->name2[i] == 0) + { + end = 1; + } + } + } + for (i = 0; i < 4; i += 2) + { + if (end) + { + ldir->name3[i] = 0xff; + ldir->name3[i + 1] = 0xff; + } + else + { + ldir->name3[i] = name[(ord - 1) * 13 + 11 + i / 2]; + ldir->name3[i + 1] = 0; + if (ldir->name3[i] == 0) + { + end = 1; + } + } + } + + idx += 1; + + if (idx >= 16) + { + unsigned int newcid; + + writeblock(lba, buf); + + newcid = get_next_cluster(data->fat->fat_lba, cid); + if (newcid >= INVALID_CID) + { + newcid = alloc_cluster(data->fat, cid); + } + + cid = newcid; + + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + readblock(lba, buf); + + idx = 0; + } + } + } + + // Write SFN + dir = (struct dir_t *)(&buf[sizeof(struct dir_t) * idx]); + + // TODO: Set these properties properly + dir->ntres = 0; + dir->crttimetenth = 0; + dir->crttime = 0; + dir->crtdate = 0; + dir->lstaccdate = 0; + dir->wrttime = 0; + dir->wrtdate = 0; + + if (entry->type == FAT_DIR) + { + dir->attr = ATTR_DIRECTORY; + dir->size = 0; + } + else + { + dir->attr = ATTR_ARCHIVE; + dir->size = entry->file->size; + } + + if (entry->cid >= INVALID_CID) + { + entry->cid = alloc_cluster(data->fat, 0); + } + + dir->ch = (entry->cid >> 16) & 0xffff; + dir->cl = entry->cid & 0xffff; + + if (lfn) + { + int lfni; + + // Creating SFN body + // TODO: handle lfnidx + for (i = 7, lfni = lfnidx; i >= 0 && lfni;) + { + dir->name[i--] = '0' + lfni % 10; + lfni /= 10; + } + + lfnidx++; + // numeric-tail: ~n (1 <= n <= 6) + dir->name[i--] = '~'; + + // TODO: handle letter case + memcpy((void *)dir->name, name, i + 1); + } + else + { + // TODO: handle letter case + for (i = 0; i != extpos && name[i]; ++i) + { + dir->name[i] = name[i]; + } + + for (; i < 8; ++i) + { + dir->name[i] = ' '; // in SFN, each part is padded with space + } + } + + // SFN format: 8.3 + // TODO: handle letter case + for (i = 0; i < 3 && ext[i]; ++i) + { + dir->name[8 + i] = ext[i]; + } + + for (; i < 3; ++i) + { + dir->name[8 + i] = ' '; + } + + idx += 1; + + if (idx >= 16) + { + int newcid; + + writeblock(lba, buf); + + newcid = get_next_cluster(data->fat->fat_lba, cid); + if (newcid >= INVALID_CID) + { + newcid = alloc_cluster(data->fat, cid); + } + + cid = newcid; + + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + + // TODO: Cache data block of directory + readblock(lba, buf); + + idx = 0; + } + } + + if (idx) + { + writeblock(lba, buf); + } +} + +void _do_sync_file(struct vnode *filenode) +{ + struct fat_file_block_t *entry; + struct fat32_inode *data; + struct list_head *head; + unsigned int cid; + + data = filenode->internal; + head = &data->file->list; + cid = data->cid; + + if (cid >= INVALID_CID) + { + // uart_sendlinek("fat32 _do_sync_file: invalid cid"); + return; + } + + list_for_each_entry(entry, head, list) + { + int lba; + + if (entry->oid == 0) + { + // cache is valid but not sync'd, just an informational warning + if (!(entry->cid >= INVALID_CID) && data->cid != entry->cid) + { + uart_sendlinek("_do_sync_file: cid isn't sync"); + } + + entry->cid = data->cid; + } + + if (entry->cid >= INVALID_CID) + { + entry->cid = alloc_cluster(data->fat, cid); + } + + // if the file is sync'd, pass + if (!entry->isDirty) + { + continue; + } + + // cache buf is not updated, initialize it to avoid error + if (!entry->bufIsUpdated) + { + memset(entry->buf, 0, BLOCK_SIZE); + entry->bufIsUpdated = 1; + } + + // sync to fat32 fs + lba = data->fat->cluster_lba + (entry->cid - 2) * data->fat->bs.sector_per_cluster; + writeblock(lba, entry->buf); + + entry->isDirty = 0; + + cid = entry->cid; + } +} + +void _sync_dir(struct vnode *dirnode) +{ + struct fat32_inode *data, *entry; + struct list_head *head; + + // Init: dirnode is mounted fs + data = dirnode->internal; + head = &data->dir->list; + + _do_sync_dir(dirnode); + + list_for_each_entry(entry, head, list) + { + if (entry->type == FAT_DIR) + { + _sync_dir(entry->node); // recursive to next dirnode + } + else + { + _do_sync_file(entry->node); + } + } +} + +int fat32_op_deny() +{ + return -1; +} + +void fat32_dump(struct vnode *vnode, int level) +{ +} + +void fat32_ls(struct vnode *vnode) +{ + uart_sendlinek("in fat32_ls\n"); + struct fat32_inode *inode = (struct fat32_inode *)vnode->internal; + int child_idx = 0; + for (; child_idx <= 16; child_idx++) + { + if (!inode->entry[child_idx]) + { + // break; + } + vnode = inode->entry[child_idx]; + uart_sendlinek("%s\n", ((struct fat32_inode *)vnode->internal)->name); + } +} \ No newline at end of file