From 97a649deb5001117854791d669b18f81f094ac70 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Wed, 22 Mar 2023 21:07:54 +0800 Subject: [PATCH 01/22] prepare to start lab3 --- Lab3/Makefile | 51 ++ Lab3/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 32753 bytes Lab3/bootloader.img | Bin 0 -> 13744 bytes Lab3/bootloader.py | 25 + Lab3/include/device_tree.h | 9 + Lab3/include/load_kernel.h | 7 + Lab3/include/mbox.h | 6 + Lab3/include/mbox_call.h | 9 + Lab3/include/mini_uart.h | 12 + Lab3/include/mm.h | 19 + Lab3/include/peripherals/base.h | 6 + Lab3/include/peripherals/device_tree.h | 16 + Lab3/include/peripherals/gpio.h | 25 + Lab3/include/peripherals/mbox_call.h | 40 + Lab3/include/peripherals/mini_uart.h | 19 + Lab3/include/peripherals/reboot.h | 8 + Lab3/include/printf.h | 109 +++ Lab3/include/read_cpio.h | 7 + Lab3/include/reboot.h | 8 + Lab3/include/shell.h | 7 + Lab3/include/stdlib.h | 21 + Lab3/include/utils.h | 8 + Lab3/initramfs.cpio | Bin 0 -> 1024 bytes Lab3/kernel8.img | Bin 0 -> 19584 bytes Lab3/rootfs/a.c | 8 + Lab3/rootfs/cat.txt | 1 + Lab3/rootfs/one | 3 + Lab3/rootfs/ts.txt | 0 Lab3/send_kernel.py | 40 + Lab3/src/bootloader/boot.S | 25 + Lab3/src/bootloader/bootloader.c | 15 + Lab3/src/bootloader/config.txt | 3 + Lab3/src/bootloader/link.ld | 21 + Lab3/src/bootloader/load_kernel.c | 67 ++ Lab3/src/kernel/boot.S | 36 + Lab3/src/kernel/config.txt | 2 + Lab3/src/kernel/device_tree.c | 266 ++++++ Lab3/src/kernel/kernel.c | 16 + Lab3/src/kernel/link.ld | 19 + Lab3/src/kernel/mbox.c | 63 ++ Lab3/src/kernel/mbox_call.c | 42 + Lab3/src/kernel/read_cpio.c | 90 ++ Lab3/src/kernel/reboot.c | 19 + Lab3/src/kernel/shell.c | 160 ++++ Lab3/src/lib/hex2int.c | 15 + Lab3/src/lib/mem.c | 22 + Lab3/src/lib/mini_uart.c | 97 +++ Lab3/src/lib/mm.S | 6 + Lab3/src/lib/printf.c | 1044 ++++++++++++++++++++++++ Lab3/src/lib/simple_malloc.c | 8 + Lab3/src/lib/string.c | 31 + Lab3/src/lib/utils.S | 15 + 52 files changed, 2546 insertions(+) create mode 100644 Lab3/Makefile create mode 100644 Lab3/bcm2710-rpi-3-b-plus.dtb create mode 100755 Lab3/bootloader.img create mode 100644 Lab3/bootloader.py create mode 100644 Lab3/include/device_tree.h create mode 100644 Lab3/include/load_kernel.h create mode 100644 Lab3/include/mbox.h create mode 100644 Lab3/include/mbox_call.h create mode 100644 Lab3/include/mini_uart.h create mode 100644 Lab3/include/mm.h create mode 100644 Lab3/include/peripherals/base.h create mode 100644 Lab3/include/peripherals/device_tree.h create mode 100644 Lab3/include/peripherals/gpio.h create mode 100644 Lab3/include/peripherals/mbox_call.h create mode 100644 Lab3/include/peripherals/mini_uart.h create mode 100644 Lab3/include/peripherals/reboot.h create mode 100644 Lab3/include/printf.h create mode 100644 Lab3/include/read_cpio.h create mode 100644 Lab3/include/reboot.h create mode 100644 Lab3/include/shell.h create mode 100644 Lab3/include/stdlib.h create mode 100644 Lab3/include/utils.h create mode 100644 Lab3/initramfs.cpio create mode 100755 Lab3/kernel8.img create mode 100644 Lab3/rootfs/a.c create mode 100644 Lab3/rootfs/cat.txt create mode 100644 Lab3/rootfs/one create mode 100644 Lab3/rootfs/ts.txt create mode 100644 Lab3/send_kernel.py create mode 100644 Lab3/src/bootloader/boot.S create mode 100644 Lab3/src/bootloader/bootloader.c create mode 100644 Lab3/src/bootloader/config.txt create mode 100644 Lab3/src/bootloader/link.ld create mode 100644 Lab3/src/bootloader/load_kernel.c create mode 100644 Lab3/src/kernel/boot.S create mode 100644 Lab3/src/kernel/config.txt create mode 100644 Lab3/src/kernel/device_tree.c create mode 100644 Lab3/src/kernel/kernel.c create mode 100644 Lab3/src/kernel/link.ld create mode 100644 Lab3/src/kernel/mbox.c create mode 100644 Lab3/src/kernel/mbox_call.c create mode 100644 Lab3/src/kernel/read_cpio.c create mode 100644 Lab3/src/kernel/reboot.c create mode 100644 Lab3/src/kernel/shell.c create mode 100644 Lab3/src/lib/hex2int.c create mode 100644 Lab3/src/lib/mem.c create mode 100644 Lab3/src/lib/mini_uart.c create mode 100644 Lab3/src/lib/mm.S create mode 100644 Lab3/src/lib/printf.c create mode 100644 Lab3/src/lib/simple_malloc.c create mode 100644 Lab3/src/lib/string.c create mode 100644 Lab3/src/lib/utils.S diff --git a/Lab3/Makefile b/Lab3/Makefile new file mode 100644 index 000000000..d6e859c68 --- /dev/null +++ b/Lab3/Makefile @@ -0,0 +1,51 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +all : kernel8.img bootloader.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 + +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 + +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 + +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 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 0000000000000000000000000000000000000000..0dd0e2f435a74e6a0c01e2a30fa0246d55ee3091 GIT binary patch literal 32753 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 OPqI~*e!TdI|Nj8Si!WyY literal 0 HcmV?d00001 diff --git a/Lab3/bootloader.img b/Lab3/bootloader.img new file mode 100755 index 0000000000000000000000000000000000000000..1df6100195b230985f22b7bad8a8902891a70102 GIT binary patch literal 13744 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 +#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..fc889988e --- /dev/null +++ b/Lab3/include/read_cpio.h @@ -0,0 +1,7 @@ +#ifndef _READ_CPIO_H +#define _READ_CPIO_H + +void read_cpio(char *cpioDest); +void read_content(char *cpioDest, char *filename); + +#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..6d9004dae --- /dev/null +++ b/Lab3/include/stdlib.h @@ -0,0 +1,21 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include "printf.h" + +int strcmp(const char *str1, const char *str2); +int strlen(const 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/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 0000000000000000000000000000000000000000..54afebd095a239102cbcb61b0eb0c4562382c77f GIT binary patch literal 1024 zcmds#O>2Wd5QcN!Uooba3{FIYBVjx_S!k@ zIy0~P%)W{N21tmGFJlUk!=?3Yz2hP=`Dw33E}<`@!1Vj2(II+$>FmkrGJC?bo)_2k zkYd;LfAXm1b+32(81FQnKG^zQPdEnL6&ctm&Wp44={ n6sF&mmf}+kyfmJSE#2H>y6=7SkJiPz literal 0 HcmV?d00001 diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img new file mode 100755 index 0000000000000000000000000000000000000000..438102d27eb5631106d945c7970414dccba3a882 GIT binary patch literal 19584 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 literal 0 HcmV?d00001 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/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..6fdfd0eb8 --- /dev/null +++ b/Lab3/src/kernel/boot.S @@ -0,0 +1,36 @@ +#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: + adr x0, __bss_start + adr x1, __bss_end + sub x1, x1, x0 + bl memzero + + mov sp, #LOW_MEMORY // 4MB + bl kernel_main + b proc_hang // should never come here 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/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..5a0d01398 --- /dev/null +++ b/Lab3/src/kernel/read_cpio.c @@ -0,0 +1,90 @@ +#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); + // 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); +} \ 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..26fa08e3a --- /dev/null +++ b/Lab3/src/kernel/shell.c @@ -0,0 +1,160 @@ +#include "stdlib.h" +#include "mini_uart.h" +#include "reboot.h" +#include "read_cpio.h" +#include "device_tree.h" + +extern void *_dtb_ptr; +extern char *cpioDest; + +#define COMMAND_BUFFER 20 +#define FILENAME_BUFFER 20 + +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:\n"); + uart_send_string("cat\t:\n"); + uart_send_string("dts\t:\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++; + } + filename[i] = '\0'; + + 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); + } +} + +void shell_start() +{ + char c; + int i = 0; + char command[COMMAND_BUFFER]; + + memset(command, '\0', COMMAND_BUFFER); + + uart_send_string("Starting shell...\n"); + 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/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..a9e9a4efd --- /dev/null +++ b/Lab3/src/lib/mini_uart.c @@ -0,0 +1,97 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" + +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); +} 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..fea41a69a --- /dev/null +++ b/Lab3/src/lib/printf.c @@ -0,0 +1,1044 @@ +/////////////////////////////////////////////////////////////////////////////// +// \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 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/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..dcb25d514 --- /dev/null +++ b/Lab3/src/lib/string.c @@ -0,0 +1,31 @@ +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++; + } +} \ No newline at end of file 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 From eb0d9ea5c933e2ec30eeeb9027e44acc2f79bb3b Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 23 Mar 2023 22:12:00 +0800 Subject: [PATCH 02/22] Lab3 ex1, add el2, el1, NO el0 --- Lab3/Makefile | 14 ++-- Lab3/bootloader.img | Bin 13744 -> 10032 bytes Lab3/initramfs.cpio | Bin 1024 -> 10240 bytes Lab3/kernel8.img | Bin 19584 -> 21992 bytes Lab3/rootfs/userprogram.img | Bin 0 -> 9240 bytes Lab3/src/kernel/boot.S | 88 ++++++++++++++++++++++ Lab3/src/kernel/exception.c | 142 +++++++++++++++++++++++++++++++++++ Lab3/src/kernel/read_cpio.c | 38 ++++++++++ Lab3/src/lib/mini_uart.c | 26 +++++++ Lab3/src/lib/printf.c | 2 + Lab3/src/userprogram/link.ld | 19 +++++ Lab3/src/userprogram/user.S | 11 +++ Lab3/userprogram.img | Bin 0 -> 9240 bytes 13 files changed, 335 insertions(+), 5 deletions(-) create mode 100755 Lab3/rootfs/userprogram.img create mode 100644 Lab3/src/kernel/exception.c create mode 100644 Lab3/src/userprogram/link.ld create mode 100644 Lab3/src/userprogram/user.S create mode 100755 Lab3/userprogram.img diff --git a/Lab3/Makefile b/Lab3/Makefile index d6e859c68..51292302e 100644 --- a/Lab3/Makefile +++ b/Lab3/Makefile @@ -1,22 +1,22 @@ ARMGNU ?= aarch64-linux-gnu -COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -g -mgeneral-regs-only ASMOPS = -Iinclude BUILD_DIR = build SRC_DIR = src -all : kernel8.img bootloader.img +all : kernel8.img bootloader.img userprogram.img clean : rm -rf $(BUILD_DIR) *.img $(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c - mkdir -p $(@D) + @mkdir -p $(@D) $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ $(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S - mkdir -p $(@D) + @mkdir -p $(@D) $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ C_FILES = $(wildcard $(SRC_DIR)/*/*.c) @@ -32,7 +32,7 @@ kernel8.img: $(SRC_DIR)/kernel/link.ld $(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 + 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 @@ -41,6 +41,10 @@ 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 diff --git a/Lab3/bootloader.img b/Lab3/bootloader.img index 1df6100195b230985f22b7bad8a8902891a70102..cb02047a14cca35d818fba9cc0bc369c387f22a4 100755 GIT binary patch delta 1092 zcmZ{jTSyd97{|XeyXl%*wu-lP+Y#9`SMmmxsJLY$qG;iE)x0HARLGW3O1L70Y2_Y| zVD!?uFJ0(C7%lc8Fa-ncVe`QUpJvSqMGszH8g;a9X4f0o!0=C|Nl5McyF-G z85e$*L=51Y2r$_hLQj|@yf6|+pXs0v0KCLupp{1Mu8y|k6nViKPn&S%y0V%8dNCb5 zR{~?GimmXaTRl8Ls!}iCuIEMUs~79&nlZ);u6Vi{1bupdD97QhDSqzlN@)l?MUHb9 zLRcBe4E6$Vpm{p>l;mr@&{@d7kq1W_lvB|cxC}I0$If%(+GZ><7l{&hjYG$r(HX~l zPr!zooB*LzOa{rqengj$Siu;RO`>1PTm6c66M)?XU=zoHx@FcFI~#rTfTadmsE(T< zZ6=Y}(`vzHauWLlL48i_)?B~duR-l3cF_9uPc>>E*x?pxE$-8RZH0p1Pp0b0r|nmxHuW0n*|% zki;=g?X;>EWlktw0!a*V=*8RM*hyAe&zwA}h> z7h^z7E@BWjhq!sfEg)_YabiaRqv&4DpdZZ-!lA0BzVhR;Z8zyQ~6ETgYG+2#lS!Y)*H>%Z5k+^9!)GEoX z;{hK-{>XcMV8l7>=sb3Gp%Nq4C|r%7t0=ZbEJPpJ8r4UK;v+NdBKoKeqrcbCM-Tew zQG9$Cy3>M#%n200Dv3uy^8XPDl&Ev72mko)K#hIQ%4dJGoEbQez<1}#NZuJ9IQR#@ CyPS;x delta 4724 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 diff --git a/Lab3/initramfs.cpio b/Lab3/initramfs.cpio index 54afebd095a239102cbcb61b0eb0c4562382c77f..05d84e383616a3f8a13e9acb0d493f0c82fef0c9 100644 GIT binary patch literal 10240 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 m-ySG+X!&U$O-@gD$3=%p3 delta 166 zcmZn&XyD*8Fb6_I0|O&7M<+vziG0ee24;?y<`d1FSwJEaYgNDk1{0Tgvzj?N8=6e4 zQ|1LJaWwz~L&J%7>XUsL6$BX=7*GszoLHyCW?<=NZs9n28Dlt5x09hIP>j#O(a_A? X8KT`3s-1K5FEuvC$q&>O(69sm1Jx^` diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img index 438102d27eb5631106d945c7970414dccba3a882..3c19453ca0fff1f1a4955ce64a45922c3b5284d4 100755 GIT binary patch literal 21992 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 delta 6996 zcmcIp4R93Kk?xt5us{p?K}bT9cV{JmelVZ|8S!IzN0KclL%_(8*hv+vgq;xMz=2&U zd{R6En7~0QXc)yhhl^Q=&o-RxyI6_CCROp;B#vX_oGk_0aV2%L`pusm;#|e7VA*{A zW=6XR64%w$sY>({UQ>z~*CM*7XaOZwxI_Af-=y;-Ka&&U+_5EY!4=`R~%e#0Gd?w&8mxdEa#Hsw)I(sTa{%QLAbo=g_it;qrlQKIvuQwa|l3Q;#z zw#N5N)7x-9;;#J}sd;Q4X^W(+6k!j6*_&GXbJOMpsd*Fl0u}?a zd@8s&WMRn;nWWluk3+Y}q3bq1G2L8SR3e|4VcuUCxrawL1{9SQL?P_>)5FwAj z<8~bU#`9k4DVuPdBIG|N*qfJMhy7b%&%RC;ES$_6QjDWsvvu}nPO68!=IPnN%&=l? z_4a)@yFt<@V7#K}ZAd`7vgrY17DUSs?N>bKEuc(vo_!|j+5AqT^9Q0LS~(<8OW|_E z%2DHg0Y8U#kK-MRmK%rR0AZP0VH|Yl(F$X~J69`>y_jo)DO71_?uK4zbYXtVbw#*R zDEYOclRafTo*^ltn&)J37aBDVus81RHLmu~+66p|`#7HTNnV;uJa3 zV%VM~QO|l;y(4YEtKBV;%3PxaaFd^-i1lrn5Xsqb#cM7JEXaNrQ=uKTVn-m5e-3NP z(A!?eXl-s<_L|o`8TfSb3t0Xf{fwh{4x_LkG*>vO8G9Tt$+_jE*fz&JbXth6ZjJx) zin+YJ+_wd+f~3zi*i~X~%{6|E`Ey?LpUM}seHBxyg$#c(*~_ErhC4t{E_B)gjy#9; zZ-K=64|N%u+Rm4uo#$T$onXL2EC=gdUUR&>L%zprZmKBB6$+`!*n{0o@Sb_0xcYa+taXx0TD@jT&35@zwt1+gYBo2p2g>r{P}q+SJ+rr;tfxPE&}+V4 z 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 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(":\n ESR_EL1 "); + uart_hex(esr >> 32); + uart_hex(esr); + uart_send_string(" ELR_EL1 "); + uart_hex(elr >> 32); + uart_hex(elr); + uart_send_string("\n SPSR_EL1 "); + uart_hex(spsr >> 32); + uart_hex(spsr); + uart_send_string(" FAR_EL1 "); + uart_hex(far >> 32); + uart_hex(far); + uart_send_string("\n"); + // no return from exception for now + while (1) + ; +} diff --git a/Lab3/src/kernel/read_cpio.c b/Lab3/src/kernel/read_cpio.c index 5a0d01398..cf6e39b2f 100644 --- a/Lab3/src/kernel/read_cpio.c +++ b/Lab3/src/kernel/read_cpio.c @@ -52,6 +52,44 @@ void read_cpio(char *cpioDest) } 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); + // 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); +} + +void load_content(char *cpioDest, char *filename) { int flag = 0; while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) diff --git a/Lab3/src/lib/mini_uart.c b/Lab3/src/lib/mini_uart.c index a9e9a4efd..758015fff 100644 --- a/Lab3/src/lib/mini_uart.c +++ b/Lab3/src/lib/mini_uart.c @@ -1,6 +1,10 @@ #include "utils.h" #include "peripherals/mini_uart.h" #include "peripherals/gpio.h" +#include "stdlib.h" + +// get address from linker +extern volatile unsigned char _end; void uart_send(char c) { @@ -95,3 +99,25 @@ 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++); +// } +// } \ No newline at end of file diff --git a/Lab3/src/lib/printf.c b/Lab3/src/lib/printf.c index fea41a69a..3eabd6eca 100644 --- a/Lab3/src/lib/printf.c +++ b/Lab3/src/lib/printf.c @@ -36,6 +36,8 @@ #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 diff --git a/Lab3/src/userprogram/link.ld b/Lab3/src/userprogram/link.ld new file mode 100644 index 000000000..7d7e655a2 --- /dev/null +++ b/Lab3/src/userprogram/link.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + . = 0x14000000; + .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..838323763 --- /dev/null +++ b/Lab3/src/userprogram/user.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b \ No newline at end of file diff --git a/Lab3/userprogram.img b/Lab3/userprogram.img new file mode 100755 index 0000000000000000000000000000000000000000..ee96ea529eb541c6fb882e18209e50d20f72e4e9 GIT binary patch literal 9240 zcmeI2U2I&%701urwPOd41L~RF9&sAaqz?u<@;&##A9BIUq}f0(MpY92$M3VynN|;r<|A35&IZ7wgl)< zBeE)J)OqMe-#m-eX{$P!u+X_mFpT`hs7BjLWuw`4Nr2f45XRq_Py_fnupQfbT4}zqCHt2qqiwNeZ8=k-}uF z6sGXebV~AP*T64@y_>*hjh`1u8D8zR*k}5d_u(4r2jmwnS4^1$Zn+SilN){b`h^Mn z5^hkev)i-y23;)f%z-|S%_iP9fN_)ZF#ZHrbO~G0fqt6!tHrLx_y@__4#OXM_1ST? zyG-|VLVA~9q>iDn-7mC@oKzjVSPwnur}B$~zjHDAtHp6-Z5bTzp2cry8F<~{=hZI( z`UGaoV%e|#y*369YaHoV&3c1-R^y#{#Ot=;4$0R$oD9|uC&kEHmffyvG}r;I;GlXx z>efnXkDR*~8$NYznNDV%AvflwdMUo}dQb5+v}=uy!E&dwTxt4V($V6J5w)#MrxE2- z#`mz(5&MkajSS`mO1B2Qe8~(s*)ffK@5MN-F>;5>?7h}KSK!oWaRRed5&!vo4Cc<( zs(czOpDQ9h^{lrt`Sn%gcdu}Ma{Q~&#r|}3J9WLe7)e**eYn>0UadMA|JWF+MnB-_ zSH$$|435V4$gRMRb*wRV7T(kNa0;49)~Q+RtdEej9nKD8jW-e{3Mdxku#zAfJ~XOcBzrCWJV%xk=ck4dkH?30!F7uroVCcl`y zI(1MoqPdDa(d=cny0yi8vsV&7(OwzvWzdrEasm7E>WG)?mtG`hbX?CZtdrs<@WPI|mw!5^SW&hFs(~=n{r+iV@{)@V{*S<4y95U$zcHps zRi#-U)2y#b^SPL2T~!*Bi;O;NtI{OlQ$nv4=f>cG-BXADRq8FhFpJA`#o2Svh_3w; zlKTX;F0|U346Y|IxS6iwMqUi==wx5 zKb=RT=gPa5kq7qp2YmKpe3K6zM4tz(e#sXl6t(&@9&EAsOp*H=IP zf_+fW5QfK<71%`f+`fR_-%`8bbJ_x+L&@5dhg6T}c_k&KKy+NL^`+ZXAa z`kR-LdSa6Hn6`iPGUMcRZ8vNGXN-fNw#PKaM?4$jN4!iwbT_J92A9=UanZS6j9+U7 z-4|jWS5=I~dLp-7_hBQet%9yC=258!9|Bc%G<#7M{g*LM<6PrQTm7amaUaER07m^_ zvd_!Br5HW!W&E3LyQ`1(2VjTJ+P?oyTFpgw()L3~9{4c9H_AS5Tf1aWr_I;~+n|rWKMwoS`!4)r{Ls~` z#YlH_(0yJKKhUVCYHe-Vj_Z5El4D!9k{g;SE z8)sK7t*o1+>BP;^pyJ$JI9G|BG}y=5V|- z*71KTrkj^6rtjfQa*vVb+S-A6%V`v$v@=Epo<}s2(=# z=Q)Elh42W;%UvS(eU&=RQmpA()@`tePVsj~nJwwhz?O2%ris}K+sfAQrL1+%*#+vb zg@FA`^Hsm@-8{S6>wa?A7r{c#K157FAM)P^{^@<$wplvsQ+Qmw zPvK{LimY;;&ft}9qwn|<C+Cwi#;Y2D*CYJb$x zrgu>L`#S6QM8ecfOsjVfS|`&p;j(PAo<~aLZ|3ICZ18gZHU6kyBcuH_-L&3l$b0Ti za?zg7H>X|qrP2p{+vJ{6ocSiz@6qDmNms-F`K(^lSSD^lFZ7#4zbW*aM!&P@7f@@{ zdX8z!^{V}Jy#n-_B>!lA)aV3`f$AiZ+c`(4x13H6E98aI{Y*8i6dT2idTNlQrM4Q> zpc6HR61m4a-V}_Lk#h?~G;|;E!H2ob4ut@k6p@tilhx z50Gj60N?q3*aZI>?iFY6QObB@PYAvfMnwpn@J{yuy|>jn67!C6^KOtk zR_+FZLBEqVd*5+gv+l0I<2<43vzE1+qJ9w3rA2Nlov`C}EBZA$8T3_# z?kxk#x7E)pFKx*QP3W5cXQ|X0-HXmNu0OB$s%N;%oM3DQK8BunX(3+?a1QbGo{ITq z--1SYuPCu^K|51-%lUD=Usg=uWA3OMtv&`9;zw~YwF1RPF#!+NQEk^ckz%HO@a%}j z)Lv6V&@s)Mim}eqXT`bE>4PlQhi?IcTZB<@@HaM=z(2eE$LK-Z&N}9p@oO!fBh)S4|J5GoQNm2 zMR-=l}Vi~Q~36V+=W-GY>@TMGM8UgxD8e!N%w zx)+F{IaH7_B>vXc8&cYH^ueh3dw_tR#}Ge>`%=L6>bbK9lGzNf%!-aq5$ zqxX-|Z#Vm~jklp_e1+{l;`&(^kKd&~p!Ap7an5%}zPK3A&_CYJZx}AOP#p8A0Sw0@+ny`+%3RYjyI8?lLt=(ZTBLA8A)8AC+T8{esuHvh%Pu|Ee-_Xf8 zXJ7TV`VoFtE5B%tcyoEI_7>*x*SS2J%6VkH%xJkD&v_YhCyb0adVIs1qX#;;+Qb Date: Tue, 28 Mar 2023 21:22:29 +0800 Subject: [PATCH 03/22] Lab3 el0->el1 interrupt ok on qemu --- .vscode/settings.json | 4 +- Lab3/debug.log | 9 +++ Lab3/include/read_cpio.h | 2 + Lab3/initramfs.cpio | Bin 10240 -> 10240 bytes Lab3/kernel8.img | Bin 21992 -> 24512 bytes Lab3/rootfs/userprogram.img | Bin 9240 -> 9240 bytes Lab3/src/kernel/boot.S | 50 ++------------- Lab3/src/kernel/exception.S | 119 +++++++++++++++++++++++++++++++++++ Lab3/src/kernel/exception.c | 49 +++------------ Lab3/src/kernel/read_cpio.c | 31 +++++++-- Lab3/src/kernel/shell.c | 26 ++++++++ Lab3/src/userprogram/link.ld | 2 +- Lab3/src/userprogram/user.S | 2 +- Lab3/userprogram.img | Bin 9240 -> 9240 bytes 14 files changed, 201 insertions(+), 93 deletions(-) create mode 100644 Lab3/debug.log create mode 100644 Lab3/src/kernel/exception.S diff --git a/.vscode/settings.json b/.vscode/settings.json index f75eddd62..151c99719 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,8 @@ "utility": "c", "typeinfo": "c", "stdlib.h": "c", - "mini_uart.h": "c" + "mini_uart.h": "c", + "gpio.h": "c", + "base.h": "c" } } \ No newline at end of file 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/read_cpio.h b/Lab3/include/read_cpio.h index fc889988e..f33833343 100644 --- a/Lab3/include/read_cpio.h +++ b/Lab3/include/read_cpio.h @@ -3,5 +3,7 @@ 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/initramfs.cpio b/Lab3/initramfs.cpio index 05d84e383616a3f8a13e9acb0d493f0c82fef0c9..55d33a5527daaa16aea9f6474c77f21cc807a82a 100644 GIT binary patch delta 301 zcmZn&Xb6~Kz-DCRYUpgV(d-=~lsQ?8$!oHWn#AT3rdUqKg3Xt>`vn<4Oiq%W%$P9w z8<^~piwBCl0+I=nz2p}&UfBFj{v0Et+T^{8#*E^VKLSaf$*M~7j4Yd7mD(8@7fn8^ z>|Ec^z%Yf8p&_V2sNoXBljWbff&2qNb}v|bMi7HAM7$L$-VRj{6R!vI6+!wLx-KaI z^;g5>c_939D8GRN!Y>B$6G7@Z!F;1n`7jM!5Cz#l{z1itAO>CtKOL%p0cv3K=7TEL LjGIH$)-VD9qzP|J delta 333 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 PNp)H@*coq6{@DKk0}swL delta 4012 zcma)9du$ZP9scdz*}jJ#XZw5(*yh%}R15_3DmJz8+OCbGm;{WIx^l6vp(&xES4xJI|p3Z|ulJUQAN_+2h2>>0=XaVmi?%UF*o~@|%5FiKN02PtH#7p+%ZE zhj$UQ#W{>s%Eg6pb~|+Sl-Q=-L$OJL46e_jVDm9OhD zl3hEM6T)s~>nm7izOdIwZc{$!T3~zZ({Yo`6|9MC?zMh7A8KR2F6Kk-_q8p&*>k`r zNwba3<>x&!swJ}8*f)wPo~|}Bx>%AAJ9tq+v~ZLRV$1pW3kK~VWUA*E3qC6vr3A4X z0e+z{Jau&m0%tgZDPq?G6t@-|nbXDm^TO{=X@pzmw1_3)k~V4VE&fE&0}qdCy2h*; zBeN0Z#S=>4BnycawfXf9Z{cK1K4J6g96k+wdb5$V+*$+eh^!^j0vfOPPK}Kcx!68b z+>B+Bp@vJAy3YxXdQ~Z#?^G>dlSsX3^R23dLe+xJFLQWZ@s`amlzfb~Mhlz(-7O2x zRRuh%u+7g>1>CBD%|}!L#alK%#o?naM{js!{z{c!tl>aw$*1uc#Jq_L*dnZ3?c}-@$n>8D_>gaE)la`fj$Prat~z+P$1GrBKH(d}e!)hX z@bgFft*}!C>>vKIpZk|;;Ze6<$j<6!AGV%-?Ia4>^L#w8dnI&5?0xi6;`D**kX;4F zUX;(1bT7LET%fSwWfy=Wy8DWpN7T#S3-EV}e~GlR?8#IeTT0p?fxpJQOwrlXR(Ues{AZQ3A-;pOQM8z@$;qK;xv@X zr&_8yG)mdMkm8PTW?0G^fD08)m9p8uF5t-(r7~6zP8?Xe6aAxTMc5Bwlq+tOu_=PX zZW4acim+D(VvfKpW95RZv<2;s-G)&nNR=_4;L2=&vtut)ehM-THleABk$2=<(JCXk zZ8bf}MBFgm^WrAi<(v*Og6wt-q+KA$#+7Z9vrm+rDrbLJwpGqPRyI|zk6<5jRz=e* zWRY%gqR3I_M3GU!u7mrT(wVld#nF8;t%QjRb`fNh5`C|@?3swhG^&(|8=ZhN&|{W2 zaTRBXjLa2$)$B*|NARi@yr$dwjr@b);vuOtDp{|O;y;86w;|i@5?={sUneq}7_DJS zirr@oo5?iR2~`iIhmB-?kk+`!Xx4WcJdL>NkLgVoc=AxWh_QzG56Y{*C8FSMgEzk~ z!BuG#UxUo~eSLyX!~&g}eB)NKJ?JDmh>Wk$WOvozHt=-09vegfI79(!xV_nc<9`al zcN>e)D5ucgjBk;xXK>L8?NXujn0<>e3cOH^gGN#`uU)Y$=8Vkk$oXu8CUoUOl z^x#3!!VVZ_E(;n~z@zO)xB&h|+E;m9RrN-%t4c(LllLV+~1N*;qn@QSC>E}jrJs~Zs=$RITdXx#2M z=I|%NomweB6P}@!@sGkYTz7ojUwsmrJeTMyAD>daBtPuD*V&n$Vq-jLZxV5g(*aCK ztel00iYU&bQ$tm!M8|in-3i`)&*ngd=nxxYV&q+ZFuc&U*T>I>r@Quf`ClZIXII+-Op>DVvQ*qywdat~jf82OcbF~+FbiW$xL{I8G`!%qj=R9m4F zHHbM;<>5_e5$6u|)cVnN{Zt}VQFn?_Q?B~i|tMBFiT6V~ln#Z4Nl0!}NkRV4efV|%P zva4~ySZw)u%{5!&540?DRcm}VhGLDs)iTZH(Z;T}JgK=}CO)*HBX$tCfwklz+O4t%B_o3zBn8=L@g60Qg4TUx8eHnz>vmKk9tE_U(y!WzCwz?#Ikl5sWX zID*E=WcLsgPj{?o;J2QyE%)ns*V?|}uHJ<@l%#j{<^Zr?U%NWbO?r2`^7Fatp5>`^ mJNW3(D1I?ekY6-HGbVp?K5YNyJQuRZfeL%P`H&a7y8i-FT()Td diff --git a/Lab3/rootfs/userprogram.img b/Lab3/rootfs/userprogram.img index ee96ea529eb541c6fb882e18209e50d20f72e4e9..83efd9631fdcd22cd665917b5808106cd758bd29 100755 GIT binary patch delta 295 zcmbQ?F~dWGfuZ3N0}I1MMh1o}@*)f$SO5PX!oa{FvLTj}v0!r?XTKohhsp1xCNm~X zZjuI(VlweSkuo5eF!`FyV#W)bt7OkHGOA7Xls9G+pIi$hc_wd^muFyr2@phYykk}X>6AO delta 273 zcmbQ?F~eh{L@Xzxz~(Z}enG|wli8#vGcrt`1t#@m;(;Q4K$2ndJDJ6d1)C?yo?~Rx znVcwZ%qTl~Dv%VKd{SPXk!$ly`F2Le6_X1Uo$IGEFic@&Xb5T$YPiJkWcjCwK>h(B zdooyjMi7HAM7$d+-V0R^6K@6b6+!wLx-KaI^*6%gc_92+D8GRN!mkAK6G7@Z!F;1n or7#U#5Cw%m{z1itAO>CtKNqTj0cv38=2WF>#t9OWA1GS@0Jl_RQ~&?~ diff --git a/Lab3/src/kernel/boot.S b/Lab3/src/kernel/boot.S index 08e4613bf..cb280077a 100644 --- a/Lab3/src/kernel/boot.S +++ b/Lab3/src/kernel/boot.S @@ -48,6 +48,8 @@ bss: sub x1, x1, x0 bl memzero + bl set_exception_vector_table // set exception vector table base + mov sp, #LOW_MEMORY // 4MB bl kernel_main b proc_hang // should never come here @@ -76,49 +78,5 @@ from_el2_to_el1: // change execution level to EL1 mov x2, #0x3c5 msr spsr_el2, x2 - adr x2, bss - msr elr_el2, x2 - eret - -.align 11 // vector table should be aligned to 0x800 -.global exception_vector_table -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 - .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 - 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_exception_vector_table: - adr x0, exception_vector_table - msr vbar_el1, x0 + msr elr_el2, lr + eret \ 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..b71651bd8 --- /dev/null +++ b/Lab3/src/kernel/exception.S @@ -0,0 +1,119 @@ +.global set_exception_vector_table +.global exception_vector_table + +#define CORE0_TIMER_IRQ_CTRL 0x40000040 + +// 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 + +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 + +.align 11 // vector table should be aligned to 0x800 +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 + .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 + 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_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + ret lr + +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mrs x0, cntfrq_el0 + msr cntp_tval_el0, x0 // set expired time + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + +core_timer_handler: + mrs x0, cntfrq_el0 + msr cntp_tval_el0, x0 + +/* +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/exception.c b/Lab3/src/kernel/exception.c index b2b909ad6..f7687ccf6 100644 --- a/Lab3/src/kernel/exception.c +++ b/Lab3/src/kernel/exception.c @@ -1,34 +1,9 @@ -/* - * Copyright (C) 2018 bzt (bztsrc@github) - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - */ - #include "mini_uart.h" /** * common exception handler */ -void exception_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) +void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsigned long spsr, unsigned long far) { // print out interruption type switch (type) @@ -123,20 +98,14 @@ void exception_handler(unsigned long type, unsigned long esr, unsigned long elr, } } // dump registers - uart_send_string(":\n ESR_EL1 "); - uart_hex(esr >> 32); - uart_hex(esr); - uart_send_string(" ELR_EL1 "); - uart_hex(elr >> 32); - uart_hex(elr); - uart_send_string("\n SPSR_EL1 "); + uart_send_string("\nSPSR_EL1 "); uart_hex(spsr >> 32); uart_hex(spsr); - uart_send_string(" FAR_EL1 "); - uart_hex(far >> 32); - uart_hex(far); + 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("\n"); - // no return from exception for now - while (1) - ; -} +} \ No newline at end of file diff --git a/Lab3/src/kernel/read_cpio.c b/Lab3/src/kernel/read_cpio.c index cf6e39b2f..665e57070 100644 --- a/Lab3/src/kernel/read_cpio.c +++ b/Lab3/src/kernel/read_cpio.c @@ -75,7 +75,10 @@ void read_content(char *cpioDest, char *filename) } // 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); @@ -89,7 +92,7 @@ void read_content(char *cpioDest, char *filename) uart_send_string_of_size((char *)cpioDest, fs); } -void load_content(char *cpioDest, char *filename) +char *find_content_addr(char *cpioDest, char *filename) { int flag = 0; while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) @@ -113,7 +116,16 @@ void load_content(char *cpioDest, char *filename) } // No hit if (flag == 0) - printf("cat: %s: No such file\n", filename); + { + 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); @@ -123,6 +135,17 @@ void load_content(char *cpioDest, char *filename) else cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); - // print content - uart_send_string_of_size((char *)cpioDest, fs); + 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/shell.c b/Lab3/src/kernel/shell.c index 26fa08e3a..67a5641e9 100644 --- a/Lab3/src/kernel/shell.c +++ b/Lab3/src/kernel/shell.c @@ -20,6 +20,7 @@ void shell_main(char *command) uart_send_string("ls\t:\n"); uart_send_string("cat\t:\n"); uart_send_string("dts\t:\n"); + uart_send_string("svc\t:\n"); } else if (!strcmp(command, "hello")) { @@ -106,6 +107,31 @@ void shell_main(char *command) { fdt_traverse(dtb_parser, _dtb_ptr); } + else if (!strcmp(command, "svc")) + { + // char *cpioDest = (char *)0x8000000; + char *userDest = (char *)0x200000; + char *cpioUserPgmDest = find_content_addr(cpioDest, "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, 0x300000;" + "msr sp_el0, x0;" + "eret;"); + } } void shell_start() diff --git a/Lab3/src/userprogram/link.ld b/Lab3/src/userprogram/link.ld index 7d7e655a2..28c90843a 100644 --- a/Lab3/src/userprogram/link.ld +++ b/Lab3/src/userprogram/link.ld @@ -1,6 +1,6 @@ SECTIONS { - . = 0x14000000; + . = 0x200000; .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } PROVIDE(_data = .); diff --git a/Lab3/src/userprogram/user.S b/Lab3/src/userprogram/user.S index 838323763..8a5ea0c0b 100644 --- a/Lab3/src/userprogram/user.S +++ b/Lab3/src/userprogram/user.S @@ -1,4 +1,4 @@ -.section ".text" +.section ".text.boot" .global _start _start: mov x0, 0 diff --git a/Lab3/userprogram.img b/Lab3/userprogram.img index ee96ea529eb541c6fb882e18209e50d20f72e4e9..83efd9631fdcd22cd665917b5808106cd758bd29 100755 GIT binary patch delta 295 zcmbQ?F~dWGfuZ3N0}I1MMh1o}@*)f$SO5PX!oa{FvLTj}v0!r?XTKohhsp1xCNm~X zZjuI(VlweSkuo5eF!`FyV#W)bt7OkHGOA7Xls9G+pIi$hc_wd^muFyr2@phYykk}X>6AO delta 273 zcmbQ?F~eh{L@Xzxz~(Z}enG|wli8#vGcrt`1t#@m;(;Q4K$2ndJDJ6d1)C?yo?~Rx znVcwZ%qTl~Dv%VKd{SPXk!$ly`F2Le6_X1Uo$IGEFic@&Xb5T$YPiJkWcjCwK>h(B zdooyjMi7HAM7$d+-V0R^6K@6b6+!wLx-KaI^*6%gc_92+D8GRN!mkAK6G7@Z!F;1n or7#U#5Cw%m{z1itAO>CtKNqTj0cv38=2WF>#t9OWA1GS@0Jl_RQ~&?~ From 265394069cccdf5789247f2bf8f16f84af485e1a Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Wed, 29 Mar 2023 15:02:46 +0800 Subject: [PATCH 04/22] timer work in el0, qemu ok --- Lab3/Makefile | 2 +- Lab3/include/stdlib.h | 1 + Lab3/include/timer.h | 6 ++++ Lab3/kernel8.img | Bin 24512 -> 24728 bytes Lab3/src/kernel/boot.S | 4 +-- Lab3/src/kernel/exception.S | 53 ++++++++++++++++++------------------ Lab3/src/kernel/exception.c | 7 ++++- Lab3/src/kernel/shell.c | 30 ++++++++++++++++++-- Lab3/src/kernel/timer.S | 16 +++++++++++ Lab3/src/kernel/timer.c | 39 ++++++++++++++++++++++++++ 10 files changed, 125 insertions(+), 33 deletions(-) create mode 100644 Lab3/include/timer.h create mode 100644 Lab3/src/kernel/timer.S create mode 100644 Lab3/src/kernel/timer.c diff --git a/Lab3/Makefile b/Lab3/Makefile index 51292302e..eccabac02 100644 --- a/Lab3/Makefile +++ b/Lab3/Makefile @@ -35,7 +35,7 @@ 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 + 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)) diff --git a/Lab3/include/stdlib.h b/Lab3/include/stdlib.h index 6d9004dae..5f7d76b91 100644 --- a/Lab3/include/stdlib.h +++ b/Lab3/include/stdlib.h @@ -4,6 +4,7 @@ #include #include #include "printf.h" +#include "mini_uart.h" int strcmp(const char *str1, const char *str2); int strlen(const char *str); diff --git a/Lab3/include/timer.h b/Lab3/include/timer.h new file mode 100644 index 000000000..e9f05bff5 --- /dev/null +++ b/Lab3/include/timer.h @@ -0,0 +1,6 @@ +#ifndef _TIMER_H +#define _TIMER_H + +void get_current_time(); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img index 26a4f301c47a6d528658a3427c1105d2d25e9baa..db4c0ac362fb82ab7b3660da3267d460d7ba75ad 100755 GIT binary patch delta 3994 zcmai14R9036@IIee4cJ=XqgU^rY&TeMw1xz?cJ@U zGMTAnPW!&M-}~8pyL)@F^W@nJWMp-PMJgQ5kqT=7-YhAD&@BgqlG0ay7NFa{9P|fr z!T1^N^nvp~nwEgd0B^w(B{6JQdkH{PV3##5#}7dh$xY*7KyH2-y9zVp(zM)-yc!7a zB^3}%Io6G<(j=FhA1zzz4{C28zx$&fv1`8WhkXTG#jcc$Efd(znD z&yZJT$ZH@9n8?0t2)O|EKo0cbtc`)j;*n|@?;jzQQ>Kg=Y~ zRibkQ5qW@tN^z=%_g!wwkq}XYivBbeAat2knYy$V-TpP+tFNWRuZe(boL{a zzs6oYvRY3cz#d%=*VCSS_RX9x^u_9_=2Q~@!5&_PFfpn?V4kkOZR#~J^@^c?-_*yE zA0JernnV`BKG!+`l6QpFd4^Joc@2F-yJ%`kk&G{k+>$5SCNUk@~4>ci$TAhMx< z)6{F(A^}7Hx~Z3iUN!XRO?@zsWg5hcj;IJ=6@eG%fg>V-Bmx-vpNIfLuNwN3raoXX z`I@0WF8muaIA@H=3-tici2$i7H1ywN$8FB}UM$ZItaTiVV3cMdjpae8Dm8Bg`)SU0 z%=-*Bt_EAVqx#++xq-gGrfmVfat^ZryJ8JA(EX7g+C9V(YoM>nYA>!aexBf#TSV*g z*qyvXJ%PpaQOtLSSgzs4bSq+;z_G7JmG_OodI*`VA=7huOC zK~x%P9pd={tBrIH;#z^VMmifY9=hf}SwbrjR|_0oLdy`(5?EeJixE2n4lktzh>J|z z3eZIDs7Q?@&_uHlr$^F6Er?Si!G!O!nt}!^G}Ak5ZGpcJzQC(vw_=ONaOKM7TVK?dNrZ+(w`0N?cc*@Ox7p$5!tTw9!$nn`!i4 zGUeArf4GevVXw@TgRwTsN$=Z@6QbML4JFDR;+^ z_W_F+H7>u1l3p`2yEw9%(DQDdz~*i*zWeuFz+;EYFCiDKYHt)lS$AAv1NtWQLZ(zDwBP6s&C4s?irwXZb&>y3tdUPJgcNXt|8@c+sNE9*F zK~ErOHgaH}Itfa}6b~qyEp%5*Y0~p=mx%-wpI)m^utwYuu>_%Svqi7|2&^fj5~!0B z8f!R0Sb~=~?nn+#az~(bOaZ`QxZ#}oI#z`{o<4Bt#hA1i+gHS850|`;9>k=Qjaao6 z>8cf>j*lE-KKca8oAA-t<&uT-mvIT1*X|}qGc95Z<#Hvfni|dgMSjSp!bz1Sd z?d<;2Ef%|-oho%%H6Lwa<~W|ogn@Pm z{!#9-DxK)BsKV*slXma8KVsb#gJc6cQ?ZO(VYe#WWSq^I{sSFM? zN2P2HchVu|t?Y2@_zb68*|#c(N|i2pUk=`~aGvyWsa>>_6*>1-j`2%B?5Zk8^#Yy{ zl#EV-cJfX=JLYuFsK#rc&{VO@PM4G4G;wDkSBbiS?Cidq>_c1bhe=hJf-mbU%#Ewi zf)BaTF{R-17Z)UMI3W=)$1PNXtqZpbzcJ*;zOT(hj1zB6co#j-FVwitrw{z*e^#x| z%Yy_SRg*Qiy19*(XZk?D`i-+kzYYNXY0AlKrW4~kXLBxu&V8I5)lo(!Uh|%?{Hr|@Y#%3fb(rSZn`sD7}yO$CE^P;YA?(%aG7ZU zro-aM+OGS^LvP`=nDX}uuL1B+MZ`qMO0XORiz)2yTZ7moV-$!lzbf2H*dzTqNc{z4WHoPWCn($wZkcaZ5qy8Sd z%$F2Cy|>wy{41Hae_`a&{x_^ya)*3>H>=(FT+Yyr$F|wp?d<%<2`iV`cQ*a0uXfi~ z`SEQJ5ANRjxID0ZXxmfX6^F-u_sI8CV@N0Va7ss6*NK3;frU`r0SX#Kq(NboD#kSfq5ZL;MMJB_g7=+!?unyn zmhAKWe&_c+&huWMyT%e%S@_zrTSp|d^ z16$6%jL5Gou~%knp|hMk(AzM%*bo>jJ1i)WNAg!kz2xWc46 zO|}c5%o>qViy_XOqb^v9S$voCCVPc;pWMwhJ(bNDvcFUHL+Z5xnRN)% zW7%*gF68k?a(0YpccNLXrzWXKtU{E@RbWBMU$S`#g_?sQ7XQYbKc?J4RH3 z#VM5hc0S>$uAisgk7=!wv zL9uhT@IASDUZMfpD6(I3&hfBb7E9!=U|wwv{z)^2X}ig*CAtR9Tt1(7dP`z01}JEO z+PTn(D+qfO)*7*raF)V)BYFvo@YXx%!od)+Kj69PJ{} zFA5R%lhoE*0;3&A1xZ^>2!3qak5g;)C)#m{f4EEwneAvU(^HpoUrt_A9Z` zfg7bA2I$0IWgDH?rR-=Yb|^d1iLJ^uJF$uE>vFVV2J5YSVLM-FLLQMYXzQ_#gi}fw zl)}eVVM=GL$7<3AAv*jI2Sh;WO{xEpt-q%9GjuzHo>~BiOpcc@&4^$uwx{ttB*nCq{=B{q`Fj2d69*|=)!5@ z(uW{!J_n28_7L|HFDPm1J42SJVlgtQd=z<96tKI$n;z}QuF=0?eWeh(|t-z=F z$&&loK7PH#d&n9?>I*7e+~fgy4}~Ei$DDM+B`eF@1F26J(&MXD-KO;W%=YW2`w?8x zS+fnqg=AIbpg9vZgKjjPj!XOb&eFN_F(9wok%z8dBEz38LE2(L1{Nwyq7FW%sR!+$I61^U~KjE8onT`PTAU*1{j6-#hr(@>e~!_LiIfb$RumE&kjsm+IOV z=@D~>9v90dj!804E;VQy@J0?q#5NISnAJdED(3{VreTi1R8h)i_*)f6bK0o9g;Q_w z_o}>nsM5!t=SM1Q*+q`@8{vPbTrA1_8dBEo{s9i?uYBM$bUZ20Z ziWu7Zq!nXihx)%tk60n!9N)d_Hp$bn7wmAtxk9hV3Zarp5Z{JOu#RUuBG>E xfY{9F+b;gXt}xdhoZ%I_Z*V= 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 +el0_core_timer_handler: + save_all + mrs x0, cntpct_el0 + mrs x1, cntfrq_el0 + bl el0_timer_handler + load_all + eret \ No newline at end of file diff --git a/Lab3/src/kernel/exception.c b/Lab3/src/kernel/exception.c index f7687ccf6..b6fa3982c 100644 --- a/Lab3/src/kernel/exception.c +++ b/Lab3/src/kernel/exception.c @@ -1,4 +1,4 @@ -#include "mini_uart.h" +#include "stdlib.h" /** * common exception handler @@ -107,5 +107,10 @@ void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsig 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; } \ No newline at end of file diff --git a/Lab3/src/kernel/shell.c b/Lab3/src/kernel/shell.c index 67a5641e9..5adfad583 100644 --- a/Lab3/src/kernel/shell.c +++ b/Lab3/src/kernel/shell.c @@ -3,6 +3,7 @@ #include "reboot.h" #include "read_cpio.h" #include "device_tree.h" +#include "timer.h" extern void *_dtb_ptr; extern char *cpioDest; @@ -10,6 +11,8 @@ extern char *cpioDest; #define COMMAND_BUFFER 20 #define FILENAME_BUFFER 20 +void shell_start(); + void shell_main(char *command) { if (!strcmp(command, "help")) @@ -21,6 +24,7 @@ void shell_main(char *command) uart_send_string("cat\t:\n"); uart_send_string("dts\t:\n"); uart_send_string("svc\t:\n"); + uart_send_string("time\t:\n"); } else if (!strcmp(command, "hello")) { @@ -128,21 +132,41 @@ void shell_main(char *command) "msr spsr_el1, x0;" "mov x0, 0x200000;" "msr elr_el1, x0;" - "mov x0, 0x300000;" + "mov x0, 0x200000;" "msr sp_el0, x0;" "eret;"); } + else if (!strcmp(command, "time")) + { + get_current_time(); + asm volatile( + "mov x3, 0x0;" // use x3 instead of x0 because x0 is reserved automatically to store the address of "handling_info" + "msr spsr_el1, x3;" + "mov x0, %0;" + "add x0, x0, 12;" + "msr elr_el1, x0;" + "mov x0, 0x1000000;" + "msr sp_el0, x0;" + "mrs x0, cntfrq_el0;" + "add x0, x0, x0;" + "msr cntp_tval_el0, x0;" + "bl core_timer_enable;" + "eret;" + : + : "r"(shell_start) + :); + } } void shell_start() { + uart_send_string("Starting shell...\n"); char c; int i = 0; - char command[COMMAND_BUFFER]; + char command[COMMAND_BUFFER]; memset(command, '\0', COMMAND_BUFFER); - uart_send_string("Starting shell...\n"); uart_send_string("# "); while (1) 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..0cd6a1546 --- /dev/null +++ b/Lab3/src/kernel/timer.c @@ -0,0 +1,39 @@ +#include "stdlib.h" + +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 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; +} \ No newline at end of file From 87c3e70ef3db92cc3d63cce81375bbd759ef5403 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 30 Mar 2023 14:29:49 +0800 Subject: [PATCH 05/22] mini_uart interrupt ok --- .vscode/settings.json | 3 +- Lab3/Makefile | 5 ++- Lab3/bootloader.img | Bin 10032 -> 10768 bytes Lab3/include/mini_uart.h | 5 +++ Lab3/include/peripherals/irq.h | 27 +++++++++++++ Lab3/include/stdlib.h | 2 + Lab3/kernel8.img | Bin 24728 -> 23424 bytes Lab3/src/kernel/exception.S | 69 ++++++++++++++++++++------------- Lab3/src/kernel/exception.c | 30 ++++++++++++++ Lab3/src/kernel/shell.c | 42 +++++++++++++------- Lab3/src/lib/mini_uart.c | 62 ++++++++++++++++++++++++++++- Lab3/src/lib/string.c | 31 ++++++++++++++- Lab3/userprogram.img | Bin 9240 -> 10008 bytes 13 files changed, 230 insertions(+), 46 deletions(-) create mode 100644 Lab3/include/peripherals/irq.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 151c99719..7f7d63f12 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "stdlib.h": "c", "mini_uart.h": "c", "gpio.h": "c", - "base.h": "c" + "base.h": "c", + "stddef.h": "c" } } \ No newline at end of file diff --git a/Lab3/Makefile b/Lab3/Makefile index eccabac02..27d9a1fd4 100644 --- a/Lab3/Makefile +++ b/Lab3/Makefile @@ -52,4 +52,7 @@ 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 newline at end of file + 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_inter: + 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/bootloader.img b/Lab3/bootloader.img index cb02047a14cca35d818fba9cc0bc369c387f22a4..5673d87c8fdad1bdce04844c475fbcbc58548ecd 100755 GIT binary patch delta 1204 zcmaJ=T}TvB6#nkcmOr_SZ7f1V0W4FP&=EXQ$ zei(NJywa$*3D_b#D1JoLoce3jD={zd=A-12b@d4HmOvTL8{1(## zYyEtljfBR%#;{}N0p1&!58kH?f68iSRONjGzo>*$jli!VAPE@RIwH2qbjPlSvrWW` z?l9*X7~`+mNTuqNu^+Ooqx zuX1t7!$!G_D+mvX`xO!&$#3xsJYN~%In78QB8uzmjwmp zc$JY5Q_!+A_}TB|*NYFODzED@6)`+dpzlEz_H_Om;o_jO*r+Ote$88|2?TLmU`8DJ zO`WU5sm{g@T+NZU;;#4?M(1A4D(9A{cUm_03nbBMzw?fW{P+pQIR$Zv^;VOXY4wWn zJz*b9gjuD+h;NcO#Dd3)r6ST&g)71%d_@Gye#2v}CZJn16|Dssjc|knoTv9*Vww^( zWn@_Glmh?W|F;VaN#U|DelktX{K5b=60vVx&jwObZ?6E|Yy3O;{A9C)!MIWprTbaGrdeF_$+5YB9qC>506P9hr`^ zlmJC0FJKhetiZfOc5;B?8%DLswo1l~;*$%NjG1^C!G^JHKC0Bt$h3%IvY(1meLn-k z6h?-Ipa!9aOAJ68yMg=zKz1)!d`1w1Fhsl+D&7uN4->Bk@)be)86YmMhRO3l_~lT3 z0|$g(4CE(*)N_LQMxXLw8lYaz2J#OoHUu&7LNug9H84O8OrGqhs=xV!x+tR>gUmz* o28W*y_!)izBVYmpkk1I_JM09;5YQ)JvB}JucNGlOfjm3_0Qg&x_5c6? diff --git a/Lab3/include/mini_uart.h b/Lab3/include/mini_uart.h index a73b446bc..12ec374b1 100644 --- a/Lab3/include/mini_uart.h +++ b/Lab3/include/mini_uart.h @@ -9,4 +9,9 @@ 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/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/stdlib.h b/Lab3/include/stdlib.h index 5f7d76b91..9d7802115 100644 --- a/Lab3/include/stdlib.h +++ b/Lab3/include/stdlib.h @@ -4,10 +4,12 @@ #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); void *memset(void *dest, register int val, int len); int memcmp(void *s1, void *s2, int n); diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img index db4c0ac362fb82ab7b3660da3267d460d7ba75ad..7141889725cd086eddf490425b1621a4a89ba4d2 100755 GIT binary patch delta 5258 zcmZ`-3vg7`8UF9x&1-k_-n=*2n}kP*5f~nlATc+AC|Z=5Qi+1FOIRcV2}u^v24^o| zwNqQ!!-<6sRyT!?nre;4*G|WADXq5Cv6WaD9b0>Y+G3s28m+S0kpAbKd$LPLGn2Xh z_y6DjIRAMj$4)86i%R&$;qxcIUIlNnk@r+mH_8M|>v- zUU_2D21ElGfrne;2OLJb0uYthYfs4IhhUMCp1=nHxvm8E<|N7Pgxrk0N{I9+#Soef z%$+?6l2=QTXC-9D=(kMDrjsm@GV@!lN7r^r1ro26vd=|orOb$H^ysY;Bd;1| zJAj*yDCQzw#9qyIc5>hp4W$&)N?W~M&m{NL+imZt4Ow)#{jfcjNj|5q+iUW)ETUz? z;CPW9=l)N&(zKKa(Vo(Fee*G$d_U{_=Mqe%Mh{4#6thEr8jLdfzV&D^qSObc|5^+ zOYc)Hz9IB~lkr6dt45TVFr1SKrdufVZ_uHPvUx{v9OrSa$8i$G%;b}=oiON`?pr=j z`wC+h9QXz{E{2Svg1s{>v3&9mI+@|K+aE*qg-+O+~l{K z(T>YaoHVAJY@wez?_C9j1Ww$p*yJBf)~#yJ%I`6)BNY@*Nj#&$@1Dtd!Iz z$I!~ilH?dh8CjSd!z?3pF8b%J>q5GRl%OR`mS=cK0b+;5F%QW_oGLMtlT5@a;^|)M z}) zpdysVcB2p1K3gsMI4Vi_IVX6zogF*xqTR{tE@KSf@rj3$>_g};^@mw9TxIMRPgJXz zD8H4>Y+$=6toSMj8-UFj5|t@p*N4HVATQuZ7zw~jMx|_c$%vFeBTq>=qLE=K`zpy} zQjS%UlTtRS$dHuHD)NYwjcRf{gdi^FHmqo~cvbH@L{V&1ybygBA;|kg@(jWItHm3a zyvQtaKk|GKX)my`_VO(fR$w-d`v;4EO7fptQdxQnn-K>naQc6ZL)Hh!~3<=^7G^30DEN2 zTr5@(8?lNbX1>PpyVfl=su?-3#V@4<*JreQD}wGP}fzAU*ceA$L)3 zD{29!lg*j=wBFE6|LE>iYUq^(nq99UZS>}XvXFrVnk+dxuigT}o}2?r*JSDHg>EnE z*dSOyCku#Me%uDNI4|rrbHYnXZkJ`ZF?*994On;z8^Ysvh*+H%YZKaHDaNw)i_&

H0iA(Z}fD3q9#R1UB37VX73ZR2@!w zZBdo7hjtZJD;Mccie8fB70Kkqw6|DW8kt8frNJOuKP;J92Kb9f?^1X-p2oy8J2dQ| zuNHTw?Zf&e2YyfgQd&UUN<6CJpxq@^%14xxRIA+%`a;Qz8CH?crqg!|%Vd>L(4Up! z30_NoQ`%Tq{tXF=9W<+~KQB^CTClWnY$hMOSxaeI@VYSja|m{N@=%F0fRFwBqhnw` zHW9F%YB%80`+91sh#lPHI78qT_6=y4+}kqgO`eLfc2voU^n$XZ`O?%np6lV((fd8A zimF{f50~prc-UM)e$D=%;FrPa%WkH7(qZtPi&ICr01#G<68hm}$UPT-;34 z*J+6S>z;p@#|AHL|^Jg|Lvp7sf9^cD2Xe(|5TLYMn zu$%X@?23yRmmXU2f%;!Ny|ZzV`cFH3q;XNzJ9d7e@GG?$EBVUmHl&L?`Az%iMB{ym z+H0rZZ)yo`LOMI>jXb<@@yfmpaSY{+Tz{+`@`Zk#rN71EOZ`oVE15rt@Ha@iOWA(Yxw8o#uKvI4bWwv}dhWU89L$sL<+$!t~yu1}i6gGkQvcfRlgBXLY zf&)`CiwGq>zN~=nV}SQ$!kqa8mrxW(m5Yf-DiK#<0b4wf$1#o)aFkWswR&D)g8$83 zz*|*GD*m;QczTQgI^s1MWkF=?nv#NI_0buvo9-NFtSiD7utTiiCV(Ba>;=iD6YZA` z0x=r|8^HQXY!4d&nSNpIm76Y(?E?YAgXB%s?cu9^P)r*zJ5%ATq z)O_pmQV?UQi1lbCzyk>#Zr7x3iFz2l(A=TgZ7R5F+x7eH?^W*h1G8L!;k5u)i=Ktcy^6MVq^tFxS*%s+&QFBcQHTG{z`?!Iu_h&??d<9gYF*vidir+xgIzt{ntw~r zo}jj^YiFPzpB?O{(;v)Ou66e8zFQ0UJGX1sUcaVQ3-)OJL4WsFf8SOO9d_>tT5N!= z!G7fPz*_@*yE+5Gz5w3S(7(46^}zrpL=lq)1EIdYJ-bDUgf&mBq zm54vIY17-8JSYP0G+MBErLtK5w=iP;*YMsH@n6FnoA@r=D86CKer#hp&21_FKes|# A^8f$< delta 4073 zcmbtXdvH`&8UOCxN3xp`@_ucSy-9!sNRyBkyLsNUK{86mbio2@VFR?&COtczRfPq*n_C61^ug1 zIYiP8bLXmz$gkM!c^RAOxN~EMlOzn>bQf;TV1I|r4rJ^@5V0ET&5*n?^)5V=!Tw>J zy~1XXKpe0m+uacL18jvXScS7T0ct&&$%CkvK`2(^O>;S(!1(p#1G91h^m_6iyKtTw zuMUheaLyZ^P{H_W=bso85_ZwE@!(N;4FG84@M=sfgE7UmH zbbrD+CqOYDoid-CK};XdFQ?-!7p-*!ocesSfD-LQvydj;mVvoRyW)ep164+=aUu45m98P&>%w9C{&dc|`BvNz4=7Gdjp`Q0Wy|=_Dr+y9Ew+l9v$6h|_ba zcar1C8Aa+ed>p>adcy2?u;g=Xbdn>C!^OkuSx=Zfg=K^blrHi-BXMOh!JjSrJeIsZ z+(iyDZmwB>*0Ns{^?DaMfHv#U%+5a?O*q!SIkPj)uRdcg@YO0Mx(M@s-)kNpgXU(5 zjDyidG_#>`2rnhOgl#M(JA@q$kgp3{3y?8k>jAP=*ogqyB5Wf-HlzJ7bJoTs2=d_P ztl)>t4K@x+BuGY4_>K^UP2sDSFe!vukgP^s81#_C0Z|Zo!_@!E(x0|=F7)C|fjV6z zLG%o2H{d;sGl?DP<{kOBrLUzg7qpfu-DJ`O(HBsNrz3erX0HS-^g5j>c;vGaqOBxW zt;F^Yh}c$JVGb7AXgJe`>R3TnZrh2mH;e1cXNt&SW*bRm^0V<|s}B_CYE9NZGDV>d*tbwfat zIT=X@9OF?t)akcpQV09RKn&Bb-trs8a~GWYq|t6J9X0_nP9~25J;ZRxpE~vp?5@!Q z(?*LrjlpiEQ>ylmpJPJ}*mx3eHdg;8YWbBVvYF^1hfz~QgT?+xspy8<%X4!FQ^C{{ z9^30>p6hWK$tMobCLL<|l!3#5R(#Z<4T?SqgIF3pQvk3J-t|pAjdO1}p2r<<*4hcb z6^}q%;JsuQMozV0Ztml`q5cwAK>>1Xc#!BNUqgEx?JQ|oxD+j>x-y$OXsP+aU_E3l z2#4X6f8)O_Z3Tn*OKmrFKEURqJAIE`M?M*B*AU2F&7w8i;W|0@voK0C{} z0gDTm9NPdZ@y8mx;i3CWOCS4;1>eKy#()}MZs8=u?BrNux*kVf#b3guh29o*+s(S8 z$OrMKVA#v7j-4ncB_c77kH3%1dT3r*EZgYg+1No}FRRLie$2Y#*j9R@>^}KHFKsKY zlUCEumDfu{^y%`GLR@1Pm(YhQ6j|-3k&5a{Hf48@`vb-;rn9~cp5$UR&ib;J)>bZ+ z&w1$QE9<25bXR4){A&+ATzNu7h|*DCx;y*mH+_n%_tPW3zS2iOA;KmP{m8eiP#qxm zX5qsNCyftM8=#S@G2aCHB!I`O3XxpIItN7uC&4&!yJ0TPtFFnb##f@Cs^-$}>KY$= ziQ+awwHo(>w0UqgwF((UNdBYeuza(de!DkG zjlS#hl$&1YZZvkYP^Rg6ejl*+JM2R69^?;MtK8 zuYyOcgul=&+8=r + int strcmp(const char *str1, const char *str2) { const unsigned char *s1 = (const unsigned char *)str1; @@ -28,4 +30,31 @@ int strlen(const char *str) return len; len++; } -} \ No newline at end of file +} + +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; +} diff --git a/Lab3/userprogram.img b/Lab3/userprogram.img index 83efd9631fdcd22cd665917b5808106cd758bd29..71bde78f0186c1817f7e3b0a6bacc9393fea509e 100755 GIT binary patch delta 1056 zcmZuwKWGzi6#jm>O06~ot6gl`%cZE$#i9=B)U#c5Q49e`jiLmc+DS;^P8|e=nt1*R zE{asDh=WvzfMPp36$5oOr8+no7jH=8_kQ;~ZKHwQz3<)ceeZkUyQ`h3ovJLTyRV1vo<%SCGgya;U@905Q>gAb^g-2qZ zBiBrEvr{m<6U~$xvR`%CI`{c!YUCGrgYw8X^)4vdN7bv~KCe%MkG8EIEq%9dszgo& zv(A)vK;fQ(pZ5p+2EHz~9O7lhqdH&9V{eonGWlV1KF$vkJ5rBZjSTDe#LxR9oHW?q z8R0(SqMYy3_(*FnYj(z3i)-GnCeed4f)zR3IXB3Z&gbl7m|3UA za#sHB^2&3um%TmRJTA~gbJO8l62aiE2TIMDQeVWh^0I2-nHy+lC;PA!LBF4(*W% zWKY^=<3fqTlKh;us=XMCC4mUq_Rf&!ve+E$cj-=czdGm-Nn`zKY9YiFO6=Ah;I_#H WtD`5!8SCHpp^U!Q%stBfd;JaF!DmeX delta 279 zcmbQ?H^XDX62_8^OV7(rek1pWQEl=~d1FTL$-D~2j69Pa733LNHYX~yGcqk=nEXo7 zslK0qVG1KdLr{ZI!zG3%%RhAk`3HdPUaNGcq>%A9jYECUJv9eg7hKfDsyPTuWMFXk`GB9{Cj$e+1O_0V5zKeksmQ Date: Thu, 30 Mar 2023 21:26:24 +0800 Subject: [PATCH 06/22] adv1 ok on qemu --- .vscode/settings.json | 6 +- Lab3/Makefile | 2 +- Lab3/bootloader.img | Bin 10768 -> 10920 bytes Lab3/include/stdlib.h | 1 + Lab3/include/timer.h | 13 ++++- Lab3/kernel8.img | Bin 23424 -> 25624 bytes Lab3/src/kernel/boot.S | 4 ++ Lab3/src/kernel/exception.c | 18 +++--- Lab3/src/kernel/shell.c | 49 ++++++++++++++-- Lab3/src/kernel/timer.c | 111 ++++++++++++++++++++++++++++++++++++ Lab3/src/lib/mini_uart.c | 4 +- Lab3/src/lib/queue.c | 7 +++ Lab3/src/lib/string.c | 19 ++++++ Lab3/userprogram.img | Bin 10008 -> 10152 bytes 14 files changed, 216 insertions(+), 18 deletions(-) create mode 100644 Lab3/src/lib/queue.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f7d63f12..e6152c4ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,10 @@ "mini_uart.h": "c", "gpio.h": "c", "base.h": "c", - "stddef.h": "c" + "stddef.h": "c", + "deque": "c", + "forward_list": "c", + "list": "c", + "vector": "c" } } \ No newline at end of file diff --git a/Lab3/Makefile b/Lab3/Makefile index 27d9a1fd4..5db4cff0d 100644 --- a/Lab3/Makefile +++ b/Lab3/Makefile @@ -54,5 +54,5 @@ pseudoTTY: 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_inter: +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/bootloader.img b/Lab3/bootloader.img index 5673d87c8fdad1bdce04844c475fbcbc58548ecd..69042bf675d797e02767f912738a24a4f41f1a80 100755 GIT binary patch delta 1159 zcmZ`&OH30{6g_WBA+S+i2Cq*nb88Cw?T5BJS1GSEkhYt`B&$V`%On&QV!{#hIca`|~Tzu?yA z^K*oKvLCr&wETHl4o|ySE6U+X!lSaj*uf{ZC-M#MZQLUK#;3(mo@==7HozquCoD!< zYQ?2M^lW#`+Wu`nbqu%yafk1D9KBCAVJ>eE5VkLPw&bPI29uzkAHbqgfI*2_cTB7W zT4r9o$a-84n^3PDgYG7^meZu5xsba%itI@vRew7q~rZ z+X?ANezQ|=SJGp&x9rqw#{^u$Lqza7FSs7j0dA1(DiTim2>Z#v7`=H8rm-;1g@0V_ zbPVp){~hyvYg06vABfHs;*+&>1s12?o?X3U(hz2#z6Yk9G*<|kTAJtqu0<3i93eXY z#~5L0%rV7d;%2b6<0FZd(;Lrr-PUH_2-Qomdu1A}j`l8h#AB(K!B*C>6qekU+=`PY zOkN0c!v{Rt$wB5U`#$tkk=oB@DB3U!VcA|7b_0wmQqOp~VR~2u6U{>aE5)RD=o4M9 z*`OJjLT;%JBk$rsDbg4;sn=|v$?3{X;@Dd!_c)rh6DILWW#_LqhhIJwq9Gh8Aa@Wv VymTxbDUvZlZMo+{n*;y*^9O-@W^Dif delta 964 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 fyhZ)E_%{XWIqt%8|h1=Lz&#n}3oiO$|o z#g4X+<5>$+ZCzShF;xgg)SaorMjdrp>sTz*M~B{}wgv3?plVq(ZohNxJ=ujZGnw;$ z|NH#sKaagTU)Rj%wep=r&wgG&t6y+Y#!VD@n3dOSY1R>GNWAMR+wf0USrgG8e-fhj z@k)wk-8Vh+ND;+9VYgouBmC)Wy<)(hJx&fCcyiKBOeY$nN4qn_KC@3FN-7-j!o}!}x)~aX`V1<#qR$=H(1MNYp$r+^rEKX8SI7WIRREWxZ`%C7DgREfnzfWIIIIFpRm?|xqBi=vfY_4Av2 zh9|`K`^WeA+O<%Ky`-Cc=sEl3vO;X5pP%;MyR3>$UBXd=gb`Ux2?9cs5qWegwgJO3|u9yHR|$!g@6u0-m9;SVWXCP7r0#E3lg<#6Btu7p*q$NoSR7sQ2S>~7 zt*2vWQ1Jr?^$(sn`5W-yw_>&2lh^^GRVpznrSJ=>5UEh-%n`^%FH^ugNVNOp!+G*u zuvh%;A{Y|k0e4Ld*^g+^Mkca#iwto9X!brIA+wP^2cFO&im-7-n-MmqXwuo!icaWk zRMF8U_JpESP3&<+o6T&OqOIl_`=tWrH1-JSjJ=K{nm%17b;M^UC1xNKF{U#Hy^oci zY3u#P(Mv16#B_E)^rDpLJI5_bDf?waqZ!ig>ry{PgA>Z&DO^5eWM*g-e+SANab3&m z=(a%w&O~*ZaBRwT4iJNWN5r&!NM4!4gI+G-8SH~%ioXn>xMtWEmw4oueHkQR#z0t` zDJeFVwb@Lju?z_MptzHo%IHcL0_b-eJPpI<8})VzeDYAau*uh#i6CftGP_Fhp}z$B zvSQA}gpcbnIV2WR6N`zgw>qVpurH7iO3Yv#m~D0dL4HkB8UG1-4d7e-T8Gs?(IKkw zU={)UJ3AsRG&qd&+Zw@bH z!2cHGfRPk|F8>OFKx6h-T1f0jHd`%aD`@a$$6n|~WfDdbOw z_h9jBzPP;GCZW@(1(Vf{hncdN=%1AS1LtWt5ZNTm! zPvW38pTts*5Xz5bl(eQp*X&N6DZ|_B}Rlh(8~i9W%tS^n?pp}2mk-LR_iIKeC!C5Sy^tIad zwF$n~LzXHq`%Wp@UAAnUpQgpHflx=#PlXVH;G9JSPqec>n}vORVeLNOh-S=|J-3tp zw4u(Adj!z!9^GEd>*~5(K0jYy*X+8zm~X*w%Ey0Ow?AjI=uDgW#r%Q#h9aCjv)R{0 zd{2E{)m}uzCb7%#j-B49j~}VO#dT*fUm3p8W%~HmaI@<}FCWEli;uq<-j6h#te)}m zZ4I?;Q)xZvwYz-cbF3%IBi1mDj!ygS%}LbCeptj0H!Q4a_=eCm;PG2)Lwr%=+G^z4 z%DV8|HVsFzELJO9jJz8M>qo>-S-Lq~0Y26TjZwMdAX!hGygtYmMjA`j;%%u|Yw#l` z(pcXImRijSX#<^KXYI4+(eClbB1Ky8Ty{YG(zBlre8;qljC0wZDbCAhJSgtLJnuP` z-67LD`|x_aJ7b;B?iilr@6G(V_R#q4v)=N$jEl!Fox82vmAaV!weum@+(mq#OAgtt z^@7>IbiH66So*8V#A0>=_SSZ+%XT9t7!J9ESHj z{6u%7>ut}t?~<*WHptnfOJnOj6c@LjS%F6iUO8)kQ=r#L{?0zCwE3$X{?(4Y;;#g5 z682=jl9nrZz{Zz2*!0@C3wVRjw_jEBPA*mkK3icS@I;f@M?o83=opaWa2~&}r>kd- zc6Vn^;^dLB9p7Q;>hV76`k7qc8sO{|?n7dkNW2PR^DJy!xUvM+cH_tBWz%BzM-PXC zHwQGwx-M25d@@F7}!7&o-Y$ZnpA z-6_8BvdY*8u=t0HP2@mO7n~-GD@V2GDzbwLP-a}}&$Zil@9K~% zCCKG`)wQc#?f(^A*UcY~FJAM9LT+q0Hh$~I77brNsEl{649!>H&l1jew0jHe?`UOi Sdz`z@9!DJc&UIecHT)k=@F9W# diff --git a/Lab3/src/kernel/boot.S b/Lab3/src/kernel/boot.S index db0ba1112..0735567bf 100644 --- a/Lab3/src/kernel/boot.S +++ b/Lab3/src/kernel/boot.S @@ -26,6 +26,10 @@ proc_hang: b proc_hang master: + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + ldr x1, =_start // get CurrentEL diff --git a/Lab3/src/kernel/exception.c b/Lab3/src/kernel/exception.c index 67c8cdbc2..cc1c2fda6 100644 --- a/Lab3/src/kernel/exception.c +++ b/Lab3/src/kernel/exception.c @@ -1,6 +1,7 @@ #include "peripherals/mini_uart.h" #include "peripherals/irq.h" #include "stdlib.h" +#include "timer.h" /** * common exception handler @@ -119,8 +120,11 @@ void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsig 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 (get32(IRQ_BASIC_PENDING & (1 << 19))) + if (irq_basic_pending) { if (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte { @@ -134,12 +138,12 @@ void el1_irq_interrupt_handler() // ARM Core Timer Interrupt else if (get32(CORE0_INTR_SRC) & (1 << 1)) { - // arm_core_timer_intr_handler(); - } - // ARM Local Timer Interrupt - else if (get32(CORE0_INTR_SRC) & (1 << 11)) - { - // arm_local_timer_intr_handler(); + 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; diff --git a/Lab3/src/kernel/shell.c b/Lab3/src/kernel/shell.c index 35f4b523f..7d025719d 100644 --- a/Lab3/src/kernel/shell.c +++ b/Lab3/src/kernel/shell.c @@ -7,9 +7,9 @@ extern void *_dtb_ptr; extern char *cpioDest; -extern char *read_buffer; +extern char read_buffer[100]; -#define COMMAND_BUFFER 20 +#define COMMAND_BUFFER 50 #define FILENAME_BUFFER 20 void shell_start(); @@ -28,6 +28,7 @@ void shell_main(char *command) 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")) { @@ -61,7 +62,6 @@ void shell_main(char *command) filename[i - 4] = command[i]; i++; } - filename[i] = '\0'; read_content((char *)cpioDest, filename); } @@ -165,9 +165,50 @@ void shell_main(char *command) } else if (!strcmp(command, "asynw")) { - asyn_write(); + 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() diff --git a/Lab3/src/kernel/timer.c b/Lab3/src/kernel/timer.c index 0cd6a1546..f1a628220 100644 --- a/Lab3/src/kernel/timer.c +++ b/Lab3/src/kernel/timer.c @@ -1,4 +1,15 @@ #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() { @@ -25,6 +36,56 @@ void get_current_time() 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 @@ -36,4 +97,54 @@ void el0_timer_handler(long cntpct_el0, long 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/mini_uart.c b/Lab3/src/lib/mini_uart.c index 29cc44b59..82b34aa13 100644 --- a/Lab3/src/lib/mini_uart.c +++ b/Lab3/src/lib/mini_uart.c @@ -143,9 +143,9 @@ void asyn_read() ; } -void asyn_write() +void asyn_write(char *str) { - strcpy(write_buffer, read_buffer); + strcpy(write_buffer, str); len_WB = strlen(write_buffer); put32(AUX_MU_IER_REG, 2); // Enable receive and transmit interrupts 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/string.c b/Lab3/src/lib/string.c index 2fee7a237..be47302ee 100644 --- a/Lab3/src/lib/string.c +++ b/Lab3/src/lib/string.c @@ -58,3 +58,22 @@ char *strcpy(char *destination, const char *source) // 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/userprogram.img b/Lab3/userprogram.img index 71bde78f0186c1817f7e3b0a6bacc9393fea509e..d1435f4f8581381aa7f23817f52e0aa1818090e2 100755 GIT binary patch delta 972 zcmZ8gO=}ZT6g_XISYxVfQz2rS$xND>5M8yKc9p3?=&Ge@aI5V?in>tfqF^DT78lh; z;tSdzP)S{hbRn~mQTzdIpb9QzqAqkNS*TNp<9#nP784S<=e~Q+xgTU}ZfCx_&1PRd z>r2Lq*mnzCgV<{ ztr<2!4BCQyZ@{8!(UIl0cT3}0Ht8gVxF-%F(}bxR!lrEi507#XInTe3+U|C8mQ^U) zcl+V_DcF7j_Aqd|l`?HtWqGV>ivWvaEw%WW7V9P~yVY-cx>Pi$O&|&f3odcp1dUG1 wc1feRPMmJ3D3st7j8%p@%X*(fR)L~L|$YjMcluT8*JcZCKrx=>UoiBB|i zW06WL;zFZBK(PUTL7;AJskqTqcC(1_yf-uSV<3}z?mOq6^X{z8wyrf+#MAe$N3!il z)1Hvuvww{*LVn1dG2RLJn}4r_JZa^OCDFcMJrJTKyS7)K7I-EQ_`yWb!>`r8L%hzq zp!+Q!N2C5^+|S1Rq<@^)QF=V?+0-9N&c`F1j&X1>!YSfgil5T>Z2v$a7>fvkxa$Ny z(h(jY5+EKJLVk9e+#WD2bKxbnP*&DH)-;^C~hVcF&efCi}fNR2Uq z>k5l`c_}|7mkMKb!6QwndEqBglKLg2Y8~Xx(C$@ZACLtP A#sB~S From 5f018b33fbe120afb177c00b798115cb7ad19041 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 6 Apr 2023 16:19:10 +0800 Subject: [PATCH 07/22] prepare to start lab4 --- Lab3/kernel8.img | Bin 25624 -> 25088 bytes Lab3/src/kernel/shell.c | 3 +- Lab4/Makefile | 58 ++ Lab4/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 32753 bytes Lab4/bootloader.img | Bin 0 -> 10920 bytes Lab4/bootloader.py | 25 + Lab4/debug.log | 9 + Lab4/include/device_tree.h | 9 + Lab4/include/load_kernel.h | 7 + Lab4/include/mbox.h | 6 + Lab4/include/mbox_call.h | 9 + Lab4/include/mini_uart.h | 17 + Lab4/include/mm.h | 19 + Lab4/include/peripherals/base.h | 6 + Lab4/include/peripherals/device_tree.h | 16 + Lab4/include/peripherals/gpio.h | 25 + Lab4/include/peripherals/irq.h | 27 + Lab4/include/peripherals/mbox_call.h | 40 + Lab4/include/peripherals/mini_uart.h | 19 + Lab4/include/peripherals/reboot.h | 8 + Lab4/include/printf.h | 109 +++ Lab4/include/read_cpio.h | 9 + Lab4/include/reboot.h | 8 + Lab4/include/shell.h | 7 + Lab4/include/stdlib.h | 25 + Lab4/include/timer.h | 13 + Lab4/include/utils.h | 8 + Lab4/initramfs.cpio | Bin 0 -> 10240 bytes Lab4/kernel8.img | Bin 0 -> 25088 bytes Lab4/rootfs/a.c | 8 + Lab4/rootfs/cat.txt | 1 + Lab4/rootfs/one | 3 + Lab4/rootfs/ts.txt | 0 Lab4/rootfs/userprogram.img | Bin 0 -> 9240 bytes Lab4/send_kernel.py | 40 + Lab4/src/bootloader/boot.S | 25 + Lab4/src/bootloader/bootloader.c | 15 + Lab4/src/bootloader/config.txt | 3 + Lab4/src/bootloader/link.ld | 21 + Lab4/src/bootloader/load_kernel.c | 67 ++ Lab4/src/kernel/boot.S | 86 ++ Lab4/src/kernel/config.txt | 2 + Lab4/src/kernel/device_tree.c | 266 ++++++ Lab4/src/kernel/exception.S | 135 +++ Lab4/src/kernel/exception.c | 150 ++++ Lab4/src/kernel/kernel.c | 16 + Lab4/src/kernel/link.ld | 19 + Lab4/src/kernel/mbox.c | 63 ++ Lab4/src/kernel/mbox_call.c | 42 + Lab4/src/kernel/read_cpio.c | 151 ++++ Lab4/src/kernel/reboot.c | 19 + Lab4/src/kernel/shell.c | 264 ++++++ Lab4/src/kernel/timer.S | 16 + Lab4/src/kernel/timer.c | 150 ++++ Lab4/src/lib/hex2int.c | 15 + Lab4/src/lib/mem.c | 22 + Lab4/src/lib/mini_uart.c | 183 +++++ Lab4/src/lib/mm.S | 6 + Lab4/src/lib/printf.c | 1046 ++++++++++++++++++++++++ Lab4/src/lib/queue.c | 7 + Lab4/src/lib/simple_malloc.c | 8 + Lab4/src/lib/string.c | 79 ++ Lab4/src/lib/utils.S | 15 + Lab4/src/userprogram/link.ld | 19 + Lab4/src/userprogram/user.S | 11 + Lab4/userprogram.img | Bin 0 -> 10152 bytes 66 files changed, 3454 insertions(+), 1 deletion(-) create mode 100644 Lab4/Makefile create mode 100644 Lab4/bcm2710-rpi-3-b-plus.dtb create mode 100755 Lab4/bootloader.img create mode 100644 Lab4/bootloader.py create mode 100644 Lab4/debug.log create mode 100644 Lab4/include/device_tree.h create mode 100644 Lab4/include/load_kernel.h create mode 100644 Lab4/include/mbox.h create mode 100644 Lab4/include/mbox_call.h create mode 100644 Lab4/include/mini_uart.h create mode 100644 Lab4/include/mm.h create mode 100644 Lab4/include/peripherals/base.h create mode 100644 Lab4/include/peripherals/device_tree.h create mode 100644 Lab4/include/peripherals/gpio.h create mode 100644 Lab4/include/peripherals/irq.h create mode 100644 Lab4/include/peripherals/mbox_call.h create mode 100644 Lab4/include/peripherals/mini_uart.h create mode 100644 Lab4/include/peripherals/reboot.h create mode 100644 Lab4/include/printf.h create mode 100644 Lab4/include/read_cpio.h create mode 100644 Lab4/include/reboot.h create mode 100644 Lab4/include/shell.h create mode 100644 Lab4/include/stdlib.h create mode 100644 Lab4/include/timer.h create mode 100644 Lab4/include/utils.h create mode 100644 Lab4/initramfs.cpio create mode 100755 Lab4/kernel8.img create mode 100644 Lab4/rootfs/a.c create mode 100644 Lab4/rootfs/cat.txt create mode 100644 Lab4/rootfs/one create mode 100644 Lab4/rootfs/ts.txt create mode 100755 Lab4/rootfs/userprogram.img create mode 100644 Lab4/send_kernel.py create mode 100644 Lab4/src/bootloader/boot.S create mode 100644 Lab4/src/bootloader/bootloader.c create mode 100644 Lab4/src/bootloader/config.txt create mode 100644 Lab4/src/bootloader/link.ld create mode 100644 Lab4/src/bootloader/load_kernel.c create mode 100644 Lab4/src/kernel/boot.S create mode 100644 Lab4/src/kernel/config.txt create mode 100644 Lab4/src/kernel/device_tree.c create mode 100644 Lab4/src/kernel/exception.S create mode 100644 Lab4/src/kernel/exception.c create mode 100644 Lab4/src/kernel/kernel.c create mode 100644 Lab4/src/kernel/link.ld create mode 100644 Lab4/src/kernel/mbox.c create mode 100644 Lab4/src/kernel/mbox_call.c create mode 100644 Lab4/src/kernel/read_cpio.c create mode 100644 Lab4/src/kernel/reboot.c create mode 100644 Lab4/src/kernel/shell.c create mode 100644 Lab4/src/kernel/timer.S create mode 100644 Lab4/src/kernel/timer.c create mode 100644 Lab4/src/lib/hex2int.c create mode 100644 Lab4/src/lib/mem.c create mode 100644 Lab4/src/lib/mini_uart.c create mode 100644 Lab4/src/lib/mm.S create mode 100644 Lab4/src/lib/printf.c create mode 100644 Lab4/src/lib/queue.c create mode 100644 Lab4/src/lib/simple_malloc.c create mode 100644 Lab4/src/lib/string.c create mode 100644 Lab4/src/lib/utils.S create mode 100644 Lab4/src/userprogram/link.ld create mode 100644 Lab4/src/userprogram/user.S create mode 100755 Lab4/userprogram.img diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img index a50de1b2e537698fee6837060d64eeb2f20d4d4f..ee17699b342c7c7789693e86f3a3e0d1bc961848 100755 GIT binary patch delta 4039 zcmaJ^du&tJ8UN1pBhJ%KoWzMk>}yCYNMK15L;~b-VxFZmqZtDgwkCjhluSjiSJid1 zp*%`~&9}#GvWJH0DuIfG%)C)qQ^6I}v<Dp3@i_EjQ0sxl|S3~S>6~wZZoj;8`ls)UD(M`&4>%upw zwznuUKmbxQz!)I!x8~@~OVCCfIesr7wj{^q z@}wPNet_Lj0Bf;;1gK3)g$rV4F;GcUeL9~2xmhVTY1*tf@^KAen(8o>2qY#QZ&n^U z^Bi{4)VzGd`7|}h#G>72Iu;XYRwnZ8R?-xXh$kiz`Pjq_%~kGldX6!3SWQz^dB6`* zX$7bhhUvM=ADyhovOzjl#A^W;* zi!@Qn#%=#-J6j5|B6OD7MN_JUG{N)!F949@&$!_%VVD4DQBERGZKv8n7qb^^^Ik=@ zo_LG$GO7ekTa*K(>|ntH@Kvov z`&~voB+@DnF7{=Ir?$wjj~lZjX+1VX_E%K%77J^OY27BQRVVWmR(r=yYhtL=y84=v^%qu4zjLy;3cXGJ*oIr!-V7F{ zx1e1iSk8>8a@vK{cLY4t{RsF(3B-yYF@Uz|*mS${Q3^WDE zTw|}fg5d;#1q#pPE~2={O~wd7UZAWuw7j+nGLx1UDIt@NEK+()!=}^4N|&ifFIE;J{Z*nL8xrpl37$6+JYg&-q`Q@Ve=HR*2Uaxuhu5TI_6lzkGv{38P+th-`E|CEmms_vj4pP8iL<5@FF^X8L# zlyTgR>?OdXopbjb%FeToDq8ACkisl-o)L8sJ@XCu7kkX@EZ*d&_iE6Js%#K{vTNWh|-Ogj! z?KOAB=$JYP8d&33)Q!N8Sd_$jmMZEX)ruud36-42CHvHvVHDIJ*I*RP%j&~E z0aGvQ`Y}U4wIBB=Yp(K26xfQY9w`FsP}L3oGg$otuS|r`b%u?RcY;vD+@7E`MA%}F zPf8QE(bG+qu}vh5DyUUClc4No~RJs(a)Ls3#|X<7A8czOoB=H=3gJPPWbOt2>7xoG_yCqXuNS?_qYWaCnm@ zkNd5}nt-&Gos_#O69Jb)2>?x5fG*?^Nu}(8+=&R6gS7`{8aOZ`qh&W;c zF?tSA@DQbl70QKkinY)%LLsKb$z>4)F}6n2Qk=%Qkbt_>NnD8>R{ z9u;qT49r=}Y>7<8H*I))uFGz(g;bkS1TPE| zH^D4Cb9SqUYRw5Xb$cY0!Ab+zu|6P2u*xGAIoIF!XU?^XMi}j|A2iW`rPm&ihdsfJSE2QN1R<* zGR?-Ar|`k&c?k5=hR-y+;tP`KW<6*KX19tKVaC*F+iKY;nqo3c#87`?{DYh3kD{r_5j5K zf^-IHe#;47)-Rtvp0N?8b6S}B)5Auz3bl z2p&`8hic{yxl_c=;muUVg2_9EI`JQ^-5LDulDwP>F_vk^mlEkpUY{`P%{gW3SNXzJ z-$%1Pe%bQHZo(IzkfEN-rgBA|=PL5V{_s3`9->D^Pi|WwR*Et6bNT>fra%0wruLQS z4;{YPV=zIJ|6CO$L2Bp0G!>t&I@rz+3-UA-5H~eqsXe)3^?M{)(|?a7*Yqd!KWt6^ z&>L&4#F8XyPurwppRzMge^>g>{bbLEkR>@fINQ2-bdv*}0?3>7-=jADH(~HK7M*Y1 Qs{hs)^3kmh*4JPEAKbd!R{#J2 delta 5561 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 lbK_vAg(G#HzCM)_$-#PtkZmoVUY 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 OPqI~*e!TdI|Nj8Si!WyY literal 0 HcmV?d00001 diff --git a/Lab4/bootloader.img b/Lab4/bootloader.img new file mode 100755 index 0000000000000000000000000000000000000000..69042bf675d797e02767f912738a24a4f41f1a80 GIT binary patch literal 10920 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 BeH;J) literal 0 HcmV?d00001 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/debug.log b/Lab4/debug.log new file mode 100644 index 000000000..af4e88a88 --- /dev/null +++ b/Lab4/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/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/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/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..d8a7167d6 --- /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 (2 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_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/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/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 0000000000000000000000000000000000000000..55d33a5527daaa16aea9f6474c77f21cc807a82a GIT binary patch literal 10240 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 V=}}!@lF;Ajru`vPABs`8{9m*L(?I|L literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..83efd9631fdcd22cd665917b5808106cd758bd29 GIT binary patch literal 9240 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 literal 0 HcmV?d00001 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..0735567bf --- /dev/null +++ b/Lab4/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/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/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..d076c7508 --- /dev/null +++ b/Lab4/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/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/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/shell.c b/Lab4/src/kernel/shell.c new file mode 100644 index 000000000..3f7c5c39d --- /dev/null +++ b/Lab4/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/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/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 0000000000000000000000000000000000000000..d1435f4f8581381aa7f23817f52e0aa1818090e2 GIT binary patch literal 10152 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 Jv7xfZe*>9UgD?O9 literal 0 HcmV?d00001 From 00585366ed16644149fc97597e229abf93885d22 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Wed, 12 Apr 2023 13:53:27 +0800 Subject: [PATCH 08/22] LG on buddy system --- .vscode/settings.json | 6 +- Lab4/Makefile | 3 +- Lab4/bootloader.img | Bin 10920 -> 7720 bytes Lab4/debug.log | 9 -- Lab4/include/math.h | 1 + Lab4/include/page_alloc.h | 37 +++++ Lab4/kernel8.img | Bin 25088 -> 21880 bytes Lab4/src/kernel/boot.S | 4 +- Lab4/src/kernel/kernel.c | 2 + Lab4/src/kernel/page_alloc.c | 257 +++++++++++++++++++++++++++++++++++ Lab4/src/kernel/shell.c | 97 ++++++------- Lab4/src/lib/math.c | 9 ++ Lab4/userprogram.img | Bin 10152 -> 7136 bytes 13 files changed, 364 insertions(+), 61 deletions(-) delete mode 100644 Lab4/debug.log create mode 100644 Lab4/include/math.h create mode 100644 Lab4/include/page_alloc.h create mode 100644 Lab4/src/kernel/page_alloc.c create mode 100644 Lab4/src/lib/math.c diff --git a/.vscode/settings.json b/.vscode/settings.json index e6152c4ee..fd665e208 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,8 @@ "deque": "c", "forward_list": "c", "list": "c", - "vector": "c" - } + "vector": "c", + "math.h": "c" + }, + "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab4/Makefile b/Lab4/Makefile index 5db4cff0d..a7ce835b0 100644 --- a/Lab4/Makefile +++ b/Lab4/Makefile @@ -1,6 +1,7 @@ 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 @@ -13,7 +14,7 @@ clean : $(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c @mkdir -p $(@D) - $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ $(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S @mkdir -p $(@D) diff --git a/Lab4/bootloader.img b/Lab4/bootloader.img index 69042bf675d797e02767f912738a24a4f41f1a80..1f3610a936246a7cec69b6a2a32841fcfe28482b 100755 GIT binary patch literal 7720 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 BeH;J) diff --git a/Lab4/debug.log b/Lab4/debug.log deleted file mode 100644 index af4e88a88..000000000 --- a/Lab4/debug.log +++ /dev/null @@ -1,9 +0,0 @@ -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/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/page_alloc.h b/Lab4/include/page_alloc.h new file mode 100644 index 000000000..27912e787 --- /dev/null +++ b/Lab4/include/page_alloc.h @@ -0,0 +1,37 @@ +#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 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; + page_frame_node *next; + page_frame_node *previous; +}; + +void init_page_frame(); +void *get_page_from_free_list(int req_size); +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); +#ifdef DEBUG +void debug(); +#endif + +#endif /*_PAGE_ALLOC_H */ diff --git a/Lab4/kernel8.img b/Lab4/kernel8.img index ee17699b342c7c7789693e86f3a3e0d1bc961848..2a4136dcb235c7827920902c8c0c9a0e08f32d25 100755 GIT binary patch literal 21880 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 CCOlLC literal 25088 zcmeHw4Rn>&neP712|p5)09qKJoG)Ms4L_mc6e38zfLdBqN}yL)ZJQjD17rvZ$q5A- zTMwny&QMqRlo_0M?igy|Nwsz0jK)jea>ot-Fms^vXu zmiIs%`W>_@{COp4D{6YeE1BMNyhz>Yt!DiT55$(J^C?Gdij}DIo>H4ilse>H87*-Z zMN3MRI{C-%Q-{u7qYh=GfAmD>cy;K1XS37h{2+BYGI@5-oW1yZvwJoxRWb+PAo}nN zPW7xFm01bg-V-V_tLL5ob#?5tQujQeCZjxgWzRSFaNMk(RruqWhvMVGBLC^{K*!Tv z15xF>N}W=_^7{8pNYy}&w5`XA^5x8ab)7RlU*D$yJ278}T={WV=gZZAD^;Cs&h&hn zjGdLQ$J|i)+I*RCGIoBx&2d0?VL^RKz8DhAF;Sr=Gz39S5UtxUyrQ~m2VA| zKd#aWeB3rx9f~Q{s>Z2J(AhB+l>1Xk)FG=sctg5c*N-W8fnRLP-U5Go817mxz0CFj z&avgr1^%0pLim<@=|$mj*L&#?T3C#`sA0j!0{^w}xV2sy_90)w$FD71-9rCd817at zeN71NU(r_tpO$gm3;q9vdd!XX40IfSv^!e+!azq_{3n>C`h!Vcf3OtarCz^MUiyNg z4rQXMyz`$QuIY4s)K;a`b`Sn<`+sBl1-G}zzyB(ANU7A6FN2=IjQ7$Rzy!dN0Zzaj z2r3NziC+4}!oCIQs{jwnRDX6J{^sH@hPGvzpMNC$GL=R>9lKVwdK1BmDQ~mo64%YtXTYORZ?O&=%!q*F*g<9@CxdL4Irb8aSfe$pjqUl4_Un|Fm#{?frlR#ku zfSu{P$7z$8*3rFYY%mS5(+sS;*#7`*W9p~WD_%cx1nBEAdn~;Ix$1|J>#71dCa_L_ z0pD8yKMr;1=9o$?_OCGh5JB75kP8Poo*mxj=o7FzeeM&Q=1`wA{kNHZp#h(J?Mm%) zA8^12Vc>H&0Y-cdb+ME2D+Qmsv9OPR)&ie`uIc;M=dv?Vh7PT-S8&`sz|GUXr(EdV zl;_(r{H=SoX-9cm%#`78b>s8pM=gx?J>r4ysWEEPt5%=%Jrj!o?cV|z*!l>3Rb&qq z`+o)5ss9Z6f~Zrj_QW^o2*a!v6|rYg<+L_oDpcLOkFjV2sm;7zdlX+k2coyb^8R%mzi6fZrxsF`5J5B1KrMff;M(- zyZa%*|GH_fgMSGAI^BN3w09z}X}Y@o71NFw(CvY4=R6_!ZM%D&;79J4cIG}Umu`R2 z;sqpgTC2-t0435_(szb%V zQE12O3SqmK`ey-a^W+NaTLp3Ofnnl6avuHD#H8dr<{J3u2LT_>dC!4{!W_t)cmB-`@C2j^%#Pd_6$GrM0sAB=2z0fxLyHTiEE z@(yxY3ci83kn-c4M_ESx^5BzFM*c$n*c42a*r`;Xn!YiEa%qwBOO;oNyj0G)ld(G+ z{8>0>*7%K51{`9pQmri6SgKUHw2e1yzy-O=1>eM>_-GIMZM5!s{~0$`gLy8^x(crM zu`W17eNdO+vwxuDgx2Q?=#z6KqU8gc3K7BzHoRd{HFr? zsX$&-S5C2Ii7DtuUGXQU-M(*MxF2vz&7Q*cI@qQbzBr9GG5z;Se@yyajQbM$C$WEG z3Muc=3!e}7BbG{o6+H@n1@-4x67lKP`@R-r%pc<!rxPdzg#SIukXNxY z+5ng5_eTG@Vs&U0*CO>1|BDgs#ZongfiHz`@N<>E61)XJPo>RT>_Fz{Ub?hIZHNHB z%e5uO&DrKI574eS_hL*K-qOqrWSOgTvT$4<nGNM-Q!GnJJ8KsLK6czShZY(8X!9|kAEH^x+vKeSV>K|G-E1bAQ!y+JuS z)-~%>ln(%26{TwaSm>;-ypP|LdR?>V4bVavGH(dL+dpHyD1Eo8)+W*>F7Q+*e0XUG zy2$`e*5jbti+<4?zOBIfGmIx6C-D{A)~yoXDKq*g1s=AmUrN5}=h{c=|K#=m>6_WZ z&uJez=%n zIq>&=;LqH}@m_xdu#~M+`{zMbSLIWN#{r|T|5x60oDp=McFk8+PnWZRM=Xy)mB{ug zVt{x`kyBm;PNmdA)qSN8A3W_~EY>ndS1(ci9F!w&=jjOWM37fjtH^;!@64XtAS2Hh z?$>yaIST7B`1-F5-Z=Ps1$^CW`?+32|0&a-(fl7%FJpg!HV^yjs{p?j@T*a#ejQJb z=|h~5SHS6bnm%>nI>M@ z1Dx>EN%#-fRbn&O5j+1EV`s~=<2k;Eyta-0i&(pe^~^xW&&(e0$1)FbysC5jCm~06 zJ!sVDZMFez(%R;z7Nh-}O2ledqVk4a$;_fa`zS@ozY--M`a)v7egU zM7+?a9|MiQGr8%f_zraZB)9ewegnsE1I%Y=bFRa<=fVC% z?Qwhu{{2nFldL<~_A4cA|CMc3&@J1R>$Zo`_8{<3zD1M^xWr#7WNlQee&|d-VEW70 zU*)CmGxbu|iR(^{>t5hWp{;vNs%D1m+oSuwfWEzGbHUqG_JIy?`_*it?YkrXDz*bZ zbg9=wj0e=g&vv$z;!oYQ@00uVJiIQ0K8!6EnJQ9g;=@||0qFRAC&p&b7KGYMvE~h` zQXF$Y{SU|I`iNz&l_o-u!@wWN!2@>~{u#}~F(-)ll;uZ=N!qrWJfj{3d@h%%s2@b5 zNB_#e?7NWN#5fq}`01b+kTo0DWAsTm7XqGudwT5Atz8#d8yXr$UmIlqiMS%2!!t4^XMnUV|-x-@vMU{tSitr>LcnKeWGuUgC2+I8*Sls(f7Jb=zA#qfxyDQg}y)Y zHuU`o19SY}O&r2L$Dxyq?5E&!Uey1iE7YO+BgD}2^v4Rw8j2yV+kypX3;7lOjWJ#5 zKhV|5LO+vp7>o^%rw>w>VgJf3@MM4W=a6Bb<8O_hMw8H!i+;-I()&docbU;F+kVz%P#R{z)~>V!530rKTR1ee>U& zGo6nC-|x-bm!aQYt^1!wzlD8@WfhTm0zSLxcLrXSXxJa1AN~paw5PMZ8vI(?4$fo1 zMoks&DS~Ma&rq|}l-z!Qzdk?7jDu}(h9dfaZZhK_Kkmzzw}(3~aWH>6-eYa4DAwV< z-@fo_HhVY|10CY3t;=f^N_jYiK#U63;E=k z_to_ImK56bd|8U|r6uEeUXzY;KZ<%J03E=TVqIC<_qktG<9-9}2QtxUx#PnAEzpHI zBbGC2tciB$z3DLpTw{UjEcErS>!UqU$a5|JdZS~GuD(0^jn%5?z}aX-_FWF>bc2Qh z4dNez`w7w~4y=`BO=at}mz0(63%0BDcJWc{FUarqRNCTkut&47kV(c-raD8*V~z$4 zeCFSNgF7J$i~YF35}z01i?CR0=IC3TBhf9S1>XBJ^{E^mmRIt@zU)?&zRt*kJ6`PF z@;nDyhuR_HdX;R?JfinBmj9xHaT(|&Y`-YR5mxMr@%u)yufaWcTix<7d?CJ^KWsJr z067Abxkmm@KXS@;(+*n+kH)SUv@>28VC`gizm)Dh>{B1X{Fa43X&<+DN})4M__%|d zUJ6*|KRIV*obbF4`&H06sJty)$)!F~-jkv&PR4ioYQ)3;bMQ zjZYpd^t}Q<7g(cf!8gRuYsFTBaE?T`ke2v)t@yd{!5l(fJ?rQ21LNm{-}t$PrLPwF zqxd=6Lw+vfjGyPmWk6>bKhO1#=;sEv@$(#fA-qXpFMod8+P8( zbCBFMU~Xp&^x{nXOcXxPIWwyBTQ+BtMM9O3@lox{_h6FP$r zp2j&Ve7_DfSYDf(%WRCAI3Ho0;yw*$SW^;Ko;C#v=D zIkpOT`fx9dGS^dFS1Ejz!u}U)mF$|q_YUMITcOe=fa7{e?s(g0Z{!^>?|NaEa^V%S za$QG0=3ovXztCT?>c&|r##|YmPqT z&9ku4IjkREnhAVddn|y?W+JC6eUDMfvvR3x+(&pv1pR}4znpnv4D{w_fU|F&2fSWC z&m%M3yJC%%f^C8dqj!t}jWlGId3i2$EN5V1Pw18FALMc3cj13yAQyQW34ahBQ!e-c zPn#X*xmSpH7iUY{zg-H4?hYpXB|;K*!$=-uI8BN5q!su|BWA zeYo=t?&6wOwFW%vl0I`@!umbh2anK?*jJ?Qms0e1jDx>!z!`KJufpVRO>|-OL>)PD zP6~hA2)#lNv9x*&ZKC!#dz_@^LGoddJdlo}G2V>fuoE~Qi-F+c>=bQ2nJx$_y zvZ{p6uD19K@rcfitRvzn)_COZnEc>=G#;Wqegyi}6ZJT&%aP!6_B4EKW4sw3aYw*? z1m|~@Z8~KWduSgT2|jM%d4{Ip-!FO;+k@^9-Epr3{4W9bIoR+lVEPcJ66=W13}QV% z?4QGVtv&n5?vFe_&=Khx*pGa>3~{{_c?a=-2Kl8A@pCrjR3h*6zqCfBaegM_={I8Y z$KdySW=;tnLr!8@Y5z*mA3%q29`h2)jN8cg<6q~u`fleN$e|~L+DK(ki+iPO@lK3m zP)5$2>>xi%&Jvv3ck0mZjyHQ@*eF9-=3&69`Dz2l;JjGxufb>U37moAo-6~}%q#AA ztUn=-elHRz?^*%d6u>f&zTpaMMtwAK|6e&ewewJ zXDL|A<35hC3yKg^JU8On;t<|`F;}53bJ<5sY+=02mD)FCuFTD49LqHd=I7}am#JqT zhR;~s-5Pfc-zMAv$G(SqM$qE-70fxY$MJ>(`Iz4()>D{&SpO>4JIE=E8T$`2w$ycj z#cSxeSi1xds`R`ZOl(9LdGjH7W{(JSO%BidM}(1FBzR_x2*dZ?S@0FZS}%AIxmRJ1 z_-%Gm_8WPv)*^bnunrRyRxZ?--Lcn5crrLTSd zfp|o(Z|1ma^5z*Ty%}(|p!b~u7_KWfk3}B9zQ)6R47>9!4*oQM6PU9_E~7)V!w&1n z59J{o%fgqn^I=n`5B(4_at$Fk-Zw-yR-R7|BX{;1Ph2e{>w zOY|~*M7?mVlhfa{A#ix-WpMn#kiHzBpj*m$SZK`{0yiUvV<;bT7B>QqG8c`|Hx@YQ z=kzarxu+1nxBeSIM|IH2L8tW}sH3kst=!n5ePFF`dFF4Bi6WHy1X57OwN-_7*hR1>5QSA>Dw(ncd+}HX* z4DbQq?f&D4r#8I^{5k!BSBXW-J9ywcukem0xX+wd+P6GLK1<^f+l1C0tnY~prU5*IWCNb# z)2**elXVq+dPtsG`8M)Q2xgc(qxWi9D^UOHTd5bZZ#){HuHLxk7ZKPlb02aGc$T%s zH}K_J<1p56SYzOPw!9B_k7qasVP3LpIIQsx2biDM@U4^f&C4qT_k>vM43Yu${}&ms z{}magPZ9W26#j&@Yk@z#i8Cnr(|2?FF)=aR92D|*#{IB!ZwRI^CLElX zT#Cmr$Q#4)xX}2^mvcN$*FOKBh{wl`9zUJq@&7BIo^siI8ut|$+*iC4zgTZ{E^~xg zn=O@hn3#X|BUU6onsYL&z2&Uw5^H>A&%^ecw?x2^L0+!H8Sh%0(_D_29)q|IF2Pf7 z-w~3+BqSc8F}DIDRLwj&*iF&SCol%xBD3b)36VHfOI7Aa@|68Dez%kJvjqfVVlZ15%@r#kGuG{zYoM1 z$XH3)yxbnbtVMVZ2fl%GLk4SW!esc)_MO^r1m43o+yxuTdJTJ(4CuJIVe&5f$eu+5k zw7G+8VUvq^e?T1XpKUW@zL*l*Wm1r=N{{D_J?z{a~pB?cL(D4L`2R_k;0YW2}E>uH)Agd0k$S>Z&WM#@Fe}pdEK7zMa#t=hShZ&Az6*tNi<^XY5J& zeqo^Fq&dU?L7p$1vGlG0U*N9~{GA1V=fK}f;4i>g8_#p(8|G`wTk{owuRi1-o{tJo z&|^TJj!pEp4dUq^3{OT^pqH`tGb8DWx}nZ+p2~Or@;lZd@PspmEOL+Ns(z@hay)^T zUYuQJ=H+=>L!Rz|p6&_jN#7aC?@hqhMXc?5_rng+sYi#{0qp^3iXDKr&<+cLU%w%? z_b4IUkS7EE_6m*Yh%_+YYMlaqFZMXRL$W%ZIt2gshTeCy*@hrR}U zU!#tn6T9PHQ0@|A*x%bbLYBMn`|XR9X1~sR!ZY~&XYVwXzBq|-jyQz=ry~C2nAdqG zA51gz0AM%|OyqAn0DE!V>eq<}zj=CI-Vqgp_er3SKE9XW+lgSR>X-8goaJNfT*3>ECUk4>=z{6#^gZV;&+dj$;}4fM3r`phbSLw#FGjss?-v{>>Wh z%2E^b@2ZyJxB5!y^Z07y9s4#+?mhS}o4E*gAml@h*JI{mjP&C-EOGRA;v4{P!3*AL z>EFj$I`0=c`tB`E$Kt$3a6+fNZ@J#ADNttq^MF79ySn^u{&)sKoyz8fD7!G5ABC2YdH#?1Zx@h2Poz^%&R|{7@Ha=j185vYL8buDtWAPmP^{`r~!b=iq+*4&;k+uPAQ~PVx6S z7jT~;d>g(#BYb?TMy=Mrb^Iji_KlU|@60gfKkzOog0&>;4(fa_k)*td*% z|Idh@1J7$-|8IduaH5<+S>ly6T)uZx>1yi7@U^^vuj>o>0*+JWz0|&8V?fJqWA_6X ze}?&i^p@y%Rgh^i=$^Q!&V%h4A zzhSX`@OQbZ$p1Gv4A11z({BI;7WzT1kbPh|_FmG4ezxUgK%Fv(JkSmKr;Wln@eF=9 z%G~UP^!zVqv-rr*Ab$a#E$|oZSD`mT!_h zF7<@@6SxuMh;z)N(uRI%>%bc-#D37B=j7vN9wfgG zayjQ!t_f&A3kN-~vvaL|2PL$@JN~;DGXDz=GglE;oOm%;$oc|b%;l58Yx>{uMmr}9 z^%0T<R;K#~#c~r8B?*9q=9w@4}RRD}}MxCuzBWqYzGXz&$naTDmUpMetV7 z!xwVi37l!Jr+^dx_ujwvH1M7V-qXN)8W?R2L~+l|`ERuT&fq>^v}lv=iBYEi$SBi) zVU+3r%_!4fGs^Vmjxzn|DAWJLQKo;wtp7(V2R$;%^l?uys`YEnnIv0y}y*#g|>o*l+x?Wi}w(HfZab2%{WPI0!^;dNL zpF1XW{YPza*Xvs+cKxp9%C0wdU)A+y2lw6hZ|c-6@0sH*?}_5CsBU@BzrBL9%yAy@ zRs-*9;9Uc}Hv#X>z?P?jh#|I4;oN zBD|?IeOxoIZi%-wxI0=J5=}R_sN?O3{L}4iiO)7B8(W&COWHRk+8X0cZu71kn-gtl zuCBSw-I3VQ(zebV_Pctb;5BALt$2=yl$KbOe0f1)|Qxhdhcx45ml5*VBmyrq`B zx}&Y7d8>O{d~0L9d%F;Gm+UE9vV4z$XliWUwP#^tb7Om3!=hw+yscgHEiLgKTavZp z#ErMRWji_Kqs^a2cCzFlIMYdscOACbCiV^x>vb~{Y7m2mwSAO`1dSi3Gs&=;QPP8p*OvV>| zHql1Y>0xTej$JvNY-);cPBh`WbKBlU^)1cKiTZW`=Rt)3Ic3Q?hX4AO9iZ6=n`wm7 zUzRkzN^NOquWfIOlW4--5^rouG%ThLlWp}C+Y@cgiKYrGYFi?)7}J&hch}8rZ7pq| zc5iNOXpA?zK+&As($cmAv|5_ymoGWMbzH7Bf$aq zxYr@7>J3Bj+uhdKPM-KLnP}ex8ju3O zpGd~HBDC{u?#kPc1kkw3UAZv<|7u9C(gQ{R7+z$Ikx>x zEpgp%6)xVb6ctUxqF?jfjc;BT>{aURs)*(x{Mwsuy>&gl8=2aujwCg=c(Ea?=DLK! z!8a0z%YW;)YynLaHX=buOLcucjNrC4ZrujwvWhYGGat2N8UFVn;HiNkZmuaUE!B9T z*c;q35OIPkYih8@ zb>my$)Gp%=f*Qu?T#S*0i2vQ<&LDJ&F~;A>jTH^gl7{}X|Njl#&G+ V=}}!@lF;Ajru`vPABs`8{9m*L(?I|L diff --git a/Lab4/src/kernel/boot.S b/Lab4/src/kernel/boot.S index 0735567bf..06ffbe9c1 100644 --- a/Lab4/src/kernel/boot.S +++ b/Lab4/src/kernel/boot.S @@ -47,8 +47,8 @@ master: bl from_el2_to_el1 bss: - adr x0, __bss_start - adr x1, __bss_end + ldr x0, =__bss_start + ldr x1, =__bss_end sub x1, x1, x0 bl memzero diff --git a/Lab4/src/kernel/kernel.c b/Lab4/src/kernel/kernel.c index d076c7508..39ced912d 100644 --- a/Lab4/src/kernel/kernel.c +++ b/Lab4/src/kernel/kernel.c @@ -1,6 +1,7 @@ #include "mini_uart.h" #include "shell.h" #include "mbox.h" +#include "page_alloc.h" #include "device_tree.h" extern void *_dtb_ptr; @@ -12,5 +13,6 @@ void kernel_main(void) fdt_traverse(initramfs_callback, _dtb_ptr); + init_page_frame(); shell_start(); } diff --git a/Lab4/src/kernel/page_alloc.c b/Lab4/src/kernel/page_alloc.c new file mode 100644 index 000000000..008c7a749 --- /dev/null +++ b/Lab4/src/kernel/page_alloc.c @@ -0,0 +1,257 @@ +#include "page_alloc.h" +#include "math.h" +#include "stddef.h" +#include "stdlib.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]; + +// 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; + } + } +} + +void *get_page_from_free_list(int req_size) +{ + // int req_num_of_page = -1; + // if (req_size % MIN_PAGE_SIZE != 0) + // req_num_of_page = req_size / MIN_PAGE_SIZE + 1; + // else + // req_num_of_page = req_size / MIN_PAGE_SIZE; + + // calculate req_order + 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 NULL; + 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 NULL; + + // 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; + } + + // // release redundant page (put back to free_list) TODO: FIX THIS BUDDY + // int num_of_redundant_page = (1 << alloc_order) - req_num_of_page; + // if (num_of_redundant_page != 0) + // put_back_to_free_list(num_of_redundant_page, alloc_index + req_num_of_page); + +#ifdef DEBUG + debug(); +#endif + + return frame_array[alloc_index].addr; +} + +// 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) +{ + // Check if is OK to free + 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); + 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; + + remove_from_free_list(free_list[order].next); + frame_array[index].val = order + 1; + for (int i = 1; i < (1 << (order + 1)); i++) + { + frame_array[i].val = FREE_BUDDY; + } + printf("------------\n"); + debug(); + printf("------------\n"); + int new_buddy = index ^ (1 << (order + 1)); + if (frame_array[index].val == frame_array[new_buddy].val) + return merge_buddy(index, new_buddy, order + 1); + else + return order + 1; +} + +#ifdef DEBUG +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); + 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; +} +#endif \ No newline at end of file diff --git a/Lab4/src/kernel/shell.c b/Lab4/src/kernel/shell.c index 3f7c5c39d..5ee0dfc33 100644 --- a/Lab4/src/kernel/shell.c +++ b/Lab4/src/kernel/shell.c @@ -4,6 +4,7 @@ #include "read_cpio.h" #include "device_tree.h" #include "timer.h" +#include "page_alloc.h" extern void *_dtb_ptr; extern char *cpioDest; @@ -11,6 +12,7 @@ extern char read_buffer[100]; #define COMMAND_BUFFER 50 #define FILENAME_BUFFER 20 +#define ARGV_BUFFER 30 void shell_start(); @@ -29,6 +31,7 @@ void shell_main(char *command) 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:\n"); } else if (!strcmp(command, "hello")) { @@ -65,51 +68,6 @@ void shell_main(char *command) 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); @@ -204,9 +162,54 @@ void shell_main(char *command) add_timer(sec, message); } - else if (!strcmp(command, "test")) + else if (!memcmp(command, "alloc", 5)) + { + if (command[5] != ' ' || command[6] == '\0') + { + printf("Usage: alloc \n"); + return; + } + + char argv_buffer[ARGV_BUFFER]; + memset(argv_buffer, '\0', ARGV_BUFFER); + int i = 6; + while (command[i] != '\0') + { + argv_buffer[i - 6] = command[i]; + i++; + } + int size; + size = atoi(argv_buffer); + + void *addr = get_page_from_free_list(size); + if (addr == NULL) + printf("FAIL\n"); + else + { + printf("SUCCESS\n"); + printf("Get addr %p\n", addr); + } + } + else if (!memcmp(command, "free", 4)) { - add_timer(2, "HELLO"); + if (command[4] != ' ' || command[5] == '\0') + { + printf("Usage: free \n"); + return; + } + + char argv_buffer[ARGV_BUFFER]; + memset(argv_buffer, '\0', ARGV_BUFFER); + int i = 5; + while (command[i] != '\0') + { + argv_buffer[i - 5] = command[i]; + i++; + } + int index; + index = atoi(argv_buffer); + + free_page_frame(index); } return; 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/userprogram.img b/Lab4/userprogram.img index d1435f4f8581381aa7f23817f52e0aa1818090e2..dbff941a2cb18aab1060c1e14c26fa80637e37a9 100755 GIT binary patch literal 7136 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 literal 10152 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 Jv7xfZe*>9UgD?O9 From 99e7b0d30271c27e623da6910769fd0cccbf4356 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 13 Apr 2023 18:15:20 +0800 Subject: [PATCH 09/22] start reserved memory --- Lab4/include/dynamic_alloc.h | 34 +++ Lab4/include/list.h | 441 ++++++++++++++++++++++++++++++++ Lab4/include/page_alloc.h | 7 +- Lab4/include/reserve_mem.h | 6 + Lab4/kernel8.img | Bin 21880 -> 24168 bytes Lab4/src/kernel/dynamic_alloc.c | 201 +++++++++++++++ Lab4/src/kernel/kernel.c | 2 + Lab4/src/kernel/page_alloc.c | 17 +- Lab4/src/kernel/reserved_mem.c | 13 + Lab4/src/kernel/shell.c | 71 ++++- 10 files changed, 767 insertions(+), 25 deletions(-) create mode 100644 Lab4/include/dynamic_alloc.h create mode 100644 Lab4/include/list.h create mode 100644 Lab4/include/reserve_mem.h create mode 100644 Lab4/src/kernel/dynamic_alloc.c create mode 100644 Lab4/src/kernel/reserved_mem.c diff --git a/Lab4/include/dynamic_alloc.h b/Lab4/include/dynamic_alloc.h new file mode 100644 index 000000000..6cb8b5610 --- /dev/null +++ b/Lab4/include/dynamic_alloc.h @@ -0,0 +1,34 @@ +#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(); +int 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(); + +#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/page_alloc.h b/Lab4/include/page_alloc.h index 27912e787..8217432f7 100644 --- a/Lab4/include/page_alloc.h +++ b/Lab4/include/page_alloc.h @@ -1,7 +1,7 @@ #ifndef _PAGE_ALLOC_H #define _PAGE_ALLOC_H -#define DEBUG +// #define DEBUG #define MAX_ORDER 11 // order 0 ~ 11 #define ALLOCATED -1 @@ -24,14 +24,13 @@ struct _page_frame_node }; void init_page_frame(); -void *get_page_from_free_list(int req_size); +int get_page_from_free_list(int req_size); 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); -#ifdef DEBUG void debug(); -#endif + #endif /*_PAGE_ALLOC_H */ diff --git a/Lab4/include/reserve_mem.h b/Lab4/include/reserve_mem.h new file mode 100644 index 000000000..ae1f5a34e --- /dev/null +++ b/Lab4/include/reserve_mem.h @@ -0,0 +1,6 @@ +typedef struct _reserved_memory_block +{ + int start; + int end; + char name[30]; +} reserved_memory_block; \ No newline at end of file diff --git a/Lab4/kernel8.img b/Lab4/kernel8.img index 2a4136dcb235c7827920902c8c0c9a0e08f32d25..e3a24fa27b8a2f9779b5ef9f9aa932a77a36b2e1 100755 GIT binary patch delta 7844 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 delta 5743 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 diff --git a/Lab4/src/kernel/dynamic_alloc.c b/Lab4/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..035a1eb5b --- /dev/null +++ b/Lab4/src/kernel/dynamic_alloc.c @@ -0,0 +1,201 @@ +#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); +} + +chunk *new_chunk() +{ + global_chunk_index++; + chunk_array[global_chunk_index].index = global_chunk_index; + return &chunk_array[global_chunk_index]; +} + +int 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); + split_page(frame_index, req_pool_index); + } + + int index = remove_a_chunk_from_pool(req_pool_index); + if (index == -1) + return -1; + + return index; +} + +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); + printf("** GET chunk\n"); + printf("size = %d\n", alloc_chunk->size); + printf("addr = %p\n", alloc_chunk->addr); + printf("val = %d\n", alloc_chunk->val); + 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; + } + 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; + } + chunk *tmp = list_entry(iter, chunk, list); + free_page_frame(tmp->belong_page); + } + + return; +} + +int free_chunk(int index) +{ + // Check if is OK to free + if (index >= global_chunk_index || chunk_array[index].val != ALLOCATED) + { + printf("Not allocated yet\n"); + return -1; + } + + // free the page + int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; + put_back_to_pool(pool_index, index); + + return 0; +} + +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"); +} \ No newline at end of file diff --git a/Lab4/src/kernel/kernel.c b/Lab4/src/kernel/kernel.c index 39ced912d..cbef0dcf3 100644 --- a/Lab4/src/kernel/kernel.c +++ b/Lab4/src/kernel/kernel.c @@ -2,6 +2,7 @@ #include "shell.h" #include "mbox.h" #include "page_alloc.h" +#include "dynamic_alloc.h" #include "device_tree.h" extern void *_dtb_ptr; @@ -14,5 +15,6 @@ void kernel_main(void) fdt_traverse(initramfs_callback, _dtb_ptr); init_page_frame(); + init_pool(); shell_start(); } diff --git a/Lab4/src/kernel/page_alloc.c b/Lab4/src/kernel/page_alloc.c index 008c7a749..e9374e6fa 100644 --- a/Lab4/src/kernel/page_alloc.c +++ b/Lab4/src/kernel/page_alloc.c @@ -51,7 +51,7 @@ void init_page_frame() } } -void *get_page_from_free_list(int req_size) +int get_page_from_free_list(int req_size) { // int req_num_of_page = -1; // if (req_size % MIN_PAGE_SIZE != 0) @@ -82,7 +82,7 @@ void *get_page_from_free_list(int req_size) break; } if (alloc_order > MAX_ORDER) - return NULL; + return -1; while (alloc_order > req_order) { // split high order @@ -95,7 +95,7 @@ void *get_page_from_free_list(int req_size) alloc_order--; } if (alloc_order != req_order) - return NULL; + return -1; // get require page alloc_index = free_list[alloc_order].next->index; @@ -118,7 +118,7 @@ void *get_page_from_free_list(int req_size) debug(); #endif - return frame_array[alloc_index].addr; + return alloc_index; } // This does NOT modify frame_array value @@ -213,9 +213,6 @@ int merge_buddy(int index, int buddy, int order) { frame_array[i].val = FREE_BUDDY; } - printf("------------\n"); - debug(); - printf("------------\n"); int new_buddy = index ^ (1 << (order + 1)); if (frame_array[index].val == frame_array[new_buddy].val) return merge_buddy(index, new_buddy, order + 1); @@ -223,10 +220,9 @@ int merge_buddy(int index, int buddy, int order) return order + 1; } -#ifdef DEBUG void debug() { - printf("**DEBUGGING free_list\n"); + printf("** DEBUGGING free_list\n"); for (int i = 0; i <= MAX_ORDER; i++) { page_frame_node *iter; @@ -240,7 +236,7 @@ void debug() printf("NULL\n"); } printf("**\n"); - printf("**DEBUGGING frame_array\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); @@ -254,4 +250,3 @@ void debug() return; } -#endif \ 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..6ade70324 --- /dev/null +++ b/Lab4/src/kernel/reserved_mem.c @@ -0,0 +1,13 @@ +#include "reserve_mem.h" +#include "string.h" + +reserved_memory_block RMarray[100]; +int RMindex = 0; + +void memory_reserve(int start, int end, char *name) +{ + RMindex++; + RMarray[RMindex].start = start; + RMarray[RMindex].end = end; + strcpy(name, RMarray[RMindex].name); +} \ No newline at end of file diff --git a/Lab4/src/kernel/shell.c b/Lab4/src/kernel/shell.c index 5ee0dfc33..b650d7c84 100644 --- a/Lab4/src/kernel/shell.c +++ b/Lab4/src/kernel/shell.c @@ -5,10 +5,13 @@ #include "device_tree.h" #include "timer.h" #include "page_alloc.h" +#include "dynamic_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 @@ -181,18 +184,40 @@ void shell_main(char *command) int size; size = atoi(argv_buffer); - void *addr = get_page_from_free_list(size); - if (addr == NULL) - printf("FAIL\n"); - else + if (size > MAX_POOL_SIZE) { - printf("SUCCESS\n"); - printf("Get addr %p\n", addr); + // alloc pages + int frame_index = get_page_from_free_list(size); + void *addr = frame_array[frame_index].addr; + if (addr == NULL) + printf("FAIL\n"); + else + { + printf("SUCCESS\n"); + printf("Get frame index %d\n", frame_index); + printf("Get addr 0x%p\n", addr); + } } + else if (size <= MAX_POOL_SIZE) + { + // alloc chunk + int chunk_index = get_chunk(size); + void *addr = chunk_array[chunk_index].addr; + if (addr == NULL) + printf("FAIL\n"); + else + { + printf("SUCCESS\n"); + printf("Get chunk index %d\n", chunk_index); + printf("Get addr 0x%p\n", addr); + } + } + debug(); + debug_pool(); } - else if (!memcmp(command, "free", 4)) + else if (!memcmp(command, "freeFrame", 9)) { - if (command[4] != ' ' || command[5] == '\0') + if (command[9] != ' ' || command[10] == '\0') { printf("Usage: free \n"); return; @@ -200,7 +225,7 @@ void shell_main(char *command) char argv_buffer[ARGV_BUFFER]; memset(argv_buffer, '\0', ARGV_BUFFER); - int i = 5; + int i = 10; while (command[i] != '\0') { argv_buffer[i - 5] = command[i]; @@ -209,7 +234,33 @@ void shell_main(char *command) int index; index = atoi(argv_buffer); - free_page_frame(index); + if (free_page_frame(index) == 0) + debug(); + } + else if (!memcmp(command, "freeChunk", 9)) + { + if (command[9] != ' ' || command[10] == '\0') + { + printf("Usage: freeChunk \n"); + return; + } + + char argv_buffer[ARGV_BUFFER]; + memset(argv_buffer, '\0', ARGV_BUFFER); + int i = 10; + while (command[i] != '\0') + { + argv_buffer[i - 10] = command[i]; + i++; + } + int index; + index = atoi(argv_buffer); + + if (free_chunk(index) == 0) + { + debug(); + debug_pool(); + } } return; From 9b3dd9f9c366969fd9aaaf3a8681fafed0a6c6e6 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 13 Apr 2023 22:40:41 +0800 Subject: [PATCH 10/22] Will work, not good enough --- .vscode/settings.json | 8 ++++++- Lab4/bootloader.img | Bin 7720 -> 7720 bytes Lab4/include/mm.h | 2 +- Lab4/include/page_alloc.h | 10 ++++---- Lab4/include/reserve_mem.h | 10 +++++--- Lab4/kernel8.img | Bin 24168 -> 24576 bytes Lab4/src/kernel/boot.S | 2 ++ Lab4/src/kernel/dynamic_alloc.c | 35 +++++++++++++++++++++++++-- Lab4/src/kernel/kernel.c | 7 +++--- Lab4/src/kernel/page_alloc.c | 41 ++++++++++++++++++++++++++++---- Lab4/src/kernel/reserved_mem.c | 36 ++++++++++++++++++++++++++-- Lab4/src/kernel/shell.c | 9 +++++-- Lab4/src/lib/test_mem_alloc.c | 8 +++++++ Lab4/userprogram.img | Bin 7136 -> 7144 bytes 14 files changed, 145 insertions(+), 23 deletions(-) create mode 100644 Lab4/src/lib/test_mem_alloc.c diff --git a/.vscode/settings.json b/.vscode/settings.json index fd665e208..be8bfe9f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,7 +14,13 @@ "forward_list": "c", "list": "c", "vector": "c", - "math.h": "c" + "math.h": "c", + "functional": "c", + "istream": "c", + "ostream": "c", + "tuple": "c", + "type_traits": "c", + "dynamic_alloc.h": "c" }, "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab4/bootloader.img b/Lab4/bootloader.img index 1f3610a936246a7cec69b6a2a32841fcfe28482b..c1b100217ddda84adfa1932c9574743ae06ddab7 100755 GIT binary patch delta 362 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 fApc&c0q4j6P~RTMN8q~rUeewST-m&7g delta 7039 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 diff --git a/Lab4/src/kernel/boot.S b/Lab4/src/kernel/boot.S index 06ffbe9c1..81fb65548 100644 --- a/Lab4/src/kernel/boot.S +++ b/Lab4/src/kernel/boot.S @@ -55,6 +55,8 @@ bss: 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 diff --git a/Lab4/src/kernel/dynamic_alloc.c b/Lab4/src/kernel/dynamic_alloc.c index 035a1eb5b..e8dd927fa 100644 --- a/Lab4/src/kernel/dynamic_alloc.c +++ b/Lab4/src/kernel/dynamic_alloc.c @@ -13,6 +13,7 @@ void init_pool() { for (int i = 0; i < 33; i++) INIT_LIST_HEAD(&pool[i].list); + return; } chunk *new_chunk() @@ -29,7 +30,7 @@ int get_chunk(int 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); + int frame_index = get_page_from_free_list(MIN_PAGE_SIZE, req_pool_index); split_page(frame_index, req_pool_index); } @@ -146,7 +147,7 @@ int free_chunk(int index) // Check if is OK to free if (index >= global_chunk_index || chunk_array[index].val != ALLOCATED) { - printf("Not allocated yet\n"); + printf("This chunk is Not Allocated yet\n"); return -1; } @@ -157,6 +158,30 @@ int free_chunk(int 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 -1; + } + + if (frame_array[frame_index].chunk_order != -1) + { + // used to allocate chunks + printf("USED for chunk"); + } + else + { + free_page_frame(frame_index); + } +} + int roundup_size(int size) { switch (size) @@ -198,4 +223,10 @@ void debug_pool() printf("NULL\n"); } printf("**\n"); + printf("** DEBUGGING chunk\n"); + for (int i = 0; i < 20; i++) + { + printf("chunk_array[%d].val = %d\n", i, chunk_array[i].val); + } + printf("**\n"); } \ No newline at end of file diff --git a/Lab4/src/kernel/kernel.c b/Lab4/src/kernel/kernel.c index cbef0dcf3..b997687fa 100644 --- a/Lab4/src/kernel/kernel.c +++ b/Lab4/src/kernel/kernel.c @@ -1,8 +1,7 @@ #include "mini_uart.h" #include "shell.h" #include "mbox.h" -#include "page_alloc.h" -#include "dynamic_alloc.h" +#include "reserve_mem.h" #include "device_tree.h" extern void *_dtb_ptr; @@ -14,7 +13,7 @@ void kernel_main(void) fdt_traverse(initramfs_callback, _dtb_ptr); - init_page_frame(); - init_pool(); + memory_init(); + shell_start(); } diff --git a/Lab4/src/kernel/page_alloc.c b/Lab4/src/kernel/page_alloc.c index e9374e6fa..a25e85f57 100644 --- a/Lab4/src/kernel/page_alloc.c +++ b/Lab4/src/kernel/page_alloc.c @@ -2,6 +2,7 @@ #include "math.h" #include "stddef.h" #include "stdlib.h" +#include "reserve_mem.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 @@ -49,9 +50,11 @@ void init_page_frame() frame_array[i].previous = NULL; } } + + return; } -int get_page_from_free_list(int req_size) +int get_page_from_free_list(int req_size, int who) { // int req_num_of_page = -1; // if (req_size % MIN_PAGE_SIZE != 0) @@ -109,15 +112,28 @@ int get_page_from_free_list(int req_size) frame_array[alloc_index + i].allocated_order = alloc_order; } - // // release redundant page (put back to free_list) TODO: FIX THIS BUDDY + // // release redundant page (put back to free_list) // int num_of_redundant_page = (1 << alloc_order) - req_num_of_page; // if (num_of_redundant_page != 0) // put_back_to_free_list(num_of_redundant_page, alloc_index + req_num_of_page); + // 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, -1); + free_page_frame(alloc_index); + alloc_index = new_alloc_index; + } + #ifdef DEBUG debug(); #endif + frame_array[alloc_index].chunk_order = who; return alloc_index; } @@ -162,7 +178,6 @@ void put_back_to_free_list(int num_of_redundant_page, int index) // 從 index int free_page_frame(int index) { - // Check if is OK to free int contiguous_head = frame_array[index].contiguous_head; if (contiguous_head != index) { @@ -207,13 +222,29 @@ int merge_buddy(int index, int buddy, int order) if (order == MAX_ORDER) return order; - remove_from_free_list(free_list[order].next); + printf("===============index = %d, buddy = %d, order = %d================================\n", index, buddy, order); + page_frame_node *iter = free_list[order].next; + while (iter->index != buddy) + { + printf("iter->index = %d\n", iter->index); + 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; } int new_buddy = index ^ (1 << (order + 1)); + + if (new_buddy < index) + { + int tmp = index; + index = new_buddy; + new_buddy = index; + } + if (frame_array[index].val == frame_array[new_buddy].val) return merge_buddy(index, new_buddy, order + 1); else @@ -241,6 +272,8 @@ void debug() { 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) diff --git a/Lab4/src/kernel/reserved_mem.c b/Lab4/src/kernel/reserved_mem.c index 6ade70324..34e45fa1d 100644 --- a/Lab4/src/kernel/reserved_mem.c +++ b/Lab4/src/kernel/reserved_mem.c @@ -1,13 +1,45 @@ #include "reserve_mem.h" -#include "string.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "stdlib.h" reserved_memory_block RMarray[100]; int RMindex = 0; -void memory_reserve(int start, int end, char *name) +void memory_reserve(unsigned long start, unsigned long end, char *name) { RMindex++; RMarray[RMindex].start = start; RMarray[RMindex].end = end; strcpy(name, RMarray[RMindex].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 (start > RMarray[i].start && start > RMarray[i].end) + continue; + else if (start < RMarray[i].start && end < RMarray[i].start) + continue; + else + return i; + } + 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, 0x10000990, "test"); + + return; } \ No newline at end of file diff --git a/Lab4/src/kernel/shell.c b/Lab4/src/kernel/shell.c index b650d7c84..bf826037b 100644 --- a/Lab4/src/kernel/shell.c +++ b/Lab4/src/kernel/shell.c @@ -187,7 +187,7 @@ void shell_main(char *command) if (size > MAX_POOL_SIZE) { // alloc pages - int frame_index = get_page_from_free_list(size); + int frame_index = get_page_from_free_list(size, -1); void *addr = frame_array[frame_index].addr; if (addr == NULL) printf("FAIL\n"); @@ -228,7 +228,7 @@ void shell_main(char *command) int i = 10; while (command[i] != '\0') { - argv_buffer[i - 5] = command[i]; + argv_buffer[i - 10] = command[i]; i++; } int index; @@ -262,6 +262,11 @@ void shell_main(char *command) debug_pool(); } } + else if (!memcmp(command, "debug", 5)) + { + debug(); + debug_pool(); + } return; } diff --git a/Lab4/src/lib/test_mem_alloc.c b/Lab4/src/lib/test_mem_alloc.c new file mode 100644 index 000000000..dd7023eee --- /dev/null +++ b/Lab4/src/lib/test_mem_alloc.c @@ -0,0 +1,8 @@ +#include "dynamic_alloc.h" + +void test_mem_alloc() +{ + // int *a = get_chunk(32); + + return; +} \ No newline at end of file diff --git a/Lab4/userprogram.img b/Lab4/userprogram.img index dbff941a2cb18aab1060c1e14c26fa80637e37a9..38fc65d0f3dace3cfc4f05e514b5b218d6fba362 100755 GIT binary patch delta 566 zcmZ9JPbdUo7{=dkcZ#+Ct+njzsx|ANls2}EgTtV8aZq3GC?PH)PRfweI@s-7e@>E= za*!xB2emG8au|{0q-{eflrjg#`^^l=9A@6I3vARpyIWQnbZn#mkH3ncsrm8vzW4v1IqIL=l0 zoTZ*Dfi23p64&>+Joh{}OFRqCsxN?Cusp3Nv4?kZ#zYV2KB~Ik^Nz*{5BZ$Xs{>Fd zbM!Onb4jI8;pj(~=Ve7^)_1iTDKlwVoAze6RJTj^DD-UTVC=tLp|%FRH$i~R>^&O` z%cRJ*!_n3kzt+u{ySd|kxx>G?A}fYl;=48i87ZI0#sx*|6(GBs%X+nzZL^Bl_oIg!4giyKl~0^NSV^*aOclYo5wb$h_#Vv zw>F00a=A+JD5<@sz@Efw-(AWiinQ8&$1WnLLf$)c%NC-E2og}uZ@*VVqdiWr^JwfF DtMI~S delta 563 zcmZ9JKS;ws6vp4xP^;Ebt<|(Cw5fw2NKwH!I7Fo`4#MH!s2~n?5UkD~;#5JDtA8d@ z5ClO`1R;ZlF^1~Uo(Lfs>0qp0n%yK1djaZ6JV4zvm7Aq6>*$> z_)b&T27w(aIJe__9A0#}=)(pm z)Z6+U^_9vfG}^l8@S^O6m)i!q^T~2=wYqI>c1rK8k^>4uTRAWfOSh=2V>OQffc5kf zpOH0E;JdQYWi}XtV!4w${g*rbn=9~Fxih|RA+WBp#711wjbRCz+9fa`0(hC|=Xp;k z-nxbupajHR5;L5mR7la#0grB|=BN<`GMgbV?!pp5#ET6!X(r3+&1C=2Pg}$`u7_H} zb53m(!DTO_c$L&zWAeAqeceN{Omua1@PS{4&pA^PvL8=>pARKw%@dIrzf;<{skpxZ DaxcN+ From a493c2cd994cac4614d9d0b0b63dd0493c224aef Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 27 Apr 2023 04:20:13 +0800 Subject: [PATCH 11/22] Fix my_malloc and free, I suppose? --- Lab3/kernel8.img | Bin 25088 -> 25624 bytes Lab4/Makefile | 4 +- Lab4/bootloader.img | Bin 7720 -> 7720 bytes Lab4/include/dynamic_alloc.h | 5 +- Lab4/include/page_alloc.h | 3 +- Lab4/include/test_mem_alloc.h | 1 + Lab4/kernel8.img | Bin 24576 -> 24344 bytes Lab4/src/kernel/dynamic_alloc.c | 49 ++++++++------- Lab4/src/kernel/page_alloc.c | 72 +++++++++++++--------- Lab4/src/kernel/reserved_mem.c | 16 ++--- Lab4/src/kernel/shell.c | 102 ++----------------------------- Lab4/src/kernel/test_mem_alloc.c | 29 +++++++++ Lab4/src/lib/test_mem_alloc.c | 8 --- Lab4/userprogram.img | Bin 7144 -> 7136 bytes 14 files changed, 122 insertions(+), 167 deletions(-) create mode 100644 Lab4/include/test_mem_alloc.h create mode 100644 Lab4/src/kernel/test_mem_alloc.c delete mode 100644 Lab4/src/lib/test_mem_alloc.c diff --git a/Lab3/kernel8.img b/Lab3/kernel8.img index ee17699b342c7c7789693e86f3a3e0d1bc961848..02da1da21b1f54f2549a578980535dcc54134b82 100755 GIT binary patch delta 5245 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 E4**Ut%m4rY delta 3751 zcmZu!e{3699sj;}7svT&Cvoh?N$hjdc%9oWAq`F1rpZg%l&z9q+p+w$m+9Q`xLS7%NpeKE<8%ZjC7dkzrZnMd!* zqX!@bn8rEo+dF`39e9o+Cb{f40Lwr37| zSgJY?_%~Fl06uA$UZ-F6@M2{jJz35lQRZlS1@BdVKo`CIiW>7o%V8=LV5Y48CgYxh zL!^j*xnP7Yl<~QO|Fr(R45o_FS!Rr-428ttiSVZZNXac>*eDHW0Gjo)h%+`Y^SGD0 z3LgnxMzxiAv;GXKB+Z)jr^@*8!Ut)KmtQON2VEQryp8@0?_?d9d}Abp{n z$J`ml(LiST;)ez(7-*mjVxhe^_wTIDC*fuNXzaId*+_u40<@xML%RkuI5 zXsZ*dtxj0#n;!lFszr~bwc=Q}N^8TzLq-1JoMTOFb=p$D?cx1Je)^_|zf=^A^o&H9`d$+CtUnqsCk`*SSUYsxzYCiB0qUwkpG3|(2e2!A#LU}mboe9r^s^%5}W3&9jP)aiV| zX*r)BS%<#cp<$E0)1ikq>3t5ZZPt4oI=)%&E^Bg}ZqYj(MS6?A3F$8q zYyU~2lMhw+W9d%48O4t}V%RG4{-vFF#$G_sw({pH{K2@Te$iIHW~pZZPsm#pewx_I z|54FJ(_8snm7i}DViN#|JVNz(0#hL;O2TsUs1wDxa zh`mzqw(VDemt^<;%FG|$SvviOwLFs~ZrKHFyJJrG?R<LeIle78?SVz`@+`PQkE#OUVF`D#fSzu?=93&d~xLO~5=uCv%p zG4X8Y-ePR4>R|3v!)J({OQ^#WlHjV?Hm!%iFyI$ z=j)?1q42-d|Gtu``Z+gVP~A+t8D}7_^7Emu)ZhyeK37+XqL|T)uY;=7m%uo?_(u=# z3Wv&niznID|Gy7dUK zlmcv%fKY?+|7snG@Va?>WDOn`gPG|k8Hf3=T}H%20m!ikfR5{vA}Ul!=g8adbi7(E zoiPfjQRmJ;KgMW0<@h1EdeRQD7+qG3(am8y7Q zk~@$=?-A@&50DA0azfGMwrBOnIEk#P#O2JK0lo3aaEEAxsOVPHj;Ouo;!`4TUa|sv zUZPT**FE{!i1YJoG!pvi?WNPQu^+t^Ct;qMb7pcbdbFqzMB~$C$=xj{D%1sF<@{>T zBoZ^Fe5}_dlx?52R3nl=)x~XRh$ne?dp)7;E`GePm9BB|U*n_R#Xsl^(P~$+e8+J@ zlLh?Lr+UW|1u#YM(==WgzXbDV7k`X=8tI=(`Sc)ES@Ki1eA>o4@=4?)!XCbNNkjV+ zj=t35pRxJ4+v2~2`~iV4lKdwZdxW+WZUGo2u}D;0fOKRrhWjTwvyH z6iW!w8KlK!DS6Gh!+J#Cc9@Q3VcM35Sz!Fxd)MC|Mfr8dPm6W`Vdx4xWwR0F8}PIp ztb-mY3f_0;;&qMP%QB* zEU@I#t|*Bzmk4GW_<3f5UE6 zKu>WJhsz#*)dI;`)MFj1XxC zj;>1zPnLg5+q_cToRLvt@h%+qzr7-$a2Opux%KryiSP|Lpn`H3JsoM1hM zpFThpFmOQ(c?0AhRBQ-h;DzvCKsA62dkSPvULYkhd5@%p;6<>;B|!{M(=IVQSpMnk zWLBws!HsC5YbUo#nKLe&yjRMZv2QY?bh2bAP{rTsgDVa&$6rHsS^nl)>D7#rb!2r| L7=U1MgzN1zPnLg5-@H=XoRLv-@n68KnKLe$yjRMZv41k7bh2a_P{rTsgDVa&$6rHsS;6L7>D7!ZAmPax LvO1F^WG?^!QeTDA diff --git a/Lab4/include/dynamic_alloc.h b/Lab4/include/dynamic_alloc.h index 6cb8b5610..55eb68307 100644 --- a/Lab4/include/dynamic_alloc.h +++ b/Lab4/include/dynamic_alloc.h @@ -10,7 +10,7 @@ typedef struct _chunk { int index; // const - int size; + int size; void *addr; // const int val; int belong_page; @@ -23,12 +23,13 @@ typedef struct _pool_list } pool_list; void init_pool(); -int get_chunk(int req_size); +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/page_alloc.h b/Lab4/include/page_alloc.h index deffe2ffb..0a191e1fe 100644 --- a/Lab4/include/page_alloc.h +++ b/Lab4/include/page_alloc.h @@ -27,12 +27,13 @@ struct _page_frame_node }; 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); +int merge_buddy(int *index, int buddy, int order); void debug(); #endif /*_PAGE_ALLOC_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/kernel8.img b/Lab4/kernel8.img index 58db808854172e309396aeacee00204ce6819402..0abff3df6aba1eee2913fa154853db0008a8f7d4 100755 GIT binary patch delta 7121 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 delta 7313 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 = split_size; @@ -58,10 +61,6 @@ void split_page(int frame_index, int req_pool_index) int remove_a_chunk_from_pool(int req_pool_index) { chunk *alloc_chunk = container_of(pool[req_pool_index].list.next, chunk, list); - printf("** GET chunk\n"); - printf("size = %d\n", alloc_chunk->size); - printf("addr = %p\n", alloc_chunk->addr); - printf("val = %d\n", alloc_chunk->val); list_del_init(pool[req_pool_index].list.next); alloc_chunk->val = ALLOCATED; return alloc_chunk->index; @@ -124,6 +123,7 @@ void put_back_to_pool(int pool_index, int chunk_index) 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); @@ -135,8 +135,7 @@ void put_back_to_pool(int pool_index, int chunk_index) iter->next = iter; iter = tmp_next; } - chunk *tmp = list_entry(iter, chunk, list); - free_page_frame(tmp->belong_page); + free_page_frame(first_chunk_in_page->belong_page); } return; @@ -144,13 +143,6 @@ void put_back_to_pool(int pool_index, int chunk_index) int free_chunk(int index) { - // Check if is OK to free - if (index >= global_chunk_index || chunk_array[index].val != ALLOCATED) - { - printf("This chunk is Not Allocated yet\n"); - return -1; - } - // free the page int pool_index = chunk_array[index].size / MIN_CHUNK_SIZE; put_back_to_pool(pool_index, index); @@ -160,7 +152,6 @@ int free_chunk(int index) 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; @@ -168,17 +159,28 @@ void free(void *addr) if (frame_array[frame_index].val != ALLOCATED) { printf("This page is Not Allocated yet\n"); - return -1; + return; } if (frame_array[frame_index].chunk_order != -1) { - // used to allocate chunks - printf("USED for chunk"); + // 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; } } @@ -226,7 +228,12 @@ void debug_pool() 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/page_alloc.c b/Lab4/src/kernel/page_alloc.c index a25e85f57..c1487c5de 100644 --- a/Lab4/src/kernel/page_alloc.c +++ b/Lab4/src/kernel/page_alloc.c @@ -3,10 +3,12 @@ #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() @@ -54,15 +56,22 @@ void init_page_frame() return; } -int get_page_from_free_list(int req_size, int who) +void *my_malloc(int req_size) { - // int req_num_of_page = -1; - // if (req_size % MIN_PAGE_SIZE != 0) - // req_num_of_page = req_size / MIN_PAGE_SIZE + 1; - // else - // req_num_of_page = req_size / MIN_PAGE_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; +} - // calculate req_order +int get_page_from_free_list(int req_size, int who) +{ int req_order = -1; for (int i = 0; i <= MAX_ORDER; i++) { @@ -112,11 +121,6 @@ int get_page_from_free_list(int req_size, int who) frame_array[alloc_index + i].allocated_order = alloc_order; } - // // release redundant page (put back to free_list) - // int num_of_redundant_page = (1 << alloc_order) - req_num_of_page; - // if (num_of_redundant_page != 0) - // put_back_to_free_list(num_of_redundant_page, alloc_index + req_num_of_page); - // 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); @@ -124,7 +128,7 @@ int get_page_from_free_list(int req_size, int who) if (RM_index != 0) { // Need to change the page allocated - int new_alloc_index = get_page_from_free_list(req_size, -1); + int new_alloc_index = get_page_from_free_list(req_size, who); free_page_frame(alloc_index); alloc_index = new_alloc_index; } @@ -132,7 +136,6 @@ int get_page_from_free_list(int req_size, int who) #ifdef DEBUG debug(); #endif - frame_array[alloc_index].chunk_order = who; return alloc_index; } @@ -191,8 +194,11 @@ int free_page_frame(int index) if (frame_array[buddy_index].val == allocated_order) { // can merge - int merged_order = merge_buddy(index, buddy_index, allocated_order); - add_to_free_list(&free_list[merged_order], index); + 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 { @@ -217,36 +223,44 @@ int free_page_frame(int index) } // return merged order, YES modify frame_array -int merge_buddy(int index, int buddy, int order) +int merge_buddy(int *index, int buddy, int order) { if (order == MAX_ORDER) return order; - printf("===============index = %d, buddy = %d, order = %d================================\n", index, buddy, order); + if (buddy < *index) + { + *index = buddy; + buddy = *index; // Find itself + } + page_frame_node *iter = free_list[order].next; while (iter->index != buddy) - { - printf("iter->index = %d\n", iter->index); iter = iter->next; - } remove_from_free_list(iter); - frame_array[index].val = order + 1; + + frame_array[*index].val = order + 1; for (int i = 1; i < (1 << (order + 1)); i++) { frame_array[i].val = FREE_BUDDY; } - int new_buddy = index ^ (1 << (order + 1)); - if (new_buddy < index) + if (order + 1 == MAX_ORDER) + return order + 1; + + int new_buddy = *index ^ (1 << (order + 1)); + if (new_buddy < *index) { - int tmp = index; - index = new_buddy; - new_buddy = index; + *index = new_buddy; + new_buddy = *index; // Find itself } - if (frame_array[index].val == frame_array[new_buddy].val) + 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; } @@ -268,7 +282,7 @@ void debug() } printf("**\n"); printf("** DEBUGGING frame_array\n"); - for (int i = 0; i < 20; i++) + 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); diff --git a/Lab4/src/kernel/reserved_mem.c b/Lab4/src/kernel/reserved_mem.c index 34e45fa1d..f213a8925 100644 --- a/Lab4/src/kernel/reserved_mem.c +++ b/Lab4/src/kernel/reserved_mem.c @@ -11,7 +11,7 @@ void memory_reserve(unsigned long start, unsigned long end, char *name) RMindex++; RMarray[RMindex].start = start; RMarray[RMindex].end = end; - strcpy(name, RMarray[RMindex].name); + strcpy(RMarray[RMindex].name, name); } // return value : if including RM, return which no. of RM. Otherwise, return 0. @@ -19,12 +19,14 @@ int check_contain_RM(unsigned long start, unsigned long end) { for (int i = 1; i <= RMindex; i++) { - if (start > RMarray[i].start && start > RMarray[i].end) - continue; - else if (start < RMarray[i].start && end < RMarray[i].start) - continue; - else + 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; } @@ -39,7 +41,7 @@ void memory_init() memory_reserve(0x1000000, 0x1000fff, "Printf Buffer"); memory_reserve(0x8000000, 0x8010000, "Initramfs"); memory_reserve(0x15000000, 0x17000000, "User Program"); - memory_reserve(0x1000000, 0x10000990, "test"); + 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 index bf826037b..1f4be8888 100644 --- a/Lab4/src/kernel/shell.c +++ b/Lab4/src/kernel/shell.c @@ -6,12 +6,13 @@ #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]; +// extern page_frame_node frame_array[TOTAL_NUM_PAGE]; +// extern chunk chunk_array[3000]; #define COMMAND_BUFFER 50 #define FILENAME_BUFFER 20 @@ -34,7 +35,7 @@ void shell_main(char *command) 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:\n"); + uart_send_string("alloc\t: [test] malloc and free\n"); } else if (!strcmp(command, "hello")) { @@ -167,100 +168,7 @@ void shell_main(char *command) } else if (!memcmp(command, "alloc", 5)) { - if (command[5] != ' ' || command[6] == '\0') - { - printf("Usage: alloc \n"); - return; - } - - char argv_buffer[ARGV_BUFFER]; - memset(argv_buffer, '\0', ARGV_BUFFER); - int i = 6; - while (command[i] != '\0') - { - argv_buffer[i - 6] = command[i]; - i++; - } - int size; - size = atoi(argv_buffer); - - if (size > MAX_POOL_SIZE) - { - // alloc pages - int frame_index = get_page_from_free_list(size, -1); - void *addr = frame_array[frame_index].addr; - if (addr == NULL) - printf("FAIL\n"); - else - { - printf("SUCCESS\n"); - printf("Get frame index %d\n", frame_index); - printf("Get addr 0x%p\n", addr); - } - } - else if (size <= MAX_POOL_SIZE) - { - // alloc chunk - int chunk_index = get_chunk(size); - void *addr = chunk_array[chunk_index].addr; - if (addr == NULL) - printf("FAIL\n"); - else - { - printf("SUCCESS\n"); - printf("Get chunk index %d\n", chunk_index); - printf("Get addr 0x%p\n", addr); - } - } - debug(); - debug_pool(); - } - else if (!memcmp(command, "freeFrame", 9)) - { - if (command[9] != ' ' || command[10] == '\0') - { - printf("Usage: free \n"); - return; - } - - char argv_buffer[ARGV_BUFFER]; - memset(argv_buffer, '\0', ARGV_BUFFER); - int i = 10; - while (command[i] != '\0') - { - argv_buffer[i - 10] = command[i]; - i++; - } - int index; - index = atoi(argv_buffer); - - if (free_page_frame(index) == 0) - debug(); - } - else if (!memcmp(command, "freeChunk", 9)) - { - if (command[9] != ' ' || command[10] == '\0') - { - printf("Usage: freeChunk \n"); - return; - } - - char argv_buffer[ARGV_BUFFER]; - memset(argv_buffer, '\0', ARGV_BUFFER); - int i = 10; - while (command[i] != '\0') - { - argv_buffer[i - 10] = command[i]; - i++; - } - int index; - index = atoi(argv_buffer); - - if (free_chunk(index) == 0) - { - debug(); - debug_pool(); - } + test_mem_alloc(); } else if (!memcmp(command, "debug", 5)) { 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/lib/test_mem_alloc.c b/Lab4/src/lib/test_mem_alloc.c deleted file mode 100644 index dd7023eee..000000000 --- a/Lab4/src/lib/test_mem_alloc.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "dynamic_alloc.h" - -void test_mem_alloc() -{ - // int *a = get_chunk(32); - - return; -} \ No newline at end of file diff --git a/Lab4/userprogram.img b/Lab4/userprogram.img index 38fc65d0f3dace3cfc4f05e514b5b218d6fba362..dbff941a2cb18aab1060c1e14c26fa80637e37a9 100755 GIT binary patch delta 563 zcmZ9JKS;ws6vp4xP^;Ebt<|(Cw5fw2NKwH!I7Fo`4#MH!s2~n?5UkD~;#5JDtA8d@ z5ClO`1R;ZlF^1~Uo(Lfs>0qp0n%yK1djaZ6JV4zvm7Aq6>*$> z_)b&T27w(aIJe__9A0#}=)(pm z)Z6+U^_9vfG}^l8@S^O6m)i!q^T~2=wYqI>c1rK8k^>4uTRAWfOSh=2V>OQffc5kf zpOH0E;JdQYWi}XtV!4w${g*rbn=9~Fxih|RA+WBp#711wjbRCz+9fa`0(hC|=Xp;k z-nxbupajHR5;L5mR7la#0grB|=BN<`GMgbV?!pp5#ET6!X(r3+&1C=2Pg}$`u7_H} zb53m(!DTO_c$L&zWAeAqeceN{Omua1@PS{4&pA^PvL8=>pARKw%@dIrzf;<{skpxZ DaxcN+ delta 566 zcmZ9JPbdUo7{=dkcZ#+Ct+njzsx|ANls2}EgTtV8aZq3GC?PH)PRfweI@s-7e@>E= za*!xB2emG8au|{0q-{eflrjg#`^^l=9A@6I3vARpyIWQnbZn#mkH3ncsrm8vzW4v1IqIL=l0 zoTZ*Dfi23p64&>+Joh{}OFRqCsxN?Cusp3Nv4?kZ#zYV2KB~Ik^Nz*{5BZ$Xs{>Fd zbM!Onb4jI8;pj(~=Ve7^)_1iTDKlwVoAze6RJTj^DD-UTVC=tLp|%FRH$i~R>^&O` z%cRJ*!_n3kzt+u{ySd|kxx>G?A}fYl;=48i87ZI0#sx*|6(GBs%X+nzZL^Bl_oIg!4giyKl~0^NSV^*aOclYo5wb$h_#Vv zw>F00a=A+JD5<@sz@Efw-(AWiinQ8&$1WnLLf$)c%NC-E2og}uZ@*VVqdiWr^JwfF DtMI~S From 560508a7f2f806709b1d51f6e93d5038b1229ee3 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 27 Apr 2023 04:20:53 +0800 Subject: [PATCH 12/22] Prepare for Lab5 --- Lab5/Makefile | 59 ++ Lab5/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 32753 bytes Lab5/bootloader.img | Bin 0 -> 7720 bytes Lab5/bootloader.py | 25 + Lab5/include/device_tree.h | 9 + Lab5/include/dynamic_alloc.h | 35 + Lab5/include/list.h | 441 ++++++++++ Lab5/include/load_kernel.h | 7 + Lab5/include/math.h | 1 + Lab5/include/mbox.h | 6 + Lab5/include/mbox_call.h | 9 + Lab5/include/mini_uart.h | 17 + Lab5/include/mm.h | 19 + Lab5/include/page_alloc.h | 39 + Lab5/include/peripherals/base.h | 6 + Lab5/include/peripherals/device_tree.h | 16 + Lab5/include/peripherals/gpio.h | 25 + Lab5/include/peripherals/irq.h | 27 + Lab5/include/peripherals/mbox_call.h | 40 + Lab5/include/peripherals/mini_uart.h | 19 + Lab5/include/peripherals/reboot.h | 8 + Lab5/include/printf.h | 109 +++ Lab5/include/read_cpio.h | 9 + Lab5/include/reboot.h | 8 + Lab5/include/reserve_mem.h | 10 + Lab5/include/shell.h | 7 + Lab5/include/stdlib.h | 25 + Lab5/include/test_mem_alloc.h | 1 + Lab5/include/timer.h | 13 + Lab5/include/utils.h | 8 + Lab5/initramfs.cpio | Bin 0 -> 10240 bytes Lab5/kernel8.img | Bin 0 -> 24344 bytes Lab5/rootfs/a.c | 8 + Lab5/rootfs/cat.txt | 1 + Lab5/rootfs/one | 3 + Lab5/rootfs/ts.txt | 0 Lab5/rootfs/userprogram.img | Bin 0 -> 9240 bytes Lab5/send_kernel.py | 40 + Lab5/src/bootloader/boot.S | 25 + Lab5/src/bootloader/bootloader.c | 15 + Lab5/src/bootloader/config.txt | 3 + Lab5/src/bootloader/link.ld | 21 + Lab5/src/bootloader/load_kernel.c | 67 ++ Lab5/src/kernel/boot.S | 88 ++ Lab5/src/kernel/config.txt | 2 + Lab5/src/kernel/device_tree.c | 266 ++++++ Lab5/src/kernel/dynamic_alloc.c | 239 ++++++ Lab5/src/kernel/exception.S | 135 +++ Lab5/src/kernel/exception.c | 150 ++++ Lab5/src/kernel/kernel.c | 19 + Lab5/src/kernel/link.ld | 19 + Lab5/src/kernel/mbox.c | 63 ++ Lab5/src/kernel/mbox_call.c | 42 + Lab5/src/kernel/page_alloc.c | 299 +++++++ Lab5/src/kernel/read_cpio.c | 151 ++++ Lab5/src/kernel/reboot.c | 19 + Lab5/src/kernel/reserved_mem.c | 47 ++ Lab5/src/kernel/shell.c | 231 ++++++ Lab5/src/kernel/test_mem_alloc.c | 29 + Lab5/src/kernel/timer.S | 16 + Lab5/src/kernel/timer.c | 150 ++++ Lab5/src/lib/hex2int.c | 15 + Lab5/src/lib/math.c | 9 + Lab5/src/lib/mem.c | 22 + Lab5/src/lib/mini_uart.c | 183 +++++ Lab5/src/lib/mm.S | 6 + Lab5/src/lib/printf.c | 1046 ++++++++++++++++++++++++ Lab5/src/lib/queue.c | 7 + Lab5/src/lib/simple_malloc.c | 8 + Lab5/src/lib/string.c | 79 ++ Lab5/src/lib/utils.S | 15 + Lab5/src/userprogram/link.ld | 19 + Lab5/src/userprogram/user.S | 11 + Lab5/userprogram.img | Bin 0 -> 7136 bytes 74 files changed, 4566 insertions(+) create mode 100644 Lab5/Makefile create mode 100644 Lab5/bcm2710-rpi-3-b-plus.dtb create mode 100755 Lab5/bootloader.img create mode 100644 Lab5/bootloader.py create mode 100644 Lab5/include/device_tree.h create mode 100644 Lab5/include/dynamic_alloc.h create mode 100644 Lab5/include/list.h create mode 100644 Lab5/include/load_kernel.h create mode 100644 Lab5/include/math.h create mode 100644 Lab5/include/mbox.h create mode 100644 Lab5/include/mbox_call.h create mode 100644 Lab5/include/mini_uart.h create mode 100644 Lab5/include/mm.h create mode 100644 Lab5/include/page_alloc.h create mode 100644 Lab5/include/peripherals/base.h create mode 100644 Lab5/include/peripherals/device_tree.h create mode 100644 Lab5/include/peripherals/gpio.h create mode 100644 Lab5/include/peripherals/irq.h create mode 100644 Lab5/include/peripherals/mbox_call.h create mode 100644 Lab5/include/peripherals/mini_uart.h create mode 100644 Lab5/include/peripherals/reboot.h create mode 100644 Lab5/include/printf.h create mode 100644 Lab5/include/read_cpio.h create mode 100644 Lab5/include/reboot.h create mode 100644 Lab5/include/reserve_mem.h create mode 100644 Lab5/include/shell.h create mode 100644 Lab5/include/stdlib.h create mode 100644 Lab5/include/test_mem_alloc.h create mode 100644 Lab5/include/timer.h create mode 100644 Lab5/include/utils.h create mode 100644 Lab5/initramfs.cpio create mode 100755 Lab5/kernel8.img create mode 100644 Lab5/rootfs/a.c create mode 100644 Lab5/rootfs/cat.txt create mode 100644 Lab5/rootfs/one create mode 100644 Lab5/rootfs/ts.txt create mode 100755 Lab5/rootfs/userprogram.img create mode 100644 Lab5/send_kernel.py create mode 100644 Lab5/src/bootloader/boot.S create mode 100644 Lab5/src/bootloader/bootloader.c create mode 100644 Lab5/src/bootloader/config.txt create mode 100644 Lab5/src/bootloader/link.ld create mode 100644 Lab5/src/bootloader/load_kernel.c create mode 100644 Lab5/src/kernel/boot.S create mode 100644 Lab5/src/kernel/config.txt create mode 100644 Lab5/src/kernel/device_tree.c create mode 100644 Lab5/src/kernel/dynamic_alloc.c create mode 100644 Lab5/src/kernel/exception.S create mode 100644 Lab5/src/kernel/exception.c create mode 100644 Lab5/src/kernel/kernel.c create mode 100644 Lab5/src/kernel/link.ld create mode 100644 Lab5/src/kernel/mbox.c create mode 100644 Lab5/src/kernel/mbox_call.c create mode 100644 Lab5/src/kernel/page_alloc.c create mode 100644 Lab5/src/kernel/read_cpio.c create mode 100644 Lab5/src/kernel/reboot.c create mode 100644 Lab5/src/kernel/reserved_mem.c create mode 100644 Lab5/src/kernel/shell.c create mode 100644 Lab5/src/kernel/test_mem_alloc.c create mode 100644 Lab5/src/kernel/timer.S create mode 100644 Lab5/src/kernel/timer.c create mode 100644 Lab5/src/lib/hex2int.c create mode 100644 Lab5/src/lib/math.c create mode 100644 Lab5/src/lib/mem.c create mode 100644 Lab5/src/lib/mini_uart.c create mode 100644 Lab5/src/lib/mm.S create mode 100644 Lab5/src/lib/printf.c create mode 100644 Lab5/src/lib/queue.c create mode 100644 Lab5/src/lib/simple_malloc.c create mode 100644 Lab5/src/lib/string.c create mode 100644 Lab5/src/lib/utils.S create mode 100644 Lab5/src/userprogram/link.ld create mode 100644 Lab5/src/userprogram/user.S create mode 100755 Lab5/userprogram.img diff --git a/Lab5/Makefile b/Lab5/Makefile new file mode 100644 index 000000000..b80e7f409 --- /dev/null +++ b/Lab5/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/Lab5/bcm2710-rpi-3-b-plus.dtb b/Lab5/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..0dd0e2f435a74e6a0c01e2a30fa0246d55ee3091 GIT binary patch literal 32753 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 OPqI~*e!TdI|Nj8Si!WyY literal 0 HcmV?d00001 diff --git a/Lab5/bootloader.img b/Lab5/bootloader.img new file mode 100755 index 0000000000000000000000000000000000000000..fc1cd073afe3a14c01152fb75e50ca25db3ae1f6 GIT binary patch literal 7720 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 literal 0 HcmV?d00001 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..12ec374b1 --- /dev/null +++ b/Lab5/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/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/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..f33833343 --- /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, char *filename); +int load_userprogram(char *cpioDest, char *userDest); + +#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..1bb8208a9 --- /dev/null +++ b/Lab5/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/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..715d7bcda --- /dev/null +++ b/Lab5/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/Lab5/include/test_mem_alloc.h b/Lab5/include/test_mem_alloc.h new file mode 100644 index 000000000..728855f3d --- /dev/null +++ b/Lab5/include/test_mem_alloc.h @@ -0,0 +1 @@ +void test_mem_alloc(); \ No newline at end of file diff --git a/Lab5/include/timer.h b/Lab5/include/timer.h new file mode 100644 index 000000000..df945b2ea --- /dev/null +++ b/Lab5/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/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/initramfs.cpio b/Lab5/initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..55d33a5527daaa16aea9f6474c77f21cc807a82a GIT binary patch literal 10240 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 p!8f+|TQm0;P&2l!-ZK(SGbq2EF(Dx{LH-2E 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..83efd9631fdcd22cd665917b5808106cd758bd29 GIT binary patch literal 9240 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 literal 0 HcmV?d00001 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..f6b6f7474 --- /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: + 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/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..bcb5b17ce --- /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 *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/Lab5/src/kernel/dynamic_alloc.c b/Lab5/src/kernel/dynamic_alloc.c new file mode 100644 index 000000000..f42b15d3d --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/exception.S b/Lab5/src/kernel/exception.S new file mode 100644 index 000000000..ba3cfbc06 --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/exception.c b/Lab5/src/kernel/exception.c new file mode 100644 index 000000000..cc1c2fda6 --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/kernel.c b/Lab5/src/kernel/kernel.c new file mode 100644 index 000000000..b997687fa --- /dev/null +++ b/Lab5/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/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/page_alloc.c b/Lab5/src/kernel/page_alloc.c new file mode 100644 index 000000000..c1487c5de --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/read_cpio.c b/Lab5/src/kernel/read_cpio.c new file mode 100644 index 000000000..665e57070 --- /dev/null +++ b/Lab5/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/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..f213a8925 --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/shell.c b/Lab5/src/kernel/shell.c new file mode 100644 index 000000000..1f4be8888 --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/test_mem_alloc.c b/Lab5/src/kernel/test_mem_alloc.c new file mode 100644 index 000000000..50edee4c2 --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/timer.S b/Lab5/src/kernel/timer.S new file mode 100644 index 000000000..9e0c519e9 --- /dev/null +++ b/Lab5/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/Lab5/src/kernel/timer.c b/Lab5/src/kernel/timer.c new file mode 100644 index 000000000..f1a628220 --- /dev/null +++ b/Lab5/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/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..d74fb96fd --- /dev/null +++ b/Lab5/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/Lab5/src/lib/mini_uart.c b/Lab5/src/lib/mini_uart.c new file mode 100644 index 000000000..82b34aa13 --- /dev/null +++ b/Lab5/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/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..28c90843a --- /dev/null +++ b/Lab5/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/Lab5/src/userprogram/user.S b/Lab5/src/userprogram/user.S new file mode 100644 index 000000000..8a5ea0c0b --- /dev/null +++ b/Lab5/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/Lab5/userprogram.img b/Lab5/userprogram.img new file mode 100755 index 0000000000000000000000000000000000000000..dbff941a2cb18aab1060c1e14c26fa80637e37a9 GIT binary patch literal 7136 zcmdT}dvH|c6+icpWyyj-b`v0p+}+>^}*lAtoVn-J}o&PbBA(~3?WIF6c*c8L!p zw7DTTBIzTGL0jyYgzCty)!Ot#^0BLjzM1h_HcW`#X2lC)-nwf?iZ0$7G`=6J6a1sAME{A^l0Vig`%krG z_`j7Xa6%FSF-Z)Zl%&9zBnOU38G&~pQ}0lNZkhIlODUF5N>)g=mWEr^V4pz6luadf z6D?Kb1#2|m;(3=$HQ`nYhFjEN+plT3X^VSo55CQW4m`JWJq>T~a*v6?Tse*Qi59K4 z#X`QOdzO@?=sTdZCQu3U<2h$n-PrVt&^twEi9!#L+?A=0^-1JOsO0UHF2h4j{y1UbDJ#WxhjK-`!dGok4y6dimZ;ftmHWkOgiJLa1KeJ za?-ldZ zbq$~wY~ynOl}OAz2ETlSQJ6J8zYp)dG`plR4I|U`(!3Hu9;fTa+*$16x!5DXUMx8} zyG8rnQRt9|brC5$@H%W0mF5M)(%gV9R4?`WE>njk+HGMK(eoZp$`pKI_p{2^u!Erg6-(XlsN zs>XnsY{N)u{t;6xB_upi8x+?IIi-?Ti-L{opt^U zx~JmJ*;O^R<8Z?B2<8%%!oGN`=#O`h?`6dIkyUDNBhl;6Kbn_yL|$2XM9L`KDYTAU zYt?G6M7%W5c`Ud2a>~lUzHGjRY7Xxqp9p^sO4h(8>$}^mKFc#!e9zoPb8f*nU9j1% z!KhqG-bL-3bDzgv_S1~cMZ-!N)Ax{NX#1l2vFT?Lo+EY&4%Vr`K^p}RO8i&~4@j34 z4nl?_k$5`ZTHyy?AMattwV(Ip|B(*SGw0WV^OhTXl66bvu01k!r*c-JLxi6Mf4q`> zqpKwNS%#l8{4wl@lgLx;$aCRJinSxxg>S|CJzJ~Q++C3N0 zB?r2EC6aewdrxk6Ci&j{p1LT#f>#GdWryH()v^!f6|s%7;X4gFDX$1#&d;g&S?p68 zG>|g&o_XNb0X*7F!C1syG%zOy<;gYW$dF;PT+pXRl3Q`XT_@%=zEb< znLbHt53B~QoAQb*qE@Rk2;TNv$W!oOewJWeSt>60acMz;T3WE^M+P3cYBC-f@*~Jc zhzaDQ9ONTQA#z{spd=Tj^RjLd zp!ajo8@XGa?OxZI!Tw%ZQn6OaN}umLk?`!6VUOu*Y16EvJ=B>~+oK2jYNAaZTd&k@ zd~W0I6EWVChLK~0uBT%59etUYT4$ zdi1gLVVl68{WUS49yRBt=B)a?D(F%+eB)fjx*-rOseTcE*wBj zd~WTIbHqFszCvBiwR?kw=J0vM=gFOrubSu6?dQQFn(^*7>xZA3bP?1Ks#!nO@EqTT zfXDwq!h<@#ra`1?y_{mN;!JG;u6*E#K<0pXZt)qXOZkPI`@RzG2DaBhtCy>>H$Y!$ z%5ln*<2)Z{g^n|cYX*6C&*z$?E(^(2yit;IpU4j^L=DnGR9sH-P#<&-qh5AV{@(C2 z6s)A2tSI!~fU}^Sa++OTPv!Ew8|QPlhl0CBYl+iJ#cao|Quf{@IA3^wPxAkSJ@W4< z_Q(L{7*CkTXVjomra5h*(CoJn`=Lf4p_={XB-~}HDRw^J>D6j%r$|FW%74H+iaA5? zvBc8Tz&aZ^zm_0 z{J?Fn`zUC2;AMY)jap+p=>LS;kg;Oyv>U!mzaz3Q?S?ORiur~w4?}0hmC79#vmcrM zPCs)_#S?S6M(29#SX~9`_M7}7dIbvZfvjUi*Vb*xS}9rk=u&p!=&DNG5mw-i!1qA6 zANL32-lW53uL09dh<83;-v)0BYJ|8scM7AhHaE+tzfmtWzMb%J?et&JX*j>1Fh{QS zzJBwaPPAT5A$W)##>)D`ZzeZW06*hMcXJg*g>)Vk5lYGC&do_o??2Qwmht8 z%OeZ5<oCkqZp9|T*76G;>unhv+eqcKQYzKiYBFVl;9`sW1R7$=GFh(-8tVlKE!FVf= zl_oC6m%=3^aRHx!tATvKG;n=<4zBNlDGE$cV2T1$6qusG6qU8CC@@8VDGE%3b1VIW za|Jvr@T4$_sObgl$8k*6=U{rvuvNhf)zGJ4hC5B4$#%ZcSo@Bt&)de@yBw+^_b!L~ zf^vUotleX-{j0Gy3cn1(FZ0J>%{=)1 zjIpo&Vc3QHqYm7ApUm9czhv-*5#+akCQx@Maa~Ez8Qd$tk9$w?*4+l?u;eHt>*jtZ zXdeV!Pa%0ZU-u%%455d^^+CMV+i<9apx}PyPj4{v)glz+0YhBd42Ip%b z`)xMn1^8FXLQfsEGuYnveHq4hmqAIoobS{={V%cZNey)}(p6Vz4g^`c&p14)|%ntgN|5TxlvIqW* zIf73cK4>UY5Oa-e^Yg@;u`W^-Gx8bMK8v-?V~Zh&x}3TVwbZ!$zZvu6@?XOHS^40@ zYdbJEF=kVGr}jjj$={`2=PQuaAolW{g);gD*rNh;aY1+I`vnI62JFq0t=KjxTl@|@ z3_F2`C~Lvgx^nyO9$GKuRX}DriOsyfcu(>?=c+;M zMcAN**BrtBIyi^fkkblep{4?Msdu3FjJ&uJdC&C)`gqt|t3lr;(_#(t^o#sD^iP)+ zgeXam9^WGLFb#UlG5vAKDcCs9&zkyvhhTi?T7=~qxdQl+Q@hkpt73xc>U4ja5#xEz z$?3Vck9%*xv>JJe*QV!T(V>=cY%Rq6YNBfXz5%(d0ayy~Z8z31Ps0Bo@IS6)5q#r% zqzPw20qTDK-bS>HX?ULNI{|bqndnm*qm2LEnBNdjD80x3j^;Uym!IV)$MG-c{`wz+dLYpN literal 0 HcmV?d00001 From 7f597d754aff2efab5dac7bd4f9ecf09a0e80528 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Wed, 3 May 2023 15:58:20 +0800 Subject: [PATCH 13/22] thread finished, NOT tested yet --- .vscode/settings.json | 6 +- Lab5/bootloader.img | Bin 7720 -> 7736 bytes Lab5/include/reserve_mem.h | 10 +- Lab5/include/test.h | 7 + Lab5/include/test_mem_alloc.h | 1 - Lab5/include/thread.h | 58 +++++ Lab5/include/timer.h | 1 + Lab5/kernel8.img | Bin 24344 -> 28128 bytes Lab5/src/bootloader/boot.S | 4 +- Lab5/src/kernel/dynamic_alloc.c | 22 +- Lab5/src/kernel/exception.S | 10 + Lab5/src/kernel/kernel.c | 3 + Lab5/src/kernel/page_alloc.c | 26 +-- Lab5/src/kernel/reserved_mem.c | 1 + Lab5/src/kernel/shell.c | 7 +- Lab5/src/kernel/{test_mem_alloc.c => test.c} | 19 +- Lab5/src/kernel/thread.S | 26 +++ Lab5/src/kernel/thread.c | 221 +++++++++++++++++++ Lab5/src/kernel/timer.S | 7 + Lab5/src/kernel/timer.c | 22 ++ 20 files changed, 420 insertions(+), 31 deletions(-) create mode 100644 Lab5/include/test.h delete mode 100644 Lab5/include/test_mem_alloc.h create mode 100644 Lab5/include/thread.h rename Lab5/src/kernel/{test_mem_alloc.c => test.c} (52%) create mode 100644 Lab5/src/kernel/thread.S create mode 100644 Lab5/src/kernel/thread.c diff --git a/.vscode/settings.json b/.vscode/settings.json index be8bfe9f7..ffd8e84ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,7 +20,11 @@ "ostream": "c", "tuple": "c", "type_traits": "c", - "dynamic_alloc.h": "c" + "dynamic_alloc.h": "c", + "page_alloc.h": "c", + "stdint.h": "c", + "device_tree.h": "c", + "list.h": "c" }, "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab5/bootloader.img b/Lab5/bootloader.img index fc1cd073afe3a14c01152fb75e50ca25db3ae1f6..4d9008127ffb956e125a3c2ea48d59a2ae75411f 100755 GIT binary patch delta 380 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 delta 6931 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 XFIW%ia_Z9L)TP;tt$&rTU0D2o2z?@~ diff --git a/Lab5/src/bootloader/boot.S b/Lab5/src/bootloader/boot.S index f6b6f7474..403960d2e 100644 --- a/Lab5/src/bootloader/boot.S +++ b/Lab5/src/bootloader/boot.S @@ -14,8 +14,8 @@ proc_hang: b proc_hang master: - adr x0, __bss_start - adr x1, __bss_end + ldr x0, =__bss_start + ldr x1, =__bss_end sub x1, x1, x0 bl memzero diff --git a/Lab5/src/kernel/dynamic_alloc.c b/Lab5/src/kernel/dynamic_alloc.c index f42b15d3d..45d103e0d 100644 --- a/Lab5/src/kernel/dynamic_alloc.c +++ b/Lab5/src/kernel/dynamic_alloc.c @@ -225,15 +225,15 @@ void debug_pool() 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"); + // 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 index ba3cfbc06..eb53f0a46 100644 --- a/Lab5/src/kernel/exception.S +++ b/Lab5/src/kernel/exception.S @@ -3,6 +3,16 @@ .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 diff --git a/Lab5/src/kernel/kernel.c b/Lab5/src/kernel/kernel.c index b997687fa..91cf55583 100644 --- a/Lab5/src/kernel/kernel.c +++ b/Lab5/src/kernel/kernel.c @@ -3,6 +3,8 @@ #include "mbox.h" #include "reserve_mem.h" #include "device_tree.h" +#include "thread.h" + extern void *_dtb_ptr; void kernel_main(void) @@ -13,6 +15,7 @@ void kernel_main(void) fdt_traverse(initramfs_callback, _dtb_ptr); + thread_init(); memory_init(); shell_start(); diff --git a/Lab5/src/kernel/page_alloc.c b/Lab5/src/kernel/page_alloc.c index c1487c5de..464bb27e8 100644 --- a/Lab5/src/kernel/page_alloc.c +++ b/Lab5/src/kernel/page_alloc.c @@ -281,19 +281,19 @@ void debug() 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"); + // 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/Lab5/src/kernel/reserved_mem.c b/Lab5/src/kernel/reserved_mem.c index f213a8925..2f466ae4b 100644 --- a/Lab5/src/kernel/reserved_mem.c +++ b/Lab5/src/kernel/reserved_mem.c @@ -1,6 +1,7 @@ #include "reserve_mem.h" #include "page_alloc.h" #include "dynamic_alloc.h" +#include "reserve_mem.h" #include "stdlib.h" reserved_memory_block RMarray[100]; diff --git a/Lab5/src/kernel/shell.c b/Lab5/src/kernel/shell.c index 1f4be8888..4f2e1b6dd 100644 --- a/Lab5/src/kernel/shell.c +++ b/Lab5/src/kernel/shell.c @@ -6,7 +6,7 @@ #include "timer.h" #include "page_alloc.h" #include "dynamic_alloc.h" -#include "test_mem_alloc.h" +#include "test.h" extern void *_dtb_ptr; extern char *cpioDest; @@ -36,6 +36,7 @@ void shell_main(char *command) 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"); } else if (!strcmp(command, "hello")) { @@ -175,6 +176,10 @@ void shell_main(char *command) debug(); debug_pool(); } + else if (!memcmp(command, "thread", 5)) + { + test_thread(); + } return; } diff --git a/Lab5/src/kernel/test_mem_alloc.c b/Lab5/src/kernel/test.c similarity index 52% rename from Lab5/src/kernel/test_mem_alloc.c rename to Lab5/src/kernel/test.c index 50edee4c2..69a1e1e81 100644 --- a/Lab5/src/kernel/test_mem_alloc.c +++ b/Lab5/src/kernel/test.c @@ -1,6 +1,7 @@ +#include "stdlib.h" #include "dynamic_alloc.h" #include "page_alloc.h" -#include "stdlib.h" +#include "thread.h" void test_mem_alloc() { @@ -25,5 +26,21 @@ void test_mem_alloc() printf("a = %p\n", a); free(a); + return; +} + +void test_thread() +{ + thread_info *thread_ret; + thread_ret = thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + thread_create(test_mem_alloc); + + debug_task_rq(); return; } \ 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..514958efa --- /dev/null +++ b/Lab5/src/kernel/thread.S @@ -0,0 +1,26 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ 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..5edb79b0d --- /dev/null +++ b/Lab5/src/kernel/thread.c @@ -0,0 +1,221 @@ +#include "stdlib.h" +#include "thread.h" +#include "page_alloc.h" +#include "dynamic_alloc.h" +#include "reserve_mem.h" +#include "list.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 *); + +long thread_cnt = 0; + +task_struct idle = {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 = &idle; + if (cur != &idle) + add_rq(cur); + + set_switch_timer(); + enable_interrupt(); + + if (next->status == FORKING) + { + add_rq(next); + switch_to(cur, &idle); + } + else if (next->status == ZOMBIE) + { + INIT_LIST_HEAD(&next->list); + list_add_tail(&next->list, &task_zombieq_head); + switch_to(cur, &idle); + } + 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.prev; + if (ret != &task_rq_head) + list_del_init(ret); + else + ret = NULL; + return container_of(ret, task_struct, list); +} + +void thread_init() +{ + INIT_LIST_HEAD(&task_rq_head); + INIT_LIST_HEAD(&task_zombieq_head); + 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)); + trapframe *new_trapframe = (trapframe *)my_malloc(sizeof(trapframe)); + + new_task->thread_info = new_thread; + new_task->trapframe = new_trapframe; + 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; + + add_rq(new_task); + + return new_thread; +} + +/* threads' routine for any task */ +void task_wrapper() +{ + task_struct *current = get_current(); + (current->job)(); + // exit(0); TODO +} + +void idle_task() +{ + while (!list_empty(&task_rq_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); + free(tmp->trapframe); + free(tmp); + } +} + +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 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 = 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"); +} \ No newline at end of file diff --git a/Lab5/src/kernel/timer.S b/Lab5/src/kernel/timer.S index 9e0c519e9..8f8d7185e 100644 --- a/Lab5/src/kernel/timer.S +++ b/Lab5/src/kernel/timer.S @@ -9,6 +9,13 @@ core_timer_enable: str w0, [x1] // unmask timer interrupt 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. diff --git a/Lab5/src/kernel/timer.c b/Lab5/src/kernel/timer.c index f1a628220..b94f53cef 100644 --- a/Lab5/src/kernel/timer.c +++ b/Lab5/src/kernel/timer.c @@ -147,4 +147,26 @@ void el1_timer_handler(long cntpct_el0, long cntfrq_el0) 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 From 4d3f717d7b412963664e987552b19f94b885eb10 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 4 May 2023 00:14:18 +0800 Subject: [PATCH 14/22] thread tested, syscall NOT tested --- .vscode/settings.json | 3 +- Lab5/bootloader.img | Bin 7736 -> 7800 bytes Lab5/include/mini_uart.h | 3 + Lab5/include/read_cpio.h | 4 +- Lab5/include/syscall.h | 15 ++++ Lab5/include/thread.h | 3 +- Lab5/kernel8.img | Bin 28128 -> 30832 bytes Lab5/src/kernel/device_tree.c | 4 +- Lab5/src/kernel/dynamic_alloc.c | 41 +++++---- Lab5/src/kernel/exception.S | 68 ++++++++++++--- Lab5/src/kernel/exception.c | 75 +++++++++++++++++ Lab5/src/kernel/kernel.c | 1 + Lab5/src/kernel/page_alloc.c | 35 ++++---- Lab5/src/kernel/read_cpio.c | 26 ++++-- Lab5/src/kernel/shell.c | 22 +---- Lab5/src/kernel/syscall.c | 143 ++++++++++++++++++++++++++++++++ Lab5/src/kernel/test.c | 39 ++++++++- Lab5/src/kernel/thread.S | 5 ++ Lab5/src/kernel/thread.c | 44 ++++++++-- Lab5/src/kernel/timer.S | 8 +- Lab5/src/kernel/timer.c | 17 ++-- Lab5/src/lib/mini_uart.c | 10 +++ Lab5/userprogram.img | Bin 7136 -> 7200 bytes 23 files changed, 465 insertions(+), 101 deletions(-) create mode 100644 Lab5/include/syscall.h create mode 100644 Lab5/src/kernel/syscall.c diff --git a/.vscode/settings.json b/.vscode/settings.json index ffd8e84ee..d345f6da2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,7 +24,8 @@ "page_alloc.h": "c", "stdint.h": "c", "device_tree.h": "c", - "list.h": "c" + "list.h": "c", + "thread.h": "c" }, "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab5/bootloader.img b/Lab5/bootloader.img index 4d9008127ffb956e125a3c2ea48d59a2ae75411f..13c2199574faa820fbb83d5e55b586b235fda35e 100755 GIT binary patch delta 804 zcmZ9KO=uHA6vy9WBZ;LcB(d3;#!WVf+KOPM&@44zVh?-sW2v`-?ahyaNTHxTWKatd zFS|^zUR01mZ4MIMgYH3WE~4NmptnK_-UP*nkOkxVW_L@Yd)Rqz-v9r5Z)fLqsaMhq zhc1So?u4$s>aa|=v<5dw~-zV}Cdbga= zbVo=u#xSM{P@V)vzsim?=Ndp^r^pOB&X!4uD5gmsjS`(*5Ra0(?521kdW(1f2-n)# zFB{!u(-Mn>jI$p}v6{sq0jkj$Z37n5T#T`V>yj2Q2t3p}LcEdi_mjS!??o||xfeWe z1oQZCtv!Vt&g7b~ucv=(wYnL5Be z9z3iRRC~&C*4zPn_zuCN6RFAb$5eYqfCk@q zjv~H3b>4;U~6Z+8Bf6zCBP~>ngW1Fdb?uAc~VaCV>%hqfVqWK_LkcJq+5#$2< zmdaHhqYQtX4k!x*spXYa`}F0Ul0o@Tyd3?Nx0`(d@c3~c)Iqpx$@Hw10dlQIU@m}Y zUd!DZ@%-beEx%t-cK`7z_UNy)o^1)2K9hlusRAsz%`<4cJxn!TbcaE}yFbp_zs>iK z^2PvgZ@6`h&U#0EWuVTQV;_dns!huB{YpAy-bt^LnC@Tq-tM)}Fj*4Wq!juKS6Cdc delta 740 zcmZ9KL1@!Z7{|ZY#5UHhY^_^rt?gc_$WAK5rE0-c(Rk7=>?mxzh|I%;sUSNnC|)W^ z<15aKiYUw#glP{hCpQF9(1QiN2^ACsMQ8QUz-s(nlEE;CyzhJ8|Nr~Gyu5aCsaV;$ z<5&c$Ze;RlQ%KcL1613tD{EN^_>-|pv}?chaT8QATZD1dn$pRT^-RUr&w+d#y~}Q7 zs44W-wqR`bLE&Dg_ZxNHQO^J>J46nW7`sG@M6K7!onB(H5qUScz%I*sdM1ev0IA&Q z|2*G1Q@3T6kQDosl4rZINWe7AgxLTr#<&>cIIh!1$RcptXbQ<%IM|m0J>Mf3Q|<*H z+`%tP#>$OBWk^R2g-yQhTrNr1|9Sa&$%0UJo~9ggbGCjn}F z<1UK$debvQrC*ENB|c#Qeexgl@kS_exc4esNavON0fE8+OA$5us0clI5p2jng5U^L zWIxi%>?0h!z55MXDRsaA1 diff --git a/Lab5/include/mini_uart.h b/Lab5/include/mini_uart.h index 12ec374b1..51eb0e34c 100644 --- a/Lab5/include/mini_uart.h +++ b/Lab5/include/mini_uart.h @@ -14,4 +14,7 @@ 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/read_cpio.h b/Lab5/include/read_cpio.h index f33833343..e843d9a0a 100644 --- a/Lab5/include/read_cpio.h +++ b/Lab5/include/read_cpio.h @@ -3,7 +3,7 @@ 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); +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/syscall.h b/Lab5/include/syscall.h new file mode 100644 index 000000000..dfb3a0291 --- /dev/null +++ b/Lab5/include/syscall.h @@ -0,0 +1,15 @@ +#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); + +#endif /*_SYSCALL_H */ \ No newline at end of file diff --git a/Lab5/include/thread.h b/Lab5/include/thread.h index abec65b21..9b09d32c9 100644 --- a/Lab5/include/thread.h +++ b/Lab5/include/thread.h @@ -32,8 +32,8 @@ typedef struct _trapframe typedef struct _task_struct { + struct _context task_context; // context need to be the first one struct _thread_info *thread_info; - struct _context task_context; struct list_head list; struct _trapframe *trapframe; func_ptr job; @@ -54,5 +54,6 @@ 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/kernel8.img b/Lab5/kernel8.img index 3d508410100211b98fe12746926a97a3ced2d7b2..3e18bc5203532bb5e2d9234057c1deddaf3dd236 100755 GIT binary patch delta 9784 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 delta 7397 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 diff --git a/Lab5/src/kernel/device_tree.c b/Lab5/src/kernel/device_tree.c index bcb5b17ce..d1b67143f 100644 --- a/Lab5/src/kernel/device_tree.c +++ b/Lab5/src/kernel/device_tree.c @@ -24,7 +24,7 @@ typedef struct uint32_t nameoff; } fdt_prop; -char *cpioDest; +char *cpioDestGlobal; static uint64_t pad_to_4(void *num); static uint32_t rev32(uint32_t val); @@ -75,7 +75,7 @@ int initramfs_callback(void *dtb) { uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); printf("initramfs_addr at %p\n", addr); - cpioDest = (char *)addr; + cpioDestGlobal = (char *)addr; } p += rev32(prop->len); p += pad_to_4(p); diff --git a/Lab5/src/kernel/dynamic_alloc.c b/Lab5/src/kernel/dynamic_alloc.c index 45d103e0d..1920ede88 100644 --- a/Lab5/src/kernel/dynamic_alloc.c +++ b/Lab5/src/kernel/dynamic_alloc.c @@ -8,7 +8,6 @@ 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() { @@ -32,7 +31,6 @@ void *get_chunk(int req_size) { // 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); } @@ -164,12 +162,23 @@ void free(void *addr) 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; + 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; } @@ -225,15 +234,15 @@ void debug_pool() 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"); + 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 index eb53f0a46..ab8b8cc66 100644 --- a/Lab5/src/kernel/exception.S +++ b/Lab5/src/kernel/exception.S @@ -35,6 +35,30 @@ disable_interrupt: 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] @@ -56,6 +80,30 @@ disable_interrupt: 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. @@ -111,14 +159,10 @@ exception_handler: 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 + save_all_sys + mov x0, sp + bl el0_to_el1_sync_handler + load_all_sys eret exception_handler_irq_sp_elx_lower_el: @@ -131,15 +175,15 @@ exception_handler_irq_sp_elx_same_el: b el1_core_interrupt_handler el0_core_timer_handler: - save_all + save_all_sys mrs x0, cntpct_el0 mrs x1, cntfrq_el0 bl el0_timer_handler - load_all + load_all_sys eret el1_core_interrupt_handler: - save_all + save_all_sys bl el1_irq_interrupt_handler - load_all + load_all_sys eret \ No newline at end of file diff --git a/Lab5/src/kernel/exception.c b/Lab5/src/kernel/exception.c index cc1c2fda6..932d15028 100644 --- a/Lab5/src/kernel/exception.c +++ b/Lab5/src/kernel/exception.c @@ -2,6 +2,11 @@ #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(); /** * common exception handler @@ -147,4 +152,74 @@ void el1_irq_interrupt_handler() } 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 = (void *)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 == 9) + { + 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 index 91cf55583..1c9e4de90 100644 --- a/Lab5/src/kernel/kernel.c +++ b/Lab5/src/kernel/kernel.c @@ -16,6 +16,7 @@ void kernel_main(void) fdt_traverse(initramfs_callback, _dtb_ptr); thread_init(); + memory_init(); shell_start(); diff --git a/Lab5/src/kernel/page_alloc.c b/Lab5/src/kernel/page_alloc.c index 464bb27e8..dc7fcfb97 100644 --- a/Lab5/src/kernel/page_alloc.c +++ b/Lab5/src/kernel/page_alloc.c @@ -225,6 +225,7 @@ int free_page_frame(int index) // 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; @@ -243,19 +244,13 @@ int merge_buddy(int *index, int buddy, int order) frame_array[*index].val = order + 1; for (int i = 1; i < (1 << (order + 1)); i++) { - frame_array[i].val = FREE_BUDDY; + frame_array[i + *index].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; @@ -281,19 +276,19 @@ void debug() 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"); + 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 index 665e57070..0d61c3a17 100644 --- a/Lab5/src/kernel/read_cpio.c +++ b/Lab5/src/kernel/read_cpio.c @@ -1,6 +1,8 @@ #include "stdlib.h" #include "mini_uart.h" +extern char *cpioDestGlobal; + typedef struct cpio_newc_header { char c_magic[6]; @@ -92,7 +94,7 @@ void read_content(char *cpioDest, char *filename) uart_send_string_of_size((char *)cpioDest, fs); } -char *find_content_addr(char *cpioDest, char *filename) +char *find_content_addr(char *cpioDest, const char *filename) { int flag = 0; while (!memcmp(cpioDest, "070701", 6) && memcmp(cpioDest + sizeof(cpio_t), "TRAILER!!!", 10)) @@ -100,7 +102,7 @@ char *find_content_addr(char *cpioDest, char *filename) cpio_t *header = (cpio_t *)cpioDest; int ns = hex2int(header->c_namesize, 8); // Check filename - if (!memcmp(cpioDest + sizeof(cpio_t), filename, ns - 1)) + if (!memcmp(cpioDest + sizeof(cpio_t), (char *)filename, ns - 1)) { flag = 1; break; @@ -124,24 +126,32 @@ char *find_content_addr(char *cpioDest, char *filename) return cpioDest; } -int load_userprogram(char *cpioDest, char *userDest) +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 *)cpioDest; + 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) - cpioDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); + cpioUserPgmDest += (sizeof(cpio_t) + ns + (4 - (sizeof(cpio_t) + ns) % 4)); else - cpioDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); + cpioUserPgmDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); - printf("load %p to %p\n", cpioDest, userDest); + printf("load %p to %p\n", cpioUserPgmDest, userDest); printf("size: %d bytes\n", fs); // load content while (fs--) { - *userDest++ = *cpioDest++; + *userDest++ = *cpioUserPgmDest++; } if (fs == -1) diff --git a/Lab5/src/kernel/shell.c b/Lab5/src/kernel/shell.c index 4f2e1b6dd..0232aa0ca 100644 --- a/Lab5/src/kernel/shell.c +++ b/Lab5/src/kernel/shell.c @@ -9,7 +9,7 @@ #include "test.h" extern void *_dtb_ptr; -extern char *cpioDest; +extern char *cpioDestGlobal; extern char read_buffer[100]; // extern page_frame_node frame_array[TOTAL_NUM_PAGE]; // extern chunk chunk_array[3000]; @@ -31,7 +31,6 @@ void shell_main(char *command) 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"); @@ -51,8 +50,7 @@ void shell_main(char *command) } else if (!strcmp(command, "ls")) { - // char *cpioDest = (char *)0x8000000; - read_cpio((char *)cpioDest); + read_cpio((char *)cpioDestGlobal); } else if (!memcmp(command, "cat", 3)) { @@ -71,7 +69,7 @@ void shell_main(char *command) i++; } - read_content((char *)cpioDest, filename); + read_content((char *)cpioDestGlobal, filename); } else if (!strcmp(command, "dts")) { @@ -79,20 +77,8 @@ void shell_main(char *command) } 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; - } + load_userprogram("userprogram.img", userDest); asm volatile( "mov x0, 0x3c0;" // EL0t diff --git a/Lab5/src/kernel/syscall.c b/Lab5/src/kernel/syscall.c new file mode 100644 index 000000000..028189185 --- /dev/null +++ b/Lab5/src/kernel/syscall.c @@ -0,0 +1,143 @@ +#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 "stdlib.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; + } + } +} diff --git a/Lab5/src/kernel/test.c b/Lab5/src/kernel/test.c index 69a1e1e81..48b61ad7a 100644 --- a/Lab5/src/kernel/test.c +++ b/Lab5/src/kernel/test.c @@ -2,6 +2,14 @@ #include "dynamic_alloc.h" #include "page_alloc.h" #include "thread.h" +#include "syscall.h" +#include "read_cpio.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() { @@ -39,8 +47,35 @@ void test_thread() thread_create(test_mem_alloc); thread_create(test_mem_alloc); thread_create(test_mem_alloc); - thread_create(test_mem_alloc); + + // thread_create(load_usrpgm_in_umode); + idle_task(); debug_task_rq(); return; -} \ No newline at end of file +} + +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"); +} diff --git a/Lab5/src/kernel/thread.S b/Lab5/src/kernel/thread.S index 514958efa..ffc3ece7b 100644 --- a/Lab5/src/kernel/thread.S +++ b/Lab5/src/kernel/thread.S @@ -23,4 +23,9 @@ switch_to: .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 index 5edb79b0d..c8fcaaf80 100644 --- a/Lab5/src/kernel/thread.c +++ b/Lab5/src/kernel/thread.c @@ -4,16 +4,18 @@ #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 idle = {0}; +task_struct kernel_thread = {0}; struct list_head task_rq_head; // run queue struct list_head task_zombieq_head; // zombie queue @@ -23,8 +25,8 @@ void schedule() task_struct *next = del_rq(); if (next == NULL) - next = &idle; - if (cur != &idle) + next = &kernel_thread; + if (cur != &kernel_thread) add_rq(cur); set_switch_timer(); @@ -33,13 +35,13 @@ void schedule() if (next->status == FORKING) { add_rq(next); - switch_to(cur, &idle); + 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, &idle); + switch_to(cur, &kernel_thread); } else { @@ -58,16 +60,19 @@ task_struct *del_rq() struct list_head *ret; ret = task_rq_head.prev; if (ret != &task_rq_head) + { list_del_init(ret); + return container_of(ret, task_struct, list); + } else - ret = NULL; - return container_of(ret, task_struct, list); + return NULL; } void thread_init() { INIT_LIST_HEAD(&task_rq_head); INIT_LIST_HEAD(&task_zombieq_head); + kernel_thread_init(&kernel_thread); return; } @@ -90,6 +95,7 @@ thread_info *thread_create(func_ptr fp) 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; add_rq(new_task); @@ -101,12 +107,12 @@ void task_wrapper() { task_struct *current = get_current(); (current->job)(); - // exit(0); TODO + exit(0); } void idle_task() { - while (!list_empty(&task_rq_head)) + while (!list_empty(&task_rq_head) || !list_empty(&task_zombieq_head)) { disable_interrupt(); kill_zombies(); @@ -132,6 +138,8 @@ void kill_zombies() free(tmp->trapframe); free(tmp); } + INIT_LIST_HEAD(&task_zombieq_head); + return; } void do_fork() @@ -218,4 +226,22 @@ void debug_task_rq() 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 index 8f8d7185e..1af00600e 100644 --- a/Lab5/src/kernel/timer.S +++ b/Lab5/src/kernel/timer.S @@ -1,6 +1,6 @@ #define CORE0_TIMER_IRQ_CTRL 0x40000040 -.global core_timer_enable +.global core_timer_enable core_timer_enable: mov x0, 1 msr cntp_ctl_el0, x0 // enable @@ -9,6 +9,12 @@ core_timer_enable: str w0, [x1] // unmask timer interrupt 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 diff --git a/Lab5/src/kernel/timer.c b/Lab5/src/kernel/timer.c index b94f53cef..ee7b8b278 100644 --- a/Lab5/src/kernel/timer.c +++ b/Lab5/src/kernel/timer.c @@ -1,5 +1,9 @@ #include "stdlib.h" #include "timer.h" +#include "thread.h" + +extern void enable_interrupt(); +extern void disable_interrupt(); typedef struct timer_queue_node { @@ -88,15 +92,9 @@ void add_timer(int sec, char *mes) 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; + disable_interrupt(); + schedule(); + enable_interrupt(); } void el1_timer_handler(long cntpct_el0, long cntfrq_el0) @@ -106,6 +104,7 @@ void el1_timer_handler(long cntpct_el0, long cntfrq_el0) "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); diff --git a/Lab5/src/lib/mini_uart.c b/Lab5/src/lib/mini_uart.c index 82b34aa13..e780073bf 100644 --- a/Lab5/src/lib/mini_uart.c +++ b/Lab5/src/lib/mini_uart.c @@ -181,3 +181,13 @@ void uart_tx_handler() 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/userprogram.img b/Lab5/userprogram.img index dbff941a2cb18aab1060c1e14c26fa80637e37a9..fcbd5d95838db78a4070e92bfeedb72de2913f01 100755 GIT binary patch delta 676 zcmZ9JO-LI-7>3{3U1%b)64Pj+!R*FE#Y;m4%|U2Xl2f5Fc(;XK+R}pxJ*bdD^pai@ zCjOj6q~Om%T0HGRYA(`qpx%UtUVBL`bRlv3O?FE|Pdnc?&-;9{GxdqR%;!p!mE>mh zFt#B&AdHmd^l}ICs<*G+Iao2mtmb-*jr>^qjf!UY*L>=aM3Gj zAUvVsv>u}G64%NMJ6v&nem~~(su!lUrLyowbyFP)6>DSUQBK7KJU-8be;$JxuT&`L zh2Q}kU0&S;DpgBxbbIx0suizfzogSYnQ7*nG1uL0PxhtP1TlQ#nBJKR6 z!4m0m%E;O?2ExMzY$QMzj*``sPvfb+UBW;m2-6H<`&)10iScw9fvjzr&+R1GyCTD! zMh*p}MnWElykmWPnfd#!8zht2_@hF>wqTmJWK& ze|NM%xzkxq`rxzy*)wnwyvsQ(Yki!$ TlNSSxU^-#a0e~G@9ZdcK5&-Xr delta 585 zcmZ9JPbfq|9LImNyRpj>*0Rgi?%P$$Wrge>92Pwn2bvrlDLIgoKW7t8tF&vDbrMlZ zDRO!T#YG$(Y}}+rlmn&Z;2n5=vu~7~W`5uI`F>`aw;9)C2N^G6eAU}XX85r;S~*4_ zb7B^^gEN^6J?`d5kG{MyGUvk>M#`%QEK&tFrwEWH-R{||z?=Z1tcv9Takr@Bz8u$S zYD*JXr-Cbzxl{5YBe+*Y7p$Yc05TqTGS`Y8;S|g#X1MR6Oy1`MgJDr}S*14%pipAX zFF2nnj6#_;KTBT7p14ul5x-T9R@2NDPL|TEf@Ft6*IEwr-TWoaW}p4W1CW}&Qw>qx z<`cf={s+~#x5J(YHV_i)N}&v?kY1{FiknI4;XmpAUnzP-cY(k9BH=A*P*^k_b|kcJ z2UJEJpg{oe!6BvG@)m!%W*ITS5fHOEm?l4s`2+3IG&mEQqW5Y+Amd2_12%LBApBfv zfx2oa=R@(||2m5@p72*UOiF7I!Fe~M_}a8^3Kq`3&Fs5r*7CNx3)i)3kc6_-dCiOJ P^RBo05K;hxoCJbDWDC+P From 8818e7ef95262c1e94a9f40fd1e961993a30178d Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Wed, 10 May 2023 14:35:24 +0800 Subject: [PATCH 15/22] lab5 no demo, lab6 start --- Lab5/Makefile | 11 +- Lab5/include/test.h | 1 + Lab5/include/thread.h | 2 +- Lab5/initramfs.cpio | Bin 10240 -> 247296 bytes Lab5/kernel8.img | Bin 30832 -> 30824 bytes Lab5/rootfs/userprogram.img | Bin 9240 -> 7224 bytes Lab5/src/kernel/dynamic_alloc.c | 1 + Lab5/src/kernel/exception.c | 2 +- Lab5/src/kernel/page_alloc.c | 22 +- Lab5/src/kernel/reserved_mem.c | 2 +- Lab5/src/kernel/shell.c | 25 +- Lab5/src/kernel/syscall.c | 2 +- Lab5/src/kernel/test.c | 28 +- Lab5/src/kernel/thread.c | 7 +- Lab5/src/kernel/timer.S | 3 + Lab5/src/kernel/timer.c | 29 +- Lab5/src/userprogram/link.ld | 2 +- Lab5/src/userprogram/user.S | 15 +- Lab5/userprogram.img | Bin 7200 -> 7240 bytes Lab6/Makefile | 62 ++ Lab6/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 32753 bytes Lab6/bootloader.img | Bin 0 -> 7800 bytes Lab6/bootloader.py | 25 + Lab6/include/device_tree.h | 9 + Lab6/include/dynamic_alloc.h | 35 + Lab6/include/list.h | 441 ++++++++++ Lab6/include/load_kernel.h | 7 + Lab6/include/math.h | 1 + Lab6/include/mbox.h | 6 + Lab6/include/mbox_call.h | 9 + Lab6/include/mini_uart.h | 20 + Lab6/include/mm.h | 19 + Lab6/include/page_alloc.h | 39 + Lab6/include/peripherals/base.h | 6 + Lab6/include/peripherals/device_tree.h | 16 + Lab6/include/peripherals/gpio.h | 25 + Lab6/include/peripherals/irq.h | 27 + Lab6/include/peripherals/mbox_call.h | 40 + Lab6/include/peripherals/mini_uart.h | 19 + Lab6/include/peripherals/reboot.h | 8 + Lab6/include/printf.h | 109 +++ Lab6/include/read_cpio.h | 9 + Lab6/include/reboot.h | 8 + Lab6/include/reserve_mem.h | 18 + Lab6/include/shell.h | 7 + Lab6/include/stdlib.h | 25 + Lab6/include/syscall.h | 15 + Lab6/include/test.h | 8 + Lab6/include/thread.h | 59 ++ Lab6/include/timer.h | 14 + Lab6/include/utils.h | 8 + Lab6/initramfs.cpio | Bin 0 -> 247296 bytes Lab6/kernel8.img | Bin 0 -> 30824 bytes Lab6/rootfs/a.c | 8 + Lab6/rootfs/cat.txt | 1 + Lab6/rootfs/one | 3 + Lab6/rootfs/ts.txt | 0 Lab6/rootfs/userprogram.img | Bin 0 -> 7224 bytes Lab6/send_kernel.py | 40 + Lab6/src/bootloader/boot.S | 25 + Lab6/src/bootloader/bootloader.c | 15 + Lab6/src/bootloader/config.txt | 3 + Lab6/src/bootloader/link.ld | 21 + Lab6/src/bootloader/load_kernel.c | 67 ++ Lab6/src/kernel/boot.S | 88 ++ Lab6/src/kernel/config.txt | 2 + Lab6/src/kernel/device_tree.c | 266 ++++++ Lab6/src/kernel/dynamic_alloc.c | 249 ++++++ Lab6/src/kernel/exception.S | 189 +++++ Lab6/src/kernel/exception.c | 225 +++++ Lab6/src/kernel/kernel.c | 23 + Lab6/src/kernel/link.ld | 19 + Lab6/src/kernel/mbox.c | 63 ++ Lab6/src/kernel/mbox_call.c | 42 + Lab6/src/kernel/page_alloc.c | 308 +++++++ Lab6/src/kernel/read_cpio.c | 161 ++++ Lab6/src/kernel/reboot.c | 19 + Lab6/src/kernel/reserved_mem.c | 48 ++ Lab6/src/kernel/shell.c | 213 +++++ Lab6/src/kernel/syscall.c | 143 ++++ Lab6/src/kernel/test.c | 91 +++ Lab6/src/kernel/thread.S | 31 + Lab6/src/kernel/thread.c | 244 ++++++ Lab6/src/kernel/timer.S | 32 + Lab6/src/kernel/timer.c | 176 ++++ Lab6/src/lib/hex2int.c | 15 + Lab6/src/lib/math.c | 9 + Lab6/src/lib/mem.c | 22 + Lab6/src/lib/mini_uart.c | 193 +++++ Lab6/src/lib/mm.S | 6 + Lab6/src/lib/printf.c | 1046 ++++++++++++++++++++++++ Lab6/src/lib/queue.c | 7 + Lab6/src/lib/simple_malloc.c | 8 + Lab6/src/lib/string.c | 79 ++ Lab6/src/lib/utils.S | 15 + Lab6/src/userprogram/link.ld | 19 + Lab6/src/userprogram/user.S | 22 + Lab6/userprogram.img | Bin 0 -> 7240 bytes 98 files changed, 5444 insertions(+), 58 deletions(-) create mode 100644 Lab6/Makefile create mode 100644 Lab6/bcm2710-rpi-3-b-plus.dtb create mode 100755 Lab6/bootloader.img create mode 100644 Lab6/bootloader.py create mode 100644 Lab6/include/device_tree.h create mode 100644 Lab6/include/dynamic_alloc.h create mode 100644 Lab6/include/list.h create mode 100644 Lab6/include/load_kernel.h create mode 100644 Lab6/include/math.h create mode 100644 Lab6/include/mbox.h create mode 100644 Lab6/include/mbox_call.h create mode 100644 Lab6/include/mini_uart.h create mode 100644 Lab6/include/mm.h create mode 100644 Lab6/include/page_alloc.h create mode 100644 Lab6/include/peripherals/base.h create mode 100644 Lab6/include/peripherals/device_tree.h create mode 100644 Lab6/include/peripherals/gpio.h create mode 100644 Lab6/include/peripherals/irq.h create mode 100644 Lab6/include/peripherals/mbox_call.h create mode 100644 Lab6/include/peripherals/mini_uart.h create mode 100644 Lab6/include/peripherals/reboot.h create mode 100644 Lab6/include/printf.h create mode 100644 Lab6/include/read_cpio.h create mode 100644 Lab6/include/reboot.h create mode 100644 Lab6/include/reserve_mem.h create mode 100644 Lab6/include/shell.h create mode 100644 Lab6/include/stdlib.h create mode 100644 Lab6/include/syscall.h create mode 100644 Lab6/include/test.h create mode 100644 Lab6/include/thread.h create mode 100644 Lab6/include/timer.h create mode 100644 Lab6/include/utils.h create mode 100644 Lab6/initramfs.cpio create mode 100755 Lab6/kernel8.img create mode 100644 Lab6/rootfs/a.c create mode 100644 Lab6/rootfs/cat.txt create mode 100644 Lab6/rootfs/one create mode 100644 Lab6/rootfs/ts.txt create mode 100755 Lab6/rootfs/userprogram.img create mode 100644 Lab6/send_kernel.py create mode 100644 Lab6/src/bootloader/boot.S create mode 100644 Lab6/src/bootloader/bootloader.c create mode 100644 Lab6/src/bootloader/config.txt create mode 100644 Lab6/src/bootloader/link.ld create mode 100644 Lab6/src/bootloader/load_kernel.c create mode 100644 Lab6/src/kernel/boot.S create mode 100644 Lab6/src/kernel/config.txt create mode 100644 Lab6/src/kernel/device_tree.c create mode 100644 Lab6/src/kernel/dynamic_alloc.c create mode 100644 Lab6/src/kernel/exception.S create mode 100644 Lab6/src/kernel/exception.c create mode 100644 Lab6/src/kernel/kernel.c create mode 100644 Lab6/src/kernel/link.ld create mode 100644 Lab6/src/kernel/mbox.c create mode 100644 Lab6/src/kernel/mbox_call.c create mode 100644 Lab6/src/kernel/page_alloc.c create mode 100644 Lab6/src/kernel/read_cpio.c create mode 100644 Lab6/src/kernel/reboot.c create mode 100644 Lab6/src/kernel/reserved_mem.c create mode 100644 Lab6/src/kernel/shell.c create mode 100644 Lab6/src/kernel/syscall.c create mode 100644 Lab6/src/kernel/test.c create mode 100644 Lab6/src/kernel/thread.S create mode 100644 Lab6/src/kernel/thread.c create mode 100644 Lab6/src/kernel/timer.S create mode 100644 Lab6/src/kernel/timer.c create mode 100644 Lab6/src/lib/hex2int.c create mode 100644 Lab6/src/lib/math.c create mode 100644 Lab6/src/lib/mem.c create mode 100644 Lab6/src/lib/mini_uart.c create mode 100644 Lab6/src/lib/mm.S create mode 100644 Lab6/src/lib/printf.c create mode 100644 Lab6/src/lib/queue.c create mode 100644 Lab6/src/lib/simple_malloc.c create mode 100644 Lab6/src/lib/string.c create mode 100644 Lab6/src/lib/utils.S create mode 100644 Lab6/src/userprogram/link.ld create mode 100644 Lab6/src/userprogram/user.S create mode 100755 Lab6/userprogram.img diff --git a/Lab5/Makefile b/Lab5/Makefile index b80e7f409..abe49536e 100644 --- a/Lab5/Makefile +++ b/Lab5/Makefile @@ -29,7 +29,7 @@ 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)-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: @@ -39,11 +39,11 @@ 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)-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)-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: @@ -56,4 +56,7 @@ 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 + 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/include/test.h b/Lab5/include/test.h index 4393a7c5d..994fcc065 100644 --- a/Lab5/include/test.h +++ b/Lab5/include/test.h @@ -3,5 +3,6 @@ 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 index 9b09d32c9..605525143 100644 --- a/Lab5/include/thread.h +++ b/Lab5/include/thread.h @@ -35,12 +35,12 @@ typedef struct _task_struct struct _context task_context; // context need to be the first one struct _thread_info *thread_info; struct list_head list; - struct _trapframe *trapframe; 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" } task_struct; void schedule(); diff --git a/Lab5/initramfs.cpio b/Lab5/initramfs.cpio index 55d33a5527daaa16aea9f6474c77f21cc807a82a..0676fb1584617bc4d73624b3b212a12133e97d8e 100644 GIT binary patch literal 247296 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 delta 6882 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 diff --git a/Lab5/rootfs/userprogram.img b/Lab5/rootfs/userprogram.img index 83efd9631fdcd22cd665917b5808106cd758bd29..2c228ba232cfbf7fc17322e1b6bcc38e3a24c19f 100755 GIT binary patch literal 7224 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 diff --git a/Lab5/src/kernel/dynamic_alloc.c b/Lab5/src/kernel/dynamic_alloc.c index 1920ede88..a16a426b0 100644 --- a/Lab5/src/kernel/dynamic_alloc.c +++ b/Lab5/src/kernel/dynamic_alloc.c @@ -150,6 +150,7 @@ int free_chunk(int index) 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; diff --git a/Lab5/src/kernel/exception.c b/Lab5/src/kernel/exception.c index 932d15028..04e02a51d 100644 --- a/Lab5/src/kernel/exception.c +++ b/Lab5/src/kernel/exception.c @@ -192,7 +192,7 @@ void el0_to_el1_sync_handler(unsigned long trapframe_addr) { task_struct *current = get_current(); current->status = FORKING; - current->trapframe = (void *)trapframe_addr; + current->trapframe = trapframe_addr; int ret = fork(); curr_trapframe = (trapframe *)get_current()->trapframe; curr_trapframe->x[0] = ret; diff --git a/Lab5/src/kernel/page_alloc.c b/Lab5/src/kernel/page_alloc.c index dc7fcfb97..a7202e2bb 100644 --- a/Lab5/src/kernel/page_alloc.c +++ b/Lab5/src/kernel/page_alloc.c @@ -58,16 +58,30 @@ void init_page_frame() void *my_malloc(int req_size) { - int ret = -1; + unsigned long ret = -1; if (req_size < MAX_POOL_SIZE) - return get_chunk(req_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; - - return frame_array[ret].addr; + } + 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) diff --git a/Lab5/src/kernel/reserved_mem.c b/Lab5/src/kernel/reserved_mem.c index 2f466ae4b..0ac1f7c71 100644 --- a/Lab5/src/kernel/reserved_mem.c +++ b/Lab5/src/kernel/reserved_mem.c @@ -42,7 +42,7 @@ void memory_init() memory_reserve(0x1000000, 0x1000fff, "Printf Buffer"); memory_reserve(0x8000000, 0x8010000, "Initramfs"); memory_reserve(0x15000000, 0x17000000, "User Program"); - memory_reserve(0x1000000, 0x1000990, "test"); + 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 index 0232aa0ca..7a5f554c8 100644 --- a/Lab5/src/kernel/shell.c +++ b/Lab5/src/kernel/shell.c @@ -7,6 +7,7 @@ #include "page_alloc.h" #include "dynamic_alloc.h" #include "test.h" +#include "thread.h" extern void *_dtb_ptr; extern char *cpioDestGlobal; @@ -30,7 +31,6 @@ void shell_main(char *command) 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("asynr\t: [test] asynchronous read\n"); uart_send_string("asynw\t: [test] asynchronous write\n"); uart_send_string("setTimeout\t: Usage: setTimeout \n"); @@ -75,20 +75,6 @@ void shell_main(char *command) { fdt_traverse(dtb_parser, _dtb_ptr); } - else if (!strcmp(command, "svc")) - { - char *userDest = (char *)0x200000; - load_userprogram("userprogram.img", userDest); - - 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(); @@ -98,7 +84,7 @@ void shell_main(char *command) "mov x2, %0;" "add x2, x2, 12;" "msr elr_el1, x2;" - "mov x2, 0x1000000;" + "mov x2, 0x15000000;" "msr sp_el0, x2;" "mrs x2, cntfrq_el0;" "add x2, x2, x2;" @@ -162,10 +148,15 @@ void shell_main(char *command) debug(); debug_pool(); } - else if (!memcmp(command, "thread", 5)) + else if (!memcmp(command, "thread", 6)) { test_thread(); } + else if (!strcmp(command, "syscall")) + { + thread_create(load_usrpgm_in_umode); + idle_task(); + } return; } diff --git a/Lab5/src/kernel/syscall.c b/Lab5/src/kernel/syscall.c index 028189185..e61c97c0d 100644 --- a/Lab5/src/kernel/syscall.c +++ b/Lab5/src/kernel/syscall.c @@ -91,7 +91,7 @@ void exit(int status) int mbox_call_u(unsigned char ch, unsigned int *mbox) { - unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF)); + unsigned int r = (((unsigned int)((unsigned long)mbox) & ~0xF) | (ch & 0xF)); /* wait until we can write to the mailbox */ while (1) diff --git a/Lab5/src/kernel/test.c b/Lab5/src/kernel/test.c index 48b61ad7a..0f97e5b8f 100644 --- a/Lab5/src/kernel/test.c +++ b/Lab5/src/kernel/test.c @@ -4,6 +4,8 @@ #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(); @@ -37,16 +39,24 @@ void test_mem_alloc() 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() { - thread_info *thread_ret; - thread_ret = thread_create(test_mem_alloc); - thread_create(test_mem_alloc); - thread_create(test_mem_alloc); - thread_create(test_mem_alloc); - thread_create(test_mem_alloc); - thread_create(test_mem_alloc); - thread_create(test_mem_alloc); + int N = 5; + for (int i = 0; i < N; ++i) + { // N should > 2 + thread_create(foo); + } // thread_create(load_usrpgm_in_umode); idle_task(); @@ -78,4 +88,4 @@ void load_usrpgm_in_umode() : : "r"(target_addr), "r"(target_sp) : "x0"); -} +} \ No newline at end of file diff --git a/Lab5/src/kernel/thread.c b/Lab5/src/kernel/thread.c index c8fcaaf80..a7fa5d37d 100644 --- a/Lab5/src/kernel/thread.c +++ b/Lab5/src/kernel/thread.c @@ -58,7 +58,7 @@ void add_rq(task_struct *task) task_struct *del_rq() { struct list_head *ret; - ret = task_rq_head.prev; + ret = task_rq_head.next; if (ret != &task_rq_head) { list_del_init(ret); @@ -80,10 +80,8 @@ 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)); - trapframe *new_trapframe = (trapframe *)my_malloc(sizeof(trapframe)); new_task->thread_info = new_thread; - new_task->trapframe = new_trapframe; new_thread->task = new_task; new_task->kstack_start = (unsigned long)my_malloc(MIN_PAGE_SIZE); @@ -135,7 +133,6 @@ void kill_zombies() free((void *)tmp->kstack_start); free((void *)tmp->ustack_start); free(tmp->thread_info); - free(tmp->trapframe); free(tmp); } INIT_LIST_HEAD(&task_zombieq_head); @@ -204,7 +201,7 @@ void create_child(task_struct *parent) child->trapframe = parent->trapframe + kstack_offset; // set child user space offset - trapframe *ctrapframe = child->trapframe; // because of data type problem + 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; diff --git a/Lab5/src/kernel/timer.S b/Lab5/src/kernel/timer.S index 1af00600e..a22d66ec4 100644 --- a/Lab5/src/kernel/timer.S +++ b/Lab5/src/kernel/timer.S @@ -7,6 +7,9 @@ core_timer_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 diff --git a/Lab5/src/kernel/timer.c b/Lab5/src/kernel/timer.c index ee7b8b278..5e8c42f58 100644 --- a/Lab5/src/kernel/timer.c +++ b/Lab5/src/kernel/timer.c @@ -99,19 +99,19 @@ void el0_timer_handler(long cntpct_el0, long cntfrq_el0) void el1_timer_handler(long cntpct_el0, long cntfrq_el0) { - // 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++; 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++) { @@ -139,7 +139,12 @@ void el1_timer_handler(long cntpct_el0, long cntfrq_el0) : "r"(timer_queue[timer_queue_front].second_ticks) :); } - + else + { + disable_interrupt(); + schedule(); + enable_interrupt(); + } return; } diff --git a/Lab5/src/userprogram/link.ld b/Lab5/src/userprogram/link.ld index 28c90843a..d84040b38 100644 --- a/Lab5/src/userprogram/link.ld +++ b/Lab5/src/userprogram/link.ld @@ -1,6 +1,6 @@ SECTIONS { - . = 0x200000; + . = 0x15000000; .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } PROVIDE(_data = .); diff --git a/Lab5/src/userprogram/user.S b/Lab5/src/userprogram/user.S index 8a5ea0c0b..6b0a2824a 100644 --- a/Lab5/src/userprogram/user.S +++ b/Lab5/src/userprogram/user.S @@ -4,8 +4,19 @@ _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 - cmp x0, 5 blt 1b 1: - b 1b \ No newline at end of file + 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 index fcbd5d95838db78a4070e92bfeedb72de2913f01..17121a6e7707228c6e9eee727389c8e5c5655ff9 100755 GIT binary patch delta 337 zcmZ2ral*oefuZ3N0}I1M4j|3Qz;LAlN^3Ae_$#3N*Z=;9FfcHPz|2-8DfGsLxnZ ziw`Kx#L}=6CX_8Hr3w^c;BeTf7~r#0o`EsDi;*FO38)*SL@9y?qEuw^DoK4oAg_t1 zA&8NoAqeP~2@E2eUr7oxa&fr*WOPvMcI052%r5=J5-7zW03u)@L54vT#6AI~VdCgC E09>n000000 delta 281 zcmX?MvA{xtfuZ3N0}I1MMh1o}@*)f$SO5PX!oa{Fve8DDm1z^h=4{q9UZw@YlP?QR zV@#NwB|I5O{u8za30aF+0XbzNvzZPsY!()EV`lo}IyqWGkLjQ53jF;j!VWP3?5kmhhnExrbWiA*dFJ3*=x zfkOR~QmQ~91`daviV8kErX}p#cszhtdq5+pGXJ*<>0_TMgh#8A)_$aP)2;RgA5x0E89z@ diff --git a/Lab6/Makefile b/Lab6/Makefile new file mode 100644 index 000000000..abe49536e --- /dev/null +++ b/Lab6/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/Lab6/bcm2710-rpi-3-b-plus.dtb b/Lab6/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..0dd0e2f435a74e6a0c01e2a30fa0246d55ee3091 GIT binary patch literal 32753 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 OPqI~*e!TdI|Nj8Si!WyY literal 0 HcmV?d00001 diff --git a/Lab6/bootloader.img b/Lab6/bootloader.img new file mode 100755 index 0000000000000000000000000000000000000000..13c2199574faa820fbb83d5e55b586b235fda35e GIT binary patch literal 7800 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 literal 0 HcmV?d00001 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/page_alloc.h b/Lab6/include/page_alloc.h new file mode 100644 index 000000000..0a191e1fe --- /dev/null +++ b/Lab6/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/Lab6/include/peripherals/base.h b/Lab6/include/peripherals/base.h new file mode 100644 index 000000000..63f9c038f --- /dev/null +++ b/Lab6/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/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..51e0cc5ea --- /dev/null +++ b/Lab6/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/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..f1d41ad2e --- /dev/null +++ b/Lab6/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/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..3922438c7 --- /dev/null +++ b/Lab6/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/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..715d7bcda --- /dev/null +++ b/Lab6/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/Lab6/include/syscall.h b/Lab6/include/syscall.h new file mode 100644 index 000000000..dfb3a0291 --- /dev/null +++ b/Lab6/include/syscall.h @@ -0,0 +1,15 @@ +#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); + +#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..605525143 --- /dev/null +++ b/Lab6/include/thread.h @@ -0,0 +1,59 @@ +#ifndef _THREAD_H +#define _THREAD_H + +#include "list.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" +} 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..a23ae37a7 --- /dev/null +++ b/Lab6/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/Lab6/initramfs.cpio b/Lab6/initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..0676fb1584617bc4d73624b3b212a12133e97d8e GIT binary patch literal 247296 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 Mx43zY^c?^EFJCst5C8xG literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2c228ba232cfbf7fc17322e1b6bcc38e3a24c19f GIT binary patch literal 7224 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 +__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..81fb65548 --- /dev/null +++ b/Lab6/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/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..d1b67143f --- /dev/null +++ b/Lab6/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/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..ab8b8cc66 --- /dev/null +++ b/Lab6/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/Lab6/src/kernel/exception.c b/Lab6/src/kernel/exception.c new file mode 100644 index 000000000..04e02a51d --- /dev/null +++ b/Lab6/src/kernel/exception.c @@ -0,0 +1,225 @@ +#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(); + +/** + * 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 == 9) + { + 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..1c9e4de90 --- /dev/null +++ b/Lab6/src/kernel/kernel.c @@ -0,0 +1,23 @@ +#include "mini_uart.h" +#include "shell.h" +#include "mbox.h" +#include "reserve_mem.h" +#include "device_tree.h" +#include "thread.h" + +extern void *_dtb_ptr; + +void kernel_main(void) +{ + uart_init(); + + mbox_main(); + + fdt_traverse(initramfs_callback, _dtb_ptr); + + thread_init(); + + memory_init(); + + shell_start(); +} diff --git a/Lab6/src/kernel/link.ld b/Lab6/src/kernel/link.ld new file mode 100644 index 000000000..a460f25b2 --- /dev/null +++ b/Lab6/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/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/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..0d61c3a17 --- /dev/null +++ b/Lab6/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/Lab6/src/kernel/reboot.c b/Lab6/src/kernel/reboot.c new file mode 100644 index 000000000..da809c59a --- /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, 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/Lab6/src/kernel/reserved_mem.c b/Lab6/src/kernel/reserved_mem.c new file mode 100644 index 000000000..0ac1f7c71 --- /dev/null +++ b/Lab6/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, 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(0x200000, 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..7a5f554c8 --- /dev/null +++ b/Lab6/src/kernel/shell.c @@ -0,0 +1,213 @@ +#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"); + } + 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..e61c97c0d --- /dev/null +++ b/Lab6/src/kernel/syscall.c @@ -0,0 +1,143 @@ +#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 "stdlib.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; + } + } +} diff --git a/Lab6/src/kernel/test.c b/Lab6/src/kernel/test.c new file mode 100644 index 000000000..0f97e5b8f --- /dev/null +++ b/Lab6/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/Lab6/src/kernel/thread.S b/Lab6/src/kernel/thread.S new file mode 100644 index 000000000..ffc3ece7b --- /dev/null +++ b/Lab6/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/Lab6/src/kernel/thread.c b/Lab6/src/kernel/thread.c new file mode 100644 index 000000000..a7fa5d37d --- /dev/null +++ b/Lab6/src/kernel/thread.c @@ -0,0 +1,244 @@ +#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; + + 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); + 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 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/Lab6/src/kernel/timer.S b/Lab6/src/kernel/timer.S new file mode 100644 index 000000000..a22d66ec4 --- /dev/null +++ b/Lab6/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/Lab6/src/kernel/timer.c b/Lab6/src/kernel/timer.c new file mode 100644 index 000000000..5e8c42f58 --- /dev/null +++ b/Lab6/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/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..d74fb96fd --- /dev/null +++ b/Lab6/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/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..c35527a42 --- /dev/null +++ b/Lab6/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/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 0000000000000000000000000000000000000000..17121a6e7707228c6e9eee727389c8e5c5655ff9 GIT binary patch literal 7240 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 Date: Thu, 11 May 2023 15:49:33 +0800 Subject: [PATCH 16/22] lab5 adv, NO on board --- .vscode/settings.json | 4 ++- Lab6/bootloader.img | Bin 7800 -> 7832 bytes Lab6/include/my_signal.h | 30 +++++++++++++++++ Lab6/include/stdlib.h | 3 ++ Lab6/include/syscall.h | 1 + Lab6/include/thread.h | 5 ++- Lab6/include/virtual_mem.h | 6 ++++ Lab6/kernel8.img | Bin 30824 -> 31088 bytes Lab6/src/kernel/exception.c | 48 +++++++++++++++++++++++++++ Lab6/src/kernel/kernel.c | 4 +++ Lab6/src/kernel/my_signal.c | 59 ++++++++++++++++++++++++++++++++++ Lab6/src/kernel/shell.c | 1 + Lab6/src/kernel/syscall.c | 20 ++++++++++-- Lab6/src/kernel/thread.c | 13 ++++++++ Lab6/src/kernel/virtual_mem.S | 54 +++++++++++++++++++++++++++++++ Lab6/src/kernel/virtual_mem.c | 10 ++++++ Lab6/src/lib/mem.c | 11 +++++++ Lab6/userprogram.img | Bin 7240 -> 7272 bytes 18 files changed, 265 insertions(+), 4 deletions(-) create mode 100644 Lab6/include/my_signal.h create mode 100644 Lab6/include/virtual_mem.h create mode 100644 Lab6/src/kernel/my_signal.c create mode 100644 Lab6/src/kernel/virtual_mem.S create mode 100644 Lab6/src/kernel/virtual_mem.c diff --git a/.vscode/settings.json b/.vscode/settings.json index d345f6da2..304a8e2ce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,7 +25,9 @@ "stdint.h": "c", "device_tree.h": "c", "list.h": "c", - "thread.h": "c" + "thread.h": "c", + "signal.h": "c", + "limits": "c" }, "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab6/bootloader.img b/Lab6/bootloader.img index 13c2199574faa820fbb83d5e55b586b235fda35e..3bca60bbee288666e26a489804b1ca873f855064 100755 GIT binary patch delta 448 zcmexiGsAX*ifk-9!<7H*S(_d(ORS1vV3_jb|9|lXavTgGP&3g{fmgwN;!{?JDM0Q9 zh6fY#v>1;~oGD|qzM)v3R+2Jjw3-|xCC#{Ca+#DikiAICShB%tq7sY4 zPG=SdL1h+(oeV&)FfuSqP&AqRPDN7B?{#Nf_`Inht zB1ldFsE&cdVW;8(zn$_7jM-g`3?WQF6G3uHEBGM#EheW)>x%$+O*{=jj0_DyKz~hO wuyFsmZ}UoN0Y*+8_n(XoirtPnn-9sfF$!&fMumhtG)m+rC(4OWE|6mf02vO1bN~PV delta 415 zcmbPX`@?2}ifkx5!<7H*S(_d(ORS1vV3_jb|9|lYISvL8h?(f9z`MX~;$2pTDM0Q9 zhJ=ZET8#T9&XloIFrRn=D7qDh#bg+SHUnv4AiWVti^>QDZ&ec3S_fobPGZn1WfIkT z$SkS_Hle|M;(JY1IiRHn{&_-n|HER3v3P%-^;|J;{MZkvZ#~}quOLADRV}x$yHL) zj17~gNofPwhop=p8LTEMu{i8>W?>LiW?|UL0CXuM1H%MGmC1b4+P*+pCYFYs5E1vE z_8CeR@<3Iefg$m?djHD5%nTDjatc6o3>*$S6&w6^$}=!#cQG=AFab>j$tiX4LG)`( zZj;s*0rHx78iE)Z8iIh%p1`2t{`1)8lhOi=oHFh|866b69c4B@l4)ZUng9*yAFvRY NpWG-XK6!#1I{+-Cbs7Kw 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/stdlib.h b/Lab6/include/stdlib.h index 715d7bcda..31b8255dc 100644 --- a/Lab6/include/stdlib.h +++ b/Lab6/include/stdlib.h @@ -6,6 +6,8 @@ #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); @@ -14,6 +16,7 @@ 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); diff --git a/Lab6/include/syscall.h b/Lab6/include/syscall.h index dfb3a0291..aca95eae1 100644 --- a/Lab6/include/syscall.h +++ b/Lab6/include/syscall.h @@ -11,5 +11,6 @@ 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/thread.h b/Lab6/include/thread.h index 605525143..a987a47be 100644 --- a/Lab6/include/thread.h +++ b/Lab6/include/thread.h @@ -2,6 +2,7 @@ #define _THREAD_H #include "list.h" +#include "my_signal.h" #define READY 1 #define ZOMBIE 2 @@ -40,7 +41,9 @@ typedef struct _task_struct 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" + 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(); diff --git a/Lab6/include/virtual_mem.h b/Lab6/include/virtual_mem.h new file mode 100644 index 000000000..d612b5079 --- /dev/null +++ b/Lab6/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/Lab6/kernel8.img b/Lab6/kernel8.img index e114ddaf81d3bf2ae44ef895e5241b2d125a789c..afc387a82554cb165167c27b0ff1211f126a9fbe 100755 GIT binary patch delta 8195 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 vK!bh%7a)nKL$ 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 MxcVKwVP3)i0kE&KNB{r; diff --git a/Lab6/src/kernel/exception.c b/Lab6/src/kernel/exception.c index 04e02a51d..6f10a5784 100644 --- a/Lab6/src/kernel/exception.c +++ b/Lab6/src/kernel/exception.c @@ -7,6 +7,8 @@ extern task_struct *get_current(); extern void enable_interrupt(); +extern void disable_interrupt(); +extern signal_handler signal_table[]; /** * common exception handler @@ -214,7 +216,53 @@ void el0_to_el1_sync_handler(unsigned long trapframe_addr) 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) diff --git a/Lab6/src/kernel/kernel.c b/Lab6/src/kernel/kernel.c index 1c9e4de90..85e57b892 100644 --- a/Lab6/src/kernel/kernel.c +++ b/Lab6/src/kernel/kernel.c @@ -5,6 +5,8 @@ #include "device_tree.h" #include "thread.h" +#include "virtual_mem.h" + extern void *_dtb_ptr; void kernel_main(void) @@ -19,5 +21,7 @@ void kernel_main(void) memory_init(); + // virtual_mem_init(); + shell_start(); } 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/shell.c b/Lab6/src/kernel/shell.c index 7a5f554c8..1db5a5a35 100644 --- a/Lab6/src/kernel/shell.c +++ b/Lab6/src/kernel/shell.c @@ -36,6 +36,7 @@ void shell_main(char *command) 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")) { diff --git a/Lab6/src/kernel/syscall.c b/Lab6/src/kernel/syscall.c index e61c97c0d..1a867f807 100644 --- a/Lab6/src/kernel/syscall.c +++ b/Lab6/src/kernel/syscall.c @@ -1,3 +1,4 @@ +#include "stdlib.h" #include "thread.h" #include "mini_uart.h" #include "page_alloc.h" @@ -5,8 +6,7 @@ #include "utils.h" #include "peripherals/mbox_call.h" #include "peripherals/gpio.h" - -#include "stdlib.h" +#include "my_signal.h" extern task_struct *get_current(); extern void set_switch_timer(); @@ -141,3 +141,19 @@ void kill(int pid) } } } + +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/thread.c b/Lab6/src/kernel/thread.c index a7fa5d37d..c88ac7b82 100644 --- a/Lab6/src/kernel/thread.c +++ b/Lab6/src/kernel/thread.c @@ -94,6 +94,7 @@ thread_info *thread_create(func_ptr fp) new_task->thread_info->id = thread_cnt++; new_task->status = READY; new_task->job = fp; + new_task->custom_signal = NULL; add_rq(new_task); @@ -133,6 +134,8 @@ void kill_zombies() 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); @@ -172,6 +175,16 @@ void create_child(task_struct *parent) 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; diff --git a/Lab6/src/kernel/virtual_mem.S b/Lab6/src/kernel/virtual_mem.S new file mode 100644 index 000000000..5453ad829 --- /dev/null +++ b/Lab6/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/Lab6/src/kernel/virtual_mem.c b/Lab6/src/kernel/virtual_mem.c new file mode 100644 index 000000000..d05dd504e --- /dev/null +++ b/Lab6/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/Lab6/src/lib/mem.c b/Lab6/src/lib/mem.c index d74fb96fd..954ba9805 100644 --- a/Lab6/src/lib/mem.c +++ b/Lab6/src/lib/mem.c @@ -1,3 +1,5 @@ +#include + void *memset(void *dest, register int val, int len) { register unsigned char *ptr = (unsigned char *)dest; @@ -19,4 +21,13 @@ int memcmp(void *s1, void *s2, int n) 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/userprogram.img b/Lab6/userprogram.img index 17121a6e7707228c6e9eee727389c8e5c5655ff9..494ba66074dfecf8d5401beca4aa079174f6f190 100755 GIT binary patch delta 240 zcmX?M@xo$*0xRR`&5Eqm0t$~97`8AoG+a{2NVZ_fP_{^BVVGFY!1TKD|NoE!%<H#Q+sl(gbt(9E86k*_S*r{0Hvs0ddF}sVA zA%uyAK@g-use%WhQf2ZvNqrF@uZgE2h>@Wohyf_epyKv(-)2E60Y**B)gI;*%3(SOM;7IGX?f From 3e643d200b5e5be8af920ee5c9fc9424bef0031d Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Thu, 11 May 2023 16:18:19 +0800 Subject: [PATCH 17/22] update Lab5 --- Lab5/bootloader.img | Bin 7800 -> 7832 bytes Lab5/include/my_signal.h | 30 +++++++++++++++++ Lab5/include/stdlib.h | 3 ++ Lab5/include/syscall.h | 1 + Lab5/include/thread.h | 5 ++- Lab5/include/virtual_mem.h | 6 ++++ Lab5/kernel8.img | Bin 30824 -> 31088 bytes Lab5/src/kernel/exception.c | 48 +++++++++++++++++++++++++++ Lab5/src/kernel/kernel.c | 4 +++ Lab5/src/kernel/my_signal.c | 59 +++++++++++++++++++++++++++++++++ Lab5/src/kernel/reserved_mem.c | 2 +- Lab5/src/kernel/shell.c | 5 +-- Lab5/src/kernel/syscall.c | 20 +++++++++-- Lab5/src/kernel/thread.c | 13 ++++++++ Lab5/src/kernel/virtual_mem.S | 54 ++++++++++++++++++++++++++++++ Lab5/src/kernel/virtual_mem.c | 10 ++++++ Lab5/src/lib/mem.c | 11 ++++++ Lab5/userprogram.img | Bin 7240 -> 7272 bytes Lab6/kernel8.img | Bin 31088 -> 31088 bytes Lab6/src/kernel/reserved_mem.c | 2 +- Lab6/src/kernel/shell.c | 4 +-- 21 files changed, 268 insertions(+), 9 deletions(-) create mode 100644 Lab5/include/my_signal.h create mode 100644 Lab5/include/virtual_mem.h create mode 100644 Lab5/src/kernel/my_signal.c create mode 100644 Lab5/src/kernel/virtual_mem.S create mode 100644 Lab5/src/kernel/virtual_mem.c diff --git a/Lab5/bootloader.img b/Lab5/bootloader.img index 13c2199574faa820fbb83d5e55b586b235fda35e..3bca60bbee288666e26a489804b1ca873f855064 100755 GIT binary patch delta 448 zcmexiGsAX*ifk-9!<7H*S(_d(ORS1vV3_jb|9|lXavTgGP&3g{fmgwN;!{?JDM0Q9 zh6fY#v>1;~oGD|qzM)v3R+2Jjw3-|xCC#{Ca+#DikiAICShB%tq7sY4 zPG=SdL1h+(oeV&)FfuSqP&AqRPDN7B?{#Nf_`Inht zB1ldFsE&cdVW;8(zn$_7jM-g`3?WQF6G3uHEBGM#EheW)>x%$+O*{=jj0_DyKz~hO wuyFsmZ}UoN0Y*+8_n(XoirtPnn-9sfF$!&fMumhtG)m+rC(4OWE|6mf02vO1bN~PV delta 415 zcmbPX`@?2}ifkx5!<7H*S(_d(ORS1vV3_jb|9|lYISvL8h?(f9z`MX~;$2pTDM0Q9 zhJ=ZET8#T9&XloIFrRn=D7qDh#bg+SHUnv4AiWVti^>QDZ&ec3S_fobPGZn1WfIkT z$SkS_Hle|M;(JY1IiRHn{&_-n|HER3v3P%-^;|J;{MZkvZ#~}quOLADRV}x$yHL) zj17~gNofPwhop=p8LTEMu{i8>W?>LiW?|UL0CXuM1H%MGmC1b4+P*+pCYFYs5E1vE z_8CeR@<3Iefg$m?djHD5%nTDjatc6o3>*$S6&w6^$}=!#cQG=AFab>j$tiX4LG)`( zZj;s*0rHx78iE)Z8iIh%p1`2t{`1)8lhOi=oHFh|866b69c4B@l4)ZUng9*yAFvRY NpWG-XK6!#1I{+-Cbs7Kw 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/stdlib.h b/Lab5/include/stdlib.h index 715d7bcda..31b8255dc 100644 --- a/Lab5/include/stdlib.h +++ b/Lab5/include/stdlib.h @@ -6,6 +6,8 @@ #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); @@ -14,6 +16,7 @@ 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); diff --git a/Lab5/include/syscall.h b/Lab5/include/syscall.h index dfb3a0291..aca95eae1 100644 --- a/Lab5/include/syscall.h +++ b/Lab5/include/syscall.h @@ -11,5 +11,6 @@ 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/thread.h b/Lab5/include/thread.h index 605525143..a987a47be 100644 --- a/Lab5/include/thread.h +++ b/Lab5/include/thread.h @@ -2,6 +2,7 @@ #define _THREAD_H #include "list.h" +#include "my_signal.h" #define READY 1 #define ZOMBIE 2 @@ -40,7 +41,9 @@ typedef struct _task_struct 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" + 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(); 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/kernel8.img b/Lab5/kernel8.img index e114ddaf81d3bf2ae44ef895e5241b2d125a789c..f449743def90a48b7f3f9d304d3a96938a7a30c0 100755 GIT binary patch delta 8216 zcmbtZeRx#Gxqr{u%_f8pHrZ^xA!ic^5I#dfz7TT=D*|$Xu2ca<4hf|Y(Gmk%LES^} z(UyqKII9s$WkE_|u^K_zu2x**(_U|>7K7w!d$o&g)hoReNsT7L~83HUnaZf)=A$_BkN+iSP39Uy> z^x)>B2L>I)d_)7j4SsfEWALjq3dK^W`pcW80}9b`QYNvD9tu@~rTt#Nq2;R4oq(%B z3+<_6aYs(%F2E+QHi@;Oua&D&j#kiC+qBpu)*@&PYVEfd6f zYIJLoc#BQcC$pOcZGjqnAW2(@_RE`R9vIx*yFcbw(Qg_NpUmb0|4ky1H@NwA)4$jG zOu0%f_wak=R_Po+CC@5BDD;eT{Y^?raUh$RxWryMyIl+)cu z@t>XT<34BJuy;(#h1xgqP9HfL3i(4Sr)t$8QjL;W^=Xo6F+3Tdz5H3HKlqyI zf|!r<9x~25sN$SMidgZU!XD3}P$rao>7WQX^}rGr{343xILVMyVp0a$G%}i(sYa7T zFV6zb0o+XS1>S+L2;q5;Y7B0E(+s1wjEpPcE5P~~1#7mhpBrs)Qr6b>L!ttHxQkzQ zR+PVnP!Dc?!<73gY!HUK!1`yRw!0)+Y(}VyFGzVjIL?VBkxC(;jyJ`oFp0MH5rX+@ zBGGN)sFF!?xGzTGzF*ilwUV7mZ0VDf@L5vAeLXf_uf(d|4f)-D2W?yo^6@==6yDRf z&&JVIw%Zx*BWIZ3=6Wb|&s6p_hOC7N7vw7bscV5#naWP_`qc6AC=XwqS|bnj@MlvS zMn~P|q8^}Cexs1c_yX&xJ)783v{vw|SPvgeU6)x}&7OhIdg!coOyl>ZEl7dR2i^R4 zY322gV17(?s%GmgmB%Su$L>P^Pf&F)RGyS*n#mKZV&4Tm?#PWC=HrHp$-W!XrvRDM zconYEOnV&nAMpoy#jQa5Z1EyhaV;umdUk=$}46NGTmdjp;0+W6xsvt zMRd+wOV{hz2cSO#x>!3mpj9XpSyiiICwSXX)fuZ}z5K{fwN9&Nzk+x=QBFw)osqD2 z%vlfDvqQi;fj42Lp1lCr?8oZaK|V2~I!B+uegY(Rk;SCfvmJb6##m>(fj!Q5XXK|) z1AElXk7g7Fwd+_Ayt8fIcs<)}DI6e;*!R(oUDi(QMhpKdcGpgm*KycJK| zv^cR=^mWjh){_`d4BdpTWkuc|&GdFx<#Sg7TcLEqpyI1bo(psgdOZ{)Y`~jVv2* zw++V|Stej^!2_faO9%X;EA)Ei0^AAME?T5MWtNWAJPLg~EInOAL$>uPfBo562M`*$ zYYsy|xNGAFa}i}qt2qXIioj>+$dCKaUI1RBv|2k&EAi$P z;MvF)qOwU&)$k3YBj%#jB=&ly732pmCHUdtzKJ5&{>@Bv-U?B8@4J-fjw5erG726F zlL)hvrbY=B@`qe<=LL6wgC0=1;P^xY5zUH>CGBT*UF9LZQZf zL3<-Gwqlamt%&(f&fNLI zu*{x-CQ7lZj?4l`Qs(5S4z|$l#~f_F-B+D#uHBD2*=)NXPGPg`KDn4`_roqW!|to8 ztikRpX)IW41K~7QZTD#it3=;A&AnwFC#>o=1X>GY2 z%OkzWQ?je?z$rkizAguaUbDxITl}L%Fzph)4pRs|1(lh;?>90pC%Rv<`Dv)BzX1FS zD)m=5s==-xeThzOE3r(qNoswVh&Os%`Qf4Lr^6_;S%|vyfJAyA zUM8`ne7R?l6U0vblBcX-I<$naqrL*^{F}sZtH=?x@^}1yJY~Ut*l-GzUGVSxZA}Me zL)jiEvf`g%#$X~e2%kR0YQ6qyx@OTwgRX;qqfDXY;F$)VUR#H5@f0U@TmpR}=%)T~ zMxi;tlQ4q*marDX(|+SQEP~biK#mfOWw4tdd9`~6Tvo)4DZ`i_G!I5P2M=-)y*wPM zr-62)bQl{3KX?7Es!l{W3w$D1{TF8ViY~yPK$ke*Pfh9>iB>A6>{z0)R^&%M$2)d} z81M{C76tYzQy{P6oZ049oaNiS`BIqg^Ntx3_FF4yC-3(zly>s++(+^ILM|@=h1_x{ za#eTup&+N&v`*9ioqSo|R!D!8HyxaX`77|dDgQ_KO)n_HZ$-f@Z&LO~$i^GhsE{k& z$vX;Wf&SZqTb=4K_67G9)4BLn&mAxyNtxcM$W!hC0}g-!hz4qYd>AXSHZA z$ZnQuF3Y@#b0~jjL}73+okAyM)i`lE@$yPpjf&a32wZ!%YM?M58L$Ub-0Upy=HRNE zeSv-zRJK3sH^eC}2Sb{N{Wsmr9C}ukHDqrVGfivGW)0vMH8|v=toB}Fy8^_&JJK6` zAG(?`wzwO{9r=;>1dNlq1?NSM3pm?`b0fb8to6>k7$$mIMrjdzs>8#63w*e#>7srq zaRm>1dnoK-uY>lHaR#yMBbq%p_Sc)E4Xb~_du}6X5 z1z3R*bJ2>s&3d!VkKD~?Dr+*~RX?T)8@K03lb8QenOSg2gbW@PzACYgaR#++`Gt|M zc$Uvoej`*Guv>Q0w-99bS{V7ekdQ22=}*M2&*1ZXld`9R)pk*!CuQ($zH)iuP=3fa zCPnu$O6MQ>#@jYvRrZ7|8^-O0MjW)RvcVeZSuqk0Vj^m^Sz-fxOR1;e$!o^{6fTxH z)w5y#Eng9Tt8|<*?q#p=^QEI?RIsA55m~+9IG2pRhS@mE=ayCHP#)_ZVqPS1tiT#k z^BCu>Y>rdUWsmX?%SPRShn*bCsqlEZ`#gB95EWO)`Qh4EjQfGJdA#oKK5vqDqseQ% zYutl=2@XKwz+;ZK!OvHWDwo&z`J`tXV%ElG>ww$jtIpT9EdHu+9J&l^1w zS)*ojyWl)H`ugnsexo@WX|bNq zR^b`UgZ*p4c}(NW$L3F=e6|z>?V*Q|aH~Y#>hVE|a+MOFb#LpTcs^Uee>(OpVe0qB zO@gV2LKLc~&avssBp>GuLWqq1c5z`3^gKmAZ{>`NNSw+^S z?=2D;tEa!I*c!gKn01)h$z!Vj4Rmz4MAi{-e&h`cHY_+V@|u9nY~`t#Ar1ghfxru) z#o&z`0Stq>#EZuv^xNS_Xb-VqfZE=;lw2S%{2;F!Up=9D1oO`MLEm{Pe6|=%U)&r% z#>nD4C35vsP#`MnUX=FzoQ=OOqjv?4>Wl9_BG`;+ayWV<)n?m_zVePE*JQ$B zvLo=R=)1^}OZ7gHb2JpE&*5YU<9KctJct}F?nrsYlO1GeF7v3*^he0x;qe8RuE?zY8UZ*mWo#anWEYImm*)TV{_km6P zVcbT|S$e?F|1eQ4?xF{f57vynhy~tNc6>Bbz=*_{Rk~paA3te=#qgMft@9bN6p}kX zDRLbf59T!w`uT;)tLH(U*0f$!KQ20%3gZ$B9Vf4Tr?tcN0%(sDg(7IL+)klJ!k9iM z?t2^N^0LX5w;~V&VhOe2M7bsN#51O`;gM_wV65>j-Zz?QE)cBRXk~MX@-@y66n2Bd!?Rq@-HsIM8_g!17 zkQon|&mm(+-;B&&%oJn@ye14i{{q9pO32x>GstI5&2i`Jf%dPHYucd)*7KE9J;6JH z(Pj%(9f38XKkuKzD;id(n!=i%Uw5K;0nD3?*J!88nk(=Ejmc_JGFZzb;HP3YIEj^N zWUQ<)uTd~d@6!TgY-}PUh?i-xk%Qvs3YW}d4Q|PJx#nL?%?-kO59&owrqf}(q9-5B zBj!O6w|61(5n4R>gU5SXLn__SE4{139C6#%=cz_NTCHQn#rFh^M|1UZ;)r;E5SQ_5 zBV?@w>(N~Nm%`Uqr3CR%FXotF_5qmbNfUB<9}2sn!zJOC4>Khp>Kgi}JeVoHucLKk zU306i&Ik7!!_t{8sGsKec(f-Ro`#E%!hOos`?j=dbt_wyIs$Kr>M(gb;Dd&*G>f09 zDj4oKEfjRXA^e$Q2wYw;NRowJ04%<<$dCLf!&LcLDIP)OHOb-0k{0T=;7cSOTL~>f zn-9Frkll)J6FH8xRH#M^wpj~ivSn(rk6GM$i;#14YPVp9Hwe7MAFUqA2dndZW*D*X z?5WZ*4I*SN6kH9{v4ekDeVuQg-w=B{)ZsS-!Gl%UET$dbO!NmX^M+})Q^O_f_xNAI zRH-QJKkWWh>T!fKR{{!b*O3l|KR2yHjw}2R(@KT(^R@)}*a5qL^*Xvnx~#^OURYBw zt{)fAug@g9&B6b9@$aIC{l=^365Vg(YB>MPM0dksR`Z2ZI>xt5pCf0FK74HY>R~d~ z@v6B`$+1!V*xV;XvtnL@K>ipFp#PlrjI502?Kk~U`50$^2Uf_aH7#|{HDz`97ls8% zHm6b)AAaekVrd*Nm_J4u!Dr5&oaI3ci6v5ND9w5>9DaDq{8}ma1~MS6%FLGNVcf;T z_edn6s(T_);IRS&dN$q*@xFKja18xlqHPbz_@CzKfZ%&EiGL}{Z&p-ymetk={%DK|H3HaSSc#jEL4{=Cq)T2excI$ z1OAJJrMb0WBY*@p*{Mg9q<<_dss@JewCS5OFIqy z&*w-!V^Q9S{G`qjaCKx@Z#Cu^;6BRR78Ob9eEXsjN#;La^j`WBT&z`J=gcL}!=2w6 zCgF*j=vMVeJ3&)bja?ylTD-^ zlHIlrA~(5pVHWw#Ukqn`EMrL`{(ft${I<1sum0Y0W!B1d>%Ox}XM*J^!Bnid< delta 7218 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 diff --git a/Lab5/src/kernel/exception.c b/Lab5/src/kernel/exception.c index 04e02a51d..6f10a5784 100644 --- a/Lab5/src/kernel/exception.c +++ b/Lab5/src/kernel/exception.c @@ -7,6 +7,8 @@ extern task_struct *get_current(); extern void enable_interrupt(); +extern void disable_interrupt(); +extern signal_handler signal_table[]; /** * common exception handler @@ -214,7 +216,53 @@ void el0_to_el1_sync_handler(unsigned long trapframe_addr) 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) diff --git a/Lab5/src/kernel/kernel.c b/Lab5/src/kernel/kernel.c index 1c9e4de90..85e57b892 100644 --- a/Lab5/src/kernel/kernel.c +++ b/Lab5/src/kernel/kernel.c @@ -5,6 +5,8 @@ #include "device_tree.h" #include "thread.h" +#include "virtual_mem.h" + extern void *_dtb_ptr; void kernel_main(void) @@ -19,5 +21,7 @@ void kernel_main(void) memory_init(); + // virtual_mem_init(); + shell_start(); } 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/reserved_mem.c b/Lab5/src/kernel/reserved_mem.c index 0ac1f7c71..74a60af3e 100644 --- a/Lab5/src/kernel/reserved_mem.c +++ b/Lab5/src/kernel/reserved_mem.c @@ -37,7 +37,7 @@ void memory_init() init_page_frame(); init_pool(); - memory_reserve(0x0000, 0x1000, "Multicore Boot"); + memory_reserve(0x0000, 0x5000, "PGD, PUD"); memory_reserve(0x60000, 0x100000, "Kernel Img"); memory_reserve(0x1000000, 0x1000fff, "Printf Buffer"); memory_reserve(0x8000000, 0x8010000, "Initramfs"); diff --git a/Lab5/src/kernel/shell.c b/Lab5/src/kernel/shell.c index 7a5f554c8..3a8d4f13f 100644 --- a/Lab5/src/kernel/shell.c +++ b/Lab5/src/kernel/shell.c @@ -36,6 +36,7 @@ void shell_main(char *command) 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")) { @@ -170,7 +171,7 @@ void shell_start() char command[COMMAND_BUFFER]; memset(command, '\0', COMMAND_BUFFER); - uart_send_string("# "); + uart_send_string("$ "); while (1) { @@ -185,7 +186,7 @@ void shell_start() shell_main(command); memset(command, '\0', COMMAND_BUFFER); i = 0; - uart_send_string("# "); + uart_send_string("$ "); } else if (c == 8) // Backspace { diff --git a/Lab5/src/kernel/syscall.c b/Lab5/src/kernel/syscall.c index e61c97c0d..1a867f807 100644 --- a/Lab5/src/kernel/syscall.c +++ b/Lab5/src/kernel/syscall.c @@ -1,3 +1,4 @@ +#include "stdlib.h" #include "thread.h" #include "mini_uart.h" #include "page_alloc.h" @@ -5,8 +6,7 @@ #include "utils.h" #include "peripherals/mbox_call.h" #include "peripherals/gpio.h" - -#include "stdlib.h" +#include "my_signal.h" extern task_struct *get_current(); extern void set_switch_timer(); @@ -141,3 +141,19 @@ void kill(int pid) } } } + +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/thread.c b/Lab5/src/kernel/thread.c index a7fa5d37d..c88ac7b82 100644 --- a/Lab5/src/kernel/thread.c +++ b/Lab5/src/kernel/thread.c @@ -94,6 +94,7 @@ thread_info *thread_create(func_ptr fp) new_task->thread_info->id = thread_cnt++; new_task->status = READY; new_task->job = fp; + new_task->custom_signal = NULL; add_rq(new_task); @@ -133,6 +134,8 @@ void kill_zombies() 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); @@ -172,6 +175,16 @@ void create_child(task_struct *parent) 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; 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/mem.c b/Lab5/src/lib/mem.c index d74fb96fd..954ba9805 100644 --- a/Lab5/src/lib/mem.c +++ b/Lab5/src/lib/mem.c @@ -1,3 +1,5 @@ +#include + void *memset(void *dest, register int val, int len) { register unsigned char *ptr = (unsigned char *)dest; @@ -19,4 +21,13 @@ int memcmp(void *s1, void *s2, int n) 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/userprogram.img b/Lab5/userprogram.img index 17121a6e7707228c6e9eee727389c8e5c5655ff9..494ba66074dfecf8d5401beca4aa079174f6f190 100755 GIT binary patch delta 240 zcmX?M@xo$*0xRR`&5Eqm0t$~97`8AoG+a{2NVZ_fP_{^BVVGFY!1TKD|NoE!%<H#Q+sl(gbt(9E86k*_S*r{0Hvs0ddF}sVA zA%uyAK@g-use%WhQf2ZvNqrF@uZgE2h>@Wohyf_epyKv(-)2E60Y**B)gI;*%3(SOM;7IGX?f diff --git a/Lab6/kernel8.img b/Lab6/kernel8.img index afc387a82554cb165167c27b0ff1211f126a9fbe..f449743def90a48b7f3f9d304d3a96938a7a30c0 100755 GIT binary patch delta 43 vcmezHiSffH#tm6)j9r_v*^Z|21-QHDC Date: Wed, 7 Jun 2023 14:54:58 +0800 Subject: [PATCH 18/22] Init Lab7 --- Lab7/Makefile | 62 ++ Lab7/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 32753 bytes Lab7/bootloader.img | Bin 0 -> 7832 bytes Lab7/bootloader.py | 25 + Lab7/include/device_tree.h | 9 + Lab7/include/dynamic_alloc.h | 35 + Lab7/include/list.h | 441 ++++++++++ Lab7/include/load_kernel.h | 7 + Lab7/include/math.h | 1 + Lab7/include/mbox.h | 6 + Lab7/include/mbox_call.h | 9 + Lab7/include/mini_uart.h | 20 + Lab7/include/mm.h | 19 + Lab7/include/my_signal.h | 30 + Lab7/include/page_alloc.h | 39 + Lab7/include/peripherals/base.h | 6 + Lab7/include/peripherals/device_tree.h | 16 + Lab7/include/peripherals/gpio.h | 25 + Lab7/include/peripherals/irq.h | 27 + Lab7/include/peripherals/mbox_call.h | 40 + Lab7/include/peripherals/mini_uart.h | 19 + Lab7/include/peripherals/reboot.h | 8 + Lab7/include/printf.h | 109 +++ Lab7/include/read_cpio.h | 9 + Lab7/include/reboot.h | 8 + Lab7/include/reserve_mem.h | 18 + Lab7/include/shell.h | 7 + Lab7/include/stdlib.h | 28 + Lab7/include/syscall.h | 16 + Lab7/include/test.h | 8 + Lab7/include/thread.h | 62 ++ Lab7/include/timer.h | 14 + Lab7/include/utils.h | 8 + Lab7/include/virtual_mem.h | 6 + Lab7/initramfs.cpio | Bin 0 -> 247296 bytes Lab7/kernel8.img | Bin 0 -> 31088 bytes Lab7/rootfs/a.c | 8 + Lab7/rootfs/cat.txt | 1 + Lab7/rootfs/one | 3 + Lab7/rootfs/ts.txt | 0 Lab7/rootfs/userprogram.img | Bin 0 -> 7224 bytes Lab7/send_kernel.py | 40 + Lab7/src/bootloader/boot.S | 25 + Lab7/src/bootloader/bootloader.c | 15 + Lab7/src/bootloader/config.txt | 3 + Lab7/src/bootloader/link.ld | 21 + Lab7/src/bootloader/load_kernel.c | 67 ++ Lab7/src/kernel/boot.S | 88 ++ Lab7/src/kernel/config.txt | 2 + Lab7/src/kernel/device_tree.c | 266 ++++++ Lab7/src/kernel/dynamic_alloc.c | 249 ++++++ Lab7/src/kernel/exception.S | 189 +++++ Lab7/src/kernel/exception.c | 273 +++++++ Lab7/src/kernel/kernel.c | 27 + Lab7/src/kernel/link.ld | 19 + Lab7/src/kernel/mbox.c | 63 ++ Lab7/src/kernel/mbox_call.c | 42 + Lab7/src/kernel/my_signal.c | 59 ++ Lab7/src/kernel/page_alloc.c | 308 +++++++ Lab7/src/kernel/read_cpio.c | 161 ++++ Lab7/src/kernel/reboot.c | 19 + Lab7/src/kernel/reserved_mem.c | 48 ++ Lab7/src/kernel/shell.c | 214 +++++ Lab7/src/kernel/syscall.c | 159 ++++ Lab7/src/kernel/test.c | 91 +++ Lab7/src/kernel/thread.S | 31 + Lab7/src/kernel/thread.c | 257 ++++++ Lab7/src/kernel/timer.S | 32 + Lab7/src/kernel/timer.c | 176 ++++ Lab7/src/kernel/virtual_mem.S | 54 ++ Lab7/src/kernel/virtual_mem.c | 10 + Lab7/src/lib/hex2int.c | 15 + Lab7/src/lib/math.c | 9 + Lab7/src/lib/mem.c | 33 + Lab7/src/lib/mini_uart.c | 193 +++++ Lab7/src/lib/mm.S | 6 + Lab7/src/lib/printf.c | 1046 ++++++++++++++++++++++++ Lab7/src/lib/queue.c | 7 + Lab7/src/lib/simple_malloc.c | 8 + Lab7/src/lib/string.c | 79 ++ Lab7/src/lib/utils.S | 15 + Lab7/src/userprogram/link.ld | 19 + Lab7/src/userprogram/user.S | 22 + Lab7/userprogram.img | Bin 0 -> 7272 bytes 84 files changed, 5609 insertions(+) create mode 100644 Lab7/Makefile create mode 100644 Lab7/bcm2710-rpi-3-b-plus.dtb create mode 100755 Lab7/bootloader.img create mode 100644 Lab7/bootloader.py create mode 100644 Lab7/include/device_tree.h create mode 100644 Lab7/include/dynamic_alloc.h create mode 100644 Lab7/include/list.h create mode 100644 Lab7/include/load_kernel.h create mode 100644 Lab7/include/math.h create mode 100644 Lab7/include/mbox.h create mode 100644 Lab7/include/mbox_call.h create mode 100644 Lab7/include/mini_uart.h create mode 100644 Lab7/include/mm.h create mode 100644 Lab7/include/my_signal.h create mode 100644 Lab7/include/page_alloc.h create mode 100644 Lab7/include/peripherals/base.h create mode 100644 Lab7/include/peripherals/device_tree.h create mode 100644 Lab7/include/peripherals/gpio.h create mode 100644 Lab7/include/peripherals/irq.h create mode 100644 Lab7/include/peripherals/mbox_call.h create mode 100644 Lab7/include/peripherals/mini_uart.h create mode 100644 Lab7/include/peripherals/reboot.h create mode 100644 Lab7/include/printf.h create mode 100644 Lab7/include/read_cpio.h create mode 100644 Lab7/include/reboot.h create mode 100644 Lab7/include/reserve_mem.h create mode 100644 Lab7/include/shell.h create mode 100644 Lab7/include/stdlib.h create mode 100644 Lab7/include/syscall.h create mode 100644 Lab7/include/test.h create mode 100644 Lab7/include/thread.h create mode 100644 Lab7/include/timer.h create mode 100644 Lab7/include/utils.h create mode 100644 Lab7/include/virtual_mem.h create mode 100644 Lab7/initramfs.cpio create mode 100755 Lab7/kernel8.img create mode 100644 Lab7/rootfs/a.c create mode 100644 Lab7/rootfs/cat.txt create mode 100644 Lab7/rootfs/one create mode 100644 Lab7/rootfs/ts.txt create mode 100755 Lab7/rootfs/userprogram.img create mode 100644 Lab7/send_kernel.py create mode 100644 Lab7/src/bootloader/boot.S create mode 100644 Lab7/src/bootloader/bootloader.c create mode 100644 Lab7/src/bootloader/config.txt create mode 100644 Lab7/src/bootloader/link.ld create mode 100644 Lab7/src/bootloader/load_kernel.c create mode 100644 Lab7/src/kernel/boot.S create mode 100644 Lab7/src/kernel/config.txt create mode 100644 Lab7/src/kernel/device_tree.c create mode 100644 Lab7/src/kernel/dynamic_alloc.c create mode 100644 Lab7/src/kernel/exception.S create mode 100644 Lab7/src/kernel/exception.c create mode 100644 Lab7/src/kernel/kernel.c create mode 100644 Lab7/src/kernel/link.ld create mode 100644 Lab7/src/kernel/mbox.c create mode 100644 Lab7/src/kernel/mbox_call.c create mode 100644 Lab7/src/kernel/my_signal.c create mode 100644 Lab7/src/kernel/page_alloc.c create mode 100644 Lab7/src/kernel/read_cpio.c create mode 100644 Lab7/src/kernel/reboot.c create mode 100644 Lab7/src/kernel/reserved_mem.c create mode 100644 Lab7/src/kernel/shell.c create mode 100644 Lab7/src/kernel/syscall.c create mode 100644 Lab7/src/kernel/test.c create mode 100644 Lab7/src/kernel/thread.S create mode 100644 Lab7/src/kernel/thread.c create mode 100644 Lab7/src/kernel/timer.S create mode 100644 Lab7/src/kernel/timer.c create mode 100644 Lab7/src/kernel/virtual_mem.S create mode 100644 Lab7/src/kernel/virtual_mem.c create mode 100644 Lab7/src/lib/hex2int.c create mode 100644 Lab7/src/lib/math.c create mode 100644 Lab7/src/lib/mem.c create mode 100644 Lab7/src/lib/mini_uart.c create mode 100644 Lab7/src/lib/mm.S create mode 100644 Lab7/src/lib/printf.c create mode 100644 Lab7/src/lib/queue.c create mode 100644 Lab7/src/lib/simple_malloc.c create mode 100644 Lab7/src/lib/string.c create mode 100644 Lab7/src/lib/utils.S create mode 100644 Lab7/src/userprogram/link.ld create mode 100644 Lab7/src/userprogram/user.S create mode 100755 Lab7/userprogram.img 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 0000000000000000000000000000000000000000..0dd0e2f435a74e6a0c01e2a30fa0246d55ee3091 GIT binary patch literal 32753 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 OPqI~*e!TdI|Nj8Si!WyY literal 0 HcmV?d00001 diff --git a/Lab7/bootloader.img b/Lab7/bootloader.img new file mode 100755 index 0000000000000000000000000000000000000000..3bca60bbee288666e26a489804b1ca873f855064 GIT binary patch literal 7832 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 Wx-t7ZYJD$WX?-u=tIUoq^!9(1gbAMj literal 0 HcmV?d00001 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/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..d43fd936b --- /dev/null +++ b/Lab7/include/mbox.h @@ -0,0 +1,6 @@ +#ifndef _MBOX_H +#define _MBOX_H + +void mbox_main(); + +#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..51eb0e34c --- /dev/null +++ b/Lab7/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/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..e843d9a0a --- /dev/null +++ b/Lab7/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/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..31b8255dc --- /dev/null +++ b/Lab7/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/Lab7/include/syscall.h b/Lab7/include/syscall.h new file mode 100644 index 000000000..aca95eae1 --- /dev/null +++ b/Lab7/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/Lab7/include/test.h b/Lab7/include/test.h new file mode 100644 index 000000000..994fcc065 --- /dev/null +++ b/Lab7/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/Lab7/include/thread.h b/Lab7/include/thread.h new file mode 100644 index 000000000..a987a47be --- /dev/null +++ b/Lab7/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/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/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/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 0000000000000000000000000000000000000000..0676fb1584617bc4d73624b3b212a12133e97d8e GIT binary patch literal 247296 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 literal 0 HcmV?d00001 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/userprogram.img b/Lab7/rootfs/userprogram.img new file mode 100755 index 0000000000000000000000000000000000000000..2c228ba232cfbf7fc17322e1b6bcc38e3a24c19f GIT binary patch literal 7224 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 +__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..81fb65548 --- /dev/null +++ b/Lab7/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/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/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..6f10a5784 --- /dev/null +++ b/Lab7/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/Lab7/src/kernel/kernel.c b/Lab7/src/kernel/kernel.c new file mode 100644 index 000000000..85e57b892 --- /dev/null +++ b/Lab7/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/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..f6d91caa7 --- /dev/null +++ b/Lab7/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/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..0d61c3a17 --- /dev/null +++ b/Lab7/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/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..74a60af3e --- /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, 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/Lab7/src/kernel/shell.c b/Lab7/src/kernel/shell.c new file mode 100644 index 000000000..3a8d4f13f --- /dev/null +++ b/Lab7/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/Lab7/src/kernel/syscall.c b/Lab7/src/kernel/syscall.c new file mode 100644 index 000000000..1a867f807 --- /dev/null +++ b/Lab7/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/Lab7/src/kernel/test.c b/Lab7/src/kernel/test.c new file mode 100644 index 000000000..0f97e5b8f --- /dev/null +++ b/Lab7/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/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..c88ac7b82 --- /dev/null +++ b/Lab7/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/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/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..e780073bf --- /dev/null +++ b/Lab7/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/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..be47302ee --- /dev/null +++ b/Lab7/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/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 0000000000000000000000000000000000000000..494ba66074dfecf8d5401beca4aa079174f6f190 GIT binary patch literal 7272 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 literal 0 HcmV?d00001 From 3ec38b94c68b465718be580f9df79c47155b4918 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Tue, 13 Jun 2023 16:30:06 +0800 Subject: [PATCH 19/22] Lab7: Base 1, 2, 3, 4, Adv 1, 2 --- .vscode/settings.json | 8 +- Lab4/initramfs.cpio | Bin 10240 -> 8192 bytes Lab4/rootfs/userprogram.img | Bin 9240 -> 7136 bytes Lab4/userprogram.img | Bin 7136 -> 7136 bytes Lab6/Makefile | 3 + Lab6/bootloader.img | Bin 7832 -> 7944 bytes Lab6/include/page_alloc.h | 6 +- Lab6/include/peripherals/base.h | 10 +- Lab6/include/peripherals/irq.h | 9 +- Lab6/include/peripherals/reboot.h | 8 +- Lab6/include/reserve_mem.h | 4 +- Lab6/include/thread.h | 16 +- Lab6/include/utils.h | 36 ++- Lab6/include/virtual_mem.h | 41 ++- Lab6/initramfs.cpio | Bin 247296 -> 256000 bytes Lab6/kernel8.img | Bin 31088 -> 33888 bytes Lab6/rootfs/vm.img | Bin 0 -> 247488 bytes Lab6/src/kernel/boot.S | 22 +- Lab6/src/kernel/device_tree.c | 3 +- Lab6/src/kernel/exception.S | 52 +--- Lab6/src/kernel/exception.c | 98 +++++- Lab6/src/kernel/kernel.c | 7 +- Lab6/src/kernel/link.ld | 7 +- Lab6/src/kernel/read_cpio.c | 3 +- Lab6/src/kernel/reboot.c | 10 +- Lab6/src/kernel/reserved_mem.c | 13 +- Lab6/src/kernel/syscall.c | 40 ++- Lab6/src/kernel/test.c | 33 +- Lab6/src/kernel/thread.S | 35 ++- Lab6/src/kernel/thread.c | 72 +++-- Lab6/src/kernel/timer.S | 2 +- Lab6/src/kernel/timer.c | 1 + Lab6/src/kernel/virtual_mem.S | 10 + Lab6/src/kernel/virtual_mem.c | 117 ++++++- Lab6/src/lib/utils.S | 10 + Lab6/userprogram.img | Bin 7272 -> 7376 bytes Lab7/bootloader.img | Bin 7832 -> 8208 bytes Lab7/include/devfs.h | 21 ++ Lab7/include/mbox.h | 1 + Lab7/include/mini_uart.h | 3 + Lab7/include/read_cpio.h | 9 + Lab7/include/stdlib.h | 3 + Lab7/include/syscall.h | 10 + Lab7/include/test.h | 1 + Lab7/include/thread.h | 3 + Lab7/include/tmpfs.h | 38 +++ Lab7/include/vfs.h | 95 ++++++ Lab7/initramfs.cpio | Bin 247296 -> 405504 bytes Lab7/kernel8.img | Bin 31088 -> 42416 bytes Lab7/pack.sh | 4 + Lab7/rootfs/userprogram.img | Bin 7224 -> 0 bytes Lab7/rootfs/vfs1.img | Bin 0 -> 404216 bytes Lab7/src/kernel/boot.S | 1 + Lab7/src/kernel/devfs.c | 129 ++++++++ Lab7/src/kernel/exception.c | 72 ++++- Lab7/src/kernel/kernel.c | 9 +- Lab7/src/kernel/mbox.c | 70 +++++ Lab7/src/kernel/read_cpio.c | 173 +++++++++++ Lab7/src/kernel/reserved_mem.c | 4 +- Lab7/src/kernel/shell.c | 28 +- Lab7/src/kernel/syscall.c | 77 +++++ Lab7/src/kernel/test.c | 25 ++ Lab7/src/kernel/thread.c | 24 ++ Lab7/src/kernel/tmpfs.c | 138 +++++++++ Lab7/src/kernel/vfs.c | 490 ++++++++++++++++++++++++++++++ Lab7/src/lib/mini_uart.c | 22 ++ Lab7/src/lib/string.c | 34 +++ Lab7/userprogram.img | Bin 7272 -> 7648 bytes 68 files changed, 2004 insertions(+), 156 deletions(-) create mode 100644 Lab6/rootfs/vm.img create mode 100644 Lab7/include/devfs.h create mode 100644 Lab7/include/tmpfs.h create mode 100644 Lab7/include/vfs.h create mode 100644 Lab7/pack.sh delete mode 100755 Lab7/rootfs/userprogram.img create mode 100644 Lab7/rootfs/vfs1.img create mode 100644 Lab7/src/kernel/devfs.c create mode 100644 Lab7/src/kernel/tmpfs.c create mode 100644 Lab7/src/kernel/vfs.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 304a8e2ce..166afa72f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,13 @@ "list.h": "c", "thread.h": "c", "signal.h": "c", - "limits": "c" + "limits": "c", + "mbox.h": "c", + "virtual_mem.h": "c", + "string.h": "c", + "tmpfs.h": "c", + "vfs.h": "c", + "string": "c" }, "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/Lab4/initramfs.cpio b/Lab4/initramfs.cpio index 55d33a5527daaa16aea9f6474c77f21cc807a82a..e7ad0c236db58bcfba3676135c810c96d2d92536 100644 GIT binary patch literal 8192 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 literal 10240 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 diff --git a/Lab4/userprogram.img b/Lab4/userprogram.img index dbff941a2cb18aab1060c1e14c26fa80637e37a9..c6419d232705ec993c562b5c09d108337cbab56a 100755 GIT binary patch delta 46 acmaE0{=j_0E@?Fm1`vplfzV(M76Jgb1_WLJ delta 46 acmaE0{=j_0E@?Fd1`vplfzV(M76JghW(1A^ diff --git a/Lab6/Makefile b/Lab6/Makefile index abe49536e..6f5ff4ea6 100644 --- a/Lab6/Makefile +++ b/Lab6/Makefile @@ -36,6 +36,9 @@ 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) diff --git a/Lab6/bootloader.img b/Lab6/bootloader.img index 3bca60bbee288666e26a489804b1ca873f855064..564c557472a775695fcf22805c26b8c61f9a0ec7 100755 GIT binary patch delta 1094 zcmZuxT}V@57=FKVrql3ebM8l-ZfANn)@<-0VgJK^9rwP05S22v$V9(9`>E(-|~4=Y7BDJn!>9-+8~C;hNqP z-m>COHTV>D^Q#DNlCJ>x{wRu3W61#a`zF;|b0Q9DBMp4GoB)57bZ0+#FO8VH8=cY; z!Xdm2X2_etsKYpoQE%2w_hGCj zAbP|+F$~H2156Xw2QhYdIElcphxsZ`;`@>{3==RHSUsfrn-SAxrCvDGXIw+FPE2_{ z7u6zojR|C?1hSGuz^QF-N{v`Z5IvBc!nkBD6IbwX(Z#}ia-M!KD08*AV-|c_acik| z(RE}Ic;bOmgJFmyUe_98NMQRZBLND^RBbZK()yL`ZoAX|xGW;ayxBiz#Cl3wK1tFaf}$S6q#m-`B??Mk zb~`JLKyM;wdnj`l46$591ij?S9)e&-z2rk^2oAE_ceWc@7w-MO@0{Q7_x;Yf=iJ=W zzdKq})nS2{ZaFia;T-WJ0I_9V7d4j^*mGG8dP+SF>em8@VL1jFSv^-vUa9w-5v@Ue zi^Eg+3Qj1W#AwHu#K=3>9G>vneNQoecLIDDOuTQ7<9#^^kkv^uVoczlD1<}!s&`C$ zFD>p#*MAvV6s$ez0LBbwk8eP0)4C;);e>cK=86FFpoyQCbiJ)8C#J~iW4fQnWEZ_k zwoC7^VZO-@RmD67S{jI!{-*1FENY1v^4(0{&{W|8GL$H`oU)N9J-@b(w9;ap;&*`G zi6bBJ+e!uDJ{hCq!ixfCCQ7e}8~N)5VuL>DBxpe+N_ZZXm*|uz@(gncXPIHZMR;%# z7wLl7KxSyIqn^B^9gck@K_56;$RJ&C)aO(;(Jkx5hzEHDl(15+qyc$Orh~Z)V^`Ru z66g;zeZd%a7K$e!^OB;b!ds z(XK!!%On!?R>ii~#!z;SgE$*_gdi4&a%6~lw9;gd88k#w|A|hm7DXM#rL?TF+WW*% zkd#K%Yi>&JGecRm8Ipi!lb{93PunZKkpYC^Nr?i&OlBpSp9*F7p7zRag#W->u+shf z;R*-1`)CMpU~NiD+QFn7$k`qOZ6=gvbKcR?JYNoFrPs&gKY#xQ_E_mojZSk%Kaf?6 zi2ww0&D}`6R-%O2WX=o + +#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 index 0676fb1584617bc4d73624b3b212a12133e97d8e..2be1223b6c44bf47b1fab04399da12e7954431e5 100644 GIT binary patch delta 9688 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 OCs2QCDB@2o%l~hk;#sr+ delta 9519 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 diff --git a/Lab6/rootfs/vm.img b/Lab6/rootfs/vm.img new file mode 100644 index 0000000000000000000000000000000000000000..8ba674e6c8650e239c6cd1322db6637bdd023baf GIT binary patch literal 247488 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
LaIRxxGkGu2Nc^r*p2nQQ3R%~CR5{JU3A)Ai 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
SF)2$rkIVdBRwZU;YFI)_}YEc(*mgDU?` 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 Y|NVQD{zsGk@BjU0zWVPb{hucNe;je&3jhEB literal 0 HcmV?d00001 diff --git a/Lab6/src/kernel/boot.S b/Lab6/src/kernel/boot.S index 81fb65548..d305049e5 100644 --- a/Lab6/src/kernel/boot.S +++ b/Lab6/src/kernel/boot.S @@ -6,15 +6,17 @@ _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, =_dtb_ptr + ldr x21, =PA(_dtb_ptr) str x24, [x21] b go x24c: - ldr x21, =_dtb_ptr + ldr x21, =PA(_dtb_ptr) str x0, [x21] go: mrs x0, mpidr_el1 @@ -30,8 +32,6 @@ master: orr x0, x0, #(3 << 20) msr cpacr_el1, x0 - ldr x1, =_start - // get CurrentEL mrs x0, CurrentEL and x0, x0, #12 // clear reserved bits @@ -47,16 +47,20 @@ master: bl from_el2_to_el1 bss: - ldr x0, =__bss_start - ldr x1, =__bss_end + 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 - mov sp, #LOW_MEMORY // 4MB - // ldr x1, =_start - // mov sp, x1 + ldr x9, =PA(_kernel_end) + mov sp, x9 bl kernel_main b proc_hang // should never come here diff --git a/Lab6/src/kernel/device_tree.c b/Lab6/src/kernel/device_tree.c index d1b67143f..ce90a1944 100644 --- a/Lab6/src/kernel/device_tree.c +++ b/Lab6/src/kernel/device_tree.c @@ -3,6 +3,7 @@ #include "stdlib.h" #include "mini_uart.h" #include "device_tree.h" +#include "virtual_mem.h" typedef struct fdt_header { @@ -75,7 +76,7 @@ int initramfs_callback(void *dtb) { uint64_t addr = (uint64_t)rev32(*((uint32_t *)prop_val)); printf("initramfs_addr at %p\n", addr); - cpioDestGlobal = (char *)addr; + cpioDestGlobal = (char *)KERNEL_PA_TO_VA(addr); } p += rev32(prop->len); p += pad_to_4(p); diff --git a/Lab6/src/kernel/exception.S b/Lab6/src/kernel/exception.S index ab8b8cc66..373463a08 100644 --- a/Lab6/src/kernel/exception.S +++ b/Lab6/src/kernel/exception.S @@ -1,6 +1,8 @@ #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 @@ -15,26 +17,6 @@ disable_interrupt: 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] @@ -60,26 +42,6 @@ disable_interrupt: .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] @@ -143,24 +105,28 @@ el1_exception_vector_table: .align 7 set_el1_exception_vector_table: - adr x0, el1_exception_vector_table + ldr x0, =el1_exception_vector_table msr vbar_el1, x0 ret lr exception_handler: - save_all + 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 + 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 diff --git a/Lab6/src/kernel/exception.c b/Lab6/src/kernel/exception.c index 6f10a5784..94c527769 100644 --- a/Lab6/src/kernel/exception.c +++ b/Lab6/src/kernel/exception.c @@ -127,23 +127,23 @@ void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsig void el1_irq_interrupt_handler() { - unsigned int irq_basic_pending = get32(IRQ_BASIC_PENDING); + 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 (get32(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte + if (get64(AUX_MU_IIR_REG) & 0b100) // Receiver holds valid byte { uart_rx_handler(); } - else if (get32(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty + else if (get64(AUX_MU_IIR_REG) & 0b010) // Transmit holding register empty { uart_tx_handler(); } } // ARM Core Timer Interrupt - else if (get32(CORE0_INTR_SRC) & (1 << 1)) + else if (get64(CORE0_INTR_SRC) & (1 << 1)) { long cntpct_el0, cntfrq_el0; asm volatile( @@ -156,8 +156,71 @@ void el1_irq_interrupt_handler() return; } -void el0_to_el1_sync_handler(unsigned long trapframe_addr) +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" @@ -196,8 +259,17 @@ void el0_to_el1_sync_handler(unsigned long trapframe_addr) current->status = FORKING; current->trapframe = trapframe_addr; int ret = fork(); - curr_trapframe = (trapframe *)get_current()->trapframe; + 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) { @@ -208,8 +280,20 @@ void el0_to_el1_sync_handler(unsigned long trapframe_addr) { 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); + 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) { diff --git a/Lab6/src/kernel/kernel.c b/Lab6/src/kernel/kernel.c index 85e57b892..9e2d2ddbe 100644 --- a/Lab6/src/kernel/kernel.c +++ b/Lab6/src/kernel/kernel.c @@ -4,9 +4,10 @@ #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) @@ -17,11 +18,9 @@ void kernel_main(void) fdt_traverse(initramfs_callback, _dtb_ptr); - thread_init(); - memory_init(); - // virtual_mem_init(); + thread_init(); shell_start(); } diff --git a/Lab6/src/kernel/link.ld b/Lab6/src/kernel/link.ld index a460f25b2..4823bda12 100644 --- a/Lab6/src/kernel/link.ld +++ b/Lab6/src/kernel/link.ld @@ -1,6 +1,9 @@ +_kernel_start = 0xffff000000080000; +_kernel_end = 0xffff000000c00000; + SECTIONS { - . = 0x80000; + . = _kernel_start; .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } PROVIDE(_data = .); @@ -14,6 +17,8 @@ SECTIONS } _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/read_cpio.c b/Lab6/src/kernel/read_cpio.c index 0d61c3a17..b278cda49 100644 --- a/Lab6/src/kernel/read_cpio.c +++ b/Lab6/src/kernel/read_cpio.c @@ -1,5 +1,6 @@ #include "stdlib.h" #include "mini_uart.h" +#include "virtual_mem.h" extern char *cpioDestGlobal; @@ -145,7 +146,7 @@ int load_userprogram(const char *filename, char *userDest) else cpioUserPgmDest += (sizeof(cpio_t) + ns + ((sizeof(cpio_t) + ns) % 4)); - printf("load %p to %p\n", cpioUserPgmDest, userDest); + printf("load %p to %p (kernel va)\n", cpioUserPgmDest, userDest); printf("size: %d bytes\n", fs); // load content diff --git a/Lab6/src/kernel/reboot.c b/Lab6/src/kernel/reboot.c index da809c59a..00d3d2c6a 100644 --- a/Lab6/src/kernel/reboot.c +++ b/Lab6/src/kernel/reboot.c @@ -7,13 +7,13 @@ void set(long addr, unsigned int 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 +{ // 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, PM_PASSWORD | 0); // full reset - set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick + 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 index 74a60af3e..db4e9c2af 100644 --- a/Lab6/src/kernel/reserved_mem.c +++ b/Lab6/src/kernel/reserved_mem.c @@ -3,6 +3,7 @@ #include "dynamic_alloc.h" #include "reserve_mem.h" #include "stdlib.h" +#include "virtual_mem.h" reserved_memory_block RMarray[100]; int RMindex = 0; @@ -37,12 +38,12 @@ 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"); + 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/syscall.c b/Lab6/src/kernel/syscall.c index 1a867f807..6146fa396 100644 --- a/Lab6/src/kernel/syscall.c +++ b/Lab6/src/kernel/syscall.c @@ -2,6 +2,7 @@ #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" @@ -49,17 +50,48 @@ size_t uart_write(const char buf[], size_t 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; + 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(); - load_userprogram(name, (char *)target_addr); - asm volatile( - "mov x0, 0x0\n\t" // EL0t + "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" diff --git a/Lab6/src/kernel/test.c b/Lab6/src/kernel/test.c index 0f97e5b8f..acafc75fd 100644 --- a/Lab6/src/kernel/test.c +++ b/Lab6/src/kernel/test.c @@ -6,6 +6,7 @@ #include "read_cpio.h" #include "utils.h" #include "timer.h" +#include "reserve_mem.h" extern task_struct *get_current(); extern void set_switch_timer(); @@ -62,16 +63,44 @@ void test_thread() 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; + 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); - load_userprogram("syscall.img", (char *)target_addr); + // 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(); diff --git a/Lab6/src/kernel/thread.S b/Lab6/src/kernel/thread.S index ffc3ece7b..e809a3f29 100644 --- a/Lab6/src/kernel/thread.S +++ b/Lab6/src/kernel/thread.S @@ -1,23 +1,42 @@ .global switch_to switch_to: - stp x19, x20, [x0, 16 * 0] + 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 - str x9, [x0, 16 * 6] + 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] + 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] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] // sp = next->sp mov sp, x9 - msr tpidr_el1, x1 + 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 diff --git a/Lab6/src/kernel/thread.c b/Lab6/src/kernel/thread.c index c88ac7b82..2b6a5ce98 100644 --- a/Lab6/src/kernel/thread.c +++ b/Lab6/src/kernel/thread.c @@ -12,6 +12,7 @@ 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; @@ -25,8 +26,8 @@ void schedule() task_struct *next = del_rq(); if (next == NULL) - next = &kernel_thread; - if (cur != &kernel_thread) + next = (void *)KERNEL_PA_TO_VA(&kernel_thread); + if (cur != (void *)KERNEL_PA_TO_VA(&kernel_thread)) add_rq(cur); set_switch_timer(); @@ -35,16 +36,19 @@ void schedule() if (next->status == FORKING) { add_rq(next); - switch_to(cur, &kernel_thread); + // 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); - switch_to(cur, &kernel_thread); + // 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); } } @@ -72,7 +76,9 @@ void thread_init() { INIT_LIST_HEAD(&task_rq_head); INIT_LIST_HEAD(&task_zombieq_head); - kernel_thread_init(&kernel_thread); + 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; } @@ -84,17 +90,18 @@ thread_info *thread_create(func_ptr fp) 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->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; - new_task->task_context.lr = (unsigned long)task_wrapper; - new_task->task_context.sp = new_task->kstack_start + MIN_PAGE_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); @@ -152,7 +159,10 @@ void do_fork() 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); + } } } @@ -162,6 +172,10 @@ 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; @@ -188,25 +202,23 @@ void create_child(task_struct *parent) // copy kernel stack parent_d = (char *)parent->kstack_start; child_d = (char *)child->kstack_start; - for (int i = 0; i < MIN_PAGE_SIZE; i++) + 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; + parent_d = (char *)parent_ustack_start; child_d = (char *)child->ustack_start; - for (int i = 0; i < MIN_PAGE_SIZE; i++) + 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; + 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; @@ -214,10 +226,30 @@ void create_child(task_struct *parent) 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; + // 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() diff --git a/Lab6/src/kernel/timer.S b/Lab6/src/kernel/timer.S index a22d66ec4..ffd869d9c 100644 --- a/Lab6/src/kernel/timer.S +++ b/Lab6/src/kernel/timer.S @@ -1,4 +1,4 @@ -#define CORE0_TIMER_IRQ_CTRL 0x40000040 +#define CORE0_TIMER_IRQ_CTRL 0xffff000000000000 + 0x40000040 .global core_timer_enable core_timer_enable: diff --git a/Lab6/src/kernel/timer.c b/Lab6/src/kernel/timer.c index 5e8c42f58..74be207e2 100644 --- a/Lab6/src/kernel/timer.c +++ b/Lab6/src/kernel/timer.c @@ -4,6 +4,7 @@ extern void enable_interrupt(); extern void disable_interrupt(); +extern task_struct *get_current(); typedef struct timer_queue_node { diff --git a/Lab6/src/kernel/virtual_mem.S b/Lab6/src/kernel/virtual_mem.S index 5453ad829..0921e45ce 100644 --- a/Lab6/src/kernel/virtual_mem.S +++ b/Lab6/src/kernel/virtual_mem.S @@ -51,4 +51,14 @@ identity_init: 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 index d05dd504e..bacba429c 100644 --- a/Lab6/src/kernel/virtual_mem.c +++ b/Lab6/src/kernel/virtual_mem.c @@ -1,10 +1,113 @@ -extern void tcr_init(); -extern void mair_init(); -extern void identity_init(); +#include "virtual_mem.h" +#include "stdlib.h" -void virtual_mem_init() +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) { - tcr_init(); - mair_init(); - identity_init(); + 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/utils.S b/Lab6/src/lib/utils.S index c35527a42..583dd1288 100644 --- a/Lab6/src/lib/utils.S +++ b/Lab6/src/lib/utils.S @@ -13,3 +13,13 @@ 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/userprogram.img b/Lab6/userprogram.img index 494ba66074dfecf8d5401beca4aa079174f6f190..4d9085ed08c32247f9dee45d4db90959ba3612c1 100755 GIT binary patch delta 848 zcmZuvO=uHQ5T2J!XrrsG4cYt{*=#B*MWkgx(}TECSVU5bODe@nD|irlvIRkU=qeth zA|yPq6%Q6s)PSfIgdk{&mtKSru<9)p1?|n$%O29_%yvT(@bZ4XneUr#=DlC(%JA!C zfPGok0?WO@O(E(vDO7yQzl9Hu)bO^*;NGJFxVpY2a!bs%ehabiSBOYs_Wg^71cl9= z;P4&uRsF}adXs$@Ic3px6uRfDNyJI#HqR)>*e8Bn(OH?lR8Fy1{Esrq9`Z~?pKCIQ zkqMV}_c6F-X|OcSfDMqJA;cNjmfdGp!Bad;Rq%cU_ZiDqV6bRW?g0-!m@A#YO;P?g8S(l(l zz*|hBz32)+K3S1V8S>mFzWN{WkL}{9M;Z_KP^`a)9*$HQmemZMPs9|KH zkDHFRI+^bHI#{PMyYjp&O{~!yVL!Yl9>|b~8=eOh`8Q?TqPXh`b&6YJq0>X>P^bAw zT$NCTPsP`QS<_*Lb|;hNXZC&{L{ARY12DWDRRMb4)mv%_2-ETOJ_z%3i3p!e1pWc= CF=Y4v delta 730 zcmZvZL1+_E5QgVvH<(HawlTZg8qMyuXi*R)P~*we!k&cMr6(`yO%S1>AYu=Bh&>n) zQeLp0tQMi#gD5$zP)(o*!HWj!tV&7i(`6v|A>sYi_aU!`2w5eE%3OPZV5ZOT2PkgB%r&&!?b zV$wLpK8QO;fvt&EW1KYvGbh-hm^2F!bwu1V&5Dga2HfZUybb8f5eLtQFwXdic?J)B z;v1@NDLvKt@a&Km;+egeXVTSn0Xf7ZO-kvx+Dpg$GI@F;&+!f@0ql(-KLF}2hx^bQ z=if0W1HpF0HOpid#GEx;w7u{czCbizvA0ml;E4}s@3&!dxwC<}oY2-Y0JRy{3klH> zU#t`74}0O31}ju}jv|Unkr`_CbUGNL1r@aQ4|H`m6glJ-qC7COToAf zeM33~kb{1PD>$}zHQ-hrq6{zvlqGZ?#4Rs8bHy#@QT|Ozvpf6dSyuymeOw52kSYbv z^tm7p>}r+4R07e|b8n<$|F9Q2Z!Z;p|NS|f(VcBQZE6=kvR#*{0E|Y%^Ju)4<~}u} zWf1WCr(F1Z_@AdNi^qd`u{OBgV|gJfId{h_v2@^*sOR>N@El~30oy9l`G4Op+M@p) diff --git a/Lab7/bootloader.img b/Lab7/bootloader.img index 3bca60bbee288666e26a489804b1ca873f855064..3d60319b027c3eababd542bdc90c5e16a538f16c 100755 GIT binary patch delta 1137 zcmZWpT}TvB6h3!m{i!rocXhV)XPwoke2L0z0@uc$+9E>QBmylhU&IYUgdW-l$LOJ~ zhaIk#J@tcRyAS;+8cUWxAygp9Vv;YhN`jGxzpqF z%l@L{FH@jPOKItk@U~z*K-apa3Fd-SVBeCl#(l$qI1lQ|DH2$%;{v~U2x9mdfJ^0* zp-Z<%%hgLqOo~hWu7*muh)eykYx^}MAJtMyBYbwS18pT6q zMAqqLvNt`NWROM5)u#i4gij)0213a%%;Y<56?Tw$dRN$;@sT80A+(D>x{piyrp@{e1nzilhpv+RzgYuX)MfqA@#-h6=>LKRG$!AAaJizDt2+Zr9W7dw9%siF+3(?35e`U%~l|`$qJ6)RiC(Qst0nmO~C8-cACw98$BG z(*}wBU+$Rrwo%#`i3zL`_l-RsMS{H`A8Y_&0{gAz717fMYgPf&s|>__Z}d~feSZ;KAhwEPS9lWaM>2fP#q4gM>cr5R&N_$% zAB>mWY-+h66=LTrQM>~6Z&x3otA{%f|dxBCQp`7P>1RuCJ2?`5Eisu zW_XvfOD+-E?xC!uVj?A>L(s(*J9G$Es8ff)5?n~8=iM10I`m$ikN5xo&BHvemYV9- zI*t#yAZNNRKCcLY;%R`~nrZs;ZEoOaT(si)wKs)z0ra0Iv9Nl|QTQc5k*;SUn$W-1 zL9UipWPSLkGoR2GbtC2gS^u$n$4-L3HC^$F;45yYT=juG?b!BRGR=XSNtN}dtQTcG z#K!P|XK4cl2X(#l0);yKP%<{XYC?^T_;=wXyYJuId=)J!xU=Qx*O`?u_SxUbt^~H? zM|L~VgYVc};0Tr&2K#ZEod_l-I>`a&8e0%16$$c}5wNQwc#Bc$BdChnqLzV35l*JO zjpC@{&=IB-uJhUbfUW1fX38b*1#1cq)!v3|xraEM$u(|EpHWnMR3|mga#Ou!0!yI4 zW~=^98(5H31w2;0Mm5)S*ebBTkPiph`B2CHSf;wiH1E|0u-VI`MR=sZ;k#w1%&dH& z+UyrTc>%Q1IC~vR97|@Z%d|UAdqTH7KCHPwi{=}RG@s0-E&M~9`b;72Kq`J|dDoCT&zC!AGmX3sRgbhVRyuhwUmg>7RRZK~D b(%o#Z^(AW(7x&HDPdZ>f>2Z + +#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/initramfs.cpio b/Lab7/initramfs.cpio index 0676fb1584617bc4d73624b3b212a12133e97d8e..1640f8c4ad3b90dadff7ff20285cd5d4c2b15d36 100644 GIT binary patch literal 405504 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

kB>&}lcHq8a+fC+(T*vXygQbOt&4+{J&H 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 opLiif|NN$4 delta 8801 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 ../initramfs.cpio +cd .. \ No newline at end of file diff --git a/Lab7/rootfs/userprogram.img b/Lab7/rootfs/userprogram.img deleted file mode 100755 index 2c228ba232cfbf7fc17322e1b6bcc38e3a24c19f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7224 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�$5T3bfUfi!~)t8f40G1mc=YEL3e#o195=B+7?@H zY}Tt-pr3GD`XD~FOZ4L$>49Sn3WLS4h%P_nV@FGfS(Opv8M2L>z%Z>)AABBho+#`T zjs52+4i0kzQnQlfRF)ZZ@H2h9@+b$7vW$ZY z2I3AdPuHS2RC+HK9+ZH`8WQ4)HsB-Lf;_VoEL>J;l{Z-Dach){Ey;@wl$LY&{}BUf zN4wZV6}Wj$%hrY2sFxcQTag;}ZYsCimBv=FvEQfN*oe`f5|l+-T^+67MVZ4qW9BNO zIYyi-+hTbuQQfm?03QTS(S*F!63wv?Cc3ee5Q{^5TLHg8TuvI>;uVs}*dR99tUh^vDS z9rX>ZYfQ1r$1?pmQQGFk8!}RSXt)+{X&88 zRLrlSwV6>5NmbYi;>rij+)C+Uf2MF~ZHEw7YDN)bLzp09ZLk)n;VAH>qrSpgoYHdQ zs5QiMu~;bhC(;v`Tz%RPj(pP29kd++hdD;Yt(Afv z5SOi@y+CYpXWeN|%<>A?ij6iewF@fdBiGvbtQeVWqnLF;d-SC%6fX!&3VI4528{M@ z(P+I?V26aUne}$0n8$8kiUoe_9Z3QnmB9QN|3c^^4idJoAM;*_59&iDxb|w?r}-CT zyHv)9o60sZZFgGjHGX#sdax5%iupL<$VxDq#-4HTT_M(s)*Un)V9SUjG_dxdlU9)i zm;*2z!-U_#S8NrUa=X){n5pJkilsxzuwa%Oo9$qnMY(FCT#{j5@Srlz1+Z@=bgjmL zuN4tn9*mCL33VxsSX`NC#~UkAMaW&p!Q5)ZOgiJ9y@nmpdYo;B4+7Iz7k3 ze@cyeXL7qRDJa2681pi+z;j!TNB}OKm|UmVh5UCt%NtQKkQ+5y%s>_sfJN7>e-G-31WF3JVka$k4VK@4P$)}e+C zr7)MpR*d3&nw|{=(W`(jsfIqTSq=rz$DlIdo7h?&{go-{(aWK1jqs|AA6TmrYj>lW#-5qk3CcP4 zbe7_5tqUorb~k|$tuML;04}72;%ZP65JXhQ`l6_(O6WlOFed>EhHCjP)_jD%d^K}` zXf`8+wSx2Fyk8tlW8R^@jI1L3tWnHy^GUCXn8BQXvsEmHg&lpLYxX)VP4;2 zebHgQ;YUMfhx*YXZq93_I7c?LBf(Nzd=pWK}kYerJ zdBf-Xq)-eP8*4?Wo)=U7N^etDH_~upYN6SBu&l1ij$Yg33WBaAI@NV5G}@;6MK9uk zSuB->IX`27j3pm0pbu9^Zq)8_QY(^xZ7L)9;IYRHeDD`Mc%8}LE33c&sWS*X)`BGfcc__n(Z!Rho!Bt!4=JK(^o;vBk4Ja{{rrYnoTkX z471hQ+6mYXfbtFd5ziU3B%Q{CR5&hP@1oC6br!XSF73z`r_d9dH_9%feZhKr5?t!! zV>c~rIdaz!B?ypkgP}FCd{NpbVFY*w7r`dsICRS~^Nk9%b=cPkV@;A7$qDDS=BIt~ zAV+*Ie2{z?-b{0#U{{!zu47#!jM<=U*aCb5pkYA8^J9QV?T^EAT${qCq@KmbRt&5Ff~7LvsPn=hz}n?P%kuze)<;L_M;>KB9XT&cDxjSvrVw z9Ep=vOHU;hQTUH0BJJOfC9IJcWd!=GIYMd%+0$HM$X~~r5SR;MtB-H}M=a!wekw41 zV3~XTobj@cBgDJ~S{h@bI58#ADNhtL?8e)noRdCwAGI}avpSsOgHh&SS24uZvQqz7 z$?_QY3HyeTQ>Ek{=eXeRH+XRCEc{07qk9k!HQEyvWon4I8>Z7-FFK$dv1*8^M%)g3 zKOsvUz2|YY=Q7yM3qc!jfBZ?f@$PW{4Ka1-gQL%eb82772a0)7XhYrD6&;u} z_9!>>Cixh6YMUO{aX>TZ@*mC-p?NNlF{-xRIs#mKSAo*pW}W&5I9Q*6uA{=jnr*No z*b~N>eX6T==_%dc%(0JUbol}~%+iT)n)lSE_J__n9`?h^He=H+dfq3(`VCl*5OJ~;UjzGLZ7SAbpj0~2;=j(~G4LDqg-edTUD|eH zaa2CsI2N)2)a|I_Nu=~~>2}vR91(p2l@HiEK5P$5(1-cR_ibu-&rO5t#4eQ!j!chi zigbopFN*(HMzE_5>Fan0(Q}X==w~u_-P_8&|l z#x7H8Mxybwzea_L-#tU+g7rzEL&si+U{~vXm4RdzKD--VvC z3UguwR^AgeUBljJxnXxOoo^#>cS%L;Nd_M(;R`95*5$Ozv|eFUfsYvLbEQ&P*j?xr z27PhTE`l%8ryp}f$C&Smeec-&=$%J6Ab*|mBR*jFoPcej+%Xi7cDNi|jRw(9e<2tl z%fK3lY81X9;87H_W!Z$c5jI6JgSd|PLE6uS?LBDI+LoB(jx{8ul(bNC(WK8`MgB2I z>RXPqZNTP^*PgR+6YFP?tZ467s&{}XE0tksyk)e|9*GPa1Xd8MLG1+rmH(zFM2b2a(KGtWgL|L&biD#VEzP z;Tia|f{1n704F59HGu_o5i^<7X0gJ$Ezh8*Z4EDGG#up*WyR_%;v>vqoKP_bMT+s# zSPZHW1nmr413%CQFf`h`&lJfopF$j)L|9q@`xO$lN%)&$VL!s9khEv9%EnPAga@gZ z_mBO~2wMqe2F0+sv4jrlk0TZk?8iy2drQKTsh0uouoJ^hA&5}z;i^%9kTD1 z^x!~}QgW8!gSN_s@J+&W5vxFJsbbGgy9odK82vzUsf->+Qx}o*#@GitF6Lw5%(WXk^0X}KaiE=SEv&+I<%&g0`M6F$E zhxu)>*`5!u{}838x+@2tWTh6Wq_MtQ7`yjHdXa1g>wRLcNCW;>?5Q}g7E@EH@w8{) z(GFVsnC5@@u_gi57gZ)f*gW>@b7Q?=UsOL4hKV(7rF)5EBpJarJS1{|l7q{gbN)m+GX5qqW}b_>rT-b;&PElyx-Hew@fb6^iCo(=2U zVsA*SH@s=VLGP%&=5!mvzI3aeqh{CJf#ge0J_)_V{5kH2x!H5gS z-n+&54C@}x)7gM9Lm3f=oAIO^Dw7T>I`o45@C=-ztd}sChMb^N6|!%ewAL$OoX{2G zr|3HRG()V3+XQxn^;Ci1cGlQr%FgPL5AeG2*!4?Xj181YpO8vq&>mw?&MviiHSC=$ z6`SE%yEAEGKQRS;i^vor#$IWdvr#IUc3Rp>wBD{sad*Jk;zJAj#Fgd1608&j?+5GG2Jqjzzlu$L2ogj`mY(O3ZNTU z4%SL0{LS%UAHQLJQO;v;^!2zUuku@EOmWpxd12B1dDL`W@PPjSV=1vZN}>(UE@CzY z7FaKw`bt>adA@COkz}2>#A+Zhl^cUzaIh4$^gt8FX1 z6xbtHr;CRNkyLc^T0i(HEbqO2^VP%Qux~8!0xTL#t0H(|T&PY856$7r$Y9%UM&z z$}h4i*%7EUT$( zgx@$ltO0;>t~uBv&)x4&USz8jyG`~5+yH9|U{dU3)(6EnpT~2aQ5CA0?eW77w^eQ2i{Rp=@`RK}T|5s1E{ z+nH{l0|XpGO&VeBw0`pLcFEqlcIB3Hw&w0}=2zY?0Pp1))pzUwP6bSX)~H47;}A_P ze2LWl{vA2q=i_geqTbiX`}%k{@{EYhBYz##hGv?#)`o3bvnw&JG!rV19 z0+`Fja7}8I15s}#S89&d@B#TtxrkrJvV2MqYcs4Oxn_GjR`Y@r;oC_b<1p`tFj6)) zgP(%O{xx!A*Z;lvcy`Y%^+ES|CS3#Gxw}Pu1~gPUzFhiCISRWOR~Aac#`wJoKY?zS zCxivSPldQ6wvL<^YxYfQ@N)z2uWDm+zEGQUk^{%X$AtM9BedmR_}Vcy?uZBY;B|&+ zMi@?rImtNucqb28a_}wG`dx9m7U6%Fy1)b|wj!sxacjWMDBgwe67tUzmS%f_F<_lM z8~ghWu%04(#fa_7VV?OqoRt0J;;doYZLCR6{(yryMD*5+9MnG`eUM(<19N%=f4Uy- z&jl~m)!e|pNo%1_Xw5kIQGipG7uXjFF=(`AssZ`is!sSU){ew|73&H{WyA{-_Ml-M zD)5O}W-7vY`V-&Oj7Z~>pXOcSAbUR6LHj>ae)!N_6Y!IpZRd!^!rqCC6nyTOw??sZ zhyx^Cmhe7?hro_#?=^}CV{53=y&&0_2D@_iJt7R$#=4Re`vsi=KOvsQZX}b!y0Vz> zam;ZP=1VX>{J_k^F>LsfTMyuiqxCEalQmTAJCw>GUJkgo7rw1jWhJCiMwchQwhEsx ztz${?sPL<8;Kz%q*jF*#D+IdTj@U(P5AZlzACq_t!p0)2USw2*yms}=arEI9Y8Kbb z=6)O90{Wv}+ILf6d)y5cw5}fLBiyW8+K5sSwOXVGzhE)24lK)Fte1+ifVEMfvn=?E zVR_MPJT=2id@HEsI>O2_Q056Q4Ypu&fr;6{`ceD2E63r3;>vKqpCJch)e+v;M2u;Z zaOML1?(k~@cg9+TuqP@Hxmu_bZpvLBVpJx*XfYS%!mm#risKAx4mz?UW~GTo&gA$vR{RzW0|EJRAlUfFWp1M~4?A`QiJXHd!% z6Rmvp1rrld1Y`d8eO&J+D&@cwmY0aEQ4* zXCKp-Ae^W%c+CJl4t908gyMXGMcq2D!Zlk;C`OjbAdGO7@RBYW&eeK1?Egbz~skS;LpQ;k8blH0WU`-iot zNb71n_huZz!E4iSJuld{YTzO1(W{$l(&udOL3~Iaa5oR&S6*n=OfeTc>}$3fy(~Lm zQ>o0d8HH~^5%6jG)naJ9&={HD3XnEDxN*h@W;nVgE9ua%xk#>9n?t2e)J^S{*4^EN z`<3A-GM|TNvlsfC@DN_zwZyQ5+ZsNh_*32R1b!NQ=rI~j@9Dv`gQ*l88Y#?GDsyu? zR`Q-LWBLuPyxN3$Ex z&Nq+ZR`EGsn;N+D0lx=nL$>f&cyv7k1{K$i{wADrux%kn+Dw55qOAvQ za8#umdYEF66RtK{wd`f@(tEVev&GBG(OzJm;@KKpEsgDsn4{MEvS=oj&UwW8S!CRQ zia!_$`7tf6mX8s#k8t<>-n7z)Y=^1pXb7gZ?@ki83A_&G%~y;3Kwpki|6phKtM7Fe zOL2#_7R@G=TPd_0UzUsD8o&7_sx4LJ?;@Uk{^YAX!yG`ozJhnk%DGoNwRqzl>ijz8|uYo6D7XA7i z&Q>7MjLsPA@j0<0h3UfYro9+1jHBtpbziH-63@n6$3rXRbNoT+^hZK~jip{Ko4#kH z=TXG}2ptJ8G<7Dk>I%Px^U{u5#U-9SrNp_DL${gOsO#5?z5|~=J$`yHnm4(@!#4cK zF{6rjo!$1$-J}oCMP!z|N8lYlR;l6Q*1okji&$ieKIiB7gGQoJS^#OS>y7B}Hc#Hz z=Cv2C%Jo$r2kxcPc79woGnxIioe$r_(Oo{r{2+g)D0AArRJw)p!^}h4qJ`4-l08Ir zevEc}41B*`gMC<~^7)bt*0=ZPG33BQ2q-b-eu6(p-a`&&8u@R-@8Ud&Pt?~wUhBN( zWgf|Wz)BaFRbVNEG6wJhAARuJ-O1XF1_?V->9w8BAX;elhEqF{ygjlP=V@Qom+Yg% z8X;dq8AucB`_kDomjeo*b5EMH)JOP(T#HO=8Pa;l%Pq0D^TIdrx21f#m&#W=?MC)w zG&#DZoZ+YBAbSx5j@PaSXo#L=vazerto?Cy_DbF7Mtq{l{ekXq$T2=Focqgx2Qgm# zxL5G~d|Qfc{((JKnatvUl`m6Y;;5>fX@k7s-f=cem~+CS6%!tKesu>=58%?*-Q*K+ z4iAaoRdbJ4Pb4%h*e_J8As!O}`~hKpp*q zI+zX>X6i^~F!lDU1^L&%rw=b5meC1)^VJNV_Qs{Um)D__&g6Q5;w7A{u0priu+hg~ z4t|@80?+I5D$$R?ovA0VsF`aI;c=FkRDNTk9OsoZFq9`WEdK%Pe7qjkW>$`j!u?SD zo((w3ZJT{PxS`)(58+s^N_3V7KgXABIeV>#_`#VNQojv9uj9O9%JB0)m5285V?%;x zGFDF6Rw=rDW~cM>i1?zEq4eUShzc+pJ?c^i- zha{URPFL9S@XwPiEF9rA6F3RmBsO9CkeNHYP2l#jr|U?*%v^3_4}aD+YVP?j{`-f# z|NO4z8|u|?zx>aG4$oRcH4i!ZBl{C4!x?RO^t|E3pPwkK^NID3+x$C;GMM`QXW#uU zpP9S)29q?lXzE(ntjc2AA*dL_%vE^r;&gI-@fYk1%OG<+FMm^d<1pWO=DsF>+Ab+2 z;Q5>W1TRTRc{Ak-1sz_PHA4L?6O^qT8}&V;znjk!*nK{gO#kJ&#I#d>{4@VoW@LBg zKDwvh&v~IdN((`D&4~DB8ZUZ$|HhP)5A;!x{f+-O;-Y%f-OJ8%HSiF4YwY37w?6Wp zu@(RAdj6PIUB9zs7GmD;&-t2P^0G~^e^J@!=H?+S1m#bAW_{+s;lyrl>SZe%m12+$ zvAsByL)S6C{g^#bb!GgyO#gZ3{p;EA2fyvultVlbxN>?H-lP6anZ6&U?7Sxh^L4cq z^)9ZD@*Rn=uVVAq*4j?{>8zkV=*JR$_-(&`mqXXqYg56Al6o>VdU0ub{mZ}dD(K7tOVsCQ7N4HU|Rsy?-L+5fR zH$92Ez~}sV@YdT|06SBVeI@CG<6M{HUZ#_n`c|LTc=!9g#sO;sbsoy&Y&)K=oWtUD zy)NYoKgYHso(_B=6T1w1wnyA1@F?)A&NEvr?ph->T7psbh>_>&6nokU_K38P%J$t4 zOcs9aKQo6@)L@2^%r|LE$)X z2EEV^{vdJk)=djOOcxxtduWv}%X@hu2mLD`a_L>57- z!7XEl$s0J|nR-Y6)LmnHmyJuYve00IE8^>a6>CQ4Pw^nJ<tk}#Oqk_0b_X9H^tww4S&rm3eT}U7rc+d z5M@)0Bls%Bw)Eo4#uJ}^b+UeQM?-s;qTBqO@jlpB*!g6Kvf|CZO zXf-=v)%|s9#ePbfo<%S<8{hxk6r3?OjJuo#WtaJA(s`Cd{V*=JiwZ(RXU0HL_%WCo z7&A6T;mF{}MR6drr9g=|Y5R68a=$F2E?i;8i|p^jveQ^uo#)x&uoMnPh@Be5jvZ^k zO#<)4b`LszY@Iyrsd@Wz1)(qb+8r8mk-$Bxc$FdgxcZR62BjNEJ21w*am%UR^4-hq z=)P*x?V>r)4l@0IZB>4{ePPSsXrUaXcmQ;;7+yP_-6Oj^Ka1FmVL*blx#%q(+pT~z?ko?jDRxwhl=RX1D36`!YhL+8 zNtj}>TWOvO6-(_IeZn6X8n3JgXfkV_s;W)!hY2m3MKFxk0Uk>U>|^Y%H@r{pAFD%i zn~tsRm^){K6;yo_zmGs_wc&FUu01=C=5d*SJ>3~Gr&5(oYBi$XSl2(3g4)Yzuj3GZ zu+iHH3uGRJD`n>+FLC(8K1pcS6^cnQt$`+pD6`bF>`G@mI&5U#5bh>j$5wH zT-5P;t69fBwR&d{p7Xe#ZO(4<6dXXOa>}OmqD)9bRG1(b6nG8XA~aL&O~=P~D3ifH zCummATZ%vEd3Q8Q6PMKuJ=W}byh!yHA1jB^HHS@8zcH7b!CU0FQ%r7X7W&y@dMnJp zaVlVGnQJypIlIf&c-7uz;N8KI!l(oG93O-0bRSeOI0D?r17#4F+Z}ni&2*Yf73VYg zW_ZQtYQ4{u>Wyc%-+pHH`49330}-bkJuZW1f1a}1Xlxgg!Gs0mI_X~vS{t(cnw#0C zZDL53dc*5Jv~P#8QRRkDaZjadJH`PzR~<=lFwxK<#*o;mg805u!iGSa(y^e7HRx-Nxxg<@sR!sLva--l%~d z?D|oCp!JBnDL(JDAviPR9Gvi#p0A$O^CWT4+7tKsu)v?agEU3eX7oSl!JeZp1XSlu zyXjQjwBo>iia!`+&K*rxtQYc!A&#Pn;asi5*Nl`Sg;;jKT+rY02k3L{@eHFIc_MBa zqyMt5v5s{WNy5XB%yf@vohKEriFdyFvte3q5I-RqR@dwS>S$mUmNPVn! z${*1e-PTVDwn4YRP(;KD4RiP-)&S!mEh=>%wIFl`L*3(-5;&}eaV+!iQ{MDm*!w&OECL2&fn$? zuC*OqdKP-l<=Fhpevb28nD#*St~o+S@OVQPm{^bF>*`=no2gFLIhL0DRW9YUt<^on zx+vQ&fY0he6!_y1$@ARqal8+wbq8CPu1$(?uiwQVWZwc_mFf>Cn5w-uY~b74F@KO> zR>VhhJQ{`_C(*0*O#WrB=HY^%FN4@bwFd<+xO!PvHWZz8OhL^5zvDmMkH;jrW^ZT~ zaqG#qIKwc`XbO{A_ZMwe!17!5S=Atm{|Sa+h!2DduC^@Er0U@NC(4iGwUN zjO+rkVg9a!&9EBn;1eTkhV<@OY7bu1cka(k?arLsU6-QqN^siPEDph^ey{;U1g|}0 z7DiVTT~sifEf^)}0E_rBWWdt9h-NVErQ z2QpXAq_=OM65DO;(ykSeI?!Koj_Ii&ld{A+IfEom@4r^yfa zrq5?o=9CQ9X2$!&@PtpxL%KQ+R*Bc110N&_Z`1E6C)k>O*?GrS9ls~|sG4t0zoI<9 zDIGJfESX|#Jj{4d4*$R-)CcGb`}sBePClSdna?+jqvbVm|@0)kucl@uP|5uLxy6XOvAO5dB{*-F{uP^$)_V}-> z?oavQZ)y+DO8&gg+%Dh5tuT9W2=h6AGiUrs*$?3>iC!>8WzN@n90}le*&SRst=6OY z5&ELTX|#%FSGsvlMy=9vtlw*a&_?~T+V0UBCP^Mg@!_3h(1*M9;jbtBDa}RvJv8?a zhr*Ag!ROllB-zs?gXE!5s&~iJQAf6?YX->%X_r~mp0w-SEV_ikY=jlc&W-NqBXei3 z)sN1Tv&D6lUkvII^uamva^CXm2tS%nCDvB(d!5fHgS4f^Uo&Ol+z|=1tC{1bxjSE% zXA2AiKYr=QS;7dxb=JZF@q1YXR;h6Y?;2`iv48ErKb`^VlqtH5CWBz``#1K=v z{B33bv+x`FMjM||T|3Y| zSJDq8hsXmMrsBrQ@1188_YpnLEVyhIoXYndufVet_DNow(i^3{6!R!0oK5zlIJ`I7 zro$e~Zo77~B)c*91jznia~OMxsTYlHG`-3p6T43N0bhf!iLDWw*=TNxClzWvGhTxj zQ{)$YLZgYQxP8<+ZNg_QuQSeEm*e<;Vd1Y^?0fE*HoTz^MjL_84X)85{kNq4`?0j; zN_pm~KqM>wn?+-XeGoj~^z$ONXX@7Vu+0gtg#Tq(POlctTSX@`yR9a-jJUP)hR=rt zPn7N@IQy*Og3}}dvsq#7tr;tpcoK$(+c`XL+*SJB(lGu^(qQxFUFIa(`LO6GiC2lf zVW`e9#&%iFaewf9uySjh-L+=u_{|C@H(WW6;|HTvHs%7HJ2iNO;eT!YM?XiF=ummOj(py9Vdy+^vc z>yWcf6-L$JF6ge&cihCM;zyzTczvxmo2NY(Y@6dr>s;u^uop)I_3+b6x8Q-nIcnic za4ywP*R0~_Nz;0}2ujBbKcWxu2Mw;~xADH!*kNlY|L{@eQz3sBl_k3_KUcOxoWIcmU@pouAo_BOI5{v3bQOv*fyXTP1Ucv{xaH>FTi> z$5sF78z}Zu4Q|C+Fjg?IFY+S-HOa-Gps>~ z0yK6bqm z(c#&2=~u!Acb@b%P25Da4Y*?eF3geY86W5v75Cr_f@g} zDgK~hY~eqgJhz+ZKM*q*4jKJw)QKhT_2?;s3MAGNuSGcIX*WJ2c29iXoAfrDv;|(K z*qax|isIOzxoKmSCVrpvIN^dR4k`PQ7wVAR#BDXC{o(`p1q`QzW@|?G8jBy}4>C_> z_GRrSh~0-ji{^UPvQ9PQPHy{B#CfJDM^k#wwaTh3Ow(QG+Qdnv@0aK+@g6blRR7oB zG!&{Fv*2;V*QI@kfOSsLAEn<-z;D$*#kZXd#rJk5LB*4`KgS;o#EC;4Vm$`a$A_fD zTH!S_yH+s}>*cHTJ`^0QfvX#eM0a&Kcex?hEIc}5TCNrIQJSw{gw~{=8!Y$LVFT<0 zKXwbvQR(lRc@19yZ_e5dZx#G@nw+=W)g)MSO#t!x;;NGNDYVPo%y}2E5a54%{qrmfdfho_j0V z%Lgub$EqGVv5Lq2;bWZB{ldPnMx}*v)R2?NJR~ZvKv~ zondhhbm$fa+sAnoIM8+$b;0$7M+vLtpZ}k~UH!iOfAd1u;=#E#=orqW++DHf>$A2w z<8tr#piWp(qrXSi!OFbcz~BLth3CzAbnn--hwWa2{7v@x*B$)eOgX(_y;wP~>MP?1 zslDMg^W|qP+XlYeAMxW=oT{OGkJZ-lvHv?iasI%4Hj6brG7sh|)$`-T6M3afJiwh; z`j%(htWx1^I|_TMEBwR%`|Gg0ucxO@V6J<71DxAsmiLtBs~-QGCiw9i+91vKgava! znRSHWoA%MzpuIE1w;?Y2Iu6#ylDr{<>Qh~D2^AaiL4jJwB=x)<6s%sqzMsrvRT_`P zvX{HOj;nCw(y4E`SAa(}xQ(bixV9=!<rQ!i3qNv`joaOTNBhNB+ql`$o=jJSZ~B=x92m}Uzb~CSHO>-W`F~@zKgt3K zc)t*CzFqB1Z`#YN-_f5mZ&7COXZqL63XGEZzV_)sM_e8y$Nc|0{)_A~r3W##$p~Y^ z_h&G#^`FY3JZ}~c@lXA+tq+v%uX3P*173?C(Y@5I2S&2(Y5x1v`2M%+;LQo)2j6SV zPwtoIpuFOdC}x-94Ql*~+akiT=BGf)wXSDgD;}2F1Mtk}j`Rn9u37r{&r9U_JOA8; z_1D;gl}~e1GiEj4=`gI8KbZRow^R;PIsOjrZCkIQTJG(PL>$@N61HFIu zHGQsqruvh~pY!EoUXKPHvp%Te368Q|6W1bS+MEdqd_uO@x-^usnrG^x#ADfTcjsj>0SUjq zyH!{S7_9JC;#HdbTRFuYT--);GzrP0jRvpcal&^*B#ZJ;wsn0wpDhQqWcaz7Gj`@J z&vk{9Vvn;*NOb1MiDZLvh`0Z-6mm3KnsqlcjGwTJp3$JK3KJLJEL*9tslK~9r-43e z?dUGtyHqCSNT!D2Cxjgpo1r+8_=t+Dm-Sl1$*f*Gt|Fy!S@0{vR2 z0Qt_nb^1l8mdz|vO?W*I`)ILxR`0{>JR8TB$ayam z-7YJqWjuyGyCu!Im$!4yZaSOkT`PwL@aQ^J-K08e=1SkUg)KB zj7tv)nLck8aqVmo|GGca%jK;phcd&0T}?=OF2Ix?l{0s<&=| z(;OBAojN{Nl{j%}n!DEiR6IBn;+ST@o~;}q{XS9zUBo{_1TJlhe$V*U916E+g+IV* z)_@yW%x~ts*Rb9p?n-Ies&*QqsdS;z8?eDD{IsR;8jN$op}`L}VE)8ceAO11<0?q4 zcAwcoWW9{_-EPSl#?PhYY{gg#b>@)AK2;9w!&|Wq`dU@y8*m)>@{43=X&r|Bes#y( z8tY7M*#_E{9e70J$@Ly{;*}lxj&e5cgGGS}!nFfqb6+L#y~K$cJT~xEy#;a1iqpqB znIB(;M_u+_XVc0Z>XqASTydZ0(x>=?tRIm>8n#tO^G&S$?8IX6LLd$Yvr~}Tk9AacHH(aHv{b8a3AEE^wbzq!;ttp13x9B4#g`q_L0pOmR3pP2Z!=dNH?Nztw z)Cf)(WBxK9-eS>JvoqFw_$mG%nuzR`(2dq!qO+$>sgw+9x8pPHQz5o=i#*Cb}rc|ytO;+!n&t;if|0Qk@Ql*r{GG8 z7(E}4ol7SRr~YI7!N%zLq(4a`B2HPxS_gLX(T0N#=Zio9%?tQW(EA%>akYgr1K@Ti z%v<<8fLqc=^s!;4ICTcG%DfXf<(GG98ytEjt{?iaFT2CscUUu5i&MOx+)|Ae`}~*H zFMNtW$Xr>wt>mY}{)g-G%sB6NHgoq_KR%C#R>wPMI;YyO$GQ<7p~cC%BHXn$C8pq* zvf&?88}3{4Q;_8#bI31`ol+8va|{;BSOJ^%R^XhE$urk#i;JFvDFWxNOWhSe#verI zCA&LLYp2ZO=Jun^yTr8&b>!=|n2qMcYqsaE=LG%E7mmyRi`aufkU`=jUx+0dU&3`H zkML^I8NGLys|;5pzUGb&Z>vj&7CGjlh5%76Y}%H zmcdc;upCdOvwC+jd{xTBofA)Hi4_qbW^K$quUaex3sX8*foP(bsiS>*$_(@8 z+_8NxMk2S|VS9?~bnerhgS80_mwTrWjcp^Te0VwCP$0C4+y1`fYv1m$`TfP@+mqOr z1jLO?gmwL(BVJ#fGXfuD3z6}o5I*)A)!9IJ8LcsV*puto%avED{mkk`YKzb=JM1?C z#es9ac&+Z;z5q6akEvo{^jys%ac8LwP&(+yrCf>8f7(6Gy2u@5+u+Hh zL_g6N18v?M!)^A{Hr5L+M7_XvzzmcQul6L1e-9hC5P>0eR-=uA zI)JKAh-qR%8#H}6Zm?VZv{;k93H86dxSP}wp`*ufw0gAIOr z)m*C|ndF_Pby_~)Q(eTlBXu7raHpQ`C>HCr!D>0{n6tSsRq4omt{f`DAIkF6mdoG5 z`C}H+nE}Xt;!p;kGVQT=6^`ZG`p`(uLPR307s$WCsMxl^EzG4i4_qu0z2}!9gCT7& zS}x8Up!-*y z8~R1Ub?IB+oDbtO;H_HaQvV=w+_Ih1)n+u!Dk_xzP8MtWeaYtx_9hyE}w zzKTZOUtj43>&B&9PolXiT)lNSJ`Kd}^vls}jv7Vy3)<@#tl$f0_~f~ktG~66-eW`0 zd^VV36H)T#rGI=jf21IriA#o0W^9}B&G_i*kJ{Rkyisd$zt5Rxu~(VC^i_@z_A9l= zpP0~0lz*#PH&|2sBy2%TxQ{RViFg0prT<2OX^gW+4mJMxz*K>);^1%Azfm?boBF!DMHl zgBK(2)r{Xna>GFG-kBMVHGal^$}AvXIQQ3jXzuZW|IC`iblI`&|Rno7rFa7ziKZcR}R^ok$>>zXs3+t(-GGHX36T4%jtyz;|3?CyI;b zvL}=ewmvuh{ql`3zGKJ1T{vSK8#O#D)mY7Yw{&k=Mk|QQ;-CB`bng!(oDn$BXdkLG zzkjw#?}w%7tuep3=-mmMb?4%Jfj#Zz`;L!$EbpzGIj}P9Cb2Ab;RcT;m|97`-kxo@ z;0|KLk{zp&_H{h_-809HRul|c`$cU^Osu#M6~sRefDHw*K_?$*ojZY>TZCIAJ823m z2^$@F1@YFvrr_C$e`+{8p)Puz#3VdZHsa8DCgx(@Xko62&*s6obHPzO938pOWBYdOlvKhZF zEj|NxBPJK$QH)FBaN_5ICH9GJ)aV>q{UGHV!MCF<@I0+p%Im;|^X5fjVwGqPYz_D| zd@OO?tl_v8(eS#^7XBXiW95iMg81o_hk-rV zEH+MIX(@4j(KPN0^9NUJwjW-s4=rmnx%KlLlALAJ9wmBCW0vT&?kfx1FkE4Hs&{ZT zt{2~>5mT$yubOw8mBe99fH2})t|&|E?v73S)3-j>uVj-YC2N&kK-e~YnFlyC94XVU zrAG^1yiVO7l?dEYai1(=M{Uyk=Q>`PB#ZcxJcr}!sJ2U=E|+CBo9?$6V+*u{(So#H zxP$RRK0J%e??EuCA{;`i(Wum4%vYq3@dxo?i-bDk6a5MQ1!?JeF~(&P%v*8FH2=hE z`EaJRYn}0BC+-#a4Llogju*K4V}iX`xRWe#6FN&WR!G3_l&@@VhK~&Vqwws~=_XT! zV`2BGT)1XuoysopX^4D`KS)0fQZJ{p;)R+RnpzVGxLc)M!fInJ_~*y!@e~3dKu2vs z`Ci1*T`&-Q8WT6YJE(r`j#nr$TAje~NsSwJd9p zADXq~1N=euCE;qs*_nCOtPim!ROV7YK1*NH`!HB%wI0pv&{NzJ*UqNx0*?;ZjoPH! zf(xQJiaaakop?m>rNk#RxNbK$d2~h^e}T=SN?VpDgGA*8ZpVDRZ>|W>sOy=zOI_?& z64>vmS)q@hf9&2BYp^v=oSaN(*x1c?tmA#`?3t2lwZN20(?sxCy}IZ^2`k9OZFha4 zU252UbL>yh6&1DvH_EjqaHqn@sJ9oVej6)C!gH-Q~beH?>Qkx zT-aZWQw`RJQ1Nhdo8#4pdYej*xlu_*4(-qt4bFM1TIk-gE%HPx!Hvq+oqE=y<8udF zQ}+cjue zJG`!Tew4KHlPiBi4}h7-UN(t?OrO7HbNJ~Ezy?jMM1H&JGS=tPEE^;&mX~qxX_w#` z!VmUZjF%m_(4~VvfOJUN6C6yuSYjSV)CWGrAKczoaK`)KUq$IFG{ilJ*xN4-Pq8+> zdvI=xt44#6t#+Ay1Rly9h272T!qo(u^>Vd7JsXrYFOv%8IsWjj!dCE1%?zzx9?6(`aj;fJM|kc=Da)!j2) zYi`K>8#TOI}9P?ddwfL zcF>U~3j~6F-N}`lefj7{3YBP>>J60-ZXmI{w#?-MPM{cBMe5^O_vsR%Fc0p*7S0+5 z>_xM;n@(5DNM8H|AD8rfXLS6Br@Q%Q{>c%tGn%u0Ea?SF~m%v|iST(JQ0wgrLLW^sVFUWt-+Eh}tEH4Tt}$ z^S2Cx-+d(hpbBC3IgZvNqo2nM?Wg^Cms)R2$6FaUfFZF5Eg#EaS=nHahEMcZ50<6! zDIEdpn>4(pY}0OT0`zft-8^)kt;Pbp&S*ab@zv%`%R`WkhzUBknGK$%iC;=4&W(=? z`TOXt4;z_&tF@NxHHV5(5j*^6g=V-V?`}F)SpSu0?U^tL|d%!2kUC=~6 zfz7);>MH5?Il7p-vV5CWgpctDRY$t8^3QAedOOBOqITjeQBK~y0qY$w#|CW1-~#;L z)eFBIc09Ps?aH-gb=38_UHUpuoUjl?SZF5(Gy7Vjg~T~A7=P5**ZSWXP5{F}b;T5<2Z z(eAK(F+YfNr1k})=YLamtTkVs>BX<5pV-a&O7G8jf6A81-&ncfb%EC^ePKJ>#rW9V zJ#Xjmu2_XtzPO*)@H=TZaqcEC&sRM(t>F$1mG@-O(M>de$<82q!w1tFk9uQ;U*#wb zZ`OAi$oKbamE((dclFXvI&e(eMQ3OA^9ZV3gIDbIhG|M$gz`Y*ieUqvwKt3ANaZqQr2MrBqePHM@_1#-j8y{;~O&+8@`EQ42n zma3>6e%>_~nsS)_{J!5Oo|&Ki;jDbKYa`>k@WuyK0KI$|;ve#YzXa%)dv z?1JQruLm`D@deiF*CsPz-$;9l)5Va`Jjl=pI*b+hKGQy*PH(*Y`?|{iyA0nS{*xD! zA!uyDQ?sT8l{bICl*Vi-|8Jea1A36e%U8RrKQSNbqYtH@_58l1^8fcqpQ+t`exJ|$ zxmxp%d@SfPtUp0VKd$2se7u@@)t{bZvxGAU&tPl5z;_ekgM2Q1+v)1 zy6Yd_Q>oNH{fGF;^c!(fO+QmP#E&qsUTBjycCLQ-y_WxDef^iE3aSsh+jlE%Lk&Yd zQ!@F|d*#p1BY)Qm`UX5O)w7B9rtg1UWXsDyTRv$C&tCG*-{{hz= z=aOIPrc+~nXDz2s^T1^^kf5I7jI%$%kCFSE>;`4~S3h?soY9Ri

9zL@=m*GkFg> ziW!6t0QnA@{h*4kGU?gegSkRQ`%uc4WyjaJPH;A^Jupz)%MZ#mEK_n^;V z(yvVY>BX<6uX7aj%DVA$|8B~_navq;FZCO>3+sZTx(o!G=U}LR&6Y=rN~#y1)$Cd{ zY{yQ8YP61A%(B?2E{n%!dlb+)K;@%<(FXCL^F97(r;K!vK9k^}D4)!qsx#(y)>y@M z_xqLd9Eon7pzIqnG;)rMdl>sV94brrP0MKed1&={Yr~JiQ~`$~<~-cS=9yi!zzANw zc&8HFn(XQx>lvbuz_Je!baaj?=K{6D{u#F)SY`;x}@7hD3K^kQ$gthnR{_N=c`YC zpe)QiU;SO?f3eT4oZ9Y{3Ru=|3oF&@A|#B(?=V`;jUgT0hC z2r0G{aTWLAeob__Qs{?T+2!oEB%~eOiO$ePBy9n=50r>S!=J)@gz_uznlYc@-0#NAmJz;u_7UApIe;kD{V@brXVHQTpUKZngL z>P)8fQzkM&o6u`H7|Qlk$2`%ctn=4OX&f}3hOx> z$WfyX)--rom#w#+Y<6jTU9VIcm2!JIgNSs^G)vn@GXwOo_PRDMzj7y!56-J}6fVMM zKC3YH&~2l=8%r9RrWfaIH=4oUf8Pq;nGed_ z<_ZNj6Cdd#?ZH|Ul^+Y-MDgvw1FH@i%_Y~&mzE7*xB>@q*Esb$_jRpsU&qnGJHTgF zoW22V$Sd$2I9+8AyY<7aHQ^c&_aw*MfFr4DdtV?_E4;H(c#(qFG;W}sn8SV*UZL|! zIW9BT#3@@~tDlhnF!K|>%dbhoeT8E1_m)A2t<@v6mtJRoI*u-yNpNizn0xU0p;81A zy=Ce7JhO()8H0V|AzJa#^)%C33#T&+_c|ZLR_NNf$|fD!HqSTknDCg`iEA(p@DFK^ zq5G;>ir`MXCWf=hu}QovFW$cNcG@gVlOY`V!$)C0`9ZmIcblEZDwO&Zf3QDI(%<*2 zd&MIAhwkywcveq^P<(f^FX?7fI^bb+UoOu}_~PbDCD>flH{}51hmQ&lf&mm`)CDRyYoe9pfywYuW4eIBT!x_r9A$k2g1&4C8RXn<2wh9~x57jXhE;2WgZ7qlyT_CsV(Mak>$qQg$Y1JWmv(`%k$Q9H z^TZczNaHoKs|SbEEFA7s8@>O^eqjqgcfdNqHXmzrkili)GNU29Xjvo0;;WQ8M3356 zU2&pc=?EIgF%v6N{m^@XvrYIsew#O%b-4l{Cs6$qe{g<}4n5aqY{JuCNmIVqKp!KG z4__x4oLY);xoQR{lf*g*w(sNr#Cat@;EKeqw7@;BL8mfYu}0Kyk98M&a0%al7uxNN zJI&FW3)Tk*h<&hs5|X%>7|y1n1^b!Ir}%@RLOEC;1)gv?=|bo^D;6?)JN8wbRyCVi z&Xb#UD!FY}dsq@D>B|%C>~*PsCm!u%o$Gq;!+EZ1XpQFhdly9Kd^%?=@0h99317Iq zdr*Nh_I%dxO2vbll#y%Ns0{Y$-gB1NjX%gA^nKyjH&sXV-MA4Q^v0*?D1NIDXEaxf z*1bhz_SAkj7k*xK7Bt5P&fmt?5|C|En@!G>;3W_pZ}m%bIKCG~*Jqm;WcnL{dD#;Z z^E1C{?q;LwZNz)dA74Cr=hrvzniEaF%%!wV0K>x{%m3WpHu&)_GRfN}yu)wdbl=n_ z@LPyS_8?AkCa@gjU!!9CSS$w~j~sk%*?6Ui?2VJx)uW@emNU5@LxD3o<;B(|e^hfn z*@1rD9{1C>E1TOpy|V3*=wBD~M@0*( zEg2u#XO3FXJ>K`xb$?v@eY-#7+1Pb-;jjBaf&%{^NWb!LNd+T4yWzF=^77-mxEJPP z#Z%)SJ^~&LV|}L<-L+-M;GFi&t^BrKeyRYnlNehG^`z$?`Pa)YxJKFe#-Oz9y)m6_ zdi|>Q6QkxnFznaIaRV2q940<8v584j9A(UPe_s3DCf|Sf;EQ+Lb4k-C+-ul~6~wuF z=Ue&uPJB!px#GMybr0ceG**~a?%v}dd=Q@p&gkQ$euv6p?z{ef-s4C6e6SRMxYYZ; zZ^*{vU2o`5?=q=;wR3PdFK)eEpKt}3T<-Qmy0C1oTC6qmlzu19+xvRvCVx^}n0x$_ zKBW#v&IK%yuP=P_?_O`x=T~8usZP&l;a5|33%GkJlRefVb{=mdY=id!zSEwi*8#=fvB#*D2F}d~cjYXIM#X@vgHE zWcT|LKl*jlxn;#337{m5F{r;)0^?gsh z_>=n9AMWv=zri@jKdo`t@rcNIec4OStsk`O{O5W4&wpXC0k98EbCd%Ktj$+BWY7I; zB)@;W+k-rpgQgtbRXk2U|4SwMlj&C`>%>1kzSdh-`M*@s9)P_cSelRFgowp?HQJAV zrDFKLXJ&l+<0F6u%8i)RTlK5IPLdy(Nx%>4NlrQAcAm$Vcm0@6=QmXbeM)$^uoL|w zwo4|`@;v9rYxTL`l#DKh-?#I+rT!c8kRJ;^mOf^P-xRUrsPDvXT4=|zf&OHa?nGD7 z+Niv4o+IAqa!O4b!C~u?tc+)g!ER9oYlia`eZIUXn-$@3G0R(5tLx@GnAmNNuch%# zq}e$We-g)G&BZ4qzcl{4g+tppEbO4Ti{MVe4Z7qH8w%XbwFM5vYnxbQUPx^)^5eD; z5;pK}MkTZeJN8TW(bP<>E8so>K(FUQ33GT0^<6t=AFoHJdD_DG4>n7(Ds z!R!j|TB3PqzV`C#>b^FB{KPUNveEFVy zrYITlfuGqv{7qr1a5oL!KSl?VdCA1PJ2ywyPPKQL?WC8uc{EpvLOD6Q?zY(e6X3M8 zN37s>yi)!c=)vU-d#ihD_v~LM_fWn*Dj&A@3SP;~X=>M1EKl80OyeW>Ze{i7 z&2eEIFAyr@x@sTOoue?VVkyu(wuA}XyRRJa`gmNHheR>F%FJf7BiCa*S+&|IBi=haE=x}YcM8&^0@e&nR6rTkX|IVrnCbP;0Bb?C+ zU0-BNCpO%xhBn;h^h^isT4NIFWzs=TPT^gmadwI4xjs*^%@YrAg8yzB{$M{GXX!nIDNw05+Myew%NDi~`9b1$8YSX`US`G9SnHSFFeQvwftJS+&k@k4hyt|kq zo}n}NVE2(Zr`%P)nG3gjx+jdLr*u zeJ~kW=(0Y%wcVQ3Z?)CDx&qUfCg5W(oA82P;tAH)#O1S%wxHoZ!p~96J21f4rb^s0 z=>J5VvuN1iW5O}{rwnFqYG-(K^3tmc9w(C39Mp#?dq znKpTq9K)Bg;dy#%{Hcp@3SYh5BDF&!+*zk>jVtc6nGF(!%eEDKGLIDhZT_HxkLGtd zOf#@Y%MfQE6R1=IclH`+WNYs+PCr(uLZ+W@EK>JDbL57(Posaim3NCKb)24qJ*OXb zxDGDJ`x0C_>4%nPn^u1hp15jWqPt_ZmGWLOOhC;J8l=KQu^qJ-PjJ=@<{sWJ?)_@# zJY&yLto_^kLA8gH+zS1(oAy!6Rm6i!bY}Fa5o;9J4534?M0?LlH4;@{w7JdWA1W6`u_JW85|Ov^v{+=Tr*k zi>IfZ-nS~}VeN+0U3}?nYG|g#nWLn4DsJWjoO#n3Hd35T>Ve9z7k#eqKUsUnSC{>- zVt1+?;6Q^T!1Yd#_nA^KTRhGu(;jQ5RrpW&gJ0N#-!`Uf^fC7A*?pqh9k0@l?AXLJ zey&3q_PK)r0Zz4Vzbwc3L@yANzVvxj3*wi-_(jth9}MQ7shg{@CwUBS>onTPE$mkK zYA z-(!aTuFgy)*nZRGXwa$#12y8LcFsG?3N8DmcB%#;@=^?6-B@8;Qny!w$E< zTE|8xU)qHVhfyrmcg~UgF*3OxQ9aIc9>k=}_en{uS%t&1wMkWW)9Y#!G@pw@$UkYc z{J)aPum2^=F8iL^#H@w>`HX9rGb`sQuFdrep>0O~#Cnf_!ZeT@v?t>vdIZ>Z-5gSH z>&g0N=hmZJzla`Ze^<%|>`*9z@m&y;g^ML%D0l__c!BWtk>#u^6@G&r#13UJ&W(y0zQXu4R~wD zUd^_zo1QhJm0F{a>17Jn{?W!SVDq{G95CKIIE0(~V-tRnZ~yFM_)zjyQoKevy9cD< zr%vVHUmyO9L_2)hX84lEyUL}9`r6j_bv1Qn&Y47`Q>jk_vmdwYkK28p~6m_FHQ!mUs14gSp^&iF` zRQ{E3f1GfwucXWRzTJ$I#8>_3tR_%661%LKE4!Dka*#*iu?E@UBO0h3Pwu*K@#BGV z_H4`V>ct%J!uQH@pQiki8yem0qtxyi(@Vd-O&=ytra9Ck|2p|&fbYELU430+|8L{Q zzplBS-xivaRezO;t1+QEhIe-00Y=-UP54B-RQz|q`2*2oX%y=h^Hb!tPhL(~)P^&$ z9D39q{Brp+T(zLXIKiF~3M3R)hq_YPR0iGm14(6kx7UyI3TGu#eiDB3g*EE~>2o5< zJBZ_}e?9b?%h2)V#CzHqkXpgl8d(<~k$dsW&3An&yYKP(e*Qoa%q9NG_0$2+s|8#@ z<$}jM{dg@dTjg{rIQ{%Szg?DBnJW7#wZ{ZJt%{!$|CE>)42Dh}eUH_Da?f8X&X4Pb z^22X?5*+4ljPf7K@uOYNR;ivUM(lB;-=VO??=#~j=~?}`&%vb}lpRWC=@0q*(e8iv z{F86E$D(=>u!CN0+4_g=xI^^One0|Rhz@PK~`&yr@ z|9^I^zc0)G?Y*(%=>oESmT{}hdWdArXg6P`CyWYL;_kQ-j@=O04Opp8f2~Oa9eKq?o zs?^bzQEQd{6YtO10A&k{_j+4^~?8VH`3?9z>wEq z4^AQs^Z(*`nOP7X6y;QT{XMGoI`6jOw|@eEkov_Arg8ADbM5C$y{Ycr55ohdyp%;_ z)#r2uf+og;DXZycmsS0MiJkRSzf)4V|9t)+b0ZnktxW!?LcP^PF=@#=`)oLQ{#bU{ z_V5@GlFf;(|UuP00#9bqHVBNYEgn3H^$ZTx);U^&?03fL~( zE3r|q?9Jo9lRroqsC&O1u<{3jTY%2OF@hanc75)i;Dg`^)wUnA4-HEXoWNa!<%Gb^ zWS3uqZGg8Ku3C{jyL42&n$gtyP5eP@_i6(QL4QVvCttdD=eN`H?!uE3relLSq1Q*= zg^iG3>_ducC)~yo zJNXfAMjrGCrChI1-%GQFE^r@kDaBh{$FONNw!|9*ua$Oci1)vV zKgjrCJVv{*TN>Lk{ae^9d4u1GjZS45fF%!~ojY;hPUlNo8lmF`d@!^&;exruT=Vrd z`To3fYRyW|PVabo&e7?$^S_Zl7)ci+#MdekmyCWb>_GfMVP(>7HM*~{`ZgWhC;FL| zwLdOLiD34QmaAu3`KO7u*mwBe@cOI(o3xz;7JCL9EzA=1yZD2Q>Fy;;yh&-HN`E#6 zhbxAV8aSK!3pnPZ6G~ zo?P3P>S3AM|3>~`us4}8e)}o|Sf{wN#Jn+(#}70Zm7`wl(3k-Hh-C;prGgS5w_?!rZZPwTG132lOl%^BBd za=Lqv96K28MbhWYf^mW7;~KwpbnQ6zac(UQp6m2z(M&nDQv*9>AUuCW7TzX?#uS{F zZg~E?_=DP5ouBxn8@#_*Vq9jPoi(;__}8x2nKY84&iOV2vz%QqAX2|@ZAx$)Utqi9 zc4Do1h0(qVbMEMtUat`6sQHyLW5<7Ve;0oc4rIyGI9RQjBa8Ty6g%}(V2Xc6ma&dH z-?V#rRySui-U#&q#Dhe`Qn-P4;hZ7;!Sq1dh~ftFn7}1Wd7BjW_6M3};lbzlgIcFa z%pPBf1&_5)wmACKp%ulbHf}R#l5gS|PlI)07{mANxNavFPw(p(Z%Si$L(&)uJcw~c z9+{sv#A&Iai5l*s_d3zo;q~5Id+giqtRK>gj88tuA5_=z%VExNrd;f^6Mhtg`XVDgW zf&)o+RNws&e~`0~@Bd&nf|P~*E&M7q(zew*2{UDX2UnK)3V$8v63td3+Y=i9v=LbP zwGi1oEO0X7AifA7w*jN|Hq2Y-ajqZzXa26n(xql zzs?`jxX{_c?8UV&r)@anMVzv=YOc=Yoq0!bn8*0CRcpjC%?#@VPwdw__f;{yX}ZJd z^qvE2mNqbWv*xCp>Y4m0X5XSbxK?gzY}%v_E=YT1HJo%NwQkq?|}#o7etjEy**o-)-)xttBt7Kc}^)bprk%}bu) z)3f*~9_OnyI(7OF`CdG;=l&yWgR7UScYBce9Dh*fG7Kz=C86`T>0Z)G`tEOu$TpI7 zCeher79D2$PPZT5QOBc(%DTdJFSM;2%td&Z6?*P-gX6U{JSED*PB0g7v$?}{9_~3! zYzv`HYB*cm^Vs3=3k^g#->O4=f_w;X*az<|+B;{IM^+%cai`?Mt>83=~%VT{ISW=5L;2sGST%oZrD8biQ~GvJt9(X>U9c<05;RcZ^8(d*=S_DC&T zwa)UsNgw$P(CxxFA23$T^k(ok{q9fa4{F|3dSekhE6yt!5^4|M`Pv&%=MhKDH-4zV>ih&rKEmHU2aEPTG`mSk6w5 zuX32r{u*UaE=_x|W@g}aO%HnLHESs4_$#%?S2@%k?{fHOgTGcee$@TTYVY;<*+zfC zeg%(WieuCCLcj7cy1!b#(%Bho&hzRgJq%8#&;DxVFdU&k=deE2H69`xNo#R~d zCjLa?^cnv_FlTY_L-?cgUOpRm^t~k@CP~X;>)LcX4(cn>CFFP*~`wqJin2t-V39qroRutnq*t@~9+%>=Z zZTvynL4Lx7_;A{R#qD-5`i|nXcztwTV`wU&Lnw}syQl7B*hFn%f#M^~c^F<8pI2Qw z*n8Ql$7{WYE4;^ly)Ql0-@_l2y#b$IG;=eT%s2-Yg?1$vrRzCf_6+6+&L)0{G*PAb zN_;POWrLN#NnOXH(C(yDNbEjvTX=(C+(YUhM9fQ@>-;YMpa?VYW#*WrRJT4ZNq((biR&!GEQHQ}i=h57z2 z{$PywiQp9>v|QD9;4Ff1f+3J!IGw9KZDIE}^NuG~*x}{2g1<(5w0oNGz~|KX0bk~~ zp0{fa6Xhko1Dwf>tts@|_=C(zlj#b*S)=_)UW#?sQmi{-Bf2)&Otp4%L+!$#-Mn-l zH_n@_Y=7(YF1^C(3AC(ukcP)v?4m!mhU2w|JJBP)U2wm&Kgb`%Pta(VYkd{hyc`db zcl9Oar?_m`+f-)*rwdKB5S(UjW@1>{O}MD3yfpQQ^)%~G?)V6O*an#&)%Ho${RTw`Ld@OTdAI8p(#>|KKgRBYiDFTCGkq}&6DQQGPQjw0vF$Pd9t9Rv5(b5Da$I=Y1Q>05YLDGIr z7~jGPa1X^BG6QR+pA!!azXQYNw5leit2ZaI zsMYVq8~E~o5W27SV_mb2hUNOu_q@x{$I&(z6iVp!!Mj7yhi(R*R>IDr*jSg*$M}P$ z>S+X14z1gY^^!D_r)J<^;(T7BvGd}(pf6(mN@GHa)O`>1<`;Q>H z3Dr{qEl?8!5gFZG$<~82ge+ZlM%MmpkaQAXtTO zd{D7|Y#Q379zp{-^2`?TDZ#JG@UzjtUs*Bqw)@duIloDsn2S^OYBU%|n{wxC(uU!w zq~YJCyUa)M=)xQ1V)K z^P1KN)^gU3JsRfg{pA@8j;2AYiY_IR3eE9#S6QdDKhfF&29i_U%F@zXZ-fzc@9l|| z3TGDe=xy+{n%LiJoNpH^cT?)}8*sP}@dwSAFm0lC(F@H}hQuA;P2;U|G;&O@Ghe=Y z=w6IduGMcB4vFsmF}jVD!9Ib5pXc@^cj?qpcdu5tXCLh+v5X#eslr_BoZLa|>XwM) zS$RCx#U0;iJnTX7*}2#s;}04;(pNdOmdKYu`*fAh^f{xaA816K{iG2b+n2oSTuy1q zSH>qa)vFgq>Hct(D&`kv?q+<^mh#OF>8#@K3v_O*2ac^PafO|}bi28>#b?B8# z{33r)`E{6U&Adx{_&5Q36nu=$*%h9WldzPZpksJ`hC>`g6NmTwt{h;H^aR`7{||NN zp{_W#Ea_*#d+$BMd+!kv@Bb9@#}e$WbMMT(XTGL5UFxO~+L)DEJ|iPCvzUPq?ulJE zZ`g)k@>RNg)sE4xAL23j=FY}()lU2%#Q zKnkDX4|?s1(y3ldU!lE7vw?GbIkJyFsx^yF^;&hMe&q8l?a0}nAR0lv#w43~aY09s z*|6+%)`97HacJiX!x`&b^3p#CK?DADH!QuxVHIxmCH^25{NRJa)%4)FW|*`8>+!~L zzzak9Q2ub}#b?EOB(giA6M!%1IjB2^V>^*K#w1}v2D8(b>JZrV>$7?Bd;ZA`rhj3A z@3Oyb5@(HrcGWERe6^&n6g#k^f-_hoCjhw(fYPZ4Q)Np$qPU(28qg==o=&RG*!JQ<^4{Oh9~3V5#s+1R&avyH zeDbP?w;k_uy?#B+N%qNDpqK!byIuCS&tk;wGB|X*TzlEUE%*k1@Yi~fDgSc3>Ppv4 z%*pGI?{mN9%kxM+ySaaP2De#b9|QYqPI}j|-u<>;=fCB6m+jZ`zJEye{Br$Y%ThD^I>$XO zdAr8Bl+VHdv#w)(pzr-J_v5GjfBDY$PyMZD{m%Qh^xeOH|1W=IKGhoY=P)0#j1z`9 z=F%EYC+h{AGh3!0E>k~0@Bb;gKK=f!_w&y;{@oMiJxg;T&p+!hUYK>{&m5_7AO0E~ zHS3wa)u z8*Ym>1pXBs-df*!t^|tEeRX6mbkMjpD6_B4do$Fd+T}O+gBlBe za!6xOi(;9k!n#)rdQ)Cgw$%PuZk=aI93gL$gyD ziF3y%T9%Ky=%fqP+wvZ|wVeC4)sKCRKWG|0{nkp!_mEV(C>>MFZQ}O0IR)WU;2#a} zkO<{ZaA`jt{ZoTyU_`91Crxt&A7@J1q#gmUxeld0~ zpWzSw%s;=*Isc-=qA}JN=tN@VeT)Ra4=8Vh-Srr;Q(+0%;1-PE+T_nMZ7ZU@mWh{U zY8jisrFwCvQ-SYjc!Th(64)R;)erLrWrx-X@AU`m=Y8aZt>L|``-Y}#hun{*tQo*2 zv{5OKvpUBQ&Cz~yhZBMTE%+vFCjf3MT~TZwIabX~;3&VYE z&@LKfFr8fMbc@eseny3-Rrwshez1mcA*Q^9;r!eu+ zlP!BB_#H{>(7fB_P|HmPT9w&!)w#CD@nPFtGAZhPIl zD+a;sQ$LCN!JY>j*GD{n!X|*qNh7(#!RC(Eq&?4vNp;M&Mnd&(s$dDdhoEUoZ-~gtm%vrVu88@||`YXL1vV@3< zVw_6MZtl9Ec}%ert?sxUN(5qsO#|!`u5j+q>fKV8urxzcfop$jQzy?Pn5?kcDteaE zPn;~B?1XkXXDq(JpDGTY>jQRyb-I`J=lFwW*%b6|$q!E1P5%J@Pw%%4dr54Iau~af z#KCU(TIW%_TXiP$Sr1IM(g|D3=q)`+NyD`in!0En9-i(%_$GW|0>3v^VdUrvALtk8 zqHns?6MD9)5BO6~cQE=Ge=w(_V~h5AX73;jJ>(lq!`v^enH1BLDVJ&@X{G1MkrKfpJHSXlH1D}=-s<6%M{NCQ=W{FOzQ=OgetK5P7oIUhZ!L5Q_ z>Ww=2Ab*hDZ5YBDTDHh0jk_Tq8q3`C1o@=Vyg;K}>k-D_OgPsH^(rTL^y`i_sfUh_ z%Hz2PYX&QKx<%_@v`)eQ+^%(N7l}WMg_qIM{+`;B*T-huO)Br>0G(ULybbu5^M)_tXK&M~I4QIuh&h<|7_0n@H{hHJu;|$$xyz*=nM4A;-xXt!Q}R$A5Ki9nl#ySUPQwu@@W>Je2Y6GyFl~52=EeuK+vn zrE?G2b69NR0x{^0X>w{Se>B`daXUK=t3FDHwu|23w78v1;d%mX(d2b`NjPb8y~1x4 z#;F{y55w7`jFS1+fWX+7L2m#k5%l6ut3r&u*?olcJe@0+3vTW3!-jSBaZu0|&4TjvI?L-8;@ zr4LRb6Y9k-Y2Ub6sR^G81hzx%gZWWD;5Fu)-};;R2R(gi?J?!ckk5m|_-1rM-;wV_ z`2daoCeAr~QLSZSzD%JsiSM?x`5oKD)uz<7rb&9L~%jAT!P_ z6YQmmzIDleoo5fn-D9oTE4!r}aR&0qqI}yWuit+l|DajB{9=!2Q4|VJj_@?x=g)<3 zL7rLW<5M5q15NaZqVsx%B;w;R*azb=A7G`7!E6SEBQmRhEB~PC;9W1AY8@QTm&%WB zbZ79G?gY#dIF_;*yb~A`=)+DIeXHKyw~O^dW83Ypr)8}I*ABb@J7lkI?D!IYkpA$m zIG0(^PiEw&RK7$?XWA0wqK~Mb@Y=bzn>Cy}`71Kv&E9f!NzD+LsCM})@jlE*&&h*2 zGj@Zb{QmrdZyTtOU-yl>R53)E)IexMw5JWz0&tDfg}a3vS0`8~6hiZRaOEG8fA{9Z zCeC`$r4Fttzv=^F=Fu|wIcttx` z-{|TZuf0C3cQ~x!*AHnuyTqA3i7BPc>HX9T{X=80cl`7DgE6#rr#o+*z*5f3pYWQ- zwYL6K4lta5u|w+~4%RQ&)0(y4*v@7XX=rHRz z&oJ})5BTTRu1sanp1{|9;$o<8c-Q^_8&t%nJ3LV6zwA)o@ap3a>K{JljwK1QSm=9) zhyNQp{;2A}j-wsGKmA&#{bI*A$6d2mq;JNwhx5iuljBkMlD$=qxKB&_t{iNIYoU2A z2fF76`KkTI-Zh*bMsJzhs+=Fj;>GN-R^)B8DgC4G<#S)kKN#@U~m@B zVfL^IKWzi5V0wwZ<+auDizfot1x{K5|2*1>ZUjG^Ft;)}Xk7SS>++)=XgjMw;jv28 zuM0Q)E&iYxFXcz^j)0#%d*Lv=E$Z$4-KoUUq?8YW9C+e@iGS7?h9k@{1$I~gqr&%) z2UE+`jK4lT?sCpGFszJn6@H07NcsI%2xn=C9F-xqNl@vOcM$&v_gv+RliQ8>@kZb6 z1cWd2*?m;evm~D%`X&Z*$P*_o+rYBi0y?mFqp1k)2G;p4{$MPN9pbKZ@gaf32;IT;)DZ;2VXLtsJ6m zr{=0PZyN1It8yDP$Fs#@kw=@LQV#n=F~i^LZ2H%Bl`*SHyQ0l>E{xCi=&>#~ z=kq}2(Fs?CADX@`J6!&b9n{C?_=Aj9_zOKd%vg&)23U?JKgL;bK&>6-%xjfK{|iOPFAG zsrP-1KS&K24w0EN%-&fGbRU}7RpGR({fIka9Ywy>TdI6QS2$k|)fP@C(V1LV^F>2B zoRvegi7k}h9nd$jbEgYN;BNY@@o8c^j$b)y#qTt2%eA|jYZ#lme8%H-{@wh8#2>0z zDF(yz55pf~w^3-N*bC+D)ttP+uelGbnLN_-La|jO-Ro|f7>%t-`veDg&T5JBgLjfU z6yGmg=jswaD03t67j7IqxF~tpqAU7RgE$S=kHwy}dOyY=^mJlOU;k?!_vZEh`?a{= zN{p`l%(S-arT3-k9_sG zEjfw8cHqTJ=zi%TP7lAc<@6K$LC-J7{wVhvuVjNBVroj@?8K?_{>J#!Z%pcEmCw!E51h{& zbaAkOUFs48|K-!i{B50v6buJ$qhpedhME`vL(ukb*70xX?F$k)EVlVzk*TZ&p!WgWBCD{g? zwksS?+*DIJ{}KM6^iTEs=HPzPUTEh$VGA}1L9-WjVo}+Jtd{>ebXT)D*T892c5rj^ z8T!V?vqQMn@8%DhdJwLry5J$q)8tQ7k;SijHIsL4$9LU4o|n@kc2D;%^#`%L#E~*i zney|Ytj*Yq(QjOhOTV2zNDuiHZ?24H#7=sL@LGepsvh((BNF44$DI`hZThs+SylL( zfJ5-a0dL^4u}OdSj^EB7d|{l%mREhK_nX73Cv)nzaMhXIWz?HunVBDbzFNdyAGhqu z@KNh+DZjYkeoN;@Z%%WP*+Z+|f4yJt`qw)@cD!Q&^o6g!Zob=VYi4@!%Bic`+iQ={ zJZkv@Pq#H=tYh5PSf~SA*faaK*2^|7z1uCP|6+^Y{;A_tANo4=4fFiV*rh4bZ*&5@ zxW<5GnOG-m;r+sJmgIgl;~e)}YXinTQ#VFa-McTp{D$hs^hw_Nt}C5>jbo^FyHtBUIqLqyI#NTdOyG3^Y@+< zM^<~SA+OvUXn3LwtAdT zIH`)Af3-x^RET(Q@LAOcvB}u2x=rEpc=tKgQ>x zV|>Atnj`$pU4uvUa^Q(kK2s1E349Gd968zyXA+zTsp1 zL5)Sa@9%YorSa2@v8s$fzslahtq8|4csO`DSj12L+~IolZ@u-dxmT=tKgS<@jr*F9Osukt`}f62RM$|{JcJIX9L!;6WKnVZs`7SB8BM`Vq~q&{;@c~CyzfK z-Of$2iNBOQV99VH6e2}f`OLjSKiMtU%egb{T#fzTJaqqgXikzHroYoixL6NwujGML z+yXxG3i%Pi|G_u$ztjHDjYG3-CjzY^dT=G-Ir!7zt{%Ez9_!mWn%UWhz}1ZthZ6FI zdZh$XIt_3T9CghjxWWp*HEr)h{6Xc`#qWn5GKeWc;}+aCv^loP@D(f&j`*H2C_;ar zZ=fp5*xzJM8&prBTeyRT$uSuTjAD%lI9b0NP0u%pGx)#) zd?Pra9>{HpmZES2_oTi(C$FD$ABCw6pQ;aGJ)7eEbNoT+aT?(r#$Kw$BHn5X2hwiS zN2NiXpg&jYqgc3J9ZjE+UM#Bh(|O^L>CU2Vp2KOpkyLrt%X~+ zB6ec}&QryC1zt{mKkT7>*#q#<@yGas8lSlT8k@X!6$5jQ`L0AUFi*62kbgH*WR4h) zXXivHH>^EYncQhHa)NoQ;B2yeJCm%~ZWQbdELj{{+8S8Ya2_9*?6p7HI`77-MP;j8 zi?k=OPq>3%>*7!He(FrQ@00_x+TvPoIq&p3k$ON{np?Z=__?qD#=*YrHCAnB3q&nn~CJWkoia1Fr_rTyBa-;i7NWBfsrS4n#^jaR0Bu;%ie9^)AL?CRshj0LLh zYF|9q0e|Pbih_B^o|#6ob+YVZ^Uj#|oO$_PyHj(IUGZlxGx9{*S0{B%7E+B%dQxyY zmxjp^>D_xYVXM6FGyFlX_*g(&GUr=^dxYb=htI0|OXp?SMf;cWsv%xynN3D9w6!9q zM?XA>%*eIa8J?Dx$l2*KvSwROCx~9dgdD?m^~TUlj7%AQ@TTJ)#M2%+nJQ~eyKc8P zd2|8lAL9?I3XGpk&srRs10R_gi_Jm{%8G9sE4O=3KRQj*-qc0d9-0;g;byYIDg~stl_}AymRkEIsf0~4;qf3_WyjX zdFXdS;Av+4D}F2|MjshYH1FZMU))u=rixn~)H7(lZIIcH9_-F$d+%mXa8=ut?W4IL z^s+Z%eS*1G^xCOT?aXFucWf3b#oL{A4Emy{uki<&8|61+u4JnG>+oBpV87OnjBopn z@FyPPMR^=T{LtuFud{<%{?=Ya=5unrdWXTU;Hr`r*5H1j;Zoo!38yym$Q zZkH(uE_8#(2t$Bhvw;6(GZc|VtyOrW150M~TjJ4qqAkjG=-n?rJD=kap2P7K{-6r} z-ka$B<+Gk83PW)P#px1~N%>%RZ@|uUqYuwkr8nyr2|row;)_Os`9ZJ&|6~W^P;ri3 z&js&)KEK8vRQYunAEDR2fX5AjLAkxQLh4}3W&jQ0H4!j_V=F>-=vY>KDJ1-}Do%fkd&U6~~Slz;N%>N5IL@Bq1(i zlg_uwmtY({rd5m_oy7i--x*y|+2L$hqtNzDpW@$d@dwSlkS(u!g_I@t6)egev?Eh9 zDwlnGZZ>DPi+s=IwyTl~`q3^2dy{iO_~)~0-(L9JtG^q)Uh^%#n?Go9b&bb5USrl0 zlAP%VUE28OX;W^)<@EZk*S73uaoZWMhR@EZLS2xf7k(jz%4WA)_4+%Q`}g>Rl;6_a zre}l4_%1~4aBsaXZ7^5GVo)#ee~nG(9fR+iVn3g8#$}lxOqz1*PwE%^>vnB}JL}3n zfIq0QSiW~3{nD#B{tfzSG1qOax%@{Qe#2!^oPZ9derca77K*w5=|1c~!U$7HwK?fQ z8Z8>^z}Lb$MzKO}XS(`x>|kxi@v0AS5MC?Zk+$WokAIFGCO$~ogzUx4dRA-B7WHM#1>jBUb}i_gyXcu4pU6$IFNU4c8#0bAAu#j?k|PVSX#qBXE=+jO$;um#-lww zzSNK})>{@^k$Sp&Dd(G!c)T`BJqESd^t#%vr?Hzwj*wArva1zBRz>~a+oS13-;Le) z;Aw|^H<$GN8n^W~pUJ1d^S^PJ?^Sv9Iny?Tfy*`@dnKdw!yhQOiNVB&8VaCu>T5=P zL?O4Aw0l!mYdQJ1hAf{;xLc5`2+s@DjJ*1>1=@=}hNA{6EJZ zKgJ(4{sq~g`+*%lYg^M_jjxfvnfW?b9I`!ckKll0NP*FG+u#8Bdckz?bpLc;$!?l(?(^7Fl7$YzKEYPoDg5Rw48tyJ=AozWvxC^)XPNXJz-14okBa_&I``@vD*uYQa_XzD|K%*5rs z@J_{u_!9X?aDMej=h(AahqVM{pePs^Shc6n+)k5Ip_|OYHGA*}# z7Eg-bt&lS=fX-&53wJ7!4_a2QI+|5RzSYG^5aTL*6I^;twRL7{N8I;ubv_#n;N!W( zH=8vJW0dY){A2tk7Gwz{ZVEoODLvQmg}TQ!D-H zbXD(8+B@>vtvAWUfr%qOoA%atPKa0Pu^H*%gNCD)z85D~rvpA%bvMQFeO)YFR!#r0 zmAQY6KWJjs&DiAgYriC)HUH#-+Ht3Kjy_g}%J|xA53l&Q$@N8^IB*a?BaO~a&c4>o6x%RTrMe-JzLJ6@a8I?r2!Pi}Gb zV{x#hRf}Ial5=R?8f&hpaU{LJqLUV_?pp!d>2%rWqS2QPGVh^5DDBs5!liA&MTpiP z>uT%LdWNu>bM9i27x0-plF`lM- z`(%2rd16BDD|~BB+JqI)Uu%ndqGj?VigyRjWVnvc19&@gM(2RZMsOKKS}ov$vugB zioNy(3E?<-ce`MmhU;|cdhKh09s45fZ2Qby=H%*IDt8~8PxM8>)`VF-gT2r32feiv z>%+9#mfwQowd^o<5_^G$_I25aA`y8W&ccsjbXEqQsy9!^8=AHF)S{C?t?n)x#5-i8 z^b_(9lKau)(1J6s8?3o-oUGw>n%tsb)1Fo;d}8p>O?3V_{-ElB*wroLloj(;!?h#2 z+4ODYl`f#$m?$Lo)6(=_=(Z!_pytCt^b|SR&j4DS6Y`73NPF3II-9P^SE)V#XY;t+ z7s!d3YgJCmW@R^7EzsZYE@lQJbT5ooa3YCmP@D#Bjs6dB&1iZemv!$~+UHq}->>eQ zd*bB&or8|fT+TeG;Wjk5_E>L78=O4A8RlEo7}Ht3AIfd}`9rNwj7DiOJB?O$Lisg? z^`XIeVf>U9pW$v#?(1lw8OaTt^sch0#a(Mu&P{6-KkwZ*>kH!*{X=c*8SHL@11Dnw(i#L^af3hWb3f(tS0wFHd7qhtcSqx zMjf<#gX1Ylhs`?1j6?rpr(meb9wQ%XwMFTcn zh_z0wd8!hsUGu@^gEyD;UV^xMk>0YL4PNc+(Pwv}zUQU2izNrX1x0zp10_r2yWVT| ze8iguEb>llb%A+{eNX=IXl@ea9UIA!-(0U@0RR7mTbeijU;klWif*Z5h#7aaw>I1; zP8GMFGMX{OdXwYB=*Z|RLctuN-h=6?KfPDUF$NiGaeJ%}O9Ta{#5Po`z0hEH&e`_T z33l_#eK0o-o;uBzld;#2%w`Hsu(UVH9XTOCmVeNTE2eZNW{G}c>DFpHq8q_l-aH}c z$Q!tK*jn=yOHRg$u1=eja@PqjkWU(CG(#5L#&zY?%MPM3*6P=LIhU(0?eXccVT2Qo zC?EUt0462pwf+1YfAH1zOnjfQhkio+Z>dv?&O>8BAds^>P6s@cWx}No80-SAL9cK( zP#sTCaPinZqW*TIg(mMU+MSMrjAqmtwq|V+ayY~>@;G}R;}5E=9v(m?s7>j>HqQp1 z-^wW;mv|1uk(%}$y`~SWadkf-XFkQJ+Xy4%T=g=s>yy+@uS&xqfvBz@0Chw){ zL0G|n_t^{%`kXk0AL0)xeoXVA%I-a|#oL#AZO(88u}fnbZ7MV>=)Te~OuH~Rh)xZM zuKT=gnkFv)6nh-)wm)(VhO_nXsvx`r;uLkF=L1|Xy`-9W{u+M}3tn~R6Sk_7{Boe3 zeLx2i44Gn`ol7mUwL+KkDS4+|oe%j!!C$K?r>NSK%55r!FTfUKo()K+PN| zKfka4o#z(@Wxj&`e@P#R-FqFvISuwid&eun9Kjzh&w8a^nn$eZ-HC^+{`B0t%=VO> zN*iDUn!DKY!mqq9`T~FOjcv+~V(G;Aq#|$!Ux#;sR12@Y^X~K9X_mT^(86|Bm3{Mm zzjl+%kvga_*??CCo9Ob&&#B8_;t#&+eA$fb-oLA7VYZ3`;1uwWI`R5GFAolT@RYk= zS17Qnsp@`c>a%13(ogMX{{ zF?jGGFL%P5yEPELy8?aU)d$R^^*i$qdUGPb&7rdY+8=dIJR9a0Z_G1g#&4B7!y9~X zo%mV&c^VT<;Wch6-@VpB{G-3=&~Lo@;ZIzH`NIeQ`K6!#*zmT^JmdQ^b4$!<1W_RD zAQ#O#1s27}PeV1%fS5bVu zzKe6dOT1isvTAQ;ovJr{=umqeQvbr0`EA*?jVboBcf@FgCdr<=O# zHSdw%*ekQjt%vrWX8%d~DYySC`3HpwdH2#Y#MofgkY39-E+OA zKFl)tb?+G?%$O%UNEPr}3wiee`&2Kly5YWjnm@>m!;2!Nt5>3yv*94vTeAero(FMbCeoAwK!6JUZ|`#B))Z z6Vd0|4~Ol{>i{cAa6~UJp70<1F!+pDcmBqAe2G7Z7A;ak?LJ&4#v!;kt20;*c&0Fs z4ER_^I41ZSd71E~w$UQQPhSW;*QbTyym)*y^dw70Y-@kdN5Bi3FjhCjK`bq}>pwXtv!yJjS`zh&oQI3kUUQ-WXug=-x zeuB02u_++kQ*6md8x_7|0lq;a>UQFk5lz&#i!ZmLvNwp4mtRcyA$5i=Mg#C;{6Xv> zk0@ij|7R-`f~y?sgB6gchf_jDu;;E?_lfAF;~qb^!O@mUxnwO&I1 zapxF+qVD5bw&06)&VBQ{okY@UH!AsHZI&(N_N_`fa%iTn!~>{4s3SOp>+~9oR~QPm zATAfLlc}M!5BaVy4JVBo$Y@^t1r#&E>BRkP@H>I#w%LA|eY9ySyzSpS{Fdeh@J9|m zTD#a0AYWsG`ZwbcV-T40S^b(eGq=QF?oIshZZ&Yu_{b#WAGB0<)@sJ@PXFM48Hsl(_7u4Xh@VBTZg2$_>1Mp4Sk+8w!1TG}`EraL}50~^w-_{Q7!M8^J={US- z#7(Zr<=uZ(ss63Q_-v^JpYgGWrm>wcoL64jN@)lpF4&`i*pX0m`W>`nTQp@Izs{m(TGBDZ5z%Qn%Gp>4C;N z<0Uyo$;(W9P9cDwc$4)jXyq3>>j6VqW!Pa$4?!;?ab2noW>j`1b zaQo4E+#$7{N%nKhX_fpjnYNn|aNBH_`%e3JpJJQ zWjE<0x~E=qIK79Rjq(yQ4oD06sa$wTQ$NNY#xMF0Z7_P9*BiBVB?UCxudEkgxc9o( z|I1;tcg%h?U{&MjXPfm;9<2-Yb=IS9Wf~{ZYbbZzCGv}h(?}%zS9q6@-SSp{Jg@eY=H;juh8C? zcmvH>{Qv&P&)$$2qui>on~kow5wF<``#e0xGMis6{J#&arHt>w?Dx=1@+qE7Kfse^ zF2>nI@ml-nkofh%)V|gheqgURk4NUPu-hbV;2zHs=UUpg%pb1X!((BMmxp?+wY#38 zIbZOyo(!&Vw;vgM_i!&e^7ZZQZgl@O{@~l6`0~ZY3#AeK&WCZicZDa3pCVX_9HO|Q zTanvlFtr*NyEbnHV%ywy+ubzAd*hRqtJ-k84j0}l%*6e6{=v7uuOI8jJP-NLzlK-j zbQcIEV^ta@lWVAdNY<8_!m-z_rEia}H0N1PX7w4Kg{K*u%^3DQ{@~jujz8SwBs28@ zZqS{8yB-`TdjLXE87I&2ywaOYm&+@v2fIT%H!ZCEaE8eB>Oq`<9^gHPyMuP8!PfZH zSNVgqE1DD>@_#)%{u$h^u6+eVKgWC9=3$mXoHJ$Liuug;(91SYJ>^wnt+Ig6rq6eQ zLl$5lEXBUXAEbAiHpN`IOa`{%ml=*37>-Khco^rAd%M0l&2i3eS+^TJPWy2@4%*>M zaMj4|S{28v5&1w??W$^C7xE9j@d#oV(st*v`V~|2&cXaPPw{{c;X(2d^U-3d$M{?oT#*cuE&@l^P?dwVU%|D5VDdtx&-2`lsYMfZ4S2w7DBFx;^}q(V$?Ae9+y zATY$kV%k2Ze0|QTj2GqV8>U#o{Ae3F@KfQ#>aYd}e(jy~WWCx4+h6l%`5?vfV=tU5 zTJ`?q)Evd~IpSh{`^AYE=v`?rxdq^^w7$k4RR7dHwG7tJx%Ri>UYplE%YSY3oP|pU z%EQ`ui2msYF}qu9Om32nv>n^{ejA>irE~||Oz>m*1j*&~CH|l{G%8+!v7TZZKd*Sd zDJ47zu#XLODzoY;(cYdPYq;1Rj{;wcxFPu8#itWC2EL>`U#mTsLB{W{I^Yca{cHR| z%5Hd|+NbN@Yur)H0yR-8t!~Q-aWBeuSm06NAx+c**j}a%-YLEgxM0O{+I{?x4Ky#I z9d**g`!w`E@Q*L?2gUy|@hI41>O*k{K40goyqX=fHo@=QitxOO<21lQ3=dNNM*Qz^ zD?IKIW$qMdUvR>}+1xs~8hj)$&ad$Y-|b0#7n}UsQY>4Hb#7^VqaDhfL6g(%sQnx4 zgLY!6{n)EWmi&Qfqs`gL)Z7($6je9va|FAEWtsT?@9_tz1liynVE#VeYQ7`1P@FF1 z+zjY}KRq#|4l3j*OOhU|g>K&=y&qcWh7Rv5@+W4b!%JIQ-3W?xuCys|6Jc{-;ty(V z~y<&1-H5WCB|IFDfkAHS{=VJK6iN8 z;7?#4YFp~Finpk=7q{NHUM4;|^OWA(08OGQ`@e;_jdpsGyQk~hSzNj$ia|2-rq;v1 zoj>Ro*N!n(V=cLLqn8%?kL|l~9O{7>7o+h?ObBDoy>-qY@?AKjGVW!y$iBEW@NdCt zo?YUT!EN0B349daC%7P5cVFWV8a$PC2{CT9a?Ex+@UsoS98NWJ@#40!POXIIkmfoO zUJNg8436DWwjUo*hF$ca+{FgIT9Y<5MUQaBLXq!5*R>0#@GbtJ;yAaK{Op1GYz05O z!nlL(_sZ=YlkI0|7+cI{^^x+?V$*af9kR7;Mf|Pup%uVS+a%`Y0%qBSJ1<-#iOpnn z75{9boQdD!4{FSf`GVVA?A&S-PtgH)LW8}%uhmC*)=#5GbLL!*@uM zzi+qN+vwN952mbR^!n=XQuo}48~6uKkgD)1)+IPgzri0gV{R-^JjON-xH5g5-&Pz| zrCl54c9Yw^l^OWx`<2Gp4xgLjmHNT@)=4())uG^KqhQPE(i%=(wtZYUV1eljkgnA{ z-q0JY&_Wk1d(nsp3E!B^dLol80?&EQru?s z4PqZ`H1CpK`bc|yuk9}OtgyST6T8Vd_BH;Xk2O1c={(s_?R0~AoA@rRo6>M&(X-e! zcJoeol$gg?c>4Dp4W&+dL!Bg-IMl$(;gE|9x!w$=)oQPm1M_fFZXq14sUQ~ubwGqwW!)Iywy$D@Kqmh zz3=N}^^Vrj4eyu_ zorQxmh&Mkbrrz1olX^FtIXhWi}y>t*F5FzC7B!jRw?Oy z$KU4$G;&UuZ;%q@zE2G`{rJHpF(e+TXV>14_MxQVtMaDZ+# zzHaPG+goB+Juj@oE?5t(3fFzdKiq_K+vw5lwrtm4GVha`jn(8Px_Yc9>(AvMWKHtp zD==#l6;{}%UV!B)w%OB4%^ky$ZY}=UwgQB{aCC(i$k#l$Ju;J9e&mnM?bvu{=MMc{ zZQBc^&Si9mmv&}Hey@ia>;#9oHC?&kU6 zb9%3vZQeW_#J&MZ49#y^YTEj7`6g><+3>r%$M5^BV*Ni4-FAJBSWB4qA z2RRToiMTk!AEb^x#2-{$ST&5%JqMqE=i1h7G4(-fQ=WXI)sY}wIjA=Y_Jx0jnueEVS)xgO$~e+qw4Hc&G@auX0|#PL&p(?8G+ z-fU?j_C2$>rHk7~1_{gDSl0jXTr4G$ne+``mELV(5iV1 zsJu2>Pw+RhR^a{bb6)>0{-D{{syrO(x8mx0?O$s}wR8TUb=IpaTcdqI05sRP`jFPc zZ9S2kptMm0$Np_eT%&S!0vF13sMAc={dry!33il4_TKjV)aL z9}j)6xqqBk`VJk&uJ?WCS=V1{M(=mLH+f?b{x7g8KI6I%V!w~l;(XPq*8BXy)bVfT z4_bc}%qu@d()y6R6MAj@bod*&XN<4niE#0UpK94l45bRSQsD1yA zS5wm7N#jq-R|%(1+r?eHI^k!OM>*C4=t!OM!8gFuly6P^r$pOv6ViEwYp_DY4vdpH zf7kf)0_bxJhpYT{{vdcPhj`^WG2>OT6KOph^1M-Ii#njJ70bhMGSWAd|2Ine5f>0& zknm=7PnC16)26(Bm+Rm+!X4lVev3c2)x4(it1tVamD2TbUsd4D$`4n`pvNhHTooRt zuuZVReQj@Nf}438?og!f!Xp%qlXk@4()bp^I9K<9v+7U}x(DCk4~jR45t=KaeyU`@ z$lK(6Bqu01r`xBD1^TnFwSshYyQfDY827ib)@0Ww4-ou_v#>L`Y-{Rg2B&tP0b|d* znK^yE!nJSl2c_TRlSV{98h0M&wU8$Fn6&O_PeiAZ^AX$HkMug$spN~xMrVYURR_Jn zEP9Y=aMs6Q)Yw|zvfEfwc5rkaXvwL6eS<&f-%kD5p*fd4OEz8Ld=3I{v~4+n^p0c;im--smckSV4F?h-00dFJ;&&1GvC)PPTU$@ zr*kz@^|jah^Bw-623$D#`t8jfT3m+qpz)Qwv^F#x`YO7ti5fW*-PRe+(QI`9K1kjb zaF4a!g&PHEQ$1qS@|n$~$Z1RdrDV=VQ}nSeHowOol!iwN{pR6893uJ${(Uff+PAQs zTatW(9fEpJP82>Y0rtzu<$BM#shxJjcEj^`3v4PWOg{6S(P z8KN^!Q} z0=gTtDw!Lf5n@XgbC1(yG;M`*49E}H#a40-s&5t1u9U{8J^2`akjaDi3bb><)686* z7{L+0c1W1ktvb79e6*o|6lw3NtJ5Vmyx0Wo2povcpf1D_#BWZ0px5_&&Mi07!AXct z)CQ2$HJZHOok4Me;om-o!hYN=x&1tC*vY5z4{FXB!wm!9l$JDWjYRqUXjIFkSz&Z@ z6O-DG`K{9@c4m7fZULTS^_bek?&)mJl=X?u>oZ9XfM@mQ^qf_ryoWcrZcY4yb~L>f zf3P+#H$48}fc-ml2S1v&HZZv^Kg1to%nhs)w=F?aF~%uk!{7rGyU-brvXNaZ-i@M- ziY|mTiRUlrWZtxS=>@y!UeQKuX>G1A!{f4YJlfXcVu!P&`{DVHImng9BO$|-a`B-_`0viGQudevLHn?1La zzI!IfoOxKdS?=jGi%r$zlO(T?Re$slCQUwGJ5x+w+jbdUXXeoZ+|R@G<{TMNOwR`k@xu?eZO%r8WCyPV_ap zKr-yV$sc_0rTH$_?j}|p{6}O6euJOToXl7dPWFdM@?(_utt>p*=ku1WF6bAddwMWx zpHIVT!RkMnsq1x`I;?}%X;mEgw|ytjD#R`^)^68KwRZulwzENEXcn7C=diW2$?x$8 zWe2{X=J>X5R!*yHMln|$KRlMp^Sl}~Q~SsI)bbscwbaVm`%Z&E>E427_>bWazI>o~ z*o`;+R~y7NgL}6Ub}+g(x`*bpGpv=$M)S#{#Rj(jDE?qHe{4TCNjRAka6s{f{=z|o z6QYUyOb(=nVl4pxbO41W;+w3?PF^Q$i2wwSu1wQX(pT=6fh=OgdC)*j9^*iM~; zyXYl2D*aRVgP;UCad~Bjj(5BAUg_Th1k>&*xqDbAJJ&zPbNfZn$M$~vob{K_e+qwa z1i{(k(NbiMQ9pPZHmRQjsL{4=+hx&zY&>?0yB}Q&GO)}FB?2|h{}lcp(I6S!^&eaG z;#YgR2kDD~tF!FiG|J%Q;GrH+=xOva+3?cQ=l;R`LFP#L{Dbbgb@Izc?7`GV+mfm0 zk2#p{nfObL10hH2Gz#uvCc=b_-~G=pg~5!n$G|?Oi-2W;EyxtDopfUSlYfRGTZ(Cw zN^YmMFmXXI?1QzL$vyRF7(ppnLux_!>`c*1Q4Z@Am0{`utW9}H_AUM(_COm9K@|GvEsAv~rgT2jqGJoJ#x`qmRdH*%#pT@5?{vx6Hkl zi$QHG>Zfg|1Jmq$cADMN@a@*(M$N%A$34o34}Q)ymt1_Eg7M9}{gF5p%$z+q@%SlUdpwFOKODagE*40ew&8Zr=Tz>-PKWx)yUrLD zk2{;dJ9&D*GTj1yA93F^-1mwlE}o~mn6SRaAJiIxm=U$3MtFOCtneoqO8lB(b4=QHYmnjWH7j7;0E`m-in!s*`HiOSsWrR}%Z)#pHA=@|)bBO@6m$nV!9Utb>K=tzXF=a-Dm-wK^^H{p%y*zqLBiOVdr?Qu)jG z_=AkKmM;u1SR7929n+rB>l1g`S)=pnHtcpKoi9$|f!xXE19wXpj4&K{QQ~~vJHhIG zb!s-rMuzWHI5u@WRD6)}RrCDY`GZ;q`L{mb!QO{xPmKR)7NeETm<6v~@fd~P>fYN` zN;~39p2M?K11g<54dcf-U3RK)|nubyp)53A75 zm%O&K^qS3#?_`&64?kw)6lUejymEPc%ZqnvR3bV;t^!F7I8Mq8Q;^zY23U$y; zg5fT}a=XMfe1|{SzucJ58p&sR%^0~RhjgYG%eN}Scqcl!_FC}0%EM}}v1taTS?QYb{y+6T6LoSb?e+4e~Ul3X`CQB^}+k-8^lye z8-GB%$`5yeZuk~)*O}s~J|d0lxbo5mW$Z<_ax)-40DNrytliqt+&xteZfAw)`f|H% zV~O-`I#2Ct>C3nHgNs?7zL5c=i=c0z7_kUE%|w0@TUqhlFhjn<*gjcbb}MOcS?NkY zi{O*eGhIE&0ZB{)Vrc{Q4`1tC>M+Ebcns!NQnP4}^&S480#xgx$B8`Tw#g&yj=?f3 z*@qh`53k{T=iH0l*MVl#cTCusWKB5+8E-dk$7cR8IS0YT7w|d#B%EBa%^Ubvx%#aT0TC!UPu$V5oyNGg}leve@1?%N39J z;&J86hkf^QIUKpPt|#k!JmwG6d3sQ~7Y9>2J9u2a#UFfWm#}VUprRe%yCII6Mqv?S ziw&H^Rjm2IGl!d|dxw{KBd6E&va&m1Y&GnvBk!)WDOW5cFc4{d_w;4;|r ze~Uk;^*UvZvNj>VllFziZy=u(dt1d&`$DC1JdG^s{L;3GgL$5xUGeP9aR;~D1v2Yie9Po5k@k|}FKJW3 zUMOLA4sh(``Zv%Tn&uo?`=zn@JU1$<;`MQg>}%V{j#RscLCf78M&VO19{(DDP_etL zcPX=vKH=5D+egb@ad3-B8`N-60dZu3@DJ<&^I!%)x9wdF?GSsp%h==A<$M_B&cRs| zY%OsPo|0eZ4~i=xpN|fYH-Swd|0!K833oE(7r*O`o4;_WUDJKG^3(Z!l@HjXRsJ&A z#jnMB{B+nQPKQ(d>-@ntSLkgEua&3o#|Fy%b1tJ^Ji>GPGL3JC(few6*oKeC#h@Ho z75)BI;Ch=CYOAmE2VZ4>a~@57P$!G|ojLq<{DKRbD?hI{&-J6QIk&FI-X+bPIvQBf z*<*TJ*JpNM@iqS7`~LshuGF@;Zo?HfxZqEju|VhVwq>48-qh>UJvnI1jFr0+>{DX` z_u!%vIJi30$-b9=@TX2>i`ts(l#TB`qwAb2=0vBA5245Ufwx8H`d2zZX6z$36l0#p z$N5_R!Jl%y>*xLaYxz~y|8|7%WnJNRNt;0NLh^@cUmk;Z0Y6gnk%t>%q{lVUVB)v< z$IMkczwZ^NKjq;3|Lf3qyvzLR>;Kp9(tGsxU+wZfhreq%;WzPXv!?Nt#3jNQlV4dj zsGRbH|9<}9yKHZJe!UU8_Wt|(`Y*33GklLB8ucOU@Nk@<#TzZPE!7Xzpm|BY6V{kI z;Q}A~G=Gra!8fgTV1Dx#9e~$suA6*=DueE+Fa^%_InxiQkLlX1W8^3H)#x{``P66f zW%FLWPv5WWhEwq1#M|3gB zbwD5WGsbvWI5ug$wT->U9vTh)pBvFXdEu?ZVw{3nOT(FshZ&UD)h!#5bG;U9SUvA{zNKkAv5 zUmIU!q)Dzd&Xv~>exCA-eJlT<+JI>z;`AzaEE7R*2=00!Pel1?w=?*D41Ot}X&1k> z9?Cl--jR4*O}Bl^K38k$;^7Mu7Y}>V@Fc|>{2qT$b$~JA4u*VA*S zO`Gd_x%~pzPrl1<@dv%RH*jd)(bG&&Ch-~3hr_RHFhYZgxamiGACLJ<`C_QH-P~7K zqg$IPgy9K-S+!&Gr%JPva)a5LoJZh=Yp}vE@du5bkvza?beDw7Hytu&zZx&Ah4Ks~bjg%la#Yw6x?TWuuoq6y-EF&@-JhLb~|!z zo|Bj5oXk@7%QaO#-&=_|qFCW?@dy2^p_cbFJWN+O4*q7@f|jL=?k@Zl=7W-T*+m95 zVX@>4ltx{*+IemcBkliAD`a0gPCcIAmd&uI^^{o){5*e!KN$Bvl8@C~xhUHN4-=m! zZfpP?Aknc7OnP90 zhbAFM@apInUFL|)y6In~_RG>1FPeCfRy3a7r!w1{vxw6M1NU{5KK$&#a|DYD(y@YL zH?w2jxh%NGmleee9HTcX4wnVyNn89-{z3T*wcppFy^s8b%zLT+)&g&$FB6a82D0{Q z%qD0UuAb`6aja)zjjTyIxlD{H_okcfAJ><35iKR@pAM#Q=->ht(cW#-zQ|eGEiRs! z1vqVU39qh8UmBVGYoEwJNRcVCV%@xbn%SFa&*Ss;YhkVJ@{IHJhXneM^KQ&cwOup(04pAE@_TnE=y#|)m$0ZcGdWVe08UNB@W}}E{G?qHcsng3Ep<)?p8mTf6#Pq_TKFK)yDp^#q44ImM^P4N3!2a z&t{`dH?o53-1d*a!ZoD9;gFsQyusnt?8mw0=U#*V;A7Bp$ai=0vBEI)$P}l0ayTz@ z!H1nMUa$F}b^ToaLHWiw@E>>&8KVbnz+h*>afC-%<{D*=(Jf$n0{t*1Vp4{}Ev&wgai< zf094=t6%h8Y?c3qvnG39_(!!8?>OynJRZn3uwK>2_#TRe8CMef;qsgL2mKa#O62Q& z`FB+%<{Hn!msRW^Uep^yz%Fq+SsTRcRXctdl;PN&VyD34xexB*yJFgRD9%b>;}8DQ zt|&JB_Js#@;>$)*-&94cB0JvgiuzDL5AJfcd%73i73;B8u^l+{kI(fWjL`e_Hs}0n z{J}Q{@T!w{zmO$A{Kt3M-}Ybwv|$s}uuCO~xXK5j{ZcU*xUQkLdo~LeuFjGf$)!-^w3U z8C3b7`WIbafSz~#rX4;wou*vGN6fT1b5w}Y4jc%k{)*WN7g`_ZYq)1H!hf1S$eR6s zupDoW^@DvJXgc2TvJa~v%^5*^IlcHvuCCdi{W>#hH-pCnv6Gl`ux?P4cfNlqhqoR0 z^{4-tSMTwoU-$B_?bgRdId;4tzm)fva`4);$InxacaDFvPQJ&|s~o)RUr>(Uwu4@4 zUZ8Bh^s9F{O#gbf2QPl2>ks-C<&X}i#`&vX{j}xK^7v7&?=@4kY;BrgwL^j?%I0R~ zE8SLke#Z9D{!ti-cfHz%^)d*>p1swO*hKOHFme36_xvN}F!L(;P{&}7(HXm|L5qJS zz9N?Kw`4r<=R&c2=uAS9>+(UglXZIpLc#tBx_m4=P(?Zo~=$)NV%ARD<|~sY%so zep9fwzN=eT;uH@q(1TDgL14)xXvR*@mRy6R3~27JLvtlBeRIfycC4@Pvrxh2EO% z@xl|NYbh?Cii;ke!4+G0QhQ~h4Bx~b z1Q)mH`_q`!QlG$<#T+C|sXTb50Bqed{5$GZoK08x%%dpc+VEhVMqDO9w#g{hkGl2m|y(~Ec$!+gNh||YmMn2e$}z^-K0&hzqiXC zmYJ@s@76I_^v&zz99*4k6Y>;iF^8rFfhD3hB%TOAemn_G#~ z%bfWlow%;TS*xqHR6c^Q>%FJI4ucJWxz)tQeq&$h#dS7f{9^HXLIuirBoXJ5b{)OQm%L1&xy@5tW@JGIq? zQ@c#lFQoa4ULSMiHRUh3Emv_u%d=T(F`bn&aMf!2X2xm#?93+X!Kd z+2Nz3{ZF}uuhpzG8SOihqjb`&W)Dn~o!EWM#gLKd3Qc{IyL?2*vo2bF*)DEujvP$Crw|wefAU2ai-ZJG#`Y zX4y(U+rF+4mf8%EY~R-j|D6L*H^9vz<2kLiShql60@Bb}nHp6PHvuyu2Rq@GD#A zl{7^&5$05M7@l+YSy*C)W{jVQZu^R|T_@%`dt$u8Lw6%3d^4(ut$}0T^Iygv)EHO4 zGDkesn{YJguoiU|Wlq+9y%(L~ln8&5=dDu$UK>bAWLF20UGNM7Lwl6Uzvhh9FFDwCOu!IJqqJ~UoCaZ~Ypym&Q4#^``|5G#;?!@7K`k86#4s#Nm6-?XPMfbTmEt?vnPuXV6}5%8E(WnD z@LY3?vD2vEsg3YwXU|eOp*tiDfaG0gVkV*(-Vqn_{=B4~^$;;xp3X703*>@efvMeK zo80@4)?skW?(#?fVV_ET8Gq38_lHlSIF;{MN$>t-I1J1KdaVZe@J;ux65%;*0hp!> z7bWuU@gQ-Zq->jod+X+wdwh^%mq0B`jJ^zf!OUqeiX98b*txJx90x1#&wn8P;QOpX zp$uOB!k^%JsW9MA@Hy8LhjQ$RGd$?-vKLd1y(Jtlbqred^S=8$3~GtXepEC%qx)Pw zZ&fRIVq2T9cw;(zA#H7{iOO0t2faECKmNvz-;b*0Ke;}C{{6gMLUTxE@XBE@Ch~AJ zIMjd0S%^ohu zlBVW>Zxr3m}ebIq1f{mh+p?wLy8&;ywVC^K{0+Q5QtXMhjHKfoXS zEzY5`p|51IKEfGd?}RoJ)3mh-vd$!ZPA9tHZ%nhvw0xU*BCeW=EBwJD)c%g-@BZNU zgUb7FuTSHh;Rj}8{2O@}yeKmq&HTn$viRU^hRuSqYCY1Fr#Vm0DVJj5eH(v}pEP~z zQ@`StcfHGq4m$o@CN8d7PnAjP7!2q9@_E8Gk7lCbOG!t7b;;}%D3mFaw%9+IKlsk8 za{jp;KAx+NtE`{&8INbD<=P*h4IcJ%El(NB!rBD?g5ps=?FX1U`JDVB)kamCzJSaAzY&pt;IF8s{lsAJtW!X>C9&6vl84=YGTc#q%Bxd72CZx~W_7hETk;3$yX(_f;Tjq{#WUSqe|rDt&r;>Q z*L%NgPR57u#}&9b*Lb}jNA^(Fw5Z*!LP zS?D!b8*`lWt3N&6Rq!Lfzm)&i<)J>ja{OripyrpZj=I0T%)G=c6KfEkEbJv#mpQ@A ztx4ZFwya-Q)w?Oj@0G!{MNs4Fd-#JM7E2%2*--kN>enXrkH&{(@=pFExn#ULiiJ8A zE7hBfYKz!0d2^=6{@E$L(;#Y<#Ty((bM;}h)EHh`t#6AzX!@hs&!=erB>mNJtu;55 zO4;LL)>@sHm;lrHx?^;V#@Mupm1QeFU&%IcvJ@-poxpDRJNzQ)NMbAgF8-kEpFTvr zP~Q%D2QHOfz3u;52=EVI6NyAKs18IOU8ayb11BI!@ik!4lAOAPtj z3-3&Woq(DAHhmR;(41K{f1CKl9zS3H$62!mn6{WjN8kiycs60-QJi7{$BADAB&Fxz zBEkv%G1=hhDSYhh4MkAFxpWxUMXVGqnZ%E(yqx41b2gNnmbiMg# z6UF9Ek@(NT7M0mIySL>Dortz@vBcA^!}F@6Lkq^~NMAJFw{N3KYPVb^`3l zLlIl?#RqeQg9$tNgY-rGLDplhJv6UMYf|$nwm8R>na%(XU!1SS*~L79%)BU@wXJsTFp6XJ^+r2geU* z7P~a-fj>%L%O6xZu!U-m;I&8GqCFO~6myWcy~2q#55eg)@mg=)%hEL*?sF@E&ftW( z_sl%YL9f#oJ~9bnq)xy{w%RwDbJBl&Wc_C^;0tY=rTKhpKkPf)mj>esTerLKx%Ob{ zbP|hGsuMy|ufJ;i8Gk* z-&?|L(XMUX;CYSu?MaDvI0>phQv#zA|Hc*;;t;D3tgPa~GQj4Z|D*J!{6Q~%`s*yG zfgbhsrxx0WXbsBN?!ZfKmq*!sQn)#B`^tg1H7vs!jGbEvFxv}py9yO>5Nz!CoxyUR ziZiwggF{dMFnuk5@MG>}tfRl~OPrQ%);88Se07MUTNp*0JnfY{Cv!J9m9M4-mp%2r z6wi)5Ht{VN9rC|Ii?ik;-iKWdzlCwqlu5ig@uuFXL!7=>`YQe)dwb5AoH-4?QVeR} z-6&lxAb$CXke768J0GnY;a0B@yr%F|xG)#TnAp-+H<6b9W|Y`B*h|nkW{f-|=))D- z*yGrn*z~O@F@(W1rO~-oTtPU4dgQgaug`o1f6$Bx4Q`r*{$+K+cpKJ|F^xaV4z6#! zyuYV*)zG%lsyK&reZ*WG9+igG^RWf=Z|}RORfKkfe<~eL6m`LiZF~cwr^jW|tCTJ# zO=?&8&sRMs#^13szHU3=?fR?ugI3pDyS+7BXCKYfcheVK~*b?DIErOV0dK=smmCLXC(b3F|`)>Z5Nk@Wf!b4=p- zm+=R+W*E$)sQyKaS7LGATf_`jeBeB~@h7_nqupo z!8e~9#oVLM$UlP^OvFHZHj0?yo)n3C%`)iHTDlo3ahQu5^HN!RL*UFT6cIvnb`hJJ!kp?{GGWUr& z?Dr|wph@XS{q6>8Cav(#Xx%IK)VX{~or~gfQx7xRa&~pNzdDzNCEnZX_Fs-aXwDX0 zuN*uOV1O`h#7`Xc&}>8-CROO+=NZ`gqV{~d)_SA$Qe!K%JDf6u;l3C= z+=_=(x;NQc*W_iD*-uYO*}(FgEo?5?!QqMk^c9p7ah$=Pj4h3J!LDia z=jB_eD^GhFyN0E9Bk#%laKioAh3<9hB+w5&7cc0y@;I#?Yu#?;Qo1>@;Che?t_!Ka zIsiuaPM=*!}i?o8oTT{ zIi1sTG@!8yfzg@0kM^V3rHcvtA?$DE55i4T-qpeMm_Mu%2D2gaCjT9^g?WyT$=-dW z&-jn{W{*@Tzc1v%`+Y8a7{DPc5PUGuiXHab)OL^x>Lk z2Q(K?&9sMyM|oo-gHpb^?$2>7pBqjs|D*Z1oc2dz3|>n|aR;T5T3F?egIV?npOCgm zeiwi6-3DfDp)H`OS|+=1&ZCKC#~lxs0z*H`Z7I|mPun-g3hj1Ia6f3pw}Vh{ga5)h zkn=C|kNa|vea`!r%eV0d)sa5USqCSB9yl1MF4_a%1)ZJ*rrq&b)qOe^#hSbI%fn%j zl|G;5>SRFZ75u?=w{mnRwRUYW@6W%DKlo{`Wr%xip)!252N+-ne6zEDP=t#sY%n9t zu)}${9i+soM<=qT?EGD0==EskH_dSrnv=2Vl>an;Q2r_MAuww@`h0c~t%<2H12e~| zNq_pw!`n>Xv2+&S6ghW1@P{9+So>ud`?p-OC1CeTdKdsjdA5f;;y*69Z|3QLOF`H( zQ|}tf%5l=~x>e4EKJiiig_r!9bxFUnx>fu~?LGf>7n=I7as;2xl;x(Y?5+Mq-Rgro zNY_%o%4Ui2)pVcf4cNIk2mV`{L~|<@FMYzK?0e}|?cuwok|Un$to%Kfx-fH%XJ;_| z06dw!w(hHco2{Xy&#Y|Ndg84K?7`_rU_8dY9jcUafk8A^8eFWBeUGK~kD)U>9}~mK zwX8HaA4}%>)T)l6vHED5YmTF}uj3DDy*6vM%3#W2uxt)bVQ$DifSnD#raAvImgzfz zjdU%2&NMbE92$N9%|ZLeZ4)00EXVa@=SAnzP06lJ>@{#Gr8h2RxOlwwI7S}t&BT)H zchDvbJE=6WYr@6*iPvIyYlUY^@r`=enbTl~!lH~0 zn6NXn}Dx} zE$~G9_LIa#iku$hWFs{5t50~BI{*7J?W8#~?h~ei4=k6lX~{Rjp0`$LYr@eI2BJ87 zJC%d@fpbVvb>U6Y=)?|BUlA^1xSE*X(w|hFMs;>qbspQ{=3zbiwldX#*4ys!hPAi-l`(5hsY^OYKo$>L1 zW6r;Q6bAiO{6X@r;bZiUHE)@=lFglXgMQf(#rK12O5c~h>&jI-5Z}t*K0d4mlUS?~ zj^f6b+7oWAu2_$b3)bSU;N7Wwick1C!FO|&UiZF^KPX=w`jzaw>SKnJ7b_K$S1^wZ zZJ{>l7+qt-39jei$;K7)bWnC;tGoZaTjuR*(1}!s%j;nwjnvZhnGO-F3oKS+LgV9e zGCW(2C8aOo532>I&JuNd>p^=P}E%1(OBSc7Nn!ubV<;QdRz^2gTM7OWCtJXp#|Dvukaj+ zuxZanN99|50Y0yUHVHM$uNw!~buU{2_SWH}iK?tgu60@I9`G^EZ69CyH@5 zduGDevEyg}9tjm6A<`4GQmU7xMR6D|cVCFZEp)k);y zxl7gL)!kG7XRuoG?Yf)?`^4D2gBfD<6=u7Rz+)plGvX63%ya$itN4T3iNz(>d}HR@b29WOA7h1Kv>sf%^Tp|LEcDO4M|U#Xm{jSlJLlR8ZG7%I4NQxMI z%%x>G^Wi?)&o0s zpN^MDEMs5%!8Pvt8@6wHG|#a|Zln32(gt4Am+=QppJL9<`r>~4BgWO$*lEi*smxx= zW*&-n?g&OKW3yhod&r!U+so!Ydi2gccpmWV+%@Y*1w%EwPJ0iwJfS@+f!(5bGJ6F1 z+!)@j=SRw#%Nk|&Z`#9=zKTDH>5}}Li}4Z6HbcY?UajN3qo*NXkE8FA6lH!`nPmZ+ z>iY(p#sKyq zi9h&g2g>L=SH6%xn9!NS+6*a*DNoSq5;vtwPYW*Lms{QL#7c6`>kXz>E!%%gXS3Nx zlXZ5#Ingh*{>XPRpwFm}4yVcSVeuG%_C_+o10ID?xDI-|#QVR_07efO{8 z4|0apK9RO&jC7l{jc`FdH}O)uxFy{v7$Rj={15G6z})52dt;p6Gl}QDeFl^Y+|Q|d zBi2Vob-EcliR5;*I|ktjhBkv#WWBe7Ovl!y5DRbiwb*))3fq3?3*!%ZegIats@)0a zM3q5j5c&7XPM}9H&KK@!v5#z&w}<-py1r#1w$m~8^(~s5tbOcW8k68KI&Gd3(@Sqx zq*02SVSacR?v^K$;1TwB#ZAxen|JfR+1K$0)tAV->RWtHvuEKymCB1!p?ZLT( zXNRQX&@!|Qj&r_YGJo-j?oX#Zc#LDd0{g2bdw;c`S(o%VrT6PVcA1PJB>7eRLB5&3 zV`4dxU&^Gjh_bCbFrV<-n#*(<4nf4KR-I?UcGneQ9$ezwv7BY@@CPA9H7k+9rP;qG zhS$UX;O>dfw;PnAN(VTEhruenE94@(Kq|Z+tWsabA2e-2E@e-X53FqcIv1L?QEl~3 z9#5rp?up~2*n-63r2aEVKB{dfg32+ChsUF9a_Zcd=i{JNI1Wbf)8Lps6=um37$;a^ z67HaRecbor-^w499pZ)ONHf&yPs+Nkg)o>m9&_kktgStbt{;!$;k*aS42)7>o8-Nf zI0r^al>?2`vpXU~;62aa zZJm#88`ZPsJ?cQ~H)A~OkN2n3^6^;jMeBh{?ADmg?a6oX2Tc)PeQJ8cj>GkAM^UJ{`dL}^{L6(T<_;ze-r2XJug?I9EFeIgH!KJD_b6y zzT=&`yj}CT9=&!&NsK$Hx$ zuu*jJJEODnk3NMhk)YJ(Y+*1Hp6b0y%J2L6$0K#5Hq+nKMzoD^Uazd&d+l5KZVD$( z7Og8Etcp~@eZZ`R0h)) z-^d@N&WxRrESbx+Ih~(2x4QJ+Qqt~3Qblx>()HuTUDt=#V`G&ZR6`BUkBv8XP>=sC zf6z3(#L(r^dGA$(&v z{|tW+UBlchB^}W;KF{KvfvGuT%0j%pw&HsE;Z?v3bn`9Z*frhs3hX0pVdwJroo}30 znC)-5gL*AoMEp+L3hWL$xPBvz9xNOn^IUE1Cstq_?5iKEmzKsxYjvCKAG~dkkIRy; zj=^q{gtpLFWjJW?@_aMLMlU`Q+Mx6quvawi+`KrM!dq+DPsJw`cMV&pbh5xJ-8g9FJ2lvRHIvUUGKYMtOE_N5UiEL6Ct>Pp{FJeoPr!|*pw=$pZRFj{r-dS(zEvv&E_ zPu>@q<}&M$&ae@S`AO>qKJ3C_l?U+UFj#6GeA}jOURtkqxPwOI`m`KfpY}NYOzp@s zu?7L$z`^NP;3w*HrVW+1|Cnfpc=WW)-TRZ0Z+kDD&&_%0mI|ltt9sJJDx|GF4bZUo zqLpEF)ll7&SFKy#rMXc~+(G@#^rQM>)?1@PwGM8B_@J{snUwT( z&sHAX0{*D_wfI^7#-`DdeuX!$^GKm%+$QF+THXO~bHO?;CxKJ|WvK{eH|`b3%I- zeOAR5P2H;9(=ocJ3$xv0%U#SKcBVP4jVr-THnwVH(>G_^Iyb}l^8>D4V`&F!a;iJh-}C;>D#(C9;3s8i`<@X0?rXjR&Q+?m)|t6MeC zCf>tUygi@x9>1StPvTGE-n2T8*B98nC(ldlM}JJBgS$;~}~C2m^&HiT}}+-e8Y7k6IU4pDD}v(W1*|@A!)3F3Enf6x>>1XT8{Rw}N{zE-PYRW%&B0Si=_?!#Jo($C9Y4UA# zSp)9=9Xy{=e>x5=>M`_~-Q4nKp)2yb%ovv+L)1rH!S=0%zBHI1^`G9~`{BZ{cnJ9} z!q6LF{OiUfChpv1yC^$8Y<2uTbJ$>s#%9mNNPLFjN&$%&d`+Aw^ez7ef3QW2Jgp7W zn%1xL_MUoHa)V~|)=JkOjz5{(SL;^S>zk|}>bt&4)L}lthXo%D@!*@|?0zz{i~5_B zs%Q6!-o8?{&|$w%kInuC>p}R@DNvV_4p=8;Ar_W9##h|G!G+v}P6b-RU|MmuQk!uOUv;Hg<=-zQ%o=1aFd z8_!ai$<-|tir3hw8GG#S^FZ@-yn8rWqH##t_~i!}{}PGn6s1Q?Gaj~!Rj(V#P2sm4 z|2BV6{g!y2U|x*DLF@4>^q2E-zjaD+_Dq-H);Z*zOZqm_;|WIK^kYqfXJ=v^hkM6m z?#4Qiz~CFjkKh7NI9m>mvWg)}{=sh1!E)Cc3sw9wxR=-(z2-%&XHSs0jm#c@n?IO= zv-`}@KbgPM9nE9c`K(@Tg`*F+bTxYsx_ISKuQ6KxXuPrxOZ$4YV~)02YunuuC_v}5 z4KAYB1G*mBbUxd0>^1Gh+zxJxcA~J_y}^;)JBD|lsCu)7(c7HoRY(6f=?wlC`GeA6 zMbyw~uRG69r-z>D12L`ftU6nAE=aXbPMjYQK0!A(%Sj}*%jVMlX=>jnbI$G$rRCw^ zyLU<6!o@ulLAy+RN-Hy)$1CvVZ)c}NKSaaW-51T;)sgQ)BJ^nYTWDM+AMH&2@A3x~ z8}`s_FQ>{}kuYuDs&6{$jTo!WJlYxtvQuqr!9HH=M>nd%aMp-g!EQ6qJFjx+Ewoa* zX{~#1rDFYHYZ@z67Pr2ahvjLTayx-^9Gfzx8BwvjHBX`77JSY6mEuvh3}eZjVY z*6E1sC*c;|?&zn!No@{${rI??8pFarQvZ1nAiXZs8eq~>L-~0=YvP}=7JNW(J25O5J1|qT@7~8?= zOfIL#y0n-Fw}(X!uAs%Pz)F4ze=sif^4Illm-p+x`uGWNIHUqu)0aN+yub$OU*bbO zZ{wI zPU<}G$HZ!4$GbiD+H7Zha1#C|e~_O-Pxd;-X~5)E^N!O0a6v;A_fHk>mEl*{ z^Ogn)ZIl2LOpngY`=@jPUGN{sAN*g({O{EPK8U%vO6|M8dJt4aPPmsbw<(qPWdP~c$)_mJ&{eRA(#R1W3U8|RUW6P#@qvP&8a zmNU+Gmcu*K^4rv*@DJHYHP>kJH-CfY!g-2W$YL(FHrj`qy7tHOk6tt61bP!IF^a50 zhey*{-m2~(vdRuFu~PqH#lMX|NS#SPLU~m=3@0)x{bEy@^bO$&nBsV?GB(HC=xW|u zTpsN(_!%+mgonhz&=eC+e5%?7z6&;Hf0z5%Rl%W@-ni5c;}25zn(>tkuBuN<= zWXBU{LGAJ4M*SXl(D0<{IBk6mf6$Wef&3p7zRQpvxTC zWc`bnY+T?rj+-<25wDW^c3boxusXLLM^iL+ys!H7FR*16K>85ad2C79vH#;YM_;3z zKH<%8-1+n47xD*XZ&YjEB(En7*wu-Twpm* zi(5MC~ZK(O$H4aUzrRDiLf==#9l}f4}IVc{d_{6!3_6ooKQVXVBx+ zzQ@S>@dxk+DMy&ZrHR*<-OOj;8U7ORF?mVTXYvVWg+Bs#Kf1Ju^2iD<=5ywzJsAi4 zXg~YMW+l@q#~QoEV{%^B#+1oDHS>w{vTU?Pf6E=z>#yez%4W!U!8hc*=nF|7k^Tff z$h3dj;}0^Ypd)-(jL!Kz<{jB|+loy`9J2PlYH$xQrJEfNPh#C8U#5%p)jIlJ{6URz zGtPtPyJ4HxSg^rniE+x8L<*kJdjPj3v2lMJJ=@MNm6=96$&W}EJ z=ga!;?7Q|oJ?=(hoW}!m*It0H&@RFOpKUnMea`5Gp@~zLUZ8LJP5eRnlJ*H&S_lAyLxDhc$oVKw;e z#RYqDJ7~{hp!h!ipoihiX0f%=cLj4}ZR^gePOBTKtu*`}EkC-cbt{L76<*x}KZaN{ zy!FPp_?h5-=m?`RN-S8wK>n5BcoGkl_?F+rAJjAjHn{V!{~F^PjqG>}e~gt3IhvnYRfNf`H$0pjM;xM{ra4GP$XJz;7lneIFj`b?5HF4NhaN3^V#UJF% zGC=Ild@mX;&5>E4!2lB!}5yD6+oA82|ZtW?x;ws0C{Vt9vGBhqn(vg}v3D zy-K;aoL+8rwCuMlj*Dp5?fdwH!k~zYx=OSEmfkLV?A4e&`MbwN>Cg*1{#K|LXZ&`1 z=TLl~81J6z3jSE=x=NomeMV1|IHB$y;z2ssi!O&|&Sh@^uZaEHpYR8>MFMklOP5pz zKJOl!T7Cc-@KbzOo{=%!O6EYYNcT*vAbvcFAwPeZPp0@Q5W}?F}p%D`wz$+Uj8AW%Xvt)!HGSIPf<%D85vFne9(Q!(w=u*0QJE!FNg(4$oYnl>IaQU}E4~ zh+7!*7rRvP*W+qOT z+Q(>_SY3Q|d5gZ=+5~;<3)>+piqF;a2xoA8ZD)pOV(ac!g=Dn9vA2z4|8Q}_cA(~4 z1M6)Iz>|N%AME6oJ22P4vE67o^wmdtJq1f!-q(rky**f7(wQoG;4I3fL+5If7xoD% z2kihxC4C9A7xDak$gnw&3k&1@-LB4rJVoUwJFk4yP$H&L|g$L<-r7ij5vfS5!CpH23Td3a% z>jd94z6%n~2ai=F&_(b~w@hqjUMb&Ql}CS48z~;7eDQt$U@wR$NoMA*4UVRA82rQS z02yYU@XjZvTl2c_Plp9KEW{_C7W_IB==Vvc9(nqH{R@9^95}c6Ht~}(cah(?z#ua4 zb-2sDYjqqOl27uZG2rEoxCNR+_&(0yN+!?dKc1UMxUyZH3uUvV0T~O~A?QO57m|g+ zIiU{`XYdLC5v~`rwcEKruWo0%O=K(kX?FwXG`jJxqVNOLf5smi4Wf_b-0qq7CELU* z<2UXn8bz9tJWQ_m9rV1uqA|Vn>|&~$jc|}xotwU+a;pBRHWgP%e>ZJoY*BtVoZvX7 z7>=7+pj#lggRoS<2;iG!5`Ts(t z-J!>`ufHsA*WmqBDEZEVQR7^gC4MIU;4d9>3!>*+Bbh1{-B1A3ZwK1C;bKU zHSq_9MNN2T-j{{nCA+nmAGO|cnRQBQ6-jf+KfoW{TZ()N&ID?)K z4tzT@%Vq5FKM8+O`T5iitU2seG;edMTw3dx|1ia^nXB19u)o!1bOHvu7XX5kUQq|@7R&C(%6@r6JJ*d;6fueH!UKw%a#JBMWy}Bn?%GWRcf8?L6n!2H{Qt#v)pKD)z zG>Q=)Yqnp^p4d!NPMSDms~vWL6`GGZ4i4>>^s^o&ZW&n5XB_88^9Q}TlzP@U6%SQy zLK%RNQ~hak5Dg8siJps2SqSU^yT%DU%dvPd*ev?ehy-h!3FiRU$zzX{US*rteor^j za0uV{&5z;_l3$H)&ySb!@IG5beXaA1SPOB*@FHLEa=>KCJ6tK@Hee%m8qG3smf+vn zSK1?X0DlwKF!1~q-Dp};?}|g| zoXD%Fsaqe#Mjx5cIv4~gpthUMko^<)421MZ;y_NVa&`F@W-s2E&eWcg-hFOA~g z;}2@ygC|d4()jI9oStIgQ15N}zU=P}H)Zo0-9e)RnXjLl=`48F^x1}gn)YL(9#uRn zEzNVk`WO9A+>`&v8>C$g-c^L_6FV(qw?U+2K~y=x4+|Uqpwq@~9`5kFUGWCt!sdhx z4=$sM=1QxpdH;2pby{sAy*tk0jE{>WUf&Mxp>UA4a9MD+egnH4y0!H>SW`eAwY5@l zf4Ez4$ii>}9NJI(M?b%7`X8TFKL3nAs6JY6En>A*c3<3K*~A8-bxbK~_=A)wO&`YI zC;ni*gC3e|#;@k+5Y}wZ~D0>tTx@mI2Op{FB2ZJ&RY0Qg_!cE z(XE}T3?|FYuI?NTiPrOytS8RN_!q<VfC=>fL*uIS{q(y zYPzS+P^ONw@A%J2{6Y5c|0(|9v%Oe4naKR;G=j-pa;e4X=mTiTA4bT;lBwr{#1l9I7iO-8L~k<_gWZ z@ILVeGlOd9T@1_Y8O_-uwx}K9o6)CRJK6!>>FSgjT`bm!c||c^tD)>;zl@)@%ltW! zp9)ihf0dZI?xOEJ)7XY48S>DDH`V4j@#4S-|AarN7OB)eda8sIG z*0<4_dx_#A7t9qJ{CU_bmLjCwFbA`*g0v3^21qtG<7TE zYY5J1JKY+$!rR20*wgn8F$eLxy01w1pgh;o+dVpq$ha2w^&-7XH`1E6PtN{t^9P?1 z;u?w<3YNDnG@NyyyM$}qI+ch8k)PYW2Y=9<>3shFG*&P=ob5hi9N!)1G`X*G&*gPAjrOYO4y2BI z;4jLA@wfs3FdegzV^W%)H#DPSesymio-q-^%aZD+2peAchNN;RJP&nv^Gfkv(-hM z0HoTx3=W*d^%*`&vbsUvaald~Cb@8Na5-j+v0yqFvogh~{rCBUIz#)i{8l_W80J^E z>tbB9CRUfbI&<$RNMwqKTrHYvd;NXjpl1s2$~8B?q>MSOtHE; z&x66gp7Bz1T#VIw!)eDK+;nBf@L?ywuJg79z8{sj|Ki#!(%|Py zHh6=7A@APKC!6JEiM9lX<4e_cG&rxR17qW*%{d#$j!OC`;t$FWq)NEUkL1OR5@*RP zl4zYI+s^bN+0LSn3#2ZpzVH9pfl=lXn5DmjKS)3Lz$xDSi+umezVm10c>VnyJMp6( zygo$%_Roijcb8AELYsrMQ7?g7?tljBad1t234aid?MFG3pZ~TkK9v!-l7VT5H^%wK zr;H!Ko)vuV?}ZQNRp0SZmeje>iyi_=_bd5>#t;8fo_$E4zv2#hMKS+_ zIpf=ae};R?I-LUkN1f^kzmrQk(Ydu6%^%7i{15s4-)@5$*xiTh$6TJTgr1pmrtwwL ztDpbE_UpRE^Q#=}3n|Zr1w?Czp8snuVK|$+);&+_OnFyXIAXgd<%h4doL%YVs#@I8jo03rVd$I?+IWJ%|&E1lGT z$pz*>du+i4$*{%+_>XM(T0{O-?V<58U6(@9tFbL}&QiZJbkS1oC zyL6n?F7LWyaJDj7LAmKVXoTQ{{cZd~?H$eeM)-#=`Bj=W2nM>z1ZSdPEwsF?@1>iY zCgvbP%Gj!vFWLaknebVIr4UasC4I$m(`omacUxdK#3)Ob zOT?SX%dgyR-QZuee~CT#fjJAy{Uv=Fe^B;aGe6Rou$u|zub+a1$}ZC<)Q8ga`8jpJ zi&GBlzvyqa+}V~fvb(o#gaE>pYJrbPd$de@!G|=MUR`!c^laaF^Y1-=A%D=6LpC0R zxkQ5SD2Nef!P^82gs&#OSHq7rarVFk%JJ>J=4SGbVe7Qr4Jr=fx(4@1nyAu)jnl`3 zC&F1ZZTFcwsOLY3Kd4sld{XemW9mPb=p&kQz+9bM-@~NP$8C{MI}Aouo|F#BDRJ7D z_jaS)t6VSFRq}jF98c0B#SRP)W(=Rr&lo|6QDfpgM&6G_=RPA>k@0}cku^hKQ(hLHc+%)@2itHp5fLy^~}A+Tx!$T zQ@1Xh)x|hEx8M#|caJ`roNMs23S86~JTI`Rrv$wUvxl=UK2mEqEQ#v(#UC_wT=u8Y zTzzy6Ywk302Hko|I?K^q)Aw-VDLBqVOKg_F)uQ`lA06K;aY!RMI5{(wL*)VA1A7;5 ztwLX0cfoVPK);DUs7aSKTODAxXgOvqfVE$jU<=`pHY(P&nYYj#EosyDa4T?)o@jMO z!)GTztlT^O4}28v5_roBJ`>{$4%@o0b2A0-_3w#4sJ!{15NprWnifuQ; zY1=A~R^{DoyCfE0gZD;ntu>m6g|_B4xLK@$Pu7_&V+H$`4R?6m5Pk%93{N!^!S@7W zhcMe89Dk7hL`j0a`RoXG&iu4eOwMrmu;_=H<5h1EJ%u*6)Q$y@y;jh7cME;tY5QdS zN87HvXUr0iSBy(D*iGV0{i}(V{L^O5GcA zJA?D-Nd5u&0)7H;(8~0A)(7!|6ACB_cW}m-C@f~PaJDiG-q+E7e0A>oHs_o5ef&Z1 zd@+ro)M<-X+D@AC5UKoW>g*ARZg+n`lBn4l6#(rfz zx-6fucpy7wA`hv*8k>oR-gm?wRD19*nC&KF+hS=Jby)8>SJ>ma1NWJI`ZT$l#?H-M zaCu!VI+NAmkhn4XG4&uNAS`r1@cA5$_9qp zOkudm2Ap7SX!)~+t?$qHgI^VYka>swuFk4MilVhd{_0WLZNLc8*Jlqe`=B1Ph3>?+ zsp3|52A4YDZ|-^daN)S%o0!QL2cvx{xEWX>dv^@M9}KJmKIhN)gKcZL4rMoygWJIt zd|yh_S2p46I)V?;#Qw|tLGouAoe6_)l4;gF4aq*GJJPB4}xN0YC@DI3Zx2HQIhH%Hp z6;oju;w<_0tDV7sNqk;)<)!!QU-*N0AHuNk4_S6P>!-oC;3=U*z`Rav{YCzu1uxmu zyT%Ezo!`GhK6GC9cr}y@e;GN!IL^95j4?D`*-3$WP=^C>!qD#EREo)NGPWqR0(O1{ zAGH?R6-uc;;}3pW{6WL3rOvyiKlv<=$7(e0)xo$hMEC;J*$F&qLVq%CaiJ|J2mDcX z_IKMEP3HbqZ&y8E_oW2!1rvL;11^JB?9cdv-x`0A!d%j5%74bBKAxii*|EC)N^d*u zZ*B)-h@SV?>;-)ATv)|_CjKCODrvC6H@2xVyq_ChY;LuDgb&&7W;omD0=q#haoV>s z`$6jG;SbUla{>oyH0b)kYrU5mm-nZ8#jogmZ=JXJSaL4I4#~WS&qp)@7HMqNOlALg z{K50;-u{SXNn3oz&(U+{YQguUD8#trT-dp^6L1F?t#dlk_-XiqZ*269S@YU6@>1ns z^3SJl&s@tt{4e+$^Mu8`5X8R-8?n|TE`9c$x=g|U0DsWrL;3R#@#f{{|DJAg4N$=R zhJU;%v(Cv?I8<-mjPWg@JeuFl0;RHjq;HEq$d7*D?Ju7QQznyV#kV`K%4vQUPbVr6 ziht~Fh!f{&wY|0mZ+t{W(DQfV(^Qc@gKb<~D_(b12g#uI+QXT)~pJ~=lwTWUWE6-+spgjWJQMd^0^ZB0M(>41a?>#0X zUAiV6)YOTWvyVLJ<-3o+AHBEezP?LaiqDr`n>~uPsnWs4y;5C^uOlA?{dN-UygJ9e zEx)vc-sg20{f+un`IOI(r2C&=o97x^+&7<5UZidDE-Uv+*j624zxGy_@d@KO>)DQ5(JHico*v@8N%nJ;n2{)8BqCX}p^; zuf2~~Umx$If0{Fx>X&?*Iy8AunM_@%tY%KpPnzphBnks_tj~5zwyn{=Tr{<{C|>Zi!bF5@*A)Dou4z8 zUaL$h8+q{X9p(@ZdsCj#lc6-5Zt%9Mo;#IRq=8kXT8eHvfP{mIQ^j7AFdC!89ei^L ze|Y@CKkQqoOeCYQ*ILwhZctp!YN5?85j)+Sd>9*C#I=dfBtBc}RL8z8oCd5-TrW6c z-^U-+@~jSHuFvzExT==+@zj6+d~NI-t2_45Ja`(|2>6XOI-RSqLGQV6H^po+xD^}{ z=Vo{dyaq>k<2XNxKlqx1-UWHJjn%CNW1P9C)ngVL9QJpx5Ag;~d@>%rSYPdDyH&=| z94?h#Wg{M&utB(PYKt~}&bQ9%59SYQe0qCSoukl7RX1Y(ZjP(Fd(*^K>polJt&P$5 zBIcv87&v3kcE};Wl&zH{?^Va(f8iw@80>i9g2IB#dxh8jLHZ*8pvJ73OEq_@9K0?D zyLgp-j^@?)p-jwAnl#m@8hgpZsb?T5Gb#cTIDrZ+D*qbSkD=ykdOz+tNaX`(^mE;mkSNd&YXjbguB&KOJOv92cE9 zFiz(NhTBd(?Rplh8h&RVU;ppn4>Ff(pDm6#e$K^Um^g-xE4^TB)-AUzUKI7(#(ph( zDKRtSjn+677+msxXPsHzQ-@m>I^m{~4m18~ufEDlTlguL4W$TFP*dg%@|+LM;pe5)-f~B5_>Qs{vGA$92#~!xhwV`_F{5LHSmL3GZ#3*Zx+OfgD;oq zD-Pie`}D1~2s!CV>D%~&viTXDGwRC+nrqfM%`y1A5DOSxzscoVz9h#XtpmOCD!ZJw z6L78iF>Bblv@*Yhtj0-@?!Q zKK>wQAo#7msgL-oG4iizr3>VhW4DobF6gVK~kh6eIm^Wa}Is1M9gi zO^HL0D;e1;D-nRtq1q30}o|^Ms@Izr`Qi{zd+v>;YhBp>}u`^Wqju(>Ywd zK2CwOzdD{=%WZgGIk>{5$@E&E)oy9rP8cH<=C3vN-ja?moZ$`#Zv_6JKhitL*8NHE z)T$hJ-^m};7@u8da2n^#{fu$(Qr&oWe(iNj!&InvsGiX5?Ao{7W)z+KsPog)J{Q23 z!PHjZoD1;5&T10NN2;LHt7#5-%GPmxi8uS`8Z zna--YK~y>)_seqbD;0CBRYvm@L;pZnE!g0hShk2hJC#DbznO7%5)Py@`^(Q!d$`X@ zXK?n|#zr3X#kQBqwUyBPhd;pr-<3O$fq`Q`@qLoE=wILunlro6Gw?M*2Y2_Kg>Mpb zR-D+`@fy62=5!WoV_CRfozW%4I&-xf*cUT1u&5U6AY)-qeA7L9kn`MWN9s4{iRLBZ zWX!+c#ve3V*hya?Tg+2w>`$UTypepfS!rH6bSI5+^)j0shwNKYv)Q#<4O!GBdXlU) zslAUtwN4Tq_$(XjI=RC1l-+L*nM3iO9VYLJQTT2ALGr_VrS?yI=U4g61U;QPuWxw# zGhZ~#IvGv*i|KhIKfrGLB<5rJ*$FN7t41V@9?x(e0~>VW=s?Epy%kyyLO&0G@O|dx z%udKVt38?BKqAv`yS3{cSwGH~Wxc)5SiySTIt;sT2;2VQy65*ze@6bG;d%-8cw-;J zLDH*^@J_*=sr|uWALZF0Hp1nabk(1PKPbD&%YJeqIGlk!KCmc(tGOBc_QTeR6jHfl z9tm#$Chr8-yP0nnXhnY-{vg$>a@5OUvRuFDKk{54VS2=Tb-zq+@wWQVk-VPMN9jCzPi40pIx|ad zy7pL2ItMq=y0im74}TE*yul}QHZ@~j|Cy)w^i7F1_%FHse>r}xhWh{Q@&DW7N7TUo z?^i#fR{!&Nc4Zgk(mBJ!rL;J(X6U|rE`2Fu-!v}pP4?pG{jq+u%G1Wl@r}EcTC5!& zMO)!myjBWcyWv%GShu#f?rUd@kYxVfPYtlP?n2ENwU>XqtT=f6%mt@=U_7K$@tsy>EgE+5#KA z&)2Sml6JhcK0nG)cxtlyuE0FN(PqR_6y_m}%EZ8vO;xx~?U|ZOSC97^3_(04FNsI> zIT?*l)dAcYx1Qy5#K=SMNwK(0e6w&GyGPls&#mZoBYk3@)h(ipr%oeX@7JaCoAC(= zVyk)Cs|{~Wbzfbb2?s|#3$Nt2;1D{lut5Dz1}YmIHpRJvqXt%s?G*bi*pG99!y}tL zSkz}u)Q{v3;G2Bm?w^9PWR*! zkA2=~M#6o8i|Ur)qsFmwqjmThcmBj1q|DUI7sekn`#+5V*-6B$jfT;Y>tCHbzU%ZO z8(ax(PPic<^>t!E(*EVnBWdH4G4r@>hTExf_TfqH;8H2>Ao%hMo!Ae&`Hee&eiR06 zGtSEG^CKJ>l((s4V;(pyL#}iM?~cKfX7mC2BpQgVTLm4V;;`B(TJ(A9T;svJOgW6d zjfO(Z>iT^4eCrV7PW?U$#t&v7&Y$Ytk2YW)tSz6I;JtrtE?4Nvfi3rmuSOq7AB;Sy z|5MlPju-Fmd-#L0VS0W;_>34WFEqKD^#E-S_PQf}s5qQ{xI@@W4QDgnT*pVFUM1^Z z-BPDjv-YibKjeaXay-+lkoI*feXHN)pWH(S`&h_PnI1Q=~vnzz!2j8|?>KN0IC$2*R* z*Hp%$&4OS$($QRyz!1*hIEm@qxfgHFBI~r_LMZm&x5OXxwCya)0S->p8y#a|Ic?^L z+@e;SpOSI>dg8Ttt=0zDDcj z#cTWo;N$&XYVYgda>V&d^dVvJxrVenJ$#Yx$`^t6Sqsqn-MYjM8%Eo{Wer=}%I+=W zsU>6DtlP9Z{&C3vnUAdC=wztkFyb!T28jpf^|HuZo}P)hWYOM$1?@Gy9Knusf+9 zTd`Jng-s!!TU?LVaV?*gw(MZqYFv|#%NcQ8!-4EX(1sLGl{k3fu({_BxEuNn-^3qO zYzy|X?Eh@`cg#uBenek^F@YX0z7nTead_BhO+a>j+pg4zO-<~uwrf9}Eqx-wnsTO3 zoY8`vq$ja*0&USvZ!+5VBcs!I@dxQo_+w^+gQ+DBB;~-@O8#K@?X2RnbJ(g@v_KLN zyl{GqBe}wgc+dM0kP~&jLw8hh_QVywk7l)Sv^q?>%WB@TCpYm21K-CV6iyfQ5w{Pm zU_V$K7@@;{%IrDg&27DOsuwT!`7JhKeKz`5U}dZ;_xaO4h1?9BNR3Z;wx?Afk{kq= zT8G5_)c90e8!S+)7GwGd-?v z7P)ge5M-}WxB30hA@Ph|QUTWdU4iic&bxqY5Mf?QEFN~CNjY1O)w6-7T;<#14{per z@zpjy%J|$SebL@DG^)0`qo|ecl&_g_E!BzcqFH-hL!+Bw?}w|}I9offhLb?{!oKox z%M6o`Y@mC}2V=ohV0z03uHoEZ8~ii=;8(>Tw4@!TZHdueVZYd=lX1i9YVT`&a((z0 zbIYeVm0ZEpHnF?mt3Yh+6}+#)dD~paBE4*37)XUTyJLL2PsP^x?tLSz(eH{sXwFQ{ z*SSjNF&94U=B%ar=s$T{`-Q_O)~Ve5+h+Oz*ZGRJU0isY3!F1wl-KYF^VRLL-SQ8| z?Ok-($#)lj&L8}u_=E59#-7BY4e--2zQ^WFW&G!Z#Kckgy1rRF&w9sDSS#;|H5!&d zry~NJNI8pVcF??A(q>?sYmdW6;_TfFUZ^=w>HYc_{@@qIAAI#I&b-F=oiVRWYcAo^ zQoC!c9k{~8PAooY30!wcDYRL6JXQLEm;J>YR>*sI`=Lb)E9=W`m*5Bk>Sa4Dpc z=fF063S6Vd{Wke${K0RFKWO?zFj*eQDz2cK^>YhegX}zer?o$1%F!X>Yh`sA`wz}} zj|i6dkAYbb{$TiL;SXA`m{8L>ww%{E5QkGfmYl01y2luKY?|>$W)qFA0 zJU7OS4py^ZDFcJ7-rcaS?of6T*VC}b~@aN&z5)%|~tu;-dt>*D(=#%tC;xGL? z{6T;E5QR)DJ}qT%%XydeBVYP{y}|n(i~c~Lq8vp9fb{la)C22`czz-3tYw*I^=`mJ+GS_dkocsKJ>qE9h4l( zuGD-lb>l>yaQ~>2k9P6QSf}1{h&7W>sZ%hU*JS6qJ$D+@Gc{PU@C4g(j~CDS7k5w| z|5pB>rN-fxor@0MdU)6FQ=OS$jLLya)2vpqd+Al!lN7;j%n5i+jYq+5;1Pi@s9b#F zJCYlp-uF0;;(`f&L;gScnQwwuQFl)9sK$aTmKE+XXy#wwA5)yZC;OWrUf-Ar^l-z{W zVq*-2`8dPeX7s^7#2>Vnhs1`|y2E~!=aRP%j0~)gJeHwNR#k3Y(TmFCI&!Z45zN(V zcE=>;d$xrh&=YyTAaL}Y84y` z?w#QOUGUmmbC*lTf*@C+9^4ctV&0mSpU3>_419m!CmER_Q#4(>J-hoX9})n46eOXywzBx$DxvMWvpaY*e;+KLF(vi9vXy1?NUV>n3zbWv^dO~GP)Z#yz4n!*Cvo={ zdBznUhB$Q(^Hk9!Kj%IVuDNILvY|E*+|@mpf^e$7?qOVVb*h`s3SUpSV&K>OE_f&X z!0+D~ulL#ix&C12OV5LSG~HsAID{Ma&}>|KGp9EPpuMkd+tutSaT>{Ez4_dw!O~K} zakCxiR$05c_=EN>o4*SWQg~lD9LbpqY-j<0kk9s?;SXYetR0Ei(+ggC+~I+EJxx^W=u)1%eLv_pT;*` z&B7l%sXUfuk99CuQA07)tZNP)p~P`9cpbpH|3UsB`>O=|@llWEff}L2F!(Vr%-qx| zCLfzsW0%w0Ido)gSG)cEX|mRBvVmzQi7kOwi*5LhX&oj7K&oiTXa@tnmP5gnQm!Xs-iVYwXIvOQ&e^`y;jbLR~7nK zd2(=#^YZb}@dxD`MXnV~tFR7p$$3QO|By#sgCVQ;%bGzbTB>sCZ&^Nqpxf~5N#NM` z)3MkpWsZsBwCcLYS~6?L9=;tP!5?g-_mfIuUn`PpG#mSNfA_k7d|cCIEq=EblgB>2 zbq(hTPxMH=zLPUdpB`iyppTRIW^>`3M012QFZEtRe-V3!%x`stRa^hde+N0Ah~I^n z-E`&ByX%{qdwTi-b#4WV+6UrqcedT}O7RCP+vQMSF5nN^qoce26#k&DAYs$ zZ~_n90RG_PR7@8_{$Mg=pR`3gUa?U2u4FT;upIamjUSIY~z7Y=z2 z&I|Ct>tt8FT$1@Kc7C5+OCQ4@L=OG11^i31=M&sXF7~rSvm^O(1yGaRx%)yaFh-fG z9rNPHb0>a}?{eqU<8$f{B8N!{KpKk>j_^`l6uhK7yPp1t*bt zuAKWE{-CCvcliqKWPU}CV2=uF>rr3%9^!5~K*_&6+B?JptgksdSe9l1`H^=B=~5tFBG%wEd4&;TtaSAMgJ0i*KKo z+-ib-94>;Z$=am4{EvV0vwreRj$iutJ9^;PxcWPK^?(1)U;6mp`|f}FO@-U=i($XZ z-d3G86c!@q6mVaOO<)hM>BNt`iWU9POU&g=Q@I?rAHyG{JzT_7NZf1qRC3hvEU8;{ z&RK#_$(l$0X&!f7DrM{j__|_4R$g*jb%}}p1pc739o#_LDlw9l#JEU|Po{`2Bwi#D z=l$`rSUTn_#E8z5R5`%+69Wsh#V?QF>ofR+>g=n=lbpvn`%y1PXMdQl){WIFTYGHb z&q=$bPTyeXgA2yF!A9ULeh7b%JeJDQAUUwWaE0d}AUCHqCSP+i)lfhi4J~qe0CdoAZ+5oWg-rdCICO&&)nI z*~tg4j2;eyAA4{Jz_` z(tdJgtK8CM_`(^m9f`RZoO@4ay&~ss5PO{phIiYw;G&XGPjLvT1qXh_;dJHN=@xlY z>#mV+{RIACtUFIi?!3yWkh_#8ZOiGu@}75U8MXdc=_Jeppkk&7yoy)O+nFc1n1!_Jdoo@aZ+4#x&=6cYb7@VIhl5Ug`DfDEMYm<%baLnYgxP=QxIv zl4-KO^D|3$UYl-t1Mh?3Cwy6j4NHz!#S>mVW0^Mi;4Wh(?AA-$-*11t{$LOn9Gka(Q%wEI)oN|_Qb}UjavgiU+7bgG7Y35h7MS z!{G(bJ{4+uv3glAmLs_RMrDw0Ox|Nk;yjgZ`VB9Aab8)k_-aVa0^y*$#Qv;0BfH&b zFJDEYzguTku9=$Oi)s9#EqsiBkw3WGv>djePU9bJ!Q<`{%8> zTfA~5qc(LCV9UhQnCHA5pDzbP)+spyvVI7EUg2oueN|jLt)FTn*V*H#k?8mO&Fz!C zqC;#tzRIo^hwd)9=`u&~S>A(R{MYz{Kdt_tTE|(-CI5<;iugNaKE?-1?hL`=3Y=@v znZ$~79(=X&(+*QlJFvlNmw%N%_!H|73dSw#h?ojD@uV^lmo4)8@IH0$bTt>WW2M@~ zVP66cbx(!-m6q})|26*LDi8Jv(~kwY+0!9C?E^3eZWGNq|laL(u2rfah|Y~_d_ zEBQllgDY+nbFTmg|0{p+=hPnzbs+0`f>>%brWil!j4or0`V+GMvd~!~@v>X2Z8jL~ zCI!Cv1OvDs0Dk`eoj>@~>JKVDnyiPiUlJQoHX^#`3;iX{bTuI-U-nh4FPLO;+YHp4Qe=y8d7Sq|k2xkzC6MmPh zE5bWdYbSo-&*u-SHcS2n=HS4e9s;|L#jr(wpNZMCM1()cnqnahI`I9&-8T;KAL0M1 zwEM& zg6vsnyL?W*;`jG|d;h<^_mlX8v_M@VhhREixX#!68ay=mo0)gK~#5z585hCQ5AkMfi{4CG_nfR3*BUJ+g=v-v1VV@XdQv z?fB0&(J;DIH`Ha?aLb20fqkOgyyeFA^*%MNHNdJ$YI>{E21Y)8$ltRMJz zSUVoOt~OHRjQ4Bx%;97BgJRQig=6SzT*#Q>O~y^R&6z~i0fP9I*qwVx^VBoJJICa? z%#nZWQ}}~Q3Qf4QJQF$Sdyx$pux%ag>F{1L_v_W;S?rL{%S=&|FNHrP1Fi!`_$mCs zw?4k+U1ckrJMc?X>V5a_j%mB{F_QbPLrxz9eu!X&lG~JgqaVT_RAXIg0gJ)P8qFMw zGUC?4dN~EK56=d}sixsH!a;2;z-pO$tBfgXm3g-|`Du|0&Y-fbGMU}2DsTRvDZC4KFkk$^vHAYJA8-h%k?D5_n`~!SKG#nP?NJK% z2r|zUH0!QcU&WLGchur@^i_3uaAv-;Us<2um*j_16V^ffp2`V%cWv?mtDJZI9(6^f z-lpO+&fwE^Cb{F*xDOT}J$aC`JICJc;)&jbKRB3!tE)MHT3P=Te~|qRrV;M# zGO^6)KwVMhqQUyD17ZM#mucg}#?BWHms&Vg6PyOjrbI24T0_+zgg@9xlD{i=bk6ys zt@wk5nfqtlLHXIw;}0@79hMRsgYPh-&D%$KPQqIr&&Z=lzrwvPs=Sa5-yDoKPY)Xo z=3q38u?0S4hw*ftJKny#&W=06A7q@$zDo@5pW_c&vPY6XnMlbdi8_uszj%XV{sPBn!i^VsjO@l6-1fc^EYI%xPL9z$Tsttq$=2Ve$8rl+;h5=$Yt-6;>HTB%2Wh*S zb1|-VeZ^WK`4bsKV1v^3&8joMZ;RCVAeXM^N&QXNu_qjAu_xBZhsa{y1sB9eN3A=_ zEh>3L?X$^y)<4A`#KzU$RYxy^Rgn;iw#LZan#-RyqemlAzRcPy*6kPdM~jwcp7CS# z+A-loFkgHOe^BkKLe9;zPhL?UPWj-L65e*BUew!-V`tFHQkSq<_x(yY7{-WMa@Ngj zry46~vLC}Al<~nHq%pPNGRpkQUPIy+sP|f%mS2m`sa3r!uZkycXR#OH0{?t@7B|MB zH{O2?e^B;=*!kpHUb*aY7yTG2^?t@tWHki~N#eAAr znc>Iq2i4w6&Pm*s@Z0L~>?x6-Eh`o^&$FYHSDkf=UEw`bfcv!Q5QSOKCUn^sm$mfA z@CVho6+aYfx!}*NhcXZ6X(P*`(vuYKgC{V?~r zw-ek09On*(;6NDy9ORuS@CVgC>g|8R4#-%m>Dfw(0ZPwI*We^8Apjo7m-wFbo( zfp10Tukd>NS-xI=@}t zl3cG^AxS(bF&Vqbquf^KMtPUBRkd!dKZZYeP>m7{1pHMNIP5{iTBtf2taDqnXTKK{ zi_yXw&f7cllCLJ6VpaHG#Pxp&e^AzFiA!WJsALFrAZxY?aL_F+Ry`jUj+waP)_TD+ z%F1q+Td*q06Zyt({^k0EVN6nj^El2Q(T(gqGD&=eS|{0lUN%`U9e6EfWfsPa!#66L zf~nxoA`bpT_=8`2CgLk&ddMYIu@?PiCv|sE#4i}aLm=Pj6@O!um+orJ#8CdM4D)H&!IS?%ERP_fp@xKo}lVV?{{_+QA8xB}^vq~Nx+Z*zLm3%<` zLB`YDE)>p5pjBHt^{Q}g;LLjDwe)j>%dUNM1#Tl8E0fw}-l%(BS&c-+_!R!2jvY&G z`S%=4&fvlps9*fm`9$2Yovb-ZBblY3Iu zgIL2EtcPIY;0e3YxOKU1*2WMVZFnv8QkNaIus(%9XsJabjh*DG2@OuU)bbL35qO8) zwVD_A3a>d47=y00SUQeD5=GB3aQ@;4P7vGsG5kU7qipE--S_L~i5hk|Z3P4AcC6Ga zml;jV`*j0MR-ew6$ChiK;eV9o39Uf5&UmGJ_l+!XLyYxMJhQvEdKaGT(yNRy+4Xs?bQR z^ySK~Emr!-H@x|z)hQ?}4bJAxi|3AcAHLmZ@CTWzl<#K!^duK&HyFSVCZ9RA?y6>M zmP{P1f_Z!9J>xnY-!tsB_SEBa7I2+~zqx=L{2BZ~$&qo;v|&2gaAocG*$4W}wSH>k zx_)E*+*#R?mG_EAt9NtZ)8@;>WiS`qx8Sy$&*2XWPgrs2^xSb-p0$&GcjuF@xk?RN zv*MffxH~)QkHRz1&*BfN_!!B>iNTX+n|VuX z&DzKH$~)E*nL$65c=fJc`BAV=yWF#j(CN~7>+(4j2k|ldK^1c>`zv+s(DX7jVBvWw zC6%?3$Q5HQpjPeBythRT&E4lOPrtTtN_~1~?gDWchLA3Gc-wiLwzWjZUIahE&=OuYjMV83$2eEs1Sa^`9 zxgh&6Zt-uit_W|c&3qOO{-DlO<{6g_{%m!A*DP`YsdMl&Tz90dn)UhoLG~f=n={XF zO?ysP#sYCpy+iOC`jefS=Guz#69C@oTb9rB9Q%lCh> zw~FCI$@*r0$5>(y2{t0@NtrPv`!R|7R{OE{n)$VMsz=$U{r>&`_TEq84_flwzq^E+ zBYTtg{v;SCsOPHkPiWW&d@1%}{GQSWlJK6ci=9Xb}O-Lg?p#EUkpE8bq(}O%0nUIY)!9ZLW{mb)g3|W+&prJ;>dVCCjP~`a2v1Mr< zwNZ`QvRsXCdyD0&ao()cuR*LxZo>Qr@dp{hl6x?`L^0oO-yu~iRuD5PjIv`h(6I{|KB+@F-8}DEbwtS2QKR=n$N_zIu+1t!AUynqE_%!yjz37OQhP zzMgu5^l@Nn!eIta8%WMt;VJIYlIM;wHhj4QFIPDfF2#M`HPX}1;SUO@nf%Szgw%#8 zoiik{1OF4Qm)~SgVm>l<)WI^T(HZN^TF=IMb!!nHE;Z05pTi$i`xVxZg6wm&_N+jT zk}f)kbvzF)q-T!<^O&Zt<>0zC6SW$(y%feNK3(!9ehPn3o$qA65hla-D9wv;YV@T|FsPFpPBd+r{22bA_z+n;lG;*xZhd%yCVpI_>kjNp=2-5Si^0L! zJ@eOI@>-cTbaFA0r!l?m&ITR%y-(o};#1dR=L?cVzgRcp8XbZ>DZ)}Ajr_VDS= z5|4f^-%8Fbm0uEm*TZ#+M~4D_pHhEN)X-J_+3j>P zaMns^3lgNl1(~PL%)790K7~KXnL={6&zDc?VH&3IOMX$-4ic^i22CE}wzoU#QWLqB zD70^bbSf}6_!WsS*{6vo`;7X7g4cvTHFk@QRfb%&x^Fj`vvPfE2X{OpX9Y$w=i9md z_@QUk-CXorHzxooT(7i%$S9(A|qQKK{2 z+`VxBu*OttI^4>*Lo$nXc52gs#%DNVJ?)o>5zIU@B3S?}DzC=EAQ+AOjh!#RuNAo@Tvk>C%qcEB4H zAD8&3c_x>_)g*>P-jBxhOODUd(=UDe(#PM?1HZ=A-_fgo@|~8Po4CZUD46jZL!h3K z!h!>%{i2Q>A1K81gDfPHE0{-1OVksI&*2YBy9HMZac|Z(Ip->2B<>>S54FAInbEC! zyi%(#C8yWP@$TS*l6Q@G_YdI@T48;OaIF@fwct5)ez{OgP`lmTjnbpku6WEB9ek#6 zy~cv22{tkaIWC{VA5^d=7@_zL)EMKrmX|Tqnw5{#g)4v^ke3fEZFj>Txg8fMSFY(!@;IhFNlUs7-{kPlif(=n?Y%qt9DEX=+Z)3CO zdwyPWJ>g5^Z;uPUol4)_W}%l{sqrm?Mmqf4MH~z<3xadXUP){spGO}GS5oAf_5xdcKfeEu0XChe78o^Gdo~|Qok?Nqy|%-x~U60 z>%*7ofmhw}Q-4VPL2v^WHf&tYx9L@82R~)q#5TYNPuRs39%M}X*5<8$^l-*8hwX{0 z&P$Cxd~zScAG9Q1nu|w-iQoGJ*WcM-Au;U3WDZ=FRGn1jKUR{=CT9p=ZE|v}F248D z$M6TGPLY^7Yj=A_N@l-F|2M(@je$RSRRVi}t(a{%Mz!*JdT^;__wv&XB|9-RpTi#% z+^GOMgYbhNUb=ML=E&#u8T>)U6WC25 zIY~c@%ukXd6**$88?lz%y*?eU5j*6LPj`??mFk5ja~1g19bEc6@q-q5c0YtaD12@9 z>XmrEL5}tfa!_|rav8{cm7^~8F($b_NCRGP)ZF5I>lpA^=0S;Eso`%Q^C( z53U$>8YFf)>-yG(`8ADPg=sdH>87sKiwhiZL53AKc)};}2c^yGW<6(|=(;7b}+ zxPxI~&ja;sw}KWN!?p)twTrVnha2@7{K2nrWr;3SjQyK87>+fLt!re{CYd?4$T$U- z1XFW_tA+*PQd<=-lbvy@Rnn(; zF?F8oia)FVAa)z_1C+mkF=h4Nz8}dT8T6!3V@y_d!(- z@|4sl_)z#c^?ZK@TXY0qd}F?QzH{O-pG%%) z_1UoRc$;h8ov&yUHVdEQdya>CQ~smhMIRdVUl~eruHee(=)xOn{qP)tjmf z>K9eJd8WSL`?_PY-hI#akocoRh5^MtVzH!A(gYpfC^h0hs1Q$n3MerzjKiPMgO8l*%bV{^#`R-X-rJF-ke7E zRE#@f_BCpnJWldMUG^#pGZ!5QPEwGu6+Tyb{IZqrALS$8zkbX7|MT8Y;txt!^4-{o z_FMZHUx#|hGs`pD56_M^3ofFjGwFYM&hLaa!|#0nf3QrB4B99Cu445?D2s8#SV14u zu#}vZZ#)00DJOG}(wWrl+PJZf3wk2*Y*9W#Dn!yiNr zIOS?wXM<2x>WPHEnfFm!%N9?|@ zJ$KqpltfO~_Gxo{lK&CE^_ZG)C;Tbq#0RfZIGA8l>V652P2$SItmLNV)Neln!+LRl z{Y)OEjvPKF?}Q&5%+9ZZ6LEX5rqs1jKB&Pf?-%|4VH3+_D$AU1$^1?O<)0;O{dTc0 zc-y&}L!~~8mhLYu1J@fh+<_VQceLB{@$lhO_H4dO>Y$}MU$hj zzYJ<|*GzAyr-)4DJ2j1N)CU0rAHQwcrtRQZKde7kZGctO7VvnN&sZ)nND?*(2F_s7 zz2`?^+?m|BXg?e6pgN~WKekjTM~2tjv;G<%mL=TXb`6$*gx$aF*f{? zx3d{r%v0dy6X6ebeDeIXhJn4=xTR8kNm8$jo~d-MmDD~rcu}|IcIiP;%?x@eKcoJ_h5;pS0NBOO^PPdJ5jiZ8%evHY?Oyja*c(5pKj<0hiL-Nz$1a{g zztT5T-&oYagC(EgWVW1_d(GA8<@d^mjd`w|r?{C@6~}Al%C+fK8ZH;(OwG5tR>)K) zZ5cb*Y*q2)Uw!w`z8+3swd5T%2Icee`0{I2{`Nx64XpK#LO{-8aD-ue2?o7_&xZb_ zR-|KrD@=b>e~?^MOLWju?e@?3?iHRQI$jCRV$g2gV!P+WPQw2gFX!kjOFWBT*}io4 zP2t5DU|fkl5;1%2?HNaKYERgx*}HAsg>I48&OMFDADrwYS8ed-g>RoHv4bW4(-=7j z!FJ(D1$k=c)MoCUoVKO7@W58Ih4Gz7Hn?Hb!u)m*<9s%}L;PAkzrh~(-J#TDtf{dI zMl|c+M@N~1mRb1MLHyzjoK|^KZ@j$jz}dxel{2~bq_!IVHhfpP!ni+r4U9D0Y;;nE z1C`R;o!3u|`qUkb-WN1Yfil)e}nc0lgF-P95?f`Un&i-kA?K()th9N&v|K_@-}1mlY{$iJ8Cr^>Rxo7 zBe{8PEwb0){COOvj{AOVuxrgboqc|dUyuGk2VT&cdnIap2K=pXF2Qcn*kP+LxhJ=4 zb42Rt7*kj6;0!!$gG+o1b&dR+=(5V3CNU*umpQ8aC>%D4vX!;hjmLAB%U5gDbznF< zY7f$O{G`}>Q0gsJ{lOr%%TJC31cjep0IiJqvT->`2K9z;Wg2%qKX>XE`P>#^f&GaDc z?xc3^OU4!Qyn62L8XMhb)#YPmlQ*^8nAThOT^zqV^+f$Ma^zVn!97(z!nl3k++L2#h`BX~UfE@ED(XHatt;5!qIr zeYWu5NgM^RUuL>m&NjYxDhzI3jxQH~n0yx-fsB?L9ZHK#r7rtqbVAL`yt{Cha6$vGxEY9$L7YCJcduDi#kTKTb6US_i!;<8?R zSToL#Ys_NpWKWi~F4a

sAsSy>wpQsKfiH#t&|}wslIi!%N7MJ|6Wb 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 Y@}IBef4!gR{Wn|tpKi7M-`Dd00N}u82><{9 literal 0 HcmV?d00001 diff --git a/Lab7/src/kernel/boot.S b/Lab7/src/kernel/boot.S index 81fb65548..5ff99aa48 100644 --- a/Lab7/src/kernel/boot.S +++ b/Lab7/src/kernel/boot.S @@ -7,6 +7,7 @@ _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: 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/exception.c b/Lab7/src/kernel/exception.c index 6f10a5784..6c8b009fb 100644 --- a/Lab7/src/kernel/exception.c +++ b/Lab7/src/kernel/exception.c @@ -9,6 +9,7 @@ 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 @@ -122,6 +123,7 @@ void exc_handler(unsigned long type, unsigned long esr, unsigned long elr, unsig uart_hex(far); uart_send_string("\n"); + proc_hang(); return; } @@ -264,10 +266,70 @@ void el0_to_el1_sync_handler(unsigned long trapframe_addr) } 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); + 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 index 85e57b892..dc764b380 100644 --- a/Lab7/src/kernel/kernel.c +++ b/Lab7/src/kernel/kernel.c @@ -4,8 +4,10 @@ #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; @@ -23,5 +25,10 @@ void kernel_main(void) // 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/mbox.c b/Lab7/src/kernel/mbox.c index f6d91caa7..ca0729b2c 100644 --- a/Lab7/src/kernel/mbox.c +++ b/Lab7/src/kernel/mbox.c @@ -1,6 +1,7 @@ #include "peripherals/mbox_call.h" #include "mbox_call.h" #include "mini_uart.h" +#include "stdlib.h" void mbox_main() { @@ -60,4 +61,73 @@ void mbox_main() { 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/read_cpio.c b/Lab7/src/kernel/read_cpio.c index 0d61c3a17..019fb866d 100644 --- a/Lab7/src/kernel/read_cpio.c +++ b/Lab7/src/kernel/read_cpio.c @@ -1,7 +1,11 @@ #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 { @@ -158,4 +162,173 @@ int load_userprogram(const char *filename, char *userDest) 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/reserved_mem.c b/Lab7/src/kernel/reserved_mem.c index 74a60af3e..d4a5649c8 100644 --- a/Lab7/src/kernel/reserved_mem.c +++ b/Lab7/src/kernel/reserved_mem.c @@ -39,8 +39,8 @@ void memory_init() 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(0x1000000, 0x100ffff, "Printf Buffer"); + memory_reserve(0x8000000, 0x8020000, "Initramfs"); memory_reserve(0x15000000, 0x17000000, "User Program"); memory_reserve(0x200000, 0x250000, "svc"); diff --git a/Lab7/src/kernel/shell.c b/Lab7/src/kernel/shell.c index 3a8d4f13f..7a068bd31 100644 --- a/Lab7/src/kernel/shell.c +++ b/Lab7/src/kernel/shell.c @@ -8,6 +8,7 @@ #include "dynamic_alloc.h" #include "test.h" #include "thread.h" +#include "vfs.h" extern void *_dtb_ptr; extern char *cpioDestGlobal; @@ -37,6 +38,7 @@ void shell_main(char *command) 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")) { @@ -49,9 +51,16 @@ void shell_main(char *command) while (1) ; } - else if (!strcmp(command, "ls")) + else if (!memcmp(command, "ls", 2)) { - read_cpio((char *)cpioDestGlobal); + 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)) { @@ -158,6 +167,21 @@ void shell_main(char *command) 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; } diff --git a/Lab7/src/kernel/syscall.c b/Lab7/src/kernel/syscall.c index 1a867f807..a2850ef9c 100644 --- a/Lab7/src/kernel/syscall.c +++ b/Lab7/src/kernel/syscall.c @@ -7,6 +7,7 @@ #include "peripherals/mbox_call.h" #include "peripherals/gpio.h" #include "my_signal.h" +#include "vfs.h" extern task_struct *get_current(); extern void set_switch_timer(); @@ -156,4 +157,80 @@ void signal(int SIGNAL, void (*handler)()) 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 index 0f97e5b8f..36c9909a8 100644 --- a/Lab7/src/kernel/test.c +++ b/Lab7/src/kernel/test.c @@ -77,6 +77,31 @@ void load_usrpgm_in_umode() 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" diff --git a/Lab7/src/kernel/thread.c b/Lab7/src/kernel/thread.c index c88ac7b82..f3f40d788 100644 --- a/Lab7/src/kernel/thread.c +++ b/Lab7/src/kernel/thread.c @@ -5,6 +5,7 @@ #include "reserve_mem.h" #include "list.h" #include "syscall.h" +#include "vfs.h" extern task_struct *get_current(); extern void set_switch_timer(); @@ -19,6 +20,10 @@ 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(); @@ -96,6 +101,15 @@ thread_info *thread_create(func_ptr fp) 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; @@ -254,4 +268,14 @@ void debug_task_zombieq() 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/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/lib/mini_uart.c b/Lab7/src/lib/mini_uart.c index e780073bf..d1896d604 100644 --- a/Lab7/src/lib/mini_uart.c +++ b/Lab7/src/lib/mini_uart.c @@ -40,6 +40,28 @@ char uart_recv(void) 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) diff --git a/Lab7/src/lib/string.c b/Lab7/src/lib/string.c index be47302ee..d29bfeb4a 100644 --- a/Lab7/src/lib/string.c +++ b/Lab7/src/lib/string.c @@ -77,3 +77,37 @@ int atoi(char *str) // 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/userprogram.img b/Lab7/userprogram.img index 494ba66074dfecf8d5401beca4aa079174f6f190..ada965550ced9a4ea364a9d02264ae761852641a 100755 GIT binary patch delta 952 zcmZ9KUr1AN6vxkRcb29L^}0JZXY;C1>_tR#HqI^{O-L*`1JdobH2aN`SUv)Qu_F- ziX8G|TFiM{NPg0<*283-5}~H(3xSb&b$8YupKydGt^C=f7O81i<4WAS z(OL9uclcu2{)OlBTyQztKO4((5Z%s)3_UQEszXAW}Qg*I}V`fQ$o5DMVR!D0e~vIt{02oN^|b)%nvA}hs83J`vQf$@G3&xmY_5_lvt z-Fd^`Y3P}+9q%H03wmpESuy%=LB}QbC{KUn)dezOcWge5XulT~ z87#Uj8F~ggO6ZAFfi%){rPZ}7{)|OcA8G{{Y*Ay4%h2>71?h>ca&N&aYma27-8pAJP9f)(w2meRc6r09$FT4|7h#fZFlEy8c#*VLxXmKtk9eG zN?{effIv_@QIlCoFu1rvC+%hR=z&~sLQJ|3H|%4P<>YYmh5 zICzf*0eS})y?r?L00&CS9IBFf%n%(9^+=B~)>7EqcTH6TF7&enWSXq1+Oqxc^hN9- zv7Ihc>^CwSoKVbT+^YR`Nj!<);Q~JV2_T?cjYuvK!f%Ap0Y_vXME@Wp%~{=SyLY;p zaKk?s8~z`dhs+Lww>c7@LKr5|mh`0thF%LB7bbkNP(R#r^|DkIs+5|rco&$$cd_UZ o6Xef*te3PLyf>rgFa?+IJG}+J%E5{ggU&->rR`1|?RMt;1>IX_SO5S3 delta 660 zcmZvZL1@!p6vzLs>9Erd*3~slTWu2OEQ&~(xQYi;2R#Xc;At22=I}BZ$Z&@S(M!E_ zd_z4ch%mR)WT51rwxS@22a5_GgbE5f3F{CHSmOJV2t&-}d;jnK{{Q!qbWd$o*7X5; zyI~AG9j2!EJbH+32+ABwT_Y^F{9PxIXms>%E4{0tnkfzMfw!$-4X{o+!14EDD>Ff_ z#6fk6HpHwtP4|VXPEtv1scK#42!dlfBTfe#QIL^b@8@&gjx7=H+Fd2RmWl4O5ijpj zTpQAhaQqPKbh6Qz;2fDLnMA}p(TZ`_7o}$~@RWOq0PGG!egn&G!?7_G>R+s-L5BV0 zTC8h(X-2%uj+OPAcf|Ma1S>@GUzs30v~lKk2i=w47uNo?@-YoG=S^X1lQb@7wEWSz zq+6A1M85VvzGpjqOd|SUS#9-q+`owunY%07pu8ET@kNyMVb}ga~}q?HIFRg aRy8YL=AVd#eOAdSV3c`mMyIU*_xu4NLfKaU From 043b21cf5d8423f5b6daa4c16ccbc0d5236c58f5 Mon Sep 17 00:00:00 2001 From: JasonJC669 Date: Tue, 13 Jun 2023 16:47:54 +0800 Subject: [PATCH 20/22] Init Lab8 --- Lab7/kernel8.img | Bin 42416 -> 42432 bytes Lab7/src/kernel/read_cpio.c | 4 +- Lab7/src/kernel/shell.c | 1 + Lab7/src/kernel/syscall.c | 11 +- Lab8/Makefile | 62 ++ Lab8/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 32753 bytes Lab8/bootloader.img | Bin 0 -> 8208 bytes Lab8/bootloader.py | 25 + Lab8/include/devfs.h | 21 + Lab8/include/device_tree.h | 9 + Lab8/include/dynamic_alloc.h | 35 + Lab8/include/list.h | 441 ++++++++++ Lab8/include/load_kernel.h | 7 + Lab8/include/math.h | 1 + Lab8/include/mbox.h | 7 + Lab8/include/mbox_call.h | 9 + Lab8/include/mini_uart.h | 23 + Lab8/include/mm.h | 19 + Lab8/include/my_signal.h | 30 + Lab8/include/page_alloc.h | 39 + Lab8/include/peripherals/base.h | 6 + Lab8/include/peripherals/device_tree.h | 16 + Lab8/include/peripherals/gpio.h | 25 + Lab8/include/peripherals/irq.h | 27 + Lab8/include/peripherals/mbox_call.h | 40 + Lab8/include/peripherals/mini_uart.h | 19 + Lab8/include/peripherals/reboot.h | 8 + Lab8/include/printf.h | 109 +++ Lab8/include/read_cpio.h | 18 + Lab8/include/reboot.h | 8 + Lab8/include/reserve_mem.h | 18 + Lab8/include/shell.h | 7 + Lab8/include/stdlib.h | 31 + Lab8/include/syscall.h | 26 + Lab8/include/test.h | 9 + Lab8/include/thread.h | 65 ++ Lab8/include/timer.h | 14 + Lab8/include/tmpfs.h | 38 + Lab8/include/utils.h | 8 + Lab8/include/vfs.h | 95 +++ Lab8/include/virtual_mem.h | 6 + Lab8/initramfs.cpio | Bin 0 -> 405504 bytes Lab8/kernel8.img | Bin 0 -> 42432 bytes Lab8/pack.sh | 4 + Lab8/rootfs/a.c | 8 + Lab8/rootfs/cat.txt | 1 + Lab8/rootfs/one | 3 + Lab8/rootfs/ts.txt | 0 Lab8/rootfs/vfs1.img | Bin 0 -> 404216 bytes Lab8/send_kernel.py | 40 + Lab8/src/bootloader/boot.S | 25 + Lab8/src/bootloader/bootloader.c | 15 + Lab8/src/bootloader/config.txt | 3 + Lab8/src/bootloader/link.ld | 21 + Lab8/src/bootloader/load_kernel.c | 67 ++ Lab8/src/kernel/boot.S | 89 ++ Lab8/src/kernel/config.txt | 2 + Lab8/src/kernel/devfs.c | 129 +++ Lab8/src/kernel/device_tree.c | 266 ++++++ Lab8/src/kernel/dynamic_alloc.c | 249 ++++++ Lab8/src/kernel/exception.S | 189 +++++ Lab8/src/kernel/exception.c | 335 ++++++++ Lab8/src/kernel/kernel.c | 34 + Lab8/src/kernel/link.ld | 19 + Lab8/src/kernel/mbox.c | 133 +++ Lab8/src/kernel/mbox_call.c | 42 + Lab8/src/kernel/my_signal.c | 59 ++ Lab8/src/kernel/page_alloc.c | 308 +++++++ Lab8/src/kernel/read_cpio.c | 334 ++++++++ Lab8/src/kernel/reboot.c | 19 + Lab8/src/kernel/reserved_mem.c | 48 ++ Lab8/src/kernel/shell.c | 239 ++++++ Lab8/src/kernel/syscall.c | 245 ++++++ Lab8/src/kernel/test.c | 116 +++ Lab8/src/kernel/thread.S | 31 + Lab8/src/kernel/thread.c | 281 +++++++ Lab8/src/kernel/timer.S | 32 + Lab8/src/kernel/timer.c | 176 ++++ Lab8/src/kernel/tmpfs.c | 138 ++++ Lab8/src/kernel/vfs.c | 490 +++++++++++ Lab8/src/kernel/virtual_mem.S | 54 ++ Lab8/src/kernel/virtual_mem.c | 10 + Lab8/src/lib/hex2int.c | 15 + Lab8/src/lib/math.c | 9 + Lab8/src/lib/mem.c | 33 + Lab8/src/lib/mini_uart.c | 215 +++++ Lab8/src/lib/mm.S | 6 + Lab8/src/lib/printf.c | 1046 ++++++++++++++++++++++++ Lab8/src/lib/queue.c | 7 + Lab8/src/lib/simple_malloc.c | 8 + Lab8/src/lib/string.c | 113 +++ Lab8/src/lib/utils.S | 15 + Lab8/src/userprogram/link.ld | 19 + Lab8/src/userprogram/user.S | 22 + Lab8/userprogram.img | Bin 0 -> 7648 bytes 95 files changed, 7096 insertions(+), 3 deletions(-) create mode 100644 Lab8/Makefile create mode 100644 Lab8/bcm2710-rpi-3-b-plus.dtb create mode 100755 Lab8/bootloader.img create mode 100644 Lab8/bootloader.py create mode 100644 Lab8/include/devfs.h create mode 100644 Lab8/include/device_tree.h create mode 100644 Lab8/include/dynamic_alloc.h create mode 100644 Lab8/include/list.h create mode 100644 Lab8/include/load_kernel.h create mode 100644 Lab8/include/math.h create mode 100644 Lab8/include/mbox.h create mode 100644 Lab8/include/mbox_call.h create mode 100644 Lab8/include/mini_uart.h create mode 100644 Lab8/include/mm.h create mode 100644 Lab8/include/my_signal.h create mode 100644 Lab8/include/page_alloc.h create mode 100644 Lab8/include/peripherals/base.h create mode 100644 Lab8/include/peripherals/device_tree.h create mode 100644 Lab8/include/peripherals/gpio.h create mode 100644 Lab8/include/peripherals/irq.h create mode 100644 Lab8/include/peripherals/mbox_call.h create mode 100644 Lab8/include/peripherals/mini_uart.h create mode 100644 Lab8/include/peripherals/reboot.h create mode 100644 Lab8/include/printf.h create mode 100644 Lab8/include/read_cpio.h create mode 100644 Lab8/include/reboot.h create mode 100644 Lab8/include/reserve_mem.h create mode 100644 Lab8/include/shell.h create mode 100644 Lab8/include/stdlib.h create mode 100644 Lab8/include/syscall.h create mode 100644 Lab8/include/test.h create mode 100644 Lab8/include/thread.h create mode 100644 Lab8/include/timer.h create mode 100644 Lab8/include/tmpfs.h create mode 100644 Lab8/include/utils.h create mode 100644 Lab8/include/vfs.h create mode 100644 Lab8/include/virtual_mem.h create mode 100644 Lab8/initramfs.cpio create mode 100755 Lab8/kernel8.img create mode 100644 Lab8/pack.sh create mode 100644 Lab8/rootfs/a.c create mode 100644 Lab8/rootfs/cat.txt create mode 100644 Lab8/rootfs/one create mode 100644 Lab8/rootfs/ts.txt create mode 100644 Lab8/rootfs/vfs1.img create mode 100644 Lab8/send_kernel.py create mode 100644 Lab8/src/bootloader/boot.S create mode 100644 Lab8/src/bootloader/bootloader.c create mode 100644 Lab8/src/bootloader/config.txt create mode 100644 Lab8/src/bootloader/link.ld create mode 100644 Lab8/src/bootloader/load_kernel.c create mode 100644 Lab8/src/kernel/boot.S create mode 100644 Lab8/src/kernel/config.txt create mode 100644 Lab8/src/kernel/devfs.c create mode 100644 Lab8/src/kernel/device_tree.c create mode 100644 Lab8/src/kernel/dynamic_alloc.c create mode 100644 Lab8/src/kernel/exception.S create mode 100644 Lab8/src/kernel/exception.c create mode 100644 Lab8/src/kernel/kernel.c create mode 100644 Lab8/src/kernel/link.ld create mode 100644 Lab8/src/kernel/mbox.c create mode 100644 Lab8/src/kernel/mbox_call.c create mode 100644 Lab8/src/kernel/my_signal.c create mode 100644 Lab8/src/kernel/page_alloc.c create mode 100644 Lab8/src/kernel/read_cpio.c create mode 100644 Lab8/src/kernel/reboot.c create mode 100644 Lab8/src/kernel/reserved_mem.c create mode 100644 Lab8/src/kernel/shell.c create mode 100644 Lab8/src/kernel/syscall.c create mode 100644 Lab8/src/kernel/test.c create mode 100644 Lab8/src/kernel/thread.S create mode 100644 Lab8/src/kernel/thread.c create mode 100644 Lab8/src/kernel/timer.S create mode 100644 Lab8/src/kernel/timer.c create mode 100644 Lab8/src/kernel/tmpfs.c create mode 100644 Lab8/src/kernel/vfs.c create mode 100644 Lab8/src/kernel/virtual_mem.S create mode 100644 Lab8/src/kernel/virtual_mem.c create mode 100644 Lab8/src/lib/hex2int.c create mode 100644 Lab8/src/lib/math.c create mode 100644 Lab8/src/lib/mem.c create mode 100644 Lab8/src/lib/mini_uart.c create mode 100644 Lab8/src/lib/mm.S create mode 100644 Lab8/src/lib/printf.c create mode 100644 Lab8/src/lib/queue.c create mode 100644 Lab8/src/lib/simple_malloc.c create mode 100644 Lab8/src/lib/string.c create mode 100644 Lab8/src/lib/utils.S create mode 100644 Lab8/src/userprogram/link.ld create mode 100644 Lab8/src/userprogram/user.S create mode 100755 Lab8/userprogram.img diff --git a/Lab7/kernel8.img b/Lab7/kernel8.img index e4fba06b51df40e1528edb1a9e03c9e1b68059ad..b182f8b5dbda04341e5992011ae4d23726964f7c 100755 GIT binary patch delta 8420 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 delta 8440 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 ObJQMI*h|koZ~H&MK2=2k diff --git a/Lab7/src/kernel/read_cpio.c b/Lab7/src/kernel/read_cpio.c index 019fb866d..ef16f5106 100644 --- a/Lab7/src/kernel/read_cpio.c +++ b/Lab7/src/kernel/read_cpio.c @@ -123,7 +123,7 @@ char *find_content_addr(char *cpioDest, const char *filename) // No hit if (flag == 0) { - printf("find_content_addr: %s: No such file\n", filename); + printf("[ERROR] find_content_addr: %s: No such file\n", filename); return NULL; } @@ -136,7 +136,7 @@ int load_userprogram(const char *filename, char *userDest) cpioUserPgmDest = find_content_addr(cpioUserPgmDest, filename); if (cpioUserPgmDest == NULL) { - uart_send_string("FAIL to find userprogram.img\n"); + printf("[ERROR] FAIL to find %s\n", filename); return -1; } diff --git a/Lab7/src/kernel/shell.c b/Lab7/src/kernel/shell.c index 7a068bd31..0fa7c8951 100644 --- a/Lab7/src/kernel/shell.c +++ b/Lab7/src/kernel/shell.c @@ -30,6 +30,7 @@ void shell_main(char *command) 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"); diff --git a/Lab7/src/kernel/syscall.c b/Lab7/src/kernel/syscall.c index a2850ef9c..b699306b5 100644 --- a/Lab7/src/kernel/syscall.c +++ b/Lab7/src/kernel/syscall.c @@ -8,6 +8,7 @@ #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(); @@ -57,7 +58,15 @@ int exec(const char *name, char *const argv[]) core_timer_enable(); enable_interrupt(); - load_userprogram(name, (char *)target_addr); + 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 diff --git a/Lab8/Makefile b/Lab8/Makefile new file mode 100644 index 000000000..abe49536e --- /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) *.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/Lab8/bcm2710-rpi-3-b-plus.dtb b/Lab8/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..0dd0e2f435a74e6a0c01e2a30fa0246d55ee3091 GIT binary patch literal 32753 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 OPqI~*e!TdI|Nj8Si!WyY literal 0 HcmV?d00001 diff --git a/Lab8/bootloader.img b/Lab8/bootloader.img new file mode 100755 index 0000000000000000000000000000000000000000..3d60319b027c3eababd542bdc90c5e16a538f16c GIT binary patch literal 8208 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 literal 0 HcmV?d00001 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/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/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/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..6b3788389 --- /dev/null +++ b/Lab8/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/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/initramfs.cpio b/Lab8/initramfs.cpio new file mode 100644 index 0000000000000000000000000000000000000000..1640f8c4ad3b90dadff7ff20285cd5d4c2b15d36 GIT binary patch literal 405504 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

kB>&}lcHq8a+fC+(T*vXygQbOt&4+{J&H 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 f>Jx5#(sG`Uy7V56J9JKU@g;xeJRd(or{w ../initramfs.cpio +cd .. \ No newline at end of file diff --git a/Lab8/rootfs/a.c b/Lab8/rootfs/a.c new file mode 100644 index 000000000..d2398d75b --- /dev/null +++ b/Lab8/rootfs/a.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + printf("HAHA return 1\n"); + + return 1; +} diff --git a/Lab8/rootfs/cat.txt b/Lab8/rootfs/cat.txt new file mode 100644 index 000000000..949e8345b --- /dev/null +++ b/Lab8/rootfs/cat.txt @@ -0,0 +1 @@ +No cat here. \ No newline at end of file diff --git a/Lab8/rootfs/one b/Lab8/rootfs/one new file mode 100644 index 000000000..50ecbb596 --- /dev/null +++ b/Lab8/rootfs/one @@ -0,0 +1,3 @@ +1 2 3 +4 5 6 +7 8 9 diff --git a/Lab8/rootfs/ts.txt b/Lab8/rootfs/ts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Lab8/rootfs/vfs1.img b/Lab8/rootfs/vfs1.img new file mode 100644 index 0000000000000000000000000000000000000000..2c41bd71abb5faca75b99d85fac8cb66d8abc187 GIT binary patch literal 404216 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�$5T3bfUfi!~)t8f40G1mc=YEL3e#o195=B+7?@H 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

9zL@=m*GkFg> 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

sAsSy>wpQsKfiH#t&|}wslIi!%N7MJ|6Wb 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 Y@}IBef4!gR{Wn|tpKi7M-`Dd00N}u82><{9 literal 0 HcmV?d00001 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/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/kernel.c b/Lab8/src/kernel/kernel.c new file mode 100644 index 000000000..dc764b380 --- /dev/null +++ b/Lab8/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/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/shell.c b/Lab8/src/kernel/shell.c new file mode 100644 index 000000000..0fa7c8951 --- /dev/null +++ b/Lab8/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/Lab8/src/kernel/syscall.c b/Lab8/src/kernel/syscall.c new file mode 100644 index 000000000..b699306b5 --- /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, 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/Lab8/src/kernel/test.c b/Lab8/src/kernel/test.c new file mode 100644 index 000000000..36c9909a8 --- /dev/null +++ b/Lab8/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/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..f3f40d788 --- /dev/null +++ b/Lab8/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/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..bebc19f19 --- /dev/null +++ b/Lab8/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/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 0000000000000000000000000000000000000000..ada965550ced9a4ea364a9d02264ae761852641a GIT binary patch literal 7648 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 Date: Thu, 15 Jun 2023 13:12:36 +0800 Subject: [PATCH 21/22] Lab8: Basic --- .vscode/settings.json | 4 +- Lab8/FAT_R.TXT | 1 + Lab8/Makefile | 6 +- Lab8/include/fat32.h | 117 ++++++++++++++++++ Lab8/include/sdhost.h | 11 ++ Lab8/include/test.h | 1 + Lab8/initramfs.cpio | Bin 405504 -> 810496 bytes Lab8/kernel8.img | Bin 42432 -> 44808 bytes Lab8/rootfs/vfs2.img | Bin 0 -> 404984 bytes Lab8/sfn_nctuos.img | Bin 0 -> 51200000 bytes Lab8/src/kernel/fat32.c | 258 +++++++++++++++++++++++++++++++++++++++ Lab8/src/kernel/kernel.c | 4 + Lab8/src/kernel/sdhost.c | 244 ++++++++++++++++++++++++++++++++++++ Lab8/src/kernel/shell.c | 5 + Lab8/src/kernel/test.c | 25 ++++ Lab8/src/kernel/vfs.c | 17 +++ 16 files changed, 689 insertions(+), 4 deletions(-) create mode 100644 Lab8/FAT_R.TXT create mode 100644 Lab8/include/fat32.h create mode 100644 Lab8/include/sdhost.h create mode 100644 Lab8/rootfs/vfs2.img create mode 100644 Lab8/sfn_nctuos.img create mode 100644 Lab8/src/kernel/fat32.c create mode 100644 Lab8/src/kernel/sdhost.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 166afa72f..0de3b7858 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,7 +33,9 @@ "string.h": "c", "tmpfs.h": "c", "vfs.h": "c", - "string": "c" + "string": "c", + "fat32.h": "c", + "random": "c" }, "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file 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 index abe49536e..b3a6c7b5b 100644 --- a/Lab8/Makefile +++ b/Lab8/Makefile @@ -10,7 +10,7 @@ SRC_DIR = src all : kernel8.img bootloader.img userprogram.img clean : - rm -rf $(BUILD_DIR) *.img + rm -rf $(BUILD_DIR) $(filter-out sfn_nctuos.img, $(wildcard *.img)) $(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c @mkdir -p $(@D) @@ -56,7 +56,7 @@ 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 + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -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 initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb -drive if=sd,file=sfn_nctuos.img,format=raw diff --git a/Lab8/include/fat32.h b/Lab8/include/fat32.h new file mode 100644 index 000000000..511583254 --- /dev/null +++ b/Lab8/include/fat32.h @@ -0,0 +1,117 @@ +#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 partitiontable[4]; + 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/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/test.h b/Lab8/include/test.h index 6b3788389..11c9a66cd 100644 --- a/Lab8/include/test.h +++ b/Lab8/include/test.h @@ -5,5 +5,6 @@ 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/initramfs.cpio b/Lab8/initramfs.cpio index 1640f8c4ad3b90dadff7ff20285cd5d4c2b15d36..37359172c0dee39460d48104274f7e19dc4c0bf0 100644 GIT binary patch delta 3358 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 delta 11105 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 zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI z0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51< z1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc z7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf( zSik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?D zU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPq zfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b z0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q? zEMNf(Sik}nuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}n zuz&?DU;ztQzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQ zzycPqfCVgI0Sj2b0v51<1uS3z3s}Gc7O;Q?EMNf(Sik}nuz&?DU;ztQzycPqfCVgI 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 z0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;a zDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-? zpaLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO z0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;a zDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-? zpaLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO z0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;a zDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-? zpaLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO z0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;a zDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-? zpaLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO z0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;a zDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-? zpaLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO z0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;a zDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-? zpaLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO0xF;aDxd-?paLqO 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%0DPh zK6Z07tP2RzGqQj4h803!L&O}|Aj@i7kM<08^JDaFIuG0eC*x~Q;uBB7cOZ!`# zn#nXvYiq_lMs0k1Tb+$E;o$A}rRXCw5gyU9lW5&IEt#(;r#X}OdXp@C%tm3LOu2?3 z&-+4N8S+}t@AL=HQuHZWU(-n<8Mks7$s`%4b&!$x^|*fr83*z(bQy^@gwt}kJVZM` zNgjrA{6)ww3X9~TZ+ppLTja7N*-F4qd?Ua&8u*oMX|NB7UEP)&y9V~_0d{3A@=ueS z_EAyZs{5%RcWno`9V+kBu0JE)H^Q%Or?@A>o}J`AwS#*_2ltsB-0w_sr||BE-O0lv z`#jillEVKX?E1H1iZxb)G-~~bIld0-f(v?;Z)1uDbDhjshNC=6ZA3NbXdBQ*q{q-! zAm94OY>u_E+_hY+8EF%T!+kONL!G3${1ez8fE}hyRV zG9#J$m*k5$H&XGkIsRA3#+3C#k(HRWZeii%=uz3 zUiwS~?n)b%IU(62qT8tqM!%Y+WHg<}*9!%;-C~M}whU8P#YQIb@OkjPG~*fUC+bAF znhy`{d(OoIePc56@JDce?Na-;MWC67v$X!_dPc-fi@7b z#=wVsNpaU)WEXqzHAseWoeVpLiSC zB#C~vL&Q3$!*BOEA~ho%*bnYhP(HRZk0VBF+O*ECu3>v0QrEDz&k!T;_7bCi0nl2u zURG1kcH8&g(%&Jk3s4_Gb}H|G{|jafacxj#$+ z<_MVNzp2fN{lu*?^rKkk%!pwR5#}WWVjqLv+?G{9bg@oN7sjiay8!nCa6i<+-JIlZ zbLaT6<2-WYQhTJD@=nRcoW&Ls+zzZk+x^u3JxQ{l9U;>!JyTQnJjJsCWkefKgI;Mq zVl5yug`1sVrUTS^@Wcl7{09q|i_rH4#LAq2qmn5%@;wqZ`B{WlQj`jL@y-2I|Cwwm zauMC1A0vNu!tq@Rr*D>tvO(hseVlP6)vY?qHhrE%^Mw@{OZ0{LFwA_IkJ8uz-(&%J znfrmJ%p<~Oc8^UKsmDq zsDssA?c&|M{pouo4f_CFWBrG)_(u3oNVaXS@v#H&$iSJ-DC=X3Qn```WQz~+u+47B z@Zeg^;kL(2!F2&oKj48QvA{Ek4K^K^V%N&Ai=&JZhK$ro4-|wKlo;(Kead!~;4nz9+RSyTFIC(S&w6 zh47>9NxT>FyhNY7h}!r|==&g!8_^G=Tr?q^6zqpk+bokmA8n6K90)g?Kfgx4J72s6 zeJs@-*k!U;aJ$K|HXz?)6R+p{fHb>{+~?@wQJb#g0&gcox1=nfxZ@d3*gvI)O?C0I zD;-f*XjAuztx(nv)W#0!5%BKZ2&_r8O!> zM2Do@i7;3^w`rcfhY4*%38nQeo6-sXA3mB^%*h>2b-z-I!_iFpmduW|HE(rDEBN_1 zskaOM)%+uxHcmR0rj4hLrDDdk%rH4#PPpm_Fz<)G7@UNzaXnJ}+i>8eQ zd%JW#Jw4a+^xSxY^yuFvozjVKlZkXrrf)*+HonVh&^vyYpMyr}Ad}J|u0D2*o%~(k z|G4jxOw+$hGEM(3I!#Z<)GM`iB7}G=u3Wo~+6=;(+lGEWCpMF(^G-dTl{W3WRid4j z2yy_n*xgj`(e}?H6XU8Ml8JnlP?o7ZxCrg}apOQg+S3bdBGvN}xC7L(jlNAGJGBcn zM{;B63n~{Ju@-B340BxV8yZ-%YJz9+k=z0ieS*1COUqO}9LV(m2ogWqD+zMWG?R` zslQl`_)MN>WVu`v9|Hb!$i3=_HMUyK4{Pj6GQl?!eFWtTtE+2r)IE!_W=h1VnwgQO zkEEdwWeWB^A-jX`*OtKxMowcRFM=c|Lvc6D4; zBBii%mGy8#IZ)Rh$PMRs0|I+q4!p5(g%I2rF$Xuw@-Hzipm7B9c>~gt%m??t$#^$* z;1y57`(g*)lvD7&#Cchux64+U<9`kL85&}XP=5W2#sWMKhU)!>$F9yF!aT1`4tqd; z$|dUVtj-maFSdzrpYy5S|Dj*e-Pd%;vz_46^Nif|Jlk}VJlh6&lldsmPR9G&4!o3S zC*%En2VTmvlkslnyp(4SaapO^KiK zk;>V9=(GO@d5M;tMBBw_$$X8R=1k&i0!?q*C=5N#G+u(dl^-Lm&vcNlJL)vaM<&Tv z+(AC#*W=Aa7|n=-Xu5nvJ8%NpAx_KT@)6&WB>5-|lFx~6Lh?~NYNqqY)Go*an@kvE zr@*eyTW&)ihc>90%ub1{CTW{Pw0RtIChtEr9H5=sh_- zrvtq=r|bI44-X-KxW1AFY)V41Y^65)r#$T*hZvi zs1KlX}s<87um2@2v+2@Q%;-fJ%(MBiHoLu*3kLLKTn0Jyc;zP(m^UFOj zuLLhlHJ+9VSPnyfb_@nJ8J zG7EkweF9cHD4fYj?o%laeD6I}4wM&~&|YDltmV61*^IgVixP|WRCd6h5Y22R(;R)N zX40;g;P#S>HE46?oybF)x07k^V2ZHo?Mp=>H#Reo7Q1uSX2jb`!MLUA?u4cTa5z|e zA5V`7_b;%9-Z5!K4)$#4Y+{vx*uSuDMR$Lg10i`6&I8O#B%Kl9G{#;>zx$?3>h?P3 z<_H641{`e5qKt-617ms0CrD>T!%Mp$`zuW8tKgjcYZ(pO8<^+tN2~$-%690%3EvVU zKXlHS?!mxW1N7@NL=)O2Q*a~Zq%W!R?^E+F5z}>HpThpbMA{JO!y-1{T6Jxc%Iaaz z6Nj{NRg-wGU_k=s)T2jYkEvsYr%`VEDo?L}dgJ2fdMd(Ujn8^mD=+GpaGP6v0Chl? zwDDRR=UJZ6=H;@4*&C-ll)Fxy$7XT+&vVNoq15Jsn#$ zOVhQT!}(Wno!FDSey`?ntin3ouKR6b-Gz4XNsPrD!DkksEsl}i!-TSx%l%W|r+;s? z|LcFbXp=6-eHo*~CsBv89(Oc;O%MGNUzegbY`pG<_(%tOt!_tqvj8%ty6u}x?tJBY2oL(X zrki;Fl0J<1Z{hq4)G*UyA;gEWuzcIl%b}M&H>(OlpFGG2*iFC+! zkj&^CX6a!gUw+Ta>>M8c_UAT4p*M}ai$3KZz22ZNxL5USZJNiw%RH=mQ-5_I_xG1! z_Q2oZr(>Dlb&@hqWvDTs>5H0%Y4v744@*zSLd03He|`VWm>aQb7GJ{MKjvkERcm#4 z8K*acegN(8p~RqqBZ&b8YX6Hcgcj=%YYTLsGUkkEqtjaQhNNK5W>W4!pNoFsDM?&O z>8rChsdG8dUFzDyK(v90SjIFho=12*s-KmT_zUvmw>Mc3R+h^ab--Wu_r4#cI36dr zKtG&Gf*ud$)Zxzb|NVXu^@%A@!3}!~urARzM*2(n376<8cZn8{!*8o|`5yy+HGp{6 zQ#i7%`8gi9sq$&rwnm+w=V#OnY;e@MWab_lt5e6w>#_f-Kz!jS@2UXpT`)r*^rBi1 z60u+D-&F1AoZ`!Rnh@6(#Dy}eu9q<1x!T->yYqD;H9yYb`2juUc9HV14~xGJzqu~k zwl{hBdB~3elovSHHi+&*K=^NiM_aQ^=WDioV|In~^jAw}oV_zCH|S}r!?;Svd7n&u$-i_O zWc!5tx$UaXaGxHo8}l{Py-#&MSyIO_hryfBX&A!@ty({0=VM{P;Zm$Rt%1h2S7RSf z4<)S?V|`DT<_wr-&$^mv9rP@;PMg(=zN#+Ml?}W*$BBLp$DP1)IexTnfDCy&K4+2) z12}Cn+AvM$sL3&i%Q2M8QQJWd;wL@M1w0|k@xk|YVBjGl58g$zz z4;P?3T-=gT@ZFZYf(%KFSnw@SH=Ar!=TI)GJ=WXVVW_KW+0)x;eB08N+?WL2KCOp$ zrS>2A$d*S6u4uubOSEaUSLh0)&4PL}4(50+!#Ko8eF^BwwV+cu`5ws(lj`hEDsBBh z=^~3b-YG_}1<=}!uETNEr7qXordz=KJ;-)b%bqJCOm6lJu%+`rbgJT0-hp2|~@Td2!yUQhDZZK$d1FwcGta?-f_1(-DM{uMW0 zhM7F>CLJBEFG?$HA_4m6O?0Q<&Xz5)52tDOC~QF-ds<#@3d1jzt$($=9D7~Ge-$%F zwK4ax<7;%s25_N zwxkswM&6pDFRJTc@{Zo(n~3{up8f>Sw?A@wie!65IM)`n;=T{Pj9Mg{vIBn74z3^a zK4i?m9U|aUc7m1#8m%#P2ko4LSSJFW3p|hG1Aq?#{vrGg1+KS87aX+4EeF*Yo-u@S9{!&z=iBkIS&0 ze@|20)9QR@0NkkD4&vcHcks5O-v)KI>4k&BWqO!H!Hf2JBjuaPZ}H0yS#Yk`;jcz} zz;Mr&w%>p$7f{?DcEIGFxZUAlaWhOdoW(_$zq*{oFNV7f_;-1n_(m@2q0U&VM~i`b zxSamZe!?B(A9%uT$DE!i!{G1b>n3iKyc45a4rau{=dySyXnjXGQqVT7WnQd_d#1!B z>RYfzuaEUdf=Ay!rq9z~=lOaC&ll!3DdRZ)=IzeN+bBolxm|0!Q(`ptx)w6e!~BHu z?2q{F--CSz%B$YyzYl*m9pewWo}@N^=XdOZ5AmJ$K>l0c_h0b)3GjWOeG2n1%mhpU z_j*`ykBBxF8|booEfLY%#c%ek#hw;Xly6NDNuETVEkbqUHXdhM1&iOoaoYPx{gBDK zPW5BK9xeFU2zexuKjTndEbCBOad*Tsi+_uAK%;wwauI(X+~#olfC{aC-^0VEa(6HC zbe8(3fS-C;f*6Y!mw!a#Kyy&`iN`E zMEmhQm^U5DD88ivx)yw!`8-~dd?Ckgt8hj$kS{d8TXNLhsVw989bD$0ayxsB#aD9N zgt~+Aw0{=jnFG0~{yu{AP@nZf^wkNqex$^(=R_@kzi1QOk7Mq6&n1#l=(ca{;dYVv z;=MeM$MrZcPkQPo?o!qxOzat~c@F7@iFuzLV|e>3_aXiB4_RXi@x7SC(L{Pgy}w+- z^I{pypThkGJv=_1&BwS9d$nC8^RE!TzTTzIFQ7ksSxrZ7(-xlBx{fZahojC1^2OIV z-h8Uxw^kE@AO7;-NN3#JBZGw(YMY58ecg`i{4ePr6eFIZAiPfcA7C+FyKe(pR7zK{=47Ix%lVn+mfvKF-DV-{BIXh4Kz^!o@2t3YV50XB$#T*^@5KF#^AuSan+B)?_yKDGn*nbD;s6`YH(}wB z6#=|}NaR3Sd zyE}mfu&03rC{BV!z#ruhfcaJucyKt&3#bJ60q-TjA{-6#10Dv{0U}vAuLal*hy(1| z?GOMS0lWwJ2w=nGBs$2zR{+)mUIDz91f%eP$qj%?KrNsy3G&ZD{sG1Se1NJXhyZ^N z@DYIJ;C~Wq%0)bY2;eP1U;ylZHvsPdVo6ZsLY@OA0j2@uBq$h!yaf~k$^gcnkpo5! z7&&0%fRO`64j4ILSm9O%kN{%%j)WCxl4RDWdG{Zm+@U?(X255NN+ z{3YU$V;_L-dV(EKN+pic=x!`#LfRq1Q4>7+JyB$8X?7q*#0EaNPq+ABFqw%kZ9Xh- z_@JxwVGEWIs&CNX&kM5wP?-vQCm+iJuSom^xK|372azcQ%y!UR05^be{CNQKQIjyT zYoM<(mwUI1pY3pu5j-51I-`lPe+oEy%t50J^E; zx?MMkeqVxRBCOE>W@6bgW77e2XP16QnD+gcN4>`j`3g^4z-*)PP4|(Jso^GmzaTDS zV^oWnjlQ{hXB)zXmAOXbBr=Wg6;9a1TUoMACIOVNw*TTnr zf(V1|{iC}82~Q+)6Lh~G-6P0(BnG+kd;jR3K>R_lqhYIZQGJ9Q3B9g(xcy{w3PQiT zkEhS0n#cjYT|qn}0HBz(JV2OSel_Hax2*Aa&vFWr+@CPR0PJfM3c>3iXe0O)%Vc$j|Q-W1}&N(=rVCzBS@gV`{V z6?C!WPT&rQ%Ab2{-ZdaJrh;R=h|~vg>ID zQli<}Xfib%aw1$kO>ATmj!?1g-tOjU$0X;Tn3M!+4W+Q)B9j3g6_DA8TdO+=Jn>wVvJg0v<5RG6AXk=EwtOcwAL;xQFS^;kXUV-~k0GU$;Xl5l$ z0-_Pbfj4kSUY8+U_dgeW1j%bNgzI69Iv4o|$mga5Cea=(&>#qWGr%zral!P!6anO3 z2y-bw2CU`4Kxc!%3;P(D1QR;IBG{J#J_4)&P*^#DBEVcgB{u^wZ9_mGj6NPP1#lPO zJ-`}nMqow(3ZMlr2K=J{9&Wl|R-rt-0`n~n3QYS@#_#|Mdk-K3DghJD*WeXS_qh=k z;5`ljn2R_bhe`aN^Nxi8>{VFbBa>h)H;K0lkOLUg0VY7ck3ic5umdM}3%Ce#H#Z4q z05T^53IK&1C;J+}Qb25VDBal`c`-ATuE5?Pz#sms023kq+dqTWAg2TM+}4&}$?>_J z{+T`vxCr``i-u<4lo+c8J}LE|=^t?V4Zs^XzPbDU^rhe#1)ddG4$YVVI+iX`D2pT} zmIlok;>~zPo>dEr6sbWLJa-RYaxmk1mh56v=E}0b_WNY!P`R>*Yah7kgz})6k-|eD zz8dh=f$t6c&d-w-)*$DV+|6#edC4c;C7wo#{u9A+v$>_nix>~u*_~CEfW$uB@|L+% zOVK8cW^XC-%YuxThJmf9Q?aFO`F@Y7;V_;xYLjFp2KSlRnAUv1C^j6%w}5LDZw?nT z!6*A=lMrBL*#ORsUmJ`>oJ@^;_`=%~yblvVRKsOOgFP^7m*1_t*d7 z67H}6!@9rz4{z7~*<}AH^4CfBmzkEf4S2Maa}Rh_=N|CrA9ZfK>_3mV(`El))0=J5 zhL09B9JaGJ+wz{#FrjVGGd6Hv#`4+*)fCkb_GVk34e1LT4i5y@_W=zP+WIWeT=Uxc zZrH5y!N+2!>X$;%F=>JBD%EHh#cR30;qYKJJPi}t^0;eWTi#-wFYkd)DGi5vsuE}z z;;k*xmbf2KvD~+=$m`y?Vo~Q@@MH9L^DqQEE9w7!SDx{d2p}ZJddcIrUuwK#IjBrqIjG!v6f~vQEPOK{8Q@4$6j-l00yhj0R72=|GB=e%Hw56%;o8 zuBjdlUsGiI9VGu6e*abd>&4fzw3Z^7+6s*P+E|Uu)R)EgOSP#rXtiq7R-;X-?HpqJ z8)`FlnNfDlYKgeIRmVED^|5HBq~`zGdms2Hs;h7O&hCV4NFWmkb(LTSS!Knj0|~lf z)FlZ53bIsa5#{NkQlLUz3Mf>p69kP2vi#AaMO+XmQfh}7DYaA=Sda?H0$Qxvy0-c{ zjr8#*tPnMj-}l^^-E2b8*5`em_xb!j2JYT7=bm%!x##}>Gk2P#gkG8GBeU^nrM~J< zVh-kHGRtC}ST;sqIjl3wWqGU%>&o(3w`Pw~g%_b51Z^4N@B%uo*;8xb0KI7821iF^ zS41nbmFALg%%LkTrZi6r?|0MyayM_baqi}0Hm=(bcP#p(ay(1%sfee-;a6o`YU8P` zbPNXMMT`~>(2Ew%;=2H`>=jx8TE5@mfmZ#ROO{zUKr1ZVcose}D_+$ze4^|S{aSEc zeAp$x9Hw6jPR2)E4a^bxwcxr#!|ww9VfwY;lXFX>p$} zOAL1U`GVYn6J_zix$N|uINJB_dS?o$bXJIvx_IR!>~vE`NJW~b0zBI?LMoku1<$N_ zWzSZg9gFtc)>9$Z;=UNEA#R_|O(7Qd#Yh!#r*xqZOUsL46VkFR4{0fQpk+!IiZNxu z{|Rf3XLtPgS=OK3$VTEYZtCA;_7gTM{iLz`*@J8$o0rC{U{A7Nq@OhQQU**iUSkK? zTWoh4%g|e&Zgz2>@nG(*U4e79wUnS8WV|LvKV?CS^ZeYWROkAed|I_L>ZBUw5WV~! zn{WOPFPP_VE=f3WDp}8=_L=2Jk6*`NRmh4SubDE8`@+?^<1^ZBxyi>+(_M1VlBRH9 zygIk{_Y^({K5!G$6n1=0!_Nwud~ry`V5#@_3Yxar@OTiCj#FhD4bN zx{|&`%()6ee3jKf5EVZ`H0qb{4Q% zL&n~^es@;i3#&#~IH&15M!Ofe+1+dp%g(mS=P)Xt>W)D6kX?g|vnupvIgl^=F|Z%9 z>$~E)D!|1vyRR*mG)20JwBO-$PV4ISgWFHs3db0pL1$SZDN3;W^jvqJ`zk9QOv=jA zE`%Q}Me!j5t5!h5@&|Lh$vc-1TcjpE_`Mjt!!Xec^=`(qfO49BfTFn$2B>zgfY8Dn z9-iw$WUl0#rx`oWBMBUB5E63g zZiB^0Ba*$Nvz{!$uhU-@9hyBP8py^oA1qhiz?yst%2=r!92||i0-%!ecIxh> zqun}dO7g9i$4-f#&7XYsl$0VRR5MD9{YXja*QA8fu&HbGI41U|rWqo2LBj3F*tgV5 z*M}XYE0Dc=M@6xfu7N*tiP;yWYvRsSy4H$x{pZbS^V-#pEKxg3Z_b($+Yu<1@7ld% z!KGt|p|;pC*qd(m0MJ{d&h&9%6@jA6S?k0uCL$i z>`S@Mvinz5?h0JqTCXfwc&4@%^~|y{o~gwZ^^RI{3)@i{I>%~@)0TJMuZ?sD~DHRg=4Iq|4yb%yL3 zcSe}#dz@L|yDht|#3sR&73@9s*&U;@{Y!p3YCOCB$Lr9n(7)T$|K_lV@UQjx4)!ws z-I2!J!9K^o*6@3!R+ym4>@NJ9+)9Bst`YxQ`-AJ*82oE}?$4gXzy4`Vf0i%hXY?gQ zGGO8w$gaY_foXcIY5eGcWxxDlyznc&6n^+1p7WZ0Iuk7qQ9J?6&1frSyefmWu4YtB ziN??rR)a7%MxTRo6){2#U<4-T1u%AV;Wru2!*AIk1;4Wn6%ziCoA8H<2y<9o49sB{ zev^mu@LP67!SAdig@ixiCj612#T3sNE1n~jJrU30%ASbxP=$!IBDVq~RY!7;5_LI} z+Z2C}BiW=xb9OE|SxKB%g45CL3-}qesencgOF`eQNkt43M^Vl&>W>gK;3Wm+^$uBdLyfgw-hxqzm9!3 z2P;H*u^_qGr|Oc>335BbrXAI!pW#KLwXO94b;!iO%$igxm1fkQKV-oFYE2p;YHwyu z`ZN1;o0>FA8kJFd9?gKyUXuAa}3xV3H( zRcq*rB9!dLL_H(BSx{wjpnC4Yn6P2;_^VjVdmqn8Y2fCrCnx#FxvG3@>AU|!tY^ub&aU)_&q%QQ7`bra#di4 zVXU8-rr|;xnn_AYpElTq-#`WZhALe0(S{_S5EB4sXHmoLCblaA_ zo`SHhVB=Q2A)vPLzA55G4Grp|6qGbG)oSH4+WWdoSw(bIc4hQGvRU&s1ua4Myi`jd zAhf|!w7ZLS`(d9S9$NG%=JfwlvtHMKe)X(3%4LqZO;cW?8uQ`SS#SKG7T?#;UiNYi zBxKHd7c^lV-Knanac`KHfui^Kgx{UU%xMbLQkrGcta4dX8F6Ve>$Ttu0FP&jn`osU z(gdwF>&>7Y+e+KphCL+1hA$E_TH{;Lo|Tq9B3{yOd8>Y7 zE!c|3g1sV63oa3HTC?7zY*~6-p|KeANrN)l%x9&o(l%*b8sl>Gbo5HkX*D)01E%(M zx8rTcJC0pxEX{f^I`%&^>(!gr+jG%X8MEGL#!d%~&uQK)X4S!HQz7gq@s8)x(WWAl zGzztH{-ala_{le&C+p33rZsAvCu8O#=E-}Ttd$*#3FBbQm}xW|bISM2BZFeDtYNOq zZpGn?9eyth562qOJN0>d4k)cPB5y+P&KhWq$buHN>}*{IoL4`YIJz zJ@8{zh1GezIZxwqRAa#f$ua(=y=w3*iJ5K|=zILsAI<99BTZiz)RTGI(k^;)z|RZW zV45)#J2%V(IxOml1G)Qk^J+^Dm7K4ZL#+knTgNkOEA7E_+W+6ru(1P((vMd4-<)AD6|Gz< zCI9X7{X^tK9mBokyeZ9+t)JZ1B`7H}<4E6r4GX#uk zU-SzpfNImw7HhT#oIURY6t-r2fR?496~hE?t2{svOV_mq3$itDun(p^wGPcy0>cXzSdx+?sFF%=HH1TxO*K z&&w)0x5n10*9Sj2G>fg7pJ4;iq$N9=d!5i^upLvA;TM`z+Rr|t;wv@rtr#jOF)v;| z#D;v!t(ot?qSublp;^6W4lMp)+IjA7KdN!}p5DnFYj&ZRq&XOJJBa?$%6H6mZ3r`9 zg2uB}4%Rvswny4+%8cKVG9LNT$z6^z;Sy!?&@{!pGQI8s$j`(oMJsN{v>woFIrLJX z7h)0Nq+tqD{1$|DLTK9}M{^l8%d8(gSts_4^h`$mxE#Ar=SdHver%a0MSsAg=xc0D zMoHZ=t$N}d_joGRSRrskb(~P4T_-ugvUUDo4ps>OMUSs_(RzWXnR&|X-KsfO(P$B zMIGmKR2otCm$%lYbgf%v&AF)7S+lBEUOJ`M>l1|qekuF(|mZs8mMyQqwrF5MU ziWLCjJ-+BjWp_W-5?SaTW9LQXzOC`M)+Cf_yL_XbE+2{7v28`FHqKcwgr1jK^%OOB zxmAxG&D*fIO=Xr=rFPN$H+-;js`M_v-i#l!QmlKY*9ZS@w;z%yr)_h!gVc*$weqc9 zD5WnW&tFYj?isS2<`AKcNd2K%R;~?2$~(?Av`EXX76~?3k*e);mRT!aXr-2qLdu#} z&|JT1*$7yoHT`qa>GvW1t?8s1nJsFh+fFIfTisq=`1<;ps?RCz!L(a(N=`&2+GP!= zAWCaOM?KJ8?6^;H<>{yL(lj>ImPR9*!ZeKywWSHT@|5b=hoUF_s9C#!IZZS|tcvxEggz{wL>v8vU!{A z&}g$)X({wGv~SH`t;J(QftE6YlAvJ048EPZsv+5Y}VJg$O~Z zcB}ehVZoQ7gY%Pr}c1 zuICaIWed38S>U7^8;VMc0kM{{C@a(zJCs$YITofl9!Ya75ggFHusY2#Kh5z_nqyI# z<1xWe2afq^jtA2m52raEO>-<297%9I2#z_YHO1k;XqW-g9Nlav;JCnsLXH6hDb+RT zi2_5ih6IK@R~K!;*z$6=WNsWAF-xwCcPa)q!-ylGh|>=h7;T1yWmJawa_7xBU7;vy z!=lc=g0+}{HUV-eUf`Ywl&6#cA`5m^x`G2xG+)W@U(lv~A z=D!@rpW4KM5i*slQemSGVoRmN(yxAKNAzD~G5x>YVs;F|VrRtmf5~F$INDgCgV?^( zVreN^e!t%;T>(+L`o*bKs4U8Ba;5W=IHK9|no$j0U&cA`COd-iusYs?u1;WlO3wPVKC%4%3r7wzc zMXAzJ${<8L>2#6O;C4P{t&EVy0x-EF^*?U&kqrS0Er`*+y>Oxu6Z z_E*^cv$p@D?eDVvBewr{+dp1#`*@}__@dRhn146SvHfkf-(>r~9d@|wZ?pX-+xP9X z!)}meOch<<9QqL4pR}yt7Yvkor$YUY5z6&X)P7OZV%}#FvHayMP?x zq-s0<{op@d9=w1g?6)LjnNj@dCrbA(bV3ee(Qqs52S4z=(I;!Ug|~Zt#O|%WC$TfY zcI?C?=F#=B@$5Ngw*NbRuU|5lD>s-E929g;8y?*`ta|sGu1(SAK=*0%>kQJ_3O3O` zL*a9?XALG_4|g^^ojl33Q1_%~ah0QI(zChB(LFhBaSZUZ%`w317Z2&yGr4~AkZ#?R z#}@Afd~EaXZdqn=Qz9ESIbhj@EHSW>EP3S}XSZZE?71*E%S>b!H+?gET9$kSax9BE zVX?pO47e?et&ZJM$4>RG(@*D2V}{NWJIAqerwxuW*N&*}>RGMCEPMRCX+Q#c_bFof zt{c9eU9l-z@%`N3xwkjza`b^UlUpuy{_nLr?ah8uyL0?~Yjee2^eu`f=s?4R!p zpxtrEUs-<^$*~94{G{doMjg5abtnsMlGP&lhr8H-iYkLi4U3zi;~9lmtI_J{wjmPk zLoQYdb-E)xf^Thg!gqwvQ|)KmDcuF(-7~@q5T2hA-UH#e8R5MU?#u{xBfJIou;Z>= z15S1O6ziR{>8*}$PcddNmyzhT+bYAUG{ZS31gE1g(&Vegw?`(dA!-TK1h(Vss# zwsEmMZPeN;)XpDN-E#2$t)J&lFPO99qwDyZ1xJ4NheMw|SAD^iP2c_d51xPhRK){1 z>z-cp+Z+DRhI_9mx@=$1sV|+GB6M z^vfY@owN%Qb+Y@i(B9jn?Q@x+y*l1ubFrgX3svdOli*RTg+^>h@vC?fjBxH?L_k>S zaxwW}Gu}OC3H0Izn_X#0NkeWMqOmK+?#H*EUy);#%&KH3`OfS+-XTc~ zNYXnbNkEHiNwm|Fcojz(^9uZl8GHu$IB3SAKa*Sq{_(7{#1?dx|NDOPO{PZQVCD~| z&z!<@s>63>1HRjWdt3>4wgpcl{M0)_L{l}F=%)($?+JPferq7nTkr@$|Kq7VCmeqA zdG^uIZtl(IhgXkxf^+(m<2kqlBz$#tr{8Kh!JLWxZy(Ufe-1{N2)&y^bBZZs$_110 zNvNQIhJU`lI{b$#Qc&+~3wk7M>|GeXHy_rUNB`y(1L7@t+4#N>+Ncm-dvdj?BV*Qg zv%f51%Bc|D)!Zw2b%`1;-;PDVQ1yp+i4@-%NYgNjW+lFT&iG7IX}BBTVn1ar*(N2Y zq5XS=4;{2xgy9<`FU^jzw{ke1#dq_h=nP}o-d-HMZRi3i`iLc)@NAlhh z7CnXUOw5h3L=HDIWIYa9pcAry7rGm^|436VXU6n;eDCC1=qJMP#aYvbZ2y%oAIT!t^7%!bQ_%g0^%ebYFXUV`s+d*$`S7nz2;>5(_c zhv)V#G+6WzQ;9Ay3XP`?7Fy%Y!+qNE-&!!yBv`@%)cDp7^1tk zC0>Yck%Fg|_*uvyo+bYCdUvChHpYLBIG->JL7&H;@k*gJb^1w39wGSF)-rvaSHJf= z;#uOqs1Kz1eqplUQ>Ghy-7Wo8CA3B_67fERZ%#RVB^=)oNJxAt@=4{DgT~tVqaq#_ zI*;Fp?}R!--5SYvgx1Wj3at_8WFhPOfrU*@9)ljTvZ;12#T!8y_%rwx;TqE?;=woe zKx2Z&rEaP{CTP}y=2@(tuQSVS8aHV2@Xb`0wy9R3Z<*r*Q}}Wf?sDH?`ay&5i{ZJ& zbZ^`gr|&lVLF?nc>PKPaNCWf}d6J?lwCSdR&JQ|@$Cp9pw&kVhD1GI&yi7V4-Qvwm zhXHwcYHe*D=@{Jvo#^`po7$w~O_7e;+P4J_eNXr|CcgJ-r{hH{9eZozEZSMv*T-Kx zBOPSdBIuN-Y^}wGJ|Pyb1>cJ%i`H6pjlSfiG|ADI3>M$&B|D^S;IeU~cPd=i89YRX z_&oX1+C(}3ttUUeHKD)OPktM50e=zU{TcYZ!Y*gxx7qk8bjy!!01wIBkcgA6;dRiF zp4TNb`sS*>2KKSy%#`iHH%U8+v(UyDfj2+;f-U=nL;0@rf?EoLK@f%4O z<@ewrnZHkLBV7_(O)HMyCfxLm(0Hv086wV1*?IVmXh(52+q%f$%~y8WvUep4Nfvw6 zmi20a@mkZtUo%zrYYX(ZcFPXsHG?U3ns&dA_TO2i@}piy;Ha*@^_I*6M?Lw0qlxFo z_2D7Jjj(?0!=i}8A9&5^h_5(s3Ey}YzU_i(w@CTX`i{cy#CJ&jfrG}`_(`{6U%?*~{733K=0B2P{)6}i-$Ans zI;PXG`z=}H?|NClS(fGlj`S|G@tL$pi`N5w3H{xgan0vu?(a`eyGMbGxRgrD!rBR_ByZMV}m+-{FE^chzZR^2+(WKG8@$%;pQ(+`i(>a)?}T{C?~1kpKH6=j zJD^vVPd;9WG(F?xp*3+A9DT3Yi)Tk@T}^gqO$@X|uj&ie$W~v{aTw_%!&Y6;4}!lR zwc}gntcjKIXT6HP4t?ty*|Oi6aS`oHXQ7SQI;F-BdCEkjV}ja6__wI^Js>4GQFjL> z>H0$xWe1;Zo*$m1Q~zK6&}hs#M~nWyQ(&V0yPApdKh%tlU!0_Gzf3nJb)q5b6ZIlx zlA$l0Av5~!Aiocz&HK&G4HyS#)uv0Eh;K5Dw#Ehi=@_%o*yBsHFsNZ%P~z<(e6N}( ze2*%|9Y{}0Sc^$f#s?!mBz|MP!iS2!3j==hPT1;BUh0*iE`Bk>S+EM%OI;DS$Y|ZH=E9AS=bfy87$%R z>PL%Y3Ulx)>(9rxKK}T2S=<#bi;H;TO^{9ejDLi@1KiI6-t2f;Y#icrMzz>&@F~s% zcAP;xj{^?_&p#=BQPxPl!}hbHuEuN7KT;YjJGgicJkz+;!As5aqndtbuNHSjHA{zR zlR1l53Yk*0g!c#SG=B4-A=I}*#xy?Emx5;jo(5XN&%*d}0%-GjX?<|7R+rL??0!G+ zGV~#z^w|NQcqktn{6^Tc)Fk^q4ZI`b-TIszmlQ3lqi<(Q5f4Ufm(&+WN_a8+F2OQG zj3wht3nJ|fq~SKCVGe9D2hSn+mQGH7G5XExpc92jA`K&0$`=`R zlKec>N%-+H=)_PbW#1$AIA=EeDM%~X{w>&k3cu3pK>q1*=z=oA;;f9q{+tmuKO<}k z^ukeNV+!;jSlDuJ5$_NEge@YPJ_l)-1-WjLyGKL)xzeOELhwBB%_BaHn`goAgmi>J z{};Fq;8Nku(Ba}FzNMBdEh$J|iMayGU)+cCS8gy~?$tjokqgidI{4M~=O=vnN2V{H zlki!2pJ+1KE|%<{5&n;M;gru45zd0s>f72gB>>qT+Dp?_A6ys5;uL$Ug%Wa0b8qdMRHxuC9!Wx`!na#xF!ai|1OFC|Aku|7&}CmaCMR*P{;&qppO( zhh;4OB*qWx@$Fd17BExX^5B@T^1N7k6>FyS4Qay1Y!r&9>1np|@O`x%oorb-LE5h@I%&;$SBi0wpmGu2j=&?T|Y&F96 zo+I@1(l^8ZMq?0F_I0=NKy85y_h8(^l&bob{6;VJO<_LD`zL#>lCse-+eT+g(;CQt zO$VJ#_y0gR@}Tho^gXM1Zofw;UuazfX~Rm1gtdP-9WPIPvp)mN`e7$yE*a;z;J*F) z)&k#J;9CoPYk_Yq@T~>DwZOk_fd`nnHaqPY!)~3}%4umy;B_T(bZap=9G9i7R3>@h zlVcZ)|KbOC^)hlcjTLY)6ZhhS?#?c|x+W1;-n_G`w*X|=Iif!dV}z^9{zumugws%F z^TksBM)-+rjBU<>kE)|%QGX71(^!swSs%8?Z;C1asGSEtA2}h_|9B!Y!UM=HS9>bqP2%v1m8Ii?~JJ| zmj^J8^oJcM@@O3@vZt0Q`0jaXPbRWcjEl|O*J8(d=8F9#oxfJU{#<7=2zmvw;D@~U zW~x8DC&rXq-X1R*jx;UNG02={&n5VmyUbZGXHq7e_&!sobz60xm*&Ydo=yIub5f1& zIpk1wg&FI~XJO4GpLK5}-@*3H_p19aPTz<5i#7j}`RjQ8y%*sa5C7IIY4>c0doBZB z(5S!lSh(MMSo~$!>vh393wt7+1n-%6(o?nLB|WV;EIO~?*_W02g#8rn^N!?Zq&vWWS#Q|uTC^AB3U4w}_KH8b z$&e#2U@rTDSICNNvTSY3bnzBfQ%@=BqPe*8Tz$OZSKb(ZHso#i4d_OB$I?1qp_JS& z)_!iTr?o<{r^83VHn*A*>Ocr#@z0%&wAVC`PriWiDgNXnql(sp!V^(m?!a2<1al6b z?A61QVl14)uj9AYH{Uu5>!K5|cO{kU0l=%_!d=QGb$D3PP`4n@9T;1?*j`t{cgPJF z|A9Gg&cv0e^v#(wVI`%@9h{)M{1e0aJ)>nw8)?W|FYYFt{iEw|Njk5^I=!&rn+=l@ z-vo19Bl))=4L8Gm-_#W;>BgFlsz@jD;QN(Hdq_{hEePIH3qLeCG{JC9yv4wq4N1VY zpiPz_T;g}sYxa5*rIibq>@!)|XESKXK0)~N_Y!M{KOk3_w!**Xx=KUFN-`MYpxP@{{hlA)B8YlYV6ImGob5q75goaG1<)4X2cxP z9PaXut|eFotoTQ7CAgD~r!saMt*Zp2#?@3#1GD>Kze%LavK`WnGHN1B=l9jmF{NaT z==k0Bdu_Vw_}!*5qSa2if38tTc1v_AhioA$KRP#|MDMSWB9H6izWW6B7M{Sq#QcQ+ zqsOrZe%jOnPZ+MiLp4|#HT_vnWZ)LnAg|0wkH_S{eYQ+?k9gcr)Ti{KqBSx7hfiUj z$x*KRA2-z7o-h>uQ@W?T6;Izwmyu7hf3zWmCmEvbhI{ebpg;0*xeI3q=Iote<&!Z( zcLisM^*y)963U_+>cy)loxxi~8Bg^K67t4-m6WW*n#QVzQDPq`~Eg8ON1n`KvZUn-p{!$q2Kb0VCmd#_~`*007k40~IK<~AAy?_Z% zbi=O7;Q{cMfmQ=f)kel?&q-^@_R%xZ_{vAdNS9WgOZh7Bcp54PkxU2bG09BPm)rEj z>n|A|^P)Y(IgptM7ip|QIG#lus(l_J33;+Q{ICOM=xx9*!0*x89q9-eBj8wXDM@rB zM~0rHlbt3Jzp$(Nxd~eey;HU%8-{=*dXh&rdmQ=%&SKAr2pdCT7!! z!y0b(N+=Kdz>#wKRhJ5xsWje}VKXW#WdBzCLL)ooM($CTU5!zXSHMH}w$Qn-(B zkTI{hr`ZGj>2%Ro_cW!TYB1ex>WjVn5xZaSVXr|my~td!SdB5=#cx4;#Ltdf{3M^L zLDSPP*}LJhThUkFC3LJQamTA6|3EwWiyF7^a-sageWtAsB4YtW?W81!7IV@~jMYf%0*>=QKyYD^=t;h$Pc9?`S zv8&LREJHjPmvp8)Th(wQ#&TKyNW)OCZs(VdGe>dQw4C&!ym_*O_eOu!Bbk5M0TEwc zQwn=AuJo9zHhcL8$5Uy~FQK(uU86O57k`Py2V~b$mi&z1<9+KXe;j@#jx}4@co@=O z8LFYWgY|Cf{4L|xCM|io%94K7^(Pg2$YBr0H7>pbdf?^B6HmgQ^7$o=W8trE?ptr2 zJL_A*^k#Dk+Q2E`k;0z3DR$YIg8EOq67N}0bB2Wi?pvSQ$D(sqEL?;$ZI_gQE>s** z!`6Jjl1ceP{JoNWFM|xTI9wDyKkN-l5j95oQ92{5J%Fh_wVv1GS+rfKFC)>vZY*)_ ziK?Dh6zRAS{mN!MGqjYn?_`v?4g)9JO_Jvbd*URE>5QwY>u_dX{F?eC?19{3)2H&H z4tX*Cv$lD$t=!6s!Gfp74Dr5RoOLT-CU_|yX&=U8Zr|?ZkZbJ~HC&Ffdd()CQycSA zNo>qZE`GnwkG%?$>OghUvWpsKNz!jZLuE+hX##fSmzdG!%T3zD#yK}r{T*nqze6@W zMRL@t`u7?^A0CErJxlh2ZGsJ#i#{f{y(At)x-YfI48RN9?%iGzBRttT_e9wn{>C7F)xeRhuTkd}FlRd3GO{EWS3=<9X2d%Juyu=lo2f0>xx}<~| z&E{>-m+&p1L%Qmz?2dW9i0`&#OE}Xe^_G0Py0G^8cO?F=`Jmy z{dom@srnNh8T)i1FJuUE&=VCv519bEoC5M|zSn@K!&8t0_r`S`%cM&$H;c8pot4kP%zSy@VeWi%6&Y2)O&CH7w@}GhhxUCL^YrqRxi88Yb z=m?jOeO`uVN~iuOpDfN^L^accb0%Lr$3{1+wy?-%i3K4VNSTQ~Sx; zak3@je+7*zTJ{RrGG#lqoa*zHBHej^DYNP}_?1(oCY9kr_++#G$sd%v0S^bR$o7M< z7xF@sc@;X`gnacp?b-7O+pb-3{(;i1pv_RiWnm}ukl?F(%H;V+@!ptUIZS6uRisC= zWl?>hv7!sMRsYD6Pe7&=(dNH^yy%a8a=<$F<>UP+4JbQ*viI)Bp~ZbchEN?eWLD{9MIwyL9Zb6qBN6zwF8={_w;Pl z`BJ;imtwOx)z+eXBOTQ4l%2ZLRR7G9KZab`TgdhOr7ZRn$Wj659WMzhabLJ4p~VuaFBU~vBU*>SPPFGyXpRmZ7wF6XSsv53mdO5p`eJl0q1rxU zQ1us<)WaylWPh}~C>OfEp-e6TOl6~te4PJXfV435L5mwM@jec_kx%Jg`MlRyEW$9J zK6QzyKF^ZhMPK@S@{-G_ZK67-+O#8n?GIE$=r z@XGr0-uRP<=T7h{*mw6L%w>mp5JqQ3sQ=wc{Wk1moIqVio!>+Ke*)*i(SPSq{ii-0 z&r#JI(@{?T3VP_%59d`x84qzQ9RcV@GD)}d(T@Tz@@CAht+@Q{@Z)SejSr=u+bD+( zQe(x~)FS>F)-3Q0gV`g_<3C6@Fu#Mzrd_2Nw+)VZ=1I~eQ zO5IeJoNwbPy)lfl>6!Ah@iQy`FsIAQPK`AgY|+-Co%_<<+c49+4CO`FrQ~7oOMGVi zus_Z)a8@7f#Ed!x{yRv|v;))Q!$7MPss8c%CA8+t0;A126UN3^Acs5bK9U``_?a>R{oTYf#_97e z9Vy1?_q3C*L;ms(CRu-EFF%Js;1D zPtB|kAD9vAhyHXW{8guBnycYQ;MM`B@Q2aPEydZm zY8v3J!}B&c&L1_mU%Dt3z4WozwuTkn2mH%pihsGTUx60|GjMJjw--2vwkXaJ-~Lk+ z-#u_Y6!FEMo`kl=O2eCgS@?eFG+Ctguh8Xf;2NO=l{ZQk;F<3M=LejCzYlFO3s;*d z89k|Ah)k;YU~b|FPde;{FT-cyNn)(&L4I6`J{9=J7vYO~kN7{l=CQcGXPHcAzm$`e zF9^;ZZ+29Q~PscO9tz@PV!zj^@oPAj@-n=(Xml6{2sX%Ynr=A$N21e^y}Altjp3iy0FHj zLOxR-5`Dx>TMt^7tfqR?L3&bYRbU577v}BdVvQ0yP#R^V!3CF+$<#%d-#SIwOAuZL zx42=c-PbI&`kJdAv-_I+P3jxG2zP0Z>3XNfWY1NP#i`ArGF^@UswPi$40afIMs4~Ywx@Zp2#sgr{D} zpGlXZf8WNZXoXxM)xjbUq2X{r1r-X~Fog?P!4);&rSCA4%ypIu}8l%1xK&s{p)! zYert4KMZgUU^l)kI}V~2OH5Y*zX|*T7&MdK8na^_M(0pfyq_E1)cSs|z4zQJIkQAH zW^bqWQmSL0%Xse=ecUA&ds}mIzhSNY%u2m08}S;0Hx+SuKlhr(p@V=IV>q0-5dM%a zO2xRHWUL2{3t74|oXwa9 z89kD_@J{cmB`k5;?8bM-7|vtNO36yRY}`_RJ_ntZeml-`+rQ=uyek~vYP~BQwREQO zaZJcv3b~Ks+zI5WkBM=|{*+whA!EXM=X1~rxxfp1uC#sBLh;P-Zk>X0NRQ+}$l3>4 zSRYakLRK!vJGfSQ@y^}E^NKm>gsjxNxrwx_RC?bxZxgaUhAhjTe*I&tq37~DQ?f$u z8A>Fg`FN*k`l5;P>-3578Sv@+f`a|-H~IS^ zrsLcIom-&sbnuq+`_HZKMBkdmx$w@Ee*FYlYj`olLN8%HXubP1AVKFo_NiIwK8#=Y z$yX-cFz~*e+DSWzccu2pKKr?w{mgpri_h-&T0CX`y>M;(#wk+`{ zj17;fav+z7ftUEc`jz(n3!OXB>~Mx~ymLGAMEp;+M349R#Pc!x*@^H3!b=ma;q*-P z<3q@k_(9m~pb2^rAJOC8+YEY?2RtLbw0sIDeh=0oBz^?^M@*-BR96nYgLnGgRkQJ) zo*aBv*S~jEF37+EFL0gtJHpqmI4bA)E!;J6i?@rQc}=Fgp|!!mVyo1W? z4X~lHB2Rg2(5A@3wfnp!en3c#_2;pQ@_y zX`JL-Y0G5>-cLuoe*Q`N%mm|~U>}L%J+A5-j$@tOsrmHdIKOdRWl<+jf?nsJ);EcD zBCK`sPeD)NCslpxNg3(Fns~Mb-LQXX=_Y6BL^`@Mo=L~NjOSGSW{UMrY`*$#LjQgr zc@CvlIeHPjyPNL^ZtPJxLUAeCWocTCXfGjr8`=nH={;bF()zA#O75GW1s-MAdT%t} z|9(QB^sX$^T0tPYEsrf9VQ26}z(eoIVsIeZs{ty#|M`^4=sp9yPfG8a7U4XZo9Frp zc|OYBGvZxfx?{i#oCi2f@T^hko`YF5UsKoAi~9{i6+Rj7lS(*?(m8M*ABQlkcZ;^S z%;u3po%x#$y9ADS1_G~-T_e*ws3Z?-=rch}_b8|mXrC8t#c%4Bf?Ckk;+^nXH5>1@ zOVQdoR`&w(eUq6Vc_EQyyr4_Q3w6v`BcAVn`Gt)vf_bO0PCR!)I5RewGen!S!p!%- zkZ>5ac&~KpMixD7R$sVP)^~4}rNGO&{?ZGw1N8bplK+Ay-*_S61b$w@D~A5j%b*i_ z;QjU51ktj%qYO7_^8@H_A@^?`$#ulH!Y*xPBMy|`j^*Qhc$C#z5eJK&$C2JGa5WSk z(y3oeeOA0v+1b+ZUx_c3CQJ4Uuu<(s7AG6k%KA%tE*wu~3Hz}0-yw4O}{WTJd8 z7j;dae1V*W`Y}L#p%&#p|-h z?-baeS3kZ%u6}BRvGA!4aTeZ#y_7FY+<{FSnL*`qldLaXhqH(nFX8?7Ym{{c?k7>1 z-kJXzeLQ5$cLz3X#F70RedZC5)IzpSk zW3@Nc7rr3t&ux-%pNJJzqiZ5NC~Bc`l}Y3-9&+tMRZ1@-eb$3tY`N ziL)bqx_dyz{V{YF1$iRc>_3RLcxpe_Bsw8Y&d>&(Z2riib;goK>*7qe@&xzHw6)`G zwDFWLA<7r)&H;w@eI&{NWc|&eU$>EkDR0)vI?;#LdGhsj2}xg913t8MYZ4vN0f#=U zW7?Gc^x5ms9%backiW*0A6k>3{7K19gK*&=DW{vTqzuN;k?2 z7NWYeCP8&+h^QAW<{B|~LEnuw2Jddpgl$FNNw)nX`d`os+h*Ej5kG`Ic>Z)A^zf#`6Lf28+$S#E$c0oS>+qQrmYtC*9 z=hch~HYkus6{J(-0 z@u%av2hXkXJ4HCfSAOU)=6CyWR__ffUhRk}`w!AQ8*@DDLECXZ1paH{hijSthzdOa z9_*kb=-HZQNP(j@z?XGPki1qL=&)9hT~=n>LWXWQFNw6iTJPYm>&yBbG#(#xFwV61 z(FWr^oy5zge&IeDHu7C#r-$tI7}|Q!&(}YqcUum6^0k8rNjq4>RJvE^b)E1I@s3Ex zVfpM}f~iM5`Tipbec(ZvX}ipP({4J<(ST8Tr=;8|Jfpe^and;Z%>*;NB>#M<9<;CiEeo)tqJd%DzOm zvxEyN z?D;oIJ5qzZi|N<5=0hgVmSqatYp|zwaEr)mJI`1f+-rg^Grfxch#_f5tvJ4iI8ZJv zn^F3moX$GH9{ApqwrchXW9qrNk+bMBtiCZ_>aU-h_q(6u~Q~ZDxAt! zre57T&@0nkBqL?(py!Av51HWz^U4~{!Ewe*__)uBc>Gu!(yY2l^;SQ5nLNl|J3@Pb zvpm#Zyl%A@>c^PtSnE9f<1ZU5QK1d6eOgZ)6o18Fypwj7@Vjb0@=+%lcW74%TxTuC z&lK!GWBrGzm|v@lVDra}JncOGGTMgz%5t;~kgY6-eifQ#`=z#DX8Sb1ze21%cG3n4 zzpIvNOW#qR!aCOz9)H1dqs&k6^_ph;k%A2d?(g&J-~Wz$1KMD^oA4(4d;;UYFde}S zI^DyC4OaeBgoD18=vml$mN0hzS(^SC+_nRH zmu9IS;kbv8MI4$~Cq)9vb zT~Ys6g~qmP#J!eSi!!gsSYP@(Xx_KSv~J8PFn)*w7plg)OA^X7!qU7nt|b1{`~tK( zz2ju9anbrxXZ{KLhSd6!973Mp+3jDmi3MriLF2;5yeWR7zZ&6^wr2j8pVGd>Vwv3l-87|0Z3O*sq7UJHRikrqu6hGqAe+SyH zitByI^xAO|{U1P2awEYt@n6!pI+QJ1yOaFu^ea`2zcC)Cxe1N;a_GLcA3h)FwKTf}LHw~y<6JFeY!Tx5rg(d&Y`w{z14J3RUMtqDJoSe+V4uvv?-l&d z*58-rf7YaO2tCmLJ3w!>*Lq|V_6!{SmxBJ6^}kBf|I+lSt!?&I1V<{gLBhXY>yi8g z&Yqq&-}(`yv$IGi-iZ`@O@F@OsaS6CDgCM7Q}I+??qatE=;zEfdoii~L(RdjLw`#B zhJtsDDG&OqusN++1RdH?d+gsIZ5#_!Xvah!`h+S4I`cb-&wp9t@8J_)D$I$nXMl6~ zFKQ&eRDA;L_fKF?%ff5vX9sW3>j2HK(2rsNr&0KjOL{>+b3le(1B5;#)4}fp{W0aK zT9gx2Lf+@%*%9pAcscclkVDT-zx7Odk~~-*F@st+Uq6eZ;}rpUsjldM7+}>D~C(L zTjo2!f4(tYSAsapi*vklzl0rdKQ-R{P3I-ffAjkhXJ)x$3A}TScWdb`@2qGKUVTQm zlP?60Y)|KhSv-e7g6B>dWtS!HHd$2SONh>p8kcn-;3Lq%;Mn1 z{7KLh<9+z2Nq34b*qcju?%IDY@(O#+38}0^1Du$v(jC-mDL!psy|9h3#5@OfX3=Hl zb2dMX*Zloof=zSy^CJHH>hI})zri@!n0&G^$ve}=G=})KEytok9wCieAjcWaX?%&| zhK{oZ{}4?P{#^A_{$MCOu`oe(xtu%8I>KKI{7-CpO2_XY3v{^CuPwj-Ec_L~lP-S% zAKn>n++pi-kF85V2f9>&rz8CJ?R41ijJz zqdzc@X_V&KD&6n!js<(@-iNNj7PE1O_3VU>=LNWLFk<1nR5s_m-HZPPanijEAEeSG z?jx9J-UfQ+m-t5r2Tv{Jds$rK{}A+(>;IXipNu|qk{Ja3JbtfNiqEdohe~pT;1lHp zcf$6NKNEa+)qkGmy9@K5sp5WGisw{DJh=P8DqB|Yv zo#PxUEh?o&fBRCNBWP#VcS+OEM7uKs@7OwhK}$FN&bMdBZ-aaX%AfwH*)qM$QW_-t z4NlL+=HQ;j0wJrl|78g$?*SaOC(Qqd*$sKl0!z$u?YJ~sMilk?vo9V-nfcNj0okNm zFPnzKKFvj$T>TWve5b_R z_{AhI@j2uZzr-&E4d8x``0Q|Ye0D5=cmoKd{OXS|DK0e*BsuY%#;f3yPIGY2={11q zd>BSZxQBJNF1d7spbJd0C(#yiTOr}}|=Fvw2m zH>^5DX_`uWl+VFOa9^>-JMQfzaq2$;e1(uT-Of`n=MTOFdph|H@RbFrt_;>l7ucK6 zwsCWXO-I}K3;F$N+9Bw_(JlmkXO82qdTAaX!aP2;E-=W}3pVD};6YmYp^oXK|2T>R zcSuv&Xxm=SmHP)p8A`XO?zq*ijxp!Pb^d@?vD?{MkV9!+2-yoDyE9}{{czd!V+!DG z8x8_S`4jc#I+T-HusiY3f*z^%nRMy|8969R>#2P+pf2PJ5|dtZ%?x14Nyht&;waBMj{~wNs+KBOCR2cXq6s zD2H@E%-lqQ9k*D&uX&9P}{KIXOLr{$dsdHw!HdA@e?dLjK!Qd%$< zw%Qv@@26Y!7UgO!XyoI$nU>!FVp<}|m*?zwYA_b0KFJk8dAWYJe9Z2L-F6x; zgie$X)Hltdbs9R`XZKBXcQo$z7VrByqg7!uLU$Gn_v+{LlAnjp4nDO0d}WA!anB*~ zPRfw@uPILDb2B?y9%G#GEueAn@>gCWJnFxP--WmJ?lvF57!&x~&1>n-;~`$U3z^ys z#@CzgLAx-e(OAt@B2k?wyQyH;^24xJT~+9hClNO4j^yE|mY6 zA*;VF>wqnb(vpRF%mqfP9;r5w`jA7UFYM8ZyBh70i@ybYX4|M*wosWO@joLB&$Yr% z%5$Q>`-^SYl$=ao%%hB9s{XN4{vbowfwrz6W$5aJuDzhElo+b#DnpQFN$X{Zd{A?h z9R3gRg7%KLZYO(|O`)wS}N@fba8mG#6&lP&u^E z7Z)UN#oQYGVsHZb#fg}+PsF!rdda82@8CbEKR+}9UP1I5+&XR;kf_ll>&ipS7ved5S3vTz+L_r zBT0ctb!@>zS<)xgk)3;hcW{R6b8XqUTgpiBiE>GC`zMIHKIFa$F-sRJuhy85-hY;G zXZYr|bWeA=mwo7yaSwQ%c&|M&!t8bG#t<_CC^MxP+c4hyREu?f&G31}{ol|7ytTyZ z=H=UU@T+(iUP2kW7-5v|OKsh#%(#&M{q6KJ+>M{I`@H_64dx&0(`|o&?bq0TO5cGt zuesKTdIxOp>Ut4hWZ=OjQ+hZmW6lr>+E#uANl0P z2*X_jWfXpuEq4{a+C%rCufjZn@MFPOW0E~e%v%KP;nz^QjFS45+<8UB<{xbHM{NFx z%|BsZieED)3fRMk5PwvwUk!ete@5I9ew|1E*d>ny&1!!2Md6hGHcbt`+MB|SwQ+y7 z(uzCQ8NUU5Ieb(-zCGn)kp;Nx`%w>#9ar(Co?|wh19THI>F_@KVt%ui`Ln82z}waa zkQZ4aNVkc09FU3m24uF<`ZH*UO|t12%FIfBvuAtbjfnI6p2g5tg+0mED{b4rZe;|M z{IFTXr$QIpqkToD?FqJQ;gzSSbfr2fbU1sN-Ds5y&w9kMLFBdHvk~xn5x;@E?me5K z$2&G`kZn;$3|{FSoOP9qGVGyp(Y~D2fzE*3Z6pt6D& zZ3h{3DjWCjPx`VvevK8M+Jz0)^%LbXYjBpIuz9KPnhV}z5mAR!2V~ukXV|g6n(7{G zw9u9b8mgm$<`JSnm@6Y+>a09$lP~FTJh<7xg-ww>AxhEkoRb zPSkrBe;o0p%N63Z<2s|fJZbBimf1?%MlWhhp0?YPx2=5gcD4J(d}T;NM_q<(eDB#| z5*v)PGv7wk4~dr>l&6t)VRigEuZl3rFDi#?+qGTKrQ0rQHyD2&Y1+KjUyE|#b+f_S z^<+8Pu5#?HxcIMa`%-*l52MM&w*a>h<#fHR1C_B^}G_aOq4s&2{<*beu?aY z_O4Z45&xYX2if8ewr=1P?amI;9cjpni(t}aa}(~7N$Gc%cvE_1wy}HhO#+5*^TjZ) z*<65ciqhR&8uJ}f@(iZsp(keikC5xsT#W~8`9y~=Hh7hSLabF5s@Z|U1nz^X(}yVX zAxe`br}-Tk#uebByONZGBCHPCJzR_yxlfCi>LUw-rM|K%g3fx$SbK@NacLH}8wBswPBc6YVINVql zw&?yzaRduNo5@>P#{zlgW`Qg6>H~{p7GyY=(i1qLis8rP4jofErEKtC z23=-aQaW~N7k|NNYb;^e-R*S;-P^ZKaj)Dy#T}ENM-S)$z3a~f-!Y{GZ7<$iQ*mbz z;i(?jI=AQRP2nnQywIk5M{ygew6LJ+8Qd-%lvhZ*=e>-)q`N&R&n~ugNZEHf>`U@G zqGv(PoUu=foe<$=urS6x(4BnPnM3#b7DwZOL)_|^j7THsp?d~1RKKU;vl;X(IX2;W61ZTnr1)HgmDD@K_0 zjSm0{18sut(hzr!faz>Lqq{UnE`WY?ZwK81a&`w*glys=D%J;_LND=)n<{Yig%`i% zgig>4pA*5@JxxcvB2M6hFRsA=U5eiJNqnobXj8nIJ~R??QaBxQw?nTgxcz`P!kKV^ z-j?&iFNdpwn+LZ5E(Z7E1+W?Xaq!7agCiU{l1DBHSL9*L0~dx{2^Y0p41SaCn&E2~ zTCNQKZE!)jV{m)n!mxES{PK$|N6#zqJO|I@*2AraBm74AAHv0LU+xRJaCgGhz%@gs zDEz%}D(ueT%karzRfNUhyvPHRbsJm~@SX78a8+;w$LaY}*a?0UT=^x8mBB55BYfQU z3jsF)_T$Sa!{Fw@QFtBv{kG%qiEa#>>oUl&{nhX{!zJuz++WHTBYYOzJh(7i37iR6 zemT+tHxDidw^2hH;n%St zI*_ly>2LwKB;0me2JsVK!yD#f;7SHsZav^SxczWNz`5Yua7}Oq+?#Mw@W!CyoGTy~ zPQmkj_))l3urcA2aKk`5=Squy&fUAZYF%;H*Oj}vt_H3danql}c6)ILOnGNfVOaP- zr2M_W&&omGp9N3%w0lnCUQ4VCHQ!Ja!&*$7H58YyV6!>@9?bW#Zpj+Dm*88YrsTKo zro=aNto?vMktv0+Ms!~Z_u#DeW`5%Uk|W;;x)~&g?&NSoK70-9xR?{SD8pOto?+I0 zM0w-oUZ>x;k?!TCJ9Zz$dMry6nyy=YvB+4gopLYMu!fp>x6;1fFf%VvZs^aI%PqV- z&iDwdZ3J+4ug@6P{Qt4{KJZahXX5vr$p90GWVAt}MSF`Qjx@DX8*8lTIxMlof@@rH z#TA#h;tC6{;{qC1TrLnLBH|P&Qe<^j5h+qTrCFtxpPQ{yq%EzpmRf4rcB*W>M)vK= z7%&u+_xqfC?qo6q)}QVEc|UKXm-Eay&w0*s{@i>1Jb%sv&NipG73l^isRVAqNAiTE zE&eL=A1#4DRsx^x*0wfqmqViyIgDS{UpIt+Q#A&z1$KTN4b>*WNj5sQK2EYRHQL7k zc77cX)jHrfjgIZ(IE|^Oj|1%dx}LgBbc3k|XZNofGt?!vPluCZyMNWdSqFC;f43zx zS?ZCS!a1GSW)^$)dX_-(Q+zj{BIu@sbLN5Tl zqxnyvKWM3T^6T1v_FoI;*a;<)HpC-Sp3S&UE%wdfxeYV#Im$&MWMY#>D}FP+CSH&;{6>0C!_VFkx9<9yZ2bDLN|P?-_qW+Ft@kq-KMqsz z<1kN+h)vlq)T#zA(&gCGYfu)ycTz zL#HDPE^xOa=hx|F`~}f>3coM;_jP~xewVCw zg1uYRl^6K&7P=_)Z!gG&jjqYCE>GuVXvPbjjSD=V+$qnu&ro$AK7E#loL}GL#pk^E zQN4H8$^AG&Phcy5H!{%$nTErkR8e%ZrOalX4qrbG^nyDeE||NUZ&9TR5wFKxmq!;L z7RFcAbNGX&F1g>j9hwg3vAG5}kgv})zFnt?Bj4d}?$9yyy@YQt-`Kp>JBKuRhrPRy z(e3KJ68n^^wmbEghy43b#LuU+ou%s|da*&}k}ud5EQHa$t{{vOHW@qi=E6U_L2O9% zonyHjZnF2=@b-PT+30!u=>hCZbs9d32Btj+|KH&;c?YCVXjm)scz>D;^tIF*c(N%o zR6^6on=E*S_YL?K9>F(dlkkF%>}?0`%Im-rCHCn-2OPSU{9@2+k9eKs9_+Ki2-Wo)>4-Z8NU*#g}> zcLqynIa^EU@*O2_#?Z956GZH8c7fM?_-S*Gg-2_b!rzXxd083zq>NNhoy`2ak1`~* zQUl8fQN}3a11)6A_%wJeWo7(`G6F3@HA>GhrS(6o>o4U`DJx%@I(`Xyit;5auVaMr zxvNLFTf~(A4e+LxmG9SaT3H#rrj7^5(@Pl=me;X{G7gzCYD^hYkJhp>q>lJ+)x69& z{Gjfy3VWp!imY<-iB;SKw32VuL#&7JwG)@`RX}|SI^ZlR#ldzeX2pX#&B}kH*ryft zDrl{7tgFP9Y?bFDA`xtOQu{SH>iT6_2uG&n%^FCj>BgN zyU78(s(`wTd9s{W!Snf*bTq4u%WsG_@N`Uq4L6v%xSVH?p2znB=q1be)@|&6w|Kw4 zA!YJ@{R`p1_v$HE{FO&}uSh%Qy^4}1V{YEzr_6ix6nDD3@47M`cu$5fueyu}39aP7 zF&`#xrJ1|JG9IAQRz_#P8M{}Ie?N32EFZg7(7DRcse+EYAMb%TupWnXJ*13dUTWtwWahsQbB|Cv5xOhmx1{JY|RS_hDlmRM%;|FLw)0 zW6xvu;=)78&x7ZG+wtq372Aiq!3#Wo>yE(IO7)M1_C-Cn*@4W0PA|N>)6=_N>{^+7 z+%tRbOj?=y+)&!HQ~8PHM@ReOp1J7MFX;3_$s_vLN3 zIPcn%EA+s8eqcF*^LWNm$CedpU55;@zdZdem6iFe|GZ2`%Jkb8Urp*~} z$qO&wQ=rpNPm}i1)OWU9yLW`f*E|=px`wMj?uEsc!Y0^(hotwNtWG_f$5&Uz%8XO9 zq>Rk-&)OTrZ`kvpd;BsoV-zqtRL{Q<$Uoodr|UA(&r`+=q4B-&!g@*W*sRVU&WH6F z8?L@3G+)1GN8ps<>fdCJWL$o(XPJFT&;52zaY+n%PSuHxtZg?lr+z(l*}*50R?jzC zhaYxx=xaz{a=*^p>iC-1b)Vt0TjI`vR&{*i>z2LQ-DmQJ$XA_zSo+o(&%FBz^8q;d zavY)k3bX^P0dIg#PCYz!55Heg4@coK`5O85s7Io&@w>X!<4u_|B8E4?7e2l$e6&3J zy7)J-lGuh3_b)omOwWPgVLpyAE+HH6m#~fPTUVok|DUR^;Dzv4&Rzhbt z?X{QRuhKs7m;43!_R?OzC~2=gQ%0@fP4J~{b_gG3?N!im9&blXzR@M^<(J{NS1FG0 z_6wnH<|O}{bph?>f3yA^SjOG)zFD7TJj(a#-+JY{^+55vg}<5;ezzJVUsr!V|J!A8 z2SfB-=6QU^wtsY0HjucN^AvC0vE&P^8}zx%GEUOocLR}CSt~JIbsOJGIs@Ztnore&SO@3=Q5V}4$D3$(!)Lo`;gSn1HBh87t4No9lG;U@4qnf zuk{o5JqIckzr8acvu|3>zDfMZNLhX!Xp#4@i@ike-|4yiq3*x;>0sMtC3p|1XAnEd zvv}W}vCBhiKe0UL9K0_SvAo}B9uh$)c#&IefB;#*bxXgK%0fgbN09a9SUVu^dW;(I2be%igN zi2G9)AFxH-se)_hKCf|q-u*%m_vdbf^TsCeeZ2T)kM^_@+T9xem)-wQ5&xI{`13vd zR`3%&Z`hVoq3&gVolTUeIy^Wu@H$(VS z?j&!0DDp;Tnn~YPk}hdONoyTEZ|1jh_TmhypH_GSzh{G&dz`$*a;Kc2+~e?!PMUm2 zygsc?yq&kwpHOEohX&Le-DmlGe)R_P&>ODv{)V}?qW;pJx9vcB zgxaj=I{8a>G5^drE7-eE@~dD$?zgGU zeP>C~dNCKu?8sP=mtrF#FBR$SHuPRCqj$cc2OaO7$$iK1^H+X={60V0H;lYrC3Tbg zal)DJY_+TxW99YRWB7Tkj31w#UqA8LBK7-YLce}hgVb-Yq4$e2dVc+&Q>>q#zqEeG z%kT43tY6Ci{?do|$&o%3zc7)m?<^_rH=Wn&@2hI&JgJNLfAQ-gc}n|6^GhCZi~N@3 z_;iar(dL^Dd%R4fj1R)gBZikng#Q^Y;C^skO#93%yu@1W+t^rrpD`@q^Lf^h9pYc? zi|^oYCUd#VAp>CJ_FSuj+d1uSp&*y5pUcgTH`Opl`fo2rG z9TVHE|BT)oL+@|O=xu#-aC-Q9)b*Azt<3xS8`cMzr-KLonb1xL4?dyS3oms)WzGY{ z-|&U;+QI_yZ@~c-!z@n{o906_s0muPr7%%j_f`06_@!@%sF>NNN}X?+_U4gp|1On zVwL!0k67kBS%2rmPn*>JWv}k>{<=?RPL#TThOjpG{U!KlqwYWA?BnIw2;h9%{vi&yS4X(7gMeMjxpqJ2r+4irrt7-HR8owt=i%I2KtMKemg{*QyAvOIsEqf z!{Z7Wc81nv~UoB#XK6vYyk% z_;6q0G=2YA?)sXX;C@X_jDN(;`Etdboa#T z76Xyth2I$5cS>+AXO$a_3|G?x2m8rZW8-OIqc=m~)JmN`Q`RvfK8KGkLiVtX-3IMXCr{W4ZAF-TRL)Sakr=MFsM#0zduoTh zbZ2d_cQs|OukJo({P7;Fm9+fjpOb!F@AI9%j4^e8%(jH~8t$72f1L zT4X?-YMCAvdp~-w9v3qH^Yo33|9vL(-FzXq`r~VxZd2|e zEX;O0H*v@0dc(!6%4R2DWj72K;S?GyqoKbyC(?~}F;8yh1lpJ<3s@6Jtu-YW2i43PER zJAlVM+50u8JluK1@?l5wc+EDiLeteAh zX$SAr=QDM}TmD)Xn`;#({!-eZ-mjv7~Yq~b`=rD{4J4aQ-!q~)6RJ385~ORkAF$3;Kr;C=wW{hlC z-(`z^k+~OZLt$b=o7i2^8&k>8eb*v`4~xCD#D|jg87mh~g!sLsa1rA%)GT*m3yzJB zPG^VJJ587SvF_GlxgTS_FxpKqhiocLXh;~m8Q^UwO!!=?aCYouyv!+%vNwS)uAfYwCK`wGbqe_NE-4whr76nRz~o5>A@J7WtAce$$zi!?rW-zL}UF?v_b z(Q!)$jOB3Q>?rM%3G<$04Ug(RJEQn!ck-Jw=IO9nPZ@l#|ETxy+4_6kXF@4Y-jjvU zwtJSkR!h4(>>zb&?O<$3-7S?S@0P;3x{Z%KVrQj1erx`$_0eo-MT~RBXlM)VTWQ9P zH@41=g;IKKHIZ1;j3-Gi9a~Ap))vzj?Z2&183SL*IJL$0kZ~&G zC|x+4bwM;XDi!UZjnH>M1C_Swgf?T^oFRK-TE>Ru#MCy@+MHNnJE8E>W!j*0EM!fZ z@Vkxgt?;;=aJ}em7#%YIUEZm2FY5N*;RfW_@(y3*?q%Frd3lGA^8TLdcCNL0cYzyF zAM4JUGA-r3zgg;%FgUw4y|cUb7#wML>gDbPXO22Iww>^6>RfwRDw^*C?&RH?alm_j zOW)&dqy?b4ZC!NxF5plinn7vU<_`AG9KUxL-^p{`J^bFrJ1On4*OcqOQ`_j%Kq{>A z@U^pW(YOrt>MUI9cJV&#GVjyA_db)%6(;Gvbxr=AjOh&T&@S^1?dKiJ>34{+USLj8 zi+G2Y#{Pt0Iz0llS@a}<+Y>8%t4}R7mtDN)Im!>GW87OF z4WaCwOdw+*K2{n(dVMsP1@B~omp~U*aD;#9bAhG4+t<~q5O$}#c;9j+UB-aGlAc^! ztHQ>1b{jlY8MyR)lQFWVa4u`0SZq`})PxzAeO3Ih_YIn(dW#wk_-pq0Hg}Ls&!kw_?@}d?VK9u z9SyC4Yp26zZFFty#$Iq+X0aX;+-tIdKdn?Y_yPQo`Rk#8rbAjwrHYakDqK4@fUlDn zZSAdtymIPD8*0*I9fZF|w{vEoH?HYj+g%U7)X9RL+z}r%=?$d&>!e1($zRD@tx_e3 zxA6JcWb$1KzSVjK>m>2h)yDVb?&5nP@;NI$i+s02e>!vh?QTZumYfg%^w_CA0#i@Y zI@Fe@M+>zNP`1a-9i3?{9wKDIy3Hip^m%Hho z%nT71lDNg+of&G{JS)@0n3Z*PM|zgt1L-w3{c3NSm0d`Ao#?<^&@&rZN~Ln3>!{g! zE{<+tzp42;>iXYI_J!~;3)xRVb(nX_O3Zf^W7)Fqc2~U5oM|tH@7aT#Q_Mc5Z-keR zrDvstUW&5#&QZ4PU0DC8%huKKyxO#nH>L!Sbv3yD_g3}_{x?SzCb934@pM;A`QOgG zS%0Vw;%CPa=~-EedatGIusv&uGjvvHJ!Qx@iQmX=W`$x~X5~&!-im+qS)sH^^V>uA z4rirjId$M%O8dqMt@JheUd{R=6N`6f_TQ`7ALrATZ=27v`0j8nIytmKxBG-D3Fnts~>Gsia^8DVt4 z4%b-`Z(ZM5j2q#_*?pW%omOWjYPZYUVDXf`(8<01X^A6uYW-)e2aNo?Y47~3_?Yb! ze!9H7*|*7fkZ%b4yGqf!&D`pqB%#qY+Fp_#FzG`|(!1WTW;{i_y#{o$Wxsd6Kg^>q zdlAximHp0{@v@tIHZ~Tv9qzR48ns7wv?JYng`V(d^5@I)M$mWz2;&R zDQ)6~f@f9pQyy%{66?K(Y)38Z1g)_=|2LuFnx~Wx%evIN+nobdFao{}WyZ0rttSx^ z_MXf=URaPgn|s*!@8{ErhZ2y>J`9 z9qQ*2b`igyc4)$GbeDOWh?989BjJV;c&5-jkB;Yw@-*=@^Blo0^ax=+T5EOVJenPZ zG2-QE;hD$N$x{oC;7cgacJj?5TrV)sZu9IX6noOhC!xs`<%#i#9jQDv^^vF7z%{_N zJPDpAo>rb2Jga%y;?N?L_h~iHe(LOCGb(nY`*@}jCy(GsdJbH9s;~tOna3r53r`o% zZXU4{mERc?_5u62G2)te#3r?kr-NrL&mJC^XA@5!PeNZZh- z6|gE#OYp6ceZ+-;=K=Q`xO3z?Bj*seH}Ju50%hl+M-yvG)?1#ua+=oNn9X|WwmK`# zdOhQvx6Uv&p>h`8x#>&=-rRaM@pHuMbxEBGxAo1{Yb{wDuO?3DNS^KFNtryG46a{B z*EX+=HmzgA8po`;WG&{|kFoYManW~|)>%EiUgW)6*HPp?mgqd9|EFzHIoBA~dl$Nu z@3~p`9`13kXq4Xpb$4cG-`bpz{ril%!%V^bTor!*0)^oJyOUM^|`%7r;gBEy2TBK=O*pqte zO2gMQP3tz=&#QB=%UdgB&*{}Kb8E)kM*l-g_rKBCs54~bE0@Gu2j{QBc>_Cehq0elQ6h$HV9;q`cV1;0Xc zUGqz^nO&A02Jao@8&X0`&Pt_Bp(FeV4@a%#S@Fv+@qRlss@v>E$VVdFu`yZAwTE+` zfZVgu3|whoXXC9QtkoX|+A#^7KYEgIC*U8}Bz&j=U z&c{A>sXfYC;_MGE$<<`;VGp?^6wY~}v+?#4J4^Q9?@lQ0g{riis8a1TcC`3pj!C`L zHe`*Awcc&FX8ShXo)zB1?C&bkJ+|`ig+&Kh@`f_vSGS zu87<0x0i+Q!_{h%&EB(<$;eF=;mfcMz7#v$3AV`G>fs@&uCzzQuW+pRg~+rn$M>Um z4!Naof4@r>d*o##xqfp-MEQ2J$3ciPwOfKSWDFSVRCb7tI7TdBG< z&XwKSA`a(Q@r&Ii${w1y%4uo7DkpZsE$SmND>Bo)ydY)X=3*bG%N80T^gaUW>h7@7 z*Yxy~ux>&P@vmhPi}t>+6foxSeIdFUq%(^pCQD*0*717faoS~N77{BloB zi_x{<{Loz?aO;`mIh%evjsC-yn_9F>S?O3e`GyKC`8rDS={%ZlIJCi}ZyJz(jvKV6 zIW2AYqi$(+oI7p^RkV7cb4LW7?f$-QRMRQ(s2gfyKTe&Z(oUI~?t0=YlH5bUoYp4i zkp`|rKFquxCZuliPNHAHIi{7Qy)%_fqHhp7B(l^YlW#EN=s36u!3}%3%|fFJU)gQ& zkWka30pMo(B!RAv@K=u={6F|>hYN|}Eg=$&Qe zdaH0XyvRDlJL6f%`y=OtgF@2qhaKJfA6TJk+zDBs=;XOOBg(Mr5W?^Y+qJWYH|?8woXX%@Uz z;ubX10c<{Brhu z=f#l`$b`1NICA%J3l#>hd1M<|$+Kz1TO%`s61@RZ%-)oW3Q;^EPqevoM2D5 zo&D7czUSzr={@0E_2086+-AyKL3!|Cm-BF-R3bSz|-#}@wy*- z&A#=<-u`o#y=pdgg0u38^sLUkTF<-x_a1)lE=k|5bpmI{#&f6Z9@ZC^5t1IeoY1dl z8+!|(BWtULz(OMj4axh95?Z2P_*%(t(eZt?nrx7-J%2I99rtO zA6n*i9J=3Kap*zTOLw}duYO6}+)kD~*nx-67k?lT)<+R_SM(IMC~D=A9p~=>_dcEn zTsikyZSw3P&yWFm9>RWoRlYj^<^1An*05jvJGWYWIe!ZF(ABN$@{W%Cd+wuOlXoVL z3q{n@u@UUJb)JaCjUjH?*i-yC+rv9W-QOP<_wY_p4~`XH)*4=Buz13r9B&JKo6@TwV5-P&FCS^{lS}g2RCD@yD{YPyw0u2Y(#fzqr-jF86NC> zH)4nCr$37Cl&5U*7xY8gpt|*$*Dd@ue6k2Xv)#(j2AOAAZx4Sbn0e+oEA!0u%h(47 zG8;4C=gZS2-by`=J?u02fV0&Ji=T+Dt=PYArQByiR%V@Bos@iI)R(24q?9>$IbYM| zY|eQ21NjJKq`s5`t-;HA)ZH{|V|L4e%~`qY-dVFTR8M==_dFE~iJd8TIEu~Rt!8eL zutEH}99V>Ju=m^=-t8K-C?;)8h;NO%^A&1wHo*B9=gKUZmte!RB<~!!o4Z8r?y%H+ z_f&&-s#>bkDy$_Zz+XVyFJiAAP?xjUsm|SpUy3p6t9*w%c=>kArp}%X!aG5G`E!=N ze7o%b0``&&IQjB)iMK4i8>DeFt`qkB{9q%tGVh$TJY?B-Fjghs7q5*C1f9qG6Gs_3yNzN?4 zX8J_p<;?O);^oZpt0rE)(f*lbIJX%6mc=3G+2fy2{!_2lr*UjGWS@Efdh)G*&(Mqa zEQ(dA1z8DYzS(x@ZeaGDeD~{#3v#caH+H=AxrrW3Z$`&*&2sP z5F!nG4)q_D)2=QV=fkxy_(H=i)JZEH_$>L|!dDjXFuH+wU&Imr6TAtXI#|XRnuZf3kS?dI|G=gDdAVB7SbGdz4k=`u>shex=?C6C%+&VEO@M|7S_^$*A3Rl7&yRauL#<^K_S++8H$?nAS? z^*Ja)lJny0NSE_sp(Q%0?a&Z?RT(!HzC}5QW8Cn4X}Ly^ z8-Hx!>qob7lrds+)*KUj8M~Yz-X}C$YxTLXz|iY{pm^>px~m>7n=*s&pPqV6F6y0E z>pI4OE97jO`qq&q<7odo-g%^zM}EM`;48AW;hvm7CJfrwtO~T8lihn>pm(gqr)PFs znK_K%xv_e5T^rChlymh)<~=KADZwd~fysIPdfG?$&!2ZqcHec}bg{ zxhxyBugV7OD_AQB)T2^HJW%+X;U9+=p)F-X%d5``_?Eqv+{f+n znwIuxnMpr;G79kk+O3rlW%b9tE^KZ`d<-6Y?^G3W<{fX=m zB7wqZ37gdQ<3w)IB>jmFon_B)d*yrLBwc(5X?~lHY@r#P2J@}iWcc`dXne2Woi9HF z{zCKpkhADDq=}4RUGc2Lf2DX9C``9 zWxp^q>V-bOnVr+Zp@WA0ZwJu74q1uZw>>@f71<;9AV;7jzsOuUEHZ6}=q`U2cqX!g zK=MZKdz8Gve2=Ev#AVSfb~)!a-y<*o@{;^_0M9V_^|y)q-nYrm&$o&E66ebp8hWP3 zq^_|;!{`SOjsi>E=tJIb`_P1jl#wyar@vFk82tQlPM)VurSgpZMxJp-agOYleU#v8 zd4@9b42c(cMxHjR>#! z_y9V&cOqIO$T1?9r8d-_spHNxalNMpG`{2=)jP3H@T^k;z4Bd`INu*oio54(dM0uY z?dhQd%oBcD`2U%UUqUU%Xb+E4zvj0u$MAgv`Em^5{a8811axRAGZ^SS%k2BD!h`{G zj6Uiwa*Ts|&h3|DyvA=|jxoo`F_K1((MG$89OG}n5jn;k<}AUNW6Yt?OXV1SdQH?X z$MERg>LknL7=kBqj6UeM0na29Ifjfckz>GPvX$?08$62~L()Z#A?YH=XeBJJe=PY6 z@SkX<=^uai=L5FDhm~PSzAXBE`$Wgj;h#km=!Z;Po?@YQDlUC8*dw}=UVoXeNtkxL z!}g@Wbm^s+5}1d$QW;6j&tDwbN2k~BcyZ*B8l-h2bRJO)(_$Bqk;F)svv`q_L=7x5 z5*A`QUSuSr4J_JSY#FZ4SWUsu$a?%^T-(X8Cc{8c>`~QpS=d&4!p;}oxr;d z+y$I7@IBDnW#D$;ody=!OxD06o9QyJ$Y!=1SY$KX3@oylP6LZlR=~{$-VfYl;L`T=+b~F4Y1@{ze`(%Qnx*~j^TzPda-h5^tCXM8 z_9?Be-zJjRZ!6)av>lF-1GPTduN&A>sv9V>9M6wnB8ToIRy)=)3o$8;DOu8=r~s zl}0!4K~Fc(UT$;)m%#%%gpv2%sg)~@Zs5|oK;+)cwA0Vhx`86Y@Z~_Ql`FOH+@fxv z^2%2jbc=u7ZLF;td+r}sZLQ2F&RNNs%lqtIR%R;WD?8x9!M90$bwtuQkBO6JFV9rk z%R4ydan2{Lq38bC@$%wx{J42-&N9aJ46CQ?0r?%u<{YS_yj+KJ!<0K%nSP$~^87sG z-~(Msy#}49&rlYzU-$GYXS@~Yoyvdi!^Q0l9b)B!Y|vkqyL= zcZ~3Qyu5;6p&nsRc0W3i4`zqKyFsX#eERo zIAu&Yj0x~`KN7Eb+i!T(@-IWH9hnh(11+!W0Nx9{n{(hjgs+?P;DoiL^L2jzqU5(H zzw*wDC0*oKd2FynHnx{gWMg{>wQNlJ-#Tp7;3f%o}+|+ZY-eWZ#x+}_lx1O`hjFp=oUtnm5tnc85_mpC32udTNvPe%8kJ_E-WIDU0kIrtO8r}ru?Pm}S*ce%rg-|HeP(|EpIts-$E{N?U` z#;EYhTsgNovhH;ZKU6E{I3B!iyVc6MdFwj#D3P7Cu6quDMcdmr-wCv?%7BwEPnUS{ zkt=C|_@hoZ`Dg~;M$zizqj~3M@m+qeTWw3eF>0rjV@sKXmy_4!tjT!z1NjKFuE|*V z#Fn(d%USNGPG6Z_ecq~Ug!U5Ox*{`4=N`z>-bwC{XbJ4zBYN=QZTxxW z!{5gk7J1K!+8DXd@6^U=+IT}*8~=BX+tc|r`E9y@dGH{GeyFnh2tNP#e6|9Q`_HcEhe5d)IFE(?h#0QCAKzsn- zY}^;2zw_F^LPWmtfA5CnE~g#$g`8K-9ZzPy(6$E#o~q}WN|Dd#IeUuyX8n-)+3i`` zOLYEhesM@imL?jB^B!?8VB%AH<%j+zWD&C79ck)0KBoBets z8*oSWTx17w^_^BO;?@>nv2Fi^-H9A6XXI$xk)!QG_T|gbG6Q61wQir0oqa{l^?E$n z+1KDHlCO98U6h%1Av5#jWuuX$U5a!htbRzIBkIyI+?AJ=yCG^-gTrrIe%Go-CwoZl z&}(2VW|K!`Mx5a@*CIa}bJQB29mRYbu|7|p3Fse<&qk0*?W`Q1v#igv&Ws{^o6wLY z&6ibie-AVQ>Sk!PBX{!QXwSs4H9eE;+MX-z@fTjsUI+Qxknthsv_MF3rH{V&&TXd8 zC%GaQspFh3&Y5++$lpj8x!YI34X6p-$b<~8Czomfzmau*!kn9n4C}iZcVhSVinz!X zB9~zwA#%4#_E7tBXH)F*+$Mf+!Z$^fa7-^eM3U~0%o<9SZ0UjXBYUlhq zoV>xwEw}+0SXD@5TcasgWLra-Q$5+%^qA=BOTTa@UBf;phkg(n58**%03uHcw%mX` z;5s+ZBK5w)J=*Hk|BIxhDOcnM>oiU7e~e37K>anp%j+h5XVh)U-ROf_dFqnJz9Taa zImBHtkwGO`_a@<`2_JR749KS^e1MPqYH)nnb5I_159{_>D|Kl}(ndGg$SIU32bx|Y z2cmw|wWaOGjL6un)}(v6D0JtxU%6ir`HKZ~Xz%7;FFFw`(M<9u=C%ZvyHjEo-&#pCV&hWYBfI3vqcD zoEx=F$Cu?aGUn?T`!RKkXCDCHG0yT|(fmcUJW*h1cE4Jb@!SM|9v$p?O5{K{b@$7G zJe-6s|CU%Vb+fYx|C4o`oudng4$aLWu?MIya6M`A4t=F}Vx#{ajh1&hp2YU*mS`Y# zbLOkmRpc?!wjysp2ITFn>@ef+O4~Vibm-%ZS1)~p+QFJ2(sLzyfPmTxd~)xV=UX8xV?7*US&y`Vpj)df17{9Dj zm@8|0CV8@w*eEre-|dClG=HS&v7eK7*zoS3FUD zVg55VAEBPPG0{0WgLw`+3G5*}J?8hpXTQ`f`>!N>+F)*hGcUg&gMCJ9A#E!DNY;Q8 zP;^3AlPAKUu%%w93{rN83bEl0jR`r+EYgpI( z9m4OQ0MAi(jtf9j^0J4NyD#Id)Q`BVeCyCf?2x!BlmGFO{40THn*36x?ZCdQN8~uZtViTHw!yK1eOZslaeP^i$Z>pGkH~R+S&ztWd|A&5 z==-uBInVcHJtD`k4E;CB>&tpXrsKO$Ppb%5>&IGt47$6B%C8h9c9MYG9G+Ofj&?P+ANu z@)uveBXXH$6JOefe!GbbrL;Xu+q^VyDb3O`;Pb{X&@vsr?@IY8ZJ){T({A|rP%<5K zHi|NnR_|=_WN%H&`tlHV@-hcbXFj`0{4KZ%=VtspR;r|Lx5-*@X4{zxyt!4J_Y<$@ z#Re13>AS_73!#xFPUy(`GD)8GCQqlq^~;#o;g!)Y>pSLM*4LV*mAs)}zGLE|?;;m4 za)&^Efsyar<;iz)^NoB*&UbIqzAM_cEim#Od{8i-E!>H11M4K=z22;ooNbqiQZPJqP-H_M3QborL~YW)}O(JMB@(3etP#=V}~JRuE2mp>xO0o=*c)jy_i~aF}(w>=Tf; z==KiDZ^pxhjvKNvhBtNGnBCHLb5?i{u~w~?i{qYig%O!nKzb-ecu-B`=| zH2RGbV~NORdc7aTzd)1O8%v&Pnjxuc~9u(xk2&Cwn4+EAiclj|Ob^X&*v)-$_UI zaU**?vDu&QZtIi1+>pt(ISV-sF0z^F?sgBiS!h(nuh2S;wd%@f0Jx4e#SUEW->TTZ zwVM0I1Mw>M$&<9rzV1)jUEcSF@W;OBe1BimZuVRFThM(ol{^)8Krw#UL$miXu+QfqYTo(0SGG;IRDF=#^eUKLylY=!WB0y`XUuH|X(FEA7G9*-XAF^4TS{ z1TQS@WcYa6u2cq2njxuXdDj z_`SBr=Zf>p!ta?+j!| zc2R@gLobf(!oO9gIJ4rfmWQ9(7JUXGA1$qC)oNf_+fOsF*qr-oHd&MVYc?C$U$e=Y z++VZB&(~YZ%i6Wxz_NCY8(4Ic>I^LF*qDK3Z5B1K z=+#KQq%N{Ht1+;w*&+rOeVZx+%UU#SU|EYwK6zyAN1)+S{!8Oa%Ppnvmn-W@pC4JH zmX>>ro{zJ!e@&S5^n6YhJ%=3YpuE2(6ulkKrY3<+lw}R?UlVeM(eReHCKMfz@-<;2 zvT;x6=Pt7*TvW%}Et4RRtO+Gg|C-P%qv?=WMsZCjyzm~0FN@;()x<^LeTX&T!}$L| z=ci?jSrb0&tqHkD#`6dEAUvQOg#JzQd27s?@cugFz?sPxpQYD?!iT>mY&mZYwhqHp zaZMQU+QnNFE@Q3b=?_h*4C*zZzv{JYeMvjrlMCQYr;~z;+oLZ!&?)| z9j#s)Ors52D~I>532Cz#w3)XiT&BN|vL=LP|C-Ry^45g?w5)e&&tb#+*M!iT2`z6; z=+Tljq0lO>2@Nf8O}L2m^XeS5?!^v;{`c2}==RJ(w@225Qa|E#U-@f7t^1N+;H?P_ zP0<-kgTGBZtaV?y)Wh}$)=FCUg*5bL=5j1h=+f)IFYl1P2uYlb0d!f=&tcvui$@oW zb>iGmc|7v^h(3D@9F$OKbei%+e+3@2?n@~TL3n5dmphS|n;z5k5#5)`#F2N5@Or$w zf?pwbDditR_vPX2FnC{0z9A*FeBBqJBm4*tM-%I^Vh6l}wT^#J@d~xbUZn2MMzBX) zk8Rv=V_UZtxYEGRukZHO&c(J}pmksJoTqEu7ZV@XYqewFW7d6oEXg~WWK4jk`?0tl zG;2Jq`(kMA!3MfIzaXE;FX$9Kliel1#a=sC@;j&XQtr}vDBx-R7ScWa7DI0ydVxYm z+M*3uXzYT9s~-#*_`zU}uD*nU41wH)q=bDnXn6!*xdxPLwQXeMRy zpqqp3cWEBElk)j|wev!L@qrgYmcoWL^#Zova@XC9*po`!Mz=csLf&~(Y`>qz_S@vi zNSxSye;qq-6W8V8ouaNVeyqxqu%u66SP7Tq0 z|84r7d%2RsRf4rwwX@@OEAmklh`jU-o(?PWZTe_OzBDc^>&VDIyWr-lGvCjrN4*rP zPJ5w`U!61YU4A@w?otMMvArFn3^lL}O+OEP^`rdy(|?*xU;OiIr1j_6d;L_~(^f=& zkPUDjCwF;fc({-92YClRJf0s$X7W-N*x@eU3=j8vzLa+^^wS?fN9!e<`<}HQt?HH^ zy>7+NRu?k|d+^KnPNxmxFSBPG@lG)Q@^jpqx&126YXb3?GT`LP(_ z2a?Y?;mk7`?CZIg`Wfz}-W;;h_!DTAd}EZ>^J$eb2QNqK`J~0pf3R}I&YyBf8@!x# z?w0Ew&1Pmj&RNgn$gLj@d42HMfIiq@>VG?PVs&KW>%uqn><}LYn!b(i1Ip-+6Py2y z`OdG5QNNNlBhR+4Y}_8i$ARRnpsgftrOc(`n*m(sqA~JYO%^bHvJ!A(G z!xefzoN3+P{S7nsOMH;{uM!_n4|ade#9Q3MXvLRv-f^!Rj-ZqG>o0|zgNwcMD??MC zUm5tHdLPt!?{#9kh#oL{Kg@CWcL&V;UDxwqtRlWVE1}G@yDnY{%vlIJFmdA0<8i$H z^3r1$2i3*0-$e&m^mPPB^3@T4mEH$q^B%ePl@azzr&9PsXHab31FUXlHPaw)gbQa#_wS zYSA5WPQ5II-iTde;0?f|3~WWt!RCFEZE>z}J9B<7XB$WD%lZCX?xgL@bN9)4kbN0C zHvDWu}m!6KzyAO|tw@F2t_tDzsUCIfl9?EGqenoNb^DeB!DBHt?Tk&K4h~{#uW%kI)y_viI@M!}oo~lT}JeBGj$Kdtb!2hez!v z-cSYlC>4>Jj+H(Wna9oTR_f!fXBU5qK2u#n9#41ZmYC>!w9{5{2at@V0e11JoAp@o z$KMM^?&I0T&yK|epE`(L{8pi9?BWH6UiY&GU-V<-ZqPdLksoLsm9*AF>DN(-mFTF{ zk;Zv(L!W5}D~~NT+7sx!%(bwK|7*fv>&>eIsSC4eR-jkgc<0+#4=!X}FU2l?8Fuma z$J)6gvr+NQ%URkEymT)p`?vM7PlNy7SvP0|~(e*?E_a1&@yO=MnZj`X0}G8)I&8UW$4<$$(d%6YwqjPSH>1;kCrz2 zezqBw7|6|N=Upstj=%9PdY=@)5TswTNNm2x*2zMh5V z58v_po&OsAQagFk^ATI(R}3A`mUw}iV1AIk`-)8LOZ?naJ*b;DuMds`f8|S|Pp2_{F0ql_V zH!E|i`DWc_6PHCU@_RZrx0>&lpP%m+`Q`iN`**4Dxh*E` zCbscDYy+p%ZBcMDDfYrXuK3;F+izcuynwOx+eP&2kjZ~koI_>&c|N*JaFG|><%T@m zW{IzY_5#|JJ6^Gizbh8!JrO=c*X!^6vjlAyj~@-};y*3lE|dNzMrU`w@xdYA?Z&?Gr+PYN_z}K? z-)nok@k^S{yHDl^(*`-^owvo!H+)oYzaQbvvyroPo0q+8AeDE z4rNDnoJ!S=&KK9Q6m`CQoh-4%w-IiLoJ3?qzE0Nv2K#%FJMD)r;Z1DuT?31*);i^zYYC% z^L4fS_AG7l{Uvh34{Cq!_npY)CYv@XZ6A@#wVU`4rK|NJ?eAxN&$GWzA&V)$n`h>6 z?CcD>@=M(k~XF1k=XI_Hk}|Z zva{qDu)nA50r?%u#*VY2yj+KJ!<0K%nSP$~^87qsfDhl!wcP&RG4}V;W+$@0Z#VY$ zrFe0F9i8#ytH|3}r_AMC!t47E`hG>3{e6nDB62xjH-0C1p!uQf?^i>sinXs#tA=&A z(E2d;_iK&)z0|MV{yrsoX~(g@--Z3L?B#YFd-zQX|I=On_4fC^t^In+g9p2uhZEV~ zlULi{A206*wZE4#efxXi!MDHHds{Om)}v47+uw6PZPEUI(+9A>7yAP(gEX|TtJmYJ z+4#QuIgV*)(=Eh1j8oA;YNN~!&QbC2gAxmP_Lt#0vrAB()MS3NXV-aTI*%g0ki zym2AY<|>2tMMLLcF?~!ZP5MF7$t(uW1l|T*HsN*Z%!GK3*w5 zq-lNx?+b(QBRXKh&t%ru^3CyaN_ino^HTb)NFRC#ABz?B`|%sin!Wyg<3HL?tm=H9 z-@h&S{ab#^9%Tc0dVyWi6d~nmyZHop4)UA&7JnD`eMCK^<*j{$+Gj!B)0L?1TAUX> zk^S&B<=}%_#@uMm*?LR-AxK%O44K%ydCsjnoFPkCX5fb#Uk8(qi5FkDi}RZ*$EfY> z^;&wCjuqd()%klQuVbUrktTmH^=Y9$1UIid+Rm_X5| zYH@xGWt_v@JJ~No4J<==_wV5m{Cy?m5I-Q_zCp^7{P2&gc@_0sicg0Jlj3JQe&9j< zZZ*?p;@hE#P<%T~JG9jJcJSI1KiTNg(5}`R7d;Hl10HfShaLtV+AQ%^(#9f3Fz&dK@8m?9{M`YAfoPh@QJv~AEZR2-aml^7MGF6e<2;M6C?F@}ukvOFwd4h?JOKMUg zYuXsIQr=h!9C)hEay%J)wY4#ij65oSulr?0$fY71k*Sx*_sfp%;|z|xMcI*0!YTh$dStPMHr7F9}-zokKiMp@?}XrKYkgffQt-H zw}IplU*S@YmdnK{C$d@l6qj-a%Ns%WK>T^1*a!F;{91T;r8G|DGd$e$6lc&ubsckY zb<5V*t*$ZZTl9qoZ{2QnA(QOdnzg!+Np@|04*%ZU|CM?Ky71NAvNd0xF7Z|xod(hZ zsb`&V?%9l$dltVU&*q))ZVg#oB9oMSW7Nl`%vLFL@NzC@O%UjM2ASkw<%q14a!4Dz zoMUBjo?nY6bBs0P*Wz~@Tf6A@f0sHiwsJ;>SUyHhXZ|W5Tf}|fv9+5rt`L02)*e$v zY2S+cQ^uBWYhOCHh?`sezhG?r6LXpVS*^6%C6nWxLYSHykb@f2D&M!hfijHj?EW3cbl1!j+5I-ZF8 zUooEW`%oNDBELJ)coIAP-^cgrd@1t;>DVQj9j`_}9Cn%6vE zF14@C&yN1S=gVapvV&6pERwg>m&-Kc%LQ6`eRUjMbV8Q82DjN?vx-hgL(zXr7$4xi zUzfO>#m1@&UoLx%FBki@`rICLMtol`+kyMkA?{ixuP$ZliWJ+#DB|H)(G5FFwTb)_;QJ(k5b=rtS=YA zm3`tjdv4S1EV?Rj`lcSaPXlt*M&rw6HD@lr23Ks$y3PLG(za#ek=IrvwWiq&;cO$-Bw&BZV8^5>V%SH47?$`cSlI}(3J0f)c zT+^A<{TD?#p3aKUiBjItqFzD2KbQF5dHzg9c2{Toxva(~g742Iir*BGd%vJ*5A@fP z;S42a(tj~X`WDi+;7cWg9pv%+xpaafepNi5D*<&Db1LwdwZyOIUV=?LtF=Fu8=d;) zH=@tc8WR19P^8tdoRi%UeqOBPD6OZ_054+u_Seu5Jq^hx_KVVAtLdXsA1}g}_Vdm> zc03<1&p;<*e7wA`Y4-bg`4PX%eY{9r#K%j+Z|5Rwyc+qr=i?=TOt1-=VKaMo@$oVl zJ&Vtp^8nw+3%`p#UIxQ2_3k&Br6MxJsZQOVU)^zIdk#M_)z18UxA89`zWhJI9BF;JO8bIok}}bC zk#|FUz|`|DH5gwojl|71QydANrL+52>%Wrwv*qQK${=BDCUzX^Ty z8$EwBc3As@S!!^WmEkn&@;yD9ZRpa}m-vEdDABWNBu(By@de|*hcVt6?GJ`FPaA(Q z2dQt`*b}DU^CQN_Ig0I}*c(Qn(ZF{`zA4d$QR;7bhXj^0+fOm?VoR!hzlfg~-gBKc z$-M7r?D86opO-I`^^4s_GLYn?=`F>u6C;$9;Fh4I(lUL4^%l*9A z#?Q-A=0Ls`ML#dM@Z0zE(#F}T_<31~>|6Z2v`c?6-yMaf*e_QU=S|)f{28|~rc3?2 z*kyiRJbLq;g=KzT1W)|D*qp<(GcV4g9P#s#Bosd{@R*##yN^E_?dL_(#m|eRi=US{ zg#KI@HGW!tKs|gvFV6wbGk#hmulQ*}wjalLi!FX$|Snhh*|WSR^t`UnXFi_NOZ{K)veX+$?6Y~n>XA!K0DP2knjdI2IQKSG;{OkVU6`V71poL&Q`fR%wo-tOx` ztR>zx@nebKXW%iwc>{|sgD*=LT?StdLUb8?8NTQ;_;P#EW$^VNM3-Twp(DBsSp$nM zLzjU?f5F#-5c#~X2O;_k8w`%v%BBr0vikJ~miq(N8d&7{s|_snv?&8;Y3CIN-VWSh zV7XUdA#j*S^cvbsyy!Kw8d&Uprx{rE2&NiXbP%Q(ct3E9f&I4ibs73hd}$w*;+N7X z9Wy>Zbg}k36N<#mYKT*u;(SuPV$H@gXHPgW$?<_74ym{ z-X&mpyjYQY`|XNNT=ZRRf_k374-R(4&f~_e7(K%t%1l0L?1~?O2ha96H}!F2SNw1t zx&@itGrhY6gb!brVb9dZ(FY!`j=-O+agbT*wa7xfCV2?CyJy3(w;|X&g*AfYE#4`8 z;hL;<&2YtU&t5aJDwA(MOObZ~Uhz(8Q;(gRw%9CtZLp6va2tlJoKJ@~+fSQ;haCPP zO`9}ypG)qPHng}a8h-m}S?AK87Y$dVeOq8?DQJOLq(z#hRlHN$(3+-c-B0^@bq?aI z+e*;?J}vGm=#A0;(2{m2zf&5$QANLbcEyIK*d?#lYn3O)G_jUgsGiVwO1B_m50F;N zngl(@LMJ#u`|%yp7k$89Uj)@I;6QnNOHl12K2#o$?nwxGVLNzGLZOi~Jm7L3 zg76@B1O(LAu`7OB_r2H^Cx|1jzEk>mc?G{heVskkW7rixksSu_9poEQLQ8bLrA+@$ zY2o2$a%0xH=kbul<9kq-*~IzHBJLTDa0kT{^roP(9pTW%l$<*mDJm59z)BBi-TRUjGr-!nV$8S>?8ZGYVatzP3kmHMVCP z7EXAfbLvXZrpc6JzdNDP=QDyHzGV+rJB?m(mHeiikHqf9&hg%ib>8x92>-;wdll=- zO78RJp60OJ>nnLf_PUJKDmFt8<1a_}89EtSx($wln<&9;_W6+Wp)|f(*spY2@zdFF z`tW#kWhb&0#y$sMF;;vOdo0m^_3T>0EmIul3!ldh@(O&eut1+8$e9=jNxIPecwwoom)v2; z$C&3No{#gK<0g1lHh?F7YBm+mMTFn;+<^KNINm*RJ2lSL-Mfl7?B5fgb`#jjh+VVX zSBOr7Dd$?+`a;fUuD6GBC*jnYXXdO(JLgRIyZ5}aG#SE1>q)cM`n(Q5!rpcn_eH{I zq{HnnXAe#K459yhI4Zi{^ri1>H*9?E3ZBK;%n;5xoI9q47W3`TsHMp&xn}?ykCdUe zx{RK@6YR&?YfNXHa+e6{EA)8={-w%rrR)%86R+zidE__#nBrFC9@nz(<2(awe#+AoL5HS86q+aqX;a7(KreZTmQ&xhL0 zIfO-@CfL`C-LchT6(khFClkMk1EhZ;-Qsu40;;6)<+FNnY~Dt%8JNle4dg za|<>(j4g23%@%EU1Qz>@lZs~wVn5-HKhbdr;LGd0V%lxA<9Wrg#~st8&+Wdvm@a!_ zZ|vQu)9&oPxtMmRYvp9@-DMBu4v?wtf?UvEh>hd|?so9P3dakbHKRl2<1#A!mTz}O zhp7VH`qP>FDmlmIP9R1))93bZY$leuHQWzx*+VCvWXAPzaM6)i=GJ<+ z%|fG!{l7!sHRG#5Y#o~!2hHY8q>+BQ!k>$=n@ICMiQPo49`B49-rawIzt!scF;;6p zi5|nrz1N?QA1If*KvUCZo~h%`G;yP61vI{l!%@B0*9o5Wmw{g1Mg5)lmf@YdDx({? zL(#eA^iZUvthd19JEQFx5;I4sU-5fI&r-cEAWgp)d*mI4zVPm^8KS(4zJd0*p|le(IWSJ=(8^70`Bc* zoGk0UU9TTlL+SpySM0+$JL&)ax9hoM9ph^_HVqGxF7%c=an==wdlj}t}-TbP4vLVun}nY-1|S(rO4xp9Z+ z!JGpeQ%{Wxq&Dlk=(T8nW~0F+E-T+Axx0Nd;R=)g?vngB14m7MDO2*-f?KQeZ)80g z#12PjZS0VE^j~#9SdmBFLxo3@R{U|kwGUxi8B{;{f7$yM_$aC@?_1rKq&pyKAV7me ziqK#{#tDiV9o(&i00GI`C@cgsV~3XzBQz#akT9u0fbb9+WDwDfAux!-Vv7ifEHGUM8FhtGc5D^3%(9;a6BHcAeE)OnR;SYjbRK>)`^`p+I(?t#KI-0k?)l$yDP7mP zqqNTYH_(dl1kPD*Fw3B>doliZ2L8LvZvhQHwPO`X5&?Jj@6g0`f6jsI) zTMt%TOK#TM2_MUIEI$^XmfTGITXM6-=hx)s#sjT#bIAOzO}Uw5U)a6!ex<7_G(k4VzXZHT;S zxw=(u7H*y|fTudQV=xOsZmxSl$jv)UxjC>+$jw{8gK2XkWBv<5Zhm$!fhjjr+^F0D( zLt3WXY^FtWGo=-kn?+it-24RU&n&ae%kP_Nm7B3IFm|*lH&glI<>oSyn=@CNa_us!co{JX_cEH2XnbO-rXax?MJ64(_c`zhpZwWEgluo814-H{c5&8}qc2FQ2oEnS}nl!ojE8l# z2y2$Zj{Q;KYc*hCVMBPgCndByqP?<9MxE5KAeG+?hxBUf;A4>SG7p1rJf0&i9t%?W zcSYpj?w~!iy9StDq)m0OJA(LO?dUF`FUQEuFGD7!a+%@mn4hJ{0gXitK)-vy zLw4S@|JW;|tTn8W$`wto#tuFPSvK4vW;{n+Jl06%LOB`_)Yy2sRIUT~y*I3VyjLdw zc?EmJ6{8P?UGUf5-Yd$r7v(a4Yrokm`r~VZ$!?w5A5GhFGV)~+n=v2rJTvw+Efi}4 ziN?o9Dwi3~9yt`2j6)hT4h8XeQViIa`{;m-vevK&l`EQFjU9XpGG6Cl5RS)l#KmI~ zD%XLC961!U8;5FaJZ&o5p$Ou`=Fb6{!c-R=2-l206n4R1`}lw;+X0l#{JrYa1H5e4 zTzGvji&$kd^Olh}Gi=5LF6X}1R3PSH$#@N8Kb6f4XE%<7CDwqYTSE{})CM2QTf+=0 zTQt2I@QSjTVPLC|$8*HRV+NJ&P((J41nsP$#>Ufr8f7~YLHw|m__|DC0tJV{>qj5K z{aOv0_W2=EwnHe}5&nDQCD74tt+jlAEZ-Kt;N>diT&F(+e(K`*@n#+p-^6#H|5oI` zJud&9x10GpW7_w-Dt{39?}^KQzsNtfe|=Z^A4LA4xctK+e`}4Pak)O(7)bNE@odv* zu?8S_TOirUhCA8K7$Mvl*};GE6t{U%SCow zyf$Mk(u4iWrl&-Du5(*5>{@M@-n6%Ke0e=++Q)txIL9p+HrRPf#c!g?zvFV7@oP#0 zIu^o(S?(^le@gDE?L6*9eWQcFUUL$EOY6DKFbb#5*d`M;q$Al36a0?LV?f*Zk=kdBp9E-I0 zI_e5xvHu?pn`iNXJy@OWg&Ej$#XjsF<{yZ=U@f`;`-pcdTe0UKf?F@_$)3W#?m=ME z+~WQ=y2~w9(1ca%CTQj#fpl+%z0;{!zc~%u#aDXDn3~>#`w|8cre8x^@T1?fKYV(5 zQ6tlSh%!zYtV$)0tFrzaUpkls9C+QCYXImGmK4d_lafsSRJR{AujKV(+SciebxJop*z3hU?mWm2uOJ-l7rz>v z>uJCE7F{xa0G|F)pEuKK{Dy~lpy@xOVIII*YBctt9#j%>m)QF4vl`cf_w`1fj0m^; zBN;Q$KayNCAzL^Nx*u#3w=49JiQB82rFgLp_adxR_Yl@pu%}ZutA^YVkMm3Uj^q2( z4>nDR?o&SqzDMi0dkbN%#h&>Z=pRr%R{(c6FHp!1G1=6EjluM#w5B;C_zs%FNV*#!7$W(i+S)zlWba6E<#<{zBXh zLAI=C>vCj%jcL2(8r^Qp51&AomLS=3k&J)h``fWREI>Nf;2ye;mF_Q5Je(Q1ZVP{v7P!Dt_;B1T^ zPbYG-PX4w?Ph&6qjduL0|62E=H-Bo{rlEWjGUqMQ9+}7WG_KG*tr0e59Iz=vd-U3X zRq|WEGs{4>VjS4Rzxe5V;9BR&F1~;NJ+22akT>Z;=u7~CVJ7^2)J7)zCEb^#swgu{e@(M}k5AZxu z$C-~q2euaLqxGoojp*agK<@+ImCgDaVvT`4ezY}g6Cn=h^a`YndowfZD1G)gWXnpf zDLD`S3iy8>8xQT6t1mR|XSp8NJJa_UT8r@&Smhw+*T{ivPCVU&H||LsB*E6sFnm8* z6xRE={!d~roa|tcp9;TcM4rWnGrf5w-=BC4;rxWT_CklH2K2cUpUMR+Dq;DVlnpHE zVx6%&@N_|0iSCXk9nY=!mNNJ~83zznGAf%6iF}z+sej4o{)qDfj0HWAzJiAt=PBK~ z430}Bn&N#P6OS2)!_#%hWV4I>E!$j_E;H`bZK|fNcPwg@yw#ePxJb5Z=9_doeq%AW z%jKaqfvqtow+;D~L%OhdF!VUO12(aof|fz^@8;5QU?G=zNQaDle}3-y^2Nm&mYsb55JE&;h~yOCC0l(x7v)oiD8PhtY2- zpR$W~qmw1;``keTW3u)@Z@Gw%G34LD_z}j$Gsc{BvNtvcbm!(dH{yOG-5z`>uyKDH zcS{p)@&Sp5r?jp6kJxUhnZ{LegKaK8W>gywdYWM)Ona`Ud}mBvFCg4m<3X0 zLHG2nI%oD9`0==>XzUcchYfR|$|J_2Zp{y3&kwq$da3yx#HGC_cK$)|f;|tR_c=}W z^fGP6ZpgzJiw9v257XY$2K;tl-9dA=4|^;&b%Qp|OLvxU$S6TyrZoWB>>~T9H$~-h zs`u5v7LeYjb>2VC*9`TTlSuFLEc_|Gb=oxSi!rr6gRnc$nZTCW6B$0Zoksd4pbPO< zpbMFVH_o~T!J~gX+NuORyE4}W$v+cs^7mrx0liIhor0}g{L#)X{cB?~{!3tqplr;} z9H0_e5~JN^#(daRK7;3(MC?Z=bEp4r|M^pOEfmkfh{nHt%Gd84^txKzyfOaX%vN_R zovVetq8+QbXd;Z{{3%5n<3Y31UqY4buUCY{hu zL6c5syP!!Yv{lfg6RHz5>4csUH0gvk3YzpQ>jh0Zp&CJxPN-VYq-)UxO&HD!L6bga zo}fuTR3>QB4^0E@z(YD7pKvGr&{#o}ekf1Sq#w!^G+``NL7xRZtQ|TwOl1kpOFM~gt&!W2Rb8CRVGHpubo?A00#kDS?-h%ynFYHEPPH?L0YQTdw zjBIx(L74)HJHz|e}JC~8`xKhn{}1AWK-%6*b_nCZQ^Ul+kw2}^VN{A z1NpX>rxhnYKP%21;KM3+8DEbrhOVm;`)YsC{gp;mlBsO(~HU zY(=5Zwx-2CK|lJ!X~1rj$rIA5Mp~v#DKjnj@wB2gr9@iLCn2rY`%qv%GfehsU^~i8 z3vx!yr)ga8MD-AFQ>u>iXpQBj9!;c4XM0v*4I43@NZ*NdiH@}g*;m?cK#vAL7hj7> z=XWBEEwYy5FbvRUAG8@^TK~qoyKKg3xXbbGip{tTcSl6}oZgfM)-*i7oyZS7z^-{) z9&F%YEO?Nx)^ZuZh9C~<(X!x%xZI}HmEux(JFT-KYoJGaBAkRZnTB`?F=>(POLv4Fsjk$Xq*&`j%1!Kd8#lYrezV^}Y~=Rvk}Xzv5-=0$_Wx*21ZXkVuR+#K$) zc@eUMxo2+booXYdUTITA_U*!+ip@~@zRiv%U@duE=&T@{t=8@ScJy=Qq@UVWgSa)M zlY(w3OggN#x~L7Z-Ag*SdJS*fok@D94f^`0c7-=WKhs9Hz(h~2v_3|t!aCb>N)u5Z>RY?GxxZH?X6j^A|htFPHJe_MF}@;Dtb z+ZL@)+OJB(m{;{J`ZV431bu50FrMFOpFW5_9g6GIl7fExQv3ay?tekQ_{M&XyI(N2 zzu5Gfs6Okv>(^nl`|+!^`-zx#$2S?(2gmnm>ziDQG7|4U5o?I=@|$b~?{CHN9{)|A zis9d~qw<&Rx2Mr>BXRv!Qnqn>K-O45eHV9Rl+xK1Epcxm`!)2nZ|!ZgHEw9cU1L$5 zt*MKx$G1ef*4l{dH+8pUYl`-(KNb5=(BtZ?ahGn_+R;TXA)W0DHHce7dfF6!d-~Z; zvfW4e=4~3@VQmBHVsTH=^9REEthTz?s;}wgh(FSo5`U)tbqQZzyw14#|>1{2k)D))c>~n{BIOCH_cfO#B@Je}`mdY|_s@e+c(CwCYpImWj^`9cs|j zr*;ULZxfr*fW5#Lt_#oL`rIwxoAkQRg6|_(_t}j*u|8nN>rL5WeIVg}FlJD_wxcgz zas;CL4)A@X26|T1>(MaXyXAvJ^hLL4oz zf$M^oUpNN&#p3apC&fiP(wlfZ3VgKPc%Spw(DYH1#|FqQFM~%j|2Fr2EjcPnF5>ZM zjUD-<_TlPmAhTo_rMb`wQHQ zb4>0KGT+gDE$kBgCBG-fW81E6bHBsYsRxn5S# zq~{=A#vO(!iYawF(&f@TpnzNHSjL)f|5h4mZkDaJ+PN8wIHPdSozv)!xwGTBQRywN5#d3Y zKV7bD#2KZ~Zx`-*%Ir0wk-4BZ#eL^>(2JZAcd64I=?YInG9E)3Ta8hJn9HPLS7Lw% z_9`-PUs3jme8~UMp>`V~Yu8IMg{5|S=ZjL&?sx0ZGiA{}@+8>vC=|AFwnBE?g|KAr zW_NU5G79};1np;MO@r)9I>xQIYyE8=?yjbHqT%jBd#2$I)4P>K+#i5`tXdk*stwONA6@HD#2qY;!sZRdpQrae_kcq8 z644zmvGO9xcn2ZNk?o#?28EG~6?~q*7kbhA==@>UWzv`83>w!D`6fU&TIwJA<)b>q zy^Oe*dAwP=gX?m~>2hFt4ek;OQ(Su;-LX`F{T6yl#vnf5waTk4kq3A@&h4I@h%KJ}o`B9t@ zk7Jf~2K-UZrVW(7?}xl>!+6>5*KObk?xm8?d7i&xWMkr!FbZa!ISu4pdKvl_rEV7R zhm!FN#8YGVXORc>f2Ta^rlSlGKz}|#N4m!62#0fQ=jojr6P}j^x@!uPjI=4tb$Ijf z2X3$@#og#lZd6Zs;MaW^RE^ih0Q+YR;_fz2>p$wOZ zqoSVZ9Nb{E3F&{6`Mj6ZL~-cOsjcLXI-zfi?wlfB%p}sQ1*XHcmp#k+{$LY^e~07o zJpntALAo0%yBE4Qx?hH=F0Pj(n$EZVg9vZdx0#k#+&Og|Wgd?-fIHx21wJ9eF6ct| z#eRGH(VxzsoV@NMQ$=5P8ugSX?oB$%%Y%JKtqkot58r%c#x&A1W>)L|-yxpAMu#ma z9)G9!#>lo5==AOAi#w72MoQmXhW(ZY6~at>1^?~vx9)o3@laMj?sM`}xqW4N0gX%E z`QV589KLN@hk~Cw@SZ8|ahgeCwQ`;Aa>}DU3Ebtxg4Oz|3y2%4(P3AL^HpNj1-BiO zNq)%dU#)Qu`s8&;6XVz&K8zC^D9*Ul#%Z8Y=iL4f*_EQRg1e*R&x6=&xJD;?LMq0g z-c%2~pPKg>y^6Gh;>=tf+Ar}7b8Z}F+?#>B-MBt{Fx?@CIKyxc582Wqn)d0)E|fxd z^r(`N1G;nby*Dy1^r-5%z=^yx?v^Ip#*dMBc#0c)HxI^Bp5DV^Tn!p`!B9__ZC za$-zg^$3@7?ryF}-3og~oAA5(d@0{kgdZ- zXXg?MEm>fG|?SIjky3}*D1~K=eeE4mqH(y(ieNnh)kj(^~5zC`+ zKDZGM>=~>*Rv{k+cmCAit->8V`m6D6!j61~t8spi{4?<;e{Uo9Yw@<$8}4%^j^cOV zF{yDr$8)>~dX>O)P+ZtiQZ#%6gt;I*2aU~y=ODM`~ zc>IGRKDk+ZF#boHm2cMP%89T=_gaX?2UGEr07B z>laH~{#SZmDJChDbl0C)~+KQLdoLMv*FLvPERs8L9@ICEQ7eY}pwi9df2{ zCmph7XNYvj8CE*b1A7EbI%GxAq(ips46Q=?ZsAV)Ov}y?>62Z;ooo?Vc7{l|>=5o` ziwMFqgCNYv7Lld*CY|#|xRE>QLYoCmI%h-Bq;oziXwo@j;m*U8&iRa>N#`69H0hjA z3z~G!rvyzp=eGn+dgew!lb-p6ph@p~T+pOv-XmzzwFU)EI@q0pCOz|ZL6e?#tDs5G zTqkJKGp`4Y;ezX#1Hzs3%+zhD?4)Nd6Ex|8{emVPa*3eNf}Yt99UCULT}aPNd7;|d z)NO3~vE_~B-zp=;waRYknd{(2Jbz8kj5BFbJ+2z7XP$$3tQ7l(f6&#CIcG)f{or1~ zgTOSAo_Q$N4e@&B&_&Wwlb(5w&@-0~rh5*C~`2zZ5VIv26W1(k;>`Z!Q%yp)oc`$?nzejIN19!)1q^WlvecIqDad;w^)k$Gs_ISh+$ffMD6{+4xw=z?T@sm zeDUWNhmyWE_>ie@6=~Alo9hwYZ>&rohWUFQ&iKl1vvWu03@ zTz+oxN^vQ?o$MJzmO;i{8BPN4=DEezv`99kJgsw!#6yc~by(}aLMC@$1$mysj8dgC zG#C1|xtbR?7n2*yaVBc6JQlQ5(AwU)roJ^AXPH7alg^U72~1{3v7bB4Sf-F2Bh%i` z;uw2BgcbcUWYzg(Us>sfdrq3&OB+)|%dua$RCYwnx3;(3-1ids@O>{qcZ7XP76LXv z;36IL8|&nY#~%vQw|@CJ?oxfV9Ou{O$_}jMoE#rYca~PdzE5d}9IC?5O#5Dyz+DqR zvG#r{!Q0ouV&5woHqWXr+V{%E{z_-;ugJIOH?qt-^^N##+W#4d@yDV^>oZ|b8FCKf zj=TB&vSrabt#1K7F(3ARu;xS=$lgyL;t?IZXktKj!sbt`+bZ(Ul#FV~a#_Ou579VV zFdzW?Kd=GvHTys0Pia1N?m@Ah$DW7RiKiQ$o_NyqEPOkA5RUeM_D5yhyTRWOT{8Ni zUar@(GMvUK9_HSr)6p>Z>P)>Ibum#%^iI&mPnrN3nD(=}V*OvD&pl6hmg20%@XR!^ zKSk*%T(+Co`^=j z`#y8<4cr&C7gWvDZu<>jA0X3*vlnFFXFvR_g?*n;Tv*Gt5ZrlPQ5=Ty5dVxlg#9&I@h8o&X8v@zb z#9mh*KH2ioqI=8t>$sx`dj@HU(+}<019BPmTfylb%1dCciKck_W8(2R0(h9;&Ng3HDK^Ds~Mhbj-<4Ix7+Ug9C5u zA<(xsN3VdbBc@S3JAtRh+;XWDbi&xVLLR+BSYRUN!2S*on@4GMgq`II)SYTf^^ncw zJk-4^_6LSyf8ZstcP?diMBU$~Xt(v2sh?3hZ8z@Au)$_k#moYp2D#DvLb~Er!p@Ea zI@Zn(>{}V+X4%qQm*wgW76MM=XMgEjm5yGuECh~oP ztV!JroL2uh$2j7I5=fuHe2aMe#HLpcnnlssXx~sl*pFrbx zK1;?d%#rx6a6dIaJ7eVIw*zB3jfdEGP;8-cEd#PR_5pB@Hu^Qj;E#|d*=w?F{!rUt z{?$GB)({tc%^XWLK4w>8tW5^qx)T1B-drsM^EnGuWiVqQ{A_s?&IdQh>9Ad0nxWvW zzy@(|YD2h#N7%bhg5R7B7w*v}|4h8e-;1%|jkh&E*I?da_*z~Z7C+OZHKIKXAU5Ml2@v-g+Nm+ufA$N-x zEPwyU{{5*kIb>g%%fOZ_%p?aFY9Oq{;-M!!5#};uVNJNxWnkSKNm#&Ne>|2goc*B5 znQY-$G8W0Zn8>+1;h`-V%MZC{yKpDDcB`PtE>4}GNydFf&?Mt-6g0`WBzsX_gp&>k znq=Hnf+o8-HG(F)IMsqCSxOT$$)**8CK=R{+eikrV3Yz56 z4A2fdBqMr+JISLR1x@lO`OA1nUZgiYv2|j_wd6~yeqzJN@*m5O#iu1-sz}?CFD*X5 zCSU%EcjLNownhlrLk$MdeFz zzn>{z;@qK-U5B9#RNN68l`m0exu`Q!zO0POmq@c!z7%Ph@?~pU>?CBuPh#Xtq?L!X zO!?AGi{wj6D=J@#v`qQ36~}I~aO^QSzGTt3>k1 zNtbX35bTEffU%V>Jc)IbdAB6ycTX4CXk?98vovB&6Ip_1xt`D_%#-}S*_6hoB2{#! zwC@o;CGr&Ro_yAm;;)Tp-0d+v#ds>BO&lb~pDTpwUcz<#y8_MJ-dADhfSR5^EIx`KnM=~j1x-pjW%%nWq&u1Fs zI!UVyn&I2>LGH|te2`ZA`K*TDhR6xy>1drW?`pjSHqrdIZwk9mXWBn+ko!Vz=?GgA z(gkymG!^UlROpbojR>O>IKhPxJI+~Pzf@z`GsOc8-lB+hVWA9(v4-}Jx{Gk##ht%) zw&*J1%Tecawpuc9w`?h}`5!PP-6)9CD-JRRWreM4?EU=?_CNPAWyL{~H6>+Hkg)vm z{^U;kSmY-ek83zz6T~?qPfF(M2<@veh58W1OE(TvK8=)T`}zEa=d(0uhHuMZN(%vt4x#q9P3+>g;1s^HKW{eH|yMGB|Pz}?lJ)&a{cl$NXp&Hu6 z*c`!~v5`8AG1NZvw!){N&bd3n><6ICA?_=6eE&ojz54I>wLr+IPPx0^ifCbZySy(HW^sa*}h&teQ z*)SdickP3{xr0?$L*Oh+5Md(m{^ZUAbdU6|h@{1zZ`pW~lo+RtZ`?hb7ZpzLIWhu<&Y!oAJfvShgcW!*Zz{^<5u+rqBE zHf~qP92d5Tabb(%M4f4GZKlJbdwe*TQU*Ihbno+A#J9nYP`QOUy0is+;M-b4 z-{>Yi2weM~S*>%Wnf7xc?dKFH<@xbX<+`=oqG?MP%=4JNF?RHZy=Q7$yn$z48nL@p zgfS*-7&n1aUE!g-(KXzSj&G%gFtXQ)aMy|Z%_c)M_#cY>lqFt6LPUv8?x0lFuotH-21$bZuxE*6U;uCgxme}tN zVLhB;EJr)g9QR{A>|7B(e(CYBD|0o!55i0vcPj9CW~Jgp`)MC9m+SgMR|>aQSzx6i zL09!Np8n#dk3{-2kuTjDN_Xbc-15F~H|=*X!hG^$=xm@PI=WoeaQ2U5qs83Cbwq-G zMV#|>kj*NL`98>PH(?&eeAZ-PoE_(uW!PPd!t|yvCweXgjXgc=PrA-6Q~ID@I-XmZ zLH58KafgJ~{V`eo)U=O7c2>v+i37ivQo7I|;tb#$9oSOMN&KOnl`m+YV4<#kyhP@h z!pMAJj*-6LaXe%i+LOFV=v<+5<9xjJ8}nY%Q@<$>pZ@uL*eWc?og1XbB3;#24w{oG zZtT6LUd+kNl{gk@arx#7VWG!b3_K(cn`iMsdMwy`Q91)39dTwiGS*ku@mpfqdl>7f zpNZ19W6$GyC4jq5fh|;eA^aJH-wwT&8#rk#FK5pC$TTr9$9ZN4W~(s1_Gj5`H!eFCpdv|-#eAOgMG8R+HW@!I51 z{eyIOv}X+cL6!$N?F_WHv}*&a>0+AKeKA>6hG+_r|_8gyx^fR(qffj-2y(YgV;I$)Yb{JXg>k>U^F z-%YCy%@FrvbVnRZhvvindM4e^3LJO^bXzk?Z{?Sa$p`~FeHvtU=q)n|1I=xT0Rue` zzvSb}5B84?I4L}5sm=0p8J$Hm){aKX@3pe->@_N) zfetAH{*<=@{mLRr%jf2JV4_J^wp4^S>&Z+*L0&e}?aljPqX%hNce-YPwhOy%XTkg6 zd7T?cZ%K8ipzf&7ev5Q3w<+^PU~$RMX>_7>Ik33=eU*BxL)y&m$G*%|taE_tM!acg zQ_C(6Z)=okEbcrVOXUmtfy+f4Z@G^15?L)OwI^(5As%c@-H~UOA()4-Wb=mVV?R$D zSa#ycH@As&q9dq3HO|v(MVPE+{0`x6S1;}lMz~yYcWN%usS&*WJKSx?9KLV!CE`sI z-w5fyK87FJk!up^F|O~NtE(9QI$3=JIAXdBm(A@ByHr!TemxUusQ5BP|)L zk!Bfi)xb0Jx^bO*0Bfe{`bFsVfr0i&b+ZTqJv8;)gFGFIYo$$l$WCy~Ar zdD2;d#)PppG5=j5tV&^JGi)BE(GmIVM!f(7&HHR2`Yi6tHTT4zBs}tI<6e%1UJ5!E3tbH?G`U$=Xa}&+6rSS7VxbA+Om2Hc z8A^bKK5Lj*=>H?eRLbjPggaxHSZE)x(7(m+Rp-q906$)zr>I|X-rBo|wG|8919LI$ z+s0#|p)SB*+j~Myx4-Xy|5V)`kEa3RT8a7-?<-+sE&u47F&APfcvL)y-HOpA`YVFY zUd4^lbkG~b3)Jx^n1qzP4{&k6!Z^4?-w*-XR)B; zJdl1eC}_gY?i4g(XSWNQ^p;x%O?t~ZL6hF{89|fYa-*P0Z@FI3q_+$Rn)H^d1WkI& z8bOoZvRcrj_tOMTddo6Flit!VXwqAj2%7YPGX+hU+G&C&ouN*Ov!HVYZS@DzJ;u7nhL5e!*fe9y70a8&Kc#7v3tf`mk7@DzHGL-R z-$(JXfmpmO;Z03BT7}F^_{At*7W=0eu)jn4Ox!{DHGSq_?CqKQ%*8^V342J0lZiN_ z&!jl5`b;yAp*_t!qIg+D;APn&J<6YO^L+6%@UqX69VuP%*9yGsvnF2Fw@Kh-p9Bw2 z@Ow(KM%N0wEbbeDJUy(y)MpYOmOfJ*U5m9)3|=Na` zQK#2J_X68X{@S3^Ut0qnaE26inY`o&y^J4zzD+@=Z&MBJ>34x&>ba*gt`ryY?G@uH zSjTsQT_)t+CccKe9mqRAUk&*>kZ*f=T5;m@v*L6DAFwCM=fpBDKdgbwx^ghw^xT0q zZkLJbtiqBl`oj*)h#0#}*srF$L=cy^u@x?Amr1lA>@6XW#Uu|8#>lLXMcaD^;;uzY zUy8mm9(@JjO2ft)zRn=KOvpQHm6f+h3-*@Uq{VumA6=i^YL^LVO+s1-7fmY_aVV{* zT_%wh^qEMj^cvIqB3l8qvMzX|-fT_(k5+GTRp9;H4Qwab(#>@vl>tHLf*ygT$i+%A*L z7PZT?UF1i4WALEF^I!uHW4X++132O5czGSyg}0NPp2*YC<31Zs0`D5q zN`TE1D=m`eC{N2S6Y-S1+&eNj&d zy#rnMd(d^i2VFP0y$oI1JJ4a@0KN8W&~=MA2D$Zv+w0J43pe&I57)(LXmxXuTR-T( zk8vGNHF()7cya!fd8q*}LoioTy*AFl;GPRFz>a{ai4^a`(zFCQhU4& zy?3kI&j{D#MVkcQhVJbh=*LgW)IRjK!l$6lxjWj!bbncwkq6>_t30gq?cR5xFMk*M z@^_)9eiyo7v?cN}dHWUT?doBEiG9D5(3#r}x_2@q^TQ9A*3TFnr9Xtu9A(o!#GXY5 z=*&Na&iuoJgpZZ9cY_EYiT5XW<~s>}^@q@9e*&HPC(z$~;zCJ7i%8ar9m8CwV%(6Xv+fuDY6%B`I=JoNwRzn`PKdWz|pO*9ty|aSL19vfVMm!*S&urTKCn) z>%b^LCy9C4X1vyPjhN%MW9&`|9>U!^hh(xfwDaeO!ogRMw7Db3*?0(Tc}T81XWos1 zbuFJK8;u=`tuEPEgm0GamZCeC_FUSh??;*kF&~65AJF^|#u)Z5d~NY^Q`Lnxu@;9N z8O#N4=;3=|K45iwakfCr2f-t_@G+*B z(fZx<@ul(Yh&Jj5=$gLmTUW4vlxEW@+UYH-EZtNrv*9-Fl&GRR~%S(cn z<$q;f8o|q*G^e6o9{(%yVhcf^4?a@r-hj>#{aAhTB|J4urS^COI>}bIU(=qYiaDb0 zjYtaGpl-yQathjq-d6Y&)H!!YdzkJo>N4^`+;5eKmA+j)8OFE|Y}Cmh9=>kSkdMjR zFF9{l4|5FT$D0Z^X^ab)|5KFrK4AWS2JZ3@^m_-HA7%5u2N@CLhyT5O%>UlOhrVd> zOa7A>qu-0f`;$BKzKQYjy@;fKs5!I`ff@J^n1K%?TKRjB5y6*>;`QTqdw@os?dS6! zJRi(CJbYU|BtOkL$OnGy=W|T&sl5@9)L&uzcvGnw)j=kmFcrj2~l z9J@2$#Q5=sygB>L#xXSJ^SQvBBgY_)&3Lm3_l58=;PfTR6W`2;lbG9&>5+ja!)JQN z%`KVc+_D*Sitvt7{1o8hh#a{}Glb>+LyUO-#!Q|J!0BQ(bb zk9@#bKO;R#H((nJb%;BvaPs|kxHD@X?&3Ol2hF$aHH^_mBJuv@&LVr!kB&s7#-nkz z)*58SsGn23e#Q#Qr;hS$Kc9M@59S;mzAYaTqd5opz_0y$cAzcy2BgN_Xv=*{)yZV} zE99rYy7%Z=*vyN)m(3Z1Ezk{n<;}m|8?AeqKWL8Z&gb=gO|Z?x+tR?;olm>VX{fr#wJ`sgN!=I zHr;D_x(JufZOk2uzy{n^!tXQUjk>>UOvBsoQQnj*M2_xFt;BGpxx1)4N6r~2YENj=roQj`T*%xZP+bW z8^;u~zehHjY(_&9x66e2=`!Znl*XeGErHe|`=C=gin)5X%$kmb<#&#@vF}$U+Q@@8 z@_frS8X&?A_#@lsR?L}J8*RYcf3-H+g|*0EqK&?ye6G?)-67vgnSXj4^^9wy$52K` z(~+YcW9r$#aVWcV?dTD?1GNQgE7m78G(rz4#!<5!*C8&oBlMg6+jD){wjEQOj)vXu z{GyHRLtwg)?op)s3vU0gzT2@j-=NGk1?;{}VZ!SY=6EFLPrJ9vd3xiH-bESM69lF> z!?L^2815)>ZPT+pf7Z{G7j%T?Dcn8h;ivAx+U^fL{5>gL+&=X{}}Fc{(O;@KAkD|(tS3obw}nV)X^sB)mNB!8<8f*+X#9l_H~#G z_pbzTdkS$K?i8y;_G6s!qWJ-VRHQ%L3%IH005AQPkOnL!KbKFEJgV>pj^fOmA>{tzlorqEeI4|RAgaMsiW{~X&!ov_IU=|*nzYcXq9njrxcHHxp z1Z+&DKle-Ew6K@@7Tn%K8jeso>~2lh?Xa!T2)C91n$RY$9E4_9f11LuCV4StL#Zkf)!vvZ~V%=-Omc(eV?G_sHvokL!Kd~V|~q=B=)oNvOH*adwCyyqV82As|uv}viLpdG3G_90zw zn>u+=H`~@}d_wuxN$1V?mD_kD(q@{5efLKd!U85?ek?HqXO;?ylcUevFg##F?iyoTdF$+Yvv@1J79DvT}FXzLpE(N+4}7Z8uF)#r$`3(fKd z3z2R;rAhq)SeiAIUX}6&eVb1pEZOjc?E~PWdUKn6JK#^Y;a(K!;LH^ADc8NC{>g6Q zn+UV>LM7M5yeqA7?%dxU&Twx~p#&@~;r*@=Fl^T5JCwm=y*r4PjR3 zTc!A=+4a#+{iT8>I+N{Y}}Z}-O_~H_)V}Nh%gk_vRBF0NzE8L zxZ5%@hQMBcM?Uv}M_blY{(eke`w;Fj?%SmJ{+e#ai}+oA-YgsZc$pe$+~B+sKE>37 zc?{F7Tl3VxGOZq5weyXW=WtYu*XY}{Y_!PN2W4-ZYiib!0JNoC(!K*|u zAaS@ARG$lvf=9&@#iM)^4ke0bvC{h!)A^=+;&3j>EEildE#|=w2Y!-mxp}Ra7YZ=% z*QPDGRWFA-olRqGXb-sJ??yaMJcMuY0E8feCQOSWXu`BucHGF8oMp$2Y{^-6+{l)k zON6fmZP{@n42(m#69xvtBu}3(FzDi(CJfAFc$l!D3BM5%G~r>e@xXZ_Jj{MU6CP%d zpa~BX6g1&sb_$yCFxv%9c$lq%COk}?puf$XWrTsL7U2m4qY0X@A{Bxr3<;W>^GO&O z3o}DF6Abj+oiH#Zf+h^iOhFR{W}2W0ui_IlVOk0WeHQejcIenJvGo?44&h$bi@34; zSUjT}aNG-(#fB%A&%YGzWi0f@gnLG0#3kIzO~Ab%?{;x7@%dU9 zmiF?ra4+%sS#fT<3hrg*Koj>ub!OpNSUb3v5`lY(4fjpB7eCs*EDrZF4}B#b_kuM4 zB-~2{(u&8uP+ETq?nM*07b;&o?q$_ga4#o-A=$`f78CbEcYI0gyT`p87r2*=$PYXy z@jP4&_ky?__j0AU-xl{md0My^;=#hbkh`_FurUVr5{$vU?EDtEmowPo_+xP|)#ww1 zd!csxHn^8OfqTij0`6s~z`bM&+)M5ia4(TK+zUVR3)~C26YeF#*ZJnYl7&(EbMjUN z?j-`;3#DzPNqa-_ysQ$qm-cuu+QGd9pyyl}ok^!nStz36`jxR!UxR>7N zT5&HOai^MvdzsV@?&Wd}?nOS=7WabRQQS*m4DRJ}4DLlf*B1AJ-%;F)F9!Fr3Oek@ z7~G2+;Vi66AO`m`7`T_-=cXzO^v+kpy>zhj<-osWVb2Qpb8veTx#I55z#oBo3480yf zFNm)YJ_dQ0`rE`e@h{CW_?NTo;9s@_V-bsg3ABTM@d*6O4+t{?{0oI4{7V33xf=e( z>VuD71qbsWr73VQL=z5X&Q)+Qd#-|mAq4_Z zN`O-_-`|Xh*(fkE{Sa?G+VOgUBjNoLd0!0^Qz`PgAMbC2iCHPqz7i&eFfUACVhHyV zkBK=3f5OE4Ql!%!CgwAQu`n@Th;Z#;VsJlV6ch7|z{IQqCg!#nOpHfhV(9y&b|AeD za4b0YZen5x+Y*b3*$+$%xmlQ)dBDU_c#0c~iNScn-L{Bv_wlFd9<9)-z#1QXS!^GHuJtZASJSN6*!^XSt$JjIob_v3CM*DUA zhK`_#&iYnBCu4Kr{4qc0yCQ?*-+)7%$nmU;o9>I^S%JxeeFCyE6{}xxk?l8^dB)cV zeS?|aQcc78akyu|ULP=%wmb^wgB#B9!*0>CjCuGr71xSj8Qv<~!K2@gIiLn(sEaKP zl7A-N>iSvl@8SA`1@bXkXa9Z-oO8gE0X>wT+z-8;Z79*ZdAV=?v&A|6<0I_&Cmf4;fnGE z)`xIKxq>EKkt%4y6K@-LZ zgCft1Fh(Ik6UOMEpnZtDU(kfhu`o=8G1@BJ31d_zXu=phBWS|wY!ozMjAAi80pU*A zomGOSyFxV34m@<L^zF1hWz*&6n z9E#^+cK7i7j6U|_r`)z;7Wfy*b6;*jZg40YJ+kMNC3_+*3B``IgrLGkj2zyO&?Bd? zOP|Gf+kBn+45g4$DO`VbcwtUqHA3eWjF^0Hes|Tae!jq6@%h3@3mnDcqB&(FCkZ(* z#r`~qp;k9pG?jv9A0>D{Vni6a&djy0Jl{Nd1i8c za`E`WYIkm4Q%JgAZAeIweBJvb7bnbI;vP{r0j})(6na7d%9El_&Ym2|PwXSB%?n%= zolzoJ$u&_v9dUfd@a3zleYM3AY)O)M?_v1;QcKN9$w$=A7G;bwQq;)E?2$zet#q?& zCd%6xb>yB=^iXfNtE87Z6?GyPFLNgsuXpEiy1m+=INBChsTNG8Ub8koZ=|ER@?WO3RMt*xF?)aZ zn8k>n=%&(KtYkiqoqEy=x9K~4Of$ZU^X^B+7(9egS(Hyjd`-u91`{6zwJAGx3 zs2kPF+JP;Q+z~1lU!z>?aodacrAftd8k@{OSCTV<3aPl$1&2!@6)T%-)n$z0+T(saLBUk`Nr9h1z znHy}ev%niHDR>)m`W(KR?3$dXa*};xLfPYnjCnq%V^4c;WsI|@dkXbl;XZQnNb0%f zug*D}bH1qhD0HpbA%Cr^sYMShcc;#1>n|6-a2Cr5&z&*pkM*y=u9#3=3?2zA~cq2xDn!NuCNu$&TCm#Vi6gx2jEH8WtuIz3K z{ZzpU_-T`?Cf8vEaHtpg2vEgG0E>HvJHw}CYdH>IUMM@eDu=kGkx9#!`FBp=i-GUO z*lFO9;Rsn9n?zs!(}$9#sb>c8DZ zs_DAT)>G}FURuzj;?ly23zDudn{89dl%LXl{Z!J#7CzhEg$xj>z$- zskv=`PYX-ekH|?y=n>f?tg#k0a_uSEIp)ks_3=W%ed>*Myc;$80>{P~Q zr?vS5(2}D>FD;ACQ)Y`io+i(9;M>^B=P8}fQ^~xSI*OCgOUD$xkf(>(M3di@4at0- z3bs2>RbxKA@;sG|blqzlbXumFaDc-sZ@Ug|`^_$)|02-ZH+=qjSN3px*eN_2D^op6gBd_TUEAb)>y`W!i9*CLv8xGbc-kLG?J9_1jb2 zGW?|s*5x|#U6wW#eT-5ZQiz=K-3`TeC-=}A>^+d{Oe&}SBM-_soUvjF;cUL^+{q}H zq}qHvF~`Z*b(wO0Vdnzsu_Hp`ueYNv6N=ZQjVru&=BN0lzj%vbXJqNmt{wTxOKDOf2bjq;ahk0ra4&>!*5sMVO?>~7a%U8fL7 z@$n8ZDq-`~;UmozW|ArwC%F^VQHB4^S1CpLlxB}nN%<~yJo=jKbEPGK&xHI7;By?> z!hiUJu3?GTT|}F zQ|??rIk+mu<=-}@GvW>y)2Zk7Y^?NsBSP5&3vvdG88Gs;k>;<7$nP}IuT-V{x~evn z<{pn-Dqewg)@pZx`cc^S{rpkhuy{q9z4*a4bv3O|Y(3eF zliK)F{I^SUk@28VVA1%@79KXHoTy^ z!JeW_?#o9Xn;HpANv>SwuF0&6BSK88CQ(8yk9`5!=;x&9EcBV0KVDBOM?BGGd z$ZC#;Hy6-&+>?h{fiN^8GhY=#+R{3MS94Tm#n!lNPwSk=%TP@79wr*!;?1mxR};9i*5*iuPw4QEZnJ5Lp}J4u97^i*J}W9= zN8V%X$d8y}pEj$sXiCZS`%7+`S~PVgq;Y$)T9@8a&A=*alv<@$2MPwUs>Be}uXQo4 zb029(9+Rp%gzD6_>LPWjTBauWw7{Y5av!EAB810Iy}E1P{(W7Jn;-Kyx=qQ<8Gn~| zOej<>DZ^O3*VU=^m0Mv88~G&dtLxHJ^{Wow%D`iV^7fg1>oT$j7NL~x(6+&eTDMUB z_Y$=(dE4%EYS)C`e|-ScFoM+th8@K0*U4LX>n*lE+irII{OZcUBZV5q8i_qJefCsk z>YRrjDw;h_d1%h;S!Ht`zC+0`diX)5+&`_TZ2B~%Y>v`@9_oVC+mp_!uRue9oGyI!>)t){)RN2RVliV+Wx?TO6yxr}x`{b&Q zp%Lm(H36-#=mxpU6-tMDr#?yA=l9zBj8_xtOOyodd?=~v_Y-ILNyjfr_vQQQ&|}Wd z=A%99UwX&zIS={g%$`2GEO+{7v<2>DF1-T{P*zlOXaACE%IrC1_m|8mnl^nJ{iHUr zrN94rFVq-A$rnwj357CH8=Adn3)4OL*$@CGk%O9dRJM5j*f?#ILwdyD78R{@~ zH2PFB;#UWLpOFY!^3?=p?|J|A*>;DP5R&%|Nr241@3w?GUaK#o)Lmb*)#qi|s=9|L z^s2y5=D+{?PMiI+&wqgAF+Wm0)dbjUPCXgCu-c6*F&efKVP_)jHN#@VHXU&CFNW1F zV!uRdy_{~Zy7sV{`V7Q4gczPSF@Al(QI!yK`dIBO=F~g#e5Ns**8A|ZzA}7@ZjdyK zzY$2G5GnkiO$tHe>E`kBn4@Ya(i%}T>mQgdNj5g?A9eJe{?8x8`Z?J3A{sapSvG;k^`RWjp>kji5W!Qa14%w#-Oz;ix z*nBfs*FL7-ALco=4xy<7>{Tg;Uvd1zxx4SWA*{9n#SqH)OLU3B3DVGrY;^Jg*va9$}*pa>JOa0mvoIf-La@Exqg%+*DD&U_h`fWPG#4i zF6QE%wv@x~zdlT|e|ArsI=JybW$ly}8^X&Uy|JS^K5~zi@X;gx+MN|S8r1K37OChm zRSt|M+Yxsb;!f!kQRiZ%vMTWF`D^=1ReeKpk;^A1+VJJ~NtAROxE!3AnYj3RO`VFd zDFt!!5LauH@A+3NYge?~BL8H_%zv=?O8#-ZV~;b?H#lVafCPMn*(||L-(?P4`A05_euDy4sWx6)&#;x`@&be|Kk(z&w03v zJ{)DnobuWHn}H+#FD=99rpzi;X3c)2sALv==zAG|4~C&qg-4<<4*va1(NWDfTS&dl^get-5hcDCvQp_D`sC9JcxDJ4~r} zcv)?(wAmw7Wrd`Af1XcmdG^-dvnq<0CE2&=6YW*%&Q+0r4r<@Ce`q%u5bDo^V=NkNzfn9}GeHEel%VqGUYLLJ%i+lfDefB&56JX8ls z(kRtRX61SLOQu~Hl98Xx^ZSw|Aiu4h5~`HY5#;8{+1jB2c~L62@Ki$eHm{Q7!@r(B zTRR=bJ2ht-{yl;aKdVpn-jl;>??O*ZI~+oMHK(q_VZ$wYpLiC8%~Q}FKteG;m|4OGtIC?%yq{N2mz;_#Mt z5Xa_~kekhWc*_UKJK5U>@hJTjSZUmR#47#5FSmAL+Or`W%5T%2J}jZ^64kY1wfkq_ zeniesc}*Wi>e-z8%GzZueW|7$m1`Hb5T7|Y^}L>`HEu#K4sT^`d5csvp~1>!%$J)x zv8sDQ#FdNJ9P?3qG=L+Dk&L>td20eG;l(%p?64QB*wP&2=my{Jnt&W$bmPN0;dA^hsw32|LmDKwaIOLIc?9o?<5KWbQYBb$?r-q%o{=%5x%bR4Pg z3tn%GzsN=zGiNB{RR0dw3t++^MxUQqQGtkj9c^ZwTLyS+ZkD zoBHF!DBA};P&OBMa#hyu`^x6)Plmf^=7if_{p_FF`>D2%Gp|bxVTWfX&3^r_ zSJf`Q>1>YuQV&_h8gOR)^D;BT*tDcj|AGA=CR2!B&ofrwn#{VOY(Gl;DaP`R>A&gG zUA+Oj9Mu8lNy3{}eWfoZK~^DpRbWS1vRIK7u`;ZF!>lxeR+(~QPAG3w`M^Eu|0V5B z;G?R}{_%6yxi{-%l8`}`J4-e&l7&D5Lb;i&ERg_Fu;ur1vq``KCL|~#kULp05~zb> zL0=ofAOgkKpjhn6O@fNn1r^_7wUvO0plyS#3#c%^@3|AG?c4YL{QsXHI+=6Nea`lr z=RDgv=b3o12H1)Ic_g&NhIDG>giHPqC8-_*DFA(99^#W(3^sT|?Qx|z>#&R2qy93d zGw}3+hV%lXIkq9cDUE4Ot!RYC=kDE*BRsE?3OCWm^)@;=|4qFM(^*LV$_c!l(2G>x z!U>`tTgC*BssU9D9>n;#3uDn5uL;nwXg>ra0PTbngnLuk)UDoWO$qD6C2Q;IJf_GD z%T-@SS)I3bI+gSbXU{Xg3!x@7(G(TAWIuP4Izi1>iNER9vXz#4Vi*GX&n=i;zuFRb z<`q#DJ2j)!5a%~WasJ3&u~Y2#@4GsSflZd91?5pVtI-iSedO%%OR!8nAm@*tCj7AX zuTYYD7URvhBijSL_thz?)-C(gUPCNx3ADXpQ7xSljWR==j#o-C#m5|c9rUK|U>#J`on0GtyHfd7kcmJw+L&eUA<&oZhMtyo*P3WiVPBq)v7@jg{`tRy=cNy$oS1oI8Rd=)SsW#UK!jx6)r zqXb}{VyA82)l&YSFrNhcyafC_3;g^9_{kc+j(H9E;YTp91M`-=mcYPCNtJ;462ns3 z-Yo+2;(P&^m%?=~`Y8reL#NY60 zM7`L#_Jq&GW6Mcdl|jLC40+bxD6HXL8DrJ=U0qDQ3OQQK2n9D{v?4Qb{BIcTCm3zd z4Ltt(ne72>eH|#QP3{IBW#Dn~$p|&V#G|}e?-pkPkFyBy7=gN5K*22Vm_35W64d-O z@VFZ_2T=3NH}LonYMva%a$#t_j9Ts<(-~Q@p5gZlvF!(>^svUv5o+H6B z%#vtWOxt;ka_;zaVqmTc@xC+gr$1UW#Z8)n8Gb!!_}tXWOSK5n#XfhT~`d}>Fa;pu_nI;?oz zwBjE@&e-SRj4clBJ4;(*3RCqEv16jzAG2r7cp(fzTX|NZpfU1_qqICBN+*tF?!0}Ur z)|P12#$Jd6oEK0qN01xw4SqAjsFpkzX`g!RPziu)?s^u!QWw{_X5HEv*r{vl8dq$< zH++~rZ`FO|o)wMjVEHz}j-_NULLbHJo9b3m!kE3fw$AkX{SzInpt+NLRcotOOawJyUA1;aBS!OY+N@ba z32IVNc=QYFmeW!H&v1AA6L$7vjYuZ_=~xjT{$$|zx7X>fl&+DjN8k}t*&8}glrOw~ zN!Q2leO=d#803aEqYp@xv%p*@WXBU^_C-(}O9DS6ssvHu0P1Pn_v`Hgv9Ufoj zEFR%5VGJw*1}Jx#48G4B)H>x37m=FDcPV`EEPU=H_>o$z7CCFlI#*r3&uP)LJSMm_ zi)j^3u~jal!u8~?u_@tH<{IHUB^SYi#+ls7(oNvK8_^@dpDO4QsB5ZQYqF>?f1(_8 z=%`9M0&g6ufoC@31|RJR+}A_>eS%T#YLPibbL-Ie=p23p@2`ss^X5g=Cq*?=@Vvd8 z<-#oTJ->L+7cD@?1x++(CG+Y>;APE(`kT-85J5dcgeZGrb@4@dqS`k)QR9!D7?X1F z#2D(cJuwD*G8WRzX%@L%q6f-ZPNjZ%J@5*4pdG>8S&rZnSy`%s+s93*sta1J7Aul&6Fg5gU` z&w&b|l0Xx&r%3u3!l7$5R` zkRl2ESb`nn(F)QInw|i!Ez&NUo=BpRR_~zc9AZ(5E|LHt#j->MPrjMk)=OJmba*b& z7vV>1nMn7`M%x*!yN{VA9KMvuA2kx$7P)d^N!<3Yj3<#>)MF$TP8>X{&USm;Llv`0 zzNkzvziFPRk2A!fZOBDUd86#CwzrR+MTRaV63LCa#1>f;coPWL0=Y>#G8HL%)S&&OzfhHV`aU&j*XtxQ+1} z{i|D7`!{BnFPO1Ia(eeb%Za+6VnuBUurqrnM5+-N47$Fs3&QxI4^KM+{@43DS z-RcqboUu8nZyL>c{J9;HRTZ_fn5!(JLL0~;&)XZk0?0qcPKMfmCw8a95Fq>WJ-j&Yx$*X;|f1q`}8;pV+8xe5#kXgliUke$Vzm z!E@w-%3*ABD%QHJ>cP=vcC|1#v5e75g6U;iZ?WZpegZz)QdP3CBOssNlJmgimLOZ! zBKw0$W&YgO;J7kQmyMR-l-pQ%4gA3wxA~{+j3_#C58>@aM|85q-ahoUM9SK;c|{M( zWqLOCe6Vzc;D1c=K+m_a1xLP(mF%`^&&BR74Eb`L9%vi2hC9T)#Io9*qXe~AjMSbL z;UJh@Fq9EZ(qo1L)g>g|9X%}JyLc!Id2x4rn(fv;&56+7l0Tgs6iqq1UHui?bHmb z$yra$JfgBnq6QxoB=yeNQ+$*btBD$acpl*~c8`-;a}IMJ3OUOQvUQ$3$oG-1h8RZm z6IOv><-?xx`j|yLquStA;LAu?RZJ7lgIeE7FHqqb0)@GQOC;Xi$UhmJVP7aFXl-II zzmR9U+1Rxr3t33IVqzRB-G4soCaruC>EdIYDjQrX!B>JBL(z>q2|i)RO2S#a;pn&c z_=!2mtY@uAx-Lf7H<2z!Y@$ZGqGAW!r(Nv9Gou*&$|BSfo1?m-7GnH0Jmix9Ya?E3 zMVT*S-sYVXZK#2C4aLN1&Y(jV@F89A#L#+9#c)BZ&g3T8dR65!I z1^Q~llzH#6Gr_U?zy+YDnbAK-?(;E^%sp>xRAw7ol0#A59f3J-*pxy^KuzZjO_|~; zmM#7-3`yZJPc~qchh+TB$O$IsS8nf<_9eSL#n`PCTK>2kh(&JlDiPB4{f?ld2a^q%_0U~sS1?LC%$Y+{es9@*4A z`LMhojxR zLraj(RzA&E;omu%v&36(pQs2!8gi*5WX$)crSsVfJWeysb$q&L^bPm~g*9Od0Lz*0 z2eMB~lxx#^vBRBrK8NY}WKk8*_>W;G=99#R6wmFbb9v1yexXQUI?q0@reS%D$~hP7-DoF06*$dQW)VEe2aQqjK|XzQkYrUz zK3P^`wJ1%7E&FG~C&eR|HzkZZg%6U0y#7Z+K#5{ckwy-7k9()LbkB}%vIjdDy@m5# zE9V(ynrG!}Muk>wrWr2@6cJ&i#fV(O+80X)y|MOcX{|TTFi5LCu4fi$d7eZEO=~nw zPRHq(-QyDa=g)e(zhYKjcYM-LgFVn<*pezCJJ@?q_m>@L*}S|rVboqF!+HU z!59Hq4Ngk8DE!byJnh6&MtbE$i=qa_bPj*{^h&8oj#BAbnujL}O}b{2r8h?^EkwHX zm2~wlUtN_3@%^W-t}b;FZMw41H$`#zCMh1@lSk8`8|uC4LtopSO1<}9`*@{NvMMd0 zr`X}85e!-4{o3B9e$>+q4)~X|_1*@1!QZ?|HW}rboTvz(G|RDHj}ni)FzN+wbKr$*aY2ygF=G@9 z+JyDzf5~W)G>J9waL}7Pr8#hGh|(>q@tA>|Fawn01p6-Ofz^vW_ewr*tDOh6>7bM_ z_ARmw>bRcPq+Js0ooAfUDBd5~uuE>+L}`Q4^ZasK=YO3DuY&o&`4r{=9TWJ{CGz_Q z$Q$x9{{9hrCdh3TH9hb}BHa0KwxReUWj*Q>gltmIdB0F+C3*{(Nc2NF1 z3MF|WKz2+8=Xj`booV2>h=Y5AZTvJP+DkRR0A3UirE$dAd8AK8!}6Htp) zqx`WW5IRmJ$Ro)P)WLb^c_BAs$V6qVmaLrSP5I&AAxXMt9-jW>_x9glXtGDE>e-l+QFP-Vke|Dd`{GL`&~p zwF>9y{%XQ~_U4vu65HQfl*jqsGs4{6;uig{8WH~I4a=r8tBy4+FK^zF9BNe|31D+I z?-&ybOAM!U1h=7L>3t^X-%nj6ZP;OuJ!)z&Juyp(@V{uJD8Qc(hEC3lQj&vN`srjU zow*)NWDmpe4_=zfn`11o%k8P{8y1g9r$JLX-BZ2*(y5IbWlATV3zz)C%mt86esuscsKAs?;n|H#ErE1u zhjgNH%fGsKL^{=S;rSqtPCpo#>CcR{4^7TIZH!iHUgzBP^)s~9YmV?!niPqCl zOPuySr6Vx=w{)#F(6zRkp{o@=jMCGU(V9zniw8A;KSN)Wy7LcD**9(f)WdmJbFGyc zl{gpi;j(8^;^|6D$4YBn2EFZL*t@KL&Rl077Rvrpbe-*9=X@CJ>>k(JUSXH$-=i}c zqqjhx5uoWmx+%hc1}9ytI;EHF)nV$nJG6UMbD#?AEEX7a0)z1lanH)+JiHYa=D)zN zenDQs-{(fKD1&P~j(2+rTXN3Wm}CPUsmA-(NvoooTub07CZUtcg8y@@WUBM>{=cJT zaxg)&@yVN#fwRBe?n|~_$KzaHqq>pfpYZ4!;aa&Pc%<5S3AomizoD3HqL@6$(;Cu} zfyoSN%ZSO!DV`kuc6JzxRQKHnOoq8u7?XcmmJm#+NHlQ?9r(cM-r@rU`Y$jg0?&rq z&G|nlWS~#JMlpJ@k#er)CU7opPoc_r;ziEC+YlgG=xVz+G*&4wxt3^htx@1wCE!}T zxl&3dM`AZ&n-1GG&V^Ah^y41}XZeM3&Mj^_?E=prWcH8B&tUD{7FvS!SQdhGzzRHc zQEg+1C=2R@VEQf7!A(8@$8&9*ee|`^q2BrP)0KBKQc#~sT46te);VG`REw;YdEd03bJJEi za@vJ9b>;$IHpe|Hk(^+%6kdr@(?0kf=~~M~)zk0g%yq4E>@8TK%#kA~(e)L%F7?iv zQtv%e>b>(FskcEQUA-jJlzOqCA1mnR$jde*!L*n@!Y$HbPvXcrWI#P(sYiK!Sn9<$ z)0ydm4Tp7o?cU;DRO&$w1_xOL>iHv9 zqbc>wF%5MoL>)?!mG;ifiVt-DuIXi5`n?84w!6Xx%baDpXFk)@DqzX$#GgZ?a_KXaz! zC)^m=#qPAnzCfARXJyD!VH{~9q1cRKl+!(5+AR;b&JjWH#_Clt+4tXkZn3i59+#G zeAf-W^&nr?+_nB&&i7VtQR#VkVF|0#b@%u+$j}jqkew&MHrU}edlu%!B6*po7gxgyU~xW zfTK9NU3wlETS`j-pZ<-DAYT`$*|$!ed1@BU)QamMWA7F%RU`Ti>^MoGGV&|jlB4D0 zQvDl}Mr7>3h>VSgjGZHfWo!w~v{JY+Gvgs+Th;TBq|}1^;)VqY84KCV-A|=&>*|6L z89Pz@hm3WK)B=>nBd`D&XzT+n1{NR_wg6*o*DXLRwSSVOTv&jzsb#aU0CP?7~SbzndB8|Oa0kV5ot-rU>;x?_mLem1QG1G!+0WvFNO$$&lEx;P7@jDh^D|GUG zlw&r}f(6KhEx=}2fX)5g@ybqvJJ4#_6lwu-rUlr#c};IZ^V(i+7dK)70^&v;BNm`N zcFi-rw&qso_}A-ZU;!2wCnRgN?K>9WgBub}3-C~H-2CenV0gR}pzALe3Sa>i!U9ZC z3Pvoz*c%pL1bRy?Kn8t|1HWDG{X1+x*e9?7ap%Rf0Rxi0+Tn?Zb=M3VFg8~2=9)8q zPNJVNM~oKNXa1ZYd4jFCe$9vtxWG8m@z91fH*7#1Helk01x8s%lxYKo={5>|Ocbu$ zfb)ZstpBh9=La*aPS}9+gQAtgUz}-_vgL2p5gX7-p{Wg+0UJ>NZ#H0tGZr>r1#CbE zY(Ujl@NaBDCu~3+6vf}L0jU<)O_&iI&^Zw_4I8k4jukc_0~;_dssT2jQ;kDk-hvIt z!v-vpcESd9Y#^`!V>i87z6LfR3t7N@#|Auh!v-Xv&RC80zhq=fYoxGUcF#`W3M?Ez`h+H)VV|AOwspQju)pGs|=rV?S2@}Yml^CG;CSRL9}E!HRyzr3GxUFIPFV&BS-LBtGVO#%00 z@OdjATs58P{K{aqeumE=YC6v`B?HFB*lbU-LLhCw?zbttsjX3I>+!H7{z-B}+7d{c za}t%e(0RK9$=Q6CXK$Y^7V^>}wEUs;-`SbY5smG-ok_aNps&650+qKrsl4s}hrX5y zPo-nTQ)$z}zRHI;1t$Neyd(GF?ElP(QsUsxNPwOea4)=JU0yUS8n1t3c;KxZfVc8( zs-ty3;)0I0Ylm}+DtH=unLSfP!T$t$FDXs@gGV(I)%S>v=WB@3a#X$m2|WsdEd9&p^@$O7NNz&@uGwC+r|3pDyysVDe(`D*JjByPGV<5b1h?tu?VraY*sI6ZxA0 zQfyou@##w`ABCXLqlRq1yz_AW4X5>%Ekq)zlCcrKuyHyUYUbTno z->nJ?t$R88alL*W(BU?8BhHfL(LI`dH+o$_WJvHSN_&4rEY6)B)Dk-a>@ml|C5a!o z&%rVzND=)DV;z65w=kHWUKX65?ilK*E-}KIOGWNmQnkIkzaTcTTG-Uy(6KSBF|?K+ z^DZjqX88Dphx1AC;pt?aUG7Q8B9BAl4N2otIR7&ScHB)r`b2YYAl>w@OWHm@Ld!}f zrY?7Ph*QJDK7?Lpa~ya-Et+y4I6(MmhFRR^b*oVc&sF zTt<9VRaK`ZzXiD4i5t}S{$$csK~C{o0;?ts9Cnb?{JXr-TN+z9aR8cybz{TkoMfh4 z9Bwy)j8=V*q$kB1D~>z)nx1h5JFFmNYGP5dk$ZEmoMOpdoCxYKo5wQ zJ~HMhy#A(9%=h&cDd*%owbJ!*&!rDfNt3+|yz2SLt(Kh14&!++I71R9HwT^zEz+iF z?@WF-_W@{_dqZcY7WWs=y4O2p^}Bh^fk#8jeN=M1>`e#HZVq&W-kbIS_}!||pytrt zoxTg{H6e<?8`A0SUSD(9->-#tT?%-W zkDEYJvp1f~KO&-s!8;`NA{`?*>a|4mC*ZQh{OvBjEq|JQ)@7~cu#e-YK8sU2 zw~^D5*zx6}vo0zVa2i4x0G3taQEk-B%bYrJ)RP}lHP&W3B&=T)|-B^$D z9}l5@Z-HHf3_F`cdvs*cBRuyYuQqpW`mDKY!)NRG#A9d1V^5ZN#1=kmYm5q=lO9mR zcxdB!6H^ady%+UWUR{pA?N`6cVFyeO`;p0EPhID*ofNyTUuFEXrQgzTJU@38_kHmF zldDyx%n#4VV^`hKXXyU>IeE}nL#2@k^~AT~?-f__8!bH!(x$>w*CvpUdJIx@#K1g! z`b~JfE|Dvw3RoV$|0V~$d-I!{p<{&ofj`LkLC&Bw1-ZS*&D90X4aEoT zd{O$dJk?8*=eDTlFg_0*-;Scsu6tdgK3hxogspT>xOvCu&z@Bcei85L<}3s5cU(*> zKd-q<{EY4ebEaS5BhU;FnxQ9$%)R*W4nE9#1};H6L;rGQco59Jdo}rNPbJPPRSM04 z#!J_|34a^k+~vK5GyIxulMD1ly$wgPlXtzw%j35N#*d~xLwciu`W0s%r+e~5doQkp zoxo9R6*LO_jG6L%asa0i(w-;dCO)snH@WxU%(u?2+&;9yM#`eM7a2HHfzXCO5t3kWk$;nYJre>ShhMRFCTXq;UVum2(86EqUGK zH0}~w`5pOoq_xXtYpp}Cp*0Toga5fTZu{Vk*53F|YYG3-+S`j;%o@zr=FV3B2@lEp zGyY%j@bdqLhg6D(SHIJ;D@b%+^kQr?tSp zv_|njYq*YwKrLvGsGEyZ*`5%Sy_sFw+D~Fr3Uf*qmV2fmJ_N_ASyfuliZa$IsnV$Y zWapNZYVRvq3DexJ1TB7QiaRkcVREvo(3qcg-tZ~$*(q+EP_uiC`I+YoUy_ZUSF>)h zTV-ONacTD(lp!dcYOYO1QKjMh@s5oqmOAHiy+K{j939XS8Om9++$GCjzb^=;e{ zmG0pn+l-A=u30<{UW+~3+pY=5Y*HsjDku7jVhdL1uCA4zqG*k zF#oLKvkVwoY=JR9e*pW%Hmz#1mQ$VU=APj!!%gl8vN8G2q&QI=&PBSxB@KU`96=mn zv{oxE@am~?I03+mu-eF_U_n-uSJq_og6<-nC~BLM$iu5~jprMD4LL8CR2HT1l^)*H zHWj<@+bm61o^0cx%Sx<2_)->PUC}c-eoL>G59y$BB4hm;_M)`?@*f~X7~cIqxY9Bk2Sdo z&XRz8MVOwbc$1UKIE!8BO@MWq0Q}?*{n;I1f@sY7Pttsc5XE;97k9@L7pZeO5!NpvPhl|I{en%WM+*&ZO_}^{4N&FDQqd7l8eU zT=?8!H=9;*%g+A)^T1JfI@>qx?@fSDl?1*>5wy=z;t-h)KGXHZ7u6>SNvw_>%NVv@ z1K^1C+@{`JukOFuHI*6=KHzF@UOb|hf=>Z~j$vBw(!~Tk-pB?ET ziCQvZCJHn!ryYK+MBjgu3(CQ4=(x&%l>bZ?G)9*J-wF7`7ilfuE#XIrINz8XC4NwT z7&>M>v0dnCr|re|^a%7;{!wq6eNf6&Wo1yR^eV8sBbUVbuPmOW9?X7pW9+77<SZ6(1z;1=}A8*Lmx@G*9ks*c9HsjM3VY7pT+^GYfv1G-D7um&G`@ zd#n96leWiW4WyVfy&p40cMW1eu|Jezd_R=&7bkNUns5t^f?b!#QW(FS35YRDoMszY zIk8#{afspT z^w4rv&)E|sjo#PviJICGXgRK_@5MPd9(N~saCk$ae4BFe2>ami1VXf8IZ?C9DKbt6 zA|lJYqG?&VS%gN`F#FBmoMaRFu;)0=JP$pc%NaE42qE;;;dsPr@P$Zy40tB#s$n9` z68lWupYGnUAL|#D%O4-_W5tg*Amvm4X6XnB_w&%5B!k|$32h)!_o0pCtcZL#=(ZMnGIcCT1!dq7OH?Gi1v1LDVV$HbT8UKab}_rU%)0jnT~hqCOEc&#oJ&F#M*QOA zmu9G#SK^N|K(D~XC7}T0{KfVBmBeT!Byg0RJ^nBg51G{wsNKLzB%T?3<1>ZyRlU?( zlvdhbl!g;KX$$&ir&aXNOPk$ao;J7twzPTu6=~)D3({`yUzm1Be`Q)_|LtkD_m{3d zEn=Vgx0aRAV0Rxlq!DqYXcQB@SKam0p(?@|@q0MKLq=))4pr0Khw4-Tg!8Bc=?AJ+ z7U`fx_SULAQb#S3-p@0_5K-P2EiJeKF4%ETx7Urk60Cl$m)5uMP!oyv`hzySLzJ6d z2i`GwNARxu&^p@6Gd$8f+I{lSJ@olliYB0c`w!hqpANV*7Ei%L>xpH!0yOb9k9!31 ztkzYPlOW=LV3djG?|6$dK*Ht|vOj#V(XrI(>caE$z%0U?QzC?nz3k-|7!qz%j zP=!IrjxrMF46Md!;;@17FcVa*CGf;l%0HOFrQj!QPbKLHZ2nCcvH(282_6zbriex0 z8bu=IDvQ8NMv-cf!WJA(APs=96Us%}0c9ncx)T($^f&8Ls#ITUWlAk+O!1}Wq*jwg zw=X$6r3yE!*Jl61=H#yPJIzTzT`IBJ>r%AT%CuTi4SwfLts)jx)xcHIXoEaMEapOPNE^Sfro&u(tETMN{BdcJb^u>;{M^RR<%}rQT_C&3JB+}XI)2= z>5ZoFJ(Yd8M~p=C86c7#eF9;*G-_`9$#k9tB^{ zrIGe)&^|#P^$8|u^sYDc^of44-vwGZVSUt&^a+~ow?2e4|03&;>P22J&3n<>ZcoK` zv9%MYjKAAX66#!{{4)Qb`g8cPmJw*Kd0Xt)`;~w@KUKJUwHkJ97H-+SLH>cM>o6UG z&L6#yUo`@GCyzVfxI0^$%KH<0nc(SsoJf2y8vgV%`6+5UZb1pS{R9t1udU@uscnvE zsEl~y9_6LV@U8nv%IimUa%Q5Wx4e34!d~rqTe>H-Cvc`u67PTqrqmUz%<6aIBzV?a z(_cuJbg7oby-M79%6F&Mknp^{Tq&Vv^rlfC>ZZ5Py47jcbk2;A(^$8<7Him1v#QoX zFTvATUuIw_+(%l3ceqhUBZJ|JQwH)gQhXB}DUDd(1W(fu%LXB3nrQiOFrB8gRk%55 z-X^5?p+r|2+NILB^r3;{+V#6oi2e2VNBLk`7LQR=`)FHWU1j)2=@`s+L~l6nZsgJE zOmT`|(o|>gU=}PXrz`k$R;$Y$?8!dRDy4uh!rW^T=)1H1l2;?U#A=@cLcWr?@EfF+^W@*+uW%(wJpF^%HZ= zJ@-Qz8w)ly)?y8Mx$M!sE*e)MJb&jFU0;FY8Rjm$Gc2jpV)y%^qsX{-(5xag&Y9`gD za(a@1Rc5eNtT;xb5vZy%ouZEO2`t1(mZ(h<(hzY%jqs1gdTep?D+SFJEXqQJDXU9loMm`ml{=O!$RjIe!Xj$DaK|PJwWdBM44=J~nDM4A;yXM~9f55- zsb71ai_@iE8WHQT5xAN74;lj2qIt)dr7N-pp6FUa#}_p+ny(jzZx%*$i%PDdCkOI~ zKLcLbojK_v?O$9->!_l%HBz2HW4vC9D5Fidj=-+%B^ND(Fx%|m8BAhXl|~3L5UFch z;Oe50^}jGe>sBAL(!1g2`u>+Yj5M#}--f?}9Rm+rq08gWnv>(P-KF-jQk0#Z(_l#P zl)@Bl)it}C6L%NmDVR*PXY%o1H&CtrNlsLyVSOJ*t|aMdgqi9LjLq$K%Jl>{%QV$8 zY~ZaPxSea5AZk9`AE_qyOyB3~c27yJBTcu+>QnGC#~94VMSoH*Q)#|7gAv_sX83cI zt0Cmzx1*lQrdT1vmyyG0+XGU)?-qgx0}UBZ9pXrQN}N_h)@BqMg>U+9VPNv5OrSAQ znlgO16s6)`D83icR%g=N*`G`ffIB8b>!-br>^e7@+9YK7U<5<(P5!^y50!QfFveM7m(dD_f*a;)3TxfwYDvy5Zt1Dqx&nsW$s z7;A7hZgIiEDkjg zc05u(XI7IBkUSfnmG;1qW8J|7&)MUDVA=zRk6DIw)D>aQiErB3cWSFe{)oey=l-GP%u3_m*jY z4-t4uKVj7{27)ufvuG6^))aRn<^=bf{WlxIj5sY)jKW-Z-{MZGA^S2I@W}34Y+7zw zKlE3IFF5iiBH9a%^b?B_B0KDRgJmAXwh{Z)y&2uNBu=2S43)L#0>@~bfyp@*@DV-m zzFGfx)Xxo{z&N`>4Stgv#-iBn}q-W<8E~}Wo zuo9$wBd=)D>>GL4-*+gM2)H@hjktyI)`>ON;GsCYO%!q8aOk6bu%3r5;BT^6tEEn4q050U!WIm# zy3Rmkj;8*?>VSRy6ubcuA1UNY0@cas`j0eMMn~YWV?8R>%xV57$0vdI1IM~m@xo8G z{5q{8a7QC6g=t(isX0n(={c3zf;jY^o@c*p$!iVp1rOiMsgFz}!);jGx&;)R#M^^I z+p*qq-LNZ%(L+*e(u~`9bOJN@*Kb0h0D8*>)$|&&a{_|{r?{v;Q3w_}>PR=_^_WIj zq(jIx+R$3t2|n-a3EtuuI)R+6ftaa!P8@98B?#m|TWPSsS86;RT&iXay@qGr-DhKl zx1m*PrCI#Y{ai;>-A&hk#j|6=VOwBgR8K7deS=;f$w<&DEtJv&SQ%3$Jg3%?N3l{` z%;(Cq=TJw5K)js&+ihPMfr(6ymCeyYdy+8*rl*!!i1c_fU2mrCX1a)Bk#958WoA0c zOeZk3tZ1eycnU+zbTZFE)@G23m>fJ229ILdX*@ zbZ>#wz$^%xf=3=XmTDV$Uy=b*lb!?uDo3+`Sv&xxGR>W)G&qX3GRyf8~iMkjO3t9vD*Ju=y ze~lpAqwZB7R;h)yPSpcH-pDKA2*N35?A!0gAZ`sL{hJzSjUse7S?NYAEozO*YqX~I z8(QCcFg>u)b(Um?LU*9t&*p|g zwF-0a1ofgA?BKbFL!k=1Z&@7*l_>0Yc6CyuMC|G$OiR$*u79_m!>vql|L?mw--k6J zsY&WTc5|M1z9^WRAFtKs6a=T{S54M~jH+d~L{-Z*xh{?zJ2M)wxer!G;^wl>8K8xO zOClM~i5!Aw4H4Tojn}g0w2gbvuHqN1Sj56-%j53++x^ZG`fECEmIrW~%ojHDw)V|nVC~l z3~p-Hv$&Z0G{Yqq6kjhf52-L@Y4I9(FDx2!aA_p7J=ze5r3{W}YDu*om{v@Xo7DMr zp_8+8&0N48d?BivbjrTRKVo+-1d9sy<&a;_RO9HL^-W1ecvLKxM0D(nup>bWK zuNb+j@1ncrI0r=-f_Z{w@vE(8kKcxPThp2I$8#8d`0N0I2kTzE;kRxN99-DkbpT3O zN8kY9QUPZJTm<0$V#002n@bNO{u|()FyS5n+%+jT4aB~3X0o)`L?#F=J2DmK1tuWy-0XG$JF2LPH z_+#lZ+w)sM-3O>~fWmzn*fxYLTehZg*$RZvURJ+m&E5Yk9vz*IOgJspHm&nr|DIF> zZ+~ZCMV3GacnWjjr+>Qk46iWQx4HJrI7HDWgeb$Hr-qj6^duQLe+_!7zN^val-F>G zkZ!I}5v$}vAGUk8ic8vqQL3OP7?PHwiQBFH-0)?rVSj&7Qd>6{%j<$+>-_o$9L^Xr zvuM0+9Xxqyp{}~t?;J;l zvf>%w`I+Un&MG)5<65%wB<`2t!{A9iN#>(W?908&=EKq?`ZWEdFzd1Lr6(=3utQ;E zh)eSUR|gVGPFgI$Qal3*@6z-@SAuquX8&8iOMMKN%KzP zQs;_ZN577k4OA~+wnvunPQ-?vYZzQCyA}6*>v2DFYg8F0#dm%adHo%el;uHu7L>1` zzfZv>gV#T5kDv3%Xu&DE?wG5oFZM6XW9FpHwa$GJzZFu6_mA7TIlD)b`f-W}PrK~d za~>TnNH6s-%Sl@FVKcMR$5s4%)P)?uBI9QlbYo!`7(sSRk(&5HpR&>3^C_QZv;n ziB^MHZdK=~QJSQAaN?8g7=!I7>_yw{_E>!AKZ(rNzSRWXW=K6%#HviwHfqtz1Z{>E zf%`yXH4APyShjukDyK*|+0SS(w!Mc`60OPl1mhCoq&%z+8L`Vld*nA_)iLUiR+bpj z)R&AoQ`zx%-E*7Kd~dg*E*pv-SAD6R_&+v)gEa)d%Q2vK1hyZEK%ajq)8}cn49;f6 zHve3GtAE)ng5B{dbuGpj_fTlhxz}jT|D0F$x~R@k4kG5~l~FNf+|Ni&)C!Eviy4g3 z|KG+~s(qvJ7^7&8F;BZ6V@%U-#Tb*cF&Lvoi!jG1VvMX79Tz<0AW@nH(R9ul^0NEB zAHXRNQ8bJ>B0FBYr`%{>Gq(CvwCuk`N3{H``WD8Mazw&-w#jGJX>sXX09xS16A_ls zp6#z!DG91u^{7l$g4W#iR4B-$t85fxEKcSNe-Z2HIYI0@ecFOma+OfIW9e~Wj|;n= zgfR##Xy#6w^C+UZ7MiQF_6KAuIL`s-JG>UHsmA75(lP1{oy5dP#mC1-;6wk>*%dLT zJZ6-|d`8yK>6e!YN@TsPeR6_XW*M9xQph&dh-&Y)SW>*aUF|s)icnta^<;{O>XhhJ zjk84Uc_qYb$1fN?$k#`s5Aw>k{s$c$_JpDr4YCZml{A<4jICa(oSYMl{XeQrWyl=< z&XiR(jr$p$sia#}PK;6M$qZS;ITj0OaxvR+6KFCfDJm%`DFPq*Pa^Zs>lx^E26{bK z!(LK-pK7U(tq<*a_q9kR2yK1YVw#INGILM;&IVeK2k*BIZn~zBPt~7C`Ma%oPK>C; z9FfuMi6#0>BhFi5oXsl1h$6O6U845J&lkpSL2MPL{F?AV$K09cmvq z=9t%$po#aQMHYFFAm=BqEuYhGNX`95!RQwaaaQ&mer&tFVotaQv0yfS0k6^^d9#gz z*<-4kyM7h=?VLDZB^vm6U*>oY(j!;R4iZPWm1)*VkVuk-J+Z;#R~2GWIi=8(RNaJG z5z|tF-q}P( z+bhfH9UsM`ck)Q@c=XOGTEN!?c#LU#nX7~w{iAa39twONB5e89`w)*fyml$KZSFdGwVO*(bAi)E z;J?+XpoR9#x$1y)Dn6V}#G8ffG0;(YMA)x(F)cz_O^Un2!k66h#kMaEiUZAU^*Bl# zm=|hE%6b~K4RQE$=nbr5uYSOoSLy*hIpC|hiJloi|95~VELMsCZ;*|b0k;=XzO$sx zz^~pS{tE^_{4MgDJh*oZ>6Q$Ql*;p66A%v!)Wj(dRO6RyBp$qas`^pK*YCEGujs$X zxJ2{&tFIj4?*?Z8IO__r>T=~C=g^6Hc{K{6I_m?kJuD3z-%JRk^+sX^*QD`Zqd^@J z292!T8Ys6CeKy4aQ)(yB-+NfM zdTm|Jn&qnzE3=mR$thg;$0JaOqxQblRjXFiIMP?FZfvTns&%Zn%dBVufi!2;)SHRs z38q`w8#7izXKY?w%AyVtGiL4laxT>uzE6INh>}-E(VGydYJ?W4#SD(2yVMG}Hr8j1 zG6i(%69=po#8{HCeu{ABAwl->Toir*L;w2IXiZR0t7p^?REq*>k-1jHiNf?0mD7`r z7L<2+ciRh(Od@Kp>UbQ#eYhG>(wp{bck~KnY&EZpgXFquNS$B4Ri(@`nwS5F679ci zaQ-hY@%}5uVg>6Wfvs;rETSRYcPvJHuEHaAB!DyC1(|G|PYmsO<=a;99GR|FrQJ?P zLaBxB95wMf(EM15cVo+58`7_(8YWXR>#aRF88dHtG(XFc%3OjeCIF%{bL z;}f)Z`ki{*0C^}BawcpQItkwV66XVt<@6vv@GlhB#~?al>5MpQmg zE&7W_XWz0lln1qg2hm7$)8cO7e!}tEw4~8$t(dMxYEgraeRZ7z{oPSYHYl({+-nt0 z3KZ)LL4omd#dj!>SKn5Dul@lPh!f{&w^IsqRGJjnj`Au0pg`;6_#KzkfU>-qRJ{lk zc#}zikOqdO^UIS}${eG4@job#H7O8s!z$F*fdcpaD+Q+YSt~$+9p6-g8=DlE)Q)?f zwKphm0l!0z@K`~C7DUSYwR#BFh`Rg*dmS(Mzjsd1vXuu`Mr*Og12dvbYTH`bHkLv? zr2Viv_5rFp{uJL~N?Z0VEh~Yx4A2(a9jnYc$u9pH-dDYFA#_SO zL)YA^R;!Kb=b*AZ(UEGTPNN_Hob3!8euF^Y4S;?+1FknoS1vPCV70jdr=3DxXwPST zWaWeQ^|A)G^RW70VA5-(%Y}0vM@H8E0Nz7_7`?*Zp~nfvOv^Xx=3368op+*nwXLTd zdTN5vd9<8#Ztl5LJ(A0`kB{tm?HcLHW%NM$j2xj*2<@pl5v#Oqo(r10`Pp2i2c^f% zfX`2eR}1i#^v7a7%(7fk%PrS=&VIB@0`qVZ^>L^?@fRL@>=&~xQH}(gfS$_&KLNz( zakGS&3xp)A<%0QEJC89;! z)hK7yQVaMs4uO4(*w5WLYSs7g+Xced>9Y*Ft+*uSD_Du$ZH^MxNvl-=em+Z27+ZW!ofkEs?78ovSD74W09K=eV>c)v_> z(qaD+v|eC{^KDVk6D~bw4X8IA$|tw5_u-c<7O3`U+@Ov968G&|aCel|FJD>%Jt2z_ z>^&bgY29X7tllEX;Lp?5GjfT#Ue3T_+c2e{1`XXA6{E(OlzxHf3`~6;=Z`??1EBPu zzE00JSOu(4OPIQ;h5BINomc4%I-1LCJZSo~O#L}=!e{H^ECy;^6UC{mCQaLnRiNqZ zn@`Za-KA0Xa6rLnXl+wDn9Qe59*;Je`(gUZEUxWO33u;#ob@jsQ zue~-CeiJ2n94&FDLajym{Pj3qsloHOYWQ1}`^V9|mT}qYKU33x-Jqt@Jj66y25Kq; zH7$z*HRXbu9KZhUyh&EdFDW$@oQ#bF%G8#LWWOBR3BoGi0_!&9};k19z68iK_}c=unfLhp;W zvfp87p-?4p(eAI@VhHEXUclpB)`uHgI(oy1FnL~q+-pfGcYY21Em>mg(PN4e#V+Uw zA`&f8Y?^X-!0RH3R(GeADw-0mT*L`2^VF5rjjb!uLJKNY^Gt%x0;?Rsz6Ub>*e^KU zTM5q6LY6vJzH~gXuJ7Vtb$IqDG7_Kh!Pg||b1I@fvC_H>eb$vqtI^MF=a&xS7>Bz{ z;DuSfkYSE2Nltl7{4se6<>4DCO+cw8Gfj5NOXSUn_^E<}T$%~_g9sbFKc`Z>R1UU7 z9L5c<8SkumZ^iMmh^x(b{izDBcRFI4JXhiAo5}?APu71{@vQSC0yi>NW7NAuxgI=g zHc~!4y9@84yS^@JRcu|~6@gzVPmm?!@!}U1I(t9zU6j(Rex;`^&5@Zo&Pnj)|RxZ5z_t&C`$Cyra*j2>i=k35{oyvx>TSAVv$5eXT+FHXb1tTJ#-zI8X)sFP3Ja-S zEE@9=rRM7!Dujbihx2uUwV`}M2Op3aa%35 zZ`&r#&B5RM%sFc5_*HmSjvZ1y+(k5=TV@9yFT@ORn?)mj-m3A(&$N1A6GzTJj*V2&_E@rQ$z97u&wT3~OEbg9(9LcrPdlW7ie=~c$ zoJ}q5d4)OfRN8ZZMd2yLz{kOUI}UQB(wDJ z5m6uMX8TgywrM|={C5`{rak~35u6#)Ut>wWEaa%O=k|1CSF4kx_JnuaSae)mOkCuj zEzTk|-|tFnzR#7}yu>xOd65g}mr3b}G-C0)zo2=V)l4;6{I(0kYb5!y@s4V>0JDd8 zKYmTbZ<|tQw{5m!EU&Sj%wx@N{4MC|WTSh2hLU;baQ?~7M7}nRLh{G{11p$I<*EPS z-3!e+>gBZNsUq1HTRn?Ncvzu^M(8`--~_ z-~X@w^ZEZPbvgThuR#q|{(~DiZfC3cVFKlE#pgrw{d}HgA;CxeszHB;`qr}J#jvG! zD%SIva3Zuytm6 zOU`JltGrcQe!n8PsLcZSSj2!|f=50_dx3j-cr@(?pBmW_EjxZ5wqyJUm>a7~Ft_MC z2tbUMjUPN3C$e+l=>!y#F$G(gr?Wjnv{nEhq4% zez43tOhBs6{7(bs;*Mv5L!YMwbaV1gERCYZpxtET9fp2_yKK3C|*;8EKvN zB8Hf6+2|3iI>B#v_g&u-%u_GzfX`?XM)SFM48A-TIuMJCr}fpoGbpv%u%`-JF5fj? zp|XcO1n*>59<_{Dd2lnV<5OU&1NeIk`#{-_z0m$OAoDF24H<6KN}J2$qy3$M-5u5S zUFuqA6h>Clw~k4wg!0wkO=g2vYPdvk?V?q>_o6^8!YB@yqoAI;v7<#f3w}#HYT+7X z?6jEPb!e}8`xj2?f2z5UXcrIV=WgWptKZLmpL3X+4mw15wPnXk;cBBkHFgjq#C3h` z>N1qIm!}EZYv1eRC7guAySH3)1JXyEYl}-7m;Pd27S;h{-14n~yT}Z0YE9omjIbES z8F$sW2k;%gpw$qC~Q(IZchd$?`|G9s1UQ|9sd#5i`FKJGwrxN5#v|q}? zBBoe_@3`hjCw2a4k$T8t-Z%f2d)knaz5J^4c0fJb(Zp{<&9{TLY<5n=_eLkx6x%zM(R=z0e^8!3Rf6{X zo&3rYMCOBG0#*TMgeTbM=h4G^cvucVQwEEXx+GED+=X|u_#})iy!*qmL`PdXei9Dv z&b$g*xUs=@lZ#X7EcWNiBAyjnRY|-#?oP+;fS=Xq*>XlXmN=nJzY3mfqua)d2|iw? z+mc%07p`?C=DL@&9X{gSR>py4)(Xg*iSu-dY$TP z4OK3PQK(#nv)Zt{_q?9yl-tK1d7L3`50oJ(z8LMyKVr1ge-CyhuFOPa4?AKKwk-LJ z)9s4U634=8%A7N_$ zeaK)&{qsTPves<~xZ}9mNOK+3lbq3-X2Ljhp;lNNV$3ub-#AlkDJ4>{vd z18$%H+cCtgV{vMksf#IrF6P_63;4eFk7<3Q@g9-s%$iz5{(H^&UUC)@;A4B2!?t1{ zG=S|mIa?beko5N*r0Z|#_{;TJc+b(h*3jZeDfIM%>v8^%27CR^b<&qxR9TJjKXbuK z(aFr*)9>Y!Dq!uF9WR*kY;9}})eN@VvO^EsAP2OVbCL!KsJK!0gW*Ulon>KKC(O)} zsYX~$#~J7s*fBA#!_b!V%c6{VI5WmC|5%x99A7#aI&?FUVr+?x$ntL$* zn(K&028dYBrU9znGlli8);=MF&xSN1m4A)?~}vP+OGgJ%dgQ`TS7m_ zU(n3ZPplFzfsLwr9I~VUo5Jt^j%)EEz1GBL4OQ#dt2)k3q2EQCQOZ3c#F`r^l`40|Ja zDWv_yaW2Y3HeIkAQ8dWLc>JDNq@jFTu{vlY2YFkOrrRqR;rYI$6bsc1%y?~2G0 z4OXX$AJVY&<=dQ#GH_F@DUmzC(4xhH{46vFwsd4yEBl=dmh<1?7nR;uq*3@CYZ6POKhdMrH(gi(jbHLP4WCdIA2IQ_)b_K zz@o}i?8~qyz7osUD8sTj+6eDH`#FQBiG0MO22oe6i@q(JEyk>7i%Jr_;EL+z$loP4 z!Td!&!X&l4Ny$}kFPCcVebF-Ib8YMi?uika0$qQo=4s);16-*RIHS$rSfwY&V;J%R z#muu3c{Ya1o|X^HXZ3x5wEWn!N`1pvFkUepE5`};*W$iVG+V~0js0)3Mi1qvGU{S$ zdq09v0M$nMs(RhSlRVwFRiHUjel6|W8m&&DoQhE=Ymr>YA$cCMWy5{}s}_oeRU#~w z{WgsF!O!o+`m&`IJ?{j#s0>5`Q8S3bGqv0mkyy%_WhyKX%oQ2lkg@zHPMS|Glr)Cw zVbFe_Fdj|yuV`%EgXhDDNsl{KdVd|(@f27=wQ}%?dAyi23?|^y<`ni#jaC6kTSA#_ zyy~+p%2-t5#=TO4%EMCgn^Zw&=DLxb7cjZFHS}Z-CssPBo-nMn&kG{#jFJ`Bo6f2f zCOcG7)k41*Q|b8sOZ%Cb#-)1(X2-p_!qXSF{1_T?=pAiB`sD{C$Vh8!e7a{P@0SdU z10fk~-`2Pk#0+eYOLSdaNpT%01_aGZv@1d7)KVKuzwiKi*$$ZOXH~ZOjI-7Z%!zA5 zRR1u0!pWO?cAWc4U3qNxG>CY5y5m{+-2Z3Lun zm^C*do%5^;wfR?6p*FL}^45|5`y2cJ6bsFv92oGAJZ$u*s764Z51ZYgwaGBoMlsjs zlhL#`tmDU{$#NxCnU|b?u-hi995mg}^oX!1@qB)pw`N+q(|zzm#sS+n%Lxio8`E86 zjYM*IveDNxfTw-}U93*A={?+W$|XVuF$nkD~+91AKeuAnWFKCfbl(yaVBAro?O z8(MXvRR&wAy=c>^JYmgIs{gIsPsUTb&c+&rt*Rr4O?T%~BCmqn|x64s3= zh*xsqx>d>Pv2xH`x#A&>v2uxa0ov*6zPq7)#Elo{5hoBgCAono6}Q*ucR%2Y&_>3I zY43uw!6}}`cCb4t9cK@liX#g+`yPz5)#@D_BO9FCWf-GmjA&#X>71ZHrwCeWqeGi<$Ztr2ZJGXCYDPLs=~}${M548H08Fl|7TcC7*Z6N;IqxGhT)%6zbPP zKg=G=@h)r2C)ohkW9MM$Ql9POv>1a0&dkYC7rUtS=TZM+)c+dmoWKI&>@2l=Lf0s& zhwgdls??}a z5^SG6z&k}Ys^<*j8+DxVa(Ygpre0=7d^zWC?8LjxMX+=%|KS);)v|PNwCcuej6w7p z24A=-Ka1KFXPj07S=6&cwG6%fNe=VhX{7ju1-`uhorHAEvH_jdT*$aCKP?~Zj*h~) zZ?WZo>QjCJiQ|>6*NqI+7!7IBs}@aJC{2lJlzMc|ItFLjeq|ISe_CjpfRXx3VcB+v zUDC!IMnc3Vk7_;ow1ja97*{rAM+enR$cPqOPr$N>fQs$fS@glo`utLTl3}<5wd=}VM$_GW?~*dwwgqn?h!xJE+`5EYOQ3wm>XRABAY50o7_ZuQ4S1=t(3-ci^m zDc&6Ko$GTjcZ9WGgjNIEM27bL^9@m~!MemizHtiY(?=n7uyQ0%w{kLWcY9tpCX2mD z0c80VF(#yRW8_*$=c)9~h6O#a1WFF)U~euev;+%h=V8wXC`~N|6p~(HkA}`=QvOUs zz^<`qw@>Bpc4t1wcN=PS^>rG}t=uyUlU2Ocw9J8MX&B}z-3%~OB!`St`w3W|uW+8pLE zTT z1oXCM5t_RTh>A`4V+-4)w>DocDiLoRu$7Om^9=CNB66^*Oj3de6jq*L(c+DYOj{sU z9hc=$xFX4K^Oj{7!*)}`Jd~E&T;s$!qj}d?xvA9zl1vlO0WIhE=oI9|xT{ ze6Z8n8hY?C=09mrDz9tJ35OJxqT3P$T`LV?cc#X`o9F!ZTE`xmw;&T^LkEkV@4MZ|T zZ;Gim%D;M3h~E6%?2ScDl^;=X_6bkJ$;WHlZs9!7?PYO}pwyuBF>Za@X0$qg#Z8N& z{gp<%%2ZAp{qg(DIAbcybGCMJyK4+?H*^nq6r+wfpT}`h3*+?mkpdq=^nRF1y%Vtx z^ghNJgLk`(X>(>9?Ah6PvpKE1Gijy6caM@Dv>r$7rMIWMBI7xk<9V15Ol~L@i7UI9 z$pN4F^^MJtI|VIX2iA&gn1{QD*5|9639M$0jrmx1hGTVz`5$JpsjypB^g%#7j?8|yhRn{ z0te=FwJjrEmeQ}VdP7`E?;OiSBk0_W8`{P&x7r$mJ?HY->kVOLAST6^9Qd!dkAYZ9 zh5~pHoJ>ZfZp^Cy+%YTWlNcT|7Gngka^Uihi>kj<+`Xq$T$vR3?(OIBH+mVt)?UQf z$2sk(PJM)?^$)OkSnPcS>=cW2tYRBS)b!0~;>gYeZgvdT9!Z-nza7wHo~ku&->oV+ zK3yNFX`v_5-MU*-fdzhuUs+m?kJ7ZzMm*(dmov#8l~P;M*<)_K46{^-Z^{KfN#rTK zQDOz2ip`qtOBazx^km^%F+Am3=zgSCXob2pRH)nWap02_8mEh)3~{VD4j6*&ge`%^ zb~bkXoYHw?tBYop&9u%0UU}6)k+Uc+oJ*MpyaGEc+$qtD9p}Z|ZFL59aa}cjZG`rk zsqu0ro?#acDdu-YUjqN|Xc;iuTR1t|P&gSMdzy@0d&Jh9$(E~M;SZVn|51W3(v?u_oAW4Q+E_#{+>;rLy02u9^pKR6b<9EJ zPH`PcftU)PPjIRtBuT}Jzmq*lmac#kc&S^@!JZWGAsQPiRwG{7#p9OX`J)QteW|`Z z@Sj@hvIOmK6Akv@-6{oVR!4#_K5*@ANy{>Vozjy6IOs0O8k2pICo7(k8EP*IcH$Nh z^tG!eg^BIBt5wk}=2f0<(X)^+7jn|U!JN}%eE+5P(!*=?+bApR$*N&D>28YKMI3-oU z=nN+LrvXQaz2(a+Mm6q``++%{{{9A6C&uF$m>LJGkx8Bv{3huwPj5NnUA~6{D-=U| zPsbc61NCv(HATt_hIIZwIm4HClYCVxIHYEqso6+vTTz8njMR$0 z>@9sJh!gs##@K)>efr(&m^tdh=;{B`imK+lkNdEGQ(uq*9Isc>+BlSV%*3} za8kW{Q9i*}W$-NyA97d|gO;O1IW9RR;8^rJ)#JF}DM1fJCITmSW3F(&3v9u@HvH&c z75d!f+Hb|2|q~ol}^j`;4DAsB5qgU57$or6n;yxH|_IHN_m%qnrK4wCR1iy*XKZR*wOVd@!z0zp1%TAJ+U4 zR&^?)2%>BqM!lANru9x~t-haw<_xstS=d9Z?ZXrOru~2@!7SdF9kd)j2!E)L^B#5H z>$?5nX%EK)s;5fWm-ayEY=*r^mLKZmsYca8NF~c|8t!q>-s=O7xetQR8;+=JDu;J1UL_yfc;FY-u@?M+xK0L@7yT(lWNm?Ior(z&iU|>l zp<1*RoqO5ZMA&j(GRESTM>l&(J*DV-S*rtI0v;E~`laI=yw zI+C&eL#daYh|y#K;w{jst-azIry`m$m~e}MHn7{SZvDpi51*zFUsq>TuRR`v$Wr=< zb-BK7oon{%BiHTG`R0pxTBvvA#Zk>lE^zq%kr(qLzn&S{lu5TPvX0-ei=&%yedcv}MK#yL7nLg`3d*3%&4-*whHvWg&(cq1~|+6zmi`8DmO#&9{VgA9hAo@iv7s zS25qqV@>F8XWxxgO#j|0K0cDLXG_P-Fjms!DJwvyX*J8H)B4wZZ#5gdc8}m19?Y$a z^1}}1dpH;ZbhDPC@a8<5%zo$s{w1`o&bcBT2tFUs_xY?;#h2-0OpBMf=un73GMBjz2=TXiX#%icq=%W!A3u+C|qzLu> z{fLW&kza3(xHwwHK6rh^#W4|D`iBv1x)G4HY~xCid`RPbqeQ=}9(-o6nvY0e%H`2b zu>$BFT%OxeqGui=T?=GPJw$PK@dq;V1CiBHCVrGXq&>l_CBz(fCW& zWP^)KG3hAi2{Z%b$%9o(<+KGx75JN0tj>J)dbgf!9?5C8P^f!1%-E@jVRG&9U))#pfWa_JVI{HF3uXchQ<;r+wY#B)TPO zpoV733@%%gHaYq!Vi>Iq@u!I~RL23xEiR&tao;A3#iHGBnSG^5lv8wNokNZnvqWx6 ze4uOw6E92}k9P+Edj|k_|B~0ZA*T-;9ON|@D@bBX zBbq4&PEAihtn?<>Q0BFZ0cmQMO21-(39F4CJedfb%As9{_L-NYPf<9r2)o-uwkKjY zK}V1GG)D4Hh{&Pt7!eueCN|5c+I>^t!lTj4qK$-*1rQOL4f4v+LW_no|5}5_W7VEE zZ2q5M*6ss;T-F}R1RC(2C}=$$8(+kZ&qNF323y!ayj09_QGIe? z%B*x3)fErSohj1YUBsiey)G1;I>L&)u&$l$ISp<-2Dc1zsF(AfGOVs*akxwt)upn1 z)2CDH?K>wY8IvtG7ws$=8h=`&B%>N$8A;DZ&Jp~eoSGT$uW|4 z3Vn8uDGBEN3FrMfSIl8YJ!q|n*{9E~*r(rF$_MT5&CIWr5bXM(owJk=$lE{m25UMcHL4B=@v?=Li- zfE=V%cMcx`JE?Act~K6AGU#cL6Fig_KreWI(--f9r?rB=oqMBipitm3BT}@-cdWN@ zstCa;&N{4F8SQ#Vp}7L~cf9X`&jf1i)aT|U`Q5RX%VFG{80chi;PK`-wtPE>ASf5` zL+h@E_(IYYX*t92(O`BUZ6hts2rI9E!kbT|U9+NWkb8M&l4L4ncDP!(^RFz4q`65bW1&N!s-obGIo(&`LOi!+`XgInpCs2jnALxungW$nCSJ>(RZ z!fL&~Ax?c}v>Zq$sjDUjM@+q(z`O2L2q!cuh`zns1zf!*4cq{u_`Hs|bixYbyG>zRkrjnp?3WyaltMVq7cV_QgzR1ML zd(c9{lSJ=cy2|_s2KlHHt$V^5!2Iy=2;or=*}FU2<@PgEs6H(;qTslamN;67oa*;8 zQI%Za04F8+9HJjL!g2n88jr-pVE%$VZuSQo$)>N0uiK(Q-#-$6wEm$LHNCG{4%#G~ z6D`Ic#F@&h_0=mfZ4CB>H0vLVvsZ|C>co?5^oxD#XRP3C9PBMQJRcTkuJ^4-u!*qN z67g(^4J#ZQpjAX4Mb+U0hXouSZv+9mkoFkj#w1v18u>W8*O9!?Ywl?JZcn3Bx{J3H z=}Ep6kykCS2|+>wYB@%p`~0p?ja@(#oF!CA%i3AkgYHE^H|>|JNgyh2w9hGB=}NrQu^A6 zRa-i44`0XklrY__GaRzxE7wVL0PP;MUk4az4w@|mO0sNK_rbDF!neu}0OMlCa2Be@ zxWBJklhlWYbZdDvMt|N)7exrF!B|3R{~I^IoyV|Bq&*v8@2ac=(K|@%063>z9MPeT z$Y&h~SnkUQSU&vX`jyqt0uVO-B^%O2?O_2}zIq06@;{I!pv+I%=>!qjk~}(ngPH^8 zFMxD`aRc^h^Hfo7o+_#h)fHUvA}|DekfuF+T`*k@oAS+Y??6Ca@c&VQ07>2@QM@0DwYM6{UZQGyms z?D>%S(f@2{Ij&eF*e>ot1pGkH`*@=v^%F=J9)YIi1L)COQQ|oCPG=E+ay`=9q2D;k zGNDy+L!a@W1m4pxor{NFbRF9V4G8IN!n^anr1oQByL^C=bRndq$Lzscl0Vi>NQ?CO zzEPxsvU9n@ej!fzCTXN1%7m?K>fa7_TOHv4$rt2^wO42E<8}quG2O{D_xT~3!)e38 z(ExXW-j#v}k>E{BG_Sxx=MnNDBe*R1A)QxjIIX<;aDppZb%56}9;L_)-x>vu69)}p zC}+wKKR@UcgSxRka$f#DJ82hGFWN10irU!oDXLA&FxonW=@PJbH6Uzfnqmko2vW>f zAy*KRlp@HL=$C|Fi9SyIVh-hNOKU|bQ4k_>IIR_YI=hp1oD&dj3PpG&*!31B}ne_nlxItKMX`h;(D zf3GEt3g!B7nhcpw4eF%6qR#w{W-lesRn+B5R;cFnkVKs{YZcO95#u826R913c~N92zYLhaLVh$G+@JQG6nC2en=q zROHaDEc7s=UiA&aN;3K+sC~HkU1fKIw`pD{2Tx6L_&!b0EEfRTTF8|`JBf9qa73#d zcWBb(e1r@NxSHIuf-VR&)klR5Oh+~s=Xdo7&Y`UXawD2lEi+}gY1E~Fl;8_kA|ra9eP~s%%LY$^OC@cl z!8F*}+M5;0PA6<(M51EBIeIT zjfkCA*Z@iWQb8MKK(+=;7VpZ3lo_DJRHuSENzS@qB&2G&8t(l5cj*iK{k)03WU?qk zog}ZFfnS2x32HCaTeT+*t9e?f4F~n%ORI59;4R?-$NS3AjOA~2Clv3uvQGgcbRwA< zj%a`Wc0E!qKYY^8oBKQk`mZ8bX&CkK&JegCSc*bko8SZejM6&!=|sIfn9YaMPNMgB zLn9_?_Ve_n9hiV`p`FwXY>?pNfM-CZa0VgXjG(YGu|TLcYuk&x8jm&N*3ueL={0He z-IjLA1E@|ATm;+EpuESiTZUls%`FsL9XrWYjPK*vf$tAO?XR|cdeGnmP?-#U=_ALi zoE_Bt5#l&s$3ArVE`#opM|s4GCJVGylx3AHcT$xd);wj1Hzce~=aU|0yvp`X3R}Rw04sY%>jk_L5{^07_{J7m-8(Ph>_7bu9>SnD ze)Q&qF*nY%GC`Sn=uLS}?x4LxX-)>tn@1H+Kca}>ATEo%4v#3m1=47{jY-g*QJVFN z|ElpUY9d4L2IaGI={dRwh*ya{W72iThj47Sc1ykX%m zG@_m6h*B`3yV$C9;h@1}zvLCwGFVSPl##6;wXR9e(vRd7r_0(`)ukESnQGKZ^3>AO zMw^C6g23zjZMvk1!P?|vz9+2Z4A;-E#PN0%Jp9**eNr}YJ>7-d?T7OOfy+x z+7UX0q4Cbgcn@Wf9X$^V8EQYu+(*yh9Z7$VDgt%`kF~styGpHTz_hbP-JZrt zqISC%Y0gX74gV?f;Nfr@@MwxCHY%PN9cu^q-$iP5oPp?r(AeXAr&xmNQ{PCS5&M)h zoa@>XC`XpjC|R^ZZ!q81K58nZ^61y_L`q9BmPi(y^CT)AI5{T@x^GcmsA$SA=#cR@ z^y5T=%)7=!W^Z~H{0A_OWvJn{EO_JM_XPZ|&gxXYNyx%k7@TD4MCFP#RZUY1p?y@C z4IlC!AF1TrkZK=CIl^@V8FU777&AbxCvoo|%;Mg~4lU8`ONsyN)sHn3-9LN~zJMmK zdG4PS!G)-N5A*A1nlamP1fx`+!>F-nq3f9>wX|ym{U02JSt-uZ#=!~}rHi3xwODsH zZw}r6#hF`W)uPS7NH?tG)Hc0!-DhaEyRLRXnjt0}k+1lY{{kq(3*kubYtj4NsPEToi}J8iuUG?hhcAQ65cJ}wtih_Ob&x-kKI{Tk zFrXa4s1QR=!+iKNVH;)D@)|DXTiUbhCB5RQsP@#^pXTuXPmD9;Pge@`#{b9ZV&KEW z47G{DW7Q{NDP2{5yfR9?%{9Na&K2oH^a3q(5Bl&3i#%7OPj}#X6$@aNXdYT&86yVc zXLF*UuUW?hZk`HT8B)BOuls}$NA~>mODe(`h=}S7n}dv_MC2Q?$vG0 zr^j+wp%Z0)A9rfv$y!7Q$@{3ZEd4{=I(lo#UrYX4 zTAD#4Oz@SKYN3-ECWqjlC{j^?-vR}jQL*SoLj@=#^*h$2GFeaR7V)ZRZ?Mua92rx(m7?E0&*hUxS7 zRCh@Fw^|p#OePc!SfRH<@u4{sx z(_ECXc-0uU*WuyRVozszI@($6+FLyeZGfMOTc5>4_dn##Ev+KGiJebo}@xu7kIitP*Rr_a8zNh$_EsqxJ zitoxB@N`wI&Jh9+P9a~XuKYw_*MwC?Lg2l+u2IBkH7gXwht?z*y7V>GmyOen5Xiuu zX|533M=D+8GZ*Wy`qL{{6+JYj#GCBdTMm2Z2`wG<=-on3_sy~D<2FWCu=alxqfzlsPpjy-O)@Qt`sQ5As%21-*>Y< zg{Ut>1<b~l~aNczIKcU9JLjY)_& zA`wPIj)?T--gH>nFtPz9d(38~AGn6(FX7$iz8aFh`~}jAci5+(bfkf#viD#Os!;Mn z_ErMfIyd8-vlHcT1HxA%gMluEJ7S(V{-;&POaF&l26t&q6-fr;jL2XvK?WmgwyD$( z$zWp8PA9kvD@PoI?JA9l(zFmuZ|V3Ew7PV=KzR2P7l-=Wb|d$tD+fSZMT2HlFeNFl zw}mW*hg{|}3o|7%;Mc%YT(Iz_WM&O4R^G)M4Rw6N_TpZ{)}G%(`qFAjX6Rl+;z|}u zUYv;{l9)B@8QqxB3=RiAK>r{~3{L%sI}?RS86;U}kmlS5O0hG8_h&0V%H(=3K#%73 zGH@>;$RR^)M+pZB&yG?NHo%;^YwRM4>F+e$Vc*-yS>k;@31^p7I=fUsPW^D&GkA-- z8!r1lG($ASk>$9erp94Sys*GD{D*hvYt8;4Fi{bk!z~VfLX?Zz zLku)G#ml4<*K5kgn;&-dKvwzZRHs6|4*$Z(DO|K})c4-N$?70i@f7+R!O0Tm*R7B* z+WIMt@G8R5bo`UxelEd1y^S>~Iowz~&fz8!Yw+~cR=suZGEiQ<$fDK`i15EF)L*O5 ztP_?6>MY<5_+<}!1hsAe5?^{+TJC|OJvCXiOdgGO4GgqS+8COvM zVCz-V!Nr7k&p%6;B^CE-?J5+5g7VbOPmYy0&AZ#8irTq0o&9^ zEwmH;>tKYZdO`mqId*>;FUpEOx>wOcWwBK6-zg5$xG3}F_^*6hzLb?zpX|>8pCAS@ z4|kzu_tYG;Mx0`#u1CJ)#5g*$V~%3Yx?VT*QK^t(>6NgxlH*`IjOd?-J3--2Rqq(h zL37TlZuE5LxSeG?$L}oPS-Eq<&RaHzetpUU>*NZyIdte$C@XZUML#+h+|FE!R$|z+ zg1H}LyncXjvoZvKc6~$U`>;n&_b#1i9H?MBG8@N!V4Z3EFrDvhWwT5EkTI+D!_3)Z zf0Vsw+>f#rmF*bacuP8JZV$0mtVr>~0&7rcSb8(@QeXN1V9-3=N`1!;Z|PVRHdhzp zxia#&GS4vza!wgBvho1kbn+!Qiiyy5zOlf*w9VRxJD@=Ii@BgQ+ixSApfxwNi?nxW zA1s5$H-bZL{PVz}GLLgog~@{=M+AqyHWDz#MIzFd_AeU!_5qDG75J@(4WWqJ^jv+s zdhUS2`X;qnzbLBq0NN^M)gB);!83V&)Fz7(*`+@nIRldPn~dp*_C>2ceLhj?0nAb` z)CFydY%Cg0C8JI1@&HEZN2|Z(1T{_-WM8I#K%Ap{d}C(>#iZr8@UF&NPLArXJz2RS z-vRC@O1sOLj5w7f9}!%3?M=WQ=^BX#c)&h{H@9-D^GRRm5MleUF~dg|Q9|I%6i^_` zV%KLAgn(F|uAZ1c62c^C`z|UJXYC03xIN@goB?iOsHP=c9Hd?;kexV+CE~-2VOP9y zQRSiuxU*o4;uSni<%=p7-CV4*l48B0_~4{sSWHBEy5F$-d$67lg?X`A`E2w>P^0ak z8Hq!6S8}}Q8+Wis((;YQf@TGnr~({SInC9K`HhvqP&8-+LB#T~uRs7Vm08yWKB}z( z7SR>JR;e@U8Ye8uSzVO9xOvP>FYjS$g2z|hJ1NH1l(z;PLsWwb`y;iy2|Pp71VL@8 z*oQcMa%Q;Q|!T!%co;*PbilFoxhD9 z>h)(V1Gd=2U;P!>sJVh~|9Cd|kF~IE_O2=#qYMxHt*9~gwaJL-IjTD)_0UiWTG6!kLEPx-CP?LIKOgLMhIRR zCE%$3X&=f>{d%8r`_~vvdB3WC)$@|4seEk%s32&?^Uc2l?r1EGQTLPx zn<~9c6K$ZZR!~-(R%*yvp{i#Hz`?~C%%4*~E^|+&3IY>#YpJa}y{x1&qqwv? zvutc*wtrmH=-@4_nIi6KZp`$LU6j=}ep}_jtmdMu#j636yeCk;ZNj#SZ8xKS2x*Hzfk6srR6D|YK!;Srpvhl*Fh5uUr@X_al2Aah!i$zD!Mm8`3??wqXj zi@2|j&uevBREsUjm7yPVugSEiS^7H;*)Pl%ZNffBDmzmJ|3LET%EXguBTFBWu$*|H z)Ra?2Xzrq$n&FX49?cegmx9|fCV{7m(%|owZHV*4I^gLB)M4T8%G5Qa&z+>}ZCJ~q zD6L#E<|B80lvbe_v+yfBN~@$eamQf+53e=zNiy8Pm$xz8#DN@1QQFN+gI%p2Q{kV) zX_t-UdC7I0|NI=B65>eM$XGSOiDakO>kB=8JM$=!{&mU?u(Rf%8Rh&nFWR9mDK z2>C)^(v$WcWZaI5g(ak&uYz5^V%&|KXKX6YyEE|XPd2sU_<=&?|EoA1=3sZnKjAqT9fYwoU6RElpo; zy=W{x$gCUHSCz(GlAW44;lnb@%o%MgtF$%AO<%!gHA2;&9i}PSrqtxs)Oc9kU9jdT zH!HU*H%^&@eJMg;BJlGV$3fBnj>eQiEK#*k+c4I#n!BJ_)bQ@7!+*0)HmbK@F8adQ zFz36RhJ$x;7mN)XJqPD9-4=7`F76-3hL!BlT)-Gqj0Z2UiC(LE#E73R2P_R&^J{Cz ztxw(*HTO8$we;@5EZvFPt2v7-ZHh8hDbIFt(>XET2kZLwZ|g2%eIk}dsRjqjEsrD9 zaY5Oo$bexjRKT6Ea??)Oj^in!4T)a`k$R$vo0n@acM@vKK%DakDu%_xY!W z`qIw!S|M>YPt@%!Vyg!W;s#DzX6zaLKgZn1KC=qbK8uXme?P3$BcoLM4QurGMtTp8 z)PEP!JIC~Uxu8q=%v#<2nJrNq@OIsbh?T}E>#*g?&8KY7-ePg?wUPvIW{i$jqj6`; z1)Ojo|N9%&Q3-e--r$9N5xzaxaYN;HJH5*jouUX2IgHpD6hCa~ScAwONnx5xqJ<}| zWz8pT7R*=Ym?K_giaqdkH4`1Ujp5#~ZlzNMwUZgsg;vA6ub-y*xEO&t1@?V6mAkCa z0Mp8mRUXYb&9#I%W@wSu-kp<;d8So>CqgK5aK2kQio%!jFW_`Up0G{ql$D@GnnqA^ z3cLAzBVs`$Vw75F7e|n&g%W|A9x>%Jv5?Q)q>!F^3Lu{33|2KNP8bo-^4T94vT?5PLW&|^b(ei;9%I&W?aidCwaO9`J2>XO0F_N zv8g$#WmDs-63-QfLW`h&V{t^kgS2X%>3B}Vk1%+zT}khm{}#IEgD z4w4N2BsNGbKE>@|WEzhkxd1!X2ANRCwDOf_gW zd1tus^q@(zsrHi(B$%rZ;X<~gbSim(gJ|WY8=9{n`jrelL1Io?UXNsK0ECP;u!bs57X5zRrV^%p9BH|(2>&6X?HY4rBH)8k7TziVKAI*` zN`#vzGkbQpuvhT|LV&Lup&dL)dWO4jR@71Ja=^v!&2!o^3%#F?-?)`4gMQxz+6mae z>Z%~;w;7Gui$-x;&204B?=cQ!#i~CXJ6zpdR;jj*)5dQt?^QNUBK|0tB|yToE&CYe z^okT&?H*^Rx!Py(Osl=e**L1xTg#=8>ePz!k6etOF1=qlhqJ$lKKTaBH{zrS8~07XA#J2}j%B#VLrBfH&Nxn;4zcpI-snOu=wl9_Xlx{s4RQC=EyxjIx z%0@{GO~>!~+X+uimi9=n1zynhy0r+mR88XKQ%9nU>|zpZev`oq-CKL^2)DLR8NOH< zs9Jm66N`J0`q}44q7hYl?^ClRwu~3aw=Gfrv0-!78YxRoIZjB8JeJ##h8 zSTbybSw}6?P(vU5;;8J#rxHZAOf<`)WNM_Oh?0>qwVzYTrnD)2jCUU@26tSA_*xty zz=FvLofqAQ3RyZ<<(kOET0~gwQ}!vRkFauWz*8P+;m)TL(SkJ80-{uQ$hS{~%OiYWIP^!yZ+h1!g8$vl!AAi4U6IGr-P@DJi;AwtwC~j&SdUIo z4qDKM8QcBH|2UqcbqsM9;px{~$3&G*=>p>Ri5rvcbPI#W5G{K<`Rz%8uim!0=t;o( zbkQ5182ILGv3ES9S#L{Y=d%(xL<5zB@;Y|D7{AG?cP3-!C*n7+athVUTO##V8+`8G zPGNgO;H#rnmx3n__3r6p+Yxas@XgV<-aEh}{(`-rU*F_Gg!E_i@TN8D3tiV!hpW$a zg;Oay|HtD$9Q(;FpG;cKKE7oLmN~BVb?btp#V3zM=gL809jqY&-gUH|m|Q0P4;W7d zj5G95=xu}*C2Qw;tMHd=mwH#TE3!zE@0+PZrZD#+O$VMy{Sw8+a^fS%Bf$} zCoS}q;68MgE5RyU5)h*V{rY6=C*?!^`Y21%XpbFaNupI1H_b6Ds%_H@W0zs8D^h10 z$s#cR-m(&5|7VuugN5V(O z$AXU)9~(YVs42Q#2+GTUftgP9Jqr6!%d^zOKa7v`@xRTvdq0cwFEUbt@eUV7xj`cP{_X)9}otNBc`g{rlfwv5_{oK-TEAA1SdnbvC_KT~z7FOKb0UMYKZ+;7W&JL$FY zYe1C`WFZEC;a5AAzOk>AJ!t(}X#z*#;X6TXcYaNrm8SM0rBivxnx`&8S{dJ)tv;-U ze#dv`$jZqh;>zxEBI42vrCs;DfL1(H<9UXE$}+aO<4Tt z5iwQ21@CU{8bvmmd-G-2-lD|XQ%5A%-U&6>A-mFwbSu2gVUvC40x&ZC%{6A?#4Np7 zdqW{V_5mdYp6phw#>C{yP{OY9$Q_Q$L2kmJ;kXRszO2ys0kpk87T)%<+FcY6E*^1W z)@FGTwQC~px>f1K{EkTn3iT{}@-$hy-RR0HQC~Bm=%sOo#{agwZxV3BY|;NZ8o2v>2yX&fLP zvLDaHRXm4h;viq;+^Em3Cuzn3;v@U;oQLN^O~m`X`8AVkjKiapehr_)urs^SI(ic0 z3II}H1U#;?R;Z0w{ePIV_6E*&5>C}t4KZR-8=YV}(U%Sm0nFedLMbFc8mfz8CKRGY z4wBu@{2bN2X~bc&)9J*SVkgQ4Q0^U)|HHzAp;u(do~4nG6s=?q{_6P|zLdb{QwT1g z%Q7<6O#G@k=#&(2Lg~6Oi#zYEVC*^l$SfX_Hk^0hGy6F`^^Q-*Kc^crH!F8E{`n>s~H4QOJU&gEtK+BW=Xnj|N>iZHSS#C^!pl6~3uChs&!`x1Ull-KLK z!z#%VPP?uk?m`XxW%^u~o)0SyturITcLzK$nKoYeW%lXd{c$}RbbCcU#Ho4-X3f(+fASk`wuV-usZE#F;PjiERrYM5ATN}4cC#8Oqnt_s1 z@1^nCNJ+#y#8m1%$^^u8+x48lxbuaWfBsY!V+{W7Sk?Pn6dSB}!;B5ww1o8u(;&}6 z8S`9NJ4aP6gU`&ojVkc|KI zm3z3~gAfcB!?r2rFKOy9pUq`!zxIiY?|=TcfbXyVIvHcXYr{`B9NI8v!>^_-ndV&o z(`oCb<)f#0=qd7Vy71pO`A}A!+;_9}^WnLIe?P~V-H+KqjJO#8`aO7_&w~2}7TAyN zj;G*b%+5bnI?cZ!9XGvGu4+Ut=t7ETpdn7DT#xQS{`u?~=n(TDX{xWkN#V8GI1jhg zFF71F=jnpf^@WK00$YShujdwcNV7USBU+1YAWwpfgFl1(Zryz5pJ2S6%XF!xFqLkZ~(RIE&doKq(FkdAW~uhN~9Y`u8>)5S2iTL{ylv<(<(+{DbW z_UaYdK)p+yru1Ei6Vp0BXBQYu$e5ZV2>R4|!Ji2kPh0&>8+H^xzCVX2s#0I&fv1v@ znE@-8siDDMW~0u(8Ig48RO{WD-F1p5y!+npJa}uh)#sxI=sV`fNSoo2%zB=wqw(Ty zV8q-Rs)s{814bmhAyY*S^Xrluw4fCvtJIQ^*o+J!V8=I9Yhh7@Xf0rH9HBMDPTtL; zOO2TS2(SSa|;vQ`0@tXYxdmXS(rHW?cFd$B8MG!*#`#?UyjicZH5k#oemu zGaQi907H%)eg0F5W&Gl&P{u$GWWQ?66Ih2lMM0!P=vjJkhIcp|IK_XY?8zi-Z77wH z9PiSz|9opBi@J?Ch$}usl!$Jh019KlV*ctm)NnE+8S!^_=Sjw{>0H0$N^JDuGXuVD z9PA>-WXWxtxzOM|*LrLXY;Ygq<{nWcNJd)b*)R_+1&xK_qNd{J3gIb(HG!MI1uVJ^j1Inv|=f+5)8p6F6dN{@Nx76{k zH`I~hwxLzn))_cqM=4eZd8Is#RxvM2Ct7{`?=ErAuQvl$cE#TM82A_JD?>XB?cmHD zCCx8=KY?~w)V7A&nc|6v3_@$Cq>EmuU(A(Asxy7$F{khPhy_H0?YUqCJx#)X8p;KAn9GbGvPZ8&j8-*2KK>Kd__5=?4Dwb;6CPG5 zwsh?q>E=qKm4!dIwJUFi2kjH80hFCt1Ff?Cf4C7If6E?x;>{aN+}y@r+k-#7^7odA z|D#o6VyFCVt2X)SjqUs`>nHo|&A_N8CNp>Bgh99(v9Vx*rj%pB|WU47bDdCBxpHuq8MHUEx${ z;!}8{%RzS{#uF%VzymVpsct#?(GtqZiB?`s&q&e2e%#V|W7>?AiB&VPQVC|qESOc( z7#vus`{+;FA`dnG~a z_5FA^OO;Y@!GGq*Fz#Fz;6XwDX#BpU-zwoG5B$0fzW0;gz~)K&5KkZc-iD|5@bu2_ z>e1e!k7!*)zQ~MK;?x;&1#LEW)UPSf1v4wMwxsqf5zIU9{%yG0b-{?Zo`9F4-MZ5*)B%c2HYk50 z+nT?1-qt)sj#ljE-FRb1cbA}4C)(7+Bj00;P zbUcC9pFqie=$R>gxE*~O;EA`yof|*P^Qa2sklZfBn_k)Wggd?Mggdz{-QDO5`;!*( z8e|}`sfbto*ll*KrJWT5B3!F^^pu7Or@f zSc3Wl#4^iW5kcIr+%2BR1>04z`QxzB07W? zPJv3S=3f0&WW9rK40I?Nid!jGR74~H{$iqVhApZNf*R{`uKj#f!O+MP!|fD=7#q+`Y?9%%P&Lfjr%L5IzcR2 zj5=(0Q}k%?h7;>@^oDgAjo>E5TRC%WhOsuc$ULH*uUOLPh{(9TZ%i#Ado+rG&LO6G zlU%tZIn?A((d$(Y(dw1^?p-GL{eIcwSo4SwcxzX%>gk1u;8O+7;_ln!4Xp1Ati#{w z+agE76?VfyJCa3DGHPtZ?OYDjni?7!u5|4h+{B&#(rzX5Eu2H*05(cjgj=M z+n>_do$m(DZNcwkqtpN359y?n3zg2}W{9miV zW7c53;mw+z3zZ!Vk&#&6t>Dh(Y=Pld0_#h`ywxg2;@DC3pCt$F0e%omF)G$2svN~T zH$OoUL)*rlf8h50`Ue(ma{YJw8=J@b@Ru#^?EB-`WJ^2yUK?}t-SPsh68`d|pW}@* zRk-{Od{gvRdt1DlyNXm6{+v@z^x^*BUS3I7TszWw-u{sn)VxpN6n6Om@fx#a(>~P_XsO2@W-DAQGQBusc3DpvBv?;E)Y=uXf@b>W{kD;QO3%tD7HqAf70k zL^bS4@!&25_P_8smu&-2@BsJb<7h80Ewr=L6nbtXzr6+a2#4*arIEZupSyRE56T zk2uwxeThGn`z|fB`M3j;fwL$_%!~K%auj!ta){r$lWveld_*D7soVp}f@w2_(;&&f zP129B1JjI!;7Dn|4KD*Pp-DzmX~eG+?GgC%?SiDf2NHNeOQ{nty?wuj?E$QJ_rrp? zt;!g2r?iEP5k%#@vcxcWkLVE~?;FPiYUg5C<3G;WjT%r3ytPx7N|(NC)*xl3w(aSc zaB_}#t$IZ5jpc?RWQra!dI>eqsWnSEE=!8kdaDAm!Yj3sZ&sPxPBPb>D&afyGN=8V zA=wAVe`U4Xm5mDTncjE^p2QAgP&sYHSM=&L+8I0*zq;hXCDK8gfruv#H?Ss;`)$I= zN0v`kKDnJ~9~ek$<#RN|MflE?+Qk^rZ@FvdKr7s?}1KX(J z+N+B@ajJzz6+9uroB3-2e$^F6m7ee}y>PT8NYNdA&Qz%}z)PpqUHa@%muL)yDhNaH zR|nk!gMa!}a3M?(xxwdbUU9fza5bjqhmzKLL%nA>XdN5(?_I@kk3}ssoo7&1#Jb}K z+d0WYzu+SdJm);{jz)4(0qFwb>TKeK#kI!J9fq+>x^g@DMcc+(vze_ut&%ovXTq6& zE{7fXWaaw88q}^92X}-E#3froDUHqZ>O;=P4i$cn`_-cDPCwuwoz&7cNC0Eo*l>gIq3N+6ZpbvBb0EWpNH0{t5z&*9;&ZBGb~n6 z?$gK{=Sf$4hGox+WmQI1Z6i|o%}o{6uieq<|5NR2Mfhw%F5w|>!yZ`Ui|d2S3PLT& zg*c%(yg#}_4xZ?jT{T8!t*!7F)_$cCs^tnB5k>U$FkeCSHCM=!GHKTaMCQu+jf_@acVkL{-FXRB8#)dsQq+A>Cxp#r|ew-%h;0$7Npk(=_!Dx z5T2rXlGk>5AdQtc6z=l3@qME4SYzS+dH36Tgz8iUKJ2S)Jqb%?cxfZI62mzQxjUri zy*SHjR&NF^+gmaUScZUSqFMjj-2)%BP;t%W@1rhpGY{DAgYVJZ%Ql)R!$bAUJN&zL zM1oHOV_?MgErpLG9zqr5&s~M#p?cTQFqc_vbh*N2{@oQ0PvBla)rGk+Q+8EwmtJl2 zLpr+xpAm&_1=*Do!n#$=_xxtS$;By`aAK>KA0XyEy{*e0wY)2zV==Wbsu+{3S1X8YA^4hS)CG|`ZOjv5eh!MC`(Z5zFT(__@OVGNQwUGvu%s1h!BSaS5$D;B@(Q4tVnI`>2Q`bBXk(fm zGgG`LxNApq-4p#bJ63%TXtkUk%nonI?Bvio-&NRyJt5emg)#WT0Bht#3!NeIZRpki z@Wp5_9EujEgZkxRdz6_C&yWvn&^V-wrkM6l>9YkolS+!+lF+ntD=gcRz7b3B!9_eoRiz`Fck7VFht`r_6+D`p>+4ViUv zqtv?&Xi^jxAYu38o6fGxfoj)%MfVlU9u7~=OjohJC{o-}G@*lN7{7yo&VZOOZmFbD zzw*s`C6#}^+LdP3Ygrsoz0e9d5YH{cnVDNG6b0dpEi|N?c1V^TGL0}-b~ffZoPo3g zS1_%R?22hjN$|~<+#>L=LrJQQkzQn_EC5OwPqi^W;3hxSZKhgY z^?wOFsx{qle$zduagE37J=IVTnq$o9M=v;;@GNIS+N>MVnyj9@y2)v3Uo?GJ0@?b; z1Z(ysg>u+oZ#8D9S?m3*b-kgLB!Nd8F*sNw%Suw)qvkUMms0HDabsRn5Rrh4<^W;( z<}9q#649gjomFZ+Q)#!0+8w5JP`>o&pU1}RSZmuBbX3TYuJ+{Za%?SY3S!PYjfE7^ zSuKc^NA8I8gaaLj8n8|7y2Fe0VAR>%dcSMg`uwIWifH3_T@~=G}2|ct=J)gf7DHgsRe4GH`XS5^ViRuTn{vxJa!H5pfnn7;kci_ z6Zqg~(^%gKYn(}IjOOpvO`JKq6jy{|QmRTL`ulShr!uv*V2Vn0thl}#rz?eWYGa{U zGmSGiUb6)yN=Y}@WMewtDj1GB$=y1y$-g03yewI1#Ms&~qe0)5oY&{1VvK(NF4KxccI-lH$2E zHyGPiH#63qB}9el^K(EOxLOJ!RW{xz&s9WEdaPT0qns>jW!&Sv%f0i^iLr12&4FZu z{dra4{MHhJ1C8;n@sW_m$%;@xYoyYQpAZ8?gEDH*37V^i$|O51m{YDA$H+KVdS_~$ z$Bun?Gdw2o#4*&B3K*t-28Nn0PP1#nIAzwtDj2IIJ+U!7Z~aV~B`W{wxb3K26Ae{a z&zPDeFvhVxf}v`<#@YB=POzWGAtjDk=_=B@ucupSUZl0&l%^w1y(uk%w9nm?rXX$U zO=yWr83^&gLf4vUg8&6oXuZx%d^A~=|%&2ZB^x^&S z$@z$lBMa61DD$!%pt|Q(sr1WW=C4BQmu~$k>S;_W7SwddTl+^0uhTnF2k#YX_ib>@ z{J^8BoX;2H=CMIUUs9>32lQu_n0+0*6Dje1Pc31C%gv`JmT>GO|N6Awh;K||w;TVF z^1nQ z_r`>RivbF}1qByB0R@*Ixp8lj%w4_L#WBDk!NimdK6t=RPunNfI@UD$yAskfuY<7* zjQ>(nyU@=k2+Ki)-a!ZEh%6wc%s&q4bF zs>_6cT@{|Mcey~}EOl8F&Qg=YDF%g;GSfLrnJ<>+6NOV(;7AE1rHceFXR^B*cwfn) z5=seCM76NRPs(=A@s{&tRWp%)X{gjSZxTZu2ZmHY6Dba&!rWP|5_)Ih+)LMKqV{+d zl6u1J7EP22nuyLH>$J}VO%w#QCBntmM?iQyAj6x(#Ny^@b)6=%Xc~e8XX1S86s5W4 z;aO%u2)hSQ)SFA=USFF2VI0v!po>k4STJcK(WE#?6H$m8Ci4uZnwwuVFE2p)V$J{UrE{sIBtwxvD=ce8+&(DWDLS*$lV&ZO0;@Ac#5UJ8@sck-O{ znNXLrVK&H=Lbs)+-cGu2L&z5rc2L134zw&E&~?IJ+@h=#lHzE0X6> zc4s!XOQ`#rLN%Bp`fq3!A=8#sv{)IkDZAaMxeiu0peNUW?=D7srQd$uq9BP%&NJ5m z)wjZ7*OJ#<1bhqUd4*7Y> zqE@$XN74g|80}aEPsW79`2yyj>ZM)<#wj5?R%R=AD3gn(7)Ju8HF@J3RBZLmty)XQHcPnNrs0QO4A#I+$4*Af;vl6`p2 z%JRap%9#$}*_zV2jK!Ig>){08kfz=xU;c7p<6S1TWzD`tt5wcSg`~}<+O#P~{p?lQ z^5QsyiI9|{Cm+;ydn_2xm@H0>YFjCj|L01xE^4RH@o*NuZ|)FAs3ATFQmj=>D@Cm> z0H!Ipfy!f=V2DhI=CUabs4#Aua z@}N3Ru&Ogm2^b=+eL9G}P$9>`;=sc7dE;yPwQvO1hndiRkPIYaoI)U&T2a;lv&|DP zx6IuHKcZDNtL}Q<&8fZx&9iB&%?oIx;ra2N^f^Al6mC#@a^oye2z5>K#W`ZsceV*u z*^=?JYSIaw<9~-V;>-%Kk@r$*i>buGwh| zs4Z(BOv+1C`~N@WC4Z2JHkdf&EZV@r(Zzwn*>>OzTXd$8o0adk$qNy0H3)qK-+S9o zZl=^DC`B1XCHX-}zX}Tjgc%CW^O$CNf@dcz3myp3<*DR5h#uSI$(_Zt3WFpj5Beln z_U)sZ6N>#0%SjGMQkUC%rw-XwqH*7k#v8@+6FeMl_w=ET4^Y0#lQySleiH4&T+jo~ z!Ue7cd-F|PXUaZ^g7Se5@ZBIMvd&}N!HU}~PBFW-%_zz$_BUGXC6NBX-dj7ApUL$I zb$JNmXj&U25TiWJs%oibll9n~=58crD(5U{C8id7Ns2AFusBNULZa zQ2zSy^lz#WXl*Cfn!}_Tte)$^!?T%|V+heX2IyJ)!-xZu+IXZupLy8N(~6^YVHW_q zBLusKhcg~7Jv^7%G%?C}{p2sAD@}~n+}9aHIq}bg&dP1LX$qWz8>L|AX z{#$4!vS=otJMgSjk1R-3h4M$|auuDX?9e*oexjDV8yf+Thq*z52hb{ig+$plD{~Umj!$x6XU;{wL@0>OkS6p_3x}SIJF;i&Cdlp zWKEM%OUULL5)Z{W#|hr_H2WvFg!7-N(DWmhrP;7Sv(e^CHTy-BOu+1yXapTo5smnu zEW~sfaaN$sIp|I&{(ouUhJA8*lZdvV*$R8d^#+r)g|(I}dg0P0%sq0#u7g&Y%C#c) z2<~%_K?7>hBocbGL6$K_vU1xxJIz5hY#noO7SXpa&p|fKJ+Z8kuI!6vq0e=YRX!I} zO73#hh(rT+WZdOgA=1i1ze@s+7NkNK9qTSxY4WuM$q5dIPVVDE+*R>Of#LQb(5@Nk zlggu|-siH}qw_g6r1t9lU+_Lxz=*QaKv_NZg|C$@8q~OJ2mc6JOPeblwi_E7!A}fJUdMX^c>zE56*}TfPW#9RIJRMK3tauL^KeG<;wd12JHkvL3O^u1P_bZe z53{%zq-FODmEcwlnKMB4n^fE6X=Te6fcv<8=?z6$64pjhhHY2!n&49k<9ZX;eWs4%|@QX|?)}q`LShNw_9EUrO=KYIV;+|4n$7_#s9Uk%$QQQw+QY;L)XA83vA7sgzs3(>#lg^(Mbp^Pr6%L#FNJTJK59~HU zWm-3}>)1=nh0nJBI9kvBe7~{Q-jCKP*DX9rDqyQ=+;)}hhb``e>a%!7CDR-GepY2x zC|lGd(Bga!SAN(0yR!LxQg!S!m*bsNKcD($mKf*U>t~6Yh`O)wWQ%3tk;z>}(nkL< z!EaI%n5O1~4<=12rBmLTajZa|d6V8|){ckgg~v9RcyAka;R;yl@G5_Y9;VfjHqIAH z8ga8;f#D4J`*A;c158Me(;+qH7|*cMarYjuxlhP|_9~Cghjie7&>BC5glwpmpJ|>$ zv>PVR4On|zhpM^GtoRDCf!v8XyEq>0LBw7s`Wg_mm+%13z?+%2I|vV38bk}l|*#|Q!yhoykFqch0$-FSalENx`Cv|^yAD0 zSv)>{^h6p0#Z0@j5C%i$)98Kp7Y)<%+tE)+ZjC2CAA4T4JTq=H$ilHIo$r~xv@*c= zrd0<|{&}ngI{Z~KXSM||j_N{;+)`?7~(wSd^ zj>5JxwY~mclAeO{V}zq&B?=rZ0RwJRwkxfWvc#!zS|Y4`v+BiK5!#XCbEY?nklj`) zaj}E-anW}^zu6-VJm8whI|X^O>%TPZTc)kcV>`Y)^3p2u7C4 z%@;cYf_EU|N>Kx#XD%{fr_eSrQqjYW>KHMPH;|ZM1wARe$eZ)>QvqwN6l;wr2T>*c ztWXp{S^(>cY48MhK(~N)2{QXXy^0kNA04cErp<0R&$QUgEN*9FI~RowGw31#5UpA! zv|`5?l`Iw31pVDEyIylFc|B}h$!51SSzvcEH#fx0pjKv;9#P4=8Z6d1<_et)M3J!TC7~gwI+}IJ|!Db zf`SR<)KG(5i`{C?NHS&wq0KNO4()Eh`EKlrRp1>nONvW7OS?*@c(&g$Pwj#VZs5i>7ov+6IvCVgfJZq{grk4>Eo?a)Y8EaO`tmH&&G-;F*ovrkHh zmovdRpTs(rM+=sTn0e%m{=k^#xoD0~*qPi8%Za7CXYHAZ8EM#)u^RD_(gmBjUZK~7 z{>q}7Cf6*ll9wM>FX}>|UDr6(kuRr1x`n{ph}B6H zgdJAn6RYfxq4^+;>UB_gi=wo29&|b-o3tmLoDDM3hz%y5q&o5( zTXL**wwKw|Efw9_+p{`mK7LxPwQG*%L#5M83g+0O74@a%WuC(F=`7hIh|1O`Qygg%-vJsZgeYD$PA>5#0U19e(L^E6t zvgWcYlQTa@uxQLl+GNWdYd0|cPGI(dl7QM>o~(9N*rHy;!bAk6NexjIw3$^VuA%g# zaS5PhIhUxeV-d(_Pkx#M%CGIZ-Nncz@cG0c0RKdkMrKj6=4zA428`AW%|4|FIv0PC+nC&bd~GOU#|9*81c#*XaY+rFm}}Q z=V=}rbdZ#r;Q8t}cVh#lYk@5_c`%F}-Tq<5Q{M&+C1k<+8dfX3pR~x4u-y}V3P>99 z7&1XUZpL^>CTs2i^K6Bu2J&(F{0-U^OXf>9&L@OhVKXY3Qs31IaVNRpP8P5EVqTKE zA%Fe6)`E?NBaHB9e{4=Je{U?7ookJCayGfqWs4dr!w%cPHsVEd{P?!v#LvJfy{>c} zBs;JycjeA1nlF{A^P5U(jMTpgNlmv%{7n?~lbtc{sNk4NzB3lasKtHeG-ov?Ts4e! zYldy0*A;m{UBY^N4Z^@~7sEY^;(+Grkd?*FLz-`R__VKOh|L@}#OY^;LoMcQAVLc*_*`s2;RHQn( zifFX7sC@a4XZN%CGqN%KME`Zu&Hhg08+L=CI$y|7fK*~_P{}x{nGDv#-33! z1|D(Aka}@fM}`?J3AwAU<68zz@2C^p6l=x1OfYAfNy>9)5Q(5unJqOd3nchle6SGp z66fmPZ$E8A{XA-3?U{{vf6O@>BMu6C1kjAID@jx<9ncOdJgCJg-=sxI{t_=zd?6pv zmasPAVAa7{HXsQ%Si&Tb4G3r1fFy7eHXsR*!@kbJTE(X3g(~MUzzp$kMnQw)NgN&d z#ptwZpAa*=`OC~%H0gAbbHPW!mDu{=xUB2MP-PYmZUZ_R%uwukgZUguMZB8t2sZN3 zX$BY5++>s5b8_O1A1O7BsL4y|kf3>O`=v7z6f?tpOR#qV+^eEuwSf6ZM|(E18Y7JJ zcaLyF<~DZp(6!Gg(t)%yc5`g^`9@BjVe240pTx&R%|P~!PuwK%k#r#GG>pE>10 zsnV$-?OiZNf-zHChhJg*E<_%?;TNC8P3}ojyvJwe8bL0}NEfRH-+DV)I)6$54}Cz7 z-WNn{xkOP^g9E+sB76Y@@c9FVgP+mC_zr~)9FISs|KUD1Fc2R`{>%4mPf8a!M*kYX z^42bEfV|P$$hQ)1hPO-{Vs|a)L22XV)omjb{RC2j=clAJ%cE!$Yw;nMfv7VnQHAx} zJqL8i_X4iiM1`OXM@&ih-8|?XS-zKXPZi%EQp!_urvH1NM^VX)#UEcQarj}-J zSIeewWW(mpmM%u8*g^fhk6G+Dxy0-3(odgP;VtsgC^~)Q8tbviItLz8JxZTk)MpI*q+oU$!)YNa0MgRrl((OFMi{rB|<7 z8j*W-WoZPSQT$Iz%{UXP3J=0vPIy24cpJ^z3AEjFIjd=*;l_Oe)l5?5#oJvHD0%+C zEoZytva5KF_CYeUFQga^%Xvs`zCQo!3-G{@BO& zdcIyFZ1{Sq)Oxt=xFP}u&9St)ub1zE26K-i>Q8Kp=G(MqYj^fNJQi%8?+tHmoF8oV z2Aj5d=Xl#TmIt?aJI&7s9-&-WV$!*%+Oj__3MmjJLTJlhgZ9HwbPAaJ_WW2kR)n5MdZ5$5hHEt+na3eW2R?b zV&nUGK4CtmHgfxI4Ymy}h}_IQWNRS5N#3*Gft!k2drtp8m3Q2Y6+vq+4OIH@m||2{ zkRE&N=)a6jrayl}`qOx>L!LV1dFqBdCvM1d^oBf-;dv?YFGc>t$WK}h?VM6!8(sRo zV*wle=-$P5aHGF?`r8%a=+l4tLxnW@`^YS%=k(eNc%_!toa1wZ(LYwCDsGjXv)OH< zmH#oVV)V=8Ys#HtpsmLCEiSJ-$0tyVbdD>wontxN=&us~twK3xub}b^&*2WTbGA%+ z3uzyW2D~U)eU8r*&WWCpbBaB5PH^$(mb!$0w(Txi2}v%Bt$dEzb8#-T zCK_VqwAKW5ks+;i!sq7^`8Fll;9W_E;90FWjepoCU;PAmr$%q9j|N5*iiUSUUw@7K zR)njM;?3hBoVB2ql#&DR2F^u_M?Ds@s0Y^fD4TNih1!KIUNwMuPX0I=oT#G)bwCq) z89gyrG{F!rqc#J#Ga2wO#uIUUd(ufaWyg7Wt2%;cSwB+*M0-R8#8QS9jWxF-8i{jn zqB@a&p_K5#WE;t7L&R^yNJYO9DFye%M+n1$rE1%_gztQF*W$T4?mf;nbhD=-?2_-|6{DBb^YXrDf{8x(#2Gcuj5 z&p`GX4;?V;CtZqU1btqA17b%~Y=B89o_U}2&xtW7b|*on^2cu}bA?3oF%N0fs}be9 zsClFdH9z{cDX)Cn(F7_)G!E$Ne)a!2#Bq2Z4=s1B_0W1qQ=*wtTfoh=#oU^&S6^Of zSLlv{y}F9l;Ez7gHZ@T-Gz<@0*_r<9YLtcm1_wPg+~5JF)Gsnaeev>(`%(G&2`PQA@`!()+`s%?<8~Gj>Sj z1*&vM;Hlg(|3Jj!A1HJC2iiP=rwYdQ>tB1iQSmB-%_LPDxF?NiPSp2OJO}F`+rV3% z2E@VHnfZP96L^<~9__*i1jyy~>U*DYso%t}1G@W9JbEVjf|;DkhlMcpj72>Lk9wX! z4pvX%$1irWrfZpDMe?ZQ#lvxm>^vw9Rk=BP4DACR(D%3da#$pd@<= zxQA-%^%Hqv3q1EYmTx@BH z50=pNCEj`6I7{n?V?NP*+pyZ9uIP-xWxq<^Fqc;@3>sr;ZFy;?*z_-w;8&;8~6D97=v z^A3!a+u(>)FjdW0^TK68p|ZNAY5t~`?r?LsJIoj}e{XD$Z0WWV285}~lUKAJ#(wS6 z11?Zh4^>Q2^bDpo7+($ATc5h3;2pY>`Cto4!+#hG%k}#fitXufkOMsIJAZ0`H3j;W zX5Z+k9@gTYCiUuXWu43>zre=8Gw}0!w!G1I#MjdqTy`X{(bv=VHTNFuz<?Vo2I@$Kn6l1C}eRs`_2AJ&{J@aBhEM}lM-e^>jV!Xp`EMR%mg z79BM7K(C&?_-QXG-KaFH2M!+;VZ91ahB?{ zY?30;to5zn)SUHKvi`Sj(zFB)Ig=0qzZ`Ov0uOQ-y)?OuPA#N z)8*DH4Bsc_hLt5o)lXYkV*nmGGkf*1Wx5O4z-(T{y#=Vv241bn+y&t)1~zSBmMxT8kvG%1Bz%SK zVQgebu{#}PPyFoEUlr_tCM*nd*v`zB9JWiUyJ*~%BWP=k!nQPbH{kyw!C$SJeHifG zMS4P^3LEX>H>7~ISV@ZCUgw!m}9Vn?$w{I`Q3c#ciuFn z?J?$iSoB@Mw2ILzzJvAB9GHMvqe6Oh`1VZ^;Hi8yo^JDKGhs3W>;E{6;49;8u*`h* zba5BUk)I&Gpr*|7a=6G)z1;j?-@5 z+rR*8ySBGCdDn$EwzYtX0b7z4Y@V6zUB7WFZS*aX&+Ka|v#t~Tx3{w;vbnRn1)R*P z>P5@fRxQ7?s-|*jP4(KE6)TpnVEFgf+KYs#aCb{**Z;kOo6C2#bazv?tOoC_S<|p~ z;fl&-HE4UqidFRuLXG zoXy$NdA8zgTLRKgkEeeWdliX-(B2yC25&`rRfm1i0#3Gw`?pN*Fp z3G(7G6ZbhYs*Kv7CI(YxQ!IO0Lw4woWP|Gb)fAGLG#f^a+G$kew#{%`WvE0_(3ufH znHQP0gYC=)-1Vb^mz2{v7mWK{rz>#Zuvxy^7&LL$cTL>IVoZBld4KNHChlrs-dLb_ z7&m4Rrua2ZBc=@jw>@k2?HO~uHJ~-vnFNUzhn($E5w|o92kg+!yC3aTR?Q^*I88~% zoziCCO$@e^Au`kOtXNj>EZkwh`tHN2eYbP2kc0>`JZ}eYbz%?8+S=ojY z+OJ#>xr>_+Kv*>L)NnCmk&g;=+XTjNI*^jp zVeVdny#Fka9C|ODF#>-asoOSNh zpC6+h(HOoHFDpCy{%P#9v_kX7uGY;P+q=D8=Jxo^{%UWz&r5Vg6F%G5ZCwA?@IY{j zchknMF51c_DuL+iGxd1G>wqM@CXqrJNjhSYDc;TN)&Ucg16e_Af-gq8|1`wfl(7!( z{J;?{G1S2MH;IyB2mcTt0to@zUb`^cdl>gHgPW&0Qfz4q&+vl z*?}RVpl`YqQI$~sUw((P`bCm6OA@^Yy^PeX-_!GWz71nXzjZ^dM3vt8VlP!%JxKaJ zq%gG{;B@T+dNX!3<#!r-*>p_hZYcyR+n%(0_D@$8|{d_GQJb_Euk|Qc~lUJ_#q>=aAB#ki6#R&8PF_=BggL`_^w-m&tR}boa?~j<6_`HRQWnN94Ii$2Os^RzV zv+=swF6XK#X#3~SwXrFP^`Ni>H}!N%)Lxf|?>riZ=S&v$HZSH?rhK4>k;;2aV#CEph37SLBAl8#S09d_V!XzV;)(bIilFfNh&sZWFE6>$Q51&g#y zzjmsK2d7s0icbb#A0GLIZ2G&Yf~p&9a&A@Y4SBEVbEzq9cJPG{x?dfXonk9$VV#Qw zLy>H^XD-fEz=KkpoCK|jAa1{7{q4!EacVrqb0XwahlXl$qAk%-sxP&TnYCO(Ew)yM zTI|s{CB8cz*z*-1S>U{q=Y$WVb8k<$SC6l&H`eSugC6WYvnI0|IzFc%E4S?UzvA$R;a+3trXIz zd-Z))zd(7;NP9lD@fUf(&iZi9nK@_Y_Uf{KIAFsLI9s7(F0)j+ucr}M_!A%OT#Qk3 zR$nt`6~>)P7uK^wq4VrteowR7s0;-mld>NQ9n_!tEi@s&r(B(&J{YhD%g5AStYGB34ZW^K6d<1|(w zFVTA1@E#LOoeTMypTSE&-w~793pVEf(*omf9>>3b1y?t>w1K39^t*YIH*9I|?%deY zmFjb#3AGkehGIHu*@e$!MYuiYho=$wdEG zzjywWCJ?rM7;vuhEqZ+l#Qh80>>Jf=(d!(Znf_gtb^D^%MGz2E+)h04cw%}l-UBUg z>`*I{REm{~_>0`4*KJ7K<~GaXW}$o87Jm)!AUm{;i6$mweP3eUE1!;yeaw27ozs{N z{}wM^Y_qXWf5^=F0*f2Zc?xYT!8yb9_n99p--`Zha>wB*0Z$vy%5;1;yR$rtUYE># zUqOi(cslBi_!VGWcIZDCXX0Dd_anUf65cIExwp*Re`1NK>rwOhHN5>Uo_6a0GNwU# zd{DptUDo$gq`qywXBgW%czVa(?XNcbnZVh``x(#qAbHuwI`Sp86L8R34cZnBgY9;U zX6vGMyAgrM{nAW+PzAht^?Mewz6Tifl1>S3{FIn^hwIcCY@s8aglk{T&~)Eu41P;HLQMvEEX0VPdPh|Pf@pHlHj{S_<#f(lC2uLH z_weSKDXD=V?Cn&X_PJg8H69DryjeGI!ke)1&c3_GB$ZRo4pk-?!<7m3pL_f?!#yHP zr+3e*ZA?7y{!2Ui2F9pg+phPEC?leAclOOQwKCz8OF8r8#=N%I?CjIWV83U+PaivW zQsE^vDIloO+Rt+aQ!^q3TME+(k_%IccJ_T~>}GnVwxzp%g2l@UZkvhiiv7A$wY~Q3 zKivMOQkVGJ0KV-wg)f`**5gm!3YtsjQY7Mp5vXGH&LwxQ>eU-Pm)>MO(6Xwlg3O$+ zi>G-zEE6#VvOu_sXm z-@5w^lOEx&em|W-D)72Vmymw;@a!g*<6&ANBKMvjWMh@Dvy~0i`>yAI(agWWgEK>o z>OVe2cI4ga@yox7vXXL7$f+wnRR~A*v>$Vd)y72akIbg}n;EBs3&@tIU%qhaPIe@_ z1+op%fO(lmWojYQ{?piShafj0RUT@@8S?p``qINpEs6rRoMtyvwcl(_@{GJ`VD9-0 zzVJg>k$~o0i}@o-;M#Rt+MA%WZfry4H_vIfdF|#%OZ!@jt~b@0pr_)M+m#&bg(Gjj zihGb}s1Eg2V__y)yT2=6G!`NPsbmb|ebL(sJcCNBxuy^5Id7lv+oJP~V?HjLYs3P) zHpe)gE=Fe=k7ppZDRyLwxm)^+>MQ!^ubNBuUkoam-eQ~JoDX545U=C@7&1a_8s2WY zLiG1Rz5eZA%1nh7oMBg|dS*cObCA94LmcPom-1e62%gTFKXXJ%2Nh5b#zFm!x2T>* z)V)Y(^&HeMymbMb(;MvFAe|@2XZ7md+Yz@7aBt2L5xeVEV@Kw6cuAbVtz1*kDrxh^ zj=ulFiCO5GNr4e>&;$wBt$6!3YVM+bvo7$>h@=SFb%LRqGyr5}plKQ6Riw=9P^PGM zwHfjyhh~F>kB>AWj;Q$r%5ARVY8hUvmGboN~>n|D71rHm{aY~H2zqJS z#NQP#H@K&g$Bom7V+u;>D}N;3=)x>mH6-M8&_dl&aizfGNr)n!NC zm?9xJB*d7nv7^;rp!>BZTI^hV+F%t1dO+=eajn&leB{a1E}YuH+t3T2@)qWKPozl+ z;+KfhprUXKRN<3^o023!%|I*XPxbNXpr|s@)-wvEe99KUf!!v>$~3agRF$FOwZ%Me z47F6WR-`_mT*~6F+_uXtERLTpL90SgO>c974lh7UCHjR~=T9X8qAjTFKWEt0*XC1> zi^dMb%qr6}V`aTAk6o3tRAU6XPt1+klii$vvNR9Ij8yfKgJ78$@!|9QEKb{iT*{*% zqJ4Jmbyr>f^g-624(W^Cv-|QjJXcox@GCQx(rZILTeSRi>-P-SpT(jjr%%kbMGJ>; zm$t@fdB%f(H2JeX(ppyGlb84p56^96@g_yv@Sm6`?eXjj@Q|A_Gzc9p_P|SsU&c>` z%ruLS!)l<gHjkVttu@EU+a|aHU%KkC-R{i!D|(ZzmiFfBLJaJguI%8pb=WL7e85YGh#eRF=uW17O9W;cQ`iGrr3t)o-)HV-gLw*pC^C{EBH5n$ZF_eyQlKQL?z$#z&e;|Q>F-~%;b`L>V!RdL@@yVL+8?}8>eG{#iAZNieoWGdd1-Aq#LS}O63ro6saPrh|)p=gd#OSp^V#T^(c7)#y?p^O$`9)M ze--v?N~13d{xa>C2la3M%F694%Yr0C^D)hBs5!m5FE8q|soX=;j7I-1d1Yv2`>qxt zR3HZy7C3hKQV)EvoN`HJoEpw=%uZ_Wi8PjlizDc5C2lMD%s9I?Y+Pqb?Z>5ebhP9$ zcF|bw3hi*dRboVi=vMY#Nk8NDw{k}`LEHMy!S@Z}p%lYdm`Tey?O?NYMX8 zIgNM8!ttU}>&m}glk-}x!33}QIa%L3sLwrK-;Ft}T9Y66>-FB$Lrweddx&@cQxBKl z&_nnCwTCz6_@8=6lDc1~WQ|i+FTa^?%E=3}FBuD=SCnU_@Dw-e^+t0wb;WhN2C)*`yJE z=?)`|d(I`~E(mr(FR|ecx+&6c+VEv4BX0(PlTJet_TU{OCf_3A-Vu_UeCV<;%Rm3& z{HZi?LUQsHxEzv6WVzjtC~yEj)7P`~D{$ETSm<5!PvXCY`y^VbDxSPl28@oBjxO_w7th7CD zE5+fd2AA$IIcbM^COE)P!IzFZl3@quyU@_(!#-h|$;D{&=9es-&Y4d?V@)&3W{EWB z&2wznS(Y?`S4U3s9!@5o_N1PVU&fi@S>Z@hEs5##tSQ(W5PF}jrckp7ecNN9S}_ect|=nd9)nZeiDtpA^wT^*OKsEOek zL-f@9AXiaD+!Fi|&ee>u6{cK;kLK2|@Y_te%IpTZB{mFNHNTwoy}-hzoMKX*MADSM zl=<9G^s8(KT&JE^mCC_afGG$od{SnO^6N}f7GrgM{5n}FimIrEjg83fSik6L=H2<| z#ZTi*`T=(lV-|Pz{nw{YmgtIQMY3Pd{(gUHrBPo@(!|%xSYMafBgo-ygr-(vG^R7N z7W4!CR++WFY-TP?s%F!I$YqE3^0_S5x18}NZnmr5>=9$3onehN`I(8ixgadimI{Ss z9%~;=tV@#8IR0P8-UKeHa*rQ=&YUwdX8>W?2Q6oY88$`P1^0&m5f>7aQd(>BZG{XS=~ZvT4!@B8s1 zGjqSIQ>Wtc|GeV1-O(t1nYC!v|&TsOHSt~>n`1utnGW8INyC z84;DDU1~Nloz^JizZ*UJ@tmUds9B6hERzKFl$r9x7LWxS7qyJZ5?#OI~3 zym_e}DK9NV5%0IaQFxk0DqWDZuHu)?+1^X>4_5rt{Lm^wNy7F?CA$ z_=Ra)7v;bYWk;GKQzBEDK8@LLUy1a|@Yo&3XazScU2@)uah8)sW6EO~(oB)Pge=M^ zaUxDE^s5wi(rHa@M#S_{xUJMPnCbEy_HjX<4b0opjiNX(+#nz=DXN ztUBdX+M@KnFY@t(?Sg#LuSv5IpK5&H7kwLjPnkZaVEk!Kl6UG*BFRc8kWFNzWTj?} zamSR#)+rg`?pUUi^&mnU)1THIWB2C&Nw=$z4 zg9>rG&2JAI2!h6RBArQZqekrv_)OaqMx2_I5G4+1_hD+E_Kbs^oz*&Utxww}T>E__ z1i!z(Mw&u6wkiU(RpDrDAR){$L^rxsAt?tN$f(vGINO+g1+&&(f^*k*AN)Xctxu3O zaa`+O;)c~jRWN+s)%y~-D!vSN{dC&R&+7^h*PaCB6jG$s9GgHH&0fsYbmKWdWfYHTJxP#T+-Zm;F-0l5qx< zz$k#>C}GquB}AhHkwjV(LJU~x*Xz4^T!hhZ&V>3#8)Z7tc)h-Rka{M1!hR!`>QBVu zR`W`FLi>^Gnvpxl=#WvZGA!@TYnC6xGe+fFYe-{en$?6FoWIoI9A`icdekr+c}M+H z!$+tgpUkrUpY0FO+Rx~g-?qO;Yd^E#N|m-{K4OdxeHP`tincI45gQ|;+Nb*R)#=eK`B5`VW|OP8%)QdSX%GY5xH#pxWkMup!3T*fy5R9qgZbSN`YAN{j%eQF51xUO>Xq;le&_tmL?%r=1lY`jPyO(8 znD+D_xf`@Vkji_8bUPh_Jhn%GtU4y1NlfjaV^ex~Iq8T4b|{A}5S3XPEhdk?TLOxo zv)hF7%0?o+s-^F>C5Unq^*d)l$+*M2NmC7RIDFsM6UT^jM{Y+cA={u2I!CnHR}nwY z5Vd@?9&+Dz(DF+ToowkYfjv>U>WBM?J^LE;9XMW4*4JC#cHCD7X_`kiE9YC8{P_me zr0q^Wk3+r3KuU-qREjb%zF@n7MU+})yqu*Y!RZ@SS4v4zwy9yP-s&=yCrwFd$SREw zb(dxV!mP@Vg6~I~FsYB0rPeE^CSNUu$D3(XXaXOza$L+W(p*h`^DIs*VwAl#9xL^D z>pY`rjn`7mE#Babs>Y2<@ZX02@hrL*Q6}pyi`?S>iUCr9iaoy=!P=me?DuRCvMRFSTBI~9IDfhzzL$4enpUu zlT0I-CQ-p^T9d_Ev(3R;*4sTT=+ky`?Qr)@V`{;Ub)TLa;Q9t!1>4egjLzvTj>}y( zr!wck^nx9es?c^shr)_cNv353<+O5#j@*D!+3g^aTV1rPc>wsYS=8(FusX~xX-~`C zyZ1abm*v*)d3vteJuUJq>eCmn+RtjW^`h?W>oN*r@Lt$+axSZPPG#;gR<9GkIbe*| zV9W%0j0($zOkS&b+(=I@tJQ=5LtPy9X`N!qy4x!jPVvTiSm}CKx!13dwZrQ;f_}0x zSYKxs9Ip7ar{1)^T2HUnxL50`4pCx!b&gYgJ*{Q+jP&*N{=uFe&zv&Y({NWgdg>ai zS?6-2&((u{X60+NX87W{vMhC$NmYt2+Obblo+R)_4bQ|dBvykhe|*33bgQqX&M zd;PKmZ$NXUV@HJtW73CK(1PxY#&vz#*qaL~(^ikp?k!H*-5ne>Wm)!vtgi5a)swyz zkGO``zde_YA1BX*#JhdVw8UD~e`=^IYAqU~wTO)iYt>6M#GAOt*P_{~@lj?gh?XG+ z-K0#+isJGvR@`1cMMimu-JmEHY%X*K(fICLb$ag-mm z-;Qx%-Zr2mF>KzpX78E4wPepzTiJ|}Jx_1lUctsGxxj|+{GORxePeZ6Yn$(>e$UCR zY}BSK+g-rw&3-T(^LcXXDY2uW*Xmn2#p*>--BOf{s7jeG^`Xq#+^XCmNE@t;+uvQ& z)qqGgdYke8O&SWR`7fili;dnK)o)jwvTIDPSA6V^HGf}IhEdGM3ai09hPlcxmU?f% zV?4%B#tPe6aT;w_E9Mj^>rQKP%NIECj$piUdy9>9*?R@7_S^?)fep_ti9N1i^{>tN z!PJi!f1~*V;EoTePgY&3-(9{IR^^^>%|;uyp?x~*WzmEddQ?R4#qZ-Tw(lBU)6|ez zv)h{o*tudF!`2E~o6lM&=CeIG#p{=^?Zf<^H}|POZf1N@=g*hLX;yMIOEwGYWr64^ zxw=-C@g20}MO&YT=kEO$ScX%2vyJR`Rj5w;Endy)8wgM@6QaHO!KO(h1Eica--6$T z=QZ=;ggS`xvU~I6cDL#9S%^=b)I6}aO(40w`APfQ^z3QU-Zlf-@WAdiBmAJt7C);djm!0fsiza*997yRTv^|Ez!dfwsehAM? zu05IJb13XUVl6d$Zzou!JEK*LE7sL%6DP>yJlK zjG=X(H!MH5x6r!R8=hbNz#eZzzOH=%@p>as_W)_}TJnYVEVADlmA~6NG{4z9EWZP_ z-jLsp_T8Aj&pRB?tVo-RHrVjno-e_Y+QDnQ<^{_R#7uJd)7ogigLGK47(wT{OcJS% z>ZwwTjk)j--D~16)Qt=v4wrvbOtF8pThRV(vm2dKYbVcG`$@nE*S~kQusq=%`UP?VTM7&a~QZ zroM%kS3FQyAvEifgqb>vMPhm&JR_Q6q0;$~mhMlA zr$$&NFh6V-&zotBs+B`eaYB`7Z`RGK1|m|m>E39g=aSkSK-=2}NS*Pm<~SC|d0^MS z2R?*Ug-bmPd&*F|euk?9dax983THQ`&d&0W(wX>k`}W;>Yq7+qQFo~On}(lpcCeFU zL(940dgzWx%QwS?mW#vDk3zoXi{V@|u^Q#6x#p^?bI2mnZR|Q-HMkO%$U=^&J4=INgUisPB7#Lvs5DK7V%PzvbMtw zomha?hFsTkxV1dMeR8pAIEgogqyi(!Xih0HMdOzp>x_@Nz~h-!m%(rLKU&@X3E^ES zW2UI{tjU?-lR}_fKDo@I3iW|Kq~bnJ zfU=V5NlTY4tz1?Cb3)=k^hSor?`-Cbhzl8cD|wp#FfzoT`+^)JB*dF;4iR+nN>!(a zca1v7RqPtlJJlM{J2fs~i*PxhJ&g>(=Ma4QO9tyO>H6DHJ#QGN7X*ji+*V11hG~m# z(u)G$kh3;S7)!$z7$u>8nEG{(5R^xWm(+>%W(vE?I#Yc>evntJyI3#Uh&P~4=h`;i z*h424sg+y5+x&&I^1Eh^Ex!LfjZj1go9ohhFLs-13dM}>&TaAll zr0nU=UxxpZ?D95~N29hJe!#Nj)q6OhS~XkEQmAWRGyiD4G7w6n`Q}8d&^S7Vu8f{e zS2+_pRU*=z@$5zfqDHrpnC)_Tg{(r?W@X~e#&{)RWwhC~EitXk?sC)6hxAq@AsgQc zT@Nb{*N7fl@2cpb4=M3(x^qO>L(60K=H_U*vt6MNJ*+%fvpRZ3?2h>Ghm?`~v^UwR zK9XuBJ*-sJIQjc94-SKlrW?qGFUls8DTWJtu)$&Wv?Pa?bP(ZTC2IT7R(MaEYw$#H z?ZsbqM?T31P?Kc5F!O{zc^$SKi{2Hn6C0FwPZ|#15bhR7P)!Qf(hy1@|MPEoI>=v6 zfTcvhdyTA z)vz1(`X==rlL-FIw5UOO>f})CNR*O*vQHfvO{hBcE*)fHJXYtE{}yl#hvPgq%&Vj? zm}0!*{5MSvigHq+vHb1WuMxCdG*ayHq;jC|2}=Sjh2ZiM8w*8vcDqVVcw;Sh%}FV@ zm(Q3j>XztLL$WW;(8dxGsA{e`CfHnCXS*CJh*4QtXj%?Ad+r(FNK-C4Ors3+bw5ol zN-azaPt&K`f!r15Fo#`h>56apJF*LX$75EG#to(of}YKm>9JtKJOx55~MtgP3~4~d8W1#GG#BQZZ?DU%AF zGP8Sbi=zMDK%31an4RroRtY-k@Dd%Q`raYgZ_vBo+m4Yy4~_SWyICLRu0%^hq)#6# zdxOStuM5W9qgZQ-G@8Qh<`22z5%|d=Y!cSGVb8Vq!}IDu$|COG}0}~hu;y;RIHCuG1Zi- z=32{4-wGKikat<#CQhFMVS7*T9-u$5v_an!Pl_6*FA>Ao+ys@IE(sxQ-hxU^f<7#D z3U1eOQ?wot3)MnA%h89XV_)D|x;`w!rOpd-o6ZWZqxz%}w*P|4OyQZ)*nMu(yFw`9 zGUozkk6DwFR>aN|&!`LI1e1)lb}EE}OxSFSl1S54!k(N7AzCbUqI^SYyrU^b7FZ_v z)|{ic))LVqg_?ll5+s?by@`~sGxb!%>R-SqCRk%67740q6wl5#L$(x*m0>ef(hNvi zrx|!@zCnL(#12Aa#ljKKQB@xGuwsZIP0;SZ>ye>(=+U~_;-~7u$k{*%o;~n|HnJ)m z8C<8#Oo};JLwbVs2Is*{w6vS@QWr+<+IIo8&)ki*v^1kp&`T?YDuKgJZx)zcbf*9t zvBF;5;QUOq5#@Jx=O+QhQ=;O{>k3yC7En_`&s5`S0YE7tAn)-C%I ze8Lpdr}%pU*r9UwTNd8pz+T6iuh_6cUdQrlmJ!(Luf=b+q%GA+EPvk$w$E*N8=_w- zo?!F8tjzR{@Lmd4^Os2`qCdvwbQzn|6KO8BaGBebp?^O;S1m@mT>X0)IOmYAW^`@` zxv|b7U?ombZ(sJ_m~uSRNq*Rea%(1JXsouXirjW`TB!5ytsy!=G(Zo7n_aNlFe60L z8G5K0^I*q3tP(Z|1qWBSzDYk26{^sotu_*;n{U1qRzf1^Qx+ew#u+M|*7h9H z_8eh*&YF9a#4R6OqpM9-G43~Lq@W?&2sn{+R(2MJus#M=noR9bQ8*P^6;rFMe3R8@ zYS)QXPQ6qe1J5z+w@~c97_5vG!*V*sAS}AsuoHXGM2u9zZkm)>Nk0U?;lnB^q#nie zc8RE`a^jr1Y>ybFauY+>7-y+f)*K>oj#|}f0moI-dIQld-q7ocW0(n33Ov2J#-f4^ z=wszNb3wMyQQ=v~6%-2X74$)KL5XM{T+w>i*lrW3+i8yx53ie2;NyG4QzVlkI@Mq;Pt8cR))QYDS%8TFwiGW5{-kt}A^T)La~ zl+-FO|BWm*sFcK8n$f$*h8w6#Yn59+8ALzHWauZYCq#@xq`?E7#)j4j#BRu7W70}V zu%05b+gTb@Wat17ux0>fD9TxqAYqhZ`Db~7z8EQM_U#VCY2r7sbP41lc7N%=%lgjA zG9i9F|0(qAxw8;>(ZEZ2U_G%hAJ6Lkb@;|FW^!0iyY@!!Ra>FgX_vcaJZiL%{)W4W zjVw9V+b!!QUJ5suB;F7p1wwK=M1rPT&QRs`jbgV~VZFXLM>y{W$rpXLPx{TuNq=9-0buBrVG zY0pTLn}kwUPc^vaJ+3gzM${56QPf%0Ye<^rjj-rh-G@n1$by6%GwL-MpK$pvM$M2r zPa)JBYmvL@R$pyZTJJY#J(mxQ#t_59fNzGp%wn*-rs`@_M{KKb&+ye=IAg;X^dmwH z?RM_wq^iTlNY)#Dk>0T3us$ z-&2w5>X}Zvt8XGtEFk`JiRvEVOMUzQq`pO+<8>ypUP3yY{EoU^o}3q?hCX&Da&E0~ z^}Zg_)BRS2OWhDbyXzuy)UCs5ckS?r!*jG5VEJ6yc8!?Y+}??duLr zn$jNH_GmiT zyV(jFky;rt&R-s>=4k6;h$nRB6m6vhdIF0*+FD}hbz<%YZAAqO(7zNf)zVgI>B0q0 z*r&bQt)(tI&_{DduB^-%e;>TUpVTa;qClZAtJ50gm8cr!hIn@V)F_sCevyIl20e5u z-JrW)__-Z-wMY5qb{4gV2sO&SD4z22cd94|7Ydh}2-hseaq?HHUNTns%`Y zIy{eQ79-u@paQ#@MLE-{4D7ly-%Xj__Oz)1v=CZ%opq?Yp*MT3OuslBkXfUYM2SK` z-S;^ztIuwf`I?49*P^Y;ma&m@3=z;3=NfGEX5p{Tuc{Edq}?F74Yh}jN$G5tiY_*< zEmTMc`k;JRXRKqZz6ESd6J|kVI?KLOG2n!#IG(<@) zngn;#gVpd~7|tnF2fst$f9R71!;DzeWo*b-%P48OoqW+%BNnKx^+ohuFIi*;wYyi7>Uazh>eU_sq)OxMqtQxBF*28LO+$eY1tVH+5UUS@nhob6VyK8;Z z?g5ujLW>3afnsv0EU%-OICE%yW{vz%zX8;A%NQXQkv z7}3|WV*M4(XK~t`LR&GAD{MI>OG=rJvl0f_3AP1ySClM)5MeF62JsSt(nOyR!~aWq z2GV%@GBlKo!{;aGPt!`L72cQHi?e%OEl^ z@RDo#;P2_$Z@IT{ZhHYdl5nz`;kYAKg&xcAXb&gb?j`x{h5t3S-^Llvu@Z(_Yxa>@ zov!Jw8H3S&f3W!8{I-T>Xtf7+`A_wuv%$aa45)$3vKkW0E>{&Kmbbb*kXIJFHb7!I z-Bk}MWR9!GYseqxN_AC3b~(ajM>;Wos4E;PO-Rqu`XMb#=RcS(U@+Z~!E}LII#Vf2 z#Y$qO40R22z2v&+QB~#iN3kBRZ+dn>MDLl;n6E!#)r@1? z1H^?@X!&z!g$}jKXnQJJBB16PwB3%@8c=%`+H6J|G2c(?jUFlg|FoLr2}B;fi}mpL z?FQ^x^`T=1tBf&kclo*ee;IRMZ*D_x);G<72=-i*892=6qHZO+wspkQwnoRd)sRz% zxyVqL1K)0iBtwKGI-rXN9f_L`oCP(0H=Pq>?2>~Fl7C4lsT^{6nY}LMAuf0Mgr2zFRE>{`Ehm)|tAS#{ zgAyo&N7o|cCQTza(xnC&#w}0e#u_H97^xe-ZM17~n8i3;=ZWYMymk4)#1JbagYN_T z`2!C2;oL+&Dn9qld^@%B6LnU@#J2t)f(UKvzk;Ww=kq}+As5AE&$ z5vgMPiJ8zR`+vgs#?jeYKY*<<*pDLC50UkQbe#!;eOVysH>1T>T0OeDI?DAQ9zv+l z)i5M`aWKY!^Mv&9-a1%qU-n9FQ=40Q+~8PqgxhmV-#! zN9Sm%VGoU-u=07NhNXixb|Ruku<|)AH789OJw@v`;?SV&t851A&8*+?_gi@Be@9XG zn^6Q7HzZSwb+-Q7F@lWuDVO#Q&Zmp4bbt?1SI0B^DaPmCtXmngLc?BJnHahbh_wen zId?S#agrcyRQM-XB$(*R5&DBxVgM>c^WgLE_nAB&$dwMmSt1ZpLaXRT<<*u-hw1Dk zNNbsFdZY4QOJ%(8O*_8%p1sjB9~9~{yb(yqfi%u|Bk11#pWf`E=C?3Y&&9rjHEae| zH7$6$`6$*li-IdVHY#CHF|MIKHq`n)d^?Vh)Ei`nUV3-bd!vI59NL-i6zG2=czV$J z?PFM4(i8}DyzG#T%FC^~{t0GM8G<{r-F}?9Nw9aqqEK(xBD2@L2$Hj{?@ykil3mP; zAx>3jch@&VHY;y8rQw`trpaEC-;Fi)C@j~2dv3ek0aj$Vf+?L>CZi}%;0qsz)lwE6%74U{x z%(5p9${j(~(9=|T!wUH=hJ4?%t3j+hv(y-E&pn!i{t5fi;PASL=NtI?dkJTw@t5ecLe#zD|kuLww z>ePGd*8aLW_1<-BMYgZk>Ve10QtM%iU@qPhy(7BWo-@7CZczCsgIZ&E=PpBE?tj(K zdLXV@>(^VlM;w23JesyYts3q0puM~tn7c2!k?u@wwks*kNFRp2``(>!mgZVPBc4b< z9+Toao&np=L$c#*dLk(l^L%nYo54=8j2uVw_hTwo3u?9QVM`xIl&Jrle4Mu1b&|e6 z9`jvJYR80H88s_djwO-GW0N^=nRGl0l5u>V$Qp&eEZ>UrH&pqnYp(;{3nt<;=*VzCa)D({SYYwrvou>}%7_<)^51+U`8tQi7FOej~;wYnx zl8wb@q&1s{#S5NmYD88r&WzKYm4WWD#9L<3ylwDluohB%f*vUhV=K{~Cl39J>2mig z*KG&OE0}+*hNm~XytMr8HRXe@J%dJAK8G)#o#Fqaoib|#UelUcRZ1x)uNp? z{#M*fdqJ`IVayEDw2o%j=T5~=7Om4db`yr^QjOo0feg>Zsy1U>FI++2vsg;mgz?GR zWQpy6EQk~aOZNUceEM>3u)xB&{|WqV#)yO-azY*r3cSA|h$O>$Tva<8f;K8s_j3Ks zc)~mt)Xru+nY5R%Q!km>i^5|FJ&M+T+c3AxKY__JYL#cMoH}$&$L4ee_~zTmRAwe zqIaZ`52XR~|_#H8ihiqy=8kSN#i z&7Xa1G;nUiY8%;d7Q5L zX5I*7gdBaiOV_01!or|FJU8J?N7%g23%!u8D*{)Yx7@cFnttQh* zg(CRZ9> z_2V2CQEi?oiC*Xfit~BzqI}{hoVnL-^b{0I)ZWE>0L(EmHKE40UQNU4$8Lj2iTOdv zv|j0Xb%K>bN5OTK1=T4(ITxmxq#Lc*M;G@gb_5$;SY4n81U(?~Zq+2Q(cfUv)-?r= zI9tuMG>w(`r&k}PAy#Jj|Jym)h+ONNi~};wL|Zn#S_zy7ZqWbl*M0xTI0?{Web(?4 z!$}~Y)!`EodxM_QiCH(_69}44?@2q%Vs2%_&rt6f^?`UUt*`(yiU@4k+2njD4cZ>d zsE2KFIvGCaI0+;%&n(km2^?&RpX5zE=6uwM^?^9)a&qN8VD{s|uxG>xdE+7OJBfqr&LdV?W=xLjFT>fH2X3JQRQ&yesbi43Fo~s}J*sH5 zFLkx$wn^}n?iY}rmv4a$ZSiZoR(71k@=xiK%nQfD7i8Rwj_6yGEn&lFbOet~#cG++ zL9Oi;p}j7Alyyc&-{=_2j1GCyemoJwoOnVWjL=f4cl?Dt3ms~mah~}Mh?W`W<(FYX z%kl<;B54;x0`Rvk#KiuF0(+yqE{tc-!_(GHQP*0hxDLafXm7t{CWZU@OYqlhWo=tH zcHuaGYcplvasR)vFpEeF6F5mHxhIvdF+j@FxO7J~_#r848^$T@ea^chU$=;%LFFP2 zX9+$rg5B#Q4VLmauH8fC#Fvs7z72NF_eEN1B5sREzPltcn$%B%MJU!FqZ!_K*(||w zSN9LlsG@YtK_LBnH=37I)s0Hz?vttU{YwH#<;O9{6a5$0_hyUj;8^dU%+X_xjv1hK zaZ`ov>5LbEbIhpDFJ1yJsvM$r#1*Nt-f52ycQSwXa?%HB6SKx2*!9J=lao(QYEZ7e zbOL+NpdO3gsGM#-nfWzl)E`KG{+M~QELfh4ciK@Xa$elMe)nYJREMEB1F<+KI#RIM`fJDpb6fvl1EtXB$O zjII;*x>e&9T1dpWAVnd};a&nBp z+DDnhn)}fYBf)kz!<7}heQT@D#}j+NX$p(&y&iF+qTB7G;r~V6O?(s2JEHkb$+=8( zjC@URla5&QBn~}^M^BucF}|L}?)ByB0jeu&=im0Q_Xpw`<@mDqm_sL_9DMKHeIWkS zp#_8Oz5Oa_Y9(FZJ*fFWoE~NBrQ@S6M}Omh?FUZzjmnREeB~;nr8S6Tn_a?zaW70b zHnM#@n}_;6%<`Gdqxh20e|r$A%)u;@ehJ?g=a3tx1ASRWSgHnP*h|$YgZBig-Eqf8 zkxhQNI0H3Wq*jT%%HAD|=NBJ{H>==bk&m_g4z2CS6It8KcOL;)$Y^>!XnJt8py~CX z>GeL27BoFLTF~^2_huX|lQaIxMM4SyjnH*2l9Ob{FISp#lOF%7@5eSM7hd4{7hxPEdI^zU{_}i;;;*&qvRI1oB0!eJnx&oTg48~_ zbK-$XA<_?HFWP;3s+0sdNc#Z7}7K(+?#y~dV@w9C9DnJwr=4pk_>`G*Pm&$cGjS}Y(;DPZtVeyeRW9Bz? z5%b0_8Ysh?u1E@zswu;Qkh&Na1OjJB3=3iiPITU4G!#h9c23#}bZH*4jB+wW?uUZ` zS>m?16Lh%g_Q8j2w5np<_FY!7vX}o+pla4QW++icI4+(|n}mJXF1!h++b~nSJ{nP>1$BBth~v4w9*-78B6o0 zE56_R(-)3kI2Qdx`K6gE*83AL{I=bl@Twh(9n>)7VbF~#{hEG?{OwnFjvPW+%3u3; zSv}y?!DnTC)A9j^GLfM9hG!$i`lj42X7vZ(?ap{J^W1K=#%dIro#!07PVg$4=ZSqn z%y=VPJm`Qx6Oq`QRNxDg=`Tr%Sq^4%1rKhK4q(ib0 z0?9(k2OKUNn`9ryYbk6K_2kSbE&c5&IHD0WNWB%vO&??@+}hnynN zKQ@Ub&u~@7T|7)Jg&fYJmODMM`l_(pvIGItu^SOF1A8`PFn?_+R#zftlCDH>9`6ok zNcAR}-IrGga%z4_-;i>gGVIv}B8}qDuE~ieDR#Za0b!Mb&k>|)s*tpe@j3ZIX=v4t zQxUTlD~&XLKuVldv0T-Od`||egVo5qz?+7b#mck>QFOk$Mw%dAjW$V)o2{|*VU$EG z<0^eTOdlH!d6UXJ7eIPw=oSY#FHz&XLI*i7{%oI1*Z^VI~a!XJ4G13^grW=b~7|OHhTA}i2USPrItCu7Q?Q}2EQ)+ zKWbxWi*C?I!ZDqvyss4Pa;qgVoae4?2^g}scVoOf^VOQsg6Ec=zRquk;M~Xhu2UQs zUoUZMoVAZPWpmAIHChIQeyoqI68MOXSKUAtM2+Sy3y06w#12ae_|ByFC1AQGswLP* z=E>!b@QLNl@JZ50n-_>uO{)nn`O3O<%~zJ-iTI@?MJplj0}1Dazv@N_wn^oVn2F^v zF_THtTvAD=VMT3&zy3TTL9%e2t3Qht`cU-#eod#fQ0Ms^BO-Xtb%VC|oIUKuS@5a4 z7bUtTl{#`Jmd4~vEK>4qJZ2H8gMCs_s=SY=Mxg!2}^ z{H-0a)gp^~c_7Askrnfwb5@%ateb(Z4B+6cLH$2=ojNpI^E|CpmV7_LdH`#|Kc8Q0 zz-`!VDTNNQo2)b~u=;^wUyZetq^`m#o2bGW7`F^f)k&^2g9HhE76(d=m3L#s^}R%6c$d>GVYR7w>DA(M8SP_eMyIpJWW(JAs}^Zs*X9GHX$rgpBUv4Cn7Z7_ zcwkn8L7Ea1BC*|@m%;M7(hrR3a&9oW(V`LRa>U+@L&R0$ads~s&B;T%f7M|?$B^}_ z4g*r$EKQ1=^{W(@KxWQbycR94BI}#>4?LG{kg7$V&77s2>e2Vru&P=#@CSVF;Pt(1 z#nKWudV;qt1N3Ss&{yrhzx%=UYRFKUAG)n{QAx$ZaxD+Dsb-L^e?Tu(Ec2mAF&bb6 z+}CZRY^5a?tfImET4Atw)k97do+4LemArP>mdC=#+!PGhA4`;bX!w zBgaQhh@98}3l+$nRP!V=ZpTNjPAMr*UzibS75X8SQq43Br%S_CUAw|?PicfVSGDBRBy_7U4lRFn?mBcbnbB(hBZJA{z z)J^nKd1m<-=HuPU52rVwP4nBc;IW;cd2IV*r607Ia5ng%KZxhf>Bv(?<2jBH-3!LN z(0>>ex*Rm=65hQCYSSOi z$vyn<^KTYEup{-!9>>J5X72QN{n&exIC}Sj_z#^2*4*ZuwzU3u%!(hrE8Do}v55Ef z-u~7d<-YVcdHNTgn_@VYIDG3HlY?~K ze|?qbLO0(tt}!@!*1}Kz`{<>2-oDt`zw(i%S4DsJOvZ*!uVkLPVfBA1rhYwi`Hcxj zHZ42o4t&b;w_#UrnKa|I6ZhHv{IPm%?LTh)#`>?8v^wM8Q#wM-^W)?%?vjeuw?=hM zA2RCf{kyim{=|jmzVQXmj`+6y=;tp_sGjqulE{(5LoeO?LF}o!!%A&=PlkUh%DcUtPU)@`J-~C`}DjEl+m7^zr+DfBp2A17El8JNV-Ezijo4&xlLl zhggGWOqn}><%7#gp4roJXy@abYX9|a_d91lzw&)SPSIVfAGmwr#58-7-ajgG&DzCf zH__R*8^obulaiwoA9-wZ&9lu1Ug#sOTCm79^Ugv)A;OY6VYKaw zPcL43@1vuyJ+V!Gc<)m$JRhJl4^JN#AG>aaRFOM<-YuD9N5%wkp*K#QcH7PCmzOU6 z=Z9||xp3<8&-+_n?%LH@y@mKk+?W_QE@j@Fo2IN<@4mbJR4yemW_`qyPrDv%KQKBJ$mKRg)u)d0n{n)4Z~Xnt`D>S7=<@!x!Sl%DFF(Kg>8({eAA9%TuOI&Gzdv8RXTkDycg>!X z&j&_@WsVzVTPodq|NP<{IBQr#QYMUyeYJi6Q=4kGH@$!Iot{%){Pf)iAHUW8>4hJ@ z{o8YUTQ}D#e=+#q5S%(WKBl5{)qS_lr3D}TQ~k$Rr@y&!;84rHjrC77`WekPPD*no zxRWSvb!HzS$N0vsh=Ew?agy% zFAa3Q`0O5e%by=5rjTLIQDY|OXWu$!N%=$fDu1e~?|k*izn(n)M(@>czxr%^YJ$xg zF+^B-f61b`cTSnv=6$h!>&CzAIQ`$RfB5^`?|-spec964x6x^nG81ElhKgMOg>QcD zdGpwZ&+dPvYjgEujRm=P-Ez0|zzTgpq6a`ucYRXTJ2~Ey1JXCSzHL@W=U6FFy00_peXvU7;TQd3)@b$=|<~zVGbU%TK0+ z3x9X?S{~8=*%%^!`gz=e)u!sip~EU}ZJs^;gXa=XC)Lb3PQJ8V?YM36n|~WSedfG( zj$L@_r<{Vc4O6b@))!9L)l#&FZr%2mtZ!d={v*42;PQ!o1cZKZ*Q2?Shxb1;vFwhh zm6B)HC($F6y1cJm2>bV){vY?Hj`N#laNgeY?$hqZooDX*>}clFE$`pdy5yE`{#$@}+q6_T&MHm5oxJ+CI+4LNPpJ5tPZuEbbZ z*VXWyxR+T;E2B$EGwcNCZj(egYmte4U%G7r%F;u!#kCV!GI?xptVSOjX@4h)_%Kou zncdgab{bKqY!z4A{n497T2A~l+11B?mF!itF{a(M8~BQEKGUFl|JNO`$GeG?+Nmnc zt!e|(kE^Cxn5#2Xwee&FJaa=d%vU@9+-CMqbX_^^YD0Ck_!Op#yHfWHS-OHZDGwj}pG0CZGZNL4K zz;x(L&$?MT*+_r_R2*IjefW}OCaa6Xr@?BcA@MqByCbt;Pcld;fBQ5;t@yYx8e_5t zJi`^B{WfXTHk16+Dx1GHLM~uK`sI{KeHemL`O{dfzeFry?>rKty5D*Ff6^ghNzv24 zrh={;>FnO4(|i>Ai&|Q{q?L)Z|!2ffzGNPR+e4VFBHqtG5nnv2+5;#0h(@00*2{J+-jd;Xbl!1xX zyg_}mmPhxZWd*p}Kn_t>F%u?huteGeIflevnHC40oC8-uz$bK zUua==G~0zg(n~UV4)@vBEk3>E5BNOasfKd~1H6eyKgKPqT8aLNA&y?uRz$5*ebEK2 zqg~>GYyQ*|9CQscBS_Xc>?5XctQlQKpm9uNQCoa(bV4E4bcZZQG<+*gAIc*a z*FnaH^|=b1;U_=Krj02?{v6!I=|ic6KFHol+Pk-ZR>l(f-zwif54j!v#rS&#s|n+@ z#K!g)`}9deaeDso8v7M09Z7(<^O`OM()tf0@}4ionK@i>i$_9bSEJ0<|D z=+vPP1SaSF>_WlwJHk(n6yagBmYS>#cVV+)Q%F;;PL4k|p8dY*d3N?jD1D;{ydV!W7v*a@!8@2DZFn4 zi&=Jh`rm9^&!RfkW!$Gq*|M&zGVxAk};r%A(3b2_a~R+7&1F=SjHaeau%cwXNJ z>E+q+bLiO#g|1`+ziD`J#*FX=7h*OC*W1O|63D^n%eH6b~uTpZCrTjdBk>YJuC%kmo*m3NB6jnWZ=lKPSJ<`kBWPiHGlwl0@+4G#_#hE?RJ%Co-mXTQN6R+7F9N zlZRI~JA(#s5Cb@IaVYd`rw*;)d9*@jsZKJ<235Fl(t#V-DjbZ5gf4WK7xR7LD~2iL zNlIB_v7B`psLzykYdLGfykM1F_g#s(Jq&T86vJAO0#v?7Lxy&p5O3 zv@ga>r^J`i2xwEw5tGq~6k}S=#4I)AJM%o*T!?X)=MS0CuPuO6+Al5O`}5I)QUWi) zEY^P5VCh(I570g6@4a97Rq2=1;<;dDBiBZao*WhP?6In!Li>xc5D2W-tKb*JEb>YP z#937OCbCGd;1x?YU838LaS`?`9fWze1B7qv|mK2lArX%EI_{ zy4dOw?o=G231_W3(7f#hB=w!QEjdm9a=|XC5 z->8(_Z1m`wa!%t&C2^*-Wyqyb#narrZ;}BibniRZB)Ep63IhlAmsVh7Gguy zm6D5eesCV51zo>4{+AZHr@Xw935|R>&)6S-a6bN@Q3ewJD9w_R`^vRgLW5}Le~@Ou z5@@M@o9GYg(;{0i)fh-3ccLvXW^K&MKWdk4^~&-xD7#pwBPQQk%`{(sQ16QJRjXH2 z{Mt)fJY;HROUfr|kxqVpAHWsiZmps0?id^yM4c?Vj@Rx(6WpuOQzA6Ol@4H|kGife z5Bl^S57Gol=i@`=l92UkqD+&6AeQ(y&6LsXBcO>{4$8mSiFt9tzuy7MIKv&Q zAtA$)2S~_H9}+Szm1d1K+pi-bcP2+LZJI75R--|DbezwI6!d=s>^FQ3n;qt^Mn`cc-^_B|qS@`|C|nCTtFYu&8;quyo(UhB-2 zDW?(rWmPPXdsct>KS0R3UUN6TiG69Ihgj*htE3Z$zQn!_QoTXTARg6(|qgi$3R0x{J%i53ZVaZ zP4+9K?I_{XLnRJoqPj6&iJNeEoi4MygwNt8a+A2p!q^y$I8%;fanSUs)ym4I*38y1yB)t!z_3rC5d=4K zYZ~dtuBOb^qE1<4O3;2a@;89oIUcb$oLIdt0XznOeuT zPHs(Ug+2yYi;RY)*T+mJY1AxsvH8hUhqBP#TBZHJY)}5tSjF74=FnKoGw_C zqB{&W%4FtcM3fjOkYH%0Sq7gJJ^C7vPlLaS3U%_K0q`CYQ>W^N+)Ba%s`0(bs}pmk z`ksU{uP6~-QDX5_ct!p4+{<&4c;V_q3#oyxK2^my9yT!ks{R7tipdlg@He0Z3iez& zO!{q;Kr3V45!|YiudwGtB29CMP6RF&QfIqno5I9g#2*YZu{dS=mSPL1l@liAA^N&r z4(HRQ)(hCr9@37cQcO zLYI@|v^$BrJ0~(r%r|A(^HZ{-bJSJV{M1}^Roo@dcWR}H*UBt%g_?Y=Dsn{tbHm5y z$5OE_=QUsnj2$(0?5LrLxm~3ktz>_Pt*wkLp;gLX?pqzl;)??(k7)Y z2~o#AvFb>(S!;uy*ik!&+L>=cgE9nl0u6HVC0a^5F?!?&f9iYlG8gxhwzVOYxzWqFhUnjM@SQYl}v0OeUkEfB$^xw{^Yoy+38o zUwFx%aVdqN7KUp2+deKG>|?7|euh?lG+JO&zuz)kMFhTsx&U2$SWkmuJ^1ON$B1!@ zzMDUI1EOZ}2Zx(@gIzjJ-=nKhOXxvBN`Sgt-?UmIH(WM*9RakhrAZT((a}2 zBkj-7GS5eRbjCj~SyqapWNBqd>8iV6h*Z7;UYhYM@e|b3Y6g;h7pTd*R+KM9xNT5h z@VyGrn38%?J?57gyEA(0(LTqHo$fwSY>)hXdtofdf~5L4t$Rz4*=~pA81jx z>8zEM$Z;wsBBof7Fe7)3SnZb!nWqwEXNVsU#j$64yDmB=g^!+-%8M-K6F(z0Uf)9_ zGqPB0R#%4&93dq{=2g9kq9qz#h1=2Cw4vz&eo2H$JZhDPn#f|7QnYk;-)Y=^o*YxrKlwkG4cEp1_ zu$3pg=d$W+o7J)+o-45r$%Pm4s;pnzx!cQ`bATl{1^9t`ygIYzfqh=yyxS|7E3*rt!ECCxy3m(@Yf()BGkz!Vsn#UK z(v3);=PHk8GUkx_E3^Eq8_wo{(QonxZUpH0mUbK8(rMFG z^D~1bwL3rPR`?UeQZx0G_*Ta9zxYO9Z!ws%f#t-d*nlZ!pr(Vlfqs%gMeK;zHj)-G zj8P-T78jGj*>l>8dQEMh4YP9VuUahu%+JGct z>w$^~R?YwW%x>_a?fXZwGxOZ@%yayn-|_vaeP`h}_ZLs5Jnmd!8(G)3R8^tT$90{E zl?fqS{|ado{h2hLo-w`9rICJ1l&naQ5CDlS-T|5i#g&m)(qBvC>uZV^XhpZbC`j__ zlbOa44}BC*3FDM<(pc$r(-O%ojpP%d*;YoFx3@weQGR{SEJ5;**Y-OUI?Yr{`UHte z%2Mt1$*{u&hv5^P_7ik1n;Oi#Ck9D7B?b9YZLlE~qilV`tMH+az;#;wr;D8=KGVd< z6Lh3+mioa#w)-`!X-CdXf!0RjD^riqPh1r=_IM822&o5)ZB+mC7lwuXa_T3pZU<+Rk>D>L-C3T7a6W25S-W z%Ww$e6O|2~nY`#-E7GzvEm7jH^apo=^#JRYLiEa8bK20KzLatz+V(l1@dGY!fW8?& zNyid|D$8vZSnaok{h@{T1K>)|f{_RC(sf;!@TO7mE#m_5BY`9S5UYS802FOIV;_X` zhfxW!`>bZ%1Ewd7Yq*qI*lyr6aXgJ)cR7idXpd$|02kOG$8{2panFbfg5;Evt&|wG zgku(PVq)7(;E%_FD;woi9>z(^=+BI<$6u{d9fxou7;H( zug7_}EYtWy{7^qqc(*Lo_yY?%XA=+pF1Ea>HJ+l`k@A+*pIuX%KEqoG4IQECeydDF z(zzz}#7>TG%;%IEZTG3l?6$40^=5O~y6lLuQDwH~uHG1%mRYYpbKtkLMVp4R_>%{IJBOgf zW92$^G?tHi-KXEeB?S;{-ne85WStu@iOZM=z0I5Kn{J;BM$noHdv!(4rnS(puB-s} z|0?mfZ>YJIF@!?#51+Sk*e@crta8JL^R_3DI#}cJKw$Hb&uGBx5#5w-zwXK?KqU}K1-THQTbj>rAbohFtRIU{k+ zLOCnLX^#)!q+$#pZE<%DcHRbcNjRo`U_fHeJ^&vmxYBU=z5z|@#B*MEr({NH0nh=g z1;r?JN;r)&5YY3ccB>L=zlQYoa9quR0^KKIv2<5UqYyWM6(SMWYUCdkP9qP<665bS zsA6}qUJLQCJl{MMdc)!YDh=TIvuCn!;%iVp4u|^&Mp4$*zdd7@*c;nD3OoRycTn~~ zIL)*HCTC%}{`ibVf0I|kG46rU;eLDZ8KRf_TsX!$5TTd*%rlbSM^A;rqXt0Lp?n49 zcRpi5Zq~oDhxO0H;WS_V)rcNyPy^4HY3QBq568U!*J#R8?tcbn0OZqfzxNp)-|z(P zUg>6(P%G|!k2{63wY5Qg9`^yzM-6IkHz)8YarEQBxap47@8b?MsHeIexF0}@6W!Uk zqe6rF%kJxOC*$r|_eR_)xC?f-;x2%@|L#74I}LXSyI;oL0Pgmq9s-M{k5JBD{XXFs zhuPbp?!q@>E&Z>*Uejdb;b{UaIS9p@Rn7PWTV4$yIJN6 zH)iilWu6EWN26-EEYh7bjtAS*vC4cS?Vj-bE+Uv?qT;NH##*`W&e1lU^=vuvYj?)l z66EuDjuSBKF@=<0 zY*23+?9kt^NgZ3a*&%v2XG6|jRb5#U@`yZgP$2~xZxk5Kp?fU!c*&)$3K7WHvO*HH zPweE0udM@Ig!aj7Vjl^UM`A5kiM3pbHC-hZT#s-m!c}5HZG~g`tHknGV!5ltVs&D9 zM{A#uAI7qZIn@=)JqFKg zhCYJlYS6uZ{^|q`;<@8DX<=T^+3p(&(@J3UTh9RWX2(~gZ1z@6D|d=}#}z~73j~(s zi#cY|O4m3nz6G6mT#X}ZJN4C?3Y@&RCfm>=714}oh1KSo>=I|mG@L8Le81b)ZVf4& zl-#zeqT{ua{@49C2WJ5HOIiMa`GaSKj8B7-=qz&@u{bdf) z{sB}m+OoJIE41Q?f1FPYCS;4S)nK{nCmQ?%IJdGB&fDEja}G*7hq+pL0P9hs`j_{W zS;T)vv)LevM($?y*kA2Idp1#$^xXaoPS4G_%x`z^9|0+B4j}N^IrQ&nQ97)~?G{qRxZ;e<$Oo0IX7$#;+J`7o zG4Jgx)uWIZV0RyS7?up7wkQ0c?aKR|&`mfE8&U2`%1}H1K)&D3Ti@@TMU+Rhg`E%Z zi}3eF%VXfy{O2&|zh09~x~xK3qLrsIQ~Z%RDFL9adRqn?3?U8@eXMj=RNUvVjfqAzTogz z=k0xT#VG-nOb_ril+BV-GjN5Fa?d!5l+rkhPw{FD2NUJw{+`8VmY4lnhVbz~C$e&gNm62l8ghkcvEML(9jr-xc^( zr#f}apEPEPOhw<3lb5*jeZb2dg58deE~8Cf|snQEymMd1N>s5qw})b#>lKb zg{B0*VDV@L&u~X$T*0q!Dq(Lc>TlxqR!m8rht4vat-I5lhB~^=P&#o0ytuLp-n|TSROj@E?xULnk%m!*4bOe%L z@^|HN>?u(Y1g`*m3ys-b11-g-``nbHZGvJdO3C2C6Ropa}Y&*HnCO$z$3*6R5RJ?PK!xrQG3Gbwi&EVt$L+-TX6{yHoWRZNC3-<{48P{Cg$#_s}|UwD+hVZyImgqYiCrQJe4I(f;dU36{dF zY{~!1P4PB^KjMwI`wC`yPJ7qX;Z0t7K(dYG{MDuwb>sbSm=u};6!2s^5xo^fkDAhG z9u@u0-ouj&;6igjrO2R+^GQLq$Rh7`jrJYSoU#4=K54@>zH9EP(;_$2<+P~j_g904 zu0e}lLz@(o?7RP{N%Y6}3c-U#m#RitcfsNZ$Tk@d{XJ!(ICCRTreaaXfing6QkgFb1M0eb-};kqnV;V34XX$pqa!EgxY@UzSQm2G+Xv+o_Pf_^Ahui2U^rK_suBZ z1^u>k!yffhyixys1vZ8N%rLu2P{~WBVo=R1b&0+KtlEMzcRNN<5|fS_T8za7i@}AQ zUYcXJE%#Np=XPo=O+9b=S7!&>njSS}WJHAHXI@0%yE_182(qPs97AM!qjHMq)Vl($d` zc3>=TfHqn^$h{9d>giDq8+ZeEm~i$#koWA#x8U1?>A9@TUTKbH-e>bxW=Go&;QI}O?Oa8jzjxh!vU9$VFX?&c0p*MW{1MJ9)O;yj<G33sX{R-+{;xw%jcV(cGW1V({@iY0`;uATmnMSyrjRJ~YNPtapEV%W zd;#pT>aCMtBhAZ?O-k=tKE5;4pv=O&P{yZXFA7X4aUaPnM}KRyJhcw}-2fcW9I4Db z9X);ys9{~1-og`;u3!AEr8@9$Mx{xLT0>awqmwI$8+uQpNu6oAPjNV$)T?B3Ela2~ zgIip0_JeLIBxzmBDA?3N28FhapV&o4op3M2H}6V~a~^iM7YE(y6sK?c_5xp9=CVbS zjvlr%qntdQ8co^!D7}|zQ5zTswnZ&E*rM*ZSDkXgy?=5UYHe`1o#oz!Y_UZuwr9*= zteAIZ2OJN&ExwJetnCARWG2pyIR;yuxi0&kt+}g9$}3huyJl5c&4%h#BTARY3?lj! zXe=pA@3GBsJzbW<=p|usH7y9Yvca)ijo_FD6s&wngj_-$k1D`tzT453lIU$UU5nlN zptBrk>36%%X?Mij?{=(zAQc{k`xf^{Lr3c;J)FMJPwJ11VKUu1tfgL)Xyt+J=P~`a z)zu%JX3$em?7WMyCokz2)-bexcF(Sn{bW^Se-!?ftm6Esy(r0+Vq@iaOh&yNuZii> z9kk9zji?nRM6U^zITFIIk^SPg!gRp^^GM0xph^0k=fR9j!;EAaA0B-MN|-}t%s`Jm10{V1vis%<9(@Lm z)@R_Z@C-D|_s|ZYHu$vXL9elN3jJ?ullu7f+RPrzu0)dv%9c^~nOZ-ZopqycA4=;?#6qgp#qCv-fZBIjIpM$EWe~VRLd`^2}}1woTub1ByR8B{(iy z9I66^B>BzD^*s2#OlRP+yP4$e-t9~p07RtgvlD;gRhMktlE2+%3b37SZ5ldDWfD>>FuDnyskU@1g7Q6Luryg1W9e|=G z`YEAE1to(MQ<2-3yPd6fq`Ly>W|njc3-n;-VeT0T{(P+TxN2+-^C;~34J@-w+u7ciDA1Dx}7Gy zheYVGnlwt{tlUsJ@|yf+pIg6khvIEYhF!ngnGftRtYIT|{7vfVZD+fyfySbcI@+kV zy{TK+3q&0z(_o3u6L3P*S74vd>392ZClH3rW(mb|%-5@q1OQF=y_`v|oK^`8w0cckQBg4}s@T)MWUW*VCDZ+CbuQ?`HM2mg^V* z$fKSkK8#Ir|6g!_hJ-K!ma)98OQC(7Fb-DKLstBjPAi|DY%{%Y86H+ldxRI^MTvWu zd`)cJydV7cTVj#L&LJXCtC$Y1OnU|gPfeZg0LQIt`c{4R{MUG4!daYHBk}ZiVjSnk zINa#%(N7PY%t3DyCe6D3zDaIWpMllB5d=5e%VMo;|6}WJ-3y#KS4a82xQ2~Dp6LWdJEp+ zC@BNB!aJmCT!Egy9U(dM{IX8Qs)pG); zKskofbwY+oF2(3tPFOFq7GAwyTQ%2q})IF|CNUl9Aqj`r=t=yAEP$7Ae; zA3*PoM|=4gL-1fm+4RCv3;eB*v;E8d(92jc%#a&PP9EeSH$~0RvH7~1?!i}`3e-+! zm*1pL+{*0of5db7ky*wghG?3c5C>fGi1=8IPqJtR+jaCm!9(_(s)b=;lRCFnr4HOT zsrj`_PAPm(D%L`co;t`~G#&@7!T8RdQ{tiN-IZBEc6MwbJl$tJLk+#C6M==u;&Vcp=ckG53^=EGBw-!r zWjFxww{XZspbsv}mURg~6%og+F}f)k}o@2G-y_T4~|dbzDP zm4^hAwdFzV)YrUPAr4B{*v^ToMILWtcutzh?;X2@-y7dMk|yI-StQR9Bk`yAurLmP z&8q#p9cL_?9`YdFiW6>p_`X?PaDKJOp(Ji^OfQLN=|k_m=c^%Uq|{H(x2XTqhFExM zVLBl{`--=nGxmvvrwBJ+1gC0wshd|8XpE-f;;J#zS*psf$WXc7b4l-8Bu?)$r)WPp z%uVlzA_M3VB3WRQaZ)@FUsyM{mAbHQ^gJjA@UFjpZP3(ZEOE0jC#N^7GyYPVUQJmq z-2KNEb#2?DB8h+7#H{n2i&<@Nn{x!0#L8_{Gruz1j))G?6GmccaJ*x#aEbmzoFgoI zm<}GJgHqZ`C#&?Z$jUtO8rPx0453Ax-4?0mWnRiXi=gFfHfaqGlUWrQzTn76IQIF- zUGX{QGmgcC-t<1igzgH?b?S2OlatFy=ai4=W1wtTVh5aJV3=Kum&ALqlg1JY8^iJb z&-)6(wARgpaR#2_!Ot_knV2ZwAOWpHI@`T2g6*J<>ZRA&cWF|sTa;5uH0Q{YIKQ#4 z`P6%(noiO2qhR&JV%=Lvdy>cxT^L0I@Mfl8J8?!Y)}A!&JtdE7Jf(~`$!6UHP=xg{ z2Rvpkc_RA>Xl%7*oHQTIKRBCJQhN&% zV))QchYthMlf~Re1jaQA7@F@cKI(kf0nCpjItM8rg03X(cM-$hJ3!Te?g`{9*6E-C z4<3*h%=5(S=M*70+k=q<4)NXOhK%^aTIx2ZWnb^~6dLL;FL>BtP;Sw3ye^!6R*--k zqVf3);OJ4q-rQn4K(|K@#v9?exrI1F2>JjBM#w81cYAETaF)56{ zhi6uTn{n;}Jl-&caSj6pyrGkE4+A^e_YNPy8`&XY2Sph0pUmTk-Zy-3wnGG^n(G7S z%puvPXNZM0G~S$)o#nI_iuIQZjyPD4F7?J`*PXIk3b58<%}}J`wBvGY+VS#Oim&X& z-zU&5J!0~~;LGEVE%oI3QU$+NUw4|+Khz#EmB?i13D&9$j6UQ8B- z*v{mX7(b%%6aj~wM=NX7rw8-ES)bcy82SokNxqX`Sq#cdE#M%EOFL@voU~hDJ+Qn0 z+-T@l)7(^Y93w;#X?6x%i4x^p;IK>&+N~mFo>{4caj}@T4fqB|jf1atmb2Pc;K1_| z;EUU*2Z`NaOH7|{MlMt9+=ZpAXBOb^i?|Q%xVVsKvVcXWHNn`cd15Cm`uX^J zYyP6BWa<(0G$(M{Ag#Y4ZHvhMr-<4`I8H{*3M+Z{sfOs`L+>PFoi(CPW%PIId@C_j zCvc9&4Ic_5#0hpS(RicAJw9K*dQOl|N@jErXiddQ`*j^y<<-CAEfRxy)duh$ImrEo z57j4_X(!D2$?-;sdVP;lrt>j;=y0NN!Tz9;d`zi_>TPFlT$W%*%hXd1 zqx3S+_BJ|_qbbo4&XMKGx{_m&o-Zd%7!=`m(kU_K~RrCrPYf>N2lko@271&gjct zj04D&*ivq8y!`4luT13?i}qUT{E4DJO7qSlLrN^kqt{H=uhW5&|Mo-!w4=_Xibw$- z&8)MNxN9WatMkrIa)i@lR!xmTuq;Auz*fZROg@3%Z&#^weT8y;XN3wloX5|T>BleN?>(TvT_owpFA>-AFNpj2 zS0tEYke5lXroxh_TC}k%(_o!8@BGvR_+yN#o;L5biOi=Qn?F7D934K?_9@yq>pZwq zKh<7O^T)oH%HlYwhtAJE8e4~P`ZiMS`11ACx>ID-^k71|%i(nQ2xroAd^y__e1f;w zMHfk$!ZF(H#vHrD?dI9(KCjpZG%Z}Sw!57cq;u&{VoOA^6MQPHt%!5$VMowQu)Ngg z^wN*mRZ+*A$WVZxCd-DQPF!CW&bzXFOQo#soG+p9e9nN|;vL8pK&jx{RG+1284y-O ztE4#e0MxI zsg_{mkAG!hnvL$`B$l@=%|q{63QA*}-|rjAmn$qwcMD4=ezN;=5vT9M2`(myjKfH} zMepI^L#3Y^sZn2-n#dmXaP}tHN&0lD9Fsz`1vQwl#O)fE^_D;@9P+z#I~T7OCzl?_e{mozOs&O0qnOy%p0%iW7}^Rm-oOS5OzrSVu> zIIJz=1!m1sBC&qmEJf(;Zph%|AX?U-uQFI|7Y-lF3HLB-KZ_eaG%4J}!-qbK_3FLL z))=F{#sDjgJSHjbhEz>@OAjZvw9wj8Mv8@%ao@Ggydtj;8(ppn8D?%u z&mj00vXQ!yrlw-!W?&;QMYh{2%GZ{_brOswS8u2Zc`f)+`X80afG6xw?T6a~sPZ$k zP-bqmY06dV(xaLCdc6{8+1*xcFJHR}GB(t|d2KBaEvl-@!xjIao#A>bpvt*<_3Db6 zbf{!zuv4?&b5W<4 zArZOTEqa$bh21&5McCfp&oRwHmSNOCMV7kRPEbEDB}nN~*D^Px-7nc~ODBc&GbpK> z+o*o@k}?JQtv$q_&?{DQl{@D-kUAQupWyoyn{7)cp6y-&Z4RWK13lZ9Sgo6I2DvA% zmEM!X%lFKyo>H4uPF!J|zboI;UItT(TXj2DhW5tbKz{W7M$(j(RQ=O5(DNAi%zBC8 zj!S1>M;}8EfuDRi$qWn;I$nU?#PFe67x`en2lCe*(*6cve%c7+D_rL_*moYI3<>t9 zO#~LJW+`4~NU#Qt!eZ4d70AF_?$Ost%m=LXt|GCzS8o| z`Pplyv7GBRaKiP#WjNd2r%P@c)$uQh;PrB{1KKDAIw|8aI3ZTAb3!;p)JsO7-Y$|` zXl?qY3_&q2fm{|RsNq9*e85gjj9UD% zZ1`+(B5y+7!!&~_k&c>{76ZO6YuSf6O z`7)#Sjmn!E^ioiUfR%qo`{dy=w*lch+S7-3v^!BVJiTJ90c-bvjo_ifh8Sl}runhq zrETWW8+@V3VLgs<@2}05!+IQ}-(Ne6wZwzA4IetYhgtfj{%}m{X3}hRS+?A>eEzC} zBF|42%(qu;0kAQa3;W18tF5f5C@E(X?+5e#O8(-d*A>iPwro|gr(p5?<;(t65&yJ| zrSq4~U%u+P8h1dS8hWwx%Ys)9k4LA0BELFN)>5tl+L+lq+$H@ zmk8q(lly;*``-}K{tVKj5Q3c7afM;-yUcF_Czz$)43u4#dLuacWgKZQM;feP;M}+1 z?xKG8IX4zH>O6V%4+t$|p{;ZIYMob0?)?MDxV7ZqD;%SFLRvWBT_fd3dVYZ~eD_N| zkBhHO@#*z0=R#cnV$|zJy#-vT{mg5MIwsCG!tdrW+amEa!tbv84!>Ifhr!BaWSU{W z?K0_lYW`epqtgwJNcQqg8PMy{sfPdUrAu{b#a!@~JTX{l;p!aAH>DPwAe@-2 z^~`7fA2b8joz$7ojzt;=($wPQENDU}vAgmC77obUiR;o7tY$UM*3p^IWSSOx$83~afltj=P*CiZ5Gn}5}JQQVMhn1O+xu*DwC$*y&}g0Adpmpz!C zy~*tiW@gu06SHbRmIiLx$LG3j!#LXBI%bUH>x z71K)8AP>R5)=4-v0ycak%36gm&-sfHVay@r{aoC`{w!Gp1hkg~uBRq)_6hwWc| z;V~GmIQd}(Y`8Vi#!B~SOXiJ~|INjTC_$n3A-?ujhK4l@Uva~&0oa)RV4eOEaKcf; z7#ay`a1rKBgLO6LRibSX-jLllM91j&%@Y>k%bO-VMA;lVrOVIR+nQtv(pL|eV%={t zVxKLC<<)9ZO@mmQ-+QqWt%<$0&BRKsx0ahNwo-?nL=-8|GZke5PRvo<@n~(C$wc>? zS^C6V_oIxP^)ebz$_p@-Bx}aEZX7hV=cG9$@>G3&nxrWYi zGI~bR%!E8~);)9XnS0MX!8}vyD-j^)NPfvGrFRjoF~2~npS4SM*D7=H&F#B#>|F=B zUAf$#eX@=0%V*=@z{~@AsX=*RYVekYjq0Z#rUnl#Vkln*w3gS;Z-(i)G7l`{EW+h- zGB+b$7H5mleBkErl|p`kY?vwBX~;8FE}wJ#+-371&JDsc0dp+ncmwA+zMH$5Fd25e zbw~RjL0$c97+&kZ^OKI#@jlr^jY{ka@wjiiQHfrzb0sJTZAjiP`G9c6Bnr^I3Q>)h zfEdo~=Eu;LonL$de~i$XyO!P(9)Zt&X9Pa?Pe$O-l@TcEk^-W_>l;=tnLY^m@{wWY z&8q}^9HZFx0gk=40!T@_wWQ?P9)- zjM3Mkpdi}f=yz8JDW3$N_((2n?v5Q$cvUCFI&Yj1cNO@ zijWyz;{e@d4EhjxUS%yV>r-{>T+I4?ZSLRmu*rgw- z*pcgpbl-Q6Wr$}qT(v{O>=rfXekN|ouBHm$=c=JYR(1#cR~U}M0%0Nz`K7N@3m1{# zk`I7@o5<-wIqM;_OGnA6HnCPfRu+`=cu*{*WQ&>A&sumn$wH+{0xl%*0hDOS zb3*;ttTsVLDfJkH!|)L($ujKe9FSYH7r!^@s9|O2)?qVE={Q2hP$$Nc3!})5(bA%d zJ0vP80^WJ`F4bg|(=q>9ANbSj(n`sF=2r0D9h$e}X;KgrihV(;mg#{gt0Flg~33i6Xm?cB{0IA@Z1Rx7$FOFMe>7*^{xad|>09R`?lNg$J6`)i` z3l(%MAwHDDs4`#2Pssd^CrBstc05IJ`jn1R_!W|y)z7|xCh0Dg|Hd>{+OjmN6bNH1aE3*| za6R8!v)KWQ3UK_=fm9!&FPy;aAUHEZcRh|!Xw2gD_}(ZE+K7zyYE*09=Q<~I@P0FN zX%wN*1BxL!c1(y0_zbpD;~;egU1|#1F^tw~Qzcn|=B0vB`&C@sP!)0nhi=2~L;C$1 za5BpAwAsw|;%C;R7!soFG(H`D-jnU{kZBGt=72z*ULp&ykQZfRfW!#4hmMk@q|z-> zY>eL_&o9mClrWU9e2~DEU9uShY+L-M1mxj^w za$+2`A~G%W=ufCdxxu@xnSgJ^&bME`r>HYb(P(wRY?LjKGC_(eN6O&k&ou0fIKvM< zgGJ>cJxiUSlUERFudOF`5U?X80_(?8&39n*Y7!(W^8gmaT&?~Mqxj7N_!$Td_C~%m zw$z@@(tXwfTH7ya{NO?4zR6}71YM+;%*$;wqJ-&C#zNK-r$uilJauMNE0rP3-X6o| z;?mAG@@30snuK*#@{iIU>N+#ZP{P}8q~%de*7WSjHu6c!mzsnxa1P_2wYNZSQh)N% z8ETak^}0A!C-l!yL8>5(2Ne$eZ zoS+3-><-t|UW%yYH64| z0)`P1B2*Z1M@Sbx1>ZTux|=g{)ort{R>txTFa$4IzwLXKA^+9V{^W|cqfONnWouWj ztte+z5$M~%BXSb`RmWlykC8RrIMEW)xxqRt^B7r@+^AYGwoFFYs;V`n1oXXGHdV#~ zRU%Sm+T8qJBSx1DV@9UcX5725CvI>qo|typMiKtQo`}gooswr!YNksDRK3x^=X3rUqqQl z^szcWo7Me!%jxh~eeav$VT+9){3WggqwgcSAM-cmqN!QXOwE?wbKN%s{g8z_G2TXB z9H2=UZ%MJF{U1Ou#Lf+g#kuad@1zlsMp32MMi$$CfuzZC+Nj%?oU&M3F*lo4ZWKdU zf|Zn&s3yg_Yz;Zr?d%0lWx9Dk{7EQVY;cZA(sCtdP>6 zK5w|b0`Nyp4abU*Dr-tG0d#?c*yTL)vcV+E#*(F4{Ozrj>azhJ$T1t1VK#iO&xS(G z1_}LJ62+mGbKS+r*F0Rn6+6%@oL(YIkn+H ze-1@$v0`YBtvT>~emzPuD1{oVVt;P1W(Qqzq!OiJ=FvKM2MW!)o4>ZB9!Z(5X%kB( zmQE_mRMfwkMXF5{6?ipLooLm$?mqJ4mhEV_4xj6Og@nHAGJMyGAF^+jX|dQ8^&faA zW@*Uo`do!KWCowk8lZo~^EgX2-4-_m`~G?c`-Ok&F`MV$3M2Y8YuC>q73)h%fsc6A zx#s;p%zr(NUKaR-NN^clhny83!jsACDS)kOF88Onx2uU013f-7s! z_fuaz{k5=(L=LuWV66!?I$V*yhFq(*Covoo}b+bha-d5h-;-@@yP!5Lgtyu`Co z&&M#=S9Tu!nj2IKCXh*wSL*tMom7%L9-}^>mz)b|jALDCc_V4XGG5%2c#b<94(^GAuH^j$+ZD z(bGviT^8u85iT>nS^e{OD?nQ+eyeT83xKL;}6DBO`Df*uk4nUIxR zSd8w2Rq(r(=vqj{;*d%2v2i%X!6FdPQFw+OC!WXQnH#d`&rv=Z*p8mAgcwGFAG<(w zPJl@fv=w5kEf9NxJ|AbroU2%6#Mg=!8#;;#p%9O zh9w;FT*OV>-jqs%u16aGBRx`3GteXzkSbx_gFiJu7c^oWFtNSN8&myhZfbp>Va-6F z!DwH^YlN%IiIAIeTV4Jc4M7upb1`d<Y zw?C~uzb-gCuR3$%6sFG&6i~tu4%kP{>O(KoWa4zOpcVU}aJt@IHmyFVDR)nLRjT

zMnGvSo1pG3w>#{kD%2Sw6^&)pT@LfkNh-Z9S{9j@JxyS zw2&03+~hOwwN{$<#`UsVIMj0KH$cgP)~&%mwPz&kz5h*zf2%LS3!ud8cV7ule5oRp zY0HXxlY5y)7EYWCRLXQ|8r2)#sYq2R3#pKrRqdtFTZG~kyi@NQWBXx_+3(1yEWIo- zFIdOEmt)F1YiuofHBO+@AVstKcz1b~uB+ zN7Icd%YB=ijyurnc{@|89n*Y!93gL`cCQ3~qaIDTu9Pz5n5-}AQc4T!rum&EUjKB- z7B{&XHk7b`{N1J$L*b@rK+pHq!qAGHphv9NMBRE#g!LM{f}P0Z8mtSzZ81RyeKKd% zc4gJ$dFv>nlBxwT|0b85Jy}IyU7_8RXMqeYN{e%324M_d?@#4%;IcG zEyL|frV%*pOY*|+v~c0~TDZ$>R9t!+|E#BKv!({m=fRFX4r`S%N>lWmft~mQ>>O9j zS~Ps5>)6r+n^lg(N^G&E%cJ16?iRA$YEV8u2ag@fWNmDzsC=#+ zK1yzSEXr=n2uMkzOUEI`&SLyHt0+U~SRUC*20vOmQf{U7ShPugEUNGXM5K^HTV>Xg z`QT7LH9XoJX&Y^3F~_2%@vKBp;WOjRZpg`)ZOdrza+}l}WME$F(&JLfbn@L=NFp`| z)+^63V)z?rkAR)?J+g~r@;oodZPVIdUoc2?9}faiZ-GC{5!u*^|(^Kf>fw~W&1Pj>z?jd(OQ)T!cMi~4* ztRn4u;U|GvVC;v~j@81fKuq`=lQ~{5;~Z+?l>u;rC^VZ3dYSD9o9m2AunP69B`Rcf zlQ}_8eIBXraS2MlCe=?qqcX4VF+6;THmVB;cf#6%8x+P&0E!xqUty5o_j>O#uf??7 zJI!S1>=e|zeKY5q_|D>g9O;$j-JQShn*Fmm=B?1~2e;RMSL>`STai2kwgE)BNAt50 znx|6EKjGQ4CofvC%5aTp!XWnFILJS{?1T)B!=2O3=my9Zl{ioZ=yy;^jX*Z4gg>@# z^L=rix8=#zbQgMN{VMr8b{F5<$9`mc5t0*pFHLw)T=S#7j(pfJ3521EBmwm)lCFz& z&;b2dZdPj|fD??hw4>u1BFPQGOsiD}Cqzlq#KG7#6G%fqOv_oX+qHL2Awk5)ms*;n zmsm={|FK3b4vSawzQoGdtCmcO0+%D85zN$Pbp?2H7VSN-qM}F?Ec3X*|3Zw#)G2|d zW-9B*B2`@~A6@cns)+STi!%CwOQLz!FkSjtMxr>iB{&m(jWZZ0kE1QPf7-jGQ{|aG zgP_GWe}0B*ntkT^XqJa738Mev-o4dwuDgYxB+c-69x>WZr^c6_o{N&WOPOUp)R z4d{0Y>hAn+lyt_cZg6g-3qaAA6X3JuayVBj>F2tONWF`VY}i$h>KR6xlD^Ts(_KNf zECN>j#zZ_QqAlH)un40zx;kV;yv?Hj#+- zC;o!~<1P^#hLxRUI04$bj589a8EmndkY>@usXNtMUzf03lYkK?@d-Vng7!d+JPNaN z*}K;k6qCJHLx+ze@*pfhVt>wqs~~{;Q9+E3AbU4d$x z00=kL@DVT#B4nwg1S!qXxMrz$BDEW-Nva*G)4_8D-{Y8qRbeW4AVwg{w2{xiDS4Qh zlrdVy;|lR;#S7T(pkRG1K4%K5REkq+DQK$ULsLGzG*!oKm;DGSCmrDUq+*P+GMiNi z?>Sp*b(!R&z1*>k;M3{qL=J7>QNIDTE2^Kvn9)qZUly@5&_vYc0Iwqut}XdoWLyCC zJc^nwUsAz!ImW3I*)zKvWn&?~;0{I~%sDvgVVn&f4rUgmqpXAZYj6Lo(TtBry zis1n|Nr9Kyoz=tU?0N?fHGIKThM1e;C?$cJj6(*Td|6Qojbm-Pot9i3rodl6%Xn5h zg@7<$+Ajpqnlg!T_t|N1?TQ51rE~LD`X<(ftIuuSXVJ*q&f2Q0RE91X|E7T9?Gxm} zbEk`JUTwup?+Jx~9^rN+bAxZ>ItUk#Vtm@lnLdPrf(|E>PV-S{ zbtrdPymp4O#WjE4%Qxlr z3i1oR!cD+A;8nCi&=Q(2_Xq;h(l`kXzqOTCcIG^@?fz*_@FxRQynr6sfO0gmmxK-y;v z>QMiGyDtB$b@(Ae6kVA#miC(_(rcuA8Y$f@O_UZ&$US!P=dO5}@{zi2?5*s5Wkza=iReb?5` zB3q<*>_bfU6VixZeX{^gHvR29y;Y`1xt6N6Q1 z5uTx2gy$DvGY$z>fYCg_FXOmi^+Djt`?7G&LQcV^rrUAN3fhaBP0W7~CDGOfN2K3a zoD~#kR*-=6^jJo{IVB+MT@Nn`z|7#hgh8>z+5tpC*V8$+#5 zyZP>^iB%@+qf_ImAD!1a^{jShOzXT2r!;aAeZyL_k6cWP*0syON$2DTro2L9gE>V8 z`I|$=uO!MWX$C06u4f_N`~#Zt2MsbWjYhwGbExM_*#oN`%v#AfU-M*uivEdf(X+Vo zF1}^a{_zb~+Qa`mCRy@$kP54{S@!lUYJ}a;d_sF-jby)vFbv5a8Y=(ZFD1&JKbtuc zGUFdnbOLz$JWwnRa;$EZ^Ua~B{$bZ*jTC>Z%uwE+eR(mDlT(9lslBkdX;a^(-yC}A zD(TjIX%7yl2E4~N=>}=POX-+PI04)p+kp3C9w0}236vII%)6A9P`))U#XUboi1?tAJqs z1T9M|Bl878X79M)l^_-S42Ao1_PKfQzFbzv30E#WT%E{y)V+5;Kkj;1>+Pqj9Yzno;0qu{X& zJ%hH3Lq)jqvO#9r<_74mqgTKIdza|63wR(KAQ!OSxUwQ@>idM6iHcb+)@Htl$0d;~{NuaYP$@xf0d}pKwvo5#9_slAEAw zlXKuN4IhfVsGl<^r`)a)U6(wB_y~C<-O3^btK?+EFvbR5YN5Ucr_SBNb znQxpa+4fBofg;2!?`cb&p3qps$O!c)>&*jsu5dqJ9}W#7p2Z~U_5y96M9@fEsMWmf zQF=_TpZ9-)RyC)l5ge z>&O>1EANBWOB`lB@i5P{)7_9)U1jWC(aZk-(^3B(AK*$E|93rgrq}I}nO=864b!+u zr2{8@alJS>KKxcH8>6~j_hEyi^CO$ouWvlt9nkqz`>0WFRDXYJ;k!H`0#OeE6J?>U z&7ES^ufRsxM-*^tGQn-yMxF(yhqT{87KV9JZ{ZHS5%8pTkOpwA7*FbJq+&d&FT?p7 ztS{*NA;$4BYm5u^0^-fE%WKW#6t)u`A^S}soe=i)-=x90FIDj0Tcm_{sy4C{`@PUh z+J8>`uKnp;UGtK9U0-@NZFQO?~Gq3pk1!u#UiGk4!)0NUq0iau@;MZi`g5A;EKsK3A1F|jimio@Xm*o z1#LP?X3C^J2=d&cbHhS!>-oXkp4Zy&PSdpTn}SlTbH+j6wimvw`ta9p3BcPiJFV0~ z6MAfq!gOtVcBaqQHKF6iHKc+1I3xH+4KRdJ_J*~@59*!q)Vlsm+Dpj-v|<$z{QuE& zw`mZ%JEH#!%mD||g;KTrnfO0MUtOzzml5t;AKpw*?$+=&^`p(Wr) z)*Dnu5T3{)Xk*Y+B*;ea&W_-Yw>8qcO&UFFZc^)RIE;Ld$`yV}R zH1uZ^a+79^o@vPoA#oO)osgs?)7Dgz%;@;m862pHROCvN<*AkP7=MXO96pqBxv=ze z=vt`Y#HEe_=M^pN!>GOgPfi_kSi&h8vLT#@i43*+U0_w73^Qk{c)fj4^lxg;RA+SrxxP#KnnAFa-? zG^NyTM1lI7F5-PNc4klie-G%&pNxeUKnyg&$fN$D4X$A0O~z2gas`H9j`Y&$0i z?0$#A=6aFZ_M2Rf?jYw+D8o2U*m9!phA*^*L{B}J^b1CYY{$wm7{$s@QI1{%$}t#s zP7uT8xPhJ9W^z9|ykqbLQK;rkn;)r~-Dm@AjnFTqk#H#_EmVpggHpt_@0F6a-;`_i zX+B$iT|`DCk8lULB_Jef_&fU#Yt&+G7KI;?u7LPH@ajF zB{)#RCA|dS=_N@C>q+|;Slvafw~Dm36T|MZUW}K#`DCrB@m5h*dKOrioY^$E9;NJD zZyWuTNw|$W+_0COSaoO~fA=q{vl#;~Rx;IT{*~laY_L zeyw@FcGsG&yg;uwPc%i?)|#lc^*YgUl=Wk$m>p}|xwtDo!aN=^#=$84?a5Ix&weF; zsNT@Kccn+j%qF`l?}?1!8$?qnatTTT-l)|YCzHX~&e};_bg5in!uuQX{!>3j8!9jo zenvd#rP%cbe@t(Mi687`DQ|Sh255CUk?td;tFjfKZFV`=aYJoxwqfuQq^U5)-^imc zQ;>%B!_AN#igH49xryOuhguc-K3uvf16;Ae-N@yT!|#yB$}KmC(sAjbl%@=}+8F~} znj3#x3)h+=2X98MD0zWwSQjlvW5@YhZ}gg{yh}=dVVa1Z_}u>~da?ky40(@k=K_Yb zxb^-#iWPeBCFgbFDc+ra;iUob(IBwnTeJ10l*f4L;J8HJa(e ztpgm9KD-_4l|uJgnpD@<3pKJJ^YI1}3E1v4;5bpzOCQ7j_Md4)`H9B(U>0z_7$2;c zGR|+4dOc!1+8OX~n^fUz=HH`9C0`e4F)xjx$AcE@FM_dFg}#hX?i&I+MWLd#?)dO9 z@Bi#Q#wCJ(JGm3B^=L8Io%P1!7 zh!Xsr;P3z9@bGm{#*G1X)g?9!IYkjvUxIXP-FJOu3!E2 zBN3Eb_(bTM*Pu#!8G=1LSv=C9Zd8p3Cxqhp%a&01oL%6m;rZj16Zrl5$p*E5XDH_2 zBawQ{V2h-emW$H<+G3>^j`yce3lo zNBj;0RZMj$4XXEfH>BF>ly`<&o=mTyX{JSbzDJN>LHZ}qlE0w6@#=Lwyg0K$M z%i_=OWUa#Ip4*_dwJ>iUhn{5bKIf58JDS4n_`|OPdOLpI(&~hFl}B393e~g^tr&jJ zZ4jn9X~1Bi=ek|!&2oagIPpkWJ($f9laE=kAH3WtQ@f#d^ZJUKDdp9h(Ag!`)m7V8 zm2ZO%-rBNNS2Sx^tzKJIG0Xm~=9`|*9!~eIQfyeW_TQDWY;##z#imWGH&-#uHB<=| z*?(Rbvtby6k->EIRp=_bCYz|qAB8u8zyl+lal)!Pjg`#5E<2UpI|CYy#@#q6pV8wR z)&JBe%M!W%Cf4xK{P}OIeS(tp=KH|QFVOv8|I)$M8SO=-*q_+NgTZ}Wg3Y-is~KS)+g`8WBNRIFc9 zTgjXqvOM<9>r1vkzmbVFQRTnM`)!@f=9(#8g}f!MuGw(gNUQ(vWsEq?`c65p1gota zafrp*!|W4MVWA+S?>~5!r|hi9yoL@R+Wigd=|=U>XCR;N9*u9LkWy-d9PU5=r<$g) z^ZLK+zw+wU%+=SbP_MBzz$YX;bz@|%$2hg$UWqTkP!TH1(GF&tc5jlE{y);*1U!l= z>l?qdbai(&It!$cr8`MyVaZB>uva=Ega8s22V7=Sodp7f>}<+X-APCcpoQowGvh$h zfTqPIY{q5At|X}FIEspnj?PR11cJ*XDk?fkzTd4*1m}I<@Bcl|pXVW6b?e@9_j}Jh z_nhCME9(@D%&}Noo0ZhhD0k}uCP^_Q1rx|`VG-uHXK{m1h!DHrp;c`fepZIUepr|e z{{zY8if^kfS3G_dMAdG*Im(2Nf2If>ze!nIw$m(uo*lK)y-#N<^@|Y**%O@&8KPbi zBn|VXM@3?IX;c^kBXjBqNr0`*ZsneB)E{Rcd}^-Pb^&+CQPMuKUfiuL-xh$o9(p@W zxm=gxsK#9>be(BPD?(mG*a{j3j-2CZoNAVt!Ut)dWXi`Usk*m-B@x#9Qf_5`a%0V-~f>dNKvx~<%zA{@HE+5Merb4fHh7XsN`%%d2;hm-t>`@8dp5chl60og&cYl!=w&x0*f8I7~F<93wc za%{J4C-&M8Q(A$#Uz>lwXXz4(y1(eO$lQv%e|~bSRJif5XqBe?^MDpPo&z_Y?AfB)^Vow=)#A3^_2)w||Mfzu1Kl60u&e45jSL z+O3>icXpx-+;}?b&7vR?Pqru?FNW1>{OR4${1K0r5Wf~%{AoV4dOYhib^#ht(%A(zj?ZwYt#dHgLRw^50`<+ z7(#%y&a1LgluIsu9!lRR_Il@A{(p+9j-PL|RDyOHp&SpiE88S~WM~wpOPZ@u*<)so$pk)1&w@|M z&L$}q(wmRpiI%w@H-qe;(A`38AUF1 z;Iw4W$%cH>u77uu-rE=4eun^kh9~ijSz$8VMfjv_Eob+bP;<7H$M4O$Q!-g*IDG6L zW@(h?*sqx-(T-!kVS+3y!lMj_5FQhC7~!_)BM28;?_vCHIMlYhSs2SWjcDCkB3Lpt zs`r{{D<8oqAAyx&w=#c6x9?%jzmqg7)1Ml|t5D`M=`q5EJtMl7@^KI z!TIn)9VBSISFz<==viO@j&ofc;_6AHSI zXt6RF{<4Dk8z4b0iMx(K=BP*x7(wNz7T1KQXSJ9yOartPX-S)iO)F^w(X|3Ecv1l5 z5+)#T;Lb#ovc{lHoV*!3dM3`^R5lYJ0gvY$iuuV(th`Qj#!wrx*a-nr12ho7(qzuX zKd0Uu%*l7Eat!YBIuvod1lzLh7=ke#}010){=oXVkhN3gk z##F{#XSSh@dHGe9^T<~8ou}SfH>nZfX3UCf0uIE~xuCT&X4JHJ(F zG^2xg|0x(ILvuV(o8v*4<4wdkILGU;I&B?XPuanBv0;qf>|Y1DR?z}|_ExfuaTyju zyZ&F8*+30(jixJuUr&IQMh*{GiQXlI;f{Q_GG!YBeE>+BH7S%JQJs>k=R}e+u127D zyi}iTw{m_f_RRz7JnIQkfbV7C64yK@hw|E8exnlf-=lO|(@mN?&J#T$B8k(HF( zde7$yFOLV>_a`LtxzDjLEdC0Eoc6v#-wl$i%*dzcSTqLXuxoKr&L@D5?(&DU{HS}d zKX2^2SRI8H(98#3Gz;ub%DGco3{Wy<<&XI~DHfsrwwk?l-c>3Y48d->cM^BG64W@f zD!E*Xr^2u*v4{j~BWA?_hy185H~2SgZjR-k;}H6e*dMzN_{7&@0wg`ozYJm-pz~V* zt|vngB=NbKjLsR_k@+0m2j7@WJX;mwv_KvL-V9*_2VeXl(n2NGR?OF&w+S@m1`cNa z==Ln#>wkXxhSd|)IoR^9 zji!x8oPmR*etg$P=Swu=z8g#H{hp25ZMy6O2aiKjfi%T=3{8)FmhD9gH32!S>3Fh9 zBHMR^>g4wSR+C&l2)vb+c##dlrB^1yd3VgpdV zLn5qASsrCt_?$|3t(~7>&LzH+#G`IH)Qz7zeD2&u^!^7Z%+PtEZ+@!78Oh(e)*HP7C*Ba!#IJYcih^8aO=z$^SF`p-hr0*0@lD+e`@b6(AS}?t3SP2tBF^6(MHDu#$A@i z#ehH3M$?`D2Felmrg}4aP~a^1YvvJ?V!|)aj!3=wCytU|k1|rN5&H%r$7C53b&9q2KrF z#mOF9Q>c{WNoZoWMR_Ko+!Z7NVN|atp~)XtM_W-Mm=#0g|Hi=+Xx-t1V@CKX1aUc0 zKsq{ZL~#jnD(j8FDs6z|Ct_3mm`BVz^Y)Dl!*0xOqhs!&tEG9JYRD5EsxYq;NW3== zF7LG>|77f4!?!_?wQ0Nsh}lh?9Iu{8c8G^VsrQ4OO8gY6i%g#x;6S}IrFG63>4;zt zeHr72p))R76($K0v4s0O(xa+NM?}uDD zQ~%|(j`l1)>s+hi!cJbTs&TUN(%cY{)_Z8SgyeN;^Pkor_3F)8)9@pm3#{0Q7WdDZ zFRpwQFNlI*7Qap?%rgrr=E=jDH3o|g^yqxC31a4CVCIW<5wm6Z6LiLO``%^Kp#w_g zT$j*qe|mhb!;fmyBYY*COycy{;=f9GB&s-XU$iTyIHv8--u&!Kk+YF615De_8R|AN!RJw~BOZ$vSNpS+1612hmGZKOjbZ-=>qT zY9sipT-e)>R9A&Mo;w>NiT<78`k(#Uqa{bwI0wp;csokJQn)T!cYeYn$tC2k z)Bif-uj%iIax5!7k`xT>mtZ+W_3xJ7Zmz++4?j40-1`}`rtMY^ZkEK6;kV+r-sF{V zsa#$Q;Z`!_TZ1aggL*8t#3^&smXsH5DX4W+Q&}RS#%}lpmg>#8|AVqrEcJ6w6{Y<8 zF*oDV3+;kpK*U;WAZaWw0w4nrVyA0Jt%GHm9$O4~p=JkqOSh>BNL+&sHrz*7+;&K~4EOd8x`=?hhup3^ck-iJ?gm8n*j9!eQ^v#<}ur z2-X~p2RicxJ!K9YT=oO6rC`O=HoDG;iJ3{KV)OIzb24)%Pjcg6@8z3o`_nzVNU3B( zj_{txPa*9G>*WvOJql-Q!d^ayCl&K_*Abi+rnK5l#>!LcG8(7Rc41Lov=yP>?>j=z z0;!ucFSP%?KWlD*p`ADgKuky0|J3(>MG8Nx?f+NYoAZQ<$YE-nSX}{W@Zbp61%JM zh6#!u*}ISF)Z!`d-i6%~l8H%dEvQgQ0NHz*w4b7~i4&lr$r_Y%Oa!QC(h21-(r8kS zy(8HwoNFmP3v*VZHyYoLYSM>~A*smqOD*>5K%3?VODj!Mp)z~H&%TVfe)+^VOeLfu zluq0rofu|=Cb6hD)A~>=-J#R-8I+92tjU6p=rFZ9hD9${jw85@s!SX>*z*mM1+~gH zH;$;IFy8-_WvXpiKn1_Ca6?>Xyw3GQ zwtccus*1~mtl)T6}&%=H%{E38XTPb>I?ks(fl9X;e;}B?p=rzf?ie zqzb>=Ktn|Mp^H!}Z371%LHUNM)i(R=04V^c8wPrf@Vkdw!D<^Tf2UGesf;w048>zD zCfKVXF`#X0kUvyqNJE1$+DeHZZL?7+I!h(0R0FO)32h4rmz$u5+9}}ogt?zbi5}Ik zMQN;uNL2#8pUb&Uv>45#n%V+FcGN&>;_D#yJ_j!HLo;XQoLg_AeD1gbl+W5*@}(G$ zGtUxFtthT#HH=RyezSq`X&S`%jK()eHpKe1yoU1$KL0}=tC$U8wLt4UCsxK=J?BL# zK{7}NlxD|)(gA1+(=+K{{ivMg>(_{Ls_Gh)5*#Hdy@FT9z8 zmaOkJa^SU}sm>axH&89Zn{gVIv!EKJIw2dPlGmN)4KcL;b@%`Etfa}4V7hqVS;9%T zVU3_An*wyq(o=*nLcUmKi}D1Gtc|q+DYOz6RuLnmQ$LcO%CQCV)MoG~&BQ-){gM?r z>ismnzfm1yl%gRMA_q1Tav8HU#V8S!Oojd*PVZ_iJS#7GliKF0r%FRMS?JsX&!&xl zS1{`Rx_$58s0E#Bo6RF9M0@HJ$bluKCK{9^ofN+IA&~`V6;0)WDb~yVkhsTEEU=W} zl5Sfp)dWtNW&!>tl?SD;@Z8Ko_X#opGtM`e6D|L3%xSfM>_!!GWSk?~F=K=(n;Z5S zic}qYZmU(OlxpJ6ZB2ot(tf5kenY~1VW)B~g`BQr=mEsi+Le zRO=RBfF;e4zTQnfH&t41Ces8-Lp#6UTwNd-scFl+rvq|WJKg@D8>Uvds{SWqZL-dR}%g;=6ZDaVbflHs3a@;x#K(;)5d$u(;#7)M$b%L zr(}jPuu)tzAEUF8SR(j))Zd@9H|L{%^o;dcvS4YYC^Sm*j2ET=PZ0Z@pRYN0H*M1i zHLz6;AIITR)=>uQ=m1d%iLu`^%h6PWbR=z*bZAnL)RGQtwr!EHQ>44aX>-eT4$@r_ zI**AGH{FjG%FoC?EfZ!Y;|QK*n5~~9p8kZ)Gv=h))7TylL{Yhu=A|#1+^nc?K!%k+ z7bu}C(+D&uS58_vb)u(u$v98xk_n!&B`q^rX12_FdbWFNxMdPJrxbE{dM0vencmkBs2sK0~CE>s<=S>&ESa3`FX_Q;D!;G zgApe{jz`DU{e76ly?<~%|qlIE$y@XRe+#+0ucPD`{H7^!OPigZD?t=HRhF z=jq_l3p+#42%iX71U;p|hYa2z{mx9akavc*4}PQjG`;076lo5-)T@G9dmA`-@ZXSY zN>t3Vj}cX4xtle|3q+zSA@pUYMK(O?2ZFv12nZmtQH+ui%3&Irwng{ zJmwW|0T*RSmE_KxVZc%pNonTQ)WT{9*iS<5-gKf#tnaiZbA6wr@&tO%ZXQ}C{~WNZ zyhZY`rhLrBZ>7oq7D&ovp()DcbqJQ9&RvxhN@*glfRlPF*e(mSTxM4{9miCPZ7 zH3#f5s{zd;Uf1`12-^2|+E-MHf|gIS5;v#CZJg=`^;a1I>j1nl&XFJohdzIw85H$> zEAbZ|coeeT*h|DNsAlFuw~bP2R`hd-CtKttX$t2N#iFTor0tEWnq&hF8`9g5+J>|?q^wV?$IU_g z}2vZ6?VsNSZf+8(<6+jmXf$wq%Vw*28H4TN~2_4ET|NY zj|QFM3H50u6B{NKCc4s#ljPLig?Ivmu`95ztg`R#wh7piFiTn^L$vy4)3vTqIcIfm$?2 zuB1E|b)9OqnW3{0FHR8yO9KXbob0V6hCSt~p}Fi$qgD?E)KX^$2+)t5cNF|g!1Ack z4MYeu4ZkfRanf}Ae5`D=hwaf3RHqhCOJjyv&=z<~vjtcNzsBltze>Z{I94z?lmD!j zIknm-$m!}@^aT0^-@@Xnky0l6x2Hw3_fcmNzmdHnC`6B6Vxji+_Hs&Fz)2?a-pq0btW>EL(bZY4z;cZRd&6TE@z@nr1OCIx zK{W@Mdwh^ANpp?EC6h7Mm=a8HRvguZYk=U0S?2Fm25aZT*uU@ZOp%<@z`+$aB#EAR zm))RavGxsjs%rri)F2H{CJ^0nXbh)?6i-Kf-lqk%O}dsWcd+ALCAgL`K& zJum+_Fu?W6ZxG_fc}ZWoTTwR99>twCX1-pM!Cp5j&RLoUkv4w`5JvBU{*SZMuGeWZ zwBF4m&sUjbUf!VgH|bx4cF(ZL+wZuiXe}uv2g*p6ht2i3T1+ZA0J}WA^I9v;g=XvB z#3Vsm5N$ee@Z5luW<{yr9FQiZ;P>o+lrG};%z!jGAHV8=G)2Pi9|xqVt@wQ&7@4Ih zGM-KjG>M*n=HUTucv$Pu$)PYgzL)ZmQs&lKRI z+|jJOIum)O0N15;&VU0?0|)2+L}v`Us1ScCAAQ$=e_i{_OganceDTi~|7?B=v?qY} zxWSncp!EZtFTb5>SGUh&n}S|<5uMIx)ERX;oiMLF-fdW6u(Y>lPn}nuxwUFh6yC2l z32)FgwG9M{H0`4^TcL~h3>V%zaQWk;-;U6Wq43gb5tGDOqI@>=;6gD1=o8UnP2SB! zK5IHSX6*O6b)jc!?)v@F-DTJNHvQn86bv95B zlmN&VCz*CBkKP#ui9VG!?oobqg#I2E_b96l(%$=sqiXnmv$Jpr#8M4HYf4$^*Iv{|2T)< zn*2623FCvqs$!I~u7<9!cD!WPU(TMyt2;Y$hXwTQ$4(fx$enOSEt-QffW%1@(|KNH zv%=1HC)^BaR(_ZjD7h1g+ZPn{s+;EIENqnYsxh~55i3=R{#4CM?<{vheC@5N{yA&Z zuFG!{DVFL&N#93`S{a@(8?>AS?XQf{wQz?T23^A4%A$f|gya=K zFQAv4BHNj%boD%nbC|sE;JEz0UKWu3!bG6-&=<_n?U+jg>qFDsN$`Z0e7QY;Dl~#scajVHemlw>t1?>q$YSKw zvC~Ini~$1Kqtl620-CNBx@{jta%EIYGfGGk4CYKY)FU`~{AzdL0ra$390fcxXFz^YtRtEOBEJ+@7(-+(*DO)24+HM1hiFWj zZjcv1pP#k|&N=Aul<5vzQ55XM7c#Og5TQ0>w zTTv`d0}nn4e8Y4H(!{A!bm91-7^rDas1{Qf+^=kSe3YelA}?dkg3^6IP@T>o)iJSB zbSmXnU=1L#2>E|P#T znW{7_ObWXPoCm(7WrQF+hY)p#&NwEsRUN>+**(}b@NMq2yKsUI1hOKLQiRc>RTwX3 z=%$GC#T6oFiQI19ezaXbPj{aV5>EYEnQ{lBL!=%qSchIUpx@^E+|gMcUIK!BS-aAZeMV=qu8zNG9^Iz}9Wor+Mc(eFl z=QJvB-y4K!)u{aWUJl_uAk74R2Ese<<-x{(Y3NjgDlQmKEpRHIyfqDeGM5mSH=kHd zhoY;pLj~%k`bbQDHZQD+UK_JZdG_l-{vk1vhXj4`o1Fy(tzx^l8Ml>r9|r^V;JY0j zdWW}yo_TH@6hH39?fxU?bGW%YA$B5!{aZr`!OA_;yV_Q{6fM(Q!0mWu0p3Z>%g=?0teE<#$0 zhvP5CuQ|jz$r{t9PC_G|F+wcHl8?#wMVrju$5wi(&~Yhx1L()(YHBIh6A_jEZ3+! zyM>1&Fj&>Y%Am_v$cIYs-LwVV*5U3@#u(Wwbm-;x)hcP3rO}(l>Ybz8yL|OXw{MGJ z;9wh-i^WD*E2x|qTrtWN-8PX;1#JG!@Bp0qGlhR5Pv zKXh=G*p{bEko0yiQWT!Le5XLWWD9GcHppT|lexh=iKUWY+{@y7dRqz`A`$R7SU0Xx zdkUg_MnchV#GAr8mFm9(3k$~BJmk86B~d6#H0s|aPHBr^2M+26Xxjz!^`rG0l{C~` z=(~nzLtiic>ia&&-(gyx8MPkz#`h^3)b~e(2KMnyfy6|yR-B8~VAO@_S^Lu%obDmd z!?+Uv6fH51G@(n37QzHpuuDsXchDDKvR%GveE)GX2Sf^3RPt0(d(FYL4(P+$a&KdM zyPypoB4h3CwLnak63AP=ah%>dIS@RkS>(Ia?fMiuALy3s`~~V+)(%e9o8k>(Syho!yatxhetDMQ_jhK3-f=3- zhm1(PHnfw8jcE&FZEXfNwvES^-(Dr4z|izaYx45!7vdg_-oM#Du)B>P+8xHf+Fiyj z-`^OZ!DKWl&6{>u0r;eE?yVqlP$YU~n^1GzMK zhparK8`~Vv;qTEqzMws~k2Y7`f}K#W=-El-3u$ zUGRko8~)8aXz-Bdw+prod%SKBmtSw}^*sP76zo|yv#{Z|s`UBan*eu=-|sdF+V9t! zbcT85Ioq)(5pla1+ps;Sd~W&FtzSx2_mtdW%Bt(sCv%IkCg0hb&ie1nYjH(gu)d)4sjA3j#4w3f*Jq=)|3)by0NA}yW(C0o0V!p);l&AtfH<7 z1|5Tf9J%EkSK*blNcM}cO>2t(TUcp{f9=)s#|@~VVLz`29>krkEjLKd?Q?iig;p!yWWesWTzeZOH7cvn zicRIZmLnr}mg~?veoVcOh(Q+Aa4E)XV*)Jn{Lf1^JOkUVG#bAX+iXlFmX+~s2~2}B z?QkW|OASi!LE=qdE~<9Dsq6_ey>$_sHdw2`njUG|5yLhZ^ZJ1(SV}c0&$kog>4Xmy zig*x*)C7jtbT6e}gAa3ldcYs1>(B!vOajW^v_KN=wKC(|tSEyMJS+8$(4gFShL)&7 zd7zzIL{^GXl3fay8kFf?l;xb1`hwz3|8NNR(HYi^` z5h(iW+MxJ4NKZPU9L>g!3_Q1N8^#!0(aS5DINW(?<6>}ZYtO5zD>h)~*PaW@9Odia zVH2|SK#^Q&{%OqhX_374n<0wAZ6bH&UWAPki4=r2b(zp;D#mWP_#4I@xH?#5u-BZ! zoX)&Jq~}B;>}<7>NI}=vKf_F{0!#M}&&vD~J7Pz2eO(A$ZqW5z!!PSuo?m275zdJv zcBiuB6D#PQ!&>43HP1KSo8rMqm{AA$C!>Yv*nmj0He3jrzpqL;of$GrJjXN#u6B?Ds7Zp&WCq-FF zf^_*dmE}RnIRZ#$^-;VtOtkS0(K=^Hej3z)uomVP@lJTV5*AkSu6S1F#7If9ITE1o zvQ|z94{LB{;IFlQ8bzLPN5^Xbs`}L>?9bcu-TSIrDg-xMtnV;NO%9>)>Jad@{*q!<$}pNRIKChrGAIC-rw=EO898X&>$piO=*C4<(eI7Q<>TY<%e0+|#P2YQH`C=apbkB?tl=H8bz;4EGRB&B z+GbK^#wfr~Rp^OmHMAcm&RxnIua3~2oIvU1sHUkot)w5giYk$@Yk{859TcyV3s(}X z6OGE7r~Uaoqvfab=a;OACnvxb3mW-I4+8f>X))nsEosqbmaC zsHb(I)#s6(1OFZvXjCTDa)#FNTv@m|q-~*@wj+`5SNOJ|Y|v zPGM#Epe7J|;koOy)aPnoTZz#Qey5<$90G)nz_m*|&#io~{oKk)UP4@9BLxm6bBLEj zimEzru>N|NPX-rsvIge|r%4U-O62#tG~gzGEQEVY$;-M)-VAa>AO~(I;XTb?QBOJa zyz%=QdhVp}AM5VU0smhUVt9E$V9!c~&|PI$4U6yh;w{2kNzSY66X5@9!j2J~!D;&Y zYIs9+d&Hx%?j4<&W6GA#gy>a)rYx`F48PYM(ap#ShorMpw@>8{&dA<(CgkADx?N|& z3T9`2EIe=~Y!fY47|MyV^%=m8JHyb!&>eEMkv=otsg`=b{e;LNze`x5i(j+r{w&_1 z+lBRB(y`lEy>8oZJ=s5yEi8AZ28wQDka#Hi zXiPS4GPvDBzI&2Rbf+<5E%ug?Dh)L_13D&S?Xr;uOw0!0AZ{=koQBRi>f;ijb!Qk$ zjWK;et+58Sr-GUV>M;vyQjK+d{ublIqEN>#+?ZMfPYc*F)Y`O6k?K$JYR#lHapcy$ z(3fxqxkhhE8?m)859jR&*XWItj>hR++*$XO>e%-7g2+sqRnOuk=^XxlXfT-3mR%RX z*AsbZW%cChxcNE8Ixct1lnwDk3%ELNdCkC->=p{o5SnSxm6lwYo#YgfFEDLQh4ZSS0$_^rKhN2I?-QwsWsR55IO>)M1!K0n#_% zNGsG3l;9b=L0+MlBTl5j*&1S)+g?(c?& zJFIuZII~k(@pnjXum=@lOpW;NRbG(CtH)tA5w8XcifOb!@NI&?^gQ?J7&1ekEwb+P zmxda#PTNnY9N?U&lhW-Jd&F)y5?Z*x4q2gHU^Sa5(En?fap-jI?T5?+J-kB?UtK7P z1tk$M*N6WdJEufX;`{q`r9H;IhP=YXIE6kik4Sg6*5mFUIz1aJ))W_Pa#T~-={58W zR)VFHRZTotlm&rn@X6L^w2{b11SOD(rW_R(6Cs9&^1I>+C~wKr@tJ7bdyNi zQzq-Bl&M0W`<(z%NJ|tMX=a15whT)Jm?jWz)-#8_cLlbGA4Kw%S zwi&XUPiWrm2~I`#?lUu}uavIakIgJL9Gk!+&D|srV^8+*DNg17^MT^A8G%6MI|AMP zK-C>fPv)LDx^5f=j|1NRlmZ)H#l-aVzCH%y4Lyl|-gK%}8yQX|^gNZ~xuMricm2P- zGv!Lu1|4=7`?!*8~$%Nq$#@97T56%m(ak!Mhk#21E=QW{!J z^3|Ni#f2kWpH?iDLQ!fDD8xy_@Xh2;{~%3oYNY8KV4tP~FE^Cl)L2ZGs5uWVF8p@) zr(la1jNQ=0F5h#cQ5j!BcQl>ag_-j2BFKsmX04$7Rgfy<)Yk#Lf{F(r*_5bk^PEwT z0-I>R-Zx-CDe63Ck&{C(CgJVl?aV0DV;(7`?M36P87M`G`zm%EC?1%fU9c=WlMwow zweY6Arm~`>V3nhsWMwXzyEL;Pg~lq#pMN`@zBj|N77m52s;Dk1DJUs{t;2dacyOSf zZpNFRmQ|3ycCs6!*wozQ=+YsZn`&qh6%9Aq?b} ztYjB3W`gS#<#;egN5NCpmLsE>w$@QPj1THhv;=F_=cB^=`1lA&K!&MdXp!)P(FlXw zqL69hp_>tY5a`y>dXRC9tqp>A38(%G*~4UzwR%+~ywv5p5o`m$RZ&h`9b*dX9CR=R z((~0(o|t;-|2Qz&-0sQoL0x&6C#GdhI&fdA4w4UwjI7edG(4IYKd=Cfl75JPQvZL zU4(}$C8~gfw4xhtyOmJ|{(P3U#6pZ^hMT0mswFVZ>e@|j|jS=VPe#KN!PcHJF9mJrReYvp!` zy3LSP36m|AK}3#K{keCqr)@)X@3;#XTUzdaLhi+bxxaK5ZG&jk&0mU}CAb?U7-peg zcPmHkVuEld1yq!SNSlkayOjfX1!7IChTmgKecxRak%i)>Jb)Oh2C479D-fukq8j!@ zyBhYOOG#XUJ7GBjSVYeUv64$UxxmWPGd1fiWx9Qz1SsGNzui8eL)5dDz|uhHWK~v{ zO2nCZMpn}Bo7h3Fg!GKLp49dj;jg4$9CJMe`hQlhLi&6{fDM{~f|TUlO?rK20%N=n zllZpX*WjW1h#IF7`fzC1N672c6sU|U#Z&t*| z^7%~OrM#anh!huw%28a(wZ&9s=YQV0OhU-s{6Nf2stF#9J&^Rg3Z0vJWkhQT8)4hUpE75M0)*tbG;UJCY?#A7Wv0gd%EKAE08DOTK3G`#S zebW(}lLih3elV`aK`t3(@o6Z|nBGIr#kbHrh9VSQ(fW-a_rT{bOXmfsT<&gI^=m z%f9hYJZn_e!>JgNgS7L<>M|CnrVoJ`^OD9_g6G|ao0!L7$B__L;xF}=B2 znfM-A3+n^OF$KvWg@6X@IaSHxgq*GF=aIIp!@(b_0aH0QukZH#pr=&U=H(XNUN~ac z=N$@G5m~VXeC{hYsHhQNCQ!a?>iy<`wRCmL#EmZ6Xq{jT#QGxuO~|AQ(4F-2uClVn zy|j+b{8j2&ksPQZ0c+zWy_!t|u!42_!t@j;Ekd8vrl9^8#ANiMOG&yt zp{)JNx)JLS?-p<`qD?9rl_Iaf!rAbB|jIr9Ss~ zl$K+^N*d$lEkc|(U)^!wg#lV}j3rnT!^f_0(Kl95L{LNzU;0~sGX=Gpi4d3a)gne@ z_9S4Qx|9KwEJp3}jfHN~J?p4cZ?t;FcV4JOOD-ZF(T)R84vg5df?vnSwsBi7f0~ze1bRR4;r) z`{g!-QA#wV^&{Y%qp#`8RJf4#wizW2<73*M*Gl@`B5Dhy!>+D#2A<7CtdL(}<^GXt zdn#y(aV$o@fYgN=t>%wtD3Reo54p#*JQXC!AzjeGUqSb3y5q&IxZwL#pgUgdie6tU z_bKj|DVkx-({B8=J`L7eS=!MuV_N>K7?BM(%;R`SLZb!LJpY2f zIKsCk5*;u-67e*Suvm?PrQ0QT7v*9WG6H5JdVZeK%z^uIiln7=6e~?iC?W`5VKlP{ zJ*0)&5?HC2=&^p9Bw4xt9;}cOgPiGG(LI4q8|oB|ve8<47tj_Tg*w!7LoGgg5j`WE zXX};yPl2``D`JY#%%BG#w^tb{Ou{+g8OjTE`?j-wIt>e;v$JP7X6F&4+xnD1N9oic zGwo%U61|k(8PQWQP3uJ3nQ947^{*ZLE1fH!Jqp@qI`f}B;(dX1cIG~N*!v=Pw|QI9 zSJ9~F%lZBt!GJQq%lOND_q;&oec-I{r|KlZkMqJst zOHt~g!BQuv>v2(a4trvZDnP@?s;&UybLtEsg}<|PG(+#8o$lb((0*9m;9)j9x5*xEGbE~Zr-$9#^9qx!vp3`!=m|A1 z0Do~qwH$qEK|fmDNasPC4j|=u5c@Dv((x{;*_+7j-FaV#A>vX?QRN1*Cas9TTAEas ztS0#@7U6V6=sHMKZi ztp_QR3}LLJrUVikLW&&gV~Z1GwP(=LF0^TQK_N;|4O?|Q=PzC{cWJ@G#Vhb!SY5P! zV&$50n!x`I{6;*(Gan9qxDCfMEz7Ff$>}Lma4sy_xTdJ20C|^?+wWLZ;6DQ{8}vI~ zK&wssXF%XV7FG;}uZ1YwUI`6PE`%>ZfAO?( z6oL>$&s4oiYvYuHqOx^^_4>KY1?$&P+4N9bBo@_H(>f)`*3{s?2#bE&m0F)A7r}`* zj;gfNhT61XT?wEp%e3-tEV|ipT6t&RH z{C)#{tqOyEdy^n7Qvn?PLF5k5*v-hlpn!(eR1_tz2e4>i#b$A8NlHp#Mmils%d59Q zP-o57y1=>|UEqfyrI4riOm6)^y$90BgL)TqND}I1ERhbg|M)}0>Gzw6wKVnro(~;Y zGzNS+4#I?@kEpkS8tW>*yZ1yZ6)tP*7@b=oN6diSpTDutQ#F=8%nH+ z*7c4p__m>@#9HjAbpRVz6F{!1pdI<2`jzSbi+<(g|BHU*6l#^UTgjSFEmjV3RO3z1 z?&Jh}POHcQ&kQJ!Mai5zLX~in7Ot+IpqEW*gkgDh$;5hiDzX~p+EcQN5{Db*GkQH| zkAY@?6zo>E-b3wHDB7c6qnX-E=u2__vd!1@op+fHkSY>Q@45>T&i(<}sF$cj?QXONk%HpX1vL0Yx|ImNCWCiD$}F#gt!!T-lwM|f|FJ=<;)gx zR1R}J2k*NQRQ5{CM`Nyk?i2XTD;_+>w6(C&-lteeba-3Y<2Z-7l-_xqw}s`L##f`- zo?;6n$PQZQJr&pX@F)rTDIH=l>U4eS1^6lN@txz28mn_$XBI|U+fK1=l*F4_B?k0} zC`+&uh?q|<2*Mh22$bFPaG|U+Jtlnfw@f(thQaKnWqe^FadPS?DM+RgqX34U;>iI4 zfQLjnE`IL&6|_rVySkL8dOr>xw=DwhgM{Pa-vv(kLimGlL_ctF!M|u+*hF1*^YXe- zSexPw&I{I<(z!M&~a2T>zH>Kd=r4*0=}5r8wxpl7&+_>Crf$E;mD&v zBg^W{k30gC7|VEU;oMz4<)ZHmQkl^O8spj?W^1Qyn6Y_!FGgw2% zRkL=?kx{n4y$OyUbLBDOO6|qym7&3!4`(OM%3t23^95&Drao1H6u1{7xmnrwr%uR! zfWV7#Q^aYE6s?5N-^aLl2=7t(L86M)m*>WDl-a3R=g@Q=hwf{Wsa_kHqUy=3rxZ@c zucLf53KH0RGXYt~mn6|_*J=Z~*3WHFR$N-QcVE2|+X zga3wjlVnP`x}>J0dLw0FZ|37?<-jwQRIhQAC6;ekUszI2NQ|Sn7}0A6$&rk-$tg+Y z)f&52Gb{B!-c!IsZ(frOW)Hb&6bI{>d}}>5MlEr@qbPBZI4y_2J(AZsYRak;Yf4JM z^Z4tRc#CQT;?-=asReCthZ{Ce^cPmE$1lRG!P?bmJkJo_TZo81w*}=7R~7!;zLXC5 zJnC|rHuW#`FZ_D5cfpUSAnMy%htad$!&V8mMxRYck?NF}_6Z`DkQ~bp@`!{xB!THG z0~aB~UpjoBHPZO|tx?=DE?D{~!YKYhZ&I0Id-SB|Fn+low`L{x&3?QO6Y1?lvoiV5 ztk{n)^nOM?IuvKho5Xol{n97jl5Qf>$v>wXc}uz@Kc~A*OJ{@>pXqVm7#N`S_#oTA zCU4c=>25VY%fBYmuYL1ry>63+X*+9xwjw`*mFWO9=&~KqJ=E6f*T|D$ddZDD*P?gs z(8WUD`&z)6nXrZc9a(6t}_Za>=M zbnSjJ}wHIUtAL5h{t z=?=vm%fH^`sT6;PxeIyZXnD9oi$b$QV?&+F#dl~PyOD=&Fb~*=Adg^T@@A8Z{54)~ z&u*OS#m$-7)E0torZzY?!%Ez5H=z_MSg}pGVTG1IeiQH;gkKweO$2sWFVXrx{yvrN z?AW_~V5TK1oDC!5Z8sbC4Yu)fJ(zfluP0I?UWx0 zeCdks3`=FJ4@p$BI1=&CvNZlQ#5Za26X36j#&AkhmLJE7YQToiEbVg-Fdp3WmHj`bN+X)#zV;-JsE5qR|fl zeZvlWIjj?36twc!YVp5(W2pRHuXpF9bQE~eyE5|q^zA-%yKrfMw z^HMu$w&H!20sAg$(12XbT07LfzO!ZjsBT|B>uPy;RKHyicDDR>R9(v>qv~7!Yg9wa zqoYQ^RyXZMf-?X^`Tp;XI1>ok31Gwmy>==v=FlEq{JKF4?e^uOwsuum+^HIH?ud9% z>;KmwAw;a1uS<6EIN|F@3ata&SWEro?AsWX-xcx-{hIhX2W?G+kPE&C*d>s;u3OkI z(wNPP<#p-!0LN)-&(m%WyQ~$XpjpX&mHsuoPFIp{v2a1N67%|<;S)G3)>bUNqw>1{1+MgFL^tPYS(Op z)TLt6&*e?}Io-{VG_}>gzb9djhYY5sWEO;On{N%I{D(hqsE+=2*O(?y7^;Xr)XysU zi$$z8>uZXpP2RjY3H$+N4`#)tq^+F(zx^TO@y|+?Fa5G6$UD}rhlI~crAO8^HE?HB z0S77&;3Od1qEbh7%{XGkz)Qk^yq4Ga%Q@8Tg%yr!@H-oS8R{R1S{Uum*qV*t=_+RB zW-pqbn3)(`3_!@uu^SeNu^T3?2Q#;(9KE@!cGk^T&5-eycNjk2^5!>dM8GYyv%u$7 z)Bv6$G1X5U#A9pzf0GCQSYA_6Rx+!$c1z}>%#?|#sgu)EQmpG%7Z+9({b%SckyEYJ z;8bTNLvEQ2S#4q2x|GT^Eo@ay?Uu@tStXlm)3x_ajx|)M2p%cm05{srF}TM_F%f4x zBqg!xB~lE3&qC?9+c?Vz>GC!z6BzD;7bNi_Zkl*ATl&>D!4f9H4jgtp7q(HnBYPzy za#FMM!f09fn5ERL9M|$;BqPE%1#A&_5W!4ZJ{iS&R+JU7cH7hSd zk%{U*6~_<>vyLx}EhI%YJvQ4SQV9$9D9+x}4_a9*pGPul<<4^^cY7UaFpDk!iX_`o z7CBTV(t#Lv5r17uhyUN|^6bn1Z*^IQy6i+DzoIVDwI7l8*M=IYQs(<_O|4h5>JOs|bA!@`#ao zdF9+d8S>PMb>@aBw5NQdle7YPNNQ9NYsZBR4A=}Gd61}G@$5l#4iWMRF18& z78Op9t+d8&Cf`ST{tstw0vE-V?~9-6s;;hv2AaJkYIj4kxq*O!D~ARFw}?xkW-_s8 zK+zCwK-?l)%_1V0DU4>BB+|A^%yM%v!Eus*?zLDcal4qM3Z358xu2GCd*{( z_dV5Uyfbs}d++Z@x~fi{bLyr00MjIr-ed@lIg`h5|xcSh|IKvjj5Cr zl0kKF}Qp zBT|t3R${vR6sQM|J_+uPB%cKDhR9EXi^D-m8)r$A7ijLNl8tQ}+J*+w+BOQBRC)}2 zpw@t)qOs0p8y+aOFW*(t$Fx1*K>*Z8%0_-~^iV4?h-S-FXnS&qY{BXl?N8yCx1Yo> z0sdS`jp@2xq|c}&K|cBB+pFRA&;f{z`TD02`)(a#I{*joA}8U8iQ%z2?)qm@$2Lt1 zXCz)cTOd?ob!{(eQ%#QuDbgALZHk#Y^XzR5qQiQn%5n1UaoibsoDnd-1~j*Ev43yW z&!Nd-u~^t2ehK_@3syiac+0FJ@<(T{LYu6#T(S00L*F@T7*=wpD*c!WMY2J*$errg z$Hhr7FS_#t}Xx;W4Gi7Q2zS?XK_RfN){2uiqSSp5m3;gGEtSu#o z(sA6nLX_+k0=$@CbKPeq$$nyQRSS-R3I^49)JS}vL(cyltk@r2Er0{DpgW9wFhJ8? z&(9{us$D0Qu^nnR-mnN+9G+O55$@W-`{Q&C1Oas3k9K#dZ)ULXW!Mz;cONnB0Mb`N zuJ_Iw*lAbPw-zxN97@SSid*}^Z}>BBCJJZXzK!kP+hj$32|T+u3}O0=L;q(rfqg=% z7J0IJtOH&TL8-J6neFq4R_>%aHbUnKFw4)2Nc*DYIJ76tY>W+Tj9XCmPP{8W3}qf;qA;WzC$IeneB{(zIn+WH5;V_E!b z$W>(Q{?EQ-dfgrB&sSkgP(SFjmSap;75&}EvHlJXttf;Z=sXVli(x_W6KY7X_it~S zXr;9Q`@uu91)sb*BOo8eZpjvUe@^bV#TR4LB7P(bVuXmZwT(wt{`^`t7QOefr z4!!!=>jQL*{NR*ACt> zN<0f21|B6|)=PL+@!yxQcB_9x34*oEO(Eb6vAgdkg97DZ$BD24vwdBJQlME;#xQc? zL_8V&7D{R;$zbH`2@3tjngO_&b>G^?tj20`A7s5Z^rgR-uMz^Dp?3|$%2oFgGH>zX z;x+SbFLEzJ9An6y!SB2QO3B*Fdu!HgY23cCYR&paARK_Vj32nB$qR8RXw%ViV8iCh zbvNbh%fZ3j%!I^@4%rGrd`N5;yT7LgM`;pWpp9}muNV0J_jHOLx-t~)lu7K1p^%{0 zH!8bEP{@!FdNT}598;!D!P%tKJ>LtO!J1wVzGy>8kR9TMLKxgo#e~OGrexjvzqY2% z3kNJ&-w)3%w^OSAq3+Qk0!?>o?Tt69Cjnll{U*^w1w?#cmeP`)19lzk{bLO;IQ)EB zt4e^y$bdKZ@=PLZ5QyJUMzDW7)!%(<5FC_o-a6GUCsiX}VAzyOJM$-g1Qt)Emc_Ia zb-AdF`Tj#ZJP1OPIxVBgoHylz`@#{c$9cH|GFC)tXTRQ@Lioi*&iCS(RQV{MNlmh- zm*HS#E$K2>1@C_nzusJ(_62GndU>~;tk>Z1%7x)}mSZ5^WUrd?`F$oW2^MavLM7qOd4Y%34KgM;orA1hT zc$M}Khr`xsIPoplH1LS8_qLuX@tfAIXlB}X)ZJ$;*22mzvX}hA%Atfek zSu^0x3K#v<_m##Y@^n~~sZ|OldWukbX)~{UT_!5LR;_!hDK2e=1aY7GLySZkS{`cg zSjxQIN#Ib!HAzoBe4jAyWuad5A@_v%Lz&E`g5A@2D{S}Hkb^0(s>?57{Ye*EY>PxD z+ehoK_okm=TtSl(sWER0knZ9c{=4=2F=rUuXZ4JYcht_Z8!l5eL*QDM~htVfB6*mt7lW zSJOjvF}w?~NJjLyME3SlT&Uj`Iw&R3aM=yW5^Xb$XM9@H^As`35q6{*%+S9k@G0*n z`LbY7lnwTD8CFCx(WB=bBA}N96A6R$E&Q0l))Bv!(0GOXuc`RfkK%kzNX4Ss;d#xg zk!ya8>7}!gh$*%Tr`Ny!3^Gt(HO|>jfvt?(!Ak?u`&WWd!Jsdf-@t2>ynkgZ^ceyJ zCVSu`@+l#K#bLUxKwuW5mo2b@7+Fb;PpzjlNMXIri(?jCB4*ccg?YpCn3+vLA7#EC7*Ls>2fvzF z?(7~cgv|(E@b@riOV!$~S=9~TH~|nrwZ#jW(fTb}jP1vO5rabis0s3RY)BL2Q5&z1 zj+iEow`rCV-swV%>K8JzA%H(DCs9hxacghJzkt z_{Jl_)&RGPzgL7eH1gw@K=sy*H|chI&{37)kukOf?_1E%bhkSotYX%$%xC+&fVhhJ zS0{(t2!%hyQ^0g*lli#oP+xr{tkw8kn2-N@OJND3`w-o$0)dZgO5$Me&phlwGq5~t z#BCgOgsE-V#VF zXX}{N)}>B454xiqw!1?IC-qP3nRse?Ps#z;gQ?txgK2btTJZ-D{nXKIE5>&&_A3OQ z*~&Q?E5BI)mx!hK)Xl#c2#m#Bl?(>Pqcx85l0b5~Pp$opg4*9h9;2VEw94n|cw1V5 z(e5bl1zhFM8EG@U67hP?qTMUKNSQ|_ui1)cb^>TIhb#tJ(ANSuDe15uTM^Us-4KIN z%%0ByKcmmZ$aFg>vlE~RaR#3GBJof62{zPucp|KGen$n1c;@Rwq02Jdt^OKz&F`Jv z$=66ly4Ly6K|TKAzE;}?NwBXs|Do~6bf+Yp`7W_V66G%QON%|4Va>C}2KX}?AeOqZ z;Bmcd=TeDWNsPcMIhmaNsofwiPse6ClRb8yS{uV5ou$UPs!4;yd`dN#XQLzs?30|l z=!kbA>Lq27>l&r^wXLw6j=ag|4?A~*zdzT&D3$x^-|(UelfQSr%~w_+8&i?CW#1`=w_Z?g!MkUwWxH ztj=i@Sbxs^6#kdoDIQRYH+mN%PfG{V)#s0j#`h;i8!!S^*~KVwy~nRd*GrtY-aNBz z3N)$Tc2KSOH&0=Ub7w9(rlo}X8jrr_dK=B->^JDKS8lfNWsdb!q`MEl9Q5g}=Wc1e zL4W&I;g4^QLySHS;jfxU$0790zxtp)4x6Qe-pv??8+6_G<4{4^cU~)ey3S@}ekLaA z;~;8Y$&a=6&qi*UUgE_YLzjYo=psB}}xzP24ug5t4C)ZtJ#IZf{((X(QtcO$6vx$j4SK3GH>4A3(($we>ZKBNB3D{NC*i|5 z?@EAGRqAobKMfjWx`~&$#i3KsslQ`5k%Zmzaty-*?zAd61sU(n4F3^nbS6Ci2YcOr z0!AU2K$UZ1vg`$Kk9J85vMF6 z$>a2eTvzlt6}aGp7lfMlhPfLLj{(c#h!eh$Bxs~bD53iK;Kw?T?VzLZH-;BCOeR78^m zI!3oRS~mI8Fp2U@(Q7?Krp$i>YmvCmaxv7Z{^y(YR^5@h`|#l$ zBhz;;SOu+LuNUhW(BMucnkt4I^~Ni3f*C#P`G@*Z#sNZby%*X=m30VT#T1SlEc<`? z4*ZwX(CU+58NN>)6IH6~SaQz1|*M=xg3gW;N(VJ)zU5Vi*>x&pVr3rXk3N-z5^*RIqn8+qJPE*q;>wpVta@rZ%G*#rK`-w>&awD@207p~i7kCs z_~%8{0%~m#f3KQA^X()SjM-Q@2rqV|2LsTl(O zpzl*sa>_xHL1hV;JHcnaFisw87Zls4CD@}oYzg*x=yeHN-l7#}CGQfHKQHuY^RtR@ zCzYJ8so4deVk?^Q?KyG@+9>!UwdaK*^t~AEzckDxUK-}3Sj*K6wQjr|skw;)In|KB zdxu1g7RfF2bzscogL&Fwpx-g=NsTdeHx-Cwad)5E~t`E28s(x5TmlBz$qUK%qzIqzl@v=loph0<@rbOE%+Nm>5mUktB-nccjbTr ziNLcx9P!9DJYPIOwcGPU-}ui1PJ#4^QjRcByE~u1j9gkxsc^XuS0Z}BCH?pmhf@Zc zB@Zi*Ii&D@?Ehfz+sRx%jx50S1MfQc0M8(_Qn)=)qHZyfdL??En6kwptK&RLbcktI zMsZy~$teEFRHH%2J!>E&f0&FT0{ul*Khm%_|m{FW$EJk;NH) z(Ud9~JB3$`zKCD(O88)VV2ZUZkY#P(Id5_M;#9of$G+m304=nkiet96z$9yXV6t^D z%h%k6oJQ2$2A%a4i~AS5kl!TS9jbfhMD!axfpPfFJB6U?a?w^Z~r z^zFI7k^(EN#U(=EZfjl%^u6=T-DN}fl;v+|r{@OVp~k^?Ggw>N19qzsCt_Mg$2+ID$yU z==;z;>A4$*2i|cAgYP=nII@=9s6B!#-V>`u+Fe&BGLGgJ@cw zPEj)ZX365IcVzNZD?hd zZ=!YEuec`it&7j`{VjOW_+JOL2oMd#L4_ir$MSwxc z=efij@pXZo2e3XLW^#yaC?VKe#m|Mofi)``KbG|YBMQHf?0(O9qW1v-OYG-{R<1-p z?Cl$U8;$ThObMs+=e|X|pVr%*$yPuM==BBAym`Vh9?xF}vegg!iMju(vk zOMAPnLd5S zjO^SQHIwte1N<-0GUgV@$FLq7O{3{D^XA~8rJi7KRInWK$gki9A!QWK-hifGUjH7> z-YA)WOUci!|9#0D(Du!LQnKuplE1s9Bs(R@NeNm55f1)jd3f({CFHR?^lPfE_|#v?F`LcfvkVQCL=7Y6mBG&!@_u3E@Q^znmNyrtFKC05d!Du@ zjcDC#Ij!rSA+2qE`}mqcA)k3Yt%iT9R}KiZx7&+PeMb)35VzW+SWeLg>EJ+#DZ+0k zq`M-)J91NvzClNDnpKbtaSa^s=`ni>-=xg+Sy1}ju zl-P@rP6{La{7UeFG*NWZ1mG&{3!(bTxQ}qR`|tz7)sSV8F1*CbB#w@81k}G~gocwb zj;&2o%g}t2F!JrMVKFlD7yMGcF}=tSKVYTKVM7$c3^X(N!Z`e}Ye*l|#H`#k$!p$9 z%vrh|`pJZA8yBQi{7nhv8MIzVDILjW0*WCyUj~34Umq?UCD#u5}W--{u-A5$y@W^ z&GK)$G$1ZLyOwYMg(mKP4LuE0#-W!HTQ&4DESNh?*5GN0g7DwV zH4&bb2y6Glvissdtm*RsvG?_%(%$4jQg|Es8p;>!{p9*+In)v=HFBMmVm*CtAU5&y zfhfJ42R&}C13n1L`0snYx93K$%hBF0Rq2V?YTkEx>tyi9moG?yy?>kaG20no`GQvj zPHUfH)LO^$nlIA5V7i;r5TjgcNig7fIR#G{(w@x=YZi8?DLt9|Ladlh{PEq=ii0$V zzDSpY!;nf})y!*#HKYBjo#%&&(mUK8!$M)TT)(F16rmmCi?H6IEv*e3JI5i#u}4oa zAmyC#l<>5_b1gfx?WDWyVMU<%hqd<>``LrdcSJ- zVPBAw4f3wTQ!g4uj6a3%0lU@1TYP>*_^bP?E%YwzZ5E+o3_T)c(4td@M52}vX*}~< zOAbFoOforqkQ9AXv>y2K!1` zPG%mE4Dtapv;gfL>W`a?q4M^i+5L)u1q?l)>}(Cgr} zMA@GL|NVQ(;g!Ts6XiBFcD6`Gz-=Q=w06V`D*@);p?aGIspO+EUl5XR8#BJ-ql|-u zgxSJzm)S;$(N^-&1YD2W(&c13((v|vmWJzAn++*Ro4L{aNvMP6AA^)G+d9Mr#{Dsl zR3R-1*EP08In@3lv|pw^#23iit4d5}?1K&rYqJ;82sxQEb5pq-2K7wTnT#N~@icL?(IvPebLE=pvB zpcRn?fw^92MQyO^r~JRlh!;g9IjVD=7Nb#99Cp&4Mbm8isauQLY6}+M&L=(+o<-fEWocfEtSUBzq7;PutW5a~%N;G=uR9QQOGxb6INk zT%Hnm8XKSophaO^$7=Z3jx_N3TZW3toG6he`-TkZ#hSF0Y571eb)`RGwf%gcA}ZF- zO1k^r4DM0CS!=O#f!cZ^urkV>2Ip(Jd@}F*R9mK%JiJ0HdD7N$WOuwkWJGqzi0aJO z#6>wagP-(5mk~W#sFAIE19_`TwCME3(xQ|y=eYs*CZo@;%^xKBW_Soa^aDtBdRH$q z_`WS$PD?b)rdE_I)}rlGQv~0qWqD4M@8jk3cDl8Std_;+2g(yq2CS>BEx+2$%b{_$ z0;?(_ts6l{X&VAIuL;%>9cn>Gv|y}_H?Oube-wlWlAn*o3XRklwn>x@o8g(g+3u^w z_XmS9<^-%7QRZ9zcK*GyR%mN-#y0hrwG4--+zg{T4}2>?y}K^tssYieB;XX#2I!L@ zJA+IenqW+)_(!4$=q@UanoK#l0j=2A#cWLv(2vfFT&MctpiFyc47m5{1;p2=4I;X6 zqMUBl`qc;yGBxNcoYVUAqPV`F;9kJ}a3t4v8u!4`)%s6zJ)m|w5kYJ7Oyav; z^UH=2TU#t4p!%&M)fVNa<0KyLe}gmj{WQE=U51FY*_v_WYvg&gUyEv*DvZQ6$CAy6 z3i}<>8gh*z-|meiO?kq|u6;?XO9YFz7Q4D}C3 zwE{CeK|`{tbhHanB-*L|>Kyw%hu$ri!CD&Ztpyg=t)9t;O{BWhw$46(QPHv*m}OLB z0vIRo(%EB>$51TfkE?MC9hcCzRZo%;F@g}24FCV7%py8T^SHQ?pIZo_S`l=x>;8p6 zPoUCeO+yswfw*)58jrD=#UCv$t{|CA=%m|B?M0o8dx>QJZdyXfpeOEN8=Tf!-iud(ufFth;|Xw}3u zp|8Gob_&P7_M@DhLNCY`Sbc+`ycV(9v``v|%q?Ux@ zx2zNRqB~0FFIhfs*_|^Tqe}ut6?HY6;dTXzR4@q1bTII+v1&VvPbR^1q-i6JHmWD4 zVzK|vWp7lt6`@aR{;s0`Q`!IW&Bi9red{+v1cmRykOaTv|5SdGqp=QtL7Bc8H=&(P5UE)f6{jfU{PIc74+r6wP=TQG92Ob>B3>{t#vU=NW`qn6Sg%E+3PPVyqk| z#>*+t^jr48bx2GW<@;y|Dt@`MTRlIWS=6!e8}!a7Yct8H-gV%%SuM+gqXC@h+t}R5#ss zA56wsE%>w2y9le}#vGaZYH=Q^H)p5}Fjm40-LB6NJB27_i0-{HLkn@jh0ddQ^N4RQ zat+`_GW7Wgj~O{|FMIYz8v2fqSUm5$H{)`51Qb25+`jPkJ2EbNBT2RqdR8l7UMk3` zm&>gro6VN%@*>>zP~^s&jLUxZtIyu#vE8ce=IlL_C)oF3_Ll3jR~Nqn9?rtJBQh@{ zLN4a+@UO229r~HpGdg>rGwsT>8}qj>FLci2ja_3mjGX_?_2AF?iS@u#K_GfuEJnlmm_ zz5REipEu`m9(?9t9vOxh>^&4<-0a}d&8{MNixo9@hBq}OtqT1K_D27UlNS)_P#iE^ z8%_uHVK-ch8{kOH%9P}kXWqcPUE{ftY_Q+QPNpz&aQgSD z)vFlq4PK@;HEs4b!EkU?6YAD9n0UeEPs64vSlBZWU;u4=5j+&P-pXh7R+giM9BgA- zGCjnb+6w8UH`QrvpUilCSe-(wz%Pulx1tZ;)b`98{cYO>_O_bD2)vN8Fh%s@fRrCCly;cU_k6?-4DDtOT#pmASfoBt{EGh0_S#$Y((}x>kzhsb5AgOh}n_I z!B<7F_wyqrN}%(*1^ye*rkohMC3iE<{;rMQsYFT9(k@#j5=bd56A=NGYnP8=f5*wO zx-PE^tK^|Y!QjPyZ)PL-3*IqCT{G(Ud5}G6UT258VE2}(B3Ai!a2%jd#`ci6uBma& zW+?E&8VKY3ecH|O1CLVzO_h$(BYhPKA?z+p?o`JFLCGkyu}_$PhgbVaLa?`D^9JB= zE8=&UxxPql!sQR*Nj8ITcQt)<_5{)T{W`|hm(;C31}J!&)+&F$!XrNnt?&}aQy6^u zC9t-=vEAxcv_D*9WrMx1Br(|b$Ji4z!)_eOxpF-SOzoZP!KPfp$Xq;a&NGfo|LA(K zVVW?a{t4;RxskL#B0ZlUc?9*>7eF$J{EagpM?|`J=1{*Sp?+4Y4x3y&VAG;aTPv8_ zhYm-QXolYJ^W#GOHlyE9Txb1WfPTaO3H?RC0~%%S1MAW6HsGxvlCVCArFz32Dwji+ zC&q`8JkUooeBJ#GdZ8w9eV6h6+e4Xw^HbqlQ6Ci_4fdvDRIC`4B<+Z7l{w62zatoo z7JZcM`;z<{W5O`mk8{5tliB*1ERTJNPQjRbn-UD3@2BJ1=L#b$Ebz=*$d3?LFbH4r z<8T6G27^b<#*x8^!C(O1WxQfAcsz$6`6)^r$rVOEGvj?88L^mx!DG_~`nQeqPp>z3 z&gh(ZbI&hE6f$UUGahQN_c1>>?YIhoHhtHdu;_$^6rN6!?xw^r|qC z&ZUd#ozw~}_tM!O%nW!w}-y~lL@^}InT zJO3n0kymappqy^$-X7*N4m8QX+sWJGWn*(w?)}queEl<`A2c}XV*M6+dqn{=WKXt4$*OSzq!e^G!4Fk2bN{J_4=Q-g}!sbW)YXt zhKY(k;xXd$v<7{caKt+e?S#L8r4vH=9<_PX8#2Ljy2m>^P8OOs&t+Bz^=L~2+LH0` zcH~#+Qk4B)Pi@a*e%$+Lm+D{dokNsQG~*RUXDGA@wlfJ&BVHD;#Zg$ZW|OyO<4i{? zl!8Y8>+)cL9isa!!PUFe9tyIwH zte1S}_$VuM4y|TjIfXuT-|rf-nEY6loEep*DNBZecQt98?46S10(6yH)t^Giy;VIO z?FCeMKHAAYiB&z0PNK7@*jG+gC&;7NL#LV%1T6wTw_eK09;{YZn*?2qVr_R>YZ=p9 zVm0(cn`CnM0NDm8s|?L6yWGs6I^E)-Jm7{Y=rv0+i>YA7`Y!^vpQW?*b*kT7m!~p+ zOsq{C_14@Q&0@`S@&SD7lh!2pY4kDJTlZJ*oW1I#uf5PQenNZ}@jb$$Pd?=!Lt>{& zZ@VD&(fY~Jsg>k_Ajq1KEFKrghNq!L#CR{$#0iM+=BJyp=B3t{ddaRG(g)|bbhk55x@=d&tnJRd=gcdWRA|T0#tA0--f4{)jWgRNqqp6R zNNv?^(g)hE^fpMBpj!@Y|J_TLmo?6W|FK0_Lo035yl~q2MRCJS@9Y-V4`0l1tjvw< zfj^m}cy0hc%%>*4U7y0%nFrb{zc}wgOgo;=%pkSXrOtC<`H!SX$`tLOQ&1*q)0_{d z?m1`77k#OBPoGq?fDY`b*k?2P#;+N zm@l)lxf`}T+cGb2s+UeMNT&JDNl^(?$K*CSy%}z8cb24t(MG96ld@Rdt>*;$)M-T; z_+n-CGcf;ADP_FR0=QxHnadlbwsR!0<(x6mHLYnjvn4iUa!x)Y#uL5PSvGY6sfAPp z*j(sz=zfCXg?~BR?h>KrzRa>T!vJ_4>#8EWu1C9PDq9i!Nv5SB;BNC1d-@e3(REX* zCO0nRmDu6I0e7Kt&X7(K8qR>d)S;%g0J;{+bIEDT#7_0`?~e zsz3WS#D8+*qz0!C?@JCOi43}E8}Ju`r62tnZ`Z`XPY(L zuUTW~jG1dIWS&s?oS}YdHRnos+#GJCJUoZHM}!9|U{V$HOp&-##HJv0BUM zX|~_i?pkhPvG$f`5P6WkK;yFN0T&|R581ALbhd=NBK6Scv10eBZ=YcPi>S5`{4?eW z2KKLk*%a3JCipuCiwySr4~M%KK_>g5=Es-bJ8^-g^BbX=*NDRhE*L_1EJOdHg|~Tq zO(WJlaom!Dxz%9O`!HH|8d5g8B~6l>Qs+b4;i-*?TDzh{?cZ*b6YMPJrIWIicf#!m z_%Tbzum(qz?bAzi9uFUQHoGWes=KH_0neP(CnENxTYu-wFv+Y&R>xBtPq@$b`+>qk zo>-uO&(pTO11s+sAbe*ogf`0gzR`&62&~F>1Xg6j zN5eC#SjlHr5MD8rJE;%f;M_%984dg-Z=qunZbSKBKE~!@`V)gLN!C&`Gadvbqowv_ zRuVs`f$8=BT&vuzcG$GkgBjK2v@3#6m5)zX0&%PSfw2`Wf%sK{z&KnJa2;RK8c1Aq zG+?hdQqZ4%Y{toH$CF2f4Oz$Y`XnE6ypYnL3@tK~Kc(3HMsn=Jyak+W(gdeTYaP>s z_+Q3MQR|rD0jFzLVrxK>d7n#rEzR(XArW><1E%QT;#3HY#B}9^$Rb5wpha5w%23)Q zi(S{Ho?3M}&G?Ej%jh!=uv89d53cJ=Ii1W}ZFKiJQ%jDd zcuFeV-D+5ZmRd;V04``PauYft8CsSl=C5s|Vr_0!|!SQ|Fw20vlaPj2uN&P*ac2Vg=s;7m0M_CENc z)-ObJeaxD4|BH+pz$kad%T9^wRwJ`+aCDAL3h@M%5C%s)5j*U+(o1*V8~mpnfp>M9 zlW}x>lR_MUiE%P0gY(G$vwkJ|5NM-M?g|DEP6r%A+JSuI$Q5recnB1Kd1DY1KRr+pFMHX*MDO;My~1sQ+Me#^pDoNp|D+aA8@^M&?HX+6oif)$R3Z z;NlEqGb_SmIYo|>+tr`VAsfR=4|-t=R(THk$4bD3nZ zHeBYwMc4%WhQE9n48ni6`j?posb!-<$KGE~CAzG}&+f$E0+f|9vw6w|zRL77XvT5mqz<8ft1su;8514{= zf}GFyu)ARImScV@)&_f@JC+HHc@e11{7IjJUUl-35EAj%mQm1+7bcceOT( zm#?k<#hh32em3pZ+2`jBJ8Loo#i%V$Q7KlL%*wPM3+X&x@(b_l-y+;DvMT0pv@-i@?Ru|C53L=MzwgC=# zW{?-+fW%gzfEO$c+>#*n+)xKE&IDy+1g#Ih~_^aL*r04)=Yi8SIZGKaul?g^%a@ zQyVj?NVnP@&+0a9XTQ%^?a5WpqcKPOa3V7-5S%T!%<7gIy}qy60p6G!d%3+iMvf7g z{R4Yuky~~)@0lAdaM;VN{O<4A@6PWOCzC}5d+R^jGnMJKDyo|!a)@unbcVkH@nBp#6oueMuBABR(MF72koD7!8|q< z*Al@vHhzOchF3#DH4g_B#H? zQP$$TzcIdHT&*DT_T*Z_XscUBTOHy60cy$VqqFAMU`pk~nVpj7Cu zmuf;*g2+Mg)jB04`4UC=ZHAPpTm8Znn%O&0^J;KJ|3N-9DoOe~VGwgW26td_$ z;cQ%;vd1(Svl%{zAN-b$m#EcGz&`jJ?l%yuk0LTAR@VlVwwx>Mz1pt+BZv?z4WEC50Llat4g@` zMG8SZzr`E((r)$gnB9l}6{>{5kddzi`PL$z8~MyGmhTO`nSy+;A>RvnzTYC>UC1{V z`I3;&q38Pu`I3usuXK?`5SHb%O+_YtFxvvUd8W;ndl?mOA7H0<142z+ha@zpsbNp782Kp;Vml|=E zAaZV-zOsd`xO0FM**B!P%H;GkK5VKyp0Ud5p29%g7w6tGHlwj?Bzj!RIIWP@^mJ=Uz)}G{53PKOTU)hcxw}K1 zx4I>?Iq1`^kr_RyRy`S zuYI?D53Jx=Zs*@62tyT z?I`&6Iv8zl((}UtI|75S((o< zt6l2VKV5czTpEG0yASUT65lW0R5OV0`~?NABm2*4F*Jd;1q{~K0B6ndybsP!)p3&l zDcZODa9>bg{-cW?Oa9h%dUI#2(Z%pL<@b{s*VjR)U!x1tp`EX-G8wK3TjPizFLS%h zWN+!9c!d(WOCbi^6p|~aT)wg;PU}>+eD1hhd%FSLVS=|yrAu0K)KW7E(2P$vIq;ig z&z2pRuj+Ya@zY_F&CUmCcJV1A*Jitj5(&_A7WDg1qpi3XIm?*h zQ(;`^7`gx3>q_56;&s!$x=4og>k`Hk-WOiN$(1t<&-{`Yz(+~LHO%fsTOLSF=9kgb zOe4OOHsc~_Nw$B%YmKb8GiU+{GI$3eDwCz#BAFHdhkf~7S_n^0ieU8O`cy_|P7xit z^&iurWUWm*4ZSSb&$8C0o&GdB8uua>F{TKu-8ZJF@7wD>w6;{^mXzx46=i5EnMItg zD(d`EDn=w*F{k5KrRkTiMw9Yxby_OZr5B-VpN>4uspTKBBaEoKNcl&g3~{#YC}$u&t)wB@jdpk0m_FG!`yDdh zXXKi48j*VeaqGPp1f^nyhY~jCZhg$%=tT>9;c>h5J{V-?eM+V_jM2L;a`^!}$z^a8M3;5CJ>bnhJjmV*P(EN$aJK8Y47m4@SEs#_chPkqi&>!_ns9FB!HL7O zjP_S&zNa-OzB;SNRYQ%fqA-&8p0+X3X#X(p^@&F-UY|8scyMyhl;Jr8b6H(qOlGuk zlw3`Iu{CL*FueP4L(q(~>YxkumU=G)?e&Sg{d4*CS@3!Nn}_py=);8uIn?JUnZNu+ z4B1;nen0Cg>ZOzT&}e;;(votZpfh*(;Q~lJ!hjEyp4PZ(a7F}W?f{F_Nl%}|$r>ZK zQWtjgi{apcylN81$e=*L(x!yBnZ)^_uT*fd@$%O(q{1NcI3H_>+4r8t37qoou*YQp zmf^>F^YQgi9y8TjhB7yED}UCiFem2+*f&8@;d@_&GmOK<)4r2j5mI&^{v@2yz-jbi z{}(ZXpo^AoNn^8G;Jjs@cc$Ff-8)GHc6DgSK1>>u8C*PI8^YRm*+*ePPuJ*9Cb(mJ znH+u`mMIJh4e9&hR+w3geb$P7R^=|%_XQ6cknP`Ym84i9lY4~ZO%N18BZq%Pa-{dP z(nO|XM-J~L^(omd&eukpAc0{iyUFIsf^X+wgKKJLqB}2au zaHCtTO{Q`(Y=si3g-)bpI*i++p8#e07x(BVK$(65l<6nHC7f5IC&0QJCxE!Y1Zs(> zcM=VC?oU1~v6}wPc1bJE;*^=1EM3$}O~f}%>o`p#_nuZFJ5Oto3*=wZ<|*3gM@*#VO-M_(~ClDU4D(~>sVju zKigVEzU_WfJIyrmhpQySzZq7rhC`?0p|I0Z4YrC&hCYSUOEbWE7XaUdsk|2={vTFu(?q>35@`!U5 z2dP%tYyntq`kd6csfCH=#Hoon@wxGGLf)4<`B}Vdnr)gR&Nb${3Iti4CQetjn(|E3 z_~}Bv6Z#WLh>}te3=0)1B2>a(&ypOpprtQ4OP?TAv1Uvpzt zSUH?*m>F>cOw2JqKLjj5B|ui@QO5Iwu3L~C2DOYHn)H$Io2;J6(cv_Y$Qy>@#m&K`lczA7aniHO^DDgA!dx>S^=m$ z(x;H4z<9Ubo@8euCtfiSA_$@i^G)WNb~1Ri4fqC=icb*xfGIPq_z!<0J{FTh!HAqE z6Zq7|nG89tt9BrWi=z>Lo0=NnwxwaSqq=6TBSY6K2t6}a0WnCNe2sTTAgT!XNSh!# zB#_cNK#h|};9b*1TOG%3>!JOy z5!heP;WZiPY1Yyrviq8`y(tk0nB^T2Rn(uWFf*8ijmlyJ07+;3ho+n$Jogg z4R_w$IJ}Wn{P@@G6xnXwW6$N)qxv-VHqT#MSw;cT+)Ah>G*sgyc`>X!|j>bQ@qUBbfZxm77sr9anCHHWB*PBr>t zIgio4EAm4gDBf*g-Vg&k{4+}F_%cgnHFc=5jkQx{x-N%(ivXrFOx#E+!SMUMIo2#E zd%#svhR%}<^#GFOV!dH;jJZ?2bXCS|I>1GmNPjj^VJe5e>b}fzu-Fu3c#e=sC!+lN z4Qq}J8rGQM(IGZPQjCjYTD~ea*b~wWK0ECga+ej!UfQOnyFl>*&wSQA26ih-Z3FGO z1My!Q`ZI;r@S#k01H)7iJ|Q)^@7*f6U}5xfBuQL7O*=4wU-K$LF02zGw zq;C&V=JK%S*)nxc;g+06a~RGy7f((*$Pt`(3da2u!P`!xkZus4lLH4BYvXFWtHrsTez`k)!O~I} z7Il&Nv)%kc=YqCzdu+uE+Qtxg04W2LKyod ze2OQ-@IKF?Jx0TC`IoRbhhS`mCU}dyQr7Y(7_PG);U zM}BzX?*xFbe+~@TRWh3qXRjxhmFhX*va)waa83V`wP9sh%8OMoc3w;lOiO zo}())t$tJb(c>iRv+loY;nxnKmSn32y8%UQ#>z<2iYiaj<(4+T$$kvE{=Dz6TC`xg z_6G79uARlNfb+7CHX=WcIE?&_FTLhvo(|g3Heh?g(a~1bd~BjOatwG)h$tv3f<|Q7 zY^pWFT8OpP1o**xF+IgcV!uwNvtel(CPO0vzJ4;aMA_VxGyu)1tt%`gupN)If#U?d zl-(q1q#C`V?(4mO>~8f(nWoQ6^gO&^p4PGDPvhZ#N;~Nf+tflmr*@NoKWtr+B39cx z<#>8y{8P|f3c_OPB{PvTb^F4x6!?)wOy?rvVOLnH6|K`{9>5CMdLyw@%-23b-=DjT zUridR~}9Y#UH>o1Zh4&fBxmzW>8DqH?F!CZAGsZMw7?gLXI>ycRNj69zst0uhnH(b zy(_e$CxhMZX|HJ3m8LGssx4H5j~Q0CyK)`Y*K?c3*)M3Ol_pt(&qD!p&2xTpA@~fM zY27wE`kBtYGXxt!xu3P!(e20k#QlGM<8FH&{VjE4#9r7G8mFXv(i?0POkIp;=nDet z2WJTG;hBWjVo;1)DxQ|pc+b3hi?sH&e;Ln1LKYN5R)YZG_!lIc-7wd&jyDxXIak#F zP}nJLr%SAj$>DdTt)FsFu5nrOZ1bcYi;IdYCPdHMoS3r3GWO0f=`%LOX02XRmbX!i zTz>bi@pIOOrB2Sdf7|U1?#z;erL!h3idj{9U(;>20@u{7^$|Z9SF^G%DL*5A=E8fY z$1TYA-c>DEmV2hn-&DmJ1j*{4<``G`n4IdO@PtXLW;EQlJnNom8-AF(b{;QUY}xYs z+osnoop@*Jf^jo9S1z>A-C2L{n*5D-q?8)N;xZ~SmrcHF&W`5BO)JxHkDsu@>5fRs zt6e>7OHFaq_}SaG-@kk7x-wJb*aGkR9~ACts#=mb8|pIPL2}YqhVhbXZ!C%<4YXZN-ENh|Yx2L%fnNvB%64~AWeqL1#Cck<|8wSL zEdDcm`*+``zto^R^wOW;jl;>I7t^AAUf z{J$A{6S%0Zvw!^DWoGWMj({+(GsEru_i>TaafHoZnVsU3S}DdVK*WmQqB|IgL&Z` z*%)o}BofaJNRPN^OLC6(V7}pWCR)w*sL|$0V%$n5i)SI=@gr-$B#QFVzKokgdC!<0 zoId~t`m7GaJ*6~RGy=wapW2N4O44#F145*Q-Rq3#6kOYNKBZbOOg`Fq!k2&_%~xtw|&#{*1&YoDuoNGlci4&-JsDIpMg-XG5E`FwQGkaqg;r zo8uA>xW@^YoYsGKB|)!9PP8Okl?r4dJ^mk z!y!j2UevB$kF1&9kM)aBrH}s5Y_@eLu{)V|^+06J+(&FCHVbxH`DB6F*klI8>!H$~ z+55&?{*)&ct26J|KKaBiW3g(AHzWo82FQ?S%@XGNngf0zCCP1=>q4w+(HNW1ZaFW; zx2)%rvJleg=TmYZzbnr5?qDfO7vZMx#LKbVqmk!Xyco|Xu{3}d=WQ0|b^@B&#yuLU zftZqVkm876h-19VhPi@L3V#A}>rzydTqT+8X8Fl+?EW6TEsW_sUK3W$XdZ8hxzLXH zNIFx;xpB{97gR4{bwhK(H)lzGVm;p$`Kk}wacsz?5E89Owgl)zn5~GmZ0JM~6x;;H zmmZkX=9RM0#}chMQm`z!v6vo|6NlR3iIhBa0M8cn!TVdJ-P%$c_=#;#%l4txc#`Th zZrqt5(|%^kyS0U3tXzdQTQyAMVUj4#?A+Yp{AOz&T(b zV9rby2&fR>B1q7Nq{|L|AkvkD@upJ{FvOS?-WQb0Q*QNJ?l+n$`)Uni6GpZ5!-xJl?DrdsV%_`OKCeCo zS2A{-{Z}$Q0udDYzSTqY^SCjSJrzc4$Ygd_$z&C>H7+iC2kC%P49~y&NXI^cv;@l) zwUnezrthb+d@vvSovu13g5vCnqik{;Vn)ImYe~ADi zN`e!*GJ2K(>m_k{$d`wFd2uVNimP&avQQ>^pS-H71X#!HD3PJDE;6d>7ma0&NphL< zB5riY?#OnQIMb+I$nMESjdSBDpaEiBX;pa@l}ls`>btyZMOBpC)mQ?o1!o@CW1J~h z<4nE)m+A>fsjUHXYKCl=scheIKPm0zOj#^NfVW1`9O#%RrrCig>);hEt4P28^j6tr znOQnO$nGj$PNO5?W93%Ftdv`-{5!JcgdLv7H0S-~1Xk#OiJOGI30tGDq2$U&d`Xki zs@C0arXNt=_E#FRPy9XU_%Yc}B+EpbBQbeu+Pyh>h09hDf^;5xpxgAO9lA)c64=

kao@>&v3eOZ9h!SUm8jZJc(Cgksp%FuWx2mVtT5W9K$NU;? zRe!qn9%%GzRg3NO5so=W53fYe*k)7d4E$PQ<7|qaU!@yI-{3gd=xLHXm$g=Pww>Kw zMe6WBM(WnJ7Wg&F+|6rS)jrrZV=`o|YS{hEHRkbF^*<+sFwWEB5UZ&oh}BQ%npRc3 z{|M?s&2cSKw+VE=tD^z3^>xEWoMv%il(6%d6647bx-V9W^_~$}6i@%0Fu?ZcN-^cT%SV7Pj}fqd zc53E)BVA1gv_~SrsiS$&x|%?7KLh=cH`BC8(Z2}`TURM1H#7UGpnZD4Jo{Q^pD#4p zeH}JFLTx5*WN;)GJ7Z4WJZQzb3-U6x^v0oI)+S=m^y=>TEhg`0!4YoQ7VNPFve|Z6 zNWpArg*Gsl1hD?1@<-Nro7=3(n~hecH}1xv$Lo{p{6W+FG+05Iw|O^0=WV%h=wiK! zIB}~1w9isa0=EY%qg02=Zp2f-5Bj`j0v@b=!_bpswFGFr_aJq7{k+X)>-^0gaA)2) z^xJyDcH_`1^(mvV6Y7~1jgd-9X_c@~{3q%7AvsK@6DKZKpS%5+L6(tyw|&o%yGRj6 zg`(Rb3rQ7u>bCDWl6{ApK`9{BE~fM;cqt@hRec1@bi8nC5es3tq!q5YP;Zax{6-`sS#ri+Eqs8dFx3 zq^&K>vF8qGm`7xP;}!IxF*a6e3$W=0_6*`-@Wf=w2EfuWTkXM(`HRAXbb`c2nnLUk zX3>IxQCdjj;+-@$@mw%_oxvLu?4siooI3NZJ*23M#;h-t3$wwQ#J9j7y*^XU%#N`c zywTkw>GFA>o-S(rRCy}WP2|1sk6CY(t=Y)|;zX6E@$Diyf7_% zQ-6?}!n9V$l0uB*YZKNQ;C?+A*6>D8ZHJY=xpJ#I{HxV@S%%0-^k_YAatks zsr)`$Q+Z`K-EFM{GCj+028)X#D_#%w72_ZlrU}2D^WpF0{FL@$zGJzSGV@OJ?R;^1zAUY{ z;Z~W;zLWFG+c~q#A}f~mjFdN$Z-B~LarFqU8Q;nGO^GRG=8D3Re8tw=<$aTL?00fL zNso-;8dA0=|4e>t5=S$rv2qVajxG3tAFQ9q#+0s`9PS*b zNz(`m9kXFTduTq}Bf$#gjvI&Cs$6uStDhSQS&ooBRW;}lF9}Q^;nF~iI+H>Rwz>do z7K=JY6H}H1VpT>5V{wfd49bsl`@|lcZgQPO#Mt5|xb#ElSXdd^((uDc5&Os2e3)l& zJyikqr%FXs+ScqIlBrchBgn@gt1LV=fbs< zu?N|;A)-AzXPG8ME@QC`h;4{%n!Lp5jI@22$GIjJUPLbmb20ePhGy<*+|#ps);uS} zYC3~k9XeUA!mcZ>wdcOL)6h9QW9@xT8$hC%TptzOyz2xP#7T!GgJ?yy@3X_dB0=GV0~Pt1<}f+Tb<5c{*q`k&csOq&5-O zei{Vt;yhmjji^|r9oU)+N_?=;*j<>;uxhp9R7SGc`fyr&^H72>oHBoSg;Q(a%;;wt zQZ7k%iAz5XN>d`N?~EeQS3XveC{0Jd`F2W@aCQC=rJkvs2cLDN3+7Wi)h}NbZJ3hf0z( zk`pTJ&+JroB38#Dh9rv5pEJ!zjzzb^EWm8#ZOp#BF%@f+8jRi}AO>sFXv`b6YDvy-hzG}G>$yhX~+&Qsd(;d5lZOJ-#fJ@4T= zut3N?B4I7EceF*=s;a5d^sN@z_wVmRJHSW_MuNhI4;{YI+P!4aCAdZ07VMmxn32eEJL=+nYCgxQ+tlAZ?^Asls_w3CJPXrcKp zRK{A79t37;UwwR?Et5M_X(N>rcLC(OIjGh|vfq)$CcjQ=cW3CuD z7s}du-DusX#gbjwkzNP%60;Imj@mnN+;xL&bOXM98QQyAx}Mpu3F+eDbD_37VBBL= zUVt^BENJ}WTE_L|Af-igLH}T|P}agy2u|#vW7lOdzRp^leXVIxL{IJesP9bWE_KPU ztV!!&tD$dKNAB=ZU`WIx=lJ<3InIZ>6@CoS+Q9hHcI%5RiN)7uIX*&;>BG0^co0-d z)Yaq>T0^hst;@dqLUMkGWvAq^Y3DS_Bq+NfS!9?J3W%Y&Hv|pkgL3kFF|y`rLR^`Q zH8;aCJBCDi|5crtCwVVxyi$t$Y{6sEazS^N^}SfN+)i0yA!FT9-p`9(K-+Ul8FdGX zBHglja}MB;EQyS|9JgHvi*b+fesunB)c*CGk5C!k9T)B zrs1Awl)NiHsEM<(H8J0Rj+@_kPM8l|83hX>iSd5gPyk#nX`E7yb~B)EIohr`Xd;>4 zqj4#LJ5o!;vu%v`U#nT$KYG)kxTq2=nso$yLdEWb2-IQ$&bo`7pzBEAekrKo0sDFx-d1KjfhzRTn}rHZ+8ZwEHv+0VFqRN$H+K#G!C~8}_E; z5rd2;?^BV)3GW+4);@0?p{$ee5m?MDt6LteO~MQid0gO>3(cQ`G?(X@A5Vd za7RYUEftb?d2q^RqAnw~1<>~#x9WibZE4)4Zo~XJ#!K3=II%)d1g)5q%3MW)k!XFm zDP$*UikwktF~YXJeyYZUPiwa5mU*K?A`W>UjwNyMXEVRu1Ahcbfj^7+ zDdDt-jbuLjl=&48{Jh>WQ?OT4(6YIkZ_N>{506L-_do~E@S!hm{>qZV_(Z~5PVi)2 z^KM9X&Tei!5klrR`%Z{tPP6w!7%9)oXwF`e-kj^s(Q<5#yp>L92a2T58eK*yDB1xb z7H1h5lyN`!)CoJgvD3-g;WoR{nZ*Xf1sW-SNcsr*&tSoW_EaS$EBC?r3>WlnpNNi%jAXv@aym`UUCt2>sE7 ztdNV_Ka0K}XZv|QIVmg^<6#$Wr);@1rTi~QU5Jmy&~M_!JYGMGmY1&zjaRZ?zo#YeTf-id$wuD>k z$5q_mt$_OtxSt{;uz0vepbfg~hyY)%w#-Jne_ox9TLq(a>SV!F^}67F^9t4OL*AE= z_c_41AMWFD{|JJ>`_*S!l;eJ4KNC;(s~reE0(Uap&%u2R?hLq}fcqy{73BcKO6W(< zKG_Hz^M@fp%iy*k_7>bGxJTet+yS>1p|{{x!hH+wdbodrGVVqh3fx_AFNFJkxKF_? z!@UyjU&5US_cFL_s5{LVHR`z^^JcmO}ScQS>N2DLF8G6Y)Rv&3dnT8Vhed4)Y z9ZQA-#5np_FkCPk0_~BZT^=TjX8_v))*eh(+}3=?PxTP?bbHmDVjfh3TVUm_QAeClKOCq}yLh%+Obg6O zn~l5Aj6hggbw<@34mIK`I4z4n>$1c{e(fZ18keukXZL*;aDFk0dzf=gw4V;Raeg(E(cKpMc1|5Bo7sXHlX%mdA|$k0z!M3+k=5Wo%=F{EHSEvwQM|9C zzboJkeDZ$w^>BNOy}=100w&{UB5DZgt{4J1bPI@(XxNX~N=Vw}fZjf{ z5=I}0Gk`Xd5HEr*5(5(2|7DU82y64H8{f7EEP7s_>V`W8ZVBNJ!fl0{rMVC8iEVlr z(0rjlD~df}*YohN5|bM`q2mLIZ5;B6uX2%P!Ge^;Kq_#_L9ffwmZemW_PK7Qj@UtM zqL1N3;TrbZft3Q+S8z#yNl@P8J8-hzfm4)@6T^cC9wX(B;K2b87wj`RfCr>D+Jsld zl+s{oR|LlY1_H{Vc!#W0~kv>+@#v6iKvH}Wt-EXTmc!FTlweozr~A~rHC zKX%0fLkIHu)VJUIrmbFw`&L`M3b(awj*^I0NdyM0_6%|q?ghFncz;8dQ5V=rbQ@wweVS7o7fonsIYRTQa(LWF|^gz&s0c9HCh9@zmdO!#KV;XS2;%D zBcxf0uE^^7Z%Y>V5eu~}ORy)1cToC3YhJz776Go!ut$qE4KvCS!a)G16}V(+k&BM_ zxpk2?=Hn}vzqhB?&R67Av2ey)o<(f#YX^q3bX%deYxURdcFn-PHc~RYZEmJ3A>b$L zqHS{XQk>MtRbds(AE9ZmT;N5TY^2Sq>hufiZ0?W)5jeqTr9Gq+$tD|%SEf&Dc~Xc_ zX5u^d)a*JqXPDsI?NxuhCc-Or@9Ln#?rHJhZYrH_I4*J}=3A}(;u_ZGFRx;4d}Iyd zEZ4{^fg^5c#e`-|B0G!iwbc!}f=q;!m6z&@?DX&&7nB~?m+NlWov)F%t|vbg!s!&l zMBK(f%a7BcgwTWG5sH771iHf9kGqP!>LbnwS_J-*YtxukPw4-5O4JWJmaTDyde1CgYGQt@KnKPd^euma9R4TeJEkc8q)C-p#|{?EY$j6dMI7v zhg8!i8tLc|yolpin@I2IaTzSm!`2)#)}Vaved`_|a&yAu<$0Z%#sea9JuwVL?6B zGQJavL-Y70_*=;z4LhfYX7fjhfq=i7r~FD{!Rp4WOt979yro55zbdUv*0T0yY4YB2 z?i^^^_xMSJ-*4GatYz=r9pLOo{rm=u?)JleZo`pjX5VGpO^oLcPcth*8>>wWZvhwI z2((3jmOBcgHNd6zcYQ%Fb$v}djij31*BancFCk=3vnU@qO7`NqF~!^5373D`IA4D@ zw4ZY*LBR+QxK}s}*5PAr!yW_JUf?DJekaDXmH=lt9N^lp&b1s3@NHcIp>4UA^EiWb z1+dO@{%L|@P}0gqG^=ae7Vzo~!XlvDoKe>5=K;yE;fUX`Atp?>6WBbb?2-(u?MDE4 zOjwLEfV5f7>16{lEK5tBC}*NACTuZsMZ0sV<^YD+Fp{77F3GsNwg&=SK~$Jrl~Fbb zSiI#(fG-f0xh>wTEKOJ>dIvBfzB0^eX;5MsM`~Kf)ld@FkJ4x}`x+zPfYpz+AGq-? zto<&c9S7l0yAS&q;K0iP;Epx~r%PvmgT|l)8w4KRSyky}xJ^gxxQ2^J-_ocBi){=V zgLEg9Zy->>0~GM&aY|ClwB=MYFXFs^WurH66!qUq6<=v?DQTTjEiH&_V)=B~R(|!L0r5`0n zK}BO54LVJBkY1ecc)=f=Ugkp^FGd@C@x|G9@ih+M3-Q%a_kmgp1`v|o?2tj-Dcv}a z0%sFk3BHS~Nykp$rMKw5Z5of({2jGurbR6Q;+YK!7a9&?6eAv1W57z+T)|P~cp~gBcK~IgZ%JA1*}hM*C)xtj zL?ynF-)GfWdW;MOSocL@-B$p(kxz#$cB3^Tb9~pA7?HLHX*_D;=SPw|Q?umGOpJ}5 z$=b{EL>t31p;6d(UE4~*_YmMy#c*Jm8o>8h9@dGSycaZ*>t-CH)V_`!%Xxtr zQSQKkFCJwp3*|;9`25*e@iTHH89ZO}{i$91 zMeJ1roJS7>Pbo_?62|LcX}(Jjye!ALMUPt>=Vtf1{%JFj-5vexlc4iY5&nms>_a75)S;gPi@sp-p}wAi zy{zRdvbPS`_iayob zdDHL<3pfvM`7m$3w35xeY!-ZRC7XGf>*z{0qXt$Up8oudc}o=BrF}C8f@^O6kyM`+ zYBJ{Sg~L5e?TJA18e_ zMa<9Y;Zk<@7$T4Qg$=kTZ}=pBq5IQhKL7gEY@}}O5p8TF_fNAlntgxD_Jdlv5o!hf zFge(laX0^rUTVheQa{`IiG*9NhU}ZDaN7X6jt)bj%-#&5|@8{3NVTU{5S^x zv-lh>yW!*HW$sU=8W>Kqe24|lcDTnjef(&s2$)X--73#W(U0g#&>;6hd0u43|=ST zF&G~MCc}rdO()=9cYK;8=A&&=7{-SIcbWU+_)n5K*22ITbxHUnL?w4Iq&FY7BTm>>jpQ_OTC=xkd-C^acROT2itm3t+L!#@7Ca9?@764Et~d{X)<_!@q( zGg-c`nOBk<721Dt9;wQK^wuKm?s1Q2QMB+|`-8U;OTidpLq8o)$*!2Wxx7+xC(UF z@8Bw|?xgr;a4oCmAmO%O?YVd|5hps;`_)%2o=n1723)^F{QcEqkozUL9<27d_Ny<# z^^gwd#f$#a@r1!X3Rkl3^20S%cl{8qnY!y4xH5Ft!HZq-Z_!hZ04*cGPWq~SpLQ}a zLQ%Dl$n-KcVsVd-*Ckq_j|)w zU7gPT zwFcbBUffM-O1G8mXsC#Nr9p$P1N|eG!YVxka`jV-+1_pc*o++_?e|Psi%z?c!O9+d0Po(+5`H} zW)ee4Ii%snK<=~`wHi2EORTVo&38qI3O2s$DI62X@_uzvr2;Mq7y7mvzeV_^&1fJQ zGu&i0ex;i;$d2+_9J>(Wn!yY%Al>Mjg(E3THyXm+w^AWp>1J53sjb;GBdKm?Sv>;} zVI9r&Wt;;X((^JysOHL2q^m!I-6d#l$(*Z%(cR(wXaA4rg{&I;676dH%^##|t{;Kl zzD1pssm=~pc5EgGmm+=5(n~nYD651Nu@=5Dq{Vn4)GG$RQht!0h21{a7V{S`2@f9= z&VFOY;K!bj$7AmB@HlXTrzkwQnqT(z;iYLE4RbxD({Y3G*xJxk}Gaqe&y);#4P9h-^Z3x7>$3w{0Du^}~EY~K!f zF}Qs7hVO=*EGFNxJ>}rq;&tq8Hd}0LES~r`W%h#G|3@Ib+IYsf%?UX3)4u~}d`;?i zLS786SUvf>VJ9<4%>M{eKx5y*^rVBl8q8Up-IKHWaKKXVuL>h5H@|1;>gNMv3i7L7 z2t*ZR^%Soz-0A6Av*B)VHoq8%E}+g%SeeHyjZK{w+9JJWxJuGE!uvyK2{zbx;*Bkl zz2entvp5o3A^43c*#EDg!ERj2 zUl%5{n|lY5&qK2yZ}PjZ>%!udh^yC-FMjBYOi>#P*>~1YZl%l^O?e6_SB<7j(o?nr z`Wt}$d;Bio*N5MKu6fitQo;{L^F4=r{SI2wpMGch*T0oEVl=G}X$KuEY6jEa1MJ`5 zhBbLKz83KV4p+@U`t-{1(Q=|jkFel%?Ut+4pPv+s_E&A1hoFnHEJ8kTS?EP3$k z($TPSqhW(*-_}e21EgjmZ->p(QyoT_K@SV-^{7AP1Xw2f+bFJI{`=N<_^Temv^ih> z_g%2k%f`xP z!lxDFI2W&zi;7BC0=^ykCTcgiDi@3>6KDC#H`i_2!L*|}^>{#Jwu#p3pjiG@Su0ki zty^5OP6hzGeJcQ#r*kcX>(Y9XI}ow+>({+^0F2$ zgXUFQN9f? zf_aMlaFM#TuD%*>z{@Uj7Oz^FyUtmZvubHB;Q59Avw2*rYFkc&yG&!PtwZQhnh3fg zdf#I7!FpfFzlEXe=yO6%g8uG5OOQ)kZiK}CP133k7aKj&8%B@ueWOSG$mspvXT)QC z9lNkA#Jl=B^h2Yc8uh+AHq3AbbY6%4-dAo%RE;~m|7Ms@(i`07U28i zJlJ)yodz4{p=U(L5mwd}afP4-iLZy)WakLxEEOA3??-MQa2mBs4p~&39J^_#jfMUep)Q0zg;1vbyG8x_4-CSH9t4cjx;mR>`oobch=y(3Zi$%~cSEXuHtD9A$C4I>bTtV|@rAS5xn`5_V>` zUv1bX9Gf%As60QEvcRedo=D9%l#|ARw}MeJTx1YBz#09m`!X3R?YNuBgC0hEZMmyV zH>cy?USEz5tnBJA06x>hwR;HgwODHK#A;q08rL3v8`^Xt=;bq*bH5an!+L5Fuxn8F zGC2{h;X^Y99m1XEpaj6~Kl>kKZP)c1oQ_$$`rwzwGb_Vfkm8gzFwWTr&OU>BtwOy9 zc9CP1_AR(EiblIm_K-72q8yO#96t1eLBp{lQ5lxbL)djmA%~!X*uD*Qptj*$G2erb`KBUykDpt`?V5D5uv}-tdvSm$|UgNxWxzuHT>rm$=d!o?3TCmDw0o4{VxK&5IEx% z2<#w)(qF}L;1l55g>Hx9I7XtoAg#XXpR8<#2U2>9wRW|+cPlV5?6YZR>s5N{g((iB|DUyE|&M$=bLN~KLFy}sIBie6V=Zcj<7OR<+$Zh|U)d;Lb%6ZLb0y-7ys=?k;ng1QY*UjbAH zdYb2Ra{67^Jx>0eGM#u+H5~n`HF>3uRtL`BY^_bZt9rK>z*8B|RUA^m%Ba~!L765_K(zy1EMeS;352Kbbe;c8e1YNV!w`oPvX{F=?R;uZYq|?fM zTD!Wjrxp?|{JtcO?H_N^%HK^Rw34>0TuCQhY|kZMrGHO*c`>X}K756ZiiAq?-@_&FH3%(M{1e zbknN0xcEQl=FM->&4GXYf6>i|p6Xk4!^Z0`zI3|aout)(hL+{l;Pmh)sl_?aU!2OcI=UsvJ+)o^$=e2u|L<#M z&}TS=IfYRR-#*Z@T&I?6|7IE-9gJR9kI?u7Np;`5gV1ZsOUpNHE4RBD8KtrlqpfvV z*-o)1Jwm|UMfiym!E!{xCz3W}p9gpT#bGmSxS{ON+BU`J+Li)6sF>|1dbh>HML1z% z=e9|<;I;|OX9-7MA_@}+wna0)BuwNQB5aO^Fy^xh6SEpj%+(}JbT#lcZv(SyBRI0( zkKPTIFRL^;`{Y3Q6BubNX1jTeE&KiQM+w<)9jlBrCqateuGAqW!3^7AN}@SN*V`*m z+`hX!2?bq=q(yx>+XN0irb8nh+>%Dh4^0duX~9!Zk$tNASAHlJJ_Yt(+n|xF?XS$0 z77Clw2D^*DT=m8zvfIy_j(|HgYp6Md*g2f7U)%qAk1%vBmZbT#Y*SQef}EiH?9jlv zhziFJ*d*o4I5QP0Cg7HgkIRQ1Kcf<^glG~KtRWk}YO0eUxAz(>B)xGkaBpVQ3{Sw8 zc_5IQX|f#*%+Xye#1|N!2^pTixJ-j$zC>x_!?snzaVq4i*3Pa_#Rghb(tuY)*+qElPD96Z~RPqIl*}mK{+uZCqj;>p6Ta`akrB{X6*xONM)Zb&OcpIlMn4L`#IsG!Jwm3P_4!A3AqW4cdGSZ3xB@=RXneHl1 z3^a*N8J-o5Y9TV z6T+G7!AA)+mOvvVMnDhMyr8@cXf|~qMGx#VzbZXzd?aql zOyEV<@^aGIplgHrQF+~uT* zO@j!FM>;OBCet8tP6t}vfLiimuJdp=Uo&CAKzgMjWoEFAcukQjxQ#q&&~0H_b)L3D_s9ILG8d>!C4rh$Z(|MXT`)O!ILMhUZaM5^ z{VB#R8d;to@|?3gXYKDcF5*tK5^@5Jca0*duVGom`;T!pdPKeA6QP)Fp3! z)84qtjGXQ#z&E!2@7)Ha7W@X|EbLpeZ`^Br+R7hOZT#q1MK)e*@@h+GZ9&V`OHm1L zSMkpH(U$s#KAyrpM}@ZMk@1b?8SFMJ5(26vJ>jh?)>lT_cC0(8Le_RKcx}=tb8FMn z(j|;ISe~8=sF`9 z(bsSvdVWkWri2$;l$dE5InT^!g*0~yEJN&pb zKxFaqMpr;ea|J@uasb7hu(FZq6}UE>Q!P8)vq!k@-GT9Gl)3}fG`giUC8zr0mElNO z>pVFedH5trE1h2%n4G4}rjQH*7iA4?qE2OY<($g7&!;~OU|&bC#rEmm z@*42#jgz;`v=mCHL7a8l3?^gpRN@{;_+&X0+`J7Il%`zlBG*diyA$MU_ZIi;XEL?| z*49NHoJYJQgzB(f0jx8#LX^q!$(d%wwBMW)_8~<}1>-db*KVLDXoUPH`Na%xS21}* z8jELfK(y|GEM02IBso1KKOk8JTL{K0=!;wJft)kdET(>~*+dSAN=W+*onS_GaS8VErSGvrF4U9PB2i+-U%8hm<>)FQx;WcU=ETLNpG_$Eq zi^;#0A{Dju7$2M5rkQW&^5w}|Ok4rvpxSz4@?Xe{bbVfE&`ftRDWs!;$eM0Qk?e-7 zRW8LY=O8uXMD9wWRd(_|HqwzZc}S-HHmip61k z-7jiesL9rulciZA2ejr$uG-}{RI~bzZ3%gcwSnQlwI%5xtd%xH>yOxO(Xvo`irOE{ zvB-MMR2yxq?uJLNm*7OB+fdC~LkT2GV||~!6(1WG$uykyB6?q37q1ZBW-lgHC2QtyylHKJh?RS zcQlGV9b)iUG>jn7>lD)9?-m*|=+qMrh90AxLu+D4s$g>kcyLRNZ~t&z=^V*+xZ9jU zQS*{%LSD%|{Qmgv)QTv#d(L7lm@8@foEWvjF-JC7lqo|Gh7v2%Z%7Nx*31n$JnOmB zp4j54Y_=}GTl5TQYi#ePmnGhk_c1>jqV>}tPQv!7KP>qYnkmZ?e|MUOp6E7hDAY_1 zhfG%^lN`6QFK%((G ziQjkso+tAV_6k`=-t5V$)-Xqul)llEwYm?|+Bb44a+Yb~CKg&)8cghy*?8rKI8Uc+ zGph8Y+(PSVu4ZZQsawlPho4Mp*63qeNOilKRtioIJ?9LT6VUqQikxrqPr5VzE}wcG z`8`;8wyV*ly@|n|{B@IZTsi)8A*SAQAt~X|>6E`NOXe}k@W$mbPJvPKJF-u`iF{>x zzR%s=$d{|<^PLNc^RI?3NaV|1$42{do0df1H;pT8A*xKqLK~JeZnh`g%HDH4jRr{GJcGGmd zU3{v=6>IxedL8O!SLpu|x&@kvz}s_W--g_T`*A)ukLKf4aa$qN6u*YjpsBbGYZ+5M zJ8f%M_rI4UNRJL{%vVcK$nC24y-o5LpyPVmh+gJY=(EYEX0KWCK@*3cG989xe@hgM z$i;j<`UB*8;BRc_yw%3IN4^|uypol@bTK=_twHL|WnNK;B8nUCH$YPC+|na*r~7Au zj|MH;uC6-3(w%9(D=HqFh8G1ZZI!Sq9(ry9DF{DJIBsa-BvN2h{yx*Bgbw*9lY(@4 z&@n+i;NZP+=!K#;y7!%AHd6i}Meu}a+)%ND6mTAs#t*H5i}woPjq1w`^I+ELAY9mK z%88!Hpcc>UGOOtGzO$Ot9q%U0t(CDsG_}ZGE&HeSfeQNyKfp`u$y)y&SOw(6QFDLy8 zYy|6{{Xxb+BIgO!{s`FFQ%lJ}5=PL-fw>>d8AxW~e?WM~R5!VVHTtfUOY=XNcj2xJ znSWix4^>V_3wXxr^^7=6hJJJxDR9C1M=RbK&#oLMWEZml!sYt9|mN7(mYdHY+w#jL}-IE`jX;-GZ)D8u-Z8Ap333^_aZ(_ zyNLKXQ_IM8iPh=4c`Cv$WL!x9bf!?*YCW06f$v7eUgfp3{19ce@fuJ|ulkqVYcu=1 z9v{B8@Wr9+pdo{Dbm$ShNk=OoLtl)8tI~wsQxefg>tr7Gxm6@a zIilxg`izXA7n0m%&!q6$W1kVhO=yXgx zski9ZlYm&(7hngjmm8Xcp1>i8IFt#O=!t=gQ9fu}Gw76WKwsk&qIBvt@$2QrDkd-M z2hRheHudiZ3HCBR$(QbiG?GL~#}R~cT^uC7CJ6S^&rLW8y!%q^rBHLmq@DR2r-~L-oib+H!`r z;eBL5){qY|Co4U{3zA@sX+E+*rH<40j@z#WV(VrMp8X0+5jkidr`CkiKZ>6Y4U=WC z#WHv{gJa=_0JSHa_Q#hK>{PYxlohp=;ZJG(oXmv9;yGj|S*KPl% zs`bnq;dX4fzPUrd{d$eP)U|npL7{$YRe9~q_0_m{pQ&>ml-JeG+_)W<%Ms?PBuH3Z z>sqmR>D@4LF>(n}wyCa^F(xpLr1t9dI7!}AXWz8hwE?X|{#SVGYpZpm!JF5u-}Fd% z**d+rbv4y>BMMD<1uEIkdvLU1GKYpoj)^9-p)oUBiL@ms;n4XRYAaF9wo=7ptHbR? ziz3(rh2Iz*YNu@835AaERBG({B30Av5Cf;=I;W;-C)m)S?Lmc`z52r^V#xl!zqlZp4 zvU?suF`&J7Tuc{n@)((JbU6=WMKxB5Hu9Gr!KrkTk?C?hNbBgOQ|CDYPXF80HP_f2 zRHK(tgPHg2n=eqKtp-@KZ};S%Yv}haNwDRc?b=+@u(aA$Mve&MXb9@VDU!#eF^vXX zfEEa_ppaAGN54N4ZlWg7Ty0CT`!1YYBVjoj;|G^UaKct`=tZic@?GN79m^m4L!h zdS`B_@h+4U33a(}>fUjJ?ZT-w;}@T%SN*&d(&rdcS*@ZSr5Nj3oLAkphYU&6St?Q6tG;y?8A{djFy96EFn6Re zi{aqNRevLsAuH1Q)KkA_?R)dkGw-mHe!?X}?;^XEVXbWho$UIPps#QoXiahB%|qOu z0RJ_nLcnNbw6)_MMtSU8lw4(0cHsByZUw54u~z5)k@)GvFV=Qd4o{&;XATg@@dG4E z$-!POf*On=m#5C-Ng||5(`b}&kRA-_p-)XSC{uAK`rF4CuXl(xaF*vr=AQoXq@#gg z=U(uzB`+WrbkXi52WeFE=BR&Y&2jDOPuKNfuQRj+x0mZ;n=Q(a);zymedb-GCr*pJ zLOdSQ2a4cvuG33DLoW-i$pVxUJn_21?*RYcNXik7h zl-J4^X<{A=?^uKUn0XCGJ+8n-#~N$tY4nVCwJoPz-TCg5)0l1RF7c$HT|KzhNZBpk z>Fs@Z{%pH(e*|i{6eBk!jH9_~yoYuo=9iC43X{fsicAqoUF*uK*XgpBweIRiMveYd zrPSMD2EDq}?!qxN5b6~kSqk-mnLSu3rb zK5$kebu*b=A~WW^Y2&wmw%ObY3Pam~y3|L1*88O2V3LSq;EHyy7Gtzt7?z>SSWWQlQVjaIa5wla#K#! zaB=SZlLh4C$(zy*r`L=(ddI9}_G|7gnKIM}Y(%!sqoT*CMZ>~o4crMhm%C%0KohWO zr*0z~>1#26jGWW0vAe3u?3LB)YRl_JL(pEg*8U!~FqHAx>@k}lqsm62w|+HnHVxm! zL!Q8Q35**!MY-5}Vl!j!ja?86>vw&b9&<9DEbX6vmH0n#0OQ@pQR_=v8OIPcf(mx4 zKPyzwN4|QUX(KVy2^ec}#(=WE7|Y7~8Oo|0x8!}S%yz-{t3ZDA8?xm6INf&~J`a3G zLQF*EjO0hl;o}J53B;LVCO`ZUSha43?h`MNj!fwUspPhC2RJV`R~q?ySePsA5th8q z>-pd1m%NWoY9MN_5Ib3i1NYaQ9&aEA<0PNN`$T9(t`t?8r|eV9eg!LqW<8~msLvy1 z6uU$_$V1f^=eye15}&QFDC@XHtfolEHlzS{P+D}Fd-$U-hljraZhs{mADB*%dj21# zf0$MXUvROy>mQ~Qc>kXNFMbhUtcSQq!~bFWaMbmT@bE`Cr1Ag?G_FzFg*OxuTajCldpma=1G+E3xjH!AgZSS*Pmv%fK(%Ye=}S)6dErXqv3Z7s_B^N zH>Sr&^I!UZLSgy;kLk-%NIy2JCL*!0X#IH$eu{LL$+&>=8*ZrORBFeb+_b0^t^LG4~=BSVsNoZl3zJ77jsO-tuGn;gTORW;r z!657>H!X^xtAKbiNXM`U3f4nAZ^HI-*J9F&&}jZv{%39`Dg@&Y3w<1E<&z58^+fwk zZ#>&;$!2g6==+DCW2DN$jRfDNkiBa80@CqEQ})SxB5E2mF(sa!I7!sg9U_K6vE8Wb z21dOmk3?F;KSGjf6yA6<(Ni5DJnnaVvpIIdGlz8ihe^lhZ@W5{-E57|K@V!RCZYe} zJ{M&e^fHoAhQU*0R?lMUgngWtS>IU zjbWl)1aA^@W9_ceWc20cGo)jy3GD|Aszz{qcUMRFO&{#X77$J^)p6QCne)uiya`;( z5K8sxr9yrLrAEthq}I96hHcs{8T2;3dSj&Bo|lMcyylJ1LiuB@L z8KqVab)E7Pmi}3!H{QnR`5XV2voC>T76iJSzmR`!`D5og_&3n2~%d3OsIGVhi<`}2B9W;KmByfQeEXmQd zjnka5<)AjxK=Wc>S-16-^&qE_>{R11pxL38b;{wL+*j7bt5l!YEb2{cH-TzPUuh%z zO0!+n1WIk#nP%34E7$9IUxL?+SL>-=X^y6No==6X17%&rjkZg$!2{=FtaMl1L^H(E zBwQ5&$FSd~GxAW+TF}QF4yFCPTL_%NTh};vMdPzJ)7r`s zGA(uE$0yWJ7;AM-%&@t$((SHEnTd6`FmGaCf4tQls$+XM3z!qA{9lW1gHC}|{~q$+ z-2lmr)`fWM#cq`Y%W$`*gvhqP^hgJcLLJ)Ot>v{wnjNU?9`pzE4(e;?_P)CQrbh@Y z#NOA1G{?0ezH@hL0!P!aBZjSLn*@td(-tXIv%Ni7V~?WzDp9kXgQd_Q<=r$gompZ_ zDfC4F`t%KLsE&(*b&Lc*pj&VNs75=(eIpt!7qhb=foW50_bHKbC|2LRitE7ZUWqj&=JN zP21#Qw{9A*(z-LHz#{ZY-2_cQA#Rz7+ojcV&p5ndH%Vze3pdKpNP7I8$>jE4_n;oM_*4E)oK;q*LnQgdIOZ;zg zG`Tp<3!`#CQwEw3Bt9mPqnazB5>|4W4@Qw3EdWhN-|SO-B~;dn`HAqU!JqCfTz$7hISNcV$hD;H6dx5%u7kOQz>SY@)Na3z#VDmB}yeC)g(iM8q0W$da3T2qUzbDubraVqN}|}Z z5S;q^Nz8kz+B&5C>tz=BG}yY6BDLtoVre4ay2gZsw& zmCGS1JkDvhahfMUBS|6N*@LKsk4&&0`ZmWf@4cui)(NP^Qz$1IVkCQyo_|BDCq^%b_uB zghrf+w+`I}|FV|r7~XZF~q~FhN&8*-Vk{vi}BP> zaOaQI@=%8>mxRDv-nN%nsP<IfWc2wnm#9<=-e|Bx z&pw%F3YGXMO8l0WI7SS$tPU;v4$o>a(|im*g>_}78+pIqSEtpNw1I(m%Di8SPJ=sv zEk*rN&eBVDR`pHZ?CE~DMQ*PhxoN0JeiG}PwwIXq6>t*lFEeHT{2AFfn>>?%cd-w1 zh&s2;L@!6;tqbM;TR_oN4lW?wt-)`SdP z3g*hP{YTYn4QR#)HU-_=|5PN(xd`yI?YBJZ??@3?bVG``dB z+hbHEZX0avV=AV3m*LwtMpeuH%P0mW zBdr^0w}EOmPdkU@oqi49J~OI1;IFXjuLo59u{I-^@`;?f13_~AuWcDISD?Dkx$Juq3=?Tf?+CJ1hlg;SXSgH#WQ zn`l0sR%@iw8a2hqOWb+{ZpK-woZW)m6?Q3`JliMeO%3B3G8@J>Ol-(%n6wFUbeV-D z{ppKZe#XJPajF)_Z2yysq1kKeMUtnw6q@ZTv9J0Y<28Z)*?3V1d(yN3rH+Qfa~scV4*E3{G_-5=^vw)oFKRfQ26^4m8;`suqPMd^M?2vS zETln)T@(Wf*0OPxd&KPuKXZ<4f&znPrLf=%YDm2^iBaE`;W}z=hhtHk|rlx6vEYr=MP60YByi zhI$5iVo=Y|d$fUJ;0orQ+gHo~xgZ9a~g2tFEUu(2RO%cwQTOq`(ed%Z$F&YYFlhh_Mgm zf;!%N0c){IRftl!Wv9lD&VF^!(H4kgDqt}12KXq@T83Vhn(wu(9Kan)mV zN>!@pe0qAL=QBsGRg(k#dYShF+LUo2Bw=GN5a;gUoEtmd;dS=)iy7^P>_j`7ZblpE z#M0sYd3R{#9D6|uya3$>o(iw^Oc(c13a6@#iU=MVnT;Y@;h|Cgr2j8j1ohf8He=5`A{p}!r|S1p7Myq{Aq*cSc6Bnf?e?GJ-U4nAen%0H6(uF`4C|#M*+C0VIuhqj!Cfj)b^H|QC!HAEGlb!gUYuJ)As@0hupR*@Zd-4@L$uwA7{po{p#FH=3udukB zLIXRw2jA_6n4HY?@fjM!UOU_O!l2IfY*XQeJFDX#%qxh;0dEPsgZGXX3`KJdMyVq^ zZO?c@9o;j2m`Ochu*5Xma$+>_k}hG)73cTaXxS>+FO~Q`)p3G{r!3w%N6M>$OHVp8`OKyv%T%+*lB!c6a+%WHm|sWtxs*fFK-bldCfpKVbB)y;xd zI4u+v)6S}RD`YBf6tzH5Y>6+)YQ174jp9OUsSPgPhV%j#=+ycPial|=thQEz-F_X~ zD}wUz>bT178*Bx2Xh9WDv#9=Ju>R!?^)Ck6Ph`F2rplB4m1(m#DDqzPA)Q1Ej6$te zm%^{N!-wdxl=owX&4rk)ku+UjnDJ7{8?K>d^nG^cL=gBo~j4H*h~B zxd`0xR`HO=FWwm%OFm$GG#dC1B*{9gH3#?^{z|x4pq7~f^Pwe@u`ZQNgK^`ioLZX@ zU#WABnA_F%PX_D&zxZ_;=?IhfD#E{TaYm+^N+#8y5ltF-ux1Cg7&PaEw{4F}&p+VEkO*c~#4sJR>8+ABpv=)QnS` zD-5`I;HW+pYrUoem$#ks>s@Aled?Ni`3*5PdKx~_0=FO@9$>Cdj<+pi&CJlQBm>AH3(5%X)Z2Xy1df7Zh6fzB;>9qG``g};I8Y^e!rZYycx4@ zGbdM<)l@Gp<1v7@{y-M}dFlU}$I7ZIs>W{c)({z9;7%MuK_^Lp$eWl9zPec%RfYTY zup%1DWI|t{Pk#0<7Zz}j23aJ3B+BvX*q7W4wFy#AA%op$*9%HfqR6> zF;!+Y2Y4T0-U}QZ#&8b1#q;zTPY%Vd-X!m~N62Bybj&@7$pg~2gY7ZYvPFv)iIQu5 zXMQW#)W)2v{Vnp+R_7%6e;in5Lo9T1S>_hc9S2-C?C4z6&QvA28G?hv5fjzXx&pV`X8oXc~$Yq`f~M z!tJUY%gz+=)CbVxL+LtAC zrb?~>a|NIWgm;Pu6hU-Gv}@2xcnh=-EuIA{>**1q2{B#Z2bLL?5ENe;dEEG>9UeoJG=WvEV&6oTx|Z7_BS0y~Xm9 zjk#j|=+$z$U5;tQ$`;cRnW^rOgP;_nOi)+?t6YfUt`+hdAaZRPc;>w z+TwYBzraUPaIN%X1XKM93?89JtXkKP+Xq0Ke~=0bo@XHohj-%cB#mnkH0+PN4zH!R!zMblet41$ zIh^T)hvDw0s^lch{r&WPNzvjU9*{b_-vZf!o?KqJ)Ev^^OIB7B=Fs)iLcct|m&L3~ znmX2&rSiPUeGpSfSw&xlm?p5ISOb5Nwad%a!G)x>tU3*VfxtuRKSz8o-@MAwc}wzX z<^KPbbN{@rYjd)F%}6Md8gtqDvXV75)b=r`VMSTd>N1+sDwbE4neA)H1(LhDguGhJ z$*PkJVIGamS~wDSqKv@G_h`>i=%NAeJdTz}%6i4%t6CNdFT>wMk_;}=3cnEAM~7{B zKJRnrl^zXgRX1j7yWx!dTj=~Po|*d&*dod!5WS=|wv3dN=FCHrfSHGs+k_8of3e5; zQj|5b_5QG#h?QaSKEq79X9B3U(*#5y_3WICo9p8BdYm`tC+Tq7pq>m>3ZJKlN`fxl zFYB?3ZucaKh!3gt{B5XSPJi_bqnj%mJ$KAq6|R1J=Dw~p`nElx(UZAo)kuIdLVVhpAu@77_aE9axQH{IrADFccVul$1Y747@9*^@gy_!Qqp&-*Y z5%mU+)1t$pyV?}=gp(DcCz?Su=$9_+%h%7X-+zi}UDG;p_P>NH4Kj?|6Q;v2Omt6} zTOH#<9&ta7Lp`3H8-u@h9vO(9X-XKkpP@AapX=HA>a2HdqMU~mRND0Y-?F0ifzh$L zJ94likb(Mw(x**r^xSuZa)OW95}hdz_ht;*P=>#dMwCuDCm*3c59J*9)1IAMXHjmV zeAK4qH0YN~dz?N-r?zhp@*H=BywEkiFAb9Klwr4DHcch@*9ViMhfcr$rd1)}VHuPu`gFE)+MiFsJ-*2sr%Rm(WB zpe3_&H1y>%$sJdCDD8}#TXhKj@_Emlf!2KwI?Xrj4E|c48>HBEN9D87iek8bbW=jl z&e5}=NwLRbxWQ$dZ{R|a*Ys2j;(TLXo^o*NlNNzS)umgwJt3!3bWZbU??1ydig?of z$Z(b=Z-y$C{o)L5IJlxPzVW+18i0F}oI@?4kq+nm-RRlAKpkfv#&XDys3&VXso$st zx%)3*b`tUI>A8)b9i5?Gcs#+CPS&B<9G2Sde{1_&S~=N;in=9 zOEWdf((#f~G_6Q3nG&azPNn?#USho_;GD$Pm$0{|bhRb3x2KYv9>?7^$04<}79#CF zhmZF?En@|`%b}lMU0qgLN{S?J-U2fg(N!zqLJbwNcKP)_gnd5Ov=JkE?p|%SSM{Up zmmX~0uZ)nNHkEZlK`HQ5tbtCu=CF7z-F3PTny$EFc@0;3s2{01_;^>+N}jtqnu}`8 z55R#Nm1pb)_?h0*lH0op`t5<#SV(gsA7{jI>^)*fgrq)Ni7EQFEfKbdVRECg0+S` ziAiVrg*B;??}Ow9UyE|$6hoMHenEuO?tdxfAZkxipEvtmsX5O2>T66qm0OSt+n=wx z8fi|&h>!=LUmlWEfMX~>)wNxI39{=hR#P^VcG|9ZVhJuiw9p*|9rQ-##T3;q4H=26<~x z#^+kx6gJme@d$sPBX_lZqHUH-k-tH!Q{a2S!d`;}qCBVBQG`O14U@WK5&Vresu z%z~vlCAz1eDwRr^j$FQzbO#VbENc&(V(>H9oj{JCNk=VY6~wI{tkaI&qoTnXSUzJ- zxlGH)RSTWq;^QhfZ|Pux$v|bnc_sdeflQ#aXac$TTkje3VrN>OPm*zbUa0ZO3Q#a) zth;kb%f&%Hvzg@;dD1|8yn*%v8rMLyfK3`VD%zxh{X`r1G>dJfCtBA<>>#9=|bu`xFjpRmpoI zFb4FU?odPw+hp}Q+kpb9#0YQ#Mln>{3t-oYUd~wMXx+~=-QwLi4l!jXJ zh9=%bH+PB&G@l)|lh^82BcyhVq43YQhVpz(_wE?MbzA!fsXB0^f_f_4 znzNar$t2|iTqsN0;)C6PvFNeyIEqt7K_@D)IsyDCDO%F#OfScq{$kamH;y6}8un}S zSv)ALprrl-ow5RW!fIaIcX!zJ;0yE(Q&8t(4O5Oz(V4p1_VC~L2K(k`yBY39x~Jt0 zmZG_)OR>1^ueV9Ps|(rc#Y@&ymRzr|p{HwZ(9>Mahb%1pb$e-ba22jj2>tCa!MOxS znEmR}FYM?0v%&SHT$k>DFUMR&6T;e}ishwfPX99hyR@ojcwY|v>iplO5AFDa)|>xZ z`qDLg1^@l+OaFFUKZe1%H#8`rsXNu2Pxz*0`@&Rn;qn!*h{C*OwpUbOUhG$=8#gVS zL16GyhY`jt+U1zX_M6jF%r!S;*FV4iu8)>u^8}37k{W=;^0|EV;^n30WOZMX7TTWl z&-Z)vyv9J(9l`n%Ki}99W0tL~;7c3Y7m~H^0d@ektO8mS4EZ$GAg4le{9{*yuSyxL z%%C}&&m(TOW^~jRM`3k}*1ch7hpp3|Gds#(XKnzJ%xg;2?0@;sRQ-=%yDrSR!oNB( ztky6$%Bh2O0PkF-xqIw)%n^0M$;Uppnd_|W^2;)5n@ygr!s-;q=s(&t<*|P95{~t) zS02k*J^6!FV9A*U-zjr5=wb>8$&qXWG<)ycL$;JFWd9aC)*jMMC{FZvFf&;1lB>L_rI^ zQ76FPRM05~e9yx7NkVnX_WbI}HCUaFZ?6JA2XjTKI!;bkd?6htla6~}YLx*e8PHa- zI?`r#e47NlH4A#{AscC~`x(jyT7OqtN>F=;2@gy`Lv)M83m=ZUj5IRmYg{b?1JO*9nB;R=WuJewO@N3-no+vc+@%N#=WJ7FKRJ+jtacrZWYi6A-x^r`QxkyH4ua`RCV} zOZWC@lr$JT%F1VkN8F)HU3=^Fx{^=`pu#&^>I1|nE zOFX?M5Q!6U$vI#y<@@+9kKEw-^EX%}z{7Qmr{@IY`GfcWP5!HK#^M3r@82l|cHqoK zQX`?A`uP+A{M608JFkAG4VV$@3>-fE)jIVv>CG^p(4P5>1@<7~S;MgautG;2Ebz+j z-TC=v)PvdBr}pgp&EG{rn(2~ zAI#L{>GqBhlr#2XS@Oxr@8fh;<4bX3J?_hN3d;CovNsXVS{c(7=v>)YFH?`lRs7mG zKw-EawO!ikxsVTBJxC&8^|03jY8cWG0s*C?dG?oPZJ8;z{>*rNSH4lg+yP8>2mP*VHYZ>E2yq z5?k4Db({w!3g8(aKcwES+yO0ARV69MrhYNapLE1h#{5aK{bZiXF?dJwn00Hb^X7~s z#1?lZC>awoCX^;i_-;{(vWg~Z0?B6b#?f_}6?4Fb@03!{Yxq({B$vb!KPO4H?4&oB ze@^`KO+_5!ejnt1utFo=yVH4mH2<9#cn#8@7&wH#CsWCzYsb|z$ktd$RXXHpOsmc8 zk>7<>m42g>J+Nl=?p*whE{ApzF`IgK-V^*z7NTmHk(D^~VjbN!c=_5O2jzh#<=|NH-C`3|-tb!|lj&cSu^vINAQ@;KbjWIJtobgcHs|F`%^tPp& zZl>?ux#oBRP>(FI=g@n)vtc#f?&{{J9W$#TwK&H&1#60B@LcaSCkZrh-Qj1IluVgwtx988p5N#gLf!( z3tCfFy%vi=bti>{z~aMtXkqVQIYqveWbvzbMK1#^pRY~7<)>f87|qwOFz>@G+?RzD z3~GRJA+LROx)meJE+-%A>&{TWK@k$w0+Jd>X6c&3^2oufx< zQom((?Y3xKcE6ydy8-Iv2N*1&qtI|RcBiPZV_N#~a`iS+lIH8dxGlN+5jbs+$<2luMcxQ+8oeZ!PTp;h zuy*Ncn+-|If+SVR(>FGFre6*RXWWHnv-_S+x-6o+{9?PoxK&#?6GrF%86NVv2?-WZ zMIdi|Qf4nT^V=58%StO$mBrs*bFd0h;+;y!Ame&DQ9gFaIf){c-Zmu?DpQt4^9#u7 z(7}UcihdvVv#z%CWc8G-!SmIn{L{INqTll(*pA6U{^=yaq7gW*>MV|wOUgXs5z zEI$~*J}sQ>ScG2!V~B-6E5we-KOK%u4)eTi$UhxP-H*53B zBOmeZZ^gLDLv{#jIxNw8IPQoVf1ue)JB?pSCqi)u*iuOyhoU zRHKU<@z$5$B7PqcH19BPdd#!zvNkn_{)zh3m_z#C>E|MUBd1=AZ()4IRv8AOCBymC zxAeTF2G$gZaiExA)>NUjV)ewl%h5`V5k~wOjMf19*3=tKR5L*(gePI0#k@UXZ+&?h zTU^xPOXmGL`dRiT!`qQxMyvm#!^Vbv8GU&*T4JM@ekJtI_@c>2CN<@0JYc z(T8Xr8t??CD2sYE`iZbtqi@3dyCPnVPQho+bvcDn^#3f3-rq0(m7KmI`kCmKzLftH z{dasnhySBuDCJ*+C8y!nte8^#eSkl|mh0pE_mIe6^*w*L@7pyhi}u?0{@=chxeV$l zQMX5(91w*zWyF3a-5yI6dHnAj{|_J4#o+PF}DE8}Y8 z$b-LkXZ2~FtVS9=*HebNz?aceG z=thitK9KZt5X~9|f5p_dLbCG~=#q#onR(w7e|N|!v>LQH1936*l_RH zkx=QIKz$0-zfHi}M66HI)G2ZR;r&N=|1%p!8`qA}@b`Z@B18)O{ZhPt zhtlKyk43jlNfv9P8fm zn|6+b`u$tncX21)doY$?2pjDKoD61QZ%i~Tp>${Do}Hg(otZ<_ zM{{D9_(2_RqMU&=y4c^I8y&;nMZw#PJ#riB7=9$gZ$8vfgF2o-Yo8YC{1KbNom1u` z-lH=#)=i-J1~>L?1!rs)4jLL$9)zu9uz!`Uz#kDke92Liz9(x{;?X(HxwHL({-!0( zc=AkEc>dO@&GKN=>$zK}uChIBzefE?)++1KIWf6iaslq1S97S%Iy$E;t>cgkyKq#Y z%>bTph0nLz+OfLS<=T@vZIaV_=l#>_$JoQ#Etbr~HY=ihABP7bariz#F&y*f>&bVn z(eq3v;W|nk4)2e8Fb4>Az^a(Z<>%4P(AXrRRh!VNZMeyDxIpsLVjzS$cN%$X5J#-< z3~_4=Xjg%DA;wWp-ihppMdgJhogsehVYv4RG|Rxz=b1l&!3L9C)YUfM(wRYfgq)%V z&xhyz3ALWL^dwJI&l#N<1zyj4QP01z+7!siQOJK^$?qfAOyv3?YWZ8{9K-ng(Ugu$ z3GuuQ@1Mu}*IBKtC|Oe*k=y9GiAIm-{A1+Yg8GpBtelIx1=0oCouWPY!b!+bvyfIh z##zDd{ek>Qu$*Mh*7zk0NfmD887V=U$eKD%lD0;`$K+p!Lp7$Jtz#i6+72lifENAz zF#5M~eMh6`&X^Fj$4G+WfWj8LWY-KjO>gx4?l5|`u?$pifoe7+d=Vq*834|F z^Dx!?ZoGR9@1BHyxsCm>d}T7FnX92)%u^Zqib zEjQemwV}~-FYl*s(TY1k`GW0!)5~0LfDw6}+!!X_C^x+mRJj=#l$&c(^UZ`+_M>w) zS_k9~&rxOO^n{~xih?o|o@+PM;e13_Xt5toKxlIA{nK1yEMZ3y?Blnlj!JKySDR+d zI9J@!o@>gOvpPx4wdPhQe~x<_&Xshu=iyoLqb>I%l;SO(YZ>d4TDzV*6^V0DSRjwS~2YYev5ooFs5Nq07KT7NTGe-Qq4po*M z-g3PE9o~On0(wHPI=|H!lVGPZm}r*M`{@iS2{x;Ig)lLJ^m1iYaHb4LPQU2ODX%xp zcr+wHhdBh6HwkOv(Q%Ds6+DEGf#t~T)X zDVO<&J5;Ub@D4^j*P)(=&{o)qp?xU7U-R4%CC+m{#B(pi{Vl*o8Q@AFl+rq&>`+^> z(H4ofC4{=;@b0_pw89U>H{Xo(bD_NknDISUq5ZC9BjJgt>xFwyQvMFF=W>HpbI8fY zTL4F~Fe9PPhE?=@=6y)7gLZM*1jL^WR?aKBje|BejH}PQk#4DeLOKw5Y~wQsV+Si# z_*)AH<}<(B9YHrhdJ7UN0sIN=@}msLJ+cxIyhmRF~; z-kom}t{!Sw+vBoYeA~>>vU)XL_X*sa&C{j#rAxV7Z_PV+FEp@j1bK)f;=IRT$~$<7 zX^nXYqXb9O;vTux!(6ghq*FF|!iH}00bt*^1E-fS%*RPaOEZ3h8)QVrM($F%fseqs zNaoABT^)_DBH`nFpn@^)sYtahuk_XBi%T>I%wmY-yd<|a_m%X4@%oY$_bcg0H#7yU3^)5j zI?IgQT*yrs3apW-n9p0X9 z%>STh2F3YqD@QW2Av!ruVcu1&o>~qBqc6IhKq{yFA03AIzc2M?NG&7v>q8vgKX8>7?W_(|p{{Wg)(EJfJo!xcT+&&s>UrT-snqi>17c?(**V@#!P+34E=4*3@cRy&t zK{En0&D{(-+;4c&NS`up3zE(F-$*+ByS9)n^dQCAw&QIKI(t*MxwQhEKsJ)`!272* z<^bI#T^r)D2Y_324E?WDoUl!h%$g(2{E5oRWmU|3R&r5~AjY>CEwKeofnMdQnkto( za04Td!9W8{oB7>ttF_f{(J2{|q0Occ*4c!qLrgB>j7K8Yd<_sFFYjV2#)lr zVW#XB=IlH?FIswNV2`0R zyH^qduK|fgr_?40&LMNH{8v|7;ZR!1iNI`Y@O*uy7PG91Af(zVvtOu9P*3NeCBW}{ z!>## zf>vYBcrbkR1l$E~bSF?<8axNi)FevIPjRZeB3#Yo)?hBHE~00FFT%Vf=xx&Jslhj; z7bVz@v(p4Vr|r~h4lH7*^FOcy(2K#@khJvUR~l?OCy*fR)Z-5CwktXpFq!NW_vP@` zU&&4q$m8&U%z!2_*Wuj&oF2C|CoDgGdWKHPAD5q*hnZ^*a494ph71p((nO~I8-Ha%)nYggy2yeoRt2ETZ1rd}ybBL7DL zwE=Dpa9hS-aoKf>+j>XX-Q#Z0xGVjx%-X~S7wW9-?YS5oYRAFLHsE$KFWhWMo`8{7dZEEOnB;!! z7Gu3#6Uc;BUhAe2eCD#wnbN`SjE*EDw^=%@_=IK7VZtXYnvR?CNj9({_#}I0JU+=1 z25aKj0r)QAdl){o_|)RlfX^2Z6F3v4ww2nU@U&=Ttj5_iQMiU>oMw_(TCOa z;lcE}V0zfrG>JTi+(15Np2wjj=o5P_F`-pXuqEW)kaLu*K9Sq&#%?k??H00VxzXdHno$reC#A@I~0{yMyediB8->!I_wMNANa^7XY=#0nHEH*oZ6?HYqRpV7#i zxJ0W~y&rLhT)sZNM9~e9Ha>12VqE|q*RVR9lnI&3(536fp(Jqc+N`w`CE&_bWmQcS zPriP#x7R&k3s&wG26dII;Sy$rEmD<~it}}p6A;#=&0)E?n{H^#!_pQ>2CCO3n{VMz z=ACB@0BC?c|#W`L}7Uvi(5NNQTq1ggwfCJdiga z`{*g`m|oB`?;iwp^_3l5eF-afRgwIwYq3}bdVu7AzqxhCD9VG@yG`-%HPVM>#E-x0 z-@5mJMs%%pzr(NBxUQ+Ektk%|YSiWh2;HYc@d2C38A~qH03@K`_z)zaW{fEvT#A(x_M0FK$ z`dporTD%oEX3~h<&U@8Eeqjl2mB?poeC$XmsSQ~Fl1x#w!3PvA`k zN;S_kot6JmS9tc|UL~|YaCmFC2f2b~9-y=FU+QXX_dyqP zc(bmMl)eD{it>PsspWWZ)Vf^Wx@=8obT5Ac#3o+zIx92}cF8BTHvS|r;KcLLDHpKO z`sHHbxj;=4k6<^W7a3w zIp!R8`@O{LXHd4o>jT0OVa$MDn9|HYm zKGwym#{DC(ZB*Yb(q3<0AOz1y!SPhb1+2#>x@34C#5%l{ynMozlhwf z>5=CyVD-91`U7fqp_T>jk{C2(=*`1Ta7Vkz^r2!zH1}YWT^VRvqr{o&l^D}WCCW6Y zC=z$Mg`4)m@9~JDGo4o?leS1Sl__=BdLXjslqYf!;h)Zu2UZT;KvB~<1qu8&)_+>Y z>!V8e5?~EcebhN-@dc;V9C!orQ1`0?rR8h|iG2zoljMN$n8>6R?~iMmth?As~) zp(oTEw*noA+N|pM%D;`pfnMsIyabr&vZ@u7%$K$626|pdjvSE z*LpGHx?Ysfi&l=ghgBBvl&+P05x7FvT6YF33_Zu=qkCN%JdgCMeQ*NL&HTBJuN;`S zQK~}yW+QIA0cWi4Jf{p?f7iScATP7Vv2EF(Uz(S0H^YM znX_eoZLHkxv^+Aj ztL@t;b)T{BpDG^UtH|+IiQmV79rpYgV}S>lj{ZysHWt-a9nW<@U83C8-mN-Mqe>Wo z4)1Vi0-{rUpya%IkCZx-F9L#50!IyWc*9^>$_^sal`z|g?9�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 z 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

VYH;S`SlBhLd4A|D=^@Jp3YmPWin=>mX-% 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

d9>23t47%Mq3MGJ~Lu4GkDAl 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

bzsY& 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Ν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

{lu7K+T@J3<{kXq!0%RG0n2}@_rSouYZmY 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

G;)vxU|45lnl~K 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

1fQ}n#8OV?WhHQFu8r1T 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%0A-v4M>Hu?ag!a5m*BINUIqIEERotkWAw+uXDO5V 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