diff --git a/lab7/.clang-format b/lab7/.clang-format new file mode 100644 index 000000000..4113d14a2 --- /dev/null +++ b/lab7/.clang-format @@ -0,0 +1,9 @@ +UseTab: Never +TabWidth: 4 +IndentWidth: 4 +AccessModifierOffset: -4 + +BasedOnStyle: Google +IncludeBlocks: Regroup +SortIncludes: true + diff --git a/lab7/.clangd b/lab7/.clangd new file mode 100644 index 000000000..656204ef0 --- /dev/null +++ b/lab7/.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/lab7/include", + ] + diff --git a/lab7/Makefile b/lab7/Makefile new file mode 100644 index 000000000..408f09705 --- /dev/null +++ b/lab7/Makefile @@ -0,0 +1,24 @@ +QEMU_OPTS = -initrd 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/lab7/README.md b/lab7/README.md new file mode 100644 index 000000000..e575fa0f3 --- /dev/null +++ b/lab7/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/lab7/bcm2710-rpi-3-b-plus.dtb b/lab7/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..c83b0817e Binary files /dev/null and b/lab7/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab7/bsp/entry.S b/lab7/bsp/entry.S new file mode 100644 index 000000000..cb6448fca --- /dev/null +++ b/lab7/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/lab7/bsp/entry.h b/lab7/bsp/entry.h new file mode 100644 index 000000000..014f23be5 --- /dev/null +++ b/lab7/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/lab7/bsp/fbdev.c b/lab7/bsp/fbdev.c new file mode 100644 index 000000000..44b6963e5 --- /dev/null +++ b/lab7/bsp/fbdev.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include + +unsigned int __attribute__((aligned(16))) mbox[36]; + +unsigned int width, height, pitch, isrgb; /* dimensions and channel order */ +unsigned char *lfb; + +// This function is responsible for setting up the framebuffer +int fb_init() { + mbox[0] = 35 * 4; // Total size of the message + mbox[1] = MBOX_REQUEST; + + // Set physical width-height + mbox[2] = 0x48003; + mbox[3] = 8; + mbox[4] = 8; + mbox[5] = 1024; // width + mbox[6] = 768; // height + + // Set virtual width-height + mbox[7] = 0x48004; + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1024; // virtual width + mbox[11] = 768; // virtual height + + // Set virtual offset + mbox[12] = 0x48009; + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // x offset + mbox[16] = 0; // y offset + + // Set depth + mbox[17] = 0x48005; + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // depth + + // Set pixel order + mbox[21] = 0x48006; + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB, not BGR + + // Get framebuffer + mbox[25] = 0x40001; + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // pointer to framebuffer + mbox[29] = 0; // size + + // Get pitch + mbox[30] = 0x40008; + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // pitch + + mbox[34] = MBOX_TAG_LAST; + + // Call the mailbox + if (mailbox_call(MBOX_CH_PROP, mbox) && mbox[20] == 32 && mbox[28] != 0) { + mbox[28] &= 0x3FFFFFFF; // Convert GPU address to ARM address + + width = mbox[5]; // get actual physical width + height = mbox[6]; // get actual physical height + pitch = mbox[33]; // get number of bytes per line + isrgb = mbox[24]; // get the actual channel order + lfb = (void *)((unsigned long)mbox[28]); + + // Use the framebuffer (width, height, pitch, isrgb, lfb) + return 0; // Success + } + + return -1; // Failure +} + +int fb_write(size_t pos, const void *buf, size_t len) { + if (len + pos > pitch * height) { + uart_puts("[ERROR] Attempted write out of bounds\r\n"); + return -1; + } + + memcpy(lfb + pos, buf, len); + return 0; +} diff --git a/lab7/bsp/fbdev.h b/lab7/bsp/fbdev.h new file mode 100644 index 000000000..6abf3e873 --- /dev/null +++ b/lab7/bsp/fbdev.h @@ -0,0 +1,16 @@ +#ifndef _FBDEV_H_ +#define _FBDEV_H_ + +#include + +struct framebuffer_info { + unsigned int width; + unsigned int height; + unsigned int pitch; + unsigned int isrgb; +}; + +int fb_init(); +int fb_write(size_t pos, const void *buf, size_t len); + +#endif diff --git a/lab7/bsp/gpio.h b/lab7/bsp/gpio.h new file mode 100644 index 000000000..6c1c10e63 --- /dev/null +++ b/lab7/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/lab7/bsp/gpu.c b/lab7/bsp/gpu.c new file mode 100644 index 000000000..e0643a273 --- /dev/null +++ b/lab7/bsp/gpu.c @@ -0,0 +1,29 @@ +#include +/* 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/lab7/bsp/irq.h b/lab7/bsp/irq.h new file mode 100644 index 000000000..5ccd3f84c --- /dev/null +++ b/lab7/bsp/irq.h @@ -0,0 +1,58 @@ +// 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. + +#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/lab7/bsp/linker.ld b/lab7/bsp/linker.ld new file mode 100644 index 000000000..54b6e7698 --- /dev/null +++ b/lab7/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/lab7/bsp/mailbox.c b/lab7/bsp/mailbox.c new file mode 100644 index 000000000..76826d874 --- /dev/null +++ b/lab7/bsp/mailbox.c @@ -0,0 +1,75 @@ +#include +#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/lab7/bsp/mailbox.h b/lab7/bsp/mailbox.h new file mode 100644 index 000000000..2eebf7fbe --- /dev/null +++ b/lab7/bsp/mailbox.h @@ -0,0 +1,31 @@ +#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 + +// lab7 +#define MBOX_REQUEST 0 +#define MBOX_CH_PROP 8 +#define MBOX_TAG_LAST 0 + +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/lab7/bsp/ramfs.c b/lab7/bsp/ramfs.c new file mode 100644 index 000000000..f66202bfe --- /dev/null +++ b/lab7/bsp/ramfs.c @@ -0,0 +1,96 @@ +#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; + + uint32_t headsize = align4(sizeof(cpio_t) + namesize); + uint32_t datasize = align4(filesize); + file_list.file_data[file_list.file_count] = (void *)fptr + headsize; + file_list.file_count++; + + 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/lab7/bsp/ramfs.h b/lab7/bsp/ramfs.h new file mode 100644 index 000000000..95ba748d8 --- /dev/null +++ b/lab7/bsp/ramfs.h @@ -0,0 +1,41 @@ +#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; + char* file_data[MAX_FILES]; +} 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/lab7/bsp/reboot.c b/lab7/bsp/reboot.c new file mode 100644 index 000000000..43b575bec --- /dev/null +++ b/lab7/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/lab7/bsp/reboot.h b/lab7/bsp/reboot.h new file mode 100644 index 000000000..8522085c8 --- /dev/null +++ b/lab7/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/lab7/bsp/sched.S b/lab7/bsp/sched.S new file mode 100644 index 000000000..323fe3998 --- /dev/null +++ b/lab7/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/lab7/bsp/start.S b/lab7/bsp/start.S new file mode 100644 index 000000000..8ea5717eb --- /dev/null +++ b/lab7/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/lab7/bsp/sysregs.h b/lab7/bsp/sysregs.h new file mode 100644 index 000000000..2e691f2c1 --- /dev/null +++ b/lab7/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/lab7/bsp/timer.c b/lab7/bsp/timer.c new file mode 100644 index 000000000..811b30d18 --- /dev/null +++ b/lab7/bsp/timer.c @@ -0,0 +1,47 @@ +#include +#include + +void core_timer_enable() { +#ifdef TIMER_DEBUG + puts("\n[core_timer_enable] "); +#endif + 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() { +#ifdef TIMER_DEBUG + puts("\n[core_timer_disable] "); +#endif + 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/lab7/bsp/traps.h b/lab7/bsp/traps.h new file mode 100644 index 000000000..0b6650e75 --- /dev/null +++ b/lab7/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/lab7/bsp/uart.c b/lab7/bsp/uart.c new file mode 100644 index 000000000..b8ddbb28c --- /dev/null +++ b/lab7/bsp/uart.c @@ -0,0 +1,171 @@ +#include +#include + +#include // for enable uart irq +#include + +#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() { +#ifdef IRQ_DEBUG + uart_puts("[INFO] enable tx interrupt\n"); +#endif /* ifdef IRQ_DEBUG */ + + *AUX_MU_IER |= (1 << 1); +} + +void uart_disable_tx_interrupt() { +#ifdef IRQ_DEBUG + uart_puts("[INFO] disable tx interrupt\n"); +#endif /* ifdef IRQ_DEBUG */ + *AUX_MU_IER &= ~(1 << 1); +} + +void uart_enable_rx_interrupt() { +#ifdef IRQ_DEBUG + uart_puts("[INFO] enable rx interrupt\n"); +#endif /* ifdef IRQ_DEBUG */ + *AUX_MU_IER |= (1 << 0); +} + +void uart_disable_rx_interrupt() { +#ifdef IRQ_DEBUG + uart_puts("[INFO] disable rx interrupt\n"); +#endif /* ifdef IRQ_DEBUG */ + *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/lab7/bsp/uart.h b/lab7/bsp/uart.h new file mode 100644 index 000000000..4e7813532 --- /dev/null +++ b/lab7/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/lab7/bsp/utils.c b/lab7/bsp/utils.c new file mode 100644 index 000000000..373780081 --- /dev/null +++ b/lab7/bsp/utils.c @@ -0,0 +1,57 @@ +#include +#include + +#include + +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/lab7/config.txt b/lab7/config.txt new file mode 100644 index 000000000..80c90a4ec --- /dev/null +++ b/lab7/config.txt @@ -0,0 +1 @@ +initramfs initramfs.cpio 0x20000000 diff --git a/lab7/file_sender.py b/lab7/file_sender.py new file mode 100644 index 000000000..39d7e57bc --- /dev/null +++ b/lab7/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/lab7/initramfs.cpio b/lab7/initramfs.cpio new file mode 100644 index 000000000..68a0ab3c7 Binary files /dev/null and b/lab7/initramfs.cpio differ diff --git a/lab7/kernel/Makefile b/lab7/kernel/Makefile new file mode 100644 index 000000000..0fe7df059 --- /dev/null +++ b/lab7/kernel/Makefile @@ -0,0 +1,48 @@ +CFLAGS = -Wall -ffreestanding -nostdinc -nostdlib -nostartfiles -fno-stack-protector -g -mgeneral-regs-only +C = aarch64-linux-gnu + +INCLUDE_PATH ?= ../ +BUILD_DIR ?= build +CFLAGS += -I$(INCLUDE_PATH) + +LIB_PATH ?= ../lib +BSP_PATH ?= ../bsp +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/lab7/kernel/buddy.c b/lab7/kernel/buddy.c new file mode 100644 index 000000000..57a101185 --- /dev/null +++ b/lab7/kernel/buddy.c @@ -0,0 +1,269 @@ +#include "io.h" +#include "memory.h" + +#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 + puts("Buddy size: "); + print_d(buddy_size); + puts("\nFrom "); + print_h((uint64_t)start); + puts(" to "); + print_h((uint64_t)end); + puts("\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 + puts("Number of pages: "); + print_d(num_of_pages); + puts("\n"); +#endif + + mem_map = simple_malloc(num_of_pages * sizeof(page_t)); + + if (mem_map == NULL) { + puts("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]; + + // puts("\ni: "); + // print_d(cur_order); + // puts("----------\n"); + // puts("Page: "); + // print_h((uint64_t)get_addr_by_page(page)); + // puts("\nBuddy: "); + // print_h((uint64_t)get_addr_by_page(buddy)); + // puts("\nbuddy_order: "); + // print_d(buddy->order); + // puts("\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; + // puts("\nPush page: "); + // print_h((uint64_t)get_addr_by_page(page)); + // puts("\nTo order "); + // print_d(cur_order); + // puts("\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]; + puts("Order "); + print_d(i); + puts(": "); + while (page) { + print_h((uint64_t)get_addr_by_page(page)); + puts(" -> "); + page = page->next; + } + puts("NULL\n"); + } +} + +phys_addr_t get_addr_by_page(page_t *page) { + // puts("Get addr by page: "); + // print_h((uint64_t)(((page - mem_map) << PAGE_SHIFT) + (void *)start)); + // puts("\n"); + return (phys_addr_t)((page - mem_map) << PAGE_SHIFT); +} + +page_t *get_page_by_addr(phys_addr_t addr) { + // puts("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) { + puts("Reserve memory: "); + print_h((uint64_t)start); + puts(" - "); + print_h((uint64_t)end); + puts("\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 + puts("\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 + puts("\nPage fully reserved\n"); + print_h((uint64_t)page_addr); + puts(" - "); + print_h((uint64_t)page_end); + puts("\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 + // puts("\nPage out of range\n"); + } else { // split the overlapping page using buddy + // puts("\nFind buddy\n"); + // puts("\nPage: "); + // print_h((uint64_t)page_addr); + // puts("\n"); + // puts("\nCur order: "); + // print_d(cur_order); + // puts("\n"); + + uint32_t buddy_pfn = (page - mem_map) ^ (1 << (cur_order - 1)); + page_t *buddy = &mem_map[buddy_pfn]; + +#ifdef MEM_DEBUG + puts("Split page: "); + print_h((uint64_t)page_addr); + puts(", "); + print_h((uint64_t)get_addr_by_page(buddy)); + puts(", "); + print_h((uint64_t)buddy_pfn); + puts("\n"); +#endif + +// remove the original page from the free area +#ifdef MEM_DEBUG + puts("\nRemove page "); + print_h((uint64_t)get_addr_by_page(page)); + puts(" from order "); + print_d(cur_order); + puts("\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); + } + + // puts("\nNext page\n"); + // print_h((uint64_t)get_addr_by_page(next_page)); + + page = next_page; + } + } +} diff --git a/lab7/kernel/commands.h b/lab7/kernel/commands.h new file mode 100644 index 000000000..bd49b9491 --- /dev/null +++ b/lab7/kernel/commands.h @@ -0,0 +1,31 @@ +#ifndef _COMMAND_H_ +#define _COMMAND_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; + +extern command_t rootfs_ls_command; +extern command_t rootfs_cat_command; +extern command_t rootfs_mkdir_command; +extern command_t rootfs_cd_command; +extern command_t rootfs_cwd_command; +extern command_t rootfs_touch_command; + +#endif diff --git a/lab7/kernel/commands/async_io.c b/lab7/kernel/commands/async_io.c new file mode 100644 index 000000000..fb1bdb593 --- /dev/null +++ b/lab7/kernel/commands/async_io.c @@ -0,0 +1,39 @@ + +#include +#include +#include + +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/lab7/kernel/commands/exec.c b/lab7/kernel/commands/exec.c new file mode 100644 index 000000000..cb28d25c4 --- /dev/null +++ b/lab7/kernel/commands/exec.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void _exec_command(int argc, char **argv) { + if (argc < 2) { + puts("\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) { + puts("[ERROR] File not found: "); + puts(file_name); + puts("\n"); + return; + } + puts("\n[INFO] Executing file: "); + puts(file_name); + puts("\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); + // puts(buf); + + // preempt_disable(); + // get_current()->state = TASK_STOPPED; + // puts("Enabling user to physical timer\n"); + // enable_user_to_physical_timer(); + // puts("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/lab7/kernel/commands/hello.c b/lab7/kernel/commands/hello.c new file mode 100644 index 000000000..4e698c1ab --- /dev/null +++ b/lab7/kernel/commands/hello.c @@ -0,0 +1,8 @@ +#include +#include + +void _hello_command(int argc, char **argv) { puts("\nHello World!"); } + +command_t hello_command = {.name = "hello", + .description = "print Hello World!", + .function = &_hello_command}; diff --git a/lab7/kernel/commands/mailbox.c b/lab7/kernel/commands/mailbox.c new file mode 100644 index 000000000..8c1564887 --- /dev/null +++ b/lab7/kernel/commands/mailbox.c @@ -0,0 +1,23 @@ +#include +#include +#include + +void _mailbox_command(int argc, char **argv) { + puts("\nMailbox info :\n"); + unsigned int r = get_board_revision(); + puts("board revision : "); + print_h(r); + puts("\r\n"); + unsigned int base_addr, size; + get_memory_info(&base_addr, &size); + puts("ARM memory base address : "); + print_h(base_addr); + puts("\r\n"); + puts("ARM memory size : "); + print_h(size); + puts("\r\n"); +} + +command_t mailbox_command = {.name = "mailbox", + .description = "print hardware info", + .function = &_mailbox_command}; diff --git a/lab7/kernel/commands/memory.c b/lab7/kernel/commands/memory.c new file mode 100644 index 000000000..2fae34175 --- /dev/null +++ b/lab7/kernel/commands/memory.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +void _test_malloc_command(int argc, char **argv) { + puts("\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) { + puts("\nMalloc failed! "); + } + + puts("\nMalloc address: "); + print_h((uintptr_t)ptr); + puts("\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) { + puts("\nUsage: test_kmalloc \n"); + return; + } + + int size = atoi(argv[1]); + puts("\nMalloc size: "); + puts(argv[1]); + void *ptr = kmalloc(size); + if (ptr == 0) { + puts("\nMalloc failed! "); + } + puts("\nMalloc address: "); + print_h((uint64_t)ptr); + puts(", "); + print_d((uint64_t)ptr); + puts("\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) { + puts("\nUsage: test_kfree
\n"); + return; + } + uintptr_t addr = atoi(argv[1]); + kfree((void *)addr); + puts("\nFree address: "); + print_h(addr); + puts("\n"); + print_free_areas(); +} + +command_t test_kfree_command = {.name = "test_kfree", + .description = "test kfree", + .function = &_test_kfree_command}; diff --git a/lab7/kernel/commands/multi_thread.c b/lab7/kernel/commands/multi_thread.c new file mode 100644 index 000000000..5ab926e9a --- /dev/null +++ b/lab7/kernel/commands/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/lab7/kernel/commands/ramfs.c b/lab7/kernel/commands/ramfs.c new file mode 100644 index 000000000..0b9a3499a --- /dev/null +++ b/lab7/kernel/commands/ramfs.c @@ -0,0 +1,49 @@ + +#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++) { + puts("\n"); + puts(file_list->file_names[i]); + puts(" "); + print_d(file_list->file_sizes[i]); + puts(" bytes"); + } + puts("\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) { + puts("\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) { + puts("\n"); + for (int i = 0; i < file_size - 1; i++) { + print_char(file_contents[i]); + } + } else { + puts("\nFile not found\n"); + } +} + +command_t cat_command = {.name = "cat", + .description = "print the contents of a file", + .function = &_cat_command}; diff --git a/lab7/kernel/commands/reboot.c b/lab7/kernel/commands/reboot.c new file mode 100644 index 000000000..9894b0a82 --- /dev/null +++ b/lab7/kernel/commands/reboot.c @@ -0,0 +1,12 @@ +#include +#include +#include + +void _reboot_command(int argc, char **argv) { + puts("\nRebooting ...\n"); + reset(200); +} + +command_t reboot_command = {.name = "reboot", + .description = "reboot the device", + .function = &_reboot_command}; diff --git a/lab7/kernel/commands/rootfs.c.old b/lab7/kernel/commands/rootfs.c.old new file mode 100644 index 000000000..671f06091 --- /dev/null +++ b/lab7/kernel/commands/rootfs.c.old @@ -0,0 +1,43 @@ +#include +#include +#include +#include + +static void rootfs_ls(int argc, char** argv) { + char *pathname; + if (argc == 1) { + pathname = get_current()->cwd; + } else if (argc == 2) { + pathname = argv[1]; + } else { + puts("\nUsage: ls [path]"); + return; + } + + struct vnode* vnode; + int ret = vfs_lookup(pathname, &vnode); + + if (ret != 0) { + puts("\r\n[ERROR] Cannot find the directory"); + return; + } + if (vnode->mount != NULL) { // check if it is a mount point + ret = vnode->mount->root->v_ops->list(vnode->mount->root); + } else { + ret = vnode->v_ops->list(vnode); + } + if (ret != 0) { + puts("\r\n[ERROR] Cannot list the directory"); + return; + } +} + +command_t rootfs_ls_command = { + .name = "ls", + .function = rootfs_ls, + .description = "List files in the root filesystem", +} command_t rootfs_cat_command; +command_t rootfs_mkdir_command; +command_t rootfs_cd_command; +command_t rootfs_cwd_command; +command_t rootfs_touch_command; diff --git a/lab7/kernel/commands/set_timeout.c b/lab7/kernel/commands/set_timeout.c new file mode 100644 index 000000000..557d9b25e --- /dev/null +++ b/lab7/kernel/commands/set_timeout.c @@ -0,0 +1,27 @@ +#include +#include +#include + +void print_timeout(int delay) { + puts("\nTimeout: "); + print_d((const int)delay); + puts("s\n"); +} + +void _set_timeout(int argc, char **argv) { + if (argc < 2) { + puts("\nUsage: set_timeout \n"); + return; + } + + puts("\nSetting timeout: "); + print_d(atoi(argv[1])); + puts("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/lab7/kernel/console.c b/lab7/kernel/console.c new file mode 100644 index 000000000..bacf231b0 --- /dev/null +++ b/lab7/kernel/console.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include + +static command_t commands[64]; +static int num_commands; + +static void read_command(char *); +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) { + puts("\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) { + puts("\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() { + puts("\n"); + + puts("help : print this help menu\n"); + for (int i = 0; i < num_commands; i++) { + puts(commands[i].name); + puts(" : "); + puts(commands[i].description); + puts("\n"); + } +} diff --git a/lab7/kernel/console.h b/lab7/kernel/console.h new file mode 100644 index 000000000..bf6df2a85 --- /dev/null +++ b/lab7/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/lab7/kernel/device_tree.c b/lab7/kernel/device_tree.c new file mode 100644 index 000000000..6a621b28f --- /dev/null +++ b/lab7/kernel/device_tree.c @@ -0,0 +1,91 @@ +#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); + puts("[INFO] Dtb loaded address: "); + uart_hex((uintptr_t)header); + puts("\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); +#ifdef FDT_DEBUG + puts("\n[fdt_traverse] "); + puts("\nptr / strings + totalsize: "); + print_h((uint32_t)ptr); + puts(" / "); + print_h((uint32_t)(strings + totalsize)); + puts(" / "); + print_h(token); +#endif + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + // puts((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 FDT_DEBUG + puts("\n[fdt_traverse] "); + puts((char *)strings + nameoff); +#endif + + // TODO: 這個寫法很怪應為很大程度限制了只能傳 ramfs 的 callback + 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/lab7/kernel/device_tree.h b/lab7/kernel/device_tree.h new file mode 100644 index 000000000..8def99cd5 --- /dev/null +++ b/lab7/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/lab7/kernel/fs/dev_uart.c b/lab7/kernel/fs/dev_uart.c new file mode 100644 index 000000000..bb980c9bc --- /dev/null +++ b/lab7/kernel/fs/dev_uart.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + +struct file_operations dev_uart_operations = {dev_uart_write, dev_uart_read, dev_uart_open, dev_uart_close, (void *)dev_uart_deny, (void *)dev_uart_deny}; + +int init_dev_uart() +{ + return register_dev(&dev_uart_operations); +} + +int dev_uart_write(struct file *file, const void *buf, size_t len) +{ + /*printf("dev_uart_write\n");*/ + const char *cbuf = buf; + for (int i = 0; i < len; i++) + { + uart_send(cbuf[i]); + } + return len; +} + +int dev_uart_read(struct file *file, void *buf, size_t len) +{ + char *cbuf = buf; + for (int i = 0; i < len; i++) + { + cbuf[i] = uart_recv(); + } + return len; +} + +int dev_uart_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = &dev_uart_operations; + return 0; +} + +int dev_uart_close(struct file *file) +{ + kfree(file); + return 0; +} + +int dev_uart_deny() +{ + return -1; +} diff --git a/lab7/kernel/fs/dev_uart.h b/lab7/kernel/fs/dev_uart.h new file mode 100644 index 000000000..6af5096b9 --- /dev/null +++ b/lab7/kernel/fs/dev_uart.h @@ -0,0 +1,15 @@ +#ifndef _DEV_UART_H_ +#define _DEV_UART_H_ + +#include +#include + +int init_dev_uart(); + +int dev_uart_write(struct file *file, const void *buf, size_t len); +int dev_uart_read(struct file *file, void *buf, size_t len); +int dev_uart_open(struct vnode *file_node, struct file **target); +int dev_uart_close(struct file *file); +int dev_uart_deny(); + +#endif diff --git a/lab7/kernel/fs/fdtable.h b/lab7/kernel/fs/fdtable.h new file mode 100644 index 000000000..9046ced26 --- /dev/null +++ b/lab7/kernel/fs/fdtable.h @@ -0,0 +1,21 @@ +#ifndef _FDTABLE_H_ +#define _FDTABLE_H_ + +# define MAX_FD 32 + +#include + +struct file +{ + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +}; + +struct fdtable { + int count; + struct file* fds[MAX_FD]; +}; + +#endif diff --git a/lab7/kernel/fs/initramfs.h b/lab7/kernel/fs/initramfs.h new file mode 100644 index 000000000..68a68e0f3 --- /dev/null +++ b/lab7/kernel/fs/initramfs.h @@ -0,0 +1,36 @@ +#ifndef _INITRAMFS_H_ +#define _INITRAMFS_H_ + +#include +#include + +#define INITRAMFS_MAX_DIR_ENTRY 100 + +// initramfs basically is same as tmpfs, but it is read-only + +struct initramfs_inode +{ + enum fsnode_type type; + char name[64]; + struct vnode *entry[INITRAMFS_MAX_DIR_ENTRY]; + char *data; + size_t datasize; +}; + +int register_initramfs(); +int initramfs_setup_mount(struct filesystem *fs, struct mount *_mount); + +int initramfs_write(struct file *file, const void *buf, size_t len); +int initramfs_read(struct file *file, void *buf, size_t len); +int initramfs_open(struct vnode *file_node, struct file **target); +int initramfs_close(struct file *file); +long initramfs_lseek64(struct file *file, long offset, int whence); +long initramfs_getsize(struct vnode *vd); + +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum fsnode_type type); + +#endif /* _INITRAMFS_H_ */ diff --git a/lab7/kernel/fs/tmpfs.c b/lab7/kernel/fs/tmpfs.c new file mode 100644 index 000000000..a9a086b50 --- /dev/null +++ b/lab7/kernel/fs/tmpfs.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include + +struct file_operations tmpfs_file_operations = {tmpfs_write, tmpfs_read, + tmpfs_open, tmpfs_close, + tmpfs_lseek64, tmpfs_getsize}; +struct vnode_operations tmpfs_vnode_operations = {tmpfs_lookup, tmpfs_create, + tmpfs_mkdir}; + + +int register_tmpfs() { + struct filesystem fs; + fs.name = "tmpfs"; + fs.setup_mount = tmpfs_setup_mount; + return register_filesystem(&fs); +} + +int tmpfs_setup_mount(struct filesystem *fs, struct mount *_mount) { + _mount->fs = fs; + _mount->root = tmpfs_create_vnode(0, DIR); + return 0; +} + +struct vnode *tmpfs_create_vnode(struct mount *_mount, enum fsnode_type type) { + struct vnode *v = kmalloc(sizeof(struct vnode)); + v->f_ops = &tmpfs_file_operations; + v->v_ops = &tmpfs_vnode_operations; + v->mount = 0; + struct tmpfs_inode *inode = kmalloc(sizeof(struct tmpfs_inode)); + memset(inode, 0, sizeof(struct tmpfs_inode)); + inode->type = type; + inode->data = kmalloc(0x1000); + v->internal = inode; + return v; +} + +// file operations +int tmpfs_write(struct file *file, const void *buf, size_t len) { + struct tmpfs_inode *inode = file->vnode->internal; + // write from f_pos + memcpy(inode->data + file->f_pos, buf, len); + // update f_pos and size + file->f_pos += len; + if (inode->datasize < file->f_pos) inode->datasize = file->f_pos; + return len; +} + +int tmpfs_read(struct file *file, void *buf, size_t len) { + struct tmpfs_inode *inode = file->vnode->internal; + // if buffer overflow, shrink the request read length + // read from f_pos + if (len + file->f_pos > inode->datasize) { + len = inode->datasize - file->f_pos; + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += inode->datasize - file->f_pos; + return len; + } else { + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += len; + return len; + } + return -1; +} + +int tmpfs_open(struct vnode *file_node, struct file **target) { + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +} + +int tmpfs_close(struct file *file) { + kfree(file); + return 0; +} + +long tmpfs_lseek64(struct file *file, long offset, int whence) { + if (whence == SEEK_SET) { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// vnode operations +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, + const char *component_name) { + struct tmpfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + // BFS search tree + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode) break; + struct tmpfs_inode *inode = vnode->internal; + if (strcmp(component_name, inode->name) == 0) { + *target = vnode; + return 0; + } + } + return -1; +} + +// file ops +int tmpfs_create(struct vnode *dir_node, struct vnode **target, + const char *component_name) { + struct tmpfs_inode *inode = dir_node->internal; + if (inode->type != DIR) { + puts("[ERROR] tmpfs create not DIR\r\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) { + if (!inode->entry[child_idx]) break; + struct tmpfs_inode *child_inode = inode->entry[child_idx]->internal; + if (strcmp(child_inode->name, component_name) == 0) { + puts("[ERROR] tmpfs create file exists\r\n"); + return -1; + } + } + + if (child_idx > MAX_DIR_ENTRY) { + puts("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) { + puts("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode *_vnode = tmpfs_create_vnode(0, FILE); + inode->entry[child_idx] = _vnode; + + struct tmpfs_inode *newinode = _vnode->internal; + strcpy(newinode->name, component_name); + + *target = _vnode; + return 0; +} + +// dir ops +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, + const char *component_name) { + struct tmpfs_inode *inode = dir_node->internal; + + if (inode->type != DIR) { + puts("[ERROR] tmpfs mkdir not DIR\r\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) { + if (!inode->entry[child_idx]) { + break; + } + } + + if (child_idx > MAX_DIR_ENTRY) { + puts("[ERROR] DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) { + puts("[ERROR] FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode *_vnode = tmpfs_create_vnode(0, DIR); + inode->entry[child_idx] = _vnode; + + struct tmpfs_inode *newinode = _vnode->internal; + strcpy(newinode->name, component_name); + + *target = _vnode; + return 0; +} + +long tmpfs_getsize(struct vnode *vd) { + struct tmpfs_inode *inode = vd->internal; + return inode->datasize; +} diff --git a/lab7/kernel/fs/tmpfs.h b/lab7/kernel/fs/tmpfs.h new file mode 100644 index 000000000..c34ed6c56 --- /dev/null +++ b/lab7/kernel/fs/tmpfs.h @@ -0,0 +1,31 @@ +#ifndef _TMPFS_H_ +#define _TMPFS_H_ + +#include + +struct tmpfs_inode +{ + enum fsnode_type type; + char name[MAX_FILE_NAME]; + struct vnode *entry[MAX_DIR_ENTRY]; + char *data; + size_t datasize; +}; + +int register_tmpfs(); +int tmpfs_setup_mount(struct filesystem *fs, struct mount *_mount); + +int tmpfs_write(struct file *file, const void *buf, size_t len); +int tmpfs_read(struct file *file, void *buf, size_t len); +int tmpfs_open(struct vnode *file_node, struct file **target); +int tmpfs_close(struct file *file); +long tmpfs_lseek64(struct file *file, long offset, int whence); +long tmpfs_getsize(struct vnode *vd); + +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); + +struct vnode *tmpfs_create_vnode(struct mount *_mount, enum fsnode_type type); + +#endif /* _TMPFS_H_ */ diff --git a/lab7/kernel/fs/vfs.c b/lab7/kernel/fs/vfs.c new file mode 100644 index 000000000..927154595 --- /dev/null +++ b/lab7/kernel/fs/vfs.c @@ -0,0 +1,266 @@ + +#include +#include +#include +#include +#include +#include +#include + +struct mount *rootfs; +struct filesystem reg_fs[MAX_FS_REG]; +struct file_operations reg_dev[MAX_DEV_REG]; + +void init_rootfs() { + // cannot use vfs_mount because starting point is vfs_root, and it has + // nothing yet will mount at rootfs -> mount -> root instead of rootfs -> + // root + int idx = register_tmpfs(); + rootfs = kmalloc(sizeof(struct mount)); + reg_fs[idx].setup_mount(®_fs[idx], rootfs); + + vfs_mkdir("/initramfs"); + register_initramfs(); + vfs_mount("/initramfs", "initramfs"); + + vfs_mkdir("/dev"); + + int uart_id = init_dev_uart(); + vfs_mknod("/dev/uart", uart_id); + printf("[INFO] uart_id: %d\n", uart_id); +} + +int register_filesystem(struct filesystem *fs) { + for (int i = 0; i < MAX_FS_REG; i++) { + if (!reg_fs[i].name) { + reg_fs[i].name = fs->name; + reg_fs[i].setup_mount = fs->setup_mount; + return i; + } + } + return -1; +} + +int register_dev(struct file_operations *fo) { + for (int i = 0; i < MAX_FS_REG; i++) { + if (!reg_dev[i].open) { + // return unique id for the assigned device + reg_dev[i].open = fo->open; + reg_dev[i].read = fo->read; + reg_dev[i].write = fo->write; + reg_dev[i].close = fo->close; + + return i; + } + } + return -1; +} + +struct filesystem *find_filesystem(const char *fs_name) { + for (int i = 0; i < MAX_FS_REG; i++) { + if (strcmp(reg_fs[i].name, fs_name) == 0) { + return ®_fs[i]; + } + } + return 0; +} + +// file ops +int vfs_open(const char *pathname, int flags, struct file **target) { + // 1. Lookup pathname + // 2. Create a new file if O_CREAT is specified in flags and vnode not found + struct vnode *node; + if (vfs_lookup(pathname, &node) != 0 && (flags & O_CREAT)) { + // grep all of the directory path + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) { + if (pathname[i] == '/') { + last_slash_idx = i; + } + } + + char dirname[MAX_PATH_NAME + 1]; + strcpy(dirname, pathname); + dirname[last_slash_idx] = 0; + // update dirname to node + if (vfs_lookup(dirname, &node) != 0) { + puts("[ERROR] cannot create no dirname\r\n"); + return -1; + } + // create a new file node on node, &node is new file, 3rd arg is + // filename + node->v_ops->create(node, &node, pathname + last_slash_idx + 1); + *target = kmalloc(sizeof(struct file)); + // attach opened file on the new node + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + + // 2. Create a new file handle for this vnode if found. + // attach opened file on the node + *target = kmalloc(sizeof(struct file)); + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; +} + +// file ops +int vfs_close(struct file *file) { + // 1. release the file handle + // 2. Return error code if fails + file->f_ops->close(file); + return 0; +} + +// file ops +int vfs_write(struct file *file, const void *buf, size_t len) { + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file, buf, len); +} + +// file ops +int vfs_read(struct file *file, void *buf, size_t len) { + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 2. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +// file ops +int vfs_mkdir(const char *pathname) { + char dirname[MAX_PATH_NAME] = {}; // before add folder + char newdirname[MAX_PATH_NAME] = {}; // after add folder + + // search for last directory + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) { + if (pathname[i] == '/') { + last_slash_idx = i; + } + } + + memcpy(dirname, pathname, last_slash_idx); + strcpy(newdirname, pathname + last_slash_idx + 1); + + // create new directory if upper directory is found + struct vnode *node; + if (vfs_lookup(dirname, &node) == 0) { + // node is the old dir, &node is new dir + node->v_ops->mkdir(node, &node, newdirname); + return 0; + } + + puts("[ERROR] vfs_MAX_OPEN_FILEmkdir cannot find pathname"); + return -1; +} + +int vfs_mount(const char *target, const char *filesystem) { + struct vnode *dirnode; + // search for the target filesystem + struct filesystem *fs = find_filesystem(filesystem); + if (!fs) { + printf("[ERROR] vfs_mount cannot find filesystem\r\n"); + return -1; + } + + if (vfs_lookup(target, &dirnode) == -1) { + printf("[ERROR] vfs_mount cannot find dir\r\n"); + return -1; + } else { + // mount fs on dirnode + dirnode->mount = kmalloc(sizeof(struct mount)); + fs->setup_mount(fs, dirnode->mount); + } + return 0; +} + +int vfs_lookup(const char *pathname, struct vnode **target) { + // if no path input, return root + if (strlen(pathname) == 0) { + *target = rootfs->root; + return 0; + } + + struct vnode *dirnode = rootfs->root; + char component_name[MAX_FILE_NAME + 1] = {}; + int c_idx = 0; + // deal with directory + for (int i = 1; i < strlen(pathname); i++) { + if (pathname[i] == '/') { + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) + return -1; + // redirect to mounted filesystem + while (dirnode->mount) { + dirnode = dirnode->mount->root; + } + c_idx = 0; + } else { + component_name[c_idx++] = pathname[i]; + } + } + + // deal with file + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) + return -1; + // redirect to mounted filesystem + while (dirnode->mount) { + dirnode = dirnode->mount->root; + } + // return file's vnode + *target = dirnode; + + return 0; +} + +int vfs_mknod(char *pathname, int id) { + struct file *f = kmalloc(sizeof(struct file)); + // create leaf and its file operations + vfs_open(pathname, O_CREAT, &f); + f->vnode->f_ops = ®_dev[id]; + vfs_close(f); + return 0; +} + +char *get_absolute_path(char *path, char *cwd) { + // if relative path -> add root path + if (path[0] != '/') { + char tmp[MAX_PATH_NAME]; + strcpy(tmp, cwd); + if (strcmp(cwd, "/") != 0) strcat(tmp, "/"); + strcat(tmp, path); + strcpy(path, tmp); + } + + char absolute_path[MAX_PATH_NAME + 1] = {}; + int idx = 0; + for (int i = 0; i < strlen(path); i++) { + // handle .. + if (path[i] == '/' && path[i + 1] == '.' && path[i + 2] == '.') { + for (int j = idx; j >= 0; j--) { + if (absolute_path[j] == '/') { + absolute_path[j] = 0; + idx = j; + } + } + i += 2; + continue; + } + + // handle . + if (path[i] == '/' && path[i + 1] == '.') { + i++; + continue; + } + + absolute_path[idx++] = path[i]; + } + absolute_path[idx] = 0; + + return strcpy(path, absolute_path); +} diff --git a/lab7/kernel/fs/vfs.h b/lab7/kernel/fs/vfs.h new file mode 100644 index 000000000..462cbee68 --- /dev/null +++ b/lab7/kernel/fs/vfs.h @@ -0,0 +1,80 @@ +#ifndef _VFS_H_ +#define _VFS_H_ + +#include +#include + +#define MAX_PATH_NAME 255 +#define O_CREAT 100 +#define O_RDWR 000 +#define SEEK_SET 0 +#define MAX_FS_REG 0x50 +#define MAX_DEV_REG 0x10 +#define MAX_FILE_NAME 15 +#define MAX_DIR_ENTRY 16 +#define MAX_FILE_SIZE 4096 + +enum fsnode_type +{ + DIR, + FILE +}; + + +struct vnode +{ + struct mount *mount; // Superblock : represents mounted fs + struct vnode_operations *v_ops; // inode & dentry Ops: represents kernel methods for vnode + struct file_operations *f_ops; // file Ops : represents process methods for opened file + void *internal; // vnode itself : directly point to fs's vnode +}; + +struct mount +{ + struct vnode *root; + struct filesystem *fs; +}; + +struct filesystem +{ + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +}; + +struct file_operations +{ + int (*write)(struct file *file, const void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + long (*lseek64)(struct file *file, long offset, int whence); + long (*getsize)(struct vnode *vd); +}; + +struct vnode_operations +{ + int (*lookup)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, + const char *component_name); +}; + +int register_filesystem(struct filesystem *fs); +int register_dev(struct file_operations* fo); +struct filesystem *find_filesystem(const char *fs_name); +int vfs_open(const char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, const void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); +int vfs_mkdir(const char *pathname); +int vfs_mount(const char *target, const char *filesystem); +int vfs_lookup(const char *pathname, struct vnode **target); +int vfs_mknod(char* pathname, int id); + +void init_rootfs(); +void vfs_test(); +char* get_absolute_path(char* path,char* xwd); + +#endif /* _VFS_H_ */ diff --git a/lab7/kernel/initramfs.c b/lab7/kernel/initramfs.c new file mode 100644 index 000000000..8a973d7bd --- /dev/null +++ b/lab7/kernel/initramfs.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include + +struct file_operations initramfs_file_operations = { + initramfs_write, initramfs_read, initramfs_open, + initramfs_close, initramfs_lseek64, initramfs_getsize}; +struct vnode_operations initramfs_vnode_operations = { + initramfs_lookup, initramfs_create, initramfs_mkdir}; + +int register_initramfs() { + struct filesystem fs; + fs.name = "initramfs"; + fs.setup_mount = initramfs_setup_mount; + return register_filesystem(&fs); +} + +int initramfs_setup_mount(struct filesystem *fs, struct mount *_mount) { + _mount->fs = fs; + _mount->root = initramfs_create_vnode(0, DIR); + // create entry under _mount, cpio files should be attached on it + struct initramfs_inode *ramdir_inode = _mount->root->internal; + + int idx = 0; + + file_list_t *files = ramfs_get_file_list(); + + for (int i = 0; i < files->file_count; i++) { + // only support file (no dir) + struct vnode *filevnode = initramfs_create_vnode(0, FILE); + struct initramfs_inode *fileinode = filevnode->internal; + /*printf("initramfs: create file %s\n", files->file_names[i]);*/ + /*printf("initramfs: size %d\n", files->file_sizes[i]);*/ + strcpy(fileinode->name, files->file_names[i]); + fileinode->data = files->file_data[i]; + fileinode->datasize = files->file_sizes[i]; + /*printf("inode - name %s\n", fileinode->name);*/ + /*printf("inode - size %d\n", fileinode->datasize);*/ + ramdir_inode->entry[idx++] = filevnode; + } + + return 0; +} + +struct vnode *initramfs_create_vnode(struct mount *_mount, + enum fsnode_type type) { + struct vnode *v = kmalloc(sizeof(struct vnode)); + v->f_ops = &initramfs_file_operations; + v->v_ops = &initramfs_vnode_operations; + v->mount = _mount; + struct initramfs_inode *inode = kmalloc(sizeof(struct initramfs_inode)); + memset(inode, 0, sizeof(struct initramfs_inode)); + inode->type = type; + inode->data = NULL; + v->internal = inode; + return v; +} + +// file operations +int initramfs_write(struct file *file, const void *buf, size_t len) { + // read-only + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) { + struct initramfs_inode *inode = file->vnode->internal; + // overflow, shrink size + if (len + file->f_pos > inode->datasize) { + memcpy(buf, inode->data + file->f_pos, inode->datasize - file->f_pos); + file->f_pos += inode->datasize - file->f_pos; + return inode->datasize - file->f_pos; + } else { + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += len; + return len; + } + return -1; +} + +int initramfs_open(struct vnode *file_node, struct file **target) { + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +} + +int initramfs_close(struct file *file) { + kfree(file); + return 0; +} + +long initramfs_lseek64(struct file *file, long offset, int whence) { + if (whence == SEEK_SET) { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// vnode operations +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, + const char *component_name) { + struct initramfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < INITRAMFS_MAX_DIR_ENTRY; child_idx++) { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode) break; + struct initramfs_inode *inode = vnode->internal; + if (strcmp(component_name, inode->name) == 0) { + *target = vnode; + return 0; + } + } + return -1; +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, + const char *component_name) { + // read-only + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, + const char *component_name) { + // read-only + return -1; +} + +long initramfs_getsize(struct vnode *vd) { + struct initramfs_inode *inode = vd->internal; + return inode->datasize; +} diff --git a/lab7/kernel/io.c b/lab7/kernel/io.c new file mode 100644 index 000000000..444d94c9b --- /dev/null +++ b/lab7/kernel/io.c @@ -0,0 +1,51 @@ +#include +#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; // 刪除最後一個字元 + puts("\b \b"); // 在控制台上刪除這個字元,\b + // 是回退一格,空格刪除字元,再一個 \b + // 是將光標回退一格。 + continue; + } + } + s[input_index] = input_char; + input_index += 1; + print_char(input_char); + } + + s[input_index] = 0; +} + +/** + * printf implementation using sprintf + */ +void printf(const char* fmt, ...) +{ + char buffer[1024]; + __builtin_va_list args; + __builtin_va_start(args, fmt); + vsprintf(buffer, fmt, args); + __builtin_va_end(args); + puts(buffer); // Assuming puts writes the string to the standard output and returns the number of characters written +} + +void print_char(const char c) { return uart_send(c); } +void puts(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); + puts(buffer); +} diff --git a/lab7/kernel/io.h b/lab7/kernel/io.h new file mode 100644 index 000000000..671732d73 --- /dev/null +++ b/lab7/kernel/io.h @@ -0,0 +1,12 @@ +#ifndef IO_H +#define IO_H + +char read_c(); +void read_s(char *s); +void printf(const char* fmt, ...); +void print_char(const char); +void puts(const char *); +void print_h(const unsigned long long x); +void print_d(const int x); + +#endif diff --git a/lab7/kernel/irq_entry.c b/lab7/kernel/irq_entry.c new file mode 100644 index 000000000..c904b3af5 --- /dev/null +++ b/lab7/kernel/irq_entry.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include + +void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr) { + puts("spsr_el1: "); + print_h(spsr); + puts("\n elr_el1: "); + print_h(elr); + puts("\n esr_el1: "); + print_h(esr); + puts("\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 11: + sys_open(tf, (char*)tf->x0, tf->x1); + break; + case 12: + sys_close(tf, tf->x0); + break; + case 13: + sys_write(tf, tf->x0, (char *)tf->x1, tf->x2); + break; + case 14: + sys_read(tf, tf->x0, (char *)tf->x1, tf->x2); + break; + case 15: + sys_mkdir(tf, (char *)tf->x0, tf->x1); + break; + case 16: + sys_mount(tf, (char *)tf->x0, (char *)tf->x1, (char *)tf->x2, tf->x3, (void*)tf->x4); + break; + case 17: + sys_chdir(tf, (char *)tf->x0); + break; + case 18: + sys_lseek64(tf, tf->x0, tf->x1, tf->x2); + break; + case 19: + sys_ioctl(tf, tf->x0, tf->x1, (void*)tf->x2); + break; + case 139: + sys_sigreturn(tf); + break; + default: + printf("[ERROR] Invalid system call\n"); + } +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + puts("[ERROR] The exception handler is not implemented\n"); + print_registers(elr, esr, spsr); + while (1); +} + +void irq_entry(trap_frame *tf) { +#ifdef IRQ_DEBUG + uart_puts("IRQ count: "); + uart_hex(count); + uart_puts("\n"); + uart_puts("IRQ CORE0_IRQ_SOURCE: "); + uart_hex(get_irq()); + uart_puts("\n"); + puts("\n[el1_irq_entry] "); + show_irq_debug_msg(type, spsr, elr, esr); +#endif + + 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 { + puts("\r\nUnknown irq"); + } + + do_signal(tf); +} diff --git a/lab7/kernel/irq_entry.h b/lab7/kernel/irq_entry.h new file mode 100644 index 000000000..16b0ccf92 --- /dev/null +++ b/lab7/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/lab7/kernel/kmalloc.c b/lab7/kernel/kmalloc.c new file mode 100644 index 000000000..64130a8ae --- /dev/null +++ b/lab7/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]; + puts("Cache Order "); + print_d(i); + puts(": "); + while (cache != NULL) { + print_h((uint64_t)cache); + puts(" -> "); + cache = cache->next; + } + puts("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/lab7/kernel/lock.c b/lab7/kernel/lock.c new file mode 100644 index 000000000..d1444ce8a --- /dev/null +++ b/lab7/kernel/lock.c @@ -0,0 +1,16 @@ +#include +#include + +static unsigned long long lock_count = 0; +void lock() +{ + disable_irq(); + lock_count++; +} + +void unlock() +{ + lock_count--; + if (lock_count == 0) + enable_irq(); +} diff --git a/lab7/kernel/lock.h b/lab7/kernel/lock.h new file mode 100644 index 000000000..5e44b27ac --- /dev/null +++ b/lab7/kernel/lock.h @@ -0,0 +1,7 @@ +#ifndef LOCK_H +#define LOCK_H + +void lock(); +void unlock(); + +#endif diff --git a/lab7/kernel/main.c b/lab7/kernel/main.c new file mode 100644 index 000000000..90efc12c1 --- /dev/null +++ b/lab7/kernel/main.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void print_boot_timeout(int delay) { + puts("\nBoot Timeout: "); + print_d((const int)delay); + puts("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(); + + // lab7 + init_rootfs(); + + // lab5 + // sched_init(); + kthread_init(); + + + puts("\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/lab7/kernel/memory.h b/lab7/kernel/memory.h new file mode 100644 index 000000000..c51df8290 --- /dev/null +++ b/lab7/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/lab7/kernel/mm.h b/lab7/kernel/mm.h new file mode 100644 index 000000000..fa6255b0f --- /dev/null +++ b/lab7/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/lab7/kernel/sched.h b/lab7/kernel/sched.h new file mode 100644 index 000000000..19ac3ca99 --- /dev/null +++ b/lab7/kernel/sched.h @@ -0,0 +1,70 @@ +#ifndef SCHED_H +#define SCHED_H + +#include +#include +#include + +#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; + + char cwd[256]; + struct fdtable fdtable; +}; + +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/lab7/kernel/signal.c b/lab7/kernel/signal.c new file mode 100644 index 000000000..980899fd4 --- /dev/null +++ b/lab7/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/lab7/kernel/signal.h b/lab7/kernel/signal.h new file mode 100644 index 000000000..913de9e3a --- /dev/null +++ b/lab7/kernel/signal.h @@ -0,0 +1,13 @@ + +#ifndef SIGNAL_H +#define SIGNAL_H + +#include + +#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/lab7/kernel/simple.c b/lab7/kernel/simple.c new file mode 100644 index 000000000..d22781d2b --- /dev/null +++ b/lab7/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) { + puts("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/lab7/kernel/syscall.c b/lab7/kernel/syscall.c new file mode 100644 index 000000000..b80521efa --- /dev/null +++ b/lab7/kernel/syscall.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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[]) { + struct file *file; + int ret = vfs_open(file_name, O_RDWR, &file); + if (ret == -1) { + printf("\r\n[ERROR] Cannot open file: %s", file_name); + return -1; + } + + int filesize = file->vnode->f_ops->getsize(file->vnode); + void *user_program = kmalloc(filesize); + + if (user_program == NULL) { + printf("\r\n[ERROR] Cannot allocate memory for file: %s", file_name); + return -1; + } + + vfs_read(file, user_program, filesize); + vfs_close(file); + + get_current()->state = TASK_STOPPED; + vfs_read(file, user_program, filesize); + 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) { + /*lock();*/ + 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; + + strcpy(child->cwd, parent->cwd); + for (int i = 0; i < MAX_FD; i++) { + if (parent->fdtable.fds[i]) { + child->fdtable.fds[i] = kmalloc(sizeof(struct file)); + *child->fdtable.fds[i] = *parent->fdtable.fds[i]; + } + } + + /*unlock();*/ + 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() { + puts("Fork Test (pid = "); + print_d(getpid()); + puts(")\n"); + int cnt = 1; + int ret = 0; + + if ((ret = fork()) == 0) { + puts("first child pid: "); + print_d(getpid()); + puts(", cnt: "); + print_d(cnt); + puts(", ptr: "); + print_h((unsigned long long)&cnt); + puts("\n"); + cnt++; + if ((ret = fork()) != 0) { + puts("first child pid: "); + print_d(getpid()); + puts(", cnt: "); + print_d(cnt); + puts(", ptr: "); + print_h((unsigned long long)&cnt); + puts("\n"); + } else { + while (cnt < 5) { + puts("second child pid: "); + print_d(getpid()); + puts(", cnt: "); + print_d(cnt); + puts(", ptr: "); + print_h((unsigned long long)&cnt); + puts("\n"); + + for (int i = 0; i < 1000000; i++); + cnt++; + } + } + exit(); + } else { + puts("parent here, pid "); + print_d(getpid()); + puts(", child "); + print_d(ret); + puts("\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"); +} + +int sys_open(trap_frame *tf, const char *pathname, int flags) { + /*printf("\r\n[SYSCALL] open - cwd: %s\r\n", get_current()->cwd);*/ + /*printf("\r\n[SYSCALL] open-1 - path: %s, flags: %x\r\n", pathname, flags);*/ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + // update abs_path + get_absolute_path(abs_path, get_current()->cwd); + /*printf("\r\n[SYSCALL] open-2 - path: %s, flags: %x\r\n", abs_path, + * flags);*/ + for (int i = 0; i < MAX_FD; i++) { + // find a usable fd + if (!get_current()->fdtable.fds[i]) { + /*printf("\r\n[SYSCALL] open-3 - fd index: %d\r\n", i);*/ + if (vfs_open(abs_path, flags, &get_current()->fdtable.fds[i]) != + 0) { + break; + } + + tf->x0 = i; + return i; + } + } + + tf->x0 = -1; + return -1; +} + +int sys_close(trap_frame *tf, int fd) { + /*printf("\r\n[SYSCALL] close - fd: %d\r\n", fd);*/ + // find an opened fd + if (get_current()->fdtable.fds[fd]) { + vfs_close(get_current()->fdtable.fds[fd]); + get_current()->fdtable.fds[fd] = 0; + tf->x0 = 0; + return 0; + } + + tf->x0 = -1; + return -1; +} + +long sys_write(trap_frame *tf, int fd, const void *buf, unsigned long count) { + /*printf("\r\n[SYSCALL] write - fd: %d, buf: %x, count: %d\r\n", fd, buf, + * count);*/ + // find an opened fd + if (get_current()->fdtable.fds[fd]) { + tf->x0 = vfs_write(get_current()->fdtable.fds[fd], buf, count); + return tf->x0; + } + + tf->x0 = -1; + return tf->x0; +} + +long sys_read(trap_frame *tf, int fd, void *buf, unsigned long count) { + /*printf("\r\n[SYSCALL] read - fd: %d, buf: %x, count: %d\r\n", fd, buf, + * count);*/ + // find an opened fd + if (get_current()->fdtable.fds[fd]) { + tf->x0 = vfs_read(get_current()->fdtable.fds[fd], buf, count); + return tf->x0; + } + + tf->x0 = -1; + return tf->x0; +} + +long sys_mkdir(trap_frame *tf, const char *pathname, unsigned mode) { + /*printf("\r\n[SYSCALL] mkdir - path: %s, mode: %x\r\n", pathname, mode);*/ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + get_absolute_path(abs_path, get_current()->cwd); + tf->x0 = vfs_mkdir(abs_path); + return tf->x0; +} + +long sys_mount(trap_frame *tf, const char *src, const char *target, + const char *filesystem, unsigned long flags, const void *data) { + /*printf(*/ + /* "\r\n[SYSCALL] mount - src: %s, target: %s, filesystem: %s, flags: %x\r\n",*/ + /* src, target, filesystem, flags);*/ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, target); + /*printf("\r\n[SYSCALL] mount - abs_path: %s\r\n", abs_path);*/ + + tf->x0 = vfs_mount(abs_path, filesystem); + return tf->x0; +} + +long sys_chdir(trap_frame *tf, const char *path) { + /*printf("\r\n[SYSCALL] chdir - path: %s\r\n", path);*/ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, path); + get_absolute_path(abs_path, get_current()->cwd); + strcpy(get_current()->cwd, abs_path); + + return 0; +} + +long sys_lseek64(trap_frame *tf, int fd, long offset, int whence) { + /*printf("\r\n[SYSCALL] lseek64 - fd: %d, offset: %d, whence: %d\r\n", fd, + * offset, whence);*/ + if (whence == SEEK_SET) // used for dev_framebuffer + { + get_current()->fdtable.fds[fd]->f_pos = offset; + tf->x0 = offset; + } else // other is not supported + { + tf->x0 = -1; + } + + return tf->x0; +} + +// ioctl 0 will be use to get info +// there will be default value in info +// if it works with default value, you can ignore this syscall +long sys_ioctl(trap_frame *tf, int fb, unsigned long request, void *info) { + /*printf("\r\n[SYSCALL] ioctl - fb: %d, request: %x, info: %x\r\n", fb, + * request, info);*/ + tf->x0 = 0; + return tf->x0; +} diff --git a/lab7/kernel/syscall.h b/lab7/kernel/syscall.h new file mode 100644 index 000000000..1cdc088a3 --- /dev/null +++ b/lab7/kernel/syscall.h @@ -0,0 +1,32 @@ + +#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(); + +int sys_open(trap_frame *tf, const char *pathname, int flags); +int sys_close(trap_frame *tf, int fd); +long sys_write(trap_frame *tf, int fd, const void *buf, unsigned long count); +long sys_read(trap_frame *tf, int fd, void *buf, unsigned long count); +long sys_mkdir(trap_frame *tf, const char *pathname, unsigned mode); +long sys_mount(trap_frame *tf, const char *src, const char *target, + const char *filesystem, unsigned long flags, const void *data); +long sys_chdir(trap_frame *tf, const char *path); +long sys_lseek64(trap_frame *tf, int fd, long offset, int whence); +long sys_ioctl(trap_frame *tf, int fb, unsigned long request, void *info); + +#endif diff --git a/lab7/kernel/task.c b/lab7/kernel/task.c new file mode 100644 index 000000000..00f4665d1 --- /dev/null +++ b/lab7/kernel/task.c @@ -0,0 +1,151 @@ +#include +#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 { + puts("Task id: "); + print_d(task->pid); + puts(", state: "); + print_d(task->state); + puts(", next: "); + print_d(task->next->pid); + puts("\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(); + // puts("Scheduling from: "); + // print_d(current->pid); + // puts(" -> "); + // print_d(next->pid); + // puts("\n"); + + switch_to(current, current->next); +} + +void kill_zombies() { + // puts("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)()) { + /*lock();*/ + 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; + strcpy(task->cwd, "/"); + + task->fdtable.count = 0; + for (int i = 0; i < MAX_FD; i++) task->fdtable.fds[i] = NULL; + vfs_open("/dev/uart", O_RDWR, &task->fdtable.fds[0]); // stdin + vfs_open("/dev/uart", O_RDWR, &task->fdtable.fds[1]); // stdout + vfs_open("/dev/uart", O_RDWR, &task->fdtable.fds[2]); // stderr + + enqueue(&run_queue, task); + /*unlock();*/ + 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) { + puts("Thread id: "); + print_d(get_current()->pid); + puts(" "); + print_d(i); + puts("\n"); + for (int i = 0; i < 1000000; i++); + schedule(); + } + kthread_exit(); +} diff --git a/lab7/kernel/timer.c b/lab7/kernel/timer.c new file mode 100644 index 000000000..74bb771c7 --- /dev/null +++ b/lab7/kernel/timer.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +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_t *timer = (timer_t *)kmalloc(STACK_SIZE); +#ifdef TIMER_DEBUG + puts("\n[set_timeout] timer_t allocated at "); + print_h((unsigned long)timer); + puts("\n"); + puts("[set_timeout] head: "); + print_h((unsigned long)head); + puts("\n"); +#endif /* ifdef TIMER_DEBUG */ + + 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() { + // puts("\n[timer_irq_handler] \n"); + // puts("Current task: "); + // print_d(get_current()->pid); + // puts("\n"); +#ifdef SCHED_DEBUG + static int count = 0; + if (count++ % 1 == 0) { + puts("\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/lab7/kernel/timer.h b/lab7/kernel/timer.h new file mode 100644 index 000000000..41c3a05b3 --- /dev/null +++ b/lab7/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/lab7/lib/limits.h b/lab7/lib/limits.h new file mode 100644 index 000000000..36f8e3237 --- /dev/null +++ b/lab7/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/lab7/lib/sprintf.c b/lab7/lib/sprintf.c new file mode 100644 index 000000000..bcf48cbb0 --- /dev/null +++ b/lab7/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, const 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, const 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/lab7/lib/stddef.h b/lab7/lib/stddef.h new file mode 100644 index 000000000..79fc8e514 --- /dev/null +++ b/lab7/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/lab7/lib/stdlib.c b/lab7/lib/stdlib.c new file mode 100644 index 000000000..d583af908 --- /dev/null +++ b/lab7/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/lab7/lib/stdlib.h b/lab7/lib/stdlib.h new file mode 100644 index 000000000..a3ce0b866 --- /dev/null +++ b/lab7/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/lab7/lib/string.c b/lab7/lib/string.c new file mode 100644 index 000000000..da45afdee --- /dev/null +++ b/lab7/lib/string.c @@ -0,0 +1,107 @@ +#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* strcat (char *dest, const char *src) +{ + strcpy (dest + strlen (dest), src); + return dest; +} + +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/lab7/lib/string.h b/lab7/lib/string.h new file mode 100644 index 000000000..5c9a946ba --- /dev/null +++ b/lab7/lib/string.h @@ -0,0 +1,15 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + +int strcmp(const char *a, const char *b); +char *strcat(char *dest, const char *src); +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/lab7/lib/utils.c b/lab7/lib/utils.c new file mode 100644 index 000000000..ce920acff --- /dev/null +++ b/lab7/lib/utils.c @@ -0,0 +1,3 @@ +#include + +int align4(int n) { return n + (4 - n % 4) % 4; } diff --git a/lab7/lib/utils.h b/lab7/lib/utils.h new file mode 100644 index 000000000..aabe00f9d --- /dev/null +++ b/lab7/lib/utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H +#define _UTILS_H + +unsigned int sprintf(char *dst, const char *fmt, ...); +unsigned int vsprintf(char *dst, const char *fmt, __builtin_va_list args); + +int align4(int n); + +#endif diff --git a/lab7/rootfs/file1 b/lab7/rootfs/file1 new file mode 100644 index 000000000..9bc8d9ca0 --- /dev/null +++ b/lab7/rootfs/file1 @@ -0,0 +1 @@ +this is file1. diff --git a/lab7/rootfs/file2.txt b/lab7/rootfs/file2.txt new file mode 100644 index 000000000..f1adef63c --- /dev/null +++ b/lab7/rootfs/file2.txt @@ -0,0 +1 @@ +nothing here diff --git a/lab7/user_program/Makefile b/lab7/user_program/Makefile new file mode 100644 index 000000000..5e2780f44 --- /dev/null +++ b/lab7/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/lab7/user_program/linker.ld b/lab7/user_program/linker.ld new file mode 100644 index 000000000..e69de29bb diff --git a/lab7/user_program/test_svc_exception.S b/lab7/user_program/test_svc_exception.S new file mode 100644 index 000000000..ce7e53908 --- /dev/null +++ b/lab7/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