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 000000000..c83b0817e Binary files /dev/null and b/lab5/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab5/bsp/base.h b/lab5/bsp/base.h new file mode 100644 index 000000000..1400433a2 --- /dev/null +++ b/lab5/bsp/base.h @@ -0,0 +1,28 @@ +// BCM2837 ARM Peripherals Page 6 +#define MMIO_BASE \ + 0x3F000000 +// BCM2835 page. 112-116 +#define IRQ_BASIC_PENDING ((volatile unsigned int*)(MMIO_BASE + 0x0000B200)) +#define IRQ_PENDING_1 \ + ((volatile unsigned int*)(MMIO_BASE + \ + 0x0000B204)) // Holds All interrupts 0...31 from + // the GPU side. +#define IRQ_PENDING_2 \ + ((volatile unsigned int*)(MMIO_BASE + 0x0000B208)) // 32...63 +#define FIQ_CONTROL ((volatile unsigned int*)(MMIO_BASE + 0x0000B20C)) +#define ENABLE_IRQS_1 \ + ((volatile unsigned int*)(MMIO_BASE + \ + 0x0000B210)) // Writing a 1 to a bit will set + // the corresponding IRQ enable bit. + // (0-31) +#define ENABLE_IRQS_2 \ + ((volatile unsigned int*)(MMIO_BASE + 0x0000B214)) // (32-63) +#define ENABLE_BASIC_IRQS ((volatile unsigned int*)(MMIO_BASE + 0x0000B218)) +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE + 0x0000B21C)) +#define DISABLE_IRQS_2 ((volatile unsigned int*)(MMIO_BASE + 0x0000B220)) +#define DISABLE_BASIC_IRQS \ + ((volatile unsigned int*)(MMIO_BASE + 0x0000B224)) // AUX interrupt. 29 + +// QA7 page. 16 +#define CORE0_IRQ_SOURCE \ + ((volatile unsigned int*)(0x40000060)) // Get the IRQ source for the core 0. diff --git a/lab5/bsp/entry.S b/lab5/bsp/entry.S new file mode 100644 index 000000000..cb6448fca --- /dev/null +++ b/lab5/bsp/entry.S @@ -0,0 +1,114 @@ +/* save general registers to stack */ +.macro save_all + sub sp, sp, 16 * 17 + stp x0, x1, [sp, 16 * 0] + stp x2, x3, [sp, 16 * 1] + stp x4, x5, [sp, 16 * 2] + stp x6, x7, [sp, 16 * 3] + stp x8, x9, [sp, 16 * 4] + stp x10, x11, [sp, 16 * 5] + stp x12, x13, [sp, 16 * 6] + stp x14, x15, [sp, 16 * 7] + stp x16, x17, [sp, 16 * 8] + stp x18, x19, [sp, 16 * 9] + stp x20, x21, [sp, 16 * 10] + stp x22, x23, [sp, 16 * 11] + stp x24, x25, [sp, 16 * 12] + stp x26, x27, [sp, 16 * 13] + stp x28, x29, [sp, 16 * 14] + mrs x10, spsr_el1 + mrs x11, elr_el1 + mrs x12, sp_el0 + stp x30, x10, [sp, 16 * 15] + stp x11, x12, [sp, 16 * 16] +.endm + +/* load general registers from stack */ +.macro load_all + ldp x30, x10, [sp, 16 * 15] + ldp x11, x12, [sp, 16 * 16] + msr spsr_el1, x10 + msr elr_el1, x11 + msr sp_el0, x12 + ldp x0, x1, [sp, 16 * 0] + ldp x2, x3, [sp, 16 * 1] + ldp x4, x5, [sp, 16 * 2] + ldp x6, x7, [sp, 16 * 3] + ldp x8, x9, [sp, 16 * 4] + ldp x10, x11, [sp, 16 * 5] + ldp x12, x13, [sp, 16 * 6] + ldp x14, x15, [sp, 16 * 7] + ldp x16, x17, [sp, 16 * 8] + ldp x18, x19, [sp, 16 * 9] + ldp x20, x21, [sp, 16 * 10] + ldp x22, x23, [sp, 16 * 11] + ldp x24, x25, [sp, 16 * 12] + ldp x26, x27, [sp, 16 * 13] + ldp x28, x29, [sp, 16 * 14] + add sp, sp, 16 * 17 +.endm + +exception_handler: + save_all + mov x0, sp + bl exception_entry // defined in traps.c + load_all + eret + +irq_handler: + save_all + mov x0, sp + bl irq_entry // defined in irq.c + load_all + eret + +invalid_exc_handler: + save_all + mrs x0, elr_el1 + mrs x1, esr_el1 + mrs x2, spsr_el1 + bl invalid_entry // defined in traps.c + load_all + eret + +.global child_ret_from_fork +child_ret_from_fork: + load_all + eret + +.global sigreturn +sigreturn: + mov x8, 139 + svc 0 + +.macro ventry label + b \label +.align 7 +.endm + +/* EL1 Exception Vector table + * vector table should be aligned to 0x800 + * and each entry size is 0x80 */ +.align 11 +.global exception_vector_table +exception_vector_table: + // EL1 -> 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 000000000..d5e4342d8 Binary files /dev/null and b/lab5/initramfs.cpio differ 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 000000000..35e0f5d18 Binary files /dev/null and b/lab5/new_initramfs.cpio differ diff --git a/lab5/ori_initramfs.cpio b/lab5/ori_initramfs.cpio new file mode 100644 index 000000000..868bd10e2 Binary files /dev/null and b/lab5/ori_initramfs.cpio differ diff --git a/lab5/rootfs/file1 b/lab5/rootfs/file1 new file mode 100644 index 000000000..9bc8d9ca0 --- /dev/null +++ b/lab5/rootfs/file1 @@ -0,0 +1 @@ +this is file1. diff --git a/lab5/rootfs/file2.txt b/lab5/rootfs/file2.txt new file mode 100644 index 000000000..f1adef63c --- /dev/null +++ b/lab5/rootfs/file2.txt @@ -0,0 +1 @@ +nothing here diff --git a/lab5/user_program/Makefile b/lab5/user_program/Makefile new file mode 100644 index 000000000..5e2780f44 --- /dev/null +++ b/lab5/user_program/Makefile @@ -0,0 +1,18 @@ +ARMGNU ?= aarch64-linux-gnu + +all: test_svc_exception.img + +test_svc_exception.img: test_svc_exception.o + $(ARMGNU)-ld -o test_svc_exception.elf test_svc_exception.o + $(ARMGNU)-objcopy test_svc_exception.elf -O binary test_svc_exception.img + cp ./test_svc_exception.img ../rootfs/ + (cd ../rootfs/ && find . | cpio -o -H newc > ../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