From 52172a654ad1448d901b1b8d04be72da5e32d046 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Wed, 26 Jun 2024 02:15:07 +0800 Subject: [PATCH] finish lab5 --- lab5/.clang-format | 9 + lab5/.clangd | 7 + lab5/Makefile | 24 +++ lab5/README.md | 22 ++ lab5/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 34228 bytes lab5/bsp/base.h | 28 +++ lab5/bsp/entry.S | 114 +++++++++++ lab5/bsp/entry.h | 39 ++++ lab5/bsp/gpio.h | 25 +++ lab5/bsp/gpu.c | 29 +++ lab5/bsp/irq.h | 29 +++ lab5/bsp/linker.ld | 26 +++ lab5/bsp/mailbox.c | 75 +++++++ lab5/bsp/mailbox.h | 26 +++ lab5/bsp/ramfs.c | 95 +++++++++ lab5/bsp/ramfs.h | 40 ++++ lab5/bsp/reboot.c | 20 ++ lab5/bsp/reboot.h | 8 + lab5/bsp/sched.S | 26 +++ lab5/bsp/start.S | 63 ++++++ lab5/bsp/syscall.h | 22 ++ lab5/bsp/sysregs.h | 11 + lab5/bsp/timer.c | 41 ++++ lab5/bsp/traps.h | 41 ++++ lab5/bsp/uart.c | 158 +++++++++++++++ lab5/bsp/uart.h | 20 ++ lab5/bsp/utils.c | 57 ++++++ lab5/config.txt | 1 + lab5/file_sender.py | 25 +++ lab5/initramfs.cpio | Bin 0 -> 247808 bytes lab5/kernel/Makefile | 47 +++++ lab5/kernel/buddy.c | 269 +++++++++++++++++++++++++ lab5/kernel/command/async_io.c | 40 ++++ lab5/kernel/command/exec.c | 57 ++++++ lab5/kernel/command/hello.c | 8 + lab5/kernel/command/mailbox.c | 23 +++ lab5/kernel/command/memory.c | 73 +++++++ lab5/kernel/command/multi_thread.c | 29 +++ lab5/kernel/command/ramfs.c | 76 +++++++ lab5/kernel/command/reboot.c | 12 ++ lab5/kernel/command/set_timeout.c | 27 +++ lab5/kernel/commands.h | 24 +++ lab5/kernel/console.c | 110 ++++++++++ lab5/kernel/console.h | 13 ++ lab5/kernel/device_tree.c | 76 +++++++ lab5/kernel/device_tree.h | 31 +++ lab5/kernel/io.c | 37 ++++ lab5/kernel/io.h | 11 + lab5/kernel/irq_entry.c | 103 ++++++++++ lab5/kernel/irq_entry.h | 17 ++ lab5/kernel/kmalloc.c | 137 +++++++++++++ lab5/kernel/main.c | 45 +++++ lab5/kernel/memory.h | 20 ++ lab5/kernel/mm.h | 31 +++ lab5/kernel/sched.h | 66 ++++++ lab5/kernel/signal.c | 49 +++++ lab5/kernel/signal.h | 13 ++ lab5/kernel/simple.c | 30 +++ lab5/kernel/syscall.c | 175 ++++++++++++++++ lab5/kernel/task.c | 140 +++++++++++++ lab5/kernel/timer.c | 62 ++++++ lab5/kernel/timer.h | 11 + lab5/lib/limits.h | 7 + lab5/lib/sprintf.c | 147 ++++++++++++++ lab5/lib/stddef.h | 13 ++ lab5/lib/stdlib.c | 41 ++++ lab5/lib/stdlib.h | 7 + lab5/lib/string.c | 101 ++++++++++ lab5/lib/string.h | 14 ++ lab5/lib/utils.c | 3 + lab5/lib/utils.h | 9 + lab5/new_initramfs.cpio | Bin 0 -> 247296 bytes lab5/ori_initramfs.cpio | Bin 0 -> 1024 bytes lab5/rootfs/file1 | 1 + lab5/rootfs/file2.txt | 1 + lab5/user_program/Makefile | 18 ++ lab5/user_program/linker.ld | 0 lab5/user_program/test_svc_exception.S | 11 + 78 files changed, 3316 insertions(+) create mode 100644 lab5/.clang-format create mode 100644 lab5/.clangd create mode 100644 lab5/Makefile create mode 100644 lab5/README.md create mode 100644 lab5/bcm2710-rpi-3-b-plus.dtb create mode 100644 lab5/bsp/base.h create mode 100644 lab5/bsp/entry.S create mode 100644 lab5/bsp/entry.h create mode 100644 lab5/bsp/gpio.h create mode 100644 lab5/bsp/gpu.c create mode 100644 lab5/bsp/irq.h create mode 100644 lab5/bsp/linker.ld create mode 100644 lab5/bsp/mailbox.c create mode 100644 lab5/bsp/mailbox.h create mode 100644 lab5/bsp/ramfs.c create mode 100644 lab5/bsp/ramfs.h create mode 100644 lab5/bsp/reboot.c create mode 100644 lab5/bsp/reboot.h create mode 100644 lab5/bsp/sched.S create mode 100644 lab5/bsp/start.S create mode 100644 lab5/bsp/syscall.h create mode 100644 lab5/bsp/sysregs.h create mode 100644 lab5/bsp/timer.c create mode 100644 lab5/bsp/traps.h create mode 100644 lab5/bsp/uart.c create mode 100644 lab5/bsp/uart.h create mode 100644 lab5/bsp/utils.c create mode 100644 lab5/config.txt create mode 100644 lab5/file_sender.py create mode 100644 lab5/initramfs.cpio create mode 100644 lab5/kernel/Makefile create mode 100644 lab5/kernel/buddy.c create mode 100644 lab5/kernel/command/async_io.c create mode 100644 lab5/kernel/command/exec.c create mode 100644 lab5/kernel/command/hello.c create mode 100644 lab5/kernel/command/mailbox.c create mode 100644 lab5/kernel/command/memory.c create mode 100644 lab5/kernel/command/multi_thread.c create mode 100644 lab5/kernel/command/ramfs.c create mode 100644 lab5/kernel/command/reboot.c create mode 100644 lab5/kernel/command/set_timeout.c create mode 100644 lab5/kernel/commands.h create mode 100644 lab5/kernel/console.c create mode 100644 lab5/kernel/console.h create mode 100644 lab5/kernel/device_tree.c create mode 100644 lab5/kernel/device_tree.h create mode 100644 lab5/kernel/io.c create mode 100644 lab5/kernel/io.h create mode 100644 lab5/kernel/irq_entry.c create mode 100644 lab5/kernel/irq_entry.h create mode 100644 lab5/kernel/kmalloc.c create mode 100644 lab5/kernel/main.c create mode 100644 lab5/kernel/memory.h create mode 100644 lab5/kernel/mm.h create mode 100644 lab5/kernel/sched.h create mode 100644 lab5/kernel/signal.c create mode 100644 lab5/kernel/signal.h create mode 100644 lab5/kernel/simple.c create mode 100644 lab5/kernel/syscall.c create mode 100644 lab5/kernel/task.c create mode 100644 lab5/kernel/timer.c create mode 100644 lab5/kernel/timer.h create mode 100644 lab5/lib/limits.h create mode 100644 lab5/lib/sprintf.c create mode 100644 lab5/lib/stddef.h create mode 100644 lab5/lib/stdlib.c create mode 100644 lab5/lib/stdlib.h create mode 100644 lab5/lib/string.c create mode 100644 lab5/lib/string.h create mode 100644 lab5/lib/utils.c create mode 100644 lab5/lib/utils.h create mode 100644 lab5/new_initramfs.cpio create mode 100644 lab5/ori_initramfs.cpio create mode 100644 lab5/rootfs/file1 create mode 100644 lab5/rootfs/file2.txt create mode 100644 lab5/user_program/Makefile create mode 100644 lab5/user_program/linker.ld create mode 100644 lab5/user_program/test_svc_exception.S diff --git a/lab5/.clang-format b/lab5/.clang-format new file mode 100644 index 000000000..4113d14a2 --- /dev/null +++ b/lab5/.clang-format @@ -0,0 +1,9 @@ +UseTab: Never +TabWidth: 4 +IndentWidth: 4 +AccessModifierOffset: -4 + +BasedOnStyle: Google +IncludeBlocks: Regroup +SortIncludes: true + diff --git a/lab5/.clangd b/lab5/.clangd new file mode 100644 index 000000000..a26c9cf1d --- /dev/null +++ b/lab5/.clangd @@ -0,0 +1,7 @@ +CompileFlags: + Compiler: /usr/homebrew/bin/aarch64-linux-gnu-gcc + Add: [ + "-Wall", "-ffreestanding", "-nostdinc", "-nostdlib", "-nostartfiles", "-fno-stack-protector", "-g", "-mgeneral-regs-only", + "-I/Users/yjack/GitHub/osc2024/lab5", + ] + diff --git a/lab5/Makefile b/lab5/Makefile new file mode 100644 index 000000000..c5c3a50b8 --- /dev/null +++ b/lab5/Makefile @@ -0,0 +1,24 @@ +QEMU_OPTS = -initrd new_initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb +BUILD_DIR := $(CURDIR)/build + +.PHONY: all bootloader kernel run clean + +all: clean kernel run + +clean: + rm -rf $(BUILD_DIR) + +bootloader: clean + $(MAKE) -C bootloader BUILD_DIR=$(BUILD_DIR) LIB_PATH=$(CURDIR)/lib BSP_PATH=$(CURDIR)/bsp + +kernel: clean + $(MAKE) -C kernel BUILD_DIR=$(BUILD_DIR) LIB_PATH=$(CURDIR)/lib BSP_PATH=$(CURDIR)/bsp + +run: + qemu-system-aarch64 -M raspi3b -kernel $(BUILD_DIR)/kernel8.img -display none -serial null -serial stdio $(QEMU_OPTS) + +run-bootloader: + qemu-system-aarch64 -M raspi3b -kernel $(BUILD_DIR)/kernel8.img -display none -serial null -serial pty $(QEMU_OPTS) + +debug: clean kernel + qemu-system-aarch64 -M raspi3b -kernel $(BUILD_DIR)/kernel8.img -display none -serial null -serial stdio -s -S $(QEMU_OPTS) diff --git a/lab5/README.md b/lab5/README.md new file mode 100644 index 000000000..e575fa0f3 --- /dev/null +++ b/lab5/README.md @@ -0,0 +1,22 @@ +# OSC2023-Lab01 + +| Github Account | Student ID | +| -------------- | ---------- | +| visitorckw | 312551071 | + +## Requirements + +- a cross-compiler for aarch64 +- (optional) qemu-system-aarch64 + +## Build + +``` +make +``` + +## Test With QEMU + +``` +make run +``` diff --git a/lab5/bcm2710-rpi-3-b-plus.dtb b/lab5/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..c83b0817e2eb5295a3dfe6aafe3aa55d93e9785d GIT binary patch literal 34228 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 EL1 while using SP_EL0 + ventry invalid_exc_handler + ventry invalid_exc_handler + ventry invalid_exc_handler + ventry invalid_exc_handler + // EL1 -> EL1 while using SP_EL1 + ventry invalid_exc_handler + ventry irq_handler + ventry invalid_exc_handler + ventry invalid_exc_handler + // EL0 (AArch64) -> EL1 + ventry exception_handler + ventry irq_handler + ventry invalid_exc_handler + ventry invalid_exc_handler + // EL0 (AArch32) -> EL1 + ventry invalid_exc_handler + ventry invalid_exc_handler + ventry invalid_exc_handler + ventry invalid_exc_handler diff --git a/lab5/bsp/entry.h b/lab5/bsp/entry.h new file mode 100644 index 000000000..014f23be5 --- /dev/null +++ b/lab5/bsp/entry.h @@ -0,0 +1,39 @@ +#ifndef _ENTRY_H +#define _ENTRY_H + +#define S_FRAME_SIZE 272 // size of all saved registers, which is 16*17 from pt_regs +#define S_X0 0 // offset of x0 register in saved stack frame + +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +#define SYNC_ERROR 16 +#define SYSCALL_ERROR 17 + +#endif + +#ifndef __ASSEMBLER__ +#include + +void ret_from_fork(void); + +void from_el1_to_el0(uint64_t, uint64_t); + +#endif diff --git a/lab5/bsp/gpio.h b/lab5/bsp/gpio.h new file mode 100644 index 000000000..6c1c10e63 --- /dev/null +++ b/lab5/bsp/gpio.h @@ -0,0 +1,25 @@ +#ifndef _GPIO_H +#define _GPIO_H + +#define MMIO_BASE 0x3F000000 // BCM2837 ARM Peripherals Page 6 + +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE + 0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE + 0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE + 0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE + 0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE + 0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE + 0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE + 0x0020009C)) + +#endif diff --git a/lab5/bsp/gpu.c b/lab5/bsp/gpu.c new file mode 100644 index 000000000..3a5d0f85b --- /dev/null +++ b/lab5/bsp/gpu.c @@ -0,0 +1,29 @@ +#include "base.h" +/* Auxilary mini UART registers */ +#define AUX_ENABLE ((volatile unsigned int *)(MMIO_BASE + 0x00215004)) +#define AUX_MU_IO ((volatile unsigned int *)(MMIO_BASE + 0x00215040)) +#define AUX_MU_IER ((volatile unsigned int *)(MMIO_BASE + 0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int *)(MMIO_BASE + 0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int *)(MMIO_BASE + 0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int *)(MMIO_BASE + 0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int *)(MMIO_BASE + 0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int *)(MMIO_BASE + 0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int *)(MMIO_BASE + 0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int *)(MMIO_BASE + 0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int *)(MMIO_BASE + 0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int *)(MMIO_BASE + 0x00215068)) + +#include +#include +#include + +uint32_t is_aux_irq() { + // uart_puts("\nIRQ_PENDING_1: "); + // uart_hex((unsigned long)*IRQ_PENDING_1); + // uart_puts("\n"); + return (*IRQ_PENDING_1 & (1 << 29)); +} + +uint32_t is_uart_rx_irq() { return (*AUX_MU_IIR & 0x6) == (1 << 2); } + +uint32_t is_uart_tx_irq() { return (*AUX_MU_IIR & 0x6) == (1 << 1); } diff --git a/lab5/bsp/irq.h b/lab5/bsp/irq.h new file mode 100644 index 000000000..104eb15ef --- /dev/null +++ b/lab5/bsp/irq.h @@ -0,0 +1,29 @@ +#ifndef __IRQ_H__ +#define __IRQ_H__ + +#include + +// el1 IRQ can be disable to protect the critical sections +void enable_irq(); +void disable_irq(); + +// GPU IRQ handle is used to handle the GPU IRQ such as UART IRQ +void gpu_irq_handler(); + +// Core timer IRQ handle is used to handle the core timer IRQ +void core_timer_enable(); +void core_timer_disable(); +void core_timer_irq_handler(); +uint64_t timer_get_uptime(); + +// IRQ utils +uint32_t is_core_timer_irq(); +uint32_t is_gpu_irq(); + +uint32_t is_aux_irq(); +uint32_t is_uart_rx_irq(); +uint32_t is_uart_tx_irq(); + +const char *get_exception_message(unsigned long type); + +#endif diff --git a/lab5/bsp/linker.ld b/lab5/bsp/linker.ld new file mode 100644 index 000000000..54b6e7698 --- /dev/null +++ b/lab5/bsp/linker.ld @@ -0,0 +1,26 @@ + +SECTIONS +{ + . = 0x60000; + __loader_start = .; + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text) + } + .rodata :{ *(.rodata) } + .data : { *(.data) } + .bss : + { + __bss_start = .; + *(.bss) + *(COMMON) + __bss_end = .; + } + . = ALIGN(8); + __heap_start = .; + __loader_end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; +__loader_size = (__loader_end - __loader_start) >> 3; diff --git a/lab5/bsp/mailbox.c b/lab5/bsp/mailbox.c new file mode 100644 index 000000000..5b11bf7fd --- /dev/null +++ b/lab5/bsp/mailbox.c @@ -0,0 +1,75 @@ +#include "mailbox.h" +#include + +int mailbox_call(unsigned char ch, unsigned int *mailbox) { + // Write the data (shifted into the upper 28 bits) combined with + // the channel (in the lower four bits) to the write register. + unsigned int r = + (((unsigned long)mailbox) & ~0xf) | (ch & 0xf); // mail_ch_prop + // & ~0xf => only "and" upper 28 bit can be saved + // |8 => if upper 28 is 1 => save and ensure last 4 bit is 1 + + // Check if Mailbox 0 status register’s full flag is set. + while (*MAILBOX_STATUS & MAILBOX_FULL) { + asm volatile("nop"); + }; + + // If not, then you can write to Mailbox 1 Read/Write register. + *MAILBOX_WRITE = r; + + while (1) { + // Check if Mailbox 0 status register’s empty flag is set. + while (*MAILBOX_STATUS & MAILBOX_EMPTY) { + asm volatile("nop"); + }; + + // If not, then you can read from Mailbox 0 Read/Write register. + // Check if the value is the same as you wrote in step 1. + if (r == *MAILBOX_READ) { + return mailbox[1] == REQUEST_SUCCEED; + } + } + + return 0; +} + +unsigned int get_board_revision() { + unsigned int mailbox[7]; + mailbox[0] = 7 * 4; // buffer size in bytes + mailbox[1] = REQUEST_CODE; + // tags begin + mailbox[2] = GET_BOARD_REVISION; // tag identifier + mailbox[3] = 4; // maximum of request and response value buffer's length. + mailbox[4] = TAG_REQUEST_CODE; + mailbox[5] = 0; // value buffer + // tags end + mailbox[6] = END_TAG; + + // message passing procedure call, you should implement + // it following the 6 steps provided above. + mailbox_call(8, mailbox); + + return mailbox[5]; +} + +void get_memory_info(unsigned int *base_addr, unsigned int *size) { + unsigned int mailbox[8]; + mailbox[0] = 8 * 4; // buffer size in bytes + mailbox[1] = REQUEST_CODE; + // tags begin + mailbox[2] = GET_ARM_MEMORY; // tag identifier + mailbox[3] = 8; // maximum of request and response value buffer's length. + mailbox[4] = TAG_REQUEST_CODE; // tag code + mailbox[5] = 0; // base address + mailbox[6] = 0; // size in bytes + mailbox[7] = END_TAG; // end tag + + // message passing procedure call, you should implement + // it following the 6 steps provided above. + mailbox_call(8, mailbox); + + // ARM memory base address + *base_addr = mailbox[5]; + // ARM memory size + *size = mailbox[6]; +} diff --git a/lab5/bsp/mailbox.h b/lab5/bsp/mailbox.h new file mode 100644 index 000000000..077298dcb --- /dev/null +++ b/lab5/bsp/mailbox.h @@ -0,0 +1,26 @@ +#ifndef __PERIPHERALS_MAILBOX_H__ +#define __PERIPHERALS_MAILBOX_H__ + +#define MMIO_BASE 0x3F000000 // BCM2837 ARM Peripherals Page 6 +#define MAILBOX_BASE MMIO_BASE + 0xb880 + +#define MAILBOX_READ ((volatile unsigned int*)(MAILBOX_BASE )) // M0: CPU read from GPU +#define MAILBOX_STATUS ((volatile unsigned int*)(MAILBOX_BASE + 0x18)) // M0: Check GPU statue +#define MAILBOX_WRITE ((volatile unsigned int*)(MAILBOX_BASE + 0x20)) // M1: CPU write to GPU + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 + +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +int mailbox_call(unsigned char ch, unsigned int *mailbox); +unsigned int get_board_revision(); +void get_memory_info(unsigned int *base, unsigned int *size); + +#endif diff --git a/lab5/bsp/ramfs.c b/lab5/bsp/ramfs.c new file mode 100644 index 000000000..6d7651b7d --- /dev/null +++ b/lab5/bsp/ramfs.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +// #define CPIO_BASE_QEMU (0x8000000) +#define CPIO_BASE_RPI (0x20000000) +static void *ramfs_base = (void *)CPIO_BASE_RPI; // DEFAULT for RPI + +static int hextoi(char *s, int n); + +void init_ramfs_callback(void *addr) { + uart_puts("[INFO] Initrd is mounted at "); + uart_hex((uint64_t)addr); + uart_send('\n'); + ramfs_base = addr; +} + +static file_list_t file_list; + +file_list_t *ramfs_get_file_list() { + uint8_t *fptr = (uint8_t *)ramfs_base; // for 8 byte alignment + file_list.file_count = 0; // 重置檔案數量 + + while (strcmp((void *)fptr + sizeof(cpio_t), "TRAILER!!!") && + file_list.file_count < MAX_FILES) { + char *file_name = (void *)fptr + sizeof(cpio_t); + + cpio_t *header = (cpio_t *)fptr; + uint32_t namesize = hextoi(header->c_namesize, 8); + uint32_t filesize = hextoi(header->c_filesize, 8); + uint32_t copy_length = + namesize < MAX_FILENAME_LENGTH ? namesize : MAX_FILENAME_LENGTH - 1; + + memcpy(file_list.file_names[file_list.file_count], file_name, + copy_length); + file_list.file_names[file_list.file_count][copy_length] = '\0'; + file_list.file_sizes[file_list.file_count] = filesize; + file_list.file_count++; + + uint32_t headsize = align4(sizeof(cpio_t) + namesize); + uint32_t datasize = align4(filesize); + + fptr += headsize + datasize; + } + + return &file_list; +} + +void ramfs_get_file_contents(const char *file_name, char *file_buf) { + uint8_t *fptr = (uint8_t *)ramfs_base; // for 8 byte alignment + while (strcmp((void *)fptr + sizeof(cpio_t), "TRAILER!!!")) { + cpio_t *header = (cpio_t *)fptr; + uint32_t namesize = hextoi(header->c_namesize, 8); + uint32_t filesize = hextoi(header->c_filesize, 8); + + uint32_t headsize = align4(sizeof(cpio_t) + namesize); + uint32_t datasize = align4(filesize); + + if (strcmp((void *)fptr + sizeof(cpio_t), file_name) == 0) { + memcpy(file_buf, (void *)fptr + headsize, filesize); + } + + fptr += headsize + datasize; + } +} + +unsigned long ramfs_get_file_size(const char *file_name) { + uint8_t *fptr = (uint8_t *)ramfs_base; // for 8 byte alignment + while (strcmp((void *)fptr + sizeof(cpio_t), "TRAILER!!!")) { + cpio_t *header = (cpio_t *)fptr; + uint32_t namesize = hextoi(header->c_namesize, 8); + uint32_t filesize = hextoi(header->c_filesize, 8); + uint32_t headsize = align4(sizeof(cpio_t) + namesize); + uint32_t datasize = align4(filesize); + if (strcmp((void *)fptr + sizeof(cpio_t), file_name) == 0) { + return filesize; + } + fptr += headsize + datasize; + } + return 0; +} + +static int hextoi(char *s, int n) { + int r = 0; + while (n-- > 0) { + r = r << 4; + if (*s >= 'A') + r += *s++ - 'A' + 10; + else if (*s >= '0') + r += *s++ - '0'; + } + return r; +} diff --git a/lab5/bsp/ramfs.h b/lab5/bsp/ramfs.h new file mode 100644 index 000000000..71795e00a --- /dev/null +++ b/lab5/bsp/ramfs.h @@ -0,0 +1,40 @@ +#ifndef _RAMFS_H +#define _RAMFS_H + +#include +#include + +typedef struct cpio_newc_header { + char c_magic[6]; // The string 070701 for new ASCII + 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]; // must be 0 for FIFOs and directories + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; // count includes terminating NUL in pathname + char c_check[8]; // 0 for "new" portable format; for CRC format the sum of + // all the bytes in the file +} cpio_t; + +#define MAX_FILES 100 // 預設的最大檔案數量 +#define MAX_FILENAME_LENGTH 256 // 每個檔案名稱的最大長度 + +typedef struct { + char file_names[MAX_FILES][MAX_FILENAME_LENGTH]; + unsigned long file_sizes[MAX_FILES]; + int file_count; +} file_list_t; + +void init_ramfs_callback(void *base_addr); +file_list_t *ramfs_get_file_list(); +unsigned long ramfs_get_file_size(const char *file_name); +void ramfs_get_file_contents(const char *file_name, char *buffer); +void ramfs_execute_file(const char *file_name); + +#endif diff --git a/lab5/bsp/reboot.c b/lab5/bsp/reboot.c new file mode 100644 index 000000000..43b575bec --- /dev/null +++ b/lab5/bsp/reboot.c @@ -0,0 +1,20 @@ +#include + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value) { + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} diff --git a/lab5/bsp/reboot.h b/lab5/bsp/reboot.h new file mode 100644 index 000000000..8522085c8 --- /dev/null +++ b/lab5/bsp/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif diff --git a/lab5/bsp/sched.S b/lab5/bsp/sched.S new file mode 100644 index 000000000..323fe3998 --- /dev/null +++ b/lab5/bsp/sched.S @@ -0,0 +1,26 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret diff --git a/lab5/bsp/start.S b/lab5/bsp/start.S new file mode 100644 index 000000000..8ea5717eb --- /dev/null +++ b/lab5/bsp/start.S @@ -0,0 +1,63 @@ +.section ".data" +.global dtb_base +dtb_base: .word 0 + +.section ".text.boot" + +.global _start + +_start: + // store dtb base address from x0 to dtb_base + ldr x1, =dtb_base + str x0, [x1] + // read cpu id, stop slave cores + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, boot + // cpu id > 0, stop + +proc_hang: + wfe + b proc_hang + +boot: + // set exception levels to EL1, where the kernel will run + bl from_el2_to_el1 // from exception.S + // set exception vector table + bl set_exception_vector_table // from exception.S + +bss_init: + // cpu id == 0 + // set top of stack just before our code (stack grows to a lower address per AAPCS64) + ldr x1, =_start + mov sp, x1 + + // clear bss + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_clear: + cbz w2, jump_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_clear + + // jump to C code, should not return +jump_main: + bl main + // for failsafe, halt this core too + b proc_hang + + +set_exception_vector_table: + adr x0, exception_vector_table // get the base address of the exception vector table + msr vbar_el1, x0 // set the base address of the exception vector table + ret + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 // msr: Status Register <-- Register (page 1924) + mov x0, 0x3c5 // EL1h (SPSel = 1) with interrupt disabled (D = 1, A = 1, I = 1, F = 1, M[3:0] = 5) + msr spsr_el2, x0 // Save the current processor's state + msr elr_el2, lr // Save the exception return address + eret // return to EL1 (use eret to return from the exception) diff --git a/lab5/bsp/syscall.h b/lab5/bsp/syscall.h new file mode 100644 index 000000000..07dd70840 --- /dev/null +++ b/lab5/bsp/syscall.h @@ -0,0 +1,22 @@ + +#ifndef syscall_h +#define syscall_h + +#include + +#include + +int sys_getpid(); +size_t sys_uart_read(char *buf, size_t size); +size_t sys_uart_write(const char *buf, size_t size); +int sys_exec(const char *name, char *const argv[]); +int sys_fork(trap_frame *tf); +void sys_exit(); +int sys_mbox_call(unsigned char ch, unsigned int *mbox); +void sys_kill(int pid); +void sys_signal(int signum, void (*handler)()); +void sys_sigkill(int pid, int sig); +void sys_sigreturn(trap_frame *regs); +void run_fork_test(); + +#endif // syscall_h diff --git a/lab5/bsp/sysregs.h b/lab5/bsp/sysregs.h new file mode 100644 index 000000000..2e691f2c1 --- /dev/null +++ b/lab5/bsp/sysregs.h @@ -0,0 +1,11 @@ +#ifndef _SYSREGS_H +#define _SYSREGS_H + +// *************************************** +// ESR_EL1, Exception Syndrome Register (EL1). Page 2431 of AArch64-Reference-Manual. +// *************************************** + +#define ESR_ELx_EC_SHIFT 26 +#define ESR_ELx_EC_SVC64 0x15 + +#endif diff --git a/lab5/bsp/timer.c b/lab5/bsp/timer.c new file mode 100644 index 000000000..30ff1d0e4 --- /dev/null +++ b/lab5/bsp/timer.c @@ -0,0 +1,41 @@ +#include +#include + +void core_timer_enable() { + asm volatile( + "mov x0, 1;" + "msr cntp_ctl_el0, x0;" // Enable + "mrs x0, cntfrq_el0;" + // "lsr x0, x0, #5;" + "msr cntp_tval_el0, x0;" // Set expired time + "mov x0, 2;" + "ldr x1, =0x40000040;" // timer interrupt control register for core 0 + "str w0, [x1];"); // Unmask timer interrupt + + asm volatile( + "mrs x0, cntkctl_el1;" + "orr x0, x0, 1;" + "msr cntkctl_el1, x0;"); +} + +void core_timer_disable() { + asm volatile( + "mov x0, 0;" + "msr cntp_ctl_el0, x0;" // disable + ); +} + +uint64_t timer_get_uptime() { + uint64_t cntpct_el0 = 0; + uint64_t cntfrq_el0 = 0; + asm volatile("mrs %0, cntpct_el0" : "=r"(cntpct_el0)); + asm volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq_el0)); + return cntpct_el0 / cntfrq_el0; +} + +void enable_user_to_physical_timer() { + asm volatile( + "mrs x0, cntkctl_el1;" + "orr x0, x0, 1;" + "msr cntkctl_el1, x0;"); +} diff --git a/lab5/bsp/traps.h b/lab5/bsp/traps.h new file mode 100644 index 000000000..0b6650e75 --- /dev/null +++ b/lab5/bsp/traps.h @@ -0,0 +1,41 @@ +#ifndef TRAPS_H +#define TRAPS_H + +typedef struct trap_frame { + 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; +} trap_frame; + +#endif diff --git a/lab5/bsp/uart.c b/lab5/bsp/uart.c new file mode 100644 index 000000000..be900e4bf --- /dev/null +++ b/lab5/bsp/uart.c @@ -0,0 +1,158 @@ +#include +#include + +#include "base.h" // for enable uart irq +#include "gpio.h" + +#define AUX_ENABLE ((volatile unsigned int *)(MMIO_BASE + 0x00215004)) +#define AUX_MU_IO ((volatile unsigned int *)(MMIO_BASE + 0x00215040)) +#define AUX_MU_IER ((volatile unsigned int *)(MMIO_BASE + 0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int *)(MMIO_BASE + 0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int *)(MMIO_BASE + 0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int *)(MMIO_BASE + 0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int *)(MMIO_BASE + 0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int *)(MMIO_BASE + 0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int *)(MMIO_BASE + 0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int *)(MMIO_BASE + 0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int *)(MMIO_BASE + 0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int *)(MMIO_BASE + 0x00215068)) + +void uart_init() { + register unsigned int r; + + /* initialize UART */ + *AUX_ENABLE |= 1; // Enable mini UART0x01 + *AUX_MU_CNTL = 0; // Disable TX, RX during configuration + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // Set baud rate to 115200 + *AUX_MU_IIR = 6; // No FIFO + + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 innitial + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx // bit1:transmitter bit0:receiver + + // *ENABLE_IRQS_1 |= (1 << 29); +} + +void uart_send(unsigned int c) { + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); // This bit is set if the transmit FIFO + // can accept at least one byte. + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +char uart_recv() { + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // This bit is set if the receive FIFO + // holds at least 1 symbol. + return (char)(*AUX_MU_IO); +} + +char uart_getc() { + char r; + do { + asm volatile("nop"); + } while (!( + *AUX_MU_LSR & + 0x01)); // This bit is set if the receive FIFO holds at least 1 symbol. + + r = (char)(*AUX_MU_IO); + // convert carrige return to newline + return r == '\r' ? '\n' : r; +} + +void uart_puts(const char *s) { + while (*s) { + /* convert newline to carrige return + newline */ + if (*s == '\n') uart_send('\r'); + uart_send(*s++); + } +} + +void uart_hex(unsigned long long int d) { + uart_puts("0x"); + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_enable_tx_interrupt() { + *AUX_MU_IER |= (1 << 1); +} + +void uart_disable_tx_interrupt() { + *AUX_MU_IER &= ~(1 << 1); +} + +void uart_enable_rx_interrupt() { + *AUX_MU_IER |= (1 << 0); +} + +void uart_disable_rx_interrupt() { + *AUX_MU_IER &= ~(1 << 0); +} + +#define UART_BUF_SIZE 2048 +static char write_buffer[UART_BUF_SIZE]; +static char read_buffer[UART_BUF_SIZE]; +static int write_idx = 0, write_end = 0; +static int read_idx = 0; + +void uart_rx_irq_handler() { + char c = (char)(*AUX_MU_IO); + read_buffer[read_idx++] = c == '\r' ? '\n' : c; + read_buffer[read_idx] = '\0'; + if (read_idx >= UART_BUF_SIZE) read_idx = 0; + uart_enable_rx_interrupt(); +} + +void uart_tx_irq_handler() { + uart_disable_tx_interrupt(); + if (write_idx != write_end && write_buffer[write_idx] != '\0') { + *AUX_MU_IO = write_buffer[write_idx]; + write_idx = (write_idx + 1) % UART_BUF_SIZE; + uart_enable_tx_interrupt(); + } +} + +void uart_async_read(char *buf) { + uart_disable_rx_interrupt(); + strncpy(buf, read_buffer, read_idx); + buf[read_idx] = '\0'; + read_idx = 0; +} + +void uart_async_write(const char *s) { + // append string to write buffer + while (*s) { + write_buffer[write_end] = *s++; + write_end = (write_end + 1) % UART_BUF_SIZE; + } + write_buffer[write_end] = '\0'; + uart_enable_tx_interrupt(); +} diff --git a/lab5/bsp/uart.h b/lab5/bsp/uart.h new file mode 100644 index 000000000..4e7813532 --- /dev/null +++ b/lab5/bsp/uart.h @@ -0,0 +1,20 @@ +#ifndef _UART_H +#define _UART_H + +void uart_init(); +void uart_send(unsigned int c); +char uart_getc(); +char uart_recv(); +void uart_puts(const char *s); +void uart_hex(unsigned long long d); + +void uart_enable_tx_interrupt(); +void uart_disable_tx_interrupt(); +void uart_enable_rx_interrupt(); +void uart_disable_rx_interrupt(); +void uart_tx_irq_handler(); +void uart_rx_irq_handler(); +void uart_async_read(char *buf); +void uart_async_write(const char *s); + +#endif diff --git a/lab5/bsp/utils.c b/lab5/bsp/utils.c new file mode 100644 index 000000000..6e2ce3d26 --- /dev/null +++ b/lab5/bsp/utils.c @@ -0,0 +1,57 @@ +#include +#include + +#include "base.h" + +uint32_t get_irq() { return *CORE0_IRQ_SOURCE; } + +uint32_t is_core_timer_irq() { + // uart_puts("\nCORE0_IRQ_SOURCE: "); + // uart_hex((unsigned long)*CORE0_IRQ_SOURCE); + // uart_puts("\n"); + // uart_puts("Timer IRQ: "); + // uart_hex((unsigned long)(1 << 1)); + // uart_puts("\n"); + return *CORE0_IRQ_SOURCE == (1 << 1); +} + +uint32_t is_gpu_irq() { + // uart_puts("\nCORE0_IRQ_SOURCE: "); + // uart_hex((unsigned long)*CORE0_IRQ_SOURCE); + // uart_puts("\n"); + // uart_puts("GPU IRQ: "); + // uart_hex((unsigned long)(1 << 8)); + // uart_puts("\n"); + return *CORE0_IRQ_SOURCE == (1 << 8); +} + +static const char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", "IRQ_INVALID_EL1t", "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", "IRQ_INVALID_EL1h", "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", "IRQ_INVALID_EL0_64", "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", "IRQ_INVALID_EL0_32", "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32", "SYNC_ERROR", "SYSCALL_ERROR"}; + +const char *get_exception_message(unsigned long type) { + return entry_error_messages[type]; +} + +void enable_irq() { + // Clear the D, A, I, F bits in the DAIF register + // to enable interrupts + // uart_puts("\n[enable_irq]\n"); + asm volatile("msr daifclr, 0xf;"); +} + +void disable_irq() { + // Set 1 to the D, A, I, F bits in the DAIF register + // to disable interrupts + // uart_puts("\n[disable_irq]\n"); + asm volatile("msr daifset, 0xf;"); +} diff --git a/lab5/config.txt b/lab5/config.txt new file mode 100644 index 000000000..80c90a4ec --- /dev/null +++ b/lab5/config.txt @@ -0,0 +1 @@ +initramfs initramfs.cpio 0x20000000 diff --git a/lab5/file_sender.py b/lab5/file_sender.py new file mode 100644 index 000000000..39d7e57bc --- /dev/null +++ b/lab5/file_sender.py @@ -0,0 +1,25 @@ +import serial +import time +import sys + +kernel_image_path = "build/kernel8.img" + +dev_name = sys.argv[1] +ser = serial.Serial(dev_name, 115200, timeout=1) + +input("Press any button to continue...") + +with open(kernel_image_path, "rb") as f: + kernel_image = f.read() + +kernel_size = len(kernel_image) +print(f"Sending kernel size: {kernel_size} bytes") +ser.write(f"{kernel_size}\n".encode()) + +time.sleep(1) + +print("Sending kernel image...") +ser.write(kernel_image) + +ser.close() +print("Kernel image sent.") diff --git a/lab5/initramfs.cpio b/lab5/initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..d5e4342d804c56e1ce6f1cf0252f7611a1e03124 GIT binary patch literal 247808 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 literal 0 HcmV?d00001 diff --git a/lab5/kernel/Makefile b/lab5/kernel/Makefile new file mode 100644 index 000000000..c14b15e16 --- /dev/null +++ b/lab5/kernel/Makefile @@ -0,0 +1,47 @@ +CFLAGS = -Wall -ffreestanding -nostdinc -nostdlib -nostartfiles -fno-stack-protector -g -mgeneral-regs-only +C = aarch64-linux-gnu + +LIB_PATH ?= ../lib +BSP_PATH ?= ../bsp +BUILD_DIR ?= build +CFLAGS += -I$(LIB_PATH) -I$(BSP_PATH) -I.. + +LIB_SRCS = $(shell find $(LIB_PATH) -type f -name '*.c') +BSP_SRCS = $(shell find $(BSP_PATH) -type f -name '*.c') +BSP_ASM_SRCS = $(shell find $(BSP_PATH) -type f -name '*.S') + +LOCAL_SRCS = $(shell find . -type f -name '*.c') +SRCS = $(LOCAL_SRCS) $(LIB_SRCS) $(BSP_SRCS) +OBJS = $(patsubst ./%.c,$(BUILD_DIR)/obj/%.o,$(LOCAL_SRCS)) \ + $(patsubst $(LIB_PATH)/%.c,$(BUILD_DIR)/obj/lib/%.o,$(LIB_SRCS)) \ + $(patsubst $(BSP_PATH)/%.c,$(BUILD_DIR)/obj/bsp/%.o,$(BSP_SRCS)) \ + $(patsubst $(BSP_PATH)/%.S,$(BUILD_DIR)/obj/bsp/%_s.o,$(BSP_ASM_SRCS)) + +QEMU_OPTS ?= + +ifeq ($(DEBUG), 1) +CFLAGS += -DDEBUG +endif + +all: kernel8.img + +$(BUILD_DIR)/obj/%.o: %.c + mkdir -p $(dir $@) + $(C)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/obj/lib/%.o: $(LIB_PATH)/%.c + mkdir -p $(dir $@) + $(C)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/obj/bsp/%.o: $(BSP_PATH)/%.c + mkdir -p $(dir $@) + $(C)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/obj/bsp/%_s.o: $(BSP_PATH)/%.S + mkdir -p $(dir $@) + $(C)-gcc $(CFLAGS) -c $< -o $@ + +kernel8.img: $(OBJS) + $(C)-ld -nostdlib $^ -T $(BSP_PATH)/linker.ld -o $(BUILD_DIR)/kernel8.elf + $(C)-objcopy -O binary $(BUILD_DIR)/kernel8.elf $(BUILD_DIR)/kernel8.img + diff --git a/lab5/kernel/buddy.c b/lab5/kernel/buddy.c new file mode 100644 index 000000000..47f435cfb --- /dev/null +++ b/lab5/kernel/buddy.c @@ -0,0 +1,269 @@ +#include +#include + +#include "mm.h" + +static page_t *mem_map; +static page_t *free_areas[MAX_ORDER + 1]; + +static inline void __push_page(page_t **head, page_t *page) { + if (*head == NULL) { + *head = page; + return; + } + + page->next = *head; + (*head)->prev = page; + *head = page; +} + +static inline page_t *__pop_page(page_t **head) { + if (*head == NULL) { + return NULL; + } + page_t *page = *head; + + *head = page->next; + page->next = NULL; + + if (*head) { // unlink the head + (*head)->prev = NULL; + } + return page; +} + +static inline void __remove_page(page_t **head, page_t *page) { + if (page->prev) { + page->prev->next = page->next; + } else { + *head = page->next; + } + + if (page->next) { + page->next->prev = page->prev; + } +} + +void buddy_init(phys_addr_t start, phys_addr_t end) { + uintptr_t buddy_size = (uintptr_t)end - (uintptr_t)start; + +#ifdef MEM_DEBUG + print_string("Buddy size: "); + print_d(buddy_size); + print_string("\nFrom "); + print_h((uint64_t)start); + print_string(" to "); + print_h((uint64_t)end); + print_string("\n"); +#endif + + // split the memory into max order pages and add them to free areas + for (int i = 0; i <= MAX_ORDER; i++) { + free_areas[i] = NULL; + } + + int num_of_pages = buddy_size / PAGE_SIZE; + +#ifdef MEM_DEBUG + print_string("Number of pages: "); + print_d(num_of_pages); + print_string("\n"); +#endif + + mem_map = simple_malloc(num_of_pages * sizeof(page_t)); + + if (mem_map == NULL) { + print_string("Failed to allocate memory for mem_map\n"); + return; + } + + for (int i = num_of_pages - 1; i >= 0; i--) { + mem_map[i].order = MAX_ORDER; + mem_map[i].status = PAGE_FREE; + mem_map[i].prev = NULL; + mem_map[i].next = NULL; + + if (i % (1 << MAX_ORDER) == + 0) { // push the chunk of page to the free area + __push_page(&free_areas[MAX_ORDER], &mem_map[i]); + } + } + +#ifdef MEM_DEBUG + print_free_areas(); +#endif +} + +page_t *alloc_pages(unsigned long order) { + unsigned long cur_order; + for (cur_order = order; cur_order <= MAX_ORDER; cur_order++) { + if (free_areas[cur_order]) { + page_t *page = __pop_page(&free_areas[cur_order]); + + // split the page into smaller pages + while (cur_order > order) { + cur_order--; + uint32_t buddy_pfn = (page - mem_map) ^ (1 << cur_order); + page_t *buddy = &mem_map[buddy_pfn]; + buddy->order = cur_order; + __push_page(&free_areas[cur_order], buddy); + } + + page->order = order; + page->status = PAGE_ALLOCATED; + return page; + } + } + return NULL; // 沒有可用記憶體 +} + +void free_pages(page_t *page, unsigned long order) { + int cur_order = order; + for (; cur_order < MAX_ORDER; cur_order++) { + uint32_t buddy_pfn = (page - mem_map) ^ (1 << cur_order); + page_t *buddy = &mem_map[buddy_pfn]; + + // print_string("\ni: "); + // print_d(cur_order); + // print_string("----------\n"); + // print_string("Page: "); + // print_h((uint64_t)get_addr_by_page(page)); + // print_string("\nBuddy: "); + // print_h((uint64_t)get_addr_by_page(buddy)); + // print_string("\nbuddy_order: "); + // print_d(buddy->order); + // print_string("\nbuddy_status: "); + // print_d(buddy->status); + + if (buddy->order != cur_order || buddy->status != PAGE_FREE) { + break; + } + + __remove_page(&free_areas[cur_order], buddy); + page = (page < buddy) + ? page + : buddy; // update the page pointer to the lower address + } + + page->status = PAGE_FREE; + page->order = cur_order; + // print_string("\nPush page: "); + // print_h((uint64_t)get_addr_by_page(page)); + // print_string("\nTo order "); + // print_d(cur_order); + // print_string("\n"); + __push_page(&free_areas[cur_order], page); +} + +void print_free_areas() { + for (int i = 0; i <= MAX_ORDER; i++) { + page_t *page = free_areas[i]; + print_string("Order "); + print_d(i); + print_string(": "); + while (page) { + print_h((uint64_t)get_addr_by_page(page)); + print_string(" -> "); + page = page->next; + } + print_string("NULL\n"); + } +} + +phys_addr_t get_addr_by_page(page_t *page) { + // print_string("Get addr by page: "); + // print_h((uint64_t)(((page - mem_map) << PAGE_SHIFT) + (void *)start)); + // print_string("\n"); + return (phys_addr_t)((page - mem_map) << PAGE_SHIFT); +} + +page_t *get_page_by_addr(phys_addr_t addr) { + // print_string("Get page by addr: "); + // print_h((uint64_t)&mem_map[(addr - start) >> PAGE_SHIFT]); + return &mem_map[(uint64_t)(void *)addr >> PAGE_SHIFT]; +} + +void memory_reserve(phys_addr_t start, phys_addr_t end) { + print_string("Reserve memory: "); + print_h((uint64_t)start); + print_string(" - "); + print_h((uint64_t)end); + print_string("\n"); + + for (int cur_order = MAX_ORDER; cur_order >= 0; cur_order--) { + page_t *page = free_areas[cur_order]; + while (page != NULL) { + page_t *next_page = page->next; + + phys_addr_t page_addr = get_addr_by_page(page); + phys_addr_t page_end = + page_addr + (1 << (cur_order + PAGE_SHIFT)) - 1; + + if (cur_order == 0) { + if (!(page_end < start || page_addr > end)) { +#ifdef MEM_DEBUG + print_string("\nPage partially reserved\n"); +#endif + page->status = PAGE_RESERVED; + __remove_page(&free_areas[cur_order], page); + } + } else if (page_addr >= start && page_end <= end) { +#ifdef MEM_DEBUG + print_string("\nPage fully reserved\n"); + print_h((uint64_t)page_addr); + print_string(" - "); + print_h((uint64_t)page_end); + print_string("\n"); +#endif + + page->status = PAGE_RESERVED; + __remove_page(&free_areas[cur_order], page); + } else if (start > page_end || + end < page_addr) { // page out of range + // print_string("\nPage out of range\n"); + } else { // split the overlapping page using buddy + // print_string("\nFind buddy\n"); + // print_string("\nPage: "); + // print_h((uint64_t)page_addr); + // print_string("\n"); + // print_string("\nCur order: "); + // print_d(cur_order); + // print_string("\n"); + + uint32_t buddy_pfn = (page - mem_map) ^ (1 << (cur_order - 1)); + page_t *buddy = &mem_map[buddy_pfn]; + +#ifdef MEM_DEBUG + print_string("Split page: "); + print_h((uint64_t)page_addr); + print_string(", "); + print_h((uint64_t)get_addr_by_page(buddy)); + print_string(", "); + print_h((uint64_t)buddy_pfn); + print_string("\n"); +#endif + +// remove the original page from the free area +#ifdef MEM_DEBUG + print_string("\nRemove page "); + print_h((uint64_t)get_addr_by_page(page)); + print_string(" from order "); + print_d(cur_order); + print_string("\n"); +#endif + __remove_page(&free_areas[cur_order], page); + buddy->order = cur_order - 1; + page->order = cur_order - 1; + page->prev = NULL; + // push split page to the free area + __push_page(&free_areas[cur_order - 1], buddy); + __push_page(&free_areas[cur_order - 1], page); + } + + // print_string("\nNext page\n"); + // print_h((uint64_t)get_addr_by_page(next_page)); + + page = next_page; + } + } +} diff --git a/lab5/kernel/command/async_io.c b/lab5/kernel/command/async_io.c new file mode 100644 index 000000000..2161f993d --- /dev/null +++ b/lab5/kernel/command/async_io.c @@ -0,0 +1,40 @@ +#include +#include +#include + +#include "kernel/io.h" + +void _async_io_demo(int argc, char **argv) { + uart_enable_rx_interrupt(); + char command[256]; + + while (1) { + uart_async_write("\r\n(async) > "); + + while (command[strlen(command) - 1] != + '\n') { // wait for enter and keep reading from buffer + for (int i = 0; i < 2000000; i++) { + // delay + } + int current_len = strlen(command); + uart_async_read(command + current_len); + uart_async_write(command + current_len); + uart_enable_rx_interrupt(); + } + command[strlen(command) - 1] = '\0'; // delete last \n + if (strcmp(command, "exit") == 0) { + break; + } + command[0] = '\0'; + } + + uart_disable_tx_interrupt(); + + // TODO: find out why I should clear the buffer... + uart_async_read(command); +} + +command_t async_io_demo_command = { + .name = "async_io", + .description = "demo async IO functions", + .function = &_async_io_demo}; diff --git a/lab5/kernel/command/exec.c b/lab5/kernel/command/exec.c new file mode 100644 index 000000000..095eb537b --- /dev/null +++ b/lab5/kernel/command/exec.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void _exec_command(int argc, char **argv) { + if (argc < 2) { + print_string("\nUsage: exec "); + return; + } + + char *file_name = argv[1]; + unsigned long file_size = ramfs_get_file_size(file_name); + char *file_contents = kmalloc(file_size); + ramfs_get_file_contents(file_name, file_contents); + + if (file_contents == NULL) { + print_string("[ERROR] File not found: "); + print_string(file_name); + print_string("\n"); + return; + } + print_string("\n[INFO] Executing file: "); + print_string(file_name); + print_string("\n"); + + char *user_program = kmalloc(file_size); + memcpy(user_program, file_contents, file_size); + enable_user_to_physical_timer(); + struct task_struct *task = kthread_create((void *)user_program); + asm volatile("msr tpidr_el1, %0;" ::"r"(task)); + asm volatile("msr spsr_el1, %0" ::"r"(0x0)); + asm volatile("msr elr_el1, %0" ::"r"(task->context.lr)); + asm volatile("msr sp_el0, %0" ::"r"(task->user_stack + STACK_SIZE)); + asm volatile("mov sp, %0" ::"r"(task->stack + STACK_SIZE)); + asm volatile("eret"); + + // char buf[100]; + // sprintf(buf, "User program pointer: %x\n", user_program); + // print_string(buf); + + // preempt_disable(); + // get_current()->state = TASK_STOPPED; + // print_string("Enabling user to physical timer\n"); + // enable_user_to_physical_timer(); + // print_string("Copying process\n"); + // copy_process(PF_KTHREAD, (unsigned long)&move_to_user_mode, (unsigned + // long)user_program, 0); preempt_enable(); +} + +command_t exec_command = {.name = "exec", + .description = "execute a user program", + .function = &_exec_command}; diff --git a/lab5/kernel/command/hello.c b/lab5/kernel/command/hello.c new file mode 100644 index 000000000..165d9d19a --- /dev/null +++ b/lab5/kernel/command/hello.c @@ -0,0 +1,8 @@ +#include +#include + +void _hello_command(int argc, char **argv) { print_string("\nHello World!"); } + +command_t hello_command = {.name = "hello", + .description = "print Hello World!", + .function = &_hello_command}; diff --git a/lab5/kernel/command/mailbox.c b/lab5/kernel/command/mailbox.c new file mode 100644 index 000000000..309ce0c68 --- /dev/null +++ b/lab5/kernel/command/mailbox.c @@ -0,0 +1,23 @@ +#include +#include +#include + +void _mailbox_command(int argc, char **argv) { + print_string("\nMailbox info :\n"); + unsigned int r = get_board_revision(); + print_string("board revision : "); + print_h(r); + print_string("\r\n"); + unsigned int base_addr, size; + get_memory_info(&base_addr, &size); + print_string("ARM memory base address : "); + print_h(base_addr); + print_string("\r\n"); + print_string("ARM memory size : "); + print_h(size); + print_string("\r\n"); +} + +command_t mailbox_command = {.name = "mailbox", + .description = "print hardware info", + .function = &_mailbox_command}; diff --git a/lab5/kernel/command/memory.c b/lab5/kernel/command/memory.c new file mode 100644 index 000000000..d92aabf9c --- /dev/null +++ b/lab5/kernel/command/memory.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +void _test_malloc_command(int argc, char **argv) { + print_string("\nMalloc size: "); + unsigned int size = 0; + while (1) { + char c = read_c(); + if (c == '\n') { + break; + } + size = size * 10 + (c - '0'); + print_char(c); + } + void *ptr = simple_malloc(size); + if (ptr == 0) { + print_string("\nMalloc failed! "); + } + + print_string("\nMalloc address: "); + print_h((uintptr_t)ptr); + print_string("\n"); +} + +command_t test_malloc_command = {.name = "test_malloc", + .description = "test malloc", + .function = &_test_malloc_command}; + +void _test_kmalloc_comand(int argc, char **argv) { + if (argc != 2) { + print_string("\nUsage: test_kmalloc \n"); + return; + } + + int size = atoi(argv[1]); + print_string("\nMalloc size: "); + print_string(argv[1]); + void *ptr = kmalloc(size); + if (ptr == 0) { + print_string("\nMalloc failed! "); + } + print_string("\nMalloc address: "); + print_h((uint64_t)ptr); + print_string(", "); + print_d((uint64_t)ptr); + print_string("\n"); + + // print_kmalloc_caches(); + // print_free_areas(); +} + +command_t test_kmalloc_command = {.name = "test_kmalloc", + .description = "test kmalloc", + .function = &_test_kmalloc_comand}; + +void _test_kfree_command(int argc, char **argv) { + if (argc != 2) { + print_string("\nUsage: test_kfree
\n"); + return; + } + uintptr_t addr = atoi(argv[1]); + kfree((void *)addr); + print_string("\nFree address: "); + print_h(addr); + print_string("\n"); + print_free_areas(); +} + +command_t test_kfree_command = {.name = "test_kfree", + .description = "test kfree", + .function = &_test_kfree_command}; diff --git a/lab5/kernel/command/multi_thread.c b/lab5/kernel/command/multi_thread.c new file mode 100644 index 000000000..1e3722e01 --- /dev/null +++ b/lab5/kernel/command/multi_thread.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include + +void tt() { + for (int i = 0; i < 3; i++) + kthread_create(thread_test); + idle(); +} + +command_t test_thread_command = { + .name = "thread", + .description = "demo thread user program", + .function = &tt, +}; + +void entry() { + kthread_create(run_fork_test); + idle(); +} + +command_t test_multi_thread_command = { + .name = "multi_thread", + .description = "demo multi-thread user program", + .function = &entry, +}; diff --git a/lab5/kernel/command/ramfs.c b/lab5/kernel/command/ramfs.c new file mode 100644 index 000000000..7fb2a5d86 --- /dev/null +++ b/lab5/kernel/command/ramfs.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include + +void _ls_command(int argc, char **argv) { + file_list_t *file_list = ramfs_get_file_list(); + for (int i = 0; i < file_list->file_count; i++) { + print_string("\n"); + print_string(file_list->file_names[i]); + print_string(" "); + print_d(file_list->file_sizes[i]); + print_string(" bytes"); + } + print_string("\n"); +} + +command_t ls_command = {.name = "ls", + .description = "list the files in ramfs", + .function = &_ls_command}; + +void _cat_command(int argc, char **argv) { + if (argc < 2) { + print_string("\nUsage: cat \n"); + return; + } + char *file_name = argv[1]; + + unsigned long file_size = ramfs_get_file_size(file_name); + char *file_contents = kmalloc(file_size); + ramfs_get_file_contents(file_name, file_contents); + + if (file_contents) { + print_string("\n"); + for (int i = 0; i < file_size-1; i++) { + print_char(file_contents[i]); + } + } else { + print_string("\nFile not found\n"); + } +} + +command_t cat_command = {.name = "cat", + .description = "print the contents of a file", + .function = &_cat_command}; + +// void _exec_command(int argc, char **argv) { +// if (argc < 2) { +// print_string("\nUsage: exec "); +// return; +// } +// +// char *file_name = argv[1]; +// char *file_contents = ramfs_get_file_contents(file_name); +// uint32_t file_size = ramfs_get_file_size(file_name); +// if (file_contents == NULL) { +// print_string("[ERROR] File not found: "); +// print_string(file_name); +// print_string("\n"); +// return; +// } +// print_string("\n[INFO] Executing file: "); +// print_string(file_name); +// print_string("\n"); +// +// char *user_program = (void *)USER_PROGRAM_BASE; +// memcpy(user_program, file_contents, file_size); +// from_el1_to_el0(USER_PROGRAM_BASE, USER_STACK_POINTER_BASE); +// } +// +// command_t exec_command = {.name = "exec", +// .description = "execute a user program", +// .function = &_exec_command}; diff --git a/lab5/kernel/command/reboot.c b/lab5/kernel/command/reboot.c new file mode 100644 index 000000000..c47dc9e55 --- /dev/null +++ b/lab5/kernel/command/reboot.c @@ -0,0 +1,12 @@ +#include +#include +#include + +void _reboot_command(int argc, char **argv) { + print_string("\nRebooting ...\n"); + reset(200); +} + +command_t reboot_command = {.name = "reboot", + .description = "reboot the device", + .function = &_reboot_command}; diff --git a/lab5/kernel/command/set_timeout.c b/lab5/kernel/command/set_timeout.c new file mode 100644 index 000000000..79532e012 --- /dev/null +++ b/lab5/kernel/command/set_timeout.c @@ -0,0 +1,27 @@ +#include +#include +#include + +void print_timeout(int delay) { + print_string("\nTimeout: "); + print_d((const int)delay); + print_string("s\n"); +} + +void _set_timeout(int argc, char **argv) { + if (argc < 2) { + print_string("\nUsage: set_timeout \n"); + return; + } + + print_string("\nSetting timeout: "); + print_d(atoi(argv[1])); + print_string("s\n"); + + unsigned long timeout = atoi(argv[1]); + set_timeout((void *)print_timeout, (void *)timeout, atoi(argv[1])); +} + +command_t set_timeout_command = {.name = "set_timeout", + .description = "set a timeout", + .function = &_set_timeout}; diff --git a/lab5/kernel/commands.h b/lab5/kernel/commands.h new file mode 100644 index 000000000..8a81336ba --- /dev/null +++ b/lab5/kernel/commands.h @@ -0,0 +1,24 @@ +#ifndef _ALL_H +#define _ALL_H + +typedef struct command { + char name[16]; + char description[64]; + void (*function)(int argc, char **argv); +} command_t; + +extern command_t hello_command; +extern command_t mailbox_command; +extern command_t reboot_command; +extern command_t ls_command; +extern command_t cat_command; +extern command_t test_malloc_command; +extern command_t async_io_demo_command; +extern command_t exec_command; +extern command_t set_timeout_command; +extern command_t test_kmalloc_command; +extern command_t test_kfree_command; +extern command_t test_multi_thread_command; +extern command_t test_thread_command; + +#endif diff --git a/lab5/kernel/console.c b/lab5/kernel/console.c new file mode 100644 index 000000000..3b393d9cd --- /dev/null +++ b/lab5/kernel/console.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include + +static command_t commands[64]; +static int num_commands; + +static void read_command(char *); +static char *load_command(int); +static int parse_command(char *, char **); +void _print_help(); + +void init_console() { + num_commands = 0; + register_all_commands(); +} + +void run_console() { + char input[256]; + + while (1) { + print_string("\n$ "); + + read_command(input); + int argc = 0; + char *argv[16]; + argc = parse_command(input, argv); + + if (argc == 0) { + continue; + } + if (strcmp(argv[0], "help") == 0) { + _print_help(); + continue; + } + int i = 0; + for (i = 0; i < num_commands; i++) { + if (strcmp(argv[0], commands[i].name) == 0) { + commands[i].function(argc, argv); + break; + } + } + if (i == num_commands) { + print_string("\ncommand not found\n"); + } + } +} + +void register_command(command_t *command) { + commands[num_commands] = *command; + num_commands++; +} + +void register_all_commands() { + register_command(&hello_command); + register_command(&mailbox_command); + register_command(&reboot_command); + register_command(&ls_command); + register_command(&cat_command); + register_command(&exec_command); + register_command(&test_malloc_command); + register_command(&async_io_demo_command); + register_command(&set_timeout_command); + register_command(&test_kmalloc_command); + register_command(&test_kfree_command); + register_command(&test_multi_thread_command); + register_command(&test_thread_command); +} + +static void read_command(char *x) { + char input_char; + x[0] = 0; + int input_index = 0; + while ((input_char = read_c()) != '\n') { + x[input_index] = input_char; + input_index += 1; + print_char(input_char); + } + if (x[input_index - 1] == '\n') { + x[input_index - 1] = '\0'; + } else { + x[input_index] = '\0'; + } +} + +static int parse_command(char *command, char **argv) { + int argc = 0; + + char *token = strtok(command, " "); + while (token != NULL) { + argv[argc] = token; + argc++; + token = strtok(NULL, " "); + } + return argc; +} + +void _print_help() { + print_string("\n"); + + print_string("help : print this help menu\n"); + for (int i = 0; i < num_commands; i++) { + print_string(commands[i].name); + print_string(" : "); + print_string(commands[i].description); + print_string("\n"); + } +} diff --git a/lab5/kernel/console.h b/lab5/kernel/console.h new file mode 100644 index 000000000..bf6df2a85 --- /dev/null +++ b/lab5/kernel/console.h @@ -0,0 +1,13 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +#include + + +void init_console(); +void run_console(); +void register_command(command_t *command); +void register_all_commands(); +void destroy_console(); + +#endif diff --git a/lab5/kernel/device_tree.c b/lab5/kernel/device_tree.c new file mode 100644 index 000000000..c3f8767cf --- /dev/null +++ b/lab5/kernel/device_tree.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +// get this value from start.S +extern void *dtb_base; + +static uint64_t initrd_start, initrd_end; +static uint32_t be2le(const void *s); + +void fdt_traverse(void (*callback)(void *)) { + struct fdt_header *header = (struct fdt_header *)(dtb_base); + print_string("[INFO] Dtb loaded address: "); + uart_hex((uintptr_t)header); + print_string("\n"); + + // get the offset of the structure block and string block by adding the base + // address of the header to the offset + uint8_t *structure = (uint8_t *)header + be2le(&header->off_dt_struct); + uint8_t *strings = (uint8_t *)header + be2le(&header->off_dt_strings); + + uint32_t totalsize = be2le(&header->totalsize); + + // Parse the structure block + uint8_t *ptr = structure; // Point to the beginning of structure block + while (ptr < strings + totalsize) { + uint32_t token = be2le((char *)ptr); + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + // print_string((char *)ptr); + ptr += align4(strlen((char *)ptr)); + break; + case FDT_END_NODE: + break; + case FDT_PROP:; // for C23 warning + uint32_t len = be2le((char *)ptr); + ptr += 4; + uint32_t nameoff = be2le((char *)ptr); + ptr += 4; + if (strcmp((char *)(strings + nameoff), "linux,initrd-start") == + 0) { + initrd_start = be2le((void *)ptr); + callback((void *)initrd_start); + } else if (strcmp((char *)(strings + nameoff), + "linux,initrd-end") == 0) { + initrd_end = be2le((void *)ptr); + } + + ptr += align4(len); + break; + case FDT_NOP: + break; + case FDT_END: + break; + } + } +} + +uint64_t fdt_get_initrd_start() { return initrd_start; } +uint64_t fdt_get_initrd_end() { return initrd_end; } + +/** + * @brief Convert a 4-byte big-endian sequence to little-endian. + * + * @param s: big-endian sequence + * @return little-endian sequence + */ +static uint32_t be2le(const void *s) { + const uint8_t *bytes = (const uint8_t *)s; + return (uint32_t)bytes[0] << 24 | (uint32_t)bytes[1] << 16 | + (uint32_t)bytes[2] << 8 | (uint32_t)bytes[3]; +} diff --git a/lab5/kernel/device_tree.h b/lab5/kernel/device_tree.h new file mode 100644 index 000000000..8def99cd5 --- /dev/null +++ b/lab5/kernel/device_tree.h @@ -0,0 +1,31 @@ +#ifndef _DEVTREE_H +#define _DEVTREE_H + +#include +#include + +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; +}; + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +void fdt_traverse(void (*callback)(void *)); +uint64_t fdt_get_initrd_start(); +uint64_t fdt_get_initrd_end(); + + +#endif diff --git a/lab5/kernel/io.c b/lab5/kernel/io.c new file mode 100644 index 000000000..f5c34cf69 --- /dev/null +++ b/lab5/kernel/io.c @@ -0,0 +1,37 @@ +#include +#include +#include + +char read_c() { return uart_getc(); } +void read_s(char *s) { + char input_char; + s[0] = 0; + int input_index = 0; + while ((input_char = read_c()) != '\n') { + if (input_char == '\b' || input_char == 127) { // 處理退格鍵 + if (input_index > 0) { // 確保有字元可以刪除 + input_index -= 1; // 刪除最後一個字元 + print_string("\b \b"); // 在控制台上刪除這個字元,\b + // 是回退一格,空格刪除字元,再一個 \b + // 是將光標回退一格。 + continue; + } + } + s[input_index] = input_char; + input_index += 1; + print_char(input_char); + } + + s[input_index] = 0; +} + +void print_char(const char c) { return uart_send(c); } +void print_string(const char *s) { return uart_puts(s); } +void print_h(const unsigned long long x) { uart_hex(x); } + +void print_d(const int x) { + char buffer[10]; + + itoa(x, buffer); + print_string(buffer); +} diff --git a/lab5/kernel/io.h b/lab5/kernel/io.h new file mode 100644 index 000000000..aaaf4b60f --- /dev/null +++ b/lab5/kernel/io.h @@ -0,0 +1,11 @@ +#ifndef IO_H +#define IO_H + +char read_c(); +void read_s(char *s); +void print_char(const char); +void print_string(const char *); +void print_h(const unsigned long long x); +void print_d(const int x); + +#endif diff --git a/lab5/kernel/irq_entry.c b/lab5/kernel/irq_entry.c new file mode 100644 index 000000000..d9cf7ba9f --- /dev/null +++ b/lab5/kernel/irq_entry.c @@ -0,0 +1,103 @@ +#include "bsp/traps.h" +#include +#include +#include +#include +#include +#include + +void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr) { + print_string("spsr_el1: "); + print_h(spsr); + print_string("\n elr_el1: "); + print_h(elr); + print_string("\n esr_el1: "); + print_h(esr); + print_string("\n\n"); +} + +void exception_entry(trap_frame *tf) { + unsigned long elr, esr, spsr; + asm volatile("mrs %0, elr_el1" : "=r"(elr)); + asm volatile("mrs %0, esr_el1" : "=r"(esr)); + asm volatile("mrs %0, spsr_el1" : "=r"(spsr)); + if (esr != 0x56000000) { + print_registers(elr, esr, spsr); + while (1); + } + + enable_irq(); + switch (tf->x8) { + case 0: + tf->x0 = sys_getpid(); + break; + case 1: + tf->x0 = sys_uart_read((char *)tf->x0, tf->x1); + break; + case 2: + tf->x0 = sys_uart_write((const char *)tf->x0, tf->x1); + break; + case 3: + tf->x0 = sys_exec((const char *)tf->x0, (char *const *)tf->x1); + tf->elr_el1 = get_current()->context.lr; + tf->sp_el0 = (unsigned long)get_current()->user_stack + STACK_SIZE; + break; + case 4: + tf->x0 = sys_fork(tf); + break; + case 5: + sys_exit(); + break; + case 6: + tf->x0 = sys_mbox_call(tf->x0, (unsigned int *)tf->x1); + break; + case 7: + sys_kill(tf->x0); + break; + case 8: + sys_signal(tf->x0, (void (*)())tf->x1); + break; + case 9: + sys_sigkill(tf->x0, tf->x1); + break; + case 139: + sys_sigreturn(tf); + break; + default: + print_string("[ERROR] Invalid system call\n"); + } +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + print_string("[ERROR] The exception handler is not implemented\n"); + print_registers(elr, esr, spsr); + while (1); +} + +void irq_entry(trap_frame *tf) { + + disable_irq(); + + if (is_core_timer_irq()) { + // Schedule processes + if (get_current() != get_current()->next) { + schedule(); + } + core_timer_disable(); + timer_irq_handler(); + } else if (is_gpu_irq()) { + if (is_aux_irq()) { + if (is_uart_rx_irq()) { + uart_disable_rx_interrupt(); + uart_rx_irq_handler(); + } else if (is_uart_tx_irq()) { + uart_disable_tx_interrupt(); + uart_tx_irq_handler(); + } + } + } else { + print_string("\r\nUnknown irq"); + } + + do_signal(tf); +} diff --git a/lab5/kernel/irq_entry.h b/lab5/kernel/irq_entry.h new file mode 100644 index 000000000..16b0ccf92 --- /dev/null +++ b/lab5/kernel/irq_entry.h @@ -0,0 +1,17 @@ +#ifndef __IRQ_ENTRY_H__ +#define __IRQ_ENTRY_H__ + +#define EL1_IRQ_TIMER_PRIORITY 0x1 +#define EL1_IRQ_UART_PRIORITY 0x2 + +// exception entrypoint for exception.S +void el1_irq_entry(int type, unsigned long spsr, unsigned long elr, + unsigned long esr); + +void default_exception_entry(int type, unsigned long spsr, unsigned long elr, + unsigned long esr); + +void el0_irq_entry(int type, unsigned long spsr, unsigned long elr, + unsigned long esr); + +#endif diff --git a/lab5/kernel/kmalloc.c b/lab5/kernel/kmalloc.c new file mode 100644 index 000000000..d9c59a861 --- /dev/null +++ b/lab5/kernel/kmalloc.c @@ -0,0 +1,137 @@ +#include +#include +#include + +#include "mm.h" + +#define KMEM_CACHE_MIN_ORDER 4 // 16 +#define KMEM_CACHE_MAX_ORDER 8 // 256 + +typedef struct kmem_cache { + struct kmem_cache *next; + unsigned long order; +} kmem_cache_t; + +static kmem_cache_t + *kmalloc_caches[KMEM_CACHE_MAX_ORDER - KMEM_CACHE_MIN_ORDER + 1]; + +uint32_t get_page_order_by_size(uint32_t size) { + uint32_t order = 0; + while (size > (1 << (PAGE_SHIFT + order))) { + order++; + } + return order; +} + +uint32_t get_cache_order_by_size(uint32_t size) { + uint32_t order = 0; + while (size > (1 << (order + KMEM_CACHE_MIN_ORDER))) { + order++; + } + return order; +} + +extern uint64_t __heap_start; +void init_kmalloc() { + // buddy_init((phys_addr_t)0x10000000, (phys_addr_t)0x20000000); + buddy_init((phys_addr_t)0x0, (phys_addr_t)0x3C000000); + memory_reserve(0x0, (phys_addr_t)0x1000); // Spin tables for multicore boot + // (0x0000 - 0x1000) + memory_reserve((phys_addr_t)0x80000, + (phys_addr_t)&__heap_start); // Kernel code and data + + memory_reserve( + (phys_addr_t)&__heap_start, + (phys_addr_t)((void *)&__heap_start + 0x100000)); // Startup allocator + + memory_reserve((phys_addr_t)fdt_get_initrd_start(), + (phys_addr_t)fdt_get_initrd_end()); // Initramfs + +// #ifdef MEM_DEBUG + print_free_areas(); +// #endif +} + +void print_kmalloc_caches() { + for (int i = 0; i < KMEM_CACHE_MAX_ORDER - KMEM_CACHE_MIN_ORDER; i++) { + kmem_cache_t *cache = kmalloc_caches[i]; + print_string("Cache Order "); + print_d(i); + print_string(": "); + while (cache != NULL) { + print_h((uint64_t)cache); + print_string(" -> "); + cache = cache->next; + } + print_string("NULL\n"); + } +} + +void *kmem_cache_alloc(uint32_t order) { + if (order > KMEM_CACHE_MAX_ORDER - KMEM_CACHE_MIN_ORDER) { + return NULL; + } + if (kmalloc_caches[order] == NULL) { + page_t *page = alloc_pages(0); + + if (page == NULL) { + return NULL; + } + + page->status = PAGE_CACHE; // mark as cache page + uint32_t cache_size = 1 << (order + KMEM_CACHE_MIN_ORDER); + for (int i = 0; i < PAGE_SIZE; i += cache_size) { + kmem_cache_t *cache = ((void *)get_addr_by_page(page) + i); + cache->order = order; + cache->next = kmalloc_caches[order]; + kmalloc_caches[order] = cache; + } + // print_d((uint64_t)page); + } + kmem_cache_t *cache = kmalloc_caches[order]; + kmalloc_caches[order] = cache->next; + + // print_kmalloc_caches(); + + return cache; +} + +void kmem_cache_free(void *ptr, uint32_t order) { + if (order > KMEM_CACHE_MAX_ORDER - KMEM_CACHE_MIN_ORDER) { + return; + } + // push cache back to the list + kmem_cache_t *cache = (kmem_cache_t *)ptr; + cache->next = kmalloc_caches[order]; + kmalloc_caches[order] = cache; + + // print_kmalloc_caches(); +} + +void *kmalloc(uint32_t size) { + uint32_t order = get_page_order_by_size(size); + if (order > MAX_ORDER) { + return NULL; + } + + // if (order == 0) { + // if (size >= (1 << KMEM_CACHE_MAX_ORDER)) { + // return (void *)get_addr_by_page(alloc_pages(order)); + // } + // + // uint32_t cache_order = get_cache_order_by_size(size); + // return kmem_cache_alloc(cache_order); + // } + + return (void *)get_addr_by_page(alloc_pages(order)); +} + +void kfree(void *ptr) { + if ((uintptr_t)ptr % PAGE_SIZE == 0) { + page_t *page = get_page_by_addr((phys_addr_t)ptr); + free_pages(page, page->order); + return; + } + kmem_cache_t *cache = (kmem_cache_t *)ptr; + kmem_cache_free(cache, cache->order); +} diff --git a/lab5/kernel/main.c b/lab5/kernel/main.c new file mode 100644 index 000000000..0d22f1c67 --- /dev/null +++ b/lab5/kernel/main.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void print_boot_timeout(int delay) { + print_string("\nBoot Timeout: "); + print_d((const int)delay); + print_string("s\n"); +} + +int main() { + // lab1 + uart_init(); + init_console(); + + // lab2 + init_memory(); + fdt_traverse(init_ramfs_callback); + + // lab3 + enable_irq(); + // set_timeout((void *)print_boot_timeout, (void *)4, 4); + core_timer_enable(); + + // lab4 + init_kmalloc(); + + // lab5 + // sched_init(); + kthread_init(); + + print_string("\nWelcome to visitorckw's shell\n"); + + // set_timeout((void*)print_boot_timeout, 0, 1); + // kthread_create((void *)run_console); + run_console(); + + return 0; +} diff --git a/lab5/kernel/memory.h b/lab5/kernel/memory.h new file mode 100644 index 000000000..c51df8290 --- /dev/null +++ b/lab5/kernel/memory.h @@ -0,0 +1,20 @@ +#ifndef _MEMORY_H +#define _MEMORY_H + +#define USER_PROGRAM_BASE 0x100000 +#define USER_STACK_POINTER_BASE 0x200000 + +#include + +void init_memory(); +void *simple_malloc(unsigned int size); +char *get_heap_top(); + +void init_kmalloc(); +void *kmalloc(uint32_t size); +void kfree(void *ptr); + +void print_kmalloc_caches(); +void print_free_areas(); + +#endif diff --git a/lab5/kernel/mm.h b/lab5/kernel/mm.h new file mode 100644 index 000000000..fa6255b0f --- /dev/null +++ b/lab5/kernel/mm.h @@ -0,0 +1,31 @@ +#ifndef __MM_TYPE_H__ +#define __MM_TYPE_H__ + +#include + +#define MAX_ORDER 10 +#define PAGE_SIZE 4096 +#define PAGE_SHIFT 12 + +#define PAGE_FREE 0 +#define PAGE_ALLOCATED 1 +#define PAGE_CACHE 2 +#define PAGE_RESERVED 3 + +typedef uintptr_t phys_addr_t; // 節點結構,代表自由區塊 +typedef struct page { + struct page *prev, *next; // 指向同一 order 的下一個節點 + unsigned long order; + uint8_t status; // 0: free, 1: allocated +} page_t; + +void buddy_init(phys_addr_t start, phys_addr_t end); +page_t *alloc_pages(unsigned long order); +void free_pages(page_t *page, unsigned long order); + +phys_addr_t get_addr_by_page(page_t *page); +page_t *get_page_by_addr(phys_addr_t addr); + +void memory_reserve(phys_addr_t, phys_addr_t); + +#endif diff --git a/lab5/kernel/sched.h b/lab5/kernel/sched.h new file mode 100644 index 000000000..0f3cec555 --- /dev/null +++ b/lab5/kernel/sched.h @@ -0,0 +1,66 @@ +#ifndef SCHED_H +#define SCHED_H + +#include "signal.h" +#include "bsp/traps.h" + +#define STACK_SIZE 4096 + +struct thread_struct { + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; + unsigned long lr; + unsigned long sp; +}; + +enum task_state { + TASK_RUNNING, + TASK_STOPPED, + EXIT_ZOMBIE, +}; + +struct task_struct { + struct thread_struct context; + unsigned long pid; + // unsigned long parent_pid; + // unsigned long child_count; + enum task_state state; + void *stack; + void *user_stack; + void (*sighand[NSIG + 1])(); + int sigpending; + int sighandling; + trap_frame sigframe; + void *sig_stack; + struct task_struct *prev; + struct task_struct *next; +}; + +typedef struct task_queue_t { + struct task_struct *front; + struct task_struct *rear; +} task_queue_t; + +extern struct task_struct *get_current(); +struct task_struct *get_task(int pid); +void kthread_init(); +struct task_struct *kthread_create(void (*func)()); +void kthread_exit(); +void kthread_stop(int pid); +void schedule(); +void idle(); +void kill_zombies(); + +void display_run_queue(); +void thread_test(); + +#endif // SCHED_H diff --git a/lab5/kernel/signal.c b/lab5/kernel/signal.c new file mode 100644 index 000000000..980899fd4 --- /dev/null +++ b/lab5/kernel/signal.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +extern void sigreturn(); // Defined in entry.S + +void signal(int signum, void (*handler)()) +{ + get_current()->sighand[signum] = handler; +} + +void sigkill(int pid, int sig) +{ + struct task_struct *task = get_task(pid); + task->sigpending |= 1 << (sig - 1); // Set the signal pending bit +} + +void do_signal(trap_frame *regs) +{ + // Prevent nested signal handling + if (get_current()->sighandling) + return; + + int signum = 1; + while (get_current()->sigpending) { + if (get_current()->sigpending & 0x1) { + get_current()->sigpending &= ~(0x1); // make sure that we not handling the same signal again + get_current()->sighandling = 1; + + if (get_current()->sighand[signum] == 0) { + kthread_exit(); // Default handler (exit the process) + get_current()->sighandling = 0; + return; // Jump to the previous context (user program) after eret + } + + // Save the sigframe + memcpy(&get_current()->sigframe, regs, sizeof(trap_frame)); + get_current()->sig_stack = kmalloc(STACK_SIZE); + regs->x30 = (unsigned long)sigreturn; + regs->spsr_el1 = 0x0; + regs->elr_el1 = (unsigned long)get_current()->sighand[signum]; + regs->sp_el0 = (unsigned long)get_current()->sig_stack + STACK_SIZE; + return; // Jump to the signal handler after eret + } + signum++; + get_current()->sigpending >>= 1; + } +} diff --git a/lab5/kernel/signal.h b/lab5/kernel/signal.h new file mode 100644 index 000000000..25937cd13 --- /dev/null +++ b/lab5/kernel/signal.h @@ -0,0 +1,13 @@ + +#ifndef SIGNAL_H +#define SIGNAL_H + +#include "bsp/traps.h" + +#define NSIG 10 + +void signal(int signum, void (*handler)()); +void sigkill(int pid, int sig); +void do_signal(trap_frame *regs); + +#endif diff --git a/lab5/kernel/simple.c b/lab5/kernel/simple.c new file mode 100644 index 000000000..ac563a94c --- /dev/null +++ b/lab5/kernel/simple.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +extern uint64_t __heap_start; +#define HEAP_MAX (&__heap_start) + 0x100000 + +static char *heap_top; + +void init_memory() { + heap_top = (char *)(&__heap_start); + // heap_top++; +} + +void *simple_malloc(unsigned int size) { + if (size == 0) { + return NULL; + } + size = align4(size); + if (heap_top + size >= HEAP_MAX) { + print_string("Out of memory\n"); + return NULL; + } + void *ret = heap_top; + heap_top += size; + return ret; +} + +char *get_heap_top() { return heap_top; } diff --git a/lab5/kernel/syscall.c b/lab5/kernel/syscall.c new file mode 100644 index 000000000..fa308de4b --- /dev/null +++ b/lab5/kernel/syscall.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bsp/traps.h" +#include +#include + +extern void child_ret_from_fork(); + +int sys_getpid() { return get_current()->pid; } + +size_t sys_uart_read(char *buf, size_t size) { + int i = 0; + while (i < size) buf[i++] = uart_getc(); + return i; +} + +size_t sys_uart_write(const char *buf, size_t size) { + int i = 0; + while (i < size) uart_send(buf[i++]); + return i; +} + +int sys_exec(const char *file_name, char *const argv[]) { + unsigned long file_size = ramfs_get_file_size(file_name); + char *file_contents = kmalloc(file_size); + ramfs_get_file_contents(file_name, file_contents); + + if (file_contents == NULL) { + print_string("[ERROR] File not found: "); + print_string(file_name); + print_string("\n"); + return 0; + } + print_string("\n[INFO] Executing file: "); + print_string(file_name); + print_string("\n"); + + char *user_program = kmalloc(file_size); + get_current()->state = TASK_STOPPED; + memcpy(user_program, file_contents, file_size); + get_current()->sigpending = 0; + memset(get_current()->sighand, 0, sizeof(get_current()->sighand)); + get_current()->context.lr = (unsigned long)user_program; + return 0; +} + +int sys_fork(trap_frame *tf) { + disable_irq(); + struct task_struct *parent = get_current(); + struct task_struct *child = kthread_create(0); + + // child->parent_pid = parent->pid; + // parent->child_count++; + // Copy the parent's memory + memcpy(child->stack, parent->stack, STACK_SIZE); + memcpy(child->user_stack, parent->user_stack, STACK_SIZE); + memcpy(child->sighand, parent->sighand, sizeof(parent->sighand)); + + unsigned long sp_off = (unsigned long)tf - (unsigned long)parent->stack; + trap_frame *child_trap_frame = (trap_frame *)(child->stack + sp_off); + + child->context.sp = (unsigned long)child_trap_frame; + child->context.lr = (unsigned long)child_ret_from_fork; + + unsigned long sp_el0_off = tf->sp_el0 - (unsigned long)parent->user_stack; + child_trap_frame->sp_el0 = (unsigned long)child->user_stack + sp_el0_off; + child_trap_frame->x0 = 0; + + enable_irq(); + return child->pid; +} + +void sys_exit() { kthread_exit(); } + +int sys_mbox_call(unsigned char ch, unsigned int *mbox) { + return mailbox_call(ch, mbox); +} + +void sys_kill(int pid) { kthread_stop(pid); } + +void sys_signal(int signum, void (*handler)()) { signal(signum, handler); } + +void sys_sigkill(int pid, int sig) { sigkill(pid, sig); } + +void sys_sigreturn(trap_frame *regs) { + // Restore the sigframe + memcpy(regs, &get_current()->sigframe, sizeof(trap_frame)); + kfree(get_current()->sig_stack); + get_current()->sighandling = 0; + return; // Jump to the previous context (user program) after eret +} + +// emit the syscall instruction +static int getpid() { + long pid = -1; + asm volatile("mov x8, 0"); + asm volatile("svc 0"); + asm volatile("mov %0, x0" : "=r"(pid)); + return pid; +} + +static int fork() { + long ret = -1; + asm volatile("mov x8, 4"); + asm volatile("svc 0"); + asm volatile("mov %0, x0" : "=r"(ret)); + return ret; +} + +static void exit() { + asm volatile("mov x8, 5"); + asm volatile("svc 0"); +} + +void fork_test() { + print_string("Fork Test (pid = "); + print_d(getpid()); + print_string(")\n"); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { + print_string("first child pid: "); + print_d(getpid()); + print_string(", cnt: "); + print_d(cnt); + print_string(", ptr: "); + print_h((unsigned long long)&cnt); + print_string("\n"); + cnt++; + if ((ret = fork()) != 0) { + print_string("first child pid: "); + print_d(getpid()); + print_string(", cnt: "); + print_d(cnt); + print_string(", ptr: "); + print_h((unsigned long long)&cnt); + print_string("\n"); + } else { + while (cnt < 5) { + print_string("second child pid: "); + print_d(getpid()); + print_string(", cnt: "); + print_d(cnt); + print_string(", ptr: "); + print_h((unsigned long long)&cnt); + print_string("\n"); + for (int i = 0; i < 1000000; i++); + cnt++; + } + } + exit(); + } else { + print_string("parent here, pid "); + print_d(getpid()); + print_string(", child "); + print_d(ret); + print_string("\n"); + } + exit(); +} + +void run_fork_test() { + asm volatile("msr spsr_el1, %0" ::"r"(0x0)); + asm volatile("msr elr_el1, %0" ::"r"(fork_test)); + asm volatile("msr sp_el0, %0" ::"r"(get_current()->context.sp)); + asm volatile("mov sp, %0" ::"r"(get_current()->stack + STACK_SIZE)); + asm volatile("eret"); +} diff --git a/lab5/kernel/task.c b/lab5/kernel/task.c new file mode 100644 index 000000000..c09800e42 --- /dev/null +++ b/lab5/kernel/task.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include + +static int thread_count = 0; +static struct task_struct *run_queue; + +extern void switch_to(struct task_struct *prev, struct task_struct *next); + +static void enqueue(struct task_struct **queue, struct task_struct *task) { + if (*queue == 0) { + *queue = task; + task->next = task; + task->prev = task; + } else { + task->next = *queue; + task->prev = (*queue)->prev; + (*queue)->prev->next = task; + (*queue)->prev = task; + } +} + +static void remove(struct task_struct **queue, struct task_struct *task) { + if (*queue == task) *queue = (task->next == task) ? 0 : task->next; + task->next->prev = task->prev; + task->prev->next = task->next; +} + +void display_run_queue() { + struct task_struct *task = run_queue; + do { + print_string("Task id: "); + print_d(task->pid); + print_string(", state: "); + print_d(task->state); + print_string(", next: "); + print_d(task->next->pid); + print_string("\n"); + task = task->next; + } while (task != run_queue); +} + +void schedule() { + struct task_struct *current = get_current(); + struct task_struct *next = current->next; + + while(next->state != TASK_RUNNING) { + next = next->next; + } + + // display_run_queue(); + // print_string("Scheduling from: "); + // print_d(current->pid); + // print_string(" -> "); + // print_d(next->pid); + // print_string("\n"); + + switch_to(current, current->next); +} + +void kill_zombies() { + // print_string("Killing zombies\n"); + // display_run_queue(); + struct task_struct *next, *task = run_queue; + do { + next = task->next; + if (task->state == EXIT_ZOMBIE) { + remove(&run_queue, task); + kfree(task->stack); + kfree(task->user_stack); + } + task = next; + } while (task != run_queue); +} + +void idle() { + while (1) { + kill_zombies(); + schedule(); + } +} + +struct task_struct *get_task(int pid) { + struct task_struct *task = run_queue; + do { + if (task->pid == pid) return task; + task = task->next; + } while (task != run_queue); + return 0; +} + +void kthread_init() { + kthread_create(idle); + asm volatile("msr tpidr_el1, %0" ::"r"(run_queue)); + // display_run_queue(); +} + +struct task_struct *kthread_create(void (*func)()) { + struct task_struct *task = kmalloc(sizeof(struct task_struct)); + task->pid = thread_count++; + task->state = TASK_RUNNING; + task->stack = kmalloc(STACK_SIZE); + task->user_stack = kmalloc(STACK_SIZE); + memset(task->sighand, 0, sizeof(task->sighand)); + task->sigpending = 0; + task->sighandling = 0; + task->context.lr = (unsigned long)func; + task->context.sp = (unsigned long)task->user_stack + STACK_SIZE; + task->context.fp = (unsigned long)task->user_stack + STACK_SIZE; + enqueue(&run_queue, task); + return task; +} + +void kthread_exit() { + get_current()->state = EXIT_ZOMBIE; + schedule(); +} + +void kthread_stop(int pid) { + struct task_struct *task = run_queue; + do { + if (task->pid == pid) task->state = EXIT_ZOMBIE; + task = task->next; + } while (task != run_queue); + schedule(); +} + +void thread_test() { + for (int i = 0; i < 5; ++i) { + print_string("Thread id: "); + print_d(get_current()->pid); + print_string(" "); + print_d(i); + print_string("\n"); + for (int i = 0; i < 1000000; i++); + schedule(); + } + kthread_exit(); +} diff --git a/lab5/kernel/timer.c b/lab5/kernel/timer.c new file mode 100644 index 000000000..1e6cbe9d4 --- /dev/null +++ b/lab5/kernel/timer.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +#include "kernel/sched.h" + +typedef struct timer_t { + void (*func)(void *); + void *arg; + int time; + struct timer_t *next; +} timer_t; + +static timer_t *head = (timer_t *)0; + +void set_timeout(void (*callback)(void *), void *arg, int delay) { + timer_t *timer = (timer_t *)kmalloc(sizeof(timer_t)); + + timer->func = callback; + timer->arg = arg; + timer->time = timer_get_uptime() + delay; + timer->next = 0; + + if (head == 0 || timer->time < head->time) { + // Insert at the beginning of the list + timer->next = head; + head = timer; + core_timer_enable(); + return; + } + + timer_t *current = head; + while (current->next != 0 && current->next->time <= timer->time) + current = current->next; + timer->next = current->next; + current->next = timer; +} + +static void empty() {} + +void timer_irq_handler() { +#ifdef SCHED_DEBUG + static int count = 0; + if (count++ % 1 == 0) { + print_string("\n[timer_irq_handler] \n"); + print_task_list(); + } +#endif + + set_timeout(&empty, 0, 1); // 1s for task switch + + // Check the timer queue + while (head != 0 && timer_get_uptime() >= head->time) { + head->func(head->arg); + timer_t *temp = head; + head = head->next; + // kfree(temp); // if free this multi_thread will crash + } + + if (head != 0) core_timer_enable(); +} diff --git a/lab5/kernel/timer.h b/lab5/kernel/timer.h new file mode 100644 index 000000000..41c3a05b3 --- /dev/null +++ b/lab5/kernel/timer.h @@ -0,0 +1,11 @@ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include +#include + +void set_timeout(void (*callback)(void *), void *arg, int after); +void timer_irq_handler(); +void enable_user_to_physical_timer(); + +#endif diff --git a/lab5/lib/limits.h b/lab5/lib/limits.h new file mode 100644 index 000000000..36f8e3237 --- /dev/null +++ b/lab5/lib/limits.h @@ -0,0 +1,7 @@ +#ifndef _LIMITS_H +#define _LIMITS_H + +#define INT_MAX 2147483647 +#define INT_MIN -2147483648 + +#endif diff --git a/lab5/lib/sprintf.c b/lab5/lib/sprintf.c new file mode 100644 index 000000000..94a64f378 --- /dev/null +++ b/lab5/lib/sprintf.c @@ -0,0 +1,147 @@ +#include + +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/** + * minimal sprintf implementation + * Use the built-in va_list, va_start, va_arg, va_end macros to parse the parameters from `char* fmt`. + */ +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) { + // argument access + if (*fmt == '%') { // %d, %x, %s, %c + 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); // use int to get char + *dst++ = (char) arg; + fmt++; + continue; + } else + // decimal number + if(*fmt == 'd') { + arg = __builtin_va_arg(args, int); + // check input + sign = 0; + if((int) arg < 0) { + arg *= -1; + sign++; + } + if(arg > 99999999999999999L) { + arg = 99999999999999999L; + } + // convert to string + i = 18; + tmpstr[i] = 0; + do { + tmpstr[--i] = '0' + (arg % 10); + arg /= 10; + } while (arg != 0 && i > 0); + if (sign) { + tmpstr[--i] = '-'; + } + // padding, only space + if (len > 0 && len < 18) { + while (i > 18 - len) { + tmpstr[--i] = ' '; + } + } + p = &tmpstr[i]; + goto copystring; + } else if (*fmt == 'x') { // hex number + arg = __builtin_va_arg(args, long int); + // convert to string + i = 16; + tmpstr[i] = 0; + do { + char n = arg & 0xf; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + tmpstr[--i] = n + (n > 9 ? 0x37 : 0x30); + arg >>= 4; + } while (arg != 0 && i > 0); + // padding, only leading zeros + if (len > 0 && len <= 16) { + while (i > 16 - len) { + tmpstr[--i] = '0'; + } + } + p = &tmpstr[i]; + goto copystring; + } else if (*fmt == 's') { // string + p = __builtin_va_arg(args, char*); +copystring: if (p == (void *) 0) { + p = "(null)"; + } + while (*p) { + *dst++ = *p++; + } + } + } else { // without '%', just copy to *dst +put: *dst++ = *fmt; + } + fmt++; + } + *dst = 0; + // number of bytes written + return dst - orig; +} + +/** + * Variable length arguments + */ +unsigned int sprintf(char *dst, char* fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); // generate the va_list from fmt. + return vsprintf(dst, fmt, args); +} diff --git a/lab5/lib/stddef.h b/lab5/lib/stddef.h new file mode 100644 index 000000000..79fc8e514 --- /dev/null +++ b/lab5/lib/stddef.h @@ -0,0 +1,13 @@ +#ifndef _STDDEF_H_ +#define _STDDEF_H_ + +#define NULL ((void *)0) + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef uint64_t uintptr_t; +typedef unsigned long long size_t; + +#endif diff --git a/lab5/lib/stdlib.c b/lab5/lib/stdlib.c new file mode 100644 index 000000000..d583af908 --- /dev/null +++ b/lab5/lib/stdlib.c @@ -0,0 +1,41 @@ +#include + +int atoi(const char *s) { + long long result = 0; // prevent overflow + int sign = 1; + int i = 0; + + while (s[i] == ' ') i++; + + if (s[i] == '-') { + sign = -1; + i++; + } else if (s[i] == '+') + i++; + + while (s[i] >= '0' && s[i] <= '9') { + result = result * 10 + (s[i] - '0'); + i++; + } + + return sign * (int)result; +} + +void itoa(int value, char *str) { + int i = 0; + int sign = value; + if (value < 0) value = -value; + do { + str[i++] = value % 10 + '0'; + } while ((value /= 10) > 0); + if (sign < 0) str[i++] = '-'; + str[i] = '\0'; + + // reverse + for (int j = 0; j < i / 2; j++) { + char temp = str[j]; + str[j] = str[i - j - 1]; + str[i - j - 1] = temp; + } +} + diff --git a/lab5/lib/stdlib.h b/lab5/lib/stdlib.h new file mode 100644 index 000000000..a3ce0b866 --- /dev/null +++ b/lab5/lib/stdlib.h @@ -0,0 +1,7 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +int atoi(const char *); +void itoa(int value, char *str); + +#endif diff --git a/lab5/lib/string.c b/lab5/lib/string.c new file mode 100644 index 000000000..721428782 --- /dev/null +++ b/lab5/lib/string.c @@ -0,0 +1,101 @@ +#include +#include + +int strcmp(const char *a, const char *b) { + while (*a && (*a == *b)) { + a++, b++; + } + return *(const unsigned char *)a - *(const unsigned char *)b; +} + +char *strcpy(char *dest, const char *src) { + char *start = dest; + + while (*src != '\0') { + *dest = *src; + dest++; + src++; + } + + *dest = '\0'; + return start; +} + +char *strncpy(char *dest, const char *src, int n) { + char *start = dest; + while (*src != '\0' && n > 0) { + *dest = *src; + dest++; + src++; + n--; + } + *dest = '\0'; + return start; +} + +void *memcpy(void *dest, const void *src, int n) +{ + char *d = dest; + const char *s = src; + while (n--) + *d++ = *s++; + return dest; +} + +void *memset(void *s, int c, size_t n) { + unsigned char *p = (unsigned char *)s; + while (n--) { + *p++ = (unsigned char)c; + } + return s; +} + +int strlen(const char *str) { + int len = 0; + while (*str) { + str++; + len++; + } + return len; +} + +char *strtok(char *s, const char *delim) { + char *spanp; + int c, sc; + char *tok; + static char *last; + + if (s == NULL && (s = last) == NULL) { + return NULL; + } + + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++);) { + if (c == sc) { + c = *s++; + spanp = (char *)delim; + } + } + + if (c == 0) { + last = NULL; + return (NULL); + } + tok = s - 1; + + while (1) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + s = NULL; + } else { + s[-1] = 0; + } + last = s; + return (tok); + } + } while (sc != 0); + } +} diff --git a/lab5/lib/string.h b/lab5/lib/string.h new file mode 100644 index 000000000..4f776d1c1 --- /dev/null +++ b/lab5/lib/string.h @@ -0,0 +1,14 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + +int strcmp(const char *a, const char *b); +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, int n); +void *memcpy(void *dest, const void *src, int n); +void *memset(void *str, int c, size_t n); +int strlen(const char *s); +char* strtok(char* str, const char* delim); + +#endif diff --git a/lab5/lib/utils.c b/lab5/lib/utils.c new file mode 100644 index 000000000..ce920acff --- /dev/null +++ b/lab5/lib/utils.c @@ -0,0 +1,3 @@ +#include + +int align4(int n) { return n + (4 - n % 4) % 4; } diff --git a/lab5/lib/utils.h b/lab5/lib/utils.h new file mode 100644 index 000000000..977a944da --- /dev/null +++ b/lab5/lib/utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H +#define _UTILS_H + +unsigned int sprintf(char *dst, char *fmt, ...); +unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +int align4(int n); + +#endif diff --git a/lab5/new_initramfs.cpio b/lab5/new_initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..35e0f5d180be495377046d74f2ef420cfed2e269 GIT binary patch literal 247296 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 literal 0 HcmV?d00001 diff --git a/lab5/ori_initramfs.cpio b/lab5/ori_initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..868bd10e203e1a3785d6baebc2d61f4557b8505c GIT binary patch literal 1024 zcmXpoH!wFaG%zr5b#k$A0@4O1hORCk+Q8V=6@@l1FfubUa5i#r1F6RXj4ZGSf`p7< z;(81~+Yt7F?XfU)Gy%(F+Ghx{&jsQR14Fnu5c6=_=LoYeEi)(8NUx-#gdrp&vseL$ z;38ZMsBUov=^@@N22eM_Y{KalGnfjH&4wuMFhq5Ovm-eCEO3WEI6O?;j3D7 ../initramfs.cpio) + +test_svc_exception.o: test_svc_exception.S + $(ARMGNU)-gcc -MMD -c $< -o $@ + +clean: + rm -f *.o *.d *.elf *.img + +dump: test_svc_exception.img + qemu-aarch64 -M raspi3 -kernel test_svc_exception.img -display none -d in_asm diff --git a/lab5/user_program/linker.ld b/lab5/user_program/linker.ld new file mode 100644 index 000000000..e69de29bb diff --git a/lab5/user_program/test_svc_exception.S b/lab5/user_program/test_svc_exception.S new file mode 100644 index 000000000..ce7e53908 --- /dev/null +++ b/lab5/user_program/test_svc_exception.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b