diff --git a/lab7/.vs/ProjectSettings.json b/lab7/.vs/ProjectSettings.json new file mode 100644 index 000000000..3daacbff1 --- /dev/null +++ b/lab7/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "沒有任何組態" +} \ No newline at end of file diff --git a/lab7/.vs/VSWorkspaceState.json b/lab7/.vs/VSWorkspaceState.json new file mode 100644 index 000000000..e2178edb8 --- /dev/null +++ b/lab7/.vs/VSWorkspaceState.json @@ -0,0 +1,10 @@ +{ + "ExpandedNodes": [ + "", + "\\kernel", + "\\user_program", + "\\user_program\\fat32_test2" + ], + "SelectedNode": "\\user_program\\fat32_test2\\fat32_test2.c", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/lab7/.vs/lab7/v16/.suo b/lab7/.vs/lab7/v16/.suo new file mode 100644 index 000000000..2839e89ce Binary files /dev/null and b/lab7/.vs/lab7/v16/.suo differ diff --git a/lab7/.vs/lab7/v16/Browse.VC.db b/lab7/.vs/lab7/v16/Browse.VC.db new file mode 100644 index 000000000..1e2a26975 Binary files /dev/null and b/lab7/.vs/lab7/v16/Browse.VC.db differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/3d883f44c99f7f2f/FAT32_TEST2.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/3d883f44c99f7f2f/FAT32_TEST2.ipch new file mode 100644 index 000000000..912d7df9a Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/3d883f44c99f7f2f/FAT32_TEST2.ipch differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/66f049d27a0ae2d/SDHOST.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/66f049d27a0ae2d/SDHOST.ipch new file mode 100644 index 000000000..ba1096c43 Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/66f049d27a0ae2d/SDHOST.ipch differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/6808b4cbd541b738/THREAD.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/6808b4cbd541b738/THREAD.ipch new file mode 100644 index 000000000..8af5a18e2 Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/6808b4cbd541b738/THREAD.ipch differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/882b4abe3f6a72b1/FAT32_TEST.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/882b4abe3f6a72b1/FAT32_TEST.ipch new file mode 100644 index 000000000..b6611ad3c Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/882b4abe3f6a72b1/FAT32_TEST.ipch differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/a0d5a54181c0b8f8/FAT32.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/a0d5a54181c0b8f8/FAT32.ipch new file mode 100644 index 000000000..1b13f0c35 Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/a0d5a54181c0b8f8/FAT32.ipch differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/f4510879bfc32bc1/MAIN.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/f4510879bfc32bc1/MAIN.ipch new file mode 100644 index 000000000..55ceeeda5 Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/f4510879bfc32bc1/MAIN.ipch differ diff --git a/lab7/.vs/lab7/v16/ipch/AutoPCH/fcb39f5bbbd58847/VFS.ipch b/lab7/.vs/lab7/v16/ipch/AutoPCH/fcb39f5bbbd58847/VFS.ipch new file mode 100644 index 000000000..255f09387 Binary files /dev/null and b/lab7/.vs/lab7/v16/ipch/AutoPCH/fcb39f5bbbd58847/VFS.ipch differ diff --git a/lab7/.vs/slnx.sqlite b/lab7/.vs/slnx.sqlite new file mode 100644 index 000000000..055e99687 Binary files /dev/null and b/lab7/.vs/slnx.sqlite differ diff --git a/lab7/FATTEST.txt b/lab7/FATTEST.txt new file mode 100644 index 000000000..4498eb4cb --- /dev/null +++ b/lab7/FATTEST.txt @@ -0,0 +1 @@ +This is for fat32 test read/write. \ No newline at end of file diff --git a/lab7/bcm2710-rpi-3-b-plus.dtb b/lab7/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..315b58137 Binary files /dev/null and b/lab7/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab7/bootloader/Makefile b/lab7/bootloader/Makefile new file mode 100644 index 000000000..11adc6c56 --- /dev/null +++ b/lab7/bootloader/Makefile @@ -0,0 +1,45 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate bootloader.img +all: bootloader.img + +# compile startup.s, generate startup.o +startup.o: startup.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate bootloader.img, need startup.o main.o util.o uart.o reboot.o linker.ld +bootloader.img: startup.o main.o util.o uart.o reboot.o kernel.o linker.ld + $(LD) $(LD_FLAGS) -o bootloader.elf startup.o main.o util.o uart.o reboot.o kernel.o + $(OBJCOPY) -O binary bootloader.elf $@ + +# clean all generate file +clean: + rm *.o bootloader.elf bootloader.img + +# check asm by qemu +asm: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -d in_asm + +# run bootloader.img for test +run: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial stdio + +# pseudo TTY +tty: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial pty + +# gdb debug +debug: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -S -s diff --git a/lab7/bootloader/auxilary.h b/lab7/bootloader/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/bootloader/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/bootloader/gpio.h b/lab7/bootloader/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/bootloader/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/bootloader/kernel.c b/lab7/bootloader/kernel.c new file mode 100644 index 000000000..4a977cbc9 --- /dev/null +++ b/lab7/bootloader/kernel.c @@ -0,0 +1,87 @@ +#include "kernel.h" +#include "uart.h" +#include "util.h" + +extern char _end[]; +extern char __kernel_begin[]; +extern char __kernel_size[]; +unsigned long int backup_address = 0x60000; + +void load_new_kernel() +{ + uart_putstr("Start Loading kernel image...\n"); + + // 1. define new kernel address + long long address_int = 0x80000; + + // 2. send kernel (use sendimg.py) + uart_putstr("Please send kernel image from UART now...\n"); + + // 3. read kernel size + char k_size[20] = {0}; + uart_read_cmd(k_size); + int kernel_size = atoi(k_size); + + // 4. show kernel size and load address + uart_putstr("Kernel size: "); + uart_putstr(k_size); + uart_putstr("\t"); + uart_putstr("Load address: 0x"); + uart_putstr("Load address: 0x80000"); + uart_putstr("\n"); + + // 5. read new kernel form uart, start on new_address(0x80000) + uart_putstr("Starting to load target kernel\n"); + + char* new_address = (char*)address_int; + for (int i = 0; i < kernel_size; i++) + { + char c = uart_getchar(); // all char should be get + new_address[i] = c; + } + + uart_putstr("Finished load target kernel and running.\n"); + + // 6. load new kernel + void (*new_kernel)(void) = (void *)new_address; + new_kernel(); +} + +void backup_old_kernel() +{ + char *kernel = __kernel_begin; + unsigned long int size = (unsigned long int)__kernel_size; + char *backup = (char *)(backup_address); + + uart_putstr("Starting to backup bootloader\n"); + + // move bootloader to backup_address(0x60000) + while (size--) + { + *backup = *kernel; + kernel++; + backup++; + } + + uart_putstr("Finished backup bootloader\n"); +} + +void load_image() +{ + // 1. backup bootloader kernel to 0x60000 + backup_old_kernel(); + + // 2. calculate new 'load_new_kernel' funciton address + + // because old kernel are backup to backup_address + // so load_new_kernel function need move to new address + // offest = load_target_func_address - kernel_start + + void (*load_target_ptr)() = load_new_kernel; + unsigned long int load_target_func_address = (unsigned long int) load_target_ptr; + void (*load_kernel)() = + (void (*)())( backup_address + (load_target_func_address - (unsigned long int)__kernel_begin) ); + + // 3. load new kernel form uart + load_kernel(); +} diff --git a/lab7/bootloader/kernel.h b/lab7/bootloader/kernel.h new file mode 100644 index 000000000..c6f0117b1 --- /dev/null +++ b/lab7/bootloader/kernel.h @@ -0,0 +1,8 @@ +#ifndef KERNEL +#define KERNEL + +void load_image(); +void backup_old_kernel(); +void load_new_kernel(); + +#endif \ No newline at end of file diff --git a/lab7/bootloader/linker.ld b/lab7/bootloader/linker.ld new file mode 100644 index 000000000..86f4760a8 --- /dev/null +++ b/lab7/bootloader/linker.ld @@ -0,0 +1,46 @@ +SECTIONS /* Use the SECTIONS keyword to declare a section, the following is its content */ +{ + . = 0x80000; /* move the 'location counter' to 0x30000 */ + __kernel_begin = .; + .text : /* assign .text section */ + { + KEEP(*(.text.boot)) /* make sure .text.boot will not be remove by linker-time garbage collection. */ + *(.text .text.*) /* *(.text .text.*) is all .text and .text. */ + } + .rodata : /* assign .rodata section */ + { + *(.rodata .rodata.*) /* *(.rodata .rodata.*) is all .rodata and .rodata. */ + } + .data : /* assign .data section */ + { + *(.data .data.*) /* *(.data .data.*) is all .data and .data. */ + } + .bss (NOLOAD) : /* assign .bss section, NOLOAD means not load at runtime */ + { + . = ALIGN(16); /* aligned to 16-byte boundaries. ex.0x0000000000080000 */ + __bss_start = .; /* assign __bss_start to the current memory position */ + *(.bss .bss.*) /* *(.bss .bss.*) is all .bss and .bss. */ + *(COMMON) /* assign uninitialized data section */ + __bss_end = .; /* assign __bss_end to the current memory position */ + } + _end = .; /* assign _end to the current memory position */ +} +__bss_size = (__bss_end - __bss_start) >> 3; /* _bss_size set to 8 */ +__kernel_size = (_end - __kernel_begin); + +/* + KEEP: make sure the section will not be remove by linker-time garbage collection. + .text: code section + .rodata: stores read-only data, e.g. const variables and strings + .bss : uninitialized data section (weak symbol) e.g. global var init to zero + .data : initialized data section (strong symbol) + NOLOAD : Does not load at runtime (because bss section is full of zero) + COMMON : uninitialized data section (weak symbol) e.g. global var just declare + + [example] + const int a = 0; // .rodata + int b = 3; // .data + int c = 0; // .bss + int d; // COMMON + +*/ \ No newline at end of file diff --git a/lab7/bootloader/main.c b/lab7/bootloader/main.c new file mode 100644 index 000000000..9bbad794c --- /dev/null +++ b/lab7/bootloader/main.c @@ -0,0 +1,69 @@ +#include "uart.h" +#include "util.h" +#include "reboot.h" +#include "kernel.h" + +#define CMDSIZE 64 +char cmd[CMDSIZE] = {0}; + +void cmd_init() +{ + for(int i = 0; i < CMDSIZE; i++) + cmd[i] = 0; +} + +void cmd_handle() // parse command +{ + if (strcmp(cmd, "help")) + { + uart_putstr("help print all available commands \n"); + uart_putstr("loadimg load new kernel from uart \n"); + uart_putstr("reboot reboot raspi3 \n"); + } + else if (strcmp(cmd, "loadimg")) + { + load_image(); + } + else if(strcmp(cmd, "reboot")) + { + uart_putstr("reboot .... \n"); + raspi3_reboot(100); + while(1); // wait for reboot + } + else if(strlen(cmd) != 0) + { + uart_putstr("command \""); + uart_putstr(cmd); + uart_putstr("\" not found, try \n"); + } + + uart_putstr("# "); +} + +void main() +{ + cmd_init(); + uart_init(); + + // put welcome ascii art + uart_putstr("\n"); + uart_putstr(" .~~. .~~. \n"); + uart_putstr(" '. \\ ' ' / .' \n"); + uart_putstr(" .~ .~~~..~. \n"); + uart_putstr(" : .~.'~'.~. : \n"); + uart_putstr(" ~ ( ) ( ) ~ \n"); + uart_putstr(" ( : '~'.~.'~' : ) \n"); + uart_putstr(" ~ .~ ( ) ~. ~ \n"); + uart_putstr(" ( : '~' : ) This is bootloader !! \n"); + uart_putstr(" '~ .~~~. ~' \n"); + uart_putstr(" '~' \n"); + uart_putstr("# "); + + while(1) + { + uart_read_cmd(cmd); + cmd_handle(); + cmd_init(); + } + +} diff --git a/lab7/bootloader/mmio.h b/lab7/bootloader/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/bootloader/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/bootloader/reboot.c b/lab7/bootloader/reboot.c new file mode 100644 index 000000000..f9c108a66 --- /dev/null +++ b/lab7/bootloader/reboot.c @@ -0,0 +1,13 @@ +#include "reboot.h" + +void raspi3_reboot(int ticks) // reboot after watchdog timer expire +{ + *PM_RSTC = PM_PASSWORD | 0x20; // full reset + *PM_WDOG = PM_PASSWORD | ticks; // number of watchdog tick +} + +void cancel_reset() +{ + *PM_RSTC = PM_PASSWORD | 0; // full reset + *PM_WDOG = PM_PASSWORD | 0; // number of watchdog tick +} \ No newline at end of file diff --git a/lab7/bootloader/reboot.h b/lab7/bootloader/reboot.h new file mode 100644 index 000000000..7831d4bcb --- /dev/null +++ b/lab7/bootloader/reboot.h @@ -0,0 +1,12 @@ +#ifndef REBOOT +#define REBOOT + +#define PM_PASSWORD 0x5A000000 + +#define PM_RSTC ((volatile unsigned int*)0x3F10001C) +#define PM_WDOG ((volatile unsigned int*)0x3F100024) + +void raspi3_reboot(int ticks); // reboot after watchdog timer expire +void cancel_reset(); // cancel_reset + +#endif \ No newline at end of file diff --git a/lab7/bootloader/startup.s b/lab7/bootloader/startup.s new file mode 100644 index 000000000..4a34a0733 --- /dev/null +++ b/lab7/bootloader/startup.s @@ -0,0 +1,38 @@ +.section ".text.boot" // Declare a section of .text.boot + +.global _start /* The .global keyword is used to make a symbol visible to the linker + .global _start makes the _start symbol to a visible global symbol */ + +// The bootloader of rpi3 loads kernel8.img to RAM (address 0x80000) by GPU +// After that, 4 CPU cores start to run the same code simultaneously. +// So, let only one core proceed, and let others enter a busy loop. + +_start: // _start represents the program start entry symbol, the following is its execution content + mrs x1, mpidr_el1 // gets the processor ID from the 'mpidr_el1' system register to x1 + and x1, x1, #3 // and bit, because rpi3 have 4 CPU cores, so and #3 + cbz x1, init // compare, if cpu id = 0 jump to init. + b busy_loop // else jump to busy_loop + +busy_loop: + wfe // Let ARM enter the 'low-power standby state' amd wait for event + b busy_loop // b means jump, the this sentence means jump back to busy_loop + +init: + ldr x1, =0x9000000 + str x0, [x1] + + ldr x1, =__bss_start // init bss segment, bss segment are initialized to 0, .bss start address to x1 + ldr x2, =__bss_size // size of the .bss section to x2 + +loop_clear_bss: + cbz x2, entry_point // if .bss size = 0, jump to entry_point + str xzr, [x1], #8 // store register. xzr is zero register. *x1 = 0; x1 += 8; + sub x2, x2, #1 // x2 = x2 - 1; (because __bss_size / 8) + cbnz x2, loop_clear_bss // if .bss size > 0 jump to loop_clear_bss. + +entry_point: + ldr x1, =_start // sp is stack pointer, mov _start to stack top + mov sp, x1 // + bl main // jump to our main() method of main.c + b busy_loop // should never come here, if failure jump to busy_loop + diff --git a/lab7/bootloader/uart.c b/lab7/bootloader/uart.c new file mode 100644 index 000000000..c2c0447b2 --- /dev/null +++ b/lab7/bootloader/uart.c @@ -0,0 +1,164 @@ +#include "gpio.h" +#include "auxilary.h" +#include "util.h" + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +full char, use for uart send kernel +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +// read command +void uart_read_cmd(char* cmd) +{ + char c; + char c2; + int cmdSize = 0; + + while (c != '\n' || c == '\r') + { + c = uart_getchar(); + + if (c == '\n' || c == '\r') // 0X0A '\n' newline, parse command + { + while(cmd[cmdSize] != 0) + cmdSize++; + cmd[cmdSize] = '\0'; + cmdSize++; + uart_putstr("\n"); + break; + } + else if (c == 127) + { + if(cmdSize > 0) + { + cmdSize--; + cmd[cmdSize] = 0; + uart_putstr("\b \b"); + } + } + else if (c == '[') + { + c2 = uart_getchar(); + if (c2 == 'A') // cursor up + { + } + else if (c2 == 'C' && cmdSize < strlen(cmd)) // cursor left + { + uart_putstr("\033[C"); + cmdSize++; + } + else if (c2 == 'D' && cmdSize > 0) // cursor right + { + uart_putstr("\033[D"); + cmdSize--; + } + } + else + { + if (c > 31 && c < 127) // visible ascii + { + cmd[cmdSize] = c; + cmdSize++; + uart_sendchar(c); + } + } + } +} diff --git a/lab7/bootloader/uart.h b/lab7/bootloader/uart.h new file mode 100644 index 000000000..968de3ab6 --- /dev/null +++ b/lab7/bootloader/uart.h @@ -0,0 +1,10 @@ +#ifndef UART_H +#define UART_H + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string +void uart_read_cmd(char* cmd); // read command + +#endif \ No newline at end of file diff --git a/lab7/bootloader/util.c b/lab7/bootloader/util.c new file mode 100644 index 000000000..9aeb53875 --- /dev/null +++ b/lab7/bootloader/util.c @@ -0,0 +1,84 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +long long hex2int(char* hex) +{ + long long val = 0; + while (*hex) + { + // get current character then increment + int byte1 = *hex++; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1 ; + } + return val; +} \ No newline at end of file diff --git a/lab7/bootloader/util.h b/lab7/bootloader/util.h new file mode 100644 index 000000000..4ed90b3ec --- /dev/null +++ b/lab7/bootloader/util.h @@ -0,0 +1,9 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal ? +int atoi(char* input); // string to int +long long hex2int(char* hex); // hex string to integer + +#endif \ No newline at end of file diff --git a/lab7/config.txt b/lab7/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/lab7/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x8000000 \ No newline at end of file diff --git a/lab7/kernel/Makefile b/lab7/kernel/Makefile new file mode 100644 index 000000000..568af45ba --- /dev/null +++ b/lab7/kernel/Makefile @@ -0,0 +1,122 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate kernel8.img +all: kernel8.img user_program argv_test fork_test vfs_test ls_test multilvl_test fat32_test fat32_test2 gencpio + +%.o : %.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate kernel8.img, need startup.o main.o util.o uart.o reboot.o cpio.o linker.ld +kernel8.img: startup.o main.o util.o uart.o reboot.o cpio.o devicetree.o buddy.o allocator.o exception.o exception_handle.o timer.o core_timer.o context_switch.o thread.o vfs.o tmpfs.o sdhost.o fat32.o linker.ld + $(LD) $(LD_FLAGS) -o kernel8.elf startup.o main.o util.o uart.o reboot.o cpio.o devicetree.o buddy.o allocator.o exception.o exception_handle.o timer.o core_timer.o context_switch.o thread.o vfs.o sdhost.o fat32.o tmpfs.o + $(OBJCOPY) -O binary kernel8.elf $@ + +# generate boot.o +../user_program/svc.o : ../user_program/svc.s + $(CC) $(CFLAGS) -c $< -o $@ + +# generate boot.elf and put rootfs +user_program : ../user_program/svc.o + aarch64-linux-gnu-ld -o ../rootfs/svc.elf ../user_program/svc.o + +../user_program/argv_test/%.o : ../user_program/argv_test/%.s + $(CC) -c $< -o $@ + +../user_program/argv_test/%.o : ../user_program/argv_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +argv_test : ../user_program/argv_test/startup.o ../user_program/argv_test/sys.o ../user_program/argv_test/argv_test.o ../user_program/argv_test/uart.o ../user_program/argv_test/util.o + $(LD) -T ../user_program/argv_test/linker.ld -o ../user_program/argv_test/argv_test.elf ../user_program/argv_test/startup.o ../user_program/argv_test/sys.o ../user_program/argv_test/argv_test.o ../user_program/argv_test/uart.o ../user_program/argv_test/util.o + $(OBJCOPY) -O binary ../user_program/argv_test/argv_test.elf ../rootfs/argv_test.img + +../user_program/fork_test/%.o : ../user_program/fork_test/%.s + $(CC) -c $< -o $@ + +../user_program/fork_test/%.o : ../user_program/fork_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +fork_test : ../user_program/fork_test/startup.o ../user_program/fork_test/sys.o ../user_program/fork_test/fork_test.o ../user_program/fork_test/uart.o ../user_program/fork_test/util.o + $(LD) -T ../user_program/fork_test/linker.ld -o ../user_program/fork_test/fork_test.elf ../user_program/fork_test/startup.o ../user_program/fork_test/sys.o ../user_program/fork_test/fork_test.o ../user_program/fork_test/uart.o ../user_program/fork_test/util.o + $(OBJCOPY) -O binary ../user_program/fork_test/fork_test.elf ../rootfs/fork_test.img + +../user_program/vfs_test/%.o : ../user_program/vfs_test/%.s + $(CC) -c $< -o $@ + +../user_program/vfs_test/%.o : ../user_program/vfs_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +vfs_test : ../user_program/vfs_test/startup.o ../user_program/vfs_test/sys.o ../user_program/vfs_test/vfs_test.o ../user_program/vfs_test/uart.o ../user_program/vfs_test/util.o + $(LD) -T ../user_program/vfs_test/linker.ld -o ../user_program/vfs_test/vfs_test.elf ../user_program/vfs_test/startup.o ../user_program/vfs_test/sys.o ../user_program/vfs_test/vfs_test.o ../user_program/vfs_test/uart.o ../user_program/vfs_test/util.o + $(OBJCOPY) -O binary ../user_program/vfs_test/vfs_test.elf ../rootfs/vfs_test.img + +../user_program/ls_test/%.o : ../user_program/ls_test/%.s + $(CC) -c $< -o $@ + +../user_program/ls_test/%.o : ../user_program/ls_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +ls_test : ../user_program/ls_test/startup.o ../user_program/ls_test/sys.o ../user_program/ls_test/ls_test.o ../user_program/ls_test/uart.o ../user_program/ls_test/util.o + $(LD) -T ../user_program/ls_test/linker.ld -o ../user_program/ls_test/ls_test.elf ../user_program/ls_test/startup.o ../user_program/ls_test/sys.o ../user_program/ls_test/ls_test.o ../user_program/ls_test/uart.o ../user_program/ls_test/util.o + $(OBJCOPY) -O binary ../user_program/ls_test/ls_test.elf ../rootfs/ls_test.img + +../user_program/multilvl_test/%.o : ../user_program/multilvl_test/%.s + $(CC) -c $< -o $@ + +../user_program/multilvl_test/%.o : ../user_program/multilvl_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +multilvl_test : ../user_program/multilvl_test/startup.o ../user_program/multilvl_test/sys.o ../user_program/multilvl_test/multilvl_test.o ../user_program/multilvl_test/uart.o ../user_program/multilvl_test/util.o + $(LD) -T ../user_program/multilvl_test/linker.ld -o ../user_program/multilvl_test/multilvl_test.elf ../user_program/multilvl_test/startup.o ../user_program/multilvl_test/sys.o ../user_program/multilvl_test/multilvl_test.o ../user_program/multilvl_test/uart.o ../user_program/multilvl_test/util.o + $(OBJCOPY) -O binary ../user_program/multilvl_test/multilvl_test.elf ../rootfs/multilvl_test.img + +../user_program/fat32_test/%.o : ../user_program/fat32_test/%.s + $(CC) -c $< -o $@ + +../user_program/fat32_test/%.o : ../user_program/fat32_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +fat32_test : ../user_program/fat32_test/startup.o ../user_program/fat32_test/sys.o ../user_program/fat32_test/fat32_test.o ../user_program/fat32_test/uart.o ../user_program/fat32_test/util.o + $(LD) -T ../user_program/fat32_test/linker.ld -o ../user_program/fat32_test/fat32_test.elf ../user_program/fat32_test/startup.o ../user_program/fat32_test/sys.o ../user_program/fat32_test/fat32_test.o ../user_program/fat32_test/uart.o ../user_program/fat32_test/util.o + $(OBJCOPY) -O binary ../user_program/fat32_test/fat32_test.elf ../rootfs/fat32_test.img + +../user_program/fat32_test2/%.o : ../user_program/fat32_test2/%.s + $(CC) -c $< -o $@ + +../user_program/fat32_test2/%.o : ../user_program/fat32_test2/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +fat32_test2 : ../user_program/fat32_test2/startup.o ../user_program/fat32_test2/sys.o ../user_program/fat32_test2/fat32_test2.o ../user_program/fat32_test2/uart.o ../user_program/fat32_test2/util.o + $(LD) -T ../user_program/fat32_test2/linker.ld -o ../user_program/fat32_test2/fat32_test2.elf ../user_program/fat32_test2/startup.o ../user_program/fat32_test2/sys.o ../user_program/fat32_test2/fat32_test2.o ../user_program/fat32_test2/uart.o ../user_program/fat32_test2/util.o + $(OBJCOPY) -O binary ../user_program/fat32_test2/fat32_test2.elf ../rootfs/fat32_test2.img + +gencpio: + cd ../rootfs; find . | cpio -o -H newc > ../initramfs.cpio + +# clean all generate file +clean: + rm *.o kernel8.elf kernel8.img ../initramfs.cpio ../user_program/svc.o ../rootfs/svc.elf ../user_program/argv_test/*.o ../user_program/argv_test/*.elf ../rootfs/argv_test.img ../user_program/fork_test/*.o ../user_program/fork_test/*.elf ../rootfs/fork_test.img ../user_program/vfs_test/*.o ../user_program/vfs_test/*.elf ../rootfs/vfs_test.img ../user_program/ls_test/*.o ../user_program/ls_test/*.elf ../rootfs/ls_test.img ../user_program/multilvl_test/*.o ../user_program/multilvl_test/*.elf ../rootfs/multilvl_test.img ../user_program/fat32_test/*.o ../user_program/fat32_test/*.elf ../rootfs/fat32_test.img ../user_program/fat32_test2/*.o ../user_program/fat32_test2/*.elf ../rootfs/fat32_test2.img + + +# check asm by qemu +asm: all + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -d in_asm + +# run kernel8.img for test +run: all + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -serial null -serial stdio -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb -drive if=sd,file=../sfn_nctuos.img,format=raw + +# gdb debug +debug: all + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -S -s diff --git a/lab7/kernel/allocator.c b/lab7/kernel/allocator.c new file mode 100644 index 000000000..35a07b5b2 --- /dev/null +++ b/lab7/kernel/allocator.c @@ -0,0 +1,223 @@ +#include "util.h" +#include "buddy.h" +#include "uart.h" +#include "allocator.h" + +struct object_allocator obj_alloc_pool[ALLOCATOR_POOL_SIZE * 4]; +unsigned int object_allocator_gid = 0; + +struct dynamic_allocator dynamic_alloc_pool[ALLOCATOR_POOL_SIZE]; +unsigned int dynamic_allocator_gid = 0; + +void memory_init() +{ + object_allocator_gid = 0; + dynamic_allocator_gid = 0; +} + +void dynamic_test() +{ + unsigned long long addr; + + uart_putstr("\n================================== dynamic allocator ==================================\n"); + buddy_init(); + struct dynamic_allocator *dyalloc = dynamic_allocator_init(); + + uart_putstr("============= init end =====================\n"); + uart_putstr("\n use 4 obj_alloc (64,512,2048,4096) \n"); + uart_putstr("[Dynamic Allocator] allocate 16 bytes...\n"); + addr = dynamic_alloc(dyalloc, 16); + + uart_putstr("[Dynamic Allocator] allocate 33 bytes...\n"); + addr = dynamic_alloc(dyalloc, 33); + uart_putstr("[Dynamic Allocator] free addr...\n"); + dynamic_free(dyalloc, addr); + uart_putstr("[Dynamic Allocator] allocate 60 bytes...\n"); + addr = dynamic_alloc(dyalloc, 60); + + uart_putstr("[Dynamic Allocator] allocate 1500 bytes...\n"); + addr = dynamic_alloc(dyalloc, 1500); + uart_putstr("[Dynamic Allocator] allocate 1800 bytes...\n"); + addr = dynamic_alloc(dyalloc, 1800); + uart_putstr("[Dynamic Allocator] allocate 2000 bytes...\n"); + addr = dynamic_alloc(dyalloc, 2000); + uart_putstr("[Dynamic Allocator] allocate 5566 bytes...\n"); + addr = dynamic_alloc(dyalloc, 5566); + uart_putstr("[Dynamic Allocator] free addr...\n"); + dynamic_free(dyalloc, addr); +} +/* +void show_allocator_alloc_message(unsigned long long addr, int obj_size) +{ + char buf[16] = {0}; + + uart_putstr("[Dynamic Allocator] allocate object "); + unsignedlonglongToStr(obj_size, buf); + uart_putstr(buf); + uart_putstr(" bytes at "); + unsignedlonglongToStrHex(addr, buf); + uart_putstr(buf); + uart_putstr(" \n\n"); +} + +void show_allocator_free_message(unsigned long long addr, int obj_size) +{ + char buf[16] = {0}; + + uart_putstr("[Dynamic Allocator] free object "); + unsignedlonglongToStr(obj_size, buf); + uart_putstr(buf); + uart_putstr(" bytes at "); + unsignedlonglongToStrHex(addr, buf); + uart_putstr(buf); + uart_putstr(" \n\n"); +} +*/ +void object_allocator_request_page(struct object_allocator *self) +{ + int page = buddy_alloc(0); + + int thispage_Id = self->page_count; + self->max_pool[thispage_Id] = self->max_pool_init; + self->cur_pool[thispage_Id] = 0; + self->page[thispage_Id] = page; + self->page_count ++; +} + +struct object_allocator* object_allocator_init(int size) +{ + int thisId = object_allocator_gid; + object_allocator_gid++; + + obj_alloc_pool[thisId].page_count = 0; + obj_alloc_pool[thisId].obj_size = size; + obj_alloc_pool[thisId].max_pool_init = PAGE_SIZE / size; + + object_allocator_request_page(&obj_alloc_pool[thisId]); + + return &obj_alloc_pool[thisId]; +} + +unsigned long long obj_alloc(struct object_allocator *self) +{ + int pool_id = self->page_count-1; + + self->cur_pool[pool_id] += 1; + if (self->cur_pool[pool_id] > self->max_pool_init) + { + //uart_putstr("[Dynamic Allocator] over allocate momory\n"); + //uart_putstr("[Dynamic Allocator] use buddy system allocate 1 page\n\n"); + object_allocator_request_page(self); + unsigned long long retAddr = obj_alloc(self); + return retAddr; + } + + unsigned int offset = (self->cur_pool[pool_id] - 1) * self->obj_size; + + unsigned long long retAddr = self->page[pool_id] * PAGE_SIZE + offset; + retAddr = MEMORY_START +retAddr; + //show_allocator_alloc_message(retAddr, self->obj_size); + + return retAddr; +} + +int obj_free(struct object_allocator *self, unsigned long long phyAddr) +{ + int page = (phyAddr - MEMORY_START) >> 12; + + int page_id = -1; + for(int i = 0; i < self->page_count; i++) + { + if(self->page[i] == page) + { + page_id = i; + break; + } + } + + if(page_id == -1) + return 0; + + self->cur_pool[page_id] -= 1; + + //show_allocator_free_message(phyAddr, self->obj_size); + + return 1; +} + +struct dynamic_allocator* dynamic_allocator_init() +{ + int thisId = dynamic_allocator_gid; + dynamic_allocator_gid++; + + if (dynamic_allocator_gid > ALLOCATOR_POOL_SIZE) + { + //uart_putstr("[Dynamic Allocator] over max allocate size\n"); + return 0; + } + + // init fix size (64, 512, 2048, 4096) + dynamic_alloc_pool[thisId].obj_allocator_64 = object_allocator_init(64); + dynamic_alloc_pool[thisId].obj_allocator_512 = object_allocator_init(512); + dynamic_alloc_pool[thisId].obj_allocator_2048 = object_allocator_init(2048); + dynamic_alloc_pool[thisId].obj_allocator_4096 = object_allocator_init(4096); + + return &dynamic_alloc_pool[thisId]; +} + +unsigned long long dynamic_alloc(struct dynamic_allocator *self, int req_size) +{ + // 1. if size < obj_allocate, use fix allocate + unsigned long long retAddr; + if(req_size <= 64) + { + retAddr = obj_alloc(self->obj_allocator_64); + } + else if(req_size <= 512) + { + retAddr = obj_alloc(self->obj_allocator_512); + } + else if(req_size <= 2048) + { + retAddr = obj_alloc(self->obj_allocator_2048); + } + else if(req_size <= 4096) + { + retAddr = obj_alloc(self->obj_allocator_4096); + } + else + { + // 2. else use buddy system + //uart_putstr("[Dynamic Allocator] over page size 4096, use buddy system allocate...\n"); + int page = buddy_alloc(req_size); + retAddr = (unsigned long long)(MEMORY_START + page * PAGE_SIZE); + } + + return retAddr; +} + +void dynamic_free(struct dynamic_allocator *self, unsigned long long addr) +{ + if(obj_free(self->obj_allocator_64, addr)) + { + return; + } + else if(obj_free(self->obj_allocator_512, addr)) + { + return; + } + else if (obj_free(self->obj_allocator_2048, addr)) + { + return; + } + else if (obj_free(self->obj_allocator_4096, addr)) + { + return; + } + else + { + unsigned long long phyAddr = addr - MEMORY_START; + int page = phyAddr >> 12; + buddy_free(page); + } +} \ No newline at end of file diff --git a/lab7/kernel/allocator.h b/lab7/kernel/allocator.h new file mode 100644 index 000000000..a44f1c183 --- /dev/null +++ b/lab7/kernel/allocator.h @@ -0,0 +1,36 @@ +#ifndef ALLOCATOR_H +#define ALLOCATOR_H + +#define OBJ_ALLOCATOR_PAGE_SIZE 16 // pool allow allocate max page count +#define ALLOCATOR_POOL_SIZE 32 + +struct object_allocator +{ + unsigned int max_pool[OBJ_ALLOCATOR_PAGE_SIZE]; // record max obj slot count (page size / obj size) + unsigned int cur_pool[OBJ_ALLOCATOR_PAGE_SIZE]; // record current obj use slot count + unsigned int page[OBJ_ALLOCATOR_PAGE_SIZE]; // record page index + unsigned int page_count; // record page count + unsigned int obj_size; // obj size (ex. 64,512) + unsigned int max_pool_init; // initial max_pool +}; + +// has 4 different obj size +struct dynamic_allocator +{ + struct object_allocator *obj_allocator_64; + struct object_allocator *obj_allocator_512; + struct object_allocator *obj_allocator_2048; + struct object_allocator *obj_allocator_4096; +}; + +struct object_allocator* object_allocator_init(int size); +unsigned long long obj_alloc(struct object_allocator *self); +int obj_allocator_free(struct object_allocator *self, unsigned long long addr); + +struct dynamic_allocator* dynamic_allocator_init(); +unsigned long long dynamic_alloc(struct dynamic_allocator *self, int req_size); +void dynamic_free(struct dynamic_allocator *self, unsigned long long addr); +void dynamic_test(); + +void memory_init(); +#endif \ No newline at end of file diff --git a/lab7/kernel/auxilary.h b/lab7/kernel/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/kernel/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/kernel/buddy.c b/lab7/kernel/buddy.c new file mode 100644 index 000000000..bfad59f4a --- /dev/null +++ b/lab7/kernel/buddy.c @@ -0,0 +1,391 @@ +#include "util.h" +#include "buddy.h" +#include "uart.h" + +struct buddy_head buddy_list[BUDDY_MAX_ORDER + 1]; +struct buddy_allocated buddy_allocated_list; + +void buddy_test() +{ + buddy_init(); + + int PFN1, PFN2, PFN3, PFN4, PFN5; + + uart_putstr("================================== buddy system ==================================\n"); + uart_putstr("PFN1: allocate 32 page...\n"); + PFN1 = buddy_alloc(32 * PAGE_SIZE); + uart_putstr("PFN2: allocate 7 page...\n"); + PFN2 = buddy_alloc(7 * PAGE_SIZE); + uart_putstr("PFN3: allocate 64 page...\n"); + PFN3 = buddy_alloc(64 * PAGE_SIZE); + uart_putstr("Free PFN1...\n"); + buddy_free(PFN1); + uart_putstr("\nFree PFN2...\n"); + buddy_free(PFN2); + uart_putstr("\nPFN4: allocate 56 page...\n"); + PFN4 = buddy_alloc(56 * PAGE_SIZE); + uart_putstr("PFN5: allocate 61 page...\n"); + PFN5 = buddy_alloc(61 * PAGE_SIZE); + uart_putstr("Free PFN3...\n"); + buddy_free(PFN3); + uart_putstr("\nFree PFN4...\n"); + buddy_free(PFN4); + uart_putstr("\nFree PFN5...\n"); + buddy_free(PFN5); + uart_putstr("====================================\n"); +} +/* +void show_alloc_message(struct buddy_node node) +{ + char buf[16] = {0}; + + uart_putstr("[Buddy system] allocate memory page ( from "); + unsignedlonglongToStr(node.start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.end, buf); + uart_putstr(buf); + uart_putstr(" )\n"); + // + uart_putstr("[Buddy system] allocate memory address ( from "); + unsignedlonglongToStrHex(node.start * PAGE_SIZE + MEMORY_START, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStrHex((node.end + 1) * PAGE_SIZE + MEMORY_START, buf); + uart_putstr(buf); + uart_putstr(" )\n\n"); + +} + +void show_free_message(struct buddy_node node) +{ + char buf[16] = {0}; + + uart_putstr("[Buddy system] free memory page ( from "); + unsignedlonglongToStr(node.start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.end, buf); + uart_putstr(buf); + uart_putstr(" )\n"); +} +*/ +bool allocated_list_push(struct buddy_node node) +{ + if(buddy_allocated_list.count == BUDDY_ALLOCATED_NUM) + { + //uart_putstr("allocated array full\n"); + return false; + } + + // put node to allocated_list and show message + node.status = USED; + for(int i = 0; i < BUDDY_ALLOCATED_NUM; i++) + { + if(buddy_allocated_list.node_list[i].status == FREE) + { + buddy_allocated_list.node_list[i].start = node.start; + buddy_allocated_list.node_list[i].end = node.end; + buddy_allocated_list.node_list[i].status = node.status; + //show_alloc_message(node); + break; + } + } + + buddy_allocated_list.count++; + return true; +} + +struct buddy_node allocated_list_pop(int node_start) +{ + // search and pop node + struct buddy_node ret_node; + for(int i = 0; i < BUDDY_ALLOCATED_NUM; i++) + { + struct buddy_node *node = &buddy_allocated_list.node_list[i]; + if(node->status == USED && node->start == node_start) + { + node->status = FREE; + ret_node.start = buddy_allocated_list.node_list[i].start; + ret_node.end = buddy_allocated_list.node_list[i].end; + ret_node.status = buddy_allocated_list.node_list[i].status; + break; + } + } + + buddy_allocated_list.count--; + return ret_node; +} + +// pop first buddy_node from buddy list +struct buddy_node buddy_list_pop(struct buddy_head *list) +{ + // 1. get first node + struct buddy_node ret_node; + ret_node.start = list->node_list[0].start; + ret_node.end = list->node_list[0].end; + ret_node.status = list->node_list[0].status; + list->count -= 1; + + // 2. move other list node forward iteration + int count = list->count; + int cur = 1; + while(count != 0) + { + list->node_list[cur - 1].start = list->node_list[cur].start; + list->node_list[cur - 1].end = list->node_list[cur].end; + list->node_list[cur - 1].status = list->node_list[cur].status; + + count--; + cur++; + } + + // 3. init last node + list->node_list[cur - 1].start = list->node_list[cur - 1].end = 0; + list->node_list[cur - 1].status = FREE; + + return ret_node; +} + +// split node to two part +void buddy_list_push(struct buddy_head *list, struct buddy_node node) +{ + int size = (node.end - node.start + 1) / 2; + + // first half + list->node_list[list->count].start = node.start; + list->node_list[list->count].end = node.start + size - 1; + list->count += 1; + + // second half + list->node_list[list->count].start = node.start + size; + list->node_list[list->count].end = node.end; + list->count += 1; + + // print first half log + /* + char buf[16] = {0}; + uart_putstr("first half (from "); + unsignedlonglongToStr(node.start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.start + size - 1, buf); + uart_putstr(buf); + uart_putstr(" )\n"); + // print second half log + uart_putstr("second half (from "); + unsignedlonglongToStr(node.start + size, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.end, buf); + uart_putstr(buf); + uart_putstr(" )\n"); + */ +} + +void buddy_merge(int order, struct buddy_node* node) +{ + struct buddy_node merge_node; + bool can_merge = false; + int merge_index; + int size = buddy_list[order].count; + + //char buf[16] = {0}; + + while(1) + { + can_merge = false; + size = buddy_list[order].count; + + // calc free node and list node can merge (continue memory) + for(merge_index = 0; merge_index < size; merge_index++) + { + if(buddy_list[order].node_list[merge_index].end + 1 == node->start) + { + merge_node.start = buddy_list[order].node_list[merge_index].start; + merge_node.end = node->end; + can_merge = true; + break; + } + + if(buddy_list[order].node_list[merge_index].start == node->end + 1) + { + merge_node.start = node->start; + merge_node.end = buddy_list[order].node_list[merge_index].end; + can_merge = true; + break; + } + } + + // if can merge, show message, and merge next level iteratively + if(can_merge) + { + /* + uart_putstr("[Buddy system] merge ( from "); + unsignedlonglongToStr(buddy_list[order].node_list[size - 1].start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(buddy_list[order].node_list[size - 1].end, buf); + uart_putstr(buf); + uart_putstr(" ) and ( from "); + unsignedlonglongToStr(node->start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node->end, buf); + uart_putstr(buf); + uart_putstr(" )\n");*/ + + if(size - 1 == 0) + { + buddy_list[order].count = 0; + } + else + { + buddy_list[order].node_list[merge_index].start = buddy_list[order].node_list[size].start; + buddy_list[order].node_list[merge_index].end = buddy_list[order].node_list[size].end; + buddy_list[order].node_list[merge_index].status = buddy_list[order].node_list[size].status; + buddy_list[order].count-- ; + } + + if(order != BUDDY_MAX_ORDER) + { + //buddy_merge(order + 1, &merge_node); + node->start = merge_node.start; + node->end = merge_node.end; + order = order + 1; + } + } + else + { + // if can't merge, free node add to list + buddy_list[order].node_list[size].start = node->start; + buddy_list[order].node_list[size].end = node->end; + buddy_list[order].node_list[size].status = node->status; + buddy_list[order].count++; + break; + } + } +} + +void buddy_init() +{ + // init buddy_list + for(int i = 0; i < BUDDY_MAX_ORDER + 1; i++) + buddy_list[i].count = 0; + + // only the last buddy has free page 0-65535 + buddy_list[BUDDY_MAX_ORDER].count = 1; + buddy_list[BUDDY_MAX_ORDER].node_list[0].start = 0; + buddy_list[BUDDY_MAX_ORDER].node_list[0].end = PAGE_NUM - 1; + buddy_list[BUDDY_MAX_ORDER].node_list[0].status = FREE; + + // init buddy_allocated_list + buddy_allocated_list.count = 0; + for(int i = 0; i < BUDDY_ALLOCATED_NUM; i++) + (buddy_allocated_list.node_list[i]).status = FREE; + +} + +int buddy_alloc(int size) +{ + // 1. calc buddy order for allocate size + // ex. size = 8192, order = 1 + int order = 0; + while(1) + { + if (size <= PAGE_SIZE * (1 << order)) + break; + + order++; + } + if (order > BUDDY_MAX_ORDER) + { + //uart_putstr("allocate memory overd BUDDY_MAX_ORDER"); + return -1; + } + + /* + char buf[16] = {0}; + uart_putstr("order is "); + unsignedlonglongToStr(order, buf); + uart_putstr(buf); + uart_putstr("\n"); + */ + + // 2. if has free buddy, allocate and return + if(buddy_list[order].count > 0) + { + //uart_putstr("buddy_list[order] has free page frame node.. \n"); + struct buddy_node alloc_node = buddy_list_pop(&buddy_list[order]); + if(allocated_list_push(alloc_node)) + return alloc_node.start; + else + return -1; + } + + //uart_putstr("buddy_list[order] no free node.. \n"); + // 3. if not, search high level buddy and spilt buddy / 2 + int new_order = order + 1; + while(order < BUDDY_MAX_ORDER + 1) + { + if(buddy_list[new_order].count > 0) + break; + + new_order++; + } + if(new_order == BUDDY_MAX_ORDER + 1) + { + //uart_putstr("fail to allocate memory\n"); + return -1; + } + /* + uart_putstr("has free in order "); + unsignedlonglongToStr(new_order, buf); + uart_putstr(buf); + uart_putstr("\n"); + + uart_putstr("pop to next level\n");*/ + struct buddy_node temp_node = buddy_list_pop(&buddy_list[new_order]); + new_order--; + + //uart_putstr("start release redundant memory block...\n\n"); + // 3.1 spilt up until allocate size + while(new_order >= order) + { + // split node to two part: + /* + uart_putstr("split node to two part:"); + uart_putstr("order="); + unsignedlonglongToStr(new_order, buf); + uart_putstr(buf); + uart_putstr("\n");*/ + + buddy_list_push(&buddy_list[new_order], temp_node); + /* + if (new_order > order) + uart_putstr("pop first part to next level\n\n"); + else + uart_putstr("pop first part\n\n");*/ + // pop first part to next level + temp_node = buddy_list_pop(&buddy_list[new_order]); + + new_order--; + } + + // 4. save to allocated_list and return address + if (allocated_list_push(temp_node)) + return temp_node.start; + else + return -1; +} + +void buddy_free(int node_start) +{ + struct buddy_node tmpNode = allocated_list_pop(node_start); + //show_free_message(tmpNode); + + int size = tmpNode.end - tmpNode.start + 1; + int order = log2(size); + + buddy_merge(order, &tmpNode); +} \ No newline at end of file diff --git a/lab7/kernel/buddy.h b/lab7/kernel/buddy.h new file mode 100644 index 000000000..812557062 --- /dev/null +++ b/lab7/kernel/buddy.h @@ -0,0 +1,47 @@ +#ifndef BUDDY_H +#define BUDDY_H + +typedef int bool; +#define true 1 +#define false 0 + +#define MEMORY_START 0x10000000 +#define MEMORY_SIZE 0x10000000 // from TA, 0x10000000-0x20000000 +#define PAGE_SIZE 0x1000 // 4096 +#define PAGE_NUM (MEMORY_SIZE / PAGE_SIZE) // all page frame = 65536 +#define BUDDY_MAX_ORDER 16 // 2^0 ~ 2^16 +#define BUDDY_ALLOCATED_NUM 65536 +#define BUDDY_NODE_LIST_NUM 16 + +enum Status +{ + FREE, + USED +}; + +struct buddy_node +{ + int start; + int end; + enum Status status; +}; + +struct buddy_head +{ + int count; + struct buddy_node node_list[BUDDY_NODE_LIST_NUM]; +}; + +struct buddy_allocated +{ + int count; + struct buddy_node node_list[BUDDY_ALLOCATED_NUM]; +}; + + +void buddy_init(); +int buddy_alloc(int size); +void buddy_free(int node_start); +void buddy_test(); + +#endif \ No newline at end of file diff --git a/lab7/kernel/context_switch.s b/lab7/kernel/context_switch.s new file mode 100644 index 000000000..b3765f205 --- /dev/null +++ b/lab7/kernel/context_switch.s @@ -0,0 +1,58 @@ +/* + switch_to (prev, next), x0:prev thread, x1:next thread + 1. 將 x19-x28, fp, lr, sp 暫存器內容儲存到 prev thread + 2. 將 next thread的內容給 x19-x28, fp, lr, sp 暫存器 + 3. 設定 x1(next) 為目前執行的thread + + x29 FP The Frame Pointer. fp儲存stack的底端 + x30 LR The Link Register. lr儲存function的連結位址 + SP The Stack Pointer. sp儲存stack的頂端 + + stp x19, x20, [x0, 16 * 0] // 將x19, x20的內容存到x0偏移16*0的位置 + mov x9, sp // 將sp內容給x9 + str x9, [x0, 16 * 6] // 將x9的內容存到x0偏移16*6的位置 + + ldp x19, x20, [x1, 16 * 0] // 將x1偏移16*0位置的內容給x19, x20 + ldr x9, [x1, 16 * 6] // 將x1偏移16*6位置的內容給x9 + mov sp, x9 // 將x9內容給sp +*/ + +.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 + +/* + Get the current thread from tpidr_el1 register +*/ + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +/* + set the current thread (tpidr_el1 register) +*/ + +.global set_current +set_current: + msr tpidr_el1, x0 + ret \ No newline at end of file diff --git a/lab7/kernel/core_timer.s b/lab7/kernel/core_timer.s new file mode 100644 index 000000000..ef84ed32d --- /dev/null +++ b/lab7/kernel/core_timer.s @@ -0,0 +1,37 @@ +/* + core_timer_enable + + cntp_ctl_el0: Physical Timer Control register. + 1: enable, 0: disable + + 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. + cntfrq_el0: the frequency of timer + + 1. set cntp_ctl_el0 enable + 2. set cntp_tval_el0 is cntfrq_el0 (i.e. expired time is cntfrq_el0) + 3. unmask the timer interrupt from the first level interrupt controller +*/ + +.equ CORE0_TIMER_IRQ_CTRL, 0x40000040 + +.globl core_timer_enable +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 + ret + +.globl core_timer_disable +core_timer_disable: + mov x0, 0 + msr cntp_ctl_el0, x0 // disable + mov x0, 0 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret \ No newline at end of file diff --git a/lab7/kernel/cpio.c b/lab7/kernel/cpio.c new file mode 100644 index 000000000..0c24eae40 --- /dev/null +++ b/lab7/kernel/cpio.c @@ -0,0 +1,265 @@ +#include "cpio.h" +#include "uart.h" +#include "util.h" +#include "timer.h" +#include "vfs.h" + +//https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 +//The pathname is followed by NUL bytes so that the total size of the fixed +// header plus pathname is a multiple of four. Likewise, the file data is +// padded to a multiple of four bytes. Note that this format supports only +// 4 gigabyte files (unlike the older ASCII format, which supports 8 giga- +// byte files). +unsigned long long align_to_4(unsigned long long size) +{ + unsigned long long residual = size % 4; + + if(residual > 0) + return size + (4 - residual); + else + return size; +} + +// list rootfs file +void cpio_list() +{ + //unsigned long int ptr = 0x20000000; + unsigned long long ptr = 0x8000000; + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if(namesize > 0) + { + uart_putstr(fileName); + uart_putstr(" "); + } + + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } +} + +// get file content +char* cpio_content(char* name) +{ + //unsigned long int ptr = 0x20000000; + unsigned long long ptr = 0x8000000; + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return 0; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return 0; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if (strcmp(fileName, name)) + { + if (filesize > 0) + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + return (char*)ptr; + } + + return 0; + } + else + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } + } +} + +// required 1-2 Add a command that can load a user program in the initramfs. Then, use eret to jump to the start address. +void cpio_run_user_program(char* name, int enable_timer) +{ + unsigned long long ptr = 0x8000000; // cpio address + unsigned long long loadAddr = 0x4000000; + + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if (strcmp(fileName, name)) + { + char buf[16] = {0}; + unsignedlonglongToStr(filesize, buf); + uart_putstr(buf); + uart_putstr("\n"); + + if (filesize > 0) + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + + uart_putstr("start load user program...\n"); + + // load user program + char* target = (char*)loadAddr; + char* cpio = (char*)ptr; + for (int i = 0; i < filesize; i++) + { + *target = *cpio; + target++; + cpio++; + } + + uart_putstr("complete...\n"); + + // required 1-2, EL1 to EL0 + // 1. load a user program in the initramfs to a specific address + // 2. set spsr_el1 to 0x3c0 and elr_el1 to the program’s start address. + // 3. set the user program’s stack pointer to a proper position by setting sp_el0. + // 4. issue eret to return to the user code. + + if(enable_timer) + { + core_timer_enable(); + asm volatile("mov x0, 0x0 \n\t"); // required 2, enable interrupt + } + else + { + asm volatile("mov x0, 0x3c0 \n\t"); // required 1-2, disable interrupt + } + + asm volatile("msr spsr_el1, x0 \n\t"); + asm volatile("msr elr_el1, %0 \n\t"::"r"(loadAddr)); + asm volatile("msr sp_el0, %0 \n\t"::"r"(loadAddr)); + asm volatile("eret \n"); + + break; + } + } + else + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } + } +} + +int cpio_load_user_program_and_get_size(char* name, unsigned long long loadAddr) +{ + unsigned long long ptr = 0x8000000; + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return 0; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return 0; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if (strcmp(fileName, name)) + { + if (filesize > 0) + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + + // load user program + char* target = (char*)loadAddr; + char* user_progame = (char*)ptr; + for (int i = 0; i < filesize; i++) + { + *target = *user_progame; + target++; + user_progame++; + } + + return filesize; + } + + return 0; + } + else + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } + } +} + +void cpio_populate_rootfs() +{ + uart_putstr("===== Populate the root file system with initramfs =====\n"); + unsigned long long ptr = 0x8000000; + struct cpio_newc_header *header; + char buf[16] = {0}; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return; + + char* pathname = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(pathname, CPIO_TRAILER, 10)) + return; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + uart_putstr("pathname: "); + uart_putstr(pathname); + uart_putstr(", filesize: "); + unsignedlonglongToStr(filesize, buf); + uart_putstr(buf); + uart_putstr("\n"); + + ptr = (unsigned long long)pathname; + ptr = align_to_4(ptr + namesize); + + if (filesize > 0) + { + struct file *file = vfs_open(pathname, O_CREAT); + if (file) + { + vfs_write(file, (const char *)ptr, filesize); + vfs_close(file); + } + } + ptr = align_to_4(ptr + filesize); + } +} \ No newline at end of file diff --git a/lab7/kernel/cpio.h b/lab7/kernel/cpio.h new file mode 100644 index 000000000..5841cb54d --- /dev/null +++ b/lab7/kernel/cpio.h @@ -0,0 +1,34 @@ +#ifndef CPIOH +#define CPIOH + +// https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 + +#define CPIO_MAGIC "070701" // cpio magic number +#define CPIO_TRAILER "TRAILER!!!" // cpio end string + +struct cpio_newc_header +{ + char c_magic[6]; // magic number "070701" + char c_ino[8]; // "i-node" number + char c_mode[8]; // file mode + char c_uid[8]; // user ID + char c_gid[8]; // group ID + char c_nlink[8]; // number of hard links + char c_mtime[8]; // modification time + char c_filesize[8]; // file size + char c_devmajor[8]; // major device number + char c_devminor[8]; // minor device number + char c_rdevmajor[8]; // only valid for chr and blk special files + char c_rdevminor[8]; // only valid for chr and blk special files + char c_namesize[8]; // length of name in bytes ( include '\0') + char c_check[8]; // checksum +}; + +unsigned long long align_to_4(unsigned long long size); // align to 4 byte +void cpio_list(); // list rootfs file +char* cpio_content(char* name); // get file content +void cpio_run_user_program(char* name, int enable_timer); +int cpio_load_user_program_and_get_size(char* name, unsigned long long loadAddr); +void cpio_populate_rootfs(); + +#endif diff --git a/lab7/kernel/devicetree.c b/lab7/kernel/devicetree.c new file mode 100644 index 000000000..6606497c6 --- /dev/null +++ b/lab7/kernel/devicetree.c @@ -0,0 +1,183 @@ +#include "devicetree.h" +#include "uart.h" +#include "util.h" + +unsigned long int align4(unsigned long int size) +{ + unsigned long int residual = size % 4; + + if(residual > 0) + return size + (4 - residual); + else + return size; +} + +unsigned int convert_bigendian(void *ptr) +{ + // ex. 1234 -> 4321 + + unsigned char *bytes = (unsigned char *)ptr; + + unsigned int value = bytes[3]; + value |= bytes[2] << 8; + value |= bytes[1] << 16; + value |= bytes[0] << 24; + + return value; +} + +void dtb_ls() +{ + unsigned long int dtb = *((unsigned long int*)DTB_ADDR); + struct fdt_header *header = (struct fdt_header *)dtb; + + if (convert_bigendian(&header->magic) != (unsigned long int)DTB_MAGIC) + { + uart_putstr("Format error !"); + return; + } + + return parse_node(header, dtb); +} + +void print_indent(int level) +{ + while(level > 0) + { + uart_putstr("-"); + level--; + } +} + +void parse_node(struct fdt_header *header, unsigned long int dtb) +{ + unsigned long int ptr = dtb + convert_bigendian(&header->off_dt_struct); + unsigned long int endptr = ptr + convert_bigendian(&header->totalsize); + int level = 0; + + while (ptr < endptr) + { + unsigned int tag = convert_bigendian((char *)ptr); + ptr += sizeof(unsigned int); + + switch (tag) + { + case FDT_BEGIN_NODE: // Start node + + // struct fdt_node_header + // { + // fdt32_t tag; => 4 byte + // char name[0]; => length + '\0' + // }; + print_indent(level); + uart_putstr((char *)ptr); + uart_putstr("\n"); + level++; + + ptr += align4(strlen((char *)ptr) + 1); // length + '\0' + break; + + case FDT_END_NODE: // End node + level--; + break; + + case FDT_NOP: // Nop + break; + + case FDT_PROP: // Property + { + // struct fdt_property + // { + // unsigned int tag; => 4 byte + // unsigned int len; => 4 byte + // unsigned int nameoff; => 4 byte + // char data[0]; => len + // }; + + unsigned int len = convert_bigendian((char *)ptr); // data length + ptr += sizeof(unsigned int); // nameoff + ptr += sizeof(unsigned int); // data + ptr += align4(len); // next node + break; + } + + case FDT_END: // Device tree end + break; + } + } +} + +int dtb_cat(char* name, dtb_callback cb) +{ + unsigned long int dtb = *((unsigned long int*)DTB_ADDR); + struct fdt_header *header = (struct fdt_header *)dtb; + + if (convert_bigendian(&header->magic) != (unsigned long int)DTB_MAGIC) + { + uart_putstr("Format error !"); + return -1; + } + + return parse_and_callback(header, dtb, name, cb); +} + +int parse_and_callback(struct fdt_header *header, unsigned long int dtb, char* name, dtb_callback cb) +{ + unsigned long int ptr = dtb + convert_bigendian(&header->off_dt_struct); + unsigned long int endptr = ptr + convert_bigendian(&header->totalsize); + unsigned long int strptr = dtb + convert_bigendian(&header->off_dt_strings); + + while (ptr < endptr) + { + unsigned int tag = convert_bigendian((char *)ptr); + ptr += sizeof(unsigned int); + + switch (tag) + { + case FDT_BEGIN_NODE: // Start node + + // struct fdt_node_header + // { + // fdt32_t tag; => 4 byte + // char name[0]; => length + '\0' + // }; + if (strcmp(name, (char *)ptr)) + { + // if node name is search name , excute callback + cb(ptr, strptr); + return 0; + } + + ptr += align4(strlen((char *)ptr) + 1); // length + '\0' + break; + + case FDT_END_NODE: // End node + break; + + case FDT_NOP: // Nop + break; + + case FDT_PROP: // Property + { + // struct fdt_property + // { + // unsigned int tag; => 4 byte + // unsigned int len; => 4 byte + // unsigned int nameoff; => 4 byte + // char data[0]; => len + // }; + + unsigned int len = convert_bigendian((char *)ptr); // data length + ptr += sizeof(unsigned int); // nameoff + ptr += sizeof(unsigned int); // data + ptr += align4(len); // next node + break; + } + + case FDT_END: // Device tree end + break; + } + } + + return -1; +} \ No newline at end of file diff --git a/lab7/kernel/devicetree.h b/lab7/kernel/devicetree.h new file mode 100644 index 000000000..4fe22fcd3 --- /dev/null +++ b/lab7/kernel/devicetree.h @@ -0,0 +1,37 @@ +#ifndef DEVICETREEH +#define DEVICETREEH + +#define DTB_ADDR 0x9000000 // dtb start address +#define DTB_MAGIC 0xd00dfeed // magic value + +#define FDT_BEGIN_NODE 1 // start node +#define FDT_END_NODE 2 // end node +#define FDT_PROP 3 // property +#define FDT_NOP 4 // nop +#define FDT_END 9 // dtb end + +struct fdt_header +{ + unsigned int magic; // magic word FDT_MAGIC + unsigned int totalsize; // total size of DT block + unsigned int off_dt_struct; // offset to structure + unsigned int off_dt_strings; // offset to strings + unsigned int off_mem_rsvmap; // offset to memory reserve map + unsigned int version; // format version (17) + unsigned int last_comp_version; // last compatible version + unsigned int boot_cpuid_phys; // Which physical CPU id we're booting on + unsigned int size_dt_strings; // size of the strings block + unsigned int size_dt_struct; // size of the structure block +}; + +typedef void (*dtb_callback)(unsigned long int ptr, unsigned long int strptr); + +unsigned long int align4(unsigned long int size); +unsigned int convert_bigendian(void *ptr); // convert char to bigendian +void dtb_ls(); // list rootfs file +void parse_node(struct fdt_header *header, unsigned long int dtb); +int dtb_cat(char* name, dtb_callback cb); // get file content +int parse_and_callback(struct fdt_header *header, unsigned long int dtb, char* name, dtb_callback cb); +void print_indent(int level); + +#endif diff --git a/lab7/kernel/exception.c b/lab7/kernel/exception.c new file mode 100644 index 000000000..2dff16017 --- /dev/null +++ b/lab7/kernel/exception.c @@ -0,0 +1,314 @@ +#include "exception.h" +#include "uart.h" +#include "util.h" +#include "timer.h" +#include "thread.h" + +// http://www.zencoder.info/2020/01/15/get-current-el-in-arm64/ +/* +* show current exception level +* +* use "mrs x CurrentEL" get current exception level and put on x +* +* CurrentEL: register for get current exception level +* %0 : output operand +* "=r" (current_el): output to current_el +* ": : :"memory" : gcc compiler fence +* +* 要右移2,請參見http://www.lujun.org.cn/?p=1676 +* +* 4 3 2 1 0 +* --------------------- +* | | EL | | | +* --------------------- +* +* 因為current_el在第3第2位,所以要右移2才是我們要的值 +*/ +void show_current_el() +{ + unsigned long long current_el; + asm volatile("mrs %0, CurrentEL\n\t" : "=r" (current_el) : : "memory"); + + char buf[16] = {0}; + uart_putstr("currentEL is "); + unsignedlonglongToStr(current_el >> 2, buf); + uart_putstr(buf); +} + +/* +* exception_entry +* +* The method use for synchronous excpetion handle +* +* In required 1-3 +* Only needs to print the content of [spsr_el1], [elr_el1], and [esr_el1] in the exception handler +* +* [spsr_el1]: current processor’s state +* [elr_el1]: the exception return address +* [esr_ell]: if exception is synchronous exception or an an SError interrupt, save cause of that exception +* +* note: if core timer is enable in requirement 2, we don't show exception info message +*/ +void exception_entry() +{ + // if core timer enable, not show message + + unsigned long long cntp_ctl_el0; + asm volatile("mrs %0, cntp_ctl_el0" : "=r"(cntp_ctl_el0)); + if(cntp_ctl_el0 != 0) + return; + + unsigned long long spsr_el1, elr_el1, esr_el1; + + asm volatile("mrs %0, spsr_el1" : "=r"(spsr_el1)); + asm volatile("mrs %0, elr_el1" : "=r"(elr_el1)); + asm volatile("mrs %0, esr_el1" : "=r"(esr_el1)); + + char buf[16] = {0}; + uart_putstr("print exception info: \n"); + uart_putstr("SPSR_EL1: "); + unsignedlonglongToStrHex(spsr_el1, buf); + uart_putstr(buf); + uart_putstr("\n"); + uart_putstr("ELR_EL1: "); + unsignedlonglongToStrHex(elr_el1, buf); + uart_putstr(buf); + uart_putstr("\n"); + uart_putstr("ESR_EL1: "); + unsignedlonglongToStrHex(esr_el1, buf); + uart_putstr(buf); + uart_putstr("\n\n"); +} + +void set_x0(unsigned long val) +{ + unsigned long* task; + asm volatile("mrs %0, tpidr_el1 \n":"=r"(task):); + task[16] = val; +} + +/* +* lowerEL_sync_interrupt +* +* 1.讀取user stack的傳遞的參數 +* - 將user stack的參數放入x0跟x1暫存器,x0的內容放到sp-8的位置,x1的內容放到sp-16的位置 +* - 再將x0跟x1暫存器的內容讀出,將sp-8的內容給x0變數,sp-16的內容給x1變數 +* +* 2. 取出svc的值,依據不同的值做不同處理 +*/ +void lowerEL_sync_interrupt() +{ + // 1. 讀取user stack的傳遞的參數 + unsigned long x0,x1,x2; + + asm volatile("\ + str x0,[sp,-8]\n\ + str x1,[sp,-16]\n\ + str x2,[sp,-24]\n\ + "::); + + asm volatile("\ + ldr %0,[sp,-8]\n\ + ldr %1,[sp,-16]\n\ + ldr %2,[sp,-24]\n\ + ":"=r"(x0),"=r"(x1),"=r"(x2):); + + // 2. 取出svc的值,依據不同的值做不同處理 + unsigned long esr,svc; + asm volatile("mrs %0, esr_el1\n":"=r"(esr):); // 讀取esr_el1的值 + + // https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1- + // 判斷EC, bits [31:26],所以先右移26 bits + // 假如為0b010101,為svc的值 + if((esr >> 26) == 0b010101) // SVC instruction execution in AArch64 state. + { + svc = esr & 0x0ffff; + + if(svc == 0) + { + uart_putstr("svc 0\n"); + } + else if (svc == 1) // get pid + { + unsigned long pid = get_pid(); + set_x0(pid); + } + else if (svc == 2) // uart read + { + unsigned long ret = uart_gets((char*)x0,(int)x1); + set_x0(ret); + } + else if (svc == 3) // uart write + { + uart_putstr((char*)x0); + set_x0(x1); + } + else if (svc == 4) // exec + { + exec((const char*)x0, (char(*)[10])x1); + set_x0(0); + } + else if (svc == 5) // exit + { + exit(); + } + else if (svc == 6) // fork + { + int pid = fork(); + set_x0(pid); + } + else if (svc == 7) // open + { + struct file *file = vfs_open((char *)x0, (int)x1); + int fd = thread_register_fd(file); + set_x0(fd); + } + else if (svc == 8) // close + { + struct file *file = thread_get_file((int)x0); + thread_clear_fd((int)x0); + int result = vfs_close(file); + set_x0(result); + } + else if (svc == 9) // write + { + struct file *file = thread_get_file((int)x0); + const void *buf = (const void *)x1; + unsigned int len = (unsigned int)x2; + unsigned int size = vfs_write(file, buf, len); + set_x0(size); + } + else if (svc == 10) // read + { + struct file *file = thread_get_file((int)x0); + void *buf = (void *)x1; + unsigned int len = (unsigned int)x2; + unsigned int size = vfs_read(file, buf, len); + set_x0(size); + } + else if (svc == 11) // list + { + struct file *file = thread_get_file((int)x0); + void *buf = (void *)x1; + int index = (int)x2; + unsigned int size = vfs_list(file, buf, index); + set_x0(size); + } + else if (svc == 12) // mkdir + { + int result = vfs_mkdir((const char *)x0); + set_x0(result); + } + else if (svc == 13) // chdir + { + int result = vfs_chdir((const char *)x0); + set_x0(result); + } + else if (svc == 14) // mount + { + const char *device = (const char *)x0; + const char *mountpoint = (const char *)x1; + const char *filesystem = (const char *)x2; + int result = vfs_mount(device, mountpoint, filesystem); + set_x0(result); + } + else if (svc == 15) // unmount + { + int result = vfs_umount((const char *)x0); + set_x0(result); + } + } +} + +/* +* no_exception_handle +* +* The method use for not implement excpetion handle +* +* From TA, +* In this lab, we only focus on Synchronous and IRQ exceptions. +* +* and only handle exception vector table's +* 1. Exception from the currentEL while using SP_ELx +* 2. Exception from a lower EL and at least one lower EL is AARCH64. +* +* so, other exception or other table in exception vector table, we don't implement +*/ +void no_exception_handle() +{ + +} + +/* +* lowerEL_irq_interrupt +* +* The method use for lowerEL irq interrupt handle +* 1. distinguish interrupt source : core timer or uart +* 2. use different exception_handle handle different interrupt +* +* irq : https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf +* p.16 +* Core0 interrupt source & CNTHPIRQ interrupt +* +* critical section +* 3. before exception_handle, disable interrupt +* 4. after exception_handle, enable interrupt +* +*/ +void lowerEL_irq_interrupt() +{ + disable_interrupt(); + + unsigned int core_irq = (*CORE0_IRQ_SOURCE & (1 << 1)); + unsigned int uart_irq = (*IRQ_PENDING_1 & AUX_IRQ); + + // ARM core timer irq interrupt + if(core_irq) + core_timer_handle(); + // uart irq interrupt + else if(uart_irq) + uart_interrupt_handler(); + + enable_interrupt(); +} + +void currentEL_irq_interrupt() +{ + disable_interrupt(); + + unsigned int core_irq = (*CORE0_IRQ_SOURCE & (1 << 1)); + unsigned int uart_irq = (*IRQ_PENDING_1 & AUX_IRQ); + + // ARM core timer irq interrupt + if(core_irq) + timout_handle(); + // uart irq interrupt + else if(uart_irq) + uart_interrupt_handler(); + + enable_interrupt(); +} + +/* +* enable_interrupt +* +* DAIFClr register : enable D,A,I,F +* 0xf = 1111 , represent D,A,I,F enable all +* +*/ +void enable_interrupt() +{ + asm volatile("msr DAIFClr, 0xf"); +} + +/* +* disable_interrupt +* +* DAIFSet register : disable D,A,I,F +* 0xf = 1111 , represent D,A,I,F disable all +* +*/ +void disable_interrupt() +{ + asm volatile("msr DAIFSet, 0xf"); +} \ No newline at end of file diff --git a/lab7/kernel/exception.h b/lab7/kernel/exception.h new file mode 100644 index 000000000..59c15a768 --- /dev/null +++ b/lab7/kernel/exception.h @@ -0,0 +1,19 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +// https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf + +// cpu 0 irq interrupt source +#define CORE0_IRQ_SOURCE ((volatile unsigned int *)(0x40000060)) + +void show_current_el(); +void exception_entry(); +void lowerEL_sync_interrupt(); +void no_exception_handle(); +void lowerEL_irq_interrupt(); +void currentEL_irq_interrupt(); + +void enable_interrupt(); +void disable_interrupt(); + +#endif \ No newline at end of file diff --git a/lab7/kernel/exception_handle.S b/lab7/kernel/exception_handle.S new file mode 100644 index 000000000..967250f62 --- /dev/null +++ b/lab7/kernel/exception_handle.S @@ -0,0 +1,264 @@ + +// required 1-3 set exception_vector_table + +.align 11 // vector table should be aligned to 0x800 = 4096 = 2^11 +.global exception_vector_table +exception_vector_table: + + // Exception from current EL while using SP_EL0 + + b exception_no_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0, 0x80 = 2^7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + + // Exception from current EL while using SP_ELX (ex. EL1 -> EL1) + // only handler Sync & irq + + b currentEL_sync_handler + .align 7 + b currentEL_irq_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + + // Exception from a lower and at least one lower EL is AArch64 (ex. EL0 -> EL1) + // only handler Sync & irq + + b lowerEL_sync_handler + .align 7 + b lowerEL_irq_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + + // Exception from a lower and all lower ELs are AArch32 + + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + +// required 1-4 Save the user program’s context before executing the exception handler. + +// 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 + +/* + 參考thread的context,包含 + unsigned long x19; (8*0) + unsigned long x20; (8*1) + unsigned long x21; (8*2) + unsigned long x22; (8*3) + unsigned long x23; (8*4) + unsigned long x24; (8*5) + unsigned long x25; (8*6) + unsigned long x26; (8*7) + unsigned long x27; (8*8) + unsigned long x28; (8*9) + unsigned long fp; (8*10) + unsigned long lr; (8*11) + unsigned long sp; (8*12) + + // for user program (spsr_el1, elr_el1, sp_el0)(user register x0-x30) + unsigned long spsr_el1; (8*13) + unsigned long elr_el1; (8*14) + unsigned long sp_el0; (8*15) + unsigned long reg[31]; // user register x0-x30 (8*16)-(8*46) + + 儲存task + 也就是將user register x0-x30還有spsr_el1, elr_el1, sp_el0等狀態都存到目前的thread的context內 + + 步驟有點多,可以略過 + 1. str x0, [sp, -8] 先將x0內容存放在sp-8的位置 + 2. mrs x0, tpidr_el1 然後x0放目前的thread + 3. stp x2, x3, [x0, 8 * 18] 將x2, x3的內容存到x0偏移8*18的位置,參考上面的context + reg[2]是在8*18,reg[3]是8*19,其餘以此類推依序將值塞入對應位置 + 4. str x30, [x0, 8 * 46],將x30的內容存到x0偏移8*46的位置 + 5. mov x9, x0,x0的內容給x9 + 6. ldr x0, [sp, -8] 將sp-8位置的內容給x0 + 7. stp x0, x1, [x9 ,8 * 16] 剛剛的x0跟x1,現在才放 + 8. mrs x10, spsr_el1,將spsr_el1的內容給x10 + 9. mrs x11, elr_el1,將elr_el1給x11 + 10.mrs x12, sp_el0,將sp_el0給x12 + 11.str x10, [x9, 8 * 13],將將x10的內容存到x9偏移8*13的位置 (就是剛剛的x0) + 12.stp x11, x12, [x9, 8 * 14],將將x11 x12的內容存到x9偏移8*14的位置 (就是剛剛的x0) + +*/ + +// save general registers to stack +.macro save_task + str x0, [sp, -8] + mrs x0, tpidr_el1 + //store x0&x1 later + stp x2, x3, [x0, 8 * 18] + stp x4, x5, [x0, 8 * 20] + stp x6, x7, [x0, 8 * 22] + stp x8, x9, [x0, 8 * 24] + stp x10, x11, [x0, 8 * 26] + stp x12, x13, [x0, 8 * 28] + stp x14, x15, [x0, 8 * 30] + stp x16, x17, [x0, 8 * 32] + stp x18, x19, [x0, 8 * 34] + stp x20, x21, [x0, 8 * 36] + stp x22, x23, [x0, 8 * 38] + stp x24, x25, [x0, 8 * 40] + stp x26, x27, [x0, 8 * 42] + stp x28, x29, [x0, 8 * 44] + str x30, [x0, 8 * 46] + mov x9, x0 + ldr x0, [sp, -8] + stp x0, x1, [x9 ,8 * 16] + mrs x10, spsr_el1 + mrs x11, elr_el1 + mrs x12, sp_el0 + str x10, [x9, 8 * 13] + stp x11, x12, [x9, 8 * 14] +.endm + +/* + 參考thread的context,包含 + unsigned long x19; (8*0) + unsigned long x20; (8*1) + unsigned long x21; (8*2) + unsigned long x22; (8*3) + unsigned long x23; (8*4) + unsigned long x24; (8*5) + unsigned long x25; (8*6) + unsigned long x26; (8*7) + unsigned long x27; (8*8) + unsigned long x28; (8*9) + unsigned long fp; (8*10) + unsigned long lr; (8*11) + unsigned long sp; (8*12) + + // for user program (spsr_el1, elr_el1, sp_el0)(user register x0-x30) + unsigned long spsr_el1; (8*13) + unsigned long elr_el1; (8*14) + unsigned long sp_el0; (8*15) + unsigned long reg[31]; // user register x0-x30 (8*16)-(8*46) + + 還原task + 也就是將由目前的thread的context還原至user register x0-x30還有spsr_el1, elr_el1, sp_el0暫存器 + + 步驟有點多,可以略過 + 1. mrs x9, tpidr_el1 先將x9放目前的thread + 2. ldr x10, [x9, 8 * 13] 將x9偏移8*13位置的內容給x10 + 3. ldp x11, x12, [x9, 8 * 14] 將x9偏移8*14 8*15位置的內容給x11跟x12 + 4. msr spsr_el1, x10 ,x10的內容給spsr_el1 + 5. msr elr_el1, x11,x11的內容給elr_el1 + 6. msr sp_el0, x12,x12的內容給sp_el0 + 7. mov x0, x9, 把x9的內容給x0 (就是目前thread) + 8. ldp x2, x3, [x0, 8 * 18],將x0偏移8*18位置的內容給x2, x3 + 後面以此類推,依序將值塞入對應位置 + +*/ + +// load general registers from stack +.macro restore_task + mrs x9, tpidr_el1 + ldr x10, [x9, 8 * 13] + ldp x11, x12, [x9, 8 * 14] + msr spsr_el1, x10 + msr elr_el1, x11 + msr sp_el0, x12 + mov x0, x9 + //restore x0&x1 later + ldp x2, x3, [x0, 8 * 18] + ldp x4, x5, [x0, 8 * 20] + ldp x6, x7, [x0, 8 * 22] + ldp x8, x9, [x0, 8 * 24] + ldp x10, x11, [x0, 8 * 26] + ldp x12, x13, [x0, 8 * 28] + ldp x14, x15, [x0, 8 * 30] + ldp x16, x17, [x0, 8 * 32] + ldp x18, x19, [x0, 8 * 34] + ldp x20, x21, [x0, 8 * 36] + ldp x22, x23, [x0, 8 * 38] + ldp x24, x25, [x0, 8 * 40] + ldp x26, x27, [x0, 8 * 42] + ldp x28, x29, [x0, 8 * 44] + ldr x30, [x0, 8 * 46] + ldp x0, x1, [x0, 8 * 16] +.endm + +lowerEL_sync_handler: + save_task + bl lowerEL_sync_interrupt // exception.c + restore_task + eret + +currentEL_sync_handler: + save_all + bl exception_entry // exception.c + load_all + eret + +lowerEL_irq_handler: + save_all + bl lowerEL_irq_interrupt // exception.c + load_all + eret + +currentEL_irq_handler: + save_all + bl currentEL_irq_interrupt // exception.c + load_all + eret + +exception_no_handler: + save_all + bl no_exception_handle // exception.c + load_all + eret \ No newline at end of file diff --git a/lab7/kernel/fat32.c b/lab7/kernel/fat32.c new file mode 100644 index 000000000..0f9eb20f5 --- /dev/null +++ b/lab7/kernel/fat32.c @@ -0,0 +1,429 @@ +#include "fat32.h" +#include "util.h" +#include "uart.h" +#include "allocator.h" +#include "sdhost.h" + +struct fatfs_boot_sector* fat_boot_sector; +struct fatfs_dir_entry* fat_root_dir_entry; +int fat_starting_sector; +int data_starting_sector; +int root_starting_sector; + +struct vnode_operations* fatfs_v_ops; +struct file_operations* fatfs_f_ops; + +struct dynamic_allocator *dyalloc_fat = 0; + +/* +* fatfs_init 初始化fatfs +* +* 1. 初始化v_node操作(分配記憶體,註冊查找、建立的方法) +* 2. 初始化file操作(分配記憶體,註冊寫入、讀取的方法) +* 3. 呼叫sd卡driver的sd_init進行初始化 +*/ +void fatfs_init() +{ + dyalloc_fat = dynamic_allocator_init(); + + fatfs_v_ops = (struct vnode_operations*)dynamic_alloc(dyalloc_fat, sizeof(struct vnode_operations)); + fatfs_v_ops->lookup = fatfs_lookup; + fatfs_v_ops->set_parent = fatfs_set_parent; + + fatfs_f_ops = (struct file_operations*)dynamic_alloc(dyalloc_fat, sizeof(struct file_operations)); + fatfs_f_ops->write = fatfs_write; + fatfs_f_ops->read = fatfs_read; + fatfs_f_ops->list = fatfs_list; + + sd_init(); +} + +/* +* fatfs_setup_mount 實作mount方法(Mount Fat32 file system in SD card) +* +* required 1-1 Get the FAT32 partition (Fat32 file system is in first partition of sd card) +* required 1-2 Parse the FAT32’s metadata and set up the mount. +* +* 1. 解析並顯示mbr metadata資訊 +* 2. 讀取boot sector +* - 配置一塊fatfs_boot_sector記憶體,由SD卡讀取一個block,並將內容複製給fatfs_boot_sector +* 3. 計算資料起始sector位址(起始sector + 保留sector數 + boot sector個數 * 一個sector多大) +* 4. 建立根目錄 +* - 計算根目錄起始sector位址 +* - 由SD卡讀取一個block, 建立fatfs_dir_entry實體 +* - 建立根目錄entry及vnode,並將SD卡根目錄entry內容建立為根目錄vnode +* 5. 掛載至檔案系統 +*/ +int fatfs_setup_mount(struct filesystem* fs, struct mount* mount) +{ + // 1. 解析並顯示mbr metadata資訊 + struct mbr_partition_entry* entry = parse_mbr_metadata(); + + // 2. 讀取boot sector + // - 配置一塊fatfs_boot_sector記憶體,由SD卡讀取一個block,並將內容複製給fatfs_boot_sector + fat_starting_sector = entry->starting_sector; + + char* fat_boot = (char*)dynamic_alloc(dyalloc_fat, BLOCK_SIZE); + readblock(fat_starting_sector, fat_boot); + int boot_sector_size = sizeof(struct fatfs_boot_sector); + fat_boot_sector = (struct fatfs_boot_sector*)dynamic_alloc(dyalloc_fat, boot_sector_size); + char* src = (char*)fat_boot; + char* dst = (char*)fat_boot_sector; + for (int i = 0; i < boot_sector_size; i++) + dst[i] = src[i]; + + dynamic_free(dyalloc_fat, (unsigned long long)fat_boot); + + // 3.計算資料起始sector位址(起始sector + 保留sector數 + boot sector個數 * 一個sector多大) + data_starting_sector = + fat_starting_sector + fat_boot_sector->reserved_sector_count + + fat_boot_sector->fat_count * fat_boot_sector->sectors_per_fat_32; + + // 4.建立根目錄 + // - 計算根目錄起始sector位址 + root_starting_sector = get_starting_sector(fat_boot_sector->root_cluster); + + // - 由SD卡讀取一個block, 建立fatfs_dir_entry實體 + char* fat_root = (char*)dynamic_alloc(dyalloc_fat, BLOCK_SIZE); + readblock(root_starting_sector, fat_root); + fat_root_dir_entry = (struct fatfs_dir_entry*)fat_root; + + // - 建立根目錄entry及vnode,並將SD卡根目錄entry內容建立為根目錄vnode + struct fatfs_entry* root_entry = (struct fatfs_entry*)dynamic_alloc(dyalloc_fat, sizeof(struct fatfs_entry)); + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fat, sizeof(struct vnode)); + vnode->mount = mount; + vnode->v_ops = fatfs_v_ops; + vnode->f_ops = fatfs_f_ops; + vnode->internal = (void*)root_entry; + root_entry->parent_vnode = 0; + fatfs_set_entry(root_entry, FILE_DIRECTORY, vnode, fat_boot_sector->root_cluster, 4096); + fatfs_set_directory(root_entry, fat_root_dir_entry); + // 5.掛載至檔案系統 + mount->fs = fs; + mount->root = vnode; + + return 1; +} + +/* +* parse_mbr_metadata (解析並顯示MBR資訊) +* +* 1. 讀取MBR +* - mbr位於sd card第一個sector,使用readblock讀取 +* - 配置一塊mbr_partition_entry記憶體,並將mbr內容複製給mbr_partition_entry +* 2. 印出[Partition type]、[Partition size]、[Block index]資訊 +* 3. 回傳mbr_partition_entry +*/ +struct mbr_partition_entry* parse_mbr_metadata() +{ + // 1. 讀取MBR + // mbr位於sd card第一個sector,使用readblock讀取 + /* + * MBR 是由 512 位元組組成,MBR 只佔用了其中的 446 個位元組, + * 另外 64 個位元組交給了 DPT(Disk Partition Table 硬碟分區表), + * 最後兩個位元組 “55,AA” 是分區的結束標誌 + */ + char* mbr = (char*)dynamic_alloc(dyalloc_fat, BLOCK_SIZE); + readblock(0, mbr); + if (mbr[510] != 0x55 || mbr[511] != 0xAA) + { + uart_putstr("bad magic in MBR\n"); + return 0; + } + + // 配置一塊mbr_partition_entry記憶體,並將mbr內容複製給mbr_partition_entry + int entry_size = sizeof(struct mbr_partition_entry); + struct mbr_partition_entry* entry = (struct mbr_partition_entry*)dynamic_alloc(dyalloc_fat, entry_size); + char* src = (char*)mbr; + char* dst = (char*)entry; + for (int i = 0; i < entry_size; i++) + dst[i] = src[MBR_PARTITION_BASE + i]; // 由446開始讀取硬碟分區表 + + dynamic_free(dyalloc_fat, (unsigned long long)mbr); + + // 2. 印出[Partition type]、[Partition size]、[Block index]資訊 + + uart_putstr("\n========== FAT32 init ==========\n"); + char buf[16] = {0}; + uart_putstr("Partition type: 0x"); + unsignedlonglongToStrHex(entry->partition_type, buf); + uart_putstr(buf); + + if (entry->partition_type == 0xB) // 代表為32bit FAT + uart_putstr(" (FAT32 with CHS addressing)"); + + uart_putstr("\nPartition size: "); + unsignedlonglongToStr(entry->sector_count, buf); + uart_putstr(buf); + uart_putstr(" (sectors)\n"); + + uart_putstr("Block index: "); + unsignedlonglongToStr(entry->starting_sector, buf); + uart_putstr(buf); + uart_putstr("\n================================\n\n"); + + // 3. 回傳mbr_partition_entry + return entry; +} + +/* +* fatfs_set_entry 設定節點內容 +* +* fatfs_entry結構,包含name、name_len、starting_cluster、type、vnode、parent_vnode、child、buf屬性 +* 1. name及name_len在外部設定 +* 2. 設定starting_cluster +* 3. 設定vnode +* 4. 設定type +* 5. parent_vnode外面設定 +* 6. 分配buf記憶體,並設定buf內容為空 +* 7. 假如節點為目錄,初始化目錄下子檔案 +*/ +void fatfs_set_entry(struct fatfs_entry* entry, enum FILE_TYPE type, struct vnode* vnode, int starting_cluster, int buf_size) +{ + entry->starting_cluster = starting_cluster; + entry->vnode = vnode; + entry->type = type; + entry->buf = (struct fatfs_buf*)dynamic_alloc(dyalloc_fat, sizeof(struct fatfs_buf)); + entry->buf->size = buf_size; + + for (int i = 0; i < FATFS_BUF_SIZE; i++) + entry->buf->buffer[i] = '\0'; + + if (entry->type == FILE_DIRECTORY) + { + for (int i = 0; i < MAX_FILES_IN_DIR; ++i) + { + entry->child[i] = (struct fatfs_entry*)dynamic_alloc(dyalloc_fat, sizeof(struct fatfs_entry)); + entry->child[i]->name[0] = 0; + entry->child[i]->type = FILE_NONE; + entry->child[i]->parent_vnode = vnode; + } + } +} + +/* +* fatfs_set_directory 設定子檔案or子資料夾 +* +* 傳入fatfs_entry 及 fatfs_dir_entry, 其中fatfs_dir_entry為SD卡的 +* +* 使用for迴圈,逐一設定 +* 1. 判斷檔名是否有值(去除雜訊) +* - 取出dir_entry的檔名及副檔名組合出檔名 +* - 建立vnode並設定子節點屬性及內容 +*/ +void fatfs_set_directory(struct fatfs_entry* entry, struct fatfs_dir_entry* dir_entry) +{ + for (int i = 0; i < MAX_FILES_IN_DIR; ++i) + { + // 1. 判斷檔名是否有值(去除雜訊) + int flag = 0; + for (int j = 0; j < 8; j++) + { + if ((dir_entry + i)->filename[j] == 0) + { + flag = 1; + break; + } + } + + if ((dir_entry + i)->filename[0] && !flag) + { + // - 取出dir_entry的檔名及副檔名組合出檔名 + // ex. filename = test, extension = txt, name => test.txt + strncpy(entry->child[i]->name, (char*)((dir_entry + i)->filename), 8); + unsigned int len = strlen(entry->child[i]->name); + entry->child[i]->name_len = len; + + if ((dir_entry + i)->extension[0]) + { + *(entry->child[i]->name + len) = '.'; + strncpy(entry->child[i]->name + len + 1, (char*)((dir_entry + i)->extension), 3); + } + + // - 建立vnode + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fat, sizeof(struct vnode)); + vnode->mount = 0; + vnode->v_ops = entry->vnode->v_ops; + vnode->f_ops = entry->vnode->f_ops; + vnode->internal = entry->child[i]; + + // - 設定子節點內容 + // cluster_low是low 2 bytes of cluster, cluster_high是high 2 byte of cluster,所以cluster_high要 * 4 + // ex. cluster_high = 10, cluster_low = 01, cluster index為1001 = 9 + int starting_cluster = ((dir_entry + i)->cluster_high << 2) + (dir_entry + i)->cluster_low; + int buf_size = (dir_entry + i)->file_size; + fatfs_set_entry(entry->child[i], FILE_REGULAR, vnode, starting_cluster, buf_size); + } + } +} + +/* +* get_starting_sector 回傳指定cluster索引的起始位置 +* +* 因為由2開始編號所以要減2 +* => (cluster編號 - 2) * 每個cluster多大 + 資料起始位置 +*/ +int get_starting_sector(int cluster) +{ + return (cluster - 2) * fat_boot_sector->sectors_per_cluster + data_starting_sector; +} + +/* +* fatfs_lookup 實作lookup方法 +* +* 傳入目前目錄,(目標node,搜尋名稱),搜尋目錄下指定名稱的子目錄或子檔案 +* +* 1. 判斷傳入的dir_node檔案類型是否為目錄,假如不是目錄回傳失敗 +* 2. 假如搜尋名稱為"."表示為目前目錄,target = 目前節點 +* 3. 假如搜尋名稱為".."表示為上一層目錄 +* - 如果沒有父節點,回傳失敗 +* - 否則target = 父節點 +* 4. 搜尋目前目錄下每一個子檔案 +* - 假如名稱 = 搜尋名稱,target = 子檔案的節點,回傳成功 +* 5. 都不符合就回傳失敗 +*/ +int fatfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)dir_node->internal; + + if (entry->type != FILE_DIRECTORY) + return 0; + if (strcmp((char*)component_name, ".")) + { + *target = entry->vnode; + return 1; + } + if (strcmp((char*)component_name, "..")) + { + if (!entry->parent_vnode) + return 0; + *target = entry->parent_vnode; + return 1; + } + + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + entry = ((struct fatfs_entry*)dir_node->internal)->child[i]; + if (strcmp(entry->name, (char*)component_name)) + { + *target = entry->vnode; + return 1; + } + } + + return 0; +} + +/* +* fatfs_set_parent 實作set_parent方法 +* +* 傳入子節點,父節點 +* +* 1. 取出子節點內容結構 +* 2. 設定父節點為傳入的父節點 +*/ +int fatfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)child_node->internal; + entry->parent_vnode = parent_vnode; + return 1; +} + +/* +* fatfs_list 實作list方法 +* +* 傳入檔案,buffer,索引,列出資料夾下指定index的檔名,並回傳檔案size +* +* 1. 取出節點內容結構 +* 2. 假如不是目錄,回傳失敗 +* 3. index超過最大子節點數,回傳失敗 +* 4. 資料夾下指定index子節點未分配,回傳失敗 +* 5. 複製資料夾下指定index的檔名到buf +* 6. 回傳檔案size +*/ +int fatfs_list(struct file* file, void* buf, int index) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)file->vnode->internal; + + if (entry->type != FILE_DIRECTORY) + return -1; + if (index >= MAX_FILES_IN_DIR) + return -1; + + if (entry->child[index]->type == FILE_NONE) + return 0; + + strcpy((char*)buf, entry->child[index]->name); + return entry->child[index]->buf->size; +} + +//======================================================================= + +/* +* fatfs_write 實作write方法 +* +* 傳入檔案,buffer,長度 +* +* 1. 取出節點內容結構 +* 2. 由buf讀取len個byte,寫入節點的buffer內 +* 3. 節點檔案的buf大小 = file當前位置(file大小) +* 4. 檢查子檔案or子資料夾,假如檔名=節點名稱,設定檔案大小=buf大小 +* 5. 取得對應SD檔案cluster,抓出sector位置,並寫入buf內容 +* 6. 回傳寫入的byte數 +*/ +int fatfs_write(struct file* file, const void* buf, unsigned int len) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)file->vnode->internal; + for (unsigned int i = 0; i < len; i++) + { + entry->buf->buffer[file->f_pos++] = ((char*)buf)[i]; + if (entry->buf->size < file->f_pos) + entry->buf->size = file->f_pos; + } + + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + if (strcmpn((char*)((fat_root_dir_entry + i)->filename), entry->name, entry->name_len)) + (fat_root_dir_entry + i)->file_size = entry->buf->size; + } + + writeblock(root_starting_sector, (char*)fat_root_dir_entry); + int starting_sector = get_starting_sector(entry->starting_cluster); + writeblock(starting_sector, entry->buf->buffer); + + return len; +} + +/* +* fatfs_read 實作read方法 +* +* 傳入檔案,buffer,長度,由傳入的buf中,寫入len byte到該節點的buf內 +* +* 1. 取出節點內容結構 +* 2. 取得對應SD檔案cluster,抓出起始sector位置 +* 3. 讀取sector內容到buffer +* +* 4. 由節點的buffer內取出len byte內容,傳入buf +* 5. 每讀一byte, read_len讀取長度++ +* 6. 假如已讀完節點buffer的內容(讀到buf size了),跳出 +* 7. 回傳讀了幾個byte +*/ +int fatfs_read(struct file* file, void* buf, unsigned int len) +{ + unsigned int read_len = 0; + struct fatfs_entry* entry = (struct fatfs_entry*)file->vnode->internal; + int starting_sector = get_starting_sector(entry->starting_cluster); + + readblock(starting_sector, entry->buf->buffer); + + for (unsigned int i = 0; i < len; i++) + { + ((char*)buf)[i] = entry->buf->buffer[file->f_pos++]; + read_len++; + + if (read_len == entry->buf->size) + break; + } + + return read_len; +} diff --git a/lab7/kernel/fat32.h b/lab7/kernel/fat32.h new file mode 100644 index 000000000..84e303ccb --- /dev/null +++ b/lab7/kernel/fat32.h @@ -0,0 +1,114 @@ +#ifndef FAT32_H +#define FAT32_H + +#include "vfs.h" + +#define BLOCK_SIZE 512 +#define MBR_PARTITION_BASE 0x1BE +#define MAX_FILES_IN_DIR 16 +#define FATFS_BUF_SIZE (10 * 1024) + +// https://en.wikipedia.org/wiki/Master_boot_record +// The struct of partition of Master Boot Record (MBR) for disk +struct mbr_partition_entry +{ + unsigned char status_flag; // 0x0 + unsigned char partition_begin_head; // 0x1 + unsigned short partition_begin_sector; // 0x2 + unsigned char partition_type; // 0x4 + unsigned char partition_end_head; // 0x5 + unsigned short partition_end_sector; // 0x6 + unsigned int starting_sector; // 0x8 + unsigned int sector_count; // 0xC +} __attribute__((packed)); + +// https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system +// Boot Sector +struct fatfs_boot_sector +{ + unsigned char bootjmp[3]; // 0x00 + unsigned char oem_name[8]; // 0x08 + + // https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#BPB + // BIOS Parameter Block + unsigned short bytes_per_sector; // 0x0B-0x0C + unsigned char sectors_per_cluster; // 0x0D + unsigned short reserved_sector_count; // 0x0E-0x0F + unsigned char fat_count; // 0x10 + unsigned short root_entry_count; // 0x11-0x12 + unsigned short total_sectors; // 0x13-0x14 + unsigned char media_descriptor; // 0x15 + unsigned short sectors_per_fat; // 0x16-0x17 + unsigned short sectors_per_track; // 0x18-0x19 + unsigned short head_count; // 0x1A-0x1B + unsigned int hidden_sector_count; // 0x1C-0x1F + unsigned int total_sectors_32; // 0x20-0x23 + + //https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#EBPB + //Extended BIOS Parameter Block + unsigned int sectors_per_fat_32; // 0x24-0x27 + unsigned short mirror_flags; // 0x28-0x29 + unsigned short fat_version; // 0x2A-0x2B + unsigned int root_cluster; // 0x2C-0x2F + unsigned short info_sector_number; // 0x30-0x31 + unsigned short backup_boot_sector_count;// 0x32-0x33 + unsigned char reserved_0[12]; // 0x34-0x3F + unsigned char drive_number; // 0x40 + unsigned char reserved_1; // 0x41 + unsigned char boot_signature; // 0x42 + unsigned int volume_id; // 0x43-0x46 + unsigned char volume_label[11]; // 0x47-0x51 + unsigned char fat_type_label[8]; // 0x52-0x59 +} __attribute__((packed)); + +// https://en.wikipedia.org/wiki/8.3_filename +// Struct of fat32 directory entry. Short Filenames(SFN) version +struct fatfs_dir_entry +{ + unsigned char filename[8]; // 0x00-0x07, File name: 8 ASCII characters + unsigned char extension[3]; // 0x08-0x0A, File extension + unsigned char attributes; // 0x0B, Attributes of the file + unsigned char reserved; // 0x0C + unsigned char created_time_ms; // 0x0D + unsigned short created_time; // 0x0E-0x0F + unsigned short created_date; // 0x10-0x11 + unsigned short last_access_date; // 0x12-0x13 + unsigned short cluster_high; // 0x14-0x15 + unsigned short last_modified_time; // 0x16-0x17 + unsigned short last_modified_date; // 0x18-0x19 + unsigned short cluster_low; // 0x1A-0x1B + unsigned int file_size; // 0x1C-0x1F. The size of the file in bytes. +} __attribute__((packed)); + +struct fatfs_buf +{ + int flag; + unsigned int size; + char buffer[FATFS_BUF_SIZE]; +}; + +struct fatfs_entry +{ + char name[20]; + int name_len; // test1.txt -> 5 (before .) + int starting_cluster; + enum FILE_TYPE type; + struct vnode* vnode; + struct vnode* parent_vnode; + struct fatfs_entry* child[MAX_FILES_IN_DIR]; + struct fatfs_buf* buf; +}; + +void fatfs_init(); +int fatfs_setup_mount(struct filesystem* fs, struct mount* mount); +struct mbr_partition_entry* parse_mbr_metadata(); +int get_starting_sector(int cluster); +void fatfs_set_directory(struct fatfs_entry* entry, struct fatfs_dir_entry* dentry); +void fatfs_set_entry(struct fatfs_entry* entry, enum FILE_TYPE type, struct vnode* vnode, int starting_cluster, int buf_size); +int fatfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); +int fatfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode); +int fatfs_write(struct file* file, const void* buf, unsigned int len); +int fatfs_read(struct file* file, void* buf, unsigned int len); +int fatfs_list(struct file* file, void* buf, int index); + +#endif \ No newline at end of file diff --git a/lab7/kernel/gpio.h b/lab7/kernel/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/kernel/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/kernel/linker.ld b/lab7/kernel/linker.ld new file mode 100644 index 000000000..a06328e3d --- /dev/null +++ b/lab7/kernel/linker.ld @@ -0,0 +1,44 @@ +SECTIONS /* Use the SECTIONS keyword to declare a section, the following is its content */ +{ + . = 0x80000; /* move the 'location counter' to 0x30000 */ + .text : /* assign .text section */ + { + KEEP(*(.text.boot)) /* make sure .text.boot will not be remove by linker-time garbage collection. */ + *(.text .text.*) /* *(.text .text.*) is all .text and .text. */ + } + .rodata : /* assign .rodata section */ + { + *(.rodata .rodata.*) /* *(.rodata .rodata.*) is all .rodata and .rodata. */ + } + .data : /* assign .data section */ + { + *(.data .data.*) /* *(.data .data.*) is all .data and .data. */ + } + .bss (NOLOAD) : /* assign .bss section, NOLOAD means not load at runtime */ + { + . = ALIGN(16); /* aligned to 16-byte boundaries. ex.0x0000000000080000 */ + __bss_start = .; /* assign __bss_start to the current memory position */ + *(.bss .bss.*) /* *(.bss .bss.*) is all .bss and .bss. */ + *(COMMON) /* assign uninitialized data section */ + __bss_end = .; /* assign __bss_end to the current memory position */ + } + _end = .; /* assign _end to the current memory position */ +} +__bss_size = (__bss_end - __bss_start) >> 3; /* _bss_size set to 8 */ + +/* + KEEP: make sure the section will not be remove by linker-time garbage collection. + .text: code section + .rodata: stores read-only data, e.g. const variables and strings + .bss : uninitialized data section (weak symbol) e.g. global var init to zero + .data : initialized data section (strong symbol) + NOLOAD : Does not load at runtime (because bss section is full of zero) + COMMON : uninitialized data section (weak symbol) e.g. global var just declare + + [example] + const int a = 0; // .rodata + int b = 3; // .data + int c = 0; // .bss + int d; // COMMON + +*/ \ No newline at end of file diff --git a/lab7/kernel/main.c b/lab7/kernel/main.c new file mode 100644 index 000000000..f23dc0d57 --- /dev/null +++ b/lab7/kernel/main.c @@ -0,0 +1,456 @@ +#include "uart.h" +#include "util.h" +#include "reboot.h" +#include "cpio.h" +#include "devicetree.h" +#include "buddy.h" +#include "allocator.h" +#include "exception.h" +#include "timer.h" +#include "thread.h" +#include "vfs.h" + +#define CMDSIZE 64 +char cmd[CMDSIZE] = {0}; +char last_cmd[CMDSIZE] = {0}; +int cmdSize = 0; + +int level = 0; +int isSync = 1; + +int vfsIsInit = 0; + +void cmd_init() +{ + cmdSize = 0; + for(int i = 0; i < CMDSIZE; i++) + cmd[i] = 0; +} + +// callback for print device +void callback(unsigned long int ptr, unsigned long int strptr) +{ + level++; + print_indent(level); + uart_putstr((char *)ptr); + uart_putstr("\n"); + + ptr += align4(strlen((char *)ptr) + 1); + + while (1) + { + unsigned int tag = convert_bigendian((char *)ptr); + ptr += sizeof(unsigned int); + + if (tag == FDT_BEGIN_NODE) + { + level++; + print_indent(level); + uart_putstr((char *)ptr); + uart_putstr("\n"); + + ptr += align4(strlen((char *)ptr) + 1); + } + else if (tag == FDT_END_NODE) + { + level--; + if (level == 0) + break; + } + else if (tag == FDT_NOP) + continue; + else if (tag == FDT_PROP) + { + unsigned int len = convert_bigendian((char *)ptr); // length + ptr += sizeof(unsigned int); // ptr + 4 + unsigned int nameoff = convert_bigendian((char *)ptr); // name offset + ptr += sizeof(unsigned int); + + print_indent(level); + uart_putstr("Property Name:"); + uart_putstr((char *)(strptr + nameoff)); + uart_putstr("\t"); + print_indent(level); + uart_putstr("Property Data:"); + uart_putstr((char *)ptr); + uart_putstr("\n"); + + ptr += align4(len); + } + else if (tag == FDT_END) + break; + } +} + +void cmd_handle() // parse command +{ + if(strcmp(cmd, "hello")) + { + uart_putstr("Hello World! \n"); + } + else if(strcmp(cmd, "help")) + { + uart_putstr("help print all available commands \n"); + uart_putstr("hello print Hello World! \n"); + uart_putstr("reboot reboot raspi3 \n"); + uart_putstr("ls list rootfs file \n"); + uart_putstr("cat cat [filename] open and read file \n"); + uart_putstr("dtbls list device tree \n"); + uart_putstr("dtbcat cat [nodename] get property \n"); + uart_putstr("buddy test buddy page frame allocator \n"); + uart_putstr("dynamic test dynamic allocator\n"); + uart_putstr("curEL get current expcetion level\n"); + uart_putstr("runUser load and run a user program in the initramfs [svc.elf]\n"); + uart_putstr("userTimer load and run a user program in the initramfs [svc.elf] (coreTimer)\n"); + uart_putstr("asyncRead use uart interrupt and read \n"); + uart_putstr("asyncWrite use uart interrupt and write \n"); + uart_putstr("recover disable interrupt \n"); + uart_putstr("setTimeout [MESSAGE] [SECONDS] set timer timeout and print message \n"); + uart_putstr("threadreq1 thread requirement 1\n"); + uart_putstr("threadreq2 thread requirement 2\n"); + uart_putstr("vfsreq1 vfs requirement 1: Populate the root file system with initramfs\n"); + uart_putstr("vfsreq2 vfs requirement 2\n"); + uart_putstr("vfselec1 vfs elective 1\n"); + uart_putstr("vfselec2 vfs elective 2\n"); + uart_putstr("fatreq1 get FAT32 partition and mount the FAT32 File System\n"); + uart_putstr("fatreq2 add read and write in FAT32 \n"); + } + else if(strcmp(cmd, "reboot")) + { + uart_putstr("reboot .... \n"); + raspi3_reboot(100); + while(1); // wait for reboot + } + else if(strcmp(cmd, "ls")) + { + cpio_list(); + uart_putstr("\n"); + } + else if(strcmpn(cmd, "cat", 2)) + { + // ex. cat init.txt + unsigned int length = strlen(cmd) - 4; + if (length > 0) + { + char *content = cpio_content(&cmd[4]); + if(content) + uart_putstr(content); + else + uart_putstr("file not found !"); + } + else + uart_putstr("no input file name !"); + + uart_putstr("\n"); + + } + else if(strcmp(cmd, "dtbls")) + { + dtb_ls(); + uart_putstr("\n"); + } + else if(strcmpn(cmd, "dtbcat", 5)) + { + level = 0; + unsigned int length = strlen(cmd) - 7; + if (length > 0) + { + int ret = dtb_cat(&cmd[7], callback); + if(ret == -1) + uart_putstr("device not found !"); + } + else + uart_putstr("no input device name !"); + uart_putstr("\n"); + } + else if(strcmp(cmd, "buddy")) + { + buddy_test(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "dynamic")) + { + dynamic_test(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "curEL")) + { + show_current_el(); + uart_putstr("\n"); + } + else if(strcmpn(cmd, "runUser", 6)) + { + // ex. runUser svc.elf + unsigned int length = strlen(cmd) - 8; + if (length > 0) + cpio_run_user_program(&cmd[8], 0); + else + uart_putstr("no input file name !"); + + uart_putstr("\n"); + } + else if(strcmpn(cmd, "userTimer", 8)) + { + uart_putstr("irq_handle\n"); + // ex. userTimer svc.elf + unsigned int length = strlen(cmd) - 10; + if (length > 0) + cpio_run_user_program(&cmd[10], 1); + else + uart_putstr("no input file name !"); + + uart_putstr("\n"); + } + else if(strcmp(cmd, "asyncRead")) + { + if(isSync == 1) + { + enable_interrupt(); + isSync = 0; + } + uart_putstr("start async read command..."); + uart_putstr("\n"); + } + else if(strcmp(cmd, "asyncWrite")) + { + if(isSync == 1) + { + enable_interrupt(); + isSync = 0; + } + uart_async_putstr("This is async write string..."); + uart_async_putstr("\n"); + } + else if(strcmp(cmd, "recover")) + { + if(isSync == 0) + { + disable_interrupt(); + isSync = 1; + uart_putstr("disable_interrupt ..."); + } + uart_putstr("\n"); + } + else if(strcmpn(cmd, "setTimeout", 9)) + { + int second = 0; + char msg[20] = {0}; + // get second and message + // setTimeout [MESSAGE] [SECONDS] + for(int i = 11; cmd[i] != '\0'; i++) + { + if(cmd[i] != ' ') + { + msg[i - 11] = cmd[i]; + } + else + { + msg[i - 11] = '\0'; + + for(int j = i + 1; cmd[j] != '\0'; j++) + { + if(cmd[j] >= '0' && cmd[j] <= '9') + second = second * 10 + cmd[j] - '0'; + } + } + } + + if(isSync == 1) + { + enable_interrupt(); + isSync = 0; + } + + add_timer(print_timer_msg, msg, second); + } + else if(strcmp(cmd, "threadreq1")) + { + thread_test(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "threadreq2")) + { + thread_test2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfsreq1")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + else + uart_putstr("vfs inited ! \n"); + + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfsreq2")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_vfs_req2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfselec1")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_vfs_ele1(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfselec2")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_vfs_ele2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "fatreq1")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_fat32_req1(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "fatreq2")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_fat32_req2(); + uart_putstr("\n"); + } + else if(strlen(cmd) != 0) + { + uart_putstr("command \""); + uart_putstr(cmd); + uart_putstr("\" not found, try \n"); + } + + uart_putstr("# "); +} + +int main() +{ + isSync = 1; + + cmd_init(); + uart_init(); + memory_init(); + init_timeout(); + init_thread(); + init_thread2(); + vfs_init(); + vfsIsInit = 1; + + // put welcome ascii art + uart_putstr("\n"); + uart_putstr(" .~~. .~~. \n"); + uart_putstr(" '. \\ ' ' / .' \n"); + uart_putstr(" .~ .~~~..~. \n"); + uart_putstr(" : .~.'~'.~. : \n"); + uart_putstr(" ~ ( ) ( ) ~ \n"); + uart_putstr(" ( : '~'.~.'~' : ) \n"); + uart_putstr(" ~ .~ ( ) ~. ~ NYCU OS 2021 \n"); + uart_putstr(" ( : '~' : ) Welcome Raspberry Pi 3 !! \n"); + uart_putstr(" '~ .~~~. ~' \n"); + uart_putstr(" '~' \n"); + uart_putstr("# "); + + char c; + char c2; + while(1) + { + if (isSync) // get char from user + c = uart_getchar(); + else + c = uart_async_getchar(); + + // https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/45106/ + switch(c) + { + case '\r': + case '\n': // 0X0A '\n' newline, parse command + while(cmd[cmdSize] != 0) + cmdSize++; + cmd[cmdSize] = '\0'; + cmdSize++; + uart_putstr("\n"); + for(int i = 0; i < cmdSize; i++) // store last command + last_cmd[i] = cmd[i]; + cmd_handle(); + cmd_init(); + break; + case 127: // backspace + if(cmdSize > 0) + { + cmdSize--; + cmd[cmdSize] = 0; + uart_putstr("\b \b"); + } + break; + case '[': + if (isSync) // get char from user + c2 = uart_getchar(); + else + c2 = uart_async_getchar(); + if (c2 == 'A') // cursor up + { + for(int i = 0; i < cmdSize; i++) // clear input + uart_putstr("\b \b"); + cmd_init(); + for(int i = 0; i < CMDSIZE; i++) // input last command + { + if(last_cmd[i] == 0) + break; + + cmd[i] = last_cmd[i]; + uart_sendchar(last_cmd[i]); + cmdSize++; + } + } + else if (c2 == 'C' && cmdSize < strlen(cmd)) // cursor left + { + uart_putstr("\033[C"); + cmdSize++; + } + else if (c2 == 'D' && cmdSize > 0) // cursor right + { + uart_putstr("\033[D"); + cmdSize--; + } + break; + default: + if (c > 31 && c < 127) // visible ascii + { + cmd[cmdSize] = c; + cmdSize++; + uart_sendchar(c); + } + break; + } + + if (cmdSize == CMDSIZE) + { + uart_putstr("\ncommand too long !\n# "); + cmd_init(); + } + } + + return 0; +} diff --git a/lab7/kernel/mmio.h b/lab7/kernel/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/kernel/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/kernel/reboot.c b/lab7/kernel/reboot.c new file mode 100644 index 000000000..f9c108a66 --- /dev/null +++ b/lab7/kernel/reboot.c @@ -0,0 +1,13 @@ +#include "reboot.h" + +void raspi3_reboot(int ticks) // reboot after watchdog timer expire +{ + *PM_RSTC = PM_PASSWORD | 0x20; // full reset + *PM_WDOG = PM_PASSWORD | ticks; // number of watchdog tick +} + +void cancel_reset() +{ + *PM_RSTC = PM_PASSWORD | 0; // full reset + *PM_WDOG = PM_PASSWORD | 0; // number of watchdog tick +} \ No newline at end of file diff --git a/lab7/kernel/reboot.h b/lab7/kernel/reboot.h new file mode 100644 index 000000000..7831d4bcb --- /dev/null +++ b/lab7/kernel/reboot.h @@ -0,0 +1,12 @@ +#ifndef REBOOT +#define REBOOT + +#define PM_PASSWORD 0x5A000000 + +#define PM_RSTC ((volatile unsigned int*)0x3F10001C) +#define PM_WDOG ((volatile unsigned int*)0x3F100024) + +void raspi3_reboot(int ticks); // reboot after watchdog timer expire +void cancel_reset(); // cancel_reset + +#endif \ No newline at end of file diff --git a/lab7/kernel/sched.s b/lab7/kernel/sched.s new file mode 100644 index 000000000..75ba8492b --- /dev/null +++ b/lab7/kernel/sched.s @@ -0,0 +1,39 @@ +/* + switch_to (prev, next) x0:prev x1:next + 1. save prev context + 2. recover next context + 3. let x1(next) is current thread +*/ + +.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 + + + +/* + Get the current thread pointer +*/ + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/lab7/kernel/sdhost.c b/lab7/kernel/sdhost.c new file mode 100644 index 000000000..d5b8f101d --- /dev/null +++ b/lab7/kernel/sdhost.c @@ -0,0 +1,251 @@ +#include "sdhost.h" +#include "gpio.h" + +// You need to modify the MMIO base according to your kernel mapping +// mmio +// #define KVA 0xffff000000000000 +// #define MMIO_BASE (KVA + 0x3f000000) + +// SD card command +#define GO_IDLE_STATE 0 +#define SEND_OP_CMD 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 +#define VOLTAGE_CHECK_PATTERN 0x1aa +#define STOP_TRANSMISSION 12 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define WRITE_SINGLE_BLOCK 24 +#define SD_APP_OP_COND 41 +#define SDCARD_3_3V (1 << 21) +#define SDCARD_ISHCS (1 << 30) +#define SDCARD_READY (1 << 31) +#define APP_CMD 55 + +// gpio +#define GPIO_BASE (MMIO_BASE + 0x200000) +#define GPIO_GPFSEL4 (GPIO_BASE + 0x10) +#define GPIO_GPFSEL5 (GPIO_BASE + 0x14) +#define GPIO_GPPUD (GPIO_BASE + 0x94) +#define GPIO_GPPUDCLK1 (GPIO_BASE + 0x9c) + +// sdhost +#define SDHOST_BASE (MMIO_BASE + 0x202000) +#define SDHOST_CMD (SDHOST_BASE + 0) +#define SDHOST_READ 0x40 +#define SDHOST_WRITE 0x80 +#define SDHOST_LONG_RESPONSE 0x200 +#define SDHOST_NO_REPONSE 0x400 +#define SDHOST_BUSY 0x800 +#define SDHOST_NEW_CMD 0x8000 +#define SDHOST_ARG (SDHOST_BASE + 0x4) +#define SDHOST_TOUT (SDHOST_BASE + 0x8) +#define SDHOST_TOUT_DEFAULT 0xf00000 +#define SDHOST_CDIV (SDHOST_BASE + 0xc) +#define SDHOST_CDIV_MAXDIV 0x7ff +#define SDHOST_CDIV_DEFAULT 0x148 +#define SDHOST_RESP0 (SDHOST_BASE + 0x10) +#define SDHOST_RESP1 (SDHOST_BASE + 0x14) +#define SDHOST_RESP2 (SDHOST_BASE + 0x18) +#define SDHOST_RESP3 (SDHOST_BASE + 0x1c) +#define SDHOST_HSTS (SDHOST_BASE + 0x20) +#define SDHOST_HSTS_MASK (0x7f8) +#define SDHOST_HSTS_ERR_MASK (0xf8) +#define SDHOST_HSTS_DATA (1 << 0) +#define SDHOST_PWR (SDHOST_BASE + 0x30) +#define SDHOST_DBG (SDHOST_BASE + 0x34) +#define SDHOST_DBG_FSM_DATA 1 +#define SDHOST_DBG_FSM_MASK 0xf +#define SDHOST_DBG_MASK (0x1f << 14 | 0x1f << 9) +#define SDHOST_DBG_FIFO (0x4 << 14 | 0x4 << 9) +#define SDHOST_CFG (SDHOST_BASE + 0x38) +#define SDHOST_CFG_DATA_EN (1 << 4) +#define SDHOST_CFG_SLOW (1 << 3) +#define SDHOST_CFG_INTBUS (1 << 1) +#define SDHOST_SIZE (SDHOST_BASE + 0x3c) +#define SDHOST_DATA (SDHOST_BASE + 0x40) +#define SDHOST_CNT (SDHOST_BASE + 0x50) + +// helper +#define set(io_addr, val) \ + asm volatile("str %w1, [%0]" ::"r"(io_addr), "r"(val) : "memory"); + +#define get(io_addr, val) \ + asm volatile("ldr %w0, [%1]" : "=r"(val) : "r"(io_addr) : "memory"); + +static inline void delay(unsigned long tick) { + while (tick--) { + asm volatile("nop"); + } +} + +static int is_hcs; // high capcacity(SDHC) + +static void pin_setup() { + set(GPIO_GPFSEL4, 0x24000000); + set(GPIO_GPFSEL5, 0x924); + set(GPIO_GPPUD, 0); + delay(15000); + + set(GPIO_GPPUDCLK1, 0xffffffff); + delay(15000); + set(GPIO_GPPUDCLK1, 0); +} + +static void sdhost_setup() { + unsigned int tmp; + set(SDHOST_PWR, 0); + set(SDHOST_CMD, 0); + set(SDHOST_ARG, 0); + set(SDHOST_TOUT, SDHOST_TOUT_DEFAULT); + set(SDHOST_CDIV, 0); + set(SDHOST_HSTS, SDHOST_HSTS_MASK); + set(SDHOST_CFG, 0); + set(SDHOST_CNT, 0); + set(SDHOST_SIZE, 0); + get(SDHOST_DBG, tmp); + tmp &= ~SDHOST_DBG_MASK; + tmp |= SDHOST_DBG_FIFO; + set(SDHOST_DBG, tmp); + delay(250000); + set(SDHOST_PWR, 1); + delay(250000); + set(SDHOST_CFG, SDHOST_CFG_SLOW | SDHOST_CFG_INTBUS | SDHOST_CFG_DATA_EN); + set(SDHOST_CDIV, SDHOST_CDIV_DEFAULT); +} + +static int wait_sd() { + int cnt = 1000000; + unsigned int cmd; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_CMD, cmd); + --cnt; + } while (cmd & SDHOST_NEW_CMD); + return 0; +} + +static int sd_cmd(unsigned cmd, unsigned int arg) { + set(SDHOST_ARG, arg); + set(SDHOST_CMD, cmd | SDHOST_NEW_CMD); + return wait_sd(); +} + +static int sdcard_setup() { + unsigned int tmp; + sd_cmd(GO_IDLE_STATE | SDHOST_NO_REPONSE, 0); + sd_cmd(SEND_IF_COND, VOLTAGE_CHECK_PATTERN); + get(SDHOST_RESP0, tmp); + if (tmp != VOLTAGE_CHECK_PATTERN) { + return -1; + } + while (1) { + if (sd_cmd(APP_CMD, 0) == -1) { + // MMC card or invalid card status + // currently not support + continue; + } + sd_cmd(SD_APP_OP_COND, SDCARD_3_3V | SDCARD_ISHCS); + get(SDHOST_RESP0, tmp); + if (tmp & SDCARD_READY) { + break; + } + delay(1000000); + } + + is_hcs = tmp & SDCARD_ISHCS; + sd_cmd(ALL_SEND_CID | SDHOST_LONG_RESPONSE, 0); + sd_cmd(SEND_RELATIVE_ADDR, 0); + get(SDHOST_RESP0, tmp); + sd_cmd(SELECT_CARD, tmp); + sd_cmd(SET_BLOCKLEN, 512); + return 0; +} + +static int wait_fifo() { + int cnt = 1000000; + unsigned int hsts; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_HSTS, hsts); + --cnt; + } while ((hsts & SDHOST_HSTS_DATA) == 0); + return 0; +} + +static void set_block(int size, int cnt) { + set(SDHOST_SIZE, size); + set(SDHOST_CNT, cnt); +} + +static void wait_finish() { + unsigned int dbg; + do { + get(SDHOST_DBG, dbg); + } while ((dbg & SDHOST_DBG_FSM_MASK) != SDHOST_HSTS_DATA); +} + +// reads/writes 512 bytes from/to the SD card to/from buf[512]. +void readblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do { + set_block(512, 1); + sd_cmd(READ_SINGLE_BLOCK | SDHOST_READ, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + get(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while (!succ); + wait_finish(); +} + +// reads/writes 512 bytes from/to the SD card to/from buf[512]. +void writeblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do { + set_block(512, 1); + sd_cmd(WRITE_SINGLE_BLOCK | SDHOST_WRITE, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + set(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while (!succ); + wait_finish(); +} + +// set up GPIO, SD host, and initialize the SD card +void sd_init() { + pin_setup(); + sdhost_setup(); + sdcard_setup(); +} \ No newline at end of file diff --git a/lab7/kernel/sdhost.h b/lab7/kernel/sdhost.h new file mode 100644 index 000000000..6cbd70564 --- /dev/null +++ b/lab7/kernel/sdhost.h @@ -0,0 +1,8 @@ +#ifndef SDHOST_H +#define SDHOST_H + +void readblock(int block_idx, void* buf); +void writeblock(int block_idx, void* buf); +void sd_init(); + +#endif diff --git a/lab7/kernel/startup.s b/lab7/kernel/startup.s new file mode 100644 index 000000000..5a721892d --- /dev/null +++ b/lab7/kernel/startup.s @@ -0,0 +1,82 @@ +.section ".text.boot" + +.global _start + + +_start: + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, init + b busy_loop + +busy_loop: + wfe + b busy_loop + +init: + + bl from_el2_to_el1 /* required 1-1 跳至 from_el2_to_el1,將excpetion level設為1 */ + bl set_exception_vector_table /* required 1-3 跳至 set_exception_vector_table,設定exception發生時,查找的vector_table */ + + ldr x1, =__bss_start + ldr x2, =__bss_size + +loop_clear_bss: + cbz x2, entry_point + str xzr, [x1], #8 + sub x2, x2, #1 + cbnz x2, loop_clear_bss + +entry_point: + ldr x1, =_start + mov sp, x1 + bl main + b busy_loop + + +/* + + exception level 由 EL2 變 EL1 + + 默認情況下,Rpi3的CPU在默認啟動後便在EL2中運行,但是我們希望內核在EL1中運行。 + 因此,您的內核需要在開始時切換到EL1 + + 以下是助教提供的code + + 1. 設置hcr_el2暫存器(Hypervisor Configuration Register) + 這個暫存器大部分bit在reset狀態是0,只有bit 31是implementation defined + 因此設定bit 31 為1,確保在EL1 也是Aarch64的 + + 2. 設置spsr_el2暫存器(Saved Program Status Register) + 用於exception發生時儲存當時CPU狀態,這邊是做初始化 + 設定在AArch64下EL1h 和 D,A,I,F interrupt disabled + + 參照 https://kaiiiz.github.io/notes/nctu/osdi/lab3/exception-level-switch/ + http://www.lujun.org.cn/?p=1676 + 可以知道是 D,A,I,F 在9,8,7,6位,EL1h是101,所以是1111000101 = 0x3c5 + + 3. 設置elr_el2暫存器(Exception Link Register) + 用於exception發生時儲存處理後要返回的地址 + 這邊是做初始化,設為lr + + 4. 最後用eret,跳至EL1,之後就在EL1運行了 + +*/ + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 + mov x0, 0x3c5 // EL1h (SPSel = 1) with interrupt disabled + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 + +/* + set vbar_el1 register to exception_vector_table address. + 1. exception_vector_table address to x0 register + 2. x0 register value to vbar_el1 status register +*/ +set_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + ret \ No newline at end of file diff --git a/lab7/kernel/thread.c b/lab7/kernel/thread.c new file mode 100644 index 000000000..91fa8a458 --- /dev/null +++ b/lab7/kernel/thread.c @@ -0,0 +1,766 @@ +#include "uart.h" +#include "util.h" +#include "buddy.h" +#include "allocator.h" +#include "cpio.h" +#include "thread.h" + +struct thread *head, *tail; +int thread_count = 0; +struct dynamic_allocator *dyalloc = 0; + +/* +* init_thread +* +* initial thread parameter and memory parameter +*/ +void init_thread() +{ + head = tail = 0; + thread_count = 0; + + buddy_init(); + dyalloc = dynamic_allocator_init(); +} + +/* +* thread_test (requirment1 Test method) +* +* this code form TA, create 3 thread excute foo +* 1. create first thread and set its current thread +* 2. create N thread excute foo, N should > 2 +* 3. idle +*/ +void thread_test() +{ + // create first thread for context_switch + struct thread *first_thread = thread_create(0); + set_current(first_thread); + + for(int i = 0; i < 3; ++i) + { + thread_create(foo); + } + + idle(); +} + +/* +* foo (requirment1 Test method) +* +* this code form TA, create a thread excute foo +* 1. for loop 0-9 +* 2. print message and call schedule to next thread +* 3. delay 1000000 +* 4. call schedule switch to next running thread +* 5. when finish, call exit to end of a thread +*/ +void foo() +{ + char buf[16] = {0}; + + for(int i = 0; i < 10; ++i) + { + // print message, ex. Thread id: 1, index:3 + uart_putstr("Thread id: "); + unsignedlonglongToStr(current_thread()->tid, buf); + uart_putstr(buf); + uart_putstr(", index: "); + unsignedlonglongToStr(i, buf); + uart_putstr(buf); + uart_putstr("\n"); + + delay(1000000); + schedule(); + } + + // when finish, call exit to end of a thread + exit(); +} + +/* +* thread_create (requirment Creating a Thread) +* +* 1. create new thread +* - allocate thread momoery +* - fp = Frame pointer = start address + THREAD_SIZE +* - lr = link register for function calls. +* - sp = stack pointer = start address + THREAD_SIZE +* - other parameter +* +* 2. add new thread to run queue +* 3. return new_thread for fist initinal +*/ +struct thread* thread_create(void* func) +{ + // create new thread + struct thread* new_thread = (struct thread*)dynamic_alloc(dyalloc, THREAD_SIZE); + new_thread->context.fp = (unsigned long)new_thread + THREAD_SIZE; + new_thread->context.lr = (unsigned long)func; + new_thread->context.sp = (unsigned long)new_thread + THREAD_SIZE; + new_thread->tid = thread_count++; + new_thread->status = TASK_RUNNING; + new_thread->next = 0; + + // for requirment 2 + new_thread->program_addr = 0; + new_thread->program_size = 0; + new_thread->childID = 0; + + // for lab6 requirment2 + for (int i = 0; i < FD_MAX; ++i) + new_thread->fd_table.files[i] = 0; + + // add new thread to run queue + add_to_run_queue(new_thread); + + return new_thread; +} + +/* +* add_to_run_queue (requirment Creating a Thread) +* +* head : pointer to run queue start +* tail : pointer to run queue end +* +* add thread to tail->next +*/ +void add_to_run_queue(struct thread* new_thread) +{ + if(head == 0) + head = tail = new_thread; + else + { + tail->next = new_thread; + tail = new_thread; + } +} + +/* +* current_thread +* +* call get_current method get cureent thread (context_switch.s) +*/ +struct thread* current_thread() +{ + return get_current(); +} + +/* +* schedule (requirment Scheduler and Context Switch) +* +* pointer to next thread and do context_switch +* 1. if head = 0 represent run queue is empty, return +* 2. if head = tail represent run queue only first thread, free first thread and init +* 3. if run queue thread count > 1, pointer to next thread and do context switch +* +*/ +void schedule() +{ + if(head == 0) // run queue is empty + return; + + if(head == tail && thread_count > 1) // run queue only first thread + { + dynamic_free(dyalloc, (unsigned long)head); + head = tail = 0; + thread_count = 0; + return; + } + + // pointer to next thread and do context switch + // + // head _________ + // | | + // head tail | ▼ + // _____ _____ _____ _____ _____ _____ + // | | | | | | | | | + // | | | | => | | x | | | + // ------------------- ------- ------------- + // ▲ | + // |________________| tail + do + { + tail->next = head; + tail = head; + head = head->next; + tail->next = 0; + } while(head->status != TASK_RUNNING); + + // put [current_thread] and [next_thread] do thread context switch + switch_to(current_thread(), head); +} + +/* +* kill_zombies (requirment The Idle Thread) +* +* scan run queue, from head to tail. recycle not use(dead) thread. +* 1. ptr = current thread pointer +* 2. if ptr = 0 represent run queue is empty, return +* 3. if next thread status is dead, free next thread, +* current thread's next pointer to ptr->next->next +* 4. if next thread status isn't dead, ptr = ptr->next +*/ +void kill_zombies() +{ + struct thread* ptr = head; + struct thread* tmp = 0; + + if(ptr == 0) // empty + return; + + while(1) + { + // ptr ________________ + // | | + // ptr | ▼ + // _____ _____ _____ _____ _____ _____ + // | | | | | | | | | | + // | |dead | | => | | |dead | | | + // ------------------- ------- ----- ----- + // free + // + while(ptr->next != 0 && ptr->next->status == TASK_DEAD) + { + tmp = ptr->next->next; + dynamic_free(dyalloc, (unsigned long)ptr->next); + ptr->next = tmp; + } + + // if next thread status isn't dead, ptr = ptr->next + if(ptr->next != 0) + { + ptr = ptr->next; + } + else + { + tail = ptr; + break; + } + } +} + +/* +* idle (requirment The Idle Thread) +* +* this code form TA, always run kill_zombies and schedule +*/ +void idle() +{ + while(1) + { + kill_zombies(); // reclaim threads marked as DEAD + do_fork(); // for requirment 2 + schedule(); // switch to any other runnable thread + + if (head == 0 && tail == 0) + return; + } +} + +void delay(int count) +{ + for(int i = 0; i < count; i++) + asm volatile("nop"); +} + +/* +* exit (requirment End of a Thread) +* +* When a thread finishes its jobs, set status to TASK_DEAD +* and call schedule to next running thread +*/ +void exit() +{ + struct thread *cur = current_thread(); + cur->status = TASK_DEAD; + schedule(); +} + +//======================================================================= + +#define USER_PROGRAM_BASE 0x5000000 +#define USER_PROGRAM_ADDR 0x60000 +int user_program_count = 0; + +void init_thread2() +{ + user_program_count = 0; +} + +/* +* thread_test2 (requirment2 Test method) +* +* this code form TA, create thread excute user_test +* 1. create first thread and set its current thread +* 2. create thread excute user_test +* 3. idle +*/ +void thread_test2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + + thread_create(user_test); + idle(); +} + +/* +* user_test (requirment2 Test method) +* +* this code form TA, Execute user program with arguments +* 1. Declare arguments +* 2. call exec function create thread and excute user program +*/ +void user_test() +{ + char argv[4][10] = { "argv_test", "-o", "arg2", ""}; + exec("argv_test.img", argv); +} + +/* +* exec (requirment2 Arguments Passing) +* +* Execute user program with arguments +* 1. Declare user program excute address, + Because we not MMU, so differnet user program should use different address +* 2. load_program_with_args +* 3. when finish, call exit to end of a thread +*/ +void exec(const char* name, char(*argv)[10]) +{ + unsigned long addr = generate_user_addr(); + load_program_with_args(name, argv, addr, head); + + // when finish, call exit to end of a thread + exit(); +} + +/* +* generate_user_addr (requirment2 Arguments Passing) +* +* calculate user program loading address (different program run different address) +* 1. formula = USER_PROGRAM_BASE + user_program_count * USER_PROGRAM_ADDR +* 2. user_program_count + 1 +* 3. return user program loading address +*/ +unsigned long generate_user_addr() +{ + unsigned long addr = USER_PROGRAM_BASE + user_program_count * USER_PROGRAM_ADDR; + user_program_count++; + return addr; +} + +/* +* load_program_with_args (requirment2 Arguments Passing) +* +* call cpio load user program, and pass argument to stack, finally run user program on EL0 +* 1. call cpio_load_user_program_and_get_size, load user program and return program size +* 2. call pass_argument , pass argument to stack +* 3. set program address, program size +* 4. run user program on EL0 (EL1 -> EL0) +*/ +void load_program_with_args(const char* name, char(*argv)[10], unsigned long addr, struct thread* current) +{ + int program_size = cpio_load_user_program_and_get_size((char*)name, addr); + + unsigned long sp_addr = pass_argument(argv, addr); + current->program_addr = addr; + current->program_size = program_size; + + // run user program on EL0 (EL1 -> EL0) + asm volatile("mov x0, 0x340 \n\t"); // 340 = 1101000000, irq enable + asm volatile("msr spsr_el1, x0 \n\t"); // spsr_el1 = 0x340 + asm volatile("msr elr_el1, %0 \n\t"::"r"(addr)); // elr_el1 = user program load address + asm volatile("msr sp_el0, %0 \n\t"::"r"(sp_addr)); // sp_el0 = stack address + asm volatile("mrs x3, sp_el0 \n\t"); // 將sp_el0給x3 + asm volatile("ldr x0, [x3, 0] \n\t"); // x0存放(x3+0),參考下圖,也就是argc的位置 + asm volatile("ldr x1, [x3, 8] \n\t"); // x1存放(x3+8),參考下圖,也就是argv的位置 + asm volatile("eret \n"); +} + +/* +* pass_argument (requirment2 Arguments Passing) +* +* pass argument to user stack, and return user stack address (aligment 16) +* 1. calc argv(argc), and argv byte count +* 2. calc offset, notice sp should aligment 16 +* 3. pass argument to user stack +* 4. return stack address +*/ +unsigned long pass_argument(char(*argv)[10], unsigned long addr) +{ + /* user stack + * + * Low ----------------------------------------------------------------------> High + * ______________ + * _________________|_____________▼______________________________________________ + * | | | | | | | | + * | | | | | | | | + * | 2(argc) | char** argv | char* argv[0] | char* argv[1] | NULL | a.out | arg1 | + * | | | | | | | | + * |_________|_____________|_______________|_______________|______|_______|______| + * | | ▲ ▲ + * ------------------------------------- | + * |__________________________| + */ + + // // 1.計算參數數量(argc), 以及參數各有幾個byte(argv byte count) + // 例如{"argv_test", "-o", "arg2", ""},共有4個參數,18個byte(含結束符號\0) + int argc = 0, argv_count = 0; + for(int i = 0;;++i) + { + ++argc; + if(strlen(argv[i]) == 0) + break; + + for(int j = 0;; ++j) + { + ++argv_count; + if(argv[i][j] == 0) + break; + } + } + + // 2. calc offset, notice sp should aligment 16 + // 參考上圖,第一個放argc,然後放argv的地址,接著放參數位址,然後放每一個參數的內容 + // 因此,會先計算參數所佔的大小是多少,然後載入位(addr) – 參數所佔大小(offset) + // 就是我們的user stack的位址,記得要對齊16 + int offset = (1 + 1 + argc) * 8 + argv_count; + addr = addr - offset; + addr = addr - (addr & 15); // aligment 16 + + // 3. 將參數塞入user stack + // 3.1 起始位置 = addr + char* data = (char*)addr; + // 3.2 塞入argc的數量,然後位址+8 + *(unsigned long*)data = argc - 1; // 不包含最後一個NULL + data += 8; + // 3.3 塞入argv的地址後,位址+8 + *(unsigned long*)data = (unsigned long)(data + 8); //argv addr + data += 8; + // 3.4 位址 += 參數數量*8 + char* argv_buf = data + 8 * argc; + // 3.5 塞入參數的內容 + for(int i = 0; i < argc - 1; ++i) + { + *(unsigned long*)data = (unsigned long)argv_buf; + for(int j = 0;;++j) + { + *argv_buf = argv[i][j]; + ++argv_buf; + if(argv[i][j] == 0) + break; + } + // 塞完後位址+8 + data += 8; + } + // 結束放0 + *(unsigned long*)data = 0; + + return addr; +} + +/* +* get_pid (requirment2) +* +* get current thread id +*/ +unsigned long get_pid() +{ + return current_thread()->tid; +} + +/* +* fork (requirment2) +* +* 1. set current thread status = fork +* 2. call schedule +* 3. return child id +*/ +int fork() +{ + head->status |= TASK_FORK; + schedule(); + return head->childID; +} + +/* +* do_fork (requirment2) +* +* 1. from head to tail,search status = fork thread +* 2. create new thread +* 3. copy current thread to new thread +*/ +void do_fork() +{ + for(struct thread* ptr = head->next; ptr != 0; ptr = ptr->next) + { + if((ptr->status) & TASK_FORK) + { + struct thread* child = thread_create(0); + copy_program(ptr, child); + } + } +} + +/* +* copy_program (requirment2) +* +* copy parent thread to new thread +* 1. set parent status and child id +* 2. copy parent thread to child thread +* 3. set +* 4. copy parent statck to child stack +* +*/ +void copy_program(struct thread* parent, struct thread* child) +{ + // 1. 設定parent狀態設為非fork,並將child id給parent的childID + parent->status ^= TASK_FORK; + parent->childID = child->tid; + + // 2. 複製parent thread內容給child + struct thread* child_next = child->next; //先將child next及id取出,以免被覆蓋 + unsigned long org_tid = child->tid; + + // 將parent thread內容複製給child + char* src = (char*) parent; + char* dst = (char*) child; + for(int i = 0; i < THREAD_SIZE; ++i) + { + *dst = *src; + src++; + dst++; + } + + // 設定child相關參數(並把剛剛的child next及id回填) + child->next = child_next; + child->childID = 0; + child->tid = org_tid; + child->program_addr = generate_user_addr(); + child->program_size = parent->program_size; + + // 3. 設定 fp, sp, elr_el1, sp_el0, user_fp, user_lr + // 3.1 計算暫存器偏移 + // 3.2 計算program偏移 + // 3.3 原本是parent的位置,應該加上偏移才是正確的值 + // 其中,fp sp是加上暫存器偏移 + // elr_el1, sp_el0, user_fp, user_lr是加上program偏移 + unsigned long reg_addr_diff = (unsigned long)child - (unsigned long)parent; + unsigned long p_diff = (unsigned long)child->program_addr - (unsigned long)parent->program_addr; + child->context.fp += reg_addr_diff; // fp + child->context.sp += reg_addr_diff; // sp + child->context.elr_el1 += p_diff; //elr_el1 + child->context.sp_el0 += p_diff; //sp_el0 + child->context.reg[29] += p_diff; //user_fp + child->context.reg[30] += p_diff; //user_lr + + // 4. 計算好正確位置後,將parent stack內容複製給child stack + src = (char*) (parent->context.sp_el0); + dst = (char*) (child->context.sp_el0); + unsigned long st_size = (parent->program_addr) + parent->program_size - parent->context.sp_el0; + for(int i = 0; i < st_size; ++i) + { + *dst = *src; + src++; + dst++; + } +} + +//======================================================================= + +/* +* thread_vfs_req2 (lab6 requirment2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute vfs_req2 +* 3. idle +*/ +void thread_vfs_req2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(vfs_req2); + idle(); +} + +/* +* vfs_req2 (requirment2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void vfs_req2() +{ + char argv[2][10] = { "vfs_test", ""}; + exec("vfs_test.img", argv); +} + +/* +* thread_get_file (requirment2 Test method) +* +* 輸入index,回傳目前thread的 file descriptor table中指定index的file +*/ +struct file *thread_get_file(int fd) +{ + struct thread *cur = current_thread(); + return cur->fd_table.files[fd]; +} + +/* +* thread_register_fd (requirment2 Test method) +* +* 傳入file,找目前thread file descriptor table中未使用的index加入,並回傳index +*/ +int thread_register_fd(struct file *file) +{ + if (file == 0) + return -1; + + struct thread *cur = current_thread(); + // find next available fd + for (int fd = 0; fd < FD_MAX; ++fd) + { + if (cur->fd_table.files[fd] == 0) + { + cur->fd_table.files[fd] = file; + return fd; + } + } + + return -1; +} + +/* +* thread_clear_fd (requirment2 Test method) +* +* 輸入index,清空目前thread的 file descriptor table中指定index的內容 +* 並回傳成功或失敗 +*/ +int thread_clear_fd(int fd) +{ + if (fd < 0 || fd >= FD_MAX) + return -1; + + struct thread *cur = current_thread(); + cur->fd_table.files[fd] = 0; + + return 1; +} + +//======================================================================= + +/* +* thread_vfs_ele1 (lab6 elective1 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute vfs_ele1 +* 3. idle +*/ +void thread_vfs_ele1() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(vfs_ele1); + idle(); +} + +/* +* vfs_ele1 (elective1 Test method) +* +* 1. Declare arguments, list directory set current directory "." +* 2. call exec function excute user program +*/ +void vfs_ele1() +{ + char argv[4][10] = { "ls_test", ".", "arg1", ""}; + exec("ls_test.img", argv); +} + +//======================================================================= + +/* +* thread_vfs_ele2 (lab6 elective2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute vfs_ele2 +* 3. idle +*/ +void thread_vfs_ele2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(vfs_ele2); + idle(); +} + +/* +* vfs_ele2 (elective2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void vfs_ele2() +{ + char argv[2][10] = { "multilvl", ""}; + exec("multilvl_test.img", argv); +} + +//======================================================================= + +/* +* thread_fat32_req1 (lab7 requirment1 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute fat32_req1 +* 3. idle +*/ +void thread_fat32_req1() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(fat32_req1); + idle(); +} + +/* +* fat32_req1 (requirment1 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void fat32_req1() +{ + char argv[2][10] = { "fat32", ""}; + exec("fat32_test.img", argv); +} + +//======================================================================= + +/* +* thread_fat32_req2 (lab7 requirment2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute fat32_req2 +* 3. idle +*/ +void thread_fat32_req2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(fat32_req2); + idle(); +} + +/* +* fat32_req2 (requirment2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void fat32_req2() +{ + char argv[2][10] = { "fat32", ""}; + exec("fat32_test2.img", argv); +} diff --git a/lab7/kernel/thread.h b/lab7/kernel/thread.h new file mode 100644 index 000000000..5f13554e7 --- /dev/null +++ b/lab7/kernel/thread.h @@ -0,0 +1,107 @@ +#ifndef THREAD_H +#define THREAD_H + +#include "vfs.h" + +#define THREAD_SIZE 0x1000 +#define TASK_RUNNING 0 +#define TASK_DEAD 1 +#define TASK_FORK 2 + +// for lab6 req2 +#define FD_MAX 256 + +struct fd_table_t +{ + struct file *files[FD_MAX]; +}; + +// kerenl stack context (use for switch_to function in context_swtich.s) +// https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard +struct cpu_context +{ + // context to kernel-stack (x19 to x28, fp(x29), lr(x30), sp) + // only use x19 to x28: Callee-saved register + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; // x29 + unsigned long lr; // x30 + unsigned long sp; // sp contain x0-x18 + + // for user program (spsr_el1, elr_el1, sp_el0)(user register x0-x30) + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; + unsigned long reg[31]; + +}; + +// thread struct +struct thread +{ + struct cpu_context context; + unsigned long tid; // thread id + int status; + struct fd_table_t fd_table; // for lab6 req2 + struct thread* next; + + unsigned long program_addr; + unsigned long program_size; + unsigned long childID; +}; + +// outer function in context_swtich.s +extern void switch_to(struct thread* prev, struct thread* next); +extern struct thread* get_current(); +extern void set_current(struct thread* current); + +void init_thread(); +void thread_test(); +void foo(); +struct thread* thread_create(void* func); +void add_to_run_queue(struct thread* new_thread); +struct thread* current_thread(); +void schedule(); +void kill_zombies(); +void idle(); +void delay(int count); +void exit(); +// +void init_thread2(); +void thread_test2(); +void user_test(); +void exec(const char* name, char(*argv)[10]); +unsigned long generate_user_addr(); +void load_program_with_args(const char* name, char(*argv)[10], unsigned long addr, struct thread* current); +unsigned long pass_argument(char(*argv)[10], unsigned long addr); +unsigned long get_pid(); +int fork(); +void do_fork(); +void copy_program(struct thread* parent, struct thread* child); +// for lab6 req2 +void thread_vfs_req2(); +void vfs_req2(); +struct file *thread_get_file(int fd); +int thread_register_fd(struct file *file); +int thread_clear_fd(int fd); +// for lab6 ele1 +void thread_vfs_ele1(); +void vfs_ele1(); +// for lab6 ele2 +void thread_vfs_ele2(); +void vfs_ele2(); +// for lab7 req1 +void thread_fat32_req1(); +void fat32_req1(); +// for lab7 req2 +void thread_fat32_req2(); +void fat32_req2(); +#endif \ No newline at end of file diff --git a/lab7/kernel/timer.c b/lab7/kernel/timer.c new file mode 100644 index 000000000..9d22b7b74 --- /dev/null +++ b/lab7/kernel/timer.c @@ -0,0 +1,193 @@ +#include "uart.h" +#include "util.h" +#include "timer.h" + +/* +* get_excute_time +* +* cntpct_el0: The timer’s current count. +* cntfrq_el0: the frequency of the timer +* +* excute time = cntpct_el0 / cntfrq_el0 +*/ +unsigned long long get_excute_time() +{ + unsigned long long cntpct_el0, cntfrq_el0; + asm volatile("mrs %0, cntpct_el0" : "=r"(cntpct_el0)); + asm volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq_el0)); + + return cntpct_el0 / cntfrq_el0; +} + +/* +* set_next_timeout +* +* cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +* cntfrq_el0: the frequency of the timer +* +* we set cntp_cval_el0 = timer frequency * second = next interrupt timeout +*/ +void set_next_timeout(unsigned int second) +{ + unsigned long cntfrq_el0; + asm volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq_el0)); + asm volatile("msr cntp_tval_el0, %0" : : "r"(cntfrq_el0 * second)); +} + +/* +* core time interrupt handle +* +* 1. set next timeout = 2s later +* 2. print excute time +*/ +void core_timer_handle() +{ + set_next_timeout(2); + + uart_putstr("seconds after booting: "); + + char buf[16] = {0}; + unsignedlonglongToStr(get_excute_time(), buf); + uart_putstr(buf); + uart_putstr("seconds\n"); +} + +////////////////////////////// timeout /////////////////////////////// + +timeout timeout_buffer[max_queue_size]; +timeout *timeout_queue; +int buffer_index; + +void init_timeout() +{ + for(int i = 0; i < max_queue_size; i++) + { + timeout_buffer[i].startTime = 0; + timeout_buffer[i].duration = 0; + timeout_buffer[i].next = 0; + } + + timeout_queue = 0; + buffer_index = 0; +} +void add_timer(void (*callback)(char *), char* msg, int duration) +{ + // + // set new timeout (in last) + // + + unsigned long long currentTime = get_excute_time(); + + timeout_buffer[buffer_index].startTime = currentTime; + timeout_buffer[buffer_index].duration = duration; + timeout_buffer[buffer_index].callback = callback; + for(int i = 0; i < msg_size; i++) + { + timeout_buffer[buffer_index].msg[i] = msg[i]; + if(msg[i] == '\0') + break; + } + + // + // find proper location insert (by end time) + // + + // 1. find position + timeout *prev = 0, *cur = 0; + for(cur = timeout_queue; cur != 0; prev = cur, cur = cur->next) + { + if(cur->startTime + cur->duration > currentTime + duration) + break; + } + + // empty + if(prev == 0 && cur == 0) + { + core_timer_enable(); + timeout_queue = &timeout_buffer[buffer_index]; + set_next_timeout(timeout_queue->duration); + } + // new timeout end time > timeout_queue end time + else if(prev != 0 && cur == 0) + { + prev->next = &timeout_buffer[buffer_index]; + } + // new timeout end time < timeout_queue end time + else if (prev == 0 && cur != 0) + { + timeout_buffer[buffer_index].next = cur; + timeout_queue = &timeout_buffer[buffer_index]; + set_next_timeout(timeout_queue->duration); + } + else + { + timeout_buffer[buffer_index].next = cur; + prev->next = &timeout_buffer[buffer_index]; + } + + // if large max size, index = 0 + for(int i = 0; i < max_queue_size && timeout_buffer[buffer_index].startTime > 0; i++) + { + if(++buffer_index == max_queue_size) + buffer_index = 0; + } + +} + +/* +* print_timer_msg +* +* start excute time: 5s, end excute time: 7s, duration time: 2s, message: text +* +*/ +void print_timer_msg(char* msg) +{ + char buf[16] = {0}; + + uart_putstr("start excute time: "); + unsignedlonglongToStr(timeout_queue->startTime, buf); + uart_putstr(buf); + uart_putstr("s, end excute time: "); + unsignedlonglongToStr(get_excute_time(), buf); + uart_putstr(buf); + uart_putstr("s, duration time: "); + unsignedlonglongToStr(timeout_queue->duration, buf); + uart_putstr(buf); + uart_putstr("s, message: "); + uart_putstr(msg); + uart_putstr("\n"); +} +/* +* timout_handle +* +* 1. if queue is empty, disable core timer and return +* 2. if queue not empty +* 2.1 excute call back +* 2.2 point to next +* 3. if next not empty +* next timeout = remainder time = alltime - currentTime +* 4. if next is empty, disable core timer +* +*/ +void timout_handle() +{ + if(timeout_queue == 0) + { + core_timer_disable(); + return; + } + + // excute callback print messgae + timeout_queue->callback(timeout_queue->msg); + timeout_queue->startTime = -1; + timeout_queue = timeout_queue->next; + + if(timeout_queue != 0) + { + unsigned long long expire_time = timeout_queue->startTime + timeout_queue->duration - get_excute_time(); + set_next_timeout(expire_time); + } + else + core_timer_disable(); + return; +} diff --git a/lab7/kernel/timer.h b/lab7/kernel/timer.h new file mode 100644 index 000000000..6308bc69d --- /dev/null +++ b/lab7/kernel/timer.h @@ -0,0 +1,29 @@ +#ifndef TIMER_H +#define TIMER_H + +extern void core_timer_enable(); +extern void core_timer_disable(); + +unsigned long long get_excute_time(); +void set_next_timeout(unsigned int second); +void core_timer_handle(); + + +#define max_queue_size 30 +#define msg_size 20 + +typedef struct timeout +{ + unsigned long long startTime; + unsigned long long duration; + char msg[msg_size]; + void (*callback)(char *); + struct timeout *next; +} timeout; + +void init_timeout(); +void add_timer(void (*callback)(char *), char *msg, int duration); +void print_timer_msg(char *msg); +void timout_handle(); + +#endif \ No newline at end of file diff --git a/lab7/kernel/tmpfs.c b/lab7/kernel/tmpfs.c new file mode 100644 index 000000000..916731e97 --- /dev/null +++ b/lab7/kernel/tmpfs.c @@ -0,0 +1,279 @@ +#include "tmpfs.h" +#include "util.h" +#include "allocator.h" + +struct vnode_operations* tmpfs_v_ops; +struct file_operations* tmpfs_f_ops; + +struct dynamic_allocator *dyalloc_fs = 0; + +/* +* tmpfs_init 初始化tmpfs +* +* 1. 初始化v_node操作(分配記憶體,註冊查找、建立的方法) +* 2. 初始化file操作(分配記憶體,註冊寫入、讀取的方法) +*/ +void tmpfs_init() +{ + dyalloc_fs = dynamic_allocator_init(); + + tmpfs_v_ops = (struct vnode_operations*)dynamic_alloc(dyalloc_fs, sizeof(struct vnode_operations)); + tmpfs_v_ops->lookup = tmpfs_lookup; + tmpfs_v_ops->create = tmpfs_create; + tmpfs_v_ops->set_parent = tmpfs_set_parent; + + tmpfs_f_ops = (struct file_operations*)dynamic_alloc(dyalloc_fs, sizeof(struct file_operations)); + tmpfs_f_ops->write = tmpfs_write; + tmpfs_f_ops->read = tmpfs_read; + tmpfs_f_ops->list = tmpfs_list; +} + +/* +* tmpfs_init 實作mount方法 (掛載檔案系統並建立root node) +* +* 傳入要掛載上去的檔案系統,以及mount物件 +* +* 1. 分配root node及root node內容的記憶體 +* 2. 初始化root node +* 3. 初始化node內容 +* 4. 設定mount屬性 +* 5. 回傳成功 +*/ +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount) +{ + /* mount結構包含 fs 跟 root屬性 + * root是一個v_node結構,包含 mount、tmpfs_v_ops、tmpfs_f_ops、internal屬性 + * internal是tmpfs_entry結構,包含name、type、vnode、parent_vnode、child、buf屬性 + */ + + // 1. 分配root node及node內容的記憶體 + struct tmpfs_entry* root_entry = (struct tmpfs_entry*)dynamic_alloc(dyalloc_fs, sizeof(struct tmpfs_entry)); + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fs, sizeof(struct vnode)); + // 2. 初始化root node + vnode->mount = mount; + vnode->v_ops = tmpfs_v_ops; + vnode->f_ops = tmpfs_f_ops; + vnode->internal = (void*)root_entry; + // 3. 初始化node內容(internal) + root_entry->parent_vnode = 0; // 因為掛載系統,所以沒有parent_node,自己就是根目錄 + tmpfs_set_entry(root_entry, "/", FILE_DIRECTORY, vnode); + // 4. 初始化mount + mount->fs = fs; + mount->root = vnode; + // 5. 回傳成功 + return 1; +} + +/* +* tmpfs_set_entry 設定節點內容 +* +* tmpfs_entry結構,包含name、type、vnode、parent_vnode、child、buf屬性 +* 1. 設定name +* 2. 設定type +* 3. 設定vnode +* 4. parent_vnode外面設定 +* 5. 分配buf記憶體,並設定buf內容為空 +* 6. 假如節點為目錄,初始化目錄下子檔案,且初始化buffer目前使用size = TMPFS_BUF_SIZE(表示滿了) +* 7. 假如節點為檔案,buffer目前使用size設為0, +*/ +void tmpfs_set_entry(struct tmpfs_entry* entry, const char* component_name, enum FILE_TYPE type, struct vnode* vnode) +{ + strcpy(entry->name, component_name); + + entry->type = type; + entry->vnode = vnode; + entry->buf = (struct tmpfs_buf*)dynamic_alloc(dyalloc_fs, sizeof(struct tmpfs_buf)); + for (int i = 0; i < TMPFS_BUF_SIZE; i++) + entry->buf->buffer[i] = '\0'; + + if (entry->type == FILE_DIRECTORY) + { + for (int i = 0; i < MAX_FILES_IN_DIR; ++i) + { + entry->child[i] = (struct tmpfs_entry*)dynamic_alloc(dyalloc_fs, sizeof(struct tmpfs_entry)); + entry->child[i]->name[0] = 0; + entry->child[i]->type = FILE_NONE; + entry->child[i]->parent_vnode = vnode; + } + + entry->buf->size = TMPFS_BUF_SIZE; + } + else if (entry->type == FILE_REGULAR) + { + entry->buf->size = 0; + } +} + +/* +* tmpfs_create 實作create方法 +* +* 傳入目前目錄,(目標node,名稱,type),在目錄下建立node +* +* 對於目錄下的每一個子節點,尋找檔案類型為無的子節點建立node +* 1. 配置一個vnode記憶體 +* 2. 設定vnode屬性 +* 3. 呼叫tmpfs_set_entry設定vnode內容(interal) +* 4. 設定target = vnode,回傳1 +* 5. 子節點下都已配置(滿了),回傳-1 +*/ +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name, enum FILE_TYPE type) +{ + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + struct tmpfs_entry* entry =((struct tmpfs_entry*)dir_node->internal)->child[i]; + if (entry->type == FILE_NONE) + { + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fs, sizeof(struct vnode)); + vnode->mount = 0; + vnode->v_ops = dir_node->v_ops; + vnode->f_ops = dir_node->f_ops; + vnode->internal = entry; + tmpfs_set_entry(entry, component_name, type, vnode); + *target = entry->vnode; + return 1; + } + } + + return -1; +} + +/* +* tmpfs_lookup 實作lookup方法 +* +* 傳入目前目錄,(目標node,搜尋名稱),搜尋目錄下指定名稱的子目錄或子檔案 +* +* 1. 判斷傳入的dir_node檔案類型是否為目錄,假如不是目錄回傳失敗 +* 2. 假如搜尋名稱為"."表示為目前目錄,target = 目前節點 +* 3. 假如搜尋名稱為".."表示為上一層目錄 +* - 如果沒有父節點,回傳失敗 +* - 否則target = 父節點 +* 4. 搜尋目前目錄下每一個子檔案 +* - 假如名稱 = 搜尋名稱,target = 子檔案的節點,回傳成功 +* 5. 都不符合就回傳失敗 +*/ +int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)dir_node->internal; + if (entry->type != FILE_DIRECTORY) + return 0; + + if (strcmp((char*)component_name, ".")) + { + *target = entry->vnode; + return 1; + } + if (strcmp((char*)component_name, "..")) + { + if (!entry->parent_vnode) + return 0; + *target = entry->parent_vnode; + return 1; + } + + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + entry = ((struct tmpfs_entry*)dir_node->internal)->child[i]; + if (strcmp(entry->name, (char*)component_name)) + { + *target = entry->vnode; + return 1; + } + } + + return 0; +} + +/* +* tmpfs_write 實作write方法 +* +* 傳入檔案,buffer,長度 +* +* 1. 取出節點內容結構 +* 2. 由buf讀取len個byte,寫入節點的buffer內 +* 3. 節點檔案的buf大小 = file當前位置(file大小) +* 4. 回傳寫入的byte數 +*/ +int tmpfs_write(struct file* file, const void* buf, unsigned int len) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)file->vnode->internal; + for (unsigned int i = 0; i < len; i++) + { + entry->buf->buffer[file->f_pos++] = ((char*)buf)[i]; + entry->buf->size = file->f_pos; + } + + return len; +} + +/* +* tmpfs_read 實作read方法 +* +* 傳入檔案,buffer,長度,由傳入的buf中,寫入len byte到該節點的buf內 +* +* 1. 取出節點內容結構 +* 2. 由節點的buffer內取出len byte內容,傳入buf +* 3. 每讀一byte, read_len讀取長度++ +* 4. 假如已讀完節點buffer的內容(讀到buf size了),跳出 +* 5. 回傳讀了幾個byte +*/ +int tmpfs_read(struct file* file, void* buf, unsigned int len) +{ + unsigned int read_len = 0; + struct tmpfs_entry* entry = (struct tmpfs_entry*)file->vnode->internal; + for (unsigned int i = 0; i < len; i++) + { + ((char*)buf)[i] = entry->buf->buffer[file->f_pos++]; + read_len++; + + if (read_len == entry->buf->size) + break; + } + + return read_len; +} + +//======================================================================= + +/* +* tmpfs_list 實作list方法 +* +* 傳入檔案,buffer,索引,列出資料夾下指定index的檔名,並回傳檔案size +* +* 1. 取出節點內容結構 +* 2. 假如不是目錄,回傳失敗 +* 3. index超過最大子節點數,回傳失敗 +* 4. 資料夾下指定index子節點未分配,回傳失敗 +* 5. 複製資料夾下指定index的檔名到buf +* 6. 回傳檔案size +*/ +int tmpfs_list(struct file* file, void* buf, int index) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)file->vnode->internal; + + if (entry->type != FILE_DIRECTORY) + return -1; + if (index >= MAX_FILES_IN_DIR) + return -1; + if (entry->child[index]->type == FILE_NONE) + return -1; + + strcpy((char*)buf, entry->child[index]->name); + return entry->child[index]->buf->size; +} + +//======================================================================= + +/* +* tmpfs_set_parent 實作set_parent方法 +* +* 傳入子節點,父節點 +* +* 1. 取出子節點內容結構 +* 2. 設定父節點為傳入的父節點 +*/ +int tmpfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)child_node->internal; + entry->parent_vnode = parent_vnode; + return 1; +} \ No newline at end of file diff --git a/lab7/kernel/tmpfs.h b/lab7/kernel/tmpfs.h new file mode 100644 index 000000000..813eda7de --- /dev/null +++ b/lab7/kernel/tmpfs.h @@ -0,0 +1,40 @@ +#ifndef TMPFS_H +#define TMPFS_H + +#include "vfs.h" + +#define MAX_FILES_IN_DIR 16 +#define TMPFS_BUF_SIZE 4096 + +// buffer結構 (用於檔案讀寫使用) +struct tmpfs_buf +{ + int flag; + unsigned int size; // 目前buffer的size + char buffer[TMPFS_BUF_SIZE]; // 最大buffer size +}; + +// 檔案描述(vnode的internal) +struct tmpfs_entry +{ + char name[20]; // 名稱 + enum FILE_TYPE type; // 檔案類型 + struct vnode* vnode; // 節點 + struct vnode* parent_vnode; // 父節點 + struct tmpfs_entry* child[MAX_FILES_IN_DIR]; // 資料夾下的檔案 + struct tmpfs_buf* buf; // buffer (用於檔案讀寫使用) +}; + +void tmpfs_init(); +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount); +int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); +void tmpfs_set_entry(struct tmpfs_entry* entry, const char* component_name, enum FILE_TYPE type, struct vnode* vnode); +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name, enum FILE_TYPE type); +int tmpfs_write(struct file* file, const void* buf, unsigned int len); +int tmpfs_read(struct file* file, void* buf, unsigned int len); +// for lab6 elective1 +int tmpfs_list(struct file* file, void* buf, int index); +// for lab6 elective2 +int tmpfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode); + +#endif \ No newline at end of file diff --git a/lab7/kernel/uart.c b/lab7/kernel/uart.c new file mode 100644 index 000000000..57664a4aa --- /dev/null +++ b/lab7/kernel/uart.c @@ -0,0 +1,271 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} + +//================================================================ + +unsigned long uart_gets(char *buf, int size) +{ + for(int i = 0; i < size; ++i) + { + buf[i] = uart_getchar(); + uart_sendchar(buf[i]); + if(buf[i] == '\n' || buf[i] == '\r') + { + buf[i] = '\0'; + return i; + } + } + return size; +} \ No newline at end of file diff --git a/lab7/kernel/uart.h b/lab7/kernel/uart.h new file mode 100644 index 000000000..fb3150dbe --- /dev/null +++ b/lab7/kernel/uart.h @@ -0,0 +1,36 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); +// +unsigned long uart_gets(char *buf, int size); + +#endif \ No newline at end of file diff --git a/lab7/kernel/util.c b/lab7/kernel/util.c new file mode 100644 index 000000000..0b2031f2f --- /dev/null +++ b/lab7/kernel/util.c @@ -0,0 +1,276 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +char *strcpy(char *dst, const char *src) +{ + // return if no memory is allocated to the destination + if (dst == 0) + return 0; + + char *ptr = dst; + while (*src != '\0') + { + *dst = *src; + dst++; + src++; + } + *dst = '\0'; + return ptr; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} + +char *strtok(char *s, const char delim) +{ + static char *pos; + char *ret; + if (s) pos = s; + + if (*pos == '\0') + return 0; + // skip leading + while (*pos == delim) + { + pos++; + } + + ret = pos; + while (*pos != delim && *pos != '\0') + { + pos++; + } + if (*pos != '\0') + { + *pos = '\0'; + pos++; + } + return ret; +} + +char *split_last(char *str, char delim) +{ + char *mid = 0; + while (*str) + { + if (*str == delim) + { + mid = str; + } + + str++; + + } + + if (mid) + { + *mid = '\0'; + mid++; + } + + return mid; +} + +void strcat(char *to, const char *from) +{ + while (*to) + to++; + + while (*from) + { + *to = *from; + to++; + from++; + } + *to = '\0'; +} + +char *strncpy(char *dst, const char *src, unsigned int len) +{ + // return if no memory is allocated to the destination + if (dst == 0) return 0; + + // take a pointer pointing to the beginning of destination string + char *ptr = dst; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*src != ' ') + { + *dst = *src; + dst++; + src++; + len--; + if (!len) + break; + } + + // include the terminating null character + *dst = '\0'; + + // destination is returned by standard strcpy() + return ptr; +} \ No newline at end of file diff --git a/lab7/kernel/util.h b/lab7/kernel/util.h new file mode 100644 index 000000000..a93718465 --- /dev/null +++ b/lab7/kernel/util.h @@ -0,0 +1,19 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +char *strcpy(char *dst, const char *src); +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); +char *strtok(char *s, const char delim); +char *split_last(char *str, char delim); +void strcat(char *to, const char *from); +char *strncpy(char *dst, const char *src, unsigned int len); + +#endif \ No newline at end of file diff --git a/lab7/kernel/vfs.c b/lab7/kernel/vfs.c new file mode 100644 index 000000000..601b72245 --- /dev/null +++ b/lab7/kernel/vfs.c @@ -0,0 +1,430 @@ +#include "vfs.h" +#include "tmpfs.h" +#include "allocator.h" +#include "cpio.h" +#include "uart.h" +#include "util.h" +#include "fat32.h" + +struct filesystem *fs_head, *fs_tail; +struct mount* rootfs; +struct vnode* current_dir; + +struct dynamic_allocator *dyalloc_vfs = 0; + +/* +* vfs_init() +* +* 1. 初始化參數(動態配置,指向檔案系統頭尾的指標) +* 2. 初始化及註冊tmpfs檔案系統 +* 3. 掛載tmpfs檔案系統到根目錄 +* 4. 將cpio裡面的檔案掛到根目錄上 +*/ +void vfs_init() +{ + // 1.初始化參數(動態配置,指向檔案系統頭尾的指標) + dyalloc_vfs = dynamic_allocator_init(); + + fs_head = 0; + fs_tail = 0; + + // 2.初始化及註冊tmpfs檔案系統 + tmpfs_init(); + struct filesystem* tmpfs = (struct filesystem*)dynamic_alloc(dyalloc_vfs, sizeof(struct filesystem)); + tmpfs->name = "tmpfs"; + tmpfs->setup_mount = tmpfs_setup_mount; + register_filesystem(tmpfs); + + // lab 7, 初始化及註冊fatfs檔案系統 + fatfs_init(); + struct filesystem* fatfs = (struct filesystem*)dynamic_alloc(dyalloc_vfs, sizeof(struct filesystem)); + fatfs->name = "fatfs"; + fatfs->setup_mount = fatfs_setup_mount; + register_filesystem(fatfs); + + // 3.掛載tmpfs檔案系統到根目錄 + rootfs = (struct mount*)dynamic_alloc(dyalloc_vfs, sizeof(struct mount)); + struct filesystem* fs = search_fs("tmpfs"); + if (fs == 0) + return; + + fs->setup_mount(fs, rootfs); + current_dir = rootfs->root; + + // 4.將cpio裡面的檔案掛到根目錄上 + cpio_populate_rootfs(); +} + +/* +* register_filesystem() 註冊至filesystem list +* +* 1. 假如為空,直接放入,頭尾指向新加入fs +* 2. 假如不為空,往後加在尾端的next,尾端指向新加入fs +*/ +void register_filesystem(struct filesystem* fs) +{ + if (fs_head == 0) + { + fs_head = fs; + fs_head->next = 0; + fs_tail = fs_head; + } + else + { + fs_tail->next = fs; + fs_tail = fs_tail->next; + } +} + +/* +* search_fs 在檔案系統List尋找指定檔案系統 +* +* 1. 從頭(head)到尾(tail)搜尋 +* 2. 假如檔案系統名稱 = 輸入名稱,回傳 +* 3. 找不到return 0 +*/ +struct filesystem* search_fs(char* name) +{ + for (struct filesystem* fs = fs_head; fs != 0; fs = fs->next) + { + if (strcmp((char*)fs->name, name)) + return fs; + } + + return 0; +} + +/* +* vfs_find_vnode 遞迴尋找指定路徑是否存在,找到後設定為target並回傳1,否則回傳0 +* +* 1. 搜尋路徑 = "/",設定target為root根目錄,回傳1 +* 2. 設定當前目錄,假如開頭為 / 為絕對路徑,須由根目錄開始查找,否則為相對路徑 +* 3. 取得component name, ex. /mnt => mnt +* 4. 搜尋component name是否存在 +* - 沒有找到回傳失敗 +* - 有找到重新設定dir及target, 並往下取出下一層compnent name繼續判斷 +* +* 舉例,傳入 /mnt/device,先搜尋/下mnt是否存在,再搜尋/mnt下device是否存在 +*/ +int vfs_find_vnode(struct vnode** target, const char* pathname) +{ + // 1. 搜尋名稱 = "/",設定target為root根目錄,回傳1 + if (strcmp((char*)pathname, "/")) + { + *target = rootfs->root; + return 1; + } + + // 2. 設定當前目錄,假如開頭為 / 為絕對路徑,須由根目錄開始查找,否則為相對路徑 + struct vnode* dir = current_dir; + if (pathname[0] == '/') + dir = rootfs->root; + + // 3. 取得component name, ex. /mnt => mnt + char* component_name = strtok((char*)pathname, '/'); + + // 4. 搜尋component name是否存在 + // - 沒有找到回傳失敗 + // - 有找到重新設定dir及target, 並往下取出下一層compnent name繼續判斷 + while (component_name && *component_name != '\0') + { + int found = dir->v_ops->lookup(dir, target, component_name); + + if (!found) + return 0; + + if ((*target)->mount) + *target = (*target)->mount->root; + dir = *target; + + component_name = strtok(0, '/'); + } + + return 1; +} + +/* +* vfs_open 開啟or建立指定路徑檔案並回傳 +* +* 此註釋來自助教,target:目標node,fd: file descriptor +* 1. 由root搜尋指定路徑檔案 +* 2. 假如找到指定路徑檔案,建立file descriptor +* 3. 假如輸入的flage是O_CREAT,則建立新檔案 +* +* dir = 目前資料夾, target = 目標node, fd = file descriptor +* 1. 判斷檔名之前的路徑是否存在(pathname_) +* ex. (1)pathname: /mnt -> pathname_: "\0", filename: mnt +* (2)pathname: /mnt/a.txt -> pathname_: /mnt, filename: a.txt +* (3)pathname: file1 -> pathname_: file1, filename: NULL +* 2. 判斷檔案是否存在 (filename) +* - 輸入的flage是O_CREAT且找不到: 建立新vnode, 及建立file +* - 輸入的flage不是O_CREAT且找到: 建立file +* +*/ +struct file* vfs_open(const char* pathname, int flags) +{ + // 1. Lookup pathname from the root vnode. + // 2. Create a new file descriptor for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags. + + struct vnode* dir = current_dir; + struct vnode* target = 0; + struct file* fd = 0; + + // 1. 判斷檔名之前的路徑是否存在(pathname_) + // (1)pathname: /mnt -> pathname_: "\0", filename: mnt + // (2)pathname: /mnt/a.txt -> pathname_: /mnt, filename: a.txt + // (3)pathname: file1 -> pathname_: file1, filename: NULL + char* pathname_ = (char*)dynamic_alloc(dyalloc_vfs, strlen((char*)pathname) + 1); + strcpy(pathname_, pathname); + + char* filename = split_last(pathname_, '/'); + + if (*pathname_ == '\0' && pathname[0] == '/') + dir = rootfs->root; + + if (filename != 0) + { + int prefix_found = vfs_find_vnode(&dir, pathname_); + // ex. given pathname /abc/zxc/file1, but /abc/zxc not found + if (!prefix_found) + return 0; + } + else + filename = (char*)pathname_; + + // 2. 判斷檔案是否存在 (filename) + // - 輸入的flage是O_CREAT且找不到: 建立新vnode, 及建立file + // - 輸入的flage不是O_CREAT且找到: 建立file + int file_found = dir->v_ops->lookup(dir, &target, filename); + + if (flags == O_CREAT) + { + if (!file_found) + { + dir->v_ops->create(dir, &target, filename, FILE_REGULAR); + fd = (struct file*)dynamic_alloc(dyalloc_vfs, sizeof(struct file)); + fd->vnode = target; + fd->f_ops = target->f_ops; + fd->f_pos = 0; + } + } + else + { + if (file_found) + { + if (target->mount) + target = target->mount->root; + + fd = (struct file*)dynamic_alloc(dyalloc_vfs, sizeof(struct file)); + fd->vnode = target; + fd->f_ops = target->f_ops; + fd->f_pos = 0; + } + } + + return fd; +} + +/* +* vfs_close 關閉(釋放file descriptor) +* +* 釋放輸入的file descriptor +*/ +int vfs_close(struct file* file) +{ + // 1. release the file descriptor + dynamic_free(dyalloc_vfs, (unsigned long)file); + return 1; +} + +/* +* vfs_write 寫入 +* +* 此註釋來自助教 +* 1. 由buf寫入len byte到開啟的檔案 +* 2. 回傳寫入的size,或發生錯誤的錯誤代碼 +*/ +int vfs_write(struct file* file, const void* buf, unsigned int len) +{ + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file, buf, len); +} + +/* +* vfs_read 讀取 +* +* 此註釋來自助教 +* 1. 由開啟的檔案讀取len byte到buf中 +* 2. 回傳讀取的size,或發生錯誤的錯誤代碼 +*/ +int vfs_read(struct file* file, void* buf, unsigned int len) +{ + // 1. read min(len, readable file data size) byte to buf from the opened file. + // 2. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +//======================================================================= + +/* +* vfs_list 列出資料夾下檔案 +* +* 傳入file,buf(檔名),index(第幾個) +* 列出指定資料夾下第幾個檔案,名稱放入buf,回傳檔案size +*/ +int vfs_list(struct file* file, void* buf, int index) +{ + return file->f_ops->list(file, buf, index); +} + +//======================================================================= + +/* +* vfs_mkdir 建立目錄 +* +* 傳入目錄名稱 +* +* 1. 如vfs_open,先判斷指定資料夾之前的路徑是否存在,不存在回傳0 +* 2. 再判斷該資料夾是否存在,假如已存在回傳0 +* 3. 建立一個新目錄,回傳建立結果 +* +* 舉例,傳入/mnt/dir1,先判斷/mnt是否存在,再判斷/mnt下是否有dir1,若無則建立新目錄 +*/ +int vfs_mkdir(const char* pathname) +{ + // 1. 先判斷指定資料夾之前的路徑是否存在,不存在回傳0 + struct vnode* dir = current_dir; + struct vnode* target = 0; + + char* pathname_ = (char*)dynamic_alloc(dyalloc_vfs, strlen((char*)pathname) + 1); + strcpy(pathname_, pathname); + // pathname: /mnt -> pathname_: "\0", dirname: mnt + // pathname: /mnt/dir1 -> pathname_: /mnt, dirname: dir1 + // pathname: dir1 -> pathname_: dir1, dirname: NULL + char* dirname = split_last(pathname_, '/'); + if (*pathname_ == '\0' && pathname[0] == '/') + dir = rootfs->root; + + if (dirname != 0) + { + int prefix_found = vfs_find_vnode(&dir, pathname_); + // e.g., given pathname /abc/zxc/file1, but /abc/zxc not found + if (!prefix_found) + return 0; + } + else + dirname = pathname_; + + // 2. 再判斷該資料夾是否存在,假如已存在回傳0 + int file_found = dir->v_ops->lookup(dir, &target, dirname); + if (file_found) + return 0; + + // 3. 建立一個新目錄,回傳建立結果 + int status = dir->v_ops->create(dir, &target, dirname, FILE_DIRECTORY); + return status; +} + +/* +* vfs_chdir 改變當前目錄 +* +* 傳入新目錄名稱 +* +* 1. 尋找目錄名稱是否存在,不存在回傳0 +* 2. 設置目前目錄 = target +*/ +int vfs_chdir(const char* pathname) +{ + // 1. 尋找目錄名稱是否存在,不存在回傳0 + struct vnode* target = 0; + int dir_found = vfs_find_vnode(&target, pathname); + if (!dir_found) + return 0; + + // 2. 設置目前目錄 = target + current_dir = target; + + return 1; +} + +/* +* vfs_mount 掛載目錄到某個檔案系統上 +* +* 傳入device, 掛載目錄名稱,掛載在甚麼檔案系統上 +* +* 1. 假如未找到掛載目錄,回傳0 或 假如目錄已掛載,回傳0 +* 2. 掛載tmpfs檔案系統到根目錄 +* 3. 設定目錄的mount為當前檔案系統,目錄的parent為目前檔案系統的根目錄 +*/ +int vfs_mount(const char* device, const char* mountpoint, const char* filesystem) +{ + // 1. 假如未找到掛載目錄,回傳0 或 假如目錄已掛載,回傳0 + struct vnode* target = 0; + + int dir_found = vfs_find_vnode(&target, mountpoint); + if (!dir_found) + return 0; + if (target->mount) + return 0; + + // 2. 掛載tmpfs檔案系統到根目錄 + struct mount* mountfs = (struct mount*)dynamic_alloc(dyalloc_vfs, sizeof(struct mount)); + struct filesystem* fs = search_fs((char*)filesystem); + fs->setup_mount(fs, mountfs); + + // 3. 設定目錄的mount為當前檔案系統,目錄的parent為目前檔案系統的根目錄 + target->mount = mountfs; + mountfs->root->v_ops->set_parent(mountfs->root, target); + return 1; +} + +/* +* vfs_umount 卸載目錄 +* +* 傳入掛載目錄名稱 +* +* 1. 如vfs_open,先判斷掛載資料夾之前的路徑是否存在,不存在回傳0 +* 2. 判斷掛載資料夾是否存在,不存在回傳0 +* 3. 假如資料夾沒有掛載在任何系統上,回傳0 +* 4. 設定目錄的mount(=0),回傳1 +*/ +int vfs_umount(const char* mountpoint) +{ + // 1. 如vfs_open,先判斷掛載資料夾之前的路徑是否存在,不存在回傳0 + struct vnode* dir = current_dir; + struct vnode* target = 0; + + char* pathname_ = (char*)dynamic_alloc(dyalloc_vfs, strlen((char*)mountpoint) + 1); + strcpy(pathname_, mountpoint); + // pathname: /mnt -> pathname_: "\0", dirname: mnt + // pathname: /mnt/dir1 -> pathname_: /mnt, dirname: dir1 + // pathname: dir1 -> pathname_: dir1, dirname: NULL + char* dirname = split_last(pathname_, '/'); + if (*pathname_ == '\0' && mountpoint[0] == '/') + dir = rootfs->root; + + if (dirname != 0) + { + int prefix_found = vfs_find_vnode(&dir, pathname_); + // e.g., given pathname /abc/zxc/file1, but /abc/zxc not found + if (!prefix_found) + return 0; + } + else + dirname = pathname_; + + // 2. 判斷掛載資料夾是否存在,不存在回傳0 + int file_found = dir->v_ops->lookup(dir, &target, dirname); + if (!file_found) + return 0; + + // 3. 假如資料夾沒有掛載在任何系統上,回傳0 + if (!target->mount) + return 0; + + // 4. 設定目錄的mount(=0),回傳1 + target->mount = 0; + return 1; +} \ No newline at end of file diff --git a/lab7/kernel/vfs.h b/lab7/kernel/vfs.h new file mode 100644 index 000000000..629047c9a --- /dev/null +++ b/lab7/kernel/vfs.h @@ -0,0 +1,84 @@ +#ifndef VFS_H +#define VFS_H + +#define O_CREAT 1 // flags use for create file + +// 檔案類型,參考助教的圖(Directory、Regular、Device) +enum FILE_TYPE +{ + FILE_NONE, + FILE_DIRECTORY, + FILE_REGULAR, + FILE_DEVICE +}; + +// 節點 +struct vnode +{ + struct mount* mount; // 屬於哪個mount + struct vnode_operations* v_ops; // v_node操作(建立、搜尋) + struct file_operations* f_ops; // file操作(讀取、寫入) + void* internal; // 節點內容(各檔案系統實作) +}; + +// 檔案 +struct file +{ + struct vnode* vnode; // 所屬節點 + unsigned int f_pos; // 檔案目前位置(file position),供讀寫操作使用 + struct file_operations* f_ops; // file操作(讀取、寫入) (各檔案系統實作) + int flags; // O_CREAT +}; + +// 掛載 +struct mount +{ + struct vnode* root; // root節點 + struct filesystem* fs; // 檔案系統 +}; + +// 檔案系統 +struct filesystem +{ + const char* name; // 名稱 + int (*setup_mount)(struct filesystem* fs, struct mount* mount); // 掛載方法(各檔案系統實作) + struct filesystem* next; // 指向下一個檔案系統指標 +}; + +// 檔案操作(讀取、寫入) +struct file_operations +{ + //寫入方法(各檔案系統實作) + int (*write)(struct file* file, const void* buf, unsigned int len); + //讀取方法(各檔案系統實作) + int (*read)(struct file* file, void* buf, unsigned int len); + // list方法(各檔案系統實作) lab6加分1 + int (*list)(struct file* file, void* buf, int index); +}; + +// v_node操作(查找、建立) +struct vnode_operations +{ + //查找方法(各檔案系統實作) + int (*lookup)(struct vnode* dir_node, struct vnode** target, const char* component_name); + //建立vnode方法(各檔案系統實作) + int (*create)(struct vnode* dir_node, struct vnode** target, const char* component_name, enum FILE_TYPE type); + // 設定parent node(各檔案系統實作) lab6加分2 + int (*set_parent)(struct vnode* child_node, struct vnode* parent_node); +}; + +void vfs_init(); +void register_filesystem(struct filesystem* fs); +struct filesystem* search_fs(char* name); +int vfs_find_vnode(struct vnode** target, const char* pathname); // lab6加分2 +struct file* vfs_open(const char* pathname, int flags); +int vfs_close(struct file* file); +int vfs_write(struct file* file, const void* buf, unsigned int len); +int vfs_read(struct file* file, void* buf, unsigned int len); +int vfs_list(struct file* file, void* buf, int index); +// lab6加分2 +int vfs_mkdir(const char* pathname); +int vfs_chdir(const char* pathname); +int vfs_mount(const char* device, const char* mountpoint, const char* filesystem); +int vfs_umount(const char* mountpoint); +#endif \ No newline at end of file diff --git a/lab7/rootfs/init.txt b/lab7/rootfs/init.txt new file mode 100644 index 000000000..794cece32 --- /dev/null +++ b/lab7/rootfs/init.txt @@ -0,0 +1 @@ +Hello world !! \ No newline at end of file diff --git a/lab7/rootfs/osc.txt b/lab7/rootfs/osc.txt new file mode 100644 index 000000000..30fe44454 --- /dev/null +++ b/lab7/rootfs/osc.txt @@ -0,0 +1 @@ +NYCU OSC 2021 ! \ No newline at end of file diff --git a/lab7/rootfs/raspi3.txt b/lab7/rootfs/raspi3.txt new file mode 100644 index 000000000..f69082dc8 --- /dev/null +++ b/lab7/rootfs/raspi3.txt @@ -0,0 +1,10 @@ + .~~. .~~. + '. \\ ' ' / .' + .~ .~~~..~. + : .~.'~'.~. : + ~ ( ) ( ) ~ + ( : '~'.~.'~' : ) + ~ .~ ( ) ~. ~ + ( : '~' : ) + '~ .~~~. ~' + '~' \ No newline at end of file diff --git a/lab7/rootfs/svc.s b/lab7/rootfs/svc.s new file mode 100644 index 000000000..ce7e53908 --- /dev/null +++ b/lab7/rootfs/svc.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 diff --git a/lab7/sendimg.py b/lab7/sendimg.py new file mode 100644 index 000000000..dcbcdd4c1 --- /dev/null +++ b/lab7/sendimg.py @@ -0,0 +1,44 @@ +#! python2 + +import os +import time +import serial + +filename = "./kernel/kernel8.img" +ser_port = "/dev/ttyS4" +ser_baudrate = 115200 + +try: + ser = serial.Serial( + port=ser_port, + baudrate=ser_baudrate, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=1 + ) + print("Open Serial Port\r") + print(f"Port=%s, baudrate=%d\r" % (ser_port, ser_baudrate)) + + with open(filename, 'rb') as file: + + size = os.path.getsize(filename) + print(f"Image size is {size}") + ser.write(f"{size}\r".encode()) + + time.sleep(2) + + position = 0 + byte = file.read(1) + while byte != b"": + file.seek(position, 0) + byte = file.read(1) + ser.write(byte) + position = position + 1 + + print("Done !") + file.close() + +except Exception as e: + print(f"[EXCEPTION] {e}") + diff --git a/lab7/user_program/argv_test/argv_test.c b/lab7/user_program/argv_test/argv_test.c new file mode 100644 index 000000000..0b0bd7ec7 --- /dev/null +++ b/lab7/user_program/argv_test/argv_test.c @@ -0,0 +1,30 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +int main(int argc, char **argv) +{ + uart_init(); + + // 1. print parent ID + char buf[16] = {0}; + uart_putstr("Argv Test, pid "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr("\n"); + + // 2. prints the arguments + uart_putstr("arguments: "); + for (int i = 0; i < argc; ++i) + { + uart_putstr(argv[i]); + uart_putstr(", "); + } + uart_putstr("\n"); + + // 3. exec fork_test.img + char *fork_argv[] = {"fork_test", 0}; + call_sys_exec("fork_test.img", fork_argv); + + return 0; +} diff --git a/lab7/user_program/argv_test/auxilary.h b/lab7/user_program/argv_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/argv_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/argv_test/gpio.h b/lab7/user_program/argv_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/argv_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/argv_test/linker.ld b/lab7/user_program/argv_test/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/argv_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/argv_test/mmio.h b/lab7/user_program/argv_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/argv_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/argv_test/startup.S b/lab7/user_program/argv_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/argv_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/argv_test/sys.S b/lab7/user_program/argv_test/sys.S new file mode 100644 index 000000000..b3aaad8c0 --- /dev/null +++ b/lab7/user_program/argv_test/sys.S @@ -0,0 +1,35 @@ + +.globl call_sys_gitPID +call_sys_gitPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret diff --git a/lab7/user_program/argv_test/sys.h b/lab7/user_program/argv_test/sys.h new file mode 100644 index 000000000..a90d157ec --- /dev/null +++ b/lab7/user_program/argv_test/sys.h @@ -0,0 +1,13 @@ +#ifndef _SYS_H +#define _SYS_H + +void call_sys_write(char * buf); +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_gitPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +void *call_sys_malloc(); + +#endif \ No newline at end of file diff --git a/lab7/user_program/argv_test/uart.c b/lab7/user_program/argv_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/argv_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/argv_test/uart.h b/lab7/user_program/argv_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/argv_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/argv_test/util.c b/lab7/user_program/argv_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab7/user_program/argv_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/argv_test/util.h b/lab7/user_program/argv_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab7/user_program/argv_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test/.vs/ProjectSettings.json b/lab7/user_program/fat32_test/.vs/ProjectSettings.json new file mode 100644 index 000000000..3daacbff1 --- /dev/null +++ b/lab7/user_program/fat32_test/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "沒有任何組態" +} \ No newline at end of file diff --git a/lab7/user_program/fat32_test/.vs/VSWorkspaceState.json b/lab7/user_program/fat32_test/.vs/VSWorkspaceState.json new file mode 100644 index 000000000..9b72d4ad7 --- /dev/null +++ b/lab7/user_program/fat32_test/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "" + ], + "SelectedNode": "\\util.c", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/.suo b/lab7/user_program/fat32_test/.vs/fat32_test/v16/.suo new file mode 100644 index 000000000..ed3a4f574 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/.suo differ diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/Browse.VC.db b/lab7/user_program/fat32_test/.vs/fat32_test/v16/Browse.VC.db new file mode 100644 index 000000000..f83b15fa7 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/Browse.VC.db differ diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/289b2102d6c3e545/UART.ipch b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/289b2102d6c3e545/UART.ipch new file mode 100644 index 000000000..1ef926cfa Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/289b2102d6c3e545/UART.ipch differ diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/5ed69a8d2a80d18a/FAT32_TEST.ipch b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/5ed69a8d2a80d18a/FAT32_TEST.ipch new file mode 100644 index 000000000..dda1ed433 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/5ed69a8d2a80d18a/FAT32_TEST.ipch differ diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/63fd4549ce15d639/UTIL.ipch b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/63fd4549ce15d639/UTIL.ipch new file mode 100644 index 000000000..5f48a6489 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/63fd4549ce15d639/UTIL.ipch differ diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/ed88b0f5eef1feeb/AUXILARY.ipch b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/ed88b0f5eef1feeb/AUXILARY.ipch new file mode 100644 index 000000000..d170be4d7 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/ed88b0f5eef1feeb/AUXILARY.ipch differ diff --git a/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/eeca96289a37febf/GPIO.ipch b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/eeca96289a37febf/GPIO.ipch new file mode 100644 index 000000000..bfa4c7560 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/fat32_test/v16/ipch/AutoPCH/eeca96289a37febf/GPIO.ipch differ diff --git a/lab7/user_program/fat32_test/.vs/slnx.sqlite b/lab7/user_program/fat32_test/.vs/slnx.sqlite new file mode 100644 index 000000000..0a6e158c2 Binary files /dev/null and b/lab7/user_program/fat32_test/.vs/slnx.sqlite differ diff --git a/lab7/user_program/fat32_test/auxilary.h b/lab7/user_program/fat32_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/fat32_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test/fat32_test.c b/lab7/user_program/fat32_test/fat32_test.c new file mode 100644 index 000000000..75165b127 --- /dev/null +++ b/lab7/user_program/fat32_test/fat32_test.c @@ -0,0 +1,40 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +char buff[16] = {0}; + +int main(int argc, char **argv) +{ + uart_init(); + + call_sys_mkdir("/fat"); + call_sys_mount("fatfs", "/fat", "fatfs"); + + int fd = call_sys_open("/fat", 0); + char name[100]; + int size; + for (int i = 0;; ++i) + { + size = call_sys_list(fd, name, i); + + if (size > 0) + { + //printf("Name: %s Size: %d\n", name, size); + uart_putstr("Name: "); + uart_putstr(name); + uart_putstr(", Size: "); + unsignedlonglongToStr(size, buff); + uart_putstr(buff); + uart_putstr("\n"); + } + else if (size < 0) + break; + } + + call_sys_close(fd); + + call_sys_exit(); + + return 0; +} diff --git a/lab7/user_program/fat32_test/gpio.h b/lab7/user_program/fat32_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/fat32_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test/linker.ld b/lab7/user_program/fat32_test/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/fat32_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/fat32_test/mmio.h b/lab7/user_program/fat32_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/fat32_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test/startup.S b/lab7/user_program/fat32_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/fat32_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/fat32_test/sys.S b/lab7/user_program/fat32_test/sys.S new file mode 100644 index 000000000..80c55862c --- /dev/null +++ b/lab7/user_program/fat32_test/sys.S @@ -0,0 +1,89 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret + +.globl call_sys_mkdir +call_sys_mkdir: + svc #12 + mov x0, x0 + ret + +.globl call_sys_chdir +call_sys_chdir: + svc #13 + mov x0, x0 + ret + +.globl call_sys_mount +call_sys_mount: + svc #14 + mov x0, x0 + ret + +.globl call_sys_umount +call_sys_umount: + svc #15 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab7/user_program/fat32_test/sys.h b/lab7/user_program/fat32_test/sys.h new file mode 100644 index 000000000..73d1c3dd2 --- /dev/null +++ b/lab7/user_program/fat32_test/sys.h @@ -0,0 +1,22 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); +int call_sys_mkdir(const char *pathname); +int call_sys_chdir(const char *pathname); +int call_sys_mount(const char* device, const char* mountpoint, const char* filesystem); +int call_sys_umount(const char* mountpoint); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test/uart.c b/lab7/user_program/fat32_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/fat32_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/fat32_test/uart.h b/lab7/user_program/fat32_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/fat32_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test/util.c b/lab7/user_program/fat32_test/util.c new file mode 100644 index 000000000..9a0addb94 --- /dev/null +++ b/lab7/user_program/fat32_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strncmp(char *s1, char *s2, int n) +{ + for(int i = 0; i < n; i++) + { + if(*s1 != *s2) + return 1; + s1++; + s2++; + } + + return 0; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/fat32_test/util.h b/lab7/user_program/fat32_test/util.h new file mode 100644 index 000000000..69966ecb6 --- /dev/null +++ b/lab7/user_program/fat32_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strncmp(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/.vs/ProjectSettings.json b/lab7/user_program/fat32_test2/.vs/ProjectSettings.json new file mode 100644 index 000000000..3daacbff1 --- /dev/null +++ b/lab7/user_program/fat32_test2/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "沒有任何組態" +} \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/.vs/VSWorkspaceState.json b/lab7/user_program/fat32_test2/.vs/VSWorkspaceState.json new file mode 100644 index 000000000..9b72d4ad7 --- /dev/null +++ b/lab7/user_program/fat32_test2/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "" + ], + "SelectedNode": "\\util.c", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/.suo b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/.suo new file mode 100644 index 000000000..918d49c60 Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/.suo differ diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/Browse.VC.db b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/Browse.VC.db new file mode 100644 index 000000000..dc888f114 Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/Browse.VC.db differ diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/2a37e9be0999b8cf/GPIO.ipch b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/2a37e9be0999b8cf/GPIO.ipch new file mode 100644 index 000000000..649232f91 Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/2a37e9be0999b8cf/GPIO.ipch differ diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/562b8835b761c21b/AUXILARY.ipch b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/562b8835b761c21b/AUXILARY.ipch new file mode 100644 index 000000000..bc8dd260f Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/562b8835b761c21b/AUXILARY.ipch differ diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/8c1dae7868d0b669/UTIL.ipch b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/8c1dae7868d0b669/UTIL.ipch new file mode 100644 index 000000000..7c7aef6ba Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/8c1dae7868d0b669/UTIL.ipch differ diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/d5968a4a9d8f2c15/UART.ipch b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/d5968a4a9d8f2c15/UART.ipch new file mode 100644 index 000000000..a77ad1f6a Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/d5968a4a9d8f2c15/UART.ipch differ diff --git a/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/e7bf21e6180ba772/FAT32_TEST2.ipch b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/e7bf21e6180ba772/FAT32_TEST2.ipch new file mode 100644 index 000000000..446fe9918 Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/fat32_test2/v16/ipch/AutoPCH/e7bf21e6180ba772/FAT32_TEST2.ipch differ diff --git a/lab7/user_program/fat32_test2/.vs/slnx.sqlite b/lab7/user_program/fat32_test2/.vs/slnx.sqlite new file mode 100644 index 000000000..9b3a1ea22 Binary files /dev/null and b/lab7/user_program/fat32_test2/.vs/slnx.sqlite differ diff --git a/lab7/user_program/fat32_test2/auxilary.h b/lab7/user_program/fat32_test2/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/fat32_test2/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/fat32_test2.c b/lab7/user_program/fat32_test2/fat32_test2.c new file mode 100644 index 000000000..a64b06930 --- /dev/null +++ b/lab7/user_program/fat32_test2/fat32_test2.c @@ -0,0 +1,34 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +int main(int argc, char **argv) +{ + uart_init(); + + uart_putstr("start test fat32 read and write.."); + + int size; + char buf[200]; + + // required 2-1 Look up and open a file in FAT32. + uart_putstr("\nlook up and open FATTEST.TXT\n"); + int fd = call_sys_open("/fat/FATTEST.TXT", 0); + + // required 2-2 Read/Write a file in FAT32. + uart_putstr("\nwrite FATTEST.TXT\n"); + call_sys_write(fd, "This is osc2021-lab7 test", 25); + call_sys_close(fd); + + fd = call_sys_open("/fat/FATTEST.TXT", 0); + size = call_sys_read(fd, buf, 200); + buf[size] = '\0'; + uart_putstr(buf); + uart_putstr("\n"); + + call_sys_close(fd); + + call_sys_exit(); + + return 0; +} diff --git a/lab7/user_program/fat32_test2/gpio.h b/lab7/user_program/fat32_test2/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/fat32_test2/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/linker.ld b/lab7/user_program/fat32_test2/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/fat32_test2/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/mmio.h b/lab7/user_program/fat32_test2/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/fat32_test2/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/startup.S b/lab7/user_program/fat32_test2/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/fat32_test2/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/fat32_test2/sys.S b/lab7/user_program/fat32_test2/sys.S new file mode 100644 index 000000000..80c55862c --- /dev/null +++ b/lab7/user_program/fat32_test2/sys.S @@ -0,0 +1,89 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret + +.globl call_sys_mkdir +call_sys_mkdir: + svc #12 + mov x0, x0 + ret + +.globl call_sys_chdir +call_sys_chdir: + svc #13 + mov x0, x0 + ret + +.globl call_sys_mount +call_sys_mount: + svc #14 + mov x0, x0 + ret + +.globl call_sys_umount +call_sys_umount: + svc #15 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/sys.h b/lab7/user_program/fat32_test2/sys.h new file mode 100644 index 000000000..73d1c3dd2 --- /dev/null +++ b/lab7/user_program/fat32_test2/sys.h @@ -0,0 +1,22 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); +int call_sys_mkdir(const char *pathname); +int call_sys_chdir(const char *pathname); +int call_sys_mount(const char* device, const char* mountpoint, const char* filesystem); +int call_sys_umount(const char* mountpoint); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/uart.c b/lab7/user_program/fat32_test2/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/fat32_test2/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/fat32_test2/uart.h b/lab7/user_program/fat32_test2/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/fat32_test2/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/util.c b/lab7/user_program/fat32_test2/util.c new file mode 100644 index 000000000..9a0addb94 --- /dev/null +++ b/lab7/user_program/fat32_test2/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strncmp(char *s1, char *s2, int n) +{ + for(int i = 0; i < n; i++) + { + if(*s1 != *s2) + return 1; + s1++; + s2++; + } + + return 0; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/fat32_test2/util.h b/lab7/user_program/fat32_test2/util.h new file mode 100644 index 000000000..69966ecb6 --- /dev/null +++ b/lab7/user_program/fat32_test2/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strncmp(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fork_test/Makefile b/lab7/user_program/fork_test/Makefile new file mode 100644 index 000000000..8ae953e1c --- /dev/null +++ b/lab7/user_program/fork_test/Makefile @@ -0,0 +1,28 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate kernel8.img +all: fork_test.img + +%.o : %.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate kernel8.img, need startup.o main.o util.o uart.o reboot.o cpio.o linker.ld +fork_test.img: startup.o fork_test.o util.o uart.o linker.ld + $(LD) $(LD_FLAGS) -o fork_test.elf startup.o fork_test.o util.o uart.o + $(OBJCOPY) -O binary fork_test.elf ../../rootfs/fork_test.img + +# clean all generate file +clean: + rm *.o fork_test.elf ../../rootfs/fork_test.img diff --git a/lab7/user_program/fork_test/auxilary.h b/lab7/user_program/fork_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/fork_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/fork_test/fork_test.c b/lab7/user_program/fork_test/fork_test.c new file mode 100644 index 000000000..5529e70a4 --- /dev/null +++ b/lab7/user_program/fork_test/fork_test.c @@ -0,0 +1,93 @@ +#include "util.h" +#include "sys.h" +#include "uart.h" + +char buf[16] = {0}; + +void delay(int count) +{ + for(int i = 0; i < count; i++) + asm volatile("nop"); +} + +void clear_buf() +{ + for(int i = 0; i < 16; i++) + buf[i] = 0; +} + +int main(void) +{ + uart_init(); + + // printf("Fork Test, pid %d\n", getpid()); + clear_buf(); + uart_putstr("Fork Test, pid "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr("\n"); + + + int cnt = 1; + int ret = 0; + if ((ret = call_sys_fork()) == 0) // child + { + // printf("pid: %d, cnt: %d, ptr: %p\n", getpid(), cnt, &cnt); + clear_buf(); + uart_putstr("pid: "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", cnt: "); + unsignedlonglongToStr(cnt, buf); + uart_putstr(buf); + uart_putstr(", ptr: "); + unsigned long long tmp = (unsigned long long)&cnt; + unsignedlonglongToStrHex(tmp, buf); + uart_putstr(buf); + uart_putstr("\n"); + + ++cnt; + call_sys_fork(); + while (cnt < 5) + { + // printf("pid: %d, cnt: %d, ptr: %p\n", getpid(), cnt, &cnt); + clear_buf(); + uart_putstr("pid: "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", cnt: "); + unsignedlonglongToStr(cnt, buf); + uart_putstr(buf); + uart_putstr(", ptr: "); + tmp = (unsigned long long)&cnt; + unsignedlonglongToStrHex(tmp, buf); + uart_putstr(buf); + uart_putstr("\n"); + + delay(1000000); + ++cnt; + } + } + else + { + // printf("parent here, pid %d, child %d\n", getpid(), ret); + clear_buf(); + uart_putstr("parent here, pid "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", child "); + unsignedlonglongToStr(ret, buf); + uart_putstr(buf); + uart_putstr("\n"); + } + + clear_buf(); + uart_putstr("task "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(" exit! \n"); + + call_sys_exit(); + + return 0; +} \ No newline at end of file diff --git a/lab7/user_program/fork_test/gpio.h b/lab7/user_program/fork_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/fork_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/fork_test/linker.ld b/lab7/user_program/fork_test/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/fork_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/fork_test/mmio.h b/lab7/user_program/fork_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/fork_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/fork_test/startup.S b/lab7/user_program/fork_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/fork_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/fork_test/sys.S b/lab7/user_program/fork_test/sys.S new file mode 100644 index 000000000..b3aaad8c0 --- /dev/null +++ b/lab7/user_program/fork_test/sys.S @@ -0,0 +1,35 @@ + +.globl call_sys_gitPID +call_sys_gitPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret diff --git a/lab7/user_program/fork_test/sys.h b/lab7/user_program/fork_test/sys.h new file mode 100644 index 000000000..a90d157ec --- /dev/null +++ b/lab7/user_program/fork_test/sys.h @@ -0,0 +1,13 @@ +#ifndef _SYS_H +#define _SYS_H + +void call_sys_write(char * buf); +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_gitPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +void *call_sys_malloc(); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fork_test/uart.c b/lab7/user_program/fork_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/fork_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/fork_test/uart.h b/lab7/user_program/fork_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/fork_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/fork_test/util.c b/lab7/user_program/fork_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab7/user_program/fork_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/fork_test/util.h b/lab7/user_program/fork_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab7/user_program/fork_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/ls_test/auxilary.h b/lab7/user_program/ls_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/ls_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/ls_test/gpio.h b/lab7/user_program/ls_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/ls_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/ls_test/linker.ld b/lab7/user_program/ls_test/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/ls_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/ls_test/ls_test.c b/lab7/user_program/ls_test/ls_test.c new file mode 100644 index 000000000..3d340a169 --- /dev/null +++ b/lab7/user_program/ls_test/ls_test.c @@ -0,0 +1,40 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +char buf[16] = {0}; + +int main(int argc, char **argv) +{ + uart_init(); + + uart_putstr("List file in directory: "); + uart_putstr(argv[1]); + uart_putstr("\n"); + + int fd = call_sys_open(argv[1], 0); + char name[100]; + int size; + // Modify the for loop to iterate the directory entries of the opened directory. + for(int i = 0;; ++i) + { + size = call_sys_list(fd, name, i); + + if(size > 0) + { + //printf("Name: %s Size: %d\n", name, size); + uart_putstr("Name: "); + uart_putstr(name); + uart_putstr(" Size: "); + unsignedlonglongToStr(size, buf); + uart_putstr(buf); + uart_putstr("\n"); + } + else if (size < 0) + break; + } + + call_sys_exit(); + + return 0; +} diff --git a/lab7/user_program/ls_test/mmio.h b/lab7/user_program/ls_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/ls_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/ls_test/startup.S b/lab7/user_program/ls_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/ls_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/ls_test/sys.S b/lab7/user_program/ls_test/sys.S new file mode 100644 index 000000000..3ebc262d9 --- /dev/null +++ b/lab7/user_program/ls_test/sys.S @@ -0,0 +1,65 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab7/user_program/ls_test/sys.h b/lab7/user_program/ls_test/sys.h new file mode 100644 index 000000000..3d52a155e --- /dev/null +++ b/lab7/user_program/ls_test/sys.h @@ -0,0 +1,18 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); + +#endif \ No newline at end of file diff --git a/lab7/user_program/ls_test/uart.c b/lab7/user_program/ls_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/ls_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/ls_test/uart.h b/lab7/user_program/ls_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/ls_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/ls_test/util.c b/lab7/user_program/ls_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab7/user_program/ls_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/ls_test/util.h b/lab7/user_program/ls_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab7/user_program/ls_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/auxilary.h b/lab7/user_program/multilvl_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/multilvl_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/gpio.h b/lab7/user_program/multilvl_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/multilvl_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/linker.ld b/lab7/user_program/multilvl_test/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/multilvl_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/mmio.h b/lab7/user_program/multilvl_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/multilvl_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/multilvl_test.c b/lab7/user_program/multilvl_test/multilvl_test.c new file mode 100644 index 000000000..5040d6ccb --- /dev/null +++ b/lab7/user_program/multilvl_test/multilvl_test.c @@ -0,0 +1,63 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +int main(int argc, char **argv) +{ + uart_init(); + + uart_putstr("start multilevel test !\n"); + + char buf[8]; + call_sys_mkdir("mnt"); + int fd = call_sys_open("/mnt/a.txt", O_CREAT); + call_sys_write(fd, "Hi", 2); + call_sys_close(fd); + call_sys_chdir("mnt"); + fd = call_sys_open("./a.txt", 0); + //assert(fd >= 0); + if (!(fd >= 0)) + { + uart_putstr("assert1, fd < 0 \n"); + return 0; + } + call_sys_read(fd, buf, 2); + //assert(strncmp(buf, "Hi", 2) == 0); + if (!(strncmp(buf, "Hi", 2) == 0)) + { + uart_putstr("assert2, buf != Hi \n"); + return 0; + } + + call_sys_chdir(".."); + call_sys_mount("tmpfs", "mnt", "tmpfs"); + fd = call_sys_open("mnt/a.txt", 0); + //assert(fd < 0); + if (!(fd < 0)) + { + uart_putstr("assert3, fd >= 0 \n"); + return 0; + } + + call_sys_umount("/mnt"); + fd = call_sys_open("/mnt/a.txt", 0); + //assert(fd >= 0); + if (!(fd >= 0)) + { + uart_putstr("assert4, fd < 0 \n"); + return 0; + } + call_sys_read(fd, buf, 2); + //assert(strncmp(buf, "Hi", 2) == 0); + if (!(strncmp(buf, "Hi", 2) == 0)) + { + uart_putstr("assert5, buf != Hi \n"); + return 0; + } + + uart_putstr("end multilevel test !\n"); + + call_sys_exit(); + + return 0; +} diff --git a/lab7/user_program/multilvl_test/startup.S b/lab7/user_program/multilvl_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/multilvl_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/multilvl_test/sys.S b/lab7/user_program/multilvl_test/sys.S new file mode 100644 index 000000000..80c55862c --- /dev/null +++ b/lab7/user_program/multilvl_test/sys.S @@ -0,0 +1,89 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret + +.globl call_sys_mkdir +call_sys_mkdir: + svc #12 + mov x0, x0 + ret + +.globl call_sys_chdir +call_sys_chdir: + svc #13 + mov x0, x0 + ret + +.globl call_sys_mount +call_sys_mount: + svc #14 + mov x0, x0 + ret + +.globl call_sys_umount +call_sys_umount: + svc #15 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/sys.h b/lab7/user_program/multilvl_test/sys.h new file mode 100644 index 000000000..73d1c3dd2 --- /dev/null +++ b/lab7/user_program/multilvl_test/sys.h @@ -0,0 +1,22 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); +int call_sys_mkdir(const char *pathname); +int call_sys_chdir(const char *pathname); +int call_sys_mount(const char* device, const char* mountpoint, const char* filesystem); +int call_sys_umount(const char* mountpoint); + +#endif \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/uart.c b/lab7/user_program/multilvl_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/multilvl_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/multilvl_test/uart.h b/lab7/user_program/multilvl_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/multilvl_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/util.c b/lab7/user_program/multilvl_test/util.c new file mode 100644 index 000000000..9a0addb94 --- /dev/null +++ b/lab7/user_program/multilvl_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strncmp(char *s1, char *s2, int n) +{ + for(int i = 0; i < n; i++) + { + if(*s1 != *s2) + return 1; + s1++; + s2++; + } + + return 0; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/multilvl_test/util.h b/lab7/user_program/multilvl_test/util.h new file mode 100644 index 000000000..69966ecb6 --- /dev/null +++ b/lab7/user_program/multilvl_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strncmp(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/svc.s b/lab7/user_program/svc.s new file mode 100644 index 000000000..864a47b5a --- /dev/null +++ b/lab7/user_program/svc.s @@ -0,0 +1,13 @@ +/* required 1-2 1-3 user program */ + +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/lab7/user_program/vfs_test/auxilary.h b/lab7/user_program/vfs_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab7/user_program/vfs_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab7/user_program/vfs_test/gpio.h b/lab7/user_program/vfs_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab7/user_program/vfs_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab7/user_program/vfs_test/linker.ld b/lab7/user_program/vfs_test/linker.ld new file mode 100644 index 000000000..e831b94fa --- /dev/null +++ b/lab7/user_program/vfs_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x5000000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab7/user_program/vfs_test/mmio.h b/lab7/user_program/vfs_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab7/user_program/vfs_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab7/user_program/vfs_test/startup.S b/lab7/user_program/vfs_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab7/user_program/vfs_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab7/user_program/vfs_test/sys.S b/lab7/user_program/vfs_test/sys.S new file mode 100644 index 000000000..fe483c59a --- /dev/null +++ b/lab7/user_program/vfs_test/sys.S @@ -0,0 +1,59 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab7/user_program/vfs_test/sys.h b/lab7/user_program/vfs_test/sys.h new file mode 100644 index 000000000..fb6ee5405 --- /dev/null +++ b/lab7/user_program/vfs_test/sys.h @@ -0,0 +1,17 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); + +#endif \ No newline at end of file diff --git a/lab7/user_program/vfs_test/uart.c b/lab7/user_program/vfs_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab7/user_program/vfs_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab7/user_program/vfs_test/uart.h b/lab7/user_program/vfs_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab7/user_program/vfs_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab7/user_program/vfs_test/util.c b/lab7/user_program/vfs_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab7/user_program/vfs_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab7/user_program/vfs_test/util.h b/lab7/user_program/vfs_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab7/user_program/vfs_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab7/user_program/vfs_test/vfs_test.c b/lab7/user_program/vfs_test/vfs_test.c new file mode 100644 index 000000000..1b387b660 --- /dev/null +++ b/lab7/user_program/vfs_test/vfs_test.c @@ -0,0 +1,31 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +char buf[100] = {0}; + +int main(int argc, char **argv) +{ + uart_init(); + + int a = call_sys_open("hello", O_CREAT); + int b = call_sys_open("world", O_CREAT); + call_sys_write(a, "Hello ", 6); + call_sys_write(b, "World!", 6); + call_sys_close(a); + call_sys_close(b); + b = call_sys_open("hello", 0); + a = call_sys_open("world", 0); + int sz; + sz = call_sys_read(b, buf, 100); + sz += call_sys_read(a, buf + sz, 100); + buf[sz] = '\0'; + + //printf("%s\n", buf); // should be Hello World! + uart_putstr(buf); + uart_putstr("\n"); + + call_sys_exit(); + + return 0; +} diff --git a/lab8/FATTEST.txt b/lab8/FATTEST.txt new file mode 100644 index 000000000..4498eb4cb --- /dev/null +++ b/lab8/FATTEST.txt @@ -0,0 +1 @@ +This is for fat32 test read/write. \ No newline at end of file diff --git a/lab8/bcm2710-rpi-3-b-plus.dtb b/lab8/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..315b58137 Binary files /dev/null and b/lab8/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab8/bootloader/Makefile b/lab8/bootloader/Makefile new file mode 100644 index 000000000..11adc6c56 --- /dev/null +++ b/lab8/bootloader/Makefile @@ -0,0 +1,45 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate bootloader.img +all: bootloader.img + +# compile startup.s, generate startup.o +startup.o: startup.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate bootloader.img, need startup.o main.o util.o uart.o reboot.o linker.ld +bootloader.img: startup.o main.o util.o uart.o reboot.o kernel.o linker.ld + $(LD) $(LD_FLAGS) -o bootloader.elf startup.o main.o util.o uart.o reboot.o kernel.o + $(OBJCOPY) -O binary bootloader.elf $@ + +# clean all generate file +clean: + rm *.o bootloader.elf bootloader.img + +# check asm by qemu +asm: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -d in_asm + +# run bootloader.img for test +run: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial stdio + +# pseudo TTY +tty: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -serial null -serial pty + +# gdb debug +debug: all + qemu-system-aarch64 -M raspi3 -kernel bootloader.img -display none -S -s diff --git a/lab8/bootloader/auxilary.h b/lab8/bootloader/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/bootloader/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/bootloader/gpio.h b/lab8/bootloader/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/bootloader/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/bootloader/kernel.c b/lab8/bootloader/kernel.c new file mode 100644 index 000000000..4a977cbc9 --- /dev/null +++ b/lab8/bootloader/kernel.c @@ -0,0 +1,87 @@ +#include "kernel.h" +#include "uart.h" +#include "util.h" + +extern char _end[]; +extern char __kernel_begin[]; +extern char __kernel_size[]; +unsigned long int backup_address = 0x60000; + +void load_new_kernel() +{ + uart_putstr("Start Loading kernel image...\n"); + + // 1. define new kernel address + long long address_int = 0x80000; + + // 2. send kernel (use sendimg.py) + uart_putstr("Please send kernel image from UART now...\n"); + + // 3. read kernel size + char k_size[20] = {0}; + uart_read_cmd(k_size); + int kernel_size = atoi(k_size); + + // 4. show kernel size and load address + uart_putstr("Kernel size: "); + uart_putstr(k_size); + uart_putstr("\t"); + uart_putstr("Load address: 0x"); + uart_putstr("Load address: 0x80000"); + uart_putstr("\n"); + + // 5. read new kernel form uart, start on new_address(0x80000) + uart_putstr("Starting to load target kernel\n"); + + char* new_address = (char*)address_int; + for (int i = 0; i < kernel_size; i++) + { + char c = uart_getchar(); // all char should be get + new_address[i] = c; + } + + uart_putstr("Finished load target kernel and running.\n"); + + // 6. load new kernel + void (*new_kernel)(void) = (void *)new_address; + new_kernel(); +} + +void backup_old_kernel() +{ + char *kernel = __kernel_begin; + unsigned long int size = (unsigned long int)__kernel_size; + char *backup = (char *)(backup_address); + + uart_putstr("Starting to backup bootloader\n"); + + // move bootloader to backup_address(0x60000) + while (size--) + { + *backup = *kernel; + kernel++; + backup++; + } + + uart_putstr("Finished backup bootloader\n"); +} + +void load_image() +{ + // 1. backup bootloader kernel to 0x60000 + backup_old_kernel(); + + // 2. calculate new 'load_new_kernel' funciton address + + // because old kernel are backup to backup_address + // so load_new_kernel function need move to new address + // offest = load_target_func_address - kernel_start + + void (*load_target_ptr)() = load_new_kernel; + unsigned long int load_target_func_address = (unsigned long int) load_target_ptr; + void (*load_kernel)() = + (void (*)())( backup_address + (load_target_func_address - (unsigned long int)__kernel_begin) ); + + // 3. load new kernel form uart + load_kernel(); +} diff --git a/lab8/bootloader/kernel.h b/lab8/bootloader/kernel.h new file mode 100644 index 000000000..c6f0117b1 --- /dev/null +++ b/lab8/bootloader/kernel.h @@ -0,0 +1,8 @@ +#ifndef KERNEL +#define KERNEL + +void load_image(); +void backup_old_kernel(); +void load_new_kernel(); + +#endif \ No newline at end of file diff --git a/lab8/bootloader/linker.ld b/lab8/bootloader/linker.ld new file mode 100644 index 000000000..86f4760a8 --- /dev/null +++ b/lab8/bootloader/linker.ld @@ -0,0 +1,46 @@ +SECTIONS /* Use the SECTIONS keyword to declare a section, the following is its content */ +{ + . = 0x80000; /* move the 'location counter' to 0x30000 */ + __kernel_begin = .; + .text : /* assign .text section */ + { + KEEP(*(.text.boot)) /* make sure .text.boot will not be remove by linker-time garbage collection. */ + *(.text .text.*) /* *(.text .text.*) is all .text and .text. */ + } + .rodata : /* assign .rodata section */ + { + *(.rodata .rodata.*) /* *(.rodata .rodata.*) is all .rodata and .rodata. */ + } + .data : /* assign .data section */ + { + *(.data .data.*) /* *(.data .data.*) is all .data and .data. */ + } + .bss (NOLOAD) : /* assign .bss section, NOLOAD means not load at runtime */ + { + . = ALIGN(16); /* aligned to 16-byte boundaries. ex.0x0000000000080000 */ + __bss_start = .; /* assign __bss_start to the current memory position */ + *(.bss .bss.*) /* *(.bss .bss.*) is all .bss and .bss. */ + *(COMMON) /* assign uninitialized data section */ + __bss_end = .; /* assign __bss_end to the current memory position */ + } + _end = .; /* assign _end to the current memory position */ +} +__bss_size = (__bss_end - __bss_start) >> 3; /* _bss_size set to 8 */ +__kernel_size = (_end - __kernel_begin); + +/* + KEEP: make sure the section will not be remove by linker-time garbage collection. + .text: code section + .rodata: stores read-only data, e.g. const variables and strings + .bss : uninitialized data section (weak symbol) e.g. global var init to zero + .data : initialized data section (strong symbol) + NOLOAD : Does not load at runtime (because bss section is full of zero) + COMMON : uninitialized data section (weak symbol) e.g. global var just declare + + [example] + const int a = 0; // .rodata + int b = 3; // .data + int c = 0; // .bss + int d; // COMMON + +*/ \ No newline at end of file diff --git a/lab8/bootloader/main.c b/lab8/bootloader/main.c new file mode 100644 index 000000000..9bbad794c --- /dev/null +++ b/lab8/bootloader/main.c @@ -0,0 +1,69 @@ +#include "uart.h" +#include "util.h" +#include "reboot.h" +#include "kernel.h" + +#define CMDSIZE 64 +char cmd[CMDSIZE] = {0}; + +void cmd_init() +{ + for(int i = 0; i < CMDSIZE; i++) + cmd[i] = 0; +} + +void cmd_handle() // parse command +{ + if (strcmp(cmd, "help")) + { + uart_putstr("help print all available commands \n"); + uart_putstr("loadimg load new kernel from uart \n"); + uart_putstr("reboot reboot raspi3 \n"); + } + else if (strcmp(cmd, "loadimg")) + { + load_image(); + } + else if(strcmp(cmd, "reboot")) + { + uart_putstr("reboot .... \n"); + raspi3_reboot(100); + while(1); // wait for reboot + } + else if(strlen(cmd) != 0) + { + uart_putstr("command \""); + uart_putstr(cmd); + uart_putstr("\" not found, try \n"); + } + + uart_putstr("# "); +} + +void main() +{ + cmd_init(); + uart_init(); + + // put welcome ascii art + uart_putstr("\n"); + uart_putstr(" .~~. .~~. \n"); + uart_putstr(" '. \\ ' ' / .' \n"); + uart_putstr(" .~ .~~~..~. \n"); + uart_putstr(" : .~.'~'.~. : \n"); + uart_putstr(" ~ ( ) ( ) ~ \n"); + uart_putstr(" ( : '~'.~.'~' : ) \n"); + uart_putstr(" ~ .~ ( ) ~. ~ \n"); + uart_putstr(" ( : '~' : ) This is bootloader !! \n"); + uart_putstr(" '~ .~~~. ~' \n"); + uart_putstr(" '~' \n"); + uart_putstr("# "); + + while(1) + { + uart_read_cmd(cmd); + cmd_handle(); + cmd_init(); + } + +} diff --git a/lab8/bootloader/mmio.h b/lab8/bootloader/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/bootloader/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/bootloader/reboot.c b/lab8/bootloader/reboot.c new file mode 100644 index 000000000..f9c108a66 --- /dev/null +++ b/lab8/bootloader/reboot.c @@ -0,0 +1,13 @@ +#include "reboot.h" + +void raspi3_reboot(int ticks) // reboot after watchdog timer expire +{ + *PM_RSTC = PM_PASSWORD | 0x20; // full reset + *PM_WDOG = PM_PASSWORD | ticks; // number of watchdog tick +} + +void cancel_reset() +{ + *PM_RSTC = PM_PASSWORD | 0; // full reset + *PM_WDOG = PM_PASSWORD | 0; // number of watchdog tick +} \ No newline at end of file diff --git a/lab8/bootloader/reboot.h b/lab8/bootloader/reboot.h new file mode 100644 index 000000000..7831d4bcb --- /dev/null +++ b/lab8/bootloader/reboot.h @@ -0,0 +1,12 @@ +#ifndef REBOOT +#define REBOOT + +#define PM_PASSWORD 0x5A000000 + +#define PM_RSTC ((volatile unsigned int*)0x3F10001C) +#define PM_WDOG ((volatile unsigned int*)0x3F100024) + +void raspi3_reboot(int ticks); // reboot after watchdog timer expire +void cancel_reset(); // cancel_reset + +#endif \ No newline at end of file diff --git a/lab8/bootloader/startup.s b/lab8/bootloader/startup.s new file mode 100644 index 000000000..4a34a0733 --- /dev/null +++ b/lab8/bootloader/startup.s @@ -0,0 +1,38 @@ +.section ".text.boot" // Declare a section of .text.boot + +.global _start /* The .global keyword is used to make a symbol visible to the linker + .global _start makes the _start symbol to a visible global symbol */ + +// The bootloader of rpi3 loads kernel8.img to RAM (address 0x80000) by GPU +// After that, 4 CPU cores start to run the same code simultaneously. +// So, let only one core proceed, and let others enter a busy loop. + +_start: // _start represents the program start entry symbol, the following is its execution content + mrs x1, mpidr_el1 // gets the processor ID from the 'mpidr_el1' system register to x1 + and x1, x1, #3 // and bit, because rpi3 have 4 CPU cores, so and #3 + cbz x1, init // compare, if cpu id = 0 jump to init. + b busy_loop // else jump to busy_loop + +busy_loop: + wfe // Let ARM enter the 'low-power standby state' amd wait for event + b busy_loop // b means jump, the this sentence means jump back to busy_loop + +init: + ldr x1, =0x9000000 + str x0, [x1] + + ldr x1, =__bss_start // init bss segment, bss segment are initialized to 0, .bss start address to x1 + ldr x2, =__bss_size // size of the .bss section to x2 + +loop_clear_bss: + cbz x2, entry_point // if .bss size = 0, jump to entry_point + str xzr, [x1], #8 // store register. xzr is zero register. *x1 = 0; x1 += 8; + sub x2, x2, #1 // x2 = x2 - 1; (because __bss_size / 8) + cbnz x2, loop_clear_bss // if .bss size > 0 jump to loop_clear_bss. + +entry_point: + ldr x1, =_start // sp is stack pointer, mov _start to stack top + mov sp, x1 // + bl main // jump to our main() method of main.c + b busy_loop // should never come here, if failure jump to busy_loop + diff --git a/lab8/bootloader/uart.c b/lab8/bootloader/uart.c new file mode 100644 index 000000000..c2c0447b2 --- /dev/null +++ b/lab8/bootloader/uart.c @@ -0,0 +1,164 @@ +#include "gpio.h" +#include "auxilary.h" +#include "util.h" + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +full char, use for uart send kernel +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +// read command +void uart_read_cmd(char* cmd) +{ + char c; + char c2; + int cmdSize = 0; + + while (c != '\n' || c == '\r') + { + c = uart_getchar(); + + if (c == '\n' || c == '\r') // 0X0A '\n' newline, parse command + { + while(cmd[cmdSize] != 0) + cmdSize++; + cmd[cmdSize] = '\0'; + cmdSize++; + uart_putstr("\n"); + break; + } + else if (c == 127) + { + if(cmdSize > 0) + { + cmdSize--; + cmd[cmdSize] = 0; + uart_putstr("\b \b"); + } + } + else if (c == '[') + { + c2 = uart_getchar(); + if (c2 == 'A') // cursor up + { + } + else if (c2 == 'C' && cmdSize < strlen(cmd)) // cursor left + { + uart_putstr("\033[C"); + cmdSize++; + } + else if (c2 == 'D' && cmdSize > 0) // cursor right + { + uart_putstr("\033[D"); + cmdSize--; + } + } + else + { + if (c > 31 && c < 127) // visible ascii + { + cmd[cmdSize] = c; + cmdSize++; + uart_sendchar(c); + } + } + } +} diff --git a/lab8/bootloader/uart.h b/lab8/bootloader/uart.h new file mode 100644 index 000000000..968de3ab6 --- /dev/null +++ b/lab8/bootloader/uart.h @@ -0,0 +1,10 @@ +#ifndef UART_H +#define UART_H + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string +void uart_read_cmd(char* cmd); // read command + +#endif \ No newline at end of file diff --git a/lab8/bootloader/util.c b/lab8/bootloader/util.c new file mode 100644 index 000000000..9aeb53875 --- /dev/null +++ b/lab8/bootloader/util.c @@ -0,0 +1,84 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +long long hex2int(char* hex) +{ + long long val = 0; + while (*hex) + { + // get current character then increment + int byte1 = *hex++; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1 ; + } + return val; +} \ No newline at end of file diff --git a/lab8/bootloader/util.h b/lab8/bootloader/util.h new file mode 100644 index 000000000..4ed90b3ec --- /dev/null +++ b/lab8/bootloader/util.h @@ -0,0 +1,9 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal ? +int atoi(char* input); // string to int +long long hex2int(char* hex); // hex string to integer + +#endif \ No newline at end of file diff --git a/lab8/config.txt b/lab8/config.txt new file mode 100644 index 000000000..e82fa85fa --- /dev/null +++ b/lab8/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/kernel/Makefile b/lab8/kernel/Makefile new file mode 100644 index 000000000..78f17e25f --- /dev/null +++ b/lab8/kernel/Makefile @@ -0,0 +1,132 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate kernel8.img +all: kernel8.img user_program argv_test fork_test vfs_test ls_test multilvl_test fat32_test fat32_test2 mmu_test gencpio + +%.o : %.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate kernel8.img, need startup.o main.o util.o uart.o reboot.o cpio.o linker.ld +kernel8.img: startup.o main.o util.o uart.o reboot.o cpio.o devicetree.o buddy.o allocator.o exception.o exception_handle.o timer.o core_timer.o context_switch.o thread.o vfs.o tmpfs.o sdhost.o fat32.o mmu.o linker.ld + $(LD) $(LD_FLAGS) -o kernel8.elf startup.o main.o util.o uart.o reboot.o cpio.o devicetree.o buddy.o allocator.o exception.o exception_handle.o timer.o core_timer.o context_switch.o thread.o vfs.o sdhost.o fat32.o tmpfs.o mmu.o + $(OBJCOPY) -O binary kernel8.elf $@ + +# generate boot.o +../user_program/svc.o : ../user_program/svc.s + $(CC) $(CFLAGS) -c $< -o $@ + +# generate boot.elf and put rootfs +user_program : ../user_program/svc.o + aarch64-linux-gnu-ld -o ../rootfs/svc.elf ../user_program/svc.o + +../user_program/argv_test/%.o : ../user_program/argv_test/%.s + $(CC) -c $< -o $@ + +../user_program/argv_test/%.o : ../user_program/argv_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +argv_test : ../user_program/argv_test/startup.o ../user_program/argv_test/sys.o ../user_program/argv_test/argv_test.o ../user_program/argv_test/uart.o ../user_program/argv_test/util.o + $(LD) -T ../user_program/argv_test/linker.ld -o ../user_program/argv_test/argv_test.elf ../user_program/argv_test/startup.o ../user_program/argv_test/sys.o ../user_program/argv_test/argv_test.o ../user_program/argv_test/uart.o ../user_program/argv_test/util.o + $(OBJCOPY) -O binary ../user_program/argv_test/argv_test.elf ../rootfs/argv_test.img + +../user_program/fork_test/%.o : ../user_program/fork_test/%.s + $(CC) -c $< -o $@ + +../user_program/fork_test/%.o : ../user_program/fork_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +fork_test : ../user_program/fork_test/startup.o ../user_program/fork_test/sys.o ../user_program/fork_test/fork_test.o ../user_program/fork_test/uart.o ../user_program/fork_test/util.o + $(LD) -T ../user_program/fork_test/linker.ld -o ../user_program/fork_test/fork_test.elf ../user_program/fork_test/startup.o ../user_program/fork_test/sys.o ../user_program/fork_test/fork_test.o ../user_program/fork_test/uart.o ../user_program/fork_test/util.o + $(OBJCOPY) -O binary ../user_program/fork_test/fork_test.elf ../rootfs/fork_test.img + +../user_program/vfs_test/%.o : ../user_program/vfs_test/%.s + $(CC) -c $< -o $@ + +../user_program/vfs_test/%.o : ../user_program/vfs_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +vfs_test : ../user_program/vfs_test/startup.o ../user_program/vfs_test/sys.o ../user_program/vfs_test/vfs_test.o ../user_program/vfs_test/uart.o ../user_program/vfs_test/util.o + $(LD) -T ../user_program/vfs_test/linker.ld -o ../user_program/vfs_test/vfs_test.elf ../user_program/vfs_test/startup.o ../user_program/vfs_test/sys.o ../user_program/vfs_test/vfs_test.o ../user_program/vfs_test/uart.o ../user_program/vfs_test/util.o + $(OBJCOPY) -O binary ../user_program/vfs_test/vfs_test.elf ../rootfs/vfs_test.img + +../user_program/ls_test/%.o : ../user_program/ls_test/%.s + $(CC) -c $< -o $@ + +../user_program/ls_test/%.o : ../user_program/ls_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +ls_test : ../user_program/ls_test/startup.o ../user_program/ls_test/sys.o ../user_program/ls_test/ls_test.o ../user_program/ls_test/uart.o ../user_program/ls_test/util.o + $(LD) -T ../user_program/ls_test/linker.ld -o ../user_program/ls_test/ls_test.elf ../user_program/ls_test/startup.o ../user_program/ls_test/sys.o ../user_program/ls_test/ls_test.o ../user_program/ls_test/uart.o ../user_program/ls_test/util.o + $(OBJCOPY) -O binary ../user_program/ls_test/ls_test.elf ../rootfs/ls_test.img + +../user_program/multilvl_test/%.o : ../user_program/multilvl_test/%.s + $(CC) -c $< -o $@ + +../user_program/multilvl_test/%.o : ../user_program/multilvl_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +multilvl_test : ../user_program/multilvl_test/startup.o ../user_program/multilvl_test/sys.o ../user_program/multilvl_test/multilvl_test.o ../user_program/multilvl_test/uart.o ../user_program/multilvl_test/util.o + $(LD) -T ../user_program/multilvl_test/linker.ld -o ../user_program/multilvl_test/multilvl_test.elf ../user_program/multilvl_test/startup.o ../user_program/multilvl_test/sys.o ../user_program/multilvl_test/multilvl_test.o ../user_program/multilvl_test/uart.o ../user_program/multilvl_test/util.o + $(OBJCOPY) -O binary ../user_program/multilvl_test/multilvl_test.elf ../rootfs/multilvl_test.img + +../user_program/fat32_test/%.o : ../user_program/fat32_test/%.s + $(CC) -c $< -o $@ + +../user_program/fat32_test/%.o : ../user_program/fat32_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +fat32_test : ../user_program/fat32_test/startup.o ../user_program/fat32_test/sys.o ../user_program/fat32_test/fat32_test.o ../user_program/fat32_test/uart.o ../user_program/fat32_test/util.o + $(LD) -T ../user_program/fat32_test/linker.ld -o ../user_program/fat32_test/fat32_test.elf ../user_program/fat32_test/startup.o ../user_program/fat32_test/sys.o ../user_program/fat32_test/fat32_test.o ../user_program/fat32_test/uart.o ../user_program/fat32_test/util.o + $(OBJCOPY) -O binary ../user_program/fat32_test/fat32_test.elf ../rootfs/fat32_test.img + +../user_program/fat32_test2/%.o : ../user_program/fat32_test2/%.s + $(CC) -c $< -o $@ + +../user_program/fat32_test2/%.o : ../user_program/fat32_test2/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +fat32_test2 : ../user_program/fat32_test2/startup.o ../user_program/fat32_test2/sys.o ../user_program/fat32_test2/fat32_test2.o ../user_program/fat32_test2/uart.o ../user_program/fat32_test2/util.o + $(LD) -T ../user_program/fat32_test2/linker.ld -o ../user_program/fat32_test2/fat32_test2.elf ../user_program/fat32_test2/startup.o ../user_program/fat32_test2/sys.o ../user_program/fat32_test2/fat32_test2.o ../user_program/fat32_test2/uart.o ../user_program/fat32_test2/util.o + $(OBJCOPY) -O binary ../user_program/fat32_test2/fat32_test2.elf ../rootfs/fat32_test2.img + +../user_program/mmu_test/%.o : ../user_program/mmu_test/%.s + $(CC) -c $< -o $@ + +../user_program/mmu_test/%.o : ../user_program/mmu_test/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +mmu_test : ../user_program/mmu_test/startup.o ../user_program/mmu_test/sys.o ../user_program/mmu_test/mmu_test.o ../user_program/mmu_test/uart.o ../user_program/mmu_test/util.o + $(LD) -T ../user_program/mmu_test/linker.ld -o ../user_program/mmu_test/mmu_test.elf ../user_program/mmu_test/startup.o ../user_program/mmu_test/sys.o ../user_program/mmu_test/mmu_test.o ../user_program/mmu_test/uart.o ../user_program/mmu_test/util.o + $(OBJCOPY) -O binary ../user_program/mmu_test/mmu_test.elf ../rootfs/mmu_test.img + +gencpio: + cd ../rootfs; find . | cpio -o -H newc > ../initramfs.cpio + +# clean all generate file +clean: + rm *.o kernel8.elf kernel8.img ../initramfs.cpio ../user_program/svc.o ../rootfs/svc.elf ../user_program/argv_test/*.o ../user_program/argv_test/*.elf ../rootfs/argv_test.img ../user_program/fork_test/*.o ../user_program/fork_test/*.elf ../rootfs/fork_test.img ../user_program/vfs_test/*.o ../user_program/vfs_test/*.elf ../rootfs/vfs_test.img ../user_program/ls_test/*.o ../user_program/ls_test/*.elf ../rootfs/ls_test.img ../user_program/multilvl_test/*.o ../user_program/multilvl_test/*.elf ../rootfs/multilvl_test.img ../user_program/fat32_test/*.o ../user_program/fat32_test/*.elf ../rootfs/fat32_test.img ../user_program/fat32_test2/*.o ../user_program/fat32_test2/*.elf ../rootfs/fat32_test2.img ../user_program/mmu_test/*.o ../user_program/mmu_test/*.elf ../rootfs/mmu_test.img + + +# check asm by qemu +asm: all + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -d in_asm + +# run kernel8.img for test +run: all + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -serial null -serial stdio -initrd ../initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb -drive if=sd,file=../sfn_nctuos.img,format=raw + +# gdb debug +debug: all + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -S -s diff --git a/lab8/kernel/allocator.c b/lab8/kernel/allocator.c new file mode 100644 index 000000000..35a07b5b2 --- /dev/null +++ b/lab8/kernel/allocator.c @@ -0,0 +1,223 @@ +#include "util.h" +#include "buddy.h" +#include "uart.h" +#include "allocator.h" + +struct object_allocator obj_alloc_pool[ALLOCATOR_POOL_SIZE * 4]; +unsigned int object_allocator_gid = 0; + +struct dynamic_allocator dynamic_alloc_pool[ALLOCATOR_POOL_SIZE]; +unsigned int dynamic_allocator_gid = 0; + +void memory_init() +{ + object_allocator_gid = 0; + dynamic_allocator_gid = 0; +} + +void dynamic_test() +{ + unsigned long long addr; + + uart_putstr("\n================================== dynamic allocator ==================================\n"); + buddy_init(); + struct dynamic_allocator *dyalloc = dynamic_allocator_init(); + + uart_putstr("============= init end =====================\n"); + uart_putstr("\n use 4 obj_alloc (64,512,2048,4096) \n"); + uart_putstr("[Dynamic Allocator] allocate 16 bytes...\n"); + addr = dynamic_alloc(dyalloc, 16); + + uart_putstr("[Dynamic Allocator] allocate 33 bytes...\n"); + addr = dynamic_alloc(dyalloc, 33); + uart_putstr("[Dynamic Allocator] free addr...\n"); + dynamic_free(dyalloc, addr); + uart_putstr("[Dynamic Allocator] allocate 60 bytes...\n"); + addr = dynamic_alloc(dyalloc, 60); + + uart_putstr("[Dynamic Allocator] allocate 1500 bytes...\n"); + addr = dynamic_alloc(dyalloc, 1500); + uart_putstr("[Dynamic Allocator] allocate 1800 bytes...\n"); + addr = dynamic_alloc(dyalloc, 1800); + uart_putstr("[Dynamic Allocator] allocate 2000 bytes...\n"); + addr = dynamic_alloc(dyalloc, 2000); + uart_putstr("[Dynamic Allocator] allocate 5566 bytes...\n"); + addr = dynamic_alloc(dyalloc, 5566); + uart_putstr("[Dynamic Allocator] free addr...\n"); + dynamic_free(dyalloc, addr); +} +/* +void show_allocator_alloc_message(unsigned long long addr, int obj_size) +{ + char buf[16] = {0}; + + uart_putstr("[Dynamic Allocator] allocate object "); + unsignedlonglongToStr(obj_size, buf); + uart_putstr(buf); + uart_putstr(" bytes at "); + unsignedlonglongToStrHex(addr, buf); + uart_putstr(buf); + uart_putstr(" \n\n"); +} + +void show_allocator_free_message(unsigned long long addr, int obj_size) +{ + char buf[16] = {0}; + + uart_putstr("[Dynamic Allocator] free object "); + unsignedlonglongToStr(obj_size, buf); + uart_putstr(buf); + uart_putstr(" bytes at "); + unsignedlonglongToStrHex(addr, buf); + uart_putstr(buf); + uart_putstr(" \n\n"); +} +*/ +void object_allocator_request_page(struct object_allocator *self) +{ + int page = buddy_alloc(0); + + int thispage_Id = self->page_count; + self->max_pool[thispage_Id] = self->max_pool_init; + self->cur_pool[thispage_Id] = 0; + self->page[thispage_Id] = page; + self->page_count ++; +} + +struct object_allocator* object_allocator_init(int size) +{ + int thisId = object_allocator_gid; + object_allocator_gid++; + + obj_alloc_pool[thisId].page_count = 0; + obj_alloc_pool[thisId].obj_size = size; + obj_alloc_pool[thisId].max_pool_init = PAGE_SIZE / size; + + object_allocator_request_page(&obj_alloc_pool[thisId]); + + return &obj_alloc_pool[thisId]; +} + +unsigned long long obj_alloc(struct object_allocator *self) +{ + int pool_id = self->page_count-1; + + self->cur_pool[pool_id] += 1; + if (self->cur_pool[pool_id] > self->max_pool_init) + { + //uart_putstr("[Dynamic Allocator] over allocate momory\n"); + //uart_putstr("[Dynamic Allocator] use buddy system allocate 1 page\n\n"); + object_allocator_request_page(self); + unsigned long long retAddr = obj_alloc(self); + return retAddr; + } + + unsigned int offset = (self->cur_pool[pool_id] - 1) * self->obj_size; + + unsigned long long retAddr = self->page[pool_id] * PAGE_SIZE + offset; + retAddr = MEMORY_START +retAddr; + //show_allocator_alloc_message(retAddr, self->obj_size); + + return retAddr; +} + +int obj_free(struct object_allocator *self, unsigned long long phyAddr) +{ + int page = (phyAddr - MEMORY_START) >> 12; + + int page_id = -1; + for(int i = 0; i < self->page_count; i++) + { + if(self->page[i] == page) + { + page_id = i; + break; + } + } + + if(page_id == -1) + return 0; + + self->cur_pool[page_id] -= 1; + + //show_allocator_free_message(phyAddr, self->obj_size); + + return 1; +} + +struct dynamic_allocator* dynamic_allocator_init() +{ + int thisId = dynamic_allocator_gid; + dynamic_allocator_gid++; + + if (dynamic_allocator_gid > ALLOCATOR_POOL_SIZE) + { + //uart_putstr("[Dynamic Allocator] over max allocate size\n"); + return 0; + } + + // init fix size (64, 512, 2048, 4096) + dynamic_alloc_pool[thisId].obj_allocator_64 = object_allocator_init(64); + dynamic_alloc_pool[thisId].obj_allocator_512 = object_allocator_init(512); + dynamic_alloc_pool[thisId].obj_allocator_2048 = object_allocator_init(2048); + dynamic_alloc_pool[thisId].obj_allocator_4096 = object_allocator_init(4096); + + return &dynamic_alloc_pool[thisId]; +} + +unsigned long long dynamic_alloc(struct dynamic_allocator *self, int req_size) +{ + // 1. if size < obj_allocate, use fix allocate + unsigned long long retAddr; + if(req_size <= 64) + { + retAddr = obj_alloc(self->obj_allocator_64); + } + else if(req_size <= 512) + { + retAddr = obj_alloc(self->obj_allocator_512); + } + else if(req_size <= 2048) + { + retAddr = obj_alloc(self->obj_allocator_2048); + } + else if(req_size <= 4096) + { + retAddr = obj_alloc(self->obj_allocator_4096); + } + else + { + // 2. else use buddy system + //uart_putstr("[Dynamic Allocator] over page size 4096, use buddy system allocate...\n"); + int page = buddy_alloc(req_size); + retAddr = (unsigned long long)(MEMORY_START + page * PAGE_SIZE); + } + + return retAddr; +} + +void dynamic_free(struct dynamic_allocator *self, unsigned long long addr) +{ + if(obj_free(self->obj_allocator_64, addr)) + { + return; + } + else if(obj_free(self->obj_allocator_512, addr)) + { + return; + } + else if (obj_free(self->obj_allocator_2048, addr)) + { + return; + } + else if (obj_free(self->obj_allocator_4096, addr)) + { + return; + } + else + { + unsigned long long phyAddr = addr - MEMORY_START; + int page = phyAddr >> 12; + buddy_free(page); + } +} \ No newline at end of file diff --git a/lab8/kernel/allocator.h b/lab8/kernel/allocator.h new file mode 100644 index 000000000..a44f1c183 --- /dev/null +++ b/lab8/kernel/allocator.h @@ -0,0 +1,36 @@ +#ifndef ALLOCATOR_H +#define ALLOCATOR_H + +#define OBJ_ALLOCATOR_PAGE_SIZE 16 // pool allow allocate max page count +#define ALLOCATOR_POOL_SIZE 32 + +struct object_allocator +{ + unsigned int max_pool[OBJ_ALLOCATOR_PAGE_SIZE]; // record max obj slot count (page size / obj size) + unsigned int cur_pool[OBJ_ALLOCATOR_PAGE_SIZE]; // record current obj use slot count + unsigned int page[OBJ_ALLOCATOR_PAGE_SIZE]; // record page index + unsigned int page_count; // record page count + unsigned int obj_size; // obj size (ex. 64,512) + unsigned int max_pool_init; // initial max_pool +}; + +// has 4 different obj size +struct dynamic_allocator +{ + struct object_allocator *obj_allocator_64; + struct object_allocator *obj_allocator_512; + struct object_allocator *obj_allocator_2048; + struct object_allocator *obj_allocator_4096; +}; + +struct object_allocator* object_allocator_init(int size); +unsigned long long obj_alloc(struct object_allocator *self); +int obj_allocator_free(struct object_allocator *self, unsigned long long addr); + +struct dynamic_allocator* dynamic_allocator_init(); +unsigned long long dynamic_alloc(struct dynamic_allocator *self, int req_size); +void dynamic_free(struct dynamic_allocator *self, unsigned long long addr); +void dynamic_test(); + +void memory_init(); +#endif \ No newline at end of file diff --git a/lab8/kernel/auxilary.h b/lab8/kernel/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/kernel/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/kernel/buddy.c b/lab8/kernel/buddy.c new file mode 100644 index 000000000..bfad59f4a --- /dev/null +++ b/lab8/kernel/buddy.c @@ -0,0 +1,391 @@ +#include "util.h" +#include "buddy.h" +#include "uart.h" + +struct buddy_head buddy_list[BUDDY_MAX_ORDER + 1]; +struct buddy_allocated buddy_allocated_list; + +void buddy_test() +{ + buddy_init(); + + int PFN1, PFN2, PFN3, PFN4, PFN5; + + uart_putstr("================================== buddy system ==================================\n"); + uart_putstr("PFN1: allocate 32 page...\n"); + PFN1 = buddy_alloc(32 * PAGE_SIZE); + uart_putstr("PFN2: allocate 7 page...\n"); + PFN2 = buddy_alloc(7 * PAGE_SIZE); + uart_putstr("PFN3: allocate 64 page...\n"); + PFN3 = buddy_alloc(64 * PAGE_SIZE); + uart_putstr("Free PFN1...\n"); + buddy_free(PFN1); + uart_putstr("\nFree PFN2...\n"); + buddy_free(PFN2); + uart_putstr("\nPFN4: allocate 56 page...\n"); + PFN4 = buddy_alloc(56 * PAGE_SIZE); + uart_putstr("PFN5: allocate 61 page...\n"); + PFN5 = buddy_alloc(61 * PAGE_SIZE); + uart_putstr("Free PFN3...\n"); + buddy_free(PFN3); + uart_putstr("\nFree PFN4...\n"); + buddy_free(PFN4); + uart_putstr("\nFree PFN5...\n"); + buddy_free(PFN5); + uart_putstr("====================================\n"); +} +/* +void show_alloc_message(struct buddy_node node) +{ + char buf[16] = {0}; + + uart_putstr("[Buddy system] allocate memory page ( from "); + unsignedlonglongToStr(node.start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.end, buf); + uart_putstr(buf); + uart_putstr(" )\n"); + // + uart_putstr("[Buddy system] allocate memory address ( from "); + unsignedlonglongToStrHex(node.start * PAGE_SIZE + MEMORY_START, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStrHex((node.end + 1) * PAGE_SIZE + MEMORY_START, buf); + uart_putstr(buf); + uart_putstr(" )\n\n"); + +} + +void show_free_message(struct buddy_node node) +{ + char buf[16] = {0}; + + uart_putstr("[Buddy system] free memory page ( from "); + unsignedlonglongToStr(node.start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.end, buf); + uart_putstr(buf); + uart_putstr(" )\n"); +} +*/ +bool allocated_list_push(struct buddy_node node) +{ + if(buddy_allocated_list.count == BUDDY_ALLOCATED_NUM) + { + //uart_putstr("allocated array full\n"); + return false; + } + + // put node to allocated_list and show message + node.status = USED; + for(int i = 0; i < BUDDY_ALLOCATED_NUM; i++) + { + if(buddy_allocated_list.node_list[i].status == FREE) + { + buddy_allocated_list.node_list[i].start = node.start; + buddy_allocated_list.node_list[i].end = node.end; + buddy_allocated_list.node_list[i].status = node.status; + //show_alloc_message(node); + break; + } + } + + buddy_allocated_list.count++; + return true; +} + +struct buddy_node allocated_list_pop(int node_start) +{ + // search and pop node + struct buddy_node ret_node; + for(int i = 0; i < BUDDY_ALLOCATED_NUM; i++) + { + struct buddy_node *node = &buddy_allocated_list.node_list[i]; + if(node->status == USED && node->start == node_start) + { + node->status = FREE; + ret_node.start = buddy_allocated_list.node_list[i].start; + ret_node.end = buddy_allocated_list.node_list[i].end; + ret_node.status = buddy_allocated_list.node_list[i].status; + break; + } + } + + buddy_allocated_list.count--; + return ret_node; +} + +// pop first buddy_node from buddy list +struct buddy_node buddy_list_pop(struct buddy_head *list) +{ + // 1. get first node + struct buddy_node ret_node; + ret_node.start = list->node_list[0].start; + ret_node.end = list->node_list[0].end; + ret_node.status = list->node_list[0].status; + list->count -= 1; + + // 2. move other list node forward iteration + int count = list->count; + int cur = 1; + while(count != 0) + { + list->node_list[cur - 1].start = list->node_list[cur].start; + list->node_list[cur - 1].end = list->node_list[cur].end; + list->node_list[cur - 1].status = list->node_list[cur].status; + + count--; + cur++; + } + + // 3. init last node + list->node_list[cur - 1].start = list->node_list[cur - 1].end = 0; + list->node_list[cur - 1].status = FREE; + + return ret_node; +} + +// split node to two part +void buddy_list_push(struct buddy_head *list, struct buddy_node node) +{ + int size = (node.end - node.start + 1) / 2; + + // first half + list->node_list[list->count].start = node.start; + list->node_list[list->count].end = node.start + size - 1; + list->count += 1; + + // second half + list->node_list[list->count].start = node.start + size; + list->node_list[list->count].end = node.end; + list->count += 1; + + // print first half log + /* + char buf[16] = {0}; + uart_putstr("first half (from "); + unsignedlonglongToStr(node.start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.start + size - 1, buf); + uart_putstr(buf); + uart_putstr(" )\n"); + // print second half log + uart_putstr("second half (from "); + unsignedlonglongToStr(node.start + size, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node.end, buf); + uart_putstr(buf); + uart_putstr(" )\n"); + */ +} + +void buddy_merge(int order, struct buddy_node* node) +{ + struct buddy_node merge_node; + bool can_merge = false; + int merge_index; + int size = buddy_list[order].count; + + //char buf[16] = {0}; + + while(1) + { + can_merge = false; + size = buddy_list[order].count; + + // calc free node and list node can merge (continue memory) + for(merge_index = 0; merge_index < size; merge_index++) + { + if(buddy_list[order].node_list[merge_index].end + 1 == node->start) + { + merge_node.start = buddy_list[order].node_list[merge_index].start; + merge_node.end = node->end; + can_merge = true; + break; + } + + if(buddy_list[order].node_list[merge_index].start == node->end + 1) + { + merge_node.start = node->start; + merge_node.end = buddy_list[order].node_list[merge_index].end; + can_merge = true; + break; + } + } + + // if can merge, show message, and merge next level iteratively + if(can_merge) + { + /* + uart_putstr("[Buddy system] merge ( from "); + unsignedlonglongToStr(buddy_list[order].node_list[size - 1].start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(buddy_list[order].node_list[size - 1].end, buf); + uart_putstr(buf); + uart_putstr(" ) and ( from "); + unsignedlonglongToStr(node->start, buf); + uart_putstr(buf); + uart_putstr(" to "); + unsignedlonglongToStr(node->end, buf); + uart_putstr(buf); + uart_putstr(" )\n");*/ + + if(size - 1 == 0) + { + buddy_list[order].count = 0; + } + else + { + buddy_list[order].node_list[merge_index].start = buddy_list[order].node_list[size].start; + buddy_list[order].node_list[merge_index].end = buddy_list[order].node_list[size].end; + buddy_list[order].node_list[merge_index].status = buddy_list[order].node_list[size].status; + buddy_list[order].count-- ; + } + + if(order != BUDDY_MAX_ORDER) + { + //buddy_merge(order + 1, &merge_node); + node->start = merge_node.start; + node->end = merge_node.end; + order = order + 1; + } + } + else + { + // if can't merge, free node add to list + buddy_list[order].node_list[size].start = node->start; + buddy_list[order].node_list[size].end = node->end; + buddy_list[order].node_list[size].status = node->status; + buddy_list[order].count++; + break; + } + } +} + +void buddy_init() +{ + // init buddy_list + for(int i = 0; i < BUDDY_MAX_ORDER + 1; i++) + buddy_list[i].count = 0; + + // only the last buddy has free page 0-65535 + buddy_list[BUDDY_MAX_ORDER].count = 1; + buddy_list[BUDDY_MAX_ORDER].node_list[0].start = 0; + buddy_list[BUDDY_MAX_ORDER].node_list[0].end = PAGE_NUM - 1; + buddy_list[BUDDY_MAX_ORDER].node_list[0].status = FREE; + + // init buddy_allocated_list + buddy_allocated_list.count = 0; + for(int i = 0; i < BUDDY_ALLOCATED_NUM; i++) + (buddy_allocated_list.node_list[i]).status = FREE; + +} + +int buddy_alloc(int size) +{ + // 1. calc buddy order for allocate size + // ex. size = 8192, order = 1 + int order = 0; + while(1) + { + if (size <= PAGE_SIZE * (1 << order)) + break; + + order++; + } + if (order > BUDDY_MAX_ORDER) + { + //uart_putstr("allocate memory overd BUDDY_MAX_ORDER"); + return -1; + } + + /* + char buf[16] = {0}; + uart_putstr("order is "); + unsignedlonglongToStr(order, buf); + uart_putstr(buf); + uart_putstr("\n"); + */ + + // 2. if has free buddy, allocate and return + if(buddy_list[order].count > 0) + { + //uart_putstr("buddy_list[order] has free page frame node.. \n"); + struct buddy_node alloc_node = buddy_list_pop(&buddy_list[order]); + if(allocated_list_push(alloc_node)) + return alloc_node.start; + else + return -1; + } + + //uart_putstr("buddy_list[order] no free node.. \n"); + // 3. if not, search high level buddy and spilt buddy / 2 + int new_order = order + 1; + while(order < BUDDY_MAX_ORDER + 1) + { + if(buddy_list[new_order].count > 0) + break; + + new_order++; + } + if(new_order == BUDDY_MAX_ORDER + 1) + { + //uart_putstr("fail to allocate memory\n"); + return -1; + } + /* + uart_putstr("has free in order "); + unsignedlonglongToStr(new_order, buf); + uart_putstr(buf); + uart_putstr("\n"); + + uart_putstr("pop to next level\n");*/ + struct buddy_node temp_node = buddy_list_pop(&buddy_list[new_order]); + new_order--; + + //uart_putstr("start release redundant memory block...\n\n"); + // 3.1 spilt up until allocate size + while(new_order >= order) + { + // split node to two part: + /* + uart_putstr("split node to two part:"); + uart_putstr("order="); + unsignedlonglongToStr(new_order, buf); + uart_putstr(buf); + uart_putstr("\n");*/ + + buddy_list_push(&buddy_list[new_order], temp_node); + /* + if (new_order > order) + uart_putstr("pop first part to next level\n\n"); + else + uart_putstr("pop first part\n\n");*/ + // pop first part to next level + temp_node = buddy_list_pop(&buddy_list[new_order]); + + new_order--; + } + + // 4. save to allocated_list and return address + if (allocated_list_push(temp_node)) + return temp_node.start; + else + return -1; +} + +void buddy_free(int node_start) +{ + struct buddy_node tmpNode = allocated_list_pop(node_start); + //show_free_message(tmpNode); + + int size = tmpNode.end - tmpNode.start + 1; + int order = log2(size); + + buddy_merge(order, &tmpNode); +} \ No newline at end of file diff --git a/lab8/kernel/buddy.h b/lab8/kernel/buddy.h new file mode 100644 index 000000000..4a382c4e9 --- /dev/null +++ b/lab8/kernel/buddy.h @@ -0,0 +1,49 @@ +#ifndef BUDDY_H +#define BUDDY_H + +#include "mmu.h" + +typedef int bool; +#define true 1 +#define false 0 + +#define MEMORY_START (0x10000000 + KVA) +#define MEMORY_SIZE 0x10000000 // from TA, 0x10000000-0x20000000 +#define PAGE_SIZE 0x1000 // 4096 +#define PAGE_NUM (MEMORY_SIZE / PAGE_SIZE) // all page frame = 65536 +#define BUDDY_MAX_ORDER 16 // 2^0 ~ 2^16 +#define BUDDY_ALLOCATED_NUM 65536 +#define BUDDY_NODE_LIST_NUM 16 + +enum Status +{ + FREE, + USED +}; + +struct buddy_node +{ + int start; + int end; + enum Status status; +}; + +struct buddy_head +{ + int count; + struct buddy_node node_list[BUDDY_NODE_LIST_NUM]; +}; + +struct buddy_allocated +{ + int count; + struct buddy_node node_list[BUDDY_ALLOCATED_NUM]; +}; + + +void buddy_init(); +int buddy_alloc(int size); +void buddy_free(int node_start); +void buddy_test(); + +#endif \ No newline at end of file diff --git a/lab8/kernel/context_switch.s b/lab8/kernel/context_switch.s new file mode 100644 index 000000000..b3765f205 --- /dev/null +++ b/lab8/kernel/context_switch.s @@ -0,0 +1,58 @@ +/* + switch_to (prev, next), x0:prev thread, x1:next thread + 1. 將 x19-x28, fp, lr, sp 暫存器內容儲存到 prev thread + 2. 將 next thread的內容給 x19-x28, fp, lr, sp 暫存器 + 3. 設定 x1(next) 為目前執行的thread + + x29 FP The Frame Pointer. fp儲存stack的底端 + x30 LR The Link Register. lr儲存function的連結位址 + SP The Stack Pointer. sp儲存stack的頂端 + + stp x19, x20, [x0, 16 * 0] // 將x19, x20的內容存到x0偏移16*0的位置 + mov x9, sp // 將sp內容給x9 + str x9, [x0, 16 * 6] // 將x9的內容存到x0偏移16*6的位置 + + ldp x19, x20, [x1, 16 * 0] // 將x1偏移16*0位置的內容給x19, x20 + ldr x9, [x1, 16 * 6] // 將x1偏移16*6位置的內容給x9 + mov sp, x9 // 將x9內容給sp +*/ + +.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 + +/* + Get the current thread from tpidr_el1 register +*/ + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +/* + set the current thread (tpidr_el1 register) +*/ + +.global set_current +set_current: + msr tpidr_el1, x0 + ret \ No newline at end of file diff --git a/lab8/kernel/core_timer.s b/lab8/kernel/core_timer.s new file mode 100644 index 000000000..808bfb93c --- /dev/null +++ b/lab8/kernel/core_timer.s @@ -0,0 +1,37 @@ +/* + core_timer_enable + + cntp_ctl_el0: Physical Timer Control register. + 1: enable, 0: disable + + 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. + cntfrq_el0: the frequency of timer + + 1. set cntp_ctl_el0 enable + 2. set cntp_tval_el0 is cntfrq_el0 (i.e. expired time is cntfrq_el0) + 3. unmask the timer interrupt from the first level interrupt controller +*/ + +.equ CORE0_TIMER_IRQ_CTRL, (0x40000040 + 0xffff000000000000) + +.globl core_timer_enable +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 + ret + +.globl core_timer_disable +core_timer_disable: + mov x0, 0 + msr cntp_ctl_el0, x0 // disable + mov x0, 0 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret diff --git a/lab8/kernel/cpio.c b/lab8/kernel/cpio.c new file mode 100644 index 000000000..b642dfc30 --- /dev/null +++ b/lab8/kernel/cpio.c @@ -0,0 +1,268 @@ +#include "cpio.h" +#include "uart.h" +#include "util.h" +#include "timer.h" +#include "vfs.h" +#include "mmu.h" + +#define CPIO_BASE 0x8000000 + KVA + +//https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 +//The pathname is followed by NUL bytes so that the total size of the fixed +// header plus pathname is a multiple of four. Likewise, the file data is +// padded to a multiple of four bytes. Note that this format supports only +// 4 gigabyte files (unlike the older ASCII format, which supports 8 giga- +// byte files). +unsigned long long align_to_4(unsigned long long size) +{ + unsigned long long residual = size % 4; + + if(residual > 0) + return size + (4 - residual); + else + return size; +} + +// list rootfs file +void cpio_list() +{ + //unsigned long int ptr = 0x20000000; + unsigned long long ptr = CPIO_BASE; + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if(namesize > 0) + { + uart_putstr(fileName); + uart_putstr(" "); + } + + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } +} + +// get file content +char* cpio_content(char* name) +{ + //unsigned long int ptr = 0x20000000; + unsigned long long ptr = CPIO_BASE; + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return 0; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return 0; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if (strcmp(fileName, name)) + { + if (filesize > 0) + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + return (char*)ptr; + } + + return 0; + } + else + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } + } +} + +// required 1-2 Add a command that can load a user program in the initramfs. Then, use eret to jump to the start address. +void cpio_run_user_program(char* name, int enable_timer) +{ + unsigned long long ptr = CPIO_BASE; // cpio address + unsigned long long loadAddr = 0x4000000 + KVA; + + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if (strcmp(fileName, name)) + { + char buf[16] = {0}; + unsignedlonglongToStr(filesize, buf); + uart_putstr(buf); + uart_putstr("\n"); + + if (filesize > 0) + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + + uart_putstr("start load user program...\n"); + + // load user program + char* target = (char*)loadAddr; + char* cpio = (char*)ptr; + for (int i = 0; i < filesize; i++) + { + *target = *cpio; + target++; + cpio++; + } + + uart_putstr("complete...\n"); + + // required 1-2, EL1 to EL0 + // 1. load a user program in the initramfs to a specific address + // 2. set spsr_el1 to 0x3c0 and elr_el1 to the program’s start address. + // 3. set the user program’s stack pointer to a proper position by setting sp_el0. + // 4. issue eret to return to the user code. + + if(enable_timer) + { + core_timer_enable(); + asm volatile("mov x0, 0x0 \n\t"); // required 2, enable interrupt + } + else + { + asm volatile("mov x0, 0x3c0 \n\t"); // required 1-2, disable interrupt + } + + asm volatile("msr spsr_el1, x0 \n\t"); + asm volatile("msr elr_el1, %0 \n\t"::"r"(loadAddr)); + asm volatile("msr sp_el0, %0 \n\t"::"r"(loadAddr)); + asm volatile("eret \n"); + + break; + } + } + else + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } + } +} + +int cpio_load_user_program_and_get_size(char* name, unsigned long long loadAddr) +{ + unsigned long long ptr = CPIO_BASE; + struct cpio_newc_header *header; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return 0; + + char* fileName = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(fileName, CPIO_TRAILER, 10)) + return 0; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + if (strcmp(fileName, name)) + { + if (filesize > 0) + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + + // load user program + char* target = (char*)loadAddr; + char* user_progame = (char*)ptr; + for (int i = 0; i < filesize; i++) + { + *target = *user_progame; + target++; + user_progame++; + } + + return filesize; + } + + return 0; + } + else + { + ptr = (unsigned long long)fileName; + ptr = align_to_4(ptr + namesize); + ptr = align_to_4(ptr + filesize); + } + } +} + +void cpio_populate_rootfs() +{ + uart_putstr("===== Populate the root file system with initramfs =====\n"); + unsigned long long ptr = CPIO_BASE; + struct cpio_newc_header *header; + char buf[16] = {0}; + + while (1) + { + header = (struct cpio_newc_header *)ptr; + if (strcmpn(header->c_magic, CPIO_MAGIC, 6)) + return; + + char* pathname = (char*)(ptr + sizeof(struct cpio_newc_header)); + if (strcmpn(pathname, CPIO_TRAILER, 10)) + return; + + unsigned long namesize = hex2int(header->c_namesize, 8); + unsigned long filesize = hex2int(header->c_filesize, 8); + + uart_putstr("pathname: "); + uart_putstr(pathname); + uart_putstr(", filesize: "); + unsignedlonglongToStr(filesize, buf); + uart_putstr(buf); + uart_putstr("\n"); + + ptr = (unsigned long long)pathname; + ptr = align_to_4(ptr + namesize); + + if (filesize > 0) + { + struct file *file = vfs_open(pathname, O_CREAT); + if (file) + { + vfs_write(file, (const char *)ptr, filesize); + vfs_close(file); + } + } + ptr = align_to_4(ptr + filesize); + } +} \ No newline at end of file diff --git a/lab8/kernel/cpio.h b/lab8/kernel/cpio.h new file mode 100644 index 000000000..5841cb54d --- /dev/null +++ b/lab8/kernel/cpio.h @@ -0,0 +1,34 @@ +#ifndef CPIOH +#define CPIOH + +// https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 + +#define CPIO_MAGIC "070701" // cpio magic number +#define CPIO_TRAILER "TRAILER!!!" // cpio end string + +struct cpio_newc_header +{ + char c_magic[6]; // magic number "070701" + char c_ino[8]; // "i-node" number + char c_mode[8]; // file mode + char c_uid[8]; // user ID + char c_gid[8]; // group ID + char c_nlink[8]; // number of hard links + char c_mtime[8]; // modification time + char c_filesize[8]; // file size + char c_devmajor[8]; // major device number + char c_devminor[8]; // minor device number + char c_rdevmajor[8]; // only valid for chr and blk special files + char c_rdevminor[8]; // only valid for chr and blk special files + char c_namesize[8]; // length of name in bytes ( include '\0') + char c_check[8]; // checksum +}; + +unsigned long long align_to_4(unsigned long long size); // align to 4 byte +void cpio_list(); // list rootfs file +char* cpio_content(char* name); // get file content +void cpio_run_user_program(char* name, int enable_timer); +int cpio_load_user_program_and_get_size(char* name, unsigned long long loadAddr); +void cpio_populate_rootfs(); + +#endif diff --git a/lab8/kernel/devicetree.c b/lab8/kernel/devicetree.c new file mode 100644 index 000000000..6606497c6 --- /dev/null +++ b/lab8/kernel/devicetree.c @@ -0,0 +1,183 @@ +#include "devicetree.h" +#include "uart.h" +#include "util.h" + +unsigned long int align4(unsigned long int size) +{ + unsigned long int residual = size % 4; + + if(residual > 0) + return size + (4 - residual); + else + return size; +} + +unsigned int convert_bigendian(void *ptr) +{ + // ex. 1234 -> 4321 + + unsigned char *bytes = (unsigned char *)ptr; + + unsigned int value = bytes[3]; + value |= bytes[2] << 8; + value |= bytes[1] << 16; + value |= bytes[0] << 24; + + return value; +} + +void dtb_ls() +{ + unsigned long int dtb = *((unsigned long int*)DTB_ADDR); + struct fdt_header *header = (struct fdt_header *)dtb; + + if (convert_bigendian(&header->magic) != (unsigned long int)DTB_MAGIC) + { + uart_putstr("Format error !"); + return; + } + + return parse_node(header, dtb); +} + +void print_indent(int level) +{ + while(level > 0) + { + uart_putstr("-"); + level--; + } +} + +void parse_node(struct fdt_header *header, unsigned long int dtb) +{ + unsigned long int ptr = dtb + convert_bigendian(&header->off_dt_struct); + unsigned long int endptr = ptr + convert_bigendian(&header->totalsize); + int level = 0; + + while (ptr < endptr) + { + unsigned int tag = convert_bigendian((char *)ptr); + ptr += sizeof(unsigned int); + + switch (tag) + { + case FDT_BEGIN_NODE: // Start node + + // struct fdt_node_header + // { + // fdt32_t tag; => 4 byte + // char name[0]; => length + '\0' + // }; + print_indent(level); + uart_putstr((char *)ptr); + uart_putstr("\n"); + level++; + + ptr += align4(strlen((char *)ptr) + 1); // length + '\0' + break; + + case FDT_END_NODE: // End node + level--; + break; + + case FDT_NOP: // Nop + break; + + case FDT_PROP: // Property + { + // struct fdt_property + // { + // unsigned int tag; => 4 byte + // unsigned int len; => 4 byte + // unsigned int nameoff; => 4 byte + // char data[0]; => len + // }; + + unsigned int len = convert_bigendian((char *)ptr); // data length + ptr += sizeof(unsigned int); // nameoff + ptr += sizeof(unsigned int); // data + ptr += align4(len); // next node + break; + } + + case FDT_END: // Device tree end + break; + } + } +} + +int dtb_cat(char* name, dtb_callback cb) +{ + unsigned long int dtb = *((unsigned long int*)DTB_ADDR); + struct fdt_header *header = (struct fdt_header *)dtb; + + if (convert_bigendian(&header->magic) != (unsigned long int)DTB_MAGIC) + { + uart_putstr("Format error !"); + return -1; + } + + return parse_and_callback(header, dtb, name, cb); +} + +int parse_and_callback(struct fdt_header *header, unsigned long int dtb, char* name, dtb_callback cb) +{ + unsigned long int ptr = dtb + convert_bigendian(&header->off_dt_struct); + unsigned long int endptr = ptr + convert_bigendian(&header->totalsize); + unsigned long int strptr = dtb + convert_bigendian(&header->off_dt_strings); + + while (ptr < endptr) + { + unsigned int tag = convert_bigendian((char *)ptr); + ptr += sizeof(unsigned int); + + switch (tag) + { + case FDT_BEGIN_NODE: // Start node + + // struct fdt_node_header + // { + // fdt32_t tag; => 4 byte + // char name[0]; => length + '\0' + // }; + if (strcmp(name, (char *)ptr)) + { + // if node name is search name , excute callback + cb(ptr, strptr); + return 0; + } + + ptr += align4(strlen((char *)ptr) + 1); // length + '\0' + break; + + case FDT_END_NODE: // End node + break; + + case FDT_NOP: // Nop + break; + + case FDT_PROP: // Property + { + // struct fdt_property + // { + // unsigned int tag; => 4 byte + // unsigned int len; => 4 byte + // unsigned int nameoff; => 4 byte + // char data[0]; => len + // }; + + unsigned int len = convert_bigendian((char *)ptr); // data length + ptr += sizeof(unsigned int); // nameoff + ptr += sizeof(unsigned int); // data + ptr += align4(len); // next node + break; + } + + case FDT_END: // Device tree end + break; + } + } + + return -1; +} \ No newline at end of file diff --git a/lab8/kernel/devicetree.h b/lab8/kernel/devicetree.h new file mode 100644 index 000000000..d814b888f --- /dev/null +++ b/lab8/kernel/devicetree.h @@ -0,0 +1,39 @@ +#ifndef DEVICETREEH +#define DEVICETREEH + +#include "mmu.h" + +#define DTB_ADDR 0x9000000 // dtb start address +#define DTB_MAGIC 0xd00dfeed // magic value + +#define FDT_BEGIN_NODE 1 // start node +#define FDT_END_NODE 2 // end node +#define FDT_PROP 3 // property +#define FDT_NOP 4 // nop +#define FDT_END 9 // dtb end + +struct fdt_header +{ + unsigned int magic; // magic word FDT_MAGIC + unsigned int totalsize; // total size of DT block + unsigned int off_dt_struct; // offset to structure + unsigned int off_dt_strings; // offset to strings + unsigned int off_mem_rsvmap; // offset to memory reserve map + unsigned int version; // format version (17) + unsigned int last_comp_version; // last compatible version + unsigned int boot_cpuid_phys; // Which physical CPU id we're booting on + unsigned int size_dt_strings; // size of the strings block + unsigned int size_dt_struct; // size of the structure block +}; + +typedef void (*dtb_callback)(unsigned long int ptr, unsigned long int strptr); + +unsigned long int align4(unsigned long int size); +unsigned int convert_bigendian(void *ptr); // convert char to bigendian +void dtb_ls(); // list rootfs file +void parse_node(struct fdt_header *header, unsigned long int dtb); +int dtb_cat(char* name, dtb_callback cb); // get file content +int parse_and_callback(struct fdt_header *header, unsigned long int dtb, char* name, dtb_callback cb); +void print_indent(int level); + +#endif diff --git a/lab8/kernel/exception.c b/lab8/kernel/exception.c new file mode 100644 index 000000000..7620dcc75 --- /dev/null +++ b/lab8/kernel/exception.c @@ -0,0 +1,333 @@ +#include "exception.h" +#include "uart.h" +#include "util.h" +#include "timer.h" +#include "thread.h" + +// http://www.zencoder.info/2020/01/15/get-current-el-in-arm64/ +/* +* show current exception level +* +* use "mrs x CurrentEL" get current exception level and put on x +* +* CurrentEL: register for get current exception level +* %0 : output operand +* "=r" (current_el): output to current_el +* ": : :"memory" : gcc compiler fence +* +* 要右移2,請參見http://www.lujun.org.cn/?p=1676 +* +* 4 3 2 1 0 +* --------------------- +* | | EL | | | +* --------------------- +* +* 因為current_el在第3第2位,所以要右移2才是我們要的值 +*/ +void show_current_el() +{ + unsigned long long current_el; + asm volatile("mrs %0, CurrentEL\n\t" : "=r" (current_el) : : "memory"); + + char buf[16] = {0}; + uart_putstr("currentEL is "); + unsignedlonglongToStr(current_el >> 2, buf); + uart_putstr(buf); +} + +/* +* exception_entry +* +* The method use for synchronous excpetion handle +* +* In required 1-3 +* Only needs to print the content of [spsr_el1], [elr_el1], and [esr_el1] in the exception handler +* +* [spsr_el1]: current processor’s state +* [elr_el1]: the exception return address +* [esr_ell]: if exception is synchronous exception or an an SError interrupt, save cause of that exception +* +* note: if core timer is enable in requirement 2, we don't show exception info message +*/ +void exception_entry() +{ + // if core timer enable, not show message + + unsigned long long cntp_ctl_el0; + asm volatile("mrs %0, cntp_ctl_el0" : "=r"(cntp_ctl_el0)); + if(cntp_ctl_el0 != 0) + return; + + unsigned long long spsr_el1, elr_el1, esr_el1; + + asm volatile("mrs %0, spsr_el1" : "=r"(spsr_el1)); + asm volatile("mrs %0, elr_el1" : "=r"(elr_el1)); + asm volatile("mrs %0, esr_el1" : "=r"(esr_el1)); + + char buf[16] = {0}; + uart_putstr("print exception info: \n"); + uart_putstr("SPSR_EL1: "); + unsignedlonglongToStrHex(spsr_el1, buf); + uart_putstr(buf); + uart_putstr("\n"); + uart_putstr("ELR_EL1: "); + unsignedlonglongToStrHex(elr_el1, buf); + uart_putstr(buf); + uart_putstr("\n"); + uart_putstr("ESR_EL1: "); + unsignedlonglongToStrHex(esr_el1, buf); + uart_putstr(buf); + uart_putstr("\n\n"); +} + +void set_x0(unsigned long val) +{ + unsigned long* task; + asm volatile("mrs %0, tpidr_el1 \n":"=r"(task):); + task[16] = val; +} + +/* +* lowerEL_sync_interrupt +* +* 1.讀取user stack的傳遞的參數 +* - 將user stack的參數放入x0跟x1暫存器,x0的內容放到sp-8的位置,x1的內容放到sp-16的位置 +* - 再將x0跟x1暫存器的內容讀出,將sp-8的內容給x0變數,sp-16的內容給x1變數 +* +* 2. 取出svc的值,依據不同的值做不同處理 +*/ +void lowerEL_sync_interrupt() +{ + // 1. 讀取user stack的傳遞的參數 + unsigned long x0,x1,x2; + + asm volatile("\ + str x0,[sp,-8]\n\ + str x1,[sp,-16]\n\ + str x2,[sp,-24]\n\ + "::); + + asm volatile("\ + ldr %0,[sp,-8]\n\ + ldr %1,[sp,-16]\n\ + ldr %2,[sp,-24]\n\ + ":"=r"(x0),"=r"(x1),"=r"(x2):); + + // 2. 取出svc的值,依據不同的值做不同處理 + unsigned long esr,svc; + asm volatile("mrs %0, esr_el1\n":"=r"(esr):); // 讀取esr_el1的值 + + // https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1- + // 判斷EC, bits [31:26],所以先右移26 bits + // 假如為0b010101,為svc的值 + if((esr >> 26) == 0b010101) // SVC instruction execution in AArch64 state. + { + svc = esr & 0x0ffff; + + if(svc == 0) + { + uart_putstr("svc 0\n"); + } + else if (svc == 1) // get pid + { + unsigned long pid = get_pid(); + set_x0(pid); + } + else if (svc == 2) // uart read + { + unsigned long ret = uart_gets((char*)x0,(int)x1); + set_x0(ret); + } + else if (svc == 3) // uart write + { + uart_putstr((char*)x0); + set_x0(x1); + } + else if (svc == 4) // exec + { + exec((const char*)x0, (char(*)[10])x1); + set_x0(0); + } + else if (svc == 5) // exit + { + exit(); + } + else if (svc == 6) // fork + { + int pid = fork(); + set_x0(pid); + } + else if (svc == 7) // open + { + struct file *file = vfs_open((char *)x0, (int)x1); + int fd = thread_register_fd(file); + set_x0(fd); + } + else if (svc == 8) // close + { + struct file *file = thread_get_file((int)x0); + thread_clear_fd((int)x0); + int result = vfs_close(file); + set_x0(result); + } + else if (svc == 9) // write + { + struct file *file = thread_get_file((int)x0); + const void *buf = (const void *)x1; + unsigned int len = (unsigned int)x2; + unsigned int size = vfs_write(file, buf, len); + set_x0(size); + } + else if (svc == 10) // read + { + struct file *file = thread_get_file((int)x0); + void *buf = (void *)x1; + unsigned int len = (unsigned int)x2; + unsigned int size = vfs_read(file, buf, len); + set_x0(size); + } + else if (svc == 11) // list + { + struct file *file = thread_get_file((int)x0); + void *buf = (void *)x1; + int index = (int)x2; + unsigned int size = vfs_list(file, buf, index); + set_x0(size); + } + else if (svc == 12) // mkdir + { + int result = vfs_mkdir((const char *)x0); + set_x0(result); + } + else if (svc == 13) // chdir + { + int result = vfs_chdir((const char *)x0); + set_x0(result); + } + else if (svc == 14) // mount + { + const char *device = (const char *)x0; + const char *mountpoint = (const char *)x1; + const char *filesystem = (const char *)x2; + int result = vfs_mount(device, mountpoint, filesystem); + set_x0(result); + } + else if (svc == 15) // unmount + { + int result = vfs_umount((const char *)x0); + set_x0(result); + } + } + else if ((esr >> 26) == 0b100100) + { + // 頁面錯誤處理 + + // 印出故障地址 + unsigned long far; + asm volatile("mrs %0, far_el1" : "=r"(far)); + + char buf[16] = {0}; + uart_putstr("[Data Abort] pid: "); + unsignedlonglongToStr(get_current()->tid, buf); + uart_putstr(buf); + uart_putstr(", far_el1: "); + unsignedlonglongToStrHex(far, buf); + uart_putstr(buf); + uart_putstr("\n"); + // 終止目前process + exit(); + } +} + +/* +* no_exception_handle +* +* The method use for not implement excpetion handle +* +* From TA, +* In this lab, we only focus on Synchronous and IRQ exceptions. +* +* and only handle exception vector table's +* 1. Exception from the currentEL while using SP_ELx +* 2. Exception from a lower EL and at least one lower EL is AARCH64. +* +* so, other exception or other table in exception vector table, we don't implement +*/ +void no_exception_handle() +{ + +} + +/* +* lowerEL_irq_interrupt +* +* The method use for lowerEL irq interrupt handle +* 1. distinguish interrupt source : core timer or uart +* 2. use different exception_handle handle different interrupt +* +* irq : https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf +* p.16 +* Core0 interrupt source & CNTHPIRQ interrupt +* +* critical section +* 3. before exception_handle, disable interrupt +* 4. after exception_handle, enable interrupt +* +*/ +void lowerEL_irq_interrupt() +{ + disable_interrupt(); + + unsigned int core_irq = (*CORE0_IRQ_SOURCE & (1 << 1)); + unsigned int uart_irq = (*IRQ_PENDING_1 & AUX_IRQ); + + // ARM core timer irq interrupt + if(core_irq) + core_timer_handle(); + // uart irq interrupt + else if(uart_irq) + uart_interrupt_handler(); + + enable_interrupt(); +} + +void currentEL_irq_interrupt() +{ + disable_interrupt(); + + unsigned int core_irq = (*CORE0_IRQ_SOURCE & (1 << 1)); + unsigned int uart_irq = (*IRQ_PENDING_1 & AUX_IRQ); + + // ARM core timer irq interrupt + if(core_irq) + timout_handle(); + // uart irq interrupt + else if(uart_irq) + uart_interrupt_handler(); + + enable_interrupt(); +} + +/* +* enable_interrupt +* +* DAIFClr register : enable D,A,I,F +* 0xf = 1111 , represent D,A,I,F enable all +* +*/ +void enable_interrupt() +{ + asm volatile("msr DAIFClr, 0xf"); +} + +/* +* disable_interrupt +* +* DAIFSet register : disable D,A,I,F +* 0xf = 1111 , represent D,A,I,F disable all +* +*/ +void disable_interrupt() +{ + asm volatile("msr DAIFSet, 0xf"); +} \ No newline at end of file diff --git a/lab8/kernel/exception.h b/lab8/kernel/exception.h new file mode 100644 index 000000000..67842ce69 --- /dev/null +++ b/lab8/kernel/exception.h @@ -0,0 +1,21 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include "mmu.h" + +// https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf + +// cpu 0 irq interrupt source +#define CORE0_IRQ_SOURCE ((volatile unsigned int *)(0x40000060 + KVA)) + +void show_current_el(); +void exception_entry(); +void lowerEL_sync_interrupt(); +void no_exception_handle(); +void lowerEL_irq_interrupt(); +void currentEL_irq_interrupt(); + +void enable_interrupt(); +void disable_interrupt(); + +#endif \ No newline at end of file diff --git a/lab8/kernel/exception_handle.S b/lab8/kernel/exception_handle.S new file mode 100644 index 000000000..967250f62 --- /dev/null +++ b/lab8/kernel/exception_handle.S @@ -0,0 +1,264 @@ + +// required 1-3 set exception_vector_table + +.align 11 // vector table should be aligned to 0x800 = 4096 = 2^11 +.global exception_vector_table +exception_vector_table: + + // Exception from current EL while using SP_EL0 + + b exception_no_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0, 0x80 = 2^7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + + // Exception from current EL while using SP_ELX (ex. EL1 -> EL1) + // only handler Sync & irq + + b currentEL_sync_handler + .align 7 + b currentEL_irq_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + + // Exception from a lower and at least one lower EL is AArch64 (ex. EL0 -> EL1) + // only handler Sync & irq + + b lowerEL_sync_handler + .align 7 + b lowerEL_irq_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + + // Exception from a lower and all lower ELs are AArch32 + + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + b exception_no_handler + .align 7 + +// required 1-4 Save the user program’s context before executing the exception handler. + +// 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 + +/* + 參考thread的context,包含 + unsigned long x19; (8*0) + unsigned long x20; (8*1) + unsigned long x21; (8*2) + unsigned long x22; (8*3) + unsigned long x23; (8*4) + unsigned long x24; (8*5) + unsigned long x25; (8*6) + unsigned long x26; (8*7) + unsigned long x27; (8*8) + unsigned long x28; (8*9) + unsigned long fp; (8*10) + unsigned long lr; (8*11) + unsigned long sp; (8*12) + + // for user program (spsr_el1, elr_el1, sp_el0)(user register x0-x30) + unsigned long spsr_el1; (8*13) + unsigned long elr_el1; (8*14) + unsigned long sp_el0; (8*15) + unsigned long reg[31]; // user register x0-x30 (8*16)-(8*46) + + 儲存task + 也就是將user register x0-x30還有spsr_el1, elr_el1, sp_el0等狀態都存到目前的thread的context內 + + 步驟有點多,可以略過 + 1. str x0, [sp, -8] 先將x0內容存放在sp-8的位置 + 2. mrs x0, tpidr_el1 然後x0放目前的thread + 3. stp x2, x3, [x0, 8 * 18] 將x2, x3的內容存到x0偏移8*18的位置,參考上面的context + reg[2]是在8*18,reg[3]是8*19,其餘以此類推依序將值塞入對應位置 + 4. str x30, [x0, 8 * 46],將x30的內容存到x0偏移8*46的位置 + 5. mov x9, x0,x0的內容給x9 + 6. ldr x0, [sp, -8] 將sp-8位置的內容給x0 + 7. stp x0, x1, [x9 ,8 * 16] 剛剛的x0跟x1,現在才放 + 8. mrs x10, spsr_el1,將spsr_el1的內容給x10 + 9. mrs x11, elr_el1,將elr_el1給x11 + 10.mrs x12, sp_el0,將sp_el0給x12 + 11.str x10, [x9, 8 * 13],將將x10的內容存到x9偏移8*13的位置 (就是剛剛的x0) + 12.stp x11, x12, [x9, 8 * 14],將將x11 x12的內容存到x9偏移8*14的位置 (就是剛剛的x0) + +*/ + +// save general registers to stack +.macro save_task + str x0, [sp, -8] + mrs x0, tpidr_el1 + //store x0&x1 later + stp x2, x3, [x0, 8 * 18] + stp x4, x5, [x0, 8 * 20] + stp x6, x7, [x0, 8 * 22] + stp x8, x9, [x0, 8 * 24] + stp x10, x11, [x0, 8 * 26] + stp x12, x13, [x0, 8 * 28] + stp x14, x15, [x0, 8 * 30] + stp x16, x17, [x0, 8 * 32] + stp x18, x19, [x0, 8 * 34] + stp x20, x21, [x0, 8 * 36] + stp x22, x23, [x0, 8 * 38] + stp x24, x25, [x0, 8 * 40] + stp x26, x27, [x0, 8 * 42] + stp x28, x29, [x0, 8 * 44] + str x30, [x0, 8 * 46] + mov x9, x0 + ldr x0, [sp, -8] + stp x0, x1, [x9 ,8 * 16] + mrs x10, spsr_el1 + mrs x11, elr_el1 + mrs x12, sp_el0 + str x10, [x9, 8 * 13] + stp x11, x12, [x9, 8 * 14] +.endm + +/* + 參考thread的context,包含 + unsigned long x19; (8*0) + unsigned long x20; (8*1) + unsigned long x21; (8*2) + unsigned long x22; (8*3) + unsigned long x23; (8*4) + unsigned long x24; (8*5) + unsigned long x25; (8*6) + unsigned long x26; (8*7) + unsigned long x27; (8*8) + unsigned long x28; (8*9) + unsigned long fp; (8*10) + unsigned long lr; (8*11) + unsigned long sp; (8*12) + + // for user program (spsr_el1, elr_el1, sp_el0)(user register x0-x30) + unsigned long spsr_el1; (8*13) + unsigned long elr_el1; (8*14) + unsigned long sp_el0; (8*15) + unsigned long reg[31]; // user register x0-x30 (8*16)-(8*46) + + 還原task + 也就是將由目前的thread的context還原至user register x0-x30還有spsr_el1, elr_el1, sp_el0暫存器 + + 步驟有點多,可以略過 + 1. mrs x9, tpidr_el1 先將x9放目前的thread + 2. ldr x10, [x9, 8 * 13] 將x9偏移8*13位置的內容給x10 + 3. ldp x11, x12, [x9, 8 * 14] 將x9偏移8*14 8*15位置的內容給x11跟x12 + 4. msr spsr_el1, x10 ,x10的內容給spsr_el1 + 5. msr elr_el1, x11,x11的內容給elr_el1 + 6. msr sp_el0, x12,x12的內容給sp_el0 + 7. mov x0, x9, 把x9的內容給x0 (就是目前thread) + 8. ldp x2, x3, [x0, 8 * 18],將x0偏移8*18位置的內容給x2, x3 + 後面以此類推,依序將值塞入對應位置 + +*/ + +// load general registers from stack +.macro restore_task + mrs x9, tpidr_el1 + ldr x10, [x9, 8 * 13] + ldp x11, x12, [x9, 8 * 14] + msr spsr_el1, x10 + msr elr_el1, x11 + msr sp_el0, x12 + mov x0, x9 + //restore x0&x1 later + ldp x2, x3, [x0, 8 * 18] + ldp x4, x5, [x0, 8 * 20] + ldp x6, x7, [x0, 8 * 22] + ldp x8, x9, [x0, 8 * 24] + ldp x10, x11, [x0, 8 * 26] + ldp x12, x13, [x0, 8 * 28] + ldp x14, x15, [x0, 8 * 30] + ldp x16, x17, [x0, 8 * 32] + ldp x18, x19, [x0, 8 * 34] + ldp x20, x21, [x0, 8 * 36] + ldp x22, x23, [x0, 8 * 38] + ldp x24, x25, [x0, 8 * 40] + ldp x26, x27, [x0, 8 * 42] + ldp x28, x29, [x0, 8 * 44] + ldr x30, [x0, 8 * 46] + ldp x0, x1, [x0, 8 * 16] +.endm + +lowerEL_sync_handler: + save_task + bl lowerEL_sync_interrupt // exception.c + restore_task + eret + +currentEL_sync_handler: + save_all + bl exception_entry // exception.c + load_all + eret + +lowerEL_irq_handler: + save_all + bl lowerEL_irq_interrupt // exception.c + load_all + eret + +currentEL_irq_handler: + save_all + bl currentEL_irq_interrupt // exception.c + load_all + eret + +exception_no_handler: + save_all + bl no_exception_handle // exception.c + load_all + eret \ No newline at end of file diff --git a/lab8/kernel/fat32.c b/lab8/kernel/fat32.c new file mode 100644 index 000000000..0f9eb20f5 --- /dev/null +++ b/lab8/kernel/fat32.c @@ -0,0 +1,429 @@ +#include "fat32.h" +#include "util.h" +#include "uart.h" +#include "allocator.h" +#include "sdhost.h" + +struct fatfs_boot_sector* fat_boot_sector; +struct fatfs_dir_entry* fat_root_dir_entry; +int fat_starting_sector; +int data_starting_sector; +int root_starting_sector; + +struct vnode_operations* fatfs_v_ops; +struct file_operations* fatfs_f_ops; + +struct dynamic_allocator *dyalloc_fat = 0; + +/* +* fatfs_init 初始化fatfs +* +* 1. 初始化v_node操作(分配記憶體,註冊查找、建立的方法) +* 2. 初始化file操作(分配記憶體,註冊寫入、讀取的方法) +* 3. 呼叫sd卡driver的sd_init進行初始化 +*/ +void fatfs_init() +{ + dyalloc_fat = dynamic_allocator_init(); + + fatfs_v_ops = (struct vnode_operations*)dynamic_alloc(dyalloc_fat, sizeof(struct vnode_operations)); + fatfs_v_ops->lookup = fatfs_lookup; + fatfs_v_ops->set_parent = fatfs_set_parent; + + fatfs_f_ops = (struct file_operations*)dynamic_alloc(dyalloc_fat, sizeof(struct file_operations)); + fatfs_f_ops->write = fatfs_write; + fatfs_f_ops->read = fatfs_read; + fatfs_f_ops->list = fatfs_list; + + sd_init(); +} + +/* +* fatfs_setup_mount 實作mount方法(Mount Fat32 file system in SD card) +* +* required 1-1 Get the FAT32 partition (Fat32 file system is in first partition of sd card) +* required 1-2 Parse the FAT32’s metadata and set up the mount. +* +* 1. 解析並顯示mbr metadata資訊 +* 2. 讀取boot sector +* - 配置一塊fatfs_boot_sector記憶體,由SD卡讀取一個block,並將內容複製給fatfs_boot_sector +* 3. 計算資料起始sector位址(起始sector + 保留sector數 + boot sector個數 * 一個sector多大) +* 4. 建立根目錄 +* - 計算根目錄起始sector位址 +* - 由SD卡讀取一個block, 建立fatfs_dir_entry實體 +* - 建立根目錄entry及vnode,並將SD卡根目錄entry內容建立為根目錄vnode +* 5. 掛載至檔案系統 +*/ +int fatfs_setup_mount(struct filesystem* fs, struct mount* mount) +{ + // 1. 解析並顯示mbr metadata資訊 + struct mbr_partition_entry* entry = parse_mbr_metadata(); + + // 2. 讀取boot sector + // - 配置一塊fatfs_boot_sector記憶體,由SD卡讀取一個block,並將內容複製給fatfs_boot_sector + fat_starting_sector = entry->starting_sector; + + char* fat_boot = (char*)dynamic_alloc(dyalloc_fat, BLOCK_SIZE); + readblock(fat_starting_sector, fat_boot); + int boot_sector_size = sizeof(struct fatfs_boot_sector); + fat_boot_sector = (struct fatfs_boot_sector*)dynamic_alloc(dyalloc_fat, boot_sector_size); + char* src = (char*)fat_boot; + char* dst = (char*)fat_boot_sector; + for (int i = 0; i < boot_sector_size; i++) + dst[i] = src[i]; + + dynamic_free(dyalloc_fat, (unsigned long long)fat_boot); + + // 3.計算資料起始sector位址(起始sector + 保留sector數 + boot sector個數 * 一個sector多大) + data_starting_sector = + fat_starting_sector + fat_boot_sector->reserved_sector_count + + fat_boot_sector->fat_count * fat_boot_sector->sectors_per_fat_32; + + // 4.建立根目錄 + // - 計算根目錄起始sector位址 + root_starting_sector = get_starting_sector(fat_boot_sector->root_cluster); + + // - 由SD卡讀取一個block, 建立fatfs_dir_entry實體 + char* fat_root = (char*)dynamic_alloc(dyalloc_fat, BLOCK_SIZE); + readblock(root_starting_sector, fat_root); + fat_root_dir_entry = (struct fatfs_dir_entry*)fat_root; + + // - 建立根目錄entry及vnode,並將SD卡根目錄entry內容建立為根目錄vnode + struct fatfs_entry* root_entry = (struct fatfs_entry*)dynamic_alloc(dyalloc_fat, sizeof(struct fatfs_entry)); + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fat, sizeof(struct vnode)); + vnode->mount = mount; + vnode->v_ops = fatfs_v_ops; + vnode->f_ops = fatfs_f_ops; + vnode->internal = (void*)root_entry; + root_entry->parent_vnode = 0; + fatfs_set_entry(root_entry, FILE_DIRECTORY, vnode, fat_boot_sector->root_cluster, 4096); + fatfs_set_directory(root_entry, fat_root_dir_entry); + // 5.掛載至檔案系統 + mount->fs = fs; + mount->root = vnode; + + return 1; +} + +/* +* parse_mbr_metadata (解析並顯示MBR資訊) +* +* 1. 讀取MBR +* - mbr位於sd card第一個sector,使用readblock讀取 +* - 配置一塊mbr_partition_entry記憶體,並將mbr內容複製給mbr_partition_entry +* 2. 印出[Partition type]、[Partition size]、[Block index]資訊 +* 3. 回傳mbr_partition_entry +*/ +struct mbr_partition_entry* parse_mbr_metadata() +{ + // 1. 讀取MBR + // mbr位於sd card第一個sector,使用readblock讀取 + /* + * MBR 是由 512 位元組組成,MBR 只佔用了其中的 446 個位元組, + * 另外 64 個位元組交給了 DPT(Disk Partition Table 硬碟分區表), + * 最後兩個位元組 “55,AA” 是分區的結束標誌 + */ + char* mbr = (char*)dynamic_alloc(dyalloc_fat, BLOCK_SIZE); + readblock(0, mbr); + if (mbr[510] != 0x55 || mbr[511] != 0xAA) + { + uart_putstr("bad magic in MBR\n"); + return 0; + } + + // 配置一塊mbr_partition_entry記憶體,並將mbr內容複製給mbr_partition_entry + int entry_size = sizeof(struct mbr_partition_entry); + struct mbr_partition_entry* entry = (struct mbr_partition_entry*)dynamic_alloc(dyalloc_fat, entry_size); + char* src = (char*)mbr; + char* dst = (char*)entry; + for (int i = 0; i < entry_size; i++) + dst[i] = src[MBR_PARTITION_BASE + i]; // 由446開始讀取硬碟分區表 + + dynamic_free(dyalloc_fat, (unsigned long long)mbr); + + // 2. 印出[Partition type]、[Partition size]、[Block index]資訊 + + uart_putstr("\n========== FAT32 init ==========\n"); + char buf[16] = {0}; + uart_putstr("Partition type: 0x"); + unsignedlonglongToStrHex(entry->partition_type, buf); + uart_putstr(buf); + + if (entry->partition_type == 0xB) // 代表為32bit FAT + uart_putstr(" (FAT32 with CHS addressing)"); + + uart_putstr("\nPartition size: "); + unsignedlonglongToStr(entry->sector_count, buf); + uart_putstr(buf); + uart_putstr(" (sectors)\n"); + + uart_putstr("Block index: "); + unsignedlonglongToStr(entry->starting_sector, buf); + uart_putstr(buf); + uart_putstr("\n================================\n\n"); + + // 3. 回傳mbr_partition_entry + return entry; +} + +/* +* fatfs_set_entry 設定節點內容 +* +* fatfs_entry結構,包含name、name_len、starting_cluster、type、vnode、parent_vnode、child、buf屬性 +* 1. name及name_len在外部設定 +* 2. 設定starting_cluster +* 3. 設定vnode +* 4. 設定type +* 5. parent_vnode外面設定 +* 6. 分配buf記憶體,並設定buf內容為空 +* 7. 假如節點為目錄,初始化目錄下子檔案 +*/ +void fatfs_set_entry(struct fatfs_entry* entry, enum FILE_TYPE type, struct vnode* vnode, int starting_cluster, int buf_size) +{ + entry->starting_cluster = starting_cluster; + entry->vnode = vnode; + entry->type = type; + entry->buf = (struct fatfs_buf*)dynamic_alloc(dyalloc_fat, sizeof(struct fatfs_buf)); + entry->buf->size = buf_size; + + for (int i = 0; i < FATFS_BUF_SIZE; i++) + entry->buf->buffer[i] = '\0'; + + if (entry->type == FILE_DIRECTORY) + { + for (int i = 0; i < MAX_FILES_IN_DIR; ++i) + { + entry->child[i] = (struct fatfs_entry*)dynamic_alloc(dyalloc_fat, sizeof(struct fatfs_entry)); + entry->child[i]->name[0] = 0; + entry->child[i]->type = FILE_NONE; + entry->child[i]->parent_vnode = vnode; + } + } +} + +/* +* fatfs_set_directory 設定子檔案or子資料夾 +* +* 傳入fatfs_entry 及 fatfs_dir_entry, 其中fatfs_dir_entry為SD卡的 +* +* 使用for迴圈,逐一設定 +* 1. 判斷檔名是否有值(去除雜訊) +* - 取出dir_entry的檔名及副檔名組合出檔名 +* - 建立vnode並設定子節點屬性及內容 +*/ +void fatfs_set_directory(struct fatfs_entry* entry, struct fatfs_dir_entry* dir_entry) +{ + for (int i = 0; i < MAX_FILES_IN_DIR; ++i) + { + // 1. 判斷檔名是否有值(去除雜訊) + int flag = 0; + for (int j = 0; j < 8; j++) + { + if ((dir_entry + i)->filename[j] == 0) + { + flag = 1; + break; + } + } + + if ((dir_entry + i)->filename[0] && !flag) + { + // - 取出dir_entry的檔名及副檔名組合出檔名 + // ex. filename = test, extension = txt, name => test.txt + strncpy(entry->child[i]->name, (char*)((dir_entry + i)->filename), 8); + unsigned int len = strlen(entry->child[i]->name); + entry->child[i]->name_len = len; + + if ((dir_entry + i)->extension[0]) + { + *(entry->child[i]->name + len) = '.'; + strncpy(entry->child[i]->name + len + 1, (char*)((dir_entry + i)->extension), 3); + } + + // - 建立vnode + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fat, sizeof(struct vnode)); + vnode->mount = 0; + vnode->v_ops = entry->vnode->v_ops; + vnode->f_ops = entry->vnode->f_ops; + vnode->internal = entry->child[i]; + + // - 設定子節點內容 + // cluster_low是low 2 bytes of cluster, cluster_high是high 2 byte of cluster,所以cluster_high要 * 4 + // ex. cluster_high = 10, cluster_low = 01, cluster index為1001 = 9 + int starting_cluster = ((dir_entry + i)->cluster_high << 2) + (dir_entry + i)->cluster_low; + int buf_size = (dir_entry + i)->file_size; + fatfs_set_entry(entry->child[i], FILE_REGULAR, vnode, starting_cluster, buf_size); + } + } +} + +/* +* get_starting_sector 回傳指定cluster索引的起始位置 +* +* 因為由2開始編號所以要減2 +* => (cluster編號 - 2) * 每個cluster多大 + 資料起始位置 +*/ +int get_starting_sector(int cluster) +{ + return (cluster - 2) * fat_boot_sector->sectors_per_cluster + data_starting_sector; +} + +/* +* fatfs_lookup 實作lookup方法 +* +* 傳入目前目錄,(目標node,搜尋名稱),搜尋目錄下指定名稱的子目錄或子檔案 +* +* 1. 判斷傳入的dir_node檔案類型是否為目錄,假如不是目錄回傳失敗 +* 2. 假如搜尋名稱為"."表示為目前目錄,target = 目前節點 +* 3. 假如搜尋名稱為".."表示為上一層目錄 +* - 如果沒有父節點,回傳失敗 +* - 否則target = 父節點 +* 4. 搜尋目前目錄下每一個子檔案 +* - 假如名稱 = 搜尋名稱,target = 子檔案的節點,回傳成功 +* 5. 都不符合就回傳失敗 +*/ +int fatfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)dir_node->internal; + + if (entry->type != FILE_DIRECTORY) + return 0; + if (strcmp((char*)component_name, ".")) + { + *target = entry->vnode; + return 1; + } + if (strcmp((char*)component_name, "..")) + { + if (!entry->parent_vnode) + return 0; + *target = entry->parent_vnode; + return 1; + } + + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + entry = ((struct fatfs_entry*)dir_node->internal)->child[i]; + if (strcmp(entry->name, (char*)component_name)) + { + *target = entry->vnode; + return 1; + } + } + + return 0; +} + +/* +* fatfs_set_parent 實作set_parent方法 +* +* 傳入子節點,父節點 +* +* 1. 取出子節點內容結構 +* 2. 設定父節點為傳入的父節點 +*/ +int fatfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)child_node->internal; + entry->parent_vnode = parent_vnode; + return 1; +} + +/* +* fatfs_list 實作list方法 +* +* 傳入檔案,buffer,索引,列出資料夾下指定index的檔名,並回傳檔案size +* +* 1. 取出節點內容結構 +* 2. 假如不是目錄,回傳失敗 +* 3. index超過最大子節點數,回傳失敗 +* 4. 資料夾下指定index子節點未分配,回傳失敗 +* 5. 複製資料夾下指定index的檔名到buf +* 6. 回傳檔案size +*/ +int fatfs_list(struct file* file, void* buf, int index) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)file->vnode->internal; + + if (entry->type != FILE_DIRECTORY) + return -1; + if (index >= MAX_FILES_IN_DIR) + return -1; + + if (entry->child[index]->type == FILE_NONE) + return 0; + + strcpy((char*)buf, entry->child[index]->name); + return entry->child[index]->buf->size; +} + +//======================================================================= + +/* +* fatfs_write 實作write方法 +* +* 傳入檔案,buffer,長度 +* +* 1. 取出節點內容結構 +* 2. 由buf讀取len個byte,寫入節點的buffer內 +* 3. 節點檔案的buf大小 = file當前位置(file大小) +* 4. 檢查子檔案or子資料夾,假如檔名=節點名稱,設定檔案大小=buf大小 +* 5. 取得對應SD檔案cluster,抓出sector位置,並寫入buf內容 +* 6. 回傳寫入的byte數 +*/ +int fatfs_write(struct file* file, const void* buf, unsigned int len) +{ + struct fatfs_entry* entry = (struct fatfs_entry*)file->vnode->internal; + for (unsigned int i = 0; i < len; i++) + { + entry->buf->buffer[file->f_pos++] = ((char*)buf)[i]; + if (entry->buf->size < file->f_pos) + entry->buf->size = file->f_pos; + } + + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + if (strcmpn((char*)((fat_root_dir_entry + i)->filename), entry->name, entry->name_len)) + (fat_root_dir_entry + i)->file_size = entry->buf->size; + } + + writeblock(root_starting_sector, (char*)fat_root_dir_entry); + int starting_sector = get_starting_sector(entry->starting_cluster); + writeblock(starting_sector, entry->buf->buffer); + + return len; +} + +/* +* fatfs_read 實作read方法 +* +* 傳入檔案,buffer,長度,由傳入的buf中,寫入len byte到該節點的buf內 +* +* 1. 取出節點內容結構 +* 2. 取得對應SD檔案cluster,抓出起始sector位置 +* 3. 讀取sector內容到buffer +* +* 4. 由節點的buffer內取出len byte內容,傳入buf +* 5. 每讀一byte, read_len讀取長度++ +* 6. 假如已讀完節點buffer的內容(讀到buf size了),跳出 +* 7. 回傳讀了幾個byte +*/ +int fatfs_read(struct file* file, void* buf, unsigned int len) +{ + unsigned int read_len = 0; + struct fatfs_entry* entry = (struct fatfs_entry*)file->vnode->internal; + int starting_sector = get_starting_sector(entry->starting_cluster); + + readblock(starting_sector, entry->buf->buffer); + + for (unsigned int i = 0; i < len; i++) + { + ((char*)buf)[i] = entry->buf->buffer[file->f_pos++]; + read_len++; + + if (read_len == entry->buf->size) + break; + } + + return read_len; +} diff --git a/lab8/kernel/fat32.h b/lab8/kernel/fat32.h new file mode 100644 index 000000000..84e303ccb --- /dev/null +++ b/lab8/kernel/fat32.h @@ -0,0 +1,114 @@ +#ifndef FAT32_H +#define FAT32_H + +#include "vfs.h" + +#define BLOCK_SIZE 512 +#define MBR_PARTITION_BASE 0x1BE +#define MAX_FILES_IN_DIR 16 +#define FATFS_BUF_SIZE (10 * 1024) + +// https://en.wikipedia.org/wiki/Master_boot_record +// The struct of partition of Master Boot Record (MBR) for disk +struct mbr_partition_entry +{ + unsigned char status_flag; // 0x0 + unsigned char partition_begin_head; // 0x1 + unsigned short partition_begin_sector; // 0x2 + unsigned char partition_type; // 0x4 + unsigned char partition_end_head; // 0x5 + unsigned short partition_end_sector; // 0x6 + unsigned int starting_sector; // 0x8 + unsigned int sector_count; // 0xC +} __attribute__((packed)); + +// https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system +// Boot Sector +struct fatfs_boot_sector +{ + unsigned char bootjmp[3]; // 0x00 + unsigned char oem_name[8]; // 0x08 + + // https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#BPB + // BIOS Parameter Block + unsigned short bytes_per_sector; // 0x0B-0x0C + unsigned char sectors_per_cluster; // 0x0D + unsigned short reserved_sector_count; // 0x0E-0x0F + unsigned char fat_count; // 0x10 + unsigned short root_entry_count; // 0x11-0x12 + unsigned short total_sectors; // 0x13-0x14 + unsigned char media_descriptor; // 0x15 + unsigned short sectors_per_fat; // 0x16-0x17 + unsigned short sectors_per_track; // 0x18-0x19 + unsigned short head_count; // 0x1A-0x1B + unsigned int hidden_sector_count; // 0x1C-0x1F + unsigned int total_sectors_32; // 0x20-0x23 + + //https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#EBPB + //Extended BIOS Parameter Block + unsigned int sectors_per_fat_32; // 0x24-0x27 + unsigned short mirror_flags; // 0x28-0x29 + unsigned short fat_version; // 0x2A-0x2B + unsigned int root_cluster; // 0x2C-0x2F + unsigned short info_sector_number; // 0x30-0x31 + unsigned short backup_boot_sector_count;// 0x32-0x33 + unsigned char reserved_0[12]; // 0x34-0x3F + unsigned char drive_number; // 0x40 + unsigned char reserved_1; // 0x41 + unsigned char boot_signature; // 0x42 + unsigned int volume_id; // 0x43-0x46 + unsigned char volume_label[11]; // 0x47-0x51 + unsigned char fat_type_label[8]; // 0x52-0x59 +} __attribute__((packed)); + +// https://en.wikipedia.org/wiki/8.3_filename +// Struct of fat32 directory entry. Short Filenames(SFN) version +struct fatfs_dir_entry +{ + unsigned char filename[8]; // 0x00-0x07, File name: 8 ASCII characters + unsigned char extension[3]; // 0x08-0x0A, File extension + unsigned char attributes; // 0x0B, Attributes of the file + unsigned char reserved; // 0x0C + unsigned char created_time_ms; // 0x0D + unsigned short created_time; // 0x0E-0x0F + unsigned short created_date; // 0x10-0x11 + unsigned short last_access_date; // 0x12-0x13 + unsigned short cluster_high; // 0x14-0x15 + unsigned short last_modified_time; // 0x16-0x17 + unsigned short last_modified_date; // 0x18-0x19 + unsigned short cluster_low; // 0x1A-0x1B + unsigned int file_size; // 0x1C-0x1F. The size of the file in bytes. +} __attribute__((packed)); + +struct fatfs_buf +{ + int flag; + unsigned int size; + char buffer[FATFS_BUF_SIZE]; +}; + +struct fatfs_entry +{ + char name[20]; + int name_len; // test1.txt -> 5 (before .) + int starting_cluster; + enum FILE_TYPE type; + struct vnode* vnode; + struct vnode* parent_vnode; + struct fatfs_entry* child[MAX_FILES_IN_DIR]; + struct fatfs_buf* buf; +}; + +void fatfs_init(); +int fatfs_setup_mount(struct filesystem* fs, struct mount* mount); +struct mbr_partition_entry* parse_mbr_metadata(); +int get_starting_sector(int cluster); +void fatfs_set_directory(struct fatfs_entry* entry, struct fatfs_dir_entry* dentry); +void fatfs_set_entry(struct fatfs_entry* entry, enum FILE_TYPE type, struct vnode* vnode, int starting_cluster, int buf_size); +int fatfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); +int fatfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode); +int fatfs_write(struct file* file, const void* buf, unsigned int len); +int fatfs_read(struct file* file, void* buf, unsigned int len); +int fatfs_list(struct file* file, void* buf, int index); + +#endif \ No newline at end of file diff --git a/lab8/kernel/gpio.h b/lab8/kernel/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/kernel/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/kernel/linker.ld b/lab8/kernel/linker.ld new file mode 100644 index 000000000..b811b421d --- /dev/null +++ b/lab8/kernel/linker.ld @@ -0,0 +1,28 @@ +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/kernel/main.c b/lab8/kernel/main.c new file mode 100644 index 000000000..fd440b5a1 --- /dev/null +++ b/lab8/kernel/main.c @@ -0,0 +1,462 @@ +#include "uart.h" +#include "util.h" +#include "reboot.h" +#include "cpio.h" +#include "devicetree.h" +#include "buddy.h" +#include "allocator.h" +#include "exception.h" +#include "timer.h" +#include "thread.h" +#include "vfs.h" + +#define CMDSIZE 64 +char cmd[CMDSIZE] = {0}; +char last_cmd[CMDSIZE] = {0}; +int cmdSize = 0; + +int level = 0; +int isSync = 1; + +int vfsIsInit = 0; + +void cmd_init() +{ + cmdSize = 0; + for(int i = 0; i < CMDSIZE; i++) + cmd[i] = 0; +} + +// callback for print device +void callback(unsigned long int ptr, unsigned long int strptr) +{ + level++; + print_indent(level); + uart_putstr((char *)ptr); + uart_putstr("\n"); + + ptr += align4(strlen((char *)ptr) + 1); + + while (1) + { + unsigned int tag = convert_bigendian((char *)ptr); + ptr += sizeof(unsigned int); + + if (tag == FDT_BEGIN_NODE) + { + level++; + print_indent(level); + uart_putstr((char *)ptr); + uart_putstr("\n"); + + ptr += align4(strlen((char *)ptr) + 1); + } + else if (tag == FDT_END_NODE) + { + level--; + if (level == 0) + break; + } + else if (tag == FDT_NOP) + continue; + else if (tag == FDT_PROP) + { + unsigned int len = convert_bigendian((char *)ptr); // length + ptr += sizeof(unsigned int); // ptr + 4 + unsigned int nameoff = convert_bigendian((char *)ptr); // name offset + ptr += sizeof(unsigned int); + + print_indent(level); + uart_putstr("Property Name:"); + uart_putstr((char *)(strptr + nameoff)); + uart_putstr("\t"); + print_indent(level); + uart_putstr("Property Data:"); + uart_putstr((char *)ptr); + uart_putstr("\n"); + + ptr += align4(len); + } + else if (tag == FDT_END) + break; + } +} + +void cmd_handle() // parse command +{ + if(strcmp(cmd, "hello")) + { + uart_putstr("Hello World! \n"); + } + else if(strcmp(cmd, "help")) + { + uart_putstr("help print all available commands \n"); + uart_putstr("hello print Hello World! \n"); + uart_putstr("reboot reboot raspi3 \n"); + uart_putstr("ls list rootfs file \n"); + uart_putstr("cat cat [filename] open and read file \n"); + uart_putstr("dtbls list device tree \n"); + uart_putstr("dtbcat cat [nodename] get property \n"); + uart_putstr("buddy test buddy page frame allocator \n"); + uart_putstr("dynamic test dynamic allocator\n"); + uart_putstr("curEL get current expcetion level\n"); + uart_putstr("runUser load and run a user program in the initramfs [svc.elf]\n"); + uart_putstr("userTimer load and run a user program in the initramfs [svc.elf] (coreTimer)\n"); + uart_putstr("asyncRead use uart interrupt and read \n"); + uart_putstr("asyncWrite use uart interrupt and write \n"); + uart_putstr("recover disable interrupt \n"); + uart_putstr("setTimeout [MESSAGE] [SECONDS] set timer timeout and print message \n"); + uart_putstr("threadreq1 thread requirement 1\n"); + uart_putstr("threadreq2 thread requirement 2\n"); + uart_putstr("vfsreq1 vfs requirement 1: Populate the root file system with initramfs\n"); + uart_putstr("vfsreq2 vfs requirement 2\n"); + uart_putstr("vfselec1 vfs elective 1\n"); + uart_putstr("vfselec2 vfs elective 2\n"); + uart_putstr("fatreq1 get FAT32 partition and mount the FAT32 File System\n"); + uart_putstr("fatreq2 add read and write in FAT32 \n"); + uart_putstr("mmureq2 mmu requirement 2 \n"); + } + else if(strcmp(cmd, "reboot")) + { + uart_putstr("reboot .... \n"); + raspi3_reboot(100); + while(1); // wait for reboot + } + else if(strcmp(cmd, "ls")) + { + cpio_list(); + uart_putstr("\n"); + } + else if(strcmpn(cmd, "cat", 2)) + { + // ex. cat init.txt + unsigned int length = strlen(cmd) - 4; + if (length > 0) + { + char *content = cpio_content(&cmd[4]); + if(content) + uart_putstr(content); + else + uart_putstr("file not found !"); + } + else + uart_putstr("no input file name !"); + + uart_putstr("\n"); + + } + else if(strcmp(cmd, "dtbls")) + { + dtb_ls(); + uart_putstr("\n"); + } + else if(strcmpn(cmd, "dtbcat", 5)) + { + level = 0; + unsigned int length = strlen(cmd) - 7; + if (length > 0) + { + int ret = dtb_cat(&cmd[7], callback); + if(ret == -1) + uart_putstr("device not found !"); + } + else + uart_putstr("no input device name !"); + uart_putstr("\n"); + } + else if(strcmp(cmd, "buddy")) + { + buddy_test(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "dynamic")) + { + dynamic_test(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "curEL")) + { + show_current_el(); + uart_putstr("\n"); + } + else if(strcmpn(cmd, "runUser", 6)) + { + // ex. runUser svc.elf + unsigned int length = strlen(cmd) - 8; + if (length > 0) + cpio_run_user_program(&cmd[8], 0); + else + uart_putstr("no input file name !"); + + uart_putstr("\n"); + } + else if(strcmpn(cmd, "userTimer", 8)) + { + uart_putstr("irq_handle\n"); + // ex. userTimer svc.elf + unsigned int length = strlen(cmd) - 10; + if (length > 0) + cpio_run_user_program(&cmd[10], 1); + else + uart_putstr("no input file name !"); + + uart_putstr("\n"); + } + else if(strcmp(cmd, "asyncRead")) + { + if(isSync == 1) + { + enable_interrupt(); + isSync = 0; + } + uart_putstr("start async read command..."); + uart_putstr("\n"); + } + else if(strcmp(cmd, "asyncWrite")) + { + if(isSync == 1) + { + enable_interrupt(); + isSync = 0; + } + uart_async_putstr("This is async write string..."); + uart_async_putstr("\n"); + } + else if(strcmp(cmd, "recover")) + { + if(isSync == 0) + { + disable_interrupt(); + isSync = 1; + uart_putstr("disable_interrupt ..."); + } + uart_putstr("\n"); + } + else if(strcmpn(cmd, "setTimeout", 9)) + { + int second = 0; + char msg[20] = {0}; + // get second and message + // setTimeout [MESSAGE] [SECONDS] + for(int i = 11; cmd[i] != '\0'; i++) + { + if(cmd[i] != ' ') + { + msg[i - 11] = cmd[i]; + } + else + { + msg[i - 11] = '\0'; + + for(int j = i + 1; cmd[j] != '\0'; j++) + { + if(cmd[j] >= '0' && cmd[j] <= '9') + second = second * 10 + cmd[j] - '0'; + } + } + } + + if(isSync == 1) + { + enable_interrupt(); + isSync = 0; + } + + add_timer(print_timer_msg, msg, second); + } + else if(strcmp(cmd, "threadreq1")) + { + thread_test(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "threadreq2")) + { + thread_test2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfsreq1")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + else + uart_putstr("vfs inited ! \n"); + + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfsreq2")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_vfs_req2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfselec1")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_vfs_ele1(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "vfselec2")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_vfs_ele2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "fatreq1")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_fat32_req1(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "fatreq2")) + { + if(!vfsIsInit) + { + vfs_init(); + vfsIsInit = 1; + } + thread_fat32_req2(); + uart_putstr("\n"); + } + else if(strcmp(cmd, "mmureq2")) + { + thread_mmu_req2(); + uart_putstr("\n"); + } + else if(strlen(cmd) != 0) + { + uart_putstr("command \""); + uart_putstr(cmd); + uart_putstr("\" not found, try \n"); + } + + uart_putstr("# "); +} + +int main() +{ + isSync = 1; + + cmd_init(); + uart_init(); + memory_init(); + init_timeout(); + init_thread(); + init_thread2(); + vfs_init(); + vfsIsInit = 1; + + // put welcome ascii art + uart_putstr("\n"); + uart_putstr(" .~~. .~~. \n"); + uart_putstr(" '. \\ ' ' / .' \n"); + uart_putstr(" .~ .~~~..~. \n"); + uart_putstr(" : .~.'~'.~. : \n"); + uart_putstr(" ~ ( ) ( ) ~ \n"); + uart_putstr(" ( : '~'.~.'~' : ) \n"); + uart_putstr(" ~ .~ ( ) ~. ~ NYCU OS 2021 \n"); + uart_putstr(" ( : '~' : ) Welcome Raspberry Pi 3 !! \n"); + uart_putstr(" '~ .~~~. ~' \n"); + uart_putstr(" '~' \n"); + uart_putstr("# "); + + char c; + char c2; + while(1) + { + if (isSync) // get char from user + c = uart_getchar(); + else + c = uart_async_getchar(); + + // https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/45106/ + switch(c) + { + case '\r': + case '\n': // 0X0A '\n' newline, parse command + while(cmd[cmdSize] != 0) + cmdSize++; + cmd[cmdSize] = '\0'; + cmdSize++; + uart_putstr("\n"); + for(int i = 0; i < cmdSize; i++) // store last command + last_cmd[i] = cmd[i]; + cmd_handle(); + cmd_init(); + break; + case 127: // backspace + if(cmdSize > 0) + { + cmdSize--; + cmd[cmdSize] = 0; + uart_putstr("\b \b"); + } + break; + case '[': + if (isSync) // get char from user + c2 = uart_getchar(); + else + c2 = uart_async_getchar(); + if (c2 == 'A') // cursor up + { + for(int i = 0; i < cmdSize; i++) // clear input + uart_putstr("\b \b"); + cmd_init(); + for(int i = 0; i < CMDSIZE; i++) // input last command + { + if(last_cmd[i] == 0) + break; + + cmd[i] = last_cmd[i]; + uart_sendchar(last_cmd[i]); + cmdSize++; + } + } + else if (c2 == 'C' && cmdSize < strlen(cmd)) // cursor left + { + uart_putstr("\033[C"); + cmdSize++; + } + else if (c2 == 'D' && cmdSize > 0) // cursor right + { + uart_putstr("\033[D"); + cmdSize--; + } + break; + default: + if (c > 31 && c < 127) // visible ascii + { + cmd[cmdSize] = c; + cmdSize++; + uart_sendchar(c); + } + break; + } + + if (cmdSize == CMDSIZE) + { + uart_putstr("\ncommand too long !\n# "); + cmd_init(); + } + } + + return 0; +} diff --git a/lab8/kernel/mmio.h b/lab8/kernel/mmio.h new file mode 100644 index 000000000..cef94de2f --- /dev/null +++ b/lab8/kernel/mmio.h @@ -0,0 +1,12 @@ +#ifndef MMIO_H +#define MMIO_H + +#include "mmu.h" + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE (0x3F000000 + KVA) + +#endif \ No newline at end of file diff --git a/lab8/kernel/mmu.c b/lab8/kernel/mmu.c new file mode 100644 index 000000000..020ed70b3 --- /dev/null +++ b/lab8/kernel/mmu.c @@ -0,0 +1,60 @@ +#include "mmu.h" +#include "uart.h" +#include "thread.h" + +// 初始化頁表 +void init_page_table(struct thread *thread, unsigned long **table) +{ + // 配置4KB + *table = (unsigned long *)thread_allocate_page(thread, 4096); + + for (int i = 0; i < 512; i++) + *((*table) + i) = 0; + + // 虛擬位址轉實體位址 + *table = (unsigned long *)VA2PA(*table); +} + +// 為process更新頁表 +void update_page_table(struct thread *thread, unsigned long virtual_addr, unsigned long physical_addr, int permission) +{ + // PGD為空錯誤 + if (thread->pgd == 0) + { + uart_putstr("Invalid PGD!!\n"); + return; + } + + // 取得各層,PGD: 2^39, PUD: 2^30, PMD: 2^21, PTE = 2^12 = 4KB + unsigned int index[4] = + { + (virtual_addr >> 39) & 0x1ff, (virtual_addr >> 30) & 0x1ff, + (virtual_addr >> 21) & 0x1ff, (virtual_addr >> 12) & 0x1ff + }; + + unsigned long *table = (unsigned long *)PA2VA(thread->pgd); + + // 由階層0-2, PGD=>PUD=>PMD + for (int level = 0; level < 3; level++) + { + // 假如下一級頁表尚未出現 + if (table[index[level]] == 0) + { + // 配置一個頁框以作下一級頁表 + init_page_table(thread, (unsigned long **)&(table[index[level]])); + table[index[level]] |= PD_TABLE; + } + + // 轉為virtual address + table = (unsigned long *)PA2VA(table[index[level]] & ~0xfff); + } + + // 設置讀取寫入權限 + unsigned long BOOT_RWX_ATTR = (1 << 6); + if (permission & 0b010) + BOOT_RWX_ATTR |= 0; + else + BOOT_RWX_ATTR |= (1 << 7); + + table[index[3]] = physical_addr | BOOT_PTE_NORMAL_NOCACHE_ATTR | BOOT_RWX_ATTR; +} \ No newline at end of file diff --git a/lab8/kernel/mmu.h b/lab8/kernel/mmu.h new file mode 100644 index 000000000..a51633b05 --- /dev/null +++ b/lab8/kernel/mmu.h @@ -0,0 +1,29 @@ +#ifndef MMU_H +#define MMU_H + +#include "thread.h" + +#define KVA 0xffff000000000000 + +#define VA2PA(addr) ((unsigned long)(addr) & (unsigned long)0x0000ffffffffffff) +#define PA2VA(addr) ((unsigned long)(addr) | (unsigned long)0xffff000000000000) + +#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_PAGE 0b11 +#define PD_ACCESS (1 << 10) +#define BOOT_PGD_ATTR (PD_TABLE) +#define BOOT_PUD_ATTR (PD_TABLE) +#define BOOT_PMD_ATTR (PD_TABLE) +#define BOOT_PTE_DEVICE_nGnRnE_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_PAGE) +#define BOOT_PTE_NORMAL_NOCACHE_ATTR (PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_PAGE) + +void init_page_table(struct thread *thread, unsigned long **table); +void update_page_table(struct thread *thread, unsigned long virtual_addr, unsigned long physical_addr, int permission); + +#endif \ No newline at end of file diff --git a/lab8/kernel/mmu_define.h b/lab8/kernel/mmu_define.h new file mode 100644 index 000000000..c712b11cc --- /dev/null +++ b/lab8/kernel/mmu_define.h @@ -0,0 +1,38 @@ +#ifndef MMUDEFINE_H +#define MMUDEFINE_H + +#define KVA 0xffff000000000000 +#define PERIPHERAL_BASE 0x3f000000 + +#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 MAIR_CONFIG_DEFAULT ( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + +#define PAGE_TABLE_BASE 0x30000000 +#define PGD_BASE (PAGE_TABLE_BASE + 0x0000) +#define PUD_BASE (PAGE_TABLE_BASE + 0x1000) +#define PMD_BASE (PAGE_TABLE_BASE + 0x2000) +#define PTE_BASE (PAGE_TABLE_BASE + 0x4000) + +#define PD_TABLE 0b11 +#define PD_BLOCK 0b01 +#define PD_PAGE 0b11 +#define PD_ACCESS (1 << 10) +#define BOOT_PGD_ATTR (PD_TABLE) +#define BOOT_PUD_ATTR (PD_TABLE) +#define BOOT_PMD_ATTR (PD_TABLE) +#define BOOT_PTE_DEVICE_nGnRnE_ATTR \ + (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_PAGE) +#define BOOT_PTE_NORMAL_NOCACHE_ATTR \ + (PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_PAGE) + +#endif \ No newline at end of file diff --git a/lab8/kernel/reboot.c b/lab8/kernel/reboot.c new file mode 100644 index 000000000..f9c108a66 --- /dev/null +++ b/lab8/kernel/reboot.c @@ -0,0 +1,13 @@ +#include "reboot.h" + +void raspi3_reboot(int ticks) // reboot after watchdog timer expire +{ + *PM_RSTC = PM_PASSWORD | 0x20; // full reset + *PM_WDOG = PM_PASSWORD | ticks; // number of watchdog tick +} + +void cancel_reset() +{ + *PM_RSTC = PM_PASSWORD | 0; // full reset + *PM_WDOG = PM_PASSWORD | 0; // number of watchdog tick +} \ No newline at end of file diff --git a/lab8/kernel/reboot.h b/lab8/kernel/reboot.h new file mode 100644 index 000000000..f7f91551b --- /dev/null +++ b/lab8/kernel/reboot.h @@ -0,0 +1,14 @@ +#ifndef REBOOT +#define REBOOT + +#include "mmu.h" + +#define PM_PASSWORD 0x5A000000 + +#define PM_RSTC ((volatile unsigned int*)0x3F10001C + KVA) +#define PM_WDOG ((volatile unsigned int*)0x3F100024 + KVA) + +void raspi3_reboot(int ticks); // reboot after watchdog timer expire +void cancel_reset(); // cancel_reset + +#endif \ No newline at end of file diff --git a/lab8/kernel/sched.s b/lab8/kernel/sched.s new file mode 100644 index 000000000..75ba8492b --- /dev/null +++ b/lab8/kernel/sched.s @@ -0,0 +1,39 @@ +/* + switch_to (prev, next) x0:prev x1:next + 1. save prev context + 2. recover next context + 3. let x1(next) is current thread +*/ + +.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 + + + +/* + Get the current thread pointer +*/ + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/lab8/kernel/sdhost.c b/lab8/kernel/sdhost.c new file mode 100644 index 000000000..d5b8f101d --- /dev/null +++ b/lab8/kernel/sdhost.c @@ -0,0 +1,251 @@ +#include "sdhost.h" +#include "gpio.h" + +// You need to modify the MMIO base according to your kernel mapping +// mmio +// #define KVA 0xffff000000000000 +// #define MMIO_BASE (KVA + 0x3f000000) + +// SD card command +#define GO_IDLE_STATE 0 +#define SEND_OP_CMD 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 +#define VOLTAGE_CHECK_PATTERN 0x1aa +#define STOP_TRANSMISSION 12 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define WRITE_SINGLE_BLOCK 24 +#define SD_APP_OP_COND 41 +#define SDCARD_3_3V (1 << 21) +#define SDCARD_ISHCS (1 << 30) +#define SDCARD_READY (1 << 31) +#define APP_CMD 55 + +// gpio +#define GPIO_BASE (MMIO_BASE + 0x200000) +#define GPIO_GPFSEL4 (GPIO_BASE + 0x10) +#define GPIO_GPFSEL5 (GPIO_BASE + 0x14) +#define GPIO_GPPUD (GPIO_BASE + 0x94) +#define GPIO_GPPUDCLK1 (GPIO_BASE + 0x9c) + +// sdhost +#define SDHOST_BASE (MMIO_BASE + 0x202000) +#define SDHOST_CMD (SDHOST_BASE + 0) +#define SDHOST_READ 0x40 +#define SDHOST_WRITE 0x80 +#define SDHOST_LONG_RESPONSE 0x200 +#define SDHOST_NO_REPONSE 0x400 +#define SDHOST_BUSY 0x800 +#define SDHOST_NEW_CMD 0x8000 +#define SDHOST_ARG (SDHOST_BASE + 0x4) +#define SDHOST_TOUT (SDHOST_BASE + 0x8) +#define SDHOST_TOUT_DEFAULT 0xf00000 +#define SDHOST_CDIV (SDHOST_BASE + 0xc) +#define SDHOST_CDIV_MAXDIV 0x7ff +#define SDHOST_CDIV_DEFAULT 0x148 +#define SDHOST_RESP0 (SDHOST_BASE + 0x10) +#define SDHOST_RESP1 (SDHOST_BASE + 0x14) +#define SDHOST_RESP2 (SDHOST_BASE + 0x18) +#define SDHOST_RESP3 (SDHOST_BASE + 0x1c) +#define SDHOST_HSTS (SDHOST_BASE + 0x20) +#define SDHOST_HSTS_MASK (0x7f8) +#define SDHOST_HSTS_ERR_MASK (0xf8) +#define SDHOST_HSTS_DATA (1 << 0) +#define SDHOST_PWR (SDHOST_BASE + 0x30) +#define SDHOST_DBG (SDHOST_BASE + 0x34) +#define SDHOST_DBG_FSM_DATA 1 +#define SDHOST_DBG_FSM_MASK 0xf +#define SDHOST_DBG_MASK (0x1f << 14 | 0x1f << 9) +#define SDHOST_DBG_FIFO (0x4 << 14 | 0x4 << 9) +#define SDHOST_CFG (SDHOST_BASE + 0x38) +#define SDHOST_CFG_DATA_EN (1 << 4) +#define SDHOST_CFG_SLOW (1 << 3) +#define SDHOST_CFG_INTBUS (1 << 1) +#define SDHOST_SIZE (SDHOST_BASE + 0x3c) +#define SDHOST_DATA (SDHOST_BASE + 0x40) +#define SDHOST_CNT (SDHOST_BASE + 0x50) + +// helper +#define set(io_addr, val) \ + asm volatile("str %w1, [%0]" ::"r"(io_addr), "r"(val) : "memory"); + +#define get(io_addr, val) \ + asm volatile("ldr %w0, [%1]" : "=r"(val) : "r"(io_addr) : "memory"); + +static inline void delay(unsigned long tick) { + while (tick--) { + asm volatile("nop"); + } +} + +static int is_hcs; // high capcacity(SDHC) + +static void pin_setup() { + set(GPIO_GPFSEL4, 0x24000000); + set(GPIO_GPFSEL5, 0x924); + set(GPIO_GPPUD, 0); + delay(15000); + + set(GPIO_GPPUDCLK1, 0xffffffff); + delay(15000); + set(GPIO_GPPUDCLK1, 0); +} + +static void sdhost_setup() { + unsigned int tmp; + set(SDHOST_PWR, 0); + set(SDHOST_CMD, 0); + set(SDHOST_ARG, 0); + set(SDHOST_TOUT, SDHOST_TOUT_DEFAULT); + set(SDHOST_CDIV, 0); + set(SDHOST_HSTS, SDHOST_HSTS_MASK); + set(SDHOST_CFG, 0); + set(SDHOST_CNT, 0); + set(SDHOST_SIZE, 0); + get(SDHOST_DBG, tmp); + tmp &= ~SDHOST_DBG_MASK; + tmp |= SDHOST_DBG_FIFO; + set(SDHOST_DBG, tmp); + delay(250000); + set(SDHOST_PWR, 1); + delay(250000); + set(SDHOST_CFG, SDHOST_CFG_SLOW | SDHOST_CFG_INTBUS | SDHOST_CFG_DATA_EN); + set(SDHOST_CDIV, SDHOST_CDIV_DEFAULT); +} + +static int wait_sd() { + int cnt = 1000000; + unsigned int cmd; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_CMD, cmd); + --cnt; + } while (cmd & SDHOST_NEW_CMD); + return 0; +} + +static int sd_cmd(unsigned cmd, unsigned int arg) { + set(SDHOST_ARG, arg); + set(SDHOST_CMD, cmd | SDHOST_NEW_CMD); + return wait_sd(); +} + +static int sdcard_setup() { + unsigned int tmp; + sd_cmd(GO_IDLE_STATE | SDHOST_NO_REPONSE, 0); + sd_cmd(SEND_IF_COND, VOLTAGE_CHECK_PATTERN); + get(SDHOST_RESP0, tmp); + if (tmp != VOLTAGE_CHECK_PATTERN) { + return -1; + } + while (1) { + if (sd_cmd(APP_CMD, 0) == -1) { + // MMC card or invalid card status + // currently not support + continue; + } + sd_cmd(SD_APP_OP_COND, SDCARD_3_3V | SDCARD_ISHCS); + get(SDHOST_RESP0, tmp); + if (tmp & SDCARD_READY) { + break; + } + delay(1000000); + } + + is_hcs = tmp & SDCARD_ISHCS; + sd_cmd(ALL_SEND_CID | SDHOST_LONG_RESPONSE, 0); + sd_cmd(SEND_RELATIVE_ADDR, 0); + get(SDHOST_RESP0, tmp); + sd_cmd(SELECT_CARD, tmp); + sd_cmd(SET_BLOCKLEN, 512); + return 0; +} + +static int wait_fifo() { + int cnt = 1000000; + unsigned int hsts; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_HSTS, hsts); + --cnt; + } while ((hsts & SDHOST_HSTS_DATA) == 0); + return 0; +} + +static void set_block(int size, int cnt) { + set(SDHOST_SIZE, size); + set(SDHOST_CNT, cnt); +} + +static void wait_finish() { + unsigned int dbg; + do { + get(SDHOST_DBG, dbg); + } while ((dbg & SDHOST_DBG_FSM_MASK) != SDHOST_HSTS_DATA); +} + +// reads/writes 512 bytes from/to the SD card to/from buf[512]. +void readblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do { + set_block(512, 1); + sd_cmd(READ_SINGLE_BLOCK | SDHOST_READ, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + get(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while (!succ); + wait_finish(); +} + +// reads/writes 512 bytes from/to the SD card to/from buf[512]. +void writeblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do { + set_block(512, 1); + sd_cmd(WRITE_SINGLE_BLOCK | SDHOST_WRITE, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + set(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while (!succ); + wait_finish(); +} + +// set up GPIO, SD host, and initialize the SD card +void sd_init() { + pin_setup(); + sdhost_setup(); + sdcard_setup(); +} \ No newline at end of file diff --git a/lab8/kernel/sdhost.h b/lab8/kernel/sdhost.h new file mode 100644 index 000000000..6cbd70564 --- /dev/null +++ b/lab8/kernel/sdhost.h @@ -0,0 +1,8 @@ +#ifndef SDHOST_H +#define SDHOST_H + +void readblock(int block_idx, void* buf); +void writeblock(int block_idx, void* buf); +void sd_init(); + +#endif diff --git a/lab8/kernel/startup.s b/lab8/kernel/startup.s new file mode 100644 index 000000000..2e0dbe745 --- /dev/null +++ b/lab8/kernel/startup.s @@ -0,0 +1,252 @@ +.section ".text.boot" + +/* + lab8 requirement 1-1 Set up TCR_EL1 + Translation Control Register (TCR) 不同層級Translation table轉換的控制暫存器(EL0-EL1) + https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/TCR-EL1--Translation-Control-Register--EL1- + 設置頁面大小=4KB + 2^12 = 4KB, 須設定第0(T0SZ)及第16 bit(T1SZ), The size offset of the memory region addressed by TTBR0_EL1. The region size is 2^(64-T0SZ) bytes. +*/ + +.equ TCR_CONFIG_REGION_48bit, (((64 - 48) << 0) | ((64 - 48) << 16)) +.equ TCR_CONFIG_4KB, ((0b00 << 14) | (0b10 << 30)) +.equ TCR_CONFIG_DEFAULT, (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB) + +/* + lab8 requirement 1-2 Set up mair_el1 + Memory Attribute Indirection Register (MAIR) + 本實驗使用的記憶體屬性有 https://grasslab.github.io/NYCU_Operating_System_Capstone/labs/lab8.html#page-attr + G/nG: 多個memory訪問可以/無法合併, R/nR: 允許訪問指令可以/不可以重排,E/nE: 寫入操作ack必須直接目的地/可以是中間buffer + 設定Device為nGnRnE,最受限制,以及Normal為no cache +*/ + +.equ MAIR_DEVICE_nGnRnE, 0b00000000 +.equ MAIR_NORMAL_NOCACHE, 0b01000100 +.equ MAIR_IDX_DEVICE_nGnRnE, 0 +.equ MAIR_IDX_NORMAL_NOCACHE, 1 + +/* + lab8 requirement 1-3 Set up identity paging. + lab8 requirement 1-5 Linear map kernel with finer granularity and map RAM as normal memory. + + Entry of PGD, PUD, PMD which point to a page table + +----+----------------------------+---------+----+ + | | | igonred | 11 | + +----+----------------------------+---------+----+ + 47 12 2 0 + + Entry of PUD, PMD which point to a block + +----+----------------------------+---------+----+ + | | |attribute| 01 | + +----+----------------------------+---------+----+ + 47 12 2 0 + + Entry of PTE which point to a page + +----+----------------------------+---------+----+ + | | |attribute| 11 | + +----+----------------------------+---------+----+ + 47 12 2 0 + + 所以PD_TABLE = 0b11, PD_BLOCK = 0b01. PD_PAGE = 0b11 + + ● PGD: 因為PGD前面為igonred,所以 BOOT_PGD_ATTR, PD_TABLE + ● PUD: 前面為memory attribute + ● PTE: 前面為memory attribute + + Bits[10] + The access flag, a page fault is generated if not set. + + 因此PD_ACCESS 設為 (1 << 10) + + 依1-3需求說明,PUD為一個block且 Map all memory as Device nGnRnE,因此PUD的momery attribute設定為 + BOOT_PUD_ATTR, (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + + 後續1-5需求說明修正 + 更細分為PMD, PTE,PTE分為DEVICE_nGnRnE,以及Normal的 +*/ + +.equ PERIPHERAL_BASE, 0x3f000000 +.equ PAGE_TABLE_BASE, 0x30000000 +.equ PGD_BASE, (PAGE_TABLE_BASE + 0x0000) +.equ PUD_BASE, (PAGE_TABLE_BASE + 0x1000) +.equ PMD_BASE, (PAGE_TABLE_BASE + 0x2000) +.equ PTE_BASE, (PAGE_TABLE_BASE + 0x4000) + +.equ PD_TABLE, 0b11 +.equ PD_BLOCK, 0b01 +.equ PD_PAGE, 0b11 +.equ PD_ACCESS, (1 << 10) +.equ BOOT_PGD_ATTR, PD_TABLE +.equ BOOT_PUD_ATTR, PD_TABLE +.equ BOOT_PMD_ATTR, PD_TABLE +.equ BOOT_PTE_DEVICE_nGnRnE_ATTR, (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_PAGE) +.equ BOOT_PTE_NORMAL_NOCACHE_ATTR, (PD_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_PAGE) + +.global _start + +_start: + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, init + b busy_loop + +busy_loop: + wfe + b busy_loop + +init: + bl from_el2_to_el1 /* required 1-1 跳至 from_el2_to_el1,將excpetion level設為1 */ + bl set_exception_vector_table /* required 1-3 跳至 set_exception_vector_table,設定exception發生時,查找的vector_table */ + + bl init_mmu /* lab8需求, 初始化mmu */ + +boot_rest: + ldr x1, =__bss_start + ldr x2, =__bss_size + +loop_clear_bss: + cbz x2, entry_point + str xzr, [x1], #8 + sub x2, x2, #1 + cbnz x2, loop_clear_bss + +entry_point: + ldr x1, =_start + mov sp, x1 + bl main + b busy_loop + + +/* + + exception level 由 EL2 變 EL1 + + 默認情況下,Rpi3的CPU在默認啟動後便在EL2中運行,但是我們希望內核在EL1中運行。 + 因此,您的內核需要在開始時切換到EL1 + + 以下是助教提供的code + + 1. 設置hcr_el2暫存器(Hypervisor Configuration Register) + 這個暫存器大部分bit在reset狀態是0,只有bit 31是implementation defined + 因此設定bit 31 為1,確保在EL1 也是Aarch64的 + + 2. 設置spsr_el2暫存器(Saved Program Status Register) + 用於exception發生時儲存當時CPU狀態,這邊是做初始化 + 設定在AArch64下EL1h 和 D,A,I,F interrupt disabled + + 參照 https://kaiiiz.github.io/notes/nctu/osdi/lab3/exception-level-switch/ + http://www.lujun.org.cn/?p=1676 + 可以知道是 D,A,I,F 在9,8,7,6位,EL1h是101,所以是1111000101 = 0x3c5 + + 3. 設置elr_el2暫存器(Exception Link Register) + 用於exception發生時儲存處理後要返回的地址 + 這邊是做初始化,設為lr + + 4. 最後用eret,跳至EL1,之後就在EL1運行了 + +*/ + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 + mov x0, 0x3c5 // EL1h (SPSel = 1) with interrupt disabled + msr spsr_el2, x0 + msr elr_el2, lr + + // IMPORTANT: disable exceptions of accessing the SIMD and floating-point registers + mov x0, #(3 << 20) + msr cpacr_el1, x0 + + eret // return to EL1 + +/* + set vbar_el1 register to exception_vector_table address. + 1. exception_vector_table address to x0 register + 2. x0 register value to vbar_el1 status register +*/ +set_exception_vector_table: + ldr x0, =exception_vector_table + msr vbar_el1, x0 + ret + +init_mmu: + + /* lab8 requirement 1-1 Set up TCR_EL1 */ + + ldr x0, =TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + + /* lab8 requirement 1-2 Set up mair_el1 */ + + ldr x0, =((MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8))) + msr mair_el1, x0 + + /* + lab8 requirement 1-3 Set up identity paging. + lab8 requirement 1-4 Modify the linker script, and map the kernel space. + lab8 requirement 1-5 Linear map kernel with finer granularity and map RAM as normal memory. + */ + + ldr x0, =PGD_BASE // PGD's page frame at 0x0 + ldr x1, =PUD_BASE // PGD's page frame at 0x1000 + ldr x2, =PMD_BASE // PGD's page frame at 0x2000 + ldr x3, =PTE_BASE // PGD's page frame at 0x4000 + + /* step1: 設定PGD,為一個PD table,physical address指向PUD */ + ldr x4, =BOOT_PGD_ATTR + mov x5, x1 + orr x6, x5, x4 // combine the physical address of next level page with attribute. + str x6, [x0] + + /* step2: 設定 PUD,分兩塊,每塊1GB,physical address指向PMD */ + ldr x4, =BOOT_PUD_ATTR + mov x5, x2 + orr x6, x5, x4 + str x6, [x1] // 1st 1GB mapped by the 1st entry of PUD + add x5, x5, #0x1000 + orr x6, x5, x4 + str x6, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + /* step3: 設定PMD,分成1024塊,每塊2MB,physical address指向PTE */ + ldr x4, =BOOT_PMD_ATTR + mov x5, x3 + mov x7, x2 + mov x9, #(512 * 2) +set_PMD: + orr x6, x5, x4 + str x6, [x7] // 2MB block + sub x9, x9, #1 + add x7, x7, #8 + add x5, x5, #0x1000 + cbnz x9, set_PMD + + /* setp4: 設定PTE,分成512*512*2塊,每塊4KB,大部分 RAM 映射為普通記憶體,將 MMIO 區域映射為裝置記憶體 */ + ldr x4, =BOOT_PTE_NORMAL_NOCACHE_ATTR + mov x5, #0x00000000 + mov x7, x3 + mov x9, #(512 * 512 * 2) + ldr x10, =PERIPHERAL_BASE +set_PTE: + cmp x5, x10 + blt normal_mem + ldr x4, =BOOT_PTE_DEVICE_nGnRnE_ATTR +normal_mem: + orr x6, x5, x4 + str x6, [x7] // 4KB page + sub x9, x9, #1 + add x7, x7, #8 + add x5, x5, #(1 << 12) + cbnz x9, set_PTE + + /* setp5: 設定ttbr0_el1, ttbr1_el1 */ + msr ttbr0_el1, x0 // load PGD to the bottom translation based register. + msr ttbr1_el1, x0 // also load PGD to the upper translation based register. + + /* setp6: 啟動MMU */ + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + + /* setp7: 跳轉到boot_rest */ + ldr x2, =boot_rest // indirect branch to the virtual address + br x2 \ No newline at end of file diff --git a/lab8/kernel/thread.c b/lab8/kernel/thread.c new file mode 100644 index 000000000..21f5d7a0e --- /dev/null +++ b/lab8/kernel/thread.c @@ -0,0 +1,865 @@ +#include "uart.h" +#include "util.h" +#include "buddy.h" +#include "allocator.h" +#include "cpio.h" +#include "thread.h" +#include "mmu.h" +#include "buddy.h" + +struct thread *head, *tail; +int thread_count = 0; +struct dynamic_allocator *dyalloc = 0; + +/* +* init_thread +* +* initial thread parameter and memory parameter +*/ +void init_thread() +{ + head = tail = 0; + thread_count = 0; + + buddy_init(); + dyalloc = dynamic_allocator_init(); +} + +/* +* thread_test (requirment1 Test method) +* +* this code form TA, create 3 thread excute foo +* 1. create first thread and set its current thread +* 2. create N thread excute foo, N should > 2 +* 3. idle +*/ +void thread_test() +{ + // create first thread for context_switch + struct thread *first_thread = thread_create(0); + set_current(first_thread); + + for(int i = 0; i < 3; ++i) + { + thread_create(foo); + } + + idle(); +} + +/* +* foo (requirment1 Test method) +* +* this code form TA, create a thread excute foo +* 1. for loop 0-9 +* 2. print message and call schedule to next thread +* 3. delay 1000000 +* 4. call schedule switch to next running thread +* 5. when finish, call exit to end of a thread +*/ +void foo() +{ + char buf[16] = {0}; + + for(int i = 0; i < 10; ++i) + { + // print message, ex. Thread id: 1, index:3 + uart_putstr("Thread id: "); + unsignedlonglongToStr(current_thread()->tid, buf); + uart_putstr(buf); + uart_putstr(", index: "); + unsignedlonglongToStr(i, buf); + uart_putstr(buf); + uart_putstr("\n"); + + delay_count(1000000); + schedule(); + } + + // when finish, call exit to end of a thread + exit(); +} + +/* +* thread_create (requirment Creating a Thread) +* +* 1. create new thread +* - allocate thread momoery +* - fp = Frame pointer = start address + THREAD_SIZE +* - lr = link register for function calls. +* - sp = stack pointer = start address + THREAD_SIZE +* - other parameter +* +* 2. add new thread to run queue +* 3. return new_thread for fist initinal +*/ +struct thread* thread_create(void* func) +{ + // create new thread + struct thread* new_thread = (struct thread*)dynamic_alloc(dyalloc, THREAD_SIZE); + // for lab8 req2 + new_thread->kernel_stack_base = thread_allocate_page(new_thread, THREAD_SIZE); + new_thread->user_stack_base = 0; + new_thread->context.fp = new_thread->kernel_stack_base + THREAD_SIZE; + new_thread->context.lr = (unsigned long)func; + new_thread->context.sp = new_thread->kernel_stack_base + THREAD_SIZE; + // + new_thread->tid = thread_count++; + new_thread->status = TASK_RUNNING; + new_thread->next = 0; + + // for requirment 2 + new_thread->program_addr = 0; + new_thread->program_size = 0; + new_thread->childID = 0; + + // for lab6 requirment2 + for (int i = 0; i < FD_MAX; ++i) + new_thread->fd_table.files[i] = 0; + + // for lab8 req2 + unsigned long *pgd; + asm volatile("mrs %0, ttbr1_el1" : "=r"(pgd)); + new_thread->pgd = pgd; + for (int i = 0; i < MAX_PAGE_FRAME_PER_THREAD; i++) + new_thread->page_frame_ids[i] = 0; + new_thread->page_frame_count = 0; + + // add new thread to run queue + add_to_run_queue(new_thread); + + return new_thread; +} + +/* +* add_to_run_queue (requirment Creating a Thread) +* +* head : pointer to run queue start +* tail : pointer to run queue end +* +* add thread to tail->next +*/ +void add_to_run_queue(struct thread* new_thread) +{ + if(head == 0) + head = tail = new_thread; + else + { + tail->next = new_thread; + tail = new_thread; + } +} + +/* +* current_thread +* +* call get_current method get cureent thread (context_switch.s) +*/ +struct thread* current_thread() +{ + return get_current(); +} + +/* +* schedule (requirment Scheduler and Context Switch) +* +* pointer to next thread and do context_switch +* 1. if head = 0 represent run queue is empty, return +* 2. if head = tail represent run queue only first thread, free first thread and init +* 3. if run queue thread count > 1, pointer to next thread and do context switch +* +*/ +void schedule() +{ + if(head == 0) // run queue is empty + return; + + if(head == tail && thread_count > 1) // run queue only first thread + { + dynamic_free(dyalloc, (unsigned long)head); + head = tail = 0; + thread_count = 0; + return; + } + + // pointer to next thread and do context switch + // + // head _________ + // | | + // head tail | ▼ + // _____ _____ _____ _____ _____ _____ + // | | | | | | | | | + // | | | | => | | x | | | + // ------------------- ------- ------------- + // ▲ | + // |________________| tail + do + { + tail->next = head; + tail = head; + head = head->next; + tail->next = 0; + } while(head->status != TASK_RUNNING); + + switch_pgd((unsigned long)(head->pgd)); // for lab8 req2 + // put [current_thread] and [next_thread] do thread context switch + switch_to(current_thread(), head); +} + +/* +* kill_zombies (requirment The Idle Thread) +* +* scan run queue, from head to tail. recycle not use(dead) thread. +* 1. ptr = current thread pointer +* 2. if ptr = 0 represent run queue is empty, return +* 3. if next thread status is dead, free next thread, +* current thread's next pointer to ptr->next->next +* 4. if next thread status isn't dead, ptr = ptr->next +*/ +void kill_zombies() +{ + struct thread* ptr = head; + struct thread* tmp = 0; + + if(ptr == 0) // empty + return; + + while(1) + { + // ptr ________________ + // | | + // ptr | ▼ + // _____ _____ _____ _____ _____ _____ + // | | | | | | | | | | + // | |dead | | => | | |dead | | | + // ------------------- ------- ----- ----- + // free + // + while(ptr->next != 0 && ptr->next->status == TASK_DEAD) + { + tmp = ptr->next->next; + dynamic_free(dyalloc, (unsigned long)ptr->next); + ptr->next = tmp; + } + + // if next thread status isn't dead, ptr = ptr->next + if(ptr->next != 0) + { + ptr = ptr->next; + } + else + { + tail = ptr; + break; + } + } +} + +/* +* idle (requirment The Idle Thread) +* +* this code form TA, always run kill_zombies and schedule +*/ +void idle() +{ + while(1) + { + kill_zombies(); // reclaim threads marked as DEAD + do_fork(); // for requirment 2 + schedule(); // switch to any other runnable thread + + if (head == 0 && tail == 0) + return; + } +} + +void delay_count(int count) +{ + for(int i = 0; i < count; i++) + asm volatile("nop"); +} + +/* +* exit (requirment End of a Thread) +* +* When a thread finishes its jobs, set status to TASK_DEAD +* and call schedule to next running thread +*/ +void exit() +{ + struct thread *cur = current_thread(); + thread_free_page(cur); // for lab8 req2 + cur->status = TASK_DEAD; + schedule(); +} + +//======================================================================= + +void init_thread2() +{ + +} + +/* +* thread_test2 (requirment2 Test method) +* +* this code form TA, create thread excute user_test +* 1. create first thread and set its current thread +* 2. create thread excute user_test +* 3. idle +*/ +void thread_test2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + + thread_create(user_test); + idle(); +} + +/* +* user_test (requirment2 Test method) +* +* this code form TA, Execute user program with arguments +* 1. Declare arguments +* 2. call exec function create thread and excute user program +*/ +void user_test() +{ + char argv[4][10] = { "argv_test", "-o", "arg2", ""}; + exec("argv_test.img", argv); +} + +/* +* exec (requirment2 Arguments Passing) +* +* Execute user program with arguments +* 1. Declare user program excute address, + Because we not MMU, so differnet user program should use different address +* 2. load_program_with_args +* 3. when finish, call exit to end of a thread +*/ +void exec(const char* name, char(*argv)[10]) +{ + //unsigned long addr = generate_user_addr(); + load_program_with_args(name, argv, head); + + // when finish, call exit to end of a thread + exit(); +} + +/* +* load_program_with_args (requirment2 Arguments Passing) +* +* call cpio load user program, and pass argument to stack, finally run user program on EL0 +* 1. call cpio_load_user_program_and_get_size, load user program and return program size +* 2. call pass_argument , pass argument to stack +* 3. set program address, program size +* 4. run user program on EL0 (EL1 -> EL0) +*/ +void load_program_with_args(const char* name, char(*argv)[10], struct thread* current) +{ + // 使用相同的虛擬位址,映射為不同的實體位址 + // 包含(user program位址, user stack地址) + + // 1. 先分配user program位址, user stack的虛擬位址,以及初始化page table + if (current->program_addr == 0) + { + current->program_addr = thread_allocate_page(current, USER_PROGRAM_SIZE); + current->user_stack_base = thread_allocate_page(current, THREAD_SIZE); + init_page_table(current, &(current->pgd)); + } + + // 2. 更新page table,並將user program位址,由虛擬位址轉實體位址 + current->program_size = cpio_load_user_program_and_get_size((char*)name, current->program_addr); + for (unsigned long size = 0; size < current->program_size; size += PAGE_SIZE) + { + unsigned long virtual_addr = USER_PROGRAM_BASE + size; + unsigned long physical_addr = VA2PA(current->program_addr + size); + update_page_table(current, virtual_addr, physical_addr, 0b101); + } + + // 3. 更新page table,並將user stack位址,由虛擬位址轉實體位址 + unsigned long virtual_addr = USER_STACK_BASE; + unsigned long physical_addr = VA2PA(current->user_stack_base); + update_page_table(current, virtual_addr, physical_addr, 0b110); + + // 4. 透過切換不同的page table,執行不同的user program (2-4需求) + unsigned long next_pgd = (unsigned long)current->pgd; + switch_pgd(next_pgd); + + unsigned long user_sp = USER_STACK_BASE + THREAD_SIZE; + + unsigned long sp_addr = pass_argument(argv, user_sp); + + // run user program on EL0 (EL1 -> EL0) + asm volatile("mov x0, 0x340 \n\t"); // 340 = 1101000000, irq enable + asm volatile("msr spsr_el1, x0 \n\t"); // spsr_el1 = 0x340 + asm volatile("msr elr_el1, %0 \n\t"::"r"(USER_PROGRAM_BASE)); // elr_el1 = user program load address + asm volatile("msr sp_el0, %0 \n\t"::"r"(sp_addr)); // sp_el0 = stack address + asm volatile("mrs x3, sp_el0 \n\t"); // 將sp_el0給x3 + asm volatile("ldr x0, [x3, 0] \n\t"); // x0存放(x3+0),參考下圖,也就是argc的位置 + asm volatile("ldr x1, [x3, 8] \n\t"); // x1存放(x3+8),參考下圖,也就是argv的位置 + asm volatile("eret \n"); +} + +/* +* pass_argument (requirment2 Arguments Passing) +* +* pass argument to user stack, and return user stack address (aligment 16) +* 1. calc argv(argc), and argv byte count +* 2. calc offset, notice sp should aligment 16 +* 3. pass argument to user stack +* 4. return stack address +*/ +unsigned long pass_argument(char(*argv)[10], unsigned long addr) +{ + /* user stack + * + * Low ----------------------------------------------------------------------> High + * ______________ + * _________________|_____________▼______________________________________________ + * | | | | | | | | + * | | | | | | | | + * | 2(argc) | char** argv | char* argv[0] | char* argv[1] | NULL | a.out | arg1 | + * | | | | | | | | + * |_________|_____________|_______________|_______________|______|_______|______| + * | | ▲ ▲ + * ------------------------------------- | + * |__________________________| + */ + + // // 1.計算參數數量(argc), 以及參數各有幾個byte(argv byte count) + // 例如{"argv_test", "-o", "arg2", ""},共有4個參數,18個byte(含結束符號\0) + int argc = 0, argv_count = 0; + for(int i = 0;;++i) + { + ++argc; + if(strlen(argv[i]) == 0) + break; + + for(int j = 0;; ++j) + { + ++argv_count; + if(argv[i][j] == 0) + break; + } + } + + // 2. calc offset, notice sp should aligment 16 + // 參考上圖,第一個放argc,然後放argv的地址,接著放參數位址,然後放每一個參數的內容 + // 因此,會先計算參數所佔的大小是多少,然後載入位(addr) – 參數所佔大小(offset) + // 就是我們的user stack的位址,記得要對齊16 + int offset = (1 + 1 + argc) * 8 + argv_count; + addr = addr - offset; + addr = addr - (addr & 15); // aligment 16 + + // 3. 將參數塞入user stack + // 3.1 起始位置 = addr + char* data = (char*)addr; + // 3.2 塞入argc的數量,然後位址+8 + *(unsigned long*)data = argc - 1; // 不包含最後一個NULL + data += 8; + // 3.3 塞入argv的地址後,位址+8 + *(unsigned long*)data = (unsigned long)(data + 8); //argv addr + data += 8; + // 3.4 位址 += 參數數量*8 + char* argv_buf = data + 8 * argc; + // 3.5 塞入參數的內容 + for(int i = 0; i < argc - 1; ++i) + { + *(unsigned long*)data = (unsigned long)argv_buf; + for(int j = 0;;++j) + { + *argv_buf = argv[i][j]; + ++argv_buf; + if(argv[i][j] == 0) + break; + } + // 塞完後位址+8 + data += 8; + } + // 結束放0 + *(unsigned long*)data = 0; + + return addr; +} + +/* +* get_pid (requirment2) +* +* get current thread id +*/ +unsigned long get_pid() +{ + return current_thread()->tid; +} + +/* +* fork (requirment2) +* +* 1. set current thread status = fork +* 2. call schedule +* 3. return child id +*/ +int fork() +{ + head->status |= TASK_FORK; + schedule(); + return head->childID; +} + +/* +* do_fork (requirment2) +* +* 1. from head to tail,search status = fork thread +* 2. create new thread +* 3. copy current thread to new thread +*/ +void do_fork() +{ + for(struct thread* ptr = head->next; ptr != 0; ptr = ptr->next) + { + if((ptr->status) & TASK_FORK) + { + struct thread* child = thread_create(0); + copy_program(ptr, child); + } + } +} + +/* +* copy_program (requirment2) +* +* copy parent thread to new thread +* 1. set parent status and child id +* 2. copy parent thread to child thread +* 3. set +* 4. copy parent statck to child stack +* +*/ +void copy_program(struct thread* parent, struct thread* child) +{ + parent->status ^= TASK_FORK; + parent->childID = child->tid; + + // 1. 一樣先分配child program位址,child user stack的虛擬位址,以及初始化page table + child->user_stack_base = thread_allocate_page(child, THREAD_SIZE); + child->program_addr = thread_allocate_page(child, USER_PROGRAM_SIZE); + child->program_size = parent->program_size; + child->childID = 0; + init_page_table(child, &(child->pgd)); + + // 2. 更新page table,並將user program位址,由虛擬位址轉實體位址 + for (unsigned long size = 0; size < child->program_size; size += PAGE_SIZE) + { + unsigned long virtual_addr = USER_PROGRAM_BASE + size; + unsigned long physical_addr = VA2PA(child->program_addr + size); + update_page_table(child, virtual_addr, physical_addr, 0b101); + } + //3. 更新page table,並將user stack位址,由虛擬位址轉實體位址 + unsigned long virtual_addr = USER_STACK_BASE; + unsigned long physical_addr = VA2PA(child->user_stack_base); + update_page_table(child, virtual_addr, physical_addr, 0b110); + + struct thread* child_next = child->next; + unsigned long org_tid = child->tid; + + // 4. 將parent thread內容複製給child (包含context,kernel stack,user stack,program) + + char* src = (char*) parent; + char* dst = (char*) child; + for(int i = 0; i < THREAD_SIZE; ++i) + { + *dst = *src; + src++; + dst++; + } + + child->next = child_next; + child->childID = 0; + child->tid = org_tid; + + // copy saved context in thread info + src = (char *)&(parent->context); + dst = (char *)&(child->context); + for (int i = 0; i < sizeof(struct cpu_context); ++i, ++src, ++dst) + *dst = *src; + + // copy kernel stack + src = (char *)(parent->kernel_stack_base); + dst = (char *)(child->kernel_stack_base); + for (int i = 0; i < THREAD_SIZE; ++i, ++src, ++dst) + *dst = *src; + + // copy user stack + src = (char *)(parent->user_stack_base); + dst = (char *)(child->user_stack_base); + for (int i = 0; i < THREAD_SIZE; ++i, ++src, ++dst) + *dst = *src; + + // copy user program + src = (char *)(parent->program_addr); + dst = (char *)(child->program_addr); + for (int i = 0; i < parent->program_size; ++i, ++src, ++dst) + *dst = *src; + + + // 5. 注意在MMU之下,child的sp, user sp, lr等等不用再跟父程式使用不同位址,所以無須加減offset + unsigned long reg_addr_diff = (unsigned long)child - (unsigned long)parent; + //unsigned long p_diff = (unsigned long)child->program_addr - (unsigned long)parent->program_addr; + child->context.fp += reg_addr_diff; // fp + child->context.sp += reg_addr_diff; // sp + //child->context.elr_el1 += p_diff; //elr_el1 + //child->context.sp_el0 += p_diff; //sp_el0 + //child->context.reg[29] += p_diff; //user_fp + //child->context.reg[30] += p_diff; //user_lr +} + +//======================================================================= + +/* +* thread_vfs_req2 (lab6 requirment2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute vfs_req2 +* 3. idle +*/ +void thread_vfs_req2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(vfs_req2); + idle(); +} + +/* +* vfs_req2 (requirment2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void vfs_req2() +{ + char argv[2][10] = { "vfs_test", ""}; + exec("vfs_test.img", argv); +} + +/* +* thread_get_file (requirment2 Test method) +* +* 輸入index,回傳目前thread的 file descriptor table中指定index的file +*/ +struct file *thread_get_file(int fd) +{ + struct thread *cur = current_thread(); + return cur->fd_table.files[fd]; +} + +/* +* thread_register_fd (requirment2 Test method) +* +* 傳入file,找目前thread file descriptor table中未使用的index加入,並回傳index +*/ +int thread_register_fd(struct file *file) +{ + if (file == 0) + return -1; + + struct thread *cur = current_thread(); + // find next available fd + for (int fd = 0; fd < FD_MAX; ++fd) + { + if (cur->fd_table.files[fd] == 0) + { + cur->fd_table.files[fd] = file; + return fd; + } + } + + return -1; +} + +/* +* thread_clear_fd (requirment2 Test method) +* +* 輸入index,清空目前thread的 file descriptor table中指定index的內容 +* 並回傳成功或失敗 +*/ +int thread_clear_fd(int fd) +{ + if (fd < 0 || fd >= FD_MAX) + return -1; + + struct thread *cur = current_thread(); + cur->fd_table.files[fd] = 0; + + return 1; +} + +//======================================================================= + +/* +* thread_vfs_ele1 (lab6 elective1 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute vfs_ele1 +* 3. idle +*/ +void thread_vfs_ele1() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(vfs_ele1); + idle(); +} + +/* +* vfs_ele1 (elective1 Test method) +* +* 1. Declare arguments, list directory set current directory "." +* 2. call exec function excute user program +*/ +void vfs_ele1() +{ + char argv[4][10] = { "ls_test", ".", "arg1", ""}; + exec("ls_test.img", argv); +} + +//======================================================================= + +/* +* thread_vfs_ele2 (lab6 elective2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute vfs_ele2 +* 3. idle +*/ +void thread_vfs_ele2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(vfs_ele2); + idle(); +} + +/* +* vfs_ele2 (elective2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void vfs_ele2() +{ + char argv[2][10] = { "multilvl", ""}; + exec("multilvl_test.img", argv); +} + +//======================================================================= + +/* +* thread_fat32_req1 (lab7 requirment1 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute fat32_req1 +* 3. idle +*/ +void thread_fat32_req1() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(fat32_req1); + idle(); +} + +/* +* fat32_req1 (requirment1 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void fat32_req1() +{ + char argv[2][10] = { "fat32", ""}; + exec("fat32_test.img", argv); +} + +//======================================================================= + +/* +* thread_fat32_req2 (lab7 requirment2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute fat32_req2 +* 3. idle +*/ +void thread_fat32_req2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(fat32_req2); + idle(); +} + +/* +* fat32_req2 (requirment2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void fat32_req2() +{ + char argv[2][10] = { "fat32", ""}; + exec("fat32_test2.img", argv); +} + +//======================================================================= + +unsigned long thread_allocate_page(struct thread *thread, unsigned long size) +{ + int page_id = buddy_alloc(size); + thread->page_frame_ids[thread->page_frame_count++] = page_id; + return (MEMORY_START + page_id * PAGE_SIZE); +} + +void thread_free_page(struct thread *thread) +{ + for (int i = 0; i < thread->page_frame_count; i++) + buddy_free(thread->page_frame_ids[i]); +} + +void switch_pgd(unsigned long next_pgd) +{ + asm volatile("dsb ish"); // ensure write has completed + asm volatile("msr ttbr0_el1, %0"::"r"(next_pgd)); // 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 +} + +/* +* thread_mmu_req2 (lab8 requirment2 Test method) +* +* 1. create first thread and set its current thread +* 2. create thread excute mmu_req2 +* 3. idle +*/ +void thread_mmu_req2() +{ + struct thread *first_thread = thread_create(0); + set_current(first_thread); + thread_create(mmu_req2); + idle(); +} + +/* +* mmu_req2 (requirment2 Test method) +* +* 1. Declare arguments +* 2. call exec function excute user program +*/ +void mmu_req2() +{ + char argv[2][10] = { "mmu_test", ""}; + exec("mmu_test.img", argv); +} \ No newline at end of file diff --git a/lab8/kernel/thread.h b/lab8/kernel/thread.h new file mode 100644 index 000000000..d2672aa43 --- /dev/null +++ b/lab8/kernel/thread.h @@ -0,0 +1,125 @@ +#ifndef THREAD_H +#define THREAD_H + +#include "vfs.h" + +#define THREAD_SIZE 0x1000 +#define TASK_RUNNING 0 +#define TASK_DEAD 1 +#define TASK_FORK 2 + +// for lab8 req2 +#define USER_STACK_BASE ((unsigned long)0x1000000000000 - THREAD_SIZE) +#define USER_PROGRAM_BASE 0x80000 +#define USER_PROGRAM_SIZE (16 * (unsigned long)1024) +#define MAX_PAGE_FRAME_PER_THREAD 1000 + +// for lab6 req2 +#define FD_MAX 256 + +struct fd_table_t +{ + struct file *files[FD_MAX]; +}; + +// kerenl stack context (use for switch_to function in context_swtich.s) +// https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard +struct cpu_context +{ + // context to kernel-stack (x19 to x28, fp(x29), lr(x30), sp) + // only use x19 to x28: Callee-saved register + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; // x29 + unsigned long lr; // x30 + unsigned long sp; // sp contain x0-x18 + + // for user program (spsr_el1, elr_el1, sp_el0)(user register x0-x30) + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; + unsigned long reg[31]; + +}; + +// thread struct +struct thread +{ + struct cpu_context context; + unsigned long tid; // thread id + int status; + struct fd_table_t fd_table; // for lab6 req2 + struct thread* next; + + unsigned long program_addr; + unsigned long program_size; + unsigned long childID; + + // for lab8 req2 + unsigned long kernel_stack_base; + unsigned long user_stack_base; + unsigned long *pgd; + unsigned int page_frame_ids[MAX_PAGE_FRAME_PER_THREAD]; + unsigned int page_frame_count; +}; + +// outer function in context_swtich.s +extern void switch_to(struct thread* prev, struct thread* next); +extern struct thread* get_current(); +extern void set_current(struct thread* current); + +void init_thread(); +void thread_test(); +void foo(); +struct thread* thread_create(void* func); +void add_to_run_queue(struct thread* new_thread); +struct thread* current_thread(); +void schedule(); +void kill_zombies(); +void idle(); +void delay_count(int count); +void exit(); +// +void init_thread2(); +void thread_test2(); +void user_test(); +void exec(const char* name, char(*argv)[10]); +void load_program_with_args(const char* name, char(*argv)[10], struct thread* current); +unsigned long pass_argument(char(*argv)[10], unsigned long addr); +unsigned long get_pid(); +int fork(); +void do_fork(); +void copy_program(struct thread* parent, struct thread* child); +// for lab6 req2 +void thread_vfs_req2(); +void vfs_req2(); +struct file *thread_get_file(int fd); +int thread_register_fd(struct file *file); +int thread_clear_fd(int fd); +// for lab6 ele1 +void thread_vfs_ele1(); +void vfs_ele1(); +// for lab6 ele2 +void thread_vfs_ele2(); +void vfs_ele2(); +// for lab7 req1 +void thread_fat32_req1(); +void fat32_req1(); +// for lab7 req2 +void thread_fat32_req2(); +void fat32_req2(); +// for lab8 req2 +unsigned long thread_allocate_page(struct thread *thread, unsigned long size); +void thread_free_page(struct thread *thread); +void switch_pgd(unsigned long next_pgd); +void thread_mmu_req2(); +void mmu_req2(); +#endif \ No newline at end of file diff --git a/lab8/kernel/timer.c b/lab8/kernel/timer.c new file mode 100644 index 000000000..9d22b7b74 --- /dev/null +++ b/lab8/kernel/timer.c @@ -0,0 +1,193 @@ +#include "uart.h" +#include "util.h" +#include "timer.h" + +/* +* get_excute_time +* +* cntpct_el0: The timer’s current count. +* cntfrq_el0: the frequency of the timer +* +* excute time = cntpct_el0 / cntfrq_el0 +*/ +unsigned long long get_excute_time() +{ + unsigned long long cntpct_el0, cntfrq_el0; + asm volatile("mrs %0, cntpct_el0" : "=r"(cntpct_el0)); + asm volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq_el0)); + + return cntpct_el0 / cntfrq_el0; +} + +/* +* set_next_timeout +* +* cntp_tval_el0: (cntp_cval_el0 - cntpct_el0). You can use it to set an expired timer after the current timer count. +* cntfrq_el0: the frequency of the timer +* +* we set cntp_cval_el0 = timer frequency * second = next interrupt timeout +*/ +void set_next_timeout(unsigned int second) +{ + unsigned long cntfrq_el0; + asm volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq_el0)); + asm volatile("msr cntp_tval_el0, %0" : : "r"(cntfrq_el0 * second)); +} + +/* +* core time interrupt handle +* +* 1. set next timeout = 2s later +* 2. print excute time +*/ +void core_timer_handle() +{ + set_next_timeout(2); + + uart_putstr("seconds after booting: "); + + char buf[16] = {0}; + unsignedlonglongToStr(get_excute_time(), buf); + uart_putstr(buf); + uart_putstr("seconds\n"); +} + +////////////////////////////// timeout /////////////////////////////// + +timeout timeout_buffer[max_queue_size]; +timeout *timeout_queue; +int buffer_index; + +void init_timeout() +{ + for(int i = 0; i < max_queue_size; i++) + { + timeout_buffer[i].startTime = 0; + timeout_buffer[i].duration = 0; + timeout_buffer[i].next = 0; + } + + timeout_queue = 0; + buffer_index = 0; +} +void add_timer(void (*callback)(char *), char* msg, int duration) +{ + // + // set new timeout (in last) + // + + unsigned long long currentTime = get_excute_time(); + + timeout_buffer[buffer_index].startTime = currentTime; + timeout_buffer[buffer_index].duration = duration; + timeout_buffer[buffer_index].callback = callback; + for(int i = 0; i < msg_size; i++) + { + timeout_buffer[buffer_index].msg[i] = msg[i]; + if(msg[i] == '\0') + break; + } + + // + // find proper location insert (by end time) + // + + // 1. find position + timeout *prev = 0, *cur = 0; + for(cur = timeout_queue; cur != 0; prev = cur, cur = cur->next) + { + if(cur->startTime + cur->duration > currentTime + duration) + break; + } + + // empty + if(prev == 0 && cur == 0) + { + core_timer_enable(); + timeout_queue = &timeout_buffer[buffer_index]; + set_next_timeout(timeout_queue->duration); + } + // new timeout end time > timeout_queue end time + else if(prev != 0 && cur == 0) + { + prev->next = &timeout_buffer[buffer_index]; + } + // new timeout end time < timeout_queue end time + else if (prev == 0 && cur != 0) + { + timeout_buffer[buffer_index].next = cur; + timeout_queue = &timeout_buffer[buffer_index]; + set_next_timeout(timeout_queue->duration); + } + else + { + timeout_buffer[buffer_index].next = cur; + prev->next = &timeout_buffer[buffer_index]; + } + + // if large max size, index = 0 + for(int i = 0; i < max_queue_size && timeout_buffer[buffer_index].startTime > 0; i++) + { + if(++buffer_index == max_queue_size) + buffer_index = 0; + } + +} + +/* +* print_timer_msg +* +* start excute time: 5s, end excute time: 7s, duration time: 2s, message: text +* +*/ +void print_timer_msg(char* msg) +{ + char buf[16] = {0}; + + uart_putstr("start excute time: "); + unsignedlonglongToStr(timeout_queue->startTime, buf); + uart_putstr(buf); + uart_putstr("s, end excute time: "); + unsignedlonglongToStr(get_excute_time(), buf); + uart_putstr(buf); + uart_putstr("s, duration time: "); + unsignedlonglongToStr(timeout_queue->duration, buf); + uart_putstr(buf); + uart_putstr("s, message: "); + uart_putstr(msg); + uart_putstr("\n"); +} +/* +* timout_handle +* +* 1. if queue is empty, disable core timer and return +* 2. if queue not empty +* 2.1 excute call back +* 2.2 point to next +* 3. if next not empty +* next timeout = remainder time = alltime - currentTime +* 4. if next is empty, disable core timer +* +*/ +void timout_handle() +{ + if(timeout_queue == 0) + { + core_timer_disable(); + return; + } + + // excute callback print messgae + timeout_queue->callback(timeout_queue->msg); + timeout_queue->startTime = -1; + timeout_queue = timeout_queue->next; + + if(timeout_queue != 0) + { + unsigned long long expire_time = timeout_queue->startTime + timeout_queue->duration - get_excute_time(); + set_next_timeout(expire_time); + } + else + core_timer_disable(); + return; +} diff --git a/lab8/kernel/timer.h b/lab8/kernel/timer.h new file mode 100644 index 000000000..6308bc69d --- /dev/null +++ b/lab8/kernel/timer.h @@ -0,0 +1,29 @@ +#ifndef TIMER_H +#define TIMER_H + +extern void core_timer_enable(); +extern void core_timer_disable(); + +unsigned long long get_excute_time(); +void set_next_timeout(unsigned int second); +void core_timer_handle(); + + +#define max_queue_size 30 +#define msg_size 20 + +typedef struct timeout +{ + unsigned long long startTime; + unsigned long long duration; + char msg[msg_size]; + void (*callback)(char *); + struct timeout *next; +} timeout; + +void init_timeout(); +void add_timer(void (*callback)(char *), char *msg, int duration); +void print_timer_msg(char *msg); +void timout_handle(); + +#endif \ No newline at end of file diff --git a/lab8/kernel/tmpfs.c b/lab8/kernel/tmpfs.c new file mode 100644 index 000000000..916731e97 --- /dev/null +++ b/lab8/kernel/tmpfs.c @@ -0,0 +1,279 @@ +#include "tmpfs.h" +#include "util.h" +#include "allocator.h" + +struct vnode_operations* tmpfs_v_ops; +struct file_operations* tmpfs_f_ops; + +struct dynamic_allocator *dyalloc_fs = 0; + +/* +* tmpfs_init 初始化tmpfs +* +* 1. 初始化v_node操作(分配記憶體,註冊查找、建立的方法) +* 2. 初始化file操作(分配記憶體,註冊寫入、讀取的方法) +*/ +void tmpfs_init() +{ + dyalloc_fs = dynamic_allocator_init(); + + tmpfs_v_ops = (struct vnode_operations*)dynamic_alloc(dyalloc_fs, sizeof(struct vnode_operations)); + tmpfs_v_ops->lookup = tmpfs_lookup; + tmpfs_v_ops->create = tmpfs_create; + tmpfs_v_ops->set_parent = tmpfs_set_parent; + + tmpfs_f_ops = (struct file_operations*)dynamic_alloc(dyalloc_fs, sizeof(struct file_operations)); + tmpfs_f_ops->write = tmpfs_write; + tmpfs_f_ops->read = tmpfs_read; + tmpfs_f_ops->list = tmpfs_list; +} + +/* +* tmpfs_init 實作mount方法 (掛載檔案系統並建立root node) +* +* 傳入要掛載上去的檔案系統,以及mount物件 +* +* 1. 分配root node及root node內容的記憶體 +* 2. 初始化root node +* 3. 初始化node內容 +* 4. 設定mount屬性 +* 5. 回傳成功 +*/ +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount) +{ + /* mount結構包含 fs 跟 root屬性 + * root是一個v_node結構,包含 mount、tmpfs_v_ops、tmpfs_f_ops、internal屬性 + * internal是tmpfs_entry結構,包含name、type、vnode、parent_vnode、child、buf屬性 + */ + + // 1. 分配root node及node內容的記憶體 + struct tmpfs_entry* root_entry = (struct tmpfs_entry*)dynamic_alloc(dyalloc_fs, sizeof(struct tmpfs_entry)); + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fs, sizeof(struct vnode)); + // 2. 初始化root node + vnode->mount = mount; + vnode->v_ops = tmpfs_v_ops; + vnode->f_ops = tmpfs_f_ops; + vnode->internal = (void*)root_entry; + // 3. 初始化node內容(internal) + root_entry->parent_vnode = 0; // 因為掛載系統,所以沒有parent_node,自己就是根目錄 + tmpfs_set_entry(root_entry, "/", FILE_DIRECTORY, vnode); + // 4. 初始化mount + mount->fs = fs; + mount->root = vnode; + // 5. 回傳成功 + return 1; +} + +/* +* tmpfs_set_entry 設定節點內容 +* +* tmpfs_entry結構,包含name、type、vnode、parent_vnode、child、buf屬性 +* 1. 設定name +* 2. 設定type +* 3. 設定vnode +* 4. parent_vnode外面設定 +* 5. 分配buf記憶體,並設定buf內容為空 +* 6. 假如節點為目錄,初始化目錄下子檔案,且初始化buffer目前使用size = TMPFS_BUF_SIZE(表示滿了) +* 7. 假如節點為檔案,buffer目前使用size設為0, +*/ +void tmpfs_set_entry(struct tmpfs_entry* entry, const char* component_name, enum FILE_TYPE type, struct vnode* vnode) +{ + strcpy(entry->name, component_name); + + entry->type = type; + entry->vnode = vnode; + entry->buf = (struct tmpfs_buf*)dynamic_alloc(dyalloc_fs, sizeof(struct tmpfs_buf)); + for (int i = 0; i < TMPFS_BUF_SIZE; i++) + entry->buf->buffer[i] = '\0'; + + if (entry->type == FILE_DIRECTORY) + { + for (int i = 0; i < MAX_FILES_IN_DIR; ++i) + { + entry->child[i] = (struct tmpfs_entry*)dynamic_alloc(dyalloc_fs, sizeof(struct tmpfs_entry)); + entry->child[i]->name[0] = 0; + entry->child[i]->type = FILE_NONE; + entry->child[i]->parent_vnode = vnode; + } + + entry->buf->size = TMPFS_BUF_SIZE; + } + else if (entry->type == FILE_REGULAR) + { + entry->buf->size = 0; + } +} + +/* +* tmpfs_create 實作create方法 +* +* 傳入目前目錄,(目標node,名稱,type),在目錄下建立node +* +* 對於目錄下的每一個子節點,尋找檔案類型為無的子節點建立node +* 1. 配置一個vnode記憶體 +* 2. 設定vnode屬性 +* 3. 呼叫tmpfs_set_entry設定vnode內容(interal) +* 4. 設定target = vnode,回傳1 +* 5. 子節點下都已配置(滿了),回傳-1 +*/ +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name, enum FILE_TYPE type) +{ + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + struct tmpfs_entry* entry =((struct tmpfs_entry*)dir_node->internal)->child[i]; + if (entry->type == FILE_NONE) + { + struct vnode* vnode = (struct vnode*)dynamic_alloc(dyalloc_fs, sizeof(struct vnode)); + vnode->mount = 0; + vnode->v_ops = dir_node->v_ops; + vnode->f_ops = dir_node->f_ops; + vnode->internal = entry; + tmpfs_set_entry(entry, component_name, type, vnode); + *target = entry->vnode; + return 1; + } + } + + return -1; +} + +/* +* tmpfs_lookup 實作lookup方法 +* +* 傳入目前目錄,(目標node,搜尋名稱),搜尋目錄下指定名稱的子目錄或子檔案 +* +* 1. 判斷傳入的dir_node檔案類型是否為目錄,假如不是目錄回傳失敗 +* 2. 假如搜尋名稱為"."表示為目前目錄,target = 目前節點 +* 3. 假如搜尋名稱為".."表示為上一層目錄 +* - 如果沒有父節點,回傳失敗 +* - 否則target = 父節點 +* 4. 搜尋目前目錄下每一個子檔案 +* - 假如名稱 = 搜尋名稱,target = 子檔案的節點,回傳成功 +* 5. 都不符合就回傳失敗 +*/ +int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)dir_node->internal; + if (entry->type != FILE_DIRECTORY) + return 0; + + if (strcmp((char*)component_name, ".")) + { + *target = entry->vnode; + return 1; + } + if (strcmp((char*)component_name, "..")) + { + if (!entry->parent_vnode) + return 0; + *target = entry->parent_vnode; + return 1; + } + + for (int i = 0; i < MAX_FILES_IN_DIR; i++) + { + entry = ((struct tmpfs_entry*)dir_node->internal)->child[i]; + if (strcmp(entry->name, (char*)component_name)) + { + *target = entry->vnode; + return 1; + } + } + + return 0; +} + +/* +* tmpfs_write 實作write方法 +* +* 傳入檔案,buffer,長度 +* +* 1. 取出節點內容結構 +* 2. 由buf讀取len個byte,寫入節點的buffer內 +* 3. 節點檔案的buf大小 = file當前位置(file大小) +* 4. 回傳寫入的byte數 +*/ +int tmpfs_write(struct file* file, const void* buf, unsigned int len) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)file->vnode->internal; + for (unsigned int i = 0; i < len; i++) + { + entry->buf->buffer[file->f_pos++] = ((char*)buf)[i]; + entry->buf->size = file->f_pos; + } + + return len; +} + +/* +* tmpfs_read 實作read方法 +* +* 傳入檔案,buffer,長度,由傳入的buf中,寫入len byte到該節點的buf內 +* +* 1. 取出節點內容結構 +* 2. 由節點的buffer內取出len byte內容,傳入buf +* 3. 每讀一byte, read_len讀取長度++ +* 4. 假如已讀完節點buffer的內容(讀到buf size了),跳出 +* 5. 回傳讀了幾個byte +*/ +int tmpfs_read(struct file* file, void* buf, unsigned int len) +{ + unsigned int read_len = 0; + struct tmpfs_entry* entry = (struct tmpfs_entry*)file->vnode->internal; + for (unsigned int i = 0; i < len; i++) + { + ((char*)buf)[i] = entry->buf->buffer[file->f_pos++]; + read_len++; + + if (read_len == entry->buf->size) + break; + } + + return read_len; +} + +//======================================================================= + +/* +* tmpfs_list 實作list方法 +* +* 傳入檔案,buffer,索引,列出資料夾下指定index的檔名,並回傳檔案size +* +* 1. 取出節點內容結構 +* 2. 假如不是目錄,回傳失敗 +* 3. index超過最大子節點數,回傳失敗 +* 4. 資料夾下指定index子節點未分配,回傳失敗 +* 5. 複製資料夾下指定index的檔名到buf +* 6. 回傳檔案size +*/ +int tmpfs_list(struct file* file, void* buf, int index) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)file->vnode->internal; + + if (entry->type != FILE_DIRECTORY) + return -1; + if (index >= MAX_FILES_IN_DIR) + return -1; + if (entry->child[index]->type == FILE_NONE) + return -1; + + strcpy((char*)buf, entry->child[index]->name); + return entry->child[index]->buf->size; +} + +//======================================================================= + +/* +* tmpfs_set_parent 實作set_parent方法 +* +* 傳入子節點,父節點 +* +* 1. 取出子節點內容結構 +* 2. 設定父節點為傳入的父節點 +*/ +int tmpfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode) +{ + struct tmpfs_entry* entry = (struct tmpfs_entry*)child_node->internal; + entry->parent_vnode = parent_vnode; + return 1; +} \ No newline at end of file diff --git a/lab8/kernel/tmpfs.h b/lab8/kernel/tmpfs.h new file mode 100644 index 000000000..813eda7de --- /dev/null +++ b/lab8/kernel/tmpfs.h @@ -0,0 +1,40 @@ +#ifndef TMPFS_H +#define TMPFS_H + +#include "vfs.h" + +#define MAX_FILES_IN_DIR 16 +#define TMPFS_BUF_SIZE 4096 + +// buffer結構 (用於檔案讀寫使用) +struct tmpfs_buf +{ + int flag; + unsigned int size; // 目前buffer的size + char buffer[TMPFS_BUF_SIZE]; // 最大buffer size +}; + +// 檔案描述(vnode的internal) +struct tmpfs_entry +{ + char name[20]; // 名稱 + enum FILE_TYPE type; // 檔案類型 + struct vnode* vnode; // 節點 + struct vnode* parent_vnode; // 父節點 + struct tmpfs_entry* child[MAX_FILES_IN_DIR]; // 資料夾下的檔案 + struct tmpfs_buf* buf; // buffer (用於檔案讀寫使用) +}; + +void tmpfs_init(); +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount); +int tmpfs_lookup(struct vnode* dir_node, struct vnode** target, const char* component_name); +void tmpfs_set_entry(struct tmpfs_entry* entry, const char* component_name, enum FILE_TYPE type, struct vnode* vnode); +int tmpfs_create(struct vnode* dir_node, struct vnode** target, const char* component_name, enum FILE_TYPE type); +int tmpfs_write(struct file* file, const void* buf, unsigned int len); +int tmpfs_read(struct file* file, void* buf, unsigned int len); +// for lab6 elective1 +int tmpfs_list(struct file* file, void* buf, int index); +// for lab6 elective2 +int tmpfs_set_parent(struct vnode* child_node, struct vnode* parent_vnode); + +#endif \ No newline at end of file diff --git a/lab8/kernel/uart.c b/lab8/kernel/uart.c new file mode 100644 index 000000000..57664a4aa --- /dev/null +++ b/lab8/kernel/uart.c @@ -0,0 +1,271 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} + +//================================================================ + +unsigned long uart_gets(char *buf, int size) +{ + for(int i = 0; i < size; ++i) + { + buf[i] = uart_getchar(); + uart_sendchar(buf[i]); + if(buf[i] == '\n' || buf[i] == '\r') + { + buf[i] = '\0'; + return i; + } + } + return size; +} \ No newline at end of file diff --git a/lab8/kernel/uart.h b/lab8/kernel/uart.h new file mode 100644 index 000000000..fb3150dbe --- /dev/null +++ b/lab8/kernel/uart.h @@ -0,0 +1,36 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); +// +unsigned long uart_gets(char *buf, int size); + +#endif \ No newline at end of file diff --git a/lab8/kernel/util.c b/lab8/kernel/util.c new file mode 100644 index 000000000..0b2031f2f --- /dev/null +++ b/lab8/kernel/util.c @@ -0,0 +1,276 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +char *strcpy(char *dst, const char *src) +{ + // return if no memory is allocated to the destination + if (dst == 0) + return 0; + + char *ptr = dst; + while (*src != '\0') + { + *dst = *src; + dst++; + src++; + } + *dst = '\0'; + return ptr; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} + +char *strtok(char *s, const char delim) +{ + static char *pos; + char *ret; + if (s) pos = s; + + if (*pos == '\0') + return 0; + // skip leading + while (*pos == delim) + { + pos++; + } + + ret = pos; + while (*pos != delim && *pos != '\0') + { + pos++; + } + if (*pos != '\0') + { + *pos = '\0'; + pos++; + } + return ret; +} + +char *split_last(char *str, char delim) +{ + char *mid = 0; + while (*str) + { + if (*str == delim) + { + mid = str; + } + + str++; + + } + + if (mid) + { + *mid = '\0'; + mid++; + } + + return mid; +} + +void strcat(char *to, const char *from) +{ + while (*to) + to++; + + while (*from) + { + *to = *from; + to++; + from++; + } + *to = '\0'; +} + +char *strncpy(char *dst, const char *src, unsigned int len) +{ + // return if no memory is allocated to the destination + if (dst == 0) return 0; + + // take a pointer pointing to the beginning of destination string + char *ptr = dst; + + // copy the C-string pointed by source into the array + // pointed by destination + while (*src != ' ') + { + *dst = *src; + dst++; + src++; + len--; + if (!len) + break; + } + + // include the terminating null character + *dst = '\0'; + + // destination is returned by standard strcpy() + return ptr; +} \ No newline at end of file diff --git a/lab8/kernel/util.h b/lab8/kernel/util.h new file mode 100644 index 000000000..a93718465 --- /dev/null +++ b/lab8/kernel/util.h @@ -0,0 +1,19 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +char *strcpy(char *dst, const char *src); +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); +char *strtok(char *s, const char delim); +char *split_last(char *str, char delim); +void strcat(char *to, const char *from); +char *strncpy(char *dst, const char *src, unsigned int len); + +#endif \ No newline at end of file diff --git a/lab8/kernel/vfs.c b/lab8/kernel/vfs.c new file mode 100644 index 000000000..601b72245 --- /dev/null +++ b/lab8/kernel/vfs.c @@ -0,0 +1,430 @@ +#include "vfs.h" +#include "tmpfs.h" +#include "allocator.h" +#include "cpio.h" +#include "uart.h" +#include "util.h" +#include "fat32.h" + +struct filesystem *fs_head, *fs_tail; +struct mount* rootfs; +struct vnode* current_dir; + +struct dynamic_allocator *dyalloc_vfs = 0; + +/* +* vfs_init() +* +* 1. 初始化參數(動態配置,指向檔案系統頭尾的指標) +* 2. 初始化及註冊tmpfs檔案系統 +* 3. 掛載tmpfs檔案系統到根目錄 +* 4. 將cpio裡面的檔案掛到根目錄上 +*/ +void vfs_init() +{ + // 1.初始化參數(動態配置,指向檔案系統頭尾的指標) + dyalloc_vfs = dynamic_allocator_init(); + + fs_head = 0; + fs_tail = 0; + + // 2.初始化及註冊tmpfs檔案系統 + tmpfs_init(); + struct filesystem* tmpfs = (struct filesystem*)dynamic_alloc(dyalloc_vfs, sizeof(struct filesystem)); + tmpfs->name = "tmpfs"; + tmpfs->setup_mount = tmpfs_setup_mount; + register_filesystem(tmpfs); + + // lab 7, 初始化及註冊fatfs檔案系統 + fatfs_init(); + struct filesystem* fatfs = (struct filesystem*)dynamic_alloc(dyalloc_vfs, sizeof(struct filesystem)); + fatfs->name = "fatfs"; + fatfs->setup_mount = fatfs_setup_mount; + register_filesystem(fatfs); + + // 3.掛載tmpfs檔案系統到根目錄 + rootfs = (struct mount*)dynamic_alloc(dyalloc_vfs, sizeof(struct mount)); + struct filesystem* fs = search_fs("tmpfs"); + if (fs == 0) + return; + + fs->setup_mount(fs, rootfs); + current_dir = rootfs->root; + + // 4.將cpio裡面的檔案掛到根目錄上 + cpio_populate_rootfs(); +} + +/* +* register_filesystem() 註冊至filesystem list +* +* 1. 假如為空,直接放入,頭尾指向新加入fs +* 2. 假如不為空,往後加在尾端的next,尾端指向新加入fs +*/ +void register_filesystem(struct filesystem* fs) +{ + if (fs_head == 0) + { + fs_head = fs; + fs_head->next = 0; + fs_tail = fs_head; + } + else + { + fs_tail->next = fs; + fs_tail = fs_tail->next; + } +} + +/* +* search_fs 在檔案系統List尋找指定檔案系統 +* +* 1. 從頭(head)到尾(tail)搜尋 +* 2. 假如檔案系統名稱 = 輸入名稱,回傳 +* 3. 找不到return 0 +*/ +struct filesystem* search_fs(char* name) +{ + for (struct filesystem* fs = fs_head; fs != 0; fs = fs->next) + { + if (strcmp((char*)fs->name, name)) + return fs; + } + + return 0; +} + +/* +* vfs_find_vnode 遞迴尋找指定路徑是否存在,找到後設定為target並回傳1,否則回傳0 +* +* 1. 搜尋路徑 = "/",設定target為root根目錄,回傳1 +* 2. 設定當前目錄,假如開頭為 / 為絕對路徑,須由根目錄開始查找,否則為相對路徑 +* 3. 取得component name, ex. /mnt => mnt +* 4. 搜尋component name是否存在 +* - 沒有找到回傳失敗 +* - 有找到重新設定dir及target, 並往下取出下一層compnent name繼續判斷 +* +* 舉例,傳入 /mnt/device,先搜尋/下mnt是否存在,再搜尋/mnt下device是否存在 +*/ +int vfs_find_vnode(struct vnode** target, const char* pathname) +{ + // 1. 搜尋名稱 = "/",設定target為root根目錄,回傳1 + if (strcmp((char*)pathname, "/")) + { + *target = rootfs->root; + return 1; + } + + // 2. 設定當前目錄,假如開頭為 / 為絕對路徑,須由根目錄開始查找,否則為相對路徑 + struct vnode* dir = current_dir; + if (pathname[0] == '/') + dir = rootfs->root; + + // 3. 取得component name, ex. /mnt => mnt + char* component_name = strtok((char*)pathname, '/'); + + // 4. 搜尋component name是否存在 + // - 沒有找到回傳失敗 + // - 有找到重新設定dir及target, 並往下取出下一層compnent name繼續判斷 + while (component_name && *component_name != '\0') + { + int found = dir->v_ops->lookup(dir, target, component_name); + + if (!found) + return 0; + + if ((*target)->mount) + *target = (*target)->mount->root; + dir = *target; + + component_name = strtok(0, '/'); + } + + return 1; +} + +/* +* vfs_open 開啟or建立指定路徑檔案並回傳 +* +* 此註釋來自助教,target:目標node,fd: file descriptor +* 1. 由root搜尋指定路徑檔案 +* 2. 假如找到指定路徑檔案,建立file descriptor +* 3. 假如輸入的flage是O_CREAT,則建立新檔案 +* +* dir = 目前資料夾, target = 目標node, fd = file descriptor +* 1. 判斷檔名之前的路徑是否存在(pathname_) +* ex. (1)pathname: /mnt -> pathname_: "\0", filename: mnt +* (2)pathname: /mnt/a.txt -> pathname_: /mnt, filename: a.txt +* (3)pathname: file1 -> pathname_: file1, filename: NULL +* 2. 判斷檔案是否存在 (filename) +* - 輸入的flage是O_CREAT且找不到: 建立新vnode, 及建立file +* - 輸入的flage不是O_CREAT且找到: 建立file +* +*/ +struct file* vfs_open(const char* pathname, int flags) +{ + // 1. Lookup pathname from the root vnode. + // 2. Create a new file descriptor for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags. + + struct vnode* dir = current_dir; + struct vnode* target = 0; + struct file* fd = 0; + + // 1. 判斷檔名之前的路徑是否存在(pathname_) + // (1)pathname: /mnt -> pathname_: "\0", filename: mnt + // (2)pathname: /mnt/a.txt -> pathname_: /mnt, filename: a.txt + // (3)pathname: file1 -> pathname_: file1, filename: NULL + char* pathname_ = (char*)dynamic_alloc(dyalloc_vfs, strlen((char*)pathname) + 1); + strcpy(pathname_, pathname); + + char* filename = split_last(pathname_, '/'); + + if (*pathname_ == '\0' && pathname[0] == '/') + dir = rootfs->root; + + if (filename != 0) + { + int prefix_found = vfs_find_vnode(&dir, pathname_); + // ex. given pathname /abc/zxc/file1, but /abc/zxc not found + if (!prefix_found) + return 0; + } + else + filename = (char*)pathname_; + + // 2. 判斷檔案是否存在 (filename) + // - 輸入的flage是O_CREAT且找不到: 建立新vnode, 及建立file + // - 輸入的flage不是O_CREAT且找到: 建立file + int file_found = dir->v_ops->lookup(dir, &target, filename); + + if (flags == O_CREAT) + { + if (!file_found) + { + dir->v_ops->create(dir, &target, filename, FILE_REGULAR); + fd = (struct file*)dynamic_alloc(dyalloc_vfs, sizeof(struct file)); + fd->vnode = target; + fd->f_ops = target->f_ops; + fd->f_pos = 0; + } + } + else + { + if (file_found) + { + if (target->mount) + target = target->mount->root; + + fd = (struct file*)dynamic_alloc(dyalloc_vfs, sizeof(struct file)); + fd->vnode = target; + fd->f_ops = target->f_ops; + fd->f_pos = 0; + } + } + + return fd; +} + +/* +* vfs_close 關閉(釋放file descriptor) +* +* 釋放輸入的file descriptor +*/ +int vfs_close(struct file* file) +{ + // 1. release the file descriptor + dynamic_free(dyalloc_vfs, (unsigned long)file); + return 1; +} + +/* +* vfs_write 寫入 +* +* 此註釋來自助教 +* 1. 由buf寫入len byte到開啟的檔案 +* 2. 回傳寫入的size,或發生錯誤的錯誤代碼 +*/ +int vfs_write(struct file* file, const void* buf, unsigned int len) +{ + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file, buf, len); +} + +/* +* vfs_read 讀取 +* +* 此註釋來自助教 +* 1. 由開啟的檔案讀取len byte到buf中 +* 2. 回傳讀取的size,或發生錯誤的錯誤代碼 +*/ +int vfs_read(struct file* file, void* buf, unsigned int len) +{ + // 1. read min(len, readable file data size) byte to buf from the opened file. + // 2. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +//======================================================================= + +/* +* vfs_list 列出資料夾下檔案 +* +* 傳入file,buf(檔名),index(第幾個) +* 列出指定資料夾下第幾個檔案,名稱放入buf,回傳檔案size +*/ +int vfs_list(struct file* file, void* buf, int index) +{ + return file->f_ops->list(file, buf, index); +} + +//======================================================================= + +/* +* vfs_mkdir 建立目錄 +* +* 傳入目錄名稱 +* +* 1. 如vfs_open,先判斷指定資料夾之前的路徑是否存在,不存在回傳0 +* 2. 再判斷該資料夾是否存在,假如已存在回傳0 +* 3. 建立一個新目錄,回傳建立結果 +* +* 舉例,傳入/mnt/dir1,先判斷/mnt是否存在,再判斷/mnt下是否有dir1,若無則建立新目錄 +*/ +int vfs_mkdir(const char* pathname) +{ + // 1. 先判斷指定資料夾之前的路徑是否存在,不存在回傳0 + struct vnode* dir = current_dir; + struct vnode* target = 0; + + char* pathname_ = (char*)dynamic_alloc(dyalloc_vfs, strlen((char*)pathname) + 1); + strcpy(pathname_, pathname); + // pathname: /mnt -> pathname_: "\0", dirname: mnt + // pathname: /mnt/dir1 -> pathname_: /mnt, dirname: dir1 + // pathname: dir1 -> pathname_: dir1, dirname: NULL + char* dirname = split_last(pathname_, '/'); + if (*pathname_ == '\0' && pathname[0] == '/') + dir = rootfs->root; + + if (dirname != 0) + { + int prefix_found = vfs_find_vnode(&dir, pathname_); + // e.g., given pathname /abc/zxc/file1, but /abc/zxc not found + if (!prefix_found) + return 0; + } + else + dirname = pathname_; + + // 2. 再判斷該資料夾是否存在,假如已存在回傳0 + int file_found = dir->v_ops->lookup(dir, &target, dirname); + if (file_found) + return 0; + + // 3. 建立一個新目錄,回傳建立結果 + int status = dir->v_ops->create(dir, &target, dirname, FILE_DIRECTORY); + return status; +} + +/* +* vfs_chdir 改變當前目錄 +* +* 傳入新目錄名稱 +* +* 1. 尋找目錄名稱是否存在,不存在回傳0 +* 2. 設置目前目錄 = target +*/ +int vfs_chdir(const char* pathname) +{ + // 1. 尋找目錄名稱是否存在,不存在回傳0 + struct vnode* target = 0; + int dir_found = vfs_find_vnode(&target, pathname); + if (!dir_found) + return 0; + + // 2. 設置目前目錄 = target + current_dir = target; + + return 1; +} + +/* +* vfs_mount 掛載目錄到某個檔案系統上 +* +* 傳入device, 掛載目錄名稱,掛載在甚麼檔案系統上 +* +* 1. 假如未找到掛載目錄,回傳0 或 假如目錄已掛載,回傳0 +* 2. 掛載tmpfs檔案系統到根目錄 +* 3. 設定目錄的mount為當前檔案系統,目錄的parent為目前檔案系統的根目錄 +*/ +int vfs_mount(const char* device, const char* mountpoint, const char* filesystem) +{ + // 1. 假如未找到掛載目錄,回傳0 或 假如目錄已掛載,回傳0 + struct vnode* target = 0; + + int dir_found = vfs_find_vnode(&target, mountpoint); + if (!dir_found) + return 0; + if (target->mount) + return 0; + + // 2. 掛載tmpfs檔案系統到根目錄 + struct mount* mountfs = (struct mount*)dynamic_alloc(dyalloc_vfs, sizeof(struct mount)); + struct filesystem* fs = search_fs((char*)filesystem); + fs->setup_mount(fs, mountfs); + + // 3. 設定目錄的mount為當前檔案系統,目錄的parent為目前檔案系統的根目錄 + target->mount = mountfs; + mountfs->root->v_ops->set_parent(mountfs->root, target); + return 1; +} + +/* +* vfs_umount 卸載目錄 +* +* 傳入掛載目錄名稱 +* +* 1. 如vfs_open,先判斷掛載資料夾之前的路徑是否存在,不存在回傳0 +* 2. 判斷掛載資料夾是否存在,不存在回傳0 +* 3. 假如資料夾沒有掛載在任何系統上,回傳0 +* 4. 設定目錄的mount(=0),回傳1 +*/ +int vfs_umount(const char* mountpoint) +{ + // 1. 如vfs_open,先判斷掛載資料夾之前的路徑是否存在,不存在回傳0 + struct vnode* dir = current_dir; + struct vnode* target = 0; + + char* pathname_ = (char*)dynamic_alloc(dyalloc_vfs, strlen((char*)mountpoint) + 1); + strcpy(pathname_, mountpoint); + // pathname: /mnt -> pathname_: "\0", dirname: mnt + // pathname: /mnt/dir1 -> pathname_: /mnt, dirname: dir1 + // pathname: dir1 -> pathname_: dir1, dirname: NULL + char* dirname = split_last(pathname_, '/'); + if (*pathname_ == '\0' && mountpoint[0] == '/') + dir = rootfs->root; + + if (dirname != 0) + { + int prefix_found = vfs_find_vnode(&dir, pathname_); + // e.g., given pathname /abc/zxc/file1, but /abc/zxc not found + if (!prefix_found) + return 0; + } + else + dirname = pathname_; + + // 2. 判斷掛載資料夾是否存在,不存在回傳0 + int file_found = dir->v_ops->lookup(dir, &target, dirname); + if (!file_found) + return 0; + + // 3. 假如資料夾沒有掛載在任何系統上,回傳0 + if (!target->mount) + return 0; + + // 4. 設定目錄的mount(=0),回傳1 + target->mount = 0; + return 1; +} \ No newline at end of file diff --git a/lab8/kernel/vfs.h b/lab8/kernel/vfs.h new file mode 100644 index 000000000..629047c9a --- /dev/null +++ b/lab8/kernel/vfs.h @@ -0,0 +1,84 @@ +#ifndef VFS_H +#define VFS_H + +#define O_CREAT 1 // flags use for create file + +// 檔案類型,參考助教的圖(Directory、Regular、Device) +enum FILE_TYPE +{ + FILE_NONE, + FILE_DIRECTORY, + FILE_REGULAR, + FILE_DEVICE +}; + +// 節點 +struct vnode +{ + struct mount* mount; // 屬於哪個mount + struct vnode_operations* v_ops; // v_node操作(建立、搜尋) + struct file_operations* f_ops; // file操作(讀取、寫入) + void* internal; // 節點內容(各檔案系統實作) +}; + +// 檔案 +struct file +{ + struct vnode* vnode; // 所屬節點 + unsigned int f_pos; // 檔案目前位置(file position),供讀寫操作使用 + struct file_operations* f_ops; // file操作(讀取、寫入) (各檔案系統實作) + int flags; // O_CREAT +}; + +// 掛載 +struct mount +{ + struct vnode* root; // root節點 + struct filesystem* fs; // 檔案系統 +}; + +// 檔案系統 +struct filesystem +{ + const char* name; // 名稱 + int (*setup_mount)(struct filesystem* fs, struct mount* mount); // 掛載方法(各檔案系統實作) + struct filesystem* next; // 指向下一個檔案系統指標 +}; + +// 檔案操作(讀取、寫入) +struct file_operations +{ + //寫入方法(各檔案系統實作) + int (*write)(struct file* file, const void* buf, unsigned int len); + //讀取方法(各檔案系統實作) + int (*read)(struct file* file, void* buf, unsigned int len); + // list方法(各檔案系統實作) lab6加分1 + int (*list)(struct file* file, void* buf, int index); +}; + +// v_node操作(查找、建立) +struct vnode_operations +{ + //查找方法(各檔案系統實作) + int (*lookup)(struct vnode* dir_node, struct vnode** target, const char* component_name); + //建立vnode方法(各檔案系統實作) + int (*create)(struct vnode* dir_node, struct vnode** target, const char* component_name, enum FILE_TYPE type); + // 設定parent node(各檔案系統實作) lab6加分2 + int (*set_parent)(struct vnode* child_node, struct vnode* parent_node); +}; + +void vfs_init(); +void register_filesystem(struct filesystem* fs); +struct filesystem* search_fs(char* name); +int vfs_find_vnode(struct vnode** target, const char* pathname); // lab6加分2 +struct file* vfs_open(const char* pathname, int flags); +int vfs_close(struct file* file); +int vfs_write(struct file* file, const void* buf, unsigned int len); +int vfs_read(struct file* file, void* buf, unsigned int len); +int vfs_list(struct file* file, void* buf, int index); +// lab6加分2 +int vfs_mkdir(const char* pathname); +int vfs_chdir(const char* pathname); +int vfs_mount(const char* device, const char* mountpoint, const char* filesystem); +int vfs_umount(const char* mountpoint); +#endif \ No newline at end of file diff --git a/lab8/rootfs/init.txt b/lab8/rootfs/init.txt new file mode 100644 index 000000000..794cece32 --- /dev/null +++ b/lab8/rootfs/init.txt @@ -0,0 +1 @@ +Hello world !! \ No newline at end of file diff --git a/lab8/rootfs/osc.txt b/lab8/rootfs/osc.txt new file mode 100644 index 000000000..30fe44454 --- /dev/null +++ b/lab8/rootfs/osc.txt @@ -0,0 +1 @@ +NYCU OSC 2021 ! \ No newline at end of file diff --git a/lab8/rootfs/raspi3.txt b/lab8/rootfs/raspi3.txt new file mode 100644 index 000000000..f69082dc8 --- /dev/null +++ b/lab8/rootfs/raspi3.txt @@ -0,0 +1,10 @@ + .~~. .~~. + '. \\ ' ' / .' + .~ .~~~..~. + : .~.'~'.~. : + ~ ( ) ( ) ~ + ( : '~'.~.'~' : ) + ~ .~ ( ) ~. ~ + ( : '~' : ) + '~ .~~~. ~' + '~' \ No newline at end of file diff --git a/lab8/rootfs/svc.s b/lab8/rootfs/svc.s new file mode 100644 index 000000000..ce7e53908 --- /dev/null +++ b/lab8/rootfs/svc.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 diff --git a/lab8/sendimg.py b/lab8/sendimg.py new file mode 100644 index 000000000..dcbcdd4c1 --- /dev/null +++ b/lab8/sendimg.py @@ -0,0 +1,44 @@ +#! python2 + +import os +import time +import serial + +filename = "./kernel/kernel8.img" +ser_port = "/dev/ttyS4" +ser_baudrate = 115200 + +try: + ser = serial.Serial( + port=ser_port, + baudrate=ser_baudrate, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=1 + ) + print("Open Serial Port\r") + print(f"Port=%s, baudrate=%d\r" % (ser_port, ser_baudrate)) + + with open(filename, 'rb') as file: + + size = os.path.getsize(filename) + print(f"Image size is {size}") + ser.write(f"{size}\r".encode()) + + time.sleep(2) + + position = 0 + byte = file.read(1) + while byte != b"": + file.seek(position, 0) + byte = file.read(1) + ser.write(byte) + position = position + 1 + + print("Done !") + file.close() + +except Exception as e: + print(f"[EXCEPTION] {e}") + diff --git a/lab8/user_program/argv_test/argv_test.c b/lab8/user_program/argv_test/argv_test.c new file mode 100644 index 000000000..03e305525 --- /dev/null +++ b/lab8/user_program/argv_test/argv_test.c @@ -0,0 +1,30 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +int main(int argc, char **argv) +{ + uart_init(); + + // 1. print parent ID + char buf[16] = {0}; + uart_putstr("Argv Test, pid "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr("\n"); + + // 2. prints the arguments + uart_putstr("arguments: "); + for (int i = 0; i < argc; ++i) + { + uart_putstr(argv[i]); + uart_putstr(", "); + } + uart_putstr("\n"); + + // 3. exec fork_test.img + char *fork_argv[] = {"fork_test", 0}; + call_sys_exec("fork_test.img", fork_argv); + + return 0; +} diff --git a/lab8/user_program/argv_test/auxilary.h b/lab8/user_program/argv_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/argv_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/argv_test/gpio.h b/lab8/user_program/argv_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/argv_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/argv_test/linker.ld b/lab8/user_program/argv_test/linker.ld new file mode 100644 index 000000000..84968cefe --- /dev/null +++ b/lab8/user_program/argv_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/argv_test/mmio.h b/lab8/user_program/argv_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/argv_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/argv_test/startup.S b/lab8/user_program/argv_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/argv_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/argv_test/sys.S b/lab8/user_program/argv_test/sys.S new file mode 100644 index 000000000..b3aaad8c0 --- /dev/null +++ b/lab8/user_program/argv_test/sys.S @@ -0,0 +1,35 @@ + +.globl call_sys_gitPID +call_sys_gitPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret diff --git a/lab8/user_program/argv_test/sys.h b/lab8/user_program/argv_test/sys.h new file mode 100644 index 000000000..a90d157ec --- /dev/null +++ b/lab8/user_program/argv_test/sys.h @@ -0,0 +1,13 @@ +#ifndef _SYS_H +#define _SYS_H + +void call_sys_write(char * buf); +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_gitPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +void *call_sys_malloc(); + +#endif \ No newline at end of file diff --git a/lab8/user_program/argv_test/uart.c b/lab8/user_program/argv_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/argv_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/argv_test/uart.h b/lab8/user_program/argv_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/argv_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/argv_test/util.c b/lab8/user_program/argv_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab8/user_program/argv_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/argv_test/util.h b/lab8/user_program/argv_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab8/user_program/argv_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test/auxilary.h b/lab8/user_program/fat32_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/fat32_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test/fat32_test.c b/lab8/user_program/fat32_test/fat32_test.c new file mode 100644 index 000000000..75165b127 --- /dev/null +++ b/lab8/user_program/fat32_test/fat32_test.c @@ -0,0 +1,40 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +char buff[16] = {0}; + +int main(int argc, char **argv) +{ + uart_init(); + + call_sys_mkdir("/fat"); + call_sys_mount("fatfs", "/fat", "fatfs"); + + int fd = call_sys_open("/fat", 0); + char name[100]; + int size; + for (int i = 0;; ++i) + { + size = call_sys_list(fd, name, i); + + if (size > 0) + { + //printf("Name: %s Size: %d\n", name, size); + uart_putstr("Name: "); + uart_putstr(name); + uart_putstr(", Size: "); + unsignedlonglongToStr(size, buff); + uart_putstr(buff); + uart_putstr("\n"); + } + else if (size < 0) + break; + } + + call_sys_close(fd); + + call_sys_exit(); + + return 0; +} diff --git a/lab8/user_program/fat32_test/gpio.h b/lab8/user_program/fat32_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/fat32_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test/linker.ld b/lab8/user_program/fat32_test/linker.ld new file mode 100644 index 000000000..84968cefe --- /dev/null +++ b/lab8/user_program/fat32_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/fat32_test/mmio.h b/lab8/user_program/fat32_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/fat32_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test/startup.S b/lab8/user_program/fat32_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/fat32_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/fat32_test/sys.S b/lab8/user_program/fat32_test/sys.S new file mode 100644 index 000000000..80c55862c --- /dev/null +++ b/lab8/user_program/fat32_test/sys.S @@ -0,0 +1,89 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret + +.globl call_sys_mkdir +call_sys_mkdir: + svc #12 + mov x0, x0 + ret + +.globl call_sys_chdir +call_sys_chdir: + svc #13 + mov x0, x0 + ret + +.globl call_sys_mount +call_sys_mount: + svc #14 + mov x0, x0 + ret + +.globl call_sys_umount +call_sys_umount: + svc #15 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab8/user_program/fat32_test/sys.h b/lab8/user_program/fat32_test/sys.h new file mode 100644 index 000000000..73d1c3dd2 --- /dev/null +++ b/lab8/user_program/fat32_test/sys.h @@ -0,0 +1,22 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); +int call_sys_mkdir(const char *pathname); +int call_sys_chdir(const char *pathname); +int call_sys_mount(const char* device, const char* mountpoint, const char* filesystem); +int call_sys_umount(const char* mountpoint); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test/uart.c b/lab8/user_program/fat32_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/fat32_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/fat32_test/uart.h b/lab8/user_program/fat32_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/fat32_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test/util.c b/lab8/user_program/fat32_test/util.c new file mode 100644 index 000000000..9a0addb94 --- /dev/null +++ b/lab8/user_program/fat32_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strncmp(char *s1, char *s2, int n) +{ + for(int i = 0; i < n; i++) + { + if(*s1 != *s2) + return 1; + s1++; + s2++; + } + + return 0; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/fat32_test/util.h b/lab8/user_program/fat32_test/util.h new file mode 100644 index 000000000..69966ecb6 --- /dev/null +++ b/lab8/user_program/fat32_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strncmp(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/auxilary.h b/lab8/user_program/fat32_test2/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/fat32_test2/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/fat32_test2.c b/lab8/user_program/fat32_test2/fat32_test2.c new file mode 100644 index 000000000..a64b06930 --- /dev/null +++ b/lab8/user_program/fat32_test2/fat32_test2.c @@ -0,0 +1,34 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +int main(int argc, char **argv) +{ + uart_init(); + + uart_putstr("start test fat32 read and write.."); + + int size; + char buf[200]; + + // required 2-1 Look up and open a file in FAT32. + uart_putstr("\nlook up and open FATTEST.TXT\n"); + int fd = call_sys_open("/fat/FATTEST.TXT", 0); + + // required 2-2 Read/Write a file in FAT32. + uart_putstr("\nwrite FATTEST.TXT\n"); + call_sys_write(fd, "This is osc2021-lab7 test", 25); + call_sys_close(fd); + + fd = call_sys_open("/fat/FATTEST.TXT", 0); + size = call_sys_read(fd, buf, 200); + buf[size] = '\0'; + uart_putstr(buf); + uart_putstr("\n"); + + call_sys_close(fd); + + call_sys_exit(); + + return 0; +} diff --git a/lab8/user_program/fat32_test2/gpio.h b/lab8/user_program/fat32_test2/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/fat32_test2/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/linker.ld b/lab8/user_program/fat32_test2/linker.ld new file mode 100644 index 000000000..84968cefe --- /dev/null +++ b/lab8/user_program/fat32_test2/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/mmio.h b/lab8/user_program/fat32_test2/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/fat32_test2/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/startup.S b/lab8/user_program/fat32_test2/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/fat32_test2/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/fat32_test2/sys.S b/lab8/user_program/fat32_test2/sys.S new file mode 100644 index 000000000..80c55862c --- /dev/null +++ b/lab8/user_program/fat32_test2/sys.S @@ -0,0 +1,89 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret + +.globl call_sys_mkdir +call_sys_mkdir: + svc #12 + mov x0, x0 + ret + +.globl call_sys_chdir +call_sys_chdir: + svc #13 + mov x0, x0 + ret + +.globl call_sys_mount +call_sys_mount: + svc #14 + mov x0, x0 + ret + +.globl call_sys_umount +call_sys_umount: + svc #15 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/sys.h b/lab8/user_program/fat32_test2/sys.h new file mode 100644 index 000000000..73d1c3dd2 --- /dev/null +++ b/lab8/user_program/fat32_test2/sys.h @@ -0,0 +1,22 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); +int call_sys_mkdir(const char *pathname); +int call_sys_chdir(const char *pathname); +int call_sys_mount(const char* device, const char* mountpoint, const char* filesystem); +int call_sys_umount(const char* mountpoint); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/uart.c b/lab8/user_program/fat32_test2/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/fat32_test2/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/fat32_test2/uart.h b/lab8/user_program/fat32_test2/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/fat32_test2/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/util.c b/lab8/user_program/fat32_test2/util.c new file mode 100644 index 000000000..9a0addb94 --- /dev/null +++ b/lab8/user_program/fat32_test2/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strncmp(char *s1, char *s2, int n) +{ + for(int i = 0; i < n; i++) + { + if(*s1 != *s2) + return 1; + s1++; + s2++; + } + + return 0; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/fat32_test2/util.h b/lab8/user_program/fat32_test2/util.h new file mode 100644 index 000000000..69966ecb6 --- /dev/null +++ b/lab8/user_program/fat32_test2/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strncmp(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fork_test/Makefile b/lab8/user_program/fork_test/Makefile new file mode 100644 index 000000000..8ae953e1c --- /dev/null +++ b/lab8/user_program/fork_test/Makefile @@ -0,0 +1,28 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate kernel8.img +all: fork_test.img + +%.o : %.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate kernel8.img, need startup.o main.o util.o uart.o reboot.o cpio.o linker.ld +fork_test.img: startup.o fork_test.o util.o uart.o linker.ld + $(LD) $(LD_FLAGS) -o fork_test.elf startup.o fork_test.o util.o uart.o + $(OBJCOPY) -O binary fork_test.elf ../../rootfs/fork_test.img + +# clean all generate file +clean: + rm *.o fork_test.elf ../../rootfs/fork_test.img diff --git a/lab8/user_program/fork_test/auxilary.h b/lab8/user_program/fork_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/fork_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/fork_test/fork_test.c b/lab8/user_program/fork_test/fork_test.c new file mode 100644 index 000000000..5529e70a4 --- /dev/null +++ b/lab8/user_program/fork_test/fork_test.c @@ -0,0 +1,93 @@ +#include "util.h" +#include "sys.h" +#include "uart.h" + +char buf[16] = {0}; + +void delay(int count) +{ + for(int i = 0; i < count; i++) + asm volatile("nop"); +} + +void clear_buf() +{ + for(int i = 0; i < 16; i++) + buf[i] = 0; +} + +int main(void) +{ + uart_init(); + + // printf("Fork Test, pid %d\n", getpid()); + clear_buf(); + uart_putstr("Fork Test, pid "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr("\n"); + + + int cnt = 1; + int ret = 0; + if ((ret = call_sys_fork()) == 0) // child + { + // printf("pid: %d, cnt: %d, ptr: %p\n", getpid(), cnt, &cnt); + clear_buf(); + uart_putstr("pid: "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", cnt: "); + unsignedlonglongToStr(cnt, buf); + uart_putstr(buf); + uart_putstr(", ptr: "); + unsigned long long tmp = (unsigned long long)&cnt; + unsignedlonglongToStrHex(tmp, buf); + uart_putstr(buf); + uart_putstr("\n"); + + ++cnt; + call_sys_fork(); + while (cnt < 5) + { + // printf("pid: %d, cnt: %d, ptr: %p\n", getpid(), cnt, &cnt); + clear_buf(); + uart_putstr("pid: "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", cnt: "); + unsignedlonglongToStr(cnt, buf); + uart_putstr(buf); + uart_putstr(", ptr: "); + tmp = (unsigned long long)&cnt; + unsignedlonglongToStrHex(tmp, buf); + uart_putstr(buf); + uart_putstr("\n"); + + delay(1000000); + ++cnt; + } + } + else + { + // printf("parent here, pid %d, child %d\n", getpid(), ret); + clear_buf(); + uart_putstr("parent here, pid "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", child "); + unsignedlonglongToStr(ret, buf); + uart_putstr(buf); + uart_putstr("\n"); + } + + clear_buf(); + uart_putstr("task "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(" exit! \n"); + + call_sys_exit(); + + return 0; +} \ No newline at end of file diff --git a/lab8/user_program/fork_test/gpio.h b/lab8/user_program/fork_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/fork_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/fork_test/linker.ld b/lab8/user_program/fork_test/linker.ld new file mode 100644 index 000000000..5e3477761 --- /dev/null +++ b/lab8/user_program/fork_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/fork_test/mmio.h b/lab8/user_program/fork_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/fork_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/fork_test/startup.S b/lab8/user_program/fork_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/fork_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/fork_test/sys.S b/lab8/user_program/fork_test/sys.S new file mode 100644 index 000000000..b3aaad8c0 --- /dev/null +++ b/lab8/user_program/fork_test/sys.S @@ -0,0 +1,35 @@ + +.globl call_sys_gitPID +call_sys_gitPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret diff --git a/lab8/user_program/fork_test/sys.h b/lab8/user_program/fork_test/sys.h new file mode 100644 index 000000000..a90d157ec --- /dev/null +++ b/lab8/user_program/fork_test/sys.h @@ -0,0 +1,13 @@ +#ifndef _SYS_H +#define _SYS_H + +void call_sys_write(char * buf); +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_gitPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +void *call_sys_malloc(); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fork_test/uart.c b/lab8/user_program/fork_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/fork_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/fork_test/uart.h b/lab8/user_program/fork_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/fork_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/fork_test/util.c b/lab8/user_program/fork_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab8/user_program/fork_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/fork_test/util.h b/lab8/user_program/fork_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab8/user_program/fork_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/ls_test/auxilary.h b/lab8/user_program/ls_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/ls_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/ls_test/gpio.h b/lab8/user_program/ls_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/ls_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/ls_test/linker.ld b/lab8/user_program/ls_test/linker.ld new file mode 100644 index 000000000..5e3477761 --- /dev/null +++ b/lab8/user_program/ls_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/ls_test/ls_test.c b/lab8/user_program/ls_test/ls_test.c new file mode 100644 index 000000000..3d340a169 --- /dev/null +++ b/lab8/user_program/ls_test/ls_test.c @@ -0,0 +1,40 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +char buf[16] = {0}; + +int main(int argc, char **argv) +{ + uart_init(); + + uart_putstr("List file in directory: "); + uart_putstr(argv[1]); + uart_putstr("\n"); + + int fd = call_sys_open(argv[1], 0); + char name[100]; + int size; + // Modify the for loop to iterate the directory entries of the opened directory. + for(int i = 0;; ++i) + { + size = call_sys_list(fd, name, i); + + if(size > 0) + { + //printf("Name: %s Size: %d\n", name, size); + uart_putstr("Name: "); + uart_putstr(name); + uart_putstr(" Size: "); + unsignedlonglongToStr(size, buf); + uart_putstr(buf); + uart_putstr("\n"); + } + else if (size < 0) + break; + } + + call_sys_exit(); + + return 0; +} diff --git a/lab8/user_program/ls_test/mmio.h b/lab8/user_program/ls_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/ls_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/ls_test/startup.S b/lab8/user_program/ls_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/ls_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/ls_test/sys.S b/lab8/user_program/ls_test/sys.S new file mode 100644 index 000000000..3ebc262d9 --- /dev/null +++ b/lab8/user_program/ls_test/sys.S @@ -0,0 +1,65 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab8/user_program/ls_test/sys.h b/lab8/user_program/ls_test/sys.h new file mode 100644 index 000000000..3d52a155e --- /dev/null +++ b/lab8/user_program/ls_test/sys.h @@ -0,0 +1,18 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); + +#endif \ No newline at end of file diff --git a/lab8/user_program/ls_test/uart.c b/lab8/user_program/ls_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/ls_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/ls_test/uart.h b/lab8/user_program/ls_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/ls_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/ls_test/util.c b/lab8/user_program/ls_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab8/user_program/ls_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/ls_test/util.h b/lab8/user_program/ls_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab8/user_program/ls_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/mmu_test/Makefile b/lab8/user_program/mmu_test/Makefile new file mode 100644 index 000000000..8ae953e1c --- /dev/null +++ b/lab8/user_program/mmu_test/Makefile @@ -0,0 +1,28 @@ +# define variable +# -g for gdb debug +# -Wall is show warning message +# -fno-builtin is Don’t recognize built-in functions, ex. strlen, strcmp +CC = aarch64-linux-gnu-gcc +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -g -Wall -fno-builtin +LD_FLAGS = -T linker.ld + +# 'all' keyword represent this makefile target, so target is generate kernel8.img +all: fork_test.img + +%.o : %.s + $(CC) -c $< -o $@ + +# compile all *.c and generate *.o +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# generate kernel8.img, need startup.o main.o util.o uart.o reboot.o cpio.o linker.ld +fork_test.img: startup.o fork_test.o util.o uart.o linker.ld + $(LD) $(LD_FLAGS) -o fork_test.elf startup.o fork_test.o util.o uart.o + $(OBJCOPY) -O binary fork_test.elf ../../rootfs/fork_test.img + +# clean all generate file +clean: + rm *.o fork_test.elf ../../rootfs/fork_test.img diff --git a/lab8/user_program/mmu_test/auxilary.h b/lab8/user_program/mmu_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/mmu_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/mmu_test/gpio.h b/lab8/user_program/mmu_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/mmu_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/mmu_test/linker.ld b/lab8/user_program/mmu_test/linker.ld new file mode 100644 index 000000000..5e3477761 --- /dev/null +++ b/lab8/user_program/mmu_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/mmu_test/mmio.h b/lab8/user_program/mmu_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/mmu_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/mmu_test/mmu_test.c b/lab8/user_program/mmu_test/mmu_test.c new file mode 100644 index 000000000..6415f1391 --- /dev/null +++ b/lab8/user_program/mmu_test/mmu_test.c @@ -0,0 +1,55 @@ +#include "util.h" +#include "sys.h" +#include "uart.h" + +char buf[16] = {0}; + +void delay(int count) +{ + for(int i = 0; i < count; i++) + asm volatile("nop"); +} + +int main(void) +{ + uart_init(); + + int cnt = 0; + if(call_sys_fork() == 0) + { + call_sys_fork(); + call_sys_fork(); + while(cnt < 10) + { + uart_putstr("pid: "); + unsignedlonglongToStr(call_sys_gitPID(), buf); + uart_putstr(buf); + uart_putstr(", sp: "); + unsigned long long tmp = (unsigned long long)&cnt; + unsignedlonglongToStrHex(tmp, buf); + uart_putstr(buf); + uart_putstr(" cnt: "); + unsignedlonglongToStr(cnt++, buf); + uart_putstr(buf); + uart_putstr("\n"); + + //printf("pid: %d, sp: 0x%llx cnt: %d\n", call_sys_getpid(), &cnt, cnt++); // address should be the same, but the cnt should be increased indepndently + delay(1000000); + } + } + else + { + int* a = 0x0; // a non-mapped address. + //printf("%d\n", *a); // trigger simple page fault. + //printf("Should not be printed\n"); + + unsignedlonglongToStr(*a, buf); + uart_putstr(buf); + uart_putstr("\n"); + uart_putstr("Should not be printed\n"); + } + + call_sys_exit(); + + return 0; +} \ No newline at end of file diff --git a/lab8/user_program/mmu_test/startup.S b/lab8/user_program/mmu_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/mmu_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/mmu_test/sys.S b/lab8/user_program/mmu_test/sys.S new file mode 100644 index 000000000..b3aaad8c0 --- /dev/null +++ b/lab8/user_program/mmu_test/sys.S @@ -0,0 +1,35 @@ + +.globl call_sys_gitPID +call_sys_gitPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret diff --git a/lab8/user_program/mmu_test/sys.h b/lab8/user_program/mmu_test/sys.h new file mode 100644 index 000000000..a90d157ec --- /dev/null +++ b/lab8/user_program/mmu_test/sys.h @@ -0,0 +1,13 @@ +#ifndef _SYS_H +#define _SYS_H + +void call_sys_write(char * buf); +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_gitPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +void *call_sys_malloc(); + +#endif \ No newline at end of file diff --git a/lab8/user_program/mmu_test/uart.c b/lab8/user_program/mmu_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/mmu_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/mmu_test/uart.h b/lab8/user_program/mmu_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/mmu_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/mmu_test/util.c b/lab8/user_program/mmu_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab8/user_program/mmu_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/mmu_test/util.h b/lab8/user_program/mmu_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab8/user_program/mmu_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/auxilary.h b/lab8/user_program/multilvl_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/multilvl_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/gpio.h b/lab8/user_program/multilvl_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/multilvl_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/linker.ld b/lab8/user_program/multilvl_test/linker.ld new file mode 100644 index 000000000..84968cefe --- /dev/null +++ b/lab8/user_program/multilvl_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/mmio.h b/lab8/user_program/multilvl_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/multilvl_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/multilvl_test.c b/lab8/user_program/multilvl_test/multilvl_test.c new file mode 100644 index 000000000..5040d6ccb --- /dev/null +++ b/lab8/user_program/multilvl_test/multilvl_test.c @@ -0,0 +1,63 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +int main(int argc, char **argv) +{ + uart_init(); + + uart_putstr("start multilevel test !\n"); + + char buf[8]; + call_sys_mkdir("mnt"); + int fd = call_sys_open("/mnt/a.txt", O_CREAT); + call_sys_write(fd, "Hi", 2); + call_sys_close(fd); + call_sys_chdir("mnt"); + fd = call_sys_open("./a.txt", 0); + //assert(fd >= 0); + if (!(fd >= 0)) + { + uart_putstr("assert1, fd < 0 \n"); + return 0; + } + call_sys_read(fd, buf, 2); + //assert(strncmp(buf, "Hi", 2) == 0); + if (!(strncmp(buf, "Hi", 2) == 0)) + { + uart_putstr("assert2, buf != Hi \n"); + return 0; + } + + call_sys_chdir(".."); + call_sys_mount("tmpfs", "mnt", "tmpfs"); + fd = call_sys_open("mnt/a.txt", 0); + //assert(fd < 0); + if (!(fd < 0)) + { + uart_putstr("assert3, fd >= 0 \n"); + return 0; + } + + call_sys_umount("/mnt"); + fd = call_sys_open("/mnt/a.txt", 0); + //assert(fd >= 0); + if (!(fd >= 0)) + { + uart_putstr("assert4, fd < 0 \n"); + return 0; + } + call_sys_read(fd, buf, 2); + //assert(strncmp(buf, "Hi", 2) == 0); + if (!(strncmp(buf, "Hi", 2) == 0)) + { + uart_putstr("assert5, buf != Hi \n"); + return 0; + } + + uart_putstr("end multilevel test !\n"); + + call_sys_exit(); + + return 0; +} diff --git a/lab8/user_program/multilvl_test/startup.S b/lab8/user_program/multilvl_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/multilvl_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/multilvl_test/sys.S b/lab8/user_program/multilvl_test/sys.S new file mode 100644 index 000000000..80c55862c --- /dev/null +++ b/lab8/user_program/multilvl_test/sys.S @@ -0,0 +1,89 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret + +.globl call_sys_list +call_sys_list: + svc #11 + mov x0, x0 + ret + +.globl call_sys_mkdir +call_sys_mkdir: + svc #12 + mov x0, x0 + ret + +.globl call_sys_chdir +call_sys_chdir: + svc #13 + mov x0, x0 + ret + +.globl call_sys_mount +call_sys_mount: + svc #14 + mov x0, x0 + ret + +.globl call_sys_umount +call_sys_umount: + svc #15 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/sys.h b/lab8/user_program/multilvl_test/sys.h new file mode 100644 index 000000000..73d1c3dd2 --- /dev/null +++ b/lab8/user_program/multilvl_test/sys.h @@ -0,0 +1,22 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); +int call_sys_list(int fd, void *buf, int index); +int call_sys_mkdir(const char *pathname); +int call_sys_chdir(const char *pathname); +int call_sys_mount(const char* device, const char* mountpoint, const char* filesystem); +int call_sys_umount(const char* mountpoint); + +#endif \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/uart.c b/lab8/user_program/multilvl_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/multilvl_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/multilvl_test/uart.h b/lab8/user_program/multilvl_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/multilvl_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/util.c b/lab8/user_program/multilvl_test/util.c new file mode 100644 index 000000000..9a0addb94 --- /dev/null +++ b/lab8/user_program/multilvl_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strncmp(char *s1, char *s2, int n) +{ + for(int i = 0; i < n; i++) + { + if(*s1 != *s2) + return 1; + s1++; + s2++; + } + + return 0; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/multilvl_test/util.h b/lab8/user_program/multilvl_test/util.h new file mode 100644 index 000000000..69966ecb6 --- /dev/null +++ b/lab8/user_program/multilvl_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strncmp(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/svc.s b/lab8/user_program/svc.s new file mode 100644 index 000000000..864a47b5a --- /dev/null +++ b/lab8/user_program/svc.s @@ -0,0 +1,13 @@ +/* required 1-2 1-3 user program */ + +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/lab8/user_program/vfs_test/auxilary.h b/lab8/user_program/vfs_test/auxilary.h new file mode 100644 index 000000000..8700cd989 --- /dev/null +++ b/lab8/user_program/vfs_test/auxilary.h @@ -0,0 +1,21 @@ +#ifndef AUXILARY_H +#define AUXILARY_H + +#include "mmio.h" + +// Auxilary mini UART registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068)) + +#endif \ No newline at end of file diff --git a/lab8/user_program/vfs_test/gpio.h b/lab8/user_program/vfs_test/gpio.h new file mode 100644 index 000000000..53405c4da --- /dev/null +++ b/lab8/user_program/vfs_test/gpio.h @@ -0,0 +1,12 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "mmio.h" + +// GPIO registers +// Use volatile tells the compiler that the value of the variable may change at any time by hardware +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) // GPIO Function Select Registers +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) // GPIO Pull-up/down Register +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) // GPIO Pull-up/down Clock Register + +#endif \ No newline at end of file diff --git a/lab8/user_program/vfs_test/linker.ld b/lab8/user_program/vfs_test/linker.ld new file mode 100644 index 000000000..5e3477761 --- /dev/null +++ b/lab8/user_program/vfs_test/linker.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0x80000; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data : + { + *(.data .data.*) + } + .bss (NOLOAD) : + { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; +} +__bss_size = (__bss_end - __bss_start) >> 3; \ No newline at end of file diff --git a/lab8/user_program/vfs_test/mmio.h b/lab8/user_program/vfs_test/mmio.h new file mode 100644 index 000000000..d5cbf0769 --- /dev/null +++ b/lab8/user_program/vfs_test/mmio.h @@ -0,0 +1,10 @@ +#ifndef MMIO_H +#define MMIO_H + +//rpi3 access peripheral registers by memory mapped io (MMIO). +//There is a VideoCore/ARM MMU sit between ARM CPU and peripheral bus. +//This MMU maps ARM’s physical address 0x3f000000 to 0x7e000000. + +#define MMIO_BASE 0x3F000000 + +#endif \ No newline at end of file diff --git a/lab8/user_program/vfs_test/startup.S b/lab8/user_program/vfs_test/startup.S new file mode 100644 index 000000000..46a341df2 --- /dev/null +++ b/lab8/user_program/vfs_test/startup.S @@ -0,0 +1,11 @@ +.section ".text.boot" + +.global _start +_start: + bl main + b proc_hang + +proc_hang: + wfe + b proc_hang + diff --git a/lab8/user_program/vfs_test/sys.S b/lab8/user_program/vfs_test/sys.S new file mode 100644 index 000000000..fe483c59a --- /dev/null +++ b/lab8/user_program/vfs_test/sys.S @@ -0,0 +1,59 @@ + +.globl call_sys_gitPID +call_sys_getPID: + svc #1 + mov x0, x0 + ret + +.globl call_sys_uart_read +call_sys_uart_read: + svc #2 + mov x0, x0 + ret + +.globl call_sys_uart_write +call_sys_uart_write: + svc #3 + mov x0, x0 + ret + +.globl call_sys_exec +call_sys_exec: + svc #4 + mov x0, x0 + ret + +.globl call_sys_exit +call_sys_exit: + svc #5 + ret + +.globl call_sys_fork +call_sys_fork: + svc #6 + mov x0, x0 + ret + +.globl call_sys_open +call_sys_open: + svc #7 + mov x0, x0 + ret + +.globl call_sys_close +call_sys_close: + svc #8 + mov x0, x0 + ret + +.globl call_sys_write +call_sys_write: + svc #9 + mov x0, x0 + ret + +.globl call_sys_read +call_sys_read: + svc #10 + mov x0, x0 + ret \ No newline at end of file diff --git a/lab8/user_program/vfs_test/sys.h b/lab8/user_program/vfs_test/sys.h new file mode 100644 index 000000000..fb6ee5405 --- /dev/null +++ b/lab8/user_program/vfs_test/sys.h @@ -0,0 +1,17 @@ +#ifndef _SYS_H +#define _SYS_H + +#define O_CREAT 1 + +int call_sys_uart_write(char buf[], unsigned int size); +int call_sys_uart_read(char buf[], unsigned int size); +int call_sys_getPID(); +int call_sys_fork(); +int call_sys_exec(const char* name, char* const argv[]); +void call_sys_exit(); +int call_sys_open(const char* name, int flags); +int call_sys_close(int fd); +int call_sys_write(int fd, const void *buf, unsigned int len); +int call_sys_read(int fd, void *buf, unsigned int len); + +#endif \ No newline at end of file diff --git a/lab8/user_program/vfs_test/uart.c b/lab8/user_program/vfs_test/uart.c new file mode 100644 index 000000000..3495f7eb2 --- /dev/null +++ b/lab8/user_program/vfs_test/uart.c @@ -0,0 +1,254 @@ +#include "gpio.h" +#include "auxilary.h" +#include "uart.h" + +char read_buf[MAX_UART_BUFFER]; +char write_buf[MAX_UART_BUFFER]; +int read_buf_start, read_buf_end; +int write_buf_start, write_buf_end; + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() +{ + // 1.Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + // 2.Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // 3.Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // 4.Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // 5.Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + /* 6.Set AUX_MU_BAUD to 270. Set baud rate to 115200 + After booting, the system clock is 250 MHz. + + systemx clock freq + baud rate = ------------------------ + 8×(AUX_MU_BAUD+1) + */ + // 7.Set AUX_MU_IIR_REG to 6. No FIFO. + // 8.Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + + *AUX_ENABLE |=1; // Enable mini uart (UART1) + *AUX_MU_CNTL = 0; // Disable TX, RX + *AUX_MU_IER = 0; // Disable interrupt + *AUX_MU_LCR = 3; // Set the data size to 8 bit + *AUX_MU_MCR = 0; // Don't need auto flow control + *AUX_MU_BAUD = 270; // set 115200 baud + *AUX_MU_IIR = 6; // no FIFO + + /* map UART1 to GPIO pins */ + + //GPIO 14, 15 can be both used for mini UART and PL011 UART. However, mini UART should set 'ALT5' + //and PL011 UART should set ALT0 You need to configure 'GPFSELn' register to change alternate function. + + //Next, you need to configure pull up/down register to disable GPIO pull up/down. It’s because + //these GPIO pins use alternate functions, not basic input-output. + //Please refer to the description of 'GPPUD' and 'GPPUDCLKn' registers for a detailed setup. + + // 1. Change GPIO 14, 15 to alternate function + + register unsigned int r = *GPFSEL1; // GPFSEL is GPIO Function Select Registers + r&=~((7<<12)|(7<<15)); // Reset GPIO 14, 15 + r|=(2<<12)|(2<<15); // set alt5 + *GPFSEL1 = r; + + // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output) + + // GPPUD is GPIO Pull-up/down Register + // GPPUDCLKn is GPIO Pull-up/down Clock Register + *GPPUD = 0; // Set control signal to disable + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = (1<<14)|(1<<15); // Clock the control signal into the GPIO pads + r=150; while(r--) { asm volatile("nop"); } // Wait 150 cycles + *GPPUDCLK0 = 0; // Remove the clock + + *AUX_MU_CNTL = 3; // enable Tx, Rx + + uart_interrupt_init(); +} + +/* +Send a character +1.Check AUX_MU_LSR_REG’s Transmitter empty field. +2.If set, write to AUX_MU_IO_REG +*/ +void uart_sendchar(unsigned int c) +{ + // wait until we can send + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x20)); + + *AUX_MU_IO = c; // write the character to the buffer +} + +/* +Receive a character +1. Check AUX_MU_LSR_REG’s data ready field. +2. If set, read from AUX_MU_IO_REG +*/ +char uart_getchar() +{ + char r; + + // wait until something is in the buffer + do + { + asm volatile("nop"); + }while(!(*AUX_MU_LSR&0x01)); + + r = (char)(*AUX_MU_IO); // read it and return + return r; // convert carrige return to newline +} + +// send a string +void uart_putstr(char *s) +{ + while(*s) { + // convert newline to carrige return + newline + if(*s=='\n') + uart_sendchar('\r'); + uart_sendchar(*s++); + } +} + +/* +* uart_interrupt_init() +* +* 1. enable uart read interrupt +* 2. initial read,write buffer parameter +* 3. enable_uart_interrupt +* +* AUX_MU_IER +* https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.12 +* +* 0' bit : enable or disable receive interrupt (read) +* 1' bit : enable or disable transmit interrupt (write) +* +* 0: disable read & write (00) +* 1: enable read, disable write (01) +* 2: enable write, disable read (10) +* 3: enable read & write (11) +*/ +void uart_interrupt_init() +{ + *AUX_MU_IER = 1; + read_buf_start = read_buf_end = 0; + write_buf_start = write_buf_end = 0; + enable_uart_interrupt(); +} + +void enable_uart_interrupt() +{ + *ENABLE_IRQS_1 = AUX_IRQ; +} + +void disable_uart_interrupt() +{ + *DISABLE_IRQS_1 = AUX_IRQ; +} + +void enable_write_interrupt() +{ + *AUX_MU_IER |= 0x2; +} + +void disable_write_interrupt() +{ + *AUX_MU_IER &= ~(0x2); +} + +/* +* uart_interrupt_handler() +* +* 1. critical section: before handle,disable interrupt; after handle,enable interrupt +* 2. AUX_MU_IIR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.13 +* 0x4 = read, 0x2 = write +* 3. AUX_MU_LSR https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf p.15 +* 0x1 = data ready, 0x20 = transmitter empty +* 4. if read, we read byte to reade buffer end inex, and if buffer end index is end , index = 0,循環 +* 5. if write +* 5.1 if write buffer full, disable write interrupt and break; +* 5.2 get char form write buffer start index and send +* 2.2 if buffer start = max size, start = 0,循環 +*/ +void uart_interrupt_handler() +{ + disable_uart_interrupt(); + + if (*AUX_MU_IIR & 0x4) // read + { + while (*AUX_MU_LSR & 0x1) + { + char c = (char)(*AUX_MU_IO); + read_buf[read_buf_end++] = c; + + if (read_buf_end == MAX_UART_BUFFER) + read_buf_end = 0; + } + } + else if (*AUX_MU_IIR & 0x2) // write + { + while (*AUX_MU_LSR & 0x20) + { + if (write_buf_start == write_buf_end) + { + disable_write_interrupt(); + break; + } + + char c = write_buf[write_buf_start++]; + *AUX_MU_IO = c; + + if (write_buf_start == MAX_UART_BUFFER) + write_buf_start = 0; + } + } + + enable_uart_interrupt(); +} + +/* +* uart_async_getchar() +* +* 1. wait until there are new data +* 2. read a char form read buffer start index +* 3. if buffer start index is end, index = 0 +*/ +char uart_async_getchar() +{ + // wait until there are new data + while (read_buf_start == read_buf_end) + { + asm volatile("nop"); + } + + char c = read_buf[read_buf_start++]; + if (read_buf_start == MAX_UART_BUFFER) + read_buf_start = 0; + + return c; +} + +/* +* uart_async_putstr(s*) +* +* 1. input char to writer buffer end +* 2. '\n' -> '\r\n' +* 3. if buffer end index is end, index = 0 +* 4. enable write intrrupt to transmit +*/ +void uart_async_putstr(char *s) +{ + for (int i = 0; s[i]; i++) + { + if (s[i] == '\n') + write_buf[write_buf_end++] = '\r'; + + write_buf[write_buf_end++] = s[i]; + if (write_buf_end == MAX_UART_BUFFER) + write_buf_end = 0; + } + + enable_write_interrupt(); +} diff --git a/lab8/user_program/vfs_test/uart.h b/lab8/user_program/vfs_test/uart.h new file mode 100644 index 000000000..b7502e271 --- /dev/null +++ b/lab8/user_program/vfs_test/uart.h @@ -0,0 +1,34 @@ +#ifndef UART_H +#define UART_H + +#include "mmio.h" + +// uart interrupt enable +// enable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define ENABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b210)) +// disable uart interrupt controller’s IRQs1(0x3f00b210)’s bit29. +#define DISABLE_IRQS_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b21c)) +// set bit29 for enable or disable or check uart interrupt +#define AUX_IRQ (1 << 29) + +// check irq interrupt type +// IRQ_PENDING_1 : uart pending 1 register , use bit29 check if irq interrupt +#define IRQ_PENDING_1 ((volatile unsigned int*)(MMIO_BASE+0x0000b204)) + +#define MAX_UART_BUFFER 1024 + +void uart_init(); // init +void uart_sendchar(unsigned int c); // send char +char uart_getchar(); // get char +void uart_putstr(char *str); // send string + +void uart_interrupt_init(); +void enable_uart_interrupt(); +void disable_uart_interrupt(); +void enable_write_interrupt(); +void disable_write_interrupt(); +void uart_interrupt_handler(); +char uart_async_getchar(); +void uart_async_putstr(char *str); + +#endif \ No newline at end of file diff --git a/lab8/user_program/vfs_test/util.c b/lab8/user_program/vfs_test/util.c new file mode 100644 index 000000000..abfc07c1a --- /dev/null +++ b/lab8/user_program/vfs_test/util.c @@ -0,0 +1,168 @@ +#include "uart.h" + +// calculate string length +int strlen(char *s) +{ + int size = 0; + + while(*s) + { + size++; + s++; + } + + return size; +} + +// compare two string is equal ? +int strcmp(char *s1, char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if(len1 != len2) + return 0; + + for(int i = 0; i <= len1; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +int strcmpn(char *s1, char *s2, int n) +{ + for(int i = 0; i <= n; i++) + { + if(*s1 != *s2) + return 0; + s1++; + s2++; + } + + return 1; +} + +// string to int +int atoi(char* input) +{ + int res = 0; + int i = 0; + int isNegative = 0; + + while (input[i] != '\0') + { + int current; + + if (i == 0 && input[i] == '-') + { + isNegative = 1; + i++; + continue; + } + current = input[i] - '0'; + res = res * 10 + current; + i++; + } + + if (isNegative) + res *= -1; + + return res; +} + +// hex string to integer +unsigned long hex2int(char* hex, int n) +{ + unsigned long val = 0; + for(int i = 0; i < n; i++) + { + // get current character then increment + int byte1 = hex[i]; + // transform hex character to the 4bit equivalent number, using the ascii table indexes + if (byte1 >= '0' && byte1 <= '9') + byte1 = byte1 - '0'; + else if (byte1 >= 'a' && byte1 <= 'f') + byte1 = byte1 - 'a' + 10; + else if (byte1 >= 'A' && byte1 <= 'F') + byte1 = byte1 - 'A' + 10; + // shift 4 to make space for new digit, and add the 4 bits of the new digit + val = val * 16 + byte1; + } + return val; +} + +void strReverse(char *buf, int size) +{ + int front = 0; + int last = size; + + while(last > front) + { + char temp; + temp = buf[front]; + buf[front] = buf[last]; + buf[last] = temp; + + front++; + last--; + } +} + +void unsignedlonglongToStr(unsigned long long num, char *buf) +{ + if(num==0) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + int size=0; + + while(num) + { + buf[size] = '0' + num % 10; + size++; + num = num / 10; + } + + buf[size] = '\0'; + strReverse(buf, size-1); + + return; +} + +void unsignedlonglongToStrHex(unsigned long long num, char *buf) +{ + unsigned int n; + int size=0; + for(int c=60; c>=0; c-=4,size++) + { + // get highest tetrad + n=(num>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + buf[size] = (char)n; + } + buf[16] = '\0'; + +} + +int log2(int input) +{ + int num = 1; + int power = 0; + + while (num != input) + { + num = num << 1; + power++; + } + + return power; +} \ No newline at end of file diff --git a/lab8/user_program/vfs_test/util.h b/lab8/user_program/vfs_test/util.h new file mode 100644 index 000000000..1586cb05f --- /dev/null +++ b/lab8/user_program/vfs_test/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +int strlen(char *s); // calculate string length +int strcmp(char *s1, char *s2); // compare two string is equal +int strcmpn(char *s1, char *s2, int n); // compare two string n char is equal +int atoi(char* input); // string to int +unsigned long hex2int(char* hex, int n); // hex string to integer +void strReverse(char *buf, int size); +void unsignedlonglongToStr(unsigned long long num, char *buf); +void unsignedlonglongToStrHex(unsigned long long num, char *buf); +int log2(int input); + +#endif \ No newline at end of file diff --git a/lab8/user_program/vfs_test/vfs_test.c b/lab8/user_program/vfs_test/vfs_test.c new file mode 100644 index 000000000..1b387b660 --- /dev/null +++ b/lab8/user_program/vfs_test/vfs_test.c @@ -0,0 +1,31 @@ +#include "uart.h" +#include "util.h" +#include "sys.h" + +char buf[100] = {0}; + +int main(int argc, char **argv) +{ + uart_init(); + + int a = call_sys_open("hello", O_CREAT); + int b = call_sys_open("world", O_CREAT); + call_sys_write(a, "Hello ", 6); + call_sys_write(b, "World!", 6); + call_sys_close(a); + call_sys_close(b); + b = call_sys_open("hello", 0); + a = call_sys_open("world", 0); + int sz; + sz = call_sys_read(b, buf, 100); + sz += call_sys_read(a, buf + sz, 100); + buf[sz] = '\0'; + + //printf("%s\n", buf); // should be Hello World! + uart_putstr(buf); + uart_putstr("\n"); + + call_sys_exit(); + + return 0; +}