diff --git a/lab2/bcm2710-rpi-3-b-plus.dtb b/lab2/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..c83b0817e Binary files /dev/null and b/lab2/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab2/bootloader/booting.S b/lab2/bootloader/booting.S new file mode 100644 index 000000000..7d65d428e --- /dev/null +++ b/lab2/bootloader/booting.S @@ -0,0 +1,27 @@ +.section ".text.boot" // Start a new section named ".text.boot" +.global _start // Declare _start symbol as global + +_start: // Start of the _start block + // read cpu id, stop slave cores + mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register into x1 + and x1, x1, #3 // Mask the lower 2 bits (CPU core ID) from x1 + cbz x1, setting // If x1 is zero, jump to setting label + +halt: wfe // Wait for Event instruction + b halt // Branch (jump) to halt (infinite loop) + +setting: // Setting label + ldr x1, =_start // Load the address of _start into x1 + mov sp, x1 // Move the value of x1 into the Stack Pointer (sp) + ldr x1, =__bss_start // Load the address of __bss_start into x1 + ldr w2, =__bss_size // Load the value of __bss_size into w2 + +clear_bss: // Clear BSS segment loop label + cbz w2, kernel_main // If w2 (BSS size) is zero, jump to kernel_main + str xzr, [x1], #8 // Store zero (xzr) at the address pointed by x1, then increment x1 by 8 + sub w2, w2, #1 // Decrement w2 (BSS size) by 1 + cbnz w2, clear_bss // If w2 is not zero, jump back to clear_bss + +kernel_main: // Label for the start of kernel_main function + bl main // Branch with link (call) to main function + b halt // Branch to halt (infinite loop) diff --git a/lab2/bootloader/bootloader.c b/lab2/bootloader/bootloader.c new file mode 100644 index 000000000..b4813137a --- /dev/null +++ b/lab2/bootloader/bootloader.c @@ -0,0 +1,64 @@ +#include"header/bootloader.h" +#include"header/uart.h" +#include"header/utils.h" +// from linker script +extern char _start; +extern char _end; +void relocate(char *arg) +{ + unsigned long bootloader_size = (&_end - &_start); + char *oldbootloader = (char *)&_start; //0x80000 + char *newbootloader = (char *)0x60000; + + unsigned long bl_ptr = 0; + // copying + while (bootloader_size--) + { + newbootloader[bl_ptr] = oldbootloader[bl_ptr]; + ++bl_ptr; + } + // run kernel in 0x60000 + void (*run)(char *) = (void (*)(char *))newbootloader; + run(arg); +} +// can be verifying in gdb +void load_img(char *dtb_base) { + // kernel start + char *kernel = (char *)(0x80000); + int kn_ptr = 0; + + // size + int idx = 0; + char sz[50] = {}; + char c; + + // receiving str size + while(1) { + c = uart_get_char(); + // receive size end + if(c == '\n') { + sz[idx] = '\0'; + break; + } + sz[idx++] = c; + } + // get kernel image size + int size = atoi(sz); + uart_send_str(sz); + + // receive kernel img + while (size--) + kernel[kn_ptr++] = uart_get_img_char(); + // test message + uart_binary_to_hex((unsigned int) dtb_base); + uart_send_str("\nKernel received\n"); + int r = 1000; + while(r--){ + asm volatile("nop"); + } + // run kernel and pass dtb base + void (*run)(char *) = (void *)kernel; + // get dtb loading address + // run("0x60000"); + run(dtb_base); +} \ No newline at end of file diff --git a/lab2/bootloader/header/bootloader.h b/lab2/bootloader/header/bootloader.h new file mode 100644 index 000000000..3f3b59262 --- /dev/null +++ b/lab2/bootloader/header/bootloader.h @@ -0,0 +1,2 @@ +void load_img(); +void relocate(char *arg); \ No newline at end of file diff --git a/lab2/bootloader/header/reboot.h b/lab2/bootloader/header/reboot.h new file mode 100644 index 000000000..bf5420c1f --- /dev/null +++ b/lab2/bootloader/header/reboot.h @@ -0,0 +1,7 @@ +#ifndef _REBOOT_H_ +#define _REBOOT_H_ + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); +#endif \ No newline at end of file diff --git a/lab2/bootloader/header/shell.h b/lab2/bootloader/header/shell.h new file mode 100644 index 000000000..12a19e675 --- /dev/null +++ b/lab2/bootloader/header/shell.h @@ -0,0 +1,3 @@ +void ls(); +void cat(); +void shell(); \ No newline at end of file diff --git a/lab2/bootloader/header/uart.h b/lab2/bootloader/header/uart.h new file mode 100644 index 000000000..542a57c14 --- /dev/null +++ b/lab2/bootloader/header/uart.h @@ -0,0 +1,44 @@ +#ifndef _UART_H_ +#define _UART_H_ +#define MMIO_BASE 0x3f000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPLEV0 ((volatile unsigned int *)(MMIO_BASE + 0x00200034)) +#define GPLEV1 ((volatile unsigned int *)(MMIO_BASE + 0x00200038)) +#define GPEDS0 ((volatile unsigned int *)(MMIO_BASE + 0x00200040)) +#define GPEDS1 ((volatile unsigned int *)(MMIO_BASE + 0x00200044)) +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +#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 + +void uart_init(); +void uart_send_char(unsigned int c); +char uart_get_char(); +void uart_send_str(char *s); +void uart_binary_to_hex(unsigned int d); +char uart_get_img_char(); \ No newline at end of file diff --git a/lab2/bootloader/header/utils.h b/lab2/bootloader/header/utils.h new file mode 100644 index 000000000..aaa489a29 --- /dev/null +++ b/lab2/bootloader/header/utils.h @@ -0,0 +1,2 @@ +int string_compare(char *a, char *b); +int atoi(char *str); diff --git a/lab2/bootloader/linker.ld b/lab2/bootloader/linker.ld new file mode 100644 index 000000000..958e7c546 --- /dev/null +++ b/lab2/bootloader/linker.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + . = 0x80000; /* Set the memory address to 0x80000 (start point)*/ + + .text : { /* Define the .text section, which contains executable code*/ + KEEP(*(.text.boot)) /* Keep all .text.boot sections*/ + *(.text) /* Keep all other .text sections*/ + } + + .rodata : { *(.rodata) } /* Define the .rodata section, which contains read-only data*/ + + .data : { *(.data) } /* Define the .data section, which contains initialized data*/ + + .bss () : { /* Define the .bss section, which contains uninitialized data*/ + __bss_start = .; /* Define __bss_start symbol as the current memory address*/ + *(.bss) /* Keep all .bss sections*/ + *(COMMON) /* Keep all common symbols*/ + __bss_end = .; /* Define __bss_end symbol as the current memory address*/ + } + + _end = .; /* Define _end symbol as the current memory address*/ +} + +__bss_size = (__bss_end - __bss_start) >> 3; /* Calculate the size of the .bss section in bytes*/ diff --git a/lab2/bootloader/main.c b/lab2/bootloader/main.c new file mode 100644 index 000000000..5f1653f7d --- /dev/null +++ b/lab2/bootloader/main.c @@ -0,0 +1,20 @@ +#include"header/uart.h" +#include"header/bootloader.h" +#include"header/shell.h" +char *dtb_base; +int relocated = 1; +void main(char *arg) +{ + uart_init(); + + // register x0 + dtb_base = arg; + + // relocate copies bootloader program from 0x80000 to 0x60000 + if (relocated) { + relocated = 0; + relocate(arg); + } + uart_send_str("\x1b[2J\x1b[H"); + shell(dtb_base); +} \ No newline at end of file diff --git a/lab2/bootloader/makefile b/lab2/bootloader/makefile new file mode 100644 index 000000000..34df2ec5e --- /dev/null +++ b/lab2/bootloader/makefile @@ -0,0 +1,31 @@ +CFLAGS = -Wall -ffreestanding -nostdinc -nostdlib -nostartfiles -fno-stack-protector -g +SRCS = $(wildcard *.c) +OBJS = $(SRCS:.c=.o) + +ASMS = $(wildcard *.S) +ASM_OBJS = $(ASMS:.S=.o) + +all:: clean_img flash clean + +%.o: %.S + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + + +%.o: %.c + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + +flash: $(ASM_OBJS) $(OBJS) + aarch64-linux-gnu-ld $(ASM_OBJS) $(OBJS) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy kernel8.elf -O binary kernel8.img +clean: + rm -f $(ASM_OBJS) $(OBJS) + +clean_img: + rm -f kernel8.elf + rm -f kernel8.img +test: + qemu-system-aarch64 -machine raspi3b -kernel kernel8.img -display none -serial null -serial stdio -initrd ../rootfs/initramfs.cpio +test_pty: + qemu-system-aarch64 -machine raspi3b -kernel kernel8.img -display none -serial null -serial pty +screen: + sudo screen /dev/ttyUSB0 115200 \ No newline at end of file diff --git a/lab2/bootloader/reboot.c b/lab2/bootloader/reboot.c new file mode 100644 index 000000000..089d71dcb --- /dev/null +++ b/lab2/bootloader/reboot.c @@ -0,0 +1,20 @@ +#include"header/reboot.h" + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/lab2/bootloader/shell.c b/lab2/bootloader/shell.c new file mode 100644 index 000000000..036db19ba --- /dev/null +++ b/lab2/bootloader/shell.c @@ -0,0 +1,59 @@ +#include"header/shell.h" +#include"header/uart.h" +#include"header/utils.h" +#include"header/reboot.h" +#include"header/bootloader.h" + +void shell(char *dtb_base){ + char cmd[256]; + char *cur; + while (1) + { + char *s = "# "; + uart_send_str(s); + cur = cmd; + char receive; + while (1) + { + receive = uart_get_char(); + if(receive == '\n'){ + *cur = '\0'; + break; + } + else if(receive == 127){ + if(cur == cmd){ + *cur = '\0'; + continue; + } + *cur = '\0'; + cur--; + uart_send_str("\b \b"); + continue; + } + *cur = receive; + uart_send_char(receive); + cur++; + } + cur = cmd; + if(string_compare(cur,"help")){ + uart_send_str("\nhelp\t\t:print this help menu\r\n"); + uart_send_str("hello\t\t:print Hello World!\r\n"); + uart_send_str("load\t\t:load kernel image through uart\r\n"); + uart_send_str("reboot\t\t:reboot the device\r\n"); + } + else if(string_compare(cur,"hello")){ + uart_send_str("\nHello World!\n"); + } + else if(string_compare(cur,"load")){ + uart_send_str("\nload kernel...\n"); + load_img(dtb_base); + } + else if (string_compare(cur,"reboot")) { + uart_send_str("\nRebooting....\n"); + reset(1000); + + } + else + uart_send_str("\n"); + } +} \ No newline at end of file diff --git a/lab2/bootloader/uart.c b/lab2/bootloader/uart.c new file mode 100644 index 000000000..4d35998a6 --- /dev/null +++ b/lab2/bootloader/uart.c @@ -0,0 +1,74 @@ +#include"header/uart.h" + +void uart_init(){ + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + + register unsigned int r; + + //??? + r =* GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 innitial + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = 0; // flush GPIO setup + + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +void uart_send_char(unsigned int c){ + do{asm volatile("nop");}while(!(*AUX_MU_LSR & 0x20)); // This bit is set if the transmit FIFO can accept at least one byte. + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +char uart_get_char(){ + char r; + /* wait until something is in the buffer */ + //bit 0 is set if the receive FIFO holds at least 1 symbol. + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + /* convert carriage return to newline */ + return r=='\r'?'\n':r; +} +char uart_get_img_char(){ + char r; + /* wait until something is in the buffer */ + //bit 0 is set if the receive FIFO holds at least 1 symbol. + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + return r; +} +void uart_send_str(char *s){ + while(*s) { + /* convert newline to carriage return + newline */ + if(*s=='\n') + uart_send_char('\r'); + uart_send_char(*s++); + } +} + +void uart_binary_to_hex(unsigned int d) { + unsigned int n; + int c; + uart_send_str("0x"); + for(c=28;c>=0;c-=4) { + // get highest tetrad + n=(d>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + uart_send_char(n); + } +} \ No newline at end of file diff --git a/lab2/bootloader/utils.c b/lab2/bootloader/utils.c new file mode 100644 index 000000000..0d398f6f5 --- /dev/null +++ b/lab2/bootloader/utils.c @@ -0,0 +1,34 @@ +#include"header/utils.h" +#include"header/uart.h" + +int string_compare(char* a, char* b) { + char *p1=a; + char *p2=b; + while(1){ + char c1 = *p1; + p1++; + char c2 = *p2; + p2++; + + if(c1==c2){ + if(c1 == '\0') return 1; + else continue; + } + else return 0; + + } +} + +int atoi(char *str) +{ + int res = 0; + + for (int i = 0; str[i] != '\0'; ++i) + { + if (str[i] > '9' || str[i] < '0') + return res; + res = res * 10 + str[i] - '0'; + } + + return res; +} diff --git a/lab2/config.txt b/lab2/config.txt new file mode 100644 index 000000000..8c6facb68 --- /dev/null +++ b/lab2/config.txt @@ -0,0 +1,4 @@ +kernel_address=0x60000 +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x20000000 \ No newline at end of file diff --git a/lab2/rootfs/file1.txt b/lab2/rootfs/file1.txt new file mode 100644 index 000000000..3677cc6cc --- /dev/null +++ b/lab2/rootfs/file1.txt @@ -0,0 +1,55 @@ + _oI=vo__ + ?/$="'" """^SATAN$~\ + .&?/' `""$$, + ,/?/' /-"^\. .-=~\T, + ,/?/' /SATAN| |\IS,&' |LT + `\?\\ ``\?\^I/HATE@:~:$=v\. `$k==v\.??\, `\d `\$$'9P'I-LOVE=SATAN\/$$~?$\ ,R/ + /$?~^'"""""`"\\&&< ?b "`~$P:c: /v==v,#::?<<&:'T| d$/' + [|:. ""=o/&. ,P o&Z'`'.##| |MH\|| ,$$' + `=:$H&=\. `"b?b. .&' 96*.-v.:?/`\==$&?$&*' + `^$?\. `*&*\\ ,P ?~-~' |$$S>' + `\7b ,T/\&&\. d? |T' + \/b .&J' `\> d' T, + &`L /|| ?| ?, + ||9 J\T H ?, + H|| ||/ || 6 6 6 9, + ||M PJ' || 6 6 6 `H + bT, ||T || 66 666 666 || + T/L H|| `b 6 6 6 6 6 6 M + &T, M| 9, 666 666 666 9 + `L9, M| `&. | + `?*,9|| `b d + `\?(|H. `b ?b + `*\ `&. `\. J*|b + `\o/\. `&. ,P 9/L + 9:&. `9\ ?? `H9. + *?9\ `b .&' |/| + `|`\. `L ./' `|H + d\/qZbo. M .,=' ,|T + ./~&$$?=??/' `"=H$| H .o='' J\| + ,*/'' `\? `' ./?ov=="*b9, ,$P + ,Td ,$$'`' ?|M ,$/ + J|| ,$?/ M|| ?$/ + M|| |>\. ._,~9$'' T|| d'M. + 9`| `Hi:R&:&&6&="' ./$J| `^"\Z\. + ||M `=Z\:"" H|T" `&H&>v_ + bT, .. v,?|\ M|| .:Z|&\. + ||H _DEATH~>TO9H| `?*\ ?$`#'H + 9ALL|1KIDS* .$/ `bZ&\ ,o\&KILL&/' + \?$.:?ooo/*""' `\$$b_ |\MAIM*:./' + `"""' `' `~?&qDESTROY#/' + "^~DIE/" + diff --git a/lab2/rootfs/file2.txt b/lab2/rootfs/file2.txt new file mode 100644 index 000000000..cfb787b3e --- /dev/null +++ b/lab2/rootfs/file2.txt @@ -0,0 +1,14 @@ +fasdfdsafsdafadsf +kernel_addressfasdfsdaf +kernel_addressfasdfsdafsadf +sdaf +sdafsad +fasdfdsafsdafadsfsadf +sdafhert +h +hbafs +gf +bootloaderhewr +hbafsabvfhwert +rwetr +gefgvgagretraegearsujew5y diff --git a/lab2/rootfs/initramfs.cpio b/lab2/rootfs/initramfs.cpio new file mode 100644 index 000000000..b0ae9915f Binary files /dev/null and b/lab2/rootfs/initramfs.cpio differ diff --git a/lab2/shell/cpio.c b/lab2/shell/cpio.c new file mode 100644 index 000000000..20055ca0e --- /dev/null +++ b/lab2/shell/cpio.c @@ -0,0 +1,125 @@ +#include"header/cpio.h" +#include"header/utils.h" +char *cpio_start; +char *cpio_end; +int cpio_newc_parse_header(struct cpio_newc_header *this_header_pointer, char **pathname, unsigned int *filesize, char **data, struct cpio_newc_header **next_header_pointer) +{ + // Ensure magic header 070701 + // new ascii format + if (strncmp(this_header_pointer->c_magic, CPIO_NEWC_HEADER_MAGIC, sizeof(this_header_pointer->c_magic)) != 0){ + return -1; + } + // transfer big endian 8 byte hex string to unsinged int + // data size + *filesize = hex_to_int(this_header_pointer->c_filesize, 8); + + // end of header is the pathname + // header | pathname str | data + *pathname = ((char *)this_header_pointer) + sizeof(struct cpio_newc_header); + // uart_send_str(pathname[0]); + // get file data, file data is just after pathname + // header | pathname str | data + // check picture on hackmd note + unsigned int pathname_length = hex_to_int(this_header_pointer->c_namesize, 8); + // get the offset to start of data + // | offset | data + unsigned int offset = pathname_length + sizeof(struct cpio_newc_header); + // pathname and data might be zero padding + // section % 4 ==0 + offset = offset % 4 == 0 ? offset : (offset + 4 - offset % 4); // padding + // header pointer + offset = start of data + // h| offset | data + *data = (char *)this_header_pointer + offset; + + // get next header pointer + if (*filesize == 0) + // hardlinked files handeld by setting filesize to zero + *next_header_pointer = (struct cpio_newc_header *)*data; + else + { + // data size + offset = *filesize; + // move pointer to the end of data + *next_header_pointer = (struct cpio_newc_header *)(*data + (offset % 4 == 0 ? offset : (offset + 4 - offset % 4))); + } + + // if filepath is TRAILER!!! means there is no more files. + // end of archieve + // empty filename : TRAILER!!! + if (strncmp(*pathname, "TRAILER!!!", sizeof("TRAILER!!!")) == 0) + *next_header_pointer = 0; + + return 0; +} +int ls(){ + // cpio_start = (char*)0x8000000 ; + // cpio_start = (char*)0x20000000; + char *filepath; + char *filedata; + unsigned int filesize; + // current pointer + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + // print every cpio pathname + while (header_pointer) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // if parse header error + if (error) + { + uart_send_str("error\n"); + break; + } + + // if this is not TRAILER!!! (last of file) + if (header_pointer != 0){ + uart_send_str(filepath); + uart_send_str("\n"); + } + } + return 0; +} +int cat(char *path){ + // cpio_start = (char*) 0x8000000; + // cpio_start = (char*)0x20000000; + char *filepath; + char *filedata; + unsigned int filesize; + // current header pointer, cpio start + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + while (header_pointer) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // if parse header error + if (error == -1) + { + uart_send_str("error \n"); + break; + } + // parse until filepath is same as cat input + // print the content of input file + if (string_compare(path, filepath)) + { + if(string_compare(filepath,".")){ + uart_send_str("cat: "); + uart_send_str(path); + uart_send_str("No such file or directory\r\n"); + break; + } + for (unsigned int i = 0; i < filesize; i++) + uart_send_char(filedata[i]); + uart_send_str("\n"); + break; + } + // end of cpio, cannot find input file + if (header_pointer == 0){ + uart_send_str("cat: \""); + uart_send_str(path); + uart_send_str("\" No such file or directory\r\n"); + break; + } + + } + return 0; +} \ No newline at end of file diff --git a/lab2/shell/dtb.c b/lab2/shell/dtb.c new file mode 100644 index 000000000..2f9f752df --- /dev/null +++ b/lab2/shell/dtb.c @@ -0,0 +1,109 @@ +#include"header/dtb.h" +#include"header/utils.h" +#include"header/uart.h" +extern char *cpio_start; +extern char *cpio_end; +char *dtb_base; +unsigned int endian_big2little(unsigned int x) { + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +} +// a tree data structure which indicating what devices are on a computer system. +// only find out node of initramfs and get the address +void fdt_traverse(dtb_callback callback) +{ + struct fdt_header *header = (struct fdt_header *)dtb_base; + // fdt header magic 0xD00DFEED (big-endian) + if (endian_big2little(header->magic) != 0xD00DFEED) + { + uart_send_str("fdt_traverse: wrong magic in fdt_traverse\n"); + uart_send_str("expect: 0XD00DFEED, get: "); + uart_send_char(endian_big2little(header->magic)); + uart_send_str("\n"); + return; + } + + // length in bytes of structure block section of dtb + unsigned int struct_size = endian_big2little(header->size_dt_struct); + + // check hackmd notes about the picture of DTB structure + // header is address of fdt_header, so we need (char *) + // offset in bytes of the structure block from beginning of header + // to locate struct start + char *dt_struct_ptr = (char *)((char *)header + endian_big2little(header->off_dt_struct)); + // offset in bytes of strings block from beginning of header + // to locate string start + // fdt_prop use string_ptr + nameoff to get the pathname + char *dt_strings_ptr = (char *)((char *)header + endian_big2little(header->off_dt_strings)); + + // parse from struct begin to end + char *end = (char *)dt_struct_ptr + struct_size; + char *pointer = dt_struct_ptr; + + // according to lexical structure + while (pointer < end) + { + // lexical big-endian-32-bit integer + // all tokens shall be alligned on 32-bit boundary + unsigned int token_type = endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // lexical structure + switch (token_type) + { + // begin of node's representation + case FDT_BEGIN_NODE: + // move node's unit name + // string end \0 + pointer += strlen(pointer); + // node name is followed by zeroed padding bytes + // allign + pointer += (4 - (unsigned long long)pointer % 4); + break; + + // end of node's representation + case FDT_END_NODE: + break; + + case FDT_PROP: + + // len | name offset | address + // uint32_t + // length of prop values in byte + unsigned int len = endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // nameoff save offset of string blocks + // strings_ptr + nameoff get the name + char *name = (char *)dt_strings_ptr + endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // check node is initrd-start/end and set cpio_start/end address + callback(token_type, name, pointer, len); + // address, byte string of length len + pointer += len; + // followed by zeroed padding bytes + if ((unsigned long long)pointer % 4 != 0) + pointer += 4 - (unsigned long long)pointer % 4; // alignment 4 byte + break; + // ** cant skip + // ignore NOP + case FDT_NOP: + break; + // marks end of structures block + case FDT_END: + break; + default: + return; + } + } +} + +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size) +{ + if (string_compare(name, "linux,initrd-start")){ + cpio_start = (char *)(unsigned long long)endian_big2little(*(unsigned int *)value); + } + if (string_compare(name, "linux,initrd-end")){ + cpio_end = (char *)(unsigned long long)endian_big2little(*(unsigned int *)value); + } +} \ No newline at end of file diff --git a/lab2/shell/header/cpio.h b/lab2/shell/header/cpio.h new file mode 100644 index 000000000..21a46e1e3 --- /dev/null +++ b/lab2/shell/header/cpio.h @@ -0,0 +1,30 @@ +#ifndef CPIO_H +#define CPIO_H + +#include "uart.h" +#include "utils.h" + +#define CPIO_NEWC_HEADER_MAGIC "070701" // big endian + +struct cpio_newc_header +{ + char c_magic[6]; // Magic number identifying the CPIO archive format. Should be "070701" for newc format. + char c_ino[8]; // File inode number. + char c_mode[8]; // File mode (permissions and file type). + char c_uid[8]; // User ID of the file owner. + char c_gid[8]; // Group ID of the file owner. + char c_nlink[8]; // Number of hard links to the file. + char c_mtime[8]; // Modification time of the file (timestamp). + char c_filesize[8]; // Size of the file in bytes. + char c_devmajor[8]; // Major number of the device (for character or block special files). + char c_devminor[8]; // Minor number of the device. + char c_rdevmajor[8]; // Major number of the device for special files. + char c_rdevminor[8]; // Minor number of the device for special files. + char c_namesize[8]; // Size of the file name including null terminator. + char c_check[8]; // Checksum of the file header. +}; +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size); +int cpio_newc_parse_header(struct cpio_newc_header *this_header_pointer, char **pathname, unsigned int *filesize, char **data, struct cpio_newc_header **next_header_pointer); +int ls(); +int cat(char *path); +#endif \ No newline at end of file diff --git a/lab2/shell/header/dtb.h b/lab2/shell/header/dtb.h new file mode 100644 index 000000000..d5381a5cc --- /dev/null +++ b/lab2/shell/header/dtb.h @@ -0,0 +1,22 @@ +struct fdt_header { + unsigned int magic; + unsigned int totalsize; + unsigned int off_dt_struct; + unsigned int off_dt_strings; + unsigned int off_mem_rsvmap; + unsigned int version; + unsigned int last_comp_version; + unsigned int boot_cpuid_phys; + unsigned int size_dt_strings; + unsigned int size_dt_struct; +}; +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 +typedef void (*dtb_callback)(unsigned int node_type, char *name, void *value, unsigned int name_size); + +unsigned int endian_big2little(unsigned int x); +void fdt_traverse(dtb_callback callback); +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size); \ No newline at end of file diff --git a/lab2/shell/header/mailbox.h b/lab2/shell/header/mailbox.h new file mode 100644 index 000000000..e7c131fc7 --- /dev/null +++ b/lab2/shell/header/mailbox.h @@ -0,0 +1,27 @@ +#ifndef _MAILBOX_H_ +#define _MAILBOX_H_ + +#define MMIO_BASE 0x3f000000 +#define MAILBOX_BASE MMIO_BASE + 0xb880 + +#define MAILBOX_READ (unsigned int*)(MAILBOX_BASE) +#define MAILBOX_STATUS (unsigned int*)(MAILBOX_BASE + 0x18) +#define MAILBOX_WRITE (unsigned int*)(MAILBOX_BASE + 0x20) + + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 + +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +void get_board_revision(); +void mailbox_call(unsigned int* mailbox); +void get_memory_info(); + +#endif \ No newline at end of file diff --git a/lab2/shell/header/malloc.h b/lab2/shell/header/malloc.h new file mode 100644 index 000000000..36c1b15a4 --- /dev/null +++ b/lab2/shell/header/malloc.h @@ -0,0 +1 @@ +void *simple_malloc(unsigned int size); \ No newline at end of file diff --git a/lab2/shell/header/reboot.h b/lab2/shell/header/reboot.h new file mode 100644 index 000000000..bf5420c1f --- /dev/null +++ b/lab2/shell/header/reboot.h @@ -0,0 +1,7 @@ +#ifndef _REBOOT_H_ +#define _REBOOT_H_ + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); +#endif \ No newline at end of file diff --git a/lab2/shell/header/shell.h b/lab2/shell/header/shell.h new file mode 100644 index 000000000..7ce4cee06 --- /dev/null +++ b/lab2/shell/header/shell.h @@ -0,0 +1 @@ +void shell(); \ No newline at end of file diff --git a/lab2/shell/header/uart.h b/lab2/shell/header/uart.h new file mode 100644 index 000000000..a3d45ed28 --- /dev/null +++ b/lab2/shell/header/uart.h @@ -0,0 +1,43 @@ +#ifndef _UART_H_ +#define _UART_H_ +#define MMIO_BASE 0x3f000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPLEV0 ((volatile unsigned int *)(MMIO_BASE + 0x00200034)) +#define GPLEV1 ((volatile unsigned int *)(MMIO_BASE + 0x00200038)) +#define GPEDS0 ((volatile unsigned int *)(MMIO_BASE + 0x00200040)) +#define GPEDS1 ((volatile unsigned int *)(MMIO_BASE + 0x00200044)) +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +#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 + +void uart_init(); +void uart_send_char(unsigned int c); +char uart_get_char(); +void uart_send_str(char *s); +void uart_binary_to_hex(unsigned int d); diff --git a/lab2/shell/header/utils.h b/lab2/shell/header/utils.h new file mode 100644 index 000000000..f12bd534c --- /dev/null +++ b/lab2/shell/header/utils.h @@ -0,0 +1,9 @@ +int string_compare(char *a, char *b); +int strncmp(char *a, char *b, int cnt); +unsigned int hex_to_int(char *a, int size); +int isdigit(int c); +int toupper(int c); +int ishex(int c); +char* strtok(char* str, const char* delimiters); +char *strcpy(char *dest, const char *src); +unsigned int strlen(const char *s); \ No newline at end of file diff --git a/lab2/shell/linker.ld b/lab2/shell/linker.ld new file mode 100644 index 000000000..9bff61183 --- /dev/null +++ b/lab2/shell/linker.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + . = 0x80000; /* Set the memory address to 0x80000 (start point)*/ + + .text : { /* Define the .text section, which contains executable code*/ + KEEP(*(.text.boot)) /* Keep all .text.boot sections*/ + *(.text) /* Keep all other .text sections*/ + } + + .rodata : { *(.rodata) } /* Define the .rodata section, which contains read-only data*/ + + .data : { *(.data) } /* Define the .data section, which contains initialized data*/ + + .bss () : { /* Define the .bss section, which contains uninitialized data*/ + __bss_start = .; /* Define __bss_start symbol as the current memory address*/ + *(.bss) /* Keep all .bss sections*/ + *(COMMON) /* Keep all common symbols*/ + __bss_end = .; /* Define __bss_end symbol as the current memory address*/ + } + __heap_start = .; + _end = .; /* Define _end symbol as the current memory address*/ +} + +__bss_size = (__bss_end - __bss_start) >> 3; /* Calculate the size of the .bss section in bytes*/ diff --git a/lab2/shell/mailbox.c b/lab2/shell/mailbox.c new file mode 100644 index 000000000..2ef732242 --- /dev/null +++ b/lab2/shell/mailbox.c @@ -0,0 +1,69 @@ +#include"header/mailbox.h" +#include"header/uart.h" + +void mailbox_call(unsigned int *mailbox){ + // Write the data (shifted into the upper 28 bits) combined with + // the channel (in the lower four bits) to the write register. + unsigned int r = (((unsigned long)mailbox) & ~0xf) | 8; //mail_ch_prop + // & ~0xf => only "and" upper 28 bit can be saved + // |8 => if upper 28 is 1 => save and ensure last 4 bit is 1 + // Check if Mailbox 0 status register’s full flag is set. + while (*MAILBOX_STATUS & MAILBOX_FULL) { + asm volatile("nop"); + }; + // If not, then you can write to Mailbox 1 Read/Write register. + *MAILBOX_WRITE = r; + while (1) { + // Check if Mailbox 0 status register’s empty flag is set. + while (*MAILBOX_STATUS & MAILBOX_EMPTY) { + asm volatile("nop"); + }; + // If not, then you can read from Mailbox 0 Read/Write register. + // Check if the value is the same as you wrote in step 1. + if (r == *MAILBOX_READ) + return; + } + +} + +void get_board_revision(){ + unsigned int mailbox[7]; + mailbox[0] = 7 * 4; // buffer size in bytes + mailbox[1] = REQUEST_CODE; + // tags begin + mailbox[2] = GET_BOARD_REVISION; // tag identifier + mailbox[3] = 4; // maximum of request and response value buffer's length. + mailbox[4] = TAG_REQUEST_CODE; + mailbox[5] = 0; // value buffer + // tags end + mailbox[6] = END_TAG; + + mailbox_call(mailbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + //printf("0x%x\n", mailbox[5]); // it should be 0xa020d3 for rpi3 b+ + uart_send_str("0x"); + uart_binary_to_hex(mailbox[5]); + uart_send_str("\n"); +} + +void get_memory_info(){ + unsigned int mailbox[8]; + mailbox[0] = 8 * 4; // buffer size in bytes + mailbox[1] = REQUEST_CODE; + // tags begin + mailbox[2] = GET_ARM_MEMORY; // tag identifier + mailbox[3] = 8; // maximum of request and response value buffer's length. + mailbox[4] = TAG_REQUEST_CODE; // tag code + mailbox[5] = 0; // base address + mailbox[6] = 0; // size in bytes + mailbox[7] = END_TAG; // end tag + // tags end + mailbox_call(mailbox); + uart_send_str("ARM memory base address : "); + uart_binary_to_hex(mailbox[5]); + uart_send_str("\n"); + + uart_send_str("ARM memory size : "); + uart_binary_to_hex(mailbox[6]); + uart_send_str("\n"); +} diff --git a/lab2/shell/main.c b/lab2/shell/main.c new file mode 100644 index 000000000..591b14bed --- /dev/null +++ b/lab2/shell/main.c @@ -0,0 +1,18 @@ +#include "header/utils.h" +#include "header/uart.h" +#include "header/shell.h" +#include "header/reboot.h" +#include "header/mailbox.h" +#include "header/cpio.h" +#include "header/dtb.h" +extern char *dtb_base; +int main(char *arg){ + uart_init(); + dtb_base = arg; + fdt_traverse(initramfs_callback); + uart_send_str("\x1b[2J\x1b[H"); + char *s = "Type in `help` to get instruction menu!\n"; + uart_send_str(s); + shell(); + return 0; +} \ No newline at end of file diff --git a/lab2/shell/makefile b/lab2/shell/makefile new file mode 100644 index 000000000..8f955a0c3 --- /dev/null +++ b/lab2/shell/makefile @@ -0,0 +1,29 @@ +CFLAGS = -Wall -ffreestanding -nostdinc -nostdlib -nostartfiles -fno-stack-protector -g +SRCS = $(wildcard *.c) +OBJS = $(SRCS:.c=.o) + +ASMS = $(wildcard *.S) +ASM_OBJS = $(ASMS:.S=.o) + +all:: clean_img flash clean + +%.o: %.S + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + + +%.o: %.c + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + +flash: $(ASM_OBJS) $(OBJS) + aarch64-linux-gnu-ld $(ASM_OBJS) $(OBJS) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy -O binary kernel8.elf shell.img +clean: + rm -f $(ASM_OBJS) $(OBJS) + +clean_img: + rm -f kernel8.elf + rm -f shell.img +test: + qemu-system-aarch64 -machine raspi3b -kernel shell.img -display none -serial null -serial stdio -initrd ../rootfs/initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb +screen: + sudo screen /dev/ttyUSB0 115200 \ No newline at end of file diff --git a/lab2/shell/malloc.c b/lab2/shell/malloc.c new file mode 100644 index 000000000..346a6b55a --- /dev/null +++ b/lab2/shell/malloc.c @@ -0,0 +1,6 @@ +#include"header/malloc.h" +extern __heap_start; +char *top = &__heap_start; +void* simple_malloc(unsigned int size) { + return top += size; +} \ No newline at end of file diff --git a/lab2/shell/reboot.c b/lab2/shell/reboot.c new file mode 100644 index 000000000..089d71dcb --- /dev/null +++ b/lab2/shell/reboot.c @@ -0,0 +1,20 @@ +#include"header/reboot.h" + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/lab2/shell/shell.c b/lab2/shell/shell.c new file mode 100644 index 000000000..e749a28ff --- /dev/null +++ b/lab2/shell/shell.c @@ -0,0 +1,91 @@ +#include"header/shell.h" +#include"header/uart.h" +#include"header/utils.h" +#include"header/reboot.h" +#include"header/mailbox.h" +#include"header/cpio.h" +#include"header/malloc.h" +void shell(){ + char cmd[256]; + char *cur; + while (1) + { + char *s = "# "; + uart_send_str(s); + cur = cmd; + char receive; + while (1) + { + receive = uart_get_char(); + if(receive == '\n'){ + *cur = '\0'; + break; + } + else if(receive == 127){ + if(cur == cmd){ + *cur = '\0'; + continue; + } + *cur = '\0'; + cur--; + uart_send_str("\b \b"); + continue; + } + *cur = receive; + uart_send_char(receive); + cur++; + } + char arg[20][20]; + char *tk = strtok(cmd," "); + for(int i = 0; tk != 0;i++){ + strcpy(arg[i],tk); + tk = strtok(0," "); + } + if(string_compare(arg[0],"help")){ + uart_send_str("\nhelp\t\t:print this help menu\r\n"); + uart_send_str("hello\t\t:print Hello World!\r\n"); + uart_send_str("info\t\t:Get the hardware's information\r\n"); + uart_send_str("ls\t\t:list files in directory\r\n"); + uart_send_str("cat\t\t:cat\r\n"); + uart_send_str("clear\t\t:clear terminal\r\n"); + uart_send_str("reboot\t\t:reboot the device\r\n"); + uart_send_str("malloc\t\t:alloc string\r\n"); + } + else if(string_compare(arg[0],"hello")){ + uart_send_str("\nHello World!\n"); + } + else if(string_compare(arg[0],"info")){ + uart_send_str("\nInfo:\n"); + uart_send_str("Board Vision: "); + get_board_revision(); + get_memory_info(); + + } + else if(string_compare(arg[0],"clear")){ + uart_send_str("\x1b[2J\x1b[H"); + } + else if(string_compare(arg[0],"ls")){ + uart_send_str("\n"); + ls("."); + } + else if(string_compare(arg[0],"cat")){ + uart_send_str("\n"); + cat(arg[1]); + } + else if (string_compare(arg[0],"reboot")) { + uart_send_str("\nRebooting....\n"); + reset(1000); + } + else if (string_compare(arg[0],"malloc")){ + uart_send_str("\n"); + unsigned int size = (strlen(arg[1]) + 31) >> 5 << 5; + char *string = simple_malloc(size); + strcpy(string,arg[1]); + uart_send_str(string); + uart_send_str("\n"); + } + else + uart_send_str("\n"); + } + +} \ No newline at end of file diff --git a/lab2/shell/shell_init.S b/lab2/shell/shell_init.S new file mode 100644 index 000000000..7d65d428e --- /dev/null +++ b/lab2/shell/shell_init.S @@ -0,0 +1,27 @@ +.section ".text.boot" // Start a new section named ".text.boot" +.global _start // Declare _start symbol as global + +_start: // Start of the _start block + // read cpu id, stop slave cores + mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register into x1 + and x1, x1, #3 // Mask the lower 2 bits (CPU core ID) from x1 + cbz x1, setting // If x1 is zero, jump to setting label + +halt: wfe // Wait for Event instruction + b halt // Branch (jump) to halt (infinite loop) + +setting: // Setting label + ldr x1, =_start // Load the address of _start into x1 + mov sp, x1 // Move the value of x1 into the Stack Pointer (sp) + ldr x1, =__bss_start // Load the address of __bss_start into x1 + ldr w2, =__bss_size // Load the value of __bss_size into w2 + +clear_bss: // Clear BSS segment loop label + cbz w2, kernel_main // If w2 (BSS size) is zero, jump to kernel_main + str xzr, [x1], #8 // Store zero (xzr) at the address pointed by x1, then increment x1 by 8 + sub w2, w2, #1 // Decrement w2 (BSS size) by 1 + cbnz w2, clear_bss // If w2 is not zero, jump back to clear_bss + +kernel_main: // Label for the start of kernel_main function + bl main // Branch with link (call) to main function + b halt // Branch to halt (infinite loop) diff --git a/lab2/shell/uart.c b/lab2/shell/uart.c new file mode 100644 index 000000000..cf4ec1b6c --- /dev/null +++ b/lab2/shell/uart.c @@ -0,0 +1,66 @@ +#include"header/uart.h" + +void uart_init(){ + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + + register unsigned int r; + + //??? + r =* GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 innitial + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = 0; // flush GPIO setup + + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +void uart_send_char(unsigned int c){ + do{asm volatile("nop");}while(!(*AUX_MU_LSR & 0x20)); // This bit is set if the transmit FIFO can accept at least one byte. + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +char uart_get_char(){ + char r; + /* wait until something is in the buffer */ + //bit 0 is set if the receive FIFO holds at least 1 symbol. + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + /* convert carriage return to newline */ + return r=='\r'?'\n':r; +} + +void uart_send_str(char *s){ + while(*s) { + /* convert newline to carriage return + newline */ + if(*s=='\n') + uart_send_char('\r'); + uart_send_char(*s++); + } +} + +void uart_binary_to_hex(unsigned int d) { + unsigned int n; + int c; + uart_send_str("0x"); + for(c=28;c>=0;c-=4) { + // get highest tetrad + n=(d>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + uart_send_char(n); + } +} \ No newline at end of file diff --git a/lab2/shell/utils.c b/lab2/shell/utils.c new file mode 100644 index 000000000..4e04a6b84 --- /dev/null +++ b/lab2/shell/utils.c @@ -0,0 +1,95 @@ +#include"header/utils.h" +#include"header/uart.h" + +int string_compare(char *s1,char *s2) { + while (*s1 && *s2 && (*s1 == *s2)) { + s1++; + s2++; + } + return !(*s1 - *s2); +} + +int strncmp(char *s1, char *s2, int n) { + while (n-- && *s1 && (*s1 == *s2)) { + s1++; + s2++; + } + if (n == (int) -1) { + return 0; + } + return *(const unsigned char *) s1 - *(const unsigned char *) s2; +} +int isdigit(int c){ + return c >= '0' && c <= '9'; +} +int toupper(int c){ + if (c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } else { + return c; + } +} + +int ishex(int c){ + return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F'); +} + +unsigned int hex_to_int(char *a, int size){ + unsigned int result = 0; + + for (int i = 0; i < size; i++) { + char c = a[i]; + if (ishex(c)) { + int val = isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; + result = (result << 4) + val; + } + } + + return result; +} + +char* strtok(char* str, const char* delimiters) { + static char* buffer = 0; + if (str != 0) { + buffer = str; + } + if (buffer == 0) { + return 0; + } + char* start = buffer; + while (*buffer != '\0') { + const char* delim = delimiters; + while (*delim != '\0') { + if (*buffer == *delim) { + *buffer = '\0'; + buffer++; + if (start != buffer) { + return start; + } else { + start++; + break; + } + } + delim++; + } + if (*delim == '\0') { + buffer++; + } + } + if (start == buffer) { + return 0; + } else { + return start; + } +} +char *strcpy(char *dest, const char *src) { + char *ret = dest; + while ((*dest++ = *src++)); + return ret; +} + +unsigned int strlen(const char *s) { + int len = 0; + while (*s++) len++; + return len; +} \ No newline at end of file diff --git a/lab2/writer.py b/lab2/writer.py new file mode 100644 index 000000000..738cc1871 --- /dev/null +++ b/lab2/writer.py @@ -0,0 +1,25 @@ +import serial +import os +import time + + +tty = serial.Serial("/dev/ttyUSB0", 115200, timeout=0.5) +# acquire the file size +file_stats = os.stat("./shell/shell.img") +# issue request and tell the size of img to rec +tty.write(str(file_stats.st_size).encode('utf-8')) +# size sended +# python3 .encode() +tty.write(str("\n").encode('utf-8')) +time.sleep(0.0005) +# send img byte-by-byte +# delay to ensure no loss +# uart is low speed interface +# if sleep too short e.g: 0.0001, it may loss +with open("./shell/shell.img", "rb") as fp: + byte = fp.read(1) + while byte: + tty.write(byte) + byte = fp.read(1) + # delay enough time to ensure no loss + time.sleep(0.0005) \ No newline at end of file diff --git a/lab3/.vscode/settings.json b/lab3/.vscode/settings.json new file mode 100644 index 000000000..c4d142e9b --- /dev/null +++ b/lab3/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "cpio.h": "c", + "uart.h": "c" + } +} \ No newline at end of file diff --git a/lab3/bcm2710-rpi-3-b-plus.dtb b/lab3/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..c83b0817e Binary files /dev/null and b/lab3/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab3/bootloader/booting.S b/lab3/bootloader/booting.S new file mode 100644 index 000000000..7d65d428e --- /dev/null +++ b/lab3/bootloader/booting.S @@ -0,0 +1,27 @@ +.section ".text.boot" // Start a new section named ".text.boot" +.global _start // Declare _start symbol as global + +_start: // Start of the _start block + // read cpu id, stop slave cores + mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register into x1 + and x1, x1, #3 // Mask the lower 2 bits (CPU core ID) from x1 + cbz x1, setting // If x1 is zero, jump to setting label + +halt: wfe // Wait for Event instruction + b halt // Branch (jump) to halt (infinite loop) + +setting: // Setting label + ldr x1, =_start // Load the address of _start into x1 + mov sp, x1 // Move the value of x1 into the Stack Pointer (sp) + ldr x1, =__bss_start // Load the address of __bss_start into x1 + ldr w2, =__bss_size // Load the value of __bss_size into w2 + +clear_bss: // Clear BSS segment loop label + cbz w2, kernel_main // If w2 (BSS size) is zero, jump to kernel_main + str xzr, [x1], #8 // Store zero (xzr) at the address pointed by x1, then increment x1 by 8 + sub w2, w2, #1 // Decrement w2 (BSS size) by 1 + cbnz w2, clear_bss // If w2 is not zero, jump back to clear_bss + +kernel_main: // Label for the start of kernel_main function + bl main // Branch with link (call) to main function + b halt // Branch to halt (infinite loop) diff --git a/lab3/bootloader/bootloader.c b/lab3/bootloader/bootloader.c new file mode 100644 index 000000000..b4813137a --- /dev/null +++ b/lab3/bootloader/bootloader.c @@ -0,0 +1,64 @@ +#include"header/bootloader.h" +#include"header/uart.h" +#include"header/utils.h" +// from linker script +extern char _start; +extern char _end; +void relocate(char *arg) +{ + unsigned long bootloader_size = (&_end - &_start); + char *oldbootloader = (char *)&_start; //0x80000 + char *newbootloader = (char *)0x60000; + + unsigned long bl_ptr = 0; + // copying + while (bootloader_size--) + { + newbootloader[bl_ptr] = oldbootloader[bl_ptr]; + ++bl_ptr; + } + // run kernel in 0x60000 + void (*run)(char *) = (void (*)(char *))newbootloader; + run(arg); +} +// can be verifying in gdb +void load_img(char *dtb_base) { + // kernel start + char *kernel = (char *)(0x80000); + int kn_ptr = 0; + + // size + int idx = 0; + char sz[50] = {}; + char c; + + // receiving str size + while(1) { + c = uart_get_char(); + // receive size end + if(c == '\n') { + sz[idx] = '\0'; + break; + } + sz[idx++] = c; + } + // get kernel image size + int size = atoi(sz); + uart_send_str(sz); + + // receive kernel img + while (size--) + kernel[kn_ptr++] = uart_get_img_char(); + // test message + uart_binary_to_hex((unsigned int) dtb_base); + uart_send_str("\nKernel received\n"); + int r = 1000; + while(r--){ + asm volatile("nop"); + } + // run kernel and pass dtb base + void (*run)(char *) = (void *)kernel; + // get dtb loading address + // run("0x60000"); + run(dtb_base); +} \ No newline at end of file diff --git a/lab3/bootloader/header/bootloader.h b/lab3/bootloader/header/bootloader.h new file mode 100644 index 000000000..3f3b59262 --- /dev/null +++ b/lab3/bootloader/header/bootloader.h @@ -0,0 +1,2 @@ +void load_img(); +void relocate(char *arg); \ No newline at end of file diff --git a/lab3/bootloader/header/reboot.h b/lab3/bootloader/header/reboot.h new file mode 100644 index 000000000..bf5420c1f --- /dev/null +++ b/lab3/bootloader/header/reboot.h @@ -0,0 +1,7 @@ +#ifndef _REBOOT_H_ +#define _REBOOT_H_ + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); +#endif \ No newline at end of file diff --git a/lab3/bootloader/header/shell.h b/lab3/bootloader/header/shell.h new file mode 100644 index 000000000..12a19e675 --- /dev/null +++ b/lab3/bootloader/header/shell.h @@ -0,0 +1,3 @@ +void ls(); +void cat(); +void shell(); \ No newline at end of file diff --git a/lab3/bootloader/header/uart.h b/lab3/bootloader/header/uart.h new file mode 100644 index 000000000..542a57c14 --- /dev/null +++ b/lab3/bootloader/header/uart.h @@ -0,0 +1,44 @@ +#ifndef _UART_H_ +#define _UART_H_ +#define MMIO_BASE 0x3f000000 + +#define GPFSEL0 ((volatile unsigned int *)(MMIO_BASE + 0x00200000)) +#define GPFSEL1 ((volatile unsigned int *)(MMIO_BASE + 0x00200004)) +#define GPFSEL2 ((volatile unsigned int *)(MMIO_BASE + 0x00200008)) +#define GPFSEL3 ((volatile unsigned int *)(MMIO_BASE + 0x0020000C)) +#define GPFSEL4 ((volatile unsigned int *)(MMIO_BASE + 0x00200010)) +#define GPFSEL5 ((volatile unsigned int *)(MMIO_BASE + 0x00200014)) +#define GPSET0 ((volatile unsigned int *)(MMIO_BASE + 0x0020001C)) +#define GPSET1 ((volatile unsigned int *)(MMIO_BASE + 0x00200020)) +#define GPCLR0 ((volatile unsigned int *)(MMIO_BASE + 0x00200028)) +#define GPLEV0 ((volatile unsigned int *)(MMIO_BASE + 0x00200034)) +#define GPLEV1 ((volatile unsigned int *)(MMIO_BASE + 0x00200038)) +#define GPEDS0 ((volatile unsigned int *)(MMIO_BASE + 0x00200040)) +#define GPEDS1 ((volatile unsigned int *)(MMIO_BASE + 0x00200044)) +#define GPHEN0 ((volatile unsigned int *)(MMIO_BASE + 0x00200064)) +#define GPHEN1 ((volatile unsigned int *)(MMIO_BASE + 0x00200068)) +#define GPPUD ((volatile unsigned int *)(MMIO_BASE + 0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(MMIO_BASE + 0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int *)(MMIO_BASE + 0x0020009C)) + +#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 + +void uart_init(); +void uart_send_char(unsigned int c); +char uart_get_char(); +void uart_send_str(char *s); +void uart_binary_to_hex(unsigned int d); +char uart_get_img_char(); \ No newline at end of file diff --git a/lab3/bootloader/header/utils.h b/lab3/bootloader/header/utils.h new file mode 100644 index 000000000..aaa489a29 --- /dev/null +++ b/lab3/bootloader/header/utils.h @@ -0,0 +1,2 @@ +int string_compare(char *a, char *b); +int atoi(char *str); diff --git a/lab3/bootloader/linker.ld b/lab3/bootloader/linker.ld new file mode 100644 index 000000000..958e7c546 --- /dev/null +++ b/lab3/bootloader/linker.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + . = 0x80000; /* Set the memory address to 0x80000 (start point)*/ + + .text : { /* Define the .text section, which contains executable code*/ + KEEP(*(.text.boot)) /* Keep all .text.boot sections*/ + *(.text) /* Keep all other .text sections*/ + } + + .rodata : { *(.rodata) } /* Define the .rodata section, which contains read-only data*/ + + .data : { *(.data) } /* Define the .data section, which contains initialized data*/ + + .bss () : { /* Define the .bss section, which contains uninitialized data*/ + __bss_start = .; /* Define __bss_start symbol as the current memory address*/ + *(.bss) /* Keep all .bss sections*/ + *(COMMON) /* Keep all common symbols*/ + __bss_end = .; /* Define __bss_end symbol as the current memory address*/ + } + + _end = .; /* Define _end symbol as the current memory address*/ +} + +__bss_size = (__bss_end - __bss_start) >> 3; /* Calculate the size of the .bss section in bytes*/ diff --git a/lab3/bootloader/main.c b/lab3/bootloader/main.c new file mode 100644 index 000000000..5f1653f7d --- /dev/null +++ b/lab3/bootloader/main.c @@ -0,0 +1,20 @@ +#include"header/uart.h" +#include"header/bootloader.h" +#include"header/shell.h" +char *dtb_base; +int relocated = 1; +void main(char *arg) +{ + uart_init(); + + // register x0 + dtb_base = arg; + + // relocate copies bootloader program from 0x80000 to 0x60000 + if (relocated) { + relocated = 0; + relocate(arg); + } + uart_send_str("\x1b[2J\x1b[H"); + shell(dtb_base); +} \ No newline at end of file diff --git a/lab3/bootloader/makefile b/lab3/bootloader/makefile new file mode 100644 index 000000000..34df2ec5e --- /dev/null +++ b/lab3/bootloader/makefile @@ -0,0 +1,31 @@ +CFLAGS = -Wall -ffreestanding -nostdinc -nostdlib -nostartfiles -fno-stack-protector -g +SRCS = $(wildcard *.c) +OBJS = $(SRCS:.c=.o) + +ASMS = $(wildcard *.S) +ASM_OBJS = $(ASMS:.S=.o) + +all:: clean_img flash clean + +%.o: %.S + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + + +%.o: %.c + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + +flash: $(ASM_OBJS) $(OBJS) + aarch64-linux-gnu-ld $(ASM_OBJS) $(OBJS) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy kernel8.elf -O binary kernel8.img +clean: + rm -f $(ASM_OBJS) $(OBJS) + +clean_img: + rm -f kernel8.elf + rm -f kernel8.img +test: + qemu-system-aarch64 -machine raspi3b -kernel kernel8.img -display none -serial null -serial stdio -initrd ../rootfs/initramfs.cpio +test_pty: + qemu-system-aarch64 -machine raspi3b -kernel kernel8.img -display none -serial null -serial pty +screen: + sudo screen /dev/ttyUSB0 115200 \ No newline at end of file diff --git a/lab3/bootloader/reboot.c b/lab3/bootloader/reboot.c new file mode 100644 index 000000000..089d71dcb --- /dev/null +++ b/lab3/bootloader/reboot.c @@ -0,0 +1,20 @@ +#include"header/reboot.h" + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/lab3/bootloader/shell.c b/lab3/bootloader/shell.c new file mode 100644 index 000000000..036db19ba --- /dev/null +++ b/lab3/bootloader/shell.c @@ -0,0 +1,59 @@ +#include"header/shell.h" +#include"header/uart.h" +#include"header/utils.h" +#include"header/reboot.h" +#include"header/bootloader.h" + +void shell(char *dtb_base){ + char cmd[256]; + char *cur; + while (1) + { + char *s = "# "; + uart_send_str(s); + cur = cmd; + char receive; + while (1) + { + receive = uart_get_char(); + if(receive == '\n'){ + *cur = '\0'; + break; + } + else if(receive == 127){ + if(cur == cmd){ + *cur = '\0'; + continue; + } + *cur = '\0'; + cur--; + uart_send_str("\b \b"); + continue; + } + *cur = receive; + uart_send_char(receive); + cur++; + } + cur = cmd; + if(string_compare(cur,"help")){ + uart_send_str("\nhelp\t\t:print this help menu\r\n"); + uart_send_str("hello\t\t:print Hello World!\r\n"); + uart_send_str("load\t\t:load kernel image through uart\r\n"); + uart_send_str("reboot\t\t:reboot the device\r\n"); + } + else if(string_compare(cur,"hello")){ + uart_send_str("\nHello World!\n"); + } + else if(string_compare(cur,"load")){ + uart_send_str("\nload kernel...\n"); + load_img(dtb_base); + } + else if (string_compare(cur,"reboot")) { + uart_send_str("\nRebooting....\n"); + reset(1000); + + } + else + uart_send_str("\n"); + } +} \ No newline at end of file diff --git a/lab3/bootloader/uart.c b/lab3/bootloader/uart.c new file mode 100644 index 000000000..4d35998a6 --- /dev/null +++ b/lab3/bootloader/uart.c @@ -0,0 +1,74 @@ +#include"header/uart.h" + +void uart_init(){ + + *AUX_ENABLE |= 1; + *AUX_MU_CNTL = 0; + *AUX_MU_IER = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_BAUD = 270; + + register unsigned int r; + + //??? + r =* GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 innitial + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = 0; // flush GPIO setup + + *AUX_MU_IIR = 6; + *AUX_MU_CNTL = 3; +} + +void uart_send_char(unsigned int c){ + do{asm volatile("nop");}while(!(*AUX_MU_LSR & 0x20)); // This bit is set if the transmit FIFO can accept at least one byte. + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +char uart_get_char(){ + char r; + /* wait until something is in the buffer */ + //bit 0 is set if the receive FIFO holds at least 1 symbol. + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + /* convert carriage return to newline */ + return r=='\r'?'\n':r; +} +char uart_get_img_char(){ + char r; + /* wait until something is in the buffer */ + //bit 0 is set if the receive FIFO holds at least 1 symbol. + do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); + /* read it and return */ + r=(char)(*AUX_MU_IO); + return r; +} +void uart_send_str(char *s){ + while(*s) { + /* convert newline to carriage return + newline */ + if(*s=='\n') + uart_send_char('\r'); + uart_send_char(*s++); + } +} + +void uart_binary_to_hex(unsigned int d) { + unsigned int n; + int c; + uart_send_str("0x"); + for(c=28;c>=0;c-=4) { + // get highest tetrad + n=(d>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + uart_send_char(n); + } +} \ No newline at end of file diff --git a/lab3/bootloader/utils.c b/lab3/bootloader/utils.c new file mode 100644 index 000000000..0d398f6f5 --- /dev/null +++ b/lab3/bootloader/utils.c @@ -0,0 +1,34 @@ +#include"header/utils.h" +#include"header/uart.h" + +int string_compare(char* a, char* b) { + char *p1=a; + char *p2=b; + while(1){ + char c1 = *p1; + p1++; + char c2 = *p2; + p2++; + + if(c1==c2){ + if(c1 == '\0') return 1; + else continue; + } + else return 0; + + } +} + +int atoi(char *str) +{ + int res = 0; + + for (int i = 0; str[i] != '\0'; ++i) + { + if (str[i] > '9' || str[i] < '0') + return res; + res = res * 10 + str[i] - '0'; + } + + return res; +} diff --git a/lab3/config.txt b/lab3/config.txt new file mode 100644 index 000000000..8c6facb68 --- /dev/null +++ b/lab3/config.txt @@ -0,0 +1,4 @@ +kernel_address=0x60000 +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x20000000 \ No newline at end of file diff --git a/lab3/rootfs/file1.txt b/lab3/rootfs/file1.txt new file mode 100644 index 000000000..3677cc6cc --- /dev/null +++ b/lab3/rootfs/file1.txt @@ -0,0 +1,55 @@ + _oI=vo__ + ?/$="'" """^SATAN$~\ + .&?/' `""$$, + ,/?/' /-"^\. .-=~\T, + ,/?/' /SATAN| |\IS,&' |LT + `\?\\ ``\?\^I/HATE@:~:$=v\. `$k==v\.??\, `\d `\$$'9P'I-LOVE=SATAN\/$$~?$\ ,R/ + /$?~^'"""""`"\\&&< ?b "`~$P:c: /v==v,#::?<<&:'T| d$/' + [|:. ""=o/&. ,P o&Z'`'.##| |MH\|| ,$$' + `=:$H&=\. `"b?b. .&' 96*.-v.:?/`\==$&?$&*' + `^$?\. `*&*\\ ,P ?~-~' |$$S>' + `\7b ,T/\&&\. d? |T' + \/b .&J' `\> d' T, + &`L /|| ?| ?, + ||9 J\T H ?, + H|| ||/ || 6 6 6 9, + ||M PJ' || 6 6 6 `H + bT, ||T || 66 666 666 || + T/L H|| `b 6 6 6 6 6 6 M + &T, M| 9, 666 666 666 9 + `L9, M| `&. | + `?*,9|| `b d + `\?(|H. `b ?b + `*\ `&. `\. J*|b + `\o/\. `&. ,P 9/L + 9:&. `9\ ?? `H9. + *?9\ `b .&' |/| + `|`\. `L ./' `|H + d\/qZbo. M .,=' ,|T + ./~&$$?=??/' `"=H$| H .o='' J\| + ,*/'' `\? `' ./?ov=="*b9, ,$P + ,Td ,$$'`' ?|M ,$/ + J|| ,$?/ M|| ?$/ + M|| |>\. ._,~9$'' T|| d'M. + 9`| `Hi:R&:&&6&="' ./$J| `^"\Z\. + ||M `=Z\:"" H|T" `&H&>v_ + bT, .. v,?|\ M|| .:Z|&\. + ||H _DEATH~>TO9H| `?*\ ?$`#'H + 9ALL|1KIDS* .$/ `bZ&\ ,o\&KILL&/' + \?$.:?ooo/*""' `\$$b_ |\MAIM*:./' + `"""' `' `~?&qDESTROY#/' + "^~DIE/" + diff --git a/lab3/rootfs/file2.txt b/lab3/rootfs/file2.txt new file mode 100644 index 000000000..cfb787b3e --- /dev/null +++ b/lab3/rootfs/file2.txt @@ -0,0 +1,14 @@ +fasdfdsafsdafadsf +kernel_addressfasdfsdaf +kernel_addressfasdfsdafsadf +sdaf +sdafsad +fasdfdsafsdafadsfsadf +sdafhert +h +hbafs +gf +bootloaderhewr +hbafsabvfhwert +rwetr +gefgvgagretraegearsujew5y diff --git a/lab3/rootfs/initramfs.cpio b/lab3/rootfs/initramfs.cpio new file mode 100644 index 000000000..de645099b Binary files /dev/null and b/lab3/rootfs/initramfs.cpio differ diff --git a/lab3/shell/cpio.c b/lab3/shell/cpio.c new file mode 100644 index 000000000..06a9aa5cd --- /dev/null +++ b/lab3/shell/cpio.c @@ -0,0 +1,156 @@ +#include"header/cpio.h" +#include"header/utils.h" +#include"header/exec.h" +char *cpio_start; +char *cpio_end; +int cpio_newc_parse_header(struct cpio_newc_header *this_header_pointer, char **pathname, unsigned int *filesize, char **data, struct cpio_newc_header **next_header_pointer) +{ + // Ensure magic header 070701 + // new ascii format + if (strncmp(this_header_pointer->c_magic, CPIO_NEWC_HEADER_MAGIC, sizeof(this_header_pointer->c_magic)) != 0){ + return -1; + } + // transfer big endian 8 byte hex string to unsinged int + // data size + *filesize = hex_to_int(this_header_pointer->c_filesize, 8); + + // end of header is the pathname + // header | pathname str | data + *pathname = ((char *)this_header_pointer) + sizeof(struct cpio_newc_header); + // uart_send_str(pathname[0]); + // get file data, file data is just after pathname + // header | pathname str | data + // check picture on hackmd note + unsigned int pathname_length = hex_to_int(this_header_pointer->c_namesize, 8); + // get the offset to start of data + // | offset | data + unsigned int offset = pathname_length + sizeof(struct cpio_newc_header); + // pathname and data might be zero padding + // section % 4 ==0 + offset = offset % 4 == 0 ? offset : (offset + 4 - offset % 4); // padding + // header pointer + offset = start of data + // h| offset | data + *data = (char *)this_header_pointer + offset; + + // get next header pointer + if (*filesize == 0) + // hardlinked files handeld by setting filesize to zero + *next_header_pointer = (struct cpio_newc_header *)*data; + else + { + // data size + offset = *filesize; + // move pointer to the end of data + *next_header_pointer = (struct cpio_newc_header *)(*data + (offset % 4 == 0 ? offset : (offset + 4 - offset % 4))); + } + + // if filepath is TRAILER!!! means there is no more files. + // end of archieve + // empty filename : TRAILER!!! + if (strncmp(*pathname, "TRAILER!!!", sizeof("TRAILER!!!")) == 0) + *next_header_pointer = 0; + + return 0; +} +int ls(){ + char *filepath; + char *filedata; + unsigned int filesize; + // current pointer + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + // print every cpio pathname + while (header_pointer) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // if parse header error + if (error) + { + uart_send_str("error\r\n"); + break; + } + + // if this is not TRAILER!!! (last of file) + if (header_pointer != 0){ + uart_send_str(filepath); + uart_send_str("\r\n"); + } + } + return 0; +} +int cat(char *path){ + char *filepath; + char *filedata; + unsigned int filesize; + // current header pointer, cpio start + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + + while (header_pointer) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + // if parse header error + if (error == -1) + { + uart_send_str("error \r\n"); + break; + } + // parse until filepath is same as cat input + // print the content of input file + if (string_compare(path, filepath)) + { + if(string_compare(filepath,".")){ + uart_send_str("cat: "); + uart_send_str(path); + uart_send_str("No such file or directory\r\n"); + break; + } + for (unsigned int i = 0; i < filesize; i++) + uart_send_char(filedata[i]); + uart_send_str("\r\n"); + break; + } + // end of cpio, cannot find input file + if (header_pointer == 0){ + uart_send_str("cat: \""); + uart_send_str(path); + uart_send_str("\" No such file or directory\r\n"); + break; + } + + } + return 0; +} +int execfile(char *thefilepath) +{ + char* filepath; + char* filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = (struct cpio_newc_header *)cpio_start; + while(header_pointer) + { + int error = cpio_newc_parse_header(header_pointer,&filepath,&filesize,&filedata,&header_pointer); + //if parse header error + if(error) + { + uart_send_str("error"); + break; + } + // traverse until find the filepath + // program start address is data + // header | pathname str | data + if(string_compare(thefilepath,filepath)) + { + exec(filedata); + break; + } + //if is TRAILER (last of file) + if (header_pointer == 0) + { + uart_send_str("execfile: "); + uart_send_str(thefilepath); + uart_send_str(": No such file or directory\r\n"); + return -1; + } + } + return 0; +} \ No newline at end of file diff --git a/lab3/shell/dtb.c b/lab3/shell/dtb.c new file mode 100644 index 000000000..2f9f752df --- /dev/null +++ b/lab3/shell/dtb.c @@ -0,0 +1,109 @@ +#include"header/dtb.h" +#include"header/utils.h" +#include"header/uart.h" +extern char *cpio_start; +extern char *cpio_end; +char *dtb_base; +unsigned int endian_big2little(unsigned int x) { + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +} +// a tree data structure which indicating what devices are on a computer system. +// only find out node of initramfs and get the address +void fdt_traverse(dtb_callback callback) +{ + struct fdt_header *header = (struct fdt_header *)dtb_base; + // fdt header magic 0xD00DFEED (big-endian) + if (endian_big2little(header->magic) != 0xD00DFEED) + { + uart_send_str("fdt_traverse: wrong magic in fdt_traverse\n"); + uart_send_str("expect: 0XD00DFEED, get: "); + uart_send_char(endian_big2little(header->magic)); + uart_send_str("\n"); + return; + } + + // length in bytes of structure block section of dtb + unsigned int struct_size = endian_big2little(header->size_dt_struct); + + // check hackmd notes about the picture of DTB structure + // header is address of fdt_header, so we need (char *) + // offset in bytes of the structure block from beginning of header + // to locate struct start + char *dt_struct_ptr = (char *)((char *)header + endian_big2little(header->off_dt_struct)); + // offset in bytes of strings block from beginning of header + // to locate string start + // fdt_prop use string_ptr + nameoff to get the pathname + char *dt_strings_ptr = (char *)((char *)header + endian_big2little(header->off_dt_strings)); + + // parse from struct begin to end + char *end = (char *)dt_struct_ptr + struct_size; + char *pointer = dt_struct_ptr; + + // according to lexical structure + while (pointer < end) + { + // lexical big-endian-32-bit integer + // all tokens shall be alligned on 32-bit boundary + unsigned int token_type = endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // lexical structure + switch (token_type) + { + // begin of node's representation + case FDT_BEGIN_NODE: + // move node's unit name + // string end \0 + pointer += strlen(pointer); + // node name is followed by zeroed padding bytes + // allign + pointer += (4 - (unsigned long long)pointer % 4); + break; + + // end of node's representation + case FDT_END_NODE: + break; + + case FDT_PROP: + + // len | name offset | address + // uint32_t + // length of prop values in byte + unsigned int len = endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // nameoff save offset of string blocks + // strings_ptr + nameoff get the name + char *name = (char *)dt_strings_ptr + endian_big2little(*(unsigned int *)pointer); + pointer += 4; + + // check node is initrd-start/end and set cpio_start/end address + callback(token_type, name, pointer, len); + // address, byte string of length len + pointer += len; + // followed by zeroed padding bytes + if ((unsigned long long)pointer % 4 != 0) + pointer += 4 - (unsigned long long)pointer % 4; // alignment 4 byte + break; + // ** cant skip + // ignore NOP + case FDT_NOP: + break; + // marks end of structures block + case FDT_END: + break; + default: + return; + } + } +} + +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size) +{ + if (string_compare(name, "linux,initrd-start")){ + cpio_start = (char *)(unsigned long long)endian_big2little(*(unsigned int *)value); + } + if (string_compare(name, "linux,initrd-end")){ + cpio_end = (char *)(unsigned long long)endian_big2little(*(unsigned int *)value); + } +} \ No newline at end of file diff --git a/lab3/shell/exec.c b/lab3/shell/exec.c new file mode 100644 index 000000000..787bd9875 --- /dev/null +++ b/lab3/shell/exec.c @@ -0,0 +1,36 @@ +#include"header/exec.h" +#include"header/malloc.h" + +void exec(char *testing){ + // allocate space for executing the input program + char *ustack = simple_malloc(2000); + // goto el0 to run program + + //uses the msr (Move to System Register) instruction to set the + //SPSR_EL1 (Saved Program Status Register at Exception Level 1) to the value of xzr (zero register). + //This effectively clears the SPSR_EL1, resetting any flags (like condition flags or interrupt masks) it holds. + // spsr_el1 + // 0~3 bit 0b0000 : el0t , jump to el0 and use el0 stack + // 6~9 bit 0b0000 : turn on every interrupt + asm volatile("msr spsr_el1, xzr\n\t"); + //writes the value in the variable testing to the ELR_EL1 (Exception Link Register at Exception Level 1) register. + //The ELR_EL1 holds the return address for exceptions, typically used when returning from an exception back to the + //main execution flow. + //The value in testing is expected to be a memory address + //where execution should resume upon executing the eret instruction later. + // elr_el1 set program start address + asm volatile("msr elr_el1, %0\n\t" + ::"r"(testing)); + //sets the SP_EL0 (Stack Pointer for Exception Level 0, set at top) register to the address ustack + 2000. + //This setup is preparing the stack pointer for use in EL0, usually after an exception return. + //The +2000 likely ensures that there's adequate space allocated in the stack + //to handle whatever process will run at EL0, avoiding stack overflow. + asm volatile("msr sp_el0, %0\n\t":: + "r"(ustack + 2000)); + //return from an exception. This instruction uses the value of ELR_EL1 to set the program counter + // and SPSR_EL1 to restore previously saved processor state. + //Essentially, this transfers control to the address specified in ELR_EL1, effectively resuming execution at the point + //decided by earlier instructions. + asm volatile("eret\n\t" ); + //stack will keep growing +} diff --git a/lab3/shell/header/cpio.h b/lab3/shell/header/cpio.h new file mode 100644 index 000000000..870e00ac5 --- /dev/null +++ b/lab3/shell/header/cpio.h @@ -0,0 +1,31 @@ +#ifndef CPIO_H +#define CPIO_H + +#include "uart.h" +#include "utils.h" + +#define CPIO_NEWC_HEADER_MAGIC "070701" // big endian + +struct cpio_newc_header +{ + char c_magic[6]; // Magic number identifying the CPIO archive format. Should be "070701" for newc format. + char c_ino[8]; // File inode number. + char c_mode[8]; // File mode (permissions and file type). + char c_uid[8]; // User ID of the file owner. + char c_gid[8]; // Group ID of the file owner. + char c_nlink[8]; // Number of hard links to the file. + char c_mtime[8]; // Modification time of the file (timestamp). + char c_filesize[8]; // Size of the file in bytes. + char c_devmajor[8]; // Major number of the device (for character or block special files). + char c_devminor[8]; // Minor number of the device. + char c_rdevmajor[8]; // Major number of the device for special files. + char c_rdevminor[8]; // Minor number of the device for special files. + char c_namesize[8]; // Size of the file name including null terminator. + char c_check[8]; // Checksum of the file header. +}; +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size); +int cpio_newc_parse_header(struct cpio_newc_header *this_header_pointer, char **pathname, unsigned int *filesize, char **data, struct cpio_newc_header **next_header_pointer); +int ls(); +int cat(char *path); +int execfile(char *thefilepath); +#endif \ No newline at end of file diff --git a/lab3/shell/header/dtb.h b/lab3/shell/header/dtb.h new file mode 100644 index 000000000..d5381a5cc --- /dev/null +++ b/lab3/shell/header/dtb.h @@ -0,0 +1,22 @@ +struct fdt_header { + unsigned int magic; + unsigned int totalsize; + unsigned int off_dt_struct; + unsigned int off_dt_strings; + unsigned int off_mem_rsvmap; + unsigned int version; + unsigned int last_comp_version; + unsigned int boot_cpuid_phys; + unsigned int size_dt_strings; + unsigned int size_dt_struct; +}; +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 +typedef void (*dtb_callback)(unsigned int node_type, char *name, void *value, unsigned int name_size); + +unsigned int endian_big2little(unsigned int x); +void fdt_traverse(dtb_callback callback); +void initramfs_callback(unsigned int node_type, char *name, void *value, unsigned int name_size); \ No newline at end of file diff --git a/lab3/shell/header/exec.h b/lab3/shell/header/exec.h new file mode 100644 index 000000000..d9c2d7abe --- /dev/null +++ b/lab3/shell/header/exec.h @@ -0,0 +1,5 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +void exec(char *testing); +#endif \ No newline at end of file diff --git a/lab3/shell/header/irq.h b/lab3/shell/header/irq.h new file mode 100644 index 000000000..0123d2f98 --- /dev/null +++ b/lab3/shell/header/irq.h @@ -0,0 +1,11 @@ +#ifndef IRQ_H +#define IRQ_H +#include "mailbox.h" +#define IRQ_PENDING_1 ((volatile unsigned int *)(MMIO_BASE + 0xB204)) +#define CORE0_INTERRUPT_SOURCE ((volatile unsigned int *)(0x40000060)) + +void except_handler_c(); +void irq_except_handler_c(); +void enable_interrupt(); +void disable_interrupt(); +#endif \ No newline at end of file diff --git a/lab3/shell/header/list.h b/lab3/shell/header/list.h new file mode 100644 index 000000000..3f3b45b37 --- /dev/null +++ b/lab3/shell/header/list.h @@ -0,0 +1,122 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +/* + * Circular doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + * + * https://github.com/torvalds/linux/blob/master/include/linux/list. + * https://elixir.bootlin.com/linux/latest/source/scripts/kconfig/list.h#L24 + */ + +typedef struct list_head { + struct list_head *next, *prev; +}list_head_t; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *node, + struct list_head *prev, + struct list_head *next) +{ + next->prev = node; + node->next = next; + node->prev = prev; + prev->next = node; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *node, struct list_head *head) +{ + __list_add(node, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *node, struct list_head *head) +{ + __list_add(node, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + + +/** + * list_is_head - tests whether @list is the list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next) + +#endif \ No newline at end of file diff --git a/lab3/shell/header/mailbox.h b/lab3/shell/header/mailbox.h new file mode 100644 index 000000000..e7c131fc7 --- /dev/null +++ b/lab3/shell/header/mailbox.h @@ -0,0 +1,27 @@ +#ifndef _MAILBOX_H_ +#define _MAILBOX_H_ + +#define MMIO_BASE 0x3f000000 +#define MAILBOX_BASE MMIO_BASE + 0xb880 + +#define MAILBOX_READ (unsigned int*)(MAILBOX_BASE) +#define MAILBOX_STATUS (unsigned int*)(MAILBOX_BASE + 0x18) +#define MAILBOX_WRITE (unsigned int*)(MAILBOX_BASE + 0x20) + + +#define MAILBOX_EMPTY 0x40000000 +#define MAILBOX_FULL 0x80000000 + +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +void get_board_revision(); +void mailbox_call(unsigned int* mailbox); +void get_memory_info(); + +#endif \ No newline at end of file diff --git a/lab3/shell/header/malloc.h b/lab3/shell/header/malloc.h new file mode 100644 index 000000000..3f1fc6492 --- /dev/null +++ b/lab3/shell/header/malloc.h @@ -0,0 +1,4 @@ +#ifndef _MALLOC_H_ +#define _MALLO_H_ +void *simple_malloc(unsigned long size); +#endif \ No newline at end of file diff --git a/lab3/shell/header/reboot.h b/lab3/shell/header/reboot.h new file mode 100644 index 000000000..bf5420c1f --- /dev/null +++ b/lab3/shell/header/reboot.h @@ -0,0 +1,7 @@ +#ifndef _REBOOT_H_ +#define _REBOOT_H_ + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); +#endif \ No newline at end of file diff --git a/lab3/shell/header/shell.h b/lab3/shell/header/shell.h new file mode 100644 index 000000000..44d095662 --- /dev/null +++ b/lab3/shell/header/shell.h @@ -0,0 +1,2 @@ +void shell(); +void choose_opt(char *cmd); \ No newline at end of file diff --git a/lab3/shell/header/task.h b/lab3/shell/header/task.h new file mode 100644 index 000000000..22c440a09 --- /dev/null +++ b/lab3/shell/header/task.h @@ -0,0 +1,26 @@ +#ifndef _TASK_H_ +#define _TASK_H_ + +#include "irq.h" +#include "list.h" +#include "timer.h" +#include "uart.h" + + +typedef struct task +{ + // struct list_head listhead; + struct task *next; + struct task *prev; + int priority; // store priority (smaller number is more preemptive) + void *task_function; // task function pointer +} task; +void irqtask_list_init(); +void add_task(void *task_function, int priority); +void run_task(); + +void high_prio(); +void low_prio(); +void test_preempt(); + +#endif \ No newline at end of file diff --git a/lab3/shell/header/timer.h b/lab3/shell/header/timer.h new file mode 100644 index 000000000..a27cdaab1 --- /dev/null +++ b/lab3/shell/header/timer.h @@ -0,0 +1,31 @@ +#ifndef TIMER_H +#define TIMER_H +#define CORE0_TIMER_IRQ_CTRL 0x40000040 +#define STR(x) #x //it converts the macro argument x into a string literal after macro replacement +#define XSTR(x) STR(x) + +#include "list.h" + + +typedef struct timer { + // struct list_head listhead; + struct timer *prev; + struct timer *next; + unsigned long long interrupt_time; //store as tick time after cpu start + void* callback; // interrupt -> timer_callback -> callback(args) + char* args; // need to free the string by event callback function +} timer; + +void core_timer_interrupt_enable(); +void core_timer_interrupt_disable(); +void set_core_timer_interrupt(unsigned long long sec); +void core_timer_interrupt_disable_alternative(); + +void timer_list_init(); +void add_timer(void* callback, unsigned long long timeout, char* args); +void poptimer(); +void core_timer_handler(); + +unsigned long long get_tick_plus_s(unsigned long long second); + +#endif \ No newline at end of file diff --git a/lab3/shell/header/uart.h b/lab3/shell/header/uart.h new file mode 100644 index 000000000..177737a6a --- /dev/null +++ b/lab3/shell/header/uart.h @@ -0,0 +1,73 @@ +#ifndef _UART_H_ +#define _UART_H_ +#include "mailbox.h" + +#define GPIO_BASE (MMIO_BASE + 0x200000) + +// ref: https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf (p. 90) + +#define GPFSEL0 ((volatile unsigned int *)(GPIO_BASE + 0x00)) +#define GPFSEL1 ((volatile unsigned int *)(GPIO_BASE + 0x04)) +#define GPFSEL2 ((volatile unsigned int *)(GPIO_BASE + 0x08)) +#define GPFSEL3 ((volatile unsigned int *)(GPIO_BASE + 0x0C)) +#define GPFSEL4 ((volatile unsigned int *)(GPIO_BASE + 0x10)) +#define GPFSEL5 ((volatile unsigned int *)(GPIO_BASE + 0x14)) +// 0x18 reserved +#define GPSET0 ((volatile unsigned int *)(GPIO_BASE + 0x1C)) +#define GPSET1 ((volatile unsigned int *)(GPIO_BASE + 0x20)) +// 0x24 reserved +#define GPCLR0 ((volatile unsigned int *)(GPIO_BASE + 0x28)) +#define GPCLR1 ((volatile unsigned int *)(GPIO_BASE + 0x2C)) +// 0x30 reserved +#define GPLEV0 ((volatile unsigned int *)(GPIO_BASE + 0x34)) +#define GPLEV1 ((volatile unsigned int *)(GPIO_BASE + 0x38)) +// 0x3C reserved +#define GPEDS0 ((volatile unsigned int *)(GPIO_BASE + 0x40)) +#define GPEDS1 ((volatile unsigned int *)(GPIO_BASE + 0x44)) +// 0x48 reserved +#define GPREN0 ((volatile unsigned int *)(GPIO_BASE + 0x4C)) +#define GPREN1 ((volatile unsigned int *)(GPIO_BASE + 0x50)) +// 0x54 reserved +#define GPFEN0 ((volatile unsigned int *)(GPIO_BASE + 0x58)) +#define GPFEN1 ((volatile unsigned int *)(GPIO_BASE + 0x5C)) +// 0x60 reserved +#define GPHEN0 ((volatile unsigned int *)(GPIO_BASE + 0x64)) +#define GPHEN1 ((volatile unsigned int *)(GPIO_BASE + 0x68)) +// 0x6C reserved +#define GPLEN0 ((volatile unsigned int *)(GPIO_BASE + 0x70)) +#define GPLEN1 ((volatile unsigned int *)(GPIO_BASE + 0x74)) +// 0x78 reserved +#define GPAREN0 ((volatile unsigned int *)(GPIO_BASE + 0x7C)) +#define GPAREN1 ((volatile unsigned int *)(GPIO_BASE + 0x80)) +// 0x84 reserved +#define GPAFEN0 ((volatile unsigned int *)(GPIO_BASE + 0x88)) +#define GPAFEN1 ((volatile unsigned int *)(GPIO_BASE + 0x8C)) +// 0x90 reserved +#define GPPUD ((volatile unsigned int *)(GPIO_BASE + 0x94)) +#define GPPUDCLK0 ((volatile unsigned int *)(GPIO_BASE + 0x98)) +#define GPPUDCLK1 ((volatile unsigned int *)(GPIO_BASE + 0x9C)) +// 0xA0 reserved +// 0xB0 reserved + +void uart_init(); // initialize the device and maps it to the GPIO ports +void uart_send_char(unsigned int c); +char uart_get_char(); +void uart_send_str(char *s); +void uart_binary_to_hex(unsigned int d); + +void uart_interrupt_enable(); +void uart_interrupt_disable(); +void uart_rx_interrupt_enable(); +void uart_rx_interrupt_disable(); +void uart_tx_interrupt_enable(); +void uart_tx_interrupt_disable(); + +void uart_tx_handler(); +void uart_rx_handler(); + +void uart_async_putc(char c); +void uart_async_puts(char *s); +char uart_async_getc(); +void uart_clear_buffers(); + +#endif \ No newline at end of file diff --git a/lab3/shell/header/utils.h b/lab3/shell/header/utils.h new file mode 100644 index 000000000..fc4f1e171 --- /dev/null +++ b/lab3/shell/header/utils.h @@ -0,0 +1,10 @@ +int string_compare(char *a, char *b); +int strncmp(char *a, char *b, int cnt); +unsigned int hex_to_int(char *a, int size); +int isdigit(int c); +int toupper(int c); +int ishex(int c); +char* strtok(char* str, const char* delimiters); +char *strcpy(char *dest, const char *src); +unsigned int strlen(const char *s); +unsigned int atoi(char* str); \ No newline at end of file diff --git a/lab3/shell/irq.c b/lab3/shell/irq.c new file mode 100644 index 000000000..6482bc337 --- /dev/null +++ b/lab3/shell/irq.c @@ -0,0 +1,135 @@ +#include"header/irq.h" +#include"header/uart.h" +#include"header/shell.h" +#include "header/timer.h" +#include "header/uart.h" +#include "header/task.h" +#define CORE0_TIMER_IRQ_CTRL_ ((volatile unsigned int *)(0x40000040)) +#define AUX_MU_IIR ((volatile unsigned int *)(0x3F215048)) + +void enable_interrupt() { + asm volatile("msr DAIFClr, 0xf"); + //DAIFClr: This is the name of the register being targeted for modification. In ARM architectures, + //DAIF stands for "Disable Asynchronous Interrupts Flag". + //The Clr suffix likely indicates that the instruction is clearing specific bits in this register. + //0xf: This is the value being written to the DAIF register. In binary, 0xf is 1111, + //meaning all four exception mask bits (Debug, SError, IRQ, and FIQ) are being cleared. + //By clearing these bits, interrupts and exceptions of all types are allowed to be taken. +} + +void disable_interrupt() { + asm volatile("msr DAIFSet, 0xf"); + //set bits 0xf +} + +void except_handler_c(unsigned int x0) { + uart_send_str("In Exception handle\n"); + // Set all bits in the DAIF (Disable Asynchronous Interrupts Flags) register to 1, effectively masking all interrupts and exceptions + asm volatile("msr DAIFSet, 0xf"); + + // Declare and initialize a variable to store the value of SPSR_EL1 (Saved Program Status Register for Exception Level 1) + unsigned long long spsr_el1 = 0; + + // Read the value of SPSR_EL1 into the variable spsr_el1 + asm volatile("mrs %0, spsr_el1":"=r"(spsr_el1)); + + // Transmit the value of SPSR_EL1 over UART + uart_send_str("spsr_el1: "); + uart_binary_to_hex(spsr_el1); + uart_send_str("\r\n"); + + // Declare and initialize a variable to store the value of ELR_EL1 (Exception Link Register for Exception Level 1) + unsigned long long elr_el1 = 0; + + // Read the value of ELR_EL1 into the variable elr_el1 + asm volatile("mrs %0, elr_el1":"=r"(elr_el1)); + + // Transmit the value of ELR_EL1 over UART + uart_send_str("elr_el1: "); + uart_binary_to_hex(elr_el1); + uart_send_str("\r\n"); + + // Declare and initialize a variable to store the value of ESR_EL1 (Exception Syndrome Register for Exception Level 1) + unsigned long long esr_el1 = 0; + + // Read the value of ESR_EL1 into the variable esr_el1 + asm volatile("mrs %0, esr_el1":"=r"(esr_el1)); + + // Transmit the value of ESR_EL1 over UART + uart_binary_to_hex(esr_el1); + uart_send_str("\r\n"); + + // Extract and transmit the value of EC (Exception Class) from ESR_EL1 over UART + unsigned ec = (esr_el1 >> 26) & 0x3F; // Extract the EC field from bits 32 to 26 in ESR_EL1 + uart_send_str("ec: "); + uart_binary_to_hex(ec); + uart_send_str("\n"); + + // Clear all bits in the DAIF register, allowing interrupts and exceptions to be taken again + asm volatile("msr DAIFClr, 0xf"); + + while (1) { + + } +} + +void irq_except_handler_c() { + // part 2 + // asm volatile("msr DAIFSet, 0xf"); + // uart_send_str("In timer interruption\n"); + // unsigned long long cntpct_el0 = 0;//The register count secs with frequency + // asm volatile("mrs %0,cntpct_el0":"=r"(cntpct_el0)); + // unsigned long long cntfrq_el0 = 0;//The base frequency + // asm volatile("mrs %0,cntfrq_el0":"=r"(cntfrq_el0)); + // unsigned long long sec = cntpct_el0 / cntfrq_el0; + // uart_send_str("sec:");//except_handler_c + // uart_binary_to_hex(sec); + // uart_send_str("\n"); + // unsigned long long wait = cntfrq_el0 * 2;// wait 2 seconds + // asm volatile ("msr cntp_tval_el0, %0"::"r"(wait));//set new timer + // asm volatile("msr DAIFClr, 0xf"); + + // part3 + // from aux && from GPU0 -> uart exception + // if(*IRQ_PENDING_1 & (1<<29) &&*CORE0_INTERRUPT_SOURCE & (1 << 8)) + // { + // if (*AUX_MU_IIR & 0x4) { + // uart_rx_interrupt_disable(); + // uart_rx_handler(); + // // uart_puts("pop task1\n"); + // } + // if (*AUX_MU_IIR & 0x2) { + // uart_tx_interrupt_disable(); + // // pop_task(); + // uart_tx_handler(); + // } + // } + // else if (*CORE0_INTERRUPT_SOURCE & (1 << 1)) { + // core_timer_interrupt_disable(); + // *CORE0_TIMER_IRQ_CTRL_ = 0; + // core_timer_handler(); + // core_timer_interrupt_enable(); + // } + + //final + // see p13 + if(*IRQ_PENDING_1 & (1<<29) && *CORE0_INTERRUPT_SOURCE & (1 << 8)) + { + if (*AUX_MU_IIR & 0x4) { + uart_rx_interrupt_disable(); + add_task(uart_rx_handler, 2); + run_task(); + } + if (*AUX_MU_IIR & 0x2) { + uart_tx_interrupt_disable(); + add_task(uart_tx_handler, 1); + run_task(); + } + } + else if (*CORE0_INTERRUPT_SOURCE & (1 << 1)) { + core_timer_interrupt_disable(); + add_task(core_timer_handler, 0); + run_task(); + core_timer_interrupt_enable(); + } +} diff --git a/lab3/shell/linker.ld b/lab3/shell/linker.ld new file mode 100644 index 000000000..9bff61183 --- /dev/null +++ b/lab3/shell/linker.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + . = 0x80000; /* Set the memory address to 0x80000 (start point)*/ + + .text : { /* Define the .text section, which contains executable code*/ + KEEP(*(.text.boot)) /* Keep all .text.boot sections*/ + *(.text) /* Keep all other .text sections*/ + } + + .rodata : { *(.rodata) } /* Define the .rodata section, which contains read-only data*/ + + .data : { *(.data) } /* Define the .data section, which contains initialized data*/ + + .bss () : { /* Define the .bss section, which contains uninitialized data*/ + __bss_start = .; /* Define __bss_start symbol as the current memory address*/ + *(.bss) /* Keep all .bss sections*/ + *(COMMON) /* Keep all common symbols*/ + __bss_end = .; /* Define __bss_end symbol as the current memory address*/ + } + __heap_start = .; + _end = .; /* Define _end symbol as the current memory address*/ +} + +__bss_size = (__bss_end - __bss_start) >> 3; /* Calculate the size of the .bss section in bytes*/ diff --git a/lab3/shell/mailbox.c b/lab3/shell/mailbox.c new file mode 100644 index 000000000..2ef732242 --- /dev/null +++ b/lab3/shell/mailbox.c @@ -0,0 +1,69 @@ +#include"header/mailbox.h" +#include"header/uart.h" + +void mailbox_call(unsigned int *mailbox){ + // Write the data (shifted into the upper 28 bits) combined with + // the channel (in the lower four bits) to the write register. + unsigned int r = (((unsigned long)mailbox) & ~0xf) | 8; //mail_ch_prop + // & ~0xf => only "and" upper 28 bit can be saved + // |8 => if upper 28 is 1 => save and ensure last 4 bit is 1 + // Check if Mailbox 0 status register’s full flag is set. + while (*MAILBOX_STATUS & MAILBOX_FULL) { + asm volatile("nop"); + }; + // If not, then you can write to Mailbox 1 Read/Write register. + *MAILBOX_WRITE = r; + while (1) { + // Check if Mailbox 0 status register’s empty flag is set. + while (*MAILBOX_STATUS & MAILBOX_EMPTY) { + asm volatile("nop"); + }; + // If not, then you can read from Mailbox 0 Read/Write register. + // Check if the value is the same as you wrote in step 1. + if (r == *MAILBOX_READ) + return; + } + +} + +void get_board_revision(){ + unsigned int mailbox[7]; + mailbox[0] = 7 * 4; // buffer size in bytes + mailbox[1] = REQUEST_CODE; + // tags begin + mailbox[2] = GET_BOARD_REVISION; // tag identifier + mailbox[3] = 4; // maximum of request and response value buffer's length. + mailbox[4] = TAG_REQUEST_CODE; + mailbox[5] = 0; // value buffer + // tags end + mailbox[6] = END_TAG; + + mailbox_call(mailbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + //printf("0x%x\n", mailbox[5]); // it should be 0xa020d3 for rpi3 b+ + uart_send_str("0x"); + uart_binary_to_hex(mailbox[5]); + uart_send_str("\n"); +} + +void get_memory_info(){ + unsigned int mailbox[8]; + mailbox[0] = 8 * 4; // buffer size in bytes + mailbox[1] = REQUEST_CODE; + // tags begin + mailbox[2] = GET_ARM_MEMORY; // tag identifier + mailbox[3] = 8; // maximum of request and response value buffer's length. + mailbox[4] = TAG_REQUEST_CODE; // tag code + mailbox[5] = 0; // base address + mailbox[6] = 0; // size in bytes + mailbox[7] = END_TAG; // end tag + // tags end + mailbox_call(mailbox); + uart_send_str("ARM memory base address : "); + uart_binary_to_hex(mailbox[5]); + uart_send_str("\n"); + + uart_send_str("ARM memory size : "); + uart_binary_to_hex(mailbox[6]); + uart_send_str("\n"); +} diff --git a/lab3/shell/main.c b/lab3/shell/main.c new file mode 100644 index 000000000..0189224db --- /dev/null +++ b/lab3/shell/main.c @@ -0,0 +1,48 @@ +#include "header/utils.h" +#include "header/uart.h" +#include "header/shell.h" +#include "header/cpio.h" +#include "header/dtb.h" +#include "header/uart.h" +#include "header/timer.h" +#include "header/task.h" +extern char *dtb_base; +int main(char *arg){ + register unsigned long long x21 asm("x21"); + // pass by x21 reg + dtb_base = (char*)x21; + + // // print addresses + // unsigned long el = 0; + // asm volatile ("mrs %0, CurrentEL":"=r"(el)); + // uart_send_str("Current exception level: "); + // uart_binary_to_hex(el>>2); // CurrentEL store el level at [3:2] + // uart_send_str("\n"); + // asm volatile("mov %0, sp"::"r"(el)); + // uart_send_str("Current stack pointer address: "); + // uart_binary_to_hex(el); + // uart_send_str("\n"); + + // fdt init + fdt_traverse(initramfs_callback); + uart_init(); + irqtask_list_init(); + timer_list_init(); + + uart_send_str("\x1b[2J\x1b[H"); + + // init interrupt + // enable_timer(); + uart_interrupt_enable(); + asm volatile("msr DAIFClr, 0xf"); + core_timer_interrupt_enable(); + core_timer_interrupt_disable_alternative(); + + + + char *s = "Type in `help` to get instruction menu!\r\n"; + uart_send_str(s); + + shell(); + return 0; +} \ No newline at end of file diff --git a/lab3/shell/makefile b/lab3/shell/makefile new file mode 100644 index 000000000..e3b1a1901 --- /dev/null +++ b/lab3/shell/makefile @@ -0,0 +1,32 @@ +CFLAGS = -Wall -ffreestanding -nostdinc -nostdlib -nostartfiles -fno-stack-protector -g +SRCS = $(wildcard *.c) +OBJS = $(SRCS:.c=.o) + +ASMS = $(wildcard *.S) +ASM_OBJS = $(ASMS:.S=.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +all:: clean_img flash clean + +%.o: %.S + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + + +%.o: %.c + aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ + +flash: $(ASM_OBJS) $(OBJS) + aarch64-linux-gnu-ld $(ASM_OBJS) $(OBJS) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy -O binary kernel8.elf shell.img +clean: + rm -f $(ASM_OBJS) $(OBJS) $(DEP_FILES) + +clean_img: + rm -f kernel8.elf + rm -f shell.img +test: + qemu-system-aarch64 -machine raspi3b -kernel shell.img -display none -serial null -serial stdio -initrd ../rootfs/initramfs.cpio -dtb ../bcm2710-rpi-3-b-plus.dtb +screen: + sudo screen /dev/ttyUSB0 115200 \ No newline at end of file diff --git a/lab3/shell/malloc.c b/lab3/shell/malloc.c new file mode 100644 index 000000000..2f1e2586d --- /dev/null +++ b/lab3/shell/malloc.c @@ -0,0 +1,11 @@ +#include"header/malloc.h" +extern char __heap_start; +char *top = &__heap_start; +void* simple_malloc(unsigned long size) { + char* r = top + 0x10; + // size paddling to multiple of 0x10 + size = 0x10 + size - size % 0x10; + *(unsigned long*)(r - 0x8) = size; + top += size; + return r; +} \ No newline at end of file diff --git a/lab3/shell/reboot.c b/lab3/shell/reboot.c new file mode 100644 index 000000000..089d71dcb --- /dev/null +++ b/lab3/shell/reboot.c @@ -0,0 +1,20 @@ +#include"header/reboot.h" + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/lab3/shell/shell.c b/lab3/shell/shell.c new file mode 100644 index 000000000..f330bdac2 --- /dev/null +++ b/lab3/shell/shell.c @@ -0,0 +1,149 @@ +#include"header/shell.h" +#include"header/uart.h" +#include"header/utils.h" +#include"header/reboot.h" +#include"header/mailbox.h" +#include"header/cpio.h" +#include"header/malloc.h" +#include"header/timer.h" +#include"header/task.h" +void shell(){ + while (1) + { + char cmd[20]; + for(int i = 0;i < 20; i++){ + cmd[20] = '\0'; + } + char *cur; + char *s = "# "; + uart_send_str(s); + cur = cmd; + char receive; + while (1) + { + receive = uart_get_char(); + if(receive == '\n'){ + *cur = '\0'; + break; + } + else if(receive == 127){ + if(cur == cmd){ + *cur = '\0'; + continue; + } + *cur = '\0'; + cur--; + uart_send_str("\b \b"); + continue; + } + else{ + *cur = receive; + uart_send_char(receive); + cur++; + } + } + choose_opt(cmd); + } +} +void choose_opt(char *cmd){ + char arg[20][20]; + for(int i = 0;i < 20;i++){ + for(int j = 0;j < 20;j++){ + arg[i][j] = '\0'; + } + } + char *tk = strtok(cmd," "); + int i = 0; + for(i = 0; tk != 0;i++){ + strcpy(arg[i],tk); + tk = strtok(0," "); + } + if(string_compare(arg[0],"help")){ + uart_send_str("\nhelp\t\t:print this help menu\r\n"); + uart_send_str("hello\t\t:print Hello World!\r\n"); + uart_send_str("info\t\t:Get the hardware's information\r\n"); + uart_send_str("ls\t\t:list files in directory\r\n"); + uart_send_str("cat\t\t:cat [dir]\r\n"); + uart_send_str("clear\t\t:clear terminal\r\n"); + uart_send_str("reboot\t\t:reboot the device\r\n"); + uart_send_str("malloc\t\t:alloc string\r\n"); + uart_send_str("async\t\t:asynchronous uart I/O\r\n"); + uart_send_str("timeout\t\t:timeout [message] [seconds]\r\n"); + uart_send_str("preemtion\t\t: preemtion testing\r\n"); + } + else if(string_compare(arg[0],"hello")){ + uart_send_str("\r\nHello World!\r\n"); + } + else if(string_compare(arg[0],"info")){ + uart_send_str("\r\nInfo:\r\n"); + uart_send_str("Board Vision: "); + get_board_revision(); + get_memory_info(); + + } + else if(string_compare(arg[0],"clear")){ + uart_send_str("\x1b[2J\x1b[H"); + } + else if(string_compare(arg[0],"ls")){ + uart_send_str("\r\n"); + ls("."); + } + else if(string_compare(arg[0],"cat")){ + uart_send_str("\r\n"); + cat(arg[1]); + } + else if (string_compare(arg[0],"reboot")) { + uart_send_str("\r\nRebooting....\r\n"); + reset(1000); + } + else if (string_compare(arg[0],"malloc")){ + uart_send_str("\r\n"); + unsigned int size = (strlen(arg[1]) + 31) >> 5 << 5; + char *string = simple_malloc(size); + strcpy(string,arg[1]); + uart_send_str(string); + uart_send_str("\r\n"); + } + else if (string_compare(arg[0],"exec")){ + uart_send_str("\r\nexecute file: "); + uart_send_str(arg[1]); + uart_send_str("...\r\n"); + execfile(arg[1]); + } + else if (string_compare(arg[0], "async")){ + uart_send_str("\r\nasync begin....\r\n"); + char c = 0; + uart_clear_buffers(); + while (1) { + c = uart_async_getc(); + if (c == 13 || c == 10) { + break; + } + + uart_async_putc(c); + } + uart_send_str("\r\n"); + } + else if(string_compare(arg[0], "timeout")){ + if(i != 3){ + uart_send_str("\r\nusage\t:timeout [message] [seconds]\r\n"); + } + else + { + core_timer_interrupt_enable(); + unsigned long long cntpct = 0, cntfrq = 0; + asm volatile("mrs %0, cntpct_el0\n\t" : "=r"(cntpct)); + asm volatile("mrs %0, cntfrq_el0\n\t" : "=r"(cntfrq)); + // add_timer(uart_send_str, atoi(arg[2])*cntfrq + cntpct, arg[1]); + add_timer(uart_send_str, atoi(arg[2]), arg[1]); + } + uart_send_str("\r\n"); + } + else if (string_compare(arg[0], "preempt")){ + uart_async_puts("\r\npreemption testing....\r\n"); + test_preempt(); + uart_async_puts("\r\n"); + } + else + uart_send_str("\r\n"); +} diff --git a/lab3/shell/shell_init.S b/lab3/shell/shell_init.S new file mode 100644 index 000000000..927033e40 --- /dev/null +++ b/lab3/shell/shell_init.S @@ -0,0 +1,158 @@ +.section ".text" + +.global _start + +_start: + mrs x1, mpidr_el1 // read the MPIDR_EL1 register; mrs = move from system register + and x1, x1, #0x3 + cbz x1, cpu0 // cbz = compare and branch if zero; if x0 is zero, branch to cpu0 + +halt: + wfe // wait for event + b halt + +cpu0: + mov x21, x0 + bl from_el2_to_el1 + bl set_exception_vector_table + + ldr x1, =_start // load the address of _start into x0 + mov sp, x1 // set the stack pointer to the address of _start + ldr x1, =__bss_start // load the address of __bss_start into x0 + ldr w2, =__bss_size + +clear_bss: + cbz w2, kernel_main + str xzr, [x1], #8 // store zero to the address in x0, then increment x0 by 8 + sub w2, w2, #1 + cbnz w2, clear_bss + +kernel_main: + bl main // jump to C code + b halt + +from_el2_to_el1: + mov x0, (1 << 31) + msr hcr_el2, x0 + mov x0, 0x3c5 + msr spsr_el2, x0 + msr elr_el2, lr + eret +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp ,16 * 0] + stp x2, x3, [sp ,16 * 1] + stp x4, x5, [sp ,16 * 2] + stp x6, x7, [sp ,16 * 3] + stp x8, x9, [sp ,16 * 4] + stp x10, x11, [sp ,16 * 5] + stp x12, x13, [sp ,16 * 6] + stp x14, x15, [sp ,16 * 7] + stp x16, x17, [sp ,16 * 8] + stp x18, x19, [sp ,16 * 9] + stp x20, x21, [sp ,16 * 10] + stp x22, x23, [sp ,16 * 11] + stp x24, x25, [sp ,16 * 12] + stp x26, x27, [sp ,16 * 13] + stp x28, x29, [sp ,16 * 14] + str x30, [sp, 16 * 15] + // for nested interrupt + mrs x0, spsr_el1 + str x0, [sp, 16 * 16] + mrs x0, elr_el1 + str x0, [sp, 16 * 17] + ldp x0, x1, [sp, 16 * 0] // restore x0, x1 +.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] + // for nested interrupt + ldr x0, [sp, 16 * 16] + msr spsr_el1, x0 + ldr x0, [sp, 16 * 17] + msr elr_el1, x0 + ldp x0, x1, [sp, 16 * 0] // restore x0, x1 + + add sp, sp, 32 * 9 // nested loop got one more item to save + + +.endm + +exception_handler: + save_all + bl except_handler_c + load_all + eret + +invalid_handler: + save_all + //bl except_handler_c + load_all + eret + +irq_exception_handler: + save_all + bl irq_except_handler_c + load_all + eret + +.align 11 // vector table should be aligned to 0x800, to a 2^11 = 2048-byte boundary. +.global exception_vector_table +exception_vector_table: + b invalid_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0, 2^7 = 128-byte boundary. + b invalid_handler + .align 7 + b invalid_handler + .align 7 + b invalid_handler + .align 7 + + b invalid_handler + .align 7 + b irq_exception_handler + .align 7 + b invalid_handler + .align 7 + b invalid_handler + .align 7 + + b exception_handler + .align 7 + b irq_exception_handler + .align 7 + b invalid_handler + .align 7 + b invalid_handler + .align 7 + + b invalid_handler + .align 7 + b invalid_handler + .align 7 + b invalid_handler + .align 7 + b invalid_handler + .align 7 + +set_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + ret \ No newline at end of file diff --git a/lab3/shell/task.c b/lab3/shell/task.c new file mode 100644 index 000000000..9a0120da5 --- /dev/null +++ b/lab3/shell/task.c @@ -0,0 +1,87 @@ +#include "header/task.h" +#include "header/list.h" +#include "header/malloc.h" +#include "header/uart.h" + +int curr_task_priority = 100; +task *task_list = 0; + +void irqtask_list_init(){ + task_list = 0; +} +void add_task(void *task_function, int priority){ + asm volatile("msr DAIFSet, 0xf"); + // make new node + task *cur = simple_malloc(sizeof(task)); + cur -> task_function = task_function; + cur -> priority = priority; + + // enqueue + asm volatile("msr DAIFSet, 0xf"); + // insert if task list is empty + if(task_list == 0 || cur->priority < task_list -> priority){ + cur -> next = task_list; + cur -> prev = 0; + if(task_list == 0){ + task_list -> prev = cur; + } + task_list = cur; + } + // find place to insert + else{ + task *node = task_list; + while (node -> next != 0 && node -> next -> priority <= cur ->priority) { + node = node -> next; + } + cur -> prev = node; + cur -> next = node -> next; + if(node -> next != 0){ + node -> next -> prev = cur; + } + node -> next = cur; + } + asm volatile("msr DAIFClr, 0xf"); + asm volatile("msr DAIFClr, 0xf"); +} + +void run_task(){ + asm volatile("msr DAIFClr, 0xf"); + while (task_list) { + asm volatile("msr DAIFSet, 0xf"); + task *tar = task_list; + if(curr_task_priority <= tar->priority){ + asm volatile("msr DAIFClr, 0xf"); + break; + } + // moving + task_list = task_list->next; + // removing prev task as it will be exec + if(task_list) + task_list -> prev = 0; + int prev_prio = curr_task_priority; + curr_task_priority = tar->priority; + // exec task + asm volatile("msr DAIFClr, 0xf"); + ((void (*)())tar->task_function)(); + asm volatile("msr DAIFSet, 0xf"); + + curr_task_priority = prev_prio; + asm volatile("msr DAIFClr, 0xf"); + } + +} + +void high_prio(){ + uart_async_puts("\r\nhigh priority testing\r\n"); + for(int i = 0;i<10000;i++); + uart_async_puts("high priority end\r\n"); +} +void low_prio(){ + uart_async_puts("\r\nlow priority testing\r\n"); + for(int i = 0;i<10000;i++); + uart_async_puts("low priority end\r\n"); +} +void test_preempt(){ + add_task(low_prio, 9); + add_task(high_prio, 0); +} diff --git a/lab3/shell/timer.c b/lab3/shell/timer.c new file mode 100644 index 000000000..d7be97771 --- /dev/null +++ b/lab3/shell/timer.c @@ -0,0 +1,114 @@ +#include"header/timer.h" +#include "header/malloc.h" +#include "header/utils.h" + +// struct list_head* timer_event_list; // first head has nothing, store timer_event_t after it +timer *timer_list = 0; + +void core_timer_interrupt_enable(){ + asm volatile("mov x1, 1\n\t");//The purpose of this instruction depends on the context of the program. Typically, it's used to prepare data or a control signal for subsequent operations. + asm volatile("msr cntp_ctl_el0, x1\n\t");//This register often controls various aspects of the timer in ARM architectures, such as enabling or disabling it. + asm volatile("mov x2, 2\n\t");// prepares a value for subsequent operations. + asm volatile("ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t");//memory-mapped control register related to the timer's interrupt control. + asm volatile("str w2, [x1]\n\t");//The value in register w2 (lower 32 bits of x2) is stored into the memory location pointed to by x1. This effectively writes the value 2 to the memory-mapped control register represented by CORE0_TIMER_IRQ_CTRL. +} +void core_timer_interrupt_disable(){ + asm volatile("mov x2, 0\n\t");//The purpose of this operation depends on the context of the program. Register x2 may be used as a general-purpose register to hold data or addresses. + asm volatile("ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t");// ads the address of a symbol named CORE0_TIMER_IRQ_CTRL into register x1. The symbol likely represents the memory-mapped control register related to the timer's interrupt control. The = operator with ldr instruction is typically used to load an address directly into a register. + asm volatile("str w2, [x1]\n\t");//tored into the memory location pointed to by x1. This effectively writes the value 0 to the memory-mapped control register represented by CORE0_TIMER_IRQ_CTRL +} +void core_timer_interrupt_disable_alternative() { + unsigned long long sec = 0xFFFFFFFFFFFFFFFF; + // unsigned long long sec = 10000; + asm volatile("msr cntp_cval_el0, %0" ::"r"(sec)); +} + +unsigned long long get_tick_plus_s(unsigned long long second){ + unsigned long long cntpct_el0=0; + __asm__ __volatile__("mrs %0, cntpct_el0\n\t": "=r"(cntpct_el0)); // tick auchor + unsigned long long cntfrq_el0=0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t": "=r"(cntfrq_el0)); // tick frequency + return (cntpct_el0 + cntfrq_el0*second); +} +void set_core_timer_interrupt(unsigned long long sec){ + + //part 2 + // core_timer_interrupt_disable_alternative(); + + //part 4 + __asm__ __volatile__( + "mrs x1, cntfrq_el0\n\t" // cntfrq_el0 -> frequency of the timer + "mul x1, x1, %0\n\t" // cntpct_el0 = cntfrq_el0 * seconds: relative timer to cntfrq_el0 + "msr cntp_tval_el0, x1\n\t" // Set expired time to cntp_tval_el0, which stores time value of EL1 physical timer. + :"=r" (sec)); +} + +void timer_list_init(){ + // INIT_LIST_HEAD(timer_event_list); + timer_list = 0; +} + +void add_timer(void* callback, unsigned long long timeout, char* args){ + // create timer node + timer* event = simple_malloc(sizeof(timer)); + // storing + event->args = simple_malloc(strlen(args)+1); + strcpy(event -> args,args); + event->interrupt_time = timeout; + event->callback = callback; + // add the event into timer_list (sorted) + timer* cur = timer_list; + asm volatile("msr DAIFSet, 0xf"); + // insert while list empty or the smallest + if(timer_list == 0 || timer_list->interrupt_time > event->interrupt_time){ + event -> next = timer_list; + event -> prev = 0; + if(timer_list != 0){ + timer_list -> prev = event; + } + timer_list = event; + set_core_timer_interrupt(timer_list->interrupt_time); + } + else { + while (cur->next != 0 && cur->next->interrupt_time < event->interrupt_time) { + cur = cur->next; + } + event->next = cur -> next; + event->prev = cur; + if(cur->next != 0){ + cur->next->prev = event; + } + cur->next = event; + } + asm volatile("msr DAIFClr, 0xf"); +} + +void poptimer(){ + asm volatile("msr DAIFSet, 0xf"); + while (timer_list) { + timer *cur = timer_list; + ((void (*)())cur->callback)(cur->args); + timer_list = timer_list->next; + cur = timer_list; + timer_list -> prev = 0; + if(timer_list == 0) + { + core_timer_interrupt_disable_alternative();// disable timer interrupt (set a very big value) + } + else + { + set_core_timer_interrupt(cur->interrupt_time); + } + } + asm volatile("msr DAIFClr, 0xf"); +} + +void core_timer_handler(){ + if (timer_list == 0) + { + core_timer_interrupt_disable_alternative(); // disable timer interrupt (set a very big value) + return; + } + poptimer(); // do callback and set new interrupt + +} \ No newline at end of file diff --git a/lab3/shell/uart.c b/lab3/shell/uart.c new file mode 100644 index 000000000..eb87c3451 --- /dev/null +++ b/lab3/shell/uart.c @@ -0,0 +1,207 @@ +#include "header/uart.h" + +/* + * Auxilary mini UART registers + * ref: https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf (p. 8) + */ +#define AUX_BASE (MMIO_BASE + 0x215000) +#define AUX_ENABLE ((volatile unsigned int *)(AUX_BASE + 0x04)) +#define AUX_MU_IO ((volatile unsigned int *)(AUX_BASE + 0x40)) // for io data +#define AUX_MU_IER ((volatile unsigned int *)(AUX_BASE + 0x44)) +#define AUX_MU_IIR ((volatile unsigned int *)(AUX_BASE + 0x48)) // for interrupt identify +#define AUX_MU_LCR ((volatile unsigned int *)(AUX_BASE + 0x4C)) +#define AUX_MU_MCR ((volatile unsigned int *)(AUX_BASE + 0x50)) +#define AUX_MU_LSR ((volatile unsigned int *)(AUX_BASE + 0x54)) +#define AUX_MU_MSR ((volatile unsigned int *)(AUX_BASE + 0x58)) +#define AUX_MU_SCRATCH ((volatile unsigned int *)(AUX_BASE + 0x5C)) +#define AUX_MU_CNTL ((volatile unsigned int *)(AUX_BASE + 0x60)) +#define AUX_MU_STAT ((volatile unsigned int *)(AUX_BASE + 0x64)) +#define AUX_MU_BAUD ((volatile unsigned int *)(AUX_BASE + 0x68)) + +//p112 +#define ENABLE_IRQS_1 ((volatile unsigned int *)(MMIO_BASE + 0xB210)) +#define DISABLE_IRQS_1 ((volatile unsigned int *)(MMIO_BASE + 0xB21C)) +#define BUFFER_SIZE 1024 +char uart_tx_buffer[BUFFER_SIZE]; +char uart_rx_buffer[BUFFER_SIZE]; +unsigned int uart_tx_buffer_head = 0; +unsigned int uart_tx_buffer_tail = 1; +unsigned int uart_rx_buffer_head = 0; +unsigned int uart_rx_buffer_tail = 1; + +void uart_init() +{ + register unsigned int r; + + /* initialize UART */ + *AUX_ENABLE |= 1; // enable UART1, AUX mini UART + *AUX_MU_CNTL = 0; + *AUX_MU_LCR = 3; + *AUX_MU_MCR = 0; + *AUX_MU_IER = 0; + *AUX_MU_IIR = 0xC6; // disable interrupts + *AUX_MU_BAUD = 270; // 115200 baud + + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx +} + +void uart_send_char(unsigned int c) +{ + /* wait until we can send */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); + + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +char uart_get_char() +{ + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); + /* read it and return */ + r = (char)(*AUX_MU_IO); + /* convert carrige return to newline */ + return r == '\r' ? '\n' : r; +} + +void uart_send_str(char *s) +{ + while (*s) { + /* convert newline to carrige return + newline */ + if (*s == '\n') { + uart_send_char('\r'); + } + uart_send_char(*s++); + } +} + +void uart_binary_to_hex(unsigned int d) +{ + uart_send_str("0x"); + unsigned int n; + for (int c = 28; c >= 0; c -= 4) { + n = (d >> c) & 0xF; + n += n > 9 ? 0x57 : 0x30; + uart_send_char(n); + } + uart_send_str("\n"); +} + + +void uart_interrupt_enable() +{ + // ref: https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf (p. 12) + *AUX_MU_IER |= 0x1; // enable rx interrupt + *AUX_MU_IER |= 0x2; // enable tx interrupt + *ENABLE_IRQS_1 |= 1 << 29; // enable mini uart interrupt +} + +void uart_interrupt_disable() +{ + *AUX_MU_IER &= ~0x1; // disable rx interrupt + *AUX_MU_IER &= ~0x2; // disable tx interrupt + *ENABLE_IRQS_1 &= ~(1 << 29); // disable mini uart interrupt +} + +void uart_rx_interrupt_enable() { *AUX_MU_IER |= 0x1; } + +void uart_rx_interrupt_disable() { *AUX_MU_IER &= ~0x1; } + +void uart_tx_interrupt_enable() { *AUX_MU_IER |= 0x2; } + +void uart_tx_interrupt_disable() { *AUX_MU_IER &= ~0x2; } + +void uart_rx_handler() +{ + if ((uart_rx_buffer_head + 1) % BUFFER_SIZE == uart_rx_buffer_tail) { // checks if the next position in the receive buffer (uart_rx_buffer_head + 1) would overlap with the current tail of the buffer (uart_rx_buffer_tail). + uart_rx_interrupt_disable(); // disable read interrupt + return; + } + + uart_rx_buffer[uart_rx_buffer_head] = (char)(*AUX_MU_IO); // receive data, reads a character from the UART receive register (*AUX_MU_IO) and stores it in the receive buffer at the current head position (uart_rx_buffer[uart_rx_buffer_head]). + uart_rx_buffer_head = (uart_rx_buffer_head + 1) % BUFFER_SIZE; //Update Buffer Head Pointer: (uart_rx_buffer_head + 1) % BUFFER_SIZE ensures that the head pointer wraps around to the beginning of the buffer if it reaches the end (BUFFER_SIZE) to implement a circular buffer. +} + +void uart_tx_handler() +{ + if (uart_tx_buffer_head == uart_tx_buffer_tail) { // check buffer is empty or not, if empty nothing to write + uart_tx_interrupt_disable(); // disable write interrupt + return; + } + + asm volatile("msr DAIFSet, 0xf"); + uart_send_char(uart_tx_buffer[uart_tx_buffer_tail]); // send last character of writing buffer + uart_tx_buffer_tail = (uart_tx_buffer_tail + 1) % BUFFER_SIZE; // ensures that the tail pointer wraps around to the beginning of the buffer if it reaches the end (BUFFER_SIZE) to implement a circular buffer. + asm volatile("msr DAIFClr, 0xf"); + uart_tx_interrupt_enable(); +} + +void uart_async_puts(char *s) +{ + while ((uart_tx_buffer_head + 1) % BUFFER_SIZE == uart_tx_buffer_tail) + uart_tx_interrupt_enable(); + + asm volatile("msr DAIFSet, 0xf"); + while (*s) { + uart_tx_buffer[uart_tx_buffer_head++] = *s++; + uart_tx_buffer_head %= BUFFER_SIZE; + } + asm volatile("msr DAIFClr, 0xf"); + + uart_tx_interrupt_enable(); +} + +void uart_async_putc(char c) +{ + while ((uart_tx_buffer_head + 1) % BUFFER_SIZE == uart_tx_buffer_tail) // check list is full or not by comparing head+1 & tail + uart_tx_interrupt_enable(); // if not enable writing interrupt to enable writing handler start writing interrupt + // below is + asm volatile("msr DAIFSet, 0xf"); + uart_tx_buffer[uart_tx_buffer_head++] = c; // store c at current head position and increase one for next position + uart_tx_buffer_head %= BUFFER_SIZE; // avoid index out of range + asm volatile("msr DAIFClr, 0xf"); + + uart_tx_interrupt_enable(); +} + +char uart_async_getc() +{ + while ((uart_rx_buffer_tail == uart_rx_buffer_head)) // check if reading buffer is empty + uart_rx_interrupt_enable(); // if empty enable reading handler to start reading + + asm volatile("msr DAIFSet, 0xf"); + char c = uart_rx_buffer[uart_rx_buffer_tail]; // get a byte from rx buffer + uart_rx_buffer_tail = (uart_rx_buffer_tail + 1) % BUFFER_SIZE; // avoid index out of range + asm volatile("msr DAIFClr, 0xf"); + + return c; +} + +void uart_clear_buffers() +{ + for (int i = 0; i < BUFFER_SIZE; i++) { + uart_rx_buffer[i] = '\0'; + uart_tx_buffer[i] = '\0'; + } +} \ No newline at end of file diff --git a/lab3/shell/utils.c b/lab3/shell/utils.c new file mode 100644 index 000000000..534e2218c --- /dev/null +++ b/lab3/shell/utils.c @@ -0,0 +1,116 @@ +#include"header/utils.h" +#include"header/uart.h" + +int string_compare(char *s1,char *s2) { + while (*s1 && *s2 && (*s1 == *s2)) { + s1++; + s2++; + } + return !(*s1 - *s2); +} + +int strncmp(char *s1, char *s2, int n) { + while (n-- && *s1 && (*s1 == *s2)) { + s1++; + s2++; + } + if (n == (int) -1) { + return 0; + } + return *(const unsigned char *) s1 - *(const unsigned char *) s2; +} +int isdigit(int c){ + return c >= '0' && c <= '9'; +} +int toupper(int c){ + if (c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } else { + return c; + } +} + +int ishex(int c){ + return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F'); +} + +unsigned int hex_to_int(char *a, int size){ + unsigned int result = 0; + + for (int i = 0; i < size; i++) { + char c = a[i]; + if (ishex(c)) { + int val = isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; + result = (result << 4) + val; + } + } + + return result; +} + +char* strtok(char* str, const char* delimiters) { + static char* buffer = 0; + if (str != 0) { + buffer = str; + } + if (buffer == 0) { + return 0; + } + char* start = buffer; + while (*buffer != '\0') { + const char* delim = delimiters; + while (*delim != '\0') { + if (*buffer == *delim) { + *buffer = '\0'; + buffer++; + if (start != buffer) { + return start; + } else { + start++; + break; + } + } + delim++; + } + if (*delim == '\0') { + buffer++; + } + } + if (start == buffer) { + return 0; + } else { + return start; + } +} +char *strcpy(char *dest, const char *src) { + char *ret = dest; + while ((*dest++ = *src++)); + return ret; +} + +unsigned int strlen(const char *s) { + const char *sc; + for (sc = s; *sc != '\0'; ++sc) + ; + return sc - s; +} + +unsigned int atoi(char* str) +{ + // Initialize result + unsigned int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + { + if(str[i] > '9' || str[i] < '0')return res; + res = res * 10 + str[i] - '0'; + } + // return result. + return res; +} \ No newline at end of file diff --git a/lab3/user program/linker.ld b/lab3/user program/linker.ld new file mode 100644 index 000000000..47697d773 --- /dev/null +++ b/lab3/user program/linker.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + . = 0x80000; /* Set the memory address to 0x80000 (start point)*/ + + .text : { /* Define the .text section, which contains executable code*/ + KEEP(*(.text.boot)) /* Keep all .text.boot sections*/ + *(.text) /* Keep all other .text sections*/ + } + + .rodata : { *(.rodata) } /* Define the .rodata section, which contains read-only data*/ + + .data : { *(.data) } /* Define the .data section, which contains initialized data*/ + + _end = .; /* Define _end symbol as the current memory address*/ +} + diff --git a/lab3/user program/makefile b/lab3/user program/makefile new file mode 100644 index 000000000..2354f0fb7 --- /dev/null +++ b/lab3/user program/makefile @@ -0,0 +1,13 @@ +all: one.img + +one.o: testing.S + aarch64-linux-gnu-gcc -c testing.S -o one.o + +one.img: one.o + aarch64-linux-gnu-ld one.o -T linker.ld -o one.elf + aarch64-linux-gnu-objcopy -O binary one.elf one.img + +clean: + rm one.o one.elf > /dev/null 2>/dev/null || true +cimg: + rm one.img \ No newline at end of file diff --git a/lab3/user program/testing b/lab3/user program/testing new file mode 100644 index 000000000..ffa21c03a --- /dev/null +++ b/lab3/user program/testing @@ -0,0 +1,7 @@ +.section ".text" +.global _start +_start: + svc 0x1337 +1: + nop + b 1b \ No newline at end of file diff --git a/lab3/user program/testing.S b/lab3/user program/testing.S new file mode 100644 index 000000000..66db5a6e8 --- /dev/null +++ b/lab3/user program/testing.S @@ -0,0 +1,14 @@ +.section ".text" // Directs the assembler to place the following code in the ".text" section +.global _start // Makes _start globally accessible, typically the entry point in bare-metal programming +_start: // Label marking the start of the executable code + + mov x0, 0 // Initializes register x0 to 0; x0 will be used as a loop counter + +1: // Label "1" marks the beginning of the first loop + add x0, x0, 1 // Increments the value in x0 by 1 + svc 0 // Performs a supervisor call with immediate value 0 (used here for demonstration) + cmp x0, 5 // Compares the value in x0 to 5 + blt 1b // Branches back to label "1" if x0 is less than 5, continuing the loop + +1: // Another label "1", re-used for clarity, marking the start of an infinite loop + b 1b // Unconditionally branches to itself, creating an infinite loop diff --git a/lab3/writer.py b/lab3/writer.py new file mode 100644 index 000000000..738cc1871 --- /dev/null +++ b/lab3/writer.py @@ -0,0 +1,25 @@ +import serial +import os +import time + + +tty = serial.Serial("/dev/ttyUSB0", 115200, timeout=0.5) +# acquire the file size +file_stats = os.stat("./shell/shell.img") +# issue request and tell the size of img to rec +tty.write(str(file_stats.st_size).encode('utf-8')) +# size sended +# python3 .encode() +tty.write(str("\n").encode('utf-8')) +time.sleep(0.0005) +# send img byte-by-byte +# delay to ensure no loss +# uart is low speed interface +# if sleep too short e.g: 0.0001, it may loss +with open("./shell/shell.img", "rb") as fp: + byte = fp.read(1) + while byte: + tty.write(byte) + byte = fp.read(1) + # delay enough time to ensure no loss + time.sleep(0.0005) \ No newline at end of file