diff --git a/README.md b/README.md index ae819384f..0e064a321 100644 --- a/README.md +++ b/README.md @@ -95,3 +95,11 @@ Goals of this lab: * Understand how to mount a file system and look up a file across file systems. * Understand how special file works. +### Lab 8: Virtual File System +In previous lab, files were stored in memory, which is volatile. In this lab, you’ll implement FAT32 that stores data in SD card. + +Goals of this lab: + +* Understand how to interact with SD card. +* Implement FAT32 file system. +* Understand memory cache for slow storage mediums. diff --git a/lab3/test/a b/lab3/test/a new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/lab3/test/a @@ -0,0 +1 @@ + diff --git a/lab8/create_cpio.sh b/lab8/create_cpio.sh new file mode 100644 index 000000000..f1270ba2c --- /dev/null +++ b/lab8/create_cpio.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Go to rootfs folder +# Select all files and put into archives +# '-o, --create' Run in copy-out mode +# '-H FORMAT' archive format, +# newc: SVR4 portable format + +cd rootfs +find . | cpio -o -H newc > ../initramfs.cpio +cd .. diff --git a/lab8/initramfs.cpio b/lab8/initramfs.cpio new file mode 100644 index 000000000..08fa91452 Binary files /dev/null and b/lab8/initramfs.cpio differ diff --git a/lab8/kernel/bcm2710-rpi-3-b-plus.dtb b/lab8/kernel/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..38395a23f Binary files /dev/null and b/lab8/kernel/bcm2710-rpi-3-b-plus.dtb differ diff --git a/lab8/kernel/build/boot_s.d b/lab8/kernel/build/boot_s.d new file mode 100644 index 000000000..6de4abbe6 --- /dev/null +++ b/lab8/kernel/build/boot_s.d @@ -0,0 +1 @@ +build/boot_s.o: src/boot.S include/mmu.h include/stddef.h diff --git a/lab8/kernel/build/boot_s.o b/lab8/kernel/build/boot_s.o new file mode 100644 index 000000000..3adf2a507 Binary files /dev/null and b/lab8/kernel/build/boot_s.o differ diff --git a/lab8/kernel/build/cpio_c.d b/lab8/kernel/build/cpio_c.d new file mode 100644 index 000000000..5f1643f91 --- /dev/null +++ b/lab8/kernel/build/cpio_c.d @@ -0,0 +1,2 @@ +build/cpio_c.o: src/cpio.c include/cpio.h include/dtb.h include/string.h \ + include/stddef.h diff --git a/lab8/kernel/build/cpio_c.o b/lab8/kernel/build/cpio_c.o new file mode 100644 index 000000000..bd874b7fc Binary files /dev/null and b/lab8/kernel/build/cpio_c.o differ diff --git a/lab8/kernel/build/dev_framebuffer_c.d b/lab8/kernel/build/dev_framebuffer_c.d new file mode 100644 index 000000000..de2ccd3d7 --- /dev/null +++ b/lab8/kernel/build/dev_framebuffer_c.d @@ -0,0 +1,6 @@ +build/dev_framebuffer_c.o: src/dev_framebuffer.c include/vfs.h \ + include/stddef.h include/dev_framebuffer.h include/vfs.h include/uart1.h \ + include/memory.h include/bcm2837/rpi_mmu.h include/list.h \ + include/stddef.h include/mbox.h include/string.h include/exception.h \ + include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h diff --git a/lab8/kernel/build/dev_framebuffer_c.o b/lab8/kernel/build/dev_framebuffer_c.o new file mode 100644 index 000000000..4b2563319 Binary files /dev/null and b/lab8/kernel/build/dev_framebuffer_c.o differ diff --git a/lab8/kernel/build/dev_uart_c.d b/lab8/kernel/build/dev_uart_c.d new file mode 100644 index 000000000..7a8609dcd --- /dev/null +++ b/lab8/kernel/build/dev_uart_c.d @@ -0,0 +1,3 @@ +build/dev_uart_c.o: src/dev_uart.c include/vfs.h include/stddef.h \ + include/dev_uart.h include/vfs.h include/uart1.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/list.h include/stddef.h diff --git a/lab8/kernel/build/dev_uart_c.o b/lab8/kernel/build/dev_uart_c.o new file mode 100644 index 000000000..1c93144d7 Binary files /dev/null and b/lab8/kernel/build/dev_uart_c.o differ diff --git a/lab8/kernel/build/dtb_c.d b/lab8/kernel/build/dtb_c.d new file mode 100644 index 000000000..9d76b74e9 --- /dev/null +++ b/lab8/kernel/build/dtb_c.d @@ -0,0 +1,4 @@ +build/dtb_c.o: src/dtb.c include/bcm2837/rpi_mmu.h include/dtb.h \ + include/uart1.h include/string.h include/stddef.h include/cpio.h \ + include/memory.h include/bcm2837/rpi_mmu.h include/list.h \ + include/stddef.h diff --git a/lab8/kernel/build/dtb_c.o b/lab8/kernel/build/dtb_c.o new file mode 100644 index 000000000..aee4f9c76 Binary files /dev/null and b/lab8/kernel/build/dtb_c.o differ diff --git a/lab8/kernel/build/entry_s.d b/lab8/kernel/build/entry_s.d new file mode 100644 index 000000000..3420854b9 --- /dev/null +++ b/lab8/kernel/build/entry_s.d @@ -0,0 +1 @@ +build/entry_s.o: src/entry.S diff --git a/lab8/kernel/build/entry_s.o b/lab8/kernel/build/entry_s.o new file mode 100644 index 000000000..8a89f1ddb Binary files /dev/null and b/lab8/kernel/build/entry_s.o differ diff --git a/lab8/kernel/build/exception_c.d b/lab8/kernel/build/exception_c.d new file mode 100644 index 000000000..83c55516f --- /dev/null +++ b/lab8/kernel/build/exception_c.d @@ -0,0 +1,6 @@ +build/exception_c.o: src/exception.c include/bcm2837/rpi_uart1.h \ + include/bcm2837/rpi_base.h include/bcm2837/rpi_mmu.h include/uart1.h \ + include/exception.h include/bcm2837/rpi_irq.h include/timer.h \ + include/list.h include/stddef.h include/irqtask.h include/syscall.h \ + include/exception.h include/stddef.h include/sched.h include/vfs.h \ + include/signal.h include/mmu.h include/sched.h diff --git a/lab8/kernel/build/exception_c.o b/lab8/kernel/build/exception_c.o new file mode 100644 index 000000000..d64c41f8d Binary files /dev/null and b/lab8/kernel/build/exception_c.o differ diff --git a/lab8/kernel/build/fat32_c.d b/lab8/kernel/build/fat32_c.d new file mode 100644 index 000000000..57d92dbfd --- /dev/null +++ b/lab8/kernel/build/fat32_c.d @@ -0,0 +1,6 @@ +build/fat32_c.o: src/fat32.c include/fat32.h include/list.h \ + include/stddef.h include/vfs.h include/stddef.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/sdhost.h include/string.h \ + include/list.h include/uart1.h include/exception.h \ + include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h diff --git a/lab8/kernel/build/fat32_c.o b/lab8/kernel/build/fat32_c.o new file mode 100644 index 000000000..38fec0dc0 Binary files /dev/null and b/lab8/kernel/build/fat32_c.o differ diff --git a/lab8/kernel/build/initramfs_c.d b/lab8/kernel/build/initramfs_c.d new file mode 100644 index 000000000..f02fed9c9 --- /dev/null +++ b/lab8/kernel/build/initramfs_c.d @@ -0,0 +1,4 @@ +build/initramfs_c.o: src/initramfs.c include/initramfs.h include/stddef.h \ + include/vfs.h include/vfs.h include/string.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/list.h include/stddef.h include/cpio.h \ + include/uart1.h diff --git a/lab8/kernel/build/initramfs_c.o b/lab8/kernel/build/initramfs_c.o new file mode 100644 index 000000000..1da6cb469 Binary files /dev/null and b/lab8/kernel/build/initramfs_c.o differ diff --git a/lab8/kernel/build/irqtask_c.d b/lab8/kernel/build/irqtask_c.d new file mode 100644 index 000000000..8bd33d2fe --- /dev/null +++ b/lab8/kernel/build/irqtask_c.d @@ -0,0 +1,4 @@ +build/irqtask_c.o: src/irqtask.c include/irqtask.h include/list.h \ + include/stddef.h include/exception.h include/bcm2837/rpi_irq.h \ + include/bcm2837/rpi_base.h include/bcm2837/rpi_mmu.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/uart1.h diff --git a/lab8/kernel/build/irqtask_c.o b/lab8/kernel/build/irqtask_c.o new file mode 100644 index 000000000..f39805d1a Binary files /dev/null and b/lab8/kernel/build/irqtask_c.o differ diff --git a/lab8/kernel/build/kernel8.elf b/lab8/kernel/build/kernel8.elf new file mode 100644 index 000000000..b2617c04a Binary files /dev/null and b/lab8/kernel/build/kernel8.elf differ diff --git a/lab8/kernel/build/main_c.d b/lab8/kernel/build/main_c.d new file mode 100644 index 000000000..411b5ed46 --- /dev/null +++ b/lab8/kernel/build/main_c.d @@ -0,0 +1,6 @@ +build/main_c.o: src/main.c include/uart1.h include/shell.h include/dtb.h \ + include/memory.h include/bcm2837/rpi_mmu.h include/list.h \ + include/stddef.h include/exception.h include/bcm2837/rpi_irq.h \ + include/bcm2837/rpi_base.h include/bcm2837/rpi_mmu.h include/irqtask.h \ + include/timer.h include/sched.h include/vfs.h include/stddef.h \ + include/vfs.h diff --git a/lab8/kernel/build/main_c.o b/lab8/kernel/build/main_c.o new file mode 100644 index 000000000..340cb53b4 Binary files /dev/null and b/lab8/kernel/build/main_c.o differ diff --git a/lab8/kernel/build/mbox_c.d b/lab8/kernel/build/mbox_c.d new file mode 100644 index 000000000..366e2771f --- /dev/null +++ b/lab8/kernel/build/mbox_c.d @@ -0,0 +1,2 @@ +build/mbox_c.o: src/mbox.c include/bcm2837/rpi_mbox.h \ + include/bcm2837/rpi_base.h include/bcm2837/rpi_mmu.h include/mbox.h diff --git a/lab8/kernel/build/mbox_c.o b/lab8/kernel/build/mbox_c.o new file mode 100644 index 000000000..40cc2307f Binary files /dev/null and b/lab8/kernel/build/mbox_c.o differ diff --git a/lab8/kernel/build/memory_c.d b/lab8/kernel/build/memory_c.d new file mode 100644 index 000000000..a5f836ed2 --- /dev/null +++ b/lab8/kernel/build/memory_c.d @@ -0,0 +1,5 @@ +build/memory_c.o: src/memory.c include/memory.h include/bcm2837/rpi_mmu.h \ + include/list.h include/stddef.h include/list.h include/uart1.h \ + include/exception.h include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h include/dtb.h include/cpio.h include/mmu.h \ + include/stddef.h include/sched.h include/vfs.h include/exception.h diff --git a/lab8/kernel/build/memory_c.o b/lab8/kernel/build/memory_c.o new file mode 100644 index 000000000..650dab9b5 Binary files /dev/null and b/lab8/kernel/build/memory_c.o differ diff --git a/lab8/kernel/build/mmu_c.d b/lab8/kernel/build/mmu_c.d new file mode 100644 index 000000000..fe3d604c1 --- /dev/null +++ b/lab8/kernel/build/mmu_c.d @@ -0,0 +1,5 @@ +build/mmu_c.o: src/mmu.c include/bcm2837/rpi_mmu.h include/mmu.h \ + include/stddef.h include/sched.h include/list.h include/stddef.h \ + include/vfs.h include/exception.h include/bcm2837/rpi_irq.h \ + include/bcm2837/rpi_base.h include/bcm2837/rpi_mmu.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/string.h include/uart1.h diff --git a/lab8/kernel/build/mmu_c.o b/lab8/kernel/build/mmu_c.o new file mode 100644 index 000000000..fdc92f604 Binary files /dev/null and b/lab8/kernel/build/mmu_c.o differ diff --git a/lab8/kernel/build/sched_c.d b/lab8/kernel/build/sched_c.d new file mode 100644 index 000000000..6b58c80d9 --- /dev/null +++ b/lab8/kernel/build/sched_c.d @@ -0,0 +1,6 @@ +build/sched_c.o: src/sched.c include/sched.h include/list.h \ + include/stddef.h include/vfs.h include/stddef.h include/exception.h \ + include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h include/memory.h include/bcm2837/rpi_mmu.h \ + include/timer.h include/uart1.h include/signal.h include/exception.h \ + include/mmu.h include/sched.h include/string.h diff --git a/lab8/kernel/build/sched_c.o b/lab8/kernel/build/sched_c.o new file mode 100644 index 000000000..c7c682819 Binary files /dev/null and b/lab8/kernel/build/sched_c.o differ diff --git a/lab8/kernel/build/sched_s.d b/lab8/kernel/build/sched_s.d new file mode 100644 index 000000000..b49148f3e --- /dev/null +++ b/lab8/kernel/build/sched_s.d @@ -0,0 +1 @@ +build/sched_s.o: src/sched.S diff --git a/lab8/kernel/build/sched_s.o b/lab8/kernel/build/sched_s.o new file mode 100644 index 000000000..34a4ec761 Binary files /dev/null and b/lab8/kernel/build/sched_s.o differ diff --git a/lab8/kernel/build/sdhost_c.d b/lab8/kernel/build/sdhost_c.d new file mode 100644 index 000000000..6b4157d83 --- /dev/null +++ b/lab8/kernel/build/sdhost_c.d @@ -0,0 +1 @@ +build/sdhost_c.o: src/sdhost.c diff --git a/lab8/kernel/build/sdhost_c.o b/lab8/kernel/build/sdhost_c.o new file mode 100644 index 000000000..1910b72c8 Binary files /dev/null and b/lab8/kernel/build/sdhost_c.o differ diff --git a/lab8/kernel/build/shell_c.d b/lab8/kernel/build/shell_c.d new file mode 100644 index 000000000..4158ad760 --- /dev/null +++ b/lab8/kernel/build/shell_c.d @@ -0,0 +1,5 @@ +build/shell_c.o: src/shell.c include/stddef.h include/shell.h \ + include/uart1.h include/mbox.h include/power.h include/bcm2837/rpi_mmu.h \ + include/cpio.h include/string.h include/stddef.h include/dtb.h \ + include/memory.h include/list.h include/timer.h include/sched.h \ + include/vfs.h diff --git a/lab8/kernel/build/shell_c.o b/lab8/kernel/build/shell_c.o new file mode 100644 index 000000000..d9a08f496 Binary files /dev/null and b/lab8/kernel/build/shell_c.o differ diff --git a/lab8/kernel/build/signal_c.d b/lab8/kernel/build/signal_c.d new file mode 100644 index 000000000..c99ff9de7 --- /dev/null +++ b/lab8/kernel/build/signal_c.d @@ -0,0 +1,5 @@ +build/signal_c.o: src/signal.c include/signal.h include/exception.h \ + include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h include/sched.h include/list.h \ + include/stddef.h include/vfs.h include/stddef.h include/syscall.h \ + include/memory.h include/bcm2837/rpi_mmu.h include/mmu.h include/sched.h diff --git a/lab8/kernel/build/signal_c.o b/lab8/kernel/build/signal_c.o new file mode 100644 index 000000000..0bb15fa57 Binary files /dev/null and b/lab8/kernel/build/signal_c.o differ diff --git a/lab8/kernel/build/string_c.d b/lab8/kernel/build/string_c.d new file mode 100644 index 000000000..0bad1be39 --- /dev/null +++ b/lab8/kernel/build/string_c.d @@ -0,0 +1,2 @@ +build/string_c.o: src/string.c include/string.h include/stddef.h \ + include/stddef.h diff --git a/lab8/kernel/build/string_c.o b/lab8/kernel/build/string_c.o new file mode 100644 index 000000000..317341031 Binary files /dev/null and b/lab8/kernel/build/string_c.o differ diff --git a/lab8/kernel/build/syscall_c.d b/lab8/kernel/build/syscall_c.d new file mode 100644 index 000000000..6a717eb1b --- /dev/null +++ b/lab8/kernel/build/syscall_c.d @@ -0,0 +1,7 @@ +build/syscall_c.o: src/syscall.c include/syscall.h include/exception.h \ + include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h include/stddef.h include/cpio.h \ + include/sched.h include/list.h include/stddef.h include/vfs.h \ + include/uart1.h include/exception.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/mbox.h include/signal.h include/mmu.h \ + include/sched.h include/string.h include/dev_framebuffer.h diff --git a/lab8/kernel/build/syscall_c.o b/lab8/kernel/build/syscall_c.o new file mode 100644 index 000000000..d9042b426 Binary files /dev/null and b/lab8/kernel/build/syscall_c.o differ diff --git a/lab8/kernel/build/timer_c.d b/lab8/kernel/build/timer_c.d new file mode 100644 index 000000000..98fe6b054 --- /dev/null +++ b/lab8/kernel/build/timer_c.d @@ -0,0 +1,5 @@ +build/timer_c.o: src/timer.c include/timer.h include/list.h \ + include/stddef.h include/uart1.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/string.h include/stddef.h \ + include/exception.h include/bcm2837/rpi_irq.h include/bcm2837/rpi_base.h \ + include/bcm2837/rpi_mmu.h diff --git a/lab8/kernel/build/timer_c.o b/lab8/kernel/build/timer_c.o new file mode 100644 index 000000000..8bbff9716 Binary files /dev/null and b/lab8/kernel/build/timer_c.o differ diff --git a/lab8/kernel/build/tmpfs_c.d b/lab8/kernel/build/tmpfs_c.d new file mode 100644 index 000000000..579016ea3 --- /dev/null +++ b/lab8/kernel/build/tmpfs_c.d @@ -0,0 +1,4 @@ +build/tmpfs_c.o: src/tmpfs.c include/tmpfs.h include/stddef.h \ + include/vfs.h include/vfs.h include/string.h include/memory.h \ + include/bcm2837/rpi_mmu.h include/list.h include/stddef.h \ + include/uart1.h diff --git a/lab8/kernel/build/tmpfs_c.o b/lab8/kernel/build/tmpfs_c.o new file mode 100644 index 000000000..6e7a4deb5 Binary files /dev/null and b/lab8/kernel/build/tmpfs_c.o differ diff --git a/lab8/kernel/build/uart1_c.d b/lab8/kernel/build/uart1_c.d new file mode 100644 index 000000000..5abc0c5c7 --- /dev/null +++ b/lab8/kernel/build/uart1_c.d @@ -0,0 +1,5 @@ +build/uart1_c.o: src/uart1.c include/bcm2837/rpi_gpio.h \ + include/bcm2837/rpi_base.h include/bcm2837/rpi_mmu.h \ + include/bcm2837/rpi_uart1.h include/bcm2837/rpi_irq.h include/uart1.h \ + include/exception.h include/bcm2837/rpi_irq.h include/string.h \ + include/stddef.h diff --git a/lab8/kernel/build/uart1_c.o b/lab8/kernel/build/uart1_c.o new file mode 100644 index 000000000..7c7732cd3 Binary files /dev/null and b/lab8/kernel/build/uart1_c.o differ diff --git a/lab8/kernel/build/vfs_c.d b/lab8/kernel/build/vfs_c.d new file mode 100644 index 000000000..e109f5a4e --- /dev/null +++ b/lab8/kernel/build/vfs_c.d @@ -0,0 +1,5 @@ +build/vfs_c.o: src/vfs.c include/vfs.h include/stddef.h include/tmpfs.h \ + include/vfs.h include/memory.h include/bcm2837/rpi_mmu.h include/list.h \ + include/stddef.h include/string.h include/uart1.h include/initramfs.h \ + include/dev_uart.h include/dev_framebuffer.h include/sdhost.h \ + include/fat32.h diff --git a/lab8/kernel/build/vfs_c.o b/lab8/kernel/build/vfs_c.o new file mode 100644 index 000000000..e10dd9dc1 Binary files /dev/null and b/lab8/kernel/build/vfs_c.o differ diff --git a/lab8/kernel/include/cpio.h b/lab8/kernel/include/cpio.h new file mode 100644 index 000000000..e662bee51 --- /dev/null +++ b/lab8/kernel/include/cpio.h @@ -0,0 +1,41 @@ +#ifndef _CPIO_H_ +#define _CPIO_H_ + +/* + cpio format : https://manpages.ubuntu.com/manpages/bionic/en/man5/cpio.5.html + We are using "newc" format + header, file path, file data, header ...... + header + file path (padding 4 bytes) + file data (padding 4 bytes) (max size 4gb) +*/ + +#define CPIO_NEWC_HEADER_MAGIC "070701" // big endian constant, to check whether it is big endian or little endian + +extern void* CPIO_DEFAULT_START; +extern void* CPIO_DEFAULT_END; + +// Using newc archive format +struct cpio_newc_header +{ + char c_magic[6]; // fixed, "070701". + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +}; + +/* write pathname, data, next header into corresponding parameter*/ +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); + +#endif /* _CPIO_H_ */ diff --git a/lab8/kernel/include/dev_framebuffer.h b/lab8/kernel/include/dev_framebuffer.h new file mode 100644 index 000000000..4ca5a8565 --- /dev/null +++ b/lab8/kernel/include/dev_framebuffer.h @@ -0,0 +1,24 @@ +#ifndef _DEV_FRAMEBUFFER_H_ +#define _DEV_FRAMEBUFFER_H_ + +#include "stddef.h" +#include "vfs.h" + +struct framebuffer_info +{ + unsigned int width; + unsigned int height; + unsigned int pitch; + unsigned int isrgb; +}; + +int init_dev_framebuffer(); + +int dev_framebuffer_write(struct file *file, const void *buf, size_t len); +int dev_framebuffer_read(struct file *file, void *buf, size_t len); +int dev_framebuffer_open(struct vnode *file_node, struct file **target); +int dev_framebuffer_close(struct file *file); +long dev_framebuffer_lseek64(struct file *file, long offset, int whence); +int dev_framebuffer_op_deny(); + +#endif diff --git a/lab8/kernel/include/dev_uart.h b/lab8/kernel/include/dev_uart.h new file mode 100644 index 000000000..b46dceed4 --- /dev/null +++ b/lab8/kernel/include/dev_uart.h @@ -0,0 +1,15 @@ +#ifndef _DEV_UART_H_ +#define _DEV_UART_H_ + +#include "stddef.h" +#include "vfs.h" + +int init_dev_uart(); + +int dev_uart_write(struct file *file, const void *buf, size_t len); +int dev_uart_read(struct file *file, void *buf, size_t len); +int dev_uart_open(struct vnode *file_node, struct file **target); +int dev_uart_close(struct file *file); +int dev_uart_op_deny(); + +#endif diff --git a/lab8/kernel/include/dtb.h b/lab8/kernel/include/dtb.h new file mode 100644 index 000000000..fb7c451d5 --- /dev/null +++ b/lab8/kernel/include/dtb.h @@ -0,0 +1,28 @@ +#ifndef _DTB_H_ +#define _DTB_H_ + +#define uint32_t unsigned int +#define uint64_t unsigned long long + +// manipulate device tree with dtb file format +// linux kernel fdt.h +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +extern char* dtb_ptr; + +typedef void (*dtb_callback)(uint32_t node_type, char *name, void *value, uint32_t name_size); + +uint32_t uint32_endian_big2lttle(uint32_t data); +uint64_t uint64_endian_big2lttle(uint64_t data); + +void traverse_device_tree(void *base, dtb_callback callback); //traverse dtb tree +void dtb_callback_show_tree(uint32_t node_type, char *name, void *value, uint32_t name_size); +void dtb_callback_initramfs(uint32_t node_type, char *name, void *value, uint32_t name_size); + +void dtb_find_and_store_reserved_memory(); + +#endif diff --git a/lab8/kernel/include/exception.h b/lab8/kernel/include/exception.h new file mode 100644 index 000000000..f02bf2edb --- /dev/null +++ b/lab8/kernel/include/exception.h @@ -0,0 +1,81 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +#include "bcm2837/rpi_irq.h" + +#define CORE0_INTERRUPT_SOURCE ((volatile unsigned int *)(PHYS_TO_VIRT(0x40000060))) + +#define IRQ_PENDING_1_AUX_INT (1<<29) +#define INTERRUPT_SOURCE_GPU (1<<8) +#define INTERRUPT_SOURCE_CNTPNSIRQ (1<<1) + +typedef struct trapframe +{ + unsigned long x0; + unsigned long x1; + unsigned long x2; + unsigned long x3; + unsigned long x4; + unsigned long x5; + unsigned long x6; + unsigned long x7; + unsigned long x8; + unsigned long x9; + unsigned long x10; + unsigned long x11; + unsigned long x12; + unsigned long x13; + unsigned long x14; + unsigned long x15; + unsigned long x16; + unsigned long x17; + unsigned long x18; + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long x29; + unsigned long x30; + unsigned long spsr_el1; + unsigned long elr_el1; + unsigned long sp_el0; +} trapframe_t; + +#define MEMFAIL_DATA_ABORT_LOWER 0b100100 // esr_el1 +#define MEMFAIL_INST_ABORT_LOWER 0b100000 // EC, bits [31:26] + +#define TF_LEVEL0 0b000100 // iss IFSC, bits [5:0] +#define TF_LEVEL1 0b000101 +#define TF_LEVEL2 0b000110 +#define TF_LEVEL3 0b000111 + +typedef struct{ + unsigned int iss : 25, // Instruction specific syndrome + il : 1, // Instruction length bit + ec : 6; // Exception class +} esr_el1_t; + +void sync_64_router(trapframe_t *tpf); +void irq_router(trapframe_t *tpf); +void invalid_exception_router(); + +static inline void el1_interrupt_enable() +{ + __asm__ __volatile__("msr daifclr, 0xf"); +} + +static inline void el1_interrupt_disable() +{ + __asm__ __volatile__("msr daifset, 0xf"); +} + +void lock(); +void unlock(); + +#endif /* _EXCEPTION_H_ */ diff --git a/lab8/kernel/include/fat32.h b/lab8/kernel/include/fat32.h new file mode 100644 index 000000000..768267c63 --- /dev/null +++ b/lab8/kernel/include/fat32.h @@ -0,0 +1,208 @@ +#ifndef FAT32_H +#define FAT32_H + +#include "list.h" +#include "vfs.h" + +#define BLOCK_SIZE 512 +#define CLUSTER_ENTRY_PER_BLOCK (BLOCK_SIZE / sizeof(struct cluster_entry_t)) +#define DIR_PER_BLOCK (BLOCK_SIZE / sizeof(struct dir_t)) +#define INVALID_CID 0x0ffffff8 // Last cluster in file (EOC), reserved by FAT32 + +struct partition_t { + unsigned char status; + unsigned char chss_head; + unsigned char chss_sector; + unsigned char chss_cylinder; + unsigned char type; + unsigned char chse_head; + unsigned char chse_sector; + unsigned char chse_cylinder; + unsigned int lba; + unsigned int sectors; +} __attribute__((packed)); + +struct boot_sector_t { + unsigned char jmpboot[3]; + unsigned char oemname[8]; + unsigned short bytes_per_sector; + unsigned char sector_per_cluster; + unsigned short reserved_sector_cnt; + unsigned char fat_cnt; + unsigned short root_entry_cnt; + unsigned short old_sector_cnt; + unsigned char media; + unsigned short sector_per_fat16; + unsigned short sector_per_track; + unsigned short head_cnt; + unsigned int hidden_sector_cnt; // Default Boot Sector ends here + unsigned int sector_cnt; // FAT32 definition + unsigned int sector_per_fat32; + unsigned short extflags; + unsigned short ver; + unsigned int root_cluster; + unsigned short info; + unsigned short bkbooksec; + unsigned char reserved[12]; + unsigned char drvnum; + unsigned char reserved1; + unsigned char bootsig; + unsigned int volid; + unsigned char vollab[11]; + unsigned char fstype[8]; +} __attribute__((packed)); + +struct fat_info_t { + struct boot_sector_t bs; // boot_sector from MBR Partition Table + unsigned int fat_lba; // FAT Region in FAT fs Definition + unsigned int cluster_lba; // Data Region in FAT32 fs Definition +}; + +// attr of dir_t +#define ATTR_READ_ONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME_ID 0x08 +#define ATTR_LFN 0x0f +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_FILE_DIR_MASK (ATTR_DIRECTORY | ATTR_ARCHIVE) + +// Directory structure for FAT SFN +// http://elm-chan.org/docs/fat_e.html +struct dir_t { + unsigned char name[11]; + unsigned char attr; + unsigned char ntres; + unsigned char crttimetenth; + unsigned short crttime; + unsigned short crtdate; + unsigned short lstaccdate; + unsigned short ch; + unsigned short wrttime; + unsigned short wrtdate; + unsigned short cl; + unsigned int size; +} __attribute__((packed)); + +// Directory structure for FAT LFN +// https://blog.csdn.net/ZCShouCSDN/article/details/97610903 +struct long_dir_t { + unsigned char order; + unsigned char name1[10]; + unsigned char attr; + unsigned char type; + unsigned char checksum; + unsigned char name2[12]; + unsigned short fstcluslo; + unsigned char name3[4]; +} __attribute__((packed)); + +struct cluster_entry_t { + union { + unsigned int val; + struct { + unsigned int idx: 28; + unsigned int reserved: 4; + }; + }; +}; + +struct filename_t { + union { + unsigned char fullname[256]; + struct { + unsigned char name[13]; + } part[20]; + }; +} __attribute__((packed)); + +struct fat_file_block_t { + /* Link fat_file_block_t */ + struct list_head list; + unsigned int oid; // offset if of file, N = offset N* BLOCK_SIZE of file + unsigned int cid; // cluster id + unsigned int bufIsUpdated; + unsigned int isDirty; + unsigned char buf[BLOCK_SIZE]; +}; + +struct fat_file_t { + /* Head of fat_file_block_t chain */ + struct list_head list; + unsigned int size; +}; + +struct fat_dir_t { + /* Head of fat32_inode chain */ + struct list_head list; +}; + +struct fat_mount_t { + /* Link fat_mount_t */ + struct list_head list; + struct mount *mount; +}; + +// type of struct fat32_inode +#define FAT_DIR 1 +#define FAT_FILE 2 + +struct fat32_inode { + const char *name; + struct vnode *node; // redirect to vnode + struct fat_info_t *fat; + /* Link fat32_inode */ + struct list_head list; + /* cluster id */ + unsigned int cid; // cluster id + unsigned int type; + union { + struct fat_dir_t *dir; + struct fat_file_t *file; + }; +}; + +int register_fat32(); + +int fat32_mount(struct filesystem *fs, struct mount *mount); +int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int fat32_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int fat32_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); +int fat32_isdir(struct vnode *dir_node); +int fat32_getname(struct vnode *dir_node, const char **name); +int fat32_getsize(struct vnode *dir_node); + +int fat32_write(struct file *file, const void *buf, size_t len); +int fat32_read(struct file *file, void *buf, size_t len); +int fat32_open(struct vnode *file_node, struct file **target); +int fat32_close(struct file *file); +long fat32_lseek64(struct file *file, long offset, int whence); +int fat32_sync(struct filesystem *fs); + +unsigned int alloc_cluster(struct fat_info_t *fat, unsigned int prev_cid); +unsigned int get_next_cluster(unsigned int fat_lba, unsigned int cluster_id); + +struct vnode *_create_vnode(struct vnode *parent, const char *name, unsigned int type, unsigned int cid, unsigned int size); + +int _lookup_cache(struct vnode *dir_node, struct vnode **target, const char *component_name); +struct dir_t* __lookup_fat32(struct vnode *dir_node, const char *component_name, unsigned char *buf, int *buflba); +int _lookup_fat32(struct vnode *dir_node, struct vnode **target, const char *component_name); + +int _readfile(void *buf, struct fat32_inode *data, unsigned long long fileoff, unsigned long long len); +int _readfile_fat32(struct fat32_inode *data, unsigned long long bckoff, unsigned char *buf, unsigned int bufoff, unsigned int size, unsigned int oid, unsigned int cid); +int _readfile_cache(struct fat32_inode *data, unsigned long long bckoff, unsigned char *buf, unsigned long long bufoff, unsigned int size, struct fat_file_block_t *block); +int _readfile_seek_fat32(struct fat32_inode *data, unsigned int foid, unsigned int fcid, struct fat_file_block_t **block); +int _readfile_seek_cache(struct fat32_inode *data, unsigned int foid, struct fat_file_block_t **block); + +int _writefile(const void *buf, struct fat32_inode *data, unsigned long long fileoff, unsigned long long len); +int _writefile_fat32(struct fat32_inode *data, unsigned long long bckoff, const unsigned char *buf, unsigned int bufoff, unsigned int size, unsigned int oid, unsigned int cid); +int _writefile_cache(struct fat32_inode *data, unsigned long long bckoff, const unsigned char *buf, unsigned long long bufoff, unsigned int size, struct fat_file_block_t *block); +int _writefile_seek_fat32(struct fat32_inode *data, unsigned int foid, unsigned int fcid, struct fat_file_block_t **block); +int _writefile_seek_cache(struct fat32_inode *data, unsigned int foid, struct fat_file_block_t **block); + +void _sync_dir(struct vnode *dirnode); +void _do_sync_dir(struct vnode *dirnode); +void _do_sync_file(struct vnode *filenode); + +#endif diff --git a/lab8/kernel/include/initramfs.h b/lab8/kernel/include/initramfs.h new file mode 100644 index 000000000..494744b1e --- /dev/null +++ b/lab8/kernel/include/initramfs.h @@ -0,0 +1,37 @@ +#ifndef _INITRAMFS_H_ +#define _INITRAMFS_H_ + +#include "stddef.h" +#include "vfs.h" + +#define INITRAMFS_MAX_DIR_ENTRY 100 + +// initramfs basically is same as tmpfs, but it is read-only + +struct initramfs_inode +{ + enum fsnode_type type; + char* name; + struct vnode *entry[INITRAMFS_MAX_DIR_ENTRY]; + char *data; + size_t datasize; +}; + +int register_initramfs(); +int initramfs_setup_mount(struct filesystem *fs, struct mount *_mount); + +int initramfs_write(struct file *file, const void *buf, size_t len); +int initramfs_read(struct file *file, void *buf, size_t len); +int initramfs_open(struct vnode *file_node, struct file **target); +int initramfs_close(struct file *file); +long initramfs_lseek64(struct file *file, long offset, int whence); +long initramfs_getsize(struct vnode *vd); + +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); +int initramfs_sync(struct filesystem *fs); + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum fsnode_type type); + +#endif /* _INITRAMFS_H_ */ diff --git a/lab8/kernel/include/irqtask.h b/lab8/kernel/include/irqtask.h new file mode 100644 index 000000000..395bea0b7 --- /dev/null +++ b/lab8/kernel/include/irqtask.h @@ -0,0 +1,22 @@ +#ifndef _IRQTASK_H_ +#define _IRQTASK_H_ + +#include "list.h" + +// smaller is more preemptive +#define UART_IRQ_PRIORITY 1 +#define TIMER_IRQ_PRIORITY 0 + +typedef struct irqtask +{ + struct list_head listhead; + unsigned long long priority; + void *task_function; +} irqtask_t; + +void irqtask_add(void *task_function, unsigned long long priority); +void irqtask_run(irqtask_t *the_task); +void irqtask_run_preemptive(); +void irqtask_init_list(); + +#endif diff --git a/lab8/kernel/include/list.h b/lab8/kernel/include/list.h new file mode 100644 index 000000000..65ba89fea --- /dev/null +++ b/lab8/kernel/include/list.h @@ -0,0 +1,223 @@ +#ifndef LIST_H +#define LIST_H + +#include + +/* + * 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.h + * 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 *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * 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 *new, struct list_head *head) +{ + __list_add(new, 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 *new, struct list_head *head) +{ + __list_add(new, 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) + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_size(const struct list_head *head) +{ + list_head_t *pos; + int i= 0; + list_for_each(pos, head) + { + i++; + } + return i; +} + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif /* defined(__GNUC__) */ + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ + (type *) ((char *) __pmember - offsetof(type, member)); \ + }) +#else /* __LIST_HAVE_TYPEOF */ +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) -offsetof(type, member))) +#endif /* __LIST_HAVE_TYPEOF */ +#endif /* container_of */ + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif /* __LIST_HAVE_TYPEOF */ + +/** + * iter_for_each_entry - iterate over list entries from @iter + * @entry: pointer used as iterator + * @iter: pointer to the start of this iteration + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define iter_for_each_entry(entry, iter, head, member) \ + for (entry = list_entry(iter, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif /* __LIST_HAVE_TYPEOF */ + +#endif diff --git a/lab8/kernel/include/mbox.h b/lab8/kernel/include/mbox.h new file mode 100644 index 000000000..d5f9514ff --- /dev/null +++ b/lab8/kernel/include/mbox.h @@ -0,0 +1,63 @@ +#ifndef _MBOX_H_ +#define _MBOX_H_ + +extern volatile unsigned int pt[36]; + +// Mailbox Register MMIO +// https://jsandler18.github.io/extra/mailbox.html +// include/bcm2837/rpi_mbox.h + +// Mailbox Channels +// https://github.com/raspberrypi/firmware/wiki/Mailboxes +typedef enum { + MBOX_POWER_MANAGEMENT = 0, + MBOX_FRAMEBUFFER, + MBOX_VIRTUAL_UART, + MBOX_VCHIQ, + MBOX_LEDS, + MBOX_BUTTONS, + MBOX_TOUCHSCREEN, + MBOX_UNUSED, + MBOX_TAGS_ARM_TO_VC, + MBOX_TAGS_VC_TO_ARM, +} mbox_channel_type; + +// Status Code from Broadcom Videocode Driver +// brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/arm_control.h +enum mbox_status_reg_bits { + BCM_ARM_VC_MS_FULL = 0x80000000, + BCM_ARM_VC_MS_EMPTY = 0x40000000, + BCM_ARM_VC_MS_LEVEL = 0x400000FF, +}; + +enum mbox_buffer_status_code { + MBOX_REQUEST_PROCESS = 0x00000000, + MBOX_REQUEST_SUCCEED = 0x80000000, + MBOX_REQUEST_FAILED = 0x80000001, +}; + +// Tag +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +// Included partition only +typedef enum { + /* Videocore */ + MBOX_TAG_GET_FIRMWARE_VERSION = 0x1, + + /* Hardware */ + MBOX_TAG_GET_BOARD_MODEL = 0x10001, + MBOX_TAG_GET_BOARD_REVISION, + MBOX_TAG_GET_BOARD_MAC_ADDRESS, + MBOX_TAG_GET_BOARD_SERIAL, + MBOX_TAG_GET_ARM_MEMORY, + MBOX_TAG_GET_VC_MEMORY, + MBOX_TAG_GET_CLOCKS, + +} mbox_tag_type; + + +#define MBOX_TAG_REQUEST_CODE 0x00000000 +#define MBOX_TAG_LAST_BYTE 0x00000000 + +int mbox_call(mbox_channel_type, unsigned int); + +#endif /*_MBOX_H_*/ diff --git a/lab8/kernel/include/memory.h b/lab8/kernel/include/memory.h new file mode 100644 index 000000000..3d8aee537 --- /dev/null +++ b/lab8/kernel/include/memory.h @@ -0,0 +1,71 @@ +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +#include "bcm2837/rpi_mmu.h" +#include "list.h" + +/* Lab2 */ +void* s_allocator(unsigned int size); +void s_free(void* ptr); + +/* Lab4 */ +#define BUDDY_MEMORY_BASE PHYS_TO_VIRT(0x0) // 0x10000000 - 0x20000000 (SPEC) -> Advanced #3 for all memory region +#define BUDDY_MEMORY_PAGE_COUNT 0x3C000 // let BUDDY_MEMORY use 0x0 ~ 0x3C000000 (SPEC) +#define PAGESIZE 0x1000 // 4KB +#define MAX_PAGES 0x10000 // 65536 (Entries), PAGESIZE * MAX_PAGES = 0x10000000 (SPEC) + +typedef enum { + FRAME_FREE = -2, + FRAME_ALLOCATED, + FRAME_IDX_0 = 0, // 0x1000 + FRAME_IDX_1, // 0x2000 + FRAME_IDX_2, // 0x4000 + FRAME_IDX_3, // 0x8000 + FRAME_IDX_4, // 0x10000 + FRAME_IDX_5, // 0x20000 + FRAME_IDX_6, // 0x40000 + FRAME_IDX_FINAL = 7, // 0x80000 + FRAME_MAX_IDX = 8 +} frame_value_type; + +typedef enum { + CACHE_NONE = -1, // Cache not used + CACHE_IDX_0 = 0, // 0x20 + CACHE_IDX_1, // 0x40 + CACHE_IDX_2, // 0x80 + CACHE_IDX_3, // 0x100 + CACHE_IDX_4, // 0x200 + CACHE_IDX_5, // 0x400 + CACHE_IDX_FINAL = 6, // 0x800 + CACHE_MAX_IDX = 7 +} cache_value_type; + +typedef struct frame +{ + struct list_head listhead; // store freelist + int val; // store order + int used; + int cache_order; + unsigned int idx; +} frame_t; + +void init_allocator(); +frame_t *release_redundant(frame_t *frame); +frame_t *get_buddy(frame_t *frame); +frame_t *coalesce(frame_t *frame_ptr); + +void dump_page_info(); +void dump_cache_info(); + +//buddy system +void* page_malloc(unsigned int size); +void page_free(void *ptr); +void page2caches(int order); +void* cache_malloc(unsigned int size); +void cache_free(void* ptr); + +void* kmalloc(unsigned int size); +void kfree(void *ptr); +void memory_reserve(unsigned long long start, unsigned long long end); + +#endif /* _MEMORY_H_ */ diff --git a/lab8/kernel/include/mmu.h b/lab8/kernel/include/mmu.h new file mode 100644 index 000000000..b882e42d0 --- /dev/null +++ b/lab8/kernel/include/mmu.h @@ -0,0 +1,69 @@ +#ifndef _MMU_H_ +#define _MMU_H_ + +#include "stddef.h" + // tcr_el1: The control register for stage 1 of the EL1&0 translation regime. +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) // T0SZ[5:0] The size offset for ttbr0_el1 is 2**(64-T0SZ): 0x0000_0000_0000_0000 <- 0x0000_FFFF_FFFF_FFFF +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) // T1SZ[21:16] The size offset for ttbr1_el1 is 2**(64-T1SZ): 0xFFFF_0000_0000_0000 -> 0xFFFF_FFFF_FFFF_FFFF +#define TCR_CONFIG_DEFAULT (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB) // TG0[15:14] Granule size for the TTBR0_EL1: 0b00 = 4KB // TG1[31:30] Granule size for the TTBR1_EL1: 0b10 = 4KB + +#define MAIR_DEVICE_nGnRnE 0b00000000 // ((MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8))) +#define MAIR_NORMAL_NOCACHE 0b01000100 // mair_el1: Provides the memory attribute encodings corresponding to the possible AttrIndx values for stage 1 translations at EL1. +#define MAIR_IDX_DEVICE_nGnRnE 0 // ATTR0[7:0]: 0b0000dd00 Device memory, dd = 0b00 Device-nGnRnE memory +#define MAIR_IDX_NORMAL_NOCACHE 1 // ATTR1[14:8] 0booooiiii Normal memory, oooo = 0b0100 Outer Non-cacheable, iiii = 0b0100 Inner Non-cacheable + +#define PD_TABLE 0b11L // Table Entry Armv8_a_address_translation p.14 +#define PD_BLOCK 0b01L // Block Entry +#define PD_UNX (1L << 54) // non-executable page frame for EL0 if set +#define PD_KNX (1L << 53) // non-executable page frame for EL1 if set +#define PD_ACCESS (1L << 10) // a page fault is generated if not set +#define PD_RDONLY (1L << 7) // 0 for read-write, 1 for read-only. +#define PD_UK_ACCESS (1L << 6) // 0 for only kernel access, 1 for user/kernel access. + +#define PERIPHERAL_START 0x3c000000L +#define PERIPHERAL_END 0x3f000000L +#define USER_KERNEL_BASE 0x00000000L +#define USER_STACK_BASE 0xfffffffff000L +#define USER_SIGNAL_WRAPPER_VA 0xffffffff9000L + +#define MMU_PGD_BASE 0x1000L +#define MMU_PGD_ADDR (MMU_PGD_BASE + 0x0000L) +#define MMU_PUD_ADDR (MMU_PGD_BASE + 0x1000L) +#define MMU_PTE_ADDR (MMU_PGD_BASE + 0x2000L) + +// Used for EL1 +#define BOOT_PGD_ATTR (PD_TABLE) +#define BOOT_PUD_ATTR (PD_TABLE | PD_ACCESS) +#define BOOT_PTE_ATTR_nGnRnE (PD_BLOCK | PD_ACCESS | (MAIR_DEVICE_nGnRnE << 2) | PD_UNX | PD_KNX | PD_UK_ACCESS) // p.17 +#define BOOT_PTE_ATTR_NOCACHE (PD_BLOCK | PD_ACCESS | (MAIR_NORMAL_NOCACHE << 2)) + +#ifndef __ASSEMBLER__ + +#include "sched.h" +#include "exception.h" + +typedef struct vm_area_struct +{ + + list_head_t listhead; + unsigned long virt_addr; + unsigned long phys_addr; + unsigned long area_size; + unsigned long rwx; // 1, 2, 4 + int is_alloced; + +} vm_area_struct_t; + +void *set_2M_kernel_mmu(void *x0); +void map_one_page(size_t *pgd_p, size_t va, size_t pa, size_t flag); + +void mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced); +void mmu_del_vma(struct thread *t); +void mmu_map_pages(size_t *pgd_p, size_t va, size_t size, size_t pa, size_t flag); +void mmu_free_page_tables(size_t *page_table, int level); + +void mmu_memfail_abort_handle(esr_el1_t* esr_el1); + +#endif //__ASSEMBLER__ + +#endif /* _MMU_H_ */ diff --git a/lab8/kernel/include/power.h b/lab8/kernel/include/power.h new file mode 100644 index 000000000..bd8e84b1d --- /dev/null +++ b/lab8/kernel/include/power.h @@ -0,0 +1,9 @@ +#ifndef _POWER_H_ +#define _POWER_H_ + +#include "bcm2837/rpi_mmu.h" +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC PHYS_TO_VIRT(0x3F10001c) +#define PM_WDOG PHYS_TO_VIRT(0x3F100024) + +#endif /*_POWER_H_*/ diff --git a/lab8/kernel/include/sched.h b/lab8/kernel/include/sched.h new file mode 100644 index 000000000..c99a9e101 --- /dev/null +++ b/lab8/kernel/include/sched.h @@ -0,0 +1,70 @@ +#ifndef _SCHED_H_ +#define _SCHED_H_ + +#include "list.h" +#include "vfs.h" + +#define PIDMAX 32768 +#define USTACK_SIZE 0x4000 +#define KSTACK_SIZE 0x4000 +#define SIGNAL_MAX 64 + +extern void switch_to(void *curr_context, void *next_context); +extern void store_context(void *curr_context); +extern void load_context(void *curr_context); +extern void *get_current(); + +typedef struct thread_context +{ + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; + unsigned long lr; + unsigned long sp; + void* pgd; // use for MMU mapping (user space) +} thread_context_t; + +typedef struct thread +{ + list_head_t listhead; + thread_context_t context; + char* data; + unsigned int datasize; + int iszombie; + int pid; + int isused; + char* stack_alloced_ptr; + char* kernel_stack_alloced_ptr; + void (*signal_handler[SIGNAL_MAX+1])(); + int sigcount[SIGNAL_MAX + 1]; + void (*curr_signal_handler)(); + int signal_is_checking; + thread_context_t signal_saved_context; + list_head_t vma_list; + char curr_working_dir[MAX_PATH_NAME+1]; + struct file* file_descriptors_table[MAX_FD+1]; +} thread_t; + +extern thread_t *curr_thread; +extern list_head_t *run_queue; +extern list_head_t *wait_queue; +extern thread_t threads[PIDMAX + 1]; + +void schedule_timer(char *notuse); +void init_thread_sched(); +void idle(); +void schedule(); +void kill_zombies(); +void thread_exit(); +thread_t *thread_create(void *start, unsigned int filesize); +int thread_exec(char *data, unsigned int filesize); + +#endif /* _SCHED_H_ */ diff --git a/lab8/kernel/include/sdhost.h b/lab8/kernel/include/sdhost.h new file mode 100644 index 000000000..fb7cd6b80 --- /dev/null +++ b/lab8/kernel/include/sdhost.h @@ -0,0 +1,8 @@ +#ifndef _SDHOST_H_ +#define _SDHOST_H_ + +void readblock(int block_idx, void* buf); +void writeblock(int block_idx, void* buf); +void sd_init(); + +#endif /* _SDHOST_H_ */ diff --git a/lab8/kernel/include/shell.h b/lab8/kernel/include/shell.h new file mode 100644 index 000000000..8c7caf695 --- /dev/null +++ b/lab8/kernel/include/shell.h @@ -0,0 +1,29 @@ +#ifndef _SHELL_H_ +#define _SHELL_H_ + +#define CMD_MAX_LEN 0x100 +#define MSG_MAX_LEN 0x100 + +typedef struct CLI_CMDS +{ + char command[CMD_MAX_LEN]; + char help[MSG_MAX_LEN]; +} CLI_CMDS; + +int cli_cmd_strcmp(const char*, const char*); +void cli_cmd_clear(char*, int); +void cli_cmd_read(char*); +void cli_cmd_exec(char*); +void cli_print_banner(); + +void do_cmd_cat(char*); +void do_cmd_dtb(); +void do_cmd_exec(char*); +void do_cmd_help(); +void do_cmd_hello(); +void do_cmd_info(); +void do_cmd_ls(char*); +void do_cmd_setTimeout(char* msg, char* sec); +void do_cmd_reboot(); + +#endif /* _SHELL_H_ */ diff --git a/lab8/kernel/include/signal.h b/lab8/kernel/include/signal.h new file mode 100644 index 000000000..f228de096 --- /dev/null +++ b/lab8/kernel/include/signal.h @@ -0,0 +1,47 @@ +#ifndef _SIGNAL_H_ +#define _SIGNAL_H_ + +#define SIGHUP 1 /* Hangup (POSIX). */ +#define SIGINT 2 /* Interrupt (ANSI). */ +#define SIGQUIT 3 /* Quit (POSIX). */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ +#define SIGTRAP 5 /* Trace trap (POSIX). */ +#define SIGABRT 6 /* Abort (ANSI). */ +#define SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define SIGBUS 7 /* BUS error (4.2 BSD). */ +#define SIGFPE 8 /* Floating-point exception (ANSI). */ +#define SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define SIGPIPE 13 /* Broken pipe (POSIX). */ +#define SIGALRM 14 /* Alarm clock (POSIX). */ +#define SIGTERM 15 /* Termination (ANSI). */ +#define SIGSTKFLT 16 /* Stack fault. */ +#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define SIGCHLD 17 /* Child status has changed (POSIX). */ +#define SIGCONT 18 /* Continue (POSIX). */ +#define SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define SIGTTIN 21 /* Background read from tty (POSIX). */ +#define SIGTTOU 22 /* Background write to tty (POSIX). */ +#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define SIGPWR 30 /* Power failure restart (System V). */ +#define SIGSYS 31 /* Bad system call. */ +#define SIGUNUSED 31 + +#include "exception.h" + +void signal_default_handler(); +void check_signal(trapframe_t *tpf); +void run_signal(trapframe_t* tpf,int signal); +void signal_handler_wrapper(); + +#endif /* _SIGNAL_H_ */ diff --git a/lab8/kernel/include/stddef.h b/lab8/kernel/include/stddef.h new file mode 100644 index 000000000..1698bc3f3 --- /dev/null +++ b/lab8/kernel/include/stddef.h @@ -0,0 +1,11 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define NULL 0 +#define size_t unsigned long + +/* Offset of member MEMBER in a struct of type TYPE. */ +#undef offsetof /* in case a system header has defined it. */ +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) + +#endif diff --git a/lab8/kernel/include/string.h b/lab8/kernel/include/string.h new file mode 100644 index 000000000..f4ba13fa4 --- /dev/null +++ b/lab8/kernel/include/string.h @@ -0,0 +1,24 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#include "stddef.h" +#define VSPRINT_MAX_BUF_SIZE 0x100 + +unsigned int sprintf(char *dst, char* fmt, ...); +unsigned int vsprintf(char *dst,char* fmt, __builtin_va_list args); + +unsigned long long strlen(const char *str); +char* strcat(char *dest, const char *src); +char* strncat(char *dest, const char *src, int n); +int strcasecmp(const char *s1, const char *s2); +int strcmp(const char*, const char*); +int strncmp(const char*, const char*, unsigned long long); +char* memcpy(void *dest, const void *src, unsigned long long len); +char* strcpy(char *dest, const char *src); +void* memset(void *s, int c, size_t n); +char* strchr(register const char *s, int c); + +char* str_SepbySpace(char* head); +int atoi(char* str); + +#endif /* _STRING_H_ */ diff --git a/lab8/kernel/include/syscall.h b/lab8/kernel/include/syscall.h new file mode 100644 index 000000000..169552061 --- /dev/null +++ b/lab8/kernel/include/syscall.h @@ -0,0 +1,34 @@ +#ifndef _SYSCALL_H_ +#define _SYSCALL_H_ + +#include "exception.h" +#include "stddef.h" + +int getpid(trapframe_t *tpf); +size_t uartread(trapframe_t *tpf, char buf[], size_t size); +size_t uartwrite(trapframe_t *tpf, const char buf[], size_t size); +int exec(trapframe_t *tpf, const char *name, char *const argv[]); +int fork(trapframe_t *tpf); +void exit(trapframe_t *tpf, int status); +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox); +void kill(trapframe_t *tpf, int pid); +void signal_register(int signal, void (*handler)()); +void signal_kill(int pid, int signal); +void* mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset); +void sigreturn(trapframe_t *tpf); + +int open(trapframe_t *tpf, const char *pathname, int flags); +int close(trapframe_t *tpf, int fd); +long write(trapframe_t *tpf, int fd, const void *buf, unsigned long count); +long read(trapframe_t *tpf, int fd, void *buf, unsigned long count); +int mkdir(trapframe_t *tpf, const char *pathname, unsigned mode); +int mount(trapframe_t *tpf, const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); +int chdir(trapframe_t *tpf, const char *path); +long lseek64(trapframe_t *tpf, int fd, long offset, int whence); +int ioctl(trapframe_t *tpf, int fd, unsigned long request, void *info); +int sync(trapframe_t *tpf); + +unsigned int get_file_size(char *thefilepath); +char *get_file_start(char *thefilepath); + +#endif /* _SYSCALL_H_*/ diff --git a/lab8/kernel/include/timer.h b/lab8/kernel/include/timer.h new file mode 100644 index 000000000..894a23b9c --- /dev/null +++ b/lab8/kernel/include/timer.h @@ -0,0 +1,29 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "list.h" +//https://github.com/Tekki/raspberrypi-documentation/blob/master/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf p13 +#define CORE0_TIMER_IRQ_CTRL PHYS_TO_VIRT(0x40000040) + +typedef struct timer_event +{ + struct list_head listhead; + unsigned long long interrupt_time; + void *callback; + char *args; +} timer_event_t; + +extern struct list_head *timer_event_list; + +void core_timer_enable(); +void core_timer_disable(); +void core_timer_handler(); + +void add_timer(void *callback, unsigned long long timeout, char *args, int bytick); +unsigned long long get_tick_plus_s(unsigned long long second); +void set_core_timer_interrupt(unsigned long long expired_time); +void set_core_timer_interrupt_by_tick(unsigned long long tick); +void timer_list_init(); +int timer_list_get_size(); + +#endif /* _TIMER_H_ */ diff --git a/lab8/kernel/include/tmpfs.h b/lab8/kernel/include/tmpfs.h new file mode 100644 index 000000000..50367d448 --- /dev/null +++ b/lab8/kernel/include/tmpfs.h @@ -0,0 +1,38 @@ +#ifndef _TMPFS_H_ +#define _TMPFS_H_ + +#include "stddef.h" +#include "vfs.h" + +// SPEC basic Note #3 +#define MAX_FILE_NAME 15 +#define MAX_DIR_ENTRY 16 +#define MAX_FILE_SIZE 4096 + +struct tmpfs_inode +{ + enum fsnode_type type; + char name[MAX_FILE_NAME]; + struct vnode *entry[MAX_DIR_ENTRY]; + char *data; + size_t datasize; +}; + +int register_tmpfs(); +int tmpfs_setup_mount(struct filesystem *fs, struct mount *_mount); + +int tmpfs_write(struct file *file, const void *buf, size_t len); +int tmpfs_read(struct file *file, void *buf, size_t len); +int tmpfs_open(struct vnode *file_node, struct file **target); +int tmpfs_close(struct file *file); +long tmpfs_lseek64(struct file *file, long offset, int whence); +long tmpfs_getsize(struct vnode *vd); + +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name); +int tmpfs_sync(struct filesystem *fs); + +struct vnode *tmpfs_create_vnode(struct mount *_mount, enum fsnode_type type); + +#endif /* _TMPFS_H_ */ diff --git a/lab8/kernel/include/uart1.h b/lab8/kernel/include/uart1.h new file mode 100644 index 000000000..933b9c816 --- /dev/null +++ b/lab8/kernel/include/uart1.h @@ -0,0 +1,18 @@ +#ifndef _UART1_H_ +#define _UART1_H_ + +void uart_init(); +char uart_recv(); +void uart_send(char c); +int uart_sendline(char* fmt, ...); +char uart_async_getc(); +void uart_async_putc(char c); +int uart_puts(char* fmt, ...); +void uart_2hex(unsigned int d); + +void uart_interrupt_enable(); +void uart_interrupt_disable(); +void uart_r_irq_handler(); +void uart_w_irq_handler(); + +#endif /*_UART1_H_*/ diff --git a/lab8/kernel/include/vfs.h b/lab8/kernel/include/vfs.h new file mode 100644 index 000000000..aa3f78030 --- /dev/null +++ b/lab8/kernel/include/vfs.h @@ -0,0 +1,91 @@ +#ifndef _VFS_H_ +#define _VFS_H_ + +#include "stddef.h" + +#define MAX_PATH_NAME 255 +#define MAX_FD 16 +#define O_CREAT 00000100 +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#define MAX_FS_REG 0x50 +#define MAX_DEV_REG 0x10 + +enum fsnode_type +{ + dir_t, + file_t +}; + +struct vnode +{ + struct mount *mount; // Superblock : represents mounted fs + struct vnode_operations *v_ops; // inode & dentry Ops: represents kernel methods for vnode + struct file_operations *f_ops; // file Ops : represents process methods for opened file + void *internal; // vnode itself : directly point to fs's vnode + struct vnode *parent; +}; + +// file handle +struct file +{ + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +}; + +struct mount +{ + struct vnode *root; + struct filesystem *fs; +}; + +struct filesystem +{ + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); + int (*sync)(struct filesystem *fs); +}; + +struct file_operations +{ + int (*write)(struct file *file, const void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + long (*lseek64)(struct file *file, long offset, int whence); + long (*getsize)(struct vnode *vd); +}; + +struct vnode_operations +{ + int (*lookup)(struct vnode *dir_node, struct vnode **target, const char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, const char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, const char *component_name); + int (*isdir)(struct vnode *dir_node); + int (*getname)(struct vnode *dir_node, const char **name); + int (*getsize)(struct vnode *dir_node); +}; + +extern struct filesystem reg_fs[MAX_FS_REG]; + +int register_filesystem(struct filesystem *fs); +int register_dev(struct file_operations* fo); +struct filesystem *find_filesystem(const char *fs_name); +int vfs_open(const char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, const void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); +int vfs_mkdir(const char *pathname); +int vfs_mount(const char *target, const char *filesystem); +int vfs_lookup(const char *pathname, struct vnode **target); +int vfs_mknod(char* pathname, int id); +int vfs_sync(struct filesystem *fs); + +void init_rootfs(); +void vfs_test(); +char* get_absolute_path(char* path,char* curr_working_dir); + +#endif /* _VFS_H_ */ diff --git a/lab8/kernel/initramfs.cpio b/lab8/kernel/initramfs.cpio new file mode 100644 index 000000000..08fa91452 Binary files /dev/null and b/lab8/kernel/initramfs.cpio differ diff --git a/lab8/kernel/kernel8.img b/lab8/kernel/kernel8.img new file mode 100644 index 000000000..a0d30074c Binary files /dev/null and b/lab8/kernel/kernel8.img differ diff --git a/lab8/kernel/makefile b/lab8/kernel/makefile new file mode 100644 index 000000000..3de158103 --- /dev/null +++ b/lab8/kernel/makefile @@ -0,0 +1,56 @@ +ARMGNU ?= aarch64-linux-gnu + +CFLAGS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude -mgeneral-regs-only +ASMFLAGS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src +#--------------------------------------------------------------------------------------- + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(ARMGNU)-gcc $(CFLAGS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMFLAGS) -MMD -c $< -o $@ + +kernel8.img: $(SRC_DIR)/link.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/link.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel8.elf -O binary kernel8.img + +all: kernel8.img + +clean: + rm -rf $(BUILD_DIR) *.img + +run: + cp -f ../create_diskimg/sdcard.img . + qemu-system-aarch64 -M raspi3b \ + -display none \ + -kernel kernel8.img \ + -serial null -serial stdio \ + -initrd initramfs.cpio \ + -drive if=sd,file=sdcard.img,format=raw \ + -dtb bcm2710-rpi-3-b-plus.dtb + +run_display: + qemu-system-aarch64 -M raspi3b -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio -dtb bcm2710-rpi-3-b-plus.dtb + +debug: + cp -f ../create_diskimg/sdcard.img . + qemu-system-aarch64 -M raspi3b \ + -display none \ + -kernel kernel8.img \ + -serial null -serial stdio \ + -initrd initfs.gz \ + -drive if=sd,file=sdcard.img,format=raw \ + -dtb bcm2710-rpi-3-b-plus.dtb -S -s + diff --git a/lab8/kernel/src/boot.S b/lab8/kernel/src/boot.S new file mode 100644 index 000000000..06acf6ee9 --- /dev/null +++ b/lab8/kernel/src/boot.S @@ -0,0 +1,83 @@ +#include "mmu.h" + +.section ".text.boot" + +.global _start + +_start: + // Switch from EL2 to EL1 . + bl from_el2_to_el1 + +set_mmu_configuration: + // set paging configuration (up : 0xffff000000000000 low : 0x0000000000000000) + ldr x4, = TCR_CONFIG_DEFAULT + msr tcr_el1, x4 + + // Set Used Memory Attributes + ldr x4, =((MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8))) + msr mair_el1, x4 + + // set and enable MMU + ldr x4, = MMU_PGD_ADDR // PGD's page frame at 0x1000 + ldr x1, = MMU_PUD_ADDR // PUD's page frame at 0x2000 + + ldr x2, = BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x4] // [MMU_PGD_ADDR] = MMU_PUD_ADDR | BOOT_PGD_ATTR + + ldr x2, = BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 // [MMU_PUD_ADDR] = 0x00000000 | BOOT_PUD_ATTR + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 // [MMU_PUD_ADDR + 8] = 0x40000000 | BOOT_PUD_ATTR + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x4 // load PGD to the bottom translation-based register. + msr ttbr1_el1, x4 // also load PGD to the upper translation based register. + + mov sp, 0x3c000000 // temp stack + bl set_2M_kernel_mmu + + mrs x2, sctlr_el1 // sctlr_el1: Provides top level control of the system, including its memory system, at EL1 and EL0. + orr x2 , x2, 1 // sctlr_el1[0]: EL1&0 stage 1 address translation enabled/disabled. + msr sctlr_el1, x2 + + // indirect branch to the upper virtual address + ldr x2, =set_exception_vector_table + br x2 + +set_exception_vector_table: + adr x1, exception_vector_table + msr vbar_el1, x1 + +setup_stack: + // set top of stack at 0xffff00003c000000 (last usable memory) + ldr x3, =_stack_top + mov sp, x3 + +setup_bss: + ldr x1, =__bss_start + ldr w2, =__bss_size +init_bss: + cbz w2, run_main + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, init_bss + +run_main: + ldr x1, =dtb_ptr + str x0, [x1], #8 + bl main // branch and update lr with "main" + +proc_hang: + wfe // waiting in low-power state + b proc_hang + +from_el2_to_el1: + mov x1, (1 << 31) // hcr_el2: Execution state control for EL2 + msr hcr_el2, x1 // RW[31]: 0b1 The processor execution environment for EL1 is AArch64 + mov x1, 0x3c5 // spsr_el2: Holds the saved process state when an exception is taken to EL2. + msr spsr_el2, x1 // (1)[1111] 00 (2)[0101] -> (1) EL2-PSTATE.DAIF Disabled (2) Exception level = EL1h + msr elr_el2, lr // elr_el2: When taking an exception to EL2, holds the address to return to. + eret // eret: Perform an exception return. EL2 -> EL1 diff --git a/lab8/kernel/src/cpio.c b/lab8/kernel/src/cpio.c new file mode 100644 index 000000000..352dee86f --- /dev/null +++ b/lab8/kernel/src/cpio.c @@ -0,0 +1,45 @@ +#include "cpio.h" +#include "dtb.h" +#include "string.h" + +void* CPIO_DEFAULT_START; +void* CPIO_DEFAULT_END; + +static unsigned int parse_hex_str(char *s, unsigned int max_len) +{ + unsigned int r = 0; + for (unsigned int i = 0; i < max_len; i++) { + r *= 16; + if (s[i] >= '0' && s[i] <= '9') r += s[i] - '0'; + else if (s[i] >= 'a' && s[i] <= 'f') r += s[i] - 'a' + 10; + else if (s[i] >= 'A' && s[i] <= 'F') r += s[i] - 'A' + 10; + else return r; + } + return r; +} + +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) +{ + if (strncmp(this_header_pointer->c_magic, CPIO_NEWC_HEADER_MAGIC, sizeof(this_header_pointer->c_magic)) != 0) return -1; + + *filesize = parse_hex_str(this_header_pointer->c_filesize,8); + *pathname = ((char *)this_header_pointer) + sizeof(struct cpio_newc_header); + + unsigned int pathname_length = parse_hex_str(this_header_pointer->c_namesize,8); + unsigned int offset = pathname_length + sizeof(struct cpio_newc_header); + offset = offset % 4 == 0 ? offset:(offset+4-offset%4); + *data = (char *)this_header_pointer+offset; + + if(*filesize==0) + { + *next_header_pointer = (struct cpio_newc_header*)*data; + } + else + { + offset = *filesize; + *next_header_pointer = (struct cpio_newc_header*)(*data + (offset%4==0?offset:(offset+4-offset%4))); + } + if(strncmp(*pathname,"TRAILER!!!", sizeof("TRAILER!!!"))==0) *next_header_pointer = 0; + + return 0; +} diff --git a/lab8/kernel/src/dev_framebuffer.c b/lab8/kernel/src/dev_framebuffer.c new file mode 100644 index 000000000..9385d0fdf --- /dev/null +++ b/lab8/kernel/src/dev_framebuffer.c @@ -0,0 +1,124 @@ +#include "vfs.h" +#include "dev_framebuffer.h" +#include "uart1.h" +#include "memory.h" +#include "mbox.h" +#include "string.h" +#include "exception.h" + +#define MBOX_CH_PROP 8 + +//The following code is for mailbox initialize used in lab7. +unsigned int width, height, pitch, isrgb; /* dimensions and channel order */ +unsigned char *lfb; /* raw frame buffer address */ + +struct file_operations dev_framebuffer_operations = {dev_framebuffer_write, (void *)dev_framebuffer_op_deny, dev_framebuffer_open, dev_framebuffer_close, dev_framebuffer_lseek64, (void *)dev_framebuffer_op_deny}; + +int init_dev_framebuffer() +{ + //The following code is for mailbox initialize used in lab7. + pt[0] = 35 * 4; + pt[1] = MBOX_TAG_REQUEST_CODE; + + pt[2] = 0x48003; // set phy wh + pt[3] = 8; + pt[4] = 8; + pt[5] = 1024; // FrameBufferInfo.width + pt[6] = 768; // FrameBufferInfo.height + + pt[7] = 0x48004; // set virt wh + pt[8] = 8; + pt[9] = 8; + pt[10] = 1024; // FrameBufferInfo.virtual_width + pt[11] = 768; // FrameBufferInfo.virtual_height + + pt[12] = 0x48009; // set virt offset + pt[13] = 8; + pt[14] = 8; + pt[15] = 0; // FrameBufferInfo.x_offset + pt[16] = 0; // FrameBufferInfo.y.offset + + pt[17] = 0x48005; // set depth + pt[18] = 4; + pt[19] = 4; + pt[20] = 32; // FrameBufferInfo.depth + + pt[21] = 0x48006; // set pixel order + pt[22] = 4; + pt[23] = 4; + pt[24] = 1; // RGB, not BGR preferably + + pt[25] = 0x40001; // get framebuffer, gets alignment on request + pt[26] = 8; + pt[27] = 8; + pt[28] = 4096; // FrameBufferInfo.pointer + pt[29] = 0; // FrameBufferInfo.size + + pt[30] = 0x40008; // get pitch + pt[31] = 4; + pt[32] = 4; + pt[33] = 0; // FrameBufferInfo.pitch + + pt[34] = MBOX_TAG_LAST_BYTE; + + // this might not return exactly what we asked for, could be + // the closest supported resolution instead + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)) && pt[20] == 32 && pt[28] != 0) + { + pt[28] &= 0x3FFFFFFF; // convert GPU address to ARM address + width = pt[5]; // get actual physical width + height = pt[6]; // get actual physical height + pitch = pt[33]; // get number of bytes per line + isrgb = pt[24]; // get the actual channel order + lfb = PHYS_TO_VIRT((void *)((unsigned long)pt[28])); + } + else + { + uart_puts("Unable to set screen resolution to 1024x768x32\n"); + } + + return register_dev(&dev_framebuffer_operations); +} + +int dev_framebuffer_write(struct file *file, const void *buf, size_t len) +{ + lock(); + if (len + file->f_pos > pitch * height) + { + uart_puts("????\r\n"); + len = pitch * height - file->f_pos; + } + memcpy(lfb + file->f_pos, buf, len); + file->f_pos += len; + unlock(); + return len; +} + +int dev_framebuffer_open(struct vnode *file_node, struct file **target) +{ + (*target)->f_pos = 0; + (*target)->vnode = file_node; + (*target)->f_ops = &dev_framebuffer_operations; + return 0; +} + +int dev_framebuffer_close(struct file *file) +{ + kfree(file); + return 0; +} + +long dev_framebuffer_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +int dev_framebuffer_op_deny() +{ + return -1; +} diff --git a/lab8/kernel/src/dev_uart.c b/lab8/kernel/src/dev_uart.c new file mode 100644 index 000000000..2aade4967 --- /dev/null +++ b/lab8/kernel/src/dev_uart.c @@ -0,0 +1,49 @@ +#include "vfs.h" +#include "dev_uart.h" +#include "uart1.h" +#include "memory.h" + +struct file_operations dev_file_operations = {dev_uart_write, dev_uart_read, dev_uart_open, dev_uart_close, (void *)dev_uart_op_deny, (void *)dev_uart_op_deny}; + +int init_dev_uart() +{ + return register_dev(&dev_file_operations); +} + +int dev_uart_write(struct file *file, const void *buf, size_t len) +{ + const char *cbuf = buf; + for (int i = 0; i < len;i++) + { + uart_async_putc(cbuf[i]); + } + return len; +} + +int dev_uart_read(struct file *file, void *buf, size_t len) +{ + char *cbuf = buf; + for (int i = 0; i < len; i++) + { + cbuf[i] = uart_async_getc(); + } + return len; +} + +int dev_uart_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = &dev_file_operations; + return 0; +} + +int dev_uart_close(struct file *file) +{ + kfree(file); + return 0; +} + +int dev_uart_op_deny() +{ + return -1; +} diff --git a/lab8/kernel/src/dtb.c b/lab8/kernel/src/dtb.c new file mode 100644 index 000000000..57d6eb9f3 --- /dev/null +++ b/lab8/kernel/src/dtb.c @@ -0,0 +1,152 @@ +#include "bcm2837/rpi_mmu.h" +#include "dtb.h" +#include "uart1.h" +#include "string.h" +#include "cpio.h" +#include "memory.h" + +char* dtb_ptr; + +//stored as big endian +struct fdt_header { + uint32_t magic; + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +}; + +// memory reservation block - https://zhuanlan.zhihu.com/p/144863497 +struct fdt_reserve_entry { + uint64_t address; + uint64_t size; +}; + +uint32_t uint32_endian_big2lttle(uint32_t data) +{ + char* r = (char*)&data; + return (r[3]<<0) | (r[2]<<8) | (r[1]<<16) | (r[0]<<24); +} + +uint64_t uint64_endian_big2lttle(uint64_t data) +{ + char *r = (char *)&data; + return ((unsigned long long)r[7] << 0) | ((unsigned long long)r[6] << 8) | ((unsigned long long)r[5] << 16) | ((unsigned long long)r[4] << 24) | ((unsigned long long)r[3] << 32) | ((unsigned long long)r[2] << 40) | ((unsigned long long)r[1] << 48) | ((unsigned long long)r[0] << 56); +} + +void traverse_device_tree(void *dtb_ptr, dtb_callback callback) +{ + struct fdt_header* header = dtb_ptr; + if(uint32_endian_big2lttle(header->magic) != 0xD00DFEED) + { + uart_puts("traverse_device_tree : wrong magic in traverse_device_tree"); + return; + } + // https://abcamus.github.io/2016/12/28/uboot%E8%AE%BE%E5%A4%87%E6%A0%91-%E8%A7%A3%E6%9E%90%E8%BF%87%E7%A8%8B/ + // https://blog.csdn.net/wangdapao12138/article/details/82934127 + uint32_t struct_size = uint32_endian_big2lttle(header->size_dt_struct); + char* dt_struct_ptr = (char*)((char*)header + uint32_endian_big2lttle(header->off_dt_struct)); + char* dt_strings_ptr = (char*)((char*)header + uint32_endian_big2lttle(header->off_dt_strings)); + + char* end = (char*)dt_struct_ptr + struct_size; + char* pointer = dt_struct_ptr; + + while(pointer < end) + { + uint32_t token_type = uint32_endian_big2lttle(*(uint32_t*)pointer); + + pointer += 4; + if(token_type == FDT_BEGIN_NODE) + { + callback(token_type, pointer, 0, 0); + pointer += strlen(pointer); + pointer += 4 - (unsigned long long) pointer % 4; //alignment 4 byte + }else if(token_type == FDT_END_NODE) + { + callback(token_type, 0, 0, 0); + }else if(token_type == FDT_PROP) + { + uint32_t len = uint32_endian_big2lttle(*(uint32_t*)pointer); + pointer += 4; + char* name = (char*)dt_strings_ptr + uint32_endian_big2lttle(*(uint32_t*)pointer); + pointer += 4; + callback(token_type, name, pointer, len); + pointer += len; + if((unsigned long long)pointer % 4 != 0) pointer += 4 - (unsigned long long)pointer%4; //alignment 4 byte + }else if(token_type == FDT_NOP) + { + callback(token_type, 0, 0, 0); + }else if(token_type == FDT_END) + { + callback(token_type, 0, 0, 0); + }else + { + uart_puts("error type:%x\n",token_type); + return; + } + } +} + +void dtb_callback_show_tree(uint32_t node_type, char *name, void *data, uint32_t name_size) +{ + static int level = 0; + if(node_type==FDT_BEGIN_NODE) + { + for(int i=0;imagic) != 0xD00DFEED) + { + uart_puts("traverse_device_tree : wrong magic in traverse_device_tree"); + return; + } + + // off_mem_rsvmap stores all of reserve memory map with address and size + char *dt_mem_rsvmap_ptr = (char *)((char *)header + uint32_endian_big2lttle(header->off_mem_rsvmap)); + struct fdt_reserve_entry *reverse_entry = (struct fdt_reserve_entry *)dt_mem_rsvmap_ptr; + + // reserve memory which is defined by dtb + while (reverse_entry->address != 0 || reverse_entry->size != 0) + { + unsigned long long start = PHYS_TO_VIRT(uint64_endian_big2lttle(reverse_entry->address)); + unsigned long long end = uint64_endian_big2lttle(reverse_entry->size) + start; + memory_reserve(start, end); + reverse_entry++; + } + + // reserve device tree itself + memory_reserve((unsigned long long)dtb_ptr, (unsigned long long)dtb_ptr + uint32_endian_big2lttle(header->totalsize)); +} diff --git a/lab8/kernel/src/entry.S b/lab8/kernel/src/entry.S new file mode 100644 index 000000000..76d4126d6 --- /dev/null +++ b/lab8/kernel/src/entry.S @@ -0,0 +1,196 @@ +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + stp x0, x1, [sp ,16 * 0] // store pair of registers + 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] + // information for nested interrupt + mrs x0, spsr_el1 + str x0, [sp, 16 * 15 + 8] + mrs x0, elr_el1 + str x0, [sp, 16 * 16] + mrs x0, sp_el0 + str x0, [sp, 16 * 16 + 8] + ldp x0, x1, [sp, 16 * 0] +.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] + // information for nested interrupt + ldr x0, [sp, 16 * 15 + 8] + msr spsr_el1,x0 + ldr x0, [sp, 16 * 16] + msr elr_el1, x0 + ldr x0, [sp, 16 * 16 + 8] + msr sp_el0, x0 + ldp x0, x1, [sp ,16 * 0] // restore x0 instead of elr_el1 + add sp, sp, 32 * 9 +.endm + + +.macro ventry label + .align 7 // entry should be aligned to 0x80 (2^7) + b \label +.endm + +.align 11 // vector table should be aligned to 0x800 (2^11) +.global exception_vector_table + +// exception_vector_table definition +// https://github.com/torvalds/linux/blob/v4.14/arch/arm64/kernel/entry.S#L367 +exception_vector_table: + //Exception from the current EL while using SP_EL0 + ventry el1t_sync_invalid // Synchronous EL1t + ventry el1t_irq_invalid // IRQ EL1t + ventry el1t_fiq_invalid // FIQ EL1t + ventry el1t_error_invalid // Error EL1t + + //Exception from the current EL while using SP_ELx + ventry el1h_sync // Synchronous EL1h + ventry el1h_irq // IRQ EL1h + ventry el1h_fiq_invalid // FIQ EL1h + ventry el1h_error_invalid // Error EL1h + + //Exception from a lower EL and at least one lower EL is AArch64 + ventry el0_sync_64 // Synchronous 64-bit EL0 + ventry el0_irq_64 // IRQ 64-bit EL0 + ventry el0_fiq_invalid_64 // FIQ 64-bit EL0 + ventry el0_error_invalid_64 // Error 64-bit EL0 + + //Exception from a lower EL and at least all lower EL are AArch32 + ventry el0_sync_invalid_32 // Synchronous 32-bit EL0 + ventry el0_irq_invalid_32 // IRQ 32-bit EL0 + ventry el0_fiq_invalid_32 // FIQ 32-bit EL0 + ventry el0_error_invalid_32 // Error 32-bit EL0 + + +el1t_sync_invalid: + save_all + mov x0,0 + bl invalid_exception_router + load_all + eret +el1t_irq_invalid: + save_all + mov x0,1 + bl invalid_exception_router + load_all + eret +el1t_fiq_invalid: + save_all + mov x0,2 + bl invalid_exception_router + load_all + eret +el1t_error_invalid: + save_all + mov x0,3 + bl invalid_exception_router + load_all + eret + + +el1h_sync: + save_all + mov x0,4 + bl invalid_exception_router + load_all + eret +el1h_irq: + save_all + mov x0, sp // trapframe + bl irq_router + load_all + eret +el1h_fiq_invalid: + save_all + mov x0,6 + bl invalid_exception_router + load_all + eret +el1h_error_invalid: + save_all + mov x0,7 + bl invalid_exception_router + load_all + eret + +el0_sync_64: + save_all + mov x0, sp // trapframe + bl sync_64_router + load_all + eret +el0_irq_64: + save_all + mov x0, sp // trapframe + bl irq_router + load_all + eret +el0_fiq_invalid_64: + save_all + mov x0,10 + bl invalid_exception_router + load_all + eret +el0_error_invalid_64: + save_all + mov x0,11 + bl invalid_exception_router + load_all + eret + + +el0_sync_invalid_32: + save_all + mov x0,12 + bl invalid_exception_router + load_all + eret +el0_irq_invalid_32: + save_all + mov x0,13 + bl invalid_exception_router + load_all + eret +el0_fiq_invalid_32: + save_all + mov x0,14 + bl invalid_exception_router + load_all + eret +el0_error_invalid_32: + save_all + mov x0,15 + bl invalid_exception_router + load_all + eret diff --git a/lab8/kernel/src/exception.c b/lab8/kernel/src/exception.c new file mode 100644 index 000000000..4b80e0825 --- /dev/null +++ b/lab8/kernel/src/exception.c @@ -0,0 +1,93 @@ +#include "bcm2837/rpi_uart1.h" +#include "uart1.h" +#include "exception.h" +#include "timer.h" +#include "irqtask.h" +#include "syscall.h" +#include "sched.h" +#include "signal.h" +#include "mmu.h" + +void sync_64_router(trapframe_t* tpf) +{ + unsigned long long esr_el1; + __asm__ __volatile__("mrs %0, esr_el1\n\t": "=r"(esr_el1)); + // esr_el1: Holds syndrome information for an exception taken to EL1. + esr_el1_t *esr = (esr_el1_t *)&esr_el1; + if (esr->ec == MEMFAIL_DATA_ABORT_LOWER || esr->ec == MEMFAIL_INST_ABORT_LOWER) + { + mmu_memfail_abort_handle(esr); + return; + } + + el1_interrupt_enable(); + unsigned long long syscall_no = tpf->x8; + if (syscall_no == 0) { getpid(tpf); } + else if(syscall_no == 1) { uartread(tpf,(char *) tpf->x0, tpf->x1); } + else if (syscall_no == 2) { uartwrite(tpf,(char *) tpf->x0, tpf->x1); } + else if (syscall_no == 3) { exec(tpf,(char *) tpf->x0, (char **)tpf->x1); } + else if (syscall_no == 4) { fork(tpf); } + else if (syscall_no == 5) { exit(tpf,tpf->x0); } + else if (syscall_no == 6) { syscall_mbox_call(tpf,(unsigned char)tpf->x0, (unsigned int *)tpf->x1); } + else if (syscall_no == 7) { kill(tpf, (int)tpf->x0); } + else if (syscall_no == 8) { signal_register(tpf->x0, (void (*)())tpf->x1); } + else if (syscall_no == 9) { signal_kill(tpf->x0, tpf->x1); } + else if (syscall_no == 10) { mmap(tpf,(void *)tpf->x0,tpf->x1,tpf->x2,tpf->x3,tpf->x4,tpf->x5); } + else if (syscall_no == 11) { open(tpf, (char*)tpf->x0, tpf->x1); } + else if (syscall_no == 12) { close(tpf, tpf->x0); } + else if (syscall_no == 13) { write(tpf, tpf->x0, (char *)tpf->x1, tpf->x2); } + else if (syscall_no == 14) { read(tpf, tpf->x0, (char *)tpf->x1, tpf->x2); } + else if (syscall_no == 15) { mkdir(tpf, (char *)tpf->x0, tpf->x1); } + else if (syscall_no == 16) { mount(tpf, (char *)tpf->x0, (char *)tpf->x1, (char *)tpf->x2, tpf->x3, (void*)tpf->x4); } + else if (syscall_no == 17) { chdir(tpf, (char *)tpf->x0); } + else if (syscall_no == 18) { lseek64(tpf, tpf->x0, tpf->x1, tpf->x2); } + else if (syscall_no == 19) { ioctl(tpf, tpf->x0, tpf->x1, (void*)tpf->x2); } + else if (syscall_no == 20) { sync(tpf); } + else if (syscall_no == 50) { sigreturn(tpf); } + el1_interrupt_disable(); +} + +void irq_router(trapframe_t* tpf) +{ + if (*IRQ_PENDING_1 & IRQ_PENDING_1_AUX_INT && *CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_GPU) { + if (*AUX_MU_IIR_REG & (1 << 1)) // can write + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + irqtask_add(uart_w_irq_handler, UART_IRQ_PRIORITY); + irqtask_run_preemptive(); + } + else if (*AUX_MU_IIR_REG & (0b10 << 1)) // can read + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + irqtask_add(uart_r_irq_handler, UART_IRQ_PRIORITY); + irqtask_run_preemptive(); + } + } else if(*CORE0_INTERRUPT_SOURCE & INTERRUPT_SOURCE_CNTPNSIRQ) { + core_timer_disable(); + irqtask_add(core_timer_handler, TIMER_IRQ_PRIORITY); + irqtask_run_preemptive(); + core_timer_enable(); + el1_interrupt_disable(); + if (run_queue->next->next != run_queue) schedule(); + } + if ((tpf->spsr_el1 & 0b1100) == 0) { check_signal(tpf); } + el1_interrupt_disable(); +} + +void invalid_exception_router(unsigned long long x0){ + // TBD +} + +static unsigned long long lock_count = 0; +void lock() +{ + el1_interrupt_disable(); + lock_count++; +} + +void unlock() +{ + lock_count--; + if (lock_count == 0) + el1_interrupt_enable(); +} diff --git a/lab8/kernel/src/fat32.c b/lab8/kernel/src/fat32.c new file mode 100644 index 000000000..cd26a5ce3 --- /dev/null +++ b/lab8/kernel/src/fat32.c @@ -0,0 +1,1309 @@ +#include "fat32.h" +#include "memory.h" +#include "sdhost.h" +#include "string.h" +#include "list.h" +#include +#include "uart1.h" +#include "exception.h" + +struct list_head mounts; // Store all new mountpoints that probably need to be sync'd + +struct vnode_operations fat32_v_ops = { + fat32_lookup, fat32_create, fat32_mkdir, + fat32_isdir, fat32_getname, fat32_getsize +}; + +struct file_operations fat32_f_ops = { + fat32_write, fat32_read, fat32_open, + fat32_close, fat32_lseek64, +}; + +int register_fat32() +{ + struct filesystem fs; + fs.name = "fat32"; + fs.setup_mount = fat32_mount; + fs.sync = fat32_sync; + INIT_LIST_HEAD(&mounts); + return register_filesystem(&fs); +} + +/* filesystem methods */ +int fat32_mount(struct filesystem *fs, struct mount *mount) +{ + struct partition_t *partition; + struct fat_info_t *fat; + struct fat_dir_t *dir; + struct fat32_inode *data; + struct vnode *oldnode, *node; + struct fat_mount_t *newmount; + unsigned int lba; + unsigned char buf[BLOCK_SIZE]; + + // Read partition table from MBR, which is at first block + readblock(0, buf); + // MBR format + // 000 ~ 1BD: Code area + // 1BE ~ 1FD: Master Partition Table + // 1FE ~ 1FF: Boot Record Signature + + // https://lexra.pixnet.net/blog/post/303910876 + partition = (struct partition_t *)&buf[0x1be]; + // check Boot Record Signature, constants + if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) + { + return -1; + } + // https://en.wikipedia.org/wiki/Partition_type + if (partition[0].type != 0xb && partition[0].type != 0xc) // check Partition type, only FAT32 + { + return -1; + } + lba = partition[0].lba; + readblock(partition[0].lba, buf); + + node = kmalloc(sizeof(struct vnode)); + data = kmalloc(sizeof(struct fat32_inode)); + fat = kmalloc(sizeof(struct fat_info_t)); + dir = kmalloc(sizeof(struct fat_dir_t)); + newmount = kmalloc(sizeof(struct fat_mount_t)); + + memcpy((void *)&fat->bs, (void *)buf, sizeof(fat->bs)); + + // According to FAT fs design, FAT Region is after reserved sectors + // https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system + fat->fat_lba = lba + fat->bs.reserved_sector_cnt; + fat->cluster_lba = fat->fat_lba + fat->bs.fat_cnt * fat->bs.sector_per_fat32; + + INIT_LIST_HEAD(&dir->list); + + // oldnode should attach mount the fs + oldnode = mount->root; + + node->mount = oldnode->mount; + node->v_ops = oldnode->v_ops; + node->f_ops = oldnode->f_ops; + node->parent = oldnode->parent; + node->internal = oldnode->internal; + + data->node = node; + data->fat = fat; + data->cid = 2; // In FAT32, Root Directory Table starts from cluster #2 + data->type = FAT_DIR; + data->dir = dir; + + oldnode->mount = mount; + oldnode->v_ops = &fat32_v_ops; + oldnode->f_ops = &fat32_f_ops; + oldnode->internal = data; + + lock(); + list_add(&newmount->list, &mounts); + unlock(); + newmount->mount = mount; + + return 0; +} + +int fat32_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) return -1; + if (!_lookup_cache(dir_node, target, component_name)) return 0; + return _lookup_fat32(dir_node, target, component_name); +} + +int fat32_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) return -1; + if (!fat32_lookup(dir_node, target, component_name)) return -1; + *target = _create_vnode(dir_node, component_name, FAT_FILE, -1, 0); + return 0; +} + +int fat32_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + if (((struct fat32_inode *)dir_node->internal)->type != FAT_DIR) return -1; + if (!fat32_lookup(dir_node, target, component_name)) return -1; + *target = _create_vnode(dir_node, component_name, FAT_DIR, -1, 0); + return 0; +} + +int fat32_isdir(struct vnode *dir_node) +{ + return (((struct fat32_inode *)dir_node->internal)->type == FAT_DIR) ? 1 : 0; +} + +int fat32_getname(struct vnode *dir_node, const char **name) +{ + *name = ((struct fat32_inode *)dir_node->internal)->name; + return 0; +} + +int fat32_getsize(struct vnode *dir_node) +{ + if (((struct fat32_inode *)dir_node->internal)->type == FAT_DIR) return 0; + return ((struct fat32_inode *)dir_node->internal)->file->size; +} + +/* file_operations methods */ +int fat32_write(struct file *file, const void *buf, size_t len) +{ + struct fat32_inode *data; + int filesize; + int ret; + + if (fat32_isdir(file->vnode)) return -1; + if (!len) return len; + + filesize = fat32_getsize(file->vnode); + data = file->vnode->internal; + + ret = _writefile(buf, data, file->f_pos, len); + if (ret <= 0) { + return ret; + } + + file->f_pos += ret; + if (file->f_pos > filesize) { + data->file->size = file->f_pos; + } + + return ret; +} + +int fat32_read(struct file *file, void *buf, size_t len) +{ + struct fat32_inode *data; + int filesize; + int ret; + + if (fat32_isdir(file->vnode)) return -1; + + filesize = fat32_getsize(file->vnode); + data = file->vnode->internal; + + if (file->f_pos + len > filesize) { + len = filesize - file->f_pos; + } + if (!len) return len; + + ret = _readfile(buf, data, file->f_pos, len); + if (ret <= 0) return ret; + + file->f_pos += ret; + return ret; +} + +int fat32_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_pos = 0; + (*target)->f_ops = file_node->f_ops; + return 0; +} + +int fat32_close(struct file *file) +{ + file->vnode = NULL; + file->f_pos = 0; + file->f_ops = NULL; + return 0; +} + +long fat32_lseek64(struct file *file, long offset, int whence) +{ + int filesize; + int base; + + if (!fat32_isdir(file->vnode)) return -1; + + filesize = fat32_getsize(file->vnode); + + if (filesize < 0) return -1; + + switch (whence) + { + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = file->f_pos; + break; + case SEEK_END: + base = filesize; + break; + default: + return -1; + } + + if (base + offset > filesize) + { + return -1; + } + file->f_pos = base + offset; + + return 0; +} + +int fat32_sync(struct filesystem *fs) +{ + struct fat_mount_t *entry; + + list_for_each_entry(entry, &mounts, list) + { + _sync_dir(entry->mount->root); + } + + return 0; +} + +// ------------------------------------------------------------------------ +// Utils +// + +unsigned int get_next_cluster(unsigned int fat_lba, unsigned int cluster_id) +{ + struct cluster_entry_t *ce; + unsigned int idx; + unsigned char buf[BLOCK_SIZE]; + + if (cluster_id >= INVALID_CID) + { + return cluster_id; + } + + // Get fat allocation table and its cluster entry in Data region + fat_lba += cluster_id / CLUSTER_ENTRY_PER_BLOCK; + idx = cluster_id % CLUSTER_ENTRY_PER_BLOCK; + + readblock(fat_lba, buf); + ce = &(((struct cluster_entry_t *)buf)[idx]); + + return ce->val; +} + +unsigned int alloc_cluster(struct fat_info_t *fat, unsigned int prev_cid) +{ + struct cluster_entry_t *ce; + unsigned int fat_lba; + unsigned int cid; + int found; + unsigned char buf[BLOCK_SIZE]; + + fat_lba = fat->fat_lba; + cid = 0; + // find unused cluster by clusters -> fats + while (fat_lba < fat->cluster_lba) { + found = 0; + readblock(fat_lba, buf); + + for (int i = 0; i < CLUSTER_ENTRY_PER_BLOCK; ++i) { + ce = &(((struct cluster_entry_t *)buf)[i]); + + if (!ce->val) { + found = 1; + break; + } + + ++cid; + } + + if (found) { + break; + } + fat_lba += 1; + } + // Only called if prev_cid is larger than 0xfffffff8, realloc ce; + if (found && prev_cid) { + unsigned int target_lba; + unsigned int target_idx; + + target_lba = fat_lba + prev_cid / CLUSTER_ENTRY_PER_BLOCK; + target_idx = prev_cid % CLUSTER_ENTRY_PER_BLOCK; + readblock(target_lba, buf); + ce = &(((struct cluster_entry_t *)buf)[target_idx]); + ce->val = cid; + writeblock(target_lba, buf); + } + + if (!found) { + uart_sendline("fat32 alloc_cluster: No space!"); + return -1; + } + + return cid; +} + +/* vnode_operations methods */ +struct vnode *_create_vnode(struct vnode *parent, const char *name, unsigned int type, unsigned int cid, unsigned int size) +{ + struct vnode *node; + struct fat32_inode *info, *data; + char *buf; + int len; + + info = parent->internal; + len = strlen(name); + + buf = kmalloc(len + 1); + node = kmalloc(sizeof(struct vnode)); + data = kmalloc(sizeof(struct fat32_inode)); + + strcpy(buf, name); + + data->name = buf; + data->node = node; + data->fat = info->fat; + data->cid = cid; + data->type = type; + + if (type == FAT_DIR) { + struct fat_dir_t *dir; + dir = kmalloc(sizeof(struct fat_dir_t)); + INIT_LIST_HEAD(&dir->list); + data->dir = dir; + } else { + struct fat_file_t *file; + file = kmalloc(sizeof(struct fat_file_t)); + INIT_LIST_HEAD(&file->list); + file->size = size; + data->file = file; + } + + node->mount = parent->mount; + node->v_ops = &fat32_v_ops; + node->f_ops = &fat32_f_ops; + node->parent = parent; + node->internal = data; + + // attach to parent's dir_t or file_t list + list_add(&data->list, &info->dir->list); + + return node; +} + +int _lookup_cache(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct fat32_inode *data, *entry; + struct fat_dir_t *dir; + int found; + + data = dir_node->internal; + dir = data->dir; + found = 0; + + list_for_each_entry(entry, &dir->list, list) { + if (!strcasecmp(component_name, entry->name)) { // same as strcmp, but tr [:lower:] + found = 1; + break; + } + } + if (!found) { + return -1; + } + *target = entry->node; + return 0; +} + +struct dir_t *__lookup_fat32(struct vnode *dir_node, const char *component_name, unsigned char *buf, int *buflba) +{ + struct dir_t *dir; + struct fat32_inode *data; + struct fat_info_t *fat; + struct filename_t name; + unsigned int cid; + int found, dirend, lfn; + + data = dir_node->internal; + fat = data->fat; + cid = data->cid; + + found = 0; + dirend = 0; + + memset(&name, 0, sizeof(struct filename_t)); + + while (1) { + int lba; + + lba = fat->cluster_lba + (cid - 2) * fat->bs.sector_per_cluster; // cluster start from #2 + readblock(lba, buf); + + // only called by sync, to update buffer + if (buflba) { + *buflba = lba; + } + + for (int i = 0; i < DIR_PER_BLOCK; ++i) { + unsigned char len; + dir = (struct dir_t *)(&buf[sizeof(struct dir_t) * i]); + + if (dir->name[0] == 0) { + dirend = 1; + break; + } + + if ((dir->attr & ATTR_LFN) == ATTR_LFN) { + struct long_dir_t *ldir; + int n; + + lfn = 1; + + ldir = (struct long_dir_t *)dir; + n = (dir->name[0] & 0x3f) - 1; // every lfn entry has 16 bytes + // update filename_t if it is LFN + for (int i = 0; ldir->name1[i] != 0xff && i < 10; i += 2) { + name.part[n].name[i / 2] = ldir->name1[i]; // LDIR_Name1 1th ~ 5th + } + for (int i = 0; ldir->name2[i] != 0xff && i < 12; i += 2) { + name.part[n].name[5 + i / 2] = ldir->name2[i]; // LDIR_Name2 6th ~ 11th + } + for (int i = 0; ldir->name3[i] != 0xff && i < 4; i += 2) { + name.part[n].name[11 + i / 2] = ldir->name3[i]; // LDIR_Name3 12th ~ 13th + } + + continue; + } + + if (lfn == 1) { + if (!strcasecmp(component_name, (void *)name.fullname)) { + found = 1; + break; + } + + lfn = 0; + memset(&name, 0, sizeof(struct filename_t)); + continue; + } + + lfn = 0; + len = 8; + + // SFN lookup + while (len) { + if (dir->name[len - 1] != 0x20) { + break; + } + len -= 1; + } + + memcpy((void *)name.fullname, (void *)dir->name, len); + name.fullname[len] = 0; + + len = 3; + + while (len) { + if (dir->name[8 + len - 1] != 0x20) { + break; + } + len -= 1; + } + + if (len >= 0) { + strcat((void *)name.fullname, "."); + strncat((void *)name.fullname, (void *)&dir->name[8], len); + } + + if (!strcasecmp(component_name, (void *)name.fullname)) { + found = 1; + break; + } + + memset(&name, 0, sizeof(struct filename_t)); + } + + if (found) { + break; + } + + if (dirend) { + break; + } + + // try next cluster and lookup again + cid = get_next_cluster(fat->fat_lba, cid); + + if (cid >= INVALID_CID) { + break; + } + } + + if (!found) { + return NULL; + } + + return dir; +} + +int _lookup_fat32(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct vnode *node; + struct dir_t *dir; + unsigned int type, cid; + unsigned char buf[BLOCK_SIZE]; + + dir = __lookup_fat32(dir_node, component_name, buf, NULL); + + if (!dir) { + return -1; + } + + if (!(dir->attr & ATTR_FILE_DIR_MASK)) { + return -1; + } + + cid = (dir->ch << 16) | dir->cl; // upper part of cluster id + lower part of cluster id + + if (dir->attr & ATTR_ARCHIVE) { + type = FAT_FILE; + } else { + type = FAT_DIR; + } + + node = _create_vnode(dir_node, component_name, type, cid, dir->size); + + *target = node; + + return 0; +} + + +int _writefile_seek_cache(struct fat32_inode *data, unsigned int foid, struct fat_file_block_t **block) +{ + struct fat_file_block_t *entry; + struct list_head *head; + + head = &data->file->list; + + if (list_empty(head)) { + return -1; + } + + list_for_each_entry(entry, head, list) { + *block = entry; + if (foid == entry->oid) { // offset match + return 0; + } + } + + return -1; +} + +int _writefile_seek_fat32(struct fat32_inode *data, unsigned int foid, unsigned int fcid, struct fat_file_block_t **block) +{ + struct fat_info_t *info; + unsigned int curoid, curcid; + + info = data->fat; + + // block is from cache + if (*block) { + curoid = (*block)->oid; + curcid = (*block)->cid; + + if (curoid == foid) { // offset match + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + } else { + curoid = 0; + curcid = fcid; + } + + // create new block in cache + while (1) { + struct fat_file_block_t *newblock; + + newblock = kmalloc(sizeof(struct fat_file_block_t)); + + newblock->oid = curoid; + newblock->cid = curcid; + newblock->bufIsUpdated = 0; + newblock->isDirty = 1; + + list_add_tail(&newblock->list, &data->file->list); + + *block = newblock; + + if (curoid == foid) { + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + } +} + +int _writefile_cache(struct fat32_inode *data, unsigned long long bckoff, const unsigned char *buf, unsigned long long bufoff, unsigned int size, struct fat_file_block_t *block) +{ + int wsize; + + // if cache block is not updated yet, read from sdcard + if (!block->bufIsUpdated) { + // read the data from sdcard + struct fat_info_t *info; + int lba; + + info = data->fat; + lba = info->cluster_lba + (block->cid - 2) * info->bs.sector_per_cluster; + + readblock(lba, block->buf); + + block->bufIsUpdated = 1; + } + + wsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + memcpy((void *)&block->buf[bckoff], (void *)&buf[bufoff], wsize); + + return wsize; +} + +int _writefile_fat32(struct fat32_inode *data, unsigned long long bckoff, const unsigned char *buf, unsigned int bufoff, unsigned int size, unsigned int oid, unsigned int cid) +{ + struct list_head *head; + struct fat_info_t *info; + struct fat_file_block_t *block; + int lba; + int wsize; + + head = &data->file->list; + info = data->fat; + + block = kmalloc(sizeof(struct fat_file_block_t)); + + wsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + // read the address from sdcard and write it + if (cid >= INVALID_CID) { + memset(block->buf, 0, BLOCK_SIZE); + } else { + lba = info->cluster_lba + (cid - 2) * info->bs.sector_per_cluster; + + readblock(lba, block->buf); + } + + memcpy((void *)&block->buf[bckoff], (void *)&buf[bufoff], wsize); + + block->oid = oid; + block->cid = cid; + block->bufIsUpdated = 1; + block->isDirty = 1; // wait for sync + + list_add_tail(&block->list, head); + + return wsize; +} + +int _writefile(const void *buf, struct fat32_inode *data, unsigned long long fileoff, unsigned long long len) +{ + struct fat_file_block_t *block; + struct list_head *head; + unsigned int foid; // first block id + unsigned int coid; // current block id + unsigned int cid; // target cluster id + unsigned long long bufoff, result; + int ret; + + block = NULL; + head = &data->file->list; + foid = fileoff / BLOCK_SIZE; + coid = 0; + cid = data->cid; + bufoff = 0; + result = 0; + + // Seek, find the particular offset + ret = _writefile_seek_cache(data, foid, &block); + + if (ret < 0) { + ret = _writefile_seek_fat32(data, foid, cid, &block); + } + + if (ret < 0) { + return 0; + } + + // Write + while (len) { + unsigned long long bckoff; + + bckoff = (fileoff + result) % BLOCK_SIZE; + + if (&block->list != head) { + // cache has the blocks to be written + ret = _writefile_cache(data, bckoff, buf, bufoff, len, block); + cid = block->cid; + block = list_first_entry(&block->list, struct fat_file_block_t, list); + } else { + // Read block from sdcard, create cache, then write it + cid = get_next_cluster(data->fat->fat_lba, cid); + ret = _writefile_fat32(data, bckoff, buf, bufoff, len, coid, cid); + } + + if (ret < 0) { + break; + } + + bufoff += ret; + result += ret; + coid += 1; + len -= ret; + } + + return result; +} + + +int _readfile_seek_cache(struct fat32_inode *data, unsigned int foid, struct fat_file_block_t **block) +{ + struct fat_file_block_t *entry; + struct list_head *head; + + // check the cache list + head = &data->file->list; + + // if cache is none, return not found + if (list_empty(head)) { + return -1; + } + + // find that offset in cache + list_for_each_entry(entry, head, list) { + *block = entry; + if (foid == entry->oid) { + return 0; + } + } + + return -1; +} + +int _readfile_seek_fat32(struct fat32_inode *data, unsigned int foid, unsigned int fcid, struct fat_file_block_t **block) +{ + struct fat_info_t *info; + unsigned int curoid, curcid; + + info = data->fat; + + // if block found in cache, change to that offset in fat32 + if (*block) { + curoid = (*block)->oid; + curcid = (*block)->cid; + + if (curoid == foid) { + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + + if (curcid >= INVALID_CID) { + return -1; + } + } else { + curoid = 0; + curcid = fcid; + } + + while (1) { + // add new offset block to cache + struct fat_file_block_t *newblock; + + newblock = kmalloc(sizeof(struct fat_file_block_t)); + + newblock->oid = curoid; + newblock->cid = curcid; + newblock->bufIsUpdated = 0; + newblock->isDirty = 0; + + list_add_tail(&newblock->list, &data->file->list); + + *block = newblock; + + if (curoid == foid) { + return 0; + } + + curoid++; + curcid = get_next_cluster(info->fat_lba, curcid); + + if (curcid >= INVALID_CID) { + return -1; + } + } +} + +int _readfile_cache(struct fat32_inode *data, unsigned long long bckoff, unsigned char *buf, unsigned long long bufoff, unsigned int size, struct fat_file_block_t *block) +{ + int rsize; + + if (!block->bufIsUpdated) { + // read the data from sdcard + struct fat_info_t *info; + int lba; + + info = data->fat; + lba = info->cluster_lba + (block->cid - 2) * info->bs.sector_per_cluster; + readblock(lba, block->buf); + + block->bufIsUpdated = 1; + } + + // if the size is overflow, resize it + rsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + + memcpy((void *)&buf[bufoff], (void *)&block->buf[bckoff], rsize); + + return rsize; +} + +int _readfile_fat32(struct fat32_inode *data, unsigned long long bckoff, unsigned char *buf, unsigned int bufoff, unsigned int size, unsigned int oid, unsigned int cid) +{ + struct list_head *head; + struct fat_info_t *info; + struct fat_file_block_t *block; + int lba; + int rsize; + + head = &data->file->list; + info = data->fat; + + block = kmalloc(sizeof(struct fat_file_block_t)); + + rsize = size > BLOCK_SIZE - bckoff ? BLOCK_SIZE - bckoff : size; + lba = info->cluster_lba + (cid - 2) * info->bs.sector_per_cluster; + + // read from fat32 + readblock(lba, block->buf); + + memcpy((void *)&buf[bufoff], (void *)&block->buf[bckoff], rsize); + + block->oid = oid; + block->cid = cid; + block->bufIsUpdated = 1; + + list_add_tail(&block->list, head); + + return rsize; +} + +int _readfile(void *buf, struct fat32_inode *data, unsigned long long fileoff, unsigned long long len) +{ + struct fat_file_block_t *block; + struct list_head *head; + unsigned int foid; // first block id + unsigned int coid; // current block id + unsigned int cid; // target cluster id + unsigned long long bufoff, result; + int ret; + + block = NULL; + head = &data->file->list; + foid = fileoff / BLOCK_SIZE; + coid = 0; + cid = data->cid; + bufoff = 0; + result = 0; + + // Seek, find the particular offset + ret = _readfile_seek_cache(data, foid, &block); + if (ret < 0) { + ret = _readfile_seek_fat32(data, foid, cid, &block); + } + + if (ret < 0) { + return 0; + } + + // Read + while (len) { + unsigned long long bckoff; + + bckoff = (fileoff + result) % BLOCK_SIZE; + + if (&block->list != head) { + // cache found + ret = _readfile_cache(data, bckoff, buf, bufoff, len, block); + + cid = block->cid; + block = list_first_entry(&block->list, struct fat_file_block_t, list); + } else { + // Read block from sdcard, create cache + cid = get_next_cluster(data->fat->fat_lba, cid); + + if (cid >= INVALID_CID) { + break; + } + + ret = _readfile_fat32(data, bckoff, buf, bufoff, len, coid, cid); + } + + if (ret < 0) { + break; + } + + bufoff += ret; + result += ret; + coid += 1; + len -= ret; + } + + return result; +} + + +void _do_sync_dir(struct vnode *dirnode) +{ + struct fat32_inode *data, *entry; + struct list_head *head; + struct dir_t *dir; + struct long_dir_t *ldir; + unsigned int cid; + int lba, idx, lfnidx; + unsigned char buf[BLOCK_SIZE]; + + data = dirnode->internal; + head = &data->dir->list; + cid = data->cid; + idx = 0; + lfnidx = 1; + + if (cid >= INVALID_CID) { + uart_sendline("fat32 _do_sync_dir: invalid dirnode->data->cid"); + } + + // get the buf from dirnode's offset + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + readblock(lba, buf); + + list_for_each_entry(entry, head, list) { + struct dir_t *origindir; + const char *name; + const char *ext; + int lfn, namelen, extpos, i, buflba; + unsigned char lookupbuf[BLOCK_SIZE]; + + name = entry->name; + + // If entry is a old file, update its size + origindir = __lookup_fat32(dirnode, name, lookupbuf, &buflba); + + if (origindir) { + if (entry->type == FAT_FILE) { + origindir->size = entry->file->size; + writeblock(buflba, lookupbuf); + } + continue; + } + + // Else if entry is a new file + ext = NULL; + extpos = -1; + + do { + + namelen = strlen(name); + if (namelen >= 13) { + lfn = 1; + break; + } + for (i = 0; i < namelen; ++i) { + // get idx of extension name + if (name[namelen - 1 - i] == '.') { + break; + } + } + if (i < namelen) { + ext = &name[namelen - i]; + extpos = namelen - 1 - i; + } + if (i >= 4) { + lfn = 1; + break; + } + if (namelen - 1 - i > 8) { + // SFN: 8.3 + lfn = 1; + break; + } + + lfn = 0; + } while(0); + + // Seek idx to the end of dir + while (1) { + dir = (struct dir_t *)(&buf[sizeof(struct dir_t) * idx]); + + if (dir->name[0] == 0) { + break; + } + + idx += 1; + + if (idx >= 16) { // if idx is over Directory Entry, create a new one + unsigned int newcid; + + writeblock(lba, buf); + + newcid = get_next_cluster(data->fat->fat_lba, cid); + if (newcid >= INVALID_CID) + { + newcid = alloc_cluster(data->fat, cid); + } + + cid = newcid; + + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + + readblock(lba, buf); + idx = 0; + } + } + + // Write LFN + if (lfn) { + int ord; + int first; + + ord = ((namelen - 1) / 13) + 1; + first = 0x40; // LAST_LONG_ENTRY flag in LFN LDIR_Ord + + for (; ord > 0; --ord) { + int end; + ldir = (struct long_dir_t *)(&buf[sizeof(struct long_dir_t) * idx]); + + ldir->order = first | ord; + ldir->attr = ATTR_LFN; + ldir->type = 0; + // TODO: Calculate checksum, SFN + LFN + ldir->checksum = 0; + ldir->fstcluslo = 0; + + first = 0; + end = 0; + + for (i = 0; i < 10; i += 2) { + if (end) { // padding 0xff if filename end + ldir->name1[i] = 0xff; + ldir->name1[i + 1] = 0xff; + } else { + ldir->name1[i] = name[(ord - 1) * 13 + i / 2]; + ldir->name1[i + 1] = 0; + if (ldir->name1[i] == 0) { + end = 1; + } + } + } + for (i = 0; i < 12; i += 2) { + if (end) { + ldir->name2[i] = 0xff; + ldir->name2[i + 1] = 0xff; + } else { + ldir->name2[i] = name[(ord - 1) * 13 + 5 + i / 2]; + ldir->name2[i + 1] = 0; + if (ldir->name2[i] == 0) { + end = 1; + } + } + } + for (i = 0; i < 4; i += 2) { + if (end) { + ldir->name3[i] = 0xff; + ldir->name3[i + 1] = 0xff; + } else { + ldir->name3[i] = name[(ord - 1) * 13 + 11 + i / 2]; + ldir->name3[i + 1] = 0; + if (ldir->name3[i] == 0) { + end = 1; + } + } + } + + idx += 1; + + if (idx >= 16) { + unsigned int newcid; + + writeblock(lba, buf); + + newcid = get_next_cluster(data->fat->fat_lba, cid); + if (newcid >= INVALID_CID) { + newcid = alloc_cluster(data->fat, cid); + } + + cid = newcid; + + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + readblock(lba, buf); + + idx = 0; + } + } + } + + // Write SFN + dir = (struct dir_t *)(&buf[sizeof(struct dir_t) * idx]); + + // TODO: Set these properties properly + dir->ntres = 0; + dir->crttimetenth = 0; + dir->crttime = 0; + dir->crtdate = 0; + dir->lstaccdate = 0; + dir->wrttime = 0; + dir->wrtdate = 0; + + if (entry->type == FAT_DIR) { + dir->attr = ATTR_DIRECTORY; + dir->size = 0; + } else { + dir->attr = ATTR_ARCHIVE; + dir->size = entry->file->size; + } + + if (entry->cid >= INVALID_CID) { + entry->cid = alloc_cluster(data->fat, 0); + } + + dir->ch = (entry->cid >> 16) & 0xffff; + dir->cl = entry->cid & 0xffff; + + if (lfn) { + int lfni; + + // Creating SFN body + // TODO: handle lfnidx + for (i = 7, lfni = lfnidx; i >= 0 && lfni;) { + dir->name[i--] = '0' + lfni % 10; + lfni /= 10; + } + + lfnidx++; + // numeric-tail: ~n (1 <= n <= 6) + dir->name[i--] = '~'; + + // TODO: handle letter case + memcpy((void *)dir->name, name, i + 1); + } else { + // TODO: handle letter case + for (i = 0; i != extpos && name[i]; ++i) { + dir->name[i] = name[i]; + } + + for (; i < 8; ++i) { + dir->name[i] = ' '; // in SFN, each part is padded with space + } + } + + // SFN format: 8.3 + // TODO: handle letter case + for (i = 0; i < 3 && ext[i]; ++i) { + dir->name[8 + i] = ext[i]; + } + + for (; i < 3; ++i) { + dir->name[8 + i] = ' '; + } + + idx += 1; + + if (idx >= 16) { + int newcid; + + writeblock(lba, buf); + + newcid = get_next_cluster(data->fat->fat_lba, cid); + if (newcid >= INVALID_CID) { + newcid = alloc_cluster(data->fat, cid); + } + + cid = newcid; + + lba = data->fat->cluster_lba + (cid - 2) * data->fat->bs.sector_per_cluster; + + // TODO: Cache data block of directory + readblock(lba, buf); + + idx = 0; + } + } + + if (idx) { + writeblock(lba, buf); + } +} + +void _do_sync_file(struct vnode *filenode) +{ + struct fat_file_block_t *entry; + struct fat32_inode *data; + struct list_head *head; + unsigned int cid; + + data = filenode->internal; + head = &data->file->list; + cid = data->cid; + + if (cid >= INVALID_CID) { + //uart_sendline("fat32 _do_sync_file: invalid cid"); + return; + } + + list_for_each_entry(entry, head, list) { + int lba; + + if (entry->oid == 0) { + // cache is valid but not sync'd, just an informational warning + if (!(entry->cid >= INVALID_CID) && data->cid != entry->cid) { + uart_sendline("_do_sync_file: cid isn't sync"); + } + + entry->cid = data->cid; + } + + if (entry->cid >= INVALID_CID) { + entry->cid = alloc_cluster(data->fat, cid); + } + + // if the file is sync'd, pass + if (!entry->isDirty) { + continue; + } + + // cache buf is not updated, initialize it to avoid error + if (!entry->bufIsUpdated) { + memset(entry->buf, 0, BLOCK_SIZE); + entry->bufIsUpdated = 1; + } + + // sync to fat32 fs + lba = data->fat->cluster_lba + (entry->cid - 2) * data->fat->bs.sector_per_cluster; + writeblock(lba, entry->buf); + + entry->isDirty = 0; + + cid = entry->cid; + } +} + +void _sync_dir(struct vnode *dirnode) +{ + struct fat32_inode *data, *entry; + struct list_head *head; + + // Init: dirnode is mounted fs + data = dirnode->internal; + head = &data->dir->list; + + _do_sync_dir(dirnode); + + list_for_each_entry(entry, head, list) { + if (entry->type == FAT_DIR) { + _sync_dir(entry->node); // recursive to next dirnode + } else { + _do_sync_file(entry->node); + } + } +} diff --git a/lab8/kernel/src/initramfs.c b/lab8/kernel/src/initramfs.c new file mode 100644 index 000000000..701518ba4 --- /dev/null +++ b/lab8/kernel/src/initramfs.c @@ -0,0 +1,163 @@ +#include "initramfs.h" +#include "vfs.h" +#include "string.h" +#include "memory.h" +#include "cpio.h" +#include "uart1.h" + +struct file_operations initramfs_file_operations = {initramfs_write, initramfs_read, initramfs_open, initramfs_close, initramfs_lseek64,initramfs_getsize}; +struct vnode_operations initramfs_vnode_operations = {initramfs_lookup, initramfs_create, initramfs_mkdir}; + +int register_initramfs() +{ + struct filesystem fs; + fs.name = "initramfs"; + fs.setup_mount = initramfs_setup_mount; + fs.sync = initramfs_sync; + return register_filesystem(&fs); +} + +int initramfs_setup_mount(struct filesystem *fs, struct mount *_mount) +{ + _mount->fs = fs; + _mount->root = initramfs_create_vnode(0, dir_t); + struct initramfs_inode *ramdir_inode = _mount->root->internal; + + // add all file in initramfs to filesystem + char *filepath; + char *filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = CPIO_DEFAULT_START; + int idx = 0; + + while (header_pointer != 0) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + //if parse header error + if (error) + { + uart_sendline("%s", "error\r\n"); + break; + } + + //if this is not TRAILER!!! (last of file) + if (header_pointer != 0) + { + // only support file (no dir) + struct vnode * filevnode = initramfs_create_vnode(0, file_t); + struct initramfs_inode *fileinode = filevnode->internal; + fileinode->data = filedata; + fileinode->datasize = filesize; + fileinode->name = filepath; + ramdir_inode->entry[idx++] = filevnode; + } + } + + return 0; +} + +struct vnode *initramfs_create_vnode(struct mount *_mount, enum fsnode_type type) +{ + struct vnode *v = kmalloc(sizeof(struct vnode)); + v->f_ops = &initramfs_file_operations; + v->v_ops = &initramfs_vnode_operations; + v->mount = _mount; + struct initramfs_inode *inode = kmalloc(sizeof(struct initramfs_inode)); + memset(inode, 0, sizeof(struct initramfs_inode)); + inode->type = type; + inode->data = kmalloc(0x1000); + v->internal = inode; + return v; +} + +// file operations +int initramfs_write(struct file *file, const void *buf, size_t len) +{ + // read-only + return -1; +} + +int initramfs_read(struct file *file, void *buf, size_t len) +{ + struct initramfs_inode *inode = file->vnode->internal; + + if (len + file->f_pos > inode->datasize) + { + memcpy(buf, inode->data + file->f_pos, inode->datasize - file->f_pos); + file->f_pos += inode->datasize - file->f_pos; + return inode->datasize - file->f_pos; + } + else + { + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += len; + return len; + } + return -1; +} + +int initramfs_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +} + +int initramfs_close(struct file *file) +{ + kfree(file); + return 0; +} + +long initramfs_lseek64(struct file *file, long offset, int whence) +{ + if (whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + +// vnode operations +int initramfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct initramfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + for (; child_idx < INITRAMFS_MAX_DIR_ENTRY; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if (!vnode)break; + struct initramfs_inode *inode = vnode->internal; + if (strcmp(component_name, inode->name) == 0) + { + *target = vnode; + return 0; + } + } + return -1; +} + +int initramfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // read-only + return -1; +} + +int initramfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + // read-only + return -1; +} + +int initramfs_sync(struct filesystem *fs) +{ + return 0; +} + +long initramfs_getsize(struct vnode *vd) +{ + struct initramfs_inode *inode = vd->internal; + return inode->datasize; +} diff --git a/lab8/kernel/src/irqtask.c b/lab8/kernel/src/irqtask.c new file mode 100644 index 000000000..fb78201e6 --- /dev/null +++ b/lab8/kernel/src/irqtask.c @@ -0,0 +1,65 @@ +#include "irqtask.h" +#include "exception.h" +#include "memory.h" +#include "uart1.h" + +int curr_task_priority = 9999; +struct list_head *task_list; + +void irqtask_init_list() +{ + task_list = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(task_list); +} + +void irqtask_add(void *task_function,unsigned long long priority){ + + irqtask_t *the_task = kmalloc(sizeof(irqtask_t)); //need to kfree by task runner + the_task->priority = priority; + the_task->task_function = task_function; + + lock(); + struct list_head *curr; + list_for_each(curr, task_list) + { + if (((irqtask_t *)curr)->priority > the_task->priority) + { + list_add(&the_task->listhead, curr->prev); + break; + } + } + if (list_is_head(curr, task_list)) list_add_tail(&(the_task->listhead), task_list); // for the time is the biggest + unlock(); +} + +void irqtask_run_preemptive(){ + while (1) + { + lock(); + if (list_empty(task_list)) + { + unlock(); + break; + } + + irqtask_t *the_task = (irqtask_t *)task_list->next; + if (curr_task_priority <= the_task->priority) + { + unlock(); + break; + } + list_del_entry((struct list_head *)the_task); + int prev_task_priority = curr_task_priority; + curr_task_priority = the_task->priority; + + unlock(); + irqtask_run(the_task); + curr_task_priority = prev_task_priority; + kfree(the_task); + } +} + +void irqtask_run(irqtask_t *the_task) +{ + ((void (*)())the_task->task_function)(); +} diff --git a/lab8/kernel/src/link.ld b/lab8/kernel/src/link.ld new file mode 100644 index 000000000..ee5b2083e --- /dev/null +++ b/lab8/kernel/src/link.ld @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + _kernel_start = .; + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _kernel_end = .; + _heap_start = .; + + . = 0xffff00002c000000; + _stack_end = .; + . = 0xffff00003c000000; + _stack_top = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start) >> 3; diff --git a/lab8/kernel/src/main.c b/lab8/kernel/src/main.c new file mode 100644 index 000000000..e44684fd8 --- /dev/null +++ b/lab8/kernel/src/main.c @@ -0,0 +1,42 @@ +#include "uart1.h" +#include "shell.h" +#include "dtb.h" +#include "memory.h" +#include "exception.h" +#include "irqtask.h" +#include "timer.h" +#include "sched.h" +#include "vfs.h" + +void main(char* arg){ + char input_buffer[CMD_MAX_LEN]; + + dtb_ptr = PHYS_TO_VIRT(arg); + traverse_device_tree(dtb_ptr, dtb_callback_initramfs); // get initramfs location from dtb + + init_allocator(); + + uart_init(); + irqtask_init_list(); + timer_list_init(); + + init_thread_sched(); + + init_rootfs(); + + uart_interrupt_enable(); + el1_interrupt_enable(); // enable interrupt in EL1 -> EL1 + core_timer_enable(); + +#if DEBUG + cli_cmd_read(input_buffer); // Wait for input, Windows cannot attach to SERIAL from two processes. +#endif + + cli_print_banner(); + while(1){ + cli_cmd_clear(input_buffer, CMD_MAX_LEN); + uart_puts("# "); + cli_cmd_read(input_buffer); + cli_cmd_exec(input_buffer); + } +} diff --git a/lab8/kernel/src/mbox.c b/lab8/kernel/src/mbox.c new file mode 100644 index 000000000..530412d28 --- /dev/null +++ b/lab8/kernel/src/mbox.c @@ -0,0 +1,23 @@ +#include "bcm2837/rpi_mbox.h" +#include "mbox.h" + +/* Aligned to 16-byte boundary while we have 28-bits for VC */ +volatile unsigned int __attribute__((aligned(16))) pt[36]; + +int mbox_call( mbox_channel_type channel, unsigned int value ) +{ + // Add channel to lower 4 bit + value &= ~(0xF); + value |= channel; + while ( (*MBOX_STATUS & BCM_ARM_VC_MS_FULL) != 0 ) {} + // Write to Register + *MBOX_WRITE = value; + while(1) { + while ( *MBOX_STATUS & BCM_ARM_VC_MS_EMPTY ) {} + // Read from Register + if (value == *MBOX_READ) + return pt[1] == MBOX_REQUEST_SUCCEED; + } + return 0; +} + diff --git a/lab8/kernel/src/memory.c b/lab8/kernel/src/memory.c new file mode 100644 index 000000000..8c455652d --- /dev/null +++ b/lab8/kernel/src/memory.c @@ -0,0 +1,380 @@ +#include "memory.h" +#include "list.h" +#include "uart1.h" +#include "exception.h" +#include "dtb.h" +#include "cpio.h" +#include "mmu.h" + +extern char _heap_start; +static char* htop_ptr = &_heap_start; + +extern char _kernel_start; +extern char _kernel_end; +extern char _stack_end; +extern char _stack_top; + +#ifdef DEBUG + #define memory_sendline(fmt, args ...) uart_sendline(fmt, ##args) +#else + #define memory_sendline(fmt, args ...) (void)0 +#endif + +// ------ Lab2 ------ +void* s_allocator(unsigned int size) { + // -> htop_ptr + // htop_ptr + 0x02: heap_block size + // htop_ptr + 0x10 ~ htop_ptr + 0x10 * k: + // { heap_block } + // -> htop_ptr + + // 0x10 for heap_block header + char* r = htop_ptr + 0x10; + // size paddling to multiple of 0x10 + size = 0x10 + size - size % 0x10; + *(unsigned int*)(r - 0x8) = size; + htop_ptr += size; + return r; +} + +void s_free(void* ptr) { + // TBD +} + +// ------ Lab4 ------ +static frame_t* frame_array; // store memory's statement and page's corresponding index +static list_head_t frame_freelist[FRAME_MAX_IDX]; // store available block for page +static list_head_t cache_list[CACHE_MAX_IDX]; // store available block for cache + +void init_allocator() +{ + frame_array = s_allocator(BUDDY_MEMORY_PAGE_COUNT * sizeof(frame_t)); + + // init frame_array + for (int i = 0; i < BUDDY_MEMORY_PAGE_COUNT; i++) + { + if (i % (1 << FRAME_IDX_FINAL) == 0) + { + frame_array[i].val = FRAME_IDX_FINAL; + frame_array[i].used = FRAME_FREE; + } + } + + //init frame freelist + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&frame_freelist[i]); + } + + //init cache list + for (int i = CACHE_IDX_0; i<= CACHE_IDX_FINAL; i++) + { + INIT_LIST_HEAD(&cache_list[i]); + } + + for (int i = 0; i < BUDDY_MEMORY_PAGE_COUNT; i++) + { + // init listhead for each frame + INIT_LIST_HEAD(&frame_array[i].listhead); + frame_array[i].idx = i; + frame_array[i].cache_order = CACHE_NONE; + + // add init frame (FRAME_IDX_FINAL) into freelist + if (i % (1 << FRAME_IDX_FINAL) == 0) + { + list_add(&frame_array[i].listhead, &frame_freelist[FRAME_IDX_FINAL]); + } + } + + /* Startup reserving the following region: + Spin tables for multicore boot (0x0000 - 0x1000) + Devicetree (Optional, if you have implement it) + Kernel image in the physical memory + Your simple allocator (startup allocator) (Stack + Heap in my case) + Initramfs + */ + memory_sendline("\r\n* Startup Allocation *\r\n"); + memory_sendline("buddy system: usable memory region: 0x%x ~ 0x%x\n", BUDDY_MEMORY_BASE, BUDDY_MEMORY_BASE + BUDDY_MEMORY_PAGE_COUNT * PAGESIZE); + dtb_find_and_store_reserved_memory(); // find spin tables in dtb + memory_reserve(PHYS_TO_VIRT(MMU_PGD_ADDR), PHYS_TO_VIRT(MMU_PTE_ADDR+0x2000)); // // PGD's page frame at 0x1000 // PUD's page frame at 0x2000 PMD 0x3000-0x5000 + memory_reserve((unsigned long long)&_kernel_start, (unsigned long long)&_kernel_end); // kernel + memory_reserve((unsigned long long)&_stack_end, (unsigned long long)&_stack_top); // heap & stack -> simple allocator + memory_reserve((unsigned long long)CPIO_DEFAULT_START, (unsigned long long)CPIO_DEFAULT_END); +} + +void* page_malloc(unsigned int size) +{ + memory_sendline(" [+] Allocate page - size : %d(0x%x)\r\n", size, size); + memory_sendline(" Before\r\n"); + dump_page_info(); + int val; + // turn size into minimum 4KB * 2**val + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + + if (size <= (PAGESIZE << i)) + { + val = i; + memory_sendline(" block size = 0x%x\n", PAGESIZE << i); + break; + } + + if ( i == FRAME_IDX_FINAL) + { + memory_sendline("[!] request size exceeded for page_malloc!!!!\r\n"); + return (void*)0; + } + + } + + // find the smallest larger frame in freelist + int target_val; + for (target_val = val; target_val <= FRAME_IDX_FINAL; target_val++) + { + // freelist does not have 2**i order frame, going for next order + if (!list_empty(&frame_freelist[target_val])) + break; + } + if (target_val > FRAME_IDX_FINAL) + { + memory_sendline("[!] No available frame in freelist, page_malloc ERROR!!!!\r\n"); + return (void*)0; + } + + // get the available frame from freelist + frame_t *target_frame_ptr = (frame_t*)frame_freelist[target_val].next; + list_del_entry((struct list_head *)target_frame_ptr); + + // Release redundant memory block to separate into pieces + for (int j = target_val; j > val; j--) // ex: 10000 -> 01111 + { + release_redundant(target_frame_ptr); + } + + // Allocate it + target_frame_ptr->used = FRAME_ALLOCATED; + memory_sendline(" physical address : 0x%x\n", BUDDY_MEMORY_BASE + (PAGESIZE*(target_frame_ptr->idx))); + memory_sendline(" After\r\n"); + dump_page_info(); + + return (void *) BUDDY_MEMORY_BASE + (PAGESIZE * (target_frame_ptr->idx)); +} + +void page_free(void* ptr) +{ + frame_t *target_frame_ptr = &frame_array[((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12]; // PAGESIZE * Available Region -> 0x1000 * 0x10000000 // SPEC #1, #2 + memory_sendline(" [+] Free page: 0x%x, val = %d\r\n",ptr, target_frame_ptr->val); + memory_sendline(" Before\r\n"); + dump_page_info(); + target_frame_ptr->used = FRAME_FREE; + frame_t* temp; + while ((temp = coalesce(target_frame_ptr)) != (frame_t *)-1)target_frame_ptr = temp; + list_add(&target_frame_ptr->listhead, &frame_freelist[target_frame_ptr->val]); + memory_sendline(" After\r\n"); + dump_page_info(); +} + +frame_t* release_redundant(frame_t *frame) +{ + // order -1 -> add its buddy to free list (frame itself will be used in master function) + frame->val -= 1; + frame_t *buddyptr = get_buddy(frame); + buddyptr->val = frame->val; + list_add(&buddyptr->listhead, &frame_freelist[buddyptr->val]); + return frame; +} + +frame_t* get_buddy(frame_t *frame) +{ + // XOR(idx, order) + return &frame_array[frame->idx ^ (1 << frame->val)]; +} + +frame_t* coalesce(frame_t *frame_ptr) +{ + frame_t *buddy = get_buddy(frame_ptr); + // frame is the boundary + if (frame_ptr->val == FRAME_IDX_FINAL) + return (frame_t*)-1; + + // Order must be the same: 2**i + 2**i = 2**(i+1) + if (frame_ptr->val != buddy->val) + return (frame_t*)-1; + + // buddy is in used + if (buddy->used == FRAME_ALLOCATED) + return (frame_t*)-1; + + list_del_entry((struct list_head *)buddy); + frame_ptr->val += 1; + buddy->val += 1; + memory_sendline(" coalesce detected, merging 0x%x, 0x%x, -> val = %d\r\n", frame_ptr->idx, buddy->idx, frame_ptr->val); + return buddy < frame_ptr ? buddy : frame_ptr; +} + +void dump_page_info(){ + unsigned int exp2 = 1; + memory_sendline(" ----------------- [ Number of Available Page Blocks ] -----------------\r\n | "); + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + { + memory_sendline("%4dKB(%1d) ", 4*exp2, i); + exp2 *= 2; + } + memory_sendline("|\r\n | "); + for (int i = FRAME_IDX_0; i <= FRAME_IDX_FINAL; i++) + memory_sendline(" %4d ", list_size(&frame_freelist[i])); + memory_sendline("|\r\n"); +} + +void dump_cache_info() +{ + unsigned int exp2 = 1; + memory_sendline(" -- [ Number of Available Cache Blocks ] --\r\n | "); + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + memory_sendline("%4dB(%1d) ", 32*exp2, i); + exp2 *= 2; + } + memory_sendline("|\r\n | "); + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + memory_sendline(" %5d ", list_size(&cache_list[i])); + memory_sendline("|\r\n"); +} + +void page2caches(int order) +{ + // make caches from a smallest-size page + char *page = page_malloc(PAGESIZE); + frame_t *pageframe_ptr = &frame_array[((unsigned long long)page - BUDDY_MEMORY_BASE) >> 12]; + pageframe_ptr->cache_order = order; + + // split page into a lot of caches and push them into cache_list + int cachesize = (32 << order); + for (int i = 0; i < PAGESIZE; i += cachesize) + { + list_head_t *c = (list_head_t *)(page + i); + list_add(c, &cache_list[order]); + } +} + +void* cache_malloc(unsigned int size) +{ + memory_sendline("[+] Allocate cache - size : %d(0x%x)\r\n", size, size); + memory_sendline(" Before\r\n"); + dump_cache_info(); + + // turn size into cache order: 32B * 2**order + int order; + for (int i = CACHE_IDX_0; i <= CACHE_IDX_FINAL; i++) + { + if (size <= (32 << i)) { order = i; break; } + } + + // if no available cache in list, assign one page for it + if (list_empty(&cache_list[order])) + { + page2caches(order); + } + + list_head_t* r = cache_list[order].next; + list_del_entry(r); + memory_sendline(" physical address : 0x%x\n", r); + memory_sendline(" After\r\n"); + dump_cache_info(); + return r; +} + +void cache_free(void *ptr) +{ + list_head_t *c = (list_head_t *)ptr; + frame_t *pageframe_ptr = &frame_array[((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12]; + memory_sendline("[+] Free cache: 0x%x, val = %d\r\n",ptr, pageframe_ptr->cache_order); + memory_sendline(" Before\r\n"); + dump_cache_info(); + list_add(c, &cache_list[pageframe_ptr->cache_order]); + memory_sendline(" After\r\n"); + dump_cache_info(); +} + +void *kmalloc(unsigned int size) +{ + lock(); + memory_sendline("\n\n"); + memory_sendline("================================\r\n"); + memory_sendline("[+] Request kmalloc size: %d\r\n", size); + memory_sendline("================================\r\n"); + // if size is larger than cache size, go for page + if (size > (32 << CACHE_IDX_FINAL)) + { + void *r = page_malloc(size); + unlock(); + return r; + } + // go for cache + void *r = cache_malloc(size); + unlock(); + return r; +} + +void kfree(void *ptr) +{ + lock(); + memory_sendline("\n\n"); + memory_sendline("==========================\r\n"); + memory_sendline("[+] Request kfree 0x%x\r\n", ptr); + memory_sendline("==========================\r\n"); + // If no cache assigned, go for page + if ((unsigned long long)ptr % PAGESIZE == 0 && frame_array[((unsigned long long)ptr - BUDDY_MEMORY_BASE) >> 12].cache_order == CACHE_NONE) + { + page_free(ptr); + unlock(); + return; + } + // go for cache + cache_free(ptr); + unlock(); +} + +void memory_reserve(unsigned long long start, unsigned long long end) +{ + start -= start % PAGESIZE; // floor (align 0x1000) + end = end % PAGESIZE ? end + PAGESIZE - (end % PAGESIZE) : end; // ceiling (align 0x1000) + + uart_sendline("Reserved Memory: "); + uart_sendline("start 0x%x ~ ", start); + uart_sendline("end 0x%x\r\n",end); + + // delete page from free list + for (int order = FRAME_IDX_FINAL; order >= 0; order--) + { + list_head_t *pos; + list_for_each(pos, &frame_freelist[order]) + { + unsigned long long pagestart = ((frame_t *)pos)->idx * PAGESIZE + BUDDY_MEMORY_BASE; + unsigned long long pageend = pagestart + (PAGESIZE << order); + + if (start <= pagestart && end >= pageend) // if page all in reserved memory -> delete it from freelist + { + ((frame_t *)pos)->used = FRAME_ALLOCATED; + memory_sendline(" [!] Reserved page in 0x%x - 0x%x\n", pagestart, pageend); + memory_sendline(" Before\n"); + dump_page_info(); + list_del_entry(pos); + memory_sendline(" Remove usable block for reserved memory: order %d\r\n", order); + memory_sendline(" After\n"); + dump_page_info(); + } + else if (start >= pageend || end <= pagestart) // no intersection + { + continue; + } + else // partial intersection, separate the page into smaller size. + { + list_del_entry(pos); + list_head_t *temppos = pos -> prev; + list_add(&release_redundant((frame_t *)pos)->listhead, &frame_freelist[order - 1]); + pos = temppos; + } + } + } +} diff --git a/lab8/kernel/src/mmu.c b/lab8/kernel/src/mmu.c new file mode 100644 index 000000000..d6a9942bc --- /dev/null +++ b/lab8/kernel/src/mmu.c @@ -0,0 +1,166 @@ +#include "bcm2837/rpi_mmu.h" +#include "mmu.h" +#include "memory.h" +#include "string.h" +#include "uart1.h" + +void* set_2M_kernel_mmu(void* x0) +{ + // Turn + // Two-level Translation (1GB) - in boot.S + // to + // Three-level Translation (2MB) - set PUD point to new table + unsigned long *pud_table = (unsigned long *) MMU_PUD_ADDR; + + unsigned long *pte_table1 = (unsigned long *) MMU_PTE_ADDR; + unsigned long *pte_table2 = (unsigned long *)(MMU_PTE_ADDR + 0x1000L); + for (int i = 0; i < 512; i++) + { + unsigned long addr = 0x200000L * i; + if ( addr >= PERIPHERAL_END ) + { + pte_table1[i] = ( 0x00000000 + addr ) + BOOT_PTE_ATTR_nGnRnE; + continue; + } + pte_table1[i] = (0x00000000 + addr ) | BOOT_PTE_ATTR_NOCACHE; // 0 * 2MB // No definition for 3-level attribute, use nocache. + pte_table2[i] = (0x40000000 + addr ) | BOOT_PTE_ATTR_NOCACHE; // 512 * 2MB + } + + // set PUD + pud_table[0] = (unsigned long) pte_table1 | BOOT_PUD_ATTR; + pud_table[1] = (unsigned long) pte_table2 | BOOT_PUD_ATTR; + + return x0; +} + +void map_one_page(size_t *virt_pgd_p, size_t va, size_t pa, size_t flag) +{ + size_t *table_p = virt_pgd_p; + for (int level = 0; level < 4; level++) + { + unsigned int idx = (va >> (39 - level * 9)) & 0x1ff; // p.14, 9-bit only + + if (level == 3) + { + table_p[idx] = pa; + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_KNX | flag; // el0 only + return; + } + + if(!table_p[idx]) + { + size_t* newtable_p =kmalloc(0x1000); // create a table + memset(newtable_p, 0, 0x1000); + table_p[idx] = VIRT_TO_PHYS((size_t)newtable_p); // point to that table + table_p[idx] |= PD_ACCESS | PD_TABLE | (MAIR_IDX_NORMAL_NOCACHE << 2); + } + + table_p = (size_t*)PHYS_TO_VIRT((size_t)(table_p[idx] & ENTRY_ADDR_MASK)); // PAGE_SIZE + } +} + + +void mmu_add_vma(struct thread *t, size_t va, size_t size, size_t pa, size_t rwx, int is_alloced) +{ + size = size % 0x1000 ? size + (0x1000 - size % 0x1000) : size; + vm_area_struct_t* new_area = kmalloc(sizeof(vm_area_struct_t)); + new_area->rwx = rwx; + new_area->area_size = size; + new_area->virt_addr = va; + new_area->phys_addr = pa; + new_area->is_alloced = is_alloced; + list_add_tail((list_head_t *)new_area, &t->vma_list); +} + +void mmu_del_vma(struct thread *t) +{ + list_head_t *pos = t->vma_list.next; + vm_area_struct_t *vma; + while(pos != &t->vma_list){ + vma = (vm_area_struct_t *)pos; + if (vma->is_alloced) + kfree((void*)PHYS_TO_VIRT(vma->phys_addr)); + list_head_t* next_pos = pos->next; + kfree(pos); + pos = next_pos; + } +} + +void mmu_map_pages(size_t *virt_pgd_p, size_t va, size_t size, size_t pa, size_t flag) +{ + pa = pa - (pa % 0x1000); // align + for (size_t s = 0; s < size; s+=0x1000) + { + map_one_page(virt_pgd_p, va + s, pa + s, flag); + } +} + +void mmu_free_page_tables(size_t *page_table, int level) +{ + size_t *table_virt = (size_t*)PHYS_TO_VIRT((char*)page_table); + for (int i = 0; i < 512; i++) + { + if (table_virt[i] != 0) + { + size_t *next_table = (size_t*)(table_virt[i] & ENTRY_ADDR_MASK); + if (table_virt[i] & PD_TABLE) + { + if(level!=2)mmu_free_page_tables(next_table, level + 1); + table_virt[i] = 0L; + kfree(PHYS_TO_VIRT((char *)next_table)); + } + } + } +} + +void mmu_memfail_abort_handle(esr_el1_t* esr_el1) +{ + unsigned long long far_el1; + __asm__ __volatile__("mrs %0, FAR_EL1\n\t": "=r"(far_el1)); + + list_head_t *pos; + vm_area_struct_t *vma; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + vma = (vm_area_struct_t *)pos; + if (vma->virt_addr <= far_el1 && vma->virt_addr + vma->area_size >= far_el1) + { + the_area_ptr = vma; + break; + } + } + // area is not part of process's address space + if (!the_area_ptr) + { + uart_sendline("[Segmentation fault]: Kill Process\r\n"); + thread_exit(); + return; + } + + // For translation fault, only map one page frame for the fault address + if ((esr_el1->iss & 0x3f) == TF_LEVEL0 || + (esr_el1->iss & 0x3f) == TF_LEVEL1 || + (esr_el1->iss & 0x3f) == TF_LEVEL2 || + (esr_el1->iss & 0x3f) == TF_LEVEL3) + { + //uart_sendline("[Translation fault]: 0x%x\r\n",far_el1); // far_el1: Fault address register. + // Holds the faulting Virtual Address for all synchronous Instruction or Data Abort, PC alignment fault and Watchpoint exceptions that are taken to EL1. + + size_t addr_offset = (far_el1 - the_area_ptr->virt_addr); + addr_offset = (addr_offset % 0x1000) == 0 ? addr_offset : addr_offset - (addr_offset % 0x1000); + + size_t flag = 0; + if(!(the_area_ptr->rwx & (0b1 << 2))) flag |= PD_UNX; // 4: executable + if(!(the_area_ptr->rwx & (0b1 << 1))) flag |= PD_RDONLY; // 2: writable + if( the_area_ptr->rwx & (0b1 << 0) ) flag |= PD_UK_ACCESS; // 1: readable / accessible + map_one_page(PHYS_TO_VIRT(curr_thread->context.pgd), the_area_ptr->virt_addr + addr_offset, the_area_ptr->phys_addr + addr_offset, flag); + } + else + { + // For other Fault (permisson ...etc) + uart_sendline("[Segmentation fault]: Kill Process\r\n"); + thread_exit(); + } + +} diff --git a/lab8/kernel/src/sched.S b/lab8/kernel/src/sched.S new file mode 100644 index 000000000..6b7d930c3 --- /dev/null +++ b/lab8/kernel/src/sched.S @@ -0,0 +1,59 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + //mrs x9, ttbr0_el1 + //str x9, [x0, 16 * 6 + 8] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldp x9, x0, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + + dsb ish // ensure write has completed, lock for context switch + msr ttbr0_el1, x0 // switch translation based address. + tlbi vmalle1is // invalidate all TLB entries in stage 1, el1 and inner shareable + dsb ish // ensure completion of TLB invalidatation + isb // clear pipeline + + ret + +.global store_context +store_context: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + ret + +.global load_context +load_context: + ldp x19, x20, [x0, 16 * 0] + ldp x21, x22, [x0, 16 * 1] + ldp x23, x24, [x0, 16 * 2] + ldp x25, x26, [x0, 16 * 3] + ldp x27, x28, [x0, 16 * 4] + ldp fp, lr, [x0, 16 * 5] + ldr x9, [x0, 16 * 6] + mov sp, x9 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret diff --git a/lab8/kernel/src/sched.c b/lab8/kernel/src/sched.c new file mode 100644 index 000000000..016f92c8c --- /dev/null +++ b/lab8/kernel/src/sched.c @@ -0,0 +1,179 @@ +#include "sched.h" +#include "exception.h" +#include "memory.h" +#include "timer.h" +#include "uart1.h" +#include "signal.h" +#include "mmu.h" +#include "string.h" + +thread_t *curr_thread; +list_head_t *run_queue; +list_head_t *wait_queue; +thread_t threads[PIDMAX + 1]; + +void init_thread_sched() +{ + lock(); + run_queue = kmalloc(sizeof(list_head_t)); + wait_queue = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(run_queue); + INIT_LIST_HEAD(wait_queue); + + //init pids + for (int i = 0; i <= PIDMAX; i++) + { + threads[i].isused = 0; + threads[i].pid = i; + threads[i].iszombie = 0; + } + + thread_t* idlethread = thread_create(idle,0x1000); + curr_thread = idlethread; + asm volatile("msr tpidr_el1, %0" ::"r" (&idlethread->context)); + unlock(); +} + +void idle(){ + while(1) + { + kill_zombies(); //reclaim threads marked as DEAD + schedule(); //switch to next thread in run queue + } +} + +void schedule(){ + lock(); + do{ + curr_thread = (thread_t *)curr_thread->listhead.next; + } while (list_is_head(&curr_thread->listhead, run_queue) || curr_thread->iszombie); + unlock(); + switch_to(get_current(), &curr_thread->context); +} + +void kill_zombies(){ + lock(); + list_head_t *curr; + thread_t *t; + list_for_each(curr,run_queue) + { + t = (thread_t *)curr; + if (t->iszombie) + { + list_del_entry(curr); + mmu_free_page_tables(t->context.pgd,0); + mmu_del_vma(t); + for(int i = 0; i < MAX_FD;i++) + { + if (t->file_descriptors_table[i]) + vfs_close(t->file_descriptors_table[i]); + } + kfree(t->kernel_stack_alloced_ptr); + kfree(PHYS_TO_VIRT(t->context.pgd)); + t->iszombie = 0; + t->isused = 0; + } + } + unlock(); +} + +int thread_exec(char *data, unsigned int filesize) +{ + thread_t *t = thread_create(data, filesize); + + mmu_add_vma(t, USER_KERNEL_BASE, t->datasize, (size_t)VIRT_TO_PHYS(t->data) , 0b111, 1); + mmu_add_vma(t, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)VIRT_TO_PHYS(t->stack_alloced_ptr), 0b111, 1); + mmu_add_vma(t, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0); + mmu_add_vma(t, USER_SIGNAL_WRAPPER_VA, 0x2000, (size_t)VIRT_TO_PHYS(signal_handler_wrapper), 0b101, 0); + + t->context.pgd = VIRT_TO_PHYS(t->context.pgd); + t->context.sp = USER_STACK_BASE; + t->context.fp = USER_STACK_BASE; + t->context.lr = USER_KERNEL_BASE; + + //copy file into data + for (int i = 0; i < filesize;i++) + { + t->data[i] = data[i]; + } + + //disable echo when going to userspace + curr_thread = t; + + vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[0]); // stdin + vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[1]); // stdout + vfs_open("/dev/uart", 0, &curr_thread->file_descriptors_table[2]); // stderr + + //disable echo when going to userspace + add_timer(schedule_timer, 1, "", 0); + // eret to exception level 0 + asm("msr tpidr_el1, %0\n\t" + "msr elr_el1, %1\n\t" + "msr spsr_el1, xzr\n\t" // enable interrupt in EL0. You can do it by setting spsr_el1 to 0 before returning to EL0. + "msr sp_el0, %2\n\t" + "mov sp, %3\n\t" + "dsb ish\n\t" // ensure write has completed + "msr ttbr0_el1, %4\n\t" + "tlbi vmalle1is\n\t" // invalidate all TLB entries + "dsb ish\n\t" // ensure completion of TLB invalidatation + "isb\n\t" // clear pipeline" + "eret\n\t" ::"r"(&t->context),"r"(t->context.lr), "r"(t->context.sp), "r"(t->kernel_stack_alloced_ptr + KSTACK_SIZE), "r"(t->context.pgd)); + + return 0; +} + + +//malloc a kstack and a userstack +thread_t *thread_create(void *start, unsigned int filesize) +{ + lock(); + + thread_t *r; + for (int i = 0; i <= PIDMAX; i++) + { + if (!threads[i].isused) + { + r = &threads[i]; + break; + } + } + INIT_LIST_HEAD(&r->vma_list); + r->iszombie = 0; + r->isused = 1; + r->context.lr = (unsigned long long)start; + r->stack_alloced_ptr = kmalloc(USTACK_SIZE); + r->kernel_stack_alloced_ptr = kmalloc(KSTACK_SIZE); + r->signal_is_checking = 0; + r->data = kmalloc(filesize); + r->datasize = filesize; + r->context.sp = (unsigned long long)r->kernel_stack_alloced_ptr + KSTACK_SIZE; + r->context.fp = r->context.sp; + strcpy(r->curr_working_dir, "/"); + + r->context.pgd = kmalloc(0x1000); + memset(r->context.pgd, 0, 0x1000); + + //initial signal handler with signal_default_handler (kill thread) + for (int i = 0; i < SIGNAL_MAX; i++) + { + r->signal_handler[i] = signal_default_handler; + r->sigcount[i] = 0; + } + + list_add(&r->listhead, run_queue); + unlock(); + return r; +} + +void thread_exit(){ + lock(); + curr_thread->iszombie = 1; + unlock(); + schedule(); +} + +void schedule_timer(char* notuse){ + unsigned long long cntfrq_el0; + __asm__ __volatile__("mrs %0, cntfrq_el0\n\t": "=r"(cntfrq_el0)); //tick frequency + add_timer(schedule_timer, cntfrq_el0 >> 5, "", 1); +} diff --git a/lab8/kernel/src/sdhost.c b/lab8/kernel/src/sdhost.c new file mode 100644 index 000000000..ba0ed32b8 --- /dev/null +++ b/lab8/kernel/src/sdhost.c @@ -0,0 +1,243 @@ +// mmio +#define KVA 0xffff000000000000 +#define MMIO_BASE (KVA + 0x3f000000) + +// SD card command +#define GO_IDLE_STATE 0 +#define SEND_OP_CMD 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 + #define VOLTAGE_CHECK_PATTERN 0x1aa +#define STOP_TRANSMISSION 12 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define WRITE_SINGLE_BLOCK 24 +#define SD_APP_OP_COND 41 + #define SDCARD_3_3V (1 << 21) + #define SDCARD_ISHCS (1 << 30) + #define SDCARD_READY (1 << 31) +#define APP_CMD 55 + +// gpio +#define GPIO_BASE (MMIO_BASE + 0x200000) +#define GPIO_GPFSEL4 (GPIO_BASE + 0x10) +#define GPIO_GPFSEL5 (GPIO_BASE + 0x14) +#define GPIO_GPPUD (GPIO_BASE + 0x94) +#define GPIO_GPPUDCLK1 (GPIO_BASE + 0x9c) + +// sdhost +#define SDHOST_BASE (MMIO_BASE + 0x202000) +#define SDHOST_CMD (SDHOST_BASE + 0) + #define SDHOST_READ 0x40 + #define SDHOST_WRITE 0x80 + #define SDHOST_LONG_RESPONSE 0x200 + #define SDHOST_NO_REPONSE 0x400 + #define SDHOST_BUSY 0x800 + #define SDHOST_NEW_CMD 0x8000 +#define SDHOST_ARG (SDHOST_BASE + 0x4) +#define SDHOST_TOUT (SDHOST_BASE + 0x8) + #define SDHOST_TOUT_DEFAULT 0xf00000 +#define SDHOST_CDIV (SDHOST_BASE + 0xc) + #define SDHOST_CDIV_MAXDIV 0x7ff + #define SDHOST_CDIV_DEFAULT 0x148 +#define SDHOST_RESP0 (SDHOST_BASE + 0x10) +#define SDHOST_RESP1 (SDHOST_BASE + 0x14) +#define SDHOST_RESP2 (SDHOST_BASE + 0x18) +#define SDHOST_RESP3 (SDHOST_BASE + 0x1c) +#define SDHOST_HSTS (SDHOST_BASE + 0x20) + #define SDHOST_HSTS_MASK (0x7f8) + #define SDHOST_HSTS_ERR_MASK (0xf8) + #define SDHOST_HSTS_DATA (1 << 0) +#define SDHOST_PWR (SDHOST_BASE + 0x30) +#define SDHOST_DBG (SDHOST_BASE + 0x34) + #define SDHOST_DBG_FSM_DATA 1 + #define SDHOST_DBG_FSM_MASK 0xf + #define SDHOST_DBG_MASK (0x1f << 14 | 0x1f << 9) + #define SDHOST_DBG_FIFO (0x4 << 14 | 0x4 << 9) +#define SDHOST_CFG (SDHOST_BASE + 0x38) + #define SDHOST_CFG_DATA_EN (1 << 4) + #define SDHOST_CFG_SLOW (1 << 3) + #define SDHOST_CFG_INTBUS (1 << 1) +#define SDHOST_SIZE (SDHOST_BASE + 0x3c) +#define SDHOST_DATA (SDHOST_BASE + 0x40) +#define SDHOST_CNT (SDHOST_BASE + 0x50) + +// helper +#define set(io_addr, val) \ + asm volatile("str %w1, [%0]" ::"r"(io_addr), "r"(val) : "memory"); + +#define get(io_addr, val) \ + asm volatile("ldr %w0, [%1]" : "=r"(val) : "r"(io_addr) : "memory"); + +static inline void delay(unsigned long tick) { + while (tick--) { + asm volatile("nop"); + } +} + +static int is_hcs; // high capcacity(SDHC) + +static void pin_setup() { + set(GPIO_GPFSEL4, 0x24000000); + set(GPIO_GPFSEL5, 0x924); + set(GPIO_GPPUD, 0); + delay(15000); + set(GPIO_GPPUDCLK1, 0xffffffff); + delay(15000); + set(GPIO_GPPUDCLK1, 0); +} + +static void sdhost_setup() { + unsigned int tmp; + set(SDHOST_PWR, 0); + set(SDHOST_CMD, 0); + set(SDHOST_ARG, 0); + set(SDHOST_TOUT, SDHOST_TOUT_DEFAULT); + set(SDHOST_CDIV, 0); + set(SDHOST_HSTS, SDHOST_HSTS_MASK); + set(SDHOST_CFG, 0); + set(SDHOST_CNT, 0); + set(SDHOST_SIZE, 0); + get(SDHOST_DBG, tmp); + tmp &= ~SDHOST_DBG_MASK; + tmp |= SDHOST_DBG_FIFO; + set(SDHOST_DBG, tmp); + delay(250000); + set(SDHOST_PWR, 1); + delay(250000); + set(SDHOST_CFG, SDHOST_CFG_SLOW | SDHOST_CFG_INTBUS | SDHOST_CFG_DATA_EN); + set(SDHOST_CDIV, SDHOST_CDIV_DEFAULT); +} + +static int wait_sd() { + int cnt = 1000000; + unsigned int cmd; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_CMD, cmd); + --cnt; + } while (cmd & SDHOST_NEW_CMD); + return 0; +} + +static int sd_cmd(unsigned cmd, unsigned int arg) { + set(SDHOST_ARG, arg); + set(SDHOST_CMD, cmd | SDHOST_NEW_CMD); + return wait_sd(); +} + +static int sdcard_setup() { + unsigned int tmp; + sd_cmd(GO_IDLE_STATE | SDHOST_NO_REPONSE, 0); + sd_cmd(SEND_IF_COND, VOLTAGE_CHECK_PATTERN); + get(SDHOST_RESP0, tmp); + if (tmp != VOLTAGE_CHECK_PATTERN) { + return -1; + } + while (1) { + if (sd_cmd(APP_CMD, 0) == -1) { + // MMC card or invalid card status + // currently not support + continue; + } + sd_cmd(SD_APP_OP_COND, SDCARD_3_3V | SDCARD_ISHCS); + get(SDHOST_RESP0, tmp); + if (tmp & SDCARD_READY) { + break; + } + delay(1000000); + } + + is_hcs = tmp & SDCARD_ISHCS; + sd_cmd(ALL_SEND_CID | SDHOST_LONG_RESPONSE, 0); + sd_cmd(SEND_RELATIVE_ADDR, 0); + get(SDHOST_RESP0, tmp); + sd_cmd(SELECT_CARD, tmp); + sd_cmd(SET_BLOCKLEN, 512); + return 0; +} + +static int wait_fifo() { + int cnt = 1000000; + unsigned int hsts; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_HSTS, hsts); + --cnt; + } while ((hsts & SDHOST_HSTS_DATA) == 0); + return 0; +} + +static void set_block(int size, int cnt) { + set(SDHOST_SIZE, size); + set(SDHOST_CNT, cnt); +} + +static void wait_finish() { + unsigned int dbg; + do { + get(SDHOST_DBG, dbg); + } while ((dbg & SDHOST_DBG_FSM_MASK) != SDHOST_HSTS_DATA); +} + +void readblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do{ + set_block(512, 1); + sd_cmd(READ_SINGLE_BLOCK | SDHOST_READ, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + get(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while(!succ); + wait_finish(); +} + +void writeblock(int block_idx, void* buf) { + unsigned int* buf_u = (unsigned int*)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do{ + set_block(512, 1); + sd_cmd(WRITE_SINGLE_BLOCK | SDHOST_WRITE, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + set(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while(!succ); + wait_finish(); +} + +void sd_init() { + pin_setup(); + sdhost_setup(); + sdcard_setup(); +} diff --git a/lab8/kernel/src/shell.c b/lab8/kernel/src/shell.c new file mode 100644 index 000000000..35fffdde8 --- /dev/null +++ b/lab8/kernel/src/shell.c @@ -0,0 +1,220 @@ +#include +#include "shell.h" +#include "uart1.h" +#include "mbox.h" +#include "power.h" +#include "cpio.h" +#include "string.h" +#include "dtb.h" +#include "memory.h" +#include "timer.h" +#include "sched.h" + +#define CLI_MAX_CMD 9 + +extern int uart_recv_echo_flag; +extern char* dtb_ptr; +extern void* CPIO_DEFAULT_START; + +struct CLI_CMDS cmd_list[CLI_MAX_CMD]= +{ + {.command="cat", .help="concatenate files and print on the standard output"}, + {.command="dtb", .help="show device tree"}, + {.command="exec", .help="execute a command, replacing current image with a new image"}, + {.command="hello", .help="print Hello World!"}, + {.command="help", .help="print all available commands"}, + {.command="info", .help="get device information via mailbox"}, + {.command="ls", .help="list directory contents"}, + {.command="setTimeout", .help="setTimeout [MESSAGE] [SECONDS]"}, + {.command="reboot", .help="reboot the device"} +}; + +void cli_cmd_clear(char* buffer, int length) +{ + for(int i=0; i= CMD_MAX_LEN ) break; + c = uart_async_getc(); + if ( c == '\n') break; + buffer[idx++] = c; + } +} + +void cli_cmd_exec(char* buffer) +{ + if (!buffer) return; + + char* cmd = buffer; + char* argvs = str_SepbySpace(buffer); + + if (strcmp(cmd, "cat") == 0) { + do_cmd_cat(argvs); + } else if (strcmp(cmd, "dtb") == 0){ + do_cmd_dtb(); + } else if (strcmp(cmd, "exec") == 0){ + do_cmd_exec(argvs); + } else if (strcmp(cmd, "hello") == 0) { + do_cmd_hello(); + } else if (strcmp(cmd, "help") == 0) { + do_cmd_help(); + } else if (strcmp(cmd, "info") == 0) { + do_cmd_info(); + } else if (strcmp(cmd, "ls") == 0) { + do_cmd_ls(argvs); + } else if (strcmp(cmd, "setTimeout") == 0) { + char* sec = str_SepbySpace(argvs); + do_cmd_setTimeout(argvs, sec); + } else if (strcmp(cmd, "reboot") == 0) { + do_cmd_reboot(); + } +} + +void cli_print_banner() +{ + //uart_puts("\r\n"); + //uart_puts("=======================================\r\n"); + //uart_puts(" Welcome to NYCU-OSC 2023 Lab8 Shell \r\n"); + //uart_puts("=======================================\r\n"); + uart_puts("Lab8:\r\n"); +} + +void do_cmd_cat(char* filepath) +{ + char* c_filepath; + char* c_filedata; + unsigned int c_filesize; + struct cpio_newc_header *header_ptr = CPIO_DEFAULT_START; + + while(header_ptr!=0) + { + int error = cpio_newc_parse_header(header_ptr, &c_filepath, &c_filesize, &c_filedata, &header_ptr); + if(error) break; + if(strcmp(c_filepath, filepath)==0) + { + uart_puts("%s", c_filedata); + break; + } + if(header_ptr==0) uart_puts("cat: %s: No such file or directory\n", filepath); + } +} + +void do_cmd_dtb() +{ + traverse_device_tree(dtb_ptr, dtb_callback_show_tree); +} + +void do_cmd_help() +{ + for(int i = 0; i < CLI_MAX_CMD; i++) + { + uart_puts(cmd_list[i].command); + uart_puts("\t\t\t: "); + uart_puts(cmd_list[i].help); + uart_puts("\r\n"); + } +} + +void do_cmd_exec(char* filepath) +{ + char* c_filepath; + char* c_filedata; + unsigned int c_filesize; + struct cpio_newc_header *header_ptr = CPIO_DEFAULT_START; + + while(header_ptr!=0) + { + int error = cpio_newc_parse_header(header_ptr, &c_filepath, &c_filesize, &c_filedata, &header_ptr); + if(error) break; + + if(strcmp(c_filepath, filepath)==0) + { + uart_recv_echo_flag = 0; // syscall.img has different mechanism on uart I/O. + thread_exec(c_filedata, c_filesize); + } + if(header_ptr==0) uart_puts("cat: %s: No such file or directory\n", filepath); + } + +} + +void do_cmd_hello() +{ + uart_puts("Hello World!\r\n"); +} + +void do_cmd_info() +{ + // print hw revision + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_BOARD_REVISION; + pt[3] = 4; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)) ) { + uart_puts("Hardware Revision\t: "); + uart_2hex(pt[6]); + uart_2hex(pt[5]); + uart_puts("\r\n"); + } + // print arm memory + pt[0] = 8 * 4; + pt[1] = MBOX_REQUEST_PROCESS; + pt[2] = MBOX_TAG_GET_ARM_MEMORY; + pt[3] = 8; + pt[4] = MBOX_TAG_REQUEST_CODE; + pt[5] = 0; + pt[6] = 0; + pt[7] = MBOX_TAG_LAST_BYTE; + + if (mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)) ) { + uart_puts("ARM Memory Base Address\t: "); + uart_2hex(pt[5]); + uart_puts("\r\n"); + uart_puts("ARM Memory Size\t\t: "); + uart_2hex(pt[6]); + uart_puts("\r\n"); + } +} + +void do_cmd_ls(char* workdir) +{ + char* c_filepath; + char* c_filedata; + unsigned int c_filesize; + struct cpio_newc_header *header_ptr = CPIO_DEFAULT_START; + + while(header_ptr!=0) + { + int error = cpio_newc_parse_header(header_ptr, &c_filepath, &c_filesize, &c_filedata, &header_ptr); + if(error) break; + if(header_ptr!=0) uart_puts("%s\n", c_filepath); + } +} + +void do_cmd_setTimeout(char* msg, char* sec) +{ + add_timer(uart_sendline,atoi(sec),msg,0); +} + +void do_cmd_reboot() +{ + uart_puts("Reboot in 5 seconds ...\r\n\r\n"); + volatile unsigned int* rst_addr = (unsigned int*)PM_RSTC; + *rst_addr = PM_PASSWORD | 0x20; + volatile unsigned int* wdg_addr = (unsigned int*)PM_WDOG; + *wdg_addr = PM_PASSWORD | 5; +} + diff --git a/lab8/kernel/src/signal.c b/lab8/kernel/src/signal.c new file mode 100644 index 000000000..d98309abe --- /dev/null +++ b/lab8/kernel/src/signal.c @@ -0,0 +1,68 @@ +#include "signal.h" +#include "sched.h" +#include "syscall.h" +#include "memory.h" +#include "mmu.h" + +void check_signal(trapframe_t *tpf) +{ + lock(); + if(curr_thread->signal_is_checking) + { + unlock(); + return; + } + //prevent nested running signal handler + curr_thread->signal_is_checking = 1; + unlock(); + for (int i = 0; i <= SIGNAL_MAX; i++) + { + store_context(&curr_thread->signal_saved_context); + if(curr_thread->sigcount[i]>0) + { + lock(); + curr_thread->sigcount[i]--; + unlock(); + run_signal(tpf,i); + } + } + lock(); + curr_thread->signal_is_checking = 0; + unlock(); +} + +void run_signal(trapframe_t* tpf,int signal) +{ + curr_thread->curr_signal_handler = curr_thread->signal_handler[signal]; + + //run default handler in kernel + if (curr_thread->curr_signal_handler == signal_default_handler) + { + signal_default_handler(); + return; + } + + asm("msr elr_el1, %0\n\t" + "msr sp_el0, %1\n\t" + "msr spsr_el1, %2\n\t" + "mov x0, %3\n\t" + "eret\n\t" + :: "r"(USER_SIGNAL_WRAPPER_VA + ((size_t)signal_handler_wrapper % 0x1000)), + "r"(tpf->sp_el0), + "r"(tpf->spsr_el1), + "r"(curr_thread->curr_signal_handler)); +} + +void signal_handler_wrapper() +{ + //elr_el1 set to function -> call function by x0 + //system call sigreturn + asm("blr x0\n\t" + "mov x8,50\n\t" + "svc 0\n\t"); +} + +void signal_default_handler() +{ + kill(0,curr_thread->pid); +} diff --git a/lab8/kernel/src/string.c b/lab8/kernel/src/string.c new file mode 100644 index 000000000..b746dfb5a --- /dev/null +++ b/lab8/kernel/src/string.c @@ -0,0 +1,313 @@ +#include "string.h" +#include + +unsigned int vsprintf(char *dst, char* fmt, __builtin_va_list args) +{ + long int arg; + int len, sign, i; + char *p, *orig=dst, tmpstr[19]; + + // failsafes + if(dst==(void*)0 || fmt==(void*)0) { + return 0; + } + + // main loop + arg = 0; + while(*fmt) { + if(dst-orig > VSPRINT_MAX_BUF_SIZE-0x10) + { + return -1; + } + // argument access + if(*fmt=='%') { + fmt++; + // literal % + if(*fmt=='%') { + goto put; + } + len=0; + // size modifier + while(*fmt>='0' && *fmt<='9') { + len *= 10; + len += *fmt-'0'; + fmt++; + } + // skip long modifier + if(*fmt=='l') { + fmt++; + } + // character + if(*fmt=='c') { + arg = __builtin_va_arg(args, int); + *dst++ = (char)arg; + fmt++; + continue; + } else + // decimal number + if(*fmt=='d') { + arg = __builtin_va_arg(args, int); + // check input + sign=0; + if((int)arg<0) { + arg*=-1; + sign++; + } + if(arg>99999999999999999L) { + arg=99999999999999999L; + } + // convert to string + i=18; + tmpstr[i]=0; + do { + tmpstr[--i]='0'+(arg%10); + arg/=10; + } while(arg!=0 && i>0); + if(sign) { + tmpstr[--i]='-'; + } + if(len>0 && len<18) { + while(i>18-len) { + tmpstr[--i]=' '; + } + } + p=&tmpstr[i]; + goto copystring; + } else + if(*fmt=='x') { + arg = __builtin_va_arg(args, long int); + i=16; + tmpstr[i]=0; + do { + char n=arg & 0xf; + tmpstr[--i]=n+(n>9?0x37:0x30); + arg>>=4; + } while(arg!=0 && i>0); + if(len>0 && len<=16) { + while(i>16-len) { + tmpstr[--i]='0'; + } + } + p=&tmpstr[i]; + goto copystring; + } else + if(*fmt=='s') { + p = __builtin_va_arg(args, char*); +copystring: if(p==(void*)0) { + p="(null)"; + } + while(*p) { + *dst++ = *p++; + } + } + } else { +put: *dst++ = *fmt; + } + fmt++; + } + *dst=0; + return dst-orig; +} + +unsigned int sprintf(char *dst, char* fmt, ...) +{ + __builtin_va_list args; + __builtin_va_start(args, fmt); + unsigned int r = vsprintf(dst,fmt,args); + __builtin_va_end(args); + return r; +} + +unsigned long long strlen(const char *str) +{ + size_t count = 0; + while((unsigned char)*str++)count++; + return count; +} + +char *strncat(char *dest, const char *src, int n) +{ + char *t; + + t = dest; + + while (*t) { + t++; + } + + while (n > 0 && *src) { + *t = *src; + t++; + src++; + n--; + } + + *t = '\0'; + + return dest; +} + +char* strcat (char *dest, const char *src) +{ + strcpy (dest + strlen (dest), src); + return dest; +} + +int strcasecmp(const char *s1, const char *s2) +{ + char c1, c2; + + while (1) { + c1 = *s1++; + c2 = *s2++; + + if (!c1 || !c2) { + break; + } + + if ('A' <= c1 && c1 <= 'Z') { + c1 |= 0x20; + } + + if ('A' <= c2 && c2 <= 'Z') { + c2 |= 0x20; + } + + if (c1 != c2) { + break; + } + } + + return c1 - c2; +} + +int strcmp(const char* p1, const char* p2) +{ + const unsigned char *s1 = (const unsigned char*) p1; + const unsigned char *s2 = (const unsigned char*) p2; + unsigned char c1, c2; + + do { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if ( c1 == '\0' ) return c1 - c2; + } while ( c1 == c2 ); + return c1 - c2; +} + +int strncmp (const char *s1, const char *s2, unsigned long long n) +{ + unsigned char c1 = '\0'; + unsigned char c2 = '\0'; + if (n >= 4) + { + size_t n4 = n >> 2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + } while (--n4 > 0); + n &= 3; + } + while (n > 0) + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + n--; + } + return c1 - c2; +} + +char* memcpy(void *dest, const void *src, unsigned long long len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} + +char* strcpy (char *dest, const char *src) +{ + return memcpy (dest, src, strlen (src) + 1); +} + +void *memset(void *s, int c, size_t n) +{ + char *start = s; + for (size_t i = 0; i < n; i++) + { + start[i] = c; + } + + return s; +} + +char* strchr (register const char *s, int c) +{ + do { + if (*s == c) + { + return (char*)s; + } + } while (*s++); + return (0); +} + +char* str_SepbySpace(char* head) +{ + char* end; + while(1){ + if(*head == '\0') + { + end = head; + break; + } + if(*head == ' ') + { + *head = '\0'; + end = head + 1; + break; + } + head++; + } + return end; +} + +// A simple atoi() function +int atoi(char* str) +{ + // Initialize result + int res = 0; + + // Iterate through all characters + // of input string and update result + // take ASCII character of corresponding digit and + // subtract the code from '0' to get numerical + // value and multiply res by 10 to shuffle + // digits left to update running total + for (int i = 0; str[i] != '\0'; ++i) + { + if(str[i] > '9' || str[i] < '0')return res; + res = res * 10 + str[i] - '0'; + } + + // return result. + return res; +} diff --git a/lab8/kernel/src/syscall.c b/lab8/kernel/src/syscall.c new file mode 100644 index 000000000..acfa404ef --- /dev/null +++ b/lab8/kernel/src/syscall.c @@ -0,0 +1,418 @@ +#include "syscall.h" +#include "cpio.h" +#include "sched.h" +#include "stddef.h" +#include "uart1.h" +#include "exception.h" +#include "memory.h" +#include "mbox.h" +#include "signal.h" +#include "mmu.h" +#include "string.h" +#include "dev_framebuffer.h" + +int getpid(trapframe_t* tpf) +{ + tpf->x0 = curr_thread->pid; + return curr_thread->pid; +} + +size_t uartread(trapframe_t *tpf,char buf[], size_t size) +{ + int i = 0; + for (int i = 0; i < size;i++) + { + buf[i] = uart_async_getc(); + } + tpf->x0 = i; + return i; +} + +size_t uartwrite(trapframe_t *tpf,const char buf[], size_t size) +{ + int i = 0; + for (int i = 0; i < size; i++) + { + uart_async_putc(buf[i]); + } + tpf->x0 = i; + return i; +} + +//In this lab, you won’t have to deal with argument passing +int exec(trapframe_t *tpf,const char *name, char *const argv[]) +{ + mmu_del_vma(curr_thread); + INIT_LIST_HEAD(&curr_thread->vma_list); + + // use virtual file system + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, name); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + struct vnode *target_file; + vfs_lookup(abs_path,&target_file); + curr_thread->datasize = target_file->f_ops->getsize(target_file); + + curr_thread->data = kmalloc(curr_thread->datasize); + curr_thread->stack_alloced_ptr = kmalloc(USTACK_SIZE); + + asm("dsb ish\n\t"); // ensure write has completed + mmu_free_page_tables(curr_thread->context.pgd, 0); + memset(PHYS_TO_VIRT(curr_thread->context.pgd), 0, 0x1000); + asm("tlbi vmalle1is\n\t" // invalidate all TLB entries + "dsb ish\n\t" // ensure completion of TLB invalidatation + "isb\n\t"); // clear pipeline + + mmu_add_vma(curr_thread, USER_KERNEL_BASE, curr_thread->datasize, (size_t)VIRT_TO_PHYS(curr_thread->data) , 0b111, 1); + mmu_add_vma(curr_thread, USER_STACK_BASE - USTACK_SIZE, USTACK_SIZE, (size_t)VIRT_TO_PHYS(curr_thread->stack_alloced_ptr), 0b111, 1); + mmu_add_vma(curr_thread, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0); + mmu_add_vma(curr_thread, USER_SIGNAL_WRAPPER_VA, 0x2000, (size_t)VIRT_TO_PHYS(signal_handler_wrapper), 0b101, 0); + + //memcpy(curr_thread->data, new_data, curr_thread->datasize); + struct file *f; + vfs_open(abs_path, 0, &f); + vfs_read(f, curr_thread->data, curr_thread->datasize); + vfs_close(f); + + for (int i = 0; i <= SIGNAL_MAX; i++) + { + curr_thread->signal_handler[i] = signal_default_handler; + } + + + tpf->elr_el1 = USER_KERNEL_BASE; + tpf->sp_el0 = USER_STACK_BASE; + tpf->x0 = 0; + return 0; +} + +int fork(trapframe_t *tpf) +{ + lock(); + thread_t *newt = thread_create(curr_thread->data,curr_thread->datasize); + + //copy signal handler + for (int i = 0; i <= SIGNAL_MAX;i++) + { + newt->signal_handler[i] = curr_thread->signal_handler[i]; + } + + for (int i = 0; i <= MAX_FD; i++) + { + if (curr_thread->file_descriptors_table[i]) + { + newt->file_descriptors_table[i] = kmalloc(sizeof(struct file)); + *newt->file_descriptors_table[i] = *curr_thread->file_descriptors_table[i]; + } + } + + list_head_t *pos; + vm_area_struct_t *vma; + list_for_each(pos, &curr_thread->vma_list){ + // ignore device and signal wrapper + vma = (vm_area_struct_t *)pos; + if (vma->virt_addr == USER_SIGNAL_WRAPPER_VA || vma->virt_addr == PERIPHERAL_START) + { + continue; + } + char *new_alloc = kmalloc(vma->area_size); + mmu_add_vma(newt, vma->virt_addr, vma->area_size, (size_t)VIRT_TO_PHYS(new_alloc), vma->rwx, 1); + memcpy(new_alloc, (void*)PHYS_TO_VIRT(vma->phys_addr), vma->area_size); + } + mmu_add_vma(newt, PERIPHERAL_START, PERIPHERAL_END - PERIPHERAL_START, PERIPHERAL_START, 0b011, 0); + mmu_add_vma(newt, USER_SIGNAL_WRAPPER_VA, 0x2000, (size_t)VIRT_TO_PHYS(signal_handler_wrapper), 0b101, 0); + + int parent_pid = curr_thread->pid; + + //copy stack into new process + for (int i = 0; i < KSTACK_SIZE; i++) + { + newt->kernel_stack_alloced_ptr[i] = curr_thread->kernel_stack_alloced_ptr[i]; + } + + store_context(get_current()); + //for child + if( parent_pid != curr_thread->pid) + { + goto child; + } + + void *temp_pgd = newt->context.pgd; + newt->context = curr_thread->context; + newt->context.pgd = VIRT_TO_PHYS(temp_pgd); + newt->context.fp += newt->kernel_stack_alloced_ptr - curr_thread->kernel_stack_alloced_ptr; // move fp + newt->context.sp += newt->kernel_stack_alloced_ptr - curr_thread->kernel_stack_alloced_ptr; // move kernel sp + + unlock(); + + tpf->x0 = newt->pid; + return newt->pid; + +child: + tpf->x0 = 0; + return 0; +} + +void exit(trapframe_t *tpf, int status) +{ + thread_exit(); +} + +int syscall_mbox_call(trapframe_t *tpf, unsigned char ch, unsigned int *mbox_user) +{ + lock(); + + unsigned int size_of_mbox = mbox_user[0]; + memcpy((char *)pt, mbox_user, size_of_mbox); + mbox_call(MBOX_TAGS_ARM_TO_VC, (unsigned int)((unsigned long)&pt)); + memcpy(mbox_user, (char *)pt, size_of_mbox); + + tpf->x0 = 8; + unlock(); + return 0; +} + +void kill(trapframe_t *tpf,int pid) +{ + lock(); + if (pid >= PIDMAX || pid < 0 || !threads[pid].isused) + { + unlock(); + return; + } + threads[pid].iszombie = 1; + unlock(); + schedule(); +} + +void signal_register(int signal, void (*handler)()) +{ + if (signal > SIGNAL_MAX || signal < 0)return; + + curr_thread->signal_handler[signal] = handler; +} + +void signal_kill(int pid, int signal) +{ + if (pid > PIDMAX || pid < 0 || !threads[pid].isused)return; + + lock(); + threads[pid].sigcount[signal]++; + unlock(); +} + +//only need to implement the anonymous page mapping in this Lab. +void *mmap(trapframe_t *tpf, void *addr, size_t len, int prot, int flags, int fd, int file_offset) +{ + // Ignore flags as we have demand pages + + // Req #3 Page size round up + len = len % 0x1000 ? len + (0x1000 - len % 0x1000) : len; + addr = (unsigned long)addr % 0x1000 ? addr + (0x1000 - (unsigned long)addr % 0x1000) : addr; + + // Req #2 check if overlap + list_head_t *pos; + vm_area_struct_t *vma; + vm_area_struct_t *the_area_ptr = 0; + list_for_each(pos, &curr_thread->vma_list) + { + vma = (vm_area_struct_t *)pos; + // Detect existing vma overlapped + if ( ! (vma->virt_addr >= (unsigned long)(addr + len) || vma->virt_addr + vma->area_size <= (unsigned long)addr ) ) + { + the_area_ptr = vma; + break; + } + } + // take as a hint to decide new region's start address + if (the_area_ptr) + { + tpf->x0 = (unsigned long) mmap(tpf, (void *)(the_area_ptr->virt_addr + the_area_ptr->area_size), len, prot, flags, fd, file_offset); + return (void *)tpf->x0; + } + // create new valid region, map and set the page attributes (prot) + mmu_add_vma(curr_thread, (unsigned long)addr, len, VIRT_TO_PHYS((unsigned long)kmalloc(len)), prot, 1); + tpf->x0 = (unsigned long)addr; + return (void*)tpf->x0; +} + + +int open(trapframe_t *tpf, const char *pathname, int flags) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + for (int i = 0; i < MAX_FD; i++) + { + if(!curr_thread->file_descriptors_table[i]) + { + if(vfs_open(abs_path, flags, &curr_thread->file_descriptors_table[i])!=0) + { + break; + } + + tpf->x0 = i; + return i; + } + } + + tpf->x0 = -1; + return -1; +} + +int close(trapframe_t *tpf, int fd) +{ + if(curr_thread->file_descriptors_table[fd]) + { + vfs_close(curr_thread->file_descriptors_table[fd]); + curr_thread->file_descriptors_table[fd] = 0; + tpf->x0 = 0; + return 0; + } + + tpf->x0 = -1; + return -1; +} + +long write(trapframe_t *tpf, int fd, const void *buf, unsigned long count) +{ + if (curr_thread->file_descriptors_table[fd]) + { + tpf->x0 = vfs_write(curr_thread->file_descriptors_table[fd], buf, count); + return tpf->x0; + } + + tpf->x0 = -1; + return tpf->x0; +} + +long read(trapframe_t *tpf, int fd, void *buf, unsigned long count) +{ + if (curr_thread->file_descriptors_table[fd]) + { + tpf->x0 = vfs_read(curr_thread->file_descriptors_table[fd], buf, count); + return tpf->x0; + } + + tpf->x0 = -1; + return tpf->x0; +} + +int mkdir(trapframe_t *tpf, const char *pathname, unsigned mode) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, pathname); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + tpf->x0 = vfs_mkdir(abs_path); + return tpf->x0; +} + +int mount(trapframe_t *tpf, const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, target); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + + tpf->x0 = vfs_mount(abs_path,filesystem); + return tpf->x0; +} + +int chdir(trapframe_t *tpf, const char *path) +{ + char abs_path[MAX_PATH_NAME]; + strcpy(abs_path, path); + get_absolute_path(abs_path, curr_thread->curr_working_dir); + strcpy(curr_thread->curr_working_dir, abs_path); + + return 0; +} + +long lseek64(trapframe_t *tpf, int fd, long offset, int whence) +{ + if(whence == SEEK_SET) + { + curr_thread->file_descriptors_table[fd]->f_pos = offset; + tpf->x0 = offset; + } + else + { + tpf->x0 = -1; + } + + return tpf->x0; +} +extern unsigned int height; +extern unsigned int isrgb; +extern unsigned int pitch; +extern unsigned int width; +int ioctl(trapframe_t *tpf, int fb, unsigned long request, void *info) +{ + if(request == 0) + { + struct framebuffer_info *fb_info = info; + fb_info->height = height; + fb_info->isrgb = isrgb; + fb_info->pitch = pitch; + fb_info->width = width; + } + + tpf->x0 = 0; + return tpf->x0; +} + +int sync(trapframe_t *tpf) +{ + for (int i = 0; i < MAX_FS_REG;i++) + { + if(!reg_fs[i].name) continue; + vfs_sync(®_fs[i]); + } + tpf->x0 = 0; + return tpf->x0; +} + +void sigreturn(trapframe_t *tpf) +{ + //unsigned long signal_ustack = tpf->sp_el0 % USTACK_SIZE == 0 ? tpf->sp_el0 - USTACK_SIZE : tpf->sp_el0 & (~(USTACK_SIZE - 1)); + //kfree((char*)signal_ustack); + load_context(&curr_thread->signal_saved_context); +} + + + + +char* get_file_start(char *thefilepath) +{ + char *filepath; + char *filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = CPIO_DEFAULT_START; + + while (header_pointer != 0) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + if (error) break; + if (strcmp(thefilepath, filepath) == 0) return filedata; + if (header_pointer == 0) uart_puts("execfile: %s: No such file or directory\r\n", thefilepath); + } + return 0; +} + +unsigned int get_file_size(char *thefilepath) +{ + char *filepath; + char *filedata; + unsigned int filesize; + struct cpio_newc_header *header_pointer = CPIO_DEFAULT_START; + + while (header_pointer != 0) + { + int error = cpio_newc_parse_header(header_pointer, &filepath, &filesize, &filedata, &header_pointer); + if (error) break; + if (strcmp(thefilepath, filepath) == 0) return filesize; + if (header_pointer == 0) uart_puts("execfile: %s: No such file or directory\r\n", thefilepath); + } + return 0; +} + diff --git a/lab8/kernel/src/timer.c b/lab8/kernel/src/timer.c new file mode 100644 index 000000000..d5e369ff6 --- /dev/null +++ b/lab8/kernel/src/timer.c @@ -0,0 +1,163 @@ +#include "timer.h" +#include "uart1.h" +#include "memory.h" +#include "string.h" +#include "exception.h" + +#define STR(x) #x +#define XSTR(s) STR(s) + +struct list_head *timer_event_list; + +void timer_list_init() +{ + unsigned long long tmp; + asm volatile("mrs %0, cntkctl_el1": "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0":: "r"(tmp)); + + timer_event_list = kmalloc(sizeof(list_head_t)); + INIT_LIST_HEAD(timer_event_list); +} + +void core_timer_enable() +{ + __asm__ __volatile__( + "mov x1, 1\n\t" + "msr cntp_ctl_el0, x1\n\t" // enable + + "mov x2, 2\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // unmask timer interrupt + :::"x1","x2"); +} + +void core_timer_disable() +{ + __asm__ __volatile__( + "mov x2, 0\n\t" + "ldr x1, =" XSTR(CORE0_TIMER_IRQ_CTRL) "\n\t" + "str w2, [x1]\n\t" // unmask timer interrupt + :::"x1","x2"); +} + +void timer_event_callback(timer_event_t *timer_event) +{ + ((void (*)(char *))timer_event->callback)(timer_event->args); // call the callback store in event + list_del_entry((struct list_head *)timer_event); // delete the event + kfree(timer_event->args); // kfree the arg space + kfree(timer_event); + + //set interrupt to next time_event if existing + if (!list_empty(timer_event_list)) + { + set_core_timer_interrupt_by_tick(((timer_event_t *)timer_event_list->next)->interrupt_time); + } + else + { + set_core_timer_interrupt(10000); // disable timer interrupt (set a very big value) + } +} + +void core_timer_handler() +{ + lock(); + //disable_interrupt(); + if (list_empty(timer_event_list)) + { + set_core_timer_interrupt(10000); // disable timer interrupt (set a very big value) + unlock(); + return; + } + + timer_event_callback((timer_event_t *)timer_event_list->next); // do callback and set new interrupt + unlock(); +} + +// give a string argument to callback timeout after seconds +void add_timer(void *callback, unsigned long long timeout, char *args, int bytick) +{ + timer_event_t *the_timer_event = kmalloc(sizeof(timer_event_t)); //need to kfree by event handler + + // store argument string into timer_event + the_timer_event->args = kmalloc(strlen(args) + 1); + strcpy(the_timer_event->args, args); + + if(bytick == 0) + { + the_timer_event->interrupt_time = get_tick_plus_s(timeout); // store interrupt time into timer_event + }else + { + the_timer_event->interrupt_time = get_tick_plus_s(0) + timeout; + } + + the_timer_event->callback = callback; + + // add the timer_event into timer_event_list (sorted) + struct list_head *curr; + + lock(); + list_for_each(curr, timer_event_list) + { + if (((timer_event_t *)curr)->interrupt_time > the_timer_event->interrupt_time) + { + list_add(&the_timer_event->listhead, curr->prev); // add this timer at the place just before the bigger one (sorted) + break; + } + } + + if (list_is_head(curr, timer_event_list)) + { + list_add_tail(&the_timer_event->listhead, timer_event_list); // for the time is the biggest + } + + + // set interrupt to first event + set_core_timer_interrupt_by_tick(((timer_event_t *)timer_event_list->next)->interrupt_time); + unlock(); +} + + +// get cpu tick add some second +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 now + + 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); +} + +// set timer interrupt time to [expired_time] seconds after now (relatively) +void set_core_timer_interrupt(unsigned long long expired_time) +{ + __asm__ __volatile__( + "mrs x1, cntfrq_el0\n\t" //cntfrq_el0 -> relative time + "mul x1, x1, %0\n\t" + "msr cntp_tval_el0, x1\n\t" // set expired time + :: "r"(expired_time):"x1"); +} + +// directly set timer interrupt time to a cpu tick (directly) +void set_core_timer_interrupt_by_tick(unsigned long long tick) +{ + __asm__ __volatile__( + "msr cntp_cval_el0, %0\n\t" //cntp_cval_el0 -> absolute time + :: "r"(tick)); +} + +int timer_list_get_size() +{ + int r = 0; + struct list_head *curr; + list_for_each(curr, timer_event_list) + { + r++; + } + return r; +} diff --git a/lab8/kernel/src/tmpfs.c b/lab8/kernel/src/tmpfs.c new file mode 100644 index 000000000..a0f571c27 --- /dev/null +++ b/lab8/kernel/src/tmpfs.c @@ -0,0 +1,212 @@ +#include "tmpfs.h" +#include "vfs.h" +#include "string.h" +#include "memory.h" +#include "uart1.h" + +struct file_operations tmpfs_file_operations = {tmpfs_write,tmpfs_read,tmpfs_open,tmpfs_close,tmpfs_lseek64,tmpfs_getsize}; +struct vnode_operations tmpfs_vnode_operations = {tmpfs_lookup,tmpfs_create,tmpfs_mkdir}; + +int register_tmpfs() +{ + struct filesystem fs; + fs.name = "tmpfs"; + fs.setup_mount = tmpfs_setup_mount; + fs.sync = tmpfs_sync; + return register_filesystem(&fs); +} + +int tmpfs_setup_mount(struct filesystem *fs, struct mount *_mount) +{ + _mount->fs = fs; + _mount->root = tmpfs_create_vnode(0,dir_t); + return 0; +} + +struct vnode* tmpfs_create_vnode(struct mount* _mount, enum fsnode_type type) +{ + struct vnode *v = kmalloc(sizeof(struct vnode)); + v->f_ops = &tmpfs_file_operations; + v->v_ops = &tmpfs_vnode_operations; + v->mount = 0; + struct tmpfs_inode* inode = kmalloc(sizeof(struct tmpfs_inode)); + memset(inode, 0, sizeof(struct tmpfs_inode)); + inode->type = type; + inode->data = kmalloc(0x1000); + v->internal = inode; + return v; +} + +// file operations +int tmpfs_write(struct file *file, const void *buf, size_t len) +{ + struct tmpfs_inode *inode = file->vnode->internal; + // write from f_pos + memcpy(inode->data + file->f_pos, buf, len); + // update f_pos and size + file->f_pos += len; + if(inode->datasize < file->f_pos) inode->datasize = file->f_pos; + return len; +} + +int tmpfs_read(struct file *file, void *buf, size_t len) +{ + struct tmpfs_inode *inode = file->vnode->internal; + // if buffer overflow, shrink the request read length + // read from f_pos + if(len+file->f_pos > inode->datasize) + { + len = inode->datasize - file->f_pos; + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += inode->datasize - file->f_pos; + return len; + } + else + { + memcpy(buf, inode->data + file->f_pos, len); + file->f_pos += len; + return len; + } + return -1; +} + +int tmpfs_open(struct vnode *file_node, struct file **target) +{ + (*target)->vnode = file_node; + (*target)->f_ops = file_node->f_ops; + (*target)->f_pos = 0; + return 0; +} + +int tmpfs_close(struct file *file) +{ + kfree(file); + return 0; +} + +long tmpfs_lseek64(struct file *file, long offset, int whence) +{ + if(whence == SEEK_SET) + { + file->f_pos = offset; + return file->f_pos; + } + return -1; +} + + +// vnode operations +int tmpfs_lookup(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct tmpfs_inode *dir_inode = dir_node->internal; + int child_idx = 0; + // BFS search tree + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + struct vnode *vnode = dir_inode->entry[child_idx]; + if(!vnode) break; + struct tmpfs_inode *inode = vnode->internal; + if (strcmp(component_name, inode->name) == 0) + { + *target = vnode; + return 0; + } + } + return -1; +} + + +int tmpfs_create(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct tmpfs_inode *inode = dir_node->internal; + if(inode->type!=dir_t) + { + uart_sendline("tmpfs create not dir_t\r\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) break; + struct tmpfs_inode *child_inode = inode->entry[child_idx]->internal; + if (strcmp(child_inode->name,component_name)==0) + { + uart_sendline("tmpfs create file exists\r\n"); + return -1; + } + } + + if (child_idx > MAX_DIR_ENTRY) + { + uart_sendline("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) + { + uart_sendline("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode *_vnode = tmpfs_create_vnode(0, file_t); + inode->entry[child_idx] = _vnode; + + struct tmpfs_inode *newinode = _vnode->internal; + strcpy(newinode->name, component_name); + + *target = _vnode; + return 0; +} + +int tmpfs_mkdir(struct vnode *dir_node, struct vnode **target, const char *component_name) +{ + struct tmpfs_inode *inode = dir_node->internal; + + if (inode->type != dir_t) + { + uart_sendline("tmpfs mkdir not dir_t\r\n"); + return -1; + } + + int child_idx = 0; + for (; child_idx <= MAX_DIR_ENTRY; child_idx++) + { + if (!inode->entry[child_idx]) + { + break; + } + } + + if(child_idx > MAX_DIR_ENTRY) + { + uart_sendline("DIR ENTRY FULL\r\n"); + return -1; + } + + if (strlen(component_name) > MAX_FILE_NAME) + { + uart_sendline("FILE NAME TOO LONG\r\n"); + return -1; + } + + struct vnode* _vnode = tmpfs_create_vnode(0, dir_t); + inode->entry[child_idx] = _vnode; + + struct tmpfs_inode *newinode = _vnode->internal; + strcpy(newinode->name, component_name); + + *target = _vnode; + return 0; +} + +long tmpfs_getsize(struct vnode* vd) +{ + struct tmpfs_inode *inode = vd->internal; + return inode->datasize; +} + +int tmpfs_sync(struct filesystem *fs) +{ + return 0; +} diff --git a/lab8/kernel/src/uart1.c b/lab8/kernel/src/uart1.c new file mode 100644 index 000000000..c2cf5fff3 --- /dev/null +++ b/lab8/kernel/src/uart1.c @@ -0,0 +1,172 @@ +#include "bcm2837/rpi_gpio.h" +#include "bcm2837/rpi_uart1.h" +#include "bcm2837/rpi_irq.h" +#include "uart1.h" +#include "exception.h" +#include "string.h" + +//implement first in first out buffer with a read index and a write index +char uart_tx_buffer[VSPRINT_MAX_BUF_SIZE]={}; +unsigned int uart_tx_buffer_widx = 0; //write index +unsigned int uart_tx_buffer_ridx = 0; //read index +char uart_rx_buffer[VSPRINT_MAX_BUF_SIZE]={}; +unsigned int uart_rx_buffer_widx = 0; +unsigned int uart_rx_buffer_ridx = 0; + +int uart_recv_echo_flag = 1; + +void uart_init() +{ + register unsigned int r; + + /* initialize UART */ + *AUX_ENABLES |= 1; // enable UART1 + *AUX_MU_CNTL_REG = 0; // disable TX/RX + + /* configure UART */ + *AUX_MU_IER_REG = 0; // disable interrupt + *AUX_MU_LCR_REG = 3; // 8 bit data size + *AUX_MU_MCR_REG = 0; // disable flow control + *AUX_MU_BAUD_REG = 270; // 115200 baud rate + *AUX_MU_IIR_REG = 0xC6; // disable FIFO + + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~(7<<12); // clean gpio14 + r |= 2<<12; // set gpio14 to alt5 + r &= ~(7<<15); // clean gpio15 + r |= 2<<15; // set gpio15 to alt5 + *GPFSEL1 = r; + + /* enable pin 14, 15 - ref: Page 101 */ + *GPPUD = 0; + r=150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = (1<<14)|(1<<15); + r=150; while(r--) { asm volatile("nop"); } + *GPPUDCLK0 = 0; + + *AUX_MU_CNTL_REG = 3; // enable TX/RX +} + +char uart_recv() { + char r; + while(!(*AUX_MU_LSR_REG & 0x01)){}; + r = (char)(*AUX_MU_IO_REG); + if(uart_recv_echo_flag){ + uart_send(r); + if(r =='\r') {uart_send('\r');uart_send('\n');} + } + return r=='\r'?'\n':r; +} + +void uart_send(char c) { + while(!(*AUX_MU_LSR_REG & 0x20)){}; + *AUX_MU_IO_REG = c; +} + +void uart_2hex(unsigned int d) { + unsigned int n; + int c; + for(c=28;c>=0;c-=4) { + n=(d>>c)&0xF; + n+=n>9?0x37:0x30; + uart_send(n); + } +} + +int uart_sendline(char* fmt, ...) { + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[VSPRINT_MAX_BUF_SIZE]; + + char *str = (char*)buf; + int count = vsprintf(str,fmt,args); + + while(*str) { + if(*str=='\n') + uart_send('\r'); + uart_send(*str++); + } + __builtin_va_end(args); + return count; +} + + +// uart_async_getc read from buffer +// uart_r_irq_handler write to buffer then output +char uart_async_getc() { + *AUX_MU_IER_REG |=1; // enable read interrupt + // do while if buffer empty + while (uart_rx_buffer_ridx == uart_rx_buffer_widx) *AUX_MU_IER_REG |=1; // enable read interrupt + lock(); + char r = uart_rx_buffer[uart_rx_buffer_ridx++]; + if (uart_rx_buffer_ridx >= VSPRINT_MAX_BUF_SIZE) uart_rx_buffer_ridx = 0; + unlock(); + return r; +} + + +// uart_async_putc writes to buffer +// uart_w_irq_handler read from buffer then output +void uart_async_putc(char c) { + // if buffer full, wait for uart_w_irq_handler + while( (uart_tx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_tx_buffer_ridx ) *AUX_MU_IER_REG |=2; // enable write interrupt + lock(); + uart_tx_buffer[uart_tx_buffer_widx++] = c; + if(uart_tx_buffer_widx >= VSPRINT_MAX_BUF_SIZE) uart_tx_buffer_widx=0; // cycle pointer + unlock(); + *AUX_MU_IER_REG |=2; // enable write interrupt +} + +int uart_puts(char* fmt, ...) { + __builtin_va_list args; + __builtin_va_start(args, fmt); + char buf[VSPRINT_MAX_BUF_SIZE]; + + char *str = (char*)buf; + int count = vsprintf(str,fmt,args); + + while(*str) { + if(*str=='\n') + uart_async_putc('\r'); + uart_async_putc(*str++); + } + __builtin_va_end(args); + return count; +} + +// AUX_MU_IER_REG -> BCM2837-ARM-Peripherals.pdf - Pg.12 +void uart_interrupt_enable(){ + *AUX_MU_IER_REG |=1; // enable read interrupt + *AUX_MU_IER_REG |=2; // enable write interrupt + *ENABLE_IRQS_1 |= 1 << 29; // Pg.112 +} + +void uart_interrupt_disable(){ + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + *AUX_MU_IER_REG &= ~(2); // disable write interrupt +} + + +void uart_r_irq_handler(){ + if((uart_rx_buffer_widx + 1) % VSPRINT_MAX_BUF_SIZE == uart_rx_buffer_ridx) + { + *AUX_MU_IER_REG &= ~(1); // disable read interrupt + return; + } + uart_rx_buffer[uart_rx_buffer_widx++] = uart_recv(); + if(uart_rx_buffer_widx>=VSPRINT_MAX_BUF_SIZE) uart_rx_buffer_widx=0; + *AUX_MU_IER_REG |=1; +} + +void uart_w_irq_handler(){ + if(uart_tx_buffer_ridx == uart_tx_buffer_widx) + { + *AUX_MU_IER_REG &= ~(2); // disable write interrupt + return; // buffer empty + } + uart_send(uart_tx_buffer[uart_tx_buffer_ridx++]); + if(uart_tx_buffer_ridx>=VSPRINT_MAX_BUF_SIZE) uart_tx_buffer_ridx=0; + *AUX_MU_IER_REG |=2; // enable write interrupt +} + diff --git a/lab8/kernel/src/vfs.c b/lab8/kernel/src/vfs.c new file mode 100644 index 000000000..fff02c0ca --- /dev/null +++ b/lab8/kernel/src/vfs.c @@ -0,0 +1,343 @@ +#include "vfs.h" +#include "tmpfs.h" +#include "memory.h" +#include "string.h" +#include "uart1.h" +#include "initramfs.h" +#include "dev_uart.h" +#include "dev_framebuffer.h" +#include "sdhost.h" +#include "fat32.h" + +struct mount *rootfs; +struct filesystem reg_fs[MAX_FS_REG]; +struct file_operations reg_dev[MAX_DEV_REG]; + +int register_filesystem(struct filesystem *fs) +{ + for (int i = 0; i < MAX_FS_REG;i++) + { + if(!reg_fs[i].name) + { + reg_fs[i].name = fs->name; + reg_fs[i].setup_mount = fs->setup_mount; + reg_fs[i].sync = fs->sync; + return i; + } + } + return -1; +} + +int register_dev(struct file_operations *fo) +{ + for (int i = 0; i < MAX_FS_REG; i++) + { + if (!reg_dev[i].open) + { + // return unique id for the assigned device + reg_dev[i] = *fo; + return i; + } + } + return -1; +} + +struct filesystem* find_filesystem(const char* fs_name) +{ + for (int i = 0; i < MAX_FS_REG; i++) + { + if (strcmp(reg_fs[i].name,fs_name)==0) + { + return ®_fs[i]; + } + } + return 0; +} + + + +int vfs_open(const char *pathname, int flags, struct file **target) +{ + // 1. Lookup pathname + // 3. Create a new file if O_CREAT is specified in flags and vnode not found + struct vnode *node; + if (vfs_lookup(pathname, &node) != 0 && (flags & O_CREAT)) + { + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) + { + if(pathname[i]=='/') + { + last_slash_idx = i; + } + } + + char dirname[MAX_PATH_NAME+1]; + strcpy(dirname, pathname); + dirname[last_slash_idx] = 0; + if (vfs_lookup(dirname,&node)!=0) + { + uart_sendline("cannot ocreate no dir name\r\n"); + return -1; + } + node->v_ops->create(node, &node, pathname+last_slash_idx+1); + *target = kmalloc(sizeof(struct file)); + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + else // 2. Create a new file handle for this vnode if found. + { + *target = kmalloc(sizeof(struct file)); + node->f_ops->open(node, target); + (*target)->flags = flags; + return 0; + } + + // lookup error code shows if file exist or not or other error occurs + // 4. Return error code if fails + return -1; +} + +int vfs_close(struct file *file) +{ + // 1. release the file handle + // 2. Return error code if fails + file->f_ops->close(file); + return 0; +} + +int vfs_write(struct file *file, const void *buf, size_t len) +{ + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file,buf,len); +} + +int vfs_read(struct file *file, void *buf, size_t len) +{ + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 2. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +int vfs_mkdir(const char *pathname) +{ + char dirname[MAX_PATH_NAME] = {}; // before add folder + char newdirname[MAX_PATH_NAME] = {}; // after add folder + + // search for last directory + int last_slash_idx = 0; + for (int i = 0; i < strlen(pathname); i++) + { + if (pathname[i] == '/') + { + last_slash_idx = i; + } + } + + memcpy(dirname, pathname, last_slash_idx); + strcpy(newdirname, pathname + last_slash_idx + 1); + + // create new directory if upper directory is found + struct vnode *node; + if(vfs_lookup(dirname,&node)==0) + { + node->v_ops->mkdir(node,&node,newdirname); + return 0; + } + + uart_sendline("vfs_mkdir cannot find pathname"); + return -1; +} + +int vfs_mount(const char *target, const char *filesystem) +{ + struct vnode *dirnode; + // search for the target filesystem + struct filesystem *fs = find_filesystem(filesystem); + if(!fs) + { + uart_sendline("vfs_mount cannot find filesystem\r\n"); + return -1; + } + // get its upper directory, if no, create new mountpoint + if(vfs_lookup(target, &dirnode)==-1) + { + uart_sendline("vfs_mount cannot find dir\r\n"); + return -1; + }else + { + dirnode->mount = kmalloc(sizeof(struct mount)); + fs->setup_mount(fs, dirnode->mount); + } + return 0; +} + +int vfs_lookup(const char *pathname, struct vnode **target) +{ + int is_fat = 0; + if (pathname[1]=='b' && pathname[2]=='o' && pathname[3]=='o' && pathname[4]=='t') + is_fat = 1; + // if no path input, return root + if(strlen(pathname)==0) + { + *target = rootfs->root; + return 0; + } + + struct vnode *dirnode = rootfs->root; + char component_name[MAX_FILE_NAME+1] = {}; + int c_idx = 0; + // deal with directory + for (int i = 1; i < strlen(pathname); i++) + { + if (pathname[i] == '/') + { + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) return -1; + // redirect to mounted filesystem + while (dirnode->mount) + { + dirnode = dirnode->mount->root; + if(is_fat) break; + } + c_idx = 0; + } + else + { + component_name[c_idx++] = pathname[i]; + } + } + + // deal with file + component_name[c_idx++] = 0; + // if fs's v_ops error, return -1 + if (dirnode->v_ops->lookup(dirnode, &dirnode, component_name) != 0) return -1; + // redirect to mounted filesystem + while (dirnode->mount) + { + if(is_fat) break; + dirnode = dirnode->mount->root; + } + // return file's vnode + *target = dirnode; + + return 0; +} + +// for device operations only +int vfs_mknod(char* pathname, int id) +{ + struct file* f = kmalloc(sizeof(struct file)); + // create leaf and its file operations + vfs_open(pathname, O_CREAT, &f); + f->vnode->f_ops = ®_dev[id]; + vfs_close(f); + return 0; +} + +int vfs_sync(struct filesystem *fs) +{ + return fs->sync(fs); +} + +void init_rootfs() +{ + // sd_init + sd_init(); + + // tmpfs + int idx = register_tmpfs(); + rootfs = kmalloc(sizeof(struct mount)); + reg_fs[idx].setup_mount(®_fs[idx], rootfs); + + // initramfs + vfs_mkdir("/initramfs"); + register_initramfs(); + vfs_mount("/initramfs","initramfs"); + + // fat32 + vfs_mkdir("/boot"); + register_fat32(); + vfs_mount("/boot", "fat32"); + + // dev_fs + vfs_mkdir("/dev"); + int uart_id = init_dev_uart(); + vfs_mknod("/dev/uart", uart_id); + int framebuffer_id = init_dev_framebuffer(); + vfs_mknod("/dev/framebuffer", framebuffer_id); +} + +void vfs_test() +{ + // test read/write + vfs_mkdir("/lll"); + vfs_mkdir("/lll/ddd"); + // test mount + vfs_mount("/lll/ddd", "tmpfs"); + + struct file* testfilew; + struct file *testfiler; + char testbufw[0x30] = "ABCDEABBBBBBDDDDDDDDDDD"; + char testbufr[0x30] = {}; + vfs_open("/lll/ddd/ggg", O_CREAT, &testfilew); + vfs_open("/lll/ddd/ggg", O_CREAT, &testfiler); + vfs_write(testfilew, testbufw, 10); + vfs_read(testfiler, testbufr, 10); + uart_sendline("%s",testbufr); + + struct file *testfile_initramfs; + vfs_open("/initramfs/get_simpleexec.sh", O_CREAT, &testfile_initramfs); + vfs_read(testfile_initramfs, testbufr, 30); + uart_sendline("%s", testbufr); +} + +char *get_absolute_path(char *path, char *curr_working_dir) +{ + // if relative path -> add root path + if(path[0] != '/') + { + char tmp[MAX_PATH_NAME]; + strcpy(tmp, curr_working_dir); + if(strcmp(curr_working_dir,"/")!=0)strcat(tmp, "/"); + strcat(tmp, path); + strcpy(path, tmp); + } + + char absolute_path[MAX_PATH_NAME+1] = {}; + int idx = 0; + for (int i = 0; i < strlen(path); i++) + { + // meet /.. + if (path[i] == '/' && path[i+1] == '.' && path[i+2] == '.') + { + for (int j = idx; j >= 0;j--) + { + if(absolute_path[j] == '/') + { + absolute_path[j] = 0; + idx = j; + } + } + i += 2; + continue; + } + + // ignore /. + if (path[i] == '/' && path[i+1] == '.') + { + i++; + continue; + } + + absolute_path[idx++] = path[i]; + } + absolute_path[idx] = 0; + + return strcpy(path, absolute_path); +} + diff --git a/lab8/rootfs/file0.txt b/lab8/rootfs/file0.txt new file mode 100644 index 000000000..c2e1803c9 --- /dev/null +++ b/lab8/rootfs/file0.txt @@ -0,0 +1,2 @@ +[This is file 0] +[Hello World!] diff --git a/lab8/rootfs/file1.txt b/lab8/rootfs/file1.txt new file mode 100644 index 000000000..5d585c1ac --- /dev/null +++ b/lab8/rootfs/file1.txt @@ -0,0 +1,11 @@ +{this is file1} +. +.F +.I +.L +.E +. +.1 +. + + diff --git a/lab8/rootfs/vfs1.img b/lab8/rootfs/vfs1.img new file mode 100644 index 000000000..2c41bd71a Binary files /dev/null and b/lab8/rootfs/vfs1.img differ diff --git a/lab8/rootfs/vfs2.img b/lab8/rootfs/vfs2.img new file mode 100644 index 000000000..f7baf4607 Binary files /dev/null and b/lab8/rootfs/vfs2.img differ diff --git a/lab8/rootfs/vm.img b/lab8/rootfs/vm.img new file mode 100644 index 000000000..8ba674e6c Binary files /dev/null and b/lab8/rootfs/vm.img differ