diff --git a/.vscode/settings.json b/.vscode/settings.json index f75eddd62..0de3b7858 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,36 @@ "utility": "c", "typeinfo": "c", "stdlib.h": "c", - "mini_uart.h": "c" - } + "mini_uart.h": "c", + "gpio.h": "c", + "base.h": "c", + "stddef.h": "c", + "deque": "c", + "forward_list": "c", + "list": "c", + "vector": "c", + "math.h": "c", + "functional": "c", + "istream": "c", + "ostream": "c", + "tuple": "c", + "type_traits": "c", + "dynamic_alloc.h": "c", + "page_alloc.h": "c", + "stdint.h": "c", + "device_tree.h": "c", + "list.h": "c", + "thread.h": "c", + "signal.h": "c", + "limits": "c", + "mbox.h": "c", + "virtual_mem.h": "c", + "string.h": "c", + "tmpfs.h": "c", + "vfs.h": "c", + "string": "c", + "fat32.h": "c", + "random": "c" + }, + "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab3/Makefile b/Lab3/Makefile new file mode 100644 index 000000000..5db4cff0d --- /dev/null +++ b/Lab3/Makefile @@ -0,0 +1,58 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.img userprogram.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + @mkdir -p $(@D) + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/kernel/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/kernel/link.ld -o $(BUILD_DIR)/kernel/kernel8.elf $(filter $(BUILD_DIR)/kernel/%_c.o $(BUILD_DIR)/kernel/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel/kernel8.elf -O binary kernel8.img + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +bootloader.img: $(SRC_DIR)/bootloader/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/bootloader/link.ld -o $(BUILD_DIR)/bootloader/bootloader.elf $(filter $(BUILD_DIR)/bootloader/%_c.o $(BUILD_DIR)/bootloader/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/bootloader/bootloader.elf bootloader.img + +userprogram.img: $(SRC_DIR)/userprogram/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/userprogram/link.ld -o $(BUILD_DIR)/userprogram/userprogram.elf $(filter $(BUILD_DIR)/userprogram/%_c.o $(BUILD_DIR)/userprogram/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/userprogram/userprogram.elf userprogram.img + +test: + @echo Hello + +pseudoTTY: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug_boot: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -s -S + +no_int_qemu: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/Lab3/bcm2710-rpi-3-b-plus.dtb b/Lab3/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..0dd0e2f43 Binary files /dev/null and b/Lab3/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab3/bootloader.img b/Lab3/bootloader.img new file mode 100755 index 000000000..69042bf67 Binary files /dev/null and b/Lab3/bootloader.img differ diff --git a/Lab3/bootloader.py b/Lab3/bootloader.py new file mode 100644 index 000000000..1b99fa7d7 --- /dev/null +++ b/Lab3/bootloader.py @@ -0,0 +1,25 @@ +import os +import time + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +# with open('/dev/pts/35', "wb", buffering=0) as tty: +with open('/dev/ttyUSB0', "wb", buffering=0) as tty: + # send Start + tty.write(b"Start") + time.sleep(1) + + # send kernel size + tty.write(file_size.to_bytes(4, 'little')) + time.sleep(1) + + # send kernel + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + tty.write(data) + time.sleep(1) + diff --git a/Lab3/debug.log b/Lab3/debug.log new file mode 100644 index 000000000..af4e88a88 --- /dev/null +++ b/Lab3/debug.log @@ -0,0 +1,9 @@ +qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int +Board model: 00000000 + Board revision: 00A02082 + Serial number: 0000000000000000 + ARM memory base address: 00000000 + ARM memory size: 3C000000 + initramfs_addr at 0000000008000000 + Starting shell... + # \ No newline at end of file diff --git a/Lab3/include/device_tree.h b/Lab3/include/device_tree.h new file mode 100644 index 000000000..e3e6aee78 --- /dev/null +++ b/Lab3/include/device_tree.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_TREE_H +#define _DEVICE_TREE_H + +typedef int (*fdt_callback)(void *); +int initramfs_callback(void *dtb); +int dtb_parser(void *dtb); +void fdt_traverse(fdt_callback cb, char *dtb); + +#endif /*_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab3/include/load_kernel.h b/Lab3/include/load_kernel.h new file mode 100644 index 000000000..a5dc68ffb --- /dev/null +++ b/Lab3/include/load_kernel.h @@ -0,0 +1,7 @@ +#ifndef _LOAD_KERNEL_H +#define _LOAD_KERNEL_H + +void load_kernel(char *dest); +void relocate(char *from_dest, char *to_dest); + +#endif /*_LOAD_KERNEL_H */ diff --git a/Lab3/include/mbox.h b/Lab3/include/mbox.h new file mode 100644 index 000000000..d43fd936b --- /dev/null +++ b/Lab3/include/mbox.h @@ -0,0 +1,6 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); + +#endif /*_MBOX_H */ diff --git a/Lab3/include/mbox_call.h b/Lab3/include/mbox_call.h new file mode 100644 index 000000000..7f60645d0 --- /dev/null +++ b/Lab3/include/mbox_call.h @@ -0,0 +1,9 @@ +#ifndef _MBOX_CALL_H +#define _MBOX_CALL_H + +/* a properly aligned buffer */ +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch); + +#endif /*_MBOX_CALL_H */ diff --git a/Lab3/include/mini_uart.h b/Lab3/include/mini_uart.h new file mode 100644 index 000000000..12ec374b1 --- /dev/null +++ b/Lab3/include/mini_uart.h @@ -0,0 +1,17 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void uart_send_string_of_size(char *str, int size); +void uart_hex(unsigned int d); +void uart_send_space(int size); + +void asyn_read(); +void asyn_write(); +void uart_rx_handler(); +void uart_tx_handler(); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab3/include/mm.h b/Lab3/include/mm.h new file mode 100644 index 000000000..d8a7167d6 --- /dev/null +++ b/Lab3/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (2 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/Lab3/include/peripherals/base.h b/Lab3/include/peripherals/base.h new file mode 100644 index 000000000..63f9c038f --- /dev/null +++ b/Lab3/include/peripherals/base.h @@ -0,0 +1,6 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab3/include/peripherals/device_tree.h b/Lab3/include/peripherals/device_tree.h new file mode 100644 index 000000000..d5d3bc729 --- /dev/null +++ b/Lab3/include/peripherals/device_tree.h @@ -0,0 +1,16 @@ +#ifndef _P_DEVICE_TREE_H +#define _P_DEVICE_TREE_H + +#define FDT_MAGIC 0xD00DFEED +#define FDT_VERSION 0x00000011 +#define FDT_TK_NULL 0X00000000 + +#define FDT_BEGIN_NODE 0X00000001 +#define FDT_END_NODE 0X00000002 +#define FDT_PROP 0X00000003 +#define FDT_NOP 0X00000004 +#define FDT_END 0X00000009 + +#define FDT_CPIO_INITRAMFS_PROPNAME "linux,initrd-start" + +#endif /*_P_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab3/include/peripherals/gpio.h b/Lab3/include/peripherals/gpio.h new file mode 100644 index 000000000..a7a6a5b4c --- /dev/null +++ b/Lab3/include/peripherals/gpio.h @@ -0,0 +1,25 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL0 (PBASE + 0x00200000) +#define GPFSEL1 (PBASE + 0x00200004) +#define GPFSEL2 (PBASE + 0x00200008) +#define GPFSEL3 (PBASE + 0x0020000C) +#define GPFSEL4 (PBASE + 0x00200010) +#define GPFSEL5 (PBASE + 0x00200014) +#define GPSET0 (PBASE + 0x0020001C) +#define GPSET1 (PBASE + 0x00200020) +#define GPCLR0 (PBASE + 0x00200028) +#define GPLEV0 (PBASE + 0x00200034) +#define GPLEV1 (PBASE + 0x00200038) +#define GPEDS0 (PBASE + 0x00200040) +#define GPEDS1 (PBASE + 0x00200044) +#define GPHEN0 (PBASE + 0x00200064) +#define GPHEN1 (PBASE + 0x00200068) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) +#define GPPUDCLK1 (PBASE + 0x0020009C) + +#endif /*_P_GPIO_H */ diff --git a/Lab3/include/peripherals/irq.h b/Lab3/include/peripherals/irq.h new file mode 100644 index 000000000..51e0cc5ea --- /dev/null +++ b/Lab3/include/peripherals/irq.h @@ -0,0 +1,27 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + +#define IRQ_BASIC_PENDING (PBASE + 0x0000B200) +#define IRQ_PENDING_1 (PBASE + 0x0000B204) +#define IRQ_PENDING_2 (PBASE + 0x0000B208) +#define FIQ_CONTROL (PBASE + 0x0000B20C) +#define ENABLE_IRQS_1 (PBASE + 0x0000B210) +#define ENABLE_IRQS_2 (PBASE + 0x0000B214) +#define ENABLE_BASIC_IRQS (PBASE + 0x0000B218) +#define DISABLE_IRQS_1 (PBASE + 0x0000B21C) +#define DISABLE_IRQS_2 (PBASE + 0x0000B220) +#define DISABLE_BASIC_IRQS (PBASE + 0x0000B224) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#define CORE0_INTR_SRC 0x40000060 +#define CORE1_INTR_SRC 0x40000064 +#define CORE2_INTR_SRC 0x40000068 +#define CORE3_INTR_SRC 0x4000006C + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/Lab3/include/peripherals/mbox_call.h b/Lab3/include/peripherals/mbox_call.h new file mode 100644 index 000000000..cfcc0d3ad --- /dev/null +++ b/Lab3/include/peripherals/mbox_call.h @@ -0,0 +1,40 @@ +#ifndef _P_MBOX_CALL_H +#define _P_MBOX_CALL_H + +#include "peripherals/base.h" + +#define VIDEOCORE_MBOX (PBASE + 0x0000B880) +#define MBOX_READ (VIDEOCORE_MBOX + 0x0) +#define MBOX_POLL (VIDEOCORE_MBOX + 0x10) +#define MBOX_SENDER (VIDEOCORE_MBOX + 0x14) +#define MBOX_STATUS (VIDEOCORE_MBOX + 0x18) +#define MBOX_CONFIG (VIDEOCORE_MBOX + 0x1C) +#define MBOX_WRITE (VIDEOCORE_MBOX + 0x20) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_MODEL 0x10001 +#define MBOX_TAG_REVISION 0x10002 +#define MBOX_TAG_MAC_ADDRESS 0x10003 +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_ARM_MEMORY 0x10005 +#define MBOX_TAG_VC_MEMORY 0x10006 +#define MBOX_TAG_CLOCKS 0x10007 +#define MBOX_TAG_LAST 0 + +#endif /* _P_MBOX_CALL_H */ diff --git a/Lab3/include/peripherals/mini_uart.h b/Lab3/include/peripherals/mini_uart.h new file mode 100644 index 000000000..71119b511 --- /dev/null +++ b/Lab3/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/Lab3/include/peripherals/reboot.h b/Lab3/include/peripherals/reboot.h new file mode 100644 index 000000000..f1d41ad2e --- /dev/null +++ b/Lab3/include/peripherals/reboot.h @@ -0,0 +1,8 @@ +#ifndef _P_REBOOT_H +#define _P_REBOOT_H + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +#endif /*_P_REBOOT_H */ \ No newline at end of file diff --git a/Lab3/include/printf.h b/Lab3/include/printf.h new file mode 100644 index 000000000..a9501cba0 --- /dev/null +++ b/Lab3/include/printf.h @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ + void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ + int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ + int sprintf_(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + int snprintf_(char *buffer, size_t count, const char *format, ...); + int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ + int vprintf_(const char *format, va_list va); + + /** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ + int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ \ No newline at end of file diff --git a/Lab3/include/read_cpio.h b/Lab3/include/read_cpio.h new file mode 100644 index 000000000..f33833343 --- /dev/null +++ b/Lab3/include/read_cpio.h @@ -0,0 +1,9 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); +char *find_content_addr(char *cpioDest, char *filename); +int load_userprogram(char *cpioDest, char *userDest); + +#endif /*_READ_CPIO_H */ \ No newline at end of file diff --git a/Lab3/include/reboot.h b/Lab3/include/reboot.h new file mode 100644 index 000000000..e9fb1d66c --- /dev/null +++ b/Lab3/include/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif /*_REBOOT_H */ \ No newline at end of file diff --git a/Lab3/include/shell.h b/Lab3/include/shell.h new file mode 100644 index 000000000..f677e039b --- /dev/null +++ b/Lab3/include/shell.h @@ -0,0 +1,7 @@ +#ifndef _SHELL_H +#define _SHELL_H + +void shell_main(char *); +void shell_start(); + +#endif /*_SHELL_H */ diff --git a/Lab3/include/stdlib.h b/Lab3/include/stdlib.h new file mode 100644 index 000000000..715d7bcda --- /dev/null +++ b/Lab3/include/stdlib.h @@ -0,0 +1,25 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" +#include "utils.h" +#include "mini_uart.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const char *str); +char *strcpy(char *destination, const char *source); +int atoi(char *str); + +void *memset(void *dest, register int val, int len); +int memcmp(void *s1, void *s2, int n); +int hex2int(char *s, int n); + +void *simple_malloc(unsigned int size); + +// void printf(char *fmt, ...); +// unsigned int sprintf(char *dst, char *fmt, ...); +// unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +#endif /*_STDLIB_H */ diff --git a/Lab3/include/timer.h b/Lab3/include/timer.h new file mode 100644 index 000000000..df945b2ea --- /dev/null +++ b/Lab3/include/timer.h @@ -0,0 +1,13 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#define MESSAGE_BUFFER 50 +#define SECONDS_BUFFER 20 + +void get_current_time(); +void el0_timer_handler(long cntpct_el0, long cntfrq_el0); +void el1_timer_handler(long cntpct_el0, long cntfrq_el0); +void add_timer(int sec, char *mes); +int is_timer_queue_empty(); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab3/include/utils.h b/Lab3/include/utils.h new file mode 100644 index 000000000..a23ae37a7 --- /dev/null +++ b/Lab3/include/utils.h @@ -0,0 +1,8 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay ( unsigned long); +extern void put32 ( unsigned long, unsigned int ); +extern unsigned int get32 ( unsigned long ); + +#endif /*_BOOT_H */ diff --git a/Lab3/initramfs.cpio b/Lab3/initramfs.cpio new file mode 100644 index 000000000..55d33a552 Binary files /dev/null and b/Lab3/initramfs.cpio differ diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img new file mode 100755 index 000000000..02da1da21 Binary files /dev/null and b/Lab3/kernel8.img differ diff --git a/Lab3/rootfs/a.c b/Lab3/rootfs/a.c new file mode 100644 index 000000000..d2398d75b --- /dev/null +++ b/Lab3/rootfs/a.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + printf("HAHA return 1\n"); + + return 1; +} diff --git a/Lab3/rootfs/cat.txt b/Lab3/rootfs/cat.txt new file mode 100644 index 000000000..949e8345b --- /dev/null +++ b/Lab3/rootfs/cat.txt @@ -0,0 +1 @@ +No cat here. \ No newline at end of file diff --git a/Lab3/rootfs/one b/Lab3/rootfs/one new file mode 100644 index 000000000..50ecbb596 --- /dev/null +++ b/Lab3/rootfs/one @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 diff --git a/Lab3/rootfs/ts.txt b/Lab3/rootfs/ts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Lab3/rootfs/userprogram.img b/Lab3/rootfs/userprogram.img new file mode 100755 index 000000000..83efd9631 Binary files /dev/null and b/Lab3/rootfs/userprogram.img differ diff --git a/Lab3/send_kernel.py b/Lab3/send_kernel.py new file mode 100644 index 000000000..1b17155f2 --- /dev/null +++ b/Lab3/send_kernel.py @@ -0,0 +1,40 @@ +# This python script is for macOS +from serial import Serial +import os +import time +import fcntl +import threading + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +with Serial('/dev/tty.usbserial-0001', 115200) as ser: + sem = threading.Semaphore() + # set tty to non-blocking mode + fcntl.fcntl(ser, fcntl.F_SETFL, os.O_NONBLOCK) + + input("Press anything to start transmission\n") + + print("Sending Strat...") + sem.acquire() + ser.write(b"Start") + sem.release() + time.sleep(1) + + sem.acquire() + ser.write(file_size.to_bytes(4, 'little')) + sem.release() + time.sleep(1) + + print("Start sending kernel img by uart...") + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + sem.acquire() + ser.write(data) + sem.release() + time.sleep(1) + + print("Transfer finished!") diff --git a/Lab3/src/bootloader/boot.S b/Lab3/src/bootloader/boot.S new file mode 100644 index 000000000..f6b6f7474 --- /dev/null +++ b/Lab3/src/bootloader/boot.S @@ -0,0 +1,25 @@ +#include "mm.h" + +.section ".text.boot" + +.globl _start +_start: + mov x24, x0 + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + adr x0, __bss_start + adr x1, __bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + mov x0, x24 + bl bootloader_main + b proc_hang // should never come here diff --git a/Lab3/src/bootloader/bootloader.c b/Lab3/src/bootloader/bootloader.c new file mode 100644 index 000000000..327413557 --- /dev/null +++ b/Lab3/src/bootloader/bootloader.c @@ -0,0 +1,15 @@ +#include "mini_uart.h" +#include "load_kernel.h" + +void bootloader_main(void) +{ + uart_init(); + + uart_send_string("Relocatting ...\n"); + char *from_dest = (char *)0x80000; + char *to_dest = (char *)0x60000; + relocate((char *)from_dest, (char *)to_dest); + + char *dest = (char *)0x80000; + load_kernel((char *)dest); +} diff --git a/Lab3/src/bootloader/config.txt b/Lab3/src/bootloader/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/Lab3/src/bootloader/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/Lab3/src/bootloader/link.ld b/Lab3/src/bootloader/link.ld new file mode 100644 index 000000000..1fca3dfb7 --- /dev/null +++ b/Lab3/src/bootloader/link.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0x80000; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; +__loader_size = (_end - _start); diff --git a/Lab3/src/bootloader/load_kernel.c b/Lab3/src/bootloader/load_kernel.c new file mode 100644 index 000000000..00808de84 --- /dev/null +++ b/Lab3/src/bootloader/load_kernel.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include "mini_uart.h" +#include "peripherals/mini_uart.h" +#include "stdlib.h" + +extern int __loader_size; +extern void *_dtb_ptr; + +void load_kernel(char *dest) +{ + int size = 0; + + uart_send_string("Waiting for kernel8.img ...\n"); + + char start[5] = "Start"; + + for (int i = 0; i < 5; i++) + { + if (uart_recv() != start[i]) + i = 0; + } + + uart_send_string("Start transmitting ...\n"); + + size = uart_recv(); + size |= uart_recv() << 8; + size |= uart_recv() << 16; + size |= uart_recv() << 24; + + printf("Size of kernel is = %d bytes\n", size); + + // read the kernel + while (size--) + { + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + *dest++ = get32(AUX_MU_IO_REG); + } + + uart_send_string("End transmitting ...\n"); + + // restore arguments and jump to the new kernel. + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + // we must force an absolute address to branch to + "mov x30, 0x80000; ret"); +} + +void relocate(char *from_dest, char *to_dest) +{ + long long size = (long long)&__loader_size; + + while (size--) + { + *to_dest++ = *from_dest++; + } + + char *redicrect = __builtin_return_address(0) + (to_dest - from_dest); + + goto *redicrect; +} \ No newline at end of file diff --git a/Lab3/src/kernel/boot.S b/Lab3/src/kernel/boot.S new file mode 100644 index 000000000..0735567bf --- /dev/null +++ b/Lab3/src/kernel/boot.S @@ -0,0 +1,86 @@ +#include "mm.h" + +.global _dtb_ptr +.section .data +_dtb_ptr: .dc.a 0x0 + +.section ".text.boot" + +.globl _start +_start: + cbz x24, x24c // Check if bootloader, see bootloader's boot.S for more info +x24nc: + ldr x21, =_dtb_ptr + str x24, [x21] + b go +x24c: + ldr x21, =_dtb_ptr + str x0, [x21] +go: + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + + ldr x1, =_start + + // get CurrentEL + mrs x0, CurrentEL + and x0, x0, #12 // clear reserved bits + + // running at EL3? branch if in EL3 + cmp x0, #12 + beq from_el3_to_el2 + + // running at EL1? branch if in EL1 + cmp x0, #4 + beq bss + + bl from_el2_to_el1 + +bss: + adr x0, __bss_start + adr x1, __bss_end + sub x1, x1, x0 + bl memzero + + bl set_el1_exception_vector_table // set el1 exception vector table base + + mov sp, #LOW_MEMORY // 4MB + bl kernel_main + b proc_hang // should never come here + +from_el3_to_el2: + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, from_el2_to_el1 + msr elr_el3, x2 + eret + +from_el2_to_el1: + msr sp_el1, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // change execution level to EL1 + mov x2, #0x3c5 + msr spsr_el2, x2 + msr elr_el2, lr + eret \ No newline at end of file diff --git a/Lab3/src/kernel/config.txt b/Lab3/src/kernel/config.txt new file mode 100644 index 000000000..279349696 --- /dev/null +++ b/Lab3/src/kernel/config.txt @@ -0,0 +1,2 @@ +kernel_old=1 +disable_commandline_tags=1 diff --git a/Lab3/src/kernel/device_tree.c b/Lab3/src/kernel/device_tree.c new file mode 100644 index 000000000..bcb5b17ce --- /dev/null +++ b/Lab3/src/kernel/device_tree.c @@ -0,0 +1,266 @@ +#include +#include "peripherals/device_tree.h" +#include "stdlib.h" +#include "mini_uart.h" +#include "device_tree.h" + +typedef struct fdt_header +{ + uint32_t magic; // big-endian + 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; +} fdt_header; + +typedef struct +{ + uint32_t len; + uint32_t nameoff; +} fdt_prop; + +char *cpioDest; + +static uint64_t pad_to_4(void *num); +static uint32_t rev32(uint32_t val); +static uint64_t endian_rev(void *input, int dtype_size); + +int initramfs_callback(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + uint8_t *p = off_dt_struct; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + if (!strcmp(FDT_CPIO_INITRAMFS_PROPNAME, prop_name)) + { + uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); + printf("initramfs_addr at %p\n", addr); + cpioDest = (char *)addr; + } + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +int dtb_parser(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + int depth = 0; + uint8_t *p = off_dt_struct; + char *node_name = NULL; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + node_name = (char *)p; + if (depth == 0) + printf("\\ {\n"); + else + { + uart_send_space(depth * 3); + printf("%s {\n", node_name); + } + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + depth++; + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + depth--; + uart_send_space(depth * 3); + printf("};\n"); + printf("\n"); + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + + if (!strcmp(prop_name, "#address-cells") || !strcmp(prop_name, "#size-cells") || !strcmp(prop_name, "interrupt-parent")) + { + // + uart_send_space(depth * 3); + printf("%s = <%d>;\n", prop_name, rev32(*((uint32_t *)prop_val))); + } + else if (!strcmp(prop_name, "model") || !strcmp(prop_name, "status") || !strcmp(prop_name, "name") || !strcmp(prop_name, "device_type") || + !strcmp(prop_name, "chassis-type") || !strcmp(prop_name, "bootargs") || !strcmp(prop_name, "stdout-path") || !strcmp(prop_name, "stdin-path") || + !strcmp(prop_name, "power-isa-version") || !strcmp(prop_name, "mmu-type") || !strcmp(prop_name, "label") || !strcmp(prop_name, "phy-connection-type")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else if (!strcmp(prop_name, "compatible")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else + { + uart_send_space(depth * 3); + printf("%s = %s;\n", prop_name, prop_val); + } + + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +void fdt_traverse(fdt_callback cb, char *dtb) +{ + if (cb(dtb) == -1) + printf("fdt_traverse failed.\n"); + + return; +} + +static uint64_t pad_to_4(void *num) +{ + uint64_t modded = ((uint64_t)num) % 4; + return modded == 0 ? 0 : 4 - modded; +} + +static uint32_t rev32(uint32_t val) +{ + return (uint32_t)endian_rev(&val, 4); +} + +/** Transform data from litle to big endian, or from big to little endian + * @param input: Pointer to a value that needs to be transformed + * @param dtype_size: data type size, size of each item in bytes. Possible value: 1, 2, 4, 8 + */ +static uint64_t endian_rev(void *input, int dtype_size) +{ + const uint8_t *ptr = (uint8_t *)input; + uint64_t ret = 0; + + switch (dtype_size) + { + // int8_t, uint8_t + case 1: + // No need to transform to big endian since the data type size is 1 byte + break; + + // int16_t, uint16_t + case 2: + ret = (ptr[0] << 8) | ptr[1]; + break; + + // int32_t, uint32_t + case 4: + ret = (ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + ptr[3]; + break; + + // int64_t, uint64_t + // case 8: + // ret = (ptr[0] << 56) | + // (ptr[1] << 48) | + // (ptr[2] << 40) | + // (ptr[3] << 32) | + // (ptr[4] << 24) | + // (ptr[5] << 16) | + // (ptr[6] << 8) | + // ptr[7]; + // break; + + default: + printf("[Error] Endian transformation(%d) not implemented. @line %d, file:%s\r\n", dtype_size, __LINE__, __FILE__); + break; + } + return ret; +} \ No newline at end of file diff --git a/Lab3/src/kernel/exception.S b/Lab3/src/kernel/exception.S new file mode 100644 index 000000000..ba3cfbc06 --- /dev/null +++ b/Lab3/src/kernel/exception.S @@ -0,0 +1,135 @@ +#define CORE0_INTERRUPT_SOURCE 0x40000060 +#define IIR_ADDR 0x3f215048 + +.global set_el1_exception_vector_table +.global exception_vector_table + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 8 + 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] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 8 +.endm + +.align 11 // vector table should be aligned to 0x800 +el1_exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_same_el + .align 7 + b exception_handler_irq_sp_elx_same_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_lower_el + .align 7 + b exception_handler_irq_sp_elx_lower_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_el1_exception_vector_table: + adr x0, el1_exception_vector_table + msr vbar_el1, x0 + ret lr + +exception_handler: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_sync_sp_elx_lower_el: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_irq_sp_elx_lower_el: + b el0_core_timer_handler + +exception_handler_sync_sp_elx_same_el: + b exception_handler + +exception_handler_irq_sp_elx_same_el: + b el1_core_interrupt_handler + +el0_core_timer_handler: + save_all + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all + eret + +el1_core_interrupt_handler: + save_all + bl el1_irq_interrupt_handler + load_all + eret \ No newline at end of file diff --git a/Lab3/src/kernel/exception.c b/Lab3/src/kernel/exception.c new file mode 100644 index 000000000..cc1c2fda6 --- /dev/null +++ b/Lab3/src/kernel/exception.c @@ -0,0 +1,150 @@ +#include "peripherals/mini_uart.h" +#include "peripherals/irq.h" +#include "stdlib.h" +#include "timer.h" + +/** + * common exception handler + */ +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + // print out interruption type + switch (type) + { + case 0: + uart_send_string("Synchronous"); + break; + case 1: + uart_send_string("IRQ"); + break; + case 2: + uart_send_string("FIQ"); + break; + case 3: + uart_send_string("SError"); + break; + } + uart_send_string(": "); + // decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28) + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown"); + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE"); + break; + case 0b001110: + uart_send_string("Illegal execution"); + break; + case 0b010101: + uart_send_string("System call"); + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL"); + break; + case 0b100001: + uart_send_string("Instruction abort, same EL"); + break; + case 0b100010: + uart_send_string("Instruction alignment fault"); + break; + case 0b100100: + uart_send_string("Data abort, lower EL"); + break; + case 0b100101: + uart_send_string("Data abort, same EL"); + break; + case 0b100110: + uart_send_string("Stack alignment fault"); + break; + case 0b101100: + uart_send_string("Floating point"); + break; + default: + uart_send_string("Unknown"); + break; + } + // decode data abort cause + if (esr >> 26 == 0b100100 || esr >> 26 == 0b100101) + { + uart_send_string(", "); + switch ((esr >> 2) & 0x3) + { + case 0: + uart_send_string("Address size fault"); + break; + case 1: + uart_send_string("Translation fault"); + break; + case 2: + uart_send_string("Access flag fault"); + break; + case 3: + uart_send_string("Permission fault"); + break; + } + switch (esr & 0x3) + { + case 0: + uart_send_string(" at level 0"); + break; + case 1: + uart_send_string(" at level 1"); + break; + case 2: + uart_send_string(" at level 2"); + break; + case 3: + uart_send_string(" at level 3"); + break; + } + } + // dump registers + uart_send_string("\nSPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + + return; +} + +void el1_irq_interrupt_handler() +{ + unsigned int irq_basic_pending = get32(IRQ_BASIC_PENDING); + irq_basic_pending &= (1 << 19); // clear bits + + // GPU IRQ 57 : UART Interrupt + if (irq_basic_pending) + { + if (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + { + uart_rx_handler(); + } + else if (get32(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + { + uart_tx_handler(); + } + } + // ARM Core Timer Interrupt + else if (get32(CORE0_INTR_SRC) & (1 << 1)) + { + long cntpct_el0, cntfrq_el0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + el1_timer_handler(cntpct_el0, cntfrq_el0); + } + + return; +} \ No newline at end of file diff --git a/Lab3/src/kernel/kernel.c b/Lab3/src/kernel/kernel.c new file mode 100644 index 000000000..d076c7508 --- /dev/null +++ b/Lab3/src/kernel/kernel.c @@ -0,0 +1,16 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "device_tree.h" +extern void *_dtb_ptr; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + fdt_traverse(initramfs_callback, _dtb_ptr); + + shell_start(); +} diff --git a/Lab3/src/kernel/link.ld b/Lab3/src/kernel/link.ld new file mode 100644 index 000000000..a460f25b2 --- /dev/null +++ b/Lab3/src/kernel/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab3/src/kernel/mbox.c b/Lab3/src/kernel/mbox.c new file mode 100644 index 000000000..f6d91caa7 --- /dev/null +++ b/Lab3/src/kernel/mbox.c @@ -0,0 +1,63 @@ +#include "peripherals/mbox_call.h" +#include "mbox_call.h" +#include "mini_uart.h" + +void mbox_main() +{ + // get the board's unique serial number with a mailbox call + mbox[0] = 21 * 4; // length of the message + mbox[1] = MBOX_REQUEST; // this is a request message + + mbox[2] = MBOX_TAG_MODEL; + mbox[3] = 4; + mbox[4] = 0; // ?? + mbox[5] = 0; + + mbox[6] = MBOX_TAG_REVISION; + mbox[7] = 4; + mbox[8] = 0; // ?? + mbox[9] = 0; + + mbox[10] = MBOX_TAG_GETSERIAL; // get serial number command + mbox[11] = 8; // buffer size + mbox[12] = 8; // ?? + mbox[13] = 0; // clear output buffer + mbox[14] = 0; + + mbox[15] = MBOX_TAG_ARM_MEMORY; // get serial number command + mbox[16] = 8; // buffer size + mbox[17] = 8; // ?? + mbox[18] = 0; // clear output buffer + mbox[19] = 0; + + mbox[20] = MBOX_TAG_LAST; + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) + { + uart_send_string("Board model: "); + uart_hex(mbox[5]); + uart_send_string("\n"); + + uart_send_string("Board revision: "); + uart_hex(mbox[9]); + uart_send_string("\n"); + + uart_send_string("Serial number: "); + uart_hex(mbox[14]); + uart_hex(mbox[13]); + uart_send_string("\n"); + + uart_send_string("ARM memory base address: "); + uart_hex(mbox[18]); + uart_send_string("\n"); + + uart_send_string("ARM memory size: "); + uart_hex(mbox[19]); + uart_send_string("\n"); + } + else + { + uart_send_string("Unable to query serial!\n"); + } +} \ No newline at end of file diff --git a/Lab3/src/kernel/mbox_call.c b/Lab3/src/kernel/mbox_call.c new file mode 100644 index 000000000..d0626f0e8 --- /dev/null +++ b/Lab3/src/kernel/mbox_call.c @@ -0,0 +1,42 @@ +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" + +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) +{ + unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} \ No newline at end of file diff --git a/Lab3/src/kernel/read_cpio.c b/Lab3/src/kernel/read_cpio.c new file mode 100644 index 000000000..665e57070 --- /dev/null +++ b/Lab3/src/kernel/read_cpio.c @@ -0,0 +1,151 @@ +#include "stdlib.h" +#include "mini_uart.h" + +typedef struct cpio_newc_header +{ + 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]; +} __attribute__((packed)) cpio_t; + +void read_cpio(char *cpioDest) +{ + uart_send_string("Type Offset Size Access rights\tFilename\n"); + + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + // print out meta information + uart_hex(hex2int(header->c_mode, 8)); // mode (access rights + type) + uart_send(' '); + uart_hex((unsigned int)((unsigned long)cpioDest) + sizeof(cpio_t) + ns); + uart_send(' '); + uart_hex(fs); // file size in hex + uart_send(' '); + uart_hex(hex2int(header->c_uid, 8)); // user id in hex + uart_send('.'); + uart_hex(hex2int(header->c_gid, 8)); // group id in hex + uart_send('\t'); + uart_send_string(cpioDest + sizeof(cpio_t)); // filename + uart_send_string("\n"); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } +} + +void read_content(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("cat: %s: No such file\n", filename); + return; + } + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + // print content + uart_send_string_of_size((char *)cpioDest, fs); +} + +char *find_content_addr(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("find_content_addr: %s: No such file\n", filename); + return NULL; + } + + return cpioDest; +} + +int load_userprogram(char *cpioDest, char *userDest) +{ + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + printf("load %p to %p\n", cpioDest, userDest); + printf("size: %d bytes\n", fs); + + // load content + while (fs--) + { + *userDest++ = *cpioDest++; + } + + if (fs == -1) + return 0; + + return 1; +} \ No newline at end of file diff --git a/Lab3/src/kernel/reboot.c b/Lab3/src/kernel/reboot.c new file mode 100644 index 000000000..da809c59a --- /dev/null +++ b/Lab3/src/kernel/reboot.c @@ -0,0 +1,19 @@ +#include "peripherals/reboot.h" + +void set(long addr, unsigned int value) +{ + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) +{ // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab3/src/kernel/shell.c b/Lab3/src/kernel/shell.c new file mode 100644 index 000000000..3f7c5c39d --- /dev/null +++ b/Lab3/src/kernel/shell.c @@ -0,0 +1,264 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" +#include "timer.h" + +extern void *_dtb_ptr; +extern char *cpioDest; +extern char read_buffer[100]; + +#define COMMAND_BUFFER 50 +#define FILENAME_BUFFER 20 + +void shell_start(); + +void shell_main(char *command) +{ + if (!strcmp(command, "help")) + { + uart_send_string("help\t: print this help menu\n"); + uart_send_string("hello\t: print Hello World!\n"); + uart_send_string("reboot\t: reboot the device\n"); + uart_send_string("ls\t: list information about files\n"); + uart_send_string("cat\t: copy each FILE to standard output\n"); + uart_send_string("dts\t: list devicetree\n"); + uart_send_string("svc\t: test svc interrupt in user program\n"); + uart_send_string("time\t: time 2 secs\n"); + uart_send_string("asynr\t: [test] asynchronous read\n"); + uart_send_string("asynw\t: [test] asynchronous write\n"); + uart_send_string("setTimeout\t: Usage: setTimeout \n"); + } + else if (!strcmp(command, "hello")) + { + uart_send_string("Hello World!\n"); + } + else if (!strcmp(command, "reboot")) + { + uart_send_string("Rebooting in 3 seconds\n"); + reset(3 << 16); + while (1) + ; + } + else if (!strcmp(command, "ls")) + { + // char *cpioDest = (char *)0x8000000; + read_cpio((char *)cpioDest); + } + else if (!memcmp(command, "cat", 3)) + { + if (command[3] != ' ' || command[4] == '\0') + { + printf("Usage: cat \n"); + return; + } + + char filename[FILENAME_BUFFER]; + memset(filename, '\0', FILENAME_BUFFER); + int i = 4; + while (command[i] != '\0') + { + filename[i - 4] = command[i]; + i++; + } + + read_content((char *)cpioDest, filename); + } + else if (!strcmp(command, "cat")) + { + uart_send_string("Filename: "); + + char c; + int i = 0; + char filename[FILENAME_BUFFER]; + // char *cpioDest = (char *)0x8000000; + + memset(filename, '\0', FILENAME_BUFFER); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + filename[i] = '\0'; + uart_send(c); + read_content((char *)cpioDest, filename); + break; + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else + { + if (i < FILENAME_BUFFER) + { + if (c == 0) + continue; + filename[i++] = c; + } + uart_send(c); + } + } + } + } + else if (!strcmp(command, "dts")) + { + fdt_traverse(dtb_parser, _dtb_ptr); + } + else if (!strcmp(command, "svc")) + { + // char *cpioDest = (char *)0x8000000; + char *cpioUserPgmDest = cpioDest; + char *userDest = (char *)0x200000; + cpioUserPgmDest = find_content_addr(cpioUserPgmDest, "userprogram.img"); + if (cpioUserPgmDest == NULL) + { + uart_send_string("FAIL to find userprogram.img\n"); + return; + } + if (load_userprogram(cpioUserPgmDest, userDest) != 0) + { + uart_send_string("FAIL to load user program.\n"); + return; + } + + asm volatile( + "mov x0, 0x3c0;" // EL0t + "msr spsr_el1, x0;" + "mov x0, 0x200000;" + "msr elr_el1, x0;" + "mov x0, 0x200000;" + "msr sp_el0, x0;" + "eret;"); + } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x1, 0x0;" // not sure why can't use x0, may have something to to with %0 + "msr spsr_el1, x1;" + "mov x2, %0;" + "add x2, x2, 12;" + "msr elr_el1, x2;" + "mov x2, 0x1000000;" + "msr sp_el0, x2;" + "mrs x2, cntfrq_el0;" + "add x2, x2, x2;" + "msr cntp_tval_el0, x2;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } + else if (!strcmp(command, "asynr")) + { + asyn_read(); + } + else if (!strcmp(command, "asynw")) + { + asyn_write(read_buffer); + uart_send('\n'); + } + else if (!memcmp(command, "setTimeout", 10)) + { + if (command[10] != ' ' || command[11] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char message[MESSAGE_BUFFER]; + memset(message, '\0', MESSAGE_BUFFER); + int i = 11; + while (command[i] != ' ' && command[i] != '\0') + { + message[i - 11] = command[i]; + i++; + } + + if (command[i] != ' ' || command[i] == '\0' || command[i + 1] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char seconds[SECONDS_BUFFER]; + memset(seconds, '\0', SECONDS_BUFFER); + while (command[i] != '\0') + { + seconds[i - strlen(message) - 1 - 11] = command[i]; + i++; + } + int sec; + sec = atoi(seconds); + + add_timer(sec, message); + } + else if (!strcmp(command, "test")) + { + add_timer(2, "HELLO"); + } + + return; +} + +void shell_start() +{ + uart_send_string("Starting shell...\n"); + char c; + int i = 0; + + char command[COMMAND_BUFFER]; + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("# "); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + command[i] = '\0'; + uart_send(c); + shell_main(command); + memset(command, '\0', COMMAND_BUFFER); + i = 0; + uart_send_string("# "); + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else if (c == 127) // Also backspace but delete + { + } + else + { + if (i < COMMAND_BUFFER) + { + if (c == 0) // solve the problem that first command on board wont work + continue; + command[i++] = c; + } + uart_send(c); + } + } + } +} diff --git a/Lab3/src/kernel/timer.S b/Lab3/src/kernel/timer.S new file mode 100644 index 000000000..9e0c519e9 --- /dev/null +++ b/Lab3/src/kernel/timer.S @@ -0,0 +1,16 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 +.global core_timer_enable + +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret + +/* +cntpct_el0: The timer’s current count. +cntp_cval_el0: A compared timer count. If cntpct_el0 >= cntp_cval_el0, interrupt the CPU core. +cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +*/ \ No newline at end of file diff --git a/Lab3/src/kernel/timer.c b/Lab3/src/kernel/timer.c new file mode 100644 index 000000000..f1a628220 --- /dev/null +++ b/Lab3/src/kernel/timer.c @@ -0,0 +1,150 @@ +#include "stdlib.h" +#include "timer.h" + +typedef struct timer_queue_node +{ + long second_ticks; + char message[MESSAGE_BUFFER]; +} tq; + +tq timer_queue[10]; +int timer_queue_front = 0; +int timer_queue_back = 0; + +void get_current_time() +{ + long cntpct_el0, cntfrq_el0; + long cntp_tval_el0; + long cntp_cval_el0; + + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + "mrs %2, cntp_tval_el0;" + "mrs %3, cntp_cval_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0), "=r"(cntp_tval_el0), "=r"(cntp_cval_el0)); + + long nowtime = cntpct_el0 / cntfrq_el0; + + printf("%ld seconds after booting\n", nowtime); + + // printf("cntpct_el0 = %d\n", cntpct_el0); + // printf("cntfrq_el0 = %d\n", cntfrq_el0); + // printf("cntp_tval_el0 = %d\n", cntp_tval_el0); + // printf("cntp_cval_el0 = %d\n", cntp_cval_el0); + + return; +} + +void add_timer(int sec, char *mes) +{ + get_current_time(); + + for (int i = 0; i < MESSAGE_BUFFER; i++) + timer_queue[timer_queue_back].message[i] = mes[i]; + + // transfer sec to frq and store to node.second + asm volatile( + "msr DAIFSet, 0xf;" + "mrs x3, cntfrq_el0;" + "mrs x4, cntpct_el0;" + "mov x2, %1;" // after secs seconds later will interrupt + "mul x2, x2, x3;" + "add x2, x2, x4;" + "mov %0, x2;" + : "=r"(timer_queue[timer_queue_back].second_ticks) + : "r"(sec) + : "x0", "x1", "x2", "x3", "x4", "memory"); // Uses register operands x0, x1, x2, x3, and x4 and specifies that the instruction may modify memory using the clobber list "x0", "x1", "x2", "x3", "x4", "memory". + + timer_queue_back++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + + asm volatile( + "msr cntp_cval_el0, %0;" + "bl core_timer_enable;" + "msr DAIFClr, 0xf;" + : + : "r"(timer_queue[timer_queue_front].second_ticks)); +} + +void el0_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + + return; +} + +void el1_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + printf("Message: %s\n", timer_queue[timer_queue_front].message); + + timer_queue_front++; + if (!is_timer_queue_empty()) + { + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + asm volatile( + "msr cntp_cval_el0, %0\n\t" // set expired time + "bl core_timer_enable\n\t" + : + : "r"(timer_queue[timer_queue_front].second_ticks) + :); + } + + return; +} + +int is_timer_queue_empty() +{ + return timer_queue_front == timer_queue_back ? 1 : 0; +} \ No newline at end of file diff --git a/Lab3/src/lib/hex2int.c b/Lab3/src/lib/hex2int.c new file mode 100644 index 000000000..4f30c1df8 --- /dev/null +++ b/Lab3/src/lib/hex2int.c @@ -0,0 +1,15 @@ +int hex2int(char *s, int n) +{ + int r = 0; + while (n-- > 0) + { + r <<= 4; + if (*s >= '0' && *s <= '9') + r += *s++ - '0'; + else if (*s >= 'A' && *s <= 'F') + r += *s++ - 'A' + 10; + else if (*s >= 'a' && *s <= 'f') + r += *s++ - 'a' + 10; + } + return r; +} \ No newline at end of file diff --git a/Lab3/src/lib/mem.c b/Lab3/src/lib/mem.c new file mode 100644 index 000000000..d74fb96fd --- /dev/null +++ b/Lab3/src/lib/mem.c @@ -0,0 +1,22 @@ +void *memset(void *dest, register int val, int len) +{ + register unsigned char *ptr = (unsigned char *)dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +int memcmp(void *s1, void *s2, int n) +{ + unsigned char *a = s1, *b = s2; + while (n-- > 0) + { + if (*a != *b) + { + return *a - *b; + } + a++; + b++; + } + return 0; +} \ No newline at end of file diff --git a/Lab3/src/lib/mini_uart.c b/Lab3/src/lib/mini_uart.c new file mode 100644 index 000000000..82b34aa13 --- /dev/null +++ b/Lab3/src/lib/mini_uart.c @@ -0,0 +1,183 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "peripherals/irq.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; + +char read_buffer[100]; +char write_buffer[100]; +int len_WB = 0; +int len_RB = 0; +int buffer_count = 0; + +void uart_send(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + + if (c != '\x7f') + put32(AUX_MU_IO_REG, c); + + if (c == '\n') + uart_send('\r'); +} + +char uart_recv(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r == '\r' ? '\n' : r; +} + +void uart_send_string(char *str) +{ + while (*str) + uart_send(*str++); +} + +void uart_send_space(int size) +{ + while (size--) + uart_send(' '); +} + +void uart_send_string_of_size(char *str, int size) +{ + while (size--) + uart_send(*str++); +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) +{ + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) + { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_init(void) +{ + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 2 << 12; // set alt5 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 2 << 15; // set alt5 for gpio15 + put32(GPFSEL1, selector); + + put32(GPPUD, 0); + delay(150); // spec + put32(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + put32(GPPUDCLK0, 0); + + put32(AUX_ENABLES, 1); // Enable mini uart (this also enables access to its registers) + put32(AUX_MU_CNTL_REG, 0); // Disable auto flow control and disable receiver and transmitter (for now) + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + put32(AUX_MU_LCR_REG, 3); // Enable 8 bit mode + put32(AUX_MU_MCR_REG, 0); // Set RTS line to be always high + put32(AUX_MU_BAUD_REG, 270); // Set baud rate to 115200 + + put32(AUX_MU_CNTL_REG, 3); // Finally, enable transmitter and receiver +} + +void _putchar(char character) +{ + uart_send(character); +} + +/** + * Display a string + */ +// void printf(char *fmt, ...) +// { +// __builtin_va_list args; +// __builtin_va_start(args, fmt); +// // we don't have memory allocation yet, so we +// // simply place our string after our code +// char *s = (char *)&_end; +// // use sprintf to format our string +// vsprintf(s, fmt, args); +// // print out as usual +// while (*s) +// { +// /* convert newline to carrige return + newline */ +// if (*s == '\n') +// uart_send('\r'); +// uart_send(*s++); +// } +// } + +void asyn_read() +{ + memset(read_buffer, '\0', 100); + + put32(AUX_MU_IER_REG, 1); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (read_buffer[strlen(read_buffer) - 1] != '\r') + ; +} + +void asyn_write(char *str) +{ + strcpy(write_buffer, str); + len_WB = strlen(write_buffer); + + put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (strlen(write_buffer) != 0) + ; +} + +void uart_rx_handler() +{ + read_buffer[len_RB++] = get32(AUX_MU_IO_REG); + + if (read_buffer[len_RB - 1] == '\r') + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + read_buffer[len_RB] = '\0'; + len_RB = 0; + } +} + +void uart_tx_handler() +{ + if (buffer_count < len_WB) + put32(AUX_MU_IO_REG, write_buffer[buffer_count++]); + else + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + buffer_count = 0; + memset(write_buffer, '\0', 100); + } +} diff --git a/Lab3/src/lib/mm.S b/Lab3/src/lib/mm.S new file mode 100644 index 000000000..1bd32ff37 --- /dev/null +++ b/Lab3/src/lib/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/Lab3/src/lib/printf.c b/Lab3/src/lib/printf.c new file mode 100644 index 000000000..3eabd6eca --- /dev/null +++ b/Lab3/src/lib/printf.c @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "printf.h" + +#define PRINTF_DISABLE_SUPPORT_FLOAT + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct +{ + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) + { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)buffer; + (void)idx; + (void)maxlen; + if (character) + { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)idx; + (void)maxlen; + if (character) + { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) +{ + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) + { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (size_t i = len; i < width; i++) + { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) + { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) + { + len--; + if (len && (base == 16U)) + { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) + { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) + { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) + { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) + { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) + { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) + { + } + else if ((frac == 0U) || (frac & 1U)) + { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) + { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) + { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) + { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) + { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union + { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) + { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) + { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) + { + if ((int)prec > expval) + { + prec = (unsigned)((int)prec - expval - 1); + } + else + { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) + { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) + { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else + { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) + { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) + { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) + { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else + { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do + { + switch (*format) + { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) + { + width = _atoi(&format); + } + else if (*format == '*') + { + const int w = va_arg(va, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else + { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) + { + precision = _atoi(&format); + } + else if (*format == '*') + { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) + { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') + { + base = 16U; + } + else if (*format == 'o') + { + base = 8U; + } + else if (*format == 'b') + { + base = 2U; + } + else + { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) + { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) + { + // signed + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else + { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else + { + // unsigned + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': + { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': + { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) + { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': + { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) + { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } + else + { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char *buffer, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} \ No newline at end of file diff --git a/Lab3/src/lib/queue.c b/Lab3/src/lib/queue.c new file mode 100644 index 000000000..6b56b4dca --- /dev/null +++ b/Lab3/src/lib/queue.c @@ -0,0 +1,7 @@ +void queue_push(char c) +{ +} + +void queue_pop() +{ +} \ No newline at end of file diff --git a/Lab3/src/lib/simple_malloc.c b/Lab3/src/lib/simple_malloc.c new file mode 100644 index 000000000..b7a80a897 --- /dev/null +++ b/Lab3/src/lib/simple_malloc.c @@ -0,0 +1,8 @@ +char *counter = (char *)0x10000000; + +void *simple_malloc(unsigned int size) +{ + char *dest = counter; + counter += size; + return dest; +} diff --git a/Lab3/src/lib/string.c b/Lab3/src/lib/string.c new file mode 100644 index 000000000..be47302ee --- /dev/null +++ b/Lab3/src/lib/string.c @@ -0,0 +1,79 @@ +#include + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + unsigned char c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + break; + if (c1 == '\0') + return 0; + } + + return c1 - c2; +} + +int strlen(const char *str) +{ + const unsigned char *s = (const unsigned char *)str; + unsigned int len = 0; + + while (1) + { + if (*s++ == '\0') + return len; + len++; + } +} + +char *strcpy(char *destination, const char *source) +{ + // return if no memory is allocated to the destination + if (destination == NULL) + { + return NULL; + } + + // take a pointer pointing to the beginning of the destination string + char *ptr = destination; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + + // include the terminating null character + *destination = '\0'; + + // the destination is returned by standard `strcpy()` + return ptr; +} + +// A simple atoi() function +int atoi(char *str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + res = res * 10 + str[i] - '0'; + + // return result. + return res; +} diff --git a/Lab3/src/lib/utils.S b/Lab3/src/lib/utils.S new file mode 100644 index 000000000..c35527a42 --- /dev/null +++ b/Lab3/src/lib/utils.S @@ -0,0 +1,15 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret diff --git a/Lab3/src/userprogram/link.ld b/Lab3/src/userprogram/link.ld new file mode 100644 index 000000000..28c90843a --- /dev/null +++ b/Lab3/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x200000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab3/src/userprogram/user.S b/Lab3/src/userprogram/user.S new file mode 100644 index 000000000..8a5ea0c0b --- /dev/null +++ b/Lab3/src/userprogram/user.S @@ -0,0 +1,11 @@ +.section ".text.boot" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b \ No newline at end of file diff --git a/Lab3/userprogram.img b/Lab3/userprogram.img new file mode 100755 index 000000000..d1435f4f8 Binary files /dev/null and b/Lab3/userprogram.img differ diff --git a/Lab4/Makefile b/Lab4/Makefile new file mode 100644 index 000000000..b80e7f409 --- /dev/null +++ b/Lab4/Makefile @@ -0,0 +1,59 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only +CFLAGS = -Wall -O -ffreestanding -nostdlib -nostartfiles -g -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.img userprogram.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + @mkdir -p $(@D) + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/kernel/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/kernel/link.ld -o $(BUILD_DIR)/kernel/kernel8.elf $(filter $(BUILD_DIR)/kernel/%_c.o $(BUILD_DIR)/kernel/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel/kernel8.elf -O binary kernel8.img + +int_qemu: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +bootloader.img: $(SRC_DIR)/bootloader/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/bootloader/link.ld -o $(BUILD_DIR)/bootloader/bootloader.elf $(filter $(BUILD_DIR)/bootloader/%_c.o $(BUILD_DIR)/bootloader/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/bootloader/bootloader.elf bootloader.img + +userprogram.img: $(SRC_DIR)/userprogram/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/userprogram/link.ld -o $(BUILD_DIR)/userprogram/userprogram.elf $(filter $(BUILD_DIR)/userprogram/%_c.o $(BUILD_DIR)/userprogram/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/userprogram/userprogram.elf userprogram.img + +test: + @echo Hello + +pseudoTTY: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug_boot: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -s -S + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb \ No newline at end of file diff --git a/Lab4/bcm2710-rpi-3-b-plus.dtb b/Lab4/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..0dd0e2f43 Binary files /dev/null and b/Lab4/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab4/bootloader.img b/Lab4/bootloader.img new file mode 100755 index 000000000..fc1cd073a Binary files /dev/null and b/Lab4/bootloader.img differ diff --git a/Lab4/bootloader.py b/Lab4/bootloader.py new file mode 100644 index 000000000..1b99fa7d7 --- /dev/null +++ b/Lab4/bootloader.py @@ -0,0 +1,25 @@ +import os +import time + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +# with open('/dev/pts/35', "wb", buffering=0) as tty: +with open('/dev/ttyUSB0', "wb", buffering=0) as tty: + # send Start + tty.write(b"Start") + time.sleep(1) + + # send kernel size + tty.write(file_size.to_bytes(4, 'little')) + time.sleep(1) + + # send kernel + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + tty.write(data) + time.sleep(1) + diff --git a/Lab4/include/device_tree.h b/Lab4/include/device_tree.h new file mode 100644 index 000000000..e3e6aee78 --- /dev/null +++ b/Lab4/include/device_tree.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_TREE_H +#define _DEVICE_TREE_H + +typedef int (*fdt_callback)(void *); +int initramfs_callback(void *dtb); +int dtb_parser(void *dtb); +void fdt_traverse(fdt_callback cb, char *dtb); + +#endif /*_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab4/include/dynamic_alloc.h b/Lab4/include/dynamic_alloc.h new file mode 100644 index 000000000..55eb68307 --- /dev/null +++ b/Lab4/include/dynamic_alloc.h @@ -0,0 +1,35 @@ +#ifndef _DYNAMIC_ALLOC_H +#define _DYNAMIC_ALLOC_H + +#include "list.h" + +#define MAX_POOL_SIZE 256 +#define MIN_CHUNK_SIZE 8 +#define FREE 98 + +typedef struct _chunk +{ + int index; // const + int size; + void *addr; // const + int val; + int belong_page; + struct list_head list; +} chunk; + +typedef struct _pool_list +{ + struct list_head list; +} pool_list; + +void init_pool(); +void *get_chunk(int req_size); +int roundup_size(int size); +void split_page(int frame_index, int req_pool_index); +int remove_a_chunk_from_pool(int req_pool_index); +int free_chunk(int index); +void put_back_to_pool(int pool_index, int chunk_index); +void debug_pool(); +void free(void *addr); + +#endif /*_DYNAMIC_ALLOC_H */ diff --git a/Lab4/include/list.h b/Lab4/include/list.h new file mode 100644 index 000000000..a0fb29587 --- /dev/null +++ b/Lab4/include/list.h @@ -0,0 +1,441 @@ +/* Linux-like double-linked list implementation */ + +#ifndef SYSPROG21_LIST_H +#define SYSPROG21_LIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *)0)->member) *__pmember = (ptr); \ + (type *)((char *)__pmember - offsetof(type, member)); \ + }) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr)-offsetof(type, member))) +#endif +#endif + + /** + * struct list_head - Head and node of a double-linked list + * @prev: pointer to the previous node in the list + * @next: pointer to the next node in the list + * + * The simple double-linked list consists of a head and nodes attached to + * this head. Both node and head share the same struct type. The list_* + * functions and macros can be used to access and modify this data structure. + * + * The @prev pointer of the list head points to the last list node of the + * list and @next points to the first list node of the list. For an empty list, + * both member variables point to the head. + * + * The list nodes are usually embedded in a container structure which holds the + * actual data. Such an container object is called entry. The helper list_entry + * can be used to calculate the object address from the address of the node. + */ + struct list_head + { + struct list_head *prev; + struct list_head *next; + }; + +/** + * LIST_HEAD - Declare list head and initialize it + * @head: name of the new object + */ +#define LIST_HEAD(head) struct list_head head = {&(head), &(head)} + + /** + * INIT_LIST_HEAD() - Initialize empty list head + * @head: pointer to list head + * + * This can also be used to initialize a unlinked list node. + * + * A node is usually linked inside a list, will be added to a list in + * the near future or the entry containing the node will be free'd soon. + * + * But an unlinked node may be given to a function which uses list_del(_init) + * before it ends up in a previously mentioned state. The list_del(_init) on an + * initialized node is well defined and safe. But the result of a + * list_del(_init) on an uninitialized node is undefined (unrelated memory is + * modified, crashes, ...). + */ + static inline void INIT_LIST_HEAD(struct list_head *head) + { + head->next = head; + head->prev = head; + } + + /** + * list_add() - Add a list node to the beginning of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add(struct list_head *node, struct list_head *head) + { + struct list_head *next = head->next; + + next->prev = node; + node->next = next; + node->prev = head; + head->next = node; + } + + /** + * list_add_tail() - Add a list node to the end of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add_tail(struct list_head *node, struct list_head *head) + { + struct list_head *prev = head->prev; + + prev->next = node; + node->next = head; + node->prev = prev; + head->prev = node; + } + + /** + * list_del() - Remove a list node from the list + * @node: pointer to the node + * + * The node is only removed from the list. Neither the memory of the removed + * node nor the memory of the entry containing the node is free'd. The node + * has to be handled like an uninitialized node. Accessing the next or prev + * pointer of the node is not safe. + * + * Unlinked, initialized nodes are also uninitialized after list_del. + * + * LIST_POISONING can be enabled during build-time to provoke an invalid memory + * access when the memory behind the next/prev pointer is used after a list_del. + * This only works on systems which prohibit access to the predefined memory + * addresses. + */ + static inline void list_del(struct list_head *node) + { + struct list_head *next = node->next; + struct list_head *prev = node->prev; + + next->prev = prev; + prev->next = next; + +#ifdef LIST_POISONING + node->prev = (struct list_head *)(0x00100100); + node->next = (struct list_head *)(0x00200200); +#endif + } + + /** + * list_del_init() - Remove a list node from the list and reinitialize it + * @node: pointer to the node + * + * The removed node will not end up in an uninitialized state like when using + * list_del. Instead the node is initialized again to the unlinked state. + */ + static inline void list_del_init(struct list_head *node) + { + list_del(node); + INIT_LIST_HEAD(node); + } + + /** + * list_empty() - Check if list head has no nodes attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not empty !0 - list is empty + */ + static inline int list_empty(const struct list_head *head) + { + return (head->next == head); + } + + /** + * list_is_singular() - Check if list head has exactly one node attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not singular !0 -list has exactly one entry + */ + static inline int list_is_singular(const struct list_head *head) + { + return (!list_empty(head) && head->prev == head->next); + } + + /** + * list_splice() - Add list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice(struct list_head *list, struct list_head *head) + { + struct list_head *head_first = head->next; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->next = list_first; + list_first->prev = head; + + list_last->next = head_first; + head_first->prev = list_last; + } + + /** + * list_splice_tail() - Add list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice_tail(struct list_head *list, + struct list_head *head) + { + struct list_head *head_last = head->prev; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->prev = list_last; + list_last->next = head; + + list_first->prev = head_last; + head_last->next = list_first; + } + + /** + * list_splice_init() - Move list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_init(struct list_head *list, + struct list_head *head) + { + list_splice(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_splice_tail_init() - Move list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) + { + list_splice_tail(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_cut_position() - Move beginning of a list to another list + * @head_to: pointer to the head of the list which receives nodes + * @head_from: pointer to the head of the list + * @node: pointer to the node in which defines the cutting point + * + * All entries from the beginning of the list @head_from to (including) the + * @node is moved to @head_to. + * + * @head_to is replaced when @head_from is not empty. @node must be a real + * list node from @head_from or the behavior is undefined. + */ + static inline void list_cut_position(struct list_head *head_to, + struct list_head *head_from, + struct list_head *node) + { + struct list_head *head_from_first = head_from->next; + + if (list_empty(head_from)) + return; + + if (head_from == node) + { + INIT_LIST_HEAD(head_to); + return; + } + + head_from->next = node->next; + head_from->next->prev = head_from; + + head_to->prev = node; + node->next = head_to; + head_to->next = head_from_first; + head_to->next->prev = head_to; + } + + /** + * list_move() - Move a list node to the beginning of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the beginning of + * @head + */ + static inline void list_move(struct list_head *node, struct list_head *head) + { + list_del(node); + list_add(node, head); + } + + /** + * list_move_tail() - Move a list node to the end of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the end of @head + */ + static inline void list_move_tail(struct list_head *node, + struct list_head *head) + { + list_del(node); + list_add_tail(node, head); + } + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_last_entry() - get last entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of last entry in list + */ +#define list_last_entry(head, type, member) \ + list_entry((head)->prev, type, member) + +/** + * list_for_each - iterate over list nodes + * @node: list_head pointer used as iterator + * @head: pointer to the head of the list + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + */ +#define list_for_each(node, head) \ + for (node = (head)->next; node != (head); node = node->next) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif + +/** + * list_for_each_safe - iterate over list nodes and allow deletes + * @node: list_head pointer used as iterator + * @safe: list_head pointer used to store info for next entry in list + * @head: pointer to the head of the list + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + */ +#define list_for_each_safe(node, safe, head) \ + for (node = (head)->next, safe = node->next; node != (head); \ + node = safe, safe = node->next) + +/** + * list_for_each_entry_safe - iterate over list entries and allow deletes + * @entry: pointer used as iterator + * @safe: @type pointer used to store info for next entry in list + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#define list_for_each_entry_safe(entry, safe, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member), \ + safe = list_entry(entry->member.next, __typeof__(*entry), member); \ + &entry->member != (head); entry = safe, \ + safe = list_entry(safe->member.next, __typeof__(*entry), member)) + +#undef __LIST_HAVE_TYPEOF + +#ifdef __cplusplus +} +#endif + +#endif /* SYSPROG21_LIST_H */ \ No newline at end of file diff --git a/Lab4/include/load_kernel.h b/Lab4/include/load_kernel.h new file mode 100644 index 000000000..a5dc68ffb --- /dev/null +++ b/Lab4/include/load_kernel.h @@ -0,0 +1,7 @@ +#ifndef _LOAD_KERNEL_H +#define _LOAD_KERNEL_H + +void load_kernel(char *dest); +void relocate(char *from_dest, char *to_dest); + +#endif /*_LOAD_KERNEL_H */ diff --git a/Lab4/include/math.h b/Lab4/include/math.h new file mode 100644 index 000000000..3107a97d8 --- /dev/null +++ b/Lab4/include/math.h @@ -0,0 +1 @@ +int pow(int base, int exp); \ No newline at end of file diff --git a/Lab4/include/mbox.h b/Lab4/include/mbox.h new file mode 100644 index 000000000..d43fd936b --- /dev/null +++ b/Lab4/include/mbox.h @@ -0,0 +1,6 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); + +#endif /*_MBOX_H */ diff --git a/Lab4/include/mbox_call.h b/Lab4/include/mbox_call.h new file mode 100644 index 000000000..7f60645d0 --- /dev/null +++ b/Lab4/include/mbox_call.h @@ -0,0 +1,9 @@ +#ifndef _MBOX_CALL_H +#define _MBOX_CALL_H + +/* a properly aligned buffer */ +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch); + +#endif /*_MBOX_CALL_H */ diff --git a/Lab4/include/mini_uart.h b/Lab4/include/mini_uart.h new file mode 100644 index 000000000..12ec374b1 --- /dev/null +++ b/Lab4/include/mini_uart.h @@ -0,0 +1,17 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void uart_send_string_of_size(char *str, int size); +void uart_hex(unsigned int d); +void uart_send_space(int size); + +void asyn_read(); +void asyn_write(); +void uart_rx_handler(); +void uart_tx_handler(); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab4/include/mm.h b/Lab4/include/mm.h new file mode 100644 index 000000000..1d947fb75 --- /dev/null +++ b/Lab4/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (6 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/Lab4/include/page_alloc.h b/Lab4/include/page_alloc.h new file mode 100644 index 000000000..0a191e1fe --- /dev/null +++ b/Lab4/include/page_alloc.h @@ -0,0 +1,39 @@ +#ifndef _PAGE_ALLOC_H +#define _PAGE_ALLOC_H + +// #define DEBUG + +#define MAX_ORDER 11 // order 0 ~ 11 +#define ALLOCATED -1 +#define FREE_BUDDY 99 +#define MIN_PAGE_SIZE 0x1000 +// #define FREE_MEM_START 0x10000000 +// #define FREE_MEM_END 0x20000000 +#define FREE_MEM_START 0x00 +#define FREE_MEM_END 0x3C000000 +#define TOTAL_NUM_PAGE (FREE_MEM_END - FREE_MEM_START) / MIN_PAGE_SIZE + +typedef struct _page_frame_node page_frame_node; +struct _page_frame_node +{ + int index; // const + int val; + void *addr; // const + int contiguous_head; // if allocated, this value keep track who (page) is the start of the contiguous memory in index + int allocated_order; + int chunk_order; // -1 if no chunk, otherwise 1, 2, 4, 6, 8, 16, 32 + page_frame_node *next; + page_frame_node *previous; +}; + +void init_page_frame(); +void *my_malloc(int req_size); +int get_page_from_free_list(int req_size, int who); +void add_to_free_list(page_frame_node *head_node, int index); +void remove_from_free_list(page_frame_node *free_list_node); +void put_back_to_free_list(int num_of_redundant_page, int index); +int free_page_frame(int index); +int merge_buddy(int *index, int buddy, int order); +void debug(); + +#endif /*_PAGE_ALLOC_H */ diff --git a/Lab4/include/peripherals/base.h b/Lab4/include/peripherals/base.h new file mode 100644 index 000000000..63f9c038f --- /dev/null +++ b/Lab4/include/peripherals/base.h @@ -0,0 +1,6 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab4/include/peripherals/device_tree.h b/Lab4/include/peripherals/device_tree.h new file mode 100644 index 000000000..d5d3bc729 --- /dev/null +++ b/Lab4/include/peripherals/device_tree.h @@ -0,0 +1,16 @@ +#ifndef _P_DEVICE_TREE_H +#define _P_DEVICE_TREE_H + +#define FDT_MAGIC 0xD00DFEED +#define FDT_VERSION 0x00000011 +#define FDT_TK_NULL 0X00000000 + +#define FDT_BEGIN_NODE 0X00000001 +#define FDT_END_NODE 0X00000002 +#define FDT_PROP 0X00000003 +#define FDT_NOP 0X00000004 +#define FDT_END 0X00000009 + +#define FDT_CPIO_INITRAMFS_PROPNAME "linux,initrd-start" + +#endif /*_P_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab4/include/peripherals/gpio.h b/Lab4/include/peripherals/gpio.h new file mode 100644 index 000000000..a7a6a5b4c --- /dev/null +++ b/Lab4/include/peripherals/gpio.h @@ -0,0 +1,25 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL0 (PBASE + 0x00200000) +#define GPFSEL1 (PBASE + 0x00200004) +#define GPFSEL2 (PBASE + 0x00200008) +#define GPFSEL3 (PBASE + 0x0020000C) +#define GPFSEL4 (PBASE + 0x00200010) +#define GPFSEL5 (PBASE + 0x00200014) +#define GPSET0 (PBASE + 0x0020001C) +#define GPSET1 (PBASE + 0x00200020) +#define GPCLR0 (PBASE + 0x00200028) +#define GPLEV0 (PBASE + 0x00200034) +#define GPLEV1 (PBASE + 0x00200038) +#define GPEDS0 (PBASE + 0x00200040) +#define GPEDS1 (PBASE + 0x00200044) +#define GPHEN0 (PBASE + 0x00200064) +#define GPHEN1 (PBASE + 0x00200068) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) +#define GPPUDCLK1 (PBASE + 0x0020009C) + +#endif /*_P_GPIO_H */ diff --git a/Lab4/include/peripherals/irq.h b/Lab4/include/peripherals/irq.h new file mode 100644 index 000000000..51e0cc5ea --- /dev/null +++ b/Lab4/include/peripherals/irq.h @@ -0,0 +1,27 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + +#define IRQ_BASIC_PENDING (PBASE + 0x0000B200) +#define IRQ_PENDING_1 (PBASE + 0x0000B204) +#define IRQ_PENDING_2 (PBASE + 0x0000B208) +#define FIQ_CONTROL (PBASE + 0x0000B20C) +#define ENABLE_IRQS_1 (PBASE + 0x0000B210) +#define ENABLE_IRQS_2 (PBASE + 0x0000B214) +#define ENABLE_BASIC_IRQS (PBASE + 0x0000B218) +#define DISABLE_IRQS_1 (PBASE + 0x0000B21C) +#define DISABLE_IRQS_2 (PBASE + 0x0000B220) +#define DISABLE_BASIC_IRQS (PBASE + 0x0000B224) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#define CORE0_INTR_SRC 0x40000060 +#define CORE1_INTR_SRC 0x40000064 +#define CORE2_INTR_SRC 0x40000068 +#define CORE3_INTR_SRC 0x4000006C + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/Lab4/include/peripherals/mbox_call.h b/Lab4/include/peripherals/mbox_call.h new file mode 100644 index 000000000..cfcc0d3ad --- /dev/null +++ b/Lab4/include/peripherals/mbox_call.h @@ -0,0 +1,40 @@ +#ifndef _P_MBOX_CALL_H +#define _P_MBOX_CALL_H + +#include "peripherals/base.h" + +#define VIDEOCORE_MBOX (PBASE + 0x0000B880) +#define MBOX_READ (VIDEOCORE_MBOX + 0x0) +#define MBOX_POLL (VIDEOCORE_MBOX + 0x10) +#define MBOX_SENDER (VIDEOCORE_MBOX + 0x14) +#define MBOX_STATUS (VIDEOCORE_MBOX + 0x18) +#define MBOX_CONFIG (VIDEOCORE_MBOX + 0x1C) +#define MBOX_WRITE (VIDEOCORE_MBOX + 0x20) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_MODEL 0x10001 +#define MBOX_TAG_REVISION 0x10002 +#define MBOX_TAG_MAC_ADDRESS 0x10003 +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_ARM_MEMORY 0x10005 +#define MBOX_TAG_VC_MEMORY 0x10006 +#define MBOX_TAG_CLOCKS 0x10007 +#define MBOX_TAG_LAST 0 + +#endif /* _P_MBOX_CALL_H */ diff --git a/Lab4/include/peripherals/mini_uart.h b/Lab4/include/peripherals/mini_uart.h new file mode 100644 index 000000000..71119b511 --- /dev/null +++ b/Lab4/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/Lab4/include/peripherals/reboot.h b/Lab4/include/peripherals/reboot.h new file mode 100644 index 000000000..f1d41ad2e --- /dev/null +++ b/Lab4/include/peripherals/reboot.h @@ -0,0 +1,8 @@ +#ifndef _P_REBOOT_H +#define _P_REBOOT_H + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +#endif /*_P_REBOOT_H */ \ No newline at end of file diff --git a/Lab4/include/printf.h b/Lab4/include/printf.h new file mode 100644 index 000000000..a9501cba0 --- /dev/null +++ b/Lab4/include/printf.h @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ + void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ + int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ + int sprintf_(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + int snprintf_(char *buffer, size_t count, const char *format, ...); + int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ + int vprintf_(const char *format, va_list va); + + /** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ + int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ \ No newline at end of file diff --git a/Lab4/include/read_cpio.h b/Lab4/include/read_cpio.h new file mode 100644 index 000000000..f33833343 --- /dev/null +++ b/Lab4/include/read_cpio.h @@ -0,0 +1,9 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); +char *find_content_addr(char *cpioDest, char *filename); +int load_userprogram(char *cpioDest, char *userDest); + +#endif /*_READ_CPIO_H */ \ No newline at end of file diff --git a/Lab4/include/reboot.h b/Lab4/include/reboot.h new file mode 100644 index 000000000..e9fb1d66c --- /dev/null +++ b/Lab4/include/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif /*_REBOOT_H */ \ No newline at end of file diff --git a/Lab4/include/reserve_mem.h b/Lab4/include/reserve_mem.h new file mode 100644 index 000000000..1bb8208a9 --- /dev/null +++ b/Lab4/include/reserve_mem.h @@ -0,0 +1,10 @@ +typedef struct _reserved_memory_block +{ + unsigned long start; + unsigned long end; + char name[30]; +} reserved_memory_block; + +void memory_reserve(unsigned long start, unsigned long end, char *name); +int check_contain_RM(unsigned long start, unsigned long end); +void memory_init(); \ No newline at end of file diff --git a/Lab4/include/shell.h b/Lab4/include/shell.h new file mode 100644 index 000000000..f677e039b --- /dev/null +++ b/Lab4/include/shell.h @@ -0,0 +1,7 @@ +#ifndef _SHELL_H +#define _SHELL_H + +void shell_main(char *); +void shell_start(); + +#endif /*_SHELL_H */ diff --git a/Lab4/include/stdlib.h b/Lab4/include/stdlib.h new file mode 100644 index 000000000..715d7bcda --- /dev/null +++ b/Lab4/include/stdlib.h @@ -0,0 +1,25 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" +#include "utils.h" +#include "mini_uart.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const char *str); +char *strcpy(char *destination, const char *source); +int atoi(char *str); + +void *memset(void *dest, register int val, int len); +int memcmp(void *s1, void *s2, int n); +int hex2int(char *s, int n); + +void *simple_malloc(unsigned int size); + +// void printf(char *fmt, ...); +// unsigned int sprintf(char *dst, char *fmt, ...); +// unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +#endif /*_STDLIB_H */ diff --git a/Lab4/include/test_mem_alloc.h b/Lab4/include/test_mem_alloc.h new file mode 100644 index 000000000..728855f3d --- /dev/null +++ b/Lab4/include/test_mem_alloc.h @@ -0,0 +1 @@ +void test_mem_alloc(); \ No newline at end of file diff --git a/Lab4/include/timer.h b/Lab4/include/timer.h new file mode 100644 index 000000000..df945b2ea --- /dev/null +++ b/Lab4/include/timer.h @@ -0,0 +1,13 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#define MESSAGE_BUFFER 50 +#define SECONDS_BUFFER 20 + +void get_current_time(); +void el0_timer_handler(long cntpct_el0, long cntfrq_el0); +void el1_timer_handler(long cntpct_el0, long cntfrq_el0); +void add_timer(int sec, char *mes); +int is_timer_queue_empty(); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab4/include/utils.h b/Lab4/include/utils.h new file mode 100644 index 000000000..a23ae37a7 --- /dev/null +++ b/Lab4/include/utils.h @@ -0,0 +1,8 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay ( unsigned long); +extern void put32 ( unsigned long, unsigned int ); +extern unsigned int get32 ( unsigned long ); + +#endif /*_BOOT_H */ diff --git a/Lab4/initramfs.cpio b/Lab4/initramfs.cpio new file mode 100644 index 000000000..e7ad0c236 Binary files /dev/null and b/Lab4/initramfs.cpio differ diff --git a/Lab4/kernel8.img b/Lab4/kernel8.img new file mode 100755 index 000000000..0abff3df6 Binary files /dev/null and b/Lab4/kernel8.img differ diff --git a/Lab4/rootfs/a.c b/Lab4/rootfs/a.c new file mode 100644 index 000000000..d2398d75b --- /dev/null +++ b/Lab4/rootfs/a.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + printf("HAHA return 1\n"); + + return 1; +} diff --git a/Lab4/rootfs/cat.txt b/Lab4/rootfs/cat.txt new file mode 100644 index 000000000..949e8345b --- /dev/null +++ b/Lab4/rootfs/cat.txt @@ -0,0 +1 @@ +No cat here. \ No newline at end of file diff --git a/Lab4/rootfs/one b/Lab4/rootfs/one new file mode 100644 index 000000000..50ecbb596 --- /dev/null +++ b/Lab4/rootfs/one @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 diff --git a/Lab4/rootfs/ts.txt b/Lab4/rootfs/ts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Lab4/rootfs/userprogram.img b/Lab4/rootfs/userprogram.img new file mode 100755 index 000000000..c6419d232 Binary files /dev/null and b/Lab4/rootfs/userprogram.img differ diff --git a/Lab4/send_kernel.py b/Lab4/send_kernel.py new file mode 100644 index 000000000..1b17155f2 --- /dev/null +++ b/Lab4/send_kernel.py @@ -0,0 +1,40 @@ +# This python script is for macOS +from serial import Serial +import os +import time +import fcntl +import threading + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +with Serial('/dev/tty.usbserial-0001', 115200) as ser: + sem = threading.Semaphore() + # set tty to non-blocking mode + fcntl.fcntl(ser, fcntl.F_SETFL, os.O_NONBLOCK) + + input("Press anything to start transmission\n") + + print("Sending Strat...") + sem.acquire() + ser.write(b"Start") + sem.release() + time.sleep(1) + + sem.acquire() + ser.write(file_size.to_bytes(4, 'little')) + sem.release() + time.sleep(1) + + print("Start sending kernel img by uart...") + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + sem.acquire() + ser.write(data) + sem.release() + time.sleep(1) + + print("Transfer finished!") diff --git a/Lab4/src/bootloader/boot.S b/Lab4/src/bootloader/boot.S new file mode 100644 index 000000000..f6b6f7474 --- /dev/null +++ b/Lab4/src/bootloader/boot.S @@ -0,0 +1,25 @@ +#include "mm.h" + +.section ".text.boot" + +.globl _start +_start: + mov x24, x0 + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + adr x0, __bss_start + adr x1, __bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + mov x0, x24 + bl bootloader_main + b proc_hang // should never come here diff --git a/Lab4/src/bootloader/bootloader.c b/Lab4/src/bootloader/bootloader.c new file mode 100644 index 000000000..327413557 --- /dev/null +++ b/Lab4/src/bootloader/bootloader.c @@ -0,0 +1,15 @@ +#include "mini_uart.h" +#include "load_kernel.h" + +void bootloader_main(void) +{ + uart_init(); + + uart_send_string("Relocatting ...\n"); + char *from_dest = (char *)0x80000; + char *to_dest = (char *)0x60000; + relocate((char *)from_dest, (char *)to_dest); + + char *dest = (char *)0x80000; + load_kernel((char *)dest); +} diff --git a/Lab4/src/bootloader/config.txt b/Lab4/src/bootloader/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/Lab4/src/bootloader/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/Lab4/src/bootloader/link.ld b/Lab4/src/bootloader/link.ld new file mode 100644 index 000000000..1fca3dfb7 --- /dev/null +++ b/Lab4/src/bootloader/link.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0x80000; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; +__loader_size = (_end - _start); diff --git a/Lab4/src/bootloader/load_kernel.c b/Lab4/src/bootloader/load_kernel.c new file mode 100644 index 000000000..00808de84 --- /dev/null +++ b/Lab4/src/bootloader/load_kernel.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include "mini_uart.h" +#include "peripherals/mini_uart.h" +#include "stdlib.h" + +extern int __loader_size; +extern void *_dtb_ptr; + +void load_kernel(char *dest) +{ + int size = 0; + + uart_send_string("Waiting for kernel8.img ...\n"); + + char start[5] = "Start"; + + for (int i = 0; i < 5; i++) + { + if (uart_recv() != start[i]) + i = 0; + } + + uart_send_string("Start transmitting ...\n"); + + size = uart_recv(); + size |= uart_recv() << 8; + size |= uart_recv() << 16; + size |= uart_recv() << 24; + + printf("Size of kernel is = %d bytes\n", size); + + // read the kernel + while (size--) + { + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + *dest++ = get32(AUX_MU_IO_REG); + } + + uart_send_string("End transmitting ...\n"); + + // restore arguments and jump to the new kernel. + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + // we must force an absolute address to branch to + "mov x30, 0x80000; ret"); +} + +void relocate(char *from_dest, char *to_dest) +{ + long long size = (long long)&__loader_size; + + while (size--) + { + *to_dest++ = *from_dest++; + } + + char *redicrect = __builtin_return_address(0) + (to_dest - from_dest); + + goto *redicrect; +} \ No newline at end of file diff --git a/Lab4/src/kernel/boot.S b/Lab4/src/kernel/boot.S new file mode 100644 index 000000000..81fb65548 --- /dev/null +++ b/Lab4/src/kernel/boot.S @@ -0,0 +1,88 @@ +#include "mm.h" + +.global _dtb_ptr +.section .data +_dtb_ptr: .dc.a 0x0 + +.section ".text.boot" + +.globl _start +_start: + cbz x24, x24c // Check if bootloader, see bootloader's boot.S for more info +x24nc: + ldr x21, =_dtb_ptr + str x24, [x21] + b go +x24c: + ldr x21, =_dtb_ptr + str x0, [x21] +go: + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + + ldr x1, =_start + + // get CurrentEL + mrs x0, CurrentEL + and x0, x0, #12 // clear reserved bits + + // running at EL3? branch if in EL3 + cmp x0, #12 + beq from_el3_to_el2 + + // running at EL1? branch if in EL1 + cmp x0, #4 + beq bss + + bl from_el2_to_el1 + +bss: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + bl set_el1_exception_vector_table // set el1 exception vector table base + + mov sp, #LOW_MEMORY // 4MB + // ldr x1, =_start + // mov sp, x1 + bl kernel_main + b proc_hang // should never come here + +from_el3_to_el2: + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, from_el2_to_el1 + msr elr_el3, x2 + eret + +from_el2_to_el1: + msr sp_el1, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // change execution level to EL1 + mov x2, #0x3c5 + msr spsr_el2, x2 + msr elr_el2, lr + eret \ No newline at end of file diff --git a/Lab4/src/kernel/config.txt b/Lab4/src/kernel/config.txt new file mode 100644 index 000000000..279349696 --- /dev/null +++ b/Lab4/src/kernel/config.txt @@ -0,0 +1,2 @@ +kernel_old=1 +disable_commandline_tags=1 diff --git a/Lab4/src/kernel/device_tree.c b/Lab4/src/kernel/device_tree.c new file mode 100644 index 000000000..bcb5b17ce --- /dev/null +++ b/Lab4/src/kernel/device_tree.c @@ -0,0 +1,266 @@ +#include +#include "peripherals/device_tree.h" +#include "stdlib.h" +#include "mini_uart.h" +#include "device_tree.h" + +typedef struct fdt_header +{ + uint32_t magic; // big-endian + 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; +} fdt_header; + +typedef struct +{ + uint32_t len; + uint32_t nameoff; +} fdt_prop; + +char *cpioDest; + +static uint64_t pad_to_4(void *num); +static uint32_t rev32(uint32_t val); +static uint64_t endian_rev(void *input, int dtype_size); + +int initramfs_callback(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + uint8_t *p = off_dt_struct; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + if (!strcmp(FDT_CPIO_INITRAMFS_PROPNAME, prop_name)) + { + uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); + printf("initramfs_addr at %p\n", addr); + cpioDest = (char *)addr; + } + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +int dtb_parser(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + int depth = 0; + uint8_t *p = off_dt_struct; + char *node_name = NULL; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + node_name = (char *)p; + if (depth == 0) + printf("\\ {\n"); + else + { + uart_send_space(depth * 3); + printf("%s {\n", node_name); + } + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + depth++; + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + depth--; + uart_send_space(depth * 3); + printf("};\n"); + printf("\n"); + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + + if (!strcmp(prop_name, "#address-cells") || !strcmp(prop_name, "#size-cells") || !strcmp(prop_name, "interrupt-parent")) + { + // + uart_send_space(depth * 3); + printf("%s = <%d>;\n", prop_name, rev32(*((uint32_t *)prop_val))); + } + else if (!strcmp(prop_name, "model") || !strcmp(prop_name, "status") || !strcmp(prop_name, "name") || !strcmp(prop_name, "device_type") || + !strcmp(prop_name, "chassis-type") || !strcmp(prop_name, "bootargs") || !strcmp(prop_name, "stdout-path") || !strcmp(prop_name, "stdin-path") || + !strcmp(prop_name, "power-isa-version") || !strcmp(prop_name, "mmu-type") || !strcmp(prop_name, "label") || !strcmp(prop_name, "phy-connection-type")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else if (!strcmp(prop_name, "compatible")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else + { + uart_send_space(depth * 3); + printf("%s = %s;\n", prop_name, prop_val); + } + + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +void fdt_traverse(fdt_callback cb, char *dtb) +{ + if (cb(dtb) == -1) + printf("fdt_traverse failed.\n"); + + return; +} + +static uint64_t pad_to_4(void *num) +{ + uint64_t modded = ((uint64_t)num) % 4; + return modded == 0 ? 0 : 4 - modded; +} + +static uint32_t rev32(uint32_t val) +{ + return (uint32_t)endian_rev(&val, 4); +} + +/** Transform data from litle to big endian, or from big to little endian + * @param input: Pointer to a value that needs to be transformed + * @param dtype_size: data type size, size of each item in bytes. Possible value: 1, 2, 4, 8 + */ +static uint64_t endian_rev(void *input, int dtype_size) +{ + const uint8_t *ptr = (uint8_t *)input; + uint64_t ret = 0; + + switch (dtype_size) + { + // int8_t, uint8_t + case 1: + // No need to transform to big endian since the data type size is 1 byte + break; + + // int16_t, uint16_t + case 2: + ret = (ptr[0] << 8) | ptr[1]; + break; + + // int32_t, uint32_t + case 4: + ret = (ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + ptr[3]; + break; + + // int64_t, uint64_t + // case 8: + // ret = (ptr[0] << 56) | + // (ptr[1] << 48) | + // (ptr[2] << 40) | + // (ptr[3] << 32) | + // (ptr[4] << 24) | + // (ptr[5] << 16) | + // (ptr[6] << 8) | + // ptr[7]; + // break; + + default: + printf("[Error] Endian transformation(%d) not implemented. @line %d, file:%s\r\n", dtype_size, __LINE__, __FILE__); + break; + } + return ret; +} \ No newline at end of file diff --git a/Lab4/src/kernel/dynamic_alloc.c b/Lab4/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..f42b15d3d --- /dev/null +++ b/Lab4/src/kernel/dynamic_alloc.c @@ -0,0 +1,239 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "list.h" + +extern page_frame_node frame_array[TOTAL_NUM_PAGE]; + +pool_list pool[33]; // 1, 2, 4, 6, 8, 16, 32 +chunk chunk_array[3000]; +int global_chunk_index = -1; +int record_chunk_index = -1; + +void init_pool() +{ + for (int i = 0; i < 33; i++) + INIT_LIST_HEAD(&pool[i].list); + return; +} + +chunk *new_chunk() +{ + global_chunk_index++; + chunk_array[global_chunk_index].index = global_chunk_index; + return &chunk_array[global_chunk_index]; +} + +void *get_chunk(int req_size) +{ + int req_pool_index = roundup_size(req_size); // req_pool_index * MIN_CHUNK_SIZE = req_size + + if (list_empty(&pool[req_pool_index].list)) + { + // empty pool on req_size + int frame_index = get_page_from_free_list(MIN_PAGE_SIZE, req_pool_index); + record_chunk_index = global_chunk_index; + split_page(frame_index, req_pool_index); + } + + int index = remove_a_chunk_from_pool(req_pool_index); + if (index == -1) + return NULL; + + void *addr = chunk_array[index].addr; + return addr; +} + +void split_page(int frame_index, int req_pool_index) +{ + int split_size = (req_pool_index * MIN_CHUNK_SIZE); + for (int i = 0; i < (MIN_PAGE_SIZE / split_size); i++) + { + chunk *new = new_chunk(); + new->size = split_size; + new->addr = (void *)frame_array[frame_index].addr + i *split_size; + new->val = FREE; + new->belong_page = frame_index; + list_add_tail(&new->list, &pool[req_pool_index].list); + } +} + +int remove_a_chunk_from_pool(int req_pool_index) +{ + chunk *alloc_chunk = container_of(pool[req_pool_index].list.next, chunk, list); + list_del_init(pool[req_pool_index].list.next); + alloc_chunk->val = ALLOCATED; + return alloc_chunk->index; +} + +void put_back_to_pool(int pool_index, int chunk_index) +{ + // addr to insert + struct list_head *node = &chunk_array[chunk_index].list; + unsigned long addr_long = (unsigned long)chunk_array[chunk_index].addr; + + // insert in by the order of addr + struct list_head *iter = &pool[pool_index].list; + struct list_head *start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + unsigned long iter_addr_long = (unsigned long)tmp->addr; + if (iter_addr_long > addr_long) + { + // list_insert() + iter->prev->next = node; + node->prev = iter->prev; + iter->prev = node; + node->next = iter; + + tmp->size = -1; + tmp->val = FREE; + + break; + } + } + + // check if there are free chunks in same page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + int count = 0; + int page_id = addr_long >> 12; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + count++; + else + break; + } + if (count == (MIN_PAGE_SIZE / (pool_index * MIN_CHUNK_SIZE))) + { + // There is a free page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + break; + } + chunk *first_chunk_in_page = list_entry(iter, chunk, list); + for (int i = 0; i < count; i++) + { + chunk *tmp = list_entry(iter, chunk, list); + tmp->val = FREE; + struct list_head *tmp_next = iter->next; + iter->prev->next = iter->next; + iter->next->prev = iter->prev; + iter->prev = iter; + iter->next = iter; + iter = tmp_next; + } + free_page_frame(first_chunk_in_page->belong_page); + } + + return; +} + +int free_chunk(int index) +{ + // free the page + int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; + put_back_to_pool(pool_index, index); + + return 0; +} + +void free(void *addr) +{ + // Check addr is in which page frame + unsigned long addr_long = (unsigned long)addr; + int frame_index = (addr_long - FREE_MEM_START) / MIN_PAGE_SIZE; + + if (frame_array[frame_index].val != ALLOCATED) + { + printf("This page is Not Allocated yet\n"); + return; + } + + if (frame_array[frame_index].chunk_order != -1) + { + // used to allocate chunks, find chunk index + unsigned long frame_start_addr = (unsigned long)frame_array[frame_index].addr; + int chunk_index = ((addr_long - frame_start_addr) / (frame_array[frame_index].chunk_order * MIN_CHUNK_SIZE)) + record_chunk_index + 1; + // Check if is OK to free + if (chunk_index >= global_chunk_index || chunk_array[chunk_index].val != ALLOCATED) + { + printf("This chunk is Not Allocated yet\n"); + return; + } + + free_chunk(chunk_index); + return; + } + else + { + free_page_frame(frame_index); + return; + } +} + +int roundup_size(int size) +{ + switch (size) + { + case 1 ... 8: + return 1; + case 9 ... 16: + return 2; + case 17 ... 32: + return 4; + case 33 ... 48: + return 6; + case 49 ... 64: + return 8; + case 65 ... 128: + return 16; + case 129 ... 256: + return 32; + } + return 0; +} + +void debug_pool() +{ + printf("** DEBUGGING pool\n"); + for (int i = 0; i < 33; i++) + { + struct list_head *iter; + struct list_head *start; + iter = &pool[i].list; + start = &pool[i].list; + printf("pool[%d] -> ", i); + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + printf("addr %p -> ", tmp->addr); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING chunk\n"); + for (int i = 0; i < 20; i++) + { + printf("chunk_array[%d].index = %d\n", i, chunk_array[i].index); + printf("chunk_array[%d].size = %d\n", i, chunk_array[i].size); + printf("chunk_array[%d].addr = %p\n", i, chunk_array[i].addr); + printf("chunk_array[%d].val = %d\n", i, chunk_array[i].val); + printf("chunk_array[%d].belong_page= %d\n", i, chunk_array[i].belong_page); + printf("\n"); + } + printf("**\n"); +} \ No newline at end of file diff --git a/Lab4/src/kernel/exception.S b/Lab4/src/kernel/exception.S new file mode 100644 index 000000000..ba3cfbc06 --- /dev/null +++ b/Lab4/src/kernel/exception.S @@ -0,0 +1,135 @@ +#define CORE0_INTERRUPT_SOURCE 0x40000060 +#define IIR_ADDR 0x3f215048 + +.global set_el1_exception_vector_table +.global exception_vector_table + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 8 + 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] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 8 +.endm + +.align 11 // vector table should be aligned to 0x800 +el1_exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_same_el + .align 7 + b exception_handler_irq_sp_elx_same_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_lower_el + .align 7 + b exception_handler_irq_sp_elx_lower_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_el1_exception_vector_table: + adr x0, el1_exception_vector_table + msr vbar_el1, x0 + ret lr + +exception_handler: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_sync_sp_elx_lower_el: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_irq_sp_elx_lower_el: + b el0_core_timer_handler + +exception_handler_sync_sp_elx_same_el: + b exception_handler + +exception_handler_irq_sp_elx_same_el: + b el1_core_interrupt_handler + +el0_core_timer_handler: + save_all + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all + eret + +el1_core_interrupt_handler: + save_all + bl el1_irq_interrupt_handler + load_all + eret \ No newline at end of file diff --git a/Lab4/src/kernel/exception.c b/Lab4/src/kernel/exception.c new file mode 100644 index 000000000..cc1c2fda6 --- /dev/null +++ b/Lab4/src/kernel/exception.c @@ -0,0 +1,150 @@ +#include "peripherals/mini_uart.h" +#include "peripherals/irq.h" +#include "stdlib.h" +#include "timer.h" + +/** + * common exception handler + */ +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + // print out interruption type + switch (type) + { + case 0: + uart_send_string("Synchronous"); + break; + case 1: + uart_send_string("IRQ"); + break; + case 2: + uart_send_string("FIQ"); + break; + case 3: + uart_send_string("SError"); + break; + } + uart_send_string(": "); + // decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28) + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown"); + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE"); + break; + case 0b001110: + uart_send_string("Illegal execution"); + break; + case 0b010101: + uart_send_string("System call"); + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL"); + break; + case 0b100001: + uart_send_string("Instruction abort, same EL"); + break; + case 0b100010: + uart_send_string("Instruction alignment fault"); + break; + case 0b100100: + uart_send_string("Data abort, lower EL"); + break; + case 0b100101: + uart_send_string("Data abort, same EL"); + break; + case 0b100110: + uart_send_string("Stack alignment fault"); + break; + case 0b101100: + uart_send_string("Floating point"); + break; + default: + uart_send_string("Unknown"); + break; + } + // decode data abort cause + if (esr >> 26 == 0b100100 || esr >> 26 == 0b100101) + { + uart_send_string(", "); + switch ((esr >> 2) & 0x3) + { + case 0: + uart_send_string("Address size fault"); + break; + case 1: + uart_send_string("Translation fault"); + break; + case 2: + uart_send_string("Access flag fault"); + break; + case 3: + uart_send_string("Permission fault"); + break; + } + switch (esr & 0x3) + { + case 0: + uart_send_string(" at level 0"); + break; + case 1: + uart_send_string(" at level 1"); + break; + case 2: + uart_send_string(" at level 2"); + break; + case 3: + uart_send_string(" at level 3"); + break; + } + } + // dump registers + uart_send_string("\nSPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + + return; +} + +void el1_irq_interrupt_handler() +{ + unsigned int irq_basic_pending = get32(IRQ_BASIC_PENDING); + irq_basic_pending &= (1 << 19); // clear bits + + // GPU IRQ 57 : UART Interrupt + if (irq_basic_pending) + { + if (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + { + uart_rx_handler(); + } + else if (get32(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + { + uart_tx_handler(); + } + } + // ARM Core Timer Interrupt + else if (get32(CORE0_INTR_SRC) & (1 << 1)) + { + long cntpct_el0, cntfrq_el0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + el1_timer_handler(cntpct_el0, cntfrq_el0); + } + + return; +} \ No newline at end of file diff --git a/Lab4/src/kernel/kernel.c b/Lab4/src/kernel/kernel.c new file mode 100644 index 000000000..b997687fa --- /dev/null +++ b/Lab4/src/kernel/kernel.c @@ -0,0 +1,19 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "reserve_mem.h" +#include "device_tree.h" +extern void *_dtb_ptr; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + fdt_traverse(initramfs_callback, _dtb_ptr); + + memory_init(); + + shell_start(); +} diff --git a/Lab4/src/kernel/link.ld b/Lab4/src/kernel/link.ld new file mode 100644 index 000000000..a460f25b2 --- /dev/null +++ b/Lab4/src/kernel/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab4/src/kernel/mbox.c b/Lab4/src/kernel/mbox.c new file mode 100644 index 000000000..f6d91caa7 --- /dev/null +++ b/Lab4/src/kernel/mbox.c @@ -0,0 +1,63 @@ +#include "peripherals/mbox_call.h" +#include "mbox_call.h" +#include "mini_uart.h" + +void mbox_main() +{ + // get the board's unique serial number with a mailbox call + mbox[0] = 21 * 4; // length of the message + mbox[1] = MBOX_REQUEST; // this is a request message + + mbox[2] = MBOX_TAG_MODEL; + mbox[3] = 4; + mbox[4] = 0; // ?? + mbox[5] = 0; + + mbox[6] = MBOX_TAG_REVISION; + mbox[7] = 4; + mbox[8] = 0; // ?? + mbox[9] = 0; + + mbox[10] = MBOX_TAG_GETSERIAL; // get serial number command + mbox[11] = 8; // buffer size + mbox[12] = 8; // ?? + mbox[13] = 0; // clear output buffer + mbox[14] = 0; + + mbox[15] = MBOX_TAG_ARM_MEMORY; // get serial number command + mbox[16] = 8; // buffer size + mbox[17] = 8; // ?? + mbox[18] = 0; // clear output buffer + mbox[19] = 0; + + mbox[20] = MBOX_TAG_LAST; + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) + { + uart_send_string("Board model: "); + uart_hex(mbox[5]); + uart_send_string("\n"); + + uart_send_string("Board revision: "); + uart_hex(mbox[9]); + uart_send_string("\n"); + + uart_send_string("Serial number: "); + uart_hex(mbox[14]); + uart_hex(mbox[13]); + uart_send_string("\n"); + + uart_send_string("ARM memory base address: "); + uart_hex(mbox[18]); + uart_send_string("\n"); + + uart_send_string("ARM memory size: "); + uart_hex(mbox[19]); + uart_send_string("\n"); + } + else + { + uart_send_string("Unable to query serial!\n"); + } +} \ No newline at end of file diff --git a/Lab4/src/kernel/mbox_call.c b/Lab4/src/kernel/mbox_call.c new file mode 100644 index 000000000..d0626f0e8 --- /dev/null +++ b/Lab4/src/kernel/mbox_call.c @@ -0,0 +1,42 @@ +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" + +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) +{ + unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} \ No newline at end of file diff --git a/Lab4/src/kernel/page_alloc.c b/Lab4/src/kernel/page_alloc.c new file mode 100644 index 000000000..c1487c5de --- /dev/null +++ b/Lab4/src/kernel/page_alloc.c @@ -0,0 +1,299 @@ +#include "page_alloc.h" +#include "math.h" +#include "stddef.h" +#include "stdlib.h" +#include "reserve_mem.h" +#include "dynamic_alloc.h" + +page_frame_node free_list[MAX_ORDER + 1]; +// int frame_array[TOTAL_NUM_PAGE]; // Why NOT use? no malloc to allocate new link list node +page_frame_node frame_array[TOTAL_NUM_PAGE]; +extern reserved_memory_block RMarray[100]; + +// initialize frame_array and free_list +void init_page_frame() +{ + // free_list + for (int i = 0; i <= MAX_ORDER; i++) + { + free_list[i].index = -1; // head of link list + free_list[i].next = NULL; + free_list[i].previous = NULL; + } + free_list[MAX_ORDER].next = &frame_array[0]; + + // frame_array + frame_array[0].index = 0; + frame_array[0].val = MAX_ORDER; + frame_array[0].addr = (void *)FREE_MEM_START; + frame_array[0].contiguous_head = -1; + frame_array[0].allocated_order = -1; + frame_array[0].next = &frame_array[0 + (1 << MAX_ORDER)]; + frame_array[0].previous = &free_list[MAX_ORDER]; + int previous_index = 0; + for (int i = 1; i < TOTAL_NUM_PAGE; i++) + { + frame_array[i].index = i; + frame_array[i].addr = (void *)FREE_MEM_START + i * MIN_PAGE_SIZE; + frame_array[i].contiguous_head = -1; + frame_array[i].allocated_order = -1; + if (i % (1 << MAX_ORDER) == 0) + { + frame_array[i].val = MAX_ORDER; + frame_array[i].next = NULL; + frame_array[i].previous = &frame_array[previous_index]; + frame_array[previous_index].next = &frame_array[i]; + previous_index = i; + } + else + { + frame_array[i].val = FREE_BUDDY; + frame_array[i].next = NULL; + frame_array[i].previous = NULL; + } + } + + return; +} + +void *my_malloc(int req_size) +{ + int ret = -1; + if (req_size < MAX_POOL_SIZE) + return get_chunk(req_size); + else + ret = get_page_from_free_list(req_size, -1); + + if (ret == -1) + return NULL; + + return frame_array[ret].addr; +} + +int get_page_from_free_list(int req_size, int who) +{ + int req_order = -1; + for (int i = 0; i <= MAX_ORDER; i++) + { + if (req_size <= MIN_PAGE_SIZE * pow(2, i)) + { + req_order = i; + break; + } + } + + int alloc_index = -1; + int alloc_order = req_order; + while (alloc_order <= MAX_ORDER) + { + if (free_list[alloc_order].next == NULL) // split high order + { + alloc_order++; + } + else + break; + } + if (alloc_order > MAX_ORDER) + return -1; + while (alloc_order > req_order) + { + // split high order + int removed_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + add_to_free_list(&free_list[alloc_order - 1], removed_index); + add_to_free_list(&free_list[alloc_order - 1], removed_index + pow(2, alloc_order - 1)); + frame_array[removed_index].val = alloc_order - 1; + frame_array[removed_index + (1 << (alloc_order - 1))].val = alloc_order - 1; + alloc_order--; + } + if (alloc_order != req_order) + return -1; + + // get require page + alloc_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + for (int i = 0; i < (1 << alloc_order); i++) + { + frame_array[alloc_index + i].val = ALLOCATED; + frame_array[alloc_index + i].next = NULL; + frame_array[alloc_index + i].previous = NULL; + frame_array[alloc_index + i].contiguous_head = alloc_index; + frame_array[alloc_index + i].allocated_order = alloc_order; + } + + // check the page if contains reserved memory + unsigned long start = (unsigned long)frame_array[alloc_index].addr; + unsigned long end = start + MIN_PAGE_SIZE * (1 << req_order); + int RM_index = check_contain_RM(start, end); + if (RM_index != 0) + { + // Need to change the page allocated + int new_alloc_index = get_page_from_free_list(req_size, who); + free_page_frame(alloc_index); + alloc_index = new_alloc_index; + } + +#ifdef DEBUG + debug(); +#endif + frame_array[alloc_index].chunk_order = who; + return alloc_index; +} + +// This does NOT modify frame_array value +void add_to_free_list(page_frame_node *head_node, int index) +{ + page_frame_node *iter = head_node; + while (iter->next != NULL) + iter = iter->next; + iter->next = &frame_array[index]; + frame_array[index].previous = iter; + frame_array[index].next = NULL; + + return; +} + +void remove_from_free_list(page_frame_node *node_to_be_removed) +{ + if (node_to_be_removed->next != NULL) + node_to_be_removed->next->previous = node_to_be_removed->previous; + node_to_be_removed->previous->next = node_to_be_removed->next; + + node_to_be_removed->next = NULL; + node_to_be_removed->previous = NULL; + + return; +} + +void put_back_to_free_list(int num_of_redundant_page, int index) // 從 index 開始有 num_of_redundant_page 個 free page +{ + int order_to_put = 0; + while (num_of_redundant_page >= (1 << order_to_put)) + order_to_put++; + order_to_put--; + add_to_free_list(&free_list[order_to_put], index); + frame_array[index].val = order_to_put; + if (num_of_redundant_page - (1 << order_to_put) != 0) + put_back_to_free_list(num_of_redundant_page - (1 << order_to_put), index + (1 << order_to_put)); + + return; +} + +int free_page_frame(int index) +{ + int contiguous_head = frame_array[index].contiguous_head; + if (contiguous_head != index) + { + printf("Please free the start page of this contiguous memory when allocated, which is index %d\n", contiguous_head); + return -1; + } + + // Check if buddy can merge + int allocated_order = frame_array[index].allocated_order; + int buddy_index = index ^ (1 << allocated_order); + if (frame_array[buddy_index].val == allocated_order) + { + // can merge + int merged_order = merge_buddy(&index, buddy_index, allocated_order); + if (buddy_index < index) + add_to_free_list(&free_list[merged_order], buddy_index); + else + add_to_free_list(&free_list[merged_order], index); + } + else + { + // can NOT merge + add_to_free_list(&free_list[allocated_order], index); + frame_array[index].val = allocated_order; + frame_array[index].contiguous_head = -1; + frame_array[index].allocated_order = -1; + for (int i = 1; i < (1 << allocated_order); i++) + { + frame_array[index + i].val = FREE_BUDDY; + frame_array[index + i].contiguous_head = -1; + frame_array[index + i].allocated_order = -1; + } + } + +#ifdef DEBUG + debug(); +#endif + + return 0; +} + +// return merged order, YES modify frame_array +int merge_buddy(int *index, int buddy, int order) +{ + if (order == MAX_ORDER) + return order; + + if (buddy < *index) + { + *index = buddy; + buddy = *index; // Find itself + } + + page_frame_node *iter = free_list[order].next; + while (iter->index != buddy) + iter = iter->next; + + remove_from_free_list(iter); + + frame_array[*index].val = order + 1; + for (int i = 1; i < (1 << (order + 1)); i++) + { + frame_array[i].val = FREE_BUDDY; + } + + if (order + 1 == MAX_ORDER) + return order + 1; + + int new_buddy = *index ^ (1 << (order + 1)); + if (new_buddy < *index) + { + *index = new_buddy; + new_buddy = *index; // Find itself + } + + if (frame_array[*index].val == frame_array[new_buddy].val) + { + frame_array[buddy].val = FREE_BUDDY; + return merge_buddy(index, new_buddy, order + 1); + } + else + return order + 1; +} + +void debug() +{ + printf("** DEBUGGING free_list\n"); + for (int i = 0; i <= MAX_ORDER; i++) + { + page_frame_node *iter; + iter = &free_list[i]; + printf("free_list[%d] -> ", i); + while (iter->next != NULL) + { + iter = iter->next; + printf("index %d -> ", iter->index); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING frame_array\n"); + for (int i = 2045; i < 2045 + 20; i++) + { + printf("frame_array[%d].addr = %p\n", i, frame_array[i].addr); + printf("frame_array[%d].val = %d\n", i, frame_array[i].val); + printf("frame_array[%d].contiguous_head = %d\n", i, frame_array[i].contiguous_head); + printf("frame_array[%d].allocated_order = %d\n", i, frame_array[i].allocated_order); + if (frame_array[i].next != NULL) + printf("frame_array[%d].next->index = %d\n", i, frame_array[i].next->index); + if (frame_array[i].previous != NULL) + printf("frame_array[%d].previous->index = %d\n", i, frame_array[i].previous->index); + } + printf("**\n"); + + return; +} diff --git a/Lab4/src/kernel/read_cpio.c b/Lab4/src/kernel/read_cpio.c new file mode 100644 index 000000000..665e57070 --- /dev/null +++ b/Lab4/src/kernel/read_cpio.c @@ -0,0 +1,151 @@ +#include "stdlib.h" +#include "mini_uart.h" + +typedef struct cpio_newc_header +{ + 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]; +} __attribute__((packed)) cpio_t; + +void read_cpio(char *cpioDest) +{ + uart_send_string("Type Offset Size Access rights\tFilename\n"); + + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + // print out meta information + uart_hex(hex2int(header->c_mode, 8)); // mode (access rights + type) + uart_send(' '); + uart_hex((unsigned int)((unsigned long)cpioDest) + sizeof(cpio_t) + ns); + uart_send(' '); + uart_hex(fs); // file size in hex + uart_send(' '); + uart_hex(hex2int(header->c_uid, 8)); // user id in hex + uart_send('.'); + uart_hex(hex2int(header->c_gid, 8)); // group id in hex + uart_send('\t'); + uart_send_string(cpioDest + sizeof(cpio_t)); // filename + uart_send_string("\n"); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } +} + +void read_content(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("cat: %s: No such file\n", filename); + return; + } + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + // print content + uart_send_string_of_size((char *)cpioDest, fs); +} + +char *find_content_addr(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("find_content_addr: %s: No such file\n", filename); + return NULL; + } + + return cpioDest; +} + +int load_userprogram(char *cpioDest, char *userDest) +{ + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + printf("load %p to %p\n", cpioDest, userDest); + printf("size: %d bytes\n", fs); + + // load content + while (fs--) + { + *userDest++ = *cpioDest++; + } + + if (fs == -1) + return 0; + + return 1; +} \ No newline at end of file diff --git a/Lab4/src/kernel/reboot.c b/Lab4/src/kernel/reboot.c new file mode 100644 index 000000000..da809c59a --- /dev/null +++ b/Lab4/src/kernel/reboot.c @@ -0,0 +1,19 @@ +#include "peripherals/reboot.h" + +void set(long addr, unsigned int value) +{ + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) +{ // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab4/src/kernel/reserved_mem.c b/Lab4/src/kernel/reserved_mem.c new file mode 100644 index 000000000..f213a8925 --- /dev/null +++ b/Lab4/src/kernel/reserved_mem.c @@ -0,0 +1,47 @@ +#include "reserve_mem.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "stdlib.h" + +reserved_memory_block RMarray[100]; +int RMindex = 0; + +void memory_reserve(unsigned long start, unsigned long end, char *name) +{ + RMindex++; + RMarray[RMindex].start = start; + RMarray[RMindex].end = end; + strcpy(RMarray[RMindex].name, name); +} + +// return value : if including RM, return which no. of RM. Otherwise, return 0. +int check_contain_RM(unsigned long start, unsigned long end) +{ + for (int i = 1; i <= RMindex; i++) + { + if (RMarray[i].start <= start && start <= RMarray[i].end) + return i; + else if (RMarray[i].start <= end && end <= RMarray[i].end) + return i; + else if (start <= RMarray[i].start && RMarray[i].end <= end) + return i; + else + continue; + } + return 0; +} + +void memory_init() +{ + init_page_frame(); + init_pool(); + + memory_reserve(0x0000, 0x1000, "Multicore Boot"); + memory_reserve(0x60000, 0x100000, "Kernel Img"); + memory_reserve(0x1000000, 0x1000fff, "Printf Buffer"); + memory_reserve(0x8000000, 0x8010000, "Initramfs"); + memory_reserve(0x15000000, 0x17000000, "User Program"); + memory_reserve(0x1000000, 0x1000990, "test"); + + return; +} \ No newline at end of file diff --git a/Lab4/src/kernel/shell.c b/Lab4/src/kernel/shell.c new file mode 100644 index 000000000..1f4be8888 --- /dev/null +++ b/Lab4/src/kernel/shell.c @@ -0,0 +1,231 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" +#include "timer.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "test_mem_alloc.h" + +extern void *_dtb_ptr; +extern char *cpioDest; +extern char read_buffer[100]; +// extern page_frame_node frame_array[TOTAL_NUM_PAGE]; +// extern chunk chunk_array[3000]; + +#define COMMAND_BUFFER 50 +#define FILENAME_BUFFER 20 +#define ARGV_BUFFER 30 + +void shell_start(); + +void shell_main(char *command) +{ + if (!strcmp(command, "help")) + { + uart_send_string("help\t: print this help menu\n"); + uart_send_string("hello\t: print Hello World!\n"); + uart_send_string("reboot\t: reboot the device\n"); + uart_send_string("ls\t: list information about files\n"); + uart_send_string("cat\t: copy each FILE to standard output\n"); + uart_send_string("dts\t: list devicetree\n"); + uart_send_string("svc\t: test svc interrupt in user program\n"); + uart_send_string("time\t: time 2 secs\n"); + uart_send_string("asynr\t: [test] asynchronous read\n"); + uart_send_string("asynw\t: [test] asynchronous write\n"); + uart_send_string("setTimeout\t: Usage: setTimeout \n"); + uart_send_string("alloc\t: [test] malloc and free\n"); + } + else if (!strcmp(command, "hello")) + { + uart_send_string("Hello World!\n"); + } + else if (!strcmp(command, "reboot")) + { + uart_send_string("Rebooting in 3 seconds\n"); + reset(3 << 16); + while (1) + ; + } + else if (!strcmp(command, "ls")) + { + // char *cpioDest = (char *)0x8000000; + read_cpio((char *)cpioDest); + } + else if (!memcmp(command, "cat", 3)) + { + if (command[3] != ' ' || command[4] == '\0') + { + printf("Usage: cat \n"); + return; + } + + char filename[FILENAME_BUFFER]; + memset(filename, '\0', FILENAME_BUFFER); + int i = 4; + while (command[i] != '\0') + { + filename[i - 4] = command[i]; + i++; + } + + read_content((char *)cpioDest, filename); + } + else if (!strcmp(command, "dts")) + { + fdt_traverse(dtb_parser, _dtb_ptr); + } + else if (!strcmp(command, "svc")) + { + // char *cpioDest = (char *)0x8000000; + char *cpioUserPgmDest = cpioDest; + char *userDest = (char *)0x200000; + cpioUserPgmDest = find_content_addr(cpioUserPgmDest, "userprogram.img"); + if (cpioUserPgmDest == NULL) + { + uart_send_string("FAIL to find userprogram.img\n"); + return; + } + if (load_userprogram(cpioUserPgmDest, userDest) != 0) + { + uart_send_string("FAIL to load user program.\n"); + return; + } + + asm volatile( + "mov x0, 0x3c0;" // EL0t + "msr spsr_el1, x0;" + "mov x0, 0x200000;" + "msr elr_el1, x0;" + "mov x0, 0x200000;" + "msr sp_el0, x0;" + "eret;"); + } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x1, 0x0;" // not sure why can't use x0, may have something to to with %0 + "msr spsr_el1, x1;" + "mov x2, %0;" + "add x2, x2, 12;" + "msr elr_el1, x2;" + "mov x2, 0x1000000;" + "msr sp_el0, x2;" + "mrs x2, cntfrq_el0;" + "add x2, x2, x2;" + "msr cntp_tval_el0, x2;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } + else if (!strcmp(command, "asynr")) + { + asyn_read(); + } + else if (!strcmp(command, "asynw")) + { + asyn_write(read_buffer); + uart_send('\n'); + } + else if (!memcmp(command, "setTimeout", 10)) + { + if (command[10] != ' ' || command[11] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char message[MESSAGE_BUFFER]; + memset(message, '\0', MESSAGE_BUFFER); + int i = 11; + while (command[i] != ' ' && command[i] != '\0') + { + message[i - 11] = command[i]; + i++; + } + + if (command[i] != ' ' || command[i] == '\0' || command[i + 1] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char seconds[SECONDS_BUFFER]; + memset(seconds, '\0', SECONDS_BUFFER); + while (command[i] != '\0') + { + seconds[i - strlen(message) - 1 - 11] = command[i]; + i++; + } + int sec; + sec = atoi(seconds); + + add_timer(sec, message); + } + else if (!memcmp(command, "alloc", 5)) + { + test_mem_alloc(); + } + else if (!memcmp(command, "debug", 5)) + { + debug(); + debug_pool(); + } + + return; +} + +void shell_start() +{ + uart_send_string("Starting shell...\n"); + char c; + int i = 0; + + char command[COMMAND_BUFFER]; + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("# "); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + command[i] = '\0'; + uart_send(c); + shell_main(command); + memset(command, '\0', COMMAND_BUFFER); + i = 0; + uart_send_string("# "); + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else if (c == 127) // Also backspace but delete + { + } + else + { + if (i < COMMAND_BUFFER) + { + if (c == 0) // solve the problem that first command on board wont work + continue; + command[i++] = c; + } + uart_send(c); + } + } + } +} diff --git a/Lab4/src/kernel/test_mem_alloc.c b/Lab4/src/kernel/test_mem_alloc.c new file mode 100644 index 000000000..50edee4c2 --- /dev/null +++ b/Lab4/src/kernel/test_mem_alloc.c @@ -0,0 +1,29 @@ +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "stdlib.h" + +void test_mem_alloc() +{ + void *a; + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + return; +} \ No newline at end of file diff --git a/Lab4/src/kernel/timer.S b/Lab4/src/kernel/timer.S new file mode 100644 index 000000000..9e0c519e9 --- /dev/null +++ b/Lab4/src/kernel/timer.S @@ -0,0 +1,16 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 +.global core_timer_enable + +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret + +/* +cntpct_el0: The timer’s current count. +cntp_cval_el0: A compared timer count. If cntpct_el0 >= cntp_cval_el0, interrupt the CPU core. +cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +*/ \ No newline at end of file diff --git a/Lab4/src/kernel/timer.c b/Lab4/src/kernel/timer.c new file mode 100644 index 000000000..f1a628220 --- /dev/null +++ b/Lab4/src/kernel/timer.c @@ -0,0 +1,150 @@ +#include "stdlib.h" +#include "timer.h" + +typedef struct timer_queue_node +{ + long second_ticks; + char message[MESSAGE_BUFFER]; +} tq; + +tq timer_queue[10]; +int timer_queue_front = 0; +int timer_queue_back = 0; + +void get_current_time() +{ + long cntpct_el0, cntfrq_el0; + long cntp_tval_el0; + long cntp_cval_el0; + + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + "mrs %2, cntp_tval_el0;" + "mrs %3, cntp_cval_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0), "=r"(cntp_tval_el0), "=r"(cntp_cval_el0)); + + long nowtime = cntpct_el0 / cntfrq_el0; + + printf("%ld seconds after booting\n", nowtime); + + // printf("cntpct_el0 = %d\n", cntpct_el0); + // printf("cntfrq_el0 = %d\n", cntfrq_el0); + // printf("cntp_tval_el0 = %d\n", cntp_tval_el0); + // printf("cntp_cval_el0 = %d\n", cntp_cval_el0); + + return; +} + +void add_timer(int sec, char *mes) +{ + get_current_time(); + + for (int i = 0; i < MESSAGE_BUFFER; i++) + timer_queue[timer_queue_back].message[i] = mes[i]; + + // transfer sec to frq and store to node.second + asm volatile( + "msr DAIFSet, 0xf;" + "mrs x3, cntfrq_el0;" + "mrs x4, cntpct_el0;" + "mov x2, %1;" // after secs seconds later will interrupt + "mul x2, x2, x3;" + "add x2, x2, x4;" + "mov %0, x2;" + : "=r"(timer_queue[timer_queue_back].second_ticks) + : "r"(sec) + : "x0", "x1", "x2", "x3", "x4", "memory"); // Uses register operands x0, x1, x2, x3, and x4 and specifies that the instruction may modify memory using the clobber list "x0", "x1", "x2", "x3", "x4", "memory". + + timer_queue_back++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + + asm volatile( + "msr cntp_cval_el0, %0;" + "bl core_timer_enable;" + "msr DAIFClr, 0xf;" + : + : "r"(timer_queue[timer_queue_front].second_ticks)); +} + +void el0_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + + return; +} + +void el1_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + printf("Message: %s\n", timer_queue[timer_queue_front].message); + + timer_queue_front++; + if (!is_timer_queue_empty()) + { + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + asm volatile( + "msr cntp_cval_el0, %0\n\t" // set expired time + "bl core_timer_enable\n\t" + : + : "r"(timer_queue[timer_queue_front].second_ticks) + :); + } + + return; +} + +int is_timer_queue_empty() +{ + return timer_queue_front == timer_queue_back ? 1 : 0; +} \ No newline at end of file diff --git a/Lab4/src/lib/hex2int.c b/Lab4/src/lib/hex2int.c new file mode 100644 index 000000000..4f30c1df8 --- /dev/null +++ b/Lab4/src/lib/hex2int.c @@ -0,0 +1,15 @@ +int hex2int(char *s, int n) +{ + int r = 0; + while (n-- > 0) + { + r <<= 4; + if (*s >= '0' && *s <= '9') + r += *s++ - '0'; + else if (*s >= 'A' && *s <= 'F') + r += *s++ - 'A' + 10; + else if (*s >= 'a' && *s <= 'f') + r += *s++ - 'a' + 10; + } + return r; +} \ No newline at end of file diff --git a/Lab4/src/lib/math.c b/Lab4/src/lib/math.c new file mode 100644 index 000000000..b96e3e335 --- /dev/null +++ b/Lab4/src/lib/math.c @@ -0,0 +1,9 @@ +int pow(int base, int exp) +{ + int result = 1; + for (int i = 0; i < exp; i++) + { + result *= base; + } + return result; +} \ No newline at end of file diff --git a/Lab4/src/lib/mem.c b/Lab4/src/lib/mem.c new file mode 100644 index 000000000..d74fb96fd --- /dev/null +++ b/Lab4/src/lib/mem.c @@ -0,0 +1,22 @@ +void *memset(void *dest, register int val, int len) +{ + register unsigned char *ptr = (unsigned char *)dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +int memcmp(void *s1, void *s2, int n) +{ + unsigned char *a = s1, *b = s2; + while (n-- > 0) + { + if (*a != *b) + { + return *a - *b; + } + a++; + b++; + } + return 0; +} \ No newline at end of file diff --git a/Lab4/src/lib/mini_uart.c b/Lab4/src/lib/mini_uart.c new file mode 100644 index 000000000..82b34aa13 --- /dev/null +++ b/Lab4/src/lib/mini_uart.c @@ -0,0 +1,183 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "peripherals/irq.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; + +char read_buffer[100]; +char write_buffer[100]; +int len_WB = 0; +int len_RB = 0; +int buffer_count = 0; + +void uart_send(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + + if (c != '\x7f') + put32(AUX_MU_IO_REG, c); + + if (c == '\n') + uart_send('\r'); +} + +char uart_recv(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r == '\r' ? '\n' : r; +} + +void uart_send_string(char *str) +{ + while (*str) + uart_send(*str++); +} + +void uart_send_space(int size) +{ + while (size--) + uart_send(' '); +} + +void uart_send_string_of_size(char *str, int size) +{ + while (size--) + uart_send(*str++); +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) +{ + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) + { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_init(void) +{ + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 2 << 12; // set alt5 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 2 << 15; // set alt5 for gpio15 + put32(GPFSEL1, selector); + + put32(GPPUD, 0); + delay(150); // spec + put32(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + put32(GPPUDCLK0, 0); + + put32(AUX_ENABLES, 1); // Enable mini uart (this also enables access to its registers) + put32(AUX_MU_CNTL_REG, 0); // Disable auto flow control and disable receiver and transmitter (for now) + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + put32(AUX_MU_LCR_REG, 3); // Enable 8 bit mode + put32(AUX_MU_MCR_REG, 0); // Set RTS line to be always high + put32(AUX_MU_BAUD_REG, 270); // Set baud rate to 115200 + + put32(AUX_MU_CNTL_REG, 3); // Finally, enable transmitter and receiver +} + +void _putchar(char character) +{ + uart_send(character); +} + +/** + * Display a string + */ +// void printf(char *fmt, ...) +// { +// __builtin_va_list args; +// __builtin_va_start(args, fmt); +// // we don't have memory allocation yet, so we +// // simply place our string after our code +// char *s = (char *)&_end; +// // use sprintf to format our string +// vsprintf(s, fmt, args); +// // print out as usual +// while (*s) +// { +// /* convert newline to carrige return + newline */ +// if (*s == '\n') +// uart_send('\r'); +// uart_send(*s++); +// } +// } + +void asyn_read() +{ + memset(read_buffer, '\0', 100); + + put32(AUX_MU_IER_REG, 1); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (read_buffer[strlen(read_buffer) - 1] != '\r') + ; +} + +void asyn_write(char *str) +{ + strcpy(write_buffer, str); + len_WB = strlen(write_buffer); + + put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (strlen(write_buffer) != 0) + ; +} + +void uart_rx_handler() +{ + read_buffer[len_RB++] = get32(AUX_MU_IO_REG); + + if (read_buffer[len_RB - 1] == '\r') + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + read_buffer[len_RB] = '\0'; + len_RB = 0; + } +} + +void uart_tx_handler() +{ + if (buffer_count < len_WB) + put32(AUX_MU_IO_REG, write_buffer[buffer_count++]); + else + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + buffer_count = 0; + memset(write_buffer, '\0', 100); + } +} diff --git a/Lab4/src/lib/mm.S b/Lab4/src/lib/mm.S new file mode 100644 index 000000000..1bd32ff37 --- /dev/null +++ b/Lab4/src/lib/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/Lab4/src/lib/printf.c b/Lab4/src/lib/printf.c new file mode 100644 index 000000000..3eabd6eca --- /dev/null +++ b/Lab4/src/lib/printf.c @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "printf.h" + +#define PRINTF_DISABLE_SUPPORT_FLOAT + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct +{ + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) + { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)buffer; + (void)idx; + (void)maxlen; + if (character) + { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)idx; + (void)maxlen; + if (character) + { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) +{ + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) + { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (size_t i = len; i < width; i++) + { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) + { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) + { + len--; + if (len && (base == 16U)) + { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) + { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) + { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) + { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) + { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) + { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) + { + } + else if ((frac == 0U) || (frac & 1U)) + { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) + { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) + { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) + { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) + { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union + { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) + { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) + { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) + { + if ((int)prec > expval) + { + prec = (unsigned)((int)prec - expval - 1); + } + else + { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) + { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) + { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else + { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) + { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) + { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) + { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else + { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do + { + switch (*format) + { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) + { + width = _atoi(&format); + } + else if (*format == '*') + { + const int w = va_arg(va, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else + { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) + { + precision = _atoi(&format); + } + else if (*format == '*') + { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) + { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') + { + base = 16U; + } + else if (*format == 'o') + { + base = 8U; + } + else if (*format == 'b') + { + base = 2U; + } + else + { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) + { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) + { + // signed + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else + { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else + { + // unsigned + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': + { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': + { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) + { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': + { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) + { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } + else + { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char *buffer, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} \ No newline at end of file diff --git a/Lab4/src/lib/queue.c b/Lab4/src/lib/queue.c new file mode 100644 index 000000000..6b56b4dca --- /dev/null +++ b/Lab4/src/lib/queue.c @@ -0,0 +1,7 @@ +void queue_push(char c) +{ +} + +void queue_pop() +{ +} \ No newline at end of file diff --git a/Lab4/src/lib/simple_malloc.c b/Lab4/src/lib/simple_malloc.c new file mode 100644 index 000000000..b7a80a897 --- /dev/null +++ b/Lab4/src/lib/simple_malloc.c @@ -0,0 +1,8 @@ +char *counter = (char *)0x10000000; + +void *simple_malloc(unsigned int size) +{ + char *dest = counter; + counter += size; + return dest; +} diff --git a/Lab4/src/lib/string.c b/Lab4/src/lib/string.c new file mode 100644 index 000000000..be47302ee --- /dev/null +++ b/Lab4/src/lib/string.c @@ -0,0 +1,79 @@ +#include + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + unsigned char c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + break; + if (c1 == '\0') + return 0; + } + + return c1 - c2; +} + +int strlen(const char *str) +{ + const unsigned char *s = (const unsigned char *)str; + unsigned int len = 0; + + while (1) + { + if (*s++ == '\0') + return len; + len++; + } +} + +char *strcpy(char *destination, const char *source) +{ + // return if no memory is allocated to the destination + if (destination == NULL) + { + return NULL; + } + + // take a pointer pointing to the beginning of the destination string + char *ptr = destination; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + + // include the terminating null character + *destination = '\0'; + + // the destination is returned by standard `strcpy()` + return ptr; +} + +// A simple atoi() function +int atoi(char *str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + res = res * 10 + str[i] - '0'; + + // return result. + return res; +} diff --git a/Lab4/src/lib/utils.S b/Lab4/src/lib/utils.S new file mode 100644 index 000000000..c35527a42 --- /dev/null +++ b/Lab4/src/lib/utils.S @@ -0,0 +1,15 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret diff --git a/Lab4/src/userprogram/link.ld b/Lab4/src/userprogram/link.ld new file mode 100644 index 000000000..28c90843a --- /dev/null +++ b/Lab4/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x200000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab4/src/userprogram/user.S b/Lab4/src/userprogram/user.S new file mode 100644 index 000000000..8a5ea0c0b --- /dev/null +++ b/Lab4/src/userprogram/user.S @@ -0,0 +1,11 @@ +.section ".text.boot" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b \ No newline at end of file diff --git a/Lab4/userprogram.img b/Lab4/userprogram.img new file mode 100755 index 000000000..c6419d232 Binary files /dev/null and b/Lab4/userprogram.img differ diff --git a/Lab5/Makefile b/Lab5/Makefile new file mode 100644 index 000000000..abe49536e --- /dev/null +++ b/Lab5/Makefile @@ -0,0 +1,62 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only +CFLAGS = -Wall -O -ffreestanding -nostdlib -nostartfiles -g -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.img userprogram.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + @mkdir -p $(@D) + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/kernel/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/kernel/link.ld -o $(BUILD_DIR)/kernel/kernel8.elf $(filter $(BUILD_DIR)/kernel/%_c.o $(BUILD_DIR)/kernel/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel/kernel8.elf -O binary kernel8.img + +int_qemu: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +bootloader.img: $(SRC_DIR)/bootloader/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/bootloader/link.ld -o $(BUILD_DIR)/bootloader/bootloader.elf $(filter $(BUILD_DIR)/bootloader/%_c.o $(BUILD_DIR)/bootloader/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/bootloader/bootloader.elf bootloader.img + +userprogram.img: $(SRC_DIR)/userprogram/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/userprogram/link.ld -o $(BUILD_DIR)/userprogram/userprogram.elf $(filter $(BUILD_DIR)/userprogram/%_c.o $(BUILD_DIR)/userprogram/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/userprogram/userprogram.elf userprogram.img + +test: + @echo Hello + +pseudoTTY: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug_boot: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -s -S + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb diff --git a/Lab5/bcm2710-rpi-3-b-plus.dtb b/Lab5/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..0dd0e2f43 Binary files /dev/null and b/Lab5/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab5/bootloader.img b/Lab5/bootloader.img new file mode 100755 index 000000000..3bca60bbe Binary files /dev/null and b/Lab5/bootloader.img differ diff --git a/Lab5/bootloader.py b/Lab5/bootloader.py new file mode 100644 index 000000000..1b99fa7d7 --- /dev/null +++ b/Lab5/bootloader.py @@ -0,0 +1,25 @@ +import os +import time + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +# with open('/dev/pts/35', "wb", buffering=0) as tty: +with open('/dev/ttyUSB0', "wb", buffering=0) as tty: + # send Start + tty.write(b"Start") + time.sleep(1) + + # send kernel size + tty.write(file_size.to_bytes(4, 'little')) + time.sleep(1) + + # send kernel + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + tty.write(data) + time.sleep(1) + diff --git a/Lab5/include/device_tree.h b/Lab5/include/device_tree.h new file mode 100644 index 000000000..e3e6aee78 --- /dev/null +++ b/Lab5/include/device_tree.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_TREE_H +#define _DEVICE_TREE_H + +typedef int (*fdt_callback)(void *); +int initramfs_callback(void *dtb); +int dtb_parser(void *dtb); +void fdt_traverse(fdt_callback cb, char *dtb); + +#endif /*_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab5/include/dynamic_alloc.h b/Lab5/include/dynamic_alloc.h new file mode 100644 index 000000000..55eb68307 --- /dev/null +++ b/Lab5/include/dynamic_alloc.h @@ -0,0 +1,35 @@ +#ifndef _DYNAMIC_ALLOC_H +#define _DYNAMIC_ALLOC_H + +#include "list.h" + +#define MAX_POOL_SIZE 256 +#define MIN_CHUNK_SIZE 8 +#define FREE 98 + +typedef struct _chunk +{ + int index; // const + int size; + void *addr; // const + int val; + int belong_page; + struct list_head list; +} chunk; + +typedef struct _pool_list +{ + struct list_head list; +} pool_list; + +void init_pool(); +void *get_chunk(int req_size); +int roundup_size(int size); +void split_page(int frame_index, int req_pool_index); +int remove_a_chunk_from_pool(int req_pool_index); +int free_chunk(int index); +void put_back_to_pool(int pool_index, int chunk_index); +void debug_pool(); +void free(void *addr); + +#endif /*_DYNAMIC_ALLOC_H */ diff --git a/Lab5/include/list.h b/Lab5/include/list.h new file mode 100644 index 000000000..a0fb29587 --- /dev/null +++ b/Lab5/include/list.h @@ -0,0 +1,441 @@ +/* Linux-like double-linked list implementation */ + +#ifndef SYSPROG21_LIST_H +#define SYSPROG21_LIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *)0)->member) *__pmember = (ptr); \ + (type *)((char *)__pmember - offsetof(type, member)); \ + }) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr)-offsetof(type, member))) +#endif +#endif + + /** + * struct list_head - Head and node of a double-linked list + * @prev: pointer to the previous node in the list + * @next: pointer to the next node in the list + * + * The simple double-linked list consists of a head and nodes attached to + * this head. Both node and head share the same struct type. The list_* + * functions and macros can be used to access and modify this data structure. + * + * The @prev pointer of the list head points to the last list node of the + * list and @next points to the first list node of the list. For an empty list, + * both member variables point to the head. + * + * The list nodes are usually embedded in a container structure which holds the + * actual data. Such an container object is called entry. The helper list_entry + * can be used to calculate the object address from the address of the node. + */ + struct list_head + { + struct list_head *prev; + struct list_head *next; + }; + +/** + * LIST_HEAD - Declare list head and initialize it + * @head: name of the new object + */ +#define LIST_HEAD(head) struct list_head head = {&(head), &(head)} + + /** + * INIT_LIST_HEAD() - Initialize empty list head + * @head: pointer to list head + * + * This can also be used to initialize a unlinked list node. + * + * A node is usually linked inside a list, will be added to a list in + * the near future or the entry containing the node will be free'd soon. + * + * But an unlinked node may be given to a function which uses list_del(_init) + * before it ends up in a previously mentioned state. The list_del(_init) on an + * initialized node is well defined and safe. But the result of a + * list_del(_init) on an uninitialized node is undefined (unrelated memory is + * modified, crashes, ...). + */ + static inline void INIT_LIST_HEAD(struct list_head *head) + { + head->next = head; + head->prev = head; + } + + /** + * list_add() - Add a list node to the beginning of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add(struct list_head *node, struct list_head *head) + { + struct list_head *next = head->next; + + next->prev = node; + node->next = next; + node->prev = head; + head->next = node; + } + + /** + * list_add_tail() - Add a list node to the end of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add_tail(struct list_head *node, struct list_head *head) + { + struct list_head *prev = head->prev; + + prev->next = node; + node->next = head; + node->prev = prev; + head->prev = node; + } + + /** + * list_del() - Remove a list node from the list + * @node: pointer to the node + * + * The node is only removed from the list. Neither the memory of the removed + * node nor the memory of the entry containing the node is free'd. The node + * has to be handled like an uninitialized node. Accessing the next or prev + * pointer of the node is not safe. + * + * Unlinked, initialized nodes are also uninitialized after list_del. + * + * LIST_POISONING can be enabled during build-time to provoke an invalid memory + * access when the memory behind the next/prev pointer is used after a list_del. + * This only works on systems which prohibit access to the predefined memory + * addresses. + */ + static inline void list_del(struct list_head *node) + { + struct list_head *next = node->next; + struct list_head *prev = node->prev; + + next->prev = prev; + prev->next = next; + +#ifdef LIST_POISONING + node->prev = (struct list_head *)(0x00100100); + node->next = (struct list_head *)(0x00200200); +#endif + } + + /** + * list_del_init() - Remove a list node from the list and reinitialize it + * @node: pointer to the node + * + * The removed node will not end up in an uninitialized state like when using + * list_del. Instead the node is initialized again to the unlinked state. + */ + static inline void list_del_init(struct list_head *node) + { + list_del(node); + INIT_LIST_HEAD(node); + } + + /** + * list_empty() - Check if list head has no nodes attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not empty !0 - list is empty + */ + static inline int list_empty(const struct list_head *head) + { + return (head->next == head); + } + + /** + * list_is_singular() - Check if list head has exactly one node attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not singular !0 -list has exactly one entry + */ + static inline int list_is_singular(const struct list_head *head) + { + return (!list_empty(head) && head->prev == head->next); + } + + /** + * list_splice() - Add list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice(struct list_head *list, struct list_head *head) + { + struct list_head *head_first = head->next; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->next = list_first; + list_first->prev = head; + + list_last->next = head_first; + head_first->prev = list_last; + } + + /** + * list_splice_tail() - Add list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice_tail(struct list_head *list, + struct list_head *head) + { + struct list_head *head_last = head->prev; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->prev = list_last; + list_last->next = head; + + list_first->prev = head_last; + head_last->next = list_first; + } + + /** + * list_splice_init() - Move list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_init(struct list_head *list, + struct list_head *head) + { + list_splice(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_splice_tail_init() - Move list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) + { + list_splice_tail(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_cut_position() - Move beginning of a list to another list + * @head_to: pointer to the head of the list which receives nodes + * @head_from: pointer to the head of the list + * @node: pointer to the node in which defines the cutting point + * + * All entries from the beginning of the list @head_from to (including) the + * @node is moved to @head_to. + * + * @head_to is replaced when @head_from is not empty. @node must be a real + * list node from @head_from or the behavior is undefined. + */ + static inline void list_cut_position(struct list_head *head_to, + struct list_head *head_from, + struct list_head *node) + { + struct list_head *head_from_first = head_from->next; + + if (list_empty(head_from)) + return; + + if (head_from == node) + { + INIT_LIST_HEAD(head_to); + return; + } + + head_from->next = node->next; + head_from->next->prev = head_from; + + head_to->prev = node; + node->next = head_to; + head_to->next = head_from_first; + head_to->next->prev = head_to; + } + + /** + * list_move() - Move a list node to the beginning of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the beginning of + * @head + */ + static inline void list_move(struct list_head *node, struct list_head *head) + { + list_del(node); + list_add(node, head); + } + + /** + * list_move_tail() - Move a list node to the end of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the end of @head + */ + static inline void list_move_tail(struct list_head *node, + struct list_head *head) + { + list_del(node); + list_add_tail(node, head); + } + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_last_entry() - get last entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of last entry in list + */ +#define list_last_entry(head, type, member) \ + list_entry((head)->prev, type, member) + +/** + * list_for_each - iterate over list nodes + * @node: list_head pointer used as iterator + * @head: pointer to the head of the list + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + */ +#define list_for_each(node, head) \ + for (node = (head)->next; node != (head); node = node->next) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif + +/** + * list_for_each_safe - iterate over list nodes and allow deletes + * @node: list_head pointer used as iterator + * @safe: list_head pointer used to store info for next entry in list + * @head: pointer to the head of the list + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + */ +#define list_for_each_safe(node, safe, head) \ + for (node = (head)->next, safe = node->next; node != (head); \ + node = safe, safe = node->next) + +/** + * list_for_each_entry_safe - iterate over list entries and allow deletes + * @entry: pointer used as iterator + * @safe: @type pointer used to store info for next entry in list + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#define list_for_each_entry_safe(entry, safe, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member), \ + safe = list_entry(entry->member.next, __typeof__(*entry), member); \ + &entry->member != (head); entry = safe, \ + safe = list_entry(safe->member.next, __typeof__(*entry), member)) + +#undef __LIST_HAVE_TYPEOF + +#ifdef __cplusplus +} +#endif + +#endif /* SYSPROG21_LIST_H */ \ No newline at end of file diff --git a/Lab5/include/load_kernel.h b/Lab5/include/load_kernel.h new file mode 100644 index 000000000..a5dc68ffb --- /dev/null +++ b/Lab5/include/load_kernel.h @@ -0,0 +1,7 @@ +#ifndef _LOAD_KERNEL_H +#define _LOAD_KERNEL_H + +void load_kernel(char *dest); +void relocate(char *from_dest, char *to_dest); + +#endif /*_LOAD_KERNEL_H */ diff --git a/Lab5/include/math.h b/Lab5/include/math.h new file mode 100644 index 000000000..3107a97d8 --- /dev/null +++ b/Lab5/include/math.h @@ -0,0 +1 @@ +int pow(int base, int exp); \ No newline at end of file diff --git a/Lab5/include/mbox.h b/Lab5/include/mbox.h new file mode 100644 index 000000000..d43fd936b --- /dev/null +++ b/Lab5/include/mbox.h @@ -0,0 +1,6 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); + +#endif /*_MBOX_H */ diff --git a/Lab5/include/mbox_call.h b/Lab5/include/mbox_call.h new file mode 100644 index 000000000..7f60645d0 --- /dev/null +++ b/Lab5/include/mbox_call.h @@ -0,0 +1,9 @@ +#ifndef _MBOX_CALL_H +#define _MBOX_CALL_H + +/* a properly aligned buffer */ +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch); + +#endif /*_MBOX_CALL_H */ diff --git a/Lab5/include/mini_uart.h b/Lab5/include/mini_uart.h new file mode 100644 index 000000000..51eb0e34c --- /dev/null +++ b/Lab5/include/mini_uart.h @@ -0,0 +1,20 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void uart_send_string_of_size(char *str, int size); +void uart_hex(unsigned int d); +void uart_send_space(int size); + +void asyn_read(); +void asyn_write(); +void uart_rx_handler(); +void uart_tx_handler(); + +void enable_uart_irq(); +void disable_uart_irq(); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab5/include/mm.h b/Lab5/include/mm.h new file mode 100644 index 000000000..1d947fb75 --- /dev/null +++ b/Lab5/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (6 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/Lab5/include/my_signal.h b/Lab5/include/my_signal.h new file mode 100644 index 000000000..d3f998486 --- /dev/null +++ b/Lab5/include/my_signal.h @@ -0,0 +1,30 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include "list.h" +#include "thread.h" + +#define SIGKILL 9 +#define SIG_NUM (sizeof(signal_table) / sizeof(signal_table[0])) +typedef void (*signal_handler)(int); + +typedef struct _custom_signal +{ + unsigned int sig_num; + signal_handler handler; + struct list_head list; +} custom_signal; + +typedef struct _signal_context +{ + struct _trapframe *trapframe; + char *user_stack; +} signal_context; + +void sig_ignore(int _); +void sigkill_handler(int pid); +void sig_context_update(struct _trapframe *trapframe, void (*handler)()); +void sig_return(void); +void sig_context_restore(struct _trapframe *trapframe); + +#endif /*_SIGNAL_H */ diff --git a/Lab5/include/page_alloc.h b/Lab5/include/page_alloc.h new file mode 100644 index 000000000..0a191e1fe --- /dev/null +++ b/Lab5/include/page_alloc.h @@ -0,0 +1,39 @@ +#ifndef _PAGE_ALLOC_H +#define _PAGE_ALLOC_H + +// #define DEBUG + +#define MAX_ORDER 11 // order 0 ~ 11 +#define ALLOCATED -1 +#define FREE_BUDDY 99 +#define MIN_PAGE_SIZE 0x1000 +// #define FREE_MEM_START 0x10000000 +// #define FREE_MEM_END 0x20000000 +#define FREE_MEM_START 0x00 +#define FREE_MEM_END 0x3C000000 +#define TOTAL_NUM_PAGE (FREE_MEM_END - FREE_MEM_START) / MIN_PAGE_SIZE + +typedef struct _page_frame_node page_frame_node; +struct _page_frame_node +{ + int index; // const + int val; + void *addr; // const + int contiguous_head; // if allocated, this value keep track who (page) is the start of the contiguous memory in index + int allocated_order; + int chunk_order; // -1 if no chunk, otherwise 1, 2, 4, 6, 8, 16, 32 + page_frame_node *next; + page_frame_node *previous; +}; + +void init_page_frame(); +void *my_malloc(int req_size); +int get_page_from_free_list(int req_size, int who); +void add_to_free_list(page_frame_node *head_node, int index); +void remove_from_free_list(page_frame_node *free_list_node); +void put_back_to_free_list(int num_of_redundant_page, int index); +int free_page_frame(int index); +int merge_buddy(int *index, int buddy, int order); +void debug(); + +#endif /*_PAGE_ALLOC_H */ diff --git a/Lab5/include/peripherals/base.h b/Lab5/include/peripherals/base.h new file mode 100644 index 000000000..63f9c038f --- /dev/null +++ b/Lab5/include/peripherals/base.h @@ -0,0 +1,6 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab5/include/peripherals/device_tree.h b/Lab5/include/peripherals/device_tree.h new file mode 100644 index 000000000..d5d3bc729 --- /dev/null +++ b/Lab5/include/peripherals/device_tree.h @@ -0,0 +1,16 @@ +#ifndef _P_DEVICE_TREE_H +#define _P_DEVICE_TREE_H + +#define FDT_MAGIC 0xD00DFEED +#define FDT_VERSION 0x00000011 +#define FDT_TK_NULL 0X00000000 + +#define FDT_BEGIN_NODE 0X00000001 +#define FDT_END_NODE 0X00000002 +#define FDT_PROP 0X00000003 +#define FDT_NOP 0X00000004 +#define FDT_END 0X00000009 + +#define FDT_CPIO_INITRAMFS_PROPNAME "linux,initrd-start" + +#endif /*_P_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab5/include/peripherals/gpio.h b/Lab5/include/peripherals/gpio.h new file mode 100644 index 000000000..a7a6a5b4c --- /dev/null +++ b/Lab5/include/peripherals/gpio.h @@ -0,0 +1,25 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL0 (PBASE + 0x00200000) +#define GPFSEL1 (PBASE + 0x00200004) +#define GPFSEL2 (PBASE + 0x00200008) +#define GPFSEL3 (PBASE + 0x0020000C) +#define GPFSEL4 (PBASE + 0x00200010) +#define GPFSEL5 (PBASE + 0x00200014) +#define GPSET0 (PBASE + 0x0020001C) +#define GPSET1 (PBASE + 0x00200020) +#define GPCLR0 (PBASE + 0x00200028) +#define GPLEV0 (PBASE + 0x00200034) +#define GPLEV1 (PBASE + 0x00200038) +#define GPEDS0 (PBASE + 0x00200040) +#define GPEDS1 (PBASE + 0x00200044) +#define GPHEN0 (PBASE + 0x00200064) +#define GPHEN1 (PBASE + 0x00200068) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) +#define GPPUDCLK1 (PBASE + 0x0020009C) + +#endif /*_P_GPIO_H */ diff --git a/Lab5/include/peripherals/irq.h b/Lab5/include/peripherals/irq.h new file mode 100644 index 000000000..51e0cc5ea --- /dev/null +++ b/Lab5/include/peripherals/irq.h @@ -0,0 +1,27 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + +#define IRQ_BASIC_PENDING (PBASE + 0x0000B200) +#define IRQ_PENDING_1 (PBASE + 0x0000B204) +#define IRQ_PENDING_2 (PBASE + 0x0000B208) +#define FIQ_CONTROL (PBASE + 0x0000B20C) +#define ENABLE_IRQS_1 (PBASE + 0x0000B210) +#define ENABLE_IRQS_2 (PBASE + 0x0000B214) +#define ENABLE_BASIC_IRQS (PBASE + 0x0000B218) +#define DISABLE_IRQS_1 (PBASE + 0x0000B21C) +#define DISABLE_IRQS_2 (PBASE + 0x0000B220) +#define DISABLE_BASIC_IRQS (PBASE + 0x0000B224) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#define CORE0_INTR_SRC 0x40000060 +#define CORE1_INTR_SRC 0x40000064 +#define CORE2_INTR_SRC 0x40000068 +#define CORE3_INTR_SRC 0x4000006C + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/Lab5/include/peripherals/mbox_call.h b/Lab5/include/peripherals/mbox_call.h new file mode 100644 index 000000000..cfcc0d3ad --- /dev/null +++ b/Lab5/include/peripherals/mbox_call.h @@ -0,0 +1,40 @@ +#ifndef _P_MBOX_CALL_H +#define _P_MBOX_CALL_H + +#include "peripherals/base.h" + +#define VIDEOCORE_MBOX (PBASE + 0x0000B880) +#define MBOX_READ (VIDEOCORE_MBOX + 0x0) +#define MBOX_POLL (VIDEOCORE_MBOX + 0x10) +#define MBOX_SENDER (VIDEOCORE_MBOX + 0x14) +#define MBOX_STATUS (VIDEOCORE_MBOX + 0x18) +#define MBOX_CONFIG (VIDEOCORE_MBOX + 0x1C) +#define MBOX_WRITE (VIDEOCORE_MBOX + 0x20) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_MODEL 0x10001 +#define MBOX_TAG_REVISION 0x10002 +#define MBOX_TAG_MAC_ADDRESS 0x10003 +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_ARM_MEMORY 0x10005 +#define MBOX_TAG_VC_MEMORY 0x10006 +#define MBOX_TAG_CLOCKS 0x10007 +#define MBOX_TAG_LAST 0 + +#endif /* _P_MBOX_CALL_H */ diff --git a/Lab5/include/peripherals/mini_uart.h b/Lab5/include/peripherals/mini_uart.h new file mode 100644 index 000000000..71119b511 --- /dev/null +++ b/Lab5/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/Lab5/include/peripherals/reboot.h b/Lab5/include/peripherals/reboot.h new file mode 100644 index 000000000..f1d41ad2e --- /dev/null +++ b/Lab5/include/peripherals/reboot.h @@ -0,0 +1,8 @@ +#ifndef _P_REBOOT_H +#define _P_REBOOT_H + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +#endif /*_P_REBOOT_H */ \ No newline at end of file diff --git a/Lab5/include/printf.h b/Lab5/include/printf.h new file mode 100644 index 000000000..a9501cba0 --- /dev/null +++ b/Lab5/include/printf.h @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ + void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ + int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ + int sprintf_(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + int snprintf_(char *buffer, size_t count, const char *format, ...); + int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ + int vprintf_(const char *format, va_list va); + + /** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ + int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ \ No newline at end of file diff --git a/Lab5/include/read_cpio.h b/Lab5/include/read_cpio.h new file mode 100644 index 000000000..e843d9a0a --- /dev/null +++ b/Lab5/include/read_cpio.h @@ -0,0 +1,9 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); +char *find_content_addr(char *cpioDest, const char *filename); +int load_userprogram(const char *, char *); + +#endif /*_READ_CPIO_H */ \ No newline at end of file diff --git a/Lab5/include/reboot.h b/Lab5/include/reboot.h new file mode 100644 index 000000000..e9fb1d66c --- /dev/null +++ b/Lab5/include/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif /*_REBOOT_H */ \ No newline at end of file diff --git a/Lab5/include/reserve_mem.h b/Lab5/include/reserve_mem.h new file mode 100644 index 000000000..3922438c7 --- /dev/null +++ b/Lab5/include/reserve_mem.h @@ -0,0 +1,18 @@ +#ifndef _RESERVE_MEM_H +#define _RESERVE_MEM_H + +#define USRPGM_BASE 0x15000000 +#define USRPGM_SIZE 0x100000 + +typedef struct _reserved_memory_block +{ + unsigned long start; + unsigned long end; + char name[30]; +} reserved_memory_block; + +void memory_reserve(unsigned long start, unsigned long end, char *name); +int check_contain_RM(unsigned long start, unsigned long end); +void memory_init(); + +#endif /*_RESERVE_MEM_H */ \ No newline at end of file diff --git a/Lab5/include/shell.h b/Lab5/include/shell.h new file mode 100644 index 000000000..f677e039b --- /dev/null +++ b/Lab5/include/shell.h @@ -0,0 +1,7 @@ +#ifndef _SHELL_H +#define _SHELL_H + +void shell_main(char *); +void shell_start(); + +#endif /*_SHELL_H */ diff --git a/Lab5/include/stdlib.h b/Lab5/include/stdlib.h new file mode 100644 index 000000000..31b8255dc --- /dev/null +++ b/Lab5/include/stdlib.h @@ -0,0 +1,28 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" +#include "utils.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const char *str); +char *strcpy(char *destination, const char *source); +int atoi(char *str); + +void *memset(void *dest, register int val, int len); +int memcmp(void *s1, void *s2, int n); +void *memcpy(void *dest, const void *src, size_t len); +int hex2int(char *s, int n); + +void *simple_malloc(unsigned int size); + +// void printf(char *fmt, ...); +// unsigned int sprintf(char *dst, char *fmt, ...); +// unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +#endif /*_STDLIB_H */ diff --git a/Lab5/include/syscall.h b/Lab5/include/syscall.h new file mode 100644 index 000000000..aca95eae1 --- /dev/null +++ b/Lab5/include/syscall.h @@ -0,0 +1,16 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + +#include "stdlib.h" + +int getpid(); +size_t uart_read(char buf[], size_t size); +size_t uart_write(const char buf[], size_t size); +int exec(const char *name, char *const argv[]); +int fork(); +void exit(int status); +int mbox_call_u(unsigned char ch, unsigned int *mbox); +void kill(int pid); +void signal(int SIGNAL, void (*handler)()); + +#endif /*_SYSCALL_H */ \ No newline at end of file diff --git a/Lab5/include/test.h b/Lab5/include/test.h new file mode 100644 index 000000000..994fcc065 --- /dev/null +++ b/Lab5/include/test.h @@ -0,0 +1,8 @@ +#ifndef _TEST_H +#define _TEST_H + +void test_mem_alloc(); +void test_thread(); +void load_usrpgm_in_umode(); + +#endif /*_TEST_H */ diff --git a/Lab5/include/thread.h b/Lab5/include/thread.h new file mode 100644 index 000000000..a987a47be --- /dev/null +++ b/Lab5/include/thread.h @@ -0,0 +1,62 @@ +#ifndef _THREAD_H +#define _THREAD_H + +#include "list.h" +#include "my_signal.h" + +#define READY 1 +#define ZOMBIE 2 +#define FORKING 4 + +typedef void (*func_ptr)(); + +typedef struct _context +{ + unsigned long x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, fp, lr, sp; +} context; + +typedef struct _thread_info +{ + long id; + long child_id; + struct _task_struct *task; + +} thread_info; + +typedef struct _trapframe +{ + unsigned long x[31]; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trapframe; + +typedef struct _task_struct +{ + struct _context task_context; // context need to be the first one + struct _thread_info *thread_info; + struct list_head list; + func_ptr job; + unsigned long kstack_start; // kernel stack base + unsigned long ustack_start; // user stack base + unsigned long usrpgm_load_addr; // user program load address + unsigned long status; + unsigned long trapframe; // using "unsigned long" to keep trapframe address, instead of claiming a "trapframe_t*" to avoid "Data Abort" + struct _custom_signal *custom_signal; + struct _signal_context *signal_context; +} task_struct; + +void schedule(); +void add_rq(task_struct *task); +task_struct *del_rq(); +void thread_init(); +thread_info *thread_create(func_ptr fp); +void task_wrapper(); +void idle_task(); +void kill_zombies(); +void do_fork(); +void create_child(task_struct *parent); +void debug_task_rq(); +void debug_task_zombieq(); + +#endif /*_THREAD_H */ \ No newline at end of file diff --git a/Lab5/include/timer.h b/Lab5/include/timer.h new file mode 100644 index 000000000..2e352e7fe --- /dev/null +++ b/Lab5/include/timer.h @@ -0,0 +1,14 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#define MESSAGE_BUFFER 50 +#define SECONDS_BUFFER 20 + +void get_current_time(); +void el0_timer_handler(long cntpct_el0, long cntfrq_el0); +void el1_timer_handler(long cntpct_el0, long cntfrq_el0); +void add_timer(int sec, char *mes); +int is_timer_queue_empty(); +void timer_delay(long seconds); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab5/include/utils.h b/Lab5/include/utils.h new file mode 100644 index 000000000..a23ae37a7 --- /dev/null +++ b/Lab5/include/utils.h @@ -0,0 +1,8 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay ( unsigned long); +extern void put32 ( unsigned long, unsigned int ); +extern unsigned int get32 ( unsigned long ); + +#endif /*_BOOT_H */ diff --git a/Lab5/include/virtual_mem.h b/Lab5/include/virtual_mem.h new file mode 100644 index 000000000..d612b5079 --- /dev/null +++ b/Lab5/include/virtual_mem.h @@ -0,0 +1,6 @@ +#ifndef _VIRTUAL_MEM_H +#define _VIRTUAL_MEM_H + +void virtual_mem_init(); + +#endif /*_VIRTUAL_MEM_H */ \ No newline at end of file diff --git a/Lab5/initramfs.cpio b/Lab5/initramfs.cpio new file mode 100644 index 000000000..0676fb158 Binary files /dev/null and b/Lab5/initramfs.cpio differ diff --git a/Lab5/kernel8.img b/Lab5/kernel8.img new file mode 100755 index 000000000..f449743de Binary files /dev/null and b/Lab5/kernel8.img differ diff --git a/Lab5/rootfs/a.c b/Lab5/rootfs/a.c new file mode 100644 index 000000000..d2398d75b --- /dev/null +++ b/Lab5/rootfs/a.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + printf("HAHA return 1\n"); + + return 1; +} diff --git a/Lab5/rootfs/cat.txt b/Lab5/rootfs/cat.txt new file mode 100644 index 000000000..949e8345b --- /dev/null +++ b/Lab5/rootfs/cat.txt @@ -0,0 +1 @@ +No cat here. \ No newline at end of file diff --git a/Lab5/rootfs/one b/Lab5/rootfs/one new file mode 100644 index 000000000..50ecbb596 --- /dev/null +++ b/Lab5/rootfs/one @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 diff --git a/Lab5/rootfs/ts.txt b/Lab5/rootfs/ts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Lab5/rootfs/userprogram.img b/Lab5/rootfs/userprogram.img new file mode 100755 index 000000000..2c228ba23 Binary files /dev/null and b/Lab5/rootfs/userprogram.img differ diff --git a/Lab5/send_kernel.py b/Lab5/send_kernel.py new file mode 100644 index 000000000..1b17155f2 --- /dev/null +++ b/Lab5/send_kernel.py @@ -0,0 +1,40 @@ +# This python script is for macOS +from serial import Serial +import os +import time +import fcntl +import threading + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +with Serial('/dev/tty.usbserial-0001', 115200) as ser: + sem = threading.Semaphore() + # set tty to non-blocking mode + fcntl.fcntl(ser, fcntl.F_SETFL, os.O_NONBLOCK) + + input("Press anything to start transmission\n") + + print("Sending Strat...") + sem.acquire() + ser.write(b"Start") + sem.release() + time.sleep(1) + + sem.acquire() + ser.write(file_size.to_bytes(4, 'little')) + sem.release() + time.sleep(1) + + print("Start sending kernel img by uart...") + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + sem.acquire() + ser.write(data) + sem.release() + time.sleep(1) + + print("Transfer finished!") diff --git a/Lab5/src/bootloader/boot.S b/Lab5/src/bootloader/boot.S new file mode 100644 index 000000000..403960d2e --- /dev/null +++ b/Lab5/src/bootloader/boot.S @@ -0,0 +1,25 @@ +#include "mm.h" + +.section ".text.boot" + +.globl _start +_start: + mov x24, x0 + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + mov x0, x24 + bl bootloader_main + b proc_hang // should never come here diff --git a/Lab5/src/bootloader/bootloader.c b/Lab5/src/bootloader/bootloader.c new file mode 100644 index 000000000..327413557 --- /dev/null +++ b/Lab5/src/bootloader/bootloader.c @@ -0,0 +1,15 @@ +#include "mini_uart.h" +#include "load_kernel.h" + +void bootloader_main(void) +{ + uart_init(); + + uart_send_string("Relocatting ...\n"); + char *from_dest = (char *)0x80000; + char *to_dest = (char *)0x60000; + relocate((char *)from_dest, (char *)to_dest); + + char *dest = (char *)0x80000; + load_kernel((char *)dest); +} diff --git a/Lab5/src/bootloader/config.txt b/Lab5/src/bootloader/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/Lab5/src/bootloader/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/Lab5/src/bootloader/link.ld b/Lab5/src/bootloader/link.ld new file mode 100644 index 000000000..1fca3dfb7 --- /dev/null +++ b/Lab5/src/bootloader/link.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0x80000; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; +__loader_size = (_end - _start); diff --git a/Lab5/src/bootloader/load_kernel.c b/Lab5/src/bootloader/load_kernel.c new file mode 100644 index 000000000..00808de84 --- /dev/null +++ b/Lab5/src/bootloader/load_kernel.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include "mini_uart.h" +#include "peripherals/mini_uart.h" +#include "stdlib.h" + +extern int __loader_size; +extern void *_dtb_ptr; + +void load_kernel(char *dest) +{ + int size = 0; + + uart_send_string("Waiting for kernel8.img ...\n"); + + char start[5] = "Start"; + + for (int i = 0; i < 5; i++) + { + if (uart_recv() != start[i]) + i = 0; + } + + uart_send_string("Start transmitting ...\n"); + + size = uart_recv(); + size |= uart_recv() << 8; + size |= uart_recv() << 16; + size |= uart_recv() << 24; + + printf("Size of kernel is = %d bytes\n", size); + + // read the kernel + while (size--) + { + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + *dest++ = get32(AUX_MU_IO_REG); + } + + uart_send_string("End transmitting ...\n"); + + // restore arguments and jump to the new kernel. + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + // we must force an absolute address to branch to + "mov x30, 0x80000; ret"); +} + +void relocate(char *from_dest, char *to_dest) +{ + long long size = (long long)&__loader_size; + + while (size--) + { + *to_dest++ = *from_dest++; + } + + char *redicrect = __builtin_return_address(0) + (to_dest - from_dest); + + goto *redicrect; +} \ No newline at end of file diff --git a/Lab5/src/kernel/boot.S b/Lab5/src/kernel/boot.S new file mode 100644 index 000000000..81fb65548 --- /dev/null +++ b/Lab5/src/kernel/boot.S @@ -0,0 +1,88 @@ +#include "mm.h" + +.global _dtb_ptr +.section .data +_dtb_ptr: .dc.a 0x0 + +.section ".text.boot" + +.globl _start +_start: + cbz x24, x24c // Check if bootloader, see bootloader's boot.S for more info +x24nc: + ldr x21, =_dtb_ptr + str x24, [x21] + b go +x24c: + ldr x21, =_dtb_ptr + str x0, [x21] +go: + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + + ldr x1, =_start + + // get CurrentEL + mrs x0, CurrentEL + and x0, x0, #12 // clear reserved bits + + // running at EL3? branch if in EL3 + cmp x0, #12 + beq from_el3_to_el2 + + // running at EL1? branch if in EL1 + cmp x0, #4 + beq bss + + bl from_el2_to_el1 + +bss: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + bl set_el1_exception_vector_table // set el1 exception vector table base + + mov sp, #LOW_MEMORY // 4MB + // ldr x1, =_start + // mov sp, x1 + bl kernel_main + b proc_hang // should never come here + +from_el3_to_el2: + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, from_el2_to_el1 + msr elr_el3, x2 + eret + +from_el2_to_el1: + msr sp_el1, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // change execution level to EL1 + mov x2, #0x3c5 + msr spsr_el2, x2 + msr elr_el2, lr + eret \ No newline at end of file diff --git a/Lab5/src/kernel/config.txt b/Lab5/src/kernel/config.txt new file mode 100644 index 000000000..279349696 --- /dev/null +++ b/Lab5/src/kernel/config.txt @@ -0,0 +1,2 @@ +kernel_old=1 +disable_commandline_tags=1 diff --git a/Lab5/src/kernel/device_tree.c b/Lab5/src/kernel/device_tree.c new file mode 100644 index 000000000..d1b67143f --- /dev/null +++ b/Lab5/src/kernel/device_tree.c @@ -0,0 +1,266 @@ +#include +#include "peripherals/device_tree.h" +#include "stdlib.h" +#include "mini_uart.h" +#include "device_tree.h" + +typedef struct fdt_header +{ + uint32_t magic; // big-endian + 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; +} fdt_header; + +typedef struct +{ + uint32_t len; + uint32_t nameoff; +} fdt_prop; + +char *cpioDestGlobal; + +static uint64_t pad_to_4(void *num); +static uint32_t rev32(uint32_t val); +static uint64_t endian_rev(void *input, int dtype_size); + +int initramfs_callback(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + uint8_t *p = off_dt_struct; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + if (!strcmp(FDT_CPIO_INITRAMFS_PROPNAME, prop_name)) + { + uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); + printf("initramfs_addr at %p\n", addr); + cpioDestGlobal = (char *)addr; + } + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +int dtb_parser(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + int depth = 0; + uint8_t *p = off_dt_struct; + char *node_name = NULL; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + node_name = (char *)p; + if (depth == 0) + printf("\\ {\n"); + else + { + uart_send_space(depth * 3); + printf("%s {\n", node_name); + } + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + depth++; + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + depth--; + uart_send_space(depth * 3); + printf("};\n"); + printf("\n"); + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + + if (!strcmp(prop_name, "#address-cells") || !strcmp(prop_name, "#size-cells") || !strcmp(prop_name, "interrupt-parent")) + { + // + uart_send_space(depth * 3); + printf("%s = <%d>;\n", prop_name, rev32(*((uint32_t *)prop_val))); + } + else if (!strcmp(prop_name, "model") || !strcmp(prop_name, "status") || !strcmp(prop_name, "name") || !strcmp(prop_name, "device_type") || + !strcmp(prop_name, "chassis-type") || !strcmp(prop_name, "bootargs") || !strcmp(prop_name, "stdout-path") || !strcmp(prop_name, "stdin-path") || + !strcmp(prop_name, "power-isa-version") || !strcmp(prop_name, "mmu-type") || !strcmp(prop_name, "label") || !strcmp(prop_name, "phy-connection-type")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else if (!strcmp(prop_name, "compatible")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else + { + uart_send_space(depth * 3); + printf("%s = %s;\n", prop_name, prop_val); + } + + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +void fdt_traverse(fdt_callback cb, char *dtb) +{ + if (cb(dtb) == -1) + printf("fdt_traverse failed.\n"); + + return; +} + +static uint64_t pad_to_4(void *num) +{ + uint64_t modded = ((uint64_t)num) % 4; + return modded == 0 ? 0 : 4 - modded; +} + +static uint32_t rev32(uint32_t val) +{ + return (uint32_t)endian_rev(&val, 4); +} + +/** Transform data from litle to big endian, or from big to little endian + * @param input: Pointer to a value that needs to be transformed + * @param dtype_size: data type size, size of each item in bytes. Possible value: 1, 2, 4, 8 + */ +static uint64_t endian_rev(void *input, int dtype_size) +{ + const uint8_t *ptr = (uint8_t *)input; + uint64_t ret = 0; + + switch (dtype_size) + { + // int8_t, uint8_t + case 1: + // No need to transform to big endian since the data type size is 1 byte + break; + + // int16_t, uint16_t + case 2: + ret = (ptr[0] << 8) | ptr[1]; + break; + + // int32_t, uint32_t + case 4: + ret = (ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + ptr[3]; + break; + + // int64_t, uint64_t + // case 8: + // ret = (ptr[0] << 56) | + // (ptr[1] << 48) | + // (ptr[2] << 40) | + // (ptr[3] << 32) | + // (ptr[4] << 24) | + // (ptr[5] << 16) | + // (ptr[6] << 8) | + // ptr[7]; + // break; + + default: + printf("[Error] Endian transformation(%d) not implemented. @line %d, file:%s\r\n", dtype_size, __LINE__, __FILE__); + break; + } + return ret; +} \ No newline at end of file diff --git a/Lab5/src/kernel/dynamic_alloc.c b/Lab5/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..a16a426b0 --- /dev/null +++ b/Lab5/src/kernel/dynamic_alloc.c @@ -0,0 +1,249 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "list.h" + +extern page_frame_node frame_array[TOTAL_NUM_PAGE]; + +pool_list pool[33]; // 1, 2, 4, 6, 8, 16, 32 +chunk chunk_array[3000]; +int global_chunk_index = -1; + +void init_pool() +{ + for (int i = 0; i < 33; i++) + INIT_LIST_HEAD(&pool[i].list); + return; +} + +chunk *new_chunk() +{ + global_chunk_index++; + chunk_array[global_chunk_index].index = global_chunk_index; + return &chunk_array[global_chunk_index]; +} + +void *get_chunk(int req_size) +{ + int req_pool_index = roundup_size(req_size); // req_pool_index * MIN_CHUNK_SIZE = req_size + + if (list_empty(&pool[req_pool_index].list)) + { + // empty pool on req_size + int frame_index = get_page_from_free_list(MIN_PAGE_SIZE, req_pool_index); + split_page(frame_index, req_pool_index); + } + + int index = remove_a_chunk_from_pool(req_pool_index); + if (index == -1) + return NULL; + + void *addr = chunk_array[index].addr; + return addr; +} + +void split_page(int frame_index, int req_pool_index) +{ + int split_size = (req_pool_index * MIN_CHUNK_SIZE); + for (int i = 0; i < (MIN_PAGE_SIZE / split_size); i++) + { + chunk *new = new_chunk(); + new->size = split_size; + new->addr = (void *)frame_array[frame_index].addr + i *split_size; + new->val = FREE; + new->belong_page = frame_index; + list_add_tail(&new->list, &pool[req_pool_index].list); + } +} + +int remove_a_chunk_from_pool(int req_pool_index) +{ + chunk *alloc_chunk = container_of(pool[req_pool_index].list.next, chunk, list); + list_del_init(pool[req_pool_index].list.next); + alloc_chunk->val = ALLOCATED; + return alloc_chunk->index; +} + +void put_back_to_pool(int pool_index, int chunk_index) +{ + // addr to insert + struct list_head *node = &chunk_array[chunk_index].list; + unsigned long addr_long = (unsigned long)chunk_array[chunk_index].addr; + + // insert in by the order of addr + struct list_head *iter = &pool[pool_index].list; + struct list_head *start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + unsigned long iter_addr_long = (unsigned long)tmp->addr; + if (iter_addr_long > addr_long) + { + // list_insert() + iter->prev->next = node; + node->prev = iter->prev; + iter->prev = node; + node->next = iter; + + tmp->size = -1; + tmp->val = FREE; + + break; + } + } + + // check if there are free chunks in same page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + int count = 0; + int page_id = addr_long >> 12; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + count++; + else + break; + } + if (count == (MIN_PAGE_SIZE / (pool_index * MIN_CHUNK_SIZE))) + { + // There is a free page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + break; + } + chunk *first_chunk_in_page = list_entry(iter, chunk, list); + for (int i = 0; i < count; i++) + { + chunk *tmp = list_entry(iter, chunk, list); + tmp->val = FREE; + struct list_head *tmp_next = iter->next; + iter->prev->next = iter->next; + iter->next->prev = iter->prev; + iter->prev = iter; + iter->next = iter; + iter = tmp_next; + } + free_page_frame(first_chunk_in_page->belong_page); + } + + return; +} + +int free_chunk(int index) +{ + // free the page + int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; + put_back_to_pool(pool_index, index); + + return 0; +} + +void free(void *addr) +{ + // printf("[DEBUG] Freeing addr = %p\n", addr); + // Check addr is in which page frame + unsigned long addr_long = (unsigned long)addr; + int frame_index = (addr_long - FREE_MEM_START) / MIN_PAGE_SIZE; + + if (frame_array[frame_index].val != ALLOCATED) + { + printf("This page is Not Allocated yet\n"); + return; + } + + if (frame_array[frame_index].chunk_order != -1) + { + int chunk_index = -1; + // Find chunk_index + for (int i = 0; i < global_chunk_index; i++) + { + if (addr == chunk_array[i].addr) + { + chunk_index = i; + break; + } + } + // Check if is OK to free + if (chunk_index >= global_chunk_index || chunk_array[chunk_index].val != ALLOCATED) + { + if (chunk_index >= global_chunk_index) + printf("chunk_index is TOO high\n"); + if (chunk_array[chunk_index].val != ALLOCATED) + printf("chunk_index = %d\n", chunk_index); + printf("This chunk is Not Allocated yet\n"); + return; + } + + free_chunk(chunk_index); + return; + } + else + { + free_page_frame(frame_index); + return; + } +} + +int roundup_size(int size) +{ + switch (size) + { + case 1 ... 8: + return 1; + case 9 ... 16: + return 2; + case 17 ... 32: + return 4; + case 33 ... 48: + return 6; + case 49 ... 64: + return 8; + case 65 ... 128: + return 16; + case 129 ... 256: + return 32; + } + return 0; +} + +void debug_pool() +{ + printf("** DEBUGGING pool\n"); + for (int i = 0; i < 33; i++) + { + struct list_head *iter; + struct list_head *start; + iter = &pool[i].list; + start = &pool[i].list; + printf("pool[%d] -> ", i); + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + printf("addr %p -> ", tmp->addr); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING chunk\n"); + for (int i = 0; i < 20; i++) + { + printf("chunk_array[%d].index = %d\n", i, chunk_array[i].index); + printf("chunk_array[%d].size = %d\n", i, chunk_array[i].size); + printf("chunk_array[%d].addr = %p\n", i, chunk_array[i].addr); + printf("chunk_array[%d].val = %d\n", i, chunk_array[i].val); + printf("chunk_array[%d].belong_page= %d\n", i, chunk_array[i].belong_page); + printf("\n"); + } + printf("**\n"); +} \ No newline at end of file diff --git a/Lab5/src/kernel/exception.S b/Lab5/src/kernel/exception.S new file mode 100644 index 000000000..ab8b8cc66 --- /dev/null +++ b/Lab5/src/kernel/exception.S @@ -0,0 +1,189 @@ +#define CORE0_INTERRUPT_SOURCE 0x40000060 +#define IIR_ADDR 0x3f215048 + +.global set_el1_exception_vector_table +.global exception_vector_table +.global enable_interrupt +.global disable_interrupt + +enable_interrupt: + msr DAIFClr, 0xf + ret + +disable_interrupt: + msr DAIFSet, 0xf + ret + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 8 + 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] +.endm + +.macro save_all_sys + 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] + mrs x0, spsr_el1 + mrs x1, elr_el1 + mrs x2, sp_el0 + stp x30, x0, [sp ,16 * 15] + stp x1, x2, [sp ,16 * 16] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 8 +.endm + +.macro load_all_sys + ldp x1, x2, [sp ,16 * 16] + ldp x30, x0, [sp ,16 * 15] + msr spsr_el1, x0 + msr elr_el1, x1 + msr sp_el0, x2 + 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, 32 * 9 +.endm + +.align 11 // vector table should be aligned to 0x800 +el1_exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_same_el + .align 7 + b exception_handler_irq_sp_elx_same_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_lower_el + .align 7 + b exception_handler_irq_sp_elx_lower_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_el1_exception_vector_table: + adr x0, el1_exception_vector_table + msr vbar_el1, x0 + ret lr + +exception_handler: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_sync_sp_elx_lower_el: + save_all_sys + mov x0, sp + bl el0_to_el1_sync_handler + load_all_sys + eret + +exception_handler_irq_sp_elx_lower_el: + b el0_core_timer_handler + +exception_handler_sync_sp_elx_same_el: + b exception_handler + +exception_handler_irq_sp_elx_same_el: + b el1_core_interrupt_handler + +el0_core_timer_handler: + save_all_sys + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all_sys + eret + +el1_core_interrupt_handler: + save_all_sys + bl el1_irq_interrupt_handler + load_all_sys + eret \ No newline at end of file diff --git a/Lab5/src/kernel/exception.c b/Lab5/src/kernel/exception.c new file mode 100644 index 000000000..6f10a5784 --- /dev/null +++ b/Lab5/src/kernel/exception.c @@ -0,0 +1,273 @@ +#include "peripherals/mini_uart.h" +#include "peripherals/irq.h" +#include "stdlib.h" +#include "timer.h" +#include "thread.h" +#include "syscall.h" + +extern task_struct *get_current(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern signal_handler signal_table[]; + +/** + * common exception handler + */ +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + // print out interruption type + switch (type) + { + case 0: + uart_send_string("Synchronous"); + break; + case 1: + uart_send_string("IRQ"); + break; + case 2: + uart_send_string("FIQ"); + break; + case 3: + uart_send_string("SError"); + break; + } + uart_send_string(": "); + // decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28) + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown"); + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE"); + break; + case 0b001110: + uart_send_string("Illegal execution"); + break; + case 0b010101: + uart_send_string("System call"); + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL"); + break; + case 0b100001: + uart_send_string("Instruction abort, same EL"); + break; + case 0b100010: + uart_send_string("Instruction alignment fault"); + break; + case 0b100100: + uart_send_string("Data abort, lower EL"); + break; + case 0b100101: + uart_send_string("Data abort, same EL"); + break; + case 0b100110: + uart_send_string("Stack alignment fault"); + break; + case 0b101100: + uart_send_string("Floating point"); + break; + default: + uart_send_string("Unknown"); + break; + } + // decode data abort cause + if (esr >> 26 == 0b100100 || esr >> 26 == 0b100101) + { + uart_send_string(", "); + switch ((esr >> 2) & 0x3) + { + case 0: + uart_send_string("Address size fault"); + break; + case 1: + uart_send_string("Translation fault"); + break; + case 2: + uart_send_string("Access flag fault"); + break; + case 3: + uart_send_string("Permission fault"); + break; + } + switch (esr & 0x3) + { + case 0: + uart_send_string(" at level 0"); + break; + case 1: + uart_send_string(" at level 1"); + break; + case 2: + uart_send_string(" at level 2"); + break; + case 3: + uart_send_string(" at level 3"); + break; + } + } + // dump registers + uart_send_string("\nSPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + + return; +} + +void el1_irq_interrupt_handler() +{ + unsigned int irq_basic_pending = get32(IRQ_BASIC_PENDING); + irq_basic_pending &= (1 << 19); // clear bits + + // GPU IRQ 57 : UART Interrupt + if (irq_basic_pending) + { + if (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + { + uart_rx_handler(); + } + else if (get32(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + { + uart_tx_handler(); + } + } + // ARM Core Timer Interrupt + else if (get32(CORE0_INTR_SRC) & (1 << 1)) + { + long cntpct_el0, cntfrq_el0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + el1_timer_handler(cntpct_el0, cntfrq_el0); + } + + return; +} + +void el0_to_el1_sync_handler(unsigned long trapframe_addr) +{ + int syscall_no; + trapframe *curr_trapframe = (trapframe *)trapframe_addr; + asm volatile("mov %0, x8" + : "=r"(syscall_no)::); + if (syscall_no == 0) + { + int pid = getpid(); + curr_trapframe->x[0] = pid; + } + else if (syscall_no == 1) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + disable_uart_irq(); + enable_interrupt(); + unsigned int ret = uart_read(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 2) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + unsigned int ret = uart_write(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 3) + { + char *name = (char *)curr_trapframe->x[0]; + char **argv = (char **)curr_trapframe->x[1]; + int ret = exec(name, argv); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 4) + { + task_struct *current = get_current(); + current->status = FORKING; + current->trapframe = trapframe_addr; + int ret = fork(); + curr_trapframe = (trapframe *)get_current()->trapframe; + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 5) + { + int status = curr_trapframe->x[0]; + exit(status); + } + else if (syscall_no == 6) + { + unsigned char ch = (unsigned char)curr_trapframe->x[0]; + unsigned int *mbox_user = (unsigned int *)curr_trapframe->x[1]; + int ret = mbox_call_u(ch, mbox_user); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 7) + { + int pid = (int)curr_trapframe->x[0]; + kill(pid); + } + else if (syscall_no == 8) + { + // signal + int SIGNAL = (int)curr_trapframe->x[0]; + void (*handler)() = (void (*)())curr_trapframe->x[1]; + signal(SIGNAL, handler); + } + else if (syscall_no == 9) + { + // signal kill + int pid = (int)curr_trapframe->x[0]; + int SIGNAL = (int)curr_trapframe->x[1]; + int if_cust = 0; + task_struct *current = get_current(); + + if (current->custom_signal) + { + custom_signal *cust_sig = current->custom_signal; + do + { + if (cust_sig->sig_num == SIGNAL) + { + if_cust = 1; + // signal's context save + sig_context_update(curr_trapframe, cust_sig->handler); + break; + } + cust_sig = container_of(cust_sig->list.next, custom_signal, list); + } while (cust_sig != current->custom_signal); + } + else if (!current->custom_signal && !if_cust) + (signal_table[SIGNAL])(pid); + } + else if (syscall_no == 10) + { + // signal restore + sig_context_restore(curr_trapframe); + + disable_interrupt(); + task_struct *current = get_current(); + free(current->signal_context->trapframe); + free(current->signal_context->user_stack); + free(current->signal_context); + current->signal_context = NULL; + enable_interrupt(); + } + else if (syscall_no == 11) + { + task_struct *current = get_current(); + if (current->thread_info->child_id == 0) + printf("child here, fork() return value : %d\n", current->thread_info->child_id); + else + printf("parent here, fork() return value : %d\n", current->thread_info->child_id); + } +} \ No newline at end of file diff --git a/Lab5/src/kernel/kernel.c b/Lab5/src/kernel/kernel.c new file mode 100644 index 000000000..85e57b892 --- /dev/null +++ b/Lab5/src/kernel/kernel.c @@ -0,0 +1,27 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "reserve_mem.h" +#include "device_tree.h" +#include "thread.h" + +#include "virtual_mem.h" + +extern void *_dtb_ptr; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + fdt_traverse(initramfs_callback, _dtb_ptr); + + thread_init(); + + memory_init(); + + // virtual_mem_init(); + + shell_start(); +} diff --git a/Lab5/src/kernel/link.ld b/Lab5/src/kernel/link.ld new file mode 100644 index 000000000..a460f25b2 --- /dev/null +++ b/Lab5/src/kernel/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab5/src/kernel/mbox.c b/Lab5/src/kernel/mbox.c new file mode 100644 index 000000000..f6d91caa7 --- /dev/null +++ b/Lab5/src/kernel/mbox.c @@ -0,0 +1,63 @@ +#include "peripherals/mbox_call.h" +#include "mbox_call.h" +#include "mini_uart.h" + +void mbox_main() +{ + // get the board's unique serial number with a mailbox call + mbox[0] = 21 * 4; // length of the message + mbox[1] = MBOX_REQUEST; // this is a request message + + mbox[2] = MBOX_TAG_MODEL; + mbox[3] = 4; + mbox[4] = 0; // ?? + mbox[5] = 0; + + mbox[6] = MBOX_TAG_REVISION; + mbox[7] = 4; + mbox[8] = 0; // ?? + mbox[9] = 0; + + mbox[10] = MBOX_TAG_GETSERIAL; // get serial number command + mbox[11] = 8; // buffer size + mbox[12] = 8; // ?? + mbox[13] = 0; // clear output buffer + mbox[14] = 0; + + mbox[15] = MBOX_TAG_ARM_MEMORY; // get serial number command + mbox[16] = 8; // buffer size + mbox[17] = 8; // ?? + mbox[18] = 0; // clear output buffer + mbox[19] = 0; + + mbox[20] = MBOX_TAG_LAST; + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) + { + uart_send_string("Board model: "); + uart_hex(mbox[5]); + uart_send_string("\n"); + + uart_send_string("Board revision: "); + uart_hex(mbox[9]); + uart_send_string("\n"); + + uart_send_string("Serial number: "); + uart_hex(mbox[14]); + uart_hex(mbox[13]); + uart_send_string("\n"); + + uart_send_string("ARM memory base address: "); + uart_hex(mbox[18]); + uart_send_string("\n"); + + uart_send_string("ARM memory size: "); + uart_hex(mbox[19]); + uart_send_string("\n"); + } + else + { + uart_send_string("Unable to query serial!\n"); + } +} \ No newline at end of file diff --git a/Lab5/src/kernel/mbox_call.c b/Lab5/src/kernel/mbox_call.c new file mode 100644 index 000000000..d0626f0e8 --- /dev/null +++ b/Lab5/src/kernel/mbox_call.c @@ -0,0 +1,42 @@ +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" + +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) +{ + unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} \ No newline at end of file diff --git a/Lab5/src/kernel/my_signal.c b/Lab5/src/kernel/my_signal.c new file mode 100644 index 000000000..e50ee5e22 --- /dev/null +++ b/Lab5/src/kernel/my_signal.c @@ -0,0 +1,59 @@ +#include "thread.h" +#include "my_signal.h" +#include "syscall.h" +#include "page_alloc.h" +#include "stdlib.h" + +extern task_struct *get_current(); + +signal_handler signal_table[] = { + [0] = &sig_ignore, + [1] = &sig_ignore, + [2] = &sig_ignore, + [3] = &sig_ignore, + [4] = &sig_ignore, + [5] = &sig_ignore, + [6] = &sig_ignore, + [7] = &sig_ignore, + [8] = &sig_ignore, + [SIGKILL] = &sigkill_handler, +}; + +#define current get_current() + +void sig_ignore(int _) +{ + return; +} + +void sigkill_handler(int pid) +{ + kill(pid); + return; +} + +void sig_context_update(struct _trapframe *trapframe, void (*handler)()) +{ + signal_context *sig_context = (signal_context *)my_malloc(sizeof(signal_context)); + sig_context->trapframe = (struct _trapframe *)my_malloc(sizeof(struct _trapframe)); + sig_context->user_stack = my_malloc(MIN_PAGE_SIZE); + memcpy(sig_context->trapframe, trapframe, sizeof(struct _trapframe)); + + current->signal_context = sig_context; + + trapframe->x[30] = (unsigned long)&sig_return; + trapframe->elr_el1 = (unsigned long)handler; + trapframe->sp_el0 = (unsigned long)sig_context->user_stack + MIN_PAGE_SIZE; +} + +void sig_return(void) +{ + asm volatile( + "mov x8, 10\n" + "svc 0\n"); +} + +void sig_context_restore(struct _trapframe *trapframe) +{ + memcpy(trapframe, current->signal_context->trapframe, sizeof(struct _trapframe)); +} diff --git a/Lab5/src/kernel/page_alloc.c b/Lab5/src/kernel/page_alloc.c new file mode 100644 index 000000000..a7202e2bb --- /dev/null +++ b/Lab5/src/kernel/page_alloc.c @@ -0,0 +1,308 @@ +#include "page_alloc.h" +#include "math.h" +#include "stddef.h" +#include "stdlib.h" +#include "reserve_mem.h" +#include "dynamic_alloc.h" + +page_frame_node free_list[MAX_ORDER + 1]; +// int frame_array[TOTAL_NUM_PAGE]; // Why NOT use? no malloc to allocate new link list node +page_frame_node frame_array[TOTAL_NUM_PAGE]; +extern reserved_memory_block RMarray[100]; + +// initialize frame_array and free_list +void init_page_frame() +{ + // free_list + for (int i = 0; i <= MAX_ORDER; i++) + { + free_list[i].index = -1; // head of link list + free_list[i].next = NULL; + free_list[i].previous = NULL; + } + free_list[MAX_ORDER].next = &frame_array[0]; + + // frame_array + frame_array[0].index = 0; + frame_array[0].val = MAX_ORDER; + frame_array[0].addr = (void *)FREE_MEM_START; + frame_array[0].contiguous_head = -1; + frame_array[0].allocated_order = -1; + frame_array[0].next = &frame_array[0 + (1 << MAX_ORDER)]; + frame_array[0].previous = &free_list[MAX_ORDER]; + int previous_index = 0; + for (int i = 1; i < TOTAL_NUM_PAGE; i++) + { + frame_array[i].index = i; + frame_array[i].addr = (void *)FREE_MEM_START + i * MIN_PAGE_SIZE; + frame_array[i].contiguous_head = -1; + frame_array[i].allocated_order = -1; + if (i % (1 << MAX_ORDER) == 0) + { + frame_array[i].val = MAX_ORDER; + frame_array[i].next = NULL; + frame_array[i].previous = &frame_array[previous_index]; + frame_array[previous_index].next = &frame_array[i]; + previous_index = i; + } + else + { + frame_array[i].val = FREE_BUDDY; + frame_array[i].next = NULL; + frame_array[i].previous = NULL; + } + } + + return; +} + +void *my_malloc(int req_size) +{ + unsigned long ret = -1; + if (req_size < MAX_POOL_SIZE) + ret = (unsigned long)get_chunk(req_size); + else + ret = get_page_from_free_list(req_size, -1); + + if (ret == -1) + { + printf("my_malloc FAILED\n"); + return NULL; + } + else + { + if (req_size < MAX_POOL_SIZE) + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, (void *)ret); + return (void *)ret; + } + else + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, frame_array[ret].addr); + return frame_array[ret].addr; + } + } +} + +int get_page_from_free_list(int req_size, int who) +{ + int req_order = -1; + for (int i = 0; i <= MAX_ORDER; i++) + { + if (req_size <= MIN_PAGE_SIZE * pow(2, i)) + { + req_order = i; + break; + } + } + + int alloc_index = -1; + int alloc_order = req_order; + while (alloc_order <= MAX_ORDER) + { + if (free_list[alloc_order].next == NULL) // split high order + { + alloc_order++; + } + else + break; + } + if (alloc_order > MAX_ORDER) + return -1; + while (alloc_order > req_order) + { + // split high order + int removed_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + add_to_free_list(&free_list[alloc_order - 1], removed_index); + add_to_free_list(&free_list[alloc_order - 1], removed_index + pow(2, alloc_order - 1)); + frame_array[removed_index].val = alloc_order - 1; + frame_array[removed_index + (1 << (alloc_order - 1))].val = alloc_order - 1; + alloc_order--; + } + if (alloc_order != req_order) + return -1; + + // get require page + alloc_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + for (int i = 0; i < (1 << alloc_order); i++) + { + frame_array[alloc_index + i].val = ALLOCATED; + frame_array[alloc_index + i].next = NULL; + frame_array[alloc_index + i].previous = NULL; + frame_array[alloc_index + i].contiguous_head = alloc_index; + frame_array[alloc_index + i].allocated_order = alloc_order; + } + + // check the page if contains reserved memory + unsigned long start = (unsigned long)frame_array[alloc_index].addr; + unsigned long end = start + MIN_PAGE_SIZE * (1 << req_order); + int RM_index = check_contain_RM(start, end); + if (RM_index != 0) + { + // Need to change the page allocated + int new_alloc_index = get_page_from_free_list(req_size, who); + free_page_frame(alloc_index); + alloc_index = new_alloc_index; + } + +#ifdef DEBUG + debug(); +#endif + frame_array[alloc_index].chunk_order = who; + return alloc_index; +} + +// This does NOT modify frame_array value +void add_to_free_list(page_frame_node *head_node, int index) +{ + page_frame_node *iter = head_node; + while (iter->next != NULL) + iter = iter->next; + iter->next = &frame_array[index]; + frame_array[index].previous = iter; + frame_array[index].next = NULL; + + return; +} + +void remove_from_free_list(page_frame_node *node_to_be_removed) +{ + if (node_to_be_removed->next != NULL) + node_to_be_removed->next->previous = node_to_be_removed->previous; + node_to_be_removed->previous->next = node_to_be_removed->next; + + node_to_be_removed->next = NULL; + node_to_be_removed->previous = NULL; + + return; +} + +void put_back_to_free_list(int num_of_redundant_page, int index) // 從 index 開始有 num_of_redundant_page 個 free page +{ + int order_to_put = 0; + while (num_of_redundant_page >= (1 << order_to_put)) + order_to_put++; + order_to_put--; + add_to_free_list(&free_list[order_to_put], index); + frame_array[index].val = order_to_put; + if (num_of_redundant_page - (1 << order_to_put) != 0) + put_back_to_free_list(num_of_redundant_page - (1 << order_to_put), index + (1 << order_to_put)); + + return; +} + +int free_page_frame(int index) +{ + int contiguous_head = frame_array[index].contiguous_head; + if (contiguous_head != index) + { + printf("Please free the start page of this contiguous memory when allocated, which is index %d\n", contiguous_head); + return -1; + } + + // Check if buddy can merge + int allocated_order = frame_array[index].allocated_order; + int buddy_index = index ^ (1 << allocated_order); + if (frame_array[buddy_index].val == allocated_order) + { + // can merge + int merged_order = merge_buddy(&index, buddy_index, allocated_order); + if (buddy_index < index) + add_to_free_list(&free_list[merged_order], buddy_index); + else + add_to_free_list(&free_list[merged_order], index); + } + else + { + // can NOT merge + add_to_free_list(&free_list[allocated_order], index); + frame_array[index].val = allocated_order; + frame_array[index].contiguous_head = -1; + frame_array[index].allocated_order = -1; + for (int i = 1; i < (1 << allocated_order); i++) + { + frame_array[index + i].val = FREE_BUDDY; + frame_array[index + i].contiguous_head = -1; + frame_array[index + i].allocated_order = -1; + } + } + +#ifdef DEBUG + debug(); +#endif + + return 0; +} + +// return merged order, YES modify frame_array +int merge_buddy(int *index, int buddy, int order) +{ + // printf("[DEBUG] Merging buddy index = %d and buddy_index = %d, allocated_order = %d\n", *index, buddy, order); + if (order == MAX_ORDER) + return order; + + if (buddy < *index) + { + *index = buddy; + buddy = *index; // Find itself + } + + page_frame_node *iter = free_list[order].next; + while (iter->index != buddy) + iter = iter->next; + + remove_from_free_list(iter); + + frame_array[*index].val = order + 1; + for (int i = 1; i < (1 << (order + 1)); i++) + { + frame_array[i + *index].val = FREE_BUDDY; + } + + if (order + 1 == MAX_ORDER) + return order + 1; + + int new_buddy = *index ^ (1 << (order + 1)); + if (frame_array[*index].val == frame_array[new_buddy].val) + { + frame_array[buddy].val = FREE_BUDDY; + return merge_buddy(index, new_buddy, order + 1); + } + else + return order + 1; +} + +void debug() +{ + printf("** DEBUGGING free_list\n"); + for (int i = 0; i <= MAX_ORDER; i++) + { + page_frame_node *iter; + iter = &free_list[i]; + printf("free_list[%d] -> ", i); + while (iter->next != NULL) + { + iter = iter->next; + printf("index %d -> ", iter->index); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING frame_array\n"); + for (int i = 0; i < 20; i++) + { + printf("frame_array[%d].addr = %p\n", i, frame_array[i].addr); + printf("frame_array[%d].val = %d\n", i, frame_array[i].val); + printf("frame_array[%d].contiguous_head = %d\n", i, frame_array[i].contiguous_head); + printf("frame_array[%d].allocated_order = %d\n", i, frame_array[i].allocated_order); + if (frame_array[i].next != NULL) + printf("frame_array[%d].next->index = %d\n", i, frame_array[i].next->index); + if (frame_array[i].previous != NULL) + printf("frame_array[%d].previous->index = %d\n", i, frame_array[i].previous->index); + } + printf("**\n"); + + return; +} diff --git a/Lab5/src/kernel/read_cpio.c b/Lab5/src/kernel/read_cpio.c new file mode 100644 index 000000000..0d61c3a17 --- /dev/null +++ b/Lab5/src/kernel/read_cpio.c @@ -0,0 +1,161 @@ +#include "stdlib.h" +#include "mini_uart.h" + +extern char *cpioDestGlobal; + +typedef struct cpio_newc_header +{ + 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]; +} __attribute__((packed)) cpio_t; + +void read_cpio(char *cpioDest) +{ + uart_send_string("Type Offset Size Access rights\tFilename\n"); + + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + // print out meta information + uart_hex(hex2int(header->c_mode, 8)); // mode (access rights + type) + uart_send(' '); + uart_hex((unsigned int)((unsigned long)cpioDest) + sizeof(cpio_t) + ns); + uart_send(' '); + uart_hex(fs); // file size in hex + uart_send(' '); + uart_hex(hex2int(header->c_uid, 8)); // user id in hex + uart_send('.'); + uart_hex(hex2int(header->c_gid, 8)); // group id in hex + uart_send('\t'); + uart_send_string(cpioDest + sizeof(cpio_t)); // filename + uart_send_string("\n"); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } +} + +void read_content(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("cat: %s: No such file\n", filename); + return; + } + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + // print content + uart_send_string_of_size((char *)cpioDest, fs); +} + +char *find_content_addr(char *cpioDest, const char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), (char *)filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("find_content_addr: %s: No such file\n", filename); + return NULL; + } + + return cpioDest; +} + +int load_userprogram(const char *filename, char *userDest) +{ + char *cpioUserPgmDest = cpioDestGlobal; + cpioUserPgmDest = find_content_addr(cpioUserPgmDest, filename); + if (cpioUserPgmDest == NULL) + { + uart_send_string("FAIL to find userprogram.img\n"); + return -1; + } + + // Found target file + cpio_t *header = (cpio_t *)cpioUserPgmDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioUserPgmDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioUserPgmDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + printf("load %p to %p\n", cpioUserPgmDest, userDest); + printf("size: %d bytes\n", fs); + + // load content + while (fs--) + { + *userDest++ = *cpioUserPgmDest++; + } + + if (fs == -1) + return 0; + + return 1; +} \ No newline at end of file diff --git a/Lab5/src/kernel/reboot.c b/Lab5/src/kernel/reboot.c new file mode 100644 index 000000000..da809c59a --- /dev/null +++ b/Lab5/src/kernel/reboot.c @@ -0,0 +1,19 @@ +#include "peripherals/reboot.h" + +void set(long addr, unsigned int value) +{ + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) +{ // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab5/src/kernel/reserved_mem.c b/Lab5/src/kernel/reserved_mem.c new file mode 100644 index 000000000..74a60af3e --- /dev/null +++ b/Lab5/src/kernel/reserved_mem.c @@ -0,0 +1,48 @@ +#include "reserve_mem.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "stdlib.h" + +reserved_memory_block RMarray[100]; +int RMindex = 0; + +void memory_reserve(unsigned long start, unsigned long end, char *name) +{ + RMindex++; + RMarray[RMindex].start = start; + RMarray[RMindex].end = end; + strcpy(RMarray[RMindex].name, name); +} + +// return value : if including RM, return which no. of RM. Otherwise, return 0. +int check_contain_RM(unsigned long start, unsigned long end) +{ + for (int i = 1; i <= RMindex; i++) + { + if (RMarray[i].start <= start && start <= RMarray[i].end) + return i; + else if (RMarray[i].start <= end && end <= RMarray[i].end) + return i; + else if (start <= RMarray[i].start && RMarray[i].end <= end) + return i; + else + continue; + } + return 0; +} + +void memory_init() +{ + init_page_frame(); + init_pool(); + + memory_reserve(0x0000, 0x5000, "PGD, PUD"); + memory_reserve(0x60000, 0x100000, "Kernel Img"); + memory_reserve(0x1000000, 0x1000fff, "Printf Buffer"); + memory_reserve(0x8000000, 0x8010000, "Initramfs"); + memory_reserve(0x15000000, 0x17000000, "User Program"); + memory_reserve(0x200000, 0x250000, "svc"); + + return; +} \ No newline at end of file diff --git a/Lab5/src/kernel/shell.c b/Lab5/src/kernel/shell.c new file mode 100644 index 000000000..3a8d4f13f --- /dev/null +++ b/Lab5/src/kernel/shell.c @@ -0,0 +1,214 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" +#include "timer.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "test.h" +#include "thread.h" + +extern void *_dtb_ptr; +extern char *cpioDestGlobal; +extern char read_buffer[100]; +// extern page_frame_node frame_array[TOTAL_NUM_PAGE]; +// extern chunk chunk_array[3000]; + +#define COMMAND_BUFFER 50 +#define FILENAME_BUFFER 20 +#define ARGV_BUFFER 30 + +void shell_start(); + +void shell_main(char *command) +{ + if (!strcmp(command, "help")) + { + uart_send_string("help\t: print this help menu\n"); + uart_send_string("hello\t: print Hello World!\n"); + uart_send_string("reboot\t: reboot the device\n"); + uart_send_string("ls\t: list information about files\n"); + uart_send_string("cat\t: copy each FILE to standard output\n"); + uart_send_string("dts\t: list devicetree\n"); + uart_send_string("asynr\t: [test] asynchronous read\n"); + uart_send_string("asynw\t: [test] asynchronous write\n"); + uart_send_string("setTimeout\t: Usage: setTimeout \n"); + uart_send_string("alloc\t: [test] malloc and free\n"); + uart_send_string("thread\t: [test]\n"); + uart_send_string("syscall\t: [test]\n"); + } + else if (!strcmp(command, "hello")) + { + uart_send_string("Hello World!\n"); + } + else if (!strcmp(command, "reboot")) + { + uart_send_string("Rebooting in 3 seconds\n"); + reset(3 << 16); + while (1) + ; + } + else if (!strcmp(command, "ls")) + { + read_cpio((char *)cpioDestGlobal); + } + else if (!memcmp(command, "cat", 3)) + { + if (command[3] != ' ' || command[4] == '\0') + { + printf("Usage: cat \n"); + return; + } + + char filename[FILENAME_BUFFER]; + memset(filename, '\0', FILENAME_BUFFER); + int i = 4; + while (command[i] != '\0') + { + filename[i - 4] = command[i]; + i++; + } + + read_content((char *)cpioDestGlobal, filename); + } + else if (!strcmp(command, "dts")) + { + fdt_traverse(dtb_parser, _dtb_ptr); + } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x1, 0x0;" // not sure why can't use x0, may have something to to with %0 + "msr spsr_el1, x1;" + "mov x2, %0;" + "add x2, x2, 12;" + "msr elr_el1, x2;" + "mov x2, 0x15000000;" + "msr sp_el0, x2;" + "mrs x2, cntfrq_el0;" + "add x2, x2, x2;" + "msr cntp_tval_el0, x2;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } + else if (!strcmp(command, "asynr")) + { + asyn_read(); + } + else if (!strcmp(command, "asynw")) + { + asyn_write(read_buffer); + uart_send('\n'); + } + else if (!memcmp(command, "setTimeout", 10)) + { + if (command[10] != ' ' || command[11] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char message[MESSAGE_BUFFER]; + memset(message, '\0', MESSAGE_BUFFER); + int i = 11; + while (command[i] != ' ' && command[i] != '\0') + { + message[i - 11] = command[i]; + i++; + } + + if (command[i] != ' ' || command[i] == '\0' || command[i + 1] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char seconds[SECONDS_BUFFER]; + memset(seconds, '\0', SECONDS_BUFFER); + while (command[i] != '\0') + { + seconds[i - strlen(message) - 1 - 11] = command[i]; + i++; + } + int sec; + sec = atoi(seconds); + + add_timer(sec, message); + } + else if (!memcmp(command, "alloc", 5)) + { + test_mem_alloc(); + } + else if (!memcmp(command, "debug", 5)) + { + debug(); + debug_pool(); + } + else if (!memcmp(command, "thread", 6)) + { + test_thread(); + } + else if (!strcmp(command, "syscall")) + { + thread_create(load_usrpgm_in_umode); + idle_task(); + } + + return; +} + +void shell_start() +{ + uart_send_string("Starting shell...\n"); + char c; + int i = 0; + + char command[COMMAND_BUFFER]; + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("$ "); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + command[i] = '\0'; + uart_send(c); + shell_main(command); + memset(command, '\0', COMMAND_BUFFER); + i = 0; + uart_send_string("$ "); + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else if (c == 127) // Also backspace but delete + { + } + else + { + if (i < COMMAND_BUFFER) + { + if (c == 0) // solve the problem that first command on board wont work + continue; + command[i++] = c; + } + uart_send(c); + } + } + } +} diff --git a/Lab5/src/kernel/syscall.c b/Lab5/src/kernel/syscall.c new file mode 100644 index 000000000..1a867f807 --- /dev/null +++ b/Lab5/src/kernel/syscall.c @@ -0,0 +1,159 @@ +#include "stdlib.h" +#include "thread.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "read_cpio.h" +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" +#include "my_signal.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); +extern void switch_to(task_struct *, task_struct *); +extern task_struct kernel_thread; +extern struct list_head task_rq_head; +extern struct list_head task_zombieq_head; + +int getpid() +{ + int ret = get_current()->thread_info->id; + return ret; +} + +size_t uart_read(char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + buf[i] = uart_recv(); + if (buf[i] == '\n' || buf[i] == '\r') + return i; + } + return size; +} + +size_t uart_write(const char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + if (buf[i] == '\0') + return i; + uart_send(buf[i]); + } + return size; +} + +int exec(const char *name, char *const argv[]) +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + load_userprogram(name, (char *)target_addr); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); + + return 0; +} + +int fork() +{ + schedule(); + return get_current()->thread_info->child_id; +} + +void exit(int status) +{ + task_struct *current = (task_struct *)get_current(); + current->status = ZOMBIE; + INIT_LIST_HEAD(¤t->list); + list_add_tail(¤t->list, &task_zombieq_head); + // schedule(); + switch_to(current, &kernel_thread); +} + +int mbox_call_u(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 (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} + +void kill(int pid) +{ + task_struct *tmp = get_current(); + if (tmp->thread_info->id == pid) + exit(0); + + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + tmp = container_of(iter, task_struct, list); + if (tmp->thread_info->id == pid) + { + tmp->status = ZOMBIE; + return; + } + } +} + +void signal(int SIGNAL, void (*handler)()) +{ + printf("[info] Called signal()\n"); + task_struct *cur = get_current(); + + custom_signal *new = (custom_signal *)my_malloc(sizeof(custom_signal)); + new->sig_num = SIGNAL; + new->handler = handler; + INIT_LIST_HEAD(&new->list); + + if (!cur->custom_signal) + cur->custom_signal = new; + else + list_add_tail(&cur->custom_signal->list, &new->list); +} \ No newline at end of file diff --git a/Lab5/src/kernel/test.c b/Lab5/src/kernel/test.c new file mode 100644 index 000000000..0f97e5b8f --- /dev/null +++ b/Lab5/src/kernel/test.c @@ -0,0 +1,91 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "thread.h" +#include "syscall.h" +#include "read_cpio.h" +#include "utils.h" +#include "timer.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); + +void test_mem_alloc() +{ + void *a; + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + return; +} + +void foo() +{ + for (int i = 0; i < 10; ++i) + { + task_struct *cur = get_current(); + printf("Thread id: %d %d\n", cur->thread_info->id, i); + delay(1000000); + schedule(); + } +} + +void test_thread() +{ + int N = 5; + for (int i = 0; i < N; ++i) + { // N should > 2 + thread_create(foo); + } + + // thread_create(load_usrpgm_in_umode); + idle_task(); + + debug_task_rq(); + return; +} + +void load_usrpgm_in_umode() +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + load_userprogram("syscall.img", (char *)target_addr); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} \ No newline at end of file diff --git a/Lab5/src/kernel/thread.S b/Lab5/src/kernel/thread.S new file mode 100644 index 000000000..ffc3ece7b --- /dev/null +++ b/Lab5/src/kernel/thread.S @@ -0,0 +1,31 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +.global kernel_thread_init +kernel_thread_init: + msr tpidr_el1, x0 + ret \ No newline at end of file diff --git a/Lab5/src/kernel/thread.c b/Lab5/src/kernel/thread.c new file mode 100644 index 000000000..c88ac7b82 --- /dev/null +++ b/Lab5/src/kernel/thread.c @@ -0,0 +1,257 @@ +#include "stdlib.h" +#include "thread.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "list.h" +#include "syscall.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void switch_to(task_struct *, task_struct *); +extern void kernel_thread_init(); + +long thread_cnt = 0; + +task_struct kernel_thread = {0}; +struct list_head task_rq_head; // run queue +struct list_head task_zombieq_head; // zombie queue + +void schedule() +{ + task_struct *cur = get_current(); + task_struct *next = del_rq(); + + if (next == NULL) + next = &kernel_thread; + if (cur != &kernel_thread) + add_rq(cur); + + set_switch_timer(); + enable_interrupt(); + + if (next->status == FORKING) + { + add_rq(next); + switch_to(cur, &kernel_thread); + } + else if (next->status == ZOMBIE) + { + INIT_LIST_HEAD(&next->list); + list_add_tail(&next->list, &task_zombieq_head); + switch_to(cur, &kernel_thread); + } + else + { + switch_to(cur, next); + } +} + +void add_rq(task_struct *task) +{ + INIT_LIST_HEAD(&task->list); + list_add_tail(&task->list, &task_rq_head); +} + +task_struct *del_rq() +{ + struct list_head *ret; + ret = task_rq_head.next; + if (ret != &task_rq_head) + { + list_del_init(ret); + return container_of(ret, task_struct, list); + } + else + return NULL; +} + +void thread_init() +{ + INIT_LIST_HEAD(&task_rq_head); + INIT_LIST_HEAD(&task_zombieq_head); + kernel_thread_init(&kernel_thread); + return; +} + +thread_info *thread_create(func_ptr fp) +{ + task_struct *new_task = (task_struct *)my_malloc(sizeof(task_struct)); + thread_info *new_thread = (thread_info *)my_malloc(sizeof(thread_info)); + + new_task->thread_info = new_thread; + new_thread->task = new_task; + + new_task->kstack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); + new_task->ustack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); + new_task->usrpgm_load_addr = USRPGM_BASE + thread_cnt * USRPGM_SIZE; + + new_task->task_context.fp = new_task->kstack_start + MIN_PAGE_SIZE; + new_task->task_context.lr = (unsigned long)task_wrapper; + new_task->task_context.sp = new_task->kstack_start + MIN_PAGE_SIZE; + new_task->thread_info->id = thread_cnt++; + new_task->status = READY; + new_task->job = fp; + new_task->custom_signal = NULL; + + add_rq(new_task); + + return new_thread; +} + +/* threads' routine for any task */ +void task_wrapper() +{ + task_struct *current = get_current(); + (current->job)(); + exit(0); +} + +void idle_task() +{ + while (!list_empty(&task_rq_head) || !list_empty(&task_zombieq_head)) + { + disable_interrupt(); + kill_zombies(); + do_fork(); + enable_interrupt(); + schedule(); + } +} + +/* kill the zombie threads and recycle their resources */ +void kill_zombies() +{ + struct list_head *iter = &task_zombieq_head; + struct list_head *start = &task_zombieq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + free((void *)tmp->kstack_start); + free((void *)tmp->ustack_start); + free(tmp->thread_info); + if (tmp->custom_signal) + free(tmp->custom_signal); + free(tmp); + } + INIT_LIST_HEAD(&task_zombieq_head); + return; +} + +void do_fork() +{ + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + if (tmp->status == FORKING) + create_child(tmp); + } +} + +/* copy all data including stack and program for child process and set the corresponding sp and lr for it*/ +void create_child(task_struct *parent) +{ + thread_info *child_thread = thread_create(0); + task_struct *child = child_thread->task; + + char *parent_d, *child_d; + + parent->status = READY; + + parent->thread_info->child_id = child->thread_info->id; + child->thread_info->child_id = 0; + + // copy context + parent_d = (char *)&(parent->task_context); + child_d = (char *)&(child->task_context); + for (int i = 0; i < sizeof(context); i++) + child_d[i] = parent_d[i]; + + // copy custom_signal + if (parent->custom_signal) + { + child->custom_signal = (custom_signal *)my_malloc(sizeof(custom_signal)); + parent_d = (char *)&(parent->custom_signal); + child_d = (char *)&(child->custom_signal); + for (int i = 0; i < sizeof(custom_signal); i++) + child_d[i] = parent_d[i]; + } + + // copy kernel stack + parent_d = (char *)parent->kstack_start; + child_d = (char *)child->kstack_start; + for (int i = 0; i < MIN_PAGE_SIZE; i++) + child_d[i] = parent_d[i]; + + // copy user stack + parent_d = (char *)parent->ustack_start; + child_d = (char *)child->ustack_start; + for (int i = 0; i < MIN_PAGE_SIZE; i++) + child_d[i] = parent_d[i]; + + // copy user program + parent_d = (char *)parent->usrpgm_load_addr; + child_d = (char *)child->usrpgm_load_addr; + for (int i = 0; i < USRPGM_SIZE; i++) + child_d[i] = parent_d[i]; + + // set offset to child's stack + unsigned long kstack_offset = child->kstack_start - parent->kstack_start; + unsigned long ustack_offset = child->ustack_start - parent->ustack_start; + unsigned long usrpgm_offset = child->usrpgm_load_addr - parent->usrpgm_load_addr; + + // set child kernel space offset + child->task_context.fp += kstack_offset; + child->task_context.sp += kstack_offset; + child->trapframe = parent->trapframe + kstack_offset; + + // set child user space offset + trapframe *ctrapframe = (trapframe *)child->trapframe; // because of data type problem + ctrapframe->x[29] += ustack_offset; + ctrapframe->sp_el0 += ustack_offset; + ctrapframe->elr_el1 += usrpgm_offset; +} + +void debug_task_rq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_rq_head; + start = &task_rq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_rq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} + +void debug_task_zombieq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_zombieq_head; + start = &task_zombieq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_zombieq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} \ No newline at end of file diff --git a/Lab5/src/kernel/timer.S b/Lab5/src/kernel/timer.S new file mode 100644 index 000000000..a22d66ec4 --- /dev/null +++ b/Lab5/src/kernel/timer.S @@ -0,0 +1,32 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + mrs x2, cntkctl_el1 // following three instructions for Lab5 to access cpu timer register + orr x2, x2, #0x1 + msr cntkctl_el1, x2 + ret + +.global core_timer_disable +core_timer_disable: + mov x0, 0 + msr cntp_ctl_el0, x0 // disable + ret + +.global set_switch_timer +set_switch_timer: + mrs x0, cntfrq_el0 + mov x0, x0, lsr#5 + msr cntp_tval_el0, x0 + ret + +/* +cntpct_el0: The timer’s current count. +cntp_cval_el0: A compared timer count. If cntpct_el0 >= cntp_cval_el0, interrupt the CPU core. +cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +*/ \ No newline at end of file diff --git a/Lab5/src/kernel/timer.c b/Lab5/src/kernel/timer.c new file mode 100644 index 000000000..5e8c42f58 --- /dev/null +++ b/Lab5/src/kernel/timer.c @@ -0,0 +1,176 @@ +#include "stdlib.h" +#include "timer.h" +#include "thread.h" + +extern void enable_interrupt(); +extern void disable_interrupt(); + +typedef struct timer_queue_node +{ + long second_ticks; + char message[MESSAGE_BUFFER]; +} tq; + +tq timer_queue[10]; +int timer_queue_front = 0; +int timer_queue_back = 0; + +void get_current_time() +{ + long cntpct_el0, cntfrq_el0; + long cntp_tval_el0; + long cntp_cval_el0; + + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + "mrs %2, cntp_tval_el0;" + "mrs %3, cntp_cval_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0), "=r"(cntp_tval_el0), "=r"(cntp_cval_el0)); + + long nowtime = cntpct_el0 / cntfrq_el0; + + printf("%ld seconds after booting\n", nowtime); + + // printf("cntpct_el0 = %d\n", cntpct_el0); + // printf("cntfrq_el0 = %d\n", cntfrq_el0); + // printf("cntp_tval_el0 = %d\n", cntp_tval_el0); + // printf("cntp_cval_el0 = %d\n", cntp_cval_el0); + + return; +} + +void add_timer(int sec, char *mes) +{ + get_current_time(); + + for (int i = 0; i < MESSAGE_BUFFER; i++) + timer_queue[timer_queue_back].message[i] = mes[i]; + + // transfer sec to frq and store to node.second + asm volatile( + "msr DAIFSet, 0xf;" + "mrs x3, cntfrq_el0;" + "mrs x4, cntpct_el0;" + "mov x2, %1;" // after secs seconds later will interrupt + "mul x2, x2, x3;" + "add x2, x2, x4;" + "mov %0, x2;" + : "=r"(timer_queue[timer_queue_back].second_ticks) + : "r"(sec) + : "x0", "x1", "x2", "x3", "x4", "memory"); // Uses register operands x0, x1, x2, x3, and x4 and specifies that the instruction may modify memory using the clobber list "x0", "x1", "x2", "x3", "x4", "memory". + + timer_queue_back++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + + asm volatile( + "msr cntp_cval_el0, %0;" + "bl core_timer_enable;" + "msr DAIFClr, 0xf;" + : + : "r"(timer_queue[timer_queue_front].second_ticks)); +} + +void el0_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + disable_interrupt(); + schedule(); + enable_interrupt(); +} + +void el1_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + if (!is_timer_queue_empty()) + { + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + printf("\n"); + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + printf("Message: %s\n", timer_queue[timer_queue_front].message); + + timer_queue_front++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + asm volatile( + "msr cntp_cval_el0, %0\n\t" // set expired time + "bl core_timer_enable\n\t" + : + : "r"(timer_queue[timer_queue_front].second_ticks) + :); + } + else + { + disable_interrupt(); + schedule(); + enable_interrupt(); + } + return; +} + +int is_timer_queue_empty() +{ + return timer_queue_front == timer_queue_back ? 1 : 0; +} + +void timer_delay(long seconds) +{ + long cntpct_el0, cntfrq_el0, nowtime, due; + + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + + due = cntpct_el0 / cntfrq_el0 + seconds; + nowtime = cntpct_el0 / cntfrq_el0; + + while (nowtime <= due) + { + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + nowtime = cntpct_el0 / cntfrq_el0; + } +} \ No newline at end of file diff --git a/Lab5/src/kernel/virtual_mem.S b/Lab5/src/kernel/virtual_mem.S new file mode 100644 index 000000000..5453ad829 --- /dev/null +++ b/Lab5/src/kernel/virtual_mem.S @@ -0,0 +1,54 @@ +#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) + +#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_ACCESS (1 << 10) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + + +.global tcr_init +tcr_init: + ldr x0, = TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + ret + +.global mair_init +mair_init: + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 + ret + +.global identity_init +identity_init: + mov x0, 0 // PGD's page frame at 0x0 + mov x1, 0x1000 // PUD's page frame at 0x1000 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x0] + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + ret \ No newline at end of file diff --git a/Lab5/src/kernel/virtual_mem.c b/Lab5/src/kernel/virtual_mem.c new file mode 100644 index 000000000..d05dd504e --- /dev/null +++ b/Lab5/src/kernel/virtual_mem.c @@ -0,0 +1,10 @@ +extern void tcr_init(); +extern void mair_init(); +extern void identity_init(); + +void virtual_mem_init() +{ + tcr_init(); + mair_init(); + identity_init(); +} \ No newline at end of file diff --git a/Lab5/src/lib/hex2int.c b/Lab5/src/lib/hex2int.c new file mode 100644 index 000000000..4f30c1df8 --- /dev/null +++ b/Lab5/src/lib/hex2int.c @@ -0,0 +1,15 @@ +int hex2int(char *s, int n) +{ + int r = 0; + while (n-- > 0) + { + r <<= 4; + if (*s >= '0' && *s <= '9') + r += *s++ - '0'; + else if (*s >= 'A' && *s <= 'F') + r += *s++ - 'A' + 10; + else if (*s >= 'a' && *s <= 'f') + r += *s++ - 'a' + 10; + } + return r; +} \ No newline at end of file diff --git a/Lab5/src/lib/math.c b/Lab5/src/lib/math.c new file mode 100644 index 000000000..b96e3e335 --- /dev/null +++ b/Lab5/src/lib/math.c @@ -0,0 +1,9 @@ +int pow(int base, int exp) +{ + int result = 1; + for (int i = 0; i < exp; i++) + { + result *= base; + } + return result; +} \ No newline at end of file diff --git a/Lab5/src/lib/mem.c b/Lab5/src/lib/mem.c new file mode 100644 index 000000000..954ba9805 --- /dev/null +++ b/Lab5/src/lib/mem.c @@ -0,0 +1,33 @@ +#include + +void *memset(void *dest, register int val, int len) +{ + register unsigned char *ptr = (unsigned char *)dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +int memcmp(void *s1, void *s2, int n) +{ + unsigned char *a = s1, *b = s2; + while (n-- > 0) + { + if (*a != *b) + { + return *a - *b; + } + a++; + b++; + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} \ No newline at end of file diff --git a/Lab5/src/lib/mini_uart.c b/Lab5/src/lib/mini_uart.c new file mode 100644 index 000000000..e780073bf --- /dev/null +++ b/Lab5/src/lib/mini_uart.c @@ -0,0 +1,193 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "peripherals/irq.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; + +char read_buffer[100]; +char write_buffer[100]; +int len_WB = 0; +int len_RB = 0; +int buffer_count = 0; + +void uart_send(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + + if (c != '\x7f') + put32(AUX_MU_IO_REG, c); + + if (c == '\n') + uart_send('\r'); +} + +char uart_recv(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r == '\r' ? '\n' : r; +} + +void uart_send_string(char *str) +{ + while (*str) + uart_send(*str++); +} + +void uart_send_space(int size) +{ + while (size--) + uart_send(' '); +} + +void uart_send_string_of_size(char *str, int size) +{ + while (size--) + uart_send(*str++); +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) +{ + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) + { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_init(void) +{ + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 2 << 12; // set alt5 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 2 << 15; // set alt5 for gpio15 + put32(GPFSEL1, selector); + + put32(GPPUD, 0); + delay(150); // spec + put32(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + put32(GPPUDCLK0, 0); + + put32(AUX_ENABLES, 1); // Enable mini uart (this also enables access to its registers) + put32(AUX_MU_CNTL_REG, 0); // Disable auto flow control and disable receiver and transmitter (for now) + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + put32(AUX_MU_LCR_REG, 3); // Enable 8 bit mode + put32(AUX_MU_MCR_REG, 0); // Set RTS line to be always high + put32(AUX_MU_BAUD_REG, 270); // Set baud rate to 115200 + + put32(AUX_MU_CNTL_REG, 3); // Finally, enable transmitter and receiver +} + +void _putchar(char character) +{ + uart_send(character); +} + +/** + * Display a string + */ +// void printf(char *fmt, ...) +// { +// __builtin_va_list args; +// __builtin_va_start(args, fmt); +// // we don't have memory allocation yet, so we +// // simply place our string after our code +// char *s = (char *)&_end; +// // use sprintf to format our string +// vsprintf(s, fmt, args); +// // print out as usual +// while (*s) +// { +// /* convert newline to carrige return + newline */ +// if (*s == '\n') +// uart_send('\r'); +// uart_send(*s++); +// } +// } + +void asyn_read() +{ + memset(read_buffer, '\0', 100); + + put32(AUX_MU_IER_REG, 1); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (read_buffer[strlen(read_buffer) - 1] != '\r') + ; +} + +void asyn_write(char *str) +{ + strcpy(write_buffer, str); + len_WB = strlen(write_buffer); + + put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (strlen(write_buffer) != 0) + ; +} + +void uart_rx_handler() +{ + read_buffer[len_RB++] = get32(AUX_MU_IO_REG); + + if (read_buffer[len_RB - 1] == '\r') + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + read_buffer[len_RB] = '\0'; + len_RB = 0; + } +} + +void uart_tx_handler() +{ + if (buffer_count < len_WB) + put32(AUX_MU_IO_REG, write_buffer[buffer_count++]); + else + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + buffer_count = 0; + memset(write_buffer, '\0', 100); + } +} + +void enable_uart_irq() +{ + put32(ENABLE_IRQS_1, 1 << 29); +} + +void disable_uart_irq() +{ + put32(DISABLE_IRQS_1, 1 << 29); +} \ No newline at end of file diff --git a/Lab5/src/lib/mm.S b/Lab5/src/lib/mm.S new file mode 100644 index 000000000..1bd32ff37 --- /dev/null +++ b/Lab5/src/lib/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/Lab5/src/lib/printf.c b/Lab5/src/lib/printf.c new file mode 100644 index 000000000..3eabd6eca --- /dev/null +++ b/Lab5/src/lib/printf.c @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "printf.h" + +#define PRINTF_DISABLE_SUPPORT_FLOAT + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct +{ + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) + { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)buffer; + (void)idx; + (void)maxlen; + if (character) + { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)idx; + (void)maxlen; + if (character) + { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) +{ + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) + { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (size_t i = len; i < width; i++) + { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) + { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) + { + len--; + if (len && (base == 16U)) + { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) + { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) + { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) + { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) + { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) + { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) + { + } + else if ((frac == 0U) || (frac & 1U)) + { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) + { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) + { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) + { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) + { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union + { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) + { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) + { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) + { + if ((int)prec > expval) + { + prec = (unsigned)((int)prec - expval - 1); + } + else + { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) + { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) + { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else + { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) + { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) + { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) + { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else + { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do + { + switch (*format) + { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) + { + width = _atoi(&format); + } + else if (*format == '*') + { + const int w = va_arg(va, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else + { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) + { + precision = _atoi(&format); + } + else if (*format == '*') + { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) + { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') + { + base = 16U; + } + else if (*format == 'o') + { + base = 8U; + } + else if (*format == 'b') + { + base = 2U; + } + else + { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) + { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) + { + // signed + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else + { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else + { + // unsigned + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': + { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': + { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) + { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': + { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) + { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } + else + { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char *buffer, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} \ No newline at end of file diff --git a/Lab5/src/lib/queue.c b/Lab5/src/lib/queue.c new file mode 100644 index 000000000..6b56b4dca --- /dev/null +++ b/Lab5/src/lib/queue.c @@ -0,0 +1,7 @@ +void queue_push(char c) +{ +} + +void queue_pop() +{ +} \ No newline at end of file diff --git a/Lab5/src/lib/simple_malloc.c b/Lab5/src/lib/simple_malloc.c new file mode 100644 index 000000000..b7a80a897 --- /dev/null +++ b/Lab5/src/lib/simple_malloc.c @@ -0,0 +1,8 @@ +char *counter = (char *)0x10000000; + +void *simple_malloc(unsigned int size) +{ + char *dest = counter; + counter += size; + return dest; +} diff --git a/Lab5/src/lib/string.c b/Lab5/src/lib/string.c new file mode 100644 index 000000000..be47302ee --- /dev/null +++ b/Lab5/src/lib/string.c @@ -0,0 +1,79 @@ +#include + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + unsigned char c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + break; + if (c1 == '\0') + return 0; + } + + return c1 - c2; +} + +int strlen(const char *str) +{ + const unsigned char *s = (const unsigned char *)str; + unsigned int len = 0; + + while (1) + { + if (*s++ == '\0') + return len; + len++; + } +} + +char *strcpy(char *destination, const char *source) +{ + // return if no memory is allocated to the destination + if (destination == NULL) + { + return NULL; + } + + // take a pointer pointing to the beginning of the destination string + char *ptr = destination; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + + // include the terminating null character + *destination = '\0'; + + // the destination is returned by standard `strcpy()` + return ptr; +} + +// A simple atoi() function +int atoi(char *str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + res = res * 10 + str[i] - '0'; + + // return result. + return res; +} diff --git a/Lab5/src/lib/utils.S b/Lab5/src/lib/utils.S new file mode 100644 index 000000000..c35527a42 --- /dev/null +++ b/Lab5/src/lib/utils.S @@ -0,0 +1,15 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret diff --git a/Lab5/src/userprogram/link.ld b/Lab5/src/userprogram/link.ld new file mode 100644 index 000000000..d84040b38 --- /dev/null +++ b/Lab5/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x15000000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab5/src/userprogram/user.S b/Lab5/src/userprogram/user.S new file mode 100644 index 000000000..6b0a2824a --- /dev/null +++ b/Lab5/src/userprogram/user.S @@ -0,0 +1,22 @@ +.section ".text.boot" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + mov x8, #0 + svc 0 + mov x8, #4 + svc 0 + mov x8, #9 + svc 0 + mov x8, #5 + svc 0 + blt 1b +1: + b 1b + +get_pid: + mov x8, #0 + svc 0 + ret \ No newline at end of file diff --git a/Lab5/userprogram.img b/Lab5/userprogram.img new file mode 100755 index 000000000..494ba6607 Binary files /dev/null and b/Lab5/userprogram.img differ diff --git a/Lab6/Makefile b/Lab6/Makefile new file mode 100644 index 000000000..6f5ff4ea6 --- /dev/null +++ b/Lab6/Makefile @@ -0,0 +1,65 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only +CFLAGS = -Wall -O -ffreestanding -nostdlib -nostartfiles -g -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.img userprogram.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + @mkdir -p $(@D) + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/kernel/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/kernel/link.ld -o $(BUILD_DIR)/kernel/kernel8.elf $(filter $(BUILD_DIR)/kernel/%_c.o $(BUILD_DIR)/kernel/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel/kernel8.elf -O binary kernel8.img + +int_qemu: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug_int: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +bootloader.img: $(SRC_DIR)/bootloader/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/bootloader/link.ld -o $(BUILD_DIR)/bootloader/bootloader.elf $(filter $(BUILD_DIR)/bootloader/%_c.o $(BUILD_DIR)/bootloader/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/bootloader/bootloader.elf bootloader.img + +userprogram.img: $(SRC_DIR)/userprogram/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/userprogram/link.ld -o $(BUILD_DIR)/userprogram/userprogram.elf $(filter $(BUILD_DIR)/userprogram/%_c.o $(BUILD_DIR)/userprogram/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/userprogram/userprogram.elf userprogram.img + +test: + @echo Hello + +pseudoTTY: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug_boot: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -s -S + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb diff --git a/Lab6/bcm2710-rpi-3-b-plus.dtb b/Lab6/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..0dd0e2f43 Binary files /dev/null and b/Lab6/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab6/bootloader.img b/Lab6/bootloader.img new file mode 100755 index 000000000..564c55747 Binary files /dev/null and b/Lab6/bootloader.img differ diff --git a/Lab6/bootloader.py b/Lab6/bootloader.py new file mode 100644 index 000000000..1b99fa7d7 --- /dev/null +++ b/Lab6/bootloader.py @@ -0,0 +1,25 @@ +import os +import time + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +# with open('/dev/pts/35', "wb", buffering=0) as tty: +with open('/dev/ttyUSB0', "wb", buffering=0) as tty: + # send Start + tty.write(b"Start") + time.sleep(1) + + # send kernel size + tty.write(file_size.to_bytes(4, 'little')) + time.sleep(1) + + # send kernel + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + tty.write(data) + time.sleep(1) + diff --git a/Lab6/include/device_tree.h b/Lab6/include/device_tree.h new file mode 100644 index 000000000..e3e6aee78 --- /dev/null +++ b/Lab6/include/device_tree.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_TREE_H +#define _DEVICE_TREE_H + +typedef int (*fdt_callback)(void *); +int initramfs_callback(void *dtb); +int dtb_parser(void *dtb); +void fdt_traverse(fdt_callback cb, char *dtb); + +#endif /*_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab6/include/dynamic_alloc.h b/Lab6/include/dynamic_alloc.h new file mode 100644 index 000000000..55eb68307 --- /dev/null +++ b/Lab6/include/dynamic_alloc.h @@ -0,0 +1,35 @@ +#ifndef _DYNAMIC_ALLOC_H +#define _DYNAMIC_ALLOC_H + +#include "list.h" + +#define MAX_POOL_SIZE 256 +#define MIN_CHUNK_SIZE 8 +#define FREE 98 + +typedef struct _chunk +{ + int index; // const + int size; + void *addr; // const + int val; + int belong_page; + struct list_head list; +} chunk; + +typedef struct _pool_list +{ + struct list_head list; +} pool_list; + +void init_pool(); +void *get_chunk(int req_size); +int roundup_size(int size); +void split_page(int frame_index, int req_pool_index); +int remove_a_chunk_from_pool(int req_pool_index); +int free_chunk(int index); +void put_back_to_pool(int pool_index, int chunk_index); +void debug_pool(); +void free(void *addr); + +#endif /*_DYNAMIC_ALLOC_H */ diff --git a/Lab6/include/list.h b/Lab6/include/list.h new file mode 100644 index 000000000..a0fb29587 --- /dev/null +++ b/Lab6/include/list.h @@ -0,0 +1,441 @@ +/* Linux-like double-linked list implementation */ + +#ifndef SYSPROG21_LIST_H +#define SYSPROG21_LIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *)0)->member) *__pmember = (ptr); \ + (type *)((char *)__pmember - offsetof(type, member)); \ + }) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr)-offsetof(type, member))) +#endif +#endif + + /** + * struct list_head - Head and node of a double-linked list + * @prev: pointer to the previous node in the list + * @next: pointer to the next node in the list + * + * The simple double-linked list consists of a head and nodes attached to + * this head. Both node and head share the same struct type. The list_* + * functions and macros can be used to access and modify this data structure. + * + * The @prev pointer of the list head points to the last list node of the + * list and @next points to the first list node of the list. For an empty list, + * both member variables point to the head. + * + * The list nodes are usually embedded in a container structure which holds the + * actual data. Such an container object is called entry. The helper list_entry + * can be used to calculate the object address from the address of the node. + */ + struct list_head + { + struct list_head *prev; + struct list_head *next; + }; + +/** + * LIST_HEAD - Declare list head and initialize it + * @head: name of the new object + */ +#define LIST_HEAD(head) struct list_head head = {&(head), &(head)} + + /** + * INIT_LIST_HEAD() - Initialize empty list head + * @head: pointer to list head + * + * This can also be used to initialize a unlinked list node. + * + * A node is usually linked inside a list, will be added to a list in + * the near future or the entry containing the node will be free'd soon. + * + * But an unlinked node may be given to a function which uses list_del(_init) + * before it ends up in a previously mentioned state. The list_del(_init) on an + * initialized node is well defined and safe. But the result of a + * list_del(_init) on an uninitialized node is undefined (unrelated memory is + * modified, crashes, ...). + */ + static inline void INIT_LIST_HEAD(struct list_head *head) + { + head->next = head; + head->prev = head; + } + + /** + * list_add() - Add a list node to the beginning of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add(struct list_head *node, struct list_head *head) + { + struct list_head *next = head->next; + + next->prev = node; + node->next = next; + node->prev = head; + head->next = node; + } + + /** + * list_add_tail() - Add a list node to the end of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add_tail(struct list_head *node, struct list_head *head) + { + struct list_head *prev = head->prev; + + prev->next = node; + node->next = head; + node->prev = prev; + head->prev = node; + } + + /** + * list_del() - Remove a list node from the list + * @node: pointer to the node + * + * The node is only removed from the list. Neither the memory of the removed + * node nor the memory of the entry containing the node is free'd. The node + * has to be handled like an uninitialized node. Accessing the next or prev + * pointer of the node is not safe. + * + * Unlinked, initialized nodes are also uninitialized after list_del. + * + * LIST_POISONING can be enabled during build-time to provoke an invalid memory + * access when the memory behind the next/prev pointer is used after a list_del. + * This only works on systems which prohibit access to the predefined memory + * addresses. + */ + static inline void list_del(struct list_head *node) + { + struct list_head *next = node->next; + struct list_head *prev = node->prev; + + next->prev = prev; + prev->next = next; + +#ifdef LIST_POISONING + node->prev = (struct list_head *)(0x00100100); + node->next = (struct list_head *)(0x00200200); +#endif + } + + /** + * list_del_init() - Remove a list node from the list and reinitialize it + * @node: pointer to the node + * + * The removed node will not end up in an uninitialized state like when using + * list_del. Instead the node is initialized again to the unlinked state. + */ + static inline void list_del_init(struct list_head *node) + { + list_del(node); + INIT_LIST_HEAD(node); + } + + /** + * list_empty() - Check if list head has no nodes attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not empty !0 - list is empty + */ + static inline int list_empty(const struct list_head *head) + { + return (head->next == head); + } + + /** + * list_is_singular() - Check if list head has exactly one node attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not singular !0 -list has exactly one entry + */ + static inline int list_is_singular(const struct list_head *head) + { + return (!list_empty(head) && head->prev == head->next); + } + + /** + * list_splice() - Add list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice(struct list_head *list, struct list_head *head) + { + struct list_head *head_first = head->next; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->next = list_first; + list_first->prev = head; + + list_last->next = head_first; + head_first->prev = list_last; + } + + /** + * list_splice_tail() - Add list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice_tail(struct list_head *list, + struct list_head *head) + { + struct list_head *head_last = head->prev; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->prev = list_last; + list_last->next = head; + + list_first->prev = head_last; + head_last->next = list_first; + } + + /** + * list_splice_init() - Move list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_init(struct list_head *list, + struct list_head *head) + { + list_splice(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_splice_tail_init() - Move list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) + { + list_splice_tail(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_cut_position() - Move beginning of a list to another list + * @head_to: pointer to the head of the list which receives nodes + * @head_from: pointer to the head of the list + * @node: pointer to the node in which defines the cutting point + * + * All entries from the beginning of the list @head_from to (including) the + * @node is moved to @head_to. + * + * @head_to is replaced when @head_from is not empty. @node must be a real + * list node from @head_from or the behavior is undefined. + */ + static inline void list_cut_position(struct list_head *head_to, + struct list_head *head_from, + struct list_head *node) + { + struct list_head *head_from_first = head_from->next; + + if (list_empty(head_from)) + return; + + if (head_from == node) + { + INIT_LIST_HEAD(head_to); + return; + } + + head_from->next = node->next; + head_from->next->prev = head_from; + + head_to->prev = node; + node->next = head_to; + head_to->next = head_from_first; + head_to->next->prev = head_to; + } + + /** + * list_move() - Move a list node to the beginning of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the beginning of + * @head + */ + static inline void list_move(struct list_head *node, struct list_head *head) + { + list_del(node); + list_add(node, head); + } + + /** + * list_move_tail() - Move a list node to the end of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the end of @head + */ + static inline void list_move_tail(struct list_head *node, + struct list_head *head) + { + list_del(node); + list_add_tail(node, head); + } + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_last_entry() - get last entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of last entry in list + */ +#define list_last_entry(head, type, member) \ + list_entry((head)->prev, type, member) + +/** + * list_for_each - iterate over list nodes + * @node: list_head pointer used as iterator + * @head: pointer to the head of the list + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + */ +#define list_for_each(node, head) \ + for (node = (head)->next; node != (head); node = node->next) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif + +/** + * list_for_each_safe - iterate over list nodes and allow deletes + * @node: list_head pointer used as iterator + * @safe: list_head pointer used to store info for next entry in list + * @head: pointer to the head of the list + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + */ +#define list_for_each_safe(node, safe, head) \ + for (node = (head)->next, safe = node->next; node != (head); \ + node = safe, safe = node->next) + +/** + * list_for_each_entry_safe - iterate over list entries and allow deletes + * @entry: pointer used as iterator + * @safe: @type pointer used to store info for next entry in list + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#define list_for_each_entry_safe(entry, safe, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member), \ + safe = list_entry(entry->member.next, __typeof__(*entry), member); \ + &entry->member != (head); entry = safe, \ + safe = list_entry(safe->member.next, __typeof__(*entry), member)) + +#undef __LIST_HAVE_TYPEOF + +#ifdef __cplusplus +} +#endif + +#endif /* SYSPROG21_LIST_H */ \ No newline at end of file diff --git a/Lab6/include/load_kernel.h b/Lab6/include/load_kernel.h new file mode 100644 index 000000000..a5dc68ffb --- /dev/null +++ b/Lab6/include/load_kernel.h @@ -0,0 +1,7 @@ +#ifndef _LOAD_KERNEL_H +#define _LOAD_KERNEL_H + +void load_kernel(char *dest); +void relocate(char *from_dest, char *to_dest); + +#endif /*_LOAD_KERNEL_H */ diff --git a/Lab6/include/math.h b/Lab6/include/math.h new file mode 100644 index 000000000..3107a97d8 --- /dev/null +++ b/Lab6/include/math.h @@ -0,0 +1 @@ +int pow(int base, int exp); \ No newline at end of file diff --git a/Lab6/include/mbox.h b/Lab6/include/mbox.h new file mode 100644 index 000000000..d43fd936b --- /dev/null +++ b/Lab6/include/mbox.h @@ -0,0 +1,6 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); + +#endif /*_MBOX_H */ diff --git a/Lab6/include/mbox_call.h b/Lab6/include/mbox_call.h new file mode 100644 index 000000000..7f60645d0 --- /dev/null +++ b/Lab6/include/mbox_call.h @@ -0,0 +1,9 @@ +#ifndef _MBOX_CALL_H +#define _MBOX_CALL_H + +/* a properly aligned buffer */ +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch); + +#endif /*_MBOX_CALL_H */ diff --git a/Lab6/include/mini_uart.h b/Lab6/include/mini_uart.h new file mode 100644 index 000000000..51eb0e34c --- /dev/null +++ b/Lab6/include/mini_uart.h @@ -0,0 +1,20 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void uart_send_string_of_size(char *str, int size); +void uart_hex(unsigned int d); +void uart_send_space(int size); + +void asyn_read(); +void asyn_write(); +void uart_rx_handler(); +void uart_tx_handler(); + +void enable_uart_irq(); +void disable_uart_irq(); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab6/include/mm.h b/Lab6/include/mm.h new file mode 100644 index 000000000..1d947fb75 --- /dev/null +++ b/Lab6/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (6 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/Lab6/include/my_signal.h b/Lab6/include/my_signal.h new file mode 100644 index 000000000..d3f998486 --- /dev/null +++ b/Lab6/include/my_signal.h @@ -0,0 +1,30 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include "list.h" +#include "thread.h" + +#define SIGKILL 9 +#define SIG_NUM (sizeof(signal_table) / sizeof(signal_table[0])) +typedef void (*signal_handler)(int); + +typedef struct _custom_signal +{ + unsigned int sig_num; + signal_handler handler; + struct list_head list; +} custom_signal; + +typedef struct _signal_context +{ + struct _trapframe *trapframe; + char *user_stack; +} signal_context; + +void sig_ignore(int _); +void sigkill_handler(int pid); +void sig_context_update(struct _trapframe *trapframe, void (*handler)()); +void sig_return(void); +void sig_context_restore(struct _trapframe *trapframe); + +#endif /*_SIGNAL_H */ diff --git a/Lab6/include/page_alloc.h b/Lab6/include/page_alloc.h new file mode 100644 index 000000000..dab0c4aab --- /dev/null +++ b/Lab6/include/page_alloc.h @@ -0,0 +1,41 @@ +#ifndef _PAGE_ALLOC_H +#define _PAGE_ALLOC_H + +// #define DEBUG + +#include "virtual_mem.h" + +#define MAX_ORDER 11 // order 0 ~ 11 +#define ALLOCATED -1 +#define FREE_BUDDY 99 +#define MIN_PAGE_SIZE 0x1000 +// #define FREE_MEM_START 0x10000000 +// #define FREE_MEM_END 0x20000000 +#define FREE_MEM_START KERNEL_PA_TO_VA(0x00) +#define FREE_MEM_END KERNEL_PA_TO_VA(0x3C000000) +#define TOTAL_NUM_PAGE (FREE_MEM_END - FREE_MEM_START) / MIN_PAGE_SIZE + +typedef struct _page_frame_node page_frame_node; +struct _page_frame_node +{ + int index; // const + int val; + void *addr; // const + int contiguous_head; // if allocated, this value keep track who (page) is the start of the contiguous memory in index + int allocated_order; + int chunk_order; // -1 if no chunk, otherwise 1, 2, 4, 6, 8, 16, 32 + page_frame_node *next; + page_frame_node *previous; +}; + +void init_page_frame(); +void *my_malloc(int req_size); +int get_page_from_free_list(int req_size, int who); +void add_to_free_list(page_frame_node *head_node, int index); +void remove_from_free_list(page_frame_node *free_list_node); +void put_back_to_free_list(int num_of_redundant_page, int index); +int free_page_frame(int index); +int merge_buddy(int *index, int buddy, int order); +void debug(); + +#endif /*_PAGE_ALLOC_H */ diff --git a/Lab6/include/peripherals/base.h b/Lab6/include/peripherals/base.h new file mode 100644 index 000000000..e5037e70c --- /dev/null +++ b/Lab6/include/peripherals/base.h @@ -0,0 +1,8 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +#include "virtual_mem.h" + +#define PBASE KERNEL_PA_TO_VA(0x3F000000) + +#endif /*_P_BASE_H */ diff --git a/Lab6/include/peripherals/device_tree.h b/Lab6/include/peripherals/device_tree.h new file mode 100644 index 000000000..d5d3bc729 --- /dev/null +++ b/Lab6/include/peripherals/device_tree.h @@ -0,0 +1,16 @@ +#ifndef _P_DEVICE_TREE_H +#define _P_DEVICE_TREE_H + +#define FDT_MAGIC 0xD00DFEED +#define FDT_VERSION 0x00000011 +#define FDT_TK_NULL 0X00000000 + +#define FDT_BEGIN_NODE 0X00000001 +#define FDT_END_NODE 0X00000002 +#define FDT_PROP 0X00000003 +#define FDT_NOP 0X00000004 +#define FDT_END 0X00000009 + +#define FDT_CPIO_INITRAMFS_PROPNAME "linux,initrd-start" + +#endif /*_P_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab6/include/peripherals/gpio.h b/Lab6/include/peripherals/gpio.h new file mode 100644 index 000000000..a7a6a5b4c --- /dev/null +++ b/Lab6/include/peripherals/gpio.h @@ -0,0 +1,25 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL0 (PBASE + 0x00200000) +#define GPFSEL1 (PBASE + 0x00200004) +#define GPFSEL2 (PBASE + 0x00200008) +#define GPFSEL3 (PBASE + 0x0020000C) +#define GPFSEL4 (PBASE + 0x00200010) +#define GPFSEL5 (PBASE + 0x00200014) +#define GPSET0 (PBASE + 0x0020001C) +#define GPSET1 (PBASE + 0x00200020) +#define GPCLR0 (PBASE + 0x00200028) +#define GPLEV0 (PBASE + 0x00200034) +#define GPLEV1 (PBASE + 0x00200038) +#define GPEDS0 (PBASE + 0x00200040) +#define GPEDS1 (PBASE + 0x00200044) +#define GPHEN0 (PBASE + 0x00200064) +#define GPHEN1 (PBASE + 0x00200068) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) +#define GPPUDCLK1 (PBASE + 0x0020009C) + +#endif /*_P_GPIO_H */ diff --git a/Lab6/include/peripherals/irq.h b/Lab6/include/peripherals/irq.h new file mode 100644 index 000000000..d2d6ec266 --- /dev/null +++ b/Lab6/include/peripherals/irq.h @@ -0,0 +1,28 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" +#include "virtual_mem.h" + +#define IRQ_BASIC_PENDING (PBASE + 0x0000B200) +#define IRQ_PENDING_1 (PBASE + 0x0000B204) +#define IRQ_PENDING_2 (PBASE + 0x0000B208) +#define FIQ_CONTROL (PBASE + 0x0000B20C) +#define ENABLE_IRQS_1 (PBASE + 0x0000B210) +#define ENABLE_IRQS_2 (PBASE + 0x0000B214) +#define ENABLE_BASIC_IRQS (PBASE + 0x0000B218) +#define DISABLE_IRQS_1 (PBASE + 0x0000B21C) +#define DISABLE_IRQS_2 (PBASE + 0x0000B220) +#define DISABLE_BASIC_IRQS (PBASE + 0x0000B224) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#define CORE0_INTR_SRC KERNEL_PA_TO_VA(0x40000060) +#define CORE1_INTR_SRC KERNEL_PA_TO_VA(0x40000064) +#define CORE2_INTR_SRC KERNEL_PA_TO_VA(0x40000068) +#define CORE3_INTR_SRC KERNEL_PA_TO_VA(0x4000006C) + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/Lab6/include/peripherals/mbox_call.h b/Lab6/include/peripherals/mbox_call.h new file mode 100644 index 000000000..cfcc0d3ad --- /dev/null +++ b/Lab6/include/peripherals/mbox_call.h @@ -0,0 +1,40 @@ +#ifndef _P_MBOX_CALL_H +#define _P_MBOX_CALL_H + +#include "peripherals/base.h" + +#define VIDEOCORE_MBOX (PBASE + 0x0000B880) +#define MBOX_READ (VIDEOCORE_MBOX + 0x0) +#define MBOX_POLL (VIDEOCORE_MBOX + 0x10) +#define MBOX_SENDER (VIDEOCORE_MBOX + 0x14) +#define MBOX_STATUS (VIDEOCORE_MBOX + 0x18) +#define MBOX_CONFIG (VIDEOCORE_MBOX + 0x1C) +#define MBOX_WRITE (VIDEOCORE_MBOX + 0x20) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_MODEL 0x10001 +#define MBOX_TAG_REVISION 0x10002 +#define MBOX_TAG_MAC_ADDRESS 0x10003 +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_ARM_MEMORY 0x10005 +#define MBOX_TAG_VC_MEMORY 0x10006 +#define MBOX_TAG_CLOCKS 0x10007 +#define MBOX_TAG_LAST 0 + +#endif /* _P_MBOX_CALL_H */ diff --git a/Lab6/include/peripherals/mini_uart.h b/Lab6/include/peripherals/mini_uart.h new file mode 100644 index 000000000..71119b511 --- /dev/null +++ b/Lab6/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/Lab6/include/peripherals/reboot.h b/Lab6/include/peripherals/reboot.h new file mode 100644 index 000000000..3795a8af8 --- /dev/null +++ b/Lab6/include/peripherals/reboot.h @@ -0,0 +1,10 @@ +#ifndef _P_REBOOT_H +#define _P_REBOOT_H + +#include "virtual_mem.h" + +#define PM_PASSWORD KERNEL_PA_TO_VA(0x5a000000) +#define PM_RSTC KERNEL_PA_TO_VA(0x3F10001c) +#define PM_WDOG KERNEL_PA_TO_VA(0x3F100024) + +#endif /*_P_REBOOT_H */ \ No newline at end of file diff --git a/Lab6/include/printf.h b/Lab6/include/printf.h new file mode 100644 index 000000000..a9501cba0 --- /dev/null +++ b/Lab6/include/printf.h @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ + void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ + int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ + int sprintf_(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + int snprintf_(char *buffer, size_t count, const char *format, ...); + int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ + int vprintf_(const char *format, va_list va); + + /** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ + int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ \ No newline at end of file diff --git a/Lab6/include/read_cpio.h b/Lab6/include/read_cpio.h new file mode 100644 index 000000000..e843d9a0a --- /dev/null +++ b/Lab6/include/read_cpio.h @@ -0,0 +1,9 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); +char *find_content_addr(char *cpioDest, const char *filename); +int load_userprogram(const char *, char *); + +#endif /*_READ_CPIO_H */ \ No newline at end of file diff --git a/Lab6/include/reboot.h b/Lab6/include/reboot.h new file mode 100644 index 000000000..e9fb1d66c --- /dev/null +++ b/Lab6/include/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif /*_REBOOT_H */ \ No newline at end of file diff --git a/Lab6/include/reserve_mem.h b/Lab6/include/reserve_mem.h new file mode 100644 index 000000000..3f10aeaa9 --- /dev/null +++ b/Lab6/include/reserve_mem.h @@ -0,0 +1,20 @@ +#ifndef _RESERVE_MEM_H +#define _RESERVE_MEM_H + +#include "virtual_mem.h" + +#define USRPGM_BASE KERNEL_PA_TO_VA(0x15000000) +#define USRPGM_SIZE 0x100000 + +typedef struct _reserved_memory_block +{ + unsigned long start; + unsigned long end; + char name[30]; +} reserved_memory_block; + +void memory_reserve(unsigned long start, unsigned long end, char *name); +int check_contain_RM(unsigned long start, unsigned long end); +void memory_init(); + +#endif /*_RESERVE_MEM_H */ \ No newline at end of file diff --git a/Lab6/include/shell.h b/Lab6/include/shell.h new file mode 100644 index 000000000..f677e039b --- /dev/null +++ b/Lab6/include/shell.h @@ -0,0 +1,7 @@ +#ifndef _SHELL_H +#define _SHELL_H + +void shell_main(char *); +void shell_start(); + +#endif /*_SHELL_H */ diff --git a/Lab6/include/stdlib.h b/Lab6/include/stdlib.h new file mode 100644 index 000000000..31b8255dc --- /dev/null +++ b/Lab6/include/stdlib.h @@ -0,0 +1,28 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" +#include "utils.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const char *str); +char *strcpy(char *destination, const char *source); +int atoi(char *str); + +void *memset(void *dest, register int val, int len); +int memcmp(void *s1, void *s2, int n); +void *memcpy(void *dest, const void *src, size_t len); +int hex2int(char *s, int n); + +void *simple_malloc(unsigned int size); + +// void printf(char *fmt, ...); +// unsigned int sprintf(char *dst, char *fmt, ...); +// unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +#endif /*_STDLIB_H */ diff --git a/Lab6/include/syscall.h b/Lab6/include/syscall.h new file mode 100644 index 000000000..aca95eae1 --- /dev/null +++ b/Lab6/include/syscall.h @@ -0,0 +1,16 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + +#include "stdlib.h" + +int getpid(); +size_t uart_read(char buf[], size_t size); +size_t uart_write(const char buf[], size_t size); +int exec(const char *name, char *const argv[]); +int fork(); +void exit(int status); +int mbox_call_u(unsigned char ch, unsigned int *mbox); +void kill(int pid); +void signal(int SIGNAL, void (*handler)()); + +#endif /*_SYSCALL_H */ \ No newline at end of file diff --git a/Lab6/include/test.h b/Lab6/include/test.h new file mode 100644 index 000000000..994fcc065 --- /dev/null +++ b/Lab6/include/test.h @@ -0,0 +1,8 @@ +#ifndef _TEST_H +#define _TEST_H + +void test_mem_alloc(); +void test_thread(); +void load_usrpgm_in_umode(); + +#endif /*_TEST_H */ diff --git a/Lab6/include/thread.h b/Lab6/include/thread.h new file mode 100644 index 000000000..05236cd87 --- /dev/null +++ b/Lab6/include/thread.h @@ -0,0 +1,68 @@ +#ifndef _THREAD_H +#define _THREAD_H + +#include "list.h" +#include "my_signal.h" + +#define READY 1 +#define ZOMBIE 2 +#define FORKING 4 + +typedef void (*func_ptr)(); + +typedef struct _context +{ + unsigned long x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, fp, lr, sp; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long esr_el1; + unsigned long ttbr0_el1; +} context; + +typedef struct _thread_info +{ + unsigned long id; + unsigned long child_id; + struct _task_struct *task; + +} thread_info; + +typedef struct _trapframe +{ + unsigned long x[31]; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trapframe; + +typedef struct _task_struct +{ + struct _context task_context; // context need to be the first one + struct _thread_info *thread_info; + struct list_head list; + func_ptr job; + unsigned long kstack_start; // kernel stack base + unsigned long ustack_start; // user stack base + unsigned long usrpgm_load_addr; // user program load address + unsigned long ustack_start_pa; // user stack base + unsigned long usrpgm_load_addr_pa; // user program load address + unsigned long status; + unsigned long trapframe; // using "unsigned long" to keep trapframe address, instead of claiming a "trapframe_t*" to avoid "Data Abort" + struct _custom_signal *custom_signal; + struct _signal_context *signal_context; +} task_struct; + +void schedule(); +void add_rq(task_struct *task); +task_struct *del_rq(); +void thread_init(); +thread_info *thread_create(func_ptr fp); +void task_wrapper(); +void idle_task(); +void kill_zombies(); +void do_fork(); +void create_child(task_struct *parent); +void debug_task_rq(); +void debug_task_zombieq(); + +#endif /*_THREAD_H */ \ No newline at end of file diff --git a/Lab6/include/timer.h b/Lab6/include/timer.h new file mode 100644 index 000000000..2e352e7fe --- /dev/null +++ b/Lab6/include/timer.h @@ -0,0 +1,14 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#define MESSAGE_BUFFER 50 +#define SECONDS_BUFFER 20 + +void get_current_time(); +void el0_timer_handler(long cntpct_el0, long cntfrq_el0); +void el1_timer_handler(long cntpct_el0, long cntfrq_el0); +void add_timer(int sec, char *mes); +int is_timer_queue_empty(); +void timer_delay(long seconds); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab6/include/utils.h b/Lab6/include/utils.h new file mode 100644 index 000000000..f8455ef45 --- /dev/null +++ b/Lab6/include/utils.h @@ -0,0 +1,32 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay(unsigned long); +extern void put32(unsigned long, unsigned int); +extern unsigned int get32(unsigned long); +extern void put64(unsigned long, unsigned int); +extern unsigned int get64(unsigned long); + +#define read_sysreg(r) ({ \ + unsigned long __val; \ + asm volatile("mrs %0, " #r \ + : "=r"(__val)); \ + __val; \ +}) + +#define write_sysreg(r, __val) ({ \ + asm volatile("msr " #r ", %0" ::"r"(__val)); \ +}) + +#define read_gen_reg(r) ({ \ + unsigned long __val; \ + asm volatile("mov %0, " #r \ + : "=r"(__val)); \ + __val; \ +}) + +#define write_gen_reg(r, __val) ({ \ + asm volatile("mov " #r ", %0" ::"r"(__val)); \ +}) + +#endif /*_BOOT_H */ diff --git a/Lab6/include/virtual_mem.h b/Lab6/include/virtual_mem.h new file mode 100644 index 000000000..ab2e974c8 --- /dev/null +++ b/Lab6/include/virtual_mem.h @@ -0,0 +1,45 @@ +#ifndef _VIRTUAL_MEM_H +#define _VIRTUAL_MEM_H + +#include + +#define PAGE_SIZE 0x1000 +#define USTACK_VA 0xffffffffb000 +#define STACK_SIZE 0x4000 +#define UPROG_VA 0x0 + +#define PD_TABLE 0b11 +#define PD_BLOCK 0b01 +#define PD_PAGE 0b11 +#define PD_ACCESS (1 << 10) // The access flag, a page fault is generated if not set. +#define PD_USER_RW (0b1 << 6) +#define PD_USER_R (0b11 << 6) +#define PD_UXN (1L << 54) +#define PD_PXN (1L << 53) + +#define DEFAULT_THREAD_VA_CODE_START 0x0000 +#define DEFAULT_THREAD_VA_STACK_START 0xFFFFFFFFB000 + +#define ENTRY_GET_ATTRS(num) ((num)&0xFFFF000000000FFF) +#define CLEAR_LOW_12bit(num) ((num)&0xFFFFFFFFFFFFF000) +#define KERNEL_VA_TO_PA(addr) (((uint64_t)(addr)) & 0x0000FFFFFFFFFFFFLL) +#define KERNEL_PA_TO_VA(addr) (((uint64_t)(addr)) | 0xFFFF000000000000LL) + +#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) + +#define MAIR_DEVICE_nGnRnE 0b00000000 +#define MAIR_NORMAL_NOCACHE 0b01000100 +#define MAIR_IDX_DEVICE_nGnRnE 0 +#define MAIR_IDX_NORMAL_NOCACHE 1 + +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + +void setup_kernel_space_mapping(); +uint64_t *new_page_table(); +void map_pages(uint64_t *pgd, uint64_t va_start, uint64_t pa_start, int num); +void *virtual_mem_translate(void *virtual_addr); + +#endif /*_VIRTUAL_MEM_H */ \ No newline at end of file diff --git a/Lab6/initramfs.cpio b/Lab6/initramfs.cpio new file mode 100644 index 000000000..2be1223b6 Binary files /dev/null and b/Lab6/initramfs.cpio differ diff --git a/Lab6/kernel8.img b/Lab6/kernel8.img new file mode 100755 index 000000000..ad311ac94 Binary files /dev/null and b/Lab6/kernel8.img differ diff --git a/Lab6/rootfs/a.c b/Lab6/rootfs/a.c new file mode 100644 index 000000000..d2398d75b --- /dev/null +++ b/Lab6/rootfs/a.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + printf("HAHA return 1\n"); + + return 1; +} diff --git a/Lab6/rootfs/cat.txt b/Lab6/rootfs/cat.txt new file mode 100644 index 000000000..949e8345b --- /dev/null +++ b/Lab6/rootfs/cat.txt @@ -0,0 +1 @@ +No cat here. \ No newline at end of file diff --git a/Lab6/rootfs/one b/Lab6/rootfs/one new file mode 100644 index 000000000..50ecbb596 --- /dev/null +++ b/Lab6/rootfs/one @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 diff --git a/Lab6/rootfs/ts.txt b/Lab6/rootfs/ts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Lab6/rootfs/userprogram.img b/Lab6/rootfs/userprogram.img new file mode 100755 index 000000000..2c228ba23 Binary files /dev/null and b/Lab6/rootfs/userprogram.img differ diff --git a/Lab6/rootfs/vm.img b/Lab6/rootfs/vm.img new file mode 100644 index 000000000..8ba674e6c Binary files /dev/null and b/Lab6/rootfs/vm.img differ diff --git a/Lab6/send_kernel.py b/Lab6/send_kernel.py new file mode 100644 index 000000000..1b17155f2 --- /dev/null +++ b/Lab6/send_kernel.py @@ -0,0 +1,40 @@ +# This python script is for macOS +from serial import Serial +import os +import time +import fcntl +import threading + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +with Serial('/dev/tty.usbserial-0001', 115200) as ser: + sem = threading.Semaphore() + # set tty to non-blocking mode + fcntl.fcntl(ser, fcntl.F_SETFL, os.O_NONBLOCK) + + input("Press anything to start transmission\n") + + print("Sending Strat...") + sem.acquire() + ser.write(b"Start") + sem.release() + time.sleep(1) + + sem.acquire() + ser.write(file_size.to_bytes(4, 'little')) + sem.release() + time.sleep(1) + + print("Start sending kernel img by uart...") + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + sem.acquire() + ser.write(data) + sem.release() + time.sleep(1) + + print("Transfer finished!") diff --git a/Lab6/src/bootloader/boot.S b/Lab6/src/bootloader/boot.S new file mode 100644 index 000000000..403960d2e --- /dev/null +++ b/Lab6/src/bootloader/boot.S @@ -0,0 +1,25 @@ +#include "mm.h" + +.section ".text.boot" + +.globl _start +_start: + mov x24, x0 + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + mov x0, x24 + bl bootloader_main + b proc_hang // should never come here diff --git a/Lab6/src/bootloader/bootloader.c b/Lab6/src/bootloader/bootloader.c new file mode 100644 index 000000000..327413557 --- /dev/null +++ b/Lab6/src/bootloader/bootloader.c @@ -0,0 +1,15 @@ +#include "mini_uart.h" +#include "load_kernel.h" + +void bootloader_main(void) +{ + uart_init(); + + uart_send_string("Relocatting ...\n"); + char *from_dest = (char *)0x80000; + char *to_dest = (char *)0x60000; + relocate((char *)from_dest, (char *)to_dest); + + char *dest = (char *)0x80000; + load_kernel((char *)dest); +} diff --git a/Lab6/src/bootloader/config.txt b/Lab6/src/bootloader/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/Lab6/src/bootloader/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/Lab6/src/bootloader/link.ld b/Lab6/src/bootloader/link.ld new file mode 100644 index 000000000..1fca3dfb7 --- /dev/null +++ b/Lab6/src/bootloader/link.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0x80000; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; +__loader_size = (_end - _start); diff --git a/Lab6/src/bootloader/load_kernel.c b/Lab6/src/bootloader/load_kernel.c new file mode 100644 index 000000000..00808de84 --- /dev/null +++ b/Lab6/src/bootloader/load_kernel.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include "mini_uart.h" +#include "peripherals/mini_uart.h" +#include "stdlib.h" + +extern int __loader_size; +extern void *_dtb_ptr; + +void load_kernel(char *dest) +{ + int size = 0; + + uart_send_string("Waiting for kernel8.img ...\n"); + + char start[5] = "Start"; + + for (int i = 0; i < 5; i++) + { + if (uart_recv() != start[i]) + i = 0; + } + + uart_send_string("Start transmitting ...\n"); + + size = uart_recv(); + size |= uart_recv() << 8; + size |= uart_recv() << 16; + size |= uart_recv() << 24; + + printf("Size of kernel is = %d bytes\n", size); + + // read the kernel + while (size--) + { + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + *dest++ = get32(AUX_MU_IO_REG); + } + + uart_send_string("End transmitting ...\n"); + + // restore arguments and jump to the new kernel. + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + // we must force an absolute address to branch to + "mov x30, 0x80000; ret"); +} + +void relocate(char *from_dest, char *to_dest) +{ + long long size = (long long)&__loader_size; + + while (size--) + { + *to_dest++ = *from_dest++; + } + + char *redicrect = __builtin_return_address(0) + (to_dest - from_dest); + + goto *redicrect; +} \ No newline at end of file diff --git a/Lab6/src/kernel/boot.S b/Lab6/src/kernel/boot.S new file mode 100644 index 000000000..d305049e5 --- /dev/null +++ b/Lab6/src/kernel/boot.S @@ -0,0 +1,92 @@ +#include "mm.h" + +.global _dtb_ptr +.section .data +_dtb_ptr: .dc.a 0x0 + +.section ".text.boot" + +#define PA(p) (p - 0xffff000000000000) + +.globl _start +_start: + cbz x24, x24c // Check if bootloader, see bootloader's boot.S for more info +x24nc: + ldr x21, =PA(_dtb_ptr) + str x24, [x21] + b go +x24c: + ldr x21, =PA(_dtb_ptr) + str x0, [x21] +go: + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + + // get CurrentEL + mrs x0, CurrentEL + and x0, x0, #12 // clear reserved bits + + // running at EL3? branch if in EL3 + cmp x0, #12 + beq from_el3_to_el2 + + // running at EL1? branch if in EL1 + cmp x0, #4 + beq bss + + bl from_el2_to_el1 + +bss: + ldr x0, =PA(__bss_start) + ldr x1, =PA(__bss_end) + sub x1, x1, x0 + bl memzero + + bl tcr_init + bl mair_init + bl setup_kernel_space_mapping + bl setup_identity_mapping + + bl set_el1_exception_vector_table // set el1 exception vector table base + + ldr x9, =PA(_kernel_end) + mov sp, x9 + bl kernel_main + b proc_hang // should never come here + +from_el3_to_el2: + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, from_el2_to_el1 + msr elr_el3, x2 + eret + +from_el2_to_el1: + msr sp_el1, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // change execution level to EL1 + mov x2, #0x3c5 + msr spsr_el2, x2 + msr elr_el2, lr + eret \ No newline at end of file diff --git a/Lab6/src/kernel/config.txt b/Lab6/src/kernel/config.txt new file mode 100644 index 000000000..279349696 --- /dev/null +++ b/Lab6/src/kernel/config.txt @@ -0,0 +1,2 @@ +kernel_old=1 +disable_commandline_tags=1 diff --git a/Lab6/src/kernel/device_tree.c b/Lab6/src/kernel/device_tree.c new file mode 100644 index 000000000..ce90a1944 --- /dev/null +++ b/Lab6/src/kernel/device_tree.c @@ -0,0 +1,267 @@ +#include +#include "peripherals/device_tree.h" +#include "stdlib.h" +#include "mini_uart.h" +#include "device_tree.h" +#include "virtual_mem.h" + +typedef struct fdt_header +{ + uint32_t magic; // big-endian + 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; +} fdt_header; + +typedef struct +{ + uint32_t len; + uint32_t nameoff; +} fdt_prop; + +char *cpioDestGlobal; + +static uint64_t pad_to_4(void *num); +static uint32_t rev32(uint32_t val); +static uint64_t endian_rev(void *input, int dtype_size); + +int initramfs_callback(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + uint8_t *p = off_dt_struct; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + if (!strcmp(FDT_CPIO_INITRAMFS_PROPNAME, prop_name)) + { + uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); + printf("initramfs_addr at %p\n", addr); + cpioDestGlobal = (char *)KERNEL_PA_TO_VA(addr); + } + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +int dtb_parser(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + int depth = 0; + uint8_t *p = off_dt_struct; + char *node_name = NULL; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + node_name = (char *)p; + if (depth == 0) + printf("\\ {\n"); + else + { + uart_send_space(depth * 3); + printf("%s {\n", node_name); + } + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + depth++; + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + depth--; + uart_send_space(depth * 3); + printf("};\n"); + printf("\n"); + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + + if (!strcmp(prop_name, "#address-cells") || !strcmp(prop_name, "#size-cells") || !strcmp(prop_name, "interrupt-parent")) + { + // + uart_send_space(depth * 3); + printf("%s = <%d>;\n", prop_name, rev32(*((uint32_t *)prop_val))); + } + else if (!strcmp(prop_name, "model") || !strcmp(prop_name, "status") || !strcmp(prop_name, "name") || !strcmp(prop_name, "device_type") || + !strcmp(prop_name, "chassis-type") || !strcmp(prop_name, "bootargs") || !strcmp(prop_name, "stdout-path") || !strcmp(prop_name, "stdin-path") || + !strcmp(prop_name, "power-isa-version") || !strcmp(prop_name, "mmu-type") || !strcmp(prop_name, "label") || !strcmp(prop_name, "phy-connection-type")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else if (!strcmp(prop_name, "compatible")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else + { + uart_send_space(depth * 3); + printf("%s = %s;\n", prop_name, prop_val); + } + + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +void fdt_traverse(fdt_callback cb, char *dtb) +{ + if (cb(dtb) == -1) + printf("fdt_traverse failed.\n"); + + return; +} + +static uint64_t pad_to_4(void *num) +{ + uint64_t modded = ((uint64_t)num) % 4; + return modded == 0 ? 0 : 4 - modded; +} + +static uint32_t rev32(uint32_t val) +{ + return (uint32_t)endian_rev(&val, 4); +} + +/** Transform data from litle to big endian, or from big to little endian + * @param input: Pointer to a value that needs to be transformed + * @param dtype_size: data type size, size of each item in bytes. Possible value: 1, 2, 4, 8 + */ +static uint64_t endian_rev(void *input, int dtype_size) +{ + const uint8_t *ptr = (uint8_t *)input; + uint64_t ret = 0; + + switch (dtype_size) + { + // int8_t, uint8_t + case 1: + // No need to transform to big endian since the data type size is 1 byte + break; + + // int16_t, uint16_t + case 2: + ret = (ptr[0] << 8) | ptr[1]; + break; + + // int32_t, uint32_t + case 4: + ret = (ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + ptr[3]; + break; + + // int64_t, uint64_t + // case 8: + // ret = (ptr[0] << 56) | + // (ptr[1] << 48) | + // (ptr[2] << 40) | + // (ptr[3] << 32) | + // (ptr[4] << 24) | + // (ptr[5] << 16) | + // (ptr[6] << 8) | + // ptr[7]; + // break; + + default: + printf("[Error] Endian transformation(%d) not implemented. @line %d, file:%s\r\n", dtype_size, __LINE__, __FILE__); + break; + } + return ret; +} \ No newline at end of file diff --git a/Lab6/src/kernel/dynamic_alloc.c b/Lab6/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..a16a426b0 --- /dev/null +++ b/Lab6/src/kernel/dynamic_alloc.c @@ -0,0 +1,249 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "list.h" + +extern page_frame_node frame_array[TOTAL_NUM_PAGE]; + +pool_list pool[33]; // 1, 2, 4, 6, 8, 16, 32 +chunk chunk_array[3000]; +int global_chunk_index = -1; + +void init_pool() +{ + for (int i = 0; i < 33; i++) + INIT_LIST_HEAD(&pool[i].list); + return; +} + +chunk *new_chunk() +{ + global_chunk_index++; + chunk_array[global_chunk_index].index = global_chunk_index; + return &chunk_array[global_chunk_index]; +} + +void *get_chunk(int req_size) +{ + int req_pool_index = roundup_size(req_size); // req_pool_index * MIN_CHUNK_SIZE = req_size + + if (list_empty(&pool[req_pool_index].list)) + { + // empty pool on req_size + int frame_index = get_page_from_free_list(MIN_PAGE_SIZE, req_pool_index); + split_page(frame_index, req_pool_index); + } + + int index = remove_a_chunk_from_pool(req_pool_index); + if (index == -1) + return NULL; + + void *addr = chunk_array[index].addr; + return addr; +} + +void split_page(int frame_index, int req_pool_index) +{ + int split_size = (req_pool_index * MIN_CHUNK_SIZE); + for (int i = 0; i < (MIN_PAGE_SIZE / split_size); i++) + { + chunk *new = new_chunk(); + new->size = split_size; + new->addr = (void *)frame_array[frame_index].addr + i *split_size; + new->val = FREE; + new->belong_page = frame_index; + list_add_tail(&new->list, &pool[req_pool_index].list); + } +} + +int remove_a_chunk_from_pool(int req_pool_index) +{ + chunk *alloc_chunk = container_of(pool[req_pool_index].list.next, chunk, list); + list_del_init(pool[req_pool_index].list.next); + alloc_chunk->val = ALLOCATED; + return alloc_chunk->index; +} + +void put_back_to_pool(int pool_index, int chunk_index) +{ + // addr to insert + struct list_head *node = &chunk_array[chunk_index].list; + unsigned long addr_long = (unsigned long)chunk_array[chunk_index].addr; + + // insert in by the order of addr + struct list_head *iter = &pool[pool_index].list; + struct list_head *start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + unsigned long iter_addr_long = (unsigned long)tmp->addr; + if (iter_addr_long > addr_long) + { + // list_insert() + iter->prev->next = node; + node->prev = iter->prev; + iter->prev = node; + node->next = iter; + + tmp->size = -1; + tmp->val = FREE; + + break; + } + } + + // check if there are free chunks in same page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + int count = 0; + int page_id = addr_long >> 12; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + count++; + else + break; + } + if (count == (MIN_PAGE_SIZE / (pool_index * MIN_CHUNK_SIZE))) + { + // There is a free page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + break; + } + chunk *first_chunk_in_page = list_entry(iter, chunk, list); + for (int i = 0; i < count; i++) + { + chunk *tmp = list_entry(iter, chunk, list); + tmp->val = FREE; + struct list_head *tmp_next = iter->next; + iter->prev->next = iter->next; + iter->next->prev = iter->prev; + iter->prev = iter; + iter->next = iter; + iter = tmp_next; + } + free_page_frame(first_chunk_in_page->belong_page); + } + + return; +} + +int free_chunk(int index) +{ + // free the page + int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; + put_back_to_pool(pool_index, index); + + return 0; +} + +void free(void *addr) +{ + // printf("[DEBUG] Freeing addr = %p\n", addr); + // Check addr is in which page frame + unsigned long addr_long = (unsigned long)addr; + int frame_index = (addr_long - FREE_MEM_START) / MIN_PAGE_SIZE; + + if (frame_array[frame_index].val != ALLOCATED) + { + printf("This page is Not Allocated yet\n"); + return; + } + + if (frame_array[frame_index].chunk_order != -1) + { + int chunk_index = -1; + // Find chunk_index + for (int i = 0; i < global_chunk_index; i++) + { + if (addr == chunk_array[i].addr) + { + chunk_index = i; + break; + } + } + // Check if is OK to free + if (chunk_index >= global_chunk_index || chunk_array[chunk_index].val != ALLOCATED) + { + if (chunk_index >= global_chunk_index) + printf("chunk_index is TOO high\n"); + if (chunk_array[chunk_index].val != ALLOCATED) + printf("chunk_index = %d\n", chunk_index); + printf("This chunk is Not Allocated yet\n"); + return; + } + + free_chunk(chunk_index); + return; + } + else + { + free_page_frame(frame_index); + return; + } +} + +int roundup_size(int size) +{ + switch (size) + { + case 1 ... 8: + return 1; + case 9 ... 16: + return 2; + case 17 ... 32: + return 4; + case 33 ... 48: + return 6; + case 49 ... 64: + return 8; + case 65 ... 128: + return 16; + case 129 ... 256: + return 32; + } + return 0; +} + +void debug_pool() +{ + printf("** DEBUGGING pool\n"); + for (int i = 0; i < 33; i++) + { + struct list_head *iter; + struct list_head *start; + iter = &pool[i].list; + start = &pool[i].list; + printf("pool[%d] -> ", i); + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + printf("addr %p -> ", tmp->addr); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING chunk\n"); + for (int i = 0; i < 20; i++) + { + printf("chunk_array[%d].index = %d\n", i, chunk_array[i].index); + printf("chunk_array[%d].size = %d\n", i, chunk_array[i].size); + printf("chunk_array[%d].addr = %p\n", i, chunk_array[i].addr); + printf("chunk_array[%d].val = %d\n", i, chunk_array[i].val); + printf("chunk_array[%d].belong_page= %d\n", i, chunk_array[i].belong_page); + printf("\n"); + } + printf("**\n"); +} \ No newline at end of file diff --git a/Lab6/src/kernel/exception.S b/Lab6/src/kernel/exception.S new file mode 100644 index 000000000..373463a08 --- /dev/null +++ b/Lab6/src/kernel/exception.S @@ -0,0 +1,155 @@ +#define CORE0_INTERRUPT_SOURCE 0x40000060 +#define IIR_ADDR 0x3f215048 + +#define PA(p) (p - 0xffff000000000000) + +.global set_el1_exception_vector_table +.global exception_vector_table +.global enable_interrupt +.global disable_interrupt + +enable_interrupt: + msr DAIFClr, 0xf + ret + +disable_interrupt: + msr DAIFSet, 0xf + ret + +// save general registers to stack +.macro save_all_sys + 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] + mrs x0, spsr_el1 + mrs x1, elr_el1 + mrs x2, sp_el0 + stp x30, x0, [sp ,16 * 15] + stp x1, x2, [sp ,16 * 16] +.endm + +// load general registers from stack +.macro load_all_sys + ldp x1, x2, [sp ,16 * 16] + ldp x30, x0, [sp ,16 * 15] + msr spsr_el1, x0 + msr elr_el1, x1 + msr sp_el0, x2 + 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, 32 * 9 +.endm + +.align 11 // vector table should be aligned to 0x800 +el1_exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_same_el + .align 7 + b exception_handler_irq_sp_elx_same_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_lower_el + .align 7 + b exception_handler_irq_sp_elx_lower_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_el1_exception_vector_table: + ldr x0, =el1_exception_vector_table + msr vbar_el1, x0 + ret lr + +exception_handler: + save_all_sys + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all_sys + eret + +exception_handler_sync_sp_elx_lower_el: + save_all_sys + mov x0, sp + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl el0_to_el1_sync_handler + load_all_sys + eret + +exception_handler_irq_sp_elx_lower_el: + b el0_core_timer_handler + +exception_handler_sync_sp_elx_same_el: + b exception_handler + +exception_handler_irq_sp_elx_same_el: + b el1_core_interrupt_handler + +el0_core_timer_handler: + save_all_sys + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all_sys + eret + +el1_core_interrupt_handler: + save_all_sys + bl el1_irq_interrupt_handler + load_all_sys + eret \ No newline at end of file diff --git a/Lab6/src/kernel/exception.c b/Lab6/src/kernel/exception.c new file mode 100644 index 000000000..94c527769 --- /dev/null +++ b/Lab6/src/kernel/exception.c @@ -0,0 +1,357 @@ +#include "peripherals/mini_uart.h" +#include "peripherals/irq.h" +#include "stdlib.h" +#include "timer.h" +#include "thread.h" +#include "syscall.h" + +extern task_struct *get_current(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern signal_handler signal_table[]; + +/** + * common exception handler + */ +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + // print out interruption type + switch (type) + { + case 0: + uart_send_string("Synchronous"); + break; + case 1: + uart_send_string("IRQ"); + break; + case 2: + uart_send_string("FIQ"); + break; + case 3: + uart_send_string("SError"); + break; + } + uart_send_string(": "); + // decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28) + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown"); + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE"); + break; + case 0b001110: + uart_send_string("Illegal execution"); + break; + case 0b010101: + uart_send_string("System call"); + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL"); + break; + case 0b100001: + uart_send_string("Instruction abort, same EL"); + break; + case 0b100010: + uart_send_string("Instruction alignment fault"); + break; + case 0b100100: + uart_send_string("Data abort, lower EL"); + break; + case 0b100101: + uart_send_string("Data abort, same EL"); + break; + case 0b100110: + uart_send_string("Stack alignment fault"); + break; + case 0b101100: + uart_send_string("Floating point"); + break; + default: + uart_send_string("Unknown"); + break; + } + // decode data abort cause + if (esr >> 26 == 0b100100 || esr >> 26 == 0b100101) + { + uart_send_string(", "); + switch ((esr >> 2) & 0x3) + { + case 0: + uart_send_string("Address size fault"); + break; + case 1: + uart_send_string("Translation fault"); + break; + case 2: + uart_send_string("Access flag fault"); + break; + case 3: + uart_send_string("Permission fault"); + break; + } + switch (esr & 0x3) + { + case 0: + uart_send_string(" at level 0"); + break; + case 1: + uart_send_string(" at level 1"); + break; + case 2: + uart_send_string(" at level 2"); + break; + case 3: + uart_send_string(" at level 3"); + break; + } + } + // dump registers + uart_send_string("\nSPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + + return; +} + +void el1_irq_interrupt_handler() +{ + unsigned int irq_basic_pending = get64(IRQ_BASIC_PENDING); + irq_basic_pending &= (1 << 19); // clear bits + + // GPU IRQ 57 : UART Interrupt + if (irq_basic_pending) + { + if (get64(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + { + uart_rx_handler(); + } + else if (get64(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + { + uart_tx_handler(); + } + } + // ARM Core Timer Interrupt + else if (get64(CORE0_INTR_SRC) & (1 << 1)) + { + long cntpct_el0, cntfrq_el0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + el1_timer_handler(cntpct_el0, cntfrq_el0); + } + + return; +} + +void el0_to_el1_sync_handler(unsigned long trapframe_addr, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown\n"); + return; + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE\n"); + return; + break; + case 0b001110: + uart_send_string("Illegal execution\n"); + return; + break; + case 0b010101: + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL\n"); + return; + break; + case 0b100001: + uart_send_string("Instruction abort, same EL\n"); + return; + break; + case 0b100010: + uart_send_string("Instruction alignment fault\n"); + return; + break; + case 0b100100: + uart_send_string("Data abort, lower EL\n"); + uart_send_string("SPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + return; + break; + case 0b100101: + uart_send_string("Data abort, same EL\n"); + return; + break; + case 0b100110: + uart_send_string("Stack alignment fault\n"); + return; + break; + case 0b101100: + uart_send_string("Floating point\n"); + return; + break; + default: + uart_send_string("Unknown\n"); + return; + break; + } + + int syscall_no; + trapframe *curr_trapframe = (trapframe *)trapframe_addr; + asm volatile("mov %0, x8" + : "=r"(syscall_no)::); + if (syscall_no == 0) + { + int pid = getpid(); + curr_trapframe->x[0] = pid; + } + else if (syscall_no == 1) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + disable_uart_irq(); + enable_interrupt(); + unsigned int ret = uart_read(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 2) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + unsigned int ret = uart_write(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 3) + { + char *name = (char *)curr_trapframe->x[0]; + char **argv = (char **)curr_trapframe->x[1]; + int ret = exec(name, argv); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 4) + { + task_struct *current = get_current(); + current->status = FORKING; + current->trapframe = trapframe_addr; + int ret = fork(); + current = get_current(); + curr_trapframe = (trapframe *)current->trapframe; + curr_trapframe->x[0] = ret; + // printf("\n[DEBUG]\n"); + // printf("cur task_struct = %p\n", current); + // printf("ret = %d\n", ret); + // printf("pid = %d\n", current->thread_info->id); + // printf("curr_trapframe->elr_el1 = %p\n", curr_trapframe->elr_el1); + // printf("ttbr0_el1 = %p\n", current->task_context.ttbr0_el1); + // printf("virtual_mem_translate curr_trapframe->elr_el1 = %p\n", virtual_mem_translate(curr_trapframe->elr_el1)); + // printf("\n"); + } + else if (syscall_no == 5) + { + int status = curr_trapframe->x[0]; + exit(status); + } + else if (syscall_no == 6) + { + unsigned char ch = (unsigned char)curr_trapframe->x[0]; + unsigned int *mbox_user = (unsigned int *)curr_trapframe->x[1]; + unsigned int *mbox_user_kernel_va = (unsigned int *)KERNEL_PA_TO_VA(virtual_mem_translate(mbox_user)); + printf("Pre mbox_user = %p\n", mbox_user); + printf("Pre mbox_user[1] = %lx\n", mbox_user[1]); + int ret = mbox_call_u(ch, mbox_user_kernel_va); + curr_trapframe->x[0] = ret; + printf("\n[DEBUG]\n"); + printf("current ttbr0_el1 = %p\n", get_current()->task_context.ttbr0_el1); + printf("mbox_user = %p\n", mbox_user); + printf("mbox_user[1] = %lx\n", mbox_user[1]); + printf("mbox_user_kernel_va = %p\n", mbox_user_kernel_va); + printf("mbox_user_kernel_va[1] = %x\n", mbox_user_kernel_va[1]); + printf("yes or no = %d\n", mbox_user_kernel_va[1] == 0x80000000); + printf("ret = %d\n", ret); + printf("\n"); + } + else if (syscall_no == 7) + { + int pid = (int)curr_trapframe->x[0]; + kill(pid); + } + else if (syscall_no == 8) + { + // signal + int SIGNAL = (int)curr_trapframe->x[0]; + void (*handler)() = (void (*)())curr_trapframe->x[1]; + signal(SIGNAL, handler); + } + else if (syscall_no == 9) + { + // signal kill + int pid = (int)curr_trapframe->x[0]; + int SIGNAL = (int)curr_trapframe->x[1]; + int if_cust = 0; + task_struct *current = get_current(); + + if (current->custom_signal) + { + custom_signal *cust_sig = current->custom_signal; + do + { + if (cust_sig->sig_num == SIGNAL) + { + if_cust = 1; + // signal's context save + sig_context_update(curr_trapframe, cust_sig->handler); + break; + } + cust_sig = container_of(cust_sig->list.next, custom_signal, list); + } while (cust_sig != current->custom_signal); + } + else if (!current->custom_signal && !if_cust) + (signal_table[SIGNAL])(pid); + } + else if (syscall_no == 10) + { + // signal restore + sig_context_restore(curr_trapframe); + + disable_interrupt(); + task_struct *current = get_current(); + free(current->signal_context->trapframe); + free(current->signal_context->user_stack); + free(current->signal_context); + current->signal_context = NULL; + enable_interrupt(); + } + else if (syscall_no == 11) + { + task_struct *current = get_current(); + if (current->thread_info->child_id == 0) + printf("child here, fork() return value : %d\n", current->thread_info->child_id); + else + printf("parent here, fork() return value : %d\n", current->thread_info->child_id); + } +} \ No newline at end of file diff --git a/Lab6/src/kernel/kernel.c b/Lab6/src/kernel/kernel.c new file mode 100644 index 000000000..9e2d2ddbe --- /dev/null +++ b/Lab6/src/kernel/kernel.c @@ -0,0 +1,26 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "reserve_mem.h" +#include "device_tree.h" +#include "thread.h" +#include "virtual_mem.h" + +#include "stdlib.h" + +extern void *_dtb_ptr; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + fdt_traverse(initramfs_callback, _dtb_ptr); + + memory_init(); + + thread_init(); + + shell_start(); +} diff --git a/Lab6/src/kernel/link.ld b/Lab6/src/kernel/link.ld new file mode 100644 index 000000000..4823bda12 --- /dev/null +++ b/Lab6/src/kernel/link.ld @@ -0,0 +1,24 @@ +_kernel_start = 0xffff000000080000; +_kernel_end = 0xffff000000c00000; + +SECTIONS +{ + . = _kernel_start; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + . = _kernel_end; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab6/src/kernel/mbox.c b/Lab6/src/kernel/mbox.c new file mode 100644 index 000000000..f6d91caa7 --- /dev/null +++ b/Lab6/src/kernel/mbox.c @@ -0,0 +1,63 @@ +#include "peripherals/mbox_call.h" +#include "mbox_call.h" +#include "mini_uart.h" + +void mbox_main() +{ + // get the board's unique serial number with a mailbox call + mbox[0] = 21 * 4; // length of the message + mbox[1] = MBOX_REQUEST; // this is a request message + + mbox[2] = MBOX_TAG_MODEL; + mbox[3] = 4; + mbox[4] = 0; // ?? + mbox[5] = 0; + + mbox[6] = MBOX_TAG_REVISION; + mbox[7] = 4; + mbox[8] = 0; // ?? + mbox[9] = 0; + + mbox[10] = MBOX_TAG_GETSERIAL; // get serial number command + mbox[11] = 8; // buffer size + mbox[12] = 8; // ?? + mbox[13] = 0; // clear output buffer + mbox[14] = 0; + + mbox[15] = MBOX_TAG_ARM_MEMORY; // get serial number command + mbox[16] = 8; // buffer size + mbox[17] = 8; // ?? + mbox[18] = 0; // clear output buffer + mbox[19] = 0; + + mbox[20] = MBOX_TAG_LAST; + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) + { + uart_send_string("Board model: "); + uart_hex(mbox[5]); + uart_send_string("\n"); + + uart_send_string("Board revision: "); + uart_hex(mbox[9]); + uart_send_string("\n"); + + uart_send_string("Serial number: "); + uart_hex(mbox[14]); + uart_hex(mbox[13]); + uart_send_string("\n"); + + uart_send_string("ARM memory base address: "); + uart_hex(mbox[18]); + uart_send_string("\n"); + + uart_send_string("ARM memory size: "); + uart_hex(mbox[19]); + uart_send_string("\n"); + } + else + { + uart_send_string("Unable to query serial!\n"); + } +} \ No newline at end of file diff --git a/Lab6/src/kernel/mbox_call.c b/Lab6/src/kernel/mbox_call.c new file mode 100644 index 000000000..d0626f0e8 --- /dev/null +++ b/Lab6/src/kernel/mbox_call.c @@ -0,0 +1,42 @@ +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" + +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) +{ + unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} \ No newline at end of file diff --git a/Lab6/src/kernel/my_signal.c b/Lab6/src/kernel/my_signal.c new file mode 100644 index 000000000..e50ee5e22 --- /dev/null +++ b/Lab6/src/kernel/my_signal.c @@ -0,0 +1,59 @@ +#include "thread.h" +#include "my_signal.h" +#include "syscall.h" +#include "page_alloc.h" +#include "stdlib.h" + +extern task_struct *get_current(); + +signal_handler signal_table[] = { + [0] = &sig_ignore, + [1] = &sig_ignore, + [2] = &sig_ignore, + [3] = &sig_ignore, + [4] = &sig_ignore, + [5] = &sig_ignore, + [6] = &sig_ignore, + [7] = &sig_ignore, + [8] = &sig_ignore, + [SIGKILL] = &sigkill_handler, +}; + +#define current get_current() + +void sig_ignore(int _) +{ + return; +} + +void sigkill_handler(int pid) +{ + kill(pid); + return; +} + +void sig_context_update(struct _trapframe *trapframe, void (*handler)()) +{ + signal_context *sig_context = (signal_context *)my_malloc(sizeof(signal_context)); + sig_context->trapframe = (struct _trapframe *)my_malloc(sizeof(struct _trapframe)); + sig_context->user_stack = my_malloc(MIN_PAGE_SIZE); + memcpy(sig_context->trapframe, trapframe, sizeof(struct _trapframe)); + + current->signal_context = sig_context; + + trapframe->x[30] = (unsigned long)&sig_return; + trapframe->elr_el1 = (unsigned long)handler; + trapframe->sp_el0 = (unsigned long)sig_context->user_stack + MIN_PAGE_SIZE; +} + +void sig_return(void) +{ + asm volatile( + "mov x8, 10\n" + "svc 0\n"); +} + +void sig_context_restore(struct _trapframe *trapframe) +{ + memcpy(trapframe, current->signal_context->trapframe, sizeof(struct _trapframe)); +} diff --git a/Lab6/src/kernel/page_alloc.c b/Lab6/src/kernel/page_alloc.c new file mode 100644 index 000000000..a7202e2bb --- /dev/null +++ b/Lab6/src/kernel/page_alloc.c @@ -0,0 +1,308 @@ +#include "page_alloc.h" +#include "math.h" +#include "stddef.h" +#include "stdlib.h" +#include "reserve_mem.h" +#include "dynamic_alloc.h" + +page_frame_node free_list[MAX_ORDER + 1]; +// int frame_array[TOTAL_NUM_PAGE]; // Why NOT use? no malloc to allocate new link list node +page_frame_node frame_array[TOTAL_NUM_PAGE]; +extern reserved_memory_block RMarray[100]; + +// initialize frame_array and free_list +void init_page_frame() +{ + // free_list + for (int i = 0; i <= MAX_ORDER; i++) + { + free_list[i].index = -1; // head of link list + free_list[i].next = NULL; + free_list[i].previous = NULL; + } + free_list[MAX_ORDER].next = &frame_array[0]; + + // frame_array + frame_array[0].index = 0; + frame_array[0].val = MAX_ORDER; + frame_array[0].addr = (void *)FREE_MEM_START; + frame_array[0].contiguous_head = -1; + frame_array[0].allocated_order = -1; + frame_array[0].next = &frame_array[0 + (1 << MAX_ORDER)]; + frame_array[0].previous = &free_list[MAX_ORDER]; + int previous_index = 0; + for (int i = 1; i < TOTAL_NUM_PAGE; i++) + { + frame_array[i].index = i; + frame_array[i].addr = (void *)FREE_MEM_START + i * MIN_PAGE_SIZE; + frame_array[i].contiguous_head = -1; + frame_array[i].allocated_order = -1; + if (i % (1 << MAX_ORDER) == 0) + { + frame_array[i].val = MAX_ORDER; + frame_array[i].next = NULL; + frame_array[i].previous = &frame_array[previous_index]; + frame_array[previous_index].next = &frame_array[i]; + previous_index = i; + } + else + { + frame_array[i].val = FREE_BUDDY; + frame_array[i].next = NULL; + frame_array[i].previous = NULL; + } + } + + return; +} + +void *my_malloc(int req_size) +{ + unsigned long ret = -1; + if (req_size < MAX_POOL_SIZE) + ret = (unsigned long)get_chunk(req_size); + else + ret = get_page_from_free_list(req_size, -1); + + if (ret == -1) + { + printf("my_malloc FAILED\n"); + return NULL; + } + else + { + if (req_size < MAX_POOL_SIZE) + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, (void *)ret); + return (void *)ret; + } + else + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, frame_array[ret].addr); + return frame_array[ret].addr; + } + } +} + +int get_page_from_free_list(int req_size, int who) +{ + int req_order = -1; + for (int i = 0; i <= MAX_ORDER; i++) + { + if (req_size <= MIN_PAGE_SIZE * pow(2, i)) + { + req_order = i; + break; + } + } + + int alloc_index = -1; + int alloc_order = req_order; + while (alloc_order <= MAX_ORDER) + { + if (free_list[alloc_order].next == NULL) // split high order + { + alloc_order++; + } + else + break; + } + if (alloc_order > MAX_ORDER) + return -1; + while (alloc_order > req_order) + { + // split high order + int removed_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + add_to_free_list(&free_list[alloc_order - 1], removed_index); + add_to_free_list(&free_list[alloc_order - 1], removed_index + pow(2, alloc_order - 1)); + frame_array[removed_index].val = alloc_order - 1; + frame_array[removed_index + (1 << (alloc_order - 1))].val = alloc_order - 1; + alloc_order--; + } + if (alloc_order != req_order) + return -1; + + // get require page + alloc_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + for (int i = 0; i < (1 << alloc_order); i++) + { + frame_array[alloc_index + i].val = ALLOCATED; + frame_array[alloc_index + i].next = NULL; + frame_array[alloc_index + i].previous = NULL; + frame_array[alloc_index + i].contiguous_head = alloc_index; + frame_array[alloc_index + i].allocated_order = alloc_order; + } + + // check the page if contains reserved memory + unsigned long start = (unsigned long)frame_array[alloc_index].addr; + unsigned long end = start + MIN_PAGE_SIZE * (1 << req_order); + int RM_index = check_contain_RM(start, end); + if (RM_index != 0) + { + // Need to change the page allocated + int new_alloc_index = get_page_from_free_list(req_size, who); + free_page_frame(alloc_index); + alloc_index = new_alloc_index; + } + +#ifdef DEBUG + debug(); +#endif + frame_array[alloc_index].chunk_order = who; + return alloc_index; +} + +// This does NOT modify frame_array value +void add_to_free_list(page_frame_node *head_node, int index) +{ + page_frame_node *iter = head_node; + while (iter->next != NULL) + iter = iter->next; + iter->next = &frame_array[index]; + frame_array[index].previous = iter; + frame_array[index].next = NULL; + + return; +} + +void remove_from_free_list(page_frame_node *node_to_be_removed) +{ + if (node_to_be_removed->next != NULL) + node_to_be_removed->next->previous = node_to_be_removed->previous; + node_to_be_removed->previous->next = node_to_be_removed->next; + + node_to_be_removed->next = NULL; + node_to_be_removed->previous = NULL; + + return; +} + +void put_back_to_free_list(int num_of_redundant_page, int index) // 從 index 開始有 num_of_redundant_page 個 free page +{ + int order_to_put = 0; + while (num_of_redundant_page >= (1 << order_to_put)) + order_to_put++; + order_to_put--; + add_to_free_list(&free_list[order_to_put], index); + frame_array[index].val = order_to_put; + if (num_of_redundant_page - (1 << order_to_put) != 0) + put_back_to_free_list(num_of_redundant_page - (1 << order_to_put), index + (1 << order_to_put)); + + return; +} + +int free_page_frame(int index) +{ + int contiguous_head = frame_array[index].contiguous_head; + if (contiguous_head != index) + { + printf("Please free the start page of this contiguous memory when allocated, which is index %d\n", contiguous_head); + return -1; + } + + // Check if buddy can merge + int allocated_order = frame_array[index].allocated_order; + int buddy_index = index ^ (1 << allocated_order); + if (frame_array[buddy_index].val == allocated_order) + { + // can merge + int merged_order = merge_buddy(&index, buddy_index, allocated_order); + if (buddy_index < index) + add_to_free_list(&free_list[merged_order], buddy_index); + else + add_to_free_list(&free_list[merged_order], index); + } + else + { + // can NOT merge + add_to_free_list(&free_list[allocated_order], index); + frame_array[index].val = allocated_order; + frame_array[index].contiguous_head = -1; + frame_array[index].allocated_order = -1; + for (int i = 1; i < (1 << allocated_order); i++) + { + frame_array[index + i].val = FREE_BUDDY; + frame_array[index + i].contiguous_head = -1; + frame_array[index + i].allocated_order = -1; + } + } + +#ifdef DEBUG + debug(); +#endif + + return 0; +} + +// return merged order, YES modify frame_array +int merge_buddy(int *index, int buddy, int order) +{ + // printf("[DEBUG] Merging buddy index = %d and buddy_index = %d, allocated_order = %d\n", *index, buddy, order); + if (order == MAX_ORDER) + return order; + + if (buddy < *index) + { + *index = buddy; + buddy = *index; // Find itself + } + + page_frame_node *iter = free_list[order].next; + while (iter->index != buddy) + iter = iter->next; + + remove_from_free_list(iter); + + frame_array[*index].val = order + 1; + for (int i = 1; i < (1 << (order + 1)); i++) + { + frame_array[i + *index].val = FREE_BUDDY; + } + + if (order + 1 == MAX_ORDER) + return order + 1; + + int new_buddy = *index ^ (1 << (order + 1)); + if (frame_array[*index].val == frame_array[new_buddy].val) + { + frame_array[buddy].val = FREE_BUDDY; + return merge_buddy(index, new_buddy, order + 1); + } + else + return order + 1; +} + +void debug() +{ + printf("** DEBUGGING free_list\n"); + for (int i = 0; i <= MAX_ORDER; i++) + { + page_frame_node *iter; + iter = &free_list[i]; + printf("free_list[%d] -> ", i); + while (iter->next != NULL) + { + iter = iter->next; + printf("index %d -> ", iter->index); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING frame_array\n"); + for (int i = 0; i < 20; i++) + { + printf("frame_array[%d].addr = %p\n", i, frame_array[i].addr); + printf("frame_array[%d].val = %d\n", i, frame_array[i].val); + printf("frame_array[%d].contiguous_head = %d\n", i, frame_array[i].contiguous_head); + printf("frame_array[%d].allocated_order = %d\n", i, frame_array[i].allocated_order); + if (frame_array[i].next != NULL) + printf("frame_array[%d].next->index = %d\n", i, frame_array[i].next->index); + if (frame_array[i].previous != NULL) + printf("frame_array[%d].previous->index = %d\n", i, frame_array[i].previous->index); + } + printf("**\n"); + + return; +} diff --git a/Lab6/src/kernel/read_cpio.c b/Lab6/src/kernel/read_cpio.c new file mode 100644 index 000000000..b278cda49 --- /dev/null +++ b/Lab6/src/kernel/read_cpio.c @@ -0,0 +1,162 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "virtual_mem.h" + +extern char *cpioDestGlobal; + +typedef struct cpio_newc_header +{ + 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]; +} __attribute__((packed)) cpio_t; + +void read_cpio(char *cpioDest) +{ + uart_send_string("Type Offset Size Access rights\tFilename\n"); + + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + // print out meta information + uart_hex(hex2int(header->c_mode, 8)); // mode (access rights + type) + uart_send(' '); + uart_hex((unsigned int)((unsigned long)cpioDest) + sizeof(cpio_t) + ns); + uart_send(' '); + uart_hex(fs); // file size in hex + uart_send(' '); + uart_hex(hex2int(header->c_uid, 8)); // user id in hex + uart_send('.'); + uart_hex(hex2int(header->c_gid, 8)); // group id in hex + uart_send('\t'); + uart_send_string(cpioDest + sizeof(cpio_t)); // filename + uart_send_string("\n"); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } +} + +void read_content(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("cat: %s: No such file\n", filename); + return; + } + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + // print content + uart_send_string_of_size((char *)cpioDest, fs); +} + +char *find_content_addr(char *cpioDest, const char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), (char *)filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("find_content_addr: %s: No such file\n", filename); + return NULL; + } + + return cpioDest; +} + +int load_userprogram(const char *filename, char *userDest) +{ + char *cpioUserPgmDest = cpioDestGlobal; + cpioUserPgmDest = find_content_addr(cpioUserPgmDest, filename); + if (cpioUserPgmDest == NULL) + { + uart_send_string("FAIL to find userprogram.img\n"); + return -1; + } + + // Found target file + cpio_t *header = (cpio_t *)cpioUserPgmDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioUserPgmDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioUserPgmDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + printf("load %p to %p (kernel va)\n", cpioUserPgmDest, userDest); + printf("size: %d bytes\n", fs); + + // load content + while (fs--) + { + *userDest++ = *cpioUserPgmDest++; + } + + if (fs == -1) + return 0; + + return 1; +} \ No newline at end of file diff --git a/Lab6/src/kernel/reboot.c b/Lab6/src/kernel/reboot.c new file mode 100644 index 000000000..00d3d2c6a --- /dev/null +++ b/Lab6/src/kernel/reboot.c @@ -0,0 +1,19 @@ +#include "peripherals/reboot.h" + +void set(long addr, unsigned int value) +{ + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) +{ // reboot after watchdog timer expire + set(PM_RSTC, (unsigned int)PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, (unsigned int)PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() +{ + set(PM_RSTC, (unsigned int)PM_PASSWORD | 0); // full reset + set(PM_WDOG, (unsigned int)PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab6/src/kernel/reserved_mem.c b/Lab6/src/kernel/reserved_mem.c new file mode 100644 index 000000000..db4e9c2af --- /dev/null +++ b/Lab6/src/kernel/reserved_mem.c @@ -0,0 +1,49 @@ +#include "reserve_mem.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "stdlib.h" +#include "virtual_mem.h" + +reserved_memory_block RMarray[100]; +int RMindex = 0; + +void memory_reserve(unsigned long start, unsigned long end, char *name) +{ + RMindex++; + RMarray[RMindex].start = start; + RMarray[RMindex].end = end; + strcpy(RMarray[RMindex].name, name); +} + +// return value : if including RM, return which no. of RM. Otherwise, return 0. +int check_contain_RM(unsigned long start, unsigned long end) +{ + for (int i = 1; i <= RMindex; i++) + { + if (RMarray[i].start <= start && start <= RMarray[i].end) + return i; + else if (RMarray[i].start <= end && end <= RMarray[i].end) + return i; + else if (start <= RMarray[i].start && RMarray[i].end <= end) + return i; + else + continue; + } + return 0; +} + +void memory_init() +{ + init_page_frame(); + init_pool(); + + memory_reserve(KERNEL_PA_TO_VA(0x0000), KERNEL_PA_TO_VA(0x4000), "kernel PGD, PUD"); + memory_reserve(KERNEL_PA_TO_VA(0x60000), KERNEL_PA_TO_VA(0x100000), "Kernel Img"); + memory_reserve(KERNEL_PA_TO_VA(0x1000000), KERNEL_PA_TO_VA(0x1000fff), "Printf Buffer"); + memory_reserve(KERNEL_PA_TO_VA(0x8000000), KERNEL_PA_TO_VA(0x8010000), "Initramfs"); + memory_reserve(KERNEL_PA_TO_VA(0x15000000), KERNEL_PA_TO_VA(0x17000000), "User Program"); + memory_reserve(KERNEL_PA_TO_VA(0x200000), KERNEL_PA_TO_VA(0x250000), "svc"); + + return; +} \ No newline at end of file diff --git a/Lab6/src/kernel/shell.c b/Lab6/src/kernel/shell.c new file mode 100644 index 000000000..3a8d4f13f --- /dev/null +++ b/Lab6/src/kernel/shell.c @@ -0,0 +1,214 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" +#include "timer.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "test.h" +#include "thread.h" + +extern void *_dtb_ptr; +extern char *cpioDestGlobal; +extern char read_buffer[100]; +// extern page_frame_node frame_array[TOTAL_NUM_PAGE]; +// extern chunk chunk_array[3000]; + +#define COMMAND_BUFFER 50 +#define FILENAME_BUFFER 20 +#define ARGV_BUFFER 30 + +void shell_start(); + +void shell_main(char *command) +{ + if (!strcmp(command, "help")) + { + uart_send_string("help\t: print this help menu\n"); + uart_send_string("hello\t: print Hello World!\n"); + uart_send_string("reboot\t: reboot the device\n"); + uart_send_string("ls\t: list information about files\n"); + uart_send_string("cat\t: copy each FILE to standard output\n"); + uart_send_string("dts\t: list devicetree\n"); + uart_send_string("asynr\t: [test] asynchronous read\n"); + uart_send_string("asynw\t: [test] asynchronous write\n"); + uart_send_string("setTimeout\t: Usage: setTimeout \n"); + uart_send_string("alloc\t: [test] malloc and free\n"); + uart_send_string("thread\t: [test]\n"); + uart_send_string("syscall\t: [test]\n"); + } + else if (!strcmp(command, "hello")) + { + uart_send_string("Hello World!\n"); + } + else if (!strcmp(command, "reboot")) + { + uart_send_string("Rebooting in 3 seconds\n"); + reset(3 << 16); + while (1) + ; + } + else if (!strcmp(command, "ls")) + { + read_cpio((char *)cpioDestGlobal); + } + else if (!memcmp(command, "cat", 3)) + { + if (command[3] != ' ' || command[4] == '\0') + { + printf("Usage: cat \n"); + return; + } + + char filename[FILENAME_BUFFER]; + memset(filename, '\0', FILENAME_BUFFER); + int i = 4; + while (command[i] != '\0') + { + filename[i - 4] = command[i]; + i++; + } + + read_content((char *)cpioDestGlobal, filename); + } + else if (!strcmp(command, "dts")) + { + fdt_traverse(dtb_parser, _dtb_ptr); + } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x1, 0x0;" // not sure why can't use x0, may have something to to with %0 + "msr spsr_el1, x1;" + "mov x2, %0;" + "add x2, x2, 12;" + "msr elr_el1, x2;" + "mov x2, 0x15000000;" + "msr sp_el0, x2;" + "mrs x2, cntfrq_el0;" + "add x2, x2, x2;" + "msr cntp_tval_el0, x2;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } + else if (!strcmp(command, "asynr")) + { + asyn_read(); + } + else if (!strcmp(command, "asynw")) + { + asyn_write(read_buffer); + uart_send('\n'); + } + else if (!memcmp(command, "setTimeout", 10)) + { + if (command[10] != ' ' || command[11] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char message[MESSAGE_BUFFER]; + memset(message, '\0', MESSAGE_BUFFER); + int i = 11; + while (command[i] != ' ' && command[i] != '\0') + { + message[i - 11] = command[i]; + i++; + } + + if (command[i] != ' ' || command[i] == '\0' || command[i + 1] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char seconds[SECONDS_BUFFER]; + memset(seconds, '\0', SECONDS_BUFFER); + while (command[i] != '\0') + { + seconds[i - strlen(message) - 1 - 11] = command[i]; + i++; + } + int sec; + sec = atoi(seconds); + + add_timer(sec, message); + } + else if (!memcmp(command, "alloc", 5)) + { + test_mem_alloc(); + } + else if (!memcmp(command, "debug", 5)) + { + debug(); + debug_pool(); + } + else if (!memcmp(command, "thread", 6)) + { + test_thread(); + } + else if (!strcmp(command, "syscall")) + { + thread_create(load_usrpgm_in_umode); + idle_task(); + } + + return; +} + +void shell_start() +{ + uart_send_string("Starting shell...\n"); + char c; + int i = 0; + + char command[COMMAND_BUFFER]; + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("$ "); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + command[i] = '\0'; + uart_send(c); + shell_main(command); + memset(command, '\0', COMMAND_BUFFER); + i = 0; + uart_send_string("$ "); + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else if (c == 127) // Also backspace but delete + { + } + else + { + if (i < COMMAND_BUFFER) + { + if (c == 0) // solve the problem that first command on board wont work + continue; + command[i++] = c; + } + uart_send(c); + } + } + } +} diff --git a/Lab6/src/kernel/syscall.c b/Lab6/src/kernel/syscall.c new file mode 100644 index 000000000..6146fa396 --- /dev/null +++ b/Lab6/src/kernel/syscall.c @@ -0,0 +1,191 @@ +#include "stdlib.h" +#include "thread.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "reserve_mem.h" +#include "read_cpio.h" +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" +#include "my_signal.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); +extern void switch_to(task_struct *, task_struct *); +extern task_struct kernel_thread; +extern struct list_head task_rq_head; +extern struct list_head task_zombieq_head; + +int getpid() +{ + int ret = get_current()->thread_info->id; + return ret; +} + +size_t uart_read(char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + buf[i] = uart_recv(); + if (buf[i] == '\n' || buf[i] == '\r') + return i; + } + return size; +} + +size_t uart_write(const char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + if (buf[i] == '\0') + return i; + uart_send(buf[i]); + } + return size; +} + +int exec(const char *name, char *const argv[]) +{ + task_struct *current = get_current(); + + // Change to kernel virtual address + current->usrpgm_load_addr = KERNEL_PA_TO_VA(virtual_mem_translate((void *)current->usrpgm_load_addr)); + current->ustack_start = KERNEL_PA_TO_VA(virtual_mem_translate((void *)current->ustack_start)); + + load_userprogram(name, (void *)current->usrpgm_load_addr); + + // Map custom virtual address to dynamic allocated address + // Note that my_malloc() return virtual (with kernel prefix), map_pages() remove it for physical + uint64_t *pgd = (uint64_t *)KERNEL_VA_TO_PA(new_page_table()); + map_pages(pgd, DEFAULT_THREAD_VA_CODE_START, (uint64_t)current->usrpgm_load_addr, USRPGM_SIZE / PAGE_SIZE); // map for code space + map_pages(pgd, DEFAULT_THREAD_VA_STACK_START, (uint64_t)current->ustack_start, 4); // map for stack + + // Use virtual address instead + current->usrpgm_load_addr = (unsigned long)DEFAULT_THREAD_VA_CODE_START; + current->ustack_start = (unsigned long)DEFAULT_THREAD_VA_STACK_START; + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE * 4; + + // Change ttbr0_el1 + current->task_context.ttbr0_el1 = (uint64_t)pgd; + write_gen_reg(x0, current->task_context.ttbr0_el1); + asm volatile("dsb ish"); // ensure write has completed + asm volatile("msr ttbr0_el1, x0"); // switch translation based address. + asm volatile("tlbi vmalle1is"); // invalidate all TLB entries + asm volatile("dsb ish"); // ensure completion of TLB invalidatation + asm volatile("isb"); // clear pipeline + + current->usrpgm_load_addr_pa = (unsigned long)virtual_mem_translate((void *)DEFAULT_THREAD_VA_CODE_START); + current->ustack_start_pa = (unsigned long)virtual_mem_translate((void *)DEFAULT_THREAD_VA_STACK_START); + + // printf("target_addr = %p\n", target_addr); + // printf("virtual_mem_translate(target_addr) = %p\n", virtual_mem_translate(target_addr)); + // printf("target_sp = %p\n", target_sp); + // printf("virtual_mem_translate(target_sp) = %p\n", virtual_mem_translate(target_sp)); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); + + return 0; +} + +int fork() +{ + schedule(); + return get_current()->thread_info->child_id; +} + +void exit(int status) +{ + task_struct *current = (task_struct *)get_current(); + current->status = ZOMBIE; + INIT_LIST_HEAD(¤t->list); + list_add_tail(¤t->list, &task_zombieq_head); + // schedule(); + switch_to(current, &kernel_thread); +} + +int mbox_call_u(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 (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} + +void kill(int pid) +{ + task_struct *tmp = get_current(); + if (tmp->thread_info->id == pid) + exit(0); + + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + tmp = container_of(iter, task_struct, list); + if (tmp->thread_info->id == pid) + { + tmp->status = ZOMBIE; + return; + } + } +} + +void signal(int SIGNAL, void (*handler)()) +{ + printf("[info] Called signal()\n"); + task_struct *cur = get_current(); + + custom_signal *new = (custom_signal *)my_malloc(sizeof(custom_signal)); + new->sig_num = SIGNAL; + new->handler = handler; + INIT_LIST_HEAD(&new->list); + + if (!cur->custom_signal) + cur->custom_signal = new; + else + list_add_tail(&cur->custom_signal->list, &new->list); +} \ No newline at end of file diff --git a/Lab6/src/kernel/test.c b/Lab6/src/kernel/test.c new file mode 100644 index 000000000..acafc75fd --- /dev/null +++ b/Lab6/src/kernel/test.c @@ -0,0 +1,120 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "thread.h" +#include "syscall.h" +#include "read_cpio.h" +#include "utils.h" +#include "timer.h" +#include "reserve_mem.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); + +void test_mem_alloc() +{ + void *a; + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + return; +} + +void foo() +{ + for (int i = 0; i < 10; ++i) + { + task_struct *cur = get_current(); + printf("Thread id: %d %d\n", cur->thread_info->id, i); + delay(1000000); + schedule(); + } +} + +void test_thread() +{ + int N = 5; + for (int i = 0; i < N; ++i) + { // N should > 2 + thread_create(foo); + } + + // thread_create(load_usrpgm_in_umode); + idle_task(); + + debug_task_rq(); + debug_task_zombieq(); + return; +} + +void load_usrpgm_in_umode() +{ + task_struct *current = get_current(); + + load_userprogram("vm.img", (void *)current->usrpgm_load_addr); + + // Map custom virtual address to dynamic allocated address + // Note that my_malloc() return virtual (with kernel prefix), map_pages() remove it for physical + uint64_t *pgd = (uint64_t *)KERNEL_VA_TO_PA(new_page_table()); + map_pages(pgd, DEFAULT_THREAD_VA_CODE_START, (uint64_t)current->usrpgm_load_addr, USRPGM_SIZE / PAGE_SIZE); // map for code space + map_pages(pgd, DEFAULT_THREAD_VA_STACK_START, (uint64_t)current->ustack_start, 4); // map for stack + + // Use virtual address instead + current->usrpgm_load_addr = (unsigned long)DEFAULT_THREAD_VA_CODE_START; + current->ustack_start = (unsigned long)DEFAULT_THREAD_VA_STACK_START; + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE * 4; + + // Change ttbr0_el1 + current->task_context.ttbr0_el1 = (uint64_t)pgd; + write_gen_reg(x0, current->task_context.ttbr0_el1); + asm volatile("dsb ish"); // ensure write has completed + asm volatile("msr ttbr0_el1, x0"); // switch translation based address. + asm volatile("tlbi vmalle1is"); // invalidate all TLB entries + asm volatile("dsb ish"); // ensure completion of TLB invalidatation + asm volatile("isb"); // clear pipeline + + current->usrpgm_load_addr_pa = (unsigned long)virtual_mem_translate((void *)DEFAULT_THREAD_VA_CODE_START); + current->ustack_start_pa = (unsigned long)virtual_mem_translate((void *)DEFAULT_THREAD_VA_STACK_START); + + // printf("target_addr = %p\n", target_addr); + // printf("virtual_mem_translate(target_addr) = %p\n", virtual_mem_translate(target_addr)); + // printf("target_sp = %p\n", target_sp); + // printf("virtual_mem_translate(target_sp) = %p\n", virtual_mem_translate(target_sp)); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} \ No newline at end of file diff --git a/Lab6/src/kernel/thread.S b/Lab6/src/kernel/thread.S new file mode 100644 index 000000000..e809a3f29 --- /dev/null +++ b/Lab6/src/kernel/thread.S @@ -0,0 +1,50 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] // curr->x19=x19, curr->x20=x20 + 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 // x9 is corruptible register, caller saved + str x9, [x0, 16 * 6] // curr->sp = sp + mrs x9, spsr_el1 // for debug purpose + str x9, [x0, 16 * 6 + 8] // for debug purpose + mrs x9, elr_el1 // for debug purpose + str x9, [x0, 16 * 7] // for debug purpose + mrs x9, esr_el1 // for debug purpose + str x9, [x0, 16 * 7 + 8] // for debug purpose + mrs x9, ttbr0_el1 + str x9, [x0, 16 * 8] + + ldp x19, x20, [x1, 16 * 0] // x19=next->x19=, x20=next->x20 + 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] // sp = next->sp + mov sp, x9 + ldr x9, [x1, 16 * 6 + 8] // for debug purpose + msr spsr_el1, x9 // for debug purpose + ldr x9, [x1, 16 * 7] // for debug purpose + msr elr_el1, x9 // for debug purpose + ldr x9, [x1, 16 * 7 + 8] // for debug purpose + msr esr_el1, x9 + ldr x9, [x1, 16 * 8] + dsb ish + msr ttbr0_el1, x9 // switch translation based address. + dsb ish + isb + msr tpidr_el1, x1 // tpidr_el1 = (void*) next, update current thread + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +.global kernel_thread_init +kernel_thread_init: + msr tpidr_el1, x0 + ret \ No newline at end of file diff --git a/Lab6/src/kernel/thread.c b/Lab6/src/kernel/thread.c new file mode 100644 index 000000000..2b6a5ce98 --- /dev/null +++ b/Lab6/src/kernel/thread.c @@ -0,0 +1,289 @@ +#include "stdlib.h" +#include "thread.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "list.h" +#include "syscall.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void switch_to(task_struct *, task_struct *); +extern void kernel_thread_init(); +extern unsigned long ttbr0_el1; + +long thread_cnt = 0; + +task_struct kernel_thread = {0}; +struct list_head task_rq_head; // run queue +struct list_head task_zombieq_head; // zombie queue + +void schedule() +{ + task_struct *cur = get_current(); + task_struct *next = del_rq(); + + if (next == NULL) + next = (void *)KERNEL_PA_TO_VA(&kernel_thread); + if (cur != (void *)KERNEL_PA_TO_VA(&kernel_thread)) + add_rq(cur); + + set_switch_timer(); + enable_interrupt(); + + if (next->status == FORKING) + { + add_rq(next); + // printf("[DEBUG] Context switch %d to kernel\n", cur->thread_info->id); + switch_to(cur, (void *)KERNEL_PA_TO_VA(&kernel_thread)); + } + else if (next->status == ZOMBIE) + { + INIT_LIST_HEAD(&next->list); + list_add_tail(&next->list, &task_zombieq_head); + // printf("[DEBUG] Context switch %d to kernel\n", cur->thread_info->id); + switch_to(cur, (void *)KERNEL_PA_TO_VA(&kernel_thread)); + } + else + { + // printf("[DEBUG] Context switch %d to %d\n", cur->thread_info->id, next->thread_info->id); + switch_to(cur, next); + } +} + +void add_rq(task_struct *task) +{ + INIT_LIST_HEAD(&task->list); + list_add_tail(&task->list, &task_rq_head); +} + +task_struct *del_rq() +{ + struct list_head *ret; + ret = task_rq_head.next; + if (ret != &task_rq_head) + { + list_del_init(ret); + return container_of(ret, task_struct, list); + } + else + return NULL; +} + +void thread_init() +{ + INIT_LIST_HEAD(&task_rq_head); + INIT_LIST_HEAD(&task_zombieq_head); + kernel_thread_init(KERNEL_PA_TO_VA(&kernel_thread)); + kernel_thread.thread_info = (thread_info *)my_malloc(sizeof(thread_info)); + kernel_thread.thread_info->id = -1; + return; +} + +thread_info *thread_create(func_ptr fp) +{ + task_struct *new_task = (task_struct *)my_malloc(sizeof(task_struct)); + thread_info *new_thread = (thread_info *)my_malloc(sizeof(thread_info)); + + new_task->thread_info = new_thread; + new_thread->task = new_task; + + new_task->kstack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE * 4); + new_task->ustack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE * 4); + new_task->usrpgm_load_addr = USRPGM_BASE + thread_cnt * USRPGM_SIZE; + + new_task->task_context.fp = new_task->kstack_start + MIN_PAGE_SIZE * 4; + new_task->task_context.lr = KERNEL_PA_TO_VA((unsigned long)task_wrapper); + new_task->task_context.sp = new_task->kstack_start + MIN_PAGE_SIZE * 4; + new_task->thread_info->id = thread_cnt++; + new_task->status = READY; + new_task->job = fp; + new_task->custom_signal = NULL; + new_task->task_context.ttbr0_el1 = read_sysreg(ttbr0_el1); + + add_rq(new_task); + + return new_thread; +} + +/* threads' routine for any task */ +void task_wrapper() +{ + task_struct *current = get_current(); + (current->job)(); + exit(0); +} + +void idle_task() +{ + while (!list_empty(&task_rq_head) || !list_empty(&task_zombieq_head)) + { + disable_interrupt(); + kill_zombies(); + do_fork(); + enable_interrupt(); + schedule(); + } +} + +/* kill the zombie threads and recycle their resources */ +void kill_zombies() +{ + struct list_head *iter = &task_zombieq_head; + struct list_head *start = &task_zombieq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + free((void *)tmp->kstack_start); + free((void *)tmp->ustack_start); + free(tmp->thread_info); + if (tmp->custom_signal) + free(tmp->custom_signal); + free(tmp); + } + INIT_LIST_HEAD(&task_zombieq_head); + return; +} + +void do_fork() +{ + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + if (tmp->status == FORKING) + { + printf("pid %d fork\n", tmp->thread_info->id); + create_child(tmp); + } + } +} + +/* copy all data including stack and program for child process and set the corresponding sp and lr for it*/ +void create_child(task_struct *parent) +{ + thread_info *child_thread = thread_create(0); + task_struct *child = child_thread->task; + + // Change to kernel virtual address + unsigned long parent_usrpgm_load_addr = KERNEL_PA_TO_VA(parent->usrpgm_load_addr_pa); + unsigned long parent_ustack_start = KERNEL_PA_TO_VA(parent->ustack_start_pa); + + char *parent_d, *child_d; + + parent->status = READY; + + parent->thread_info->child_id = child->thread_info->id; + child->thread_info->child_id = 0; + + // copy context + parent_d = (char *)&(parent->task_context); + child_d = (char *)&(child->task_context); + for (int i = 0; i < sizeof(context); i++) + child_d[i] = parent_d[i]; + + // copy custom_signal + if (parent->custom_signal) + { + child->custom_signal = (custom_signal *)my_malloc(sizeof(custom_signal)); + parent_d = (char *)&(parent->custom_signal); + child_d = (char *)&(child->custom_signal); + for (int i = 0; i < sizeof(custom_signal); i++) + child_d[i] = parent_d[i]; + } + + // copy kernel stack + parent_d = (char *)parent->kstack_start; + child_d = (char *)child->kstack_start; + for (int i = 0; i < MIN_PAGE_SIZE * 4; i++) + child_d[i] = parent_d[i]; + + // copy user stack + parent_d = (char *)parent_ustack_start; + child_d = (char *)child->ustack_start; + for (int i = 0; i < MIN_PAGE_SIZE * 4; i++) + child_d[i] = parent_d[i]; + + // copy user program + parent_d = (char *)parent_usrpgm_load_addr; + child_d = (char *)child->usrpgm_load_addr; + for (int i = 0; i < USRPGM_SIZE; i++) + child_d[i] = parent_d[i]; + + // set offset to child's stack + unsigned long kstack_offset = child->kstack_start - parent->kstack_start; + + // set child kernel space offset + child->task_context.fp += kstack_offset; + child->task_context.sp += kstack_offset; + child->trapframe = parent->trapframe + kstack_offset; + + // set child user space offset + // trapframe *ctrapframe = (trapframe *)child->trapframe; // because of data type problem + // printf("ctrapframe->x[29] = %p\n", ctrapframe->x[29]); + // printf("ctrapframe->sp_el0 = %p\n", ctrapframe->sp_el0); + // printf("ctrapframe->elr_el1 = %p\n", ctrapframe->elr_el1); + // ctrapframe->x[29] += ustack_offset; + // ctrapframe->sp_el0 += ustack_offset; + // ctrapframe->elr_el1 += usrpgm_offset; + + // Map custom virtual address to dynamic allocated address + // Note that my_malloc() return virtual (with kernel prefix), map_pages() remove it for physical + uint64_t *pgd = (uint64_t *)KERNEL_VA_TO_PA(new_page_table()); + map_pages(pgd, DEFAULT_THREAD_VA_CODE_START, (uint64_t)child->usrpgm_load_addr, USRPGM_SIZE / PAGE_SIZE); // map for code space + map_pages(pgd, DEFAULT_THREAD_VA_STACK_START, (uint64_t)child->ustack_start, 4); // map for stack + + // Use virtual address instead + child->usrpgm_load_addr_pa = KERNEL_VA_TO_PA(child->usrpgm_load_addr); + child->ustack_start_pa = KERNEL_VA_TO_PA(child->ustack_start); + child->usrpgm_load_addr = (unsigned long)DEFAULT_THREAD_VA_CODE_START; + child->ustack_start = (unsigned long)DEFAULT_THREAD_VA_STACK_START; + + // Change ttbr0_el1 + child->task_context.ttbr0_el1 = (uint64_t)pgd; + + return; +} + +void debug_task_rq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_rq_head; + start = &task_rq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_rq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} + +void debug_task_zombieq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_zombieq_head; + start = &task_zombieq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_zombieq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} \ No newline at end of file diff --git a/Lab6/src/kernel/timer.S b/Lab6/src/kernel/timer.S new file mode 100644 index 000000000..ffd869d9c --- /dev/null +++ b/Lab6/src/kernel/timer.S @@ -0,0 +1,32 @@ +#define CORE0_TIMER_IRQ_CTRL 0xffff000000000000 + 0x40000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + mrs x2, cntkctl_el1 // following three instructions for Lab5 to access cpu timer register + orr x2, x2, #0x1 + msr cntkctl_el1, x2 + ret + +.global core_timer_disable +core_timer_disable: + mov x0, 0 + msr cntp_ctl_el0, x0 // disable + ret + +.global set_switch_timer +set_switch_timer: + mrs x0, cntfrq_el0 + mov x0, x0, lsr#5 + msr cntp_tval_el0, x0 + ret + +/* +cntpct_el0: The timer’s current count. +cntp_cval_el0: A compared timer count. If cntpct_el0 >= cntp_cval_el0, interrupt the CPU core. +cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +*/ \ No newline at end of file diff --git a/Lab6/src/kernel/timer.c b/Lab6/src/kernel/timer.c new file mode 100644 index 000000000..74be207e2 --- /dev/null +++ b/Lab6/src/kernel/timer.c @@ -0,0 +1,177 @@ +#include "stdlib.h" +#include "timer.h" +#include "thread.h" + +extern void enable_interrupt(); +extern void disable_interrupt(); +extern task_struct *get_current(); + +typedef struct timer_queue_node +{ + long second_ticks; + char message[MESSAGE_BUFFER]; +} tq; + +tq timer_queue[10]; +int timer_queue_front = 0; +int timer_queue_back = 0; + +void get_current_time() +{ + long cntpct_el0, cntfrq_el0; + long cntp_tval_el0; + long cntp_cval_el0; + + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + "mrs %2, cntp_tval_el0;" + "mrs %3, cntp_cval_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0), "=r"(cntp_tval_el0), "=r"(cntp_cval_el0)); + + long nowtime = cntpct_el0 / cntfrq_el0; + + printf("%ld seconds after booting\n", nowtime); + + // printf("cntpct_el0 = %d\n", cntpct_el0); + // printf("cntfrq_el0 = %d\n", cntfrq_el0); + // printf("cntp_tval_el0 = %d\n", cntp_tval_el0); + // printf("cntp_cval_el0 = %d\n", cntp_cval_el0); + + return; +} + +void add_timer(int sec, char *mes) +{ + get_current_time(); + + for (int i = 0; i < MESSAGE_BUFFER; i++) + timer_queue[timer_queue_back].message[i] = mes[i]; + + // transfer sec to frq and store to node.second + asm volatile( + "msr DAIFSet, 0xf;" + "mrs x3, cntfrq_el0;" + "mrs x4, cntpct_el0;" + "mov x2, %1;" // after secs seconds later will interrupt + "mul x2, x2, x3;" + "add x2, x2, x4;" + "mov %0, x2;" + : "=r"(timer_queue[timer_queue_back].second_ticks) + : "r"(sec) + : "x0", "x1", "x2", "x3", "x4", "memory"); // Uses register operands x0, x1, x2, x3, and x4 and specifies that the instruction may modify memory using the clobber list "x0", "x1", "x2", "x3", "x4", "memory". + + timer_queue_back++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + + asm volatile( + "msr cntp_cval_el0, %0;" + "bl core_timer_enable;" + "msr DAIFClr, 0xf;" + : + : "r"(timer_queue[timer_queue_front].second_ticks)); +} + +void el0_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + disable_interrupt(); + schedule(); + enable_interrupt(); +} + +void el1_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + if (!is_timer_queue_empty()) + { + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + printf("\n"); + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + printf("Message: %s\n", timer_queue[timer_queue_front].message); + + timer_queue_front++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + asm volatile( + "msr cntp_cval_el0, %0\n\t" // set expired time + "bl core_timer_enable\n\t" + : + : "r"(timer_queue[timer_queue_front].second_ticks) + :); + } + else + { + disable_interrupt(); + schedule(); + enable_interrupt(); + } + return; +} + +int is_timer_queue_empty() +{ + return timer_queue_front == timer_queue_back ? 1 : 0; +} + +void timer_delay(long seconds) +{ + long cntpct_el0, cntfrq_el0, nowtime, due; + + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + + due = cntpct_el0 / cntfrq_el0 + seconds; + nowtime = cntpct_el0 / cntfrq_el0; + + while (nowtime <= due) + { + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + nowtime = cntpct_el0 / cntfrq_el0; + } +} \ No newline at end of file diff --git a/Lab6/src/kernel/virtual_mem.S b/Lab6/src/kernel/virtual_mem.S new file mode 100644 index 000000000..0921e45ce --- /dev/null +++ b/Lab6/src/kernel/virtual_mem.S @@ -0,0 +1,64 @@ +#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) + +#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_ACCESS (1 << 10) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + + +.global tcr_init +tcr_init: + ldr x0, = TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + ret + +.global mair_init +mair_init: + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 + ret + +.global identity_init +identity_init: + mov x0, 0 // PGD's page frame at 0x0 + mov x1, 0x1000 // PUD's page frame at 0x1000 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x0] + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + ret + +.global setup_identity_mapping +setup_identity_mapping: + mov x0, 0x1000 + msr ttbr0_el1, x0 + msr ttbr1_el1, x0 // also load PGD to the upper translation based register. + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 + ret \ No newline at end of file diff --git a/Lab6/src/kernel/virtual_mem.c b/Lab6/src/kernel/virtual_mem.c new file mode 100644 index 000000000..bacba429c --- /dev/null +++ b/Lab6/src/kernel/virtual_mem.c @@ -0,0 +1,113 @@ +#include "virtual_mem.h" +#include "stdlib.h" + +extern uint64_t par_el1; + +void setup_kernel_space_mapping() +{ + /* three-level 2MB block mapping */ + uint64_t *PGD = (uint64_t *)0x1000; + uint64_t *PUD = (uint64_t *)0x2000; // L1 table, entry points to L2 table or 1GB block + uint64_t *PMD = (uint64_t *)0x3000; // L2 table, entry points to L3 table or 2MB block + + /* + * Set Identity Paging + * 0x00000000 ~ 0x3f000000: Normal // PUD[0] + * 0x3f000000 ~ 0x40000000: Device // PUD[0] + * 0x40000000 ~ 0x80000000: Device // PUD[1] + * PGD[0] = (uint64_t)PUD | BOOT_PGD_ATTR + */ + + /* 1st entry points to a L1 table */ + PGD[0] = (uint64_t)PUD | PD_TABLE; + + /* 1st 1GB mapped by L2 table, where L2 table pointed by 1st entry of PUD */ + PUD[0] = (uint64_t)PMD | PD_TABLE; + /* 2nd 1GB mapped by the 2nd entry of PUD */ + PUD[1] = 0x40000000 | BOOT_PUD_ATTR; // 2nd 1GB mapped by the 2nd entry of PUD + + /* + * Note for following for: + * <<21 because each entry of PMD is 2MB=1<<21 + * 504 = 0x3f000000 / 2MB + * 512 = 0x40000000 / 2MB + */ + + // 0x00000000 ~ 0x3f000000: Normal + for (uint64_t i = 0; i < 504; i++) + PMD[i] = (i << 21) | PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_BLOCK; + + // 0x3f000000 ~ 0x40000000: Device + for (uint64_t i = 504; i < 512; i++) + PMD[i] = (i << 21) | PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK; +} + +uint64_t *new_page_table() +{ + uint64_t *table_addr = my_malloc(PAGE_SIZE); + memset(table_addr, 0, PAGE_SIZE); + return table_addr; +} + +void map_pages(uint64_t *pgd, uint64_t va_start, uint64_t pa_start, int num) +{ + if (pgd == NULL || pa_start == 0) + { + printf("[ERROR] map_pages(), pgd=0x%p pa_start=0x%p\r\n", pgd, (void *)pa_start); + return; + } + + int index[4]; // index of each table for L0~3 + uint64_t va = 0; + uint64_t *table = NULL; + uint64_t entry = 0; + pa_start = KERNEL_VA_TO_PA(pa_start); + for (int n = 0; n < num; ++n) + { + // Get index of each level + va = (uint64_t)(va_start + n * PAGE_SIZE); + va >>= 12; + index[3] = va & 0x1ff; + va >>= 9; + index[2] = va & 0x1ff; + va >>= 9; + index[1] = va & 0x1ff; + va >>= 9; + index[0] = va & 0x1ff; + table = (uint64_t *)KERNEL_PA_TO_VA((uint64_t)pgd); + entry = 0; + + // Find the address of level3 table + for (int lv = 0; lv < 3; lv++) + { // map lv0~2 + + // Allocate a table that table[index[lv]] can point to + if (table[index[lv]] == 0) + { + table[index[lv]] = PD_ACCESS | KERNEL_VA_TO_PA(new_page_table()) | PD_TABLE; + } + + // Remove attributes at low 12 bits + entry = CLEAR_LOW_12bit(table[index[lv]]); + + // Next level + table = (uint64_t *)KERNEL_PA_TO_VA(entry); // address of the first entry of next level table + } + + // leve3, aka PTE + if (table[index[3]] != 0) + printf("Warning, in map_pages(), PTE[%d]=%lx alread mapped\r\n", index[3], table[index[3]]); + table[index[3]] = (pa_start + n * PAGE_SIZE) | PD_ACCESS | PD_USER_RW | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_PAGE; + } +} + +void *virtual_mem_translate(void *virtual_addr) +{ + asm volatile("mov x4, %0\n" ::"r"(virtual_addr)); + asm volatile("at s1e0r, x4\n"); + uint64_t frame_addr = (uint64_t)read_sysreg(par_el1) & 0xFFFFFFFFF000; // physical frame address + uint64_t pa = frame_addr | ((uint64_t)virtual_addr & 0xFFF); // combine 12bits offset + if ((read_sysreg(par_el1) & 0x1) == 1) + printf("Error, virtual_mem_translate() failed\r\n"); + return (void *)pa; +} \ No newline at end of file diff --git a/Lab6/src/lib/hex2int.c b/Lab6/src/lib/hex2int.c new file mode 100644 index 000000000..4f30c1df8 --- /dev/null +++ b/Lab6/src/lib/hex2int.c @@ -0,0 +1,15 @@ +int hex2int(char *s, int n) +{ + int r = 0; + while (n-- > 0) + { + r <<= 4; + if (*s >= '0' && *s <= '9') + r += *s++ - '0'; + else if (*s >= 'A' && *s <= 'F') + r += *s++ - 'A' + 10; + else if (*s >= 'a' && *s <= 'f') + r += *s++ - 'a' + 10; + } + return r; +} \ No newline at end of file diff --git a/Lab6/src/lib/math.c b/Lab6/src/lib/math.c new file mode 100644 index 000000000..b96e3e335 --- /dev/null +++ b/Lab6/src/lib/math.c @@ -0,0 +1,9 @@ +int pow(int base, int exp) +{ + int result = 1; + for (int i = 0; i < exp; i++) + { + result *= base; + } + return result; +} \ No newline at end of file diff --git a/Lab6/src/lib/mem.c b/Lab6/src/lib/mem.c new file mode 100644 index 000000000..954ba9805 --- /dev/null +++ b/Lab6/src/lib/mem.c @@ -0,0 +1,33 @@ +#include + +void *memset(void *dest, register int val, int len) +{ + register unsigned char *ptr = (unsigned char *)dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +int memcmp(void *s1, void *s2, int n) +{ + unsigned char *a = s1, *b = s2; + while (n-- > 0) + { + if (*a != *b) + { + return *a - *b; + } + a++; + b++; + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} \ No newline at end of file diff --git a/Lab6/src/lib/mini_uart.c b/Lab6/src/lib/mini_uart.c new file mode 100644 index 000000000..e780073bf --- /dev/null +++ b/Lab6/src/lib/mini_uart.c @@ -0,0 +1,193 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "peripherals/irq.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; + +char read_buffer[100]; +char write_buffer[100]; +int len_WB = 0; +int len_RB = 0; +int buffer_count = 0; + +void uart_send(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + + if (c != '\x7f') + put32(AUX_MU_IO_REG, c); + + if (c == '\n') + uart_send('\r'); +} + +char uart_recv(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r == '\r' ? '\n' : r; +} + +void uart_send_string(char *str) +{ + while (*str) + uart_send(*str++); +} + +void uart_send_space(int size) +{ + while (size--) + uart_send(' '); +} + +void uart_send_string_of_size(char *str, int size) +{ + while (size--) + uart_send(*str++); +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) +{ + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) + { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_init(void) +{ + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 2 << 12; // set alt5 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 2 << 15; // set alt5 for gpio15 + put32(GPFSEL1, selector); + + put32(GPPUD, 0); + delay(150); // spec + put32(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + put32(GPPUDCLK0, 0); + + put32(AUX_ENABLES, 1); // Enable mini uart (this also enables access to its registers) + put32(AUX_MU_CNTL_REG, 0); // Disable auto flow control and disable receiver and transmitter (for now) + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + put32(AUX_MU_LCR_REG, 3); // Enable 8 bit mode + put32(AUX_MU_MCR_REG, 0); // Set RTS line to be always high + put32(AUX_MU_BAUD_REG, 270); // Set baud rate to 115200 + + put32(AUX_MU_CNTL_REG, 3); // Finally, enable transmitter and receiver +} + +void _putchar(char character) +{ + uart_send(character); +} + +/** + * Display a string + */ +// void printf(char *fmt, ...) +// { +// __builtin_va_list args; +// __builtin_va_start(args, fmt); +// // we don't have memory allocation yet, so we +// // simply place our string after our code +// char *s = (char *)&_end; +// // use sprintf to format our string +// vsprintf(s, fmt, args); +// // print out as usual +// while (*s) +// { +// /* convert newline to carrige return + newline */ +// if (*s == '\n') +// uart_send('\r'); +// uart_send(*s++); +// } +// } + +void asyn_read() +{ + memset(read_buffer, '\0', 100); + + put32(AUX_MU_IER_REG, 1); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (read_buffer[strlen(read_buffer) - 1] != '\r') + ; +} + +void asyn_write(char *str) +{ + strcpy(write_buffer, str); + len_WB = strlen(write_buffer); + + put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (strlen(write_buffer) != 0) + ; +} + +void uart_rx_handler() +{ + read_buffer[len_RB++] = get32(AUX_MU_IO_REG); + + if (read_buffer[len_RB - 1] == '\r') + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + read_buffer[len_RB] = '\0'; + len_RB = 0; + } +} + +void uart_tx_handler() +{ + if (buffer_count < len_WB) + put32(AUX_MU_IO_REG, write_buffer[buffer_count++]); + else + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + buffer_count = 0; + memset(write_buffer, '\0', 100); + } +} + +void enable_uart_irq() +{ + put32(ENABLE_IRQS_1, 1 << 29); +} + +void disable_uart_irq() +{ + put32(DISABLE_IRQS_1, 1 << 29); +} \ No newline at end of file diff --git a/Lab6/src/lib/mm.S b/Lab6/src/lib/mm.S new file mode 100644 index 000000000..1bd32ff37 --- /dev/null +++ b/Lab6/src/lib/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/Lab6/src/lib/printf.c b/Lab6/src/lib/printf.c new file mode 100644 index 000000000..3eabd6eca --- /dev/null +++ b/Lab6/src/lib/printf.c @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "printf.h" + +#define PRINTF_DISABLE_SUPPORT_FLOAT + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct +{ + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) + { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)buffer; + (void)idx; + (void)maxlen; + if (character) + { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)idx; + (void)maxlen; + if (character) + { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) +{ + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) + { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (size_t i = len; i < width; i++) + { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) + { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) + { + len--; + if (len && (base == 16U)) + { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) + { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) + { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) + { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) + { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) + { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) + { + } + else if ((frac == 0U) || (frac & 1U)) + { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) + { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) + { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) + { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) + { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union + { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) + { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) + { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) + { + if ((int)prec > expval) + { + prec = (unsigned)((int)prec - expval - 1); + } + else + { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) + { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) + { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else + { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) + { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) + { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) + { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else + { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do + { + switch (*format) + { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) + { + width = _atoi(&format); + } + else if (*format == '*') + { + const int w = va_arg(va, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else + { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) + { + precision = _atoi(&format); + } + else if (*format == '*') + { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) + { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') + { + base = 16U; + } + else if (*format == 'o') + { + base = 8U; + } + else if (*format == 'b') + { + base = 2U; + } + else + { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) + { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) + { + // signed + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else + { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else + { + // unsigned + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': + { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': + { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) + { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': + { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) + { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } + else + { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char *buffer, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} \ No newline at end of file diff --git a/Lab6/src/lib/queue.c b/Lab6/src/lib/queue.c new file mode 100644 index 000000000..6b56b4dca --- /dev/null +++ b/Lab6/src/lib/queue.c @@ -0,0 +1,7 @@ +void queue_push(char c) +{ +} + +void queue_pop() +{ +} \ No newline at end of file diff --git a/Lab6/src/lib/simple_malloc.c b/Lab6/src/lib/simple_malloc.c new file mode 100644 index 000000000..b7a80a897 --- /dev/null +++ b/Lab6/src/lib/simple_malloc.c @@ -0,0 +1,8 @@ +char *counter = (char *)0x10000000; + +void *simple_malloc(unsigned int size) +{ + char *dest = counter; + counter += size; + return dest; +} diff --git a/Lab6/src/lib/string.c b/Lab6/src/lib/string.c new file mode 100644 index 000000000..be47302ee --- /dev/null +++ b/Lab6/src/lib/string.c @@ -0,0 +1,79 @@ +#include + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + unsigned char c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + break; + if (c1 == '\0') + return 0; + } + + return c1 - c2; +} + +int strlen(const char *str) +{ + const unsigned char *s = (const unsigned char *)str; + unsigned int len = 0; + + while (1) + { + if (*s++ == '\0') + return len; + len++; + } +} + +char *strcpy(char *destination, const char *source) +{ + // return if no memory is allocated to the destination + if (destination == NULL) + { + return NULL; + } + + // take a pointer pointing to the beginning of the destination string + char *ptr = destination; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + + // include the terminating null character + *destination = '\0'; + + // the destination is returned by standard `strcpy()` + return ptr; +} + +// A simple atoi() function +int atoi(char *str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + res = res * 10 + str[i] - '0'; + + // return result. + return res; +} diff --git a/Lab6/src/lib/utils.S b/Lab6/src/lib/utils.S new file mode 100644 index 000000000..583dd1288 --- /dev/null +++ b/Lab6/src/lib/utils.S @@ -0,0 +1,25 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl put64 +put64: + str x1,[x0] + ret + +.globl get64 +get64: + ldr x0,[x0] + ret diff --git a/Lab6/src/userprogram/link.ld b/Lab6/src/userprogram/link.ld new file mode 100644 index 000000000..d84040b38 --- /dev/null +++ b/Lab6/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x15000000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab6/src/userprogram/user.S b/Lab6/src/userprogram/user.S new file mode 100644 index 000000000..6b0a2824a --- /dev/null +++ b/Lab6/src/userprogram/user.S @@ -0,0 +1,22 @@ +.section ".text.boot" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + mov x8, #0 + svc 0 + mov x8, #4 + svc 0 + mov x8, #9 + svc 0 + mov x8, #5 + svc 0 + blt 1b +1: + b 1b + +get_pid: + mov x8, #0 + svc 0 + ret \ No newline at end of file diff --git a/Lab6/userprogram.img b/Lab6/userprogram.img new file mode 100755 index 000000000..4d9085ed0 Binary files /dev/null and b/Lab6/userprogram.img differ diff --git a/Lab7/Makefile b/Lab7/Makefile new file mode 100644 index 000000000..abe49536e --- /dev/null +++ b/Lab7/Makefile @@ -0,0 +1,62 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only +CFLAGS = -Wall -O -ffreestanding -nostdlib -nostartfiles -g -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.img userprogram.img + +clean : + rm -rf $(BUILD_DIR) *.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + @mkdir -p $(@D) + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/kernel/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/kernel/link.ld -o $(BUILD_DIR)/kernel/kernel8.elf $(filter $(BUILD_DIR)/kernel/%_c.o $(BUILD_DIR)/kernel/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel/kernel8.elf -O binary kernel8.img + +int_qemu: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -d int + +bootloader.img: $(SRC_DIR)/bootloader/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/bootloader/link.ld -o $(BUILD_DIR)/bootloader/bootloader.elf $(filter $(BUILD_DIR)/bootloader/%_c.o $(BUILD_DIR)/bootloader/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/bootloader/bootloader.elf bootloader.img + +userprogram.img: $(SRC_DIR)/userprogram/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/userprogram/link.ld -o $(BUILD_DIR)/userprogram/userprogram.elf $(filter $(BUILD_DIR)/userprogram/%_c.o $(BUILD_DIR)/userprogram/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/userprogram/userprogram.elf userprogram.img + +test: + @echo Hello + +pseudoTTY: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug_boot: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -s -S + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb diff --git a/Lab7/bcm2710-rpi-3-b-plus.dtb b/Lab7/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..0dd0e2f43 Binary files /dev/null and b/Lab7/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab7/bootloader.img b/Lab7/bootloader.img new file mode 100755 index 000000000..3d60319b0 Binary files /dev/null and b/Lab7/bootloader.img differ diff --git a/Lab7/bootloader.py b/Lab7/bootloader.py new file mode 100644 index 000000000..1b99fa7d7 --- /dev/null +++ b/Lab7/bootloader.py @@ -0,0 +1,25 @@ +import os +import time + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +# with open('/dev/pts/35', "wb", buffering=0) as tty: +with open('/dev/ttyUSB0', "wb", buffering=0) as tty: + # send Start + tty.write(b"Start") + time.sleep(1) + + # send kernel size + tty.write(file_size.to_bytes(4, 'little')) + time.sleep(1) + + # send kernel + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + tty.write(data) + time.sleep(1) + diff --git a/Lab7/include/devfs.h b/Lab7/include/devfs.h new file mode 100644 index 000000000..d450ca06c --- /dev/null +++ b/Lab7/include/devfs.h @@ -0,0 +1,21 @@ +#ifndef __DEVFS_H_ +#define __DEVFS_H_ + +#include "vfs.h" +extern struct filesystem devfs; + +// fops +int devfs_write(struct file *file, void *buf, size_t len); +int devfs_read(struct file *file, void *buf, size_t len); +int devfs_open(struct vnode *file_node, struct file **target); +int devfs_close(struct file *file); +long devfs_lseek(struct file *file, long offset, int whence); + +// vops +int devfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); +int devfs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int devfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); + +int devfs_setup_mount(struct filesystem *fs, struct mount *mount); + +#endif // __DEVFS_H_ \ No newline at end of file diff --git a/Lab7/include/device_tree.h b/Lab7/include/device_tree.h new file mode 100644 index 000000000..e3e6aee78 --- /dev/null +++ b/Lab7/include/device_tree.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_TREE_H +#define _DEVICE_TREE_H + +typedef int (*fdt_callback)(void *); +int initramfs_callback(void *dtb); +int dtb_parser(void *dtb); +void fdt_traverse(fdt_callback cb, char *dtb); + +#endif /*_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab7/include/dynamic_alloc.h b/Lab7/include/dynamic_alloc.h new file mode 100644 index 000000000..55eb68307 --- /dev/null +++ b/Lab7/include/dynamic_alloc.h @@ -0,0 +1,35 @@ +#ifndef _DYNAMIC_ALLOC_H +#define _DYNAMIC_ALLOC_H + +#include "list.h" + +#define MAX_POOL_SIZE 256 +#define MIN_CHUNK_SIZE 8 +#define FREE 98 + +typedef struct _chunk +{ + int index; // const + int size; + void *addr; // const + int val; + int belong_page; + struct list_head list; +} chunk; + +typedef struct _pool_list +{ + struct list_head list; +} pool_list; + +void init_pool(); +void *get_chunk(int req_size); +int roundup_size(int size); +void split_page(int frame_index, int req_pool_index); +int remove_a_chunk_from_pool(int req_pool_index); +int free_chunk(int index); +void put_back_to_pool(int pool_index, int chunk_index); +void debug_pool(); +void free(void *addr); + +#endif /*_DYNAMIC_ALLOC_H */ diff --git a/Lab7/include/list.h b/Lab7/include/list.h new file mode 100644 index 000000000..a0fb29587 --- /dev/null +++ b/Lab7/include/list.h @@ -0,0 +1,441 @@ +/* Linux-like double-linked list implementation */ + +#ifndef SYSPROG21_LIST_H +#define SYSPROG21_LIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *)0)->member) *__pmember = (ptr); \ + (type *)((char *)__pmember - offsetof(type, member)); \ + }) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr)-offsetof(type, member))) +#endif +#endif + + /** + * struct list_head - Head and node of a double-linked list + * @prev: pointer to the previous node in the list + * @next: pointer to the next node in the list + * + * The simple double-linked list consists of a head and nodes attached to + * this head. Both node and head share the same struct type. The list_* + * functions and macros can be used to access and modify this data structure. + * + * The @prev pointer of the list head points to the last list node of the + * list and @next points to the first list node of the list. For an empty list, + * both member variables point to the head. + * + * The list nodes are usually embedded in a container structure which holds the + * actual data. Such an container object is called entry. The helper list_entry + * can be used to calculate the object address from the address of the node. + */ + struct list_head + { + struct list_head *prev; + struct list_head *next; + }; + +/** + * LIST_HEAD - Declare list head and initialize it + * @head: name of the new object + */ +#define LIST_HEAD(head) struct list_head head = {&(head), &(head)} + + /** + * INIT_LIST_HEAD() - Initialize empty list head + * @head: pointer to list head + * + * This can also be used to initialize a unlinked list node. + * + * A node is usually linked inside a list, will be added to a list in + * the near future or the entry containing the node will be free'd soon. + * + * But an unlinked node may be given to a function which uses list_del(_init) + * before it ends up in a previously mentioned state. The list_del(_init) on an + * initialized node is well defined and safe. But the result of a + * list_del(_init) on an uninitialized node is undefined (unrelated memory is + * modified, crashes, ...). + */ + static inline void INIT_LIST_HEAD(struct list_head *head) + { + head->next = head; + head->prev = head; + } + + /** + * list_add() - Add a list node to the beginning of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add(struct list_head *node, struct list_head *head) + { + struct list_head *next = head->next; + + next->prev = node; + node->next = next; + node->prev = head; + head->next = node; + } + + /** + * list_add_tail() - Add a list node to the end of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add_tail(struct list_head *node, struct list_head *head) + { + struct list_head *prev = head->prev; + + prev->next = node; + node->next = head; + node->prev = prev; + head->prev = node; + } + + /** + * list_del() - Remove a list node from the list + * @node: pointer to the node + * + * The node is only removed from the list. Neither the memory of the removed + * node nor the memory of the entry containing the node is free'd. The node + * has to be handled like an uninitialized node. Accessing the next or prev + * pointer of the node is not safe. + * + * Unlinked, initialized nodes are also uninitialized after list_del. + * + * LIST_POISONING can be enabled during build-time to provoke an invalid memory + * access when the memory behind the next/prev pointer is used after a list_del. + * This only works on systems which prohibit access to the predefined memory + * addresses. + */ + static inline void list_del(struct list_head *node) + { + struct list_head *next = node->next; + struct list_head *prev = node->prev; + + next->prev = prev; + prev->next = next; + +#ifdef LIST_POISONING + node->prev = (struct list_head *)(0x00100100); + node->next = (struct list_head *)(0x00200200); +#endif + } + + /** + * list_del_init() - Remove a list node from the list and reinitialize it + * @node: pointer to the node + * + * The removed node will not end up in an uninitialized state like when using + * list_del. Instead the node is initialized again to the unlinked state. + */ + static inline void list_del_init(struct list_head *node) + { + list_del(node); + INIT_LIST_HEAD(node); + } + + /** + * list_empty() - Check if list head has no nodes attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not empty !0 - list is empty + */ + static inline int list_empty(const struct list_head *head) + { + return (head->next == head); + } + + /** + * list_is_singular() - Check if list head has exactly one node attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not singular !0 -list has exactly one entry + */ + static inline int list_is_singular(const struct list_head *head) + { + return (!list_empty(head) && head->prev == head->next); + } + + /** + * list_splice() - Add list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice(struct list_head *list, struct list_head *head) + { + struct list_head *head_first = head->next; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->next = list_first; + list_first->prev = head; + + list_last->next = head_first; + head_first->prev = list_last; + } + + /** + * list_splice_tail() - Add list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice_tail(struct list_head *list, + struct list_head *head) + { + struct list_head *head_last = head->prev; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->prev = list_last; + list_last->next = head; + + list_first->prev = head_last; + head_last->next = list_first; + } + + /** + * list_splice_init() - Move list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_init(struct list_head *list, + struct list_head *head) + { + list_splice(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_splice_tail_init() - Move list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) + { + list_splice_tail(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_cut_position() - Move beginning of a list to another list + * @head_to: pointer to the head of the list which receives nodes + * @head_from: pointer to the head of the list + * @node: pointer to the node in which defines the cutting point + * + * All entries from the beginning of the list @head_from to (including) the + * @node is moved to @head_to. + * + * @head_to is replaced when @head_from is not empty. @node must be a real + * list node from @head_from or the behavior is undefined. + */ + static inline void list_cut_position(struct list_head *head_to, + struct list_head *head_from, + struct list_head *node) + { + struct list_head *head_from_first = head_from->next; + + if (list_empty(head_from)) + return; + + if (head_from == node) + { + INIT_LIST_HEAD(head_to); + return; + } + + head_from->next = node->next; + head_from->next->prev = head_from; + + head_to->prev = node; + node->next = head_to; + head_to->next = head_from_first; + head_to->next->prev = head_to; + } + + /** + * list_move() - Move a list node to the beginning of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the beginning of + * @head + */ + static inline void list_move(struct list_head *node, struct list_head *head) + { + list_del(node); + list_add(node, head); + } + + /** + * list_move_tail() - Move a list node to the end of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the end of @head + */ + static inline void list_move_tail(struct list_head *node, + struct list_head *head) + { + list_del(node); + list_add_tail(node, head); + } + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_last_entry() - get last entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of last entry in list + */ +#define list_last_entry(head, type, member) \ + list_entry((head)->prev, type, member) + +/** + * list_for_each - iterate over list nodes + * @node: list_head pointer used as iterator + * @head: pointer to the head of the list + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + */ +#define list_for_each(node, head) \ + for (node = (head)->next; node != (head); node = node->next) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif + +/** + * list_for_each_safe - iterate over list nodes and allow deletes + * @node: list_head pointer used as iterator + * @safe: list_head pointer used to store info for next entry in list + * @head: pointer to the head of the list + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + */ +#define list_for_each_safe(node, safe, head) \ + for (node = (head)->next, safe = node->next; node != (head); \ + node = safe, safe = node->next) + +/** + * list_for_each_entry_safe - iterate over list entries and allow deletes + * @entry: pointer used as iterator + * @safe: @type pointer used to store info for next entry in list + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#define list_for_each_entry_safe(entry, safe, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member), \ + safe = list_entry(entry->member.next, __typeof__(*entry), member); \ + &entry->member != (head); entry = safe, \ + safe = list_entry(safe->member.next, __typeof__(*entry), member)) + +#undef __LIST_HAVE_TYPEOF + +#ifdef __cplusplus +} +#endif + +#endif /* SYSPROG21_LIST_H */ \ No newline at end of file diff --git a/Lab7/include/load_kernel.h b/Lab7/include/load_kernel.h new file mode 100644 index 000000000..a5dc68ffb --- /dev/null +++ b/Lab7/include/load_kernel.h @@ -0,0 +1,7 @@ +#ifndef _LOAD_KERNEL_H +#define _LOAD_KERNEL_H + +void load_kernel(char *dest); +void relocate(char *from_dest, char *to_dest); + +#endif /*_LOAD_KERNEL_H */ diff --git a/Lab7/include/math.h b/Lab7/include/math.h new file mode 100644 index 000000000..3107a97d8 --- /dev/null +++ b/Lab7/include/math.h @@ -0,0 +1 @@ +int pow(int base, int exp); \ No newline at end of file diff --git a/Lab7/include/mbox.h b/Lab7/include/mbox.h new file mode 100644 index 000000000..b60f6738d --- /dev/null +++ b/Lab7/include/mbox.h @@ -0,0 +1,7 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); +char *framebuffer_init(); + +#endif /*_MBOX_H */ diff --git a/Lab7/include/mbox_call.h b/Lab7/include/mbox_call.h new file mode 100644 index 000000000..7f60645d0 --- /dev/null +++ b/Lab7/include/mbox_call.h @@ -0,0 +1,9 @@ +#ifndef _MBOX_CALL_H +#define _MBOX_CALL_H + +/* a properly aligned buffer */ +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch); + +#endif /*_MBOX_CALL_H */ diff --git a/Lab7/include/mini_uart.h b/Lab7/include/mini_uart.h new file mode 100644 index 000000000..27ae3e20b --- /dev/null +++ b/Lab7/include/mini_uart.h @@ -0,0 +1,23 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void uart_send_string_of_size(char *str, int size); +void uart_hex(unsigned int d); +void uart_send_space(int size); + +void uart_send_byte(char c); +char uart_recv_byte(void); + +void asyn_read(); +void asyn_write(); +void uart_rx_handler(); +void uart_tx_handler(); + +void enable_uart_irq(); +void disable_uart_irq(); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab7/include/mm.h b/Lab7/include/mm.h new file mode 100644 index 000000000..1d947fb75 --- /dev/null +++ b/Lab7/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (6 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/Lab7/include/my_signal.h b/Lab7/include/my_signal.h new file mode 100644 index 000000000..d3f998486 --- /dev/null +++ b/Lab7/include/my_signal.h @@ -0,0 +1,30 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include "list.h" +#include "thread.h" + +#define SIGKILL 9 +#define SIG_NUM (sizeof(signal_table) / sizeof(signal_table[0])) +typedef void (*signal_handler)(int); + +typedef struct _custom_signal +{ + unsigned int sig_num; + signal_handler handler; + struct list_head list; +} custom_signal; + +typedef struct _signal_context +{ + struct _trapframe *trapframe; + char *user_stack; +} signal_context; + +void sig_ignore(int _); +void sigkill_handler(int pid); +void sig_context_update(struct _trapframe *trapframe, void (*handler)()); +void sig_return(void); +void sig_context_restore(struct _trapframe *trapframe); + +#endif /*_SIGNAL_H */ diff --git a/Lab7/include/page_alloc.h b/Lab7/include/page_alloc.h new file mode 100644 index 000000000..0a191e1fe --- /dev/null +++ b/Lab7/include/page_alloc.h @@ -0,0 +1,39 @@ +#ifndef _PAGE_ALLOC_H +#define _PAGE_ALLOC_H + +// #define DEBUG + +#define MAX_ORDER 11 // order 0 ~ 11 +#define ALLOCATED -1 +#define FREE_BUDDY 99 +#define MIN_PAGE_SIZE 0x1000 +// #define FREE_MEM_START 0x10000000 +// #define FREE_MEM_END 0x20000000 +#define FREE_MEM_START 0x00 +#define FREE_MEM_END 0x3C000000 +#define TOTAL_NUM_PAGE (FREE_MEM_END - FREE_MEM_START) / MIN_PAGE_SIZE + +typedef struct _page_frame_node page_frame_node; +struct _page_frame_node +{ + int index; // const + int val; + void *addr; // const + int contiguous_head; // if allocated, this value keep track who (page) is the start of the contiguous memory in index + int allocated_order; + int chunk_order; // -1 if no chunk, otherwise 1, 2, 4, 6, 8, 16, 32 + page_frame_node *next; + page_frame_node *previous; +}; + +void init_page_frame(); +void *my_malloc(int req_size); +int get_page_from_free_list(int req_size, int who); +void add_to_free_list(page_frame_node *head_node, int index); +void remove_from_free_list(page_frame_node *free_list_node); +void put_back_to_free_list(int num_of_redundant_page, int index); +int free_page_frame(int index); +int merge_buddy(int *index, int buddy, int order); +void debug(); + +#endif /*_PAGE_ALLOC_H */ diff --git a/Lab7/include/peripherals/base.h b/Lab7/include/peripherals/base.h new file mode 100644 index 000000000..63f9c038f --- /dev/null +++ b/Lab7/include/peripherals/base.h @@ -0,0 +1,6 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab7/include/peripherals/device_tree.h b/Lab7/include/peripherals/device_tree.h new file mode 100644 index 000000000..d5d3bc729 --- /dev/null +++ b/Lab7/include/peripherals/device_tree.h @@ -0,0 +1,16 @@ +#ifndef _P_DEVICE_TREE_H +#define _P_DEVICE_TREE_H + +#define FDT_MAGIC 0xD00DFEED +#define FDT_VERSION 0x00000011 +#define FDT_TK_NULL 0X00000000 + +#define FDT_BEGIN_NODE 0X00000001 +#define FDT_END_NODE 0X00000002 +#define FDT_PROP 0X00000003 +#define FDT_NOP 0X00000004 +#define FDT_END 0X00000009 + +#define FDT_CPIO_INITRAMFS_PROPNAME "linux,initrd-start" + +#endif /*_P_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab7/include/peripherals/gpio.h b/Lab7/include/peripherals/gpio.h new file mode 100644 index 000000000..a7a6a5b4c --- /dev/null +++ b/Lab7/include/peripherals/gpio.h @@ -0,0 +1,25 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL0 (PBASE + 0x00200000) +#define GPFSEL1 (PBASE + 0x00200004) +#define GPFSEL2 (PBASE + 0x00200008) +#define GPFSEL3 (PBASE + 0x0020000C) +#define GPFSEL4 (PBASE + 0x00200010) +#define GPFSEL5 (PBASE + 0x00200014) +#define GPSET0 (PBASE + 0x0020001C) +#define GPSET1 (PBASE + 0x00200020) +#define GPCLR0 (PBASE + 0x00200028) +#define GPLEV0 (PBASE + 0x00200034) +#define GPLEV1 (PBASE + 0x00200038) +#define GPEDS0 (PBASE + 0x00200040) +#define GPEDS1 (PBASE + 0x00200044) +#define GPHEN0 (PBASE + 0x00200064) +#define GPHEN1 (PBASE + 0x00200068) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) +#define GPPUDCLK1 (PBASE + 0x0020009C) + +#endif /*_P_GPIO_H */ diff --git a/Lab7/include/peripherals/irq.h b/Lab7/include/peripherals/irq.h new file mode 100644 index 000000000..51e0cc5ea --- /dev/null +++ b/Lab7/include/peripherals/irq.h @@ -0,0 +1,27 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + +#define IRQ_BASIC_PENDING (PBASE + 0x0000B200) +#define IRQ_PENDING_1 (PBASE + 0x0000B204) +#define IRQ_PENDING_2 (PBASE + 0x0000B208) +#define FIQ_CONTROL (PBASE + 0x0000B20C) +#define ENABLE_IRQS_1 (PBASE + 0x0000B210) +#define ENABLE_IRQS_2 (PBASE + 0x0000B214) +#define ENABLE_BASIC_IRQS (PBASE + 0x0000B218) +#define DISABLE_IRQS_1 (PBASE + 0x0000B21C) +#define DISABLE_IRQS_2 (PBASE + 0x0000B220) +#define DISABLE_BASIC_IRQS (PBASE + 0x0000B224) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#define CORE0_INTR_SRC 0x40000060 +#define CORE1_INTR_SRC 0x40000064 +#define CORE2_INTR_SRC 0x40000068 +#define CORE3_INTR_SRC 0x4000006C + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/Lab7/include/peripherals/mbox_call.h b/Lab7/include/peripherals/mbox_call.h new file mode 100644 index 000000000..cfcc0d3ad --- /dev/null +++ b/Lab7/include/peripherals/mbox_call.h @@ -0,0 +1,40 @@ +#ifndef _P_MBOX_CALL_H +#define _P_MBOX_CALL_H + +#include "peripherals/base.h" + +#define VIDEOCORE_MBOX (PBASE + 0x0000B880) +#define MBOX_READ (VIDEOCORE_MBOX + 0x0) +#define MBOX_POLL (VIDEOCORE_MBOX + 0x10) +#define MBOX_SENDER (VIDEOCORE_MBOX + 0x14) +#define MBOX_STATUS (VIDEOCORE_MBOX + 0x18) +#define MBOX_CONFIG (VIDEOCORE_MBOX + 0x1C) +#define MBOX_WRITE (VIDEOCORE_MBOX + 0x20) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_MODEL 0x10001 +#define MBOX_TAG_REVISION 0x10002 +#define MBOX_TAG_MAC_ADDRESS 0x10003 +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_ARM_MEMORY 0x10005 +#define MBOX_TAG_VC_MEMORY 0x10006 +#define MBOX_TAG_CLOCKS 0x10007 +#define MBOX_TAG_LAST 0 + +#endif /* _P_MBOX_CALL_H */ diff --git a/Lab7/include/peripherals/mini_uart.h b/Lab7/include/peripherals/mini_uart.h new file mode 100644 index 000000000..71119b511 --- /dev/null +++ b/Lab7/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/Lab7/include/peripherals/reboot.h b/Lab7/include/peripherals/reboot.h new file mode 100644 index 000000000..f1d41ad2e --- /dev/null +++ b/Lab7/include/peripherals/reboot.h @@ -0,0 +1,8 @@ +#ifndef _P_REBOOT_H +#define _P_REBOOT_H + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +#endif /*_P_REBOOT_H */ \ No newline at end of file diff --git a/Lab7/include/printf.h b/Lab7/include/printf.h new file mode 100644 index 000000000..a9501cba0 --- /dev/null +++ b/Lab7/include/printf.h @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ + void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ + int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ + int sprintf_(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + int snprintf_(char *buffer, size_t count, const char *format, ...); + int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ + int vprintf_(const char *format, va_list va); + + /** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ + int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ \ No newline at end of file diff --git a/Lab7/include/read_cpio.h b/Lab7/include/read_cpio.h new file mode 100644 index 000000000..ea7c5f9c6 --- /dev/null +++ b/Lab7/include/read_cpio.h @@ -0,0 +1,18 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +typedef struct cpio_node_struct +{ + char *name; + int type; + int size; + char *data; + struct cpio_node_struct *next; +} cpio_node_t; + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); +char *find_content_addr(char *cpioDest, const char *filename); +int load_userprogram(const char *, char *); + +#endif /*_READ_CPIO_H */ \ No newline at end of file diff --git a/Lab7/include/reboot.h b/Lab7/include/reboot.h new file mode 100644 index 000000000..e9fb1d66c --- /dev/null +++ b/Lab7/include/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif /*_REBOOT_H */ \ No newline at end of file diff --git a/Lab7/include/reserve_mem.h b/Lab7/include/reserve_mem.h new file mode 100644 index 000000000..3922438c7 --- /dev/null +++ b/Lab7/include/reserve_mem.h @@ -0,0 +1,18 @@ +#ifndef _RESERVE_MEM_H +#define _RESERVE_MEM_H + +#define USRPGM_BASE 0x15000000 +#define USRPGM_SIZE 0x100000 + +typedef struct _reserved_memory_block +{ + unsigned long start; + unsigned long end; + char name[30]; +} reserved_memory_block; + +void memory_reserve(unsigned long start, unsigned long end, char *name); +int check_contain_RM(unsigned long start, unsigned long end); +void memory_init(); + +#endif /*_RESERVE_MEM_H */ \ No newline at end of file diff --git a/Lab7/include/shell.h b/Lab7/include/shell.h new file mode 100644 index 000000000..f677e039b --- /dev/null +++ b/Lab7/include/shell.h @@ -0,0 +1,7 @@ +#ifndef _SHELL_H +#define _SHELL_H + +void shell_main(char *); +void shell_start(); + +#endif /*_SHELL_H */ diff --git a/Lab7/include/stdlib.h b/Lab7/include/stdlib.h new file mode 100644 index 000000000..61d6ea3a4 --- /dev/null +++ b/Lab7/include/stdlib.h @@ -0,0 +1,31 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" +#include "utils.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const char *str); +char *strcpy(char *destination, const char *source); +int atoi(char *str); +char *strncpy(char src[], char des[], int n); +int strnchr(char *pathname, char target); +void strcat(char *des, char *s); + +void *memset(void *dest, register int val, int len); +int memcmp(void *s1, void *s2, int n); +void *memcpy(void *dest, const void *src, size_t len); +int hex2int(char *s, int n); + +void *simple_malloc(unsigned int size); + +// void printf(char *fmt, ...); +// unsigned int sprintf(char *dst, char *fmt, ...); +// unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +#endif /*_STDLIB_H */ diff --git a/Lab7/include/syscall.h b/Lab7/include/syscall.h new file mode 100644 index 000000000..3f59e9c25 --- /dev/null +++ b/Lab7/include/syscall.h @@ -0,0 +1,26 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + +#include "stdlib.h" + +int getpid(); +size_t uart_read(char buf[], size_t size); +size_t uart_write(const char buf[], size_t size); +int exec(const char *name, char *const argv[]); +int fork(); +void exit(int status); +int mbox_call_u(unsigned char ch, unsigned int *mbox); +void kill(int pid); +void signal(int SIGNAL, void (*handler)()); + +int open(char *pathname, int flags); +int close(int fd); +long write(int fd, void *buf, unsigned long count); +long read(int fd, void *buf, unsigned long count); +int mkdir(char *pathname, unsigned mode); +int mount(char *src, char *target, char *filesystem, unsigned long flags, void *data); +int chdir(char *path); +long lseek(int fd, long offset, int whence); +int ioctl(int fd, unsigned long request, unsigned long arg); + +#endif /*_SYSCALL_H */ \ No newline at end of file diff --git a/Lab7/include/test.h b/Lab7/include/test.h new file mode 100644 index 000000000..6b3788389 --- /dev/null +++ b/Lab7/include/test.h @@ -0,0 +1,9 @@ +#ifndef _TEST_H +#define _TEST_H + +void test_mem_alloc(); +void test_thread(); +void load_usrpgm_in_umode(); +void load_vfs1_usrpgm_in_umode(); + +#endif /*_TEST_H */ diff --git a/Lab7/include/thread.h b/Lab7/include/thread.h new file mode 100644 index 000000000..c3ca6e5a4 --- /dev/null +++ b/Lab7/include/thread.h @@ -0,0 +1,65 @@ +#ifndef _THREAD_H +#define _THREAD_H + +#include "list.h" +#include "my_signal.h" +#include "vfs.h" + +#define READY 1 +#define ZOMBIE 2 +#define FORKING 4 + +typedef void (*func_ptr)(); + +typedef struct _context +{ + unsigned long x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, fp, lr, sp; +} context; + +typedef struct _thread_info +{ + long id; + long child_id; + struct _task_struct *task; + +} thread_info; + +typedef struct _trapframe +{ + unsigned long x[31]; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trapframe; + +typedef struct _task_struct +{ + struct _context task_context; // context need to be the first one + struct _thread_info *thread_info; + struct list_head list; + func_ptr job; + unsigned long kstack_start; // kernel stack base + unsigned long ustack_start; // user stack base + unsigned long usrpgm_load_addr; // user program load address + unsigned long status; + unsigned long trapframe; // using "unsigned long" to keep trapframe address, instead of claiming a "trapframe_t*" to avoid "Data Abort" + struct _custom_signal *custom_signal; + struct _signal_context *signal_context; + struct file *fd_table[VFS_PROCESS_MAX_OPEN_FILE]; // should be zeroed out on thread_create +} task_struct; + +void schedule(); +void add_rq(task_struct *task); +task_struct *del_rq(); +void thread_init(); +thread_info *thread_create(func_ptr fp); +void task_wrapper(); +void idle_task(); +void kill_zombies(); +void do_fork(); +void create_child(task_struct *parent); +void debug_task_rq(); +void debug_task_zombieq(); +int thread_get_idle_fd(task_struct *thd); + +#endif /*_THREAD_H */ \ No newline at end of file diff --git a/Lab7/include/timer.h b/Lab7/include/timer.h new file mode 100644 index 000000000..2e352e7fe --- /dev/null +++ b/Lab7/include/timer.h @@ -0,0 +1,14 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#define MESSAGE_BUFFER 50 +#define SECONDS_BUFFER 20 + +void get_current_time(); +void el0_timer_handler(long cntpct_el0, long cntfrq_el0); +void el1_timer_handler(long cntpct_el0, long cntfrq_el0); +void add_timer(int sec, char *mes); +int is_timer_queue_empty(); +void timer_delay(long seconds); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab7/include/tmpfs.h b/Lab7/include/tmpfs.h new file mode 100644 index 000000000..3442de435 --- /dev/null +++ b/Lab7/include/tmpfs.h @@ -0,0 +1,38 @@ +#ifndef _TMPFS_H +#define _TMPFS_H + +#include "vfs.h" + +#define EXISTED 0 +#define NOTFOUND -1 +#define NOTDIR -2 + +/* mount operations */ +int tmpfs_mount(struct filesystem *fs, struct mount *mount); + +/* vnode operations */ +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); +int tmpfs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); + +/* file operations */ +int tmpfs_write(struct file *file, void *buf, size_t len); +int tmpfs_read(struct file *file, void *buf, size_t len); +int tmpfs_open(struct vnode *file_node, struct file **target); +int tmpfs_close(struct file *file); + +int initramfs_mount(filesystem_t *fs, mount_t *mount); +void init_cpio(); + +/* vnode operations */ +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); +int initramfs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); + +/* file operations */ +int initramfs_write(struct file *file, void *buf, size_t len); +int initramfs_read(struct file *file, void *buf, size_t len); +int initramfs_open(struct vnode *file_node, struct file **target); +int initramfs_close(struct file *file); + +#endif \ No newline at end of file diff --git a/Lab7/include/utils.h b/Lab7/include/utils.h new file mode 100644 index 000000000..a23ae37a7 --- /dev/null +++ b/Lab7/include/utils.h @@ -0,0 +1,8 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay ( unsigned long); +extern void put32 ( unsigned long, unsigned int ); +extern unsigned int get32 ( unsigned long ); + +#endif /*_BOOT_H */ diff --git a/Lab7/include/vfs.h b/Lab7/include/vfs.h new file mode 100644 index 000000000..0897269a4 --- /dev/null +++ b/Lab7/include/vfs.h @@ -0,0 +1,95 @@ +#ifndef _VFS_H +#define _VFS_H + +#include + +#define O_CREAT 00000100 +#define DIR 0 +#define FILE 1 +#define COMPONENT_NAME_MAX 32 // include '\0' +#define MAX_NUM_OF_ENTRY 16 +#define TMPFS_FILE_SIZE_MAX 4096 +#define PATHNAME_MAX 256 // include '\0' +#define VFS_PROCESS_MAX_OPEN_FILE 16 + +typedef struct vnode +{ + struct mount *mount; + struct vnode_operations *v_ops; + struct file_operations *f_ops; + struct node_info *internal; +} vnode_t; + +typedef struct node_info +{ + char *name; + unsigned int type; + unsigned int size; // if type is 'DIR' means # of entries, if is 'FILE' means size of file(in bytes). + vnode_t **entry; + char *data; +} node_info_t; + +// file handle +typedef struct file +{ + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +} file_t; + +typedef struct mount +{ + struct vnode *root; + struct filesystem *fs; +} mount_t; + +typedef struct filesystem +{ + char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +} filesystem_t; + +typedef struct vnode_operations +{ + int (*lookup)(struct vnode *dir_node, struct vnode **target, char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, char *component_name); +} vnode_operations_t; + +typedef struct file_operations +{ + int (*write)(struct file *file, void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + long (*lseek64)(struct file *file, long offset, int whence); +} file_operations_t; + +extern struct mount *rootfs; +extern char cwdpath[256]; +extern file_t *kfd[16]; +extern int fd_count; + +int register_filesystem(struct filesystem *fs); +int vfs_mount(char *target, char *filesystem); +int vfs_lookup(char *pathname, struct vnode **target); +int vfs_create(char *pathname); +int vfs_mkdir(char *pathname); + +int vfs_open(char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); + +void get_next_component(char *pathname, char *target, int level); +void basename(char *src, char *des); +void dirname(char *src, char *des); +void handle_path(char *rela, char *abso); + +void vfs_ls(char *pathname, int flag); +int vfs_cd(char *target_dir); +long vfs_lseek(struct file *file, long offset, int whence); +long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +#endif \ No newline at end of file diff --git a/Lab7/include/virtual_mem.h b/Lab7/include/virtual_mem.h new file mode 100644 index 000000000..d612b5079 --- /dev/null +++ b/Lab7/include/virtual_mem.h @@ -0,0 +1,6 @@ +#ifndef _VIRTUAL_MEM_H +#define _VIRTUAL_MEM_H + +void virtual_mem_init(); + +#endif /*_VIRTUAL_MEM_H */ \ No newline at end of file diff --git a/Lab7/initramfs.cpio b/Lab7/initramfs.cpio new file mode 100644 index 000000000..1640f8c4a Binary files /dev/null and b/Lab7/initramfs.cpio differ diff --git a/Lab7/kernel8.img b/Lab7/kernel8.img new file mode 100755 index 000000000..b182f8b5d Binary files /dev/null and b/Lab7/kernel8.img differ diff --git a/Lab7/pack.sh b/Lab7/pack.sh new file mode 100644 index 000000000..e68484ad3 --- /dev/null +++ b/Lab7/pack.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd rootfs +find . | cpio -o -H newc > ../initramfs.cpio +cd .. \ No newline at end of file diff --git a/Lab7/rootfs/a.c b/Lab7/rootfs/a.c new file mode 100644 index 000000000..d2398d75b --- /dev/null +++ b/Lab7/rootfs/a.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + printf("HAHA return 1\n"); + + return 1; +} diff --git a/Lab7/rootfs/cat.txt b/Lab7/rootfs/cat.txt new file mode 100644 index 000000000..949e8345b --- /dev/null +++ b/Lab7/rootfs/cat.txt @@ -0,0 +1 @@ +No cat here. \ No newline at end of file diff --git a/Lab7/rootfs/one b/Lab7/rootfs/one new file mode 100644 index 000000000..50ecbb596 --- /dev/null +++ b/Lab7/rootfs/one @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 diff --git a/Lab7/rootfs/ts.txt b/Lab7/rootfs/ts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Lab7/rootfs/vfs1.img b/Lab7/rootfs/vfs1.img new file mode 100644 index 000000000..2c41bd71a Binary files /dev/null and b/Lab7/rootfs/vfs1.img differ diff --git a/Lab7/send_kernel.py b/Lab7/send_kernel.py new file mode 100644 index 000000000..1b17155f2 --- /dev/null +++ b/Lab7/send_kernel.py @@ -0,0 +1,40 @@ +# This python script is for macOS +from serial import Serial +import os +import time +import fcntl +import threading + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +with Serial('/dev/tty.usbserial-0001', 115200) as ser: + sem = threading.Semaphore() + # set tty to non-blocking mode + fcntl.fcntl(ser, fcntl.F_SETFL, os.O_NONBLOCK) + + input("Press anything to start transmission\n") + + print("Sending Strat...") + sem.acquire() + ser.write(b"Start") + sem.release() + time.sleep(1) + + sem.acquire() + ser.write(file_size.to_bytes(4, 'little')) + sem.release() + time.sleep(1) + + print("Start sending kernel img by uart...") + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + sem.acquire() + ser.write(data) + sem.release() + time.sleep(1) + + print("Transfer finished!") diff --git a/Lab7/src/bootloader/boot.S b/Lab7/src/bootloader/boot.S new file mode 100644 index 000000000..403960d2e --- /dev/null +++ b/Lab7/src/bootloader/boot.S @@ -0,0 +1,25 @@ +#include "mm.h" + +.section ".text.boot" + +.globl _start +_start: + mov x24, x0 + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + mov x0, x24 + bl bootloader_main + b proc_hang // should never come here diff --git a/Lab7/src/bootloader/bootloader.c b/Lab7/src/bootloader/bootloader.c new file mode 100644 index 000000000..327413557 --- /dev/null +++ b/Lab7/src/bootloader/bootloader.c @@ -0,0 +1,15 @@ +#include "mini_uart.h" +#include "load_kernel.h" + +void bootloader_main(void) +{ + uart_init(); + + uart_send_string("Relocatting ...\n"); + char *from_dest = (char *)0x80000; + char *to_dest = (char *)0x60000; + relocate((char *)from_dest, (char *)to_dest); + + char *dest = (char *)0x80000; + load_kernel((char *)dest); +} diff --git a/Lab7/src/bootloader/config.txt b/Lab7/src/bootloader/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/Lab7/src/bootloader/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/Lab7/src/bootloader/link.ld b/Lab7/src/bootloader/link.ld new file mode 100644 index 000000000..1fca3dfb7 --- /dev/null +++ b/Lab7/src/bootloader/link.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0x80000; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; +__loader_size = (_end - _start); diff --git a/Lab7/src/bootloader/load_kernel.c b/Lab7/src/bootloader/load_kernel.c new file mode 100644 index 000000000..00808de84 --- /dev/null +++ b/Lab7/src/bootloader/load_kernel.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include "mini_uart.h" +#include "peripherals/mini_uart.h" +#include "stdlib.h" + +extern int __loader_size; +extern void *_dtb_ptr; + +void load_kernel(char *dest) +{ + int size = 0; + + uart_send_string("Waiting for kernel8.img ...\n"); + + char start[5] = "Start"; + + for (int i = 0; i < 5; i++) + { + if (uart_recv() != start[i]) + i = 0; + } + + uart_send_string("Start transmitting ...\n"); + + size = uart_recv(); + size |= uart_recv() << 8; + size |= uart_recv() << 16; + size |= uart_recv() << 24; + + printf("Size of kernel is = %d bytes\n", size); + + // read the kernel + while (size--) + { + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + *dest++ = get32(AUX_MU_IO_REG); + } + + uart_send_string("End transmitting ...\n"); + + // restore arguments and jump to the new kernel. + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + // we must force an absolute address to branch to + "mov x30, 0x80000; ret"); +} + +void relocate(char *from_dest, char *to_dest) +{ + long long size = (long long)&__loader_size; + + while (size--) + { + *to_dest++ = *from_dest++; + } + + char *redicrect = __builtin_return_address(0) + (to_dest - from_dest); + + goto *redicrect; +} \ No newline at end of file diff --git a/Lab7/src/kernel/boot.S b/Lab7/src/kernel/boot.S new file mode 100644 index 000000000..5ff99aa48 --- /dev/null +++ b/Lab7/src/kernel/boot.S @@ -0,0 +1,89 @@ +#include "mm.h" + +.global _dtb_ptr +.section .data +_dtb_ptr: .dc.a 0x0 + +.section ".text.boot" + +.globl _start +.globl proc_hang +_start: + cbz x24, x24c // Check if bootloader, see bootloader's boot.S for more info +x24nc: + ldr x21, =_dtb_ptr + str x24, [x21] + b go +x24c: + ldr x21, =_dtb_ptr + str x0, [x21] +go: + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + + ldr x1, =_start + + // get CurrentEL + mrs x0, CurrentEL + and x0, x0, #12 // clear reserved bits + + // running at EL3? branch if in EL3 + cmp x0, #12 + beq from_el3_to_el2 + + // running at EL1? branch if in EL1 + cmp x0, #4 + beq bss + + bl from_el2_to_el1 + +bss: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + bl set_el1_exception_vector_table // set el1 exception vector table base + + mov sp, #LOW_MEMORY // 4MB + // ldr x1, =_start + // mov sp, x1 + bl kernel_main + b proc_hang // should never come here + +from_el3_to_el2: + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, from_el2_to_el1 + msr elr_el3, x2 + eret + +from_el2_to_el1: + msr sp_el1, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // change execution level to EL1 + mov x2, #0x3c5 + msr spsr_el2, x2 + msr elr_el2, lr + eret \ No newline at end of file diff --git a/Lab7/src/kernel/config.txt b/Lab7/src/kernel/config.txt new file mode 100644 index 000000000..279349696 --- /dev/null +++ b/Lab7/src/kernel/config.txt @@ -0,0 +1,2 @@ +kernel_old=1 +disable_commandline_tags=1 diff --git a/Lab7/src/kernel/devfs.c b/Lab7/src/kernel/devfs.c new file mode 100644 index 000000000..d6a528e85 --- /dev/null +++ b/Lab7/src/kernel/devfs.c @@ -0,0 +1,129 @@ +#include "devfs.h" +#include "tmpfs.h" +#include "stdlib.h" +#include "page_alloc.h" +#include "mbox.h" + +#define DEVFS_UART_NAME "uart" +#define DEVFS_FRAMEBUFFER_NAME "framebuffer" +#define SEEK_SET 0 + +filesystem_t devfs = {.name = "devfs", .setup_mount = devfs_setup_mount}; +file_operations_t devfs_fops = {.write = devfs_write, .read = devfs_read, .open = devfs_open, .close = devfs_close, .lseek64 = devfs_lseek}; +vnode_operations_t devfs_vops = {.lookup = devfs_lookup, .create = devfs_create, .mkdir = devfs_mkdir}; + +int devfs_setup_mount(struct filesystem *fs, struct mount *mount) +{ + mount->root->f_ops = &devfs_fops; + mount->root->v_ops = &devfs_vops; + vnode_t *dir_node = mount->root; + + // Create uart + vnode_t *node_new = NULL; + int ret = dir_node->v_ops->create(dir_node, &node_new, DEVFS_UART_NAME); + if (ret == 0) + node_new->internal->type = FILE; + else + printf("Error, devfs_setup_mount(), failed to create uart, ret=%d\r\n", ret); + + // Create and init framebuffer + node_new = NULL; + ret = dir_node->v_ops->create(dir_node, &node_new, DEVFS_FRAMEBUFFER_NAME); + if (ret == 0) + { + node_new->internal->type = FILE; + node_new->internal->data = framebuffer_init(); + } + else + printf("Error, devfs_setup_mount(), failed to create framebuffer, ret=%d\r\n", ret); + + return 0; +} + +// fops +int devfs_write(struct file *file, void *buf, size_t len) +{ + if (strcmp(file->vnode->internal->name, DEVFS_UART_NAME) == 0) + { + const char *ptr = buf; + for (size_t i = 0; i < len; i++) + uart_send_byte(*ptr++); + return len; + } + else if (strcmp(file->vnode->internal->name, DEVFS_FRAMEBUFFER_NAME) == 0) + { + node_info_t *node_info = file->vnode->internal; + const char *ptr = buf; + char *dest = (char *)(node_info->data + file->f_pos); + for (size_t i = 0; i < len / sizeof(char); i++) + *dest++ = *ptr++; + file->f_pos += len; + // printf("Debug, devfs_write(), f_pos wrote to %ld\r\n", file->f_pos); + // uint64_t tk = 0; + // WAIT_TICKS(tk, 1000); + return len; + } + else + { + printf("Error, devfs_write(), writing to unrecognized device %s\r\n", file->vnode->internal->name); + return 0; + } +} + +int devfs_read(struct file *file, void *buf, size_t len) +{ + if (strcmp(file->vnode->internal->name, DEVFS_UART_NAME) == 0) + { + char *ptr = buf; + for (size_t i = 0; i < len; i++) + *ptr++ = uart_recv_byte(); + return len; + } + else + { + printf("Error, devfs_read(), reading from unrecognized device %s\r\n", file->vnode->internal->name); + return 0; + } +} + +int devfs_open(struct vnode *file_node, struct file **target) +{ + return tmpfs_open(file_node, target); +} + +int devfs_close(struct file *file) +{ + return tmpfs_close(file); +} + +long devfs_lseek(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = (size_t)offset; + // printf("Debug, devfs_lseek(), f_pos set to %ld\r\n", file->f_pos); + return file->f_pos; + } + else + { + printf("Error, devfs_lseek(), unknown whence=%d\r\n", whence); + return -1; + } +} + +// vops +int devfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + printf("Error, devfs_mkdir(), cannot mkdir with devfs\r\n"); + return 1; +} + +int devfs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + return tmpfs_create(dir_node, target, component_name); +} + +int devfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + return tmpfs_lookup(dir_node, target, component_name); +} diff --git a/Lab7/src/kernel/device_tree.c b/Lab7/src/kernel/device_tree.c new file mode 100644 index 000000000..d1b67143f --- /dev/null +++ b/Lab7/src/kernel/device_tree.c @@ -0,0 +1,266 @@ +#include +#include "peripherals/device_tree.h" +#include "stdlib.h" +#include "mini_uart.h" +#include "device_tree.h" + +typedef struct fdt_header +{ + uint32_t magic; // big-endian + 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; +} fdt_header; + +typedef struct +{ + uint32_t len; + uint32_t nameoff; +} fdt_prop; + +char *cpioDestGlobal; + +static uint64_t pad_to_4(void *num); +static uint32_t rev32(uint32_t val); +static uint64_t endian_rev(void *input, int dtype_size); + +int initramfs_callback(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + uint8_t *p = off_dt_struct; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + if (!strcmp(FDT_CPIO_INITRAMFS_PROPNAME, prop_name)) + { + uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); + printf("initramfs_addr at %p\n", addr); + cpioDestGlobal = (char *)addr; + } + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +int dtb_parser(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + int depth = 0; + uint8_t *p = off_dt_struct; + char *node_name = NULL; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + node_name = (char *)p; + if (depth == 0) + printf("\\ {\n"); + else + { + uart_send_space(depth * 3); + printf("%s {\n", node_name); + } + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + depth++; + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + depth--; + uart_send_space(depth * 3); + printf("};\n"); + printf("\n"); + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + + if (!strcmp(prop_name, "#address-cells") || !strcmp(prop_name, "#size-cells") || !strcmp(prop_name, "interrupt-parent")) + { + // + uart_send_space(depth * 3); + printf("%s = <%d>;\n", prop_name, rev32(*((uint32_t *)prop_val))); + } + else if (!strcmp(prop_name, "model") || !strcmp(prop_name, "status") || !strcmp(prop_name, "name") || !strcmp(prop_name, "device_type") || + !strcmp(prop_name, "chassis-type") || !strcmp(prop_name, "bootargs") || !strcmp(prop_name, "stdout-path") || !strcmp(prop_name, "stdin-path") || + !strcmp(prop_name, "power-isa-version") || !strcmp(prop_name, "mmu-type") || !strcmp(prop_name, "label") || !strcmp(prop_name, "phy-connection-type")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else if (!strcmp(prop_name, "compatible")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else + { + uart_send_space(depth * 3); + printf("%s = %s;\n", prop_name, prop_val); + } + + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +void fdt_traverse(fdt_callback cb, char *dtb) +{ + if (cb(dtb) == -1) + printf("fdt_traverse failed.\n"); + + return; +} + +static uint64_t pad_to_4(void *num) +{ + uint64_t modded = ((uint64_t)num) % 4; + return modded == 0 ? 0 : 4 - modded; +} + +static uint32_t rev32(uint32_t val) +{ + return (uint32_t)endian_rev(&val, 4); +} + +/** Transform data from litle to big endian, or from big to little endian + * @param input: Pointer to a value that needs to be transformed + * @param dtype_size: data type size, size of each item in bytes. Possible value: 1, 2, 4, 8 + */ +static uint64_t endian_rev(void *input, int dtype_size) +{ + const uint8_t *ptr = (uint8_t *)input; + uint64_t ret = 0; + + switch (dtype_size) + { + // int8_t, uint8_t + case 1: + // No need to transform to big endian since the data type size is 1 byte + break; + + // int16_t, uint16_t + case 2: + ret = (ptr[0] << 8) | ptr[1]; + break; + + // int32_t, uint32_t + case 4: + ret = (ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + ptr[3]; + break; + + // int64_t, uint64_t + // case 8: + // ret = (ptr[0] << 56) | + // (ptr[1] << 48) | + // (ptr[2] << 40) | + // (ptr[3] << 32) | + // (ptr[4] << 24) | + // (ptr[5] << 16) | + // (ptr[6] << 8) | + // ptr[7]; + // break; + + default: + printf("[Error] Endian transformation(%d) not implemented. @line %d, file:%s\r\n", dtype_size, __LINE__, __FILE__); + break; + } + return ret; +} \ No newline at end of file diff --git a/Lab7/src/kernel/dynamic_alloc.c b/Lab7/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..a16a426b0 --- /dev/null +++ b/Lab7/src/kernel/dynamic_alloc.c @@ -0,0 +1,249 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "list.h" + +extern page_frame_node frame_array[TOTAL_NUM_PAGE]; + +pool_list pool[33]; // 1, 2, 4, 6, 8, 16, 32 +chunk chunk_array[3000]; +int global_chunk_index = -1; + +void init_pool() +{ + for (int i = 0; i < 33; i++) + INIT_LIST_HEAD(&pool[i].list); + return; +} + +chunk *new_chunk() +{ + global_chunk_index++; + chunk_array[global_chunk_index].index = global_chunk_index; + return &chunk_array[global_chunk_index]; +} + +void *get_chunk(int req_size) +{ + int req_pool_index = roundup_size(req_size); // req_pool_index * MIN_CHUNK_SIZE = req_size + + if (list_empty(&pool[req_pool_index].list)) + { + // empty pool on req_size + int frame_index = get_page_from_free_list(MIN_PAGE_SIZE, req_pool_index); + split_page(frame_index, req_pool_index); + } + + int index = remove_a_chunk_from_pool(req_pool_index); + if (index == -1) + return NULL; + + void *addr = chunk_array[index].addr; + return addr; +} + +void split_page(int frame_index, int req_pool_index) +{ + int split_size = (req_pool_index * MIN_CHUNK_SIZE); + for (int i = 0; i < (MIN_PAGE_SIZE / split_size); i++) + { + chunk *new = new_chunk(); + new->size = split_size; + new->addr = (void *)frame_array[frame_index].addr + i *split_size; + new->val = FREE; + new->belong_page = frame_index; + list_add_tail(&new->list, &pool[req_pool_index].list); + } +} + +int remove_a_chunk_from_pool(int req_pool_index) +{ + chunk *alloc_chunk = container_of(pool[req_pool_index].list.next, chunk, list); + list_del_init(pool[req_pool_index].list.next); + alloc_chunk->val = ALLOCATED; + return alloc_chunk->index; +} + +void put_back_to_pool(int pool_index, int chunk_index) +{ + // addr to insert + struct list_head *node = &chunk_array[chunk_index].list; + unsigned long addr_long = (unsigned long)chunk_array[chunk_index].addr; + + // insert in by the order of addr + struct list_head *iter = &pool[pool_index].list; + struct list_head *start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + unsigned long iter_addr_long = (unsigned long)tmp->addr; + if (iter_addr_long > addr_long) + { + // list_insert() + iter->prev->next = node; + node->prev = iter->prev; + iter->prev = node; + node->next = iter; + + tmp->size = -1; + tmp->val = FREE; + + break; + } + } + + // check if there are free chunks in same page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + int count = 0; + int page_id = addr_long >> 12; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + count++; + else + break; + } + if (count == (MIN_PAGE_SIZE / (pool_index * MIN_CHUNK_SIZE))) + { + // There is a free page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + break; + } + chunk *first_chunk_in_page = list_entry(iter, chunk, list); + for (int i = 0; i < count; i++) + { + chunk *tmp = list_entry(iter, chunk, list); + tmp->val = FREE; + struct list_head *tmp_next = iter->next; + iter->prev->next = iter->next; + iter->next->prev = iter->prev; + iter->prev = iter; + iter->next = iter; + iter = tmp_next; + } + free_page_frame(first_chunk_in_page->belong_page); + } + + return; +} + +int free_chunk(int index) +{ + // free the page + int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; + put_back_to_pool(pool_index, index); + + return 0; +} + +void free(void *addr) +{ + // printf("[DEBUG] Freeing addr = %p\n", addr); + // Check addr is in which page frame + unsigned long addr_long = (unsigned long)addr; + int frame_index = (addr_long - FREE_MEM_START) / MIN_PAGE_SIZE; + + if (frame_array[frame_index].val != ALLOCATED) + { + printf("This page is Not Allocated yet\n"); + return; + } + + if (frame_array[frame_index].chunk_order != -1) + { + int chunk_index = -1; + // Find chunk_index + for (int i = 0; i < global_chunk_index; i++) + { + if (addr == chunk_array[i].addr) + { + chunk_index = i; + break; + } + } + // Check if is OK to free + if (chunk_index >= global_chunk_index || chunk_array[chunk_index].val != ALLOCATED) + { + if (chunk_index >= global_chunk_index) + printf("chunk_index is TOO high\n"); + if (chunk_array[chunk_index].val != ALLOCATED) + printf("chunk_index = %d\n", chunk_index); + printf("This chunk is Not Allocated yet\n"); + return; + } + + free_chunk(chunk_index); + return; + } + else + { + free_page_frame(frame_index); + return; + } +} + +int roundup_size(int size) +{ + switch (size) + { + case 1 ... 8: + return 1; + case 9 ... 16: + return 2; + case 17 ... 32: + return 4; + case 33 ... 48: + return 6; + case 49 ... 64: + return 8; + case 65 ... 128: + return 16; + case 129 ... 256: + return 32; + } + return 0; +} + +void debug_pool() +{ + printf("** DEBUGGING pool\n"); + for (int i = 0; i < 33; i++) + { + struct list_head *iter; + struct list_head *start; + iter = &pool[i].list; + start = &pool[i].list; + printf("pool[%d] -> ", i); + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + printf("addr %p -> ", tmp->addr); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING chunk\n"); + for (int i = 0; i < 20; i++) + { + printf("chunk_array[%d].index = %d\n", i, chunk_array[i].index); + printf("chunk_array[%d].size = %d\n", i, chunk_array[i].size); + printf("chunk_array[%d].addr = %p\n", i, chunk_array[i].addr); + printf("chunk_array[%d].val = %d\n", i, chunk_array[i].val); + printf("chunk_array[%d].belong_page= %d\n", i, chunk_array[i].belong_page); + printf("\n"); + } + printf("**\n"); +} \ No newline at end of file diff --git a/Lab7/src/kernel/exception.S b/Lab7/src/kernel/exception.S new file mode 100644 index 000000000..ab8b8cc66 --- /dev/null +++ b/Lab7/src/kernel/exception.S @@ -0,0 +1,189 @@ +#define CORE0_INTERRUPT_SOURCE 0x40000060 +#define IIR_ADDR 0x3f215048 + +.global set_el1_exception_vector_table +.global exception_vector_table +.global enable_interrupt +.global disable_interrupt + +enable_interrupt: + msr DAIFClr, 0xf + ret + +disable_interrupt: + msr DAIFSet, 0xf + ret + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 8 + 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] +.endm + +.macro save_all_sys + 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] + mrs x0, spsr_el1 + mrs x1, elr_el1 + mrs x2, sp_el0 + stp x30, x0, [sp ,16 * 15] + stp x1, x2, [sp ,16 * 16] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 8 +.endm + +.macro load_all_sys + ldp x1, x2, [sp ,16 * 16] + ldp x30, x0, [sp ,16 * 15] + msr spsr_el1, x0 + msr elr_el1, x1 + msr sp_el0, x2 + 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, 32 * 9 +.endm + +.align 11 // vector table should be aligned to 0x800 +el1_exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_same_el + .align 7 + b exception_handler_irq_sp_elx_same_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_lower_el + .align 7 + b exception_handler_irq_sp_elx_lower_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_el1_exception_vector_table: + adr x0, el1_exception_vector_table + msr vbar_el1, x0 + ret lr + +exception_handler: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_sync_sp_elx_lower_el: + save_all_sys + mov x0, sp + bl el0_to_el1_sync_handler + load_all_sys + eret + +exception_handler_irq_sp_elx_lower_el: + b el0_core_timer_handler + +exception_handler_sync_sp_elx_same_el: + b exception_handler + +exception_handler_irq_sp_elx_same_el: + b el1_core_interrupt_handler + +el0_core_timer_handler: + save_all_sys + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all_sys + eret + +el1_core_interrupt_handler: + save_all_sys + bl el1_irq_interrupt_handler + load_all_sys + eret \ No newline at end of file diff --git a/Lab7/src/kernel/exception.c b/Lab7/src/kernel/exception.c new file mode 100644 index 000000000..6c8b009fb --- /dev/null +++ b/Lab7/src/kernel/exception.c @@ -0,0 +1,335 @@ +#include "peripherals/mini_uart.h" +#include "peripherals/irq.h" +#include "stdlib.h" +#include "timer.h" +#include "thread.h" +#include "syscall.h" + +extern task_struct *get_current(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern signal_handler signal_table[]; +extern void proc_hang(); + +/** + * common exception handler + */ +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + // print out interruption type + switch (type) + { + case 0: + uart_send_string("Synchronous"); + break; + case 1: + uart_send_string("IRQ"); + break; + case 2: + uart_send_string("FIQ"); + break; + case 3: + uart_send_string("SError"); + break; + } + uart_send_string(": "); + // decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28) + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown"); + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE"); + break; + case 0b001110: + uart_send_string("Illegal execution"); + break; + case 0b010101: + uart_send_string("System call"); + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL"); + break; + case 0b100001: + uart_send_string("Instruction abort, same EL"); + break; + case 0b100010: + uart_send_string("Instruction alignment fault"); + break; + case 0b100100: + uart_send_string("Data abort, lower EL"); + break; + case 0b100101: + uart_send_string("Data abort, same EL"); + break; + case 0b100110: + uart_send_string("Stack alignment fault"); + break; + case 0b101100: + uart_send_string("Floating point"); + break; + default: + uart_send_string("Unknown"); + break; + } + // decode data abort cause + if (esr >> 26 == 0b100100 || esr >> 26 == 0b100101) + { + uart_send_string(", "); + switch ((esr >> 2) & 0x3) + { + case 0: + uart_send_string("Address size fault"); + break; + case 1: + uart_send_string("Translation fault"); + break; + case 2: + uart_send_string("Access flag fault"); + break; + case 3: + uart_send_string("Permission fault"); + break; + } + switch (esr & 0x3) + { + case 0: + uart_send_string(" at level 0"); + break; + case 1: + uart_send_string(" at level 1"); + break; + case 2: + uart_send_string(" at level 2"); + break; + case 3: + uart_send_string(" at level 3"); + break; + } + } + // dump registers + uart_send_string("\nSPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + + proc_hang(); + return; +} + +void el1_irq_interrupt_handler() +{ + unsigned int irq_basic_pending = get32(IRQ_BASIC_PENDING); + irq_basic_pending &= (1 << 19); // clear bits + + // GPU IRQ 57 : UART Interrupt + if (irq_basic_pending) + { + if (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + { + uart_rx_handler(); + } + else if (get32(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + { + uart_tx_handler(); + } + } + // ARM Core Timer Interrupt + else if (get32(CORE0_INTR_SRC) & (1 << 1)) + { + long cntpct_el0, cntfrq_el0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + el1_timer_handler(cntpct_el0, cntfrq_el0); + } + + return; +} + +void el0_to_el1_sync_handler(unsigned long trapframe_addr) +{ + int syscall_no; + trapframe *curr_trapframe = (trapframe *)trapframe_addr; + asm volatile("mov %0, x8" + : "=r"(syscall_no)::); + if (syscall_no == 0) + { + int pid = getpid(); + curr_trapframe->x[0] = pid; + } + else if (syscall_no == 1) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + disable_uart_irq(); + enable_interrupt(); + unsigned int ret = uart_read(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 2) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + unsigned int ret = uart_write(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 3) + { + char *name = (char *)curr_trapframe->x[0]; + char **argv = (char **)curr_trapframe->x[1]; + int ret = exec(name, argv); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 4) + { + task_struct *current = get_current(); + current->status = FORKING; + current->trapframe = trapframe_addr; + int ret = fork(); + curr_trapframe = (trapframe *)get_current()->trapframe; + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 5) + { + int status = curr_trapframe->x[0]; + exit(status); + } + else if (syscall_no == 6) + { + unsigned char ch = (unsigned char)curr_trapframe->x[0]; + unsigned int *mbox_user = (unsigned int *)curr_trapframe->x[1]; + int ret = mbox_call_u(ch, mbox_user); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 7) + { + int pid = (int)curr_trapframe->x[0]; + kill(pid); + } + else if (syscall_no == 8) + { + // signal + int SIGNAL = (int)curr_trapframe->x[0]; + void (*handler)() = (void (*)())curr_trapframe->x[1]; + signal(SIGNAL, handler); + } + else if (syscall_no == 9) + { + // signal kill + int pid = (int)curr_trapframe->x[0]; + int SIGNAL = (int)curr_trapframe->x[1]; + int if_cust = 0; + task_struct *current = get_current(); + + if (current->custom_signal) + { + custom_signal *cust_sig = current->custom_signal; + do + { + if (cust_sig->sig_num == SIGNAL) + { + if_cust = 1; + // signal's context save + sig_context_update(curr_trapframe, cust_sig->handler); + break; + } + cust_sig = container_of(cust_sig->list.next, custom_signal, list); + } while (cust_sig != current->custom_signal); + } + else if (!current->custom_signal && !if_cust) + (signal_table[SIGNAL])(pid); + } + else if (syscall_no == 10) + { + // signal restore + sig_context_restore(curr_trapframe); + + disable_interrupt(); + task_struct *current = get_current(); + free(current->signal_context->trapframe); + free(current->signal_context->user_stack); + free(current->signal_context); + current->signal_context = NULL; + enable_interrupt(); + } + else if (syscall_no == 11) + { + char *pathname = (char *)curr_trapframe->x[0]; + int flags = (int)curr_trapframe->x[1]; + int ret = open(pathname, flags); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 12) + { + int fd = (int)curr_trapframe->x[0]; + int ret = close(fd); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 13) + { + int fd = (int)curr_trapframe->x[0]; + void *buf = (void *)curr_trapframe->x[1]; + unsigned long count = (unsigned long)curr_trapframe->x[2]; + int ret = write(fd, buf, count); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 14) + { + int fd = (int)curr_trapframe->x[0]; + void *buf = (void *)curr_trapframe->x[1]; + unsigned long count = (unsigned long)curr_trapframe->x[2]; + int ret = read(fd, buf, count); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 15) + { + char *pathname = (char *)curr_trapframe->x[0]; + unsigned mode = (unsigned)curr_trapframe->x[1]; + int ret = mkdir(pathname, mode); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 16) + { + char *src = (char *)curr_trapframe->x[0]; + char *target = (char *)curr_trapframe->x[1]; + char *filesystem = (char *)curr_trapframe->x[2]; + unsigned long flags = (unsigned long)curr_trapframe->x[3]; + void *data = (void *)curr_trapframe->x[4]; + int ret = mount(src, target, filesystem, flags, data); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 17) + { + char *pathname = (char *)curr_trapframe->x[0]; + int ret = chdir(pathname); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 18) + { + int fd = (int)curr_trapframe->x[0]; + long offset = (long)curr_trapframe->x[1]; + int whence = (int)curr_trapframe->x[2]; + int ret = lseek(fd, offset, whence); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 19) + { + int fd = (int)curr_trapframe->x[0]; + unsigned long request = (unsigned long)curr_trapframe->x[1]; + unsigned long arg = (unsigned long)curr_trapframe->x[2]; + int ret = ioctl(fd, request, arg); + curr_trapframe->x[0] = ret; + } +} \ No newline at end of file diff --git a/Lab7/src/kernel/kernel.c b/Lab7/src/kernel/kernel.c new file mode 100644 index 000000000..dc764b380 --- /dev/null +++ b/Lab7/src/kernel/kernel.c @@ -0,0 +1,34 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "reserve_mem.h" +#include "device_tree.h" +#include "thread.h" +#include "virtual_mem.h" +#include "vfs.h" +#include "tmpfs.h" +#include "stdlib.h" + +extern void *_dtb_ptr; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + fdt_traverse(initramfs_callback, _dtb_ptr); + + thread_init(); + + memory_init(); + + // virtual_mem_init(); + + vfs_mount("/", "tmpfs"); + vfs_mount("/initramfs", "initramfs"); + vfs_mount("/dev", "devfs"); + strcpy(cwdpath, rootfs->root->internal->name); + + shell_start(); +} diff --git a/Lab7/src/kernel/link.ld b/Lab7/src/kernel/link.ld new file mode 100644 index 000000000..a460f25b2 --- /dev/null +++ b/Lab7/src/kernel/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab7/src/kernel/mbox.c b/Lab7/src/kernel/mbox.c new file mode 100644 index 000000000..ca0729b2c --- /dev/null +++ b/Lab7/src/kernel/mbox.c @@ -0,0 +1,133 @@ +#include "peripherals/mbox_call.h" +#include "mbox_call.h" +#include "mini_uart.h" +#include "stdlib.h" + +void mbox_main() +{ + // get the board's unique serial number with a mailbox call + mbox[0] = 21 * 4; // length of the message + mbox[1] = MBOX_REQUEST; // this is a request message + + mbox[2] = MBOX_TAG_MODEL; + mbox[3] = 4; + mbox[4] = 0; // ?? + mbox[5] = 0; + + mbox[6] = MBOX_TAG_REVISION; + mbox[7] = 4; + mbox[8] = 0; // ?? + mbox[9] = 0; + + mbox[10] = MBOX_TAG_GETSERIAL; // get serial number command + mbox[11] = 8; // buffer size + mbox[12] = 8; // ?? + mbox[13] = 0; // clear output buffer + mbox[14] = 0; + + mbox[15] = MBOX_TAG_ARM_MEMORY; // get serial number command + mbox[16] = 8; // buffer size + mbox[17] = 8; // ?? + mbox[18] = 0; // clear output buffer + mbox[19] = 0; + + mbox[20] = MBOX_TAG_LAST; + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) + { + uart_send_string("Board model: "); + uart_hex(mbox[5]); + uart_send_string("\n"); + + uart_send_string("Board revision: "); + uart_hex(mbox[9]); + uart_send_string("\n"); + + uart_send_string("Serial number: "); + uart_hex(mbox[14]); + uart_hex(mbox[13]); + uart_send_string("\n"); + + uart_send_string("ARM memory base address: "); + uart_hex(mbox[18]); + uart_send_string("\n"); + + uart_send_string("ARM memory size: "); + uart_hex(mbox[19]); + uart_send_string("\n"); + } + else + { + uart_send_string("Unable to query serial!\n"); + } +} + +// lab7, ref: https://oscapstone.github.io/labs/lab7.html +char *framebuffer_init() +{ + unsigned int __attribute__((unused)) width, height, pitch, isrgb; /* dimensions and channel order */ + char *lfb; /* raw frame buffer address */ + + mbox[0] = 35 * 4; + mbox[1] = MBOX_REQUEST; + + mbox[2] = 0x48003; // set phy wh + mbox[3] = 8; + mbox[4] = 8; + mbox[5] = 1024; // FrameBufferInfo.width + mbox[6] = 768; // FrameBufferInfo.height + + mbox[7] = 0x48004; // set virt wh + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1024; // FrameBufferInfo.virtual_width + mbox[11] = 768; // FrameBufferInfo.virtual_height + + mbox[12] = 0x48009; // set virt offset + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // FrameBufferInfo.x_offset + mbox[16] = 0; // FrameBufferInfo.y.offset + + mbox[17] = 0x48005; // set depth + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // FrameBufferInfo.depth + + mbox[21] = 0x48006; // set pixel order + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB, not BGR preferably + + mbox[25] = 0x40001; // get framebuffer, gets alignment on request + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // FrameBufferInfo.pointer + mbox[29] = 0; // FrameBufferInfo.size + + mbox[30] = 0x40008; // get pitch + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // FrameBufferInfo.pitch + + mbox[34] = MBOX_TAG_LAST; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) + { + mbox[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + width = mbox[5]; // get actual physical width + height = mbox[6]; // get actual physical height + pitch = mbox[33]; // get number of bytes per line + isrgb = mbox[24]; // get the actual channel order + lfb = (void *)((unsigned long)mbox[28]); + return lfb; + } + else + { + printf("Error, framebuffer_init(), Unable to set screen resolution to 1024x768x32\r\n"); + return NULL; + } +} \ No newline at end of file diff --git a/Lab7/src/kernel/mbox_call.c b/Lab7/src/kernel/mbox_call.c new file mode 100644 index 000000000..d0626f0e8 --- /dev/null +++ b/Lab7/src/kernel/mbox_call.c @@ -0,0 +1,42 @@ +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" + +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) +{ + unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} \ No newline at end of file diff --git a/Lab7/src/kernel/my_signal.c b/Lab7/src/kernel/my_signal.c new file mode 100644 index 000000000..e50ee5e22 --- /dev/null +++ b/Lab7/src/kernel/my_signal.c @@ -0,0 +1,59 @@ +#include "thread.h" +#include "my_signal.h" +#include "syscall.h" +#include "page_alloc.h" +#include "stdlib.h" + +extern task_struct *get_current(); + +signal_handler signal_table[] = { + [0] = &sig_ignore, + [1] = &sig_ignore, + [2] = &sig_ignore, + [3] = &sig_ignore, + [4] = &sig_ignore, + [5] = &sig_ignore, + [6] = &sig_ignore, + [7] = &sig_ignore, + [8] = &sig_ignore, + [SIGKILL] = &sigkill_handler, +}; + +#define current get_current() + +void sig_ignore(int _) +{ + return; +} + +void sigkill_handler(int pid) +{ + kill(pid); + return; +} + +void sig_context_update(struct _trapframe *trapframe, void (*handler)()) +{ + signal_context *sig_context = (signal_context *)my_malloc(sizeof(signal_context)); + sig_context->trapframe = (struct _trapframe *)my_malloc(sizeof(struct _trapframe)); + sig_context->user_stack = my_malloc(MIN_PAGE_SIZE); + memcpy(sig_context->trapframe, trapframe, sizeof(struct _trapframe)); + + current->signal_context = sig_context; + + trapframe->x[30] = (unsigned long)&sig_return; + trapframe->elr_el1 = (unsigned long)handler; + trapframe->sp_el0 = (unsigned long)sig_context->user_stack + MIN_PAGE_SIZE; +} + +void sig_return(void) +{ + asm volatile( + "mov x8, 10\n" + "svc 0\n"); +} + +void sig_context_restore(struct _trapframe *trapframe) +{ + memcpy(trapframe, current->signal_context->trapframe, sizeof(struct _trapframe)); +} diff --git a/Lab7/src/kernel/page_alloc.c b/Lab7/src/kernel/page_alloc.c new file mode 100644 index 000000000..a7202e2bb --- /dev/null +++ b/Lab7/src/kernel/page_alloc.c @@ -0,0 +1,308 @@ +#include "page_alloc.h" +#include "math.h" +#include "stddef.h" +#include "stdlib.h" +#include "reserve_mem.h" +#include "dynamic_alloc.h" + +page_frame_node free_list[MAX_ORDER + 1]; +// int frame_array[TOTAL_NUM_PAGE]; // Why NOT use? no malloc to allocate new link list node +page_frame_node frame_array[TOTAL_NUM_PAGE]; +extern reserved_memory_block RMarray[100]; + +// initialize frame_array and free_list +void init_page_frame() +{ + // free_list + for (int i = 0; i <= MAX_ORDER; i++) + { + free_list[i].index = -1; // head of link list + free_list[i].next = NULL; + free_list[i].previous = NULL; + } + free_list[MAX_ORDER].next = &frame_array[0]; + + // frame_array + frame_array[0].index = 0; + frame_array[0].val = MAX_ORDER; + frame_array[0].addr = (void *)FREE_MEM_START; + frame_array[0].contiguous_head = -1; + frame_array[0].allocated_order = -1; + frame_array[0].next = &frame_array[0 + (1 << MAX_ORDER)]; + frame_array[0].previous = &free_list[MAX_ORDER]; + int previous_index = 0; + for (int i = 1; i < TOTAL_NUM_PAGE; i++) + { + frame_array[i].index = i; + frame_array[i].addr = (void *)FREE_MEM_START + i * MIN_PAGE_SIZE; + frame_array[i].contiguous_head = -1; + frame_array[i].allocated_order = -1; + if (i % (1 << MAX_ORDER) == 0) + { + frame_array[i].val = MAX_ORDER; + frame_array[i].next = NULL; + frame_array[i].previous = &frame_array[previous_index]; + frame_array[previous_index].next = &frame_array[i]; + previous_index = i; + } + else + { + frame_array[i].val = FREE_BUDDY; + frame_array[i].next = NULL; + frame_array[i].previous = NULL; + } + } + + return; +} + +void *my_malloc(int req_size) +{ + unsigned long ret = -1; + if (req_size < MAX_POOL_SIZE) + ret = (unsigned long)get_chunk(req_size); + else + ret = get_page_from_free_list(req_size, -1); + + if (ret == -1) + { + printf("my_malloc FAILED\n"); + return NULL; + } + else + { + if (req_size < MAX_POOL_SIZE) + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, (void *)ret); + return (void *)ret; + } + else + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, frame_array[ret].addr); + return frame_array[ret].addr; + } + } +} + +int get_page_from_free_list(int req_size, int who) +{ + int req_order = -1; + for (int i = 0; i <= MAX_ORDER; i++) + { + if (req_size <= MIN_PAGE_SIZE * pow(2, i)) + { + req_order = i; + break; + } + } + + int alloc_index = -1; + int alloc_order = req_order; + while (alloc_order <= MAX_ORDER) + { + if (free_list[alloc_order].next == NULL) // split high order + { + alloc_order++; + } + else + break; + } + if (alloc_order > MAX_ORDER) + return -1; + while (alloc_order > req_order) + { + // split high order + int removed_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + add_to_free_list(&free_list[alloc_order - 1], removed_index); + add_to_free_list(&free_list[alloc_order - 1], removed_index + pow(2, alloc_order - 1)); + frame_array[removed_index].val = alloc_order - 1; + frame_array[removed_index + (1 << (alloc_order - 1))].val = alloc_order - 1; + alloc_order--; + } + if (alloc_order != req_order) + return -1; + + // get require page + alloc_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + for (int i = 0; i < (1 << alloc_order); i++) + { + frame_array[alloc_index + i].val = ALLOCATED; + frame_array[alloc_index + i].next = NULL; + frame_array[alloc_index + i].previous = NULL; + frame_array[alloc_index + i].contiguous_head = alloc_index; + frame_array[alloc_index + i].allocated_order = alloc_order; + } + + // check the page if contains reserved memory + unsigned long start = (unsigned long)frame_array[alloc_index].addr; + unsigned long end = start + MIN_PAGE_SIZE * (1 << req_order); + int RM_index = check_contain_RM(start, end); + if (RM_index != 0) + { + // Need to change the page allocated + int new_alloc_index = get_page_from_free_list(req_size, who); + free_page_frame(alloc_index); + alloc_index = new_alloc_index; + } + +#ifdef DEBUG + debug(); +#endif + frame_array[alloc_index].chunk_order = who; + return alloc_index; +} + +// This does NOT modify frame_array value +void add_to_free_list(page_frame_node *head_node, int index) +{ + page_frame_node *iter = head_node; + while (iter->next != NULL) + iter = iter->next; + iter->next = &frame_array[index]; + frame_array[index].previous = iter; + frame_array[index].next = NULL; + + return; +} + +void remove_from_free_list(page_frame_node *node_to_be_removed) +{ + if (node_to_be_removed->next != NULL) + node_to_be_removed->next->previous = node_to_be_removed->previous; + node_to_be_removed->previous->next = node_to_be_removed->next; + + node_to_be_removed->next = NULL; + node_to_be_removed->previous = NULL; + + return; +} + +void put_back_to_free_list(int num_of_redundant_page, int index) // 從 index 開始有 num_of_redundant_page 個 free page +{ + int order_to_put = 0; + while (num_of_redundant_page >= (1 << order_to_put)) + order_to_put++; + order_to_put--; + add_to_free_list(&free_list[order_to_put], index); + frame_array[index].val = order_to_put; + if (num_of_redundant_page - (1 << order_to_put) != 0) + put_back_to_free_list(num_of_redundant_page - (1 << order_to_put), index + (1 << order_to_put)); + + return; +} + +int free_page_frame(int index) +{ + int contiguous_head = frame_array[index].contiguous_head; + if (contiguous_head != index) + { + printf("Please free the start page of this contiguous memory when allocated, which is index %d\n", contiguous_head); + return -1; + } + + // Check if buddy can merge + int allocated_order = frame_array[index].allocated_order; + int buddy_index = index ^ (1 << allocated_order); + if (frame_array[buddy_index].val == allocated_order) + { + // can merge + int merged_order = merge_buddy(&index, buddy_index, allocated_order); + if (buddy_index < index) + add_to_free_list(&free_list[merged_order], buddy_index); + else + add_to_free_list(&free_list[merged_order], index); + } + else + { + // can NOT merge + add_to_free_list(&free_list[allocated_order], index); + frame_array[index].val = allocated_order; + frame_array[index].contiguous_head = -1; + frame_array[index].allocated_order = -1; + for (int i = 1; i < (1 << allocated_order); i++) + { + frame_array[index + i].val = FREE_BUDDY; + frame_array[index + i].contiguous_head = -1; + frame_array[index + i].allocated_order = -1; + } + } + +#ifdef DEBUG + debug(); +#endif + + return 0; +} + +// return merged order, YES modify frame_array +int merge_buddy(int *index, int buddy, int order) +{ + // printf("[DEBUG] Merging buddy index = %d and buddy_index = %d, allocated_order = %d\n", *index, buddy, order); + if (order == MAX_ORDER) + return order; + + if (buddy < *index) + { + *index = buddy; + buddy = *index; // Find itself + } + + page_frame_node *iter = free_list[order].next; + while (iter->index != buddy) + iter = iter->next; + + remove_from_free_list(iter); + + frame_array[*index].val = order + 1; + for (int i = 1; i < (1 << (order + 1)); i++) + { + frame_array[i + *index].val = FREE_BUDDY; + } + + if (order + 1 == MAX_ORDER) + return order + 1; + + int new_buddy = *index ^ (1 << (order + 1)); + if (frame_array[*index].val == frame_array[new_buddy].val) + { + frame_array[buddy].val = FREE_BUDDY; + return merge_buddy(index, new_buddy, order + 1); + } + else + return order + 1; +} + +void debug() +{ + printf("** DEBUGGING free_list\n"); + for (int i = 0; i <= MAX_ORDER; i++) + { + page_frame_node *iter; + iter = &free_list[i]; + printf("free_list[%d] -> ", i); + while (iter->next != NULL) + { + iter = iter->next; + printf("index %d -> ", iter->index); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING frame_array\n"); + for (int i = 0; i < 20; i++) + { + printf("frame_array[%d].addr = %p\n", i, frame_array[i].addr); + printf("frame_array[%d].val = %d\n", i, frame_array[i].val); + printf("frame_array[%d].contiguous_head = %d\n", i, frame_array[i].contiguous_head); + printf("frame_array[%d].allocated_order = %d\n", i, frame_array[i].allocated_order); + if (frame_array[i].next != NULL) + printf("frame_array[%d].next->index = %d\n", i, frame_array[i].next->index); + if (frame_array[i].previous != NULL) + printf("frame_array[%d].previous->index = %d\n", i, frame_array[i].previous->index); + } + printf("**\n"); + + return; +} diff --git a/Lab7/src/kernel/read_cpio.c b/Lab7/src/kernel/read_cpio.c new file mode 100644 index 000000000..ef16f5106 --- /dev/null +++ b/Lab7/src/kernel/read_cpio.c @@ -0,0 +1,334 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "vfs.h" +#include "tmpfs.h" +#include "read_cpio.h" + +extern char *cpioDestGlobal; +cpio_node_t *cpio_list; + +typedef struct cpio_newc_header +{ + 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]; +} __attribute__((packed)) cpio_t; + +void read_cpio(char *cpioDest) +{ + uart_send_string("Type Offset Size Access rights\tFilename\n"); + + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + // print out meta information + uart_hex(hex2int(header->c_mode, 8)); // mode (access rights + type) + uart_send(' '); + uart_hex((unsigned int)((unsigned long)cpioDest) + sizeof(cpio_t) + ns); + uart_send(' '); + uart_hex(fs); // file size in hex + uart_send(' '); + uart_hex(hex2int(header->c_uid, 8)); // user id in hex + uart_send('.'); + uart_hex(hex2int(header->c_gid, 8)); // group id in hex + uart_send('\t'); + uart_send_string(cpioDest + sizeof(cpio_t)); // filename + uart_send_string("\n"); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } +} + +void read_content(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("cat: %s: No such file\n", filename); + return; + } + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + // print content + uart_send_string_of_size((char *)cpioDest, fs); +} + +char *find_content_addr(char *cpioDest, const char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), (char *)filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("[ERROR] find_content_addr: %s: No such file\n", filename); + return NULL; + } + + return cpioDest; +} + +int load_userprogram(const char *filename, char *userDest) +{ + char *cpioUserPgmDest = cpioDestGlobal; + cpioUserPgmDest = find_content_addr(cpioUserPgmDest, filename); + if (cpioUserPgmDest == NULL) + { + printf("[ERROR] FAIL to find %s\n", filename); + return -1; + } + + // Found target file + cpio_t *header = (cpio_t *)cpioUserPgmDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioUserPgmDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioUserPgmDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + printf("load %p to %p\n", cpioUserPgmDest, userDest); + printf("size: %d bytes\n", fs); + + // load content + while (fs--) + { + *userDest++ = *cpioUserPgmDest++; + } + + if (fs == -1) + return 0; + + return 1; +} + +/* For Lab7 */ +int get_size(cpio_t *root_addr, char *attr) +{ + char *temp_addr = (char *)root_addr; + + if (!strcmp(attr, "name")) + temp_addr += 94; + else if (!strcmp(attr, "file")) + temp_addr += 54; + + char size_string[9]; + for (int i = 0; i < 8; i++) + { + size_string[i] = temp_addr[i]; + } + + size_string[8] = '\0'; + + // hexadecimal to decimal + return hex2int(size_string, 8); +} + +int initramfs_mount(filesystem_t *fs, mount_t *mount) +{ + mount->root->v_ops = my_malloc(sizeof(vnode_operations_t)); + mount->root->v_ops->lookup = initramfs_lookup; + mount->root->v_ops->create = initramfs_create; + mount->root->v_ops->mkdir = initramfs_mkdir; + + mount->root->f_ops = my_malloc(sizeof(file_operations_t)); + mount->root->f_ops->write = initramfs_write; + mount->root->f_ops->read = initramfs_read; + mount->root->f_ops->open = initramfs_open; + mount->root->f_ops->close = initramfs_close; + + init_cpio(); + + cpio_node_t *tmp = cpio_list; + int i = 0; + while (tmp != NULL) + { + mount->root->internal->entry[i] = my_malloc(sizeof(vnode_t)); + mount->root->internal->entry[i]->mount = NULL; + mount->root->internal->entry[i]->v_ops = mount->root->v_ops; + mount->root->internal->entry[i]->f_ops = mount->root->f_ops; + + mount->root->internal->entry[i]->internal = my_malloc(sizeof(node_info_t)); + mount->root->internal->entry[i]->internal->name = my_malloc(COMPONENT_NAME_MAX); + strcpy(mount->root->internal->entry[i]->internal->name, tmp->name); + mount->root->internal->entry[i]->internal->type = tmp->type; + mount->root->internal->entry[i]->internal->size = tmp->size; + mount->root->internal->entry[i]->internal->data = tmp->data; + + mount->root->internal->size++; + + tmp = tmp->next; + i++; + } + + return 0; +} + +void init_cpio() +{ + cpio_t *current_file = (cpio_t *)cpioDestGlobal; + + int namesize = get_size(current_file, "name") - 1; + int filesize = get_size(current_file, "file"); + + char *temp_addr = (char *)(current_file + 1); + + char temp_name[30]; + + for (int i = 0; i < namesize; i++) + { + temp_name[i] = temp_addr[i]; + } + + temp_name[namesize] = '\0'; + + if (!strcmp(temp_name, "TRAILER!!!")) + return; + + cpio_list = my_malloc(sizeof(cpio_node_t)); + cpio_node_t *tmp = cpio_list; + + while (strcmp(temp_name, "TRAILER!!!")) + { + tmp->name = my_malloc(namesize + 10); + strcpy(tmp->name, temp_name); + tmp->type = FILE; + tmp->size = filesize; + + int NUL_nums = 1; + char *next_file = (char *)(current_file + 1); + + while ((2 + namesize + NUL_nums) % 4 != 0) + NUL_nums++; + + next_file += (namesize + NUL_nums); + tmp->data = next_file; + + NUL_nums = 0; + while ((filesize + NUL_nums) % 4 != 0) + NUL_nums++; + + next_file += (filesize + NUL_nums); + current_file = (cpio_t *)next_file; + + namesize = get_size(current_file, "name") - 1; + filesize = get_size(current_file, "file"); + + temp_addr = (char *)(current_file + 1); + + for (int i = 0; i < namesize; i++) + temp_name[i] = temp_addr[i]; + + temp_name[namesize] = '\0'; + + if (strcmp(temp_name, "TRAILER!!!")) + { + tmp->next = my_malloc(sizeof(cpio_node_t)); + tmp = tmp->next; + } + else + tmp->next = NULL; + } +} + +/* vnode operations : defined in tmpfs.h not in cpio.h */ +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + return tmpfs_lookup(dir_node, target, component_name); +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + printf("/initramfs is read-only!!\n"); + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + printf("/initramfs is read-only!!\n"); + return -1; +} + +/* file operations */ +int initramfs_write(struct file *file, void *buf, size_t len) +{ + printf("/initramfs is read-only!!\n"); + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) +{ + return tmpfs_read(file, buf, len); +} + +int initramfs_open(struct vnode *file_node, struct file **target) +{ + return tmpfs_open(file_node, target); +} + +int initramfs_close(struct file *file) +{ + return tmpfs_close(file); +} \ No newline at end of file diff --git a/Lab7/src/kernel/reboot.c b/Lab7/src/kernel/reboot.c new file mode 100644 index 000000000..da809c59a --- /dev/null +++ b/Lab7/src/kernel/reboot.c @@ -0,0 +1,19 @@ +#include "peripherals/reboot.h" + +void set(long addr, unsigned int value) +{ + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) +{ // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab7/src/kernel/reserved_mem.c b/Lab7/src/kernel/reserved_mem.c new file mode 100644 index 000000000..d4a5649c8 --- /dev/null +++ b/Lab7/src/kernel/reserved_mem.c @@ -0,0 +1,48 @@ +#include "reserve_mem.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "stdlib.h" + +reserved_memory_block RMarray[100]; +int RMindex = 0; + +void memory_reserve(unsigned long start, unsigned long end, char *name) +{ + RMindex++; + RMarray[RMindex].start = start; + RMarray[RMindex].end = end; + strcpy(RMarray[RMindex].name, name); +} + +// return value : if including RM, return which no. of RM. Otherwise, return 0. +int check_contain_RM(unsigned long start, unsigned long end) +{ + for (int i = 1; i <= RMindex; i++) + { + if (RMarray[i].start <= start && start <= RMarray[i].end) + return i; + else if (RMarray[i].start <= end && end <= RMarray[i].end) + return i; + else if (start <= RMarray[i].start && RMarray[i].end <= end) + return i; + else + continue; + } + return 0; +} + +void memory_init() +{ + init_page_frame(); + init_pool(); + + memory_reserve(0x0000, 0x5000, "PGD, PUD"); + memory_reserve(0x60000, 0x100000, "Kernel Img"); + memory_reserve(0x1000000, 0x100ffff, "Printf Buffer"); + memory_reserve(0x8000000, 0x8020000, "Initramfs"); + memory_reserve(0x15000000, 0x17000000, "User Program"); + memory_reserve(0x200000, 0x250000, "svc"); + + return; +} \ No newline at end of file diff --git a/Lab7/src/kernel/shell.c b/Lab7/src/kernel/shell.c new file mode 100644 index 000000000..0fa7c8951 --- /dev/null +++ b/Lab7/src/kernel/shell.c @@ -0,0 +1,239 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" +#include "timer.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "test.h" +#include "thread.h" +#include "vfs.h" + +extern void *_dtb_ptr; +extern char *cpioDestGlobal; +extern char read_buffer[100]; +// extern page_frame_node frame_array[TOTAL_NUM_PAGE]; +// extern chunk chunk_array[3000]; + +#define COMMAND_BUFFER 50 +#define FILENAME_BUFFER 20 +#define ARGV_BUFFER 30 + +void shell_start(); + +void shell_main(char *command) +{ + if (!strcmp(command, "help")) + { + uart_send_string("help\t: print this help menu\n"); + uart_send_string("hello\t: print Hello World!\n"); + uart_send_string("reboot\t: reboot the device\n"); + uart_send_string("ls\t: list information about files\n"); + uart_send_string("cd\t: \n"); + uart_send_string("cat\t: copy each FILE to standard output\n"); + uart_send_string("dts\t: list devicetree\n"); + uart_send_string("asynr\t: [test] asynchronous read\n"); + uart_send_string("asynw\t: [test] asynchronous write\n"); + uart_send_string("setTimeout\t: Usage: setTimeout \n"); + uart_send_string("alloc\t: [test] malloc and free\n"); + uart_send_string("thread\t: [test]\n"); + uart_send_string("syscall\t: [test]\n"); + uart_send_string("vfs1\t: [test] load lab7 user program\n"); + } + else if (!strcmp(command, "hello")) + { + uart_send_string("Hello World!\n"); + } + else if (!strcmp(command, "reboot")) + { + uart_send_string("Rebooting in 3 seconds\n"); + reset(3 << 16); + while (1) + ; + } + else if (!memcmp(command, "ls", 2)) + { + char pathname[256]; + if (strlen(command) == 2 || strlen(command) == 3) + vfs_ls(pathname, 0); + else + { + handle_path(command + 3, pathname); + vfs_ls(pathname, 1); + } + } + else if (!memcmp(command, "cat", 3)) + { + if (command[3] != ' ' || command[4] == '\0') + { + printf("Usage: cat \n"); + return; + } + + char filename[FILENAME_BUFFER]; + memset(filename, '\0', FILENAME_BUFFER); + int i = 4; + while (command[i] != '\0') + { + filename[i - 4] = command[i]; + i++; + } + + read_content((char *)cpioDestGlobal, filename); + } + else if (!strcmp(command, "dts")) + { + fdt_traverse(dtb_parser, _dtb_ptr); + } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x1, 0x0;" // not sure why can't use x0, may have something to to with %0 + "msr spsr_el1, x1;" + "mov x2, %0;" + "add x2, x2, 12;" + "msr elr_el1, x2;" + "mov x2, 0x15000000;" + "msr sp_el0, x2;" + "mrs x2, cntfrq_el0;" + "add x2, x2, x2;" + "msr cntp_tval_el0, x2;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } + else if (!strcmp(command, "asynr")) + { + asyn_read(); + } + else if (!strcmp(command, "asynw")) + { + asyn_write(read_buffer); + uart_send('\n'); + } + else if (!memcmp(command, "setTimeout", 10)) + { + if (command[10] != ' ' || command[11] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char message[MESSAGE_BUFFER]; + memset(message, '\0', MESSAGE_BUFFER); + int i = 11; + while (command[i] != ' ' && command[i] != '\0') + { + message[i - 11] = command[i]; + i++; + } + + if (command[i] != ' ' || command[i] == '\0' || command[i + 1] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char seconds[SECONDS_BUFFER]; + memset(seconds, '\0', SECONDS_BUFFER); + while (command[i] != '\0') + { + seconds[i - strlen(message) - 1 - 11] = command[i]; + i++; + } + int sec; + sec = atoi(seconds); + + add_timer(sec, message); + } + else if (!memcmp(command, "alloc", 5)) + { + test_mem_alloc(); + } + else if (!memcmp(command, "debug", 5)) + { + debug(); + debug_pool(); + } + else if (!memcmp(command, "thread", 6)) + { + test_thread(); + } + else if (!strcmp(command, "syscall")) + { + thread_create(load_usrpgm_in_umode); + idle_task(); + } + else if (!strcmp(command, "vfs1")) + { + thread_create(load_vfs1_usrpgm_in_umode); + idle_task(); + } + else if (!memcmp(command, "cd", 2)) + { + char pathname[256]; + handle_path(command + 3, pathname); + vfs_cd(pathname); + } + else if (!strcmp(command, "cwd")) + { + printf("%s\n", cwdpath); + } + + return; +} + +void shell_start() +{ + uart_send_string("Starting shell...\n"); + char c; + int i = 0; + + char command[COMMAND_BUFFER]; + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("$ "); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + command[i] = '\0'; + uart_send(c); + shell_main(command); + memset(command, '\0', COMMAND_BUFFER); + i = 0; + uart_send_string("$ "); + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else if (c == 127) // Also backspace but delete + { + } + else + { + if (i < COMMAND_BUFFER) + { + if (c == 0) // solve the problem that first command on board wont work + continue; + command[i++] = c; + } + uart_send(c); + } + } + } +} diff --git a/Lab7/src/kernel/syscall.c b/Lab7/src/kernel/syscall.c new file mode 100644 index 000000000..b699306b5 --- /dev/null +++ b/Lab7/src/kernel/syscall.c @@ -0,0 +1,245 @@ +#include "stdlib.h" +#include "thread.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "read_cpio.h" +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" +#include "my_signal.h" +#include "vfs.h" +#include "reserve_mem.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); +extern void switch_to(task_struct *, task_struct *); +extern task_struct kernel_thread; +extern struct list_head task_rq_head; +extern struct list_head task_zombieq_head; + +int getpid() +{ + int ret = get_current()->thread_info->id; + return ret; +} + +size_t uart_read(char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + buf[i] = uart_recv(); + if (buf[i] == '\n' || buf[i] == '\r') + return i; + } + return size; +} + +size_t uart_write(const char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + if (buf[i] == '\0') + return i; + uart_send(buf[i]); + } + return size; +} + +int exec(const char *name, char *const argv[]) +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + file_t *fh = NULL; + int ret = vfs_open((char *)name, 0, &fh); + if (ret == 0) + { + fh->f_ops->read(fh, (void *)target_addr, USRPGM_SIZE); + fh->f_ops->close(fh); + } + else + return -1; + + asm volatile( + "mov x0, 0x0\n\t" // EL0t + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); + + return 0; +} + +int fork() +{ + schedule(); + return get_current()->thread_info->child_id; +} + +void exit(int status) +{ + task_struct *current = (task_struct *)get_current(); + current->status = ZOMBIE; + INIT_LIST_HEAD(¤t->list); + list_add_tail(¤t->list, &task_zombieq_head); + // schedule(); + switch_to(current, &kernel_thread); +} + +int mbox_call_u(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 (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} + +void kill(int pid) +{ + task_struct *tmp = get_current(); + if (tmp->thread_info->id == pid) + exit(0); + + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + tmp = container_of(iter, task_struct, list); + if (tmp->thread_info->id == pid) + { + tmp->status = ZOMBIE; + return; + } + } +} + +void signal(int SIGNAL, void (*handler)()) +{ + printf("[info] Called signal()\n"); + task_struct *cur = get_current(); + + custom_signal *new = (custom_signal *)my_malloc(sizeof(custom_signal)); + new->sig_num = SIGNAL; + new->handler = handler; + INIT_LIST_HEAD(&new->list); + + if (!cur->custom_signal) + cur->custom_signal = new; + else + list_add_tail(&cur->custom_signal->list, &new->list); +} + +int open(char *pathname, int flags) +{ + task_struct *cur = get_current(); + int fd = thread_get_idle_fd(cur); + if (fd < 0) + { + printf("Error, priv_open(), cannot open more file for pid=%d\r\n", cur->thread_info->id); + return -1; + } + + file_t *target; + int ret = vfs_open(pathname, flags, &target); + + if (ret != 0) + return ret; + + cur->fd_table[fd] = target; + + return fd; +} + +int close(int fd) +{ + task_struct *cur = get_current(); + int ret = vfs_close(cur->fd_table[fd]); + return ret; +} + +long write(int fd, void *buf, unsigned long count) +{ + task_struct *cur = get_current(); + int ret = vfs_write(cur->fd_table[fd], buf, count); + return ret; +} + +long read(int fd, void *buf, unsigned long count) +{ + task_struct *cur = get_current(); + int ret = vfs_read(cur->fd_table[fd], buf, count); + return ret; +} + +int mkdir(char *pathname, unsigned mode) +{ + int ret = vfs_mkdir(pathname); + return ret; +} + +int mount(char *src, char *target, char *filesystem, unsigned long flags, void *data) +{ + int ret = vfs_mount(target, filesystem); + return ret; +} + +int chdir(char *path) +{ + char pathname[256]; + handle_path(path, pathname); + int ret = vfs_cd(pathname); + return ret; +} + +long lseek(int fd, long offset, int whence) +{ + task_struct *cur = get_current(); + int ret = vfs_lseek(cur->fd_table[fd], offset, whence); + return ret; +} + +int ioctl(int fd, unsigned long request, unsigned long arg) +{ + task_struct *cur = get_current(); + int ret = vfs_ioctl(cur->fd_table[fd], request, arg); + return ret; +} \ No newline at end of file diff --git a/Lab7/src/kernel/test.c b/Lab7/src/kernel/test.c new file mode 100644 index 000000000..36c9909a8 --- /dev/null +++ b/Lab7/src/kernel/test.c @@ -0,0 +1,116 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "thread.h" +#include "syscall.h" +#include "read_cpio.h" +#include "utils.h" +#include "timer.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); + +void test_mem_alloc() +{ + void *a; + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + return; +} + +void foo() +{ + for (int i = 0; i < 10; ++i) + { + task_struct *cur = get_current(); + printf("Thread id: %d %d\n", cur->thread_info->id, i); + delay(1000000); + schedule(); + } +} + +void test_thread() +{ + int N = 5; + for (int i = 0; i < N; ++i) + { // N should > 2 + thread_create(foo); + } + + // thread_create(load_usrpgm_in_umode); + idle_task(); + + debug_task_rq(); + return; +} + +void load_usrpgm_in_umode() +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + load_userprogram("syscall.img", (char *)target_addr); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} + +void load_vfs1_usrpgm_in_umode() +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + load_userprogram("vfs1.img", (char *)target_addr); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} \ No newline at end of file diff --git a/Lab7/src/kernel/thread.S b/Lab7/src/kernel/thread.S new file mode 100644 index 000000000..ffc3ece7b --- /dev/null +++ b/Lab7/src/kernel/thread.S @@ -0,0 +1,31 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +.global kernel_thread_init +kernel_thread_init: + msr tpidr_el1, x0 + ret \ No newline at end of file diff --git a/Lab7/src/kernel/thread.c b/Lab7/src/kernel/thread.c new file mode 100644 index 000000000..f3f40d788 --- /dev/null +++ b/Lab7/src/kernel/thread.c @@ -0,0 +1,281 @@ +#include "stdlib.h" +#include "thread.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "list.h" +#include "syscall.h" +#include "vfs.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void switch_to(task_struct *, task_struct *); +extern void kernel_thread_init(); + +long thread_cnt = 0; + +task_struct kernel_thread = {0}; +struct list_head task_rq_head; // run queue +struct list_head task_zombieq_head; // zombie queue + +#define FD_STDIN 0 +#define FD_STDOUT 1 +#define FD_STDERR 2 + +void schedule() +{ + task_struct *cur = get_current(); + task_struct *next = del_rq(); + + if (next == NULL) + next = &kernel_thread; + if (cur != &kernel_thread) + add_rq(cur); + + set_switch_timer(); + enable_interrupt(); + + if (next->status == FORKING) + { + add_rq(next); + switch_to(cur, &kernel_thread); + } + else if (next->status == ZOMBIE) + { + INIT_LIST_HEAD(&next->list); + list_add_tail(&next->list, &task_zombieq_head); + switch_to(cur, &kernel_thread); + } + else + { + switch_to(cur, next); + } +} + +void add_rq(task_struct *task) +{ + INIT_LIST_HEAD(&task->list); + list_add_tail(&task->list, &task_rq_head); +} + +task_struct *del_rq() +{ + struct list_head *ret; + ret = task_rq_head.next; + if (ret != &task_rq_head) + { + list_del_init(ret); + return container_of(ret, task_struct, list); + } + else + return NULL; +} + +void thread_init() +{ + INIT_LIST_HEAD(&task_rq_head); + INIT_LIST_HEAD(&task_zombieq_head); + kernel_thread_init(&kernel_thread); + return; +} + +thread_info *thread_create(func_ptr fp) +{ + task_struct *new_task = (task_struct *)my_malloc(sizeof(task_struct)); + thread_info *new_thread = (thread_info *)my_malloc(sizeof(thread_info)); + + new_task->thread_info = new_thread; + new_thread->task = new_task; + + new_task->kstack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); + new_task->ustack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); + new_task->usrpgm_load_addr = USRPGM_BASE + thread_cnt * USRPGM_SIZE; + + new_task->task_context.fp = new_task->kstack_start + MIN_PAGE_SIZE; + new_task->task_context.lr = (unsigned long)task_wrapper; + new_task->task_context.sp = new_task->kstack_start + MIN_PAGE_SIZE; + new_task->thread_info->id = thread_cnt++; + new_task->status = READY; + new_task->job = fp; + new_task->custom_signal = NULL; + + // Open stdin, stdout, stderr for the process + file_t *fh = NULL; + vfs_open("/dev/uart", 0, &fh); + new_task->fd_table[FD_STDIN] = fh; + vfs_open("/dev/uart", 0, &fh); + new_task->fd_table[FD_STDOUT] = fh; + vfs_open("/dev/uart", 0, &fh); + new_task->fd_table[FD_STDERR] = fh; + + add_rq(new_task); + + return new_thread; +} + +/* threads' routine for any task */ +void task_wrapper() +{ + task_struct *current = get_current(); + (current->job)(); + exit(0); +} + +void idle_task() +{ + while (!list_empty(&task_rq_head) || !list_empty(&task_zombieq_head)) + { + disable_interrupt(); + kill_zombies(); + do_fork(); + enable_interrupt(); + schedule(); + } +} + +/* kill the zombie threads and recycle their resources */ +void kill_zombies() +{ + struct list_head *iter = &task_zombieq_head; + struct list_head *start = &task_zombieq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + free((void *)tmp->kstack_start); + free((void *)tmp->ustack_start); + free(tmp->thread_info); + if (tmp->custom_signal) + free(tmp->custom_signal); + free(tmp); + } + INIT_LIST_HEAD(&task_zombieq_head); + return; +} + +void do_fork() +{ + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + if (tmp->status == FORKING) + create_child(tmp); + } +} + +/* copy all data including stack and program for child process and set the corresponding sp and lr for it*/ +void create_child(task_struct *parent) +{ + thread_info *child_thread = thread_create(0); + task_struct *child = child_thread->task; + + char *parent_d, *child_d; + + parent->status = READY; + + parent->thread_info->child_id = child->thread_info->id; + child->thread_info->child_id = 0; + + // copy context + parent_d = (char *)&(parent->task_context); + child_d = (char *)&(child->task_context); + for (int i = 0; i < sizeof(context); i++) + child_d[i] = parent_d[i]; + + // copy custom_signal + if (parent->custom_signal) + { + child->custom_signal = (custom_signal *)my_malloc(sizeof(custom_signal)); + parent_d = (char *)&(parent->custom_signal); + child_d = (char *)&(child->custom_signal); + for (int i = 0; i < sizeof(custom_signal); i++) + child_d[i] = parent_d[i]; + } + + // copy kernel stack + parent_d = (char *)parent->kstack_start; + child_d = (char *)child->kstack_start; + for (int i = 0; i < MIN_PAGE_SIZE; i++) + child_d[i] = parent_d[i]; + + // copy user stack + parent_d = (char *)parent->ustack_start; + child_d = (char *)child->ustack_start; + for (int i = 0; i < MIN_PAGE_SIZE; i++) + child_d[i] = parent_d[i]; + + // copy user program + parent_d = (char *)parent->usrpgm_load_addr; + child_d = (char *)child->usrpgm_load_addr; + for (int i = 0; i < USRPGM_SIZE; i++) + child_d[i] = parent_d[i]; + + // set offset to child's stack + unsigned long kstack_offset = child->kstack_start - parent->kstack_start; + unsigned long ustack_offset = child->ustack_start - parent->ustack_start; + unsigned long usrpgm_offset = child->usrpgm_load_addr - parent->usrpgm_load_addr; + + // set child kernel space offset + child->task_context.fp += kstack_offset; + child->task_context.sp += kstack_offset; + child->trapframe = parent->trapframe + kstack_offset; + + // set child user space offset + trapframe *ctrapframe = (trapframe *)child->trapframe; // because of data type problem + ctrapframe->x[29] += ustack_offset; + ctrapframe->sp_el0 += ustack_offset; + ctrapframe->elr_el1 += usrpgm_offset; +} + +void debug_task_rq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_rq_head; + start = &task_rq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_rq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} + +void debug_task_zombieq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_zombieq_head; + start = &task_zombieq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_zombieq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} + +int thread_get_idle_fd(task_struct *thd) +{ + for (int i = 0; i < VFS_PROCESS_MAX_OPEN_FILE; i++) + { + if (thd->fd_table[i] == NULL) + return i; + } + return -1; +} \ No newline at end of file diff --git a/Lab7/src/kernel/timer.S b/Lab7/src/kernel/timer.S new file mode 100644 index 000000000..a22d66ec4 --- /dev/null +++ b/Lab7/src/kernel/timer.S @@ -0,0 +1,32 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + mrs x2, cntkctl_el1 // following three instructions for Lab5 to access cpu timer register + orr x2, x2, #0x1 + msr cntkctl_el1, x2 + ret + +.global core_timer_disable +core_timer_disable: + mov x0, 0 + msr cntp_ctl_el0, x0 // disable + ret + +.global set_switch_timer +set_switch_timer: + mrs x0, cntfrq_el0 + mov x0, x0, lsr#5 + msr cntp_tval_el0, x0 + ret + +/* +cntpct_el0: The timer’s current count. +cntp_cval_el0: A compared timer count. If cntpct_el0 >= cntp_cval_el0, interrupt the CPU core. +cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +*/ \ No newline at end of file diff --git a/Lab7/src/kernel/timer.c b/Lab7/src/kernel/timer.c new file mode 100644 index 000000000..5e8c42f58 --- /dev/null +++ b/Lab7/src/kernel/timer.c @@ -0,0 +1,176 @@ +#include "stdlib.h" +#include "timer.h" +#include "thread.h" + +extern void enable_interrupt(); +extern void disable_interrupt(); + +typedef struct timer_queue_node +{ + long second_ticks; + char message[MESSAGE_BUFFER]; +} tq; + +tq timer_queue[10]; +int timer_queue_front = 0; +int timer_queue_back = 0; + +void get_current_time() +{ + long cntpct_el0, cntfrq_el0; + long cntp_tval_el0; + long cntp_cval_el0; + + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + "mrs %2, cntp_tval_el0;" + "mrs %3, cntp_cval_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0), "=r"(cntp_tval_el0), "=r"(cntp_cval_el0)); + + long nowtime = cntpct_el0 / cntfrq_el0; + + printf("%ld seconds after booting\n", nowtime); + + // printf("cntpct_el0 = %d\n", cntpct_el0); + // printf("cntfrq_el0 = %d\n", cntfrq_el0); + // printf("cntp_tval_el0 = %d\n", cntp_tval_el0); + // printf("cntp_cval_el0 = %d\n", cntp_cval_el0); + + return; +} + +void add_timer(int sec, char *mes) +{ + get_current_time(); + + for (int i = 0; i < MESSAGE_BUFFER; i++) + timer_queue[timer_queue_back].message[i] = mes[i]; + + // transfer sec to frq and store to node.second + asm volatile( + "msr DAIFSet, 0xf;" + "mrs x3, cntfrq_el0;" + "mrs x4, cntpct_el0;" + "mov x2, %1;" // after secs seconds later will interrupt + "mul x2, x2, x3;" + "add x2, x2, x4;" + "mov %0, x2;" + : "=r"(timer_queue[timer_queue_back].second_ticks) + : "r"(sec) + : "x0", "x1", "x2", "x3", "x4", "memory"); // Uses register operands x0, x1, x2, x3, and x4 and specifies that the instruction may modify memory using the clobber list "x0", "x1", "x2", "x3", "x4", "memory". + + timer_queue_back++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + + asm volatile( + "msr cntp_cval_el0, %0;" + "bl core_timer_enable;" + "msr DAIFClr, 0xf;" + : + : "r"(timer_queue[timer_queue_front].second_ticks)); +} + +void el0_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + disable_interrupt(); + schedule(); + enable_interrupt(); +} + +void el1_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + if (!is_timer_queue_empty()) + { + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + printf("\n"); + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + printf("Message: %s\n", timer_queue[timer_queue_front].message); + + timer_queue_front++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + asm volatile( + "msr cntp_cval_el0, %0\n\t" // set expired time + "bl core_timer_enable\n\t" + : + : "r"(timer_queue[timer_queue_front].second_ticks) + :); + } + else + { + disable_interrupt(); + schedule(); + enable_interrupt(); + } + return; +} + +int is_timer_queue_empty() +{ + return timer_queue_front == timer_queue_back ? 1 : 0; +} + +void timer_delay(long seconds) +{ + long cntpct_el0, cntfrq_el0, nowtime, due; + + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + + due = cntpct_el0 / cntfrq_el0 + seconds; + nowtime = cntpct_el0 / cntfrq_el0; + + while (nowtime <= due) + { + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + nowtime = cntpct_el0 / cntfrq_el0; + } +} \ No newline at end of file diff --git a/Lab7/src/kernel/tmpfs.c b/Lab7/src/kernel/tmpfs.c new file mode 100644 index 000000000..948a59fa5 --- /dev/null +++ b/Lab7/src/kernel/tmpfs.c @@ -0,0 +1,138 @@ +#include "stdlib.h" +#include "vfs.h" +#include "tmpfs.h" + +int tmpfs_mount(struct filesystem *fs, struct mount *mount) +{ + mount->root = my_malloc(sizeof(vnode_t)); + mount->root->mount = NULL; + + mount->root->v_ops = my_malloc(sizeof(vnode_operations_t)); + mount->root->v_ops->lookup = tmpfs_lookup; + mount->root->v_ops->create = tmpfs_create; + mount->root->v_ops->mkdir = tmpfs_mkdir; + + mount->root->f_ops = my_malloc(sizeof(file_operations_t)); + mount->root->f_ops->write = tmpfs_write; + mount->root->f_ops->read = tmpfs_read; + mount->root->f_ops->open = tmpfs_open; + mount->root->f_ops->close = tmpfs_close; + + mount->root->internal = my_malloc(sizeof(node_info_t)); + mount->root->internal->name = my_malloc(strlen("/") + 1); + strcpy(mount->root->internal->name, "/"); + + mount->root->internal->type = DIR; + mount->root->internal->size = 0; + mount->root->internal->entry = my_malloc(sizeof(vnode_t *) * MAX_NUM_OF_ENTRY); + + return 0; +} + +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + if (dir_node->internal->type != DIR) + { + printf("'%s' is not a directory.\n", dir_node->internal->name); + return NOTDIR; + } + + for (int i = 0; i < dir_node->internal->size; i++) + { + if (!strcmp(dir_node->internal->entry[i]->internal->name, component_name)) + { + *target = dir_node->internal->entry[i]; + return EXISTED; + } + } + + // printf("'%s' is not exist. in lookup()\n", component_name); + return NOTFOUND; +} + +int tmpfs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + if (dir_node->internal->size == MAX_NUM_OF_ENTRY) + { + printf("'%s' has no more entries.\n", dir_node->internal->name); + return -1; + } + + *target = my_malloc(sizeof(vnode_t)); + (*target)->mount = NULL; + (*target)->v_ops = dir_node->v_ops; + (*target)->f_ops = dir_node->f_ops; + + (*target)->internal = my_malloc(sizeof(node_info_t)); + (*target)->internal->name = my_malloc(COMPONENT_NAME_MAX); + strcpy((*target)->internal->name, component_name); + (*target)->internal->type = FILE; + (*target)->internal->size = 0; + (*target)->internal->entry = NULL; // mkdir will my_malloc() + (*target)->internal->data = NULL; // open will my_malloc() + + dir_node->internal->entry[dir_node->internal->size] = *target; + dir_node->internal->size++; + + return 0; +} + +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + int ret = tmpfs_create(dir_node, target, component_name); + if (ret == 0) + { + (*target)->internal->type = DIR; + (*target)->internal->entry = my_malloc(sizeof(vnode_t *) * MAX_NUM_OF_ENTRY); + return 0; + } + else + return -1; +} + +int tmpfs_write(struct file *file, void *buf, size_t len) +{ + size_t writeable_size = TMPFS_FILE_SIZE_MAX - file->f_pos; + size_t write_len = len <= writeable_size ? len : writeable_size; + + char *buf_ = (char *)buf; + for (int i = 0; i < write_len; i++) + file->vnode->internal->data[file->f_pos + i] = buf_[i]; + + file->vnode->internal->size += write_len; + file->f_pos += write_len; + + return write_len; +} + +int tmpfs_read(struct file *file, void *buf, size_t len) +{ + size_t readable_size = file->vnode->internal->size - file->f_pos; + size_t read_len = len <= readable_size ? len : readable_size; + + char *buf_ = (char *)buf; + for (int i = 0; i < read_len; i++) + buf_[i] = file->vnode->internal->data[file->f_pos + i]; + + file->f_pos += read_len; + + return read_len; +} + +int tmpfs_open(struct vnode *file_node, struct file **target) +{ + if (file_node->internal->data == NULL) + file_node->internal->data = my_malloc(sizeof(char) * TMPFS_FILE_SIZE_MAX); + *target = my_malloc(sizeof(file_t)); + (*target)->vnode = file_node; + (*target)->f_pos = 0; + (*target)->f_ops = file_node->f_ops; + + return 0; +} + +int tmpfs_close(struct file *file) +{ + free(file); + return 0; +} \ No newline at end of file diff --git a/Lab7/src/kernel/vfs.c b/Lab7/src/kernel/vfs.c new file mode 100644 index 000000000..bebc19f19 --- /dev/null +++ b/Lab7/src/kernel/vfs.c @@ -0,0 +1,490 @@ +#include "vfs.h" +#include "tmpfs.h" +#include "devfs.h" +#include "stdlib.h" + +filesystem_t *fs_list[50]; +unsigned int fs_count = 0; +struct mount *rootfs; +char cwdpath[256]; +file_t *kfd[16]; +int fd_count = 0; + +int register_filesystem(struct filesystem *fs) +{ + // register the file system to the kernel. + for (int i = 0; i < fs_count; i++) + { + if (!strcmp(fs->name, fs_list[i]->name)) + return -1; + } + + fs_list[fs_count] = fs; + fs_count++; + + return 0; + // you can also initialize memory pool of the file system here. +} + +int vfs_mount(char *target, char *filesystem) +{ + if (!strcmp(target, "/")) + { + rootfs = my_malloc(sizeof(mount_t)); + rootfs->fs = my_malloc(sizeof(filesystem_t)); + rootfs->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(rootfs->fs->name, filesystem); + rootfs->fs->setup_mount = tmpfs_mount; + register_filesystem(rootfs->fs); + return rootfs->fs->setup_mount(rootfs->fs, rootfs); + } + else if (!strcmp(target, "/initramfs")) + { + vnode_t *dir_node = NULL; + + vfs_mkdir(target); + vfs_lookup(target, &dir_node); + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + dir_node->mount->fs->setup_mount = initramfs_mount; + register_filesystem(dir_node->mount->fs); + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + else if (!strcmp(target, "/dev")) + { + vnode_t *dir_node = NULL; + + vfs_mkdir(target); + vfs_lookup(target, &dir_node); + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + dir_node->mount->fs->setup_mount = devfs_setup_mount; + register_filesystem(dir_node->mount->fs); + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + else + { + vnode_t *dir_node = NULL; + + switch (vfs_lookup(target, &dir_node)) + { + case EXISTED: + if (dir_node->internal->type != DIR) + { + printf("%s is not a directory.\n", target); + return -1; + } + break; + case NOTDIR: + return -1; + case NOTFOUND: + printf("%s is not existed.\n", target); + return -1; + } + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + // if mounting to a existed directory, all original content will not be used temporarily. When unmounted, will recover the contents. + // TODO : keep original "vnode->internal", use new one replace it. And change v_ops & f_ops to new filesystem's + if (register_filesystem(dir_node->mount->fs) == -1) + { + char dir_name[COMPONENT_NAME_MAX]; + strcpy(dir_name, dir_node->internal->name); + + dir_node->internal = my_malloc(sizeof(node_info_t)); + dir_node->internal->name = my_malloc(strlen(dir_name) + 1); + strcpy(dir_node->internal->name, dir_name); + + dir_node->internal->type = DIR; + dir_node->internal->size = 0; + dir_node->internal->entry = my_malloc(sizeof(vnode_t *) * MAX_NUM_OF_ENTRY); + + return 0; + } + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + + return -1; +} + +int vfs_lookup(char *pathname, struct vnode **target) +{ + char abs_pathname[PATHNAME_MAX]; + handle_path(pathname, abs_pathname); + + if (!strcmp("/", abs_pathname)) + { + *target = rootfs->root; + return 0; + } + + vnode_t *vnode = rootfs->root; + int level = 0; + char next_component[COMPONENT_NAME_MAX]; + int total_level = strnchr(abs_pathname, '/'); + + while (level < total_level) + { + get_next_component(abs_pathname, next_component, level); + + vnode_t *next_node = NULL; + int ret = vnode->v_ops->lookup(vnode, &next_node, next_component); + + if (ret != EXISTED) + return ret; + + vnode = next_node; + level++; + } + + *target = vnode; + + return EXISTED; +} + +int vfs_create(char *pathname) +{ + vnode_t *dir_node = NULL; + char abs_pathname[PATHNAME_MAX]; + + handle_path(pathname, abs_pathname); + + switch (vfs_lookup(abs_pathname, &dir_node)) + { + case EXISTED: + printf("%s is existed.\n", abs_pathname); + return -1; + case NOTDIR: + return -1; + } + + char target_component[COMPONENT_NAME_MAX]; + char dir_pathname[PATHNAME_MAX]; + + basename(abs_pathname, target_component); + dirname(abs_pathname, dir_pathname); + if (vfs_lookup(dir_pathname, &dir_node) == NOTFOUND) + { + printf("%s not found.\n", dir_pathname); + return -1; + } + + vnode_t *target = NULL; + + int ret = dir_node->v_ops->create(dir_node, &target, target_component); + + if (ret != 0) + return ret; + + return 0; +} + +int vfs_mkdir(char *pathname) +{ + vnode_t *dir_node = NULL; + char abs_pathname[PATHNAME_MAX]; + + handle_path(pathname, abs_pathname); + + switch (vfs_lookup(abs_pathname, &dir_node)) + { + case EXISTED: + printf("%s is existed.\n", abs_pathname); + return -1; + case NOTDIR: + return -1; + } + + char target_component[COMPONENT_NAME_MAX]; + char dir_pathname[PATHNAME_MAX]; + + basename(abs_pathname, target_component); + dirname(abs_pathname, dir_pathname); + if (vfs_lookup(dir_pathname, &dir_node) == NOTFOUND) + { + printf("%s not found.\n", dir_pathname); + return -1; + } + + vnode_t *target = NULL; + int ret = dir_node->v_ops->mkdir(dir_node, &target, target_component); + + if (ret != 0) + return ret; + + return 0; +} + +int vfs_open(char *pathname, int flags, struct 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 + vnode_t *target_node = NULL; + char abs_pathname[PATHNAME_MAX]; + + handle_path(pathname, abs_pathname); + + int ret = vfs_lookup(abs_pathname, &target_node); + + if (ret == EXISTED && target_node->internal->type == FILE) + { + return target_node->f_ops->open(target_node, target); + } + else if (ret == EXISTED && target_node->internal->type == DIR) + { + printf("%s is not a file.\n", abs_pathname); + return -1; + } + else if (ret == NOTFOUND && (flags & O_CREAT)) + { + if (vfs_create(abs_pathname) != 0) + return -1; + vfs_lookup(abs_pathname, &target_node); + return target_node->f_ops->open(target_node, target); + } + else if (ret == NOTFOUND) + { + printf("[DEBUG/vfs_open] %s is not existed.\n", abs_pathname); + return -1; + } + else + return -1; +} + +int vfs_close(struct file *file) +{ + // 1. release the file handle + // 2. Return error code if fails + if (file == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->close(file); +} + +int vfs_write(struct file *file, 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 == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->write(file, buf, len); +} + +int vfs_read(struct file *file, void *buf, size_t len) +{ + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 3. return read size or error code if an error occurs. + if (file == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->read(file, buf, len); +} + +void get_next_component(char *pathname, char *target, int level) +{ + int level_count = -1; + int target_begin = 0; + while (level_count != level) + { + if (pathname[target_begin] == '/') + level_count++; + target_begin++; + } + + int i = 0; + while (pathname[target_begin + i] != '/' && pathname[target_begin + i] != '\0') + { + target[i] = pathname[target_begin + i]; + i++; + } + + target[i] = '\0'; +} + +void basename(char *src, char *des) +{ + int level = 0; + int total_level = strnchr(src, '/'); + + if (!strcmp(src, "/")) + { + strcpy(des, "/"); + return; + } + + while (level < total_level) + { + get_next_component(src, des, level); + level++; + } +} + +void dirname(char *src, char *des) +{ + int end = -1; + + for (int i = 0; i < strlen(src); i++) + if (src[i] == '/') + end = i; + + if (end == 0) + strcpy(des, "/"); + else + strncpy(src, des, end); +} + +void handle_path(char *rela, char *abso) +{ + if (rela[0] == '/') + { + strcpy(abso, rela); + if (abso[strlen(abso) - 1] == '/' && strlen(abso) != 1) + abso[strlen(abso) - 1] = '\0'; + return; + } + + strcpy(abso, cwdpath); + char next_component[COMPONENT_NAME_MAX]; + + int i = 0, j = 0; + while (rela[i] != '\0') + { + if (rela[i] != '/') + { + next_component[j] = rela[i]; + j++; + } + else if (rela[i] == '/' && i == strlen(rela) - 1) + break; + else + { + next_component[j] = '\0'; + j = 0; + + if (!strcmp(next_component, ".")) + ; + else if (!strcmp(next_component, "..")) + dirname(abso, abso); + else + { + if (abso[strlen(abso) - 1] != '/') + strcat(abso, "/"); + strcat(abso, next_component); + } + } + + i++; + } + + next_component[j] = '\0'; + + if (!strcmp(next_component, ".")) + return; + else if (!strcmp(next_component, "..")) + dirname(abso, abso); + else + { + if (abso[strlen(abso) - 1] != '/') + strcat(abso, "/"); + strcat(abso, next_component); + } +} + +void vfs_ls(char *pathname, int flag) +{ + vnode_t *target_vnode; + int ret; + + if (flag) + ret = vfs_lookup(pathname, &target_vnode); + else + ret = vfs_lookup(cwdpath, &target_vnode); + + switch (ret) + { + case NOTFOUND: + printf("%s is not existed.\n", pathname); + return; + case NOTDIR: + return; + } + + if (target_vnode->internal->type != DIR) + { + printf("%s is not a directory.\n", pathname); + return; + } + + for (int i = 0; i < target_vnode->internal->size; i++) + { + printf("%s", target_vnode->internal->entry[i]->internal->name); + if (target_vnode->internal->entry[i]->internal->type == DIR) + printf("/"); + printf(" "); + } + printf("\n"); +} + +int vfs_cd(char *target_dir) +{ + vnode_t *dir_node = NULL; + + switch (vfs_lookup(target_dir, &dir_node)) + { + case EXISTED: + if (dir_node->internal->type != DIR) + { + printf("%s is not a directory.\n", target_dir); + return -1; + } + break; + case NOTDIR: + return -1; + case NOTFOUND: + printf("%s is not existed.\n", target_dir); + return -1; + } + + strcpy(cwdpath, target_dir); + + return 0; +} + +long vfs_lseek(struct file *file, long offset, int whence) +{ + if (file == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->lseek64(file, offset, whence); +} + +long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int error = 0; + printf("ioctl() unimplement, return 0\n"); + return error; +} \ No newline at end of file diff --git a/Lab7/src/kernel/virtual_mem.S b/Lab7/src/kernel/virtual_mem.S new file mode 100644 index 000000000..5453ad829 --- /dev/null +++ b/Lab7/src/kernel/virtual_mem.S @@ -0,0 +1,54 @@ +#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) + +#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_ACCESS (1 << 10) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + + +.global tcr_init +tcr_init: + ldr x0, = TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + ret + +.global mair_init +mair_init: + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 + ret + +.global identity_init +identity_init: + mov x0, 0 // PGD's page frame at 0x0 + mov x1, 0x1000 // PUD's page frame at 0x1000 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x0] + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + ret \ No newline at end of file diff --git a/Lab7/src/kernel/virtual_mem.c b/Lab7/src/kernel/virtual_mem.c new file mode 100644 index 000000000..d05dd504e --- /dev/null +++ b/Lab7/src/kernel/virtual_mem.c @@ -0,0 +1,10 @@ +extern void tcr_init(); +extern void mair_init(); +extern void identity_init(); + +void virtual_mem_init() +{ + tcr_init(); + mair_init(); + identity_init(); +} \ No newline at end of file diff --git a/Lab7/src/lib/hex2int.c b/Lab7/src/lib/hex2int.c new file mode 100644 index 000000000..4f30c1df8 --- /dev/null +++ b/Lab7/src/lib/hex2int.c @@ -0,0 +1,15 @@ +int hex2int(char *s, int n) +{ + int r = 0; + while (n-- > 0) + { + r <<= 4; + if (*s >= '0' && *s <= '9') + r += *s++ - '0'; + else if (*s >= 'A' && *s <= 'F') + r += *s++ - 'A' + 10; + else if (*s >= 'a' && *s <= 'f') + r += *s++ - 'a' + 10; + } + return r; +} \ No newline at end of file diff --git a/Lab7/src/lib/math.c b/Lab7/src/lib/math.c new file mode 100644 index 000000000..b96e3e335 --- /dev/null +++ b/Lab7/src/lib/math.c @@ -0,0 +1,9 @@ +int pow(int base, int exp) +{ + int result = 1; + for (int i = 0; i < exp; i++) + { + result *= base; + } + return result; +} \ No newline at end of file diff --git a/Lab7/src/lib/mem.c b/Lab7/src/lib/mem.c new file mode 100644 index 000000000..954ba9805 --- /dev/null +++ b/Lab7/src/lib/mem.c @@ -0,0 +1,33 @@ +#include + +void *memset(void *dest, register int val, int len) +{ + register unsigned char *ptr = (unsigned char *)dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +int memcmp(void *s1, void *s2, int n) +{ + unsigned char *a = s1, *b = s2; + while (n-- > 0) + { + if (*a != *b) + { + return *a - *b; + } + a++; + b++; + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} \ No newline at end of file diff --git a/Lab7/src/lib/mini_uart.c b/Lab7/src/lib/mini_uart.c new file mode 100644 index 000000000..d1896d604 --- /dev/null +++ b/Lab7/src/lib/mini_uart.c @@ -0,0 +1,215 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "peripherals/irq.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; + +char read_buffer[100]; +char write_buffer[100]; +int len_WB = 0; +int len_RB = 0; +int buffer_count = 0; + +void uart_send(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + + if (c != '\x7f') + put32(AUX_MU_IO_REG, c); + + if (c == '\n') + uart_send('\r'); +} + +char uart_recv(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r == '\r' ? '\n' : r; +} + +void uart_send_byte(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + put32(AUX_MU_IO_REG, c); +} + +char uart_recv_byte(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r; +} + +void uart_send_string(char *str) +{ + while (*str) + uart_send(*str++); +} + +void uart_send_space(int size) +{ + while (size--) + uart_send(' '); +} + +void uart_send_string_of_size(char *str, int size) +{ + while (size--) + uart_send(*str++); +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) +{ + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) + { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_init(void) +{ + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 2 << 12; // set alt5 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 2 << 15; // set alt5 for gpio15 + put32(GPFSEL1, selector); + + put32(GPPUD, 0); + delay(150); // spec + put32(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + put32(GPPUDCLK0, 0); + + put32(AUX_ENABLES, 1); // Enable mini uart (this also enables access to its registers) + put32(AUX_MU_CNTL_REG, 0); // Disable auto flow control and disable receiver and transmitter (for now) + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + put32(AUX_MU_LCR_REG, 3); // Enable 8 bit mode + put32(AUX_MU_MCR_REG, 0); // Set RTS line to be always high + put32(AUX_MU_BAUD_REG, 270); // Set baud rate to 115200 + + put32(AUX_MU_CNTL_REG, 3); // Finally, enable transmitter and receiver +} + +void _putchar(char character) +{ + uart_send(character); +} + +/** + * Display a string + */ +// void printf(char *fmt, ...) +// { +// __builtin_va_list args; +// __builtin_va_start(args, fmt); +// // we don't have memory allocation yet, so we +// // simply place our string after our code +// char *s = (char *)&_end; +// // use sprintf to format our string +// vsprintf(s, fmt, args); +// // print out as usual +// while (*s) +// { +// /* convert newline to carrige return + newline */ +// if (*s == '\n') +// uart_send('\r'); +// uart_send(*s++); +// } +// } + +void asyn_read() +{ + memset(read_buffer, '\0', 100); + + put32(AUX_MU_IER_REG, 1); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (read_buffer[strlen(read_buffer) - 1] != '\r') + ; +} + +void asyn_write(char *str) +{ + strcpy(write_buffer, str); + len_WB = strlen(write_buffer); + + put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (strlen(write_buffer) != 0) + ; +} + +void uart_rx_handler() +{ + read_buffer[len_RB++] = get32(AUX_MU_IO_REG); + + if (read_buffer[len_RB - 1] == '\r') + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + read_buffer[len_RB] = '\0'; + len_RB = 0; + } +} + +void uart_tx_handler() +{ + if (buffer_count < len_WB) + put32(AUX_MU_IO_REG, write_buffer[buffer_count++]); + else + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + buffer_count = 0; + memset(write_buffer, '\0', 100); + } +} + +void enable_uart_irq() +{ + put32(ENABLE_IRQS_1, 1 << 29); +} + +void disable_uart_irq() +{ + put32(DISABLE_IRQS_1, 1 << 29); +} \ No newline at end of file diff --git a/Lab7/src/lib/mm.S b/Lab7/src/lib/mm.S new file mode 100644 index 000000000..1bd32ff37 --- /dev/null +++ b/Lab7/src/lib/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/Lab7/src/lib/printf.c b/Lab7/src/lib/printf.c new file mode 100644 index 000000000..3eabd6eca --- /dev/null +++ b/Lab7/src/lib/printf.c @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "printf.h" + +#define PRINTF_DISABLE_SUPPORT_FLOAT + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct +{ + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) + { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)buffer; + (void)idx; + (void)maxlen; + if (character) + { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)idx; + (void)maxlen; + if (character) + { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) +{ + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) + { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (size_t i = len; i < width; i++) + { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) + { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) + { + len--; + if (len && (base == 16U)) + { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) + { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) + { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) + { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) + { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) + { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) + { + } + else if ((frac == 0U) || (frac & 1U)) + { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) + { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) + { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) + { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) + { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union + { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) + { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) + { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) + { + if ((int)prec > expval) + { + prec = (unsigned)((int)prec - expval - 1); + } + else + { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) + { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) + { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else + { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) + { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) + { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) + { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else + { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do + { + switch (*format) + { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) + { + width = _atoi(&format); + } + else if (*format == '*') + { + const int w = va_arg(va, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else + { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) + { + precision = _atoi(&format); + } + else if (*format == '*') + { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) + { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') + { + base = 16U; + } + else if (*format == 'o') + { + base = 8U; + } + else if (*format == 'b') + { + base = 2U; + } + else + { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) + { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) + { + // signed + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else + { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else + { + // unsigned + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': + { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': + { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) + { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': + { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) + { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } + else + { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char *buffer, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} \ No newline at end of file diff --git a/Lab7/src/lib/queue.c b/Lab7/src/lib/queue.c new file mode 100644 index 000000000..6b56b4dca --- /dev/null +++ b/Lab7/src/lib/queue.c @@ -0,0 +1,7 @@ +void queue_push(char c) +{ +} + +void queue_pop() +{ +} \ No newline at end of file diff --git a/Lab7/src/lib/simple_malloc.c b/Lab7/src/lib/simple_malloc.c new file mode 100644 index 000000000..b7a80a897 --- /dev/null +++ b/Lab7/src/lib/simple_malloc.c @@ -0,0 +1,8 @@ +char *counter = (char *)0x10000000; + +void *simple_malloc(unsigned int size) +{ + char *dest = counter; + counter += size; + return dest; +} diff --git a/Lab7/src/lib/string.c b/Lab7/src/lib/string.c new file mode 100644 index 000000000..d29bfeb4a --- /dev/null +++ b/Lab7/src/lib/string.c @@ -0,0 +1,113 @@ +#include + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + unsigned char c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + break; + if (c1 == '\0') + return 0; + } + + return c1 - c2; +} + +int strlen(const char *str) +{ + const unsigned char *s = (const unsigned char *)str; + unsigned int len = 0; + + while (1) + { + if (*s++ == '\0') + return len; + len++; + } +} + +char *strcpy(char *destination, const char *source) +{ + // return if no memory is allocated to the destination + if (destination == NULL) + { + return NULL; + } + + // take a pointer pointing to the beginning of the destination string + char *ptr = destination; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + + // include the terminating null character + *destination = '\0'; + + // the destination is returned by standard `strcpy()` + return ptr; +} + +// A simple atoi() function +int atoi(char *str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + res = res * 10 + str[i] - '0'; + + // return result. + return res; +} + +char *strncpy(char src[], char des[], int n) +{ + int i = 0; + while (src[i] != '\0' && i < n) + { + des[i] = src[i]; + i++; + } + des[i] = '\0'; + + return des; +} + +int strnchr(char *pathname, char target) +{ + int count = 0; + for (int i = 0; i < strlen(pathname); i++) + if (pathname[i] == target) + count++; + + return count; +} + +void strcat(char *des, char *s) +{ + int begin = strlen(des); + int cat_len = strlen(s); + + for (int i = 0; i < cat_len; i++) + des[begin + i] = s[i]; + + des[begin + cat_len] = '\0'; +} \ No newline at end of file diff --git a/Lab7/src/lib/utils.S b/Lab7/src/lib/utils.S new file mode 100644 index 000000000..c35527a42 --- /dev/null +++ b/Lab7/src/lib/utils.S @@ -0,0 +1,15 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret diff --git a/Lab7/src/userprogram/link.ld b/Lab7/src/userprogram/link.ld new file mode 100644 index 000000000..d84040b38 --- /dev/null +++ b/Lab7/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x15000000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab7/src/userprogram/user.S b/Lab7/src/userprogram/user.S new file mode 100644 index 000000000..6b0a2824a --- /dev/null +++ b/Lab7/src/userprogram/user.S @@ -0,0 +1,22 @@ +.section ".text.boot" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + mov x8, #0 + svc 0 + mov x8, #4 + svc 0 + mov x8, #9 + svc 0 + mov x8, #5 + svc 0 + blt 1b +1: + b 1b + +get_pid: + mov x8, #0 + svc 0 + ret \ No newline at end of file diff --git a/Lab7/userprogram.img b/Lab7/userprogram.img new file mode 100755 index 000000000..ada965550 Binary files /dev/null and b/Lab7/userprogram.img differ diff --git a/Lab8/FAT_R.TXT b/Lab8/FAT_R.TXT new file mode 100644 index 000000000..5d2252cd1 --- /dev/null +++ b/Lab8/FAT_R.TXT @@ -0,0 +1 @@ +fat_r test \ No newline at end of file diff --git a/Lab8/Makefile b/Lab8/Makefile new file mode 100644 index 000000000..a3dd22761 --- /dev/null +++ b/Lab8/Makefile @@ -0,0 +1,62 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only +CFLAGS = -Wall -O -ffreestanding -nostdlib -nostartfiles -g -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.img userprogram.img + +clean : + rm -rf $(BUILD_DIR) $(filter-out sfn_nctuos.img, $(wildcard *.img)) + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + @mkdir -p $(@D) + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/kernel/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/kernel/link.ld -o $(BUILD_DIR)/kernel/kernel8.elf $(filter $(BUILD_DIR)/kernel/%_c.o $(BUILD_DIR)/kernel/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel/kernel8.elf -O binary kernel8.img + +int_qemu: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initfs.cp -dtb bcm2710-rpi-3-b-plus.dtb -d int + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -s -S -initrd initfs.cp -dtb bcm2710-rpi-3-b-plus.dtb -d int -drive if=sd,file=sfn_nctuos.img,format=raw + +bootloader.img: $(SRC_DIR)/bootloader/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/bootloader/link.ld -o $(BUILD_DIR)/bootloader/bootloader.elf $(filter $(BUILD_DIR)/bootloader/%_c.o $(BUILD_DIR)/bootloader/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/bootloader/bootloader.elf bootloader.img + +userprogram.img: $(SRC_DIR)/userprogram/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/userprogram/link.ld -o $(BUILD_DIR)/userprogram/userprogram.elf $(filter $(BUILD_DIR)/userprogram/%_c.o $(BUILD_DIR)/userprogram/%_s.o $(BUILD_DIR)/lib/%_c.o $(BUILD_DIR)/lib/%_s.o, $(OBJ_FILES)) + $(ARMGNU)-objcopy -O binary $(BUILD_DIR)/userprogram/userprogram.elf userprogram.img + +test: + @echo Hello + +pseudoTTY: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initfs.cp -dtb bcm2710-rpi-3-b-plus.dtb + +debug_boot: + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -serial null -serial pty -display none -initrd initfs.cp -dtb bcm2710-rpi-3-b-plus.dtb -drive if=sd,file=sfn_nctuos.img,format=raw -s -S + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initfs.cp -dtb bcm2710-rpi-3-b-plus.dtb -drive if=sd,file=sfn_nctuos.img,format=raw + +display: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initfs.cp -dtb bcm2710-rpi-3-b-plus.dtb -drive if=sd,file=sfn_nctuos.img,format=raw diff --git a/Lab8/bcm2710-rpi-3-b-plus.dtb b/Lab8/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..0dd0e2f43 Binary files /dev/null and b/Lab8/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab8/bootloader.img b/Lab8/bootloader.img new file mode 100755 index 000000000..3d60319b0 Binary files /dev/null and b/Lab8/bootloader.img differ diff --git a/Lab8/bootloader.py b/Lab8/bootloader.py new file mode 100644 index 000000000..1b99fa7d7 --- /dev/null +++ b/Lab8/bootloader.py @@ -0,0 +1,25 @@ +import os +import time + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +# with open('/dev/pts/35', "wb", buffering=0) as tty: +with open('/dev/ttyUSB0', "wb", buffering=0) as tty: + # send Start + tty.write(b"Start") + time.sleep(1) + + # send kernel size + tty.write(file_size.to_bytes(4, 'little')) + time.sleep(1) + + # send kernel + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + tty.write(data) + time.sleep(1) + diff --git a/Lab8/config.txt b/Lab8/config.txt new file mode 100644 index 000000000..e22e7d79c --- /dev/null +++ b/Lab8/config.txt @@ -0,0 +1,3 @@ +kernel=kernel8.img +arm_64bit=1 +initramfs initfs.cp 0x8000000 diff --git a/Lab8/include/devfs.h b/Lab8/include/devfs.h new file mode 100644 index 000000000..d450ca06c --- /dev/null +++ b/Lab8/include/devfs.h @@ -0,0 +1,21 @@ +#ifndef __DEVFS_H_ +#define __DEVFS_H_ + +#include "vfs.h" +extern struct filesystem devfs; + +// fops +int devfs_write(struct file *file, void *buf, size_t len); +int devfs_read(struct file *file, void *buf, size_t len); +int devfs_open(struct vnode *file_node, struct file **target); +int devfs_close(struct file *file); +long devfs_lseek(struct file *file, long offset, int whence); + +// vops +int devfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); +int devfs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int devfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); + +int devfs_setup_mount(struct filesystem *fs, struct mount *mount); + +#endif // __DEVFS_H_ \ No newline at end of file diff --git a/Lab8/include/device_tree.h b/Lab8/include/device_tree.h new file mode 100644 index 000000000..e3e6aee78 --- /dev/null +++ b/Lab8/include/device_tree.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_TREE_H +#define _DEVICE_TREE_H + +typedef int (*fdt_callback)(void *); +int initramfs_callback(void *dtb); +int dtb_parser(void *dtb); +void fdt_traverse(fdt_callback cb, char *dtb); + +#endif /*_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab8/include/dynamic_alloc.h b/Lab8/include/dynamic_alloc.h new file mode 100644 index 000000000..55eb68307 --- /dev/null +++ b/Lab8/include/dynamic_alloc.h @@ -0,0 +1,35 @@ +#ifndef _DYNAMIC_ALLOC_H +#define _DYNAMIC_ALLOC_H + +#include "list.h" + +#define MAX_POOL_SIZE 256 +#define MIN_CHUNK_SIZE 8 +#define FREE 98 + +typedef struct _chunk +{ + int index; // const + int size; + void *addr; // const + int val; + int belong_page; + struct list_head list; +} chunk; + +typedef struct _pool_list +{ + struct list_head list; +} pool_list; + +void init_pool(); +void *get_chunk(int req_size); +int roundup_size(int size); +void split_page(int frame_index, int req_pool_index); +int remove_a_chunk_from_pool(int req_pool_index); +int free_chunk(int index); +void put_back_to_pool(int pool_index, int chunk_index); +void debug_pool(); +void free(void *addr); + +#endif /*_DYNAMIC_ALLOC_H */ diff --git a/Lab8/include/fat32.h b/Lab8/include/fat32.h new file mode 100644 index 000000000..71a50ea25 --- /dev/null +++ b/Lab8/include/fat32.h @@ -0,0 +1,120 @@ +#ifndef _FAT32_H +#define _FAT32_H + +#include "vfs.h" +#include "sdhost.h" +#include + +/* + Ref[1]: https://wiki.osdev.org/MBR_(x86) + Ref[2]: https://wiki.osdev.org/FAT#Boot_Record + Ref[3]: https://github.com/LJP-TW/osc2022 + Ref[4]: https://www.easeus.com/resource/fat32-disk-structure.htm + Ref[5]: https://academy.cba.mit.edu/classes/networking_communications/SD/FAT.pdf + Ref[6]: https://www.youtube.com/watch?v=lz83GavddB0 + Ref[7]: https://cscie92.dce.harvard.edu/fall2022/slides/FAT32%20File%20Structure.pdf +*/ + +extern uint32_t FAT1_LBA; // Logical Block Address, FAT has size of sec_per_fat32*SD_BLOCK_SIZE bytes +extern uint32_t root_dir_LBA; // physical block +extern uint32_t root_cluster; // boot_sector_t.root_cluster, usually 2 +extern uint32_t sec_per_fat32; // boot_sector_t.sector_per_fat32 + +#define FROM_LBA_TO_PHY_BK(lba) (root_dir_LBA + (lba) + -root_cluster) +#define FAT_ENTRY_EOF ((uint32_t)0x0FFFFFF8) // end of file (end of linked list in FAT) +#define FAT_ENTRY_EMPTY ((uint32_t)0x00000000) +#define DIR_ENTRY_ATTR_ARCHIVE 0x20 // file, page 23, Ref[5] +#define DIR_ENTRY_wrtTime_mock 0x58D8 // 11:06:48AM +#define DIR_ENTRY_wrtDate_mock 0x50C4 // 20200604 + +typedef struct partition_struct +{ + uint8_t bootindicator; + uint8_t start_head; + uint16_t start_sector_and_cylinder; + uint8_t fs_type; // '0B':FAT32 ; '04':FAT16 ; '07':NTFS + uint8_t end_head; + uint16_t end_sector_and_cylinder; + uint32_t relative_sector; + uint32_t number_sectors; +} __attribute__((packed)) partition_t; + +typedef struct MBR_struct +{ + uint8_t bootstrap_code[446]; + partition_t part1; + partition_t part2; + partition_t part3; + partition_t part4; + uint8_t signature[2]; +} __attribute__((packed)) MBR_t; + +// page 7, Ref[5] +typedef struct boot_sector_t +{ + uint8_t jmpboot[3]; + uint8_t oemname[8]; + uint16_t bytes_per_sector; + uint8_t sector_per_cluster; + uint16_t reserved_sector_cnt; // Number of reserved sectors in the reserved region of the volume starting at the first sector of the volume. This field is used to align the start of the data area to integral multiples of the cluster size with respect to the start of the partition/media + uint8_t fat_cnt; // The count of file allocation tables (FATs) on the volume + uint16_t root_entry_cnt; + uint16_t old_sector_cnt; + uint8_t media; + uint16_t sector_per_fat16; + uint16_t sector_per_track; + uint16_t head_cnt; + uint32_t hidden_sector_cnt; + uint32_t sector_cnt; + uint32_t sector_per_fat32; // This field is the FAT32 32-bit count of sectors occupied by one FAT. + uint16_t extflags; + uint16_t ver; + uint32_t root_cluster; + uint16_t info; // Sector number of FSINFO structure in the reserved area of the FAT32 volume. Usually 1. + uint16_t bkbooksec; + uint8_t reserved[12]; + uint8_t drvnum; + uint8_t reserved1; + uint8_t bootsig; + uint32_t volid; + uint8_t vollab[11]; + uint8_t fstype[8]; + uint8_t skipped[420]; + uint8_t valid_bootsector[2]; // 0x55, 0xAA +} __attribute__((packed)) boot_sector_t; + +// page 23, Ref[5] +typedef struct dir_entry +{ + char name[11]; // 0~10 + uint8_t attr; // 11 + uint8_t NTRes; // 12 + uint8_t crtTimeTenth; // 13 + uint16_t crtTime; // 14~15 + uint16_t crtDate; // 16~17 + uint16_t lstAccDate; // 18~19 + uint16_t fstClusHI; // 20~21, High word of first data cluster number for file/directory described by this entry. + uint16_t wrtTime; // 22~23 + uint16_t wrtDate; // 24~26 + uint16_t fstClusLO; // 26~27, Low word of first data cluster number for file/directory described by this entry. + uint32_t fileSize; // 28~31, 32-bit quantity containing size in bytes of file/directory described by this entry. +} __attribute__((packed)) dir_entry; + +// VFS bridge ----------------------------------------------------------- +extern int fat32fs_mounted; +int fat32fs_mount(struct filesystem *fs, struct mount *mount); + +// fops +int fat32fs_write(struct file *file, void *buf, size_t len); // no lseek() +int fat32fs_read(struct file *file, void *buf, size_t len); // no lseek() +int fat32fs_open(struct vnode *file_node, struct file **target); +int fat32fs_close(struct file *file); + +// vops +int fat32fs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); +int fat32fs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int fat32fs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); + +int fat32_filename_to_str(const char *fat32_filename, char *str); + +#endif \ No newline at end of file diff --git a/Lab8/include/list.h b/Lab8/include/list.h new file mode 100644 index 000000000..a0fb29587 --- /dev/null +++ b/Lab8/include/list.h @@ -0,0 +1,441 @@ +/* Linux-like double-linked list implementation */ + +#ifndef SYSPROG21_LIST_H +#define SYSPROG21_LIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *)0)->member) *__pmember = (ptr); \ + (type *)((char *)__pmember - offsetof(type, member)); \ + }) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr)-offsetof(type, member))) +#endif +#endif + + /** + * struct list_head - Head and node of a double-linked list + * @prev: pointer to the previous node in the list + * @next: pointer to the next node in the list + * + * The simple double-linked list consists of a head and nodes attached to + * this head. Both node and head share the same struct type. The list_* + * functions and macros can be used to access and modify this data structure. + * + * The @prev pointer of the list head points to the last list node of the + * list and @next points to the first list node of the list. For an empty list, + * both member variables point to the head. + * + * The list nodes are usually embedded in a container structure which holds the + * actual data. Such an container object is called entry. The helper list_entry + * can be used to calculate the object address from the address of the node. + */ + struct list_head + { + struct list_head *prev; + struct list_head *next; + }; + +/** + * LIST_HEAD - Declare list head and initialize it + * @head: name of the new object + */ +#define LIST_HEAD(head) struct list_head head = {&(head), &(head)} + + /** + * INIT_LIST_HEAD() - Initialize empty list head + * @head: pointer to list head + * + * This can also be used to initialize a unlinked list node. + * + * A node is usually linked inside a list, will be added to a list in + * the near future or the entry containing the node will be free'd soon. + * + * But an unlinked node may be given to a function which uses list_del(_init) + * before it ends up in a previously mentioned state. The list_del(_init) on an + * initialized node is well defined and safe. But the result of a + * list_del(_init) on an uninitialized node is undefined (unrelated memory is + * modified, crashes, ...). + */ + static inline void INIT_LIST_HEAD(struct list_head *head) + { + head->next = head; + head->prev = head; + } + + /** + * list_add() - Add a list node to the beginning of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add(struct list_head *node, struct list_head *head) + { + struct list_head *next = head->next; + + next->prev = node; + node->next = next; + node->prev = head; + head->next = node; + } + + /** + * list_add_tail() - Add a list node to the end of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ + static inline void list_add_tail(struct list_head *node, struct list_head *head) + { + struct list_head *prev = head->prev; + + prev->next = node; + node->next = head; + node->prev = prev; + head->prev = node; + } + + /** + * list_del() - Remove a list node from the list + * @node: pointer to the node + * + * The node is only removed from the list. Neither the memory of the removed + * node nor the memory of the entry containing the node is free'd. The node + * has to be handled like an uninitialized node. Accessing the next or prev + * pointer of the node is not safe. + * + * Unlinked, initialized nodes are also uninitialized after list_del. + * + * LIST_POISONING can be enabled during build-time to provoke an invalid memory + * access when the memory behind the next/prev pointer is used after a list_del. + * This only works on systems which prohibit access to the predefined memory + * addresses. + */ + static inline void list_del(struct list_head *node) + { + struct list_head *next = node->next; + struct list_head *prev = node->prev; + + next->prev = prev; + prev->next = next; + +#ifdef LIST_POISONING + node->prev = (struct list_head *)(0x00100100); + node->next = (struct list_head *)(0x00200200); +#endif + } + + /** + * list_del_init() - Remove a list node from the list and reinitialize it + * @node: pointer to the node + * + * The removed node will not end up in an uninitialized state like when using + * list_del. Instead the node is initialized again to the unlinked state. + */ + static inline void list_del_init(struct list_head *node) + { + list_del(node); + INIT_LIST_HEAD(node); + } + + /** + * list_empty() - Check if list head has no nodes attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not empty !0 - list is empty + */ + static inline int list_empty(const struct list_head *head) + { + return (head->next == head); + } + + /** + * list_is_singular() - Check if list head has exactly one node attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not singular !0 -list has exactly one entry + */ + static inline int list_is_singular(const struct list_head *head) + { + return (!list_empty(head) && head->prev == head->next); + } + + /** + * list_splice() - Add list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice(struct list_head *list, struct list_head *head) + { + struct list_head *head_first = head->next; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->next = list_first; + list_first->prev = head; + + list_last->next = head_first; + head_first->prev = list_last; + } + + /** + * list_splice_tail() - Add list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ + static inline void list_splice_tail(struct list_head *list, + struct list_head *head) + { + struct list_head *head_last = head->prev; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->prev = list_last; + list_last->next = head; + + list_first->prev = head_last; + head_last->next = list_first; + } + + /** + * list_splice_init() - Move list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_init(struct list_head *list, + struct list_head *head) + { + list_splice(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_splice_tail_init() - Move list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ + static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) + { + list_splice_tail(list, head); + INIT_LIST_HEAD(list); + } + + /** + * list_cut_position() - Move beginning of a list to another list + * @head_to: pointer to the head of the list which receives nodes + * @head_from: pointer to the head of the list + * @node: pointer to the node in which defines the cutting point + * + * All entries from the beginning of the list @head_from to (including) the + * @node is moved to @head_to. + * + * @head_to is replaced when @head_from is not empty. @node must be a real + * list node from @head_from or the behavior is undefined. + */ + static inline void list_cut_position(struct list_head *head_to, + struct list_head *head_from, + struct list_head *node) + { + struct list_head *head_from_first = head_from->next; + + if (list_empty(head_from)) + return; + + if (head_from == node) + { + INIT_LIST_HEAD(head_to); + return; + } + + head_from->next = node->next; + head_from->next->prev = head_from; + + head_to->prev = node; + node->next = head_to; + head_to->next = head_from_first; + head_to->next->prev = head_to; + } + + /** + * list_move() - Move a list node to the beginning of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the beginning of + * @head + */ + static inline void list_move(struct list_head *node, struct list_head *head) + { + list_del(node); + list_add(node, head); + } + + /** + * list_move_tail() - Move a list node to the end of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the end of @head + */ + static inline void list_move_tail(struct list_head *node, + struct list_head *head) + { + list_del(node); + list_add_tail(node, head); + } + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_last_entry() - get last entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of last entry in list + */ +#define list_last_entry(head, type, member) \ + list_entry((head)->prev, type, member) + +/** + * list_for_each - iterate over list nodes + * @node: list_head pointer used as iterator + * @head: pointer to the head of the list + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + */ +#define list_for_each(node, head) \ + for (node = (head)->next; node != (head); node = node->next) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif + +/** + * list_for_each_safe - iterate over list nodes and allow deletes + * @node: list_head pointer used as iterator + * @safe: list_head pointer used to store info for next entry in list + * @head: pointer to the head of the list + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + */ +#define list_for_each_safe(node, safe, head) \ + for (node = (head)->next, safe = node->next; node != (head); \ + node = safe, safe = node->next) + +/** + * list_for_each_entry_safe - iterate over list entries and allow deletes + * @entry: pointer used as iterator + * @safe: @type pointer used to store info for next entry in list + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#define list_for_each_entry_safe(entry, safe, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member), \ + safe = list_entry(entry->member.next, __typeof__(*entry), member); \ + &entry->member != (head); entry = safe, \ + safe = list_entry(safe->member.next, __typeof__(*entry), member)) + +#undef __LIST_HAVE_TYPEOF + +#ifdef __cplusplus +} +#endif + +#endif /* SYSPROG21_LIST_H */ \ No newline at end of file diff --git a/Lab8/include/load_kernel.h b/Lab8/include/load_kernel.h new file mode 100644 index 000000000..a5dc68ffb --- /dev/null +++ b/Lab8/include/load_kernel.h @@ -0,0 +1,7 @@ +#ifndef _LOAD_KERNEL_H +#define _LOAD_KERNEL_H + +void load_kernel(char *dest); +void relocate(char *from_dest, char *to_dest); + +#endif /*_LOAD_KERNEL_H */ diff --git a/Lab8/include/math.h b/Lab8/include/math.h new file mode 100644 index 000000000..3107a97d8 --- /dev/null +++ b/Lab8/include/math.h @@ -0,0 +1 @@ +int pow(int base, int exp); \ No newline at end of file diff --git a/Lab8/include/mbox.h b/Lab8/include/mbox.h new file mode 100644 index 000000000..b60f6738d --- /dev/null +++ b/Lab8/include/mbox.h @@ -0,0 +1,7 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); +char *framebuffer_init(); + +#endif /*_MBOX_H */ diff --git a/Lab8/include/mbox_call.h b/Lab8/include/mbox_call.h new file mode 100644 index 000000000..7f60645d0 --- /dev/null +++ b/Lab8/include/mbox_call.h @@ -0,0 +1,9 @@ +#ifndef _MBOX_CALL_H +#define _MBOX_CALL_H + +/* a properly aligned buffer */ +extern volatile unsigned int mbox[36]; + +int mbox_call(unsigned char ch); + +#endif /*_MBOX_CALL_H */ diff --git a/Lab8/include/mini_uart.h b/Lab8/include/mini_uart.h new file mode 100644 index 000000000..27ae3e20b --- /dev/null +++ b/Lab8/include/mini_uart.h @@ -0,0 +1,23 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void uart_send_string_of_size(char *str, int size); +void uart_hex(unsigned int d); +void uart_send_space(int size); + +void uart_send_byte(char c); +char uart_recv_byte(void); + +void asyn_read(); +void asyn_write(); +void uart_rx_handler(); +void uart_tx_handler(); + +void enable_uart_irq(); +void disable_uart_irq(); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab8/include/mm.h b/Lab8/include/mm.h new file mode 100644 index 000000000..1d947fb75 --- /dev/null +++ b/Lab8/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (6 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/Lab8/include/my_signal.h b/Lab8/include/my_signal.h new file mode 100644 index 000000000..d3f998486 --- /dev/null +++ b/Lab8/include/my_signal.h @@ -0,0 +1,30 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include "list.h" +#include "thread.h" + +#define SIGKILL 9 +#define SIG_NUM (sizeof(signal_table) / sizeof(signal_table[0])) +typedef void (*signal_handler)(int); + +typedef struct _custom_signal +{ + unsigned int sig_num; + signal_handler handler; + struct list_head list; +} custom_signal; + +typedef struct _signal_context +{ + struct _trapframe *trapframe; + char *user_stack; +} signal_context; + +void sig_ignore(int _); +void sigkill_handler(int pid); +void sig_context_update(struct _trapframe *trapframe, void (*handler)()); +void sig_return(void); +void sig_context_restore(struct _trapframe *trapframe); + +#endif /*_SIGNAL_H */ diff --git a/Lab8/include/page_alloc.h b/Lab8/include/page_alloc.h new file mode 100644 index 000000000..0a191e1fe --- /dev/null +++ b/Lab8/include/page_alloc.h @@ -0,0 +1,39 @@ +#ifndef _PAGE_ALLOC_H +#define _PAGE_ALLOC_H + +// #define DEBUG + +#define MAX_ORDER 11 // order 0 ~ 11 +#define ALLOCATED -1 +#define FREE_BUDDY 99 +#define MIN_PAGE_SIZE 0x1000 +// #define FREE_MEM_START 0x10000000 +// #define FREE_MEM_END 0x20000000 +#define FREE_MEM_START 0x00 +#define FREE_MEM_END 0x3C000000 +#define TOTAL_NUM_PAGE (FREE_MEM_END - FREE_MEM_START) / MIN_PAGE_SIZE + +typedef struct _page_frame_node page_frame_node; +struct _page_frame_node +{ + int index; // const + int val; + void *addr; // const + int contiguous_head; // if allocated, this value keep track who (page) is the start of the contiguous memory in index + int allocated_order; + int chunk_order; // -1 if no chunk, otherwise 1, 2, 4, 6, 8, 16, 32 + page_frame_node *next; + page_frame_node *previous; +}; + +void init_page_frame(); +void *my_malloc(int req_size); +int get_page_from_free_list(int req_size, int who); +void add_to_free_list(page_frame_node *head_node, int index); +void remove_from_free_list(page_frame_node *free_list_node); +void put_back_to_free_list(int num_of_redundant_page, int index); +int free_page_frame(int index); +int merge_buddy(int *index, int buddy, int order); +void debug(); + +#endif /*_PAGE_ALLOC_H */ diff --git a/Lab8/include/peripherals/base.h b/Lab8/include/peripherals/base.h new file mode 100644 index 000000000..63f9c038f --- /dev/null +++ b/Lab8/include/peripherals/base.h @@ -0,0 +1,6 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab8/include/peripherals/device_tree.h b/Lab8/include/peripherals/device_tree.h new file mode 100644 index 000000000..d5d3bc729 --- /dev/null +++ b/Lab8/include/peripherals/device_tree.h @@ -0,0 +1,16 @@ +#ifndef _P_DEVICE_TREE_H +#define _P_DEVICE_TREE_H + +#define FDT_MAGIC 0xD00DFEED +#define FDT_VERSION 0x00000011 +#define FDT_TK_NULL 0X00000000 + +#define FDT_BEGIN_NODE 0X00000001 +#define FDT_END_NODE 0X00000002 +#define FDT_PROP 0X00000003 +#define FDT_NOP 0X00000004 +#define FDT_END 0X00000009 + +#define FDT_CPIO_INITRAMFS_PROPNAME "linux,initrd-start" + +#endif /*_P_DEVICE_TREE_H */ \ No newline at end of file diff --git a/Lab8/include/peripherals/gpio.h b/Lab8/include/peripherals/gpio.h new file mode 100644 index 000000000..a7a6a5b4c --- /dev/null +++ b/Lab8/include/peripherals/gpio.h @@ -0,0 +1,25 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL0 (PBASE + 0x00200000) +#define GPFSEL1 (PBASE + 0x00200004) +#define GPFSEL2 (PBASE + 0x00200008) +#define GPFSEL3 (PBASE + 0x0020000C) +#define GPFSEL4 (PBASE + 0x00200010) +#define GPFSEL5 (PBASE + 0x00200014) +#define GPSET0 (PBASE + 0x0020001C) +#define GPSET1 (PBASE + 0x00200020) +#define GPCLR0 (PBASE + 0x00200028) +#define GPLEV0 (PBASE + 0x00200034) +#define GPLEV1 (PBASE + 0x00200038) +#define GPEDS0 (PBASE + 0x00200040) +#define GPEDS1 (PBASE + 0x00200044) +#define GPHEN0 (PBASE + 0x00200064) +#define GPHEN1 (PBASE + 0x00200068) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) +#define GPPUDCLK1 (PBASE + 0x0020009C) + +#endif /*_P_GPIO_H */ diff --git a/Lab8/include/peripherals/irq.h b/Lab8/include/peripherals/irq.h new file mode 100644 index 000000000..51e0cc5ea --- /dev/null +++ b/Lab8/include/peripherals/irq.h @@ -0,0 +1,27 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + +#define IRQ_BASIC_PENDING (PBASE + 0x0000B200) +#define IRQ_PENDING_1 (PBASE + 0x0000B204) +#define IRQ_PENDING_2 (PBASE + 0x0000B208) +#define FIQ_CONTROL (PBASE + 0x0000B20C) +#define ENABLE_IRQS_1 (PBASE + 0x0000B210) +#define ENABLE_IRQS_2 (PBASE + 0x0000B214) +#define ENABLE_BASIC_IRQS (PBASE + 0x0000B218) +#define DISABLE_IRQS_1 (PBASE + 0x0000B21C) +#define DISABLE_IRQS_2 (PBASE + 0x0000B220) +#define DISABLE_BASIC_IRQS (PBASE + 0x0000B224) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#define CORE0_INTR_SRC 0x40000060 +#define CORE1_INTR_SRC 0x40000064 +#define CORE2_INTR_SRC 0x40000068 +#define CORE3_INTR_SRC 0x4000006C + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/Lab8/include/peripherals/mbox_call.h b/Lab8/include/peripherals/mbox_call.h new file mode 100644 index 000000000..cfcc0d3ad --- /dev/null +++ b/Lab8/include/peripherals/mbox_call.h @@ -0,0 +1,40 @@ +#ifndef _P_MBOX_CALL_H +#define _P_MBOX_CALL_H + +#include "peripherals/base.h" + +#define VIDEOCORE_MBOX (PBASE + 0x0000B880) +#define MBOX_READ (VIDEOCORE_MBOX + 0x0) +#define MBOX_POLL (VIDEOCORE_MBOX + 0x10) +#define MBOX_SENDER (VIDEOCORE_MBOX + 0x14) +#define MBOX_STATUS (VIDEOCORE_MBOX + 0x18) +#define MBOX_CONFIG (VIDEOCORE_MBOX + 0x1C) +#define MBOX_WRITE (VIDEOCORE_MBOX + 0x20) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_MODEL 0x10001 +#define MBOX_TAG_REVISION 0x10002 +#define MBOX_TAG_MAC_ADDRESS 0x10003 +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_ARM_MEMORY 0x10005 +#define MBOX_TAG_VC_MEMORY 0x10006 +#define MBOX_TAG_CLOCKS 0x10007 +#define MBOX_TAG_LAST 0 + +#endif /* _P_MBOX_CALL_H */ diff --git a/Lab8/include/peripherals/mini_uart.h b/Lab8/include/peripherals/mini_uart.h new file mode 100644 index 000000000..71119b511 --- /dev/null +++ b/Lab8/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/Lab8/include/peripherals/reboot.h b/Lab8/include/peripherals/reboot.h new file mode 100644 index 000000000..f1d41ad2e --- /dev/null +++ b/Lab8/include/peripherals/reboot.h @@ -0,0 +1,8 @@ +#ifndef _P_REBOOT_H +#define _P_REBOOT_H + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +#endif /*_P_REBOOT_H */ \ No newline at end of file diff --git a/Lab8/include/printf.h b/Lab8/include/printf.h new file mode 100644 index 000000000..a9501cba0 --- /dev/null +++ b/Lab8/include/printf.h @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ + void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ + int printf_(const char *format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ + int sprintf_(char *buffer, const char *format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + int snprintf_(char *buffer, size_t count, const char *format, ...); + int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ + int vprintf_(const char *format, va_list va); + + /** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ + int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ \ No newline at end of file diff --git a/Lab8/include/read_cpio.h b/Lab8/include/read_cpio.h new file mode 100644 index 000000000..ea7c5f9c6 --- /dev/null +++ b/Lab8/include/read_cpio.h @@ -0,0 +1,18 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +typedef struct cpio_node_struct +{ + char *name; + int type; + int size; + char *data; + struct cpio_node_struct *next; +} cpio_node_t; + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); +char *find_content_addr(char *cpioDest, const char *filename); +int load_userprogram(const char *, char *); + +#endif /*_READ_CPIO_H */ \ No newline at end of file diff --git a/Lab8/include/reboot.h b/Lab8/include/reboot.h new file mode 100644 index 000000000..e9fb1d66c --- /dev/null +++ b/Lab8/include/reboot.h @@ -0,0 +1,8 @@ +#ifndef _REBOOT_H +#define _REBOOT_H + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif /*_REBOOT_H */ \ No newline at end of file diff --git a/Lab8/include/reserve_mem.h b/Lab8/include/reserve_mem.h new file mode 100644 index 000000000..3922438c7 --- /dev/null +++ b/Lab8/include/reserve_mem.h @@ -0,0 +1,18 @@ +#ifndef _RESERVE_MEM_H +#define _RESERVE_MEM_H + +#define USRPGM_BASE 0x15000000 +#define USRPGM_SIZE 0x100000 + +typedef struct _reserved_memory_block +{ + unsigned long start; + unsigned long end; + char name[30]; +} reserved_memory_block; + +void memory_reserve(unsigned long start, unsigned long end, char *name); +int check_contain_RM(unsigned long start, unsigned long end); +void memory_init(); + +#endif /*_RESERVE_MEM_H */ \ No newline at end of file diff --git a/Lab8/include/sdhost.h b/Lab8/include/sdhost.h new file mode 100644 index 000000000..614816a6e --- /dev/null +++ b/Lab8/include/sdhost.h @@ -0,0 +1,11 @@ +#ifndef _SDHOST_H +#define _SDHOST_H + +#define SD_BLOCK_SIZE 512 +#define SECTOR_SIZE 512 + +void readblock(int block_idx, void *buf); +void writeblock(int block_idx, void *buf); +void sd_init(); + +#endif \ No newline at end of file diff --git a/Lab8/include/shell.h b/Lab8/include/shell.h new file mode 100644 index 000000000..f677e039b --- /dev/null +++ b/Lab8/include/shell.h @@ -0,0 +1,7 @@ +#ifndef _SHELL_H +#define _SHELL_H + +void shell_main(char *); +void shell_start(); + +#endif /*_SHELL_H */ diff --git a/Lab8/include/stdlib.h b/Lab8/include/stdlib.h new file mode 100644 index 000000000..61d6ea3a4 --- /dev/null +++ b/Lab8/include/stdlib.h @@ -0,0 +1,31 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" +#include "utils.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const char *str); +char *strcpy(char *destination, const char *source); +int atoi(char *str); +char *strncpy(char src[], char des[], int n); +int strnchr(char *pathname, char target); +void strcat(char *des, char *s); + +void *memset(void *dest, register int val, int len); +int memcmp(void *s1, void *s2, int n); +void *memcpy(void *dest, const void *src, size_t len); +int hex2int(char *s, int n); + +void *simple_malloc(unsigned int size); + +// void printf(char *fmt, ...); +// unsigned int sprintf(char *dst, char *fmt, ...); +// unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args); + +#endif /*_STDLIB_H */ diff --git a/Lab8/include/syscall.h b/Lab8/include/syscall.h new file mode 100644 index 000000000..3f59e9c25 --- /dev/null +++ b/Lab8/include/syscall.h @@ -0,0 +1,26 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + +#include "stdlib.h" + +int getpid(); +size_t uart_read(char buf[], size_t size); +size_t uart_write(const char buf[], size_t size); +int exec(const char *name, char *const argv[]); +int fork(); +void exit(int status); +int mbox_call_u(unsigned char ch, unsigned int *mbox); +void kill(int pid); +void signal(int SIGNAL, void (*handler)()); + +int open(char *pathname, int flags); +int close(int fd); +long write(int fd, void *buf, unsigned long count); +long read(int fd, void *buf, unsigned long count); +int mkdir(char *pathname, unsigned mode); +int mount(char *src, char *target, char *filesystem, unsigned long flags, void *data); +int chdir(char *path); +long lseek(int fd, long offset, int whence); +int ioctl(int fd, unsigned long request, unsigned long arg); + +#endif /*_SYSCALL_H */ \ No newline at end of file diff --git a/Lab8/include/test.h b/Lab8/include/test.h new file mode 100644 index 000000000..11c9a66cd --- /dev/null +++ b/Lab8/include/test.h @@ -0,0 +1,10 @@ +#ifndef _TEST_H +#define _TEST_H + +void test_mem_alloc(); +void test_thread(); +void load_usrpgm_in_umode(); +void load_vfs1_usrpgm_in_umode(); +void load_vfs2_usrpgm_in_umode(); + +#endif /*_TEST_H */ diff --git a/Lab8/include/thread.h b/Lab8/include/thread.h new file mode 100644 index 000000000..c3ca6e5a4 --- /dev/null +++ b/Lab8/include/thread.h @@ -0,0 +1,65 @@ +#ifndef _THREAD_H +#define _THREAD_H + +#include "list.h" +#include "my_signal.h" +#include "vfs.h" + +#define READY 1 +#define ZOMBIE 2 +#define FORKING 4 + +typedef void (*func_ptr)(); + +typedef struct _context +{ + unsigned long x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, fp, lr, sp; +} context; + +typedef struct _thread_info +{ + long id; + long child_id; + struct _task_struct *task; + +} thread_info; + +typedef struct _trapframe +{ + unsigned long x[31]; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trapframe; + +typedef struct _task_struct +{ + struct _context task_context; // context need to be the first one + struct _thread_info *thread_info; + struct list_head list; + func_ptr job; + unsigned long kstack_start; // kernel stack base + unsigned long ustack_start; // user stack base + unsigned long usrpgm_load_addr; // user program load address + unsigned long status; + unsigned long trapframe; // using "unsigned long" to keep trapframe address, instead of claiming a "trapframe_t*" to avoid "Data Abort" + struct _custom_signal *custom_signal; + struct _signal_context *signal_context; + struct file *fd_table[VFS_PROCESS_MAX_OPEN_FILE]; // should be zeroed out on thread_create +} task_struct; + +void schedule(); +void add_rq(task_struct *task); +task_struct *del_rq(); +void thread_init(); +thread_info *thread_create(func_ptr fp); +void task_wrapper(); +void idle_task(); +void kill_zombies(); +void do_fork(); +void create_child(task_struct *parent); +void debug_task_rq(); +void debug_task_zombieq(); +int thread_get_idle_fd(task_struct *thd); + +#endif /*_THREAD_H */ \ No newline at end of file diff --git a/Lab8/include/timer.h b/Lab8/include/timer.h new file mode 100644 index 000000000..2e352e7fe --- /dev/null +++ b/Lab8/include/timer.h @@ -0,0 +1,14 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#define MESSAGE_BUFFER 50 +#define SECONDS_BUFFER 20 + +void get_current_time(); +void el0_timer_handler(long cntpct_el0, long cntfrq_el0); +void el1_timer_handler(long cntpct_el0, long cntfrq_el0); +void add_timer(int sec, char *mes); +int is_timer_queue_empty(); +void timer_delay(long seconds); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab8/include/tmpfs.h b/Lab8/include/tmpfs.h new file mode 100644 index 000000000..3442de435 --- /dev/null +++ b/Lab8/include/tmpfs.h @@ -0,0 +1,38 @@ +#ifndef _TMPFS_H +#define _TMPFS_H + +#include "vfs.h" + +#define EXISTED 0 +#define NOTFOUND -1 +#define NOTDIR -2 + +/* mount operations */ +int tmpfs_mount(struct filesystem *fs, struct mount *mount); + +/* vnode operations */ +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); +int tmpfs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); + +/* file operations */ +int tmpfs_write(struct file *file, void *buf, size_t len); +int tmpfs_read(struct file *file, void *buf, size_t len); +int tmpfs_open(struct vnode *file_node, struct file **target); +int tmpfs_close(struct file *file); + +int initramfs_mount(filesystem_t *fs, mount_t *mount); +void init_cpio(); + +/* vnode operations */ +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name); +int initramfs_create(struct vnode *dir_node, struct vnode **target, char *component_name); +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name); + +/* file operations */ +int initramfs_write(struct file *file, void *buf, size_t len); +int initramfs_read(struct file *file, void *buf, size_t len); +int initramfs_open(struct vnode *file_node, struct file **target); +int initramfs_close(struct file *file); + +#endif \ No newline at end of file diff --git a/Lab8/include/utils.h b/Lab8/include/utils.h new file mode 100644 index 000000000..a23ae37a7 --- /dev/null +++ b/Lab8/include/utils.h @@ -0,0 +1,8 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay ( unsigned long); +extern void put32 ( unsigned long, unsigned int ); +extern unsigned int get32 ( unsigned long ); + +#endif /*_BOOT_H */ diff --git a/Lab8/include/vfs.h b/Lab8/include/vfs.h new file mode 100644 index 000000000..0897269a4 --- /dev/null +++ b/Lab8/include/vfs.h @@ -0,0 +1,95 @@ +#ifndef _VFS_H +#define _VFS_H + +#include + +#define O_CREAT 00000100 +#define DIR 0 +#define FILE 1 +#define COMPONENT_NAME_MAX 32 // include '\0' +#define MAX_NUM_OF_ENTRY 16 +#define TMPFS_FILE_SIZE_MAX 4096 +#define PATHNAME_MAX 256 // include '\0' +#define VFS_PROCESS_MAX_OPEN_FILE 16 + +typedef struct vnode +{ + struct mount *mount; + struct vnode_operations *v_ops; + struct file_operations *f_ops; + struct node_info *internal; +} vnode_t; + +typedef struct node_info +{ + char *name; + unsigned int type; + unsigned int size; // if type is 'DIR' means # of entries, if is 'FILE' means size of file(in bytes). + vnode_t **entry; + char *data; +} node_info_t; + +// file handle +typedef struct file +{ + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +} file_t; + +typedef struct mount +{ + struct vnode *root; + struct filesystem *fs; +} mount_t; + +typedef struct filesystem +{ + char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +} filesystem_t; + +typedef struct vnode_operations +{ + int (*lookup)(struct vnode *dir_node, struct vnode **target, char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, char *component_name); +} vnode_operations_t; + +typedef struct file_operations +{ + int (*write)(struct file *file, void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + long (*lseek64)(struct file *file, long offset, int whence); +} file_operations_t; + +extern struct mount *rootfs; +extern char cwdpath[256]; +extern file_t *kfd[16]; +extern int fd_count; + +int register_filesystem(struct filesystem *fs); +int vfs_mount(char *target, char *filesystem); +int vfs_lookup(char *pathname, struct vnode **target); +int vfs_create(char *pathname); +int vfs_mkdir(char *pathname); + +int vfs_open(char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); + +void get_next_component(char *pathname, char *target, int level); +void basename(char *src, char *des); +void dirname(char *src, char *des); +void handle_path(char *rela, char *abso); + +void vfs_ls(char *pathname, int flag); +int vfs_cd(char *target_dir); +long vfs_lseek(struct file *file, long offset, int whence); +long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +#endif \ No newline at end of file diff --git a/Lab8/include/virtual_mem.h b/Lab8/include/virtual_mem.h new file mode 100644 index 000000000..d612b5079 --- /dev/null +++ b/Lab8/include/virtual_mem.h @@ -0,0 +1,6 @@ +#ifndef _VIRTUAL_MEM_H +#define _VIRTUAL_MEM_H + +void virtual_mem_init(); + +#endif /*_VIRTUAL_MEM_H */ \ No newline at end of file diff --git a/Lab8/initfs.cp b/Lab8/initfs.cp new file mode 100644 index 000000000..f150741ce Binary files /dev/null and b/Lab8/initfs.cp differ diff --git a/Lab8/kernel8.img b/Lab8/kernel8.img new file mode 100755 index 000000000..81690d4c8 Binary files /dev/null and b/Lab8/kernel8.img differ diff --git a/Lab8/pack.sh b/Lab8/pack.sh new file mode 100644 index 000000000..79ed28135 --- /dev/null +++ b/Lab8/pack.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd rootfs +find . | cpio -o -H newc > ../initfs.cp +cd .. \ No newline at end of file diff --git a/Lab8/rootfs/vfs1.img b/Lab8/rootfs/vfs1.img new file mode 100644 index 000000000..2c41bd71a Binary files /dev/null and b/Lab8/rootfs/vfs1.img differ diff --git a/Lab8/rootfs/vfs2.img b/Lab8/rootfs/vfs2.img new file mode 100644 index 000000000..f7baf4607 Binary files /dev/null and b/Lab8/rootfs/vfs2.img differ diff --git a/Lab8/send_kernel.py b/Lab8/send_kernel.py new file mode 100644 index 000000000..1b17155f2 --- /dev/null +++ b/Lab8/send_kernel.py @@ -0,0 +1,40 @@ +# This python script is for macOS +from serial import Serial +import os +import time +import fcntl +import threading + +file_size = os.path.getsize('kernel8.img') +print("File Size is :", file_size, "bytes") + +with Serial('/dev/tty.usbserial-0001', 115200) as ser: + sem = threading.Semaphore() + # set tty to non-blocking mode + fcntl.fcntl(ser, fcntl.F_SETFL, os.O_NONBLOCK) + + input("Press anything to start transmission\n") + + print("Sending Strat...") + sem.acquire() + ser.write(b"Start") + sem.release() + time.sleep(1) + + sem.acquire() + ser.write(file_size.to_bytes(4, 'little')) + sem.release() + time.sleep(1) + + print("Start sending kernel img by uart...") + with open('kernel8.img', 'rb') as kernel_file: + while True: + data = kernel_file.read() + if not data: + break + sem.acquire() + ser.write(data) + sem.release() + time.sleep(1) + + print("Transfer finished!") diff --git a/Lab8/sfn_nctuos.img b/Lab8/sfn_nctuos.img new file mode 100644 index 000000000..5138de3e5 Binary files /dev/null and b/Lab8/sfn_nctuos.img differ diff --git a/Lab8/src/bootloader/boot.S b/Lab8/src/bootloader/boot.S new file mode 100644 index 000000000..403960d2e --- /dev/null +++ b/Lab8/src/bootloader/boot.S @@ -0,0 +1,25 @@ +#include "mm.h" + +.section ".text.boot" + +.globl _start +_start: + mov x24, x0 + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + mov x0, x24 + bl bootloader_main + b proc_hang // should never come here diff --git a/Lab8/src/bootloader/bootloader.c b/Lab8/src/bootloader/bootloader.c new file mode 100644 index 000000000..327413557 --- /dev/null +++ b/Lab8/src/bootloader/bootloader.c @@ -0,0 +1,15 @@ +#include "mini_uart.h" +#include "load_kernel.h" + +void bootloader_main(void) +{ + uart_init(); + + uart_send_string("Relocatting ...\n"); + char *from_dest = (char *)0x80000; + char *to_dest = (char *)0x60000; + relocate((char *)from_dest, (char *)to_dest); + + char *dest = (char *)0x80000; + load_kernel((char *)dest); +} diff --git a/Lab8/src/bootloader/config.txt b/Lab8/src/bootloader/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/Lab8/src/bootloader/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/Lab8/src/bootloader/link.ld b/Lab8/src/bootloader/link.ld new file mode 100644 index 000000000..1fca3dfb7 --- /dev/null +++ b/Lab8/src/bootloader/link.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + . = 0x80000; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; +__loader_size = (_end - _start); diff --git a/Lab8/src/bootloader/load_kernel.c b/Lab8/src/bootloader/load_kernel.c new file mode 100644 index 000000000..00808de84 --- /dev/null +++ b/Lab8/src/bootloader/load_kernel.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include "mini_uart.h" +#include "peripherals/mini_uart.h" +#include "stdlib.h" + +extern int __loader_size; +extern void *_dtb_ptr; + +void load_kernel(char *dest) +{ + int size = 0; + + uart_send_string("Waiting for kernel8.img ...\n"); + + char start[5] = "Start"; + + for (int i = 0; i < 5; i++) + { + if (uart_recv() != start[i]) + i = 0; + } + + uart_send_string("Start transmitting ...\n"); + + size = uart_recv(); + size |= uart_recv() << 8; + size |= uart_recv() << 16; + size |= uart_recv() << 24; + + printf("Size of kernel is = %d bytes\n", size); + + // read the kernel + while (size--) + { + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + *dest++ = get32(AUX_MU_IO_REG); + } + + uart_send_string("End transmitting ...\n"); + + // restore arguments and jump to the new kernel. + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + // we must force an absolute address to branch to + "mov x30, 0x80000; ret"); +} + +void relocate(char *from_dest, char *to_dest) +{ + long long size = (long long)&__loader_size; + + while (size--) + { + *to_dest++ = *from_dest++; + } + + char *redicrect = __builtin_return_address(0) + (to_dest - from_dest); + + goto *redicrect; +} \ No newline at end of file diff --git a/Lab8/src/kernel/boot.S b/Lab8/src/kernel/boot.S new file mode 100644 index 000000000..5ff99aa48 --- /dev/null +++ b/Lab8/src/kernel/boot.S @@ -0,0 +1,89 @@ +#include "mm.h" + +.global _dtb_ptr +.section .data +_dtb_ptr: .dc.a 0x0 + +.section ".text.boot" + +.globl _start +.globl proc_hang +_start: + cbz x24, x24c // Check if bootloader, see bootloader's boot.S for more info +x24nc: + ldr x21, =_dtb_ptr + str x24, [x21] + b go +x24c: + ldr x21, =_dtb_ptr + str x0, [x21] +go: + mrs x0, mpidr_el1 + and x0, x0,#0xFF // Check processor id + cbz x0, master // Hang for all non-primary CPU + b proc_hang + +proc_hang: + b proc_hang + +master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + + ldr x1, =_start + + // get CurrentEL + mrs x0, CurrentEL + and x0, x0, #12 // clear reserved bits + + // running at EL3? branch if in EL3 + cmp x0, #12 + beq from_el3_to_el2 + + // running at EL1? branch if in EL1 + cmp x0, #4 + beq bss + + bl from_el2_to_el1 + +bss: + ldr x0, =__bss_start + ldr x1, =__bss_end + sub x1, x1, x0 + bl memzero + + bl set_el1_exception_vector_table // set el1 exception vector table base + + mov sp, #LOW_MEMORY // 4MB + // ldr x1, =_start + // mov sp, x1 + bl kernel_main + b proc_hang // should never come here + +from_el3_to_el2: + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, from_el2_to_el1 + msr elr_el3, x2 + eret + +from_el2_to_el1: + msr sp_el1, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // change execution level to EL1 + mov x2, #0x3c5 + msr spsr_el2, x2 + msr elr_el2, lr + eret \ No newline at end of file diff --git a/Lab8/src/kernel/config.txt b/Lab8/src/kernel/config.txt new file mode 100644 index 000000000..279349696 --- /dev/null +++ b/Lab8/src/kernel/config.txt @@ -0,0 +1,2 @@ +kernel_old=1 +disable_commandline_tags=1 diff --git a/Lab8/src/kernel/devfs.c b/Lab8/src/kernel/devfs.c new file mode 100644 index 000000000..d6a528e85 --- /dev/null +++ b/Lab8/src/kernel/devfs.c @@ -0,0 +1,129 @@ +#include "devfs.h" +#include "tmpfs.h" +#include "stdlib.h" +#include "page_alloc.h" +#include "mbox.h" + +#define DEVFS_UART_NAME "uart" +#define DEVFS_FRAMEBUFFER_NAME "framebuffer" +#define SEEK_SET 0 + +filesystem_t devfs = {.name = "devfs", .setup_mount = devfs_setup_mount}; +file_operations_t devfs_fops = {.write = devfs_write, .read = devfs_read, .open = devfs_open, .close = devfs_close, .lseek64 = devfs_lseek}; +vnode_operations_t devfs_vops = {.lookup = devfs_lookup, .create = devfs_create, .mkdir = devfs_mkdir}; + +int devfs_setup_mount(struct filesystem *fs, struct mount *mount) +{ + mount->root->f_ops = &devfs_fops; + mount->root->v_ops = &devfs_vops; + vnode_t *dir_node = mount->root; + + // Create uart + vnode_t *node_new = NULL; + int ret = dir_node->v_ops->create(dir_node, &node_new, DEVFS_UART_NAME); + if (ret == 0) + node_new->internal->type = FILE; + else + printf("Error, devfs_setup_mount(), failed to create uart, ret=%d\r\n", ret); + + // Create and init framebuffer + node_new = NULL; + ret = dir_node->v_ops->create(dir_node, &node_new, DEVFS_FRAMEBUFFER_NAME); + if (ret == 0) + { + node_new->internal->type = FILE; + node_new->internal->data = framebuffer_init(); + } + else + printf("Error, devfs_setup_mount(), failed to create framebuffer, ret=%d\r\n", ret); + + return 0; +} + +// fops +int devfs_write(struct file *file, void *buf, size_t len) +{ + if (strcmp(file->vnode->internal->name, DEVFS_UART_NAME) == 0) + { + const char *ptr = buf; + for (size_t i = 0; i < len; i++) + uart_send_byte(*ptr++); + return len; + } + else if (strcmp(file->vnode->internal->name, DEVFS_FRAMEBUFFER_NAME) == 0) + { + node_info_t *node_info = file->vnode->internal; + const char *ptr = buf; + char *dest = (char *)(node_info->data + file->f_pos); + for (size_t i = 0; i < len / sizeof(char); i++) + *dest++ = *ptr++; + file->f_pos += len; + // printf("Debug, devfs_write(), f_pos wrote to %ld\r\n", file->f_pos); + // uint64_t tk = 0; + // WAIT_TICKS(tk, 1000); + return len; + } + else + { + printf("Error, devfs_write(), writing to unrecognized device %s\r\n", file->vnode->internal->name); + return 0; + } +} + +int devfs_read(struct file *file, void *buf, size_t len) +{ + if (strcmp(file->vnode->internal->name, DEVFS_UART_NAME) == 0) + { + char *ptr = buf; + for (size_t i = 0; i < len; i++) + *ptr++ = uart_recv_byte(); + return len; + } + else + { + printf("Error, devfs_read(), reading from unrecognized device %s\r\n", file->vnode->internal->name); + return 0; + } +} + +int devfs_open(struct vnode *file_node, struct file **target) +{ + return tmpfs_open(file_node, target); +} + +int devfs_close(struct file *file) +{ + return tmpfs_close(file); +} + +long devfs_lseek(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = (size_t)offset; + // printf("Debug, devfs_lseek(), f_pos set to %ld\r\n", file->f_pos); + return file->f_pos; + } + else + { + printf("Error, devfs_lseek(), unknown whence=%d\r\n", whence); + return -1; + } +} + +// vops +int devfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + printf("Error, devfs_mkdir(), cannot mkdir with devfs\r\n"); + return 1; +} + +int devfs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + return tmpfs_create(dir_node, target, component_name); +} + +int devfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + return tmpfs_lookup(dir_node, target, component_name); +} diff --git a/Lab8/src/kernel/device_tree.c b/Lab8/src/kernel/device_tree.c new file mode 100644 index 000000000..d1b67143f --- /dev/null +++ b/Lab8/src/kernel/device_tree.c @@ -0,0 +1,266 @@ +#include +#include "peripherals/device_tree.h" +#include "stdlib.h" +#include "mini_uart.h" +#include "device_tree.h" + +typedef struct fdt_header +{ + uint32_t magic; // big-endian + 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; +} fdt_header; + +typedef struct +{ + uint32_t len; + uint32_t nameoff; +} fdt_prop; + +char *cpioDestGlobal; + +static uint64_t pad_to_4(void *num); +static uint32_t rev32(uint32_t val); +static uint64_t endian_rev(void *input, int dtype_size); + +int initramfs_callback(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + uint8_t *p = off_dt_struct; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + if (!strcmp(FDT_CPIO_INITRAMFS_PROPNAME, prop_name)) + { + uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); + printf("initramfs_addr at %p\n", addr); + cpioDestGlobal = (char *)addr; + } + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +int dtb_parser(void *dtb) +{ + fdt_header *header = (fdt_header *)dtb; + + // Magic Number + if (rev32(header->magic) != FDT_MAGIC) + { + printf("Wrong Magic Number 0x%08x\n", rev32(header->magic)); + return -1; + } + + uint8_t *off_dt_struct = (uint8_t *)header + rev32(header->off_dt_struct); + uint8_t *off_dt_strings = (uint8_t *)header + rev32(header->off_dt_strings); + + int depth = 0; + uint8_t *p = off_dt_struct; + char *node_name = NULL; + fdt_prop *prop = NULL; + char *prop_name = NULL; + char *prop_val = NULL; + + // Traverse the device tree structure + while (1) + { + const uint32_t token = rev32(*(uint32_t *)p); + switch (token) + { + case FDT_BEGIN_NODE: + // Move to the next entry + p += 4; + node_name = (char *)p; + if (depth == 0) + printf("\\ {\n"); + else + { + uart_send_space(depth * 3); + printf("%s {\n", node_name); + } + p += strlen((char *)(p)) + 1; // +1 for null terminated string + p += pad_to_4(p); + depth++; + break; + case FDT_END_NODE: + // Move to the next entry + p += 4; + depth--; + uart_send_space(depth * 3); + printf("};\n"); + printf("\n"); + break; + case FDT_PROP: + p += 4; + prop = (fdt_prop *)p; + p += sizeof(fdt_prop); + prop_name = (char *)off_dt_strings + rev32(prop->nameoff); + prop_val = (char *)p; + + if (!strcmp(prop_name, "#address-cells") || !strcmp(prop_name, "#size-cells") || !strcmp(prop_name, "interrupt-parent")) + { + // + uart_send_space(depth * 3); + printf("%s = <%d>;\n", prop_name, rev32(*((uint32_t *)prop_val))); + } + else if (!strcmp(prop_name, "model") || !strcmp(prop_name, "status") || !strcmp(prop_name, "name") || !strcmp(prop_name, "device_type") || + !strcmp(prop_name, "chassis-type") || !strcmp(prop_name, "bootargs") || !strcmp(prop_name, "stdout-path") || !strcmp(prop_name, "stdin-path") || + !strcmp(prop_name, "power-isa-version") || !strcmp(prop_name, "mmu-type") || !strcmp(prop_name, "label") || !strcmp(prop_name, "phy-connection-type")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else if (!strcmp(prop_name, "compatible")) + { + // + uart_send_space(depth * 3); + printf("%s = \"%s\";\n", prop_name, prop_val); + } + else + { + uart_send_space(depth * 3); + printf("%s = %s;\n", prop_name, prop_val); + } + + p += rev32(prop->len); + p += pad_to_4(p); + break; + case FDT_NOP: + p += 4; + break; + case FDT_END: + return rev32(header->totalsize); + default: + // Invalid entry + printf("Invalid FDT entry\n"); + return -1; + } + } + + // Property not found + return -1; +} + +void fdt_traverse(fdt_callback cb, char *dtb) +{ + if (cb(dtb) == -1) + printf("fdt_traverse failed.\n"); + + return; +} + +static uint64_t pad_to_4(void *num) +{ + uint64_t modded = ((uint64_t)num) % 4; + return modded == 0 ? 0 : 4 - modded; +} + +static uint32_t rev32(uint32_t val) +{ + return (uint32_t)endian_rev(&val, 4); +} + +/** Transform data from litle to big endian, or from big to little endian + * @param input: Pointer to a value that needs to be transformed + * @param dtype_size: data type size, size of each item in bytes. Possible value: 1, 2, 4, 8 + */ +static uint64_t endian_rev(void *input, int dtype_size) +{ + const uint8_t *ptr = (uint8_t *)input; + uint64_t ret = 0; + + switch (dtype_size) + { + // int8_t, uint8_t + case 1: + // No need to transform to big endian since the data type size is 1 byte + break; + + // int16_t, uint16_t + case 2: + ret = (ptr[0] << 8) | ptr[1]; + break; + + // int32_t, uint32_t + case 4: + ret = (ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + ptr[3]; + break; + + // int64_t, uint64_t + // case 8: + // ret = (ptr[0] << 56) | + // (ptr[1] << 48) | + // (ptr[2] << 40) | + // (ptr[3] << 32) | + // (ptr[4] << 24) | + // (ptr[5] << 16) | + // (ptr[6] << 8) | + // ptr[7]; + // break; + + default: + printf("[Error] Endian transformation(%d) not implemented. @line %d, file:%s\r\n", dtype_size, __LINE__, __FILE__); + break; + } + return ret; +} \ No newline at end of file diff --git a/Lab8/src/kernel/dynamic_alloc.c b/Lab8/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..a16a426b0 --- /dev/null +++ b/Lab8/src/kernel/dynamic_alloc.c @@ -0,0 +1,249 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "list.h" + +extern page_frame_node frame_array[TOTAL_NUM_PAGE]; + +pool_list pool[33]; // 1, 2, 4, 6, 8, 16, 32 +chunk chunk_array[3000]; +int global_chunk_index = -1; + +void init_pool() +{ + for (int i = 0; i < 33; i++) + INIT_LIST_HEAD(&pool[i].list); + return; +} + +chunk *new_chunk() +{ + global_chunk_index++; + chunk_array[global_chunk_index].index = global_chunk_index; + return &chunk_array[global_chunk_index]; +} + +void *get_chunk(int req_size) +{ + int req_pool_index = roundup_size(req_size); // req_pool_index * MIN_CHUNK_SIZE = req_size + + if (list_empty(&pool[req_pool_index].list)) + { + // empty pool on req_size + int frame_index = get_page_from_free_list(MIN_PAGE_SIZE, req_pool_index); + split_page(frame_index, req_pool_index); + } + + int index = remove_a_chunk_from_pool(req_pool_index); + if (index == -1) + return NULL; + + void *addr = chunk_array[index].addr; + return addr; +} + +void split_page(int frame_index, int req_pool_index) +{ + int split_size = (req_pool_index * MIN_CHUNK_SIZE); + for (int i = 0; i < (MIN_PAGE_SIZE / split_size); i++) + { + chunk *new = new_chunk(); + new->size = split_size; + new->addr = (void *)frame_array[frame_index].addr + i *split_size; + new->val = FREE; + new->belong_page = frame_index; + list_add_tail(&new->list, &pool[req_pool_index].list); + } +} + +int remove_a_chunk_from_pool(int req_pool_index) +{ + chunk *alloc_chunk = container_of(pool[req_pool_index].list.next, chunk, list); + list_del_init(pool[req_pool_index].list.next); + alloc_chunk->val = ALLOCATED; + return alloc_chunk->index; +} + +void put_back_to_pool(int pool_index, int chunk_index) +{ + // addr to insert + struct list_head *node = &chunk_array[chunk_index].list; + unsigned long addr_long = (unsigned long)chunk_array[chunk_index].addr; + + // insert in by the order of addr + struct list_head *iter = &pool[pool_index].list; + struct list_head *start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + unsigned long iter_addr_long = (unsigned long)tmp->addr; + if (iter_addr_long > addr_long) + { + // list_insert() + iter->prev->next = node; + node->prev = iter->prev; + iter->prev = node; + node->next = iter; + + tmp->size = -1; + tmp->val = FREE; + + break; + } + } + + // check if there are free chunks in same page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + int count = 0; + int page_id = addr_long >> 12; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + count++; + else + break; + } + if (count == (MIN_PAGE_SIZE / (pool_index * MIN_CHUNK_SIZE))) + { + // There is a free page + iter = &pool[pool_index].list; + start = &pool[pool_index].list; + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = list_entry(iter, chunk, list); + unsigned long tmp_addr_long = (unsigned long)tmp->addr; + if (tmp_addr_long >> 12 == page_id) + break; + } + chunk *first_chunk_in_page = list_entry(iter, chunk, list); + for (int i = 0; i < count; i++) + { + chunk *tmp = list_entry(iter, chunk, list); + tmp->val = FREE; + struct list_head *tmp_next = iter->next; + iter->prev->next = iter->next; + iter->next->prev = iter->prev; + iter->prev = iter; + iter->next = iter; + iter = tmp_next; + } + free_page_frame(first_chunk_in_page->belong_page); + } + + return; +} + +int free_chunk(int index) +{ + // free the page + int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; + put_back_to_pool(pool_index, index); + + return 0; +} + +void free(void *addr) +{ + // printf("[DEBUG] Freeing addr = %p\n", addr); + // Check addr is in which page frame + unsigned long addr_long = (unsigned long)addr; + int frame_index = (addr_long - FREE_MEM_START) / MIN_PAGE_SIZE; + + if (frame_array[frame_index].val != ALLOCATED) + { + printf("This page is Not Allocated yet\n"); + return; + } + + if (frame_array[frame_index].chunk_order != -1) + { + int chunk_index = -1; + // Find chunk_index + for (int i = 0; i < global_chunk_index; i++) + { + if (addr == chunk_array[i].addr) + { + chunk_index = i; + break; + } + } + // Check if is OK to free + if (chunk_index >= global_chunk_index || chunk_array[chunk_index].val != ALLOCATED) + { + if (chunk_index >= global_chunk_index) + printf("chunk_index is TOO high\n"); + if (chunk_array[chunk_index].val != ALLOCATED) + printf("chunk_index = %d\n", chunk_index); + printf("This chunk is Not Allocated yet\n"); + return; + } + + free_chunk(chunk_index); + return; + } + else + { + free_page_frame(frame_index); + return; + } +} + +int roundup_size(int size) +{ + switch (size) + { + case 1 ... 8: + return 1; + case 9 ... 16: + return 2; + case 17 ... 32: + return 4; + case 33 ... 48: + return 6; + case 49 ... 64: + return 8; + case 65 ... 128: + return 16; + case 129 ... 256: + return 32; + } + return 0; +} + +void debug_pool() +{ + printf("** DEBUGGING pool\n"); + for (int i = 0; i < 33; i++) + { + struct list_head *iter; + struct list_head *start; + iter = &pool[i].list; + start = &pool[i].list; + printf("pool[%d] -> ", i); + while (iter->next != start) + { + iter = iter->next; + chunk *tmp = container_of(iter, chunk, list); + printf("addr %p -> ", tmp->addr); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING chunk\n"); + for (int i = 0; i < 20; i++) + { + printf("chunk_array[%d].index = %d\n", i, chunk_array[i].index); + printf("chunk_array[%d].size = %d\n", i, chunk_array[i].size); + printf("chunk_array[%d].addr = %p\n", i, chunk_array[i].addr); + printf("chunk_array[%d].val = %d\n", i, chunk_array[i].val); + printf("chunk_array[%d].belong_page= %d\n", i, chunk_array[i].belong_page); + printf("\n"); + } + printf("**\n"); +} \ No newline at end of file diff --git a/Lab8/src/kernel/exception.S b/Lab8/src/kernel/exception.S new file mode 100644 index 000000000..ab8b8cc66 --- /dev/null +++ b/Lab8/src/kernel/exception.S @@ -0,0 +1,189 @@ +#define CORE0_INTERRUPT_SOURCE 0x40000060 +#define IIR_ADDR 0x3f215048 + +.global set_el1_exception_vector_table +.global exception_vector_table +.global enable_interrupt +.global disable_interrupt + +enable_interrupt: + msr DAIFClr, 0xf + ret + +disable_interrupt: + msr DAIFSet, 0xf + ret + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 8 + 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] +.endm + +.macro save_all_sys + 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] + mrs x0, spsr_el1 + mrs x1, elr_el1 + mrs x2, sp_el0 + stp x30, x0, [sp ,16 * 15] + stp x1, x2, [sp ,16 * 16] +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 8 +.endm + +.macro load_all_sys + ldp x1, x2, [sp ,16 * 16] + ldp x30, x0, [sp ,16 * 15] + msr spsr_el1, x0 + msr elr_el1, x1 + msr sp_el0, x2 + 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, 32 * 9 +.endm + +.align 11 // vector table should be aligned to 0x800 +el1_exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_same_el + .align 7 + b exception_handler_irq_sp_elx_same_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler_sync_sp_elx_lower_el + .align 7 + b exception_handler_irq_sp_elx_lower_el + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_el1_exception_vector_table: + adr x0, el1_exception_vector_table + msr vbar_el1, x0 + ret lr + +exception_handler: + save_all + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + bl exc_handler + load_all + eret + +exception_handler_sync_sp_elx_lower_el: + save_all_sys + mov x0, sp + bl el0_to_el1_sync_handler + load_all_sys + eret + +exception_handler_irq_sp_elx_lower_el: + b el0_core_timer_handler + +exception_handler_sync_sp_elx_same_el: + b exception_handler + +exception_handler_irq_sp_elx_same_el: + b el1_core_interrupt_handler + +el0_core_timer_handler: + save_all_sys + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all_sys + eret + +el1_core_interrupt_handler: + save_all_sys + bl el1_irq_interrupt_handler + load_all_sys + eret \ No newline at end of file diff --git a/Lab8/src/kernel/exception.c b/Lab8/src/kernel/exception.c new file mode 100644 index 000000000..6c8b009fb --- /dev/null +++ b/Lab8/src/kernel/exception.c @@ -0,0 +1,335 @@ +#include "peripherals/mini_uart.h" +#include "peripherals/irq.h" +#include "stdlib.h" +#include "timer.h" +#include "thread.h" +#include "syscall.h" + +extern task_struct *get_current(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern signal_handler signal_table[]; +extern void proc_hang(); + +/** + * common exception handler + */ +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +{ + // print out interruption type + switch (type) + { + case 0: + uart_send_string("Synchronous"); + break; + case 1: + uart_send_string("IRQ"); + break; + case 2: + uart_send_string("FIQ"); + break; + case 3: + uart_send_string("SError"); + break; + } + uart_send_string(": "); + // decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28) + switch (esr >> 26) + { + case 0b000000: + uart_send_string("Unknown"); + break; + case 0b000001: + uart_send_string("Trapped WFI/WFE"); + break; + case 0b001110: + uart_send_string("Illegal execution"); + break; + case 0b010101: + uart_send_string("System call"); + break; + case 0b100000: + uart_send_string("Instruction abort, lower EL"); + break; + case 0b100001: + uart_send_string("Instruction abort, same EL"); + break; + case 0b100010: + uart_send_string("Instruction alignment fault"); + break; + case 0b100100: + uart_send_string("Data abort, lower EL"); + break; + case 0b100101: + uart_send_string("Data abort, same EL"); + break; + case 0b100110: + uart_send_string("Stack alignment fault"); + break; + case 0b101100: + uart_send_string("Floating point"); + break; + default: + uart_send_string("Unknown"); + break; + } + // decode data abort cause + if (esr >> 26 == 0b100100 || esr >> 26 == 0b100101) + { + uart_send_string(", "); + switch ((esr >> 2) & 0x3) + { + case 0: + uart_send_string("Address size fault"); + break; + case 1: + uart_send_string("Translation fault"); + break; + case 2: + uart_send_string("Access flag fault"); + break; + case 3: + uart_send_string("Permission fault"); + break; + } + switch (esr & 0x3) + { + case 0: + uart_send_string(" at level 0"); + break; + case 1: + uart_send_string(" at level 1"); + break; + case 2: + uart_send_string(" at level 2"); + break; + case 3: + uart_send_string(" at level 3"); + break; + } + } + // dump registers + uart_send_string("\nSPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" ; ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string(" ; ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ; FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + + proc_hang(); + return; +} + +void el1_irq_interrupt_handler() +{ + unsigned int irq_basic_pending = get32(IRQ_BASIC_PENDING); + irq_basic_pending &= (1 << 19); // clear bits + + // GPU IRQ 57 : UART Interrupt + if (irq_basic_pending) + { + if (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + { + uart_rx_handler(); + } + else if (get32(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + { + uart_tx_handler(); + } + } + // ARM Core Timer Interrupt + else if (get32(CORE0_INTR_SRC) & (1 << 1)) + { + long cntpct_el0, cntfrq_el0; + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)); + el1_timer_handler(cntpct_el0, cntfrq_el0); + } + + return; +} + +void el0_to_el1_sync_handler(unsigned long trapframe_addr) +{ + int syscall_no; + trapframe *curr_trapframe = (trapframe *)trapframe_addr; + asm volatile("mov %0, x8" + : "=r"(syscall_no)::); + if (syscall_no == 0) + { + int pid = getpid(); + curr_trapframe->x[0] = pid; + } + else if (syscall_no == 1) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + disable_uart_irq(); + enable_interrupt(); + unsigned int ret = uart_read(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 2) + { + char *buf = (char *)curr_trapframe->x[0]; + unsigned int size = curr_trapframe->x[1]; + unsigned int ret = uart_write(buf, size); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 3) + { + char *name = (char *)curr_trapframe->x[0]; + char **argv = (char **)curr_trapframe->x[1]; + int ret = exec(name, argv); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 4) + { + task_struct *current = get_current(); + current->status = FORKING; + current->trapframe = trapframe_addr; + int ret = fork(); + curr_trapframe = (trapframe *)get_current()->trapframe; + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 5) + { + int status = curr_trapframe->x[0]; + exit(status); + } + else if (syscall_no == 6) + { + unsigned char ch = (unsigned char)curr_trapframe->x[0]; + unsigned int *mbox_user = (unsigned int *)curr_trapframe->x[1]; + int ret = mbox_call_u(ch, mbox_user); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 7) + { + int pid = (int)curr_trapframe->x[0]; + kill(pid); + } + else if (syscall_no == 8) + { + // signal + int SIGNAL = (int)curr_trapframe->x[0]; + void (*handler)() = (void (*)())curr_trapframe->x[1]; + signal(SIGNAL, handler); + } + else if (syscall_no == 9) + { + // signal kill + int pid = (int)curr_trapframe->x[0]; + int SIGNAL = (int)curr_trapframe->x[1]; + int if_cust = 0; + task_struct *current = get_current(); + + if (current->custom_signal) + { + custom_signal *cust_sig = current->custom_signal; + do + { + if (cust_sig->sig_num == SIGNAL) + { + if_cust = 1; + // signal's context save + sig_context_update(curr_trapframe, cust_sig->handler); + break; + } + cust_sig = container_of(cust_sig->list.next, custom_signal, list); + } while (cust_sig != current->custom_signal); + } + else if (!current->custom_signal && !if_cust) + (signal_table[SIGNAL])(pid); + } + else if (syscall_no == 10) + { + // signal restore + sig_context_restore(curr_trapframe); + + disable_interrupt(); + task_struct *current = get_current(); + free(current->signal_context->trapframe); + free(current->signal_context->user_stack); + free(current->signal_context); + current->signal_context = NULL; + enable_interrupt(); + } + else if (syscall_no == 11) + { + char *pathname = (char *)curr_trapframe->x[0]; + int flags = (int)curr_trapframe->x[1]; + int ret = open(pathname, flags); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 12) + { + int fd = (int)curr_trapframe->x[0]; + int ret = close(fd); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 13) + { + int fd = (int)curr_trapframe->x[0]; + void *buf = (void *)curr_trapframe->x[1]; + unsigned long count = (unsigned long)curr_trapframe->x[2]; + int ret = write(fd, buf, count); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 14) + { + int fd = (int)curr_trapframe->x[0]; + void *buf = (void *)curr_trapframe->x[1]; + unsigned long count = (unsigned long)curr_trapframe->x[2]; + int ret = read(fd, buf, count); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 15) + { + char *pathname = (char *)curr_trapframe->x[0]; + unsigned mode = (unsigned)curr_trapframe->x[1]; + int ret = mkdir(pathname, mode); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 16) + { + char *src = (char *)curr_trapframe->x[0]; + char *target = (char *)curr_trapframe->x[1]; + char *filesystem = (char *)curr_trapframe->x[2]; + unsigned long flags = (unsigned long)curr_trapframe->x[3]; + void *data = (void *)curr_trapframe->x[4]; + int ret = mount(src, target, filesystem, flags, data); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 17) + { + char *pathname = (char *)curr_trapframe->x[0]; + int ret = chdir(pathname); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 18) + { + int fd = (int)curr_trapframe->x[0]; + long offset = (long)curr_trapframe->x[1]; + int whence = (int)curr_trapframe->x[2]; + int ret = lseek(fd, offset, whence); + curr_trapframe->x[0] = ret; + } + else if (syscall_no == 19) + { + int fd = (int)curr_trapframe->x[0]; + unsigned long request = (unsigned long)curr_trapframe->x[1]; + unsigned long arg = (unsigned long)curr_trapframe->x[2]; + int ret = ioctl(fd, request, arg); + curr_trapframe->x[0] = ret; + } +} \ No newline at end of file diff --git a/Lab8/src/kernel/fat32.c b/Lab8/src/kernel/fat32.c new file mode 100644 index 000000000..5337bb6ee --- /dev/null +++ b/Lab8/src/kernel/fat32.c @@ -0,0 +1,260 @@ +#include "fat32.h" +#include "tmpfs.h" +#include "stdlib.h" + +int fat32fs_mounted = 0; + +uint32_t FAT1_LBA; // Logical Block Address, FAT has size of sec_per_fat32*SD_BLOCK_SIZE bytes +uint32_t root_dir_LBA; // physical block +uint32_t root_cluster; // boot_sector_t.root_cluster, usually 2 +uint32_t sec_per_fat32; // boot_sector_t.sector_per_fat32 + +filesystem_t fat32fs = { + .name = "fat32fs", + .setup_mount = fat32fs_mount}; +file_operations_t fat32fs_fops = {.write = fat32fs_write, .read = fat32fs_read, .open = fat32fs_open, .close = fat32fs_close}; +vnode_operations_t fat32fs_vops = {.lookup = fat32fs_lookup, .create = fat32fs_create, .mkdir = fat32fs_mkdir}; + +int fat32fs_mount(struct filesystem *fs, struct mount *mount) +{ + mount->root->f_ops = &fat32fs_fops; + mount->root->v_ops = &fat32fs_vops; + + /* parse MBR */ + MBR_t mbr; + readblock(0, &mbr); + + if (mbr.signature[0] != 0x55 || mbr.signature[1] != 0xAA) + return -1; + + boot_sector_t boot_sector; + // readblock(mbr.part1.relative_sector, &boot_sector); + readblock(2048, &boot_sector); + + // FAT1_LBA = mbr.part1.relative_sector + boot_sector.reserved_sector_cnt; + FAT1_LBA = 2048 + boot_sector.reserved_sector_cnt; + root_dir_LBA = FAT1_LBA + (boot_sector.fat_cnt * boot_sector.sector_per_fat32); + root_cluster = boot_sector.root_cluster; + sec_per_fat32 = boot_sector.sector_per_fat32; + + char *buf = my_malloc(sizeof(char *) * SECTOR_SIZE); + readblock(root_dir_LBA, buf); + dir_entry *root_dir_entry = (dir_entry *)buf; + + /* Insert files in the root_dir_entry to the file system */ + vnode_t *dir_node = mount->root; + vnode_t *node_new = NULL; + const dir_entry *item = (dir_entry *)root_dir_entry; + for (int i = 0; i < SD_BLOCK_SIZE / sizeof(dir_entry); i++) + { + // Skip empty entry + if (item[i].name[0] != '\0') + { + char *name = my_malloc(sizeof(item->name) + 2); // +1 for . in fileName.ext and end of string + + // Copy entry name + fat32_filename_to_str(item[i].name, name); + + // Create entry along tmpfs + switch (tmpfs_lookup(dir_node, &node_new, name)) + { + case NOTFOUND: + tmpfs_create(dir_node, &node_new, name); + } + + // Config entry to a file + node_new->internal->type = FILE; + node_new->internal->size = item[i].fileSize; + node_new->internal->data = (char *)(uint64_t)root_dir_LBA + (item[i].fstClusHI << 16 | item[i].fstClusLO) - root_cluster; + } + } + + fat32fs_mounted = 1; + return 0; +} + +int fat32fs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + switch (tmpfs_lookup(dir_node, target, component_name)) + { + case NOTFOUND: + return NOTFOUND; + case NOTDIR: + return NOTDIR; + case EXISTED: + return EXISTED; + } + + return 0; +} + +int fat32fs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) { return 0; } + +int fat32fs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + /* Find free entry in FAT1 and mark it as used (eof) */ + char temp[SD_BLOCK_SIZE]; + uint32_t free_entry_LBA = 0; + for (int j = 0; j < sec_per_fat32; j++) + { + readblock(FAT1_LBA + j, temp); + uint32_t *fat_entry = (uint32_t *)temp; + for (int i = 0; i < SD_BLOCK_SIZE / sizeof(uint32_t); i++) + { + if (fat_entry[i] == FAT_ENTRY_EMPTY) + { + fat_entry[i] = FAT_ENTRY_EOF; // immediate mark this as entry EOF, i.e., this file occupies 1 sector + writeblock(FAT1_LBA + j, temp); // write back + free_entry_LBA = j * SD_BLOCK_SIZE / sizeof(uint32_t) + i; + break; + } + } + if (free_entry_LBA != 0) + break; + } + + if (free_entry_LBA == 0) + { + printf("[info/fat32fs_create] Fail to find free entry in FAT1\n"); + return 1; + } + + /* Update root dir entry */ + readblock(root_dir_LBA, temp); + dir_entry *root_dir_entry = (dir_entry *)temp; + dir_entry *item = (dir_entry *)root_dir_entry; + int i = 0; + for (i = 0; i < SD_BLOCK_SIZE / sizeof(dir_entry); i++) + { + // Find empty entry + if (item[i].name[0] == '\0') + break; + } + if (i >= SD_BLOCK_SIZE / sizeof(dir_entry)) + { + printf("[info/fat32fs_write] Fail to find free entry in root_dir_entry\n"); + return -1; + } + + int j = 0; // component_name[j] + int k = 0; // item->name[k] + // Copy filename + while (k < 8 && component_name[j] != '\0' && component_name[j] != '.') + item[i].name[k++] = component_name[j++]; + j++; // skip '.' + // Fill space + while (k < 8) + item[i].name[k++] = ' '; + // Copy extention + while (k < 11 && component_name[j] != '\0') + item[i].name[k++] = component_name[j++]; + + /* Config entry metadata and write back to SD card */ + item[i].attr = DIR_ENTRY_ATTR_ARCHIVE; + item[i].fstClusHI = (free_entry_LBA >> 16) & 0x0000FFFF; + item[i].fstClusLO = free_entry_LBA & 0x0000FFFF; + item[i].fileSize = 0; + writeblock(root_dir_LBA, temp); // since item is sort of temp's reference + + readblock(root_dir_LBA, temp); + root_dir_entry = (dir_entry *)temp; + item = (dir_entry *)root_dir_entry; + i = 0; + for (i = 0; i < SD_BLOCK_SIZE / sizeof(dir_entry); i++) + { + // Skip empty entry + if (item[i].name[0] != '\0') + { + char name[13]; // 11 char + '.' + '\0' + // Copy entry name + fat32_filename_to_str(item[i].name, name); + } + } + + int ret = tmpfs_create(dir_node, target, component_name); + if (ret == 0) + (*target)->internal->data = (char *)(uint64_t)root_dir_LBA + free_entry_LBA - root_cluster; + return ret; +} + +// fops +int fat32fs_write(struct file *file, void *buf, size_t len) +{ + len = len <= SD_BLOCK_SIZE ? len : SD_BLOCK_SIZE; + uint32_t block_cnt = len / SD_BLOCK_SIZE + (len % SD_BLOCK_SIZE != 0); + char *temp = my_malloc(SD_BLOCK_SIZE * block_cnt); + char name[13]; // 11 char + '.' + '\0' + + /* Read modify write */ + uint32_t phy_block = (uint64_t)file->vnode->internal->data; + readblock(phy_block, temp); + memcpy(temp, buf, len); + writeblock(phy_block, temp); + + /* Update root dir entry */ + readblock(root_dir_LBA, temp); + dir_entry *root_dir_entry = (dir_entry *)temp; + dir_entry *item = (dir_entry *)root_dir_entry; + int i = 0; + for (i = 0; i < SD_BLOCK_SIZE / sizeof(dir_entry); i++) + { + // Skip empty entry + if (item[i].name[0] != '\0') + { + // Copy entry name + fat32_filename_to_str(item[i].name, name); + if (!strcmp(file->vnode->internal->name, name)) + break; + } + } + if (i >= SD_BLOCK_SIZE / sizeof(dir_entry)) + { + printf("[ERROR/fat32fs_write] %s not found\n", file->vnode->internal->name); + return -1; + } + item[i].fileSize = len; + writeblock(root_dir_LBA, temp); + + file->vnode->internal->size = len; + free(temp); + + return len; +} + +int fat32fs_read(struct file *file, void *buf, size_t len) +{ + len = len <= file->vnode->internal->size ? len : file->vnode->internal->size; + uint32_t block_cnt = len / SD_BLOCK_SIZE + (len % SD_BLOCK_SIZE != 0); + char *temp = my_malloc(SD_BLOCK_SIZE * block_cnt); + uint32_t phy_block = (uint64_t)file->vnode->internal->data; + for (int i = 0; i < block_cnt; i++) + readblock(phy_block + i, temp + i * SD_BLOCK_SIZE); + memcpy(buf, temp, len); + + free(temp); + return len; +} + +int fat32fs_open(struct vnode *file_node, struct file **target) +{ + return tmpfs_open(file_node, target); +} + +int fat32fs_close(struct file *file) +{ + return tmpfs_close(file); +} + +int fat32_filename_to_str(const char *fat32_filename, char *str) +{ + // Copy entry name + int j = 0; // str[j] + int k = 0; // fat32_filename[k]; + for (k = 0; k < 8 && fat32_filename[k] != ' '; k++) + str[j++] = fat32_filename[k]; + str[j++] = '.'; + for (k = 8; k < 11 && fat32_filename[k] != ' '; k++) + str[j++] = fat32_filename[k]; + str[j] = '\0'; + return j; +} \ No newline at end of file diff --git a/Lab8/src/kernel/kernel.c b/Lab8/src/kernel/kernel.c new file mode 100644 index 000000000..6b4367464 --- /dev/null +++ b/Lab8/src/kernel/kernel.c @@ -0,0 +1,40 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "reserve_mem.h" +#include "device_tree.h" +#include "thread.h" +#include "virtual_mem.h" +#include "vfs.h" +#include "tmpfs.h" +#include "stdlib.h" +#include "sdhost.h" + +extern void *_dtb_ptr; +extern char *cpioDestGlobal; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + // fdt_traverse(initramfs_callback, _dtb_ptr); + cpioDestGlobal = (char *)0x8000000; + + thread_init(); + + memory_init(); + + // virtual_mem_init(); + + vfs_mount("/", "tmpfs"); + vfs_mount("/initramfs", "initramfs"); + vfs_mount("/dev", "devfs"); + strcpy(cwdpath, rootfs->root->internal->name); + + sd_init(); + vfs_mount("/boot", "fat32fs"); + + shell_start(); +} diff --git a/Lab8/src/kernel/link.ld b/Lab8/src/kernel/link.ld new file mode 100644 index 000000000..a460f25b2 --- /dev/null +++ b/Lab8/src/kernel/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x80000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab8/src/kernel/mbox.c b/Lab8/src/kernel/mbox.c new file mode 100644 index 000000000..ca0729b2c --- /dev/null +++ b/Lab8/src/kernel/mbox.c @@ -0,0 +1,133 @@ +#include "peripherals/mbox_call.h" +#include "mbox_call.h" +#include "mini_uart.h" +#include "stdlib.h" + +void mbox_main() +{ + // get the board's unique serial number with a mailbox call + mbox[0] = 21 * 4; // length of the message + mbox[1] = MBOX_REQUEST; // this is a request message + + mbox[2] = MBOX_TAG_MODEL; + mbox[3] = 4; + mbox[4] = 0; // ?? + mbox[5] = 0; + + mbox[6] = MBOX_TAG_REVISION; + mbox[7] = 4; + mbox[8] = 0; // ?? + mbox[9] = 0; + + mbox[10] = MBOX_TAG_GETSERIAL; // get serial number command + mbox[11] = 8; // buffer size + mbox[12] = 8; // ?? + mbox[13] = 0; // clear output buffer + mbox[14] = 0; + + mbox[15] = MBOX_TAG_ARM_MEMORY; // get serial number command + mbox[16] = 8; // buffer size + mbox[17] = 8; // ?? + mbox[18] = 0; // clear output buffer + mbox[19] = 0; + + mbox[20] = MBOX_TAG_LAST; + + // send the message to the GPU and receive answer + if (mbox_call(MBOX_CH_PROP)) + { + uart_send_string("Board model: "); + uart_hex(mbox[5]); + uart_send_string("\n"); + + uart_send_string("Board revision: "); + uart_hex(mbox[9]); + uart_send_string("\n"); + + uart_send_string("Serial number: "); + uart_hex(mbox[14]); + uart_hex(mbox[13]); + uart_send_string("\n"); + + uart_send_string("ARM memory base address: "); + uart_hex(mbox[18]); + uart_send_string("\n"); + + uart_send_string("ARM memory size: "); + uart_hex(mbox[19]); + uart_send_string("\n"); + } + else + { + uart_send_string("Unable to query serial!\n"); + } +} + +// lab7, ref: https://oscapstone.github.io/labs/lab7.html +char *framebuffer_init() +{ + unsigned int __attribute__((unused)) width, height, pitch, isrgb; /* dimensions and channel order */ + char *lfb; /* raw frame buffer address */ + + mbox[0] = 35 * 4; + mbox[1] = MBOX_REQUEST; + + mbox[2] = 0x48003; // set phy wh + mbox[3] = 8; + mbox[4] = 8; + mbox[5] = 1024; // FrameBufferInfo.width + mbox[6] = 768; // FrameBufferInfo.height + + mbox[7] = 0x48004; // set virt wh + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1024; // FrameBufferInfo.virtual_width + mbox[11] = 768; // FrameBufferInfo.virtual_height + + mbox[12] = 0x48009; // set virt offset + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // FrameBufferInfo.x_offset + mbox[16] = 0; // FrameBufferInfo.y.offset + + mbox[17] = 0x48005; // set depth + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // FrameBufferInfo.depth + + mbox[21] = 0x48006; // set pixel order + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB, not BGR preferably + + mbox[25] = 0x40001; // get framebuffer, gets alignment on request + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // FrameBufferInfo.pointer + mbox[29] = 0; // FrameBufferInfo.size + + mbox[30] = 0x40008; // get pitch + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // FrameBufferInfo.pitch + + mbox[34] = MBOX_TAG_LAST; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) + { + mbox[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + width = mbox[5]; // get actual physical width + height = mbox[6]; // get actual physical height + pitch = mbox[33]; // get number of bytes per line + isrgb = mbox[24]; // get the actual channel order + lfb = (void *)((unsigned long)mbox[28]); + return lfb; + } + else + { + printf("Error, framebuffer_init(), Unable to set screen resolution to 1024x768x32\r\n"); + return NULL; + } +} \ No newline at end of file diff --git a/Lab8/src/kernel/mbox_call.c b/Lab8/src/kernel/mbox_call.c new file mode 100644 index 000000000..d0626f0e8 --- /dev/null +++ b/Lab8/src/kernel/mbox_call.c @@ -0,0 +1,42 @@ +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" + +/* mailbox message buffer */ +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mbox_call(unsigned char ch) +{ + unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} \ No newline at end of file diff --git a/Lab8/src/kernel/my_signal.c b/Lab8/src/kernel/my_signal.c new file mode 100644 index 000000000..e50ee5e22 --- /dev/null +++ b/Lab8/src/kernel/my_signal.c @@ -0,0 +1,59 @@ +#include "thread.h" +#include "my_signal.h" +#include "syscall.h" +#include "page_alloc.h" +#include "stdlib.h" + +extern task_struct *get_current(); + +signal_handler signal_table[] = { + [0] = &sig_ignore, + [1] = &sig_ignore, + [2] = &sig_ignore, + [3] = &sig_ignore, + [4] = &sig_ignore, + [5] = &sig_ignore, + [6] = &sig_ignore, + [7] = &sig_ignore, + [8] = &sig_ignore, + [SIGKILL] = &sigkill_handler, +}; + +#define current get_current() + +void sig_ignore(int _) +{ + return; +} + +void sigkill_handler(int pid) +{ + kill(pid); + return; +} + +void sig_context_update(struct _trapframe *trapframe, void (*handler)()) +{ + signal_context *sig_context = (signal_context *)my_malloc(sizeof(signal_context)); + sig_context->trapframe = (struct _trapframe *)my_malloc(sizeof(struct _trapframe)); + sig_context->user_stack = my_malloc(MIN_PAGE_SIZE); + memcpy(sig_context->trapframe, trapframe, sizeof(struct _trapframe)); + + current->signal_context = sig_context; + + trapframe->x[30] = (unsigned long)&sig_return; + trapframe->elr_el1 = (unsigned long)handler; + trapframe->sp_el0 = (unsigned long)sig_context->user_stack + MIN_PAGE_SIZE; +} + +void sig_return(void) +{ + asm volatile( + "mov x8, 10\n" + "svc 0\n"); +} + +void sig_context_restore(struct _trapframe *trapframe) +{ + memcpy(trapframe, current->signal_context->trapframe, sizeof(struct _trapframe)); +} diff --git a/Lab8/src/kernel/page_alloc.c b/Lab8/src/kernel/page_alloc.c new file mode 100644 index 000000000..a7202e2bb --- /dev/null +++ b/Lab8/src/kernel/page_alloc.c @@ -0,0 +1,308 @@ +#include "page_alloc.h" +#include "math.h" +#include "stddef.h" +#include "stdlib.h" +#include "reserve_mem.h" +#include "dynamic_alloc.h" + +page_frame_node free_list[MAX_ORDER + 1]; +// int frame_array[TOTAL_NUM_PAGE]; // Why NOT use? no malloc to allocate new link list node +page_frame_node frame_array[TOTAL_NUM_PAGE]; +extern reserved_memory_block RMarray[100]; + +// initialize frame_array and free_list +void init_page_frame() +{ + // free_list + for (int i = 0; i <= MAX_ORDER; i++) + { + free_list[i].index = -1; // head of link list + free_list[i].next = NULL; + free_list[i].previous = NULL; + } + free_list[MAX_ORDER].next = &frame_array[0]; + + // frame_array + frame_array[0].index = 0; + frame_array[0].val = MAX_ORDER; + frame_array[0].addr = (void *)FREE_MEM_START; + frame_array[0].contiguous_head = -1; + frame_array[0].allocated_order = -1; + frame_array[0].next = &frame_array[0 + (1 << MAX_ORDER)]; + frame_array[0].previous = &free_list[MAX_ORDER]; + int previous_index = 0; + for (int i = 1; i < TOTAL_NUM_PAGE; i++) + { + frame_array[i].index = i; + frame_array[i].addr = (void *)FREE_MEM_START + i * MIN_PAGE_SIZE; + frame_array[i].contiguous_head = -1; + frame_array[i].allocated_order = -1; + if (i % (1 << MAX_ORDER) == 0) + { + frame_array[i].val = MAX_ORDER; + frame_array[i].next = NULL; + frame_array[i].previous = &frame_array[previous_index]; + frame_array[previous_index].next = &frame_array[i]; + previous_index = i; + } + else + { + frame_array[i].val = FREE_BUDDY; + frame_array[i].next = NULL; + frame_array[i].previous = NULL; + } + } + + return; +} + +void *my_malloc(int req_size) +{ + unsigned long ret = -1; + if (req_size < MAX_POOL_SIZE) + ret = (unsigned long)get_chunk(req_size); + else + ret = get_page_from_free_list(req_size, -1); + + if (ret == -1) + { + printf("my_malloc FAILED\n"); + return NULL; + } + else + { + if (req_size < MAX_POOL_SIZE) + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, (void *)ret); + return (void *)ret; + } + else + { + // printf("[DEBUG] Allocating %d size of mem, get addr = %p\n", req_size, frame_array[ret].addr); + return frame_array[ret].addr; + } + } +} + +int get_page_from_free_list(int req_size, int who) +{ + int req_order = -1; + for (int i = 0; i <= MAX_ORDER; i++) + { + if (req_size <= MIN_PAGE_SIZE * pow(2, i)) + { + req_order = i; + break; + } + } + + int alloc_index = -1; + int alloc_order = req_order; + while (alloc_order <= MAX_ORDER) + { + if (free_list[alloc_order].next == NULL) // split high order + { + alloc_order++; + } + else + break; + } + if (alloc_order > MAX_ORDER) + return -1; + while (alloc_order > req_order) + { + // split high order + int removed_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + add_to_free_list(&free_list[alloc_order - 1], removed_index); + add_to_free_list(&free_list[alloc_order - 1], removed_index + pow(2, alloc_order - 1)); + frame_array[removed_index].val = alloc_order - 1; + frame_array[removed_index + (1 << (alloc_order - 1))].val = alloc_order - 1; + alloc_order--; + } + if (alloc_order != req_order) + return -1; + + // get require page + alloc_index = free_list[alloc_order].next->index; + remove_from_free_list(free_list[alloc_order].next); + for (int i = 0; i < (1 << alloc_order); i++) + { + frame_array[alloc_index + i].val = ALLOCATED; + frame_array[alloc_index + i].next = NULL; + frame_array[alloc_index + i].previous = NULL; + frame_array[alloc_index + i].contiguous_head = alloc_index; + frame_array[alloc_index + i].allocated_order = alloc_order; + } + + // check the page if contains reserved memory + unsigned long start = (unsigned long)frame_array[alloc_index].addr; + unsigned long end = start + MIN_PAGE_SIZE * (1 << req_order); + int RM_index = check_contain_RM(start, end); + if (RM_index != 0) + { + // Need to change the page allocated + int new_alloc_index = get_page_from_free_list(req_size, who); + free_page_frame(alloc_index); + alloc_index = new_alloc_index; + } + +#ifdef DEBUG + debug(); +#endif + frame_array[alloc_index].chunk_order = who; + return alloc_index; +} + +// This does NOT modify frame_array value +void add_to_free_list(page_frame_node *head_node, int index) +{ + page_frame_node *iter = head_node; + while (iter->next != NULL) + iter = iter->next; + iter->next = &frame_array[index]; + frame_array[index].previous = iter; + frame_array[index].next = NULL; + + return; +} + +void remove_from_free_list(page_frame_node *node_to_be_removed) +{ + if (node_to_be_removed->next != NULL) + node_to_be_removed->next->previous = node_to_be_removed->previous; + node_to_be_removed->previous->next = node_to_be_removed->next; + + node_to_be_removed->next = NULL; + node_to_be_removed->previous = NULL; + + return; +} + +void put_back_to_free_list(int num_of_redundant_page, int index) // 從 index 開始有 num_of_redundant_page 個 free page +{ + int order_to_put = 0; + while (num_of_redundant_page >= (1 << order_to_put)) + order_to_put++; + order_to_put--; + add_to_free_list(&free_list[order_to_put], index); + frame_array[index].val = order_to_put; + if (num_of_redundant_page - (1 << order_to_put) != 0) + put_back_to_free_list(num_of_redundant_page - (1 << order_to_put), index + (1 << order_to_put)); + + return; +} + +int free_page_frame(int index) +{ + int contiguous_head = frame_array[index].contiguous_head; + if (contiguous_head != index) + { + printf("Please free the start page of this contiguous memory when allocated, which is index %d\n", contiguous_head); + return -1; + } + + // Check if buddy can merge + int allocated_order = frame_array[index].allocated_order; + int buddy_index = index ^ (1 << allocated_order); + if (frame_array[buddy_index].val == allocated_order) + { + // can merge + int merged_order = merge_buddy(&index, buddy_index, allocated_order); + if (buddy_index < index) + add_to_free_list(&free_list[merged_order], buddy_index); + else + add_to_free_list(&free_list[merged_order], index); + } + else + { + // can NOT merge + add_to_free_list(&free_list[allocated_order], index); + frame_array[index].val = allocated_order; + frame_array[index].contiguous_head = -1; + frame_array[index].allocated_order = -1; + for (int i = 1; i < (1 << allocated_order); i++) + { + frame_array[index + i].val = FREE_BUDDY; + frame_array[index + i].contiguous_head = -1; + frame_array[index + i].allocated_order = -1; + } + } + +#ifdef DEBUG + debug(); +#endif + + return 0; +} + +// return merged order, YES modify frame_array +int merge_buddy(int *index, int buddy, int order) +{ + // printf("[DEBUG] Merging buddy index = %d and buddy_index = %d, allocated_order = %d\n", *index, buddy, order); + if (order == MAX_ORDER) + return order; + + if (buddy < *index) + { + *index = buddy; + buddy = *index; // Find itself + } + + page_frame_node *iter = free_list[order].next; + while (iter->index != buddy) + iter = iter->next; + + remove_from_free_list(iter); + + frame_array[*index].val = order + 1; + for (int i = 1; i < (1 << (order + 1)); i++) + { + frame_array[i + *index].val = FREE_BUDDY; + } + + if (order + 1 == MAX_ORDER) + return order + 1; + + int new_buddy = *index ^ (1 << (order + 1)); + if (frame_array[*index].val == frame_array[new_buddy].val) + { + frame_array[buddy].val = FREE_BUDDY; + return merge_buddy(index, new_buddy, order + 1); + } + else + return order + 1; +} + +void debug() +{ + printf("** DEBUGGING free_list\n"); + for (int i = 0; i <= MAX_ORDER; i++) + { + page_frame_node *iter; + iter = &free_list[i]; + printf("free_list[%d] -> ", i); + while (iter->next != NULL) + { + iter = iter->next; + printf("index %d -> ", iter->index); + } + printf("NULL\n"); + } + printf("**\n"); + printf("** DEBUGGING frame_array\n"); + for (int i = 0; i < 20; i++) + { + printf("frame_array[%d].addr = %p\n", i, frame_array[i].addr); + printf("frame_array[%d].val = %d\n", i, frame_array[i].val); + printf("frame_array[%d].contiguous_head = %d\n", i, frame_array[i].contiguous_head); + printf("frame_array[%d].allocated_order = %d\n", i, frame_array[i].allocated_order); + if (frame_array[i].next != NULL) + printf("frame_array[%d].next->index = %d\n", i, frame_array[i].next->index); + if (frame_array[i].previous != NULL) + printf("frame_array[%d].previous->index = %d\n", i, frame_array[i].previous->index); + } + printf("**\n"); + + return; +} diff --git a/Lab8/src/kernel/read_cpio.c b/Lab8/src/kernel/read_cpio.c new file mode 100644 index 000000000..ef16f5106 --- /dev/null +++ b/Lab8/src/kernel/read_cpio.c @@ -0,0 +1,334 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "vfs.h" +#include "tmpfs.h" +#include "read_cpio.h" + +extern char *cpioDestGlobal; +cpio_node_t *cpio_list; + +typedef struct cpio_newc_header +{ + 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]; +} __attribute__((packed)) cpio_t; + +void read_cpio(char *cpioDest) +{ + uart_send_string("Type Offset Size Access rights\tFilename\n"); + + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + // print out meta information + uart_hex(hex2int(header->c_mode, 8)); // mode (access rights + type) + uart_send(' '); + uart_hex((unsigned int)((unsigned long)cpioDest) + sizeof(cpio_t) + ns); + uart_send(' '); + uart_hex(fs); // file size in hex + uart_send(' '); + uart_hex(hex2int(header->c_uid, 8)); // user id in hex + uart_send('.'); + uart_hex(hex2int(header->c_gid, 8)); // group id in hex + uart_send('\t'); + uart_send_string(cpioDest + sizeof(cpio_t)); // filename + uart_send_string("\n"); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } +} + +void read_content(char *cpioDest, char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("cat: %s: No such file\n", filename); + return; + } + // Found target file + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + // print content + uart_send_string_of_size((char *)cpioDest, fs); +} + +char *find_content_addr(char *cpioDest, const char *filename) +{ + int flag = 0; + while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) + { + cpio_t *header = (cpio_t *)cpioDest; + int ns = hex2int(header->c_namesize, 8); + // Check filename + if (!memcmp(cpioDest + sizeof(cpio_t), (char *)filename, ns - 1)) + { + flag = 1; + break; + } + int fs = hex2int(header->c_filesize, 8); + // jump to the next file + if (fs % 4 != 0) + fs += 4 - fs % 4; + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4) + fs); + else + cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4) + fs); + } + // No hit + if (flag == 0) + { + printf("[ERROR] find_content_addr: %s: No such file\n", filename); + return NULL; + } + + return cpioDest; +} + +int load_userprogram(const char *filename, char *userDest) +{ + char *cpioUserPgmDest = cpioDestGlobal; + cpioUserPgmDest = find_content_addr(cpioUserPgmDest, filename); + if (cpioUserPgmDest == NULL) + { + printf("[ERROR] FAIL to find %s\n", filename); + return -1; + } + + // Found target file + cpio_t *header = (cpio_t *)cpioUserPgmDest; + int ns = hex2int(header->c_namesize, 8); + int fs = hex2int(header->c_filesize, 8); + if ((sizeof(cpio_t) + ns) % 4 != 0) + cpioUserPgmDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + else + cpioUserPgmDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + + printf("load %p to %p\n", cpioUserPgmDest, userDest); + printf("size: %d bytes\n", fs); + + // load content + while (fs--) + { + *userDest++ = *cpioUserPgmDest++; + } + + if (fs == -1) + return 0; + + return 1; +} + +/* For Lab7 */ +int get_size(cpio_t *root_addr, char *attr) +{ + char *temp_addr = (char *)root_addr; + + if (!strcmp(attr, "name")) + temp_addr += 94; + else if (!strcmp(attr, "file")) + temp_addr += 54; + + char size_string[9]; + for (int i = 0; i < 8; i++) + { + size_string[i] = temp_addr[i]; + } + + size_string[8] = '\0'; + + // hexadecimal to decimal + return hex2int(size_string, 8); +} + +int initramfs_mount(filesystem_t *fs, mount_t *mount) +{ + mount->root->v_ops = my_malloc(sizeof(vnode_operations_t)); + mount->root->v_ops->lookup = initramfs_lookup; + mount->root->v_ops->create = initramfs_create; + mount->root->v_ops->mkdir = initramfs_mkdir; + + mount->root->f_ops = my_malloc(sizeof(file_operations_t)); + mount->root->f_ops->write = initramfs_write; + mount->root->f_ops->read = initramfs_read; + mount->root->f_ops->open = initramfs_open; + mount->root->f_ops->close = initramfs_close; + + init_cpio(); + + cpio_node_t *tmp = cpio_list; + int i = 0; + while (tmp != NULL) + { + mount->root->internal->entry[i] = my_malloc(sizeof(vnode_t)); + mount->root->internal->entry[i]->mount = NULL; + mount->root->internal->entry[i]->v_ops = mount->root->v_ops; + mount->root->internal->entry[i]->f_ops = mount->root->f_ops; + + mount->root->internal->entry[i]->internal = my_malloc(sizeof(node_info_t)); + mount->root->internal->entry[i]->internal->name = my_malloc(COMPONENT_NAME_MAX); + strcpy(mount->root->internal->entry[i]->internal->name, tmp->name); + mount->root->internal->entry[i]->internal->type = tmp->type; + mount->root->internal->entry[i]->internal->size = tmp->size; + mount->root->internal->entry[i]->internal->data = tmp->data; + + mount->root->internal->size++; + + tmp = tmp->next; + i++; + } + + return 0; +} + +void init_cpio() +{ + cpio_t *current_file = (cpio_t *)cpioDestGlobal; + + int namesize = get_size(current_file, "name") - 1; + int filesize = get_size(current_file, "file"); + + char *temp_addr = (char *)(current_file + 1); + + char temp_name[30]; + + for (int i = 0; i < namesize; i++) + { + temp_name[i] = temp_addr[i]; + } + + temp_name[namesize] = '\0'; + + if (!strcmp(temp_name, "TRAILER!!!")) + return; + + cpio_list = my_malloc(sizeof(cpio_node_t)); + cpio_node_t *tmp = cpio_list; + + while (strcmp(temp_name, "TRAILER!!!")) + { + tmp->name = my_malloc(namesize + 10); + strcpy(tmp->name, temp_name); + tmp->type = FILE; + tmp->size = filesize; + + int NUL_nums = 1; + char *next_file = (char *)(current_file + 1); + + while ((2 + namesize + NUL_nums) % 4 != 0) + NUL_nums++; + + next_file += (namesize + NUL_nums); + tmp->data = next_file; + + NUL_nums = 0; + while ((filesize + NUL_nums) % 4 != 0) + NUL_nums++; + + next_file += (filesize + NUL_nums); + current_file = (cpio_t *)next_file; + + namesize = get_size(current_file, "name") - 1; + filesize = get_size(current_file, "file"); + + temp_addr = (char *)(current_file + 1); + + for (int i = 0; i < namesize; i++) + temp_name[i] = temp_addr[i]; + + temp_name[namesize] = '\0'; + + if (strcmp(temp_name, "TRAILER!!!")) + { + tmp->next = my_malloc(sizeof(cpio_node_t)); + tmp = tmp->next; + } + else + tmp->next = NULL; + } +} + +/* vnode operations : defined in tmpfs.h not in cpio.h */ +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + return tmpfs_lookup(dir_node, target, component_name); +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + printf("/initramfs is read-only!!\n"); + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + printf("/initramfs is read-only!!\n"); + return -1; +} + +/* file operations */ +int initramfs_write(struct file *file, void *buf, size_t len) +{ + printf("/initramfs is read-only!!\n"); + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) +{ + return tmpfs_read(file, buf, len); +} + +int initramfs_open(struct vnode *file_node, struct file **target) +{ + return tmpfs_open(file_node, target); +} + +int initramfs_close(struct file *file) +{ + return tmpfs_close(file); +} \ No newline at end of file diff --git a/Lab8/src/kernel/reboot.c b/Lab8/src/kernel/reboot.c new file mode 100644 index 000000000..da809c59a --- /dev/null +++ b/Lab8/src/kernel/reboot.c @@ -0,0 +1,19 @@ +#include "peripherals/reboot.h" + +void set(long addr, unsigned int value) +{ + volatile unsigned int *point = (unsigned int *)addr; + *point = value; +} + +void reset(int tick) +{ // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() +{ + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab8/src/kernel/reserved_mem.c b/Lab8/src/kernel/reserved_mem.c new file mode 100644 index 000000000..d4a5649c8 --- /dev/null +++ b/Lab8/src/kernel/reserved_mem.c @@ -0,0 +1,48 @@ +#include "reserve_mem.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "stdlib.h" + +reserved_memory_block RMarray[100]; +int RMindex = 0; + +void memory_reserve(unsigned long start, unsigned long end, char *name) +{ + RMindex++; + RMarray[RMindex].start = start; + RMarray[RMindex].end = end; + strcpy(RMarray[RMindex].name, name); +} + +// return value : if including RM, return which no. of RM. Otherwise, return 0. +int check_contain_RM(unsigned long start, unsigned long end) +{ + for (int i = 1; i <= RMindex; i++) + { + if (RMarray[i].start <= start && start <= RMarray[i].end) + return i; + else if (RMarray[i].start <= end && end <= RMarray[i].end) + return i; + else if (start <= RMarray[i].start && RMarray[i].end <= end) + return i; + else + continue; + } + return 0; +} + +void memory_init() +{ + init_page_frame(); + init_pool(); + + memory_reserve(0x0000, 0x5000, "PGD, PUD"); + memory_reserve(0x60000, 0x100000, "Kernel Img"); + memory_reserve(0x1000000, 0x100ffff, "Printf Buffer"); + memory_reserve(0x8000000, 0x8020000, "Initramfs"); + memory_reserve(0x15000000, 0x17000000, "User Program"); + memory_reserve(0x200000, 0x250000, "svc"); + + return; +} \ No newline at end of file diff --git a/Lab8/src/kernel/sdhost.c b/Lab8/src/kernel/sdhost.c new file mode 100644 index 000000000..4ae1e7b89 --- /dev/null +++ b/Lab8/src/kernel/sdhost.c @@ -0,0 +1,244 @@ +// mmio +#define KVA 0xffff000000000000 +// #define MMIO_BASE (KVA + 0x3f000000) +#define MMIO_BASE (0x3f000000) + +// SD card command +#define GO_IDLE_STATE 0 +#define SEND_OP_CMD 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 + #define VOLTAGE_CHECK_PATTERN 0x1aa +#define STOP_TRANSMISSION 12 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define WRITE_SINGLE_BLOCK 24 +#define SD_APP_OP_COND 41 + #define SDCARD_3_3V (1 << 21) + #define SDCARD_ISHCS (1 << 30) + #define SDCARD_READY (1 << 31) +#define APP_CMD 55 + +// gpio +#define GPIO_BASE (MMIO_BASE + 0x200000) +#define GPIO_GPFSEL4 (GPIO_BASE + 0x10) +#define GPIO_GPFSEL5 (GPIO_BASE + 0x14) +#define GPIO_GPPUD (GPIO_BASE + 0x94) +#define GPIO_GPPUDCLK1 (GPIO_BASE + 0x9c) + +// sdhost +#define SDHOST_BASE (MMIO_BASE + 0x202000) +#define SDHOST_CMD (SDHOST_BASE + 0) + #define SDHOST_READ 0x40 + #define SDHOST_WRITE 0x80 + #define SDHOST_LONG_RESPONSE 0x200 + #define SDHOST_NO_REPONSE 0x400 + #define SDHOST_BUSY 0x800 + #define SDHOST_NEW_CMD 0x8000 +#define SDHOST_ARG (SDHOST_BASE + 0x4) +#define SDHOST_TOUT (SDHOST_BASE + 0x8) + #define SDHOST_TOUT_DEFAULT 0xf00000 +#define SDHOST_CDIV (SDHOST_BASE + 0xc) + #define SDHOST_CDIV_MAXDIV 0x7ff + #define SDHOST_CDIV_DEFAULT 0x148 +#define SDHOST_RESP0 (SDHOST_BASE + 0x10) +#define SDHOST_RESP1 (SDHOST_BASE + 0x14) +#define SDHOST_RESP2 (SDHOST_BASE + 0x18) +#define SDHOST_RESP3 (SDHOST_BASE + 0x1c) +#define SDHOST_HSTS (SDHOST_BASE + 0x20) + #define SDHOST_HSTS_MASK (0x7f8) + #define SDHOST_HSTS_ERR_MASK (0xf8) + #define SDHOST_HSTS_DATA (1 << 0) +#define SDHOST_PWR (SDHOST_BASE + 0x30) +#define SDHOST_DBG (SDHOST_BASE + 0x34) + #define SDHOST_DBG_FSM_DATA 1 + #define SDHOST_DBG_FSM_MASK 0xf + #define SDHOST_DBG_MASK (0x1f << 14 | 0x1f << 9) + #define SDHOST_DBG_FIFO (0x4 << 14 | 0x4 << 9) +#define SDHOST_CFG (SDHOST_BASE + 0x38) + #define SDHOST_CFG_DATA_EN (1 << 4) + #define SDHOST_CFG_SLOW (1 << 3) + #define SDHOST_CFG_INTBUS (1 << 1) +#define SDHOST_SIZE (SDHOST_BASE + 0x3c) +#define SDHOST_DATA (SDHOST_BASE + 0x40) +#define SDHOST_CNT (SDHOST_BASE + 0x50) + +// helper +#define set(io_addr, val) \ + asm volatile("str %w1, [%0]" ::"r"(io_addr), "r"(val) : "memory"); + +#define get(io_addr, val) \ + asm volatile("ldr %w0, [%1]" : "=r"(val) : "r"(io_addr) : "memory"); + +static inline void delay(unsigned long tick) { + while (tick--) { + asm volatile("nop"); + } +} + +static int is_hcs; // high capcacity(SDHC) + +static void pin_setup() { + set(GPIO_GPFSEL4, 0x24000000); + set(GPIO_GPFSEL5, 0x924); + set(GPIO_GPPUD, 0); + delay(15000); + set(GPIO_GPPUDCLK1, 0xffffffff); + delay(15000); + set(GPIO_GPPUDCLK1, 0); +} + +static void sdhost_setup() { + unsigned int tmp; + set(SDHOST_PWR, 0); + set(SDHOST_CMD, 0); + set(SDHOST_ARG, 0); + set(SDHOST_TOUT, SDHOST_TOUT_DEFAULT); + set(SDHOST_CDIV, 0); + set(SDHOST_HSTS, SDHOST_HSTS_MASK); + set(SDHOST_CFG, 0); + set(SDHOST_CNT, 0); + set(SDHOST_SIZE, 0); + get(SDHOST_DBG, tmp); + tmp &= ~SDHOST_DBG_MASK; + tmp |= SDHOST_DBG_FIFO; + set(SDHOST_DBG, tmp); + delay(250000); + set(SDHOST_PWR, 1); + delay(250000); + set(SDHOST_CFG, SDHOST_CFG_SLOW | SDHOST_CFG_INTBUS | SDHOST_CFG_DATA_EN); + set(SDHOST_CDIV, SDHOST_CDIV_DEFAULT); +} + +static int wait_sd() { + int cnt = 1000000; + unsigned int cmd; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_CMD, cmd); + --cnt; + } while (cmd & SDHOST_NEW_CMD); + return 0; +} + +static int sd_cmd(unsigned cmd, unsigned int arg) { + set(SDHOST_ARG, arg); + set(SDHOST_CMD, cmd | SDHOST_NEW_CMD); + return wait_sd(); +} + +static int sdcard_setup() { + unsigned int tmp; + sd_cmd(GO_IDLE_STATE | SDHOST_NO_REPONSE, 0); + sd_cmd(SEND_IF_COND, VOLTAGE_CHECK_PATTERN); + get(SDHOST_RESP0, tmp); + if (tmp != VOLTAGE_CHECK_PATTERN) { + return -1; + } + while (1) { + if (sd_cmd(APP_CMD, 0) == -1) { + // MMC card or invalid card status + // currently not support + continue; + } + sd_cmd(SD_APP_OP_COND, SDCARD_3_3V | SDCARD_ISHCS); + get(SDHOST_RESP0, tmp); + if (tmp & SDCARD_READY) { + break; + } + delay(1000000); + } + + is_hcs = tmp & SDCARD_ISHCS; + sd_cmd(ALL_SEND_CID | SDHOST_LONG_RESPONSE, 0); + sd_cmd(SEND_RELATIVE_ADDR, 0); + get(SDHOST_RESP0, tmp); + sd_cmd(SELECT_CARD, tmp); + sd_cmd(SET_BLOCKLEN, 512); + return 0; +} + +static int wait_fifo() { + int cnt = 1000000; + unsigned int hsts; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_HSTS, hsts); + --cnt; + } while ((hsts & SDHOST_HSTS_DATA) == 0); + return 0; +} + +static void set_block(int size, int cnt) { + set(SDHOST_SIZE, size); + set(SDHOST_CNT, cnt); +} + +static void wait_finish() { + unsigned int dbg; + do { + get(SDHOST_DBG, dbg); + } while ((dbg & SDHOST_DBG_FSM_MASK) != SDHOST_HSTS_DATA); +} + +void readblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do{ + set_block(512, 1); + sd_cmd(READ_SINGLE_BLOCK | SDHOST_READ, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + get(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while(!succ); + wait_finish(); +} + +void writeblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do{ + set_block(512, 1); + sd_cmd(WRITE_SINGLE_BLOCK | SDHOST_WRITE, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + set(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while(!succ); + wait_finish(); +} + +void sd_init() { + pin_setup(); + sdhost_setup(); + sdcard_setup(); +} diff --git a/Lab8/src/kernel/shell.c b/Lab8/src/kernel/shell.c new file mode 100644 index 000000000..4720c5537 --- /dev/null +++ b/Lab8/src/kernel/shell.c @@ -0,0 +1,244 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" +#include "timer.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "test.h" +#include "thread.h" +#include "vfs.h" + +extern void *_dtb_ptr; +extern char *cpioDestGlobal; +extern char read_buffer[100]; +// extern page_frame_node frame_array[TOTAL_NUM_PAGE]; +// extern chunk chunk_array[3000]; + +#define COMMAND_BUFFER 50 +#define FILENAME_BUFFER 20 +#define ARGV_BUFFER 30 + +void shell_start(); + +void shell_main(char *command) +{ + if (!strcmp(command, "help")) + { + uart_send_string("help\t: print this help menu\n"); + uart_send_string("hello\t: print Hello World!\n"); + uart_send_string("reboot\t: reboot the device\n"); + uart_send_string("ls\t: list information about files\n"); + uart_send_string("cd\t: \n"); + uart_send_string("cat\t: copy each FILE to standard output\n"); + uart_send_string("dts\t: list devicetree\n"); + uart_send_string("asynr\t: [test] asynchronous read\n"); + uart_send_string("asynw\t: [test] asynchronous write\n"); + uart_send_string("setTimeout\t: Usage: setTimeout \n"); + uart_send_string("alloc\t: [test] malloc and free\n"); + uart_send_string("thread\t: [test]\n"); + uart_send_string("syscall\t: [test]\n"); + uart_send_string("vfs1\t: [test] load lab7 user program\n"); + } + else if (!strcmp(command, "hello")) + { + uart_send_string("Hello World!\n"); + } + else if (!strcmp(command, "reboot")) + { + uart_send_string("Rebooting in 3 seconds\n"); + reset(3 << 16); + while (1) + ; + } + else if (!memcmp(command, "ls", 2)) + { + char pathname[256]; + if (strlen(command) == 2 || strlen(command) == 3) + vfs_ls(pathname, 0); + else + { + handle_path(command + 3, pathname); + vfs_ls(pathname, 1); + } + } + else if (!memcmp(command, "cat", 3)) + { + if (command[3] != ' ' || command[4] == '\0') + { + printf("Usage: cat \n"); + return; + } + + char filename[FILENAME_BUFFER]; + memset(filename, '\0', FILENAME_BUFFER); + int i = 4; + while (command[i] != '\0') + { + filename[i - 4] = command[i]; + i++; + } + + read_content((char *)cpioDestGlobal, filename); + } + else if (!strcmp(command, "dts")) + { + fdt_traverse(dtb_parser, _dtb_ptr); + } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x1, 0x0;" // not sure why can't use x0, may have something to to with %0 + "msr spsr_el1, x1;" + "mov x2, %0;" + "add x2, x2, 12;" + "msr elr_el1, x2;" + "mov x2, 0x15000000;" + "msr sp_el0, x2;" + "mrs x2, cntfrq_el0;" + "add x2, x2, x2;" + "msr cntp_tval_el0, x2;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } + else if (!strcmp(command, "asynr")) + { + asyn_read(); + } + else if (!strcmp(command, "asynw")) + { + asyn_write(read_buffer); + uart_send('\n'); + } + else if (!memcmp(command, "setTimeout", 10)) + { + if (command[10] != ' ' || command[11] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char message[MESSAGE_BUFFER]; + memset(message, '\0', MESSAGE_BUFFER); + int i = 11; + while (command[i] != ' ' && command[i] != '\0') + { + message[i - 11] = command[i]; + i++; + } + + if (command[i] != ' ' || command[i] == '\0' || command[i + 1] == '\0') + { + printf("Usage: setTimeout \n"); + return; + } + + char seconds[SECONDS_BUFFER]; + memset(seconds, '\0', SECONDS_BUFFER); + while (command[i] != '\0') + { + seconds[i - strlen(message) - 1 - 11] = command[i]; + i++; + } + int sec; + sec = atoi(seconds); + + add_timer(sec, message); + } + else if (!memcmp(command, "alloc", 5)) + { + test_mem_alloc(); + } + else if (!memcmp(command, "debug", 5)) + { + debug(); + debug_pool(); + } + else if (!memcmp(command, "thread", 6)) + { + test_thread(); + } + else if (!strcmp(command, "syscall")) + { + thread_create(load_usrpgm_in_umode); + idle_task(); + } + else if (!strcmp(command, "vfs1")) + { + thread_create(load_vfs1_usrpgm_in_umode); + idle_task(); + } + else if (!strcmp(command, "vfs2")) + { + thread_create(load_vfs2_usrpgm_in_umode); + idle_task(); + } + else if (!memcmp(command, "cd", 2)) + { + char pathname[256]; + handle_path(command + 3, pathname); + vfs_cd(pathname); + } + else if (!strcmp(command, "cwd")) + { + printf("%s\n", cwdpath); + } + + return; +} + +void shell_start() +{ + uart_send_string("Starting shell...\n"); + char c; + int i = 0; + + char command[COMMAND_BUFFER]; + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("$ "); + + while (1) + { + c = uart_recv(); + + if (c >= 0 && c < 128) // Legal + { + if (c == '\n') // Enter + { + command[i] = '\0'; + uart_send(c); + shell_main(command); + memset(command, '\0', COMMAND_BUFFER); + i = 0; + uart_send_string("$ "); + } + else if (c == 8) // Backspace + { + uart_send(c); + uart_send(' '); + uart_send(c); + if (i > 0) + i--; + } + else if (c == 127) // Also backspace but delete + { + } + else + { + if (i < COMMAND_BUFFER) + { + if (c == 0) // solve the problem that first command on board wont work + continue; + command[i++] = c; + } + uart_send(c); + } + } + } +} diff --git a/Lab8/src/kernel/syscall.c b/Lab8/src/kernel/syscall.c new file mode 100644 index 000000000..4f2748778 --- /dev/null +++ b/Lab8/src/kernel/syscall.c @@ -0,0 +1,245 @@ +#include "stdlib.h" +#include "thread.h" +#include "mini_uart.h" +#include "page_alloc.h" +#include "read_cpio.h" +#include "utils.h" +#include "peripherals/mbox_call.h" +#include "peripherals/gpio.h" +#include "my_signal.h" +#include "vfs.h" +#include "reserve_mem.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); +extern void switch_to(task_struct *, task_struct *); +extern task_struct kernel_thread; +extern struct list_head task_rq_head; +extern struct list_head task_zombieq_head; + +int getpid() +{ + int ret = get_current()->thread_info->id; + return ret; +} + +size_t uart_read(char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + buf[i] = uart_recv(); + if (buf[i] == '\n' || buf[i] == '\r') + return i; + } + return size; +} + +size_t uart_write(const char buf[], size_t size) +{ + for (int i = 0; i < size; i++) + { + if (buf[i] == '\0') + return i; + uart_send(buf[i]); + } + return size; +} + +int exec(const char *name, char *const argv[]) +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + file_t *fh = NULL; + int ret = vfs_open((char *)name, 0, &fh); + if (ret == 0) + { + fh->f_ops->read(fh, (void *)target_addr, USRPGM_SIZE); + fh->f_ops->close(fh); + } + else + return -1; + + asm volatile( + "mov x0, 0x0\n\t" // EL0t + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); + + return 0; +} + +int fork() +{ + schedule(); + return get_current()->thread_info->child_id; +} + +void exit(int status) +{ + task_struct *current = (task_struct *)get_current(); + current->status = ZOMBIE; + INIT_LIST_HEAD(¤t->list); + list_add_tail(¤t->list, &task_zombieq_head); + // schedule(); + switch_to(current, &kernel_thread); +} + +int mbox_call_u(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 (1) + { + if (!(get32(MBOX_STATUS) & MBOX_FULL)) + break; + } + + /* write the address of our message to the mailbox with channel identifier */ + put32(MBOX_WRITE, r); + + /* now wait for the response */ + while (1) + { + /* is there a response? */ + while (1) + { + if (!(get32(MBOX_STATUS) & MBOX_EMPTY)) + break; + } + + /* is it a response to our message? */ + if (r == get32(MBOX_READ)) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + + return 0; +} + +void kill(int pid) +{ + task_struct *tmp = get_current(); + if (tmp->thread_info->id == pid) + exit(0); + + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + tmp = container_of(iter, task_struct, list); + if (tmp->thread_info->id == pid) + { + tmp->status = ZOMBIE; + return; + } + } +} + +void signal(int SIGNAL, void (*handler)()) +{ + printf("[info] Called signal()\n"); + task_struct *cur = get_current(); + + custom_signal *new = (custom_signal *)my_malloc(sizeof(custom_signal)); + new->sig_num = SIGNAL; + new->handler = handler; + INIT_LIST_HEAD(&new->list); + + if (!cur->custom_signal) + cur->custom_signal = new; + else + list_add_tail(&cur->custom_signal->list, &new->list); +} + +int open(char *pathname, int flags) +{ + task_struct *cur = get_current(); + int fd = thread_get_idle_fd(cur); + if (fd < 0) + { + printf("[ERROR/syscall.c/open()] cannot open more file for pid=%d\r\n", cur->thread_info->id); + return -1; + } + + file_t *target; + int ret = vfs_open(pathname, flags, &target); + + if (ret != 0) + return ret; + + cur->fd_table[fd] = target; + + return fd; +} + +int close(int fd) +{ + task_struct *cur = get_current(); + int ret = vfs_close(cur->fd_table[fd]); + return ret; +} + +long write(int fd, void *buf, unsigned long count) +{ + task_struct *cur = get_current(); + int ret = vfs_write(cur->fd_table[fd], buf, count); + return ret; +} + +long read(int fd, void *buf, unsigned long count) +{ + task_struct *cur = get_current(); + int ret = vfs_read(cur->fd_table[fd], buf, count); + return ret; +} + +int mkdir(char *pathname, unsigned mode) +{ + int ret = vfs_mkdir(pathname); + return ret; +} + +int mount(char *src, char *target, char *filesystem, unsigned long flags, void *data) +{ + int ret = vfs_mount(target, filesystem); + return ret; +} + +int chdir(char *path) +{ + char pathname[256]; + handle_path(path, pathname); + int ret = vfs_cd(pathname); + return ret; +} + +long lseek(int fd, long offset, int whence) +{ + task_struct *cur = get_current(); + int ret = vfs_lseek(cur->fd_table[fd], offset, whence); + return ret; +} + +int ioctl(int fd, unsigned long request, unsigned long arg) +{ + task_struct *cur = get_current(); + int ret = vfs_ioctl(cur->fd_table[fd], request, arg); + return ret; +} \ No newline at end of file diff --git a/Lab8/src/kernel/test.c b/Lab8/src/kernel/test.c new file mode 100644 index 000000000..5cb20919f --- /dev/null +++ b/Lab8/src/kernel/test.c @@ -0,0 +1,141 @@ +#include "stdlib.h" +#include "dynamic_alloc.h" +#include "page_alloc.h" +#include "thread.h" +#include "syscall.h" +#include "read_cpio.h" +#include "utils.h" +#include "timer.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void core_timer_enable(); + +void test_mem_alloc() +{ + void *a; + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + a = my_malloc(200); + printf("a = %p\n", a); + free(a); + + return; +} + +void foo() +{ + for (int i = 0; i < 10; ++i) + { + task_struct *cur = get_current(); + printf("Thread id: %d %d\n", cur->thread_info->id, i); + delay(1000000); + schedule(); + } +} + +void test_thread() +{ + int N = 5; + for (int i = 0; i < N; ++i) + { // N should > 2 + thread_create(foo); + } + + // thread_create(load_usrpgm_in_umode); + idle_task(); + + debug_task_rq(); + return; +} + +void load_usrpgm_in_umode() +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + load_userprogram("syscall.img", (char *)target_addr); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} + +void load_vfs1_usrpgm_in_umode() +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + load_userprogram("vfs1.img", (char *)target_addr); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} + +void load_vfs2_usrpgm_in_umode() +{ + task_struct *current = get_current(); + unsigned long target_addr = current->usrpgm_load_addr; + unsigned long target_sp = current->ustack_start + MIN_PAGE_SIZE; + + load_userprogram("vfs2.img", (char *)target_addr); + + set_switch_timer(); + core_timer_enable(); + enable_interrupt(); + + asm volatile( + "mov x0, 0x0\n\t" // EL0t, and open diaf(interrupt) + "msr spsr_el1, x0\n\t" + "mov x0, %0\n\t" + "msr elr_el1, x0\n\t" + "mov x0, %1\n\t" + "msr sp_el0, x0\n\t" + "eret" + : + : "r"(target_addr), "r"(target_sp) + : "x0"); +} \ No newline at end of file diff --git a/Lab8/src/kernel/thread.S b/Lab8/src/kernel/thread.S new file mode 100644 index 000000000..ffc3ece7b --- /dev/null +++ b/Lab8/src/kernel/thread.S @@ -0,0 +1,31 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +.global kernel_thread_init +kernel_thread_init: + msr tpidr_el1, x0 + ret \ No newline at end of file diff --git a/Lab8/src/kernel/thread.c b/Lab8/src/kernel/thread.c new file mode 100644 index 000000000..5cc36aa1d --- /dev/null +++ b/Lab8/src/kernel/thread.c @@ -0,0 +1,282 @@ +#include "stdlib.h" +#include "thread.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "list.h" +#include "syscall.h" +#include "vfs.h" + +extern task_struct *get_current(); +extern void set_switch_timer(); +extern void enable_interrupt(); +extern void disable_interrupt(); +extern void switch_to(task_struct *, task_struct *); +extern void kernel_thread_init(); + +long thread_cnt = 0; + +task_struct kernel_thread = {0}; +struct list_head task_rq_head; // run queue +struct list_head task_zombieq_head; // zombie queue + +#define FD_STDIN 0 +#define FD_STDOUT 1 +#define FD_STDERR 2 + +void schedule() +{ + task_struct *cur = get_current(); + task_struct *next = del_rq(); + + if (next == NULL) + next = &kernel_thread; + if (cur != &kernel_thread) + add_rq(cur); + + set_switch_timer(); + enable_interrupt(); + + if (next->status == FORKING) + { + add_rq(next); + switch_to(cur, &kernel_thread); + } + else if (next->status == ZOMBIE) + { + INIT_LIST_HEAD(&next->list); + list_add_tail(&next->list, &task_zombieq_head); + switch_to(cur, &kernel_thread); + } + else + { + switch_to(cur, next); + } +} + +void add_rq(task_struct *task) +{ + INIT_LIST_HEAD(&task->list); + list_add_tail(&task->list, &task_rq_head); +} + +task_struct *del_rq() +{ + struct list_head *ret; + ret = task_rq_head.next; + if (ret != &task_rq_head) + { + list_del_init(ret); + return container_of(ret, task_struct, list); + } + else + return NULL; +} + +void thread_init() +{ + INIT_LIST_HEAD(&task_rq_head); + INIT_LIST_HEAD(&task_zombieq_head); + kernel_thread_init(&kernel_thread); + return; +} + +thread_info *thread_create(func_ptr fp) +{ + task_struct *new_task = (task_struct *)my_malloc(sizeof(task_struct)); + thread_info *new_thread = (thread_info *)my_malloc(sizeof(thread_info)); + + new_task->thread_info = new_thread; + new_thread->task = new_task; + + new_task->kstack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); + new_task->ustack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); + new_task->usrpgm_load_addr = USRPGM_BASE + thread_cnt * USRPGM_SIZE; + + new_task->task_context.fp = new_task->kstack_start + MIN_PAGE_SIZE; + new_task->task_context.lr = (unsigned long)task_wrapper; + new_task->task_context.sp = new_task->kstack_start + MIN_PAGE_SIZE; + new_task->thread_info->id = thread_cnt++; + new_task->status = READY; + new_task->job = fp; + new_task->custom_signal = NULL; + + memset(new_task->fd_table, 0, sizeof(new_task->fd_table)); // init file table + // Open stdin, stdout, stderr for the process + file_t *fh = NULL; + vfs_open("/dev/uart", 0, &fh); + new_task->fd_table[FD_STDIN] = fh; + vfs_open("/dev/uart", 0, &fh); + new_task->fd_table[FD_STDOUT] = fh; + vfs_open("/dev/uart", 0, &fh); + new_task->fd_table[FD_STDERR] = fh; + + add_rq(new_task); + + return new_thread; +} + +/* threads' routine for any task */ +void task_wrapper() +{ + task_struct *current = get_current(); + (current->job)(); + exit(0); +} + +void idle_task() +{ + while (!list_empty(&task_rq_head) || !list_empty(&task_zombieq_head)) + { + disable_interrupt(); + kill_zombies(); + do_fork(); + enable_interrupt(); + schedule(); + } +} + +/* kill the zombie threads and recycle their resources */ +void kill_zombies() +{ + struct list_head *iter = &task_zombieq_head; + struct list_head *start = &task_zombieq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + free((void *)tmp->kstack_start); + free((void *)tmp->ustack_start); + free(tmp->thread_info); + if (tmp->custom_signal) + free(tmp->custom_signal); + free(tmp); + } + INIT_LIST_HEAD(&task_zombieq_head); + return; +} + +void do_fork() +{ + struct list_head *iter = &task_rq_head; + struct list_head *start = &task_rq_head; + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + if (tmp->status == FORKING) + create_child(tmp); + } +} + +/* copy all data including stack and program for child process and set the corresponding sp and lr for it*/ +void create_child(task_struct *parent) +{ + thread_info *child_thread = thread_create(0); + task_struct *child = child_thread->task; + + char *parent_d, *child_d; + + parent->status = READY; + + parent->thread_info->child_id = child->thread_info->id; + child->thread_info->child_id = 0; + + // copy context + parent_d = (char *)&(parent->task_context); + child_d = (char *)&(child->task_context); + for (int i = 0; i < sizeof(context); i++) + child_d[i] = parent_d[i]; + + // copy custom_signal + if (parent->custom_signal) + { + child->custom_signal = (custom_signal *)my_malloc(sizeof(custom_signal)); + parent_d = (char *)&(parent->custom_signal); + child_d = (char *)&(child->custom_signal); + for (int i = 0; i < sizeof(custom_signal); i++) + child_d[i] = parent_d[i]; + } + + // copy kernel stack + parent_d = (char *)parent->kstack_start; + child_d = (char *)child->kstack_start; + for (int i = 0; i < MIN_PAGE_SIZE; i++) + child_d[i] = parent_d[i]; + + // copy user stack + parent_d = (char *)parent->ustack_start; + child_d = (char *)child->ustack_start; + for (int i = 0; i < MIN_PAGE_SIZE; i++) + child_d[i] = parent_d[i]; + + // copy user program + parent_d = (char *)parent->usrpgm_load_addr; + child_d = (char *)child->usrpgm_load_addr; + for (int i = 0; i < USRPGM_SIZE; i++) + child_d[i] = parent_d[i]; + + // set offset to child's stack + unsigned long kstack_offset = child->kstack_start - parent->kstack_start; + unsigned long ustack_offset = child->ustack_start - parent->ustack_start; + unsigned long usrpgm_offset = child->usrpgm_load_addr - parent->usrpgm_load_addr; + + // set child kernel space offset + child->task_context.fp += kstack_offset; + child->task_context.sp += kstack_offset; + child->trapframe = parent->trapframe + kstack_offset; + + // set child user space offset + trapframe *ctrapframe = (trapframe *)child->trapframe; // because of data type problem + ctrapframe->x[29] += ustack_offset; + ctrapframe->sp_el0 += ustack_offset; + ctrapframe->elr_el1 += usrpgm_offset; +} + +void debug_task_rq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_rq_head; + start = &task_rq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_rq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} + +void debug_task_zombieq() +{ + struct list_head *iter; + struct list_head *start; + iter = &task_zombieq_head; + start = &task_zombieq_head; + printf("\n[DEBUG] task run queue\n"); + printf("task_zombieq_head -> "); + while (iter->next != start) + { + iter = iter->next; + task_struct *tmp; + tmp = container_of(iter, task_struct, list); + printf("thread_id %d -> ", tmp->thread_info->id); + } + printf("NULL\n\n"); +} + +int thread_get_idle_fd(task_struct *thd) +{ + for (int i = 0; i < VFS_PROCESS_MAX_OPEN_FILE; i++) + { + if (thd->fd_table[i] == NULL) + return i; + } + return -1; +} \ No newline at end of file diff --git a/Lab8/src/kernel/timer.S b/Lab8/src/kernel/timer.S new file mode 100644 index 000000000..a22d66ec4 --- /dev/null +++ b/Lab8/src/kernel/timer.S @@ -0,0 +1,32 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + mrs x2, cntkctl_el1 // following three instructions for Lab5 to access cpu timer register + orr x2, x2, #0x1 + msr cntkctl_el1, x2 + ret + +.global core_timer_disable +core_timer_disable: + mov x0, 0 + msr cntp_ctl_el0, x0 // disable + ret + +.global set_switch_timer +set_switch_timer: + mrs x0, cntfrq_el0 + mov x0, x0, lsr#5 + msr cntp_tval_el0, x0 + ret + +/* +cntpct_el0: The timer’s current count. +cntp_cval_el0: A compared timer count. If cntpct_el0 >= cntp_cval_el0, interrupt the CPU core. +cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +*/ \ No newline at end of file diff --git a/Lab8/src/kernel/timer.c b/Lab8/src/kernel/timer.c new file mode 100644 index 000000000..5e8c42f58 --- /dev/null +++ b/Lab8/src/kernel/timer.c @@ -0,0 +1,176 @@ +#include "stdlib.h" +#include "timer.h" +#include "thread.h" + +extern void enable_interrupt(); +extern void disable_interrupt(); + +typedef struct timer_queue_node +{ + long second_ticks; + char message[MESSAGE_BUFFER]; +} tq; + +tq timer_queue[10]; +int timer_queue_front = 0; +int timer_queue_back = 0; + +void get_current_time() +{ + long cntpct_el0, cntfrq_el0; + long cntp_tval_el0; + long cntp_cval_el0; + + asm volatile( + "mrs %0, cntpct_el0;" + "mrs %1, cntfrq_el0;" + "mrs %2, cntp_tval_el0;" + "mrs %3, cntp_cval_el0;" + : "=r"(cntpct_el0), "=r"(cntfrq_el0), "=r"(cntp_tval_el0), "=r"(cntp_cval_el0)); + + long nowtime = cntpct_el0 / cntfrq_el0; + + printf("%ld seconds after booting\n", nowtime); + + // printf("cntpct_el0 = %d\n", cntpct_el0); + // printf("cntfrq_el0 = %d\n", cntfrq_el0); + // printf("cntp_tval_el0 = %d\n", cntp_tval_el0); + // printf("cntp_cval_el0 = %d\n", cntp_cval_el0); + + return; +} + +void add_timer(int sec, char *mes) +{ + get_current_time(); + + for (int i = 0; i < MESSAGE_BUFFER; i++) + timer_queue[timer_queue_back].message[i] = mes[i]; + + // transfer sec to frq and store to node.second + asm volatile( + "msr DAIFSet, 0xf;" + "mrs x3, cntfrq_el0;" + "mrs x4, cntpct_el0;" + "mov x2, %1;" // after secs seconds later will interrupt + "mul x2, x2, x3;" + "add x2, x2, x4;" + "mov %0, x2;" + : "=r"(timer_queue[timer_queue_back].second_ticks) + : "r"(sec) + : "x0", "x1", "x2", "x3", "x4", "memory"); // Uses register operands x0, x1, x2, x3, and x4 and specifies that the instruction may modify memory using the clobber list "x0", "x1", "x2", "x3", "x4", "memory". + + timer_queue_back++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + + asm volatile( + "msr cntp_cval_el0, %0;" + "bl core_timer_enable;" + "msr DAIFClr, 0xf;" + : + : "r"(timer_queue[timer_queue_front].second_ticks)); +} + +void el0_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + disable_interrupt(); + schedule(); + enable_interrupt(); +} + +void el1_timer_handler(long cntpct_el0, long cntfrq_el0) +{ + if (!is_timer_queue_empty()) + { + // disable core timer interrupt + asm volatile( + "mov x1, 0;" + "msr cntp_ctl_el0, x1;"); + + printf("\n"); + long nowtime = cntpct_el0 / cntfrq_el0; + printf("Time out, now time: %ld seconds after booting\n", nowtime); + printf("Message: %s\n", timer_queue[timer_queue_front].message); + + timer_queue_front++; + // Find min + for (int i = timer_queue_front; i < timer_queue_back; i++) + { + if (timer_queue[i].second_ticks < timer_queue[timer_queue_front].second_ticks) + { + int sec_tmp; + char mes_tmp[MESSAGE_BUFFER]; + + sec_tmp = timer_queue[timer_queue_front].second_ticks; + timer_queue[timer_queue_front].second_ticks = timer_queue[i].second_ticks; + timer_queue[i].second_ticks = sec_tmp; + + for (int j = 0; j < MESSAGE_BUFFER; j++) + { + mes_tmp[j] = timer_queue[timer_queue_front].message[j]; + timer_queue[timer_queue_front].message[j] = timer_queue[i].message[j]; + timer_queue[i].message[j] = mes_tmp[j]; + } + } + } + asm volatile( + "msr cntp_cval_el0, %0\n\t" // set expired time + "bl core_timer_enable\n\t" + : + : "r"(timer_queue[timer_queue_front].second_ticks) + :); + } + else + { + disable_interrupt(); + schedule(); + enable_interrupt(); + } + return; +} + +int is_timer_queue_empty() +{ + return timer_queue_front == timer_queue_back ? 1 : 0; +} + +void timer_delay(long seconds) +{ + long cntpct_el0, cntfrq_el0, nowtime, due; + + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + + due = cntpct_el0 / cntfrq_el0 + seconds; + nowtime = cntpct_el0 / cntfrq_el0; + + while (nowtime <= due) + { + asm volatile( + "mrs %0, cntpct_el0\n\t" + "mrs %1, cntfrq_el0\n\t" + : "=r"(cntpct_el0), "=r"(cntfrq_el0)::); + nowtime = cntpct_el0 / cntfrq_el0; + } +} \ No newline at end of file diff --git a/Lab8/src/kernel/tmpfs.c b/Lab8/src/kernel/tmpfs.c new file mode 100644 index 000000000..948a59fa5 --- /dev/null +++ b/Lab8/src/kernel/tmpfs.c @@ -0,0 +1,138 @@ +#include "stdlib.h" +#include "vfs.h" +#include "tmpfs.h" + +int tmpfs_mount(struct filesystem *fs, struct mount *mount) +{ + mount->root = my_malloc(sizeof(vnode_t)); + mount->root->mount = NULL; + + mount->root->v_ops = my_malloc(sizeof(vnode_operations_t)); + mount->root->v_ops->lookup = tmpfs_lookup; + mount->root->v_ops->create = tmpfs_create; + mount->root->v_ops->mkdir = tmpfs_mkdir; + + mount->root->f_ops = my_malloc(sizeof(file_operations_t)); + mount->root->f_ops->write = tmpfs_write; + mount->root->f_ops->read = tmpfs_read; + mount->root->f_ops->open = tmpfs_open; + mount->root->f_ops->close = tmpfs_close; + + mount->root->internal = my_malloc(sizeof(node_info_t)); + mount->root->internal->name = my_malloc(strlen("/") + 1); + strcpy(mount->root->internal->name, "/"); + + mount->root->internal->type = DIR; + mount->root->internal->size = 0; + mount->root->internal->entry = my_malloc(sizeof(vnode_t *) * MAX_NUM_OF_ENTRY); + + return 0; +} + +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + if (dir_node->internal->type != DIR) + { + printf("'%s' is not a directory.\n", dir_node->internal->name); + return NOTDIR; + } + + for (int i = 0; i < dir_node->internal->size; i++) + { + if (!strcmp(dir_node->internal->entry[i]->internal->name, component_name)) + { + *target = dir_node->internal->entry[i]; + return EXISTED; + } + } + + // printf("'%s' is not exist. in lookup()\n", component_name); + return NOTFOUND; +} + +int tmpfs_create(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + if (dir_node->internal->size == MAX_NUM_OF_ENTRY) + { + printf("'%s' has no more entries.\n", dir_node->internal->name); + return -1; + } + + *target = my_malloc(sizeof(vnode_t)); + (*target)->mount = NULL; + (*target)->v_ops = dir_node->v_ops; + (*target)->f_ops = dir_node->f_ops; + + (*target)->internal = my_malloc(sizeof(node_info_t)); + (*target)->internal->name = my_malloc(COMPONENT_NAME_MAX); + strcpy((*target)->internal->name, component_name); + (*target)->internal->type = FILE; + (*target)->internal->size = 0; + (*target)->internal->entry = NULL; // mkdir will my_malloc() + (*target)->internal->data = NULL; // open will my_malloc() + + dir_node->internal->entry[dir_node->internal->size] = *target; + dir_node->internal->size++; + + return 0; +} + +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, char *component_name) +{ + int ret = tmpfs_create(dir_node, target, component_name); + if (ret == 0) + { + (*target)->internal->type = DIR; + (*target)->internal->entry = my_malloc(sizeof(vnode_t *) * MAX_NUM_OF_ENTRY); + return 0; + } + else + return -1; +} + +int tmpfs_write(struct file *file, void *buf, size_t len) +{ + size_t writeable_size = TMPFS_FILE_SIZE_MAX - file->f_pos; + size_t write_len = len <= writeable_size ? len : writeable_size; + + char *buf_ = (char *)buf; + for (int i = 0; i < write_len; i++) + file->vnode->internal->data[file->f_pos + i] = buf_[i]; + + file->vnode->internal->size += write_len; + file->f_pos += write_len; + + return write_len; +} + +int tmpfs_read(struct file *file, void *buf, size_t len) +{ + size_t readable_size = file->vnode->internal->size - file->f_pos; + size_t read_len = len <= readable_size ? len : readable_size; + + char *buf_ = (char *)buf; + for (int i = 0; i < read_len; i++) + buf_[i] = file->vnode->internal->data[file->f_pos + i]; + + file->f_pos += read_len; + + return read_len; +} + +int tmpfs_open(struct vnode *file_node, struct file **target) +{ + if (file_node->internal->data == NULL) + file_node->internal->data = my_malloc(sizeof(char) * TMPFS_FILE_SIZE_MAX); + *target = my_malloc(sizeof(file_t)); + (*target)->vnode = file_node; + (*target)->f_pos = 0; + (*target)->f_ops = file_node->f_ops; + + return 0; +} + +int tmpfs_close(struct file *file) +{ + free(file); + return 0; +} \ No newline at end of file diff --git a/Lab8/src/kernel/vfs.c b/Lab8/src/kernel/vfs.c new file mode 100644 index 000000000..e9ca19d62 --- /dev/null +++ b/Lab8/src/kernel/vfs.c @@ -0,0 +1,507 @@ +#include "vfs.h" +#include "tmpfs.h" +#include "devfs.h" +#include "fat32.h" +#include "stdlib.h" + +filesystem_t *fs_list[50]; +unsigned int fs_count = 0; +struct mount *rootfs; +char cwdpath[256]; +file_t *kfd[16]; +int fd_count = 0; + +int register_filesystem(struct filesystem *fs) +{ + // register the file system to the kernel. + for (int i = 0; i < fs_count; i++) + { + if (!strcmp(fs->name, fs_list[i]->name)) + return -1; + } + + fs_list[fs_count] = fs; + fs_count++; + + return 0; + // you can also initialize memory pool of the file system here. +} + +int vfs_mount(char *target, char *filesystem) +{ + if (!strcmp(target, "/")) + { + rootfs = my_malloc(sizeof(mount_t)); + rootfs->fs = my_malloc(sizeof(filesystem_t)); + rootfs->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(rootfs->fs->name, filesystem); + rootfs->fs->setup_mount = tmpfs_mount; + register_filesystem(rootfs->fs); + return rootfs->fs->setup_mount(rootfs->fs, rootfs); + } + else if (!strcmp(target, "/initramfs")) + { + vnode_t *dir_node = NULL; + + vfs_mkdir(target); + vfs_lookup(target, &dir_node); + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + dir_node->mount->fs->setup_mount = initramfs_mount; + register_filesystem(dir_node->mount->fs); + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + else if (!strcmp(target, "/dev")) + { + vnode_t *dir_node = NULL; + + vfs_mkdir(target); + vfs_lookup(target, &dir_node); + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + dir_node->mount->fs->setup_mount = devfs_setup_mount; + register_filesystem(dir_node->mount->fs); + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + else if (!strcmp(target, "/boot")) + { + vnode_t *dir_node = NULL; + + vfs_mkdir(target); + vfs_lookup(target, &dir_node); + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + dir_node->mount->fs->setup_mount = fat32fs_mount; + register_filesystem(dir_node->mount->fs); + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + else + { + vnode_t *dir_node = NULL; + + switch (vfs_lookup(target, &dir_node)) + { + case EXISTED: + if (dir_node->internal->type != DIR) + { + printf("%s is not a directory.\n", target); + return -1; + } + break; + case NOTDIR: + return -1; + case NOTFOUND: + printf("%s is not existed.\n", target); + return -1; + } + + dir_node->mount = my_malloc(sizeof(mount_t)); + dir_node->mount->root = dir_node; + dir_node->mount->fs = my_malloc(sizeof(filesystem_t)); + dir_node->mount->fs->name = my_malloc(strlen(filesystem) + 1); + strcpy(dir_node->mount->fs->name, filesystem); + // if mounting to a existed directory, all original content will not be used temporarily. When unmounted, will recover the contents. + // TODO : keep original "vnode->internal", use new one replace it. And change v_ops & f_ops to new filesystem's + if (register_filesystem(dir_node->mount->fs) == -1) + { + char dir_name[COMPONENT_NAME_MAX]; + strcpy(dir_name, dir_node->internal->name); + + dir_node->internal = my_malloc(sizeof(node_info_t)); + dir_node->internal->name = my_malloc(strlen(dir_name) + 1); + strcpy(dir_node->internal->name, dir_name); + + dir_node->internal->type = DIR; + dir_node->internal->size = 0; + dir_node->internal->entry = my_malloc(sizeof(vnode_t *) * MAX_NUM_OF_ENTRY); + + return 0; + } + return dir_node->mount->fs->setup_mount(dir_node->mount->fs, dir_node->mount); + } + + return -1; +} + +int vfs_lookup(char *pathname, struct vnode **target) +{ + char abs_pathname[PATHNAME_MAX]; + handle_path(pathname, abs_pathname); + + if (!strcmp("/", abs_pathname)) + { + *target = rootfs->root; + return 0; + } + + vnode_t *vnode = rootfs->root; + int level = 0; + char next_component[COMPONENT_NAME_MAX]; + int total_level = strnchr(abs_pathname, '/'); + + while (level < total_level) + { + get_next_component(abs_pathname, next_component, level); + + vnode_t *next_node = NULL; + int ret = vnode->v_ops->lookup(vnode, &next_node, next_component); + + if (ret != EXISTED) + return ret; + + vnode = next_node; + level++; + } + + *target = vnode; + + return EXISTED; +} + +int vfs_create(char *pathname) +{ + vnode_t *dir_node = NULL; + char abs_pathname[PATHNAME_MAX]; + + handle_path(pathname, abs_pathname); + + switch (vfs_lookup(abs_pathname, &dir_node)) + { + case EXISTED: + printf("%s is existed.\n", abs_pathname); + return -1; + case NOTDIR: + return -1; + } + + char target_component[COMPONENT_NAME_MAX]; + char dir_pathname[PATHNAME_MAX]; + + basename(abs_pathname, target_component); + dirname(abs_pathname, dir_pathname); + if (vfs_lookup(dir_pathname, &dir_node) == NOTFOUND) + { + printf("%s not found.\n", dir_pathname); + return -1; + } + + vnode_t *target = NULL; + + int ret = dir_node->v_ops->create(dir_node, &target, target_component); + + if (ret != 0) + return ret; + + return 0; +} + +int vfs_mkdir(char *pathname) +{ + vnode_t *dir_node = NULL; + char abs_pathname[PATHNAME_MAX]; + + handle_path(pathname, abs_pathname); + + switch (vfs_lookup(abs_pathname, &dir_node)) + { + case EXISTED: + printf("%s is existed.\n", abs_pathname); + return -1; + case NOTDIR: + return -1; + } + + char target_component[COMPONENT_NAME_MAX]; + char dir_pathname[PATHNAME_MAX]; + + basename(abs_pathname, target_component); + dirname(abs_pathname, dir_pathname); + if (vfs_lookup(dir_pathname, &dir_node) == NOTFOUND) + { + printf("%s not found.\n", dir_pathname); + return -1; + } + + vnode_t *target = NULL; + int ret = dir_node->v_ops->mkdir(dir_node, &target, target_component); + + if (ret != 0) + return ret; + + return 0; +} + +int vfs_open(char *pathname, int flags, struct 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 + vnode_t *target_node = NULL; + char abs_pathname[PATHNAME_MAX]; + + handle_path(pathname, abs_pathname); + + int ret = vfs_lookup(abs_pathname, &target_node); + + if (ret == EXISTED && target_node->internal->type == FILE) + { + return target_node->f_ops->open(target_node, target); + } + else if (ret == EXISTED && target_node->internal->type == DIR) + { + printf("%s is not a file.\n", abs_pathname); + return -1; + } + else if (ret == NOTFOUND && (flags & O_CREAT)) + { + if (vfs_create(abs_pathname) != 0) + return -1; + vfs_lookup(abs_pathname, &target_node); + return target_node->f_ops->open(target_node, target); + } + else if (ret == NOTFOUND) + { + printf("[DEBUG/vfs_open] %s is not existed.\n", abs_pathname); + return -1; + } + else + return -1; +} + +int vfs_close(struct file *file) +{ + // 1. release the file handle + // 2. Return error code if fails + if (file == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->close(file); +} + +int vfs_write(struct file *file, 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 == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->write(file, buf, len); +} + +int vfs_read(struct file *file, void *buf, size_t len) +{ + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 3. return read size or error code if an error occurs. + if (file == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->read(file, buf, len); +} + +void get_next_component(char *pathname, char *target, int level) +{ + int level_count = -1; + int target_begin = 0; + while (level_count != level) + { + if (pathname[target_begin] == '/') + level_count++; + target_begin++; + } + + int i = 0; + while (pathname[target_begin + i] != '/' && pathname[target_begin + i] != '\0') + { + target[i] = pathname[target_begin + i]; + i++; + } + + target[i] = '\0'; +} + +void basename(char *src, char *des) +{ + int level = 0; + int total_level = strnchr(src, '/'); + + if (!strcmp(src, "/")) + { + strcpy(des, "/"); + return; + } + + while (level < total_level) + { + get_next_component(src, des, level); + level++; + } +} + +void dirname(char *src, char *des) +{ + int end = -1; + + for (int i = 0; i < strlen(src); i++) + if (src[i] == '/') + end = i; + + if (end == 0) + strcpy(des, "/"); + else + strncpy(src, des, end); +} + +void handle_path(char *rela, char *abso) +{ + if (rela[0] == '/') + { + strcpy(abso, rela); + if (abso[strlen(abso) - 1] == '/' && strlen(abso) != 1) + abso[strlen(abso) - 1] = '\0'; + return; + } + + strcpy(abso, cwdpath); + char next_component[COMPONENT_NAME_MAX]; + + int i = 0, j = 0; + while (rela[i] != '\0') + { + if (rela[i] != '/') + { + next_component[j] = rela[i]; + j++; + } + else if (rela[i] == '/' && i == strlen(rela) - 1) + break; + else + { + next_component[j] = '\0'; + j = 0; + + if (!strcmp(next_component, ".")) + ; + else if (!strcmp(next_component, "..")) + dirname(abso, abso); + else + { + if (abso[strlen(abso) - 1] != '/') + strcat(abso, "/"); + strcat(abso, next_component); + } + } + + i++; + } + + next_component[j] = '\0'; + + if (!strcmp(next_component, ".")) + return; + else if (!strcmp(next_component, "..")) + dirname(abso, abso); + else + { + if (abso[strlen(abso) - 1] != '/') + strcat(abso, "/"); + strcat(abso, next_component); + } +} + +void vfs_ls(char *pathname, int flag) +{ + vnode_t *target_vnode; + int ret; + + if (flag) + ret = vfs_lookup(pathname, &target_vnode); + else + ret = vfs_lookup(cwdpath, &target_vnode); + + switch (ret) + { + case NOTFOUND: + printf("%s is not existed.\n", pathname); + return; + case NOTDIR: + return; + } + + if (target_vnode->internal->type != DIR) + { + printf("%s is not a directory.\n", pathname); + return; + } + + for (int i = 0; i < target_vnode->internal->size; i++) + { + printf("%s", target_vnode->internal->entry[i]->internal->name); + if (target_vnode->internal->entry[i]->internal->type == DIR) + printf("/"); + printf(" "); + } + printf("\n"); +} + +int vfs_cd(char *target_dir) +{ + vnode_t *dir_node = NULL; + + switch (vfs_lookup(target_dir, &dir_node)) + { + case EXISTED: + if (dir_node->internal->type != DIR) + { + printf("%s is not a directory.\n", target_dir); + return -1; + } + break; + case NOTDIR: + return -1; + case NOTFOUND: + printf("%s is not existed.\n", target_dir); + return -1; + } + + strcpy(cwdpath, target_dir); + + return 0; +} + +long vfs_lseek(struct file *file, long offset, int whence) +{ + if (file == NULL) + { + printf("file is not existed.\n"); + return -1; + } + return file->f_ops->lseek64(file, offset, whence); +} + +long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int error = 0; + printf("ioctl() unimplement, return 0\n"); + return error; +} \ No newline at end of file diff --git a/Lab8/src/kernel/virtual_mem.S b/Lab8/src/kernel/virtual_mem.S new file mode 100644 index 000000000..5453ad829 --- /dev/null +++ b/Lab8/src/kernel/virtual_mem.S @@ -0,0 +1,54 @@ +#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) + +#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_ACCESS (1 << 10) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + + +.global tcr_init +tcr_init: + ldr x0, = TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + ret + +.global mair_init +mair_init: + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 + ret + +.global identity_init +identity_init: + mov x0, 0 // PGD's page frame at 0x0 + mov x1, 0x1000 // PUD's page frame at 0x1000 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x0] + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + ret \ No newline at end of file diff --git a/Lab8/src/kernel/virtual_mem.c b/Lab8/src/kernel/virtual_mem.c new file mode 100644 index 000000000..d05dd504e --- /dev/null +++ b/Lab8/src/kernel/virtual_mem.c @@ -0,0 +1,10 @@ +extern void tcr_init(); +extern void mair_init(); +extern void identity_init(); + +void virtual_mem_init() +{ + tcr_init(); + mair_init(); + identity_init(); +} \ No newline at end of file diff --git a/Lab8/src/lib/hex2int.c b/Lab8/src/lib/hex2int.c new file mode 100644 index 000000000..4f30c1df8 --- /dev/null +++ b/Lab8/src/lib/hex2int.c @@ -0,0 +1,15 @@ +int hex2int(char *s, int n) +{ + int r = 0; + while (n-- > 0) + { + r <<= 4; + if (*s >= '0' && *s <= '9') + r += *s++ - '0'; + else if (*s >= 'A' && *s <= 'F') + r += *s++ - 'A' + 10; + else if (*s >= 'a' && *s <= 'f') + r += *s++ - 'a' + 10; + } + return r; +} \ No newline at end of file diff --git a/Lab8/src/lib/math.c b/Lab8/src/lib/math.c new file mode 100644 index 000000000..b96e3e335 --- /dev/null +++ b/Lab8/src/lib/math.c @@ -0,0 +1,9 @@ +int pow(int base, int exp) +{ + int result = 1; + for (int i = 0; i < exp; i++) + { + result *= base; + } + return result; +} \ No newline at end of file diff --git a/Lab8/src/lib/mem.c b/Lab8/src/lib/mem.c new file mode 100644 index 000000000..954ba9805 --- /dev/null +++ b/Lab8/src/lib/mem.c @@ -0,0 +1,33 @@ +#include + +void *memset(void *dest, register int val, int len) +{ + register unsigned char *ptr = (unsigned char *)dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +int memcmp(void *s1, void *s2, int n) +{ + unsigned char *a = s1, *b = s2; + while (n-- > 0) + { + if (*a != *b) + { + return *a - *b; + } + a++; + b++; + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} \ No newline at end of file diff --git a/Lab8/src/lib/mini_uart.c b/Lab8/src/lib/mini_uart.c new file mode 100644 index 000000000..d1896d604 --- /dev/null +++ b/Lab8/src/lib/mini_uart.c @@ -0,0 +1,215 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "peripherals/irq.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; + +char read_buffer[100]; +char write_buffer[100]; +int len_WB = 0; +int len_RB = 0; +int buffer_count = 0; + +void uart_send(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + + if (c != '\x7f') + put32(AUX_MU_IO_REG, c); + + if (c == '\n') + uart_send('\r'); +} + +char uart_recv(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r == '\r' ? '\n' : r; +} + +void uart_send_byte(char c) +{ + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x20) + break; + } + put32(AUX_MU_IO_REG, c); +} + +char uart_recv_byte(void) +{ + char r; + while (1) + { + if (get32(AUX_MU_LSR_REG) & 0x01) + break; + } + r = get32(AUX_MU_IO_REG); + return r; +} + +void uart_send_string(char *str) +{ + while (*str) + uart_send(*str++); +} + +void uart_send_space(int size) +{ + while (size--) + uart_send(' '); +} + +void uart_send_string_of_size(char *str, int size) +{ + while (size--) + uart_send(*str++); +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) +{ + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) + { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_init(void) +{ + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 2 << 12; // set alt5 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 2 << 15; // set alt5 for gpio15 + put32(GPFSEL1, selector); + + put32(GPPUD, 0); + delay(150); // spec + put32(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + put32(GPPUDCLK0, 0); + + put32(AUX_ENABLES, 1); // Enable mini uart (this also enables access to its registers) + put32(AUX_MU_CNTL_REG, 0); // Disable auto flow control and disable receiver and transmitter (for now) + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + put32(AUX_MU_LCR_REG, 3); // Enable 8 bit mode + put32(AUX_MU_MCR_REG, 0); // Set RTS line to be always high + put32(AUX_MU_BAUD_REG, 270); // Set baud rate to 115200 + + put32(AUX_MU_CNTL_REG, 3); // Finally, enable transmitter and receiver +} + +void _putchar(char character) +{ + uart_send(character); +} + +/** + * Display a string + */ +// void printf(char *fmt, ...) +// { +// __builtin_va_list args; +// __builtin_va_start(args, fmt); +// // we don't have memory allocation yet, so we +// // simply place our string after our code +// char *s = (char *)&_end; +// // use sprintf to format our string +// vsprintf(s, fmt, args); +// // print out as usual +// while (*s) +// { +// /* convert newline to carrige return + newline */ +// if (*s == '\n') +// uart_send('\r'); +// uart_send(*s++); +// } +// } + +void asyn_read() +{ + memset(read_buffer, '\0', 100); + + put32(AUX_MU_IER_REG, 1); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (read_buffer[strlen(read_buffer) - 1] != '\r') + ; +} + +void asyn_write(char *str) +{ + strcpy(write_buffer, str); + len_WB = strlen(write_buffer); + + put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts + put32(ENABLE_IRQS_1, 1 << 29); + + // enable interrupt in el1 + asm("msr DAIFClr, 0xf;"); + + while (strlen(write_buffer) != 0) + ; +} + +void uart_rx_handler() +{ + read_buffer[len_RB++] = get32(AUX_MU_IO_REG); + + if (read_buffer[len_RB - 1] == '\r') + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + read_buffer[len_RB] = '\0'; + len_RB = 0; + } +} + +void uart_tx_handler() +{ + if (buffer_count < len_WB) + put32(AUX_MU_IO_REG, write_buffer[buffer_count++]); + else + { + put32(AUX_MU_IER_REG, 0); // Disable receive and transmit interrupts + buffer_count = 0; + memset(write_buffer, '\0', 100); + } +} + +void enable_uart_irq() +{ + put32(ENABLE_IRQS_1, 1 << 29); +} + +void disable_uart_irq() +{ + put32(DISABLE_IRQS_1, 1 << 29); +} \ No newline at end of file diff --git a/Lab8/src/lib/mm.S b/Lab8/src/lib/mm.S new file mode 100644 index 000000000..1bd32ff37 --- /dev/null +++ b/Lab8/src/lib/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/Lab8/src/lib/printf.c b/Lab8/src/lib/printf.c new file mode 100644 index 000000000..3eabd6eca --- /dev/null +++ b/Lab8/src/lib/printf.c @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "printf.h" + +#define PRINTF_DISABLE_SUPPORT_FLOAT + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct +{ + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) + { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)buffer; + (void)idx; + (void)maxlen; + if (character) + { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) +{ + (void)idx; + (void)maxlen; + if (character) + { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) +{ + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) + { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (size_t i = len; i < width; i++) + { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) + { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) + { + len--; + if (len && (base == 16U)) + { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) + { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) + { + do + { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) + { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) + { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) + { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) + { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) + { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) + { + } + else if ((frac == 0U) || (frac & 1U)) + { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) + { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) + { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) + { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) + { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) + { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) + { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union + { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) + { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) + { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) + { + if ((int)prec > expval) + { + prec = (unsigned)((int)prec - expval - 1); + } + else + { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) + { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) + { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else + { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) + { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) + { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) + { + while (idx - start_idx < width) + out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) + { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else + { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do + { + switch (*format) + { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) + { + width = _atoi(&format); + } + else if (*format == '*') + { + const int w = va_arg(va, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else + { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) + { + precision = _atoi(&format); + } + else if (*format == '*') + { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) + { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') + { + base = 16U; + } + else if (*format == 'o') + { + base = 8U; + } + else if (*format == 'b') + { + base = 2U; + } + else + { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) + { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) + { + // signed + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else + { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else + { + // unsigned + if (flags & FLAGS_LONG_LONG) + { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) + { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': + { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': + { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) + { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': + { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) + { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } + else + { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char *buffer, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char *format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} \ No newline at end of file diff --git a/Lab8/src/lib/queue.c b/Lab8/src/lib/queue.c new file mode 100644 index 000000000..6b56b4dca --- /dev/null +++ b/Lab8/src/lib/queue.c @@ -0,0 +1,7 @@ +void queue_push(char c) +{ +} + +void queue_pop() +{ +} \ No newline at end of file diff --git a/Lab8/src/lib/simple_malloc.c b/Lab8/src/lib/simple_malloc.c new file mode 100644 index 000000000..b7a80a897 --- /dev/null +++ b/Lab8/src/lib/simple_malloc.c @@ -0,0 +1,8 @@ +char *counter = (char *)0x10000000; + +void *simple_malloc(unsigned int size) +{ + char *dest = counter; + counter += size; + return dest; +} diff --git a/Lab8/src/lib/string.c b/Lab8/src/lib/string.c new file mode 100644 index 000000000..d29bfeb4a --- /dev/null +++ b/Lab8/src/lib/string.c @@ -0,0 +1,113 @@ +#include + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + unsigned char c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + break; + if (c1 == '\0') + return 0; + } + + return c1 - c2; +} + +int strlen(const char *str) +{ + const unsigned char *s = (const unsigned char *)str; + unsigned int len = 0; + + while (1) + { + if (*s++ == '\0') + return len; + len++; + } +} + +char *strcpy(char *destination, const char *source) +{ + // return if no memory is allocated to the destination + if (destination == NULL) + { + return NULL; + } + + // take a pointer pointing to the beginning of the destination string + char *ptr = destination; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + + // include the terminating null character + *destination = '\0'; + + // the destination is returned by standard `strcpy()` + return ptr; +} + +// A simple atoi() function +int atoi(char *str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + res = res * 10 + str[i] - '0'; + + // return result. + return res; +} + +char *strncpy(char src[], char des[], int n) +{ + int i = 0; + while (src[i] != '\0' && i < n) + { + des[i] = src[i]; + i++; + } + des[i] = '\0'; + + return des; +} + +int strnchr(char *pathname, char target) +{ + int count = 0; + for (int i = 0; i < strlen(pathname); i++) + if (pathname[i] == target) + count++; + + return count; +} + +void strcat(char *des, char *s) +{ + int begin = strlen(des); + int cat_len = strlen(s); + + for (int i = 0; i < cat_len; i++) + des[begin + i] = s[i]; + + des[begin + cat_len] = '\0'; +} \ No newline at end of file diff --git a/Lab8/src/lib/utils.S b/Lab8/src/lib/utils.S new file mode 100644 index 000000000..c35527a42 --- /dev/null +++ b/Lab8/src/lib/utils.S @@ -0,0 +1,15 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret diff --git a/Lab8/src/userprogram/link.ld b/Lab8/src/userprogram/link.ld new file mode 100644 index 000000000..d84040b38 --- /dev/null +++ b/Lab8/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x15000000; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; \ No newline at end of file diff --git a/Lab8/src/userprogram/user.S b/Lab8/src/userprogram/user.S new file mode 100644 index 000000000..6b0a2824a --- /dev/null +++ b/Lab8/src/userprogram/user.S @@ -0,0 +1,22 @@ +.section ".text.boot" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + mov x8, #0 + svc 0 + mov x8, #4 + svc 0 + mov x8, #9 + svc 0 + mov x8, #5 + svc 0 + blt 1b +1: + b 1b + +get_pid: + mov x8, #0 + svc 0 + ret \ No newline at end of file diff --git a/Lab8/userprogram.img b/Lab8/userprogram.img new file mode 100755 index 000000000..ada965550 Binary files /dev/null and b/Lab8/userprogram.img differ