diff --git a/lab2/bootloader/main.c b/lab2/bootloader/main.c index 393de8cae..265e655f2 100644 --- a/lab2/bootloader/main.c +++ b/lab2/bootloader/main.c @@ -39,4 +39,4 @@ int main() { "ret;"); return 0; -} \ No newline at end of file +} diff --git a/lab2/src/initramfs.c b/lab2/src/initramfs.c index 941f6c944..db9f91cd2 100644 --- a/lab2/src/initramfs.c +++ b/lab2/src/initramfs.c @@ -62,8 +62,9 @@ void initramfs_cat(const char *target) { strncpy(pathname, fptr + sizeof(cpio_t), namesize); if (!strcmp(target, pathname)) { // Print its content - char data[filesize]; + char data[filesize + 1]; strncpy(data, fptr + headsize, filesize); + data[filesize] = '\0'; uart_puts(data); uart_putc('\n'); return; diff --git a/lab3/.gitignore b/lab3/.gitignore new file mode 100644 index 000000000..eddcca901 --- /dev/null +++ b/lab3/.gitignore @@ -0,0 +1,6 @@ +.vscode +build +rootfs +temp +*.img +*.dtb \ No newline at end of file diff --git a/lab3/Makefile b/lab3/Makefile new file mode 100644 index 000000000..729b22978 --- /dev/null +++ b/lab3/Makefile @@ -0,0 +1,36 @@ +ARMGNU ?= aarch64-linux-gnu + +CFLAGS = -Iinclude -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +ASMFLAGS = -Iinclude +QEMUFLAGS = -M raspi3b -display none -serial null -serial stdio + +SRC_DIR = src +BUILD_DIR = build + +all: clean kernel8.img + +clean: + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/_%.o: %.S + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard *.S) +OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/_%.o) + +kernel8.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/kernel8.elf kernel8.img + +qemu: all kernel8.img initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug: all kernel8.img initramfs.cpio bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -S -s -d int \ No newline at end of file diff --git a/lab3/bootloader/Makefile b/lab3/bootloader/Makefile new file mode 100644 index 000000000..d7882aa27 --- /dev/null +++ b/lab3/bootloader/Makefile @@ -0,0 +1,19 @@ +ARMGNU ?= aarch64-linux-gnu +CFLAGS = -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +QEMUFLAGS = -M raspi3b -display none -serial null -serial pty + +all: clean bootloader.img + +clean: + rm -rf build *.img + +bootloader.img: + mkdir -p build + $(ARMGNU)-gcc $(CFLAGS) -c main.c -o build/main.o + $(ARMGNU)-gcc $(CFLAGS) -c start.S -o build/start.o + $(ARMGNU)-gcc $(CFLAGS) -c boot.c -o build/boot.o + $(ARMGNU)-ld -T linker.ld -o build/bootloader.elf build/main.o build/start.o build/boot.o + $(ARMGNU)-objcopy -O binary build/bootloader.elf bootloader.img + +qemu: clean bootloader.img + qemu-system-aarch64 $(QEMUFLAGS) -kernel bootloader.img -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/lab3/bootloader/boot.c b/lab3/bootloader/boot.c new file mode 100644 index 000000000..fd16525a6 --- /dev/null +++ b/lab3/bootloader/boot.c @@ -0,0 +1,61 @@ +#include "boot.h" + +void uart_init() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + *GPPUD = 0; + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +char uart_recv() { + while (!(*AUX_MU_LSR & 0x01)) asm volatile("nop"); + return (char)(*AUX_MU_IO); +} + +void uart_putc(char c) { + if (c == '\n') uart_putc('\r'); + while (!(*AUX_MU_LSR & 0x20)) asm volatile("nop"); + *AUX_MU_IO = c; +} + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} + +int atoi(const char *s) { + int result = 0; + 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 * result; +} \ No newline at end of file diff --git a/lab3/bootloader/boot.h b/lab3/bootloader/boot.h new file mode 100644 index 000000000..6eedd57fd --- /dev/null +++ b/lab3/bootloader/boot.h @@ -0,0 +1,49 @@ +#ifndef BOOT_H +#define BOOT_H + +/* ==================== GPIO ==================== */ +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) + +/* ==================== UART ==================== */ +#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(); +char uart_recv(); +void uart_putc(char c); +void uart_puts(const char *s); + +/* ==================== UTILS ==================== */ +int atoi(const char *s); + +#endif // BOOT_H \ No newline at end of file diff --git a/lab3/bootloader/linker.ld b/lab3/bootloader/linker.ld new file mode 100644 index 000000000..de083fba3 --- /dev/null +++ b/lab3/bootloader/linker.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + . = 0x60000; + __loader_start = .; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } + __loader_end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; +__loader_size = (__loader_end - __loader_start) >> 3; \ No newline at end of file diff --git a/lab3/bootloader/main.c b/lab3/bootloader/main.c new file mode 100644 index 000000000..393de8cae --- /dev/null +++ b/lab3/bootloader/main.c @@ -0,0 +1,42 @@ +#include "boot.h" + +int main() { + uart_init(); + uart_puts("\033[2J\033[H"); + uart_puts( + "UART Bootloader\n" + "Waiting for kernel...\n"); + + // Get kernel image size + char buf[16] = {0}; + for (int i = 0; i < 16; i++) { + buf[i] = uart_recv(); + if (buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + + // Load kernel image + uart_puts("Kernel size: "); + uart_puts(buf); + uart_puts(" bytes.\n"); + uart_puts("Loading the kernel image...\n"); + + unsigned int size = atoi(buf); + char *kernel = (char *)0x80000; + while (size--) *kernel++ = uart_recv(); + + // Restore registers x0 x1 x2 x3 + // Jump to the new kernel + asm volatile( + "" + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + "mov x30, 0x80000;" + "ret;"); + + return 0; +} \ No newline at end of file diff --git a/lab3/bootloader/sender.sh b/lab3/bootloader/sender.sh new file mode 100755 index 000000000..6de9ec437 --- /dev/null +++ b/lab3/bootloader/sender.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This script sends the kernel image to Rpi3 through UART + +DEST_PATH="/dev/ttyUSB0" +KERNEL_PATH="../kernel8.img" + +# Check the root permission +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +if [ $1 ] +then + DEST_PATH="$1" +fi + +# Get the size of the kernel image file and send it to Rpi3 +# wc -c: count bytes of a file +# sleep: wait n seconds +wc -c < $KERNEL_PATH > $DEST_PATH | sleep 1 + +# Send the kernel image +# pv: redirect file input to specified tty +# add --rate-limit option to limit the speed +pv $KERNEL_PATH > $DEST_PATH diff --git a/lab3/bootloader/start.S b/lab3/bootloader/start.S new file mode 100644 index 000000000..ec8488dde --- /dev/null +++ b/lab3/bootloader/start.S @@ -0,0 +1,40 @@ +.section ".text.boot" + +.global _start + +_start: + /* save registers x0 x1 x2 x3 */ + mov x10, x0 /* dtb_base address */ + mov x11, x1 + mov x12, x2 + mov x12, x3 + + /* relocate bootloader */ + ldr x1, =0x80000 + ldr x2, =__loader_start // 0x60000 + ldr w3, =__loader_size + +relocate: + ldr x4, [x1], #8 + str x4, [x2], #8 + sub w3, w3, #1 + cbnz w3, relocate + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main-0x20000 + b run_main diff --git a/lab3/include/command.h b/lab3/include/command.h new file mode 100644 index 000000000..5342dbd96 --- /dev/null +++ b/lab3/include/command.h @@ -0,0 +1,25 @@ +#pragma once + +#define MAX_BUF_SIZE 1024 +#define END_OF_COMMAND_LIST "NULL" + +struct command { + const char *name; + const char *help; + void (*func)(void); +}; + +extern struct command commands[]; + +// Commands +void cmd_help(); +void cmd_hello(); +void cmd_reboot(); +void cmd_cancel(); +void cmd_info(); +void cmd_ls(); +void cmd_cat(); +void cmd_run(); +void cmd_clear(); +void cmd_timer(); +void cmd_lab(); \ No newline at end of file diff --git a/lab3/include/devtree.h b/lab3/include/devtree.h new file mode 100644 index 000000000..8bfbc9e49 --- /dev/null +++ b/lab3/include/devtree.h @@ -0,0 +1,18 @@ +#pragma once + +#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; +}; + +void fdt_traverse(void (*callback)(void *, char *)); diff --git a/lab3/include/hardware.h b/lab3/include/hardware.h new file mode 100644 index 000000000..41c5c6053 --- /dev/null +++ b/lab3/include/hardware.h @@ -0,0 +1,219 @@ +#pragma once + +#define MMIO_BASE 0x3F000000 + +// ARM Interrupt Registers + +#define IRQ_BASIC_PENDING (volatile unsigned int *)(MMIO_BASE + 0x0000B200) +#define IRQ_PENDING_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B204) +#define IRQ_PENDING_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B208) +#define FIQ_CONTROL (volatile unsigned int *)(MMIO_BASE + 0x0000B20C) +#define ENABLE_IRQS_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B210) +#define ENABLE_IRQS_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B214) +#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) +#define CORE0_INTERRUPT_SOURCE (volatile unsigned int *)(0x40000060) + +// Timers interrupt control registers + +#define CORE0_TIMER_IRQCNTL 0x40000040 +#define CORE1_TIMER_IRQCNTL 0x40000044 +#define CORE2_TIMER_IRQCNTL 0x40000048 +#define CORE3_TIMER_IRQCNTL 0x4000004C + +// Where to route timer interrupt to, IRQ/FIQ +// Setting both the IRQ and FIQ bit gives an FIQ +#define TIMER0_IRQ 0x01 +#define TIMER1_IRQ 0x02 +#define TIMER2_IRQ 0x04 +#define TIMER3_IRQ 0x08 +#define TIMER0_FIQ 0x10 +#define TIMER1_FIQ 0x20 +#define TIMER2_FIQ 0x40 +#define TIMER3_FIQ 0x80 + +// PASSWORD + +#define PM_PASSWORD 0x5A000000 +#define PM_RSTC (volatile unsigned int *)0x3F10001C +#define PM_WDOG (volatile unsigned int *)0x3F100024 + +// DEVICETREE + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +// GPIO + +#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 GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) +// Skip GPRENn, GPFENn +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +// Skip GPLENn, GPARENn +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +// UART + +#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)) + +// MAILBOX + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 +#define MAILBOX_REQUEST 0x00000000 +#define MAILBOX_RESPONSE 0x80000000 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +// Channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +#define MAILBOX_BASE (MMIO_BASE + 0x0000B880) + +// mailbox register +#define MAILBOX_REG_READ ((volatile unsigned int *)(MAILBOX_BASE + 0x00000000)) +#define MAILBOX_REG_STATUS \ + ((volatile unsigned int *)(MAILBOX_BASE + 0x00000018)) +#define MAILBOX_REG_WRITE ((volatile unsigned int *)(MAILBOX_BASE + 0x00000020)) + +// mailbox channel +// https://github.com/raspberrypi/firmware/wiki/Mailboxes#channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +// mailbox status +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 + +// tags +#define TAGS_REQ_CODE 0x00000000 +#define TAGS_REQ_SUCCEED 0x80000000 +#define TAGS_REQ_FAILED 0x80000001 +#define TAGS_END 0x00000000 + +// hardware tags operator +#define TAGS_HARDWARE_BOARD_MODEL 0x00010001 +#define TAGS_HARDWARE_BOARD_REVISION 0x00010002 +#define TAGS_HARDWARE_MAC_ADDR 0x00010003 +#define TAGS_HARDWARE_BOARD_SERIAL 0x00010004 +#define TAGS_HARDWARE_ARM_MEM 0x00010005 +#define TAGS_HARDWARE_VC_MEM 0x00010006 +#define TAGS_HARDWARE_CLOCKS 0x00010007 + +// CLOCK ID + +#define CLOCK_ID_RESERVED 0x000000000 +#define CLOCK_ID_EMMC 0x000000001 +#define CLOCK_ID_UART 0x000000002 +#define CLOCK_ID_ARM 0x000000003 +#define CLOCK_ID_CORE 0x000000004 +#define CLOCK_ID_V3D 0x000000005 +#define CLOCK_ID_H264 0x000000006 +#define CLOCK_ID_ISP 0x000000007 +#define CLOCK_ID_SDRAM 0x000000008 +#define CLOCK_ID_PIXEL 0x000000009 +#define CLOCK_ID_PWM 0x00000000a +#define CLOCK_ID_HEVC 0x00000000b +#define CLOCK_ID_EMMC2 0x00000000c +#define CLOCK_ID_M2MC 0x00000000d +#define CLOCK_ID_PIXEL_BVB 0x00000000e + +// clock tags operator +#define TAGS_GET_CLOCK 0x00030002 +#define TAGS_SET_CLOCK 0x00038002 + +// framebuffer tages operator +#define FB_ALLOC_BUFFER 0x00040001 +#define FB_FREE_BUFFER 0x00048001 + +#define FB_BLANK_SCREEN 0x00040002 + +#define FB_PHY_WID_HEIGHT_GET 0x00040003 +#define FB_PHY_WID_HEIGHT_TEST 0x00044003 +#define FB_PHY_WID_HEIGHT_SET 0x00048003 + +#define FB_VIR_WID_HEIGHT_GET 0x00040004 +#define FB_VIR_WID_HEIGHT_TEST 0x00044004 +#define FB_VIR_WID_HEIGHT_SET 0x00048004 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_PIXEL_ORDER_GET 0x00040006 +#define FB_PIXEL_ORDER_TEST 0x00044006 +#define FB_PIXEL_ORDER_SET 0x00048006 + +#define FB_ALPHA_MODE_GET 0x00040007 +#define FB_ALPHA_MODE_TEST 0x00044007 +#define FB_ALPHA_MODE_SET 0x00048007 + +#define FB_PITCH_GET 0x00040008 + +#define FB_VIR_OFFSET_GET 0x00040009 +#define FB_VIR_OFFSET_TEST 0x00044009 +#define FB_VIR_OFFSET_SET 0x00048009 + +#define FB_OVERSCAN_GET 0x0004000A +#define FB_OVERSCAN_TEST 0x0004400A +#define FB_OVERSCAN_SET 0x0004800A + +#define FB_PALETTE_GET 0x0004000B +#define FB_PALETTE_TEST 0x0004400B +#define FB_PALETTE_SET 0x0004800B + +#define FB_CURSOR_INFO_SET 0x00008010 +#define FB_CURSOR_STATE_SET 0x00008011 diff --git a/lab3/include/initramfs.h b/lab3/include/initramfs.h new file mode 100644 index 000000000..ba65e30fb --- /dev/null +++ b/lab3/include/initramfs.h @@ -0,0 +1,26 @@ +#pragma once + +// Cpio Archive File Header (New ASCII Format) +typedef struct { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +} cpio_t; + +#define DEFAULT_STACK_SIZE 8192 + +void initramfs_list(); +void initramfs_cat(const char *target); +void initramfs_callback(void *addr, char *property); +void initramfs_run(const char *target); diff --git a/lab3/include/irq.h b/lab3/include/irq.h new file mode 100644 index 000000000..88310213e --- /dev/null +++ b/lab3/include/irq.h @@ -0,0 +1,10 @@ +#pragma once + +#include "hardware.h" + +#define PRIORITY_HIGH 0 +#define PRIORITY_LOW 255 + +void enable_interrupt(); +void disable_interrupt(); +void irq_entry(); \ No newline at end of file diff --git a/lab3/include/malloc.h b/lab3/include/malloc.h new file mode 100644 index 000000000..0ef6b09b4 --- /dev/null +++ b/lab3/include/malloc.h @@ -0,0 +1,4 @@ +#pragma once + +void malloc_init(); +void *simple_malloc(int size); \ No newline at end of file diff --git a/lab3/include/mbox.h b/lab3/include/mbox.h new file mode 100644 index 000000000..223864626 --- /dev/null +++ b/lab3/include/mbox.h @@ -0,0 +1,8 @@ +#pragma once + +#include "hardware.h" + +extern volatile unsigned int mbox[36]; +int mailbox_call(unsigned char c); +int get_board_revision(); +int get_arm_memory_status(); diff --git a/lab3/include/shell.h b/lab3/include/shell.h new file mode 100644 index 000000000..407f09837 --- /dev/null +++ b/lab3/include/shell.h @@ -0,0 +1,9 @@ +#pragma once + +#define SHELL_BUF_SIZE 256 + +void welcome_msg(); +void run_shell(); +void read_user_input(char *buf); +void buffer_overflow_message(); +int exec_command(const char *command); diff --git a/lab3/include/string.h b/lab3/include/string.h new file mode 100644 index 000000000..489673d47 --- /dev/null +++ b/lab3/include/string.h @@ -0,0 +1,7 @@ +#pragma once + +int strcmp(const char *str1, const char *str2); +int memcmp(const void *str1, const void *str2, int n); +char *strncpy(char *dest, const char *src, int n); +int strlen(const char *str); +char *strcat(char *dest, const char *src); \ No newline at end of file diff --git a/lab3/include/timer.h b/lab3/include/timer.h new file mode 100644 index 000000000..e7066718e --- /dev/null +++ b/lab3/include/timer.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +void timer_enable_interrupt(); +void timer_disable_interrupt(); +void timer_irq_handler(); +uint64_t timer_get_uptime(); + +void timer_add(void (*callback)(void *), void *arg, int after); +void set_timer(const char *message, int after); +void reset_timeup(); +int get_timeup(); +void set_timeup(); \ No newline at end of file diff --git a/lab3/include/uart.h b/lab3/include/uart.h new file mode 100644 index 000000000..0a8480fa0 --- /dev/null +++ b/lab3/include/uart.h @@ -0,0 +1,28 @@ +#pragma once + +#include "hardware.h" + +#define UART_BUF_SIZE 1024 + +#define BACKSPACE '\b' +#define DELETE 127 +#define NEWLINE '\n' +#define TAB '\t' +#define CARRIAGE_RETURN '\r' + +void uart_init(); +char uart_getc(); +void uart_putc(char c); +void uart_hex(unsigned int h); +void uart_puts(const char *s); + +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, int len); +void uart_async_write(const char *s); \ No newline at end of file diff --git a/lab3/include/utils.h b/lab3/include/utils.h new file mode 100644 index 000000000..f3d3597d8 --- /dev/null +++ b/lab3/include/utils.h @@ -0,0 +1,20 @@ +#pragma once + +/** + * @brief Align `n` to be a multiple of 4. + * + * @param n A number + * @return Algined number + */ +int align4(int n); + +int atoi(const char *s); + +/** + * @brief Convert hexadecimal string to int. + * + * @param s: hexadecimal string + * @param n: string length + * @return Converted int number + */ +int hextoi(char *s, int n); \ No newline at end of file diff --git a/lab3/initramfs.cpio b/lab3/initramfs.cpio new file mode 100644 index 000000000..4710cde21 Binary files /dev/null and b/lab3/initramfs.cpio differ diff --git a/lab3/linker.ld b/lab3/linker.ld new file mode 100644 index 000000000..b545f82af --- /dev/null +++ b/lab3/linker.ld @@ -0,0 +1,13 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab3/src/command.c b/lab3/src/command.c new file mode 100644 index 000000000..2b69e71c2 --- /dev/null +++ b/lab3/src/command.c @@ -0,0 +1,175 @@ +#include "command.h" + +#include "initramfs.h" +#include "malloc.h" +#include "mbox.h" +#include "shell.h" +#include "string.h" +#include "timer.h" +#include "uart.h" +#include "utils.h" + +struct command commands[] = { + {.name = "help", .help = "print this help menu", .func = cmd_help}, + {.name = "hello", .help = "print Hello there!", .func = cmd_hello}, + {.name = "clear", .help = "clear screen", .func = cmd_clear}, + {.name = "reboot", .help = "reboot the device", .func = cmd_reboot}, + {.name = "cancel", .help = "cancel reboot", .func = cmd_cancel}, + {.name = "info", .help = "hardware information", .func = cmd_info}, + {.name = "ls", .help = "list ramdisk files", .func = cmd_ls}, + {.name = "cat", .help = "print ramdisk file", .func = cmd_cat}, + {.name = "run", .help = "run a program", .func = cmd_run}, + {.name = "timer", .help = "set timer", .func = cmd_timer}, + {.name = "lab", .help = "showcase lab requirements", .func = cmd_lab}, + {.name = END_OF_COMMAND_LIST}}; + +void cmd_lab() { + char buf[SHELL_BUF_SIZE]; + uart_puts("(1) UART async write"); + uart_putc(NEWLINE); + uart_puts("(2) UART async read"); + uart_putc(NEWLINE); + uart_puts("Please select: "); + read_user_input(buf); + switch (atoi(buf)) { + case 1: + uart_puts(": "); + read_user_input(buf); + uart_async_write(buf); + // uart_async_write("[INFO] Test the UART async write function\n"); + break; + case 2: + uart_puts("(Please type something in 3 sec.)"); + uart_putc(NEWLINE); + // set a timer to 3 sec. + reset_timeup(); + timer_add((void (*)(void *))set_timeup, (void *)0, 3); + uart_enable_rx_interrupt(); + while (!get_timeup()); + // time's up, get the buf content and print + uart_async_read(buf, 256); + uart_puts(buf); + uart_putc(NEWLINE); + uart_putc(NEWLINE); + break; + default: + uart_puts("Option not found."); + uart_putc(NEWLINE); + break; + } +} + +void cmd_timer() { + char sec[SHELL_BUF_SIZE]; + uart_puts("Duration(sec.): "); + read_user_input(sec); + + char *msg = simple_malloc(SHELL_BUF_SIZE); + uart_puts(": "); + read_user_input(msg); + set_timer(msg, atoi(sec)); +} + +void cmd_run() { + // Get filename from user input + char buffer[SHELL_BUF_SIZE]; + uart_puts(": "); + read_user_input(buffer); + initramfs_run(buffer); +} + +void cmd_help() { + int i = 0; + + uart_puts("------------------------------"); + uart_putc(NEWLINE); + while (1) { + if (!strcmp(commands[i].name, END_OF_COMMAND_LIST)) { + break; + } + uart_puts(commands[i].name); + uart_putc(TAB); + uart_puts(": "); + uart_puts(commands[i].help); + uart_putc(NEWLINE); + i++; + } + uart_puts("------------------------------"); + uart_putc(NEWLINE); + uart_putc(NEWLINE); +} + +void cmd_hello() { + uart_puts("Hello there!"); + uart_putc(NEWLINE); + uart_putc(NEWLINE); +} + +void cmd_reboot() { + uart_puts("Start Rebooting..."); + uart_putc(NEWLINE); + uart_putc(NEWLINE); + + // Reboot after 0x20000 ticks + *PM_RSTC = PM_PASSWORD | 0x20; // Full reset + *PM_WDOG = PM_PASSWORD | 0x20000; // Number of watchdog ticks +} + +void cmd_cancel() { + uart_puts("Rebooting Attempt Aborted, if any."); + uart_putc(NEWLINE); + uart_putc(NEWLINE); + *PM_RSTC = PM_PASSWORD | 0; + *PM_WDOG = PM_PASSWORD | 0; +} + +void cmd_info() { + // Get board revision + get_board_revision(); + uart_puts("board revision: "); + uart_hex(mbox[5]); + uart_putc(NEWLINE); + + // Get ARM memory base address and size + get_arm_memory_status(); + uart_puts("device base memory address: "); + uart_hex(mbox[5]); + uart_putc(NEWLINE); + uart_puts("device memory size: "); + uart_hex(mbox[6]); + uart_putc(NEWLINE); + uart_putc(NEWLINE); +} + +void cmd_ls() { + initramfs_list(); + uart_putc(NEWLINE); +} + +void cmd_cat() { + // Get filename from user input + uart_puts(": "); + char buf[MAX_BUF_SIZE + 1]; + int idx = 0; + while (1) { + char c = uart_getc(); + uart_putc(c); + if (c == NEWLINE) { + buf[idx] = '\0'; + break; + } else if (c == 127) { // Backspaces + if (idx > 0) { + buf[idx--] = 0; + uart_putc(BACKSPACE); + uart_putc(' '); + uart_putc(BACKSPACE); + } + } else { + buf[idx++] = c; + } + } + initramfs_cat(buf); + uart_putc(NEWLINE); +} + +void cmd_clear() { uart_puts("\033[2J\033[H"); } diff --git a/lab3/src/devtree.c b/lab3/src/devtree.c new file mode 100644 index 000000000..441d6bafc --- /dev/null +++ b/lab3/src/devtree.c @@ -0,0 +1,66 @@ +#include "devtree.h" + +#include "string.h" +#include "uart.h" +#include "utils.h" + +// Assign a non-zero value to be stored in the .data section +void *DTB_BASE = (void *)0xF; + +/** + * @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]; +} + +void fdt_traverse(void (*callback)(void *, char *)) { + uart_puts("[INFO] Dtb is loaded at "); + uart_hex((uintptr_t)DTB_BASE); + uart_putc(NEWLINE); + + uintptr_t dtb_ptr = (uintptr_t)DTB_BASE; + struct fdt_header *header = (struct fdt_header *)dtb_ptr; + + // Check the magic number + if (be2le(&(header->magic)) != 0xD00DFEED) { + uart_puts("[ERROR] Dtb header magic does not match!"); + uart_putc(NEWLINE); + } + + uintptr_t structure = (uintptr_t)header + be2le(&header->off_dt_struct); + uintptr_t strings = (uintptr_t)header + be2le(&header->off_dt_strings); + uint32_t structure_size = be2le(&header->size_dt_struct); + + // Parse the structure block + uintptr_t ptr = structure; // Point to the beginning of structure block + while (ptr < structure + structure_size) { + uint32_t token = be2le((char *)ptr); + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + ptr += align4(strlen((char *)ptr) + 1); + break; + case FDT_PROP: + uint32_t len = be2le((char *)ptr); + ptr += 4; + uint32_t nameoff = be2le((char *)ptr); + ptr += 4; + callback((void *)(uintptr_t)be2le((void *)ptr), + (char *)(strings + nameoff)); + // if (!strcmp((char *)(strings + nameoff), "linux,initrd-start")) + // callback((void *)(uintptr_t)be2le((void *)ptr)); + ptr += align4(len); + break; + case FDT_END_NODE: + case FDT_NOP: + case FDT_END: + } + } +} \ No newline at end of file diff --git a/lab3/src/initramfs.c b/lab3/src/initramfs.c new file mode 100644 index 000000000..bf69e44b8 --- /dev/null +++ b/lab3/src/initramfs.c @@ -0,0 +1,118 @@ +#include "initramfs.h" + +#include + +#include "malloc.h" +#include "string.h" +#include "uart.h" +#include "utils.h" + +static void *ramfs_base; + +void initramfs_callback(void *addr, char *property) { + if (!strcmp(property, "linux,initrd-start")) { + uart_puts("[INFO] Initial Ramdisk is mounted at "); + uart_hex((uintptr_t)addr); + uart_putc(NEWLINE); + ramfs_base = (char *)addr; + } +} + +void initramfs_list() { + char *fptr = (char *)ramfs_base; + + // Check if the file is encoded with New ASCII Format + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + int namesize = hextoi(header->c_namesize, 8); + int filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + int headsize = align4(sizeof(cpio_t) + namesize); + int datasize = align4(filesize); + + // Print file pathname + char pathname[namesize]; + strncpy(pathname, fptr + sizeof(cpio_t), namesize); + uart_puts(pathname); + uart_putc(NEWLINE); + + fptr += headsize + datasize; + } +} + +void initramfs_cat(const char *filename) { + char *fptr = (char *)ramfs_base; + + // Check if the file is encoded with New ASCII Format + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + int namesize = hextoi(header->c_namesize, 8); + int filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + int headsize = align4(sizeof(cpio_t) + namesize); + int datasize = align4(filesize); + + // Match target filename + char pathname[namesize]; + strncpy(pathname, fptr + sizeof(cpio_t), namesize); + if (!strcmp(filename, pathname)) { + // Print its content + char data[filesize + 1]; + strncpy(data, fptr + headsize, filesize); + data[filesize] = '\0'; + uart_puts(data); + return; + } + + fptr += headsize + datasize; + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +void initramfs_run(const char *filename) { + char *fptr = (char *)ramfs_base; + + // Check if the file is encoded with New ASCII Format + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + int namesize = hextoi(header->c_namesize, 8); + int filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + int headsize = align4(sizeof(cpio_t) + namesize); + int datasize = align4(filesize); + + // Match target filename + char pathname[namesize]; + strncpy(pathname, fptr + sizeof(cpio_t), namesize); + if (!strcmp(filename, pathname)) { + // Load the user program + char *program = (char *)0x40000; + for (int i = 0; i < filesize; i++) *program++ = (fptr + headsize)[i]; + unsigned long sp = (unsigned long)simple_malloc(DEFAULT_STACK_SIZE); + asm volatile("msr spsr_el1, %0" ::"r"(0x3C0)); + asm volatile("msr elr_el1, %0" ::"r"(0x40000)); + asm volatile("msr sp_el0, %0" ::"r"(sp + DEFAULT_STACK_SIZE)); + asm volatile("eret;"); // Return to EL0 and execute + return; + } + + fptr += headsize + datasize; + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} \ No newline at end of file diff --git a/lab3/src/irq.c b/lab3/src/irq.c new file mode 100644 index 000000000..0677cf743 --- /dev/null +++ b/lab3/src/irq.c @@ -0,0 +1,93 @@ +#include "irq.h" + +#include "malloc.h" +#include "timer.h" +#include "uart.h" + +typedef struct __irq_task_t { + void (*func)(); + int priority; + int busy; // 0 (default) or 1 (busy) + struct __irq_task_t *prev; + struct __irq_task_t *next; +} irq_task_t; + +static irq_task_t *head = 0; + +/* Disable interrupt before calling irq_add_task() */ +void irq_add_task(void (*callback)(), int priority) { + irq_task_t *task = (irq_task_t *)simple_malloc(sizeof(irq_task_t)); + task->func = callback; + task->priority = priority; + task->busy = 0; + task->prev = 0; + task->next = 0; + + if (head == 0 || task->priority < head->priority) { + task->next = head; + task->prev = 0; + if (head != 0) head->prev = task; + head = task; + return; + } + + irq_task_t *current = head; + while (current->next != 0 && current->next->priority <= task->priority) + current = current->next; + task->next = current->next; + if (current->next != 0) current->next->prev = task; + current->next = task; + task->prev = current; +} + +void enable_interrupt() { + // to enable interrupts in EL1 + asm volatile( // Clear the D, A, I, F bits in the DAIF register + "msr DAIFClr, 0xF;"); +} + +void disable_interrupt() { + // to disable interrupts in EL1 + asm volatile( // Set the D, A, I, F bits to 1 in the DAIF register + "msr DAIFSet, 0xF;"); +} + +void irq_entry() { + disable_interrupt(); // Enter the critical section + + if (*IRQ_PENDING_1 & (1 << 29)) { // UART interrupt + // 0110 -> 0x2: Transmit interrupt, 0x4: Receive interrupt + switch (*AUX_MU_IIR & 0x6) { + case 0x2: + uart_disable_tx_interrupt(); + irq_add_task(uart_tx_irq_handler, PRIORITY_HIGH); + break; + case 0x4: + uart_disable_rx_interrupt(); + irq_add_task(uart_rx_irq_handler, PRIORITY_HIGH); + break; + } + } else if (*CORE0_INTERRUPT_SOURCE & 0x2) { // Core 0 timer interrupt + timer_disable_interrupt(); + irq_add_task(timer_irq_handler, PRIORITY_LOW); + } + + enable_interrupt(); // Leave the critical section + + // Preemption: run the task with the highest priority + while (head != 0 && !head->busy) { + disable_interrupt(); + irq_task_t *task = head; // Get a task from head + task->busy = 1; // Flag the task as under processing + enable_interrupt(); + + task->func(); // Run the tasks with interrupts enabled + + // Remove the task + disable_interrupt(); + if (task->prev != 0) task->prev->next = task->next; + if (task->next != 0) task->next->prev = task->prev; + if (task == head) head = task->next; + enable_interrupt(); + } +} \ No newline at end of file diff --git a/lab3/src/main.c b/lab3/src/main.c new file mode 100644 index 000000000..c0f235395 --- /dev/null +++ b/lab3/src/main.c @@ -0,0 +1,22 @@ +#include "devtree.h" +#include "initramfs.h" +#include "irq.h" +#include "malloc.h" +#include "shell.h" +#include "timer.h" +#include "uart.h" + +int main() { + /* Initialization */ + uart_init(); + malloc_init(); + enable_interrupt(); + timer_enable_interrupt(); + fdt_traverse(initramfs_callback); + + /* Shell */ + welcome_msg(); + run_shell(); + + return 0; +} \ No newline at end of file diff --git a/lab3/src/malloc.c b/lab3/src/malloc.c new file mode 100644 index 000000000..b583d6f8a --- /dev/null +++ b/lab3/src/malloc.c @@ -0,0 +1,16 @@ +#include "malloc.h" + +// `__bss_end` is defined in linker script +extern char *__bss_end; + +static char *heap_top; + +// Set heap base address +void malloc_init() { heap_top = (char *)&__bss_end; } + +void *simple_malloc(int size) { + void *p = (void *)heap_top; + if (size < 0) return 0; + heap_top += size; + return p; +} \ No newline at end of file diff --git a/lab3/src/mbox.c b/lab3/src/mbox.c new file mode 100644 index 000000000..7bb73792a --- /dev/null +++ b/lab3/src/mbox.c @@ -0,0 +1,44 @@ +#include "mbox.h" + +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +int mailbox_call(unsigned char c) { + unsigned int r = ((unsigned int)(((unsigned long)&mbox & ~0xF) | (c & 0xF))); + + // Wait until the mailbox is not full + while (*MAILBOX_REG_STATUS & MAILBOX_FULL) asm volatile("nop"); + + // Write to the register + *MAILBOX_REG_WRITE = r; + + while (1) { + // Wait for the response + while (*MAILBOX_REG_STATUS & MAILBOX_EMPTY) asm volatile("nop"); + + if (r == *MAILBOX_REG_READ) return mbox[1] == MAILBOX_RESPONSE; + } + return 0; +} + +int get_board_revision() { + mbox[0] = 7 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_BOARD_REVISION; + mbox[3] = 4; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = END_TAG; + return mailbox_call(MAILBOX_CH_PROP); +} + +int get_arm_memory_status() { + mbox[0] = 8 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_ARM_MEM; + mbox[3] = 8; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = 0; + mbox[7] = END_TAG; + return mailbox_call(MAILBOX_CH_PROP); +} \ No newline at end of file diff --git a/lab3/src/shell.c b/lab3/src/shell.c new file mode 100644 index 000000000..d7ad7d386 --- /dev/null +++ b/lab3/src/shell.c @@ -0,0 +1,88 @@ +#include "shell.h" + +#include "command.h" +#include "string.h" +#include "uart.h" + +void welcome_msg() { + cmd_info(); + uart_puts( + "*******************************\n" + "*** YADOS 0.03 for OSC 2024 ***\n" + "*******************************\n" + "Hopefully this will be Yet Another Dope OS!"); + uart_putc(NEWLINE); + uart_putc(NEWLINE); + cmd_hello(); +} + +void run_shell() { + while (1) { + char buffer[SHELL_BUF_SIZE]; + + uart_puts("# "); + read_user_input(buffer); + exec_command(buffer); + + // Stop the shell if rebooting + // if (!strcmp(buffer, "reboot")) break; + } +} + +void read_user_input(char *buf) { + int idx = 0; + while (idx < SHELL_BUF_SIZE) { + char c = uart_getc(); + if (c == NEWLINE) { + uart_putc(c); + buf[idx] = '\0'; + return; + } + if (c >= 32 && c <= 126) { + uart_putc(c); + buf[idx++] = c; + } else if (c == BACKSPACE || c == DELETE) { + // Handle backspaces + if (idx > 0) { + idx--; + // buf[idx--] = '\0'; + uart_putc(BACKSPACE); + uart_putc(' '); + uart_putc(BACKSPACE); + } + } else { + // Ignore unprintable chars + continue; + } + } + buffer_overflow_message(); +} + +void buffer_overflow_message() { + uart_putc(NEWLINE); + uart_puts("Buffer overflow:"); + uart_putc(NEWLINE); + uart_puts("Please re-enter your command."); + uart_putc(NEWLINE); +} + +int exec_command(const char *command) { + int i = 0; + + while (1) { + if (strlen(command) == 0) { + return 0; + } + if (!strcmp(commands[i].name, END_OF_COMMAND_LIST)) { + uart_puts("Command not found."); + uart_putc(NEWLINE); + return -1; + } + if (!strcmp(commands[i].name, command)) { + commands[i].func(); + return 0; + } + i++; + } + return 0; +} \ No newline at end of file diff --git a/lab3/src/string.c b/lab3/src/string.c new file mode 100644 index 000000000..ffe02eb48 --- /dev/null +++ b/lab3/src/string.c @@ -0,0 +1,40 @@ +#include "string.h" + +int strcmp(const char *str1, const char *str2) { + while (*str1 && *str1 == *str2) str1++, str2++; + return *(unsigned char *)str1 - *(unsigned char *)str2; +} + +int memcmp(const void *str1, const void *str2, int n) { + const unsigned char *a = str1, *b = str2; + while (n-- > 0) { + if (*a != *b) return *a - *b; + a++; + b++; + } + return 0; +} + +char *strncpy(char *dest, const char *src, int n) { + while (n-- && (*dest++ = *src++)); + return dest; +} + +int strlen(const char *str) { + int len = 0; + while (*str++ != '\0') len++; + return len; +} + +char *strcat(char *dest, const char *src) { + char *d = dest; + + // Move the pointer to the end of the dest string + while (*d != '\0') d++; + // Copy the src string to the end of the dest string + while (*src != '\0') *d++ = *src++; + // Add the null terminator + *d = '\0'; + + return dest; +} \ No newline at end of file diff --git a/lab3/src/timer.c b/lab3/src/timer.c new file mode 100644 index 000000000..f2e139067 --- /dev/null +++ b/lab3/src/timer.c @@ -0,0 +1,104 @@ +#include "timer.h" + +#include "malloc.h" +#include "string.h" +#include "uart.h" + +static int timeup = 0; + +typedef struct __timer_t { + void (*func)(void *); + void *arg; + int time; + struct __timer_t *next; +} timer_entry; + +static timer_entry *head = 0; + +void timer_enable_interrupt() { + asm volatile( + // Enable Counter-timer interrupt + "mov x0, 1;" + "msr cntp_ctl_el0, x0;" // Physical Timer Control register + + // Set the Counter-timer frequency to 1 second + "mrs x0, cntfrq_el0;" // Frequency register + "msr cntp_tval_el0, x0;" // Physical Timer TimerValue register + + // Unmask Counter-timer interrupt + "mov x0, 2;" + "ldr x1, =%0;" + "str w0, [x1];" // IRQ Enable Register + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1" // registers modified + ); +} + +void timer_disable_interrupt() { + asm volatile( + // Mask timer interrupt + "mov x0, 0;" + "ldr x1, =%0;" + "str w0, [x1];" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void timer_irq_handler() { + // Set up 1 second core timer interrupt + asm volatile( + "mrs x0, cntfrq_el0;" // Frequency register + "msr cntp_tval_el0, x0;" // Physical Timer TimerValue register + ); + + // Check the timer queue + while (head != 0 && timer_get_uptime() >= head->time) { + head->func(head->arg); // Execute the callback function + head = head->next; // Remove the head node after func finished + } + + timer_enable_interrupt(); +} + +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 timer_add(void (*callback)(void *), void *arg, int duration) { + // Insert the new timer node into the linked list (sorted by time) + + timer_entry *timer = (timer_entry *)simple_malloc(sizeof(timer_entry)); + timer->func = callback; + timer->arg = arg; + timer->time = timer_get_uptime() + duration; + timer->next = 0; + + if (head == 0 || timer->time < head->time) { + // Insert at the beginning of the list + timer->next = head; + head = timer; + return; + } + + timer_entry *current = head; + while (current->next != 0 && current->next->time <= timer->time) + current = current->next; + timer->next = current->next; + current->next = timer; +} + +void set_timer(const char *message, int duration) { + timer_add((void (*)(void *))uart_puts, (void *)message, duration); +} + +void reset_timeup() { timeup = 0; } + +int get_timeup() { return timeup; } + +void set_timeup() { timeup = 1; } \ No newline at end of file diff --git a/lab3/src/traps.c b/lab3/src/traps.c new file mode 100644 index 000000000..f4a97dc44 --- /dev/null +++ b/lab3/src/traps.c @@ -0,0 +1,34 @@ +#include + +#include "uart.h" + +void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr) { + // Print spsr_el1 + uart_puts("spsr_el1: "); + uart_hex(spsr); + uart_putc(NEWLINE); + + // Print elr_el1 + uart_puts(" elr_el1: "); + uart_hex(elr); + uart_putc(NEWLINE); + + // Print esr_el1 + uart_puts(" esr_el1: "); + uart_hex(esr); + uart_putc(NEWLINE); + uart_putc(NEWLINE); +} + +void exception_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + uart_puts("Exception Handler"); + uart_putc(NEWLINE); + print_registers(elr, esr, spsr); +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + uart_puts("[ERROR] The exception handler is not implemented"); + uart_putc(NEWLINE); + print_registers(elr, esr, spsr); + while (1); +} \ No newline at end of file diff --git a/lab3/src/uart.c b/lab3/src/uart.c new file mode 100644 index 000000000..1adfdd0e3 --- /dev/null +++ b/lab3/src/uart.c @@ -0,0 +1,126 @@ +#include "uart.h" + +#include "irq.h" +#include "string.h" + +static int uart_read_idx = 0; +static int uart_write_idx = 0; +static char uart_read_buffer[UART_BUF_SIZE]; +static char uart_write_buffer[UART_BUF_SIZE]; + +void uart_init() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + // Disable GPIO pull up/down + *GPPUD = 0; + for (int i = 0; i < 150; i++); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + // Initialize mini UART + *AUX_ENABLE |= 1; // Enable mini UART + *AUX_MU_CNTL = 0; // Disable Tx and Rx during setup + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set data size to 8 bits + *AUX_MU_MCR = 0; // Disable auto flow control + *AUX_MU_BAUD = 270; // Set baud rate to 115200 + *AUX_MU_IIR = 6; // No FIFO + *AUX_MU_CNTL = 3; // Enable Tx and Rx + + // Enable AUX interrupts + *ENABLE_IRQS_1 |= 1 << 29; +} + +char uart_getc() { + // Check the data ready field on bit 0 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); // Read from AUX_MU_IO_REG + return c == CARRIAGE_RETURN ? NEWLINE : c; +} + +void uart_putc(char c) { + // Convert '\n' to '\r\n' + if (c == NEWLINE) uart_putc(CARRIAGE_RETURN); + + // Check the transmitter empty field on bit 5 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = c; // Write to AUX_MU_IO_REG +} + +void uart_hex(unsigned int h) { + uart_puts("0x"); + unsigned int n; + for (int c = 28; c >= 0; c -= 4) { + n = (h >> c) & 0xF; + n += n > 9 ? 0x37 : '0'; + uart_putc(n); + } +} + +void uart_puts(const char *s) { + while (*s) { + uart_putc(*s++); + } +} + +void uart_enable_tx_interrupt() { *AUX_MU_IER |= 0x02; } + +void uart_disable_tx_interrupt() { *AUX_MU_IER &= 0x01; } + +void uart_enable_rx_interrupt() { *AUX_MU_IER |= 0x01; } + +void uart_disable_rx_interrupt() { *AUX_MU_IER &= 0x2; } + +void uart_tx_irq_handler() { + uart_disable_tx_interrupt(); + if (uart_write_idx < UART_BUF_SIZE && + uart_write_buffer[uart_write_idx] != 0) { + // Buffer is not empty -> send characters + // *AUX_MU_IO = uart_write_buffer[uart_write_idx++]; + uart_putc(uart_write_buffer[uart_write_idx++]); + uart_enable_tx_interrupt(); + } +} + +void uart_rx_irq_handler() { + char c = (char)(*AUX_MU_IO); + uart_read_buffer[uart_read_idx++] = (c == CARRIAGE_RETURN) ? NEWLINE : c; + if (uart_read_idx >= UART_BUF_SIZE) uart_read_idx = 0; + uart_enable_rx_interrupt(); +} + +void uart_async_read(char *buf, int len) { + uart_disable_rx_interrupt(); + for (int i = 0; i < uart_read_idx && i < len; i++) + buf[i] = uart_read_buffer[i]; + buf[uart_read_idx] = 0; + uart_read_idx = 0; +} + +void uart_async_write(const char *s) { + // Copy string to the write buffer + int len = 0; + while (*s != '\0') { + if (len >= UART_BUF_SIZE) { + uart_puts("[ERROR] Exceed the UART buffer size"); + uart_putc(NEWLINE); + return; + } + if (*s == NEWLINE) { + // Convert \n to \r\n + uart_write_buffer[len++] = CARRIAGE_RETURN; + uart_write_buffer[len++] = NEWLINE; + } else + uart_write_buffer[len++] = *s; + s++; + } + uart_write_buffer[len] = '\0'; + uart_write_idx = 0; // Reset the buffer index + uart_enable_tx_interrupt(); +} \ No newline at end of file diff --git a/lab3/src/utils.c b/lab3/src/utils.c new file mode 100644 index 000000000..f5226d183 --- /dev/null +++ b/lab3/src/utils.c @@ -0,0 +1,42 @@ +#include "utils.h" + +int align4(int n) { return n + (4 - n % 4) % 4; } + +int atoi(const char *s) { + int result = 0; + int sign = 1; + int i = 0; + + // Skip leading spaces + while (s[i] == ' ') { + i++; + } + + // Handle positive and negative sign + if (s[i] == '-') { + sign = -1; + i++; + } else if (s[i] == '+') { + i++; + } + + // Convert string to integer + while (s[i] >= '0' && s[i] <= '9') { + result = result * 10 + (s[i] - '0'); + i++; + } + + return sign * result; +} + +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; +} \ No newline at end of file diff --git a/lab3/start.S b/lab3/start.S new file mode 100644 index 000000000..c6b255ecf --- /dev/null +++ b/lab3/start.S @@ -0,0 +1,50 @@ +.section ".text.boot" + +.global _start + +_start: + /* get dtb address from x0 */ + ldr x1, =DTB_BASE // defined in devtree.c + str x0, [x1] + + /* get cpu id */ + mrs x1, mpidr_el1 /* Multiprocessor Affinity Register */ + and x1, x1, #3 + cbnz x1, halt // halt if cpu id != 0 + + /* set exception vector table */ + adr x0, exception_vector_table // defined in traps.S + msr vbar_el1, x0 + + /* switch from EL2 to EL1 */ + bl from_el2_to_el1 + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main + +halt: + wfe + b halt + +from_el2_to_el1: + mov x0, (1 << 31) + msr hcr_el2, x0 // EL1 uses aarch64 + mov x0, 0x3C5 // ... 0011 1100 0101 + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 diff --git a/lab3/traps.S b/lab3/traps.S new file mode 100644 index 000000000..80b4eafc4 --- /dev/null +++ b/lab3/traps.S @@ -0,0 +1,103 @@ +/* save general registers to stack */ +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp, 16 * 0] + stp x2, x3, [sp, 16 * 1] + stp x4, x5, [sp, 16 * 2] + stp x6, x7, [sp, 16 * 3] + stp x8, x9, [sp, 16 * 4] + stp x10, x11, [sp, 16 * 5] + stp x12, x13, [sp, 16 * 6] + stp x14, x15, [sp, 16 * 7] + stp x16, x17, [sp, 16 * 8] + stp x18, x19, [sp, 16 * 9] + stp x20, x21, [sp, 16 * 10] + stp x22, x23, [sp, 16 * 11] + stp x24, x25, [sp, 16 * 12] + stp x26, x27, [sp, 16 * 13] + stp x28, x29, [sp, 16 * 14] + str x30, [sp, 16 * 15] + mrs x28, spsr_el1 /* Saved Program Status Register (EL1) */ + mrs x29, elr_el1 /* Exception Link Register (EL1) */ + stp x28, x29, [sp, 16 * 16] +.endm + +/* load general registers from stack */ +.macro load_all + ldp x28, x29, [sp, 16 * 16] + msr spsr_el1, x28 /* Saved Program Status Register (EL1) */ + msr elr_el1, x29 /* Exception Link Register (EL1) */ + ldp x0, x1, [sp, 16 * 0] + ldp x2, x3, [sp, 16 * 1] + ldp x4, x5, [sp, 16 * 2] + ldp x6, x7, [sp, 16 * 3] + ldp x8, x9, [sp, 16 * 4] + ldp x10, x11, [sp, 16 * 5] + ldp x12, x13, [sp, 16 * 6] + ldp x14, x15, [sp, 16 * 7] + ldp x16, x17, [sp, 16 * 8] + ldp x18, x19, [sp, 16 * 9] + ldp x20, x21, [sp, 16 * 10] + ldp x22, x23, [sp, 16 * 11] + ldp x24, x25, [sp, 16 * 12] + ldp x26, x27, [sp, 16 * 13] + ldp x28, x29, [sp, 16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 9 +.endm + +exception_handler: + save_all + mrs x0, elr_el1 /* Exception Link Register (EL1) */ + mrs x1, esr_el1 /* ESR_EL1, Exception Syndrome Register (EL1) */ + mrs x2, spsr_el1 /* Saved Program Status Register (EL1) */ + bl exception_entry /* defined in traps.c */ + load_all + eret + +irq_handler: + save_all + bl irq_entry /* defined in irq.c */ + load_all + eret + +invalid_exc_handler: + save_all + mrs x0, elr_el1 /* Exception Link Register (EL1) */ + mrs x1, esr_el1 /* ESR_EL1, Exception Syndrome Register (EL1) */ + mrs x2, spsr_el1 /* Saved Program Status Register (EL1) */ + bl invalid_entry /* defined in traps.c */ + load_all + eret + +.macro v_entry 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 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL1 -> EL1 while using SP_EL1 + v_entry invalid_exc_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch64) -> EL1 + v_entry exception_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch32) -> EL1 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler diff --git a/lab4/.gitignore b/lab4/.gitignore new file mode 100644 index 000000000..eddcca901 --- /dev/null +++ b/lab4/.gitignore @@ -0,0 +1,6 @@ +.vscode +build +rootfs +temp +*.img +*.dtb \ No newline at end of file diff --git a/lab4/Makefile b/lab4/Makefile new file mode 100644 index 000000000..729b22978 --- /dev/null +++ b/lab4/Makefile @@ -0,0 +1,36 @@ +ARMGNU ?= aarch64-linux-gnu + +CFLAGS = -Iinclude -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +ASMFLAGS = -Iinclude +QEMUFLAGS = -M raspi3b -display none -serial null -serial stdio + +SRC_DIR = src +BUILD_DIR = build + +all: clean kernel8.img + +clean: + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/_%.o: %.S + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard *.S) +OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) +OBJ_FILES += $(ASM_FILES:%.S=$(BUILD_DIR)/_%.o) + +kernel8.img: linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/kernel8.elf kernel8.img + +qemu: all kernel8.img initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug: all kernel8.img initramfs.cpio bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -S -s -d int \ No newline at end of file diff --git a/lab4/bootloader/Makefile b/lab4/bootloader/Makefile new file mode 100644 index 000000000..d7882aa27 --- /dev/null +++ b/lab4/bootloader/Makefile @@ -0,0 +1,19 @@ +ARMGNU ?= aarch64-linux-gnu +CFLAGS = -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +QEMUFLAGS = -M raspi3b -display none -serial null -serial pty + +all: clean bootloader.img + +clean: + rm -rf build *.img + +bootloader.img: + mkdir -p build + $(ARMGNU)-gcc $(CFLAGS) -c main.c -o build/main.o + $(ARMGNU)-gcc $(CFLAGS) -c start.S -o build/start.o + $(ARMGNU)-gcc $(CFLAGS) -c boot.c -o build/boot.o + $(ARMGNU)-ld -T linker.ld -o build/bootloader.elf build/main.o build/start.o build/boot.o + $(ARMGNU)-objcopy -O binary build/bootloader.elf bootloader.img + +qemu: clean bootloader.img + qemu-system-aarch64 $(QEMUFLAGS) -kernel bootloader.img -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/lab4/bootloader/boot.c b/lab4/bootloader/boot.c new file mode 100644 index 000000000..d4ff535f5 --- /dev/null +++ b/lab4/bootloader/boot.c @@ -0,0 +1,61 @@ +#include "boot.h" + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + *GPPUD = 0; + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +char uart_recv() { + while (!(*AUX_MU_LSR & 0x01)) asm volatile("nop"); + return (char)(*AUX_MU_IO); +} + +void uart_putc(char c) { + if (c == '\n') uart_putc('\r'); + while (!(*AUX_MU_LSR & 0x20)) asm volatile("nop"); + *AUX_MU_IO = c; +} + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} + +int atoi(const char *s) { + int result = 0; + 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 * result; +} \ No newline at end of file diff --git a/lab4/bootloader/boot.h b/lab4/bootloader/boot.h new file mode 100644 index 000000000..84cece020 --- /dev/null +++ b/lab4/bootloader/boot.h @@ -0,0 +1,49 @@ +#ifndef BOOT_H +#define BOOT_H + +/* ==================== GPIO ==================== */ +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) + +/* ==================== UART ==================== */ +#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 init_uart(); +char uart_recv(); +void uart_putc(char c); +void uart_puts(const char *s); + +/* ==================== UTILS ==================== */ +int atoi(const char *s); + +#endif // BOOT_H \ No newline at end of file diff --git a/lab4/bootloader/linker.ld b/lab4/bootloader/linker.ld new file mode 100644 index 000000000..de083fba3 --- /dev/null +++ b/lab4/bootloader/linker.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + . = 0x60000; + __loader_start = .; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } + __loader_end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; +__loader_size = (__loader_end - __loader_start) >> 3; \ No newline at end of file diff --git a/lab4/bootloader/main.c b/lab4/bootloader/main.c new file mode 100644 index 000000000..8cc94937b --- /dev/null +++ b/lab4/bootloader/main.c @@ -0,0 +1,42 @@ +#include "boot.h" + +int main() { + init_uart(); + uart_puts("\033[2J\033[H"); + uart_puts( + "UART Bootloader\n" + "Waiting for kernel...\n"); + + // Get kernel image size + char buf[16] = {0}; + for (int i = 0; i < 16; i++) { + buf[i] = uart_recv(); + if (buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + + // Load kernel image + uart_puts("Kernel size: "); + uart_puts(buf); + uart_puts(" bytes.\n"); + uart_puts("Loading the kernel image...\n"); + + unsigned int size = atoi(buf); + char *kernel = (char *)0x80000; + while (size--) *kernel++ = uart_recv(); + + // Restore registers x0 x1 x2 x3 + // Jump to the new kernel + asm volatile( + "" + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + "mov x30, 0x80000;" + "ret;"); + + return 0; +} \ No newline at end of file diff --git a/lab4/bootloader/start.S b/lab4/bootloader/start.S new file mode 100644 index 000000000..ec8488dde --- /dev/null +++ b/lab4/bootloader/start.S @@ -0,0 +1,40 @@ +.section ".text.boot" + +.global _start + +_start: + /* save registers x0 x1 x2 x3 */ + mov x10, x0 /* dtb_base address */ + mov x11, x1 + mov x12, x2 + mov x12, x3 + + /* relocate bootloader */ + ldr x1, =0x80000 + ldr x2, =__loader_start // 0x60000 + ldr w3, =__loader_size + +relocate: + ldr x4, [x1], #8 + str x4, [x2], #8 + sub w3, w3, #1 + cbnz w3, relocate + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main-0x20000 + b run_main diff --git a/lab4/include/command.h b/lab4/include/command.h new file mode 100644 index 000000000..d81861093 --- /dev/null +++ b/lab4/include/command.h @@ -0,0 +1,31 @@ +#pragma once + +#define MAX_BUF_SIZE 1024 +#define END_OF_COMMAND_LIST "NULL" + +struct command { + const char *name; + const char *help; + void (*func)(void); +}; + +extern struct command commands[]; +extern unsigned int BOARD_REVISION; +extern unsigned int BASE_MEMORY; +extern unsigned int NUM_PAGES; + +// Commands +void cmd_help(); +void cmd_hello(); +void cmd_reboot(); +void cmd_cancel(); +void cmd_info(); +void cmd_cat(); +void cmd_run(); +void cmd_clear(); +void cmd_timer(); +void cmd_mem(); +void cmd_bd(); +void cmd_fm(); +void cmd_ca(); +void cmd_lab(); \ No newline at end of file diff --git a/lab4/include/devtree.h b/lab4/include/devtree.h new file mode 100644 index 000000000..0e9bb3da4 --- /dev/null +++ b/lab4/include/devtree.h @@ -0,0 +1,19 @@ +#pragma once + +#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; +}; + +uint32_t be2le(const void *s); +void fdt_traverse(void (*callback)(void *, char *)); diff --git a/lab4/include/hardware.h b/lab4/include/hardware.h new file mode 100644 index 000000000..41c5c6053 --- /dev/null +++ b/lab4/include/hardware.h @@ -0,0 +1,219 @@ +#pragma once + +#define MMIO_BASE 0x3F000000 + +// ARM Interrupt Registers + +#define IRQ_BASIC_PENDING (volatile unsigned int *)(MMIO_BASE + 0x0000B200) +#define IRQ_PENDING_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B204) +#define IRQ_PENDING_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B208) +#define FIQ_CONTROL (volatile unsigned int *)(MMIO_BASE + 0x0000B20C) +#define ENABLE_IRQS_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B210) +#define ENABLE_IRQS_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B214) +#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) +#define CORE0_INTERRUPT_SOURCE (volatile unsigned int *)(0x40000060) + +// Timers interrupt control registers + +#define CORE0_TIMER_IRQCNTL 0x40000040 +#define CORE1_TIMER_IRQCNTL 0x40000044 +#define CORE2_TIMER_IRQCNTL 0x40000048 +#define CORE3_TIMER_IRQCNTL 0x4000004C + +// Where to route timer interrupt to, IRQ/FIQ +// Setting both the IRQ and FIQ bit gives an FIQ +#define TIMER0_IRQ 0x01 +#define TIMER1_IRQ 0x02 +#define TIMER2_IRQ 0x04 +#define TIMER3_IRQ 0x08 +#define TIMER0_FIQ 0x10 +#define TIMER1_FIQ 0x20 +#define TIMER2_FIQ 0x40 +#define TIMER3_FIQ 0x80 + +// PASSWORD + +#define PM_PASSWORD 0x5A000000 +#define PM_RSTC (volatile unsigned int *)0x3F10001C +#define PM_WDOG (volatile unsigned int *)0x3F100024 + +// DEVICETREE + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +// GPIO + +#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 GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) +// Skip GPRENn, GPFENn +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +// Skip GPLENn, GPARENn +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +// UART + +#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)) + +// MAILBOX + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 +#define MAILBOX_REQUEST 0x00000000 +#define MAILBOX_RESPONSE 0x80000000 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +// Channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +#define MAILBOX_BASE (MMIO_BASE + 0x0000B880) + +// mailbox register +#define MAILBOX_REG_READ ((volatile unsigned int *)(MAILBOX_BASE + 0x00000000)) +#define MAILBOX_REG_STATUS \ + ((volatile unsigned int *)(MAILBOX_BASE + 0x00000018)) +#define MAILBOX_REG_WRITE ((volatile unsigned int *)(MAILBOX_BASE + 0x00000020)) + +// mailbox channel +// https://github.com/raspberrypi/firmware/wiki/Mailboxes#channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +// mailbox status +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 + +// tags +#define TAGS_REQ_CODE 0x00000000 +#define TAGS_REQ_SUCCEED 0x80000000 +#define TAGS_REQ_FAILED 0x80000001 +#define TAGS_END 0x00000000 + +// hardware tags operator +#define TAGS_HARDWARE_BOARD_MODEL 0x00010001 +#define TAGS_HARDWARE_BOARD_REVISION 0x00010002 +#define TAGS_HARDWARE_MAC_ADDR 0x00010003 +#define TAGS_HARDWARE_BOARD_SERIAL 0x00010004 +#define TAGS_HARDWARE_ARM_MEM 0x00010005 +#define TAGS_HARDWARE_VC_MEM 0x00010006 +#define TAGS_HARDWARE_CLOCKS 0x00010007 + +// CLOCK ID + +#define CLOCK_ID_RESERVED 0x000000000 +#define CLOCK_ID_EMMC 0x000000001 +#define CLOCK_ID_UART 0x000000002 +#define CLOCK_ID_ARM 0x000000003 +#define CLOCK_ID_CORE 0x000000004 +#define CLOCK_ID_V3D 0x000000005 +#define CLOCK_ID_H264 0x000000006 +#define CLOCK_ID_ISP 0x000000007 +#define CLOCK_ID_SDRAM 0x000000008 +#define CLOCK_ID_PIXEL 0x000000009 +#define CLOCK_ID_PWM 0x00000000a +#define CLOCK_ID_HEVC 0x00000000b +#define CLOCK_ID_EMMC2 0x00000000c +#define CLOCK_ID_M2MC 0x00000000d +#define CLOCK_ID_PIXEL_BVB 0x00000000e + +// clock tags operator +#define TAGS_GET_CLOCK 0x00030002 +#define TAGS_SET_CLOCK 0x00038002 + +// framebuffer tages operator +#define FB_ALLOC_BUFFER 0x00040001 +#define FB_FREE_BUFFER 0x00048001 + +#define FB_BLANK_SCREEN 0x00040002 + +#define FB_PHY_WID_HEIGHT_GET 0x00040003 +#define FB_PHY_WID_HEIGHT_TEST 0x00044003 +#define FB_PHY_WID_HEIGHT_SET 0x00048003 + +#define FB_VIR_WID_HEIGHT_GET 0x00040004 +#define FB_VIR_WID_HEIGHT_TEST 0x00044004 +#define FB_VIR_WID_HEIGHT_SET 0x00048004 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_PIXEL_ORDER_GET 0x00040006 +#define FB_PIXEL_ORDER_TEST 0x00044006 +#define FB_PIXEL_ORDER_SET 0x00048006 + +#define FB_ALPHA_MODE_GET 0x00040007 +#define FB_ALPHA_MODE_TEST 0x00044007 +#define FB_ALPHA_MODE_SET 0x00048007 + +#define FB_PITCH_GET 0x00040008 + +#define FB_VIR_OFFSET_GET 0x00040009 +#define FB_VIR_OFFSET_TEST 0x00044009 +#define FB_VIR_OFFSET_SET 0x00048009 + +#define FB_OVERSCAN_GET 0x0004000A +#define FB_OVERSCAN_TEST 0x0004400A +#define FB_OVERSCAN_SET 0x0004800A + +#define FB_PALETTE_GET 0x0004000B +#define FB_PALETTE_TEST 0x0004400B +#define FB_PALETTE_SET 0x0004800B + +#define FB_CURSOR_INFO_SET 0x00008010 +#define FB_CURSOR_STATE_SET 0x00008011 diff --git a/lab4/include/initramfs.h b/lab4/include/initramfs.h new file mode 100644 index 000000000..7704bc1b0 --- /dev/null +++ b/lab4/include/initramfs.h @@ -0,0 +1,32 @@ +#pragma once + +// Cpio Archive File Header (New ASCII Format) +typedef struct { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +} cpio_t; + +typedef struct { + int namesize; + int filesize; + int headsize; + int datasize; + char *pathname; +} ramfsRec; + +void initramfs_ls(); +void initramfs_cat(const char *target); +void initramfs_callback(void *addr, char *property); +void initramfs_run(const char *target); diff --git a/lab4/include/irq.h b/lab4/include/irq.h new file mode 100644 index 000000000..a7e823584 --- /dev/null +++ b/lab4/include/irq.h @@ -0,0 +1,10 @@ +#pragma once + +#include "hardware.h" + +#define ORDER_FIRST 0 +#define ORDER_LAST 255 + +void enable_interrupt(); +void disable_interrupt(); +void irq_entry(); \ No newline at end of file diff --git a/lab4/include/mbox.h b/lab4/include/mbox.h new file mode 100644 index 000000000..4ea42ba26 --- /dev/null +++ b/lab4/include/mbox.h @@ -0,0 +1,9 @@ +#pragma once + +#include "hardware.h" + +extern volatile unsigned int mbox[36]; + +int mailbox_call(unsigned char c); +int get_board_revision(); +int get_arm_memory_status(); diff --git a/lab4/include/mem.h b/lab4/include/mem.h new file mode 100644 index 000000000..75123f534 --- /dev/null +++ b/lab4/include/mem.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#define BUDDY_MAX_ORDER 16 +#define CACHE_MAX_ORDER 6 +#define PAGE_SIZE 0x1000 +#define MIN_CACHE_SIZE (PAGE_SIZE / (2 << CACHE_MAX_ORDER)) +#define DEFAULT_STACK_SIZE 8192 + +struct page { + unsigned int order; + unsigned int used; + unsigned int cache_order; + struct page *prev; + struct page *next; +}; + +struct object { + unsigned int order; + struct object *next; +}; + +void malloc_init(); + +struct page *allocatePagesByOrder(unsigned int order, int silent); +void freePages(struct page *page, unsigned int order, int silent); +void mergePages(struct page *page, unsigned int order, int echo); + +void *allocateCacheMemory(unsigned int index, int silent); +void freeCacheEntry(void *ptr, unsigned int index, int silent); + +void *kmalloc(unsigned int size, int silent); +void kfree(void *ptr, int silent); + +void init_mem(); +void reserveMemory(uint64_t start, uint64_t end); + +void printFreeListByOrder(unsigned int order); + +void pushPageToFreeList(struct page **list, struct page *page, + unsigned int order); +void removePageFromFreeList(struct page **list, struct page *page); +struct page *lookupBuddy(struct page *page, unsigned int order); \ No newline at end of file diff --git a/lab4/include/shell.h b/lab4/include/shell.h new file mode 100644 index 000000000..fccef918e --- /dev/null +++ b/lab4/include/shell.h @@ -0,0 +1,7 @@ +#pragma once + +#define SHELL_BUF_SIZE 1024 + +void run_shell(); +void read_user_input(char *buf); +int exec_command(const char *command); diff --git a/lab4/include/string.h b/lab4/include/string.h new file mode 100644 index 000000000..489673d47 --- /dev/null +++ b/lab4/include/string.h @@ -0,0 +1,7 @@ +#pragma once + +int strcmp(const char *str1, const char *str2); +int memcmp(const void *str1, const void *str2, int n); +char *strncpy(char *dest, const char *src, int n); +int strlen(const char *str); +char *strcat(char *dest, const char *src); \ No newline at end of file diff --git a/lab4/include/timer.h b/lab4/include/timer.h new file mode 100644 index 000000000..a5171a0df --- /dev/null +++ b/lab4/include/timer.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +typedef struct __timer_t { + void (*func)(void *); + void *arg; + int time; + struct __timer_t *next; +} timer_entry; + +void enable_timer_interrupt(); +void disable_timer_interrupt(); +void timer_irq_handler(); +uint64_t timer_get_uptime(); + +void timer_add(void (*callback)(void *), void *arg, int duration); +void set_timer(const char *message, int duration); diff --git a/lab4/include/uart.h b/lab4/include/uart.h new file mode 100644 index 000000000..f23d6c9a0 --- /dev/null +++ b/lab4/include/uart.h @@ -0,0 +1,31 @@ +#pragma once + +#include "hardware.h" + +#define UART_BUF_SIZE 1024 + +#define BACKSPACE '\b' +#define DELETE 127 +#define ESC 27 +#define NEWLINE '\n' +#define TAB '\t' +#define CARRIAGE_RETURN '\r' + +void init_uart(); +char uart_getc(); +void uart_putc(char c); +void uart_hex(unsigned int h); +void uart_simple_hex(unsigned int h); +void uart_dec(unsigned int h); +void uart_puts(const char *s); + +void enable_uart_tx_interrupt(); +void disable_uart_tx_interrupt(); +void enable_uart_rx_interrupt(); +void disable_uart_rx_interrupt(); + +void uart_tx_irq_handler(); +void uart_rx_irq_handler(); + +void uart_async_read(char *buf, int len); +void uart_async_write(const char *s); \ No newline at end of file diff --git a/lab4/include/utils.h b/lab4/include/utils.h new file mode 100644 index 000000000..f3d3597d8 --- /dev/null +++ b/lab4/include/utils.h @@ -0,0 +1,20 @@ +#pragma once + +/** + * @brief Align `n` to be a multiple of 4. + * + * @param n A number + * @return Algined number + */ +int align4(int n); + +int atoi(const char *s); + +/** + * @brief Convert hexadecimal string to int. + * + * @param s: hexadecimal string + * @param n: string length + * @return Converted int number + */ +int hextoi(char *s, int n); \ No newline at end of file diff --git a/lab4/initramfs.cpio b/lab4/initramfs.cpio new file mode 100644 index 000000000..4710cde21 Binary files /dev/null and b/lab4/initramfs.cpio differ diff --git a/lab4/linker.ld b/lab4/linker.ld new file mode 100644 index 000000000..b545f82af --- /dev/null +++ b/lab4/linker.ld @@ -0,0 +1,13 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab4/sender.sh b/lab4/sender.sh new file mode 100755 index 000000000..501756d76 --- /dev/null +++ b/lab4/sender.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This script sends the kernel image to Rpi3 through UART + +DEST_PATH="/dev/ttyUSB0" +KERNEL_PATH="./kernel8.img" + +# Check the root permission +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +if [ $1 ] +then + DEST_PATH="$1" +fi + +# Get the size of the kernel image file and send it to Rpi3 +# wc -c: count bytes of a file +# sleep: wait n seconds +wc -c < $KERNEL_PATH > $DEST_PATH | sleep 1 + +# Send the kernel image +# pv: redirect file input to specified tty +# add --rate-limit option to limit the speed +pv $KERNEL_PATH > $DEST_PATH diff --git a/lab4/src/command.c b/lab4/src/command.c new file mode 100644 index 000000000..fcbd0747c --- /dev/null +++ b/lab4/src/command.c @@ -0,0 +1,228 @@ +#include "command.h" + +#include "initramfs.h" +#include "mem.h" +#include "shell.h" +#include "string.h" +#include "timer.h" +#include "uart.h" +#include "utils.h" + +#define DEMO_ALLOCATED_LIST_MAX 64 + +static void *allocatedList[DEMO_ALLOCATED_LIST_MAX]; +static int allocatedListSize = 0; + +struct command commands[] = { + {.name = "help", .help = "Display this help menu", .func = cmd_help}, + {.name = "hello", .help = "Print 'Hello there!'", .func = cmd_hello}, + {.name = "clear", .help = "Clear the screen", .func = cmd_clear}, + {.name = "reboot", .help = "Reboot the device", .func = cmd_reboot}, + {.name = "cancel", .help = "Cancel a scheduled reboot", .func = cmd_cancel}, + {.name = "info", .help = "Show hardware information", .func = cmd_info}, + {.name = "ls", .help = "List files in ramdisk", .func = initramfs_ls}, + {.name = "cat", .help = "Display content of ramdisk file", .func = cmd_cat}, + {.name = "run", .help = "Run a specified program", .func = cmd_run}, + {.name = "timer", .help = "Set timer with duration", .func = cmd_timer}, + {.name = "mem", .help = "Display free memory blocks", .func = cmd_mem}, + {.name = "bd", .help = "Allocate a memory block", .func = cmd_bd}, + {.name = "ca", .help = "Use the dynamic memory allocator", .func = cmd_ca}, + {.name = "fm", .help = "Free all memory in the demo list", .func = cmd_fm}, + {.name = "lab", .help = "Showcase lab requirements", .func = cmd_lab}, + {.name = END_OF_COMMAND_LIST}}; + +void cmd_mem() { + for (int i = BUDDY_MAX_ORDER; i >= 0; i--) { + printFreeListByOrder(i); + } +} + +static void allocatedListFullMessage() { + uart_puts( + "Max memory block allocation for demo is reached.\n" + "Consider using 'fm' to clear all allocated memory blocks.\n"); +} + +void cmd_bd() { + if (allocatedListSize >= DEMO_ALLOCATED_LIST_MAX) { + allocatedListFullMessage(); + return; + } + uart_puts("Buddy System: request order (0-"); + uart_dec(BUDDY_MAX_ORDER); + uart_puts("): "); + char *buf = (char *)kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int order = atoi(buf); + if (buf[0] < '0' || buf[0] > '9' || order < 0 || order > BUDDY_MAX_ORDER) { + uart_puts("Invalid order."); + uart_putc(NEWLINE); + } else + allocatedList[allocatedListSize++] = kmalloc(PAGE_SIZE << order, 0); + kfree(buf, 1); +} + +void cmd_fm() { + if (allocatedListSize == 0) { + uart_puts("No memory block allocated."); + uart_putc(NEWLINE); + } else { + uart_puts("Freeing all allocated memory blocks..."); + uart_putc(NEWLINE); + for (int i = 0; i < allocatedListSize; i++) { + kfree(allocatedList[i], 0); + } + allocatedListSize = 0; + } +} + +void cmd_ca() { + if (allocatedListSize >= DEMO_ALLOCATED_LIST_MAX) { + allocatedListFullMessage(); + return; + } + uart_puts("Dynamic Memory Allocator: request byte(s) (1-"); + uart_dec(PAGE_SIZE / 2); + uart_puts("): "); + char *buf = (char *)kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int size = atoi(buf); + if (buf[0] == '\0' || size <= 0) { + uart_puts("Invalid size for dynamic allocator."); + uart_putc(NEWLINE); + } else { + if (size > PAGE_SIZE / 2) { + uart_puts( + "[INFO] Size too large for dynamic allocator.\n" + "[INFO] Switching to Buddy System.\n"); + } + void *p = kmalloc(size, 0); + allocatedList[allocatedListSize++] = p; + // kfree(p, 0); + } + kfree(buf, 1); +} + +static int *timeup = 0; +static void set_timeup(int *timeup) { *timeup = 1; } + +void cmd_lab() { + uart_puts("(1) Lab 3: UART async write"); + uart_putc(NEWLINE); + uart_puts("(2) Lab 3: UART async read"); + uart_putc(NEWLINE); + uart_puts("Please select: "); + char *buf = (char *)kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + switch (atoi(buf)) { + case 1: + uart_puts(": "); + strncpy(buf, "[INFO] Async write: ", strlen("[INFO] Async write: ")); + read_user_input(buf + strlen("[INFO] Async write: ")); + // read_user_input(buf); + uart_async_write(buf); + break; + case 2: + uart_puts("(Please type something in 3 sec.)"); + uart_putc(NEWLINE); + // set a timer to 3 sec. + *timeup = 0; + timer_add((void (*)(void *))set_timeup, (void *)timeup, 3); + enable_uart_rx_interrupt(); + while (!*timeup); + // time's up, get the buf content and print + uart_async_read(buf, SHELL_BUF_SIZE); + uart_puts("[INFO] Async read received: "); + uart_puts(buf); + uart_putc(NEWLINE); + break; + default: + uart_puts("Option not found."); + uart_putc(NEWLINE); + } + kfree(buf, 1); +} + +void cmd_timer() { + uart_puts("Duration(sec.): "); + char *buf = (char *)kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int sec = atoi(buf); + + char *msg = (char *)kmalloc(SHELL_BUF_SIZE, 0); + uart_puts(": "); + read_user_input(msg); + set_timer(msg, sec); + kfree(buf, 1); +} + +void cmd_run() { + // Get filename from user input + uart_puts(": "); + char *buf = (char *)kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + initramfs_run(buf); + kfree(buf, 1); +} + +void cmd_help() { + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); + struct command *cmd = commands; + while (1) { + if (!strcmp(cmd->name, END_OF_COMMAND_LIST)) { + break; + } + uart_puts(cmd->name); + uart_putc(TAB); + uart_puts(": "); + uart_puts(cmd->help); + uart_putc(NEWLINE); + cmd++; + } + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); +} + +void cmd_hello() { + uart_puts("Hello there!"); + uart_putc(NEWLINE); +} + +void cmd_reboot() { + uart_puts("Start Rebooting..."); + uart_putc(NEWLINE); + // Reboot after 0x20000 ticks + *PM_RSTC = PM_PASSWORD | 0x20; // Full reset + *PM_WDOG = PM_PASSWORD | 0x20000; // Number of watchdog ticks +} + +void cmd_cancel() { + uart_puts("Rebooting Attempt Aborted, if any."); + uart_putc(NEWLINE); + *PM_RSTC = PM_PASSWORD | 0; + *PM_WDOG = PM_PASSWORD | 0; +} + +void cmd_info() { + uart_puts("[INFO] board revision: "); + uart_hex(BOARD_REVISION); + uart_putc(NEWLINE); + uart_puts("[INFO] device base memory address: "); + uart_hex(BASE_MEMORY); + uart_putc(NEWLINE); + uart_puts("[INFO] device memory size: "); + uart_hex(NUM_PAGES * PAGE_SIZE); + uart_putc(NEWLINE); +} + +void cmd_cat() { + // Get filename from user input + uart_puts(": "); + char *buf = (char *)kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + initramfs_cat(buf); + kfree(buf, 1); +} + +void cmd_clear() { uart_puts("\033[2J\033[H"); } diff --git a/lab4/src/devtree.c b/lab4/src/devtree.c new file mode 100644 index 000000000..0c905e312 --- /dev/null +++ b/lab4/src/devtree.c @@ -0,0 +1,67 @@ +#include "devtree.h" + +#include "string.h" +#include "uart.h" +#include "utils.h" + +// Assign a non-zero value to be stored in the .data section +void *DTB_BASE = (void *)0xF; +void *DTB_END = (void *)0xF; + +/** + * @brief Convert a 4-byte big-endian sequence to little-endian. + * + * @param s: big-endian sequence + * @return little-endian sequence + */ +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]; +} + +void fdt_traverse(void (*callback)(void *, char *)) { + struct fdt_header *header = (struct fdt_header *)(uintptr_t)DTB_BASE; + + // Check the magic number + if (be2le(&(header->magic)) != 0xD00DFEED) { + uart_puts("[WARN] Dtb header magic does not match!"); + uart_putc(NEWLINE); + } + DTB_END = DTB_BASE + be2le(&header->totalsize); + + uart_puts("[INFO] Dtb loaded at "); + uart_hex((uintptr_t)DTB_BASE); + uart_putc('-'); + uart_hex((uintptr_t)DTB_END); + uart_putc(NEWLINE); + + uintptr_t structure = (uintptr_t)header + be2le(&header->off_dt_struct); + uintptr_t strings = (uintptr_t)header + be2le(&header->off_dt_strings); + uint32_t structure_size = be2le(&header->size_dt_struct); + + // Parse the structure block + uintptr_t ptr = structure; // Point to the beginning of structure block + while (ptr < structure + structure_size) { + uint32_t token = be2le((char *)ptr); + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + ptr += align4(strlen((char *)ptr) + 1); + break; + case FDT_PROP: + uint32_t len = be2le((char *)ptr); + ptr += 4; + uint32_t nameoff = be2le((char *)ptr); + ptr += 4; + callback((void *)(uintptr_t)be2le((void *)ptr), + (char *)(strings + nameoff)); + ptr += align4(len); + break; + case FDT_END_NODE: + case FDT_NOP: + case FDT_END: + } + } +} \ No newline at end of file diff --git a/lab4/src/initramfs.c b/lab4/src/initramfs.c new file mode 100644 index 000000000..6ed817dc5 --- /dev/null +++ b/lab4/src/initramfs.c @@ -0,0 +1,124 @@ +#include "initramfs.h" + +#include + +#include "mem.h" +#include "string.h" +#include "uart.h" +#include "utils.h" + +void *initrd_start; +void *initrd_end; + +void initramfs_callback(void *addr, char *property) { + if (!strcmp(property, "linux,initrd-start")) { + uart_puts("[INFO] linux,initrd-start: "); + uart_hex((uintptr_t)addr); + initrd_start = (char *)addr; + uart_putc(NEWLINE); + } else if (!strcmp(property, "linux,initrd-end")) { + uart_puts("[INFO] linux,initrd-end: "); + uart_hex((uintptr_t)addr); + initrd_end = (char *)addr; + uart_putc(NEWLINE); + } +} + +static ramfsRec *ramfsNext(char *fptr) { + ramfsRec *rec = (ramfsRec *)kmalloc(sizeof(ramfsRec), 1); + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + rec->namesize = hextoi(header->c_namesize, 8); + rec->filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + rec->headsize = align4(sizeof(cpio_t) + rec->namesize); + rec->datasize = align4(rec->filesize); + + // Get file pathname + rec->pathname = kmalloc(rec->namesize, 1); + strncpy(rec->pathname, fptr + sizeof(cpio_t), rec->namesize); + + return rec; +} + +void initramfs_ls() { + uart_putc(NEWLINE); + + char *fptr = (char *)initrd_start; + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + uart_puts(rec->pathname); + uart_putc(TAB); + uart_dec(rec->filesize); + uart_puts(" byte"); + if (rec->filesize > 1) uart_putc('s'); + uart_putc(NEWLINE); + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } +} + +void initramfs_cat(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(filename, rec->pathname)) { + // Dump its content + uart_putc(NEWLINE); + for (char *c = fptr + rec->headsize; + c < fptr + rec->headsize + rec->filesize; c++) { + uart_putc(*c); + } + uart_putc(NEWLINE); + uart_putc(NEWLINE); + // kfree(data, 0); + kfree(rec->pathname, 1); + kfree(rec, 1); + return; + } + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +void initramfs_run(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + // Match target filename + if (!strcmp(filename, rec->pathname)) { + // Load the user program + char *program = (char *)0x40000; + for (int i = 0; i < rec->filesize; i++) + *program++ = (fptr + rec->headsize)[i]; + unsigned long sp = (unsigned long)kmalloc(DEFAULT_STACK_SIZE, 0); + asm volatile( + "msr spsr_el1, %0;" + "msr elr_el1, %1;" + "msr sp_el0, %2;" + "eret" // Return to EL0 and execute + : + : "r"(0x3C0), "r"(0x40000), "r"(sp + DEFAULT_STACK_SIZE) + : "memory"); + return; + } + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} \ No newline at end of file diff --git a/lab4/src/irq.c b/lab4/src/irq.c new file mode 100644 index 000000000..d57fdb74b --- /dev/null +++ b/lab4/src/irq.c @@ -0,0 +1,98 @@ +#include "irq.h" + +#include "mem.h" +#include "timer.h" +#include "uart.h" + +typedef struct __irq_task_t { + void (*func)(); + int order; + int busy; // 0 (default) or 1 (busy) + struct __irq_task_t *prev; + struct __irq_task_t *next; +} irq_task_t; + +static irq_task_t *head = 0; + +// Disable interrupt before calling irq_add_task() +void irq_add_task(void (*callback)(), int order) { + irq_task_t *task = (irq_task_t *)kmalloc(sizeof(irq_task_t), 1); + task->func = callback; + task->order = order; + task->busy = 0; + task->prev = 0; + task->next = 0; + + // 0 -> task -> head -> ... + if (head == 0 || task->order < head->order) { + task->next = head; + task->prev = 0; + if (head != 0) head->prev = task; + head = task; + return; + } + + irq_task_t *current = head; + while (current->next != 0 && current->next->order <= task->order) + current = current->next; + task->next = current->next; + if (current->next != 0) current->next->prev = task; + current->next = task; + task->prev = current; +} + +// to enable interrupts in EL1 +void enable_interrupt() { + asm volatile( + "msr DAIFClr, 0xF\n" // Clear the D, A, I, F bits in the DAIF register + ); +} + +// to disable interrupts in EL1 +void disable_interrupt() { + asm volatile( + "msr DAIFSet, 0xF\n" // Set the D, A, I, F bits to 1 in the DAIF register + ); +} + +void irq_entry() { + disable_interrupt(); // Enter the critical section + + if (*IRQ_PENDING_1 & (1 << 29)) { // UART interrupt + switch (*AUX_MU_IIR & 0x6) { // 0x6 = 0110 -> Get 0x2 and 0x4 + + case 0x2: // 0x2 Transmit interrupt + disable_uart_tx_interrupt(); + irq_add_task(uart_tx_irq_handler, ORDER_FIRST); + break; + + case 0x4: // 0x4 Receive interrupt + disable_uart_rx_interrupt(); + irq_add_task(uart_rx_irq_handler, ORDER_FIRST); + break; + } + } else if (*CORE0_INTERRUPT_SOURCE & 0x2) { // Core 0 timer interrupt + disable_timer_interrupt(); + irq_add_task(timer_irq_handler, ORDER_LAST); + } + + enable_interrupt(); // Leave the critical section + + // Preemption: run the task with the highest priority + while (head != 0 && !head->busy) { + disable_interrupt(); + irq_task_t *task = head; // Get a task from head + task->busy = 1; // Flag the task as under processing + enable_interrupt(); + + task->func(); // Run the tasks with interrupts enabled + + // Remove the task + disable_interrupt(); + if (task->prev != 0) task->prev->next = task->next; + if (task->next != 0) task->next->prev = task->prev; + if (task == head) head = task->next; + kfree((void *)task, 1); + enable_interrupt(); + } +} \ No newline at end of file diff --git a/lab4/src/main.c b/lab4/src/main.c new file mode 100644 index 000000000..5b6bf0d14 --- /dev/null +++ b/lab4/src/main.c @@ -0,0 +1,21 @@ +#include "devtree.h" +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "shell.h" +#include "timer.h" +#include "uart.h" + +int main() { + /* Initialization */ + fdt_traverse(initramfs_callback); + init_mem(); + + init_uart(); + enable_interrupt(); + enable_timer_interrupt(); + + run_shell(); + + return 0; +} \ No newline at end of file diff --git a/lab4/src/mbox.c b/lab4/src/mbox.c new file mode 100644 index 000000000..7bb73792a --- /dev/null +++ b/lab4/src/mbox.c @@ -0,0 +1,44 @@ +#include "mbox.h" + +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +int mailbox_call(unsigned char c) { + unsigned int r = ((unsigned int)(((unsigned long)&mbox & ~0xF) | (c & 0xF))); + + // Wait until the mailbox is not full + while (*MAILBOX_REG_STATUS & MAILBOX_FULL) asm volatile("nop"); + + // Write to the register + *MAILBOX_REG_WRITE = r; + + while (1) { + // Wait for the response + while (*MAILBOX_REG_STATUS & MAILBOX_EMPTY) asm volatile("nop"); + + if (r == *MAILBOX_REG_READ) return mbox[1] == MAILBOX_RESPONSE; + } + return 0; +} + +int get_board_revision() { + mbox[0] = 7 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_BOARD_REVISION; + mbox[3] = 4; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = END_TAG; + return mailbox_call(MAILBOX_CH_PROP); +} + +int get_arm_memory_status() { + mbox[0] = 8 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_ARM_MEM; + mbox[3] = 8; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = 0; + mbox[7] = END_TAG; + return mailbox_call(MAILBOX_CH_PROP); +} \ No newline at end of file diff --git a/lab4/src/mem.c b/lab4/src/mem.c new file mode 100644 index 000000000..a5b917f1f --- /dev/null +++ b/lab4/src/mem.c @@ -0,0 +1,373 @@ +#include "mem.h" + +#include "devtree.h" +#include "mbox.h" +#include "string.h" +#include "uart.h" +#include "utils.h" + +extern char *__bss_end; +extern void *DTB_BASE; +extern void *DTB_END; +extern void *initrd_start; +extern void *initrd_end; + +unsigned int BOARD_REVISION; +unsigned int BASE_MEMORY; +unsigned int NUM_PAGES; + +static struct page *pageTable; +static struct page *freeList[BUDDY_MAX_ORDER + 1]; +static struct object *objectCache[CACHE_MAX_ORDER + 1]; + +// `__bss_end` is defined in linker script +extern char *__bss_end; +static char *heap_top; + +static void *simple_malloc(int size) { + void *p = (void *)heap_top; + if (size < 0) return 0; + heap_top += size; + return p; +} + +void init_mem() { + // Simple malloc init: Set heap base address + heap_top = (char *)&__bss_end; + + if (BOARD_REVISION == 0) { + // Get board revision + get_board_revision(); + BOARD_REVISION = mbox[5]; + } + // Get ARM memory base address and size + if (NUM_PAGES == 0) { + get_arm_memory_status(); + BASE_MEMORY = mbox[5]; + NUM_PAGES = (mbox[6] - BASE_MEMORY) / PAGE_SIZE; + } + // Initialize the buddy allocator + pageTable = simple_malloc(sizeof(struct page) * NUM_PAGES); + unsigned int current_order = BUDDY_MAX_ORDER; + // for (int i = NUM_PAGES - 1; i >= 0; i--) { + for (int i = 0; i < NUM_PAGES; i++) { + pageTable[i].order = 0; + pageTable[i].used = 0; + pageTable[i].cache_order = -1; + pageTable[i].prev = 0; + pageTable[i].next = 0; + if (i % (1 << current_order) == 0) { + while (current_order > 0 && NUM_PAGES - i < (1 << current_order)) { + current_order--; + } + pushPageToFreeList(&freeList[current_order], &pageTable[i], + current_order); + } + } + unsigned int sa = NUM_PAGES * sizeof(struct page); + + // Reserve memory: + // Spin tables for multicore boot + reserveMemory(0x0, 0x1000); + // User program + reserveMemory(0x40000, 0x40000 + PAGE_SIZE); + // Kernel & stack; Simple allocator + reserveMemory(0x80000 - DEFAULT_STACK_SIZE, (unsigned long)&__bss_end + sa); + // Initramfs + reserveMemory((uint64_t)initrd_start, (uint64_t)initrd_end); + // Devicetree + reserveMemory((uint64_t)DTB_BASE, (uint64_t)DTB_END); +} + +void reserveMemory(uint64_t resv_start, uint64_t resv_end) { + // Round the start and end addresses to the page boundary + resv_start = resv_start & ~(PAGE_SIZE - 1); + resv_end = (resv_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + resv_end--; + + uart_puts("[INFO] Reserving memory: "); + uart_hex(resv_start); + uart_putc('-'); + uart_hex(resv_end); + uart_putc(NEWLINE); + + for (int order = BUDDY_MAX_ORDER; order >= 0; order--) { + struct page *current = freeList[order]; + while (current != 0) { + struct page *next = current->next; + uint64_t page_start = (current - pageTable) * PAGE_SIZE; + uint64_t page_end = page_start + (PAGE_SIZE << order) - 1; + if (page_start >= resv_start && page_end <= resv_end) { + // [page page] + // Remove the page from the free list + current->used = 1; + removePageFromFreeList(&freeList[order], current); + } else if (resv_start > page_end || resv_end < page_start) { + // ---resv> [page or page] or order = order; + page->used = 0; + page->prev = 0; + page->next = 0; + + if (*list_head == 0 || (*list_head) < page) { + if (*list_head != 0) (*list_head)->prev = page; + page->next = *list_head; + *list_head = page; + return; + } + + struct page *current = *list_head; + while (current->next != 0 && page < current->next) { + current = current->next; + } + page->prev = current; + page->next = current->next; + if (current->next != 0) current->next->prev = page; + current->next = page; +} + +struct page *popFreeList(struct page **list_head) { + if (*list_head == 0) return 0; + + struct page *page = *list_head; + *list_head = page->next; + page->used = 1; + return page; +} + +void removePageFromFreeList(struct page **list_head, struct page *page) { + if (page->prev != 0) page->prev->next = page->next; + if (page->next != 0) page->next->prev = page->prev; + if (page == *list_head) *list_head = page->next; +} + +void printFreeListByOrder(unsigned int order) { + struct page *page = freeList[order]; + if (page > 0) uart_puts("[BUDD]"); + while (page != 0) { + uart_putc(TAB); + uart_hex((unsigned int)(page - pageTable) * PAGE_SIZE); + uart_puts("-"); + uart_hex((unsigned int)(page - pageTable + (1 << order)) * PAGE_SIZE - 1); + uart_puts(" ["); + if (order < 10) uart_putc(' '); + uart_dec(order); + uart_puts("] "); + uart_putc(NEWLINE); + page = page->next; + } +} + +struct page *lookupBuddy(struct page *page, unsigned int order) { + unsigned int buddy_pfn = (unsigned int)(page - pageTable) ^ (1 << order); + return &pageTable[buddy_pfn]; +} + +struct page *allocatePagesByOrder(unsigned int order, int silent) { + if (!silent) { + uart_puts("[BUDD] "); + uart_dec(1 << order); + uart_puts("-page memory block requested."); + uart_putc(NEWLINE); + } + + for (int i = order; i <= BUDDY_MAX_ORDER; i++) { + if (freeList[i] == 0) // No free page available + continue; // Try next order + struct page *page = popFreeList(&freeList[i]); + page->order = order; // Update order of the page + + while (i > order) { // requires splitting + i--; + struct page *buddy = lookupBuddy(page, i); + pushPageToFreeList(&freeList[i], buddy, i); + + if (silent) continue; + // Print information + unsigned int pfn = page - pageTable; + unsigned int buddy_pfn = buddy - pageTable; + uart_puts("[BUDD] Split "); + uart_hex(pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((pfn + (1 << i)) * PAGE_SIZE - 1); + uart_puts("//"); + uart_hex(buddy_pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((buddy_pfn + (1 << i)) * PAGE_SIZE - 1); + uart_puts(" => ["); + uart_dec(i); + uart_puts("]"); + uart_putc(NEWLINE); + } + if (!silent) { + uart_puts("[BUDD] Memory allocated: "); + uart_hex((unsigned int)(page - pageTable) * PAGE_SIZE); + uart_puts("-"); + uart_hex((unsigned int)(page - pageTable + (1 << order)) * PAGE_SIZE - 1); + uart_putc(NEWLINE); + } + + return page; + } + return 0; +} + +void freePages(struct page *page, unsigned int order, int silence) { + if (!silence) { + uart_puts("[BUDD] Free "); + uart_dec(1 << order); + uart_puts("-page memory block starting from "); + uart_hex((unsigned int)(page - pageTable) * PAGE_SIZE); + uart_putc(NEWLINE); + } + mergePages(page, order, silence); +} + +void mergePages(struct page *page, unsigned int order, int silence) { + struct page *current = page; + while (order < BUDDY_MAX_ORDER) { + struct page *buddy = lookupBuddy(current, order); + if (buddy->order != order || buddy->used == 1) break; + + removePageFromFreeList(&freeList[order], buddy); + + if (current > buddy) { + struct page *tmp = current; + current = buddy; + buddy = tmp; + } + + order++; + if (silence) continue; + unsigned int pfn = current - pageTable; + unsigned int buddy_pfn = buddy - pageTable; + uart_puts("[BUDD] Merge "); + uart_hex(pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((pfn + (1 << (order - 1))) * PAGE_SIZE - 1); + uart_puts("~~"); + uart_hex(buddy_pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((buddy_pfn + (1 << (order - 1))) * PAGE_SIZE - 1); + uart_puts(" => ["); + uart_dec(order); + uart_puts("]"); + uart_putc(NEWLINE); + } + pushPageToFreeList(&freeList[order], current, order); +} + +/* Cache Allocator */ + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order) { + object->order = order; + object->next = *list_head; + *list_head = object; +} + +struct object *popObjectFromCache(struct object **list_head) { + if (*list_head == 0) return 0; + + struct object *object = *list_head; + *list_head = object->next; + return object; +} + +void *allocateCacheMemory(unsigned int order, int silent) { + if (!silent) { + uart_puts("[CACH] Allocating "); + uart_dec(MIN_CACHE_SIZE << order); + uart_puts(" bytes."); + uart_putc(NEWLINE); + } + + if (objectCache[order] == 0) { + struct page *page = allocatePagesByOrder(0, silent); + page->cache_order = order; + unsigned int page_addr = (page - pageTable) * PAGE_SIZE; + unsigned int cache_size = MIN_CACHE_SIZE << order; + for (int i = 0; i < PAGE_SIZE; i += cache_size) { + struct object *obj = (struct object *)(uintptr_t)(page_addr + i); + pushObjectToList(&objectCache[order], obj, order); + } + } + void *p = popObjectFromCache(&objectCache[order]); + if (!silent) { + uart_puts("[CACH] Allocated memory starting from "); + uart_hex((uintptr_t)p); + uart_putc(NEWLINE); + } + return p; +} + +void freeCacheEntry(void *ptr, unsigned int index, int silence) { + if (!silence) { + uart_puts("[CACH] Free memory cache of "); + uart_dec(MIN_CACHE_SIZE << index); + uart_puts(" bytes starting from "); + uart_hex((uintptr_t)ptr); + uart_putc(NEWLINE); + } + pushObjectToList(&objectCache[index], ptr, index); +} + +/* Dynamic Memory Allocator */ + +void *kmalloc(unsigned int size, int silent) { + if (size == 0) return 0; + + if (!silent) { + uart_puts("[INFO] Memory requested: "); + uart_dec(size); + uart_puts(" byte(s)."); + uart_putc(NEWLINE); + } + + if (size > PAGE_SIZE / 2) { + // Buddy Allocator + int order = 0; + while ((PAGE_SIZE << order) < size) order++; + struct page *page = allocatePagesByOrder(order, silent); + return (void *)((page - pageTable) * PAGE_SIZE); + } else { + // Cache Allocator + int power = 0; + while ((1 << power) < size) power++; + int order = (power > 5) ? power - 5 : 0; + return allocateCacheMemory(order, silent); + } +} + +void kfree(void *ptr, int silence) { + // Check if the pointer is page-aligned + struct page *page = &pageTable[(uintptr_t)ptr / PAGE_SIZE]; + if ((uintptr_t)ptr % PAGE_SIZE == 0) { + // Check if the page is allocated by the buddy allocator + if (page->cache_order == -1) { + // Free the page using the buddy allocator + freePages(page, page->order, silence); + return; + } + } + // Free the object using the cache allocator + struct object *object = ptr; + freeCacheEntry(object, page->cache_order, silence); +} diff --git a/lab4/src/shell.c b/lab4/src/shell.c new file mode 100644 index 000000000..0f1d63888 --- /dev/null +++ b/lab4/src/shell.c @@ -0,0 +1,87 @@ +#include "shell.h" + +#include "command.h" +#include "string.h" +#include "uart.h" + +static void welcome_msg() { + cmd_info(); + uart_puts( + "*******************************\n" + "*** YADOS 0.04 for OSC 2024 ***\n" + "*******************************\n" + "Hopefully this will be Yet Another Dope OS!"); + uart_putc(NEWLINE); + uart_putc(NEWLINE); + cmd_hello(); +} + +void run_shell() { + welcome_msg(); + while (1) { + char buffer[SHELL_BUF_SIZE]; + uart_putc(NEWLINE); + uart_puts("# "); + read_user_input(buffer); + exec_command(buffer); + } +} + +void read_user_input(char *buf) { + int idx = 0; + while (idx < SHELL_BUF_SIZE) { + char c = uart_getc(); + switch (c) { + case NEWLINE: + uart_putc(NEWLINE); + buf[idx] = '\0'; + return; + case DELETE: + if (idx > 0) { + idx--; + uart_putc(BACKSPACE); + uart_putc(' '); + uart_putc(BACKSPACE); + } + break; + case ESC: + uart_putc(NEWLINE); + uart_puts( + "ESC detected. Clearing buffer. Press Spacebar or Enter to exit."); + uart_putc(NEWLINE); + while (1) { + char c = uart_getc(); + if (c == ' ' || c == NEWLINE) break; + } + buf[0] = '\0'; + return; + default: + if (c >= 32 && c <= 126) { + uart_putc(c); + buf[idx++] = c; + } + } + } + uart_putc(NEWLINE); + uart_puts("Buffer overflow. Please re-enter your command."); + uart_putc(NEWLINE); + buf[0] = '\0'; +} + +int exec_command(const char *input) { + if (strlen(input) == 0) return 0; + struct command *cmd = commands; + while (1) { + if (!strcmp(cmd->name, END_OF_COMMAND_LIST)) { + uart_puts("Command not found."); + uart_putc(NEWLINE); + break; + } + if (!strcmp(cmd->name, input)) { + cmd->func(); + break; + } + cmd++; + } + return 0; +} \ No newline at end of file diff --git a/lab4/src/string.c b/lab4/src/string.c new file mode 100644 index 000000000..ffe02eb48 --- /dev/null +++ b/lab4/src/string.c @@ -0,0 +1,40 @@ +#include "string.h" + +int strcmp(const char *str1, const char *str2) { + while (*str1 && *str1 == *str2) str1++, str2++; + return *(unsigned char *)str1 - *(unsigned char *)str2; +} + +int memcmp(const void *str1, const void *str2, int n) { + const unsigned char *a = str1, *b = str2; + while (n-- > 0) { + if (*a != *b) return *a - *b; + a++; + b++; + } + return 0; +} + +char *strncpy(char *dest, const char *src, int n) { + while (n-- && (*dest++ = *src++)); + return dest; +} + +int strlen(const char *str) { + int len = 0; + while (*str++ != '\0') len++; + return len; +} + +char *strcat(char *dest, const char *src) { + char *d = dest; + + // Move the pointer to the end of the dest string + while (*d != '\0') d++; + // Copy the src string to the end of the dest string + while (*src != '\0') *d++ = *src++; + // Add the null terminator + *d = '\0'; + + return dest; +} \ No newline at end of file diff --git a/lab4/src/timer.c b/lab4/src/timer.c new file mode 100644 index 000000000..662559223 --- /dev/null +++ b/lab4/src/timer.c @@ -0,0 +1,99 @@ +#include "timer.h" + +#include "mem.h" +#include "string.h" +#include "uart.h" + +static timer_entry *head = 0; + +void enable_timer_interrupt() { + asm volatile( + // Enable Counter-timer interrupt + "mov x0, 1;" + "msr cntp_ctl_el0, x0;" // Physical Timer Control register + + // Set the Counter-timer frequency to 1 second + "mrs x0, cntfrq_el0;" // Frequency register + "msr cntp_tval_el0, x0;" // Physical Timer TimerValue register + + // Unmask Counter-timer interrupt + "mov x0, 2;" + "ldr x1, =%0;" + "str w0, [x1];" // IRQ Enable Register + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1" // registers modified + ); +} + +void disable_timer_interrupt() { + // Mask timer interrupt + asm volatile( + "mov x0, 0;" + "ldr x1, =%0;" + "str w0, [x1];" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void timer_irq_handler() { + // Set up 1 second core timer interrupt + asm volatile( + "mrs x0, cntfrq_el0;" // Frequency register + "msr cntp_tval_el0, x0;" // Physical Timer TimerValue register + ); + + // Check the timer queue + while (head != 0 && timer_get_uptime() >= head->time) { + head->func(head->arg); // Execute the callback function + kfree((void *)head, 0); // Free the memory allocated for the timer node + head = head->next; // Remove the head node after func finished + } + + enable_timer_interrupt(); +} + +uint64_t timer_get_uptime() { + uint64_t cntpct_el0 = 0, cntfrq_el0 = 0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + return cntpct_el0 / cntfrq_el0; +} + +void timer_add(void (*callback)(void *), void *arg, int duration) { + // Insert the new timer node into the linked list (sorted by time) + + timer_entry *timer = (timer_entry *)kmalloc(sizeof(timer_entry), 0); + timer->func = callback; + timer->arg = arg; + timer->time = timer_get_uptime() + duration; + timer->next = 0; + + if (head == 0 || timer->time < head->time) { + // Insert at the beginning of the list + timer->next = head; + head = timer; + return; + } + + timer_entry *current = head; + while (current->next != 0 && current->next->time <= timer->time) + current = current->next; + timer->next = current->next; + current->next = timer; +} + +static void show_timer_message(void *arg) { + uart_putc(NEWLINE); + uart_puts("[INFO] Timer message: "); + uart_puts((char *)arg); + uart_putc(NEWLINE); + kfree(arg, 0); // Free the memory allocated for the argument +} + +void set_timer(const char *message, int duration) { + timer_add((void (*)(void *))show_timer_message, (void *)message, duration); +} diff --git a/lab4/src/traps.c b/lab4/src/traps.c new file mode 100644 index 000000000..f4a97dc44 --- /dev/null +++ b/lab4/src/traps.c @@ -0,0 +1,34 @@ +#include + +#include "uart.h" + +void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr) { + // Print spsr_el1 + uart_puts("spsr_el1: "); + uart_hex(spsr); + uart_putc(NEWLINE); + + // Print elr_el1 + uart_puts(" elr_el1: "); + uart_hex(elr); + uart_putc(NEWLINE); + + // Print esr_el1 + uart_puts(" esr_el1: "); + uart_hex(esr); + uart_putc(NEWLINE); + uart_putc(NEWLINE); +} + +void exception_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + uart_puts("Exception Handler"); + uart_putc(NEWLINE); + print_registers(elr, esr, spsr); +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + uart_puts("[ERROR] The exception handler is not implemented"); + uart_putc(NEWLINE); + print_registers(elr, esr, spsr); + while (1); +} \ No newline at end of file diff --git a/lab4/src/uart.c b/lab4/src/uart.c new file mode 100644 index 000000000..3925f9f7b --- /dev/null +++ b/lab4/src/uart.c @@ -0,0 +1,171 @@ +#include "uart.h" + +#include "irq.h" +#include "mem.h" +#include "string.h" + +static int uart_read_idx = 0; +static int uart_write_idx = 0; +static char *uart_read_buffer; +static char *uart_write_buffer; + +void init_uart() { + uart_puts("[INFO] initializing uart_read_buffer."); + uart_putc(NEWLINE); + uart_read_buffer = (char *)kmalloc(UART_BUF_SIZE, 0); + + uart_puts("[INFO] initializing uart_write_buffer."); + uart_putc(NEWLINE); + uart_write_buffer = (char *)kmalloc(UART_BUF_SIZE, 0); + + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + // Disable GPIO pull up/down + *GPPUD = 0; + for (int i = 0; i < 150; i++); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + // Initialize mini UART + *AUX_ENABLE |= 1; // Enable mini UART + *AUX_MU_CNTL = 0; // Disable Tx and Rx during setup + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set data size to 8 bits + *AUX_MU_MCR = 0; // Disable auto flow control + *AUX_MU_BAUD = 270; // Set baud rate to 115200 + *AUX_MU_IIR = 6; // No FIFO + *AUX_MU_CNTL = 3; // Enable Tx and Rx + + // Enable AUX interrupts + *ENABLE_IRQS_1 |= 1 << 29; +} + +char uart_getc() { + // Check the data ready field on bit 0 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); // Read from AUX_MU_IO_REG + return c == CARRIAGE_RETURN ? NEWLINE : c; +} + +void uart_putc(char c) { + // Add CARRIAGE_RETURN before NEWLINE + if (c == NEWLINE) uart_putc(CARRIAGE_RETURN); + + // Check the transmitter empty field on bit 5 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = c; // Write to AUX_MU_IO_REG +} + +void uart_hex(unsigned int h) { + uart_puts("0x"); + unsigned int n; + for (int c = 28; c >= 0; c -= 4) { + n = (h >> c) & 0xF; + n += n > 9 ? 0x37 : '0'; + uart_putc(n); + } +} + +void uart_simple_hex(unsigned int h) { + uart_puts("0x"); + if (h == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + unsigned int n; + while (h > 0) { + n = h % 16; + buf[i++] = n + (n > 9 ? 0x37 : '0'); + h /= 16; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_dec(unsigned int d) { + if (d == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + while (d > 0) { + buf[i++] = d % 10 + '0'; + d /= 10; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_puts(const char *s) { + while (*s) { + uart_putc(*s++); + } +} + +void enable_uart_tx_interrupt() { *AUX_MU_IER |= 0x02; } + +void disable_uart_tx_interrupt() { *AUX_MU_IER &= 0x01; } + +void enable_uart_rx_interrupt() { *AUX_MU_IER |= 0x01; } + +void disable_uart_rx_interrupt() { *AUX_MU_IER &= 0x2; } + +void uart_tx_irq_handler() { + disable_uart_tx_interrupt(); + // Buffer is not empty -> send characters + if (uart_write_idx < UART_BUF_SIZE && + uart_write_buffer[uart_write_idx] != 0) { + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = uart_write_buffer[uart_write_idx++]; + enable_uart_tx_interrupt(); + } +} + +void uart_rx_irq_handler() { + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); + uart_read_buffer[uart_read_idx++] = (c == CARRIAGE_RETURN) ? NEWLINE : c; + if (uart_read_idx >= UART_BUF_SIZE) uart_read_idx = 0; + enable_uart_rx_interrupt(); +} + +void uart_async_read(char *buf, int len) { + disable_uart_rx_interrupt(); + for (int i = 0; i < uart_read_idx && i < len; i++) + buf[i] = uart_read_buffer[i]; + buf[uart_read_idx] = 0; + uart_read_idx = 0; +} + +void uart_async_write(const char *s) { + // Copy string to the write buffer + int len = 0; + while (*s != '\0') { + if (len >= UART_BUF_SIZE) { + uart_puts("[ERROR] Exceed the UART buffer size"); + uart_putc(NEWLINE); + return; + } + if (*s == NEWLINE) { + // Insert CARRIAGE_RETURN before NEWLINE + uart_write_buffer[len++] = CARRIAGE_RETURN; + uart_write_buffer[len++] = NEWLINE; + } else + uart_write_buffer[len++] = *s; + s++; + } + uart_write_buffer[len] = '\0'; + uart_write_idx = 0; // Reset the buffer index + enable_uart_tx_interrupt(); +} \ No newline at end of file diff --git a/lab4/src/utils.c b/lab4/src/utils.c new file mode 100644 index 000000000..f5226d183 --- /dev/null +++ b/lab4/src/utils.c @@ -0,0 +1,42 @@ +#include "utils.h" + +int align4(int n) { return n + (4 - n % 4) % 4; } + +int atoi(const char *s) { + int result = 0; + int sign = 1; + int i = 0; + + // Skip leading spaces + while (s[i] == ' ') { + i++; + } + + // Handle positive and negative sign + if (s[i] == '-') { + sign = -1; + i++; + } else if (s[i] == '+') { + i++; + } + + // Convert string to integer + while (s[i] >= '0' && s[i] <= '9') { + result = result * 10 + (s[i] - '0'); + i++; + } + + return sign * result; +} + +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; +} \ No newline at end of file diff --git a/lab4/start.S b/lab4/start.S new file mode 100644 index 000000000..c6b255ecf --- /dev/null +++ b/lab4/start.S @@ -0,0 +1,50 @@ +.section ".text.boot" + +.global _start + +_start: + /* get dtb address from x0 */ + ldr x1, =DTB_BASE // defined in devtree.c + str x0, [x1] + + /* get cpu id */ + mrs x1, mpidr_el1 /* Multiprocessor Affinity Register */ + and x1, x1, #3 + cbnz x1, halt // halt if cpu id != 0 + + /* set exception vector table */ + adr x0, exception_vector_table // defined in traps.S + msr vbar_el1, x0 + + /* switch from EL2 to EL1 */ + bl from_el2_to_el1 + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main + +halt: + wfe + b halt + +from_el2_to_el1: + mov x0, (1 << 31) + msr hcr_el2, x0 // EL1 uses aarch64 + mov x0, 0x3C5 // ... 0011 1100 0101 + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 diff --git a/lab4/traps.S b/lab4/traps.S new file mode 100644 index 000000000..80b4eafc4 --- /dev/null +++ b/lab4/traps.S @@ -0,0 +1,103 @@ +/* save general registers to stack */ +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp, 16 * 0] + stp x2, x3, [sp, 16 * 1] + stp x4, x5, [sp, 16 * 2] + stp x6, x7, [sp, 16 * 3] + stp x8, x9, [sp, 16 * 4] + stp x10, x11, [sp, 16 * 5] + stp x12, x13, [sp, 16 * 6] + stp x14, x15, [sp, 16 * 7] + stp x16, x17, [sp, 16 * 8] + stp x18, x19, [sp, 16 * 9] + stp x20, x21, [sp, 16 * 10] + stp x22, x23, [sp, 16 * 11] + stp x24, x25, [sp, 16 * 12] + stp x26, x27, [sp, 16 * 13] + stp x28, x29, [sp, 16 * 14] + str x30, [sp, 16 * 15] + mrs x28, spsr_el1 /* Saved Program Status Register (EL1) */ + mrs x29, elr_el1 /* Exception Link Register (EL1) */ + stp x28, x29, [sp, 16 * 16] +.endm + +/* load general registers from stack */ +.macro load_all + ldp x28, x29, [sp, 16 * 16] + msr spsr_el1, x28 /* Saved Program Status Register (EL1) */ + msr elr_el1, x29 /* Exception Link Register (EL1) */ + ldp x0, x1, [sp, 16 * 0] + ldp x2, x3, [sp, 16 * 1] + ldp x4, x5, [sp, 16 * 2] + ldp x6, x7, [sp, 16 * 3] + ldp x8, x9, [sp, 16 * 4] + ldp x10, x11, [sp, 16 * 5] + ldp x12, x13, [sp, 16 * 6] + ldp x14, x15, [sp, 16 * 7] + ldp x16, x17, [sp, 16 * 8] + ldp x18, x19, [sp, 16 * 9] + ldp x20, x21, [sp, 16 * 10] + ldp x22, x23, [sp, 16 * 11] + ldp x24, x25, [sp, 16 * 12] + ldp x26, x27, [sp, 16 * 13] + ldp x28, x29, [sp, 16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 9 +.endm + +exception_handler: + save_all + mrs x0, elr_el1 /* Exception Link Register (EL1) */ + mrs x1, esr_el1 /* ESR_EL1, Exception Syndrome Register (EL1) */ + mrs x2, spsr_el1 /* Saved Program Status Register (EL1) */ + bl exception_entry /* defined in traps.c */ + load_all + eret + +irq_handler: + save_all + bl irq_entry /* defined in irq.c */ + load_all + eret + +invalid_exc_handler: + save_all + mrs x0, elr_el1 /* Exception Link Register (EL1) */ + mrs x1, esr_el1 /* ESR_EL1, Exception Syndrome Register (EL1) */ + mrs x2, spsr_el1 /* Saved Program Status Register (EL1) */ + bl invalid_entry /* defined in traps.c */ + load_all + eret + +.macro v_entry 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 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL1 -> EL1 while using SP_EL1 + v_entry invalid_exc_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch64) -> EL1 + v_entry exception_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch32) -> EL1 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler diff --git a/lab5/.gitignore b/lab5/.gitignore new file mode 100644 index 000000000..eddcca901 --- /dev/null +++ b/lab5/.gitignore @@ -0,0 +1,6 @@ +.vscode +build +rootfs +temp +*.img +*.dtb \ No newline at end of file diff --git a/lab5/Makefile b/lab5/Makefile new file mode 100644 index 000000000..8821c90c1 --- /dev/null +++ b/lab5/Makefile @@ -0,0 +1,38 @@ +ARMGNU ?= aarch64-linux-gnu + +CFLAGS = -Iinclude -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +ASMFLAGS = -Iinclude +QEMUFLAGS = -M raspi3b -serial null -serial stdio +# QEMUFLAGS = -M raspi3b -display none -serial null -serial stdio + +SRC_DIR = src +BUILD_DIR = build + +all: clean kernel8.img + +clean: + rm -rf $(BUILD_DIR) *.img + +build: + mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/_%.o: $(SRC_DIR)/%.S | $(BUILD_DIR) + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/_%.o) + +kernel8.img: $(SRC_DIR)/linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/kernel8.elf kernel8.img + +qemu: all kernel8.img initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear & qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug: all kernel8.img initramfs.cpio bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -S -s -d int \ No newline at end of file diff --git a/lab5/bootloader/Makefile b/lab5/bootloader/Makefile new file mode 100644 index 000000000..d7882aa27 --- /dev/null +++ b/lab5/bootloader/Makefile @@ -0,0 +1,19 @@ +ARMGNU ?= aarch64-linux-gnu +CFLAGS = -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +QEMUFLAGS = -M raspi3b -display none -serial null -serial pty + +all: clean bootloader.img + +clean: + rm -rf build *.img + +bootloader.img: + mkdir -p build + $(ARMGNU)-gcc $(CFLAGS) -c main.c -o build/main.o + $(ARMGNU)-gcc $(CFLAGS) -c start.S -o build/start.o + $(ARMGNU)-gcc $(CFLAGS) -c boot.c -o build/boot.o + $(ARMGNU)-ld -T linker.ld -o build/bootloader.elf build/main.o build/start.o build/boot.o + $(ARMGNU)-objcopy -O binary build/bootloader.elf bootloader.img + +qemu: clean bootloader.img + qemu-system-aarch64 $(QEMUFLAGS) -kernel bootloader.img -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/lab5/bootloader/boot.c b/lab5/bootloader/boot.c new file mode 100644 index 000000000..d4ff535f5 --- /dev/null +++ b/lab5/bootloader/boot.c @@ -0,0 +1,61 @@ +#include "boot.h" + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + *GPPUD = 0; + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +char uart_recv() { + while (!(*AUX_MU_LSR & 0x01)) asm volatile("nop"); + return (char)(*AUX_MU_IO); +} + +void uart_putc(char c) { + if (c == '\n') uart_putc('\r'); + while (!(*AUX_MU_LSR & 0x20)) asm volatile("nop"); + *AUX_MU_IO = c; +} + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} + +int atoi(const char *s) { + int result = 0; + 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 * result; +} \ No newline at end of file diff --git a/lab5/bootloader/boot.h b/lab5/bootloader/boot.h new file mode 100644 index 000000000..84cece020 --- /dev/null +++ b/lab5/bootloader/boot.h @@ -0,0 +1,49 @@ +#ifndef BOOT_H +#define BOOT_H + +/* ==================== GPIO ==================== */ +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) + +/* ==================== UART ==================== */ +#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 init_uart(); +char uart_recv(); +void uart_putc(char c); +void uart_puts(const char *s); + +/* ==================== UTILS ==================== */ +int atoi(const char *s); + +#endif // BOOT_H \ No newline at end of file diff --git a/lab5/bootloader/linker.ld b/lab5/bootloader/linker.ld new file mode 100644 index 000000000..556816b11 --- /dev/null +++ b/lab5/bootloader/linker.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + . = 0x60000; + __loader_start = .; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } + __loader_end = .; +} +__bss_size = SIZEOF(.bss); +__loader_size = __loader_end - __loader_start; \ No newline at end of file diff --git a/lab5/bootloader/main.c b/lab5/bootloader/main.c new file mode 100644 index 000000000..cc8faa0fe --- /dev/null +++ b/lab5/bootloader/main.c @@ -0,0 +1,41 @@ +#include "boot.h" + +int main() { + init_uart(); + uart_puts("\033[2J\033[H"); + uart_puts( + "UART Bootloader\n" + "Waiting for kernel...\n"); + + // Get kernel image size + char buf[16] = {0}; + for (int i = 0; i < 16; i++) { + buf[i] = uart_recv(); + if (buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + + // Load kernel image + uart_puts("Kernel size: "); + uart_puts(buf); + uart_puts(" bytes.\n"); + uart_puts("Loading the kernel image...\n"); + + unsigned int size = atoi(buf); + char *kernel = (char *)0x80000; + while (size--) *kernel++ = uart_recv(); + + // Restore registers x0 x1 x2 x3 + asm volatile( + "mov x0, x10\n" + "mov x1, x11\n" + "mov x2, x12\n" + "mov x3, x13\n" + "mov x30, 0x80000\n" + "ret\n" // Jump to the new kernel + ); + + return 0; +} \ No newline at end of file diff --git a/lab5/bootloader/start.S b/lab5/bootloader/start.S new file mode 100644 index 000000000..ec8488dde --- /dev/null +++ b/lab5/bootloader/start.S @@ -0,0 +1,40 @@ +.section ".text.boot" + +.global _start + +_start: + /* save registers x0 x1 x2 x3 */ + mov x10, x0 /* dtb_base address */ + mov x11, x1 + mov x12, x2 + mov x12, x3 + + /* relocate bootloader */ + ldr x1, =0x80000 + ldr x2, =__loader_start // 0x60000 + ldr w3, =__loader_size + +relocate: + ldr x4, [x1], #8 + str x4, [x2], #8 + sub w3, w3, #1 + cbnz w3, relocate + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main-0x20000 + b run_main diff --git a/lab5/include/command.h b/lab5/include/command.h new file mode 100644 index 000000000..6fce9aa9a --- /dev/null +++ b/lab5/include/command.h @@ -0,0 +1,19 @@ +#pragma once + +#define MAX_BUF_SIZE 1024 +#define END_OF_COMMAND_LIST "NULL" + +struct command { + const char *name; + const char *help; + void (*func)(void); +}; + +extern struct command cmd_list[]; +extern unsigned int BOARD_REVISION; +extern unsigned int BASE_MEMORY; +extern unsigned int NUM_PAGES; + +// Commands +void cmd_info(); +void cmd_hello(); \ No newline at end of file diff --git a/lab5/include/devtree.h b/lab5/include/devtree.h new file mode 100644 index 000000000..a55abe78b --- /dev/null +++ b/lab5/include/devtree.h @@ -0,0 +1,26 @@ +#pragma once + +#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; +}; + +/** + * @brief Convert a 4-byte big-endian sequence to little-endian. + * + * @param s: big-endian sequence + * @return little-endian sequence + */ +uint32_t be2le(const void *s); + +void fdt_traverse(void (*callback)(void *, char *)); diff --git a/lab5/include/hardware.h b/lab5/include/hardware.h new file mode 100644 index 000000000..41c5c6053 --- /dev/null +++ b/lab5/include/hardware.h @@ -0,0 +1,219 @@ +#pragma once + +#define MMIO_BASE 0x3F000000 + +// ARM Interrupt Registers + +#define IRQ_BASIC_PENDING (volatile unsigned int *)(MMIO_BASE + 0x0000B200) +#define IRQ_PENDING_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B204) +#define IRQ_PENDING_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B208) +#define FIQ_CONTROL (volatile unsigned int *)(MMIO_BASE + 0x0000B20C) +#define ENABLE_IRQS_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B210) +#define ENABLE_IRQS_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B214) +#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) +#define CORE0_INTERRUPT_SOURCE (volatile unsigned int *)(0x40000060) + +// Timers interrupt control registers + +#define CORE0_TIMER_IRQCNTL 0x40000040 +#define CORE1_TIMER_IRQCNTL 0x40000044 +#define CORE2_TIMER_IRQCNTL 0x40000048 +#define CORE3_TIMER_IRQCNTL 0x4000004C + +// Where to route timer interrupt to, IRQ/FIQ +// Setting both the IRQ and FIQ bit gives an FIQ +#define TIMER0_IRQ 0x01 +#define TIMER1_IRQ 0x02 +#define TIMER2_IRQ 0x04 +#define TIMER3_IRQ 0x08 +#define TIMER0_FIQ 0x10 +#define TIMER1_FIQ 0x20 +#define TIMER2_FIQ 0x40 +#define TIMER3_FIQ 0x80 + +// PASSWORD + +#define PM_PASSWORD 0x5A000000 +#define PM_RSTC (volatile unsigned int *)0x3F10001C +#define PM_WDOG (volatile unsigned int *)0x3F100024 + +// DEVICETREE + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +// GPIO + +#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 GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) +// Skip GPRENn, GPFENn +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +// Skip GPLENn, GPARENn +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +// UART + +#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)) + +// MAILBOX + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 +#define MAILBOX_REQUEST 0x00000000 +#define MAILBOX_RESPONSE 0x80000000 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +// Channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +#define MAILBOX_BASE (MMIO_BASE + 0x0000B880) + +// mailbox register +#define MAILBOX_REG_READ ((volatile unsigned int *)(MAILBOX_BASE + 0x00000000)) +#define MAILBOX_REG_STATUS \ + ((volatile unsigned int *)(MAILBOX_BASE + 0x00000018)) +#define MAILBOX_REG_WRITE ((volatile unsigned int *)(MAILBOX_BASE + 0x00000020)) + +// mailbox channel +// https://github.com/raspberrypi/firmware/wiki/Mailboxes#channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +// mailbox status +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 + +// tags +#define TAGS_REQ_CODE 0x00000000 +#define TAGS_REQ_SUCCEED 0x80000000 +#define TAGS_REQ_FAILED 0x80000001 +#define TAGS_END 0x00000000 + +// hardware tags operator +#define TAGS_HARDWARE_BOARD_MODEL 0x00010001 +#define TAGS_HARDWARE_BOARD_REVISION 0x00010002 +#define TAGS_HARDWARE_MAC_ADDR 0x00010003 +#define TAGS_HARDWARE_BOARD_SERIAL 0x00010004 +#define TAGS_HARDWARE_ARM_MEM 0x00010005 +#define TAGS_HARDWARE_VC_MEM 0x00010006 +#define TAGS_HARDWARE_CLOCKS 0x00010007 + +// CLOCK ID + +#define CLOCK_ID_RESERVED 0x000000000 +#define CLOCK_ID_EMMC 0x000000001 +#define CLOCK_ID_UART 0x000000002 +#define CLOCK_ID_ARM 0x000000003 +#define CLOCK_ID_CORE 0x000000004 +#define CLOCK_ID_V3D 0x000000005 +#define CLOCK_ID_H264 0x000000006 +#define CLOCK_ID_ISP 0x000000007 +#define CLOCK_ID_SDRAM 0x000000008 +#define CLOCK_ID_PIXEL 0x000000009 +#define CLOCK_ID_PWM 0x00000000a +#define CLOCK_ID_HEVC 0x00000000b +#define CLOCK_ID_EMMC2 0x00000000c +#define CLOCK_ID_M2MC 0x00000000d +#define CLOCK_ID_PIXEL_BVB 0x00000000e + +// clock tags operator +#define TAGS_GET_CLOCK 0x00030002 +#define TAGS_SET_CLOCK 0x00038002 + +// framebuffer tages operator +#define FB_ALLOC_BUFFER 0x00040001 +#define FB_FREE_BUFFER 0x00048001 + +#define FB_BLANK_SCREEN 0x00040002 + +#define FB_PHY_WID_HEIGHT_GET 0x00040003 +#define FB_PHY_WID_HEIGHT_TEST 0x00044003 +#define FB_PHY_WID_HEIGHT_SET 0x00048003 + +#define FB_VIR_WID_HEIGHT_GET 0x00040004 +#define FB_VIR_WID_HEIGHT_TEST 0x00044004 +#define FB_VIR_WID_HEIGHT_SET 0x00048004 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_PIXEL_ORDER_GET 0x00040006 +#define FB_PIXEL_ORDER_TEST 0x00044006 +#define FB_PIXEL_ORDER_SET 0x00048006 + +#define FB_ALPHA_MODE_GET 0x00040007 +#define FB_ALPHA_MODE_TEST 0x00044007 +#define FB_ALPHA_MODE_SET 0x00048007 + +#define FB_PITCH_GET 0x00040008 + +#define FB_VIR_OFFSET_GET 0x00040009 +#define FB_VIR_OFFSET_TEST 0x00044009 +#define FB_VIR_OFFSET_SET 0x00048009 + +#define FB_OVERSCAN_GET 0x0004000A +#define FB_OVERSCAN_TEST 0x0004400A +#define FB_OVERSCAN_SET 0x0004800A + +#define FB_PALETTE_GET 0x0004000B +#define FB_PALETTE_TEST 0x0004400B +#define FB_PALETTE_SET 0x0004800B + +#define FB_CURSOR_INFO_SET 0x00008010 +#define FB_CURSOR_STATE_SET 0x00008011 diff --git a/lab5/include/initramfs.h b/lab5/include/initramfs.h new file mode 100644 index 000000000..4c82cb598 --- /dev/null +++ b/lab5/include/initramfs.h @@ -0,0 +1,35 @@ +#pragma once + +#include "traps.h" + +// Cpio Archive File Header (New ASCII Format) +typedef struct { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +} cpio_t; + +typedef struct { + int namesize; + int filesize; + int headsize; + int datasize; + char *pathname; +} ramfsRec; + +void initramfs_ls(); +void initramfs_cat(const char *target); +void initramfs_callback(void *addr, char *property); +void initramfs_run(const char *target); +void initramfs_sys_exec(const char *target, trap_frame *tf); diff --git a/lab5/include/irq.h b/lab5/include/irq.h new file mode 100644 index 000000000..cef36db3a --- /dev/null +++ b/lab5/include/irq.h @@ -0,0 +1,20 @@ +#pragma once + +#include "hardware.h" +#include "traps.h" + +#define ORDER_FIRST 0x0 +#define ORDER_REGULAR 0x8 +#define ORDER_LAST 0xF + +typedef struct irq_task_t { + void (*func)(); + int order; + int busy; // 0 (default) or 1 (busy) + struct irq_task_t *prev; + struct irq_task_t *next; +} irqTask; + +void enable_interrupt(); +void disable_interrupt(); +void irq_entry(trap_frame *tf); \ No newline at end of file diff --git a/lab5/include/mbox.h b/lab5/include/mbox.h new file mode 100644 index 000000000..090e7d72c --- /dev/null +++ b/lab5/include/mbox.h @@ -0,0 +1,11 @@ +#pragma once + +#include "hardware.h" + +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch, unsigned int *mbox); +// int mailbox_call(unsigned char c); +// int sys_mbox_call(unsigned char ch, unsigned int *_mbox); +int get_board_revision(unsigned int *mbox); +int get_arm_memory_status(unsigned int *mbox); diff --git a/lab5/include/mem.h b/lab5/include/mem.h new file mode 100644 index 000000000..f2f077f1c --- /dev/null +++ b/lab5/include/mem.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#define BUDDY_MAX_ORDER 16 +#define CACHE_MAX_ORDER 6 +#define PAGE_SIZE 0x1000 +#define MIN_CACHE_SIZE (PAGE_SIZE / (2 << CACHE_MAX_ORDER)) +#define STACK_SIZE 0x1000 + +struct page { + unsigned int order; + unsigned int used; + unsigned int cache_order; + struct page *prev; + struct page *next; +}; + +struct object { + unsigned int order; + struct object *next; +}; + +void init_mem(); +void reserveMemory(uint64_t start, uint64_t end); + +void pushPageToFreeList(struct page **list, struct page *page, + unsigned int order); +struct page *popFreeList(struct page **list_head); +void removePageFromFreeList(struct page **list, struct page *page); +void printFreeListByOrder(unsigned int order); + +struct page *lookupBuddy(struct page *page, unsigned int order); +struct page *allocatePagesByOrder(unsigned int order, int silent); +void freePages(struct page *page, unsigned int order, int silent); +void mergePages(struct page *page, unsigned int order, int echo); + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order); +struct object *popObjectFromList(struct object **list_head); +void *allocateCacheMemory(unsigned int index, int silent); +void freeCacheEntry(void *ptr, unsigned int index, int silent); + +void *kmalloc(unsigned int size, int silent); +void kfree(void *ptr, int silent); diff --git a/lab5/include/scheduler.h b/lab5/include/scheduler.h new file mode 100644 index 000000000..0ef41cb11 --- /dev/null +++ b/lab5/include/scheduler.h @@ -0,0 +1,59 @@ +#pragma once + +#include "signals.h" +#include "traps.h" + +struct context_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 thread_state { + RUNNING, + DEAD, +}; + +typedef struct thread_struct_t { + struct context_struct context; // callee-saved (don't move) + int pid; + enum thread_state state; + void *stack; + void *user_stack; + + // Signal handling + void (*sig_handlers[NSIG + 1])(); // Signal handlers + int sig_reg; // Pending signals + int sig_busy; // Handling a signal + trap_frame *sig_tf; // tf Saved before signal handling + void *sig_stack; // Stack for signal handling + + struct thread_struct_t *prev; + struct thread_struct_t *next; +} thread_struct; + +// Defined in scheduler.S +extern void switch_to(thread_struct *prev, thread_struct *next); +extern thread_struct *get_current(); // get tpidr_el1, current thread_struct + +thread_struct *get_thread_by_pid(int pid); +void sched_init(); + +thread_struct *kcreate_thread(void (*func)()); +void list_tcircle(); + +void schedule(); + +void kill_current_thread(); +void kill_thread_by_pid(int pid); +void idle(); \ No newline at end of file diff --git a/lab5/include/shell.h b/lab5/include/shell.h new file mode 100644 index 000000000..fccef918e --- /dev/null +++ b/lab5/include/shell.h @@ -0,0 +1,7 @@ +#pragma once + +#define SHELL_BUF_SIZE 1024 + +void run_shell(); +void read_user_input(char *buf); +int exec_command(const char *command); diff --git a/lab5/include/signals.h b/lab5/include/signals.h new file mode 100644 index 000000000..1a4088676 --- /dev/null +++ b/lab5/include/signals.h @@ -0,0 +1,11 @@ +#pragma once + +#include "traps.h" + +#define NSIG 10 // Number of signals + +extern void sigreturn(); // Defined in traps.S + +void signal(int signum, void (*handler)()); +void signal_kill(int pid, int sig); +void do_signal(trap_frame *regs); diff --git a/lab5/include/str.h b/lab5/include/str.h new file mode 100644 index 000000000..fe18f924d --- /dev/null +++ b/lab5/include/str.h @@ -0,0 +1,8 @@ +#pragma once + +int strcmp(const char *str1, const char *str2); +int memcmp(const void *str1, const void *str2, int n); +char *strncpy(char *dest, const char *src, int n); +int strlen(const char *str); +char *strcat(char *dest, const char *src); +void *memcpy(void *dest, const void *src, int n); \ No newline at end of file diff --git a/lab5/include/syscalls.h b/lab5/include/syscalls.h new file mode 100644 index 000000000..ed048898f --- /dev/null +++ b/lab5/include/syscalls.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "traps.h" + +extern void child_ret_from_fork(); // traps.S + +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, trap_frame *tf); +int sys_fork(trap_frame *tf); +void sys_exit(int status); +// 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); \ No newline at end of file diff --git a/lab5/include/test.h b/lab5/include/test.h new file mode 100644 index 000000000..37346deb5 --- /dev/null +++ b/lab5/include/test.h @@ -0,0 +1,4 @@ +#pragma once + +void thread_test(); +void run_fork_test(); \ No newline at end of file diff --git a/lab5/include/timer.h b/lab5/include/timer.h new file mode 100644 index 000000000..1dd4d2287 --- /dev/null +++ b/lab5/include/timer.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef struct timerEntry_t { + void (*func)(void *); + void *arg; + int time; + struct timerEntry_t *next; +} timerEntry; + +void init_timer(); + +void enable_timer_interrupt(); +void disable_timer_interrupt(); + +void timer_irq_handler(); +uint64_t timer_get_uptime(); + +void timer_add(void (*callback)(void *), void *arg, int duration); +void set_timer(const char *message, int duration); +void set_timeup(int *timeup); diff --git a/lab5/include/traps.h b/lab5/include/traps.h new file mode 100644 index 000000000..bf940f5d7 --- /dev/null +++ b/lab5/include/traps.h @@ -0,0 +1,38 @@ +#pragma once + +typedef struct { // order is important + 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; // system call number + 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; // link register + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trap_frame; diff --git a/lab5/include/uart.h b/lab5/include/uart.h new file mode 100644 index 000000000..6d2b99c09 --- /dev/null +++ b/lab5/include/uart.h @@ -0,0 +1,40 @@ +#pragma once + +#include "hardware.h" + +#define UART_BUF_SIZE 1024 + +#define BACKSPACE '\b' +#define DELETE 127 +#define ESC 27 +#define NEWLINE '\n' +#define TAB '\t' +#define CARRIAGE_RETURN '\r' + +#define INFO 0 +#define WARN 1 +#define TEST 2 +#define BUDD 10 +#define CACH 11 +#define ERR 255 + +void init_uart(); +char uart_getc(); +void uart_putc(char c); +void uart_puts(const char *s); +void uart_clear(); +void uart_hex(unsigned int h); +void uart_simple_hex(unsigned int h); +void uart_dec(unsigned int h); +void uart_log(int type, const char *msg); + +void enable_uart_tx_interrupt(); +void disable_uart_tx_interrupt(); +void enable_uart_rx_interrupt(); +void disable_uart_rx_interrupt(); + +void uart_tx_irq_handler(); +void uart_rx_irq_handler(); + +void uart_async_read(char *buf, int len); +void uart_async_write(const char *s); \ No newline at end of file diff --git a/lab5/include/utils.h b/lab5/include/utils.h new file mode 100644 index 000000000..c2ebcb54b --- /dev/null +++ b/lab5/include/utils.h @@ -0,0 +1,24 @@ +#pragma once + +/** + * @brief Align `n` to be a multiple of 4. + * + * @param n A number + * @return Algined number + */ +int align4(int n); + +int atoi(const char *s); + +/** + * @brief Convert hexadecimal string to int. + * + * @param s: hexadecimal string + * @param n: string length + * @return Converted int number + */ +int hextoi(char *s, int n); + +int memcmp(const void *str1, const void *str2, int n); +void *memcpy(void *dest, const void *src, int n); +void *memset(void *s, int c, int n); \ No newline at end of file diff --git a/lab5/initramfs.cpio b/lab5/initramfs.cpio new file mode 100644 index 000000000..586b0671c Binary files /dev/null and b/lab5/initramfs.cpio differ diff --git a/lab5/sender.sh b/lab5/sender.sh new file mode 100755 index 000000000..501756d76 --- /dev/null +++ b/lab5/sender.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This script sends the kernel image to Rpi3 through UART + +DEST_PATH="/dev/ttyUSB0" +KERNEL_PATH="./kernel8.img" + +# Check the root permission +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +if [ $1 ] +then + DEST_PATH="$1" +fi + +# Get the size of the kernel image file and send it to Rpi3 +# wc -c: count bytes of a file +# sleep: wait n seconds +wc -c < $KERNEL_PATH > $DEST_PATH | sleep 1 + +# Send the kernel image +# pv: redirect file input to specified tty +# add --rate-limit option to limit the speed +pv $KERNEL_PATH > $DEST_PATH diff --git a/lab5/src/command.c b/lab5/src/command.c new file mode 100644 index 000000000..421100c36 --- /dev/null +++ b/lab5/src/command.c @@ -0,0 +1,234 @@ +#include "command.h" + +#include "initramfs.h" +#include "mem.h" +#include "scheduler.h" +#include "shell.h" +#include "str.h" +#include "syscalls.h" +#include "test.h" +#include "timer.h" +#include "uart.h" +#include "utils.h" + +typedef struct { + void *ptr; + void *next; +} demo_mem_rec; + +static demo_mem_rec *dmr_list = 0; + +void cmd_info() { + uart_log(INFO, "board revision: "); + uart_hex(BOARD_REVISION); + uart_putc(NEWLINE); + uart_log(INFO, "device base memory address: "); + uart_hex(BASE_MEMORY); + uart_putc(NEWLINE); + uart_log(INFO, "device memory size: "); + uart_hex(NUM_PAGES * PAGE_SIZE); + uart_putc(NEWLINE); +} + +void cmd_hello() { + uart_puts("Hello there!"); + uart_putc(NEWLINE); +} + +static void cmd_mem() { + for (int i = BUDDY_MAX_ORDER; i >= 0; i--) { + printFreeListByOrder(i); + } +} + +static void cmd_bd() { + uart_puts("Buddy System: request order (0-"); + uart_dec(BUDDY_MAX_ORDER); + uart_puts("): "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int order = atoi(buf); + if (buf[0] < '0' || buf[0] > '9' || order < 0 || order > BUDDY_MAX_ORDER) { + uart_puts("Invalid order."); + uart_putc(NEWLINE); + } else { + demo_mem_rec *dmr = kmalloc(sizeof(demo_mem_rec), 1); + dmr->ptr = kmalloc(PAGE_SIZE << order, 0); + dmr->next = dmr_list; + dmr_list = dmr; + } + kfree(buf, 1); +} + +static void cmd_fm() { + if (dmr_list == 0) { + uart_puts("No memory block allocated."); + uart_putc(NEWLINE); + } else { + uart_puts("Freeing all allocated memory blocks..."); + uart_putc(NEWLINE); + do { + demo_mem_rec *dmr = dmr_list; + kfree(dmr->ptr, 0); + dmr_list = dmr->next; + kfree(dmr, 1); + } while (dmr_list != 0); + } +} + +static void cmd_ca() { + uart_log(INFO, "Dynamic Memory Allocator: request byte(s) (1-"); + uart_dec(PAGE_SIZE / 2); + uart_puts("): "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int size = atoi(buf); + if (buf[0] == '\0' || size <= 0) { + uart_log(WARN, "Invalid size.\n"); + } else { + if (size > PAGE_SIZE / 2) { + uart_log( + INFO, + "Size too large for dynamic allocator. Switching to Buddy System.\n"); + } + demo_mem_rec *dmr = kmalloc(sizeof(demo_mem_rec), 1); + dmr->ptr = kmalloc(size, 0); + dmr->next = dmr_list; + dmr_list = dmr; + } + kfree(buf, 1); +} + +static void cmd_lab() { + uart_puts( + "(1) Lab 3: UART async write\n" + "(2) Lab 3: UART async read\n" + "(3) Lab 5: Thread creation test\n" + "(4) Lab 5: Fork test\n" + ": "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + switch (atoi(buf)) { + case 1: + uart_puts(": "); + strncpy(buf, "[INFO] Async write: ", strlen("[INFO] Async write: ")); + read_user_input(buf + strlen("[INFO] Async write: ")); + // read_user_input(buf); + uart_async_write(buf); + break; + case 2: + uart_puts("(Please type something in 3 sec.)"); + uart_putc(NEWLINE); + // set a timer to 3 sec. + int *timeup = 0; + timer_add((void (*)(void *))set_timeup, (void *)timeup, 3); + enable_uart_rx_interrupt(); + while (!*timeup); + // time's up, get the buf content and print + uart_async_read(buf, SHELL_BUF_SIZE); + uart_puts("[INFO] Async read received: "); + uart_puts(buf); + uart_putc(NEWLINE); + break; + case 3: + for (int i = 0; i < 3; i++) kcreate_thread(thread_test); + idle(); + break; + case 4: + kcreate_thread(run_fork_test); + idle(); + break; + default: + uart_puts("Option not found."); + uart_putc(NEWLINE); + } + kfree(buf, 1); +} + +static void cmd_timer() { + uart_puts("Duration(sec.): "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int sec = atoi(buf); + + char *msg = kmalloc(SHELL_BUF_SIZE, 0); + uart_puts(": "); + read_user_input(msg); + set_timer(msg, sec); + kfree(buf, 1); +} + +static void cmd_run() { + // Get filename from user input + uart_puts(": "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + initramfs_run(buf); + kfree(buf, 1); +} + +static void cmd_reboot() { + uart_puts("Start Rebooting..."); + uart_putc(NEWLINE); + // Reboot after 0x20000 ticks + *PM_RSTC = PM_PASSWORD | 0x20; // Full reset + *PM_WDOG = PM_PASSWORD | 0x20000; // Number of watchdog ticks +} + +static void cmd_cancel() { + uart_puts("Rebooting Attempt Aborted, if any."); + uart_putc(NEWLINE); + *PM_RSTC = PM_PASSWORD | 0; + *PM_WDOG = PM_PASSWORD | 0; +} + +static void cmd_cat() { + // Get filename from user input + uart_puts(": "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + initramfs_cat(buf); + kfree(buf, 1); +} + +static void cmd_clear() { uart_clear(); } + +static void cmd_help() { + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); + struct command *cmd = cmd_list; + while (1) { + if (!strcmp(cmd->name, END_OF_COMMAND_LIST)) { + break; + } + uart_puts(cmd->name); + uart_putc(TAB); + uart_puts(": "); + uart_puts(cmd->help); + uart_putc(NEWLINE); + cmd++; + } + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); +} + +struct command cmd_list[] = { + {.name = "help", .help = "Display this help menu", .func = cmd_help}, + {.name = "hello", .help = "Print 'Hello there!'", .func = cmd_hello}, + {.name = "clear", .help = "Clear the screen", .func = cmd_clear}, + {.name = "reboot", .help = "Reboot the device", .func = cmd_reboot}, + {.name = "cancel", .help = "Cancel a scheduled reboot", .func = cmd_cancel}, + {.name = "info", .help = "Show hardware information", .func = cmd_info}, + {.name = "ls", .help = "List files in ramdisk", .func = initramfs_ls}, + {.name = "cat", .help = "Display content of ramdisk file", .func = cmd_cat}, + {.name = "run", .help = "Run a specified program", .func = cmd_run}, + {.name = "timer", .help = "Set timer with duration", .func = cmd_timer}, + // lab4 + {.name = "mem", .help = "Display free memory blocks", .func = cmd_mem}, + {.name = "bd", .help = "Allocate a memory block", .func = cmd_bd}, + {.name = "ca", .help = "Use the dynamic memory allocator", .func = cmd_ca}, + // lab5 + {.name = "fm", .help = "Free all memory in the demo list", .func = cmd_fm}, + {.name = "pid", .help = "List all running threads", .func = list_tcircle}, + {.name = "lab", .help = "Showcase lab requirements", .func = cmd_lab}, + {.name = END_OF_COMMAND_LIST}}; diff --git a/lab5/src/devtree.c b/lab5/src/devtree.c new file mode 100644 index 000000000..3cefae084 --- /dev/null +++ b/lab5/src/devtree.c @@ -0,0 +1,60 @@ +#include "devtree.h" + +#include "str.h" +#include "uart.h" +#include "utils.h" + +// Assign a non-zero value to be stored in the .data section +void *DTB_BASE = (void *)0xF; +void *DTB_END = (void *)0xF; + +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]; +} + +void fdt_traverse(void (*callback)(void *, char *)) { + struct fdt_header *header = (struct fdt_header *)(uintptr_t)DTB_BASE; + + // Check the magic number + if (be2le(&(header->magic)) != 0xD00DFEED) { + uart_log(WARN, "Dtb header magic does not match!\n"); + } + DTB_END = DTB_BASE + be2le(&header->totalsize); + + uart_log(INFO, "Dtb loaded at "); + uart_hex((uintptr_t)DTB_BASE); + uart_putc('-'); + uart_hex((uintptr_t)DTB_END); + uart_putc(NEWLINE); + + uintptr_t structure = (uintptr_t)header + be2le(&header->off_dt_struct); + uintptr_t strings = (uintptr_t)header + be2le(&header->off_dt_strings); + uint32_t structure_size = be2le(&header->size_dt_struct); + + // Parse the structure block + uintptr_t ptr = structure; // Point to the beginning of structure block + while (ptr < structure + structure_size) { + uint32_t token = be2le((char *)ptr); + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + ptr += align4(strlen((char *)ptr) + 1); + break; + case FDT_PROP: + uint32_t len = be2le((char *)ptr); + ptr += 4; + uint32_t nameoff = be2le((char *)ptr); + ptr += 4; + callback((void *)(uintptr_t)be2le((void *)ptr), + (char *)(strings + nameoff)); + ptr += align4(len); + break; + case FDT_END_NODE: + case FDT_NOP: + case FDT_END: + } + } +} \ No newline at end of file diff --git a/lab5/src/initramfs.c b/lab5/src/initramfs.c new file mode 100644 index 000000000..d753c16b6 --- /dev/null +++ b/lab5/src/initramfs.c @@ -0,0 +1,158 @@ +#include "initramfs.h" + +#include + +#include "mem.h" +#include "scheduler.h" +#include "str.h" +#include "uart.h" +#include "utils.h" + +void *initrd_start; +void *initrd_end; + +void initramfs_callback(void *addr, char *property) { + if (!strcmp(property, "linux,initrd-start")) { + uart_log(INFO, "linux,initrd-start: "); + uart_hex((uintptr_t)addr); + initrd_start = (char *)addr; + uart_putc(NEWLINE); + } else if (!strcmp(property, "linux,initrd-end")) { + uart_log(INFO, "linux,initrd-end: "); + uart_hex((uintptr_t)addr); + initrd_end = (char *)addr; + uart_putc(NEWLINE); + } +} + +static ramfsRec *ramfsNext(char *fptr) { + ramfsRec *rec = kmalloc(sizeof(ramfsRec), 1); + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + rec->namesize = hextoi(header->c_namesize, 8); + rec->filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + rec->headsize = align4(sizeof(cpio_t) + rec->namesize); + rec->datasize = align4(rec->filesize); + + // Get file pathname + rec->pathname = kmalloc(rec->namesize, 1); + strncpy(rec->pathname, fptr + sizeof(cpio_t), rec->namesize); + + return rec; +} + +void initramfs_ls() { + uart_putc(NEWLINE); + + char *fptr = (char *)initrd_start; + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + uart_puts(rec->pathname); + for (int i = 0; i < 15 - strlen(rec->pathname); i++) { + uart_putc(' '); + } + uart_dec(rec->filesize); + uart_puts(" byte"); + if (rec->filesize > 1) uart_putc('s'); + uart_putc(NEWLINE); + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } +} + +void initramfs_cat(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(filename, rec->pathname)) { + // Dump its content + uart_putc(NEWLINE); + for (char *c = fptr + rec->headsize; + c < fptr + rec->headsize + rec->filesize; c++) { + uart_putc(*c); + } + uart_putc(NEWLINE); + uart_putc(NEWLINE); + kfree(rec->pathname, 1); + kfree(rec, 1); + return; + } + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +void initramfs_run(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(filename, rec->pathname)) { + // Load the user program + void *program = kmalloc(rec->filesize, 0); + memcpy(program, fptr + rec->headsize, rec->filesize); + + thread_struct *thread = kcreate_thread((void *)program); + thread->user_stack = kmalloc(STACK_SIZE, 0); + asm volatile( + "msr tpidr_el1, %0\n" + "msr spsr_el1, %1\n" + "msr elr_el1, %2\n" + "msr sp_el0, %3\n" + "mov sp, %4\n" + "eret\n" + : + : "r"(thread), // 0 thread_struct + "r"(0x340), // 1 + "r"(thread->context.lr), // 2 link register + "r"(thread->user_stack + STACK_SIZE), // 3 + "r"(thread->context.sp) // 4 + :); + return; + } + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +// using current thread to run the loaded program +void initramfs_sys_exec(const char *target, trap_frame *tf) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(target, rec->pathname)) { + void *program = kmalloc(rec->filesize, 0); + memcpy(program, fptr + rec->headsize, rec->filesize); + thread_struct *thread = get_current(); + while (thread->sig_busy); // wait for signal handling + thread->sig_reg = 0; + memset(thread->sig_handlers, 0, sizeof(thread->sig_handlers)); + thread->context.lr = (unsigned long)program; // change to elr + thread->context.sp = (uintptr_t)thread->stack + STACK_SIZE; + thread->context.fp = (uintptr_t)thread->stack + STACK_SIZE; + tf->elr_el1 = (unsigned long)program; + if (thread->user_stack == 0) thread->user_stack = kmalloc(STACK_SIZE, 0); + tf->sp_el0 = (uintptr_t)thread->user_stack + STACK_SIZE; + return; + } + fptr += rec->headsize + rec->datasize; + } + uart_puts("File not found.\n"); +} \ No newline at end of file diff --git a/lab5/src/irq.c b/lab5/src/irq.c new file mode 100644 index 000000000..01f50c775 --- /dev/null +++ b/lab5/src/irq.c @@ -0,0 +1,96 @@ +#include "irq.h" + +#include "mem.h" +#include "scheduler.h" +#include "timer.h" +#include "uart.h" + +static irqTask *head = 0; + +// Disable interrupt before calling irq_add_task() +void irq_add_task(void (*callback)(), int order) { + irqTask *task = (irqTask *)kmalloc(sizeof(irqTask), 1); + task->func = callback; + task->order = order; + task->busy = 0; + task->prev = 0; + task->next = 0; + + // 0 -> task -> head -> ... + if (head == 0 || task->order < head->order) { + task->next = head; + task->prev = 0; + if (head != 0) head->prev = task; + head = task; + return; + } + + irqTask *current = head; + while (current->next != 0 && current->next->order <= task->order) + current = current->next; + task->next = current->next; + if (current->next != 0) current->next->prev = task; + current->next = task; + task->prev = current; +} + +// to enable interrupts in EL1 +void enable_interrupt() { + asm volatile( + "msr DAIFClr, 0xF\n" // Clear the D, A, I, F bits in the DAIF register + ); +} + +// to disable interrupts in EL1 +void disable_interrupt() { + asm volatile( + "msr DAIFSet, 0xF\n" // Set the D, A, I, F bits to 1 in the DAIF register + ); +} + +void irq_entry(trap_frame *tf) { + disable_interrupt(); // Enter the critical section + + if (*IRQ_PENDING_1 & (1 << 29)) { // UART interrupt + switch (*AUX_MU_IIR & 0x6) { // 0x6 = 0110 -> Get 0x2 and 0x4 + + case 0x2: // 0x2 UART Transmit interrupt + disable_uart_tx_interrupt(); + irq_add_task(uart_tx_irq_handler, ORDER_LAST); + break; + + case 0x4: // 0x4 UART Receive interrupt + disable_uart_rx_interrupt(); + irq_add_task(uart_rx_irq_handler, ORDER_FIRST); + break; + } + } else if (*CORE0_INTERRUPT_SOURCE & 0x2) { + // Core 0 timer interrupt for schedule() + // tell if thread_struct is not the only one in the run_queue + if (get_current() != get_current()->next) schedule(); + + disable_timer_interrupt(); + irq_add_task(timer_irq_handler, ORDER_REGULAR); + } + + enable_interrupt(); // Leave the critical section + + // Preemption: run the task with the highest priority + while (head != 0 && !head->busy) { + disable_interrupt(); + irqTask *task = head; // Get a task from head + task->busy = 1; // Flag the task as under processing + enable_interrupt(); + + task->func(); // Run the tasks with interrupts enabled + + // Remove the task + disable_interrupt(); + if (task->prev != 0) task->prev->next = task->next; + if (task->next != 0) task->next->prev = task->prev; + if (task == head) head = task->next; + kfree((void *)task, 1); + enable_interrupt(); + } + do_signal(tf); +} \ No newline at end of file diff --git a/lab5/src/linker.ld b/lab5/src/linker.ld new file mode 100644 index 000000000..1f2d111af --- /dev/null +++ b/lab5/src/linker.ld @@ -0,0 +1,13 @@ +SECTIONS +{ + . = 0x80000; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } +} +__bss_size = SIZEOF(.bss); \ No newline at end of file diff --git a/lab5/src/main.c b/lab5/src/main.c new file mode 100644 index 000000000..3fb01ac2f --- /dev/null +++ b/lab5/src/main.c @@ -0,0 +1,26 @@ +#include "devtree.h" +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "shell.h" +#include "timer.h" +#include "uart.h" + +int main() { + /* Initialization */ + init_uart(); + fdt_traverse(initramfs_callback); + init_mem(); + + uart_log(INFO, "enable_interrupt()\n"); + enable_interrupt(); + uart_log(INFO, "init_timer()\n"); + init_timer(); + uart_log(INFO, "sched_init()\n"); + sched_init(); + + run_shell(); + + return 0; +} \ No newline at end of file diff --git a/lab5/src/mbox.c b/lab5/src/mbox.c new file mode 100644 index 000000000..281d4cd28 --- /dev/null +++ b/lab5/src/mbox.c @@ -0,0 +1,39 @@ +#include "mbox.h" + +// volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +int mbox_call(unsigned char ch, unsigned int *mbox) { + unsigned int r = (unsigned int)((unsigned long)mbox & ~0xF) | (ch & 0xF); + // Wait until we can write to the mailbox + while (*MAILBOX_REG_STATUS & MAILBOX_FULL); + *MAILBOX_REG_WRITE = r; // Write the request + while (1) { + // Wait for the response + while (*MAILBOX_REG_STATUS & MAILBOX_EMPTY); + if (r == *MAILBOX_REG_READ) return mbox[1] == MAILBOX_RESPONSE; + } + return 0; +} + +int get_board_revision(unsigned int *mbox) { + mbox[0] = 7 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_BOARD_REVISION; + mbox[3] = 4; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = END_TAG; + return mbox_call(MAILBOX_CH_PROP, mbox); +} + +int get_arm_memory_status(unsigned int *mbox) { + mbox[0] = 8 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_ARM_MEM; + mbox[3] = 8; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = 0; + mbox[7] = END_TAG; + return mbox_call(MAILBOX_CH_PROP, mbox); +} \ No newline at end of file diff --git a/lab5/src/mem.c b/lab5/src/mem.c new file mode 100644 index 000000000..65f0bd82e --- /dev/null +++ b/lab5/src/mem.c @@ -0,0 +1,374 @@ +#include "mem.h" + +#include "command.h" +#include "devtree.h" +#include "mbox.h" +#include "str.h" +#include "uart.h" +#include "utils.h" + +extern char *__bss_end; +extern void *DTB_BASE; +extern void *DTB_END; +extern void *initrd_start; +extern void *initrd_end; + +unsigned int BOARD_REVISION; +unsigned int BASE_MEMORY; +unsigned int NUM_PAGES; + +static struct page *pageTable; +static struct page *freeList[BUDDY_MAX_ORDER + 1]; +static struct object *objectCache[CACHE_MAX_ORDER + 1]; + +extern char *__bss_end; // `__bss_end` is defined in linker script + +static char *heap_top; + +static void *simple_malloc(int size) { + void *p = (void *)heap_top; + if (size < 0) return 0; + heap_top += size; + return p; +} + +void init_mem() { + // Simple malloc init + // Set heap base address + heap_top = (char *)&__bss_end; + uart_log(INFO, "Simple initialized.\n"); + + unsigned int __attribute__((aligned(16))) mbox[36]; + + // Get board revision + if (BOARD_REVISION == 0) { + get_board_revision(mbox); + BOARD_REVISION = mbox[5]; + } + // Get ARM memory base address and size + if (NUM_PAGES == 0) { + get_arm_memory_status(mbox); + BASE_MEMORY = mbox[5]; + NUM_PAGES = (mbox[6] - BASE_MEMORY) / PAGE_SIZE; + } + + cmd_info(); + + // Initialize the buddy allocator + pageTable = simple_malloc(sizeof(struct page) * NUM_PAGES); + unsigned int current_order = BUDDY_MAX_ORDER; + for (int i = 0; i < NUM_PAGES; i++) { + pageTable[i].order = 0; + pageTable[i].used = 0; + pageTable[i].cache_order = -1; + pageTable[i].prev = 0; + pageTable[i].next = 0; + if (i % (1 << current_order) == 0) { + while (current_order > 0 && NUM_PAGES - i < (1 << current_order)) { + current_order--; + } + pushPageToFreeList(&freeList[current_order], &pageTable[i], + current_order); + } + } + unsigned int page_table = NUM_PAGES * sizeof(struct page) * 2; + + // Reserve memory: + // Spin tables for multicore boot + reserveMemory(0x0, 0x1000); + // Kernel & stack; Simple allocator + reserveMemory(0x80000 - STACK_SIZE, (unsigned long)&__bss_end + page_table); + // Initramfs + reserveMemory((uint64_t)initrd_start, (uint64_t)initrd_end); + // Devicetree + reserveMemory((uint64_t)DTB_BASE, (uint64_t)DTB_END); +} + +void reserveMemory(uint64_t resv_start, uint64_t resv_end) { + // Round the start and end addresses to the page boundary + resv_start = resv_start & ~(PAGE_SIZE - 1); + resv_end = (resv_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + resv_end--; + + uart_log(INFO, "Reserving memory: "); + uart_hex(resv_start); + uart_putc('-'); + uart_hex(resv_end); + uart_putc(NEWLINE); + + for (int order = BUDDY_MAX_ORDER; order >= 0; order--) { + struct page *current = freeList[order]; + while (current != 0) { + struct page *next = current->next; + uint64_t page_start = (current - pageTable) * PAGE_SIZE; + uint64_t page_end = page_start + (PAGE_SIZE << order) - 1; + if (page_start >= resv_start && page_end <= resv_end) { + // [page page] + // Remove the page from the free list + current->used = 1; + removePageFromFreeList(&freeList[order], current); + } else if (resv_start > page_end || resv_end < page_start) { + // ---resv> [page or page] or order = order; + page->used = 0; + page->prev = 0; + page->next = 0; + + if (*list_head == 0 || (*list_head) < page) { + if (*list_head != 0) (*list_head)->prev = page; + page->next = *list_head; + *list_head = page; + return; + } + + struct page *current = *list_head; + while (current->next != 0 && page < current->next) { + current = current->next; + } + page->prev = current; + page->next = current->next; + if (current->next != 0) current->next->prev = page; + current->next = page; +} + +struct page *popFreeList(struct page **list_head) { + if (*list_head == 0) return 0; + + struct page *page = *list_head; + *list_head = page->next; + page->used = 1; + return page; +} + +void removePageFromFreeList(struct page **list_head, struct page *page) { + if (page->prev != 0) page->prev->next = page->next; + if (page->next != 0) page->next->prev = page->prev; + if (page == *list_head) *list_head = page->next; +} + +void printFreeListByOrder(unsigned int order) { + struct page *page = freeList[order]; + if (page > 0) uart_log(BUDD, ""); + while (page != 0) { + uart_putc(TAB); + uart_hex((unsigned int)(page - pageTable) * PAGE_SIZE); + uart_puts("-"); + uart_hex((unsigned int)(page - pageTable + (1 << order)) * PAGE_SIZE - 1); + uart_puts(" ["); + if (order < 10) uart_putc(' '); + uart_dec(order); + uart_puts("]\n"); + page = page->next; + } +} + +struct page *lookupBuddy(struct page *page, unsigned int order) { + unsigned int buddy_pfn = (unsigned int)(page - pageTable) ^ (1 << order); + return &pageTable[buddy_pfn]; +} + +struct page *allocatePagesByOrder(unsigned int order, int silent) { + if (!silent) { + uart_log(BUDD, "memory block requested: "); + uart_dec(1 << order); + uart_puts(" page(s).\n"); + } + + for (int i = order; i <= BUDDY_MAX_ORDER; i++) { + if (freeList[i] == 0) // No free page available + continue; // Try next order + struct page *page = popFreeList(&freeList[i]); + page->order = order; // Update order of the page + + while (i > order) { // requires splitting + i--; + struct page *buddy = lookupBuddy(page, i); + pushPageToFreeList(&freeList[i], buddy, i); + + if (silent) continue; + // Print information + unsigned int pfn = page - pageTable; + unsigned int buddy_pfn = buddy - pageTable; + uart_log(BUDD, "Split "); + uart_hex(pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((pfn + (1 << i)) * PAGE_SIZE - 1); + uart_puts("//"); + uart_hex(buddy_pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((buddy_pfn + (1 << i)) * PAGE_SIZE - 1); + uart_puts(" => ["); + uart_dec(i); + uart_puts("]\n"); + } + if (!silent) { + uart_log(BUDD, "Memory allocated: "); + uart_hex((unsigned int)(page - pageTable) * PAGE_SIZE); + uart_puts("-"); + uart_hex((unsigned int)(page - pageTable + (1 << order)) * PAGE_SIZE - 1); + uart_putc(NEWLINE); + } + + return page; + } + return 0; +} + +void freePages(struct page *page, unsigned int order, int silence) { + if (!silence) { + uart_log(BUDD, "Free "); + uart_dec(1 << order); + uart_puts("-page memory block starting from "); + uart_hex((unsigned int)(page - pageTable) * PAGE_SIZE); + uart_putc(NEWLINE); + } + mergePages(page, order, silence); +} + +void mergePages(struct page *page, unsigned int order, int silence) { + struct page *current = page; + while (order < BUDDY_MAX_ORDER) { + struct page *buddy = lookupBuddy(current, order); + if (buddy->order != order || buddy->used == 1) break; + + removePageFromFreeList(&freeList[order], buddy); + + if (current > buddy) { + struct page *tmp = current; + current = buddy; + buddy = tmp; + } + + order++; + if (silence) continue; + unsigned int pfn = current - pageTable; + unsigned int buddy_pfn = buddy - pageTable; + uart_log(BUDD, "Merge "); + uart_hex(pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((pfn + (1 << (order - 1))) * PAGE_SIZE - 1); + uart_puts("~~"); + uart_hex(buddy_pfn * PAGE_SIZE); + uart_puts("-"); + uart_hex((buddy_pfn + (1 << (order - 1))) * PAGE_SIZE - 1); + uart_puts(" => ["); + uart_dec(order); + uart_puts("]\n"); + } + pushPageToFreeList(&freeList[order], current, order); +} + +/* Cache Allocator */ + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order) { + object->order = order; + object->next = *list_head; + *list_head = object; +} + +struct object *popObjectFromList(struct object **list_head) { + if (*list_head == 0) return 0; + + struct object *object = *list_head; + *list_head = object->next; + return object; +} + +void *allocateCacheMemory(unsigned int order, int silent) { + if (!silent) { + uart_log(CACH, "Allocating "); + uart_dec(MIN_CACHE_SIZE << order); + uart_puts(" bytes."); + uart_putc(NEWLINE); + } + + if (objectCache[order] == 0) { + struct page *page = allocatePagesByOrder(0, silent); + page->cache_order = order; + unsigned int page_addr = (page - pageTable) * PAGE_SIZE; + unsigned int cache_size = MIN_CACHE_SIZE << order; + for (int i = 0; i < PAGE_SIZE; i += cache_size) { + struct object *obj = (struct object *)(uintptr_t)(page_addr + i); + pushObjectToList(&objectCache[order], obj, order); + } + } + void *p = popObjectFromList(&objectCache[order]); + if (!silent) { + uart_log(CACH, "Allocated memory starting from "); + uart_hex((uintptr_t)p); + uart_putc(NEWLINE); + } + return p; +} + +void freeCacheEntry(void *ptr, unsigned int index, int silence) { + if (!silence) { + uart_log(CACH, "Free memory cache of "); + uart_dec(MIN_CACHE_SIZE << index); + uart_puts(" bytes starting from "); + uart_hex((uintptr_t)ptr); + uart_putc(NEWLINE); + } + pushObjectToList(&objectCache[index], ptr, index); +} + +/* Dynamic Memory Allocator */ + +void *kmalloc(unsigned int size, int silent) { + if (size == 0) return 0; + + if (!silent) { + uart_log(INFO, "Memory requested: "); + uart_dec(size); + uart_puts(" byte(s)."); + uart_putc(NEWLINE); + } + + if (size > PAGE_SIZE / 2) { + // Buddy Allocator + int order = 0; + while ((PAGE_SIZE << order) < size) order++; + struct page *page = allocatePagesByOrder(order, silent); + return (void *)((page - pageTable) * PAGE_SIZE); + } else { + // Cache Allocator + int power = 0; + while ((1 << power) < size) power++; + int order = (power > 5) ? power - 5 : 0; + return allocateCacheMemory(order, silent); + } +} + +void kfree(void *ptr, int silence) { + // Check if the pointer is page-aligned + struct page *page = &pageTable[(uintptr_t)ptr / PAGE_SIZE]; + if ((uintptr_t)ptr % PAGE_SIZE == 0) { + // Check if the page is allocated by the buddy allocator + if (page->cache_order == -1) { + // Free the page using the buddy allocator + freePages(page, page->order, silence); + return; + } + } + // Free the object using the cache allocator + struct object *object = ptr; + freeCacheEntry(object, page->cache_order, silence); +} diff --git a/lab5/src/scheduler.S b/lab5/src/scheduler.S new file mode 100644 index 000000000..27f0ec45c --- /dev/null +++ b/lab5/src/scheduler.S @@ -0,0 +1,47 @@ +.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 // set current thread_struct + ret + + /* + struct context_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; + }; + */ + + + +.global get_current +get_current: + mrs x0, tpidr_el1 // current thread_struct + ret + diff --git a/lab5/src/scheduler.c b/lab5/src/scheduler.c new file mode 100644 index 000000000..ac929387a --- /dev/null +++ b/lab5/src/scheduler.c @@ -0,0 +1,128 @@ +#include "scheduler.h" + +#include "mem.h" +#include "uart.h" +#include "utils.h" + +static int IDLE_PROCESS_PID; +static int thread_count = 0; +thread_struct *run_circle; + +void sched_init() { + uart_log(INFO, "Initializing Scheduler...\n"); + IDLE_PROCESS_PID = kcreate_thread(idle)->pid; + asm volatile( + "msr tpidr_el1, %0" ::"r"(run_circle) // Software Thread ID Register + ); + uart_log(INFO, "Scheduler Initialized.\n"); +} + +static void add_thread(thread_struct **queue, thread_struct *thread) { + if (*queue == 0) { + *queue = thread; + thread->next = thread; + thread->prev = thread; + } else { + // prev <- thread -> *queue + thread->next = *queue; + thread->prev = (*queue)->prev; + // prev -> thread <- *queue + (*queue)->prev->next = thread; + (*queue)->prev = thread; + } +} + +thread_struct *kcreate_thread(void (*func)()) { + thread_struct *thread = kmalloc(sizeof(thread_struct), 0); + thread->pid = thread_count++; + thread->state = RUNNING; + thread->stack = kmalloc(STACK_SIZE, 0); + thread->user_stack = 0; // alloc when needed (el0) + memset(thread->sig_handlers, 0, sizeof(thread->sig_handlers)); + thread->sig_reg = 0; + thread->sig_busy = 0; + thread->context.lr = (unsigned long)func; + thread->context.sp = (unsigned long)thread->stack + STACK_SIZE; + thread->context.fp = (unsigned long)thread->stack + STACK_SIZE; + add_thread(&run_circle, thread); + return thread; +} + +static void remove_thread(thread_struct **queue, thread_struct *thread) { + if (*queue == thread) *queue = (thread->next == thread) ? 0 : thread->next; + thread->next->prev = thread->prev; + thread->prev->next = thread->next; +} + +void list_tcircle() { + thread_struct *thread = run_circle; + uart_log(INFO, "All thread(s) in queue:\n"); + do { + uart_log(INFO, "pid = "); + uart_dec(thread->pid); + if (thread->sig_handlers[9] > 0) uart_putc('*'); + uart_puts(", sp: "); + uart_hex((uintptr_t)thread->context.sp); + if (thread->pid == get_current()->pid) uart_puts(" <- current"); + uart_putc(NEWLINE); + thread = thread->next; + } while (thread != run_circle); +} + +void schedule() { switch_to(get_current(), get_current()->next); } + +void kill_zombies() { + thread_struct *next, *thread = run_circle; + do { + next = thread->next; + if (thread->state == DEAD) { + remove_thread(&run_circle, thread); + kfree(thread->stack, 0); + if (thread->user_stack) kfree(thread->user_stack, 0); + kfree(thread, 0); + } + thread = next; + } while (thread != run_circle); +} + +void idle() { + while (1) { + kill_zombies(); + schedule(); + } +} + +thread_struct *get_thread_by_pid(int pid) { + thread_struct *thread = run_circle; + do { + if (thread->pid == pid) return thread; + thread = thread->next; + } while (thread != run_circle); + return 0; +} + +void kill_current_thread() { + get_current()->state = DEAD; + schedule(); +} + +void kill_thread_by_pid(int pid) { + if (pid == IDLE_PROCESS_PID) { + uart_log(WARN, "Cannot kill idle process.\n"); + } else { + thread_struct *thread = run_circle; + do { + if (thread->pid == pid) { + uart_log(INFO, "Killing pid "); + uart_dec(pid); + uart_putc(NEWLINE); + thread->state = DEAD; + schedule(); + return; + } + thread = thread->next; + } while (thread != run_circle); + uart_log(WARN, "Nothing to kill.\n"); + } + schedule(); +} diff --git a/lab5/src/shell.c b/lab5/src/shell.c new file mode 100644 index 000000000..85ed4902d --- /dev/null +++ b/lab5/src/shell.c @@ -0,0 +1,84 @@ +#include "shell.h" + +#include "command.h" +#include "mem.h" +#include "str.h" +#include "uart.h" + +static void welcome_msg() { + cmd_info(); + uart_puts( + "*******************************\n" + "*** YADOS 0.04 for OSC 2024 ***\n" + "*******************************\n" + "Hopefully this will be Yet Another Dope OS!\n"); + uart_putc(NEWLINE); + cmd_hello(); +} + +void run_shell() { + welcome_msg(); + + char *buffer = kmalloc(SHELL_BUF_SIZE, 1); + while (1) { + uart_putc(NEWLINE); + uart_puts("# "); + read_user_input(buffer); + if (exec_command(buffer)) { + uart_log(WARN, "Command not found.\n"); + } + } +} + +void read_user_input(char *buf) { + int idx = 0; + while (idx < SHELL_BUF_SIZE) { + char c = uart_getc(); + switch (c) { + case NEWLINE: + uart_putc(NEWLINE); + buf[idx] = '\0'; + return; + case DELETE: + if (idx > 0) { + idx--; + uart_putc(BACKSPACE); + uart_putc(' '); + uart_putc(BACKSPACE); + } + break; + case ESC: + uart_putc(NEWLINE); + uart_log(WARN, "ESC detected. Clearing buffer...\n"); + uart_log(INFO, "Press Spacebar or Enter to continue.\n"); + while (1) { + char c = uart_getc(); + if (c == ' ' || c == NEWLINE) break; + } + buf[0] = '\0'; + uart_clear(); + return; + default: + if (c >= 32 && c <= 126) { + uart_putc(c); + buf[idx++] = c; + } + } + } + uart_putc(NEWLINE); + uart_log(WARN, "Buffer overflow. Please re-enter your command.\n"); + buf[0] = '\0'; +} + +int exec_command(const char *input) { + if (strlen(input) == 0) return 0; + struct command *cmd = cmd_list; + while (strcmp(cmd->name, END_OF_COMMAND_LIST)) { + if (!strcmp(cmd->name, input)) { + cmd->func(); + return 0; + } + cmd++; + } + return -1; +} \ No newline at end of file diff --git a/lab5/src/signals.c b/lab5/src/signals.c new file mode 100644 index 000000000..3bbf55773 --- /dev/null +++ b/lab5/src/signals.c @@ -0,0 +1,52 @@ +#include "signals.h" + +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "traps.h" +#include "uart.h" +#include "utils.h" + +void signal(int SIGNAL, void (*handler)()) { + get_current()->sig_handlers[SIGNAL] = handler; +} + +void signal_kill(int pid, int SIGNAL) { + thread_struct *thread = get_thread_by_pid(pid); + if (thread == 0) { + uart_log(WARN, "No one gets the signal.\n"); + return; + }; + thread->sig_reg |= 1 << (SIGNAL - 1); // Set the signal pending bit +} + +void do_signal(trap_frame *tf) { + // Prevent nested signal handling + if (get_current()->sig_busy) return; + int sig_num = 1; + while (get_current()->sig_reg) { + if (get_current()->sig_reg & (0x1 << (sig_num - 1))) { + get_current()->sig_busy = 1; // block other signal handling + get_current()->sig_reg &= ~(0x1 << (sig_num - 1)); + + if (get_current()->sig_handlers[sig_num] == 0) { + kill_current_thread(); // Default handler: (exit the process) + get_current()->sig_busy = 0; + return; // Jump to the previous context (user program) after eret + } + + // Save the sigframe + get_current()->sig_tf = kmalloc(sizeof(trap_frame), 0); + memcpy(get_current()->sig_tf, tf, sizeof(trap_frame)); + get_current()->sig_stack = kmalloc(STACK_SIZE, 0); + // will be releases in sys_sigreturn() + tf->x30 = (uintptr_t) + sigreturn; // Return to sigreturn (traps.S) after signal handling + tf->spsr_el1 = 0x340; + tf->elr_el1 = (unsigned long)get_current()->sig_handlers[sig_num]; + tf->sp_el0 = (unsigned long)get_current()->sig_stack + STACK_SIZE; + return; // eret to the signal handler (->el0) + } + sig_num++; + } +} \ No newline at end of file diff --git a/lab5/src/start.S b/lab5/src/start.S new file mode 100644 index 000000000..c6b255ecf --- /dev/null +++ b/lab5/src/start.S @@ -0,0 +1,50 @@ +.section ".text.boot" + +.global _start + +_start: + /* get dtb address from x0 */ + ldr x1, =DTB_BASE // defined in devtree.c + str x0, [x1] + + /* get cpu id */ + mrs x1, mpidr_el1 /* Multiprocessor Affinity Register */ + and x1, x1, #3 + cbnz x1, halt // halt if cpu id != 0 + + /* set exception vector table */ + adr x0, exception_vector_table // defined in traps.S + msr vbar_el1, x0 + + /* switch from EL2 to EL1 */ + bl from_el2_to_el1 + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main + +halt: + wfe + b halt + +from_el2_to_el1: + mov x0, (1 << 31) + msr hcr_el2, x0 // EL1 uses aarch64 + mov x0, 0x3C5 // ... 0011 1100 0101 + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 diff --git a/lab5/src/str.c b/lab5/src/str.c new file mode 100644 index 000000000..e8d54f053 --- /dev/null +++ b/lab5/src/str.c @@ -0,0 +1,30 @@ +#include "str.h" + +int strcmp(const char *str1, const char *str2) { + while (*str1 && *str1 == *str2) str1++, str2++; + return *(unsigned char *)str1 - *(unsigned char *)str2; +} + +char *strncpy(char *dest, const char *src, int n) { + while (n-- && (*dest++ = *src++)); + return dest; +} + +int strlen(const char *str) { + int len = 0; + while (*str++ != '\0') len++; + return len; +} + +char *strcat(char *dest, const char *src) { + char *d = dest; + + // Move the pointer to the end of the dest string + while (*d != '\0') d++; + // Copy the src string to the end of the dest string + while (*src != '\0') *d++ = *src++; + // Add the null terminator + *d = '\0'; + + return dest; +} diff --git a/lab5/src/syscalls.c b/lab5/src/syscalls.c new file mode 100644 index 000000000..4188f4848 --- /dev/null +++ b/lab5/src/syscalls.c @@ -0,0 +1,77 @@ +#include "syscalls.h" + +#include "initramfs.h" +#include "irq.h" +#include "mbox.h" +#include "mem.h" +#include "scheduler.h" +#include "str.h" +#include "traps.h" +#include "uart.h" +#include "utils.h" + +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_putc(buf[i++]); + return i; + return size; +} + +int sys_exec(const char *name, trap_frame *tf) { + initramfs_sys_exec(name, tf); + return 0; +} + +int sys_fork(trap_frame *tf) { + disable_interrupt(); // prevent schedule premature fork + + thread_struct *parent = get_current(); + thread_struct *child = kcreate_thread(0); + + // Handling kernel stack (incl. tf from parent) + memcpy(child->stack, parent->stack, STACK_SIZE); + // get child's trap frame from kstack via parent's offset + size_t ksp_offset = (uintptr_t)tf - (uintptr_t)parent->stack; + trap_frame *child_tf = (trap_frame *)(child->stack + ksp_offset); + child->context.sp = (uintptr_t)child_tf; // set child's ksp + + // Handling user stack + if (parent->user_stack) { + child->user_stack = kmalloc(STACK_SIZE, 0); + memcpy(child->user_stack, parent->user_stack, STACK_SIZE); + size_t usp_offset = tf->sp_el0 - (uintptr_t)parent->user_stack; + child_tf->sp_el0 = (uintptr_t)child->user_stack + usp_offset; + } + + // Copy signal handlers + memcpy(child->sig_handlers, parent->sig_handlers, + sizeof(parent->sig_handlers)); + + child->context.lr = (uintptr_t)child_ret_from_fork; // traps.S + child_tf->x0 = 0; // ret value of fork() for child + + enable_interrupt(); + return child->pid; +} + +void sys_exit(int status) { + uart_log(status ? WARN : INFO, "Exiting process ...\n"); + kill_current_thread(); +} + +void sys_sigreturn(trap_frame *tf) { + // Restore the sigframe + memcpy(tf, get_current()->sig_tf, sizeof(trap_frame)); + kfree(get_current()->sig_tf, 0); + kfree(get_current()->sig_stack, 0); + get_current()->sig_busy = 0; + return; // Jump to the previous context (user program) after eret +} diff --git a/lab5/src/test.c b/lab5/src/test.c new file mode 100644 index 000000000..344d70f74 --- /dev/null +++ b/lab5/src/test.c @@ -0,0 +1,137 @@ +#include "test.h" + +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "timer.h" +#include "uart.h" + +/* System Call Test */ + +static int get_pid() { + int pid = -1; + asm volatile( + "mov x7, 255\n" + "mov x8, 0\n" + "svc 0\n" + "mov %0, x0\n" + : "=r"(pid) + : + : "x8", "x0"); + return pid; +} + +static int fork() { + int ret = -1; + asm volatile( + "mov x8, 4\n" + "svc 0\n" + "mov %0, x0\n" + : "=r"(ret) + : + : "x8", "x0"); + return ret; +} + +static void exit() { + asm volatile( + "mov x0, 0\n" + "mov x8, 5\n" + "svc 0\n"); +} + +void thread_test() { + for (int i = 0; i < 10; ++i) { + uart_log(TEST, "Thread id: "); + uart_dec(get_current()->pid); + uart_puts(" "); + uart_dec(i); + uart_putc(NEWLINE); + + int *timeup = 0; + timer_add((void (*)(void *))set_timeup, (void *)timeup, 3); + while (!*timeup); + + schedule(); + } + kill_current_thread(); +} + +static void fork_test() { + uart_log(TEST, "Fork Test, pid "); + uart_dec(get_pid()); + uart_putc(NEWLINE); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; // force 64-bit aka uint64_t + asm volatile("mov %0, sp" : "=r"(cur_sp)); + uart_log(TEST, "first child pid: "); + uart_dec(get_pid()); + uart_puts(", cnt: "); + uart_dec(cnt); + uart_puts(", ptr: "); + uart_hex((uintptr_t)&cnt); + uart_puts(", sp: "); + uart_hex(cur_sp); + uart_putc(NEWLINE); + cnt++; + if ((ret = fork()) != 0) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + uart_log(TEST, "first child pid: "); + uart_dec(get_pid()); + uart_puts(", cnt: "); + uart_dec(cnt); + uart_puts(", ptr: "); + uart_hex((uintptr_t)&cnt); + uart_puts(", sp: "); + uart_hex(cur_sp); + uart_putc(NEWLINE); + } else { + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + uart_log(TEST, "second child pid: "); + uart_dec(get_pid()); + uart_puts(", cnt: "); + uart_dec(cnt); + uart_puts(", ptr: "); + uart_hex((uintptr_t)&cnt); + uart_puts(", sp: "); + uart_hex(cur_sp); + uart_putc(NEWLINE); + int *timeup = 0; + timer_add((void (*)(void *))set_timeup, (void *)timeup, 3); + while (!*timeup); + + cnt++; + } + } + exit(); // child + } else { + uart_log(TEST, "parent here, pid "); + uart_dec(get_pid()); + uart_puts(", child "); + uart_dec(ret); + uart_putc(NEWLINE); + } + exit(); // parent +} + +void run_fork_test() { + if (get_current()->user_stack == 0) { + uart_log(TEST, "Allocate user_stack for Fork Test\n"); + get_current()->user_stack = kmalloc(STACK_SIZE, 1); + } + asm volatile( // el1 -> el0 + "msr spsr_el1, %0\n" + "msr elr_el1, %1\n" + "msr sp_el0, %2\n" + "mov sp, %3\n" + "eret\n" + : + : "r"(0x340), // 0 + "r"(fork_test), // 1 + "r"(get_current()->user_stack + STACK_SIZE), // 2 + "r"(get_current()->context.sp) // 3 + : "memory"); +} diff --git a/lab5/src/timer.c b/lab5/src/timer.c new file mode 100644 index 000000000..71a415f14 --- /dev/null +++ b/lab5/src/timer.c @@ -0,0 +1,111 @@ +#include "timer.h" + +#include "mem.h" +#include "str.h" +#include "uart.h" + +static timerEntry *head = 0; + +void init_timer() { + asm volatile( + "mov x0, 1\n" + "msr cntp_ctl_el0, x0\n"); + + uart_log(INFO, "enable_timer_interrupt()\n"); + enable_timer_interrupt(); + + asm volatile( + "mrs x0, cntfrq_el0\n" + "msr cntp_tval_el0, x0\n"); + + // required by lab5 + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); +} + +void enable_timer_interrupt() { + // Unmask the timer interrupt + asm volatile( + "mov x0, 2\n" + "ldr x1, =%0\n" + "str w0, [x1]\n" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void disable_timer_interrupt() { + // Mask timer interrupt + asm volatile( + "mov x0, 0\n" + "ldr x1, =%0\n" + "str w0, [x1]\n" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void timer_irq_handler() { + // Set up the next timer interrupt (frequency >> 5, aka. 1/32 of the + // frequency) + asm volatile( + "mrs x0, cntfrq_el0\n" + "lsr x0, x0, 5\n" + "msr cntp_tval_el0, x0\n"); + + // Check the timer queue + while (head != 0 && timer_get_uptime() >= head->time) { + head->func(head->arg); // Execute the callback function + kfree(head, 0); // Free the memory allocated for the timer node + head = head->next; // Remove the head node after func finished + } + + enable_timer_interrupt(); +} + +uint64_t timer_get_uptime() { + uint64_t cntpct_el0 = 0, cntfrq_el0 = 0; + asm volatile( + "mrs %0, cntpct_el0\n" + "mrs %1, cntfrq_el0\n" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + return cntpct_el0 / cntfrq_el0; +} + +void timer_add(void (*callback)(void *), void *arg, int duration) { + // Insert a new timer into the linked list (sorted by time) + timerEntry *timer = kmalloc(sizeof(timerEntry), 1); + timer->func = callback; + timer->arg = arg; + timer->time = timer_get_uptime() + duration; + timer->next = 0; + + if (head == 0 || timer->time < head->time) { + // Insert as the head of the list + timer->next = head; + head = timer; + return; + } + + timerEntry *current = head; + while (current->next != 0 && current->next->time <= timer->time) + current = current->next; + timer->next = current->next; + current->next = timer; +} + +static void show_timer_message(void *arg) { + uart_putc(NEWLINE); + uart_log(INFO, "Timer message: "); + uart_puts((char *)arg); + uart_putc(NEWLINE); + kfree(arg, 1); // Free the memory allocated for arg +} + +void set_timer(const char *msg, int duration) { + timer_add((void (*)(void *))show_timer_message, (void *)msg, duration); +} + +void set_timeup(int *timeup) { *timeup = 1; } diff --git a/lab5/src/traps.S b/lab5/src/traps.S new file mode 100644 index 000000000..e2df8226b --- /dev/null +++ b/lab5/src/traps.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 /* Saved Program Status Register (EL1) */ + mrs x11, elr_el1 /* Exception Link Register (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 // pass the stack pointer to exception_entry + 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 // Exception Link Register (EL1) + mrs x1, esr_el1 // ESR_EL1, Exception Syndrome Register (EL1) + mrs x2, spsr_el1 // Saved Program Status Register (EL1) + bl invalid_entry // defined in traps.c + load_all + eret + +.global child_ret_from_fork +child_ret_from_fork: + load_all // child's kernel stack (trap frame) + eret + +.global sigreturn +sigreturn: + mov x8, 139 + svc 0 + +.macro v_entry 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 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL1 -> EL1 while using SP_EL1 + v_entry invalid_exc_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch64) -> EL1 + v_entry exception_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch32) -> EL1 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler diff --git a/lab5/src/traps.c b/lab5/src/traps.c new file mode 100644 index 000000000..8cfcefe43 --- /dev/null +++ b/lab5/src/traps.c @@ -0,0 +1,134 @@ +#include "traps.h" + +#include + +#include "irq.h" +#include "mbox.h" +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "uart.h" + +extern thread_struct *run_circle; + +void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr) { + // Print spsr_el1 + uart_log(INFO, "spsr_el1: "); + uart_hex(spsr); + uart_putc(NEWLINE); + + // Print elr_el1 + uart_log(INFO, "elr_el1: "); + uart_hex(elr); + uart_putc(NEWLINE); + + // Print esr_el1 + uart_log(INFO, "esr_el1: "); + uart_hex(esr); + uart_putc(NEWLINE); +} + +void exception_entry(trap_frame *tf) { + unsigned long elr, esr, spsr; + asm volatile( + "mrs %0, elr_el1\n" + "mrs %1, esr_el1\n" + "mrs %2, spsr_el1\n" + : "=r"(elr), "=r"(esr), "=r"(spsr) + : + : "memory"); + if (esr != 0x56000000) { + print_registers(elr, esr, spsr); + while (1); + } + + enable_interrupt(); // Prevent uart blocking the interrupt[?] + + unsigned int syscall_num = tf->x8; + // if (syscall_num > 2) { + // uart_log(INFO, "syscall_num: "); + // uart_dec(syscall_num); + // uart_putc(NEWLINE); + // } + switch (syscall_num) { + case 0: { // int getpid() + unsigned int test = tf->x7; + unsigned int pid = sys_getpid(); + tf->x0 = pid; + if (test != 255) list_tcircle(); + break; + } + case 1: { // size_t uartread(char buf[], size_t size) + char *buf = (char *)tf->x0; + size_t size = tf->x1; + tf->x0 = sys_uart_read(buf, size); + break; + } + case 2: { // size_t uartwrite(const char buf[], size_t size) + char *buf = (char *)tf->x0; + size_t size = tf->x1; + tf->x0 = sys_uart_write(buf, size); + } break; + case 3: // int exec(const char *name, char *const argv[]) + const char *name = (char *)tf->x0; + tf->x0 = sys_exec(name, tf); + break; + case 4: { // int fork() + tf->x0 = sys_fork(tf); + break; + } + case 5: { // void exit(int status) + int status = tf->x0; + sys_exit(status); + break; + } + case 6: { // int mbox_call(unsigned char ch, unsigned int *mbox) + unsigned char ch = tf->x0; + unsigned int *mbox = (unsigned int *)tf->x1; + tf->x0 = mbox_call(ch, mbox); + break; + } + case 7: { // void kill(int pid) + unsigned int pid = tf->x0; + kill_thread_by_pid(pid); + break; + } + case 8: { // signal(int SIGNAL, void (*handler)()) + unsigned int SIGNAL = tf->x0; + void (*handler)() = (void (*)())tf->x1; + signal(SIGNAL, handler); + break; + } + case 9: { // (signal_)kill(int pid, int SIGNAL) + unsigned int pid = tf->x0; + unsigned int SIGNAL = tf->x1; + uart_log(INFO, "Signal "); + uart_dec(SIGNAL); + uart_puts(" sent to pid "); + uart_dec(pid); + uart_putc(NEWLINE); + signal_kill(pid, SIGNAL); + break; + } + case 139: + sys_sigreturn(tf); + break; + default: + uart_log(ERR, "Invalid system call\n"); + } +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr) { + uart_log(ERR, "The exception handler is not implemented\n"); + print_registers(elr, esr, spsr); + + if ((esr & 0xFC000000) == 0x94000000) { + uint32_t svc_number = esr & 0xFFFF; + uart_log(ERR, "SVC Call Exception. SVC Number: "); + uart_dec(svc_number); + uart_putc(NEWLINE); + } else { + uart_log(ERR, "Unknown Exception\n"); + } + while (1); +} \ No newline at end of file diff --git a/lab5/src/uart.c b/lab5/src/uart.c new file mode 100644 index 000000000..e5f85f732 --- /dev/null +++ b/lab5/src/uart.c @@ -0,0 +1,191 @@ +#include "uart.h" + +#include "irq.h" +#include "mem.h" +#include "str.h" + +static int uart_read_idx = 0; +static int uart_write_idx = 0; +static char uart_read_buffer[UART_BUF_SIZE]; +static char uart_write_buffer[UART_BUF_SIZE]; + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + // Disable GPIO pull up/down + *GPPUD = 0; + for (int i = 0; i < 150; i++); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + // Initialize mini UART + *AUX_ENABLE |= 1; // Enable mini UART + *AUX_MU_CNTL = 0; // Disable Tx and Rx during setup + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set data size to 8 bits + *AUX_MU_MCR = 0; // Disable auto flow control + *AUX_MU_BAUD = 270; // Set baud rate to 115200 + *AUX_MU_IIR = 6; // No FIFO + *AUX_MU_CNTL = 3; // Enable Tx and Rx + + // Enable AUX interrupts + *ENABLE_IRQS_1 |= 1 << 29; +} + +char uart_getc() { + // Check the data ready field on bit 0 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); // Read from AUX_MU_IO_REG + return c == CARRIAGE_RETURN ? NEWLINE : c; +} + +void uart_putc(char c) { + // Add CARRIAGE_RETURN before NEWLINE + if (c == NEWLINE) uart_putc(CARRIAGE_RETURN); + + // Check the transmitter empty field on bit 5 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = c; // Write to AUX_MU_IO_REG +} + +void uart_puts(const char *s) { + while (*s) { + uart_putc(*s++); + } +} + +void uart_clear() { uart_puts("\033[2J\033[H"); } + +void uart_hex(unsigned int h) { + uart_puts("0x"); + unsigned int n; + for (int c = 28; c >= 0; c -= 4) { + n = (h >> c) & 0xF; + n += n > 9 ? 0x37 : '0'; + uart_putc(n); + } +} + +void uart_simple_hex(unsigned int h) { + uart_puts("0x"); + if (h == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + unsigned int n; + while (h > 0) { + n = h % 16; + buf[i++] = n + (n > 9 ? 0x37 : '0'); + h /= 16; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_dec(unsigned int d) { + if (d == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + while (d > 0) { + buf[i++] = d % 10 + '0'; + d /= 10; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_log(int type, const char *msg) { + switch (type) { + case INFO: + uart_puts("[INFO] "); + break; + case TEST: + uart_puts("[TEST] "); + break; + case BUDD: + uart_puts("[BUDD] "); + break; + case CACH: + uart_puts("[CACH] "); + break; + case WARN: + uart_puts("[WARN] "); + break; + case ERR: + uart_puts("[ERR!] "); + break; + default: + uart_puts("[LOG?] "); + break; + } + uart_puts(msg); +} + +void enable_uart_tx_interrupt() { *AUX_MU_IER |= 0x02; } + +void disable_uart_tx_interrupt() { *AUX_MU_IER &= 0x01; } + +void enable_uart_rx_interrupt() { *AUX_MU_IER |= 0x01; } + +void disable_uart_rx_interrupt() { *AUX_MU_IER &= 0x2; } + +void uart_tx_irq_handler() { + disable_uart_tx_interrupt(); + // Buffer is not empty -> send characters + if (uart_write_idx < UART_BUF_SIZE && + uart_write_buffer[uart_write_idx] != 0) { + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = uart_write_buffer[uart_write_idx++]; + enable_uart_tx_interrupt(); + } +} + +void uart_rx_irq_handler() { + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); + uart_read_buffer[uart_read_idx++] = (c == CARRIAGE_RETURN) ? NEWLINE : c; + if (uart_read_idx >= UART_BUF_SIZE) uart_read_idx = 0; + enable_uart_rx_interrupt(); +} + +void uart_async_read(char *buf, int len) { + disable_uart_rx_interrupt(); + for (int i = 0; i < uart_read_idx && i < len; i++) + buf[i] = uart_read_buffer[i]; + buf[uart_read_idx] = 0; + uart_read_idx = 0; +} + +void uart_async_write(const char *s) { + // Copy string to the write buffer + int len = 0; + while (*s != '\0') { + if (len >= UART_BUF_SIZE) { + uart_log(ERR, "Exceed the UART buffer size\n"); + return; + } + if (*s == NEWLINE) { + // Insert CARRIAGE_RETURN before NEWLINE + uart_write_buffer[len++] = CARRIAGE_RETURN; + uart_write_buffer[len++] = NEWLINE; + } else + uart_write_buffer[len++] = *s; + s++; + } + uart_write_buffer[len] = '\0'; + uart_write_idx = 0; // Reset the buffer index + enable_uart_tx_interrupt(); +} diff --git a/lab5/src/utils.c b/lab5/src/utils.c new file mode 100644 index 000000000..dec35665b --- /dev/null +++ b/lab5/src/utils.c @@ -0,0 +1,71 @@ +#include "utils.h" + +int align4(int n) { return n + (4 - n % 4) % 4; } + +int atoi(const char *s) { + int result = 0; + int sign = 1; + int i = 0; + + // Skip leading spaces + while (s[i] == ' ') { + i++; + } + + // Handle positive and negative sign + if (s[i] == '-') { + sign = -1; + i++; + } else if (s[i] == '+') { + i++; + } + + // Convert string to integer + while (s[i] >= '0' && s[i] <= '9') { + result = result * 10 + (s[i] - '0'); + i++; + } + + return sign * result; +} + +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; +} + +int memcmp(const void *m1, const void *m2, int n) { + const unsigned char *a = m1, *b = m2; + while (n-- > 0) { + if (*a != *b) return *a - *b; + a++; + b++; + } + return 0; +} + +/** + * @brief Copy `n` bytes from `src` to `dest`. + */ +void *memcpy(void *dest, const void *src, int n) { + char *d = dest; + const char *s = src; + while (n--) *d++ = *s++; + return dest; +} + +/** + * @brief Set `n` bytes of `s` to `c`. + */ +void *memset(void *s, int c, int n) { + unsigned char *p = s; + while (n--) *p++ = (unsigned char)c; + return s; +} \ No newline at end of file diff --git a/lab6/.gitignore b/lab6/.gitignore new file mode 100644 index 000000000..eddcca901 --- /dev/null +++ b/lab6/.gitignore @@ -0,0 +1,6 @@ +.vscode +build +rootfs +temp +*.img +*.dtb \ No newline at end of file diff --git a/lab6/Makefile b/lab6/Makefile new file mode 100644 index 000000000..c6dde9a1a --- /dev/null +++ b/lab6/Makefile @@ -0,0 +1,41 @@ +ARMGNU ?= aarch64-linux-gnu + +CFLAGS = -Iinclude -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +ASMFLAGS = -Iinclude +QEMUFLAGS = -M raspi3b -display none -serial null -serial stdio +QEMUFLAGSDISP = -M raspi3b -serial null -serial stdio + +SRC_DIR = src +BUILD_DIR = build + +all: clean kernel8.img + +clean: + rm -rf $(BUILD_DIR) *.img + +build: + mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/_%.o: $(SRC_DIR)/%.S | $(BUILD_DIR) + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/_%.o) + +kernel8.img: $(SRC_DIR)/linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/kernel8.elf kernel8.img + +qemu: all initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear & qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +dpqemu: all initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear & qemu-system-aarch64 $(QEMUFLAGSDISP) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug: all initramfs.cpio bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -S -s -d int \ No newline at end of file diff --git a/lab6/bootloader/Makefile b/lab6/bootloader/Makefile new file mode 100644 index 000000000..d7882aa27 --- /dev/null +++ b/lab6/bootloader/Makefile @@ -0,0 +1,19 @@ +ARMGNU ?= aarch64-linux-gnu +CFLAGS = -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +QEMUFLAGS = -M raspi3b -display none -serial null -serial pty + +all: clean bootloader.img + +clean: + rm -rf build *.img + +bootloader.img: + mkdir -p build + $(ARMGNU)-gcc $(CFLAGS) -c main.c -o build/main.o + $(ARMGNU)-gcc $(CFLAGS) -c start.S -o build/start.o + $(ARMGNU)-gcc $(CFLAGS) -c boot.c -o build/boot.o + $(ARMGNU)-ld -T linker.ld -o build/bootloader.elf build/main.o build/start.o build/boot.o + $(ARMGNU)-objcopy -O binary build/bootloader.elf bootloader.img + +qemu: clean bootloader.img + qemu-system-aarch64 $(QEMUFLAGS) -kernel bootloader.img -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/lab6/bootloader/boot.c b/lab6/bootloader/boot.c new file mode 100644 index 000000000..d4ff535f5 --- /dev/null +++ b/lab6/bootloader/boot.c @@ -0,0 +1,61 @@ +#include "boot.h" + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + *GPPUD = 0; + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +char uart_recv() { + while (!(*AUX_MU_LSR & 0x01)) asm volatile("nop"); + return (char)(*AUX_MU_IO); +} + +void uart_putc(char c) { + if (c == '\n') uart_putc('\r'); + while (!(*AUX_MU_LSR & 0x20)) asm volatile("nop"); + *AUX_MU_IO = c; +} + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} + +int atoi(const char *s) { + int result = 0; + 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 * result; +} \ No newline at end of file diff --git a/lab6/bootloader/boot.h b/lab6/bootloader/boot.h new file mode 100644 index 000000000..84cece020 --- /dev/null +++ b/lab6/bootloader/boot.h @@ -0,0 +1,49 @@ +#ifndef BOOT_H +#define BOOT_H + +/* ==================== GPIO ==================== */ +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) + +/* ==================== UART ==================== */ +#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 init_uart(); +char uart_recv(); +void uart_putc(char c); +void uart_puts(const char *s); + +/* ==================== UTILS ==================== */ +int atoi(const char *s); + +#endif // BOOT_H \ No newline at end of file diff --git a/lab6/bootloader/linker.ld b/lab6/bootloader/linker.ld new file mode 100644 index 000000000..556816b11 --- /dev/null +++ b/lab6/bootloader/linker.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + . = 0x60000; + __loader_start = .; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } + __loader_end = .; +} +__bss_size = SIZEOF(.bss); +__loader_size = __loader_end - __loader_start; \ No newline at end of file diff --git a/lab6/bootloader/main.c b/lab6/bootloader/main.c new file mode 100644 index 000000000..cc8faa0fe --- /dev/null +++ b/lab6/bootloader/main.c @@ -0,0 +1,41 @@ +#include "boot.h" + +int main() { + init_uart(); + uart_puts("\033[2J\033[H"); + uart_puts( + "UART Bootloader\n" + "Waiting for kernel...\n"); + + // Get kernel image size + char buf[16] = {0}; + for (int i = 0; i < 16; i++) { + buf[i] = uart_recv(); + if (buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + + // Load kernel image + uart_puts("Kernel size: "); + uart_puts(buf); + uart_puts(" bytes.\n"); + uart_puts("Loading the kernel image...\n"); + + unsigned int size = atoi(buf); + char *kernel = (char *)0x80000; + while (size--) *kernel++ = uart_recv(); + + // Restore registers x0 x1 x2 x3 + asm volatile( + "mov x0, x10\n" + "mov x1, x11\n" + "mov x2, x12\n" + "mov x3, x13\n" + "mov x30, 0x80000\n" + "ret\n" // Jump to the new kernel + ); + + return 0; +} \ No newline at end of file diff --git a/lab6/bootloader/start.S b/lab6/bootloader/start.S new file mode 100644 index 000000000..ec8488dde --- /dev/null +++ b/lab6/bootloader/start.S @@ -0,0 +1,40 @@ +.section ".text.boot" + +.global _start + +_start: + /* save registers x0 x1 x2 x3 */ + mov x10, x0 /* dtb_base address */ + mov x11, x1 + mov x12, x2 + mov x12, x3 + + /* relocate bootloader */ + ldr x1, =0x80000 + ldr x2, =__loader_start // 0x60000 + ldr w3, =__loader_size + +relocate: + ldr x4, [x1], #8 + str x4, [x2], #8 + sub w3, w3, #1 + cbnz w3, relocate + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main-0x20000 + b run_main diff --git a/lab6/dump.sh b/lab6/dump.sh new file mode 100755 index 000000000..91ab357ab --- /dev/null +++ b/lab6/dump.sh @@ -0,0 +1,2 @@ +aarch64-linux-gnu-objdump build/kernel8.elf -d > dump.txt + diff --git a/lab6/include/command.h b/lab6/include/command.h new file mode 100644 index 000000000..b48238d64 --- /dev/null +++ b/lab6/include/command.h @@ -0,0 +1,19 @@ +#pragma once + +#define MAX_BUF_SIZE 1024 +#define END_OF_COMMAND_LIST "NULL" + +struct command { + const char *name; + const char *help; + void (*func)(void); +}; + +extern struct command cmd_list[]; +extern unsigned int BOARD_REVISION; +extern unsigned long BASE_MEMORY; +extern unsigned int NUM_PAGES; + +// Commands +void cmd_info(); +void cmd_hello(); \ No newline at end of file diff --git a/lab6/include/devtree.h b/lab6/include/devtree.h new file mode 100644 index 000000000..0a1e5bf65 --- /dev/null +++ b/lab6/include/devtree.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "hardware.h" + +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; +}; + +/** + * @brief Convert a 4-byte big-endian sequence to little-endian. + * + * @param s: big-endian sequence + * @return little-endian sequence + */ +uint32_t be2le(const void *s); + +void fdt_traverse(void (*callback)(void *, char *)); diff --git a/lab6/include/hardware.h b/lab6/include/hardware.h new file mode 100644 index 000000000..d64eabf86 --- /dev/null +++ b/lab6/include/hardware.h @@ -0,0 +1,212 @@ +#pragma once + +#include "start.h" + +#define VM 0xFFFF000000000000 + +#define TO_VIRT(x) ((unsigned long long)(x) | VM) +#define TO_PHYS(x) ((unsigned long long)(x) & ~VM) + +#define MMIO_BASE TO_VIRT(0x3F000000) + +// ARM Interrupt Registers + +#define IRQ_BASIC_PENDING (volatile unsigned int *)(MMIO_BASE + 0x0000B200) +#define IRQ_PENDING_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B204) +#define IRQ_PENDING_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B208) +#define FIQ_CONTROL (volatile unsigned int *)(MMIO_BASE + 0x0000B20C) +#define ENABLE_IRQS_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B210) +#define ENABLE_IRQS_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B214) +#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) + +// Timers interrupt control registers + +#define CORE0_INTERRUPT_SOURCE (volatile unsigned int *)TO_VIRT(0x40000060) +#define CORE0_TIMER_IRQCNTL TO_VIRT(0x40000040) +#define CORE1_TIMER_IRQCNTL TO_VIRT(0x40000044) +#define CORE2_TIMER_IRQCNTL TO_VIRT(0x40000048) +#define CORE3_TIMER_IRQCNTL TO_VIRT(0x4000004C) + +// Where to route timer interrupt to, IRQ/FIQ +// Setting both the IRQ and FIQ bit gives an FIQ +#define TIMER0_IRQ 0x01 +#define TIMER1_IRQ 0x02 +#define TIMER2_IRQ 0x04 +#define TIMER3_IRQ 0x08 +#define TIMER0_FIQ 0x10 +#define TIMER1_FIQ 0x20 +#define TIMER2_FIQ 0x40 +#define TIMER3_FIQ 0x80 + +// PASSWORD + +#define PM_PASSWORD 0x5A000000 +#define PM_RSTC (volatile unsigned int *)0x3F10001C +#define PM_WDOG (volatile unsigned int *)0x3F100024 + +// DEVICETREE + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +// GPIO + +#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 GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) +// Skip GPRENn, GPFENn +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +// Skip GPLENn, GPARENn +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +// UART + +#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)) + +// MAILBOX + +#define MAILBOX_BASE (MMIO_BASE + 0x0000B880) + +// mailbox register +#define MAILBOX_REG_READ ((volatile unsigned int *)(MAILBOX_BASE + 0x00000000)) +#define MAILBOX_REG_STATUS \ + ((volatile unsigned int *)(MAILBOX_BASE + 0x00000018)) +#define MAILBOX_REG_WRITE ((volatile unsigned int *)(MAILBOX_BASE + 0x00000020)) + +// mailbox channel +// https://github.com/raspberrypi/firmware/wiki/Mailboxes#channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +// mailbox status +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 +#define MAILBOX_REQUEST 0x00000000 +#define MAILBOX_RESPONSE 0x80000000 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +// tags +#define TAGS_REQ_CODE 0x00000000 +#define TAGS_REQ_SUCCEED 0x80000000 +#define TAGS_REQ_FAILED 0x80000001 +#define TAGS_END 0x00000000 + +// hardware tags operator +#define TAGS_HARDWARE_BOARD_MODEL 0x00010001 +#define TAGS_HARDWARE_BOARD_REVISION 0x00010002 +#define TAGS_HARDWARE_MAC_ADDR 0x00010003 +#define TAGS_HARDWARE_BOARD_SERIAL 0x00010004 +#define TAGS_HARDWARE_ARM_MEM 0x00010005 +#define TAGS_HARDWARE_VC_MEM 0x00010006 +#define TAGS_HARDWARE_CLOCKS 0x00010007 + +// CLOCK ID + +#define CLOCK_ID_RESERVED 0x000000000 +#define CLOCK_ID_EMMC 0x000000001 +#define CLOCK_ID_UART 0x000000002 +#define CLOCK_ID_ARM 0x000000003 +#define CLOCK_ID_CORE 0x000000004 +#define CLOCK_ID_V3D 0x000000005 +#define CLOCK_ID_H264 0x000000006 +#define CLOCK_ID_ISP 0x000000007 +#define CLOCK_ID_SDRAM 0x000000008 +#define CLOCK_ID_PIXEL 0x000000009 +#define CLOCK_ID_PWM 0x00000000a +#define CLOCK_ID_HEVC 0x00000000b +#define CLOCK_ID_EMMC2 0x00000000c +#define CLOCK_ID_M2MC 0x00000000d +#define CLOCK_ID_PIXEL_BVB 0x00000000e + +// clock tags operator +#define TAGS_GET_CLOCK 0x00030002 +#define TAGS_SET_CLOCK 0x00038002 + +// framebuffer tages operator +#define FB_ALLOC_BUFFER 0x00040001 +#define FB_FREE_BUFFER 0x00048001 + +#define FB_BLANK_SCREEN 0x00040002 + +#define FB_PHY_WID_HEIGHT_GET 0x00040003 +#define FB_PHY_WID_HEIGHT_TEST 0x00044003 +#define FB_PHY_WID_HEIGHT_SET 0x00048003 + +#define FB_VIR_WID_HEIGHT_GET 0x00040004 +#define FB_VIR_WID_HEIGHT_TEST 0x00044004 +#define FB_VIR_WID_HEIGHT_SET 0x00048004 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_PIXEL_ORDER_GET 0x00040006 +#define FB_PIXEL_ORDER_TEST 0x00044006 +#define FB_PIXEL_ORDER_SET 0x00048006 + +#define FB_ALPHA_MODE_GET 0x00040007 +#define FB_ALPHA_MODE_TEST 0x00044007 +#define FB_ALPHA_MODE_SET 0x00048007 + +#define FB_PITCH_GET 0x00040008 + +#define FB_VIR_OFFSET_GET 0x00040009 +#define FB_VIR_OFFSET_TEST 0x00044009 +#define FB_VIR_OFFSET_SET 0x00048009 + +#define FB_OVERSCAN_GET 0x0004000A +#define FB_OVERSCAN_TEST 0x0004400A +#define FB_OVERSCAN_SET 0x0004800A + +#define FB_PALETTE_GET 0x0004000B +#define FB_PALETTE_TEST 0x0004400B +#define FB_PALETTE_SET 0x0004800B + +#define FB_CURSOR_INFO_SET 0x00008010 +#define FB_CURSOR_STATE_SET 0x00008011 diff --git a/lab6/include/initramfs.h b/lab6/include/initramfs.h new file mode 100644 index 000000000..4c82cb598 --- /dev/null +++ b/lab6/include/initramfs.h @@ -0,0 +1,35 @@ +#pragma once + +#include "traps.h" + +// Cpio Archive File Header (New ASCII Format) +typedef struct { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +} cpio_t; + +typedef struct { + int namesize; + int filesize; + int headsize; + int datasize; + char *pathname; +} ramfsRec; + +void initramfs_ls(); +void initramfs_cat(const char *target); +void initramfs_callback(void *addr, char *property); +void initramfs_run(const char *target); +void initramfs_sys_exec(const char *target, trap_frame *tf); diff --git a/lab6/include/irq.h b/lab6/include/irq.h new file mode 100644 index 000000000..cef36db3a --- /dev/null +++ b/lab6/include/irq.h @@ -0,0 +1,20 @@ +#pragma once + +#include "hardware.h" +#include "traps.h" + +#define ORDER_FIRST 0x0 +#define ORDER_REGULAR 0x8 +#define ORDER_LAST 0xF + +typedef struct irq_task_t { + void (*func)(); + int order; + int busy; // 0 (default) or 1 (busy) + struct irq_task_t *prev; + struct irq_task_t *next; +} irqTask; + +void enable_interrupt(); +void disable_interrupt(); +void irq_entry(trap_frame *tf); \ No newline at end of file diff --git a/lab6/include/mbox.h b/lab6/include/mbox.h new file mode 100644 index 000000000..c40987c13 --- /dev/null +++ b/lab6/include/mbox.h @@ -0,0 +1,9 @@ +#pragma once + +#include "hardware.h" + +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch, unsigned int *mbox); +int get_board_revision(unsigned int *mbox); +int get_arm_memory_status(unsigned int *mbox); diff --git a/lab6/include/mem.h b/lab6/include/mem.h new file mode 100644 index 000000000..9eab5e6ea --- /dev/null +++ b/lab6/include/mem.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include "start.h" + +#define BUDDY_MAX_ORDER 16 +#define CACHE_MAX_ORDER 6 +#define MIN_CACHE_SIZE (PAGE_SIZE / (2 << CACHE_MAX_ORDER)) + +struct page { + unsigned int order; + unsigned int used; + unsigned int cache_order; + struct page *prev; + struct page *next; +}; + +struct object { + unsigned int order; + struct object *next; +}; + +void init_mem(); +void reserveMemory(uint64_t start, uint64_t end); + +uintptr_t page_vaddr(struct page *p); + +void pushPageToFreeList(struct page **list, struct page *page, + unsigned int order); +struct page *popFreeList(struct page **list_head); +void removePageFromFreeList(struct page **list, struct page *page); +void printFreeListByOrder(unsigned int order); + +struct page *lookupBuddy(struct page *page, unsigned int order); +struct page *allocatePagesByOrder(unsigned int order, int silent); +void freePages(struct page *page, unsigned int order, int silent); +void mergePages(struct page *page, unsigned int order, int echo); + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order); +struct object *popObjectFromList(struct object **list_head); +void *allocateCacheMemory(unsigned int index, int silent); +void freeCacheEntry(void *ptr, unsigned int index, int silent); + +void *kmalloc(unsigned int size, int silent); +void kfree(void *ptr, int silent); diff --git a/lab6/include/scheduler.h b/lab6/include/scheduler.h new file mode 100644 index 000000000..785c2c28a --- /dev/null +++ b/lab6/include/scheduler.h @@ -0,0 +1,69 @@ +#pragma once + +#include "signals.h" +#include "traps.h" + +struct context_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 thread_state { + RUNNING, + DEAD, +}; + +typedef struct thread_struct_t { + struct context_struct context; // callee-saved (don't move) + int pid; + + void *start; + unsigned int size; + + enum thread_state state; + void *stack; + void *user_stack; + + // Signal handling + void (*sig_handlers[NSIG + 1])(); // Signal handlers + int sig_reg; // Pending signals + int sig_busy; // Handling a signal + trap_frame *sig_tf; // tf Saved before signal handling + void *sig_stack; // Stack for signal handling + + void *pgd; + // struct vm_area_struct *mmap; + + struct thread_struct_t *prev; + struct thread_struct_t *next; +} thread_struct; + +// Defined in scheduler.S +extern void switch_to(thread_struct *prev, thread_struct *next); +extern void switch_mm(unsigned long pgd); + +extern thread_struct *get_current(); // get tpidr_el1, current thread_struct + +thread_struct *get_thread_by_pid(int pid); +void sched_init(); + +thread_struct *kcreate_thread(void (*func)()); +thread_struct *kcreate_user_thread(void *program, int filesize); +void list_tcircle(); + +void schedule(); + +void kill_current_thread(); +void kill_thread_by_pid(int pid); +void idle(); \ No newline at end of file diff --git a/lab6/include/shell.h b/lab6/include/shell.h new file mode 100644 index 000000000..fccef918e --- /dev/null +++ b/lab6/include/shell.h @@ -0,0 +1,7 @@ +#pragma once + +#define SHELL_BUF_SIZE 1024 + +void run_shell(); +void read_user_input(char *buf); +int exec_command(const char *command); diff --git a/lab6/include/signals.h b/lab6/include/signals.h new file mode 100644 index 000000000..e3ee32af0 --- /dev/null +++ b/lab6/include/signals.h @@ -0,0 +1,12 @@ +#pragma once + +#include "traps.h" + +#define NSIG 10 // Number of signals +#define SIG_STACK (USER_STACK - STACK_SIZE) + +extern void sigreturn(); // Defined in traps.S + +void signal(int signum, void (*handler)()); +void signal_kill(int pid, int sig); +void do_signal(trap_frame *regs); diff --git a/lab6/include/start.h b/lab6/include/start.h new file mode 100644 index 000000000..b484682c3 --- /dev/null +++ b/lab6/include/start.h @@ -0,0 +1,42 @@ +#define PAGE_SIZE 0x1000 +#define STACK_SIZE 0x4000 + +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) +#define TCR_CONFIG_DEFAULT (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB) + +/* + * nGnRnE: Non-Gathering, Non-Reordering, No Early Write Acknowledgement + * Normal memory, Inner Non-cacheable, Outer Non-cacheable + */ +#define MAIR_DEVICE_nGnRnE 0b00000000 +#define MAIR_NORMAL_NOCACHE 0b01000100 +#define MAIR_IDX_DEVICE_nGnRnE 0 +#define MAIR_IDX_NORMAL_NOCACHE 1 + +#define PD_TABLE 0b11 +#define PD_BLOCK 0b01 +#define PD_ENTRY 0b11 + +// Bits[10] The access flag, a page fault is generated if not set. +#define PD_ACCESS (1 << 10) + +// Bits[7] 0 for read-write, 1 or read-only +#define PD_RDONLY (1 << 7) + +// Bits[6] 0 for only kernel access, 1 for user/kernel access +#define PD_UKACCESS (1 << 6) + +// Bits[4:2] The index to MAIR +#define PD_MAIR_DEVICE_nGnRnE (MAIR_IDX_DEVICE_nGnRnE << 2) +#define PD_MAIR_NORMAL_NOCACHE (MAIR_IDX_NORMAL_NOCACHE << 2) + +#define BOOT_PGD_PAGE_FRAME 0x1000 // Boot PGD's page frame +#define BOOT_PUD_PAGE_FRAME 0x2000 // Boot PUD's page frame +#define BOOT_PMD_PAGE_FRAME 0x3000 // Boot PMD's page frame + +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR PD_TABLE +#define BOOT_PUD_DEVICE_ATTR (PD_ACCESS | PD_MAIR_DEVICE_nGnRnE | PD_BLOCK) +#define BOOT_PMD_NORMAL_ATTR (PD_ACCESS | PD_MAIR_NORMAL_NOCACHE | PD_BLOCK) +#define BOOT_PMD_DEVICE_ATTR (PD_ACCESS | PD_MAIR_DEVICE_nGnRnE | PD_BLOCK) \ No newline at end of file diff --git a/lab6/include/str.h b/lab6/include/str.h new file mode 100644 index 000000000..766ff7ea8 --- /dev/null +++ b/lab6/include/str.h @@ -0,0 +1,6 @@ +#pragma once + +int strcmp(const char *str1, const char *str2); +char *strncpy(char *dest, const char *src, int n); +int strlen(const char *str); +char *strcat(char *dest, const char *src); \ No newline at end of file diff --git a/lab6/include/syscalls.h b/lab6/include/syscalls.h new file mode 100644 index 000000000..42fb31d6d --- /dev/null +++ b/lab6/include/syscalls.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "traps.h" + +extern void child_ret_from_fork(); // traps.S + +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, trap_frame *tf); +int sys_fork(trap_frame *tf); +void sys_exit(int status); +int sys_mbox_call(unsigned char ch, unsigned int *mbox); +void sys_sigreturn(trap_frame *regs); \ No newline at end of file diff --git a/lab6/include/test.h b/lab6/include/test.h new file mode 100644 index 000000000..37346deb5 --- /dev/null +++ b/lab6/include/test.h @@ -0,0 +1,4 @@ +#pragma once + +void thread_test(); +void run_fork_test(); \ No newline at end of file diff --git a/lab6/include/timer.h b/lab6/include/timer.h new file mode 100644 index 000000000..464beb68f --- /dev/null +++ b/lab6/include/timer.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#define TIMER_SPEED 5 + +typedef struct timerEntry_t { + void (*func)(void *); + void *arg; + int time; + struct timerEntry_t *next; +} timerEntry; + +void init_timer(); + +void enable_timer_interrupt(); +void disable_timer_interrupt(); + +void timer_irq_handler(); +uint64_t timer_get_uptime(); + +void timer_add(void (*callback)(void *), void *arg, int duration); +void set_timer(const char *message, int duration); +void set_timeup(int *timeup); diff --git a/lab6/include/traps.h b/lab6/include/traps.h new file mode 100644 index 000000000..bf940f5d7 --- /dev/null +++ b/lab6/include/traps.h @@ -0,0 +1,38 @@ +#pragma once + +typedef struct { // order is important + 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; // system call number + 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; // link register + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trap_frame; diff --git a/lab6/include/uart.h b/lab6/include/uart.h new file mode 100644 index 000000000..6253f2bfa --- /dev/null +++ b/lab6/include/uart.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include "hardware.h" + +#define UART_BUF_SIZE 1024 + +// Special characters +#define BACKSPACE '\b' +#define DELETE 127 +#define ESC 27 +#define NEWLINE '\n' +#define TAB '\t' +#define CARRIAGE_RETURN '\r' + +// uart_log types +#define INFO 0 +#define WARN 1 +#define TEST 2 +#define BUDD 10 +#define CACH 11 +#define ERR 255 + +void init_uart(); +char uart_getc(); +void uart_putc(char c); +void uart_puts(const char *s); +void uart_clear(); +void uart_hex(unsigned long h); +void uart_addr_range(uintptr_t start, uintptr_t end); +void uart_simple_hex(unsigned int h); +void uart_dec(unsigned int h); +void uart_log(int type, const char *msg); + +void enable_uart_tx_interrupt(); +void disable_uart_tx_interrupt(); +void enable_uart_rx_interrupt(); +void disable_uart_rx_interrupt(); + +void uart_tx_irq_handler(); +void uart_rx_irq_handler(); + +void uart_async_read(char *buf, int len); +void uart_async_write(const char *s); \ No newline at end of file diff --git a/lab6/include/utils.h b/lab6/include/utils.h new file mode 100644 index 000000000..c2ebcb54b --- /dev/null +++ b/lab6/include/utils.h @@ -0,0 +1,24 @@ +#pragma once + +/** + * @brief Align `n` to be a multiple of 4. + * + * @param n A number + * @return Algined number + */ +int align4(int n); + +int atoi(const char *s); + +/** + * @brief Convert hexadecimal string to int. + * + * @param s: hexadecimal string + * @param n: string length + * @return Converted int number + */ +int hextoi(char *s, int n); + +int memcmp(const void *str1, const void *str2, int n); +void *memcpy(void *dest, const void *src, int n); +void *memset(void *s, int c, int n); \ No newline at end of file diff --git a/lab6/include/virtm.h b/lab6/include/virtm.h new file mode 100644 index 000000000..5b1e8b628 --- /dev/null +++ b/lab6/include/virtm.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "hardware.h" +#include "scheduler.h" + +#define USER_STACK 0x0000FFFFFFFFB000 + +#define PTE_NORMAL_ATTR \ + (PD_ACCESS | PD_UKACCESS | PD_MAIR_NORMAL_NOCACHE | PD_ENTRY) + +void map_pages(uintptr_t pgd, uintptr_t va, uintptr_t size, uintptr_t pa); + +void mapping_user_thread(thread_struct *thread, int gpu_mem_size); \ No newline at end of file diff --git a/lab6/initramfs.cpio b/lab6/initramfs.cpio new file mode 100644 index 000000000..d7eff64c7 Binary files /dev/null and b/lab6/initramfs.cpio differ diff --git a/lab6/sender.sh b/lab6/sender.sh new file mode 100755 index 000000000..501756d76 --- /dev/null +++ b/lab6/sender.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This script sends the kernel image to Rpi3 through UART + +DEST_PATH="/dev/ttyUSB0" +KERNEL_PATH="./kernel8.img" + +# Check the root permission +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +if [ $1 ] +then + DEST_PATH="$1" +fi + +# Get the size of the kernel image file and send it to Rpi3 +# wc -c: count bytes of a file +# sleep: wait n seconds +wc -c < $KERNEL_PATH > $DEST_PATH | sleep 1 + +# Send the kernel image +# pv: redirect file input to specified tty +# add --rate-limit option to limit the speed +pv $KERNEL_PATH > $DEST_PATH diff --git a/lab6/src/command.c b/lab6/src/command.c new file mode 100644 index 000000000..a31d53327 --- /dev/null +++ b/lab6/src/command.c @@ -0,0 +1,186 @@ +#include "command.h" + +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "shell.h" +#include "start.h" +#include "str.h" +#include "syscalls.h" +#include "test.h" +#include "timer.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +typedef struct { + void *ptr; + void *next; +} demo_mem_rec; + +static demo_mem_rec *dmr_list = 0; + +void cmd_info() { + uart_log(INFO, "board revision: "); + uart_hex(BOARD_REVISION); + uart_putc(NEWLINE); + uart_log(INFO, "device base memory address: "); + uart_hex(BASE_MEMORY); + uart_putc(NEWLINE); + uart_log(INFO, "device memory size: "); + uart_hex(NUM_PAGES * PAGE_SIZE); + uart_putc(NEWLINE); +} + +void cmd_hello() { uart_log(INFO, "Hello there!\n"); } + +static void cmd_mem() { + for (int i = BUDDY_MAX_ORDER; i >= 0; i--) { + printFreeListByOrder(i); + } +} + +static void cmd_bd() { + uart_puts("Buddy System: request order (0-"); + uart_dec(BUDDY_MAX_ORDER); + uart_puts("): "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int order = atoi(buf); + if (buf[0] < '0' || buf[0] > '9' || order < 0 || order > BUDDY_MAX_ORDER) { + uart_log(WARN, "Invalid order.\n"); + } else { + demo_mem_rec *dmr = kmalloc(sizeof(demo_mem_rec), 0); + dmr->ptr = kmalloc(PAGE_SIZE << order, 1); + dmr->next = dmr_list; + dmr_list = dmr; + } + kfree(buf, 1); +} + +static void cmd_fm() { + if (dmr_list == 0) { + uart_log(WARN, "No memory block allocated.\n"); + } else { + uart_log(INFO, "Freeing all allocated memory blocks...\n"); + do { + demo_mem_rec *dmr = dmr_list; + kfree(dmr->ptr, 0); + dmr_list = dmr->next; + kfree(dmr, 1); + } while (dmr_list != 0); + } +} + +static void cmd_ca() { + uart_log(INFO, "Dynamic Memory Allocator: request byte(s) (1-"); + uart_dec(PAGE_SIZE / 2); + uart_puts("): "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int size = atoi(buf); + if (buf[0] == '\0' || size <= 0) { + uart_log(WARN, "Invalid size.\n"); + } else { + if (size > PAGE_SIZE / 2) { + uart_log( + WARN, + "Size too large for dynamic allocator. Switching to Buddy System.\n"); + } + demo_mem_rec *dmr = kmalloc(sizeof(demo_mem_rec), 1); + dmr->ptr = kmalloc(size, 0); + dmr->next = dmr_list; + dmr_list = dmr; + } + kfree(buf, 1); +} + +static void cmd_lab() { uart_log(INFO, "Nothing to show in this lab.\n"); } + +static void cmd_timer() { + uart_puts("Duration(sec.): "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + int sec = atoi(buf); + + char *msg = kmalloc(SHELL_BUF_SIZE, 1); + uart_puts(": "); + read_user_input(msg); + set_timer(msg, sec); + kfree(buf, 1); +} + +static void cmd_run() { + // Get filename from user input + uart_puts(": "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + initramfs_run(buf); + kfree(buf, 1); +} + +static void cmd_reboot() { + uart_log(INFO, "Start Rebooting...\n"); + // Reboot after 0x20000 ticks + *PM_RSTC = PM_PASSWORD | 0x20; // Full reset + *PM_WDOG = PM_PASSWORD | 0x20000; // Number of watchdog ticks +} + +static void cmd_cancel() { + uart_log(INFO, "Rebooting Attempt Aborted, if any.\n"); + *PM_RSTC = PM_PASSWORD | 0; + *PM_WDOG = PM_PASSWORD | 0; +} + +static void cmd_cat() { + // Get filename from user input + uart_puts(": "); + char *buf = kmalloc(SHELL_BUF_SIZE, 1); + read_user_input(buf); + initramfs_cat(buf); + kfree(buf, 1); +} + +static void cmd_clear() { uart_clear(); } + +static void cmd_help() { + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); + struct command *cmd = cmd_list; + while (1) { + if (!strcmp(cmd->name, END_OF_COMMAND_LIST)) { + break; + } + uart_puts(cmd->name); + uart_putc(TAB); + uart_puts(": "); + uart_puts(cmd->help); + uart_putc(NEWLINE); + cmd++; + } + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); +} + +struct command cmd_list[] = { + {.name = "help", .help = "Display this help menu", .func = cmd_help}, + {.name = "hello", .help = "Print 'Hello there!'", .func = cmd_hello}, + {.name = "clear", .help = "Clear the screen", .func = cmd_clear}, + {.name = "reboot", .help = "Reboot the device", .func = cmd_reboot}, + {.name = "cancel", .help = "Cancel a scheduled reboot", .func = cmd_cancel}, + {.name = "info", .help = "Show hardware information", .func = cmd_info}, + {.name = "ls", .help = "List files in ramdisk", .func = initramfs_ls}, + {.name = "cat", .help = "Display content of ramdisk file", .func = cmd_cat}, + {.name = "run", .help = "Run a specified program", .func = cmd_run}, + {.name = "timer", .help = "Set timer with duration", .func = cmd_timer}, + // lab4 + {.name = "mem", .help = "Display free memory blocks", .func = cmd_mem}, + {.name = "bd", .help = "Allocate a memory block", .func = cmd_bd}, + {.name = "ca", .help = "Use the dynamic memory allocator", .func = cmd_ca}, + // lab5 + {.name = "fm", .help = "Free all memory in the demo list", .func = cmd_fm}, + {.name = "pid", .help = "List all running threads", .func = list_tcircle}, + // retired + {.name = "lab", .help = "Showcase lab requirements", .func = cmd_lab}, + {.name = END_OF_COMMAND_LIST}}; diff --git a/lab6/src/devtree.c b/lab6/src/devtree.c new file mode 100644 index 000000000..ae3900f9a --- /dev/null +++ b/lab6/src/devtree.c @@ -0,0 +1,61 @@ +#include "devtree.h" + +#include "str.h" +#include "uart.h" +#include "utils.h" + +// Assign a non-zero value to be stored in the .data section +void *DTB_BASE = (void *)0xF; +void *DTB_END = (void *)0xF; + +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]; +} + +void fdt_traverse(void (*callback)(void *, char *)) { + struct fdt_header *header = (struct fdt_header *)(uintptr_t)DTB_BASE; + + // Check the magic number + if (be2le(&(header->magic)) != 0xD00DFEED) { + uart_log(WARN, "Dtb header magic does not match!\n"); + } + DTB_BASE = (void *)TO_VIRT(DTB_BASE); + DTB_END = DTB_BASE + be2le(&header->totalsize); + + uart_log(INFO, "Dtb loaded at "); + uart_hex((uintptr_t)DTB_BASE); + uart_putc('-'); + uart_hex((uintptr_t)DTB_END); + uart_putc(NEWLINE); + + uintptr_t structure = (uintptr_t)header + be2le(&header->off_dt_struct); + uintptr_t strings = (uintptr_t)header + be2le(&header->off_dt_strings); + uint32_t structure_size = be2le(&header->size_dt_struct); + + // Parse the structure block + uintptr_t ptr = structure; // Point to the beginning of structure block + while (ptr < structure + structure_size) { + uint32_t token = be2le((char *)ptr); + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + ptr += align4(strlen((char *)ptr) + 1); + break; + case FDT_PROP: + uint32_t len = be2le((char *)ptr); + ptr += 4; + uint32_t nameoff = be2le((char *)ptr); + ptr += 4; + callback((void *)(uintptr_t)be2le((void *)ptr), + (char *)(strings + nameoff)); + ptr += align4(len); + break; + case FDT_END_NODE: + case FDT_NOP: + case FDT_END: + } + } +} \ No newline at end of file diff --git a/lab6/src/initramfs.c b/lab6/src/initramfs.c new file mode 100644 index 000000000..b41b28859 --- /dev/null +++ b/lab6/src/initramfs.c @@ -0,0 +1,185 @@ +#include "initramfs.h" + +#include + +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "start.h" +#include "str.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +void *initrd_start; +void *initrd_end; + +void initramfs_callback(void *addr, char *property) { + if (!strcmp(property, "linux,initrd-start")) { + uart_log(INFO, "linux,initrd-start: "); + initrd_start = (char *)TO_VIRT(addr); + uart_hex((uintptr_t)initrd_start); + uart_putc(NEWLINE); + } else if (!strcmp(property, "linux,initrd-end")) { + uart_log(INFO, "linux,initrd-end: "); + initrd_end = (char *)TO_VIRT(addr); + uart_hex((uintptr_t)initrd_end); + uart_putc(NEWLINE); + } +} + +static ramfsRec *ramfsNext(char *fptr) { + ramfsRec *rec = kmalloc(sizeof(ramfsRec), 1); + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + rec->namesize = hextoi(header->c_namesize, 8); + rec->filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + rec->headsize = align4(sizeof(cpio_t) + rec->namesize); + rec->datasize = align4(rec->filesize); + + // Get file pathname + rec->pathname = kmalloc(rec->namesize, 1); + strncpy(rec->pathname, fptr + sizeof(cpio_t), rec->namesize); + + return rec; +} + +void initramfs_ls() { + uart_putc(NEWLINE); + + char *fptr = (char *)initrd_start; + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + uart_puts(rec->pathname); + for (int i = 0; i < 15 - strlen(rec->pathname); i++) { + uart_putc(' '); + } + uart_dec(rec->filesize); + uart_puts(" byte"); + if (rec->filesize > 1) uart_putc('s'); + uart_putc(NEWLINE); + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } +} + +void initramfs_cat(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(filename, rec->pathname)) { + // Dump its content + uart_putc(NEWLINE); + for (char *c = fptr + rec->headsize; + c < fptr + rec->headsize + rec->filesize; c++) { + uart_putc(*c); + } + uart_putc(NEWLINE); + kfree(rec->pathname, 1); + kfree(rec, 1); + return; + } + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +void initramfs_run(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + + if (!strcmp(filename, rec->pathname)) { + // Load the user program + disable_interrupt(); + + thread_struct *thread = kcreate_thread(0); + + thread->user_stack = kmalloc(STACK_SIZE, 0); + uart_log(INFO, "Acquired thread user stack: "); + uart_hex((uintptr_t)thread->stack); + uart_putc(NEWLINE); + + void *program = kmalloc(rec->filesize, 0); + uart_log(INFO, "Acquired program space: "); + uart_hex((uintptr_t)(thread->start = program)); + uart_putc(NEWLINE); + memcpy(program, fptr + rec->headsize, thread->size = rec->filesize); + + mapping_user_thread(thread, 0); + + switch_mm((uintptr_t)TO_PHYS(thread->pgd)); + + kfree(rec->pathname, 1); + kfree(rec, 1); + + asm volatile( + "msr tpidr_el1, %0\n" + "msr spsr_el1, %1\n" + "msr elr_el1, %2\n" + "msr sp_el0, %3\n" + "mov sp, %4\n" + "eret\n" + : + : "r"(thread), // 0 thread_struct + "r"(0x340), // 1 + "r"(0x0), // 2 link register + "r"(USER_STACK + STACK_SIZE), // 3 + "r"(thread->stack + STACK_SIZE) // 4 + :); + return; + } + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +// using current thread to run the loaded program +void initramfs_sys_exec(const char *target, trap_frame *tf) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(target, rec->pathname)) { + thread_struct *thread = get_current(); + + void *program = kmalloc(rec->filesize, 0); + uart_log(INFO, "Acquired program space: "); + uart_hex((uintptr_t)(thread->start = program)); + uart_putc(NEWLINE); + memcpy(program, fptr + rec->headsize, thread->size = rec->filesize); + + memset(thread->pgd, 0, PAGE_SIZE); + mapping_user_thread(thread, 0); + + // while (thread->sig_busy); // wait for signal handling + thread->sig_reg = 0; + memset(thread->sig_handlers, 0, sizeof(thread->sig_handlers)); + + kfree(rec->pathname, 1); + kfree(rec, 1); + return; + } + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, 1); + kfree(rec, 1); + } + uart_puts("File not found.\n"); +} \ No newline at end of file diff --git a/lab6/src/irq.c b/lab6/src/irq.c new file mode 100644 index 000000000..eb5e92ad3 --- /dev/null +++ b/lab6/src/irq.c @@ -0,0 +1,96 @@ +#include "irq.h" + +#include "mem.h" +#include "scheduler.h" +#include "timer.h" +#include "uart.h" + +static irqTask *irqTask_head = 0; + +// Disable interrupt before calling irq_add_task() +void irq_add_task(void (*callback)(), int order) { + irqTask *task = kmalloc(sizeof(irqTask), 1); + task->func = callback; + task->order = order; + task->busy = 0; + task->prev = 0; + task->next = 0; + + // 0 -> task -> head -> ... + if (irqTask_head == 0 || task->order < irqTask_head->order) { + task->next = irqTask_head; + task->prev = 0; + if (irqTask_head != 0) irqTask_head->prev = task; + irqTask_head = task; + return; + } + + irqTask *current = irqTask_head; + while (current->next != 0 && current->next->order <= task->order) + current = current->next; + task->next = current->next; + if (current->next != 0) current->next->prev = task; + current->next = task; + task->prev = current; +} + +// to enable interrupts in EL1 +void enable_interrupt() { + asm volatile( + "msr DAIFClr, 0xF\n" // Clear the D, A, I, F bits in the DAIF register + ); +} + +// to disable interrupts in EL1 +void disable_interrupt() { + asm volatile( + "msr DAIFSet, 0xF\n" // Set the D, A, I, F bits to 1 in the DAIF register + ); +} + +void irq_entry(trap_frame *tf) { + disable_interrupt(); // Enter the critical section + + if (*IRQ_PENDING_1 & (1 << 29)) { // UART interrupt + switch (*AUX_MU_IIR & 0x6) { // 0x6 = 0110 -> Get 0x2 and 0x4 + + case 0x2: // 0x2 UART Transmit interrupt + disable_uart_tx_interrupt(); + irq_add_task(uart_tx_irq_handler, ORDER_LAST); + break; + + case 0x4: // 0x4 UART Receive interrupt + disable_uart_rx_interrupt(); + irq_add_task(uart_rx_irq_handler, ORDER_FIRST); + break; + } + } else if (*CORE0_INTERRUPT_SOURCE & 0x2) { + // Core 0 timer interrupt for schedule() + // tell if thread_struct is not the only one in the run_queue + if (get_current() != get_current()->next) schedule(); + + disable_timer_interrupt(); + irq_add_task(timer_irq_handler, ORDER_REGULAR); + } + + enable_interrupt(); // Leave the critical section + + // Preemption: run the task with the highest priority + while (irqTask_head != 0 && !irqTask_head->busy) { + disable_interrupt(); + irqTask *task = irqTask_head; // Get a task from head + task->busy = 1; // Flag the task as under processing + enable_interrupt(); + + task->func(); // Run the tasks with interrupts enabled + + // Remove the task + disable_interrupt(); + if (task->prev != 0) task->prev->next = task->next; + if (task->next != 0) task->next->prev = task->prev; + if (task == irqTask_head) irqTask_head = task->next; + kfree((void *)task, 1); + enable_interrupt(); + } + do_signal(tf); +} \ No newline at end of file diff --git a/lab6/src/linker.ld b/lab6/src/linker.ld new file mode 100644 index 000000000..451d21bfd --- /dev/null +++ b/lab6/src/linker.ld @@ -0,0 +1,14 @@ +SECTIONS +{ + . = 0xFFFF000000000000; + . += 0x80000; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } +} +__bss_size = (__bss_end - __bss_start) >> 3; /* SIZEOF(.bss); */ diff --git a/lab6/src/main.c b/lab6/src/main.c new file mode 100644 index 000000000..af5faedff --- /dev/null +++ b/lab6/src/main.c @@ -0,0 +1,33 @@ +#include "devtree.h" +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "shell.h" +#include "timer.h" +#include "uart.h" + +int main() { + /* Initialization */ + init_uart(); + // uart_log(INFO, "init_uart()\n"); + + uart_log(INFO, "fdt_traverse()\n"); + fdt_traverse(initramfs_callback); + + uart_log(INFO, "init_mem()\n"); + init_mem(); + + uart_log(INFO, "enable_interrupt()\n"); + enable_interrupt(); + + uart_log(INFO, "init_timer()\n"); + init_timer(); + + uart_log(INFO, "sched_init()\n"); + sched_init(); + + run_shell(); + + return 0; +} \ No newline at end of file diff --git a/lab6/src/mbox.c b/lab6/src/mbox.c new file mode 100644 index 000000000..743ecf555 --- /dev/null +++ b/lab6/src/mbox.c @@ -0,0 +1,37 @@ +#include "mbox.h" + +int mbox_call(unsigned char ch, unsigned int *mbox) { + unsigned int r = (unsigned int)((unsigned long)mbox & ~0xF) | (ch & 0xF); + // Wait until we can write to the mailbox + while (*MAILBOX_REG_STATUS & MAILBOX_FULL); + *MAILBOX_REG_WRITE = r; // Write the request + while (1) { + // Wait for the response + while (*MAILBOX_REG_STATUS & MAILBOX_EMPTY); + if (r == *MAILBOX_REG_READ) return mbox[1] == MAILBOX_RESPONSE; + } + return 0; +} + +int get_board_revision(unsigned int *mbox) { + mbox[0] = 7 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_BOARD_REVISION; + mbox[3] = 4; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = END_TAG; + return mbox_call(MAILBOX_CH_PROP, mbox); +} + +int get_arm_memory_status(unsigned int *mbox) { + mbox[0] = 8 * 4; + mbox[1] = REQUEST_CODE; + mbox[2] = TAGS_HARDWARE_ARM_MEM; + mbox[3] = 8; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = 0; + mbox[7] = END_TAG; + return mbox_call(MAILBOX_CH_PROP, mbox); +} \ No newline at end of file diff --git a/lab6/src/mem.c b/lab6/src/mem.c new file mode 100644 index 000000000..451661496 --- /dev/null +++ b/lab6/src/mem.c @@ -0,0 +1,380 @@ +#include "mem.h" + +#include "command.h" +#include "devtree.h" +#include "hardware.h" +#include "mbox.h" +#include "start.h" +#include "str.h" +#include "uart.h" +#include "utils.h" + +extern char *__bss_end; +extern void *DTB_BASE; +extern void *DTB_END; +extern void *initrd_start; +extern void *initrd_end; + +unsigned int BOARD_REVISION; +unsigned long BASE_MEMORY; +unsigned long TOTAL_MEMORY; +unsigned int NUM_PAGES; + +static struct page *pTable; +static struct page *freeList[BUDDY_MAX_ORDER + 1]; +static struct object *objectCache[CACHE_MAX_ORDER + 1]; + +extern char *__bss_end; // `__bss_end` is defined in linker script + +static char *heap_top; + +static void *simple_malloc(int size) { + void *p = (void *)heap_top; + if (size < 0) return 0; + heap_top += size; + return p; +} + +void init_mem() { + // Simple malloc init: Set heap base address + heap_top = (char *)&__bss_end; + uart_log(INFO, "heap_top = "); + uart_hex((uintptr_t)heap_top); + uart_putc(NEWLINE); + uart_log(INFO, "Simple malloc initialized.\n"); + + unsigned int __attribute__((aligned(16))) mbox[36]; + + // Get board revision + if (BOARD_REVISION == 0) { + get_board_revision(mbox); + BOARD_REVISION = mbox[5]; + } + // Get ARM memory base address and size + if (NUM_PAGES == 0) { + get_arm_memory_status(mbox); + BASE_MEMORY = mbox[5]; + TOTAL_MEMORY = mbox[6]; + NUM_PAGES = (TOTAL_MEMORY - BASE_MEMORY) / PAGE_SIZE; + uart_log(INFO, "Initialized pages: "); + uart_dec(NUM_PAGES); + uart_putc(NEWLINE); + } + + // cmd_info(); + + // Initialize the buddy allocator + int pTableSize = sizeof(struct page) * NUM_PAGES; + pTable = simple_malloc(pTableSize); + uart_log(INFO, "Page Table: "); + uart_hex((uintptr_t)pTable); + uart_putc(NEWLINE); + int current_order = BUDDY_MAX_ORDER; + for (int i = 0; i < NUM_PAGES; i++) { + pTable[i].order = 0; + pTable[i].used = 0; + pTable[i].cache_order = -1; + pTable[i].prev = 0; + pTable[i].next = 0; + if (i % (1 << current_order) == 0) { + while (current_order > 0 && NUM_PAGES - i < (1 << current_order)) { + current_order--; + } + pushPageToFreeList(&freeList[current_order], &pTable[i], current_order); + } + } + + // Reserve memory: + // PGD table + reserveMemory(BOOT_PGD_PAGE_FRAME, BOOT_PGD_PAGE_FRAME + PAGE_SIZE); + // PUD table + reserveMemory(BOOT_PUD_PAGE_FRAME, BOOT_PUD_PAGE_FRAME + PAGE_SIZE); + // PMD table + reserveMemory(BOOT_PMD_PAGE_FRAME, BOOT_PMD_PAGE_FRAME + PAGE_SIZE); + // Kernel & stack; page table + reserveMemory(0x80000 - STACK_SIZE, (uintptr_t)&__bss_end + pTableSize); + // Initramfs + reserveMemory((uintptr_t)initrd_start, (uintptr_t)initrd_end); + // Devicetree + reserveMemory((uintptr_t)DTB_BASE, (uintptr_t)DTB_END); +} + +void reserveMemory(uintptr_t resv_start, uintptr_t resv_end) { + // Round the start and end addresses to the page boundary + resv_start = TO_VIRT(resv_start & ~(PAGE_SIZE - 1)); + resv_end = TO_VIRT((resv_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)); + + uart_log(INFO, "Reserving memory: "); + uart_addr_range(resv_start, resv_end); + uart_putc(NEWLINE); + resv_end--; + + for (int order = BUDDY_MAX_ORDER; order >= 0; order--) { + struct page *current = freeList[order]; + while (current != 0) { + struct page *next = current->next; + uint64_t page_start = page_vaddr(current); + uint64_t page_end = page_start + (PAGE_SIZE << order) - 1; + if (page_start >= resv_start && page_end <= resv_end) { + // [page page] + // Remove the page from the free list + current->used = 1; + removePageFromFreeList(&freeList[order], current); + } else if (resv_start > page_end || resv_end < page_start) { + // ---resv> [page or page] or order = order; + page->used = 0; + page->prev = 0; + page->next = 0; + + if (*list_head == 0 || (*list_head) < page) { + if (*list_head != 0) (*list_head)->prev = page; + page->next = *list_head; + *list_head = page; + return; + } + + struct page *current = *list_head; + while (current->next != 0 && page < current->next) { + current = current->next; + } + page->prev = current; + page->next = current->next; + if (current->next != 0) current->next->prev = page; + current->next = page; +} + +struct page *popFreeList(struct page **list_head) { + if (*list_head == 0) return 0; + + struct page *page = *list_head; + *list_head = page->next; + page->used = 1; + return page; +} + +void removePageFromFreeList(struct page **list_head, struct page *page) { + if (page->prev != 0) page->prev->next = page->next; + if (page->next != 0) page->next->prev = page->prev; + if (page == *list_head) *list_head = page->next; +} + +uintptr_t page_vaddr(struct page *p) { + return TO_VIRT((unsigned long)(p - pTable) * PAGE_SIZE); +} + +uintptr_t page_paddr(struct page *p) { + return TO_PHYS((unsigned long)(p - pTable) * PAGE_SIZE); +} + +void printFreeListByOrder(unsigned int order) { + struct page *page = freeList[order]; + if (page > 0) uart_log(BUDD, ""); + while (page != 0) { + uart_putc(TAB); + uart_addr_range(page_vaddr(page), page_vaddr(page + (1 << order))); + uart_puts(" ["); + if (order < 10) uart_putc(' '); + uart_dec(order); + uart_puts("]\n"); + page = page->next; + } +} + +struct page *lookupBuddy(struct page *page, unsigned int order) { + unsigned int buddy_pfn = (unsigned int)(page - pTable) ^ (1 << order); + return &pTable[buddy_pfn]; +} + +struct page *allocatePagesByOrder(unsigned int order, int silent) { + if (!silent) { + uart_log(BUDD, "Memory block requested: "); + uart_dec(1 << order); + uart_puts(" page(s).\n"); + } + + for (int i = order; i <= BUDDY_MAX_ORDER; i++) { + if (freeList[i] == 0) // No free page available + continue; // Try next order + struct page *page = popFreeList(&freeList[i]); + page->order = order; // Update order of the page + + while (i > order) { // requires splitting + i--; + struct page *buddy = lookupBuddy(page, i); + pushPageToFreeList(&freeList[i], buddy, i); + + if (silent) continue; + // Print information + uart_log(BUDD, "Split "); + uart_addr_range(page_vaddr(page), page_vaddr(page + (1 << i))); + uart_puts("//"); + uart_addr_range(page_vaddr(buddy), page_vaddr(buddy + (1 << i))); + uart_puts(" => ["); + uart_dec(i); + uart_puts("]\n"); + } + if (!silent) { + uart_log(BUDD, "Memory allocated: "); + uart_addr_range(page_vaddr(page), page_vaddr(page + (1 << order))); + uart_putc(NEWLINE); + } + + return page; + } + return 0; +} + +void freePages(struct page *page, unsigned int order, int silence) { + if (!silence) { + uart_log(BUDD, "Free "); + uart_dec(1 << order); + uart_puts("-page memory block starting from "); + uart_hex(page_vaddr(page)); + uart_putc(NEWLINE); + } + mergePages(page, order, silence); +} + +void mergePages(struct page *page, unsigned int order, int silence) { + struct page *current = page; + while (order < BUDDY_MAX_ORDER) { + struct page *buddy = lookupBuddy(current, order); + if (buddy->order != order || buddy->used == 1) break; + + removePageFromFreeList(&freeList[order], buddy); + + if (current > buddy) { + struct page *tmp = current; + current = buddy; + buddy = tmp; + } + + order++; + if (silence) continue; + uart_log(BUDD, "Merge "); + uart_addr_range(page_vaddr(current), + page_vaddr(current + (1 << (order - 1)))); + uart_puts("~~"); + uart_addr_range(page_vaddr(buddy), page_vaddr(buddy + (1 << (order - 1)))); + uart_puts(" => ["); + uart_dec(order); + uart_puts("]\n"); + } + pushPageToFreeList(&freeList[order], current, order); +} + +/* Cache Allocator */ + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order) { + object->order = order; + object->next = *list_head; + *list_head = object; +} + +struct object *popObjectFromList(struct object **list_head) { + if (*list_head == 0) return 0; + + struct object *object = *list_head; + *list_head = object->next; + return object; +} + +void *allocateCacheMemory(unsigned int order, int silent) { + if (!silent) { + uart_log(CACH, "Allocating "); + uart_dec(MIN_CACHE_SIZE << order); + uart_puts(" bytes."); + uart_putc(NEWLINE); + } + + if (objectCache[order] == 0) { + struct page *page = allocatePagesByOrder(0, silent); + page->cache_order = order; + uintptr_t page_addr = page_vaddr(page); + int cache_size = MIN_CACHE_SIZE << order; + for (int i = 0; i < PAGE_SIZE; i += cache_size) { + struct object *obj = (struct object *)(uintptr_t)(page_addr + i); + pushObjectToList(&objectCache[order], obj, order); + } + } + void *p = popObjectFromList(&objectCache[order]); + if (!silent) { + uart_log(CACH, "Allocated memory starting from "); + uart_hex((uintptr_t)p); + uart_putc(NEWLINE); + } + return p; +} + +void freeCacheEntry(void *ptr, unsigned int index, int silence) { + if (!silence) { + uart_log(CACH, "Free memory cache of "); + uart_dec(MIN_CACHE_SIZE << index); + uart_puts(" bytes starting from "); + uart_hex((uintptr_t)ptr); + uart_putc(NEWLINE); + } + pushObjectToList(&objectCache[index], ptr, index); +} + +/* Dynamic Memory Allocator */ + +void *kmalloc(unsigned int size, int silent) { + if (size == 0) return 0; + + if (!silent) { + uart_log(INFO, "Memory requested: "); + uart_dec(size); + uart_puts(" byte(s)."); + uart_putc(NEWLINE); + } + + if (size > PAGE_SIZE / 2) { + // Buddy Allocator + int order = 0; + while ((PAGE_SIZE << order) < size) order++; + struct page *page = allocatePagesByOrder(order, silent); + return (void *)page_vaddr(page); + } else { + // Cache Allocator + int power = 0; + while ((1 << power) < size) power++; + int order = (power > 5) ? power - 5 : 0; + return allocateCacheMemory(order, silent); + } +} + +void kfree(void *ptr, int silence) { + // Check if the pointer is page-aligned + struct page *page = &pTable[TO_PHYS(ptr) / PAGE_SIZE]; + if (TO_PHYS(ptr) % PAGE_SIZE == 0) { + // Check if the page is allocated by the buddy allocator + if (page->cache_order == -1) { + // Free the page using the buddy allocator + freePages(page, page->order, silence); + return; + } + } + // Free the object using the cache allocator + struct object *object = ptr; + freeCacheEntry(object, page->cache_order, silence); +} diff --git a/lab6/src/scheduler.S b/lab6/src/scheduler.S new file mode 100644 index 000000000..15fcfbeca --- /dev/null +++ b/lab6/src/scheduler.S @@ -0,0 +1,54 @@ +.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 // set current thread_struct + ret + + /* + struct context_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; + }; + */ + +.global switch_mm +switch_mm: + dsb ish // ensure write has completed + msr ttbr0_el1, x0 // switch translation based address + tlbi vmalle1is // invalidate all TLB entries + dsb ish // ensure completion of TLB invalidation + isb // clear pipeline + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 // current thread_struct + ret + diff --git a/lab6/src/scheduler.c b/lab6/src/scheduler.c new file mode 100644 index 000000000..c5600fb3a --- /dev/null +++ b/lab6/src/scheduler.c @@ -0,0 +1,152 @@ +#include "scheduler.h" + +#include "irq.h" +#include "mem.h" +#include "start.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +static int IDLE_PROCESS_PID; +static int thread_count = 0; +thread_struct *running_tcircle; + +void sched_init() { + uart_log(INFO, "Initializing Scheduler...\n"); + IDLE_PROCESS_PID = kcreate_thread(idle)->pid; + asm volatile( + "msr tpidr_el1, %0" ::"r"(running_tcircle) // Software Thread ID Register + ); + uart_log(INFO, "Scheduler Initialized.\n"); +} + +static void add_thread(thread_struct **queue, thread_struct *thread) { + if (*queue == 0) { + *queue = thread; + thread->next = thread; + thread->prev = thread; + } else { + // prev <- thread -> *queue + thread->next = *queue; + thread->prev = (*queue)->prev; + // prev -> thread <- *queue + (*queue)->prev->next = thread; + (*queue)->prev = thread; + } +} + +thread_struct *kcreate_thread(void (*func)()) { + thread_struct *thread = kmalloc(sizeof(thread_struct), 0); + uart_log(INFO, "Creating thread PID: "); + uart_dec(thread->pid = thread_count++); + uart_putc(NEWLINE); + + thread->start = func ? func : 0; + thread->size = 0; + thread->state = RUNNING; + + thread->stack = kmalloc(STACK_SIZE, 0); + uart_log(INFO, "Acquired thread kernel stack at "); + uart_hex((uintptr_t)thread->stack); + uart_putc(NEWLINE); + thread->user_stack = 0; // alloc when needed (el0) + + memset(thread->sig_handlers, 0, sizeof(thread->sig_handlers)); + thread->sig_reg = 0; + thread->sig_busy = 0; + + // Added for lab6 + memset(thread->pgd = kmalloc(PAGE_SIZE, 0), 0, PAGE_SIZE); + uart_log(INFO, "Acquired thread pgd at "); + uart_hex((uintptr_t)thread->pgd); + uart_putc(NEWLINE); + + thread->context.lr = (unsigned long)func; + thread->context.sp = (unsigned long)thread->stack + STACK_SIZE; + thread->context.fp = (unsigned long)thread->stack + STACK_SIZE; + add_thread(&running_tcircle, thread); + return thread; +} + +static void remove_thread(thread_struct **queue, thread_struct *thread) { + if (*queue == thread) *queue = (thread->next == thread) ? 0 : thread->next; + thread->next->prev = thread->prev; + thread->prev->next = thread->next; +} + +void list_tcircle() { + thread_struct *thread = running_tcircle; + uart_log(INFO, "All thread(s) in queue:\n"); + do { + uart_log(INFO, "pid = "); + uart_dec(thread->pid); + if (thread->sig_handlers[9] > 0) uart_putc('*'); + uart_puts(", sp: "); + uart_hex((uintptr_t)thread->context.sp); + if (thread->pid == get_current()->pid) uart_puts(" <- current"); + uart_putc(NEWLINE); + thread = thread->next; + } while (thread != running_tcircle); +} + +void schedule() { + switch_mm((unsigned long)TO_PHYS(get_current()->next->pgd)); + switch_to(get_current(), get_current()->next); +} + +void kill_zombies() { + thread_struct *next, *thread = running_tcircle; + do { + next = thread->next; + if (thread->state == DEAD) { + remove_thread(&running_tcircle, thread); + kfree(thread->stack, 0); + if (thread->user_stack) kfree(thread->user_stack, 0); + kfree(thread->pgd, 0); + kfree(thread, 0); + } + thread = next; + } while (thread != running_tcircle); +} + +void idle() { + while (1) { + kill_zombies(); + schedule(); + } +} + +thread_struct *get_thread_by_pid(int pid) { + thread_struct *thread = running_tcircle; + do { + if (thread->pid == pid) return thread; + thread = thread->next; + } while (thread != running_tcircle); + return 0; +} + +void kill_current_thread() { + get_current()->state = DEAD; + schedule(); +} + +void kill_thread_by_pid(int pid) { + if (pid == IDLE_PROCESS_PID) { + uart_log(WARN, "Cannot kill idle process.\n"); + } else { + thread_struct *thread = running_tcircle; + do { + if (thread->pid == pid) { + uart_log(INFO, "Killing pid "); + uart_dec(pid); + uart_putc(NEWLINE); + thread->state = DEAD; + schedule(); + return; + } + thread = thread->next; + } while (thread != running_tcircle); + uart_log(WARN, "Nothing to kill.\n"); + } + schedule(); +} diff --git a/lab6/src/shell.c b/lab6/src/shell.c new file mode 100644 index 000000000..cb9aad578 --- /dev/null +++ b/lab6/src/shell.c @@ -0,0 +1,84 @@ +#include "shell.h" + +#include "command.h" +#include "mem.h" +#include "str.h" +#include "uart.h" + +static void welcome_msg() { + cmd_info(); + uart_puts( + "*******************************\n" + "*** YADOS 0.06 for OSC 2024 ***\n" + "*******************************\n" + "Hopefully this will be Yet Another Dope OS!\n"); + uart_putc(NEWLINE); + cmd_hello(); +} + +void run_shell() { + welcome_msg(); + + char *buffer = kmalloc(SHELL_BUF_SIZE, 1); + while (1) { + uart_putc(NEWLINE); + uart_puts("# "); + read_user_input(buffer); + if (exec_command(buffer)) { + uart_log(WARN, "Command not found.\n"); + } + } +} + +void read_user_input(char *buf) { + int idx = 0; + while (idx < SHELL_BUF_SIZE) { + char c = uart_getc(); + switch (c) { + case NEWLINE: + uart_putc(NEWLINE); + buf[idx] = '\0'; + return; + case DELETE: + if (idx > 0) { + idx--; + uart_putc(BACKSPACE); + uart_putc(' '); + uart_putc(BACKSPACE); + } + break; + case ESC: + uart_putc(NEWLINE); + uart_log(WARN, "ESC detected. Clearing buffer...\n"); + uart_log(INFO, "Press Spacebar or Enter to continue.\n"); + while (1) { + char c = uart_getc(); + if (c == ' ' || c == NEWLINE) break; + } + buf[0] = '\0'; + uart_clear(); + return; + default: + if (c >= 32 && c <= 126) { + uart_putc(c); + buf[idx++] = c; + } + } + } + uart_putc(NEWLINE); + uart_log(WARN, "Buffer overflow. Please re-enter your command.\n"); + buf[0] = '\0'; +} + +int exec_command(const char *input) { + if (strlen(input) == 0) return 0; + struct command *cmd = cmd_list; + while (strcmp(cmd->name, END_OF_COMMAND_LIST)) { + if (!strcmp(cmd->name, input)) { + cmd->func(); + return 0; + } + cmd++; + } + return -1; +} \ No newline at end of file diff --git a/lab6/src/signals.c b/lab6/src/signals.c new file mode 100644 index 000000000..a7294b4de --- /dev/null +++ b/lab6/src/signals.c @@ -0,0 +1,56 @@ +#include "signals.h" + +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "traps.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +void signal(int SIGNAL, void (*handler)()) { + get_current()->sig_handlers[SIGNAL] = handler; +} + +void signal_kill(int pid, int SIGNAL) { + thread_struct *thread = get_thread_by_pid(pid); + if (thread == 0) { + uart_log(WARN, "No one gets the signal.\n"); + return; + }; + thread->sig_reg |= 1 << (SIGNAL - 1); // Set the signal pending bit +} + +void do_signal(trap_frame *tf) { + // Prevent nested signal handling + if (get_current()->sig_busy) return; + int sig_num = 1; + while (get_current()->sig_reg) { + if (get_current()->sig_reg & (0x1 << (sig_num - 1))) { + get_current()->sig_busy = 1; // block other signal handling + get_current()->sig_reg &= ~(0x1 << (sig_num - 1)); + + if (get_current()->sig_handlers[sig_num] == 0) { + kill_current_thread(); // Default handler: kill the process + return; + } + + // Save the sigframe + memcpy(get_current()->sig_tf = kmalloc(sizeof(trap_frame), 0), tf, + sizeof(trap_frame)); + get_current()->sig_stack = + kmalloc(STACK_SIZE, 0); // free at sys_sigreturn + + // Map the stack for signal handling + map_pages((unsigned long)get_current()->pgd, SIG_STACK, STACK_SIZE, + (unsigned long)TO_PHYS(get_current()->sig_stack)); + + tf->x30 = (uintptr_t)sigreturn; // Return to sigreturn (traps.S) after + tf->spsr_el1 = 0x340; + tf->elr_el1 = (unsigned long)get_current()->sig_handlers[sig_num]; + tf->sp_el0 = (unsigned long)SIG_STACK + STACK_SIZE; // top of sig_stack + return; // eret to the signal handler (->el0) + } + sig_num++; + } +} \ No newline at end of file diff --git a/lab6/src/start.S b/lab6/src/start.S new file mode 100644 index 000000000..bde3a7215 --- /dev/null +++ b/lab6/src/start.S @@ -0,0 +1,139 @@ +#include + +.section ".text.boot" + +.global _start +_start: + mov x10, x0 // preserve dtb base address + + /* switch from EL2 to EL1 */ + bl from_el2_to_el1 + +/* --- lab6 --- */ + + /* set up kernel page tables */ + ldr x0, =TCR_CONFIG_DEFAULT + msr tcr_el1, x0 // Translation Control Register + + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 // Memory Attribute Indirection Register + + mov x0, BOOT_PGD_PAGE_FRAME // PGD's page frame + mov x1, BOOT_PUD_PAGE_FRAME // PUD's page frame + mov x2, BOOT_PMD_PAGE_FRAME // PMD's page frame + + // PGD + ldr x6, =BOOT_PGD_ATTR + orr x6, x1, x6 // set PGD->PUD entries + str x6, [x0] + + // PUD + ldr x6, =BOOT_PUD_ATTR + orr x6, x2, x6 // set PUD[0]->PMD entries + str x6, [x1] + + ldr x6, =BOOT_PUD_DEVICE_ATTR + mov x3, 0x40000000 + orr x6, x3, x6 // set PUD[1]->MMIO device addr + str x6, [x1, 8] + + /* + +-------------------+ 0x00000000 + | PGD[0]->PUD[0] | PGD bottom + | | + |-------------------| 0x00001000 + | PUD[0]->PMD[0] | PUD bottom + | PUD[1]>>0x40000000| PUD to MMIO device + | | + |-------------------| 0x00002000 + | PMD[0]>>0x0 | PMD bottom + | PMD[1]>>0x200000 | PMD to normal memory + | PMD[2]>>0x400000 | ... + | ... | + | PMD[_]>>0x3F000000| PMD to MMIO device + | ... | ... + | PMD[_]>>0x3FE00000| PMD top + | | + |-------------------| 0x00003000 + | ... | + + */ + + + // PMD + mov x3, 0x00000000 // PMD block bottom + mov x4, 0x3F000000 // MMIO device bottom + mov x5, 0x40000000 // PMD block top + +pmd_loop: + cmp x3, x5 // reach PMD top? + beq enable_mmu // leave the loop + cmp x3, x4 // reach MMIO device bottom? + ldr x6, =BOOT_PMD_DEVICE_ATTR // yes, set device PMD entry + bge set_pmd_entry + ldr x6, =BOOT_PMD_NORMAL_ATTR // no, set normal PMD entry + +set_pmd_entry: + orr x6, x3, x6 + str x6, [x2], #8 // x2 += 8 + add x3, x3, 0x200000 + b pmd_loop + +enable_mmu: // mmu: memory management unit + msr ttbr0_el1, x0 // [no need?]will be modified by each user process + msr ttbr1_el1, x0 // also load PGD to the upper translation based register + mrs x2, sctlr_el1 // System Control Register + orr x2, x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + + ldr x2, =boot_rest // indirect branch to the virtual address + br x2 + +/* --- lab6^^^ --- */ + +boot_rest: + /* get dtb address from x10 */ + ldr x1, =DTB_BASE // defined in devtree.c + str x10, [x1] + + /* get cpu id */ + mrs x1, mpidr_el1 /* Multiprocessor Affinity Register */ + and x1, x1, #3 + cbnz x1, halt // halt if cpu id != 0 + + /* set exception vector table */ + adr x0, exception_vector_table // defined in traps.S + msr vbar_el1, x0 + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main + +halt: + wfe + b halt + +from_el2_to_el1: + mov x0, (1 << 31) + msr hcr_el2, x0 // EL1 uses aarch64 + mov x0, 0x3C5 // ... 0011 1100 0101 + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 diff --git a/lab6/src/str.c b/lab6/src/str.c new file mode 100644 index 000000000..e8d54f053 --- /dev/null +++ b/lab6/src/str.c @@ -0,0 +1,30 @@ +#include "str.h" + +int strcmp(const char *str1, const char *str2) { + while (*str1 && *str1 == *str2) str1++, str2++; + return *(unsigned char *)str1 - *(unsigned char *)str2; +} + +char *strncpy(char *dest, const char *src, int n) { + while (n-- && (*dest++ = *src++)); + return dest; +} + +int strlen(const char *str) { + int len = 0; + while (*str++ != '\0') len++; + return len; +} + +char *strcat(char *dest, const char *src) { + char *d = dest; + + // Move the pointer to the end of the dest string + while (*d != '\0') d++; + // Copy the src string to the end of the dest string + while (*src != '\0') *d++ = *src++; + // Add the null terminator + *d = '\0'; + + return dest; +} diff --git a/lab6/src/syscalls.c b/lab6/src/syscalls.c new file mode 100644 index 000000000..4c735a6be --- /dev/null +++ b/lab6/src/syscalls.c @@ -0,0 +1,99 @@ +#include "syscalls.h" + +#include "initramfs.h" +#include "irq.h" +#include "mbox.h" +#include "mem.h" +#include "scheduler.h" +#include "str.h" +#include "traps.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +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_putc(buf[i++]); + return i; + return size; +} + +int sys_exec(const char *name, trap_frame *tf) { + initramfs_sys_exec(name, tf); + return 0; +} + +int sys_fork(trap_frame *tf) { + disable_interrupt(); // prevent schedule premature fork + + thread_struct *parent = get_current(); + thread_struct *child = kcreate_thread(0); + + child->start = parent->start; + uart_log(INFO, "parent->start: "); + uart_hex((uintptr_t)parent->start); + uart_putc(NEWLINE); + + child->size = parent->size; + uart_log(INFO, "parent->size: "); + uart_hex(parent->size); + uart_putc(NEWLINE); + + child->user_stack = kmalloc(STACK_SIZE, 1); + uart_log(INFO, "Acquired thread user stack: "); + uart_hex((uintptr_t)child->stack); + uart_putc(NEWLINE); + + mapping_user_thread(child, 0x4000000); + + // Handling kernel stack (incl. tf from parent) + memcpy(child->stack, parent->stack, STACK_SIZE); + memcpy(child->user_stack, parent->user_stack, STACK_SIZE); + + // Copy signal handlers + memcpy(child->sig_handlers, parent->sig_handlers, + sizeof(parent->sig_handlers)); + + // get child's trap frame from kstack via parent's offset + size_t ksp_offset = (uintptr_t)tf - (uintptr_t)parent->stack; + trap_frame *child_tf = (trap_frame *)(child->stack + ksp_offset); + + child->context.lr = (uintptr_t)child_ret_from_fork; // traps.S + child->context.sp = (uintptr_t)child_tf; // set child's ksp + child->context.fp = (uintptr_t)child_tf; // set child's ksp + child_tf->sp_el0 = tf->sp_el0; // set child's user sp + child_tf->x0 = 0; // ret value of fork() for child + + enable_interrupt(); + return child->pid; +} + +void sys_exit(int status) { + uart_log(status ? WARN : INFO, "Exiting process ...\n"); + kill_current_thread(); +} + +int sys_mbox_call(unsigned char ch, unsigned int *mbox) { + unsigned int *itm = kmalloc(mbox[0], 1); + memcpy(itm, mbox, mbox[0]); + int ret = mbox_call(ch, (unsigned int *)itm); + memcpy(mbox, itm, mbox[0]); + kfree(itm, 1); + return ret; +} + +void sys_sigreturn(trap_frame *tf) { + memcpy(tf, get_current()->sig_tf, sizeof(trap_frame)); + kfree(get_current()->sig_tf, 1); + kfree(get_current()->sig_stack, 1); + get_current()->sig_busy = 0; + return; +} diff --git a/lab6/src/timer.c b/lab6/src/timer.c new file mode 100644 index 000000000..a2ccfa40a --- /dev/null +++ b/lab6/src/timer.c @@ -0,0 +1,114 @@ +#include "timer.h" + +#include "mem.h" +#include "str.h" +#include "uart.h" + +static timerEntry *head = 0; + +void init_timer() { + asm volatile( + "mov x0, 1\n" + "msr cntp_ctl_el0, x0\n"); + + uart_log(INFO, "enable_timer_interrupt()\n"); + enable_timer_interrupt(); + + asm volatile( + "mrs x0, cntfrq_el0\n" + "msr cntp_tval_el0, x0\n"); + + // required by lab5 + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); +} + +void enable_timer_interrupt() { + // Unmask the timer interrupt + asm volatile( + "mov x0, 2\n" + "ldr x1, =%0\n" + "str w0, [x1]\n" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void disable_timer_interrupt() { + // Mask timer interrupt + asm volatile( + "mov x0, 0\n" + "ldr x1, =%0\n" + "str w0, [x1]\n" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void timer_irq_handler() { + // Set up the next timer interrupt (frequency >> 5, aka. 1/32 of the + // frequency) + asm volatile( + "mrs x0, cntfrq_el0\n" + "lsr x0, x0, %0\n" + "msr cntp_tval_el0, x0\n" + : + : "i"(TIMER_SPEED) + : "x0"); + + // Check the timer queue + while (head != 0 && timer_get_uptime() >= head->time) { + head->func(head->arg); // Execute the callback function + kfree(head, 1); // Free the memory allocated for the timer node + head = head->next; // Remove the head node after func finished + } + + enable_timer_interrupt(); +} + +uint64_t timer_get_uptime() { + uint64_t cntpct_el0 = 0, cntfrq_el0 = 0; + asm volatile( + "mrs %0, cntpct_el0\n" + "mrs %1, cntfrq_el0\n" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + return cntpct_el0 / cntfrq_el0; +} + +void timer_add(void (*callback)(void *), void *arg, int duration) { + // Insert a new timer into the linked list (sorted by time) + timerEntry *timer = kmalloc(sizeof(timerEntry), 1); + timer->func = callback; + timer->arg = arg; + timer->time = timer_get_uptime() + duration; + timer->next = 0; + + if (head == 0 || timer->time < head->time) { + // Insert as the head of the list + timer->next = head; + head = timer; + return; + } + + timerEntry *current = head; + while (current->next != 0 && current->next->time <= timer->time) + current = current->next; + timer->next = current->next; + current->next = timer; +} + +static void show_timer_message(void *arg) { + uart_putc(NEWLINE); + uart_log(INFO, "Timer message: "); + uart_puts((char *)arg); + uart_putc(NEWLINE); + kfree(arg, 1); // Free the memory allocated for arg +} + +void set_timer(const char *msg, int duration) { + timer_add((void (*)(void *))show_timer_message, (void *)msg, duration); +} + +void set_timeup(int *timeup) { *timeup = 1; } diff --git a/lab6/src/traps.S b/lab6/src/traps.S new file mode 100644 index 000000000..f0bfd215c --- /dev/null +++ b/lab6/src/traps.S @@ -0,0 +1,117 @@ +/* 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 // Saved Program Status Register (EL1) + mrs x11, elr_el1 // Exception Link Register (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 // pass the stack pointer to exception_entry + 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 // Exception Link Register (EL1) + mrs x1, esr_el1 // ESR_EL1, Exception Syndrome Register (EL1) + mrs x2, spsr_el1 // Saved Program Status Register (EL1) + mrs x3, ttbr0_el1 // Translation Table Base Register 0 (EL1) + mrs x4, ttbr1_el1 // Translation Table Base Register 1 (EL1) + mrs x5, far_el1 // Fault Address Register, EL1 + bl invalid_entry // defined in traps.c + load_all + eret + +.global child_ret_from_fork +child_ret_from_fork: + load_all // child's kernel stack (trap frame) + eret + +.global sigreturn +sigreturn: + mov x8, 139 + svc 0 + +.macro v_entry 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 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL1 -> EL1 while using SP_EL1 + v_entry invalid_exc_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch64) -> EL1 + v_entry exception_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch32) -> EL1 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler diff --git a/lab6/src/traps.c b/lab6/src/traps.c new file mode 100644 index 000000000..ec1b82cb8 --- /dev/null +++ b/lab6/src/traps.c @@ -0,0 +1,163 @@ +#include "traps.h" + +#include + +#include "irq.h" +#include "mbox.h" +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "uart.h" +#include "virtm.h" + +extern thread_struct *running_tcircle; + +static void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr, + uint64_t ttbr0_el1, uint64_t ttbr1_el1, + uint64_t far_el1) { + // Print spsr_el1 + uart_log(INFO, "spsr_el1: "); + uart_hex(spsr); + uart_putc(NEWLINE); + + // Print elr_el1 + uart_log(INFO, "elr_el1: "); + uart_hex(elr); + uart_putc(NEWLINE); + + // Print esr_el1 + uart_log(INFO, "esr_el1: "); + uart_hex(esr); + uart_putc(NEWLINE); + + // Print ttbr0_el1 + uart_log(INFO, "ttbr0_el1: "); + uart_hex(ttbr0_el1); + uart_putc(NEWLINE); + + // Print ttbr1_el1 + uart_log(INFO, "ttbr1_el1: "); + uart_hex(ttbr1_el1); + uart_putc(NEWLINE); + + // Print far_el1 + uart_log(INFO, "far_el1: "); + uart_hex(far_el1); + uart_putc(NEWLINE); +} + +void exception_entry(trap_frame *tf) { + unsigned long elr, esr, spsr, ttbr0_el1, ttbr1_el1, far_el1; + asm volatile( + "mrs %0, elr_el1\n" + "mrs %1, esr_el1\n" + "mrs %2, spsr_el1\n" + "mrs %3, ttbr0_el1\n" + "mrs %4, ttbr1_el1\n" + "mrs %5, far_el1\n" + : "=r"(elr), // + "=r"(esr), // + "=r"(spsr), // + "=r"(ttbr0_el1), // + "=r"(ttbr1_el1), // + "=r"(far_el1) // + : + : "memory"); + if (esr != 0x56000000) { + print_registers(elr, esr, spsr, ttbr0_el1, ttbr1_el1, far_el1); + while (1); + } + + enable_interrupt(); // Prevent uart blocking the interrupt[?] + + unsigned int syscall_num = tf->x8; + // if (syscall_num > 2) { + // uart_log(INFO, "syscall_num: "); + // uart_dec(syscall_num); + // uart_putc(NEWLINE); + // } + switch (syscall_num) { + case 0: { // int getpid() + unsigned int test = tf->x7; + unsigned int pid = sys_getpid(); + tf->x0 = pid; + if (test != 255) list_tcircle(); + break; + } + case 1: { // size_t uartread(char buf[], size_t size) + char *buf = (char *)tf->x0; + size_t size = tf->x1; + tf->x0 = sys_uart_read(buf, size); + break; + } + case 2: { // size_t uartwrite(const char buf[], size_t size) + char *buf = (char *)tf->x0; + size_t size = tf->x1; + tf->x0 = sys_uart_write(buf, size); + } break; + case 3: // int exec(const char *name, char *const argv[]) + const char *name = (char *)tf->x0; + tf->x0 = sys_exec(name, tf); + tf->elr_el1 = 0x0; + tf->sp_el0 = USER_STACK + STACK_SIZE; + break; + case 4: { // int fork() + tf->x0 = sys_fork(tf); + break; + } + case 5: { // void exit(int status) + int status = tf->x0; + sys_exit(status); + break; + } + case 6: { // int mbox_call(unsigned char ch, unsigned int *mbox) + unsigned char ch = tf->x0; + unsigned int *mbox = (unsigned int *)tf->x1; + tf->x0 = sys_mbox_call(ch, mbox); + break; + } + case 7: { // void kill(int pid) + unsigned int pid = tf->x0; + kill_thread_by_pid(pid); + break; + } + case 8: { // signal(int SIGNAL, void (*handler)()) + unsigned int SIGNAL = tf->x0; + void (*handler)() = (void (*)())tf->x1; + signal(SIGNAL, handler); + break; + } + case 9: { // (signal_)kill(int pid, int SIGNAL) + unsigned int pid = tf->x0; + unsigned int SIGNAL = tf->x1; + uart_log(INFO, "Signal "); + uart_dec(SIGNAL); + uart_puts(" sent to pid "); + uart_dec(pid); + uart_putc(NEWLINE); + signal_kill(pid, SIGNAL); + break; + } + case 139: + sys_sigreturn(tf); + break; + default: + uart_log(ERR, "Invalid system call\n"); + } +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr, + uint64_t ttbr0_el1, uint64_t ttbr1_el1, uint64_t far_el1) { + uart_log(ERR, "The exception handler is not implemented\n"); + print_registers(elr, esr, spsr, ttbr0_el1, ttbr1_el1, far_el1); + + if ((esr & 0xFC000000) == 0x94000000) { + uint32_t svc_number = esr & 0xFFFF; + uart_log(ERR, "SVC Call Exception. SVC Number: "); + uart_dec(svc_number); + uart_putc(NEWLINE); + } else { + uart_log(ERR, "Unknown Exception\n"); + } + while (1); +} \ No newline at end of file diff --git a/lab6/src/uart.c b/lab6/src/uart.c new file mode 100644 index 000000000..c2611837b --- /dev/null +++ b/lab6/src/uart.c @@ -0,0 +1,198 @@ +#include "uart.h" + +#include "irq.h" +#include "mem.h" + +static int uart_read_idx = 0; +static int uart_write_idx = 0; +static char uart_read_buffer[UART_BUF_SIZE]; +static char uart_write_buffer[UART_BUF_SIZE]; + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + // Disable GPIO pull up/down + *GPPUD = 0; + for (int i = 0; i < 150; i++); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + // Initialize mini UART + *AUX_ENABLE |= 1; // Enable mini UART + *AUX_MU_CNTL = 0; // Disable Tx and Rx during setup + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set data size to 8 bits + *AUX_MU_MCR = 0; // Disable auto flow control + *AUX_MU_BAUD = 270; // Set baud rate to 115200 + *AUX_MU_IIR = 6; // No FIFO + *AUX_MU_CNTL = 3; // Enable Tx and Rx + + // Enable AUX interrupts + *ENABLE_IRQS_1 |= 1 << 29; +} + +char uart_getc() { + // Check the data ready field on bit 0 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); // Read from AUX_MU_IO_REG + return c == CARRIAGE_RETURN ? NEWLINE : c; +} + +void uart_putc(char c) { + // Add CARRIAGE_RETURN before NEWLINE + if (c == NEWLINE) uart_putc(CARRIAGE_RETURN); + + // Check the transmitter empty field on bit 5 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = c; // Write to AUX_MU_IO_REG +} + +void uart_puts(const char *s) { + while (*s) { + uart_putc(*s++); + } +} + +void uart_clear() { uart_puts("\033[2J\033[H"); } + +void uart_hex(unsigned long h) { + uart_puts("0x"); + unsigned long n; + int length = h > 0xFFFFFFFF ? 60 : 28; + for (int c = length; c >= 0; c -= 4) { + n = (h >> c) & 0xF; + n += n > 9 ? 0x37 : '0'; + uart_putc(n); + if (c / 4 % 8 == 0 && c > 0) uart_putc(' '); + } +} + +void uart_addr_range(uintptr_t start, uintptr_t end) { + uart_hex(start); + uart_puts("-"); + uart_hex(end - 1); +} + +void uart_simple_hex(unsigned int h) { + uart_puts("0x"); + if (h == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + unsigned int n; + while (h > 0) { + n = h % 16; + buf[i++] = n + (n > 9 ? 0x37 : '0'); + h /= 16; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_dec(unsigned int d) { + if (d == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + while (d > 0) { + buf[i++] = d % 10 + '0'; + d /= 10; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_log(int type, const char *msg) { + switch (type) { + case INFO: + uart_puts("[INFO] "); + break; + case TEST: + uart_puts("[TEST] "); + break; + case BUDD: + uart_puts("[BUDD] "); + break; + case CACH: + uart_puts("[CACH] "); + break; + case WARN: + uart_puts("[WARN] "); + break; + case ERR: + uart_puts("[ERR!] "); + break; + default: + uart_puts("[LOG?] "); + break; + } + uart_puts(msg); +} + +void enable_uart_tx_interrupt() { *AUX_MU_IER |= 0x02; } + +void disable_uart_tx_interrupt() { *AUX_MU_IER &= 0x01; } + +void enable_uart_rx_interrupt() { *AUX_MU_IER |= 0x01; } + +void disable_uart_rx_interrupt() { *AUX_MU_IER &= 0x2; } + +void uart_tx_irq_handler() { + disable_uart_tx_interrupt(); + // Buffer is not empty -> send characters + if (uart_write_idx < UART_BUF_SIZE && + uart_write_buffer[uart_write_idx] != 0) { + while (!(*AUX_MU_LSR & 0x20)); + *AUX_MU_IO = uart_write_buffer[uart_write_idx++]; + enable_uart_tx_interrupt(); + } +} + +void uart_rx_irq_handler() { + while (!(*AUX_MU_LSR & 0x01)); + char c = (char)(*AUX_MU_IO); + uart_read_buffer[uart_read_idx++] = (c == CARRIAGE_RETURN) ? NEWLINE : c; + if (uart_read_idx >= UART_BUF_SIZE) uart_read_idx = 0; + enable_uart_rx_interrupt(); +} + +void uart_async_read(char *buf, int len) { + disable_uart_rx_interrupt(); + for (int i = 0; i < uart_read_idx && i < len; i++) + buf[i] = uart_read_buffer[i]; + buf[uart_read_idx] = 0; + uart_read_idx = 0; +} + +void uart_async_write(const char *s) { + // Copy string to the write buffer + int len = 0; + while (*s != '\0') { + if (len >= UART_BUF_SIZE) { + uart_log(ERR, "Exceed the UART buffer size\n"); + return; + } + if (*s == NEWLINE) { + // Insert CARRIAGE_RETURN before NEWLINE + uart_write_buffer[len++] = CARRIAGE_RETURN; + uart_write_buffer[len++] = NEWLINE; + } else + uart_write_buffer[len++] = *s; + s++; + } + uart_write_buffer[len] = '\0'; + uart_write_idx = 0; // Reset the buffer index + enable_uart_tx_interrupt(); +} diff --git a/lab6/src/utils.c b/lab6/src/utils.c new file mode 100644 index 000000000..dec35665b --- /dev/null +++ b/lab6/src/utils.c @@ -0,0 +1,71 @@ +#include "utils.h" + +int align4(int n) { return n + (4 - n % 4) % 4; } + +int atoi(const char *s) { + int result = 0; + int sign = 1; + int i = 0; + + // Skip leading spaces + while (s[i] == ' ') { + i++; + } + + // Handle positive and negative sign + if (s[i] == '-') { + sign = -1; + i++; + } else if (s[i] == '+') { + i++; + } + + // Convert string to integer + while (s[i] >= '0' && s[i] <= '9') { + result = result * 10 + (s[i] - '0'); + i++; + } + + return sign * result; +} + +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; +} + +int memcmp(const void *m1, const void *m2, int n) { + const unsigned char *a = m1, *b = m2; + while (n-- > 0) { + if (*a != *b) return *a - *b; + a++; + b++; + } + return 0; +} + +/** + * @brief Copy `n` bytes from `src` to `dest`. + */ +void *memcpy(void *dest, const void *src, int n) { + char *d = dest; + const char *s = src; + while (n--) *d++ = *s++; + return dest; +} + +/** + * @brief Set `n` bytes of `s` to `c`. + */ +void *memset(void *s, int c, int n) { + unsigned char *p = s; + while (n--) *p++ = (unsigned char)c; + return s; +} \ No newline at end of file diff --git a/lab6/src/virtm.c b/lab6/src/virtm.c new file mode 100644 index 000000000..5f3fec0ee --- /dev/null +++ b/lab6/src/virtm.c @@ -0,0 +1,46 @@ +#include "virtm.h" + +#include "mem.h" +#include "start.h" +#include "uart.h" +#include "utils.h" + +extern unsigned long TOTAL_MEMORY; + +static void walk(uintptr_t pt, uintptr_t va, uintptr_t pa) { + uintptr_t *table = (uintptr_t *)pt; + for (int level = 0; level <= 3; level++) { + uintptr_t offset = (va >> (39 - 9 * level)) & 0x1FF; + if (level == 3) { + table[offset] = pa | PTE_NORMAL_ATTR; + return; + } + if (!table[offset]) { + uintptr_t *t = kmalloc(PAGE_SIZE, 0); + uart_log(INFO, "Acquired page table at "); + uart_hex((uintptr_t)t); + uart_puts(" for level "); + uart_dec(level); + uart_putc(NEWLINE); + memset(t, 0, PAGE_SIZE); + table[offset] = TO_PHYS((uintptr_t)t) | PD_TABLE; + } + table = (uintptr_t *)TO_VIRT((table[offset] & ~0xFFF)); + } +} + +void map_pages(uintptr_t pgd, uintptr_t va, uintptr_t size, uintptr_t pa) { + for (int i = 0; i < size; i += PAGE_SIZE) walk(pgd, va + i, pa + i); +} + +void mapping_user_thread(thread_struct *thread, int gpu_mem_size) { + // user code + map_pages((uintptr_t)thread->pgd, 0x0, thread->size, + (uintptr_t)TO_PHYS(thread->start)); + // stack region + map_pages((uintptr_t)thread->pgd, USER_STACK, STACK_SIZE, + (uintptr_t)TO_PHYS(thread->user_stack)); + // GPU + if (gpu_mem_size > 0) + map_pages((uintptr_t)thread->pgd, 0x3C000000, gpu_mem_size, 0x3C000000); +} \ No newline at end of file diff --git a/lab7/.gitignore b/lab7/.gitignore new file mode 100644 index 000000000..36e2308e8 --- /dev/null +++ b/lab7/.gitignore @@ -0,0 +1,7 @@ +.vscode +build +rootfs +temp +dump.* +*.img +*.dtb diff --git a/lab7/Makefile b/lab7/Makefile new file mode 100644 index 000000000..c6dde9a1a --- /dev/null +++ b/lab7/Makefile @@ -0,0 +1,41 @@ +ARMGNU ?= aarch64-linux-gnu + +CFLAGS = -Iinclude -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +ASMFLAGS = -Iinclude +QEMUFLAGS = -M raspi3b -display none -serial null -serial stdio +QEMUFLAGSDISP = -M raspi3b -serial null -serial stdio + +SRC_DIR = src +BUILD_DIR = build + +all: clean kernel8.img + +clean: + rm -rf $(BUILD_DIR) *.img + +build: + mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/_%.o: $(SRC_DIR)/%.S | $(BUILD_DIR) + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(SRC_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/_%.o) + +kernel8.img: $(SRC_DIR)/linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/kernel8.elf kernel8.img + +qemu: all initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear & qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +dpqemu: all initramfs.cpio bcm2710-rpi-3-b-plus.dtb + clear & qemu-system-aarch64 $(QEMUFLAGSDISP) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug: all initramfs.cpio bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 $(QEMUFLAGS) -kernel kernel8.img -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -S -s -d int \ No newline at end of file diff --git a/lab7/bootloader/Makefile b/lab7/bootloader/Makefile new file mode 100644 index 000000000..d7882aa27 --- /dev/null +++ b/lab7/bootloader/Makefile @@ -0,0 +1,19 @@ +ARMGNU ?= aarch64-linux-gnu +CFLAGS = -nostdlib -nostartfiles -ffreestanding -mgeneral-regs-only -Wall -g +QEMUFLAGS = -M raspi3b -display none -serial null -serial pty + +all: clean bootloader.img + +clean: + rm -rf build *.img + +bootloader.img: + mkdir -p build + $(ARMGNU)-gcc $(CFLAGS) -c main.c -o build/main.o + $(ARMGNU)-gcc $(CFLAGS) -c start.S -o build/start.o + $(ARMGNU)-gcc $(CFLAGS) -c boot.c -o build/boot.o + $(ARMGNU)-ld -T linker.ld -o build/bootloader.elf build/main.o build/start.o build/boot.o + $(ARMGNU)-objcopy -O binary build/bootloader.elf bootloader.img + +qemu: clean bootloader.img + qemu-system-aarch64 $(QEMUFLAGS) -kernel bootloader.img -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/lab7/bootloader/boot.c b/lab7/bootloader/boot.c new file mode 100644 index 000000000..d4ff535f5 --- /dev/null +++ b/lab7/bootloader/boot.c @@ -0,0 +1,61 @@ +#include "boot.h" + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + *GPPUD = 0; + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++) asm volatile("nop"); + *GPPUD = 0; + *GPPUDCLK0 = 0; + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +char uart_recv() { + while (!(*AUX_MU_LSR & 0x01)) asm volatile("nop"); + return (char)(*AUX_MU_IO); +} + +void uart_putc(char c) { + if (c == '\n') uart_putc('\r'); + while (!(*AUX_MU_LSR & 0x20)) asm volatile("nop"); + *AUX_MU_IO = c; +} + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} + +int atoi(const char *s) { + int result = 0; + 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 * result; +} \ No newline at end of file diff --git a/lab7/bootloader/boot.h b/lab7/bootloader/boot.h new file mode 100644 index 000000000..84cece020 --- /dev/null +++ b/lab7/bootloader/boot.h @@ -0,0 +1,49 @@ +#ifndef BOOT_H +#define BOOT_H + +/* ==================== GPIO ==================== */ +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) + +/* ==================== UART ==================== */ +#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 init_uart(); +char uart_recv(); +void uart_putc(char c); +void uart_puts(const char *s); + +/* ==================== UTILS ==================== */ +int atoi(const char *s); + +#endif // BOOT_H \ No newline at end of file diff --git a/lab7/bootloader/linker.ld b/lab7/bootloader/linker.ld new file mode 100644 index 000000000..556816b11 --- /dev/null +++ b/lab7/bootloader/linker.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + . = 0x60000; + __loader_start = .; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } + __loader_end = .; +} +__bss_size = SIZEOF(.bss); +__loader_size = __loader_end - __loader_start; \ No newline at end of file diff --git a/lab7/bootloader/main.c b/lab7/bootloader/main.c new file mode 100644 index 000000000..cc8faa0fe --- /dev/null +++ b/lab7/bootloader/main.c @@ -0,0 +1,41 @@ +#include "boot.h" + +int main() { + init_uart(); + uart_puts("\033[2J\033[H"); + uart_puts( + "UART Bootloader\n" + "Waiting for kernel...\n"); + + // Get kernel image size + char buf[16] = {0}; + for (int i = 0; i < 16; i++) { + buf[i] = uart_recv(); + if (buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + + // Load kernel image + uart_puts("Kernel size: "); + uart_puts(buf); + uart_puts(" bytes.\n"); + uart_puts("Loading the kernel image...\n"); + + unsigned int size = atoi(buf); + char *kernel = (char *)0x80000; + while (size--) *kernel++ = uart_recv(); + + // Restore registers x0 x1 x2 x3 + asm volatile( + "mov x0, x10\n" + "mov x1, x11\n" + "mov x2, x12\n" + "mov x3, x13\n" + "mov x30, 0x80000\n" + "ret\n" // Jump to the new kernel + ); + + return 0; +} \ No newline at end of file diff --git a/lab7/bootloader/start.S b/lab7/bootloader/start.S new file mode 100644 index 000000000..ec8488dde --- /dev/null +++ b/lab7/bootloader/start.S @@ -0,0 +1,40 @@ +.section ".text.boot" + +.global _start + +_start: + /* save registers x0 x1 x2 x3 */ + mov x10, x0 /* dtb_base address */ + mov x11, x1 + mov x12, x2 + mov x12, x3 + + /* relocate bootloader */ + ldr x1, =0x80000 + ldr x2, =__loader_start // 0x60000 + ldr w3, =__loader_size + +relocate: + ldr x4, [x1], #8 + str x4, [x2], #8 + sub w3, w3, #1 + cbnz w3, relocate + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main-0x20000 + b run_main diff --git a/lab7/include/command.h b/lab7/include/command.h new file mode 100644 index 000000000..b48238d64 --- /dev/null +++ b/lab7/include/command.h @@ -0,0 +1,19 @@ +#pragma once + +#define MAX_BUF_SIZE 1024 +#define END_OF_COMMAND_LIST "NULL" + +struct command { + const char *name; + const char *help; + void (*func)(void); +}; + +extern struct command cmd_list[]; +extern unsigned int BOARD_REVISION; +extern unsigned long BASE_MEMORY; +extern unsigned int NUM_PAGES; + +// Commands +void cmd_info(); +void cmd_hello(); \ No newline at end of file diff --git a/lab7/include/devtree.h b/lab7/include/devtree.h new file mode 100644 index 000000000..0a1e5bf65 --- /dev/null +++ b/lab7/include/devtree.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "hardware.h" + +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; +}; + +/** + * @brief Convert a 4-byte big-endian sequence to little-endian. + * + * @param s: big-endian sequence + * @return little-endian sequence + */ +uint32_t be2le(const void *s); + +void fdt_traverse(void (*callback)(void *, char *)); diff --git a/lab7/include/hardware.h b/lab7/include/hardware.h new file mode 100644 index 000000000..c3714e381 --- /dev/null +++ b/lab7/include/hardware.h @@ -0,0 +1,209 @@ +#pragma once + +#include "start.h" + +#define VM 0xFFFF000000000000 + +#define TO_VIRT(x) ((uintptr_t)(x) | VM) +#define TO_PHYS(x) ((uintptr_t)(x) & ~VM) + +#define MMIO_BASE TO_VIRT(0x3F000000) + +// ARM Interrupt Registers + +#define IRQ_BASIC_PENDING (volatile unsigned int *)(MMIO_BASE + 0x0000B200) +#define IRQ_PENDING_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B204) +#define IRQ_PENDING_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B208) +#define FIQ_CONTROL (volatile unsigned int *)(MMIO_BASE + 0x0000B20C) +#define ENABLE_IRQS_1 (volatile unsigned int *)(MMIO_BASE + 0x0000B210) +#define ENABLE_IRQS_2 (volatile unsigned int *)(MMIO_BASE + 0x0000B214) +#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) + +#define CORE0_INTERRUPT_SOURCE (volatile unsigned int *)TO_VIRT(0x40000060) + +// Timers interrupt control registers + +#define CORE0_TIMER_IRQCNTL TO_VIRT(0x40000040) +#define CORE1_TIMER_IRQCNTL TO_VIRT(0x40000044) +#define CORE2_TIMER_IRQCNTL TO_VIRT(0x40000048) +#define CORE3_TIMER_IRQCNTL TO_VIRT(0x4000004C) + +// Where to route timer interrupt to, IRQ/FIQ +// Setting both the IRQ and FIQ bit gives an FIQ +#define TIMER0_IRQ 0x01 +#define TIMER1_IRQ 0x02 +#define TIMER2_IRQ 0x04 +#define TIMER3_IRQ 0x08 +#define TIMER0_FIQ 0x10 +#define TIMER1_FIQ 0x20 +#define TIMER2_FIQ 0x40 +#define TIMER3_FIQ 0x80 + +// PASSWORD + +#define PM_PASSWORD 0x5A000000 +#define PM_RSTC (volatile unsigned int *)(MMIO_BASE + 0x0010001C) +#define PM_WDOG (volatile unsigned int *)(MMIO_BASE + 0x00100024) +// #define PM_RSTC (volatile unsigned int *)0x3F10001C +// #define PM_WDOG (volatile unsigned int *)0x3F100024 + +// DEVICETREE + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +// GPIO + +#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 GPCLR1 ((volatile unsigned int *)(MMIO_BASE + 0x0020002C)) +#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)) +// Skip GPRENn, GPFENn +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +// Skip GPLENn, GPARENn +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +// UART + +#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)) + +// MAILBOX + +#define MAILBOX_BASE (MMIO_BASE + 0x0000B880) + +// mailbox register +#define MAILBOX_REG_READ ((volatile unsigned int *)(MAILBOX_BASE + 0x00000000)) +#define MAILBOX_REG_STATUS \ + ((volatile unsigned int *)(MAILBOX_BASE + 0x00000018)) +#define MAILBOX_REG_WRITE ((volatile unsigned int *)(MAILBOX_BASE + 0x00000020)) + +// mailbox channel +// https://github.com/raspberrypi/firmware/wiki/Mailboxes#channels +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +// mailbox status +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 +#define MAILBOX_REQUEST 0x00000000 +#define MAILBOX_RESPONSE 0x80000000 +#define MAILBOX_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +// tags +#define TAGS_REQ_CODE 0x00000000 +#define TAGS_REQ_SUCCEED 0x80000000 +#define TAGS_REQ_FAILED 0x80000001 +#define TAGS_END 0x00000000 + +// hardware tags operator +#define TAGS_HARDWARE_BOARD_MODEL 0x00010001 +#define TAGS_HARDWARE_BOARD_REVISION 0x00010002 +#define TAGS_HARDWARE_MAC_ADDR 0x00010003 +#define TAGS_HARDWARE_BOARD_SERIAL 0x00010004 +#define TAGS_HARDWARE_ARM_MEM 0x00010005 +#define TAGS_HARDWARE_VC_MEM 0x00010006 +#define TAGS_HARDWARE_CLOCKS 0x00010007 + +// CLOCK ID + +#define CLOCK_ID_RESERVED 0x000000000 +#define CLOCK_ID_EMMC 0x000000001 +#define CLOCK_ID_UART 0x000000002 +#define CLOCK_ID_ARM 0x000000003 +#define CLOCK_ID_CORE 0x000000004 +#define CLOCK_ID_V3D 0x000000005 +#define CLOCK_ID_H264 0x000000006 +#define CLOCK_ID_ISP 0x000000007 +#define CLOCK_ID_SDRAM 0x000000008 +#define CLOCK_ID_PIXEL 0x000000009 +#define CLOCK_ID_PWM 0x00000000a +#define CLOCK_ID_HEVC 0x00000000b +#define CLOCK_ID_EMMC2 0x00000000c +#define CLOCK_ID_M2MC 0x00000000d +#define CLOCK_ID_PIXEL_BVB 0x00000000e + +// clock tags operator +#define TAGS_GET_CLOCK 0x00030002 +#define TAGS_SET_CLOCK 0x00038002 + +// framebuffer tages operator +#define FB_ALLOC_BUFFER 0x00040001 +#define FB_FREE_BUFFER 0x00048001 + +#define FB_BLANK_SCREEN 0x00040002 + +#define FB_PHY_WID_HEIGHT_GET 0x00040003 +#define FB_PHY_WID_HEIGHT_TEST 0x00044003 +#define FB_PHY_WID_HEIGHT_SET 0x00048003 + +#define FB_VIR_WID_HEIGHT_GET 0x00040004 +#define FB_VIR_WID_HEIGHT_TEST 0x00044004 +#define FB_VIR_WID_HEIGHT_SET 0x00048004 + +#define FB_DEPTH_GET 0x00040005 +#define FB_DEPTH_TEST 0x00044005 +#define FB_DEPTH_SET 0x00048005 + +#define FB_PIXEL_ORDER_GET 0x00040006 +#define FB_PIXEL_ORDER_TEST 0x00044006 +#define FB_PIXEL_ORDER_SET 0x00048006 + +#define FB_ALPHA_MODE_GET 0x00040007 +#define FB_ALPHA_MODE_TEST 0x00044007 +#define FB_ALPHA_MODE_SET 0x00048007 + +#define FB_PITCH_GET 0x00040008 + +#define FB_VIR_OFFSET_GET 0x00040009 +#define FB_VIR_OFFSET_TEST 0x00044009 +#define FB_VIR_OFFSET_SET 0x00048009 + +#define FB_OVERSCAN_GET 0x0004000A +#define FB_OVERSCAN_TEST 0x0004400A +#define FB_OVERSCAN_SET 0x0004800A + +#define FB_PALETTE_GET 0x0004000B +#define FB_PALETTE_TEST 0x0004400B +#define FB_PALETTE_SET 0x0004800B + +#define FB_CURSOR_INFO_SET 0x00008010 +#define FB_CURSOR_STATE_SET 0x00008011 diff --git a/lab7/include/initramfs.h b/lab7/include/initramfs.h new file mode 100644 index 000000000..1ed3dae4a --- /dev/null +++ b/lab7/include/initramfs.h @@ -0,0 +1,54 @@ +#pragma once + +#include "traps.h" +#include "vfs.h" + +// Cpio Archive File Header (New ASCII Format) +typedef struct { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +} cpio_t; + +typedef struct { + int namesize; + int filesize; + int headsize; + int datasize; + char *pathname; +} ramfsRec; + +typedef struct initramfs_inode { + fsnode_type type; + char *name; + vnode *entry; + char *data; + size_t datasize; +} initramfs_inode; + +int initramfs_setup_mount(filesystem *fs, mount *mnt); + +void initramfs_ls(); +void initramfs_cat(const char *target); +void initramfs_callback(void *addr, char *property); +void initramfs_run(vnode *v); + +int initramfs_read(file *file, void *buf, size_t len); +int initramfs_open(vnode *file_node, file *target); +int initramfs_close(file *file); +long initramfs_lseek64(file *file, long offset, int whence); +long initramfs_getsize(vnode *vn); + +int initramfs_lookup(vnode *dir_node, vnode **target, + const char *component_name); diff --git a/lab7/include/irq.h b/lab7/include/irq.h new file mode 100644 index 000000000..cef36db3a --- /dev/null +++ b/lab7/include/irq.h @@ -0,0 +1,20 @@ +#pragma once + +#include "hardware.h" +#include "traps.h" + +#define ORDER_FIRST 0x0 +#define ORDER_REGULAR 0x8 +#define ORDER_LAST 0xF + +typedef struct irq_task_t { + void (*func)(); + int order; + int busy; // 0 (default) or 1 (busy) + struct irq_task_t *prev; + struct irq_task_t *next; +} irqTask; + +void enable_interrupt(); +void disable_interrupt(); +void irq_entry(trap_frame *tf); \ No newline at end of file diff --git a/lab7/include/mbox.h b/lab7/include/mbox.h new file mode 100644 index 000000000..dbfe23ad3 --- /dev/null +++ b/lab7/include/mbox.h @@ -0,0 +1,24 @@ +#pragma once + +#include "hardware.h" +#include "vfs.h" + +extern volatile unsigned int mbox[36]; + +typedef struct framebuffer_info { + unsigned int width; + unsigned int height; + unsigned int pitch; + unsigned int isrgb; +} framebuffer_info; + +int mbox_call(unsigned char ch, unsigned int *mbox); +int get_board_revision(unsigned int *mbox); +int get_arm_memory_status(unsigned int *mbox); + +file_operations *init_dev_framebuffer(); + +int dev_framebuffer_write(file *f, const void *buf, size_t len); +int dev_framebuffer_open(vnode *file_node, file *target); +int dev_framebuffer_close(file *f); +long dev_framebuffer_lseek64(file *f, long offset, int whence); diff --git a/lab7/include/mem.h b/lab7/include/mem.h new file mode 100644 index 000000000..5f8b7388d --- /dev/null +++ b/lab7/include/mem.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "start.h" + +#define BUDDY_MAX_ORDER 16 +#define CACHE_MAX_ORDER 6 +#define MIN_CACHE_SIZE (PAGE_SIZE / (2 << CACHE_MAX_ORDER)) + +#define VERBAL 0 +#define SILENT 1 + +struct page { + unsigned int order; + unsigned int used; + unsigned int cache_order; + struct page *prev; + struct page *next; +}; + +struct object { + unsigned int order; + struct object *next; +}; + +void init_mem(); +void reserveMemory(uintptr_t resv_start, uintptr_t resv_end); + +uintptr_t page_vaddr(struct page *p); + +void pushPageToFreeList(struct page **list, struct page *page, + unsigned int order); +struct page *popFreeList(struct page **list_head); +void removePageFromFreeList(struct page **list, struct page *page); +void printFreeListByOrder(unsigned int order); + +struct page *lookupBuddy(struct page *page, unsigned int order); +struct page *allocatePagesByOrder(unsigned int order, int silent); +void freePages(struct page *page, unsigned int order, int silent); +void mergePages(struct page *page, unsigned int order, int echo); + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order); +struct object *popObjectFromList(struct object **list_head); +void *allocateCacheMemory(unsigned int index, int silent); +void freeCacheEntry(void *ptr, unsigned int index, int silent); + +void *kmalloc(unsigned int size, int silent); +void kfree(void *ptr, int silent); diff --git a/lab7/include/scheduler.h b/lab7/include/scheduler.h new file mode 100644 index 000000000..2907dd1cd --- /dev/null +++ b/lab7/include/scheduler.h @@ -0,0 +1,81 @@ +#pragma once + +#include "signals.h" +#include "traps.h" +#include "vfs.h" + +struct context_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 thread_state { + RUNNING, + DEAD, +}; + +typedef struct thread_struct_t { + struct context_struct context; // callee-saved (don't move) + + struct thread_struct_t *prev; + struct thread_struct_t *next; + + int pid; + + void *start; + unsigned int size; + + enum thread_state state; + void *stack; + void *user_stack; + + // Signal handling + void (*sig_handlers[NSIG + 1])(); // Signal handlers + int sig_reg; // Pending signals + int sig_busy; // Handling a signal + trap_frame *sig_tf; // tf Saved before signal handling + void *sig_stack; // Stack for signal handling + + void *pgd; + // struct vm_area_struct *mmap; + + char *cwd; + file *file_descriptor; + unsigned int file_descriptor_index; +} thread_struct; + +int push_file_descriptor(thread_struct *thread, file *fd); + +// Defined in scheduler.S +extern void switch_to(thread_struct *prev, thread_struct *next); +extern void switch_mm(unsigned long pgd); + +extern thread_struct *get_current(); // get tpidr_el1, current thread_struct + +thread_struct *get_thread_by_pid(int pid); +void sched_init(); + +thread_struct *kcreate_thread(void (*func)()); +// thread_struct *kcreate_user_thread(void *program, int filesize); +void list_tcircle(); +void list_fd(thread_struct *thread); + +void schedule(); + +void kill_current_thread(); +void kill_thread_by_pid(int pid); +void idle(); + +file *get_file_descriptor_by_id(thread_struct *thread, int id); +file *get_file_descriptor_by_vnode(thread_struct *thread, vnode *v); \ No newline at end of file diff --git a/lab7/include/shell.h b/lab7/include/shell.h new file mode 100644 index 000000000..fccef918e --- /dev/null +++ b/lab7/include/shell.h @@ -0,0 +1,7 @@ +#pragma once + +#define SHELL_BUF_SIZE 1024 + +void run_shell(); +void read_user_input(char *buf); +int exec_command(const char *command); diff --git a/lab7/include/signals.h b/lab7/include/signals.h new file mode 100644 index 000000000..e3ee32af0 --- /dev/null +++ b/lab7/include/signals.h @@ -0,0 +1,12 @@ +#pragma once + +#include "traps.h" + +#define NSIG 10 // Number of signals +#define SIG_STACK (USER_STACK - STACK_SIZE) + +extern void sigreturn(); // Defined in traps.S + +void signal(int signum, void (*handler)()); +void signal_kill(int pid, int sig); +void do_signal(trap_frame *regs); diff --git a/lab7/include/start.h b/lab7/include/start.h new file mode 100644 index 000000000..f3259bdd0 --- /dev/null +++ b/lab7/include/start.h @@ -0,0 +1,44 @@ +#pragma once + +#define PAGE_SIZE 0x1000 +#define STACK_SIZE 0x4000 + +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) +#define TCR_CONFIG_DEFAULT (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB) + +/* + * nGnRnE: Non-Gathering, Non-Reordering, No Early Write Acknowledgement + * Normal memory, Inner Non-cacheable, Outer Non-cacheable + */ +#define MAIR_DEVICE_nGnRnE 0b00000000 +#define MAIR_NORMAL_NOCACHE 0b01000100 +#define MAIR_IDX_DEVICE_nGnRnE 0 +#define MAIR_IDX_NORMAL_NOCACHE 1 + +#define PD_TABLE 0b11 +#define PD_BLOCK 0b01 +#define PD_ENTRY 0b11 + +// Bits[10] The access flag, a page fault is generated if not set. +#define PD_ACCESS (1 << 10) + +// Bits[7] 0 for read-write, 1 or read-only +#define PD_RDONLY (1 << 7) + +// Bits[6] 0 for only kernel access, 1 for user/kernel access +#define PD_UKACCESS (1 << 6) + +// Bits[4:2] The index to MAIR +#define PD_MAIR_DEVICE_nGnRnE (MAIR_IDX_DEVICE_nGnRnE << 2) +#define PD_MAIR_NORMAL_NOCACHE (MAIR_IDX_NORMAL_NOCACHE << 2) + +#define BOOT_PGD_PAGE_FRAME 0x1000 // Boot PGD's page frame +#define BOOT_PUD_PAGE_FRAME 0x2000 // Boot PUD's page frame +#define BOOT_PMD_PAGE_FRAME 0x3000 // Boot PMD's page frame + +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR PD_TABLE +#define BOOT_PUD_DEVICE_ATTR (PD_ACCESS | PD_MAIR_DEVICE_nGnRnE | PD_BLOCK) +#define BOOT_PMD_NORMAL_ATTR (PD_ACCESS | PD_MAIR_NORMAL_NOCACHE | PD_BLOCK) +#define BOOT_PMD_DEVICE_ATTR (PD_ACCESS | PD_MAIR_DEVICE_nGnRnE | PD_BLOCK) \ No newline at end of file diff --git a/lab7/include/str.h b/lab7/include/str.h new file mode 100644 index 000000000..766ff7ea8 --- /dev/null +++ b/lab7/include/str.h @@ -0,0 +1,6 @@ +#pragma once + +int strcmp(const char *str1, const char *str2); +char *strncpy(char *dest, const char *src, int n); +int strlen(const char *str); +char *strcat(char *dest, const char *src); \ No newline at end of file diff --git a/lab7/include/syscalls.h b/lab7/include/syscalls.h new file mode 100644 index 000000000..c1691d6f4 --- /dev/null +++ b/lab7/include/syscalls.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "mbox.h" +#include "traps.h" + +#define GET_INFO 0 + +extern void child_ret_from_fork(); // traps.S + +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, trap_frame *tf); +int sys_fork(trap_frame *tf); +void sys_exit(int status); +int sys_mbox_call(unsigned char ch, unsigned int *mbox); +void sys_sigreturn(trap_frame *regs); + +int sys_open(const char *pathname, int flags); +int sys_close(int fd); +int sys_write(int fd, void *buf, size_t count); +int sys_read(int fd, void *buf, size_t count); +int sys_mkdir(const char *pathname, unsigned mode); +int sys_mount(const char *target, const char *filesystem); +int sys_chdir(const char *path); + +long sys_lseek64(int fd, long offset, int whence); +int sys_ioctl(int fd, unsigned long request, void *info); diff --git a/lab7/include/timer.h b/lab7/include/timer.h new file mode 100644 index 000000000..b92678a35 --- /dev/null +++ b/lab7/include/timer.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#define TIMER_SPEED 3 // default 5 + +typedef struct timerEntry_t { + void (*func)(void *); + void *arg; + int time; + struct timerEntry_t *next; +} timerEntry; + +void init_timer(); + +void enable_timer_interrupt(); +void disable_timer_interrupt(); + +void timer_irq_handler(); +uint64_t timer_get_uptime(); + +void timer_add(void (*callback)(void *), void *arg, int duration); +void set_timer(const char *message, int duration); +void set_timeup(int *timeup); diff --git a/lab7/include/tmpfs.h b/lab7/include/tmpfs.h new file mode 100644 index 000000000..de23a12f5 --- /dev/null +++ b/lab7/include/tmpfs.h @@ -0,0 +1,34 @@ +#pragma once + +#include "vfs.h" + +#define MAX_DIR_ENTRY 16 +// #define MAX_FILE_SIZE 4096 + +typedef struct tmpfs_inode { + fsnode_type type; + char *name; + vnode *entry[MAX_DIR_ENTRY]; + char *data; + size_t datasize; +} tmpfs_inode; + +int tmpfs_setup_mount(filesystem *fs, mount *mnt); + +vnode *tmpfs_create_vnode(fsnode_type type); + +int tmpfs_write(file *file, const void *buf, size_t len); + +int tmpfs_read(file *file, void *buf, size_t len); + +// open a file, initailze everything +int tmpfs_open(vnode *file_node, file *target); + +int tmpfs_close(file *file); + +// long seek 64-bit +long tmpfs_lseek64(file *file, long offset, int whence); +long tmpfs_getsize(vnode *vn); +int tmpfs_lookup(vnode *dir_node, vnode **target, const char *component_name); +int tmpfs_create(vnode *dir_node, vnode **target, const char *component_name); +int tmpfs_mkdir(vnode *dir_node, vnode **target, const char *component_name); \ No newline at end of file diff --git a/lab7/include/traps.h b/lab7/include/traps.h new file mode 100644 index 000000000..bf940f5d7 --- /dev/null +++ b/lab7/include/traps.h @@ -0,0 +1,38 @@ +#pragma once + +typedef struct { // order is important + 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; // system call number + 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; // link register + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trap_frame; diff --git a/lab7/include/uart.h b/lab7/include/uart.h new file mode 100644 index 000000000..4d01eaa25 --- /dev/null +++ b/lab7/include/uart.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "hardware.h" +#include "vfs.h" + +#define UART_BUF_SIZE 1024 + +// Special characters +#define BACKSPACE '\b' +#define DELETE 127 +#define ESC 27 +#define NEWLINE '\n' +#define TAB '\t' +#define CARRIAGE_RETURN '\r' + +// uart_log types +#define INFO 0 +#define WARN 1 +#define TEST 2 +#define BUDD 10 +#define CACH 11 +#define VFS 12 +#define MMAP 13 +#define SCALL 253 +#define DEBUG 254 +#define ERR 255 + +void init_uart(); +char uart_getc(); +void uart_putc(char c); +void uart_puts(const char *s); +void uart_clear(); +void uart_hex(unsigned long h); +void uart_addr_range(uintptr_t start, uintptr_t end); +void uart_simple_hex(unsigned int h); +void uart_dec(unsigned int h); +void uart_log(int type, const char *msg); + +void enable_uart_tx_interrupt(); +void disable_uart_tx_interrupt(); +void enable_uart_rx_interrupt(); +void disable_uart_rx_interrupt(); + +void uart_tx_irq_handler(); +void uart_rx_irq_handler(); + +void uart_async_read(char *buf, int len); +void uart_async_write(const char *s); + +file_operations *init_dev_uart(); +int dev_uart_write(file *file, const void *buf, size_t len); +int dev_uart_read(file *file, void *buf, size_t len); +int dev_uart_open(vnode *file_node, file *target); +int dev_uart_close(file *file); \ No newline at end of file diff --git a/lab7/include/utils.h b/lab7/include/utils.h new file mode 100644 index 000000000..c2ebcb54b --- /dev/null +++ b/lab7/include/utils.h @@ -0,0 +1,24 @@ +#pragma once + +/** + * @brief Align `n` to be a multiple of 4. + * + * @param n A number + * @return Algined number + */ +int align4(int n); + +int atoi(const char *s); + +/** + * @brief Convert hexadecimal string to int. + * + * @param s: hexadecimal string + * @param n: string length + * @return Converted int number + */ +int hextoi(char *s, int n); + +int memcmp(const void *str1, const void *str2, int n); +void *memcpy(void *dest, const void *src, int n); +void *memset(void *s, int c, int n); \ No newline at end of file diff --git a/lab7/include/vfs.h b/lab7/include/vfs.h new file mode 100644 index 000000000..1a1f152f5 --- /dev/null +++ b/lab7/include/vfs.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +#include "traps.h" + +#define O_RDONLY 00000000 +#define O_WRONLY 00000001 +#define O_RDWR 00000002 +#define O_ACCMODE 00000003 +#define O_CREAT 00000100 + +#define SEEK_SET 0 +#define SEEK_CUR 1 + +typedef enum fsnode_type { + dir_t, // + file_t // +} fsnode_type; + +typedef struct vnode { + struct mount *mount; + struct vnode_operations *v_ops; + struct file_operations *f_ops; + void *internal; + struct vnode *next; +} vnode; + +// file handle +typedef struct _file { + int id; + vnode *vnode; + size_t f_pos; + struct file_operations *f_ops; + int flags; + struct _file *prev; + struct _file *next; +} file; + +typedef struct mount { + vnode *root; + struct filesystem *fs; +} mount; + +typedef struct filesystem { + char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); + struct filesystem *next; +} filesystem; + +typedef struct file_operations { + int (*write)(file *file, const void *buf, size_t len); + int (*read)(file *file, void *buf, size_t len); + int (*open)(vnode *file_node, file *target); + int (*close)(file *file); + long (*lseek64)(file *file, long offset, int whence); + long (*getsize)(vnode *vd); +} file_operations; + +typedef struct vnode_operations { + int (*lookup)(vnode *dir_node, vnode **target, const char *component_name); + int (*create)(vnode *dir_node, vnode **target, const char *component_name); + int (*mkdir)(vnode *dir_node, vnode **target, const char *component_name); +} vnode_operations; + +void init_vfs(); +filesystem *register_filesystem(char *const name, void *setup_mount); +vnode *vfs_mknod(char *pathname); + +int vfs_open(const char *pathname, int flags, file *target); +int vfs_close(file *file); +int vfs_write(file *file, const void *buf, size_t len); +int vfs_read(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, vnode **target); +void vfs_exec(vnode *target, trap_frame *tf); + +file *create_file(); +char *absolute_path(const char *path, const char *cwd); +int not_supported(); \ No newline at end of file diff --git a/lab7/include/virtm.h b/lab7/include/virtm.h new file mode 100644 index 000000000..5b1e8b628 --- /dev/null +++ b/lab7/include/virtm.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "hardware.h" +#include "scheduler.h" + +#define USER_STACK 0x0000FFFFFFFFB000 + +#define PTE_NORMAL_ATTR \ + (PD_ACCESS | PD_UKACCESS | PD_MAIR_NORMAL_NOCACHE | PD_ENTRY) + +void map_pages(uintptr_t pgd, uintptr_t va, uintptr_t size, uintptr_t pa); + +void mapping_user_thread(thread_struct *thread, int gpu_mem_size); \ No newline at end of file diff --git a/lab7/initramfs.cpio b/lab7/initramfs.cpio new file mode 100644 index 000000000..a25f28e27 Binary files /dev/null and b/lab7/initramfs.cpio differ diff --git a/lab7/sender.sh b/lab7/sender.sh new file mode 100755 index 000000000..501756d76 --- /dev/null +++ b/lab7/sender.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This script sends the kernel image to Rpi3 through UART + +DEST_PATH="/dev/ttyUSB0" +KERNEL_PATH="./kernel8.img" + +# Check the root permission +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +if [ $1 ] +then + DEST_PATH="$1" +fi + +# Get the size of the kernel image file and send it to Rpi3 +# wc -c: count bytes of a file +# sleep: wait n seconds +wc -c < $KERNEL_PATH > $DEST_PATH | sleep 1 + +# Send the kernel image +# pv: redirect file input to specified tty +# add --rate-limit option to limit the speed +pv $KERNEL_PATH > $DEST_PATH diff --git a/lab7/src/command.c b/lab7/src/command.c new file mode 100644 index 000000000..1dbb23fcf --- /dev/null +++ b/lab7/src/command.c @@ -0,0 +1,225 @@ +#include "command.h" + +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "shell.h" +#include "start.h" +#include "str.h" +#include "syscalls.h" +#include "timer.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +typedef struct { + void *ptr; + void *next; +} demo_mem_rec; + +static demo_mem_rec *dmr_list = 0; + +void cmd_info() { + uart_log(INFO, "board revision: "); + uart_hex(BOARD_REVISION); + uart_putc(NEWLINE); + uart_log(INFO, "device base memory address: "); + uart_hex(BASE_MEMORY); + uart_putc(NEWLINE); + uart_log(INFO, "device memory size: "); + uart_hex(NUM_PAGES * PAGE_SIZE); + uart_putc(NEWLINE); +} + +void cmd_hello() { uart_log(INFO, "Hello there!\n"); } + +static void cmd_mem() { + for (int i = BUDDY_MAX_ORDER; i >= 0; i--) { + printFreeListByOrder(i); + } +} + +static void cmd_bd() { + uart_puts("Buddy System: request order (0-"); + uart_dec(BUDDY_MAX_ORDER); + uart_puts("): "); + char *buf = kmalloc(SHELL_BUF_SIZE, SILENT); + read_user_input(buf); + int order = atoi(buf); + if (buf[0] < '0' || buf[0] > '9' || order < 0 || order > BUDDY_MAX_ORDER) { + uart_log(WARN, "Invalid order.\n"); + } else { + demo_mem_rec *dmr = kmalloc(sizeof(demo_mem_rec), SILENT); + dmr->ptr = kmalloc(PAGE_SIZE << order, SILENT); + dmr->next = dmr_list; + dmr_list = dmr; + } + kfree(buf, SILENT); +} + +static void cmd_fm() { + if (dmr_list == 0) { + uart_log(WARN, "No memory block allocated.\n"); + } else { + uart_log(INFO, "Freeing all allocated memory blocks...\n"); + do { + demo_mem_rec *dmr = dmr_list; + kfree(dmr->ptr, SILENT); + dmr_list = dmr->next; + kfree(dmr, SILENT); + } while (dmr_list != 0); + } +} + +static void cmd_ca() { + uart_log(INFO, "Dynamic Memory Allocator: request byte(s) (1-"); + uart_dec(PAGE_SIZE / 2); + uart_puts("): "); + char *buf = kmalloc(SHELL_BUF_SIZE, SILENT); + read_user_input(buf); + int size = atoi(buf); + if (buf[0] == '\0' || size <= 0) { + uart_log(WARN, "Invalid size.\n"); + } else { + if (size > PAGE_SIZE / 2) { + uart_log( + WARN, + "Size too large for dynamic allocator. Switching to Buddy System.\n"); + } + demo_mem_rec *dmr = kmalloc(sizeof(demo_mem_rec), SILENT); + dmr->ptr = kmalloc(size, SILENT); + dmr->next = dmr_list; + dmr_list = dmr; + } + kfree(buf, SILENT); +} + +static void cmd_lab7() { + char *buf = "/initramfs/vfs1.img"; + uart_log(INFO, "run: "); + uart_puts(buf); + uart_putc(NEWLINE); + strcat(buf, "\0"); + vnode *target_file; + int ret = vfs_lookup(buf, &target_file); // assign the vnode to target + if (ret != 0) { + uart_log(WARN, "File not found.\n"); + return; + } + initramfs_run(target_file); +} + +static void cmd_timer() { + uart_puts("Duration(sec.): "); + char *buf = kmalloc(SHELL_BUF_SIZE, SILENT); + read_user_input(buf); + int sec = atoi(buf); + + char *msg = kmalloc(SHELL_BUF_SIZE, SILENT); + uart_puts(": "); + read_user_input(msg); + set_timer(msg, sec); + kfree(buf, SILENT); +} + +static void cmd_run() { + // Get filename from user input + uart_puts(": "); + char *buf = kmalloc(SHELL_BUF_SIZE, SILENT); + read_user_input(buf); + vnode *target_file; + int ret = vfs_lookup(buf, &target_file); // assign the vnode to target + kfree(buf, SILENT); + if (ret != 0) { + uart_log(WARN, "File not found.\n"); + return; + } + initramfs_run(target_file); +} + +static void cmd_ls() { + uart_log(INFO, "Listing initramfs files...\n"); + vnode *dirnode; + if (vfs_lookup("/initramfs", &dirnode)) { + uart_log(WARN, "initramfs not found.\n"); + return; + } + initramfs_inode *dir_inode = dirnode->internal; + uart_puts(dir_inode->name); + uart_putc(NEWLINE); + vnode *ptr = dir_inode->entry; + while (ptr) { + initramfs_inode *inode = ptr->internal; + uart_puts(inode->name); + for (int i = 0; i < 20 - strlen(inode->name); i++) uart_putc(' '); + uart_dec(inode->datasize); + uart_putc(NEWLINE); + ptr = ptr->next; + } +} + +static void cmd_reboot() { + uart_log(INFO, "Start Rebooting...\n"); + // Reboot after 0x20000 ticks + *PM_RSTC = PM_PASSWORD | 0x20; // Full reset + *PM_WDOG = PM_PASSWORD | 0x20000; // Number of watchdog ticks +} + +static void cmd_cancel() { + uart_log(INFO, "Rebooting Attempt Aborted, if any.\n"); + *PM_RSTC = PM_PASSWORD | 0; + *PM_WDOG = PM_PASSWORD | 0; +} + +static void cmd_cat() { + // Get filename from user input + uart_puts(": "); + char *buf = kmalloc(SHELL_BUF_SIZE, SILENT); + read_user_input(buf); + initramfs_cat(buf); + kfree(buf, SILENT); +} + +static void cmd_clear() { uart_clear(); } + +static void cmd_help() { + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); + struct command *cmd = cmd_list; + while (1) { + if (!strcmp(cmd->name, END_OF_COMMAND_LIST)) { + break; + } + uart_puts(cmd->name); + uart_putc(TAB); + uart_puts(": "); + uart_puts(cmd->help); + uart_putc(NEWLINE); + cmd++; + } + uart_puts("-----------------------------------------"); + uart_putc(NEWLINE); +} + +struct command cmd_list[] = { + {.name = "help", .help = "Display this help menu", .func = cmd_help}, + {.name = "hello", .help = "Print 'Hello there!'", .func = cmd_hello}, + {.name = "clear", .help = "Clear the screen", .func = cmd_clear}, + {.name = "reboot", .help = "Reboot the device", .func = cmd_reboot}, + {.name = "cancel", .help = "Cancel a scheduled reboot", .func = cmd_cancel}, + {.name = "info", .help = "Show hardware information", .func = cmd_info}, + {.name = "ls", .help = "List the content of initramfs", .func = cmd_ls}, + {.name = "cat", .help = "Display content of ramdisk file", .func = cmd_cat}, + {.name = "run", .help = "Run a specified program", .func = cmd_run}, + {.name = "timer", .help = "Set timer with duration", .func = cmd_timer}, + // lab4 + {.name = "mem", .help = "Display free memory blocks", .func = cmd_mem}, + {.name = "bd", .help = "Allocate a memory block", .func = cmd_bd}, + {.name = "ca", .help = "Use the dynamic memory allocator", .func = cmd_ca}, + // lab5 + {.name = "fm", .help = "Free all memory in the demo list", .func = cmd_fm}, + {.name = "pid", .help = "List all running threads", .func = list_tcircle}, + // retired + {.name = "lab7", .help = "Showcase lab7 requirements", .func = cmd_lab7}, + {.name = END_OF_COMMAND_LIST}}; diff --git a/lab7/src/devtree.c b/lab7/src/devtree.c new file mode 100644 index 000000000..a64ea7638 --- /dev/null +++ b/lab7/src/devtree.c @@ -0,0 +1,64 @@ +#include "devtree.h" + +#include "str.h" +#include "uart.h" +#include "utils.h" + +// Assign a non-zero value to be stored in the .data section +void *DTB_BASE = (void *)0xF; +void *DTB_END = (void *)0xF; + +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]; +} + +void fdt_traverse(void (*callback)(void *, char *)) { + struct fdt_header *header = (struct fdt_header *)(uintptr_t)DTB_BASE; + + // Check the magic number + if (be2le(&(header->magic)) != 0xD00DFEED) { + uart_log(WARN, "Dtb header magic does not match!\n"); + } + DTB_BASE = (void *)TO_VIRT(DTB_BASE); + DTB_END = DTB_BASE + be2le(&header->totalsize); + + uart_log(INFO, "Dtb loaded at "); + uart_addr_range((uintptr_t)DTB_BASE, (uintptr_t)DTB_END); + uart_putc(NEWLINE); + + uintptr_t structure = (uintptr_t)header + be2le(&header->off_dt_struct); + uintptr_t strings = (uintptr_t)header + be2le(&header->off_dt_strings); + uint32_t structure_size = be2le(&header->size_dt_struct); + + // Parse the structure block + uintptr_t ptr = structure; // Point to the beginning of structure block + while (ptr < structure + structure_size) { + uint32_t token = be2le((char *)ptr); + ptr += 4; // Token takes 4 bytes + + switch (token) { + case FDT_BEGIN_NODE: + ptr += align4(strlen((char *)ptr) + 1); + break; + case FDT_PROP: + uint32_t len = be2le((char *)ptr); + ptr += 4; + uint32_t nameoff = be2le((char *)ptr); + ptr += 4; + callback((void *)(uintptr_t)be2le((void *)ptr), + (char *)(strings + nameoff)); + ptr += align4(len); + break; + case FDT_END_NODE: + case FDT_NOP: + case FDT_END: + break; + default: + uart_log(WARN, "Unknown fdt token: "); + uart_simple_hex(token); + uart_putc(NEWLINE); + } + } +} \ No newline at end of file diff --git a/lab7/src/initramfs.c b/lab7/src/initramfs.c new file mode 100644 index 000000000..ecfe73783 --- /dev/null +++ b/lab7/src/initramfs.c @@ -0,0 +1,262 @@ +#include "initramfs.h" + +#include + +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "start.h" +#include "str.h" +#include "syscalls.h" +#include "uart.h" +#include "utils.h" +#include "vfs.h" +#include "virtm.h" + +void *initrd_start; +void *initrd_end; + +void initramfs_callback(void *addr, char *property) { + if (!strcmp(property, "linux,initrd-start")) { + uart_log(INFO, "linux,initrd-start: "); + initrd_start = (char *)TO_VIRT(addr); + uart_hex((uintptr_t)initrd_start); + uart_putc(NEWLINE); + } else if (!strcmp(property, "linux,initrd-end")) { + uart_log(INFO, "linux,initrd-end: "); + initrd_end = (char *)TO_VIRT(addr); + uart_hex((uintptr_t)initrd_end); + uart_putc(NEWLINE); + } +} + +file_operations initramfs_file_operations = { + (void *)not_supported, // write + initramfs_read, // + initramfs_open, // + initramfs_close, // + (void *)not_supported, // lseek64 + initramfs_getsize // +}; + +vnode_operations initramfs_vnode_operations = { + initramfs_lookup, // + (void *)not_supported, // create + (void *)not_supported // mkdir +}; + +static vnode *initramfs_create_vnode(mount *mnt, fsnode_type type) { + vnode *v = kmalloc(sizeof(vnode), SILENT); + v->f_ops = &initramfs_file_operations; + v->v_ops = &initramfs_vnode_operations; + v->mount = mnt; + + initramfs_inode *inode = kmalloc(sizeof(initramfs_inode), SILENT); + memset(inode, 0, sizeof(initramfs_inode)); + inode->type = type; + inode->data = kmalloc(PAGE_SIZE, SILENT); + + v->internal = inode; + return v; +} + +static ramfsRec *ramfsNext(char *fptr) { + ramfsRec *rec = kmalloc(sizeof(ramfsRec), SILENT); + cpio_t *header = (cpio_t *)fptr; + + // New ASCII Format uses 8-byte hexadecimal string for all numbers + rec->namesize = hextoi(header->c_namesize, 8); + rec->filesize = hextoi(header->c_filesize, 8); + + // Total size of (header + pathname) is a multiple of four bytes + // File data is also padded to a multiple of four bytes + rec->headsize = align4(sizeof(cpio_t) + rec->namesize); + rec->datasize = align4(rec->filesize); + + // Get file pathname + rec->pathname = kmalloc(rec->namesize, SILENT); + strncpy(rec->pathname, fptr + sizeof(cpio_t), rec->namesize); + + return rec; +} + +int initramfs_setup_mount(filesystem *fs, mount *mnt) { + mnt->fs = fs; + mnt->root = initramfs_create_vnode(0, dir_t); + + initramfs_inode *ramfs_inode = mnt->root->internal; + ramfs_inode->entry = 0; + + char *fptr = (char *)initrd_start; + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + vnode *file_vnode = initramfs_create_vnode(0, file_t); + initramfs_inode *file_inode = file_vnode->internal; + file_inode->data = fptr + rec->headsize; + file_inode->datasize = rec->filesize; + file_inode->name = kmalloc(strlen(rec->pathname) + 1, SILENT); + strncpy(file_inode->name, rec->pathname, strlen(rec->pathname) + 1); + + file_vnode->next = ramfs_inode->entry; + ramfs_inode->entry = file_vnode; + + fptr += rec->headsize + rec->datasize; + kfree(rec, SILENT); + } + return 0; +} + +int initramfs_read(file *file, void *buf, size_t len) { + initramfs_inode *inode = file->vnode->internal; + + // if overflow, shrink size + if (len + file->f_pos > inode->datasize) + len = inode->datasize - file->f_pos; + + memcpy(buf, inode->data, len); + file->f_pos += len; + return len; +}; + +int initramfs_open(vnode *file_node, file *target) { + target->vnode = file_node; + target->f_ops = file_node->f_ops; + target->f_pos = 0; + return 0; +}; + +int initramfs_close(file *f) { + kfree(f, SILENT); + return 0; +}; + +long initramfs_getsize(vnode *vn) { + initramfs_inode *file_inode = vn->internal; + return file_inode->datasize; +}; + +int initramfs_lookup(vnode *dir_node, vnode **target, + const char *component_name) { + initramfs_inode *dir_inode = dir_node->internal; + vnode *ptr = dir_inode->entry; + while (ptr) { + initramfs_inode *inode = ptr->internal; + if (strcmp(component_name, inode->name) == 0) { + *target = ptr; + return 0; + } + ptr = ptr->next; + } + return -1; +}; + +void initramfs_ls() { + uart_putc(NEWLINE); + + char *fptr = (char *)initrd_start; + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + uart_puts(rec->pathname); + for (int i = 0; i < 15 - strlen(rec->pathname); i++) { + uart_putc(' '); + } + uart_dec(rec->filesize); + uart_puts(" byte"); + if (rec->filesize > 1) + uart_putc('s'); + uart_putc(NEWLINE); + + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, SILENT); + kfree(rec, SILENT); + } +} + +void initramfs_cat(const char *filename) { + char *fptr = (char *)initrd_start; + + while (memcmp(fptr + sizeof(cpio_t), "TRAILER!!!", 10)) { + ramfsRec *rec = ramfsNext(fptr); + if (!strcmp(filename, rec->pathname)) { + // Dump its content + uart_putc(NEWLINE); + for (char *c = fptr + rec->headsize; + c < fptr + rec->headsize + rec->filesize; c++) { + uart_putc(*c); + } + uart_putc(NEWLINE); + kfree(rec->pathname, SILENT); + kfree(rec, SILENT); + return; + } + fptr += rec->headsize + rec->datasize; + kfree(rec->pathname, SILENT); + kfree(rec, SILENT); + } + + uart_puts("File not found."); + uart_putc(NEWLINE); +} + +void initramfs_run(vnode *v) { + // Load the user program + disable_interrupt(); + initramfs_inode *inode = v->internal; + + thread_struct *thread = kcreate_thread(0); + + thread->user_stack = kmalloc(STACK_SIZE, 0); + uart_log(INFO, "Acquired thread user stack: "); + uart_hex((uintptr_t)thread->stack); + uart_putc(NEWLINE); + + void *program = kmalloc(inode->datasize, 0); + uart_log(INFO, "Acquired program space: "); + uart_hex((uintptr_t)(thread->start = program)); + uart_putc(NEWLINE); + memcpy(program, inode->data, thread->size = inode->datasize); + + mapping_user_thread(thread, 0x1000000); + + file *stdin = create_file(); + vfs_open("/dev/uart", 0, stdin); + uart_log(DEBUG, "stdin: "); + uart_dec(push_file_descriptor(thread, stdin)); + uart_putc(NEWLINE); + + file *stdout = create_file(); + vfs_open("/dev/uart", 0, stdout); + uart_log(DEBUG, "stdout: "); + uart_dec(push_file_descriptor(thread, stdout)); + uart_putc(NEWLINE); + + file *stderr = create_file(); + vfs_open("/dev/uart", 0, stderr); + uart_log(DEBUG, "stderr: "); + uart_dec(push_file_descriptor(thread, stderr)); + uart_putc(NEWLINE); + + file *framebuffer = create_file(); + vfs_open("/dev/framebuffer", 0, framebuffer); + uart_log(DEBUG, "framebuffer: "); + uart_dec(push_file_descriptor(thread, framebuffer)); + uart_putc(NEWLINE); + + switch_mm((uintptr_t)TO_PHYS(thread->pgd)); + + // asm volatile("msr tpidr_el1, %0\n" ::"r"(thread)); + asm volatile("msr tpidr_el1, %4\n" + "msr spsr_el1, %0\n" + "msr elr_el1, %1\n" + "msr sp_el0, %2\n" + "mov sp, %3\n" + "eret\n" + : + : "r"(0x340), // 0 + "r"(0x0), // 1 link register + "r"(USER_STACK + STACK_SIZE), // 2 + "r"(thread->stack + STACK_SIZE), // 3 + "r"(thread) // 4 + :); + return; +} diff --git a/lab7/src/irq.c b/lab7/src/irq.c new file mode 100644 index 000000000..51fa3db8a --- /dev/null +++ b/lab7/src/irq.c @@ -0,0 +1,102 @@ +#include "irq.h" + +#include "mem.h" +#include "scheduler.h" +#include "timer.h" +#include "uart.h" + +static irqTask *irqTask_head = 0; + +// Disable interrupt before calling irq_add_task() +void irq_add_task(void (*callback)(), int order) { + irqTask *task = kmalloc(sizeof(irqTask), SILENT); + task->func = callback; + task->order = order; + task->busy = 0; + task->prev = 0; + task->next = 0; + + // 0 -> task -> head -> ... + if (irqTask_head == 0 || task->order < irqTask_head->order) { + task->next = irqTask_head; + task->prev = 0; + if (irqTask_head != 0) + irqTask_head->prev = task; + irqTask_head = task; + return; + } + + irqTask *current = irqTask_head; + while (current->next != 0 && current->next->order <= task->order) + current = current->next; + task->next = current->next; + if (current->next != 0) + current->next->prev = task; + current->next = task; + task->prev = current; +} + +// to enable interrupts in EL1 +void enable_interrupt() { + asm volatile( + "msr DAIFClr, 0xF\n" // Clear the D, A, I, F bits in the DAIF register + ); +} + +// to disable interrupts in EL1 +void disable_interrupt() { + asm volatile( + "msr DAIFSet, 0xF\n" // Set the D, A, I, F bits to 1 in the DAIF register + ); +} + +void irq_entry(trap_frame *tf) { + disable_interrupt(); // Enter the critical section + + if (*IRQ_PENDING_1 & (1 << 29)) { // UART interrupt + switch (*AUX_MU_IIR & 0x6) { // 0x6 = 0110 -> Get 0x2 and 0x4 + + case 0x2: // 0x2 UART Transmit interrupt + disable_uart_tx_interrupt(); + irq_add_task(uart_tx_irq_handler, ORDER_LAST); + break; + + case 0x4: // 0x4 UART Receive interrupt + disable_uart_rx_interrupt(); + irq_add_task(uart_rx_irq_handler, ORDER_REGULAR); + break; + } + } else if (*CORE0_INTERRUPT_SOURCE & 0x2) { + // Core 0 timer interrupt for schedule() + // tell if thread_struct is not the only one in the run_queue + if (get_current() != get_current()->next) + schedule(); + + disable_timer_interrupt(); + irq_add_task(timer_irq_handler, ORDER_FIRST); + } + + enable_interrupt(); // Leave the critical section + + // Preemption: run the task with the highest priority + while (irqTask_head != 0 && !irqTask_head->busy) { + disable_interrupt(); + irqTask *task = irqTask_head; // Get a task from head + task->busy = 1; // Flag the task as under processing + enable_interrupt(); + + task->func(); // Run the tasks with interrupts enabled + + // Remove the task + disable_interrupt(); + if (task->prev != 0) + task->prev->next = task->next; + if (task->next != 0) + task->next->prev = task->prev; + if (task == irqTask_head) + irqTask_head = task->next; + kfree((void *)task, SILENT); + enable_interrupt(); + } + do_signal(tf); +} \ No newline at end of file diff --git a/lab7/src/linker.ld b/lab7/src/linker.ld new file mode 100644 index 000000000..451d21bfd --- /dev/null +++ b/lab7/src/linker.ld @@ -0,0 +1,14 @@ +SECTIONS +{ + . = 0xFFFF000000000000; + . += 0x80000; + .text : { *(.text.boot) *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { + __bss_start = .; + *(.bss) + __bss_end = .; + } +} +__bss_size = (__bss_end - __bss_start) >> 3; /* SIZEOF(.bss); */ diff --git a/lab7/src/main.c b/lab7/src/main.c new file mode 100644 index 000000000..797362b14 --- /dev/null +++ b/lab7/src/main.c @@ -0,0 +1,37 @@ +#include "devtree.h" +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "shell.h" +#include "timer.h" +#include "uart.h" +#include "vfs.h" + +int main() { + /* Initialization */ + uart_log(INFO, "init_uart()\n"); + init_uart(); + + uart_log(INFO, "fdt_traverse()\n"); + fdt_traverse(initramfs_callback); + + uart_log(INFO, "init_mem()\n"); + init_mem(); + + uart_log(INFO, "init_vfs()\n"); + init_vfs(); + + uart_log(INFO, "enable_interrupt()\n"); + enable_interrupt(); + + uart_log(INFO, "init_timer()\n"); + init_timer(); + + uart_log(INFO, "sched_init()\n"); + sched_init(); + + run_shell(); + + return 0; +} \ No newline at end of file diff --git a/lab7/src/mbox.c b/lab7/src/mbox.c new file mode 100644 index 000000000..06b2e0933 --- /dev/null +++ b/lab7/src/mbox.c @@ -0,0 +1,160 @@ +#include "mbox.h" + +#include "irq.h" +#include "mem.h" +#include "uart.h" +#include "utils.h" +#include "vfs.h" + +int mbox_call(unsigned char ch, unsigned int *mbox) { + unsigned int r = (unsigned int)((unsigned long)mbox & ~0xF) | (ch & 0xF); + // Wait until we can write to the mailbox + while (*MAILBOX_REG_STATUS & MAILBOX_FULL) + ; + *MAILBOX_REG_WRITE = r; // Write the request + while (1) { + // Wait for the response + while (*MAILBOX_REG_STATUS & MAILBOX_EMPTY) + ; + if (r == *MAILBOX_REG_READ) + return mbox[1] == MAILBOX_RESPONSE; + } + return 0; +} + +unsigned int __attribute__((aligned(16))) mbox_fb[36]; +framebuffer_info fb_info; +uintptr_t lfb; // raw frame buffer addess + +int get_board_revision(unsigned int *mbox) { + mbox[0] = 7 * 4; + mbox[1] = MAILBOX_REQUEST; + mbox[2] = TAGS_HARDWARE_BOARD_REVISION; + mbox[3] = 4; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = END_TAG; + return mbox_call(MAILBOX_CH_PROP, mbox); +} + +int get_arm_memory_status(unsigned int *mbox) { + mbox[0] = 8 * 4; + mbox[1] = MAILBOX_REQUEST; + mbox[2] = TAGS_HARDWARE_ARM_MEM; + mbox[3] = 8; + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; + mbox[6] = 0; + mbox[7] = END_TAG; + return mbox_call(MAILBOX_CH_PROP, mbox); +} + +file_operations dev_framebuffer_operation = { + dev_framebuffer_write, // + (void *)not_supported, // read + dev_framebuffer_open, // + dev_framebuffer_close, // + dev_framebuffer_lseek64, // + (void *)not_supported // getsize +}; + +file_operations *init_dev_framebuffer() { + // The following code is for mailbox initialize used in lab7. + mbox_fb[0] = 35 * 4; + mbox_fb[1] = MAILBOX_REQUEST; + + mbox_fb[2] = FB_PHY_WID_HEIGHT_SET; // set phy wh + mbox_fb[3] = 8; + mbox_fb[4] = 8; + mbox_fb[5] = 1024; // FrameBufferInfo.width + mbox_fb[6] = 768; // FrameBufferInfo.height + + mbox_fb[7] = FB_VIR_WID_HEIGHT_SET; // set virt wh + mbox_fb[8] = 8; + mbox_fb[9] = 8; + mbox_fb[10] = 1024; // FrameBufferInfo.virtual_width + mbox_fb[11] = 768; // FrameBufferInfo.virtual_height + + mbox_fb[12] = FB_VIR_OFFSET_SET; // set virt offset + mbox_fb[13] = 8; + mbox_fb[14] = 8; + mbox_fb[15] = 0; // FrameBufferInfo.x_offset + mbox_fb[16] = 0; // FrameBufferInfo.y.offset + + mbox_fb[17] = FB_DEPTH_SET; // set depth + mbox_fb[18] = 4; + mbox_fb[19] = 4; + mbox_fb[20] = 32; // FrameBufferInfo.depth + + mbox_fb[21] = FB_PIXEL_ORDER_SET; // set pixel order + mbox_fb[22] = 4; + mbox_fb[23] = 4; + mbox_fb[24] = 1; // RGB, not BGR preferably + + mbox_fb[25] = FB_ALLOC_BUFFER; // get framebuffer, gets alignment on request + mbox_fb[26] = 8; + mbox_fb[27] = 8; + mbox_fb[28] = 4096; // FrameBufferInfo.pointer + mbox_fb[29] = 0; // FrameBufferInfo.size + + mbox_fb[30] = FB_PITCH_GET; // get pitch + mbox_fb[31] = 4; + mbox_fb[32] = 4; + mbox_fb[33] = 0; // FrameBufferInfo.pitch + + mbox_fb[34] = END_TAG; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mbox_call(MAILBOX_CH_PROP, mbox_fb) && mbox_fb[20] == 32 && + mbox_fb[28] != 0) { + mbox_fb[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + fb_info.width = mbox_fb[5]; // get actual physical width + fb_info.height = mbox_fb[6]; // get actual physical height + fb_info.pitch = mbox_fb[33]; // get number of bytes per line + fb_info.isrgb = mbox_fb[24]; // get the actual channel order + lfb = mbox_fb[28]; // get pointer to the framebuffer + } else { + uart_log(ERR, "Unable to set screen resolution to 1024x768x32\n"); + } + + return &dev_framebuffer_operation; +} + +int dev_framebuffer_write(file *f, const void *buf, size_t len) { + disable_interrupt(); + if (len + f->f_pos > fb_info.pitch * fb_info.height) { + uart_log(WARN, "Buffer overflow\n"); + len = fb_info.pitch * fb_info.height - f->f_pos; + } + memcpy((void *)(TO_VIRT(lfb) + f->f_pos), buf, len); + f->f_pos += len; + enable_interrupt(); + return len; +} + +int dev_framebuffer_open(vnode *file_node, file *target) { + target->f_pos = 0; + target->vnode = file_node; + target->f_ops = file_node->f_ops; + return 0; +} + +int dev_framebuffer_close(file *f) { + kfree(f, SILENT); + return 0; +} + +long dev_framebuffer_lseek64(file *f, long offset, int whence) { + if (whence == SEEK_SET) { + f->f_pos = offset; + return f->f_pos; + } else if (whence == SEEK_CUR) { + long size = f->f_ops->getsize(f->vnode); + f->f_pos += offset; + if (f->f_pos > size) + f->f_pos = size; + } else + return not_supported(); + return f->f_pos; +} diff --git a/lab7/src/mem.c b/lab7/src/mem.c new file mode 100644 index 000000000..451661496 --- /dev/null +++ b/lab7/src/mem.c @@ -0,0 +1,380 @@ +#include "mem.h" + +#include "command.h" +#include "devtree.h" +#include "hardware.h" +#include "mbox.h" +#include "start.h" +#include "str.h" +#include "uart.h" +#include "utils.h" + +extern char *__bss_end; +extern void *DTB_BASE; +extern void *DTB_END; +extern void *initrd_start; +extern void *initrd_end; + +unsigned int BOARD_REVISION; +unsigned long BASE_MEMORY; +unsigned long TOTAL_MEMORY; +unsigned int NUM_PAGES; + +static struct page *pTable; +static struct page *freeList[BUDDY_MAX_ORDER + 1]; +static struct object *objectCache[CACHE_MAX_ORDER + 1]; + +extern char *__bss_end; // `__bss_end` is defined in linker script + +static char *heap_top; + +static void *simple_malloc(int size) { + void *p = (void *)heap_top; + if (size < 0) return 0; + heap_top += size; + return p; +} + +void init_mem() { + // Simple malloc init: Set heap base address + heap_top = (char *)&__bss_end; + uart_log(INFO, "heap_top = "); + uart_hex((uintptr_t)heap_top); + uart_putc(NEWLINE); + uart_log(INFO, "Simple malloc initialized.\n"); + + unsigned int __attribute__((aligned(16))) mbox[36]; + + // Get board revision + if (BOARD_REVISION == 0) { + get_board_revision(mbox); + BOARD_REVISION = mbox[5]; + } + // Get ARM memory base address and size + if (NUM_PAGES == 0) { + get_arm_memory_status(mbox); + BASE_MEMORY = mbox[5]; + TOTAL_MEMORY = mbox[6]; + NUM_PAGES = (TOTAL_MEMORY - BASE_MEMORY) / PAGE_SIZE; + uart_log(INFO, "Initialized pages: "); + uart_dec(NUM_PAGES); + uart_putc(NEWLINE); + } + + // cmd_info(); + + // Initialize the buddy allocator + int pTableSize = sizeof(struct page) * NUM_PAGES; + pTable = simple_malloc(pTableSize); + uart_log(INFO, "Page Table: "); + uart_hex((uintptr_t)pTable); + uart_putc(NEWLINE); + int current_order = BUDDY_MAX_ORDER; + for (int i = 0; i < NUM_PAGES; i++) { + pTable[i].order = 0; + pTable[i].used = 0; + pTable[i].cache_order = -1; + pTable[i].prev = 0; + pTable[i].next = 0; + if (i % (1 << current_order) == 0) { + while (current_order > 0 && NUM_PAGES - i < (1 << current_order)) { + current_order--; + } + pushPageToFreeList(&freeList[current_order], &pTable[i], current_order); + } + } + + // Reserve memory: + // PGD table + reserveMemory(BOOT_PGD_PAGE_FRAME, BOOT_PGD_PAGE_FRAME + PAGE_SIZE); + // PUD table + reserveMemory(BOOT_PUD_PAGE_FRAME, BOOT_PUD_PAGE_FRAME + PAGE_SIZE); + // PMD table + reserveMemory(BOOT_PMD_PAGE_FRAME, BOOT_PMD_PAGE_FRAME + PAGE_SIZE); + // Kernel & stack; page table + reserveMemory(0x80000 - STACK_SIZE, (uintptr_t)&__bss_end + pTableSize); + // Initramfs + reserveMemory((uintptr_t)initrd_start, (uintptr_t)initrd_end); + // Devicetree + reserveMemory((uintptr_t)DTB_BASE, (uintptr_t)DTB_END); +} + +void reserveMemory(uintptr_t resv_start, uintptr_t resv_end) { + // Round the start and end addresses to the page boundary + resv_start = TO_VIRT(resv_start & ~(PAGE_SIZE - 1)); + resv_end = TO_VIRT((resv_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)); + + uart_log(INFO, "Reserving memory: "); + uart_addr_range(resv_start, resv_end); + uart_putc(NEWLINE); + resv_end--; + + for (int order = BUDDY_MAX_ORDER; order >= 0; order--) { + struct page *current = freeList[order]; + while (current != 0) { + struct page *next = current->next; + uint64_t page_start = page_vaddr(current); + uint64_t page_end = page_start + (PAGE_SIZE << order) - 1; + if (page_start >= resv_start && page_end <= resv_end) { + // [page page] + // Remove the page from the free list + current->used = 1; + removePageFromFreeList(&freeList[order], current); + } else if (resv_start > page_end || resv_end < page_start) { + // ---resv> [page or page] or order = order; + page->used = 0; + page->prev = 0; + page->next = 0; + + if (*list_head == 0 || (*list_head) < page) { + if (*list_head != 0) (*list_head)->prev = page; + page->next = *list_head; + *list_head = page; + return; + } + + struct page *current = *list_head; + while (current->next != 0 && page < current->next) { + current = current->next; + } + page->prev = current; + page->next = current->next; + if (current->next != 0) current->next->prev = page; + current->next = page; +} + +struct page *popFreeList(struct page **list_head) { + if (*list_head == 0) return 0; + + struct page *page = *list_head; + *list_head = page->next; + page->used = 1; + return page; +} + +void removePageFromFreeList(struct page **list_head, struct page *page) { + if (page->prev != 0) page->prev->next = page->next; + if (page->next != 0) page->next->prev = page->prev; + if (page == *list_head) *list_head = page->next; +} + +uintptr_t page_vaddr(struct page *p) { + return TO_VIRT((unsigned long)(p - pTable) * PAGE_SIZE); +} + +uintptr_t page_paddr(struct page *p) { + return TO_PHYS((unsigned long)(p - pTable) * PAGE_SIZE); +} + +void printFreeListByOrder(unsigned int order) { + struct page *page = freeList[order]; + if (page > 0) uart_log(BUDD, ""); + while (page != 0) { + uart_putc(TAB); + uart_addr_range(page_vaddr(page), page_vaddr(page + (1 << order))); + uart_puts(" ["); + if (order < 10) uart_putc(' '); + uart_dec(order); + uart_puts("]\n"); + page = page->next; + } +} + +struct page *lookupBuddy(struct page *page, unsigned int order) { + unsigned int buddy_pfn = (unsigned int)(page - pTable) ^ (1 << order); + return &pTable[buddy_pfn]; +} + +struct page *allocatePagesByOrder(unsigned int order, int silent) { + if (!silent) { + uart_log(BUDD, "Memory block requested: "); + uart_dec(1 << order); + uart_puts(" page(s).\n"); + } + + for (int i = order; i <= BUDDY_MAX_ORDER; i++) { + if (freeList[i] == 0) // No free page available + continue; // Try next order + struct page *page = popFreeList(&freeList[i]); + page->order = order; // Update order of the page + + while (i > order) { // requires splitting + i--; + struct page *buddy = lookupBuddy(page, i); + pushPageToFreeList(&freeList[i], buddy, i); + + if (silent) continue; + // Print information + uart_log(BUDD, "Split "); + uart_addr_range(page_vaddr(page), page_vaddr(page + (1 << i))); + uart_puts("//"); + uart_addr_range(page_vaddr(buddy), page_vaddr(buddy + (1 << i))); + uart_puts(" => ["); + uart_dec(i); + uart_puts("]\n"); + } + if (!silent) { + uart_log(BUDD, "Memory allocated: "); + uart_addr_range(page_vaddr(page), page_vaddr(page + (1 << order))); + uart_putc(NEWLINE); + } + + return page; + } + return 0; +} + +void freePages(struct page *page, unsigned int order, int silence) { + if (!silence) { + uart_log(BUDD, "Free "); + uart_dec(1 << order); + uart_puts("-page memory block starting from "); + uart_hex(page_vaddr(page)); + uart_putc(NEWLINE); + } + mergePages(page, order, silence); +} + +void mergePages(struct page *page, unsigned int order, int silence) { + struct page *current = page; + while (order < BUDDY_MAX_ORDER) { + struct page *buddy = lookupBuddy(current, order); + if (buddy->order != order || buddy->used == 1) break; + + removePageFromFreeList(&freeList[order], buddy); + + if (current > buddy) { + struct page *tmp = current; + current = buddy; + buddy = tmp; + } + + order++; + if (silence) continue; + uart_log(BUDD, "Merge "); + uart_addr_range(page_vaddr(current), + page_vaddr(current + (1 << (order - 1)))); + uart_puts("~~"); + uart_addr_range(page_vaddr(buddy), page_vaddr(buddy + (1 << (order - 1)))); + uart_puts(" => ["); + uart_dec(order); + uart_puts("]\n"); + } + pushPageToFreeList(&freeList[order], current, order); +} + +/* Cache Allocator */ + +void pushObjectToList(struct object **list_head, struct object *object, + unsigned int order) { + object->order = order; + object->next = *list_head; + *list_head = object; +} + +struct object *popObjectFromList(struct object **list_head) { + if (*list_head == 0) return 0; + + struct object *object = *list_head; + *list_head = object->next; + return object; +} + +void *allocateCacheMemory(unsigned int order, int silent) { + if (!silent) { + uart_log(CACH, "Allocating "); + uart_dec(MIN_CACHE_SIZE << order); + uart_puts(" bytes."); + uart_putc(NEWLINE); + } + + if (objectCache[order] == 0) { + struct page *page = allocatePagesByOrder(0, silent); + page->cache_order = order; + uintptr_t page_addr = page_vaddr(page); + int cache_size = MIN_CACHE_SIZE << order; + for (int i = 0; i < PAGE_SIZE; i += cache_size) { + struct object *obj = (struct object *)(uintptr_t)(page_addr + i); + pushObjectToList(&objectCache[order], obj, order); + } + } + void *p = popObjectFromList(&objectCache[order]); + if (!silent) { + uart_log(CACH, "Allocated memory starting from "); + uart_hex((uintptr_t)p); + uart_putc(NEWLINE); + } + return p; +} + +void freeCacheEntry(void *ptr, unsigned int index, int silence) { + if (!silence) { + uart_log(CACH, "Free memory cache of "); + uart_dec(MIN_CACHE_SIZE << index); + uart_puts(" bytes starting from "); + uart_hex((uintptr_t)ptr); + uart_putc(NEWLINE); + } + pushObjectToList(&objectCache[index], ptr, index); +} + +/* Dynamic Memory Allocator */ + +void *kmalloc(unsigned int size, int silent) { + if (size == 0) return 0; + + if (!silent) { + uart_log(INFO, "Memory requested: "); + uart_dec(size); + uart_puts(" byte(s)."); + uart_putc(NEWLINE); + } + + if (size > PAGE_SIZE / 2) { + // Buddy Allocator + int order = 0; + while ((PAGE_SIZE << order) < size) order++; + struct page *page = allocatePagesByOrder(order, silent); + return (void *)page_vaddr(page); + } else { + // Cache Allocator + int power = 0; + while ((1 << power) < size) power++; + int order = (power > 5) ? power - 5 : 0; + return allocateCacheMemory(order, silent); + } +} + +void kfree(void *ptr, int silence) { + // Check if the pointer is page-aligned + struct page *page = &pTable[TO_PHYS(ptr) / PAGE_SIZE]; + if (TO_PHYS(ptr) % PAGE_SIZE == 0) { + // Check if the page is allocated by the buddy allocator + if (page->cache_order == -1) { + // Free the page using the buddy allocator + freePages(page, page->order, silence); + return; + } + } + // Free the object using the cache allocator + struct object *object = ptr; + freeCacheEntry(object, page->cache_order, silence); +} diff --git a/lab7/src/scheduler.S b/lab7/src/scheduler.S new file mode 100644 index 000000000..15fcfbeca --- /dev/null +++ b/lab7/src/scheduler.S @@ -0,0 +1,54 @@ +.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 // set current thread_struct + ret + + /* + struct context_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; + }; + */ + +.global switch_mm +switch_mm: + dsb ish // ensure write has completed + msr ttbr0_el1, x0 // switch translation based address + tlbi vmalle1is // invalidate all TLB entries + dsb ish // ensure completion of TLB invalidation + isb // clear pipeline + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 // current thread_struct + ret + diff --git a/lab7/src/scheduler.c b/lab7/src/scheduler.c new file mode 100644 index 000000000..80e500839 --- /dev/null +++ b/lab7/src/scheduler.c @@ -0,0 +1,237 @@ +#include "scheduler.h" + +#include "irq.h" +#include "mem.h" +#include "start.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +static int IDLE_PROCESS_PID; +static int thread_count = 0; +thread_struct *running_tcircle; + +void sched_init() { + uart_log(INFO, "Initializing Scheduler...\n"); + IDLE_PROCESS_PID = kcreate_thread(idle)->pid; + asm volatile( + "msr tpidr_el1, %0" ::"r"(running_tcircle) // Software Thread ID Register + ); + uart_log(INFO, "Scheduler Initialized.\n"); +} + +static void add_thread(thread_struct **queue, thread_struct *thread) { + if (*queue == 0) { + *queue = thread; + thread->next = thread; + thread->prev = thread; + } else { + // prev <- thread -> *queue + thread->next = *queue; + thread->prev = (*queue)->prev; + // prev -> thread <- *queue + (*queue)->prev->next = thread; + (*queue)->prev = thread; + } +} + +thread_struct *kcreate_thread(void (*func)()) { + thread_struct *thread = kmalloc(sizeof(thread_struct), SILENT); + uart_log(INFO, "Creating thread PID: "); + uart_dec(thread->pid = thread_count++); + uart_putc(NEWLINE); + + thread->start = func ? func : 0; + thread->size = 0; + thread->state = RUNNING; + + thread->stack = kmalloc(STACK_SIZE, SILENT); + uart_log(INFO, "Acquired thread kernel stack at "); + uart_hex((uintptr_t)thread->stack); + uart_putc(NEWLINE); + thread->user_stack = 0; // alloc when needed (el0) + + memset(thread->sig_handlers, 0, sizeof(thread->sig_handlers)); + thread->sig_reg = 0; + thread->sig_busy = 0; + + // Added for lab6 + memset(thread->pgd = kmalloc(PAGE_SIZE, SILENT), 0, PAGE_SIZE); + uart_log(INFO, "Acquired thread pgd at "); + uart_hex((uintptr_t)thread->pgd); + uart_putc(NEWLINE); + + // Added for lab7 + thread->cwd = kmalloc(2, SILENT); + thread->cwd[0] = '/'; + thread->cwd[1] = 0; + + thread->file_descriptor = 0; + thread->file_descriptor_index = 0; + + thread->context.lr = (unsigned long)func; + thread->context.sp = (unsigned long)thread->stack + STACK_SIZE; + thread->context.fp = (unsigned long)thread->stack + STACK_SIZE; + add_thread(&running_tcircle, thread); + return thread; +} + +static void remove_thread(thread_struct **queue, thread_struct *thread) { + if (*queue == thread) + *queue = (thread->next == thread) ? 0 : thread->next; + thread->next->prev = thread->prev; + thread->prev->next = thread->next; +} + +void list_tcircle() { + thread_struct *thread = running_tcircle; + uart_log(INFO, "All thread(s) in queue:\n"); + do { + uart_putc(TAB); + uart_puts("pid = "); + uart_dec(thread->pid); + if (thread->sig_handlers[9] > 0) + uart_putc('*'); + uart_puts(", lr: "); + uart_hex((uintptr_t)thread->start); + uart_puts(", ksp: "); + uart_hex((uintptr_t)thread->context.sp); + if (thread->pid == get_current()->pid) + uart_puts(" <- current"); + uart_putc(NEWLINE); + thread = thread->next; + } while (thread != running_tcircle); +} + +void list_fd(thread_struct *thread) { + file *curr = thread->file_descriptor; + uart_log(INFO, "File descriptors from pid "); + uart_dec(thread->pid); + uart_putc(' '); + uart_hex((uintptr_t)thread); + uart_putc(NEWLINE); + while (curr != 0) { + uart_putc(TAB); + uart_dec(curr->id); + uart_putc(' '); + uart_hex((uintptr_t)curr); + if (curr == thread->file_descriptor) + uart_puts(" <- head"); + uart_putc(NEWLINE); + if (curr == curr->next) + break; + curr = curr->next; + } + uart_putc(TAB); + uart_puts("File descriptor index: "); + uart_dec(thread->file_descriptor_index); + uart_putc(NEWLINE); +} + +void schedule() { + switch_mm((unsigned long)TO_PHYS(get_current()->next->pgd)); + switch_to(get_current(), get_current()->next); +} + +void kill_zombies() { + thread_struct *next, *thread = running_tcircle; + do { + next = thread->next; + if (thread->state == DEAD) { + remove_thread(&running_tcircle, thread); + kfree(thread->stack, SILENT); + if (thread->user_stack) + kfree(thread->user_stack, SILENT); + kfree(thread->pgd, SILENT); + kfree(thread, SILENT); + } + thread = next; + } while (thread != running_tcircle); +} + +void idle() { + while (1) { + kill_zombies(); + schedule(); + } +} + +thread_struct *get_thread_by_pid(int pid) { + thread_struct *thread = running_tcircle; + do { + if (thread->pid == pid) + return thread; + thread = thread->next; + } while (thread != running_tcircle); + return 0; +} + +void kill_current_thread() { + get_current()->state = DEAD; + schedule(); +} + +void kill_thread_by_pid(int pid) { + if (pid == IDLE_PROCESS_PID) { + uart_log(WARN, "Cannot kill idle process.\n"); + } else { + thread_struct *thread = running_tcircle; + do { + if (thread->pid == pid) { + uart_log(INFO, "Killing pid "); + uart_dec(pid); + uart_putc(NEWLINE); + thread->state = DEAD; + schedule(); + return; + } + thread = thread->next; + } while (thread != running_tcircle); + uart_log(WARN, "Nothing to kill.\n"); + } + schedule(); +} + +int push_file_descriptor(thread_struct *thread, file *fd) { + fd->id = thread->file_descriptor_index++; + fd->next = thread->file_descriptor; + if (thread->file_descriptor != 0) { + thread->file_descriptor->prev = fd; + } + thread->file_descriptor = fd; + return fd->id; +} + +file *get_file_descriptor_by_id(thread_struct *thread, int id) { + // uart_log(INFO, "Getting file descriptor from pid "); + // uart_dec(thread->pid); + // uart_puts(" with id "); + // uart_dec(id); + // uart_putc(NEWLINE); + file *fd = thread->file_descriptor; + while (fd != 0) { + if (fd->id == id) { + if (fd != 0) { + // uart_log(INFO, "Found file descriptor: "); + // uart_hex((uintptr_t)fd); + // uart_putc(NEWLINE); + return fd; + } + } + fd = fd->next; + } + return 0; +} + +file *get_file_descriptor_by_vnode(thread_struct *thread, vnode *v) { + file *fd = thread->file_descriptor; + while (fd != 0) { + if (fd->vnode == v) { + if (fd != 0) { + return fd; + } + } + fd = fd->next; + } + return 0; +} \ No newline at end of file diff --git a/lab7/src/shell.c b/lab7/src/shell.c new file mode 100644 index 000000000..6f833e53c --- /dev/null +++ b/lab7/src/shell.c @@ -0,0 +1,84 @@ +#include "shell.h" + +#include "command.h" +#include "mem.h" +#include "str.h" +#include "uart.h" + +static void welcome_msg() { + cmd_info(); + uart_puts( + "*******************************\n" + "*** YADOS 0.07 for OSC 2024 ***\n" + "*******************************\n" + "Hopefully this will be Yet Another Dope OS!\n"); + uart_putc(NEWLINE); + cmd_hello(); +} + +void run_shell() { + welcome_msg(); + + char *buffer = kmalloc(SHELL_BUF_SIZE, SILENT); + while (1) { + uart_putc(NEWLINE); + uart_puts("# "); + read_user_input(buffer); + if (exec_command(buffer)) { + uart_log(WARN, "Command not found.\n"); + } + } +} + +void read_user_input(char *buf) { + int idx = 0; + while (idx < SHELL_BUF_SIZE) { + char c = uart_getc(); + switch (c) { + case NEWLINE: + uart_putc(NEWLINE); + buf[idx] = '\0'; + return; + case DELETE: + if (idx > 0) { + idx--; + uart_putc(BACKSPACE); + uart_putc(' '); + uart_putc(BACKSPACE); + } + break; + case ESC: + uart_putc(NEWLINE); + uart_log(WARN, "ESC detected. Clearing buffer...\n"); + uart_log(INFO, "Press Spacebar or Enter to continue.\n"); + while (1) { + char c = uart_getc(); + if (c == ' ' || c == NEWLINE) break; + } + buf[0] = '\0'; + uart_clear(); + return; + default: + if (c >= 32 && c <= 126) { + uart_putc(c); + buf[idx++] = c; + } + } + } + uart_putc(NEWLINE); + uart_log(WARN, "Buffer overflow. Please re-enter your command.\n"); + buf[0] = '\0'; +} + +int exec_command(const char *input) { + if (strlen(input) == 0) return 0; + struct command *cmd = cmd_list; + while (strcmp(cmd->name, END_OF_COMMAND_LIST)) { + if (!strcmp(cmd->name, input)) { + cmd->func(); + return 0; + } + cmd++; + } + return -1; +} \ No newline at end of file diff --git a/lab7/src/signals.c b/lab7/src/signals.c new file mode 100644 index 000000000..7179952f7 --- /dev/null +++ b/lab7/src/signals.c @@ -0,0 +1,68 @@ +#include "signals.h" + +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "traps.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +void signal(int SIGNAL, void (*handler)()) { + if (SIGNAL < 1 || SIGNAL > NSIG) { + uart_log(ERR, "Invalid signal number.\n"); + return; + } + uart_log(INFO, "Signal handler set for signal "); + uart_dec(SIGNAL); + uart_putc(NEWLINE); + get_current()->sig_handlers[SIGNAL] = handler; +} + +void signal_kill(int pid, int SIGNAL) { + uart_log(INFO, "Signal "); + uart_dec(SIGNAL); + uart_puts(" sent to pid "); + uart_dec(pid); + uart_putc(NEWLINE); + thread_struct *thread = get_thread_by_pid(pid); + if (thread == 0) { + uart_log(WARN, "No one gets the signal.\n"); + return; + }; + thread->sig_reg |= 1 << (SIGNAL - 1); // Set the signal pending bit +} + +void do_signal(trap_frame *tf) { + // Prevent nested signal handling + if (get_current()->sig_busy) return; + int sig_num = 1; + while (get_current()->sig_reg) { + if (get_current()->sig_reg & (0x1 << (sig_num - 1))) { + get_current()->sig_busy = 1; // block other signal handling + get_current()->sig_reg &= ~(0x1 << (sig_num - 1)); + + if (get_current()->sig_handlers[sig_num] == 0) { + kill_current_thread(); // Default handler: kill the process + return; + } + + // Save the sigframe + memcpy(get_current()->sig_tf = kmalloc(sizeof(trap_frame), SILENT), tf, + sizeof(trap_frame)); + get_current()->sig_stack = + kmalloc(STACK_SIZE, SILENT); // free at sys_sigreturn + + // Map the stack for signal handling + map_pages((unsigned long)get_current()->pgd, SIG_STACK, STACK_SIZE, + (unsigned long)TO_PHYS(get_current()->sig_stack)); + + tf->x30 = (uintptr_t)sigreturn; // Return to sigreturn (traps.S) after + tf->spsr_el1 = 0x340; + tf->elr_el1 = (unsigned long)get_current()->sig_handlers[sig_num]; + tf->sp_el0 = (unsigned long)SIG_STACK + STACK_SIZE; // top of sig_stack + return; // eret to the signal handler (->el0) + } + sig_num++; + } +} \ No newline at end of file diff --git a/lab7/src/start.S b/lab7/src/start.S new file mode 100644 index 000000000..015417aff --- /dev/null +++ b/lab7/src/start.S @@ -0,0 +1,139 @@ +#include "start.h" + +.section ".text.boot" + +.global _start +_start: + mov x10, x0 // preserve dtb base address + + /* switch from EL2 to EL1 */ + bl from_el2_to_el1 + +/* --- lab6 --- */ + + /* set up kernel page tables */ + ldr x0, =TCR_CONFIG_DEFAULT + msr tcr_el1, x0 // Translation Control Register + + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 // Memory Attribute Indirection Register + + mov x0, BOOT_PGD_PAGE_FRAME // PGD's page frame + mov x1, BOOT_PUD_PAGE_FRAME // PUD's page frame + mov x2, BOOT_PMD_PAGE_FRAME // PMD's page frame + + // PGD + ldr x6, =BOOT_PGD_ATTR + orr x6, x1, x6 // set PGD->PUD entries + str x6, [x0] + + // PUD + ldr x6, =BOOT_PUD_ATTR + orr x6, x2, x6 // set PUD[0]->PMD entries + str x6, [x1] + + ldr x6, =BOOT_PUD_DEVICE_ATTR + mov x3, 0x40000000 + orr x6, x3, x6 // set PUD[1]->MMIO device addr + str x6, [x1, 8] + + /* + +-------------------+ 0x00000000 + | PGD[0]->PUD[0] | PGD bottom + | | + |-------------------| 0x00001000 + | PUD[0]->PMD[0] | PUD bottom + | PUD[1]>>0x40000000| PUD to MMIO device + | | + |-------------------| 0x00002000 + | PMD[0]>>0x0 | PMD bottom + | PMD[1]>>0x200000 | PMD to normal memory + | PMD[2]>>0x400000 | ... + | ... | + | PMD[_]>>0x3F000000| PMD to MMIO device + | ... | ... + | PMD[_]>>0x3FE00000| PMD top + | | + |-------------------| 0x00003000 + | ... | + + */ + + + // PMD + mov x3, 0x00000000 // PMD block bottom + mov x4, 0x3F000000 // MMIO device bottom + mov x5, 0x40000000 // PMD block top + +pmd_loop: + cmp x3, x5 // reach PMD top? + beq enable_mmu // leave the loop + cmp x3, x4 // reach MMIO device bottom? + ldr x6, =BOOT_PMD_DEVICE_ATTR // yes, set device PMD entry + bge set_pmd_entry + ldr x6, =BOOT_PMD_NORMAL_ATTR // no, set normal PMD entry + +set_pmd_entry: + orr x6, x3, x6 + str x6, [x2], #8 // x2 += 8 + add x3, x3, 0x200000 + b pmd_loop + +enable_mmu: // mmu: memory management unit + msr ttbr0_el1, x0 // [no need?]will be modified by each user process + msr ttbr1_el1, x0 // also load PGD to the upper translation based register + mrs x2, sctlr_el1 // System Control Register + orr x2, x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + + ldr x2, =boot_rest // indirect branch to the virtual address + br x2 + +/* --- lab6^^^ --- */ + +boot_rest: + /* get dtb address from x10 */ + ldr x1, =DTB_BASE // defined in devtree.c + str x10, [x1] + + /* get cpu id */ + mrs x1, mpidr_el1 /* Multiprocessor Affinity Register */ + and x1, x1, #3 + cbnz x1, halt // halt if cpu id != 0 + + /* set exception vector table */ + adr x0, exception_vector_table // defined in traps.S + msr vbar_el1, x0 + + /* set stack pointer */ + ldr x1, =_start + mov sp, x1 + + /* clear bss section */ + ldr x1, =__bss_start + ldr w2, =__bss_size + +bss_reset: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, bss_reset + +run_main: + /* branch to main function */ + bl main + +halt: + wfe + b halt + +from_el2_to_el1: + mov x0, (1 << 31) + msr hcr_el2, x0 // EL1 uses aarch64 + mov x0, 0x3C5 // ... 0011 1100 0101 + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 diff --git a/lab7/src/str.c b/lab7/src/str.c new file mode 100644 index 000000000..ac88615dd --- /dev/null +++ b/lab7/src/str.c @@ -0,0 +1,33 @@ +#include "str.h" + +/** + * Compare two strings, return 0 if they are equal, or otherwise. + */ +int strcmp(const char *str1, const char *str2) { + while (*str1 && *str1 == *str2) str1++, str2++; + return *(unsigned char *)str1 - *(unsigned char *)str2; +} + +char *strncpy(char *dest, const char *src, int n) { + while (n-- && (*dest++ = *src++)); + return dest; +} + +int strlen(const char *str) { + int len = 0; + while (*str++ != '\0') len++; + return len; +} + +char *strcat(char *dest, const char *src) { + char *d = dest; + + // Move the pointer to the end of the dest string + while (*d != '\0') d++; + // Copy the src string to the end of the dest string + while (*src != '\0') *d++ = *src++; + // Add the null terminator + *d = '\0'; + + return dest; +} diff --git a/lab7/src/syscalls.c b/lab7/src/syscalls.c new file mode 100644 index 000000000..aa43d245c --- /dev/null +++ b/lab7/src/syscalls.c @@ -0,0 +1,204 @@ +#include "syscalls.h" + +#include "initramfs.h" +#include "irq.h" +#include "mem.h" +#include "scheduler.h" +#include "str.h" +#include "traps.h" +#include "uart.h" +#include "utils.h" +#include "virtm.h" + +int sys_getpid() { return get_current()->pid; } + +size_t sys_uart_read(char *buf, size_t size) { + size_t i = 0; + while (i < size) + buf[i++] = uart_getc(); + return i; +} + +size_t sys_uart_write(const char *buf, size_t size) { + size_t i = 0; + while (i < size) + uart_putc(buf[i++]); + return i; +} + +int sys_exec(const char *name, trap_frame *tf) { + thread_struct *thread = get_current(); + char *p = absolute_path(name, thread->cwd); + vnode *target_file; + vfs_lookup(p, &target_file); // assign the vnode to target + kfree(p, SILENT); + vfs_exec(target_file, tf); + return 0; +} + +extern void *lfb; + +int sys_fork(trap_frame *tf) { + disable_interrupt(); // prevent schedule premature fork + + thread_struct *parent = get_current(); + thread_struct *child = kcreate_thread(0); + + child->start = parent->start; + child->size = parent->size; + + // list_fd(parent); + file *fd = parent->file_descriptor; + while (fd) { + file *f = create_file(); + memcpy(f, fd, sizeof(file)); + push_file_descriptor(child, f); + f->id = fd->id; + fd = fd->next; + } + // list_fd(child); + + child->file_descriptor_index = parent->file_descriptor_index; + + child->user_stack = kmalloc(STACK_SIZE, SILENT); + + mapping_user_thread(child, 0x3000000); + + // Handling kernel stack (incl. tf from parent) + memcpy(child->stack, parent->stack, STACK_SIZE); + memcpy(child->user_stack, parent->user_stack, STACK_SIZE); + + // Copy signal handlers + memcpy(child->sig_handlers, parent->sig_handlers, + sizeof(parent->sig_handlers)); + + // get child's trap frame from kstack via parent's offset + size_t ksp_offset = (uintptr_t)tf - (uintptr_t)parent->stack; + trap_frame *child_tf = (trap_frame *)(child->stack + ksp_offset); + + child->context.lr = (uintptr_t)child_ret_from_fork; // traps.S + child->context.sp = (uintptr_t)child_tf; // set child's ksp + child->context.fp = (uintptr_t)child_tf; // set child's ksp + child_tf->sp_el0 = tf->sp_el0; // set child's user sp + child_tf->x0 = 0; // ret value of fork() for child + + enable_interrupt(); + return child->pid; +} + +void sys_exit(int status) { + uart_log(status ? WARN : INFO, "Exiting process ...\n"); + kill_current_thread(); +} + +int sys_mbox_call(unsigned char ch, unsigned int *mbox) { + unsigned int *itm = kmalloc(mbox[0], SILENT); + memcpy(itm, mbox, mbox[0]); + int ret = mbox_call(ch, (unsigned int *)itm); + memcpy(mbox, itm, mbox[0]); + kfree(itm, SILENT); + return ret; +} + +void sys_sigreturn(trap_frame *tf) { + memcpy(tf, get_current()->sig_tf, sizeof(trap_frame)); + kfree(get_current()->sig_tf, SILENT); + kfree(get_current()->sig_stack, SILENT); + get_current()->sig_busy = 0; + return; +} + +int sys_open(const char *pathname, int flags) { + thread_struct *thread = get_current(); + char *p = absolute_path(pathname, thread->cwd); + file *f = create_file(); + if (vfs_open(p, flags, f)) + return -1; + // list_fd(thread); + file *existing = get_file_descriptor_by_vnode(thread, f->vnode); + // uart_log(DEBUG, "existing: "); + // uart_hex((uintptr_t)existing); + // uart_putc(NEWLINE); + int id = (flags & O_CREAT) || !existing ? push_file_descriptor(thread, f) + : existing->id; + uart_log(VFS, "open: "); + uart_puts(p); + uart_puts(", id: "); + uart_dec(id); + uart_puts(", flags: "); + uart_simple_hex(flags); + uart_putc(NEWLINE); + kfree(p, SILENT); + return id; +} + +int sys_close(int fd) { + thread_struct *thread = get_current(); + file *f = get_file_descriptor_by_id(thread, fd); + // list_fd(thread); + if (f == 0) + return -1; + if (thread->file_descriptor == f) + thread->file_descriptor = f->next; + else { + f->prev->next = f->next; + if (f->next) + f->next->prev = f->prev; + } + // list_fd(thread); + return vfs_close(f); +} + +int sys_write(int fd, void *buf, size_t count) { + file *f = get_file_descriptor_by_id(get_current(), fd); + if (f == 0) + return -1; + return vfs_write(f, buf, count); +} + +int sys_read(int fd, void *buf, size_t count) { + // list_fd(get_current()); + file *f = get_file_descriptor_by_id(get_current(), fd); + if (f == 0) + return -1; + return vfs_read(f, buf, count); +} + +int sys_mkdir(const char *pathname, unsigned mode) { + char *p = absolute_path(pathname, get_current()->cwd); + return vfs_mkdir(p); +} + +int sys_mount(const char *target, const char *filesystem) { + char *p = absolute_path(target, get_current()->cwd); + int ret = vfs_mount(p, filesystem); + return ret; +} + +int sys_chdir(const char *path) { + char *p = absolute_path(path, get_current()->cwd); + char *cwd = get_current()->cwd; + get_current()->cwd = p; + kfree(cwd, SILENT); + return 0; +} + +long sys_lseek64(int fd, long offset, int whence) { + file *f = get_file_descriptor_by_id(get_current(), fd); + if (f == 0) + return -1; + return f->f_ops->lseek64(f, offset, whence); +} + +extern framebuffer_info *fb_info; + +// 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 +int sys_ioctl(int fd, unsigned long request, void *info) { + if (request == GET_INFO) { + memcpy(info, &fb_info, sizeof(framebuffer_info)); + return 0; + } + return not_supported(); +} diff --git a/lab7/src/timer.c b/lab7/src/timer.c new file mode 100644 index 000000000..ebd9a849c --- /dev/null +++ b/lab7/src/timer.c @@ -0,0 +1,117 @@ +#include "timer.h" + +#include "mem.h" +#include "str.h" +#include "uart.h" + +static timerEntry *head = 0; + +void init_timer() { + asm volatile( + "mov x0, 1\n" + "msr cntp_ctl_el0, x0\n"); + + uart_log(INFO, "enable_timer_interrupt()\n"); + enable_timer_interrupt(); + + asm volatile( + "mrs x0, cntfrq_el0\n" + "msr cntp_tval_el0, x0\n"); + + // required by lab5 + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); + + uart_log(INFO, "Timer IRQ frequency factor = "); + uart_dec(TIMER_SPEED); + uart_putc(NEWLINE); +} + +void enable_timer_interrupt() { + // Unmask the timer interrupt + asm volatile( + "mov x0, 2\n" + "ldr x1, =%0\n" + "str w0, [x1]\n" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void disable_timer_interrupt() { + // Mask timer interrupt + asm volatile( + "mov x0, 0\n" + "ldr x1, =%0\n" + "str w0, [x1]\n" + : + : "i"(CORE0_TIMER_IRQCNTL) + : "x0", "x1"); +} + +void timer_irq_handler() { + // Set up the next timer interrupt + asm volatile( + "mrs x0, cntfrq_el0\n" + "lsr x0, x0, %0\n" + "msr cntp_tval_el0, x0\n" + : + : "i"(TIMER_SPEED) // frequency >> 5, aka. 1/32 + : "x0"); + + // Check the timer queue + while (head != 0 && timer_get_uptime() >= head->time) { + head->func(head->arg); // Execute the callback function + kfree(head, SILENT); // Free the memory allocated for the timer node + head = head->next; // Remove the head node after func finished + } + + enable_timer_interrupt(); +} + +uint64_t timer_get_uptime() { + uint64_t cntpct_el0 = 0, cntfrq_el0 = 0; + asm volatile( + "mrs %0, cntpct_el0\n" + "mrs %1, cntfrq_el0\n" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + return cntpct_el0 / cntfrq_el0; +} + +void timer_add(void (*callback)(void *), void *arg, int duration) { + // Insert a new timer into the linked list (sorted by time) + timerEntry *timer = kmalloc(sizeof(timerEntry), SILENT); + timer->func = callback; + timer->arg = arg; + timer->time = timer_get_uptime() + duration; + timer->next = 0; + + if (head == 0 || timer->time < head->time) { + // Insert as the head of the list + timer->next = head; + head = timer; + return; + } + + timerEntry *current = head; + while (current->next != 0 && current->next->time <= timer->time) + current = current->next; + timer->next = current->next; + current->next = timer; +} + +static void show_timer_message(void *arg) { + uart_putc(NEWLINE); + uart_log(INFO, "Timer message: "); + uart_puts((char *)arg); + uart_putc(NEWLINE); + kfree(arg, SILENT); // Free the memory allocated for arg +} + +void set_timer(const char *msg, int duration) { + timer_add((void (*)(void *))show_timer_message, (void *)msg, duration); +} + +void set_timeup(int *timeup) { *timeup = 1; } diff --git a/lab7/src/tmpfs.c b/lab7/src/tmpfs.c new file mode 100644 index 000000000..8ec1695ae --- /dev/null +++ b/lab7/src/tmpfs.c @@ -0,0 +1,174 @@ +#include "tmpfs.h" + +#include "mem.h" +#include "str.h" +#include "uart.h" +#include "utils.h" +#include "vfs.h" + +file_operations tmpfs_file_operations = { + tmpfs_write, // + tmpfs_read, // + tmpfs_open, // + tmpfs_close, // + tmpfs_lseek64, // + tmpfs_getsize // +}; + +vnode_operations tmpfs_vnode_operations = { + tmpfs_lookup, // + tmpfs_create, // + tmpfs_mkdir // +}; + +int tmpfs_setup_mount(filesystem *fs, mount *mnt) { + mnt->fs = fs; + mnt->root = tmpfs_create_vnode(dir_t); + return 0; +} + +vnode *tmpfs_create_vnode(fsnode_type type) { + vnode *v = kmalloc(sizeof(vnode), SILENT); + v->f_ops = &tmpfs_file_operations; + v->v_ops = &tmpfs_vnode_operations; + v->mount = 0; + + tmpfs_inode *inode = kmalloc(sizeof(tmpfs_inode), SILENT); + memset(inode, 0, sizeof(tmpfs_inode)); + inode->type = type; + inode->data = kmalloc(PAGE_SIZE, SILENT); + v->internal = inode; + + return v; +} + +int tmpfs_write(file *file, const void *buf, size_t len) { + tmpfs_inode *inode = file->vnode->internal; + memcpy(inode->data + file->f_pos, buf, len); + file->f_pos += len; + if (inode->datasize < file->f_pos) + inode->datasize = file->f_pos; + return len; +} + +int tmpfs_read(file *file, void *buf, size_t len) { + tmpfs_inode *inode = file->vnode->internal; + if (len + file->f_pos > inode->datasize) { + len = inode->datasize - file->f_pos; + } + + strncpy(buf, inode->data + file->f_pos, len); + file->f_pos += inode->datasize - file->f_pos; + return len; +} + +int tmpfs_open(vnode *file_node, file *target) { + target->vnode = file_node; + target->f_ops = file_node->f_ops; + target->f_pos = 0; + return 0; +} + +int tmpfs_close(file *f) { + kfree(f, SILENT); + return 0; +} + +// long seek 64-bit +long tmpfs_lseek64(file *f, long offset, int whence) { + // uart_log(DEBUG, "tmpfs_lseek64\n"); + if (whence == SEEK_SET) { + // return current position as offset + f->f_pos = offset; + } else if (whence == SEEK_CUR) { + // return current position + offset + long size = f->f_ops->getsize(f->vnode); + f->f_pos += offset; + if (f->f_pos > size) + f->f_pos = size; + } else + return not_supported(); + return f->f_pos; +} + +long tmpfs_getsize(vnode *vn) { + tmpfs_inode *inode = vn->internal; + return inode->datasize; +} + +int tmpfs_lookup(vnode *dir_node, vnode **target, const char *component_name) { + tmpfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + + // BFS search tree + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) { + vnode *vnode_it = + dir_inode->entry[child_idx]; // entry stores all sub folder/file + if (!vnode_it) + break; + tmpfs_inode *inode_it = vnode_it->internal; + if (strcmp(component_name, inode_it->name) == 0) { + *target = vnode_it; + return 0; + } + } + return -1; +} + +int tmpfs_create(vnode *dir_node, vnode **target, const char *component_name) { + tmpfs_inode *inode = dir_node->internal; + if (inode->type != dir_t) { + uart_puts("[tmpfs create] not a dir\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) { + if (!inode->entry[child_idx]) + break; + tmpfs_inode *child_inode = inode->entry[child_idx]->internal; + if (!strcmp(child_inode->name, component_name)) { + uart_log(WARN, "tmpfs_create: file exists\n"); + return -1; + } + } + + vnode *vn = tmpfs_create_vnode(file_t); + inode->entry[child_idx] = vn; + + tmpfs_inode *new_inode = vn->internal; + new_inode->name = kmalloc(strlen(component_name) + 1, SILENT); + strncpy(new_inode->name, component_name, strlen(component_name) + 1); + + *target = vn; + return 0; +} + +int tmpfs_mkdir(vnode *dir_node, vnode **target, const char *component_name) { + tmpfs_inode *inode = dir_node->internal; + if (inode->type != dir_t) { + uart_puts("[tmpfs mkdir] not a directory\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx < MAX_DIR_ENTRY; child_idx++) { + if (!inode->entry[child_idx]) + break; + } + + if (child_idx > MAX_DIR_ENTRY) { + uart_puts("[tmpfs mkdir] dir entry full\n"); + return -1; + } + + vnode *vn = tmpfs_create_vnode(dir_t); + inode->entry[child_idx] = vn; + + tmpfs_inode *new_inode = vn->internal; + new_inode->name = kmalloc(strlen(component_name) + 1, SILENT); + strncpy(new_inode->name, component_name, strlen(component_name) + 1); + + *target = vn; + return 0; +} \ No newline at end of file diff --git a/lab7/src/traps.S b/lab7/src/traps.S new file mode 100644 index 000000000..f0bfd215c --- /dev/null +++ b/lab7/src/traps.S @@ -0,0 +1,117 @@ +/* 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 // Saved Program Status Register (EL1) + mrs x11, elr_el1 // Exception Link Register (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 // pass the stack pointer to exception_entry + 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 // Exception Link Register (EL1) + mrs x1, esr_el1 // ESR_EL1, Exception Syndrome Register (EL1) + mrs x2, spsr_el1 // Saved Program Status Register (EL1) + mrs x3, ttbr0_el1 // Translation Table Base Register 0 (EL1) + mrs x4, ttbr1_el1 // Translation Table Base Register 1 (EL1) + mrs x5, far_el1 // Fault Address Register, EL1 + bl invalid_entry // defined in traps.c + load_all + eret + +.global child_ret_from_fork +child_ret_from_fork: + load_all // child's kernel stack (trap frame) + eret + +.global sigreturn +sigreturn: + mov x8, 139 + svc 0 + +.macro v_entry 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 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL1 -> EL1 while using SP_EL1 + v_entry invalid_exc_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch64) -> EL1 + v_entry exception_handler + v_entry irq_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + // EL0 (AArch32) -> EL1 + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler + v_entry invalid_exc_handler diff --git a/lab7/src/traps.c b/lab7/src/traps.c new file mode 100644 index 000000000..8a16345c0 --- /dev/null +++ b/lab7/src/traps.c @@ -0,0 +1,228 @@ +#include "traps.h" + +#include + +#include "irq.h" +#include "mbox.h" +#include "mem.h" +#include "scheduler.h" +#include "syscalls.h" +#include "uart.h" +#include "virtm.h" + +extern thread_struct *running_tcircle; +extern uintptr_t lfb; + +static void print_registers(uint64_t elr, uint64_t esr, uint64_t spsr, + uint64_t ttbr0_el1, uint64_t ttbr1_el1, + uint64_t far_el1) { + // Print spsr_el1 + uart_log(INFO, "spsr_el1: "); + uart_hex(spsr); + uart_putc(NEWLINE); + + // Print elr_el1 + uart_log(INFO, "elr_el1: "); + uart_hex(elr); + uart_putc(NEWLINE); + + // Print esr_el1 + uart_log(INFO, "esr_el1: "); + uart_hex(esr); + uart_putc(NEWLINE); + + // Print ttbr0_el1 + uart_log(INFO, "ttbr0_el1: "); + uart_hex(ttbr0_el1); + uart_putc(NEWLINE); + + // Print ttbr1_el1 + uart_log(INFO, "ttbr1_el1: "); + uart_hex(ttbr1_el1); + uart_putc(NEWLINE); + + // Print far_el1 + uart_log(INFO, "far_el1: "); + uart_hex(far_el1); + uart_putc(NEWLINE); +} + +void exception_entry(trap_frame *tf) { + unsigned long elr, esr, spsr, ttbr0_el1, ttbr1_el1, far_el1; + asm volatile("mrs %0, elr_el1\n" + "mrs %1, esr_el1\n" + "mrs %2, spsr_el1\n" + "mrs %3, ttbr0_el1\n" + "mrs %4, ttbr1_el1\n" + "mrs %5, far_el1\n" + : "=r"(elr), // + "=r"(esr), // + "=r"(spsr), // + "=r"(ttbr0_el1), // + "=r"(ttbr1_el1), // + "=r"(far_el1) // + : + : "memory"); + if (esr != 0x56000000) { + print_registers(elr, esr, spsr, ttbr0_el1, ttbr1_el1, far_el1); + while (1) + ; + } + + enable_interrupt(); // Prevent uart blocking the interrupt[?] + + unsigned int syscall_num = tf->x8; + switch (syscall_num) { + case 0: { // int getpid() + unsigned int test = tf->x7; + unsigned int pid = sys_getpid(); + tf->x0 = pid; + if (test != 255) { + list_tcircle(); + list_fd(get_current()); + } + break; + } + case 1: { // size_t uartread(char buf[], size_t size) + char *buf = (char *)tf->x0; + size_t size = tf->x1; + tf->x0 = sys_uart_read(buf, size); + break; + } + case 2: { // size_t uartwrite(const char buf[], size_t size) + char *buf = (char *)tf->x0; + size_t size = tf->x1; + tf->x0 = sys_uart_write(buf, size); + break; + } + case 3: { // int exec(const char *name, char *const argv[]) + const char *name = (char *)tf->x0; + tf->x0 = sys_exec(name, tf); + tf->elr_el1 = 0x0; + tf->sp_el0 = USER_STACK + STACK_SIZE; + break; + } + case 4: { // int fork() + tf->x0 = sys_fork(tf); + list_tcircle(); + break; + } + case 5: { // void exit(int status) + int status = tf->x0; + sys_exit(status); + break; + } + case 6: { // int mbox_call(unsigned char ch, unsigned int *mbox) + unsigned char ch = tf->x0; + unsigned int *mbox = (unsigned int *)tf->x1; + tf->x0 = sys_mbox_call(ch, mbox); + break; + } + case 7: { // void kill(int pid) + unsigned int pid = tf->x0; + kill_thread_by_pid(pid); + break; + } + case 8: { // signal(int SIGNAL, void (*handler)()) + unsigned int SIGNAL = tf->x0; + void (*handler)() = (void (*)())tf->x1; + signal(SIGNAL, handler); + break; + } + case 9: { // (signal_)kill(int pid, int SIGNAL) + unsigned int pid = tf->x0; + unsigned int SIGNAL = tf->x1; + signal_kill(pid, SIGNAL); + break; + } + case 11: { // int open(const char *pathname, int flags) + const char *pathname = (char *)tf->x0; + int flags = tf->x1; + tf->x0 = sys_open(pathname, flags); + break; + } + case 12: { // int close(int fd) + int fd = tf->x0; + tf->x0 = sys_close(fd); + break; + } + case 13: { // int write(int fd, void *buf, size_t count) + int fd = tf->x0; + void *buf = (void *)tf->x1; + size_t count = tf->x2; + tf->x0 = sys_write(fd, buf, count); + break; + } + case 14: { // int read(int fd, void *buf, size_t count) + int fd = tf->x0; + void *buf = (void *)tf->x1; + size_t count = tf->x2; + tf->x0 = sys_read(fd, buf, count); + break; + } + case 15: { // int mkdir(const char *pathname, unsigned mode) + const char *pathname = (char *)tf->x0; + unsigned mode = tf->x1; + tf->x0 = sys_mkdir(pathname, mode); + break; + } + case 16: { // int mount(const char *src, const char *target, + // const char *filesystem, unsigned long flags, + // const void *data) + // const char *src = (char *)tf->x0; + const char *target = (char *)tf->x1; + const char *fs = (char *)tf->x2; + tf->x0 = sys_mount(target, fs); //, flags, data); + break; + } + case 17: { // int chdir(const char *path) + const char *path = (char *)tf->x0; + tf->x0 = sys_chdir(path); + break; + } + case 18: { // long lseek(int fd, off_t offset, int whence) + int fd = tf->x0; + long offset = tf->x1; + int whence = tf->x2; + tf->x0 = sys_lseek64(fd, offset, whence); + break; + } + case 19: { // int ioctl(int fd, unsigned long request, ...) + int fd = tf->x0; + unsigned long request = tf->x1; + void *info = (void *)tf->x2; + tf->x0 = sys_ioctl(fd, request, info); + if (request == GET_INFO) { + uart_log(INFO, "GET_INFO lfb = "); + uart_hex(lfb); + uart_putc(NEWLINE); + } + break; + } + case 139: { + sys_sigreturn(tf); + break; + } + default: + uart_log(ERR, "Invalid system call "); + uart_dec(syscall_num); + uart_putc(NEWLINE); + } +} + +void invalid_entry(uint64_t elr, uint64_t esr, uint64_t spsr, + uint64_t ttbr0_el1, uint64_t ttbr1_el1, uint64_t far_el1) { + uart_log(ERR, "The exception handler is not implemented\n"); + print_registers(elr, esr, spsr, ttbr0_el1, ttbr1_el1, far_el1); + + if ((esr & 0xFC000000) == 0x94000000) { + uint32_t svc_number = esr & 0xFFFF; + uart_log(ERR, "SVC Call Exception. SVC Number: "); + uart_dec(svc_number); + uart_putc(NEWLINE); + } else { + uart_log(ERR, "Unknown Exception\n"); + } + while (1) + ; +} \ No newline at end of file diff --git a/lab7/src/uart.c b/lab7/src/uart.c new file mode 100644 index 000000000..f4b6b6341 --- /dev/null +++ b/lab7/src/uart.c @@ -0,0 +1,259 @@ +#include "uart.h" + +#include "irq.h" +#include "mem.h" + +static int uart_read_idx = 0; +static int uart_write_idx = 0; +static char uart_read_buffer[UART_BUF_SIZE]; +static char uart_write_buffer[UART_BUF_SIZE]; + +void init_uart() { + // Configure GPIO pins + register unsigned int r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); + r |= (2 << 12) | (2 << 15); + *GPFSEL1 = r; + + // Disable GPIO pull up/down + *GPPUD = 0; + for (int i = 0; i < 150; i++) + ; + *GPPUDCLK0 = (1 << 14) | (1 << 15); + for (int i = 0; i < 150; i++) + ; + *GPPUD = 0; + *GPPUDCLK0 = 0; + + // Initialize mini UART + *AUX_ENABLE |= 1; // Enable mini UART + *AUX_MU_CNTL = 0; // Disable Tx and Rx during setup + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set data size to 8 bits + *AUX_MU_MCR = 0; // Disable auto flow control + *AUX_MU_BAUD = 270; // Set baud rate to 115200 + *AUX_MU_IIR = 6; // No FIFO + *AUX_MU_CNTL = 3; // Enable Tx and Rx + + // Enable AUX interrupts + *ENABLE_IRQS_1 |= 1 << 29; +} + +char uart_getc() { + // Check the data ready field on bit 0 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x01)) + ; + char c = (char)(*AUX_MU_IO); // Read from AUX_MU_IO_REG + return c == CARRIAGE_RETURN ? NEWLINE : c; +} + +void uart_putc(char c) { + // Add CARRIAGE_RETURN before NEWLINE + if (c == NEWLINE) + uart_putc(CARRIAGE_RETURN); + + // Check the transmitter empty field on bit 5 of AUX_MU_LSR_REG + while (!(*AUX_MU_LSR & 0x20)) + ; + *AUX_MU_IO = c; // Write to AUX_MU_IO_REG +} + +void uart_puts(const char *s) { + while (*s) { + uart_putc(*s++); + } +} + +void uart_clear() { uart_puts("\033[2J\033[H"); } + +void uart_hex(unsigned long h) { + uart_puts("0x"); + unsigned long n; + int length = h > 0xFFFFFFFF ? 60 : 28; + for (int c = length; c >= 0; c -= 4) { + n = (h >> c) & 0xF; + n += n > 9 ? 0x37 : '0'; + uart_putc(n); + if (c / 4 % 8 == 0 && c > 0) + uart_putc(' '); + } +} + +void uart_addr_range(uintptr_t start, uintptr_t end) { + uart_hex(start); + uart_puts("-"); + uart_hex(end - 1); +} + +void uart_simple_hex(unsigned int h) { + uart_puts("0x"); + if (h == 0) { + uart_putc('0'); + return; + } + char buf[10]; + int i = 0; + unsigned int n; + while (h > 0) { + n = h % 16; + buf[i++] = n + (n > 9 ? 0x37 : '0'); + h /= 16; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_dec(unsigned int d) { + if (d == 0) { + uart_putc('0'); + return; + } + char buf[12]; + int i = 0; + while (d > 0) { + buf[i++] = d % 10 + '0'; + d /= 10; + } + for (int j = i - 1; j >= 0; j--) { + uart_putc(buf[j]); + } +} + +void uart_log(int type, const char *msg) { + switch (type) { + case INFO: + uart_puts("[INFO] "); + break; + case TEST: + uart_puts("[TEST] "); + break; + case BUDD: + uart_puts("[BUDD] "); + break; + case CACH: + uart_puts("[CACH] "); + break; + case VFS: + uart_puts("[VFS ] "); + break; + case MMAP: + uart_puts("[MMAP] "); + break; + case SCALL: + uart_puts("[SYSC] "); + break; + case WARN: + uart_puts("[WARN] "); + break; + case DEBUG: + uart_puts("[DBUG] "); + break; + case ERR: + uart_puts("[ERR!] "); + break; + default: + uart_puts("[LOG?] "); + break; + } + uart_puts(msg); +} + +void enable_uart_tx_interrupt() { *AUX_MU_IER |= 0x02; } + +void disable_uart_tx_interrupt() { *AUX_MU_IER &= 0x01; } + +void enable_uart_rx_interrupt() { *AUX_MU_IER |= 0x01; } + +void disable_uart_rx_interrupt() { *AUX_MU_IER &= 0x2; } + +void uart_tx_irq_handler() { + disable_uart_tx_interrupt(); + // Buffer is not empty -> send characters + if (uart_write_idx < UART_BUF_SIZE && + uart_write_buffer[uart_write_idx] != 0) { + while (!(*AUX_MU_LSR & 0x20)) + ; + *AUX_MU_IO = uart_write_buffer[uart_write_idx++]; + enable_uart_tx_interrupt(); + } +} + +void uart_rx_irq_handler() { + while (!(*AUX_MU_LSR & 0x01)) + ; + char c = (char)(*AUX_MU_IO); + uart_read_buffer[uart_read_idx++] = (c == CARRIAGE_RETURN) ? NEWLINE : c; + if (uart_read_idx >= UART_BUF_SIZE) + uart_read_idx = 0; + enable_uart_rx_interrupt(); +} + +void uart_async_read(char *buf, int len) { + disable_uart_rx_interrupt(); + for (int i = 0; i < uart_read_idx && i < len; i++) + buf[i] = uart_read_buffer[i]; + buf[uart_read_idx] = 0; + uart_read_idx = 0; +} + +void uart_async_write(const char *s) { + // Copy string to the write buffer + int len = 0; + while (*s != '\0') { + if (len >= UART_BUF_SIZE) { + uart_log(ERR, "Exceed the UART buffer size\n"); + return; + } + if (*s == NEWLINE) { + // Insert CARRIAGE_RETURN before NEWLINE + uart_write_buffer[len++] = CARRIAGE_RETURN; + uart_write_buffer[len++] = NEWLINE; + } else + uart_write_buffer[len++] = *s; + s++; + } + uart_write_buffer[len] = '\0'; + uart_write_idx = 0; // Reset the buffer index + enable_uart_tx_interrupt(); +} + +file_operations dev_file_operations = { + dev_uart_write, // + dev_uart_read, // + dev_uart_open, // + dev_uart_close, // + (void *)not_supported, // lseek64 + (void *)not_supported // getsize +}; + +file_operations *init_dev_uart() { return &dev_file_operations; } + +int dev_uart_write(file *file, const void *buf, size_t len) { + // uart_log(DEBUG, "dev_uart_write\n"); + const char *char_buf = buf; + for (int i = 0; i < len; i++) + uart_putc(char_buf[i]); + return len; +} + +int dev_uart_read(file *file, void *buf, size_t len) { + // uart_log(DEBUG, "dev_uart_read\n"); + char *char_buf = buf; + for (int i = 0; i < len; i++) + char_buf[i] = uart_getc(); + return len; +} + +int dev_uart_open(vnode *file_node, file *target) { + // uart_log(DEBUG, "dev_uart_open\n"); + target->vnode = file_node; + target->f_ops = file_node->f_ops; //&dev_file_operations; + return 0; +} + +int dev_uart_close(file *file) { + // uart_log(DEBUG, "dev_uart_close\n"); + kfree(file, SILENT); + return 0; +} diff --git a/lab7/src/utils.c b/lab7/src/utils.c new file mode 100644 index 000000000..dec35665b --- /dev/null +++ b/lab7/src/utils.c @@ -0,0 +1,71 @@ +#include "utils.h" + +int align4(int n) { return n + (4 - n % 4) % 4; } + +int atoi(const char *s) { + int result = 0; + int sign = 1; + int i = 0; + + // Skip leading spaces + while (s[i] == ' ') { + i++; + } + + // Handle positive and negative sign + if (s[i] == '-') { + sign = -1; + i++; + } else if (s[i] == '+') { + i++; + } + + // Convert string to integer + while (s[i] >= '0' && s[i] <= '9') { + result = result * 10 + (s[i] - '0'); + i++; + } + + return sign * result; +} + +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; +} + +int memcmp(const void *m1, const void *m2, int n) { + const unsigned char *a = m1, *b = m2; + while (n-- > 0) { + if (*a != *b) return *a - *b; + a++; + b++; + } + return 0; +} + +/** + * @brief Copy `n` bytes from `src` to `dest`. + */ +void *memcpy(void *dest, const void *src, int n) { + char *d = dest; + const char *s = src; + while (n--) *d++ = *s++; + return dest; +} + +/** + * @brief Set `n` bytes of `s` to `c`. + */ +void *memset(void *s, int c, int n) { + unsigned char *p = s; + while (n--) *p++ = (unsigned char)c; + return s; +} \ No newline at end of file diff --git a/lab7/src/vfs.c b/lab7/src/vfs.c new file mode 100644 index 000000000..4895b3616 --- /dev/null +++ b/lab7/src/vfs.c @@ -0,0 +1,309 @@ +#include "vfs.h" + +#include "initramfs.h" +#include "mbox.h" +#include "mem.h" +#include "scheduler.h" +#include "str.h" +#include "tmpfs.h" +#include "uart.h" +#include "utils.h" +#include "vfs.h" +#include "virtm.h" + +extern file_operations dev_file_operations; + +mount *rootfs; +filesystem *reg_fs = 0; + +void init_vfs() { + register_filesystem("tmpfs", tmpfs_setup_mount); + rootfs = kmalloc(sizeof(mount), SILENT); + reg_fs->setup_mount(reg_fs, rootfs); + + vfs_mkdir("/initramfs"); + register_filesystem("initramfs", initramfs_setup_mount); + vfs_mount("/initramfs", "initramfs"); + + vfs_mkdir("/dev"); + vfs_mknod("/dev/uart")->f_ops = init_dev_uart(); + vfs_mknod("/dev/framebuffer")->f_ops = init_dev_framebuffer(); +} + +vnode *vfs_mknod(char *pathname) { + uart_log(VFS, "Making node: "); + uart_puts(pathname); + uart_putc(NEWLINE); + file *f = create_file(); + vfs_open(pathname, O_CREAT, f); + vnode *node = f->vnode; + vfs_close(f); + return node; +} + +filesystem *register_filesystem(char *name, void *setup_mount) { + // register the file system to the kernel. + // you can also initialize memory pool of the file system here. + uart_log(VFS, "Registering filesystem: "); + uart_puts(name); + uart_putc(NEWLINE); + filesystem *fs = kmalloc(sizeof(filesystem), VERBAL); + fs->name = kmalloc(strlen(name) + 1, SILENT); + strncpy(fs->name, name, strlen(name) + 1); + fs->setup_mount = setup_mount; + fs->next = reg_fs; + reg_fs = fs; + + filesystem *tmp = reg_fs; + while (tmp) { + tmp = tmp->next; + } + return fs; +} + +int vfs_open(const char *pathname, int flags, file *target) { + // 1. Lookup pathname + // 2. Create a new file handle for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags and vnode not found + // lookup error code shows if file exist or not or other error occurs + // 4. Return error code if fails + int path_len = strlen(pathname); + + vnode *node; + if (vfs_lookup(pathname, &node)) { // file not found + if (flags & O_CREAT) { + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) { + if (pathname[i] == '/') + last_slash_idx = i; + } + + char dirname[path_len + 1]; + strncpy(dirname, pathname, strlen(pathname) + 1); + dirname[last_slash_idx] = 0; + + if (vfs_lookup(dirname, &node)) { + uart_log(VFS, "Directory does not exist: "); + uart_puts(dirname); + uart_putc(NEWLINE); + // kfree(dirname, SILENT); + return -1; + } + node->v_ops->create(node, &node, pathname + last_slash_idx + 1); + } else { + uart_log(VFS, "File does not exist: "); + uart_puts(pathname); + uart_putc(NEWLINE); + return -1; + } + } + node = (vnode *)TO_VIRT(node); + node->f_ops->open(node, target); + target->flags = flags; + return 0; +} + +int vfs_close(file *file) { + // 1. release the file handle + // 2. Return error code if fails + uart_log(VFS, "vfs_close: "); + uart_dec(file->id); + uart_putc(NEWLINE); + return file->f_ops->close(file); +} + +int vfs_write(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. + if (file->id > 3) { + uart_log(VFS, "vfs_write: "); + uart_dec(file->id); + uart_putc(NEWLINE); + } + return file->f_ops->write(file, buf, len); +} + +int vfs_read(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. + if (file->id > 3) { + uart_log(VFS, "vfs_read: "); + uart_dec(file->id); + uart_putc(NEWLINE); + } + return file->f_ops->read(file, buf, len); +} + +int vfs_mkdir(const char *pathname) { + uart_log(VFS, "vfs_mkdir: "); + uart_puts(pathname); + uart_putc(NEWLINE); + + char dirname[strlen(pathname)]; + char newdirname[strlen(pathname)]; + dirname[0] = '\0'; + newdirname[0] = '\0'; + + 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); + strncpy(newdirname, pathname + last_slash_idx + 1, + strlen(pathname) - last_slash_idx); + + vnode *node; + if (vfs_lookup(dirname, &node) == 0) { + node->v_ops->mkdir(node, &node, newdirname); + return 0; + } + + uart_log(VFS, "Path not found: "); + uart_puts(dirname); + uart_putc(NEWLINE); + return -1; +} + +int vfs_mount(const char *target, const char *file_sys) { + filesystem *fs = reg_fs; + + uart_log(VFS, "Mounting filesystem: "); + uart_puts(file_sys); + uart_putc(NEWLINE); + + while (!fs || strcmp(fs->name, file_sys)) { + if (!fs) { + uart_log(VFS, "Filesystem not found\n"); + return -1; + } + fs = fs->next; + } + + uart_log(VFS, "Mounting directory: "); + uart_puts(target); + uart_putc(NEWLINE); + + vnode *dirnode; + if (vfs_lookup(target, &dirnode)) { + uart_log(VFS, "Directory not found\n"); + return -1; + } + + dirnode->mount = kmalloc(sizeof(mount), SILENT); + fs->setup_mount(fs, dirnode->mount); + return 0; +} + +int vfs_lookup(const char *pathname, vnode **target) { + if (strlen(pathname) == 0) { + *target = rootfs->root; + return 0; + } + + vnode *dirnode = rootfs->root; + char component_name[strlen(pathname) + 1]; + component_name[0] = '\0'; + int c_idx = 0; + for (int i = 1; i < strlen(pathname); i++) { + if (pathname[i] == '/') { + component_name[c_idx++] = '\0'; + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name)) { + return -1; + } + // if mount exists, change dirnode to the root of the mounted fs + while (dirnode->mount) + dirnode = dirnode->mount->root; + c_idx = 0; + } else + component_name[c_idx++] = pathname[i]; + } + component_name[c_idx++] = 0; + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name)) + return -1; + while (dirnode->mount) + dirnode = dirnode->mount->root; + *target = dirnode; + return 0; +} + +void vfs_exec(vnode *target, trap_frame *tf) { + thread_struct *thread = get_current(); + initramfs_inode *inode = target->internal; + void *program = kmalloc(inode->datasize, SILENT); + kfree(thread->start, VERBAL); + thread->start = program; + thread->size = inode->datasize; + uart_log(INFO, "Acquired program space: "); + uart_hex((uintptr_t)program); + uart_putc(NEWLINE); + memcpy(program, inode->data, inode->datasize); + + memset(thread->pgd, 0, PAGE_SIZE); + mapping_user_thread(thread, 0); + + while (thread->sig_busy) + ; // wait for signal handling + thread->sig_reg = 0; + memset(thread->sig_handlers, 0, sizeof(thread->sig_handlers)); + + // thread->context.sp = (uintptr_t)thread->stack + STACK_SIZE; + // thread->context.fp = (uintptr_t)thread->stack + STACK_SIZE; + // tf->elr_el1 = 0x0; + // tf->sp_el0 = USER_STACK + STACK_SIZE; + return; +} + +file *create_file() { + file *new_file = kmalloc(sizeof(file), SILENT); + new_file->vnode = 0; + new_file->f_ops = 0; + new_file->flags = 0; + new_file->f_pos = 0; + new_file->prev = 0; + new_file->next = 0; + return new_file; +} + +char *absolute_path(const char *path, const char *cwd) { + // relative path + char *tmp = kmalloc(strlen(cwd) + strlen(path) + 1, SILENT); + if (path[0] != '/') { + strncpy(tmp, cwd, strlen(cwd) + 1); + if (strcmp(cwd, "/")) { + strcat(tmp, "/\0"); + } + } + strcat(tmp, path); + char *ret = kmalloc(strlen(tmp), SILENT); + *ret = '\0'; + int idx = 0; + for (int i = 0; i < strlen(tmp); i++) { + if (tmp[i] == '/' && tmp[i + 1] == '.') { + if (tmp[i + 2] == '.') { + for (int j = idx; j >= 0; j--) { + if (ret[j] == '/') { + ret[j] = '\0'; + idx = j; + break; + } + } + i += 2; + continue; + } else { + i++; + continue; + } + } + ret[idx++] = tmp[i]; + } + ret[idx] = '\0'; + kfree(tmp, SILENT); + return ret; +} + +int not_supported() { + uart_log(WARN, "Not supported.\n"); + return -1; +} \ No newline at end of file diff --git a/lab7/src/virtm.c b/lab7/src/virtm.c new file mode 100644 index 000000000..c571443ef --- /dev/null +++ b/lab7/src/virtm.c @@ -0,0 +1,56 @@ +#include "virtm.h" + +#include "mem.h" +#include "start.h" +#include "uart.h" +#include "utils.h" + +extern unsigned long TOTAL_MEMORY; + +static void walk(uintptr_t pt, uintptr_t va, uintptr_t pa) { + uintptr_t *table = (uintptr_t *)pt; + for (int level = 0; level <= 3; level++) { + uintptr_t offset = (va >> (39 - 9 * level)) & 0x1FF; + if (level == 3) { + table[offset] = pa | PTE_NORMAL_ATTR; + return; + } + if (!table[offset]) { + uintptr_t *t = kmalloc(PAGE_SIZE, SILENT); + uart_log(INFO, "Acquired page table at "); + uart_hex((uintptr_t)t); + uart_puts(" for level "); + uart_dec(level); + uart_putc(NEWLINE); + memset(t, 0, PAGE_SIZE); + table[offset] = TO_PHYS((uintptr_t)t) | PD_TABLE; + } + table = (uintptr_t *)TO_VIRT((table[offset] & ~0xFFF)); + } +} + +void map_pages(uintptr_t pgd, uintptr_t va, uintptr_t size, uintptr_t pa) { + uart_log(MMAP, "Mapping virtual -> physical: \n"); + uart_putc(TAB); + uart_addr_range(va, va + size); + uart_puts(" -> "); + uart_addr_range(pa, pa + size); + uart_putc(NEWLINE); + for (int i = 0; i < size; i += PAGE_SIZE) + walk(pgd, va + i, pa + i); +} + +extern void *lfb; + +void mapping_user_thread(thread_struct *thread, int gpu_mem_size) { + // user code + map_pages((uintptr_t)thread->pgd, TO_VIRT(0x0), thread->size, + (uintptr_t)TO_PHYS(thread->start)); + // stack region + map_pages((uintptr_t)thread->pgd, TO_VIRT(USER_STACK), STACK_SIZE, + (uintptr_t)TO_PHYS(thread->user_stack)); + // GPU + if (gpu_mem_size > 0) + map_pages((uintptr_t)thread->pgd, (uintptr_t)TO_VIRT(lfb), + MMIO_BASE - TO_VIRT(lfb), (uintptr_t)lfb); +} \ No newline at end of file