From efef2bb3fce534114bfc09fa37d88df1047557eb Mon Sep 17 00:00:00 2001 From: kelee Date: Wed, 7 Apr 2021 23:15:06 +0800 Subject: [PATCH] init --- .gitignore | 81 + .gitmodules | 3 + README.md | 44 + lab8/.gdb.in | 3 + lab8/CMakeLists.txt | 169 + lab8/Makefile | 350 ++ lab8/giveitatry.pyq | 0 lab8/kern/debug/assert.h | 27 + lab8/kern/debug/kdebug.c | 92 + lab8/kern/debug/kdebug.h | 12 + lab8/kern/debug/kmonitor.c | 132 + lab8/kern/debug/kmonitor.h | 19 + lab8/kern/debug/panic.c | 52 + lab8/kern/debug/stab.h | 54 + lab8/kern/driver/clock.c | 43 + lab8/kern/driver/clock.h | 11 + lab8/kern/driver/console.c | 102 + lab8/kern/driver/console.h | 11 + lab8/kern/driver/fpioa.c | 5288 +++++++++++++++++ lab8/kern/driver/fpioa.h | 458 ++ lab8/kern/driver/gpiohs.c | 67 + lab8/kern/driver/gpiohs.h | 157 + lab8/kern/driver/ide.c | 45 + lab8/kern/driver/ide.h | 36 + lab8/kern/driver/intr.c | 11 + lab8/kern/driver/intr.h | 8 + lab8/kern/driver/io.h | 39 + lab8/kern/driver/kbdreg.h | 84 + lab8/kern/driver/picirq.c | 6 + lab8/kern/driver/picirq.h | 8 + lab8/kern/driver/ramdisk.c | 62 + lab8/kern/driver/ramdisk.h | 3 + lab8/kern/driver/sdcard.c | 582 ++ lab8/kern/driver/sdcard.h | 166 + lab8/kern/driver/spi.c | 195 + lab8/kern/driver/spi.h | 210 + lab8/kern/driver/sysctl.c | 971 +++ lab8/kern/driver/sysctl.h | 940 +++ lab8/kern/fs/devs/dev.c | 167 + lab8/kern/fs/devs/dev.h | 31 + lab8/kern/fs/devs/dev_disk0.c | 144 + lab8/kern/fs/devs/dev_stdin.c | 144 + lab8/kern/fs/devs/dev_stdout.c | 64 + lab8/kern/fs/file.c | 352 ++ lab8/kern/fs/file.h | 62 + lab8/kern/fs/fs.c | 99 + lab8/kern/fs/fs.h | 61 + lab8/kern/fs/iobuf.c | 77 + lab8/kern/fs/iobuf.h | 24 + lab8/kern/fs/sfs/bitmap.c | 114 + lab8/kern/fs/sfs/bitmap.h | 32 + lab8/kern/fs/sfs/sfs.c | 19 + lab8/kern/fs/sfs/sfs.h | 129 + lab8/kern/fs/sfs/sfs_fs.c | 258 + lab8/kern/fs/sfs/sfs_inode.c | 1032 ++++ lab8/kern/fs/sfs/sfs_io.c | 167 + lab8/kern/fs/sfs/sfs_lock.c | 44 + lab8/kern/fs/swap/swapfs.c | 27 + lab8/kern/fs/swap/swapfs.h | 12 + lab8/kern/fs/sysfile.c | 317 + lab8/kern/fs/sysfile.h | 28 + lab8/kern/fs/vfs/inode.c | 116 + lab8/kern/fs/vfs/inode.h | 248 + lab8/kern/fs/vfs/vfs.c | 97 + lab8/kern/fs/vfs/vfs.h | 191 + lab8/kern/fs/vfs/vfsdev.c | 309 + lab8/kern/fs/vfs/vfsfile.c | 110 + lab8/kern/fs/vfs/vfslookup.c | 101 + lab8/kern/fs/vfs/vfspath.c | 126 + lab8/kern/init/entry.S | 67 + lab8/kern/init/init.c | 106 + lab8/kern/libs/readline.c | 50 + lab8/kern/libs/stdio.c | 78 + lab8/kern/libs/string.c | 26 + lab8/kern/libs/util.c | 26 + lab8/kern/mm/default_pmm.c | 306 + lab8/kern/mm/default_pmm.h | 9 + lab8/kern/mm/kmalloc.c | 305 + lab8/kern/mm/kmalloc.h | 16 + lab8/kern/mm/memlayout.h | 133 + lab8/kern/mm/mmu.h | 92 + lab8/kern/mm/pmm.c | 773 +++ lab8/kern/mm/pmm.h | 167 + lab8/kern/mm/swap.c | 289 + lab8/kern/mm/swap.h | 62 + lab8/kern/mm/swap_fifo.c | 147 + lab8/kern/mm/swap_fifo.h | 7 + lab8/kern/mm/vmm.c | 524 ++ lab8/kern/mm/vmm.h | 110 + lab8/kern/process/entry.S | 7 + lab8/kern/process/proc.c | 1120 ++++ lab8/kern/process/proc.h | 106 + lab8/kern/process/switch.S | 39 + lab8/kern/schedule/default_sched.h | 9 + lab8/kern/schedule/default_sched_c | 58 + lab8/kern/schedule/default_sched_stride.c | 159 + lab8/kern/schedule/sched.c | 176 + lab8/kern/schedule/sched.h | 73 + lab8/kern/sync/check_sync.c | 271 + lab8/kern/sync/monitor.c | 81 + lab8/kern/sync/monitor.h | 92 + lab8/kern/sync/sem.c | 77 + lab8/kern/sync/sem.h | 19 + lab8/kern/sync/sync.h | 29 + lab8/kern/sync/wait.c | 122 + lab8/kern/sync/wait.h | 48 + lab8/kern/syscall/syscall.c | 195 + lab8/kern/syscall/syscall.h | 7 + lab8/kern/trap/trap.c | 295 + lab8/kern/trap/trap.h | 56 + lab8/kern/trap/trapentry.S | 222 + lab8/lab5.md | 62 + lab8/libs/atomic.h | 109 + lab8/libs/defs.h | 84 + lab8/libs/dirent.h | 13 + lab8/libs/elf.h | 48 + lab8/libs/error.h | 33 + lab8/libs/hash.c | 18 + lab8/libs/list.h | 163 + lab8/libs/printfmt.c | 359 ++ lab8/libs/rand.c | 26 + lab8/libs/riscv.h | 1501 +++++ lab8/libs/sbi.h | 105 + lab8/libs/skew_heap.h | 87 + lab8/libs/stat.h | 27 + lab8/libs/stdarg.h | 12 + lab8/libs/stdio.h | 25 + lab8/libs/stdlib.h | 17 + lab8/libs/string.c | 379 ++ lab8/libs/string.h | 28 + lab8/libs/unistd.h | 68 + lab8/libs/util.h | 15 + lab8/tools/boot.ld | 15 + lab8/tools/function.mk | 95 + lab8/tools/gdbinit | 3 + lab8/tools/grade.sh | 560 ++ lab8/tools/kernel.ld | 51 + lab8/tools/kflash.py | 1548 +++++ lab8/tools/mksfs.c | 582 ++ lab8/tools/rustsbi-k210.bin | Bin 0 -> 72032 bytes lab8/tools/sign.c | 43 + lab8/tools/user.ld | 39 + lab8/tools/vector.c | 29 + lab8/user/badarg.c | 22 + lab8/user/badsegment.c | 11 + lab8/user/divzero.c | 11 + lab8/user/exit.c | 34 + lab8/user/faultread.c | 9 + lab8/user/faultreadkernel.c | 9 + lab8/user/forktest.c | 34 + lab8/user/forktree.c | 37 + lab8/user/hello.c | 11 + lab8/user/libs/dir.c | 46 + lab8/user/libs/dir.h | 19 + lab8/user/libs/file.c | 68 + lab8/user/libs/file.h | 23 + lab8/user/libs/initcode.S | 13 + lab8/user/libs/lock.h | 42 + lab8/user/libs/panic.c | 28 + lab8/user/libs/stdio.c | 89 + lab8/user/libs/syscall.c | 152 + lab8/user/libs/syscall.h | 33 + lab8/user/libs/ulib.c | 72 + lab8/user/libs/ulib.h | 40 + lab8/user/libs/umain.c | 34 + lab8/user/matrix.c | 84 + lab8/user/pgdir.c | 11 + lab8/user/priority.c | 77 + lab8/user/sh.c | 254 + lab8/user/sleep.c | 28 + lab8/user/sleepkill.c | 18 + lab8/user/softint.c | 10 + lab8/user/spin.c | 29 + lab8/user/testbss.c | 33 + lab8/user/waitkill.c | 59 + lab8/user/yield.c | 16 + lab8_practice/.gdb.in | 3 + lab8_practice/Makefile | 335 ++ lab8_practice/disk0/badarg | Bin 0 -> 9368 bytes lab8_practice/disk0/badsegment | Bin 0 -> 8800 bytes lab8_practice/disk0/divzero | Bin 0 -> 8960 bytes lab8_practice/disk0/exit | Bin 0 -> 10216 bytes lab8_practice/disk0/faultread | Bin 0 -> 8648 bytes lab8_practice/disk0/faultreadkernel | Bin 0 -> 8856 bytes lab8_practice/disk0/forktest | Bin 0 -> 9168 bytes lab8_practice/disk0/forktree | Bin 0 -> 9344 bytes lab8_practice/disk0/hello | Bin 0 -> 8808 bytes lab8_practice/disk0/matrix | Bin 0 -> 10824 bytes lab8_practice/disk0/pgdir | Bin 0 -> 8856 bytes lab8_practice/disk0/priority | Bin 0 -> 10040 bytes lab8_practice/disk0/sh | Bin 0 -> 12104 bytes lab8_practice/disk0/sleep | Bin 0 -> 9376 bytes lab8_practice/disk0/sleepkill | Bin 0 -> 9160 bytes lab8_practice/disk0/softint | Bin 0 -> 8640 bytes lab8_practice/disk0/spin | Bin 0 -> 9512 bytes lab8_practice/disk0/testbss | Bin 0 -> 9264 bytes lab8_practice/disk0/waitkill | Bin 0 -> 9848 bytes lab8_practice/disk0/yield | Bin 0 -> 8992 bytes lab8_practice/giveitatry.pyq | 0 lab8_practice/kern/debug/assert.h | 27 + lab8_practice/kern/debug/kdebug.c | 92 + lab8_practice/kern/debug/kdebug.h | 12 + lab8_practice/kern/debug/kmonitor.c | 132 + lab8_practice/kern/debug/kmonitor.h | 19 + lab8_practice/kern/debug/panic.c | 52 + lab8_practice/kern/debug/stab.h | 54 + lab8_practice/kern/driver/clock.c | 43 + lab8_practice/kern/driver/clock.h | 11 + lab8_practice/kern/driver/console.c | 102 + lab8_practice/kern/driver/console.h | 11 + lab8_practice/kern/driver/ide.c | 45 + lab8_practice/kern/driver/ide.h | 36 + lab8_practice/kern/driver/intr.c | 8 + lab8_practice/kern/driver/intr.h | 8 + lab8_practice/kern/driver/kbdreg.h | 84 + lab8_practice/kern/driver/picirq.c | 6 + lab8_practice/kern/driver/picirq.h | 8 + lab8_practice/kern/driver/ramdisk.c | 62 + lab8_practice/kern/driver/ramdisk.h | 3 + lab8_practice/kern/fs/devs/dev.c | 167 + lab8_practice/kern/fs/devs/dev.h | 31 + lab8_practice/kern/fs/devs/dev_disk0.c | 144 + lab8_practice/kern/fs/devs/dev_stdin.c | 126 + lab8_practice/kern/fs/devs/dev_stdout.c | 64 + lab8_practice/kern/fs/file.c | 352 ++ lab8_practice/kern/fs/file.h | 62 + lab8_practice/kern/fs/fs.c | 99 + lab8_practice/kern/fs/fs.h | 61 + lab8_practice/kern/fs/iobuf.c | 77 + lab8_practice/kern/fs/iobuf.h | 24 + lab8_practice/kern/fs/sfs/bitmap.c | 114 + lab8_practice/kern/fs/sfs/bitmap.h | 32 + lab8_practice/kern/fs/sfs/sfs.c | 19 + lab8_practice/kern/fs/sfs/sfs.h | 129 + lab8_practice/kern/fs/sfs/sfs_fs.c | 258 + lab8_practice/kern/fs/sfs/sfs_inode.c | 988 +++ lab8_practice/kern/fs/sfs/sfs_io.c | 167 + lab8_practice/kern/fs/sfs/sfs_lock.c | 44 + lab8_practice/kern/fs/swap/swapfs.c | 27 + lab8_practice/kern/fs/swap/swapfs.h | 12 + lab8_practice/kern/fs/sysfile.c | 317 + lab8_practice/kern/fs/sysfile.h | 28 + lab8_practice/kern/fs/vfs/inode.c | 116 + lab8_practice/kern/fs/vfs/inode.h | 248 + lab8_practice/kern/fs/vfs/vfs.c | 97 + lab8_practice/kern/fs/vfs/vfs.h | 191 + lab8_practice/kern/fs/vfs/vfsdev.c | 309 + lab8_practice/kern/fs/vfs/vfsfile.c | 110 + lab8_practice/kern/fs/vfs/vfslookup.c | 101 + lab8_practice/kern/fs/vfs/vfspath.c | 126 + lab8_practice/kern/init/entry.S | 55 + lab8_practice/kern/init/init.c | 103 + lab8_practice/kern/libs/readline.c | 50 + lab8_practice/kern/libs/stdio.c | 78 + lab8_practice/kern/libs/string.c | 26 + lab8_practice/kern/mm/default_pmm.c | 306 + lab8_practice/kern/mm/default_pmm.h | 9 + lab8_practice/kern/mm/kmalloc.c | 305 + lab8_practice/kern/mm/kmalloc.h | 16 + lab8_practice/kern/mm/memlayout.h | 129 + lab8_practice/kern/mm/mmu.h | 91 + lab8_practice/kern/mm/pmm.c | 625 ++ lab8_practice/kern/mm/pmm.h | 164 + lab8_practice/kern/mm/swap.c | 285 + lab8_practice/kern/mm/swap.h | 65 + lab8_practice/kern/mm/swap_fifo.c | 147 + lab8_practice/kern/mm/swap_fifo.h | 7 + lab8_practice/kern/mm/vmm.c | 522 ++ lab8_practice/kern/mm/vmm.h | 110 + lab8_practice/kern/process/entry.S | 7 + lab8_practice/kern/process/proc.c | 959 +++ lab8_practice/kern/process/proc.h | 106 + lab8_practice/kern/process/switch.S | 39 + lab8_practice/kern/schedule/default_sched.h | 9 + lab8_practice/kern/schedule/default_sched_c | 58 + .../kern/schedule/default_sched_stride.c | 159 + lab8_practice/kern/schedule/sched.c | 176 + lab8_practice/kern/schedule/sched.h | 73 + lab8_practice/kern/sync/check_sync.c | 264 + lab8_practice/kern/sync/monitor.c | 75 + lab8_practice/kern/sync/monitor.h | 90 + lab8_practice/kern/sync/sem.c | 77 + lab8_practice/kern/sync/sem.h | 19 + lab8_practice/kern/sync/sync.h | 29 + lab8_practice/kern/sync/wait.c | 122 + lab8_practice/kern/sync/wait.h | 48 + lab8_practice/kern/syscall/syscall.c | 195 + lab8_practice/kern/syscall/syscall.h | 7 + lab8_practice/kern/trap/trap.c | 296 + lab8_practice/kern/trap/trap.h | 56 + lab8_practice/kern/trap/trapentry.S | 139 + lab8_practice/lab5.md | 62 + lab8_practice/libs/atomic.h | 109 + lab8_practice/libs/defs.h | 84 + lab8_practice/libs/dirent.h | 13 + lab8_practice/libs/elf.h | 48 + lab8_practice/libs/error.h | 33 + lab8_practice/libs/hash.c | 18 + lab8_practice/libs/list.h | 163 + lab8_practice/libs/printfmt.c | 359 ++ lab8_practice/libs/rand.c | 26 + lab8_practice/libs/riscv.h | 1502 +++++ lab8_practice/libs/sbi.h | 100 + lab8_practice/libs/skew_heap.h | 87 + lab8_practice/libs/stat.h | 27 + lab8_practice/libs/stdarg.h | 12 + lab8_practice/libs/stdio.h | 24 + lab8_practice/libs/stdlib.h | 17 + lab8_practice/libs/string.c | 379 ++ lab8_practice/libs/string.h | 28 + lab8_practice/libs/unistd.h | 68 + lab8_practice/tools/boot.ld | 15 + lab8_practice/tools/function.mk | 95 + lab8_practice/tools/gdbinit | 3 + lab8_practice/tools/grade.sh | 560 ++ lab8_practice/tools/kernel.ld | 51 + lab8_practice/tools/mksfs.c | 582 ++ lab8_practice/tools/sign.c | 43 + lab8_practice/tools/user.ld | 39 + lab8_practice/tools/vector.c | 29 + lab8_practice/user/badarg.c | 22 + lab8_practice/user/badsegment.c | 11 + lab8_practice/user/divzero.c | 11 + lab8_practice/user/exit.c | 34 + lab8_practice/user/faultread.c | 9 + lab8_practice/user/faultreadkernel.c | 9 + lab8_practice/user/forktest.c | 34 + lab8_practice/user/forktree.c | 37 + lab8_practice/user/hello.c | 11 + lab8_practice/user/libs/dir.c | 46 + lab8_practice/user/libs/dir.h | 19 + lab8_practice/user/libs/file.c | 68 + lab8_practice/user/libs/file.h | 23 + lab8_practice/user/libs/initcode.S | 13 + lab8_practice/user/libs/lock.h | 42 + lab8_practice/user/libs/panic.c | 28 + lab8_practice/user/libs/stdio.c | 89 + lab8_practice/user/libs/syscall.c | 152 + lab8_practice/user/libs/syscall.h | 33 + lab8_practice/user/libs/ulib.c | 72 + lab8_practice/user/libs/ulib.h | 40 + lab8_practice/user/libs/umain.c | 34 + lab8_practice/user/matrix.c | 84 + lab8_practice/user/pgdir.c | 11 + lab8_practice/user/priority.c | 77 + lab8_practice/user/sh.c | 254 + lab8_practice/user/sleep.c | 28 + lab8_practice/user/sleepkill.c | 18 + lab8_practice/user/softint.c | 10 + lab8_practice/user/spin.c | 29 + lab8_practice/user/testbss.c | 33 + lab8_practice/user/waitkill.c | 59 + lab8_practice/user/yield.c | 16 + 353 files changed, 47318 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 README.md create mode 100644 lab8/.gdb.in create mode 100644 lab8/CMakeLists.txt create mode 100644 lab8/Makefile create mode 100644 lab8/giveitatry.pyq create mode 100644 lab8/kern/debug/assert.h create mode 100644 lab8/kern/debug/kdebug.c create mode 100644 lab8/kern/debug/kdebug.h create mode 100644 lab8/kern/debug/kmonitor.c create mode 100644 lab8/kern/debug/kmonitor.h create mode 100644 lab8/kern/debug/panic.c create mode 100644 lab8/kern/debug/stab.h create mode 100644 lab8/kern/driver/clock.c create mode 100644 lab8/kern/driver/clock.h create mode 100644 lab8/kern/driver/console.c create mode 100644 lab8/kern/driver/console.h create mode 100644 lab8/kern/driver/fpioa.c create mode 100644 lab8/kern/driver/fpioa.h create mode 100644 lab8/kern/driver/gpiohs.c create mode 100644 lab8/kern/driver/gpiohs.h create mode 100644 lab8/kern/driver/ide.c create mode 100644 lab8/kern/driver/ide.h create mode 100644 lab8/kern/driver/intr.c create mode 100644 lab8/kern/driver/intr.h create mode 100644 lab8/kern/driver/io.h create mode 100644 lab8/kern/driver/kbdreg.h create mode 100644 lab8/kern/driver/picirq.c create mode 100644 lab8/kern/driver/picirq.h create mode 100644 lab8/kern/driver/ramdisk.c create mode 100644 lab8/kern/driver/ramdisk.h create mode 100644 lab8/kern/driver/sdcard.c create mode 100644 lab8/kern/driver/sdcard.h create mode 100644 lab8/kern/driver/spi.c create mode 100644 lab8/kern/driver/spi.h create mode 100644 lab8/kern/driver/sysctl.c create mode 100644 lab8/kern/driver/sysctl.h create mode 100644 lab8/kern/fs/devs/dev.c create mode 100644 lab8/kern/fs/devs/dev.h create mode 100644 lab8/kern/fs/devs/dev_disk0.c create mode 100644 lab8/kern/fs/devs/dev_stdin.c create mode 100644 lab8/kern/fs/devs/dev_stdout.c create mode 100644 lab8/kern/fs/file.c create mode 100644 lab8/kern/fs/file.h create mode 100644 lab8/kern/fs/fs.c create mode 100644 lab8/kern/fs/fs.h create mode 100644 lab8/kern/fs/iobuf.c create mode 100644 lab8/kern/fs/iobuf.h create mode 100644 lab8/kern/fs/sfs/bitmap.c create mode 100644 lab8/kern/fs/sfs/bitmap.h create mode 100644 lab8/kern/fs/sfs/sfs.c create mode 100644 lab8/kern/fs/sfs/sfs.h create mode 100644 lab8/kern/fs/sfs/sfs_fs.c create mode 100644 lab8/kern/fs/sfs/sfs_inode.c create mode 100644 lab8/kern/fs/sfs/sfs_io.c create mode 100644 lab8/kern/fs/sfs/sfs_lock.c create mode 100644 lab8/kern/fs/swap/swapfs.c create mode 100644 lab8/kern/fs/swap/swapfs.h create mode 100644 lab8/kern/fs/sysfile.c create mode 100644 lab8/kern/fs/sysfile.h create mode 100644 lab8/kern/fs/vfs/inode.c create mode 100644 lab8/kern/fs/vfs/inode.h create mode 100644 lab8/kern/fs/vfs/vfs.c create mode 100644 lab8/kern/fs/vfs/vfs.h create mode 100644 lab8/kern/fs/vfs/vfsdev.c create mode 100644 lab8/kern/fs/vfs/vfsfile.c create mode 100644 lab8/kern/fs/vfs/vfslookup.c create mode 100644 lab8/kern/fs/vfs/vfspath.c create mode 100644 lab8/kern/init/entry.S create mode 100644 lab8/kern/init/init.c create mode 100644 lab8/kern/libs/readline.c create mode 100644 lab8/kern/libs/stdio.c create mode 100644 lab8/kern/libs/string.c create mode 100644 lab8/kern/libs/util.c create mode 100644 lab8/kern/mm/default_pmm.c create mode 100644 lab8/kern/mm/default_pmm.h create mode 100644 lab8/kern/mm/kmalloc.c create mode 100644 lab8/kern/mm/kmalloc.h create mode 100644 lab8/kern/mm/memlayout.h create mode 100644 lab8/kern/mm/mmu.h create mode 100644 lab8/kern/mm/pmm.c create mode 100644 lab8/kern/mm/pmm.h create mode 100644 lab8/kern/mm/swap.c create mode 100644 lab8/kern/mm/swap.h create mode 100644 lab8/kern/mm/swap_fifo.c create mode 100644 lab8/kern/mm/swap_fifo.h create mode 100644 lab8/kern/mm/vmm.c create mode 100644 lab8/kern/mm/vmm.h create mode 100644 lab8/kern/process/entry.S create mode 100644 lab8/kern/process/proc.c create mode 100644 lab8/kern/process/proc.h create mode 100644 lab8/kern/process/switch.S create mode 100644 lab8/kern/schedule/default_sched.h create mode 100644 lab8/kern/schedule/default_sched_c create mode 100644 lab8/kern/schedule/default_sched_stride.c create mode 100644 lab8/kern/schedule/sched.c create mode 100644 lab8/kern/schedule/sched.h create mode 100644 lab8/kern/sync/check_sync.c create mode 100644 lab8/kern/sync/monitor.c create mode 100644 lab8/kern/sync/monitor.h create mode 100644 lab8/kern/sync/sem.c create mode 100644 lab8/kern/sync/sem.h create mode 100644 lab8/kern/sync/sync.h create mode 100644 lab8/kern/sync/wait.c create mode 100644 lab8/kern/sync/wait.h create mode 100644 lab8/kern/syscall/syscall.c create mode 100644 lab8/kern/syscall/syscall.h create mode 100644 lab8/kern/trap/trap.c create mode 100644 lab8/kern/trap/trap.h create mode 100644 lab8/kern/trap/trapentry.S create mode 100644 lab8/lab5.md create mode 100644 lab8/libs/atomic.h create mode 100644 lab8/libs/defs.h create mode 100644 lab8/libs/dirent.h create mode 100644 lab8/libs/elf.h create mode 100644 lab8/libs/error.h create mode 100644 lab8/libs/hash.c create mode 100644 lab8/libs/list.h create mode 100644 lab8/libs/printfmt.c create mode 100644 lab8/libs/rand.c create mode 100644 lab8/libs/riscv.h create mode 100644 lab8/libs/sbi.h create mode 100644 lab8/libs/skew_heap.h create mode 100644 lab8/libs/stat.h create mode 100644 lab8/libs/stdarg.h create mode 100644 lab8/libs/stdio.h create mode 100644 lab8/libs/stdlib.h create mode 100644 lab8/libs/string.c create mode 100644 lab8/libs/string.h create mode 100644 lab8/libs/unistd.h create mode 100644 lab8/libs/util.h create mode 100644 lab8/tools/boot.ld create mode 100644 lab8/tools/function.mk create mode 100644 lab8/tools/gdbinit create mode 100644 lab8/tools/grade.sh create mode 100644 lab8/tools/kernel.ld create mode 100644 lab8/tools/kflash.py create mode 100644 lab8/tools/mksfs.c create mode 100644 lab8/tools/rustsbi-k210.bin create mode 100644 lab8/tools/sign.c create mode 100644 lab8/tools/user.ld create mode 100644 lab8/tools/vector.c create mode 100644 lab8/user/badarg.c create mode 100644 lab8/user/badsegment.c create mode 100644 lab8/user/divzero.c create mode 100644 lab8/user/exit.c create mode 100644 lab8/user/faultread.c create mode 100644 lab8/user/faultreadkernel.c create mode 100644 lab8/user/forktest.c create mode 100644 lab8/user/forktree.c create mode 100644 lab8/user/hello.c create mode 100644 lab8/user/libs/dir.c create mode 100644 lab8/user/libs/dir.h create mode 100644 lab8/user/libs/file.c create mode 100644 lab8/user/libs/file.h create mode 100644 lab8/user/libs/initcode.S create mode 100644 lab8/user/libs/lock.h create mode 100644 lab8/user/libs/panic.c create mode 100644 lab8/user/libs/stdio.c create mode 100644 lab8/user/libs/syscall.c create mode 100644 lab8/user/libs/syscall.h create mode 100644 lab8/user/libs/ulib.c create mode 100644 lab8/user/libs/ulib.h create mode 100644 lab8/user/libs/umain.c create mode 100644 lab8/user/matrix.c create mode 100644 lab8/user/pgdir.c create mode 100644 lab8/user/priority.c create mode 100644 lab8/user/sh.c create mode 100644 lab8/user/sleep.c create mode 100644 lab8/user/sleepkill.c create mode 100644 lab8/user/softint.c create mode 100644 lab8/user/spin.c create mode 100644 lab8/user/testbss.c create mode 100644 lab8/user/waitkill.c create mode 100644 lab8/user/yield.c create mode 100644 lab8_practice/.gdb.in create mode 100644 lab8_practice/Makefile create mode 100644 lab8_practice/disk0/badarg create mode 100644 lab8_practice/disk0/badsegment create mode 100644 lab8_practice/disk0/divzero create mode 100644 lab8_practice/disk0/exit create mode 100644 lab8_practice/disk0/faultread create mode 100644 lab8_practice/disk0/faultreadkernel create mode 100644 lab8_practice/disk0/forktest create mode 100644 lab8_practice/disk0/forktree create mode 100644 lab8_practice/disk0/hello create mode 100644 lab8_practice/disk0/matrix create mode 100644 lab8_practice/disk0/pgdir create mode 100644 lab8_practice/disk0/priority create mode 100644 lab8_practice/disk0/sh create mode 100644 lab8_practice/disk0/sleep create mode 100644 lab8_practice/disk0/sleepkill create mode 100644 lab8_practice/disk0/softint create mode 100644 lab8_practice/disk0/spin create mode 100644 lab8_practice/disk0/testbss create mode 100644 lab8_practice/disk0/waitkill create mode 100644 lab8_practice/disk0/yield create mode 100644 lab8_practice/giveitatry.pyq create mode 100644 lab8_practice/kern/debug/assert.h create mode 100644 lab8_practice/kern/debug/kdebug.c create mode 100644 lab8_practice/kern/debug/kdebug.h create mode 100644 lab8_practice/kern/debug/kmonitor.c create mode 100644 lab8_practice/kern/debug/kmonitor.h create mode 100644 lab8_practice/kern/debug/panic.c create mode 100644 lab8_practice/kern/debug/stab.h create mode 100644 lab8_practice/kern/driver/clock.c create mode 100644 lab8_practice/kern/driver/clock.h create mode 100644 lab8_practice/kern/driver/console.c create mode 100644 lab8_practice/kern/driver/console.h create mode 100644 lab8_practice/kern/driver/ide.c create mode 100644 lab8_practice/kern/driver/ide.h create mode 100644 lab8_practice/kern/driver/intr.c create mode 100644 lab8_practice/kern/driver/intr.h create mode 100644 lab8_practice/kern/driver/kbdreg.h create mode 100644 lab8_practice/kern/driver/picirq.c create mode 100644 lab8_practice/kern/driver/picirq.h create mode 100644 lab8_practice/kern/driver/ramdisk.c create mode 100644 lab8_practice/kern/driver/ramdisk.h create mode 100644 lab8_practice/kern/fs/devs/dev.c create mode 100644 lab8_practice/kern/fs/devs/dev.h create mode 100644 lab8_practice/kern/fs/devs/dev_disk0.c create mode 100644 lab8_practice/kern/fs/devs/dev_stdin.c create mode 100644 lab8_practice/kern/fs/devs/dev_stdout.c create mode 100644 lab8_practice/kern/fs/file.c create mode 100644 lab8_practice/kern/fs/file.h create mode 100644 lab8_practice/kern/fs/fs.c create mode 100644 lab8_practice/kern/fs/fs.h create mode 100644 lab8_practice/kern/fs/iobuf.c create mode 100644 lab8_practice/kern/fs/iobuf.h create mode 100644 lab8_practice/kern/fs/sfs/bitmap.c create mode 100644 lab8_practice/kern/fs/sfs/bitmap.h create mode 100644 lab8_practice/kern/fs/sfs/sfs.c create mode 100644 lab8_practice/kern/fs/sfs/sfs.h create mode 100644 lab8_practice/kern/fs/sfs/sfs_fs.c create mode 100644 lab8_practice/kern/fs/sfs/sfs_inode.c create mode 100644 lab8_practice/kern/fs/sfs/sfs_io.c create mode 100644 lab8_practice/kern/fs/sfs/sfs_lock.c create mode 100644 lab8_practice/kern/fs/swap/swapfs.c create mode 100644 lab8_practice/kern/fs/swap/swapfs.h create mode 100644 lab8_practice/kern/fs/sysfile.c create mode 100644 lab8_practice/kern/fs/sysfile.h create mode 100644 lab8_practice/kern/fs/vfs/inode.c create mode 100644 lab8_practice/kern/fs/vfs/inode.h create mode 100644 lab8_practice/kern/fs/vfs/vfs.c create mode 100644 lab8_practice/kern/fs/vfs/vfs.h create mode 100644 lab8_practice/kern/fs/vfs/vfsdev.c create mode 100644 lab8_practice/kern/fs/vfs/vfsfile.c create mode 100644 lab8_practice/kern/fs/vfs/vfslookup.c create mode 100644 lab8_practice/kern/fs/vfs/vfspath.c create mode 100644 lab8_practice/kern/init/entry.S create mode 100644 lab8_practice/kern/init/init.c create mode 100644 lab8_practice/kern/libs/readline.c create mode 100644 lab8_practice/kern/libs/stdio.c create mode 100644 lab8_practice/kern/libs/string.c create mode 100644 lab8_practice/kern/mm/default_pmm.c create mode 100644 lab8_practice/kern/mm/default_pmm.h create mode 100644 lab8_practice/kern/mm/kmalloc.c create mode 100644 lab8_practice/kern/mm/kmalloc.h create mode 100644 lab8_practice/kern/mm/memlayout.h create mode 100644 lab8_practice/kern/mm/mmu.h create mode 100644 lab8_practice/kern/mm/pmm.c create mode 100644 lab8_practice/kern/mm/pmm.h create mode 100644 lab8_practice/kern/mm/swap.c create mode 100644 lab8_practice/kern/mm/swap.h create mode 100644 lab8_practice/kern/mm/swap_fifo.c create mode 100644 lab8_practice/kern/mm/swap_fifo.h create mode 100644 lab8_practice/kern/mm/vmm.c create mode 100644 lab8_practice/kern/mm/vmm.h create mode 100644 lab8_practice/kern/process/entry.S create mode 100644 lab8_practice/kern/process/proc.c create mode 100644 lab8_practice/kern/process/proc.h create mode 100644 lab8_practice/kern/process/switch.S create mode 100644 lab8_practice/kern/schedule/default_sched.h create mode 100644 lab8_practice/kern/schedule/default_sched_c create mode 100644 lab8_practice/kern/schedule/default_sched_stride.c create mode 100644 lab8_practice/kern/schedule/sched.c create mode 100644 lab8_practice/kern/schedule/sched.h create mode 100644 lab8_practice/kern/sync/check_sync.c create mode 100644 lab8_practice/kern/sync/monitor.c create mode 100644 lab8_practice/kern/sync/monitor.h create mode 100644 lab8_practice/kern/sync/sem.c create mode 100644 lab8_practice/kern/sync/sem.h create mode 100644 lab8_practice/kern/sync/sync.h create mode 100644 lab8_practice/kern/sync/wait.c create mode 100644 lab8_practice/kern/sync/wait.h create mode 100644 lab8_practice/kern/syscall/syscall.c create mode 100644 lab8_practice/kern/syscall/syscall.h create mode 100644 lab8_practice/kern/trap/trap.c create mode 100644 lab8_practice/kern/trap/trap.h create mode 100644 lab8_practice/kern/trap/trapentry.S create mode 100644 lab8_practice/lab5.md create mode 100644 lab8_practice/libs/atomic.h create mode 100644 lab8_practice/libs/defs.h create mode 100644 lab8_practice/libs/dirent.h create mode 100644 lab8_practice/libs/elf.h create mode 100644 lab8_practice/libs/error.h create mode 100644 lab8_practice/libs/hash.c create mode 100644 lab8_practice/libs/list.h create mode 100644 lab8_practice/libs/printfmt.c create mode 100644 lab8_practice/libs/rand.c create mode 100644 lab8_practice/libs/riscv.h create mode 100644 lab8_practice/libs/sbi.h create mode 100644 lab8_practice/libs/skew_heap.h create mode 100644 lab8_practice/libs/stat.h create mode 100644 lab8_practice/libs/stdarg.h create mode 100644 lab8_practice/libs/stdio.h create mode 100644 lab8_practice/libs/stdlib.h create mode 100644 lab8_practice/libs/string.c create mode 100644 lab8_practice/libs/string.h create mode 100644 lab8_practice/libs/unistd.h create mode 100644 lab8_practice/tools/boot.ld create mode 100644 lab8_practice/tools/function.mk create mode 100644 lab8_practice/tools/gdbinit create mode 100644 lab8_practice/tools/grade.sh create mode 100644 lab8_practice/tools/kernel.ld create mode 100644 lab8_practice/tools/mksfs.c create mode 100644 lab8_practice/tools/sign.c create mode 100644 lab8_practice/tools/user.ld create mode 100644 lab8_practice/tools/vector.c create mode 100644 lab8_practice/user/badarg.c create mode 100644 lab8_practice/user/badsegment.c create mode 100644 lab8_practice/user/divzero.c create mode 100644 lab8_practice/user/exit.c create mode 100644 lab8_practice/user/faultread.c create mode 100644 lab8_practice/user/faultreadkernel.c create mode 100644 lab8_practice/user/forktest.c create mode 100644 lab8_practice/user/forktree.c create mode 100644 lab8_practice/user/hello.c create mode 100644 lab8_practice/user/libs/dir.c create mode 100644 lab8_practice/user/libs/dir.h create mode 100644 lab8_practice/user/libs/file.c create mode 100644 lab8_practice/user/libs/file.h create mode 100644 lab8_practice/user/libs/initcode.S create mode 100644 lab8_practice/user/libs/lock.h create mode 100644 lab8_practice/user/libs/panic.c create mode 100644 lab8_practice/user/libs/stdio.c create mode 100644 lab8_practice/user/libs/syscall.c create mode 100644 lab8_practice/user/libs/syscall.h create mode 100644 lab8_practice/user/libs/ulib.c create mode 100644 lab8_practice/user/libs/ulib.h create mode 100644 lab8_practice/user/libs/umain.c create mode 100644 lab8_practice/user/matrix.c create mode 100644 lab8_practice/user/pgdir.c create mode 100644 lab8_practice/user/priority.c create mode 100644 lab8_practice/user/sh.c create mode 100644 lab8_practice/user/sleep.c create mode 100644 lab8_practice/user/sleepkill.c create mode 100644 lab8_practice/user/softint.c create mode 100644 lab8_practice/user/spin.c create mode 100644 lab8_practice/user/testbss.c create mode 100644 lab8_practice/user/waitkill.c create mode 100644 lab8_practice/user/yield.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b07ef6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,81 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Kernel Build Results +obj/ +bin/ + +# vim + +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# Visual Studio Code +.vscode/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0fcdf0a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "riscv-pk"] + path = riscv-pk + url = https://github.com/riscv/riscv-pk.git \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..73d194b --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# 环境配置 +关于环境配置可以在[这里](https://github.com/NKU-EmbeddedSystem/riscv64-ucore/blob/k210-lab0/README.md)找到 + +# 与运行在qemu上版本的代码改动 +对每一处修改的说明: +- [不包括终端的改动](https://github.com/Kirhhoff/riscv64-ucore/commit/82db2c9b26aff29d82aed49b53b5fd611d3e30d9) +- [添加SD卡驱动](https://github.com/Kirhhoff/riscv64-ucore/commit/671c0baf00365361793694413ce2fb95bbc1813b) +- [对终端的改动](https://github.com/Kirhhoff/riscv64-ucore/commit/7680ebb35c247c356f3846e88b57f37ee68ce4a8) + +### 添加低地址内核映射 +初始内核页表(entry.S中hard code的部分)只设置了高地址的内存映射,而刚开启分页时,内核仍然运行在低地址,随后才跳转到高地址,因此低地址部分的内核映射是必要的。qemu模拟器对此没有要求,而对实际环境这是必须的。 + +### 对memlayout的修改 +qemu版本使用opensbi作为bootloader,将控制权移交给内核时跳转地址为0x80200000,同时内核运行的起始虚拟地址为0xFFFFFFFFC0200000,模拟器模拟出的最大物理地址为0x88000000。k210版本使用rustsbi作为bootloader,跳转地址为0x80020000,内核运行的起始虚拟地址为0xFFFFFFFFC0020000,实际物理地址最大为0x80600000(见[k210规格说明书](https://s3.cn-north-1.amazonaws.com.cn/dl.kendryte.com/documents/kendryte_datasheet_20180919020633.pdf))。 + +因此,要修改两部分代码:一方面要修改memlayout中宏的定义,使得符号地址能够被正确计算;另一方面要修改linker script中的链接地址,使得代码运行的虚拟地址正确。 + +### 不同版本指令集带来的兼容性问题 +risc-v ucore编写时基于[v1.11](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMFDQC-and-Priv-v1.11/riscv-privileged-20190608.pdf)的特权指令集,而k210实现时基于[v1.9](https://people.eecs.berkeley.edu/~krste/papers/riscv-privileged-v1.9.1.pdf)的特权指令集,两者大体上相同,但对我们的代码产生了两处影响 +- v1.11中sstatus寄存器的SUM bit设置时允许S-mode访问用户态地址,然而在v1.9中,这个bit代表PUM,设置时会反而禁止这样的访问,因此需要删除原本设置这一bit的语句,同时修改对这个位的宏名称。 +- 相比于v1.11,v1.9没有定义sstatus的UXL位,在宏定义中删去这一项 + +### 对齐问题 +risc-v对内存读写指令有着较强的对齐要求,如当读取8字节指令作用在仅4字节对齐的地址上时,会触发misalignment异常,而在qemu版本中用户态syscall的内联汇编使用了错误对齐的内存读写指令,会触发异常。对齐在模拟器中不是强制要求的,但在实际环境中是必须的。 + +### 初始化部分 +qemu版本在初始化时有一个get_char操作,但在实际环境下会阻塞内核的正常运行,因此删去这条语句。 + +### 用户栈的对齐 +在给用户态进程准备初始栈以及参数时,除argc外其他数据类型均为8字节长,最后位于栈顶的argc为4字节,这会导致控制权刚移交到用户态时的栈指针不是8字节对齐的,但用户态并不知道这一点,会默认刚进入用户态的初始状态为8字节对齐。因此这里使用一个trick在内核准备栈参数以及传递这个参数时临时使用8字节的long类型,而在umain移交给main时再将该类型cast回4字节的int,同时避免的截断(因为最开始类型为4字节)。 + +### SD卡驱动 +默认booting时会尝试检测SD卡,如果存在且成功初始化则会打印: +``` +Successfully initialize SD card! +``` +失败或超时则打印: +``` +Fail to connect to SD card! +``` +初始化时默认**不会**进行读写测试,以防出现意料外的数据损失;也可以通过取消注释kern/driver/sdcard.c的sd_test来进行读写测试,这个测试会对倒数第10、9两个扇区备份后进行读写测试,然后恢复其数据,但仍可能会造成数据损失,因此需要谨慎。 + +### 控制台改动 +qemu版本的控制台输入使用sbi调用来获得,在每个时钟tick时检查,并将其feed进缓冲区。实际环境中通过外部中断获得,这里注册外部中断handler需要使用rustsbi提供的ecall扩展,同时键盘中断通过UARTHS传递到处理器,在handler中通过其中断号来识别。另外,在中断handler中不能直接feed输入,而只将其放在缓冲区中,而在每个时钟tick才尝试唤醒等待io的进程,从而避免在handler中触发调度。 \ No newline at end of file diff --git a/lab8/.gdb.in b/lab8/.gdb.in new file mode 100644 index 0000000..6188bef --- /dev/null +++ b/lab8/.gdb.in @@ -0,0 +1,3 @@ +target remote localhost:1234 +break *0x00000000802000ea +continue diff --git a/lab8/CMakeLists.txt b/lab8/CMakeLists.txt new file mode 100644 index 0000000..6b65b23 --- /dev/null +++ b/lab8/CMakeLists.txt @@ -0,0 +1,169 @@ +cmake_minimum_required(VERSION 3.17) +project(lab8 C) + +set(CMAKE_C_STANDARD 11) + +include_directories(.) +include_directories(kern) +include_directories(kern/debug) +include_directories(kern/driver) +include_directories(kern/fs) +include_directories(kern/fs/devs) +include_directories(kern/fs/sfs) +include_directories(kern/fs/swap) +include_directories(kern/fs/vfs) +include_directories(kern/init) +include_directories(kern/libs) +include_directories(kern/mm) +include_directories(kern/process) +include_directories(kern/schedule) +include_directories(kern/sync) +include_directories(kern/syscall) +include_directories(kern/trap) +include_directories(libs) +include_directories(tools) +include_directories(user) +include_directories(user/libs) + +add_executable(lab8 + kern/debug/assert.h + kern/debug/kdebug.c + kern/debug/kdebug.h + kern/debug/kmonitor.c + kern/debug/kmonitor.h + kern/debug/panic.c + kern/debug/stab.h + kern/driver/clock.c + kern/driver/clock.h + kern/driver/console.c + kern/driver/console.h + kern/driver/ide.c + kern/driver/ide.h + kern/driver/intr.c + kern/driver/intr.h + kern/driver/kbdreg.h + kern/driver/picirq.c + kern/driver/picirq.h + kern/driver/ramdisk.c + kern/driver/ramdisk.h + kern/fs/devs/dev.c + kern/fs/devs/dev.h + kern/fs/devs/dev_disk0.c + kern/fs/devs/dev_stdin.c + kern/fs/devs/dev_stdout.c + kern/fs/sfs/bitmap.c + kern/fs/sfs/bitmap.h + kern/fs/sfs/sfs.c + kern/fs/sfs/sfs.h + kern/fs/sfs/sfs_fs.c + kern/fs/sfs/sfs_inode.c + kern/fs/sfs/sfs_io.c + kern/fs/sfs/sfs_lock.c + kern/fs/swap/swapfs.c + kern/fs/swap/swapfs.h + kern/fs/vfs/inode.c + kern/fs/vfs/inode.h + kern/fs/vfs/vfs.c + kern/fs/vfs/vfs.h + kern/fs/vfs/vfsdev.c + kern/fs/vfs/vfsfile.c + kern/fs/vfs/vfslookup.c + kern/fs/vfs/vfspath.c + kern/fs/file.c + kern/fs/file.h + kern/fs/fs.c + kern/fs/fs.h + kern/fs/iobuf.c + kern/fs/iobuf.h + kern/fs/sysfile.c + kern/fs/sysfile.h + kern/init/init.c + kern/libs/readline.c + kern/libs/stdio.c + kern/libs/string.c + kern/mm/default_pmm.c + kern/mm/default_pmm.h + kern/mm/kmalloc.c + kern/mm/kmalloc.h + kern/mm/memlayout.h + kern/mm/mmu.h + kern/mm/pmm.c + kern/mm/pmm.h + kern/mm/swap.c + kern/mm/swap.h + kern/mm/swap_fifo.c + kern/mm/swap_fifo.h + kern/mm/vmm.c + kern/mm/vmm.h + kern/process/proc.c + kern/process/proc.h + kern/schedule/default_sched.h + kern/schedule/default_sched_stride.c + kern/schedule/sched.c + kern/schedule/sched.h + kern/sync/check_sync.c + kern/sync/monitor.c + kern/sync/monitor.h + kern/sync/sem.c + kern/sync/sem.h + kern/sync/sync.h + kern/sync/wait.c + kern/sync/wait.h + kern/syscall/syscall.c + kern/syscall/syscall.h + kern/trap/trap.c + kern/trap/trap.h + libs/atomic.h + libs/defs.h + libs/dirent.h + libs/elf.h + libs/error.h + libs/hash.c + libs/list.h + libs/printfmt.c + libs/rand.c + libs/riscv.h + libs/sbi.h + libs/skew_heap.h + libs/stat.h + libs/stdarg.h + libs/stdio.h + libs/stdlib.h + libs/string.c + libs/string.h + libs/unistd.h + tools/mksfs.c + tools/sign.c + tools/vector.c + user/libs/dir.c + user/libs/dir.h + user/libs/file.c + user/libs/file.h + user/libs/lock.h + user/libs/panic.c + user/libs/stdio.c + user/libs/syscall.c + user/libs/syscall.h + user/libs/ulib.c + user/libs/ulib.h + user/libs/umain.c + user/badarg.c + user/badsegment.c + user/divzero.c + user/exit.c + user/faultread.c + user/faultreadkernel.c + user/forktest.c + user/forktree.c + user/hello.c + user/matrix.c + user/pgdir.c + user/priority.c + user/sh.c + user/sleep.c + user/sleepkill.c + user/softint.c + user/spin.c + user/testbss.c + user/waitkill.c + user/yield.c kern/driver/sdcard.h kern/driver/sdcard.c kern/driver/fpioa.h kern/driver/fpioa.c kern/driver/sysctl.h kern/driver/sysctl.c kern/driver/spi.h kern/driver/spi.c kern/driver/gpiohs.h kern/driver/gpiohs.c kern/driver/io.h libs/util.h kern/libs/util.c) diff --git a/lab8/Makefile b/lab8/Makefile new file mode 100644 index 0000000..09a42bc --- /dev/null +++ b/lab8/Makefile @@ -0,0 +1,350 @@ +PROJ := lab8 +EMPTY := +SPACE := $(EMPTY) $(EMPTY) +SLASH := / + +V := @ + +ifndef GCCPREFIX +GCCPREFIX := riscv64-unknown-elf- +endif + +ifndef QEMU +QEMU := qemu-system-riscv64 +endif + +ifndef SPIKE +SPIKE := spike +endif + +# eliminate default suffix rules +.SUFFIXES: .c .S .h + +# delete target files if there is an error (or make is interrupted) +.DELETE_ON_ERROR: + +# define compiler and flags +HOSTCC := gcc +HOSTCFLAGS := -Wall -O2 + +GDB := $(GCCPREFIX)gdb + +CC := $(GCCPREFIX)gcc +CFLAGS := -mcmodel=medany -O2 -std=gnu99 -Wno-unused +CFLAGS += -fno-builtin -Wall -nostdinc $(DEFS) +CFLAGS += -fno-stack-protector -ffunction-sections -fdata-sections -fstrict-volatile-bitfields +CTYPE := c S + +LD := $(GCCPREFIX)ld +LDFLAGS := -m elf64lriscv +LDFLAGS += -nostdlib --gc-sections + +OBJCOPY := $(GCCPREFIX)objcopy +OBJDUMP := $(GCCPREFIX)objdump + +COPY := cp +MKDIR := mkdir -p +MV := mv +RM := rm -f +AWK := awk +SED := sed +SH := sh +TR := tr +TOUCH := touch -c +PYTHON := python3 +TERM := miniterm + +OBJDIR := obj +BINDIR := bin + +ALLOBJS := +ALLDEPS := +TARGETS := + +USER_PREFIX := __user_ + +include tools/function.mk + +listf_cc = $(call listf,$(1),$(CTYPE)) + +# for cc +add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4)) +create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS)) + +# for hostcc +add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3)) +create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS)) + +cgtype = $(patsubst %.$(2),%.$(3),$(1)) +objfile = $(call toobj,$(1)) +asmfile = $(call cgtype,$(call toobj,$(1)),o,asm) +outfile = $(call cgtype,$(call toobj,$(1)),o,out) +symfile = $(call cgtype,$(call toobj,$(1)),o,sym) +filename = $(basename $(notdir $(1))) +ubinfile = $(call outfile,$(addprefix $(USER_PREFIX),$(call filename,$(1)))) + +# for match pattern +match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?) + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# include kernel/user + +INCLUDE += libs/ + +CFLAGS += $(addprefix -I,$(INCLUDE)) + +LIBDIR += libs + +$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) + +# ------------------------------------------------------------------- +# user programs + +UINCLUDE += user/include/ \ + user/libs/ + +USRCDIR += user + +ULIBDIR += user/libs + +UCFLAGS += $(addprefix -I,$(UINCLUDE)) +USER_BINS := + +$(call add_files_cc,$(call listf_cc,$(ULIBDIR)),ulibs,$(UCFLAGS)) +$(call add_files_cc,$(call listf_cc,$(USRCDIR)),uprog,$(UCFLAGS)) + +UOBJS := $(call read_packet,ulibs libs) + +define uprog_ld +__user_bin__ := $$(call ubinfile,$(1)) +USER_BINS += $$(__user_bin__) +$$(__user_bin__): tools/user.ld +$$(__user_bin__): $$(UOBJS) +$$(__user_bin__): $(1) | $$$$(dir $$$$@) + $(V)$(LD) $(LDFLAGS) -T tools/user.ld -o $$@ $$(UOBJS) $(1) + @$(OBJDUMP) -S $$@ > $$(call cgtype,$$<,o,asm) + @$(OBJDUMP) -t $$@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$$$/d' > $$(call cgtype,$$<,o,sym) +endef + +$(foreach p,$(call read_packet,uprog),$(eval $(call uprog_ld,$(p)))) + + +# ------------------------------------------------------------------- +# create 'mksfs' tools +$(call add_files_host,tools/mksfs.c,mksfs,mksfs) +$(call create_target_host,mksfs,mksfs) + +# ------------------------------------------------------------------- +# create swap.img +SWAPIMG := $(call totarget,swap.img) + +$(SWAPIMG): + $(V)dd if=/dev/zero of=$@ bs=4kB count=8 + +$(call create_target,swap.img) + +# ------------------------------------------------------------------- +# create sfs.img +SFSIMG := $(call totarget,sfs.img) +SFSBINS := +SFSROOT := disk0 + +define fscopy +__fs_bin__ := $(2)$(SLASH)$(patsubst $(USER_PREFIX)%,%,$(basename $(notdir $(1)))) +SFSBINS += $$(__fs_bin__) +$$(__fs_bin__): $(1) | $$$$(dir $@) + @$(COPY) $$< $$@ +endef + +$(foreach p,$(USER_BINS),$(eval $(call fscopy,$(p),$(SFSROOT)$(SLASH)))) + +$(SFSROOT): + $(V)$(MKDIR) $@ + +$(SFSIMG): $(SFSROOT) $(SFSBINS) | $(call totarget,mksfs) + $(V)dd if=/dev/zero of=$@ bs=1kB count=480 + @$(call totarget,mksfs) $@ $(SFSROOT) + +$(call create_target,sfs.img) + +# ------------------------------------------------------------------- +# kernel + +KINCLUDE += kern/debug/ \ + kern/driver/ \ + kern/trap/ \ + kern/mm/ \ + kern/libs/ \ + kern/sync/ \ + kern/fs/ \ + kern/process/ \ + kern/schedule/ \ + kern/syscall/ \ + kern/fs/swap/ \ + kern/fs/vfs/ \ + kern/fs/devs/ \ + kern/fs/sfs/ + + +KSRCDIR += kern/init \ + kern/libs \ + kern/debug \ + kern/driver \ + kern/trap \ + kern/mm \ + kern/sync \ + kern/fs \ + kern/process \ + kern/schedule \ + kern/syscall \ + kern/fs/swap \ + kern/fs/vfs \ + kern/fs/devs \ + kern/fs/sfs + +KCFLAGS += $(addprefix -I,$(KINCLUDE)) + +$(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS)) + +KOBJS = $(call read_packet,kernel libs) + +# create kernel target +kernel = $(call totarget,kernel) + +$(kernel): tools/kernel.ld + +$(kernel): $(KOBJS) $(SWAPIMG) $(SFSIMG) + @echo + ld $@ + $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) --format=binary $(SWAPIMG) $(SFSIMG) --format=default + @$(OBJDUMP) -S $@ > $(call asmfile,kernel) + @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel) + +$(call create_target,kernel) + +# ------------------------------------------------------------------- +# create ucore.img +UCOREIMG := $(call totarget,ucore.img) + +#$(UCOREIMG): $(kernel) +# cd ../../riscv-pk && rm -rf build && mkdir build && cd build && ../configure --prefix=$(RISCV) --host=riscv32-unknown-linux-gnu --with-payload=../../labcodes/$(PROJ)/$(kernel) && make && cp bbl ../../labcodes/$(PROJ)/$(UCOREIMG) + +$(UCOREIMG): $(kernel) + $(OBJCOPY) $(kernel) --strip-all -O binary $@ + +$(call create_target,ucore.img) + +# ------------------------------------------------------------------- +# create kernel.img +KERNELIMG := $(call totarget,kernel.img) +BOOTLOADER := tools/rustsbi-k210.bin +PORT := /dev/ttyUSB0 + +$(KERNELIMG): $(UCOREIMG) $(SWAPIMG) $(SFSIMG) $(BOOTLOADER) + $(COPY) $(BOOTLOADER) $@ + $(V)dd if=$(UCOREIMG) of=$@ bs=128K seek=1 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +$(call finish_all) + +IGNORE_ALLDEPS = clean \ + dist-clean \ + grade \ + touch \ + print-.+ \ + run-.+ \ + build-.+ \ + sh-.+ \ + script-.+ \ + handin + +ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0) +-include $(ALLDEPS) +endif + +# files for grade script + +TARGETS: $(TARGETS) + +.DEFAULT_GOAL := TARGETS + +QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback -drive file=$(SFSIMG),media=disk,cache=writeback + +.PHONY: qemu spike + +qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG) +# $(V)$(QEMU) -kernel $(UCOREIMG) -nographic + $(V)$(QEMU) \ + -machine virt \ + -nographic \ + -bios default \ + -device loader,file=$(UCOREIMG),addr=0x80200000 + +k210: $(KERNELIMG) + $(PYTHON) tools/kflash.py -p $(PORT) -b 1500000 $(KERNELIMG) + $(TERM) --eol LF --dtr 0 --rts 0 --filter direct $(PORT) 115200 + +spike: $(UCOREIMG) $(SWAPIMG) $(SFSIMG) + $(V)$(SPIKE) $(UCOREIMG) + +TERMINAL := gnome-terminal + +RUN_PREFIX := _binary_$(OBJDIR)_$(USER_PREFIX) +MAKEOPTS := --quiet --no-print-directory + +run-%: build-% + $(V)$(SPIKE) $(UCOREIMG) + +sh-%: script-% + $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null + +run-nox-%: build-% + $(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic + +build-%: touch + $(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=$*" + +script-%: touch + $(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=sh -DTESTSCRIPT=/script/$*" + +.PHONY: grade touch buildfs + +GRADE_GDB_IN := .gdb.in +GRADE_QEMU_OUT := .qemu.out +HANDIN := proj$(PROJ)-handin.tar.gz + +TOUCH_FILES := kern/process/proc.c + +MAKEOPTS := --quiet --no-print-directory + +grade: + $(V)$(MAKE) $(MAKEOPTS) clean + $(V)$(SH) tools/grade.sh + +touch: + $(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f)) + +print-%: + @echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z])) + +.PHONY: clean dist-clean handin packall tags +clean: + $(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) $(SFSBINS) cscope* tags + $(V)$(RM) -r $(OBJDIR) $(BINDIR) $(SFSROOT) + +dist-clean: clean + -$(RM) $(HANDIN) + +handin: packall + @echo Please visit http://learn.tsinghua.edu.cn and upload $(HANDIN). Thanks! + +packall: clean + @$(RM) -f $(HANDIN) + @tar -czf $(HANDIN) `find . -type f -o -type d | grep -v '^\.*$$' | grep -vF '$(HANDIN)'` + +tags: + @echo TAGS ALL + $(V)rm -f cscope.files cscope.in.out cscope.out cscope.po.out tags + $(V)find . -type f -name "*.[chS]" >cscope.files + $(V)cscope -bq + $(V)ctags -L cscope.files diff --git a/lab8/giveitatry.pyq b/lab8/giveitatry.pyq new file mode 100644 index 0000000..e69de29 diff --git a/lab8/kern/debug/assert.h b/lab8/kern/debug/assert.h new file mode 100644 index 0000000..ac1a966 --- /dev/null +++ b/lab8/kern/debug/assert.h @@ -0,0 +1,27 @@ +#ifndef __KERN_DEBUG_ASSERT_H__ +#define __KERN_DEBUG_ASSERT_H__ + +#include + +void __warn(const char *file, int line, const char *fmt, ...); +void __noreturn __panic(const char *file, int line, const char *fmt, ...); + +#define warn(...) \ + __warn(__FILE__, __LINE__, __VA_ARGS__) + +#define panic(...) \ + __panic(__FILE__, __LINE__, __VA_ARGS__) + +#define assert(x) \ + do { \ + if (!(x)) { \ + panic("assertion failed: %s", #x); \ + } \ + } while (0) + +// static_assert(x) will generate a compile-time error if 'x' is false. +#define static_assert(x) \ + switch (x) { case 0: case (x): ; } + +#endif /* !__KERN_DEBUG_ASSERT_H__ */ + diff --git a/lab8/kern/debug/kdebug.c b/lab8/kern/debug/kdebug.c new file mode 100644 index 0000000..672c47b --- /dev/null +++ b/lab8/kern/debug/kdebug.c @@ -0,0 +1,92 @@ +#include +#include +#include + +/* * + * print_kerninfo - print the information about kernel, including the location + * of kernel entry, the start addresses of data and text segements, the start + * address of free memory and how many memory that kernel has used. + * */ +void print_kerninfo(void) { + extern char etext[], edata[], end[], kern_init[]; + cprintf("Special kernel symbols:\n"); + cprintf(" entry 0x%08x (virtual)\n", kern_init); + cprintf(" etext 0x%08x (virtual)\n", etext); + cprintf(" edata 0x%08x (virtual)\n", edata); + cprintf(" end 0x%08x (virtual)\n", end); + cprintf("Kernel executable memory footprint: %dKB\n", + (end - kern_init + 1023) / 1024); +} + +/* * + * print_debuginfo - read and print the stat information for the address @eip, + * and info.eip_fn_addr should be the first address of the related function. + * */ +void print_debuginfo(uintptr_t eip) { panic("Not Implemented!"); } + +/* * + * print_stackframe - print a list of the saved eip values from the nested + * 'call' + * instructions that led to the current point of execution + * + * The x86 stack pointer, namely esp, points to the lowest location on the stack + * that is currently in use. Everything below that location in stack is free. + * Pushing + * a value onto the stack will invole decreasing the stack pointer and then + * writing + * the value to the place that stack pointer pointes to. And popping a value do + * the + * opposite. + * + * The ebp (base pointer) register, in contrast, is associated with the stack + * primarily by software convention. On entry to a C function, the function's + * prologue code normally saves the previous function's base pointer by pushing + * it onto the stack, and then copies the current esp value into ebp for the + * duration + * of the function. If all the functions in a program obey this convention, + * then at any given point during the program's execution, it is possible to + * trace + * back through the stack by following the chain of saved ebp pointers and + * determining + * exactly what nested sequence of function calls caused this particular point + * in the + * program to be reached. This capability can be particularly useful, for + * example, + * when a particular function causes an assert failure or panic because bad + * arguments + * were passed to it, but you aren't sure who passed the bad arguments. A stack + * backtrace lets you find the offending function. + * + * The inline function read_ebp() can tell us the value of current ebp. And the + * non-inline function read_eip() is useful, it can read the value of current + * eip, + * since while calling this function, read_eip() can read the caller's eip from + * stack easily. + * + * In print_debuginfo(), the function debuginfo_eip() can get enough information + * about + * calling-chain. Finally print_stackframe() will trace and print them for + * debugging. + * + * Note that, the length of ebp-chain is limited. In boot/bootasm.S, before + * jumping + * to the kernel entry, the value of ebp has been set to zero, that's the + * boundary. + * */ +void print_stackframe(void) { + /* LAB1 YOUR CODE : STEP 1 */ + /* (1) call read_ebp() to get the value of ebp. the type is (uint32_t); + * (2) call read_eip() to get the value of eip. the type is (uint32_t); + * (3) from 0 .. STACKFRAME_DEPTH + * (3.1) printf value of ebp, eip + * (3.2) (uint32_t)calling arguments [0..4] = the contents in address + * (unit32_t)ebp +2 [0..4] + * (3.3) cprintf("\n"); + * (3.4) call print_debuginfo(eip-1) to print the C calling function name + * and line number, etc. + * (3.5) popup a calling stackframe + * NOTICE: the calling funciton's return addr eip = ss:[ebp+4] + * the calling funciton's ebp = ss:[ebp] + */ + panic("Not Implemented!"); +} diff --git a/lab8/kern/debug/kdebug.h b/lab8/kern/debug/kdebug.h new file mode 100644 index 0000000..c2a7b74 --- /dev/null +++ b/lab8/kern/debug/kdebug.h @@ -0,0 +1,12 @@ +#ifndef __KERN_DEBUG_KDEBUG_H__ +#define __KERN_DEBUG_KDEBUG_H__ + +#include +#include + +void print_kerninfo(void); +void print_stackframe(void); +void print_debuginfo(uintptr_t eip); + +#endif /* !__KERN_DEBUG_KDEBUG_H__ */ + diff --git a/lab8/kern/debug/kmonitor.c b/lab8/kern/debug/kmonitor.c new file mode 100644 index 0000000..af7c401 --- /dev/null +++ b/lab8/kern/debug/kmonitor.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include + +/* * + * Simple command-line kernel monitor useful for controlling the + * kernel and exploring the system interactively. + * */ + +struct command { + const char *name; + const char *desc; + // return -1 to force monitor to exit + int(*func)(int argc, char **argv, struct trapframe *tf); +}; + +static struct command commands[] = { + {"help", "Display this list of commands.", mon_help}, + {"kerninfo", "Display information about the kernel.", mon_kerninfo}, + {"backtrace", "Print backtrace of stack frame.", mon_backtrace}, +}; + +/* return if kernel is panic, in kern/debug/panic.c */ +bool is_kernel_panic(void); + +#define NCOMMANDS (sizeof(commands)/sizeof(struct command)) + +/***** Kernel monitor command interpreter *****/ + +#define MAXARGS 16 +#define WHITESPACE " \t\n\r" + +/* parse - parse the command buffer into whitespace-separated arguments */ +static int +parse(char *buf, char **argv) { + int argc = 0; + while (1) { + // find global whitespace + while (*buf != '\0' && strchr(WHITESPACE, *buf) != NULL) { + *buf ++ = '\0'; + } + if (*buf == '\0') { + break; + } + + // save and scan past next arg + if (argc == MAXARGS - 1) { + cprintf("Too many arguments (max %d).\n", MAXARGS); + } + argv[argc ++] = buf; + while (*buf != '\0' && strchr(WHITESPACE, *buf) == NULL) { + buf ++; + } + } + return argc; +} + +/* * + * runcmd - parse the input string, split it into separated arguments + * and then lookup and invoke some related commands/ + * */ +static int +runcmd(char *buf, struct trapframe *tf) { + char *argv[MAXARGS]; + int argc = parse(buf, argv); + if (argc == 0) { + return 0; + } + int i; + for (i = 0; i < NCOMMANDS; i ++) { + if (strcmp(commands[i].name, argv[0]) == 0) { + return commands[i].func(argc - 1, argv + 1, tf); + } + } + cprintf("Unknown command '%s'\n", argv[0]); + return 0; +} + +/***** Implementations of basic kernel monitor commands *****/ + +void +kmonitor(struct trapframe *tf) { + cprintf("Welcome to the kernel debug monitor!!\n"); + cprintf("Type 'help' for a list of commands.\n"); + + if (tf != NULL) { + print_trapframe(tf); + } + + char *buf; + while (1) { + if ((buf = readline("K> ")) != NULL) { + if (runcmd(buf, tf) < 0) { + break; + } + } + } +} + +/* mon_help - print the information about mon_* functions */ +int +mon_help(int argc, char **argv, struct trapframe *tf) { + int i; + for (i = 0; i < NCOMMANDS; i ++) { + cprintf("%s - %s\n", commands[i].name, commands[i].desc); + } + return 0; +} + +/* * + * mon_kerninfo - call print_kerninfo in kern/debug/kdebug.c to + * print the memory occupancy in kernel. + * */ +int +mon_kerninfo(int argc, char **argv, struct trapframe *tf) { + print_kerninfo(); + return 0; +} + +/* * + * mon_backtrace - call print_stackframe in kern/debug/kdebug.c to + * print a backtrace of the stack. + * */ +int +mon_backtrace(int argc, char **argv, struct trapframe *tf) { + print_stackframe(); + return 0; +} + diff --git a/lab8/kern/debug/kmonitor.h b/lab8/kern/debug/kmonitor.h new file mode 100644 index 0000000..67dfe64 --- /dev/null +++ b/lab8/kern/debug/kmonitor.h @@ -0,0 +1,19 @@ +#ifndef __KERN_DEBUG_MONITOR_H__ +#define __KERN_DEBUG_MONITOR_H__ + +#include + +void kmonitor(struct trapframe *tf); + +int mon_help(int argc, char **argv, struct trapframe *tf); +int mon_kerninfo(int argc, char **argv, struct trapframe *tf); +int mon_backtrace(int argc, char **argv, struct trapframe *tf); +int mon_continue(int argc, char **argv, struct trapframe *tf); +int mon_step(int argc, char **argv, struct trapframe *tf); +int mon_breakpoint(int argc, char **argv, struct trapframe *tf); +int mon_watchpoint(int argc, char **argv, struct trapframe *tf); +int mon_delete_dr(int argc, char **argv, struct trapframe *tf); +int mon_list_dr(int argc, char **argv, struct trapframe *tf); + +#endif /* !__KERN_DEBUG_MONITOR_H__ */ + diff --git a/lab8/kern/debug/panic.c b/lab8/kern/debug/panic.c new file mode 100644 index 0000000..1d6a237 --- /dev/null +++ b/lab8/kern/debug/panic.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static bool is_panic = 0; + +/* * + * __panic - __panic is called on unresolvable fatal errors. it prints + * "panic: 'message'", and then enters the kernel monitor. + * */ +void +__panic(const char *file, int line, const char *fmt, ...) { + if (is_panic) { + goto panic_dead; + } + is_panic = 1; + + // print the 'message' + va_list ap; + va_start(ap, fmt); + cprintf("kernel panic at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); + +panic_dead: + // No debug monitor here + sbi_shutdown(); + intr_disable(); + while (1) { + kmonitor(NULL); + } +} + +/* __warn - like panic, but don't */ +void +__warn(const char *file, int line, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + cprintf("kernel warning at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); +} + +bool +is_kernel_panic(void) { + return is_panic; +} + diff --git a/lab8/kern/debug/stab.h b/lab8/kern/debug/stab.h new file mode 100644 index 0000000..8d5cea3 --- /dev/null +++ b/lab8/kern/debug/stab.h @@ -0,0 +1,54 @@ +#ifndef __KERN_DEBUG_STAB_H__ +#define __KERN_DEBUG_STAB_H__ + +#include + +/* * + * STABS debugging info + * + * The kernel debugger can understand some debugging information in + * the STABS format. For more information on this format, see + * http://sources.redhat.com/gdb/onlinedocs/stabs_toc.html + * + * The constants below define some symbol types used by various debuggers + * and compilers. Kernel uses the N_SO, N_SOL, N_FUN, and N_SLINE types. + * */ + +#define N_GSYM 0x20 // global symbol +#define N_FNAME 0x22 // F77 function name +#define N_FUN 0x24 // procedure name +#define N_STSYM 0x26 // data segment variable +#define N_LCSYM 0x28 // bss segment variable +#define N_MAIN 0x2a // main function name +#define N_PC 0x30 // global Pascal symbol +#define N_RSYM 0x40 // register variable +#define N_SLINE 0x44 // text segment line number +#define N_DSLINE 0x46 // data segment line number +#define N_BSLINE 0x48 // bss segment line number +#define N_SSYM 0x60 // structure/union element +#define N_SO 0x64 // main source file name +#define N_LSYM 0x80 // stack variable +#define N_BINCL 0x82 // include file beginning +#define N_SOL 0x84 // included source file name +#define N_PSYM 0xa0 // parameter variable +#define N_EINCL 0xa2 // include file end +#define N_ENTRY 0xa4 // alternate entry point +#define N_LBRAC 0xc0 // left bracket +#define N_EXCL 0xc2 // deleted include file +#define N_RBRAC 0xe0 // right bracket +#define N_BCOMM 0xe2 // begin common +#define N_ECOMM 0xe4 // end common +#define N_ECOML 0xe8 // end common (local name) +#define N_LENG 0xfe // length of preceding entry + +/* Entries in the STABS table are formatted as follows. */ +struct stab { + uint32_t n_strx; // index into string table of name + uint8_t n_type; // type of symbol + uint8_t n_other; // misc info (usually empty) + uint16_t n_desc; // description field + uintptr_t n_value; // value of symbol +}; + +#endif /* !__KERN_DEBUG_STAB_H__ */ + diff --git a/lab8/kern/driver/clock.c b/lab8/kern/driver/clock.c new file mode 100644 index 0000000..7af6122 --- /dev/null +++ b/lab8/kern/driver/clock.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +volatile size_t ticks; + +static inline uint64_t get_cycles(void) { +#if __riscv_xlen == 64 + uint64_t n; + __asm__ __volatile__("rdtime %0" : "=r"(n)); + return n; +#else + uint32_t lo, hi, tmp; + __asm__ __volatile__( + "1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r"(hi), "=&r"(lo), "=&r"(tmp)); + return ((uint64_t)hi << 32) | lo; +#endif +} + +static uint64_t timebase = 100000; + +/* * + * clock_init - initialize 8253 clock to interrupt 100 times per second, + * and then enable IRQ_TIMER. + * */ +void clock_init(void) { + set_csr(sie, MIP_STIP); + + clock_set_next_event(); + // initialize time counter 'ticks' to zero + ticks = 0; + + cprintf("++ setup timer interrupts\n"); +} + +void clock_set_next_event(void) { sbi_set_timer(get_cycles() + timebase); } diff --git a/lab8/kern/driver/clock.h b/lab8/kern/driver/clock.h new file mode 100644 index 0000000..3b75c98 --- /dev/null +++ b/lab8/kern/driver/clock.h @@ -0,0 +1,11 @@ +#ifndef __KERN_DRIVER_CLOCK_H__ +#define __KERN_DRIVER_CLOCK_H__ + +#include + +extern volatile size_t ticks; + +void clock_init(void); +void clock_set_next_event(void); + +#endif /* !__KERN_DRIVER_CLOCK_H__ */ diff --git a/lab8/kern/driver/console.c b/lab8/kern/driver/console.c new file mode 100644 index 0000000..2c61a86 --- /dev/null +++ b/lab8/kern/driver/console.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#define CONSBUFSIZE 512 + +static struct { + uint8_t buf[CONSBUFSIZE]; + uint32_t rpos; + uint32_t wpos; +} cons; + +/* * + * cons_intr - called by device interrupt routines to feed input + * characters into the circular console input buffer. + * */ +void cons_intr(int (*proc)(void)) { + int c; + while ((c = (*proc)()) != -1) { + if (c != 0) { + cons.buf[cons.wpos++] = c; + if (cons.wpos == CONSBUFSIZE) { + cons.wpos = 0; + } + } + } +} + +/* kbd_intr - try to feed input characters from keyboard */ +void kbd_intr(void) { + serial_intr(); +} + +/* serial_proc_data - get data from serial port */ +int serial_proc_data(void) { + int c = sbi_console_getchar(); + if (c < 0) { + return -1; + } + if (c == 127) { + c = '\b'; + } + return c; +} + +/* serial_intr - try to feed input characters from serial port */ +void serial_intr(void) { + cons_intr(serial_proc_data); +} + +/* serial_putc - print character to serial port */ +void serial_putc(int c) { + if (c != '\b') { + sbi_console_putchar(c); + } else { + sbi_console_putchar('\b'); + sbi_console_putchar(' '); + sbi_console_putchar('\b'); + } +} + +/* cons_init - initializes the console devices */ +void cons_init(void) { + sbi_console_getchar(); +} + +/* cons_putc - print a single character @c to console devices */ +void cons_putc(int c) { + bool intr_flag; + local_intr_save(intr_flag); + { + serial_putc(c); + } + local_intr_restore(intr_flag); +} + +/* * + * cons_getc - return the next input character from console, + * or 0 if none waiting. + * */ +int cons_getc(void) { + int c = 0; + bool intr_flag; + local_intr_save(intr_flag); + { + // poll for any pending input characters, + // so that this function works even when interrupts are disabled + // (e.g., when called from the kernel monitor). + serial_intr(); + + // grab the next character from the input buffer. + if (cons.rpos != cons.wpos) { + c = cons.buf[cons.rpos++]; + if (cons.rpos == CONSBUFSIZE) { + cons.rpos = 0; + } + } + } + local_intr_restore(intr_flag); + return c; +} diff --git a/lab8/kern/driver/console.h b/lab8/kern/driver/console.h new file mode 100644 index 0000000..72e6167 --- /dev/null +++ b/lab8/kern/driver/console.h @@ -0,0 +1,11 @@ +#ifndef __KERN_DRIVER_CONSOLE_H__ +#define __KERN_DRIVER_CONSOLE_H__ + +void cons_init(void); +void cons_putc(int c); +int cons_getc(void); +void serial_intr(void); +void kbd_intr(void); + +#endif /* !__KERN_DRIVER_CONSOLE_H__ */ + diff --git a/lab8/kern/driver/fpioa.c b/lab8/kern/driver/fpioa.c new file mode 100644 index 0000000..9f41204 --- /dev/null +++ b/lab8/kern/driver/fpioa.c @@ -0,0 +1,5288 @@ +// +// Created by lumin on 2020/11/2. +// +#include + +/** + * @brief Internal used FPIOA function initialize cell + * + * This is NOT fpioa_io_config_t, can't assign directly + * + */ +typedef struct +{ + uint32_t ch_sel: 8; + /* Channel select from 256 input. */ + uint32_t ds: 4; + /* Driving selector. */ + uint32_t oe_en: 1; + /* Static output enable, will AND with OE_INV. */ + uint32_t oe_inv: 1; + /* Invert output enable. */ + uint32_t do_sel: 1; + /* Data output select: 0 for DO, 1 for OE. */ + uint32_t do_inv: 1; + /* Invert the result of data output select (DO_SEL). */ + uint32_t pu: 1; + /* Pull up enable. 0 for nothing, 1 for pull up. */ + uint32_t pd: 1; + /* Pull down enable. 0 for nothing, 1 for pull down. */ + uint32_t resv0: 1; + /* Reserved bits. */ + uint32_t sl: 1; + /* Slew rate control enable. */ + uint32_t ie_en: 1; + /* Static input enable, will AND with IE_INV. */ + uint32_t ie_inv: 1; + /* Invert input enable. */ + uint32_t di_inv: 1; + /* Invert Data input. */ + uint32_t st: 1; + /* Schmitt trigger. */ + uint32_t tie_en: 1; + /* Input tie enable, 1 for enable, 0 for disable. */ + uint32_t tie_val: 1; + /* Input tie value, 1 for high, 0 for low. */ + uint32_t resv1: 5; + /* Reserved bits. */ + uint32_t pad_di: 1; + /* Read current PAD's data input. */ +} __attribute__((packed, aligned(4))) fpioa_assign_t; + +/* Function list */ +static const fpioa_assign_t function_config[FUNC_MAX] = + { + { + .ch_sel = FUNC_JTAG_TCLK, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_JTAG_TDI, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_JTAG_TMS, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_JTAG_TDO, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D5, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D6, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_D7, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_SS0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_SS1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_SS2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_SS3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_ARB, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 1, + .tie_val = 1, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI0_SCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UARTHS_RX, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UARTHS_TX, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV6, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV7, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CLK_SPI1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CLK_I2C1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS5, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS6, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS7, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS8, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS9, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS10, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS11, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS12, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS13, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS14, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS15, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS16, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS17, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS18, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS19, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS20, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS21, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS22, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS23, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS24, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS25, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS26, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS27, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS28, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS29, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS30, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIOHS31, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO5, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO6, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_GPIO7, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_RX, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_TX, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_RX, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_TX, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_RX, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_TX, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D5, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D6, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_D7, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_SS0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_SS1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_SS2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_SS3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_ARB, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 1, + .tie_val = 1, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI1_SCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI_SLAVE_D0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 1, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI_SLAVE_SS, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SPI_SLAVE_SCLK, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_MCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_SCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_WS, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_IN_D0, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_IN_D1, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_IN_D2, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_IN_D3, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_OUT_D0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_OUT_D1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_OUT_D2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S0_OUT_D3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_MCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_SCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_WS, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_IN_D0, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_IN_D1, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_IN_D2, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_IN_D3, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_OUT_D0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_OUT_D1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_OUT_D2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S1_OUT_D3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_MCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_SCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_WS, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_IN_D0, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_IN_D1, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_IN_D2, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_IN_D3, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_OUT_D0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_OUT_D1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_OUT_D2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2S2_OUT_D3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV0, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV1, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV2, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV3, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV4, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_RESV5, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2C0_SCLK, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2C0_SDA, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2C1_SCLK, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2C1_SDA, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2C2_SCLK, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_I2C2_SDA, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_XCLK, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_RST, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_PWDN, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_VSYNC, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_HREF, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_PCLK, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D0, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D1, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D2, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D3, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D4, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D5, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D6, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CMOS_D7, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SCCB_SCLK, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_SCCB_SDA, + .ds = 0x0, + .oe_en = 1, + .oe_inv = 1, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_CTS, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_DSR, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_DCD, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_RI, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_SIR_IN, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_DTR, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_RTS, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_OUT2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_OUT1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_SIR_OUT, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_BAUD, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_RE, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_DE, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART1_RS485_EN, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_CTS, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_DSR, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_DCD, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_RI, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_SIR_IN, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_DTR, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_RTS, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_OUT2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_OUT1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_SIR_OUT, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_BAUD, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_RE, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_DE, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART2_RS485_EN, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_CTS, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_DSR, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_DCD, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_RI, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_SIR_IN, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_DTR, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_RTS, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_OUT2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_OUT1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_SIR_OUT, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_BAUD, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_RE, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_DE, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_UART3_RS485_EN, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER0_TOGGLE1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER0_TOGGLE2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER0_TOGGLE3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER0_TOGGLE4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER1_TOGGLE1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER1_TOGGLE2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER1_TOGGLE3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER1_TOGGLE4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER2_TOGGLE1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER2_TOGGLE2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER2_TOGGLE3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_TIMER2_TOGGLE4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CLK_SPI2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CLK_I2C2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL5, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL6, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL7, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL8, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL9, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL10, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL11, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL12, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL13, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL14, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 1, + .pd = 0, + .resv1 = 0, + .sl = 1, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL15, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL16, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL17, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_CONSTANT, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_INTERNAL18, + .ds = 0x0, + .oe_en = 0, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 1, + .ie_inv = 0, + .di_inv = 0, + .st = 1, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG0, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG1, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG2, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG3, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG4, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG5, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG6, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG7, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG8, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG9, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG10, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG11, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG12, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG13, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG14, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG15, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG16, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG17, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG18, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG19, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG20, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG21, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG22, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG23, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG24, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG25, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG26, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG27, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG28, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG29, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG30, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + { + .ch_sel = FUNC_DEBUG31, + .ds = 0xf, + .oe_en = 1, + .oe_inv = 0, + .do_sel = 0, + .do_inv = 0, + .pu = 0, + .pd = 0, + .resv1 = 0, + .sl = 0, + .ie_en = 0, + .ie_inv = 0, + .di_inv = 0, + .st = 0, + .tie_en = 0, + .tie_val = 0, + .resv0 = 0, + .pad_di = 0 + }, + }; + +volatile fpioa_t *const fpioa = (volatile fpioa_t *)FPIOA_BASE_ADDR; + +static void fpioa_set_function_raw(int pin, fpioa_function_t function); + +int fpioa_set_function(int pin, fpioa_function_t function) +{ + /** + * FUNC_RESV0 is the fallback, so steps are: + * 1. sanity check + * 2. if 'function' is FUNC_RESV0, we can directly set without check collision + * (fallback can be occupied simultaneously by multiple pins) and return + * 3. while most 'function' is not FUNC_RESV0, we must first check collision and + * map previous owner(if there is one and not our pin) to FUNC_RESV0 + * 4. after collision check, we are free to set it + */ + if (pin >= FPIOA_NUM_IO|| pin <0 || function >= FUNC_MAX || function <0) + return -1; + + if (function == FUNC_RESV0) + goto done; + + volatile fpioa_io_config_t *config; + for (int i = 0; i < FPIOA_NUM_IO; ++i) { + /* + * Here we have to first declare(cast) config as a + * volatile pointer and use it to access its ch_sel, + * as only in this way the compiler will generate right + * instructions(access the whole word rather than partial byte). + */ + config = &fpioa->io[i]; + if ((config->ch_sel == function) && (i != pin)) { + fpioa_set_function_raw(i, FUNC_RESV0); + break; + } + } + +done: + fpioa_set_function_raw(pin,function); + return 0; +} + +/** + * perform the real set operation + */ +static void fpioa_set_function_raw(int pin, fpioa_function_t function) +{ + fpioa->io[pin] = (const fpioa_io_config_t) + { + .ch_sel = function_config[function].ch_sel, + .ds = function_config[function].ds, + .oe_en = function_config[function].oe_en, + .oe_inv = function_config[function].oe_inv, + .do_sel = function_config[function].do_sel, + .do_inv = function_config[function].do_inv, + .pu = function_config[function].pu, + .pd = function_config[function].pd, + .sl = function_config[function].sl, + .ie_en = function_config[function].ie_en, + .ie_inv = function_config[function].ie_inv, + .di_inv = function_config[function].di_inv, + .st= function_config[function].st, + /* resv and pad_di do not need initialization */ + }; +} + +int fpioa_set_io_pull(int pin, fpioa_pull_t pull) +{ + /* Sanity check */ + if (pin < 0 || pin >= FPIOA_NUM_IO || pull >= FPIOA_PULL_MAX) + return -1; + + fpioa_io_config_t config = fpioa->io[pin]; + + switch (pull) + { + case FPIOA_PULL_NONE: + config.pu = 0; + config.pd = 0; + break; + case FPIOA_PULL_DOWN: + config.pu = 0; + config.pd = 1; + break; + case FPIOA_PULL_UP: + config.pu = 1; + config.pd = 0; + break; + default: + break; + } + + fpioa->io[pin] = config; + return 0; +} + +int fpioa_get_owner_pin_by_function(fpioa_function_t function) +{ + volatile fpioa_io_config_t *config; + for (int pin = 0; pin < FPIOA_NUM_IO; pin++){ + /* + * Here we have to first declare(cast) config as a + * volatile pointer and use it to access its ch_sel, + * as only in this way the compiler will generate right + * instructions(access the whole word rather than partial byte). + */ + config = &fpioa->io[pin]; + if (config->ch_sel == function) + return pin; + } + + return -1; +} \ No newline at end of file diff --git a/lab8/kern/driver/fpioa.h b/lab8/kern/driver/fpioa.h new file mode 100644 index 0000000..4d52b93 --- /dev/null +++ b/lab8/kern/driver/fpioa.h @@ -0,0 +1,458 @@ +// +// Created by lumin on 2020/11/2. +// + +#ifndef LAB8_FPIOA_H +#define LAB8_FPIOA_H + +#include +#include + +#define FPIOA_NUM_IO (48) + +typedef enum +{ + FUNC_JTAG_TCLK = 0, /*!< JTAG Test Clock */ + FUNC_JTAG_TDI = 1, /*!< JTAG Test Data In */ + FUNC_JTAG_TMS = 2, /*!< JTAG Test Mode Select */ + FUNC_JTAG_TDO = 3, /*!< JTAG Test Data Out */ + FUNC_SPI0_D0 = 4, /*!< SPI0 Data 0 */ + FUNC_SPI0_D1 = 5, /*!< SPI0 Data 1 */ + FUNC_SPI0_D2 = 6, /*!< SPI0 Data 2 */ + FUNC_SPI0_D3 = 7, /*!< SPI0 Data 3 */ + FUNC_SPI0_D4 = 8, /*!< SPI0 Data 4 */ + FUNC_SPI0_D5 = 9, /*!< SPI0 Data 5 */ + FUNC_SPI0_D6 = 10, /*!< SPI0 Data 6 */ + FUNC_SPI0_D7 = 11, /*!< SPI0 Data 7 */ + FUNC_SPI0_SS0 = 12, /*!< SPI0 Chip Select 0 */ + FUNC_SPI0_SS1 = 13, /*!< SPI0 Chip Select 1 */ + FUNC_SPI0_SS2 = 14, /*!< SPI0 Chip Select 2 */ + FUNC_SPI0_SS3 = 15, /*!< SPI0 Chip Select 3 */ + FUNC_SPI0_ARB = 16, /*!< SPI0 Arbitration */ + FUNC_SPI0_SCLK = 17, /*!< SPI0 Serial Clock */ + FUNC_UARTHS_RX = 18, /*!< UART High speed Receiver */ + FUNC_UARTHS_TX = 19, /*!< UART High speed Transmitter */ + FUNC_RESV6 = 20, /*!< Reserved function */ + FUNC_RESV7 = 21, /*!< Reserved function */ + FUNC_CLK_SPI1 = 22, /*!< Clock SPI1 */ + FUNC_CLK_I2C1 = 23, /*!< Clock I2C1 */ + FUNC_GPIOHS0 = 24, /*!< GPIO High speed 0 */ + FUNC_GPIOHS1 = 25, /*!< GPIO High speed 1 */ + FUNC_GPIOHS2 = 26, /*!< GPIO High speed 2 */ + FUNC_GPIOHS3 = 27, /*!< GPIO High speed 3 */ + FUNC_GPIOHS4 = 28, /*!< GPIO High speed 4 */ + FUNC_GPIOHS5 = 29, /*!< GPIO High speed 5 */ + FUNC_GPIOHS6 = 30, /*!< GPIO High speed 6 */ + FUNC_GPIOHS7 = 31, /*!< GPIO High speed 7 */ + FUNC_GPIOHS8 = 32, /*!< GPIO High speed 8 */ + FUNC_GPIOHS9 = 33, /*!< GPIO High speed 9 */ + FUNC_GPIOHS10 = 34, /*!< GPIO High speed 10 */ + FUNC_GPIOHS11 = 35, /*!< GPIO High speed 11 */ + FUNC_GPIOHS12 = 36, /*!< GPIO High speed 12 */ + FUNC_GPIOHS13 = 37, /*!< GPIO High speed 13 */ + FUNC_GPIOHS14 = 38, /*!< GPIO High speed 14 */ + FUNC_GPIOHS15 = 39, /*!< GPIO High speed 15 */ + FUNC_GPIOHS16 = 40, /*!< GPIO High speed 16 */ + FUNC_GPIOHS17 = 41, /*!< GPIO High speed 17 */ + FUNC_GPIOHS18 = 42, /*!< GPIO High speed 18 */ + FUNC_GPIOHS19 = 43, /*!< GPIO High speed 19 */ + FUNC_GPIOHS20 = 44, /*!< GPIO High speed 20 */ + FUNC_GPIOHS21 = 45, /*!< GPIO High speed 21 */ + FUNC_GPIOHS22 = 46, /*!< GPIO High speed 22 */ + FUNC_GPIOHS23 = 47, /*!< GPIO High speed 23 */ + FUNC_GPIOHS24 = 48, /*!< GPIO High speed 24 */ + FUNC_GPIOHS25 = 49, /*!< GPIO High speed 25 */ + FUNC_GPIOHS26 = 50, /*!< GPIO High speed 26 */ + FUNC_GPIOHS27 = 51, /*!< GPIO High speed 27 */ + FUNC_GPIOHS28 = 52, /*!< GPIO High speed 28 */ + FUNC_GPIOHS29 = 53, /*!< GPIO High speed 29 */ + FUNC_GPIOHS30 = 54, /*!< GPIO High speed 30 */ + FUNC_GPIOHS31 = 55, /*!< GPIO High speed 31 */ + FUNC_GPIO0 = 56, /*!< GPIO pin 0 */ + FUNC_GPIO1 = 57, /*!< GPIO pin 1 */ + FUNC_GPIO2 = 58, /*!< GPIO pin 2 */ + FUNC_GPIO3 = 59, /*!< GPIO pin 3 */ + FUNC_GPIO4 = 60, /*!< GPIO pin 4 */ + FUNC_GPIO5 = 61, /*!< GPIO pin 5 */ + FUNC_GPIO6 = 62, /*!< GPIO pin 6 */ + FUNC_GPIO7 = 63, /*!< GPIO pin 7 */ + FUNC_UART1_RX = 64, /*!< UART1 Receiver */ + FUNC_UART1_TX = 65, /*!< UART1 Transmitter */ + FUNC_UART2_RX = 66, /*!< UART2 Receiver */ + FUNC_UART2_TX = 67, /*!< UART2 Transmitter */ + FUNC_UART3_RX = 68, /*!< UART3 Receiver */ + FUNC_UART3_TX = 69, /*!< UART3 Transmitter */ + FUNC_SPI1_D0 = 70, /*!< SPI1 Data 0 */ + FUNC_SPI1_D1 = 71, /*!< SPI1 Data 1 */ + FUNC_SPI1_D2 = 72, /*!< SPI1 Data 2 */ + FUNC_SPI1_D3 = 73, /*!< SPI1 Data 3 */ + FUNC_SPI1_D4 = 74, /*!< SPI1 Data 4 */ + FUNC_SPI1_D5 = 75, /*!< SPI1 Data 5 */ + FUNC_SPI1_D6 = 76, /*!< SPI1 Data 6 */ + FUNC_SPI1_D7 = 77, /*!< SPI1 Data 7 */ + FUNC_SPI1_SS0 = 78, /*!< SPI1 Chip Select 0 */ + FUNC_SPI1_SS1 = 79, /*!< SPI1 Chip Select 1 */ + FUNC_SPI1_SS2 = 80, /*!< SPI1 Chip Select 2 */ + FUNC_SPI1_SS3 = 81, /*!< SPI1 Chip Select 3 */ + FUNC_SPI1_ARB = 82, /*!< SPI1 Arbitration */ + FUNC_SPI1_SCLK = 83, /*!< SPI1 Serial Clock */ + FUNC_SPI_SLAVE_D0 = 84, /*!< SPI Slave Data 0 */ + FUNC_SPI_SLAVE_SS = 85, /*!< SPI Slave Select */ + FUNC_SPI_SLAVE_SCLK = 86, /*!< SPI Slave Serial Clock */ + FUNC_I2S0_MCLK = 87, /*!< I2S0 Master Clock */ + FUNC_I2S0_SCLK = 88, /*!< I2S0 Serial Clock(BCLK) */ + FUNC_I2S0_WS = 89, /*!< I2S0 Word Select(LRCLK) */ + FUNC_I2S0_IN_D0 = 90, /*!< I2S0 Serial Data Input 0 */ + FUNC_I2S0_IN_D1 = 91, /*!< I2S0 Serial Data Input 1 */ + FUNC_I2S0_IN_D2 = 92, /*!< I2S0 Serial Data Input 2 */ + FUNC_I2S0_IN_D3 = 93, /*!< I2S0 Serial Data Input 3 */ + FUNC_I2S0_OUT_D0 = 94, /*!< I2S0 Serial Data Output 0 */ + FUNC_I2S0_OUT_D1 = 95, /*!< I2S0 Serial Data Output 1 */ + FUNC_I2S0_OUT_D2 = 96, /*!< I2S0 Serial Data Output 2 */ + FUNC_I2S0_OUT_D3 = 97, /*!< I2S0 Serial Data Output 3 */ + FUNC_I2S1_MCLK = 98, /*!< I2S1 Master Clock */ + FUNC_I2S1_SCLK = 99, /*!< I2S1 Serial Clock(BCLK) */ + FUNC_I2S1_WS = 100, /*!< I2S1 Word Select(LRCLK) */ + FUNC_I2S1_IN_D0 = 101, /*!< I2S1 Serial Data Input 0 */ + FUNC_I2S1_IN_D1 = 102, /*!< I2S1 Serial Data Input 1 */ + FUNC_I2S1_IN_D2 = 103, /*!< I2S1 Serial Data Input 2 */ + FUNC_I2S1_IN_D3 = 104, /*!< I2S1 Serial Data Input 3 */ + FUNC_I2S1_OUT_D0 = 105, /*!< I2S1 Serial Data Output 0 */ + FUNC_I2S1_OUT_D1 = 106, /*!< I2S1 Serial Data Output 1 */ + FUNC_I2S1_OUT_D2 = 107, /*!< I2S1 Serial Data Output 2 */ + FUNC_I2S1_OUT_D3 = 108, /*!< I2S1 Serial Data Output 3 */ + FUNC_I2S2_MCLK = 109, /*!< I2S2 Master Clock */ + FUNC_I2S2_SCLK = 110, /*!< I2S2 Serial Clock(BCLK) */ + FUNC_I2S2_WS = 111, /*!< I2S2 Word Select(LRCLK) */ + FUNC_I2S2_IN_D0 = 112, /*!< I2S2 Serial Data Input 0 */ + FUNC_I2S2_IN_D1 = 113, /*!< I2S2 Serial Data Input 1 */ + FUNC_I2S2_IN_D2 = 114, /*!< I2S2 Serial Data Input 2 */ + FUNC_I2S2_IN_D3 = 115, /*!< I2S2 Serial Data Input 3 */ + FUNC_I2S2_OUT_D0 = 116, /*!< I2S2 Serial Data Output 0 */ + FUNC_I2S2_OUT_D1 = 117, /*!< I2S2 Serial Data Output 1 */ + FUNC_I2S2_OUT_D2 = 118, /*!< I2S2 Serial Data Output 2 */ + FUNC_I2S2_OUT_D3 = 119, /*!< I2S2 Serial Data Output 3 */ + FUNC_RESV0 = 120, /*!< Reserved function */ + FUNC_RESV1 = 121, /*!< Reserved function */ + FUNC_RESV2 = 122, /*!< Reserved function */ + FUNC_RESV3 = 123, /*!< Reserved function */ + FUNC_RESV4 = 124, /*!< Reserved function */ + FUNC_RESV5 = 125, /*!< Reserved function */ + FUNC_I2C0_SCLK = 126, /*!< I2C0 Serial Clock */ + FUNC_I2C0_SDA = 127, /*!< I2C0 Serial Data */ + FUNC_I2C1_SCLK = 128, /*!< I2C1 Serial Clock */ + FUNC_I2C1_SDA = 129, /*!< I2C1 Serial Data */ + FUNC_I2C2_SCLK = 130, /*!< I2C2 Serial Clock */ + FUNC_I2C2_SDA = 131, /*!< I2C2 Serial Data */ + FUNC_CMOS_XCLK = 132, /*!< DVP System Clock */ + FUNC_CMOS_RST = 133, /*!< DVP System Reset */ + FUNC_CMOS_PWDN = 134, /*!< DVP Power Down Mode */ + FUNC_CMOS_VSYNC = 135, /*!< DVP Vertical Sync */ + FUNC_CMOS_HREF = 136, /*!< DVP Horizontal Reference output */ + FUNC_CMOS_PCLK = 137, /*!< Pixel Clock */ + FUNC_CMOS_D0 = 138, /*!< Data Bit 0 */ + FUNC_CMOS_D1 = 139, /*!< Data Bit 1 */ + FUNC_CMOS_D2 = 140, /*!< Data Bit 2 */ + FUNC_CMOS_D3 = 141, /*!< Data Bit 3 */ + FUNC_CMOS_D4 = 142, /*!< Data Bit 4 */ + FUNC_CMOS_D5 = 143, /*!< Data Bit 5 */ + FUNC_CMOS_D6 = 144, /*!< Data Bit 6 */ + FUNC_CMOS_D7 = 145, /*!< Data Bit 7 */ + FUNC_SCCB_SCLK = 146, /*!< SCCB Serial Clock */ + FUNC_SCCB_SDA = 147, /*!< SCCB Serial Data */ + FUNC_UART1_CTS = 148, /*!< UART1 Clear To Send */ + FUNC_UART1_DSR = 149, /*!< UART1 Data Set Ready */ + FUNC_UART1_DCD = 150, /*!< UART1 Data Carrier Detect */ + FUNC_UART1_RI = 151, /*!< UART1 Ring Indicator */ + FUNC_UART1_SIR_IN = 152, /*!< UART1 Serial Infrared Input */ + FUNC_UART1_DTR = 153, /*!< UART1 Data Terminal Ready */ + FUNC_UART1_RTS = 154, /*!< UART1 Request To Send */ + FUNC_UART1_OUT2 = 155, /*!< UART1 User-designated Output 2 */ + FUNC_UART1_OUT1 = 156, /*!< UART1 User-designated Output 1 */ + FUNC_UART1_SIR_OUT = 157, /*!< UART1 Serial Infrared Output */ + FUNC_UART1_BAUD = 158, /*!< UART1 Transmit Clock Output */ + FUNC_UART1_RE = 159, /*!< UART1 Receiver Output Enable */ + FUNC_UART1_DE = 160, /*!< UART1 Driver Output Enable */ + FUNC_UART1_RS485_EN = 161, /*!< UART1 RS485 Enable */ + FUNC_UART2_CTS = 162, /*!< UART2 Clear To Send */ + FUNC_UART2_DSR = 163, /*!< UART2 Data Set Ready */ + FUNC_UART2_DCD = 164, /*!< UART2 Data Carrier Detect */ + FUNC_UART2_RI = 165, /*!< UART2 Ring Indicator */ + FUNC_UART2_SIR_IN = 166, /*!< UART2 Serial Infrared Input */ + FUNC_UART2_DTR = 167, /*!< UART2 Data Terminal Ready */ + FUNC_UART2_RTS = 168, /*!< UART2 Request To Send */ + FUNC_UART2_OUT2 = 169, /*!< UART2 User-designated Output 2 */ + FUNC_UART2_OUT1 = 170, /*!< UART2 User-designated Output 1 */ + FUNC_UART2_SIR_OUT = 171, /*!< UART2 Serial Infrared Output */ + FUNC_UART2_BAUD = 172, /*!< UART2 Transmit Clock Output */ + FUNC_UART2_RE = 173, /*!< UART2 Receiver Output Enable */ + FUNC_UART2_DE = 174, /*!< UART2 Driver Output Enable */ + FUNC_UART2_RS485_EN = 175, /*!< UART2 RS485 Enable */ + FUNC_UART3_CTS = 176, /*!< UART3 Clear To Send */ + FUNC_UART3_DSR = 177, /*!< UART3 Data Set Ready */ + FUNC_UART3_DCD = 178, /*!< UART3 Data Carrier Detect */ + FUNC_UART3_RI = 179, /*!< UART3 Ring Indicator */ + FUNC_UART3_SIR_IN = 180, /*!< UART3 Serial Infrared Input */ + FUNC_UART3_DTR = 181, /*!< UART3 Data Terminal Ready */ + FUNC_UART3_RTS = 182, /*!< UART3 Request To Send */ + FUNC_UART3_OUT2 = 183, /*!< UART3 User-designated Output 2 */ + FUNC_UART3_OUT1 = 184, /*!< UART3 User-designated Output 1 */ + FUNC_UART3_SIR_OUT = 185, /*!< UART3 Serial Infrared Output */ + FUNC_UART3_BAUD = 186, /*!< UART3 Transmit Clock Output */ + FUNC_UART3_RE = 187, /*!< UART3 Receiver Output Enable */ + FUNC_UART3_DE = 188, /*!< UART3 Driver Output Enable */ + FUNC_UART3_RS485_EN = 189, /*!< UART3 RS485 Enable */ + FUNC_TIMER0_TOGGLE1 = 190, /*!< TIMER0 Toggle Output 1 */ + FUNC_TIMER0_TOGGLE2 = 191, /*!< TIMER0 Toggle Output 2 */ + FUNC_TIMER0_TOGGLE3 = 192, /*!< TIMER0 Toggle Output 3 */ + FUNC_TIMER0_TOGGLE4 = 193, /*!< TIMER0 Toggle Output 4 */ + FUNC_TIMER1_TOGGLE1 = 194, /*!< TIMER1 Toggle Output 1 */ + FUNC_TIMER1_TOGGLE2 = 195, /*!< TIMER1 Toggle Output 2 */ + FUNC_TIMER1_TOGGLE3 = 196, /*!< TIMER1 Toggle Output 3 */ + FUNC_TIMER1_TOGGLE4 = 197, /*!< TIMER1 Toggle Output 4 */ + FUNC_TIMER2_TOGGLE1 = 198, /*!< TIMER2 Toggle Output 1 */ + FUNC_TIMER2_TOGGLE2 = 199, /*!< TIMER2 Toggle Output 2 */ + FUNC_TIMER2_TOGGLE3 = 200, /*!< TIMER2 Toggle Output 3 */ + FUNC_TIMER2_TOGGLE4 = 201, /*!< TIMER2 Toggle Output 4 */ + FUNC_CLK_SPI2 = 202, /*!< Clock SPI2 */ + FUNC_CLK_I2C2 = 203, /*!< Clock I2C2 */ + FUNC_INTERNAL0 = 204, /*!< Internal function signal 0 */ + FUNC_INTERNAL1 = 205, /*!< Internal function signal 1 */ + FUNC_INTERNAL2 = 206, /*!< Internal function signal 2 */ + FUNC_INTERNAL3 = 207, /*!< Internal function signal 3 */ + FUNC_INTERNAL4 = 208, /*!< Internal function signal 4 */ + FUNC_INTERNAL5 = 209, /*!< Internal function signal 5 */ + FUNC_INTERNAL6 = 210, /*!< Internal function signal 6 */ + FUNC_INTERNAL7 = 211, /*!< Internal function signal 7 */ + FUNC_INTERNAL8 = 212, /*!< Internal function signal 8 */ + FUNC_INTERNAL9 = 213, /*!< Internal function signal 9 */ + FUNC_INTERNAL10 = 214, /*!< Internal function signal 10 */ + FUNC_INTERNAL11 = 215, /*!< Internal function signal 11 */ + FUNC_INTERNAL12 = 216, /*!< Internal function signal 12 */ + FUNC_INTERNAL13 = 217, /*!< Internal function signal 13 */ + FUNC_INTERNAL14 = 218, /*!< Internal function signal 14 */ + FUNC_INTERNAL15 = 219, /*!< Internal function signal 15 */ + FUNC_INTERNAL16 = 220, /*!< Internal function signal 16 */ + FUNC_INTERNAL17 = 221, /*!< Internal function signal 17 */ + FUNC_CONSTANT = 222, /*!< Constant function */ + FUNC_INTERNAL18 = 223, /*!< Internal function signal 18 */ + FUNC_DEBUG0 = 224, /*!< Debug function 0 */ + FUNC_DEBUG1 = 225, /*!< Debug function 1 */ + FUNC_DEBUG2 = 226, /*!< Debug function 2 */ + FUNC_DEBUG3 = 227, /*!< Debug function 3 */ + FUNC_DEBUG4 = 228, /*!< Debug function 4 */ + FUNC_DEBUG5 = 229, /*!< Debug function 5 */ + FUNC_DEBUG6 = 230, /*!< Debug function 6 */ + FUNC_DEBUG7 = 231, /*!< Debug function 7 */ + FUNC_DEBUG8 = 232, /*!< Debug function 8 */ + FUNC_DEBUG9 = 233, /*!< Debug function 9 */ + FUNC_DEBUG10 = 234, /*!< Debug function 10 */ + FUNC_DEBUG11 = 235, /*!< Debug function 11 */ + FUNC_DEBUG12 = 236, /*!< Debug function 12 */ + FUNC_DEBUG13 = 237, /*!< Debug function 13 */ + FUNC_DEBUG14 = 238, /*!< Debug function 14 */ + FUNC_DEBUG15 = 239, /*!< Debug function 15 */ + FUNC_DEBUG16 = 240, /*!< Debug function 16 */ + FUNC_DEBUG17 = 241, /*!< Debug function 17 */ + FUNC_DEBUG18 = 242, /*!< Debug function 18 */ + FUNC_DEBUG19 = 243, /*!< Debug function 19 */ + FUNC_DEBUG20 = 244, /*!< Debug function 20 */ + FUNC_DEBUG21 = 245, /*!< Debug function 21 */ + FUNC_DEBUG22 = 246, /*!< Debug function 22 */ + FUNC_DEBUG23 = 247, /*!< Debug function 23 */ + FUNC_DEBUG24 = 248, /*!< Debug function 24 */ + FUNC_DEBUG25 = 249, /*!< Debug function 25 */ + FUNC_DEBUG26 = 250, /*!< Debug function 26 */ + FUNC_DEBUG27 = 251, /*!< Debug function 27 */ + FUNC_DEBUG28 = 252, /*!< Debug function 28 */ + FUNC_DEBUG29 = 253, /*!< Debug function 29 */ + FUNC_DEBUG30 = 254, /*!< Debug function 30 */ + FUNC_DEBUG31 = 255, /*!< Debug function 31 */ + FUNC_MAX = 256, /*!< Function numbers */ +} fpioa_function_t; + +/** + * @brief FPIOA pull settings + * + * @note FPIOA pull settings description + * + * | PU | PD | Description | + * |-----|-----|-----------------------------------| + * | 0 | 0 | No Pull | + * | 0 | 1 | Pull Down | + * | 1 | 0 | Pull Up | + * | 1 | 1 | Undefined | + * + */ + +/* clang-format off */ +typedef enum +{ + FPIOA_PULL_NONE, /*!< No Pull */ + FPIOA_PULL_DOWN, /*!< Pull Down */ + FPIOA_PULL_UP, /*!< Pull Up */ + FPIOA_PULL_MAX /*!< Count of pull settings */ +} fpioa_pull_t; + +/** + * @brief FPIOA IO + * + * FPIOA IO is the specific pin of the chip package. Every IO + * has a 32bit width register that can independently implement + * schmitt trigger, invert input, invert output, strong pull + * up, driving selector, static input and static output. And more, + * it can implement any pin of any peripheral devices. + * + * @note FPIOA IO's register bits Layout + * + * | Bits | Name |Description | + * |-----------|----------|---------------------------------------------------| + * | 31 | PAD_DI | Read current IO's data input. | + * | 30:24 | NA | Reserved bits. | + * | 23 | ST | Schmitt trigger. | + * | 22 | DI_INV | Invert Data input. | + * | 21 | IE_INV | Invert the input enable signal. | + * | 20 | IE_EN | Input enable. It can disable or enable IO input. | + * | 19 | SL | Slew rate control enable. | + * | 18 | SPU | Strong pull up. | + * | 17 | PD | Pull select: 0 for pull down, 1 for pull up. | + * | 16 | PU | Pull enable. | + * | 15 | DO_INV | Invert the result of data output select (DO_SEL). | + * | 14 | DO_SEL | Data output select: 0 for DO, 1 for OE. | + * | 13 | OE_INV | Invert the output enable signal. | + * | 12 | OE_EN | Output enable.It can disable or enable IO output. | + * | 11:8 | DS | Driving selector. | + * | 7:0 | CH_SEL | Channel select from 256 input. | + * + */ +typedef struct +{ + uint32_t ch_sel : 8; + /*!< Channel select from 256 input. */ + uint32_t ds : 4; + /*!< Driving selector. */ + uint32_t oe_en : 1; + /*!< Static output enable, will AND with OE_INV. */ + uint32_t oe_inv : 1; + /*!< Invert output enable. */ + uint32_t do_sel : 1; + /*!< Data output select: 0 for DO, 1 for OE. */ + uint32_t do_inv : 1; + /*!< Invert the result of data output select (DO_SEL). */ + uint32_t pu : 1; + /*!< Pull up enable. 0 for nothing, 1 for pull up. */ + uint32_t pd : 1; + /*!< Pull down enable. 0 for nothing, 1 for pull down. */ + uint32_t resv0 : 1; + /*!< Reserved bits. */ + uint32_t sl : 1; + /*!< Slew rate control enable. */ + uint32_t ie_en : 1; + /*!< Static input enable, will AND with IE_INV. */ + uint32_t ie_inv : 1; + /*!< Invert input enable. */ + uint32_t di_inv : 1; + /*!< Invert Data input. */ + uint32_t st : 1; + /*!< Schmitt trigger. */ + uint32_t resv1 : 7; + /*!< Reserved bits. */ + uint32_t pad_di : 1; + /*!< Read current IO's data input. */ +} __attribute__((packed,aligned(4))) fpioa_io_config_t; + +/** + * @brief FPIOA tie setting + * + * FPIOA Object have 48 IO pin object and 256 bit input tie bits. + * All SPI arbitration signal will tie high by default. + * + * @note FPIOA function tie bits RAM Layout + * + * | Address | Name |Description | + * |-----------|------------------|----------------------------------| + * | 0x000 | TIE_EN[31:0] | Input tie enable bits [31:0] | + * | 0x004 | TIE_EN[63:32] | Input tie enable bits [63:32] | + * | 0x008 | TIE_EN[95:64] | Input tie enable bits [95:64] | + * | 0x00C | TIE_EN[127:96] | Input tie enable bits [127:96] | + * | 0x010 | TIE_EN[159:128] | Input tie enable bits [159:128] | + * | 0x014 | TIE_EN[191:160] | Input tie enable bits [191:160] | + * | 0x018 | TIE_EN[223:192] | Input tie enable bits [223:192] | + * | 0x01C | TIE_EN[255:224] | Input tie enable bits [255:224] | + * | 0x020 | TIE_VAL[31:0] | Input tie value bits [31:0] | + * | 0x024 | TIE_VAL[63:32] | Input tie value bits [63:32] | + * | 0x028 | TIE_VAL[95:64] | Input tie value bits [95:64] | + * | 0x02C | TIE_VAL[127:96] | Input tie value bits [127:96] | + * | 0x030 | TIE_VAL[159:128] | Input tie value bits [159:128] | + * | 0x034 | TIE_VAL[191:160] | Input tie value bits [191:160] | + * | 0x038 | TIE_VAL[223:192] | Input tie value bits [223:192] | + * | 0x03C | TIE_VAL[255:224] | Input tie value bits [255:224] | + * + * @note Function which input tie high by default + * + * | Name |Description | + * |---------------|---------------------------------------| + * | SPI0_ARB | Arbitration function of SPI master 0 | + * | SPI1_ARB | Arbitration function of SPI master 1 | + * + * Tie high means the SPI Arbitration input is 1 + * + */ + +typedef struct +{ + uint32_t en[FUNC_MAX/32]; + /*!< FPIOA GPIO multiplexer tie enable array */ + uint32_t val[FUNC_MAX/32]; + /*!< FPIOA GPIO multiplexer tie value array */ +} __attribute__((packed,aligned(4))) fpioa_tie_t; + +typedef struct +{ + fpioa_io_config_t io[FPIOA_NUM_IO]; + /*!< FPIOA GPIO multiplexer io array */ + fpioa_tie_t tie; + /*!< FPIOA GPIO multiplexer tie */ +} __attribute__((packed,aligned(4))) fpioa_t; + +extern volatile fpioa_t *const fpioa; + +/** + * @brief Set only IO configuration with function number + * + * @note The default IO configuration which bind to function number will + * set automatically + * + * @param[in] pin The IO number + * @param[in] function The function enum number + * + * @return result + * - 0 Success + * - Other Fail + */ +int fpioa_set_function(int pin, fpioa_function_t function); + +/** + * @brief Set IO pull function + * + * @param[in] number The IO number + * @param[in] pull The pull enum number + * + * @return result + * - 0 Success + * - Other Fail + */ +int fpioa_set_io_pull(int pin, fpioa_pull_t pull); + +/** + * @brief Get IO by function + * + * @param[in] function The function enum number + * + * @return result + * - -1 Fail + * - Other The IO number + */ +int fpioa_get_owner_pin_by_function(fpioa_function_t function); + +#endif //LAB8_FPIOA_H diff --git a/lab8/kern/driver/gpiohs.c b/lab8/kern/driver/gpiohs.c new file mode 100644 index 0000000..7d9c36e --- /dev/null +++ b/lab8/kern/driver/gpiohs.c @@ -0,0 +1,67 @@ +// +// Created by lumin on 2020/11/3. +// +#include +#include + +volatile gpiohs_t *const gpiohs = (volatile gpiohs_t *)GPIOHS_BASE_ADDR; + +void gpiohs_set_drive_mode(uint8_t gpiohs_pin, gpio_drive_mode_t mode) +{ + + assert(gpiohs_pin < GPIOHS_MAX_PINNO); + + // some control regs of gpiohs are managed by fpioa + // so we must find the owner of this function i.e. the + // bound pin, and set registers w.r.t that pin so that + // the changes does matter the owner pin + int bound_pin = fpioa_get_owner_pin_by_function(FUNC_GPIOHS0 + gpiohs_pin); + assert(bound_pin >= 0); + + fpioa_pull_t pull; + uint32_t gpiohs_dir; + switch (mode) + { + case GPIO_DM_INPUT: + pull = FPIOA_PULL_NONE; + gpiohs_dir = 0; + break; + case GPIO_DM_INPUT_PULL_DOWN: + pull = FPIOA_PULL_DOWN; + gpiohs_dir = 0; + break; + case GPIO_DM_INPUT_PULL_UP: + pull = FPIOA_PULL_UP; + gpiohs_dir = 0; + break; + case GPIO_DM_OUTPUT: + pull = FPIOA_PULL_DOWN; + gpiohs_dir = 1; + break; + default: + assert(!"GPIO drive mode is not supported."); + } + + // configure pin register on FPIOA + fpioa_set_io_pull(bound_pin, pull); + + // configure gpiohs register(as input or output) + set_bit(gpiohs_dir ? gpiohs->output_en.u32 : gpiohs->input_en.u32, bound_pin); + clear_bit((!gpiohs_dir) ? gpiohs->output_en.u32 : gpiohs->input_en.u32, bound_pin); +} + +void gpiohs_set_pin_output_value(uint8_t gpiohs_pin, gpio_pin_value_t value) +{ + assert(gpiohs_pin < GPIOHS_MAX_PINNO); + switch (value) + { + case GPIO_PV_LOW: + clear_bit(gpiohs->output_val.u32, gpiohs_pin); + break; + case GPIO_PV_HIGH: + set_bit(gpiohs->output_val.u32, gpiohs_pin); + break; + default: + break; + } +} \ No newline at end of file diff --git a/lab8/kern/driver/gpiohs.h b/lab8/kern/driver/gpiohs.h new file mode 100644 index 0000000..afd32b1 --- /dev/null +++ b/lab8/kern/driver/gpiohs.h @@ -0,0 +1,157 @@ +// +// Created by lumin on 2020/11/3. +// + +#ifndef LAB8_GPIOHS_H +#define LAB8_GPIOHS_H + +#include +#include +#include + +#define GPIOHS_MAX_PINNO 32 + +typedef enum +{ + GPIO_PV_LOW, + GPIO_PV_HIGH +} gpio_pin_value_t; + +/** + * drive mode(work mode) of specific gpio pin + */ +typedef enum +{ + GPIO_DM_INPUT, // as input + GPIO_DM_INPUT_PULL_UP, // as input with pull up resistance + GPIO_DM_INPUT_PULL_DOWN, // as input with pull down resistance + GPIO_DM_OUTPUT, // as output +} gpio_drive_mode_t; + +/** + * @brief GPIO bits object + */ +typedef struct +{ + uint32_t b0 : 1; + uint32_t b1 : 1; + uint32_t b2 : 1; + uint32_t b3 : 1; + uint32_t b4 : 1; + uint32_t b5 : 1; + uint32_t b6 : 1; + uint32_t b7 : 1; + uint32_t b8 : 1; + uint32_t b9 : 1; + uint32_t b10 : 1; + uint32_t b11 : 1; + uint32_t b12 : 1; + uint32_t b13 : 1; + uint32_t b14 : 1; + uint32_t b15 : 1; + uint32_t b16 : 1; + uint32_t b17 : 1; + uint32_t b18 : 1; + uint32_t b19 : 1; + uint32_t b20 : 1; + uint32_t b21 : 1; + uint32_t b22 : 1; + uint32_t b23 : 1; + uint32_t b24 : 1; + uint32_t b25 : 1; + uint32_t b26 : 1; + uint32_t b27 : 1; + uint32_t b28 : 1; + uint32_t b29 : 1; + uint32_t b30 : 1; + uint32_t b31 : 1; +} __attribute__((packed, aligned(4))) gpiohs_bits_t; + +/** + * @brief GPIO bits multi access union + */ +typedef union +{ + /* 32x1 bit mode */ + uint32_t u32[1]; + /* 16x2 bit mode */ + uint16_t u16[2]; + /* 8x4 bit mode */ + uint8_t u8[4]; + /* 1 bit mode */ + gpiohs_bits_t bits; +} __attribute__((packed, aligned(4))) gpiohs_u32_t; + +/** + * @brief GPIO object + * + * The GPIO controller is a peripheral device mapped in the + * internal memory map, discoverable in the Configuration String. + * It is responsible for low-level configuration of the actual + * GPIO pads on the device (direction, pull up-enable, and drive + * value), as well as selecting between various sources of the + * controls for these signals. The GPIO controller allows seperate + * configuration of each of N GPIO bits. + * + * Once the interrupt is pending, it will remain set until a 1 is + * written to the *_ip register at that bit. + */ +typedef struct +{ + /* Address offset 0x00, Input Values */ + gpiohs_u32_t input_val; + /* Address offset 0x04, Input enable */ + gpiohs_u32_t input_en; + /* Address offset 0x08, Output enable */ + gpiohs_u32_t output_en; + /* Address offset 0x0c, Onput Values */ + gpiohs_u32_t output_val; + /* Address offset 0x10, Internal Pull-Ups enable */ + gpiohs_u32_t pullup_en; + /* Address offset 0x14, Drive Strength */ + gpiohs_u32_t drive; + /* Address offset 0x18, Rise interrupt enable */ + gpiohs_u32_t rise_ie; + /* Address offset 0x1c, Rise interrupt pending */ + gpiohs_u32_t rise_ip; + /* Address offset 0x20, Fall interrupt enable */ + gpiohs_u32_t fall_ie; + /* Address offset 0x24, Fall interrupt pending */ + gpiohs_u32_t fall_ip; + /* Address offset 0x28, High interrupt enable */ + gpiohs_u32_t high_ie; + /* Address offset 0x2c, High interrupt pending */ + gpiohs_u32_t high_ip; + /* Address offset 0x30, Low interrupt enable */ + gpiohs_u32_t low_ie; + /* Address offset 0x34, Low interrupt pending */ + gpiohs_u32_t low_ip; + /* Address offset 0x38, HW I/O Function enable */ + gpiohs_u32_t iof_en; + /* Address offset 0x3c, HW I/O Function select */ + gpiohs_u32_t iof_sel; + /* Address offset 0x40, Output XOR (invert) */ + gpiohs_u32_t output_xor; +} __attribute__((packed, aligned(4))) gpiohs_t; + +/** + * @brief GPIO High-speed object instanse + */ +extern volatile gpiohs_t *const gpiohs; + +/** + * @brief Set Gpiohs drive mode + * + * @param[in] gpiohs_pin Gpiohs pin + * @param[in] mode Gpiohs pin drive mode + */ +void gpiohs_set_drive_mode(uint8_t gpiohs_pin, gpio_drive_mode_t mode); + +/** + * @brief Set Gpiohs pin value + * + * @param[in] gpiohs_pin Gpiohs pin + * @param[in] value Gpiohs pin value + */ +void gpiohs_set_pin_output_value(uint8_t gpiohs_pin, gpio_pin_value_t value); +#endif //LAB8_GPIOHS_H diff --git a/lab8/kern/driver/ide.c b/lab8/kern/driver/ide.c new file mode 100644 index 0000000..71a7656 --- /dev/null +++ b/lab8/kern/driver/ide.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +static int ide_wait_ready(unsigned short iobase, bool check_error) { return 0; } + +void ide_init(void) { + static_assert((SECTSIZE % 4) == 0); + for (int ideno = 0; ideno < MAX_IDE; ++ideno) { + ide_devices[ideno].valid = 0; + } + + ramdisk_init(SWAP_DEV_NO, &ide_devices[SWAP_DEV_NO]); + assert(VALID_IDE(SWAP_DEV_NO)); + + ramdisk_init(DISK0_DEV_NO, &ide_devices[DISK0_DEV_NO]); + assert(VALID_IDE(DISK0_DEV_NO)); +} + +bool ide_device_valid(unsigned short ideno) { return VALID_IDE(ideno); } + +size_t ide_device_size(unsigned short ideno) { + if (ide_device_valid(ideno)) { + return ide_devices[ideno].size; + } + return 0; +} + +int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, + size_t nsecs) { + assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); + assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); + return ide_devices[ideno].read_secs(&ide_devices[ideno], secno, dst, nsecs); +} + +int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, + size_t nsecs) { + assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); + assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); + return ide_devices[ideno].write_secs(&ide_devices[ideno], secno, src, + nsecs); +} diff --git a/lab8/kern/driver/ide.h b/lab8/kern/driver/ide.h new file mode 100644 index 0000000..1c21394 --- /dev/null +++ b/lab8/kern/driver/ide.h @@ -0,0 +1,36 @@ +#ifndef __KERN_DRIVER_IDE_H__ +#define __KERN_DRIVER_IDE_H__ + +#include + +#define MAX_IDE 4 +#define MAX_NSECS 128 +#define MAX_DISK_NSECS 0x10000000U +#define VALID_IDE(ideno) \ + (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid)) + +static struct ide_device { + unsigned int valid; // 0 or 1 (If Device Really Exists) + unsigned int sets; // Commend Sets Supported + unsigned int size; // Size in Sectors + uintptr_t iobase; + void *dev_data; + char model[32]; // Model in String + + /* return 0 if succeed */ + int (*read_secs)(struct ide_device *dev, size_t secno, void *dst, + size_t nsecs); + int (*write_secs)(struct ide_device *dev, size_t secno, const void *src, + size_t nsecs); +} ide_devices[MAX_IDE]; + +void ide_init(void); +bool ide_device_valid(unsigned short ideno); +size_t ide_device_size(unsigned short ideno); + +int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, + size_t nsecs); +int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, + size_t nsecs); + +#endif /* !__KERN_DRIVER_IDE_H__ */ diff --git a/lab8/kern/driver/intr.c b/lab8/kern/driver/intr.c new file mode 100644 index 0000000..2201574 --- /dev/null +++ b/lab8/kern/driver/intr.c @@ -0,0 +1,11 @@ +#include +#include + +/* intr_enable - enable irq interrupt */ +void intr_enable(void) { + set_csr(sie, MIP_SEIP); + set_csr(sstatus, SSTATUS_SIE); +} + +/* intr_disable - disable irq interrupt */ +void intr_disable(void) { clear_csr(sstatus, SSTATUS_SIE); } diff --git a/lab8/kern/driver/intr.h b/lab8/kern/driver/intr.h new file mode 100644 index 0000000..5fdf7a5 --- /dev/null +++ b/lab8/kern/driver/intr.h @@ -0,0 +1,8 @@ +#ifndef __KERN_DRIVER_INTR_H__ +#define __KERN_DRIVER_INTR_H__ + +void intr_enable(void); +void intr_disable(void); + +#endif /* !__KERN_DRIVER_INTR_H__ */ + diff --git a/lab8/kern/driver/io.h b/lab8/kern/driver/io.h new file mode 100644 index 0000000..141209f --- /dev/null +++ b/lab8/kern/driver/io.h @@ -0,0 +1,39 @@ +// +// Created by lumin on 2020/11/4. +// + +#ifndef LAB8_IO_H +#define LAB8_IO_H + +#include + +#define UARTHS_IRQ (0x0C200004U) + +#define UARTHS_DATA_REG (0x38000004U) +#define GPIOHS_BASE_ADDR (0x38001000U) + +#define SPI_SLAVE_BASE_ADDR (0x50240000U) +#define FPIOA_BASE_ADDR (0x502B0000U) +#define SYSCTL_BASE_ADDR (0x50440000U) +#define SPI0_BASE_ADDR (0x52000000U) +#define SPI1_BASE_ADDR (0x53000000U) +#define SPI3_BASE_ADDR (0x54000000U) + +#define IO_REGION_NUM (4) +#define IO_REGION_START0 (0x0C200000U) +#define IO_REGION_END0 (0x0C201000U) +#define IO_REGION_START1 (0x38000000U) +#define IO_REGION_END1 (0x38002000U) +#define IO_REGION_START2 (0x50240000U) +#define IO_REGION_END2 (0x55000000U) +#define IO_REGION_START3 (0x80000000U) +#define IO_REGION_END3 (0x80000000U + PTSIZE) + +typedef struct +{ + uintptr_t io_start; + uintptr_t io_end; + uintptr_t io_size; +} io_region_t; + +#endif //LAB8_IO_H diff --git a/lab8/kern/driver/kbdreg.h b/lab8/kern/driver/kbdreg.h new file mode 100644 index 0000000..00dc49a --- /dev/null +++ b/lab8/kern/driver/kbdreg.h @@ -0,0 +1,84 @@ +#ifndef __KERN_DRIVER_KBDREG_H__ +#define __KERN_DRIVER_KBDREG_H__ + +// Special keycodes +#define KEY_HOME 0xE0 +#define KEY_END 0xE1 +#define KEY_UP 0xE2 +#define KEY_DN 0xE3 +#define KEY_LF 0xE4 +#define KEY_RT 0xE5 +#define KEY_PGUP 0xE6 +#define KEY_PGDN 0xE7 +#define KEY_INS 0xE8 +#define KEY_DEL 0xE9 + + +/* This is i8042reg.h + kbdreg.h from NetBSD. */ + +#define KBSTATP 0x64 // kbd controller status port(I) +#define KBS_DIB 0x01 // kbd data in buffer +#define KBS_IBF 0x02 // kbd input buffer low +#define KBS_WARM 0x04 // kbd input buffer low +#define BS_OCMD 0x08 // kbd output buffer has command +#define KBS_NOSEC 0x10 // kbd security lock not engaged +#define KBS_TERR 0x20 // kbd transmission error +#define KBS_RERR 0x40 // kbd receive error +#define KBS_PERR 0x80 // kbd parity error + +#define KBCMDP 0x64 // kbd controller port(O) +#define KBC_RAMREAD 0x20 // read from RAM +#define KBC_RAMWRITE 0x60 // write to RAM +#define KBC_AUXDISABLE 0xa7 // disable auxiliary port +#define KBC_AUXENABLE 0xa8 // enable auxiliary port +#define KBC_AUXTEST 0xa9 // test auxiliary port +#define KBC_KBDECHO 0xd2 // echo to keyboard port +#define KBC_AUXECHO 0xd3 // echo to auxiliary port +#define KBC_AUXWRITE 0xd4 // write to auxiliary port +#define KBC_SELFTEST 0xaa // start self-test +#define KBC_KBDTEST 0xab // test keyboard port +#define KBC_KBDDISABLE 0xad // disable keyboard port +#define KBC_KBDENABLE 0xae // enable keyboard port +#define KBC_PULSE0 0xfe // pulse output bit 0 +#define KBC_PULSE1 0xfd // pulse output bit 1 +#define KBC_PULSE2 0xfb // pulse output bit 2 +#define KBC_PULSE3 0xf7 // pulse output bit 3 + +#define KBDATAP 0x60 // kbd data port(I) +#define KBOUTP 0x60 // kbd data port(O) + +#define K_RDCMDBYTE 0x20 +#define K_LDCMDBYTE 0x60 + +#define KC8_TRANS 0x40 // convert to old scan codes +#define KC8_MDISABLE 0x20 // disable mouse +#define KC8_KDISABLE 0x10 // disable keyboard +#define KC8_IGNSEC 0x08 // ignore security lock +#define KC8_CPU 0x04 // exit from protected mode reset +#define KC8_MENABLE 0x02 // enable mouse interrupt +#define KC8_KENABLE 0x01 // enable keyboard interrupt +#define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE) + +/* keyboard commands */ +#define KBC_RESET 0xFF // reset the keyboard +#define KBC_RESEND 0xFE // request the keyboard resend the last byte +#define KBC_SETDEFAULT 0xF6 // resets keyboard to its power-on defaults +#define KBC_DISABLE 0xF5 // as per KBC_SETDEFAULT, but also disable key scanning +#define KBC_ENABLE 0xF4 // enable key scanning +#define KBC_TYPEMATIC 0xF3 // set typematic rate and delay +#define KBC_SETTABLE 0xF0 // set scancode translation table +#define KBC_MODEIND 0xED // set mode indicators(i.e. LEDs) +#define KBC_ECHO 0xEE // request an echo from the keyboard + +/* keyboard responses */ +#define KBR_EXTENDED 0xE0 // extended key sequence +#define KBR_RESEND 0xFE // needs resend of command +#define KBR_ACK 0xFA // received a valid command +#define KBR_OVERRUN 0x00 // flooded +#define KBR_FAILURE 0xFD // diagnosic failure +#define KBR_BREAK 0xF0 // break code prefix - sent on key release +#define KBR_RSTDONE 0xAA // reset complete +#define KBR_ECHO 0xEE // echo response + +#endif /* !__KERN_DRIVER_KBDREG_H__ */ + diff --git a/lab8/kern/driver/picirq.c b/lab8/kern/driver/picirq.c new file mode 100644 index 0000000..d05cbef --- /dev/null +++ b/lab8/kern/driver/picirq.c @@ -0,0 +1,6 @@ +#include + +void pic_enable(unsigned int irq) {} + +/* pic_init - initialize the 8259A interrupt controllers */ +void pic_init(void) {} diff --git a/lab8/kern/driver/picirq.h b/lab8/kern/driver/picirq.h new file mode 100644 index 0000000..41e965d --- /dev/null +++ b/lab8/kern/driver/picirq.h @@ -0,0 +1,8 @@ +#ifndef __KERN_DRIVER_PICIRQ_H__ +#define __KERN_DRIVER_PICIRQ_H__ + +void pic_init(void); +void pic_enable(unsigned int irq); + +#endif /* !__KERN_DRIVER_PICIRQ_H__ */ + diff --git a/lab8/kern/driver/ramdisk.c b/lab8/kern/driver/ramdisk.c new file mode 100644 index 0000000..e68ba0b --- /dev/null +++ b/lab8/kern/driver/ramdisk.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +extern char _binary_bin_swap_img_start[], _binary_bin_swap_img_end[]; +extern char _binary_bin_sfs_img_start[], _binary_bin_sfs_img_end[]; + +bool check_initrd(const char _initrd_begin[], const char _initrd_end[]) { + if (_initrd_begin == _initrd_end) { + cprintf("Warning: No Initrd!\n"); + return 0; + } + cprintf("Initrd: 0x%08x - 0x%08x, size: 0x%08x\n", _initrd_begin, + _initrd_end - 1, _initrd_end - _initrd_begin); + return 1; +} + +static int ramdisk_read(struct ide_device *dev, size_t secno, void *dst, + size_t nsecs) { + nsecs = MIN(nsecs, dev->size - secno); + if (nsecs < 0) return -1; + memcpy(dst, (void *)(dev->iobase + secno * SECTSIZE), nsecs * SECTSIZE); + return 0; +} + +static int ramdisk_write(struct ide_device *dev, size_t secno, const void *src, + size_t nsecs) { + nsecs = MIN(nsecs, dev->size - secno); + if (nsecs < 0) return -1; + memcpy((void *)(dev->iobase + secno * SECTSIZE), src, nsecs * SECTSIZE); + return 0; +} + +void ramdisk_init(int devno, struct ide_device *dev) { + memset(dev, 0, sizeof(struct ide_device)); + char *_initrd_begin; + char *_initrd_end; + if (devno == SWAP_DEV_NO) { + _initrd_begin = _binary_bin_swap_img_start; + _initrd_end = _binary_bin_swap_img_end; + } else if (devno == DISK0_DEV_NO) { + _initrd_begin = _binary_bin_sfs_img_start; + _initrd_end = _binary_bin_sfs_img_end; + } else { + panic("Device Not Found"); + } + + if (check_initrd(_initrd_begin, _initrd_end)) { + dev->valid = 1; + dev->sets = ~0; + dev->size = (unsigned int)(_initrd_end - _initrd_begin) / SECTSIZE; + dev->iobase = (uintptr_t)_initrd_begin; + strcpy(dev->model, "KERN_INITRD"); + dev->read_secs = ramdisk_read; + dev->write_secs = ramdisk_write; + } +} diff --git a/lab8/kern/driver/ramdisk.h b/lab8/kern/driver/ramdisk.h new file mode 100644 index 0000000..87c0907 --- /dev/null +++ b/lab8/kern/driver/ramdisk.h @@ -0,0 +1,3 @@ +#include + +void ramdisk_init(int devno, struct ide_device *dev); diff --git a/lab8/kern/driver/sdcard.c b/lab8/kern/driver/sdcard.c new file mode 100644 index 0000000..11beca1 --- /dev/null +++ b/lab8/kern/driver/sdcard.c @@ -0,0 +1,582 @@ +// +// Created by lumin on 2020/11/2. +// +#include +#include +#include +#include +#include +#include + +#define SECTOR_SIZE (512) + +#define SD_SPI_CLK (200000UL) +#define SD_SPI_HIGH_SPEED_CLK (25000000UL) +#define SD_SPI_DEVICE SPI_DEVICE_1 +#define SD_SPI_WORK_MODE SPI_WORK_MODE_0 +#define SD_SPI_FRAME_FORMAT SPI_FF_STANDARD +#define SD_SPI_DATA_BIT_LENGTH 8 +#define SD_SPI_ENDIAN 0 +#define SD_SPI_CHIP_SELECT SPI_CHIP_SELECT_1 + +#define SD_CMD0_ARGS (0) +#define SD_CMD0_CRC (0x95) +#define SD_CMD55_ARGS (0) +#define SD_CMD55_CRC (0) +#define SD_ACMD41_CRC (0) +#define SD_CMD58_ARGS (0) +#define SD_CMD58_CRC (1) +#define SD_CMD10_ARGS (0) +#define SD_CMD10_CRC (0) +#define SD_VOLTAGE_SELECT (0x0100) +#define SD_CHECK_PATTERN (0xAA) +#define SD_CMD8_CRC (0x87) +#define SD_HIGH_CAPACITY (0x40000000) +#define SD_ERROR_RES (0xFF) +#define SD_TIMEMOUT_COUNT (0xFF) + +static void sdcard_pin_mapping_init(); +static void sdcard_gpiohs_init(); +static void sdcard_spi_init(); +static void sd_SPI_mode_prepare(); +static void SD_CS_HIGH(); +static void SD_CS_LOW(); +static void SD_HIGH_SPEED_ENABLE(); +static void sd_write_data(uint8_t *frame, uint32_t length); +static void sd_read_data(uint8_t *frame, uint32_t length); +static void sd_send_cmd(uint8_t cmd, uint32_t args, uint8_t crc); +static void sd_end_cmd(); +static uint8_t sd_get_R1_response(); +static void sd_get_R3_rest_response(uint8_t *frame); +static void sd_get_R7_rest_response(uint8_t *frame); +static uint8_t sd_get_data_write_response(); +static int sd_switch_to_SPI_mode(); +static int sd_negotiate_voltage(); +static int sd_set_SDXC_capacity(); +static int sd_query_capacity_status(); +static int sd_get_cardinfo(SD_CardInfo *cardinfo); +static int sd_get_csdregister(SD_CSD *SD_csd); +static int sd_get_cidregister(SD_CID *SD_cid); +static void sd_test(); + +sdcard_hardware_pin_config_t config = { + 28, 26, 27, 29, 29 +}; + +SD_CardInfo cardinfo; + +void sd_init() +{ + cardinfo.active = 0; + + // Part 1: Hardware infrastructure configuration + sdcard_pin_mapping_init(); + sdcard_gpiohs_init(); + sdcard_spi_init(); + + // Part 2: SD protocol(SPI Mode) + sd_SPI_mode_prepare(); + + if(sd_switch_to_SPI_mode() != 0) + return; + if(sd_negotiate_voltage() != 0) + return; + if(sd_set_SDXC_capacity() != 0) + return; + if(sd_query_capacity_status() != 0) + return; + + SD_HIGH_SPEED_ENABLE(); + + if(sd_get_cardinfo(&cardinfo) != 0) + return; + + // Part 3: Read/Write test +// sd_test(); + cprintf("Successfully initialize SD card!\n"); +} + +uint8_t sd_read_sector(uint8_t *data_buff, uint32_t sector, uint32_t count) +{ + uint8_t dummy_crc[2], multiple; + if (count == 1){ + multiple = 0; + sd_send_cmd(SD_CMD17, sector, 0); + } else{ + multiple = 1; + sd_send_cmd(SD_CMD18, sector, 0); + } + + uint32_t result = sd_get_R1_response(); + if (result != SD_TRANS_MODE_RESULT_OK) { + cprintf("Read sector(s) CMD error! result = %d\n", result); + return SD_ERROR_RES; + } + + while (count) + { + result = sd_get_R1_response(); + if (result != SD_START_DATA_READ_RESPONSE) { + cprintf("Data read response error! result = %d\n", result); + return SD_ERROR_RES; + } + + sd_read_data(data_buff, SECTOR_SIZE); + sd_read_data(dummy_crc, 2); + + data_buff += SECTOR_SIZE; + count--; + } + + sd_end_cmd(); + if (multiple){ + sd_send_cmd(SD_CMD12, 0, 0); + sd_get_R1_response(); + sd_end_cmd(); + sd_end_cmd(); + } + + if (count > 0) { + cprintf("Not all sectors are read!\n"); + return SD_ERROR_RES; + } + + return 0; +} + +uint8_t sd_write_sector(uint8_t *data_buff, uint32_t sector, uint32_t count) +{ + assert(((uint32_t)data_buff) % 4 == 0); + uint8_t token[2] = {0xFF, 0x00}, dummpy_crc[2] = {0xFF, 0xFF}; + + if (count == 1){ + token[1] = SD_START_DATA_SINGLE_BLOCK_WRITE_TOKEN; + sd_send_cmd(SD_CMD24, sector, 0); + } else{ + token[1] = SD_START_DATA_MULTIPLE_BLOCK_WRITE_TOKEN; + sd_send_cmd(SD_ACMD23, count, 0); + sd_get_R1_response(); + sd_end_cmd(); + sd_send_cmd(SD_CMD25, sector, 0); + } + + if (sd_get_R1_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Write sector(s) CMD error!\n"); + return SD_ERROR_RES; + } + + while (count) + { + sd_write_data(token, 2); + sd_write_data(data_buff, SECTOR_SIZE); + sd_write_data(dummpy_crc, 2); + + if (sd_get_data_write_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Data write response error!\n"); + return SD_ERROR_RES; + } + + data_buff += SECTOR_SIZE; + count--; + } + + sd_end_cmd(); + sd_end_cmd(); + + if (count > 0) { + cprintf("Not all sectors are written!\n"); + return SD_ERROR_RES; + } + + return 0; +} + +static void sdcard_pin_mapping_init() +{ + fpioa_set_function(config.sclk_pin, FUNC_SPI1_SCLK); + fpioa_set_function(config.mosi_pin, FUNC_SPI1_D0); + fpioa_set_function(config.miso_pin, FUNC_SPI1_D1); + fpioa_set_function(config.cs_pin, FUNC_GPIOHS0 + config.cs_gpio_num); +} + +static void sdcard_gpiohs_init() +{ + gpiohs_set_drive_mode(config.cs_gpio_num, GPIO_DM_OUTPUT); +} + +static void sdcard_spi_init() +{ + spi_set_clk_rate(SD_SPI_DEVICE, SD_SPI_CLK); +} + +static void sd_SPI_mode_prepare() +{ + /*!< Send dummy byte 0xFF, 10 times with CS high */ + /*!< Rise CS and MOSI for 80 clocks cycles */ + /*!< Send dummy byte 0xFF */ + uint8_t frame[10]; + for (int i = 0; i < 10; i++) + frame[i] = SD_EMPTY_FILL; + /*!< SD chip select high */ + SD_CS_HIGH(); + sd_write_data(frame, 10); +} + +static void SD_CS_HIGH() +{ + gpiohs_set_pin_output_value(config.cs_gpio_num, GPIO_PV_HIGH); +} + +static void SD_CS_LOW() +{ + gpiohs_set_pin_output_value(config.cs_gpio_num, GPIO_PV_LOW); +} + +static void SD_HIGH_SPEED_ENABLE() +{ + spi_set_clk_rate(SD_SPI_DEVICE, SD_SPI_HIGH_SPEED_CLK); +} + +static void sd_write_data(uint8_t *frame, uint32_t length) +{ + spi_init(SD_SPI_DEVICE, SD_SPI_WORK_MODE, + SD_SPI_FRAME_FORMAT, SD_SPI_DATA_BIT_LENGTH, SD_SPI_ENDIAN); + spi_send_data_standard(SD_SPI_DEVICE, SD_SPI_CHIP_SELECT, frame, length); +} + +static void sd_read_data(uint8_t *frame, uint32_t length) +{ + spi_init(SD_SPI_DEVICE, SD_SPI_WORK_MODE, + SD_SPI_FRAME_FORMAT, SD_SPI_DATA_BIT_LENGTH, SD_SPI_ENDIAN); + spi_receive_data_standard(SD_SPI_DEVICE, SD_SPI_CHIP_SELECT, frame, length); +} + +static void sd_send_cmd(uint8_t cmd, uint32_t args, uint8_t crc) +{ + // prepare cmd frame + uint8_t frame[SD_CMD_FRAME_SIZE]; + frame[SD_CMD_CMD_BIT] = (SD_CMD_SIGN | cmd); + frame[SD_CMD_ARG_MSB0] = (uint8_t)(args >> 24); + frame[SD_CMD_ARG_MSB1] = (uint8_t)(args >> 16); + frame[SD_CMD_ARG_MSB2] = (uint8_t)(args >> 8); + frame[SD_CMD_ARG_MSB3] = (uint8_t)args; + frame[SD_CMD_CRC_BIT] = crc; + + // real send operation + SD_CS_LOW(); + sd_write_data(frame,SD_CMD_FRAME_SIZE); +} + +static void sd_end_cmd() +{ + uint8_t fill = SD_EMPTY_FILL; + SD_CS_HIGH(); + sd_write_data(&fill, 1); +} + +static uint8_t sd_get_R1_response() +{ + uint8_t result; + + uint16_t timeout = SD_TIMEMOUT_COUNT; + while (timeout--) + { + sd_read_data(&result, 1); + if (result != SD_EMPTY_FILL) + return result; + } + return SD_ERROR_RES; +} + +static void sd_get_R3_rest_response(uint8_t *frame) +{ + sd_read_data(frame, SD_R3_RESPONSE_REST_LENGTH); +} + +static void sd_get_R7_rest_response(uint8_t *frame) +{ + sd_read_data(frame, SD_R7_RESPONSE_REST_LENGTH); +} + +static uint8_t sd_get_data_write_response() +{ + uint8_t result; + sd_read_data(&result, 1); + + // protocol defined correct response + if ((result & 0x1F) != 0x05) + return SD_ERROR_RES; + + do { + sd_read_data(&result, 1); + } while (result == SD_TRANS_MODE_RESULT_OK); + + return SD_TRANS_MODE_RESULT_OK; +} + +static int sd_switch_to_SPI_mode() +{ + uint8_t result; + sd_send_cmd(SD_CMD0, SD_CMD0_ARGS, SD_CMD0_CRC); + result = sd_get_R1_response(); + sd_end_cmd(); + + if (result != SD_INIT_MODE_RESULT_OK){ + cprintf("Fail to connect to SD card!\n"); + return SD_ERROR_RES; + } + return 0; +} + +static int sd_negotiate_voltage() +{ + uint8_t frame[SD_R7_RESPONSE_REST_LENGTH],result; + sd_send_cmd(SD_CMD8, SD_VOLTAGE_SELECT | SD_CHECK_PATTERN, SD_CMD8_CRC); + result = sd_get_R1_response(); + sd_get_R7_rest_response(frame); + sd_end_cmd(); + + if (result != SD_INIT_MODE_RESULT_OK){ + cprintf("Fail to negotiate voltage with SD card!\n"); + return SD_ERROR_RES; + } + return 0; +} + +static int sd_set_SDXC_capacity() +{ + uint8_t result; + + uint16_t timeout = SD_TIMEMOUT_COUNT; + while (timeout--) + { + sd_send_cmd(SD_CMD55, SD_CMD55_ARGS, SD_CMD55_CRC); + result = sd_get_R1_response(); + sd_end_cmd(); + if (result != SD_INIT_MODE_RESULT_OK){ + cprintf("Fail to prepare application command when set SDXC capacity!\n"); + return SD_ERROR_RES; + } + + sd_send_cmd(SD_ACMD41, SD_HIGH_CAPACITY, SD_ACMD41_CRC); + result = sd_get_R1_response(); + sd_end_cmd(); + if (result == SD_TRANS_MODE_RESULT_OK) + return 0; + } + cprintf("Timeout to set card capacity!\n"); + return SD_ERROR_RES; +} + +static int sd_query_capacity_status() +{ + uint8_t frame[SD_R3_RESPONSE_REST_LENGTH], result; + + uint16_t timeout = SD_TIMEMOUT_COUNT; + while (timeout--) + { + sd_send_cmd(SD_CMD58, SD_CMD58_ARGS, SD_CMD58_CRC); + result = sd_get_R1_response(); + sd_get_R3_rest_response(frame); + sd_end_cmd(); + if (result == SD_TRANS_MODE_RESULT_OK) + break; + } + if (timeout == 0) { + cprintf("Timeout to query card capacity status!\n"); + return SD_ERROR_RES; + } + // protocol defined correct response format + if ((frame[0] & 0x40) == 0) { + cprintf("SDXC card capacity status wrong!\n"); + return SD_ERROR_RES; + } + return 0; +} + +static int sd_get_cardinfo(SD_CardInfo *cardinfo) +{ + if(sd_get_csdregister(&cardinfo->SD_csd) != 0) + return SD_ERROR_RES; + if(sd_get_cidregister(&cardinfo->SD_cid) != 0) + return SD_ERROR_RES; + + cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 2 * SECTOR_SIZE; + cardinfo->CardBlockSize = (1 << (cardinfo->SD_csd.RdBlockLen)); + cardinfo->CardCapacity *= cardinfo->CardBlockSize; + cardinfo->active = 1; + + return 0; +} + +static int sd_get_csdregister(SD_CSD *SD_csd) +{ + uint8_t csd_frame[18]; + + // protocol + sd_send_cmd(SD_CMD9, 0, 0); + if (sd_get_R1_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Get CSD cmd error!\n"); + return SD_ERROR_RES; + } + if (sd_get_R1_response() != SD_START_DATA_READ_RESPONSE) { + cprintf("Get CSD data response error!\n"); + return SD_ERROR_RES; + } + + // retrieve data, 16 for SD_CID, 2 for CRC + sd_read_data(csd_frame, 18); + sd_end_cmd(); + + // set field + SD_csd->CSDStruct = csd_frame[0] >> 6; + SD_csd->SysSpecVersion = (csd_frame[0] & 0x3C) >> 2; + SD_csd->Reserved1 = csd_frame[0] & 0x03; + + SD_csd->TAAC = csd_frame[1]; + + SD_csd->NSAC = csd_frame[2]; + + SD_csd->MaxBusClkFrec = csd_frame[3]; + + SD_csd->CardComdClasses = (csd_frame[4] << 4); + + SD_csd->CardComdClasses |= (csd_frame[5] >> 4); + SD_csd->RdBlockLen = csd_frame[5] & 0x0F; + + SD_csd->PartBlockRead = csd_frame[6] >> 7; + SD_csd->WrBlockMisalign = (csd_frame[6] & 0x40) >> 6; + SD_csd->RdBlockMisalign = (csd_frame[6] & 0x20) >> 5; + SD_csd->DSRImpl = (csd_frame[6] & 0x10) >> 4; + SD_csd->Reserved2 = 0; + + SD_csd->DeviceSize = (csd_frame[7] & 0x3F) << 16; + SD_csd->DeviceSize |= csd_frame[8] << 8; + SD_csd->DeviceSize |= csd_frame[9]; + + SD_csd->EraseGrSize = (csd_frame[10] & 0x40) >> 6; + SD_csd->EraseGrMul = ((csd_frame[10] & 0x3F) << 1); + + SD_csd->EraseGrMul |= (csd_frame[11] >> 7); + SD_csd->WrProtectGrSize = csd_frame[11] & 0x7F; + + SD_csd->WrProtectGrEnable = csd_frame[12] >> 7; + SD_csd->ManDeflECC = (csd_frame[12] & 0x60) >> 5; + SD_csd->WrSpeedFact = (csd_frame[12] & 0x1C) >> 2; + SD_csd->MaxWrBlockLen = ((csd_frame[12] & 0x03) << 2); + + SD_csd->MaxWrBlockLen |= (csd_frame[13] >> 6); + SD_csd->WriteBlockPaPartial = (csd_frame[13] & 0x20) >> 5; + SD_csd->Reserved3 = 0; + SD_csd->ContentProtectAppli = csd_frame[13] & 0x01; + + SD_csd->FileFormatGrouop = csd_frame[14] >> 7; + SD_csd->CopyFlag = (csd_frame[14] & 0x40) >> 6; + SD_csd->PermWrProtect = (csd_frame[14] & 0x20) >> 5; + SD_csd->TempWrProtect = (csd_frame[14] & 0x10) >> 4; + SD_csd->FileFormat = (csd_frame[14] & 0x0C) >> 2; + SD_csd->ECC = csd_frame[14] & 0x03; + + SD_csd->CSD_CRC = csd_frame[15] >> 1; + SD_csd->Reserved4 = 1; + + return 0; +} + +static int sd_get_cidregister(SD_CID *SD_cid) { + uint8_t cid_frame[18]; + + // protocol + sd_send_cmd(SD_CMD10, SD_CMD10_ARGS, SD_CMD10_CRC); + if (sd_get_R1_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Get CID cmd error!\n"); + return SD_ERROR_RES; + } + if (sd_get_R1_response() != SD_START_DATA_READ_RESPONSE) { + cprintf("Get CID data response error!\n"); + return SD_ERROR_RES; + } + + // retrieve data, 16 for SD_CID, 2 for CRC + sd_read_data(cid_frame, 18); + sd_end_cmd(); + + // set field + SD_cid->ManufacturerID = cid_frame[0]; + SD_cid->OEM_AppliID = (cid_frame[1] << 8); + SD_cid->OEM_AppliID |= cid_frame[2]; + SD_cid->ProdName1 = (cid_frame[3] << 24); + SD_cid->ProdName1 |= (cid_frame[4] << 16); + SD_cid->ProdName1 |= (cid_frame[5] << 8); + SD_cid->ProdName1 |= cid_frame[6]; + SD_cid->ProdName2 = cid_frame[7]; + SD_cid->ProdRev = cid_frame[8]; + SD_cid->ProdSN = (cid_frame[9] << 24); + SD_cid->ProdSN |= (cid_frame[10] << 16); + SD_cid->ProdSN |= (cid_frame[11] << 8); + SD_cid->ProdSN |= cid_frame[12]; + SD_cid->Reserved1 = cid_frame[13] >> 4; + SD_cid->ManufactDate = ((cid_frame[13] & 0x0F) << 8); + SD_cid->ManufactDate |= cid_frame[14]; + SD_cid->CID_CRC = cid_frame[15] >> 1; + SD_cid->Reserved2 = 1; + + return 0; +} + +static void sd_test() +{ + uint64_t num_sectors = cardinfo.CardCapacity / SECTOR_SIZE; + uint32_t sector = num_sectors - 10; + + uint8_t buffer0[2 * SECTOR_SIZE], buffer1[2 * SECTOR_SIZE], buffer2[2 * SECTOR_SIZE], buffer3[2 * SECTOR_SIZE]; + const char *msg = "Well! I've often seen a cat without a grin', thought Alice, 'but a grin without a cat! It's the most curious thing I ever saw in my life!'"; + + cprintf("Start SD card read/write test\n", sector); + + memset(buffer0, 0, SECTOR_SIZE); + memset(buffer1, 0, SECTOR_SIZE); + memset(buffer2, 0, SECTOR_SIZE); + memset(buffer3, 0, SECTOR_SIZE); + cprintf("- single sector read/write test on sector 0x%x\n", sector); + assert(sd_read_sector(buffer0, sector, 1) == 0); + cprintf("\tread sector 0x%x successfully!\n", sector); + + memcpy(buffer1, msg, strlen(msg) + 1 ); + assert(memcmp(buffer1, buffer2, SECTOR_SIZE) != 0); + assert(sd_write_sector(buffer1, sector, 1) == 0); + cprintf("\twrite sector 0x%x successfully!\n", sector); + + assert(sd_read_sector(buffer2, sector, 1) == 0); + assert(memcmp(buffer1, buffer2, SECTOR_SIZE) == 0); + cprintf("\treread sector 0x%x successfully!\n", sector); + + assert(sd_write_sector(buffer0, sector, 1) == 0); + assert(sd_read_sector(buffer3, sector, 1) == 0); + assert(memcmp(buffer0, buffer3, SECTOR_SIZE) == 0); + cprintf("\trestore sector 0x%x successfully!\n", sector); + + memset(buffer0, 0, 2 * SECTOR_SIZE); + memset(buffer1, 0, 2 * SECTOR_SIZE); + memset(buffer2, 0, 2 * SECTOR_SIZE); + memset(buffer3, 0, 2 * SECTOR_SIZE); + cprintf("- multiple sector read/write test on sector 0x%x\n", sector); + assert(sd_read_sector(buffer0, sector, 2) == 0); + cprintf("\tread sector 0x%x successfully!\n", sector); + + memcpy(buffer1, msg, strlen(msg) + 1 ); + assert(memcmp(buffer1, buffer2, 2 * SECTOR_SIZE) != 0); + assert(sd_write_sector(buffer1, sector, 2) == 0); + cprintf("\twrite sector 0x%x successfully!\n", sector); + + assert(sd_read_sector(buffer2, sector, 2) == 0); + assert(memcmp(buffer1, buffer2, 2 * SECTOR_SIZE) == 0); + cprintf("\treread sector 0x%x successfully!\n", sector); + + assert(sd_write_sector(buffer0, sector, 2) == 0); + assert(sd_read_sector(buffer3, sector, 2) == 0); + assert(memcmp(buffer0, buffer3, 2 * SECTOR_SIZE) == 0); + cprintf("\trestore sector 0x%x successfully!\n", sector); + + cprintf("SD card initialized successfully!\n"); +} \ No newline at end of file diff --git a/lab8/kern/driver/sdcard.h b/lab8/kern/driver/sdcard.h new file mode 100644 index 0000000..5254065 --- /dev/null +++ b/lab8/kern/driver/sdcard.h @@ -0,0 +1,166 @@ +// +// Created by lumin on 2020/11/2. +// + +#ifndef LAB8_SDCARD_H +#define LAB8_SDCARD_H + +#include + +/* + * SD Card Commands + * @brief Commands: CMDxx = CMD-number | 0x40 + */ +#define SD_CMD_SIGN (0x40) +#define SD_CMD0 0 // chip reset +#define SD_CMD8 8 // voltage negotiation +#define SD_CMD9 9 // read CSD register +#define SD_CMD10 10 // read CID register +#define SD_CMD12 12 // end multiple continuous sector read +#define SD_CMD17 17 // start single sector read +#define SD_CMD18 18 // start multiple continuous sector read and send start sector +#define SD_ACMD23 23 // start multiple continuous sector write and send sector count +#define SD_CMD24 24 // start single sector write +#define SD_CMD25 25 // start multiple continuous sector write and send start sector +#define SD_ACMD41 41 // capacity mode set +#define SD_CMD55 55 // ACMD prefix +#define SD_CMD58 58 // read CCS(card capacity status) + +#define SD_INIT_MODE_RESULT_OK (0x01) +#define SD_TRANS_MODE_RESULT_OK (0x00) +#define SD_START_DATA_READ_RESPONSE (0xFE) +#define SD_START_DATA_SINGLE_BLOCK_WRITE_TOKEN (0xFE) +#define SD_START_DATA_MULTIPLE_BLOCK_WRITE_TOKEN (0xFC) + +/** + * CMD frame format + * | CRC | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | CMD | + * | byte 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | + */ +#define SD_CMD_CMD_BIT 0 +#define SD_CMD_ARG_MSB0 1 +#define SD_CMD_ARG_MSB1 2 +#define SD_CMD_ARG_MSB2 3 +#define SD_CMD_ARG_MSB3 4 +#define SD_CMD_CRC_BIT 5 +#define SD_CMD_FRAME_SIZE 6 + +#define SD_EMPTY_FILL (0xFF) + +#define SD_R3_RESPONSE_REST_LENGTH 4 +#define SD_R7_RESPONSE_REST_LENGTH 4 + +// SD card here uses SPI mode, pin +// names also inherit from it. +typedef struct +{ + int mosi_pin; // pin num of Master Out Slave In + int miso_pin; // pin num of Master In Slave Out + int sclk_pin; // pin num of SPI Clock + int cs_pin; // pin num of Chip Selector + int cs_gpio_num;// what is this..? +} sdcard_hardware_pin_config_t; + +typedef struct +{ + uint8_t CSDStruct; /*!< CSD structure */ + uint8_t SysSpecVersion; /*!< System specification version */ + uint8_t Reserved1; /*!< Reserved */ + uint8_t TAAC; /*!< Data read access-time 1 */ + uint8_t NSAC; /*!< Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec; /*!< Max. bus clock frequency */ + uint16_t CardComdClasses; /*!< Card command classes */ + uint8_t RdBlockLen; /*!< Max. read data block length */ + uint8_t PartBlockRead; /*!< Partial blocks for read allowed */ + uint8_t WrBlockMisalign; /*!< Write block misalignment */ + uint8_t RdBlockMisalign; /*!< Read block misalignment */ + uint8_t DSRImpl; /*!< DSR implemented */ + uint8_t Reserved2; /*!< Reserved */ + uint32_t DeviceSize; /*!< Device Size */ + uint8_t MaxRdCurrentVDDMin; /*!< Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax; /*!< Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin; /*!< Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax; /*!< Max. write current @ VDD max */ + uint8_t DeviceSizeMul; /*!< Device size multiplier */ + uint8_t EraseGrSize; /*!< Erase group size */ + uint8_t EraseGrMul; /*!< Erase group size multiplier */ + uint8_t WrProtectGrSize; /*!< Write protect group size */ + uint8_t WrProtectGrEnable; /*!< Write protect group enable */ + uint8_t ManDeflECC; /*!< Manufacturer default ECC */ + uint8_t WrSpeedFact; /*!< Write speed factor */ + uint8_t MaxWrBlockLen; /*!< Max. write data block length */ + uint8_t WriteBlockPaPartial; /*!< Partial blocks for write allowed */ + uint8_t Reserved3; /*!< Reserded */ + uint8_t ContentProtectAppli; /*!< Content protection application */ + uint8_t FileFormatGrouop; /*!< File format group */ + uint8_t CopyFlag; /*!< Copy flag (OTP) */ + uint8_t PermWrProtect; /*!< Permanent write protection */ + uint8_t TempWrProtect; /*!< Temporary write protection */ + uint8_t FileFormat; /*!< File Format */ + uint8_t ECC; /*!< ECC code */ + uint8_t CSD_CRC; /*!< CSD CRC */ + uint8_t Reserved4; /*!< always 1*/ + uint8_t CSizeMlut; /*!< */ +} SD_CSD; + +/** +* @brief Card Identification Data: CID Register +*/ +typedef struct +{ + uint8_t ManufacturerID; /*!< ManufacturerID */ + uint16_t OEM_AppliID; /*!< OEM/Application ID */ + uint32_t ProdName1; /*!< Product Name part1 */ + uint8_t ProdName2; /*!< Product Name part2*/ + uint8_t ProdRev; /*!< Product Revision */ + uint32_t ProdSN; /*!< Product Serial Number */ + uint8_t Reserved1; /*!< Reserved1 */ + uint16_t ManufactDate; /*!< Manufacturing Date */ + uint8_t CID_CRC; /*!< CID CRC */ + uint8_t Reserved2; /*!< always 1 */ +} SD_CID; + +/** +* @brief SD Card information +*/ +typedef struct +{ + SD_CSD SD_csd; + SD_CID SD_cid; + uint64_t CardCapacity; /*!< Card Capacity */ + uint32_t CardBlockSize; /*!< Card Block Size */ + uint8_t active; +} SD_CardInfo; + +extern SD_CardInfo cardinfo; + +/** + * Initialize the SD Card + */ +void sd_init(); + +/** + * Synchronously read single/multiple sector(s) from SD card + * + * @param data_buff pointer to the buffer that receives the data read from the SD. + * @param sector SD's internal address to read from. + * @param count count of sectors to be read + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8_t sd_read_sector(uint8_t *data_buff, uint32_t sector, uint32_t count); + +/** + * Synchronously write single/multiple sector(s) from SD card + * + * @param data_buff pointer to the buffer to be written to the SD. + * @param sector SD's internal address to write to. + * @param count count of sectors to be read + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8_t sd_write_sector(uint8_t *data_buff, uint32_t sector, uint32_t count); + +#endif //LAB8_SDCARD_H diff --git a/lab8/kern/driver/spi.c b/lab8/kern/driver/spi.c new file mode 100644 index 0000000..6dcb4cd --- /dev/null +++ b/lab8/kern/driver/spi.c @@ -0,0 +1,195 @@ +// +// Created by lumin on 2020/11/2. +// +#include +#include +#include +#include + +volatile spi_t *const spi[4] = + { + (volatile spi_t *)SPI0_BASE_ADDR, + (volatile spi_t *)SPI1_BASE_ADDR, + (volatile spi_t *)SPI_SLAVE_BASE_ADDR, + (volatile spi_t *)SPI3_BASE_ADDR + }; + +static void spi_clk_init(uint8_t spi_num); +static uint32_t spi_get_ctrlr0(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, size_t data_bit_length); +static void spi_set_transfer_mode(uint8_t spi_num, spi_transfer_mode_t tmode); + +void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, + size_t data_bit_length, uint32_t endian) +{ + assert(data_bit_length >= SPI_MIN_DATA_BIT_LENGTH && data_bit_length <= SPI_MAX_DATA_BIT_LENGTH); + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + + spi_clk_init(spi_num); + + // init spi controller + volatile spi_t *spi_controller = spi[spi_num]; + if (spi_controller->baudr == 0) + spi_controller->baudr = SPI_BAUDRATE_DEFAULT_VAL; + spi_controller->imr = SPI_INTERRUPT_DISABLE; // default disable interrupt mode + spi_controller->dmacr = SPI_DMACR_DEFAULT_VAL; + spi_controller->dmatdlr = SPI_DMATDLR_DEFAULT_VAL; + spi_controller->dmardlr = SPI_DMARDLR_DEFAULT_VAL; + spi_controller->ser = SPI_SLAVE_DISABLE; // default disable slave + spi_controller->ssienr = SPI_MASTER_DISABLE; // default disable master + spi_controller->ctrlr0 = spi_get_ctrlr0(spi_num, work_mode, frame_format, data_bit_length); + spi_controller->spi_ctrlr0 = SPI_TMOD_DEFAULT_VAL; // default transmit mode + spi_controller->endian = endian; // MSB or LSB +} + +void spi_send_data_standard(spi_device_num_t spi_num, spi_chip_select_t slave, const uint8_t *tx_buff, size_t tx_len) +{ + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + assert(tx_len != 0); + + // set transfer mode + spi_set_transfer_mode(spi_num,SPI_TMODE_TRANS); + + // set register status, begin to transfer + volatile spi_t *spi_controller = spi[spi_num]; + spi_controller->ser = 1U << slave; + spi_controller->ssienr = SPI_MASTER_ENABLE; + + // data transmission + size_t fifo_len, i; + uint32_t cur = 0; + while (tx_len) + { + fifo_len = SPI_FIFO_CAPCITY_IN_BYTE - spi_controller->txflr; + if (tx_len < fifo_len) + fifo_len = tx_len; + + for (i = 0; i < fifo_len; i++) + spi_controller->dr[0] = tx_buff[cur++]; + tx_len -= fifo_len; + } + // finish sign + while ((spi_controller->sr & 0x05) != 0x04) + ; + + spi_controller->ser = SPI_SLAVE_DISABLE; + spi_controller->ser = SPI_MASTER_DISABLE; +} + +void spi_receive_data_standard(spi_device_num_t spi_num, spi_chip_select_t chip_select, uint8_t *rx_buff, size_t rx_len) +{ + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + assert(rx_len != 0); + + // set transfer mode + spi_set_transfer_mode(spi_num, SPI_TMODE_RECV); + + // set register status, begin to transfer + volatile spi_t *spi_controller = spi[spi_num]; + spi_controller->ctrlr1 = (uint32_t)(rx_len - 1); + spi_controller->ssienr = SPI_MASTER_ENABLE; + spi_controller->dr[0] = 0xffffffff; + spi_controller->ser = 1U << chip_select; + + // data transmission + size_t fifo_len; + uint32_t cur = 0, i; + while (rx_len) + { + fifo_len = spi_controller->rxflr; + if (rx_len < fifo_len) + fifo_len = rx_len; + + for (i = 0; i < fifo_len; i++) + rx_buff[cur++] = spi_controller->dr[0]; + rx_len -= fifo_len; + } + + spi_controller->ser = SPI_SLAVE_DISABLE; + spi_controller->ssienr = SPI_MASTER_DISABLE; +} + +uint32_t spi_set_clk_rate(spi_device_num_t spi_num, uint32_t spi_clk) +{ + uint32_t spi_baudr = sysctl_clock_get_freq(SYSCTL_CLOCK_SPI0 + spi_num) / spi_clk; + if (spi_baudr < 2) + spi_baudr = 2; + else if (spi_baudr > 65534) + spi_baudr = 65534; + volatile spi_t *spi_controller = spi[spi_num]; + spi_controller->baudr = spi_baudr; + return sysctl_clock_get_freq(SYSCTL_CLOCK_SPI0 + spi_num) / spi_clk; +} + +static void spi_clk_init(uint8_t spi_num) +{ + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + if (spi_num == 3) + sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_SPI3, 1); + sysctl_clock_enable(SYSCTL_CLOCK_SPI0 + spi_num); + sysctl_clock_set_threshold(SYSCTL_CLOCK_SPI0 + spi_num, 0); +} + +/* + * calculate ctrlr0 from passed arguments + */ +static uint32_t spi_get_ctrlr0(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, size_t data_bit_length) +{ + uint8_t work_mode_offset, data_bit_length_offset, frame_format_offset; + switch (spi_num) + { + case 0: + case 1: + work_mode_offset = SPI01_WORK_MODE_OFFSET; + data_bit_length_offset = SPI01_DATA_BIT_LENGTH_OFFSET; + frame_format_offset = SPI01_FRAME_FORMAT_OFFSET; + break; + case 3: + work_mode_offset = SPI3_WORK_MODE_OFFSET; + data_bit_length_offset = SPI3_DATA_BIT_LENGTH_OFFSET; + frame_format_offset = SPI3_FRAME_FORMAT_OFFSET; + break; + default: + break; + } + + switch (frame_format) + { + case SPI_FF_DUAL: + assert(data_bit_length % 2 == 0); + break; + case SPI_FF_QUAD: + assert(data_bit_length % 4 == 0); + break; + case SPI_FF_OCTAL: + assert(data_bit_length % 8 == 0); + break; + default: + break; + } + + return (frame_format << frame_format_offset) + | ((data_bit_length - 1) << data_bit_length_offset) + | (work_mode << work_mode_offset); +} + +static void spi_set_transfer_mode(uint8_t spi_num, spi_transfer_mode_t tmode) +{ + assert(spi_num < SPI_DEVICE_MAX); + + uint8_t tmode_offset; + switch (spi_num) + { + case 0: + case 1: + case 2: + tmode_offset = SPI012_TRANSFER_MODE_OFFSET; + break; + case 3: + tmode_offset = SPI3_TRANSFER_MODE_OFFSET; + break; + default: + break; + } + + set_bits_value_offset(&spi[spi_num]->ctrlr0, 3, tmode, tmode_offset); +} \ No newline at end of file diff --git a/lab8/kern/driver/spi.h b/lab8/kern/driver/spi.h new file mode 100644 index 0000000..9f4f3bd --- /dev/null +++ b/lab8/kern/driver/spi.h @@ -0,0 +1,210 @@ +// +// Created by lumin on 2020/11/2. +// + +#ifndef LAB8_SPI_H +#define LAB8_SPI_H + +#include +#include + +#define SPI01_WORK_MODE_OFFSET 6 +#define SPI012_TRANSFER_MODE_OFFSET 8 +#define SPI01_DATA_BIT_LENGTH_OFFSET 16 +#define SPI01_FRAME_FORMAT_OFFSET 21 + +#define SPI3_WORK_MODE_OFFSET 8 +#define SPI3_TRANSFER_MODE_OFFSET 10 +#define SPI3_DATA_BIT_LENGTH_OFFSET 0 +#define SPI3_FRAME_FORMAT_OFFSET 22 + +#define SPI_DATA_BIT_LENGTH_BIT 5 +#define SPI_MIN_DATA_BIT_LENGTH 4 +#define SPI_MAX_DATA_BIT_LENGTH (1 << SPI_DATA_BIT_LENGTH_BIT) + +#define SPI_BAUDRATE_DEFAULT_VAL (0x14) +#define SPI_INTERRUPT_DISABLE (0x00) +#define SPI_DMACR_DEFAULT_VAL (0x00) +#define SPI_DMATDLR_DEFAULT_VAL (0x00) +#define SPI_DMARDLR_DEFAULT_VAL (0x00) +#define SPI_SLAVE_DISABLE (0x00) +#define SPI_MASTER_DISABLE (0x00) +#define SPI_MASTER_ENABLE (0x01) +#define SPI_TMOD_DEFAULT_VAL 0 + +#define SPI_FIFO_CAPCITY_IN_BYTE (32) + +typedef struct +{ + /* SPI Control Register 0 (0x00)*/ + volatile uint32_t ctrlr0; + /* SPI Control Register 1 (0x04)*/ + volatile uint32_t ctrlr1; + /* SPI Enable Register (0x08)*/ + volatile uint32_t ssienr; + /* SPI Microwire Control Register (0x0c)*/ + volatile uint32_t mwcr; + /* SPI Slave Enable Register (0x10)*/ + volatile uint32_t ser; + /* SPI Baud Rate Select (0x14)*/ + volatile uint32_t baudr; + /* SPI Transmit FIFO Threshold Level (0x18)*/ + volatile uint32_t txftlr; + /* SPI Receive FIFO Threshold Level (0x1c)*/ + volatile uint32_t rxftlr; + /* SPI Transmit FIFO Level Register (0x20)*/ + volatile uint32_t txflr; + /* SPI Receive FIFO Level Register (0x24)*/ + volatile uint32_t rxflr; + /* SPI Status Register (0x28)*/ + volatile uint32_t sr; + /* SPI Interrupt Mask Register (0x2c)*/ + volatile uint32_t imr; + /* SPI Interrupt Status Register (0x30)*/ + volatile uint32_t isr; + /* SPI Raw Interrupt Status Register (0x34)*/ + volatile uint32_t risr; + /* SPI Transmit FIFO Overflow Interrupt Clear Register (0x38)*/ + volatile uint32_t txoicr; + /* SPI Receive FIFO Overflow Interrupt Clear Register (0x3c)*/ + volatile uint32_t rxoicr; + /* SPI Receive FIFO Underflow Interrupt Clear Register (0x40)*/ + volatile uint32_t rxuicr; + /* SPI Multi-Master Interrupt Clear Register (0x44)*/ + volatile uint32_t msticr; + /* SPI Interrupt Clear Register (0x48)*/ + volatile uint32_t icr; + /* SPI DMA Control Register (0x4c)*/ + volatile uint32_t dmacr; + /* SPI DMA Transmit Data Level (0x50)*/ + volatile uint32_t dmatdlr; + /* SPI DMA Receive Data Level (0x54)*/ + volatile uint32_t dmardlr; + /* SPI Identification Register (0x58)*/ + volatile uint32_t idr; + /* SPI DWC_ssi component version (0x5c)*/ + volatile uint32_t ssic_version_id; + /* SPI Data Register 0-36 (0x60 -- 0xec)*/ + volatile uint32_t dr[36]; + /* SPI RX Sample Delay Register (0xf0)*/ + volatile uint32_t rx_sample_delay; + /* SPI SPI Control Register (0xf4)*/ + volatile uint32_t spi_ctrlr0; + /* reserved (0xf8)*/ + volatile uint32_t resv; + /* SPI XIP Mode bits (0xfc)*/ + volatile uint32_t xip_mode_bits; + /* SPI XIP INCR transfer opcode (0x100)*/ + volatile uint32_t xip_incr_inst; + /* SPI XIP WRAP transfer opcode (0x104)*/ + volatile uint32_t xip_wrap_inst; + /* SPI XIP Control Register (0x108)*/ + volatile uint32_t xip_ctrl; + /* SPI XIP Slave Enable Register (0x10c)*/ + volatile uint32_t xip_ser; + /* SPI XIP Receive FIFO Overflow Interrupt Clear Register (0x110)*/ + volatile uint32_t xrxoicr; + /* SPI XIP time out register for continuous transfers (0x114)*/ + volatile uint32_t xip_cnt_time_out; + volatile uint32_t endian; +} __attribute__((packed, aligned(4))) spi_t; + +typedef enum +{ + SPI_DEVICE_0, + SPI_DEVICE_1, + SPI_DEVICE_2, + SPI_DEVICE_3, + SPI_DEVICE_MAX, +} spi_device_num_t; + +typedef enum +{ + SPI_WORK_MODE_0, + SPI_WORK_MODE_1, + SPI_WORK_MODE_2, + SPI_WORK_MODE_3, +} spi_work_mode_t; + +typedef enum +{ + SPI_FF_STANDARD, + SPI_FF_DUAL, + SPI_FF_QUAD, + SPI_FF_OCTAL, +} spi_frame_format_t; + +typedef enum +{ + SPI_TMODE_TRANS_RECV, + SPI_TMODE_TRANS, + SPI_TMODE_RECV, + SPI_TMODE_EEROM, +} spi_transfer_mode_t; + +typedef enum +{ + SPI_CHIP_SELECT_0, + SPI_CHIP_SELECT_1, + SPI_CHIP_SELECT_2, + SPI_CHIP_SELECT_3, + SPI_CHIP_SELECT_MAX, +} spi_chip_select_t; + +extern volatile spi_t *const spi[4]; + +/** + * @brief Set spi configuration + * + * @param[in] spi_num Spi bus number + * @param[in] mode Spi mode + * @param[in] frame_format Spi frame format + * @param[in] data_bit_length Spi data bit length + * @param[in] endian 0:little-endian 1:big-endian + * + * @return Void + */ +void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, + size_t data_bit_length, uint32_t endian); + +/** + * @brief Spi send data + * + * @param[in] spi_num Spi bus number + * @param[in] slave Spi chip select + * @param[in] cmd_buff Spi command buffer point + * @param[in] cmd_len Spi command length + * @param[in] tx_buff Spi transmit buffer point + * @param[in] tx_len Spi transmit buffer length + * + * @return Result + * - 0 Success + * - Other Fail + */ +void spi_send_data_standard(spi_device_num_t spi_num, spi_chip_select_t slave, const uint8_t *tx_buff, size_t tx_len); + +/** + * @brief Spi receive data + * + * @param[in] spi_num Spi bus number + * @param[in] chip_select Spi chip select + * @param[in] rx_buff Spi receive buffer point + * @param[in] rx_len Spi receive buffer length + * + * @return Result + * - 0 Success + * - Other Fail + */ +void spi_receive_data_standard(spi_device_num_t spi_num, spi_chip_select_t chip_select, uint8_t *rx_buff, size_t rx_len); + +/** + * @brief Spi normal send by dma + * + * @param[in] spi_num Spi bus number + * @param[in] spi_clk Spi clock rate + * + * @return The real spi clock rate + */ +uint32_t spi_set_clk_rate(spi_device_num_t spi_num, uint32_t spi_clk); + +#endif //LAB8_SPI_H diff --git a/lab8/kern/driver/sysctl.c b/lab8/kern/driver/sysctl.c new file mode 100644 index 0000000..92eafe7 --- /dev/null +++ b/lab8/kern/driver/sysctl.c @@ -0,0 +1,971 @@ +// +// Created by lumin on 2020/11/2. +// +#include + +const uint8_t source_select_pll2[] = + { + [0] = SYSCTL_CLOCK_SOURCE_IN0, + [1] = SYSCTL_CLOCK_SOURCE_PLL0, + [2] = SYSCTL_CLOCK_SOURCE_PLL1 + }; + +volatile sysctl_t *const sysctl = (volatile sysctl_t *)SYSCTL_BASE_ADDR; + +static int sysctl_clock_bus_end_en(sysctl_clock_t clock, int en); +static int sysctl_clock_device_end_en(sysctl_clock_t clock, int en); +static uint32_t sysctl_clock_source_get_freq(sysctl_clock_source_t input); + +static int sysctl_clock_bus_end_en(sysctl_clock_t clock, int en) +{ + /* + * The timer is under APB0, to prevent apb0_clk_en1 and apb0_clk_en0 + * on same register, we split it to peripheral and central two + * registers, to protect CPU close apb0 clock accidentally. + * + * The apb0_clk_en0 and apb0_clk_en1 have same function, + * one of them set, the APB0 clock enable. + */ + + /* The APB clock should carefully disable */ + if (en){ + switch (clock) { + /* + * These peripheral devices are under APB0 + * GPIO, UART1, UART2, UART3, SPI_SLAVE, I2S0, I2S1, + * I2S2, I2C0, I2C1, I2C2, FPIOA, SHA256, TIMER0, + * TIMER1, TIMER2 + */ + case SYSCTL_CLOCK_GPIO: + case SYSCTL_CLOCK_SPI2: + case SYSCTL_CLOCK_I2S0: + case SYSCTL_CLOCK_I2S1: + case SYSCTL_CLOCK_I2S2: + case SYSCTL_CLOCK_I2C0: + case SYSCTL_CLOCK_I2C1: + case SYSCTL_CLOCK_I2C2: + case SYSCTL_CLOCK_UART1: + case SYSCTL_CLOCK_UART2: + case SYSCTL_CLOCK_UART3: + case SYSCTL_CLOCK_FPIOA: + case SYSCTL_CLOCK_TIMER0: + case SYSCTL_CLOCK_TIMER1: + case SYSCTL_CLOCK_TIMER2: + case SYSCTL_CLOCK_SHA: + sysctl->clk_en_cent.apb0_clk_en = en; + break; + + /* + * These peripheral devices are under APB1 + * WDT, AES, OTP, DVP, SYSCTL + */ + case SYSCTL_CLOCK_AES: + case SYSCTL_CLOCK_WDT0: + case SYSCTL_CLOCK_WDT1: + case SYSCTL_CLOCK_OTP: + case SYSCTL_CLOCK_RTC: + sysctl->clk_en_cent.apb1_clk_en = en; + break; + + /* + * These peripheral devices are under APB2 + * SPI0, SPI1 + */ + case SYSCTL_CLOCK_SPI0: + case SYSCTL_CLOCK_SPI1: + sysctl->clk_en_cent.apb2_clk_en = en; + break; + + default: + break; + } + } + + return 0; +} + +static int sysctl_clock_device_end_en(sysctl_clock_t clock, int en) +{ + switch (clock) { + /* + * These devices are PLL + */ + case SYSCTL_CLOCK_PLL0: + sysctl->pll0.pll_out_en0 = en; + break; + case SYSCTL_CLOCK_PLL1: + sysctl->pll1.pll_out_en1 = en; + break; + case SYSCTL_CLOCK_PLL2: + sysctl->pll2.pll_out_en2 = en; + break; + + /* + * These devices are CPU, SRAM, APB bus, ROM, DMA, AI + */ + case SYSCTL_CLOCK_CPU: + sysctl->clk_en_cent.cpu_clk_en = en; + break; + case SYSCTL_CLOCK_SRAM0: + sysctl->clk_en_cent.sram0_clk_en = en; + break; + case SYSCTL_CLOCK_SRAM1: + sysctl->clk_en_cent.sram1_clk_en = en; + break; + case SYSCTL_CLOCK_APB0: + sysctl->clk_en_cent.apb0_clk_en = en; + break; + case SYSCTL_CLOCK_APB1: + sysctl->clk_en_cent.apb1_clk_en = en; + break; + case SYSCTL_CLOCK_APB2: + sysctl->clk_en_cent.apb2_clk_en = en; + break; + case SYSCTL_CLOCK_ROM: + sysctl->clk_en_peri.rom_clk_en = en; + break; + case SYSCTL_CLOCK_DMA: + sysctl->clk_en_peri.dma_clk_en = en; + break; + case SYSCTL_CLOCK_AI: + sysctl->clk_en_peri.ai_clk_en = en; + break; + case SYSCTL_CLOCK_DVP: + sysctl->clk_en_peri.dvp_clk_en = en; + break; + case SYSCTL_CLOCK_FFT: + sysctl->clk_en_peri.fft_clk_en = en; + break; + case SYSCTL_CLOCK_SPI3: + sysctl->clk_en_peri.spi3_clk_en = en; + break; + + /* + * These peripheral devices are under APB0 + * GPIO, UART1, UART2, UART3, SPI_SLAVE, I2S0, I2S1, + * I2S2, I2C0, I2C1, I2C2, FPIOA, SHA256, TIMER0, + * TIMER1, TIMER2 + */ + case SYSCTL_CLOCK_GPIO: + sysctl->clk_en_peri.gpio_clk_en = en; + break; + case SYSCTL_CLOCK_SPI2: + sysctl->clk_en_peri.spi2_clk_en = en; + break; + case SYSCTL_CLOCK_I2S0: + sysctl->clk_en_peri.i2s0_clk_en = en; + break; + case SYSCTL_CLOCK_I2S1: + sysctl->clk_en_peri.i2s1_clk_en = en; + break; + case SYSCTL_CLOCK_I2S2: + sysctl->clk_en_peri.i2s2_clk_en = en; + break; + case SYSCTL_CLOCK_I2C0: + sysctl->clk_en_peri.i2c0_clk_en = en; + break; + case SYSCTL_CLOCK_I2C1: + sysctl->clk_en_peri.i2c1_clk_en = en; + break; + case SYSCTL_CLOCK_I2C2: + sysctl->clk_en_peri.i2c2_clk_en = en; + break; + case SYSCTL_CLOCK_UART1: + sysctl->clk_en_peri.uart1_clk_en = en; + break; + case SYSCTL_CLOCK_UART2: + sysctl->clk_en_peri.uart2_clk_en = en; + break; + case SYSCTL_CLOCK_UART3: + sysctl->clk_en_peri.uart3_clk_en = en; + break; + case SYSCTL_CLOCK_FPIOA: + sysctl->clk_en_peri.fpioa_clk_en = en; + break; + case SYSCTL_CLOCK_TIMER0: + sysctl->clk_en_peri.timer0_clk_en = en; + break; + case SYSCTL_CLOCK_TIMER1: + sysctl->clk_en_peri.timer1_clk_en = en; + break; + case SYSCTL_CLOCK_TIMER2: + sysctl->clk_en_peri.timer2_clk_en = en; + break; + case SYSCTL_CLOCK_SHA: + sysctl->clk_en_peri.sha_clk_en = en; + break; + + /* + * These peripheral devices are under APB1 + * WDT, AES, OTP, DVP, SYSCTL + */ + case SYSCTL_CLOCK_AES: + sysctl->clk_en_peri.aes_clk_en = en; + break; + case SYSCTL_CLOCK_WDT0: + sysctl->clk_en_peri.wdt0_clk_en = en; + break; + case SYSCTL_CLOCK_WDT1: + sysctl->clk_en_peri.wdt1_clk_en = en; + break; + case SYSCTL_CLOCK_OTP: + sysctl->clk_en_peri.otp_clk_en = en; + break; + case SYSCTL_CLOCK_RTC: + sysctl->clk_en_peri.rtc_clk_en = en; + break; + + /* + * These peripheral devices are under APB2 + * SPI0, SPI1 + */ + case SYSCTL_CLOCK_SPI0: + sysctl->clk_en_peri.spi0_clk_en = en; + break; + case SYSCTL_CLOCK_SPI1: + sysctl->clk_en_peri.spi1_clk_en = en; + break; + + default: + break; + } + + return 0; +} + +int sysctl_clock_enable(sysctl_clock_t clock) +{ + if (clock >= SYSCTL_CLOCK_MAX) + return -1; + sysctl_clock_bus_end_en(clock,1); + sysctl_clock_device_end_en(clock,1); + return 0; +} + +int sysctl_clock_set_threshold(sysctl_threshold_t which, int threshold) +{ + int result = 0; + switch (which) + { + /* + * These threshold is 2 bit width + */ + case SYSCTL_THRESHOLD_ACLK: + sysctl->clk_sel0.aclk_divider_sel = (uint8_t)threshold & 0x03; + break; + + /* + * These threshold is 3 bit width + */ + case SYSCTL_THRESHOLD_APB0: + sysctl->clk_sel0.apb0_clk_sel = (uint8_t)threshold & 0x07; + break; + case SYSCTL_THRESHOLD_APB1: + sysctl->clk_sel0.apb1_clk_sel = (uint8_t)threshold & 0x07; + break; + case SYSCTL_THRESHOLD_APB2: + sysctl->clk_sel0.apb2_clk_sel = (uint8_t)threshold & 0x07; + break; + + /* + * These threshold is 4 bit width + */ + case SYSCTL_THRESHOLD_SRAM0: + sysctl->clk_th0.sram0_gclk_threshold = (uint8_t)threshold & 0x0F; + break; + case SYSCTL_THRESHOLD_SRAM1: + sysctl->clk_th0.sram1_gclk_threshold = (uint8_t)threshold & 0x0F; + break; + case SYSCTL_THRESHOLD_AI: + sysctl->clk_th0.ai_gclk_threshold = (uint8_t)threshold & 0x0F; + break; + case SYSCTL_THRESHOLD_DVP: + sysctl->clk_th0.dvp_gclk_threshold = (uint8_t)threshold & 0x0F; + break; + case SYSCTL_THRESHOLD_ROM: + sysctl->clk_th0.rom_gclk_threshold = (uint8_t)threshold & 0x0F; + break; + + /* + * These threshold is 8 bit width + */ + case SYSCTL_THRESHOLD_SPI0: + sysctl->clk_th1.spi0_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_SPI1: + sysctl->clk_th1.spi1_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_SPI2: + sysctl->clk_th1.spi2_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_SPI3: + sysctl->clk_th1.spi3_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_TIMER0: + sysctl->clk_th2.timer0_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_TIMER1: + sysctl->clk_th2.timer1_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_TIMER2: + sysctl->clk_th2.timer2_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_I2S0_M: + sysctl->clk_th4.i2s0_mclk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_I2S1_M: + sysctl->clk_th4.i2s1_mclk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_I2S2_M: + sysctl->clk_th5.i2s2_mclk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_I2C0: + sysctl->clk_th5.i2c0_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_I2C1: + sysctl->clk_th5.i2c1_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_I2C2: + sysctl->clk_th5.i2c2_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_WDT0: + sysctl->clk_th6.wdt0_clk_threshold = (uint8_t)threshold; + break; + case SYSCTL_THRESHOLD_WDT1: + sysctl->clk_th6.wdt1_clk_threshold = (uint8_t)threshold; + break; + + /* + * These threshold is 16 bit width + */ + case SYSCTL_THRESHOLD_I2S0: + sysctl->clk_th3.i2s0_clk_threshold = (uint16_t)threshold; + break; + case SYSCTL_THRESHOLD_I2S1: + sysctl->clk_th3.i2s1_clk_threshold = (uint16_t)threshold; + break; + case SYSCTL_THRESHOLD_I2S2: + sysctl->clk_th4.i2s2_clk_threshold = (uint16_t)threshold; + break; + + default: + result = -1; + break; + } + return result; +} + +int sysctl_clock_get_threshold(sysctl_threshold_t which) +{ + int threshold = 0; + + switch (which) + { + /* + * Select and get threshold value + */ + case SYSCTL_THRESHOLD_ACLK: + threshold = (int)sysctl->clk_sel0.aclk_divider_sel; + break; + case SYSCTL_THRESHOLD_APB0: + threshold = (int)sysctl->clk_sel0.apb0_clk_sel; + break; + case SYSCTL_THRESHOLD_APB1: + threshold = (int)sysctl->clk_sel0.apb1_clk_sel; + break; + case SYSCTL_THRESHOLD_APB2: + threshold = (int)sysctl->clk_sel0.apb2_clk_sel; + break; + case SYSCTL_THRESHOLD_SRAM0: + threshold = (int)sysctl->clk_th0.sram0_gclk_threshold; + break; + case SYSCTL_THRESHOLD_SRAM1: + threshold = (int)sysctl->clk_th0.sram1_gclk_threshold; + break; + case SYSCTL_THRESHOLD_AI: + threshold = (int)sysctl->clk_th0.ai_gclk_threshold; + break; + case SYSCTL_THRESHOLD_DVP: + threshold = (int)sysctl->clk_th0.dvp_gclk_threshold; + break; + case SYSCTL_THRESHOLD_ROM: + threshold = (int)sysctl->clk_th0.rom_gclk_threshold; + break; + case SYSCTL_THRESHOLD_SPI0: + threshold = (int)sysctl->clk_th1.spi0_clk_threshold; + break; + case SYSCTL_THRESHOLD_SPI1: + threshold = (int)sysctl->clk_th1.spi1_clk_threshold; + break; + case SYSCTL_THRESHOLD_SPI2: + threshold = (int)sysctl->clk_th1.spi2_clk_threshold; + break; + case SYSCTL_THRESHOLD_SPI3: + threshold = (int)sysctl->clk_th1.spi3_clk_threshold; + break; + case SYSCTL_THRESHOLD_TIMER0: + threshold = (int)sysctl->clk_th2.timer0_clk_threshold; + break; + case SYSCTL_THRESHOLD_TIMER1: + threshold = (int)sysctl->clk_th2.timer1_clk_threshold; + break; + case SYSCTL_THRESHOLD_TIMER2: + threshold = (int)sysctl->clk_th2.timer2_clk_threshold; + break; + case SYSCTL_THRESHOLD_I2S0: + threshold = (int)sysctl->clk_th3.i2s0_clk_threshold; + break; + case SYSCTL_THRESHOLD_I2S1: + threshold = (int)sysctl->clk_th3.i2s1_clk_threshold; + break; + case SYSCTL_THRESHOLD_I2S2: + threshold = (int)sysctl->clk_th4.i2s2_clk_threshold; + break; + case SYSCTL_THRESHOLD_I2S0_M: + threshold = (int)sysctl->clk_th4.i2s0_mclk_threshold; + break; + case SYSCTL_THRESHOLD_I2S1_M: + threshold = (int)sysctl->clk_th4.i2s1_mclk_threshold; + break; + case SYSCTL_THRESHOLD_I2S2_M: + threshold = (int)sysctl->clk_th5.i2s2_mclk_threshold; + break; + case SYSCTL_THRESHOLD_I2C0: + threshold = (int)sysctl->clk_th5.i2c0_clk_threshold; + break; + case SYSCTL_THRESHOLD_I2C1: + threshold = (int)sysctl->clk_th5.i2c1_clk_threshold; + break; + case SYSCTL_THRESHOLD_I2C2: + threshold = (int)sysctl->clk_th5.i2c2_clk_threshold; + break; + case SYSCTL_THRESHOLD_WDT0: + threshold = (int)sysctl->clk_th6.wdt0_clk_threshold; + break; + case SYSCTL_THRESHOLD_WDT1: + threshold = (int)sysctl->clk_th6.wdt1_clk_threshold; + break; + + default: + break; + } + + return threshold; +} + +int sysctl_clock_set_clock_select(sysctl_clock_select_t which, int select) +{ + int result = 0; + switch (which) + { + /* + * These clock select is 1 bit width + */ + case SYSCTL_CLOCK_SELECT_PLL0_BYPASS: + sysctl->pll0.pll_bypass0 = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_PLL1_BYPASS: + sysctl->pll1.pll_bypass1 = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_PLL2_BYPASS: + sysctl->pll2.pll_bypass2 = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_ACLK: + sysctl->clk_sel0.aclk_sel = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_SPI3: + sysctl->clk_sel0.spi3_clk_sel = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_TIMER0: + sysctl->clk_sel0.timer0_clk_sel = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_TIMER1: + sysctl->clk_sel0.timer1_clk_sel = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_TIMER2: + sysctl->clk_sel0.timer2_clk_sel = select & 0x01; + break; + case SYSCTL_CLOCK_SELECT_SPI3_SAMPLE: + sysctl->clk_sel1.spi3_sample_clk_sel = select & 0x01; + break; + + /* + * These clock select is 2 bit width + */ + case SYSCTL_CLOCK_SELECT_PLL2: + sysctl->pll2.pll_ckin_sel2 = select & 0x03; + break; + + default: + result = -1; + break; + } + + return result; +} + +int sysctl_clock_get_clock_select(sysctl_clock_select_t which) +{ + int clock_select = 0; + + switch (which) + { + /* + * Select and get clock select value + */ + case SYSCTL_CLOCK_SELECT_PLL0_BYPASS: + clock_select = (int)sysctl->pll0.pll_bypass0; + break; + case SYSCTL_CLOCK_SELECT_PLL1_BYPASS: + clock_select = (int)sysctl->pll1.pll_bypass1; + break; + case SYSCTL_CLOCK_SELECT_PLL2_BYPASS: + clock_select = (int)sysctl->pll2.pll_bypass2; + break; + case SYSCTL_CLOCK_SELECT_PLL2: + clock_select = (int)sysctl->pll2.pll_ckin_sel2; + break; + case SYSCTL_CLOCK_SELECT_ACLK: + clock_select = (int)sysctl->clk_sel0.aclk_sel; + break; + case SYSCTL_CLOCK_SELECT_SPI3: + clock_select = (int)sysctl->clk_sel0.spi3_clk_sel; + break; + case SYSCTL_CLOCK_SELECT_TIMER0: + clock_select = (int)sysctl->clk_sel0.timer0_clk_sel; + break; + case SYSCTL_CLOCK_SELECT_TIMER1: + clock_select = (int)sysctl->clk_sel0.timer1_clk_sel; + break; + case SYSCTL_CLOCK_SELECT_TIMER2: + clock_select = (int)sysctl->clk_sel0.timer2_clk_sel; + break; + case SYSCTL_CLOCK_SELECT_SPI3_SAMPLE: + clock_select = (int)sysctl->clk_sel1.spi3_sample_clk_sel; + break; + + default: + break; + } + + return clock_select; +} + +static uint32_t sysctl_clock_source_get_freq(sysctl_clock_source_t input) +{ + uint32_t result; + + switch (input) + { + case SYSCTL_CLOCK_SOURCE_IN0: + result = SYSCTL_CLOCK_FREQ_IN; + break; + case SYSCTL_CLOCK_SOURCE_PLL0: + result = sysctl_pll_get_freq(SYSCTL_PLL0); + break; + case SYSCTL_CLOCK_SOURCE_PLL1: + result = sysctl_pll_get_freq(SYSCTL_PLL1); + break; + case SYSCTL_CLOCK_SOURCE_PLL2: + result = sysctl_pll_get_freq(SYSCTL_PLL2); + break; + case SYSCTL_CLOCK_SOURCE_ACLK: + result = sysctl_clock_get_freq(SYSCTL_CLOCK_ACLK); + break; + default: + result = 0; + break; + } + + return result; +} + +uint32_t sysctl_pll_get_freq(sysctl_pll_t pll) +{ + uint32_t freq_in = 0, freq_out = 0; + uint32_t nr = 0, nf = 0, od = 0; + uint8_t select = 0; + + if (pll >= SYSCTL_PLL_MAX) + return 0; + + switch (pll) + { + case SYSCTL_PLL0: + freq_in = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + nr = sysctl->pll0.clkr0 + 1; + nf = sysctl->pll0.clkf0 + 1; + od = sysctl->pll0.clkod0 + 1; + break; + case SYSCTL_PLL1: + freq_in = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + nr = sysctl->pll1.clkr1 + 1; + nf = sysctl->pll1.clkf1 + 1; + od = sysctl->pll1.clkod1 + 1; + break; + case SYSCTL_PLL2: + /* + * Get input freq accroding select register + */ + select = sysctl->pll2.pll_ckin_sel2; + if (select < sizeof(source_select_pll2)) + freq_in = sysctl_clock_source_get_freq(source_select_pll2[select]); + else + freq_in = 0; + nr = sysctl->pll2.clkr2 + 1; + nf = sysctl->pll2.clkf2 + 1; + od = sysctl->pll2.clkod2 + 1; + break; + default: + break; + } + + freq_out = (double)freq_in/(double)nr*(double)nf/(double)od; + return freq_out; +} + +uint32_t sysctl_clock_get_freq(sysctl_clock_t clock) +{ + uint32_t source = 0; + uint32_t result = 0; + + switch (clock) + { + /* + * The clock IN0 + */ + case SYSCTL_CLOCK_IN0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + result = source; + break; + + /* + * These clock directly under PLL clock domain + * They are using gated divider. + */ + case SYSCTL_CLOCK_PLL0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source; + break; + case SYSCTL_CLOCK_PLL1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL1); + result = source; + break; + case SYSCTL_CLOCK_PLL2: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL2); + result = source; + break; + + /* + * These clock directly under ACLK clock domain + */ + case SYSCTL_CLOCK_CPU: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0) / + (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); + break; + default: + break; + } + result = source; + break; + case SYSCTL_CLOCK_DMA: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0) / + (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); + break; + default: + break; + } + result = source; + break; + case SYSCTL_CLOCK_FFT: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0) / + (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); + break; + default: + break; + } + result = source; + break; + case SYSCTL_CLOCK_ACLK: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0) / + (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); + break; + default: + break; + } + result = source; + break; + case SYSCTL_CLOCK_HCLK: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0) / + (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); + break; + default: + break; + } + result = source; + break; + + /* + * These clock under ACLK clock domain. + * They are using gated divider. + */ + case SYSCTL_CLOCK_SRAM0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SRAM0) + 1); + break; + case SYSCTL_CLOCK_SRAM1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SRAM1) + 1); + break; + case SYSCTL_CLOCK_ROM: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ROM) + 1); + break; + case SYSCTL_CLOCK_DVP: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_DVP) + 1); + break; + + /* + * These clock under ACLK clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_APB0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_APB0) + 1); + break; + case SYSCTL_CLOCK_APB1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_APB1) + 1); + break; + case SYSCTL_CLOCK_APB2: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_ACLK); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_APB2) + 1); + break; + + /* + * These clock under AI clock domain. + * They are using gated divider. + */ + case SYSCTL_CLOCK_AI: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL1); + result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_AI) + 1); + break; + + /* + * These clock under I2S clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_I2S0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL2); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2S0) + 1) * 2); + break; + case SYSCTL_CLOCK_I2S1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL2); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2S1) + 1) * 2); + break; + case SYSCTL_CLOCK_I2S2: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL2); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2S2) + 1) * 2); + break; + + /* + * These clock under WDT clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_WDT0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_WDT0) + 1) * 2); + break; + case SYSCTL_CLOCK_WDT1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_WDT1) + 1) * 2); + break; + + /* + * These clock under PLL0 clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_SPI0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI0) + 1) * 2); + break; + case SYSCTL_CLOCK_SPI1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI1) + 1) * 2); + break; + case SYSCTL_CLOCK_SPI2: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI2) + 1) * 2); + break; + case SYSCTL_CLOCK_I2C0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2C0) + 1) * 2); + break; + case SYSCTL_CLOCK_I2C1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2C1) + 1) * 2); + break; + case SYSCTL_CLOCK_I2C2: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2C2) + 1) * 2); + break; + + /* + * These clock under PLL0_SEL clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_SPI3: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_SPI3)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + break; + default: + break; + } + + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI3) + 1) * 2); + break; + case SYSCTL_CLOCK_TIMER0: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_TIMER0)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + break; + default: + break; + } + + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_TIMER0) + 1) * 2); + break; + case SYSCTL_CLOCK_TIMER1: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_TIMER1)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + break; + default: + break; + } + + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_TIMER1) + 1) * 2); + break; + case SYSCTL_CLOCK_TIMER2: + switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_TIMER2)) + { + case 0: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + break; + case 1: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_PLL0); + break; + default: + break; + } + + result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_TIMER2) + 1) * 2); + break; + + /* + * These clock under MISC clock domain. + * They are using even divider. + */ + + /* + * These clock under APB0 clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_GPIO: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); + result = source; + break; + case SYSCTL_CLOCK_UART1: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); + result = source; + break; + case SYSCTL_CLOCK_UART2: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); + result = source; + break; + case SYSCTL_CLOCK_UART3: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); + result = source; + break; + case SYSCTL_CLOCK_FPIOA: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); + result = source; + break; + case SYSCTL_CLOCK_SHA: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); + result = source; + break; + + /* + * These clock under APB1 clock domain. + * They are using even divider. + */ + case SYSCTL_CLOCK_AES: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB1); + result = source; + break; + case SYSCTL_CLOCK_OTP: + source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB1); + result = source; + break; + case SYSCTL_CLOCK_RTC: + source = sysctl_clock_source_get_freq(SYSCTL_CLOCK_SOURCE_IN0); + result = source; + break; + + /* + * These clock under APB2 clock domain. + * They are using even divider. + */ + /* + * Do nothing. + */ + default: + break; + } + return result; +} \ No newline at end of file diff --git a/lab8/kern/driver/sysctl.h b/lab8/kern/driver/sysctl.h new file mode 100644 index 0000000..34dccb2 --- /dev/null +++ b/lab8/kern/driver/sysctl.h @@ -0,0 +1,940 @@ +// +// Created by lumin on 2020/11/2. +// + +#ifndef LAB8_SYSCTL_H +#define LAB8_SYSCTL_H + +#include +#include + +#define SYSCTL_CLOCK_FREQ_IN (26000000UL) + +typedef enum _sysctl_pll_t +{ + SYSCTL_PLL0, + SYSCTL_PLL1, + SYSCTL_PLL2, + SYSCTL_PLL_MAX +} sysctl_pll_t; + +typedef enum _sysctl_clock_source_t +{ + SYSCTL_CLOCK_SOURCE_IN0, + SYSCTL_CLOCK_SOURCE_PLL0, + SYSCTL_CLOCK_SOURCE_PLL1, + SYSCTL_CLOCK_SOURCE_PLL2, + SYSCTL_CLOCK_SOURCE_ACLK, + SYSCTL_CLOCK_SOURCE_MAX +} sysctl_clock_source_t; + +typedef enum _sysctl_dma_channel_t +{ + SYSCTL_DMA_CHANNEL_0, + SYSCTL_DMA_CHANNEL_1, + SYSCTL_DMA_CHANNEL_2, + SYSCTL_DMA_CHANNEL_3, + SYSCTL_DMA_CHANNEL_4, + SYSCTL_DMA_CHANNEL_5, + SYSCTL_DMA_CHANNEL_MAX +} sysctl_dma_channel_t; + +typedef enum _sysctl_dma_select_t +{ + SYSCTL_DMA_SELECT_SSI0_RX_REQ, + SYSCTL_DMA_SELECT_SSI0_TX_REQ, + SYSCTL_DMA_SELECT_SSI1_RX_REQ, + SYSCTL_DMA_SELECT_SSI1_TX_REQ, + SYSCTL_DMA_SELECT_SSI2_RX_REQ, + SYSCTL_DMA_SELECT_SSI2_TX_REQ, + SYSCTL_DMA_SELECT_SSI3_RX_REQ, + SYSCTL_DMA_SELECT_SSI3_TX_REQ, + SYSCTL_DMA_SELECT_I2C0_RX_REQ, + SYSCTL_DMA_SELECT_I2C0_TX_REQ, + SYSCTL_DMA_SELECT_I2C1_RX_REQ, + SYSCTL_DMA_SELECT_I2C1_TX_REQ, + SYSCTL_DMA_SELECT_I2C2_RX_REQ, + SYSCTL_DMA_SELECT_I2C2_TX_REQ, + SYSCTL_DMA_SELECT_UART1_RX_REQ, + SYSCTL_DMA_SELECT_UART1_TX_REQ, + SYSCTL_DMA_SELECT_UART2_RX_REQ, + SYSCTL_DMA_SELECT_UART2_TX_REQ, + SYSCTL_DMA_SELECT_UART3_RX_REQ, + SYSCTL_DMA_SELECT_UART3_TX_REQ, + SYSCTL_DMA_SELECT_AES_REQ, + SYSCTL_DMA_SELECT_SHA_RX_REQ, + SYSCTL_DMA_SELECT_AI_RX_REQ, + SYSCTL_DMA_SELECT_FFT_RX_REQ, + SYSCTL_DMA_SELECT_FFT_TX_REQ, + SYSCTL_DMA_SELECT_I2S0_TX_REQ, + SYSCTL_DMA_SELECT_I2S0_RX_REQ, + SYSCTL_DMA_SELECT_I2S1_TX_REQ, + SYSCTL_DMA_SELECT_I2S1_RX_REQ, + SYSCTL_DMA_SELECT_I2S2_TX_REQ, + SYSCTL_DMA_SELECT_I2S2_RX_REQ, + SYSCTL_DMA_SELECT_I2S0_BF_DIR_REQ, + SYSCTL_DMA_SELECT_I2S0_BF_VOICE_REQ, + SYSCTL_DMA_SELECT_MAX +} sysctl_dma_select_t; + +/** + * @brief System controller clock id + */ +typedef enum _sysctl_clock_t +{ + SYSCTL_CLOCK_PLL0, + SYSCTL_CLOCK_PLL1, + SYSCTL_CLOCK_PLL2, + SYSCTL_CLOCK_CPU, + SYSCTL_CLOCK_SRAM0, + SYSCTL_CLOCK_SRAM1, + SYSCTL_CLOCK_APB0, + SYSCTL_CLOCK_APB1, + SYSCTL_CLOCK_APB2, + SYSCTL_CLOCK_ROM, + SYSCTL_CLOCK_DMA, + SYSCTL_CLOCK_AI, + SYSCTL_CLOCK_DVP, + SYSCTL_CLOCK_FFT, + SYSCTL_CLOCK_GPIO, + SYSCTL_CLOCK_SPI0, + SYSCTL_CLOCK_SPI1, + SYSCTL_CLOCK_SPI2, + SYSCTL_CLOCK_SPI3, + SYSCTL_CLOCK_I2S0, + SYSCTL_CLOCK_I2S1, + SYSCTL_CLOCK_I2S2, + SYSCTL_CLOCK_I2C0, + SYSCTL_CLOCK_I2C1, + SYSCTL_CLOCK_I2C2, + SYSCTL_CLOCK_UART1, + SYSCTL_CLOCK_UART2, + SYSCTL_CLOCK_UART3, + SYSCTL_CLOCK_AES, + SYSCTL_CLOCK_FPIOA, + SYSCTL_CLOCK_TIMER0, + SYSCTL_CLOCK_TIMER1, + SYSCTL_CLOCK_TIMER2, + SYSCTL_CLOCK_WDT0, + SYSCTL_CLOCK_WDT1, + SYSCTL_CLOCK_SHA, + SYSCTL_CLOCK_OTP, + SYSCTL_CLOCK_RTC, + SYSCTL_CLOCK_ACLK = 40, + SYSCTL_CLOCK_HCLK, + SYSCTL_CLOCK_IN0, + SYSCTL_CLOCK_MAX +} sysctl_clock_t; + +/** + * @brief System controller clock select id + */ +typedef enum _sysctl_clock_select_t +{ + SYSCTL_CLOCK_SELECT_PLL0_BYPASS, + SYSCTL_CLOCK_SELECT_PLL1_BYPASS, + SYSCTL_CLOCK_SELECT_PLL2_BYPASS, + SYSCTL_CLOCK_SELECT_PLL2, + SYSCTL_CLOCK_SELECT_ACLK, + SYSCTL_CLOCK_SELECT_SPI3, + SYSCTL_CLOCK_SELECT_TIMER0, + SYSCTL_CLOCK_SELECT_TIMER1, + SYSCTL_CLOCK_SELECT_TIMER2, + SYSCTL_CLOCK_SELECT_SPI3_SAMPLE, + SYSCTL_CLOCK_SELECT_MAX = 11 +} sysctl_clock_select_t; + +/** + * @brief System controller clock threshold id + */ +typedef enum _sysctl_threshold_t +{ + SYSCTL_THRESHOLD_ACLK, + SYSCTL_THRESHOLD_APB0, + SYSCTL_THRESHOLD_APB1, + SYSCTL_THRESHOLD_APB2, + SYSCTL_THRESHOLD_SRAM0, + SYSCTL_THRESHOLD_SRAM1, + SYSCTL_THRESHOLD_AI, + SYSCTL_THRESHOLD_DVP, + SYSCTL_THRESHOLD_ROM, + SYSCTL_THRESHOLD_SPI0, + SYSCTL_THRESHOLD_SPI1, + SYSCTL_THRESHOLD_SPI2, + SYSCTL_THRESHOLD_SPI3, + SYSCTL_THRESHOLD_TIMER0, + SYSCTL_THRESHOLD_TIMER1, + SYSCTL_THRESHOLD_TIMER2, + SYSCTL_THRESHOLD_I2S0, + SYSCTL_THRESHOLD_I2S1, + SYSCTL_THRESHOLD_I2S2, + SYSCTL_THRESHOLD_I2S0_M, + SYSCTL_THRESHOLD_I2S1_M, + SYSCTL_THRESHOLD_I2S2_M, + SYSCTL_THRESHOLD_I2C0, + SYSCTL_THRESHOLD_I2C1, + SYSCTL_THRESHOLD_I2C2, + SYSCTL_THRESHOLD_WDT0, + SYSCTL_THRESHOLD_WDT1, + SYSCTL_THRESHOLD_MAX = 28 +} sysctl_threshold_t; + +/** + * @brief System controller reset control id + */ +typedef enum _sysctl_reset_t +{ + SYSCTL_RESET_SOC, + SYSCTL_RESET_ROM, + SYSCTL_RESET_DMA, + SYSCTL_RESET_AI, + SYSCTL_RESET_DVP, + SYSCTL_RESET_FFT, + SYSCTL_RESET_GPIO, + SYSCTL_RESET_SPI0, + SYSCTL_RESET_SPI1, + SYSCTL_RESET_SPI2, + SYSCTL_RESET_SPI3, + SYSCTL_RESET_I2S0, + SYSCTL_RESET_I2S1, + SYSCTL_RESET_I2S2, + SYSCTL_RESET_I2C0, + SYSCTL_RESET_I2C1, + SYSCTL_RESET_I2C2, + SYSCTL_RESET_UART1, + SYSCTL_RESET_UART2, + SYSCTL_RESET_UART3, + SYSCTL_RESET_AES, + SYSCTL_RESET_FPIOA, + SYSCTL_RESET_TIMER0, + SYSCTL_RESET_TIMER1, + SYSCTL_RESET_TIMER2, + SYSCTL_RESET_WDT0, + SYSCTL_RESET_WDT1, + SYSCTL_RESET_SHA, + SYSCTL_RESET_RTC, + SYSCTL_RESET_MAX = 31 +} sysctl_reset_t; + +/** + * @brief System controller power bank id + */ +typedef enum _sysctl_power_bank +{ + SYSCTL_POWER_BANK0, + SYSCTL_POWER_BANK1, + SYSCTL_POWER_BANK2, + SYSCTL_POWER_BANK3, + SYSCTL_POWER_BANK4, + SYSCTL_POWER_BANK5, + SYSCTL_POWER_BANK6, + SYSCTL_POWER_BANK7, + SYSCTL_POWER_BANK_MAX, +} sysctl_power_bank_t; + +/** + * @brief System controller reset control id + */ +typedef enum _sysctl_io_power_mode +{ + SYSCTL_POWER_V33, + SYSCTL_POWER_V18 +} sysctl_io_power_mode_t; + +/** + * @brief System reset status + */ +typedef enum _sysctl_reset_enum_status +{ + SYSCTL_RESET_STATUS_HARD, + SYSCTL_RESET_STATUS_SOFT, + SYSCTL_RESET_STATUS_WDT0, + SYSCTL_RESET_STATUS_WDT1, + SYSCTL_RESET_STATUS_MAX, +} sysctl_reset_enum_status_t; + +/** + * @brief Git short commit id + * + * No. 0 Register (0x00) + */ +typedef struct _sysctl_git_id +{ + uint32_t git_id : 32; +} __attribute__((packed, aligned(4))) sysctl_git_id_t; + +/** + * @brief System clock base frequency + * + * No. 1 Register (0x04) + */ +typedef struct _sysctl_clk_freq +{ + uint32_t clk_freq : 32; +} __attribute__((packed, aligned(4))) sysctl_clk_freq_t; + +/** + * @brief PLL0 controller + * + * No. 2 Register (0x08) + */ +typedef struct _sysctl_pll0 +{ + uint32_t clkr0 : 4; + uint32_t clkf0 : 6; + uint32_t clkod0 : 4; + uint32_t bwadj0 : 6; + uint32_t pll_reset0 : 1; + uint32_t pll_pwrd0 : 1; + uint32_t pll_intfb0 : 1; + uint32_t pll_bypass0 : 1; + uint32_t pll_test0 : 1; + uint32_t pll_out_en0 : 1; + uint32_t pll_test_en : 1; + uint32_t reserved : 5; +} __attribute__((packed, aligned(4))) sysctl_pll0_t; + +/** + * @brief PLL1 controller + * + * No. 3 Register (0x0c) + */ +typedef struct _sysctl_pll1 +{ + uint32_t clkr1 : 4; + uint32_t clkf1 : 6; + uint32_t clkod1 : 4; + uint32_t bwadj1 : 6; + uint32_t pll_reset1 : 1; + uint32_t pll_pwrd1 : 1; + uint32_t pll_intfb1 : 1; + uint32_t pll_bypass1 : 1; + uint32_t pll_test1 : 1; + uint32_t pll_out_en1 : 1; + uint32_t reserved : 6; +} __attribute__((packed, aligned(4))) sysctl_pll1_t; + +/** + * @brief PLL2 controller + * + * No. 4 Register (0x10) + */ +typedef struct _sysctl_pll2 +{ + uint32_t clkr2 : 4; + uint32_t clkf2 : 6; + uint32_t clkod2 : 4; + uint32_t bwadj2 : 6; + uint32_t pll_reset2 : 1; + uint32_t pll_pwrd2 : 1; + uint32_t pll_intfb2 : 1; + uint32_t pll_bypass2 : 1; + uint32_t pll_test2 : 1; + uint32_t pll_out_en2 : 1; + uint32_t pll_ckin_sel2 : 2; + uint32_t reserved : 4; +} __attribute__((packed, aligned(4))) sysctl_pll2_t; + +/** + * @brief PLL lock tester + * + * No. 6 Register (0x18) + */ +typedef struct _sysctl_pll_lock +{ + uint32_t pll_lock0 : 2; + uint32_t pll_slip_clear0 : 1; + uint32_t test_clk_out0 : 1; + uint32_t reserved0 : 4; + uint32_t pll_lock1 : 2; + uint32_t pll_slip_clear1 : 1; + uint32_t test_clk_out1 : 1; + uint32_t reserved1 : 4; + uint32_t pll_lock2 : 2; + uint32_t pll_slip_clear2 : 1; + uint32_t test_clk_out2 : 1; + uint32_t reserved2 : 12; +} __attribute__((packed, aligned(4))) sysctl_pll_lock_t; + +/** + * @brief AXI ROM detector + * + * No. 7 Register (0x1c) + */ +typedef struct _sysctl_rom_error +{ + uint32_t rom_mul_error : 1; + uint32_t rom_one_error : 1; + uint32_t reserved : 30; +} __attribute__((packed, aligned(4))) sysctl_rom_error_t; + +/** + * @brief Clock select controller0 + * + * No. 8 Register (0x20) + */ +typedef struct _sysctl_clk_sel0 +{ + uint32_t aclk_sel : 1; + uint32_t aclk_divider_sel : 2; + uint32_t apb0_clk_sel : 3; + uint32_t apb1_clk_sel : 3; + uint32_t apb2_clk_sel : 3; + uint32_t spi3_clk_sel : 1; + uint32_t timer0_clk_sel : 1; + uint32_t timer1_clk_sel : 1; + uint32_t timer2_clk_sel : 1; + uint32_t reserved : 16; +} __attribute__((packed, aligned(4))) sysctl_clk_sel0_t; + +/** + * @brief Clock select controller1 + * + * No. 9 Register (0x24) + */ +typedef struct _sysctl_clk_sel1 +{ + uint32_t spi3_sample_clk_sel : 1; + uint32_t reserved0 : 30; + uint32_t reserved1 : 1; +} __attribute__((packed, aligned(4))) sysctl_clk_sel1_t; + +/** + * @brief Central clock enable + * + * No. 10 Register (0x28) + */ +typedef struct _sysctl_clk_en_cent +{ + uint32_t cpu_clk_en : 1; + uint32_t sram0_clk_en : 1; + uint32_t sram1_clk_en : 1; + uint32_t apb0_clk_en : 1; + uint32_t apb1_clk_en : 1; + uint32_t apb2_clk_en : 1; + uint32_t reserved : 26; +} __attribute__((packed, aligned(4))) sysctl_clk_en_cent_t; + +/** + * @brief Peripheral clock enable + * + * No. 11 Register (0x2c) + */ +typedef struct _sysctl_clk_en_peri +{ + uint32_t rom_clk_en : 1; + uint32_t dma_clk_en : 1; + uint32_t ai_clk_en : 1; + uint32_t dvp_clk_en : 1; + uint32_t fft_clk_en : 1; + uint32_t gpio_clk_en : 1; + uint32_t spi0_clk_en : 1; + uint32_t spi1_clk_en : 1; + uint32_t spi2_clk_en : 1; + uint32_t spi3_clk_en : 1; + uint32_t i2s0_clk_en : 1; + uint32_t i2s1_clk_en : 1; + uint32_t i2s2_clk_en : 1; + uint32_t i2c0_clk_en : 1; + uint32_t i2c1_clk_en : 1; + uint32_t i2c2_clk_en : 1; + uint32_t uart1_clk_en : 1; + uint32_t uart2_clk_en : 1; + uint32_t uart3_clk_en : 1; + uint32_t aes_clk_en : 1; + uint32_t fpioa_clk_en : 1; + uint32_t timer0_clk_en : 1; + uint32_t timer1_clk_en : 1; + uint32_t timer2_clk_en : 1; + uint32_t wdt0_clk_en : 1; + uint32_t wdt1_clk_en : 1; + uint32_t sha_clk_en : 1; + uint32_t otp_clk_en : 1; + uint32_t reserved : 1; + uint32_t rtc_clk_en : 1; + uint32_t reserved0 : 2; +} __attribute__((packed, aligned(4))) sysctl_clk_en_peri_t; + +/** + * @brief Soft reset ctrl + * + * No. 12 Register (0x30) + */ +typedef struct _sysctl_soft_reset +{ + uint32_t soft_reset : 1; + uint32_t reserved : 31; +} __attribute__((packed, aligned(4))) sysctl_soft_reset_t; + +/** + * @brief Peripheral reset controller + * + * No. 13 Register (0x34) + */ +typedef struct _sysctl_peri_reset +{ + uint32_t rom_reset : 1; + uint32_t dma_reset : 1; + uint32_t ai_reset : 1; + uint32_t dvp_reset : 1; + uint32_t fft_reset : 1; + uint32_t gpio_reset : 1; + uint32_t spi0_reset : 1; + uint32_t spi1_reset : 1; + uint32_t spi2_reset : 1; + uint32_t spi3_reset : 1; + uint32_t i2s0_reset : 1; + uint32_t i2s1_reset : 1; + uint32_t i2s2_reset : 1; + uint32_t i2c0_reset : 1; + uint32_t i2c1_reset : 1; + uint32_t i2c2_reset : 1; + uint32_t uart1_reset : 1; + uint32_t uart2_reset : 1; + uint32_t uart3_reset : 1; + uint32_t aes_reset : 1; + uint32_t fpioa_reset : 1; + uint32_t timer0_reset : 1; + uint32_t timer1_reset : 1; + uint32_t timer2_reset : 1; + uint32_t wdt0_reset : 1; + uint32_t wdt1_reset : 1; + uint32_t sha_reset : 1; + uint32_t reserved : 2; + uint32_t rtc_reset : 1; + uint32_t reserved0 : 2; +} __attribute__((packed, aligned(4))) sysctl_peri_reset_t; + +/** + * @brief Clock threshold controller 0 + * + * No. 14 Register (0x38) + */ +typedef struct _sysctl_clk_th0 +{ + uint32_t sram0_gclk_threshold : 4; + uint32_t sram1_gclk_threshold : 4; + uint32_t ai_gclk_threshold : 4; + uint32_t dvp_gclk_threshold : 4; + uint32_t rom_gclk_threshold : 4; + uint32_t reserved : 12; +} __attribute__((packed, aligned(4))) sysctl_clk_th0_t; + +/** + * @brief Clock threshold controller 1 + * + * No. 15 Register (0x3c) + */ +typedef struct _sysctl_clk_th1 +{ + uint32_t spi0_clk_threshold : 8; + uint32_t spi1_clk_threshold : 8; + uint32_t spi2_clk_threshold : 8; + uint32_t spi3_clk_threshold : 8; +} __attribute__((packed, aligned(4))) sysctl_clk_th1_t; + +/** + * @brief Clock threshold controller 2 + * + * No. 16 Register (0x40) + */ +typedef struct _sysctl_clk_th2 +{ + uint32_t timer0_clk_threshold : 8; + uint32_t timer1_clk_threshold : 8; + uint32_t timer2_clk_threshold : 8; + uint32_t reserved : 8; +} __attribute__((packed, aligned(4))) sysctl_clk_th2_t; + +/** + * @brief Clock threshold controller 3 + * + * No. 17 Register (0x44) + */ +typedef struct _sysctl_clk_th3 +{ + uint32_t i2s0_clk_threshold : 16; + uint32_t i2s1_clk_threshold : 16; +} __attribute__((packed, aligned(4))) sysctl_clk_th3_t; + +/** + * @brief Clock threshold controller 4 + * + * No. 18 Register (0x48) + */ +typedef struct _sysctl_clk_th4 +{ + uint32_t i2s2_clk_threshold : 16; + uint32_t i2s0_mclk_threshold : 8; + uint32_t i2s1_mclk_threshold : 8; +} __attribute__((packed, aligned(4))) sysctl_clk_th4_t; + +/** + * @brief Clock threshold controller 5 + * + * No. 19 Register (0x4c) + */ +typedef struct _sysctl_clk_th5 +{ + uint32_t i2s2_mclk_threshold : 8; + uint32_t i2c0_clk_threshold : 8; + uint32_t i2c1_clk_threshold : 8; + uint32_t i2c2_clk_threshold : 8; +} __attribute__((packed, aligned(4))) sysctl_clk_th5_t; + +/** + * @brief Clock threshold controller 6 + * + * No. 20 Register (0x50) + */ +typedef struct _sysctl_clk_th6 +{ + uint32_t wdt0_clk_threshold : 8; + uint32_t wdt1_clk_threshold : 8; + uint32_t reserved0 : 8; + uint32_t reserved1 : 8; +} __attribute__((packed, aligned(4))) sysctl_clk_th6_t; + +/** + * @brief Miscellaneous controller + * + * No. 21 Register (0x54) + */ +typedef struct _sysctl_misc +{ + uint32_t debug_sel : 6; + uint32_t reserved0 : 4; + uint32_t spi_dvp_data_enable: 1; + uint32_t reserved1 : 21; +} __attribute__((packed, aligned(4))) sysctl_misc_t; + +/** + * @brief Peripheral controller + * + * No. 22 Register (0x58) + */ +typedef struct _sysctl_peri +{ + uint32_t timer0_pause : 1; + uint32_t timer1_pause : 1; + uint32_t timer2_pause : 1; + uint32_t timer3_pause : 1; + uint32_t timer4_pause : 1; + uint32_t timer5_pause : 1; + uint32_t timer6_pause : 1; + uint32_t timer7_pause : 1; + uint32_t timer8_pause : 1; + uint32_t timer9_pause : 1; + uint32_t timer10_pause : 1; + uint32_t timer11_pause : 1; + uint32_t spi0_xip_en : 1; + uint32_t spi1_xip_en : 1; + uint32_t spi2_xip_en : 1; + uint32_t spi3_xip_en : 1; + uint32_t spi0_clk_bypass : 1; + uint32_t spi1_clk_bypass : 1; + uint32_t spi2_clk_bypass : 1; + uint32_t i2s0_clk_bypass : 1; + uint32_t i2s1_clk_bypass : 1; + uint32_t i2s2_clk_bypass : 1; + uint32_t jtag_clk_bypass : 1; + uint32_t dvp_clk_bypass : 1; + uint32_t debug_clk_bypass : 1; + uint32_t reserved0 : 1; + uint32_t reserved1 : 6; +} __attribute__((packed, aligned(4))) sysctl_peri_t; + +/** + * @brief SPI sleep controller + * + * No. 23 Register (0x5c) + */ +typedef struct _sysctl_spi_sleep +{ + uint32_t ssi0_sleep : 1; + uint32_t ssi1_sleep : 1; + uint32_t ssi2_sleep : 1; + uint32_t ssi3_sleep : 1; + uint32_t reserved : 28; +} __attribute__((packed, aligned(4))) sysctl_spi_sleep_t; + +/** + * @brief Reset source status + * + * No. 24 Register (0x60) + */ +typedef struct _sysctl_reset_status +{ + uint32_t reset_sts_clr : 1; + uint32_t pin_reset_sts : 1; + uint32_t wdt0_reset_sts : 1; + uint32_t wdt1_reset_sts : 1; + uint32_t soft_reset_sts : 1; + uint32_t reserved : 27; +} __attribute__((packed, aligned(4))) sysctl_reset_status_t; + +/** + * @brief DMA handshake selector + * + * No. 25 Register (0x64) + */ +typedef struct _sysctl_dma_sel0 +{ + uint32_t dma_sel0 : 6; + uint32_t dma_sel1 : 6; + uint32_t dma_sel2 : 6; + uint32_t dma_sel3 : 6; + uint32_t dma_sel4 : 6; + uint32_t reserved : 2; +} __attribute__((packed, aligned(4))) sysctl_dma_sel0_t; + +/** + * @brief DMA handshake selector + * + * No. 26 Register (0x68) + */ +typedef struct _sysctl_dma_sel1 +{ + uint32_t dma_sel5 : 6; + uint32_t reserved : 26; +} __attribute__((packed, aligned(4))) sysctl_dma_sel1_t; + +/** + * @brief IO Power Mode Select controller + * + * No. 27 Register (0x6c) + */ +typedef struct _sysctl_power_sel +{ + uint32_t power_mode_sel0 : 1; + uint32_t power_mode_sel1 : 1; + uint32_t power_mode_sel2 : 1; + uint32_t power_mode_sel3 : 1; + uint32_t power_mode_sel4 : 1; + uint32_t power_mode_sel5 : 1; + uint32_t power_mode_sel6 : 1; + uint32_t power_mode_sel7 : 1; + uint32_t reserved : 24; +} __attribute__((packed, aligned(4))) sysctl_power_sel_t; + +/** + * @brief System controller register + * + * @note System controller register table + * + * | Offset | Name | Description | + * |-----------|----------------|-------------------------------------| + * | 0x00 | git_id | Git short commit id | + * | 0x04 | clk_freq | System clock base frequency | + * | 0x08 | pll0 | PLL0 controller | + * | 0x0c | pll1 | PLL1 controller | + * | 0x10 | pll2 | PLL2 controller | + * | 0x14 | resv5 | Reserved | + * | 0x18 | pll_lock | PLL lock tester | + * | 0x1c | rom_error | AXI ROM detector | + * | 0x20 | clk_sel0 | Clock select controller0 | + * | 0x24 | clk_sel1 | Clock select controller1 | + * | 0x28 | clk_en_cent | Central clock enable | + * | 0x2c | clk_en_peri | Peripheral clock enable | + * | 0x30 | soft_reset | Soft reset ctrl | + * | 0x34 | peri_reset | Peripheral reset controller | + * | 0x38 | clk_th0 | Clock threshold controller 0 | + * | 0x3c | clk_th1 | Clock threshold controller 1 | + * | 0x40 | clk_th2 | Clock threshold controller 2 | + * | 0x44 | clk_th3 | Clock threshold controller 3 | + * | 0x48 | clk_th4 | Clock threshold controller 4 | + * | 0x4c | clk_th5 | Clock threshold controller 5 | + * | 0x50 | clk_th6 | Clock threshold controller 6 | + * | 0x54 | misc | Miscellaneous controller | + * | 0x58 | peri | Peripheral controller | + * | 0x5c | spi_sleep | SPI sleep controller | + * | 0x60 | reset_status | Reset source status | + * | 0x64 | dma_sel0 | DMA handshake selector | + * | 0x68 | dma_sel1 | DMA handshake selector | + * | 0x6c | power_sel | IO Power Mode Select controller | + * | 0x70 | resv28 | Reserved | + * | 0x74 | resv29 | Reserved | + * | 0x78 | resv30 | Reserved | + * | 0x7c | resv31 | Reserved | + * + */ + +/** + * @brief System controller object + * + * The System controller is a peripheral device mapped in the + * internal memory map, discoverable in the Configuration String. + * It is responsible for low-level configuration of all system + * related peripheral device. It contain PLL controller, clock + * controller, reset controller, DMA handshake controller, SPI + * controller, timer controller, WDT controller and sleep + * controller. + */ +typedef struct _sysctl +{ + /* No. 0 (0x00): Git short commit id */ + sysctl_git_id_t git_id; + /* No. 1 (0x04): System clock base frequency */ + sysctl_clk_freq_t clk_freq; + /* No. 2 (0x08): PLL0 controller */ + sysctl_pll0_t pll0; + /* No. 3 (0x0c): PLL1 controller */ + sysctl_pll1_t pll1; + /* No. 4 (0x10): PLL2 controller */ + sysctl_pll2_t pll2; + /* No. 5 (0x14): Reserved */ + uint32_t resv5; + /* No. 6 (0x18): PLL lock tester */ + sysctl_pll_lock_t pll_lock; + /* No. 7 (0x1c): AXI ROM detector */ + sysctl_rom_error_t rom_error; + /* No. 8 (0x20): Clock select controller0 */ + sysctl_clk_sel0_t clk_sel0; + /* No. 9 (0x24): Clock select controller1 */ + sysctl_clk_sel1_t clk_sel1; + /* No. 10 (0x28): Central clock enable */ + sysctl_clk_en_cent_t clk_en_cent; + /* No. 11 (0x2c): Peripheral clock enable */ + sysctl_clk_en_peri_t clk_en_peri; + /* No. 12 (0x30): Soft reset ctrl */ + sysctl_soft_reset_t soft_reset; + /* No. 13 (0x34): Peripheral reset controller */ + sysctl_peri_reset_t peri_reset; + /* No. 14 (0x38): Clock threshold controller 0 */ + sysctl_clk_th0_t clk_th0; + /* No. 15 (0x3c): Clock threshold controller 1 */ + sysctl_clk_th1_t clk_th1; + /* No. 16 (0x40): Clock threshold controller 2 */ + sysctl_clk_th2_t clk_th2; + /* No. 17 (0x44): Clock threshold controller 3 */ + sysctl_clk_th3_t clk_th3; + /* No. 18 (0x48): Clock threshold controller 4 */ + sysctl_clk_th4_t clk_th4; + /* No. 19 (0x4c): Clock threshold controller 5 */ + sysctl_clk_th5_t clk_th5; + /* No. 20 (0x50): Clock threshold controller 6 */ + sysctl_clk_th6_t clk_th6; + /* No. 21 (0x54): Miscellaneous controller */ + sysctl_misc_t misc; + /* No. 22 (0x58): Peripheral controller */ + sysctl_peri_t peri; + /* No. 23 (0x5c): SPI sleep controller */ + sysctl_spi_sleep_t spi_sleep; + /* No. 24 (0x60): Reset source status */ + sysctl_reset_status_t reset_status; + /* No. 25 (0x64): DMA handshake selector */ + sysctl_dma_sel0_t dma_sel0; + /* No. 26 (0x68): DMA handshake selector */ + sysctl_dma_sel1_t dma_sel1; + /* No. 27 (0x6c): IO Power Mode Select controller */ + sysctl_power_sel_t power_sel; + /* No. 28 (0x70): Reserved */ + uint32_t resv28; + /* No. 29 (0x74): Reserved */ + uint32_t resv29; + /* No. 30 (0x78): Reserved */ + uint32_t resv30; + /* No. 31 (0x7c): Reserved */ + uint32_t resv31; +} __attribute__((packed, aligned(4))) sysctl_t; + +/** + * @brief Abstruct PLL struct + */ +typedef struct _sysctl_general_pll +{ + uint32_t clkr : 4; + uint32_t clkf : 6; + uint32_t clkod : 4; + uint32_t bwadj : 6; + uint32_t pll_reset : 1; + uint32_t pll_pwrd : 1; + uint32_t pll_intfb : 1; + uint32_t pll_bypass : 1; + uint32_t pll_test : 1; + uint32_t pll_out_en : 1; + uint32_t pll_ckin_sel : 2; + uint32_t reserved : 4; +} __attribute__((packed, aligned(4))) sysctl_general_pll_t; + +/** + * @brief System controller object instanse + */ +extern volatile sysctl_t *const sysctl; + +/** + * @brief Enable clock for peripheral + * + * @param[in] clock The clock to be enable + * + * @return result + * - 0 Success + * - Other Fail + */ +int sysctl_clock_enable(sysctl_clock_t clock); + +/** + * @brief Sysctl clock set threshold + * + * @param[in] which Which threshold to set + * @param[in] threshold The threshold value + * + * @return result + * - 0 Success + * - Other Fail + */ +int sysctl_clock_set_threshold(sysctl_threshold_t which, int threshold); + +/** + * @brief Sysctl clock get threshold + * + * @param[in] which Which threshold to get + * + * @return The threshold value + * - Other Value of threshold + * - -1 Fail + */ +int sysctl_clock_get_threshold(sysctl_threshold_t which); + +/** + * @brief Sysctl clock set clock select + * + * @param[in] which Which clock select to set + * @param[in] select The clock select value + * + * @return result + * - 0 Success + * - Other Fail + */ +int sysctl_clock_set_clock_select(sysctl_clock_select_t which, int select); + +/** + * @brief Sysctl clock get clock select + * + * @param[in] which Which clock select to get + * + * @return The clock select value + * - Other Value of clock select + * - -1 Fail + */ +int sysctl_clock_get_clock_select(sysctl_clock_select_t which); + +/** + * @brief Get PLL frequency + * + * @param[in] pll The PLL id + * + * @return The frequency of PLL + */ +uint32_t sysctl_pll_get_freq(sysctl_pll_t pll); + +/** + * @brief Get base clock frequency by clock id + * + * @param[in] clock The clock id + * + * @return The clock frequency + */ +uint32_t sysctl_clock_get_freq(sysctl_clock_t clock); + +#endif //LAB8_SYSCTL_H diff --git a/lab8/kern/fs/devs/dev.c b/lab8/kern/fs/devs/dev.c new file mode 100644 index 0000000..007d748 --- /dev/null +++ b/lab8/kern/fs/devs/dev.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * dev_open - Called for each open(). + */ +static int +dev_open(struct inode *node, uint32_t open_flags) { + if (open_flags & (O_CREAT | O_TRUNC | O_EXCL | O_APPEND)) { + return -E_INVAL; + } + struct device *dev = vop_info(node, device); + return dop_open(dev, open_flags); +} + +/* + * dev_close - Called on the last close(). Just pass through. + */ +static int +dev_close(struct inode *node) { + struct device *dev = vop_info(node, device); + return dop_close(dev); +} + +/* + * dev_read -Called for read. Hand off to iobuf. + */ +static int +dev_read(struct inode *node, struct iobuf *iob) { + struct device *dev = vop_info(node, device); + return dop_io(dev, iob, 0); +} + +/* + * dev_write -Called for write. Hand off to iobuf. + */ +static int +dev_write(struct inode *node, struct iobuf *iob) { + struct device *dev = vop_info(node, device); + return dop_io(dev, iob, 1); +} + +/* + * dev_ioctl - Called for ioctl(). Just pass through. + */ +static int +dev_ioctl(struct inode *node, int op, void *data) { + struct device *dev = vop_info(node, device); + return dop_ioctl(dev, op, data); +} + +/* + * dev_fstat - Called for stat(). + * Set the type and the size (block devices only). + * The link count for a device is always 1. + */ +static int +dev_fstat(struct inode *node, struct stat *stat) { + int ret; + memset(stat, 0, sizeof(struct stat)); + if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) { + return ret; + } + struct device *dev = vop_info(node, device); + stat->st_nlinks = 1; + stat->st_blocks = dev->d_blocks; + stat->st_size = stat->st_blocks * dev->d_blocksize; + return 0; +} + +/* + * dev_gettype - Return the type. A device is a "block device" if it has a known + * length. A device that generates data in a stream is a "character + * device". + */ +static int +dev_gettype(struct inode *node, uint32_t *type_store) { + struct device *dev = vop_info(node, device); + *type_store = (dev->d_blocks > 0) ? S_IFBLK : S_IFCHR; + return 0; +} + +/* + * dev_tryseek - Attempt a seek. + * For block devices, require block alignment. + * For character devices, prohibit seeking entirely. + */ +static int +dev_tryseek(struct inode *node, off_t pos) { + struct device *dev = vop_info(node, device); + if (dev->d_blocks > 0) { + if ((pos % dev->d_blocksize) == 0) { + if (pos >= 0 && pos < dev->d_blocks * dev->d_blocksize) { + return 0; + } + } + } + return -E_INVAL; +} + +/* + * dev_lookup - Name lookup. + * + * One interesting feature of device:name pathname syntax is that you + * can implement pathnames on arbitrary devices. For instance, if you + * had a graphics device that supported multiple resolutions (which we + * don't), you might arrange things so that you could open it with + * pathnames like "video:800x600/24bpp" in order to select the operating + * mode. + * + * However, we have no support for this in the base system. + */ +static int +dev_lookup(struct inode *node, char *path, struct inode **node_store) { + if (*path != '\0') { + return -E_NOENT; + } + vop_ref_inc(node); + *node_store = node; + return 0; +} + +/* + * Function table for device inodes. + */ +static const struct inode_ops dev_node_ops = { + .vop_magic = VOP_MAGIC, + .vop_open = dev_open, + .vop_close = dev_close, + .vop_read = dev_read, + .vop_write = dev_write, + .vop_fstat = dev_fstat, + .vop_ioctl = dev_ioctl, + .vop_gettype = dev_gettype, + .vop_tryseek = dev_tryseek, + .vop_lookup = dev_lookup, +}; + +#define init_device(x) \ + do { \ + extern void dev_init_##x(void); \ + dev_init_##x(); \ + } while (0) + +/* dev_init - Initialization functions for builtin vfs-level devices. */ +void +dev_init(void) { + // init_device(null); + init_device(stdin); + init_device(stdout); + init_device(disk0); +} +/* dev_create_inode - Create inode for a vfs-level device. */ +struct inode * +dev_create_inode(void) { + struct inode *node; + if ((node = alloc_inode(device)) != NULL) { + vop_init(node, &dev_node_ops, NULL); + } + return node; +} + diff --git a/lab8/kern/fs/devs/dev.h b/lab8/kern/fs/devs/dev.h new file mode 100644 index 0000000..9c605d7 --- /dev/null +++ b/lab8/kern/fs/devs/dev.h @@ -0,0 +1,31 @@ +#ifndef __KERN_FS_DEVS_DEV_H__ +#define __KERN_FS_DEVS_DEV_H__ + +#include + +struct inode; +struct iobuf; + +/* + * Filesystem-namespace-accessible device. + * d_io is for both reads and writes; the iobuf will indicates the direction. + */ +struct device { + size_t d_blocks; + size_t d_blocksize; + int (*d_open)(struct device *dev, uint32_t open_flags); + int (*d_close)(struct device *dev); + int (*d_io)(struct device *dev, struct iobuf *iob, bool write); + int (*d_ioctl)(struct device *dev, int op, void *data); +}; + +#define dop_open(dev, open_flags) ((dev)->d_open(dev, open_flags)) +#define dop_close(dev) ((dev)->d_close(dev)) +#define dop_io(dev, iob, write) ((dev)->d_io(dev, iob, write)) +#define dop_ioctl(dev, op, data) ((dev)->d_ioctl(dev, op, data)) + +void dev_init(void); +struct inode *dev_create_inode(void); + +#endif /* !__KERN_FS_DEVS_DEV_H__ */ + diff --git a/lab8/kern/fs/devs/dev_disk0.c b/lab8/kern/fs/devs/dev_disk0.c new file mode 100644 index 0000000..3d6fc1d --- /dev/null +++ b/lab8/kern/fs/devs/dev_disk0.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISK0_BLKSIZE PGSIZE +#define DISK0_BUFSIZE (4 * DISK0_BLKSIZE) +#define DISK0_BLK_NSECT (DISK0_BLKSIZE / SECTSIZE) + +static char *disk0_buffer; +static semaphore_t disk0_sem; + +static void +lock_disk0(void) { + down(&(disk0_sem)); +} + +static void +unlock_disk0(void) { + up(&(disk0_sem)); +} + +static int +disk0_open(struct device *dev, uint32_t open_flags) { + return 0; +} + +static int +disk0_close(struct device *dev) { + return 0; +} + +static void +disk0_read_blks_nolock(uint32_t blkno, uint32_t nblks) { + int ret; + uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT; + if ((ret = ide_read_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) { + panic("disk0: read blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n", + blkno, sectno, nblks, nsecs, ret); + } +} + +static void +disk0_write_blks_nolock(uint32_t blkno, uint32_t nblks) { + int ret; + uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT; + if ((ret = ide_write_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) { + panic("disk0: write blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n", + blkno, sectno, nblks, nsecs, ret); + } +} + +static int +disk0_io(struct device *dev, struct iobuf *iob, bool write) { + off_t offset = iob->io_offset; + size_t resid = iob->io_resid; + uint32_t blkno = offset / DISK0_BLKSIZE; + uint32_t nblks = resid / DISK0_BLKSIZE; + + /* don't allow I/O that isn't block-aligned */ + if ((offset % DISK0_BLKSIZE) != 0 || (resid % DISK0_BLKSIZE) != 0) { + return -E_INVAL; + } + + /* don't allow I/O past the end of disk0 */ + if (blkno + nblks > dev->d_blocks) { + return -E_INVAL; + } + + /* read/write nothing ? */ + if (nblks == 0) { + return 0; + } + + lock_disk0(); + while (resid != 0) { + size_t copied, alen = DISK0_BUFSIZE; + if (write) { + iobuf_move(iob, disk0_buffer, alen, 0, &copied); + assert(copied != 0 && copied <= resid && copied % DISK0_BLKSIZE == 0); + nblks = copied / DISK0_BLKSIZE; + disk0_write_blks_nolock(blkno, nblks); + } + else { + if (alen > resid) { + alen = resid; + } + nblks = alen / DISK0_BLKSIZE; + disk0_read_blks_nolock(blkno, nblks); + iobuf_move(iob, disk0_buffer, alen, 1, &copied); + assert(copied == alen && copied % DISK0_BLKSIZE == 0); + } + resid -= copied, blkno += nblks; + } + unlock_disk0(); + return 0; +} + +static int +disk0_ioctl(struct device *dev, int op, void *data) { + return -E_UNIMP; +} + +static void +disk0_device_init(struct device *dev) { + static_assert(DISK0_BLKSIZE % SECTSIZE == 0); + if (!ide_device_valid(DISK0_DEV_NO)) { + panic("disk0 device isn't available.\n"); + } + dev->d_blocks = ide_device_size(DISK0_DEV_NO) / DISK0_BLK_NSECT; + dev->d_blocksize = DISK0_BLKSIZE; + dev->d_open = disk0_open; + dev->d_close = disk0_close; + dev->d_io = disk0_io; + dev->d_ioctl = disk0_ioctl; + sem_init(&(disk0_sem), 1); + + static_assert(DISK0_BUFSIZE % DISK0_BLKSIZE == 0); + if ((disk0_buffer = kmalloc(DISK0_BUFSIZE)) == NULL) { + panic("disk0 alloc buffer failed.\n"); + } +} + +void +dev_init_disk0(void) { + struct inode *node; + if ((node = dev_create_inode()) == NULL) { + panic("disk0: dev_create_node.\n"); + } + disk0_device_init(vop_info(node, device)); + + int ret; + if ((ret = vfs_add_dev("disk0", node, 1)) != 0) { + panic("disk0: vfs_add_dev: %e.\n", ret); + } +} + diff --git a/lab8/kern/fs/devs/dev_stdin.c b/lab8/kern/fs/devs/dev_stdin.c new file mode 100644 index 0000000..9c4afcb --- /dev/null +++ b/lab8/kern/fs/devs/dev_stdin.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STDIN_BUFSIZE 4096 + +static char stdin_buffer[STDIN_BUFSIZE]; +static off_t p_rpos, p_wpos; +static wait_queue_t __wait_queue, *wait_queue = &__wait_queue; + +void +dev_stdin_write(char c) { + bool intr_flag; + if (c != '\0') { + local_intr_save(intr_flag); + { + stdin_buffer[p_wpos % STDIN_BUFSIZE] = c; + if (p_wpos - p_rpos < STDIN_BUFSIZE) { + p_wpos ++; + } + } + local_intr_restore(intr_flag); + } +} + +static int +dev_stdin_read(char *buf, size_t len) { + int ret = 0; + bool intr_flag; + local_intr_save(intr_flag); + { + for (; ret < len; ret ++, p_rpos ++) { + try_again: + if (p_rpos < p_wpos) { + *buf ++ = stdin_buffer[p_rpos % STDIN_BUFSIZE]; + } + else { + wait_t __wait, *wait = &__wait; + wait_current_set(wait_queue, wait, WT_KBD); + local_intr_restore(intr_flag); + + schedule(); + + local_intr_save(intr_flag); + wait_current_del(wait_queue, wait); + if (wait->wakeup_flags == WT_KBD) { + goto try_again; + } + break; + } + } + } + local_intr_restore(intr_flag); + return ret; +} + +static int +stdin_open(struct device *dev, uint32_t open_flags) { + if (open_flags != O_RDONLY) { + return -E_INVAL; + } + return 0; +} + +static int +stdin_close(struct device *dev) { + return 0; +} + +static int +stdin_io(struct device *dev, struct iobuf *iob, bool write) { + if (!write) { + int ret; + if ((ret = dev_stdin_read(iob->io_base, iob->io_resid)) > 0) { + iob->io_resid -= ret; + } + return ret; + } + return -E_INVAL; +} + +static int +stdin_ioctl(struct device *dev, int op, void *data) { + return -E_INVAL; +} + +void input_wakeup() +{ + if (p_rpos < p_wpos && !wait_queue_empty(wait_queue)) + wakeup_queue(wait_queue, WT_KBD, 1); +} + +static void +stdin_device_init(struct device *dev) { + dev->d_blocks = 0; + dev->d_blocksize = 1; + dev->d_open = stdin_open; + dev->d_close = stdin_close; + dev->d_io = stdin_io; + dev->d_ioctl = stdin_ioctl; + + p_rpos = p_wpos = 0; + wait_queue_init(wait_queue); +} + +void dev_intr() { + volatile uint32_t *hart0m_claim = (volatile uint32_t *)UARTHS_IRQ; + volatile uint32_t *reg = (volatile uint32_t *)UARTHS_DATA_REG; + uint32_t c, irq = *hart0m_claim; + if (irq == 0x21){ + c = *reg; + if (c <= 0xFF) + dev_stdin_write(c); + } + *hart0m_claim = irq; +} + +void +dev_init_stdin(void) { + struct inode *node; + if ((node = dev_create_inode()) == NULL) { + panic("stdin: dev_create_node.\n"); + } + stdin_device_init(vop_info(node, device)); + sbi_register_devintr(dev_intr - (KERNBASE - KERNEL_BEGIN_PADDR)); + + int ret; + if ((ret = vfs_add_dev("stdin", node, 0)) != 0) { + panic("stdin: vfs_add_dev: %e.\n", ret); + } +} + diff --git a/lab8/kern/fs/devs/dev_stdout.c b/lab8/kern/fs/devs/dev_stdout.c new file mode 100644 index 0000000..641acf0 --- /dev/null +++ b/lab8/kern/fs/devs/dev_stdout.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +stdout_open(struct device *dev, uint32_t open_flags) { + if (open_flags != O_WRONLY) { + return -E_INVAL; + } + return 0; +} + +static int +stdout_close(struct device *dev) { + return 0; +} + +static int +stdout_io(struct device *dev, struct iobuf *iob, bool write) { + if (write) { + char *data = iob->io_base; + for (; iob->io_resid != 0; iob->io_resid --) { + cputchar(*data ++); + } + return 0; + } + return -E_INVAL; +} + +static int +stdout_ioctl(struct device *dev, int op, void *data) { + return -E_INVAL; +} + +static void +stdout_device_init(struct device *dev) { + dev->d_blocks = 0; + dev->d_blocksize = 1; + dev->d_open = stdout_open; + dev->d_close = stdout_close; + dev->d_io = stdout_io; + dev->d_ioctl = stdout_ioctl; +} + +void +dev_init_stdout(void) { + struct inode *node; + if ((node = dev_create_inode()) == NULL) { + panic("stdout: dev_create_node.\n"); + } + stdout_device_init(vop_info(node, device)); + + int ret; + if ((ret = vfs_add_dev("stdout", node, 0)) != 0) { + panic("stdout: vfs_add_dev: %e.\n", ret); + } +} + diff --git a/lab8/kern/fs/file.c b/lab8/kern/fs/file.c new file mode 100644 index 0000000..b11486a --- /dev/null +++ b/lab8/kern/fs/file.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define testfd(fd) ((fd) >= 0 && (fd) < FILES_STRUCT_NENTRY) + +// get_fd_array - get current process's open files table +static struct file * +get_fd_array(void) { + struct files_struct *filesp = current->filesp; + assert(filesp != NULL && files_count(filesp) > 0); + return filesp->fd_array; +} + +// fd_array_init - initialize the open files table +void +fd_array_init(struct file *fd_array) { + int fd; + struct file *file = fd_array; + for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) { + file->open_count = 0; + file->status = FD_NONE, file->fd = fd; + } +} + +// fs_array_alloc - allocate a free file item (with FD_NONE status) in open files table +static int +fd_array_alloc(int fd, struct file **file_store) { +// panic("debug"); + struct file *file = get_fd_array(); + if (fd == NO_FD) { + for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) { + if (file->status == FD_NONE) { + goto found; + } + } + return -E_MAX_OPEN; + } + else { + if (testfd(fd)) { + file += fd; + if (file->status == FD_NONE) { + goto found; + } + return -E_BUSY; + } + return -E_INVAL; + } +found: + assert(fopen_count(file) == 0); + file->status = FD_INIT, file->node = NULL; + *file_store = file; + return 0; +} + +// fd_array_free - free a file item in open files table +static void +fd_array_free(struct file *file) { + assert(file->status == FD_INIT || file->status == FD_CLOSED); + assert(fopen_count(file) == 0); + if (file->status == FD_CLOSED) { + vfs_close(file->node); + } + file->status = FD_NONE; +} + +static void +fd_array_acquire(struct file *file) { + assert(file->status == FD_OPENED); + fopen_count_inc(file); +} + +// fd_array_release - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item +static void +fd_array_release(struct file *file) { + assert(file->status == FD_OPENED || file->status == FD_CLOSED); + assert(fopen_count(file) > 0); + if (fopen_count_dec(file) == 0) { + fd_array_free(file); + } +} + +// fd_array_open - file's open_count++, set status to FD_OPENED +void +fd_array_open(struct file *file) { + assert(file->status == FD_INIT && file->node != NULL); + file->status = FD_OPENED; + fopen_count_inc(file); +} + +// fd_array_close - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item +void +fd_array_close(struct file *file) { + assert(file->status == FD_OPENED); + assert(fopen_count(file) > 0); + file->status = FD_CLOSED; + if (fopen_count_dec(file) == 0) { + fd_array_free(file); + } +} + +//fs_array_dup - duplicate file 'from' to file 'to' +void +fd_array_dup(struct file *to, struct file *from) { + //cprintf("[fd_array_dup]from fd=%d, to fd=%d\n",from->fd, to->fd); + assert(to->status == FD_INIT && from->status == FD_OPENED); + to->pos = from->pos; + to->readable = from->readable; + to->writable = from->writable; + struct inode *node = from->node; + vop_ref_inc(node), vop_open_inc(node); + to->node = node; + fd_array_open(to); +} + +// fd2file - use fd as index of fd_array, return the array item (file) +static inline int +fd2file(int fd, struct file **file_store) { + if (testfd(fd)) { + struct file *file = get_fd_array() + fd; + if (file->status == FD_OPENED && file->fd == fd) { + *file_store = file; + return 0; + } + } + return -E_INVAL; +} + +// file_testfd - test file is readble or writable? +bool +file_testfd(int fd, bool readable, bool writable) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return 0; + } + if (readable && !file->readable) { + return 0; + } + if (writable && !file->writable) { + return 0; + } + return 1; +} + +// open file +int +file_open(char *path, uint32_t open_flags) { + bool readable = 0, writable = 0; + switch (open_flags & O_ACCMODE) { + case O_RDONLY: readable = 1; break; + case O_WRONLY: writable = 1; break; + case O_RDWR: + readable = writable = 1; + break; + default: + return -E_INVAL; + } + int ret; + struct file *file; + if ((ret = fd_array_alloc(NO_FD, &file)) != 0) { + return ret; + } + struct inode *node; + if ((ret = vfs_open(path, open_flags, &node)) != 0) { + fd_array_free(file); + return ret; + } + file->pos = 0; + if (open_flags & O_APPEND) { + struct stat __stat, *stat = &__stat; + if ((ret = vop_fstat(node, stat)) != 0) { + vfs_close(node); + fd_array_free(file); + return ret; + } + file->pos = stat->st_size; + } + file->node = node; + file->readable = readable; + file->writable = writable; + fd_array_open(file); + return file->fd; +} + +// close file +int +file_close(int fd) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_close(file); + return 0; +} + +// read file +int +file_read(int fd, void *base, size_t len, size_t *copied_store) { + int ret; + struct file *file; + *copied_store = 0; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + if (!file->readable) { + return -E_INVAL; + } + fd_array_acquire(file); + + struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos); + ret = vop_read(file->node, iob); + + size_t copied = iobuf_used(iob); + if (file->status == FD_OPENED) { + file->pos += copied; + } + *copied_store = copied; + fd_array_release(file); + return ret; +} + +// write file +int +file_write(int fd, void *base, size_t len, size_t *copied_store) { + int ret; + struct file *file; + *copied_store = 0; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + if (!file->writable) { + return -E_INVAL; + } + fd_array_acquire(file); + + struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos); + ret = vop_write(file->node, iob); + + size_t copied = iobuf_used(iob); + if (file->status == FD_OPENED) { + file->pos += copied; + } + *copied_store = copied; + fd_array_release(file); + return ret; +} + +// seek file +int +file_seek(int fd, off_t pos, int whence) { + struct stat __stat, *stat = &__stat; + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + + switch (whence) { + case LSEEK_SET: break; + case LSEEK_CUR: pos += file->pos; break; + case LSEEK_END: + if ((ret = vop_fstat(file->node, stat)) == 0) { + pos += stat->st_size; + } + break; + default: ret = -E_INVAL; + } + + if (ret == 0) { + if ((ret = vop_tryseek(file->node, pos)) == 0) { + file->pos = pos; + } +// cprintf("file_seek, pos=%d, whence=%d, ret=%d\n", pos, whence, ret); + } + fd_array_release(file); + return ret; +} + +// stat file +int +file_fstat(int fd, struct stat *stat) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + ret = vop_fstat(file->node, stat); + fd_array_release(file); + return ret; +} + +// sync file +int +file_fsync(int fd) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + ret = vop_fsync(file->node); + fd_array_release(file); + return ret; +} + +// get file entry in DIR +int +file_getdirentry(int fd, struct dirent *direntp) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + + struct iobuf __iob, *iob = iobuf_init(&__iob, direntp->name, sizeof(direntp->name), direntp->offset); + if ((ret = vop_getdirentry(file->node, iob)) == 0) { + direntp->offset += iobuf_used(iob); + } + fd_array_release(file); + return ret; +} + +// duplicate file +int +file_dup(int fd1, int fd2) { + int ret; + struct file *file1, *file2; + if ((ret = fd2file(fd1, &file1)) != 0) { + return ret; + } + if ((ret = fd_array_alloc(fd2, &file2)) != 0) { + return ret; + } + fd_array_dup(file2, file1); + return file2->fd; +} + + diff --git a/lab8/kern/fs/file.h b/lab8/kern/fs/file.h new file mode 100644 index 0000000..46d523c --- /dev/null +++ b/lab8/kern/fs/file.h @@ -0,0 +1,62 @@ +#ifndef __KERN_FS_FILE_H__ +#define __KERN_FS_FILE_H__ + +//#include +#include +#include +#include +#include + +struct inode; +struct stat; +struct dirent; + +struct file { + enum { + FD_NONE, FD_INIT, FD_OPENED, FD_CLOSED, + } status; + bool readable; + bool writable; + int fd; + off_t pos; + struct inode *node; + int open_count; +}; + +void fd_array_init(struct file *fd_array); +void fd_array_open(struct file *file); +void fd_array_close(struct file *file); +void fd_array_dup(struct file *to, struct file *from); +bool file_testfd(int fd, bool readable, bool writable); + +int file_open(char *path, uint32_t open_flags); +int file_close(int fd); +int file_read(int fd, void *base, size_t len, size_t *copied_store); +int file_write(int fd, void *base, size_t len, size_t *copied_store); +int file_seek(int fd, off_t pos, int whence); +int file_fstat(int fd, struct stat *stat); +int file_fsync(int fd); +int file_getdirentry(int fd, struct dirent *dirent); +int file_dup(int fd1, int fd2); +int file_pipe(int fd[]); +int file_mkfifo(const char *name, uint32_t open_flags); + +static inline int +fopen_count(struct file *file) { + return file->open_count; +} + +static inline int +fopen_count_inc(struct file *file) { + file->open_count += 1; + return file->open_count; +} + +static inline int +fopen_count_dec(struct file *file) { + file->open_count -= 1; + return file->open_count; +} + +#endif /* !__KERN_FS_FILE_H__ */ + diff --git a/lab8/kern/fs/fs.c b/lab8/kern/fs/fs.c new file mode 100644 index 0000000..6e6db6f --- /dev/null +++ b/lab8/kern/fs/fs.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +//called when init_main proc start +void +fs_init(void) { + vfs_init(); + dev_init(); + sfs_init(); +} + +void +fs_cleanup(void) { + vfs_cleanup(); +} + +void +lock_files(struct files_struct *filesp) { + down(&(filesp->files_sem)); +} + +void +unlock_files(struct files_struct *filesp) { + up(&(filesp->files_sem)); +} +//Called when a new proc init +struct files_struct * +files_create(void) { + //cprintf("[files_create]\n"); + //static_assert((int)FILES_STRUCT_NENTRY > 128); + struct files_struct *filesp; + if ((filesp = kmalloc(sizeof(struct files_struct) + FILES_STRUCT_BUFSIZE)) != NULL) { + filesp->pwd = NULL; + filesp->fd_array = (void *)(filesp + 1); + filesp->files_count = 0; + sem_init(&(filesp->files_sem), 1); + fd_array_init(filesp->fd_array); + } + return filesp; +} +//Called when a proc exit +void +files_destroy(struct files_struct *filesp) { +// cprintf("[files_destroy]\n"); + assert(filesp != NULL && files_count(filesp) == 0); + if (filesp->pwd != NULL) { + vop_ref_dec(filesp->pwd); + } + int i; + struct file *file = filesp->fd_array; + for (i = 0; i < FILES_STRUCT_NENTRY; i ++, file ++) { + if (file->status == FD_OPENED) { + fd_array_close(file); + } + assert(file->status == FD_NONE); + } + kfree(filesp); +} + +void +files_closeall(struct files_struct *filesp) { +// cprintf("[files_closeall]\n"); + assert(filesp != NULL && files_count(filesp) > 0); + int i; + struct file *file = filesp->fd_array; + //skip the stdin & stdout + for (i = 2, file += 2; i < FILES_STRUCT_NENTRY; i ++, file ++) { + if (file->status == FD_OPENED) { + fd_array_close(file); + } + } +} + +int +dup_files(struct files_struct *to, struct files_struct *from) { +// cprintf("[dup_fs]\n"); + assert(to != NULL && from != NULL); + assert(files_count(to) == 0 && files_count(from) > 0); + if ((to->pwd = from->pwd) != NULL) { + vop_ref_inc(to->pwd); + } + int i; + struct file *to_file = to->fd_array, *from_file = from->fd_array; + for (i = 0; i < FILES_STRUCT_NENTRY; i ++, to_file ++, from_file ++) { + if (from_file->status == FD_OPENED) { + /* alloc_fd first */ + to_file->status = FD_INIT; + fd_array_dup(to_file, from_file); + } + } + return 0; +} + diff --git a/lab8/kern/fs/fs.h b/lab8/kern/fs/fs.h new file mode 100644 index 0000000..4301c3a --- /dev/null +++ b/lab8/kern/fs/fs.h @@ -0,0 +1,61 @@ +#ifndef __KERN_FS_FS_H__ +#define __KERN_FS_FS_H__ + +#include +#include +#include +#include + +#define SECTSIZE 512 +#define PAGE_NSECT (PGSIZE / SECTSIZE) + +#define SWAP_DEV_NO 1 +#define DISK0_DEV_NO 2 +#define DISK1_DEV_NO 3 + +void fs_init(void); +void fs_cleanup(void); + +struct inode; +struct file; + +/* + * process's file related informaction + */ +struct files_struct { + struct inode *pwd; // inode of present working directory + struct file *fd_array; // opened files array + int files_count; // the number of opened files + semaphore_t files_sem; // lock protect sem +}; + +#define FILES_STRUCT_BUFSIZE (PGSIZE - sizeof(struct files_struct)) +#define FILES_STRUCT_NENTRY (FILES_STRUCT_BUFSIZE / sizeof(struct file)) + +void lock_files(struct files_struct *filesp); +void unlock_files(struct files_struct *filesp); + +struct files_struct *files_create(void); +void files_destroy(struct files_struct *filesp); +void files_closeall(struct files_struct *filesp); +int dup_files(struct files_struct *to, struct files_struct *from); + +static inline int +files_count(struct files_struct *filesp) { + return filesp->files_count; +} + +static inline int +files_count_inc(struct files_struct *filesp) { + filesp->files_count += 1; + return filesp->files_count; +} + +static inline int +files_count_dec(struct files_struct *filesp) { + filesp->files_count -= 1; + return filesp->files_count; +} + +#endif /* !__KERN_FS_FS_H__ */ + diff --git a/lab8/kern/fs/iobuf.c b/lab8/kern/fs/iobuf.c new file mode 100644 index 0000000..2d13b2e --- /dev/null +++ b/lab8/kern/fs/iobuf.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +/* + * iobuf_init - init io buffer struct. + * set up io_base to point to the buffer you want to transfer to, and set io_len to the length of buffer; + * initialize io_offset as desired; + * initialize io_resid to the total amount of data that can be transferred through this io. + */ +struct iobuf * +iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset) { + iob->io_base = base; + iob->io_offset = offset; + iob->io_len = iob->io_resid = len; + return iob; +} + +/* iobuf_move - move data (iob->io_base ---> data OR data --> iob->io.base) in memory + * @copiedp: the size of data memcopied + * + * iobuf_move may be called repeatedly on the same io to transfer + * additional data until the available buffer space the io refers to + * is exhausted. + */ +int +iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp) { + size_t alen; + if ((alen = iob->io_resid) > len) { + alen = len; + } + if (alen > 0) { + void *src = iob->io_base, *dst = data; + if (m2b) { + void *tmp = src; + src = dst, dst = tmp; + } + memmove(dst, src, alen); + iobuf_skip(iob, alen), len -= alen; + } + if (copiedp != NULL) { + *copiedp = alen; + } + return (len == 0) ? 0 : -E_NO_MEM; +} + +/* + * iobuf_move_zeros - set io buffer zero + * @copiedp: the size of data memcopied + */ +int +iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp) { + size_t alen; + if ((alen = iob->io_resid) > len) { + alen = len; + } + if (alen > 0) { + memset(iob->io_base, 0, alen); + iobuf_skip(iob, alen), len -= alen; + } + if (copiedp != NULL) { + *copiedp = alen; + } + return (len == 0) ? 0 : -E_NO_MEM; +} + +/* + * iobuf_skip - change the current position of io buffer + */ +void +iobuf_skip(struct iobuf *iob, size_t n) { + assert(iob->io_resid >= n); + iob->io_base += n, iob->io_offset += n, iob->io_resid -= n; +} + diff --git a/lab8/kern/fs/iobuf.h b/lab8/kern/fs/iobuf.h new file mode 100644 index 0000000..8bb668a --- /dev/null +++ b/lab8/kern/fs/iobuf.h @@ -0,0 +1,24 @@ +#ifndef __KERN_FS_IOBUF_H__ +#define __KERN_FS_IOBUF_H__ + +#include + +/* + * iobuf is a buffer Rd/Wr status record + */ +struct iobuf { + void *io_base; // the base addr of buffer (used for Rd/Wr) + off_t io_offset; // current Rd/Wr position in buffer, will have been incremented by the amount transferred + size_t io_len; // the length of buffer (used for Rd/Wr) + size_t io_resid; // current resident length need to Rd/Wr, will have been decremented by the amount transferred. +}; + +#define iobuf_used(iob) ((size_t)((iob)->io_len - (iob)->io_resid)) + +struct iobuf *iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset); +int iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp); +int iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp); +void iobuf_skip(struct iobuf *iob, size_t n); + +#endif /* !__KERN_FS_IOBUF_H__ */ + diff --git a/lab8/kern/fs/sfs/bitmap.c b/lab8/kern/fs/sfs/bitmap.c new file mode 100644 index 0000000..c2d1c8d --- /dev/null +++ b/lab8/kern/fs/sfs/bitmap.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +#define WORD_TYPE uint32_t +#define WORD_BITS (sizeof(WORD_TYPE) * CHAR_BIT) + +struct bitmap { + uint32_t nbits; + uint32_t nwords; + WORD_TYPE *map; +}; + +// bitmap_create - allocate a new bitmap object. +struct bitmap * +bitmap_create(uint32_t nbits) { + static_assert(WORD_BITS != 0); + assert(nbits != 0 && nbits + WORD_BITS > nbits); + + struct bitmap *bitmap; + if ((bitmap = kmalloc(sizeof(struct bitmap))) == NULL) { + return NULL; + } + + uint32_t nwords = ROUNDUP_DIV(nbits, WORD_BITS); + WORD_TYPE *map; + if ((map = kmalloc(sizeof(WORD_TYPE) * nwords)) == NULL) { + kfree(bitmap); + return NULL; + } + + bitmap->nbits = nbits, bitmap->nwords = nwords; + bitmap->map = memset(map, 0xFF, sizeof(WORD_TYPE) * nwords); + + /* mark any leftover bits at the end in use(0) */ + if (nbits != nwords * WORD_BITS) { + uint32_t ix = nwords - 1, overbits = nbits - ix * WORD_BITS; + + assert(nbits / WORD_BITS == ix); + assert(overbits > 0 && overbits < WORD_BITS); + + for (; overbits < WORD_BITS; overbits ++) { + bitmap->map[ix] ^= (1 << overbits); + } + } + return bitmap; +} + +// bitmap_alloc - locate a cleared bit, set it, and return its index. +int +bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store) { + WORD_TYPE *map = bitmap->map; + uint32_t ix, offset, nwords = bitmap->nwords; + for (ix = 0; ix < nwords; ix ++) { + if (map[ix] != 0) { + for (offset = 0; offset < WORD_BITS; offset ++) { + WORD_TYPE mask = (1 << offset); + if (map[ix] & mask) { + map[ix] ^= mask; + *index_store = ix * WORD_BITS + offset; + return 0; + } + } + assert(0); + } + } + return -E_NO_MEM; +} + +// bitmap_translate - according index, get the related word and mask +static void +bitmap_translate(struct bitmap *bitmap, uint32_t index, WORD_TYPE **word, WORD_TYPE *mask) { + assert(index < bitmap->nbits); + uint32_t ix = index / WORD_BITS, offset = index % WORD_BITS; + *word = bitmap->map + ix; + *mask = (1 << offset); +} + +// bitmap_test - according index, get the related value (0 OR 1) in the bitmap +bool +bitmap_test(struct bitmap *bitmap, uint32_t index) { + WORD_TYPE *word, mask; + bitmap_translate(bitmap, index, &word, &mask); + return (*word & mask); +} + +// bitmap_free - according index, set related bit to 1 +void +bitmap_free(struct bitmap *bitmap, uint32_t index) { + WORD_TYPE *word, mask; + bitmap_translate(bitmap, index, &word, &mask); + assert(!(*word & mask)); + *word |= mask; +} + +// bitmap_destroy - free memory contains bitmap +void +bitmap_destroy(struct bitmap *bitmap) { + kfree(bitmap->map); + kfree(bitmap); +} + +// bitmap_getdata - return bitmap->map, return the length of bits to len_store +void * +bitmap_getdata(struct bitmap *bitmap, size_t *len_store) { + if (len_store != NULL) { + *len_store = sizeof(WORD_TYPE) * bitmap->nwords; + } + return bitmap->map; +} + diff --git a/lab8/kern/fs/sfs/bitmap.h b/lab8/kern/fs/sfs/bitmap.h new file mode 100644 index 0000000..4a3a8eb --- /dev/null +++ b/lab8/kern/fs/sfs/bitmap.h @@ -0,0 +1,32 @@ +#ifndef __KERN_FS_SFS_BITMAP_H__ +#define __KERN_FS_SFS_BITMAP_H__ + +#include + + +/* + * Fixed-size array of bits. (Intended for storage management.) + * + * Functions: + * bitmap_create - allocate a new bitmap object. + * Returns NULL on error. + * bitmap_getdata - return pointer to raw bit data (for I/O). + * bitmap_alloc - locate a cleared bit, set it, and return its index. + * bitmap_mark - set a clear bit by its index. + * bitmap_unmark - clear a set bit by its index. + * bitmap_isset - return whether a particular bit is set or not. + * bitmap_destroy - destroy bitmap. + */ + + +struct bitmap; + +struct bitmap *bitmap_create(uint32_t nbits); // allocate a new bitmap object. +int bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store); // locate a cleared bit, set it, and return its index. +bool bitmap_test(struct bitmap *bitmap, uint32_t index); // return whether a particular bit is set or not. +void bitmap_free(struct bitmap *bitmap, uint32_t index); // according index, set related bit to 1 +void bitmap_destroy(struct bitmap *bitmap); // free memory contains bitmap +void *bitmap_getdata(struct bitmap *bitmap, size_t *len_store); // return pointer to raw bit data (for I/O) + +#endif /* !__KERN_FS_SFS_BITMAP_H__ */ + diff --git a/lab8/kern/fs/sfs/sfs.c b/lab8/kern/fs/sfs/sfs.c new file mode 100644 index 0000000..c1d7d44 --- /dev/null +++ b/lab8/kern/fs/sfs/sfs.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +/* + * sfs_init - mount sfs on disk0 + * + * CALL GRAPH: + * kern_init-->fs_init-->sfs_init + */ +void +sfs_init(void) { + int ret; + if ((ret = sfs_mount("disk0")) != 0) { + panic("failed: sfs: sfs_mount: %e.\n", ret); + } +} + diff --git a/lab8/kern/fs/sfs/sfs.h b/lab8/kern/fs/sfs/sfs.h new file mode 100644 index 0000000..39c1772 --- /dev/null +++ b/lab8/kern/fs/sfs/sfs.h @@ -0,0 +1,129 @@ +#ifndef __KERN_FS_SFS_SFS_H__ +#define __KERN_FS_SFS_SFS_H__ + +#include +#include +#include +#include +#include + +/* + * Simple FS (SFS) definitions visible to ucore. This covers the on-disk format + * and is used by tools that work on SFS volumes, such as mksfs. + */ + +#define SFS_MAGIC 0x2f8dbe2a /* magic number for sfs */ +#define SFS_BLKSIZE PGSIZE /* size of block */ +#define SFS_NDIRECT 12 /* # of direct blocks in inode */ +#define SFS_MAX_INFO_LEN 31 /* max length of infomation */ +#define SFS_MAX_FNAME_LEN FS_MAX_FNAME_LEN /* max length of filename */ +#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) /* max file size (128M) */ +#define SFS_BLKN_SUPER 0 /* block the superblock lives in */ +#define SFS_BLKN_ROOT 1 /* location of the root dir inode */ +#define SFS_BLKN_FREEMAP 2 /* 1st block of the freemap */ + +/* # of bits in a block */ +#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT) + +/* # of entries in a block */ +#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t)) + +/* file types */ +#define SFS_TYPE_INVAL 0 /* Should not appear on disk */ +#define SFS_TYPE_FILE 1 +#define SFS_TYPE_DIR 2 +#define SFS_TYPE_LINK 3 + +/* + * On-disk superblock + */ +struct sfs_super { + uint32_t magic; /* magic number, should be SFS_MAGIC */ + uint32_t blocks; /* # of blocks in fs */ + uint32_t unused_blocks; /* # of unused blocks in fs */ + char info[SFS_MAX_INFO_LEN + 1]; /* infomation for sfs */ +}; + +/* inode (on disk) */ +struct sfs_disk_inode { + uint32_t size; /* size of the file (in bytes) */ + uint16_t type; /* one of SYS_TYPE_* above */ + uint16_t nlinks; /* # of hard links to this file */ + uint32_t blocks; /* # of blocks */ + uint32_t direct[SFS_NDIRECT]; /* direct blocks */ + uint32_t indirect; /* indirect blocks */ +// uint32_t db_indirect; /* double indirect blocks */ +// unused +}; + +/* file entry (on disk) */ +struct sfs_disk_entry { + uint32_t ino; /* inode number */ + char name[SFS_MAX_FNAME_LEN + 1]; /* file name */ +}; + +#define sfs_dentry_size \ + sizeof(((struct sfs_disk_entry *)0)->name) + +/* inode for sfs */ +struct sfs_inode { + struct sfs_disk_inode *din; /* on-disk inode */ + uint32_t ino; /* inode number */ + bool dirty; /* true if inode modified */ + int reclaim_count; /* kill inode if it hits zero */ + semaphore_t sem; /* semaphore for din */ + list_entry_t inode_link; /* entry for linked-list in sfs_fs */ + list_entry_t hash_link; /* entry for hash linked-list in sfs_fs */ +}; + +#define le2sin(le, member) \ + to_struct((le), struct sfs_inode, member) + +/* filesystem for sfs */ +struct sfs_fs { + struct sfs_super super; /* on-disk superblock */ + struct device *dev; /* device mounted on */ + struct bitmap *freemap; /* blocks in use are mared 0 */ + bool super_dirty; /* true if super/freemap modified */ + void *sfs_buffer; /* buffer for non-block aligned io */ + semaphore_t fs_sem; /* semaphore for fs */ + semaphore_t io_sem; /* semaphore for io */ + semaphore_t mutex_sem; /* semaphore for link/unlink and rename */ + list_entry_t inode_list; /* inode linked-list */ + list_entry_t *hash_list; /* inode hash linked-list */ +}; + +/* hash for sfs */ +#define SFS_HLIST_SHIFT 10 +#define SFS_HLIST_SIZE (1 << SFS_HLIST_SHIFT) +#define sin_hashfn(x) (hash32(x, SFS_HLIST_SHIFT)) + +/* size of freemap (in bits) */ +#define sfs_freemap_bits(super) ROUNDUP((super)->blocks, SFS_BLKBITS) + +/* size of freemap (in blocks) */ +#define sfs_freemap_blocks(super) ROUNDUP_DIV((super)->blocks, SFS_BLKBITS) + +struct fs; +struct inode; + +void sfs_init(void); +int sfs_mount(const char *devname); + +void lock_sfs_fs(struct sfs_fs *sfs); +void lock_sfs_io(struct sfs_fs *sfs); +void unlock_sfs_fs(struct sfs_fs *sfs); +void unlock_sfs_io(struct sfs_fs *sfs); + +int sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks); +int sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks); +int sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset); +int sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset); +int sfs_sync_super(struct sfs_fs *sfs); +int sfs_sync_freemap(struct sfs_fs *sfs); +int sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks); + +int sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino); + +#endif /* !__KERN_FS_SFS_SFS_H__ */ + diff --git a/lab8/kern/fs/sfs/sfs_fs.c b/lab8/kern/fs/sfs/sfs_fs.c new file mode 100644 index 0000000..2d3e3b3 --- /dev/null +++ b/lab8/kern/fs/sfs/sfs_fs.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * sfs_sync - sync sfs's superblock and freemap in memroy into disk + */ +static int +sfs_sync(struct fs *fs) { + struct sfs_fs *sfs = fsop_info(fs, sfs); + lock_sfs_fs(sfs); + { + list_entry_t *list = &(sfs->inode_list), *le = list; + while ((le = list_next(le)) != list) { + struct sfs_inode *sin = le2sin(le, inode_link); + vop_fsync(info2node(sin, sfs_inode)); + } + } + unlock_sfs_fs(sfs); + + int ret; + if (sfs->super_dirty) { + sfs->super_dirty = 0; + if ((ret = sfs_sync_super(sfs)) != 0) { + sfs->super_dirty = 1; + return ret; + } + if ((ret = sfs_sync_freemap(sfs)) != 0) { + sfs->super_dirty = 1; + return ret; + } + } + return 0; +} + +/* + * sfs_get_root - get the root directory inode from disk (SFS_BLKN_ROOT,1) + */ +static struct inode * +sfs_get_root(struct fs *fs) { + struct inode *node; + int ret; + if ((ret = sfs_load_inode(fsop_info(fs, sfs), &node, SFS_BLKN_ROOT)) != 0) { + panic("load sfs root failed: %e", ret); + } + return node; +} + +/* + * sfs_unmount - unmount sfs, and free the memorys contain sfs->freemap/sfs_buffer/hash_liskt and sfs itself. + */ +static int +sfs_unmount(struct fs *fs) { + struct sfs_fs *sfs = fsop_info(fs, sfs); + if (!list_empty(&(sfs->inode_list))) { + return -E_BUSY; + } + assert(!sfs->super_dirty); + bitmap_destroy(sfs->freemap); + kfree(sfs->sfs_buffer); + kfree(sfs->hash_list); + kfree(sfs); + return 0; +} + +/* + * sfs_cleanup - when sfs failed, then should call this function to sync sfs by calling sfs_sync + * + * NOTICE: nouse now. + */ +static void +sfs_cleanup(struct fs *fs) { + struct sfs_fs *sfs = fsop_info(fs, sfs); + uint32_t blocks = sfs->super.blocks, unused_blocks = sfs->super.unused_blocks; + cprintf("sfs: cleanup: '%s' (%d/%d/%d)\n", sfs->super.info, + blocks - unused_blocks, unused_blocks, blocks); + int i, ret; + for (i = 0; i < 32; i ++) { + if ((ret = fsop_sync(fs)) == 0) { + break; + } + } + if (ret != 0) { + warn("sfs: sync error: '%s': %e.\n", sfs->super.info, ret); + } +} + +/* + * sfs_init_read - used in sfs_do_mount to read disk block(blkno, 1) directly. + * + * @dev: the block device + * @blkno: the NO. of disk block + * @blk_buffer: the buffer used for read + * + * (1) init iobuf + * (2) read dev into iobuf + */ +static int +sfs_init_read(struct device *dev, uint32_t blkno, void *blk_buffer) { + struct iobuf __iob, *iob = iobuf_init(&__iob, blk_buffer, SFS_BLKSIZE, blkno * SFS_BLKSIZE); + return dop_io(dev, iob, 0); +} + +/* + * sfs_init_freemap - used in sfs_do_mount to read freemap data info in disk block(blkno, nblks) directly. + * + * @dev: the block device + * @bitmap: the bitmap in memroy + * @blkno: the NO. of disk block + * @nblks: Rd number of disk block + * @blk_buffer: the buffer used for read + * + * (1) get data addr in bitmap + * (2) read dev into iobuf + */ +static int +sfs_init_freemap(struct device *dev, struct bitmap *freemap, uint32_t blkno, uint32_t nblks, void *blk_buffer) { + size_t len; + void *data = bitmap_getdata(freemap, &len); + assert(data != NULL && len == nblks * SFS_BLKSIZE); + while (nblks != 0) { + int ret; + if ((ret = sfs_init_read(dev, blkno, data)) != 0) { + return ret; + } + blkno ++, nblks --, data += SFS_BLKSIZE; + } + return 0; +} + +/* + * sfs_do_mount - mount sfs file system. + * + * @dev: the block device contains sfs file system + * @fs_store: the fs struct in memroy + */ +static int +sfs_do_mount(struct device *dev, struct fs **fs_store) { + static_assert(SFS_BLKSIZE >= sizeof(struct sfs_super)); + static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_inode)); + static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_entry)); + + if (dev->d_blocksize != SFS_BLKSIZE) { + return -E_NA_DEV; + } + + /* allocate fs structure */ + struct fs *fs; + if ((fs = alloc_fs(sfs)) == NULL) { + return -E_NO_MEM; + } + struct sfs_fs *sfs = fsop_info(fs, sfs); + sfs->dev = dev; + + int ret = -E_NO_MEM; + + void *sfs_buffer; + if ((sfs->sfs_buffer = sfs_buffer = kmalloc(SFS_BLKSIZE)) == NULL) { + goto failed_cleanup_fs; + } + + /* load and check superblock */ + if ((ret = sfs_init_read(dev, SFS_BLKN_SUPER, sfs_buffer)) != 0) { + goto failed_cleanup_sfs_buffer; + } + + ret = -E_INVAL; + + struct sfs_super *super = sfs_buffer; + if (super->magic != SFS_MAGIC) { + cprintf("sfs: wrong magic in superblock. (%08x should be %08x).\n", + super->magic, SFS_MAGIC); + goto failed_cleanup_sfs_buffer; + } + if (super->blocks > dev->d_blocks) { + cprintf("sfs: fs has %u blocks, device has %u blocks.\n", + super->blocks, dev->d_blocks); + goto failed_cleanup_sfs_buffer; + } + super->info[SFS_MAX_INFO_LEN] = '\0'; + sfs->super = *super; + + ret = -E_NO_MEM; + + uint32_t i; + + /* alloc and initialize hash list */ + list_entry_t *hash_list; + if ((sfs->hash_list = hash_list = kmalloc(sizeof(list_entry_t) * SFS_HLIST_SIZE)) == NULL) { + goto failed_cleanup_sfs_buffer; + } + for (i = 0; i < SFS_HLIST_SIZE; i ++) { + list_init(hash_list + i); + } + + /* load and check freemap */ + struct bitmap *freemap; + uint32_t freemap_size_nbits = sfs_freemap_bits(super); + if ((sfs->freemap = freemap = bitmap_create(freemap_size_nbits)) == NULL) { + goto failed_cleanup_hash_list; + } + uint32_t freemap_size_nblks = sfs_freemap_blocks(super); + if ((ret = sfs_init_freemap(dev, freemap, SFS_BLKN_FREEMAP, freemap_size_nblks, sfs_buffer)) != 0) { + goto failed_cleanup_freemap; + } + + uint32_t blocks = sfs->super.blocks, unused_blocks = 0; + for (i = 0; i < freemap_size_nbits; i ++) { + if (bitmap_test(freemap, i)) { + unused_blocks ++; + } + } + assert(unused_blocks == sfs->super.unused_blocks); + + /* and other fields */ + sfs->super_dirty = 0; + sem_init(&(sfs->fs_sem), 1); + sem_init(&(sfs->io_sem), 1); + sem_init(&(sfs->mutex_sem), 1); + list_init(&(sfs->inode_list)); + cprintf("sfs: mount: '%s' (%d/%d/%d)\n", sfs->super.info, + blocks - unused_blocks, unused_blocks, blocks); + + /* link addr of sync/get_root/unmount/cleanup funciton fs's function pointers*/ + fs->fs_sync = sfs_sync; + fs->fs_get_root = sfs_get_root; + fs->fs_unmount = sfs_unmount; + fs->fs_cleanup = sfs_cleanup; + *fs_store = fs; + return 0; + +failed_cleanup_freemap: + bitmap_destroy(freemap); +failed_cleanup_hash_list: + kfree(hash_list); +failed_cleanup_sfs_buffer: + kfree(sfs_buffer); +failed_cleanup_fs: + kfree(fs); + return ret; +} + +int +sfs_mount(const char *devname) { + return vfs_mount(devname, sfs_do_mount); +} + diff --git a/lab8/kern/fs/sfs/sfs_inode.c b/lab8/kern/fs/sfs/sfs_inode.c new file mode 100644 index 0000000..100ec0e --- /dev/null +++ b/lab8/kern/fs/sfs/sfs_inode.c @@ -0,0 +1,1032 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct inode_ops sfs_node_dirops; // dir operations +static const struct inode_ops sfs_node_fileops; // file operations + +/* + * lock_sin - lock the process of inode Rd/Wr + */ +static void +lock_sin(struct sfs_inode *sin) { + down(&(sin->sem)); +} + +/* + * unlock_sin - unlock the process of inode Rd/Wr + */ +static void +unlock_sin(struct sfs_inode *sin) { + up(&(sin->sem)); +} + +/* + * sfs_get_ops - return function addr of fs_node_dirops/sfs_node_fileops + */ +static const struct inode_ops * +sfs_get_ops(uint16_t type) { + switch (type) { + case SFS_TYPE_DIR: + return &sfs_node_dirops; + case SFS_TYPE_FILE: + return &sfs_node_fileops; + } + panic("invalid file type %d.\n", type); +} + +/* + * sfs_hash_list - return inode entry in sfs->hash_list + */ +static list_entry_t * +sfs_hash_list(struct sfs_fs *sfs, uint32_t ino) { + return sfs->hash_list + sin_hashfn(ino); +} + +/* + * sfs_set_links - link inode sin in sfs->linked-list AND sfs->hash_link + */ +static void +sfs_set_links(struct sfs_fs *sfs, struct sfs_inode *sin) { + list_add(&(sfs->inode_list), &(sin->inode_link)); + list_add(sfs_hash_list(sfs, sin->ino), &(sin->hash_link)); +} + +/* + * sfs_remove_links - unlink inode sin in sfs->linked-list AND sfs->hash_link + */ +static void +sfs_remove_links(struct sfs_inode *sin) { + list_del(&(sin->inode_link)); + list_del(&(sin->hash_link)); +} + +/* + * sfs_block_inuse - check the inode with NO. ino inuse info in bitmap + */ +static bool +sfs_block_inuse(struct sfs_fs *sfs, uint32_t ino) { + if (ino != 0 && ino < sfs->super.blocks) { + return !bitmap_test(sfs->freemap, ino); + } + panic("sfs_block_inuse: called out of range (0, %u) %u.\n", sfs->super.blocks, ino); +} + +/* + * sfs_block_alloc - check and get a free disk block + */ +static int +sfs_block_alloc(struct sfs_fs *sfs, uint32_t *ino_store) { + int ret; + if ((ret = bitmap_alloc(sfs->freemap, ino_store)) != 0) { + return ret; + } + assert(sfs->super.unused_blocks > 0); + sfs->super.unused_blocks --, sfs->super_dirty = 1; + assert(sfs_block_inuse(sfs, *ino_store)); + return sfs_clear_block(sfs, *ino_store, 1); +} + +/* + * sfs_block_free - set related bits for ino block to 1(means free) in bitmap, add sfs->super.unused_blocks, set superblock dirty * + */ +static void +sfs_block_free(struct sfs_fs *sfs, uint32_t ino) { + assert(sfs_block_inuse(sfs, ino)); + bitmap_free(sfs->freemap, ino); + sfs->super.unused_blocks ++, sfs->super_dirty = 1; +} + +/* + * sfs_create_inode - alloc a inode in memroy, and init din/ino/dirty/reclian_count/sem fields in sfs_inode in inode + */ +static int +sfs_create_inode(struct sfs_fs *sfs, struct sfs_disk_inode *din, uint32_t ino, struct inode **node_store) { + struct inode *node; + if ((node = alloc_inode(sfs_inode)) != NULL) { + vop_init(node, sfs_get_ops(din->type), info2fs(sfs, sfs)); + struct sfs_inode *sin = vop_info(node, sfs_inode); + sin->din = din, sin->ino = ino, sin->dirty = 0, sin->reclaim_count = 1; + sem_init(&(sin->sem), 1); + *node_store = node; + return 0; + } + return -E_NO_MEM; +} + +/* + * lookup_sfs_nolock - according ino, find related inode + * + * NOTICE: le2sin, info2node MACRO + */ +static struct inode * +lookup_sfs_nolock(struct sfs_fs *sfs, uint32_t ino) { + struct inode *node; + list_entry_t *list = sfs_hash_list(sfs, ino), *le = list; + while ((le = list_next(le)) != list) { + struct sfs_inode *sin = le2sin(le, hash_link); + if (sin->ino == ino) { + node = info2node(sin, sfs_inode); + if (vop_ref_inc(node) == 1) { + sin->reclaim_count ++; + } + return node; + } + } + return NULL; +} + +/* + * sfs_load_inode - If the inode isn't existed, load inode related ino disk block data into a new created inode. + * If the inode is in memory alreadily, then do nothing + */ +int +sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino) { + lock_sfs_fs(sfs); + struct inode *node; + if ((node = lookup_sfs_nolock(sfs, ino)) != NULL) { + goto out_unlock; + } + + int ret = -E_NO_MEM; + struct sfs_disk_inode *din; + if ((din = kmalloc(sizeof(struct sfs_disk_inode))) == NULL) { + goto failed_unlock; + } + + assert(sfs_block_inuse(sfs, ino)); + if ((ret = sfs_rbuf(sfs, din, sizeof(struct sfs_disk_inode), ino, 0)) != 0) { + goto failed_cleanup_din; + } + + assert(din->nlinks != 0); + if ((ret = sfs_create_inode(sfs, din, ino, &node)) != 0) { + goto failed_cleanup_din; + } + sfs_set_links(sfs, vop_info(node, sfs_inode)); + +out_unlock: + unlock_sfs_fs(sfs); + *node_store = node; + return 0; + +failed_cleanup_din: + kfree(din); +failed_unlock: + unlock_sfs_fs(sfs); + return ret; +} + +/* + * sfs_bmap_get_sub_nolock - according entry pointer entp and index, find the index of indrect disk block + * return the index of indrect disk block to ino_store. no lock protect + * @sfs: sfs file system + * @entp: the pointer of index of entry disk block + * @index: the index of block in indrect block + * @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing + * @ino_store: 0 OR the index of already inused block or new allocated block. + */ +static int +sfs_bmap_get_sub_nolock(struct sfs_fs *sfs, uint32_t *entp, uint32_t index, bool create, uint32_t *ino_store) { + assert(index < SFS_BLK_NENTRY); + int ret; + uint32_t ent, ino = 0; + off_t offset = index * sizeof(uint32_t); // the offset of entry in entry block + // if entry block is existd, read the content of entry block into sfs->sfs_buffer + if ((ent = *entp) != 0) { + if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) { + return ret; + } + if (ino != 0 || !create) { + goto out; + } + } + else { + if (!create) { + goto out; + } + //if entry block isn't existd, allocated a entry block (for indrect block) + if ((ret = sfs_block_alloc(sfs, &ent)) != 0) { + return ret; + } + } + + if ((ret = sfs_block_alloc(sfs, &ino)) != 0) { + goto failed_cleanup; + } + if ((ret = sfs_wbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) { + sfs_block_free(sfs, ino); + goto failed_cleanup; + } + +out: + if (ent != *entp) { + *entp = ent; + } + *ino_store = ino; + return 0; + +failed_cleanup: + if (ent != *entp) { + sfs_block_free(sfs, ent); + } + return ret; +} + +/* + * sfs_bmap_get_nolock - according sfs_inode and index of block, find the NO. of disk block + * no lock protect + * @sfs: sfs file system + * @sin: sfs inode in memory + * @index: the index of block in inode + * @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing + * @ino_store: 0 OR the index of already inused block or new allocated block. + */ +static int +sfs_bmap_get_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, bool create, uint32_t *ino_store) { + struct sfs_disk_inode *din = sin->din; + int ret; + uint32_t ent, ino; + // the index of disk block is in the fist SFS_NDIRECT direct blocks + if (index < SFS_NDIRECT) { + if ((ino = din->direct[index]) == 0 && create) { + if ((ret = sfs_block_alloc(sfs, &ino)) != 0) { + return ret; + } + din->direct[index] = ino; + sin->dirty = 1; + } + goto out; + } + // the index of disk block is in the indirect blocks. + index -= SFS_NDIRECT; + if (index < SFS_BLK_NENTRY) { + ent = din->indirect; + if ((ret = sfs_bmap_get_sub_nolock(sfs, &ent, index, create, &ino)) != 0) { + return ret; + } + if (ent != din->indirect) { + assert(din->indirect == 0); + din->indirect = ent; + sin->dirty = 1; + } + goto out; + } else { + panic ("sfs_bmap_get_nolock - index out of range"); + } +out: + assert(ino == 0 || sfs_block_inuse(sfs, ino)); + *ino_store = ino; + return 0; +} + +/* + * sfs_bmap_free_sub_nolock - set the entry item to 0 (free) in the indirect block + */ +static int +sfs_bmap_free_sub_nolock(struct sfs_fs *sfs, uint32_t ent, uint32_t index) { + assert(sfs_block_inuse(sfs, ent) && index < SFS_BLK_NENTRY); + int ret; + uint32_t ino, zero = 0; + off_t offset = index * sizeof(uint32_t); + if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) { + return ret; + } + if (ino != 0) { + if ((ret = sfs_wbuf(sfs, &zero, sizeof(uint32_t), ent, offset)) != 0) { + return ret; + } + sfs_block_free(sfs, ino); + } + return 0; +} + +/* + * sfs_bmap_free_nolock - free a block with logical index in inode and reset the inode's fields + */ +static int +sfs_bmap_free_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index) { + struct sfs_disk_inode *din = sin->din; + int ret; + uint32_t ent, ino; + if (index < SFS_NDIRECT) { + if ((ino = din->direct[index]) != 0) { + // free the block + sfs_block_free(sfs, ino); + din->direct[index] = 0; + sin->dirty = 1; + } + return 0; + } + + index -= SFS_NDIRECT; + if (index < SFS_BLK_NENTRY) { + if ((ent = din->indirect) != 0) { + // set the entry item to 0 in the indirect block + if ((ret = sfs_bmap_free_sub_nolock(sfs, ent, index)) != 0) { + return ret; + } + } + return 0; + } + return 0; +} + +/* + * sfs_bmap_load_nolock - according to the DIR's inode and the logical index of block in inode, find the NO. of disk block. + * @sfs: sfs file system + * @sin: sfs inode in memory + * @index: the logical index of disk block in inode + * @ino_store:the NO. of disk block + */ +static int +sfs_bmap_load_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, uint32_t *ino_store) { + struct sfs_disk_inode *din = sin->din; + assert(index <= din->blocks); + int ret; + uint32_t ino; + bool create = (index == din->blocks); + if ((ret = sfs_bmap_get_nolock(sfs, sin, index, create, &ino)) != 0) { + return ret; + } + assert(sfs_block_inuse(sfs, ino)); + if (create) { + din->blocks ++; + } + if (ino_store != NULL) { + *ino_store = ino; + } + return 0; +} + +/* + * sfs_bmap_truncate_nolock - free the disk block at the end of file + */ +static int +sfs_bmap_truncate_nolock(struct sfs_fs *sfs, struct sfs_inode *sin) { + struct sfs_disk_inode *din = sin->din; + assert(din->blocks != 0); + int ret; + if ((ret = sfs_bmap_free_nolock(sfs, sin, din->blocks - 1)) != 0) { + return ret; + } + din->blocks --; + sin->dirty = 1; + return 0; +} + +/* + * sfs_dirent_read_nolock - read the file entry from disk block which contains this entry + * @sfs: sfs file system + * @sin: sfs inode in memory + * @slot: the index of file entry + * @entry: file entry + */ +static int +sfs_dirent_read_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) { + assert(sin->din->type == SFS_TYPE_DIR && (slot >= 0 && slot < sin->din->blocks)); + int ret; + uint32_t ino; + // according to the DIR's inode and the slot of file entry, find the index of disk block which contains this file entry + if ((ret = sfs_bmap_load_nolock(sfs, sin, slot, &ino)) != 0) { + return ret; + } + assert(sfs_block_inuse(sfs, ino)); + // read the content of file entry in the disk block + if ((ret = sfs_rbuf(sfs, entry, sizeof(struct sfs_disk_entry), ino, 0)) != 0) { + return ret; + } + entry->name[SFS_MAX_FNAME_LEN] = '\0'; + return 0; +} + +#define sfs_dirent_link_nolock_check(sfs, sin, slot, lnksin, name) \ + do { \ + int err; \ + if ((err = sfs_dirent_link_nolock(sfs, sin, slot, lnksin, name)) != 0) { \ + warn("sfs_dirent_link error: %e.\n", err); \ + } \ + } while (0) + +#define sfs_dirent_unlink_nolock_check(sfs, sin, slot, lnksin) \ + do { \ + int err; \ + if ((err = sfs_dirent_unlink_nolock(sfs, sin, slot, lnksin)) != 0) { \ + warn("sfs_dirent_unlink error: %e.\n", err); \ + } \ + } while (0) + +/* + * sfs_dirent_search_nolock - read every file entry in the DIR, compare file name with each entry->name + * If equal, then return slot and NO. of disk of this file's inode + * @sfs: sfs file system + * @sin: sfs inode in memory + * @name: the filename + * @ino_store: NO. of disk of this file (with the filename)'s inode + * @slot: logical index of file entry (NOTICE: each file entry ocupied one disk block) + * @empty_slot: the empty logical index of file entry. + */ +static int +sfs_dirent_search_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, uint32_t *ino_store, int *slot, int *empty_slot) { + assert(strlen(name) <= SFS_MAX_FNAME_LEN); + struct sfs_disk_entry *entry; + if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) { + return -E_NO_MEM; + } + +#define set_pvalue(x, v) do { if ((x) != NULL) { *(x) = (v); } } while (0) + int ret, i, nslots = sin->din->blocks; + set_pvalue(empty_slot, nslots); + for (i = 0; i < nslots; i ++) { + if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) { + goto out; + } + if (entry->ino == 0) { + set_pvalue(empty_slot, i); + continue ; + } + if (strcmp(name, entry->name) == 0) { + set_pvalue(slot, i); + set_pvalue(ino_store, entry->ino); + goto out; + } + } +#undef set_pvalue + ret = -E_NOENT; +out: + kfree(entry); + return ret; +} + +/* + * sfs_dirent_findino_nolock - read all file entries in DIR's inode and find a entry->ino == ino + */ + +static int +sfs_dirent_findino_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t ino, struct sfs_disk_entry *entry) { + int ret, i, nslots = sin->din->blocks; + for (i = 0; i < nslots; i ++) { + if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) { + return ret; + } + if (entry->ino == ino) { + return 0; + } + } + return -E_NOENT; +} + +/* + * sfs_lookup_once - find inode corresponding the file name in DIR's sin inode + * @sfs: sfs file system + * @sin: DIR sfs inode in memory + * @name: the file name in DIR + * @node_store: the inode corresponding the file name in DIR + * @slot: the logical index of file entry + */ +static int +sfs_lookup_once(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, struct inode **node_store, int *slot) { + int ret; + uint32_t ino; + lock_sin(sin); + { // find the NO. of disk block and logical index of file entry + ret = sfs_dirent_search_nolock(sfs, sin, name, &ino, slot, NULL); + } + unlock_sin(sin); + if (ret == 0) { + // load the content of inode with the the NO. of disk block + ret = sfs_load_inode(sfs, node_store, ino); + } + return ret; +} + +// sfs_opendir - just check the opne_flags, now support readonly +static int +sfs_opendir(struct inode *node, uint32_t open_flags) { + switch (open_flags & O_ACCMODE) { + case O_RDONLY: + break; + case O_WRONLY: + case O_RDWR: + default: + return -E_ISDIR; + } + if (open_flags & O_APPEND) { + return -E_ISDIR; + } + return 0; +} + +// sfs_openfile - open file (no use) +static int +sfs_openfile(struct inode *node, uint32_t open_flags) { + return 0; +} + +// sfs_close - close file +static int +sfs_close(struct inode *node) { + return vop_fsync(node); +} + +/* + * sfs_io_nolock - Rd/Wr a file contentfrom offset position to offset+ length disk blocks<-->buffer (in memroy) + * @sfs: sfs file system + * @sin: sfs inode in memory + * @buf: the buffer Rd/Wr + * @offset: the offset of file + * @alenp: the length need to read (is a pointer). and will RETURN the really Rd/Wr lenght + * @write: BOOL, 0 read, 1 write + */ +static int +sfs_io_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, void *buf, off_t offset, size_t *alenp, bool write) { + struct sfs_disk_inode *din = sin->din; + assert(din->type != SFS_TYPE_DIR); + off_t endpos = offset + *alenp, blkoff; + *alenp = 0; + // calculate the Rd/Wr end position + if (offset < 0 || offset >= SFS_MAX_FILE_SIZE || offset > endpos) { + return -E_INVAL; + } + if (offset == endpos) { + return 0; + } + if (endpos > SFS_MAX_FILE_SIZE) { + endpos = SFS_MAX_FILE_SIZE; + } + if (!write) { + if (offset >= din->size) { + return 0; + } + if (endpos > din->size) { + endpos = din->size; + } + } + + int (*sfs_buf_op)(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset); + int (*sfs_block_op)(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks); + if (write) { + sfs_buf_op = sfs_wbuf, sfs_block_op = sfs_wblock; + } + else { + sfs_buf_op = sfs_rbuf, sfs_block_op = sfs_rblock; + } + + int ret = 0; + size_t size, alen = 0; + uint32_t ino; + uint32_t blkno = offset / SFS_BLKSIZE; // The NO. of Rd/Wr begin block + uint32_t nblks = endpos / SFS_BLKSIZE - blkno; // The size of Rd/Wr blocks + + //LAB8:EXERCISE1 YOUR CODE HINT: call sfs_bmap_load_nolock, sfs_rbuf, sfs_rblock,etc. read different kind of blocks in file + /* + * (1) If offset isn't aligned with the first block, Rd/Wr some content from offset to the end of the first block + * NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op + * Rd/Wr size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset) + * (2) Rd/Wr aligned blocks + * NOTICE: useful function: sfs_bmap_load_nolock, sfs_block_op + * (3) If end position isn't aligned with the last block, Rd/Wr some content from begin to the (endpos % SFS_BLKSIZE) of the last block + * NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op + */ + + if ((blkoff = offset % SFS_BLKSIZE) != 0) { + size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset); + if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) { + goto out; + } + if ((ret = sfs_buf_op(sfs, buf, size, ino, blkoff)) != 0) { + goto out; + } + + alen += size; + buf += size; + + if (nblks == 0) { + goto out; + } + + blkno++; + nblks--; + } + + if (nblks > 0) { + if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) { + goto out; + } + if ((ret = sfs_block_op(sfs, buf, ino, nblks)) != 0) { + goto out; + } + + alen += nblks * SFS_BLKSIZE; + buf += nblks * SFS_BLKSIZE; + blkno += nblks; + nblks -= nblks; + } + + if ((size = endpos % SFS_BLKSIZE) != 0) { + if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) { + goto out; + } + if ((ret = sfs_buf_op(sfs, buf, size, ino, 0)) != 0) { + goto out; + } + alen += size; + } + +out: + *alenp = alen; + if (offset + alen > sin->din->size) { + sin->din->size = offset + alen; + sin->dirty = 1; + } + return ret; +} + +/* + * sfs_io - Rd/Wr file. the wrapper of sfs_io_nolock + with lock protect + */ +static inline int +sfs_io(struct inode *node, struct iobuf *iob, bool write) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + int ret; + lock_sin(sin); + { + size_t alen = iob->io_resid; + ret = sfs_io_nolock(sfs, sin, iob->io_base, iob->io_offset, &alen, write); + if (alen != 0) { + iobuf_skip(iob, alen); + } + } + unlock_sin(sin); + return ret; +} + +// sfs_read - read file +static int +sfs_read(struct inode *node, struct iobuf *iob) { + return sfs_io(node, iob, 0); +} + +// sfs_write - write file +static int +sfs_write(struct inode *node, struct iobuf *iob) { + return sfs_io(node, iob, 1); +} + +/* + * sfs_fstat - Return nlinks/block/size, etc. info about a file. The pointer is a pointer to struct stat; + */ +static int +sfs_fstat(struct inode *node, struct stat *stat) { + int ret; + memset(stat, 0, sizeof(struct stat)); + if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) { + return ret; + } + struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din; + stat->st_nlinks = din->nlinks; + stat->st_blocks = din->blocks; + stat->st_size = din->size; + return 0; +} + +/* + * sfs_fsync - Force any dirty inode info associated with this file to stable storage. + */ +static int +sfs_fsync(struct inode *node) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + int ret = 0; + if (sin->dirty) { + lock_sin(sin); + { + if (sin->dirty) { + sin->dirty = 0; + if ((ret = sfs_wbuf(sfs, sin->din, sizeof(struct sfs_disk_inode), sin->ino, 0)) != 0) { + sin->dirty = 1; + } + } + } + unlock_sin(sin); + } + return ret; +} + +/* + *sfs_namefile -Compute pathname relative to filesystem root of the file and copy to the specified io buffer. + * + */ +static int +sfs_namefile(struct inode *node, struct iobuf *iob) { + struct sfs_disk_entry *entry; + if (iob->io_resid <= 2 || (entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) { + return -E_NO_MEM; + } + + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + + int ret; + char *ptr = iob->io_base + iob->io_resid; + size_t alen, resid = iob->io_resid - 2; + vop_ref_inc(node); + while (1) { + struct inode *parent; + if ((ret = sfs_lookup_once(sfs, sin, "..", &parent, NULL)) != 0) { + goto failed; + } + + uint32_t ino = sin->ino; + vop_ref_dec(node); + if (node == parent) { + vop_ref_dec(node); + break; + } + + node = parent, sin = vop_info(node, sfs_inode); + assert(ino != sin->ino && sin->din->type == SFS_TYPE_DIR); + + lock_sin(sin); + { + ret = sfs_dirent_findino_nolock(sfs, sin, ino, entry); + } + unlock_sin(sin); + + if (ret != 0) { + goto failed; + } + + if ((alen = strlen(entry->name) + 1) > resid) { + goto failed_nomem; + } + resid -= alen, ptr -= alen; + memcpy(ptr, entry->name, alen - 1); + ptr[alen - 1] = '/'; + } + alen = iob->io_resid - resid - 2; + ptr = memmove(iob->io_base + 1, ptr, alen); + ptr[-1] = '/', ptr[alen] = '\0'; + iobuf_skip(iob, alen); + kfree(entry); + return 0; + +failed_nomem: + ret = -E_NO_MEM; +failed: + vop_ref_dec(node); + kfree(entry); + return ret; +} + +/* + * sfs_getdirentry_sub_noblock - get the content of file entry in DIR + */ +static int +sfs_getdirentry_sub_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) { + int ret, i, nslots = sin->din->blocks; + for (i = 0; i < nslots; i ++) { + if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) { + return ret; + } + if (entry->ino != 0) { + if (slot == 0) { + return 0; + } + slot --; + } + } + return -E_NOENT; +} + +/* + * sfs_getdirentry - according to the iob->io_offset, calculate the dir entry's slot in disk block, + get dir entry content from the disk + */ +static int +sfs_getdirentry(struct inode *node, struct iobuf *iob) { + struct sfs_disk_entry *entry; + if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) { + return -E_NO_MEM; + } + + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + + int ret, slot; + off_t offset = iob->io_offset; + if (offset < 0 || offset % sfs_dentry_size != 0) { + kfree(entry); + return -E_INVAL; + } + if ((slot = offset / sfs_dentry_size) > sin->din->blocks) { + kfree(entry); + return -E_NOENT; + } + lock_sin(sin); + if ((ret = sfs_getdirentry_sub_nolock(sfs, sin, slot, entry)) != 0) { + unlock_sin(sin); + goto out; + } + unlock_sin(sin); + ret = iobuf_move(iob, entry->name, sfs_dentry_size, 1, NULL); +out: + kfree(entry); + return ret; +} + +/* + * sfs_reclaim - Free all resources inode occupied . Called when inode is no longer in use. + */ +static int +sfs_reclaim(struct inode *node) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + + int ret = -E_BUSY; + uint32_t ent; + lock_sfs_fs(sfs); + assert(sin->reclaim_count > 0); + if ((-- sin->reclaim_count) != 0 || inode_ref_count(node) != 0) { + goto failed_unlock; + } + if (sin->din->nlinks == 0) { + if ((ret = vop_truncate(node, 0)) != 0) { + goto failed_unlock; + } + } + if (sin->dirty) { + if ((ret = vop_fsync(node)) != 0) { + goto failed_unlock; + } + } + sfs_remove_links(sin); + unlock_sfs_fs(sfs); + + if (sin->din->nlinks == 0) { + sfs_block_free(sfs, sin->ino); + if ((ent = sin->din->indirect) != 0) { + sfs_block_free(sfs, ent); + } + } + kfree(sin->din); + vop_kill(node); + return 0; + +failed_unlock: + unlock_sfs_fs(sfs); + return ret; +} + +/* + * sfs_gettype - Return type of file. The values for file types are in sfs.h. + */ +static int +sfs_gettype(struct inode *node, uint32_t *type_store) { + struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din; + switch (din->type) { + case SFS_TYPE_DIR: + *type_store = S_IFDIR; + return 0; + case SFS_TYPE_FILE: + *type_store = S_IFREG; + return 0; + case SFS_TYPE_LINK: + *type_store = S_IFLNK; + return 0; + } + panic("invalid file type %d.\n", din->type); +} + +/* + * sfs_tryseek - Check if seeking to the specified position within the file is legal. + */ +static int +sfs_tryseek(struct inode *node, off_t pos) { + if (pos < 0 || pos >= SFS_MAX_FILE_SIZE) { + return -E_INVAL; + } + struct sfs_inode *sin = vop_info(node, sfs_inode); + if (pos > sin->din->size) { + return vop_truncate(node, pos); + } + return 0; +} + +/* + * sfs_truncfile : reszie the file with new length + */ +static int +sfs_truncfile(struct inode *node, off_t len) { + if (len < 0 || len > SFS_MAX_FILE_SIZE) { + return -E_INVAL; + } + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + struct sfs_disk_inode *din = sin->din; + + int ret = 0; + //new number of disk blocks of file + uint32_t nblks, tblks = ROUNDUP_DIV(len, SFS_BLKSIZE); + if (din->size == len) { + assert(tblks == din->blocks); + return 0; + } + + lock_sin(sin); + // old number of disk blocks of file + nblks = din->blocks; + if (nblks < tblks) { + // try to enlarge the file size by add new disk block at the end of file + while (nblks != tblks) { + if ((ret = sfs_bmap_load_nolock(sfs, sin, nblks, NULL)) != 0) { + goto out_unlock; + } + nblks ++; + } + } + else if (tblks < nblks) { + // try to reduce the file size + while (tblks != nblks) { + if ((ret = sfs_bmap_truncate_nolock(sfs, sin)) != 0) { + goto out_unlock; + } + nblks --; + } + } + assert(din->blocks == tblks); + din->size = len; + sin->dirty = 1; + +out_unlock: + unlock_sin(sin); + return ret; +} + +/* + * sfs_lookup - Parse path relative to the passed directory + * DIR, and hand back the inode for the file it + * refers to. + */ +static int +sfs_lookup(struct inode *node, char *path, struct inode **node_store) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + assert(*path != '\0' && *path != '/'); + vop_ref_inc(node); + struct sfs_inode *sin = vop_info(node, sfs_inode); + if (sin->din->type != SFS_TYPE_DIR) { + vop_ref_dec(node); + return -E_NOTDIR; + } + struct inode *subnode; + int ret = sfs_lookup_once(sfs, sin, path, &subnode, NULL); + + vop_ref_dec(node); + if (ret != 0) { + return ret; + } + *node_store = subnode; + return 0; +} + +// The sfs specific DIR operations correspond to the abstract operations on a inode. +static const struct inode_ops sfs_node_dirops = { + .vop_magic = VOP_MAGIC, + .vop_open = sfs_opendir, + .vop_close = sfs_close, + .vop_fstat = sfs_fstat, + .vop_fsync = sfs_fsync, + .vop_namefile = sfs_namefile, + .vop_getdirentry = sfs_getdirentry, + .vop_reclaim = sfs_reclaim, + .vop_gettype = sfs_gettype, + .vop_lookup = sfs_lookup, +}; +/// The sfs specific FILE operations correspond to the abstract operations on a inode. +static const struct inode_ops sfs_node_fileops = { + .vop_magic = VOP_MAGIC, + .vop_open = sfs_openfile, + .vop_close = sfs_close, + .vop_read = sfs_read, + .vop_write = sfs_write, + .vop_fstat = sfs_fstat, + .vop_fsync = sfs_fsync, + .vop_reclaim = sfs_reclaim, + .vop_gettype = sfs_gettype, + .vop_tryseek = sfs_tryseek, + .vop_truncate = sfs_truncfile, +}; + diff --git a/lab8/kern/fs/sfs/sfs_io.c b/lab8/kern/fs/sfs/sfs_io.c new file mode 100644 index 0000000..f3c705f --- /dev/null +++ b/lab8/kern/fs/sfs/sfs_io.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +//Basic block-level I/O routines + +/* sfs_rwblock_nolock - Basic block-level I/O routine for Rd/Wr one disk block, + * without lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @write: BOOL: Read or Write + * @check: BOOL: if check (blono < sfs super.blocks) + */ +static int +sfs_rwblock_nolock(struct sfs_fs *sfs, void *buf, uint32_t blkno, bool write, bool check) { + assert((blkno != 0 || !check) && blkno < sfs->super.blocks); + struct iobuf __iob, *iob = iobuf_init(&__iob, buf, SFS_BLKSIZE, blkno * SFS_BLKSIZE); + return dop_io(sfs->dev, iob, write); +} + +/* sfs_rwblock - Basic block-level I/O routine for Rd/Wr N disk blocks , + * with lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + * @write: BOOL: Read - 0 or Write - 1 + */ +static int +sfs_rwblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks, bool write) { + int ret = 0; + lock_sfs_io(sfs); + { + while (nblks != 0) { + if ((ret = sfs_rwblock_nolock(sfs, buf, blkno, write, 1)) != 0) { + break; + } + blkno ++, nblks --; + buf += SFS_BLKSIZE; + } + } + unlock_sfs_io(sfs); + return ret; +} + +/* sfs_rblock - The Wrap of sfs_rwblock function for Rd N disk blocks , + * + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + */ +int +sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) { + return sfs_rwblock(sfs, buf, blkno, nblks, 0); +} + +/* sfs_wblock - The Wrap of sfs_rwblock function for Wr N disk blocks , + * + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + */ +int +sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) { + return sfs_rwblock(sfs, buf, blkno, nblks, 1); +} + +/* sfs_rbuf - The Basic block-level I/O routine for Rd( non-block & non-aligned io) one disk block(using sfs->sfs_buffer) + * with lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd + * @len: the length need to Rd + * @blkno: the NO. of disk block + * @offset: the offset in the content of disk block + */ +int +sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) { + assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE); + int ret; + lock_sfs_io(sfs); + { + if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) { + memcpy(buf, sfs->sfs_buffer + offset, len); + } + } + unlock_sfs_io(sfs); + return ret; +} + +/* sfs_wbuf - The Basic block-level I/O routine for Wr( non-block & non-aligned io) one disk block(using sfs->sfs_buffer) + * with lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Wr + * @len: the length need to Wr + * @blkno: the NO. of disk block + * @offset: the offset in the content of disk block + */ +int +sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) { + assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE); + int ret; + lock_sfs_io(sfs); + { + if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) { + memcpy(sfs->sfs_buffer + offset, buf, len); + ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1); + } + } + unlock_sfs_io(sfs); + return ret; +} + +/* + * sfs_sync_super - write sfs->super (in memory) into disk (SFS_BLKN_SUPER, 1) with lock protect. + */ +int +sfs_sync_super(struct sfs_fs *sfs) { + int ret; + lock_sfs_io(sfs); + { + memset(sfs->sfs_buffer, 0, SFS_BLKSIZE); + memcpy(sfs->sfs_buffer, &(sfs->super), sizeof(sfs->super)); + ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, SFS_BLKN_SUPER, 1, 0); + } + unlock_sfs_io(sfs); + return ret; +} + +/* + * sfs_sync_freemap - write sfs bitmap into disk (SFS_BLKN_FREEMAP, nblks) without lock protect. + */ +int +sfs_sync_freemap(struct sfs_fs *sfs) { + uint32_t nblks = sfs_freemap_blocks(&(sfs->super)); + return sfs_wblock(sfs, bitmap_getdata(sfs->freemap, NULL), SFS_BLKN_FREEMAP, nblks); +} + +/* + * sfs_clear_block - write zero info into disk (blkno, nblks) with lock protect. + * @sfs: sfs_fs which will be process + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + */ +int +sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks) { + int ret; + lock_sfs_io(sfs); + { + memset(sfs->sfs_buffer, 0, SFS_BLKSIZE); + while (nblks != 0) { + if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1)) != 0) { + break; + } + blkno ++, nblks --; + } + } + unlock_sfs_io(sfs); + return ret; +} + diff --git a/lab8/kern/fs/sfs/sfs_lock.c b/lab8/kern/fs/sfs/sfs_lock.c new file mode 100644 index 0000000..6d83ecd --- /dev/null +++ b/lab8/kern/fs/sfs/sfs_lock.c @@ -0,0 +1,44 @@ +#include +#include +#include + + +/* + * lock_sfs_fs - lock the process of SFS Filesystem Rd/Wr Disk Block + * + * called by: sfs_load_inode, sfs_sync, sfs_reclaim + */ +void +lock_sfs_fs(struct sfs_fs *sfs) { + down(&(sfs->fs_sem)); +} + +/* + * lock_sfs_io - lock the process of SFS File Rd/Wr Disk Block + * + * called by: sfs_rwblock, sfs_clear_block, sfs_sync_super + */ +void +lock_sfs_io(struct sfs_fs *sfs) { + down(&(sfs->io_sem)); +} + +/* + * unlock_sfs_fs - unlock the process of SFS Filesystem Rd/Wr Disk Block + * + * called by: sfs_load_inode, sfs_sync, sfs_reclaim + */ +void +unlock_sfs_fs(struct sfs_fs *sfs) { + up(&(sfs->fs_sem)); +} + +/* + * unlock_sfs_io - unlock the process of sfs Rd/Wr Disk Block + * + * called by: sfs_rwblock sfs_clear_block sfs_sync_super + */ +void +unlock_sfs_io(struct sfs_fs *sfs) { + up(&(sfs->io_sem)); +} diff --git a/lab8/kern/fs/swap/swapfs.c b/lab8/kern/fs/swap/swapfs.c new file mode 100644 index 0000000..d9f6090 --- /dev/null +++ b/lab8/kern/fs/swap/swapfs.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include +#include + +void +swapfs_init(void) { + static_assert((PGSIZE % SECTSIZE) == 0); + if (!ide_device_valid(SWAP_DEV_NO)) { + panic("swap fs isn't available.\n"); + } + max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE); +} + +int +swapfs_read(swap_entry_t entry, struct Page *page) { + return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT); +} + +int +swapfs_write(swap_entry_t entry, struct Page *page) { + return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT); +} + diff --git a/lab8/kern/fs/swap/swapfs.h b/lab8/kern/fs/swap/swapfs.h new file mode 100644 index 0000000..904fb2e --- /dev/null +++ b/lab8/kern/fs/swap/swapfs.h @@ -0,0 +1,12 @@ +#ifndef __KERN_FS_SWAP_SWAPFS_H__ +#define __KERN_FS_SWAP_SWAPFS_H__ + +#include +#include + +void swapfs_init(void); +int swapfs_read(swap_entry_t entry, struct Page *page); +int swapfs_write(swap_entry_t entry, struct Page *page); + +#endif /* !__KERN_FS_SWAP_SWAPFS_H__ */ + diff --git a/lab8/kern/fs/sysfile.c b/lab8/kern/fs/sysfile.c new file mode 100644 index 0000000..0dd8e58 --- /dev/null +++ b/lab8/kern/fs/sysfile.c @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IOBUF_SIZE 4096 + +/* copy_path - copy path name */ +static int +copy_path(char **to, const char *from) { + struct mm_struct *mm = current->mm; + char *buffer; + if ((buffer = kmalloc(FS_MAX_FPATH_LEN + 1)) == NULL) { + return -E_NO_MEM; + } + lock_mm(mm); + if (!copy_string(mm, buffer, from, FS_MAX_FPATH_LEN + 1)) { + unlock_mm(mm); + goto failed_cleanup; + } + unlock_mm(mm); + *to = buffer; + return 0; + +failed_cleanup: + kfree(buffer); + return -E_INVAL; +} + +/* sysfile_open - open file */ +int +sysfile_open(const char *__path, uint32_t open_flags) { + int ret; + char *path; + if ((ret = copy_path(&path, __path)) != 0) { + return ret; + } + ret = file_open(path, open_flags); + kfree(path); + return ret; +} + +/* sysfile_close - close file */ +int +sysfile_close(int fd) { + return file_close(fd); +} + +/* sysfile_read - read file */ +int +sysfile_read(int fd, void *base, size_t len) { + struct mm_struct *mm = current->mm; + if (len == 0) { + return 0; + } + if (!file_testfd(fd, 1, 0)) { + return -E_INVAL; + } + void *buffer; + if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { + return -E_NO_MEM; + } + + int ret = 0; + size_t copied = 0, alen; + while (len != 0) { + if ((alen = IOBUF_SIZE) > len) { + alen = len; + } + ret = file_read(fd, buffer, alen, &alen); + if (alen != 0) { + lock_mm(mm); + { + if (copy_to_user(mm, base, buffer, alen)) { + assert(len >= alen); + base += alen, len -= alen, copied += alen; + } + else if (ret == 0) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + } + if (ret != 0 || alen == 0) { + goto out; + } + } + +out: + kfree(buffer); + if (copied != 0) { + return copied; + } + return ret; +} + +/* sysfile_write - write file */ +int +sysfile_write(int fd, void *base, size_t len) { + struct mm_struct *mm = current->mm; + if (len == 0) { + return 0; + } + if (!file_testfd(fd, 0, 1)) { + return -E_INVAL; + } + void *buffer; + if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { + return -E_NO_MEM; + } + + int ret = 0; + size_t copied = 0, alen; + while (len != 0) { + if ((alen = IOBUF_SIZE) > len) { + alen = len; + } + lock_mm(mm); + { + if (!copy_from_user(mm, buffer, base, alen, 0)) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + if (ret == 0) { + ret = file_write(fd, buffer, alen, &alen); + if (alen != 0) { + assert(len >= alen); + base += alen, len -= alen, copied += alen; + } + } + if (ret != 0 || alen == 0) { + goto out; + } + } + +out: + kfree(buffer); + if (copied != 0) { + return copied; + } + return ret; +} + +/* sysfile_seek - seek file */ +int +sysfile_seek(int fd, off_t pos, int whence) { + return file_seek(fd, pos, whence); +} + +/* sysfile_fstat - stat file */ +int +sysfile_fstat(int fd, struct stat *__stat) { + struct mm_struct *mm = current->mm; + int ret; + struct stat __local_stat, *stat = &__local_stat; + if ((ret = file_fstat(fd, stat)) != 0) { + return ret; + } + + lock_mm(mm); + { + if (!copy_to_user(mm, __stat, stat, sizeof(struct stat))) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + return ret; +} + +/* sysfile_fsync - sync file */ +int +sysfile_fsync(int fd) { + return file_fsync(fd); +} + +/* sysfile_chdir - change dir */ +int +sysfile_chdir(const char *__path) { + int ret; + char *path; + if ((ret = copy_path(&path, __path)) != 0) { + return ret; + } + ret = vfs_chdir(path); + kfree(path); + return ret; +} + +/* sysfile_link - link file */ +int +sysfile_link(const char *__path1, const char *__path2) { + int ret; + char *old_path, *new_path; + if ((ret = copy_path(&old_path, __path1)) != 0) { + return ret; + } + if ((ret = copy_path(&new_path, __path2)) != 0) { + kfree(old_path); + return ret; + } + ret = vfs_link(old_path, new_path); + kfree(old_path), kfree(new_path); + return ret; +} + +/* sysfile_rename - rename file */ +int +sysfile_rename(const char *__path1, const char *__path2) { + int ret; + char *old_path, *new_path; + if ((ret = copy_path(&old_path, __path1)) != 0) { + return ret; + } + if ((ret = copy_path(&new_path, __path2)) != 0) { + kfree(old_path); + return ret; + } + ret = vfs_rename(old_path, new_path); + kfree(old_path), kfree(new_path); + return ret; +} + +/* sysfile_unlink - unlink file */ +int +sysfile_unlink(const char *__path) { + int ret; + char *path; + if ((ret = copy_path(&path, __path)) != 0) { + return ret; + } + ret = vfs_unlink(path); + kfree(path); + return ret; +} + +/* sysfile_get cwd - get current working directory */ +int +sysfile_getcwd(char *buf, size_t len) { + struct mm_struct *mm = current->mm; + if (len == 0) { + return -E_INVAL; + } + + int ret = -E_INVAL; + lock_mm(mm); + { + if (user_mem_check(mm, (uintptr_t)buf, len, 1)) { + struct iobuf __iob, *iob = iobuf_init(&__iob, buf, len, 0); + ret = vfs_getcwd(iob); + } + } + unlock_mm(mm); + return ret; +} + +/* sysfile_getdirentry - get the file entry in DIR */ +int +sysfile_getdirentry(int fd, struct dirent *__direntp) { + struct mm_struct *mm = current->mm; + struct dirent *direntp; + if ((direntp = kmalloc(sizeof(struct dirent))) == NULL) { + return -E_NO_MEM; + } + + int ret = 0; + lock_mm(mm); + { + if (!copy_from_user(mm, &(direntp->offset), &(__direntp->offset), sizeof(direntp->offset), 1)) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + + if (ret != 0 || (ret = file_getdirentry(fd, direntp)) != 0) { + goto out; + } + + lock_mm(mm); + { + if (!copy_to_user(mm, __direntp, direntp, sizeof(struct dirent))) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + +out: + kfree(direntp); + return ret; +} + +/* sysfile_dup - duplicate fd1 to fd2 */ +int +sysfile_dup(int fd1, int fd2) { + return file_dup(fd1, fd2); +} + +int +sysfile_pipe(int *fd_store) { + return -E_UNIMP; +} + +int +sysfile_mkfifo(const char *__name, uint32_t open_flags) { + return -E_UNIMP; +} + diff --git a/lab8/kern/fs/sysfile.h b/lab8/kern/fs/sysfile.h new file mode 100644 index 0000000..bc060e9 --- /dev/null +++ b/lab8/kern/fs/sysfile.h @@ -0,0 +1,28 @@ +#ifndef __KERN_FS_SYSFILE_H__ +#define __KERN_FS_SYSFILE_H__ + +#include + +struct stat; +struct dirent; + +int sysfile_open(const char *path, uint32_t open_flags); // Open or create a file. FLAGS/MODE per the syscall. +int sysfile_close(int fd); // Close a vnode opened +int sysfile_read(int fd, void *base, size_t len); // Read file +int sysfile_write(int fd, void *base, size_t len); // Write file +int sysfile_seek(int fd, off_t pos, int whence); // Seek file +int sysfile_fstat(int fd, struct stat *stat); // Stat file +int sysfile_fsync(int fd); // Sync file +int sysfile_chdir(const char *path); // change DIR +int sysfile_mkdir(const char *path); // create DIR +int sysfile_link(const char *path1, const char *path2); // set a path1's link as path2 +int sysfile_rename(const char *path1, const char *path2); // rename file +int sysfile_unlink(const char *path); // unlink a path +int sysfile_getcwd(char *buf, size_t len); // get current working directory +int sysfile_getdirentry(int fd, struct dirent *direntp); // get the file entry in DIR +int sysfile_dup(int fd1, int fd2); // duplicate file +int sysfile_pipe(int *fd_store); // build PIPE +int sysfile_mkfifo(const char *name, uint32_t open_flags); // build named PIPE + +#endif /* !__KERN_FS_SYSFILE_H__ */ + diff --git a/lab8/kern/fs/vfs/inode.c b/lab8/kern/fs/vfs/inode.c new file mode 100644 index 0000000..ffc873b --- /dev/null +++ b/lab8/kern/fs/vfs/inode.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* * + * __alloc_inode - alloc a inode structure and initialize in_type + * */ +struct inode * +__alloc_inode(int type) { + struct inode *node; + if ((node = kmalloc(sizeof(struct inode))) != NULL) { + node->in_type = type; + } + return node; +} + +/* * + * inode_init - initialize a inode structure + * invoked by vop_init + * */ +void +inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs) { + node->ref_count = 0; + node->open_count = 0; + node->in_ops = ops, node->in_fs = fs; + vop_ref_inc(node); +} + +/* * + * inode_kill - kill a inode structure + * invoked by vop_kill + * */ +void +inode_kill(struct inode *node) { + assert(inode_ref_count(node) == 0); + assert(inode_open_count(node) == 0); + kfree(node); +} + +/* * + * inode_ref_inc - increment ref_count + * invoked by vop_ref_inc + * */ +int +inode_ref_inc(struct inode *node) { + node->ref_count += 1; + return node->ref_count; +} + +/* * + * inode_ref_dec - decrement ref_count + * invoked by vop_ref_dec + * calls vop_reclaim if the ref_count hits zero + * */ +int +inode_ref_dec(struct inode *node) { + assert(inode_ref_count(node) > 0); + int ref_count, ret; + node->ref_count-= 1; + ref_count = node->ref_count; + if (ref_count == 0) { + if ((ret = vop_reclaim(node)) != 0 && ret != -E_BUSY) { + cprintf("vfs: warning: vop_reclaim: %e.\n", ret); + } + } + return ref_count; +} + +/* * + * inode_open_inc - increment the open_count + * invoked by vop_open_inc + * */ +int +inode_open_inc(struct inode *node) { + node->open_count += 1; + return node->open_count; +} + +/* * + * inode_open_dec - decrement the open_count + * invoked by vop_open_dec + * calls vop_close if the open_count hits zero + * */ +int +inode_open_dec(struct inode *node) { + assert(inode_open_count(node) > 0); + int open_count, ret; + node->open_count -= 1; + open_count = node->open_count; + if (open_count == 0) { + if ((ret = vop_close(node)) != 0) { + cprintf("vfs: warning: vop_close: %e.\n", ret); + } + } + return open_count; +} + +/* * + * inode_check - check the various things being valid + * called before all vop_* calls + * */ +void +inode_check(struct inode *node, const char *opstr) { + assert(node != NULL && node->in_ops != NULL); + assert(node->in_ops->vop_magic == VOP_MAGIC); + int ref_count = inode_ref_count(node), open_count = inode_open_count(node); + assert(ref_count >= open_count && open_count >= 0); + assert(ref_count < MAX_INODE_COUNT && open_count < MAX_INODE_COUNT); +} + diff --git a/lab8/kern/fs/vfs/inode.h b/lab8/kern/fs/vfs/inode.h new file mode 100644 index 0000000..edc7efa --- /dev/null +++ b/lab8/kern/fs/vfs/inode.h @@ -0,0 +1,248 @@ +#ifndef __KERN_FS_VFS_INODE_H__ +#define __KERN_FS_VFS_INODE_H__ + +#include +#include +#include +#include +#include + +struct stat; +struct iobuf; + +/* + * A struct inode is an abstract representation of a file. + * + * It is an interface that allows the kernel's filesystem-independent + * code to interact usefully with multiple sets of filesystem code. + */ + +/* + * Abstract low-level file. + * + * Note: in_info is Filesystem-specific data, in_type is the inode type + * + * open_count is managed using VOP_INCOPEN and VOP_DECOPEN by + * vfs_open() and vfs_close(). Code above the VFS layer should not + * need to worry about it. + */ +struct inode { + union { + struct device __device_info; + struct sfs_inode __sfs_inode_info; + } in_info; + enum { + inode_type_device_info = 0x1234, + inode_type_sfs_inode_info, + } in_type; + int ref_count; + int open_count; + struct fs *in_fs; + const struct inode_ops *in_ops; +}; + +#define __in_type(type) inode_type_##type##_info + +#define check_inode_type(node, type) ((node)->in_type == __in_type(type)) + +#define __vop_info(node, type) \ + ({ \ + struct inode *__node = (node); \ + assert(__node != NULL && check_inode_type(__node, type)); \ + &(__node->in_info.__##type##_info); \ + }) + +#define vop_info(node, type) __vop_info(node, type) + +#define info2node(info, type) \ + to_struct((info), struct inode, in_info.__##type##_info) + +struct inode *__alloc_inode(int type); + +#define alloc_inode(type) __alloc_inode(__in_type(type)) + +#define MAX_INODE_COUNT 0x10000 + +int inode_ref_inc(struct inode *node); +int inode_ref_dec(struct inode *node); +int inode_open_inc(struct inode *node); +int inode_open_dec(struct inode *node); + +void inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs); +void inode_kill(struct inode *node); + +#define VOP_MAGIC 0x8c4ba476 + +/* + * Abstract operations on a inode. + * + * These are used in the form VOP_FOO(inode, args), which are macros + * that expands to inode->inode_ops->vop_foo(inode, args). The operations + * "foo" are: + * + * vop_open - Called on open() of a file. Can be used to + * reject illegal or undesired open modes. Note that + * various operations can be performed without the + * file actually being opened. + * The inode need not look at O_CREAT, O_EXCL, or + * O_TRUNC, as these are handled in the VFS layer. + * + * VOP_EACHOPEN should not be called directly from + * above the VFS layer - use vfs_open() to open inodes. + * This maintains the open count so VOP_LASTCLOSE can + * be called at the right time. + * + * vop_close - To be called on *last* close() of a file. + * + * VOP_LASTCLOSE should not be called directly from + * above the VFS layer - use vfs_close() to close + * inodes opened with vfs_open(). + * + * vop_reclaim - Called when inode is no longer in use. Note that + * this may be substantially after vop_lastclose is + * called. + * + ***************************************** + * + * vop_read - Read data from file to uio, at offset specified + * in the uio, updating uio_resid to reflect the + * amount read, and updating uio_offset to match. + * Not allowed on directories or symlinks. + * + * vop_getdirentry - Read a single filename from a directory into a + * uio, choosing what name based on the offset + * field in the uio, and updating that field. + * Unlike with I/O on regular files, the value of + * the offset field is not interpreted outside + * the filesystem and thus need not be a byte + * count. However, the uio_resid field should be + * handled in the normal fashion. + * On non-directory objects, return ENOTDIR. + * + * vop_write - Write data from uio to file at offset specified + * in the uio, updating uio_resid to reflect the + * amount written, and updating uio_offset to match. + * Not allowed on directories or symlinks. + * + * vop_ioctl - Perform ioctl operation OP on file using data + * DATA. The interpretation of the data is specific + * to each ioctl. + * + * vop_fstat -Return info about a file. The pointer is a + * pointer to struct stat; see stat.h. + * + * vop_gettype - Return type of file. The values for file types + * are in sfs.h. + * + * vop_tryseek - Check if seeking to the specified position within + * the file is legal. (For instance, all seeks + * are illegal on serial port devices, and seeks + * past EOF on files whose sizes are fixed may be + * as well.) + * + * vop_fsync - Force any dirty buffers associated with this file + * to stable storage. + * + * vop_truncate - Forcibly set size of file to the length passed + * in, discarding any excess blocks. + * + * vop_namefile - Compute pathname relative to filesystem root + * of the file and copy to the specified io buffer. + * Need not work on objects that are not + * directories. + * + ***************************************** + * + * vop_creat - Create a regular file named NAME in the passed + * directory DIR. If boolean EXCL is true, fail if + * the file already exists; otherwise, use the + * existing file if there is one. Hand back the + * inode for the file as per vop_lookup. + * + ***************************************** + * + * vop_lookup - Parse PATHNAME relative to the passed directory + * DIR, and hand back the inode for the file it + * refers to. May destroy PATHNAME. Should increment + * refcount on inode handed back. + */ +struct inode_ops { + unsigned long vop_magic; + int (*vop_open)(struct inode *node, uint32_t open_flags); + int (*vop_close)(struct inode *node); + int (*vop_read)(struct inode *node, struct iobuf *iob); + int (*vop_write)(struct inode *node, struct iobuf *iob); + int (*vop_fstat)(struct inode *node, struct stat *stat); + int (*vop_fsync)(struct inode *node); + int (*vop_namefile)(struct inode *node, struct iobuf *iob); + int (*vop_getdirentry)(struct inode *node, struct iobuf *iob); + int (*vop_reclaim)(struct inode *node); + int (*vop_gettype)(struct inode *node, uint32_t *type_store); + int (*vop_tryseek)(struct inode *node, off_t pos); + int (*vop_truncate)(struct inode *node, off_t len); + int (*vop_create)(struct inode *node, const char *name, bool excl, struct inode **node_store); + int (*vop_lookup)(struct inode *node, char *path, struct inode **node_store); + int (*vop_ioctl)(struct inode *node, int op, void *data); +}; + +/* + * Consistency check + */ +void inode_check(struct inode *node, const char *opstr); + +#define __vop_op(node, sym) \ + ({ \ + struct inode *__node = (node); \ + assert(__node != NULL && __node->in_ops != NULL && __node->in_ops->vop_##sym != NULL); \ + inode_check(__node, #sym); \ + __node->in_ops->vop_##sym; \ + }) + +#define vop_open(node, open_flags) (__vop_op(node, open)(node, open_flags)) +#define vop_close(node) (__vop_op(node, close)(node)) +#define vop_read(node, iob) (__vop_op(node, read)(node, iob)) +#define vop_write(node, iob) (__vop_op(node, write)(node, iob)) +#define vop_fstat(node, stat) (__vop_op(node, fstat)(node, stat)) +#define vop_fsync(node) (__vop_op(node, fsync)(node)) +#define vop_namefile(node, iob) (__vop_op(node, namefile)(node, iob)) +#define vop_getdirentry(node, iob) (__vop_op(node, getdirentry)(node, iob)) +#define vop_reclaim(node) (__vop_op(node, reclaim)(node)) +#define vop_ioctl(node, op, data) (__vop_op(node, ioctl)(node, op, data)) +#define vop_gettype(node, type_store) (__vop_op(node, gettype)(node, type_store)) +#define vop_tryseek(node, pos) (__vop_op(node, tryseek)(node, pos)) +#define vop_truncate(node, len) (__vop_op(node, truncate)(node, len)) +#define vop_create(node, name, excl, node_store) (__vop_op(node, create)(node, name, excl, node_store)) +#define vop_lookup(node, path, node_store) (__vop_op(node, lookup)(node, path, node_store)) + + +#define vop_fs(node) ((node)->in_fs) +#define vop_init(node, ops, fs) inode_init(node, ops, fs) +#define vop_kill(node) inode_kill(node) + +/* + * Reference count manipulation (handled above filesystem level) + */ +#define vop_ref_inc(node) inode_ref_inc(node) +#define vop_ref_dec(node) inode_ref_dec(node) +/* + * Open count manipulation (handled above filesystem level) + * + * VOP_INCOPEN is called by vfs_open. VOP_DECOPEN is called by vfs_close. + * Neither of these should need to be called from above the vfs layer. + */ +#define vop_open_inc(node) inode_open_inc(node) +#define vop_open_dec(node) inode_open_dec(node) + + +static inline int +inode_ref_count(struct inode *node) { + return node->ref_count; +} + +static inline int +inode_open_count(struct inode *node) { + return node->open_count; +} + +#endif /* !__KERN_FS_VFS_INODE_H__ */ + diff --git a/lab8/kern/fs/vfs/vfs.c b/lab8/kern/fs/vfs/vfs.c new file mode 100644 index 0000000..e3ed0da --- /dev/null +++ b/lab8/kern/fs/vfs/vfs.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +static semaphore_t bootfs_sem; +static struct inode *bootfs_node = NULL; + +extern void vfs_devlist_init(void); + +// __alloc_fs - allocate memory for fs, and set fs type +struct fs * +__alloc_fs(int type) { + struct fs *fs; + if ((fs = kmalloc(sizeof(struct fs))) != NULL) { + fs->fs_type = type; + } + return fs; +} + +// vfs_init - vfs initialize +void +vfs_init(void) { + sem_init(&bootfs_sem, 1); + vfs_devlist_init(); +} + +// lock_bootfs - lock for bootfs +static void +lock_bootfs(void) { + down(&bootfs_sem); +} +// ulock_bootfs - ulock for bootfs +static void +unlock_bootfs(void) { + up(&bootfs_sem); +} + +// change_bootfs - set the new fs inode +static void +change_bootfs(struct inode *node) { + struct inode *old; + lock_bootfs(); + { + old = bootfs_node, bootfs_node = node; + } + unlock_bootfs(); + if (old != NULL) { + vop_ref_dec(old); + } +} + +// vfs_set_bootfs - change the dir of file system +int +vfs_set_bootfs(char *fsname) { + struct inode *node = NULL; + if (fsname != NULL) { + char *s; + if ((s = strchr(fsname, ':')) == NULL || s[1] != '\0') { + return -E_INVAL; + } + int ret; + if ((ret = vfs_chdir(fsname)) != 0) { + return ret; + } + if ((ret = vfs_get_curdir(&node)) != 0) { + return ret; + } + } + change_bootfs(node); + return 0; +} + +// vfs_get_bootfs - get the inode of bootfs +int +vfs_get_bootfs(struct inode **node_store) { + struct inode *node = NULL; + if (bootfs_node != NULL) { + lock_bootfs(); + { + if ((node = bootfs_node) != NULL) { + vop_ref_inc(bootfs_node); + } + } + unlock_bootfs(); + } + if (node == NULL) { + return -E_NOENT; + } + *node_store = node; + return 0; +} + diff --git a/lab8/kern/fs/vfs/vfs.h b/lab8/kern/fs/vfs/vfs.h new file mode 100644 index 0000000..7ff8a10 --- /dev/null +++ b/lab8/kern/fs/vfs/vfs.h @@ -0,0 +1,191 @@ +#ifndef __KERN_FS_VFS_VFS_H__ +#define __KERN_FS_VFS_VFS_H__ + +#include +#include +#include + +struct inode; // abstract structure for an on-disk file (inode.h) +struct device; // abstract structure for a device (dev.h) +struct iobuf; // kernel or userspace I/O buffer (iobuf.h) + +/* + * Abstract filesystem. (Or device accessible as a file.) + * + * Information: + * fs_info : filesystem-specific data (sfs_fs) + * fs_type : filesystem type + * Operations: + * + * fs_sync - Flush all dirty buffers to disk. + * fs_get_root - Return root inode of filesystem. + * fs_unmount - Attempt unmount of filesystem. + * fs_cleanup - Cleanup of filesystem.??? + * + * + * fs_get_root should increment the refcount of the inode returned. + * It should not ever return NULL. + * + * If fs_unmount returns an error, the filesystem stays mounted, and + * consequently the struct fs instance should remain valid. On success, + * however, the filesystem object and all storage associated with the + * filesystem should have been discarded/released. + * + */ +struct fs { + union { + struct sfs_fs __sfs_info; + } fs_info; // filesystem-specific data + enum { + fs_type_sfs_info, + } fs_type; // filesystem type + int (*fs_sync)(struct fs *fs); // Flush all dirty buffers to disk + struct inode *(*fs_get_root)(struct fs *fs); // Return root inode of filesystem. + int (*fs_unmount)(struct fs *fs); // Attempt unmount of filesystem. + void (*fs_cleanup)(struct fs *fs); // Cleanup of filesystem.??? +}; + +#define __fs_type(type) fs_type_##type##_info + +#define check_fs_type(fs, type) ((fs)->fs_type == __fs_type(type)) + +#define __fsop_info(_fs, type) ({ \ + struct fs *__fs = (_fs); \ + assert(__fs != NULL && check_fs_type(__fs, type)); \ + &(__fs->fs_info.__##type##_info); \ + }) + +#define fsop_info(fs, type) __fsop_info(fs, type) + +#define info2fs(info, type) \ + to_struct((info), struct fs, fs_info.__##type##_info) + +struct fs *__alloc_fs(int type); + +#define alloc_fs(type) __alloc_fs(__fs_type(type)) + +// Macros to shorten the calling sequences. +#define fsop_sync(fs) ((fs)->fs_sync(fs)) +#define fsop_get_root(fs) ((fs)->fs_get_root(fs)) +#define fsop_unmount(fs) ((fs)->fs_unmount(fs)) +#define fsop_cleanup(fs) ((fs)->fs_cleanup(fs)) + +/* + * Virtual File System layer functions. + * + * The VFS layer translates operations on abstract on-disk files or + * pathnames to operations on specific files on specific filesystems. + */ +void vfs_init(void); +void vfs_cleanup(void); +void vfs_devlist_init(void); + +/* + * VFS layer low-level operations. + * See inode.h for direct operations on inodes. + * See fs.h for direct operations on filesystems/devices. + * + * vfs_set_curdir - change current directory of current thread by inode + * vfs_get_curdir - retrieve inode of current directory of current thread + * vfs_get_root - get root inode for the filesystem named DEVNAME + * vfs_get_devname - get mounted device name for the filesystem passed in + */ +int vfs_set_curdir(struct inode *dir); +int vfs_get_curdir(struct inode **dir_store); +int vfs_get_root(const char *devname, struct inode **root_store); +const char *vfs_get_devname(struct fs *fs); + + +/* + * VFS layer high-level operations on pathnames + * Because namei may destroy pathnames, these all may too. + * + * vfs_open - Open or create a file. FLAGS/MODE per the syscall. + * vfs_close - Close a inode opened with vfs_open. Does not fail. + * (See vfspath.c for a discussion of why.) + * vfs_link - Create a hard link to a file. + * vfs_symlink - Create a symlink PATH containing contents CONTENTS. + * vfs_readlink - Read contents of a symlink into a uio. + * vfs_mkdir - Create a directory. MODE per the syscall. + * vfs_unlink - Delete a file/directory. + * vfs_rename - rename a file. + * vfs_chdir - Change current directory of current thread by name. + * vfs_getcwd - Retrieve name of current directory of current thread. + * + */ +int vfs_open(char *path, uint32_t open_flags, struct inode **inode_store); +int vfs_close(struct inode *node); +int vfs_link(char *old_path, char *new_path); +int vfs_symlink(char *old_path, char *new_path); +int vfs_readlink(char *path, struct iobuf *iob); +int vfs_mkdir(char *path); +int vfs_unlink(char *path); +int vfs_rename(char *old_path, char *new_path); +int vfs_chdir(char *path); +int vfs_getcwd(struct iobuf *iob); + + +/* + * VFS layer mid-level operations. + * + * vfs_lookup - Like VOP_LOOKUP, but takes a full device:path name, + * or a name relative to the current directory, and + * goes to the correct filesystem. + * vfs_lookparent - Likewise, for VOP_LOOKPARENT. + * + * Both of these may destroy the path passed in. + */ +int vfs_lookup(char *path, struct inode **node_store); +int vfs_lookup_parent(char *path, struct inode **node_store, char **endp); + +/* + * Misc + * + * vfs_set_bootfs - Set the filesystem that paths beginning with a + * slash are sent to. If not set, these paths fail + * with ENOENT. The argument should be the device + * name or volume name for the filesystem (such as + * "lhd0:") but need not have the trailing colon. + * + * vfs_get_bootfs - return the inode of the bootfs filesystem. + * + * vfs_add_fs - Add a hardwired filesystem to the VFS named device + * list. It will be accessible as "devname:". This is + * intended for filesystem-devices like emufs, and + * gizmos like Linux procfs or BSD kernfs, not for + * mounting filesystems on disk devices. + * + * vfs_add_dev - Add a device to the VFS named device list. If + * MOUNTABLE is zero, the device will be accessible + * as "DEVNAME:". If the mountable flag is set, the + * device will be accessible as "DEVNAMEraw:" and + * mountable under the name "DEVNAME". Thus, the + * console, added with MOUNTABLE not set, would be + * accessed by pathname as "con:", and lhd0, added + * with mountable set, would be accessed by + * pathname as "lhd0raw:" and mounted by passing + * "lhd0" to vfs_mount. + * + * vfs_mount - Attempt to mount a filesystem on a device. The + * device named by DEVNAME will be looked up and + * passed, along with DATA, to the supplied function + * MOUNTFUNC, which should create a struct fs and + * return it in RESULT. + * + * vfs_unmount - Unmount the filesystem presently mounted on the + * specified device. + * + * vfs_unmountall - Unmount all mounted filesystems. + */ +int vfs_set_bootfs(char *fsname); +int vfs_get_bootfs(struct inode **node_store); + +int vfs_add_fs(const char *devname, struct fs *fs); +int vfs_add_dev(const char *devname, struct inode *devnode, bool mountable); + +int vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store)); +int vfs_unmount(const char *devname); +int vfs_unmount_all(void); + +#endif /* !__KERN_FS_VFS_VFS_H__ */ + diff --git a/lab8/kern/fs/vfs/vfsdev.c b/lab8/kern/fs/vfs/vfsdev.c new file mode 100644 index 0000000..9166695 --- /dev/null +++ b/lab8/kern/fs/vfs/vfsdev.c @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// device info entry in vdev_list +typedef struct { + const char *devname; + struct inode *devnode; + struct fs *fs; + bool mountable; + list_entry_t vdev_link; +} vfs_dev_t; + +#define le2vdev(le, member) \ + to_struct((le), vfs_dev_t, member) + +static list_entry_t vdev_list; // device info list in vfs layer +static semaphore_t vdev_list_sem; + +static void +lock_vdev_list(void) { + down(&vdev_list_sem); +} + +static void +unlock_vdev_list(void) { + up(&vdev_list_sem); +} + +void +vfs_devlist_init(void) { + list_init(&vdev_list); + sem_init(&vdev_list_sem, 1); +} + +// vfs_cleanup - finally clean (or sync) fs +void +vfs_cleanup(void) { + if (!list_empty(&vdev_list)) { + lock_vdev_list(); + { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->fs != NULL) { + fsop_cleanup(vdev->fs); + } + } + } + unlock_vdev_list(); + } +} + +/* + * vfs_get_root - Given a device name (stdin, stdout, etc.), hand + * back an appropriate inode. + */ +int +vfs_get_root(const char *devname, struct inode **node_store) { + assert(devname != NULL); + int ret = -E_NO_DEV; + if (!list_empty(&vdev_list)) { + lock_vdev_list(); + { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (strcmp(devname, vdev->devname) == 0) { + struct inode *found = NULL; + if (vdev->fs != NULL) { + found = fsop_get_root(vdev->fs); + } + else if (!vdev->mountable) { + vop_ref_inc(vdev->devnode); + found = vdev->devnode; + } + if (found != NULL) { + ret = 0, *node_store = found; + } + else { + ret = -E_NA_DEV; + } + break; + } + } + } + unlock_vdev_list(); + } + return ret; +} + +/* + * vfs_get_devname - Given a filesystem, hand back the name of the device it's mounted on. + */ +const char * +vfs_get_devname(struct fs *fs) { + assert(fs != NULL); + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->fs == fs) { + return vdev->devname; + } + } + return NULL; +} + +/* + * check_devname_confilct - Is there alreadily device which has the same name? + */ +static bool +check_devname_conflict(const char *devname) { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (strcmp(vdev->devname, devname) == 0) { + return 0; + } + } + return 1; +} + + +/* +* vfs_do_add - Add a new device to the VFS layer's device table. +* +* If "mountable" is set, the device will be treated as one that expects +* to have a filesystem mounted on it, and a raw device will be created +* for direct access. +*/ +static int +vfs_do_add(const char *devname, struct inode *devnode, struct fs *fs, bool mountable) { + assert(devname != NULL); + assert((devnode == NULL && !mountable) || (devnode != NULL && check_inode_type(devnode, device))); + if (strlen(devname) > FS_MAX_DNAME_LEN) { + return -E_TOO_BIG; + } + + int ret = -E_NO_MEM; + char *s_devname; + if ((s_devname = strdup(devname)) == NULL) { + return ret; + } + + vfs_dev_t *vdev; + if ((vdev = kmalloc(sizeof(vfs_dev_t))) == NULL) { + goto failed_cleanup_name; + } + + ret = -E_EXISTS; + lock_vdev_list(); + if (!check_devname_conflict(s_devname)) { + unlock_vdev_list(); + goto failed_cleanup_vdev; + } + vdev->devname = s_devname; + vdev->devnode = devnode; + vdev->mountable = mountable; + vdev->fs = fs; + + list_add(&vdev_list, &(vdev->vdev_link)); + unlock_vdev_list(); + return 0; + +failed_cleanup_vdev: + kfree(vdev); +failed_cleanup_name: + kfree(s_devname); + return ret; +} + +/* + * vfs_add_fs - Add a new fs, by name. See vfs_do_add information for the description of + * mountable. + */ +int +vfs_add_fs(const char *devname, struct fs *fs) { + return vfs_do_add(devname, NULL, fs, 0); +} + +/* + * vfs_add_dev - Add a new device, by name. See vfs_do_add information for the description of + * mountable. + */ +int +vfs_add_dev(const char *devname, struct inode *devnode, bool mountable) { + return vfs_do_add(devname, devnode, NULL, mountable); +} + +/* + * find_mount - Look for a mountable device named DEVNAME. + * Should already hold vdev_list lock. + */ +static int +find_mount(const char *devname, vfs_dev_t **vdev_store) { + assert(devname != NULL); + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->mountable && strcmp(vdev->devname, devname) == 0) { + *vdev_store = vdev; + return 0; + } + } + return -E_NO_DEV; +} + +/* + * vfs_mount - Mount a filesystem. Once we've found the device, call MOUNTFUNC to + * set up the filesystem and hand back a struct fs. + * + * The DATA argument is passed through unchanged to MOUNTFUNC. + */ +int +vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store)) { + int ret; + lock_vdev_list(); + vfs_dev_t *vdev; + if ((ret = find_mount(devname, &vdev)) != 0) { + goto out; + } + if (vdev->fs != NULL) { + ret = -E_BUSY; + goto out; + } + assert(vdev->devname != NULL && vdev->mountable); + + struct device *dev = vop_info(vdev->devnode, device); + if ((ret = mountfunc(dev, &(vdev->fs))) == 0) { + assert(vdev->fs != NULL); + cprintf("vfs: mount %s.\n", vdev->devname); + } + +out: + unlock_vdev_list(); + return ret; +} + +/* + * vfs_unmount - Unmount a filesystem/device by name. + * First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT. + */ +int +vfs_unmount(const char *devname) { + int ret; + lock_vdev_list(); + vfs_dev_t *vdev; + if ((ret = find_mount(devname, &vdev)) != 0) { + goto out; + } + if (vdev->fs == NULL) { + ret = -E_INVAL; + goto out; + } + assert(vdev->devname != NULL && vdev->mountable); + + if ((ret = fsop_sync(vdev->fs)) != 0) { + goto out; + } + if ((ret = fsop_unmount(vdev->fs)) == 0) { + vdev->fs = NULL; + cprintf("vfs: unmount %s.\n", vdev->devname); + } + +out: + unlock_vdev_list(); + return ret; +} + +/* + * vfs_unmount_all - Global unmount function. + */ +int +vfs_unmount_all(void) { + if (!list_empty(&vdev_list)) { + lock_vdev_list(); + { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->mountable && vdev->fs != NULL) { + int ret; + if ((ret = fsop_sync(vdev->fs)) != 0) { + cprintf("vfs: warning: sync failed for %s: %e.\n", vdev->devname, ret); + continue ; + } + if ((ret = fsop_unmount(vdev->fs)) != 0) { + cprintf("vfs: warning: unmount failed for %s: %e.\n", vdev->devname, ret); + continue ; + } + vdev->fs = NULL; + cprintf("vfs: unmount %s.\n", vdev->devname); + } + } + } + unlock_vdev_list(); + } + return 0; +} + diff --git a/lab8/kern/fs/vfs/vfsfile.c b/lab8/kern/fs/vfs/vfsfile.c new file mode 100644 index 0000000..eb79dca --- /dev/null +++ b/lab8/kern/fs/vfs/vfsfile.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include + + +// open file in vfs, get/create inode for file with filename path. +int +vfs_open(char *path, uint32_t open_flags, struct inode **node_store) { + bool can_write = 0; + switch (open_flags & O_ACCMODE) { + case O_RDONLY: + break; + case O_WRONLY: + case O_RDWR: + can_write = 1; + break; + default: + return -E_INVAL; + } + + if (open_flags & O_TRUNC) { + if (!can_write) { + return -E_INVAL; + } + } + + int ret; + struct inode *node; + bool excl = (open_flags & O_EXCL) != 0; + bool create = (open_flags & O_CREAT) != 0; + ret = vfs_lookup(path, &node); + + if (ret != 0) { + if (ret == -16 && (create)) { + char *name; + struct inode *dir; + if ((ret = vfs_lookup_parent(path, &dir, &name)) != 0) { + return ret; + } + ret = vop_create(dir, name, excl, &node); + } else return ret; + } else if (excl && create) { + return -E_EXISTS; + } + assert(node != NULL); + + if ((ret = vop_open(node, open_flags)) != 0) { + vop_ref_dec(node); + return ret; + } + + vop_open_inc(node); + if (open_flags & O_TRUNC || create) { + if ((ret = vop_truncate(node, 0)) != 0) { + vop_open_dec(node); + vop_ref_dec(node); + return ret; + } + } + *node_store = node; + return 0; +} + +// close file in vfs +int +vfs_close(struct inode *node) { + vop_open_dec(node); + vop_ref_dec(node); + return 0; +} + +// unimplement +int +vfs_unlink(char *path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_rename(char *old_path, char *new_path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_link(char *old_path, char *new_path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_symlink(char *old_path, char *new_path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_readlink(char *path, struct iobuf *iob) { + return -E_UNIMP; +} + +// unimplement +int +vfs_mkdir(char *path){ + return -E_UNIMP; +} diff --git a/lab8/kern/fs/vfs/vfslookup.c b/lab8/kern/fs/vfs/vfslookup.c new file mode 100644 index 0000000..619261d --- /dev/null +++ b/lab8/kern/fs/vfs/vfslookup.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include + +/* + * get_device- Common code to pull the device name, if any, off the front of a + * path and choose the inode to begin the name lookup relative to. + */ + +static int +get_device(char *path, char **subpath, struct inode **node_store) { + int i, slash = -1, colon = -1; + for (i = 0; path[i] != '\0'; i ++) { + if (path[i] == ':') { colon = i; break; } + if (path[i] == '/') { slash = i; break; } + } + if (colon < 0 && slash != 0) { + /* * + * No colon before a slash, so no device name specified, and the slash isn't leading + * or is also absent, so this is a relative path or just a bare filename. Start from + * the current directory, and use the whole thing as the subpath. + * */ + *subpath = path; + return vfs_get_curdir(node_store); + } + if (colon > 0) { + /* device:path - get root of device's filesystem */ + path[colon] = '\0'; + + /* device:/path - skip slash, treat as device:path */ + while (path[++ colon] == '/'); + *subpath = path + colon; + return vfs_get_root(path, node_store); + } + + /* * + * we have either /path or :path + * /path is a path relative to the root of the "boot filesystem" + * :path is a path relative to the root of the current filesystem + * */ + int ret; + if (*path == '/') { + if ((ret = vfs_get_bootfs(node_store)) != 0) { + return ret; + } + } + else { + assert(*path == ':'); + struct inode *node; + if ((ret = vfs_get_curdir(&node)) != 0) { + return ret; + } + /* The current directory may not be a device, so it must have a fs. */ + assert(node->in_fs != NULL); + *node_store = fsop_get_root(node->in_fs); + vop_ref_dec(node); + } + + /* ///... or :/... */ + while (*(++ path) == '/'); + *subpath = path; + return 0; +} + +/* + * vfs_lookup - get the inode according to the path filename + */ +int +vfs_lookup(char *path, struct inode **node_store) { + int ret; + struct inode *node; + if ((ret = get_device(path, &path, &node)) != 0) { + return ret; + } + if (*path != '\0') { + ret = vop_lookup(node, path, node_store); + vop_ref_dec(node); + return ret; + } + *node_store = node; + return 0; +} + +/* + * vfs_lookup_parent - Name-to-vnode translation. + * (In BSD, both of these are subsumed by namei().) + */ +int +vfs_lookup_parent(char *path, struct inode **node_store, char **endp){ + int ret; + struct inode *node; + if ((ret = get_device(path, &path, &node)) != 0) { + return ret; + } + *endp = path; + *node_store = node; + return 0; +} diff --git a/lab8/kern/fs/vfs/vfspath.c b/lab8/kern/fs/vfs/vfspath.c new file mode 100644 index 0000000..0e164df --- /dev/null +++ b/lab8/kern/fs/vfs/vfspath.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * get_cwd_nolock - retrieve current process's working directory. without lock protect + */ +static struct inode * +get_cwd_nolock(void) { + return current->filesp->pwd; +} +/* + * set_cwd_nolock - set current working directory. + */ +static void +set_cwd_nolock(struct inode *pwd) { + current->filesp->pwd = pwd; +} + +/* + * lock_cfs - lock the fs related process on current process + */ +static void +lock_cfs(void) { + lock_files(current->filesp); +} +/* + * unlock_cfs - unlock the fs related process on current process + */ +static void +unlock_cfs(void) { + unlock_files(current->filesp); +} + +/* + * vfs_get_curdir - Get current directory as a inode. + */ +int +vfs_get_curdir(struct inode **dir_store) { + struct inode *node; + if ((node = get_cwd_nolock()) != NULL) { + vop_ref_inc(node); + *dir_store = node; + return 0; + } + return -E_NOENT; +} + +/* + * vfs_set_curdir - Set current directory as a inode. + * The passed inode must in fact be a directory. + */ +int +vfs_set_curdir(struct inode *dir) { + int ret = 0; + lock_cfs(); + struct inode *old_dir; + if ((old_dir = get_cwd_nolock()) != dir) { + if (dir != NULL) { + uint32_t type; + if ((ret = vop_gettype(dir, &type)) != 0) { + goto out; + } + if (!S_ISDIR(type)) { + ret = -E_NOTDIR; + goto out; + } + vop_ref_inc(dir); + } + set_cwd_nolock(dir); + if (old_dir != NULL) { + vop_ref_dec(old_dir); + } + } +out: + unlock_cfs(); + return ret; +} + +/* + * vfs_chdir - Set current directory, as a pathname. Use vfs_lookup to translate + * it to a inode. + */ +int +vfs_chdir(char *path) { + int ret; + struct inode *node; + if ((ret = vfs_lookup(path, &node)) == 0) { + ret = vfs_set_curdir(node); + vop_ref_dec(node); + } + return ret; +} +/* + * vfs_getcwd - retrieve current working directory(cwd). + */ +int +vfs_getcwd(struct iobuf *iob) { + int ret; + struct inode *node; + if ((ret = vfs_get_curdir(&node)) != 0) { + return ret; + } + assert(node->in_fs != NULL); + + const char *devname = vfs_get_devname(node->in_fs); + if ((ret = iobuf_move(iob, (char *)devname, strlen(devname), 1, NULL)) != 0) { + goto out; + } + char colon = ':'; + if ((ret = iobuf_move(iob, &colon, sizeof(colon), 1, NULL)) != 0) { + goto out; + } + ret = vop_namefile(node, iob); + +out: + vop_ref_dec(node); + return ret; +} + diff --git a/lab8/kern/init/entry.S b/lab8/kern/init/entry.S new file mode 100644 index 0000000..9086414 --- /dev/null +++ b/lab8/kern/init/entry.S @@ -0,0 +1,67 @@ +#include +#include + + .section .text,"ax",%progbits + .globl kern_entry +kern_entry: + # t0 := 三级页表的虚拟地址 + lui t0, %hi(boot_page_table_sv39) + # t1 := 0xffffffff40000000 即虚实映射偏移量 + li t1, 0xffffffffc0000000 - 0x80000000 + # t0 减去虚实映射偏移量 0xffffffff40000000,变为三级页表的物理地址 + sub t0, t0, t1 + # t0 >>= 12,变为三级页表的物理页号 + srli t0, t0, 12 + + # t1 := 8 << 60,设置 satp 的 MODE 字段为 Sv39 + li t1, 8 << 60 + # 将刚才计算出的预设三级页表物理页号附加到 satp 中 + or t0, t0, t1 + # 将算出的 t0(即新的MODE|页表基址物理页号) 覆盖到 satp 中 + csrw satp, t0 + # 使用 sfence.vma 指令刷新 TLB + sfence.vma + # 从此,我们给内核搭建出了一个完美的虚拟内存空间! + #nop # 可能映射的位置有些bug。。插入一个nop + + # 我们在虚拟内存空间中:随意将 sp 设置为虚拟地址! + lui sp, %hi(bootstacktop) + + # 我们在虚拟内存空间中:随意跳转到虚拟地址! + # 跳转到 kern_init + lui t0, %hi(kern_init) + addi t0, t0, %lo(kern_init) + jr t0 + +.section .data + # .align 2^12 + .align PGSHIFT + .global bootstackguard +bootstackguard: + .space PGSIZE + +.section .data + # .align 2^12 + .align PGSHIFT + .global bootstack +bootstack: + .space KSTACKSIZE + .global bootstacktop +bootstacktop: + +.section .data + # 由于我们要把这个页表放到一个页里面,因此必须 12 位对齐 + .align PGSHIFT + .global boot_page_table_sv39 +# 分配 4KiB 内存给预设的三级页表 +boot_page_table_sv39: + # 前3个页表项设置identity mapping + # VRWXAD, low memory fix-mapping + .quad (0x00000 << 10) | 0xcf + .quad (0x40000 << 10) | 0xcf + .quad (0x80000 << 10) | 0xcf + # 中间 508 个页表项均设置为 0 ,因此 V=0 ,意味着是空的(unmapped) + .zero 8 * 508 + # 0xffffffff_c0000000 map to 0x80000000 (1G) + # 设置最后一个页表项,PPN=0x80000,标志位 VRWXAD 均为 1 + .quad (0x80000 << 10) | 0xcf # VRWXAD, high memory fix-mapping diff --git a/lab8/kern/init/init.c b/lab8/kern/init/init.c new file mode 100644 index 0000000..02ec6fe --- /dev/null +++ b/lab8/kern/init/init.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int kern_init(void) __attribute__((noreturn)); +void grade_backtrace(void); +static void lab1_switch_test(void); + +int +kern_init(void) { + extern char edata[], end[]; + memset(edata, 0, end - edata); + // cons_init(); // init the console + + const char *message = "(THU.CST) os is loading ..."; + cprintf("%s\n\n", message); + + print_kerninfo(); + + // grade_backtrace(); + + pmm_init(); // init physical memory management + + sd_init(); // init SD card driver + + pic_init(); // init interrupt controller + idt_init(); // init interrupt descriptor table + + vmm_init(); // init virtual memory management + sched_init(); + proc_init(); // init process table + + ide_init(); // init ide devices + swap_init(); // init swap + fs_init(); + + clock_init(); // init clock interrupt + intr_enable(); // enable irq interrupt + + //LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test() + // user/kernel mode switch test + //lab1_switch_test(); + cpu_idle(); // run idle process +} + +void __attribute__((noinline)) +grade_backtrace2(int arg0, int arg1, int arg2, int arg3) { + mon_backtrace(0, NULL, NULL); +} + +void __attribute__((noinline)) +grade_backtrace1(int arg0, int arg1) { + grade_backtrace2(arg0, (int64_t)&arg0, arg1, (int64_t)&arg1); +} + +void __attribute__((noinline)) +grade_backtrace0(int arg0, int arg1, int arg2) { + grade_backtrace1(arg0, arg2); +} + +void +grade_backtrace(void) { + grade_backtrace0(0, (int64_t)kern_init, 0xffff0000); +} + +static void +lab1_print_cur_status(void) { + static int round = 0; + round ++; +} + +static void +lab1_switch_to_user(void) { + //LAB1 CHALLENGE 1 : TODO +} + +static void +lab1_switch_to_kernel(void) { + //LAB1 CHALLENGE 1 : TODO +} + +static void +lab1_switch_test(void) { + lab1_print_cur_status(); + cprintf("+++ switch to user mode +++\n"); + lab1_switch_to_user(); + lab1_print_cur_status(); + cprintf("+++ switch to kernel mode +++\n"); + lab1_switch_to_kernel(); + lab1_print_cur_status(); +} + diff --git a/lab8/kern/libs/readline.c b/lab8/kern/libs/readline.c new file mode 100644 index 0000000..cc1eddb --- /dev/null +++ b/lab8/kern/libs/readline.c @@ -0,0 +1,50 @@ +#include + +#define BUFSIZE 1024 +static char buf[BUFSIZE]; + +/* * + * readline - get a line from stdin + * @prompt: the string to be written to stdout + * + * The readline() function will write the input string @prompt to + * stdout first. If the @prompt is NULL or the empty string, + * no prompt is issued. + * + * This function will keep on reading characters and saving them to buffer + * 'buf' until '\n' or '\r' is encountered. + * + * Note that, if the length of string that will be read is longer than + * buffer size, the end of string will be discarded. + * + * The readline() function returns the text of the line read. If some errors + * are happened, NULL is returned. The return value is a global variable, + * thus it should be copied before it is used. + * */ +char * +readline(const char *prompt) { + if (prompt != NULL) { + cprintf("%s", prompt); + } + int i = 0, c; + while (1) { + c = getchar(); + if (c < 0) { + return NULL; + } + else if (c >= ' ' && i < BUFSIZE - 1) { + cputchar(c); + buf[i ++] = c; + } + else if (c == '\b' && i > 0) { + cputchar(c); + i --; + } + else if (c == '\n' || c == '\r') { + cputchar(c); + buf[i] = '\0'; + return buf; + } + } +} + diff --git a/lab8/kern/libs/stdio.c b/lab8/kern/libs/stdio.c new file mode 100644 index 0000000..aaade41 --- /dev/null +++ b/lab8/kern/libs/stdio.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +/* HIGH level console I/O */ + +/* * + * cputch - writes a single character @c to stdout, and it will + * increace the value of counter pointed by @cnt. + * */ +static void +cputch(int c, int *cnt) { + cons_putc(c); + (*cnt) ++; +} + +/* * + * vcprintf - format a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * + * Call this function if you are already dealing with a va_list. + * Or you probably want cprintf() instead. + * */ +int +vcprintf(const char *fmt, va_list ap) { + int cnt = 0; + vprintfmt((void*)cputch, NO_FD, &cnt, fmt, ap); + return cnt; +} + +/* * + * cprintf - formats a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * */ +int +cprintf(const char *fmt, ...) { + va_list ap; + int cnt; + va_start(ap, fmt); + cnt = vcprintf(fmt, ap); + va_end(ap); + return cnt; +} + +/* cputchar - writes a single character to stdout */ +void +cputchar(int c) { + cons_putc(c); +} + +/* * + * cputs- writes the string pointed by @str to stdout and + * appends a newline character. + * */ +int +cputs(const char *str) { + int cnt = 0; + char c; + while ((c = *str ++) != '\0') { + cputch(c, &cnt); + } + cputch('\n', &cnt); + return cnt; +} + +/* getchar - reads a single non-zero character from stdin */ +int +getchar(void) { + int c; + while ((c = cons_getc()) == 0) + /* do nothing */; + return c; +} + diff --git a/lab8/kern/libs/string.c b/lab8/kern/libs/string.c new file mode 100644 index 0000000..4b3548e --- /dev/null +++ b/lab8/kern/libs/string.c @@ -0,0 +1,26 @@ +#include +#include + +char * +strdup(const char *src) { + char *dst; + size_t len = strlen(src); + if ((dst = kmalloc(len + 1)) != NULL) { + memcpy(dst, src, len); + dst[len] = '\0'; + } + return dst; +} + +char * +stradd(const char *src1, const char *src2) { + char *ret, *dst; + size_t len1 = strlen(src1), len2 = strlen(src2); + if ((ret = dst = kmalloc(len1 + len2 + 1)) != NULL) { + memcpy(dst, src1, len1), dst += len1; + memcpy(dst, src2, len2), dst += len2; + *dst = '\0'; + } + return ret; +} + diff --git a/lab8/kern/libs/util.c b/lab8/kern/libs/util.c new file mode 100644 index 0000000..ef50007 --- /dev/null +++ b/lab8/kern/libs/util.c @@ -0,0 +1,26 @@ +// +// Created by lumin on 2020/11/16. +// + +#include + +void set_bits_value(volatile uint32_t *bits, uint32_t mask, uint32_t value) +{ + uint32_t masked_origin_value = (*bits) & ~mask; + *bits = masked_origin_value | (value & mask); +} + +void set_bits_value_offset(volatile uint32_t *bits, uint32_t mask, uint32_t value, uint32_t offset) +{ + set_bits_value(bits, mask << offset, value << offset); +} + +void set_bit(volatile uint32_t *bits, uint32_t offset) +{ + set_bits_value(bits, 1 << offset, 1 << offset); +} + +void clear_bit(volatile uint32_t *bits, uint32_t offset) +{ + set_bits_value(bits, 1 << offset, 0); +} \ No newline at end of file diff --git a/lab8/kern/mm/default_pmm.c b/lab8/kern/mm/default_pmm.c new file mode 100644 index 0000000..c11c7c3 --- /dev/null +++ b/lab8/kern/mm/default_pmm.c @@ -0,0 +1,306 @@ +#include +#include +#include +#include + +/* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and, + on receiving a request for memory, scans along the list for the first block that is large enough to + satisfy the request. If the chosen block is significantly larger than that requested, then it is + usually split, and the remainder added to the list as another free block. + Please see Page 196~198, Section 8.2 of Yan Wei Min's chinese book "Data Structure -- C programming language" +*/ +// LAB2 EXERCISE 1: YOUR CODE +// you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages. +/* + * Details of FFMA + * (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list. + * The struct free_area_t is used for the management of free mem blocks. At first you should + * be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation. + * You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev + * Another tricky method is to transform a general list struct to a special struct (such as struct page): + * you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.) + * (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0. + * free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks. + * (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap + * This fun is used to init a free block (with parameter: addr_base, page_number). + * First you should init each page (in memlayout.h) in this free block, include: + * p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c), + * the bit PG_reserved is setted in p->flags) + * if this page is free and is not the first page of free block, p->property should be set to 0. + * if this page is free and is the first page of free block, p->property should be set to total num of block. + * p->ref should be 0, because now p is free and no reference. + * We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); ) + * Finally, we should sum the number of free mem block: nr_free+=n + * (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr + * of malloced block. + * (4.1) So you should search freelist like this: + * list_entry_t le = &free_list; + * while((le=list_next(le)) != &free_list) { + * .... + * (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n? + * struct Page *p = le2page(le, page_link); + * if(p->property >= n){ ... + * (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced. + * Some flag bits of this page should be setted: PG_reserved =1, PG_property =0 + * unlink the pages from free_list + * (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block, + * (such as: le2page(le,page_link))->property = p->property - n;) + * (4.1.3) re-caluclate nr_free (number of the the rest of all free block) + * (4.1.4) return p + * (4.2) If we can not find a free block (block size >=n), then return NULL + * (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks. + * (5.1) according the base addr of withdrawed blocks, search free list, find the correct position + * (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before) + * (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty) + * (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly. + */ +free_area_t free_area; + +#define free_list (free_area.free_list) +#define nr_free (free_area.nr_free) + +static void +default_init(void) { + list_init(&free_list); + nr_free = 0; +} + +static void +default_init_memmap(struct Page *base, size_t n) { + assert(n > 0); + struct Page *p = base; + for (; p != base + n; p ++) { + assert(PageReserved(p)); + p->flags = p->property = 0; + set_page_ref(p, 0); + } + base->property = n; + SetPageProperty(base); + nr_free += n; + if (list_empty(&free_list)) { + list_add(&free_list, &(base->page_link)); + } else { + list_entry_t* le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page* page = le2page(le, page_link); + if (base < page) { + list_add_before(le, &(base->page_link)); + break; + } else if (list_next(le) == &free_list) { + list_add(le, &(base->page_link)); + } + } + } +} + +static struct Page * +default_alloc_pages(size_t n) { + assert(n > 0); + if (n > nr_free) { + return NULL; + } + struct Page *page = NULL; + list_entry_t *le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + if (p->property >= n) { + page = p; + break; + } + } + if (page != NULL) { + list_entry_t* prev = list_prev(&(page->page_link)); + list_del(&(page->page_link)); + if (page->property > n) { + struct Page *p = page + n; + p->property = page->property - n; + SetPageProperty(p); + list_add(prev, &(p->page_link)); + } + nr_free -= n; + ClearPageProperty(page); + } + return page; +} + +static void +default_free_pages(struct Page *base, size_t n) { + assert(n > 0); + struct Page *p = base; + for (; p != base + n; p ++) { + assert(!PageReserved(p) && !PageProperty(p)); + p->flags = 0; + set_page_ref(p, 0); + } + base->property = n; + SetPageProperty(base); + nr_free += n; + + if (list_empty(&free_list)) { + list_add(&free_list, &(base->page_link)); + } else { + list_entry_t* le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page* page = le2page(le, page_link); + if (base < page) { + list_add_before(le, &(base->page_link)); + break; + } else if (list_next(le) == &free_list) { + list_add(le, &(base->page_link)); + } + } + } + + list_entry_t* le = list_prev(&(base->page_link)); + if (le != &free_list) { + p = le2page(le, page_link); + if (p + p->property == base) { + p->property += base->property; + ClearPageProperty(base); + list_del(&(base->page_link)); + base = p; + } + } + + le = list_next(&(base->page_link)); + if (le != &free_list) { + p = le2page(le, page_link); + if (base + base->property == p) { + base->property += p->property; + ClearPageProperty(p); + list_del(&(p->page_link)); + } + } +} + +static size_t +default_nr_free_pages(void) { + return nr_free; +} + +static void +basic_check(void) { + struct Page *p0, *p1, *p2; + p0 = p1 = p2 = NULL; + assert((p0 = alloc_page()) != NULL); + assert((p1 = alloc_page()) != NULL); + assert((p2 = alloc_page()) != NULL); + + assert(p0 != p1 && p0 != p2 && p1 != p2); + assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0); + + assert(page2pa(p0) < npage * PGSIZE); + assert(page2pa(p1) < npage * PGSIZE); + assert(page2pa(p2) < npage * PGSIZE); + + list_entry_t free_list_store = free_list; + list_init(&free_list); + assert(list_empty(&free_list)); + + unsigned int nr_free_store = nr_free; + nr_free = 0; + + assert(alloc_page() == NULL); + + free_page(p0); + free_page(p1); + free_page(p2); + assert(nr_free == 3); + + assert((p0 = alloc_page()) != NULL); + assert((p1 = alloc_page()) != NULL); + assert((p2 = alloc_page()) != NULL); + + assert(alloc_page() == NULL); + + free_page(p0); + assert(!list_empty(&free_list)); + + struct Page *p; + assert((p = alloc_page()) == p0); + assert(alloc_page() == NULL); + + assert(nr_free == 0); + free_list = free_list_store; + nr_free = nr_free_store; + + free_page(p); + free_page(p1); + free_page(p2); +} + +// LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1) +// NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions! +static void +default_check(void) { + int count = 0, total = 0; + list_entry_t *le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + assert(PageProperty(p)); + count ++, total += p->property; + } + assert(total == nr_free_pages()); + + basic_check(); + + struct Page *p0 = alloc_pages(5), *p1, *p2; + assert(p0 != NULL); + assert(!PageProperty(p0)); + + list_entry_t free_list_store = free_list; + list_init(&free_list); + assert(list_empty(&free_list)); + assert(alloc_page() == NULL); + + unsigned int nr_free_store = nr_free; + nr_free = 0; + + free_pages(p0 + 2, 3); + assert(alloc_pages(4) == NULL); + assert(PageProperty(p0 + 2) && p0[2].property == 3); + assert((p1 = alloc_pages(3)) != NULL); + assert(alloc_page() == NULL); + assert(p0 + 2 == p1); + + p2 = p0 + 1; + free_page(p0); + free_pages(p1, 3); + assert(PageProperty(p0) && p0->property == 1); + assert(PageProperty(p1) && p1->property == 3); + + assert((p0 = alloc_page()) == p2 - 1); + free_page(p0); + assert((p0 = alloc_pages(2)) == p2 + 1); + + free_pages(p0, 2); + free_page(p2); + + assert((p0 = alloc_pages(5)) != NULL); + assert(alloc_page() == NULL); + + assert(nr_free == 0); + nr_free = nr_free_store; + + free_list = free_list_store; + free_pages(p0, 5); + + le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + count --, total -= p->property; + } + assert(count == 0); + assert(total == 0); +} + +const struct pmm_manager default_pmm_manager = { + .name = "default_pmm_manager", + .init = default_init, + .init_memmap = default_init_memmap, + .alloc_pages = default_alloc_pages, + .free_pages = default_free_pages, + .nr_free_pages = default_nr_free_pages, + .check = default_check, +}; + diff --git a/lab8/kern/mm/default_pmm.h b/lab8/kern/mm/default_pmm.h new file mode 100644 index 0000000..5f4e6fc --- /dev/null +++ b/lab8/kern/mm/default_pmm.h @@ -0,0 +1,9 @@ +#ifndef __KERN_MM_DEFAULT_PMM_H__ +#define __KERN_MM_DEFAULT_PMM_H__ + +#include + +extern const struct pmm_manager default_pmm_manager; +extern free_area_t free_area; +#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */ + diff --git a/lab8/kern/mm/kmalloc.c b/lab8/kern/mm/kmalloc.c new file mode 100644 index 0000000..ed3e29a --- /dev/null +++ b/lab8/kern/mm/kmalloc.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * SLOB Allocator: Simple List Of Blocks + * + * Matt Mackall 12/30/03 + * + * How SLOB works: + * + * The core of SLOB is a traditional K&R style heap allocator, with + * support for returning aligned objects. The granularity of this + * allocator is 8 bytes on x86, though it's perhaps possible to reduce + * this to 4 if it's deemed worth the effort. The slob heap is a + * singly-linked list of pages from __get_free_page, grown on demand + * and allocation from the heap is currently first-fit. + * + * Above this is an implementation of kmalloc/kfree. Blocks returned + * from kmalloc are 8-byte aligned and prepended with a 8-byte header. + * If kmalloc is asked for objects of PAGE_SIZE or larger, it calls + * __get_free_pages directly so that it can return page-aligned blocks + * and keeps a linked list of such pages and their orders. These + * objects are detected in kfree() by their page alignment. + * + * SLAB is emulated on top of SLOB by simply calling constructors and + * destructors for every SLAB allocation. Objects are returned with + * the 8-byte alignment unless the SLAB_MUST_HWCACHE_ALIGN flag is + * set, in which case the low-level allocator will fragment blocks to + * create the proper alignment. Again, objects of page-size or greater + * are allocated by calling __get_free_pages. As SLAB objects know + * their size, no separate size bookkeeping is necessary and there is + * essentially no allocation space overhead. + */ + + +//some helper +#define spin_lock_irqsave(l, f) local_intr_save(f) +#define spin_unlock_irqrestore(l, f) local_intr_restore(f) +typedef unsigned int gfp_t; +#ifndef PAGE_SIZE +#define PAGE_SIZE PGSIZE +#endif + +#ifndef L1_CACHE_BYTES +#define L1_CACHE_BYTES 64 +#endif + +#ifndef ALIGN +#define ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1))) +#endif + + +struct slob_block { + int units; + struct slob_block *next; +}; +typedef struct slob_block slob_t; + +#define SLOB_UNIT sizeof(slob_t) +#define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT) +#define SLOB_ALIGN L1_CACHE_BYTES + +struct bigblock { + int order; + void *pages; + struct bigblock *next; +}; +typedef struct bigblock bigblock_t; + +static slob_t arena = { .next = &arena, .units = 1 }; +static slob_t *slobfree = &arena; +static bigblock_t *bigblocks; + + +static void* __slob_get_free_pages(gfp_t gfp, int order) +{ + struct Page * page = alloc_pages(1 << order); + if(!page) + return NULL; + return page2kva(page); +} + +#define __slob_get_free_page(gfp) __slob_get_free_pages(gfp, 0) + +static inline void __slob_free_pages(unsigned long kva, int order) +{ + free_pages(kva2page(kva), 1 << order); +} + +static void slob_free(void *b, int size); + +static void *slob_alloc(size_t size, gfp_t gfp, int align) +{ + assert( (size + SLOB_UNIT) < PAGE_SIZE ); + + slob_t *prev, *cur, *aligned = 0; + int delta = 0, units = SLOB_UNITS(size); + unsigned long flags; + + spin_lock_irqsave(&slob_lock, flags); + prev = slobfree; + for (cur = prev->next; ; prev = cur, cur = cur->next) { + if (align) { + aligned = (slob_t *)ALIGN((unsigned long)cur, align); + delta = aligned - cur; + } + if (cur->units >= units + delta) { /* room enough? */ + if (delta) { /* need to fragment head to align? */ + aligned->units = cur->units - delta; + aligned->next = cur->next; + cur->next = aligned; + cur->units = delta; + prev = cur; + cur = aligned; + } + + if (cur->units == units) /* exact fit? */ + prev->next = cur->next; /* unlink */ + else { /* fragment */ + prev->next = cur + units; + prev->next->units = cur->units - units; + prev->next->next = cur->next; + cur->units = units; + } + + slobfree = prev; + spin_unlock_irqrestore(&slob_lock, flags); + return cur; + } + if (cur == slobfree) { + spin_unlock_irqrestore(&slob_lock, flags); + + if (size == PAGE_SIZE) /* trying to shrink arena? */ + return 0; + + cur = (slob_t *)__slob_get_free_page(gfp); + if (!cur) + return 0; + + slob_free(cur, PAGE_SIZE); + spin_lock_irqsave(&slob_lock, flags); + cur = slobfree; + } + } +} + +static void slob_free(void *block, int size) +{ + slob_t *cur, *b = (slob_t *)block; + unsigned long flags; + + if (!block) + return; + + if (size) + b->units = SLOB_UNITS(size); + + /* Find reinsertion point */ + spin_lock_irqsave(&slob_lock, flags); + for (cur = slobfree; !(b > cur && b < cur->next); cur = cur->next) + if (cur >= cur->next && (b > cur || b < cur->next)) + break; + + if (b + b->units == cur->next) { + b->units += cur->next->units; + b->next = cur->next->next; + } else + b->next = cur->next; + + if (cur + cur->units == b) { + cur->units += b->units; + cur->next = b->next; + } else + cur->next = b; + + slobfree = cur; + + spin_unlock_irqrestore(&slob_lock, flags); +} + + + +void +slob_init(void) { + cprintf("use SLOB allocator\n"); +} + +inline void +kmalloc_init(void) { + slob_init(); + cprintf("kmalloc_init() succeeded!\n"); +} + +size_t +slob_allocated(void) { + return 0; +} + +size_t +kallocated(void) { + return slob_allocated(); +} + +static int find_order(int size) +{ + int order = 0; + for ( ; size > 4096 ; size >>=1) + order++; + return order; +} + +static void *__kmalloc(size_t size, gfp_t gfp) +{ + slob_t *m; + bigblock_t *bb; + unsigned long flags; + + if (size < PAGE_SIZE - SLOB_UNIT) { + m = slob_alloc(size + SLOB_UNIT, gfp, 0); + return m ? (void *)(m + 1) : 0; + } + + bb = slob_alloc(sizeof(bigblock_t), gfp, 0); + if (!bb) + return 0; + + bb->order = find_order(size); + bb->pages = (void *)__slob_get_free_pages(gfp, bb->order); + + if (bb->pages) { + spin_lock_irqsave(&block_lock, flags); + bb->next = bigblocks; + bigblocks = bb; + spin_unlock_irqrestore(&block_lock, flags); + return bb->pages; + } + + slob_free(bb, sizeof(bigblock_t)); + return 0; +} + +void * +kmalloc(size_t size) +{ + return __kmalloc(size, 0); +} + + +void kfree(void *block) +{ + bigblock_t *bb, **last = &bigblocks; + unsigned long flags; + + if (!block) + return; + + if (!((unsigned long)block & (PAGE_SIZE-1))) { + /* might be on the big block list */ + spin_lock_irqsave(&block_lock, flags); + for (bb = bigblocks; bb; last = &bb->next, bb = bb->next) { + if (bb->pages == block) { + *last = bb->next; + spin_unlock_irqrestore(&block_lock, flags); + __slob_free_pages((unsigned long)block, bb->order); + slob_free(bb, sizeof(bigblock_t)); + return; + } + } + spin_unlock_irqrestore(&block_lock, flags); + } + + slob_free((slob_t *)block - 1, 0); + return; +} + + +unsigned int ksize(const void *block) +{ + bigblock_t *bb; + unsigned long flags; + + if (!block) + return 0; + + if (!((unsigned long)block & (PAGE_SIZE-1))) { + spin_lock_irqsave(&block_lock, flags); + for (bb = bigblocks; bb; bb = bb->next) + if (bb->pages == block) { + spin_unlock_irqrestore(&slob_lock, flags); + return PAGE_SIZE << bb->order; + } + spin_unlock_irqrestore(&block_lock, flags); + } + + return ((slob_t *)block - 1)->units * SLOB_UNIT; +} + + + diff --git a/lab8/kern/mm/kmalloc.h b/lab8/kern/mm/kmalloc.h new file mode 100644 index 0000000..0fd2628 --- /dev/null +++ b/lab8/kern/mm/kmalloc.h @@ -0,0 +1,16 @@ +#ifndef __KERN_MM_KMALLOC_H__ +#define __KERN_MM_KMALLOC_H__ + +#include + +#define KMALLOC_MAX_ORDER 10 + +void kmalloc_init(void); + +void *kmalloc(size_t n); +void kfree(void *objp); + +size_t kallocated(void); + +#endif /* !__KERN_MM_KMALLOC_H__ */ + diff --git a/lab8/kern/mm/memlayout.h b/lab8/kern/mm/memlayout.h new file mode 100644 index 0000000..1e29ae6 --- /dev/null +++ b/lab8/kern/mm/memlayout.h @@ -0,0 +1,133 @@ +#ifndef __KERN_MM_MEMLAYOUT_H__ +#define __KERN_MM_MEMLAYOUT_H__ + +/* This file contains the definitions for memory management in our OS. */ + +/* * + * Virtual memory map: Permissions + * kernel/user + * + * 4G ------------------> +---------------------------------+ + * | | + * | Empty Memory (*) | + * | | + * +---------------------------------+ 0xFB000000 + * | Cur. Page Table (Kern, RW) | RW/-- PTSIZE + * VPT -----------------> +---------------------------------+ 0xFAC00000 + * | Invalid Memory (*) | --/-- + * KERNTOP -------------> +---------------------------------+ 0xF8000000 + * | | + * | Remapped Physical Memory | RW/-- KMEMSIZE + * | | + * KERNBASE ------------> +---------------------------------+ 0xC0000000 + * | Invalid Memory (*) | --/-- + * USERTOP -------------> +---------------------------------+ 0xB0000000 + * | User stack | + * IOTOP ---------------> +---------------------------------+ 0x60000000 + * | Fixed mapping for IO | --/-- + * IOBASE --------------> +---------------------------------+ 0x30000000 + * | | + * : : + * | ~~~~~~~~~~~~~~~~ | + * : : + * | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | User Program & Heap | + * UTEXT ---------------> +---------------------------------+ 0x00800000 + * | Invalid Memory (*) | --/-- + * | - - - - - - - - - - - - - - - | + * | User STAB Data (optional) | + * USERBASE, USTAB------> +---------------------------------+ 0x00200000 + * | Invalid Memory (*) | --/-- + * 0 -------------------> +---------------------------------+ 0x00000000 + * (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped. + * "Empty Memory" is normally unmapped, but user programs may map pages + * there if desired. + * + * */ + +/* All physical memory mapped at this address */ +#define KERNBASE 0xFFFFFFFFC0020000 // = 0x80020000(物理内存里内核的起始位置, KERN_BEGIN_PADDR) + 0xFFFFFFFF40000000(偏移量, PHYSICAL_MEMORY_OFFSET) +//把原有内存映射到虚拟内存空间的最后一页 +#define KMEMSIZE 0x5E0000 // the maximum amount of physical memory +// 0x5E0000 = 0x600000 - 0x20000 +// k210的RAM为 0x80000000到0x80600000, 6MiB, 0x80000000到0x80020000被RustSBI占用 +#define KERNTOP (KERNBASE + KMEMSIZE) // 0x80600000对应的虚拟地址 + +#define PHYSICAL_MEMORY_END 0x80600000 +#define PHYSICAL_MEMORY_OFFSET 0xFFFFFFFF40000000 +#define KERNEL_BEGIN_PADDR 0x80020000 +#define KERNEL_BEGIN_VADDR 0xFFFFFFFFC0020000 + +#define IO_BEGIN_PADDR (0x30000000U) +#define IOSIZE (0x30000000U) +#define IOTOP (IO_BEGIN_PADDR + IOSIZE) + +#define KSTACKPAGE 2 // # of pages in kernel stack +#define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack + +#define USERTOP 0x80000000 +#define USTACKTOP USERTOP +#define USTACKPAGE 256 // # of pages in user stack +#define USTACKSIZE (USTACKPAGE * PGSIZE) // sizeof user stack + +#define USERBASE 0x00200000 +#define UTEXT 0x00800000 // where user programs generally begin +#define USTAB USERBASE // the location of the user STABS data structure + +#define USER_ACCESS(start, end) \ +(USERBASE <= (start) && (start) < (end) && (end) <= USERTOP) + +#define KERN_ACCESS(start, end) \ +(KERNBASE <= (start) && (start) < (end) && (end) <= KERNTOP) + +#ifndef __ASSEMBLER__ + +#include +#include +#include + +typedef uintptr_t pte_t; +typedef uintptr_t pde_t; +typedef pte_t swap_entry_t; //the pte can also be a swap entry + +/* * + * struct Page - Page descriptor structures. Each Page describes one + * physical page. In kern/mm/pmm.h, you can find lots of useful functions + * that convert Page to other data types, such as physical address. + * */ +struct Page { + int ref; // page frame's reference counter + uint64_t flags; // array of flags that describe the status of the page frame + unsigned int property; // the num of free block, used in first fit pm manager + list_entry_t page_link; // free list link + list_entry_t pra_page_link; // used for pra (page replace algorithm) + uintptr_t pra_vaddr; // used for pra (page replace algorithm) +}; + +/* Flags describing the status of a page frame */ +#define PG_reserved 0 // if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0 +#define PG_property 1 // if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page. + +#define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags)) +#define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags)) +#define PageReserved(page) test_bit(PG_reserved, &((page)->flags)) +#define SetPageProperty(page) set_bit(PG_property, &((page)->flags)) +#define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags)) +#define PageProperty(page) test_bit(PG_property, &((page)->flags)) + +// convert list entry to page +#define le2page(le, member) \ + to_struct((le), struct Page, member) + +/* free_area_t - maintains a doubly linked list to record free (unused) pages */ +typedef struct { + list_entry_t free_list; // the list header + unsigned int nr_free; // # of free pages in this free list +} free_area_t; + + +#endif /* !__ASSEMBLER__ */ + +#endif /* !__KERN_MM_MEMLAYOUT_H__ */ + diff --git a/lab8/kern/mm/mmu.h b/lab8/kern/mm/mmu.h new file mode 100644 index 0000000..64d31e2 --- /dev/null +++ b/lab8/kern/mm/mmu.h @@ -0,0 +1,92 @@ +#ifndef __KERN_MM_MMU_H__ +#define __KERN_MM_MMU_H__ + +#ifndef __ASSEMBLER__ +#include +#endif + +// A linear address 'la' has a three-part structure as follows: +// +// +--------10------+-------10-------+---------12----------+ +// | Page Directory | Page Table | Offset within Page | +// | Index | Index | | +// +----------------+----------------+---------------------+ +// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/ +// \----------- PPN(la) -----------/ +// +// The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown. +// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la), +// use PGADDR(PDX(la), PTX(la), PGOFF(la)). + +// RISC-V uses 32-bit virtual address to access 34-bit physical address! +// Sv32 page table entry: +// +---------12----------+--------10-------+---2----+-------8-------+ +// | PPN[1] | PPN[0] |Reserved|D|A|G|U|X|W|R|V| +// +---------12----------+-----------------+--------+---------------+ + +/* + * RV32Sv32 page table entry: + * | 31 10 | 9 7 | 6 | 5 | 4 1 | 0 + * PFN reserved for SW D R TYPE V + * + * RV64Sv39 / RV64Sv48 page table entry: + * | 63 48 | 47 10 | 9 7 | 6 | 5 | 4 1 | 0 + * reserved for HW PFN reserved for SW D R TYPE V + */ + +// page directory index +#define PDX1(la) ((((uintptr_t)(la)) >> PDX1SHIFT) & 0x1FF) +#define PDX0(la) ((((uintptr_t)(la)) >> PDX0SHIFT) & 0x1FF) + +// page table index +#define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x1FF) + +// page number field of address +#define PPN(la) (((uintptr_t)(la)) >> PTXSHIFT) + +// offset in page +#define PGOFF(la) (((uintptr_t)(la)) & 0xFFF) + +// construct linear address from indexes and offset +#define PGADDR(d1, d0, t, o) ((uintptr_t)((d1) << PDX1SHIFT | (d0) << PDX0SHIFT | (t) << PTXSHIFT | (o))) + +// address in page table or page directory entry +#define PTE_ADDR(pte) (((uintptr_t)(pte) & ~0x3FF) << (PTXSHIFT - PTE_PPN_SHIFT)) +#define PDE_ADDR(pde) PTE_ADDR(pde) + +/* page directory and page table constants */ +#define NPDEENTRY 512 // page directory entries per page directory +#define NPTEENTRY 512 // page table entries per page table + +#define PGSIZE 4096 // bytes mapped by a page +#define PGSHIFT 12 // log2(PGSIZE) +#define PTSIZE (PGSIZE * NPTEENTRY) // bytes mapped by a page directory entry +#define PTSHIFT 21 // log2(PTSIZE) +#define PDSIZE (PTSIZE * NPDEENTRY) // bytes mapped by a page directory + +#define PTXSHIFT 12 // offset of PTX in a linear address +#define PDX0SHIFT 21 // offset of PDX in a linear address +#define PDX1SHIFT 30 +#define PTE_PPN_SHIFT 10 // offset of PPN in a physical address + +// page table entry (PTE) fields +#define PTE_V 0x001 // Valid +#define PTE_R 0x002 // Read +#define PTE_W 0x004 // Write +#define PTE_X 0x008 // Execute +#define PTE_U 0x010 // User +#define PTE_G 0x020 // Global +#define PTE_A 0x040 // Accessed +#define PTE_D 0x080 // Dirty +#define PTE_SOFT 0x300 // Reserved for Software + +#define PAGE_TABLE_DIR (PTE_V) +#define READ_ONLY (PTE_R | PTE_V) +#define READ_WRITE (PTE_R | PTE_W | PTE_V) +#define EXEC_ONLY (PTE_X | PTE_V) +#define READ_EXEC (PTE_R | PTE_X | PTE_V) +#define READ_WRITE_EXEC (PTE_R | PTE_W | PTE_X | PTE_V) + +#define PTE_USER (PTE_R | PTE_W | PTE_X | PTE_U | PTE_V) + +#endif /* !__KERN_MM_MMU_H__ */ diff --git a/lab8/kern/mm/pmm.c b/lab8/kern/mm/pmm.c new file mode 100644 index 0000000..7356462 --- /dev/null +++ b/lab8/kern/mm/pmm.c @@ -0,0 +1,773 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// virtual address of physical page array +struct Page *pages; +// amount of physical memory (in pages) +size_t npage = 0; +// The kernel image is mapped at VA=KERNBASE and PA=info.base +uint_t va_pa_offset; +// memory starts at 0x80000000 in RISC-V +const size_t nbase = DRAM_BASE / PGSIZE; + +// virtual address of boot-time page directory +pde_t *boot_pgdir = NULL; +// physical address of boot-time page directory +uintptr_t boot_cr3; + +// physical memory management +const struct pmm_manager *pmm_manager; + +const io_region_t io_regions[IO_REGION_NUM] = + { + [0] = {IO_REGION_START0, IO_REGION_END0, IO_REGION_END0 - IO_REGION_START0}, + [1] = {IO_REGION_START1, IO_REGION_END1, IO_REGION_END1 - IO_REGION_START1}, + [2] = {IO_REGION_START2, IO_REGION_END2, IO_REGION_END2 - IO_REGION_START2}, + [3] = {IO_REGION_START3, IO_REGION_END3, IO_REGION_END3 - IO_REGION_START3}, + }; + +static void check_alloc_page(void); +static void check_pgdir(void); +static void check_boot_pgdir(void); + +// init_pmm_manager - initialize a pmm_manager instance +static void init_pmm_manager(void) { + pmm_manager = &default_pmm_manager; + cprintf("memory management: %s\n", pmm_manager->name); + pmm_manager->init(); +} + +// init_memmap - call pmm->init_memmap to build Page struct for free memory +static void init_memmap(struct Page *base, size_t n) { + pmm_manager->init_memmap(base, n); +} + +// alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE +// memory +struct Page *alloc_pages(size_t n) { + struct Page *page = NULL; + bool intr_flag; + + while (1) { + local_intr_save(intr_flag); + { + page = pmm_manager->alloc_pages(n); + } + local_intr_restore(intr_flag); + + if (page != NULL || n > 1 || swap_init_ok == 0) break; + + extern struct mm_struct *check_mm_struct; + // cprintf("page %x, call swap_out in alloc_pages %d\n",page, n); + swap_out(check_mm_struct, n, 0); + } + // cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages)); + return page; +} + +// free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory +void free_pages(struct Page *base, size_t n) { + bool intr_flag; + local_intr_save(intr_flag); + { + pmm_manager->free_pages(base, n); + } + local_intr_restore(intr_flag); +} + +// nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE) +// of current free memory +size_t nr_free_pages(void) { + size_t ret; + bool intr_flag; + local_intr_save(intr_flag); + { + ret = pmm_manager->nr_free_pages(); + } + local_intr_restore(intr_flag); + return ret; +} + +/* pmm_init - initialize the physical memory management */ +static void page_init(void) { + extern char kern_entry[]; + + va_pa_offset = KERNBASE - 0x80020000; + + uint_t mem_begin = KERNEL_BEGIN_PADDR; + uint_t mem_size = PHYSICAL_MEMORY_END - KERNEL_BEGIN_PADDR; + uint_t mem_end = PHYSICAL_MEMORY_END; + + cprintf("physcial memory map:\n"); + cprintf(" memory: 0x%08lx, [0x%08lx, 0x%08lx].\n", mem_size, mem_begin, + mem_end - 1); + + uint64_t maxpa = mem_end; + + if (maxpa > KERNTOP) { + maxpa = KERNTOP; + } + + extern char end[]; + + npage = maxpa / PGSIZE; + // BBL has put the initial page table at the first available page after the + // kernel + // so stay away from it by adding extra offset to end + pages = (struct Page *)ROUNDUP((void *)end, PGSIZE); + + for (size_t i = 0; i < npage - nbase; i++) { + SetPageReserved(pages + i); + } + + uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * (npage - nbase)); + + mem_begin = ROUNDUP(freemem, PGSIZE); + mem_end = ROUNDDOWN(mem_end, PGSIZE); + if (freemem < mem_end) { + init_memmap(pa2page(mem_begin), (mem_end - mem_begin) / PGSIZE); + } + cprintf("vapaofset is %llu\n",va_pa_offset); +} + +// boot_map_segment - setup&enable the paging mechanism +// parameters +// la: linear address of this memory need to map (after x86 segment map) +// size: memory size +// pa: physical address of this memory +// perm: permission of this memory +static void boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, + uintptr_t pa, uint32_t perm) { + assert(PGOFF(la) == PGOFF(pa)); + size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE; + la = ROUNDDOWN(la, PGSIZE); + pa = ROUNDDOWN(pa, PGSIZE); + for (; n > 0; n--, la += PGSIZE, pa += PGSIZE) { + pte_t *ptep = get_pte(pgdir, la, 1); + assert(ptep != NULL); + *ptep = pte_create(pa >> PGSHIFT, PTE_V | perm); + } +} + +// boot_alloc_page - allocate one page using pmm->alloc_pages(1) +// return value: the kernel virtual address of this allocated page +// note: this function is used to get the memory for PDT(Page Directory +// Table)&PT(Page Table) +static void *boot_alloc_page(void) { + struct Page *p = alloc_page(); + if (p == NULL) { + panic("boot_alloc_page failed.\n"); + } + return page2kva(p); +} + +void setup_kernel_io_mapping(pde_t *pgdir){ + for (int i = 0; i < IO_REGION_NUM; i++) + boot_map_segment(pgdir, io_regions[i].io_start, io_regions[i].io_size, io_regions[i].io_start, PTE_R | PTE_W); +} + +void free_kernel_io_mapping(pde_t *pgdir){ + for (int i = 0; i < IO_REGION_NUM; i++) + unmap_range_io(pgdir, io_regions[i].io_start, io_regions[i].io_end); + for (int i = 0; i < IO_REGION_NUM; i++) + exit_range(pgdir, io_regions[i].io_start, io_regions[i].io_end, 0); +} + +/** + * from transient boot pgdir switch to a new one and add some protection + * 1. switch pgdir + * 2. set refined permission(rx, rw...) + * 3. set previous transient boot pgdir and another dedicated page + * as guard pages for kernel stack + */ +static void +switch_kernel_memorylayout(){ + /** + * Free intermediate here is uncessary because initially we use + * big-big-big page such that not intermediate page is occupied + */ + + // new page directory + pde_t *kern_pgdir = (pde_t *)boot_alloc_page(); + memset(kern_pgdir, 0, PGSIZE); + + // insert kernel mappings + extern const char etext[]; + uintptr_t retext = ROUNDUP((uintptr_t)etext, PGSIZE); + boot_map_segment(kern_pgdir, KERNBASE, retext - KERNBASE, PADDR(KERNBASE), PTE_R | PTE_X); + boot_map_segment(kern_pgdir, retext, KERNTOP - retext, PADDR(retext), PTE_R | PTE_W); + setup_kernel_io_mapping(kern_pgdir); + + // perform switch + boot_pgdir = kern_pgdir; + boot_cr3 = PADDR(boot_pgdir); + lcr3(boot_cr3); + flush_tlb(); + cprintf("Page table directory switch succeeded!\n"); + + /** + * set up kernel stack guardian pages + */ + extern char bootstackguard[],boot_page_table_sv39[]; + if ((bootstackguard + PGSIZE == bootstack) && (bootstacktop == boot_page_table_sv39)){ + // check writeable and set 0 + memset(boot_page_table_sv39,0,PGSIZE); + bootstack[-1] = 0; + bootstack[-PGSIZE] = 0; + + // set pages beneath and above the kernel stack as guardians + boot_map_segment(boot_pgdir, bootstackguard, PGSIZE, PADDR(bootstackguard), 0); + boot_map_segment(boot_pgdir, boot_page_table_sv39, PGSIZE, PADDR(boot_page_table_sv39), 0); + flush_tlb(); + + // the following four statements should all crash + // bootstack[-1] = 0; + // bootstack[-PGSIZE] = 0; + // bootstacktop[0] = 0; + // bootstacktop[PGSIZE-1] = 0; + + cprintf("Kernel stack guardians set succeeded!\n"); + } +} + +// pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup +// paging mechanism +// - check the correctness of pmm & paging mechanism, print PDT&PT +void pmm_init(void) { + // We need to alloc/free the physical memory (granularity is 4KB or other + // size). + // So a framework of physical memory manager (struct pmm_manager)is defined + // in pmm.h + // First we should init a physical memory manager(pmm) based on the + // framework. + // Then pmm can alloc/free the physical memory. + // Now the first_fit/best_fit/worst_fit/buddy_system pmm are available. + init_pmm_manager(); + + // detect physical memory space, reserve already used memory, + // then use pmm->init_memmap to create free page list + page_init(); + + // use pmm->check to verify the correctness of the alloc/free function in a + // pmm + check_alloc_page(); + + // switch from transient boot page directory to refined kernel page directory + switch_kernel_memorylayout(); + + check_pgdir(); + + static_assert(KERNTOP % PTSIZE == 0); + + // now the basic virtual memory map(see memalyout.h) is established. + // check the correctness of the basic virtual memory map. + check_boot_pgdir(); + + + kmalloc_init(); +} + +// get_pte - get pte and return the kernel virtual address of this pte for la +// - if the PT contians this pte didn't exist, alloc a page for PT +// parameter: +// pgdir: the kernel virtual base address of PDT +// la: the linear address need to map +// create: a logical value to decide if alloc a page for PT +// return vaule: the kernel virtual address of this pte +pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create) { + /* LAB2 EXERCISE 2: YOUR CODE + * + * If you need to visit a physical address, please use KADDR() + * please read pmm.h for useful macros + * + * Maybe you want help comment, BELOW comments can help you finish the code + * + * Some Useful MACROs and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la. + * KADDR(pa) : takes a physical address and returns the corresponding + * kernel virtual address. + * set_page_ref(page,1) : means the page be referenced by one time + * page2pa(page): get the physical address of memory which this (struct + * Page *) page manages + * struct Page * alloc_page() : allocation a page + * memset(void *s, char c, size_t n) : sets the first n bytes of the + * memory area pointed by s + * to the specified value c. + * DEFINEs: + * PTE_P 0x001 // page table/directory entry + * flags bit : Present + * PTE_W 0x002 // page table/directory entry + * flags bit : Writeable + * PTE_U 0x004 // page table/directory entry + * flags bit : User can access + */ + pde_t *pdep1 = &pgdir[PDX1(la)]; + if (!(*pdep1 & PTE_V)) { + struct Page *page; + if (!create || (page = alloc_page()) == NULL) { + return NULL; + } + set_page_ref(page, 1); + uintptr_t pa = page2pa(page); + memset(KADDR(pa), 0, PGSIZE); + *pdep1 = pte_create(page2ppn(page), PTE_U | PTE_V); + } + + pde_t *pdep0 = &((pde_t *)KADDR(PDE_ADDR(*pdep1)))[PDX0(la)]; + if (!(*pdep0 & PTE_V)) { + struct Page *page; + if (!create || (page = alloc_page()) == NULL) { + return NULL; + } + set_page_ref(page, 1); + uintptr_t pa = page2pa(page); + memset(KADDR(pa), 0, PGSIZE); + *pdep0 = pte_create(page2ppn(page), PTE_U | PTE_V); + } + return &((pte_t *)KADDR(PDE_ADDR(*pdep0)))[PTX(la)]; +} + +// get_page - get related Page struct for linear address la using PDT pgdir +struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) { + pte_t *ptep = get_pte(pgdir, la, 0); + if (ptep_store != NULL) { + *ptep_store = ptep; + } + if (ptep != NULL && *ptep & PTE_V) { + return pte2page(*ptep); + } + return NULL; +} + +// page_remove_pte - free an Page sturct which is related linear address la +// - and clean(invalidate) pte which is related linear address la +// note: PT is changed, so the TLB need to be invalidate +static inline void page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) { + /* LAB2 EXERCISE 3: YOUR CODE + * + * Please check if ptep is valid, and tlb must be manually updated if + * mapping is updated + * + * Maybe you want help comment, BELOW comments can help you finish the code + * + * Some Useful MACROs and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * struct Page *page pte2page(*ptep): get the according page from the + * value of a ptep + * free_page : free a page + * page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , + * then this page should be free. + * tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, + * but only if the page tables being + * edited are the ones currently in use by the + * processor. + * DEFINEs: + * PTE_P 0x001 // page table/directory entry + * flags bit : Present + */ + if (*ptep & PTE_V) { //(1) check if this page table entry is + struct Page *page = + pte2page(*ptep); //(2) find corresponding page to pte + page_ref_dec(page); //(3) decrease page reference + if (page_ref(page) == + 0) { //(4) and free this page when page reference reachs 0 + free_page(page); + } + *ptep = 0; //(5) clear second page table entry + tlb_invalidate(pgdir, la); //(6) flush tlb + } +} + +void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end) { + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + assert(USER_ACCESS(start, end)); + + do { + pte_t *ptep = get_pte(pgdir, start, 0); + if (ptep == NULL) { + start = ROUNDDOWN(start + PTSIZE, PTSIZE); + continue; + } + if (*ptep != 0) { + page_remove_pte(pgdir, start, ptep); + } + start += PGSIZE; + } while (start != 0 && start < end); +} + +static void try_to_free_table(pde_t *table, pde_t *entryp, uint32_t nentry) +{ + int free = 1; + for (int i = 0; i < nentry; i++) + if (table[i] & PTE_V){ + free = 0; + break; + } + if (free) { + free_page(pde2page(*entryp)); + *entryp = 0; + } +} + +void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end, int check) { + + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + if (check) + assert(USER_ACCESS(start, end)); + + uintptr_t d1start = ROUNDDOWN(start, PDSIZE), d0start = ROUNDDOWN(start, PTSIZE); + pde_t *pd0, pde1, pde0; + do { + // level 1 page directory entry + pde1 = pgdir[PDX1(d1start)]; + // if there is a valid entry, get into level 0 + // and try to free all page tables pointed to by + // all valid entries in level 0 page directory, + // then try to free this level 0 page directory + // and update level 1 entry + if (pde1&PTE_V){ + pd0 = page2kva(pde2page(pde1)); + do { + pde0 = pd0[PDX0(d0start)]; + // try to free page table + if (pde0&PTE_V) + try_to_free_table(page2kva(pde2page(pde0)), &pd0[PDX0(d0start)], NPTEENTRY); + d0start += PTSIZE; + } while (d0start != 0 && d0start < d1start+PDSIZE && d0start < end); + // free level 0 page directory only when all pde0s in it are already invalid + try_to_free_table(pd0, &pgdir[PDX1(d1start)], NPDEENTRY); + } + d1start += PDSIZE; + d0start = d1start; + } while (d1start != 0 && d1start < end); +} + +/* + * Difference between origin version is that io mapping doesn't + * really occupy physical pages, so there is no need to decrease + * page reference count, but simply set page table entry to 0. + */ +void unmap_range_io(pde_t *pgdir, uintptr_t start, uintptr_t end) { + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + + do { + pte_t *ptep = get_pte(pgdir, start, 0); + if (ptep == NULL) { + start = ROUNDDOWN(start + PTSIZE, PTSIZE); + continue; + } + *ptep = 0; + start += PGSIZE; + } while (start != 0 && start < end); +} + +/* copy_range - copy content of memory (start, end) of one process A to another + * process B + * @to: the addr of process B's Page Directory + * @from: the addr of process A's Page Directory + * @share: flags to indicate to dup OR share. We just use dup method, so it + * didn't be used. + * + * CALL GRAPH: copy_mm-->dup_mmap-->copy_range + */ +int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, + bool share) { + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + assert(USER_ACCESS(start, end)); + // copy content by page unit. + do { + // call get_pte to find process A's pte according to the addr start + pte_t *ptep = get_pte(from, start, 0), *nptep; + if (ptep == NULL) { + start = ROUNDDOWN(start + PTSIZE, PTSIZE); + continue; + } + // call get_pte to find process B's pte according to the addr start. If + // pte is NULL, just alloc a PT + if (*ptep & PTE_V) { + if ((nptep = get_pte(to, start, 1)) == NULL) { + return -E_NO_MEM; + } + uint32_t perm = (*ptep & PTE_USER); + // get page from ptep + struct Page *page = pte2page(*ptep); + // alloc a page for process B + struct Page *npage = alloc_page(); + assert(page != NULL); + assert(npage != NULL); + int ret = 0; + /* LAB5:EXERCISE2 YOUR CODE + * replicate content of page to npage, build the map of phy addr of + * nage with the linear addr start + * + * Some Useful MACROs and DEFINEs, you can use them in below + * implementation. + * MACROs or Functions: + * page2kva(struct Page *page): return the kernel vritual addr of + * memory which page managed (SEE pmm.h) + * page_insert: build the map of phy addr of an Page with the + * linear addr la + * memcpy: typical memory copy function + * + * (1) find src_kvaddr: the kernel virtual address of page + * (2) find dst_kvaddr: the kernel virtual address of npage + * (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE + * (4) build the map of phy addr of nage with the linear addr start + */ + void *kva_src = page2kva(page); + void *kva_dst = page2kva(npage); + + memcpy(kva_dst, kva_src, PGSIZE); + + ret = page_insert(to, npage, start, perm); + assert(ret == 0); + } + start += PGSIZE; + } while (start != 0 && start < end); + return 0; +} + +// page_remove - free an Page which is related linear address la and has an +// validated pte +void page_remove(pde_t *pgdir, uintptr_t la) { + pte_t *ptep = get_pte(pgdir, la, 0); + if (ptep != NULL) { + page_remove_pte(pgdir, la, ptep); + } +} + +// page_insert - build the map of phy addr of an Page with the linear addr la +// paramemters: +// pgdir: the kernel virtual base address of PDT +// page: the Page which need to map +// la: the linear address need to map +// perm: the permission of this Page which is setted in related pte +// return value: always 0 +// note: PT is changed, so the TLB need to be invalidate +int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) { + pte_t *ptep = get_pte(pgdir, la, 1); + if (ptep == NULL) { + return -E_NO_MEM; + } + page_ref_inc(page); + if (*ptep & PTE_V) { + struct Page *p = pte2page(*ptep); + if (p == page) { + page_ref_dec(page); + } else { + page_remove_pte(pgdir, la, ptep); + } + } + *ptep = pte_create(page2ppn(page), PTE_V | perm); + tlb_invalidate(pgdir, la); + return 0; +} + +// invalidate a TLB entry, but only if the page tables being +// edited are the ones currently in use by the processor. +void tlb_invalidate(pde_t *pgdir, uintptr_t la) { + asm volatile("sfence.vma %0" : : "r"(la)); +} + +// pgdir_alloc_page - call alloc_page & page_insert functions to +// - allocate a page size memory & setup an addr map +// - pa<->la with linear address la and the PDT pgdir +struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) { + struct Page *page = alloc_page(); + if (page != NULL) { + if (page_insert(pgdir, page, la, perm) != 0) { + free_page(page); + return NULL; + } + if (swap_init_ok) { + if (check_mm_struct != NULL) { + swap_map_swappable(check_mm_struct, la, page, 0); + page->pra_vaddr = la; + assert(page_ref(page) == 1); + // cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, + // pra_link_next %x in pgdir_alloc_page\n", (page-pages), + // page->pra_vaddr,page->pra_page_link.prev, + // page->pra_page_link.next); + } else { // now current is existed, should fix it in the future + // swap_map_swappable(current->mm, la, page, 0); + // page->pra_vaddr=la; + // assert(page_ref(page) == 1); + // panic("pgdir_alloc_page: no pages. now current is existed, + // should fix it in the future\n"); + } + } + } + + return page; +} + +static void check_alloc_page(void) { + pmm_manager->check(); + cprintf("check_alloc_page() succeeded!\n"); +} + +static void check_pgdir(void) { + // assert(npage <= KMEMSIZE / PGSIZE); + // The memory starts at 2GB in RISC-V + // so npage is always larger than KMEMSIZE / PGSIZE + size_t nr_free_store; + + nr_free_store=nr_free_pages(); + + assert(npage <= KERNTOP / PGSIZE); + assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0); + assert(boot_pgdir[3] == 0); + assert(get_page(boot_pgdir, 0xC0000000, NULL) == NULL); + + struct Page *p1, *p2; + p1 = alloc_page(); + assert(page_insert(boot_pgdir, p1, 0xC0000000, 0) == 0); + + pte_t *ptep; + assert((ptep = get_pte(boot_pgdir, 0xC0000000, 0)) != NULL); + assert(pte2page(*ptep) == p1); + assert(page_ref(p1) == 1); + + ptep = (pte_t *)KADDR(PDE_ADDR(boot_pgdir[3])); + ptep = (pte_t *)KADDR(PDE_ADDR(ptep[0])) + 1; + assert(get_pte(boot_pgdir, 0xC0000000 + PGSIZE, 0) == ptep); + + p2 = alloc_page(); + assert(page_insert(boot_pgdir, p2, 0xC0000000 + PGSIZE, PTE_U | PTE_W) == 0); + assert((ptep = get_pte(boot_pgdir, 0xC0000000 + PGSIZE, 0)) != NULL); + assert(*ptep & PTE_U); + assert(*ptep & PTE_W); + assert(boot_pgdir[3] & PTE_U); + assert(page_ref(p2) == 1); + + assert(page_insert(boot_pgdir, p1, 0xC0000000 + PGSIZE, 0) == 0); + assert(page_ref(p1) == 2); + assert(page_ref(p2) == 0); + assert((ptep = get_pte(boot_pgdir, 0xC0000000 + PGSIZE, 0)) != NULL); + assert(pte2page(*ptep) == p1); + assert((*ptep & PTE_U) == 0); + + page_remove(boot_pgdir, 0xC0000000); + assert(page_ref(p1) == 1); + assert(page_ref(p2) == 0); + + page_remove(boot_pgdir, 0xC0000000 + PGSIZE); + assert(page_ref(p1) == 0); + assert(page_ref(p2) == 0); + + assert(page_ref(pde2page(boot_pgdir[3])) == 1); + + pde_t *pd1 = boot_pgdir, *pd0 = page2kva(pde2page(pd1[3])); + free_page(pde2page(pd0[0])); + free_page(pde2page(pd1[3])); + boot_pgdir[3] = 0; + flush_tlb(); + + assert(nr_free_store==nr_free_pages()); + + cprintf("check_pgdir() succeeded!\n"); +} + +static void check_boot_pgdir(void) { + size_t nr_free_store; + pte_t *ptep; + int i; + + nr_free_store = nr_free_pages(); + + for (i = ROUNDDOWN(KERNBASE, PGSIZE); i < npage * PGSIZE; i += PGSIZE) { + assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL); + assert(PTE_ADDR(*ptep) == i); + } + + + assert(boot_pgdir[3] == 0); + + struct Page *p; + p = alloc_page(); + assert(page_insert(boot_pgdir, p, 0xC0000100, PTE_W | PTE_R) == 0); + assert(page_ref(p) == 1); + assert(page_insert(boot_pgdir, p, 0xC0000100 + PGSIZE, PTE_W | PTE_R) == 0); + assert(page_ref(p) == 2); + + const char *str = "ucore: Hello world!!"; + strcpy((void *)0xC0000100, str); + assert(strcmp((void *)0xC0000100, (void *)(0xC0000100 + PGSIZE)) == 0); + + *(char *)(page2kva(p) + 0x100) = '\0'; + assert(strlen((const char *)0xC0000100) == 0); + + pde_t *pd1 = boot_pgdir, *pd0 = page2kva(pde2page(boot_pgdir[3])); + free_page(p); + free_page(pde2page(pd0[0])); + free_page(pde2page(pd1[3])); + boot_pgdir[3] = 0; + flush_tlb(); + + assert(nr_free_store==nr_free_pages()); + + cprintf("check_boot_pgdir() succeeded!\n"); +} + +// perm2str - use string 'u,r,w,-' to present the permission +static const char *perm2str(int perm) { + static char str[4]; + str[0] = (perm & PTE_U) ? 'u' : '-'; + str[1] = 'r'; + str[2] = (perm & PTE_W) ? 'w' : '-'; + str[3] = '\0'; + return str; +} + +// get_pgtable_items - In [left, right] range of PDT or PT, find a continuous +// linear addr space +// - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT +// - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT +// paramemters: +// left: no use ??? +// right: the high side of table's range +// start: the low side of table's range +// table: the beginning addr of table +// left_store: the pointer of the high side of table's next range +// right_store: the pointer of the low side of table's next range +// return value: 0 - not a invalid item range, perm - a valid item range with +// perm permission +static int get_pgtable_items(size_t left, size_t right, size_t start, + uintptr_t *table, size_t *left_store, + size_t *right_store) { + if (start >= right) { + return 0; + } + while (start < right && !(table[start] & PTE_V)) { + start++; + } + if (start < right) { + if (left_store != NULL) { + *left_store = start; + } + int perm = (table[start++] & PTE_USER); + while (start < right && (table[start] & PTE_USER) == perm) { + start++; + } + if (right_store != NULL) { + *right_store = start; + } + return perm; + } + return 0; +} + diff --git a/lab8/kern/mm/pmm.h b/lab8/kern/mm/pmm.h new file mode 100644 index 0000000..77cebbd --- /dev/null +++ b/lab8/kern/mm/pmm.h @@ -0,0 +1,167 @@ +#ifndef __KERN_MM_PMM_H__ +#define __KERN_MM_PMM_H__ + +#include +#include +#include +#include +#include + +// pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager +// only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used +// by ucore to manage the total physical memory space. +struct pmm_manager { + const char *name; // XXX_pmm_manager's name + void (*init)(void); // initialize internal description&management data structure + // (free block list, number of free block) of XXX_pmm_manager + void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to + // the initial free physical memory space + struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm + void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h) + size_t (*nr_free_pages)(void); // return the number of free pages + void (*check)(void); // check the correctness of XXX_pmm_manager +}; + +extern const struct pmm_manager *pmm_manager; +extern pde_t *boot_pgdir; +extern const size_t nbase; +extern uintptr_t boot_cr3; + +void pmm_init(void); + +struct Page *alloc_pages(size_t n); +void free_pages(struct Page *base, size_t n); +size_t nr_free_pages(void); + +#define alloc_page() alloc_pages(1) +#define free_page(page) free_pages(page, 1) + +pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create); +struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store); +void page_remove(pde_t *pgdir, uintptr_t la); +int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm); + +void load_esp0(uintptr_t esp0); +void tlb_invalidate(pde_t *pgdir, uintptr_t la); +struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm); +void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end); +void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end, int check); +void unmap_range_io(pde_t *pgdir, uintptr_t start, uintptr_t end); +int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share); +void setup_kernel_io_mapping(pde_t *pgdir); +void free_kernel_io_mapping(pde_t *pgdir); + +void print_pgdir(void); + +/* * + * PADDR - takes a kernel virtual address (an address that points above KERNBASE), + * where the machine's maximum 256MB of physical memory is mapped and returns the + * corresponding physical address. It panics if you pass it a non-kernel virtual address. + * */ +#define PADDR(kva) \ + ({ \ + uintptr_t __m_kva = (uintptr_t)(kva); \ + if (__m_kva < KERNBASE) { \ + panic("PADDR called with invalid kva %08lx", __m_kva); \ + } \ + __m_kva - va_pa_offset; \ + }) + +/* * + * KADDR - takes a physical address and returns the corresponding kernel virtual + * address. It panics if you pass an invalid physical address. + * */ +#define KADDR(pa) \ + ({ \ + uintptr_t __m_pa = (pa); \ + size_t __m_ppn = PPN(__m_pa); \ + if (__m_ppn >= npage) { \ + panic("KADDR called with invalid pa %08lx", __m_pa); \ + } \ + (void *)(__m_pa + va_pa_offset); \ + }) + +extern struct Page *pages; +extern size_t npage; +extern uint_t va_pa_offset; + +static inline ppn_t +page2ppn(struct Page *page) { + return page - pages + nbase; +} + +static inline uintptr_t +page2pa(struct Page *page) { + return page2ppn(page) << PGSHIFT; +} + +static inline struct Page * +pa2page(uintptr_t pa) { + if (PPN(pa) >= npage) { + panic("pa2page called with invalid pa"); + } + return &pages[PPN(pa) - nbase]; +} + +static inline void * +page2kva(struct Page *page) { + return KADDR(page2pa(page)); +} + +static inline struct Page * +kva2page(void *kva) { + return pa2page(PADDR(kva)); +} + +static inline struct Page * +pte2page(pte_t pte) { + if (!(pte & PTE_V)) { + panic("pte2page called with invalid pte"); + } + return pa2page(PTE_ADDR(pte)); +} + +static inline struct Page * +pde2page(pde_t pde) { + return pa2page(PDE_ADDR(pde)); +} + +static inline int +page_ref(struct Page *page) { + return page->ref; +} + +static inline void +set_page_ref(struct Page *page, int val) { + page->ref = val; +} + +static inline int +page_ref_inc(struct Page *page) { + page->ref += 1; + return page->ref; +} + +static inline int +page_ref_dec(struct Page *page) { + page->ref -= 1; + return page->ref; +} + +static inline void flush_tlb() { + asm volatile("sfence.vma"); +} + +// construct PTE from a page and permission bits +static inline pte_t pte_create(uintptr_t ppn, int type) { + return (ppn << PTE_PPN_SHIFT) | PTE_V | type; +} + +static inline pte_t ptd_create(uintptr_t ppn) { + return pte_create(ppn, PTE_V); +} + +extern char bootstack[], bootstacktop[]; + +#endif /* !__KERN_MM_PMM_H__ */ + diff --git a/lab8/kern/mm/swap.c b/lab8/kern/mm/swap.c new file mode 100644 index 0000000..45b5316 --- /dev/null +++ b/lab8/kern/mm/swap.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// the valid vaddr for check is between 0~CHECK_VALID_VADDR-1 +#define CHECK_VALID_VIR_PAGE_NUM (5) +#define BEING_CHECK_VALID_VADDR (0XC0001000) +#define CHECK_VALID_VADDR (BEING_CHECK_VALID_VADDR + CHECK_VALID_VIR_PAGE_NUM * 0x1000) +// the max number of valid physical page for check +#define CHECK_VALID_PHY_PAGE_NUM 4 +// the max access seq number +#define MAX_SEQ_NO 10 + +static struct swap_manager *sm; +size_t max_swap_offset; + +volatile int swap_init_ok = 0; + +unsigned int swap_page[CHECK_VALID_VIR_PAGE_NUM]; + +unsigned int swap_in_seq_no[MAX_SEQ_NO],swap_out_seq_no[MAX_SEQ_NO]; + +static void check_swap(void); + +int +swap_init(void) +{ + swapfs_init(); + + // Since the IDE is faked, it can only store 7 pages at most to pass the test + if (!(7 <= max_swap_offset && + max_swap_offset < MAX_SWAP_OFFSET_LIMIT)) { + panic("bad max_swap_offset %08x.\n", max_swap_offset); + } + + + sm = &swap_manager_fifo; + int r = sm->init(); + + if (r == 0) + { + swap_init_ok = 1; + cprintf("SWAP: manager = %s\n", sm->name); + check_swap(); + } + + return r; +} + +int +swap_init_mm(struct mm_struct *mm) +{ + return sm->init_mm(mm); +} + +int +swap_tick_event(struct mm_struct *mm) +{ + return sm->tick_event(mm); +} + +int +swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) +{ + return sm->map_swappable(mm, addr, page, swap_in); +} + +int +swap_set_unswappable(struct mm_struct *mm, uintptr_t addr) +{ + return sm->set_unswappable(mm, addr); +} + +volatile unsigned int swap_out_num=0; + +int +swap_out(struct mm_struct *mm, int n, int in_tick) +{ + int i; + for (i = 0; i != n; ++ i) + { + uintptr_t v; + //struct Page **ptr_page=NULL; + struct Page *page; + // cprintf("i %d, SWAP: call swap_out_victim\n",i); + int r = sm->swap_out_victim(mm, &page, in_tick); + if (r != 0) { + cprintf("i %d, swap_out: call swap_out_victim failed\n",i); + break; + } + //assert(!PageReserved(page)); + + //cprintf("SWAP: choose victim page 0x%08x\n", page); + + v=page->pra_vaddr; + pte_t *ptep = get_pte(mm->pgdir, v, 0); + assert((*ptep & PTE_V) != 0); + + if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) { + cprintf("SWAP: failed to save\n"); + sm->map_swappable(mm, v, page, 0); + continue; + } + else { + cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1); + *ptep = (page->pra_vaddr/PGSIZE+1)<<8; + free_page(page); + } + + tlb_invalidate(mm->pgdir, v); + } + return i; +} + +int +swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result) +{ + struct Page *result = alloc_page(); + assert(result!=NULL); + + pte_t *ptep = get_pte(mm->pgdir, addr, 0); + // cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages)); + + int r; + if ((r = swapfs_read((*ptep), result)) != 0) + { + assert(r!=0); + } + cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr); + *ptr_result=result; + return 0; +} + + + +static inline void +check_content_set(void) +{ + *(unsigned char *)0xC0001000 = 0x0a; + assert(pgfault_num==1); + *(unsigned char *)0xC0001010 = 0x0a; + assert(pgfault_num==1); + *(unsigned char *)0xC0002000 = 0x0b; + assert(pgfault_num==2); + *(unsigned char *)0xC0002010 = 0x0b; + assert(pgfault_num==2); + *(unsigned char *)0xC0003000 = 0x0c; + assert(pgfault_num==3); + *(unsigned char *)0xC0003010 = 0x0c; + assert(pgfault_num==3); + *(unsigned char *)0xC0004000 = 0x0d; + assert(pgfault_num==4); + *(unsigned char *)0xC0004010 = 0x0d; + assert(pgfault_num==4); +} + +static inline int +check_content_access(void) +{ + int ret = sm->check_swap(); + return ret; +} + +struct Page * check_rp[CHECK_VALID_PHY_PAGE_NUM]; +pte_t * check_ptep[CHECK_VALID_PHY_PAGE_NUM]; +unsigned int check_swap_addr[CHECK_VALID_VIR_PAGE_NUM]; + +extern free_area_t free_area; + +#define free_list (free_area.free_list) +#define nr_free (free_area.nr_free) + +static void +check_swap(void) +{ + //backup mem env + int ret, count = 0, total = 0, i; + list_entry_t *le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + assert(PageProperty(p)); + count++, total += p->property; + } + assert(total == nr_free_pages()); + cprintf("BEGIN check_swap: count %d, total %d\n", count, total); + + //now we set the phy pages env + struct mm_struct *mm = mm_create(); + assert(mm != NULL); + + extern struct mm_struct *check_mm_struct; + assert(check_mm_struct == NULL); + + check_mm_struct = mm; + + pde_t *pgdir = mm->pgdir = boot_pgdir; + assert(pgdir[3] == 0); + + struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ); + assert(vma != NULL); + + insert_vma_struct(mm, vma); + + //setup the temp Page Table vaddr 0~4MB + cprintf("setup Page Table for vaddr 0XC0001000, so alloc two pages\n"); + pte_t *temp_ptep = NULL; + temp_ptep = get_pte(mm->pgdir, BEING_CHECK_VALID_VADDR, 1); + assert(temp_ptep != NULL); + cprintf("setup Page Table vaddr 0~4MB OVER!\n"); + + for (i = 0; i < CHECK_VALID_PHY_PAGE_NUM; i++) { + check_rp[i] = alloc_page(); + assert(check_rp[i] != NULL ); + assert(!PageProperty(check_rp[i])); + } + list_entry_t free_list_store = free_list; + list_init(&free_list); + assert(list_empty(&free_list)); + + //assert(alloc_page() == NULL); + + unsigned int nr_free_store = nr_free; + nr_free = 0; + for (i = 0; i < CHECK_VALID_PHY_PAGE_NUM; i++) { + free_pages(check_rp[i], 1); + } + assert(nr_free == CHECK_VALID_PHY_PAGE_NUM); + + cprintf("set up init env for check_swap begin!\n"); + //setup initial vir_page<->phy_page environment for page relpacement algorithm + + + pgfault_num = 0; + + check_content_set(); + assert(nr_free == 0); + for(i = 0; i < MAX_SEQ_NO ; i++) + swap_out_seq_no[i] = swap_in_seq_no[i] = -1; + + for (i = 0; i < CHECK_VALID_PHY_PAGE_NUM; i++) { + check_ptep[i] = 0; + check_ptep[i] = get_pte(pgdir, BEING_CHECK_VALID_VADDR + i * 0x1000, 0); + //cprintf("i %d, check_ptep addr %x, value %x\n", i, check_ptep[i], *check_ptep[i]); + assert(check_ptep[i] != NULL); + assert(pte2page(*check_ptep[i]) == check_rp[i]); + assert((*check_ptep[i] & PTE_V)); + } + cprintf("set up init env for check_swap over!\n"); + // now access the virt pages to test page relpacement algorithm + ret = check_content_access(); + assert(ret == 0); + + nr_free = nr_free_store; + free_list = free_list_store; + + //restore kernel mem env + for (i = 0; i < CHECK_VALID_PHY_PAGE_NUM; i++) { + free_pages(check_rp[i], 1); + } + + //free_page(pte2page(*temp_ptep)); + + mm->pgdir = NULL; + mm_destroy(mm); + check_mm_struct = NULL; + + pde_t *pd1 = pgdir, *pd0 = page2kva(pde2page(boot_pgdir[3])); + free_page(pde2page(pd0[0])); + free_page(pde2page(pd1[3])); + pgdir[3] = 0; + flush_tlb(); + + le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + count --, total -= p->property; + } + assert(count == 0); + assert(total == 0); + + cprintf("check_swap() succeeded!\n"); +} diff --git a/lab8/kern/mm/swap.h b/lab8/kern/mm/swap.h new file mode 100644 index 0000000..3d7f45e --- /dev/null +++ b/lab8/kern/mm/swap.h @@ -0,0 +1,62 @@ +#ifndef __KERN_MM_SWAP_H__ +#define __KERN_MM_SWAP_H__ + +#include +#include +#include +#include + +/* * + * swap_entry_t + * -------------------------------------------- + * | offset | reserved | 0 | + * -------------------------------------------- + * 24 bits 7 bits 1 bit + * */ + +#define MAX_SWAP_OFFSET_LIMIT (1 << 24) + +extern size_t max_swap_offset; + +/* * + * swap_offset - takes a swap_entry (saved in pte), and returns + * the corresponding offset in swap mem_map. + * */ +#define swap_offset(entry) ({ \ + size_t __offset = (entry >> 8); \ + __offset % max_swap_offset; \ + }) + +struct swap_manager +{ + const char *name; + /* Global initialization for the swap manager */ + int (*init) (void); + /* Initialize the priv data inside mm_struct */ + int (*init_mm) (struct mm_struct *mm); + /* Called when tick interrupt occured */ + int (*tick_event) (struct mm_struct *mm); + /* Called when map a swappable page into the mm_struct */ + int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in); + /* When a page is marked as shared, this routine is called to + * delete the addr entry from the swap manager */ + int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr); + /* Try to swap out a page, return then victim */ + int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick); + /* check the page relpacement algorithm */ + int (*check_swap)(void); +}; + +extern volatile int swap_init_ok; +int swap_init(void); +int swap_init_mm(struct mm_struct *mm); +int swap_tick_event(struct mm_struct *mm); +int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in); +int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr); +int swap_out(struct mm_struct *mm, int n, int in_tick); +int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result); + +//#define MEMBER_OFFSET(m,t) ((int)(&((t *)0)->m)) +//#define FROM_MEMBER(m,t,a) ((t *)((char *)(a) - MEMBER_OFFSET(m,t))) + +#endif diff --git a/lab8/kern/mm/swap_fifo.c b/lab8/kern/mm/swap_fifo.c new file mode 100644 index 0000000..8c02bdd --- /dev/null +++ b/lab8/kern/mm/swap_fifo.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + +/* [wikipedia]The simplest Page Replacement Algorithm(PRA) is a FIFO algorithm. The first-in, first-out + * page replacement algorithm is a low-overhead algorithm that requires little book-keeping on + * the part of the operating system. The idea is obvious from the name - the operating system + * keeps track of all the pages in memory in a queue, with the most recent arrival at the back, + * and the earliest arrival in front. When a page needs to be replaced, the page at the front + * of the queue (the oldest page) is selected. While FIFO is cheap and intuitive, it performs + * poorly in practical application. Thus, it is rarely used in its unmodified form. This + * algorithm experiences Belady's anomaly. + * + * Details of FIFO PRA + * (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can + * link these pages into pra_list_head according the time order. At first you should + * be familiar to the struct list in list.h. struct list is a simple doubly linked list + * implementation. You should know howto USE: list_init, list_add(list_add_after), + * list_add_before, list_del, list_next, list_prev. Another tricky method is to transform + * a general list struct to a special struct (such as struct page). You can find some MACRO: + * le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc. + */ + +list_entry_t pra_list_head; +/* + * (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head. + * Now, From the memory control struct mm_struct, we can access FIFO PRA + */ +static int +_fifo_init_mm(struct mm_struct *mm) +{ + list_init(&pra_list_head); + mm->sm_priv = &pra_list_head; + //cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv); + return 0; +} +/* + * (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue + */ +static int +_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) +{ + list_entry_t *head=(list_entry_t*) mm->sm_priv; + list_entry_t *entry=&(page->pra_page_link); + + assert(entry != NULL && head != NULL); + //record the page access situlation + /*LAB3 EXERCISE 2: YOUR CODE*/ + //(1)link the most recent arrival page at the back of the pra_list_head qeueue. + list_add_before(head, entry); + return 0; +} +/* + * (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue, + * then set the addr of addr of this page to ptr_page. + */ +static int +_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) +{ + list_entry_t *head=(list_entry_t*) mm->sm_priv; + assert(head != NULL); + assert(in_tick==0); + /* Select the victim */ + /*LAB3 EXERCISE 2: YOUR CODE*/ + //(1) unlink the earliest arrival page in front of pra_list_head qeueue + //(2) set the addr of addr of this page to ptr_page + list_entry_t* entry = list_next(head); + list_del(entry); + *ptr_page = le2page(entry, pra_page_link); + return 0; +} + +static int +_fifo_check_swap(void) { + cprintf("write Virt Page c in fifo_check_swap\n"); + *(unsigned char *)0xC0003000 = 0x0c; + assert(pgfault_num==4); + cprintf("write Virt Page a in fifo_check_swap\n"); + *(unsigned char *)0xC0001000 = 0x0a; + assert(pgfault_num==4); + cprintf("write Virt Page d in fifo_check_swap\n"); + *(unsigned char *)0xC0004000 = 0x0d; + assert(pgfault_num==4); + cprintf("write Virt Page b in fifo_check_swap\n"); + *(unsigned char *)0xC0002000 = 0x0b; + assert(pgfault_num==4); + cprintf("write Virt Page e in fifo_check_swap\n"); + *(unsigned char *)0xC0005000 = 0x0e; + assert(pgfault_num==5); + cprintf("write Virt Page b in fifo_check_swap\n"); + *(unsigned char *)0xC0002000 = 0x0b; + assert(pgfault_num==5); + cprintf("write Virt Page a in fifo_check_swap\n"); + *(unsigned char *)0xC0001000 = 0x0a; + assert(pgfault_num==6); + cprintf("write Virt Page b in fifo_check_swap\n"); + *(unsigned char *)0xC0002000 = 0x0b; + assert(pgfault_num==7); + cprintf("write Virt Page c in fifo_check_swap\n"); + *(unsigned char *)0xC0003000 = 0x0c; + assert(pgfault_num==8); + cprintf("write Virt Page d in fifo_check_swap\n"); + *(unsigned char *)0xC0004000 = 0x0d; + assert(pgfault_num==9); + cprintf("write Virt Page e in fifo_check_swap\n"); + *(unsigned char *)0xC0005000 = 0x0e; + assert(pgfault_num==10); + cprintf("write Virt Page a in fifo_check_swap\n"); + assert(*(unsigned char *)0xC0001000 == 0x0a); + *(unsigned char *)0xC0001000 = 0x0a; + assert(pgfault_num == 11); + return 0; +} + + +static int +_fifo_init(void) +{ + return 0; +} + +static int +_fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr) +{ + return 0; +} + +static int +_fifo_tick_event(struct mm_struct *mm) +{ return 0; } + + +struct swap_manager swap_manager_fifo = +{ + .name = "fifo swap manager", + .init = &_fifo_init, + .init_mm = &_fifo_init_mm, + .tick_event = &_fifo_tick_event, + .map_swappable = &_fifo_map_swappable, + .set_unswappable = &_fifo_set_unswappable, + .swap_out_victim = &_fifo_swap_out_victim, + .check_swap = &_fifo_check_swap, +}; diff --git a/lab8/kern/mm/swap_fifo.h b/lab8/kern/mm/swap_fifo.h new file mode 100644 index 0000000..1d74269 --- /dev/null +++ b/lab8/kern/mm/swap_fifo.h @@ -0,0 +1,7 @@ +#ifndef __KERN_MM_SWAP_FIFO_H__ +#define __KERN_MM_SWAP_FIFO_H__ + +#include +extern struct swap_manager swap_manager_fifo; + +#endif diff --git a/lab8/kern/mm/vmm.c b/lab8/kern/mm/vmm.c new file mode 100644 index 0000000..33a1c85 --- /dev/null +++ b/lab8/kern/mm/vmm.c @@ -0,0 +1,524 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + vmm design include two parts: mm_struct (mm) & vma_struct (vma) + mm is the memory manager for the set of continuous virtual memory + area which have the same PDT. vma is a continuous virtual memory area. + There a linear link list for vma & a redblack link list for vma in mm. +--------------- + mm related functions: + golbal functions + struct mm_struct * mm_create(void) + void mm_destroy(struct mm_struct *mm) + int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) +-------------- + vma related functions: + global functions + struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...) + void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) + struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr) + local functions + inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) +--------------- + check correctness functions + void check_vmm(void); + void check_vma_struct(void); + void check_pgfault(void); +*/ + +static void check_vmm(void); +static void check_vma_struct(void); +static void check_pgfault(void); + +// mm_create - alloc a mm_struct & initialize it. +struct mm_struct * +mm_create(void) { + struct mm_struct *mm = kmalloc(sizeof(struct mm_struct)); + + if (mm != NULL) { + list_init(&(mm->mmap_list)); + mm->mmap_cache = NULL; + mm->pgdir = NULL; + mm->map_count = 0; + + if (swap_init_ok) swap_init_mm(mm); + else mm->sm_priv = NULL; + + set_mm_count(mm, 0); + sem_init(&(mm->mm_sem), 1); + } + return mm; +} + +// vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end) +struct vma_struct * +vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) { + struct vma_struct *vma = kmalloc(sizeof(struct vma_struct)); + + if (vma != NULL) { + vma->vm_start = vm_start; + vma->vm_end = vm_end; + vma->vm_flags = vm_flags; + } + return vma; +} + + +// find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end) +struct vma_struct * +find_vma(struct mm_struct *mm, uintptr_t addr) { + struct vma_struct *vma = NULL; + if (mm != NULL) { + vma = mm->mmap_cache; + if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) { + bool found = 0; + list_entry_t *list = &(mm->mmap_list), *le = list; + while ((le = list_next(le)) != list) { + vma = le2vma(le, list_link); + if (vma->vm_start<=addr && addr < vma->vm_end) { + found = 1; + break; + } + } + if (!found) { + vma = NULL; + } + } + if (vma != NULL) { + mm->mmap_cache = vma; + } + } + return vma; +} + + +// check_vma_overlap - check if vma1 overlaps vma2 ? +static inline void +check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) { + assert(prev->vm_start < prev->vm_end); + assert(prev->vm_end <= next->vm_start); + assert(next->vm_start < next->vm_end); +} + + +// insert_vma_struct -insert vma in mm's list link +void +insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) { + assert(vma->vm_start < vma->vm_end); + list_entry_t *list = &(mm->mmap_list); + list_entry_t *le_prev = list, *le_next; + + list_entry_t *le = list; + while ((le = list_next(le)) != list) { + struct vma_struct *mmap_prev = le2vma(le, list_link); + if (mmap_prev->vm_start > vma->vm_start) { + break; + } + le_prev = le; + } + + le_next = list_next(le_prev); + + /* check overlap */ + if (le_prev != list) { + check_vma_overlap(le2vma(le_prev, list_link), vma); + } + if (le_next != list) { + check_vma_overlap(vma, le2vma(le_next, list_link)); + } + + vma->vm_mm = mm; + list_add_after(le_prev, &(vma->list_link)); + + mm->map_count ++; +} + +// mm_destroy - free mm and mm internal fields +void +mm_destroy(struct mm_struct *mm) { + assert(mm_count(mm) == 0); + + list_entry_t *list = &(mm->mmap_list), *le; + while ((le = list_next(list)) != list) { + list_del(le); + kfree(le2vma(le, list_link)); //kfree vma + } + kfree(mm); //kfree mm + mm=NULL; +} + +int +mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags, + struct vma_struct **vma_store) { + uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE); + if (!USER_ACCESS(start, end)) { + return -E_INVAL; + } + + assert(mm != NULL); + + int ret = -E_INVAL; + + struct vma_struct *vma; + if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) { + goto out; + } + ret = -E_NO_MEM; + + if ((vma = vma_create(start, end, vm_flags)) == NULL) { + goto out; + } + insert_vma_struct(mm, vma); + if (vma_store != NULL) { + *vma_store = vma; + } + ret = 0; + +out: + return ret; +} + +int +dup_mmap(struct mm_struct *to, struct mm_struct *from) { + assert(to != NULL && from != NULL); + list_entry_t *list = &(from->mmap_list), *le = list; + while ((le = list_prev(le)) != list) { + struct vma_struct *vma, *nvma; + vma = le2vma(le, list_link); + nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags); + if (nvma == NULL) { + return -E_NO_MEM; + } + + insert_vma_struct(to, nvma); + + bool share = 0; + if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) { + return -E_NO_MEM; + } + } + return 0; +} + +void +exit_mmap(struct mm_struct *mm) { + assert(mm != NULL && mm_count(mm) == 0); + pde_t *pgdir = mm->pgdir; + list_entry_t *list = &(mm->mmap_list), *le = list; + while ((le = list_next(le)) != list) { + struct vma_struct *vma = le2vma(le, list_link); + unmap_range(pgdir, vma->vm_start, vma->vm_end); + } + while ((le = list_next(le)) != list) { + struct vma_struct *vma = le2vma(le, list_link); + exit_range(pgdir, vma->vm_start, vma->vm_end, 1); + } +} + +bool +copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) { + if (!user_mem_check(mm, (uintptr_t)src, len, writable)) { + return 0; + } + memcpy(dst, src, len); + return 1; +} + +bool +copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) { + if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) { + return 0; + } + memcpy(dst, src, len); + return 1; +} + +// vmm_init - initialize virtual memory management +// - now just call check_vmm to check correctness of vmm +void +vmm_init(void) { + check_vmm(); +} + +// check_vmm - check correctness of vmm +static void +check_vmm(void) { + // size_t nr_free_pages_store = nr_free_pages(); + + check_vma_struct(); + check_pgfault(); + + cprintf("check_vmm() succeeded.\n"); +} + +static void +check_vma_struct(void) { + // size_t nr_free_pages_store = nr_free_pages(); + + struct mm_struct *mm = mm_create(); + assert(mm != NULL); + + int step1 = 10, step2 = step1 * 10; + + int i; + for (i = step1; i >= 1; i --) { + struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); + assert(vma != NULL); + insert_vma_struct(mm, vma); + } + + for (i = step1 + 1; i <= step2; i ++) { + struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); + assert(vma != NULL); + insert_vma_struct(mm, vma); + } + + list_entry_t *le = list_next(&(mm->mmap_list)); + + for (i = 1; i <= step2; i ++) { + assert(le != &(mm->mmap_list)); + struct vma_struct *mmap = le2vma(le, list_link); + assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2); + le = list_next(le); + } + + for (i = 5; i <= 5 * step2; i +=5) { + struct vma_struct *vma1 = find_vma(mm, i); + assert(vma1 != NULL); + struct vma_struct *vma2 = find_vma(mm, i+1); + assert(vma2 != NULL); + struct vma_struct *vma3 = find_vma(mm, i+2); + assert(vma3 == NULL); + struct vma_struct *vma4 = find_vma(mm, i+3); + assert(vma4 == NULL); + struct vma_struct *vma5 = find_vma(mm, i+4); + assert(vma5 == NULL); + + assert(vma1->vm_start == i && vma1->vm_end == i + 2); + assert(vma2->vm_start == i && vma2->vm_end == i + 2); + } + + for (i =4; i>=0; i--) { + struct vma_struct *vma_below_5= find_vma(mm,i); + if (vma_below_5 != NULL ) { + cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end); + } + assert(vma_below_5 == NULL); + } + + mm_destroy(mm); + + cprintf("check_vma_struct() succeeded!\n"); +} + +struct mm_struct *check_mm_struct; + +// check_pgfault - check correctness of pgfault handler +static void +check_pgfault(void) { + size_t nr_free_pages_store = nr_free_pages(); + + check_mm_struct = mm_create(); + assert(check_mm_struct != NULL); + + struct mm_struct *mm = check_mm_struct; + pde_t *pgdir = mm->pgdir = boot_pgdir; + assert(pgdir[3] == 0); + + struct vma_struct *vma = vma_create(0xC0000000, 0xC0000000 + PTSIZE, VM_WRITE); + assert(vma != NULL); + + insert_vma_struct(mm, vma); + + uintptr_t addr = 0xC0000100; + assert(find_vma(mm, addr) == vma); + + int i, sum = 0; + + for (i = 0; i < 100; i ++) { + *(char *)(addr + i) = i; + sum += i; + } + for (i = 0; i < 100; i ++) { + sum -= *(char *)(addr + i); + } + + assert(sum == 0); + + pde_t *pd1 = pgdir, *pd0 = page2kva(pde2page(pgdir[3])); + page_remove(pgdir, ROUNDDOWN(addr, PGSIZE)); + free_page(pde2page(pd0[0])); + free_page(pde2page(pd1[3])); + pgdir[3] = 0; + flush_tlb(); + + mm->pgdir = NULL; + mm_destroy(mm); + check_mm_struct = NULL; + + assert(nr_free_pages_store == nr_free_pages()); + + cprintf("check_pgfault() succeeded!\n"); +} +//page fault number +volatile unsigned int pgfault_num=0; + +/* do_pgfault - interrupt handler to process the page fault execption + * @mm : the control struct for a set of vma using the same PDT + * @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware + * @addr : the addr which causes a memory access exception, (the contents of the CR2 register) + * + * CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault + * The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing + * the exception and recovering from it. + * (1) The contents of the CR2 register. The processor loads the CR2 register with the + * 32-bit linear address that generated the exception. The do_pgfault fun can + * use this address to locate the corresponding page directory and page-table + * entries. + * (2) An error code on the kernel stack. The error code for a page fault has a format different from + * that for other exceptions. The error code tells the exception handler three things: + * -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0) + * or to either an access rights violation or the use of a reserved bit (1). + * -- The W/R flag (bit 1) indicates whether the memory access that caused the exception + * was a read (0) or write (1). + * -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1) + * or supervisor mode (0) at the time of the exception. + */ +int +do_pgfault(struct mm_struct *mm, uint_t error_code, uintptr_t addr) { + int ret = -E_INVAL; + //try to find a vma which include addr + struct vma_struct *vma = find_vma(mm, addr); + + pgfault_num++; + //If the addr is in the range of a mm's vma? + if (vma == NULL || vma->vm_start > addr) { + cprintf("not valid addr %x, and can not find it in vma\n", addr); + goto failed; + } + + /* IF (write an existed addr ) OR + * (write an non_existed addr && addr is writable) OR + * (read an non_existed addr && addr is readable) + * THEN + * continue process + */ + uint32_t perm = PTE_U; + if (vma->vm_flags & VM_WRITE) { + perm |= READ_WRITE; + } + addr = ROUNDDOWN(addr, PGSIZE); + + ret = -E_NO_MEM; + + pte_t *ptep=NULL; + /*LAB3 EXERCISE 1: YOUR CODE + * Maybe you want help comment, BELOW comments can help you finish the code + * + * Some Useful MACROs and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * get_pte : get an pte and return the kernel virtual address of this pte for la + * if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1') + * pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup + * an addr map pa<--->la with linear address la and the PDT pgdir + * DEFINES: + * VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable + * PTE_W 0x002 // page table/directory entry flags bit : Writeable + * PTE_U 0x004 // page table/directory entry flags bit : User can access + * VARIABLES: + * mm->pgdir : the PDT of these vma + * + */ + // try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. + // (notice the 3th parameter '1') + if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { + cprintf("get_pte in do_pgfault failed\n"); + goto failed; + } + + if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr + if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { + cprintf("pgdir_alloc_page in do_pgfault failed\n"); + goto failed; + } + } + else { // if this pte is a swap entry, then load data from disk to a page with phy addr + // and call page_insert to map the phy addr with logical addr + if(swap_init_ok) { + struct Page *page=NULL; + if ((ret = swap_in(mm, addr, &page)) != 0) { + cprintf("swap_in in do_pgfault failed\n"); + goto failed; + } + page_insert(mm->pgdir, page, addr, perm); + swap_map_swappable(mm, addr, page, 1); + page->pra_vaddr = addr; + } + else { + cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep); + goto failed; + } + } + ret = 0; +failed: + return ret; +} + +bool +user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) { + if (mm != NULL) { + if (!USER_ACCESS(addr, addr + len)) { + return 0; + } + struct vma_struct *vma; + uintptr_t start = addr, end = addr + len; + while (start < end) { + if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) { + return 0; + } + if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) { + return 0; + } + if (write && (vma->vm_flags & VM_STACK)) { + if (start < vma->vm_start + PGSIZE) { //check stack start & size + return 0; + } + } + start = vma->vm_end; + } + return 1; + } + return KERN_ACCESS(addr, addr + len); +} +bool copy_string(struct mm_struct *mm, char *dst, const char *src, + size_t maxn) { + size_t alen, + part = ROUNDDOWN((uintptr_t)src + PGSIZE, PGSIZE) - (uintptr_t)src; + while (1) { + if (part > maxn) { + part = maxn; + } + if (!user_mem_check(mm, (uintptr_t)src, part, 0)) { + return 0; + } + if ((alen = strnlen(src, part)) < part) { + memcpy(dst, src, alen + 1); + return 1; + } + if (part == maxn) { + return 0; + } + memcpy(dst, src, part); + dst += part, src += part, maxn -= part; + part = PGSIZE; + } +} diff --git a/lab8/kern/mm/vmm.h b/lab8/kern/mm/vmm.h new file mode 100644 index 0000000..3c73ebd --- /dev/null +++ b/lab8/kern/mm/vmm.h @@ -0,0 +1,110 @@ +#ifndef __KERN_MM_VMM_H__ +#define __KERN_MM_VMM_H__ + +#include +#include +#include +#include +#include +#include +//pre define +struct mm_struct; + +// the virtual continuous memory area(vma), [vm_start, vm_end), +// addr belong to a vma means vma.vm_start<= addr mm_count; +} + +static inline void +set_mm_count(struct mm_struct *mm, int val) { + mm->mm_count = val; +} + +static inline int +mm_count_inc(struct mm_struct *mm) { + mm->mm_count += 1; + return mm->mm_count; +} + +static inline int +mm_count_dec(struct mm_struct *mm) { + mm->mm_count -= 1; + return mm->mm_count; +} + +static inline void +lock_mm(struct mm_struct *mm) { + if (mm != NULL) { + down(&(mm->mm_sem)); + if (current != NULL) { + mm->locked_by = current->pid; + } + } +} + +static inline void +unlock_mm(struct mm_struct *mm) { + if (mm != NULL) { + up(&(mm->mm_sem)); + mm->locked_by = 0; + } +} + +#endif /* !__KERN_MM_VMM_H__ */ + diff --git a/lab8/kern/process/entry.S b/lab8/kern/process/entry.S new file mode 100644 index 0000000..cb59759 --- /dev/null +++ b/lab8/kern/process/entry.S @@ -0,0 +1,7 @@ +.text +.globl kernel_thread_entry +kernel_thread_entry: # void kernel_thread(void) + move a0, s1 + jalr s0 + + jal do_exit diff --git a/lab8/kern/process/proc.c b/lab8/kern/process/proc.c new file mode 100644 index 0000000..3232094 --- /dev/null +++ b/lab8/kern/process/proc.c @@ -0,0 +1,1120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* ------------- process/thread mechanism design&implementation ------------- +(an simplified Linux process/thread mechanism ) +introduction: + ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads +for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to +manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory). +------------------------------ +process state : meaning -- reason + PROC_UNINIT : uninitialized -- alloc_proc + PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep + PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc, + PROC_ZOMBIE : almost dead -- do_exit + +----------------------------- +process state changing: + + alloc_proc RUNNING + + +--<----<--+ + + + proc_run + + V +-->---->--+ +PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING -- + A + + + | +--- do_exit --> PROC_ZOMBIE + + + + + -----------------------wakeup_proc---------------------------------- +----------------------------- +process relations +parent: proc->parent (proc is children) +children: proc->cptr (proc is parent) +older sibling: proc->optr (proc is younger sibling) +younger sibling: proc->yptr (proc is older sibling) +----------------------------- +related syscall for process: +SYS_exit : process exit, -->do_exit +SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc +SYS_wait : wait process -->do_wait +SYS_exec : after fork, process execute a program -->load a program and refresh the mm +SYS_clone : create child thread -->do_fork-->wakeup_proc +SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process +SYS_sleep : process sleep -->do_sleep +SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING + -->wakeup_proc-->do_wait-->do_exit +SYS_getpid : get the process's pid + +*/ + +// the process set's list +list_entry_t proc_list; + +#define HASH_SHIFT 10 +#define HASH_LIST_SIZE (1 << HASH_SHIFT) +#define pid_hashfn(x) (hash32(x, HASH_SHIFT)) + +// has list for process set based on pid +static list_entry_t hash_list[HASH_LIST_SIZE]; + +// idle proc +struct proc_struct *idleproc = NULL; +// init proc +struct proc_struct *initproc = NULL; +// current proc +struct proc_struct *current = NULL; + +static int nr_process = 0; + +void kernel_thread_entry(void); +void forkrets(struct trapframe *tf); +void switch_to(struct context *from, struct context *to); + +// alloc_proc - alloc a proc_struct and init all fields of proc_struct +static struct proc_struct * +alloc_proc(void) { + struct proc_struct *proc = kmalloc(sizeof(struct proc_struct)); + if (proc != NULL) { + //LAB4:EXERCISE1 YOUR CODE + /* + * below fields in proc_struct need to be initialized + * enum proc_state state; // Process state + * int pid; // Process ID + * int runs; // the running times of Proces + * uintptr_t kstack; // Process kernel stack + * volatile bool need_resched; // bool value: need to be rescheduled to release CPU? + * struct proc_struct *parent; // the parent process + * struct mm_struct *mm; // Process's memory management field + * struct context context; // Switch here to run process + * struct trapframe *tf; // Trap frame for current interrupt + * uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) + * uint32_t flags; // Process flag + * char name[PROC_NAME_LEN + 1]; // Process name + */ + //LAB5 YOUR CODE : (update LAB4 steps) + /* + * below fields(add in LAB5) in proc_struct need to be initialized + * uint32_t wait_state; // waiting state + * struct proc_struct *cptr, *yptr, *optr; // relations between processes + */ + + proc->state = PROC_UNINIT; + proc->pid = -1; + proc->runs = 0; + proc->kstack = 0; + proc->need_resched = 0; + proc->parent = NULL; + proc->mm = NULL; + memset(&(proc->context), 0, sizeof(struct context)); + proc->tf = NULL; + proc->cr3 = boot_cr3; + proc->flags = 0; + memset(proc->name, 0, PROC_NAME_LEN); + proc->wait_state = 0; + proc->cptr = proc->optr = proc->yptr = NULL; + proc->rq = NULL; + list_init(&(proc->run_link)); + proc->time_slice = 0; + proc->lab6_run_pool.left = proc->lab6_run_pool.right = proc->lab6_run_pool.parent = NULL; + proc->lab6_stride = 0; + proc->lab6_priority = 0; + proc->filesp = NULL; + + } + return proc; +} + +// set_proc_name - set the name of proc +char * +set_proc_name(struct proc_struct *proc, const char *name) { + memset(proc->name, 0, sizeof(proc->name)); + return memcpy(proc->name, name, PROC_NAME_LEN); +} + +// get_proc_name - get the name of proc +char * +get_proc_name(struct proc_struct *proc) { + static char name[PROC_NAME_LEN + 1]; + memset(name, 0, sizeof(name)); + return memcpy(name, proc->name, PROC_NAME_LEN); +} + +// set_links - set the relation links of process +static void +set_links(struct proc_struct *proc) { + list_add(&proc_list, &(proc->list_link)); + proc->yptr = NULL; + if ((proc->optr = proc->parent->cptr) != NULL) { + proc->optr->yptr = proc; + } + proc->parent->cptr = proc; + nr_process ++; +} + +// remove_links - clean the relation links of process +static void +remove_links(struct proc_struct *proc) { + list_del(&(proc->list_link)); + if (proc->optr != NULL) { + proc->optr->yptr = proc->yptr; + } + if (proc->yptr != NULL) { + proc->yptr->optr = proc->optr; + } + else { + proc->parent->cptr = proc->optr; + } + nr_process --; +} + +// get_pid - alloc a unique pid for process +static int +get_pid(void) { + static_assert(MAX_PID > MAX_PROCESS); + struct proc_struct *proc; + list_entry_t *list = &proc_list, *le; + static int next_safe = MAX_PID, last_pid = MAX_PID; + if (++ last_pid >= MAX_PID) { + last_pid = 1; + goto inside; + } + if (last_pid >= next_safe) { + inside: + next_safe = MAX_PID; + repeat: + le = list; + while ((le = list_next(le)) != list) { + proc = le2proc(le, list_link); + if (proc->pid == last_pid) { + if (++ last_pid >= next_safe) { + if (last_pid >= MAX_PID) { + last_pid = 1; + } + next_safe = MAX_PID; + goto repeat; + } + } + else if (proc->pid > last_pid && next_safe > proc->pid) { + next_safe = proc->pid; + } + } + } + return last_pid; +} + +// proc_run - make process "proc" running on cpu +// NOTE: before call switch_to, should load base addr of "proc"'s new PDT +void +proc_run(struct proc_struct *proc) { + if (proc != current) { + bool intr_flag; + struct proc_struct *prev = current, *next = proc; + local_intr_save(intr_flag); + { + current = proc; + lcr3(next->cr3); + flush_tlb(); + switch_to(&(prev->context), &(next->context)); + } + local_intr_restore(intr_flag); + } +} + +// forkret -- the first kernel entry point of a new thread/process +// NOTE: the addr of forkret is setted in copy_thread function +// after switch_to, the current proc will execute here. +static void +forkret(void) { + forkrets(current->tf); +} + +// hash_proc - add proc into proc hash_list +static void +hash_proc(struct proc_struct *proc) { + list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link)); +} + +// unhash_proc - delete proc from proc hash_list +static void +unhash_proc(struct proc_struct *proc) { + list_del(&(proc->hash_link)); +} + +// find_proc - find proc frome proc hash_list according to pid +struct proc_struct * +find_proc(int pid) { + if (0 < pid && pid < MAX_PID) { + list_entry_t *list = hash_list + pid_hashfn(pid), *le = list; + while ((le = list_next(le)) != list) { + struct proc_struct *proc = le2proc(le, hash_link); + if (proc->pid == pid) { + return proc; + } + } + } + return NULL; +} + +// kernel_thread - create a kernel thread using "fn" function +// NOTE: the contents of temp trapframe tf will be copied to +// proc->tf in do_fork-->copy_thread function +int +kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) { + struct trapframe tf; + memset(&tf, 0, sizeof(struct trapframe)); + tf.gpr.s0 = (uintptr_t)fn; + tf.gpr.s1 = (uintptr_t)arg; + tf.status = (read_csr(sstatus) | SSTATUS_SPP | SSTATUS_SPIE) & ~SSTATUS_SIE; + tf.epc = (uintptr_t)kernel_thread_entry; + return do_fork(clone_flags | CLONE_VM, 0, &tf); +} + +// setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack +static int +setup_kstack(struct proc_struct *proc) { + struct Page *page = alloc_pages(KSTACKPAGE); + if (page != NULL) { + proc->kstack = (uintptr_t)page2kva(page); + return 0; + } + return -E_NO_MEM; +} + +// put_kstack - free the memory space of process kernel stack +static void +put_kstack(struct proc_struct *proc) { + free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE); +} + +// setup_pgdir - alloc one page as PDT +static int +setup_pgdir(struct mm_struct *mm) { + struct Page *page; + if ((page = alloc_page()) == NULL) { + return -E_NO_MEM; + } + pde_t *pgdir = page2kva(page); + memset(pgdir, 0, PGSIZE); + pgdir[511] = boot_pgdir[511]; + setup_kernel_io_mapping(pgdir); + + mm->pgdir = pgdir; + return 0; +} + +// put_pgdir - free the memory space of PDT +static void +put_pgdir(struct mm_struct *mm) { + free_kernel_io_mapping(mm->pgdir); + free_page(kva2page(mm->pgdir)); +} + +// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags +// - if clone_flags & CLONE_VM, then "share" ; else "duplicate" +static int +copy_mm(uint32_t clone_flags, struct proc_struct *proc) { + struct mm_struct *mm, *oldmm = current->mm; + + /* current is a kernel thread */ + if (oldmm == NULL) { + return 0; + } + if (clone_flags & CLONE_VM) { + mm = oldmm; + goto good_mm; + } + int ret = -E_NO_MEM; + if ((mm = mm_create()) == NULL) { + goto bad_mm; + } + if (setup_pgdir(mm) != 0) { + goto bad_pgdir_cleanup_mm; + } + lock_mm(oldmm); + { + ret = dup_mmap(mm, oldmm); + } + unlock_mm(oldmm); + + if (ret != 0) { + goto bad_dup_cleanup_mmap; + } + +good_mm: + mm_count_inc(mm); + proc->mm = mm; + proc->cr3 = PADDR(mm->pgdir); + return 0; +bad_dup_cleanup_mmap: + exit_mmap(mm); + put_pgdir(mm); +bad_pgdir_cleanup_mm: + mm_destroy(mm); +bad_mm: + return ret; +} + +// copy_thread - setup the trapframe on the process's kernel stack top and +// - setup the kernel entry point and stack of process +static void +copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) { + proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1; + *(proc->tf) = *tf; + + // Set a0 to 0 so a child process knows it's just forked + proc->tf->gpr.a0 = 0; + proc->tf->gpr.sp = (esp == 0) ? (uintptr_t)proc->tf : esp; + + proc->context.ra = (uintptr_t)forkret; + proc->context.sp = (uintptr_t)(proc->tf); +} +//copy_files&put_files function used by do_fork in LAB8 +//copy the files_struct from current to proc +static int +copy_files(uint32_t clone_flags, struct proc_struct *proc) { + struct files_struct *filesp, *old_filesp = current->filesp; + assert(old_filesp != NULL); + + if (clone_flags & CLONE_FS) { + filesp = old_filesp; + goto good_files_struct; + } + + int ret = -E_NO_MEM; + if ((filesp = files_create()) == NULL) { + goto bad_files_struct; + } + + if ((ret = dup_files(filesp, old_filesp)) != 0) { + goto bad_dup_cleanup_fs; + } + +good_files_struct: + files_count_inc(filesp); + proc->filesp = filesp; + return 0; + +bad_dup_cleanup_fs: + files_destroy(filesp); +bad_files_struct: + return ret; +} + +//decrease the ref_count of files, and if ref_count==0, then destroy files_struct +static void +put_files(struct proc_struct *proc) { + struct files_struct *filesp = proc->filesp; + if (filesp != NULL) { + if (files_count_dec(filesp) == 0) { + files_destroy(filesp); + } + } +} + +/* do_fork - parent process for a new child process + * @clone_flags: used to guide how to clone the child process + * @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread. + * @tf: the trapframe info, which will be copied to child process's proc->tf + */ +int +do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) { + int ret = -E_NO_FREE_PROC; + struct proc_struct *proc; + if (nr_process >= MAX_PROCESS) { + goto fork_out; + } + ret = -E_NO_MEM; + //LAB4:EXERCISE2 YOUR CODE + //LAB8:EXERCISE2 YOUR CODE HINT:how to copy the fs in parent's proc_struct? + /* + * Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * alloc_proc: create a proc struct and init fields (lab4:exercise1) + * setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack + * copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags + * if clone_flags & CLONE_VM, then "share" ; else "duplicate" + * copy_thread: setup the trapframe on the process's kernel stack top and + * setup the kernel entry point and stack of process + * hash_proc: add proc into proc hash_list + * get_pid: alloc a unique pid for process + * wakeup_proc: set proc->state = PROC_RUNNABLE + * VARIABLES: + * proc_list: the process set's list + * nr_process: the number of process set + */ + + // 1. call alloc_proc to allocate a proc_struct + // 2. call setup_kstack to allocate a kernel stack for child process + // 3. call copy_mm to dup OR share mm according clone_flag + // 4. call copy_thread to setup tf & context in proc_struct + // 5. insert proc_struct into hash_list && proc_list + // 6. call wakeup_proc to make the new child process RUNNABLE + // 7. set ret vaule using child proc's pid + + //LAB5 YOUR CODE : (update LAB4 steps) + /* Some Functions + * set_links: set the relation links of process. ALSO SEE: remove_links: lean the relation links of process + * ------------------- + * update step 1: set child proc's parent to current process, make sure current process's wait_state is 0 + * update step 5: insert proc_struct into hash_list && proc_list, set the relation links of process + */ + if ((proc = alloc_proc()) == NULL) { + goto fork_out; + } + + proc->parent = current; + assert(current->wait_state == 0); + + if (setup_kstack(proc) != 0) { + goto bad_fork_cleanup_proc; + } + if (copy_files(clone_flags, proc) != 0) { //for LAB8 + goto bad_fork_cleanup_kstack; + } + if (copy_mm(clone_flags, proc) != 0) { + goto bad_fork_cleanup_fs; + } + copy_thread(proc, stack, tf); + + bool intr_flag; + local_intr_save(intr_flag); + { + proc->pid = get_pid(); + hash_proc(proc); + set_links(proc); + } + local_intr_restore(intr_flag); + + wakeup_proc(proc); + + ret = proc->pid; +fork_out: + return ret; + +bad_fork_cleanup_fs: //for LAB8 + put_files(proc); +bad_fork_cleanup_kstack: + put_kstack(proc); +bad_fork_cleanup_proc: + kfree(proc); + goto fork_out; +} + + +// do_exit - called by sys_exit +// 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process +// 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself. +// 3. call scheduler to switch to other process +int +do_exit(int error_code) { + if (current == idleproc) { + panic("idleproc exit.\n"); + } + if (current == initproc) { + panic("initproc exit.\n"); + } + struct mm_struct *mm = current->mm; + if (mm != NULL) { + lcr3(boot_cr3); + if (mm_count_dec(mm) == 0) { + exit_mmap(mm); + put_pgdir(mm); + mm_destroy(mm); + } + current->mm = NULL; + put_files(current); + } + current->state = PROC_ZOMBIE; + current->exit_code = error_code; + bool intr_flag; + struct proc_struct *proc; + local_intr_save(intr_flag); + { + proc = current->parent; + if (proc->wait_state == WT_CHILD) { + wakeup_proc(proc); + } + while (current->cptr != NULL) { + proc = current->cptr; + current->cptr = proc->optr; + + proc->yptr = NULL; + if ((proc->optr = initproc->cptr) != NULL) { + initproc->cptr->yptr = proc; + } + proc->parent = initproc; + initproc->cptr = proc; + if (proc->state == PROC_ZOMBIE) { + if (initproc->wait_state == WT_CHILD) { + wakeup_proc(initproc); + } + } + } + } + local_intr_restore(intr_flag); + schedule(); + panic("do_exit will not return!! %d.\n", current->pid); +} + +//load_icode_read is used by load_icode in LAB8 +static int +load_icode_read(int fd, void *buf, size_t len, off_t offset) { + int ret; + if ((ret = sysfile_seek(fd, offset, LSEEK_SET)) != 0) { + return ret; + } + if ((ret = sysfile_read(fd, buf, len)) != len) { + return (ret < 0) ? ret : -1; + } + return 0; +} + +// load_icode - called by sys_exec-->do_execve + +static int +load_icode(int fd, int argc, char **kargv) { + /* LAB8:EXERCISE2 YOUR CODE HINT:how to load the file with handler fd in to process's memory? how to setup argc/argv? + * MACROs or Functions: + * mm_create - create a mm + * setup_pgdir - setup pgdir in mm + * load_icode_read - read raw data content of program file + * mm_map - build new vma + * pgdir_alloc_page - allocate new memory for TEXT/DATA/BSS/stack parts + * lcr3 - update Page Directory Addr Register -- CR3 + */ + /* (1) create a new mm for current process + * (2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT + * (3) copy TEXT/DATA/BSS parts in binary to memory space of process + * (3.1) read raw data content in file and resolve elfhdr + * (3.2) read raw data content in file and resolve proghdr based on info in elfhdr + * (3.3) call mm_map to build vma related to TEXT/DATA + * (3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file + * and copy them into the new allocated pages + * (3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages + * (4) call mm_map to setup user stack, and put parameters into user stack + * (5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO) + * (6) setup uargc and uargv in user stacks + * (7) setup trapframe for user environment + * (8) if up steps failed, you should cleanup the env. + */ + assert(argc >= 0 && argc <= EXEC_MAX_ARG_NUM); + if (current->mm != NULL) { + panic("load_icode: current->mm must be empty.\n"); + } + + int ret = -E_NO_MEM; + struct mm_struct *mm; + if ((mm = mm_create()) == NULL) { + goto bad_mm; + } + if (setup_pgdir(mm) != 0) { + goto bad_pgdir_cleanup_mm; + } + + struct Page *page; + + struct elfhdr __elf, *elf = &__elf; + if ((ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0)) != 0) { + goto bad_elf_cleanup_pgdir; + } + + if (elf->e_magic != ELF_MAGIC) { + ret = -E_INVAL_ELF; + goto bad_elf_cleanup_pgdir; + } + struct proghdr __ph, *ph = &__ph; + uint32_t vm_flags, perm, phnum; + for (phnum = 0; phnum < elf->e_phnum; phnum ++) { + off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum; + if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) { + goto bad_cleanup_mmap; + } + if (ph->p_type != ELF_PT_LOAD) { + continue ; + } + if (ph->p_filesz > ph->p_memsz) { + ret = -E_INVAL_ELF; + goto bad_cleanup_mmap; + } + if (ph->p_filesz == 0) { + // continue ; + // do nothing here since static variables may not occupy any space + } + vm_flags = 0, perm = PTE_U | PTE_V; + if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; + if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE; + if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ; + // modify the perm bits here for RISC-V + if (vm_flags & VM_READ) perm |= PTE_R; + if (vm_flags & VM_WRITE) perm |= (PTE_W | PTE_R); + if (vm_flags & VM_EXEC) perm |= PTE_X; + if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) { + goto bad_cleanup_mmap; + } + off_t offset = ph->p_offset; + size_t off, size; + uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE); + + ret = -E_NO_MEM; + + end = ph->p_va + ph->p_filesz; + while (start < end) { + if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { + ret = -E_NO_MEM; + goto bad_cleanup_mmap; + } + off = start - la, size = PGSIZE - off, la += PGSIZE; + if (end < la) { + size -= la - end; + } + if ((ret = load_icode_read(fd, page2kva(page) + off, size, offset)) != 0) { + goto bad_cleanup_mmap; + } + start += size, offset += size; + } + end = ph->p_va + ph->p_memsz; + + if (start < la) { + /* ph->p_memsz == ph->p_filesz */ + if (start == end) { + continue ; + } + off = start + PGSIZE - la, size = PGSIZE - off; + if (end < la) { + size -= la - end; + } + memset(page2kva(page) + off, 0, size); + start += size; + assert((end < la && start == end) || (end >= la && start == la)); + } + while (start < end) { + if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { + ret = -E_NO_MEM; + goto bad_cleanup_mmap; + } + off = start - la, size = PGSIZE - off, la += PGSIZE; + if (end < la) { + size -= la - end; + } + memset(page2kva(page) + off, 0, size); + start += size; + } + } + sysfile_close(fd); + + vm_flags = VM_READ | VM_WRITE | VM_STACK; + if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) { + goto bad_cleanup_mmap; + } + assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL); + assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL); + assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL); + assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL); + + mm_count_inc(mm); + current->mm = mm; + current->cr3 = PADDR(mm->pgdir); + lcr3(PADDR(mm->pgdir)); + flush_tlb(); + + //setup argc, argv + uint32_t argv_size=0, i; + for (i = 0; i < argc; i ++) { + argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1; + } + + uintptr_t stacktop = USTACKTOP - (argv_size/sizeof(long)+1)*sizeof(long); + char** uargv=(char **)(stacktop - argc * sizeof(char *)); + + argv_size = 0; + for (i = 0; i < argc; i ++) { + uargv[i] = strcpy((char *)(stacktop + argv_size ), kargv[i]); + argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1; + } + + stacktop = (uintptr_t)uargv - sizeof(long); + *(long *)stacktop = argc; + + struct trapframe *tf = current->tf; + // Keep sstatus + uintptr_t sstatus = tf->status; + memset(tf, 0, sizeof(struct trapframe)); + tf->gpr.sp = stacktop; + tf->epc = elf->e_entry; + tf->status = sstatus & ~(SSTATUS_SPP | SSTATUS_SPIE); + ret = 0; +out: + return ret; +bad_cleanup_mmap: + exit_mmap(mm); +bad_elf_cleanup_pgdir: + put_pgdir(mm); +bad_pgdir_cleanup_mm: + mm_destroy(mm); +bad_mm: + goto out; +} + +// this function isn't very correct in LAB8 +static void +put_kargv(int argc, char **kargv) { + while (argc > 0) { + kfree(kargv[-- argc]); + } +} + +static int +copy_kargv(struct mm_struct *mm, int argc, char **kargv, const char **argv) { + int i, ret = -E_INVAL; + if (!user_mem_check(mm, (uintptr_t)argv, sizeof(const char *) * argc, 0)) { + return ret; + } + for (i = 0; i < argc; i ++) { + char *buffer; + if ((buffer = kmalloc(EXEC_MAX_ARG_LEN + 1)) == NULL) { + goto failed_nomem; + } + if (!copy_string(mm, buffer, argv[i], EXEC_MAX_ARG_LEN + 1)) { + kfree(buffer); + goto failed_cleanup; + } + kargv[i] = buffer; + } + return 0; + +failed_nomem: + ret = -E_NO_MEM; +failed_cleanup: + put_kargv(i, kargv); + return ret; +} + +// do_execve - call exit_mmap(mm)&put_pgdir(mm) to reclaim memory space of current process +// - call load_icode to setup new memory space accroding binary prog. +int +do_execve(const char *name, int argc, const char **argv) { + static_assert(EXEC_MAX_ARG_LEN >= FS_MAX_FPATH_LEN); + struct mm_struct *mm = current->mm; + if (!(argc >= 1 && argc <= EXEC_MAX_ARG_NUM)) { + return -E_INVAL; + } + + char local_name[PROC_NAME_LEN + 1]; + memset(local_name, 0, sizeof(local_name)); + + char *kargv[EXEC_MAX_ARG_NUM]; + const char *path; + + int ret = -E_INVAL; + + lock_mm(mm); + if (name == NULL) { + snprintf(local_name, sizeof(local_name), " %d", current->pid); + } + else { + if (!copy_string(mm, local_name, name, sizeof(local_name))) { + unlock_mm(mm); + return ret; + } + } + if ((ret = copy_kargv(mm, argc, kargv, argv)) != 0) { + unlock_mm(mm); + return ret; + } + path = argv[0]; + unlock_mm(mm); + files_closeall(current->filesp); + + /* sysfile_open will check the first argument path, thus we have to use a user-space pointer, and argv[0] may be incorrect */ + int fd; + if ((ret = fd = sysfile_open(path, O_RDONLY)) < 0) { + goto execve_exit; + } + if (mm != NULL) { + lcr3(boot_cr3); + if (mm_count_dec(mm) == 0) { + exit_mmap(mm); + put_pgdir(mm); + mm_destroy(mm); + } + current->mm = NULL; + } + ret= -E_NO_MEM;; + if ((ret = load_icode(fd, argc, kargv)) != 0) { + goto execve_exit; + } + put_kargv(argc, kargv); + set_proc_name(current, local_name); + return 0; + +execve_exit: + put_kargv(argc, kargv); + do_exit(ret); + panic("already exit: %e.\n", ret); +} + +// do_yield - ask the scheduler to reschedule +int +do_yield(void) { + current->need_resched = 1; + return 0; +} + +// do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack +// - proc struct of this child. +// NOTE: only after do_wait function, all resources of the child proces are free. +int +do_wait(int pid, int *code_store) { + struct mm_struct *mm = current->mm; + if (code_store != NULL) { + if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) { + return -E_INVAL; + } + } + + struct proc_struct *proc; + bool intr_flag, haskid; +repeat: + haskid = 0; + if (pid != 0) { + proc = find_proc(pid); + if (proc != NULL && proc->parent == current) { + haskid = 1; + if (proc->state == PROC_ZOMBIE) { + goto found; + } + } + } + else { + proc = current->cptr; + for (; proc != NULL; proc = proc->optr) { + haskid = 1; + if (proc->state == PROC_ZOMBIE) { + goto found; + } + } + } + if (haskid) { + current->state = PROC_SLEEPING; + current->wait_state = WT_CHILD; + schedule(); + if (current->flags & PF_EXITING) { + do_exit(-E_KILLED); + } + goto repeat; + } + return -E_BAD_PROC; + +found: + if (proc == idleproc || proc == initproc) { + panic("wait idleproc or initproc.\n"); + } + if (code_store != NULL) { + *code_store = proc->exit_code; + } + local_intr_save(intr_flag); + { + unhash_proc(proc); + remove_links(proc); + } + local_intr_restore(intr_flag); + put_kstack(proc); + kfree(proc); + return 0; +} +// do_kill - kill process with pid by set this process's flags with PF_EXITING +int +do_kill(int pid) { + struct proc_struct *proc; + if ((proc = find_proc(pid)) != NULL) { + if (!(proc->flags & PF_EXITING)) { + proc->flags |= PF_EXITING; + if (proc->wait_state & WT_INTERRUPTED) { + wakeup_proc(proc); + } + return 0; + } + return -E_KILLED; + } + return -E_INVAL; +} + +// kernel_execve - do SYS_exec syscall to exec a user program called by user_main kernel_thread +static int +kernel_execve(const char *name, const char **argv) { + int64_t argc = 0, ret; + while (argv[argc] != NULL){ + argc++; + } + asm volatile( + "li a0, %1\n" + "ld a1, %2\n" + "ld a2, %3\n" + "ld a3, %4\n" + "li a7, 10\n" + "ebreak\n" + "sd a0, %0\n" + : "=m"(ret) + : "i"(SYS_exec), "m"(name), "m"(argc), "m"(argv) + : "memory"); +// cprintf("ret = %d\n", ret); + return ret; +} +#define __KERNEL_EXECVE(name, path, ...) ({ \ +const char *argv[] = {path, ##__VA_ARGS__, NULL}; \ + cprintf("kernel_execve: pid = %d, name = \"%s\".\n", \ + current->pid, name); \ + kernel_execve(name, argv); \ +}) + +#define KERNEL_EXECVE(x, ...) __KERNEL_EXECVE(#x, #x, ##__VA_ARGS__) + +#define KERNEL_EXECVE2(x, ...) KERNEL_EXECVE(x, ##__VA_ARGS__) + +#define __KERNEL_EXECVE3(x, s, ...) KERNEL_EXECVE(x, #s, ##__VA_ARGS__) + +#define KERNEL_EXECVE3(x, s, ...) __KERNEL_EXECVE3(x, s, ##__VA_ARGS__) + +// user_main - kernel thread used to exec a user program +static int +user_main(void *arg) { +#ifdef TEST +#ifdef TESTSCRIPT + KERNEL_EXECVE3(TEST, TESTSCRIPT); +#else + KERNEL_EXECVE2(TEST); +#endif +#else + KERNEL_EXECVE(sh); +#endif + panic("user_main execve failed.\n"); +} + +// init_main - the second kernel thread used to create user_main kernel threads +static int +init_main(void *arg) { + int ret; + if ((ret = vfs_set_bootfs("disk0:")) != 0) { + panic("set boot fs failed: %e.\n", ret); + } + size_t nr_free_pages_store = nr_free_pages(); + size_t kernel_allocated_store = kallocated(); + + int pid = kernel_thread(user_main, NULL, 0); + if (pid <= 0) { + panic("create user_main failed.\n"); + } + extern void check_sync(void); + //check_sync(); // check philosopher sync problem + + while (do_wait(0, NULL) == 0) { + schedule(); + } + + fs_cleanup(); + + cprintf("all user-mode processes have quit.\n"); + assert(initproc->cptr == NULL && initproc->yptr == NULL && initproc->optr == NULL); + assert(nr_process == 2); + assert(list_next(&proc_list) == &(initproc->list_link)); + assert(list_prev(&proc_list) == &(initproc->list_link)); + + cprintf("init check memory pass.\n"); + return 0; +} + +// proc_init - set up the first kernel thread idleproc "idle" by itself and +// - create the second kernel thread init_main +void +proc_init(void) { + int i; + + list_init(&proc_list); + for (i = 0; i < HASH_LIST_SIZE; i ++) { + list_init(hash_list + i); + } + + if ((idleproc = alloc_proc()) == NULL) { + panic("cannot alloc idleproc.\n"); + } + + idleproc->pid = 0; + idleproc->state = PROC_RUNNABLE; + idleproc->kstack = (uintptr_t)bootstack; + idleproc->need_resched = 1; + + + if ((idleproc->filesp = files_create()) == NULL) { + panic("create filesp (idleproc) failed.\n"); + } + files_count_inc(idleproc->filesp); + + set_proc_name(idleproc, "idle"); + nr_process ++; + + current = idleproc; + + int pid = kernel_thread(init_main, NULL, 0); + if (pid <= 0) { + panic("create init_main failed.\n"); + } + + initproc = find_proc(pid); + set_proc_name(initproc, "init"); + + assert(idleproc != NULL && idleproc->pid == 0); + assert(initproc != NULL && initproc->pid == 1); +} + +// cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works +void +cpu_idle(void) { + while (1) { + if (current->need_resched) { + schedule(); + } + } +} +//FOR LAB6, set the process's priority (bigger value will get more CPU time) +void +lab6_set_priority(uint32_t priority) +{ + cprintf("set priority to %d\n", priority); + if (priority == 0) + current->lab6_priority = 1; + else current->lab6_priority = priority; +} +// do_sleep - set current process state to sleep and add timer with "time" +// - then call scheduler. if process run again, delete timer first. +int +do_sleep(unsigned int time) { + if (time == 0) { + return 0; + } + bool intr_flag; + local_intr_save(intr_flag); + timer_t __timer, *timer = timer_init(&__timer, current, time); + current->state = PROC_SLEEPING; + current->wait_state = WT_TIMER; + add_timer(timer); + local_intr_restore(intr_flag); + + schedule(); + + del_timer(timer); + return 0; +} diff --git a/lab8/kern/process/proc.h b/lab8/kern/process/proc.h new file mode 100644 index 0000000..0a6824a --- /dev/null +++ b/lab8/kern/process/proc.h @@ -0,0 +1,106 @@ +#ifndef __KERN_PROCESS_PROC_H__ +#define __KERN_PROCESS_PROC_H__ + +#include +#include +#include +#include +#include + +// process's state in his life cycle +enum proc_state { + PROC_UNINIT = 0, // uninitialized + PROC_SLEEPING, // sleeping + PROC_RUNNABLE, // runnable(maybe running) + PROC_ZOMBIE, // almost dead, and wait parent proc to reclaim his resource +}; + +struct context { + uintptr_t ra; + uintptr_t sp; + uintptr_t s0; + uintptr_t s1; + uintptr_t s2; + uintptr_t s3; + uintptr_t s4; + uintptr_t s5; + uintptr_t s6; + uintptr_t s7; + uintptr_t s8; + uintptr_t s9; + uintptr_t s10; + uintptr_t s11; +}; + +#define PROC_NAME_LEN 15 +#define MAX_PROCESS 4096 +#define MAX_PID (MAX_PROCESS * 2) + +extern list_entry_t proc_list; + +struct inode; + +struct proc_struct { + enum proc_state state; // Process state + int pid; // Process ID + int runs; // the running times of Proces + uintptr_t kstack; // Process kernel stack + volatile bool need_resched; // bool value: need to be rescheduled to release CPU? + struct proc_struct *parent; // the parent process + struct mm_struct *mm; // Process's memory management field + struct context context; // Switch here to run process + struct trapframe *tf; // Trap frame for current interrupt + uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) + uint32_t flags; // Process flag + char name[PROC_NAME_LEN + 1]; // Process name + list_entry_t list_link; // Process link list + list_entry_t hash_link; // Process hash list + int exit_code; // exit code (be sent to parent proc) + uint32_t wait_state; // waiting state + struct proc_struct *cptr, *yptr, *optr; // relations between processes + struct run_queue *rq; // running queue contains Process + list_entry_t run_link; // the entry linked in run queue + int time_slice; // time slice for occupying the CPU + skew_heap_entry_t lab6_run_pool; // FOR LAB6 ONLY: the entry in the run pool + uint32_t lab6_stride; // FOR LAB6 ONLY: the current stride of the process + uint32_t lab6_priority; // FOR LAB6 ONLY: the priority of process, set by lab6_set_priority(uint32_t) + struct files_struct *filesp; // the file related info(pwd, files_count, files_array, fs_semaphore) of process +}; + +#define PF_EXITING 0x00000001 // getting shutdown + +#define WT_CHILD (0x00000001 | WT_INTERRUPTED) +#define WT_INTERRUPTED 0x80000000 // the wait state could be interrupted + +#define WT_CHILD (0x00000001 | WT_INTERRUPTED) // wait child process +#define WT_KSEM 0x00000100 // wait kernel semaphore +#define WT_TIMER (0x00000002 | WT_INTERRUPTED) // wait timer +#define WT_KBD (0x00000004 | WT_INTERRUPTED) // wait the input of keyboard + +#define le2proc(le, member) \ + to_struct((le), struct proc_struct, member) + +extern struct proc_struct *idleproc, *initproc, *current; + +void proc_init(void); +void proc_run(struct proc_struct *proc); +int kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags); + +char *set_proc_name(struct proc_struct *proc, const char *name); +char *get_proc_name(struct proc_struct *proc); +void cpu_idle(void) __attribute__((noreturn)); + +//FOR LAB6, set the process's priority (bigger value will get more CPU time) +void lab6_set_priority(uint32_t priority); + + +struct proc_struct *find_proc(int pid); +int do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf); +int do_exit(int error_code); +int do_yield(void); +int do_execve(const char *name, int argc, const char **argv); +int do_wait(int pid, int *code_store); +int do_kill(int pid); +int do_sleep(unsigned int time); +#endif /* !__KERN_PROCESS_PROC_H__ */ + diff --git a/lab8/kern/process/switch.S b/lab8/kern/process/switch.S new file mode 100644 index 0000000..77efab7 --- /dev/null +++ b/lab8/kern/process/switch.S @@ -0,0 +1,39 @@ +#include + +.text +# void switch_to(struct proc_struct* from, struct proc_struct* to) +.globl switch_to +switch_to: + # save from's registers + STORE ra, 0*REGBYTES(a0) + STORE sp, 1*REGBYTES(a0) + STORE s0, 2*REGBYTES(a0) + STORE s1, 3*REGBYTES(a0) + STORE s2, 4*REGBYTES(a0) + STORE s3, 5*REGBYTES(a0) + STORE s4, 6*REGBYTES(a0) + STORE s5, 7*REGBYTES(a0) + STORE s6, 8*REGBYTES(a0) + STORE s7, 9*REGBYTES(a0) + STORE s8, 10*REGBYTES(a0) + STORE s9, 11*REGBYTES(a0) + STORE s10, 12*REGBYTES(a0) + STORE s11, 13*REGBYTES(a0) + + # restore to's registers + LOAD ra, 0*REGBYTES(a1) + LOAD sp, 1*REGBYTES(a1) + LOAD s0, 2*REGBYTES(a1) + LOAD s1, 3*REGBYTES(a1) + LOAD s2, 4*REGBYTES(a1) + LOAD s3, 5*REGBYTES(a1) + LOAD s4, 6*REGBYTES(a1) + LOAD s5, 7*REGBYTES(a1) + LOAD s6, 8*REGBYTES(a1) + LOAD s7, 9*REGBYTES(a1) + LOAD s8, 10*REGBYTES(a1) + LOAD s9, 11*REGBYTES(a1) + LOAD s10, 12*REGBYTES(a1) + LOAD s11, 13*REGBYTES(a1) + + ret diff --git a/lab8/kern/schedule/default_sched.h b/lab8/kern/schedule/default_sched.h new file mode 100644 index 0000000..2f21fbd --- /dev/null +++ b/lab8/kern/schedule/default_sched.h @@ -0,0 +1,9 @@ +#ifndef __KERN_SCHEDULE_SCHED_RR_H__ +#define __KERN_SCHEDULE_SCHED_RR_H__ + +#include + +extern struct sched_class default_sched_class; + +#endif /* !__KERN_SCHEDULE_SCHED_RR_H__ */ + diff --git a/lab8/kern/schedule/default_sched_c b/lab8/kern/schedule/default_sched_c new file mode 100644 index 0000000..2316990 --- /dev/null +++ b/lab8/kern/schedule/default_sched_c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +static void +RR_init(struct run_queue *rq) { + list_init(&(rq->run_list)); + rq->proc_num = 0; +} + +static void +RR_enqueue(struct run_queue *rq, struct proc_struct *proc) { + assert(list_empty(&(proc->run_link))); + list_add_before(&(rq->run_list), &(proc->run_link)); + if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) { + proc->time_slice = rq->max_time_slice; + } + proc->rq = rq; + rq->proc_num ++; +} + +static void +RR_dequeue(struct run_queue *rq, struct proc_struct *proc) { + assert(!list_empty(&(proc->run_link)) && proc->rq == rq); + list_del_init(&(proc->run_link)); + rq->proc_num --; +} + +static struct proc_struct * +RR_pick_next(struct run_queue *rq) { + list_entry_t *le = list_next(&(rq->run_list)); + if (le != &(rq->run_list)) { + return le2proc(le, run_link); + } + return NULL; +} + +static void +RR_proc_tick(struct run_queue *rq, struct proc_struct *proc) { + if (proc->time_slice > 0) { + proc->time_slice --; + } + if (proc->time_slice == 0) { + proc->need_resched = 1; + } +} + +struct sched_class default_sched_class = { + .name = "RR_scheduler", + .init = RR_init, + .enqueue = RR_enqueue, + .dequeue = RR_dequeue, + .pick_next = RR_pick_next, + .proc_tick = RR_proc_tick, +}; + diff --git a/lab8/kern/schedule/default_sched_stride.c b/lab8/kern/schedule/default_sched_stride.c new file mode 100644 index 0000000..d8e176d --- /dev/null +++ b/lab8/kern/schedule/default_sched_stride.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include + +#define USE_SKEW_HEAP 1 + +/* You should define the BigStride constant here*/ +/* LAB6: YOUR CODE */ +#define BIG_STRIDE (1 << 30) /* you should give a value, and is ??? */ + +/* The compare function for two skew_heap_node_t's and the + * corresponding procs*/ +static int +proc_stride_comp_f(void *a, void *b) +{ + struct proc_struct *p = le2proc(a, lab6_run_pool); + struct proc_struct *q = le2proc(b, lab6_run_pool); + int32_t c = p->lab6_stride - q->lab6_stride; + if (c > 0) return 1; + else if (c == 0) return 0; + else return -1; +} + +/* + * stride_init initializes the run-queue rq with correct assignment for + * member variables, including: + * + * - run_list: should be a empty list after initialization. + * - lab6_run_pool: NULL + * - proc_num: 0 + * - max_time_slice: no need here, the variable would be assigned by the caller. + * + * hint: see libs/list.h for routines of the list structures. + */ +static void +stride_init(struct run_queue *rq) { + /* LAB6: YOUR CODE + * (1) init the ready process list: rq->run_list + * (2) init the run pool: rq->lab6_run_pool + * (3) set number of process: rq->proc_num to 0 + */ + list_init(&(rq->run_list)); + rq->lab6_run_pool = NULL; + rq->proc_num = 0; +} + +/* + * stride_enqueue inserts the process ``proc'' into the run-queue + * ``rq''. The procedure should verify/initialize the relevant members + * of ``proc'', and then put the ``lab6_run_pool'' node into the + * queue(since we use priority queue here). The procedure should also + * update the meta date in ``rq'' structure. + * + * proc->time_slice denotes the time slices allocation for the + * process, which should set to rq->max_time_slice. + * + * hint: see libs/skew_heap.h for routines of the priority + * queue structures. + */ +static void +stride_enqueue(struct run_queue *rq, struct proc_struct *proc) { + /* LAB6: YOUR CODE + * (1) insert the proc into rq correctly + * NOTICE: you can use skew_heap or list. Important functions + * skew_heap_insert: insert a entry into skew_heap + * list_add_before: insert a entry into the last of list + * (2) recalculate proc->time_slice + * (3) set proc->rq pointer to rq + * (4) increase rq->proc_num + */ + rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f); + + if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) { + proc->time_slice = rq->max_time_slice; + } + + proc->rq = rq; + rq->proc_num++; +} + +/* + * stride_dequeue removes the process ``proc'' from the run-queue + * ``rq'', the operation would be finished by the skew_heap_remove + * operations. Remember to update the ``rq'' structure. + * + * hint: see libs/skew_heap.h for routines of the priority + * queue structures. + */ +static void +stride_dequeue(struct run_queue *rq, struct proc_struct *proc) { + /* LAB6: YOUR CODE + * (1) remove the proc from rq correctly + * NOTICE: you can use skew_heap or list. Important functions + * skew_heap_remove: remove a entry from skew_heap + * list_del_init: remove a entry from the list + */ + assert(proc->rq == rq && rq->proc_num > 0); + rq->lab6_run_pool = skew_heap_remove(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f); + rq->proc_num--; +} +/* + * stride_pick_next pick the element from the ``run-queue'', with the + * minimum value of stride, and returns the corresponding process + * pointer. The process pointer would be calculated by macro le2proc, + * see kern/process/proc.h for definition. Return NULL if + * there is no process in the queue. + * + * When one proc structure is selected, remember to update the stride + * property of the proc. (stride += BIG_STRIDE / priority) + * + * hint: see libs/skew_heap.h for routines of the priority + * queue structures. + */ +static struct proc_struct * +stride_pick_next(struct run_queue *rq) { + /* LAB6: YOUR CODE + * (1) get a proc_struct pointer p with the minimum value of stride + (1.1) If using skew_heap, we can use le2proc get the p from rq->lab6_run_poll + (1.2) If using list, we have to search list to find the p with minimum stride value + * (2) update p;s stride value: p->lab6_stride + * (3) return p + */ + if (rq->lab6_run_pool == NULL) { + return NULL; + } + struct proc_struct* proc = le2proc(rq->lab6_run_pool, lab6_run_pool); + proc->lab6_stride += proc->lab6_priority ? BIG_STRIDE / proc->lab6_priority : BIG_STRIDE; + return proc; +} + +/* + * stride_proc_tick works with the tick event of current process. You + * should check whether the time slices for current process is + * exhausted and update the proc struct ``proc''. proc->time_slice + * denotes the time slices left for current + * process. proc->need_resched is the flag variable for process + * switching. + */ +static void +stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) { + /* LAB6: YOUR CODE */ + if (proc->time_slice > 0) { + proc->time_slice--; + } + if (proc->time_slice == 0) { + proc->need_resched = 1; + } +} + +struct sched_class default_sched_class = { + .name = "stride_scheduler", + .init = stride_init, + .enqueue = stride_enqueue, + .dequeue = stride_dequeue, + .pick_next = stride_pick_next, + .proc_tick = stride_proc_tick, +}; diff --git a/lab8/kern/schedule/sched.c b/lab8/kern/schedule/sched.c new file mode 100644 index 0000000..e5ce8a3 --- /dev/null +++ b/lab8/kern/schedule/sched.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include + +// the list of timer +static list_entry_t timer_list; + +static struct sched_class *sched_class; + +static struct run_queue *rq; + +static inline void +sched_class_enqueue(struct proc_struct *proc) { + if (proc != idleproc) { + sched_class->enqueue(rq, proc); + } +} + +static inline void +sched_class_dequeue(struct proc_struct *proc) { + sched_class->dequeue(rq, proc); +} + +static inline struct proc_struct * +sched_class_pick_next(void) { + return sched_class->pick_next(rq); +} + +static void +sched_class_proc_tick(struct proc_struct *proc) { + if (proc != idleproc) { + sched_class->proc_tick(rq, proc); + } + else { + proc->need_resched = 1; + } +} + +static struct run_queue __rq; + +void +sched_init(void) { + list_init(&timer_list); + + sched_class = &default_sched_class; + + rq = &__rq; + rq->max_time_slice = MAX_TIME_SLICE; + sched_class->init(rq); + + cprintf("sched class: %s\n", sched_class->name); +} + +void +wakeup_proc(struct proc_struct *proc) { + assert(proc->state != PROC_ZOMBIE); + bool intr_flag; + local_intr_save(intr_flag); + { + if (proc->state != PROC_RUNNABLE) { + proc->state = PROC_RUNNABLE; + proc->wait_state = 0; + if (proc != current) { + sched_class_enqueue(proc); + } + } + else { + warn("wakeup runnable process.\n"); + } + } + local_intr_restore(intr_flag); +} + +void +schedule(void) { + bool intr_flag; + struct proc_struct *next; + local_intr_save(intr_flag); + { + current->need_resched = 0; + if (current->state == PROC_RUNNABLE) { + sched_class_enqueue(current); + } + if ((next = sched_class_pick_next()) != NULL) { + sched_class_dequeue(next); + } + if (next == NULL) { + next = idleproc; + } + next->runs ++; + if (next != current) { + proc_run(next); + } + } + local_intr_restore(intr_flag); +} + +// add timer to timer_list +void +add_timer(timer_t *timer) { + bool intr_flag; + local_intr_save(intr_flag); + { + assert(timer->expires > 0 && timer->proc != NULL); + assert(list_empty(&(timer->timer_link))); + list_entry_t *le = list_next(&timer_list); + while (le != &timer_list) { + timer_t *next = le2timer(le, timer_link); + if (timer->expires < next->expires) { + next->expires -= timer->expires; + break; + } + timer->expires -= next->expires; + le = list_next(le); + } + list_add_before(le, &(timer->timer_link)); + } + local_intr_restore(intr_flag); +} + +// del timer from timer_list +void +del_timer(timer_t *timer) { + bool intr_flag; + local_intr_save(intr_flag); + { + if (!list_empty(&(timer->timer_link))) { + if (timer->expires != 0) { + list_entry_t *le = list_next(&(timer->timer_link)); + if (le != &timer_list) { + timer_t *next = le2timer(le, timer_link); + next->expires += timer->expires; + } + } + list_del_init(&(timer->timer_link)); + } + } + local_intr_restore(intr_flag); +} + +// call scheduler to update tick related info, and check the timer is expired? If expired, then wakup proc +void +run_timer_list(void) { + bool intr_flag; + local_intr_save(intr_flag); + { + list_entry_t *le = list_next(&timer_list); + if (le != &timer_list) { + timer_t *timer = le2timer(le, timer_link); + assert(timer->expires != 0); + timer->expires --; + while (timer->expires == 0) { + le = list_next(le); + struct proc_struct *proc = timer->proc; + if (proc->wait_state != 0) { + assert(proc->wait_state & WT_INTERRUPTED); + } + else { + warn("process %d's wait_state == 0.\n", proc->pid); + } + wakeup_proc(proc); + del_timer(timer); + if (le == &timer_list) { + break; + } + timer = le2timer(le, timer_link); + } + } + if(current)sched_class_proc_tick(current); + } + local_intr_restore(intr_flag); +} diff --git a/lab8/kern/schedule/sched.h b/lab8/kern/schedule/sched.h new file mode 100644 index 0000000..766ac03 --- /dev/null +++ b/lab8/kern/schedule/sched.h @@ -0,0 +1,73 @@ +#ifndef __KERN_SCHEDULE_SCHED_H__ +#define __KERN_SCHEDULE_SCHED_H__ + +#include +#include +#include + +#define MAX_TIME_SLICE 5 + +struct proc_struct; + +typedef struct { + unsigned int expires; //the expire time + struct proc_struct *proc; //the proc wait in this timer. If the expire time is end, then this proc will be scheduled + list_entry_t timer_link; //the timer list +} timer_t; + +#define le2timer(le, member) \ +to_struct((le), timer_t, member) + +// init a timer +static inline timer_t * +timer_init(timer_t *timer, struct proc_struct *proc, int expires) { + timer->expires = expires; + timer->proc = proc; + list_init(&(timer->timer_link)); + return timer; +} + +struct run_queue; + +// The introduction of scheduling classes is borrrowed from Linux, and makes the +// core scheduler quite extensible. These classes (the scheduler modules) encapsulate +// the scheduling policies. +struct sched_class { + // the name of sched_class + const char *name; + // Init the run queue + void (*init)(struct run_queue *rq); + // put the proc into runqueue, and this function must be called with rq_lock + void (*enqueue)(struct run_queue *rq, struct proc_struct *proc); + // get the proc out runqueue, and this function must be called with rq_lock + void (*dequeue)(struct run_queue *rq, struct proc_struct *proc); + // choose the next runnable task + struct proc_struct *(*pick_next)(struct run_queue *rq); + // dealer of the time-tick + void (*proc_tick)(struct run_queue *rq, struct proc_struct *proc); + /* for SMP support in the future + * load_balance + * void (*load_balance)(struct rq* rq); + * get some proc from this rq, used in load_balance, + * return value is the num of gotten proc + * int (*get_proc)(struct rq* rq, struct proc* procs_moved[]); + */ +}; + +struct run_queue { + list_entry_t run_list; + unsigned int proc_num; + int max_time_slice; + // For LAB6 ONLY + skew_heap_entry_t *lab6_run_pool; +}; + +void sched_init(void); +void wakeup_proc(struct proc_struct *proc); +void schedule(void); +void add_timer(timer_t *timer); // add timer to timer_list +void del_timer(timer_t *timer); // del timer from timer_list +void run_timer_list(void); // call scheduler to update tick related info, and check the timer is expired? If expired, then wakup proc + +#endif /* !__KERN_SCHEDULE_SCHED_H__ */ + diff --git a/lab8/kern/sync/check_sync.c b/lab8/kern/sync/check_sync.c new file mode 100644 index 0000000..99f3c65 --- /dev/null +++ b/lab8/kern/sync/check_sync.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include + +#define N 5 /* 哲学家数目 */ +#define LEFT (i-1+N)%N /* i的左邻号码 */ +#define RIGHT (i+1)%N /* i的右邻号码 */ +#define THINKING 0 /* 哲学家正在思考 */ +#define HUNGRY 1 /* 哲学家想取得叉子 */ +#define EATING 2 /* 哲学家正在吃面 */ +#define TIMES 4 /* 吃4次饭 */ +#define SLEEP_TIME 10 + +//-----------------philosopher problem using monitor ------------ +/*PSEUDO CODE :philosopher problem using semaphore +system DINING_PHILOSOPHERS + +VAR +me: semaphore, initially 1; # for mutual exclusion +s[5]: semaphore s[5], initially 0; # for synchronization +pflag[5]: {THINK, HUNGRY, EAT}, initially THINK; # philosopher flag + +# As before, each philosopher is an endless cycle of thinking and eating. + +procedure philosopher(i) + { + while TRUE do + { + THINKING; + take_chopsticks(i); + EATING; + drop_chopsticks(i); + } + } + +# The take_chopsticks procedure involves checking the status of neighboring +# philosophers and then declaring one's own intention to eat. This is a two-phase +# protocol; first declaring the status HUNGRY, then going on to EAT. + +procedure take_chopsticks(i) + { + DOWN(me); # critical section + pflag[i] := HUNGRY; + test[i]; + UP(me); # end critical section + DOWN(s[i]) # Eat if enabled + } + +void test(i) # Let phil[i] eat, if waiting + { + if ( pflag[i] == HUNGRY + && pflag[i-1] != EAT + && pflag[i+1] != EAT) + then + { + pflag[i] := EAT; + UP(s[i]) + } + } + + +# Once a philosopher finishes eating, all that remains is to relinquish the +# resources---its two chopsticks---and thereby release waiting neighbors. + +void drop_chopsticks(int i) + { + DOWN(me); # critical section + test(i-1); # Let phil. on left eat if possible + test(i+1); # Let phil. on rght eat if possible + UP(me); # up critical section + } + +*/ +//---------- philosophers problem using semaphore ---------------------- +int state_sema[N]; /* 记录每个人状态的数组 */ +/* 信号量是一个特殊的整型变量 */ +semaphore_t mutex; /* 临界区互斥 */ +semaphore_t s[N]; /* 每个哲学家一个信号量 */ + +struct proc_struct *philosopher_proc_sema[N]; + +void phi_test_sema(int i) /* i:哲学家号码从0到N-1 */ +{ + if(state_sema[i]==HUNGRY&&state_sema[LEFT]!=EATING + &&state_sema[RIGHT]!=EATING) + { + state_sema[i]=EATING; + up(&s[i]); + } +} + +void phi_take_forks_sema(int i) /* i:哲学家号码从0到N-1 */ +{ + down(&mutex); /* 进入临界区 */ + state_sema[i]=HUNGRY; /* 记录下哲学家i饥饿的事实 */ + phi_test_sema(i); /* 试图得到两只叉子 */ + up(&mutex); /* 离开临界区 */ + down(&s[i]); /* 如果得不到叉子就阻塞 */ +} + +void phi_put_forks_sema(int i) /* i:哲学家号码从0到N-1 */ +{ + down(&mutex); /* 进入临界区 */ + state_sema[i]=THINKING; /* 哲学家进餐结束 */ + phi_test_sema(LEFT); /* 看一下左邻居现在是否能进餐 */ + phi_test_sema(RIGHT); /* 看一下右邻居现在是否能进餐 */ + up(&mutex); /* 离开临界区 */ +} + +int philosopher_using_semaphore(void * arg) /* i:哲学家号码,从0到N-1 */ +{ + int i, iter=0; + i=(int)arg; + cprintf("I am No.%d philosopher_sema\n",i); + while(iter++cv[i]) ; + } +} + + +void phi_take_forks_condvar(int i) { + down(&(mtp->mutex)); +//--------into routine in monitor-------------- + // LAB7 EXERCISE1: YOUR CODE + // I am hungry + // try to get fork + state_condvar[i] = HUNGRY; + phi_test_condvar(i); + if (state_condvar[i] == HUNGRY) { + cond_wait(&mtp->cv[i]); + } +//--------leave routine in monitor-------------- + if(mtp->next_count>0) + up(&(mtp->next)); + else + up(&(mtp->mutex)); +} + +void phi_put_forks_condvar(int i) { + down(&(mtp->mutex)); + +//--------into routine in monitor-------------- + // LAB7 EXERCISE1: YOUR CODE + // I ate over + // test left and right neighbors + state_condvar[i] = THINKING; + phi_test_condvar(LEFT); + phi_test_condvar(RIGHT); +//--------leave routine in monitor-------------- + if(mtp->next_count>0) + up(&(mtp->next)); + else + up(&(mtp->mutex)); +} + +//---------- philosophers using monitor (condition variable) ---------------------- +int philosopher_using_condvar(void * arg) { /* arg is the No. of philosopher 0~N-1*/ + + int i, iter=0; + i=(int)arg; + cprintf("I am No.%d philosopher_condvar\n",i); + while(iter++ +#include +#include +#include + + +// Initialize monitor. +void +monitor_init (monitor_t * mtp, size_t num_cv) { + int i; + assert(num_cv>0); + mtp->next_count = 0; + mtp->cv = NULL; + sem_init(&(mtp->mutex), 1); //unlocked + sem_init(&(mtp->next), 0); + mtp->cv =(condvar_t *) kmalloc(sizeof(condvar_t)*num_cv); + assert(mtp->cv!=NULL); + for(i=0; icv[i].count=0; + sem_init(&(mtp->cv[i].sem),0); + mtp->cv[i].owner=mtp; + } +} + +// Free monitor. +void +monitor_free (monitor_t * mtp, size_t num_cv) { + kfree(mtp->cv); +} + +// Unlock one of threads waiting on the condition variable. +void +cond_signal (condvar_t *cvp) { + //LAB7 EXERCISE1: YOUR CODE + cprintf("cond_signal begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); + /* + * cond_signal(cv) { + * if(cv.count>0) { + * mt.next_count ++; + * signal(cv.sem); + * wait(mt.next); + * mt.next_count--; + * } + * } + */ + if (cvp->count > 0) { + monitor_t* const mtp = cvp->owner; + mtp->next_count++; + up(&(cvp->sem)); + down(&(mtp->next)); + mtp->next_count--; + } + cprintf("cond_signal end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); +} + +// Suspend calling thread on a condition variable waiting for condition Atomically unlocks +// mutex and suspends calling thread on conditional variable after waking up locks mutex. Notice: mp is mutex semaphore for monitor's procedures +void +cond_wait (condvar_t *cvp) { + //LAB7 EXERCISE1: YOUR CODE + cprintf("cond_wait begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); + /* + * cv.count ++; + * if(mt.next_count>0) + * signal(mt.next) + * else + * signal(mt.mutex); + * wait(cv.sem); + * cv.count --; + */ + cvp->count++; + monitor_t* const mtp = cvp->owner; + if (mtp->next_count > 0) { + up(&(mtp->next)); + } else { + up(&(mtp->mutex)); + } + down(&(cvp->sem)); + cvp->count--; + cprintf("cond_wait end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); +} diff --git a/lab8/kern/sync/monitor.h b/lab8/kern/sync/monitor.h new file mode 100644 index 0000000..bcc106d --- /dev/null +++ b/lab8/kern/sync/monitor.h @@ -0,0 +1,92 @@ +#ifndef __KERN_SYNC_MONITOR_CONDVAR_H__ +#define __KERN_SYNC_MOINTOR_CONDVAR_H__ + +#include +/* In [OS CONCEPT] 7.7 section, the accurate define and approximate implementation of MONITOR was introduced. + * INTRODUCTION: + * Monitors were invented by C. A. R. Hoare and Per Brinch Hansen, and were first implemented in Brinch Hansen's + * Concurrent Pascal language. Generally, a monitor is a language construct and the compiler usually enforces mutual exclusion. Compare this with semaphores, which are usually an OS construct. + * DEFNIE & CHARACTERISTIC: + * A monitor is a collection of procedures, variables, and data structures grouped together. + * Processes can call the monitor procedures but cannot access the internal data structures. + * Only one process at a time may be be active in a monitor. + * Condition variables allow for blocking and unblocking. + * cv.wait() blocks a process. + * The process is said to be waiting for (or waiting on) the condition variable cv. + * cv.signal() (also called cv.notify) unblocks a process waiting for the condition variable cv. + * When this occurs, we need to still require that only one process is active in the monitor. This can be done in several ways: + * on some systems the old process (the one executing the signal) leaves the monitor and the new one enters + * on some systems the signal must be the last statement executed inside the monitor. + * on some systems the old process will block until the monitor is available again. + * on some systems the new process (the one unblocked by the signal) will remain blocked until the monitor is available again. + * If a condition variable is signaled with nobody waiting, the signal is lost. Compare this with semaphores, in which a signal will allow a process that executes a wait in the future to no block. + * You should not think of a condition variable as a variable in the traditional sense. + * It does not have a value. + * Think of it as an object in the OOP sense. + * It has two methods, wait and signal that manipulate the calling process. + * IMPLEMENTATION: + * monitor mt { + * ----------------variable------------------ + * semaphore mutex; + * semaphore next; + * int next_count; + * condvar {int count, sempahore sem} cv[N]; + * other variables in mt; + * --------condvar wait/signal--------------- + * cond_wait (cv) { + * cv.count ++; + * if(mt.next_count>0) + * signal(mt.next) + * else + * signal(mt.mutex); + * wait(cv.sem); + * cv.count --; + * } + * + * cond_signal(cv) { + * if(cv.count>0) { + * mt.next_count ++; + * signal(cv.sem); + * wait(mt.next); + * mt.next_count--; + * } + * } + * --------routines in monitor--------------- + * routineA_in_mt () { + * wait(mt.mutex); + * ... + * real body of routineA + * ... + * if(next_count>0) + * signal(mt.next); + * else + * signal(mt.mutex); + * } + */ + +typedef struct monitor monitor_t; + +typedef struct condvar{ + semaphore_t sem; // the sem semaphore is used to down the waiting proc, and the signaling proc should up the waiting proc + int count; // the number of waiters on condvar + monitor_t * owner; // the owner(monitor) of this condvar +} condvar_t; + +typedef struct monitor{ + semaphore_t mutex; // the mutex lock for going into the routines in monitor, should be initialized to 1 + semaphore_t next; // the next semaphore is used to down the signaling proc itself, and the other OR wakeuped waiting proc should wake up the sleeped signaling proc. + int next_count; // the number of of sleeped signaling proc + condvar_t *cv; // the condvars in monitor +} monitor_t; + +// Initialize variables in monitor. +void monitor_init (monitor_t *cvp, size_t num_cv); +// Free variables in monitor. +void monitor_free (monitor_t *cvp, size_t num_cv); +// Unlock one of threads waiting on the condition variable. +void cond_signal (condvar_t *cvp); +// Suspend calling thread on a condition variable waiting for condition atomically unlock mutex in monitor, +// and suspends calling thread on conditional variable after waking up locks mutex. +void cond_wait (condvar_t *cvp); + +#endif /* !__KERN_SYNC_MONITOR_CONDVAR_H__ */ diff --git a/lab8/kern/sync/sem.c b/lab8/kern/sync/sem.c new file mode 100644 index 0000000..62c81db --- /dev/null +++ b/lab8/kern/sync/sem.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void +sem_init(semaphore_t *sem, int value) { + sem->value = value; + wait_queue_init(&(sem->wait_queue)); +} + +static __noinline void __up(semaphore_t *sem, uint32_t wait_state) { + bool intr_flag; + local_intr_save(intr_flag); + { + wait_t *wait; + if ((wait = wait_queue_first(&(sem->wait_queue))) == NULL) { + sem->value ++; + } + else { + assert(wait->proc->wait_state == wait_state); + wakeup_wait(&(sem->wait_queue), wait, wait_state, 1); + } + } + local_intr_restore(intr_flag); +} + +static __noinline uint32_t __down(semaphore_t *sem, uint32_t wait_state) { + bool intr_flag; + local_intr_save(intr_flag); + if (sem->value > 0) { + sem->value --; + local_intr_restore(intr_flag); + return 0; + } + wait_t __wait, *wait = &__wait; + wait_current_set(&(sem->wait_queue), wait, wait_state); + local_intr_restore(intr_flag); + + schedule(); + + local_intr_save(intr_flag); + wait_current_del(&(sem->wait_queue), wait); + local_intr_restore(intr_flag); + + if (wait->wakeup_flags != wait_state) { + return wait->wakeup_flags; + } + return 0; +} + +void +up(semaphore_t *sem) { + __up(sem, WT_KSEM); +} + +void +down(semaphore_t *sem) { + uint32_t flags = __down(sem, WT_KSEM); + assert(flags == 0); +} + +bool +try_down(semaphore_t *sem) { + bool intr_flag, ret = 0; + local_intr_save(intr_flag); + if (sem->value > 0) { + sem->value --, ret = 1; + } + local_intr_restore(intr_flag); + return ret; +} + diff --git a/lab8/kern/sync/sem.h b/lab8/kern/sync/sem.h new file mode 100644 index 0000000..4450fe7 --- /dev/null +++ b/lab8/kern/sync/sem.h @@ -0,0 +1,19 @@ +#ifndef __KERN_SYNC_SEM_H__ +#define __KERN_SYNC_SEM_H__ + +#include +#include +#include + +typedef struct { + int value; + wait_queue_t wait_queue; +} semaphore_t; + +void sem_init(semaphore_t *sem, int value); +void up(semaphore_t *sem); +void down(semaphore_t *sem); +bool try_down(semaphore_t *sem); + +#endif /* !__KERN_SYNC_SEM_H__ */ + diff --git a/lab8/kern/sync/sync.h b/lab8/kern/sync/sync.h new file mode 100644 index 0000000..e30ad31 --- /dev/null +++ b/lab8/kern/sync/sync.h @@ -0,0 +1,29 @@ +#ifndef __KERN_SYNC_SYNC_H__ +#define __KERN_SYNC_SYNC_H__ + +#include +#include +#include +#include +#include +#include + +static inline bool __intr_save(void) { + if (read_csr(sstatus) & SSTATUS_SIE) { + intr_disable(); + return 1; + } + return 0; +} + +static inline void __intr_restore(bool flag) { + if (flag) { + intr_enable(); + } +} + +#define local_intr_save(x) do { x = __intr_save(); } while (0) +#define local_intr_restore(x) __intr_restore(x); + +#endif /* !__KERN_SYNC_SYNC_H__ */ + diff --git a/lab8/kern/sync/wait.c b/lab8/kern/sync/wait.c new file mode 100644 index 0000000..6aea172 --- /dev/null +++ b/lab8/kern/sync/wait.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +void +wait_init(wait_t *wait, struct proc_struct *proc) { + wait->proc = proc; + wait->wakeup_flags = WT_INTERRUPTED; + list_init(&(wait->wait_link)); +} + +void +wait_queue_init(wait_queue_t *queue) { + list_init(&(queue->wait_head)); +} + +void +wait_queue_add(wait_queue_t *queue, wait_t *wait) { + assert(list_empty(&(wait->wait_link)) && wait->proc != NULL); + wait->wait_queue = queue; + list_add_before(&(queue->wait_head), &(wait->wait_link)); +} + +void +wait_queue_del(wait_queue_t *queue, wait_t *wait) { + assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue); + list_del_init(&(wait->wait_link)); +} + +wait_t * +wait_queue_next(wait_queue_t *queue, wait_t *wait) { + assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue); + list_entry_t *le = list_next(&(wait->wait_link)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +wait_t * +wait_queue_prev(wait_queue_t *queue, wait_t *wait) { + assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue); + list_entry_t *le = list_prev(&(wait->wait_link)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +wait_t * +wait_queue_first(wait_queue_t *queue) { + list_entry_t *le = list_next(&(queue->wait_head)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +wait_t * +wait_queue_last(wait_queue_t *queue) { + list_entry_t *le = list_prev(&(queue->wait_head)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +bool +wait_queue_empty(wait_queue_t *queue) { + return list_empty(&(queue->wait_head)); +} + +bool +wait_in_queue(wait_t *wait) { + return !list_empty(&(wait->wait_link)); +} + +void +wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del) { + if (del) { + wait_queue_del(queue, wait); + } + wait->wakeup_flags = wakeup_flags; + wakeup_proc(wait->proc); +} + +void +wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del) { + wait_t *wait; + if ((wait = wait_queue_first(queue)) != NULL) { + wakeup_wait(queue, wait, wakeup_flags, del); + } +} + +void +wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del) { + wait_t *wait; + if ((wait = wait_queue_first(queue)) != NULL) { + if (del) { + do { + wakeup_wait(queue, wait, wakeup_flags, 1); + } while ((wait = wait_queue_first(queue)) != NULL); + } + else { + do { + wakeup_wait(queue, wait, wakeup_flags, 0); + } while ((wait = wait_queue_next(queue, wait)) != NULL); + } + } +} + +void +wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state) { + assert(current != NULL); + wait_init(wait, current); + current->state = PROC_SLEEPING; + current->wait_state = wait_state; + wait_queue_add(queue, wait); +} + diff --git a/lab8/kern/sync/wait.h b/lab8/kern/sync/wait.h new file mode 100644 index 0000000..46758b7 --- /dev/null +++ b/lab8/kern/sync/wait.h @@ -0,0 +1,48 @@ +#ifndef __KERN_SYNC_WAIT_H__ +#define __KERN_SYNC_WAIT_H__ + +#include + +typedef struct { + list_entry_t wait_head; +} wait_queue_t; + +struct proc_struct; + +typedef struct { + struct proc_struct *proc; + uint32_t wakeup_flags; + wait_queue_t *wait_queue; + list_entry_t wait_link; +} wait_t; + +#define le2wait(le, member) \ + to_struct((le), wait_t, member) + +void wait_init(wait_t *wait, struct proc_struct *proc); +void wait_queue_init(wait_queue_t *queue); +void wait_queue_add(wait_queue_t *queue, wait_t *wait); +void wait_queue_del(wait_queue_t *queue, wait_t *wait); + +wait_t *wait_queue_next(wait_queue_t *queue, wait_t *wait); +wait_t *wait_queue_prev(wait_queue_t *queue, wait_t *wait); +wait_t *wait_queue_first(wait_queue_t *queue); +wait_t *wait_queue_last(wait_queue_t *queue); + +bool wait_queue_empty(wait_queue_t *queue); +bool wait_in_queue(wait_t *wait); +void wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del); +void wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del); +void wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del); + +void wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state); + +#define wait_current_del(queue, wait) \ + do { \ + if (wait_in_queue(wait)) { \ + wait_queue_del(queue, wait); \ + } \ + } while (0) + +#endif /* !__KERN_SYNC_WAIT_H__ */ + diff --git a/lab8/kern/syscall/syscall.c b/lab8/kern/syscall/syscall.c new file mode 100644 index 0000000..4079fcd --- /dev/null +++ b/lab8/kern/syscall/syscall.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +static int +sys_exit(uint64_t arg[]) { + int error_code = (int)arg[0]; + return do_exit(error_code); +} + +static int +sys_fork(uint64_t arg[]) { + struct trapframe *tf = current->tf; + uintptr_t stack = tf->gpr.sp; + return do_fork(0, stack, tf); +} + +static int +sys_wait(uint64_t arg[]) { + int pid = (int)arg[0]; + int *store = (int *)arg[1]; + return do_wait(pid, store); +} +static int +sys_exec(uint64_t arg[]) { + const char *name = (const char *)(arg[0]); + int argc = (int)arg[1]; + const char **argv = (const char **)arg[2]; + return do_execve(name, argc, argv); +} + +static int +sys_yield(uint64_t arg[]) { + return do_yield(); +} + +static int +sys_kill(uint64_t arg[]) { + int pid = (int)arg[0]; + return do_kill(pid); +} + +static int +sys_getpid(uint64_t arg[]) { + return current->pid; +} + +static int +sys_putc(uint64_t arg[]) { + int c = (int)arg[0]; + cputchar(c); + return 0; +} + +static int +sys_pgdir(uint64_t arg[]) { + //print_pgdir(); + return 0; +} +static int sys_gettime(uint64_t arg[]){ + return (int)ticks*10; +} +static int sys_lab6_set_priority(uint64_t arg[]){ + uint64_t priority = (uint64_t)arg[0]; + lab6_set_priority(priority); + return 0; +} +static int +sys_sleep(uint64_t arg[]) { + unsigned int time = (unsigned int)arg[0]; + return do_sleep(time); +} +static int +sys_open(uint64_t arg[]) { + const char *path = (const char *)arg[0]; + uint32_t open_flags = (uint32_t)arg[1]; + return sysfile_open(path, open_flags); +} + +static int +sys_close(uint64_t arg[]) { + int fd = (int)arg[0]; + return sysfile_close(fd); +} + +static int +sys_read(uint64_t arg[]) { + int fd = (int)arg[0]; + void *base = (void *)arg[1]; + size_t len = (size_t)arg[2]; + return sysfile_read(fd, base, len); +} + +static int +sys_write(uint64_t arg[]) { + int fd = (int)arg[0]; + void *base = (void *)arg[1]; + size_t len = (size_t)arg[2]; + return sysfile_write(fd, base, len); +} + +static int +sys_seek(uint64_t arg[]) { + int fd = (int)arg[0]; + off_t pos = (off_t)arg[1]; + int whence = (int)arg[2]; + return sysfile_seek(fd, pos, whence); +} + +static int +sys_fstat(uint64_t arg[]) { + int fd = (int)arg[0]; + struct stat *stat = (struct stat *)arg[1]; + return sysfile_fstat(fd, stat); +} + +static int +sys_fsync(uint64_t arg[]) { + int fd = (int)arg[0]; + return sysfile_fsync(fd); +} + +static int +sys_getcwd(uint64_t arg[]) { + char *buf = (char *)arg[0]; + size_t len = (size_t)arg[1]; + return sysfile_getcwd(buf, len); +} + +static int +sys_getdirentry(uint64_t arg[]) { + int fd = (int)arg[0]; + struct dirent *direntp = (struct dirent *)arg[1]; + return sysfile_getdirentry(fd, direntp); +} + +static int +sys_dup(uint64_t arg[]) { + int fd1 = (int)arg[0]; + int fd2 = (int)arg[1]; + return sysfile_dup(fd1, fd2); +} +static int (*syscalls[])(uint64_t arg[]) = { + [SYS_exit] sys_exit, + [SYS_fork] sys_fork, + [SYS_wait] sys_wait, + [SYS_exec] sys_exec, + [SYS_yield] sys_yield, + [SYS_kill] sys_kill, + [SYS_getpid] sys_getpid, + [SYS_putc] sys_putc, + [SYS_pgdir] sys_pgdir, + [SYS_gettime] sys_gettime, + [SYS_lab6_set_priority] sys_lab6_set_priority, + [SYS_sleep] sys_sleep, + [SYS_open] sys_open, + [SYS_close] sys_close, + [SYS_read] sys_read, + [SYS_write] sys_write, + [SYS_seek] sys_seek, + [SYS_fstat] sys_fstat, + [SYS_fsync] sys_fsync, + [SYS_getcwd] sys_getcwd, + [SYS_getdirentry] sys_getdirentry, + [SYS_dup] sys_dup, +}; + +#define NUM_SYSCALLS ((sizeof(syscalls)) / (sizeof(syscalls[0]))) + +void +syscall(void) { + struct trapframe *tf = current->tf; + uint64_t arg[5]; + int num = tf->gpr.a0; + if (num >= 0 && num < NUM_SYSCALLS) { + if (syscalls[num] != NULL) { + arg[0] = tf->gpr.a1; + arg[1] = tf->gpr.a2; + arg[2] = tf->gpr.a3; + arg[3] = tf->gpr.a4; + arg[4] = tf->gpr.a5; + tf->gpr.a0 = syscalls[num](arg); + return ; + } + } + print_trapframe(tf); + panic("undefined syscall %d, pid = %d, name = %s.\n", + num, current->pid, current->name); +} + diff --git a/lab8/kern/syscall/syscall.h b/lab8/kern/syscall/syscall.h new file mode 100644 index 0000000..a8fe843 --- /dev/null +++ b/lab8/kern/syscall/syscall.h @@ -0,0 +1,7 @@ +#ifndef __KERN_SYSCALL_SYSCALL_H__ +#define __KERN_SYSCALL_SYSCALL_H__ + +void syscall(void); + +#endif /* !__KERN_SYSCALL_SYSCALL_H__ */ + diff --git a/lab8/kern/trap/trap.c b/lab8/kern/trap/trap.c new file mode 100644 index 0000000..e06feaa --- /dev/null +++ b/lab8/kern/trap/trap.c @@ -0,0 +1,295 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TICK_NUM 2 + +static void print_ticks() { + cprintf("%d ticks\n",TICK_NUM); +#ifdef DEBUG_GRADE + cprintf("End of Test.\n"); + panic("EOT: kernel seems ok."); +#endif +} + +/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */ +void +idt_init(void) { + extern void __alltraps(void); + /* Set sscratch register to 0, indicating to exception vector that we are + * presently executing in the kernel */ + write_csr(sscratch, 0); + /* Set the exception vector address */ + write_csr(stvec, &__alltraps); +} + +/* trap_in_kernel - test if trap happened in kernel */ +bool trap_in_kernel(struct trapframe *tf) { + return (tf->status & SSTATUS_SPP) != 0; +} + +void +print_trapframe(struct trapframe *tf) { + cprintf("trapframe at %p\n", tf); + print_regs(&tf->gpr); + cprintf(" status 0x%08x\n", tf->status); + cprintf(" epc 0x%08x\n", tf->epc); + cprintf(" tval 0x%08x\n", tf->tval); + cprintf(" cause 0x%08x\n", tf->cause); +} + +void print_regs(struct pushregs* gpr) { + cprintf(" zero 0x%08x\n", gpr->zero); + cprintf(" ra 0x%08x\n", gpr->ra); + cprintf(" sp 0x%08x\n", gpr->sp); + cprintf(" gp 0x%08x\n", gpr->gp); + cprintf(" tp 0x%08x\n", gpr->tp); + cprintf(" t0 0x%08x\n", gpr->t0); + cprintf(" t1 0x%08x\n", gpr->t1); + cprintf(" t2 0x%08x\n", gpr->t2); + cprintf(" s0 0x%08x\n", gpr->s0); + cprintf(" s1 0x%08x\n", gpr->s1); + cprintf(" a0 0x%08x\n", gpr->a0); + cprintf(" a1 0x%08x\n", gpr->a1); + cprintf(" a2 0x%08x\n", gpr->a2); + cprintf(" a3 0x%08x\n", gpr->a3); + cprintf(" a4 0x%08x\n", gpr->a4); + cprintf(" a5 0x%08x\n", gpr->a5); + cprintf(" a6 0x%08x\n", gpr->a6); + cprintf(" a7 0x%08x\n", gpr->a7); + cprintf(" s2 0x%08x\n", gpr->s2); + cprintf(" s3 0x%08x\n", gpr->s3); + cprintf(" s4 0x%08x\n", gpr->s4); + cprintf(" s5 0x%08x\n", gpr->s5); + cprintf(" s6 0x%08x\n", gpr->s6); + cprintf(" s7 0x%08x\n", gpr->s7); + cprintf(" s8 0x%08x\n", gpr->s8); + cprintf(" s9 0x%08x\n", gpr->s9); + cprintf(" s10 0x%08x\n", gpr->s10); + cprintf(" s11 0x%08x\n", gpr->s11); + cprintf(" t3 0x%08x\n", gpr->t3); + cprintf(" t4 0x%08x\n", gpr->t4); + cprintf(" t5 0x%08x\n", gpr->t5); + cprintf(" t6 0x%08x\n", gpr->t6); +} + +static inline void print_pgfault(struct trapframe *tf) { + cprintf("page falut at 0x%08x: %c/%c\n", tf->tval, + trap_in_kernel(tf) ? 'K' : 'U', + tf->cause == CAUSE_STORE_PAGE_FAULT ? 'W' : 'R'); +} + +static int +pgfault_handler(struct trapframe *tf) { + extern struct mm_struct *check_mm_struct; + if(check_mm_struct !=NULL) { //used for test check_swap + print_pgfault(tf); + } + struct mm_struct *mm; + if (check_mm_struct != NULL) { + assert(current == idleproc); + mm = check_mm_struct; + } + else { + if (current == NULL) { + print_trapframe(tf); + print_pgfault(tf); + panic("unhandled page fault.\n"); + } + mm = current->mm; + } + return do_pgfault(mm, tf->cause, tf->tval); +} + +static volatile int in_swap_tick_event = 0; +extern struct mm_struct *check_mm_struct; + +void interrupt_handler(struct trapframe *tf) { + intptr_t cause = (tf->cause << 1) >> 1; + switch (cause) { + case IRQ_U_SOFT: + cprintf("User software interrupt\n"); + break; + case IRQ_S_SOFT: + cprintf("Supervisor software interrupt\n"); + break; + case IRQ_H_SOFT: + cprintf("Hypervisor software interrupt\n"); + break; + case IRQ_M_SOFT: + cprintf("Machine software interrupt\n"); + break; + case IRQ_U_TIMER: + cprintf("User software interrupt\n"); + break; + case IRQ_S_TIMER: + // "All bits besides SSIP and USIP in the sip register are + // read-only." -- privileged spec1.9.1, 4.1.4, p59 + // In fact, Call sbi_set_timer will clear STIP, or you can clear it + // directly. + // clear_csr(sip, SIP_STIP); + clock_set_next_event(); + ++ticks; + run_timer_list(); + input_wakeup(); + break; + case IRQ_H_TIMER: + cprintf("Hypervisor software interrupt\n"); + break; + case IRQ_M_TIMER: + cprintf("Machine software interrupt\n"); + break; + case IRQ_U_EXT: + cprintf("User software interrupt\n"); + break; + case IRQ_S_EXT: + cprintf("Supervisor external interrupt\n"); + break; + case IRQ_H_EXT: + cprintf("Hypervisor software interrupt\n"); + break; + case IRQ_M_EXT: + cprintf("Machine software interrupt\n"); + break; + default: + print_trapframe(tf); + break; + } +} +void kernel_execve_ret(struct trapframe *tf,uintptr_t kstacktop); +void exception_handler(struct trapframe *tf) { + int ret; + switch (tf->cause) { + case CAUSE_MISALIGNED_FETCH: + cprintf("Instruction address misaligned\n"); + break; + case CAUSE_FETCH_ACCESS: + cprintf("Instruction access fault\n"); + break; + case CAUSE_ILLEGAL_INSTRUCTION: + cprintf("Illegal instruction\n"); + break; + case CAUSE_BREAKPOINT: + cprintf("Breakpoint\n"); + if(tf->gpr.a7 == 10){ + tf->epc += 4; + syscall(); + kernel_execve_ret(tf,current->kstack+KSTACKSIZE); + } + break; + case CAUSE_MISALIGNED_LOAD: + cprintf("Load address misaligned\n"); + break; + case CAUSE_LOAD_ACCESS: + cprintf("Load access fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + case CAUSE_MISALIGNED_STORE: + panic("AMO address misaligned\n"); + break; + case CAUSE_STORE_ACCESS: + cprintf("Store/AMO access fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + case CAUSE_USER_ECALL: + //cprintf("Environment call from U-mode\n"); + tf->epc += 4; + syscall(); + break; + case CAUSE_SUPERVISOR_ECALL: + cprintf("Environment call from S-mode\n"); + tf->epc += 4; + syscall(); + break; + case CAUSE_HYPERVISOR_ECALL: + cprintf("Environment call from H-mode\n"); + break; + case CAUSE_MACHINE_ECALL: + cprintf("Environment call from M-mode\n"); + break; + case CAUSE_FETCH_PAGE_FAULT: + cprintf("Instruction page fault\n"); + break; + case CAUSE_LOAD_PAGE_FAULT: + cprintf("Load page fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + case CAUSE_STORE_PAGE_FAULT: + cprintf("Store/AMO page fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + default: + print_trapframe(tf); + break; + } +} + +static inline void trap_dispatch(struct trapframe* tf) { + if ((intptr_t)tf->cause < 0) { + // interrupts + interrupt_handler(tf); + } else { + // exceptions + exception_handler(tf); + } +} + +/* * + * trap - handles or dispatches an exception/interrupt. if and when trap() returns, + * the code in kern/trap/trapentry.S restores the old CPU state saved in the + * trapframe and then uses the iret instruction to return from the exception. + * */ +void +trap(struct trapframe *tf) { + // dispatch based on what type of trap occurred + if (current == NULL) { + trap_dispatch(tf); + } else { + struct trapframe *otf = current->tf; + current->tf = tf; + + bool in_kernel = trap_in_kernel(tf); + + trap_dispatch(tf); + + current->tf = otf; + if (!in_kernel) { + if (current->flags & PF_EXITING) { + do_exit(-E_KILLED); + } + if (current->need_resched) { + schedule(); + } + } + } +} + + diff --git a/lab8/kern/trap/trap.h b/lab8/kern/trap/trap.h new file mode 100644 index 0000000..40f4816 --- /dev/null +++ b/lab8/kern/trap/trap.h @@ -0,0 +1,56 @@ +#ifndef __KERN_TRAP_TRAP_H__ +#define __KERN_TRAP_TRAP_H__ + +#include + +struct pushregs { + uintptr_t zero; // Hard-wired zero + uintptr_t ra; // Return address + uintptr_t sp; // Stack pointer + uintptr_t gp; // Global pointer + uintptr_t tp; // Thread pointer + uintptr_t t0; // Temporary + uintptr_t t1; // Temporary + uintptr_t t2; // Temporary + uintptr_t s0; // Saved register/frame pointer + uintptr_t s1; // Saved register + uintptr_t a0; // Function argument/return value + uintptr_t a1; // Function argument/return value + uintptr_t a2; // Function argument + uintptr_t a3; // Function argument + uintptr_t a4; // Function argument + uintptr_t a5; // Function argument + uintptr_t a6; // Function argument + uintptr_t a7; // Function argument + uintptr_t s2; // Saved register + uintptr_t s3; // Saved register + uintptr_t s4; // Saved register + uintptr_t s5; // Saved register + uintptr_t s6; // Saved register + uintptr_t s7; // Saved register + uintptr_t s8; // Saved register + uintptr_t s9; // Saved register + uintptr_t s10; // Saved register + uintptr_t s11; // Saved register + uintptr_t t3; // Temporary + uintptr_t t4; // Temporary + uintptr_t t5; // Temporary + uintptr_t t6; // Temporary +}; + +struct trapframe { + struct pushregs gpr; + uintptr_t status; + uintptr_t epc; + uintptr_t tval; + uintptr_t cause; +}; + +void trap(struct trapframe *tf); +void idt_init(void); +void print_trapframe(struct trapframe *tf); +void print_regs(struct pushregs* gpr); +bool trap_in_kernel(struct trapframe *tf); + +#endif /* !__KERN_TRAP_TRAP_H__ */ + diff --git a/lab8/kern/trap/trapentry.S b/lab8/kern/trap/trapentry.S new file mode 100644 index 0000000..1c37a50 --- /dev/null +++ b/lab8/kern/trap/trapentry.S @@ -0,0 +1,222 @@ +#include + + .altmacro + .align 2 + .macro SAVE_ALL + LOCAL _restore_kernel_sp + LOCAL _save_context + + # If coming from userspace, preserve the user stack pointer and load + # the kernel stack pointer. If we came from the kernel, sscratch + # will contain 0, and we should continue on the current stack. + csrrw sp, sscratch, sp + bnez sp, _save_context + +_restore_kernel_sp: + csrr sp, sscratch +_save_context: + addi sp, sp, -36 * REGBYTES + # save x registers + STORE x0, 0*REGBYTES(sp) + STORE x1, 1*REGBYTES(sp) + STORE x3, 3*REGBYTES(sp) + STORE x4, 4*REGBYTES(sp) + STORE x5, 5*REGBYTES(sp) + STORE x6, 6*REGBYTES(sp) + STORE x7, 7*REGBYTES(sp) + STORE x8, 8*REGBYTES(sp) + STORE x9, 9*REGBYTES(sp) + STORE x10, 10*REGBYTES(sp) + STORE x11, 11*REGBYTES(sp) + STORE x12, 12*REGBYTES(sp) + STORE x13, 13*REGBYTES(sp) + STORE x14, 14*REGBYTES(sp) + STORE x15, 15*REGBYTES(sp) + STORE x16, 16*REGBYTES(sp) + STORE x17, 17*REGBYTES(sp) + STORE x18, 18*REGBYTES(sp) + STORE x19, 19*REGBYTES(sp) + STORE x20, 20*REGBYTES(sp) + STORE x21, 21*REGBYTES(sp) + STORE x22, 22*REGBYTES(sp) + STORE x23, 23*REGBYTES(sp) + STORE x24, 24*REGBYTES(sp) + STORE x25, 25*REGBYTES(sp) + STORE x26, 26*REGBYTES(sp) + STORE x27, 27*REGBYTES(sp) + STORE x28, 28*REGBYTES(sp) + STORE x29, 29*REGBYTES(sp) + STORE x30, 30*REGBYTES(sp) + STORE x31, 31*REGBYTES(sp) + + # get sr, epc, tval, cause + # Set sscratch register to 0, so that if a recursive exception + # occurs, the exception vector knows it came from the kernel + csrrw s0, sscratch, x0 + csrr s1, sstatus + csrr s2, sepc + csrr s3, 0x143 + csrr s4, scause + + STORE s0, 2*REGBYTES(sp) + STORE s1, 32*REGBYTES(sp) + STORE s2, 33*REGBYTES(sp) + STORE s3, 34*REGBYTES(sp) + STORE s4, 35*REGBYTES(sp) + .endm + + .macro RESTORE_ALL + LOCAL _save_kernel_sp + LOCAL _restore_context + + LOAD s1, 32*REGBYTES(sp) + LOAD s2, 33*REGBYTES(sp) + + andi s0, s1, SSTATUS_SPP + bnez s0, _restore_context + +_save_kernel_sp: + # Save unwound kernel stack pointer in sscratch + addi s0, sp, 36 * REGBYTES + csrw sscratch, s0 +_restore_context: + csrw sstatus, s1 + csrw sepc, s2 + + # restore x registers + LOAD x1, 1*REGBYTES(sp) + LOAD x3, 3*REGBYTES(sp) + LOAD x4, 4*REGBYTES(sp) + LOAD x5, 5*REGBYTES(sp) + LOAD x6, 6*REGBYTES(sp) + LOAD x7, 7*REGBYTES(sp) + LOAD x8, 8*REGBYTES(sp) + LOAD x9, 9*REGBYTES(sp) + LOAD x10, 10*REGBYTES(sp) + LOAD x11, 11*REGBYTES(sp) + LOAD x12, 12*REGBYTES(sp) + LOAD x13, 13*REGBYTES(sp) + LOAD x14, 14*REGBYTES(sp) + LOAD x15, 15*REGBYTES(sp) + LOAD x16, 16*REGBYTES(sp) + LOAD x17, 17*REGBYTES(sp) + LOAD x18, 18*REGBYTES(sp) + LOAD x19, 19*REGBYTES(sp) + LOAD x20, 20*REGBYTES(sp) + LOAD x21, 21*REGBYTES(sp) + LOAD x22, 22*REGBYTES(sp) + LOAD x23, 23*REGBYTES(sp) + LOAD x24, 24*REGBYTES(sp) + LOAD x25, 25*REGBYTES(sp) + LOAD x26, 26*REGBYTES(sp) + LOAD x27, 27*REGBYTES(sp) + LOAD x28, 28*REGBYTES(sp) + LOAD x29, 29*REGBYTES(sp) + LOAD x30, 30*REGBYTES(sp) + LOAD x31, 31*REGBYTES(sp) + # restore sp last + LOAD x2, 2*REGBYTES(sp) + .endm + + .globl __alltraps +__alltraps: + SAVE_ALL + + move a0, sp + jal trap + # sp should be the same as before "jal trap" + + .globl __trapret +__trapret: + RESTORE_ALL + # return from supervisor call + sret + + .globl forkrets +forkrets: + # set stack to this new process's trapframe + move sp, a0 + j __trapret + + .global kernel_execve_ret +kernel_execve_ret: + // adjust sp to beneath kstacktop of current process + addi a1, a1, -36*REGBYTES + + // copy from previous trapframe to new trapframe + LOAD s1, 35*REGBYTES(a0) + STORE s1, 35*REGBYTES(a1) + LOAD s1, 34*REGBYTES(a0) + STORE s1, 34*REGBYTES(a1) + LOAD s1, 33*REGBYTES(a0) + STORE s1, 33*REGBYTES(a1) + LOAD s1, 32*REGBYTES(a0) + STORE s1, 32*REGBYTES(a1) + LOAD s1, 31*REGBYTES(a0) + STORE s1, 31*REGBYTES(a1) + LOAD s1, 30*REGBYTES(a0) + STORE s1, 30*REGBYTES(a1) + LOAD s1, 29*REGBYTES(a0) + STORE s1, 29*REGBYTES(a1) + LOAD s1, 28*REGBYTES(a0) + STORE s1, 28*REGBYTES(a1) + LOAD s1, 27*REGBYTES(a0) + STORE s1, 27*REGBYTES(a1) + LOAD s1, 26*REGBYTES(a0) + STORE s1, 26*REGBYTES(a1) + LOAD s1, 25*REGBYTES(a0) + STORE s1, 25*REGBYTES(a1) + LOAD s1, 24*REGBYTES(a0) + STORE s1, 24*REGBYTES(a1) + LOAD s1, 23*REGBYTES(a0) + STORE s1, 23*REGBYTES(a1) + LOAD s1, 22*REGBYTES(a0) + STORE s1, 22*REGBYTES(a1) + LOAD s1, 21*REGBYTES(a0) + STORE s1, 21*REGBYTES(a1) + LOAD s1, 20*REGBYTES(a0) + STORE s1, 20*REGBYTES(a1) + LOAD s1, 19*REGBYTES(a0) + STORE s1, 19*REGBYTES(a1) + LOAD s1, 18*REGBYTES(a0) + STORE s1, 18*REGBYTES(a1) + LOAD s1, 17*REGBYTES(a0) + STORE s1, 17*REGBYTES(a1) + LOAD s1, 16*REGBYTES(a0) + STORE s1, 16*REGBYTES(a1) + LOAD s1, 15*REGBYTES(a0) + STORE s1, 15*REGBYTES(a1) + LOAD s1, 14*REGBYTES(a0) + STORE s1, 14*REGBYTES(a1) + LOAD s1, 13*REGBYTES(a0) + STORE s1, 13*REGBYTES(a1) + LOAD s1, 12*REGBYTES(a0) + STORE s1, 12*REGBYTES(a1) + LOAD s1, 11*REGBYTES(a0) + STORE s1, 11*REGBYTES(a1) + LOAD s1, 10*REGBYTES(a0) + STORE s1, 10*REGBYTES(a1) + LOAD s1, 9*REGBYTES(a0) + STORE s1, 9*REGBYTES(a1) + LOAD s1, 8*REGBYTES(a0) + STORE s1, 8*REGBYTES(a1) + LOAD s1, 7*REGBYTES(a0) + STORE s1, 7*REGBYTES(a1) + LOAD s1, 6*REGBYTES(a0) + STORE s1, 6*REGBYTES(a1) + LOAD s1, 5*REGBYTES(a0) + STORE s1, 5*REGBYTES(a1) + LOAD s1, 4*REGBYTES(a0) + STORE s1, 4*REGBYTES(a1) + LOAD s1, 3*REGBYTES(a0) + STORE s1, 3*REGBYTES(a1) + LOAD s1, 2*REGBYTES(a0) + STORE s1, 2*REGBYTES(a1) + LOAD s1, 1*REGBYTES(a0) + STORE s1, 1*REGBYTES(a1) + LOAD s1, 0*REGBYTES(a0) + STORE s1, 0*REGBYTES(a1) + + // acutually adjust sp + move sp, a1 + j __trapret \ No newline at end of file diff --git a/lab8/lab5.md b/lab8/lab5.md new file mode 100644 index 0000000..c340794 --- /dev/null +++ b/lab8/lab5.md @@ -0,0 +1,62 @@ +# 实验五:用户进程管理 + +## 练习一 + +### 实现方法 + +* 将中断帧的保存的cs和ds、es、ss分别指向用户代码段和用户数据段 +* 将esp指向用户栈的栈顶 +* 将eip设置为ELF文件提供的程序入口地址 +* 设置eflags中控制中断的位 + +用户态进程从创建到执行 + +* 调用KERNEL_EXECVE宏,通过kernel_execve函数发出系统调用 +* 经过多次函数调用到达do_execve函数 +* 进入load_icode读入程序的数据段和代码段 +* 修改栈帧 +* 逐步返回,最后iret,恢复栈帧后程序开始运行 +* 首先进入initcode.S,将ebp设为0,esp减小(用于printstackframe) +* 调用umain +* umain中调用main()函数,开始执行程序 + +## 练习二 + +### Copy on Write + +* 在do_fork时不进行内存复制,只将对应内存页的页目录项中的R/W Bit设为只读 +* 一旦发生写操作,就会引发page fault +* 在中断处理程序中恢复内存页的R/W状态 +* 进行程序代码段、数据段的复制 +* 返回继续执行 + +## 练习三 + +### fork/exec/wait/exit + +用户态的fork/exec/wait/exit函数会调用/usrlibs/syscall.h中的相应函数,然后由`static inline int syscall(int num, ...)`中的内联汇编发出中断,通过系统调用实现各个功能。 + +用户态进程的执行状态生命周期图如下: + +``` + alloc_proc RUNNING + | +--<----<--+ + | | proc_run | + V +-->---->--+ +PROC_UNINIT --> proc_init/wakeup_proc --> PROC_RUNNABLE --> try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --+ + ^ | | + | +--- do_exit --> PROC_ZOMBIE | + | | + +----------------------wakeup_proc-----------------------------------+ +``` + +## 总结 + +### 实现与参考答案的区别 + +实现相同 + +### 知识点 + +* 用户进程管理 +* 系统调用实现:系统调用通过中断实现 diff --git a/lab8/libs/atomic.h b/lab8/libs/atomic.h new file mode 100644 index 0000000..394c30d --- /dev/null +++ b/lab8/libs/atomic.h @@ -0,0 +1,109 @@ +#ifndef __LIBS_ATOMIC_H__ +#define __LIBS_ATOMIC_H__ + +/* Atomic operations that C can't guarantee us. Useful for resource counting + * etc.. */ + +static inline void set_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline void clear_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline void change_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline bool test_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline bool test_and_set_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline bool test_and_clear_bit(int nr, volatile void *addr) + __attribute__((always_inline)); + +#define BITS_PER_LONG __riscv_xlen + +#if (BITS_PER_LONG == 64) +#define __AMO(op) "amo" #op ".d" +#elif (BITS_PER_LONG == 32) +#define __AMO(op) "amo" #op ".w" +#else +#error "Unexpected BITS_PER_LONG" +#endif + +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) + +#define __test_and_op_bit(op, mod, nr, addr) \ + ({ \ + unsigned long __res, __mask; \ + __mask = BIT_MASK(nr); \ + __asm__ __volatile__(__AMO(op) " %0, %2, %1" \ + : "=r"(__res), "+A"(addr[BIT_WORD(nr)]) \ + : "r"(mod(__mask))); \ + ((__res & __mask) != 0); \ + }) + +#define __op_bit(op, mod, nr, addr) \ + __asm__ __volatile__(__AMO(op) " zero, %1, %0" \ + : "+A"(addr[BIT_WORD(nr)]) \ + : "r"(mod(BIT_MASK(nr)))) + +/* Bitmask modifiers */ +#define __NOP(x) (x) +#define __NOT(x) (~(x)) + +/* * + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + * */ +static inline void set_bit(int nr, volatile void *addr) { + __op_bit(or, __NOP, nr, ((volatile unsigned long *)addr)); +} + +/* * + * clear_bit - Atomically clears a bit in memory + * @nr: the bit to clear + * @addr: the address to start counting from + * */ +static inline void clear_bit(int nr, volatile void *addr) { + __op_bit(and, __NOT, nr, ((volatile unsigned long *)addr)); +} + +/* * + * change_bit - Atomically toggle a bit in memory + * @nr: the bit to change + * @addr: the address to start counting from + * */ +static inline void change_bit(int nr, volatile void *addr) { + __op_bit (xor, __NOP, nr, ((volatile unsigned long *)addr)); +} + +/* * + * test_bit - Determine whether a bit is set + * @nr: the bit to test + * @addr: the address to count from + * */ +static inline bool test_bit(int nr, volatile void *addr) { + return (((*(volatile unsigned long *)addr) >> nr) & 1); +} + +/* * + * test_and_set_bit - Atomically set a bit and return its old value + * @nr: the bit to set + * @addr: the address to count from + * */ +static inline bool test_and_set_bit(int nr, volatile void *addr) { + return __test_and_op_bit(or, __NOP, nr, ((volatile unsigned long *)addr)); +} + +/* * + * test_and_clear_bit - Atomically clear a bit and return its old value + * @nr: the bit to clear + * @addr: the address to count from + * */ +static inline bool test_and_clear_bit(int nr, volatile void *addr) { + return __test_and_op_bit(and, __NOT, nr, ((volatile unsigned long *)addr)); +} + +#endif /* !__LIBS_ATOMIC_H__ */ diff --git a/lab8/libs/defs.h b/lab8/libs/defs.h new file mode 100644 index 0000000..a19867a --- /dev/null +++ b/lab8/libs/defs.h @@ -0,0 +1,84 @@ +#ifndef __LIBS_DEFS_H__ +#define __LIBS_DEFS_H__ + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define __always_inline inline __attribute__((always_inline)) +#define __noinline __attribute__((noinline)) +#define __noreturn __attribute__((noreturn)) + +#define CHAR_BIT 8 + +/* Represents true-or-false values */ +typedef long long bool; + +/* Explicitly-sized versions of integer types */ +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#if __riscv_xlen == 64 + typedef uint64_t uint_t; + typedef int64_t sint_t; +#elif __riscv_xlen == 32 + typedef uint32_t uint_t; + typedef int32_t sint_t; +#endif + +/* * + * Pointers and addresses are 32 bits long. + * We use pointer types to represent addresses, + * uintptr_t to represent the numerical values of addresses. + * */ +typedef sint_t intptr_t; +typedef uint_t uintptr_t; + +/* size_t is used for memory object sizes */ +typedef uintptr_t size_t; +/* off_t is used for file offsets and lengths */ +typedef intptr_t off_t; +/* used for page numbers */ +typedef size_t ppn_t; + +/* * + * Rounding operations (efficient when n is a power of 2) + * Round down to the nearest multiple of n + * */ +#define ROUNDDOWN(a, n) ({ \ + size_t __a = (size_t)(a); \ + (typeof(a))(__a - __a % (n)); \ + }) + +/* Round up to the nearest multiple of n */ +#define ROUNDUP(a, n) ({ \ + size_t __n = (size_t)(n); \ + (typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \ + }) + +/* Round up the result of dividing of n */ +#define ROUNDUP_DIV(a, n) ({ \ +uint64_t __n = (uint64_t)(n); \ +(typeof(a))(((a) + __n - 1) / __n); \ +}) + +/* Return the offset of 'member' relative to the beginning of a struct type */ +#define offsetof(type, member) \ + ((size_t)(&((type *)0)->member)) + +/* * + * to_struct - get the struct from a ptr + * @ptr: a struct pointer of member + * @type: the type of the struct this is embedded in + * @member: the name of the member within the struct + * */ +#define to_struct(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +#endif /* !__LIBS_DEFS_H__ */ + diff --git a/lab8/libs/dirent.h b/lab8/libs/dirent.h new file mode 100644 index 0000000..429e4fe --- /dev/null +++ b/lab8/libs/dirent.h @@ -0,0 +1,13 @@ +#ifndef __LIBS_DIRENT_H__ +#define __LIBS_DIRENT_H__ + +#include +#include + +struct dirent { + off_t offset; + char name[FS_MAX_FNAME_LEN + 1]; +}; + +#endif /* !__LIBS_DIRENT_H__ */ + diff --git a/lab8/libs/elf.h b/lab8/libs/elf.h new file mode 100644 index 0000000..867066f --- /dev/null +++ b/lab8/libs/elf.h @@ -0,0 +1,48 @@ +#ifndef __LIBS_ELF_H__ +#define __LIBS_ELF_H__ + +#include + +#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian + +/* file header */ +struct elfhdr { + uint32_t e_magic; // must equal ELF_MAGIC + uint8_t e_elf[12]; + uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image + uint16_t e_machine; // 3=x86, 4=68K, etc. + uint32_t e_version; // file version, always 1 + uint64_t e_entry; // entry point if executable + uint64_t e_phoff; // file position of program header or 0 + uint64_t e_shoff; // file position of section header or 0 + uint32_t e_flags; // architecture-specific flags, usually 0 + uint16_t e_ehsize; // size of this elf header + uint16_t e_phentsize; // size of an entry in program header + uint16_t e_phnum; // number of entries in program header or 0 + uint16_t e_shentsize; // size of an entry in section header + uint16_t e_shnum; // number of entries in section header or 0 + uint16_t e_shstrndx; // section number that contains section name strings +}; + +/* program section header */ +struct proghdr { + uint32_t p_type; // loadable code or data, dynamic linking info,etc. + uint32_t p_flags; // read/write/execute bits + uint64_t p_offset; // file offset of segment + uint64_t p_va; // virtual address to map segment + uint64_t p_pa; // physical address, not used + uint64_t p_filesz; // size of segment in file + uint64_t p_memsz; // size of segment in memory (bigger if contains bss) + uint64_t p_align; // required alignment, invariably hardware page size +}; + +/* values for Proghdr::p_type */ +#define ELF_PT_LOAD 1 + +/* flag bits for Proghdr::p_flags */ +#define ELF_PF_X 1 +#define ELF_PF_W 2 +#define ELF_PF_R 4 + +#endif /* !__LIBS_ELF_H__ */ + diff --git a/lab8/libs/error.h b/lab8/libs/error.h new file mode 100644 index 0000000..ddad593 --- /dev/null +++ b/lab8/libs/error.h @@ -0,0 +1,33 @@ +#ifndef __LIBS_ERROR_H__ +#define __LIBS_ERROR_H__ + +/* kernel error codes -- keep in sync with list in lib/printfmt.c */ +#define E_UNSPECIFIED 1 // Unspecified or unknown problem +#define E_BAD_PROC 2 // Process doesn't exist or otherwise +#define E_INVAL 3 // Invalid parameter +#define E_NO_MEM 4 // Request failed due to memory shortage +#define E_NO_FREE_PROC 5 // Attempt to create a new process beyond +#define E_FAULT 6 // Memory fault +#define E_SWAP_FAULT 7 // SWAP READ/WRITE fault +#define E_INVAL_ELF 8 // Invalid elf file +#define E_KILLED 9 // Process is killed +#define E_PANIC 10 // Panic Failure +#define E_TIMEOUT 11 // Timeout +#define E_TOO_BIG 12 // Argument is Too Big +#define E_NO_DEV 13 // No such Device +#define E_NA_DEV 14 // Device Not Available +#define E_BUSY 15 // Device/File is Busy +#define E_NOENT 16 // No Such File or Directory +#define E_ISDIR 17 // Is a Directory +#define E_NOTDIR 18 // Not a Directory +#define E_XDEV 19 // Cross Device-Link +#define E_UNIMP 20 // Unimplemented Feature +#define E_SEEK 21 // Illegal Seek +#define E_MAX_OPEN 22 // Too Many Files are Open +#define E_EXISTS 23 // File/Directory Already Exists +#define E_NOTEMPTY 24 // Directory is Not Empty +/* the maximum allowed */ +#define MAXERROR 24 + +#endif /* !__LIBS_ERROR_H__ */ + diff --git a/lab8/libs/hash.c b/lab8/libs/hash.c new file mode 100644 index 0000000..61bcd88 --- /dev/null +++ b/lab8/libs/hash.c @@ -0,0 +1,18 @@ +#include + +/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ +#define GOLDEN_RATIO_PRIME_32 0x9e370001UL + +/* * + * hash32 - generate a hash value in the range [0, 2^@bits - 1] + * @val: the input value + * @bits: the number of bits in a return value + * + * High bits are more random, so we use them. + * */ +uint32_t +hash32(uint32_t val, unsigned int bits) { + uint32_t hash = val * GOLDEN_RATIO_PRIME_32; + return (hash >> (32 - bits)); +} + diff --git a/lab8/libs/list.h b/lab8/libs/list.h new file mode 100644 index 0000000..3dbf772 --- /dev/null +++ b/lab8/libs/list.h @@ -0,0 +1,163 @@ +#ifndef __LIBS_LIST_H__ +#define __LIBS_LIST_H__ + +#ifndef __ASSEMBLER__ + +#include + +/* * + * Simple 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. + * */ + +struct list_entry { + struct list_entry *prev, *next; +}; + +typedef struct list_entry list_entry_t; + +static inline void list_init(list_entry_t *elm) __attribute__((always_inline)); +static inline void list_add(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); +static inline void list_add_before(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); +static inline void list_add_after(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); +static inline void list_del(list_entry_t *listelm) __attribute__((always_inline)); +static inline void list_del_init(list_entry_t *listelm) __attribute__((always_inline)); +static inline bool list_empty(list_entry_t *list) __attribute__((always_inline)); +static inline list_entry_t *list_next(list_entry_t *listelm) __attribute__((always_inline)); +static inline list_entry_t *list_prev(list_entry_t *listelm) __attribute__((always_inline)); + +static inline void __list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); +static inline void __list_del(list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); + +/* * + * list_init - initialize a new entry + * @elm: new entry to be initialized + * */ +static inline void +list_init(list_entry_t *elm) { + elm->prev = elm->next = elm; +} + +/* * + * list_add - add a new entry + * @listelm: list head to add after + * @elm: new entry to be added + * + * Insert the new element @elm *after* the element @listelm which + * is already in the list. + * */ +static inline void +list_add(list_entry_t *listelm, list_entry_t *elm) { + list_add_after(listelm, elm); +} + +/* * + * list_add_before - add a new entry + * @listelm: list head to add before + * @elm: new entry to be added + * + * Insert the new element @elm *before* the element @listelm which + * is already in the list. + * */ +static inline void +list_add_before(list_entry_t *listelm, list_entry_t *elm) { + __list_add(elm, listelm->prev, listelm); +} + +/* * + * list_add_after - add a new entry + * @listelm: list head to add after + * @elm: new entry to be added + * + * Insert the new element @elm *after* the element @listelm which + * is already in the list. + * */ +static inline void +list_add_after(list_entry_t *listelm, list_entry_t *elm) { + __list_add(elm, listelm, listelm->next); +} + +/* * + * list_del - deletes entry from list + * @listelm: the element to delete from the list + * + * Note: list_empty() on @listelm does not return true after this, the entry is + * in an undefined state. + * */ +static inline void +list_del(list_entry_t *listelm) { + __list_del(listelm->prev, listelm->next); +} + +/* * + * list_del_init - deletes entry from list and reinitialize it. + * @listelm: the element to delete from the list. + * + * Note: list_empty() on @listelm returns true after this. + * */ +static inline void +list_del_init(list_entry_t *listelm) { + list_del(listelm); + list_init(listelm); +} + +/* * + * list_empty - tests whether a list is empty + * @list: the list to test. + * */ +static inline bool +list_empty(list_entry_t *list) { + return list->next == list; +} + +/* * + * list_next - get the next entry + * @listelm: the list head + **/ +static inline list_entry_t * +list_next(list_entry_t *listelm) { + return listelm->next; +} + +/* * + * list_prev - get the previous entry + * @listelm: the list head + **/ +static inline list_entry_t * +list_prev(list_entry_t *listelm) { + return listelm->prev; +} + +/* * + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + * */ +static inline void +__list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) { + prev->next = next->prev = elm; + elm->next = next; + elm->prev = prev; +} + +/* * + * 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(list_entry_t *prev, list_entry_t *next) { + prev->next = next; + next->prev = prev; +} + +#endif /* !__ASSEMBLER__ */ + +#endif /* !__LIBS_LIST_H__ */ + diff --git a/lab8/libs/printfmt.c b/lab8/libs/printfmt.c new file mode 100644 index 0000000..9c11ce5 --- /dev/null +++ b/lab8/libs/printfmt.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include +#include +#include + +/* * + * Space or zero padding and a field width are supported for the numeric + * formats only. + * + * The special format %e takes an integer error code + * and prints a string describing the error. + * The integer may be positive or negative, + * so that -E_NO_MEM and E_NO_MEM are equivalent. + * */ + +static const char * const error_string[MAXERROR + 1] = { + [0] NULL, + [E_UNSPECIFIED] "unspecified error", + [E_BAD_PROC] "bad process", + [E_INVAL] "invalid parameter", + [E_NO_MEM] "out of memory", + [E_NO_FREE_PROC] "out of processes", + [E_FAULT] "segmentation fault", + [E_INVAL_ELF] "invalid elf file", + [E_KILLED] "process is killed", + [E_PANIC] "panic failure", + [E_NO_DEV] "no such device", + [E_NA_DEV] "device not available", + [E_BUSY] "device/file is busy", + [E_NOENT] "no such file or directory", + [E_ISDIR] "is a directory", + [E_NOTDIR] "not a directory", + [E_XDEV] "cross device link", + [E_UNIMP] "unimplemented feature", + [E_SEEK] "illegal seek", + [E_MAX_OPEN] "too many files are open", + [E_EXISTS] "file or directory already exists", + [E_NOTEMPTY] "directory is not empty", +}; + +/* * + * printnum - print a number (base <= 16) in reverse order + * @putch: specified putch function, print a single character + * @fd: file descriptor + * @putdat: used by @putch function + * @num: the number will be printed + * @base: base for print, must be in [1, 16] + * @width: maximum number of digits, if the actual width is less than @width, use @padc instead + * @padc: character that padded on the left if the actual width is less than @width + * */ +static void +printnum(void (*putch)(int, void*, int), int fd, void *putdat, + unsigned long long num, unsigned base, int width, int padc) { + unsigned long long result = num; + unsigned mod = do_div(result, base); + + // first recursively print all preceding (more significant) digits + if (num >= base) { + printnum(putch, fd, putdat, result, base, width - 1, padc); + } else { + // print any needed pad characters before first digit + while (-- width > 0) + putch(padc, putdat, fd); + } + // then print this (the least significant) digit + putch("0123456789abcdef"[mod], putdat, fd); +} + +/* * + * getuint - get an unsigned int of various possible sizes from a varargs list + * @ap: a varargs list pointer + * @lflag: determines the size of the vararg that @ap points to + * */ +static unsigned long long +getuint(va_list *ap, int lflag) { + if (lflag >= 2) { + return va_arg(*ap, unsigned long long); + } + else if (lflag) { + return va_arg(*ap, unsigned long); + } + else { + return va_arg(*ap, unsigned int); + } +} + +/* * + * getint - same as getuint but signed, we can't use getuint because of sign extension + * @ap: a varargs list pointer + * @lflag: determines the size of the vararg that @ap points to + * */ +static long long +getint(va_list *ap, int lflag) { + if (lflag >= 2) { + return va_arg(*ap, long long); + } + else if (lflag) { + return va_arg(*ap, long); + } + else { + return va_arg(*ap, int); + } +} + +/* * + * printfmt - format a string and print it by using putch + * @putch: specified putch function, print a single character + * @fd: file descriptor + * @putdat: used by @putch function + * @fmt: the format string to use + * */ +void +printfmt(void (*putch)(int, void*, int), int fd, void *putdat, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vprintfmt(putch, fd, putdat, fmt, ap); + va_end(ap); +} + +/* * + * vprintfmt - format a string and print it by using putch, it's called with a va_list + * instead of a variable number of arguments + * @fd: file descriptor + * @putch: specified putch function, print a single character + * @putdat: used by @putch function + * @fmt: the format string to use + * @ap: arguments for the format string + * + * Call this function if you are already dealing with a va_list. + * Or you probably want printfmt() instead. + * */ +void +vprintfmt(void (*putch)(int, void*, int), int fd, void *putdat, const char *fmt, va_list ap) { + register const char *p; + register int ch, err; + unsigned long long num; + int base, width, precision, lflag, altflag; + + while (1) { + while ((ch = *(unsigned char *)fmt ++) != '%') { + if (ch == '\0') { + return; + } + putch(ch, putdat, fd); + } + + // Process a %-escape sequence + char padc = ' '; + width = precision = -1; + lflag = altflag = 0; + + reswitch: + switch (ch = *(unsigned char *)fmt ++) { + + // flag to pad on the right + case '-': + padc = '-'; + goto reswitch; + + // flag to pad with 0's instead of spaces + case '0': + padc = '0'; + goto reswitch; + + // width field + case '1' ... '9': + for (precision = 0; ; ++ fmt) { + precision = precision * 10 + ch - '0'; + ch = *fmt; + if (ch < '0' || ch > '9') { + break; + } + } + goto process_precision; + + case '*': + precision = va_arg(ap, int); + goto process_precision; + + case '.': + if (width < 0) + width = 0; + goto reswitch; + + case '#': + altflag = 1; + goto reswitch; + + process_precision: + if (width < 0) + width = precision, precision = -1; + goto reswitch; + + // long flag (doubled for long long) + case 'l': + lflag ++; + goto reswitch; + + // character + case 'c': + putch(va_arg(ap, int), putdat, fd); + break; + + // error message + case 'e': + err = va_arg(ap, int); + if (err < 0) { + err = -err; + } + if (err > MAXERROR || (p = error_string[err]) == NULL) { + printfmt(putch, fd, putdat, "error %d", err); + } + else { + printfmt(putch, fd, putdat, "%s", p); + } + break; + + // string + case 's': + if ((p = va_arg(ap, char *)) == NULL) { + p = "(null)"; + } + if (width > 0 && padc != '-') { + for (width -= strnlen(p, precision); width > 0; width --) { + putch(padc, putdat, fd); + } + } + for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) { + if (altflag && (ch < ' ' || ch > '~')) { + putch('?', putdat, fd); + } + else { + putch(ch, putdat, fd); + } + } + for (; width > 0; width --) { + putch(' ', putdat, fd); + } + break; + + // (signed) decimal + case 'd': + num = getint(&ap, lflag); + if ((long long)num < 0) { + putch('-', putdat, fd); + num = -(long long)num; + } + base = 10; + goto number; + + // unsigned decimal + case 'u': + num = getuint(&ap, lflag); + base = 10; + goto number; + + // (unsigned) octal + case 'o': + num = getuint(&ap, lflag); + base = 8; + goto number; + + // pointer + case 'p': + putch('0', putdat, fd); + putch('x', putdat, fd); + num = (unsigned long long)(uintptr_t)va_arg(ap, void *); + base = 16; + goto number; + + // (unsigned) hexadecimal + case 'x': + num = getuint(&ap, lflag); + base = 16; + number: + printnum(putch, fd, putdat, num, base, width, padc); + break; + + // escaped '%' character + case '%': + putch(ch, putdat, fd); + break; + + // unrecognized escape sequence - just print it literally + default: + putch('%', putdat, fd); + for (fmt --; fmt[-1] != '%'; fmt --) + /* do nothing */; + break; + } + } +} + +/* sprintbuf is used to save enough information of a buffer */ +struct sprintbuf { + char *buf; // address pointer points to the first unused memory + char *ebuf; // points the end of the buffer + int cnt; // the number of characters that have been placed in this buffer +}; + +/* * + * sprintputch - 'print' a single character in a buffer + * @ch: the character will be printed + * @b: the buffer to place the character @ch + * */ +static void +sprintputch(int ch, struct sprintbuf *b) { + b->cnt ++; + if (b->buf < b->ebuf) { + *b->buf ++ = ch; + } +} + +/* * + * snprintf - format a string and place it in a buffer + * @str: the buffer to place the result into + * @size: the size of buffer, including the trailing null space + * @fmt: the format string to use + * */ +int +snprintf(char *str, size_t size, const char *fmt, ...) { + va_list ap; + int cnt; + va_start(ap, fmt); + cnt = vsnprintf(str, size, fmt, ap); + va_end(ap); + return cnt; +} + +/* * + * vsnprintf - format a string and place it in a buffer, it's called with a va_list + * instead of a variable number of arguments + * @str: the buffer to place the result into + * @size: the size of buffer, including the trailing null space + * @fmt: the format string to use + * @ap: arguments for the format string + * + * The return value is the number of characters which would be generated for the + * given input, excluding the trailing '\0'. + * + * Call this function if you are already dealing with a va_list. + * Or you probably want snprintf() instead. + * */ +int +vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { + struct sprintbuf b = {str, str + size - 1, 0}; + if (str == NULL || b.buf > b.ebuf) { + return -E_INVAL; + } + // print the string to the buffer + vprintfmt((void*)sprintputch, NO_FD, &b, fmt, ap); + // null terminate the buffer + *b.buf = '\0'; + return b.cnt; +} + diff --git a/lab8/libs/rand.c b/lab8/libs/rand.c new file mode 100644 index 0000000..ff74c71 --- /dev/null +++ b/lab8/libs/rand.c @@ -0,0 +1,26 @@ +#include +#include + +static unsigned long long next = 1; + +/* * + * rand - returns a pseudo-random integer + * + * The rand() function return a value in the range [0, RAND_MAX]. + * */ +int +rand(void) { + next = (next * 0x5DEECE66DLL + 0xBLL) & ((1LL << 48) - 1); + unsigned long long result = (next >> 12); + return (int)do_div(result, RAND_MAX + 1); +} + +/* * + * srand - seed the random number generator with the given number + * @seed: the required seed number + * */ +void +srand(unsigned int seed) { + next = seed; +} + diff --git a/lab8/libs/riscv.h b/lab8/libs/riscv.h new file mode 100644 index 0000000..5df00b2 --- /dev/null +++ b/lab8/libs/riscv.h @@ -0,0 +1,1501 @@ +// See LICENSE for license details. + +#ifndef __LIBS_RISCV_H__ +#define __LIBS_RISCV_H__ + +#if __riscv_xlen == 64 +# define SLL32 sllw +# define STORE sd +# define LOAD ld +# define LWU lwu +# define LOG_REGBYTES 3 +#else +# define SLL32 sll +# define STORE sw +# define LOAD lw +# define LWU lw +# define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_SUM 0x00040000 +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_TVM 0x00100000 +#define MSTATUS_TW 0x00200000 +#define MSTATUS_TSR 0x00400000 +#define MSTATUS32_SD 0x80000000 +#define MSTATUS_UXL 0x0000000300000000 +#define MSTATUS_SXL 0x0000000C00000000 +#define MSTATUS64_SD 0x8000000000000000 + +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_PUM 0x00040000 +#define SSTATUS_MXR 0x00080000 +#define SSTATUS32_SD 0x80000000 +#define SSTATUS64_SD 0x8000000000000000 + +#define DCSR_XDEBUGVER (3U<<30) +#define DCSR_NDRESET (1<<29) +#define DCSR_FULLRESET (1<<28) +#define DCSR_EBREAKM (1<<15) +#define DCSR_EBREAKH (1<<14) +#define DCSR_EBREAKS (1<<13) +#define DCSR_EBREAKU (1<<12) +#define DCSR_STOPCYCLE (1<<10) +#define DCSR_STOPTIME (1<<9) +#define DCSR_CAUSE (7<<6) +#define DCSR_DEBUGINT (1<<5) +#define DCSR_HALT (1<<3) +#define DCSR_STEP (1<<2) +#define DCSR_PRV (3<<0) + +#define DCSR_CAUSE_NONE 0 +#define DCSR_CAUSE_SWBP 1 +#define DCSR_CAUSE_HWBP 2 +#define DCSR_CAUSE_DEBUGINT 3 +#define DCSR_CAUSE_STEP 4 +#define DCSR_CAUSE_HALT 5 + +#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) +#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) +#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11)) + +#define MCONTROL_SELECT (1<<19) +#define MCONTROL_TIMING (1<<18) +#define MCONTROL_ACTION (0x3f<<12) +#define MCONTROL_CHAIN (1<<11) +#define MCONTROL_MATCH (0xf<<7) +#define MCONTROL_M (1<<6) +#define MCONTROL_H (1<<5) +#define MCONTROL_S (1<<4) +#define MCONTROL_U (1<<3) +#define MCONTROL_EXECUTE (1<<2) +#define MCONTROL_STORE (1<<1) +#define MCONTROL_LOAD (1<<0) + +#define MCONTROL_TYPE_NONE 0 +#define MCONTROL_TYPE_MATCH 2 + +#define MCONTROL_ACTION_DEBUG_EXCEPTION 0 +#define MCONTROL_ACTION_DEBUG_MODE 1 +#define MCONTROL_ACTION_TRACE_START 2 +#define MCONTROL_ACTION_TRACE_STOP 3 +#define MCONTROL_ACTION_TRACE_EMIT 4 + +#define MCONTROL_MATCH_EQUAL 0 +#define MCONTROL_MATCH_NAPOT 1 +#define MCONTROL_MATCH_GE 2 +#define MCONTROL_MATCH_LT 3 +#define MCONTROL_MATCH_MASK_LOW 4 +#define MCONTROL_MATCH_MASK_HIGH 5 + +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7FC00000 +#define SATP32_PPN 0x003FFFFF +#define SATP64_MODE 0xF000000000000000 +#define SATP64_ASID 0x0FFFF00000000000 +#define SATP64_PPN 0x00000FFFFFFFFFFF + +#define SATP_MODE_OFF 0 +#define SATP_MODE_SV32 1 +#define SATP_MODE_SV39 8 +#define SATP_MODE_SV48 9 +#define SATP_MODE_SV57 10 +#define SATP_MODE_SV64 11 + +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_L 0x80 +#define PMP_SHIFT 2 + +#define PMP_TOR 0x08 +#define PMP_NA4 0x10 +#define PMP_NAPOT 0x18 + +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_COP 12 +#define IRQ_HOST 13 + +#define DEFAULT_RSTVEC 0x00001000 +#define CLINT_BASE 0x02000000 +#define CLINT_SIZE 0x000c0000 +#define EXT_IO_BASE 0x40000000 +#define DRAM_BASE 0x80000000 + +// page table entry (PTE) fields +#define PTE_V 0x001 // Valid +#define PTE_R 0x002 // Read +#define PTE_W 0x004 // Write +#define PTE_X 0x008 // Execute +#define PTE_U 0x010 // User +#define PTE_G 0x020 // Global +#define PTE_A 0x040 // Accessed +#define PTE_D 0x080 // Dirty +#define PTE_SOFT 0x300 // Reserved for Software + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) + +#ifdef __riscv + +#if __riscv_xlen == 64 +# define MSTATUS_SD MSTATUS64_SD +# define SSTATUS_SD SSTATUS64_SD +# define RISCV_PGLEVEL_BITS 9 +#else +# define MSTATUS_SD MSTATUS32_SD +# define SSTATUS_SD SSTATUS32_SD +# define RISCV_PGLEVEL_BITS 10 +#endif +#define RISCV_PGSHIFT 12 +#define RISCV_PGSIZE (1 << RISCV_PGSHIFT) + +#ifndef __ASSEMBLER__ + +#ifdef __GNUC__ + +#define read_csr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ + asm volatile ("csrw " #reg ", %0" :: "rK"(val)); }) + +#define swap_csr(reg, val) ({ unsigned long __tmp; \ + asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ + __tmp; }) + +#define set_csr(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define clear_csr(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define rdtime() read_csr(time) +#define rdcycle() read_csr(cycle) +#define rdinstret() read_csr(instret) + +#endif + +#define do_div(n, base) \ + ({ \ + int __res; \ + __res = ((unsigned long)n) % (unsigned)base; \ + n = ((unsigned long)n) / (unsigned)base; \ + __res; \ + }) + +#define barrier() __asm__ __volatile__ ("fence" ::: "memory") + +static inline void +lcr3(unsigned long cr3) { + write_csr(satp, 0x8000000000000000 | (cr3 >> RISCV_PGSHIFT)); +} + +#endif + +#endif + +#endif +/* Automatically generated by parse-opcodes. */ +#ifndef RISCV_ENCODING_H +#define RISCV_ENCODING_H +#define MATCH_BEQ 0x63 +#define MASK_BEQ 0x707f +#define MATCH_BNE 0x1063 +#define MASK_BNE 0x707f +#define MATCH_BLT 0x4063 +#define MASK_BLT 0x707f +#define MATCH_BGE 0x5063 +#define MASK_BGE 0x707f +#define MATCH_BLTU 0x6063 +#define MASK_BLTU 0x707f +#define MATCH_BGEU 0x7063 +#define MASK_BGEU 0x707f +#define MATCH_JALR 0x67 +#define MASK_JALR 0x707f +#define MATCH_JAL 0x6f +#define MASK_JAL 0x7f +#define MATCH_LUI 0x37 +#define MASK_LUI 0x7f +#define MATCH_AUIPC 0x17 +#define MASK_AUIPC 0x7f +#define MATCH_ADDI 0x13 +#define MASK_ADDI 0x707f +#define MATCH_SLLI 0x1013 +#define MASK_SLLI 0xfc00707f +#define MATCH_SLTI 0x2013 +#define MASK_SLTI 0x707f +#define MATCH_SLTIU 0x3013 +#define MASK_SLTIU 0x707f +#define MATCH_XORI 0x4013 +#define MASK_XORI 0x707f +#define MATCH_SRLI 0x5013 +#define MASK_SRLI 0xfc00707f +#define MATCH_SRAI 0x40005013 +#define MASK_SRAI 0xfc00707f +#define MATCH_ORI 0x6013 +#define MASK_ORI 0x707f +#define MATCH_ANDI 0x7013 +#define MASK_ANDI 0x707f +#define MATCH_ADD 0x33 +#define MASK_ADD 0xfe00707f +#define MATCH_SUB 0x40000033 +#define MASK_SUB 0xfe00707f +#define MATCH_SLL 0x1033 +#define MASK_SLL 0xfe00707f +#define MATCH_SLT 0x2033 +#define MASK_SLT 0xfe00707f +#define MATCH_SLTU 0x3033 +#define MASK_SLTU 0xfe00707f +#define MATCH_XOR 0x4033 +#define MASK_XOR 0xfe00707f +#define MATCH_SRL 0x5033 +#define MASK_SRL 0xfe00707f +#define MATCH_SRA 0x40005033 +#define MASK_SRA 0xfe00707f +#define MATCH_OR 0x6033 +#define MASK_OR 0xfe00707f +#define MATCH_AND 0x7033 +#define MASK_AND 0xfe00707f +#define MATCH_ADDIW 0x1b +#define MASK_ADDIW 0x707f +#define MATCH_SLLIW 0x101b +#define MASK_SLLIW 0xfe00707f +#define MATCH_SRLIW 0x501b +#define MASK_SRLIW 0xfe00707f +#define MATCH_SRAIW 0x4000501b +#define MASK_SRAIW 0xfe00707f +#define MATCH_ADDW 0x3b +#define MASK_ADDW 0xfe00707f +#define MATCH_SUBW 0x4000003b +#define MASK_SUBW 0xfe00707f +#define MATCH_SLLW 0x103b +#define MASK_SLLW 0xfe00707f +#define MATCH_SRLW 0x503b +#define MASK_SRLW 0xfe00707f +#define MATCH_SRAW 0x4000503b +#define MASK_SRAW 0xfe00707f +#define MATCH_LB 0x3 +#define MASK_LB 0x707f +#define MATCH_LH 0x1003 +#define MASK_LH 0x707f +#define MATCH_LW 0x2003 +#define MASK_LW 0x707f +#define MATCH_LD 0x3003 +#define MASK_LD 0x707f +#define MATCH_LBU 0x4003 +#define MASK_LBU 0x707f +#define MATCH_LHU 0x5003 +#define MASK_LHU 0x707f +#define MATCH_LWU 0x6003 +#define MASK_LWU 0x707f +#define MATCH_SB 0x23 +#define MASK_SB 0x707f +#define MATCH_SH 0x1023 +#define MASK_SH 0x707f +#define MATCH_SW 0x2023 +#define MASK_SW 0x707f +#define MATCH_SD 0x3023 +#define MASK_SD 0x707f +#define MATCH_FENCE 0xf +#define MASK_FENCE 0x707f +#define MATCH_FENCE_I 0x100f +#define MASK_FENCE_I 0x707f +#define MATCH_MUL 0x2000033 +#define MASK_MUL 0xfe00707f +#define MATCH_MULH 0x2001033 +#define MASK_MULH 0xfe00707f +#define MATCH_MULHSU 0x2002033 +#define MASK_MULHSU 0xfe00707f +#define MATCH_MULHU 0x2003033 +#define MASK_MULHU 0xfe00707f +#define MATCH_DIV 0x2004033 +#define MASK_DIV 0xfe00707f +#define MATCH_DIVU 0x2005033 +#define MASK_DIVU 0xfe00707f +#define MATCH_REM 0x2006033 +#define MASK_REM 0xfe00707f +#define MATCH_REMU 0x2007033 +#define MASK_REMU 0xfe00707f +#define MATCH_MULW 0x200003b +#define MASK_MULW 0xfe00707f +#define MATCH_DIVW 0x200403b +#define MASK_DIVW 0xfe00707f +#define MATCH_DIVUW 0x200503b +#define MASK_DIVUW 0xfe00707f +#define MATCH_REMW 0x200603b +#define MASK_REMW 0xfe00707f +#define MATCH_REMUW 0x200703b +#define MASK_REMUW 0xfe00707f +#define MATCH_AMOADD_W 0x202f +#define MASK_AMOADD_W 0xf800707f +#define MATCH_AMOXOR_W 0x2000202f +#define MASK_AMOXOR_W 0xf800707f +#define MATCH_AMOOR_W 0x4000202f +#define MASK_AMOOR_W 0xf800707f +#define MATCH_AMOAND_W 0x6000202f +#define MASK_AMOAND_W 0xf800707f +#define MATCH_AMOMIN_W 0x8000202f +#define MASK_AMOMIN_W 0xf800707f +#define MATCH_AMOMAX_W 0xa000202f +#define MASK_AMOMAX_W 0xf800707f +#define MATCH_AMOMINU_W 0xc000202f +#define MASK_AMOMINU_W 0xf800707f +#define MATCH_AMOMAXU_W 0xe000202f +#define MASK_AMOMAXU_W 0xf800707f +#define MATCH_AMOSWAP_W 0x800202f +#define MASK_AMOSWAP_W 0xf800707f +#define MATCH_LR_W 0x1000202f +#define MASK_LR_W 0xf9f0707f +#define MATCH_SC_W 0x1800202f +#define MASK_SC_W 0xf800707f +#define MATCH_AMOADD_D 0x302f +#define MASK_AMOADD_D 0xf800707f +#define MATCH_AMOXOR_D 0x2000302f +#define MASK_AMOXOR_D 0xf800707f +#define MATCH_AMOOR_D 0x4000302f +#define MASK_AMOOR_D 0xf800707f +#define MATCH_AMOAND_D 0x6000302f +#define MASK_AMOAND_D 0xf800707f +#define MATCH_AMOMIN_D 0x8000302f +#define MASK_AMOMIN_D 0xf800707f +#define MATCH_AMOMAX_D 0xa000302f +#define MASK_AMOMAX_D 0xf800707f +#define MATCH_AMOMINU_D 0xc000302f +#define MASK_AMOMINU_D 0xf800707f +#define MATCH_AMOMAXU_D 0xe000302f +#define MASK_AMOMAXU_D 0xf800707f +#define MATCH_AMOSWAP_D 0x800302f +#define MASK_AMOSWAP_D 0xf800707f +#define MATCH_LR_D 0x1000302f +#define MASK_LR_D 0xf9f0707f +#define MATCH_SC_D 0x1800302f +#define MASK_SC_D 0xf800707f +#define MATCH_ECALL 0x73 +#define MASK_ECALL 0xffffffff +#define MATCH_EBREAK 0x100073 +#define MASK_EBREAK 0xffffffff +#define MATCH_URET 0x200073 +#define MASK_URET 0xffffffff +#define MATCH_SRET 0x10200073 +#define MASK_SRET 0xffffffff +#define MATCH_MRET 0x30200073 +#define MASK_MRET 0xffffffff +#define MATCH_DRET 0x7b200073 +#define MASK_DRET 0xffffffff +#define MATCH_SFENCE_VMA 0x12000073 +#define MASK_SFENCE_VMA 0xfe007fff +#define MATCH_WFI 0x10500073 +#define MASK_WFI 0xffffffff +#define MATCH_CSRRW 0x1073 +#define MASK_CSRRW 0x707f +#define MATCH_CSRRS 0x2073 +#define MASK_CSRRS 0x707f +#define MATCH_CSRRC 0x3073 +#define MASK_CSRRC 0x707f +#define MATCH_CSRRWI 0x5073 +#define MASK_CSRRWI 0x707f +#define MATCH_CSRRSI 0x6073 +#define MASK_CSRRSI 0x707f +#define MATCH_CSRRCI 0x7073 +#define MASK_CSRRCI 0x707f +#define MATCH_FADD_S 0x53 +#define MASK_FADD_S 0xfe00007f +#define MATCH_FSUB_S 0x8000053 +#define MASK_FSUB_S 0xfe00007f +#define MATCH_FMUL_S 0x10000053 +#define MASK_FMUL_S 0xfe00007f +#define MATCH_FDIV_S 0x18000053 +#define MASK_FDIV_S 0xfe00007f +#define MATCH_FSGNJ_S 0x20000053 +#define MASK_FSGNJ_S 0xfe00707f +#define MATCH_FSGNJN_S 0x20001053 +#define MASK_FSGNJN_S 0xfe00707f +#define MATCH_FSGNJX_S 0x20002053 +#define MASK_FSGNJX_S 0xfe00707f +#define MATCH_FMIN_S 0x28000053 +#define MASK_FMIN_S 0xfe00707f +#define MATCH_FMAX_S 0x28001053 +#define MASK_FMAX_S 0xfe00707f +#define MATCH_FSQRT_S 0x58000053 +#define MASK_FSQRT_S 0xfff0007f +#define MATCH_FADD_D 0x2000053 +#define MASK_FADD_D 0xfe00007f +#define MATCH_FSUB_D 0xa000053 +#define MASK_FSUB_D 0xfe00007f +#define MATCH_FMUL_D 0x12000053 +#define MASK_FMUL_D 0xfe00007f +#define MATCH_FDIV_D 0x1a000053 +#define MASK_FDIV_D 0xfe00007f +#define MATCH_FSGNJ_D 0x22000053 +#define MASK_FSGNJ_D 0xfe00707f +#define MATCH_FSGNJN_D 0x22001053 +#define MASK_FSGNJN_D 0xfe00707f +#define MATCH_FSGNJX_D 0x22002053 +#define MASK_FSGNJX_D 0xfe00707f +#define MATCH_FMIN_D 0x2a000053 +#define MASK_FMIN_D 0xfe00707f +#define MATCH_FMAX_D 0x2a001053 +#define MASK_FMAX_D 0xfe00707f +#define MATCH_FCVT_S_D 0x40100053 +#define MASK_FCVT_S_D 0xfff0007f +#define MATCH_FCVT_D_S 0x42000053 +#define MASK_FCVT_D_S 0xfff0007f +#define MATCH_FSQRT_D 0x5a000053 +#define MASK_FSQRT_D 0xfff0007f +#define MATCH_FADD_Q 0x6000053 +#define MASK_FADD_Q 0xfe00007f +#define MATCH_FSUB_Q 0xe000053 +#define MASK_FSUB_Q 0xfe00007f +#define MATCH_FMUL_Q 0x16000053 +#define MASK_FMUL_Q 0xfe00007f +#define MATCH_FDIV_Q 0x1e000053 +#define MASK_FDIV_Q 0xfe00007f +#define MATCH_FSGNJ_Q 0x26000053 +#define MASK_FSGNJ_Q 0xfe00707f +#define MATCH_FSGNJN_Q 0x26001053 +#define MASK_FSGNJN_Q 0xfe00707f +#define MATCH_FSGNJX_Q 0x26002053 +#define MASK_FSGNJX_Q 0xfe00707f +#define MATCH_FMIN_Q 0x2e000053 +#define MASK_FMIN_Q 0xfe00707f +#define MATCH_FMAX_Q 0x2e001053 +#define MASK_FMAX_Q 0xfe00707f +#define MATCH_FCVT_S_Q 0x40300053 +#define MASK_FCVT_S_Q 0xfff0007f +#define MATCH_FCVT_Q_S 0x46000053 +#define MASK_FCVT_Q_S 0xfff0007f +#define MATCH_FCVT_D_Q 0x42300053 +#define MASK_FCVT_D_Q 0xfff0007f +#define MATCH_FCVT_Q_D 0x46100053 +#define MASK_FCVT_Q_D 0xfff0007f +#define MATCH_FSQRT_Q 0x5e000053 +#define MASK_FSQRT_Q 0xfff0007f +#define MATCH_FLE_S 0xa0000053 +#define MASK_FLE_S 0xfe00707f +#define MATCH_FLT_S 0xa0001053 +#define MASK_FLT_S 0xfe00707f +#define MATCH_FEQ_S 0xa0002053 +#define MASK_FEQ_S 0xfe00707f +#define MATCH_FLE_D 0xa2000053 +#define MASK_FLE_D 0xfe00707f +#define MATCH_FLT_D 0xa2001053 +#define MASK_FLT_D 0xfe00707f +#define MATCH_FEQ_D 0xa2002053 +#define MASK_FEQ_D 0xfe00707f +#define MATCH_FLE_Q 0xa6000053 +#define MASK_FLE_Q 0xfe00707f +#define MATCH_FLT_Q 0xa6001053 +#define MASK_FLT_Q 0xfe00707f +#define MATCH_FEQ_Q 0xa6002053 +#define MASK_FEQ_Q 0xfe00707f +#define MATCH_FCVT_W_S 0xc0000053 +#define MASK_FCVT_W_S 0xfff0007f +#define MATCH_FCVT_WU_S 0xc0100053 +#define MASK_FCVT_WU_S 0xfff0007f +#define MATCH_FCVT_L_S 0xc0200053 +#define MASK_FCVT_L_S 0xfff0007f +#define MATCH_FCVT_LU_S 0xc0300053 +#define MASK_FCVT_LU_S 0xfff0007f +#define MATCH_FMV_X_W 0xe0000053 +#define MASK_FMV_X_W 0xfff0707f +#define MATCH_FCLASS_S 0xe0001053 +#define MASK_FCLASS_S 0xfff0707f +#define MATCH_FCVT_W_D 0xc2000053 +#define MASK_FCVT_W_D 0xfff0007f +#define MATCH_FCVT_WU_D 0xc2100053 +#define MASK_FCVT_WU_D 0xfff0007f +#define MATCH_FCVT_L_D 0xc2200053 +#define MASK_FCVT_L_D 0xfff0007f +#define MATCH_FCVT_LU_D 0xc2300053 +#define MASK_FCVT_LU_D 0xfff0007f +#define MATCH_FMV_X_D 0xe2000053 +#define MASK_FMV_X_D 0xfff0707f +#define MATCH_FCLASS_D 0xe2001053 +#define MASK_FCLASS_D 0xfff0707f +#define MATCH_FCVT_W_Q 0xc6000053 +#define MASK_FCVT_W_Q 0xfff0007f +#define MATCH_FCVT_WU_Q 0xc6100053 +#define MASK_FCVT_WU_Q 0xfff0007f +#define MATCH_FCVT_L_Q 0xc6200053 +#define MASK_FCVT_L_Q 0xfff0007f +#define MATCH_FCVT_LU_Q 0xc6300053 +#define MASK_FCVT_LU_Q 0xfff0007f +#define MATCH_FMV_X_Q 0xe6000053 +#define MASK_FMV_X_Q 0xfff0707f +#define MATCH_FCLASS_Q 0xe6001053 +#define MASK_FCLASS_Q 0xfff0707f +#define MATCH_FCVT_S_W 0xd0000053 +#define MASK_FCVT_S_W 0xfff0007f +#define MATCH_FCVT_S_WU 0xd0100053 +#define MASK_FCVT_S_WU 0xfff0007f +#define MATCH_FCVT_S_L 0xd0200053 +#define MASK_FCVT_S_L 0xfff0007f +#define MATCH_FCVT_S_LU 0xd0300053 +#define MASK_FCVT_S_LU 0xfff0007f +#define MATCH_FMV_W_X 0xf0000053 +#define MASK_FMV_W_X 0xfff0707f +#define MATCH_FCVT_D_W 0xd2000053 +#define MASK_FCVT_D_W 0xfff0007f +#define MATCH_FCVT_D_WU 0xd2100053 +#define MASK_FCVT_D_WU 0xfff0007f +#define MATCH_FCVT_D_L 0xd2200053 +#define MASK_FCVT_D_L 0xfff0007f +#define MATCH_FCVT_D_LU 0xd2300053 +#define MASK_FCVT_D_LU 0xfff0007f +#define MATCH_FMV_D_X 0xf2000053 +#define MASK_FMV_D_X 0xfff0707f +#define MATCH_FCVT_Q_W 0xd6000053 +#define MASK_FCVT_Q_W 0xfff0007f +#define MATCH_FCVT_Q_WU 0xd6100053 +#define MASK_FCVT_Q_WU 0xfff0007f +#define MATCH_FCVT_Q_L 0xd6200053 +#define MASK_FCVT_Q_L 0xfff0007f +#define MATCH_FCVT_Q_LU 0xd6300053 +#define MASK_FCVT_Q_LU 0xfff0007f +#define MATCH_FMV_Q_X 0xf6000053 +#define MASK_FMV_Q_X 0xfff0707f +#define MATCH_FLW 0x2007 +#define MASK_FLW 0x707f +#define MATCH_FLD 0x3007 +#define MASK_FLD 0x707f +#define MATCH_FLQ 0x4007 +#define MASK_FLQ 0x707f +#define MATCH_FSW 0x2027 +#define MASK_FSW 0x707f +#define MATCH_FSD 0x3027 +#define MASK_FSD 0x707f +#define MATCH_FSQ 0x4027 +#define MASK_FSQ 0x707f +#define MATCH_FMADD_S 0x43 +#define MASK_FMADD_S 0x600007f +#define MATCH_FMSUB_S 0x47 +#define MASK_FMSUB_S 0x600007f +#define MATCH_FNMSUB_S 0x4b +#define MASK_FNMSUB_S 0x600007f +#define MATCH_FNMADD_S 0x4f +#define MASK_FNMADD_S 0x600007f +#define MATCH_FMADD_D 0x2000043 +#define MASK_FMADD_D 0x600007f +#define MATCH_FMSUB_D 0x2000047 +#define MASK_FMSUB_D 0x600007f +#define MATCH_FNMSUB_D 0x200004b +#define MASK_FNMSUB_D 0x600007f +#define MATCH_FNMADD_D 0x200004f +#define MASK_FNMADD_D 0x600007f +#define MATCH_FMADD_Q 0x6000043 +#define MASK_FMADD_Q 0x600007f +#define MATCH_FMSUB_Q 0x6000047 +#define MASK_FMSUB_Q 0x600007f +#define MATCH_FNMSUB_Q 0x600004b +#define MASK_FNMSUB_Q 0x600007f +#define MATCH_FNMADD_Q 0x600004f +#define MASK_FNMADD_Q 0x600007f +#define MATCH_C_NOP 0x1 +#define MASK_C_NOP 0xffff +#define MATCH_C_ADDI16SP 0x6101 +#define MASK_C_ADDI16SP 0xef83 +#define MATCH_C_JR 0x8002 +#define MASK_C_JR 0xf07f +#define MATCH_C_JALR 0x9002 +#define MASK_C_JALR 0xf07f +#define MATCH_C_EBREAK 0x9002 +#define MASK_C_EBREAK 0xffff +#define MATCH_C_LD 0x6000 +#define MASK_C_LD 0xe003 +#define MATCH_C_SD 0xe000 +#define MASK_C_SD 0xe003 +#define MATCH_C_ADDIW 0x2001 +#define MASK_C_ADDIW 0xe003 +#define MATCH_C_LDSP 0x6002 +#define MASK_C_LDSP 0xe003 +#define MATCH_C_SDSP 0xe002 +#define MASK_C_SDSP 0xe003 +#define MATCH_C_ADDI4SPN 0x0 +#define MASK_C_ADDI4SPN 0xe003 +#define MATCH_C_FLD 0x2000 +#define MASK_C_FLD 0xe003 +#define MATCH_C_LW 0x4000 +#define MASK_C_LW 0xe003 +#define MATCH_C_FLW 0x6000 +#define MASK_C_FLW 0xe003 +#define MATCH_C_FSD 0xa000 +#define MASK_C_FSD 0xe003 +#define MATCH_C_SW 0xc000 +#define MASK_C_SW 0xe003 +#define MATCH_C_FSW 0xe000 +#define MASK_C_FSW 0xe003 +#define MATCH_C_ADDI 0x1 +#define MASK_C_ADDI 0xe003 +#define MATCH_C_JAL 0x2001 +#define MASK_C_JAL 0xe003 +#define MATCH_C_LI 0x4001 +#define MASK_C_LI 0xe003 +#define MATCH_C_LUI 0x6001 +#define MASK_C_LUI 0xe003 +#define MATCH_C_SRLI 0x8001 +#define MASK_C_SRLI 0xec03 +#define MATCH_C_SRAI 0x8401 +#define MASK_C_SRAI 0xec03 +#define MATCH_C_ANDI 0x8801 +#define MASK_C_ANDI 0xec03 +#define MATCH_C_SUB 0x8c01 +#define MASK_C_SUB 0xfc63 +#define MATCH_C_XOR 0x8c21 +#define MASK_C_XOR 0xfc63 +#define MATCH_C_OR 0x8c41 +#define MASK_C_OR 0xfc63 +#define MATCH_C_AND 0x8c61 +#define MASK_C_AND 0xfc63 +#define MATCH_C_SUBW 0x9c01 +#define MASK_C_SUBW 0xfc63 +#define MATCH_C_ADDW 0x9c21 +#define MASK_C_ADDW 0xfc63 +#define MATCH_C_J 0xa001 +#define MASK_C_J 0xe003 +#define MATCH_C_BEQZ 0xc001 +#define MASK_C_BEQZ 0xe003 +#define MATCH_C_BNEZ 0xe001 +#define MASK_C_BNEZ 0xe003 +#define MATCH_C_SLLI 0x2 +#define MASK_C_SLLI 0xe003 +#define MATCH_C_FLDSP 0x2002 +#define MASK_C_FLDSP 0xe003 +#define MATCH_C_LWSP 0x4002 +#define MASK_C_LWSP 0xe003 +#define MATCH_C_FLWSP 0x6002 +#define MASK_C_FLWSP 0xe003 +#define MATCH_C_MV 0x8002 +#define MASK_C_MV 0xf003 +#define MATCH_C_ADD 0x9002 +#define MASK_C_ADD 0xf003 +#define MATCH_C_FSDSP 0xa002 +#define MASK_C_FSDSP 0xe003 +#define MATCH_C_SWSP 0xc002 +#define MASK_C_SWSP 0xe003 +#define MATCH_C_FSWSP 0xe002 +#define MASK_C_FSWSP 0xe003 +#define MATCH_CUSTOM0 0xb +#define MASK_CUSTOM0 0x707f +#define MATCH_CUSTOM0_RS1 0x200b +#define MASK_CUSTOM0_RS1 0x707f +#define MATCH_CUSTOM0_RS1_RS2 0x300b +#define MASK_CUSTOM0_RS1_RS2 0x707f +#define MATCH_CUSTOM0_RD 0x400b +#define MASK_CUSTOM0_RD 0x707f +#define MATCH_CUSTOM0_RD_RS1 0x600b +#define MASK_CUSTOM0_RD_RS1 0x707f +#define MATCH_CUSTOM0_RD_RS1_RS2 0x700b +#define MASK_CUSTOM0_RD_RS1_RS2 0x707f +#define MATCH_CUSTOM1 0x2b +#define MASK_CUSTOM1 0x707f +#define MATCH_CUSTOM1_RS1 0x202b +#define MASK_CUSTOM1_RS1 0x707f +#define MATCH_CUSTOM1_RS1_RS2 0x302b +#define MASK_CUSTOM1_RS1_RS2 0x707f +#define MATCH_CUSTOM1_RD 0x402b +#define MASK_CUSTOM1_RD 0x707f +#define MATCH_CUSTOM1_RD_RS1 0x602b +#define MASK_CUSTOM1_RD_RS1 0x707f +#define MATCH_CUSTOM1_RD_RS1_RS2 0x702b +#define MASK_CUSTOM1_RD_RS1_RS2 0x707f +#define MATCH_CUSTOM2 0x5b +#define MASK_CUSTOM2 0x707f +#define MATCH_CUSTOM2_RS1 0x205b +#define MASK_CUSTOM2_RS1 0x707f +#define MATCH_CUSTOM2_RS1_RS2 0x305b +#define MASK_CUSTOM2_RS1_RS2 0x707f +#define MATCH_CUSTOM2_RD 0x405b +#define MASK_CUSTOM2_RD 0x707f +#define MATCH_CUSTOM2_RD_RS1 0x605b +#define MASK_CUSTOM2_RD_RS1 0x707f +#define MATCH_CUSTOM2_RD_RS1_RS2 0x705b +#define MASK_CUSTOM2_RD_RS1_RS2 0x707f +#define MATCH_CUSTOM3 0x7b +#define MASK_CUSTOM3 0x707f +#define MATCH_CUSTOM3_RS1 0x207b +#define MASK_CUSTOM3_RS1 0x707f +#define MATCH_CUSTOM3_RS1_RS2 0x307b +#define MASK_CUSTOM3_RS1_RS2 0x707f +#define MATCH_CUSTOM3_RD 0x407b +#define MASK_CUSTOM3_RD 0x707f +#define MATCH_CUSTOM3_RD_RS1 0x607b +#define MASK_CUSTOM3_RD_RS1 0x707f +#define MATCH_CUSTOM3_RD_RS1_RS2 0x707b +#define MASK_CUSTOM3_RD_RS1_RS2 0x707f +#define CSR_FFLAGS 0x1 +#define CSR_FRM 0x2 +#define CSR_FCSR 0x3 +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 +#define CSR_SATP 0x180 +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f +#define CAUSE_MISALIGNED_FETCH 0x0 +#define CAUSE_FETCH_ACCESS 0x1 +#define CAUSE_ILLEGAL_INSTRUCTION 0x2 +#define CAUSE_BREAKPOINT 0x3 +#define CAUSE_MISALIGNED_LOAD 0x4 +#define CAUSE_LOAD_ACCESS 0x5 +#define CAUSE_MISALIGNED_STORE 0x6 +#define CAUSE_STORE_ACCESS 0x7 +#define CAUSE_USER_ECALL 0x8 +#define CAUSE_SUPERVISOR_ECALL 0x9 +#define CAUSE_HYPERVISOR_ECALL 0xa +#define CAUSE_MACHINE_ECALL 0xb +#define CAUSE_FETCH_PAGE_FAULT 0xc +#define CAUSE_LOAD_PAGE_FAULT 0xd +#define CAUSE_STORE_PAGE_FAULT 0xf +#endif +#ifdef DECLARE_INSN +DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ) +DECLARE_INSN(bne, MATCH_BNE, MASK_BNE) +DECLARE_INSN(blt, MATCH_BLT, MASK_BLT) +DECLARE_INSN(bge, MATCH_BGE, MASK_BGE) +DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU) +DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU) +DECLARE_INSN(jalr, MATCH_JALR, MASK_JALR) +DECLARE_INSN(jal, MATCH_JAL, MASK_JAL) +DECLARE_INSN(lui, MATCH_LUI, MASK_LUI) +DECLARE_INSN(auipc, MATCH_AUIPC, MASK_AUIPC) +DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI) +DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI) +DECLARE_INSN(slti, MATCH_SLTI, MASK_SLTI) +DECLARE_INSN(sltiu, MATCH_SLTIU, MASK_SLTIU) +DECLARE_INSN(xori, MATCH_XORI, MASK_XORI) +DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI) +DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI) +DECLARE_INSN(ori, MATCH_ORI, MASK_ORI) +DECLARE_INSN(andi, MATCH_ANDI, MASK_ANDI) +DECLARE_INSN(add, MATCH_ADD, MASK_ADD) +DECLARE_INSN(sub, MATCH_SUB, MASK_SUB) +DECLARE_INSN(sll, MATCH_SLL, MASK_SLL) +DECLARE_INSN(slt, MATCH_SLT, MASK_SLT) +DECLARE_INSN(sltu, MATCH_SLTU, MASK_SLTU) +DECLARE_INSN(xor, MATCH_XOR, MASK_XOR) +DECLARE_INSN(srl, MATCH_SRL, MASK_SRL) +DECLARE_INSN(sra, MATCH_SRA, MASK_SRA) +DECLARE_INSN(or, MATCH_OR, MASK_OR) +DECLARE_INSN(and, MATCH_AND, MASK_AND) +DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW) +DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW) +DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW) +DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW) +DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW) +DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW) +DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW) +DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW) +DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW) +DECLARE_INSN(lb, MATCH_LB, MASK_LB) +DECLARE_INSN(lh, MATCH_LH, MASK_LH) +DECLARE_INSN(lw, MATCH_LW, MASK_LW) +DECLARE_INSN(ld, MATCH_LD, MASK_LD) +DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU) +DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU) +DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU) +DECLARE_INSN(sb, MATCH_SB, MASK_SB) +DECLARE_INSN(sh, MATCH_SH, MASK_SH) +DECLARE_INSN(sw, MATCH_SW, MASK_SW) +DECLARE_INSN(sd, MATCH_SD, MASK_SD) +DECLARE_INSN(fence, MATCH_FENCE, MASK_FENCE) +DECLARE_INSN(fence_i, MATCH_FENCE_I, MASK_FENCE_I) +DECLARE_INSN(mul, MATCH_MUL, MASK_MUL) +DECLARE_INSN(mulh, MATCH_MULH, MASK_MULH) +DECLARE_INSN(mulhsu, MATCH_MULHSU, MASK_MULHSU) +DECLARE_INSN(mulhu, MATCH_MULHU, MASK_MULHU) +DECLARE_INSN(div, MATCH_DIV, MASK_DIV) +DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU) +DECLARE_INSN(rem, MATCH_REM, MASK_REM) +DECLARE_INSN(remu, MATCH_REMU, MASK_REMU) +DECLARE_INSN(mulw, MATCH_MULW, MASK_MULW) +DECLARE_INSN(divw, MATCH_DIVW, MASK_DIVW) +DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW) +DECLARE_INSN(remw, MATCH_REMW, MASK_REMW) +DECLARE_INSN(remuw, MATCH_REMUW, MASK_REMUW) +DECLARE_INSN(amoadd_w, MATCH_AMOADD_W, MASK_AMOADD_W) +DECLARE_INSN(amoxor_w, MATCH_AMOXOR_W, MASK_AMOXOR_W) +DECLARE_INSN(amoor_w, MATCH_AMOOR_W, MASK_AMOOR_W) +DECLARE_INSN(amoand_w, MATCH_AMOAND_W, MASK_AMOAND_W) +DECLARE_INSN(amomin_w, MATCH_AMOMIN_W, MASK_AMOMIN_W) +DECLARE_INSN(amomax_w, MATCH_AMOMAX_W, MASK_AMOMAX_W) +DECLARE_INSN(amominu_w, MATCH_AMOMINU_W, MASK_AMOMINU_W) +DECLARE_INSN(amomaxu_w, MATCH_AMOMAXU_W, MASK_AMOMAXU_W) +DECLARE_INSN(amoswap_w, MATCH_AMOSWAP_W, MASK_AMOSWAP_W) +DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W) +DECLARE_INSN(sc_w, MATCH_SC_W, MASK_SC_W) +DECLARE_INSN(amoadd_d, MATCH_AMOADD_D, MASK_AMOADD_D) +DECLARE_INSN(amoxor_d, MATCH_AMOXOR_D, MASK_AMOXOR_D) +DECLARE_INSN(amoor_d, MATCH_AMOOR_D, MASK_AMOOR_D) +DECLARE_INSN(amoand_d, MATCH_AMOAND_D, MASK_AMOAND_D) +DECLARE_INSN(amomin_d, MATCH_AMOMIN_D, MASK_AMOMIN_D) +DECLARE_INSN(amomax_d, MATCH_AMOMAX_D, MASK_AMOMAX_D) +DECLARE_INSN(amominu_d, MATCH_AMOMINU_D, MASK_AMOMINU_D) +DECLARE_INSN(amomaxu_d, MATCH_AMOMAXU_D, MASK_AMOMAXU_D) +DECLARE_INSN(amoswap_d, MATCH_AMOSWAP_D, MASK_AMOSWAP_D) +DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D) +DECLARE_INSN(sc_d, MATCH_SC_D, MASK_SC_D) +DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL) +DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK) +DECLARE_INSN(uret, MATCH_URET, MASK_URET) +DECLARE_INSN(sret, MATCH_SRET, MASK_SRET) +DECLARE_INSN(mret, MATCH_MRET, MASK_MRET) +DECLARE_INSN(dret, MATCH_DRET, MASK_DRET) +DECLARE_INSN(sfence_vma, MATCH_SFENCE_VMA, MASK_SFENCE_VMA) +DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI) +DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW) +DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS) +DECLARE_INSN(csrrc, MATCH_CSRRC, MASK_CSRRC) +DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI) +DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI) +DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI) +DECLARE_INSN(fadd_s, MATCH_FADD_S, MASK_FADD_S) +DECLARE_INSN(fsub_s, MATCH_FSUB_S, MASK_FSUB_S) +DECLARE_INSN(fmul_s, MATCH_FMUL_S, MASK_FMUL_S) +DECLARE_INSN(fdiv_s, MATCH_FDIV_S, MASK_FDIV_S) +DECLARE_INSN(fsgnj_s, MATCH_FSGNJ_S, MASK_FSGNJ_S) +DECLARE_INSN(fsgnjn_s, MATCH_FSGNJN_S, MASK_FSGNJN_S) +DECLARE_INSN(fsgnjx_s, MATCH_FSGNJX_S, MASK_FSGNJX_S) +DECLARE_INSN(fmin_s, MATCH_FMIN_S, MASK_FMIN_S) +DECLARE_INSN(fmax_s, MATCH_FMAX_S, MASK_FMAX_S) +DECLARE_INSN(fsqrt_s, MATCH_FSQRT_S, MASK_FSQRT_S) +DECLARE_INSN(fadd_d, MATCH_FADD_D, MASK_FADD_D) +DECLARE_INSN(fsub_d, MATCH_FSUB_D, MASK_FSUB_D) +DECLARE_INSN(fmul_d, MATCH_FMUL_D, MASK_FMUL_D) +DECLARE_INSN(fdiv_d, MATCH_FDIV_D, MASK_FDIV_D) +DECLARE_INSN(fsgnj_d, MATCH_FSGNJ_D, MASK_FSGNJ_D) +DECLARE_INSN(fsgnjn_d, MATCH_FSGNJN_D, MASK_FSGNJN_D) +DECLARE_INSN(fsgnjx_d, MATCH_FSGNJX_D, MASK_FSGNJX_D) +DECLARE_INSN(fmin_d, MATCH_FMIN_D, MASK_FMIN_D) +DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D) +DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D) +DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S) +DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D) +DECLARE_INSN(fadd_q, MATCH_FADD_Q, MASK_FADD_Q) +DECLARE_INSN(fsub_q, MATCH_FSUB_Q, MASK_FSUB_Q) +DECLARE_INSN(fmul_q, MATCH_FMUL_Q, MASK_FMUL_Q) +DECLARE_INSN(fdiv_q, MATCH_FDIV_Q, MASK_FDIV_Q) +DECLARE_INSN(fsgnj_q, MATCH_FSGNJ_Q, MASK_FSGNJ_Q) +DECLARE_INSN(fsgnjn_q, MATCH_FSGNJN_Q, MASK_FSGNJN_Q) +DECLARE_INSN(fsgnjx_q, MATCH_FSGNJX_Q, MASK_FSGNJX_Q) +DECLARE_INSN(fmin_q, MATCH_FMIN_Q, MASK_FMIN_Q) +DECLARE_INSN(fmax_q, MATCH_FMAX_Q, MASK_FMAX_Q) +DECLARE_INSN(fcvt_s_q, MATCH_FCVT_S_Q, MASK_FCVT_S_Q) +DECLARE_INSN(fcvt_q_s, MATCH_FCVT_Q_S, MASK_FCVT_Q_S) +DECLARE_INSN(fcvt_d_q, MATCH_FCVT_D_Q, MASK_FCVT_D_Q) +DECLARE_INSN(fcvt_q_d, MATCH_FCVT_Q_D, MASK_FCVT_Q_D) +DECLARE_INSN(fsqrt_q, MATCH_FSQRT_Q, MASK_FSQRT_Q) +DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S) +DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S) +DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S) +DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D) +DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D) +DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D) +DECLARE_INSN(fle_q, MATCH_FLE_Q, MASK_FLE_Q) +DECLARE_INSN(flt_q, MATCH_FLT_Q, MASK_FLT_Q) +DECLARE_INSN(feq_q, MATCH_FEQ_Q, MASK_FEQ_Q) +DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S) +DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S) +DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S) +DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S) +DECLARE_INSN(fmv_x_w, MATCH_FMV_X_W, MASK_FMV_X_W) +DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S) +DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D) +DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D) +DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D) +DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D) +DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D) +DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D) +DECLARE_INSN(fcvt_w_q, MATCH_FCVT_W_Q, MASK_FCVT_W_Q) +DECLARE_INSN(fcvt_wu_q, MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q) +DECLARE_INSN(fcvt_l_q, MATCH_FCVT_L_Q, MASK_FCVT_L_Q) +DECLARE_INSN(fcvt_lu_q, MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q) +DECLARE_INSN(fmv_x_q, MATCH_FMV_X_Q, MASK_FMV_X_Q) +DECLARE_INSN(fclass_q, MATCH_FCLASS_Q, MASK_FCLASS_Q) +DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W) +DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU) +DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L) +DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU) +DECLARE_INSN(fmv_w_x, MATCH_FMV_W_X, MASK_FMV_W_X) +DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W) +DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU) +DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L) +DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU) +DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X) +DECLARE_INSN(fcvt_q_w, MATCH_FCVT_Q_W, MASK_FCVT_Q_W) +DECLARE_INSN(fcvt_q_wu, MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU) +DECLARE_INSN(fcvt_q_l, MATCH_FCVT_Q_L, MASK_FCVT_Q_L) +DECLARE_INSN(fcvt_q_lu, MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU) +DECLARE_INSN(fmv_q_x, MATCH_FMV_Q_X, MASK_FMV_Q_X) +DECLARE_INSN(flw, MATCH_FLW, MASK_FLW) +DECLARE_INSN(fld, MATCH_FLD, MASK_FLD) +DECLARE_INSN(flq, MATCH_FLQ, MASK_FLQ) +DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW) +DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD) +DECLARE_INSN(fsq, MATCH_FSQ, MASK_FSQ) +DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S) +DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S) +DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S) +DECLARE_INSN(fnmadd_s, MATCH_FNMADD_S, MASK_FNMADD_S) +DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D) +DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D) +DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D) +DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D) +DECLARE_INSN(fmadd_q, MATCH_FMADD_Q, MASK_FMADD_Q) +DECLARE_INSN(fmsub_q, MATCH_FMSUB_Q, MASK_FMSUB_Q) +DECLARE_INSN(fnmsub_q, MATCH_FNMSUB_Q, MASK_FNMSUB_Q) +DECLARE_INSN(fnmadd_q, MATCH_FNMADD_Q, MASK_FNMADD_Q) +DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP) +DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP) +DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR) +DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR) +DECLARE_INSN(c_ebreak, MATCH_C_EBREAK, MASK_C_EBREAK) +DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD) +DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD) +DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW) +DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP) +DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP) +DECLARE_INSN(c_addi4spn, MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN) +DECLARE_INSN(c_fld, MATCH_C_FLD, MASK_C_FLD) +DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW) +DECLARE_INSN(c_flw, MATCH_C_FLW, MASK_C_FLW) +DECLARE_INSN(c_fsd, MATCH_C_FSD, MASK_C_FSD) +DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW) +DECLARE_INSN(c_fsw, MATCH_C_FSW, MASK_C_FSW) +DECLARE_INSN(c_addi, MATCH_C_ADDI, MASK_C_ADDI) +DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL) +DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI) +DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI) +DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI) +DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI) +DECLARE_INSN(c_andi, MATCH_C_ANDI, MASK_C_ANDI) +DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB) +DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR) +DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR) +DECLARE_INSN(c_and, MATCH_C_AND, MASK_C_AND) +DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW) +DECLARE_INSN(c_addw, MATCH_C_ADDW, MASK_C_ADDW) +DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J) +DECLARE_INSN(c_beqz, MATCH_C_BEQZ, MASK_C_BEQZ) +DECLARE_INSN(c_bnez, MATCH_C_BNEZ, MASK_C_BNEZ) +DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI) +DECLARE_INSN(c_fldsp, MATCH_C_FLDSP, MASK_C_FLDSP) +DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP) +DECLARE_INSN(c_flwsp, MATCH_C_FLWSP, MASK_C_FLWSP) +DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV) +DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD) +DECLARE_INSN(c_fsdsp, MATCH_C_FSDSP, MASK_C_FSDSP) +DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP) +DECLARE_INSN(c_fswsp, MATCH_C_FSWSP, MASK_C_FSWSP) +DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0) +DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1) +DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2) +DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD) +DECLARE_INSN(custom0_rd_rs1, MATCH_CUSTOM0_RD_RS1, MASK_CUSTOM0_RD_RS1) +DECLARE_INSN(custom0_rd_rs1_rs2, MATCH_CUSTOM0_RD_RS1_RS2, MASK_CUSTOM0_RD_RS1_RS2) +DECLARE_INSN(custom1, MATCH_CUSTOM1, MASK_CUSTOM1) +DECLARE_INSN(custom1_rs1, MATCH_CUSTOM1_RS1, MASK_CUSTOM1_RS1) +DECLARE_INSN(custom1_rs1_rs2, MATCH_CUSTOM1_RS1_RS2, MASK_CUSTOM1_RS1_RS2) +DECLARE_INSN(custom1_rd, MATCH_CUSTOM1_RD, MASK_CUSTOM1_RD) +DECLARE_INSN(custom1_rd_rs1, MATCH_CUSTOM1_RD_RS1, MASK_CUSTOM1_RD_RS1) +DECLARE_INSN(custom1_rd_rs1_rs2, MATCH_CUSTOM1_RD_RS1_RS2, MASK_CUSTOM1_RD_RS1_RS2) +DECLARE_INSN(custom2, MATCH_CUSTOM2, MASK_CUSTOM2) +DECLARE_INSN(custom2_rs1, MATCH_CUSTOM2_RS1, MASK_CUSTOM2_RS1) +DECLARE_INSN(custom2_rs1_rs2, MATCH_CUSTOM2_RS1_RS2, MASK_CUSTOM2_RS1_RS2) +DECLARE_INSN(custom2_rd, MATCH_CUSTOM2_RD, MASK_CUSTOM2_RD) +DECLARE_INSN(custom2_rd_rs1, MATCH_CUSTOM2_RD_RS1, MASK_CUSTOM2_RD_RS1) +DECLARE_INSN(custom2_rd_rs1_rs2, MATCH_CUSTOM2_RD_RS1_RS2, MASK_CUSTOM2_RD_RS1_RS2) +DECLARE_INSN(custom3, MATCH_CUSTOM3, MASK_CUSTOM3) +DECLARE_INSN(custom3_rs1, MATCH_CUSTOM3_RS1, MASK_CUSTOM3_RS1) +DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2) +DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD) +DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1) +DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2) +#endif +#ifdef DECLARE_CSR +DECLARE_CSR(fflags, CSR_FFLAGS) +DECLARE_CSR(frm, CSR_FRM) +DECLARE_CSR(fcsr, CSR_FCSR) +DECLARE_CSR(cycle, CSR_CYCLE) +DECLARE_CSR(time, CSR_TIME) +DECLARE_CSR(instret, CSR_INSTRET) +DECLARE_CSR(hpmcounter3, CSR_HPMCOUNTER3) +DECLARE_CSR(hpmcounter4, CSR_HPMCOUNTER4) +DECLARE_CSR(hpmcounter5, CSR_HPMCOUNTER5) +DECLARE_CSR(hpmcounter6, CSR_HPMCOUNTER6) +DECLARE_CSR(hpmcounter7, CSR_HPMCOUNTER7) +DECLARE_CSR(hpmcounter8, CSR_HPMCOUNTER8) +DECLARE_CSR(hpmcounter9, CSR_HPMCOUNTER9) +DECLARE_CSR(hpmcounter10, CSR_HPMCOUNTER10) +DECLARE_CSR(hpmcounter11, CSR_HPMCOUNTER11) +DECLARE_CSR(hpmcounter12, CSR_HPMCOUNTER12) +DECLARE_CSR(hpmcounter13, CSR_HPMCOUNTER13) +DECLARE_CSR(hpmcounter14, CSR_HPMCOUNTER14) +DECLARE_CSR(hpmcounter15, CSR_HPMCOUNTER15) +DECLARE_CSR(hpmcounter16, CSR_HPMCOUNTER16) +DECLARE_CSR(hpmcounter17, CSR_HPMCOUNTER17) +DECLARE_CSR(hpmcounter18, CSR_HPMCOUNTER18) +DECLARE_CSR(hpmcounter19, CSR_HPMCOUNTER19) +DECLARE_CSR(hpmcounter20, CSR_HPMCOUNTER20) +DECLARE_CSR(hpmcounter21, CSR_HPMCOUNTER21) +DECLARE_CSR(hpmcounter22, CSR_HPMCOUNTER22) +DECLARE_CSR(hpmcounter23, CSR_HPMCOUNTER23) +DECLARE_CSR(hpmcounter24, CSR_HPMCOUNTER24) +DECLARE_CSR(hpmcounter25, CSR_HPMCOUNTER25) +DECLARE_CSR(hpmcounter26, CSR_HPMCOUNTER26) +DECLARE_CSR(hpmcounter27, CSR_HPMCOUNTER27) +DECLARE_CSR(hpmcounter28, CSR_HPMCOUNTER28) +DECLARE_CSR(hpmcounter29, CSR_HPMCOUNTER29) +DECLARE_CSR(hpmcounter30, CSR_HPMCOUNTER30) +DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31) +DECLARE_CSR(sstatus, CSR_SSTATUS) +DECLARE_CSR(sie, CSR_SIE) +DECLARE_CSR(stvec, CSR_STVEC) +DECLARE_CSR(scounteren, CSR_SCOUNTEREN) +DECLARE_CSR(sscratch, CSR_SSCRATCH) +DECLARE_CSR(sepc, CSR_SEPC) +DECLARE_CSR(scause, CSR_SCAUSE) +DECLARE_CSR(stval, CSR_STVAL) +DECLARE_CSR(sip, CSR_SIP) +DECLARE_CSR(satp, CSR_SATP) +DECLARE_CSR(mstatus, CSR_MSTATUS) +DECLARE_CSR(misa, CSR_MISA) +DECLARE_CSR(medeleg, CSR_MEDELEG) +DECLARE_CSR(mideleg, CSR_MIDELEG) +DECLARE_CSR(mie, CSR_MIE) +DECLARE_CSR(mtvec, CSR_MTVEC) +DECLARE_CSR(mcounteren, CSR_MCOUNTEREN) +DECLARE_CSR(mscratch, CSR_MSCRATCH) +DECLARE_CSR(mepc, CSR_MEPC) +DECLARE_CSR(mcause, CSR_MCAUSE) +DECLARE_CSR(mtval, CSR_MTVAL) +DECLARE_CSR(mip, CSR_MIP) +DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) +DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) +DECLARE_CSR(pmpcfg2, CSR_PMPCFG2) +DECLARE_CSR(pmpcfg3, CSR_PMPCFG3) +DECLARE_CSR(pmpaddr0, CSR_PMPADDR0) +DECLARE_CSR(pmpaddr1, CSR_PMPADDR1) +DECLARE_CSR(pmpaddr2, CSR_PMPADDR2) +DECLARE_CSR(pmpaddr3, CSR_PMPADDR3) +DECLARE_CSR(pmpaddr4, CSR_PMPADDR4) +DECLARE_CSR(pmpaddr5, CSR_PMPADDR5) +DECLARE_CSR(pmpaddr6, CSR_PMPADDR6) +DECLARE_CSR(pmpaddr7, CSR_PMPADDR7) +DECLARE_CSR(pmpaddr8, CSR_PMPADDR8) +DECLARE_CSR(pmpaddr9, CSR_PMPADDR9) +DECLARE_CSR(pmpaddr10, CSR_PMPADDR10) +DECLARE_CSR(pmpaddr11, CSR_PMPADDR11) +DECLARE_CSR(pmpaddr12, CSR_PMPADDR12) +DECLARE_CSR(pmpaddr13, CSR_PMPADDR13) +DECLARE_CSR(pmpaddr14, CSR_PMPADDR14) +DECLARE_CSR(pmpaddr15, CSR_PMPADDR15) +DECLARE_CSR(tselect, CSR_TSELECT) +DECLARE_CSR(tdata1, CSR_TDATA1) +DECLARE_CSR(tdata2, CSR_TDATA2) +DECLARE_CSR(tdata3, CSR_TDATA3) +DECLARE_CSR(dcsr, CSR_DCSR) +DECLARE_CSR(dpc, CSR_DPC) +DECLARE_CSR(dscratch, CSR_DSCRATCH) +DECLARE_CSR(mcycle, CSR_MCYCLE) +DECLARE_CSR(minstret, CSR_MINSTRET) +DECLARE_CSR(mhpmcounter3, CSR_MHPMCOUNTER3) +DECLARE_CSR(mhpmcounter4, CSR_MHPMCOUNTER4) +DECLARE_CSR(mhpmcounter5, CSR_MHPMCOUNTER5) +DECLARE_CSR(mhpmcounter6, CSR_MHPMCOUNTER6) +DECLARE_CSR(mhpmcounter7, CSR_MHPMCOUNTER7) +DECLARE_CSR(mhpmcounter8, CSR_MHPMCOUNTER8) +DECLARE_CSR(mhpmcounter9, CSR_MHPMCOUNTER9) +DECLARE_CSR(mhpmcounter10, CSR_MHPMCOUNTER10) +DECLARE_CSR(mhpmcounter11, CSR_MHPMCOUNTER11) +DECLARE_CSR(mhpmcounter12, CSR_MHPMCOUNTER12) +DECLARE_CSR(mhpmcounter13, CSR_MHPMCOUNTER13) +DECLARE_CSR(mhpmcounter14, CSR_MHPMCOUNTER14) +DECLARE_CSR(mhpmcounter15, CSR_MHPMCOUNTER15) +DECLARE_CSR(mhpmcounter16, CSR_MHPMCOUNTER16) +DECLARE_CSR(mhpmcounter17, CSR_MHPMCOUNTER17) +DECLARE_CSR(mhpmcounter18, CSR_MHPMCOUNTER18) +DECLARE_CSR(mhpmcounter19, CSR_MHPMCOUNTER19) +DECLARE_CSR(mhpmcounter20, CSR_MHPMCOUNTER20) +DECLARE_CSR(mhpmcounter21, CSR_MHPMCOUNTER21) +DECLARE_CSR(mhpmcounter22, CSR_MHPMCOUNTER22) +DECLARE_CSR(mhpmcounter23, CSR_MHPMCOUNTER23) +DECLARE_CSR(mhpmcounter24, CSR_MHPMCOUNTER24) +DECLARE_CSR(mhpmcounter25, CSR_MHPMCOUNTER25) +DECLARE_CSR(mhpmcounter26, CSR_MHPMCOUNTER26) +DECLARE_CSR(mhpmcounter27, CSR_MHPMCOUNTER27) +DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28) +DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29) +DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30) +DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31) +DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3) +DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4) +DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5) +DECLARE_CSR(mhpmevent6, CSR_MHPMEVENT6) +DECLARE_CSR(mhpmevent7, CSR_MHPMEVENT7) +DECLARE_CSR(mhpmevent8, CSR_MHPMEVENT8) +DECLARE_CSR(mhpmevent9, CSR_MHPMEVENT9) +DECLARE_CSR(mhpmevent10, CSR_MHPMEVENT10) +DECLARE_CSR(mhpmevent11, CSR_MHPMEVENT11) +DECLARE_CSR(mhpmevent12, CSR_MHPMEVENT12) +DECLARE_CSR(mhpmevent13, CSR_MHPMEVENT13) +DECLARE_CSR(mhpmevent14, CSR_MHPMEVENT14) +DECLARE_CSR(mhpmevent15, CSR_MHPMEVENT15) +DECLARE_CSR(mhpmevent16, CSR_MHPMEVENT16) +DECLARE_CSR(mhpmevent17, CSR_MHPMEVENT17) +DECLARE_CSR(mhpmevent18, CSR_MHPMEVENT18) +DECLARE_CSR(mhpmevent19, CSR_MHPMEVENT19) +DECLARE_CSR(mhpmevent20, CSR_MHPMEVENT20) +DECLARE_CSR(mhpmevent21, CSR_MHPMEVENT21) +DECLARE_CSR(mhpmevent22, CSR_MHPMEVENT22) +DECLARE_CSR(mhpmevent23, CSR_MHPMEVENT23) +DECLARE_CSR(mhpmevent24, CSR_MHPMEVENT24) +DECLARE_CSR(mhpmevent25, CSR_MHPMEVENT25) +DECLARE_CSR(mhpmevent26, CSR_MHPMEVENT26) +DECLARE_CSR(mhpmevent27, CSR_MHPMEVENT27) +DECLARE_CSR(mhpmevent28, CSR_MHPMEVENT28) +DECLARE_CSR(mhpmevent29, CSR_MHPMEVENT29) +DECLARE_CSR(mhpmevent30, CSR_MHPMEVENT30) +DECLARE_CSR(mhpmevent31, CSR_MHPMEVENT31) +DECLARE_CSR(mvendorid, CSR_MVENDORID) +DECLARE_CSR(marchid, CSR_MARCHID) +DECLARE_CSR(mimpid, CSR_MIMPID) +DECLARE_CSR(mhartid, CSR_MHARTID) +DECLARE_CSR(cycleh, CSR_CYCLEH) +DECLARE_CSR(timeh, CSR_TIMEH) +DECLARE_CSR(instreth, CSR_INSTRETH) +DECLARE_CSR(hpmcounter3h, CSR_HPMCOUNTER3H) +DECLARE_CSR(hpmcounter4h, CSR_HPMCOUNTER4H) +DECLARE_CSR(hpmcounter5h, CSR_HPMCOUNTER5H) +DECLARE_CSR(hpmcounter6h, CSR_HPMCOUNTER6H) +DECLARE_CSR(hpmcounter7h, CSR_HPMCOUNTER7H) +DECLARE_CSR(hpmcounter8h, CSR_HPMCOUNTER8H) +DECLARE_CSR(hpmcounter9h, CSR_HPMCOUNTER9H) +DECLARE_CSR(hpmcounter10h, CSR_HPMCOUNTER10H) +DECLARE_CSR(hpmcounter11h, CSR_HPMCOUNTER11H) +DECLARE_CSR(hpmcounter12h, CSR_HPMCOUNTER12H) +DECLARE_CSR(hpmcounter13h, CSR_HPMCOUNTER13H) +DECLARE_CSR(hpmcounter14h, CSR_HPMCOUNTER14H) +DECLARE_CSR(hpmcounter15h, CSR_HPMCOUNTER15H) +DECLARE_CSR(hpmcounter16h, CSR_HPMCOUNTER16H) +DECLARE_CSR(hpmcounter17h, CSR_HPMCOUNTER17H) +DECLARE_CSR(hpmcounter18h, CSR_HPMCOUNTER18H) +DECLARE_CSR(hpmcounter19h, CSR_HPMCOUNTER19H) +DECLARE_CSR(hpmcounter20h, CSR_HPMCOUNTER20H) +DECLARE_CSR(hpmcounter21h, CSR_HPMCOUNTER21H) +DECLARE_CSR(hpmcounter22h, CSR_HPMCOUNTER22H) +DECLARE_CSR(hpmcounter23h, CSR_HPMCOUNTER23H) +DECLARE_CSR(hpmcounter24h, CSR_HPMCOUNTER24H) +DECLARE_CSR(hpmcounter25h, CSR_HPMCOUNTER25H) +DECLARE_CSR(hpmcounter26h, CSR_HPMCOUNTER26H) +DECLARE_CSR(hpmcounter27h, CSR_HPMCOUNTER27H) +DECLARE_CSR(hpmcounter28h, CSR_HPMCOUNTER28H) +DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H) +DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H) +DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H) +DECLARE_CSR(mcycleh, CSR_MCYCLEH) +DECLARE_CSR(minstreth, CSR_MINSTRETH) +DECLARE_CSR(mhpmcounter3h, CSR_MHPMCOUNTER3H) +DECLARE_CSR(mhpmcounter4h, CSR_MHPMCOUNTER4H) +DECLARE_CSR(mhpmcounter5h, CSR_MHPMCOUNTER5H) +DECLARE_CSR(mhpmcounter6h, CSR_MHPMCOUNTER6H) +DECLARE_CSR(mhpmcounter7h, CSR_MHPMCOUNTER7H) +DECLARE_CSR(mhpmcounter8h, CSR_MHPMCOUNTER8H) +DECLARE_CSR(mhpmcounter9h, CSR_MHPMCOUNTER9H) +DECLARE_CSR(mhpmcounter10h, CSR_MHPMCOUNTER10H) +DECLARE_CSR(mhpmcounter11h, CSR_MHPMCOUNTER11H) +DECLARE_CSR(mhpmcounter12h, CSR_MHPMCOUNTER12H) +DECLARE_CSR(mhpmcounter13h, CSR_MHPMCOUNTER13H) +DECLARE_CSR(mhpmcounter14h, CSR_MHPMCOUNTER14H) +DECLARE_CSR(mhpmcounter15h, CSR_MHPMCOUNTER15H) +DECLARE_CSR(mhpmcounter16h, CSR_MHPMCOUNTER16H) +DECLARE_CSR(mhpmcounter17h, CSR_MHPMCOUNTER17H) +DECLARE_CSR(mhpmcounter18h, CSR_MHPMCOUNTER18H) +DECLARE_CSR(mhpmcounter19h, CSR_MHPMCOUNTER19H) +DECLARE_CSR(mhpmcounter20h, CSR_MHPMCOUNTER20H) +DECLARE_CSR(mhpmcounter21h, CSR_MHPMCOUNTER21H) +DECLARE_CSR(mhpmcounter22h, CSR_MHPMCOUNTER22H) +DECLARE_CSR(mhpmcounter23h, CSR_MHPMCOUNTER23H) +DECLARE_CSR(mhpmcounter24h, CSR_MHPMCOUNTER24H) +DECLARE_CSR(mhpmcounter25h, CSR_MHPMCOUNTER25H) +DECLARE_CSR(mhpmcounter26h, CSR_MHPMCOUNTER26H) +DECLARE_CSR(mhpmcounter27h, CSR_MHPMCOUNTER27H) +DECLARE_CSR(mhpmcounter28h, CSR_MHPMCOUNTER28H) +DECLARE_CSR(mhpmcounter29h, CSR_MHPMCOUNTER29H) +DECLARE_CSR(mhpmcounter30h, CSR_MHPMCOUNTER30H) +DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H) +#endif +#ifdef DECLARE_CAUSE +DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH) +DECLARE_CAUSE("fetch access", CAUSE_FETCH_ACCESS) +DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION) +DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT) +DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD) +DECLARE_CAUSE("load access", CAUSE_LOAD_ACCESS) +DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE) +DECLARE_CAUSE("store access", CAUSE_STORE_ACCESS) +DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL) +DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL) +DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL) +DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL) +DECLARE_CAUSE("fetch page fault", CAUSE_FETCH_PAGE_FAULT) +DECLARE_CAUSE("load page fault", CAUSE_LOAD_PAGE_FAULT) +DECLARE_CAUSE("store page fault", CAUSE_STORE_PAGE_FAULT) +#endif diff --git a/lab8/libs/sbi.h b/lab8/libs/sbi.h new file mode 100644 index 0000000..b61d018 --- /dev/null +++ b/lab8/libs/sbi.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 Regents of the University of California + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SBI_H__ +#define __SBI_H__ + +#include + +#define SBI_SET_TIMER 0 +#define SBI_CONSOLE_PUTCHAR 1 +#define SBI_CONSOLE_GETCHAR 2 +#define SBI_CLEAR_IPI 3 +#define SBI_SEND_IPI 4 +#define SBI_REMOTE_FENCE_I 5 +#define SBI_REMOTE_SFENCE_VMA 6 +#define SBI_REMOTE_SFENCE_VMA_ASID 7 +#define SBI_SHUTDOWN 8 +#define SBI_REGISTER_DEVINTR 9 + +#define SBI_CALL(which, arg0, arg1, arg2) ({ \ + register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \ + register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \ + register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \ + register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \ + asm volatile ("ecall" \ + : "+r" (a0) \ + : "r" (a1), "r" (a2), "r" (a7) \ + : "memory"); \ + a0; \ +}) + +/* Lazy implementations until SBI is finalized */ +#define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0) +#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0) +#define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0) + +static inline void sbi_console_putchar(int ch) +{ + SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch); +} + +static inline int sbi_console_getchar(void) +{ + return SBI_CALL_0(SBI_CONSOLE_GETCHAR); +} + +static inline void sbi_set_timer(uint64_t stime_value) +{ +#if __riscv_xlen == 32 + SBI_CALL_2(SBI_SET_TIMER, stime_value, stime_value >> 32); +#else + SBI_CALL_1(SBI_SET_TIMER, stime_value); +#endif +} + +static inline void sbi_shutdown(void) +{ + SBI_CALL_0(SBI_SHUTDOWN); +} + +static inline void sbi_clear_ipi(void) +{ + SBI_CALL_0(SBI_CLEAR_IPI); +} + +static inline void sbi_send_ipi(const unsigned long *hart_mask) +{ + SBI_CALL_1(SBI_SEND_IPI, hart_mask); +} + +static inline void sbi_remote_fence_i(const unsigned long *hart_mask) +{ + SBI_CALL_1(SBI_REMOTE_FENCE_I, hart_mask); +} + +static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask, + unsigned long start, + unsigned long size) +{ + SBI_CALL_1(SBI_REMOTE_SFENCE_VMA, hart_mask); +} + +static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, + unsigned long start, + unsigned long size, + unsigned long asid) +{ + SBI_CALL_1(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask); +} + +static inline void sbi_register_devintr(uintptr_t entry) { + SBI_CALL_1(SBI_REGISTER_DEVINTR, entry); +} + +#endif /* !__SBI_H__ */ diff --git a/lab8/libs/skew_heap.h b/lab8/libs/skew_heap.h new file mode 100644 index 0000000..0c216b8 --- /dev/null +++ b/lab8/libs/skew_heap.h @@ -0,0 +1,87 @@ +#ifndef __LIBS_SKEW_HEAP_H__ +#define __LIBS_SKEW_HEAP_H__ + +struct skew_heap_entry { + struct skew_heap_entry *parent, *left, *right; +}; + +typedef struct skew_heap_entry skew_heap_entry_t; + +typedef int(*compare_f)(void *a, void *b); + +static inline void skew_heap_init(skew_heap_entry_t *a) __attribute__((always_inline)); +static inline skew_heap_entry_t *skew_heap_merge( + skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp); +static inline skew_heap_entry_t *skew_heap_insert( + skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) __attribute__((always_inline)); +static inline skew_heap_entry_t *skew_heap_remove( + skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) __attribute__((always_inline)); + +static inline void +skew_heap_init(skew_heap_entry_t *a) +{ + a->left = a->right = a->parent = NULL; +} + +static inline skew_heap_entry_t * +skew_heap_merge(skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) +{ + if (a == NULL) return b; + else if (b == NULL) return a; + + skew_heap_entry_t *l, *r; + if (comp(a, b) == -1) + { + r = a->left; + l = skew_heap_merge(a->right, b, comp); + + a->left = l; + a->right = r; + if (l) l->parent = a; + + return a; + } + else + { + r = b->left; + l = skew_heap_merge(a, b->right, comp); + + b->left = l; + b->right = r; + if (l) l->parent = b; + + return b; + } +} + +static inline skew_heap_entry_t * +skew_heap_insert(skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) +{ + skew_heap_init(b); + return skew_heap_merge(a, b, comp); +} + +static inline skew_heap_entry_t * +skew_heap_remove(skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) +{ + skew_heap_entry_t *p = b->parent; + skew_heap_entry_t *rep = skew_heap_merge(b->left, b->right, comp); + if (rep) rep->parent = p; + + if (p) + { + if (p->left == b) + p->left = rep; + else p->right = rep; + return a; + } + else return rep; +} + +#endif /* !__LIBS_SKEW_HEAP_H__ */ diff --git a/lab8/libs/stat.h b/lab8/libs/stat.h new file mode 100644 index 0000000..41a392d --- /dev/null +++ b/lab8/libs/stat.h @@ -0,0 +1,27 @@ +#ifndef __LIBS_STAT_H__ +#define __LIBS_STAT_H__ + +#include + +struct stat { + uint32_t st_mode; // protection mode and file type + size_t st_nlinks; // number of hard links + size_t st_blocks; // number of blocks file is using + size_t st_size; // file size (bytes) +}; + +#define S_IFMT 070000 // mask for type of file +#define S_IFREG 010000 // ordinary regular file +#define S_IFDIR 020000 // directory +#define S_IFLNK 030000 // symbolic link +#define S_IFCHR 040000 // character device +#define S_IFBLK 050000 // block device + +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) // regular file +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) // directory +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) // symlink +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) // char device +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) // block device + +#endif /* !__LIBS_STAT_H__ */ + diff --git a/lab8/libs/stdarg.h b/lab8/libs/stdarg.h new file mode 100644 index 0000000..f6e0758 --- /dev/null +++ b/lab8/libs/stdarg.h @@ -0,0 +1,12 @@ +#ifndef __LIBS_STDARG_H__ +#define __LIBS_STDARG_H__ + +/* compiler provides size of save area */ +typedef __builtin_va_list va_list; + +#define va_start(ap, last) (__builtin_va_start(ap, last)) +#define va_arg(ap, type) (__builtin_va_arg(ap, type)) +#define va_end(ap) /*nothing*/ + +#endif /* !__LIBS_STDARG_H__ */ + diff --git a/lab8/libs/stdio.h b/lab8/libs/stdio.h new file mode 100644 index 0000000..389c1a3 --- /dev/null +++ b/lab8/libs/stdio.h @@ -0,0 +1,25 @@ +#ifndef __LIBS_STDIO_H__ +#define __LIBS_STDIO_H__ + +#include +#include + +/* kern/libs/stdio.c */ +int cprintf(const char *fmt, ...); +int vcprintf(const char *fmt, va_list ap); +void cputchar(int c); +int cputs(const char *str); +int getchar(void); +void input_wakeup(); + +/* kern/libs/readline.c */ +char *readline(const char *prompt); + +/* libs/printfmt.c */ +void printfmt(void (*putch)(int, void *, int), int fd, void *putdat, const char *fmt, ...); +void vprintfmt(void (*putch)(int, void *, int), int fd, void *putdat, const char *fmt, va_list ap); +int snprintf(char *str, size_t size, const char *fmt, ...); +int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); + +#endif /* !__LIBS_STDIO_H__ */ + diff --git a/lab8/libs/stdlib.h b/lab8/libs/stdlib.h new file mode 100644 index 0000000..51878ef --- /dev/null +++ b/lab8/libs/stdlib.h @@ -0,0 +1,17 @@ +#ifndef __LIBS_STDLIB_H__ +#define __LIBS_STDLIB_H__ + +#include + +/* the largest number rand will return */ +#define RAND_MAX 2147483647UL + +/* libs/rand.c */ +int rand(void); +void srand(unsigned int seed); + +/* libs/hash.c */ +uint32_t hash32(uint32_t val, unsigned int bits); + +#endif /* !__LIBS_RAND_H__ */ + diff --git a/lab8/libs/string.c b/lab8/libs/string.c new file mode 100644 index 0000000..8a326d1 --- /dev/null +++ b/lab8/libs/string.c @@ -0,0 +1,379 @@ +#include +#include + +/* * + * strlen - calculate the length of the string @s, not including + * the terminating '\0' character. + * @s: the input string + * + * The strlen() function returns the length of string @s. + * */ +size_t +strlen(const char *s) { + size_t cnt = 0; + while (*s ++ != '\0') { + cnt ++; + } + return cnt; +} + +/* * + * strnlen - calculate the length of the string @s, not including + * the terminating '\0' char acter, but at most @len. + * @s: the input string + * @len: the max-length that function will scan + * + * Note that, this function looks only at the first @len characters + * at @s, and never beyond @s + @len. + * + * The return value is strlen(s), if that is less than @len, or + * @len if there is no '\0' character among the first @len characters + * pointed by @s. + * */ +size_t +strnlen(const char *s, size_t len) { + size_t cnt = 0; + while (cnt < len && *s ++ != '\0') { + cnt ++; + } + return cnt; +} + +/* * + * strcpy - copies the string pointed by @src into the array pointed by @dst, + * including the terminating null character. + * @dst: pointer to the destination array where the content is to be copied + * @src: string to be copied + * + * The return value is @dst. + * + * To avoid overflows, the size of array pointed by @dst should be long enough to + * contain the same string as @src (including the terminating null character), and + * should not overlap in memory with @src. + * */ +char * +strcpy(char *dst, const char *src) { +#ifdef __HAVE_ARCH_STRCPY + return __strcpy(dst, src); +#else + char *p = dst; + while ((*p ++ = *src ++) != '\0') + /* nothing */; + return dst; +#endif /* __HAVE_ARCH_STRCPY */ +} + +/* * + * strncpy - copies the first @len characters of @src to @dst. If the end of string @src + * if found before @len characters have been copied, @dst is padded with '\0' until a + * total of @len characters have been written to it. + * @dst: pointer to the destination array where the content is to be copied + * @src: string to be copied + * @len: maximum number of characters to be copied from @src + * + * The return value is @dst + * */ +char * +strncpy(char *dst, const char *src, size_t len) { + char *p = dst; + while (len > 0) { + if ((*p = *src) != '\0') { + src ++; + } + p ++, len --; + } + return dst; +} +/* * + * strcat - appends a copy of the @src string to the @dst string. The terminating null + * character in @dst is overwritten by the first character of @src, and a new null-character + * is appended at the end of the new string formed by the concatenation of both in @dst. + * @dst: pointer to the @dst array, which should be large enough to contain the concatenated + * resulting string. + * @src: string to be appended, this should not overlap @dst + * */ +char * +strcat(char *dst, const char *src) { + return strcpy(dst + strlen(dst), src); +} + +/* * + * strcmp - compares the string @s1 and @s2 + * @s1: string to be compared + * @s2: string to be compared + * + * This function starts comparing the first character of each string. If + * they are equal to each other, it continues with the following pairs until + * the characters differ or until a terminanting null-character is reached. + * + * Returns an integral value indicating the relationship between the strings: + * - A zero value indicates that both strings are equal; + * - A value greater than zero indicates that the first character that does + * not match has a greater value in @s1 than in @s2; + * - And a value less than zero indicates the opposite. + * */ +int +strcmp(const char *s1, const char *s2) { +#ifdef __HAVE_ARCH_STRCMP + return __strcmp(s1, s2); +#else + while (*s1 != '\0' && *s1 == *s2) { + s1 ++, s2 ++; + } + return (int)((unsigned char)*s1 - (unsigned char)*s2); +#endif /* __HAVE_ARCH_STRCMP */ +} + +/* * + * strncmp - compares up to @n characters of the string @s1 to those of the string @s2 + * @s1: string to be compared + * @s2: string to be compared + * @n: maximum number of characters to compare + * + * This function starts comparing the first character of each string. If + * they are equal to each other, it continues with the following pairs until + * the characters differ, until a terminating null-character is reached, or + * until @n characters match in both strings, whichever happens first. + * */ +int +strncmp(const char *s1, const char *s2, size_t n) { + while (n > 0 && *s1 != '\0' && *s1 == *s2) { + n --, s1 ++, s2 ++; + } + return (n == 0) ? 0 : (int)((unsigned char)*s1 - (unsigned char)*s2); +} + +/* * + * strchr - locates first occurrence of character in string + * @s: the input string + * @c: character to be located + * + * The strchr() function returns a pointer to the first occurrence of + * character in @s. If the value is not found, the function returns 'NULL'. + * */ +char * +strchr(const char *s, char c) { + while (*s != '\0') { + if (*s == c) { + return (char *)s; + } + s ++; + } + return NULL; +} + +/* * + * strfind - locates first occurrence of character in string + * @s: the input string + * @c: character to be located + * + * The strfind() function is like strchr() except that if @c is + * not found in @s, then it returns a pointer to the null byte at the + * end of @s, rather than 'NULL'. + * */ +char * +strfind(const char *s, char c) { + while (*s != '\0') { + if (*s == c) { + break; + } + s ++; + } + return (char *)s; +} + +/* * + * strtol - converts string to long integer + * @s: the input string that contains the representation of an integer number + * @endptr: reference to an object of type char *, whose value is set by the + * function to the next character in @s after the numerical value. This + * parameter can also be a null pointer, in which case it is not used. + * @base: x + * + * The function first discards as many whitespace characters as necessary until + * the first non-whitespace character is found. Then, starting from this character, + * takes as many characters as possible that are valid following a syntax that + * depends on the base parameter, and interprets them as a numerical value. Finally, + * a pointer to the first character following the integer representation in @s + * is stored in the object pointed by @endptr. + * + * If the value of base is zero, the syntax expected is similar to that of + * integer constants, which is formed by a succession of: + * - An optional plus or minus sign; + * - An optional prefix indicating octal or hexadecimal base ("0" or "0x" respectively) + * - A sequence of decimal digits (if no base prefix was specified) or either octal + * or hexadecimal digits if a specific prefix is present + * + * If the base value is between 2 and 36, the format expected for the integral number + * is a succession of the valid digits and/or letters needed to represent integers of + * the specified radix (starting from '0' and up to 'z'/'Z' for radix 36). The + * sequence may optionally be preceded by a plus or minus sign and, if base is 16, + * an optional "0x" or "0X" prefix. + * + * The strtol() function returns the converted integral number as a long int value. + * */ +long +strtol(const char *s, char **endptr, int base) { + int neg = 0; + long val = 0; + + // gobble initial whitespace + while (*s == ' ' || *s == '\t') { + s ++; + } + + // plus/minus sign + if (*s == '+') { + s ++; + } + else if (*s == '-') { + s ++, neg = 1; + } + + // hex or octal base prefix + if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) { + s += 2, base = 16; + } + else if (base == 0 && s[0] == '0') { + s ++, base = 8; + } + else if (base == 0) { + base = 10; + } + + // digits + while (1) { + int dig; + + if (*s >= '0' && *s <= '9') { + dig = *s - '0'; + } + else if (*s >= 'a' && *s <= 'z') { + dig = *s - 'a' + 10; + } + else if (*s >= 'A' && *s <= 'Z') { + dig = *s - 'A' + 10; + } + else { + break; + } + if (dig >= base) { + break; + } + s ++, val = (val * base) + dig; + // we don't properly detect overflow! + } + + if (endptr) { + *endptr = (char *) s; + } + return (neg ? -val : val); +} + +/* * + * memset - sets the first @n bytes of the memory area pointed by @s + * to the specified value @c. + * @s: pointer the the memory area to fill + * @c: value to set + * @n: number of bytes to be set to the value + * + * The memset() function returns @s. + * */ +void * +memset(void *s, char c, size_t n) { +#ifdef __HAVE_ARCH_MEMSET + return __memset(s, c, n); +#else + char *p = s; + while (n -- > 0) { + *p ++ = c; + } + return s; +#endif /* __HAVE_ARCH_MEMSET */ +} + +/* * + * memmove - copies the values of @n bytes from the location pointed by @src to + * the memory area pointed by @dst. @src and @dst are allowed to overlap. + * @dst pointer to the destination array where the content is to be copied + * @src pointer to the source of data to by copied + * @n: number of bytes to copy + * + * The memmove() function returns @dst. + * */ +void * +memmove(void *dst, const void *src, size_t n) { +#ifdef __HAVE_ARCH_MEMMOVE + return __memmove(dst, src, n); +#else + const char *s = src; + char *d = dst; + if (s < d && s + n > d) { + s += n, d += n; + while (n -- > 0) { + *-- d = *-- s; + } + } else { + while (n -- > 0) { + *d ++ = *s ++; + } + } + return dst; +#endif /* __HAVE_ARCH_MEMMOVE */ +} + +/* * + * memcpy - copies the value of @n bytes from the location pointed by @src to + * the memory area pointed by @dst. + * @dst pointer to the destination array where the content is to be copied + * @src pointer to the source of data to by copied + * @n: number of bytes to copy + * + * The memcpy() returns @dst. + * + * Note that, the function does not check any terminating null character in @src, + * it always copies exactly @n bytes. To avoid overflows, the size of arrays pointed + * by both @src and @dst, should be at least @n bytes, and should not overlap + * (for overlapping memory area, memmove is a safer approach). + * */ +void * +memcpy(void *dst, const void *src, size_t n) { +#ifdef __HAVE_ARCH_MEMCPY + return __memcpy(dst, src, n); +#else + const char *s = src; + char *d = dst; + while (n -- > 0) { + *d ++ = *s ++; + } + return dst; +#endif /* __HAVE_ARCH_MEMCPY */ +} + +/* * + * memcmp - compares two blocks of memory + * @v1: pointer to block of memory + * @v2: pointer to block of memory + * @n: number of bytes to compare + * + * The memcmp() functions returns an integral value indicating the + * relationship between the content of the memory blocks: + * - A zero value indicates that the contents of both memory blocks are equal; + * - A value greater than zero indicates that the first byte that does not + * match in both memory blocks has a greater value in @v1 than in @v2 + * as if evaluated as unsigned char values; + * - And a value less than zero indicates the opposite. + * */ +int +memcmp(const void *v1, const void *v2, size_t n) { + const char *s1 = (const char *)v1; + const char *s2 = (const char *)v2; + while (n -- > 0) { + if (*s1 != *s2) { + return (int)((unsigned char)*s1 - (unsigned char)*s2); + } + s1 ++, s2 ++; + } + return 0; +} + diff --git a/lab8/libs/string.h b/lab8/libs/string.h new file mode 100644 index 0000000..76c6f3b --- /dev/null +++ b/lab8/libs/string.h @@ -0,0 +1,28 @@ +#ifndef __LIBS_STRING_H__ +#define __LIBS_STRING_H__ + +#include + +size_t strlen(const char *s); +size_t strnlen(const char *s, size_t len); + +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, size_t len); +char *strcat(char *dst, const char *src); +char *strdup(const char *src); +char *stradd(const char *src1, const char *src2); + +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); + +char *strchr(const char *s, char c); +char *strfind(const char *s, char c); +long strtol(const char *s, char **endptr, int base); + +void *memset(void *s, char c, size_t n); +void *memmove(void *dst, const void *src, size_t n); +void *memcpy(void *dst, const void *src, size_t n); +int memcmp(const void *v1, const void *v2, size_t n); + +#endif /* !__LIBS_STRING_H__ */ + diff --git a/lab8/libs/unistd.h b/lab8/libs/unistd.h new file mode 100644 index 0000000..8599be6 --- /dev/null +++ b/lab8/libs/unistd.h @@ -0,0 +1,68 @@ +#ifndef __LIBS_UNISTD_H__ +#define __LIBS_UNISTD_H__ + +#define T_SYSCALL 0x80 + +/* syscall number */ +#define SYS_exit 1 +#define SYS_fork 2 +#define SYS_wait 3 +#define SYS_exec 4 +#define SYS_clone 5 +#define SYS_yield 10 +#define SYS_sleep 11 +#define SYS_kill 12 +#define SYS_gettime 17 +#define SYS_getpid 18 +#define SYS_mmap 20 +#define SYS_munmap 21 +#define SYS_shmem 22 +#define SYS_putc 30 +#define SYS_pgdir 31 +#define SYS_open 100 +#define SYS_close 101 +#define SYS_read 102 +#define SYS_write 103 +#define SYS_seek 104 +#define SYS_fstat 110 +#define SYS_fsync 111 +#define SYS_getcwd 121 +#define SYS_getdirentry 128 +#define SYS_dup 130 +/* OLNY FOR LAB6 */ +#define SYS_lab6_set_priority 255 + +/* SYS_fork flags */ +#define CLONE_VM 0x00000100 // set if VM shared between processes +#define CLONE_THREAD 0x00000200 // thread group +#define CLONE_FS 0x00000800 // set if shared between processes + +/* VFS flags */ +// flags for open: choose one of these +#define O_RDONLY 0 // open for reading only +#define O_WRONLY 1 // open for writing only +#define O_RDWR 2 // open for reading and writing +// then or in any of these: +#define O_CREAT 0x00000004 // create file if it does not exist +#define O_EXCL 0x00000008 // error if O_CREAT and the file exists +#define O_TRUNC 0x00000010 // truncate file upon open +#define O_APPEND 0x00000020 // append on each write +// additonal related definition +#define O_ACCMODE 3 // mask for O_RDONLY / O_WRONLY / O_RDWR + +#define NO_FD -0x9527 // invalid fd + +/* lseek codes */ +#define LSEEK_SET 0 // seek relative to beginning of file +#define LSEEK_CUR 1 // seek relative to current position in file +#define LSEEK_END 2 // seek relative to end of file + +#define FS_MAX_DNAME_LEN 31 +#define FS_MAX_FNAME_LEN 255 +#define FS_MAX_FPATH_LEN 4095 + +#define EXEC_MAX_ARG_NUM 32 +#define EXEC_MAX_ARG_LEN 4095 + +#endif /* !__LIBS_UNISTD_H__ */ + diff --git a/lab8/libs/util.h b/lab8/libs/util.h new file mode 100644 index 0000000..e3d6596 --- /dev/null +++ b/lab8/libs/util.h @@ -0,0 +1,15 @@ +// +// Created by lumin on 2020/11/16. +// + +#ifndef LAB8_UTIL_H +#define LAB8_UTIL_H + +#include + +void set_bits_value(volatile uint32_t *bits, uint32_t mask, uint32_t value); +void set_bits_value_offset(volatile uint32_t *bits, uint32_t mask, uint32_t value, uint32_t offset); +void set_bit(volatile uint32_t *bits, uint32_t offset); +void clear_bit(volatile uint32_t *bits, uint32_t offset); + +#endif //LAB8_UTIL_H diff --git a/lab8/tools/boot.ld b/lab8/tools/boot.ld new file mode 100644 index 0000000..dc732b0 --- /dev/null +++ b/lab8/tools/boot.ld @@ -0,0 +1,15 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + . = 0x7C00; + + .startup : { + *bootasm.o(.text) + } + + .text : { *(.text) } + .data : { *(.data .rodata) } + + /DISCARD/ : { *(.eh_*) } +} diff --git a/lab8/tools/function.mk b/lab8/tools/function.mk new file mode 100644 index 0000000..9b8be0c --- /dev/null +++ b/lab8/tools/function.mk @@ -0,0 +1,95 @@ +OBJPREFIX := __objs_ + +.SECONDEXPANSION: +# -------------------- function begin -------------------- + +# list all files in some directories: (#directories, #types) +listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\ + $(wildcard $(addsuffix $(SLASH)*,$(1)))) + +# get .o obj files: (#files[, packet]) +toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\ + $(addsuffix .o,$(basename $(1)))) + +# get .d dependency files: (#files[, packet]) +todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2))) + +totarget = $(addprefix $(BINDIR)$(SLASH),$(1)) + +# change $(name) to $(OBJPREFIX)$(name): (#names) +packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX)) + +# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir]) +define cc_template +$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@) + @$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@ +$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@) + @echo + cc $$< + $(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@ +ALLOBJS += $$(call toobj,$(1),$(4)) +endef + +# compile file: (#files, cc[, flags, dir]) +define do_cc_compile +$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4)))) +endef + +# add files to packet: (#files, cc[, flags, packet, dir]) +define do_add_files_to_packet +__temp_packet__ := $(call packetname,$(4)) +ifeq ($$(origin $$(__temp_packet__)),undefined) +$$(__temp_packet__) := +endif +__temp_objs__ := $(call toobj,$(1),$(5)) +$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5)))) +$$(__temp_packet__) += $$(__temp_objs__) +endef + +# add objs to packet: (#objs, packet) +define do_add_objs_to_packet +__temp_packet__ := $(call packetname,$(2)) +ifeq ($$(origin $$(__temp_packet__)),undefined) +$$(__temp_packet__) := +endif +$$(__temp_packet__) += $(1) +endef + +# add packets and objs to target (target, #packes, #objs[, cc, flags]) +define do_create_target +__temp_target__ = $(call totarget,$(1)) +__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3) +TARGETS += $$(__temp_target__) +ifneq ($(4),) +$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) + $(V)$(4) $(5) $$^ -o $$@ +else +$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) +endif +endef + +# finish all +define do_finish_all +ALLDEPS = $$(ALLOBJS:.o=.d) +$$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)): + @$(MKDIR) $$@ +endef + +# -------------------- function end -------------------- +# compile file: (#files, cc[, flags, dir]) +cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4))) + +# add files to packet: (#files, cc[, flags, packet, dir]) +add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5))) + +# add objs to packet: (#objs, packet) +add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2))) + +# add packets and objs to target (target, #packes, #objs, cc, [, flags]) +create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5))) + +read_packet = $(foreach p,$(call packetname,$(1)),$($(p))) + +add_dependency = $(eval $(1): $(2)) + +finish_all = $(eval $(call do_finish_all)) + diff --git a/lab8/tools/gdbinit b/lab8/tools/gdbinit new file mode 100644 index 0000000..df5df85 --- /dev/null +++ b/lab8/tools/gdbinit @@ -0,0 +1,3 @@ +file bin/kernel +target remote :1234 +break kern_init diff --git a/lab8/tools/grade.sh b/lab8/tools/grade.sh new file mode 100644 index 0000000..4b46529 --- /dev/null +++ b/lab8/tools/grade.sh @@ -0,0 +1,560 @@ +#!/bin/sh + +verbose=false +if [ "x$1" = "x-v" ]; then + verbose=true + out=/dev/stdout + err=/dev/stderr +else + out=/dev/null + err=/dev/null +fi + +## make & makeopts +if gmake --version > /dev/null 2>&1; then + make=gmake; +else + make=make; +fi + +makeopts="--quiet --no-print-directory -j" + +make_print() { + echo `$make $makeopts print-$1` +} + +## command tools +awk='awk' +bc='bc' +date='date' +grep='grep' +rm='rm -f' +sed='sed' + +## symbol table +sym_table='obj/kernel.sym' + +## gdb & gdbopts +gdb="$(make_print GDB)" +gdbport='1234' + +gdb_in="$(make_print GRADE_GDB_IN)" + +## qemu & qemuopts +qemu="$(make_print qemu)" + +qemu_out="$(make_print GRADE_QEMU_OUT)" + +if $qemu -nographic -help | grep -q '^-gdb'; then + qemugdb="-gdb tcp::$gdbport" +else + qemugdb="-s -p $gdbport" +fi + +## default variables +default_timeout=30 +default_pts=5 + +pts=5 +part=0 +part_pos=0 +total=0 +total_pos=0 + +## default functions +update_score() { + total=`expr $total + $part` + total_pos=`expr $total_pos + $part_pos` + part=0 + part_pos=0 +} + +get_time() { + echo `$date +%s.%N 2> /dev/null` +} + +show_part() { + echo "Part $1 Score: $part/$part_pos" + echo + update_score +} + +show_final() { + update_score + echo "Total Score: $total/$total_pos" + if [ $total -lt $total_pos ]; then + exit 1 + fi +} + +show_time() { + t1=$(get_time) + time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null` + echo "(${time}s)" +} + +show_build_tag() { + echo "$1:" | $awk '{printf "%-24s ", $0}' +} + +show_check_tag() { + echo "$1:" | $awk '{printf " -%-40s ", $0}' +} + +show_msg() { + echo $1 + shift + if [ $# -gt 0 ]; then + echo -e "$@" | awk '{printf " %s\n", $0}' + echo + fi +} + +pass() { + show_msg OK "$@" + part=`expr $part + $pts` + part_pos=`expr $part_pos + $pts` +} + +fail() { + show_msg WRONG "$@" + part_pos=`expr $part_pos + $pts` +} + +run_qemu() { + # Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty, + # wait until $brkfun is reached or $timeout expires, then kill QEMU + qemuextra= + if [ "$brkfun" ]; then + qemuextra="-S $qemugdb" + fi + + if [ -z "$timeout" ] || [ $timeout -le 0 ]; then + timeout=$default_timeout; + fi + + t0=$(get_time) + ( + ulimit -t $timeout + exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra + ) > $out 2> $err & + pid=$! + + # wait for QEMU to start + sleep 1 + + if [ -n "$brkfun" ]; then + # find the address of the kernel $brkfun function + brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'` + brkaddr_phys=`echo $brkaddr | sed "s/^c0/00/g"` + ( + echo "target remote localhost:$gdbport" + echo "break *0x$brkaddr" + if [ "$brkaddr" != "$brkaddr_phys" ]; then + echo "break *0x$brkaddr_phys" + fi + echo "continue" + ) > $gdb_in + + $gdb -batch -nx -x $gdb_in > /dev/null 2>&1 + + # make sure that QEMU is dead + # on OS X, exiting gdb doesn't always exit qemu + kill $pid > /dev/null 2>&1 + fi +} + +build_run() { + # usage: build_run + show_build_tag "$1" + shift + + if $verbose; then + echo "$make $@ ..." + fi + $make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err + + if [ $? -ne 0 ]; then + echo $make $@ failed + exit 1 + fi + + # now run qemu and save the output + run_qemu + + show_time + + cp $qemu_out .`echo $tag | tr '[:upper:]' '[:lower:]' | sed 's/ /_/g'`.log +} + +check_result() { + # usage: check_result + show_check_tag "$1" + shift + + # give qemu some time to run (for asynchronous mode) + if [ ! -s $qemu_out ]; then + sleep 4 + fi + + if [ ! -s $qemu_out ]; then + fail > /dev/null + echo 'no $qemu_out' + else + check=$1 + shift + $check "$@" + fi +} + +check_regexps() { + okay=yes + not=0 + reg=0 + error= + for i do + if [ "x$i" = "x!" ]; then + not=1 + elif [ "x$i" = "x-" ]; then + reg=1 + else + if [ $reg -ne 0 ]; then + $grep '-E' "^$i\$" $qemu_out > /dev/null + else + $grep '-F' "$i" $qemu_out > /dev/null + fi + found=$(($? == 0)) + if [ $found -eq $not ]; then + if [ $found -eq 0 ]; then + msg="!! error: missing '$i'" + else + msg="!! error: got unexpected line '$i'" + fi + okay=no + if [ -z "$error" ]; then + error="$msg" + else + error="$error\n$msg" + fi + fi + not=0 + reg=0 + fi + done + if [ "$okay" = "yes" ]; then + pass + else + fail "$error" + if $verbose; then + exit 1 + fi + fi +} + +run_test() { + # usage: run_test [-tag ] [-prog ] [-Ddef...] [-check ] checkargs ... + tag= + prog= + check=check_regexps + while true; do + select= + case $1 in + -tag|-prog) + select=`expr substr $1 2 ${#1}` + eval $select='$2' + ;; + esac + if [ -z "$select" ]; then + break + fi + shift + shift + done + defs= + while expr "x$1" : "x-D.*" > /dev/null; do + defs="DEFS+='$1' $defs" + shift + done + if [ "x$1" = "x-check" ]; then + check=$2 + shift + shift + fi + + if [ -z "$prog" ]; then + $make $makeopts touch > /dev/null 2>&1 + args="$defs" + else + if [ -z "$tag" ]; then + tag="$prog" + fi + args="build-$prog $defs" + fi + + build_run "$tag" "$args" + + check_result 'check result' "$check" "$@" +} + +quick_run() { + # usage: quick_run [-Ddef...] + tag="$1" + shift + defs= + while expr "x$1" : "x-D.*" > /dev/null; do + defs="DEFS+='$1' $defs" + shift + done + + $make $makeopts touch > /dev/null 2>&1 + build_run "$tag" "$defs" +} + +quick_check() { + # usage: quick_check checkargs ... + tag="$1" + shift + check_result "$tag" check_regexps "$@" +} + +## kernel image +osimg=$(make_print ucoreimg) + +## swap image +swapimg=$(make_print swapimg) + +## set default qemu-options +qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback" + +## set break-function, default is readline +brkfun=readline + +default_check() { + pts=7 + check_regexps "$@" + + pts=3 + quick_check 'check output' \ + 'memory management: default_pmm_manager' \ + 'check_alloc_page() succeeded!' \ + 'check_pgdir() succeeded!' \ + 'check_boot_pgdir() succeeded!' \ + 'PDE(0e0) c0000000-f8000000 38000000 urw' \ + ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ + 'PDE(001) fac00000-fb000000 00400000 -rw' \ + ' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \ + ' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \ + 'check_vma_struct() succeeded!' \ + 'page fault at 0x00000100: K/W [no page found].' \ + 'check_pgfault() succeeded!' \ + 'check_vmm() succeeded.' \ + 'page fault at 0x00001000: K/W [no page found].' \ + 'page fault at 0x00002000: K/W [no page found].' \ + 'page fault at 0x00003000: K/W [no page found].' \ + 'page fault at 0x00004000: K/W [no page found].' \ + 'write Virt Page e in fifo_check_swap' \ + 'page fault at 0x00005000: K/W [no page found].' \ + 'page fault at 0x00001000: K/W [no page found]' \ + 'page fault at 0x00002000: K/W [no page found].' \ + 'page fault at 0x00003000: K/W [no page found].' \ + 'page fault at 0x00004000: K/W [no page found].' \ + 'check_swap() succeeded!' \ + '++ setup timer interrupts' +} + +## check now!! + +run_test -prog 'badsegment' -check default_check \ + 'kernel_execve: pid = 2, name = "badsegment".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000d General Protection' \ + ' err 0x00000028' \ + - ' eip 0x008.....' \ + - ' esp 0xaff.....' \ + ' cs 0x----001b' \ + ' ss 0x----0023' \ + ! - 'user panic at .*' + +run_test -prog 'divzero' -check default_check \ + 'kernel_execve: pid = 2, name = "divzero".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x00000000 Divide error' \ + - ' eip 0x008.....' \ + - ' esp 0xaff.....' \ + ' cs 0x----001b' \ + ' ss 0x----0023' \ + ! - 'user panic at .*' + +run_test -prog 'softint' -check default_check \ + 'kernel_execve: pid = 2, name = "softint".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000d General Protection' \ + ' err 0x00000072' \ + - ' eip 0x008.....' \ + - ' esp 0xaff.....' \ + ' cs 0x----001b' \ + ' ss 0x----0023' \ + ! - 'user panic at .*' + +pts=10 + +run_test -prog 'faultread' -check default_check \ + 'kernel_execve: pid = 2, name = "faultread".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000e Page Fault' \ + ' err 0x00000004' \ + - ' eip 0x008.....' \ + ! - 'user panic at .*' + +run_test -prog 'faultreadkernel' -check default_check \ + 'kernel_execve: pid = 2, name = "faultreadkernel".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000e Page Fault' \ + ' err 0x00000005' \ + - ' eip 0x008.....' \ + ! - 'user panic at .*' + +run_test -prog 'hello' -check default_check \ + 'kernel_execve: pid = 2, name = "hello".' \ + 'Hello world!!.' \ + 'I am process 2.' \ + 'hello pass.' + +run_test -prog 'testbss' -check default_check \ + 'kernel_execve: pid = 2, name = "testbss".' \ + 'Making sure bss works right...' \ + 'Yes, good. Now doing a wild write off the end...' \ + 'testbss may pass.' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000e Page Fault' \ + ' err 0x00000006' \ + - ' eip 0x008.....' \ + 'killed by kernel.' \ + ! - 'user panic at .*' + +run_test -prog 'pgdir' -check default_check \ + 'kernel_execve: pid = 2, name = "pgdir".' \ + 'I am 2, print pgdir.' \ + 'PDE(001) 00800000-00c00000 00400000 urw' \ + ' |-- PTE(00002) 00800000-00802000 00002000 ur-' \ + ' |-- PTE(00001) 00802000-00803000 00001000 urw' \ + 'PDE(001) afc00000-b0000000 00400000 urw' \ + ' |-- PTE(00004) afffc000-b0000000 00004000 urw' \ + 'PDE(0e0) c0000000-f8000000 38000000 urw' \ + ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ + 'pgdir pass.' + +run_test -prog 'yield' -check default_check \ + 'kernel_execve: pid = 2, name = "yield".' \ + 'Hello, I am process 2.' \ + 'Back in process 2, iteration 0.' \ + 'Back in process 2, iteration 1.' \ + 'Back in process 2, iteration 2.' \ + 'Back in process 2, iteration 3.' \ + 'Back in process 2, iteration 4.' \ + 'All done in process 2.' \ + 'yield pass.' + + +run_test -prog 'badarg' -check default_check \ + 'kernel_execve: pid = 2, name = "badarg".' \ + 'fork ok.' \ + 'badarg pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +pts=10 + +run_test -prog 'exit' -check default_check \ + 'kernel_execve: pid = 2, name = "exit".' \ + 'I am the parent. Forking the child...' \ + 'I am the parent, waiting now..' \ + 'I am the child.' \ + - 'waitpid [0-9]+ ok\.' \ + 'exit pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +run_test -prog 'spin' -check default_check \ + 'kernel_execve: pid = 2, name = "spin".' \ + 'I am the parent. Forking the child...' \ + 'I am the parent. Running the child...' \ + 'I am the child. spinning ...' \ + 'I am the parent. Killing the child...' \ + 'kill returns 0' \ + 'wait returns 0' \ + 'spin may pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +run_test -prog 'waitkill' -check default_check \ + 'kernel_execve: pid = 2, name = "waitkill".' \ + 'wait child 1.' \ + 'child 2.' \ + 'child 1.' \ + 'kill parent ok.' \ + 'kill child1 ok.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +pts=15 + +run_test -prog 'forktest' -check default_check \ + 'kernel_execve: pid = 2, name = "forktest".' \ + 'I am child 31' \ + 'I am child 19' \ + 'I am child 13' \ + 'I am child 0' \ + 'forktest pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'fork claimed to work [0-9]+ times!' \ + ! 'wait stopped early' \ + ! 'wait got too many' \ + ! - 'user panic at .*' + +pts=10 +run_test -prog 'forktree' -check default_check \ + 'kernel_execve: pid = 2, name = "forktree".' \ + - '....: I am '\'''\' \ + - '....: I am '\''0'\' \ + - '....: I am '\'''\' \ + - '....: I am '\''1'\' \ + - '....: I am '\''0'\' \ + - '....: I am '\''01'\' \ + - '....: I am '\''00'\' \ + - '....: I am '\''11'\' \ + - '....: I am '\''10'\' \ + - '....: I am '\''101'\' \ + - '....: I am '\''100'\' \ + - '....: I am '\''111'\' \ + - '....: I am '\''110'\' \ + - '....: I am '\''001'\' \ + - '....: I am '\''000'\' \ + - '....: I am '\''011'\' \ + - '....: I am '\''010'\' \ + - '....: I am '\''0101'\' \ + - '....: I am '\''0100'\' \ + - '....: I am '\''0111'\' \ + - '....: I am '\''0110'\' \ + - '....: I am '\''0001'\' \ + - '....: I am '\''0000'\' \ + - '....: I am '\''0011'\' \ + - '....: I am '\''0010'\' \ + - '....: I am '\''1101'\' \ + - '....: I am '\''1100'\' \ + - '....: I am '\''1111'\' \ + - '....: I am '\''1110'\' \ + - '....: I am '\''1001'\' \ + - '....: I am '\''1000'\' \ + - '....: I am '\''1011'\' \ + - '....: I am '\''1010'\' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' + +## print final-score +show_final diff --git a/lab8/tools/kernel.ld b/lab8/tools/kernel.ld new file mode 100644 index 0000000..c7ead90 --- /dev/null +++ b/lab8/tools/kernel.ld @@ -0,0 +1,51 @@ +/* Simple linker script for the ucore kernel. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(kern_entry) + +BASE_ADDRESS = 0xFFFFFFFFC0020000; + +SECTIONS +{ + /* Load the kernel at this address: "." means the current address */ + . = BASE_ADDRESS; + + .text : { + *(.text.kern_entry .text .stub .text.* .gnu.linkonce.t.*) + } + + PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + /* Adjust the address for the data segment to the next page */ + . = ALIGN(0x1000); + + /* The data segment */ + .data : { + *(.data) + *(.data.*) + } + + .sdata : { + *(.sdata) + *(.sdata.*) + } + + PROVIDE(edata = .); + + .bss : { + *(.bss) + *(.bss.*) + *(.sbss*) + } + + PROVIDE(end = .); + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack) + } +} diff --git a/lab8/tools/kflash.py b/lab8/tools/kflash.py new file mode 100644 index 0000000..db2a124 --- /dev/null +++ b/lab8/tools/kflash.py @@ -0,0 +1,1548 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import (division, print_function) + +import sys +import time +import zlib +import copy +import struct +import binascii +import hashlib +import argparse +import math +import zipfile, tempfile +import json +import re +import os + + +class KFlash: + print_callback = None + + def __init__(self, print_callback = None): + self.killProcess = False + self.loader = None + KFlash.print_callback = print_callback + + @staticmethod + def log(*args, **kwargs): + if KFlash.print_callback: + KFlash.print_callback(*args, **kwargs) + else: + print(*args, **kwargs) + + def process(self, terminal=True, dev="", baudrate=1500000, board=None, sram = False, file="", callback=None, noansi=False, terminal_auto_size=False, terminal_size=(50, 1), slow_mode = False, addr=None, length=None): + self.killProcess = False + BASH_TIPS = dict(NORMAL='\033[0m',BOLD='\033[1m',DIM='\033[2m',UNDERLINE='\033[4m', + DEFAULT='\033[0m', RED='\033[31m', YELLOW='\033[33m', GREEN='\033[32m', + BG_DEFAULT='\033[49m', BG_WHITE='\033[107m') + + ERROR_MSG = BASH_TIPS['RED']+BASH_TIPS['BOLD']+'[ERROR]'+BASH_TIPS['NORMAL'] + WARN_MSG = BASH_TIPS['YELLOW']+BASH_TIPS['BOLD']+'[WARN]'+BASH_TIPS['NORMAL'] + INFO_MSG = BASH_TIPS['GREEN']+BASH_TIPS['BOLD']+'[INFO]'+BASH_TIPS['NORMAL'] + + VID_LIST_FOR_AUTO_LOOKUP = "(1A86)|(0403)|(067B)|(10C4)|(C251)|(0403)" + # WCH FTDI PL CL DAP OPENEC + ISP_RECEIVE_TIMEOUT = 0.5 + + MAX_RETRY_TIMES = 10 + + ISP_FLASH_SECTOR_SIZE = 4096 + ISP_FLASH_DATA_FRAME_SIZE = ISP_FLASH_SECTOR_SIZE * 16 + + def tuple2str(t): + ret = "" + for i in t: + ret += i+" " + return ret + + def raise_exception(exception): + if self.loader: + try: + self.loader._port.close() + except Exception: + pass + raise exception + + try: + from enum import Enum + except ImportError: + err = (ERROR_MSG,'enum34 must be installed, run '+BASH_TIPS['GREEN']+'`' + ('pip', 'pip3')[sys.version_info > (3, 0)] + ' install enum34`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise Exception(err) + try: + import serial + import serial.tools.list_ports + except ImportError: + err = (ERROR_MSG,'PySerial must be installed, run '+BASH_TIPS['GREEN']+'`' + ('pip', 'pip3')[sys.version_info > (3, 0)] + ' install pyserial`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise Exception(err) + + class TimeoutError(Exception): pass + + class ProgramFileFormat(Enum): + FMT_BINARY = 0 + FMT_ELF = 1 + FMT_KFPKG = 2 + + # AES is from: https://github.com/ricmoo/pyaes, Copyright by Richard Moore + class AES: + '''Encapsulates the AES block cipher. + You generally should not need this. Use the AESModeOfOperation classes + below instead.''' + @staticmethod + def _compact_word(word): + return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] + + # Number of rounds by keysize + number_of_rounds = {16: 10, 24: 12, 32: 14} + + # Round constant words + rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] + + # S-box and Inverse S-box (S is for Substitution) + S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] + Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] + + # Transformations for encryption + T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] + T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] + T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] + T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] + + # Transformations for decryption + T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] + T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] + T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] + T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] + + # Transformations for decryption key expansion + U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] + U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] + U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] + U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] + + def __init__(self, key): + + if len(key) not in (16, 24, 32): + raise_exception( ValueError('Invalid key size') ) + + rounds = self.number_of_rounds[len(key)] + + # Encryption round keys + self._Ke = [[0] * 4 for i in range(rounds + 1)] + + # Decryption round keys + self._Kd = [[0] * 4 for i in range(rounds + 1)] + + round_key_count = (rounds + 1) * 4 + KC = len(key) // 4 + + # Convert the key into ints + tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in range(0, len(key), 4) ] + + # Copy values into round key arrays + for i in range(0, KC): + self._Ke[i // 4][i % 4] = tk[i] + self._Kd[rounds - (i // 4)][i % 4] = tk[i] + + # Key expansion (fips-197 section 5.2) + rconpointer = 0 + t = KC + while t < round_key_count: + + tt = tk[KC - 1] + tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ + (self.S[(tt >> 8) & 0xFF] << 16) ^ + (self.S[ tt & 0xFF] << 8) ^ + self.S[(tt >> 24) & 0xFF] ^ + (self.rcon[rconpointer] << 24)) + rconpointer += 1 + + if KC != 8: + for i in range(1, KC): + tk[i] ^= tk[i - 1] + + # Key expansion for 256-bit keys is "slightly different" (fips-197) + else: + for i in range(1, KC // 2): + tk[i] ^= tk[i - 1] + tt = tk[KC // 2 - 1] + + tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ + (self.S[(tt >> 8) & 0xFF] << 8) ^ + (self.S[(tt >> 16) & 0xFF] << 16) ^ + (self.S[(tt >> 24) & 0xFF] << 24)) + + for i in range(KC // 2 + 1, KC): + tk[i] ^= tk[i - 1] + + # Copy values into round key arrays + j = 0 + while j < KC and t < round_key_count: + self._Ke[t // 4][t % 4] = tk[j] + self._Kd[rounds - (t // 4)][t % 4] = tk[j] + j += 1 + t += 1 + + # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) + for r in range(1, rounds): + for j in range(0, 4): + tt = self._Kd[r][j] + self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ + self.U2[(tt >> 16) & 0xFF] ^ + self.U3[(tt >> 8) & 0xFF] ^ + self.U4[ tt & 0xFF]) + + def encrypt(self, plaintext): + 'Encrypt a block of plain text using the AES block cipher.' + + if len(plaintext) != 16: + raise_exception( ValueError('wrong block length') ) + + rounds = len(self._Ke) - 1 + (s1, s2, s3) = [1, 2, 3] + a = [0, 0, 0, 0] + + # Convert plaintext to (ints ^ key) + t = [(AES._compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in range(0, 4)] + + # Apply round transforms + for r in range(1, rounds): + for i in range(0, 4): + a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ + self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T4[ t[(i + s3) % 4] & 0xFF] ^ + self._Ke[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in range(0, 4): + tt = self._Ke[rounds][i] + result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + def decrypt(self, ciphertext): + 'Decrypt a block of cipher text using the AES block cipher.' + + if len(ciphertext) != 16: + raise_exception( ValueError('wrong block length') ) + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(AES._compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in range(0, 4)] + + # Apply round transforms + for r in range(1, rounds): + for i in range(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in range(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + class AES_128_CBC: + + def __init__(self, key, iv = None): + self._aes = AES(key) + if iv is None: + self._last_cipherblock = [ 0 ] * 16 + elif len(iv) != 16: + raise_exception( ValueError('initialization vector must be 16 bytes') ) + else: + self._last_cipherblock = iv + + + def encrypt(self, plaintext): + if len(plaintext) != 16: + raise_exception( ValueError('plaintext block must be 16 bytes') ) + + precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] + self._last_cipherblock = self._aes.encrypt(precipherblock) + + return b''.join(map(lambda x: x.to_bytes(1, 'little'), self._last_cipherblock)) + + def decrypt(self, ciphertext): + if len(ciphertext) != 16: + raise_exception( ValueError('ciphertext block must be 16 bytes') ) + + cipherblock = ciphertext + plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] + self._last_cipherblock = cipherblock + + return b''.join(map(lambda x: x.to_bytes(1, 'little'), plaintext)) + + ISP_PROG = '789cedbd0d5453c7d63f3c27c9c94910141b3058628b4440b92dd78a8aadd60b4a8862ebb57e807a6b0b3d2062d14ad552dbd21292438c68911e2028d82256b0dc5b6f5bd458d12255c4da8fdb2fc57e68d10041c50f2a105030ef9e734e02a4dee73efff5bc6bfdd7bbde275d3ff799af3d7b66f69ed933670e1dbbefe6671f5e24108186fed62f983871fd82004024c664b30821363b7bc4faed0b27ea2289285d14314b378b98ad9b4d44eba2098d4e43c4e86208ad4e4bccd1cd21e6eae612b1ba58629e6e1ef194ee29e269ddd3c47cdd7ce2afbabfee78617d9968e27aef89681142b77e002c228002168980021689810216498002169140018ba440018b28a0804532a0804572a080451e40018b8601052cf2040a58e40514b0683850c0a2114001661db4495757a1bc2c430ad1273f285e16f5ad0f8aec5010be6826f140fe4b010bfe4aa44b9b3dda86b73f706b54e7833d0ff59537553457b5ed6fffe4d6a1ce233d9ff5cdba14d3127b65fef5851d715dcb7b9febbf7ca9b5e5ea951bd77fefe8eebad37baf3f2480f00e1937d23b64fc58ef90471ef30e09887a2064dc330f848c4f7c20e491f4074202743e21e3f27d42c6eff60979e4639f9080ba5121e3be1b1532fed2a890473a4641f9d1507e34941f0de54743797f28ef0fe5fda1bc3f941f03e5c740f931507e0c947f18ca3f0ce51f86f20faf0f0a987c3bc87bb2478e8f885e42a29b0113178f2ebb39b169ee7a6fdfc9e509b3122f278a5e2e7f79d6facbeba549154931c9adc9d2572a5e89c968cdf05855b52a36f56aaac76b55afc5be7ef5f5e169fbd3e6afb9b166f89bfbdf9cffd68db728689b3140e74d8d25461ac7ea465281c403c640dd03949a5018d53a05358ef0318ed3f9504184af3148e74b0513a38cc1ba515408a13486e894d478c2cf385ee7474d20461b27e84653a1c483c650dd83d49f087fe39f74fed42384caf8884e453d4a8c313eaa1b4385110f19c3740f517f261e36fe59f7b0822002d6a38089bcc6468d9436af0f89ec60b4b4ca8e488d19e26d594d336e67444f5c8f167877d59abdb998b3e50984268ac8ce526e4448416a3ccd19a007c68661b4a91229c5284b2116ff8b168b91489f9d85d3f40d94077313f337238e439f994259acb1f22bd397ca64cca3de834b019427305a2594514bed525c07d4f22a42e2393de418ed742545f07551941962f5a76594d79c527aba9f1f119e42114448f1097f63b7e3fc09525bdc5055488f392882a72fbf5aa40bf2379288ee3c84fc8d52a4d3f83310a25b44ac2f25d2e5ef7837aca8e39e7a1f85d4ef5388d0646791a458dbe538fc4366ad781f250d270f2186542392b864e1a5f1ebe7a561e608f2dce3e365f7409a62994b1af5fb4a02e4187550b275918242a21ad21219667493236d400e521b5654fca5824422e66a7e4c71bc6faef5a1c6fb4ab6d182a5126b48906cdf0fc9b55b17618ed3650723e90de7913f25c3dc3703f7556d88e71e56b4e3dd359670d9212c276e4f1945f5d612237768c723987962188db45d2de940c218755c86512613f911648db69ba5b43fe5ec5b9fa8033f2c06147ff9e3225d30ae99de700e6a8536c5f833105adb82d85150eb3ba3df3d54983ea4055c8f32671123c53dfa45eda6f4e0f6fef82ac3464de02d69a7daa703299340272427779b23b12468774bf3180dff9cb0eb49e1a9ec3d9d2648783ebef763cd58415f0326d6e378ee39e1cff99a09c2735958a1e651a74e3fba43f39833ffa3bb34539cf91f512570ba37cdde54c769e11eb1fde26e5e1f45f6a6b3fc1369bfc8f749e5c5525a0ebd77fe4b75052572b611741bd2523cb9bc11f69f75317c4cc83c22c6a4e16227da7fe67b39eb97f275a20d9793088d067a7a82d01e943757788accd76149f9b69b55695ce9c7ede770fd7b18eac70f788924f6733f690b2dc4c8c0b6426da86122f260187e3c91733cd1577aad0ee13155a34e342b098f2c315241762069bb204b9f58d3013948134edb946ed2aa3d3b50493c49ec01cac63388b5db4704922bb6493b036f85354f6a528fd9ede4fe6b7972f6caf1b2b09c7c8da03f5f93d142da1d22c83a8cb9875bcab79318c9822d605de3b84bdcb8b7ab7d9d7cb3cef1bae9e49a75daa995984700f9dfe111f9fd501e912707f3c8eaffeff040df0ce5818e0fe681a4832d27f24ba73d09e163cebc2bd2cb9be6772ebc15d7bebcedb9e69ce6e0bed89e2a53d8964906d603792b862191f975d094dcbf5be4ed1509d2a4cb1b666d2adf247aedf2ea592f96bf48681281076bef1d31dd7827f288915edf8cca8d25060a8d290d36d11b4a90420e53ae6518fa9e99cff84be528dbcc2ae5221d5b63181755237f9c98b9f352df0acb98f4853d8a9d52515ce7f25b2fb429965f2594cf82bd793ed5217f317f7579db99a6f97dcfb5e7b45734871ac24c9f980ee5beb0d37f987f60c9962d63e9f7e4a8352106d63969eae54db35e2b7f4df4fae51767a5119a202c1b8c6a89410ef36c04c14a90a8504bdfc810eb433cd06573b6719b96ef6989d0d306a4501ab8defe294feddb812a7262417bc63b7b7c376b9680ec41a53377d49876442eb37cf81fcaf7c7aba53c9742cd9a5a96fa483cf3246fa721ef17d0d84ec45aea7d93b6c66c43ca57201473a80ccfe20de8a73c3e1cbc9b14c281a77421a59fababe24604e79c619c321dffe7410bf01d36f36409d43cf314cf5dfbae937bc8bb83b99fdb39947b4c89933be6cd52de81eaaaeee11acc51ea94d4b8633bcc28fc9aa9dd6186f5555fbfa468574c8dd28e6a8a0ea1b37966e0a53f9553345dd928c8a9fa5c1d1a3762376ef1f0a19255160c704b29e0b96dccff23b78a7c9e1bcf6be588c72c4c0cace32805edb910613c7cbd3f5e415193742d8a7abb233b4b1f43a28379bb63fccf2911935b43dd8aeccdfb38af7faa2ea4bf9b24fba75caa7d3c3df656845c86027b823bc325414495818c6161a6916be6b74bfbf65004d23f6e40c23cde00fe86779704cf3d2cf9055151288cff4b2514ac5e5f5222d553fe4625ca56ea58bfafbe31abb32a91df5375297514dd6543d08bc355df5e5e376b43f906d1a6cba984662c9ebbe2ad66db3d9f240b2541f994c2188d2accf4ce56e4d3a66ab6c0caf85981fa1f1274c5a26af36baeafc516eced256d0f6cc36b4fa84198699e35b97431212190e17d9f04a684241184f839ee39412f965d4ee067d42a063cf898b131728d7236eebfd99feb1b7c91b88144e5d44f792546dbef6c1125ae7bc7574baf6c9ca98c07bba3b435ac39181d3197ce5e965e7eaba2fd4c5b4ee737cddf37c5f52def79aef3855b2bdb57b7ad6d3e629e640a335419b28de2fa42184f06915a3c4be91bf6a2cf294c0fa2cbd4173086f4063bda16cf941547ca7347de5474d94708d29a88a0fcf8b12da591ac2d04951ed7c54fd9a78a32e576fcb25ba3200dceb65e126b1723bd361a114181d48a3cbd3609f307ba01f8631da10d952253fc1ed48b2ad64937b426c5a456a44a575f7e6dd6ebe5af8bdeb89c366b4df99a64e80bbdf647a48f3e092dafcfd34737a3cf259876a2cb920b5b75e33e8da777750a7506fc43a8db4c0465f6eeb5e8b505604d062889eb7d1f89a30f800c3fe5bd2059b19518c7a484a714a07e6566de685b6646d07b966e07cc38ad88893f68897a71f78bbab90acfbb0e76eb5d07b966e616269659539d6120989b35c6c951f49514b173f4b2ca88a0d17585f1636a85701e5ea79638ea526a4c11c493a5d6961d7d1dc7aa330a886d36aeece58c81b2c544d0a60e4153b7f2e576a75cb25467441397ec38b7f55265bf3377423e1114d52fb4d5c4e7ae313d494c376c43a9b5780cac9ba9667dc359243e538ff0589453d5362d213ed382667954a7dc81f82ef48247f8ea3b88082d21b708fd5676ea3f8d95b5b4b219f7a63eda8098d5fc48bc8ffa1f542ec15e7750213f26078431a9b62f263ebc56bd693941bf23978e4d8b92d4bda8f0ea73306bc8794c9ab567ee85ba947e654bde87d76aa827a2e8d60c2a5868e1f15c68953f79d57ccbe1004d7b0db731d9c116c2ea931fb5b2bcd0992f723bdfbbc74a9faee56568d926dd50b1eedf69113f0bcd3e556d7f9fd86de56abd6e143bb90564413dde64335f6bd6fac1b58e1d546bd3e6dd29a916dcba4207e661bd66b90d695ca9b20c28f51079910f45be34984730430217a1af0d35a619448da114b51c13c348e91bea11d7e70d2d7c9f3774097d3e526b2db19ddb3c5768716de2a7d95951235932523252033b20ced33cce9427b0e402892feca394e1dc4c6bd8948efdaae0f6d836a175fd82579a2b78b04972a7bf9bd791cccf332c4306c805ef35eb75a586db4b6d12b4eef10829eccba539229d86df65ed7d5c4879d89cc2f9f4d3f8127b5f55a6c890fa31fb346134a7e8347297d7092bc84ac89d736a8a528a30bf72932b1599790e648620f3246512d4299184b392807e652aa4c9e74ef25cc5b4a8165cff4db5a0ff173cabe2f55b2dc947665c6b39354990caeb52ad1e66ca586622c22bfd1e09e19a1fb8b918e7aea6c2040bf4781a66ee3dd46e84571612665cf063f97e4a061f16e61fd84579f2fa63edc77b4fd87f79d2de2d2205f9919750639f823cee559e0063c0e5a3ef2aa88fbcac7e2df784f43b0af25baf7acba6743c2ed2ce2a03115d026b0a6db320de6fc7de6cb5d180c4f5140a6b2625826cbde5c997403a697b687358931067cf4e0e4c16f8dec512f2758abbb15ca551b47f0bd2d9186d69ddd87dea3212f5c6cf6460b7a7a58d36648aa1df6e04ffa6df52a224092c6349231565f56eb967d2d6c1ba7fdc8b1ed5858aebc0fff2bc6261a5df7af5c75b479eedc3311f7925d73e393bd448a79f10b1197654673d76897d45ea7872cb91224faba72d8ab2ae69bc37f232b4437a22728b149f531515a56455203565417ba416d88903258102f6488002f6888102f6888002f61040017b105000f8825d5d23d822069dddc6169104f7afe8ecb66516b62804f175e464dbb282ef1465d529c7e4cd2cb2a69fe8d96559965ed15e650863ca617f91d3298cfe70b396f3b3167ed6c4683f6b56f99527cf6c3769677e7b744e681f4986f5847666adcab739ad3dcbef2919a93107703d7d481f2219c6e4ea6eeac16f64ed54c00e9b829478f0a9c7bfd12b2584c21c22a69fa6903c05fc48bf4e844b6ccac854f6e6a9c7777a04331365e5ce596e74ffe291973e88feaf7806fcb050f3948c7fceda8f57c860a63509af8782cdddae6236d3fcd8eb3baa8c0c256eb03bd638c299cac84c5f3c53043649c1f356fb1d44ead1f5b05724917a32603ce9a19e01348014a9c791a2b1313a58b5190f6196b96c56721e5fa7aa8f7f4ab1cf68129efa6634f34f4607ea99d186651525715aafe6b4fe88d9175299201f61b67d523893e910d28f0ae15b7c585ccf8735b79ce585f04d21fd84b30dc278f4cf98a3fb52a891b7b37e7e476ceb99d12e4b827dc1b8ce852c6558a49ed6b910efb1d413250b85b23765abc898bdc24eaaec862c4d1ef385103a7e9dd1ce389d6c51add6d9745a6c2dfa4692189d5ede73e6d6f76d397d159ddfb49f6bfeb9e9b9be177a5676aebeb5b67d77eea42d618650d3d986d235f50d78f4f48f7b20853d04d10f50de30a2b17416e5476874f38879341a365e7e7d79ce079ae79cbedf66da8b0c26c91a8f8d51715be9f57f23966f9525a8d27c16d09d95c1d1d1074be96f0d211799e01c21ff3baa8472703f2accaa26212698f623bd6bb6ec8c2435f40e0f8453f994e3e332afe8233c888a92f0612591d50673a47ada4e74b0ef83b2813ca8ad3ffeb1b38feed31f9123fd0113aa93f79fafc8a1d797a09f73662478beaa0b8ecb8965b2cd745c9907e655b5d3b4156b957a5a15dad4abd3b6ae8bd950b141baa935356675c56ae98b975f9ff546f91ba2cccb6b663272d0cf8861284b17ab8b56cc1b767c8505d7223e866b898eae305ae55ef7c4d05fb204f6152a80ce967ad36b76c9e079ce1767240b3cd36a24a522af6d255e248cea1874d55c73b2404c77752232a846b297506450912b4e54e096f01ed343be2d1f6bd501bd88b3201b25c176d71fff692d96819d77cd413cad9877d7e1f594e9a9b916cfb4b539a1c2c9e1f1742c45798a22c36e51a45032b55f3352b54bbebd61ae311c885235459fbc6abc6aa0d7f7a3ab4659c2cc9c73cc4ca164d6a6a5d1e52963de5b7ad22fe1088c08d7db3bc3bd76b97a5b07de5e708e53c680cc2ae38a0ecf3467ef2704f2d66eb763594d16551add528960ec5fbf20a2d7ef42570dc1509b20e5cbcf464f379a2355094b4fe2d17fe6eec0280648977d37c172d548dffe3bf1b3b1a6a151340dc60e8f5c4d4ea34864ae3258679775b71cd4d740ffd79810a1a99317d69478f8a3f46a7adeaf088ff473dc3843ce79653f671e7b21e7a2d3bb7af1d9e8cb861aa331ead993aa84cbb8e61a7ce610dc3770e610db33bf3374cbfe2dac845c484493f3faff666a0f64fe78da80cf1ac49f82857c6040fd8fd33fca9121e1a75a7dbc9ca05fbf8bf4ca09046dcc20ce300b99ec9d73c12b57c84dc32ed58e49cf692f6fab68c667080b7bf009c373ed2fb4ad6c668d1b61dd6a12d1afd9456c27e5b0e0f35f98f7e84eb3e863adaa8ed0dcef54a13c8d3b55902091f5c681be50933092892cb5ee61f33298414cf287e826b3846d9c8c74217897e8af54a2e1d47c334a61a594831ef523422f9942e8e146892ac92fcd67554749b9b9f886cfb5e214458a1215db12cb148d05b08bbc2526beccd7d22d3619db98844a8c46b11b379272646a68f901e497945e122e3547d20beb3dfa239810f101b9c8bc04cf9f9a51b4e880b75f527f8c90e3d75332d6e68354cdd5d2026486bd9efa68a30f470f34fab029a3d0d61b5baf96b6069abdad4ead3bbe186667e2ecb1efb02ca884b2907b617d8f24691bec751b4f826436e92e0bc4f8f2319d109341e5d7b2d2c8252cd544595b5a3bd9c6714489b1817ab2d6278d9e2345aa5505374baf5653cd688265aa614aad390e24b01cf2e468ed210f9c27d0ac4a616d4aa4b2ed025eb71e161f3621fa72eb282f3d11e21f171249863031b837aa8b4e713d52dd2a2594b87cf839a90fad80bef66ef76943d6ac1bfe8d907b76b8c28ac416e0f1428ab4fa9514b4a3b5a3442ac64ff9212d1733e70ff46e9472bef989cd3319f1e4cd043d5a329a0c625f9982a2642d77d8ee0844ef92aa301f567bdbd19b87f7cbea27ecf7708c544c27c14a325aac024b21c4077211fbda49076ddb89c28d3b23fbb5f4d7b6507ec5d51af97d76c31b59b40fdd1f1fbef56f4470c1135be85ddd7ee0a98aca5df35242667f7c682e3da22b80dec01074c9f4b1e6341962b7fe63b3b8d2138d7d8ff340f7db72e851694a56f994987e871aa5b7c889b80245920fcab209fbf9b7f490db8fa65fb1234ee2c7ec6ff229216f42ec3abb4a68c71b4baa147166941f435f3a2561e35250494e8ed83f6714f21f350a0d97ce2f52905287f70bb45fbd94d4969c0b8eac1ed588b89ec7e5a736de90b57bb7a12be846fac1ef62e89653a8447a88ac03ff0febcb29c4c63500c75669268ef1e563ec10f30a955eab20415fa4a02fa9f53d6c5c085192738a5a66f15e4d4beb91cfca8e9dd6171befc9d66a2c3e49de691d3bab371745d27f7b48cc6bb36a55385580ca8bb6df545de5b4f9635b1b473fb5b5b2363fe479d3f39acaa6fe6814f16835fda50c7296aec4ba55daaa5a85cb8cac9964187d2c4ae9f3627f84f5edd626af63f921bc6eb45c6ca9ae325ea8255332a9270afa3332b7161ed44ff58095812eeaf6e0eaa84e4954bc7117e9b4b4d413d569ab61ae9b594226876fa944cb0b327dab33de47e11e7722c35fab42dfbbc6b5e979e030ba0b053234e9099e8aa723b09088c5a3144e2647d518b544e84e653c8c6d807db562f583bcad1e49f9493d791e61cd996ecdd4d26be3c58c563d19663e18efd2948e927098dfe91633024d3b6d93b8ecd76fcc67d5546ed4a7ef71fcc6db934cb57a4b2e5218b6dc8b2a6672ebf2d407eea2bd31d5c65c54602bb866cd30feca32c308d71a7f74630dcc2884306f403f73567aa0fb5bdd67d8a30e2da27deab9b7287a2d89acdb2cf748b2df52ddb017edca0da7ea6107b14b4bdf684055467a5d8b87fe1489188a21f7e435deb35eb5dd83b9f4958d90967f66700adb9d81fa2dec2b46872247da47df6a90eaab49a4ff8441fa5a9210d73284fe335224fe8c1189ab49b1fe3023661946a2fe2803890f330ef58178a43e6c27d4476d84faa346b1fa4083587d3845a43eaa1483c729511fa51c1b2ddb137753bb49f5d1867bd66b204bb3ad3b3b8bbebd57529d538fe8b7ce8a584db743a7ed57d26c17ec1dc28ddd483c87024b6e40a16635a2880b96706317d2c7401cd50592ab5123b22abba1453a6d8b7092a099ab5470dee548213c870f8b1f1c935ede96d31e6618ba2e7dc28c6e8f496a4d18baea0409fbb2136cd59649cdce7d6079b27eaac10133ac88356d24d8611287a2f79483be1321c62b99e2aedcc1af662962f06bcaee202216af670b1977deb02b1d06abd9da9a3e3a3e758a302f7decfd62b5f14f44710a2deb468a8d36c7cc425932691a7b3dbda45a7e05d1dedd1205394c449ffa40f27349b529358ad7cf8c7fb25746836e4c072fd6e0886302b7b0bda351f59554423d63b243d0c79b7b6be933a944f14ba52f16df545dbb6898528eef18305be87c69a07eb2c4516d2889141f3120c56b671cb13be91ccaaf5aae256855b7074b419ddff48e2a5e559aa6909a1c8a2d124275b3f89a82ca92d0c90d3256ba4ef4bd91ee3f855409a56daaa4d2765897c5b4b5411225618d12915f42b1d52fa9d8e6b7aaf85ab53189c0927333d793f6725ef208a23489c9adbe9242a80f4c26e29cfbb7e6ef0eaa5695de52a59576d2542b3e4d10013fb1a656b50e9ec5a53de208180ba34452dc975e5b0e1e88304abfa4d6d2a752bd705b3979190981db8ce5c772b358eea6060f05b54e4467362090af0de46b5760a95fc2524309912a41754995a46a51ad525d294eaace498dca8c2173ab8d93097addb907ab53af212c6b359344b0ada350414b714a1d19c7f0de84d0e76f395bd2d488fbec420df4c02dbfb462684d37d71aa8479c885bc348247c8bf4d0a2d2be672cce9d66d937cf58e8c8d5f2d2b4e255ceb6b8da70a34172df36ac69407f6cc3f7a6476bef577fc77deabf3450ffa94b9638e1842c800edd621591ad388f82221dbd2517cd74b10de91f671c6cef178e333bd654631939d95a1ba49c6c6f3548fc12fc2ef925f9b570b2d10d220505fe3345123e7481d527a5c0e6f762f1b572c6d9570969c441bf557e57fcd2fcae73725230ea1429fa02cb4949c4aa0e9f0d1016fb74d559aa8c81c635b54e599b6ad6d49e318da9aebe72851b1ba76ff7e131bc0f0d358431e606ee9ca9dc79fad614ceaf07389e35369495cece3fa13a5e9e204ac2370e70a93083be308880fcdcac5c5644cf25d1b3abaacccfb63973e1dd40267f4a00fb81eb16bcf7e4cf8962dbe6374b3b593be55d6508334c321d3299bec4dc4c31448ca0e16f0c2dadf71d47883694affb262736a73c75566a30834f9684f32172883c7cf957fbe3f13e88f6ef42fd19fd0217a7648c708e31a7addf824b0967600f387be313666a0ed7231f50db9d3d824207f74865ded01e2146e219873586b40537571936d3816d52984d47d7cf986bb209e7a6845809a3aba4c4752cdb6d449fde60bb29c27463afb6f438ecd324789f96b904bf2529e7de93e4e7325f3e92a6fb9aa5a2291a6676e7d90d7a4a3d4322c1bda31e2741acddd28f5ba24a83fd1ab75334223ebc833b7b51502800fa793cbdd9067b69d769ef3a2ecdae4464836a813a5882f03b2ad58faa344273ccc19df8d829d9945f9cfbcebd16532ea1cdb7fa2d587ab2a3d6a53316e8a17dd49bce1e4ae8137ac8827bc89839b48706ce6e0fd6e1511fdc375937d3f06d1a7e17b9eddf9c1df1a7007bb174bbac7c9e21291f4ed2a42161076922e63e5d36d0de328f19ab424d3b1a3c5f0dc6bbff4524ca5c267097416c9aebec7deafd7abc6932eef147ded07de13acb0f8718292ffbf1dfb02c6a3f180b89c483c9e54ed9789b78dff9d454c4e74517391979ae6fe2f3d158d0e359a9f88c546fe6ad4361a6c4512c3e4bc556023e07c2bd45bf23f52653141495f5535eb1465ddc85e8a79bd0e165746c13f83afc49ebacd4728ed3b4a4cc46f107204f5184585d2641f4cf52d98255b8bdf9b93b1a54d163de9e91b6e3c48ca540933c5fd5c5e03ee98fd7c781b58ce942a5911bb574b25d16414506905478bc1d91b98ffeab3f8335dadff24bebca2b3153043e5bf56bc29a817550618e10d34b29ec83fae0f2f9d6396d8fa4a9cbc611c7be73efcb8000dc979f5a3c5f256218a859043587e6581fe8ea2e8dfcf437a899b1a1655abac026c1e5c758589b94d3c0cc258fd5ea3f6508eb16f92df1a724f2ca557f3485982e7f3c4aa71d692d7de6d993d6150137f1ceff4ccefc1c6e94b565aeb393ccdaaa1dd3e51f4476d43acfa963dbdc7530b2016b1631f7e0094e1779bd49fa37bac85b62e680866535e39ca1a67fa7b74dfa81918f7c7ee8c80fcc8961066927ece1e7e27dbb2950d0af24ec4f9329861496b2f7871b8c911dc5c1058f6565fa4c92d0cf37a358610d8aa4ddf540a7719d2695631e1566eb835d7dd56456a4333eab0cc66c4c97682067c27b62c8d91f534db5445a7fe8be837d6b3627a6a4e0057eefebbbcd1c0756ff61c5be440be6a9dbca36fb2083cd1c8f6d5ebbaf74762c331d64c4617595b142b0ef65a4a6d042e7900fe1fb37c1cda186fd0669bbd0be38fd41f05572240ee13d8982b5b738c273ba91df4af13492604ce925f498d3922366d315b145e2d8f1f6f6d547ccc56bfd6e785f53804cc5ad4a2d784c931bbd795f91ca871d64bb0df1f371e58eadb4eb5df495406656d2e5043cbf2a6f3b1cb0722cf06b25e6145ed21f9438cc7320ffe6f19edbdede6fdeba9adee22bd71fce019fdd97501f3e8caac956b435993115b61c31fbada6c7c8a4660d77222ca79516a9320624f8cb39194723cec98a6f145f25e7f0d2614f5d65b52a643d0aead6c374925da2688c40fc9901a3ad363720efe4eff2b89233bb45259412959895e86daaca5c4c2b7228c776e005dec0cd7c0b39e7c35aefb5de37fdaeaeb028ce99d1482d9d087e4e630af2a78c627fa952d8158716b1fcaeb8b85eca68fde34322c3cd36a44a5e9187df12a86b6c7d5b6f6cbf5a70c5e746fdb167c0d61ba03cde152bf0294a4a035234e2181bec8a15f814858bb1434c06ec8a59d8152bf029ca95fa1e456308e14f35f0bb62d8a5f8ad4c2fb15eb3ddf3845d31ef23b8d652bc52eca154ce9522ab72f04a617b70e84a31253db8653fb79a075e915e9f64a07f85bd5623ac77655284676ce6264ba1c831fb26caa63a771f0f94afb4fe95ecfb5dfb640a99dc9b274fd991c75a603ffd39cea96a02adca9a5006cf125513a1b99a109b5495e495c89f8663eb937662fb33e586194cff62a90d94aa49d5ce6855df3aed095bd36234116662c182bbcb93434dcef7a3912788ebc29a19b0c9de5182ef0ee0396b45add9e6002fbcf28da3ed784e9035c9da613692719e488aeadb77b4cc09ecd7b8e61039f105ce873928390e38149eda0bcfe388c2da3ff468152575f66859e1e01e6d20ffe8aff125b972fb2991eb2dab717039ade8fee584f143ce5201a621e3776f6829febfe1a4b911b79e1a8b3539f3373c1f119a497a911ebff50b6cd35c0f33f06fad52962b818f82b2dd195b964f8174f7acd76df7f8b7c1c29e7f2fff265858dddf970befaecaaa8595b64019c4bd7f7946786bf5144f23170b6f9c0cc22cbc4f08670bf362b7f29cc3a190b6ce2a67088d590bad3c403de7a27c9e2e81d75c594ab6f96c6d68b3530ac15fc974beb38cec506a713b5296abe88ebbf83e30a1f9ce824f22a2462aa770f2153bfdd3b2503e666fb1109ea020b72f129effac80447304e74f161f318bab2924d29b63a0af732a96e8b4538d0a298adcfa02bd732fca56266a746cb5b152144eda1178b62197a456b6e11eae952f91b370a044fe757c47e8524cb5b15174c4bcd90aad44eac314c26f86b892ef35f4e9b45fd78e40b74610da3f47b47812c03333fe88f19f115d9e38b6ff6e76162bfb96e46fe3af0779b1ef8bc733b839b669fd84c88efe7867de6356e7fbadb2ce31979cb330b2cbafbb66e45ea1e7eeea23b29068bb2ab9d00a76d7a29e1644e8a25d3749ecd82b065f2195f78cfd52fb175b1f08713094aae54a6dffe2af2dc23bb38ea16fd59a3a9d1ccada47d7bbde124ec3b79306bf6bd377e05549da8ed7a5dbe3229d77563a9ca58fb70d481f706d407a7443987d6e3aa537ddc4d24b06af33bc64d7fc52e9c26684253e6b7195bf6a8e861132244d2b4dea77b85a7b6d4defc02e801819dc8c7b37b6a9ca807b57e82fd79b5cf45ba861e41597a72a685764ab6a15adec15a992fa95aa667ace2d68b5045a0f982841583611eca006c977c9e5d90aefe2032e5fa9e5d76a693bf46734c272068b351234b26540bad20452926dbe6e714a09328e431f7909761a394fe44be4879385228b7604be013e2e7c4997e87608fa489767bd5e798fe7c3dffbbc1d12d94194c17fa0b7c4d7e6c6e7f1bcf22bdd5e89b835ebe34a9fec2ce54139580c53939df5cc12c885ef494fcb919943b875fe0d62a422a367c411b3be8142816d930ce0950a3664db72c41cdcac073ddf6f98f1e6b4d726bff248daf855135f96b667178f6fc352289868c4b728730aa3a57b2b450a32eb0e6e9fe94b713d8926352b4cbe28db1cb8f20c93e933f2784b97736598c40c7870d8ab9c24f4d5fb8a2bb513ae8f49777fcf83dffdac6cc6774675d1ca5898013cb678f72f57ce83a7612f7a1f9c37e1291adf0e55c845f8742c9891a65624fdf17d0f2f9d3ec2036d6b004f975c22566ce81aa14626a48f188658cfd18891ec3811085eb0d56bc35deb30c3dd95a6fed12b2cb8445d6d503ddf2f2992629a18f9710a3f7633cde27146873ea4c1516510f9642b89ed5e9a155b85f935cc62bf232a3152b29af82e51846604f6d3c65d4ec0b349757ca76856527863a5a8da5e292a86f5b5b459d05d1821a62e6a6418a390664975f8664f13779fe84a79026e015e2bf896f0eb0c3f873331ce3932e1fc503ba51b611ee5e7f965ce3cc7fb78cd71a6ec5de44c09b83b34855ce23a1be81d9a92fc57d75717763e05fc02e4a5dd964b97da907e1f8914f61e8a16db45bbb4e46c1ad9452e39163a4b46760a1a3cbba4710ee17fde12494b65f8e6ed5ff0a9a7fff9d34891310d9173a6fbb521ffc61e94993b9c9a64f6039f7da659a70d1f5e89c4f387a39287a620920c7fe822516d6b20fc8793c83a7bb8c3bff155c8af28f4130fa7e879a4881e2d169962e811e7f17b11e2bc8889a1479d07afd108fb3263d41345aacb63f73977695fd7ea42fa2d56a9f8de91a2d2cb0a4683b8d58893be25cad5d36d6eed7ed7868aa314e4447c1f86c2b71915e43a11b43dabd2d57632dad59fcdceb693c9fe4bb544497c06227cc33792447f2ebe6da9386b4125321992d7bbfa63a91d295eed2186cb26158ba7c09ad9839fabccf430b1e8f3a2cd2ff8c7372026664cee74732bfe5aa2682a0adfdc88c2e3eab92f224e23ff1c29b26a5aeff1ed9955247ba1d7e2948b99e2940bfdc2cbc5cd20072a7f861591f37f2bcf83dee115969b398c139c2b2c58fecd4ae4415a20003ecb6c989972473714cf26359b4a8a4fc0dcb064f029c67a582960469ec85259b2db16edc4db2992890a2a4b144e358bcc959c5ff4eb678699669f974ca764ebc39ab38bca936f6748269a52c08e36dfaea426aef79e8b4b8baaa94e5185b9f8452f6b69ca8e7f45689168b8e48902fe1dbc739e09e3e619f060b9ba0666dcfd062c07fefe08d7ef5ab16e3f2051ee95617bf83eb05095b8e3842c39db3cb4f6db29d113f97595b7d615e91b3539ce9e7b03fc05894ee3c1f0169bb59f88c1375714e23d4f2864c85b111f17c98e0a46ac6f082a453b8b3e2b2c9d0576d2a7d8d84d949c3312cc969aa20a54dd3a8a184ba9cbcef679500568aa591f4cf595349a0926b7c65c89cee6a903ec7d64b0f541e9dd2d46421b41fe69c1d898ad8bd85121a8aa485d7668f8d6d96408f88365873cadbe0df72a12f8dbfdb08b125519e9d5a093d4ad11994be8951651b6af32847b4f30b15777a4b038ca22ed43fda330c72db08fda98577c02cf4578fc58284ddfa8940e78fdf39b1736614e6106f06957592430221431977ebb4b546588f0f8d302568ec4fea41cb1a05edf9b4b67e3dbf4f44e23a265858825b328a757434eb08e79bc7fd0195b974e3bb01f17ad9ec98d603f77670deba5c248892c52e419484d35ab0328a4301bd19122790a8402c12b1b4721bcaf156ba9bdbe560b893c15526f09ee9bfd45781f4486581f6cb8476acd78df98d35ace1635a2080a794e355ba401c087db2b49d12cece9992bb01f28fd54210d901c32eb62d41f51d8bf454c701d65dd79ea1e2ec1520ba453cd8b67eb82777cde1fd1e260b09e64ddaed44ec47de62da15b2a45f8cbb792f825e841ea88790b1511823c611f3e9b89c163bd240f7fcb35d0cb50e26a2512f9822d796ed2cd2cf4ac73a6716da70224d5469b08fb07ca4aec3387fcb3cafc0ef0dd6add6ee3f24c5698b5627a01e5f178bab40fdf928fbd35bf1d97bd0d3687fb4d51d088a61644500128d01c3d5b3c8142ba718a2289887847e11b0c1a54308b98a00bf6390136295b1fa19db89eb34fbe5ebe4e5be5275c9dc5b4bc61734a18986d79b205f8cd2c883eee66a9dbe74e54078c23066ed18731f8167d188c658796be5c395c24686065ec26dd1385aa28c674c49c6d8ca0a217a8fed65b72619be72fd95938f42c177aca152a29f4455d25fd166728085d2fa97785a6a00b255fd43a431a74ace43b57da12bcbaf42f71a526e3f0dd25aef48d386c7fc695ce7db573f319577a210eff56e84adf8bc3b585168b347ac1afcb33334a0a0f427bec6ff5e65dd8f615cc9d7c7c49613dc2a7f2d72dcef059b429a3bed6196a41e9195fbbd2ba5062c677ae349288caf8c995e64b1019c9aeb4202282ea7324bb52a740b8c791e84ad740f89623d195be04c24d8ebdaef464081f77ec75a56f84709663b72b9d813072ec76a517e2f0bd2057fa5e1cee0b1a68150e378dad05bb10d16d9522e7f8dac2078f2fce5d1a8339652a314fdc575b4f614de7f8b8a57c75ea02c7bf34d6adc4995e6e5454f19ca634d609a1c1ba814303ba814303ba814303ba8143437483e3fa54e387fcc80fe1faec10aecf0ee1faec10ae38c46bcc1257aa508b2b5dd030573aaf51cff07d1ae7a651e7ae1ce3e3dd342a6e8846c50dd1a8b8211a153744a3e28668549c9b46c5b934c895ce6b50a22b5dd020573aaf417b5de9bc86ec76a5f31abadb95ce6b58902b5dd03057fa411cee19eb4a1734ccd57bbcfdee38e60cf3f6bbe360d448063c67ec1f0bbedaa71334c2db8409bc7facd3383de43d16962a13d1d64a29becded4f05392e68e9269b089f33da504443080a5266e691d608ed79949937c6ba237797cdf90ddef1ef9cdf2cf35a84df24a91abfb6384314f175adeb5974c182e7f22b5ada5619ec9afb86f1b6e19af9166d33616d6b29f93a4ff503b60a3e769709eb196bcfe86bb10cc470edb7fec4f54fe9c2cc8c5d26d09587b1d6f5e67d9d57fabd6015cf1f33d5612bf94d53eb0c633d85b2b7a096df52f9f2cf1d33617d63b0be8de0ca5f8cb23853b0c66940e35ab89863a6d2e7b02645501d8e2e7e349ee76e66dba95b6037bf0923fc1c8e8b1ac2f3d78b53b8349c1241118e5f393e99cab34209185f025b39c35bffc57e6e6c67cc1ee0fec889eb0771dea3b3fec8fde8e7bd0707733f3a6b30f7a331380ea7e11a48822b712afd98336d70cdff38f5cc415cf31bda819ad31a182e6f5fcc1f6bee3b957f6c70cd7d31836bee73e3de7e6a0cc77dc6bc41edfa52ce4b121b95816331675c0327e5990b359c34b33ff7ad7ba52ebf0b78a49df8ba9a8fc3357daeac63f99198a11dc8f34843660dd6b97c2d3e1570ea5cc8b5019dc31a26bc11f62535fc935a24d75ca8855da64848f189a8df4ac0ee65df605f7e7e33f8fe649747d0d7262d8dba3cb9bf37319710ce31b25095c9750bd951c5cc77de44cae1ce80f9b38e7b55aefbab09b55506e7d770c7fbaa18671ea4174e0ebb88a0a16f6a2cd056f1074664d06669159b7a51c98fd188edec14937246e2bff87df4e4662c3529c1721fb4f0b91bd049edf13fe486b95b025631c74b4b684a1a2b896db9d65c5b8fb07691b0d3a9a79cef1faf89274808c91c34c7c903d784f93cb99908c2bc48096121847edc739d9b59da2b1f71f67cc38f7ccfe39926b089ef7fe709f0e05dfbec3e5233382cbe22d7d4f3522e6072e9b7a9604273a9d1b933572dc03e6149a3126dcb6542c297742122688d058f9fe067f7e0d6973e7f2c775bd9406c59371f5bd2a8451706e50ee872c65344efa0f8b2dbcef806b4a27650fedfb9f845258d116817d43f7d499768ca607eb79ce929a87e30bf1bce7833ba6471be3d4cf8d9598b11e96ae5ceefa27fc2b17db3b1968fdee78c2d6be463b1a44c087e03f48cc599d67416a71dd5f67223bf6f9e6263d70832a844e98bb056f8834ec0aac4a5f82f61d0d716613e6fc671bf3e8ff72b99425c96958fc312b5087191979d7114f19df09d1f34024ba3bde40c37f1e10e67f8373e0c7c0853adb3edb067cfd5fd8bd0e437086dfa9508e2795faa755a5dbd2b77a35beee33ff1b98fcefed4f2c7dc36b7dc4de79cb97defc3dbee961bfde8cc3d61506ee7d307d583668eef07763e78479d9d259c217eb579f64c23d9c2cd6a0b4b1a43a086fc7f99b4e6a9b0ffdad77d868fc7ef6d8e7e2f9b9d9d85f344409e1d27b641afe1901642577e6360143147bd4d7b9a3ba1e4ce201aaaf02e2473c9b3f340ee06d96c3cc2d959fa1452c47d95e5caa7dd77c48cff9e42a9168f2adb6d47747eab8897903a513a5b17626a50a5c26e7f4db6d9b99aefca1da9b5eeb4dd5325f637ca925b84da538e0f70b595e3da9f9d0773b7e8ba8308929d000bf655c49f258473a2329df68942bab001f93314cacef2493c1f4be66efca56016be71c8df34fcdac297c0dcd5e5d411bea466172f9bf6d3270a8b671321f85de3e6d944507616438a1bba1ce26924d207cf21d86ea9a8e51e11a20e6843c5b0ebe77af59fad073a2c9bd263dbfce5871c78b71e6a086ec73bf16d5afa6e2512cefb8a856f140b89b2422dfd50378219dcdc2d12eadf21bc6366893298e14bbad1d0f8c87788b2a9cd614d50e6aacd43a414faa39a60679a476bf4d1ceb9b2ec6276b2c86763d6f4250745e52b4b0a26a3f275fe71f588cde81ee1af3c8522ba47a0f43c6530fef64ffaf6a402ef84e9544ba47fbc163d1523daa00b625b48d953a79efd9ccdb08f507dee0f1bfbea886f11ce53025556db420805a4a8035a3debf3d4c152a40b79f6d4b331787dd085602f1042b3e0c946c98007b55dac3a85570de1bceb6de11d3b834f34b19f981c24757eeff9bc3fe94308b32ff7c65bff37fe6d0dfec21c5a7da5923b2357ffbdf25d46bb87ea40cac9f8ef8a18de95453adfccc822077f4929d8938dd722aa94d32225778ef626d64df57e4aa6d776707f7380695092dcf9ea778234a7f19b073e8e3cef7c07af3f25e86f59296dbac6a76abee14b240827d50def96d21d3f38bf2017f9b258377db9ef73336616aa12d5fb2d32eeef4c2849116e1d3347c72a8b618f2fdbf728bdcb97620b414a3feefdfe86fdc5e26a19d28f9f8d74e3d9b6f1481da820585996a87ab352fc79b1faf069a43e7a1ec611628c0d227fc94174840997f620d9f781cc24a15f137e0f5c092b3c896f0090a7842f7a7fe7dffab8ee165c37cdb1eef2bd3b50f7aab4a175fbb9d7fd11d47de03cccfc2bd2f17728f8ab14fc2d0aff65cafedcf5a101287319bb4c23562cfb02b1bdbd6f11a1e19b3e40ecf22e319d2517b1d4adb7e86dbde0ed0f975f54e2f7c6ac69f471629e2e3a681f4dca65aebbde19445009c53858f9b7c7c347f7a26ad30791576a694a2e6595c0577eeb2daf7975d1d67aaa8f6d9c27ce5cfc58168ce65be11b3610ec2f77c59f3628703d7a9b94d40e973ca75cb695352a8f135a76d49fd04f6f579b7a23fb477daa0d3ff51a41b7364af9efe6ea93b748f71795ceda0f5e826e2e39ef9805d7229fb7cbe275daf5de6b2d2f55b84f2762a96f8f9bf2365ae45fb1ca9388fe86c2df9fbce5a5ad8b4eadd56949ed33b5382cd7a65bf8f8672c0a5f03a25f3579f78fd6c5281693a2eaa526517fbc8ebd92a77b273cc3243ac584431f8b944bf2e8372822d4cc323d0e3aa512d11b298265da1ca1e6708a8a0a5f6a1485671845d53994985552a25946baaf11559915b0d3839d0ea2bb1ac1f36973404c01292acc5bb6b53ae2165a013dd0fb177ac9ade18a8c0dc8eb845c8befc4d092ab9e5e5a7af8550fdacf0329323af169bdb8118d9428941b107f5feab2b25f9909a5e5910aaae92daf2cfa793bb6cae1776430c749efc868d24396b9988534b67931ca5c82c7e1315de2b6ef950a9f6ef18a6d51db761c5f5fe683eeff6d123baa17346534aa3f1697136b586ec49ced78f614d97d18ad7594bd938826cb9fcb69b1b0cd064444e3be4caf25a3e7fb10315eda2f6ac7de1b2bc9af9df0f3f5da29f7f8d40f6b93ef31b5a476d735f6950d28d0b8eb73a1adc3aecaa0ade2abb240233dda03b1af406b35b4e110f456344a64155a252273494d35d515c94ab3c4f4e7dd32bc96e01b19a6f7aae3de4778d4c32767a1255b59e8c36d274c3c5ff28e04e41d714784fb90e5fb508ffb9055c2789fa7443817110dad115137d8e671a8e358a031ea8e8232a0544ba071d9f7176a37dd190b5e7122b4c22b4ba7b50ea7ae2aec3ea8aed6ebb85007714704755077906f342df54064f4e8b2a0e835f7bcb2303f9d36f56050f4c63bd72db85f701f103175d1bb8e0d843e3c787f6bbd0dd6dabf5801d6fa711678d86fe942af6c659781a58e9073da4c0fc396fab6e4a2f2c256851cdb2911ebbbafeea4eb36c893bc3d28c04aab0ba077b8fb6bc74eb2e69348c1d96854acb5e1fe36bafb4bce460df7b7d13366b0504df8a94d8462b312d12d673d84af5bff3c60a504582953abe0ac74ef202b0d98ec6ea56bfe60a551b1899c95ce15ac3455b0d2b983fa302af67eda4c4b3cd0fa8007ff8d3e67c6132c7b4e2262974844bd5b376d0d270b44e1861cb0d643a26aa650c482dd9717ee2fa0bb0c88d48a94478a321713dbc3ed0694b84da40c352b7ccc48b71d6bd90af65041545e8524bcb91971d6b89c92b1f13e88dc0cb64cf5fc855e70cbc369cbf86b173aefaa14f4db74554a17ff075ba664822d5b17d87f078b33711aacbb23a1b33c24bc2d2b562f4772cbfc9c60de2649fb03d8daed0f80163f60ef21a21fe76c72e6e65db54fff3e56b2a9f6998bd76b37fdce5be07796e47b6bdc2c90933097b3403d67813bfedfb1408e2fc3c99f0716587c7f0bc4b9380b94519715ab4351d0c1ff608112aa4991f1a060815c1d3ace028d9c05e60cb640cc4fa7fda986b740be072e550f68d1c69adb216065f18a78b0323df8506fb1d2a6bf5cc9cbcca74fb522f2e6ae6bd8b31f730562a3e833ad22268608f97e94829c38fb6cdee85dfe54e3f1f0b395089f8d1141a67f31daf5654a7c8706f6ce176aebb4d701994bbab8bb6c788febf5b5c2871411db5d7fb3440ca339dbf49e829a387b3a698f0cf7791f29baec6ff5c77faccfdcbad0376a9beb7678b44e7b01ac204b1c1edf85b0af37da7a49d9755701bd3f26af1ae2786fa6fe4e15b7439f4e5646e2bf9483ff46967307ae1ece7ba79468338d25d26906df7549500a5fb74b37d333d6e49fc836bbeebe041323f9349b34b04de9c3fddd38e998adf8fda197e133c3c8e681fb1c993e2c388015e0c7659b5b2cce7229625739112e17db844beaa2679ae8228388cf1342f039de4763b60a69ef1910cf59948a79b3e6c9e80c706624d9e64b96c1a19fb85bb9aef7ff28ac7d52dbd46669a7705be0145f03d55b4c97af9cb5521f617cf872b2c9c65254807a86fda1626da6867ebec5b91fe8e34b357d95b9049fb1603fd9ebb4c23702cd2c544fabf716efa31e2363a68f6a45e1ab4ee3dbeb8fa9a79d0d30c5d0ca1f91be42fbd892b7d5550d134ba44a24a78653050d47cc3cdf20fb562bcf39b281d0ac000f2daebd6a8b707fa00bcffacbdb9c6bc073cd615b04298ef0b26bafeb3fa510394f7fc0f4f0ae26695245c2d0d94dd867dd4a4c545a39dfb12e2bda5fdee9182e092eb0ce95f7d1720fa48f3004d0a09a998f27fa74594aa37d92fa2368738858fdfe38a2eb9cb852f2d8f4517f22f47f37c04e840891dfcc2e2a8db61667f4ea2da687ad39e6be672cf8fecf4fb5facae8c7f4530cde1d79ba109d2d8ec92eba541b06ff7e51cb6bf9c05f596565dbc5f88657a8815e4ea2e992ac48e7373ba82e73b13f29717d4f31b8ccb7242e433f47227e37226d1e2819f923aac3b77cf00d4fe9f5fd86c02b5b98819288bc5f6de83b5c5b30b3c5f96edb07d73c70877374fa2328a74f90ec4e7f3c2def429334fe4bce4692a6d67572ee1e4a8bb36d424d4df76d57c2195c53203370fb7170995bf72d13d030b48cb09fffb198c6a543f533378fb9e6fccbb5984b8217e60252aaba9c7fc5ed04feba1bb435c1595e7da8f25bf134fcd729899870524c3825e0cbce62ad3ec97d5cbe4f2abf99c55cb20c951349ee2767d9d1a1729626aabdbb5069b27a24fc3bf7d9ba5fe7aaf1ddfda7d5be5de8d7a7d5a3e0395dad847f37a9ae1c4d9c71a574c58c96a3c9aaeb47d7a8ba8e6e54f5676721b4f06387e31dc07180df270ec76ac0278033071c8e6f000b0f391c154025871d8e6ca05169a9296b939302925f7ef9a597c77aa0a792d7a66c58e50aa2bf456b662dd1ae08d0acdd90fc72404c5ae2fa55014fbf94948c849f339d4bc1094f04a4ae4ddd10b031f1e50df004855edeb86ec37df3a5bdf4d23ae421f049dab866ddf3f44b2f273fe1c5456d5cbb2a716d521a48368809fc447f4659f7a38b23ee1fefa45f4dfdafd3273df15fa7c74ee6699240df1468b1403f11e8f6293cad12e84aa1dee593fe6bfac2cbc9892fae7b091acbf7c7cac48d691b0256266fa0570d0aa7bd949834387dfd06e8312e9c9a96969c929806bdb57ec3cb1be90da92fade5e2d7a4ae4f14867880d9d0788ee9a0f000d3ffc98fe2fe855961e84f3a382012fd8fab71fd64ff2799c5ffed9ca1c2f83c22d071029d28d07f08b447a07de13c8d13c2db85303c21022002881f4648f2978f1009900228800c2007780086013c218f1760386004c01b6d178f04fa00945700f501ea0b1805cf4a80dfc3dbd168c083007f800a3006f010e0614000602c2010a0068c03040182012180f180098050c09f008f001e058401fe0c9808780c3009100e980c9802980a88004c033c0e7802301d3003f0246026e02f804890350ae49c057436d068a01aa031d0562d600e602e2016300ff014e069c07cc8f357c002c0338085d00f8b802e86b425c0230e100fe1a5806580e510fe1be059c00ac07310f73c20019008780140039200c990be12900258054885b8d580170169803580b5809700eb203d1df032603d6003c46d04bc02c800bc0a23bc09f01ae075c01b804cc09bde3bd05b802c800e900dd0030c508601e4008cc06f33c004d802c8056c056c03bc0dc803e0fff201ef4cdc815840013c17028a006640316007f0da09280194027601de05bc072803ec069443be3d80f7017b0115804a88df07f8005005f83be01f800f01fb01ff047c04f93e067c02a8061c001c84f843000be030e053c011400de028e018e033402de038a00ef039e004e024a01e700ad000380df8027006f025e02bc0d7806f00ff027c0bf80ef03de007c08f80b320c3394023e03ce027e8a19f1fde817e81e75f01170017610c7e03da04f92f012e03ac8066400ba0156003b401ae00ae02ae01da01d70137003701b7001d80df01b7019d802e4037c00ee801f402ee00ee02fa00fd807b00c7c3dc04b050985216f164dc0221bc18ffb3fa1787e355400ea000500ef808f019e01bc045c00dc03dc0f05f1d8e870053013180c5801700eb006f02b6024a01ff001c057c05f815d00ee803785e7038548073f07c0b2083e700401860062016b014b012f00ac00078075006d80ff80af0e06f0e4728e057783e0a6807f4017c2e3a1ce30093009180f9806701ab016700af01b6007602aa003f03ae028e00ee003c80e752c05a4016e01dc03f004701df03da01f700dee0cb8f034402e2006b01d9807701af5c823a003b010720fc2da00dd007f083b830400c60392007900448bbc4977b0d70eeb2c3d10ce804f40082c1ff9f013000de071c052cb6f2693f024d139e71fae266876315e00dc049c0558843405580ed8032c047cd7c5ec3209e98fe0ef1012dd0678030789ed33c90fe06c47d7599478f001c87d058418f0205aae66852e286c4e7335293c0979bf964c09480e0e0804151339e0c98c6e786c57edd0be0af8d7d326049d4c2c5cf2f5afcd705cf4ffa37f18f3d3fc5b92e8e0d9cbbf615f0189202d625be9cba6153a0101f7bcde198058811e8daf681f07c01380e874307210eb0f9068f77010601af5c1f08e36783909e3d08476f423f102291187e12e147bafda4ffe147fd0f7fb2ffcbbf1edae96720dea92204df8ae07d9d00b12b9dfb4d4ce5c36d022d5b3d34fdffeb3fe7ffd76478f8d77f9ffcc09ad6f68b241af67f55a2fffdfdefef7f7ffffbfbdfdfff9f7e4b5fe4d755275de5465f75a39bdd68b11bdde7460fbbd1d36ef4bc1b6d73a33d6e54963694fab9d1f16e749a1b9de34697bad1556ef45537bad98d16bbd17d6ef4b01b3ded46cfbbd13637dae346656b86523f373ade8d4e73a373dce85237baca8dbeea4637bbd16237bacf8d1e76a3a7dde87937dae6467bdca86ced50eae746c7bbd1696e748e1b5dea4657b9d157dde866375aec46f7b9d1c36ef4b41b3def46dbdc688f1b95bd3494fab9d1f16e749a1b9de34697bad1556ef45537bad98d16bbd17d6ef4b01b3ded46cfbbd13637dae34665eb86523f373ade8d4e73a373dce85237baca8dbeea4637bbd16237bacf8d1e76a3a7dde87937dae6467bdca82c7d28f573a3e3dde834373ac78d2e75a3abdce8ab6e74b31b2d76a3fbdce861377ada8d9e77a36d6eb4c78dca5e1e4afddce878373acd8dce71a34bdde82a37faaa1bddec468bdde83e377ad88d9e76a3e7dd689b1bed71a3b2f543a99f1b1def46a7b9d1396ed4b92f77083fe7f61c5d946721b7dfffc9db90ff07cff9cdf8' + ISP_PROG = binascii.unhexlify(ISP_PROG) + ISP_PROG = zlib.decompress(ISP_PROG) + + def printProgressBar (iteration, total, prefix = '', suffix = '', filename = '', decimals = 1, length = 100, fill = '='): + """ + Call in a loop to create terminal progress bar + @params: + iteration - Required : current iteration (Int) + total - Required : total iterations (Int) + prefix - Optional : prefix string (Str) + suffix - Optional : suffix string (Str) + decimals - Optional : positive number of decimals in percent complete (Int) + length - Optional : character length of bar (Int) + fill - Optional : bar fill character (Str) + """ + percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) + filledLength = int(length * iteration // total) + bar = fill * filledLength + '-' * (length - filledLength) + KFlash.log('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = '\r') + # Print New Line on Complete + if iteration == total: + KFlash.log() + if callback: + fileTypeStr = filename + if prefix == "Downloading ISP:": + fileTypeStr = "ISP" + elif prefix == "Programming BIN:" and fileTypeStr == "": + fileTypeStr = "BIN" + callback(fileTypeStr, iteration, total, suffix) + + def slip_reader(port): + partial_packet = None + in_escape = False + + while True: + waiting = port.inWaiting() + read_bytes = port.read(1 if waiting == 0 else waiting) + if read_bytes == b'': + raise_exception( Exception("Timed out waiting for packet %s" % ("header" if partial_packet is None else "content")) ) + for b in read_bytes: + + if type(b) is int: + b = bytes([b]) # python 2/3 compat + + if partial_packet is None: # waiting for packet header + if b == b'\xc0': + partial_packet = b"" + else: + raise_exception( Exception('Invalid head of packet (%r)' % b) ) + elif in_escape: # part-way through escape sequence + in_escape = False + if b == b'\xdc': + partial_packet += b'\xc0' + elif b == b'\xdd': + partial_packet += b'\xdb' + else: + raise_exception( Exception('Invalid SLIP escape (%r%r)' % (b'\xdb', b)) ) + elif b == b'\xdb': # start of escape sequence + in_escape = True + elif b == b'\xc0': # end of packet + yield partial_packet + partial_packet = None + else: # normal byte in packet + partial_packet += b + + + class ISPResponse: + class ISPOperation(Enum): + ISP_ECHO = 0xC1 + ISP_NOP = 0xC2 + ISP_MEMORY_WRITE = 0xC3 + ISP_MEMORY_READ = 0xC4 + ISP_MEMORY_BOOT = 0xC5 + ISP_DEBUG_INFO = 0xD1 + ISP_CHANGE_BAUDRATE = 0xc6 + + class ErrorCode(Enum): + ISP_RET_DEFAULT = 0 + ISP_RET_OK = 0xE0 + ISP_RET_BAD_DATA_LEN = 0xE1 + ISP_RET_BAD_DATA_CHECKSUM = 0xE2 + ISP_RET_INVALID_COMMAND = 0xE3 + + @staticmethod + def parse(data): + # type: (bytes) -> (int, int, str) + op = 0 + reason = 0 + text = '' + + if (sys.version_info > (3, 0)): + op = int(data[0]) + reason = int(data[1]) + else: + op = ord(data[0]) + reason = ord(data[1]) + + try: + if ISPResponse.ISPOperation(op) == ISPResponse.ISPOperation.ISP_DEBUG_INFO: + text = data[2:].decode() + except ValueError: + KFlash.log('Warning: recv unknown op', op) + + return (op, reason, text) + + + class FlashModeResponse: + class Operation(Enum): + ISP_DEBUG_INFO = 0xD1 + ISP_NOP = 0xD2 + ISP_FLASH_ERASE = 0xD3 + ISP_FLASH_WRITE = 0xD4 + ISP_REBOOT = 0xD5 + ISP_UARTHS_BAUDRATE_SET = 0xD6 + FLASHMODE_FLASH_INIT = 0xD7 + + class ErrorCode(Enum): + ISP_RET_DEFAULT = 0 + ISP_RET_OK = 0xE0 + ISP_RET_BAD_DATA_LEN = 0xE1 + ISP_RET_BAD_DATA_CHECKSUM = 0xE2 + ISP_RET_INVALID_COMMAND = 0xE3 + ISP_RET_BAD_INITIALIZATION = 0xE4 + ISP_RET_BAD_EXEC = 0xE5 + + @staticmethod + def parse(data): + # type: (bytes) -> (int, int, str) + op = 0 + reason = 0 + text = '' + + if (sys.version_info > (3, 0)): + op = int(data[0]) + reason = int(data[1]) + else: + op = ord(data[0]) + reason = ord(data[1]) + + if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_DEBUG_INFO: + text = data[2:].decode() + reason_enum = FlashModeResponse.ErrorCode(reason) + if (not text) or (text.strip() == ""): + if reason_enum == FlashModeResponse.ErrorCode.ISP_RET_OK: + text = "ok" + elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_DATA_LEN: + text = "bad data len" + elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_DATA_CHECKSUM: + print("aaa") + text = "bad data checksum" + elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_INITIALIZATION: + text = "bad initialization" + elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_INVALID_COMMAND: + text = "invalid command" + elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_EXEC: + text = "execute cmd error" + else: + text = "unknown error" + return (op, reason, text) + + + def chunks(l, n, address=None): + """Yield successive n-sized chunks from l.""" + if address != None and (address % n != 0): + start_pos = n - (address - address // n * n) + if start_pos % ISP_FLASH_SECTOR_SIZE != 0: + raise_exception(Exception("data should 4KiB align")) + count_4k_blocks = start_pos // ISP_FLASH_SECTOR_SIZE + count = math.ceil((len(l) - start_pos)/n) + count_4k_blocks + for i in range(count): + if i < count_4k_blocks: + yield l[ISP_FLASH_SECTOR_SIZE*i:ISP_FLASH_SECTOR_SIZE*(i+1)] + else: + start = start_pos+(i-count_4k_blocks)*n + yield l[start:start+n] + else: + for i in range(0, len(l), n): + yield l[i:i + n] + + class TerminalSize: + @staticmethod + def getTerminalSize(): + import platform + current_os = platform.system() + tuple_xy=None + if current_os == 'Windows': + tuple_xy = TerminalSize._getTerminalSize_windows() + if tuple_xy is None: + tuple_xy = TerminalSize._getTerminalSize_tput() + # needed for window's python in cygwin's xterm! + if current_os == 'Linux' or current_os == 'Darwin' or current_os.startswith('CYGWIN'): + tuple_xy = TerminalSize._getTerminalSize_linux() + if tuple_xy is None: + # Use default value + tuple_xy = (80, 25) # default value + return tuple_xy + + @staticmethod + def _getTerminalSize_windows(): + res=None + try: + from ctypes import windll, create_string_buffer + + # stdin handle is -10 + # stdout handle is -11 + # stderr handle is -12 + + h = windll.kernel32.GetStdHandle(-12) + csbi = create_string_buffer(22) + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) + except: + return None + if res: + import struct + (bufx, bufy, curx, cury, wattr, + left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + sizex = right - left + 1 + sizey = bottom - top + 1 + return sizex, sizey + else: + return None + + @staticmethod + def _getTerminalSize_tput(): + # get terminal width + # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window + try: + import subprocess + proc=subprocess.Popen(["tput", "cols"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) + output=proc.communicate(input=None) + cols=int(output[0]) + proc=subprocess.Popen(["tput", "lines"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) + output=proc.communicate(input=None) + rows=int(output[0]) + return (cols,rows) + except: + return None + + @staticmethod + def _getTerminalSize_linux(): + def ioctl_GWINSZ(fd): + try: + import fcntl, termios, struct, os + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234')) + except: + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + try: + cr = (os.env['LINES'], os.env['COLUMNS']) + except: + return None + return int(cr[1]), int(cr[0]) + + @staticmethod + def get_terminal_size(fallback=(100, 24), terminal = False): + try: + columns, rows = TerminalSize.getTerminalSize() + if not terminal: + if not terminal_auto_size: + columns, rows = terminal_size + except: + columns, rows = fallback + + return columns, rows + + class MAIXLoader: + def change_baudrate(self, baudrate): + KFlash.log(INFO_MSG,"Selected Baudrate: ", baudrate, BASH_TIPS['DEFAULT']) + out = struct.pack('III', 0, 4, baudrate) + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + out = struct.pack('HH', 0xd6, 0x00) + crc32_checksum + out + self.write(out) + time.sleep(0.05) + self._port.baudrate = baudrate + if args.Board == "goE": + if baudrate >= 4500000: + # OPENEC super baudrate + KFlash.log(INFO_MSG, "Enable OPENEC super baudrate!!!", BASH_TIPS['DEFAULT']) + if baudrate == 4500000: + self._port.baudrate = 300 + if baudrate == 6000000: + self._port.baudrate = 250 + if baudrate == 7500000: + self._port.baudrate = 350 + + def change_baudrate_stage0(self, baudrate): + # Dangerous, here are dinosaur infested!!!!! + # Don't touch this code unless you know what you are doing + # Stage0 baudrate is fixed + # Contributor: [@rgwan](https://github.com/rgwan) + # rgwan + baudrate = 1500000 + if args.Board == "goE" or args.Board == "trainer": + KFlash.log(INFO_MSG,"Selected Stage0 Baudrate: ", baudrate, BASH_TIPS['DEFAULT']) + # This is for openec, contained ft2232, goE and trainer + KFlash.log(INFO_MSG,"FT2232 mode", BASH_TIPS['DEFAULT']) + baudrate_stage0 = int(baudrate * 38.6 / 38) + out = struct.pack('III', 0, 4, baudrate_stage0) + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + out = struct.pack('HH', 0xc6, 0x00) + crc32_checksum + out + self.write(out) + time.sleep(0.05) + self._port.baudrate = baudrate + + retry_count = 0 + while 1: + self.checkKillExit() + retry_count = retry_count + 1 + if retry_count > 3: + err = (ERROR_MSG,'Fast mode failed, please use slow mode by add parameter ' + BASH_TIPS['GREEN'] + '--Slow', BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + try: + self.greeting() + break + except TimeoutError: + pass + elif args.Board == "dan" or args.Board == "bit" or args.Board == "kd233": + KFlash.log(INFO_MSG,"CH340 mode", BASH_TIPS['DEFAULT']) + # This is for CH340, contained dan, bit and kd233 + baudrate_stage0 = int(baudrate * 38.4 / 38) + # CH340 can not use this method, test failed, take risks at your own risk + else: + # This is for unknown board + KFlash.log(WARN_MSG,"Unknown mode", BASH_TIPS['DEFAULT']) + + def __init__(self, port='/dev/ttyUSB1', baudrate=115200): + # configure the serial connections (the parameters differs on the device you are connecting to) + self._port = serial.Serial( + port=port, + baudrate=baudrate, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=0.1 + ) + KFlash.log(INFO_MSG, "Default baudrate is", baudrate, ", later it may be changed to the value you set.", BASH_TIPS['DEFAULT']) + + self._port.isOpen() + self._slip_reader = slip_reader(self._port) + self._kill_process = False + + """ Read a SLIP packet from the serial port """ + + def read(self): + return next(self._slip_reader) + + """ Write bytes to the serial port while performing SLIP escaping """ + + def write(self, packet): + buf = b'\xc0' \ + + (packet.replace(b'\xdb', b'\xdb\xdd').replace(b'\xc0', b'\xdb\xdc')) \ + + b'\xc0' + #KFlash.log('[WRITE]', binascii.hexlify(buf)) + return self._port.write(buf) + + def read_loop(self): + #out = b'' + # while self._port.inWaiting() > 0: + # out += self._port.read(1) + + # KFlash.log(out) + while 1: + sys.stdout.write('[RECV] raw data: ') + sys.stdout.write(binascii.hexlify(self._port.read(1)).decode()) + sys.stdout.flush() + + def recv_one_return(self, timeout_s = None): + timeout_init = time.time() + data = b'' + if timeout_s == None: + timeout_s = ISP_RECEIVE_TIMEOUT + # find start boarder + #sys.stdout.write('[RECV one return] raw data: ') + while 1: + if time.time() - timeout_init > timeout_s: + raise_exception( TimeoutError ) + c = self._port.read(1) + #sys.stdout.write(binascii.hexlify(c).decode()) + sys.stdout.flush() + if c == b'\xc0': + break + + in_escape = False + while 1: + if time.time() - timeout_init > timeout_s: + raise_exception( TimeoutError ) + c = self._port.read(1) + #sys.stdout.write(binascii.hexlify(c).decode()) + sys.stdout.flush() + if c == b'\xc0': + break + + elif in_escape: # part-way through escape sequence + in_escape = False + if c == b'\xdc': + data += b'\xc0' + elif c == b'\xdd': + data += b'\xdb' + else: + raise_exception( Exception('Invalid SLIP escape (%r%r)' % (b'\xdb', c)) ) + elif c == b'\xdb': # start of escape sequence + in_escape = True + + data += c + + #sys.stdout.write('\n') + return data + + # kd233 or open-ec or new cmsis-dap + def reset_to_isp_kd233(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW, IO16 to HIGH --') + # Pull reset down and keep 10ms + self._port.setDTR (True) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- IO16 to LOW, RESET to HIGH --') + # Pull IO16 to low and release reset + self._port.setRTS (True) + self._port.setDTR (False) + time.sleep(0.1) + def reset_to_boot_kd233(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW --') + # Pull reset down and keep 10ms + self._port.setDTR (True) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to HIGH, BOOT --') + # Pull IO16 to low and release reset + self._port.setRTS (False) + self._port.setDTR (False) + time.sleep(0.1) + + #dan dock + def reset_to_isp_dan(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW, IO16 to HIGH --') + # Pull reset down and keep 10ms + self._port.setDTR (False) + self._port.setRTS (True) + time.sleep(0.1) + #KFlash.log('-- IO16 to LOW, RESET to HIGH --') + # Pull IO16 to low and release reset + self._port.setRTS (False) + self._port.setDTR (True) + time.sleep(0.1) + def reset_to_boot_dan(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW --') + # Pull reset down and keep 10ms + self._port.setDTR (False) + self._port.setRTS (True) + time.sleep(0.1) + #KFlash.log('-- RESET to HIGH, BOOT --') + # Pull IO16 to low and release reset + self._port.setRTS (False) + self._port.setDTR (False) + time.sleep(0.1) + + # maix goD for old cmsis-dap firmware + def reset_to_isp_goD(self): + self._port.setDTR (True) ## output 0 + self._port.setRTS (True) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW --') + # Pull reset down and keep 10ms + self._port.setRTS (False) + self._port.setDTR (True) + time.sleep(0.1) + #KFlash.log('-- RESET to HIGH, BOOT --') + # Pull IO16 to low and release reset + self._port.setRTS (False) + self._port.setDTR (True) + time.sleep(0.1) + def reset_to_boot_goD(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW --') + # Pull reset down and keep 10ms + self._port.setRTS (False) + self._port.setDTR (True) + time.sleep(0.1) + #KFlash.log('-- RESET to HIGH, BOOT --') + # Pull IO16 to low and release reset + self._port.setRTS (True) + self._port.setDTR (True) + time.sleep(0.1) + + # maix goE for openec or new cmsis-dap firmware + def reset_to_boot_maixgo(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.1) + #KFlash.log('-- RESET to LOW --') + # Pull reset down and keep 10ms + self._port.setRTS (False) + self._port.setDTR (True) + time.sleep(0.1) + #KFlash.log('-- RESET to HIGH, BOOT --') + # Pull IO16 to low and release reset + self._port.setRTS (False) + self._port.setDTR (False) + time.sleep(0.1) + + def greeting(self): + self._port.write(b'\xc0\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') + op, reason, text = ISPResponse.parse(self.recv_one_return()) + + #KFlash.log('MAIX return op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name) + + + def flash_greeting(self): + retry_count = 0 + while 1: + self.checkKillExit() + try: + self._port.write(b'\xc0\xd2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') + except Exception: + raise_exception( Exception("Connection disconnected, try again or maybe need use Slow mode, or decrease baudrate") ) + retry_count = retry_count + 1 + try: + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + except IndexError: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Index Error, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + except TimeoutError: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Timeout Error, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + except: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Unexcepted Error, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + # KFlash.log('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_NOP and FlashModeResponse.ErrorCode(reason) == FlashModeResponse.ErrorCode.ISP_RET_OK: + KFlash.log(INFO_MSG,"Boot to Flashmode Successfully",BASH_TIPS['DEFAULT']) + self._port.flushInput() + self._port.flushOutput() + break + else: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + + def boot(self, address=0x80000000): + KFlash.log(INFO_MSG,"Booting From " + hex(address),BASH_TIPS['DEFAULT']) + + out = struct.pack('II', address, 0) + + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xc5, 0x00) + crc32_checksum + out # op: ISP_MEMORY_WRITE: 0xc3 + self.write(out) + + def recv_debug(self): + op, reason, text = ISPResponse.parse(self.recv_one_return()) + #KFlash.log('[RECV] op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name) + if text: + KFlash.log('-' * 30) + KFlash.log(text) + KFlash.log('-' * 30) + if ISPResponse.ErrorCode(reason) not in (ISPResponse.ErrorCode.ISP_RET_DEFAULT, ISPResponse.ErrorCode.ISP_RET_OK): + KFlash.log('Failed, retry, errcode=', hex(reason)) + return False + return True + + def flash_recv_debug(self): + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + #KFlash.log('[Flash-RECV] op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + if text: + KFlash.log('-' * 30) + KFlash.log(text) + KFlash.log('-' * 30) + + if FlashModeResponse.ErrorCode(reason) not in (FlashModeResponse.ErrorCode.ISP_RET_OK, FlashModeResponse.ErrorCode.ISP_RET_OK): + KFlash.log('Failed, retry') + return False + return True + + def init_flash(self, chip_type): + chip_type = int(chip_type) + KFlash.log(INFO_MSG,"Selected Flash: ",("In-Chip", "On-Board")[chip_type],BASH_TIPS['DEFAULT']) + out = struct.pack('II', chip_type, 0) + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + out = struct.pack('HH', 0xd7, 0x00) + crc32_checksum + out + '''Retry when it have error''' + retry_count = 0 + while 1: + self.checkKillExit() + sent = self.write(out) + retry_count = retry_count + 1 + try: + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + except IndexError: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Index Error, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + except TimeoutError: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Timeout Error, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + except: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Unexcepted Error, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + # KFlash.log('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.FLASHMODE_FLASH_INIT and FlashModeResponse.ErrorCode(reason) == FlashModeResponse.ErrorCode.ISP_RET_OK: + KFlash.log(INFO_MSG,"Initialization flash Successfully",BASH_TIPS['DEFAULT']) + break + else: + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + KFlash.log(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + + def flash_dataframe(self, data, address=0x80000000): + DATAFRAME_SIZE = 1024 + data_chunks = chunks(data, DATAFRAME_SIZE) + #KFlash.log('[DEBUG] flash dataframe | data length:', len(data)) + total_chunk = math.ceil(len(data)/DATAFRAME_SIZE) + + time_start = time.time() + for n, chunk in enumerate(data_chunks): + self.checkKillExit() + while 1: + self.checkKillExit() + #KFlash.log('[INFO] sending chunk', i, '@address', hex(address), 'chunklen', len(chunk)) + out = struct.pack('II', address, len(chunk)) + + crc32_checksum = struct.pack('I', binascii.crc32(out + chunk) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xc3, 0x00) + crc32_checksum + out + chunk # op: ISP_MEMORY_WRITE: 0xc3 + sent = self.write(out) + #KFlash.log('[INFO]', 'sent', sent, 'bytes', 'checksum', binascii.hexlify(crc32_checksum).decode()) + + address += len(chunk) + + if self.recv_debug(): + break + + columns, lines = TerminalSize.get_terminal_size((100, 24), terminal) + time_delta = time.time() - time_start + speed = '' + if (time_delta > 1): + speed = str(int((n + 1) * DATAFRAME_SIZE / 1024.0 / time_delta)) + 'kiB/s' + printProgressBar(n+1, total_chunk, prefix = 'Downloading ISP:', suffix = speed, length = columns - 35) + + def dump_to_flash(self, data, address=0, size=None): + ''' + typedef struct __attribute__((packed)) { + uint8_t op; + int32_t checksum; /* All the fields below are involved in the calculation of checksum */ + uint32_t address; + uint32_t data_len; + uint8_t data_buf[1024]; + } isp_request_t; + ''' + if size == None: + DATAFRAME_SIZE = ISP_FLASH_DATA_FRAME_SIZE + size = DATAFRAME_SIZE + data_chunks = chunks(data, size) + #KFlash.log('[DEBUG] flash dataframe | data length:', len(data)) + + + + for n, chunk in enumerate(data_chunks): + #KFlash.log('[INFO] sending chunk', i, '@address', hex(address)) + out = struct.pack('II', address, len(chunk)) + + crc32_checksum = struct.pack('I', binascii.crc32(out + chunk) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xd4, 0x00) + crc32_checksum + out + chunk + #KFlash.log("[$$$$]", binascii.hexlify(out[:32]).decode()) + retry_count = 0 + while True: + try: + sent = self.write(out) + #KFlash.log('[INFO]', 'sent', sent, 'bytes', 'checksum', crc32_checksum) + self.flash_recv_debug() + except: + retry_count = retry_count + 1 + if retry_count > MAX_RETRY_TIMES: + err = (ERROR_MSG,"Error Count Exceeded, Stop Trying",BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + continue + break + address += len(chunk) + + + + def flash_erase(self, erase_addr = 0, erase_len = 0): + #KFlash.log('[DEBUG] erasing spi flash.') + cmd0 = b'\xd3\x00\x00\x00' + cmd = struct.pack("I", erase_addr) + cmd += struct.pack("I", erase_len) + cmd = cmd0 + struct.pack('I', binascii.crc32(cmd) & 0xFFFFFFFF) + cmd + self.write(cmd) + t = time.time() + op, reason, text = FlashModeResponse.parse(self.recv_one_return(timeout_s=90)) + if FlashModeResponse.ErrorCode(reason) != FlashModeResponse.ErrorCode.ISP_RET_OK: + err = (ERROR_MSG,"erase error, error code: 0x{:02X}: {}".format(reason, text)) + err = tuple2str(err) + raise_exception( Exception(err) ) + else: + KFlash.log(INFO_MSG,"erase ok") + #KFlash.log('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + + def install_flash_bootloader(self, data): + # Download flash bootloader + self.flash_dataframe(data, address=0x80000000) + + def load_elf_to_sram(self, f): + try: + from elftools.elf.elffile import ELFFile + from elftools.elf.descriptions import describe_p_type + except ImportError: + err = (ERROR_MSG,'pyelftools must be installed, run '+BASH_TIPS['GREEN']+'`' + ('pip', 'pip3')[sys.version_info > (3, 0)] + ' install pyelftools`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + + elffile = ELFFile(f) + if elffile['e_entry'] != 0x80000000: + KFlash.log(WARN_MSG,"ELF entry is 0x%x instead of 0x80000000" % (elffile['e_entry']), BASH_TIPS['DEFAULT']) + + for segment in elffile.iter_segments(): + t = describe_p_type(segment['p_type']) + KFlash.log(INFO_MSG, ("Program Header: Size: %d, Virtual Address: 0x%x, Type: %s" % (segment['p_filesz'], segment['p_vaddr'], t)), BASH_TIPS['DEFAULT']) + if not (segment['p_vaddr'] & 0x80000000): + continue + if segment['p_filesz']==0 or segment['p_vaddr']==0: + KFlash.log("Skipped") + continue + self.flash_dataframe(segment.data(), segment['p_vaddr']) + + def flash_firmware(self, firmware_bin, aes_key = None, address_offset = 0, sha256Prefix = True, filename = ""): + # type: (bytes, bytes, int, bool) -> None + # Don't remove above code! + + #KFlash.log('[DEBUG] flash_firmware DEBUG: aeskey=', aes_key) + + if sha256Prefix == True: + # Add header to the firmware + # Format: SHA256(after)(32bytes) + AES_CIPHER_FLAG (1byte) + firmware_size(4bytes) + firmware_data + aes_cipher_flag = b'\x01' if aes_key else b'\x00' + + # Encryption + if aes_key: + enc = AES_128_CBC(aes_key, iv=b'\x00'*16).encrypt + padded = firmware_bin + b'\x00'*15 # zero pad + firmware_bin = b''.join([enc(padded[i*16:i*16+16]) for i in range(len(padded)//16)]) + + firmware_len = len(firmware_bin) + + data = aes_cipher_flag + struct.pack('I', firmware_len) + firmware_bin + + sha256_hash = hashlib.sha256(data).digest() + + firmware_with_header = data + sha256_hash + + total_len = len(firmware_with_header) + # Slice download firmware + data_chunks = chunks(firmware_with_header, ISP_FLASH_DATA_FRAME_SIZE) # 4kiB for a sector, 16kiB for dataframe + else: + total_len = len(firmware_bin) + data_chunks = chunks(firmware_bin, ISP_FLASH_DATA_FRAME_SIZE, address = address_offset) + + time_start = time.time() + write_len = 0 + for n, chunk in enumerate(data_chunks): + self.checkKillExit() + # 4K align + aligned_chunk = len(chunk) + aligned_chunk = (ISP_FLASH_SECTOR_SIZE - (aligned_chunk % ISP_FLASH_SECTOR_SIZE))%ISP_FLASH_SECTOR_SIZE + aligned_chunk + chunk = chunk.ljust(aligned_chunk, b'\x00') # align by size of dataframe + + # Download a dataframe + #KFlash.log('[INFO]', 'Write firmware data piece') + chunk_len = len(chunk) + self.dump_to_flash(chunk, address= write_len + address_offset, size=chunk_len) + write_len += chunk_len + columns, lines = TerminalSize.get_terminal_size((100, 24), terminal) + time_delta = time.time() - time_start + speed = '' + if (time_delta > 1): + speed = str(int(write_len / 1024.0 / time_delta)) + 'kiB/s' + printProgressBar(write_len, total_len, prefix = 'Programming BIN:', filename=filename, suffix = speed, length = columns - 35) + + def kill(self): + self._kill_process = True + + def checkKillExit(self): + if self._kill_process: + self._port.close() + self._kill_process = False + raise Exception("Cancel") + + def open_terminal(reset): + control_signal = '0' if reset else '1' + control_signal_b = not reset + import serial.tools.miniterm + # For using the terminal with MaixPy the 'filter' option must be set to 'direct' + # because some control characters are emited + sys.argv = [sys.argv[0], _port, '115200', '--dtr='+control_signal, '--rts='+control_signal, '--filter=direct'] + serial.tools.miniterm.main(default_port=_port, default_baudrate=115200, default_dtr=control_signal_b, default_rts=control_signal_b) + sys.exit(0) + + boards_choices = ["kd233", "dan", "bit", "bit_mic", "goE", "goD", "maixduino", "trainer"] + if terminal: + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="COM Port", default="DEFAULT") + parser.add_argument("-f", "--flash", help="SPI Flash type, 0 for SPI3, 1 for SPI0", default=1) + parser.add_argument("-b", "--baudrate", type=int, help="UART baudrate for uploading firmware", default=115200) + parser.add_argument("-l", "--bootloader", help="Bootloader bin path", required=False, default=None) + parser.add_argument("-k", "--key", help="AES key in hex, if you need encrypt your firmware.", required=False, default=None) + parser.add_argument("-v", "--version", help="Print version.", action='version', version='0.8.3') + parser.add_argument("--verbose", help="Increase output verbosity", default=False, action="store_true") + parser.add_argument("-t", "--terminal", help="Start a terminal after finish (Python miniterm)", default=False, action="store_true") + parser.add_argument("-n", "--noansi", help="Do not use ANSI colors, recommended in Windows CMD", default=False, action="store_true") + parser.add_argument("-s", "--sram", help="Download firmware to SRAM and boot", default=False, action="store_true") + parser.add_argument("-B", "--Board",required=False, type=str, help="Select dev board, e.g. kd233, dan, bit, goD, goE or trainer") + parser.add_argument("-S", "--Slow",required=False, help="Slow download mode", default=False) + parser.add_argument("-A", "--addr",required=False, help="flash addr", type=str, default="-1") + parser.add_argument("-L", "--length",required=False, help="flash addr", type=str, default="-1") + parser.add_argument("firmware", help="firmware bin path") + args = parser.parse_args() + else: + args = argparse.Namespace() + setattr(args, "port", "DEFAULT") + setattr(args, "flash", 1) + setattr(args, "baudrate", 115200) + setattr(args, "bootloader", None) + setattr(args, "key", None) + setattr(args, "verbose", False) + setattr(args, "terminal", False) + setattr(args, "noansi", False) + setattr(args, "sram", False) + setattr(args, "Board", None) + setattr(args, "Slow", False) + setattr(args, "addr", -1) + setattr(args, "length", -1) + + # udpate args for none terminal call + if not terminal: + args.port = dev + args.baudrate = baudrate + args.noansi = noansi + args.sram = sram + args.Board = board + args.firmware = file + args.Slow = slow_mode + args.addr = addr + args.length = length + + if args.Board == "maixduino" or args.Board == "bit_mic": + args.Board = "goE" + + if (args.noansi == True): + BASH_TIPS = dict(NORMAL='',BOLD='',DIM='',UNDERLINE='', + DEFAULT='', RED='', YELLOW='', GREEN='', + BG_DEFAULT='', BG_WHITE='') + ERROR_MSG = BASH_TIPS['RED']+BASH_TIPS['BOLD']+'[ERROR]'+BASH_TIPS['NORMAL'] + WARN_MSG = BASH_TIPS['YELLOW']+BASH_TIPS['BOLD']+'[WARN]'+BASH_TIPS['NORMAL'] + INFO_MSG = BASH_TIPS['GREEN']+BASH_TIPS['BOLD']+'[INFO]'+BASH_TIPS['NORMAL'] + KFlash.log(INFO_MSG,'ANSI colors not used',BASH_TIPS['DEFAULT']) + + manually_set_the_board = False + if args.Board: + manually_set_the_board = True + + if args.port == "DEFAULT": + if args.Board == "goE": + list_port_info = list(serial.tools.list_ports.grep("0403")) #Take the second one + if len(list_port_info) == 0: + err = (ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + list_port_info.sort() + if len(list_port_info) == 1: + _port = list_port_info[0].device + elif len(list_port_info) > 1: + _port = list_port_info[1].device + KFlash.log(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT']) + elif args.Board == "trainer": + list_port_info = list(serial.tools.list_ports.grep("0403")) #Take the first one + if(len(list_port_info)==0): + err = (ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + list_port_info.sort() + _port = list_port_info[0].device + KFlash.log(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT']) + else: + try: + list_port_info = next(serial.tools.list_ports.grep(VID_LIST_FOR_AUTO_LOOKUP)) #Take the first one within the list + _port = list_port_info.device + KFlash.log(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT']) + except StopIteration: + err = (ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + else: + _port = args.port + KFlash.log(INFO_MSG,"COM Port Selected Manually: ", _port, BASH_TIPS['DEFAULT']) + + self.loader = MAIXLoader(port=_port, baudrate=115200) + file_format = ProgramFileFormat.FMT_BINARY + + # 0. Check firmware or cmd + cmds = ['erase'] + if not args.firmware in cmds: + if not os.path.exists(args.firmware): + err = (ERROR_MSG,'Unable to find the firmware at ', args.firmware, BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + + with open(args.firmware, 'rb') as f: + file_header = f.read(4) + #if file_header.startswith(bytes([0x50, 0x4B])): + if file_header.startswith(b'\x50\x4B'): + if ".kfpkg" != os.path.splitext(args.firmware)[1]: + KFlash.log(INFO_MSG, 'Find a zip file, but not with ext .kfpkg:', args.firmware, BASH_TIPS['DEFAULT']) + else: + file_format = ProgramFileFormat.FMT_KFPKG + + #if file_header.startswith(bytes([0x7F, 0x45, 0x4C, 0x46])): + if file_header.startswith(b'\x7f\x45\x4c\x46'): + file_format = ProgramFileFormat.FMT_ELF + if args.sram: + KFlash.log(INFO_MSG, 'Find an ELF file:', args.firmware, BASH_TIPS['DEFAULT']) + else: + err = (ERROR_MSG, 'This is an ELF file and cannot be programmed to flash directly:', args.firmware, BASH_TIPS['DEFAULT'] , '\r\nPlease retry:', args.firmware + '.bin', BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + + # 1. Greeting. + KFlash.log(INFO_MSG,"Trying to Enter the ISP Mode...",BASH_TIPS['DEFAULT']) + + retry_count = 0 + + while 1: + self.checkKillExit() + if not self.loader._port.isOpen(): + self.loader._port.open() + try: + retry_count = retry_count + 1 + if retry_count > 15: + err = (ERROR_MSG,"No vaild Kendryte K210 found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`-p '+('/dev/ttyUSB0', 'COM3')[sys.platform == 'win32']+'`',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + if args.Board == "dan" or args.Board == "bit" or args.Board == "trainer": + try: + KFlash.log('.', end='') + self.loader.reset_to_isp_dan() + self.loader.greeting() + break + except TimeoutError: + pass + elif args.Board == "kd233": + try: + KFlash.log('_', end='') + self.loader.reset_to_isp_kd233() + self.loader.greeting() + break + except TimeoutError: + pass + elif args.Board == "goE": + try: + KFlash.log('*', end='') + self.loader.reset_to_isp_kd233() + self.loader.greeting() + break + except TimeoutError: + pass + elif args.Board == "goD": + try: + KFlash.log('#', end='') + self.loader.reset_to_isp_goD() + self.loader.greeting() + break + except TimeoutError: + pass + else: + try: + KFlash.log('.', end='') + self.loader.reset_to_isp_dan() + self.loader.greeting() + args.Board = "dan" + KFlash.log() + KFlash.log(INFO_MSG,"Automatically detected dan/bit/trainer",BASH_TIPS['DEFAULT']) + break + except TimeoutError: + if not self.loader._port.isOpen(): + self.loader._port.open() + pass + try: + KFlash.log('_', end='') + self.loader.reset_to_isp_kd233() + self.loader.greeting() + args.Board = "kd233" + KFlash.log() + KFlash.log(INFO_MSG,"Automatically detected goE/kd233",BASH_TIPS['DEFAULT']) + break + except TimeoutError: + if not self.loader._port.isOpen(): + self.loader._port.open() + pass + try: + KFlash.log('.', end='') + self.loader.reset_to_isp_goD() + self.loader.greeting() + args.Board = "goD" + KFlash.log() + KFlash.log(INFO_MSG,"Automatically detected goD",BASH_TIPS['DEFAULT']) + break + except TimeoutError: + if not self.loader._port.isOpen(): + self.loader._port.open() + pass + try: + # Magic, just repeat, don't remove, it may unstable, don't know why. + KFlash.log('_', end='') + self.loader.reset_to_isp_kd233() + self.loader.greeting() + args.Board = "kd233" + KFlash.log() + KFlash.log(INFO_MSG,"Automatically detected goE/kd233",BASH_TIPS['DEFAULT']) + break + except TimeoutError: + if not self.loader._port.isOpen(): + self.loader._port.open() + pass + except Exception as e: + KFlash.log() + raise_exception( Exception("Greeting fail, check serial port ("+str(e)+")" ) ) + + # Don't remove this line + # Dangerous, here are dinosaur infested!!!!! + ISP_RECEIVE_TIMEOUT = 3 + + KFlash.log() + KFlash.log(INFO_MSG,"Greeting Message Detected, Start Downloading ISP",BASH_TIPS['DEFAULT']) + + if manually_set_the_board and (not args.Slow): + if (args.baudrate >= 1500000) or args.sram: + self.loader.change_baudrate_stage0(args.baudrate) + + # 2. download bootloader and firmware + if args.sram: + with open(args.firmware, 'rb') as firmware_bin: + if file_format == ProgramFileFormat.FMT_KFPKG: + err = (ERROR_MSG, "Unable to load kfpkg to SRAM") + err = tuple2str(err) + raise_exception( Exception(err) ) + elif file_format == ProgramFileFormat.FMT_ELF: + self.loader.load_elf_to_sram(firmware_bin) + else: + self.loader.install_flash_bootloader(firmware_bin.read()) + else: + # install bootloader at 0x80000000 + if args.bootloader: + with open(args.bootloader, 'rb') as f: + isp_loader = f.read() + else: + isp_loader = ISP_PROG + self.loader.install_flash_bootloader(isp_loader) + + # Boot the code from SRAM + self.loader.boot() + + if args.sram: + # Dangerous, here are dinosaur infested!!!!! + # Don't touch this code unless you know what you are doing + self.loader._port.baudrate = args.baudrate + KFlash.log(INFO_MSG,"Boot user code from SRAM", BASH_TIPS['DEFAULT']) + if(args.terminal == True): + open_terminal(False) + msg = "Burn SRAM OK" + raise_exception( Exception(msg) ) + + # Dangerous, here are dinosaur infested!!!!! + # Don't touch this code unless you know what you are doing + self.loader._port.baudrate = 115200 + + KFlash.log(INFO_MSG,"Wait For 0.1 second for ISP to Boot", BASH_TIPS['DEFAULT']) + + time.sleep(0.1) + + self.loader.flash_greeting() + + if args.baudrate != 115200: + self.loader.change_baudrate(args.baudrate) + KFlash.log(INFO_MSG,"Baudrate changed, greeting with ISP again ... ", BASH_TIPS['DEFAULT']) + self.loader.flash_greeting() + + self.loader.init_flash(args.flash) + + if file_format == ProgramFileFormat.FMT_KFPKG: + KFlash.log(INFO_MSG,"Extracting KFPKG ... ", BASH_TIPS['DEFAULT']) + with tempfile.TemporaryDirectory() as tmpdir: + try: + with zipfile.ZipFile(args.firmware) as zf: + zf.extractall(tmpdir) + except zipfile.BadZipFile: + err = (ERROR_MSG,'Unable to Decompress the kfpkg, your file might be corrupted.',BASH_TIPS['DEFAULT']) + err = tuple2str(err) + raise_exception( Exception(err) ) + + fFlashList = open(os.path.join(tmpdir, 'flash-list.json'), "r") + sFlashList = re.sub(r'"address": (.*),', r'"address": "\1",', fFlashList.read()) #Pack the Hex Number in json into str + fFlashList.close() + jsonFlashList = json.loads(sFlashList) + for lBinFiles in jsonFlashList['files']: + self.checkKillExit() + KFlash.log(INFO_MSG,"Writing",lBinFiles['bin'],"into","0x%08x"%int(lBinFiles['address'], 0),BASH_TIPS['DEFAULT']) + with open(os.path.join(tmpdir, lBinFiles["bin"]), "rb") as firmware_bin: + self.loader.flash_firmware(firmware_bin.read(), None, int(lBinFiles['address'], 0), lBinFiles['sha256Prefix'], filename=lBinFiles['bin']) + else: + if args.firmware == "erase": + if args.addr.lower().startswith("0x"): + addr = int(args.addr, base=16) + else: + addr = int(args.addr) + if args.length.lower() == "all": + addr = 0 + length = 0xFFFFFFEE + KFlash.log(INFO_MSG,"erase all") + else: + if args.length.lower().startswith("0x"): + length = int(args.length, base=16) + else: + length = int(args.length) + KFlash.log(INFO_MSG,"erase '0x{:x}' - '0x{:x}' ({}B, {:.02}KiB, {:.02}MiB)".format(addr, addr+length, length, length/1024.0, length/1024.0/1024.0)) + if ((addr % 4096) != 0) or ( length != 0xFFFFFFEE and (length % 4096) != 0) or addr < 0 or addr > 0x01000000 or length < 0 or ( length > 0x01000000 and length != 0xFFFFFFEE): + err = (ERROR_MSG,"erase flash addr or length error, addr should >= 0x00000000, and length should >= 4096 or 'all'") + err = tuple2str(err) + raise_exception( Exception(err) ) + self.loader.flash_erase(addr, length) + else: + with open(args.firmware, 'rb') as firmware_bin: + if args.key: + aes_key = binascii.a2b_hex(args.key) + if len(aes_key) != 16: + raise_exception( ValueError('AES key must by 16 bytes') ) + + self.loader.flash_firmware(firmware_bin.read(), aes_key=aes_key) + else: + self.loader.flash_firmware(firmware_bin.read()) + + # 3. boot + if args.Board == "dan" or args.Board == "bit" or args.Board == "trainer": + self.loader.reset_to_boot_dan() + elif args.Board == "kd233": + self.loader.reset_to_boot_kd233() + elif args.Board == "goE": + self.loader.reset_to_boot_maixgo() + elif args.Board == "goD": + self.loader.reset_to_boot_goD() + else: + KFlash.log(WARN_MSG,"Board unknown !! please press reset to boot!!") + + KFlash.log(INFO_MSG,"Rebooting...", BASH_TIPS['DEFAULT']) + try: + self.loader._port.close() + except Exception: + pass + + if(args.terminal == True): + open_terminal(True) + + def kill(self): + if self.loader: + self.loader.kill() + self.killProcess = True + + def checkKillExit(self): + if self.killProcess: + if self.loader: + self.loader._port.close() + raise Exception("Cancel") + + +def main(): + kflash = KFlash() + try: + kflash.process() + except Exception as e: + if str(e) == "Burn SRAM OK": + sys.exit(0) + kflash.log(str(e)) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/lab8/tools/mksfs.c b/lab8/tools/mksfs.c new file mode 100644 index 0000000..41d2a0e --- /dev/null +++ b/lab8/tools/mksfs.c @@ -0,0 +1,582 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int bool; + +#define __error(msg, quit, ...) \ + do { \ + fprintf(stderr, #msg ": function %s - line %d: ", __FUNCTION__, __LINE__); \ + if (errno != 0) { \ + fprintf(stderr, "[error] %s: ", strerror(errno)); \ + } \ + fprintf(stderr, "\n\t"), fprintf(stderr, __VA_ARGS__); \ + errno = 0; \ + if (quit) { \ + exit(-1); \ + } \ + } while (0) + +#define warn(...) __error(warn, 0, __VA_ARGS__) +#define bug(...) __error(bug, 1, __VA_ARGS__) + +#define static_assert(x) \ + switch (x) {case 0: case (x): ; } + +/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ +#define GOLDEN_RATIO_PRIME_32 0x9e370001UL + +#define HASH_SHIFT 10 +#define HASH_LIST_SIZE (1 << HASH_SHIFT) + +static inline uint32_t +__hash32(uint32_t val, unsigned int bits) { + uint32_t hash = val * GOLDEN_RATIO_PRIME_32; + return (hash >> (32 - bits)); +} + +static uint32_t +hash32(uint32_t val) { + return __hash32(val, HASH_SHIFT); +} + +static uint32_t +hash64(uint64_t val) { + return __hash32((uint32_t)val, HASH_SHIFT); +} + +void * +safe_malloc(size_t size) { + void *ret; + if ((ret = malloc(size)) == NULL) { + bug("malloc %lu bytes failed.\n", (long unsigned)size); + } + return ret; +} + +char * +safe_strdup(const char *str) { + char *ret; + if ((ret = strdup(str)) == NULL) { + bug("strdup failed: %s\n", str); + } + return ret; +} + +struct stat * +safe_stat(const char *filename) { + static struct stat __stat; + if (stat(filename, &__stat) != 0) { + bug("stat %s failed.\n", filename); + } + return &__stat; +} + +struct stat * +safe_fstat(int fd) { + static struct stat __stat; + if (fstat(fd, &__stat) != 0) { + bug("fstat %d failed.\n", fd); + } + return &__stat; +} + +struct stat * +safe_lstat(const char *name) { + static struct stat __stat; + if (lstat(name, &__stat) != 0) { + bug("lstat '%s' failed.\n", name); + } + return &__stat; +} + +void +safe_fchdir(int fd) { + if (fchdir(fd) != 0) { + bug("fchdir failed %d.\n", fd); + } +} + +#define SFS_MAGIC 0x2f8dbe2a +#define SFS_NDIRECT 12 +#define SFS_BLKSIZE 4096 // 4K +#define SFS_MAX_NBLKS (1024UL * 512) // 4K * 512K +#define SFS_MAX_INFO_LEN 31 +#define SFS_MAX_FNAME_LEN 255 +#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) // 128M + +#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT) +#define SFS_TYPE_FILE 1 +#define SFS_TYPE_DIR 2 +#define SFS_TYPE_LINK 3 + +#define SFS_BLKN_SUPER 0 +#define SFS_BLKN_ROOT 1 +#define SFS_BLKN_FREEMAP 2 + +struct cache_block { + uint32_t ino; + struct cache_block *hash_next; + void *cache; +}; + +struct cache_inode { + struct inode { + uint32_t size; + uint16_t type; + uint16_t nlinks; + uint32_t blocks; + uint32_t direct[SFS_NDIRECT]; + uint32_t indirect; + uint32_t db_indirect; + } inode; + ino_t real; + uint32_t ino; + uint32_t nblks; + struct cache_block *l1, *l2; + struct cache_inode *hash_next; +}; + +struct sfs_fs { + struct { + uint32_t magic; + uint32_t blocks; + uint32_t unused_blocks; + char info[SFS_MAX_INFO_LEN + 1]; + } super; + struct subpath { + struct subpath *next, *prev; + char *subname; + } __sp_nil, *sp_root, *sp_end; + int imgfd; + uint32_t ninos, next_ino; + struct cache_inode *root; + struct cache_inode *inodes[HASH_LIST_SIZE]; + struct cache_block *blocks[HASH_LIST_SIZE]; +}; + +struct sfs_entry { + uint32_t ino; + char name[SFS_MAX_FNAME_LEN + 1]; +}; + +static uint32_t +sfs_alloc_ino(struct sfs_fs *sfs) { + if (sfs->next_ino < sfs->ninos) { + sfs->super.unused_blocks --; + return sfs->next_ino ++; + } + bug("out of disk space.\n"); +} + +static struct cache_block * +alloc_cache_block(struct sfs_fs *sfs, uint32_t ino) { + struct cache_block *cb = safe_malloc(sizeof(struct cache_block)); + cb->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs); + cb->cache = memset(safe_malloc(SFS_BLKSIZE), 0, SFS_BLKSIZE); + struct cache_block **head = sfs->blocks + hash32(ino); + cb->hash_next = *head, *head = cb; + return cb; +} + +struct cache_block * +search_cache_block(struct sfs_fs *sfs, uint32_t ino) { + struct cache_block *cb = sfs->blocks[hash32(ino)]; + while (cb != NULL && cb->ino != ino) { + cb = cb->hash_next; + } + return cb; +} + +static struct cache_inode * +alloc_cache_inode(struct sfs_fs *sfs, ino_t real, uint32_t ino, uint16_t type) { + struct cache_inode *ci = safe_malloc(sizeof(struct cache_inode)); + ci->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs); + ci->real = real, ci->nblks = 0, ci->l1 = ci->l2 = NULL; + struct inode *inode = &(ci->inode); + memset(inode, 0, sizeof(struct inode)); + inode->type = type; + struct cache_inode **head = sfs->inodes + hash64(real); + ci->hash_next = *head, *head = ci; + return ci; +} + +struct cache_inode * +search_cache_inode(struct sfs_fs *sfs, ino_t real) { + struct cache_inode *ci = sfs->inodes[hash64(real)]; + while (ci != NULL && ci->real != real) { + ci = ci->hash_next; + } + return ci; +} + +struct sfs_fs * +create_sfs(int imgfd) { + uint32_t ninos, next_ino; + struct stat *stat = safe_fstat(imgfd); + if ((ninos = stat->st_size / SFS_BLKSIZE) > SFS_MAX_NBLKS) { + ninos = SFS_MAX_NBLKS; + warn("img file is too big (%llu bytes, only use %u blocks).\n", + (unsigned long long)stat->st_size, ninos); + } + if ((next_ino = SFS_BLKN_FREEMAP + (ninos + SFS_BLKBITS - 1) / SFS_BLKBITS) >= ninos) { + bug("img file is too small (%llu bytes, %u blocks, bitmap use at least %u blocks).\n", + (unsigned long long)stat->st_size, ninos, next_ino - 2); + } + + struct sfs_fs *sfs = safe_malloc(sizeof(struct sfs_fs)); + sfs->super.magic = SFS_MAGIC; + sfs->super.blocks = ninos, sfs->super.unused_blocks = ninos - next_ino; + snprintf(sfs->super.info, SFS_MAX_INFO_LEN, "simple file system"); + + sfs->ninos = ninos, sfs->next_ino = next_ino, sfs->imgfd = imgfd; + sfs->sp_root = sfs->sp_end = &(sfs->__sp_nil); + sfs->sp_end->prev = sfs->sp_end->next = NULL; + + int i; + for (i = 0; i < HASH_LIST_SIZE; i ++) { + sfs->inodes[i] = NULL; + sfs->blocks[i] = NULL; + } + + sfs->root = alloc_cache_inode(sfs, 0, SFS_BLKN_ROOT, SFS_TYPE_DIR); + return sfs; +} + +static void +subpath_push(struct sfs_fs *sfs, const char *subname) { + struct subpath *subpath = safe_malloc(sizeof(struct subpath)); + subpath->subname = safe_strdup(subname); + sfs->sp_end->next = subpath; + subpath->prev = sfs->sp_end; + subpath->next = NULL; + sfs->sp_end = subpath; +} + +static void +subpath_pop(struct sfs_fs *sfs) { + assert(sfs->sp_root != sfs->sp_end); + struct subpath *subpath = sfs->sp_end; + sfs->sp_end = sfs->sp_end->prev, sfs->sp_end->next = NULL; + free(subpath->subname), free(subpath); +} + +static void +subpath_show(FILE *fout, struct sfs_fs *sfs, const char *name) { + struct subpath *subpath = sfs->sp_root; + fprintf(fout, "current is: /"); + while ((subpath = subpath->next) != NULL) { + fprintf(fout, "%s/", subpath->subname); + } + if (name != NULL) { + fprintf(fout, "%s", name); + } + fprintf(fout, "\n"); +} + +static void +write_block(struct sfs_fs *sfs, void *data, size_t len, uint32_t ino) { + assert(len <= SFS_BLKSIZE && ino < sfs->ninos); + static char buffer[SFS_BLKSIZE]; + if (len != SFS_BLKSIZE) { + memset(buffer, 0, sizeof(buffer)); + data = memcpy(buffer, data, len); + } + off_t offset = (off_t)ino * SFS_BLKSIZE; + ssize_t ret; + if ((ret = pwrite(sfs->imgfd, data, SFS_BLKSIZE, offset)) != SFS_BLKSIZE) { + bug("write %u block failed: (%d/%d).\n", ino, (int)ret, SFS_BLKSIZE); + } +} + +static void +flush_cache_block(struct sfs_fs *sfs, struct cache_block *cb) { + write_block(sfs, cb->cache, SFS_BLKSIZE, cb->ino); +} + +static void +flush_cache_inode(struct sfs_fs *sfs, struct cache_inode *ci) { + write_block(sfs, &(ci->inode), sizeof(ci->inode), ci->ino); +} + +void +close_sfs(struct sfs_fs *sfs) { + static char buffer[SFS_BLKSIZE]; + uint32_t i, j, ino = SFS_BLKN_FREEMAP; + uint32_t ninos = sfs->ninos, next_ino = sfs->next_ino; + for (i = 0; i < ninos; ino ++, i += SFS_BLKBITS) { + memset(buffer, 0, sizeof(buffer)); + if (i + SFS_BLKBITS > next_ino) { + uint32_t start = 0, end = SFS_BLKBITS; + if (i < next_ino) { + start = next_ino - i; + } + if (i + SFS_BLKBITS > ninos) { + end = ninos - i; + } + uint32_t *data = (uint32_t *)buffer; + const uint32_t bits = sizeof(bits) * CHAR_BIT; + for (j = start; j < end; j ++) { + data[j / bits] |= (1 << (j % bits)); + } + } + write_block(sfs, buffer, sizeof(buffer), ino); + } + write_block(sfs, &(sfs->super), sizeof(sfs->super), SFS_BLKN_SUPER); + + for (i = 0; i < HASH_LIST_SIZE; i ++) { + struct cache_block *cb = sfs->blocks[i]; + while (cb != NULL) { + flush_cache_block(sfs, cb); + cb = cb->hash_next; + } + struct cache_inode *ci = sfs->inodes[i]; + while (ci != NULL) { + flush_cache_inode(sfs, ci); + ci = ci->hash_next; + } + } +} + +struct sfs_fs * +open_img(const char *imgname) { + const char *expect = ".img", *ext = imgname + strlen(imgname) - strlen(expect); + if (ext <= imgname || strcmp(ext, expect) != 0) { + bug("invalid .img file name '%s'.\n", imgname); + } + int imgfd; + if ((imgfd = open(imgname, O_WRONLY)) < 0) { + bug("open '%s' failed.\n", imgname); + } + return create_sfs(imgfd); +} + +#define open_bug(sfs, name, ...) \ + do { \ + subpath_show(stderr, sfs, name); \ + bug(__VA_ARGS__); \ + } while (0) + +#define show_fullpath(sfs, name) subpath_show(stderr, sfs, name) + +void open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent); +void open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd); +void open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename); + +#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t)) +#define SFS_L0_NBLKS SFS_NDIRECT +#define SFS_L1_NBLKS (SFS_BLK_NENTRY + SFS_L0_NBLKS) +#define SFS_L2_NBLKS (SFS_BLK_NENTRY * SFS_BLK_NENTRY + SFS_L1_NBLKS) +#define SFS_LN_NBLKS (SFS_MAX_FILE_SIZE / SFS_BLKSIZE) + +static void +update_cache(struct sfs_fs *sfs, struct cache_block **cbp, uint32_t *inop) { + uint32_t ino = *inop; + struct cache_block *cb = *cbp; + if (ino == 0) { + cb = alloc_cache_block(sfs, 0); + ino = cb->ino; + } + else if (cb == NULL || cb->ino != ino) { + cb = search_cache_block(sfs, ino); + assert(cb != NULL && cb->ino == ino); + } + *cbp = cb, *inop = ino; +} + +static void +append_block(struct sfs_fs *sfs, struct cache_inode *file, size_t size, uint32_t ino, const char *filename) { + static_assert(SFS_LN_NBLKS <= SFS_L2_NBLKS); + assert(size <= SFS_BLKSIZE); + uint32_t nblks = file->nblks; + struct inode *inode = &(file->inode); + if (nblks >= SFS_LN_NBLKS) { + open_bug(sfs, filename, "file is too big.\n"); + } + if (nblks < SFS_L0_NBLKS) { + inode->direct[nblks] = ino; + } + else if (nblks < SFS_L1_NBLKS) { + nblks -= SFS_L0_NBLKS; + update_cache(sfs, &(file->l1), &(inode->indirect)); + uint32_t *data = file->l1->cache; + data[nblks] = ino; + } + else if (nblks < SFS_L2_NBLKS) { + nblks -= SFS_L1_NBLKS; + update_cache(sfs, &(file->l2), &(inode->db_indirect)); + uint32_t *data2 = file->l2->cache; + update_cache(sfs, &(file->l1), &data2[nblks / SFS_BLK_NENTRY]); + uint32_t *data1 = file->l1->cache; + data1[nblks % SFS_BLK_NENTRY] = ino; + } + file->nblks ++; + inode->size += size; + inode->blocks ++; +} + +static void +add_entry(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *file, const char *name) { + static struct sfs_entry __entry, *entry = &__entry; + assert(current->inode.type == SFS_TYPE_DIR && strlen(name) <= SFS_MAX_FNAME_LEN); + entry->ino = file->ino, strcpy(entry->name, name); + uint32_t entry_ino = sfs_alloc_ino(sfs); + write_block(sfs, entry, sizeof(entry->name), entry_ino); + append_block(sfs, current, sizeof(entry->name), entry_ino, name); + file->inode.nlinks ++; +} + +static void +add_dir(struct sfs_fs *sfs, struct cache_inode *parent, const char *dirname, int curfd, int fd, ino_t real) { + assert(search_cache_inode(sfs, real) == NULL); + struct cache_inode *current = alloc_cache_inode(sfs, real, 0, SFS_TYPE_DIR); + safe_fchdir(fd), subpath_push(sfs, dirname); + open_dir(sfs, current, parent); + safe_fchdir(curfd), subpath_pop(sfs); + add_entry(sfs, parent, current, dirname); +} + +static void +add_file(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, int fd, ino_t real) { + struct cache_inode *file; + if ((file = search_cache_inode(sfs, real)) == NULL) { + file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_FILE); + open_file(sfs, file, filename, fd); + } + add_entry(sfs, current, file, filename); +} + +static void +add_link(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, ino_t real) { + struct cache_inode *file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_LINK); + open_link(sfs, file, filename); + add_entry(sfs, current, file, filename); +} + +void +open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent) { + DIR *dir; + if ((dir = opendir(".")) == NULL) { + open_bug(sfs, NULL, "opendir failed.\n"); + } + add_entry(sfs, current, current, "."); + add_entry(sfs, current, parent, ".."); + struct dirent *direntp; + while ((direntp = readdir(dir)) != NULL) { + const char *name = direntp->d_name; + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue ; + } + if (name[0] == '.') { + continue ; + } + if (strlen(name) > SFS_MAX_FNAME_LEN) { + open_bug(sfs, NULL, "file name is too long: %s\n", name); + } + struct stat *stat = safe_lstat(name); + if (S_ISLNK(stat->st_mode)) { + add_link(sfs, current, name, stat->st_ino); + } + else { + int fd; + if ((fd = open(name, O_RDONLY)) < 0) { + open_bug(sfs, NULL, "open failed: %s\n", name); + } + if (S_ISDIR(stat->st_mode)) { + add_dir(sfs, current, name, dirfd(dir), fd, stat->st_ino); + } + else if (S_ISREG(stat->st_mode)) { + add_file(sfs, current, name, fd, stat->st_ino); + } + else { + char mode = '?'; + if (S_ISFIFO(stat->st_mode)) mode = 'f'; + if (S_ISSOCK(stat->st_mode)) mode = 's'; + if (S_ISCHR(stat->st_mode)) mode = 'c'; + if (S_ISBLK(stat->st_mode)) mode = 'b'; + show_fullpath(sfs, NULL); + warn("unsupported mode %07x (%c): file %s\n", stat->st_mode, mode, name); + } + close(fd); + } + } + closedir(dir); +} + +void +open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd) { + static char buffer[SFS_BLKSIZE]; + ssize_t ret, last = SFS_BLKSIZE; + while ((ret = read(fd, buffer, sizeof(buffer))) != 0) { + assert(last == SFS_BLKSIZE); + uint32_t ino = sfs_alloc_ino(sfs); + write_block(sfs, buffer, ret, ino); + append_block(sfs, file, ret, ino, filename); + last = ret; + } + if (ret < 0) { + open_bug(sfs, filename, "read file failed.\n"); + } +} + +void +open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename) { + static char buffer[SFS_BLKSIZE]; + uint32_t ino = sfs_alloc_ino(sfs); + ssize_t ret = readlink(filename, buffer, sizeof(buffer)); + if (ret < 0 || ret == SFS_BLKSIZE) { + open_bug(sfs, filename, "read link failed, %d", (int)ret); + } + write_block(sfs, buffer, ret, ino); + append_block(sfs, file, ret, ino, filename); +} + +int +create_img(struct sfs_fs *sfs, const char *home) { + int curfd, homefd; + if ((curfd = open(".", O_RDONLY)) < 0) { + bug("get current fd failed.\n"); + } + if ((homefd = open(home, O_RDONLY | O_NOFOLLOW)) < 0) { + bug("open home directory '%s' failed.\n", home); + } + safe_fchdir(homefd); + open_dir(sfs, sfs->root, sfs->root); + safe_fchdir(curfd); + close(curfd), close(homefd); + close_sfs(sfs); + return 0; +} + +static void +static_check(void) { + static_assert(sizeof(off_t) == 8); + static_assert(sizeof(ino_t) == 8); + static_assert(SFS_MAX_NBLKS <= 0x80000000UL); + static_assert(SFS_MAX_FILE_SIZE <= 0x80000000UL); +} + +int +main(int argc, char **argv) { + static_check(); + if (argc != 3) { + bug("usage: \n"); + } + const char *imgname = argv[1], *home = argv[2]; + if (create_img(open_img(imgname), home) != 0) { + bug("create img failed.\n"); + } + printf("create %s (%s) successfully.\n", imgname, home); + return 0; +} + diff --git a/lab8/tools/rustsbi-k210.bin b/lab8/tools/rustsbi-k210.bin new file mode 100644 index 0000000000000000000000000000000000000000..c294968e0fb6520dd10a00a44358f3354f343ce0 GIT binary patch literal 72032 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͇@3@EG^LJ4SE)G1Cm@VAn*LosOyu%S#s+&{i8N(1@63`Hnt(9W zE87}vNaS*W?g<jXirR%joyk4GL( zXbFCd^fNy()#O;ZjyNy!J98|NHpt`a?DJe0pCY&Kw=7}I)vy?oA|(Wx@K>Q#4sP+t z`3I*z651oxyvQR*F({pYV}5l&m*lT&Dr)+jM_k9Wk&>~Dv}~J_s^M<ezf$%ZEyY zUF0NA-ijU9Y;`zgAn0F9-kOm5sog>UE$y_|CT3Lq$=nXwYa8gQSCtgTO?gF80zu^a zW>UT`&KG#PULCe_zR+J4#Ty(5V%~DMjrz;G=9y4~GIve1Ufm{hN62Fd^*3|=VgK5$ zxwLazA5xEYV>MgD0dP|16-hlXOHNOt|@9&>a-ffh(T(+%b(7%(u!ao9gV68n`U~c)? zmVruG=O;GZviy60R;oVb>?@7=FUVcM_NwK9cEt#6gIv<|R^M9a72Ib{ZQO=tBhiLv zr=oCb*Q1=;-I7R+Ma|UyJ+l4H!ph0*-)E+(KjlkPM$j=)4<`#yBy=Vb155Cz1&l^O!V+Qn3@q}FkK<{2<7xX58`FIQMk@fLqo5J)BtK{{NBLV)gr+R`UZh&HWHg3ka{N{d8J$24Qm9 zn)Nv8yCbz)p31pw&8NR`k-Fwjc)yR^OZxaiP=J2Tvs}LQ^oFxsQL(nYo8H74BW@$N z^_3e@vbNfSnPqcz@QL&ea(|rj>PSIeq1{C8fo2YFmM zSP|!=5z-wre4hq)vkwhckV)rygpbzK=r`dvV}VY3wk`ec3epigfN=Np=ZpjmeZ|aQ zOp&M}xg>GUNbrbbbu#x5JhXEdXE#Kz#qQ!22fsz`B8XzOrx2ao-${l^mNy!?vSbC# z>F6=BVx&C>@}gv3j?LGwt`vS`*c-wLfs48@uEc81CuX;k0I$&bFk`4yo??R)0(Ix< zrX_GPp1yU-cskZi#xuG;8P8=Ew$ml|whp!pY5lur9L`B8Ph|d7?q9h982RT1LMDsXAb;PRES#td_*P z`zXbipw%b5z=gi$UdJm8sb}MC5Yd?59G9`rc|FrF?J+sK30t7x^P{hnm* zqm!GN(svf4J6XRMy*XWr9Z4;=mu=X-A;;WPrL`)2G{+^i&anTb*iDeU8G*G&ZNV%p zphs_(ye8g4t7-p}`E7SbgBAX=8GEd=;CacpW7mmv(H2Ta+#saW3|N(dm2`@(wPfx=@4N>2^pE zc#dM%&bpK4NlHzE1ur_XlkqBzS`FI^W|1^cKJr!Jj9N8L7L{sw5p!F>9!4p|q?v^tSa|F&^u=Er2u=^E;n&hhw{^6W% z*e>ZriZ$3PXV}P}lgq|l;<8B=H1GpT3&@ebn4ZdVc5X|8#=JQOd<8`tJ|naiU+$5TV;(75t0#vRRh*?tOO?rm%Vl*vdxkX>C~FMoJa6x7F)6_en#p0c;7o`MMyhS!}`?c|M`X>4yGJ~n0O?(9>tI29iG=F#b zs?2A3nES_f-nm6Ne3XBmKE}T(BSMNoD}k;hS`)pLTf@?HYHH}($*CfSN}qpDdn%19 z*mFh11`)eqOFVXii02?;H{3_x7E(SD?O_o;)zP;CdJ;P5QhK_VzmX;VVH(Z&t)p>^ z-=%c!9>23%X7M|8T=9;RkMz5wWr6rjYgx$e(0u7T#<*HxVe5;;+TL!LSl`=(ClPz1 zhMoleVR{m=C+?vqf&WH&60s+$=}EMtik?JELi8kB@?Bb6iI!}or)AWpE%a1OPn+rK zDtfw)o~{u1Z6SL;@cgN4=N5_{fcJ-GJL@Q_0KLn0)&UQCUn<)AW-(dEmEi=860mP_WY|)AujV+^k5s1WAY) zLdYneoqY(fO=B#Y1o!=DC8QTn)^Es#c4bJ&D*@A)A#g-@IIo+Fpr-gGy zUgw;_7OuVKC-$s8@tl!+>9hprRE}}ZPs^NzEsGZ%JctUd7K+O%il zFzIw~Z5ro{zREeTk}zM4v}6f91)oH}gSN}s9sQg$tHo}W`1J7?=8U#G)?ltGsIa}R zz3O~T8zSBYXJGfjZ=ut6BHl+8ub-=Re-&|oi1$%NT#)4v-@pYq`~)soz)#?Uh5R(1 zI78&a$=m{d`UrGMsEe>B`-Pt zz&W3n{2BOOOXkDzoQ|)8^0M~O^PE$cTsQE5-SW$L&X5i~B`?LE<(zj*m>t^}r!~cM z#>#=G?05HXIj1=~3b)!9JQ>gF_5r5krAQa&G)Kq4rWQ*A&&XGRr>tFhnsb_?W9PlK zSwD@#RKAS1OJ4FtIcI!yY;0N370>DWB4Eneqfc^9b94kk_Vh&CqicX?I&-7r3C@Yp zAtXc1e=fe@Kpdvy^Jx18oHO(b&WX_>a?ZQE*4iG=8M+R5N)C^;b54v7k#lFTC8_q< zXMv}z-TfHn%xbB8(Zz7bOQK8~f+DD6=400R18^oy_AI&JfufY08 zyM>5wg!v!8OK4sc9#ojmr9TvDUD(T%X-Fb6=jhCVE4IUyGP|fg9)`S3x)v7gRG;ue8Pw`X4-$$~Fx;rBY2c^NjH7$9B;< z6Ib8WG5>`#R9YEV>&w0EN_NRS1t(Iho_*Z1ruE#i!U|zO7A>Rwe@^X^?e1PLth^#M z2ETt!?U!W-a7quSi$tn&E%}bZW&u5u?O5fo<4WD|eVoDK(@~}-PZlT(R)6B8-5|}` z)p95G?ll@bgIL3-99kdV21%C!1YvO~8Fj@<`ds0TFhRjrSe;y>TAvGXOeEa%_QF67 z?=!E6*>sPvra)WN`Yfb{=ya;lIYgR!o_S^vaQ{!`eLvnP=ZypO@} z@)e|UnrnG6uQ`OIfTu>n+eD7L)e`3mlWUINOCtwzSw@?2)F;;r8^gc3O*#cn}Jh*85PcnjCIcOjJ3|u40v#2S7^v?ztqkxh~b1dY!8s}@V8i+C*y(8 znE}({gfsDd?3o=#6h!8_P-^l@GcMsftGp#Ie_?*sYDjlT8`tp48cEaTED7^qK`<@T z4)`_^6}TRPp$*mow_4a$YS(I`wd=I;+6rx=7JbY3g)gz&i|j0AqqioPrZI#$X2^Na zUS`IHRo>zpJbDPe5wn+V3%Vw@Rk+5tt#gfTTk9I$rn`nVmpKPBOnS&tjFlLhLWY{N zYxIATp?WH6UWLcj?^} z&L;~iU8lp|z+J|$-}+}U3)cjp;ii&?Yd&Skr)7u%1MbhFH9hlg!QzZEO}{44N-?hl zA1Tiny{C~c?D6+CS&!_yHCRVasr&J?#p8dy$@bFTYl7Na8`b@3`AWO8r4ethi~Yej zPkFvyb-J#;U(KMDjh_8!n6*>zZr_df0s}b@i=404s|AYlwNsuwRZmj>*_5A3C+ANt zpKobb`dbj+`wc4JdVa*rh7{(T?37kB}c7ytvud)%hhkRSbn}>)~5zIvWk$C3PmjvW63iI zdaaLSU-W>Yqz8?GUC;r6QL2Hn!^8_12cowe`%HOItv}_vTaH|C)yvL_wpfW7PvxZ~ zt634j4Lt}r7(uj0WRBGbAr~~nPsWa@9lnT1X=f!*L`KHW&z3GlL7MWP-7>`%3411Xc44~}zG0J7-|bE4vFEb< zDq^-=#xun^Ex#g{@=q+eq~%w9^L;7F@(Wo%jcu9B{^R-75WDj18r)wvzZznfoKFqW zlCx`Ye2!`mk{A|Fc-|t0sS6PRSpNE!k&X?IJxG^?H>XcVuS-#@U?0y9H7R*|ZND|J z?MOl<(;4}x&_b6^2QAveeEfc)MgCzrXrb5#gBH!34jNggY0fT~ads{Qp#BThgG#&I&wYq$bzFV+u7&j}w~vNvGJ2GZS&0Vh_*$(9j(u zb0YOzidL`h8U0qYH1-IWXpGB$x-Eotlm&=sS#qy|ef&1mdcdZQVhlrnTq#GfBTMua zxRCTHYh`C3?-s`1k;3|VIq3$7&qMp^#;7*FETSlQ8fT4m;EpuRE(_uPNfC#sL|b3} zOhg)<*^}zR3`o(2xm|8I%b~_#X=S26+!PlbqbN_W)>)gMLL)(w(3No-i8(P=DO)VO zA!x+3(Y%V&Yx@zux@1uH^%Y!7=!XK1`xEAl&no!%$Xu72UN)=#ABqaht2y>GQ!j%4 zD|#_1<5u#jj(je)(_vWd_oQ#f`L#VZLvoexxPf(DKu512I#OChhouto$wvE@%>g4; z60VVc8j%=zJCp|vtnfK+drKku5c|OPJ;+b6VSVG(*ba*IryWTTYW=tBT94{6CJOaM zD&yvcHUl|j-kS3C^AS^@iQUX=7h@1B4gty6K{GupD>HG9uAf#i>`BNt!&gmsbO~QI zp^qZga?mgKPhfiuVy^^RDEEx-0@Ef^P$%sv_X$hUbwUSP$uVXzVh)qFHh*SAdNWRf zIK#bC*NXm?InEfmX>Ts{FvNNJPlnT6-DSqNFHsb;EsifTzZipcd#}+ugSE#rz4k~t z=C44owg;yO?G3gxk1>?x-JQNay*Z$P*43t7_m|V{!m5;R*>~=~iyS=PzKi|(y!uXX zPUKtX=sU5m82Var-$gbh_1*PT`z`{x@!|Ac(W!mc?_)Rv0kiKS7bf)`tUx41K1kpBesw;57wb6RzH>Z${(a~0zgyqAKbLGb z@ZFW@yXdl{z7t#!`}T*`cd?hvzH7RkY2N(#MBlkn%)WCxGJW5}DC3Oo#_@H1xL z#m*%9PAT}{eHV$GPv1EnJm0?aZ8`tGi+bLz@08`qedoR@sqZeH+IPeSAI`XQkY}|R zcm2h3yj+&(yU?p<-}$yp-*=(fsePv}VK{-0nSJLtoYZ$0eDJ=D-IM4$n)NkAcw=7( z_r(mA-yDxHPSS>zMs{DJGmF_DEfRBk=x6e-pEwlXg~?E&KRh2Oj-O1063R`6;=l@- zYJYcJqS!B=+#-jh6SpWNBksRD6kj8GLYqEj49~Q!`Z5>~o}bPA8P@rxiMcC(Q zDlXBVoC_C+ac*4P^Uj4!=)YLnNz8tBHZDH+x=fEt$T$}+v6ge=5}I{RT(%|S;`r~g zafxJ~3m5l~&V@_lp>yMcXybGOVwyhkZA!+)_g`n@LR`gkxq`fl&3Mt=_o-AG`pdh= zixzof7Osi;nC8>Td#?mt?7NR=WYX-(Jr%;7h+RtQrHvU9_0e8oUO=9L_;-v9;fqZ+ zJ={kl&VAT3ozhkr!dyxFex17i0kZZ}1aJ07bu1!S*o&>qllS+Mp#qze3vy$Z+*S`2kS03t?px2A}R!SfN>sUsI<~v z-Y82tay3{#fDhAW4x*@v8|Z}p#zfqIqtE}tL#U7TG0Z5VeDXxv0c4HMTZE5=s4-eB zd@LpqC83Y^Mc~zvQ%oLXgX(dDMbT3HHrge#20QiTH?~_vbcXxLQ$&2JY_~QB;2AMq zg43}S6_;!4+RW|i-Xy$1xP6HEC-wBLQcvI9@8PyPmYQh=+c~XyD^9hhaH{(s`L-vz znc~eRwa8w!ew!*Iam43TcmzlaS&2I*`wC3&VE{Ya6fXiXd&VJQk9iU#+QT(#iA+<= z)+rIhEa2X$HL&hSJv!A-Av(sK&pHvx;#7SDN7;z^)qc#ROuZe|yTq=XEljaCGVQoU zP0-hu3I-9;Hc(b_r87*v6sE5$Y@#xXK_m@aG{G`LQtbqBwd%qBGT?8F1iE1b{3K4T z0WQwNy{(NFB)^}c69nZ62|vhuY78Ta4!pt{u7b0Z1R3K%_;-h1qmgCMKo@kO5fi~NnK zQHoizJm8<%0k7OrSS7=r*5Ot}2zuyPa^Bd>#%L#e5)x-4qA!I_fA|B8h4L3z=Xx+A z2WL?&`sp>Y=0D+kf32+5{h|Tt!h51`5ir#z7J+KKx=&cG4RPO;W%WvmzA=ip_IEjl z9}idG{(jM-HFTP1jwp24`5l@l*n{D46gE$Qkc8+#P>bajKIzc+f z9*>GM0C{+SOl^0Yay8bR=m&-^q^GdjcMLxsE14i$I&7yRdJ3H?ko|WMbVUT1#-J@A zPopiQ$S{pT|LtTOrW9D)<;^XMSlbZA;^kkUF`XBz*5l=KIA*VTP7l#65V=PiB|kgt z*H|CpTR%MTgSd6kb_MnfwyT(7Q`(_b(sS4&b_+^Hk5L|X*vvDl zJ>w)|9Z`O5$UnRN5`x{UV;>>)CB*d;-v|Av?HYaWr1K@26z@-b@AId(J0(T$ZCXKg zFi><;(}fj#nVz02B|Se;uSQLJ;`F}_5g@wW$(~oNlx?cF4bOREXSY&%SN&aY&3R(i zZTdgf(C3$}@1u^)bJTI+yVr5-k@r!@x!Um_b=>rR>Nr!R;Dha~%+x!>bk6uHC;NtyoyHc$Kn`Rw<=25=cm89U8nSJhhi$ePal_yuAV% za+q#VZw!zhv49|KaWO>PVvUk44p_Uf-f~E4u`^gl=GuLaV(kH&8oagn95QbD7AyWg zP;`WdomV5MeF15>$R%osK3EJrHtw-4Jl?BCbY3$@zdlKt@fb@Wr9h-y-oBQN8I|{6&-pO7+m+BKm7$ zC)S_1$&2pb(gweev+_8#kKKsvB0}gaoX!(>5J7jvSp^I6P-2xk#m7hFiOKT}!pBE^ zp_qic)+PO#wQJ`@Hf}CMgih=Rz=t(`AXTnl61sKb4*E$e81?6J{5>UfYatOG*{De9 z)?`(yMH>o4yk~m4Dnfd-`Q6m23x35=ngIn`wSW?x&04jf2>8f_o6AHrf$OO4(aX)2 zC(>t|_d1IlkrFe<6=Y)+767`7xyn12ZnRI_hW80`ZP}~W9?J`C8)51c<6d3^QAKK{ z-;R5h0b7R`>vGBK|3<6l0HlZiW*TW(Es`_-)c|L}k4WTh@Hmec!UNi-o2yFYW5S+- zbQ88ijM`GNgJe?caB57P{%5Lq+pQD@z#(RvIdScLRd!aQed4?O zlL9Y61?-z9W8ek`N62ak+YT^bJAjlw2G?H55!Sf=qihExo1d^2K(@CHyAU^tY3#V% zhzSWS>hcw{+)U8O_nMTtoG$KiNwUI;uTjJjNWgGo)dUGDZbf2AB5Rs-9x2%t0$V8h zS?2l}+0x82IKE4%?XZ7v?P9N)`WkbG*@w`}%-oSe^Q@2Hb+V^(6n0VC@dnE(jyVdQ z=eteTD_~QoSI6Fg9pFDGf*4MRAqHMK?viy5=g9fT^L?gjH^m++BJK+v5^;5f#fC?T zr2qY#vl(&OGR^UOP*F^55cN-i9e&6kXhvPI8&l1LxM2=&rRYt`l{geGi0?+ z^3?+-4({j8xxsTEr+M#B)av`4oExx@4i8e%{s?(Bij$eb^X!;&WR=%-8WzRAHgEvf z?N~&!HjPn^n6k)Yz_w}1r=~4jQz2s3-C^p}SoIg`of7xx2;yRl@oots*`6$jM!>?~d#94@d4y+}Qa2y_#$YU2KMYb8zv(!Fwm|05WeR`?lW?;Ce_BbFJ)GHm?5 zwa`$QBK_MLmx1plTCMy_QOIsfW6CvQa)qOlI9RqXo5ncF5&vx@t=R5m?#0DCW=nIn zN_uaHsbMUn6B%nfw*B9YI1Bj>|RE$WN@mg?ID%?NREYV#yD z6`ac??oD{lyGBc3tHe1_iku@#I-X(qb}3@ynBSm90UGR%wClr?=bsb%G>foypkBf* z)DBt~)WJ_PPn^w={JxQ?)LfP5gXnJdMuBY@c6_pW;$Gjs=dZ|elpI?*()xPan=8_^ zBk(T9&5IJR7R&>0wSw&-!b8ugs)&lAS*0-Vz| zMmFiKJx0GXXdL-m&^T7tl8y+2G~U{V{I@sH6El>A6#5OIze6`Ndl8irdE&7<({=4A zZdW|6^*RSsZ`$t5EH6li*k7^|QssC$)D@FgLdJwC#tZUu^L@Id}9;3@5xb@fp>MW`b{cWG7H*!4B zVgKzS{;00KQ5|%i2vowx2N*6H&lJ*QUDsS;sXkRF=h|pLOG(HyQQrleSAh-_Xr8+S zD?h5QVqY9a%>PxY16(_RbAU&+Gp)U<*L4c_rsFi$K+yT7R_S`{*~*f)Z@p{fI|uK2 z>hK$PJ@x9&Tb_E&INth3+lh`-kDvLsfa|rV0wu3re9Owi&)%}_od@3j-dmNG+uppf za@*@AmD^t1{A%rCb#Jat_T2(KHz4f0ep7n+svx~Yn!bk925uHSiDLaTJSkeZT2VXbbrH4^;|3ACZ+l+%~bu1 zoH|f$Vifthnb!0v3Aa1}H+rX{EUB=ap;@;BD=Xr}MXDUjKQb`-YBRNyo99D23OAE| zU5wR88}ZZKInwhho#IerMHEE~8Ygk0YwT9t7`a9_;CXDo^VoprvC-oVYNr#hl^>8K z-vt`ihYP-3ZfaC5G`nJlr7Sy)yIonL?4UV60RGG9|6CAvhE}>xY^^MLXR^OBKa%!fHskRIUsc$-%Z*O@n$!8b7XIMoGQMp#LbMkdVQ&saPEg#&SsRg z4sCdnu%c8&^kKohVcFxh&(y0iD|2Y%^NMmq z(CDQxYV@W>=IIB6o6C$H&ZGX`rV<*-2jw`uA!u?!tV$bDBRnEJpJp!V22AX83$FKl zPElH{ZIW`1uct*ybeyrA-L5U*6!@sozkUS?=6v zoNOEO`#Oqd0V|vXm}hCZGuVc^ZjBMYzrAPy;-**3Q|r{8g)L5eKWuG3)3fh$;FH;v zd$!cK!wQzNXWq!Z8}$=9?tpjT2Kc-?gT{{5V`!5zE3&7BXmec1zqTWgI?w@c2)>|a z^oTYpLYu$C!2H4eihDz3TcY1Fpl{E{&W_+nku!3ehI^fE2p$DTF8pzvekc8dO|aBo z1N{6KkzA#g;{@#vZ4CXREn}L@Xje|RgZ{yco?}df6%BcB1YMPFJG2oe@a+PObhPZ& zfd2xDJR#b2c|%_Qm3#6CTjvqUO}f^f)gtIv)Nsj-I%<8ZDRqZzf&K|iwji=zLkrR; zwIJj(F#2wQ9M0&!Gk8tlF3l~ ztJ({RGXyO{Uol8~;c@(No6P6C&43;gp#88@U)gtoZk#wCY(2Hen_sklaT`XD1)~Z! zy^D$4y2-b}>Uwn-WN>{=;OYm;FHd>=Ov&N=D|;@v_8{(Du#)7j?Y(i;EtAD}Tj-!u-p73U|#w z4fo#ik&>`;{HgFtd4ANrkVoY%m1I%)LUVLRFXGW@4H}1g_10HQ18uK020C81&z$9b zASG>er`gtv%=R~}w9n-6;JAb$W7CX$5IebPG?OmISY86{P++N^;W1>(Hl?&1rvNz3 zGr?)zTr({+-%JailI7xQ(RpTC*Epwj&oj#@IcA#r7N^z3)7)77%lgJ-xj4*FmYEiQ zQ9z*B)OK(=pKmYZ+l;?6MBJRgzuEfC%yEI-nvr!j0x(px$pWg}Ko zY&$V+FKkm1k>7ZJ*qLlidhhrm@9pm@v}CE9njQ=8`6Ts)H<6enXpK2*i?tGQ))C{y z;-Ptq_QP4j6_x;&D)4=B#)LjJ-i5fQ8X_Aw$6w-TinIbGU039Es5FB>mpu=UDvFdq z`Vp^}h%&|gXJ8h5QGW7q=@GFLse(EAb<`ENIY2l;VEnopJJjWjaOQWEk~1s6~mx z{{$Vl)xz2|Y?gqv6}4JCjTC1Zav8lfv>ndfqyf4eNpuXQYHs=>ZMvSUK=Q!FnW4|>Y|CX0TaAyGBH*|=4 z1)~VwQC9SP(!E~!bPI{Rfh5#VeY6a^0NoC>0IQyIxuV3WHk9ng-gu9=H|QkYl7}x{ zu#jtmlk35qNJ0jCdgQ%ELISyQ4*>adXhXCnJa?)7za^K7J(XN)75Y7KBNF$dYa)!k zJYancw?0``yJ*#YPd{3_44NgZu19jF^kye`pU(bay~{fSi0*anUR;$0UN%oBh+k>Z z0$T9yvX~Z?RpPcA)0*zM5taoX$qCMFFEeLzpYZB5KNIKi0Ib9d%Ou%fn4>?f5oJTdy(L2KiQ~!#41Sz#(ShpmdqK@6}}I$+n~(z47J~zGC#X=%q>b zL6MZD`=A=lxZzbqd2u6DEMfhjv6Z;Xi6o^9H#=qa^SDcvsxd?xV3{Q4xoOisVA>Pn z+=!a^(P!xy%n z`P;s%ikj1{V~|-X`)6may?b6gJYBE@g9ttT+yRC#)FdEeY-kKqRErW3bm8gWcvx ztM2UIsylm*23sed4YrOOh~vH~*gCv9vsBX5{p-YRg*DM^z8ke%KYm9R`b4aqW1t2xv+>E2M068R*h$a_A;m*_`1FO5%ej?l{1 z>s55h{SMkU_5H=v6YD(r@aVI6chA|cz?unfQCD9{O=1o0NH_10h274KP*u&eOYFsQ z#B8h7G>j_*t&m`4|t(=HIrp)G+(_YLodnFfQ-G!$JqlO_6%4Bxu&_TXU`1E({;U{7fC}G(>&@B!{|BCsOqZJ_U2YftK!Psr{QvcY zO!DY_56`>*^@L2K)&F`zp6x08!95|x2<=+RBh*$RyvM`{bsT}kNTjxZOpf1T;XTIV z7cl_-?|VX;>lI$Pq+qtl#{Rlm$2JuDi`%7gl*_9_bYBln5zO3^YK+#a6{>+%TAMt1 zquj>6WNeWj?S7W0nQZcQ((7}oPQyCnKSr_J3vm`J2tUO7d4)>Hj=hCSM#qky+dGC4 zAMuED1UIsemdG)TK9$L7N_sW-`{fk?8n2O%T+dYUCBd8cT1R9IkbD*#ucZikBeHh1|6ot;v zIBN{d&CoP(yLPr(mV#YdiC^?z*s9HXeoxH0h)*yFpcJ(cWO^*IH~Wi}>b!iclMb+CeeQb_O!?TGM(;boI|UtCH54(jnBDAJQo5_H?(2K z{~1LguMzYNVo^bx6SRl^CUAfULrxcPa4-&5*C_ej0Ef;$DL9J~CK^g=-8AX;cR@Gz zCrk>)Jf`0t<4~Bp-BT_x2<=ytr4_d0h%NXV=MnIPdHV1ZyqCuGfhXOju1wm|9ne3H z6L;2mmisr%tDk4HR^5-A8Gq1?H25OD*do1{B3q0Yt-tEkuw^*2ZQMELI*ACPfBZsL z$uZzPk&HLuFP=7@hX0UkOLTeMGQCnnP!*IyL=5br;TA?Uth643we5F*NNei{EZLv# z8||y$!7yU{nKXQ+isNIwvKOMo2}rSxwE9B-s$Gh+7m)rwyrAtQ^{C{xFHnCv2lBOs z%tVj+v}y@US%y70Uk=t3Ji6iM%=()hCkGkVhs}{6mT9&bV#kvE{J+U#h{h&ysCny9 zh^+9U-*C1};AadP=J~KZiP=9GFBrczx?2=&Vn{L1&H0v!z9;^RkDky+&AxY#&rUo# z7;KW@Fx3MNyM?%%I81Q4wQWah%;I&ua>slZ?f6Msr8`x??lW-LvPsqG0u#2+VOl&S zU!~T?`AYdAt#q&|r913j%-GKwIIYBlZRJD2!<}W~5zC!|$LRzfp?VV!__|EO1Cmkw zO6#O& z3v3)IIwFp~!2QOy4Gp)|BwN5&(H++@XE$LXA;2Fh18YipqZK!3+@)B{;Pqzmx$gwi z1)i<2Z^Q0n*M>@AGZZ!sc^dlMZNAKwsT|lcxz#BiRYV*Gt%SwSIkB-HF_d8s;TWnJ zWvcgyoDQ*?&BA_jo{nhofp(=g&=G6v-quN@fGEhLSajc$u)m8b`E2SR1C~R;<)YwDqjqtLXl1wL!^7tU(dGLFH3t)jCdFVU!{^ z8{Lb6Cvj#Desy$a0N=LIleiZ{MD`c=V${*Mg&wW@st7!V@Jsh`YTYWJXx&S2tBuy3 zMG;}UuRuwydl7Ea(7LfR>7hQuh=py-zREeQy;!@Sd5*0o=yz~C_megb9>hsG;=vrr zgLE?O3L5Y4nM-(($@`}qGVW@<>VZ6Kjmwyj^z@GXS$jqM1Gps906$u+8>$eIJvATy z*wT`XxZlRGF^W~Kpl%+{zT_x%uy>-wM2c#$BA*@h1e_6}I}Bh=$e^1^=?-sWob2#C z;&CsVf|Dj0bhZ#DO)Rh{$Qx}~Zz3zcfiwDYOn;Ph2Lpa{lAC|azp>{aDgT1n=L09* zk^+2ZHrSwn*zk*f0ltU?uJr(48M}w@CI4%}jd0w9JUvz;wpUdycjB&R$mHK$mj-Jt zJhHitNQ#)BNLxzi8_{YxJ2?=^jV(UZk`XIwPUso*E2Ic{H)CdI%YrrF5+@$>74o=6 zTo?4AkZR`6GOTcvm!uBYtvt@ZzNfU8wdcq(W%Q-!RTk0CRJ1b{{R3E_uZ5_Sf?uXE zwR1aml7Sm;K@zRy+mLVvMzio`6?)Jx>p)_L`kFaI6)Tr{70ghXnQ;v$_P`Vk2s2d~ zJRMPIVy24x%#^6n4pXA?=&l(OGlUr3zGY&DAil@xS41c8N$@?5Os!k{wUln4#%1Ui zeq#${{~4TDLVRrcX31LQ!3Yqu_iQ>7ne5RgRs!MUT)%Bd=mVnuNMXW*74=K~Dqml)YZ##f1^~bcJ{4|`5!!i`}SCJ9XiUey#vB}B4EcHWJQ9! z!|vzu;;6#XiSu`v`4ROSH(YRjoLgqy#9n5Ju#LPiRyg6j$GB$~V8J)nSlDmBBjf9m zjz?U5@N65j4#{>6x<;+zMbh)hv2{Tgdh+YGn|T*6JA9AQ7&hN?VZpdX+1PYhe-q}Z zF&oWIV>gPwSu|($e@kje!oprMyclPkatp{}rp`8Sx2iEJ-_ne6Zc$SobVRIGtP_ve zTl$JLE=oR>H^)K_5gsr>6pIqCYQk-T< zdq(S93@=oMUtlk8Nhz*VHm*ohGS(G_tFd?dZR{QI&>qMBaQnB!4)QX3_Wa&|al5rJ zscd)#BJ#(n)%^FiSem!3uX-@k-ZmWmWb^1Yh3Ovp)}kY*!&0xRmTa5HV%Dv{LvtbH z(Z3&Oh2V3VfLHjpw%9BIZ!XqgZEiwu=nnPuczOf{Ff>;wzyOQZa)E?hpnTJC{D7;q!5vg%ohO-lG$#ww>=i!Ew-VBL%1yqfBr{&{NVV_UYk zn>YEtJ8L%eBIyNL&um1`;Z1p9U6!|6uihE#xv}qdwEyg_+iT=n%19XSb+5jrwx+W=jfu2AX%MTP1w!Ve5{!twG#?1?=(_Rk{5h!i+G? zE}5%5*1bik@0h2o+4|T$n-ukg{~JG_ky4YY;EeOz{)62%v!%0oQo{JE4!?c)w@t%4 zA`ESZB|G&(#e;q0EYj~c*w!`Q=Dls;YqoDSf7AQTfxFY!8aH2aGoRt$KJkui_vD2c zH#d2{{0qj`j9Y$lWB7R3;%zjkGS^I5EmHDoj>{A!d%8ZNiG7s(F5Lxg-M$lgIz&ie- zM`wAfcZNT?W`xUyKe>C(b|tf7Zjf%m$+nGHT!Yu$w9yt$ng4%v_a$IaTv^+4vI`;tY?VggOkXe%@b-7ZO-p@9ZbaY?(zi9<9o$YhKYA|#nC zngqAWKBFd?OhSSAI&(u7xS5lCH?~1HaX~bZIiR@dV$( z)m1RQ0aNkrw%{w+MQZVAbD&-aZLm*Rk``PaL^ZV;vPTfABLf{}-Dw<@(iF;H@X!>V z?NkDu^ykm{X3%(OEH$Mc9iV=a++MwZnOc=+ToYG(^6Z}n%k|#ZDb{Doi?4tyapS4uA8;qeewNaP7Q?a& zZGmJ7zw?Lj{cw*Zf}f-D1Xsy=T-hUALO02%K9W+rC9Jt~+O%22w3`UeYfle#?OD;e zouaExL(h&l9Z~9Qp(%c)!8I`kZ$TC}Kg)8dL5$xaq$8E*HdZ*#cPA>!wW?*$M42nON&Wj@I0#MfEQFs?%mR(DCDr@Yq4a_AUjgJaW@h(^2# z3Ey1VVBKU{YmUJ0V@wzs$@#=TrFWsXmiqn_gX|q>&7mCanZ-SH^cz`*J-NNwNO1Yij+j8sRgNUJgP|58omK$5j z8cJ21p|XO}S5!<3{i&_Z;Oo2KWGOeiv@PN@j#m+kWmst!!(!;mXK(hkh+ABY)=b~07g&WE5PW+9VOtQkRSN5iu)ntWD`B5-@p4$~4?r)a zY7z4-d|$rS^*mW+-e5V7mg(W?I1j1D`xI|k^0%aF=Txz6D#JQ06N{nT%5Z}yz>`gYbI)`|qnn}HBUnps) zF8oZ{iCdH}dqBG`?m2h-?E9xbZk+qmgV!IqoOv<(+@iC#({EHHSJbVJDE-4R&Z6Sz zs3t)lNVXp`==Za=YvUQWBbDrJKrR&N*^_0lfy8e2xd-8XvC@J|hdxlps3NgZE>uN8%1PG)*oHLil{)Xu2~s_55)c&Fj> zU_w2xmLTU*s?~-i)9+`udy_h%mY6e*CEX{qbZ0#Mc;cVcV)ZpaZN2_t?hkNIg{GyPMS#@dVR#@sTXISyXWlO(+@UAtO>Ge&D0lgyRNqJ zY4jHabmytFIcb+z%J;*!?Tw(U)%kMqYZzy$QIo{D)xl)tBW~`ZpPMEA3EF|AUK>b` zmriwYr7B`53nK1im;J^2){z%WtyLm-M#a4o1p2MSV86`0Sn6)Q#)xf~Sdqn(S!(1_ z2>QV`Zp8Zjs=L>U=jX8^`A)^9`QyngQV;1@%}ji;G`Wm&HZ@=R$*{#nY8RA|_$nqX zS>kHCHV@SIL4AkdcV=nst&vRaH4W2Wb!h!Z>&5kOcHyHeEi@J+%FMO5so5DBLh%XKPe>P%R-Bu!0tyd6aC z>n>AYr+)Uz;JVGZS70Af>v|Mc zp7t8d67dZsX=^xUQspFLS^RjhmWcT@Uw$B_+eBaVWnh_$eD@ znTw`zRIaDQ^HYM)mn8YHT-+L^gjT*2kUvu@pN>!YD)EPS#>l@yq}4$+`Kf7k!v!X_ ziQzAN^ExvH-ez~rir{;3<0}H4#nP3*!Lz}*?kL%jrP~~9m-NdsRn(NyUxAy-*sbpP zFjCYwY}GSl*h?z1d(}&%a~Z}vJYBH}C;Jw6{h6);>^i&W^zp`-KizYE?&SwBK5{Pe zZ1(9zjdXM#kiTh3O(}i_%hGS*@o2}{++8^na=u>7KDO)836FlgLT%lXm6j#%x8%M- z`Nc1SD<+CFhcm-dMcrO@6uy?SR~4TshU{hCIzgk`W#9!?)bB;ed{$J`^Fdw=-Aj&F z{H@BZOVKn`P4$e1{B-AWEzgK734;%Myy6(bG^!od21^*B`_N}py5|g+{-#QQUD98k zp>&>|QAyWf|NHyZncxA?(lFIQDklcdsF=!6+dIu~`o`%@wQHiMC2H!+e{0yRVwsI% z+V9AiW6p{1yH3<&rwQNGXHJX3(c)h@Q9M>dhP)>RSU3jnX8Ta{QN75x_`n%WgE#-^ z2g+tCMDTCiK*IVrDrkVh-!dK4cU?xzpR1W4=?^B zc=0o;3@7TypyvCD;Y2nuSn%CD7IW-Z;JI7y%{yG{(q6CL;8?X}U_qTr@(0TeC)CXS z;4z#yOgZ4YgM)HAuC#M`Bw$tva8=wI{j-Xx%%DsWdk&! z0TVQa_P^saZcd}EEq|755`VLx_V8R{IC8b^$t}iZ%gBv&#v}8Hd(ySG{#$FWEh8(} zf4a_jW<1}tqWUK>bB{_4K@XqiOsy%4uX3j8)*-h&TI)g?s`bfMXKJl_5Ax5IYAUPQ z5{NVXqNJSut?#W|0q14cNsHmekIu<7 zG*-wfWr?HCMEL)S`7V%BEHsZW|H~i&@??(qn!F96zZBtoTwfJZq zsns+PZk2|(HRzY-UT>wllWQl{Ng@BX4&SFc{$ja-8?PEyCi>U0d#hnHV$hPG#N{%t zWUDVUoLh6X&0QA?PvEFDgY?n5ab@3sp>X0?h@tVclsx-ux=R>eE|p4#fG<_h5KU}d zEB*aoNx7P=yx)E4n!h;rI@$SFHE~UvQ;pd`R|pY*`z&!zu5n>5&d+fD5+gP(Wevd# zNv$QAdA+LSNC;+;M-cb*Yn&%U)AvhTF>3*Tp{6*gs^l3 zxkSWmy9kr2t2P85CC4iLs<;MLYC+r~;Y_^-KJ2*N7aF=_kZLr&@eZcM8!uS=HIeTXyiBj&&$`%t0M8e z+0ZK(P4cedh#Sbg7CHBx-B$C}=zx9rJ-(cKsc+Ss3r?-MSbTXj_U%M`Rjoy9C0Q;7 zU&Np-)s8tlbEw&wIzD@VT7A*F&TwxT2}OB?-*n8N75Z+SjM!H#e9z5}LWxdOThc(9He|PoO|^r?oaKWLZ)T2DySez4Ax%53 zwjS8E)72C<@25kSmiTWf<(A^`r+liRY_4HO)s8vA%r{^4q|2#q4cd!(S(i^PxY;AB zU!cqR+8{A^d5E_z=c~HwqP|RZG4;PvWx%^A-zeMul=wF9Ye8ZBb^?(VChNNT!z|fRcE%Ymy|)r{6AgDJn1(w;!-dfMtK&!QbY-KxKV&qWl(;E% z#3MA+h({3W>QA_z`=?Pm>#yn|Z|+~^aIT7{k1?ryh1?={TzOK}bd+zUscI@c+}b5o zt|RK6Qgx`F8D7u#!HNlg3;m^>ELWZxDBB(B+NpS2qy{t1(rA3dF6<@ln^Yy=r9F+g zZv9{-RMl>Tl4^6q1o`=4c+dM;`ORF7OXPK)$W_<_z!|RcOxU;XD?GdRtG8TqB}Dc< zxheE_=FwztT2hs}d>Oyn|C+ibIH|#?LCdr33JG*JT{+e2{=0^gpIEN=Rd?Y~=z;I` z&SAh*id!9^6_=i1Yc=~*jC>vH1+NV?p?;{#?suhDq)kxjQ^pN>l}3J+HV595R_^@{ zZt$eXnaV9d8=`fR1fB$}jNdyUsmh>U%I{u(1iJgZU0eP#W+*3N+;xo@uk9gC1#??5 zL&?d{b1zYS43a(#vOcbtJL@y6i#`{-=rg^OKIQ*JA0@tjTc5Np`ux3%K6gp_NG;L9 zU&;CenIm1!R0FItYEoyrY<#~~gLGcKN;VcZInH#lNb^;9nPQR4hb4==wb@YKo$qX$B-yyj znN}a)AN4feMprW(&(pv0ch&LkRR=azSO%2vOAc8EtZQgXxOO)gpqclwNn_9~yCWHY zuZ72I+)hE2g3f6rlS`TCSHzV=EpW7Mb^W9gqs$wc%Fno#oA=92PYQqsV~uRqQ1W;U_I zxXbKr_9gcFkfyIc_`zMaR7zXZ<>8m0Us*$&ap^IlEAClKgC=3V(S)~x78^7vYG?zE zzDZGQZp7cHyb3WS6!)2r8HH&I$?!p1-xT@9eJ13bKYq~ z+Vk$<|Hpavm;bAIcjmK>yldRqIq#&AqirjEWW04@dvIEDc>vy^IM7xsjaBJ9dPJk% zv;SbL-0OMPR)%faihckyQGPm%0_$9%m)hDEkfuXHW_+ikdXwp6&5ORQs zP0i%>G(&kAafgm1avB;c_ig>nT|2hc6H&ZMUM(|}r%6w}sqk~b<4uJJ+?~301nNe= zy9R{TV+3=ABuW0txGhG_Npd4iJbgw_dC)7}CTdP$=Kc!dveU?%Vz>HQn`l2pm`Ovb zS56XtJfmH$-nXUXjG8291hO*0vHp$C=-S=cKWk z<`e^ewN3UA&XkMn%WQ2KeEh>|a^SaO_BD8wO(6$bYg~pa!5)u9>Y2}AhjGAd=$R#} zR(%I=L#7wBq$Yd5CUC(+Ewi_zqA!rbHlCCB@yIi^o;@Ztr3M||1SFLbM@^Wt&t&Ln z{Q8aT)-u>-AU}!U8=4^b%}WFlrwvZSy1bmL`l(6?%X6}4z2LbxZCJ;1Eq)cp;0?6Q zpksXM=H{Vz-x-(2b9BAj?MNfro43MO!Y-BfbZIeF@^$mObWN0MP=giG{iM^3B=-NQ zFAu#q;#`OJvVQ!o;j-q9_{|;G^r87#`N@?kS@Q)8^sXT7pftr6t$52jDM@t_SueI1 zz2e{L(980+q!;ANdVQV6+ILCc77RRIQ^u8}{VzI$xmcIl#pnKjy+Mbux4)=PqF7pS zf4n3O(@sY)C$vop$MgKkt9UO`k!nR$yHdCQ_AMRUP| zyr>0Mdz82kFAmCc*bAcytcBvzF0nHFPgyvY=u9c4_)&W=t3dx02LU|C=- zv|9?Ia_vhkVq`&~ET1%zB6{_pa*LP9k^tqJp3HW;B{$zL*z*K)R+g~LYF{YiEwvP6 z=j1J;&+nfi^V4g3F!G5U4wmT;6*vm*g_+ie1-x4M4+#|V zvV~krZeGC(Av>=?$T6>=L`mLtB|J7~Nr$&Q;VW5>$;vrD$83j;+^EH|G0~D?a?Ms- zhdE{|;ldPoYjKVMI`o-qDJZn&*@WoGn8;}TDkVH#86zE4Uhyg9Zs~kk2^X0z9fEwM zr_;0im!73V2_j#2P&+u&)Af&2HbIy#iNQ_#e?f?nXc9@eqSx)zc23Gd>o{N0L?+W4N|C4_(UjaSu}J!WJfB7u ziARWhBt%8hd#EXCC7TMC6ag_eSQ^Rn$qGz8D>u#hMD zxjnqoDJXnD*B-su(IeM$_N2#i?u{=+(bwCKeh`EY+7oYjBQlKIw^RRI=*WbPMSIsy z+EdW%a2qUkG#ptOR2Oe~cOo8aswCz@WCU$Rglw}F*_JG1TJ30!@3rJ)Cnrysa$oxN z)R~j+o7|gUBD)y{E2OR!PRc?@W~7*xJ0`{=il*44q^!&&iq&cF^w!?h;-4j}omny#{&X(SJ=R}OK;_&y zd@tp{3rDx`1=a-%y()~9dqP$onqga>UC2g-x7y(F)0!_S42j)jX zd{%`ob-8Gfij<=5p1HF9J?**Yyl19NpJKz1mrPji{0uP1uJDzJL@O{=?^hNC#D(U}9E%|4Wm&p4 zG5`Kh6lqb~`!yHje11SF55sY${hl`+pE0Ey`P+|9<*(=dPYLNQCkpgXpfmr?>Qhhr zR7X$a+GC@N=a`Wx!ijkNHcY^ifq-ve&*feFh)m!AuzdblId?ZcjoFbOWzWmYK_ib%3w8*48?AeLQg3W^BB*=~)+7LMF!X_JtOlv$HJ)@SF<;Nj!O3iI0{6dK_2oJ$e5(Q7V{@nWE%}?tF9qV@D&&9CRnxC_xtG9+Q^zh~%0UENdGk=zKDTbgZM#7_(f;`HE zqbELUZ_m*wez-EC1A}vmRN@LObmAuqg-&uF%>osVj)BfKQTZOn=N?NT^!9dqY(hqA zK|v?S=M$wqd*Y?ODm|utF54T0yucz@X%8zDI>d$0fsW%*-p%l%#3c?ZYF_~c*yciu zkb{=Jd-?odtMG*@Y+{tzo|kLwm>0ZpN!ELXoDA|XI%L{j-XtADXZ5}L=YyY?xts8J zw?iTA@MHzN<<0u%`n1avZX#!LZ0c?BpGG;c(+ZKPeuw1B-J53?-=$TON* zH2*|PAqM_RNUEW?e$|jm+pb@fUOMFJtU?cR_dqVKvp133o)0~U>!#;(kVoqaie-VN z5CcScxLzQbmqHoZ_m4%gQEu@+Z?SoWV9gdV`&uA)CQ?yvvZ$T1@Ddm^%xCT@r*l&z zMWFO32d~QIi;kh_f!A>p9=J)m+Viu$o=Exn->oP^uXU{_|7yAPmg6ax-Bb4qyD9Yk z=mxH{^U;2wmwx z>n=@?H+?8*bdKVm+P8ax<>i$~Ibp)M?umuiZt>Cif}ZrC^9f^n2tQK^-$}ol`Ul?W zTMvHfV|3#0vHW@SZ2}MN$GquJg67)`osK!Y!&iYueWae~)JOHEKL?tB5A@%I)>HU> zz0ltUJ+TKlAAyzvbk9zjk2Du)>79;NAUV0C*ml?ROUURcA1{DT>%C_>G0UPfCUmMW z95dgU=qpyB`eo%g=zuZ1z+xGN@ZNSf--~=&|L7RWJ3OV|-UIzd&<6KHr|p8boS#68 z?t$J48ttdO!)xHc^(03JdQW=x0lg>r{=LWv1-++y3lg8nQ|eB0Bqo{Adn>(PV1lRW06>K zv&mV8DWY}ZUZp}%r--)i-tK7c*+9a8{=fiWATS6R z4BP?;zz|?4FbudA7!Hg8Mgrl$C?Ep34HylK0V07YAR34PVgVx%2gCy=AOT1Ol7M7j zEO0w;2QUt}6G(xs;!%b zQ~^%|yAW-pw|KQz{|iZ zz^lL?f!Bc7fj59RfrG#y;4R>7;2od=I1C&CjsnMkkAXh} ze*sQFhm*i5;1l3eSm?7`wv)f&`g7oKz!%7aFL7=}_^*JkfhOQI&K?|_TI_rU)Emw?N_72qmx4Y&^60Db^|1bzZqfL5RlII6T49*5*V*(`N# z??2IWZSUid{$YUU=-giRvflBZRIR}OBIciu;k6e(}%>)1v> z8kKk-QVgJm|LO5jDXEh^dLDmvcbq$Kh@i3OSZoXI3&ZJXr;9vn+U#+Ef)p+zl3IX} zPj#|gSn2K;`Nhcscs^116?QT}-&Nk|KlbboKcUb)%OQf%!Omu8|EIn@n`4S zqcR=V98A{IRjT%tBv}wRWJS*^dMURg+()(#EqnCv*r%U-OW=-4$LiN*yXoJO&)+I% zt->!T;pmue2;fbR0gc8XJf1NPxMf^&2B}cu=$31w)1Jb?+%>2{cC~}b!l)@=L zq5F`fB1~r*!wPQUDH!hi8z)x{!4R6cF(3Z&i6iAs2MP13O+6`roSJro%k`7m9n z^`x54`^!79h(!(YCw3{rqKn(KOSe(Q7~9 zIOREAgQSOd{N3#9z_K#kB$1D`BRgF;kxS+_Mb2BPYMbEo`&DsqE2mP&&|Fr2p{uCK?=lqA$4}4u( zxo+bVd;jq29~%xIJO0J#<~E`V>^mwZE+KjB9aE-0T)GYqUwZYAhmXJi!RcnA^6Mi# zO&*(?Hf8D~S(eg`JDxiB{s(@6qf)2L%d%8%+>5{s$G$w>e9ay7hmktAF#*;UlL4LqZ># zfAv~xTajPxk}tmO<9~}SZ{VQxmA~5e!XIDzAtZG0EoqZx&bt4>`H%eS*9RK@a^mxg z-(M*xTyJ-5iAo<48TH%?^=}^e;MA8-rfl67z5bSihu&|SIqSiPG+O_Fkx>`Ev*jh+ zap#284I5kA?p@$GdhElGKKbQx{#|{G`#32U%a2|uP$!k9?%5)gzAEPgM1gM_3HlW2Q;_wY5LJB zUlqf}_>5NdSNm{9&*RaMJEyD0aCYtOoIiKFCQ*AEU)&bhPa73Dh7$sY_!q5J6>kmm z30}2{kK)H_*gpMqMYUt>zC|bd`|?F?e9@P_7yP)=N-ja?oF7#5vbN|?5s92$ov2OI z`l{_dgSiJ)59o?Y`VG{F=%%ZRR;z#ajBlta=4qAlvk@9!o-cYf(3u;sJUl{;r6&ml{) zIVLKysCW<;<3CV!t8OrV8$VIiC!(lEeXHtLRk$_|efb7wf-Xd_E!vjEiCSHB2tR}y zz%wa{s%dWlU~GBnjkul6$}Y4oPis(_+{ zw`bk!J6WesOC2y-d+(qmrzTB5h?_Jef$O8ys}ag+3M;B-{A2w}c4Rwzir!iMT4~h! zkBTQf{Yr6?CPKw#jL@g)!+AsT{)a5nR7sk^6q?mruWL(BM(B23bKVle1*){pwH2zx zydS621h_MbuIdZ5w$QYqCxd+F>B5T2os&4%1pi>y?AwY?jN$sLSZ97R&$vj@sZrBa zdc3MEaN_hkir%_i&8YCL+&I?RXS6EIcdou@U*aIY(JCE^w7TfYl25dLoF8ZReL#aM zqe_JS;o6}yo%i_$a~!YHg=sW8&QCL{=ugA-rRrXrXQi1=&obMkt!D|razB7BNjj$C zFTM7r)BQZOZ}X;8e}S%XdHhI^ALBXyO4+w)b*bL=L`D}WrjA-_Z=kd1r>dDu`z z`i52w_7xfje>WQMo{z4&EpJc8Xtug>^xz*G$B@>l_?>Ny@hyzm#OR+k-Rfsf_+6i` z5~KPJNs1n5PP;Vt>8TeNnP<+*dwS+muML@H`RJ=z}4{Hj`m> zKI*CL07zq)ggy-7<9(Q0bxgL3Q9~s*j8$;~DnDGR8LZB5gd4;SVh6J+EThpftdEWv z!!2WnGD|rXt7FvMUy*!hs-f~&ty<4A(SdiWqH)hN;W}SdKmr*q5f7mTm#k&kZ5-pr z;8)>lhGP#WGdyA59zwWECPN@e%-76j{EQH(RE2XY=2^%m>~^31 zjLAQdi-D|E?pC~1cpT%)#_3Som~@UBP|Z{^oHkgpCc}gNiflsjsci-eJh24-OH31Kx< z+M{Yj06U{}(Wn^ck1mTacdG8D*GQ*|*J@eKVAUp$B&cGwOdlqMXZ#^0P?Er-gsO-p mS3@$2E|Tfe4*{hGdTAR#dhhixOQ~lv3u+tuw58Y!_WuAG{HF#0 literal 0 HcmV?d00001 diff --git a/lab8/tools/sign.c b/lab8/tools/sign.c new file mode 100644 index 0000000..9d81bb6 --- /dev/null +++ b/lab8/tools/sign.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + struct stat st; + if (argc != 3) { + fprintf(stderr, "Usage: \n"); + return -1; + } + if (stat(argv[1], &st) != 0) { + fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno)); + return -1; + } + printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size); + if (st.st_size > 510) { + fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size); + return -1; + } + char buf[512]; + memset(buf, 0, sizeof(buf)); + FILE *ifp = fopen(argv[1], "rb"); + int size = fread(buf, 1, st.st_size, ifp); + if (size != st.st_size) { + fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size); + return -1; + } + fclose(ifp); + buf[510] = 0x55; + buf[511] = 0xAA; + FILE *ofp = fopen(argv[2], "wb+"); + size = fwrite(buf, 1, 512, ofp); + if (size != 512) { + fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size); + return -1; + } + fclose(ofp); + printf("build 512 bytes boot sector: '%s' success!\n", argv[2]); + return 0; +} + diff --git a/lab8/tools/user.ld b/lab8/tools/user.ld new file mode 100644 index 0000000..7b3c185 --- /dev/null +++ b/lab8/tools/user.ld @@ -0,0 +1,39 @@ +/* Simple linker script for ucore user-level programs. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS { + /* Load programs at this address: "." means the current address */ + . = 0x800020; + + .text : { + *(.text .stub .text.* .gnu.linkonce.t.*) + } + + PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + /* Adjust the address for the data segment to the next page */ + . = ALIGN(0x1000); + + .data : { + *(.data) + } + + PROVIDE(edata = .); + + .bss : { + *(.bss) + } + + PROVIDE(end = .); + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack .comment) + } +} diff --git a/lab8/tools/vector.c b/lab8/tools/vector.c new file mode 100644 index 0000000..e24d77e --- /dev/null +++ b/lab8/tools/vector.c @@ -0,0 +1,29 @@ +#include + +int +main(void) { + printf("# handler\n"); + printf(".text\n"); + printf(".globl __alltraps\n"); + + int i; + for (i = 0; i < 256; i ++) { + printf(".globl vector%d\n", i); + printf("vector%d:\n", i); + if ((i < 8 || i > 14) && i != 17) { + printf(" pushl $0\n"); + } + printf(" pushl $%d\n", i); + printf(" jmp __alltraps\n"); + } + printf("\n"); + printf("# vector table\n"); + printf(".data\n"); + printf(".globl __vectors\n"); + printf("__vectors:\n"); + for (i = 0; i < 256; i ++) { + printf(" .long vector%d\n", i); + } + return 0; +} + diff --git a/lab8/user/badarg.c b/lab8/user/badarg.c new file mode 100644 index 0000000..7b4ffad --- /dev/null +++ b/lab8/user/badarg.c @@ -0,0 +1,22 @@ +#include +#include + +int +main(void) { + int pid, exit_code; + if ((pid = fork()) == 0) { + cprintf("fork ok.\n"); + int i; + for (i = 0; i < 10; i ++) { + yield(); + } + exit(0xbeaf); + } + assert(pid > 0); + assert(waitpid(-1, NULL) != 0); + assert(waitpid(pid, (void *)0xC0000000) != 0); + assert(waitpid(pid, &exit_code) == 0 && exit_code == 0xbeaf); + cprintf("badarg pass.\n"); + return 0; +} + diff --git a/lab8/user/badsegment.c b/lab8/user/badsegment.c new file mode 100644 index 0000000..42f1446 --- /dev/null +++ b/lab8/user/badsegment.c @@ -0,0 +1,11 @@ +#include +#include + +/* try to load the kernel's TSS selector into the DS register */ + +int +main(void) { + // asm volatile("movw $0x28,%ax; movw %ax,%ds"); + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/divzero.c b/lab8/user/divzero.c new file mode 100644 index 0000000..16c23d5 --- /dev/null +++ b/lab8/user/divzero.c @@ -0,0 +1,11 @@ +#include +#include + +int zero; + +int +main(void) { + cprintf("value is %d.\n", 1 / zero); + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/exit.c b/lab8/user/exit.c new file mode 100644 index 0000000..c3ac5f8 --- /dev/null +++ b/lab8/user/exit.c @@ -0,0 +1,34 @@ +#include +#include + +int magic = -0x10384; + +int +main(void) { + int pid, code; + cprintf("I am the parent. Forking the child...\n"); + if ((pid = fork()) == 0) { + cprintf("I am the child.\n"); + yield(); + yield(); + yield(); + yield(); + yield(); + yield(); + yield(); + exit(magic); + } + else { + cprintf("I am parent, fork a child pid %d\n",pid); + } + assert(pid > 0); + cprintf("I am the parent, waiting now..\n"); + + assert(waitpid(pid, &code) == 0 && code == magic); + assert(waitpid(pid, &code) != 0 && wait() != 0); + cprintf("waitpid %d ok.\n", pid); + + cprintf("exit pass.\n"); + return 0; +} + diff --git a/lab8/user/faultread.c b/lab8/user/faultread.c new file mode 100644 index 0000000..6d292e1 --- /dev/null +++ b/lab8/user/faultread.c @@ -0,0 +1,9 @@ +#include +#include + +int +main(void) { + cprintf("I read %8x from 0.\n", *(unsigned int *)0); + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/faultreadkernel.c b/lab8/user/faultreadkernel.c new file mode 100644 index 0000000..53457f6 --- /dev/null +++ b/lab8/user/faultreadkernel.c @@ -0,0 +1,9 @@ +#include +#include + +int +main(void) { + cprintf("I read %08x from 0xfac00000!\n", *(unsigned *)0xfac00000); + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/forktest.c b/lab8/user/forktest.c new file mode 100644 index 0000000..3eda228 --- /dev/null +++ b/lab8/user/forktest.c @@ -0,0 +1,34 @@ +#include +#include + +const int max_child = 32; + +int +main(void) { + int n, pid; + for (n = 0; n < max_child; n ++) { + if ((pid = fork()) == 0) { + cprintf("I am child %d\n", n); + exit(0); + } + assert(pid > 0); + } + + if (n > max_child) { + panic("fork claimed to work %d times!\n", n); + } + + for (; n > 0; n --) { + if (wait() != 0) { + panic("wait stopped early\n"); + } + } + + if (wait() == 0) { + panic("wait got too many\n"); + } + + cprintf("forktest pass.\n"); + return 0; +} + diff --git a/lab8/user/forktree.c b/lab8/user/forktree.c new file mode 100644 index 0000000..aea2a9a --- /dev/null +++ b/lab8/user/forktree.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#define DEPTH 3 + +void forktree(const char *cur); + +void +forkchild(const char *cur, char branch) { + char nxt[DEPTH + 1]; + + if (strlen(cur) >= DEPTH) + return; + + snprintf(nxt, DEPTH + 1, "%s%c", cur, branch); + if (fork() == 0) { + forktree(nxt); + yield(); + exit(0); + } +} + +void +forktree(const char *cur) { + cprintf("%04x: I am '%s'\n", getpid(), cur); + + forkchild(cur, '0'); + forkchild(cur, '1'); +} + +int +main(void) { + forktree(""); + return 0; +} + diff --git a/lab8/user/hello.c b/lab8/user/hello.c new file mode 100644 index 0000000..0f05251 --- /dev/null +++ b/lab8/user/hello.c @@ -0,0 +1,11 @@ +#include +#include + +int +main(void) { + cprintf("Hello world!!.\n"); + cprintf("I am process %d.\n", getpid()); + cprintf("hello pass.\n"); + return 0; +} + diff --git a/lab8/user/libs/dir.c b/lab8/user/libs/dir.c new file mode 100644 index 0000000..62d926e --- /dev/null +++ b/lab8/user/libs/dir.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DIR dir, *dirp=&dir; +DIR * +opendir(const char *path) { + + if ((dirp->fd = open(path, O_RDONLY)) < 0) { + goto failed; + } + struct stat __stat, *stat = &__stat; + if (fstat(dirp->fd, stat) != 0 || !S_ISDIR(stat->st_mode)) { + goto failed; + } + dirp->dirent.offset = 0; + return dirp; + +failed: + return NULL; +} + +struct dirent * +readdir(DIR *dirp) { + if (sys_getdirentry(dirp->fd, &(dirp->dirent)) == 0) { + return &(dirp->dirent); + } + return NULL; +} + +void +closedir(DIR *dirp) { + close(dirp->fd); +} + +int +getcwd(char *buffer, size_t len) { + return sys_getcwd(buffer, len); +} + diff --git a/lab8/user/libs/dir.h b/lab8/user/libs/dir.h new file mode 100644 index 0000000..4862492 --- /dev/null +++ b/lab8/user/libs/dir.h @@ -0,0 +1,19 @@ +#ifndef __USER_LIBS_DIR_H__ +#define __USER_LIBS_DIR_H__ + +#include +#include + +typedef struct { + int fd; + struct dirent dirent; +} DIR; + +DIR *opendir(const char *path); +struct dirent *readdir(DIR *dirp); +void closedir(DIR *dirp); +int chdir(const char *path); +int getcwd(char *buffer, size_t len); + +#endif /* !__USER_LIBS_DIR_H__ */ + diff --git a/lab8/user/libs/file.c b/lab8/user/libs/file.c new file mode 100644 index 0000000..113e078 --- /dev/null +++ b/lab8/user/libs/file.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include + +int +open(const char *path, uint32_t open_flags) { + return sys_open(path, open_flags); +} + +int +close(int fd) { + return sys_close(fd); +} + +int +read(int fd, void *base, size_t len) { + return sys_read(fd, base, len); +} + +int +write(int fd, void *base, size_t len) { + return sys_write(fd, base, len); +} + +int +seek(int fd, off_t pos, int whence) { + return sys_seek(fd, pos, whence); +} + +int +fstat(int fd, struct stat *stat) { + return sys_fstat(fd, stat); +} + +int +fsync(int fd) { + return sys_fsync(fd); +} + +int +dup2(int fd1, int fd2) { + return sys_dup(fd1, fd2); +} + +static char +transmode(struct stat *stat) { + uint32_t mode = stat->st_mode; + if (S_ISREG(mode)) return 'r'; + if (S_ISDIR(mode)) return 'd'; + if (S_ISLNK(mode)) return 'l'; + if (S_ISCHR(mode)) return 'c'; + if (S_ISBLK(mode)) return 'b'; + return '-'; +} + +void +print_stat(const char *name, int fd, struct stat *stat) { + cprintf("[%03d] %s\n", fd, name); + cprintf(" mode : %c\n", transmode(stat)); + cprintf(" links : %lu\n", stat->st_nlinks); + cprintf(" blocks : %lu\n", stat->st_blocks); + cprintf(" size : %lu\n", stat->st_size); +} + diff --git a/lab8/user/libs/file.h b/lab8/user/libs/file.h new file mode 100644 index 0000000..fc46fb5 --- /dev/null +++ b/lab8/user/libs/file.h @@ -0,0 +1,23 @@ +#ifndef __USER_LIBS_FILE_H__ +#define __USER_LIBS_FILE_H__ + +#include + +struct stat; + +int open(const char *path, uint32_t open_flags); +int close(int fd); +int read(int fd, void *base, size_t len); +int write(int fd, void *base, size_t len); +int seek(int fd, off_t pos, int whence); +int fstat(int fd, struct stat *stat); +int fsync(int fd); +int dup(int fd); +int dup2(int fd1, int fd2); +int pipe(int *fd_store); +int mkfifo(const char *name, uint32_t open_flags); + +void print_stat(const char *name, int fd, struct stat *stat); + +#endif /* !__USER_LIBS_FILE_H__ */ + diff --git a/lab8/user/libs/initcode.S b/lab8/user/libs/initcode.S new file mode 100644 index 0000000..6d122c3 --- /dev/null +++ b/lab8/user/libs/initcode.S @@ -0,0 +1,13 @@ +.text +.globl _start +_start: + # set ebp for backtrace + // movl $0x0, %ebp + + # move down the esp register + # since it may cause page fault in backtrace + // subl $0x20, %esp + + # call user-program function + call umain +1: j 1b diff --git a/lab8/user/libs/lock.h b/lab8/user/libs/lock.h new file mode 100644 index 0000000..e221c12 --- /dev/null +++ b/lab8/user/libs/lock.h @@ -0,0 +1,42 @@ +#ifndef __USER_LIBS_LOCK_H__ +#define __USER_LIBS_LOCK_H__ + +#include +#include +#include + +#define INIT_LOCK {0} + +typedef volatile bool lock_t; + +static inline void +lock_init(lock_t *l) { + *l = 0; +} + +static inline bool +try_lock(lock_t *l) { + return test_and_set_bit(0, l); +} + +static inline void +lock(lock_t *l) { + if (try_lock(l)) { + int step = 0; + do { + yield(); + if (++ step == 100) { + step = 0; + sleep(10); + } + } while (try_lock(l)); + } +} + +static inline void +unlock(lock_t *l) { + test_and_clear_bit(0, l); +} + +#endif /* !__USER_LIBS_LOCK_H__ */ + diff --git a/lab8/user/libs/panic.c b/lab8/user/libs/panic.c new file mode 100644 index 0000000..783be16 --- /dev/null +++ b/lab8/user/libs/panic.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +void +__panic(const char *file, int line, const char *fmt, ...) { + // print the 'message' + va_list ap; + va_start(ap, fmt); + cprintf("user panic at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); + exit(-E_PANIC); +} + +void +__warn(const char *file, int line, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + cprintf("user warning at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); +} + diff --git a/lab8/user/libs/stdio.c b/lab8/user/libs/stdio.c new file mode 100644 index 0000000..56c5499 --- /dev/null +++ b/lab8/user/libs/stdio.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +/* * + * cputch - writes a single character @c to stdout, and it will + * increace the value of counter pointed by @cnt. + * */ +static void +cputch(int c, int *cnt) { + sys_putc(c); + (*cnt) ++; +} + +/* * + * vcprintf - format a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * + * Call this function if you are already dealing with a va_list. + * Or you probably want cprintf() instead. + * */ +int +vcprintf(const char *fmt, va_list ap) { + int cnt = 0; + vprintfmt((void*)cputch, NO_FD, &cnt, fmt, ap); + return cnt; +} + +/* * + * cprintf - formats a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * */ +int +cprintf(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + int cnt = vcprintf(fmt, ap); + va_end(ap); + + return cnt; +} + +/* * + * cputs- writes the string pointed by @str to stdout and + * appends a newline character. + * */ +int +cputs(const char *str) { + int cnt = 0; + char c; + while ((c = *str ++) != '\0') { + cputch(c, &cnt); + } + cputch('\n', &cnt); + return cnt; +} + + +static void +fputch(char c, int *cnt, int fd) { + write(fd, &c, sizeof(char)); + (*cnt) ++; +} + +int +vfprintf(int fd, const char *fmt, va_list ap) { + int cnt = 0; + vprintfmt((void*)fputch, fd, &cnt, fmt, ap); + return cnt; +} + +int +fprintf(int fd, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + int cnt = vfprintf(fd, fmt, ap); + va_end(ap); + + return cnt; +} diff --git a/lab8/user/libs/syscall.c b/lab8/user/libs/syscall.c new file mode 100644 index 0000000..110b4da --- /dev/null +++ b/lab8/user/libs/syscall.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include + + +#define MAX_ARGS 5 + +static inline int +syscall(uint64_t num, ...) { + va_list ap; + va_start(ap, num); + uint64_t a[MAX_ARGS]; + int i, ret; + for (i = 0; i < MAX_ARGS; i ++) { + a[i] = va_arg(ap, uint64_t); + } + va_end(ap); + + asm volatile ( + "ld a0, %1\n" + "ld a1, %2\n" + "ld a2, %3\n" + "ld a3, %4\n" + "ld a4, %5\n" + "ld a5, %6\n" + "ecall\n" + "sw a0, %0" + : "=m" (ret) + : "m" (num), + "m" (a[0]), + "m" (a[1]), + "m" (a[2]), + "m" (a[3]), + "m" (a[4]) + : "memory" + ); + return ret; +} + +int +sys_exit(int64_t error_code) { + return syscall(SYS_exit, error_code); +} + +int +sys_fork(void) { + return syscall(SYS_fork); +} + +int +sys_wait(int64_t pid, int64_t *store) { + return syscall(SYS_wait, pid, store); +} + +int +sys_yield(void) { + return syscall(SYS_yield); +} + +int +sys_kill(int64_t pid) { + return syscall(SYS_kill, pid); +} + +int +sys_getpid(void) { + return syscall(SYS_getpid); +} + +int +sys_putc(int64_t c) { + return syscall(SYS_putc, c); +} + +int +sys_pgdir(void) { + return syscall(SYS_pgdir); +} + +void +sys_lab6_set_priority(uint64_t priority) +{ + syscall(SYS_lab6_set_priority, priority); +} + +int +sys_sleep(int64_t time) { + return syscall(SYS_sleep, time); +} + +int +sys_gettime(void) { + return syscall(SYS_gettime); +} + +int +sys_exec(const char *name, int64_t argc, const char **argv) { + return syscall(SYS_exec, name, argc, argv); +} + +int +sys_open(const char *path, uint64_t open_flags) { + return syscall(SYS_open, path, open_flags); +} + +int +sys_close(int64_t fd) { + return syscall(SYS_close, fd); +} + +int +sys_read(int64_t fd, void *base, size_t len) { + return syscall(SYS_read, fd, base, len); +} + +int +sys_write(int64_t fd, void *base, size_t len) { + return syscall(SYS_write, fd, base, len); +} + +int +sys_seek(int64_t fd, off_t pos, int64_t whence) { + return syscall(SYS_seek, fd, pos, whence); +} + +int +sys_fstat(int64_t fd, struct stat *stat) { + return syscall(SYS_fstat, fd, stat); +} + +int +sys_fsync(int64_t fd) { + return syscall(SYS_fsync, fd); +} + +int +sys_getcwd(char *buffer, size_t len) { + return syscall(SYS_getcwd, buffer, len); +} + +int +sys_getdirentry(int64_t fd, struct dirent *dirent) { + return syscall(SYS_getdirentry, fd, dirent); +} + +int +sys_dup(int64_t fd1, int64_t fd2) { + return syscall(SYS_dup, fd1, fd2); +} diff --git a/lab8/user/libs/syscall.h b/lab8/user/libs/syscall.h new file mode 100644 index 0000000..cb8f12b --- /dev/null +++ b/lab8/user/libs/syscall.h @@ -0,0 +1,33 @@ +#ifndef __USER_LIBS_SYSCALL_H__ +#define __USER_LIBS_SYSCALL_H__ + +int sys_exit(int64_t error_code); +int sys_fork(void); +int sys_wait(int64_t pid, int64_t *store); +int sys_exec(const char *name, int64_t argc, const char **argv); +int sys_yield(void); +int sys_kill(int64_t pid); +int sys_getpid(void); +int sys_putc(int64_t c); +int sys_pgdir(void); +int sys_sleep(int64_t time); +int sys_gettime(void); + +struct stat; +struct dirent; + +int sys_open(const char *path, uint64_t open_flags); +int sys_close(int64_t fd); +int sys_read(int64_t fd, void *base, size_t len); +int sys_write(int64_t fd, void *base, size_t len); +int sys_seek(int64_t fd, off_t pos, int64_t whence); +int sys_fstat(int64_t fd, struct stat *stat); +int sys_fsync(int64_t fd); +int sys_getcwd(char *buffer, size_t len); +int sys_getdirentry(int64_t fd, struct dirent *dirent); +int sys_dup(int64_t fd1, int64_t fd2); +void sys_lab6_set_priority(uint64_t priority); //only for lab6 + + +#endif /* !__USER_LIBS_SYSCALL_H__ */ + diff --git a/lab8/user/libs/ulib.c b/lab8/user/libs/ulib.c new file mode 100644 index 0000000..4b25c73 --- /dev/null +++ b/lab8/user/libs/ulib.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +void +exit(int error_code) { + sys_exit(error_code); + cprintf("BUG: exit failed.\n"); + while (1); +} + +int +fork(void) { + return sys_fork(); +} + +int +wait(void) { + return sys_wait(0, NULL); +} + +int +waitpid(int pid, int *store) { + return sys_wait(pid, store); +} + +void +yield(void) { + sys_yield(); +} + +int +kill(int pid) { + return sys_kill(pid); +} + +int +getpid(void) { + return sys_getpid(); +} + +//print_pgdir - print the PDT&PT +void +print_pgdir(void) { + sys_pgdir(); +} + +unsigned int +gettime_msec(void) { + return (unsigned int)sys_gettime(); +} + +void +lab6_set_priority(uint32_t priority) +{ + sys_lab6_set_priority(priority); +} + +int +sleep(unsigned int time) { + return sys_sleep(time); +} +int +__exec(const char *name, const char **argv) { + int argc = 0; + while (argv[argc] != NULL) { + argc ++; + } + return sys_exec(name, argc, argv); +} diff --git a/lab8/user/libs/ulib.h b/lab8/user/libs/ulib.h new file mode 100644 index 0000000..97c8133 --- /dev/null +++ b/lab8/user/libs/ulib.h @@ -0,0 +1,40 @@ +#ifndef __USER_LIBS_ULIB_H__ +#define __USER_LIBS_ULIB_H__ + +#include + +void __warn(const char *file, int line, const char *fmt, ...); +void __noreturn __panic(const char *file, int line, const char *fmt, ...); + +#define warn(...) \ + __warn(__FILE__, __LINE__, __VA_ARGS__) + +#define panic(...) \ + __panic(__FILE__, __LINE__, __VA_ARGS__) + +#define assert(x) \ + do { \ + if (!(x)) { \ + panic("assertion failed: %s", #x); \ + } \ + } while (0) + +// static_assert(x) will generate a compile-time error if 'x' is false. +#define static_assert(x) \ + switch (x) { case 0: case (x): ; } + +void __noreturn exit(int error_code); +int fork(void); +int wait(void); +int waitpid(int pid, int *store); +void yield(void); +int kill(int pid); +int getpid(void); +void print_pgdir(void); +unsigned int gettime_msec(void); +void lab6_set_priority(uint32_t priority); +int sleep(unsigned int time); +int fprintf(int fd, const char *fmt, ...); +int __exec(const char *name, const char **argv); +#endif /* !__USER_LIBS_ULIB_H__ */ + diff --git a/lab8/user/libs/umain.c b/lab8/user/libs/umain.c new file mode 100644 index 0000000..a93908c --- /dev/null +++ b/lab8/user/libs/umain.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +int main(int argc, char *argv[]); + +static int +initfd(int fd2, const char *path, uint32_t open_flags) { + int fd1, ret; + if ((fd1 = open(path, open_flags)) < 0) { + return fd1; + } + if (fd1 != fd2) { + close(fd2); + ret = dup2(fd1, fd2); + close(fd1); + } + return ret; +} + +void +umain(long argc, char *argv[]) { + int fd; + if ((fd = initfd(0, "stdin:", O_RDONLY)) < 0) { + warn("open failed: %e.\n", fd); + } + if ((fd = initfd(1, "stdout:", O_WRONLY)) < 0) { + warn("open failed: %e.\n", fd); + } + int ret = main((int)argc, argv); + exit(ret); +} + diff --git a/lab8/user/matrix.c b/lab8/user/matrix.c new file mode 100644 index 0000000..c5ec869 --- /dev/null +++ b/lab8/user/matrix.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include + +#define MATSIZE 10 + +static int mata[MATSIZE][MATSIZE]; +static int matb[MATSIZE][MATSIZE]; +static int matc[MATSIZE][MATSIZE]; + +void +work(unsigned int times) { + int i, j, k, size = MATSIZE; + for (i = 0; i < size; i ++) { + for (j = 0; j < size; j ++) { + mata[i][j] = matb[i][j] = 1; + } + } + + yield(); + + cprintf("pid %d is running (%d times)!.\n", getpid(), times); + + while (times -- > 0) { + for (i = 0; i < size; i ++) { + for (j = 0; j < size; j ++) { + matc[i][j] = 0; + for (k = 0; k < size; k ++) { + matc[i][j] += mata[i][k] * matb[k][j]; + } + } + } + for (i = 0; i < size; i ++) { + for (j = 0; j < size; j ++) { + mata[i][j] = matb[i][j] = matc[i][j]; + } + } + } + cprintf("pid %d done!.\n", getpid()); + exit(0); +} + +const int total = 21; + +int +main(void) { + int pids[total]; + memset(pids, 0, sizeof(pids)); + + int i; + for (i = 0; i < total; i ++) { + if ((pids[i] = fork()) == 0) { + srand(i * i); + int times = (((unsigned int)rand()) % total); + times = (times * times + 10) * 100; + work(times); + } + if (pids[i] < 0) { + goto failed; + } + } + + cprintf("fork ok.\n"); + + for (i = 0; i < total; i ++) { + if (wait() != 0) { + cprintf("wait failed.\n"); + goto failed; + } + } + + cprintf("matrix pass.\n"); + return 0; + +failed: + for (i = 0; i < total; i ++) { + if (pids[i] > 0) { + kill(pids[i]); + } + } + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/pgdir.c b/lab8/user/pgdir.c new file mode 100644 index 0000000..09fd7e3 --- /dev/null +++ b/lab8/user/pgdir.c @@ -0,0 +1,11 @@ +#include +#include + +int +main(void) { + cprintf("I am %d, print pgdir.\n", getpid()); + print_pgdir(); + cprintf("pgdir pass.\n"); + return 0; +} + diff --git a/lab8/user/priority.c b/lab8/user/priority.c new file mode 100644 index 0000000..96373fa --- /dev/null +++ b/lab8/user/priority.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +#define TOTAL 5 +/* to get enough accuracy, MAX_TIME (the running time of each process) should >1000 mseconds. */ +#define MAX_TIME 10000 +unsigned int acc[TOTAL]; +int status[TOTAL]; +int pids[TOTAL]; + +static void +spin_delay(void) +{ + int i; + volatile int j; + for (i = 0; i != 200; ++ i) + { + j = !j; + } +} + +int +main(void) { + int i,time; + memset(pids, 0, sizeof(pids)); + lab6_set_priority(TOTAL + 1); + + for (i = 0; i < TOTAL; i ++) { + acc[i]=0; + if ((pids[i] = fork()) == 0) { + lab6_set_priority(i + 1); + acc[i] = 0; + while (1) { + spin_delay(); + ++ acc[i]; + if(acc[i]%4000==0) { + if((time=gettime_msec())>MAX_TIME) { + cprintf("child pid %d, acc %d, time %d\n",getpid(),acc[i],time); + exit(acc[i]); + } + } + } + + } + if (pids[i] < 0) { + goto failed; + } + } + + cprintf("main: fork ok,now need to wait pids.\n"); + + for (i = 0; i < TOTAL; i ++) { + status[i]=0; + waitpid(pids[i],&status[i]); + cprintf("main: pid %d, acc %d, time %d\n",pids[i],status[i],gettime_msec()); + } + cprintf("main: wait pids over\n"); + cprintf("stride sched correct result:"); + for (i = 0; i < TOTAL; i ++) + { + cprintf(" %d", (status[i] * 2 / status[0] + 1) / 2); + } + cprintf("\n"); + + return 0; + +failed: + for (i = 0; i < TOTAL; i ++) { + if (pids[i] > 0) { + kill(pids[i]); + } + } + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/sh.c b/lab8/user/sh.c new file mode 100644 index 0000000..d63d71c --- /dev/null +++ b/lab8/user/sh.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include + +#define printf(...) fprintf(1, __VA_ARGS__) +#define putc(c) printf("%c", c) + +#define BUFSIZE 4096 +#define WHITESPACE " \t\r\n" +#define SYMBOLS "<|>&;" + +char shcwd[BUFSIZE]; + +int +gettoken(char **p1, char **p2) { + char *s; + if ((s = *p1) == NULL) { + return 0; + } + while (strchr(WHITESPACE, *s) != NULL) { + *s ++ = '\0'; + } + if (*s == '\0') { + return 0; + } + + *p2 = s; + int token = 'w'; + if (strchr(SYMBOLS, *s) != NULL) { + token = *s, *s ++ = '\0'; + } + else { + bool flag = 0; + while (*s != '\0' && (flag || strchr(WHITESPACE SYMBOLS, *s) == NULL)) { + if (*s == '"') { + *s = ' ', flag = !flag; + } + s ++; + } + } + *p1 = (*s != '\0' ? s : NULL); + return token; +} + +char * +readline(const char *prompt) { + static char buffer[BUFSIZE]; + if (prompt != NULL) { + printf("%s", prompt); + } + int ret, i = 0; + while (1) { + char c; + if ((ret = read(0, &c, sizeof(char))) < 0) { + return NULL; + } + else if (ret == 0) { + if (i > 0) { + buffer[i] = '\0'; + break; + } + return NULL; + } + + if (c == 3) { + return NULL; + } + else if (c >= ' ' && i < BUFSIZE - 1) { + putc(c); + buffer[i ++] = c; + } + else if (c == '\b' && i > 0) { + putc(c); + i --; + } + else if (c == '\n' || c == '\r') { + putc(c); + buffer[i] = '\0'; + break; + } + } + return buffer; +} + +void +usage(void) { + printf("usage: sh [command-file]\n"); +} + +int +reopen(int fd2, const char *filename, uint32_t open_flags) { + int ret, fd1; + close(fd2); + if ((ret = open(filename, open_flags)) >= 0 && ret != fd2) { + close(fd2); + fd1 = ret, ret = dup2(fd1, fd2); + close(fd1); + } + return ret < 0 ? ret : 0; +} + +int +testfile(const char *name) { + int ret; + if ((ret = open(name, O_RDONLY)) < 0) { + return ret; + } + close(ret); + return 0; +} + +int +runcmd(char *cmd) { + static char argv0[BUFSIZE]; + static const char *argv[EXEC_MAX_ARG_NUM + 1];//must be static! + char *t; + int argc, token, ret, p[2]; +again: + argc = 0; + while (1) { + switch (token = gettoken(&cmd, &t)) { + case 'w': + if (argc == EXEC_MAX_ARG_NUM) { + printf("sh error: too many arguments\n"); + return -1; + } + argv[argc ++] = t; + break; + case '<': + if (gettoken(&cmd, &t) != 'w') { + printf("sh error: syntax error: < not followed by word\n"); + return -1; + } + if ((ret = reopen(0, t, O_RDONLY)) != 0) { + return ret; + } + break; + case '>': + if (gettoken(&cmd, &t) != 'w') { + printf("sh error: syntax error: > not followed by word\n"); + return -1; + } + if ((ret = reopen(1, t, O_RDWR | O_TRUNC | O_CREAT)) != 0) { + return ret; + } + break; + case '|': + // if ((ret = pipe(p)) != 0) { + // return ret; + // } + if ((ret = fork()) == 0) { + close(0); + if ((ret = dup2(p[0], 0)) < 0) { + return ret; + } + close(p[0]), close(p[1]); + goto again; + } + else { + if (ret < 0) { + return ret; + } + close(1); + if ((ret = dup2(p[1], 1)) < 0) { + return ret; + } + close(p[0]), close(p[1]); + goto runit; + } + break; + case 0: + goto runit; + case ';': + if ((ret = fork()) == 0) { + goto runit; + } + else { + if (ret < 0) { + return ret; + } + waitpid(ret, NULL); + goto again; + } + break; + default: + printf("sh error: bad return %d from gettoken\n", token); + return -1; + } + } + +runit: + if (argc == 0) { + return 0; + } + else if (strcmp(argv[0], "cd") == 0) { + if (argc != 2) { + return -1; + } + strcpy(shcwd, argv[1]); + return 0; + } + if ((ret = testfile(argv[0])) != 0) { + if (ret != -E_NOENT) { + return ret; + } + snprintf(argv0, sizeof(argv0), "/%s", argv[0]); + argv[0] = argv0; + } + argv[argc] = NULL; + return __exec(argv[0], argv); +} + +int +main(int argc, char **argv) { + cputs("user sh is running!!!"); + int ret, interactive = 1; + if (argc == 2) { + if ((ret = reopen(0, argv[1], O_RDONLY)) != 0) { + return ret; + } + interactive = 0; + } + else if (argc > 2) { + usage(); + return -1; + } + //shcwd = malloc(BUFSIZE); + assert(shcwd != NULL); + + char *buffer; + while ((buffer = readline((interactive) ? "$ " : NULL)) != NULL) { + shcwd[0] = '\0'; + int pid; + if ((pid = fork()) == 0) { + ret = runcmd(buffer); + exit(ret); + } + assert(pid >= 0); + if (waitpid(pid, &ret) == 0) { + if (ret == 0 && shcwd[0] != '\0') { + ret = 0; + } + if (ret != 0) { + printf("error: %d - %e\n", ret, ret); + } + } + } + return 0; +} + diff --git a/lab8/user/sleep.c b/lab8/user/sleep.c new file mode 100644 index 0000000..5bc56e0 --- /dev/null +++ b/lab8/user/sleep.c @@ -0,0 +1,28 @@ +#include +#include + +void +sleepy(int pid) { + int i, time = 100; + for (i = 0; i < 10; i ++) { + sleep(time); + cprintf("sleep %d x %d slices.\n", i + 1, time); + } + exit(0); +} + +int +main(void) { + unsigned int time = gettime_msec(); + int pid1, exit_code; + + if ((pid1 = fork()) == 0) { + sleepy(pid1); + } + + assert(waitpid(pid1, &exit_code) == 0 && exit_code == 0); + cprintf("use %04d msecs.\n", gettime_msec() - time); + cprintf("sleep pass.\n"); + return 0; +} + diff --git a/lab8/user/sleepkill.c b/lab8/user/sleepkill.c new file mode 100644 index 0000000..01268a2 --- /dev/null +++ b/lab8/user/sleepkill.c @@ -0,0 +1,18 @@ +#include +#include + +int +main(void) { + int pid; + if ((pid = fork()) == 0) { + sleep(~0); + exit(0xdead); + } + assert(pid > 0); + + sleep(100); + assert(kill(pid) == 0); + cprintf("sleepkill pass.\n"); + return 0; +} + diff --git a/lab8/user/softint.c b/lab8/user/softint.c new file mode 100644 index 0000000..bdc9f27 --- /dev/null +++ b/lab8/user/softint.c @@ -0,0 +1,10 @@ +#include +#include + +int +main(void) { + // asm volatile("int $14"); + exit(0); + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/spin.c b/lab8/user/spin.c new file mode 100644 index 0000000..91581e5 --- /dev/null +++ b/lab8/user/spin.c @@ -0,0 +1,29 @@ +#include +#include + +int +main(void) { + int pid, ret; + cprintf("I am the parent. Forking the child...\n"); + if ((pid = fork()) == 0) { + cprintf("I am the child. spinning ...\n"); + while (1); + } + cprintf("I am the parent. Running the child...\n"); + + yield(); + yield(); + yield(); + + cprintf("I am the parent. Killing the child...\n"); + + assert((ret = kill(pid)) == 0); + cprintf("kill returns %d\n", ret); + + assert((ret = waitpid(pid, NULL)) == 0); + cprintf("wait returns %d\n", ret); + + cprintf("spin may pass.\n"); + return 0; +} + diff --git a/lab8/user/testbss.c b/lab8/user/testbss.c new file mode 100644 index 0000000..211d7cd --- /dev/null +++ b/lab8/user/testbss.c @@ -0,0 +1,33 @@ +#include +#include + +#define ARRAYSIZE (1024*1024) + +uint32_t bigarray[ARRAYSIZE]; + +int +main(void) { + cprintf("Making sure bss works right...\n"); + int i; + for (i = 0; i < ARRAYSIZE; i ++) { + if (bigarray[i] != 0) { + panic("bigarray[%d] isn't cleared!\n", i); + } + } + for (i = 0; i < ARRAYSIZE; i ++) { + bigarray[i] = i; + } + for (i = 0; i < ARRAYSIZE; i ++) { + if (bigarray[i] != i) { + panic("bigarray[%d] didn't hold its value!\n", i); + } + } + + cprintf("Yes, good. Now doing a wild write off the end...\n"); + cprintf("testbss may pass.\n"); + + bigarray[ARRAYSIZE + 1024] = 0; + // asm volatile ("int $0x14"); + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/waitkill.c b/lab8/user/waitkill.c new file mode 100644 index 0000000..9bb3f80 --- /dev/null +++ b/lab8/user/waitkill.c @@ -0,0 +1,59 @@ +#include +#include + +void +do_yield(void) { + yield(); + yield(); + yield(); + yield(); + yield(); + yield(); +} + +int parent, pid1, pid2; + +void +loop(void) { + cprintf("child 1.\n"); + while (1); +} + +void +work(void) { + cprintf("child 2.\n"); + do_yield(); + if (kill(parent) == 0) { + cprintf("kill parent ok.\n"); + do_yield(); + if (kill(pid1) == 0) { + cprintf("kill child1 ok.\n"); + exit(0); + } + } + exit(-1); +} + +int +main(void) { + parent = getpid(); + if ((pid1 = fork()) == 0) { + loop(); + } + + assert(pid1 > 0); + + if ((pid2 = fork()) == 0) { + work(); + } + if (pid2 > 0) { + cprintf("wait child 1.\n"); + waitpid(pid1, NULL); + panic("waitpid %d returns\n", pid1); + } + else { + kill(pid1); + } + panic("FAIL: T.T\n"); +} + diff --git a/lab8/user/yield.c b/lab8/user/yield.c new file mode 100644 index 0000000..a19890d --- /dev/null +++ b/lab8/user/yield.c @@ -0,0 +1,16 @@ +#include +#include + +int +main(void) { + int i; + cprintf("Hello, I am process %d.\n", getpid()); + for (i = 0; i < 5; i ++) { + yield(); + cprintf("Back in process %d, iteration %d.\n", getpid(), i); + } + cprintf("All done in process %d.\n", getpid()); + cprintf("yield pass.\n"); + return 0; +} + diff --git a/lab8_practice/.gdb.in b/lab8_practice/.gdb.in new file mode 100644 index 0000000..6188bef --- /dev/null +++ b/lab8_practice/.gdb.in @@ -0,0 +1,3 @@ +target remote localhost:1234 +break *0x00000000802000ea +continue diff --git a/lab8_practice/Makefile b/lab8_practice/Makefile new file mode 100644 index 0000000..6475b65 --- /dev/null +++ b/lab8_practice/Makefile @@ -0,0 +1,335 @@ +PROJ := lab8 +EMPTY := +SPACE := $(EMPTY) $(EMPTY) +SLASH := / + +V := @ + +ifndef GCCPREFIX +GCCPREFIX := riscv64-unknown-elf- +endif + +ifndef QEMU +QEMU := qemu-system-riscv64 +endif + +ifndef SPIKE +SPIKE := spike +endif + +# eliminate default suffix rules +.SUFFIXES: .c .S .h + +# delete target files if there is an error (or make is interrupted) +.DELETE_ON_ERROR: + +# define compiler and flags +HOSTCC := gcc +HOSTCFLAGS := -Wall -O2 + +GDB := $(GCCPREFIX)gdb + +CC := $(GCCPREFIX)gcc +CFLAGS := -mcmodel=medany -O2 -std=gnu99 -Wno-unused +CFLAGS += -fno-builtin -Wall -nostdinc $(DEFS) +CFLAGS += -fno-stack-protector -ffunction-sections -fdata-sections +CTYPE := c S + +LD := $(GCCPREFIX)ld +LDFLAGS := -m elf64lriscv +LDFLAGS += -nostdlib --gc-sections + +OBJCOPY := $(GCCPREFIX)objcopy +OBJDUMP := $(GCCPREFIX)objdump + +COPY := cp +MKDIR := mkdir -p +MV := mv +RM := rm -f +AWK := awk +SED := sed +SH := sh +TR := tr +TOUCH := touch -c + +OBJDIR := obj +BINDIR := bin + +ALLOBJS := +ALLDEPS := +TARGETS := + +USER_PREFIX := __user_ + +include tools/function.mk + +listf_cc = $(call listf,$(1),$(CTYPE)) + +# for cc +add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4)) +create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS)) + +# for hostcc +add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3)) +create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS)) + +cgtype = $(patsubst %.$(2),%.$(3),$(1)) +objfile = $(call toobj,$(1)) +asmfile = $(call cgtype,$(call toobj,$(1)),o,asm) +outfile = $(call cgtype,$(call toobj,$(1)),o,out) +symfile = $(call cgtype,$(call toobj,$(1)),o,sym) +filename = $(basename $(notdir $(1))) +ubinfile = $(call outfile,$(addprefix $(USER_PREFIX),$(call filename,$(1)))) + +# for match pattern +match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?) + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# include kernel/user + +INCLUDE += libs/ + +CFLAGS += $(addprefix -I,$(INCLUDE)) + +LIBDIR += libs + +$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) + +# ------------------------------------------------------------------- +# user programs + +UINCLUDE += user/include/ \ + user/libs/ + +USRCDIR += user + +ULIBDIR += user/libs + +UCFLAGS += $(addprefix -I,$(UINCLUDE)) +USER_BINS := + +$(call add_files_cc,$(call listf_cc,$(ULIBDIR)),ulibs,$(UCFLAGS)) +$(call add_files_cc,$(call listf_cc,$(USRCDIR)),uprog,$(UCFLAGS)) + +UOBJS := $(call read_packet,ulibs libs) + +define uprog_ld +__user_bin__ := $$(call ubinfile,$(1)) +USER_BINS += $$(__user_bin__) +$$(__user_bin__): tools/user.ld +$$(__user_bin__): $$(UOBJS) +$$(__user_bin__): $(1) | $$$$(dir $$$$@) + $(V)$(LD) $(LDFLAGS) -T tools/user.ld -o $$@ $$(UOBJS) $(1) + @$(OBJDUMP) -S $$@ > $$(call cgtype,$$<,o,asm) + @$(OBJDUMP) -t $$@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$$$/d' > $$(call cgtype,$$<,o,sym) +endef + +$(foreach p,$(call read_packet,uprog),$(eval $(call uprog_ld,$(p)))) + + +# ------------------------------------------------------------------- +# create 'mksfs' tools +$(call add_files_host,tools/mksfs.c,mksfs,mksfs) +$(call create_target_host,mksfs,mksfs) + +# ------------------------------------------------------------------- +# create swap.img +SWAPIMG := $(call totarget,swap.img) + +$(SWAPIMG): + $(V)dd if=/dev/zero of=$@ bs=4kB count=8 + +$(call create_target,swap.img) + +# ------------------------------------------------------------------- +# create sfs.img +SFSIMG := $(call totarget,sfs.img) +SFSBINS := +SFSROOT := disk0 + +define fscopy +__fs_bin__ := $(2)$(SLASH)$(patsubst $(USER_PREFIX)%,%,$(basename $(notdir $(1)))) +SFSBINS += $$(__fs_bin__) +$$(__fs_bin__): $(1) | $$$$(dir $@) + @$(COPY) $$< $$@ +endef + +$(foreach p,$(USER_BINS),$(eval $(call fscopy,$(p),$(SFSROOT)$(SLASH)))) + +$(SFSROOT): + $(V)$(MKDIR) $@ + +$(SFSIMG): $(SFSROOT) $(SFSBINS) | $(call totarget,mksfs) + $(V)dd if=/dev/zero of=$@ bs=1kB count=480 + @$(call totarget,mksfs) $@ $(SFSROOT) + +$(call create_target,sfs.img) + +# ------------------------------------------------------------------- +# kernel + +KINCLUDE += kern/debug/ \ + kern/driver/ \ + kern/trap/ \ + kern/mm/ \ + kern/libs/ \ + kern/sync/ \ + kern/fs/ \ + kern/process/ \ + kern/schedule/ \ + kern/syscall/ \ + kern/fs/swap/ \ + kern/fs/vfs/ \ + kern/fs/devs/ \ + kern/fs/sfs/ + + +KSRCDIR += kern/init \ + kern/libs \ + kern/debug \ + kern/driver \ + kern/trap \ + kern/mm \ + kern/sync \ + kern/fs \ + kern/process \ + kern/schedule \ + kern/syscall \ + kern/fs/swap \ + kern/fs/vfs \ + kern/fs/devs \ + kern/fs/sfs + +KCFLAGS += $(addprefix -I,$(KINCLUDE)) + +$(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS)) + +KOBJS = $(call read_packet,kernel libs) + +# create kernel target +kernel = $(call totarget,kernel) + +$(kernel): tools/kernel.ld + +$(kernel): $(KOBJS) $(SWAPIMG) $(SFSIMG) + @echo + ld $@ + $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) --format=binary $(SWAPIMG) $(SFSIMG) --format=default + @$(OBJDUMP) -S $@ > $(call asmfile,kernel) + @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel) + +$(call create_target,kernel) + +# ------------------------------------------------------------------- +# create ucore.img +UCOREIMG := $(call totarget,ucore.img) + +#$(UCOREIMG): $(kernel) +# cd ../../riscv-pk && rm -rf build && mkdir build && cd build && ../configure --prefix=$(RISCV) --host=riscv32-unknown-linux-gnu --with-payload=../../labcodes/$(PROJ)/$(kernel) && make && cp bbl ../../labcodes/$(PROJ)/$(UCOREIMG) + +$(UCOREIMG): $(kernel) + $(OBJCOPY) $(kernel) --strip-all -O binary $@ + +$(call create_target,ucore.img) + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +$(call finish_all) + +IGNORE_ALLDEPS = clean \ + dist-clean \ + grade \ + touch \ + print-.+ \ + run-.+ \ + build-.+ \ + sh-.+ \ + script-.+ \ + handin + +ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0) +-include $(ALLDEPS) +endif + +# files for grade script + +TARGETS: $(TARGETS) + +.DEFAULT_GOAL := TARGETS + +QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback -drive file=$(SFSIMG),media=disk,cache=writeback + +.PHONY: qemu spike + +qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG) +# $(V)$(QEMU) -kernel $(UCOREIMG) -nographic + $(V)$(QEMU) \ + -machine virt \ + -nographic \ + -bios default \ + -device loader,file=$(UCOREIMG),addr=0x80200000 + + +spike: $(UCOREIMG) $(SWAPIMG) $(SFSIMG) + $(V)$(SPIKE) $(UCOREIMG) + +TERMINAL := gnome-terminal + +RUN_PREFIX := _binary_$(OBJDIR)_$(USER_PREFIX) +MAKEOPTS := --quiet --no-print-directory + +run-%: build-% + $(V)$(SPIKE) $(UCOREIMG) + +sh-%: script-% + $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null + +run-nox-%: build-% + $(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic + +build-%: touch + $(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=$*" + +script-%: touch + $(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=sh -DTESTSCRIPT=/script/$*" + +.PHONY: grade touch buildfs + +GRADE_GDB_IN := .gdb.in +GRADE_QEMU_OUT := .qemu.out +HANDIN := proj$(PROJ)-handin.tar.gz + +TOUCH_FILES := kern/process/proc.c + +MAKEOPTS := --quiet --no-print-directory + +grade: + $(V)$(MAKE) $(MAKEOPTS) clean + $(V)$(SH) tools/grade.sh + +touch: + $(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f)) + +print-%: + @echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z])) + +.PHONY: clean dist-clean handin packall tags +clean: + $(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) $(SFSBINS) cscope* tags + $(V)$(RM) -r $(OBJDIR) $(BINDIR) $(SFSROOT) + +dist-clean: clean + -$(RM) $(HANDIN) + +handin: packall + @echo Please visit http://learn.tsinghua.edu.cn and upload $(HANDIN). Thanks! + +packall: clean + @$(RM) -f $(HANDIN) + @tar -czf $(HANDIN) `find . -type f -o -type d | grep -v '^\.*$$' | grep -vF '$(HANDIN)'` + +tags: + @echo TAGS ALL + $(V)rm -f cscope.files cscope.in.out cscope.out cscope.po.out tags + $(V)find . -type f -name "*.[chS]" >cscope.files + $(V)cscope -bq + $(V)ctags -L cscope.files diff --git a/lab8_practice/disk0/badarg b/lab8_practice/disk0/badarg new file mode 100644 index 0000000000000000000000000000000000000000..1d3aaa6cedaf89ca252e73f7c9d071481eaf6415 GIT binary patch literal 9368 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 literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/badsegment b/lab8_practice/disk0/badsegment new file mode 100644 index 0000000000000000000000000000000000000000..1f918bce4332eb1d1ac2294580f5cfb270746164 GIT binary patch literal 8800 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 NHbRy1yfj^J|1W6}T$lg= literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/exit b/lab8_practice/disk0/exit new file mode 100644 index 0000000000000000000000000000000000000000..baa7a4772b9bc35a0bea57fd7de5b8ca2912d737 GIT binary patch literal 10216 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 VPFOoJk;fwj!aSXT;psyAe*yHAHdg=u literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/faultread b/lab8_practice/disk0/faultread new file mode 100644 index 0000000000000000000000000000000000000000..7d87fd6a730b0c2f93c1177e2c1ca7ac6559d9fc GIT binary patch literal 8648 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 literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/faultreadkernel b/lab8_practice/disk0/faultreadkernel new file mode 100644 index 0000000000000000000000000000000000000000..ca43c871522096176f6422a15942a76c4141a90b GIT binary patch literal 8856 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 I;bQ&&0JA{zzW@LL literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/hello b/lab8_practice/disk0/hello new file mode 100644 index 0000000000000000000000000000000000000000..1016d8b442cbacd0a0d48f56a8b4db41e49bc4ce GIT binary patch literal 8808 zcmeHNe@q+K9e+M^Ho;+qIDrJ3^gxA%sGK`3z3{3;Rrp zRup4wXqrar#yU!}X&&lW>a|hTEcMznA&ErVMpKtmT~}3WN5M&HM2D-9hDJEv_uYLj z(3DKufBnNJ-TS`xd7tl(_kH)icgXCq{GguEXoz|-FV!OSu&qGe621$4g_EG#42Awesw_={Lyp$_&(H`4IAMOh`=2)v0;) z8^G)MJMqY=UMoxX{%05{vQ2hGI`+gwVFsM*kh&bz-m2nf5*Uw*-TU7 z$khUE>0J5j*5sqpy7|VbSorYXs=dYGE9ssM!j}cC`Dx)tLDIeH(Y~{h#z^Rw-E*O$ zfsb;SK|QRf|1zhd*p)x?QlO`-RJQ&9V4@6 zlkZM{koai)C-bdy?&-ey;koyw?6W(QvAvahUk$&u-?T6F*kBG=emci$Tt3cf$8yPl zzJW1@*An5D%cNq=nA5=IM=MTk?Hh@(S)n1VWi~yz#*)_7XAxsIVKiHN9!c%!etdv! zV9PAU`VUgZ-B)fI$X!W$IB;MA(|OZn&>;Uxjt- zQ12ZE@@ajEq4@7t>R$iZ1O1=X3TG~^94OpO2H{y6=(Ag28~n8(JfmMx)2|@){&Lu} zN1-0nuG?~4yV(FM6>{}O#;igjG)4=Jx^s+B@b(J&G!?UYlKYM@ww$CM6ndZ3(x`-G zIR%={Lg#dWkxgntLu{7TI-Bl(h;qBWo@|I-=zfUuj4WY7LzCIL6WX zFguUttpxIx8wDNNhj~0Sq}T}RLqjEo-s*j6roGOpS%0Y?qfe&aJAaJkII`Xyt~g42 z>{fSJb5zl50N>k>E%MWookRUpfFIZB0*A&b*Ol-(z0W{c4h5rJd-9R=Tr(^mwZH$M8I*uz5 zpJQld8uuO11#2s;O#9ezp(&~{>d!HgtERqEB6@$Dkqt6NeUSOx(fgquY8X?ps9!f- zGi#o!pD%(qN6$>Ol)y7zmyh+hkyhtDFTtt(gZd5;q4G5UQJF8>Hk15qY`8^Zp_N#9 z>jnIxkMr_whrsa?SLQdB*-SYcT*@?&=Xdiyx5M4G6vMpTYU0Ev91>^e9Zu0!2{nLT ze#z!=n+WlEMK|}LJgPy8i7OLTnr#0rr=!(>SHQ(P+?9f&PW3$j$+VykqZpezre{4>gPD92IGp;UGAaumJwC~Hxw^Qvq>yanYGD4#|73d)lx&!L<` zNeqggJe2EEHlVzFW7R#I?ycT@-~D{6U=!`C-+ycRB#$eY{JQM3o1RBj>SmF45)jbpa2@s46PTfcwQZm*|#w$>DLs^B-_Z z)ars=(P`)GaBviq5H!F!{M;^w6AlzkC`Xi@v4D?~-5$;#5ZXDL*y#`eP$cK}NF3h@ zEWTB-so-}}7u05Jz)z!H(3dAXKF;RwiGrl8h0X()Uku8I7XgCLv`SKfQmXem;1UCZCfZta9{qR0YhN~FvrRBi;dWDf{$vtN@KaVf@+1??wDgv>(E)IE{EE#t*MN`T7L$f1n)( z@%@NT-+=!D@t4pJmI?Aa;vD)dM>|o(V`v9oyW~Z{^B{ipcV(oLSCIb#@^v`zI^u8M zU}pgF9rzqpBR`CI4dUw%KZSTU#;?YC7I6;ko00!F#LpmJfcSaD4Wgg=#c5VesJ$+Kv*7gjMbfG25L>`orC6p0-A<@~lqMHK z(C2-UJf;!I9pIBrK~X2o&GfTO#_9K$+-Vafufs-2rN7H1@vR^wpCa4Uo+LgYkxHM( z#!Ea%TG1+b36|3ukQQEBn8p8J&^khElDco|zN`PY;8Q2i^!0tgaS7_HxLOCF#|LCR zi$oOa`BYI_m*8l6)v+p1(!k)lR}t4+8tdYVj#b}_@j29g7SBBmV+Gu*|J6vQbr&g! hDB`Kx6nWtrsFJJ2P(BQn(sywR>w(2&=-(Lse*i|&NvHq- literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/matrix b/lab8_practice/disk0/matrix new file mode 100644 index 0000000000000000000000000000000000000000..a94029c9a2ed55ceec8fb7a01f09a2d2d2fe6887 GIT binary patch literal 10824 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 literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/pgdir b/lab8_practice/disk0/pgdir new file mode 100644 index 0000000000000000000000000000000000000000..d40ac5f74218c9dc47a426c4388c5bbb46281716 GIT binary patch literal 8856 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 IVKz|zpD|9Ns{jB1 literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/sh b/lab8_practice/disk0/sh new file mode 100644 index 0000000000000000000000000000000000000000..3d3012633ba0d62cbf55adbbf1f4a7b36e999cbf GIT binary patch literal 12104 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 literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/sleep b/lab8_practice/disk0/sleep new file mode 100644 index 0000000000000000000000000000000000000000..6b2950a604dd25c428b754821f0876c2a267ef07 GIT binary patch literal 9376 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 Xo1!$}ETsQ@uB?vkRm6>czW#p!t&sq? literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/sleepkill b/lab8_practice/disk0/sleepkill new file mode 100644 index 0000000000000000000000000000000000000000..b487299cfe6f1ff2dd07c751049984e31be18329 GIT binary patch literal 9160 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 BD*6Bb literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/spin b/lab8_practice/disk0/spin new file mode 100644 index 0000000000000000000000000000000000000000..6cd4ad900f786b4377c5779598ea807d1d6fac02 GIT binary patch literal 9512 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 literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/testbss b/lab8_practice/disk0/testbss new file mode 100644 index 0000000000000000000000000000000000000000..2d3b9ccc024d7511b61a0f3fc25f95bdf902b762 GIT binary patch literal 9264 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 H7VG~D7n<8r literal 0 HcmV?d00001 diff --git a/lab8_practice/disk0/waitkill b/lab8_practice/disk0/waitkill new file mode 100644 index 0000000000000000000000000000000000000000..cbb640e9df507d51c0fa23efc7b114ab863c3135 GIT binary patch literal 9848 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 U=q#mw2M!FB`xSAkUhMyW0OSR)0RR91 literal 0 HcmV?d00001 diff --git a/lab8_practice/giveitatry.pyq b/lab8_practice/giveitatry.pyq new file mode 100644 index 0000000..e69de29 diff --git a/lab8_practice/kern/debug/assert.h b/lab8_practice/kern/debug/assert.h new file mode 100644 index 0000000..ac1a966 --- /dev/null +++ b/lab8_practice/kern/debug/assert.h @@ -0,0 +1,27 @@ +#ifndef __KERN_DEBUG_ASSERT_H__ +#define __KERN_DEBUG_ASSERT_H__ + +#include + +void __warn(const char *file, int line, const char *fmt, ...); +void __noreturn __panic(const char *file, int line, const char *fmt, ...); + +#define warn(...) \ + __warn(__FILE__, __LINE__, __VA_ARGS__) + +#define panic(...) \ + __panic(__FILE__, __LINE__, __VA_ARGS__) + +#define assert(x) \ + do { \ + if (!(x)) { \ + panic("assertion failed: %s", #x); \ + } \ + } while (0) + +// static_assert(x) will generate a compile-time error if 'x' is false. +#define static_assert(x) \ + switch (x) { case 0: case (x): ; } + +#endif /* !__KERN_DEBUG_ASSERT_H__ */ + diff --git a/lab8_practice/kern/debug/kdebug.c b/lab8_practice/kern/debug/kdebug.c new file mode 100644 index 0000000..672c47b --- /dev/null +++ b/lab8_practice/kern/debug/kdebug.c @@ -0,0 +1,92 @@ +#include +#include +#include + +/* * + * print_kerninfo - print the information about kernel, including the location + * of kernel entry, the start addresses of data and text segements, the start + * address of free memory and how many memory that kernel has used. + * */ +void print_kerninfo(void) { + extern char etext[], edata[], end[], kern_init[]; + cprintf("Special kernel symbols:\n"); + cprintf(" entry 0x%08x (virtual)\n", kern_init); + cprintf(" etext 0x%08x (virtual)\n", etext); + cprintf(" edata 0x%08x (virtual)\n", edata); + cprintf(" end 0x%08x (virtual)\n", end); + cprintf("Kernel executable memory footprint: %dKB\n", + (end - kern_init + 1023) / 1024); +} + +/* * + * print_debuginfo - read and print the stat information for the address @eip, + * and info.eip_fn_addr should be the first address of the related function. + * */ +void print_debuginfo(uintptr_t eip) { panic("Not Implemented!"); } + +/* * + * print_stackframe - print a list of the saved eip values from the nested + * 'call' + * instructions that led to the current point of execution + * + * The x86 stack pointer, namely esp, points to the lowest location on the stack + * that is currently in use. Everything below that location in stack is free. + * Pushing + * a value onto the stack will invole decreasing the stack pointer and then + * writing + * the value to the place that stack pointer pointes to. And popping a value do + * the + * opposite. + * + * The ebp (base pointer) register, in contrast, is associated with the stack + * primarily by software convention. On entry to a C function, the function's + * prologue code normally saves the previous function's base pointer by pushing + * it onto the stack, and then copies the current esp value into ebp for the + * duration + * of the function. If all the functions in a program obey this convention, + * then at any given point during the program's execution, it is possible to + * trace + * back through the stack by following the chain of saved ebp pointers and + * determining + * exactly what nested sequence of function calls caused this particular point + * in the + * program to be reached. This capability can be particularly useful, for + * example, + * when a particular function causes an assert failure or panic because bad + * arguments + * were passed to it, but you aren't sure who passed the bad arguments. A stack + * backtrace lets you find the offending function. + * + * The inline function read_ebp() can tell us the value of current ebp. And the + * non-inline function read_eip() is useful, it can read the value of current + * eip, + * since while calling this function, read_eip() can read the caller's eip from + * stack easily. + * + * In print_debuginfo(), the function debuginfo_eip() can get enough information + * about + * calling-chain. Finally print_stackframe() will trace and print them for + * debugging. + * + * Note that, the length of ebp-chain is limited. In boot/bootasm.S, before + * jumping + * to the kernel entry, the value of ebp has been set to zero, that's the + * boundary. + * */ +void print_stackframe(void) { + /* LAB1 YOUR CODE : STEP 1 */ + /* (1) call read_ebp() to get the value of ebp. the type is (uint32_t); + * (2) call read_eip() to get the value of eip. the type is (uint32_t); + * (3) from 0 .. STACKFRAME_DEPTH + * (3.1) printf value of ebp, eip + * (3.2) (uint32_t)calling arguments [0..4] = the contents in address + * (unit32_t)ebp +2 [0..4] + * (3.3) cprintf("\n"); + * (3.4) call print_debuginfo(eip-1) to print the C calling function name + * and line number, etc. + * (3.5) popup a calling stackframe + * NOTICE: the calling funciton's return addr eip = ss:[ebp+4] + * the calling funciton's ebp = ss:[ebp] + */ + panic("Not Implemented!"); +} diff --git a/lab8_practice/kern/debug/kdebug.h b/lab8_practice/kern/debug/kdebug.h new file mode 100644 index 0000000..c2a7b74 --- /dev/null +++ b/lab8_practice/kern/debug/kdebug.h @@ -0,0 +1,12 @@ +#ifndef __KERN_DEBUG_KDEBUG_H__ +#define __KERN_DEBUG_KDEBUG_H__ + +#include +#include + +void print_kerninfo(void); +void print_stackframe(void); +void print_debuginfo(uintptr_t eip); + +#endif /* !__KERN_DEBUG_KDEBUG_H__ */ + diff --git a/lab8_practice/kern/debug/kmonitor.c b/lab8_practice/kern/debug/kmonitor.c new file mode 100644 index 0000000..af7c401 --- /dev/null +++ b/lab8_practice/kern/debug/kmonitor.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include + +/* * + * Simple command-line kernel monitor useful for controlling the + * kernel and exploring the system interactively. + * */ + +struct command { + const char *name; + const char *desc; + // return -1 to force monitor to exit + int(*func)(int argc, char **argv, struct trapframe *tf); +}; + +static struct command commands[] = { + {"help", "Display this list of commands.", mon_help}, + {"kerninfo", "Display information about the kernel.", mon_kerninfo}, + {"backtrace", "Print backtrace of stack frame.", mon_backtrace}, +}; + +/* return if kernel is panic, in kern/debug/panic.c */ +bool is_kernel_panic(void); + +#define NCOMMANDS (sizeof(commands)/sizeof(struct command)) + +/***** Kernel monitor command interpreter *****/ + +#define MAXARGS 16 +#define WHITESPACE " \t\n\r" + +/* parse - parse the command buffer into whitespace-separated arguments */ +static int +parse(char *buf, char **argv) { + int argc = 0; + while (1) { + // find global whitespace + while (*buf != '\0' && strchr(WHITESPACE, *buf) != NULL) { + *buf ++ = '\0'; + } + if (*buf == '\0') { + break; + } + + // save and scan past next arg + if (argc == MAXARGS - 1) { + cprintf("Too many arguments (max %d).\n", MAXARGS); + } + argv[argc ++] = buf; + while (*buf != '\0' && strchr(WHITESPACE, *buf) == NULL) { + buf ++; + } + } + return argc; +} + +/* * + * runcmd - parse the input string, split it into separated arguments + * and then lookup and invoke some related commands/ + * */ +static int +runcmd(char *buf, struct trapframe *tf) { + char *argv[MAXARGS]; + int argc = parse(buf, argv); + if (argc == 0) { + return 0; + } + int i; + for (i = 0; i < NCOMMANDS; i ++) { + if (strcmp(commands[i].name, argv[0]) == 0) { + return commands[i].func(argc - 1, argv + 1, tf); + } + } + cprintf("Unknown command '%s'\n", argv[0]); + return 0; +} + +/***** Implementations of basic kernel monitor commands *****/ + +void +kmonitor(struct trapframe *tf) { + cprintf("Welcome to the kernel debug monitor!!\n"); + cprintf("Type 'help' for a list of commands.\n"); + + if (tf != NULL) { + print_trapframe(tf); + } + + char *buf; + while (1) { + if ((buf = readline("K> ")) != NULL) { + if (runcmd(buf, tf) < 0) { + break; + } + } + } +} + +/* mon_help - print the information about mon_* functions */ +int +mon_help(int argc, char **argv, struct trapframe *tf) { + int i; + for (i = 0; i < NCOMMANDS; i ++) { + cprintf("%s - %s\n", commands[i].name, commands[i].desc); + } + return 0; +} + +/* * + * mon_kerninfo - call print_kerninfo in kern/debug/kdebug.c to + * print the memory occupancy in kernel. + * */ +int +mon_kerninfo(int argc, char **argv, struct trapframe *tf) { + print_kerninfo(); + return 0; +} + +/* * + * mon_backtrace - call print_stackframe in kern/debug/kdebug.c to + * print a backtrace of the stack. + * */ +int +mon_backtrace(int argc, char **argv, struct trapframe *tf) { + print_stackframe(); + return 0; +} + diff --git a/lab8_practice/kern/debug/kmonitor.h b/lab8_practice/kern/debug/kmonitor.h new file mode 100644 index 0000000..67dfe64 --- /dev/null +++ b/lab8_practice/kern/debug/kmonitor.h @@ -0,0 +1,19 @@ +#ifndef __KERN_DEBUG_MONITOR_H__ +#define __KERN_DEBUG_MONITOR_H__ + +#include + +void kmonitor(struct trapframe *tf); + +int mon_help(int argc, char **argv, struct trapframe *tf); +int mon_kerninfo(int argc, char **argv, struct trapframe *tf); +int mon_backtrace(int argc, char **argv, struct trapframe *tf); +int mon_continue(int argc, char **argv, struct trapframe *tf); +int mon_step(int argc, char **argv, struct trapframe *tf); +int mon_breakpoint(int argc, char **argv, struct trapframe *tf); +int mon_watchpoint(int argc, char **argv, struct trapframe *tf); +int mon_delete_dr(int argc, char **argv, struct trapframe *tf); +int mon_list_dr(int argc, char **argv, struct trapframe *tf); + +#endif /* !__KERN_DEBUG_MONITOR_H__ */ + diff --git a/lab8_practice/kern/debug/panic.c b/lab8_practice/kern/debug/panic.c new file mode 100644 index 0000000..1d6a237 --- /dev/null +++ b/lab8_practice/kern/debug/panic.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static bool is_panic = 0; + +/* * + * __panic - __panic is called on unresolvable fatal errors. it prints + * "panic: 'message'", and then enters the kernel monitor. + * */ +void +__panic(const char *file, int line, const char *fmt, ...) { + if (is_panic) { + goto panic_dead; + } + is_panic = 1; + + // print the 'message' + va_list ap; + va_start(ap, fmt); + cprintf("kernel panic at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); + +panic_dead: + // No debug monitor here + sbi_shutdown(); + intr_disable(); + while (1) { + kmonitor(NULL); + } +} + +/* __warn - like panic, but don't */ +void +__warn(const char *file, int line, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + cprintf("kernel warning at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); +} + +bool +is_kernel_panic(void) { + return is_panic; +} + diff --git a/lab8_practice/kern/debug/stab.h b/lab8_practice/kern/debug/stab.h new file mode 100644 index 0000000..8d5cea3 --- /dev/null +++ b/lab8_practice/kern/debug/stab.h @@ -0,0 +1,54 @@ +#ifndef __KERN_DEBUG_STAB_H__ +#define __KERN_DEBUG_STAB_H__ + +#include + +/* * + * STABS debugging info + * + * The kernel debugger can understand some debugging information in + * the STABS format. For more information on this format, see + * http://sources.redhat.com/gdb/onlinedocs/stabs_toc.html + * + * The constants below define some symbol types used by various debuggers + * and compilers. Kernel uses the N_SO, N_SOL, N_FUN, and N_SLINE types. + * */ + +#define N_GSYM 0x20 // global symbol +#define N_FNAME 0x22 // F77 function name +#define N_FUN 0x24 // procedure name +#define N_STSYM 0x26 // data segment variable +#define N_LCSYM 0x28 // bss segment variable +#define N_MAIN 0x2a // main function name +#define N_PC 0x30 // global Pascal symbol +#define N_RSYM 0x40 // register variable +#define N_SLINE 0x44 // text segment line number +#define N_DSLINE 0x46 // data segment line number +#define N_BSLINE 0x48 // bss segment line number +#define N_SSYM 0x60 // structure/union element +#define N_SO 0x64 // main source file name +#define N_LSYM 0x80 // stack variable +#define N_BINCL 0x82 // include file beginning +#define N_SOL 0x84 // included source file name +#define N_PSYM 0xa0 // parameter variable +#define N_EINCL 0xa2 // include file end +#define N_ENTRY 0xa4 // alternate entry point +#define N_LBRAC 0xc0 // left bracket +#define N_EXCL 0xc2 // deleted include file +#define N_RBRAC 0xe0 // right bracket +#define N_BCOMM 0xe2 // begin common +#define N_ECOMM 0xe4 // end common +#define N_ECOML 0xe8 // end common (local name) +#define N_LENG 0xfe // length of preceding entry + +/* Entries in the STABS table are formatted as follows. */ +struct stab { + uint32_t n_strx; // index into string table of name + uint8_t n_type; // type of symbol + uint8_t n_other; // misc info (usually empty) + uint16_t n_desc; // description field + uintptr_t n_value; // value of symbol +}; + +#endif /* !__KERN_DEBUG_STAB_H__ */ + diff --git a/lab8_practice/kern/driver/clock.c b/lab8_practice/kern/driver/clock.c new file mode 100644 index 0000000..7af6122 --- /dev/null +++ b/lab8_practice/kern/driver/clock.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +volatile size_t ticks; + +static inline uint64_t get_cycles(void) { +#if __riscv_xlen == 64 + uint64_t n; + __asm__ __volatile__("rdtime %0" : "=r"(n)); + return n; +#else + uint32_t lo, hi, tmp; + __asm__ __volatile__( + "1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r"(hi), "=&r"(lo), "=&r"(tmp)); + return ((uint64_t)hi << 32) | lo; +#endif +} + +static uint64_t timebase = 100000; + +/* * + * clock_init - initialize 8253 clock to interrupt 100 times per second, + * and then enable IRQ_TIMER. + * */ +void clock_init(void) { + set_csr(sie, MIP_STIP); + + clock_set_next_event(); + // initialize time counter 'ticks' to zero + ticks = 0; + + cprintf("++ setup timer interrupts\n"); +} + +void clock_set_next_event(void) { sbi_set_timer(get_cycles() + timebase); } diff --git a/lab8_practice/kern/driver/clock.h b/lab8_practice/kern/driver/clock.h new file mode 100644 index 0000000..3b75c98 --- /dev/null +++ b/lab8_practice/kern/driver/clock.h @@ -0,0 +1,11 @@ +#ifndef __KERN_DRIVER_CLOCK_H__ +#define __KERN_DRIVER_CLOCK_H__ + +#include + +extern volatile size_t ticks; + +void clock_init(void); +void clock_set_next_event(void); + +#endif /* !__KERN_DRIVER_CLOCK_H__ */ diff --git a/lab8_practice/kern/driver/console.c b/lab8_practice/kern/driver/console.c new file mode 100644 index 0000000..2c61a86 --- /dev/null +++ b/lab8_practice/kern/driver/console.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#define CONSBUFSIZE 512 + +static struct { + uint8_t buf[CONSBUFSIZE]; + uint32_t rpos; + uint32_t wpos; +} cons; + +/* * + * cons_intr - called by device interrupt routines to feed input + * characters into the circular console input buffer. + * */ +void cons_intr(int (*proc)(void)) { + int c; + while ((c = (*proc)()) != -1) { + if (c != 0) { + cons.buf[cons.wpos++] = c; + if (cons.wpos == CONSBUFSIZE) { + cons.wpos = 0; + } + } + } +} + +/* kbd_intr - try to feed input characters from keyboard */ +void kbd_intr(void) { + serial_intr(); +} + +/* serial_proc_data - get data from serial port */ +int serial_proc_data(void) { + int c = sbi_console_getchar(); + if (c < 0) { + return -1; + } + if (c == 127) { + c = '\b'; + } + return c; +} + +/* serial_intr - try to feed input characters from serial port */ +void serial_intr(void) { + cons_intr(serial_proc_data); +} + +/* serial_putc - print character to serial port */ +void serial_putc(int c) { + if (c != '\b') { + sbi_console_putchar(c); + } else { + sbi_console_putchar('\b'); + sbi_console_putchar(' '); + sbi_console_putchar('\b'); + } +} + +/* cons_init - initializes the console devices */ +void cons_init(void) { + sbi_console_getchar(); +} + +/* cons_putc - print a single character @c to console devices */ +void cons_putc(int c) { + bool intr_flag; + local_intr_save(intr_flag); + { + serial_putc(c); + } + local_intr_restore(intr_flag); +} + +/* * + * cons_getc - return the next input character from console, + * or 0 if none waiting. + * */ +int cons_getc(void) { + int c = 0; + bool intr_flag; + local_intr_save(intr_flag); + { + // poll for any pending input characters, + // so that this function works even when interrupts are disabled + // (e.g., when called from the kernel monitor). + serial_intr(); + + // grab the next character from the input buffer. + if (cons.rpos != cons.wpos) { + c = cons.buf[cons.rpos++]; + if (cons.rpos == CONSBUFSIZE) { + cons.rpos = 0; + } + } + } + local_intr_restore(intr_flag); + return c; +} diff --git a/lab8_practice/kern/driver/console.h b/lab8_practice/kern/driver/console.h new file mode 100644 index 0000000..72e6167 --- /dev/null +++ b/lab8_practice/kern/driver/console.h @@ -0,0 +1,11 @@ +#ifndef __KERN_DRIVER_CONSOLE_H__ +#define __KERN_DRIVER_CONSOLE_H__ + +void cons_init(void); +void cons_putc(int c); +int cons_getc(void); +void serial_intr(void); +void kbd_intr(void); + +#endif /* !__KERN_DRIVER_CONSOLE_H__ */ + diff --git a/lab8_practice/kern/driver/ide.c b/lab8_practice/kern/driver/ide.c new file mode 100644 index 0000000..71a7656 --- /dev/null +++ b/lab8_practice/kern/driver/ide.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +static int ide_wait_ready(unsigned short iobase, bool check_error) { return 0; } + +void ide_init(void) { + static_assert((SECTSIZE % 4) == 0); + for (int ideno = 0; ideno < MAX_IDE; ++ideno) { + ide_devices[ideno].valid = 0; + } + + ramdisk_init(SWAP_DEV_NO, &ide_devices[SWAP_DEV_NO]); + assert(VALID_IDE(SWAP_DEV_NO)); + + ramdisk_init(DISK0_DEV_NO, &ide_devices[DISK0_DEV_NO]); + assert(VALID_IDE(DISK0_DEV_NO)); +} + +bool ide_device_valid(unsigned short ideno) { return VALID_IDE(ideno); } + +size_t ide_device_size(unsigned short ideno) { + if (ide_device_valid(ideno)) { + return ide_devices[ideno].size; + } + return 0; +} + +int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, + size_t nsecs) { + assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); + assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); + return ide_devices[ideno].read_secs(&ide_devices[ideno], secno, dst, nsecs); +} + +int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, + size_t nsecs) { + assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); + assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); + return ide_devices[ideno].write_secs(&ide_devices[ideno], secno, src, + nsecs); +} diff --git a/lab8_practice/kern/driver/ide.h b/lab8_practice/kern/driver/ide.h new file mode 100644 index 0000000..1c21394 --- /dev/null +++ b/lab8_practice/kern/driver/ide.h @@ -0,0 +1,36 @@ +#ifndef __KERN_DRIVER_IDE_H__ +#define __KERN_DRIVER_IDE_H__ + +#include + +#define MAX_IDE 4 +#define MAX_NSECS 128 +#define MAX_DISK_NSECS 0x10000000U +#define VALID_IDE(ideno) \ + (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid)) + +static struct ide_device { + unsigned int valid; // 0 or 1 (If Device Really Exists) + unsigned int sets; // Commend Sets Supported + unsigned int size; // Size in Sectors + uintptr_t iobase; + void *dev_data; + char model[32]; // Model in String + + /* return 0 if succeed */ + int (*read_secs)(struct ide_device *dev, size_t secno, void *dst, + size_t nsecs); + int (*write_secs)(struct ide_device *dev, size_t secno, const void *src, + size_t nsecs); +} ide_devices[MAX_IDE]; + +void ide_init(void); +bool ide_device_valid(unsigned short ideno); +size_t ide_device_size(unsigned short ideno); + +int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, + size_t nsecs); +int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, + size_t nsecs); + +#endif /* !__KERN_DRIVER_IDE_H__ */ diff --git a/lab8_practice/kern/driver/intr.c b/lab8_practice/kern/driver/intr.c new file mode 100644 index 0000000..f722636 --- /dev/null +++ b/lab8_practice/kern/driver/intr.c @@ -0,0 +1,8 @@ +#include +#include + +/* intr_enable - enable irq interrupt */ +void intr_enable(void) { set_csr(sstatus, SSTATUS_SIE); } + +/* intr_disable - disable irq interrupt */ +void intr_disable(void) { clear_csr(sstatus, SSTATUS_SIE); } diff --git a/lab8_practice/kern/driver/intr.h b/lab8_practice/kern/driver/intr.h new file mode 100644 index 0000000..5fdf7a5 --- /dev/null +++ b/lab8_practice/kern/driver/intr.h @@ -0,0 +1,8 @@ +#ifndef __KERN_DRIVER_INTR_H__ +#define __KERN_DRIVER_INTR_H__ + +void intr_enable(void); +void intr_disable(void); + +#endif /* !__KERN_DRIVER_INTR_H__ */ + diff --git a/lab8_practice/kern/driver/kbdreg.h b/lab8_practice/kern/driver/kbdreg.h new file mode 100644 index 0000000..00dc49a --- /dev/null +++ b/lab8_practice/kern/driver/kbdreg.h @@ -0,0 +1,84 @@ +#ifndef __KERN_DRIVER_KBDREG_H__ +#define __KERN_DRIVER_KBDREG_H__ + +// Special keycodes +#define KEY_HOME 0xE0 +#define KEY_END 0xE1 +#define KEY_UP 0xE2 +#define KEY_DN 0xE3 +#define KEY_LF 0xE4 +#define KEY_RT 0xE5 +#define KEY_PGUP 0xE6 +#define KEY_PGDN 0xE7 +#define KEY_INS 0xE8 +#define KEY_DEL 0xE9 + + +/* This is i8042reg.h + kbdreg.h from NetBSD. */ + +#define KBSTATP 0x64 // kbd controller status port(I) +#define KBS_DIB 0x01 // kbd data in buffer +#define KBS_IBF 0x02 // kbd input buffer low +#define KBS_WARM 0x04 // kbd input buffer low +#define BS_OCMD 0x08 // kbd output buffer has command +#define KBS_NOSEC 0x10 // kbd security lock not engaged +#define KBS_TERR 0x20 // kbd transmission error +#define KBS_RERR 0x40 // kbd receive error +#define KBS_PERR 0x80 // kbd parity error + +#define KBCMDP 0x64 // kbd controller port(O) +#define KBC_RAMREAD 0x20 // read from RAM +#define KBC_RAMWRITE 0x60 // write to RAM +#define KBC_AUXDISABLE 0xa7 // disable auxiliary port +#define KBC_AUXENABLE 0xa8 // enable auxiliary port +#define KBC_AUXTEST 0xa9 // test auxiliary port +#define KBC_KBDECHO 0xd2 // echo to keyboard port +#define KBC_AUXECHO 0xd3 // echo to auxiliary port +#define KBC_AUXWRITE 0xd4 // write to auxiliary port +#define KBC_SELFTEST 0xaa // start self-test +#define KBC_KBDTEST 0xab // test keyboard port +#define KBC_KBDDISABLE 0xad // disable keyboard port +#define KBC_KBDENABLE 0xae // enable keyboard port +#define KBC_PULSE0 0xfe // pulse output bit 0 +#define KBC_PULSE1 0xfd // pulse output bit 1 +#define KBC_PULSE2 0xfb // pulse output bit 2 +#define KBC_PULSE3 0xf7 // pulse output bit 3 + +#define KBDATAP 0x60 // kbd data port(I) +#define KBOUTP 0x60 // kbd data port(O) + +#define K_RDCMDBYTE 0x20 +#define K_LDCMDBYTE 0x60 + +#define KC8_TRANS 0x40 // convert to old scan codes +#define KC8_MDISABLE 0x20 // disable mouse +#define KC8_KDISABLE 0x10 // disable keyboard +#define KC8_IGNSEC 0x08 // ignore security lock +#define KC8_CPU 0x04 // exit from protected mode reset +#define KC8_MENABLE 0x02 // enable mouse interrupt +#define KC8_KENABLE 0x01 // enable keyboard interrupt +#define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE) + +/* keyboard commands */ +#define KBC_RESET 0xFF // reset the keyboard +#define KBC_RESEND 0xFE // request the keyboard resend the last byte +#define KBC_SETDEFAULT 0xF6 // resets keyboard to its power-on defaults +#define KBC_DISABLE 0xF5 // as per KBC_SETDEFAULT, but also disable key scanning +#define KBC_ENABLE 0xF4 // enable key scanning +#define KBC_TYPEMATIC 0xF3 // set typematic rate and delay +#define KBC_SETTABLE 0xF0 // set scancode translation table +#define KBC_MODEIND 0xED // set mode indicators(i.e. LEDs) +#define KBC_ECHO 0xEE // request an echo from the keyboard + +/* keyboard responses */ +#define KBR_EXTENDED 0xE0 // extended key sequence +#define KBR_RESEND 0xFE // needs resend of command +#define KBR_ACK 0xFA // received a valid command +#define KBR_OVERRUN 0x00 // flooded +#define KBR_FAILURE 0xFD // diagnosic failure +#define KBR_BREAK 0xF0 // break code prefix - sent on key release +#define KBR_RSTDONE 0xAA // reset complete +#define KBR_ECHO 0xEE // echo response + +#endif /* !__KERN_DRIVER_KBDREG_H__ */ + diff --git a/lab8_practice/kern/driver/picirq.c b/lab8_practice/kern/driver/picirq.c new file mode 100644 index 0000000..d05cbef --- /dev/null +++ b/lab8_practice/kern/driver/picirq.c @@ -0,0 +1,6 @@ +#include + +void pic_enable(unsigned int irq) {} + +/* pic_init - initialize the 8259A interrupt controllers */ +void pic_init(void) {} diff --git a/lab8_practice/kern/driver/picirq.h b/lab8_practice/kern/driver/picirq.h new file mode 100644 index 0000000..41e965d --- /dev/null +++ b/lab8_practice/kern/driver/picirq.h @@ -0,0 +1,8 @@ +#ifndef __KERN_DRIVER_PICIRQ_H__ +#define __KERN_DRIVER_PICIRQ_H__ + +void pic_init(void); +void pic_enable(unsigned int irq); + +#endif /* !__KERN_DRIVER_PICIRQ_H__ */ + diff --git a/lab8_practice/kern/driver/ramdisk.c b/lab8_practice/kern/driver/ramdisk.c new file mode 100644 index 0000000..e68ba0b --- /dev/null +++ b/lab8_practice/kern/driver/ramdisk.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +extern char _binary_bin_swap_img_start[], _binary_bin_swap_img_end[]; +extern char _binary_bin_sfs_img_start[], _binary_bin_sfs_img_end[]; + +bool check_initrd(const char _initrd_begin[], const char _initrd_end[]) { + if (_initrd_begin == _initrd_end) { + cprintf("Warning: No Initrd!\n"); + return 0; + } + cprintf("Initrd: 0x%08x - 0x%08x, size: 0x%08x\n", _initrd_begin, + _initrd_end - 1, _initrd_end - _initrd_begin); + return 1; +} + +static int ramdisk_read(struct ide_device *dev, size_t secno, void *dst, + size_t nsecs) { + nsecs = MIN(nsecs, dev->size - secno); + if (nsecs < 0) return -1; + memcpy(dst, (void *)(dev->iobase + secno * SECTSIZE), nsecs * SECTSIZE); + return 0; +} + +static int ramdisk_write(struct ide_device *dev, size_t secno, const void *src, + size_t nsecs) { + nsecs = MIN(nsecs, dev->size - secno); + if (nsecs < 0) return -1; + memcpy((void *)(dev->iobase + secno * SECTSIZE), src, nsecs * SECTSIZE); + return 0; +} + +void ramdisk_init(int devno, struct ide_device *dev) { + memset(dev, 0, sizeof(struct ide_device)); + char *_initrd_begin; + char *_initrd_end; + if (devno == SWAP_DEV_NO) { + _initrd_begin = _binary_bin_swap_img_start; + _initrd_end = _binary_bin_swap_img_end; + } else if (devno == DISK0_DEV_NO) { + _initrd_begin = _binary_bin_sfs_img_start; + _initrd_end = _binary_bin_sfs_img_end; + } else { + panic("Device Not Found"); + } + + if (check_initrd(_initrd_begin, _initrd_end)) { + dev->valid = 1; + dev->sets = ~0; + dev->size = (unsigned int)(_initrd_end - _initrd_begin) / SECTSIZE; + dev->iobase = (uintptr_t)_initrd_begin; + strcpy(dev->model, "KERN_INITRD"); + dev->read_secs = ramdisk_read; + dev->write_secs = ramdisk_write; + } +} diff --git a/lab8_practice/kern/driver/ramdisk.h b/lab8_practice/kern/driver/ramdisk.h new file mode 100644 index 0000000..87c0907 --- /dev/null +++ b/lab8_practice/kern/driver/ramdisk.h @@ -0,0 +1,3 @@ +#include + +void ramdisk_init(int devno, struct ide_device *dev); diff --git a/lab8_practice/kern/fs/devs/dev.c b/lab8_practice/kern/fs/devs/dev.c new file mode 100644 index 0000000..007d748 --- /dev/null +++ b/lab8_practice/kern/fs/devs/dev.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * dev_open - Called for each open(). + */ +static int +dev_open(struct inode *node, uint32_t open_flags) { + if (open_flags & (O_CREAT | O_TRUNC | O_EXCL | O_APPEND)) { + return -E_INVAL; + } + struct device *dev = vop_info(node, device); + return dop_open(dev, open_flags); +} + +/* + * dev_close - Called on the last close(). Just pass through. + */ +static int +dev_close(struct inode *node) { + struct device *dev = vop_info(node, device); + return dop_close(dev); +} + +/* + * dev_read -Called for read. Hand off to iobuf. + */ +static int +dev_read(struct inode *node, struct iobuf *iob) { + struct device *dev = vop_info(node, device); + return dop_io(dev, iob, 0); +} + +/* + * dev_write -Called for write. Hand off to iobuf. + */ +static int +dev_write(struct inode *node, struct iobuf *iob) { + struct device *dev = vop_info(node, device); + return dop_io(dev, iob, 1); +} + +/* + * dev_ioctl - Called for ioctl(). Just pass through. + */ +static int +dev_ioctl(struct inode *node, int op, void *data) { + struct device *dev = vop_info(node, device); + return dop_ioctl(dev, op, data); +} + +/* + * dev_fstat - Called for stat(). + * Set the type and the size (block devices only). + * The link count for a device is always 1. + */ +static int +dev_fstat(struct inode *node, struct stat *stat) { + int ret; + memset(stat, 0, sizeof(struct stat)); + if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) { + return ret; + } + struct device *dev = vop_info(node, device); + stat->st_nlinks = 1; + stat->st_blocks = dev->d_blocks; + stat->st_size = stat->st_blocks * dev->d_blocksize; + return 0; +} + +/* + * dev_gettype - Return the type. A device is a "block device" if it has a known + * length. A device that generates data in a stream is a "character + * device". + */ +static int +dev_gettype(struct inode *node, uint32_t *type_store) { + struct device *dev = vop_info(node, device); + *type_store = (dev->d_blocks > 0) ? S_IFBLK : S_IFCHR; + return 0; +} + +/* + * dev_tryseek - Attempt a seek. + * For block devices, require block alignment. + * For character devices, prohibit seeking entirely. + */ +static int +dev_tryseek(struct inode *node, off_t pos) { + struct device *dev = vop_info(node, device); + if (dev->d_blocks > 0) { + if ((pos % dev->d_blocksize) == 0) { + if (pos >= 0 && pos < dev->d_blocks * dev->d_blocksize) { + return 0; + } + } + } + return -E_INVAL; +} + +/* + * dev_lookup - Name lookup. + * + * One interesting feature of device:name pathname syntax is that you + * can implement pathnames on arbitrary devices. For instance, if you + * had a graphics device that supported multiple resolutions (which we + * don't), you might arrange things so that you could open it with + * pathnames like "video:800x600/24bpp" in order to select the operating + * mode. + * + * However, we have no support for this in the base system. + */ +static int +dev_lookup(struct inode *node, char *path, struct inode **node_store) { + if (*path != '\0') { + return -E_NOENT; + } + vop_ref_inc(node); + *node_store = node; + return 0; +} + +/* + * Function table for device inodes. + */ +static const struct inode_ops dev_node_ops = { + .vop_magic = VOP_MAGIC, + .vop_open = dev_open, + .vop_close = dev_close, + .vop_read = dev_read, + .vop_write = dev_write, + .vop_fstat = dev_fstat, + .vop_ioctl = dev_ioctl, + .vop_gettype = dev_gettype, + .vop_tryseek = dev_tryseek, + .vop_lookup = dev_lookup, +}; + +#define init_device(x) \ + do { \ + extern void dev_init_##x(void); \ + dev_init_##x(); \ + } while (0) + +/* dev_init - Initialization functions for builtin vfs-level devices. */ +void +dev_init(void) { + // init_device(null); + init_device(stdin); + init_device(stdout); + init_device(disk0); +} +/* dev_create_inode - Create inode for a vfs-level device. */ +struct inode * +dev_create_inode(void) { + struct inode *node; + if ((node = alloc_inode(device)) != NULL) { + vop_init(node, &dev_node_ops, NULL); + } + return node; +} + diff --git a/lab8_practice/kern/fs/devs/dev.h b/lab8_practice/kern/fs/devs/dev.h new file mode 100644 index 0000000..9c605d7 --- /dev/null +++ b/lab8_practice/kern/fs/devs/dev.h @@ -0,0 +1,31 @@ +#ifndef __KERN_FS_DEVS_DEV_H__ +#define __KERN_FS_DEVS_DEV_H__ + +#include + +struct inode; +struct iobuf; + +/* + * Filesystem-namespace-accessible device. + * d_io is for both reads and writes; the iobuf will indicates the direction. + */ +struct device { + size_t d_blocks; + size_t d_blocksize; + int (*d_open)(struct device *dev, uint32_t open_flags); + int (*d_close)(struct device *dev); + int (*d_io)(struct device *dev, struct iobuf *iob, bool write); + int (*d_ioctl)(struct device *dev, int op, void *data); +}; + +#define dop_open(dev, open_flags) ((dev)->d_open(dev, open_flags)) +#define dop_close(dev) ((dev)->d_close(dev)) +#define dop_io(dev, iob, write) ((dev)->d_io(dev, iob, write)) +#define dop_ioctl(dev, op, data) ((dev)->d_ioctl(dev, op, data)) + +void dev_init(void); +struct inode *dev_create_inode(void); + +#endif /* !__KERN_FS_DEVS_DEV_H__ */ + diff --git a/lab8_practice/kern/fs/devs/dev_disk0.c b/lab8_practice/kern/fs/devs/dev_disk0.c new file mode 100644 index 0000000..3d6fc1d --- /dev/null +++ b/lab8_practice/kern/fs/devs/dev_disk0.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISK0_BLKSIZE PGSIZE +#define DISK0_BUFSIZE (4 * DISK0_BLKSIZE) +#define DISK0_BLK_NSECT (DISK0_BLKSIZE / SECTSIZE) + +static char *disk0_buffer; +static semaphore_t disk0_sem; + +static void +lock_disk0(void) { + down(&(disk0_sem)); +} + +static void +unlock_disk0(void) { + up(&(disk0_sem)); +} + +static int +disk0_open(struct device *dev, uint32_t open_flags) { + return 0; +} + +static int +disk0_close(struct device *dev) { + return 0; +} + +static void +disk0_read_blks_nolock(uint32_t blkno, uint32_t nblks) { + int ret; + uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT; + if ((ret = ide_read_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) { + panic("disk0: read blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n", + blkno, sectno, nblks, nsecs, ret); + } +} + +static void +disk0_write_blks_nolock(uint32_t blkno, uint32_t nblks) { + int ret; + uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT; + if ((ret = ide_write_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) { + panic("disk0: write blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n", + blkno, sectno, nblks, nsecs, ret); + } +} + +static int +disk0_io(struct device *dev, struct iobuf *iob, bool write) { + off_t offset = iob->io_offset; + size_t resid = iob->io_resid; + uint32_t blkno = offset / DISK0_BLKSIZE; + uint32_t nblks = resid / DISK0_BLKSIZE; + + /* don't allow I/O that isn't block-aligned */ + if ((offset % DISK0_BLKSIZE) != 0 || (resid % DISK0_BLKSIZE) != 0) { + return -E_INVAL; + } + + /* don't allow I/O past the end of disk0 */ + if (blkno + nblks > dev->d_blocks) { + return -E_INVAL; + } + + /* read/write nothing ? */ + if (nblks == 0) { + return 0; + } + + lock_disk0(); + while (resid != 0) { + size_t copied, alen = DISK0_BUFSIZE; + if (write) { + iobuf_move(iob, disk0_buffer, alen, 0, &copied); + assert(copied != 0 && copied <= resid && copied % DISK0_BLKSIZE == 0); + nblks = copied / DISK0_BLKSIZE; + disk0_write_blks_nolock(blkno, nblks); + } + else { + if (alen > resid) { + alen = resid; + } + nblks = alen / DISK0_BLKSIZE; + disk0_read_blks_nolock(blkno, nblks); + iobuf_move(iob, disk0_buffer, alen, 1, &copied); + assert(copied == alen && copied % DISK0_BLKSIZE == 0); + } + resid -= copied, blkno += nblks; + } + unlock_disk0(); + return 0; +} + +static int +disk0_ioctl(struct device *dev, int op, void *data) { + return -E_UNIMP; +} + +static void +disk0_device_init(struct device *dev) { + static_assert(DISK0_BLKSIZE % SECTSIZE == 0); + if (!ide_device_valid(DISK0_DEV_NO)) { + panic("disk0 device isn't available.\n"); + } + dev->d_blocks = ide_device_size(DISK0_DEV_NO) / DISK0_BLK_NSECT; + dev->d_blocksize = DISK0_BLKSIZE; + dev->d_open = disk0_open; + dev->d_close = disk0_close; + dev->d_io = disk0_io; + dev->d_ioctl = disk0_ioctl; + sem_init(&(disk0_sem), 1); + + static_assert(DISK0_BUFSIZE % DISK0_BLKSIZE == 0); + if ((disk0_buffer = kmalloc(DISK0_BUFSIZE)) == NULL) { + panic("disk0 alloc buffer failed.\n"); + } +} + +void +dev_init_disk0(void) { + struct inode *node; + if ((node = dev_create_inode()) == NULL) { + panic("disk0: dev_create_node.\n"); + } + disk0_device_init(vop_info(node, device)); + + int ret; + if ((ret = vfs_add_dev("disk0", node, 1)) != 0) { + panic("disk0: vfs_add_dev: %e.\n", ret); + } +} + diff --git a/lab8_practice/kern/fs/devs/dev_stdin.c b/lab8_practice/kern/fs/devs/dev_stdin.c new file mode 100644 index 0000000..0da00fc --- /dev/null +++ b/lab8_practice/kern/fs/devs/dev_stdin.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STDIN_BUFSIZE 4096 + +static char stdin_buffer[STDIN_BUFSIZE]; +static off_t p_rpos, p_wpos; +static wait_queue_t __wait_queue, *wait_queue = &__wait_queue; + +void +dev_stdin_write(char c) { + bool intr_flag; + if (c != '\0') { + local_intr_save(intr_flag); + { + stdin_buffer[p_wpos % STDIN_BUFSIZE] = c; + if (p_wpos - p_rpos < STDIN_BUFSIZE) { + p_wpos ++; + } + if (!wait_queue_empty(wait_queue)) { + wakeup_queue(wait_queue, WT_KBD, 1); + } + } + local_intr_restore(intr_flag); + } +} + +static int +dev_stdin_read(char *buf, size_t len) { + int ret = 0; + bool intr_flag; + local_intr_save(intr_flag); + { + for (; ret < len; ret ++, p_rpos ++) { + try_again: + if (p_rpos < p_wpos) { + *buf ++ = stdin_buffer[p_rpos % STDIN_BUFSIZE]; + } + else { + wait_t __wait, *wait = &__wait; + wait_current_set(wait_queue, wait, WT_KBD); + local_intr_restore(intr_flag); + + schedule(); + + local_intr_save(intr_flag); + wait_current_del(wait_queue, wait); + if (wait->wakeup_flags == WT_KBD) { + goto try_again; + } + break; + } + } + } + local_intr_restore(intr_flag); + return ret; +} + +static int +stdin_open(struct device *dev, uint32_t open_flags) { + if (open_flags != O_RDONLY) { + return -E_INVAL; + } + return 0; +} + +static int +stdin_close(struct device *dev) { + return 0; +} + +static int +stdin_io(struct device *dev, struct iobuf *iob, bool write) { + if (!write) { + int ret; + if ((ret = dev_stdin_read(iob->io_base, iob->io_resid)) > 0) { + iob->io_resid -= ret; + } + return ret; + } + return -E_INVAL; +} + +static int +stdin_ioctl(struct device *dev, int op, void *data) { + return -E_INVAL; +} + +static void +stdin_device_init(struct device *dev) { + dev->d_blocks = 0; + dev->d_blocksize = 1; + dev->d_open = stdin_open; + dev->d_close = stdin_close; + dev->d_io = stdin_io; + dev->d_ioctl = stdin_ioctl; + + p_rpos = p_wpos = 0; + wait_queue_init(wait_queue); +} + +void +dev_init_stdin(void) { + struct inode *node; + if ((node = dev_create_inode()) == NULL) { + panic("stdin: dev_create_node.\n"); + } + stdin_device_init(vop_info(node, device)); + + int ret; + if ((ret = vfs_add_dev("stdin", node, 0)) != 0) { + panic("stdin: vfs_add_dev: %e.\n", ret); + } +} + diff --git a/lab8_practice/kern/fs/devs/dev_stdout.c b/lab8_practice/kern/fs/devs/dev_stdout.c new file mode 100644 index 0000000..641acf0 --- /dev/null +++ b/lab8_practice/kern/fs/devs/dev_stdout.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +stdout_open(struct device *dev, uint32_t open_flags) { + if (open_flags != O_WRONLY) { + return -E_INVAL; + } + return 0; +} + +static int +stdout_close(struct device *dev) { + return 0; +} + +static int +stdout_io(struct device *dev, struct iobuf *iob, bool write) { + if (write) { + char *data = iob->io_base; + for (; iob->io_resid != 0; iob->io_resid --) { + cputchar(*data ++); + } + return 0; + } + return -E_INVAL; +} + +static int +stdout_ioctl(struct device *dev, int op, void *data) { + return -E_INVAL; +} + +static void +stdout_device_init(struct device *dev) { + dev->d_blocks = 0; + dev->d_blocksize = 1; + dev->d_open = stdout_open; + dev->d_close = stdout_close; + dev->d_io = stdout_io; + dev->d_ioctl = stdout_ioctl; +} + +void +dev_init_stdout(void) { + struct inode *node; + if ((node = dev_create_inode()) == NULL) { + panic("stdout: dev_create_node.\n"); + } + stdout_device_init(vop_info(node, device)); + + int ret; + if ((ret = vfs_add_dev("stdout", node, 0)) != 0) { + panic("stdout: vfs_add_dev: %e.\n", ret); + } +} + diff --git a/lab8_practice/kern/fs/file.c b/lab8_practice/kern/fs/file.c new file mode 100644 index 0000000..b11486a --- /dev/null +++ b/lab8_practice/kern/fs/file.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define testfd(fd) ((fd) >= 0 && (fd) < FILES_STRUCT_NENTRY) + +// get_fd_array - get current process's open files table +static struct file * +get_fd_array(void) { + struct files_struct *filesp = current->filesp; + assert(filesp != NULL && files_count(filesp) > 0); + return filesp->fd_array; +} + +// fd_array_init - initialize the open files table +void +fd_array_init(struct file *fd_array) { + int fd; + struct file *file = fd_array; + for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) { + file->open_count = 0; + file->status = FD_NONE, file->fd = fd; + } +} + +// fs_array_alloc - allocate a free file item (with FD_NONE status) in open files table +static int +fd_array_alloc(int fd, struct file **file_store) { +// panic("debug"); + struct file *file = get_fd_array(); + if (fd == NO_FD) { + for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) { + if (file->status == FD_NONE) { + goto found; + } + } + return -E_MAX_OPEN; + } + else { + if (testfd(fd)) { + file += fd; + if (file->status == FD_NONE) { + goto found; + } + return -E_BUSY; + } + return -E_INVAL; + } +found: + assert(fopen_count(file) == 0); + file->status = FD_INIT, file->node = NULL; + *file_store = file; + return 0; +} + +// fd_array_free - free a file item in open files table +static void +fd_array_free(struct file *file) { + assert(file->status == FD_INIT || file->status == FD_CLOSED); + assert(fopen_count(file) == 0); + if (file->status == FD_CLOSED) { + vfs_close(file->node); + } + file->status = FD_NONE; +} + +static void +fd_array_acquire(struct file *file) { + assert(file->status == FD_OPENED); + fopen_count_inc(file); +} + +// fd_array_release - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item +static void +fd_array_release(struct file *file) { + assert(file->status == FD_OPENED || file->status == FD_CLOSED); + assert(fopen_count(file) > 0); + if (fopen_count_dec(file) == 0) { + fd_array_free(file); + } +} + +// fd_array_open - file's open_count++, set status to FD_OPENED +void +fd_array_open(struct file *file) { + assert(file->status == FD_INIT && file->node != NULL); + file->status = FD_OPENED; + fopen_count_inc(file); +} + +// fd_array_close - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item +void +fd_array_close(struct file *file) { + assert(file->status == FD_OPENED); + assert(fopen_count(file) > 0); + file->status = FD_CLOSED; + if (fopen_count_dec(file) == 0) { + fd_array_free(file); + } +} + +//fs_array_dup - duplicate file 'from' to file 'to' +void +fd_array_dup(struct file *to, struct file *from) { + //cprintf("[fd_array_dup]from fd=%d, to fd=%d\n",from->fd, to->fd); + assert(to->status == FD_INIT && from->status == FD_OPENED); + to->pos = from->pos; + to->readable = from->readable; + to->writable = from->writable; + struct inode *node = from->node; + vop_ref_inc(node), vop_open_inc(node); + to->node = node; + fd_array_open(to); +} + +// fd2file - use fd as index of fd_array, return the array item (file) +static inline int +fd2file(int fd, struct file **file_store) { + if (testfd(fd)) { + struct file *file = get_fd_array() + fd; + if (file->status == FD_OPENED && file->fd == fd) { + *file_store = file; + return 0; + } + } + return -E_INVAL; +} + +// file_testfd - test file is readble or writable? +bool +file_testfd(int fd, bool readable, bool writable) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return 0; + } + if (readable && !file->readable) { + return 0; + } + if (writable && !file->writable) { + return 0; + } + return 1; +} + +// open file +int +file_open(char *path, uint32_t open_flags) { + bool readable = 0, writable = 0; + switch (open_flags & O_ACCMODE) { + case O_RDONLY: readable = 1; break; + case O_WRONLY: writable = 1; break; + case O_RDWR: + readable = writable = 1; + break; + default: + return -E_INVAL; + } + int ret; + struct file *file; + if ((ret = fd_array_alloc(NO_FD, &file)) != 0) { + return ret; + } + struct inode *node; + if ((ret = vfs_open(path, open_flags, &node)) != 0) { + fd_array_free(file); + return ret; + } + file->pos = 0; + if (open_flags & O_APPEND) { + struct stat __stat, *stat = &__stat; + if ((ret = vop_fstat(node, stat)) != 0) { + vfs_close(node); + fd_array_free(file); + return ret; + } + file->pos = stat->st_size; + } + file->node = node; + file->readable = readable; + file->writable = writable; + fd_array_open(file); + return file->fd; +} + +// close file +int +file_close(int fd) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_close(file); + return 0; +} + +// read file +int +file_read(int fd, void *base, size_t len, size_t *copied_store) { + int ret; + struct file *file; + *copied_store = 0; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + if (!file->readable) { + return -E_INVAL; + } + fd_array_acquire(file); + + struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos); + ret = vop_read(file->node, iob); + + size_t copied = iobuf_used(iob); + if (file->status == FD_OPENED) { + file->pos += copied; + } + *copied_store = copied; + fd_array_release(file); + return ret; +} + +// write file +int +file_write(int fd, void *base, size_t len, size_t *copied_store) { + int ret; + struct file *file; + *copied_store = 0; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + if (!file->writable) { + return -E_INVAL; + } + fd_array_acquire(file); + + struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos); + ret = vop_write(file->node, iob); + + size_t copied = iobuf_used(iob); + if (file->status == FD_OPENED) { + file->pos += copied; + } + *copied_store = copied; + fd_array_release(file); + return ret; +} + +// seek file +int +file_seek(int fd, off_t pos, int whence) { + struct stat __stat, *stat = &__stat; + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + + switch (whence) { + case LSEEK_SET: break; + case LSEEK_CUR: pos += file->pos; break; + case LSEEK_END: + if ((ret = vop_fstat(file->node, stat)) == 0) { + pos += stat->st_size; + } + break; + default: ret = -E_INVAL; + } + + if (ret == 0) { + if ((ret = vop_tryseek(file->node, pos)) == 0) { + file->pos = pos; + } +// cprintf("file_seek, pos=%d, whence=%d, ret=%d\n", pos, whence, ret); + } + fd_array_release(file); + return ret; +} + +// stat file +int +file_fstat(int fd, struct stat *stat) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + ret = vop_fstat(file->node, stat); + fd_array_release(file); + return ret; +} + +// sync file +int +file_fsync(int fd) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + ret = vop_fsync(file->node); + fd_array_release(file); + return ret; +} + +// get file entry in DIR +int +file_getdirentry(int fd, struct dirent *direntp) { + int ret; + struct file *file; + if ((ret = fd2file(fd, &file)) != 0) { + return ret; + } + fd_array_acquire(file); + + struct iobuf __iob, *iob = iobuf_init(&__iob, direntp->name, sizeof(direntp->name), direntp->offset); + if ((ret = vop_getdirentry(file->node, iob)) == 0) { + direntp->offset += iobuf_used(iob); + } + fd_array_release(file); + return ret; +} + +// duplicate file +int +file_dup(int fd1, int fd2) { + int ret; + struct file *file1, *file2; + if ((ret = fd2file(fd1, &file1)) != 0) { + return ret; + } + if ((ret = fd_array_alloc(fd2, &file2)) != 0) { + return ret; + } + fd_array_dup(file2, file1); + return file2->fd; +} + + diff --git a/lab8_practice/kern/fs/file.h b/lab8_practice/kern/fs/file.h new file mode 100644 index 0000000..46d523c --- /dev/null +++ b/lab8_practice/kern/fs/file.h @@ -0,0 +1,62 @@ +#ifndef __KERN_FS_FILE_H__ +#define __KERN_FS_FILE_H__ + +//#include +#include +#include +#include +#include + +struct inode; +struct stat; +struct dirent; + +struct file { + enum { + FD_NONE, FD_INIT, FD_OPENED, FD_CLOSED, + } status; + bool readable; + bool writable; + int fd; + off_t pos; + struct inode *node; + int open_count; +}; + +void fd_array_init(struct file *fd_array); +void fd_array_open(struct file *file); +void fd_array_close(struct file *file); +void fd_array_dup(struct file *to, struct file *from); +bool file_testfd(int fd, bool readable, bool writable); + +int file_open(char *path, uint32_t open_flags); +int file_close(int fd); +int file_read(int fd, void *base, size_t len, size_t *copied_store); +int file_write(int fd, void *base, size_t len, size_t *copied_store); +int file_seek(int fd, off_t pos, int whence); +int file_fstat(int fd, struct stat *stat); +int file_fsync(int fd); +int file_getdirentry(int fd, struct dirent *dirent); +int file_dup(int fd1, int fd2); +int file_pipe(int fd[]); +int file_mkfifo(const char *name, uint32_t open_flags); + +static inline int +fopen_count(struct file *file) { + return file->open_count; +} + +static inline int +fopen_count_inc(struct file *file) { + file->open_count += 1; + return file->open_count; +} + +static inline int +fopen_count_dec(struct file *file) { + file->open_count -= 1; + return file->open_count; +} + +#endif /* !__KERN_FS_FILE_H__ */ + diff --git a/lab8_practice/kern/fs/fs.c b/lab8_practice/kern/fs/fs.c new file mode 100644 index 0000000..6e6db6f --- /dev/null +++ b/lab8_practice/kern/fs/fs.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +//called when init_main proc start +void +fs_init(void) { + vfs_init(); + dev_init(); + sfs_init(); +} + +void +fs_cleanup(void) { + vfs_cleanup(); +} + +void +lock_files(struct files_struct *filesp) { + down(&(filesp->files_sem)); +} + +void +unlock_files(struct files_struct *filesp) { + up(&(filesp->files_sem)); +} +//Called when a new proc init +struct files_struct * +files_create(void) { + //cprintf("[files_create]\n"); + //static_assert((int)FILES_STRUCT_NENTRY > 128); + struct files_struct *filesp; + if ((filesp = kmalloc(sizeof(struct files_struct) + FILES_STRUCT_BUFSIZE)) != NULL) { + filesp->pwd = NULL; + filesp->fd_array = (void *)(filesp + 1); + filesp->files_count = 0; + sem_init(&(filesp->files_sem), 1); + fd_array_init(filesp->fd_array); + } + return filesp; +} +//Called when a proc exit +void +files_destroy(struct files_struct *filesp) { +// cprintf("[files_destroy]\n"); + assert(filesp != NULL && files_count(filesp) == 0); + if (filesp->pwd != NULL) { + vop_ref_dec(filesp->pwd); + } + int i; + struct file *file = filesp->fd_array; + for (i = 0; i < FILES_STRUCT_NENTRY; i ++, file ++) { + if (file->status == FD_OPENED) { + fd_array_close(file); + } + assert(file->status == FD_NONE); + } + kfree(filesp); +} + +void +files_closeall(struct files_struct *filesp) { +// cprintf("[files_closeall]\n"); + assert(filesp != NULL && files_count(filesp) > 0); + int i; + struct file *file = filesp->fd_array; + //skip the stdin & stdout + for (i = 2, file += 2; i < FILES_STRUCT_NENTRY; i ++, file ++) { + if (file->status == FD_OPENED) { + fd_array_close(file); + } + } +} + +int +dup_files(struct files_struct *to, struct files_struct *from) { +// cprintf("[dup_fs]\n"); + assert(to != NULL && from != NULL); + assert(files_count(to) == 0 && files_count(from) > 0); + if ((to->pwd = from->pwd) != NULL) { + vop_ref_inc(to->pwd); + } + int i; + struct file *to_file = to->fd_array, *from_file = from->fd_array; + for (i = 0; i < FILES_STRUCT_NENTRY; i ++, to_file ++, from_file ++) { + if (from_file->status == FD_OPENED) { + /* alloc_fd first */ + to_file->status = FD_INIT; + fd_array_dup(to_file, from_file); + } + } + return 0; +} + diff --git a/lab8_practice/kern/fs/fs.h b/lab8_practice/kern/fs/fs.h new file mode 100644 index 0000000..4301c3a --- /dev/null +++ b/lab8_practice/kern/fs/fs.h @@ -0,0 +1,61 @@ +#ifndef __KERN_FS_FS_H__ +#define __KERN_FS_FS_H__ + +#include +#include +#include +#include + +#define SECTSIZE 512 +#define PAGE_NSECT (PGSIZE / SECTSIZE) + +#define SWAP_DEV_NO 1 +#define DISK0_DEV_NO 2 +#define DISK1_DEV_NO 3 + +void fs_init(void); +void fs_cleanup(void); + +struct inode; +struct file; + +/* + * process's file related informaction + */ +struct files_struct { + struct inode *pwd; // inode of present working directory + struct file *fd_array; // opened files array + int files_count; // the number of opened files + semaphore_t files_sem; // lock protect sem +}; + +#define FILES_STRUCT_BUFSIZE (PGSIZE - sizeof(struct files_struct)) +#define FILES_STRUCT_NENTRY (FILES_STRUCT_BUFSIZE / sizeof(struct file)) + +void lock_files(struct files_struct *filesp); +void unlock_files(struct files_struct *filesp); + +struct files_struct *files_create(void); +void files_destroy(struct files_struct *filesp); +void files_closeall(struct files_struct *filesp); +int dup_files(struct files_struct *to, struct files_struct *from); + +static inline int +files_count(struct files_struct *filesp) { + return filesp->files_count; +} + +static inline int +files_count_inc(struct files_struct *filesp) { + filesp->files_count += 1; + return filesp->files_count; +} + +static inline int +files_count_dec(struct files_struct *filesp) { + filesp->files_count -= 1; + return filesp->files_count; +} + +#endif /* !__KERN_FS_FS_H__ */ + diff --git a/lab8_practice/kern/fs/iobuf.c b/lab8_practice/kern/fs/iobuf.c new file mode 100644 index 0000000..2d13b2e --- /dev/null +++ b/lab8_practice/kern/fs/iobuf.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +/* + * iobuf_init - init io buffer struct. + * set up io_base to point to the buffer you want to transfer to, and set io_len to the length of buffer; + * initialize io_offset as desired; + * initialize io_resid to the total amount of data that can be transferred through this io. + */ +struct iobuf * +iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset) { + iob->io_base = base; + iob->io_offset = offset; + iob->io_len = iob->io_resid = len; + return iob; +} + +/* iobuf_move - move data (iob->io_base ---> data OR data --> iob->io.base) in memory + * @copiedp: the size of data memcopied + * + * iobuf_move may be called repeatedly on the same io to transfer + * additional data until the available buffer space the io refers to + * is exhausted. + */ +int +iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp) { + size_t alen; + if ((alen = iob->io_resid) > len) { + alen = len; + } + if (alen > 0) { + void *src = iob->io_base, *dst = data; + if (m2b) { + void *tmp = src; + src = dst, dst = tmp; + } + memmove(dst, src, alen); + iobuf_skip(iob, alen), len -= alen; + } + if (copiedp != NULL) { + *copiedp = alen; + } + return (len == 0) ? 0 : -E_NO_MEM; +} + +/* + * iobuf_move_zeros - set io buffer zero + * @copiedp: the size of data memcopied + */ +int +iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp) { + size_t alen; + if ((alen = iob->io_resid) > len) { + alen = len; + } + if (alen > 0) { + memset(iob->io_base, 0, alen); + iobuf_skip(iob, alen), len -= alen; + } + if (copiedp != NULL) { + *copiedp = alen; + } + return (len == 0) ? 0 : -E_NO_MEM; +} + +/* + * iobuf_skip - change the current position of io buffer + */ +void +iobuf_skip(struct iobuf *iob, size_t n) { + assert(iob->io_resid >= n); + iob->io_base += n, iob->io_offset += n, iob->io_resid -= n; +} + diff --git a/lab8_practice/kern/fs/iobuf.h b/lab8_practice/kern/fs/iobuf.h new file mode 100644 index 0000000..8bb668a --- /dev/null +++ b/lab8_practice/kern/fs/iobuf.h @@ -0,0 +1,24 @@ +#ifndef __KERN_FS_IOBUF_H__ +#define __KERN_FS_IOBUF_H__ + +#include + +/* + * iobuf is a buffer Rd/Wr status record + */ +struct iobuf { + void *io_base; // the base addr of buffer (used for Rd/Wr) + off_t io_offset; // current Rd/Wr position in buffer, will have been incremented by the amount transferred + size_t io_len; // the length of buffer (used for Rd/Wr) + size_t io_resid; // current resident length need to Rd/Wr, will have been decremented by the amount transferred. +}; + +#define iobuf_used(iob) ((size_t)((iob)->io_len - (iob)->io_resid)) + +struct iobuf *iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset); +int iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp); +int iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp); +void iobuf_skip(struct iobuf *iob, size_t n); + +#endif /* !__KERN_FS_IOBUF_H__ */ + diff --git a/lab8_practice/kern/fs/sfs/bitmap.c b/lab8_practice/kern/fs/sfs/bitmap.c new file mode 100644 index 0000000..c2d1c8d --- /dev/null +++ b/lab8_practice/kern/fs/sfs/bitmap.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +#define WORD_TYPE uint32_t +#define WORD_BITS (sizeof(WORD_TYPE) * CHAR_BIT) + +struct bitmap { + uint32_t nbits; + uint32_t nwords; + WORD_TYPE *map; +}; + +// bitmap_create - allocate a new bitmap object. +struct bitmap * +bitmap_create(uint32_t nbits) { + static_assert(WORD_BITS != 0); + assert(nbits != 0 && nbits + WORD_BITS > nbits); + + struct bitmap *bitmap; + if ((bitmap = kmalloc(sizeof(struct bitmap))) == NULL) { + return NULL; + } + + uint32_t nwords = ROUNDUP_DIV(nbits, WORD_BITS); + WORD_TYPE *map; + if ((map = kmalloc(sizeof(WORD_TYPE) * nwords)) == NULL) { + kfree(bitmap); + return NULL; + } + + bitmap->nbits = nbits, bitmap->nwords = nwords; + bitmap->map = memset(map, 0xFF, sizeof(WORD_TYPE) * nwords); + + /* mark any leftover bits at the end in use(0) */ + if (nbits != nwords * WORD_BITS) { + uint32_t ix = nwords - 1, overbits = nbits - ix * WORD_BITS; + + assert(nbits / WORD_BITS == ix); + assert(overbits > 0 && overbits < WORD_BITS); + + for (; overbits < WORD_BITS; overbits ++) { + bitmap->map[ix] ^= (1 << overbits); + } + } + return bitmap; +} + +// bitmap_alloc - locate a cleared bit, set it, and return its index. +int +bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store) { + WORD_TYPE *map = bitmap->map; + uint32_t ix, offset, nwords = bitmap->nwords; + for (ix = 0; ix < nwords; ix ++) { + if (map[ix] != 0) { + for (offset = 0; offset < WORD_BITS; offset ++) { + WORD_TYPE mask = (1 << offset); + if (map[ix] & mask) { + map[ix] ^= mask; + *index_store = ix * WORD_BITS + offset; + return 0; + } + } + assert(0); + } + } + return -E_NO_MEM; +} + +// bitmap_translate - according index, get the related word and mask +static void +bitmap_translate(struct bitmap *bitmap, uint32_t index, WORD_TYPE **word, WORD_TYPE *mask) { + assert(index < bitmap->nbits); + uint32_t ix = index / WORD_BITS, offset = index % WORD_BITS; + *word = bitmap->map + ix; + *mask = (1 << offset); +} + +// bitmap_test - according index, get the related value (0 OR 1) in the bitmap +bool +bitmap_test(struct bitmap *bitmap, uint32_t index) { + WORD_TYPE *word, mask; + bitmap_translate(bitmap, index, &word, &mask); + return (*word & mask); +} + +// bitmap_free - according index, set related bit to 1 +void +bitmap_free(struct bitmap *bitmap, uint32_t index) { + WORD_TYPE *word, mask; + bitmap_translate(bitmap, index, &word, &mask); + assert(!(*word & mask)); + *word |= mask; +} + +// bitmap_destroy - free memory contains bitmap +void +bitmap_destroy(struct bitmap *bitmap) { + kfree(bitmap->map); + kfree(bitmap); +} + +// bitmap_getdata - return bitmap->map, return the length of bits to len_store +void * +bitmap_getdata(struct bitmap *bitmap, size_t *len_store) { + if (len_store != NULL) { + *len_store = sizeof(WORD_TYPE) * bitmap->nwords; + } + return bitmap->map; +} + diff --git a/lab8_practice/kern/fs/sfs/bitmap.h b/lab8_practice/kern/fs/sfs/bitmap.h new file mode 100644 index 0000000..4a3a8eb --- /dev/null +++ b/lab8_practice/kern/fs/sfs/bitmap.h @@ -0,0 +1,32 @@ +#ifndef __KERN_FS_SFS_BITMAP_H__ +#define __KERN_FS_SFS_BITMAP_H__ + +#include + + +/* + * Fixed-size array of bits. (Intended for storage management.) + * + * Functions: + * bitmap_create - allocate a new bitmap object. + * Returns NULL on error. + * bitmap_getdata - return pointer to raw bit data (for I/O). + * bitmap_alloc - locate a cleared bit, set it, and return its index. + * bitmap_mark - set a clear bit by its index. + * bitmap_unmark - clear a set bit by its index. + * bitmap_isset - return whether a particular bit is set or not. + * bitmap_destroy - destroy bitmap. + */ + + +struct bitmap; + +struct bitmap *bitmap_create(uint32_t nbits); // allocate a new bitmap object. +int bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store); // locate a cleared bit, set it, and return its index. +bool bitmap_test(struct bitmap *bitmap, uint32_t index); // return whether a particular bit is set or not. +void bitmap_free(struct bitmap *bitmap, uint32_t index); // according index, set related bit to 1 +void bitmap_destroy(struct bitmap *bitmap); // free memory contains bitmap +void *bitmap_getdata(struct bitmap *bitmap, size_t *len_store); // return pointer to raw bit data (for I/O) + +#endif /* !__KERN_FS_SFS_BITMAP_H__ */ + diff --git a/lab8_practice/kern/fs/sfs/sfs.c b/lab8_practice/kern/fs/sfs/sfs.c new file mode 100644 index 0000000..c1d7d44 --- /dev/null +++ b/lab8_practice/kern/fs/sfs/sfs.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +/* + * sfs_init - mount sfs on disk0 + * + * CALL GRAPH: + * kern_init-->fs_init-->sfs_init + */ +void +sfs_init(void) { + int ret; + if ((ret = sfs_mount("disk0")) != 0) { + panic("failed: sfs: sfs_mount: %e.\n", ret); + } +} + diff --git a/lab8_practice/kern/fs/sfs/sfs.h b/lab8_practice/kern/fs/sfs/sfs.h new file mode 100644 index 0000000..39c1772 --- /dev/null +++ b/lab8_practice/kern/fs/sfs/sfs.h @@ -0,0 +1,129 @@ +#ifndef __KERN_FS_SFS_SFS_H__ +#define __KERN_FS_SFS_SFS_H__ + +#include +#include +#include +#include +#include + +/* + * Simple FS (SFS) definitions visible to ucore. This covers the on-disk format + * and is used by tools that work on SFS volumes, such as mksfs. + */ + +#define SFS_MAGIC 0x2f8dbe2a /* magic number for sfs */ +#define SFS_BLKSIZE PGSIZE /* size of block */ +#define SFS_NDIRECT 12 /* # of direct blocks in inode */ +#define SFS_MAX_INFO_LEN 31 /* max length of infomation */ +#define SFS_MAX_FNAME_LEN FS_MAX_FNAME_LEN /* max length of filename */ +#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) /* max file size (128M) */ +#define SFS_BLKN_SUPER 0 /* block the superblock lives in */ +#define SFS_BLKN_ROOT 1 /* location of the root dir inode */ +#define SFS_BLKN_FREEMAP 2 /* 1st block of the freemap */ + +/* # of bits in a block */ +#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT) + +/* # of entries in a block */ +#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t)) + +/* file types */ +#define SFS_TYPE_INVAL 0 /* Should not appear on disk */ +#define SFS_TYPE_FILE 1 +#define SFS_TYPE_DIR 2 +#define SFS_TYPE_LINK 3 + +/* + * On-disk superblock + */ +struct sfs_super { + uint32_t magic; /* magic number, should be SFS_MAGIC */ + uint32_t blocks; /* # of blocks in fs */ + uint32_t unused_blocks; /* # of unused blocks in fs */ + char info[SFS_MAX_INFO_LEN + 1]; /* infomation for sfs */ +}; + +/* inode (on disk) */ +struct sfs_disk_inode { + uint32_t size; /* size of the file (in bytes) */ + uint16_t type; /* one of SYS_TYPE_* above */ + uint16_t nlinks; /* # of hard links to this file */ + uint32_t blocks; /* # of blocks */ + uint32_t direct[SFS_NDIRECT]; /* direct blocks */ + uint32_t indirect; /* indirect blocks */ +// uint32_t db_indirect; /* double indirect blocks */ +// unused +}; + +/* file entry (on disk) */ +struct sfs_disk_entry { + uint32_t ino; /* inode number */ + char name[SFS_MAX_FNAME_LEN + 1]; /* file name */ +}; + +#define sfs_dentry_size \ + sizeof(((struct sfs_disk_entry *)0)->name) + +/* inode for sfs */ +struct sfs_inode { + struct sfs_disk_inode *din; /* on-disk inode */ + uint32_t ino; /* inode number */ + bool dirty; /* true if inode modified */ + int reclaim_count; /* kill inode if it hits zero */ + semaphore_t sem; /* semaphore for din */ + list_entry_t inode_link; /* entry for linked-list in sfs_fs */ + list_entry_t hash_link; /* entry for hash linked-list in sfs_fs */ +}; + +#define le2sin(le, member) \ + to_struct((le), struct sfs_inode, member) + +/* filesystem for sfs */ +struct sfs_fs { + struct sfs_super super; /* on-disk superblock */ + struct device *dev; /* device mounted on */ + struct bitmap *freemap; /* blocks in use are mared 0 */ + bool super_dirty; /* true if super/freemap modified */ + void *sfs_buffer; /* buffer for non-block aligned io */ + semaphore_t fs_sem; /* semaphore for fs */ + semaphore_t io_sem; /* semaphore for io */ + semaphore_t mutex_sem; /* semaphore for link/unlink and rename */ + list_entry_t inode_list; /* inode linked-list */ + list_entry_t *hash_list; /* inode hash linked-list */ +}; + +/* hash for sfs */ +#define SFS_HLIST_SHIFT 10 +#define SFS_HLIST_SIZE (1 << SFS_HLIST_SHIFT) +#define sin_hashfn(x) (hash32(x, SFS_HLIST_SHIFT)) + +/* size of freemap (in bits) */ +#define sfs_freemap_bits(super) ROUNDUP((super)->blocks, SFS_BLKBITS) + +/* size of freemap (in blocks) */ +#define sfs_freemap_blocks(super) ROUNDUP_DIV((super)->blocks, SFS_BLKBITS) + +struct fs; +struct inode; + +void sfs_init(void); +int sfs_mount(const char *devname); + +void lock_sfs_fs(struct sfs_fs *sfs); +void lock_sfs_io(struct sfs_fs *sfs); +void unlock_sfs_fs(struct sfs_fs *sfs); +void unlock_sfs_io(struct sfs_fs *sfs); + +int sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks); +int sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks); +int sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset); +int sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset); +int sfs_sync_super(struct sfs_fs *sfs); +int sfs_sync_freemap(struct sfs_fs *sfs); +int sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks); + +int sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino); + +#endif /* !__KERN_FS_SFS_SFS_H__ */ + diff --git a/lab8_practice/kern/fs/sfs/sfs_fs.c b/lab8_practice/kern/fs/sfs/sfs_fs.c new file mode 100644 index 0000000..2d3e3b3 --- /dev/null +++ b/lab8_practice/kern/fs/sfs/sfs_fs.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * sfs_sync - sync sfs's superblock and freemap in memroy into disk + */ +static int +sfs_sync(struct fs *fs) { + struct sfs_fs *sfs = fsop_info(fs, sfs); + lock_sfs_fs(sfs); + { + list_entry_t *list = &(sfs->inode_list), *le = list; + while ((le = list_next(le)) != list) { + struct sfs_inode *sin = le2sin(le, inode_link); + vop_fsync(info2node(sin, sfs_inode)); + } + } + unlock_sfs_fs(sfs); + + int ret; + if (sfs->super_dirty) { + sfs->super_dirty = 0; + if ((ret = sfs_sync_super(sfs)) != 0) { + sfs->super_dirty = 1; + return ret; + } + if ((ret = sfs_sync_freemap(sfs)) != 0) { + sfs->super_dirty = 1; + return ret; + } + } + return 0; +} + +/* + * sfs_get_root - get the root directory inode from disk (SFS_BLKN_ROOT,1) + */ +static struct inode * +sfs_get_root(struct fs *fs) { + struct inode *node; + int ret; + if ((ret = sfs_load_inode(fsop_info(fs, sfs), &node, SFS_BLKN_ROOT)) != 0) { + panic("load sfs root failed: %e", ret); + } + return node; +} + +/* + * sfs_unmount - unmount sfs, and free the memorys contain sfs->freemap/sfs_buffer/hash_liskt and sfs itself. + */ +static int +sfs_unmount(struct fs *fs) { + struct sfs_fs *sfs = fsop_info(fs, sfs); + if (!list_empty(&(sfs->inode_list))) { + return -E_BUSY; + } + assert(!sfs->super_dirty); + bitmap_destroy(sfs->freemap); + kfree(sfs->sfs_buffer); + kfree(sfs->hash_list); + kfree(sfs); + return 0; +} + +/* + * sfs_cleanup - when sfs failed, then should call this function to sync sfs by calling sfs_sync + * + * NOTICE: nouse now. + */ +static void +sfs_cleanup(struct fs *fs) { + struct sfs_fs *sfs = fsop_info(fs, sfs); + uint32_t blocks = sfs->super.blocks, unused_blocks = sfs->super.unused_blocks; + cprintf("sfs: cleanup: '%s' (%d/%d/%d)\n", sfs->super.info, + blocks - unused_blocks, unused_blocks, blocks); + int i, ret; + for (i = 0; i < 32; i ++) { + if ((ret = fsop_sync(fs)) == 0) { + break; + } + } + if (ret != 0) { + warn("sfs: sync error: '%s': %e.\n", sfs->super.info, ret); + } +} + +/* + * sfs_init_read - used in sfs_do_mount to read disk block(blkno, 1) directly. + * + * @dev: the block device + * @blkno: the NO. of disk block + * @blk_buffer: the buffer used for read + * + * (1) init iobuf + * (2) read dev into iobuf + */ +static int +sfs_init_read(struct device *dev, uint32_t blkno, void *blk_buffer) { + struct iobuf __iob, *iob = iobuf_init(&__iob, blk_buffer, SFS_BLKSIZE, blkno * SFS_BLKSIZE); + return dop_io(dev, iob, 0); +} + +/* + * sfs_init_freemap - used in sfs_do_mount to read freemap data info in disk block(blkno, nblks) directly. + * + * @dev: the block device + * @bitmap: the bitmap in memroy + * @blkno: the NO. of disk block + * @nblks: Rd number of disk block + * @blk_buffer: the buffer used for read + * + * (1) get data addr in bitmap + * (2) read dev into iobuf + */ +static int +sfs_init_freemap(struct device *dev, struct bitmap *freemap, uint32_t blkno, uint32_t nblks, void *blk_buffer) { + size_t len; + void *data = bitmap_getdata(freemap, &len); + assert(data != NULL && len == nblks * SFS_BLKSIZE); + while (nblks != 0) { + int ret; + if ((ret = sfs_init_read(dev, blkno, data)) != 0) { + return ret; + } + blkno ++, nblks --, data += SFS_BLKSIZE; + } + return 0; +} + +/* + * sfs_do_mount - mount sfs file system. + * + * @dev: the block device contains sfs file system + * @fs_store: the fs struct in memroy + */ +static int +sfs_do_mount(struct device *dev, struct fs **fs_store) { + static_assert(SFS_BLKSIZE >= sizeof(struct sfs_super)); + static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_inode)); + static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_entry)); + + if (dev->d_blocksize != SFS_BLKSIZE) { + return -E_NA_DEV; + } + + /* allocate fs structure */ + struct fs *fs; + if ((fs = alloc_fs(sfs)) == NULL) { + return -E_NO_MEM; + } + struct sfs_fs *sfs = fsop_info(fs, sfs); + sfs->dev = dev; + + int ret = -E_NO_MEM; + + void *sfs_buffer; + if ((sfs->sfs_buffer = sfs_buffer = kmalloc(SFS_BLKSIZE)) == NULL) { + goto failed_cleanup_fs; + } + + /* load and check superblock */ + if ((ret = sfs_init_read(dev, SFS_BLKN_SUPER, sfs_buffer)) != 0) { + goto failed_cleanup_sfs_buffer; + } + + ret = -E_INVAL; + + struct sfs_super *super = sfs_buffer; + if (super->magic != SFS_MAGIC) { + cprintf("sfs: wrong magic in superblock. (%08x should be %08x).\n", + super->magic, SFS_MAGIC); + goto failed_cleanup_sfs_buffer; + } + if (super->blocks > dev->d_blocks) { + cprintf("sfs: fs has %u blocks, device has %u blocks.\n", + super->blocks, dev->d_blocks); + goto failed_cleanup_sfs_buffer; + } + super->info[SFS_MAX_INFO_LEN] = '\0'; + sfs->super = *super; + + ret = -E_NO_MEM; + + uint32_t i; + + /* alloc and initialize hash list */ + list_entry_t *hash_list; + if ((sfs->hash_list = hash_list = kmalloc(sizeof(list_entry_t) * SFS_HLIST_SIZE)) == NULL) { + goto failed_cleanup_sfs_buffer; + } + for (i = 0; i < SFS_HLIST_SIZE; i ++) { + list_init(hash_list + i); + } + + /* load and check freemap */ + struct bitmap *freemap; + uint32_t freemap_size_nbits = sfs_freemap_bits(super); + if ((sfs->freemap = freemap = bitmap_create(freemap_size_nbits)) == NULL) { + goto failed_cleanup_hash_list; + } + uint32_t freemap_size_nblks = sfs_freemap_blocks(super); + if ((ret = sfs_init_freemap(dev, freemap, SFS_BLKN_FREEMAP, freemap_size_nblks, sfs_buffer)) != 0) { + goto failed_cleanup_freemap; + } + + uint32_t blocks = sfs->super.blocks, unused_blocks = 0; + for (i = 0; i < freemap_size_nbits; i ++) { + if (bitmap_test(freemap, i)) { + unused_blocks ++; + } + } + assert(unused_blocks == sfs->super.unused_blocks); + + /* and other fields */ + sfs->super_dirty = 0; + sem_init(&(sfs->fs_sem), 1); + sem_init(&(sfs->io_sem), 1); + sem_init(&(sfs->mutex_sem), 1); + list_init(&(sfs->inode_list)); + cprintf("sfs: mount: '%s' (%d/%d/%d)\n", sfs->super.info, + blocks - unused_blocks, unused_blocks, blocks); + + /* link addr of sync/get_root/unmount/cleanup funciton fs's function pointers*/ + fs->fs_sync = sfs_sync; + fs->fs_get_root = sfs_get_root; + fs->fs_unmount = sfs_unmount; + fs->fs_cleanup = sfs_cleanup; + *fs_store = fs; + return 0; + +failed_cleanup_freemap: + bitmap_destroy(freemap); +failed_cleanup_hash_list: + kfree(hash_list); +failed_cleanup_sfs_buffer: + kfree(sfs_buffer); +failed_cleanup_fs: + kfree(fs); + return ret; +} + +int +sfs_mount(const char *devname) { + return vfs_mount(devname, sfs_do_mount); +} + diff --git a/lab8_practice/kern/fs/sfs/sfs_inode.c b/lab8_practice/kern/fs/sfs/sfs_inode.c new file mode 100644 index 0000000..503bba8 --- /dev/null +++ b/lab8_practice/kern/fs/sfs/sfs_inode.c @@ -0,0 +1,988 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct inode_ops sfs_node_dirops; // dir operations +static const struct inode_ops sfs_node_fileops; // file operations + +/* + * lock_sin - lock the process of inode Rd/Wr + */ +static void +lock_sin(struct sfs_inode *sin) { + down(&(sin->sem)); +} + +/* + * unlock_sin - unlock the process of inode Rd/Wr + */ +static void +unlock_sin(struct sfs_inode *sin) { + up(&(sin->sem)); +} + +/* + * sfs_get_ops - return function addr of fs_node_dirops/sfs_node_fileops + */ +static const struct inode_ops * +sfs_get_ops(uint16_t type) { + switch (type) { + case SFS_TYPE_DIR: + return &sfs_node_dirops; + case SFS_TYPE_FILE: + return &sfs_node_fileops; + } + panic("invalid file type %d.\n", type); +} + +/* + * sfs_hash_list - return inode entry in sfs->hash_list + */ +static list_entry_t * +sfs_hash_list(struct sfs_fs *sfs, uint32_t ino) { + return sfs->hash_list + sin_hashfn(ino); +} + +/* + * sfs_set_links - link inode sin in sfs->linked-list AND sfs->hash_link + */ +static void +sfs_set_links(struct sfs_fs *sfs, struct sfs_inode *sin) { + list_add(&(sfs->inode_list), &(sin->inode_link)); + list_add(sfs_hash_list(sfs, sin->ino), &(sin->hash_link)); +} + +/* + * sfs_remove_links - unlink inode sin in sfs->linked-list AND sfs->hash_link + */ +static void +sfs_remove_links(struct sfs_inode *sin) { + list_del(&(sin->inode_link)); + list_del(&(sin->hash_link)); +} + +/* + * sfs_block_inuse - check the inode with NO. ino inuse info in bitmap + */ +static bool +sfs_block_inuse(struct sfs_fs *sfs, uint32_t ino) { + if (ino != 0 && ino < sfs->super.blocks) { + return !bitmap_test(sfs->freemap, ino); + } + panic("sfs_block_inuse: called out of range (0, %u) %u.\n", sfs->super.blocks, ino); +} + +/* + * sfs_block_alloc - check and get a free disk block + */ +static int +sfs_block_alloc(struct sfs_fs *sfs, uint32_t *ino_store) { + int ret; + if ((ret = bitmap_alloc(sfs->freemap, ino_store)) != 0) { + return ret; + } + assert(sfs->super.unused_blocks > 0); + sfs->super.unused_blocks --, sfs->super_dirty = 1; + assert(sfs_block_inuse(sfs, *ino_store)); + return sfs_clear_block(sfs, *ino_store, 1); +} + +/* + * sfs_block_free - set related bits for ino block to 1(means free) in bitmap, add sfs->super.unused_blocks, set superblock dirty * + */ +static void +sfs_block_free(struct sfs_fs *sfs, uint32_t ino) { + assert(sfs_block_inuse(sfs, ino)); + bitmap_free(sfs->freemap, ino); + sfs->super.unused_blocks ++, sfs->super_dirty = 1; +} + +/* + * sfs_create_inode - alloc a inode in memroy, and init din/ino/dirty/reclian_count/sem fields in sfs_inode in inode + */ +static int +sfs_create_inode(struct sfs_fs *sfs, struct sfs_disk_inode *din, uint32_t ino, struct inode **node_store) { + struct inode *node; + if ((node = alloc_inode(sfs_inode)) != NULL) { + vop_init(node, sfs_get_ops(din->type), info2fs(sfs, sfs)); + struct sfs_inode *sin = vop_info(node, sfs_inode); + sin->din = din, sin->ino = ino, sin->dirty = 0, sin->reclaim_count = 1; + sem_init(&(sin->sem), 1); + *node_store = node; + return 0; + } + return -E_NO_MEM; +} + +/* + * lookup_sfs_nolock - according ino, find related inode + * + * NOTICE: le2sin, info2node MACRO + */ +static struct inode * +lookup_sfs_nolock(struct sfs_fs *sfs, uint32_t ino) { + struct inode *node; + list_entry_t *list = sfs_hash_list(sfs, ino), *le = list; + while ((le = list_next(le)) != list) { + struct sfs_inode *sin = le2sin(le, hash_link); + if (sin->ino == ino) { + node = info2node(sin, sfs_inode); + if (vop_ref_inc(node) == 1) { + sin->reclaim_count ++; + } + return node; + } + } + return NULL; +} + +/* + * sfs_load_inode - If the inode isn't existed, load inode related ino disk block data into a new created inode. + * If the inode is in memory alreadily, then do nothing + */ +int +sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino) { + lock_sfs_fs(sfs); + struct inode *node; + if ((node = lookup_sfs_nolock(sfs, ino)) != NULL) { + goto out_unlock; + } + + int ret = -E_NO_MEM; + struct sfs_disk_inode *din; + if ((din = kmalloc(sizeof(struct sfs_disk_inode))) == NULL) { + goto failed_unlock; + } + + assert(sfs_block_inuse(sfs, ino)); + if ((ret = sfs_rbuf(sfs, din, sizeof(struct sfs_disk_inode), ino, 0)) != 0) { + goto failed_cleanup_din; + } + + assert(din->nlinks != 0); + if ((ret = sfs_create_inode(sfs, din, ino, &node)) != 0) { + goto failed_cleanup_din; + } + sfs_set_links(sfs, vop_info(node, sfs_inode)); + +out_unlock: + unlock_sfs_fs(sfs); + *node_store = node; + return 0; + +failed_cleanup_din: + kfree(din); +failed_unlock: + unlock_sfs_fs(sfs); + return ret; +} + +/* + * sfs_bmap_get_sub_nolock - according entry pointer entp and index, find the index of indrect disk block + * return the index of indrect disk block to ino_store. no lock protect + * @sfs: sfs file system + * @entp: the pointer of index of entry disk block + * @index: the index of block in indrect block + * @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing + * @ino_store: 0 OR the index of already inused block or new allocated block. + */ +static int +sfs_bmap_get_sub_nolock(struct sfs_fs *sfs, uint32_t *entp, uint32_t index, bool create, uint32_t *ino_store) { + assert(index < SFS_BLK_NENTRY); + int ret; + uint32_t ent, ino = 0; + off_t offset = index * sizeof(uint32_t); // the offset of entry in entry block + // if entry block is existd, read the content of entry block into sfs->sfs_buffer + if ((ent = *entp) != 0) { + if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) { + return ret; + } + if (ino != 0 || !create) { + goto out; + } + } + else { + if (!create) { + goto out; + } + //if entry block isn't existd, allocated a entry block (for indrect block) + if ((ret = sfs_block_alloc(sfs, &ent)) != 0) { + return ret; + } + } + + if ((ret = sfs_block_alloc(sfs, &ino)) != 0) { + goto failed_cleanup; + } + if ((ret = sfs_wbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) { + sfs_block_free(sfs, ino); + goto failed_cleanup; + } + +out: + if (ent != *entp) { + *entp = ent; + } + *ino_store = ino; + return 0; + +failed_cleanup: + if (ent != *entp) { + sfs_block_free(sfs, ent); + } + return ret; +} + +/* + * sfs_bmap_get_nolock - according sfs_inode and index of block, find the NO. of disk block + * no lock protect + * @sfs: sfs file system + * @sin: sfs inode in memory + * @index: the index of block in inode + * @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing + * @ino_store: 0 OR the index of already inused block or new allocated block. + */ +static int +sfs_bmap_get_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, bool create, uint32_t *ino_store) { + struct sfs_disk_inode *din = sin->din; + int ret; + uint32_t ent, ino; + // the index of disk block is in the fist SFS_NDIRECT direct blocks + if (index < SFS_NDIRECT) { + if ((ino = din->direct[index]) == 0 && create) { + if ((ret = sfs_block_alloc(sfs, &ino)) != 0) { + return ret; + } + din->direct[index] = ino; + sin->dirty = 1; + } + goto out; + } + // the index of disk block is in the indirect blocks. + index -= SFS_NDIRECT; + if (index < SFS_BLK_NENTRY) { + ent = din->indirect; + if ((ret = sfs_bmap_get_sub_nolock(sfs, &ent, index, create, &ino)) != 0) { + return ret; + } + if (ent != din->indirect) { + assert(din->indirect == 0); + din->indirect = ent; + sin->dirty = 1; + } + goto out; + } else { + panic ("sfs_bmap_get_nolock - index out of range"); + } +out: + assert(ino == 0 || sfs_block_inuse(sfs, ino)); + *ino_store = ino; + return 0; +} + +/* + * sfs_bmap_free_sub_nolock - set the entry item to 0 (free) in the indirect block + */ +static int +sfs_bmap_free_sub_nolock(struct sfs_fs *sfs, uint32_t ent, uint32_t index) { + assert(sfs_block_inuse(sfs, ent) && index < SFS_BLK_NENTRY); + int ret; + uint32_t ino, zero = 0; + off_t offset = index * sizeof(uint32_t); + if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) { + return ret; + } + if (ino != 0) { + if ((ret = sfs_wbuf(sfs, &zero, sizeof(uint32_t), ent, offset)) != 0) { + return ret; + } + sfs_block_free(sfs, ino); + } + return 0; +} + +/* + * sfs_bmap_free_nolock - free a block with logical index in inode and reset the inode's fields + */ +static int +sfs_bmap_free_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index) { + struct sfs_disk_inode *din = sin->din; + int ret; + uint32_t ent, ino; + if (index < SFS_NDIRECT) { + if ((ino = din->direct[index]) != 0) { + // free the block + sfs_block_free(sfs, ino); + din->direct[index] = 0; + sin->dirty = 1; + } + return 0; + } + + index -= SFS_NDIRECT; + if (index < SFS_BLK_NENTRY) { + if ((ent = din->indirect) != 0) { + // set the entry item to 0 in the indirect block + if ((ret = sfs_bmap_free_sub_nolock(sfs, ent, index)) != 0) { + return ret; + } + } + return 0; + } + return 0; +} + +/* + * sfs_bmap_load_nolock - according to the DIR's inode and the logical index of block in inode, find the NO. of disk block. + * @sfs: sfs file system + * @sin: sfs inode in memory + * @index: the logical index of disk block in inode + * @ino_store:the NO. of disk block + */ +static int +sfs_bmap_load_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, uint32_t *ino_store) { + struct sfs_disk_inode *din = sin->din; + assert(index <= din->blocks); + int ret; + uint32_t ino; + bool create = (index == din->blocks); + if ((ret = sfs_bmap_get_nolock(sfs, sin, index, create, &ino)) != 0) { + return ret; + } + assert(sfs_block_inuse(sfs, ino)); + if (create) { + din->blocks ++; + } + if (ino_store != NULL) { + *ino_store = ino; + } + return 0; +} + +/* + * sfs_bmap_truncate_nolock - free the disk block at the end of file + */ +static int +sfs_bmap_truncate_nolock(struct sfs_fs *sfs, struct sfs_inode *sin) { + struct sfs_disk_inode *din = sin->din; + assert(din->blocks != 0); + int ret; + if ((ret = sfs_bmap_free_nolock(sfs, sin, din->blocks - 1)) != 0) { + return ret; + } + din->blocks --; + sin->dirty = 1; + return 0; +} + +/* + * sfs_dirent_read_nolock - read the file entry from disk block which contains this entry + * @sfs: sfs file system + * @sin: sfs inode in memory + * @slot: the index of file entry + * @entry: file entry + */ +static int +sfs_dirent_read_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) { + assert(sin->din->type == SFS_TYPE_DIR && (slot >= 0 && slot < sin->din->blocks)); + int ret; + uint32_t ino; + // according to the DIR's inode and the slot of file entry, find the index of disk block which contains this file entry + if ((ret = sfs_bmap_load_nolock(sfs, sin, slot, &ino)) != 0) { + return ret; + } + assert(sfs_block_inuse(sfs, ino)); + // read the content of file entry in the disk block + if ((ret = sfs_rbuf(sfs, entry, sizeof(struct sfs_disk_entry), ino, 0)) != 0) { + return ret; + } + entry->name[SFS_MAX_FNAME_LEN] = '\0'; + return 0; +} + +#define sfs_dirent_link_nolock_check(sfs, sin, slot, lnksin, name) \ + do { \ + int err; \ + if ((err = sfs_dirent_link_nolock(sfs, sin, slot, lnksin, name)) != 0) { \ + warn("sfs_dirent_link error: %e.\n", err); \ + } \ + } while (0) + +#define sfs_dirent_unlink_nolock_check(sfs, sin, slot, lnksin) \ + do { \ + int err; \ + if ((err = sfs_dirent_unlink_nolock(sfs, sin, slot, lnksin)) != 0) { \ + warn("sfs_dirent_unlink error: %e.\n", err); \ + } \ + } while (0) + +/* + * sfs_dirent_search_nolock - read every file entry in the DIR, compare file name with each entry->name + * If equal, then return slot and NO. of disk of this file's inode + * @sfs: sfs file system + * @sin: sfs inode in memory + * @name: the filename + * @ino_store: NO. of disk of this file (with the filename)'s inode + * @slot: logical index of file entry (NOTICE: each file entry ocupied one disk block) + * @empty_slot: the empty logical index of file entry. + */ +static int +sfs_dirent_search_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, uint32_t *ino_store, int *slot, int *empty_slot) { + assert(strlen(name) <= SFS_MAX_FNAME_LEN); + struct sfs_disk_entry *entry; + if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) { + return -E_NO_MEM; + } + +#define set_pvalue(x, v) do { if ((x) != NULL) { *(x) = (v); } } while (0) + int ret, i, nslots = sin->din->blocks; + set_pvalue(empty_slot, nslots); + for (i = 0; i < nslots; i ++) { + if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) { + goto out; + } + if (entry->ino == 0) { + set_pvalue(empty_slot, i); + continue ; + } + if (strcmp(name, entry->name) == 0) { + set_pvalue(slot, i); + set_pvalue(ino_store, entry->ino); + goto out; + } + } +#undef set_pvalue + ret = -E_NOENT; +out: + kfree(entry); + return ret; +} + +/* + * sfs_dirent_findino_nolock - read all file entries in DIR's inode and find a entry->ino == ino + */ + +static int +sfs_dirent_findino_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t ino, struct sfs_disk_entry *entry) { + int ret, i, nslots = sin->din->blocks; + for (i = 0; i < nslots; i ++) { + if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) { + return ret; + } + if (entry->ino == ino) { + return 0; + } + } + return -E_NOENT; +} + +/* + * sfs_lookup_once - find inode corresponding the file name in DIR's sin inode + * @sfs: sfs file system + * @sin: DIR sfs inode in memory + * @name: the file name in DIR + * @node_store: the inode corresponding the file name in DIR + * @slot: the logical index of file entry + */ +static int +sfs_lookup_once(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, struct inode **node_store, int *slot) { + int ret; + uint32_t ino; + lock_sin(sin); + { // find the NO. of disk block and logical index of file entry + ret = sfs_dirent_search_nolock(sfs, sin, name, &ino, slot, NULL); + } + unlock_sin(sin); + if (ret == 0) { + // load the content of inode with the the NO. of disk block + ret = sfs_load_inode(sfs, node_store, ino); + } + return ret; +} + +// sfs_opendir - just check the opne_flags, now support readonly +static int +sfs_opendir(struct inode *node, uint32_t open_flags) { + switch (open_flags & O_ACCMODE) { + case O_RDONLY: + break; + case O_WRONLY: + case O_RDWR: + default: + return -E_ISDIR; + } + if (open_flags & O_APPEND) { + return -E_ISDIR; + } + return 0; +} + +// sfs_openfile - open file (no use) +static int +sfs_openfile(struct inode *node, uint32_t open_flags) { + return 0; +} + +// sfs_close - close file +static int +sfs_close(struct inode *node) { + return vop_fsync(node); +} + +/* + * sfs_io_nolock - Rd/Wr a file contentfrom offset position to offset+ length disk blocks<-->buffer (in memroy) + * @sfs: sfs file system + * @sin: sfs inode in memory + * @buf: the buffer Rd/Wr + * @offset: the offset of file + * @alenp: the length need to read (is a pointer). and will RETURN the really Rd/Wr lenght + * @write: BOOL, 0 read, 1 write + */ +static int +sfs_io_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, void *buf, off_t offset, size_t *alenp, bool write) { + struct sfs_disk_inode *din = sin->din; + assert(din->type != SFS_TYPE_DIR); + off_t endpos = offset + *alenp, blkoff; + *alenp = 0; + // calculate the Rd/Wr end position + if (offset < 0 || offset >= SFS_MAX_FILE_SIZE || offset > endpos) { + return -E_INVAL; + } + if (offset == endpos) { + return 0; + } + if (endpos > SFS_MAX_FILE_SIZE) { + endpos = SFS_MAX_FILE_SIZE; + } + if (!write) { + if (offset >= din->size) { + return 0; + } + if (endpos > din->size) { + endpos = din->size; + } + } + + int (*sfs_buf_op)(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset); + int (*sfs_block_op)(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks); + if (write) { + sfs_buf_op = sfs_wbuf, sfs_block_op = sfs_wblock; + } + else { + sfs_buf_op = sfs_rbuf, sfs_block_op = sfs_rblock; + } + + int ret = 0; + size_t size, alen = 0; + uint32_t ino; + uint32_t blkno = offset / SFS_BLKSIZE; // The NO. of Rd/Wr begin block + uint32_t nblks = endpos / SFS_BLKSIZE - blkno; // The size of Rd/Wr blocks + + //LAB8:EXERCISE1 YOUR CODE HINT: call sfs_bmap_load_nolock, sfs_rbuf, sfs_rblock,etc. read different kind of blocks in file + /* + * (1) If offset isn't aligned with the first block, Rd/Wr some content from offset to the end of the first block + * NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op + * Rd/Wr size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset) + * (2) Rd/Wr aligned blocks + * NOTICE: useful function: sfs_bmap_load_nolock, sfs_block_op + * (3) If end position isn't aligned with the last block, Rd/Wr some content from begin to the (endpos % SFS_BLKSIZE) of the last block + * NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op + */ + +out: + *alenp = alen; + if (offset + alen > sin->din->size) { + sin->din->size = offset + alen; + sin->dirty = 1; + } + return ret; +} + +/* + * sfs_io - Rd/Wr file. the wrapper of sfs_io_nolock + with lock protect + */ +static inline int +sfs_io(struct inode *node, struct iobuf *iob, bool write) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + int ret; + lock_sin(sin); + { + size_t alen = iob->io_resid; + ret = sfs_io_nolock(sfs, sin, iob->io_base, iob->io_offset, &alen, write); + if (alen != 0) { + iobuf_skip(iob, alen); + } + } + unlock_sin(sin); + return ret; +} + +// sfs_read - read file +static int +sfs_read(struct inode *node, struct iobuf *iob) { + return sfs_io(node, iob, 0); +} + +// sfs_write - write file +static int +sfs_write(struct inode *node, struct iobuf *iob) { + return sfs_io(node, iob, 1); +} + +/* + * sfs_fstat - Return nlinks/block/size, etc. info about a file. The pointer is a pointer to struct stat; + */ +static int +sfs_fstat(struct inode *node, struct stat *stat) { + int ret; + memset(stat, 0, sizeof(struct stat)); + if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) { + return ret; + } + struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din; + stat->st_nlinks = din->nlinks; + stat->st_blocks = din->blocks; + stat->st_size = din->size; + return 0; +} + +/* + * sfs_fsync - Force any dirty inode info associated with this file to stable storage. + */ +static int +sfs_fsync(struct inode *node) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + int ret = 0; + if (sin->dirty) { + lock_sin(sin); + { + if (sin->dirty) { + sin->dirty = 0; + if ((ret = sfs_wbuf(sfs, sin->din, sizeof(struct sfs_disk_inode), sin->ino, 0)) != 0) { + sin->dirty = 1; + } + } + } + unlock_sin(sin); + } + return ret; +} + +/* + *sfs_namefile -Compute pathname relative to filesystem root of the file and copy to the specified io buffer. + * + */ +static int +sfs_namefile(struct inode *node, struct iobuf *iob) { + struct sfs_disk_entry *entry; + if (iob->io_resid <= 2 || (entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) { + return -E_NO_MEM; + } + + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + + int ret; + char *ptr = iob->io_base + iob->io_resid; + size_t alen, resid = iob->io_resid - 2; + vop_ref_inc(node); + while (1) { + struct inode *parent; + if ((ret = sfs_lookup_once(sfs, sin, "..", &parent, NULL)) != 0) { + goto failed; + } + + uint32_t ino = sin->ino; + vop_ref_dec(node); + if (node == parent) { + vop_ref_dec(node); + break; + } + + node = parent, sin = vop_info(node, sfs_inode); + assert(ino != sin->ino && sin->din->type == SFS_TYPE_DIR); + + lock_sin(sin); + { + ret = sfs_dirent_findino_nolock(sfs, sin, ino, entry); + } + unlock_sin(sin); + + if (ret != 0) { + goto failed; + } + + if ((alen = strlen(entry->name) + 1) > resid) { + goto failed_nomem; + } + resid -= alen, ptr -= alen; + memcpy(ptr, entry->name, alen - 1); + ptr[alen - 1] = '/'; + } + alen = iob->io_resid - resid - 2; + ptr = memmove(iob->io_base + 1, ptr, alen); + ptr[-1] = '/', ptr[alen] = '\0'; + iobuf_skip(iob, alen); + kfree(entry); + return 0; + +failed_nomem: + ret = -E_NO_MEM; +failed: + vop_ref_dec(node); + kfree(entry); + return ret; +} + +/* + * sfs_getdirentry_sub_noblock - get the content of file entry in DIR + */ +static int +sfs_getdirentry_sub_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) { + int ret, i, nslots = sin->din->blocks; + for (i = 0; i < nslots; i ++) { + if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) { + return ret; + } + if (entry->ino != 0) { + if (slot == 0) { + return 0; + } + slot --; + } + } + return -E_NOENT; +} + +/* + * sfs_getdirentry - according to the iob->io_offset, calculate the dir entry's slot in disk block, + get dir entry content from the disk + */ +static int +sfs_getdirentry(struct inode *node, struct iobuf *iob) { + struct sfs_disk_entry *entry; + if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) { + return -E_NO_MEM; + } + + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + + int ret, slot; + off_t offset = iob->io_offset; + if (offset < 0 || offset % sfs_dentry_size != 0) { + kfree(entry); + return -E_INVAL; + } + if ((slot = offset / sfs_dentry_size) > sin->din->blocks) { + kfree(entry); + return -E_NOENT; + } + lock_sin(sin); + if ((ret = sfs_getdirentry_sub_nolock(sfs, sin, slot, entry)) != 0) { + unlock_sin(sin); + goto out; + } + unlock_sin(sin); + ret = iobuf_move(iob, entry->name, sfs_dentry_size, 1, NULL); +out: + kfree(entry); + return ret; +} + +/* + * sfs_reclaim - Free all resources inode occupied . Called when inode is no longer in use. + */ +static int +sfs_reclaim(struct inode *node) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + + int ret = -E_BUSY; + uint32_t ent; + lock_sfs_fs(sfs); + assert(sin->reclaim_count > 0); + if ((-- sin->reclaim_count) != 0 || inode_ref_count(node) != 0) { + goto failed_unlock; + } + if (sin->din->nlinks == 0) { + if ((ret = vop_truncate(node, 0)) != 0) { + goto failed_unlock; + } + } + if (sin->dirty) { + if ((ret = vop_fsync(node)) != 0) { + goto failed_unlock; + } + } + sfs_remove_links(sin); + unlock_sfs_fs(sfs); + + if (sin->din->nlinks == 0) { + sfs_block_free(sfs, sin->ino); + if ((ent = sin->din->indirect) != 0) { + sfs_block_free(sfs, ent); + } + } + kfree(sin->din); + vop_kill(node); + return 0; + +failed_unlock: + unlock_sfs_fs(sfs); + return ret; +} + +/* + * sfs_gettype - Return type of file. The values for file types are in sfs.h. + */ +static int +sfs_gettype(struct inode *node, uint32_t *type_store) { + struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din; + switch (din->type) { + case SFS_TYPE_DIR: + *type_store = S_IFDIR; + return 0; + case SFS_TYPE_FILE: + *type_store = S_IFREG; + return 0; + case SFS_TYPE_LINK: + *type_store = S_IFLNK; + return 0; + } + panic("invalid file type %d.\n", din->type); +} + +/* + * sfs_tryseek - Check if seeking to the specified position within the file is legal. + */ +static int +sfs_tryseek(struct inode *node, off_t pos) { + if (pos < 0 || pos >= SFS_MAX_FILE_SIZE) { + return -E_INVAL; + } + struct sfs_inode *sin = vop_info(node, sfs_inode); + if (pos > sin->din->size) { + return vop_truncate(node, pos); + } + return 0; +} + +/* + * sfs_truncfile : reszie the file with new length + */ +static int +sfs_truncfile(struct inode *node, off_t len) { + if (len < 0 || len > SFS_MAX_FILE_SIZE) { + return -E_INVAL; + } + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + struct sfs_inode *sin = vop_info(node, sfs_inode); + struct sfs_disk_inode *din = sin->din; + + int ret = 0; + //new number of disk blocks of file + uint32_t nblks, tblks = ROUNDUP_DIV(len, SFS_BLKSIZE); + if (din->size == len) { + assert(tblks == din->blocks); + return 0; + } + + lock_sin(sin); + // old number of disk blocks of file + nblks = din->blocks; + if (nblks < tblks) { + // try to enlarge the file size by add new disk block at the end of file + while (nblks != tblks) { + if ((ret = sfs_bmap_load_nolock(sfs, sin, nblks, NULL)) != 0) { + goto out_unlock; + } + nblks ++; + } + } + else if (tblks < nblks) { + // try to reduce the file size + while (tblks != nblks) { + if ((ret = sfs_bmap_truncate_nolock(sfs, sin)) != 0) { + goto out_unlock; + } + nblks --; + } + } + assert(din->blocks == tblks); + din->size = len; + sin->dirty = 1; + +out_unlock: + unlock_sin(sin); + return ret; +} + +/* + * sfs_lookup - Parse path relative to the passed directory + * DIR, and hand back the inode for the file it + * refers to. + */ +static int +sfs_lookup(struct inode *node, char *path, struct inode **node_store) { + struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs); + assert(*path != '\0' && *path != '/'); + vop_ref_inc(node); + struct sfs_inode *sin = vop_info(node, sfs_inode); + if (sin->din->type != SFS_TYPE_DIR) { + vop_ref_dec(node); + return -E_NOTDIR; + } + struct inode *subnode; + int ret = sfs_lookup_once(sfs, sin, path, &subnode, NULL); + + vop_ref_dec(node); + if (ret != 0) { + return ret; + } + *node_store = subnode; + return 0; +} + +// The sfs specific DIR operations correspond to the abstract operations on a inode. +static const struct inode_ops sfs_node_dirops = { + .vop_magic = VOP_MAGIC, + .vop_open = sfs_opendir, + .vop_close = sfs_close, + .vop_fstat = sfs_fstat, + .vop_fsync = sfs_fsync, + .vop_namefile = sfs_namefile, + .vop_getdirentry = sfs_getdirentry, + .vop_reclaim = sfs_reclaim, + .vop_gettype = sfs_gettype, + .vop_lookup = sfs_lookup, +}; +/// The sfs specific FILE operations correspond to the abstract operations on a inode. +static const struct inode_ops sfs_node_fileops = { + .vop_magic = VOP_MAGIC, + .vop_open = sfs_openfile, + .vop_close = sfs_close, + .vop_read = sfs_read, + .vop_write = sfs_write, + .vop_fstat = sfs_fstat, + .vop_fsync = sfs_fsync, + .vop_reclaim = sfs_reclaim, + .vop_gettype = sfs_gettype, + .vop_tryseek = sfs_tryseek, + .vop_truncate = sfs_truncfile, +}; + diff --git a/lab8_practice/kern/fs/sfs/sfs_io.c b/lab8_practice/kern/fs/sfs/sfs_io.c new file mode 100644 index 0000000..f3c705f --- /dev/null +++ b/lab8_practice/kern/fs/sfs/sfs_io.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +//Basic block-level I/O routines + +/* sfs_rwblock_nolock - Basic block-level I/O routine for Rd/Wr one disk block, + * without lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @write: BOOL: Read or Write + * @check: BOOL: if check (blono < sfs super.blocks) + */ +static int +sfs_rwblock_nolock(struct sfs_fs *sfs, void *buf, uint32_t blkno, bool write, bool check) { + assert((blkno != 0 || !check) && blkno < sfs->super.blocks); + struct iobuf __iob, *iob = iobuf_init(&__iob, buf, SFS_BLKSIZE, blkno * SFS_BLKSIZE); + return dop_io(sfs->dev, iob, write); +} + +/* sfs_rwblock - Basic block-level I/O routine for Rd/Wr N disk blocks , + * with lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + * @write: BOOL: Read - 0 or Write - 1 + */ +static int +sfs_rwblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks, bool write) { + int ret = 0; + lock_sfs_io(sfs); + { + while (nblks != 0) { + if ((ret = sfs_rwblock_nolock(sfs, buf, blkno, write, 1)) != 0) { + break; + } + blkno ++, nblks --; + buf += SFS_BLKSIZE; + } + } + unlock_sfs_io(sfs); + return ret; +} + +/* sfs_rblock - The Wrap of sfs_rwblock function for Rd N disk blocks , + * + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + */ +int +sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) { + return sfs_rwblock(sfs, buf, blkno, nblks, 0); +} + +/* sfs_wblock - The Wrap of sfs_rwblock function for Wr N disk blocks , + * + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd/Wr + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + */ +int +sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) { + return sfs_rwblock(sfs, buf, blkno, nblks, 1); +} + +/* sfs_rbuf - The Basic block-level I/O routine for Rd( non-block & non-aligned io) one disk block(using sfs->sfs_buffer) + * with lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Rd + * @len: the length need to Rd + * @blkno: the NO. of disk block + * @offset: the offset in the content of disk block + */ +int +sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) { + assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE); + int ret; + lock_sfs_io(sfs); + { + if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) { + memcpy(buf, sfs->sfs_buffer + offset, len); + } + } + unlock_sfs_io(sfs); + return ret; +} + +/* sfs_wbuf - The Basic block-level I/O routine for Wr( non-block & non-aligned io) one disk block(using sfs->sfs_buffer) + * with lock protect for mutex process on Rd/Wr disk block + * @sfs: sfs_fs which will be process + * @buf: the buffer uesed for Wr + * @len: the length need to Wr + * @blkno: the NO. of disk block + * @offset: the offset in the content of disk block + */ +int +sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) { + assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE); + int ret; + lock_sfs_io(sfs); + { + if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) { + memcpy(sfs->sfs_buffer + offset, buf, len); + ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1); + } + } + unlock_sfs_io(sfs); + return ret; +} + +/* + * sfs_sync_super - write sfs->super (in memory) into disk (SFS_BLKN_SUPER, 1) with lock protect. + */ +int +sfs_sync_super(struct sfs_fs *sfs) { + int ret; + lock_sfs_io(sfs); + { + memset(sfs->sfs_buffer, 0, SFS_BLKSIZE); + memcpy(sfs->sfs_buffer, &(sfs->super), sizeof(sfs->super)); + ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, SFS_BLKN_SUPER, 1, 0); + } + unlock_sfs_io(sfs); + return ret; +} + +/* + * sfs_sync_freemap - write sfs bitmap into disk (SFS_BLKN_FREEMAP, nblks) without lock protect. + */ +int +sfs_sync_freemap(struct sfs_fs *sfs) { + uint32_t nblks = sfs_freemap_blocks(&(sfs->super)); + return sfs_wblock(sfs, bitmap_getdata(sfs->freemap, NULL), SFS_BLKN_FREEMAP, nblks); +} + +/* + * sfs_clear_block - write zero info into disk (blkno, nblks) with lock protect. + * @sfs: sfs_fs which will be process + * @blkno: the NO. of disk block + * @nblks: Rd/Wr number of disk block + */ +int +sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks) { + int ret; + lock_sfs_io(sfs); + { + memset(sfs->sfs_buffer, 0, SFS_BLKSIZE); + while (nblks != 0) { + if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1)) != 0) { + break; + } + blkno ++, nblks --; + } + } + unlock_sfs_io(sfs); + return ret; +} + diff --git a/lab8_practice/kern/fs/sfs/sfs_lock.c b/lab8_practice/kern/fs/sfs/sfs_lock.c new file mode 100644 index 0000000..6d83ecd --- /dev/null +++ b/lab8_practice/kern/fs/sfs/sfs_lock.c @@ -0,0 +1,44 @@ +#include +#include +#include + + +/* + * lock_sfs_fs - lock the process of SFS Filesystem Rd/Wr Disk Block + * + * called by: sfs_load_inode, sfs_sync, sfs_reclaim + */ +void +lock_sfs_fs(struct sfs_fs *sfs) { + down(&(sfs->fs_sem)); +} + +/* + * lock_sfs_io - lock the process of SFS File Rd/Wr Disk Block + * + * called by: sfs_rwblock, sfs_clear_block, sfs_sync_super + */ +void +lock_sfs_io(struct sfs_fs *sfs) { + down(&(sfs->io_sem)); +} + +/* + * unlock_sfs_fs - unlock the process of SFS Filesystem Rd/Wr Disk Block + * + * called by: sfs_load_inode, sfs_sync, sfs_reclaim + */ +void +unlock_sfs_fs(struct sfs_fs *sfs) { + up(&(sfs->fs_sem)); +} + +/* + * unlock_sfs_io - unlock the process of sfs Rd/Wr Disk Block + * + * called by: sfs_rwblock sfs_clear_block sfs_sync_super + */ +void +unlock_sfs_io(struct sfs_fs *sfs) { + up(&(sfs->io_sem)); +} diff --git a/lab8_practice/kern/fs/swap/swapfs.c b/lab8_practice/kern/fs/swap/swapfs.c new file mode 100644 index 0000000..d9f6090 --- /dev/null +++ b/lab8_practice/kern/fs/swap/swapfs.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include +#include + +void +swapfs_init(void) { + static_assert((PGSIZE % SECTSIZE) == 0); + if (!ide_device_valid(SWAP_DEV_NO)) { + panic("swap fs isn't available.\n"); + } + max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE); +} + +int +swapfs_read(swap_entry_t entry, struct Page *page) { + return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT); +} + +int +swapfs_write(swap_entry_t entry, struct Page *page) { + return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT); +} + diff --git a/lab8_practice/kern/fs/swap/swapfs.h b/lab8_practice/kern/fs/swap/swapfs.h new file mode 100644 index 0000000..904fb2e --- /dev/null +++ b/lab8_practice/kern/fs/swap/swapfs.h @@ -0,0 +1,12 @@ +#ifndef __KERN_FS_SWAP_SWAPFS_H__ +#define __KERN_FS_SWAP_SWAPFS_H__ + +#include +#include + +void swapfs_init(void); +int swapfs_read(swap_entry_t entry, struct Page *page); +int swapfs_write(swap_entry_t entry, struct Page *page); + +#endif /* !__KERN_FS_SWAP_SWAPFS_H__ */ + diff --git a/lab8_practice/kern/fs/sysfile.c b/lab8_practice/kern/fs/sysfile.c new file mode 100644 index 0000000..0dd8e58 --- /dev/null +++ b/lab8_practice/kern/fs/sysfile.c @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IOBUF_SIZE 4096 + +/* copy_path - copy path name */ +static int +copy_path(char **to, const char *from) { + struct mm_struct *mm = current->mm; + char *buffer; + if ((buffer = kmalloc(FS_MAX_FPATH_LEN + 1)) == NULL) { + return -E_NO_MEM; + } + lock_mm(mm); + if (!copy_string(mm, buffer, from, FS_MAX_FPATH_LEN + 1)) { + unlock_mm(mm); + goto failed_cleanup; + } + unlock_mm(mm); + *to = buffer; + return 0; + +failed_cleanup: + kfree(buffer); + return -E_INVAL; +} + +/* sysfile_open - open file */ +int +sysfile_open(const char *__path, uint32_t open_flags) { + int ret; + char *path; + if ((ret = copy_path(&path, __path)) != 0) { + return ret; + } + ret = file_open(path, open_flags); + kfree(path); + return ret; +} + +/* sysfile_close - close file */ +int +sysfile_close(int fd) { + return file_close(fd); +} + +/* sysfile_read - read file */ +int +sysfile_read(int fd, void *base, size_t len) { + struct mm_struct *mm = current->mm; + if (len == 0) { + return 0; + } + if (!file_testfd(fd, 1, 0)) { + return -E_INVAL; + } + void *buffer; + if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { + return -E_NO_MEM; + } + + int ret = 0; + size_t copied = 0, alen; + while (len != 0) { + if ((alen = IOBUF_SIZE) > len) { + alen = len; + } + ret = file_read(fd, buffer, alen, &alen); + if (alen != 0) { + lock_mm(mm); + { + if (copy_to_user(mm, base, buffer, alen)) { + assert(len >= alen); + base += alen, len -= alen, copied += alen; + } + else if (ret == 0) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + } + if (ret != 0 || alen == 0) { + goto out; + } + } + +out: + kfree(buffer); + if (copied != 0) { + return copied; + } + return ret; +} + +/* sysfile_write - write file */ +int +sysfile_write(int fd, void *base, size_t len) { + struct mm_struct *mm = current->mm; + if (len == 0) { + return 0; + } + if (!file_testfd(fd, 0, 1)) { + return -E_INVAL; + } + void *buffer; + if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { + return -E_NO_MEM; + } + + int ret = 0; + size_t copied = 0, alen; + while (len != 0) { + if ((alen = IOBUF_SIZE) > len) { + alen = len; + } + lock_mm(mm); + { + if (!copy_from_user(mm, buffer, base, alen, 0)) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + if (ret == 0) { + ret = file_write(fd, buffer, alen, &alen); + if (alen != 0) { + assert(len >= alen); + base += alen, len -= alen, copied += alen; + } + } + if (ret != 0 || alen == 0) { + goto out; + } + } + +out: + kfree(buffer); + if (copied != 0) { + return copied; + } + return ret; +} + +/* sysfile_seek - seek file */ +int +sysfile_seek(int fd, off_t pos, int whence) { + return file_seek(fd, pos, whence); +} + +/* sysfile_fstat - stat file */ +int +sysfile_fstat(int fd, struct stat *__stat) { + struct mm_struct *mm = current->mm; + int ret; + struct stat __local_stat, *stat = &__local_stat; + if ((ret = file_fstat(fd, stat)) != 0) { + return ret; + } + + lock_mm(mm); + { + if (!copy_to_user(mm, __stat, stat, sizeof(struct stat))) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + return ret; +} + +/* sysfile_fsync - sync file */ +int +sysfile_fsync(int fd) { + return file_fsync(fd); +} + +/* sysfile_chdir - change dir */ +int +sysfile_chdir(const char *__path) { + int ret; + char *path; + if ((ret = copy_path(&path, __path)) != 0) { + return ret; + } + ret = vfs_chdir(path); + kfree(path); + return ret; +} + +/* sysfile_link - link file */ +int +sysfile_link(const char *__path1, const char *__path2) { + int ret; + char *old_path, *new_path; + if ((ret = copy_path(&old_path, __path1)) != 0) { + return ret; + } + if ((ret = copy_path(&new_path, __path2)) != 0) { + kfree(old_path); + return ret; + } + ret = vfs_link(old_path, new_path); + kfree(old_path), kfree(new_path); + return ret; +} + +/* sysfile_rename - rename file */ +int +sysfile_rename(const char *__path1, const char *__path2) { + int ret; + char *old_path, *new_path; + if ((ret = copy_path(&old_path, __path1)) != 0) { + return ret; + } + if ((ret = copy_path(&new_path, __path2)) != 0) { + kfree(old_path); + return ret; + } + ret = vfs_rename(old_path, new_path); + kfree(old_path), kfree(new_path); + return ret; +} + +/* sysfile_unlink - unlink file */ +int +sysfile_unlink(const char *__path) { + int ret; + char *path; + if ((ret = copy_path(&path, __path)) != 0) { + return ret; + } + ret = vfs_unlink(path); + kfree(path); + return ret; +} + +/* sysfile_get cwd - get current working directory */ +int +sysfile_getcwd(char *buf, size_t len) { + struct mm_struct *mm = current->mm; + if (len == 0) { + return -E_INVAL; + } + + int ret = -E_INVAL; + lock_mm(mm); + { + if (user_mem_check(mm, (uintptr_t)buf, len, 1)) { + struct iobuf __iob, *iob = iobuf_init(&__iob, buf, len, 0); + ret = vfs_getcwd(iob); + } + } + unlock_mm(mm); + return ret; +} + +/* sysfile_getdirentry - get the file entry in DIR */ +int +sysfile_getdirentry(int fd, struct dirent *__direntp) { + struct mm_struct *mm = current->mm; + struct dirent *direntp; + if ((direntp = kmalloc(sizeof(struct dirent))) == NULL) { + return -E_NO_MEM; + } + + int ret = 0; + lock_mm(mm); + { + if (!copy_from_user(mm, &(direntp->offset), &(__direntp->offset), sizeof(direntp->offset), 1)) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + + if (ret != 0 || (ret = file_getdirentry(fd, direntp)) != 0) { + goto out; + } + + lock_mm(mm); + { + if (!copy_to_user(mm, __direntp, direntp, sizeof(struct dirent))) { + ret = -E_INVAL; + } + } + unlock_mm(mm); + +out: + kfree(direntp); + return ret; +} + +/* sysfile_dup - duplicate fd1 to fd2 */ +int +sysfile_dup(int fd1, int fd2) { + return file_dup(fd1, fd2); +} + +int +sysfile_pipe(int *fd_store) { + return -E_UNIMP; +} + +int +sysfile_mkfifo(const char *__name, uint32_t open_flags) { + return -E_UNIMP; +} + diff --git a/lab8_practice/kern/fs/sysfile.h b/lab8_practice/kern/fs/sysfile.h new file mode 100644 index 0000000..bc060e9 --- /dev/null +++ b/lab8_practice/kern/fs/sysfile.h @@ -0,0 +1,28 @@ +#ifndef __KERN_FS_SYSFILE_H__ +#define __KERN_FS_SYSFILE_H__ + +#include + +struct stat; +struct dirent; + +int sysfile_open(const char *path, uint32_t open_flags); // Open or create a file. FLAGS/MODE per the syscall. +int sysfile_close(int fd); // Close a vnode opened +int sysfile_read(int fd, void *base, size_t len); // Read file +int sysfile_write(int fd, void *base, size_t len); // Write file +int sysfile_seek(int fd, off_t pos, int whence); // Seek file +int sysfile_fstat(int fd, struct stat *stat); // Stat file +int sysfile_fsync(int fd); // Sync file +int sysfile_chdir(const char *path); // change DIR +int sysfile_mkdir(const char *path); // create DIR +int sysfile_link(const char *path1, const char *path2); // set a path1's link as path2 +int sysfile_rename(const char *path1, const char *path2); // rename file +int sysfile_unlink(const char *path); // unlink a path +int sysfile_getcwd(char *buf, size_t len); // get current working directory +int sysfile_getdirentry(int fd, struct dirent *direntp); // get the file entry in DIR +int sysfile_dup(int fd1, int fd2); // duplicate file +int sysfile_pipe(int *fd_store); // build PIPE +int sysfile_mkfifo(const char *name, uint32_t open_flags); // build named PIPE + +#endif /* !__KERN_FS_SYSFILE_H__ */ + diff --git a/lab8_practice/kern/fs/vfs/inode.c b/lab8_practice/kern/fs/vfs/inode.c new file mode 100644 index 0000000..ffc873b --- /dev/null +++ b/lab8_practice/kern/fs/vfs/inode.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* * + * __alloc_inode - alloc a inode structure and initialize in_type + * */ +struct inode * +__alloc_inode(int type) { + struct inode *node; + if ((node = kmalloc(sizeof(struct inode))) != NULL) { + node->in_type = type; + } + return node; +} + +/* * + * inode_init - initialize a inode structure + * invoked by vop_init + * */ +void +inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs) { + node->ref_count = 0; + node->open_count = 0; + node->in_ops = ops, node->in_fs = fs; + vop_ref_inc(node); +} + +/* * + * inode_kill - kill a inode structure + * invoked by vop_kill + * */ +void +inode_kill(struct inode *node) { + assert(inode_ref_count(node) == 0); + assert(inode_open_count(node) == 0); + kfree(node); +} + +/* * + * inode_ref_inc - increment ref_count + * invoked by vop_ref_inc + * */ +int +inode_ref_inc(struct inode *node) { + node->ref_count += 1; + return node->ref_count; +} + +/* * + * inode_ref_dec - decrement ref_count + * invoked by vop_ref_dec + * calls vop_reclaim if the ref_count hits zero + * */ +int +inode_ref_dec(struct inode *node) { + assert(inode_ref_count(node) > 0); + int ref_count, ret; + node->ref_count-= 1; + ref_count = node->ref_count; + if (ref_count == 0) { + if ((ret = vop_reclaim(node)) != 0 && ret != -E_BUSY) { + cprintf("vfs: warning: vop_reclaim: %e.\n", ret); + } + } + return ref_count; +} + +/* * + * inode_open_inc - increment the open_count + * invoked by vop_open_inc + * */ +int +inode_open_inc(struct inode *node) { + node->open_count += 1; + return node->open_count; +} + +/* * + * inode_open_dec - decrement the open_count + * invoked by vop_open_dec + * calls vop_close if the open_count hits zero + * */ +int +inode_open_dec(struct inode *node) { + assert(inode_open_count(node) > 0); + int open_count, ret; + node->open_count -= 1; + open_count = node->open_count; + if (open_count == 0) { + if ((ret = vop_close(node)) != 0) { + cprintf("vfs: warning: vop_close: %e.\n", ret); + } + } + return open_count; +} + +/* * + * inode_check - check the various things being valid + * called before all vop_* calls + * */ +void +inode_check(struct inode *node, const char *opstr) { + assert(node != NULL && node->in_ops != NULL); + assert(node->in_ops->vop_magic == VOP_MAGIC); + int ref_count = inode_ref_count(node), open_count = inode_open_count(node); + assert(ref_count >= open_count && open_count >= 0); + assert(ref_count < MAX_INODE_COUNT && open_count < MAX_INODE_COUNT); +} + diff --git a/lab8_practice/kern/fs/vfs/inode.h b/lab8_practice/kern/fs/vfs/inode.h new file mode 100644 index 0000000..edc7efa --- /dev/null +++ b/lab8_practice/kern/fs/vfs/inode.h @@ -0,0 +1,248 @@ +#ifndef __KERN_FS_VFS_INODE_H__ +#define __KERN_FS_VFS_INODE_H__ + +#include +#include +#include +#include +#include + +struct stat; +struct iobuf; + +/* + * A struct inode is an abstract representation of a file. + * + * It is an interface that allows the kernel's filesystem-independent + * code to interact usefully with multiple sets of filesystem code. + */ + +/* + * Abstract low-level file. + * + * Note: in_info is Filesystem-specific data, in_type is the inode type + * + * open_count is managed using VOP_INCOPEN and VOP_DECOPEN by + * vfs_open() and vfs_close(). Code above the VFS layer should not + * need to worry about it. + */ +struct inode { + union { + struct device __device_info; + struct sfs_inode __sfs_inode_info; + } in_info; + enum { + inode_type_device_info = 0x1234, + inode_type_sfs_inode_info, + } in_type; + int ref_count; + int open_count; + struct fs *in_fs; + const struct inode_ops *in_ops; +}; + +#define __in_type(type) inode_type_##type##_info + +#define check_inode_type(node, type) ((node)->in_type == __in_type(type)) + +#define __vop_info(node, type) \ + ({ \ + struct inode *__node = (node); \ + assert(__node != NULL && check_inode_type(__node, type)); \ + &(__node->in_info.__##type##_info); \ + }) + +#define vop_info(node, type) __vop_info(node, type) + +#define info2node(info, type) \ + to_struct((info), struct inode, in_info.__##type##_info) + +struct inode *__alloc_inode(int type); + +#define alloc_inode(type) __alloc_inode(__in_type(type)) + +#define MAX_INODE_COUNT 0x10000 + +int inode_ref_inc(struct inode *node); +int inode_ref_dec(struct inode *node); +int inode_open_inc(struct inode *node); +int inode_open_dec(struct inode *node); + +void inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs); +void inode_kill(struct inode *node); + +#define VOP_MAGIC 0x8c4ba476 + +/* + * Abstract operations on a inode. + * + * These are used in the form VOP_FOO(inode, args), which are macros + * that expands to inode->inode_ops->vop_foo(inode, args). The operations + * "foo" are: + * + * vop_open - Called on open() of a file. Can be used to + * reject illegal or undesired open modes. Note that + * various operations can be performed without the + * file actually being opened. + * The inode need not look at O_CREAT, O_EXCL, or + * O_TRUNC, as these are handled in the VFS layer. + * + * VOP_EACHOPEN should not be called directly from + * above the VFS layer - use vfs_open() to open inodes. + * This maintains the open count so VOP_LASTCLOSE can + * be called at the right time. + * + * vop_close - To be called on *last* close() of a file. + * + * VOP_LASTCLOSE should not be called directly from + * above the VFS layer - use vfs_close() to close + * inodes opened with vfs_open(). + * + * vop_reclaim - Called when inode is no longer in use. Note that + * this may be substantially after vop_lastclose is + * called. + * + ***************************************** + * + * vop_read - Read data from file to uio, at offset specified + * in the uio, updating uio_resid to reflect the + * amount read, and updating uio_offset to match. + * Not allowed on directories or symlinks. + * + * vop_getdirentry - Read a single filename from a directory into a + * uio, choosing what name based on the offset + * field in the uio, and updating that field. + * Unlike with I/O on regular files, the value of + * the offset field is not interpreted outside + * the filesystem and thus need not be a byte + * count. However, the uio_resid field should be + * handled in the normal fashion. + * On non-directory objects, return ENOTDIR. + * + * vop_write - Write data from uio to file at offset specified + * in the uio, updating uio_resid to reflect the + * amount written, and updating uio_offset to match. + * Not allowed on directories or symlinks. + * + * vop_ioctl - Perform ioctl operation OP on file using data + * DATA. The interpretation of the data is specific + * to each ioctl. + * + * vop_fstat -Return info about a file. The pointer is a + * pointer to struct stat; see stat.h. + * + * vop_gettype - Return type of file. The values for file types + * are in sfs.h. + * + * vop_tryseek - Check if seeking to the specified position within + * the file is legal. (For instance, all seeks + * are illegal on serial port devices, and seeks + * past EOF on files whose sizes are fixed may be + * as well.) + * + * vop_fsync - Force any dirty buffers associated with this file + * to stable storage. + * + * vop_truncate - Forcibly set size of file to the length passed + * in, discarding any excess blocks. + * + * vop_namefile - Compute pathname relative to filesystem root + * of the file and copy to the specified io buffer. + * Need not work on objects that are not + * directories. + * + ***************************************** + * + * vop_creat - Create a regular file named NAME in the passed + * directory DIR. If boolean EXCL is true, fail if + * the file already exists; otherwise, use the + * existing file if there is one. Hand back the + * inode for the file as per vop_lookup. + * + ***************************************** + * + * vop_lookup - Parse PATHNAME relative to the passed directory + * DIR, and hand back the inode for the file it + * refers to. May destroy PATHNAME. Should increment + * refcount on inode handed back. + */ +struct inode_ops { + unsigned long vop_magic; + int (*vop_open)(struct inode *node, uint32_t open_flags); + int (*vop_close)(struct inode *node); + int (*vop_read)(struct inode *node, struct iobuf *iob); + int (*vop_write)(struct inode *node, struct iobuf *iob); + int (*vop_fstat)(struct inode *node, struct stat *stat); + int (*vop_fsync)(struct inode *node); + int (*vop_namefile)(struct inode *node, struct iobuf *iob); + int (*vop_getdirentry)(struct inode *node, struct iobuf *iob); + int (*vop_reclaim)(struct inode *node); + int (*vop_gettype)(struct inode *node, uint32_t *type_store); + int (*vop_tryseek)(struct inode *node, off_t pos); + int (*vop_truncate)(struct inode *node, off_t len); + int (*vop_create)(struct inode *node, const char *name, bool excl, struct inode **node_store); + int (*vop_lookup)(struct inode *node, char *path, struct inode **node_store); + int (*vop_ioctl)(struct inode *node, int op, void *data); +}; + +/* + * Consistency check + */ +void inode_check(struct inode *node, const char *opstr); + +#define __vop_op(node, sym) \ + ({ \ + struct inode *__node = (node); \ + assert(__node != NULL && __node->in_ops != NULL && __node->in_ops->vop_##sym != NULL); \ + inode_check(__node, #sym); \ + __node->in_ops->vop_##sym; \ + }) + +#define vop_open(node, open_flags) (__vop_op(node, open)(node, open_flags)) +#define vop_close(node) (__vop_op(node, close)(node)) +#define vop_read(node, iob) (__vop_op(node, read)(node, iob)) +#define vop_write(node, iob) (__vop_op(node, write)(node, iob)) +#define vop_fstat(node, stat) (__vop_op(node, fstat)(node, stat)) +#define vop_fsync(node) (__vop_op(node, fsync)(node)) +#define vop_namefile(node, iob) (__vop_op(node, namefile)(node, iob)) +#define vop_getdirentry(node, iob) (__vop_op(node, getdirentry)(node, iob)) +#define vop_reclaim(node) (__vop_op(node, reclaim)(node)) +#define vop_ioctl(node, op, data) (__vop_op(node, ioctl)(node, op, data)) +#define vop_gettype(node, type_store) (__vop_op(node, gettype)(node, type_store)) +#define vop_tryseek(node, pos) (__vop_op(node, tryseek)(node, pos)) +#define vop_truncate(node, len) (__vop_op(node, truncate)(node, len)) +#define vop_create(node, name, excl, node_store) (__vop_op(node, create)(node, name, excl, node_store)) +#define vop_lookup(node, path, node_store) (__vop_op(node, lookup)(node, path, node_store)) + + +#define vop_fs(node) ((node)->in_fs) +#define vop_init(node, ops, fs) inode_init(node, ops, fs) +#define vop_kill(node) inode_kill(node) + +/* + * Reference count manipulation (handled above filesystem level) + */ +#define vop_ref_inc(node) inode_ref_inc(node) +#define vop_ref_dec(node) inode_ref_dec(node) +/* + * Open count manipulation (handled above filesystem level) + * + * VOP_INCOPEN is called by vfs_open. VOP_DECOPEN is called by vfs_close. + * Neither of these should need to be called from above the vfs layer. + */ +#define vop_open_inc(node) inode_open_inc(node) +#define vop_open_dec(node) inode_open_dec(node) + + +static inline int +inode_ref_count(struct inode *node) { + return node->ref_count; +} + +static inline int +inode_open_count(struct inode *node) { + return node->open_count; +} + +#endif /* !__KERN_FS_VFS_INODE_H__ */ + diff --git a/lab8_practice/kern/fs/vfs/vfs.c b/lab8_practice/kern/fs/vfs/vfs.c new file mode 100644 index 0000000..e3ed0da --- /dev/null +++ b/lab8_practice/kern/fs/vfs/vfs.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +static semaphore_t bootfs_sem; +static struct inode *bootfs_node = NULL; + +extern void vfs_devlist_init(void); + +// __alloc_fs - allocate memory for fs, and set fs type +struct fs * +__alloc_fs(int type) { + struct fs *fs; + if ((fs = kmalloc(sizeof(struct fs))) != NULL) { + fs->fs_type = type; + } + return fs; +} + +// vfs_init - vfs initialize +void +vfs_init(void) { + sem_init(&bootfs_sem, 1); + vfs_devlist_init(); +} + +// lock_bootfs - lock for bootfs +static void +lock_bootfs(void) { + down(&bootfs_sem); +} +// ulock_bootfs - ulock for bootfs +static void +unlock_bootfs(void) { + up(&bootfs_sem); +} + +// change_bootfs - set the new fs inode +static void +change_bootfs(struct inode *node) { + struct inode *old; + lock_bootfs(); + { + old = bootfs_node, bootfs_node = node; + } + unlock_bootfs(); + if (old != NULL) { + vop_ref_dec(old); + } +} + +// vfs_set_bootfs - change the dir of file system +int +vfs_set_bootfs(char *fsname) { + struct inode *node = NULL; + if (fsname != NULL) { + char *s; + if ((s = strchr(fsname, ':')) == NULL || s[1] != '\0') { + return -E_INVAL; + } + int ret; + if ((ret = vfs_chdir(fsname)) != 0) { + return ret; + } + if ((ret = vfs_get_curdir(&node)) != 0) { + return ret; + } + } + change_bootfs(node); + return 0; +} + +// vfs_get_bootfs - get the inode of bootfs +int +vfs_get_bootfs(struct inode **node_store) { + struct inode *node = NULL; + if (bootfs_node != NULL) { + lock_bootfs(); + { + if ((node = bootfs_node) != NULL) { + vop_ref_inc(bootfs_node); + } + } + unlock_bootfs(); + } + if (node == NULL) { + return -E_NOENT; + } + *node_store = node; + return 0; +} + diff --git a/lab8_practice/kern/fs/vfs/vfs.h b/lab8_practice/kern/fs/vfs/vfs.h new file mode 100644 index 0000000..7ff8a10 --- /dev/null +++ b/lab8_practice/kern/fs/vfs/vfs.h @@ -0,0 +1,191 @@ +#ifndef __KERN_FS_VFS_VFS_H__ +#define __KERN_FS_VFS_VFS_H__ + +#include +#include +#include + +struct inode; // abstract structure for an on-disk file (inode.h) +struct device; // abstract structure for a device (dev.h) +struct iobuf; // kernel or userspace I/O buffer (iobuf.h) + +/* + * Abstract filesystem. (Or device accessible as a file.) + * + * Information: + * fs_info : filesystem-specific data (sfs_fs) + * fs_type : filesystem type + * Operations: + * + * fs_sync - Flush all dirty buffers to disk. + * fs_get_root - Return root inode of filesystem. + * fs_unmount - Attempt unmount of filesystem. + * fs_cleanup - Cleanup of filesystem.??? + * + * + * fs_get_root should increment the refcount of the inode returned. + * It should not ever return NULL. + * + * If fs_unmount returns an error, the filesystem stays mounted, and + * consequently the struct fs instance should remain valid. On success, + * however, the filesystem object and all storage associated with the + * filesystem should have been discarded/released. + * + */ +struct fs { + union { + struct sfs_fs __sfs_info; + } fs_info; // filesystem-specific data + enum { + fs_type_sfs_info, + } fs_type; // filesystem type + int (*fs_sync)(struct fs *fs); // Flush all dirty buffers to disk + struct inode *(*fs_get_root)(struct fs *fs); // Return root inode of filesystem. + int (*fs_unmount)(struct fs *fs); // Attempt unmount of filesystem. + void (*fs_cleanup)(struct fs *fs); // Cleanup of filesystem.??? +}; + +#define __fs_type(type) fs_type_##type##_info + +#define check_fs_type(fs, type) ((fs)->fs_type == __fs_type(type)) + +#define __fsop_info(_fs, type) ({ \ + struct fs *__fs = (_fs); \ + assert(__fs != NULL && check_fs_type(__fs, type)); \ + &(__fs->fs_info.__##type##_info); \ + }) + +#define fsop_info(fs, type) __fsop_info(fs, type) + +#define info2fs(info, type) \ + to_struct((info), struct fs, fs_info.__##type##_info) + +struct fs *__alloc_fs(int type); + +#define alloc_fs(type) __alloc_fs(__fs_type(type)) + +// Macros to shorten the calling sequences. +#define fsop_sync(fs) ((fs)->fs_sync(fs)) +#define fsop_get_root(fs) ((fs)->fs_get_root(fs)) +#define fsop_unmount(fs) ((fs)->fs_unmount(fs)) +#define fsop_cleanup(fs) ((fs)->fs_cleanup(fs)) + +/* + * Virtual File System layer functions. + * + * The VFS layer translates operations on abstract on-disk files or + * pathnames to operations on specific files on specific filesystems. + */ +void vfs_init(void); +void vfs_cleanup(void); +void vfs_devlist_init(void); + +/* + * VFS layer low-level operations. + * See inode.h for direct operations on inodes. + * See fs.h for direct operations on filesystems/devices. + * + * vfs_set_curdir - change current directory of current thread by inode + * vfs_get_curdir - retrieve inode of current directory of current thread + * vfs_get_root - get root inode for the filesystem named DEVNAME + * vfs_get_devname - get mounted device name for the filesystem passed in + */ +int vfs_set_curdir(struct inode *dir); +int vfs_get_curdir(struct inode **dir_store); +int vfs_get_root(const char *devname, struct inode **root_store); +const char *vfs_get_devname(struct fs *fs); + + +/* + * VFS layer high-level operations on pathnames + * Because namei may destroy pathnames, these all may too. + * + * vfs_open - Open or create a file. FLAGS/MODE per the syscall. + * vfs_close - Close a inode opened with vfs_open. Does not fail. + * (See vfspath.c for a discussion of why.) + * vfs_link - Create a hard link to a file. + * vfs_symlink - Create a symlink PATH containing contents CONTENTS. + * vfs_readlink - Read contents of a symlink into a uio. + * vfs_mkdir - Create a directory. MODE per the syscall. + * vfs_unlink - Delete a file/directory. + * vfs_rename - rename a file. + * vfs_chdir - Change current directory of current thread by name. + * vfs_getcwd - Retrieve name of current directory of current thread. + * + */ +int vfs_open(char *path, uint32_t open_flags, struct inode **inode_store); +int vfs_close(struct inode *node); +int vfs_link(char *old_path, char *new_path); +int vfs_symlink(char *old_path, char *new_path); +int vfs_readlink(char *path, struct iobuf *iob); +int vfs_mkdir(char *path); +int vfs_unlink(char *path); +int vfs_rename(char *old_path, char *new_path); +int vfs_chdir(char *path); +int vfs_getcwd(struct iobuf *iob); + + +/* + * VFS layer mid-level operations. + * + * vfs_lookup - Like VOP_LOOKUP, but takes a full device:path name, + * or a name relative to the current directory, and + * goes to the correct filesystem. + * vfs_lookparent - Likewise, for VOP_LOOKPARENT. + * + * Both of these may destroy the path passed in. + */ +int vfs_lookup(char *path, struct inode **node_store); +int vfs_lookup_parent(char *path, struct inode **node_store, char **endp); + +/* + * Misc + * + * vfs_set_bootfs - Set the filesystem that paths beginning with a + * slash are sent to. If not set, these paths fail + * with ENOENT. The argument should be the device + * name or volume name for the filesystem (such as + * "lhd0:") but need not have the trailing colon. + * + * vfs_get_bootfs - return the inode of the bootfs filesystem. + * + * vfs_add_fs - Add a hardwired filesystem to the VFS named device + * list. It will be accessible as "devname:". This is + * intended for filesystem-devices like emufs, and + * gizmos like Linux procfs or BSD kernfs, not for + * mounting filesystems on disk devices. + * + * vfs_add_dev - Add a device to the VFS named device list. If + * MOUNTABLE is zero, the device will be accessible + * as "DEVNAME:". If the mountable flag is set, the + * device will be accessible as "DEVNAMEraw:" and + * mountable under the name "DEVNAME". Thus, the + * console, added with MOUNTABLE not set, would be + * accessed by pathname as "con:", and lhd0, added + * with mountable set, would be accessed by + * pathname as "lhd0raw:" and mounted by passing + * "lhd0" to vfs_mount. + * + * vfs_mount - Attempt to mount a filesystem on a device. The + * device named by DEVNAME will be looked up and + * passed, along with DATA, to the supplied function + * MOUNTFUNC, which should create a struct fs and + * return it in RESULT. + * + * vfs_unmount - Unmount the filesystem presently mounted on the + * specified device. + * + * vfs_unmountall - Unmount all mounted filesystems. + */ +int vfs_set_bootfs(char *fsname); +int vfs_get_bootfs(struct inode **node_store); + +int vfs_add_fs(const char *devname, struct fs *fs); +int vfs_add_dev(const char *devname, struct inode *devnode, bool mountable); + +int vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store)); +int vfs_unmount(const char *devname); +int vfs_unmount_all(void); + +#endif /* !__KERN_FS_VFS_VFS_H__ */ + diff --git a/lab8_practice/kern/fs/vfs/vfsdev.c b/lab8_practice/kern/fs/vfs/vfsdev.c new file mode 100644 index 0000000..9166695 --- /dev/null +++ b/lab8_practice/kern/fs/vfs/vfsdev.c @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// device info entry in vdev_list +typedef struct { + const char *devname; + struct inode *devnode; + struct fs *fs; + bool mountable; + list_entry_t vdev_link; +} vfs_dev_t; + +#define le2vdev(le, member) \ + to_struct((le), vfs_dev_t, member) + +static list_entry_t vdev_list; // device info list in vfs layer +static semaphore_t vdev_list_sem; + +static void +lock_vdev_list(void) { + down(&vdev_list_sem); +} + +static void +unlock_vdev_list(void) { + up(&vdev_list_sem); +} + +void +vfs_devlist_init(void) { + list_init(&vdev_list); + sem_init(&vdev_list_sem, 1); +} + +// vfs_cleanup - finally clean (or sync) fs +void +vfs_cleanup(void) { + if (!list_empty(&vdev_list)) { + lock_vdev_list(); + { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->fs != NULL) { + fsop_cleanup(vdev->fs); + } + } + } + unlock_vdev_list(); + } +} + +/* + * vfs_get_root - Given a device name (stdin, stdout, etc.), hand + * back an appropriate inode. + */ +int +vfs_get_root(const char *devname, struct inode **node_store) { + assert(devname != NULL); + int ret = -E_NO_DEV; + if (!list_empty(&vdev_list)) { + lock_vdev_list(); + { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (strcmp(devname, vdev->devname) == 0) { + struct inode *found = NULL; + if (vdev->fs != NULL) { + found = fsop_get_root(vdev->fs); + } + else if (!vdev->mountable) { + vop_ref_inc(vdev->devnode); + found = vdev->devnode; + } + if (found != NULL) { + ret = 0, *node_store = found; + } + else { + ret = -E_NA_DEV; + } + break; + } + } + } + unlock_vdev_list(); + } + return ret; +} + +/* + * vfs_get_devname - Given a filesystem, hand back the name of the device it's mounted on. + */ +const char * +vfs_get_devname(struct fs *fs) { + assert(fs != NULL); + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->fs == fs) { + return vdev->devname; + } + } + return NULL; +} + +/* + * check_devname_confilct - Is there alreadily device which has the same name? + */ +static bool +check_devname_conflict(const char *devname) { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (strcmp(vdev->devname, devname) == 0) { + return 0; + } + } + return 1; +} + + +/* +* vfs_do_add - Add a new device to the VFS layer's device table. +* +* If "mountable" is set, the device will be treated as one that expects +* to have a filesystem mounted on it, and a raw device will be created +* for direct access. +*/ +static int +vfs_do_add(const char *devname, struct inode *devnode, struct fs *fs, bool mountable) { + assert(devname != NULL); + assert((devnode == NULL && !mountable) || (devnode != NULL && check_inode_type(devnode, device))); + if (strlen(devname) > FS_MAX_DNAME_LEN) { + return -E_TOO_BIG; + } + + int ret = -E_NO_MEM; + char *s_devname; + if ((s_devname = strdup(devname)) == NULL) { + return ret; + } + + vfs_dev_t *vdev; + if ((vdev = kmalloc(sizeof(vfs_dev_t))) == NULL) { + goto failed_cleanup_name; + } + + ret = -E_EXISTS; + lock_vdev_list(); + if (!check_devname_conflict(s_devname)) { + unlock_vdev_list(); + goto failed_cleanup_vdev; + } + vdev->devname = s_devname; + vdev->devnode = devnode; + vdev->mountable = mountable; + vdev->fs = fs; + + list_add(&vdev_list, &(vdev->vdev_link)); + unlock_vdev_list(); + return 0; + +failed_cleanup_vdev: + kfree(vdev); +failed_cleanup_name: + kfree(s_devname); + return ret; +} + +/* + * vfs_add_fs - Add a new fs, by name. See vfs_do_add information for the description of + * mountable. + */ +int +vfs_add_fs(const char *devname, struct fs *fs) { + return vfs_do_add(devname, NULL, fs, 0); +} + +/* + * vfs_add_dev - Add a new device, by name. See vfs_do_add information for the description of + * mountable. + */ +int +vfs_add_dev(const char *devname, struct inode *devnode, bool mountable) { + return vfs_do_add(devname, devnode, NULL, mountable); +} + +/* + * find_mount - Look for a mountable device named DEVNAME. + * Should already hold vdev_list lock. + */ +static int +find_mount(const char *devname, vfs_dev_t **vdev_store) { + assert(devname != NULL); + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->mountable && strcmp(vdev->devname, devname) == 0) { + *vdev_store = vdev; + return 0; + } + } + return -E_NO_DEV; +} + +/* + * vfs_mount - Mount a filesystem. Once we've found the device, call MOUNTFUNC to + * set up the filesystem and hand back a struct fs. + * + * The DATA argument is passed through unchanged to MOUNTFUNC. + */ +int +vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store)) { + int ret; + lock_vdev_list(); + vfs_dev_t *vdev; + if ((ret = find_mount(devname, &vdev)) != 0) { + goto out; + } + if (vdev->fs != NULL) { + ret = -E_BUSY; + goto out; + } + assert(vdev->devname != NULL && vdev->mountable); + + struct device *dev = vop_info(vdev->devnode, device); + if ((ret = mountfunc(dev, &(vdev->fs))) == 0) { + assert(vdev->fs != NULL); + cprintf("vfs: mount %s.\n", vdev->devname); + } + +out: + unlock_vdev_list(); + return ret; +} + +/* + * vfs_unmount - Unmount a filesystem/device by name. + * First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT. + */ +int +vfs_unmount(const char *devname) { + int ret; + lock_vdev_list(); + vfs_dev_t *vdev; + if ((ret = find_mount(devname, &vdev)) != 0) { + goto out; + } + if (vdev->fs == NULL) { + ret = -E_INVAL; + goto out; + } + assert(vdev->devname != NULL && vdev->mountable); + + if ((ret = fsop_sync(vdev->fs)) != 0) { + goto out; + } + if ((ret = fsop_unmount(vdev->fs)) == 0) { + vdev->fs = NULL; + cprintf("vfs: unmount %s.\n", vdev->devname); + } + +out: + unlock_vdev_list(); + return ret; +} + +/* + * vfs_unmount_all - Global unmount function. + */ +int +vfs_unmount_all(void) { + if (!list_empty(&vdev_list)) { + lock_vdev_list(); + { + list_entry_t *list = &vdev_list, *le = list; + while ((le = list_next(le)) != list) { + vfs_dev_t *vdev = le2vdev(le, vdev_link); + if (vdev->mountable && vdev->fs != NULL) { + int ret; + if ((ret = fsop_sync(vdev->fs)) != 0) { + cprintf("vfs: warning: sync failed for %s: %e.\n", vdev->devname, ret); + continue ; + } + if ((ret = fsop_unmount(vdev->fs)) != 0) { + cprintf("vfs: warning: unmount failed for %s: %e.\n", vdev->devname, ret); + continue ; + } + vdev->fs = NULL; + cprintf("vfs: unmount %s.\n", vdev->devname); + } + } + } + unlock_vdev_list(); + } + return 0; +} + diff --git a/lab8_practice/kern/fs/vfs/vfsfile.c b/lab8_practice/kern/fs/vfs/vfsfile.c new file mode 100644 index 0000000..eb79dca --- /dev/null +++ b/lab8_practice/kern/fs/vfs/vfsfile.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include + + +// open file in vfs, get/create inode for file with filename path. +int +vfs_open(char *path, uint32_t open_flags, struct inode **node_store) { + bool can_write = 0; + switch (open_flags & O_ACCMODE) { + case O_RDONLY: + break; + case O_WRONLY: + case O_RDWR: + can_write = 1; + break; + default: + return -E_INVAL; + } + + if (open_flags & O_TRUNC) { + if (!can_write) { + return -E_INVAL; + } + } + + int ret; + struct inode *node; + bool excl = (open_flags & O_EXCL) != 0; + bool create = (open_flags & O_CREAT) != 0; + ret = vfs_lookup(path, &node); + + if (ret != 0) { + if (ret == -16 && (create)) { + char *name; + struct inode *dir; + if ((ret = vfs_lookup_parent(path, &dir, &name)) != 0) { + return ret; + } + ret = vop_create(dir, name, excl, &node); + } else return ret; + } else if (excl && create) { + return -E_EXISTS; + } + assert(node != NULL); + + if ((ret = vop_open(node, open_flags)) != 0) { + vop_ref_dec(node); + return ret; + } + + vop_open_inc(node); + if (open_flags & O_TRUNC || create) { + if ((ret = vop_truncate(node, 0)) != 0) { + vop_open_dec(node); + vop_ref_dec(node); + return ret; + } + } + *node_store = node; + return 0; +} + +// close file in vfs +int +vfs_close(struct inode *node) { + vop_open_dec(node); + vop_ref_dec(node); + return 0; +} + +// unimplement +int +vfs_unlink(char *path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_rename(char *old_path, char *new_path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_link(char *old_path, char *new_path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_symlink(char *old_path, char *new_path) { + return -E_UNIMP; +} + +// unimplement +int +vfs_readlink(char *path, struct iobuf *iob) { + return -E_UNIMP; +} + +// unimplement +int +vfs_mkdir(char *path){ + return -E_UNIMP; +} diff --git a/lab8_practice/kern/fs/vfs/vfslookup.c b/lab8_practice/kern/fs/vfs/vfslookup.c new file mode 100644 index 0000000..619261d --- /dev/null +++ b/lab8_practice/kern/fs/vfs/vfslookup.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include + +/* + * get_device- Common code to pull the device name, if any, off the front of a + * path and choose the inode to begin the name lookup relative to. + */ + +static int +get_device(char *path, char **subpath, struct inode **node_store) { + int i, slash = -1, colon = -1; + for (i = 0; path[i] != '\0'; i ++) { + if (path[i] == ':') { colon = i; break; } + if (path[i] == '/') { slash = i; break; } + } + if (colon < 0 && slash != 0) { + /* * + * No colon before a slash, so no device name specified, and the slash isn't leading + * or is also absent, so this is a relative path or just a bare filename. Start from + * the current directory, and use the whole thing as the subpath. + * */ + *subpath = path; + return vfs_get_curdir(node_store); + } + if (colon > 0) { + /* device:path - get root of device's filesystem */ + path[colon] = '\0'; + + /* device:/path - skip slash, treat as device:path */ + while (path[++ colon] == '/'); + *subpath = path + colon; + return vfs_get_root(path, node_store); + } + + /* * + * we have either /path or :path + * /path is a path relative to the root of the "boot filesystem" + * :path is a path relative to the root of the current filesystem + * */ + int ret; + if (*path == '/') { + if ((ret = vfs_get_bootfs(node_store)) != 0) { + return ret; + } + } + else { + assert(*path == ':'); + struct inode *node; + if ((ret = vfs_get_curdir(&node)) != 0) { + return ret; + } + /* The current directory may not be a device, so it must have a fs. */ + assert(node->in_fs != NULL); + *node_store = fsop_get_root(node->in_fs); + vop_ref_dec(node); + } + + /* ///... or :/... */ + while (*(++ path) == '/'); + *subpath = path; + return 0; +} + +/* + * vfs_lookup - get the inode according to the path filename + */ +int +vfs_lookup(char *path, struct inode **node_store) { + int ret; + struct inode *node; + if ((ret = get_device(path, &path, &node)) != 0) { + return ret; + } + if (*path != '\0') { + ret = vop_lookup(node, path, node_store); + vop_ref_dec(node); + return ret; + } + *node_store = node; + return 0; +} + +/* + * vfs_lookup_parent - Name-to-vnode translation. + * (In BSD, both of these are subsumed by namei().) + */ +int +vfs_lookup_parent(char *path, struct inode **node_store, char **endp){ + int ret; + struct inode *node; + if ((ret = get_device(path, &path, &node)) != 0) { + return ret; + } + *endp = path; + *node_store = node; + return 0; +} diff --git a/lab8_practice/kern/fs/vfs/vfspath.c b/lab8_practice/kern/fs/vfs/vfspath.c new file mode 100644 index 0000000..0e164df --- /dev/null +++ b/lab8_practice/kern/fs/vfs/vfspath.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * get_cwd_nolock - retrieve current process's working directory. without lock protect + */ +static struct inode * +get_cwd_nolock(void) { + return current->filesp->pwd; +} +/* + * set_cwd_nolock - set current working directory. + */ +static void +set_cwd_nolock(struct inode *pwd) { + current->filesp->pwd = pwd; +} + +/* + * lock_cfs - lock the fs related process on current process + */ +static void +lock_cfs(void) { + lock_files(current->filesp); +} +/* + * unlock_cfs - unlock the fs related process on current process + */ +static void +unlock_cfs(void) { + unlock_files(current->filesp); +} + +/* + * vfs_get_curdir - Get current directory as a inode. + */ +int +vfs_get_curdir(struct inode **dir_store) { + struct inode *node; + if ((node = get_cwd_nolock()) != NULL) { + vop_ref_inc(node); + *dir_store = node; + return 0; + } + return -E_NOENT; +} + +/* + * vfs_set_curdir - Set current directory as a inode. + * The passed inode must in fact be a directory. + */ +int +vfs_set_curdir(struct inode *dir) { + int ret = 0; + lock_cfs(); + struct inode *old_dir; + if ((old_dir = get_cwd_nolock()) != dir) { + if (dir != NULL) { + uint32_t type; + if ((ret = vop_gettype(dir, &type)) != 0) { + goto out; + } + if (!S_ISDIR(type)) { + ret = -E_NOTDIR; + goto out; + } + vop_ref_inc(dir); + } + set_cwd_nolock(dir); + if (old_dir != NULL) { + vop_ref_dec(old_dir); + } + } +out: + unlock_cfs(); + return ret; +} + +/* + * vfs_chdir - Set current directory, as a pathname. Use vfs_lookup to translate + * it to a inode. + */ +int +vfs_chdir(char *path) { + int ret; + struct inode *node; + if ((ret = vfs_lookup(path, &node)) == 0) { + ret = vfs_set_curdir(node); + vop_ref_dec(node); + } + return ret; +} +/* + * vfs_getcwd - retrieve current working directory(cwd). + */ +int +vfs_getcwd(struct iobuf *iob) { + int ret; + struct inode *node; + if ((ret = vfs_get_curdir(&node)) != 0) { + return ret; + } + assert(node->in_fs != NULL); + + const char *devname = vfs_get_devname(node->in_fs); + if ((ret = iobuf_move(iob, (char *)devname, strlen(devname), 1, NULL)) != 0) { + goto out; + } + char colon = ':'; + if ((ret = iobuf_move(iob, &colon, sizeof(colon), 1, NULL)) != 0) { + goto out; + } + ret = vop_namefile(node, iob); + +out: + vop_ref_dec(node); + return ret; +} + diff --git a/lab8_practice/kern/init/entry.S b/lab8_practice/kern/init/entry.S new file mode 100644 index 0000000..d567f54 --- /dev/null +++ b/lab8_practice/kern/init/entry.S @@ -0,0 +1,55 @@ +#include +#include + + .section .text,"ax",%progbits + .globl kern_entry +kern_entry: + # t0 := 三级页表的虚拟地址 + lui t0, %hi(boot_page_table_sv39) + # t1 := 0xffffffff40000000 即虚实映射偏移量 + li t1, 0xffffffffc0000000 - 0x80000000 + # t0 减去虚实映射偏移量 0xffffffff40000000,变为三级页表的物理地址 + sub t0, t0, t1 + # t0 >>= 12,变为三级页表的物理页号 + srli t0, t0, 12 + + # t1 := 8 << 60,设置 satp 的 MODE 字段为 Sv39 + li t1, 8 << 60 + # 将刚才计算出的预设三级页表物理页号附加到 satp 中 + or t0, t0, t1 + # 将算出的 t0(即新的MODE|页表基址物理页号) 覆盖到 satp 中 + csrw satp, t0 + # 使用 sfence.vma 指令刷新 TLB + sfence.vma + # 从此,我们给内核搭建出了一个完美的虚拟内存空间! + #nop # 可能映射的位置有些bug。。插入一个nop + + # 我们在虚拟内存空间中:随意将 sp 设置为虚拟地址! + lui sp, %hi(bootstacktop) + + # 我们在虚拟内存空间中:随意跳转到虚拟地址! + # 跳转到 kern_init + lui t0, %hi(kern_init) + addi t0, t0, %lo(kern_init) + jr t0 + +.section .data + # .align 2^12 + .align PGSHIFT + .global bootstack +bootstack: + .space KSTACKSIZE + .global bootstacktop +bootstacktop: + +.section .data + # 由于我们要把这个页表放到一个页里面,因此必须 12 位对齐 + .align PGSHIFT + .global boot_page_table_sv39 +# 分配 4KiB 内存给预设的三级页表 +boot_page_table_sv39: + # 0xffffffff_c0000000 map to 0x80000000 (1G) + # 前 511 个页表项均设置为 0 ,因此 V=0 ,意味着是空的(unmapped) + .zero 8 * 511 + # 设置最后一个页表项,PPN=0x80000,标志位 VRWXAD 均为 1 + .quad (0x80000 << 10) | 0xcf # VRWXAD diff --git a/lab8_practice/kern/init/init.c b/lab8_practice/kern/init/init.c new file mode 100644 index 0000000..c6a8b6d --- /dev/null +++ b/lab8_practice/kern/init/init.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int kern_init(void) __attribute__((noreturn)); +void grade_backtrace(void); +static void lab1_switch_test(void); + +int +kern_init(void) { + extern char edata[], end[]; + memset(edata, 0, end - edata); + cons_init(); // init the console + + const char *message = "(THU.CST) os is loading ..."; + cprintf("%s\n\n", message); + + print_kerninfo(); + + // grade_backtrace(); + + pmm_init(); // init physical memory management + + pic_init(); // init interrupt controller + idt_init(); // init interrupt descriptor table + + vmm_init(); // init virtual memory management + sched_init(); + proc_init(); // init process table + + ide_init(); // init ide devices + swap_init(); // init swap + fs_init(); + + clock_init(); // init clock interrupt + intr_enable(); // enable irq interrupt + + //LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test() + // user/kernel mode switch test + //lab1_switch_test(); + cpu_idle(); // run idle process +} + +void __attribute__((noinline)) +grade_backtrace2(int arg0, int arg1, int arg2, int arg3) { + mon_backtrace(0, NULL, NULL); +} + +void __attribute__((noinline)) +grade_backtrace1(int arg0, int arg1) { + grade_backtrace2(arg0, (int64_t)&arg0, arg1, (int64_t)&arg1); +} + +void __attribute__((noinline)) +grade_backtrace0(int arg0, int arg1, int arg2) { + grade_backtrace1(arg0, arg2); +} + +void +grade_backtrace(void) { + grade_backtrace0(0, (int64_t)kern_init, 0xffff0000); +} + +static void +lab1_print_cur_status(void) { + static int round = 0; + round ++; +} + +static void +lab1_switch_to_user(void) { + //LAB1 CHALLENGE 1 : TODO +} + +static void +lab1_switch_to_kernel(void) { + //LAB1 CHALLENGE 1 : TODO +} + +static void +lab1_switch_test(void) { + lab1_print_cur_status(); + cprintf("+++ switch to user mode +++\n"); + lab1_switch_to_user(); + lab1_print_cur_status(); + cprintf("+++ switch to kernel mode +++\n"); + lab1_switch_to_kernel(); + lab1_print_cur_status(); +} + diff --git a/lab8_practice/kern/libs/readline.c b/lab8_practice/kern/libs/readline.c new file mode 100644 index 0000000..cc1eddb --- /dev/null +++ b/lab8_practice/kern/libs/readline.c @@ -0,0 +1,50 @@ +#include + +#define BUFSIZE 1024 +static char buf[BUFSIZE]; + +/* * + * readline - get a line from stdin + * @prompt: the string to be written to stdout + * + * The readline() function will write the input string @prompt to + * stdout first. If the @prompt is NULL or the empty string, + * no prompt is issued. + * + * This function will keep on reading characters and saving them to buffer + * 'buf' until '\n' or '\r' is encountered. + * + * Note that, if the length of string that will be read is longer than + * buffer size, the end of string will be discarded. + * + * The readline() function returns the text of the line read. If some errors + * are happened, NULL is returned. The return value is a global variable, + * thus it should be copied before it is used. + * */ +char * +readline(const char *prompt) { + if (prompt != NULL) { + cprintf("%s", prompt); + } + int i = 0, c; + while (1) { + c = getchar(); + if (c < 0) { + return NULL; + } + else if (c >= ' ' && i < BUFSIZE - 1) { + cputchar(c); + buf[i ++] = c; + } + else if (c == '\b' && i > 0) { + cputchar(c); + i --; + } + else if (c == '\n' || c == '\r') { + cputchar(c); + buf[i] = '\0'; + return buf; + } + } +} + diff --git a/lab8_practice/kern/libs/stdio.c b/lab8_practice/kern/libs/stdio.c new file mode 100644 index 0000000..aaade41 --- /dev/null +++ b/lab8_practice/kern/libs/stdio.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +/* HIGH level console I/O */ + +/* * + * cputch - writes a single character @c to stdout, and it will + * increace the value of counter pointed by @cnt. + * */ +static void +cputch(int c, int *cnt) { + cons_putc(c); + (*cnt) ++; +} + +/* * + * vcprintf - format a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * + * Call this function if you are already dealing with a va_list. + * Or you probably want cprintf() instead. + * */ +int +vcprintf(const char *fmt, va_list ap) { + int cnt = 0; + vprintfmt((void*)cputch, NO_FD, &cnt, fmt, ap); + return cnt; +} + +/* * + * cprintf - formats a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * */ +int +cprintf(const char *fmt, ...) { + va_list ap; + int cnt; + va_start(ap, fmt); + cnt = vcprintf(fmt, ap); + va_end(ap); + return cnt; +} + +/* cputchar - writes a single character to stdout */ +void +cputchar(int c) { + cons_putc(c); +} + +/* * + * cputs- writes the string pointed by @str to stdout and + * appends a newline character. + * */ +int +cputs(const char *str) { + int cnt = 0; + char c; + while ((c = *str ++) != '\0') { + cputch(c, &cnt); + } + cputch('\n', &cnt); + return cnt; +} + +/* getchar - reads a single non-zero character from stdin */ +int +getchar(void) { + int c; + while ((c = cons_getc()) == 0) + /* do nothing */; + return c; +} + diff --git a/lab8_practice/kern/libs/string.c b/lab8_practice/kern/libs/string.c new file mode 100644 index 0000000..4b3548e --- /dev/null +++ b/lab8_practice/kern/libs/string.c @@ -0,0 +1,26 @@ +#include +#include + +char * +strdup(const char *src) { + char *dst; + size_t len = strlen(src); + if ((dst = kmalloc(len + 1)) != NULL) { + memcpy(dst, src, len); + dst[len] = '\0'; + } + return dst; +} + +char * +stradd(const char *src1, const char *src2) { + char *ret, *dst; + size_t len1 = strlen(src1), len2 = strlen(src2); + if ((ret = dst = kmalloc(len1 + len2 + 1)) != NULL) { + memcpy(dst, src1, len1), dst += len1; + memcpy(dst, src2, len2), dst += len2; + *dst = '\0'; + } + return ret; +} + diff --git a/lab8_practice/kern/mm/default_pmm.c b/lab8_practice/kern/mm/default_pmm.c new file mode 100644 index 0000000..c11c7c3 --- /dev/null +++ b/lab8_practice/kern/mm/default_pmm.c @@ -0,0 +1,306 @@ +#include +#include +#include +#include + +/* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and, + on receiving a request for memory, scans along the list for the first block that is large enough to + satisfy the request. If the chosen block is significantly larger than that requested, then it is + usually split, and the remainder added to the list as another free block. + Please see Page 196~198, Section 8.2 of Yan Wei Min's chinese book "Data Structure -- C programming language" +*/ +// LAB2 EXERCISE 1: YOUR CODE +// you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages. +/* + * Details of FFMA + * (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list. + * The struct free_area_t is used for the management of free mem blocks. At first you should + * be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation. + * You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev + * Another tricky method is to transform a general list struct to a special struct (such as struct page): + * you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.) + * (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0. + * free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks. + * (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap + * This fun is used to init a free block (with parameter: addr_base, page_number). + * First you should init each page (in memlayout.h) in this free block, include: + * p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c), + * the bit PG_reserved is setted in p->flags) + * if this page is free and is not the first page of free block, p->property should be set to 0. + * if this page is free and is the first page of free block, p->property should be set to total num of block. + * p->ref should be 0, because now p is free and no reference. + * We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); ) + * Finally, we should sum the number of free mem block: nr_free+=n + * (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr + * of malloced block. + * (4.1) So you should search freelist like this: + * list_entry_t le = &free_list; + * while((le=list_next(le)) != &free_list) { + * .... + * (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n? + * struct Page *p = le2page(le, page_link); + * if(p->property >= n){ ... + * (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced. + * Some flag bits of this page should be setted: PG_reserved =1, PG_property =0 + * unlink the pages from free_list + * (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block, + * (such as: le2page(le,page_link))->property = p->property - n;) + * (4.1.3) re-caluclate nr_free (number of the the rest of all free block) + * (4.1.4) return p + * (4.2) If we can not find a free block (block size >=n), then return NULL + * (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks. + * (5.1) according the base addr of withdrawed blocks, search free list, find the correct position + * (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before) + * (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty) + * (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly. + */ +free_area_t free_area; + +#define free_list (free_area.free_list) +#define nr_free (free_area.nr_free) + +static void +default_init(void) { + list_init(&free_list); + nr_free = 0; +} + +static void +default_init_memmap(struct Page *base, size_t n) { + assert(n > 0); + struct Page *p = base; + for (; p != base + n; p ++) { + assert(PageReserved(p)); + p->flags = p->property = 0; + set_page_ref(p, 0); + } + base->property = n; + SetPageProperty(base); + nr_free += n; + if (list_empty(&free_list)) { + list_add(&free_list, &(base->page_link)); + } else { + list_entry_t* le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page* page = le2page(le, page_link); + if (base < page) { + list_add_before(le, &(base->page_link)); + break; + } else if (list_next(le) == &free_list) { + list_add(le, &(base->page_link)); + } + } + } +} + +static struct Page * +default_alloc_pages(size_t n) { + assert(n > 0); + if (n > nr_free) { + return NULL; + } + struct Page *page = NULL; + list_entry_t *le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + if (p->property >= n) { + page = p; + break; + } + } + if (page != NULL) { + list_entry_t* prev = list_prev(&(page->page_link)); + list_del(&(page->page_link)); + if (page->property > n) { + struct Page *p = page + n; + p->property = page->property - n; + SetPageProperty(p); + list_add(prev, &(p->page_link)); + } + nr_free -= n; + ClearPageProperty(page); + } + return page; +} + +static void +default_free_pages(struct Page *base, size_t n) { + assert(n > 0); + struct Page *p = base; + for (; p != base + n; p ++) { + assert(!PageReserved(p) && !PageProperty(p)); + p->flags = 0; + set_page_ref(p, 0); + } + base->property = n; + SetPageProperty(base); + nr_free += n; + + if (list_empty(&free_list)) { + list_add(&free_list, &(base->page_link)); + } else { + list_entry_t* le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page* page = le2page(le, page_link); + if (base < page) { + list_add_before(le, &(base->page_link)); + break; + } else if (list_next(le) == &free_list) { + list_add(le, &(base->page_link)); + } + } + } + + list_entry_t* le = list_prev(&(base->page_link)); + if (le != &free_list) { + p = le2page(le, page_link); + if (p + p->property == base) { + p->property += base->property; + ClearPageProperty(base); + list_del(&(base->page_link)); + base = p; + } + } + + le = list_next(&(base->page_link)); + if (le != &free_list) { + p = le2page(le, page_link); + if (base + base->property == p) { + base->property += p->property; + ClearPageProperty(p); + list_del(&(p->page_link)); + } + } +} + +static size_t +default_nr_free_pages(void) { + return nr_free; +} + +static void +basic_check(void) { + struct Page *p0, *p1, *p2; + p0 = p1 = p2 = NULL; + assert((p0 = alloc_page()) != NULL); + assert((p1 = alloc_page()) != NULL); + assert((p2 = alloc_page()) != NULL); + + assert(p0 != p1 && p0 != p2 && p1 != p2); + assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0); + + assert(page2pa(p0) < npage * PGSIZE); + assert(page2pa(p1) < npage * PGSIZE); + assert(page2pa(p2) < npage * PGSIZE); + + list_entry_t free_list_store = free_list; + list_init(&free_list); + assert(list_empty(&free_list)); + + unsigned int nr_free_store = nr_free; + nr_free = 0; + + assert(alloc_page() == NULL); + + free_page(p0); + free_page(p1); + free_page(p2); + assert(nr_free == 3); + + assert((p0 = alloc_page()) != NULL); + assert((p1 = alloc_page()) != NULL); + assert((p2 = alloc_page()) != NULL); + + assert(alloc_page() == NULL); + + free_page(p0); + assert(!list_empty(&free_list)); + + struct Page *p; + assert((p = alloc_page()) == p0); + assert(alloc_page() == NULL); + + assert(nr_free == 0); + free_list = free_list_store; + nr_free = nr_free_store; + + free_page(p); + free_page(p1); + free_page(p2); +} + +// LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1) +// NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions! +static void +default_check(void) { + int count = 0, total = 0; + list_entry_t *le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + assert(PageProperty(p)); + count ++, total += p->property; + } + assert(total == nr_free_pages()); + + basic_check(); + + struct Page *p0 = alloc_pages(5), *p1, *p2; + assert(p0 != NULL); + assert(!PageProperty(p0)); + + list_entry_t free_list_store = free_list; + list_init(&free_list); + assert(list_empty(&free_list)); + assert(alloc_page() == NULL); + + unsigned int nr_free_store = nr_free; + nr_free = 0; + + free_pages(p0 + 2, 3); + assert(alloc_pages(4) == NULL); + assert(PageProperty(p0 + 2) && p0[2].property == 3); + assert((p1 = alloc_pages(3)) != NULL); + assert(alloc_page() == NULL); + assert(p0 + 2 == p1); + + p2 = p0 + 1; + free_page(p0); + free_pages(p1, 3); + assert(PageProperty(p0) && p0->property == 1); + assert(PageProperty(p1) && p1->property == 3); + + assert((p0 = alloc_page()) == p2 - 1); + free_page(p0); + assert((p0 = alloc_pages(2)) == p2 + 1); + + free_pages(p0, 2); + free_page(p2); + + assert((p0 = alloc_pages(5)) != NULL); + assert(alloc_page() == NULL); + + assert(nr_free == 0); + nr_free = nr_free_store; + + free_list = free_list_store; + free_pages(p0, 5); + + le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + count --, total -= p->property; + } + assert(count == 0); + assert(total == 0); +} + +const struct pmm_manager default_pmm_manager = { + .name = "default_pmm_manager", + .init = default_init, + .init_memmap = default_init_memmap, + .alloc_pages = default_alloc_pages, + .free_pages = default_free_pages, + .nr_free_pages = default_nr_free_pages, + .check = default_check, +}; + diff --git a/lab8_practice/kern/mm/default_pmm.h b/lab8_practice/kern/mm/default_pmm.h new file mode 100644 index 0000000..5f4e6fc --- /dev/null +++ b/lab8_practice/kern/mm/default_pmm.h @@ -0,0 +1,9 @@ +#ifndef __KERN_MM_DEFAULT_PMM_H__ +#define __KERN_MM_DEFAULT_PMM_H__ + +#include + +extern const struct pmm_manager default_pmm_manager; +extern free_area_t free_area; +#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */ + diff --git a/lab8_practice/kern/mm/kmalloc.c b/lab8_practice/kern/mm/kmalloc.c new file mode 100644 index 0000000..ed3e29a --- /dev/null +++ b/lab8_practice/kern/mm/kmalloc.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * SLOB Allocator: Simple List Of Blocks + * + * Matt Mackall 12/30/03 + * + * How SLOB works: + * + * The core of SLOB is a traditional K&R style heap allocator, with + * support for returning aligned objects. The granularity of this + * allocator is 8 bytes on x86, though it's perhaps possible to reduce + * this to 4 if it's deemed worth the effort. The slob heap is a + * singly-linked list of pages from __get_free_page, grown on demand + * and allocation from the heap is currently first-fit. + * + * Above this is an implementation of kmalloc/kfree. Blocks returned + * from kmalloc are 8-byte aligned and prepended with a 8-byte header. + * If kmalloc is asked for objects of PAGE_SIZE or larger, it calls + * __get_free_pages directly so that it can return page-aligned blocks + * and keeps a linked list of such pages and their orders. These + * objects are detected in kfree() by their page alignment. + * + * SLAB is emulated on top of SLOB by simply calling constructors and + * destructors for every SLAB allocation. Objects are returned with + * the 8-byte alignment unless the SLAB_MUST_HWCACHE_ALIGN flag is + * set, in which case the low-level allocator will fragment blocks to + * create the proper alignment. Again, objects of page-size or greater + * are allocated by calling __get_free_pages. As SLAB objects know + * their size, no separate size bookkeeping is necessary and there is + * essentially no allocation space overhead. + */ + + +//some helper +#define spin_lock_irqsave(l, f) local_intr_save(f) +#define spin_unlock_irqrestore(l, f) local_intr_restore(f) +typedef unsigned int gfp_t; +#ifndef PAGE_SIZE +#define PAGE_SIZE PGSIZE +#endif + +#ifndef L1_CACHE_BYTES +#define L1_CACHE_BYTES 64 +#endif + +#ifndef ALIGN +#define ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1))) +#endif + + +struct slob_block { + int units; + struct slob_block *next; +}; +typedef struct slob_block slob_t; + +#define SLOB_UNIT sizeof(slob_t) +#define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT) +#define SLOB_ALIGN L1_CACHE_BYTES + +struct bigblock { + int order; + void *pages; + struct bigblock *next; +}; +typedef struct bigblock bigblock_t; + +static slob_t arena = { .next = &arena, .units = 1 }; +static slob_t *slobfree = &arena; +static bigblock_t *bigblocks; + + +static void* __slob_get_free_pages(gfp_t gfp, int order) +{ + struct Page * page = alloc_pages(1 << order); + if(!page) + return NULL; + return page2kva(page); +} + +#define __slob_get_free_page(gfp) __slob_get_free_pages(gfp, 0) + +static inline void __slob_free_pages(unsigned long kva, int order) +{ + free_pages(kva2page(kva), 1 << order); +} + +static void slob_free(void *b, int size); + +static void *slob_alloc(size_t size, gfp_t gfp, int align) +{ + assert( (size + SLOB_UNIT) < PAGE_SIZE ); + + slob_t *prev, *cur, *aligned = 0; + int delta = 0, units = SLOB_UNITS(size); + unsigned long flags; + + spin_lock_irqsave(&slob_lock, flags); + prev = slobfree; + for (cur = prev->next; ; prev = cur, cur = cur->next) { + if (align) { + aligned = (slob_t *)ALIGN((unsigned long)cur, align); + delta = aligned - cur; + } + if (cur->units >= units + delta) { /* room enough? */ + if (delta) { /* need to fragment head to align? */ + aligned->units = cur->units - delta; + aligned->next = cur->next; + cur->next = aligned; + cur->units = delta; + prev = cur; + cur = aligned; + } + + if (cur->units == units) /* exact fit? */ + prev->next = cur->next; /* unlink */ + else { /* fragment */ + prev->next = cur + units; + prev->next->units = cur->units - units; + prev->next->next = cur->next; + cur->units = units; + } + + slobfree = prev; + spin_unlock_irqrestore(&slob_lock, flags); + return cur; + } + if (cur == slobfree) { + spin_unlock_irqrestore(&slob_lock, flags); + + if (size == PAGE_SIZE) /* trying to shrink arena? */ + return 0; + + cur = (slob_t *)__slob_get_free_page(gfp); + if (!cur) + return 0; + + slob_free(cur, PAGE_SIZE); + spin_lock_irqsave(&slob_lock, flags); + cur = slobfree; + } + } +} + +static void slob_free(void *block, int size) +{ + slob_t *cur, *b = (slob_t *)block; + unsigned long flags; + + if (!block) + return; + + if (size) + b->units = SLOB_UNITS(size); + + /* Find reinsertion point */ + spin_lock_irqsave(&slob_lock, flags); + for (cur = slobfree; !(b > cur && b < cur->next); cur = cur->next) + if (cur >= cur->next && (b > cur || b < cur->next)) + break; + + if (b + b->units == cur->next) { + b->units += cur->next->units; + b->next = cur->next->next; + } else + b->next = cur->next; + + if (cur + cur->units == b) { + cur->units += b->units; + cur->next = b->next; + } else + cur->next = b; + + slobfree = cur; + + spin_unlock_irqrestore(&slob_lock, flags); +} + + + +void +slob_init(void) { + cprintf("use SLOB allocator\n"); +} + +inline void +kmalloc_init(void) { + slob_init(); + cprintf("kmalloc_init() succeeded!\n"); +} + +size_t +slob_allocated(void) { + return 0; +} + +size_t +kallocated(void) { + return slob_allocated(); +} + +static int find_order(int size) +{ + int order = 0; + for ( ; size > 4096 ; size >>=1) + order++; + return order; +} + +static void *__kmalloc(size_t size, gfp_t gfp) +{ + slob_t *m; + bigblock_t *bb; + unsigned long flags; + + if (size < PAGE_SIZE - SLOB_UNIT) { + m = slob_alloc(size + SLOB_UNIT, gfp, 0); + return m ? (void *)(m + 1) : 0; + } + + bb = slob_alloc(sizeof(bigblock_t), gfp, 0); + if (!bb) + return 0; + + bb->order = find_order(size); + bb->pages = (void *)__slob_get_free_pages(gfp, bb->order); + + if (bb->pages) { + spin_lock_irqsave(&block_lock, flags); + bb->next = bigblocks; + bigblocks = bb; + spin_unlock_irqrestore(&block_lock, flags); + return bb->pages; + } + + slob_free(bb, sizeof(bigblock_t)); + return 0; +} + +void * +kmalloc(size_t size) +{ + return __kmalloc(size, 0); +} + + +void kfree(void *block) +{ + bigblock_t *bb, **last = &bigblocks; + unsigned long flags; + + if (!block) + return; + + if (!((unsigned long)block & (PAGE_SIZE-1))) { + /* might be on the big block list */ + spin_lock_irqsave(&block_lock, flags); + for (bb = bigblocks; bb; last = &bb->next, bb = bb->next) { + if (bb->pages == block) { + *last = bb->next; + spin_unlock_irqrestore(&block_lock, flags); + __slob_free_pages((unsigned long)block, bb->order); + slob_free(bb, sizeof(bigblock_t)); + return; + } + } + spin_unlock_irqrestore(&block_lock, flags); + } + + slob_free((slob_t *)block - 1, 0); + return; +} + + +unsigned int ksize(const void *block) +{ + bigblock_t *bb; + unsigned long flags; + + if (!block) + return 0; + + if (!((unsigned long)block & (PAGE_SIZE-1))) { + spin_lock_irqsave(&block_lock, flags); + for (bb = bigblocks; bb; bb = bb->next) + if (bb->pages == block) { + spin_unlock_irqrestore(&slob_lock, flags); + return PAGE_SIZE << bb->order; + } + spin_unlock_irqrestore(&block_lock, flags); + } + + return ((slob_t *)block - 1)->units * SLOB_UNIT; +} + + + diff --git a/lab8_practice/kern/mm/kmalloc.h b/lab8_practice/kern/mm/kmalloc.h new file mode 100644 index 0000000..0fd2628 --- /dev/null +++ b/lab8_practice/kern/mm/kmalloc.h @@ -0,0 +1,16 @@ +#ifndef __KERN_MM_KMALLOC_H__ +#define __KERN_MM_KMALLOC_H__ + +#include + +#define KMALLOC_MAX_ORDER 10 + +void kmalloc_init(void); + +void *kmalloc(size_t n); +void kfree(void *objp); + +size_t kallocated(void); + +#endif /* !__KERN_MM_KMALLOC_H__ */ + diff --git a/lab8_practice/kern/mm/memlayout.h b/lab8_practice/kern/mm/memlayout.h new file mode 100644 index 0000000..86c5c18 --- /dev/null +++ b/lab8_practice/kern/mm/memlayout.h @@ -0,0 +1,129 @@ +#ifndef __KERN_MM_MEMLAYOUT_H__ +#define __KERN_MM_MEMLAYOUT_H__ + +/* This file contains the definitions for memory management in our OS. */ + +/* * + * Virtual memory map: Permissions + * kernel/user + * + * 4G ------------------> +---------------------------------+ + * | | + * | Empty Memory (*) | + * | | + * +---------------------------------+ 0xFB000000 + * | Cur. Page Table (Kern, RW) | RW/-- PTSIZE + * VPT -----------------> +---------------------------------+ 0xFAC00000 + * | Invalid Memory (*) | --/-- + * KERNTOP -------------> +---------------------------------+ 0xF8000000 + * | | + * | Remapped Physical Memory | RW/-- KMEMSIZE + * | | + * KERNBASE ------------> +---------------------------------+ 0xC0000000 + * | Invalid Memory (*) | --/-- + * USERTOP -------------> +---------------------------------+ 0xB0000000 + * | User stack | + * +---------------------------------+ + * | | + * : : + * | ~~~~~~~~~~~~~~~~ | + * : : + * | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | User Program & Heap | + * UTEXT ---------------> +---------------------------------+ 0x00800000 + * | Invalid Memory (*) | --/-- + * | - - - - - - - - - - - - - - - | + * | User STAB Data (optional) | + * USERBASE, USTAB------> +---------------------------------+ 0x00200000 + * | Invalid Memory (*) | --/-- + * 0 -------------------> +---------------------------------+ 0x00000000 + * (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped. + * "Empty Memory" is normally unmapped, but user programs may map pages + * there if desired. + * + * */ + +/* All physical memory mapped at this address */ +#define KERNBASE 0xFFFFFFFFC0200000 +#define KMEMSIZE 0x7E00000 // the maximum amount of physical memory +#define KERNTOP (KERNBASE + KMEMSIZE) + +#define KERNEL_BEGIN_PADDR 0x80200000 +#define KERNEL_BEGIN_VADDR 0xFFFFFFFFC0200000 +#define PHYSICAL_MEMORY_END 0x8800000 +/* * + * Virtual page table. Entry PDX[VPT] in the PD (Page Directory) contains + * a pointer to the page directory itself, thereby turning the PD into a page + * table, which maps all the PTEs (Page Table Entry) containing the page mappings + * for the entire virtual address space into that 4 Meg region starting at VPT. + * */ + +#define KSTACKPAGE 2 // # of pages in kernel stack +#define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack + +#define USERTOP 0x80000000 +#define USTACKTOP USERTOP +#define USTACKPAGE 256 // # of pages in user stack +#define USTACKSIZE (USTACKPAGE * PGSIZE) // sizeof user stack + +#define USERBASE 0x00200000 +#define UTEXT 0x00800000 // where user programs generally begin +#define USTAB USERBASE // the location of the user STABS data structure + +#define USER_ACCESS(start, end) \ +(USERBASE <= (start) && (start) < (end) && (end) <= USERTOP) + +#define KERN_ACCESS(start, end) \ +(KERNBASE <= (start) && (start) < (end) && (end) <= KERNTOP) + +#ifndef __ASSEMBLER__ + +#include +#include +#include + +typedef uintptr_t pte_t; +typedef uintptr_t pde_t; +typedef pte_t swap_entry_t; //the pte can also be a swap entry + +/* * + * struct Page - Page descriptor structures. Each Page describes one + * physical page. In kern/mm/pmm.h, you can find lots of useful functions + * that convert Page to other data types, such as phyical address. + * */ +struct Page { + int ref; // page frame's reference counter + uint64_t flags; // array of flags that describe the status of the page frame + unsigned int property; // the num of free block, used in first fit pm manager + list_entry_t page_link; // free list link + list_entry_t pra_page_link; // used for pra (page replace algorithm) + uintptr_t pra_vaddr; // used for pra (page replace algorithm) +}; + +/* Flags describing the status of a page frame */ +#define PG_reserved 0 // if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0 +#define PG_property 1 // if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page. + +#define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags)) +#define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags)) +#define PageReserved(page) test_bit(PG_reserved, &((page)->flags)) +#define SetPageProperty(page) set_bit(PG_property, &((page)->flags)) +#define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags)) +#define PageProperty(page) test_bit(PG_property, &((page)->flags)) + +// convert list entry to page +#define le2page(le, member) \ + to_struct((le), struct Page, member) + +/* free_area_t - maintains a doubly linked list to record free (unused) pages */ +typedef struct { + list_entry_t free_list; // the list header + unsigned int nr_free; // # of free pages in this free list +} free_area_t; + + +#endif /* !__ASSEMBLER__ */ + +#endif /* !__KERN_MM_MEMLAYOUT_H__ */ + diff --git a/lab8_practice/kern/mm/mmu.h b/lab8_practice/kern/mm/mmu.h new file mode 100644 index 0000000..31923a5 --- /dev/null +++ b/lab8_practice/kern/mm/mmu.h @@ -0,0 +1,91 @@ +#ifndef __KERN_MM_MMU_H__ +#define __KERN_MM_MMU_H__ + +#ifndef __ASSEMBLER__ +#include +#endif + +// A linear address 'la' has a three-part structure as follows: +// +// +--------10------+-------10-------+---------12----------+ +// | Page Directory | Page Table | Offset within Page | +// | Index | Index | | +// +----------------+----------------+---------------------+ +// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/ +// \----------- PPN(la) -----------/ +// +// The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown. +// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la), +// use PGADDR(PDX(la), PTX(la), PGOFF(la)). + +// RISC-V uses 32-bit virtual address to access 34-bit physical address! +// Sv32 page table entry: +// +---------12----------+--------10-------+---2----+-------8-------+ +// | PPN[1] | PPN[0] |Reserved|D|A|G|U|X|W|R|V| +// +---------12----------+-----------------+--------+---------------+ + +/* + * RV32Sv32 page table entry: + * | 31 10 | 9 7 | 6 | 5 | 4 1 | 0 + * PFN reserved for SW D R TYPE V + * + * RV64Sv39 / RV64Sv48 page table entry: + * | 63 48 | 47 10 | 9 7 | 6 | 5 | 4 1 | 0 + * reserved for HW PFN reserved for SW D R TYPE V + */ + +// page directory index +#define PDX1(la) ((((uintptr_t)(la)) >> PDX1SHIFT) & 0x1FF) +#define PDX0(la) ((((uintptr_t)(la)) >> PDX0SHIFT) & 0x1FF) + +// page table index +#define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x1FF) + +// page number field of address +#define PPN(la) (((uintptr_t)(la)) >> PTXSHIFT) + +// offset in page +#define PGOFF(la) (((uintptr_t)(la)) & 0xFFF) + +// construct linear address from indexes and offset +#define PGADDR(d1, d0, t, o) ((uintptr_t)((d1) << PDX1SHIFT | (d0) << PDX0SHIFT | (t) << PTXSHIFT | (o))) + +// address in page table or page directory entry +#define PTE_ADDR(pte) (((uintptr_t)(pte) & ~0x3FF) << (PTXSHIFT - PTE_PPN_SHIFT)) +#define PDE_ADDR(pde) PTE_ADDR(pde) + +/* page directory and page table constants */ +#define NPDEENTRY 512 // page directory entries per page directory +#define NPTEENTRY 512 // page table entries per page table + +#define PGSIZE 4096 // bytes mapped by a page +#define PGSHIFT 12 // log2(PGSIZE) +#define PTSIZE (PGSIZE * NPTEENTRY) // bytes mapped by a page directory entry +#define PTSHIFT 21 // log2(PTSIZE) + +#define PTXSHIFT 12 // offset of PTX in a linear address +#define PDX0SHIFT 21 // offset of PDX in a linear address +#define PDX1SHIFT 30 +#define PTE_PPN_SHIFT 10 // offset of PPN in a physical address + +// page table entry (PTE) fields +#define PTE_V 0x001 // Valid +#define PTE_R 0x002 // Read +#define PTE_W 0x004 // Write +#define PTE_X 0x008 // Execute +#define PTE_U 0x010 // User +#define PTE_G 0x020 // Global +#define PTE_A 0x040 // Accessed +#define PTE_D 0x080 // Dirty +#define PTE_SOFT 0x300 // Reserved for Software + +#define PAGE_TABLE_DIR (PTE_V) +#define READ_ONLY (PTE_R | PTE_V) +#define READ_WRITE (PTE_R | PTE_W | PTE_V) +#define EXEC_ONLY (PTE_X | PTE_V) +#define READ_EXEC (PTE_R | PTE_X | PTE_V) +#define READ_WRITE_EXEC (PTE_R | PTE_W | PTE_X | PTE_V) + +#define PTE_USER (PTE_R | PTE_W | PTE_X | PTE_U | PTE_V) + +#endif /* !__KERN_MM_MMU_H__ */ diff --git a/lab8_practice/kern/mm/pmm.c b/lab8_practice/kern/mm/pmm.c new file mode 100644 index 0000000..fd18a60 --- /dev/null +++ b/lab8_practice/kern/mm/pmm.c @@ -0,0 +1,625 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// virtual address of physical page array +struct Page *pages; +// amount of physical memory (in pages) +size_t npage = 0; +// The kernel image is mapped at VA=KERNBASE and PA=info.base +uint_t va_pa_offset; +// memory starts at 0x80000000 in RISC-V +const size_t nbase = DRAM_BASE / PGSIZE; + +// virtual address of boot-time page directory +pde_t *boot_pgdir = NULL; +// physical address of boot-time page directory +uintptr_t boot_cr3; + +// physical memory management +const struct pmm_manager *pmm_manager; + +static void check_alloc_page(void); +static void check_pgdir(void); +static void check_boot_pgdir(void); + +// init_pmm_manager - initialize a pmm_manager instance +static void init_pmm_manager(void) { + pmm_manager = &default_pmm_manager; + cprintf("memory management: %s\n", pmm_manager->name); + pmm_manager->init(); +} + +// init_memmap - call pmm->init_memmap to build Page struct for free memory +static void init_memmap(struct Page *base, size_t n) { + pmm_manager->init_memmap(base, n); +} + +// alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE +// memory +struct Page *alloc_pages(size_t n) { + struct Page *page = NULL; + bool intr_flag; + + while (1) { + local_intr_save(intr_flag); + { + page = pmm_manager->alloc_pages(n); + } + local_intr_restore(intr_flag); + + if (page != NULL || n > 1 || swap_init_ok == 0) break; + + extern struct mm_struct *check_mm_struct; + // cprintf("page %x, call swap_out in alloc_pages %d\n",page, n); + swap_out(check_mm_struct, n, 0); + } + // cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages)); + return page; +} + +// free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory +void free_pages(struct Page *base, size_t n) { + bool intr_flag; + local_intr_save(intr_flag); + { + pmm_manager->free_pages(base, n); + } + local_intr_restore(intr_flag); +} + +// nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE) +// of current free memory +size_t nr_free_pages(void) { + size_t ret; + bool intr_flag; + local_intr_save(intr_flag); + { + ret = pmm_manager->nr_free_pages(); + } + local_intr_restore(intr_flag); + return ret; +} + +/* pmm_init - initialize the physical memory management */ +static void page_init(void) { + extern char kern_entry[]; + + va_pa_offset = KERNBASE - 0x80200000; + + uint_t mem_begin = KERNEL_BEGIN_PADDR; + uint_t mem_end = PHYSICAL_MEMORY_END - KERNEL_BEGIN_PADDR; + uint_t mem_size = PHYSICAL_MEMORY_END; + + cprintf("physcial memory map:\n"); + cprintf(" memory: 0x%08lx, [0x%08lx, 0x%08lx].\n", mem_size, mem_begin, + mem_end - 1); + + uint64_t maxpa = mem_end; + + if (maxpa > KERNTOP) { + maxpa = KERNTOP; + } + + extern char end[]; + + npage = maxpa / PGSIZE; + // BBL has put the initial page table at the first available page after the + // kernel + // so stay away from it by adding extra offset to end + pages = (struct Page *)ROUNDUP((void *)end, PGSIZE); + + for (size_t i = 0; i < npage - nbase; i++) { + SetPageReserved(pages + i); + } + + uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * (npage - nbase)); + + mem_begin = ROUNDUP(freemem, PGSIZE); + mem_end = ROUNDDOWN(mem_end, PGSIZE); + if (freemem < mem_end) { + init_memmap(pa2page(mem_begin), (mem_end - mem_begin) / PGSIZE); + } + cprintf("vapaofset is %llu\n",va_pa_offset); +} + +// boot_map_segment - setup&enable the paging mechanism +// parameters +// la: linear address of this memory need to map (after x86 segment map) +// size: memory size +// pa: physical address of this memory +// perm: permission of this memory +static void boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, + uintptr_t pa, uint32_t perm) { + assert(PGOFF(la) == PGOFF(pa)); + size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE; + la = ROUNDDOWN(la, PGSIZE); + pa = ROUNDDOWN(pa, PGSIZE); + for (; n > 0; n--, la += PGSIZE, pa += PGSIZE) { + pte_t *ptep = get_pte(pgdir, la, 1); + assert(ptep != NULL); + *ptep = pte_create(pa >> PGSHIFT, PTE_V | perm); + } +} + +// boot_alloc_page - allocate one page using pmm->alloc_pages(1) +// return value: the kernel virtual address of this allocated page +// note: this function is used to get the memory for PDT(Page Directory +// Table)&PT(Page Table) +static void *boot_alloc_page(void) { + struct Page *p = alloc_page(); + if (p == NULL) { + panic("boot_alloc_page failed.\n"); + } + return page2kva(p); +} + +// pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup +// paging mechanism +// - check the correctness of pmm & paging mechanism, print PDT&PT +void pmm_init(void) { + // We need to alloc/free the physical memory (granularity is 4KB or other + // size). + // So a framework of physical memory manager (struct pmm_manager)is defined + // in pmm.h + // First we should init a physical memory manager(pmm) based on the + // framework. + // Then pmm can alloc/free the physical memory. + // Now the first_fit/best_fit/worst_fit/buddy_system pmm are available. + init_pmm_manager(); + + // detect physical memory space, reserve already used memory, + // then use pmm->init_memmap to create free page list + page_init(); + + // use pmm->check to verify the correctness of the alloc/free function in a + // pmm + check_alloc_page(); + + // create boot_pgdir, an initial page directory(Page Directory Table, PDT) + extern char boot_page_table_sv39[]; + boot_pgdir = (pte_t*)boot_page_table_sv39; + boot_cr3 = PADDR(boot_pgdir); + + check_pgdir(); + + static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0); + + // now the basic virtual memory map(see memalyout.h) is established. + // check the correctness of the basic virtual memory map. + check_boot_pgdir(); + + + kmalloc_init(); +} + +// get_pte - get pte and return the kernel virtual address of this pte for la +// - if the PT contians this pte didn't exist, alloc a page for PT +// parameter: +// pgdir: the kernel virtual base address of PDT +// la: the linear address need to map +// create: a logical value to decide if alloc a page for PT +// return vaule: the kernel virtual address of this pte +pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create) { + /* LAB2 EXERCISE 2: YOUR CODE + * + * If you need to visit a physical address, please use KADDR() + * please read pmm.h for useful macros + * + * Maybe you want help comment, BELOW comments can help you finish the code + * + * Some Useful MACROs and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la. + * KADDR(pa) : takes a physical address and returns the corresponding + * kernel virtual address. + * set_page_ref(page,1) : means the page be referenced by one time + * page2pa(page): get the physical address of memory which this (struct + * Page *) page manages + * struct Page * alloc_page() : allocation a page + * memset(void *s, char c, size_t n) : sets the first n bytes of the + * memory area pointed by s + * to the specified value c. + * DEFINEs: + * PTE_P 0x001 // page table/directory entry + * flags bit : Present + * PTE_W 0x002 // page table/directory entry + * flags bit : Writeable + * PTE_U 0x004 // page table/directory entry + * flags bit : User can access + */ + pde_t *pdep1 = &pgdir[PDX1(la)]; + if (!(*pdep1 & PTE_V)) { + struct Page *page; + if (!create || (page = alloc_page()) == NULL) { + return NULL; + } + set_page_ref(page, 1); + uintptr_t pa = page2pa(page); + memset(KADDR(pa), 0, PGSIZE); + *pdep1 = pte_create(page2ppn(page), PTE_U | PTE_V); + } + + pde_t *pdep0 = &((pde_t *)KADDR(PDE_ADDR(*pdep1)))[PDX0(la)]; + if (!(*pdep0 & PTE_V)) { + struct Page *page; + if (!create || (page = alloc_page()) == NULL) { + return NULL; + } + set_page_ref(page, 1); + uintptr_t pa = page2pa(page); + memset(KADDR(pa), 0, PGSIZE); + *pdep0 = pte_create(page2ppn(page), PTE_U | PTE_V); + } + return &((pte_t *)KADDR(PDE_ADDR(*pdep0)))[PTX(la)]; +} + +// get_page - get related Page struct for linear address la using PDT pgdir +struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) { + pte_t *ptep = get_pte(pgdir, la, 0); + if (ptep_store != NULL) { + *ptep_store = ptep; + } + if (ptep != NULL && *ptep & PTE_V) { + return pte2page(*ptep); + } + return NULL; +} + +// page_remove_pte - free an Page sturct which is related linear address la +// - and clean(invalidate) pte which is related linear address la +// note: PT is changed, so the TLB need to be invalidate +static inline void page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) { + /* LAB2 EXERCISE 3: YOUR CODE + * + * Please check if ptep is valid, and tlb must be manually updated if + * mapping is updated + * + * Maybe you want help comment, BELOW comments can help you finish the code + * + * Some Useful MACROs and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * struct Page *page pte2page(*ptep): get the according page from the + * value of a ptep + * free_page : free a page + * page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , + * then this page should be free. + * tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, + * but only if the page tables being + * edited are the ones currently in use by the + * processor. + * DEFINEs: + * PTE_P 0x001 // page table/directory entry + * flags bit : Present + */ + if (*ptep & PTE_V) { //(1) check if this page table entry is + struct Page *page = + pte2page(*ptep); //(2) find corresponding page to pte + page_ref_dec(page); //(3) decrease page reference + if (page_ref(page) == + 0) { //(4) and free this page when page reference reachs 0 + free_page(page); + } + *ptep = 0; //(5) clear second page table entry + tlb_invalidate(pgdir, la); //(6) flush tlb + } +} + +void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end) { + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + assert(USER_ACCESS(start, end)); + + do { + pte_t *ptep = get_pte(pgdir, start, 0); + if (ptep == NULL) { + start = ROUNDDOWN(start + PTSIZE, PTSIZE); + continue; + } + if (*ptep != 0) { + page_remove_pte(pgdir, start, ptep); + } + start += PGSIZE; + } while (start != 0 && start < end); +} + +void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end) { + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + assert(USER_ACCESS(start, end)); + + start = ROUNDDOWN(start, PTSIZE); + do { + int pde_idx = PDX1(start); + if (pgdir[pde_idx] & PTE_V) { + free_page(pde2page(pgdir[pde_idx])); + pgdir[pde_idx] = 0; + } + start += PTSIZE; + } while (start != 0 && start < end); +} +/* copy_range - copy content of memory (start, end) of one process A to another + * process B + * @to: the addr of process B's Page Directory + * @from: the addr of process A's Page Directory + * @share: flags to indicate to dup OR share. We just use dup method, so it + * didn't be used. + * + * CALL GRAPH: copy_mm-->dup_mmap-->copy_range + */ +int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, + bool share) { + assert(start % PGSIZE == 0 && end % PGSIZE == 0); + assert(USER_ACCESS(start, end)); + // copy content by page unit. + do { + // call get_pte to find process A's pte according to the addr start + pte_t *ptep = get_pte(from, start, 0), *nptep; + if (ptep == NULL) { + start = ROUNDDOWN(start + PTSIZE, PTSIZE); + continue; + } + // call get_pte to find process B's pte according to the addr start. If + // pte is NULL, just alloc a PT + if (*ptep & PTE_V) { + if ((nptep = get_pte(to, start, 1)) == NULL) { + return -E_NO_MEM; + } + uint32_t perm = (*ptep & PTE_USER); + // get page from ptep + struct Page *page = pte2page(*ptep); + // alloc a page for process B + struct Page *npage = alloc_page(); + assert(page != NULL); + assert(npage != NULL); + int ret = 0; + /* LAB5:EXERCISE2 YOUR CODE + * replicate content of page to npage, build the map of phy addr of + * nage with the linear addr start + * + * Some Useful MACROs and DEFINEs, you can use them in below + * implementation. + * MACROs or Functions: + * page2kva(struct Page *page): return the kernel vritual addr of + * memory which page managed (SEE pmm.h) + * page_insert: build the map of phy addr of an Page with the + * linear addr la + * memcpy: typical memory copy function + * + * (1) find src_kvaddr: the kernel virtual address of page + * (2) find dst_kvaddr: the kernel virtual address of npage + * (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE + * (4) build the map of phy addr of nage with the linear addr start + */ + void *kva_src = page2kva(page); + void *kva_dst = page2kva(npage); + + memcpy(kva_dst, kva_src, PGSIZE); + + ret = page_insert(to, npage, start, perm); + assert(ret == 0); + } + start += PGSIZE; + } while (start != 0 && start < end); + return 0; +} + +// page_remove - free an Page which is related linear address la and has an +// validated pte +void page_remove(pde_t *pgdir, uintptr_t la) { + pte_t *ptep = get_pte(pgdir, la, 0); + if (ptep != NULL) { + page_remove_pte(pgdir, la, ptep); + } +} + +// page_insert - build the map of phy addr of an Page with the linear addr la +// paramemters: +// pgdir: the kernel virtual base address of PDT +// page: the Page which need to map +// la: the linear address need to map +// perm: the permission of this Page which is setted in related pte +// return value: always 0 +// note: PT is changed, so the TLB need to be invalidate +int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) { + pte_t *ptep = get_pte(pgdir, la, 1); + if (ptep == NULL) { + return -E_NO_MEM; + } + page_ref_inc(page); + if (*ptep & PTE_V) { + struct Page *p = pte2page(*ptep); + if (p == page) { + page_ref_dec(page); + } else { + page_remove_pte(pgdir, la, ptep); + } + } + *ptep = pte_create(page2ppn(page), PTE_V | perm); + tlb_invalidate(pgdir, la); + return 0; +} + +// invalidate a TLB entry, but only if the page tables being +// edited are the ones currently in use by the processor. +void tlb_invalidate(pde_t *pgdir, uintptr_t la) { + asm volatile("sfence.vma %0" : : "r"(la)); +} + +// pgdir_alloc_page - call alloc_page & page_insert functions to +// - allocate a page size memory & setup an addr map +// - pa<->la with linear address la and the PDT pgdir +struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) { + struct Page *page = alloc_page(); + if (page != NULL) { + if (page_insert(pgdir, page, la, perm) != 0) { + free_page(page); + return NULL; + } + if (swap_init_ok) { + if (check_mm_struct != NULL) { + swap_map_swappable(check_mm_struct, la, page, 0); + page->pra_vaddr = la; + assert(page_ref(page) == 1); + // cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, + // pra_link_next %x in pgdir_alloc_page\n", (page-pages), + // page->pra_vaddr,page->pra_page_link.prev, + // page->pra_page_link.next); + } else { // now current is existed, should fix it in the future + // swap_map_swappable(current->mm, la, page, 0); + // page->pra_vaddr=la; + // assert(page_ref(page) == 1); + // panic("pgdir_alloc_page: no pages. now current is existed, + // should fix it in the future\n"); + } + } + } + + return page; +} + +static void check_alloc_page(void) { + pmm_manager->check(); + cprintf("check_alloc_page() succeeded!\n"); +} + +static void check_pgdir(void) { + // assert(npage <= KMEMSIZE / PGSIZE); + // The memory starts at 2GB in RISC-V + // so npage is always larger than KMEMSIZE / PGSIZE + assert(npage <= KERNTOP / PGSIZE); + assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0); + assert(get_page(boot_pgdir, 0x0, NULL) == NULL); + + struct Page *p1, *p2; + p1 = alloc_page(); + assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0); + + pte_t *ptep; + assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL); + assert(pte2page(*ptep) == p1); + assert(page_ref(p1) == 1); + + ptep = (pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])); + ptep = (pte_t *)KADDR(PDE_ADDR(ptep[0])) + 1; + assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep); + + p2 = alloc_page(); + assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_U | PTE_W) == 0); + assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL); + assert(*ptep & PTE_U); + assert(*ptep & PTE_W); + assert(boot_pgdir[0] & PTE_U); + assert(page_ref(p2) == 1); + + assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0); + assert(page_ref(p1) == 2); + assert(page_ref(p2) == 0); + assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL); + assert(pte2page(*ptep) == p1); + assert((*ptep & PTE_U) == 0); + + page_remove(boot_pgdir, 0x0); + assert(page_ref(p1) == 1); + assert(page_ref(p2) == 0); + + page_remove(boot_pgdir, PGSIZE); + assert(page_ref(p1) == 0); + assert(page_ref(p2) == 0); + + assert(page_ref(pde2page(boot_pgdir[0])) == 1); + free_page(pde2page(boot_pgdir[0])); + boot_pgdir[0] = 0; + + cprintf("check_pgdir() succeeded!\n"); +} + +static void check_boot_pgdir(void) { + pte_t *ptep; + int i; + for (i = ROUNDDOWN(KERNBASE, PGSIZE); i < npage * PGSIZE; i += PGSIZE) { + assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL); + assert(PTE_ADDR(*ptep) == i); + } + + + assert(boot_pgdir[0] == 0); + + struct Page *p; + p = alloc_page(); + assert(page_insert(boot_pgdir, p, 0x100, PTE_W | PTE_R) == 0); + assert(page_ref(p) == 1); + assert(page_insert(boot_pgdir, p, 0x100 + PGSIZE, PTE_W | PTE_R) == 0); + assert(page_ref(p) == 2); + + const char *str = "ucore: Hello world!!"; + strcpy((void *)0x100, str); + assert(strcmp((void *)0x100, (void *)(0x100 + PGSIZE)) == 0); + + *(char *)(page2kva(p) + 0x100) = '\0'; + assert(strlen((const char *)0x100) == 0); + + free_page(p); + free_page(pde2page(boot_pgdir[0])); + boot_pgdir[0] = 0; + + cprintf("check_boot_pgdir() succeeded!\n"); +} + +// perm2str - use string 'u,r,w,-' to present the permission +static const char *perm2str(int perm) { + static char str[4]; + str[0] = (perm & PTE_U) ? 'u' : '-'; + str[1] = 'r'; + str[2] = (perm & PTE_W) ? 'w' : '-'; + str[3] = '\0'; + return str; +} + +// get_pgtable_items - In [left, right] range of PDT or PT, find a continuous +// linear addr space +// - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT +// - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT +// paramemters: +// left: no use ??? +// right: the high side of table's range +// start: the low side of table's range +// table: the beginning addr of table +// left_store: the pointer of the high side of table's next range +// right_store: the pointer of the low side of table's next range +// return value: 0 - not a invalid item range, perm - a valid item range with +// perm permission +static int get_pgtable_items(size_t left, size_t right, size_t start, + uintptr_t *table, size_t *left_store, + size_t *right_store) { + if (start >= right) { + return 0; + } + while (start < right && !(table[start] & PTE_V)) { + start++; + } + if (start < right) { + if (left_store != NULL) { + *left_store = start; + } + int perm = (table[start++] & PTE_USER); + while (start < right && (table[start] & PTE_USER) == perm) { + start++; + } + if (right_store != NULL) { + *right_store = start; + } + return perm; + } + return 0; +} + diff --git a/lab8_practice/kern/mm/pmm.h b/lab8_practice/kern/mm/pmm.h new file mode 100644 index 0000000..0559df3 --- /dev/null +++ b/lab8_practice/kern/mm/pmm.h @@ -0,0 +1,164 @@ +#ifndef __KERN_MM_PMM_H__ +#define __KERN_MM_PMM_H__ + +#include +#include +#include +#include +#include + +// pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager +// only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used +// by ucore to manage the total physical memory space. +struct pmm_manager { + const char *name; // XXX_pmm_manager's name + void (*init)(void); // initialize internal description&management data structure + // (free block list, number of free block) of XXX_pmm_manager + void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to + // the initial free physical memory space + struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm + void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h) + size_t (*nr_free_pages)(void); // return the number of free pages + void (*check)(void); // check the correctness of XXX_pmm_manager +}; + +extern const struct pmm_manager *pmm_manager; +extern pde_t *boot_pgdir; +extern const size_t nbase; +extern uintptr_t boot_cr3; + +void pmm_init(void); + +struct Page *alloc_pages(size_t n); +void free_pages(struct Page *base, size_t n); +size_t nr_free_pages(void); + +#define alloc_page() alloc_pages(1) +#define free_page(page) free_pages(page, 1) + +pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create); +struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store); +void page_remove(pde_t *pgdir, uintptr_t la); +int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm); + +void load_esp0(uintptr_t esp0); +void tlb_invalidate(pde_t *pgdir, uintptr_t la); +struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm); +void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end); +void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end); +int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share); + +void print_pgdir(void); + +/* * + * PADDR - takes a kernel virtual address (an address that points above KERNBASE), + * where the machine's maximum 256MB of physical memory is mapped and returns the + * corresponding physical address. It panics if you pass it a non-kernel virtual address. + * */ +#define PADDR(kva) \ + ({ \ + uintptr_t __m_kva = (uintptr_t)(kva); \ + if (__m_kva < KERNBASE) { \ + panic("PADDR called with invalid kva %08lx", __m_kva); \ + } \ + __m_kva - va_pa_offset; \ + }) + +/* * + * KADDR - takes a physical address and returns the corresponding kernel virtual + * address. It panics if you pass an invalid physical address. + * */ +#define KADDR(pa) \ + ({ \ + uintptr_t __m_pa = (pa); \ + size_t __m_ppn = PPN(__m_pa); \ + if (__m_ppn >= npage) { \ + panic("KADDR called with invalid pa %08lx", __m_pa); \ + } \ + (void *)(__m_pa + va_pa_offset); \ + }) + +extern struct Page *pages; +extern size_t npage; +extern uint_t va_pa_offset; + +static inline ppn_t +page2ppn(struct Page *page) { + return page - pages + nbase; +} + +static inline uintptr_t +page2pa(struct Page *page) { + return page2ppn(page) << PGSHIFT; +} + +static inline struct Page * +pa2page(uintptr_t pa) { + if (PPN(pa) >= npage) { + panic("pa2page called with invalid pa"); + } + return &pages[PPN(pa) - nbase]; +} + +static inline void * +page2kva(struct Page *page) { + return KADDR(page2pa(page)); +} + +static inline struct Page * +kva2page(void *kva) { + return pa2page(PADDR(kva)); +} + +static inline struct Page * +pte2page(pte_t pte) { + if (!(pte & PTE_V)) { + panic("pte2page called with invalid pte"); + } + return pa2page(PTE_ADDR(pte)); +} + +static inline struct Page * +pde2page(pde_t pde) { + return pa2page(PDE_ADDR(pde)); +} + +static inline int +page_ref(struct Page *page) { + return page->ref; +} + +static inline void +set_page_ref(struct Page *page, int val) { + page->ref = val; +} + +static inline int +page_ref_inc(struct Page *page) { + page->ref += 1; + return page->ref; +} + +static inline int +page_ref_dec(struct Page *page) { + page->ref -= 1; + return page->ref; +} + +static inline void flush_tlb() { + asm volatile("sfence.vm"); +} + +// construct PTE from a page and permission bits +static inline pte_t pte_create(uintptr_t ppn, int type) { + return (ppn << PTE_PPN_SHIFT) | PTE_V | type; +} + +static inline pte_t ptd_create(uintptr_t ppn) { + return pte_create(ppn, PTE_V); +} + +extern char bootstack[], bootstacktop[]; + +#endif /* !__KERN_MM_PMM_H__ */ + diff --git a/lab8_practice/kern/mm/swap.c b/lab8_practice/kern/mm/swap.c new file mode 100644 index 0000000..c9597ea --- /dev/null +++ b/lab8_practice/kern/mm/swap.c @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// the valid vaddr for check is between 0~CHECK_VALID_VADDR-1 +#define CHECK_VALID_VIR_PAGE_NUM 5 +#define BEING_CHECK_VALID_VADDR 0X1000 +#define CHECK_VALID_VADDR (CHECK_VALID_VIR_PAGE_NUM+1)*0x1000 +// the max number of valid physical page for check +#define CHECK_VALID_PHY_PAGE_NUM 4 +// the max access seq number +#define MAX_SEQ_NO 10 + +static struct swap_manager *sm; +size_t max_swap_offset; + +volatile int swap_init_ok = 0; + +unsigned int swap_page[CHECK_VALID_VIR_PAGE_NUM]; + +unsigned int swap_in_seq_no[MAX_SEQ_NO],swap_out_seq_no[MAX_SEQ_NO]; + +static void check_swap(void); + +int +swap_init(void) +{ + swapfs_init(); + + // Since the IDE is faked, it can only store 7 pages at most to pass the test + if (!(7 <= max_swap_offset && + max_swap_offset < MAX_SWAP_OFFSET_LIMIT)) { + panic("bad max_swap_offset %08x.\n", max_swap_offset); + } + + + sm = &swap_manager_fifo; + int r = sm->init(); + + if (r == 0) + { + swap_init_ok = 1; + cprintf("SWAP: manager = %s\n", sm->name); + check_swap(); + } + + return r; +} + +int +swap_init_mm(struct mm_struct *mm) +{ + return sm->init_mm(mm); +} + +int +swap_tick_event(struct mm_struct *mm) +{ + return sm->tick_event(mm); +} + +int +swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) +{ + return sm->map_swappable(mm, addr, page, swap_in); +} + +int +swap_set_unswappable(struct mm_struct *mm, uintptr_t addr) +{ + return sm->set_unswappable(mm, addr); +} + +volatile unsigned int swap_out_num=0; + +int +swap_out(struct mm_struct *mm, int n, int in_tick) +{ + int i; + for (i = 0; i != n; ++ i) + { + uintptr_t v; + //struct Page **ptr_page=NULL; + struct Page *page; + // cprintf("i %d, SWAP: call swap_out_victim\n",i); + int r = sm->swap_out_victim(mm, &page, in_tick); + if (r != 0) { + cprintf("i %d, swap_out: call swap_out_victim failed\n",i); + break; + } + //assert(!PageReserved(page)); + + //cprintf("SWAP: choose victim page 0x%08x\n", page); + + v=page->pra_vaddr; + pte_t *ptep = get_pte(mm->pgdir, v, 0); + assert((*ptep & PTE_V) != 0); + + if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) { + cprintf("SWAP: failed to save\n"); + sm->map_swappable(mm, v, page, 0); + continue; + } + else { + cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1); + *ptep = (page->pra_vaddr/PGSIZE+1)<<8; + free_page(page); + } + + tlb_invalidate(mm->pgdir, v); + } + return i; +} + +int +swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result) +{ + struct Page *result = alloc_page(); + assert(result!=NULL); + + pte_t *ptep = get_pte(mm->pgdir, addr, 0); + // cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages)); + + int r; + if ((r = swapfs_read((*ptep), result)) != 0) + { + assert(r!=0); + } + cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr); + *ptr_result=result; + return 0; +} + + + +static inline void +check_content_set(void) +{ + *(unsigned char *)0x1000 = 0x0a; + assert(pgfault_num==1); + *(unsigned char *)0x1010 = 0x0a; + assert(pgfault_num==1); + *(unsigned char *)0x2000 = 0x0b; + assert(pgfault_num==2); + *(unsigned char *)0x2010 = 0x0b; + assert(pgfault_num==2); + *(unsigned char *)0x3000 = 0x0c; + assert(pgfault_num==3); + *(unsigned char *)0x3010 = 0x0c; + assert(pgfault_num==3); + *(unsigned char *)0x4000 = 0x0d; + assert(pgfault_num==4); + *(unsigned char *)0x4010 = 0x0d; + assert(pgfault_num==4); +} + +static inline int +check_content_access(void) +{ + int ret = sm->check_swap(); + return ret; +} + +struct Page * check_rp[CHECK_VALID_PHY_PAGE_NUM]; +pte_t * check_ptep[CHECK_VALID_PHY_PAGE_NUM]; +unsigned int check_swap_addr[CHECK_VALID_VIR_PAGE_NUM]; + +extern free_area_t free_area; + +#define free_list (free_area.free_list) +#define nr_free (free_area.nr_free) + +static void +check_swap(void) +{ + //backup mem env + int ret, count = 0, total = 0, i; + list_entry_t *le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + assert(PageProperty(p)); + count ++, total += p->property; + } + assert(total == nr_free_pages()); + cprintf("BEGIN check_swap: count %d, total %d\n",count,total); + + //now we set the phy pages env + struct mm_struct *mm = mm_create(); + assert(mm != NULL); + + extern struct mm_struct *check_mm_struct; + assert(check_mm_struct == NULL); + + check_mm_struct = mm; + + pde_t *pgdir = mm->pgdir = boot_pgdir; + assert(pgdir[0] == 0); + + struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ); + assert(vma != NULL); + + insert_vma_struct(mm, vma); + + //setup the temp Page Table vaddr 0~4MB + cprintf("setup Page Table for vaddr 0X1000, so alloc a page\n"); + pte_t *temp_ptep=NULL; + temp_ptep = get_pte(mm->pgdir, BEING_CHECK_VALID_VADDR, 1); + assert(temp_ptep!= NULL); + cprintf("setup Page Table vaddr 0~4MB OVER!\n"); + + for (i=0;iphy_page environment for page relpacement algorithm + + + pgfault_num=0; + + check_content_set(); + assert( nr_free == 0); + for(i = 0; ipgdir = NULL; + mm_destroy(mm); + check_mm_struct = NULL; + + nr_free = nr_free_store; + free_list = free_list_store; + + + le = &free_list; + while ((le = list_next(le)) != &free_list) { + struct Page *p = le2page(le, page_link); + count --, total -= p->property; + } + cprintf("count is %d, total is %d\n",count,total); + //assert(count == 0); + + cprintf("check_swap() succeeded!\n"); +} diff --git a/lab8_practice/kern/mm/swap.h b/lab8_practice/kern/mm/swap.h new file mode 100644 index 0000000..5d4aea8 --- /dev/null +++ b/lab8_practice/kern/mm/swap.h @@ -0,0 +1,65 @@ +#ifndef __KERN_MM_SWAP_H__ +#define __KERN_MM_SWAP_H__ + +#include +#include +#include +#include + +/* * + * swap_entry_t + * -------------------------------------------- + * | offset | reserved | 0 | + * -------------------------------------------- + * 24 bits 7 bits 1 bit + * */ + +#define MAX_SWAP_OFFSET_LIMIT (1 << 24) + +extern size_t max_swap_offset; + +/* * + * swap_offset - takes a swap_entry (saved in pte), and returns + * the corresponding offset in swap mem_map. + * */ +#define swap_offset(entry) ({ \ + size_t __offset = (entry >> 8); \ + if (!(__offset > 0 && __offset < max_swap_offset)) { \ + panic("invalid swap_entry_t = %08x.\n", entry); \ + } \ + __offset; \ + }) + +struct swap_manager +{ + const char *name; + /* Global initialization for the swap manager */ + int (*init) (void); + /* Initialize the priv data inside mm_struct */ + int (*init_mm) (struct mm_struct *mm); + /* Called when tick interrupt occured */ + int (*tick_event) (struct mm_struct *mm); + /* Called when map a swappable page into the mm_struct */ + int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in); + /* When a page is marked as shared, this routine is called to + * delete the addr entry from the swap manager */ + int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr); + /* Try to swap out a page, return then victim */ + int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick); + /* check the page relpacement algorithm */ + int (*check_swap)(void); +}; + +extern volatile int swap_init_ok; +int swap_init(void); +int swap_init_mm(struct mm_struct *mm); +int swap_tick_event(struct mm_struct *mm); +int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in); +int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr); +int swap_out(struct mm_struct *mm, int n, int in_tick); +int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result); + +//#define MEMBER_OFFSET(m,t) ((int)(&((t *)0)->m)) +//#define FROM_MEMBER(m,t,a) ((t *)((char *)(a) - MEMBER_OFFSET(m,t))) + +#endif diff --git a/lab8_practice/kern/mm/swap_fifo.c b/lab8_practice/kern/mm/swap_fifo.c new file mode 100644 index 0000000..ee3a3e9 --- /dev/null +++ b/lab8_practice/kern/mm/swap_fifo.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + +/* [wikipedia]The simplest Page Replacement Algorithm(PRA) is a FIFO algorithm. The first-in, first-out + * page replacement algorithm is a low-overhead algorithm that requires little book-keeping on + * the part of the operating system. The idea is obvious from the name - the operating system + * keeps track of all the pages in memory in a queue, with the most recent arrival at the back, + * and the earliest arrival in front. When a page needs to be replaced, the page at the front + * of the queue (the oldest page) is selected. While FIFO is cheap and intuitive, it performs + * poorly in practical application. Thus, it is rarely used in its unmodified form. This + * algorithm experiences Belady's anomaly. + * + * Details of FIFO PRA + * (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can + * link these pages into pra_list_head according the time order. At first you should + * be familiar to the struct list in list.h. struct list is a simple doubly linked list + * implementation. You should know howto USE: list_init, list_add(list_add_after), + * list_add_before, list_del, list_next, list_prev. Another tricky method is to transform + * a general list struct to a special struct (such as struct page). You can find some MACRO: + * le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc. + */ + +list_entry_t pra_list_head; +/* + * (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head. + * Now, From the memory control struct mm_struct, we can access FIFO PRA + */ +static int +_fifo_init_mm(struct mm_struct *mm) +{ + list_init(&pra_list_head); + mm->sm_priv = &pra_list_head; + //cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv); + return 0; +} +/* + * (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue + */ +static int +_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) +{ + list_entry_t *head=(list_entry_t*) mm->sm_priv; + list_entry_t *entry=&(page->pra_page_link); + + assert(entry != NULL && head != NULL); + //record the page access situlation + /*LAB3 EXERCISE 2: YOUR CODE*/ + //(1)link the most recent arrival page at the back of the pra_list_head qeueue. + list_add_before(head, entry); + return 0; +} +/* + * (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue, + * then set the addr of addr of this page to ptr_page. + */ +static int +_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) +{ + list_entry_t *head=(list_entry_t*) mm->sm_priv; + assert(head != NULL); + assert(in_tick==0); + /* Select the victim */ + /*LAB3 EXERCISE 2: YOUR CODE*/ + //(1) unlink the earliest arrival page in front of pra_list_head qeueue + //(2) set the addr of addr of this page to ptr_page + list_entry_t* entry = list_next(head); + list_del(entry); + *ptr_page = le2page(entry, pra_page_link); + return 0; +} + +static int +_fifo_check_swap(void) { + cprintf("write Virt Page c in fifo_check_swap\n"); + *(unsigned char *)0x3000 = 0x0c; + assert(pgfault_num==4); + cprintf("write Virt Page a in fifo_check_swap\n"); + *(unsigned char *)0x1000 = 0x0a; + assert(pgfault_num==4); + cprintf("write Virt Page d in fifo_check_swap\n"); + *(unsigned char *)0x4000 = 0x0d; + assert(pgfault_num==4); + cprintf("write Virt Page b in fifo_check_swap\n"); + *(unsigned char *)0x2000 = 0x0b; + assert(pgfault_num==4); + cprintf("write Virt Page e in fifo_check_swap\n"); + *(unsigned char *)0x5000 = 0x0e; + assert(pgfault_num==5); + cprintf("write Virt Page b in fifo_check_swap\n"); + *(unsigned char *)0x2000 = 0x0b; + assert(pgfault_num==5); + cprintf("write Virt Page a in fifo_check_swap\n"); + *(unsigned char *)0x1000 = 0x0a; + assert(pgfault_num==6); + cprintf("write Virt Page b in fifo_check_swap\n"); + *(unsigned char *)0x2000 = 0x0b; + assert(pgfault_num==7); + cprintf("write Virt Page c in fifo_check_swap\n"); + *(unsigned char *)0x3000 = 0x0c; + assert(pgfault_num==8); + cprintf("write Virt Page d in fifo_check_swap\n"); + *(unsigned char *)0x4000 = 0x0d; + assert(pgfault_num==9); + cprintf("write Virt Page e in fifo_check_swap\n"); + *(unsigned char *)0x5000 = 0x0e; + assert(pgfault_num==10); + cprintf("write Virt Page a in fifo_check_swap\n"); + assert(*(unsigned char *)0x1000 == 0x0a); + *(unsigned char *)0x1000 = 0x0a; + assert(pgfault_num==11); + return 0; +} + + +static int +_fifo_init(void) +{ + return 0; +} + +static int +_fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr) +{ + return 0; +} + +static int +_fifo_tick_event(struct mm_struct *mm) +{ return 0; } + + +struct swap_manager swap_manager_fifo = +{ + .name = "fifo swap manager", + .init = &_fifo_init, + .init_mm = &_fifo_init_mm, + .tick_event = &_fifo_tick_event, + .map_swappable = &_fifo_map_swappable, + .set_unswappable = &_fifo_set_unswappable, + .swap_out_victim = &_fifo_swap_out_victim, + .check_swap = &_fifo_check_swap, +}; diff --git a/lab8_practice/kern/mm/swap_fifo.h b/lab8_practice/kern/mm/swap_fifo.h new file mode 100644 index 0000000..1d74269 --- /dev/null +++ b/lab8_practice/kern/mm/swap_fifo.h @@ -0,0 +1,7 @@ +#ifndef __KERN_MM_SWAP_FIFO_H__ +#define __KERN_MM_SWAP_FIFO_H__ + +#include +extern struct swap_manager swap_manager_fifo; + +#endif diff --git a/lab8_practice/kern/mm/vmm.c b/lab8_practice/kern/mm/vmm.c new file mode 100644 index 0000000..a96d374 --- /dev/null +++ b/lab8_practice/kern/mm/vmm.c @@ -0,0 +1,522 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + vmm design include two parts: mm_struct (mm) & vma_struct (vma) + mm is the memory manager for the set of continuous virtual memory + area which have the same PDT. vma is a continuous virtual memory area. + There a linear link list for vma & a redblack link list for vma in mm. +--------------- + mm related functions: + golbal functions + struct mm_struct * mm_create(void) + void mm_destroy(struct mm_struct *mm) + int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) +-------------- + vma related functions: + global functions + struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...) + void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) + struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr) + local functions + inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) +--------------- + check correctness functions + void check_vmm(void); + void check_vma_struct(void); + void check_pgfault(void); +*/ + +static void check_vmm(void); +static void check_vma_struct(void); +static void check_pgfault(void); + +// mm_create - alloc a mm_struct & initialize it. +struct mm_struct * +mm_create(void) { + struct mm_struct *mm = kmalloc(sizeof(struct mm_struct)); + + if (mm != NULL) { + list_init(&(mm->mmap_list)); + mm->mmap_cache = NULL; + mm->pgdir = NULL; + mm->map_count = 0; + + if (swap_init_ok) swap_init_mm(mm); + else mm->sm_priv = NULL; + + set_mm_count(mm, 0); + sem_init(&(mm->mm_sem), 1); + } + return mm; +} + +// vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end) +struct vma_struct * +vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) { + struct vma_struct *vma = kmalloc(sizeof(struct vma_struct)); + + if (vma != NULL) { + vma->vm_start = vm_start; + vma->vm_end = vm_end; + vma->vm_flags = vm_flags; + } + return vma; +} + + +// find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end) +struct vma_struct * +find_vma(struct mm_struct *mm, uintptr_t addr) { + struct vma_struct *vma = NULL; + if (mm != NULL) { + vma = mm->mmap_cache; + if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) { + bool found = 0; + list_entry_t *list = &(mm->mmap_list), *le = list; + while ((le = list_next(le)) != list) { + vma = le2vma(le, list_link); + if (vma->vm_start<=addr && addr < vma->vm_end) { + found = 1; + break; + } + } + if (!found) { + vma = NULL; + } + } + if (vma != NULL) { + mm->mmap_cache = vma; + } + } + return vma; +} + + +// check_vma_overlap - check if vma1 overlaps vma2 ? +static inline void +check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) { + assert(prev->vm_start < prev->vm_end); + assert(prev->vm_end <= next->vm_start); + assert(next->vm_start < next->vm_end); +} + + +// insert_vma_struct -insert vma in mm's list link +void +insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) { + assert(vma->vm_start < vma->vm_end); + list_entry_t *list = &(mm->mmap_list); + list_entry_t *le_prev = list, *le_next; + + list_entry_t *le = list; + while ((le = list_next(le)) != list) { + struct vma_struct *mmap_prev = le2vma(le, list_link); + if (mmap_prev->vm_start > vma->vm_start) { + break; + } + le_prev = le; + } + + le_next = list_next(le_prev); + + /* check overlap */ + if (le_prev != list) { + check_vma_overlap(le2vma(le_prev, list_link), vma); + } + if (le_next != list) { + check_vma_overlap(vma, le2vma(le_next, list_link)); + } + + vma->vm_mm = mm; + list_add_after(le_prev, &(vma->list_link)); + + mm->map_count ++; +} + +// mm_destroy - free mm and mm internal fields +void +mm_destroy(struct mm_struct *mm) { + assert(mm_count(mm) == 0); + + list_entry_t *list = &(mm->mmap_list), *le; + while ((le = list_next(list)) != list) { + list_del(le); + kfree(le2vma(le, list_link)); //kfree vma + } + kfree(mm); //kfree mm + mm=NULL; +} + +int +mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags, + struct vma_struct **vma_store) { + uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE); + if (!USER_ACCESS(start, end)) { + return -E_INVAL; + } + + assert(mm != NULL); + + int ret = -E_INVAL; + + struct vma_struct *vma; + if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) { + goto out; + } + ret = -E_NO_MEM; + + if ((vma = vma_create(start, end, vm_flags)) == NULL) { + goto out; + } + insert_vma_struct(mm, vma); + if (vma_store != NULL) { + *vma_store = vma; + } + ret = 0; + +out: + return ret; +} + +int +dup_mmap(struct mm_struct *to, struct mm_struct *from) { + assert(to != NULL && from != NULL); + list_entry_t *list = &(from->mmap_list), *le = list; + while ((le = list_prev(le)) != list) { + struct vma_struct *vma, *nvma; + vma = le2vma(le, list_link); + nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags); + if (nvma == NULL) { + return -E_NO_MEM; + } + + insert_vma_struct(to, nvma); + + bool share = 0; + if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) { + return -E_NO_MEM; + } + } + return 0; +} + +void +exit_mmap(struct mm_struct *mm) { + assert(mm != NULL && mm_count(mm) == 0); + pde_t *pgdir = mm->pgdir; + list_entry_t *list = &(mm->mmap_list), *le = list; + while ((le = list_next(le)) != list) { + struct vma_struct *vma = le2vma(le, list_link); + unmap_range(pgdir, vma->vm_start, vma->vm_end); + } + while ((le = list_next(le)) != list) { + struct vma_struct *vma = le2vma(le, list_link); + exit_range(pgdir, vma->vm_start, vma->vm_end); + } +} + +bool +copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) { + if (!user_mem_check(mm, (uintptr_t)src, len, writable)) { + return 0; + } + memcpy(dst, src, len); + return 1; +} + +bool +copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) { + if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) { + return 0; + } + memcpy(dst, src, len); + return 1; +} + +// vmm_init - initialize virtual memory management +// - now just call check_vmm to check correctness of vmm +void +vmm_init(void) { + check_vmm(); +} + +// check_vmm - check correctness of vmm +static void +check_vmm(void) { + // size_t nr_free_pages_store = nr_free_pages(); + + check_vma_struct(); + check_pgfault(); + + cprintf("check_vmm() succeeded.\n"); +} + +static void +check_vma_struct(void) { + // size_t nr_free_pages_store = nr_free_pages(); + + struct mm_struct *mm = mm_create(); + assert(mm != NULL); + + int step1 = 10, step2 = step1 * 10; + + int i; + for (i = step1; i >= 1; i --) { + struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); + assert(vma != NULL); + insert_vma_struct(mm, vma); + } + + for (i = step1 + 1; i <= step2; i ++) { + struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); + assert(vma != NULL); + insert_vma_struct(mm, vma); + } + + list_entry_t *le = list_next(&(mm->mmap_list)); + + for (i = 1; i <= step2; i ++) { + assert(le != &(mm->mmap_list)); + struct vma_struct *mmap = le2vma(le, list_link); + assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2); + le = list_next(le); + } + + for (i = 5; i <= 5 * step2; i +=5) { + struct vma_struct *vma1 = find_vma(mm, i); + assert(vma1 != NULL); + struct vma_struct *vma2 = find_vma(mm, i+1); + assert(vma2 != NULL); + struct vma_struct *vma3 = find_vma(mm, i+2); + assert(vma3 == NULL); + struct vma_struct *vma4 = find_vma(mm, i+3); + assert(vma4 == NULL); + struct vma_struct *vma5 = find_vma(mm, i+4); + assert(vma5 == NULL); + + assert(vma1->vm_start == i && vma1->vm_end == i + 2); + assert(vma2->vm_start == i && vma2->vm_end == i + 2); + } + + for (i =4; i>=0; i--) { + struct vma_struct *vma_below_5= find_vma(mm,i); + if (vma_below_5 != NULL ) { + cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end); + } + assert(vma_below_5 == NULL); + } + + mm_destroy(mm); + + cprintf("check_vma_struct() succeeded!\n"); +} + +struct mm_struct *check_mm_struct; + +// check_pgfault - check correctness of pgfault handler +static void +check_pgfault(void) { + size_t nr_free_pages_store = nr_free_pages(); + + check_mm_struct = mm_create(); + assert(check_mm_struct != NULL); + + struct mm_struct *mm = check_mm_struct; + pde_t *pgdir = mm->pgdir = boot_pgdir; + assert(pgdir[0] == 0); + + struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE); + assert(vma != NULL); + + insert_vma_struct(mm, vma); + + uintptr_t addr = 0x100; + assert(find_vma(mm, addr) == vma); + + int i, sum = 0; + + for (i = 0; i < 100; i ++) { + *(char *)(addr + i) = i; + sum += i; + } + for (i = 0; i < 100; i ++) { + sum -= *(char *)(addr + i); + } + + assert(sum == 0); + + page_remove(pgdir, ROUNDDOWN(addr, PGSIZE)); + free_page(pde2page(pgdir[0])); + pgdir[0] = 0; + + mm->pgdir = NULL; + mm_destroy(mm); + check_mm_struct = NULL; + nr_free_pages_store--; // szx : 三级页表 + + assert(nr_free_pages_store == nr_free_pages()); + + cprintf("check_pgfault() succeeded!\n"); +} +//page fault number +volatile unsigned int pgfault_num=0; + +/* do_pgfault - interrupt handler to process the page fault execption + * @mm : the control struct for a set of vma using the same PDT + * @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware + * @addr : the addr which causes a memory access exception, (the contents of the CR2 register) + * + * CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault + * The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing + * the exception and recovering from it. + * (1) The contents of the CR2 register. The processor loads the CR2 register with the + * 32-bit linear address that generated the exception. The do_pgfault fun can + * use this address to locate the corresponding page directory and page-table + * entries. + * (2) An error code on the kernel stack. The error code for a page fault has a format different from + * that for other exceptions. The error code tells the exception handler three things: + * -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0) + * or to either an access rights violation or the use of a reserved bit (1). + * -- The W/R flag (bit 1) indicates whether the memory access that caused the exception + * was a read (0) or write (1). + * -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1) + * or supervisor mode (0) at the time of the exception. + */ +int +do_pgfault(struct mm_struct *mm, uint_t error_code, uintptr_t addr) { + int ret = -E_INVAL; + //try to find a vma which include addr + struct vma_struct *vma = find_vma(mm, addr); + + pgfault_num++; + //If the addr is in the range of a mm's vma? + if (vma == NULL || vma->vm_start > addr) { + cprintf("not valid addr %x, and can not find it in vma\n", addr); + goto failed; + } + + /* IF (write an existed addr ) OR + * (write an non_existed addr && addr is writable) OR + * (read an non_existed addr && addr is readable) + * THEN + * continue process + */ + uint32_t perm = PTE_U; + if (vma->vm_flags & VM_WRITE) { + perm |= READ_WRITE; + } + addr = ROUNDDOWN(addr, PGSIZE); + + ret = -E_NO_MEM; + + pte_t *ptep=NULL; + /*LAB3 EXERCISE 1: YOUR CODE + * Maybe you want help comment, BELOW comments can help you finish the code + * + * Some Useful MACROs and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * get_pte : get an pte and return the kernel virtual address of this pte for la + * if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1') + * pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup + * an addr map pa<--->la with linear address la and the PDT pgdir + * DEFINES: + * VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable + * PTE_W 0x002 // page table/directory entry flags bit : Writeable + * PTE_U 0x004 // page table/directory entry flags bit : User can access + * VARIABLES: + * mm->pgdir : the PDT of these vma + * + */ + // try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. + // (notice the 3th parameter '1') + if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { + cprintf("get_pte in do_pgfault failed\n"); + goto failed; + } + + if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr + if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { + cprintf("pgdir_alloc_page in do_pgfault failed\n"); + goto failed; + } + } + else { // if this pte is a swap entry, then load data from disk to a page with phy addr + // and call page_insert to map the phy addr with logical addr + if(swap_init_ok) { + struct Page *page=NULL; + if ((ret = swap_in(mm, addr, &page)) != 0) { + cprintf("swap_in in do_pgfault failed\n"); + goto failed; + } + page_insert(mm->pgdir, page, addr, perm); + swap_map_swappable(mm, addr, page, 1); + page->pra_vaddr = addr; + } + else { + cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep); + goto failed; + } + } + ret = 0; +failed: + return ret; +} + +bool +user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) { + if (mm != NULL) { + if (!USER_ACCESS(addr, addr + len)) { + return 0; + } + struct vma_struct *vma; + uintptr_t start = addr, end = addr + len; + while (start < end) { + if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) { + return 0; + } + if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) { + return 0; + } + if (write && (vma->vm_flags & VM_STACK)) { + if (start < vma->vm_start + PGSIZE) { //check stack start & size + return 0; + } + } + start = vma->vm_end; + } + return 1; + } + return KERN_ACCESS(addr, addr + len); +} +bool copy_string(struct mm_struct *mm, char *dst, const char *src, + size_t maxn) { + size_t alen, + part = ROUNDDOWN((uintptr_t)src + PGSIZE, PGSIZE) - (uintptr_t)src; + while (1) { + if (part > maxn) { + part = maxn; + } + if (!user_mem_check(mm, (uintptr_t)src, part, 0)) { + return 0; + } + if ((alen = strnlen(src, part)) < part) { + memcpy(dst, src, alen + 1); + return 1; + } + if (part == maxn) { + return 0; + } + memcpy(dst, src, part); + dst += part, src += part, maxn -= part; + part = PGSIZE; + } +} diff --git a/lab8_practice/kern/mm/vmm.h b/lab8_practice/kern/mm/vmm.h new file mode 100644 index 0000000..3c73ebd --- /dev/null +++ b/lab8_practice/kern/mm/vmm.h @@ -0,0 +1,110 @@ +#ifndef __KERN_MM_VMM_H__ +#define __KERN_MM_VMM_H__ + +#include +#include +#include +#include +#include +#include +//pre define +struct mm_struct; + +// the virtual continuous memory area(vma), [vm_start, vm_end), +// addr belong to a vma means vma.vm_start<= addr mm_count; +} + +static inline void +set_mm_count(struct mm_struct *mm, int val) { + mm->mm_count = val; +} + +static inline int +mm_count_inc(struct mm_struct *mm) { + mm->mm_count += 1; + return mm->mm_count; +} + +static inline int +mm_count_dec(struct mm_struct *mm) { + mm->mm_count -= 1; + return mm->mm_count; +} + +static inline void +lock_mm(struct mm_struct *mm) { + if (mm != NULL) { + down(&(mm->mm_sem)); + if (current != NULL) { + mm->locked_by = current->pid; + } + } +} + +static inline void +unlock_mm(struct mm_struct *mm) { + if (mm != NULL) { + up(&(mm->mm_sem)); + mm->locked_by = 0; + } +} + +#endif /* !__KERN_MM_VMM_H__ */ + diff --git a/lab8_practice/kern/process/entry.S b/lab8_practice/kern/process/entry.S new file mode 100644 index 0000000..cb59759 --- /dev/null +++ b/lab8_practice/kern/process/entry.S @@ -0,0 +1,7 @@ +.text +.globl kernel_thread_entry +kernel_thread_entry: # void kernel_thread(void) + move a0, s1 + jalr s0 + + jal do_exit diff --git a/lab8_practice/kern/process/proc.c b/lab8_practice/kern/process/proc.c new file mode 100644 index 0000000..9eac9f8 --- /dev/null +++ b/lab8_practice/kern/process/proc.c @@ -0,0 +1,959 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* ------------- process/thread mechanism design&implementation ------------- +(an simplified Linux process/thread mechanism ) +introduction: + ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads +for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to +manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory). +------------------------------ +process state : meaning -- reason + PROC_UNINIT : uninitialized -- alloc_proc + PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep + PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc, + PROC_ZOMBIE : almost dead -- do_exit + +----------------------------- +process state changing: + + alloc_proc RUNNING + + +--<----<--+ + + + proc_run + + V +-->---->--+ +PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING -- + A + + + | +--- do_exit --> PROC_ZOMBIE + + + + + -----------------------wakeup_proc---------------------------------- +----------------------------- +process relations +parent: proc->parent (proc is children) +children: proc->cptr (proc is parent) +older sibling: proc->optr (proc is younger sibling) +younger sibling: proc->yptr (proc is older sibling) +----------------------------- +related syscall for process: +SYS_exit : process exit, -->do_exit +SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc +SYS_wait : wait process -->do_wait +SYS_exec : after fork, process execute a program -->load a program and refresh the mm +SYS_clone : create child thread -->do_fork-->wakeup_proc +SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process +SYS_sleep : process sleep -->do_sleep +SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING + -->wakeup_proc-->do_wait-->do_exit +SYS_getpid : get the process's pid + +*/ + +// the process set's list +list_entry_t proc_list; + +#define HASH_SHIFT 10 +#define HASH_LIST_SIZE (1 << HASH_SHIFT) +#define pid_hashfn(x) (hash32(x, HASH_SHIFT)) + +// has list for process set based on pid +static list_entry_t hash_list[HASH_LIST_SIZE]; + +// idle proc +struct proc_struct *idleproc = NULL; +// init proc +struct proc_struct *initproc = NULL; +// current proc +struct proc_struct *current = NULL; + +static int nr_process = 0; + +void kernel_thread_entry(void); +void forkrets(struct trapframe *tf); +void switch_to(struct context *from, struct context *to); + +// alloc_proc - alloc a proc_struct and init all fields of proc_struct +static struct proc_struct * +alloc_proc(void) { + struct proc_struct *proc = kmalloc(sizeof(struct proc_struct)); + if (proc != NULL) { + //LAB4:EXERCISE1 YOUR CODE + /* + * below fields in proc_struct need to be initialized + * enum proc_state state; // Process state + * int pid; // Process ID + * int runs; // the running times of Proces + * uintptr_t kstack; // Process kernel stack + * volatile bool need_resched; // bool value: need to be rescheduled to release CPU? + * struct proc_struct *parent; // the parent process + * struct mm_struct *mm; // Process's memory management field + * struct context context; // Switch here to run process + * struct trapframe *tf; // Trap frame for current interrupt + * uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) + * uint32_t flags; // Process flag + * char name[PROC_NAME_LEN + 1]; // Process name + */ + //LAB5 YOUR CODE : (update LAB4 steps) + /* + * below fields(add in LAB5) in proc_struct need to be initialized + * uint32_t wait_state; // waiting state + * struct proc_struct *cptr, *yptr, *optr; // relations between processes + */ + + proc->state = PROC_UNINIT; + proc->pid = -1; + proc->runs = 0; + proc->kstack = 0; + proc->need_resched = 0; + proc->parent = NULL; + proc->mm = NULL; + memset(&(proc->context), 0, sizeof(struct context)); + proc->tf = NULL; + proc->cr3 = boot_cr3; + proc->flags = 0; + memset(proc->name, 0, PROC_NAME_LEN); + proc->wait_state = 0; + proc->cptr = proc->optr = proc->yptr = NULL; + proc->rq = NULL; + list_init(&(proc->run_link)); + proc->time_slice = 0; + proc->lab6_run_pool.left = proc->lab6_run_pool.right = proc->lab6_run_pool.parent = NULL; + proc->lab6_stride = 0; + proc->lab6_priority = 0; + proc->filesp = NULL; + + } + return proc; +} + +// set_proc_name - set the name of proc +char * +set_proc_name(struct proc_struct *proc, const char *name) { + memset(proc->name, 0, sizeof(proc->name)); + return memcpy(proc->name, name, PROC_NAME_LEN); +} + +// get_proc_name - get the name of proc +char * +get_proc_name(struct proc_struct *proc) { + static char name[PROC_NAME_LEN + 1]; + memset(name, 0, sizeof(name)); + return memcpy(name, proc->name, PROC_NAME_LEN); +} + +// set_links - set the relation links of process +static void +set_links(struct proc_struct *proc) { + list_add(&proc_list, &(proc->list_link)); + proc->yptr = NULL; + if ((proc->optr = proc->parent->cptr) != NULL) { + proc->optr->yptr = proc; + } + proc->parent->cptr = proc; + nr_process ++; +} + +// remove_links - clean the relation links of process +static void +remove_links(struct proc_struct *proc) { + list_del(&(proc->list_link)); + if (proc->optr != NULL) { + proc->optr->yptr = proc->yptr; + } + if (proc->yptr != NULL) { + proc->yptr->optr = proc->optr; + } + else { + proc->parent->cptr = proc->optr; + } + nr_process --; +} + +// get_pid - alloc a unique pid for process +static int +get_pid(void) { + static_assert(MAX_PID > MAX_PROCESS); + struct proc_struct *proc; + list_entry_t *list = &proc_list, *le; + static int next_safe = MAX_PID, last_pid = MAX_PID; + if (++ last_pid >= MAX_PID) { + last_pid = 1; + goto inside; + } + if (last_pid >= next_safe) { + inside: + next_safe = MAX_PID; + repeat: + le = list; + while ((le = list_next(le)) != list) { + proc = le2proc(le, list_link); + if (proc->pid == last_pid) { + if (++ last_pid >= next_safe) { + if (last_pid >= MAX_PID) { + last_pid = 1; + } + next_safe = MAX_PID; + goto repeat; + } + } + else if (proc->pid > last_pid && next_safe > proc->pid) { + next_safe = proc->pid; + } + } + } + return last_pid; +} + +// proc_run - make process "proc" running on cpu +// NOTE: before call switch_to, should load base addr of "proc"'s new PDT +void +proc_run(struct proc_struct *proc) { + if (proc != current) { + bool intr_flag; + struct proc_struct *prev = current, *next = proc; + local_intr_save(intr_flag); + { + current = proc; + lcr3(next->cr3); + switch_to(&(prev->context), &(next->context)); + } + local_intr_restore(intr_flag); + } +} + +// forkret -- the first kernel entry point of a new thread/process +// NOTE: the addr of forkret is setted in copy_thread function +// after switch_to, the current proc will execute here. +static void +forkret(void) { + forkrets(current->tf); +} + +// hash_proc - add proc into proc hash_list +static void +hash_proc(struct proc_struct *proc) { + list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link)); +} + +// unhash_proc - delete proc from proc hash_list +static void +unhash_proc(struct proc_struct *proc) { + list_del(&(proc->hash_link)); +} + +// find_proc - find proc frome proc hash_list according to pid +struct proc_struct * +find_proc(int pid) { + if (0 < pid && pid < MAX_PID) { + list_entry_t *list = hash_list + pid_hashfn(pid), *le = list; + while ((le = list_next(le)) != list) { + struct proc_struct *proc = le2proc(le, hash_link); + if (proc->pid == pid) { + return proc; + } + } + } + return NULL; +} + +// kernel_thread - create a kernel thread using "fn" function +// NOTE: the contents of temp trapframe tf will be copied to +// proc->tf in do_fork-->copy_thread function +int +kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) { + struct trapframe tf; + memset(&tf, 0, sizeof(struct trapframe)); + tf.gpr.s0 = (uintptr_t)fn; + tf.gpr.s1 = (uintptr_t)arg; + tf.status = (read_csr(sstatus) | SSTATUS_SPP | SSTATUS_SPIE) & ~SSTATUS_SIE; + tf.epc = (uintptr_t)kernel_thread_entry; + return do_fork(clone_flags | CLONE_VM, 0, &tf); +} + +// setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack +static int +setup_kstack(struct proc_struct *proc) { + struct Page *page = alloc_pages(KSTACKPAGE); + if (page != NULL) { + proc->kstack = (uintptr_t)page2kva(page); + return 0; + } + return -E_NO_MEM; +} + +// put_kstack - free the memory space of process kernel stack +static void +put_kstack(struct proc_struct *proc) { + free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE); +} + +// setup_pgdir - alloc one page as PDT +static int +setup_pgdir(struct mm_struct *mm) { + struct Page *page; + if ((page = alloc_page()) == NULL) { + return -E_NO_MEM; + } + pde_t *pgdir = page2kva(page); + memcpy(pgdir, boot_pgdir, PGSIZE); + + mm->pgdir = pgdir; + return 0; +} + +// put_pgdir - free the memory space of PDT +static void +put_pgdir(struct mm_struct *mm) { + free_page(kva2page(mm->pgdir)); +} + +// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags +// - if clone_flags & CLONE_VM, then "share" ; else "duplicate" +static int +copy_mm(uint32_t clone_flags, struct proc_struct *proc) { + struct mm_struct *mm, *oldmm = current->mm; + + /* current is a kernel thread */ + if (oldmm == NULL) { + return 0; + } + if (clone_flags & CLONE_VM) { + mm = oldmm; + goto good_mm; + } + int ret = -E_NO_MEM; + if ((mm = mm_create()) == NULL) { + goto bad_mm; + } + if (setup_pgdir(mm) != 0) { + goto bad_pgdir_cleanup_mm; + } + lock_mm(oldmm); + { + ret = dup_mmap(mm, oldmm); + } + unlock_mm(oldmm); + + if (ret != 0) { + goto bad_dup_cleanup_mmap; + } + +good_mm: + mm_count_inc(mm); + proc->mm = mm; + proc->cr3 = PADDR(mm->pgdir); + return 0; +bad_dup_cleanup_mmap: + exit_mmap(mm); + put_pgdir(mm); +bad_pgdir_cleanup_mm: + mm_destroy(mm); +bad_mm: + return ret; +} + +// copy_thread - setup the trapframe on the process's kernel stack top and +// - setup the kernel entry point and stack of process +static void +copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) { + proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1; + *(proc->tf) = *tf; + + // Set a0 to 0 so a child process knows it's just forked + proc->tf->gpr.a0 = 0; + proc->tf->gpr.sp = (esp == 0) ? (uintptr_t)proc->tf - 4 : esp; + + proc->context.ra = (uintptr_t)forkret; + proc->context.sp = (uintptr_t)(proc->tf); +} +//copy_files&put_files function used by do_fork in LAB8 +//copy the files_struct from current to proc +static int +copy_files(uint32_t clone_flags, struct proc_struct *proc) { + struct files_struct *filesp, *old_filesp = current->filesp; + assert(old_filesp != NULL); + + if (clone_flags & CLONE_FS) { + filesp = old_filesp; + goto good_files_struct; + } + + int ret = -E_NO_MEM; + if ((filesp = files_create()) == NULL) { + goto bad_files_struct; + } + + if ((ret = dup_files(filesp, old_filesp)) != 0) { + goto bad_dup_cleanup_fs; + } + +good_files_struct: + files_count_inc(filesp); + proc->filesp = filesp; + return 0; + +bad_dup_cleanup_fs: + files_destroy(filesp); +bad_files_struct: + return ret; +} + +//decrease the ref_count of files, and if ref_count==0, then destroy files_struct +static void +put_files(struct proc_struct *proc) { + struct files_struct *filesp = proc->filesp; + if (filesp != NULL) { + if (files_count_dec(filesp) == 0) { + files_destroy(filesp); + } + } +} + +/* do_fork - parent process for a new child process + * @clone_flags: used to guide how to clone the child process + * @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread. + * @tf: the trapframe info, which will be copied to child process's proc->tf + */ +int +do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) { + int ret = -E_NO_FREE_PROC; + struct proc_struct *proc; + if (nr_process >= MAX_PROCESS) { + goto fork_out; + } + ret = -E_NO_MEM; + //LAB4:EXERCISE2 YOUR CODE + //LAB8:EXERCISE2 YOUR CODE HINT:how to copy the fs in parent's proc_struct? + /* + * Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation. + * MACROs or Functions: + * alloc_proc: create a proc struct and init fields (lab4:exercise1) + * setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack + * copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags + * if clone_flags & CLONE_VM, then "share" ; else "duplicate" + * copy_thread: setup the trapframe on the process's kernel stack top and + * setup the kernel entry point and stack of process + * hash_proc: add proc into proc hash_list + * get_pid: alloc a unique pid for process + * wakeup_proc: set proc->state = PROC_RUNNABLE + * VARIABLES: + * proc_list: the process set's list + * nr_process: the number of process set + */ + + // 1. call alloc_proc to allocate a proc_struct + // 2. call setup_kstack to allocate a kernel stack for child process + // 3. call copy_mm to dup OR share mm according clone_flag + // 4. call copy_thread to setup tf & context in proc_struct + // 5. insert proc_struct into hash_list && proc_list + // 6. call wakeup_proc to make the new child process RUNNABLE + // 7. set ret vaule using child proc's pid + + //LAB5 YOUR CODE : (update LAB4 steps) + /* Some Functions + * set_links: set the relation links of process. ALSO SEE: remove_links: lean the relation links of process + * ------------------- + * update step 1: set child proc's parent to current process, make sure current process's wait_state is 0 + * update step 5: insert proc_struct into hash_list && proc_list, set the relation links of process + */ + if ((proc = alloc_proc()) == NULL) { + goto fork_out; + } + + proc->parent = current; + assert(current->wait_state == 0); + + if (setup_kstack(proc) != 0) { + goto bad_fork_cleanup_proc; + } + if (copy_files(clone_flags, proc) != 0) { //for LAB8 + goto bad_fork_cleanup_kstack; + } + if (copy_mm(clone_flags, proc) != 0) { + goto bad_fork_cleanup_fs; + } + copy_thread(proc, stack, tf); + + bool intr_flag; + local_intr_save(intr_flag); + { + proc->pid = get_pid(); + hash_proc(proc); + set_links(proc); + } + local_intr_restore(intr_flag); + + wakeup_proc(proc); + + ret = proc->pid; +fork_out: + return ret; + +bad_fork_cleanup_fs: //for LAB8 + put_files(proc); +bad_fork_cleanup_kstack: + put_kstack(proc); +bad_fork_cleanup_proc: + kfree(proc); + goto fork_out; +} + + +// do_exit - called by sys_exit +// 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process +// 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself. +// 3. call scheduler to switch to other process +int +do_exit(int error_code) { + if (current == idleproc) { + panic("idleproc exit.\n"); + } + if (current == initproc) { + panic("initproc exit.\n"); + } + struct mm_struct *mm = current->mm; + if (mm != NULL) { + lcr3(boot_cr3); + if (mm_count_dec(mm) == 0) { + exit_mmap(mm); + put_pgdir(mm); + mm_destroy(mm); + } + current->mm = NULL; + } + current->state = PROC_ZOMBIE; + current->exit_code = error_code; + bool intr_flag; + struct proc_struct *proc; + local_intr_save(intr_flag); + { + proc = current->parent; + if (proc->wait_state == WT_CHILD) { + wakeup_proc(proc); + } + while (current->cptr != NULL) { + proc = current->cptr; + current->cptr = proc->optr; + + proc->yptr = NULL; + if ((proc->optr = initproc->cptr) != NULL) { + initproc->cptr->yptr = proc; + } + proc->parent = initproc; + initproc->cptr = proc; + if (proc->state == PROC_ZOMBIE) { + if (initproc->wait_state == WT_CHILD) { + wakeup_proc(initproc); + } + } + } + } + local_intr_restore(intr_flag); + schedule(); + panic("do_exit will not return!! %d.\n", current->pid); +} + +//load_icode_read is used by load_icode in LAB8 +static int +load_icode_read(int fd, void *buf, size_t len, off_t offset) { + int ret; + if ((ret = sysfile_seek(fd, offset, LSEEK_SET)) != 0) { + return ret; + } + if ((ret = sysfile_read(fd, buf, len)) != len) { + return (ret < 0) ? ret : -1; + } + return 0; +} + +// load_icode - called by sys_exec-->do_execve + +static int +load_icode(int fd, int argc, char **kargv) { + /* LAB8:EXERCISE2 YOUR CODE HINT:how to load the file with handler fd in to process's memory? how to setup argc/argv? + * MACROs or Functions: + * mm_create - create a mm + * setup_pgdir - setup pgdir in mm + * load_icode_read - read raw data content of program file + * mm_map - build new vma + * pgdir_alloc_page - allocate new memory for TEXT/DATA/BSS/stack parts + * lcr3 - update Page Directory Addr Register -- CR3 + */ + /* (1) create a new mm for current process + * (2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT + * (3) copy TEXT/DATA/BSS parts in binary to memory space of process + * (3.1) read raw data content in file and resolve elfhdr + * (3.2) read raw data content in file and resolve proghdr based on info in elfhdr + * (3.3) call mm_map to build vma related to TEXT/DATA + * (3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file + * and copy them into the new allocated pages + * (3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages + * (4) call mm_map to setup user stack, and put parameters into user stack + * (5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO) + * (6) setup uargc and uargv in user stacks + * (7) setup trapframe for user environment + * (8) if up steps failed, you should cleanup the env. + */ +} + +// this function isn't very correct in LAB8 +static void +put_kargv(int argc, char **kargv) { + while (argc > 0) { + kfree(kargv[-- argc]); + } +} + +static int +copy_kargv(struct mm_struct *mm, int argc, char **kargv, const char **argv) { + int i, ret = -E_INVAL; + if (!user_mem_check(mm, (uintptr_t)argv, sizeof(const char *) * argc, 0)) { + return ret; + } + for (i = 0; i < argc; i ++) { + char *buffer; + if ((buffer = kmalloc(EXEC_MAX_ARG_LEN + 1)) == NULL) { + goto failed_nomem; + } + if (!copy_string(mm, buffer, argv[i], EXEC_MAX_ARG_LEN + 1)) { + kfree(buffer); + goto failed_cleanup; + } + kargv[i] = buffer; + } + return 0; + +failed_nomem: + ret = -E_NO_MEM; +failed_cleanup: + put_kargv(i, kargv); + return ret; +} + +// do_execve - call exit_mmap(mm)&put_pgdir(mm) to reclaim memory space of current process +// - call load_icode to setup new memory space accroding binary prog. +int +do_execve(const char *name, int argc, const char **argv) { + static_assert(EXEC_MAX_ARG_LEN >= FS_MAX_FPATH_LEN); + struct mm_struct *mm = current->mm; + if (!(argc >= 1 && argc <= EXEC_MAX_ARG_NUM)) { + return -E_INVAL; + } + + char local_name[PROC_NAME_LEN + 1]; + memset(local_name, 0, sizeof(local_name)); + + char *kargv[EXEC_MAX_ARG_NUM]; + const char *path; + + int ret = -E_INVAL; + + lock_mm(mm); + if (name == NULL) { + snprintf(local_name, sizeof(local_name), " %d", current->pid); + } + else { + if (!copy_string(mm, local_name, name, sizeof(local_name))) { + unlock_mm(mm); + return ret; + } + } + if ((ret = copy_kargv(mm, argc, kargv, argv)) != 0) { + unlock_mm(mm); + return ret; + } + path = argv[0]; + unlock_mm(mm); + files_closeall(current->filesp); + + /* sysfile_open will check the first argument path, thus we have to use a user-space pointer, and argv[0] may be incorrect */ + int fd; + if ((ret = fd = sysfile_open(path, O_RDONLY)) < 0) { + goto execve_exit; + } + if (mm != NULL) { + lcr3(boot_cr3); + if (mm_count_dec(mm) == 0) { + exit_mmap(mm); + put_pgdir(mm); + mm_destroy(mm); + } + current->mm = NULL; + } + ret= -E_NO_MEM;; + if ((ret = load_icode(fd, argc, kargv)) != 0) { + goto execve_exit; + } + put_kargv(argc, kargv); + set_proc_name(current, local_name); + return 0; + +execve_exit: + put_kargv(argc, kargv); + do_exit(ret); + panic("already exit: %e.\n", ret); +} + +// do_yield - ask the scheduler to reschedule +int +do_yield(void) { + current->need_resched = 1; + return 0; +} + +// do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack +// - proc struct of this child. +// NOTE: only after do_wait function, all resources of the child proces are free. +int +do_wait(int pid, int *code_store) { + struct mm_struct *mm = current->mm; + if (code_store != NULL) { + if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) { + return -E_INVAL; + } + } + + struct proc_struct *proc; + bool intr_flag, haskid; +repeat: + haskid = 0; + if (pid != 0) { + proc = find_proc(pid); + if (proc != NULL && proc->parent == current) { + haskid = 1; + if (proc->state == PROC_ZOMBIE) { + goto found; + } + } + } + else { + proc = current->cptr; + for (; proc != NULL; proc = proc->optr) { + haskid = 1; + if (proc->state == PROC_ZOMBIE) { + goto found; + } + } + } + if (haskid) { + current->state = PROC_SLEEPING; + current->wait_state = WT_CHILD; + schedule(); + if (current->flags & PF_EXITING) { + do_exit(-E_KILLED); + } + goto repeat; + } + return -E_BAD_PROC; + +found: + if (proc == idleproc || proc == initproc) { + panic("wait idleproc or initproc.\n"); + } + if (code_store != NULL) { + *code_store = proc->exit_code; + } + local_intr_save(intr_flag); + { + unhash_proc(proc); + remove_links(proc); + } + local_intr_restore(intr_flag); + put_kstack(proc); + kfree(proc); + return 0; +} +// do_kill - kill process with pid by set this process's flags with PF_EXITING +int +do_kill(int pid) { + struct proc_struct *proc; + if ((proc = find_proc(pid)) != NULL) { + if (!(proc->flags & PF_EXITING)) { + proc->flags |= PF_EXITING; + if (proc->wait_state & WT_INTERRUPTED) { + wakeup_proc(proc); + } + return 0; + } + return -E_KILLED; + } + return -E_INVAL; +} + +// kernel_execve - do SYS_exec syscall to exec a user program called by user_main kernel_thread +static int +kernel_execve(const char *name, const char **argv) { + int64_t argc = 0, ret; + while (argv[argc] != NULL){ + argc++; + } + asm volatile( + "li a0, %1\n" + "lw a1, %2\n" + "lw a2, %3\n" + "lw a3, %4\n" + "li a7, 10\n" + "ebreak\n" + "sw a0, %0\n" + : "=m"(ret) + : "i"(SYS_exec), "m"(name), "m"(argc), "m"(argv) + : "memory"); +// cprintf("ret = %d\n", ret); + return ret; +} +#define __KERNEL_EXECVE(name, path, ...) ({ \ +const char *argv[] = {path, ##__VA_ARGS__, NULL}; \ + cprintf("kernel_execve: pid = %d, name = \"%s\".\n", \ + current->pid, name); \ + kernel_execve(name, argv); \ +}) + +#define KERNEL_EXECVE(x, ...) __KERNEL_EXECVE(#x, #x, ##__VA_ARGS__) + +#define KERNEL_EXECVE2(x, ...) KERNEL_EXECVE(x, ##__VA_ARGS__) + +#define __KERNEL_EXECVE3(x, s, ...) KERNEL_EXECVE(x, #s, ##__VA_ARGS__) + +#define KERNEL_EXECVE3(x, s, ...) __KERNEL_EXECVE3(x, s, ##__VA_ARGS__) + +// user_main - kernel thread used to exec a user program +static int +user_main(void *arg) { +#ifdef TEST +#ifdef TESTSCRIPT + KERNEL_EXECVE3(TEST, TESTSCRIPT); +#else + KERNEL_EXECVE2(TEST); +#endif +#else + KERNEL_EXECVE(sh); +#endif + panic("user_main execve failed.\n"); +} + +// init_main - the second kernel thread used to create user_main kernel threads +static int +init_main(void *arg) { + int ret; + if ((ret = vfs_set_bootfs("disk0:")) != 0) { + panic("set boot fs failed: %e.\n", ret); + } + size_t nr_free_pages_store = nr_free_pages(); + size_t kernel_allocated_store = kallocated(); + + int pid = kernel_thread(user_main, NULL, 0); + if (pid <= 0) { + panic("create user_main failed.\n"); + } + extern void check_sync(void); + //check_sync(); // check philosopher sync problem + + while (do_wait(0, NULL) == 0) { + schedule(); + } + + fs_cleanup(); + + cprintf("all user-mode processes have quit.\n"); + assert(initproc->cptr == NULL && initproc->yptr == NULL && initproc->optr == NULL); + assert(nr_process == 2); + assert(list_next(&proc_list) == &(initproc->list_link)); + assert(list_prev(&proc_list) == &(initproc->list_link)); + + cprintf("init check memory pass.\n"); + return 0; +} + +// proc_init - set up the first kernel thread idleproc "idle" by itself and +// - create the second kernel thread init_main +void +proc_init(void) { + int i; + + list_init(&proc_list); + for (i = 0; i < HASH_LIST_SIZE; i ++) { + list_init(hash_list + i); + } + + if ((idleproc = alloc_proc()) == NULL) { + panic("cannot alloc idleproc.\n"); + } + + idleproc->pid = 0; + idleproc->state = PROC_RUNNABLE; + idleproc->kstack = (uintptr_t)bootstack; + idleproc->need_resched = 1; + + + if ((idleproc->filesp = files_create()) == NULL) { + panic("create filesp (idleproc) failed.\n"); + } + files_count_inc(idleproc->filesp); + + set_proc_name(idleproc, "idle"); + nr_process ++; + + current = idleproc; + + int pid = kernel_thread(init_main, NULL, 0); + if (pid <= 0) { + panic("create init_main failed.\n"); + } + + initproc = find_proc(pid); + set_proc_name(initproc, "init"); + + assert(idleproc != NULL && idleproc->pid == 0); + assert(initproc != NULL && initproc->pid == 1); +} + +// cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works +void +cpu_idle(void) { + while (1) { + if (current->need_resched) { + schedule(); + } + } +} +//FOR LAB6, set the process's priority (bigger value will get more CPU time) +void +lab6_set_priority(uint32_t priority) +{ + cprintf("set priority to %d\n", priority); + if (priority == 0) + current->lab6_priority = 1; + else current->lab6_priority = priority; +} +// do_sleep - set current process state to sleep and add timer with "time" +// - then call scheduler. if process run again, delete timer first. +int +do_sleep(unsigned int time) { + if (time == 0) { + return 0; + } + bool intr_flag; + local_intr_save(intr_flag); + timer_t __timer, *timer = timer_init(&__timer, current, time); + current->state = PROC_SLEEPING; + current->wait_state = WT_TIMER; + add_timer(timer); + local_intr_restore(intr_flag); + + schedule(); + + del_timer(timer); + return 0; +} diff --git a/lab8_practice/kern/process/proc.h b/lab8_practice/kern/process/proc.h new file mode 100644 index 0000000..0a6824a --- /dev/null +++ b/lab8_practice/kern/process/proc.h @@ -0,0 +1,106 @@ +#ifndef __KERN_PROCESS_PROC_H__ +#define __KERN_PROCESS_PROC_H__ + +#include +#include +#include +#include +#include + +// process's state in his life cycle +enum proc_state { + PROC_UNINIT = 0, // uninitialized + PROC_SLEEPING, // sleeping + PROC_RUNNABLE, // runnable(maybe running) + PROC_ZOMBIE, // almost dead, and wait parent proc to reclaim his resource +}; + +struct context { + uintptr_t ra; + uintptr_t sp; + uintptr_t s0; + uintptr_t s1; + uintptr_t s2; + uintptr_t s3; + uintptr_t s4; + uintptr_t s5; + uintptr_t s6; + uintptr_t s7; + uintptr_t s8; + uintptr_t s9; + uintptr_t s10; + uintptr_t s11; +}; + +#define PROC_NAME_LEN 15 +#define MAX_PROCESS 4096 +#define MAX_PID (MAX_PROCESS * 2) + +extern list_entry_t proc_list; + +struct inode; + +struct proc_struct { + enum proc_state state; // Process state + int pid; // Process ID + int runs; // the running times of Proces + uintptr_t kstack; // Process kernel stack + volatile bool need_resched; // bool value: need to be rescheduled to release CPU? + struct proc_struct *parent; // the parent process + struct mm_struct *mm; // Process's memory management field + struct context context; // Switch here to run process + struct trapframe *tf; // Trap frame for current interrupt + uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) + uint32_t flags; // Process flag + char name[PROC_NAME_LEN + 1]; // Process name + list_entry_t list_link; // Process link list + list_entry_t hash_link; // Process hash list + int exit_code; // exit code (be sent to parent proc) + uint32_t wait_state; // waiting state + struct proc_struct *cptr, *yptr, *optr; // relations between processes + struct run_queue *rq; // running queue contains Process + list_entry_t run_link; // the entry linked in run queue + int time_slice; // time slice for occupying the CPU + skew_heap_entry_t lab6_run_pool; // FOR LAB6 ONLY: the entry in the run pool + uint32_t lab6_stride; // FOR LAB6 ONLY: the current stride of the process + uint32_t lab6_priority; // FOR LAB6 ONLY: the priority of process, set by lab6_set_priority(uint32_t) + struct files_struct *filesp; // the file related info(pwd, files_count, files_array, fs_semaphore) of process +}; + +#define PF_EXITING 0x00000001 // getting shutdown + +#define WT_CHILD (0x00000001 | WT_INTERRUPTED) +#define WT_INTERRUPTED 0x80000000 // the wait state could be interrupted + +#define WT_CHILD (0x00000001 | WT_INTERRUPTED) // wait child process +#define WT_KSEM 0x00000100 // wait kernel semaphore +#define WT_TIMER (0x00000002 | WT_INTERRUPTED) // wait timer +#define WT_KBD (0x00000004 | WT_INTERRUPTED) // wait the input of keyboard + +#define le2proc(le, member) \ + to_struct((le), struct proc_struct, member) + +extern struct proc_struct *idleproc, *initproc, *current; + +void proc_init(void); +void proc_run(struct proc_struct *proc); +int kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags); + +char *set_proc_name(struct proc_struct *proc, const char *name); +char *get_proc_name(struct proc_struct *proc); +void cpu_idle(void) __attribute__((noreturn)); + +//FOR LAB6, set the process's priority (bigger value will get more CPU time) +void lab6_set_priority(uint32_t priority); + + +struct proc_struct *find_proc(int pid); +int do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf); +int do_exit(int error_code); +int do_yield(void); +int do_execve(const char *name, int argc, const char **argv); +int do_wait(int pid, int *code_store); +int do_kill(int pid); +int do_sleep(unsigned int time); +#endif /* !__KERN_PROCESS_PROC_H__ */ + diff --git a/lab8_practice/kern/process/switch.S b/lab8_practice/kern/process/switch.S new file mode 100644 index 0000000..77efab7 --- /dev/null +++ b/lab8_practice/kern/process/switch.S @@ -0,0 +1,39 @@ +#include + +.text +# void switch_to(struct proc_struct* from, struct proc_struct* to) +.globl switch_to +switch_to: + # save from's registers + STORE ra, 0*REGBYTES(a0) + STORE sp, 1*REGBYTES(a0) + STORE s0, 2*REGBYTES(a0) + STORE s1, 3*REGBYTES(a0) + STORE s2, 4*REGBYTES(a0) + STORE s3, 5*REGBYTES(a0) + STORE s4, 6*REGBYTES(a0) + STORE s5, 7*REGBYTES(a0) + STORE s6, 8*REGBYTES(a0) + STORE s7, 9*REGBYTES(a0) + STORE s8, 10*REGBYTES(a0) + STORE s9, 11*REGBYTES(a0) + STORE s10, 12*REGBYTES(a0) + STORE s11, 13*REGBYTES(a0) + + # restore to's registers + LOAD ra, 0*REGBYTES(a1) + LOAD sp, 1*REGBYTES(a1) + LOAD s0, 2*REGBYTES(a1) + LOAD s1, 3*REGBYTES(a1) + LOAD s2, 4*REGBYTES(a1) + LOAD s3, 5*REGBYTES(a1) + LOAD s4, 6*REGBYTES(a1) + LOAD s5, 7*REGBYTES(a1) + LOAD s6, 8*REGBYTES(a1) + LOAD s7, 9*REGBYTES(a1) + LOAD s8, 10*REGBYTES(a1) + LOAD s9, 11*REGBYTES(a1) + LOAD s10, 12*REGBYTES(a1) + LOAD s11, 13*REGBYTES(a1) + + ret diff --git a/lab8_practice/kern/schedule/default_sched.h b/lab8_practice/kern/schedule/default_sched.h new file mode 100644 index 0000000..2f21fbd --- /dev/null +++ b/lab8_practice/kern/schedule/default_sched.h @@ -0,0 +1,9 @@ +#ifndef __KERN_SCHEDULE_SCHED_RR_H__ +#define __KERN_SCHEDULE_SCHED_RR_H__ + +#include + +extern struct sched_class default_sched_class; + +#endif /* !__KERN_SCHEDULE_SCHED_RR_H__ */ + diff --git a/lab8_practice/kern/schedule/default_sched_c b/lab8_practice/kern/schedule/default_sched_c new file mode 100644 index 0000000..2316990 --- /dev/null +++ b/lab8_practice/kern/schedule/default_sched_c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +static void +RR_init(struct run_queue *rq) { + list_init(&(rq->run_list)); + rq->proc_num = 0; +} + +static void +RR_enqueue(struct run_queue *rq, struct proc_struct *proc) { + assert(list_empty(&(proc->run_link))); + list_add_before(&(rq->run_list), &(proc->run_link)); + if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) { + proc->time_slice = rq->max_time_slice; + } + proc->rq = rq; + rq->proc_num ++; +} + +static void +RR_dequeue(struct run_queue *rq, struct proc_struct *proc) { + assert(!list_empty(&(proc->run_link)) && proc->rq == rq); + list_del_init(&(proc->run_link)); + rq->proc_num --; +} + +static struct proc_struct * +RR_pick_next(struct run_queue *rq) { + list_entry_t *le = list_next(&(rq->run_list)); + if (le != &(rq->run_list)) { + return le2proc(le, run_link); + } + return NULL; +} + +static void +RR_proc_tick(struct run_queue *rq, struct proc_struct *proc) { + if (proc->time_slice > 0) { + proc->time_slice --; + } + if (proc->time_slice == 0) { + proc->need_resched = 1; + } +} + +struct sched_class default_sched_class = { + .name = "RR_scheduler", + .init = RR_init, + .enqueue = RR_enqueue, + .dequeue = RR_dequeue, + .pick_next = RR_pick_next, + .proc_tick = RR_proc_tick, +}; + diff --git a/lab8_practice/kern/schedule/default_sched_stride.c b/lab8_practice/kern/schedule/default_sched_stride.c new file mode 100644 index 0000000..d8e176d --- /dev/null +++ b/lab8_practice/kern/schedule/default_sched_stride.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include + +#define USE_SKEW_HEAP 1 + +/* You should define the BigStride constant here*/ +/* LAB6: YOUR CODE */ +#define BIG_STRIDE (1 << 30) /* you should give a value, and is ??? */ + +/* The compare function for two skew_heap_node_t's and the + * corresponding procs*/ +static int +proc_stride_comp_f(void *a, void *b) +{ + struct proc_struct *p = le2proc(a, lab6_run_pool); + struct proc_struct *q = le2proc(b, lab6_run_pool); + int32_t c = p->lab6_stride - q->lab6_stride; + if (c > 0) return 1; + else if (c == 0) return 0; + else return -1; +} + +/* + * stride_init initializes the run-queue rq with correct assignment for + * member variables, including: + * + * - run_list: should be a empty list after initialization. + * - lab6_run_pool: NULL + * - proc_num: 0 + * - max_time_slice: no need here, the variable would be assigned by the caller. + * + * hint: see libs/list.h for routines of the list structures. + */ +static void +stride_init(struct run_queue *rq) { + /* LAB6: YOUR CODE + * (1) init the ready process list: rq->run_list + * (2) init the run pool: rq->lab6_run_pool + * (3) set number of process: rq->proc_num to 0 + */ + list_init(&(rq->run_list)); + rq->lab6_run_pool = NULL; + rq->proc_num = 0; +} + +/* + * stride_enqueue inserts the process ``proc'' into the run-queue + * ``rq''. The procedure should verify/initialize the relevant members + * of ``proc'', and then put the ``lab6_run_pool'' node into the + * queue(since we use priority queue here). The procedure should also + * update the meta date in ``rq'' structure. + * + * proc->time_slice denotes the time slices allocation for the + * process, which should set to rq->max_time_slice. + * + * hint: see libs/skew_heap.h for routines of the priority + * queue structures. + */ +static void +stride_enqueue(struct run_queue *rq, struct proc_struct *proc) { + /* LAB6: YOUR CODE + * (1) insert the proc into rq correctly + * NOTICE: you can use skew_heap or list. Important functions + * skew_heap_insert: insert a entry into skew_heap + * list_add_before: insert a entry into the last of list + * (2) recalculate proc->time_slice + * (3) set proc->rq pointer to rq + * (4) increase rq->proc_num + */ + rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f); + + if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) { + proc->time_slice = rq->max_time_slice; + } + + proc->rq = rq; + rq->proc_num++; +} + +/* + * stride_dequeue removes the process ``proc'' from the run-queue + * ``rq'', the operation would be finished by the skew_heap_remove + * operations. Remember to update the ``rq'' structure. + * + * hint: see libs/skew_heap.h for routines of the priority + * queue structures. + */ +static void +stride_dequeue(struct run_queue *rq, struct proc_struct *proc) { + /* LAB6: YOUR CODE + * (1) remove the proc from rq correctly + * NOTICE: you can use skew_heap or list. Important functions + * skew_heap_remove: remove a entry from skew_heap + * list_del_init: remove a entry from the list + */ + assert(proc->rq == rq && rq->proc_num > 0); + rq->lab6_run_pool = skew_heap_remove(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f); + rq->proc_num--; +} +/* + * stride_pick_next pick the element from the ``run-queue'', with the + * minimum value of stride, and returns the corresponding process + * pointer. The process pointer would be calculated by macro le2proc, + * see kern/process/proc.h for definition. Return NULL if + * there is no process in the queue. + * + * When one proc structure is selected, remember to update the stride + * property of the proc. (stride += BIG_STRIDE / priority) + * + * hint: see libs/skew_heap.h for routines of the priority + * queue structures. + */ +static struct proc_struct * +stride_pick_next(struct run_queue *rq) { + /* LAB6: YOUR CODE + * (1) get a proc_struct pointer p with the minimum value of stride + (1.1) If using skew_heap, we can use le2proc get the p from rq->lab6_run_poll + (1.2) If using list, we have to search list to find the p with minimum stride value + * (2) update p;s stride value: p->lab6_stride + * (3) return p + */ + if (rq->lab6_run_pool == NULL) { + return NULL; + } + struct proc_struct* proc = le2proc(rq->lab6_run_pool, lab6_run_pool); + proc->lab6_stride += proc->lab6_priority ? BIG_STRIDE / proc->lab6_priority : BIG_STRIDE; + return proc; +} + +/* + * stride_proc_tick works with the tick event of current process. You + * should check whether the time slices for current process is + * exhausted and update the proc struct ``proc''. proc->time_slice + * denotes the time slices left for current + * process. proc->need_resched is the flag variable for process + * switching. + */ +static void +stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) { + /* LAB6: YOUR CODE */ + if (proc->time_slice > 0) { + proc->time_slice--; + } + if (proc->time_slice == 0) { + proc->need_resched = 1; + } +} + +struct sched_class default_sched_class = { + .name = "stride_scheduler", + .init = stride_init, + .enqueue = stride_enqueue, + .dequeue = stride_dequeue, + .pick_next = stride_pick_next, + .proc_tick = stride_proc_tick, +}; diff --git a/lab8_practice/kern/schedule/sched.c b/lab8_practice/kern/schedule/sched.c new file mode 100644 index 0000000..e5ce8a3 --- /dev/null +++ b/lab8_practice/kern/schedule/sched.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include + +// the list of timer +static list_entry_t timer_list; + +static struct sched_class *sched_class; + +static struct run_queue *rq; + +static inline void +sched_class_enqueue(struct proc_struct *proc) { + if (proc != idleproc) { + sched_class->enqueue(rq, proc); + } +} + +static inline void +sched_class_dequeue(struct proc_struct *proc) { + sched_class->dequeue(rq, proc); +} + +static inline struct proc_struct * +sched_class_pick_next(void) { + return sched_class->pick_next(rq); +} + +static void +sched_class_proc_tick(struct proc_struct *proc) { + if (proc != idleproc) { + sched_class->proc_tick(rq, proc); + } + else { + proc->need_resched = 1; + } +} + +static struct run_queue __rq; + +void +sched_init(void) { + list_init(&timer_list); + + sched_class = &default_sched_class; + + rq = &__rq; + rq->max_time_slice = MAX_TIME_SLICE; + sched_class->init(rq); + + cprintf("sched class: %s\n", sched_class->name); +} + +void +wakeup_proc(struct proc_struct *proc) { + assert(proc->state != PROC_ZOMBIE); + bool intr_flag; + local_intr_save(intr_flag); + { + if (proc->state != PROC_RUNNABLE) { + proc->state = PROC_RUNNABLE; + proc->wait_state = 0; + if (proc != current) { + sched_class_enqueue(proc); + } + } + else { + warn("wakeup runnable process.\n"); + } + } + local_intr_restore(intr_flag); +} + +void +schedule(void) { + bool intr_flag; + struct proc_struct *next; + local_intr_save(intr_flag); + { + current->need_resched = 0; + if (current->state == PROC_RUNNABLE) { + sched_class_enqueue(current); + } + if ((next = sched_class_pick_next()) != NULL) { + sched_class_dequeue(next); + } + if (next == NULL) { + next = idleproc; + } + next->runs ++; + if (next != current) { + proc_run(next); + } + } + local_intr_restore(intr_flag); +} + +// add timer to timer_list +void +add_timer(timer_t *timer) { + bool intr_flag; + local_intr_save(intr_flag); + { + assert(timer->expires > 0 && timer->proc != NULL); + assert(list_empty(&(timer->timer_link))); + list_entry_t *le = list_next(&timer_list); + while (le != &timer_list) { + timer_t *next = le2timer(le, timer_link); + if (timer->expires < next->expires) { + next->expires -= timer->expires; + break; + } + timer->expires -= next->expires; + le = list_next(le); + } + list_add_before(le, &(timer->timer_link)); + } + local_intr_restore(intr_flag); +} + +// del timer from timer_list +void +del_timer(timer_t *timer) { + bool intr_flag; + local_intr_save(intr_flag); + { + if (!list_empty(&(timer->timer_link))) { + if (timer->expires != 0) { + list_entry_t *le = list_next(&(timer->timer_link)); + if (le != &timer_list) { + timer_t *next = le2timer(le, timer_link); + next->expires += timer->expires; + } + } + list_del_init(&(timer->timer_link)); + } + } + local_intr_restore(intr_flag); +} + +// call scheduler to update tick related info, and check the timer is expired? If expired, then wakup proc +void +run_timer_list(void) { + bool intr_flag; + local_intr_save(intr_flag); + { + list_entry_t *le = list_next(&timer_list); + if (le != &timer_list) { + timer_t *timer = le2timer(le, timer_link); + assert(timer->expires != 0); + timer->expires --; + while (timer->expires == 0) { + le = list_next(le); + struct proc_struct *proc = timer->proc; + if (proc->wait_state != 0) { + assert(proc->wait_state & WT_INTERRUPTED); + } + else { + warn("process %d's wait_state == 0.\n", proc->pid); + } + wakeup_proc(proc); + del_timer(timer); + if (le == &timer_list) { + break; + } + timer = le2timer(le, timer_link); + } + } + if(current)sched_class_proc_tick(current); + } + local_intr_restore(intr_flag); +} diff --git a/lab8_practice/kern/schedule/sched.h b/lab8_practice/kern/schedule/sched.h new file mode 100644 index 0000000..766ac03 --- /dev/null +++ b/lab8_practice/kern/schedule/sched.h @@ -0,0 +1,73 @@ +#ifndef __KERN_SCHEDULE_SCHED_H__ +#define __KERN_SCHEDULE_SCHED_H__ + +#include +#include +#include + +#define MAX_TIME_SLICE 5 + +struct proc_struct; + +typedef struct { + unsigned int expires; //the expire time + struct proc_struct *proc; //the proc wait in this timer. If the expire time is end, then this proc will be scheduled + list_entry_t timer_link; //the timer list +} timer_t; + +#define le2timer(le, member) \ +to_struct((le), timer_t, member) + +// init a timer +static inline timer_t * +timer_init(timer_t *timer, struct proc_struct *proc, int expires) { + timer->expires = expires; + timer->proc = proc; + list_init(&(timer->timer_link)); + return timer; +} + +struct run_queue; + +// The introduction of scheduling classes is borrrowed from Linux, and makes the +// core scheduler quite extensible. These classes (the scheduler modules) encapsulate +// the scheduling policies. +struct sched_class { + // the name of sched_class + const char *name; + // Init the run queue + void (*init)(struct run_queue *rq); + // put the proc into runqueue, and this function must be called with rq_lock + void (*enqueue)(struct run_queue *rq, struct proc_struct *proc); + // get the proc out runqueue, and this function must be called with rq_lock + void (*dequeue)(struct run_queue *rq, struct proc_struct *proc); + // choose the next runnable task + struct proc_struct *(*pick_next)(struct run_queue *rq); + // dealer of the time-tick + void (*proc_tick)(struct run_queue *rq, struct proc_struct *proc); + /* for SMP support in the future + * load_balance + * void (*load_balance)(struct rq* rq); + * get some proc from this rq, used in load_balance, + * return value is the num of gotten proc + * int (*get_proc)(struct rq* rq, struct proc* procs_moved[]); + */ +}; + +struct run_queue { + list_entry_t run_list; + unsigned int proc_num; + int max_time_slice; + // For LAB6 ONLY + skew_heap_entry_t *lab6_run_pool; +}; + +void sched_init(void); +void wakeup_proc(struct proc_struct *proc); +void schedule(void); +void add_timer(timer_t *timer); // add timer to timer_list +void del_timer(timer_t *timer); // del timer from timer_list +void run_timer_list(void); // call scheduler to update tick related info, and check the timer is expired? If expired, then wakup proc + +#endif /* !__KERN_SCHEDULE_SCHED_H__ */ + diff --git a/lab8_practice/kern/sync/check_sync.c b/lab8_practice/kern/sync/check_sync.c new file mode 100644 index 0000000..83a6567 --- /dev/null +++ b/lab8_practice/kern/sync/check_sync.c @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include + +#define N 5 /* 哲学家数目 */ +#define LEFT (i-1+N)%N /* i的左邻号码 */ +#define RIGHT (i+1)%N /* i的右邻号码 */ +#define THINKING 0 /* 哲学家正在思考 */ +#define HUNGRY 1 /* 哲学家想取得叉子 */ +#define EATING 2 /* 哲学家正在吃面 */ +#define TIMES 4 /* 吃4次饭 */ +#define SLEEP_TIME 10 + +//-----------------philosopher problem using monitor ------------ +/*PSEUDO CODE :philosopher problem using semaphore +system DINING_PHILOSOPHERS + +VAR +me: semaphore, initially 1; # for mutual exclusion +s[5]: semaphore s[5], initially 0; # for synchronization +pflag[5]: {THINK, HUNGRY, EAT}, initially THINK; # philosopher flag + +# As before, each philosopher is an endless cycle of thinking and eating. + +procedure philosopher(i) + { + while TRUE do + { + THINKING; + take_chopsticks(i); + EATING; + drop_chopsticks(i); + } + } + +# The take_chopsticks procedure involves checking the status of neighboring +# philosophers and then declaring one's own intention to eat. This is a two-phase +# protocol; first declaring the status HUNGRY, then going on to EAT. + +procedure take_chopsticks(i) + { + DOWN(me); # critical section + pflag[i] := HUNGRY; + test[i]; + UP(me); # end critical section + DOWN(s[i]) # Eat if enabled + } + +void test(i) # Let phil[i] eat, if waiting + { + if ( pflag[i] == HUNGRY + && pflag[i-1] != EAT + && pflag[i+1] != EAT) + then + { + pflag[i] := EAT; + UP(s[i]) + } + } + + +# Once a philosopher finishes eating, all that remains is to relinquish the +# resources---its two chopsticks---and thereby release waiting neighbors. + +void drop_chopsticks(int i) + { + DOWN(me); # critical section + test(i-1); # Let phil. on left eat if possible + test(i+1); # Let phil. on rght eat if possible + UP(me); # up critical section + } + +*/ +//---------- philosophers problem using semaphore ---------------------- +int state_sema[N]; /* 记录每个人状态的数组 */ +/* 信号量是一个特殊的整型变量 */ +semaphore_t mutex; /* 临界区互斥 */ +semaphore_t s[N]; /* 每个哲学家一个信号量 */ + +struct proc_struct *philosopher_proc_sema[N]; + +void phi_test_sema(int i) /* i:哲学家号码从0到N-1 */ +{ + if(state_sema[i]==HUNGRY&&state_sema[LEFT]!=EATING + &&state_sema[RIGHT]!=EATING) + { + state_sema[i]=EATING; + up(&s[i]); + } +} + +void phi_take_forks_sema(int i) /* i:哲学家号码从0到N-1 */ +{ + down(&mutex); /* 进入临界区 */ + state_sema[i]=HUNGRY; /* 记录下哲学家i饥饿的事实 */ + phi_test_sema(i); /* 试图得到两只叉子 */ + up(&mutex); /* 离开临界区 */ + down(&s[i]); /* 如果得不到叉子就阻塞 */ +} + +void phi_put_forks_sema(int i) /* i:哲学家号码从0到N-1 */ +{ + down(&mutex); /* 进入临界区 */ + state_sema[i]=THINKING; /* 哲学家进餐结束 */ + phi_test_sema(LEFT); /* 看一下左邻居现在是否能进餐 */ + phi_test_sema(RIGHT); /* 看一下右邻居现在是否能进餐 */ + up(&mutex); /* 离开临界区 */ +} + +int philosopher_using_semaphore(void * arg) /* i:哲学家号码,从0到N-1 */ +{ + int i, iter=0; + i=(int)arg; + cprintf("I am No.%d philosopher_sema\n",i); + while(iter++cv[i]) ; + } +} + + +void phi_take_forks_condvar(int i) { + down(&(mtp->mutex)); +//--------into routine in monitor-------------- + // LAB7 EXERCISE1: YOUR CODE + // I am hungry + // try to get fork + state_condvar[i] = HUNGRY; + phi_test_condvar(i); + if (state_condvar[i] == HUNGRY) { + cond_wait(&mtp->cv[i]); + } +//--------leave routine in monitor-------------- + if(mtp->next_count>0) + up(&(mtp->next)); + else + up(&(mtp->mutex)); +} + +void phi_put_forks_condvar(int i) { + down(&(mtp->mutex)); + +//--------into routine in monitor-------------- + // LAB7 EXERCISE1: YOUR CODE + // I ate over + // test left and right neighbors + state_condvar[i] = THINKING; + phi_test_condvar(LEFT); + phi_test_condvar(RIGHT); +//--------leave routine in monitor-------------- + if(mtp->next_count>0) + up(&(mtp->next)); + else + up(&(mtp->mutex)); +} + +//---------- philosophers using monitor (condition variable) ---------------------- +int philosopher_using_condvar(void * arg) { /* arg is the No. of philosopher 0~N-1*/ + + int i, iter=0; + i=(int)arg; + cprintf("I am No.%d philosopher_condvar\n",i); + while(iter++ +#include +#include +#include + + +// Initialize monitor. +void +monitor_init (monitor_t * mtp, size_t num_cv) { + int i; + assert(num_cv>0); + mtp->next_count = 0; + mtp->cv = NULL; + sem_init(&(mtp->mutex), 1); //unlocked + sem_init(&(mtp->next), 0); + mtp->cv =(condvar_t *) kmalloc(sizeof(condvar_t)*num_cv); + assert(mtp->cv!=NULL); + for(i=0; icv[i].count=0; + sem_init(&(mtp->cv[i].sem),0); + mtp->cv[i].owner=mtp; + } +} + +// Unlock one of threads waiting on the condition variable. +void +cond_signal (condvar_t *cvp) { + //LAB7 EXERCISE1: YOUR CODE + cprintf("cond_signal begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); + /* + * cond_signal(cv) { + * if(cv.count>0) { + * mt.next_count ++; + * signal(cv.sem); + * wait(mt.next); + * mt.next_count--; + * } + * } + */ + if (cvp->count > 0) { + monitor_t* const mtp = cvp->owner; + mtp->next_count++; + up(&(cvp->sem)); + down(&(mtp->next)); + mtp->next_count--; + } + cprintf("cond_signal end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); +} + +// Suspend calling thread on a condition variable waiting for condition Atomically unlocks +// mutex and suspends calling thread on conditional variable after waking up locks mutex. Notice: mp is mutex semaphore for monitor's procedures +void +cond_wait (condvar_t *cvp) { + //LAB7 EXERCISE1: YOUR CODE + cprintf("cond_wait begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); + /* + * cv.count ++; + * if(mt.next_count>0) + * signal(mt.next) + * else + * signal(mt.mutex); + * wait(cv.sem); + * cv.count --; + */ + cvp->count++; + monitor_t* const mtp = cvp->owner; + if (mtp->next_count > 0) { + up(&(mtp->next)); + } else { + up(&(mtp->mutex)); + } + down(&(cvp->sem)); + cvp->count--; + cprintf("cond_wait end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count); +} diff --git a/lab8_practice/kern/sync/monitor.h b/lab8_practice/kern/sync/monitor.h new file mode 100644 index 0000000..39b9610 --- /dev/null +++ b/lab8_practice/kern/sync/monitor.h @@ -0,0 +1,90 @@ +#ifndef __KERN_SYNC_MONITOR_CONDVAR_H__ +#define __KERN_SYNC_MOINTOR_CONDVAR_H__ + +#include +/* In [OS CONCEPT] 7.7 section, the accurate define and approximate implementation of MONITOR was introduced. + * INTRODUCTION: + * Monitors were invented by C. A. R. Hoare and Per Brinch Hansen, and were first implemented in Brinch Hansen's + * Concurrent Pascal language. Generally, a monitor is a language construct and the compiler usually enforces mutual exclusion. Compare this with semaphores, which are usually an OS construct. + * DEFNIE & CHARACTERISTIC: + * A monitor is a collection of procedures, variables, and data structures grouped together. + * Processes can call the monitor procedures but cannot access the internal data structures. + * Only one process at a time may be be active in a monitor. + * Condition variables allow for blocking and unblocking. + * cv.wait() blocks a process. + * The process is said to be waiting for (or waiting on) the condition variable cv. + * cv.signal() (also called cv.notify) unblocks a process waiting for the condition variable cv. + * When this occurs, we need to still require that only one process is active in the monitor. This can be done in several ways: + * on some systems the old process (the one executing the signal) leaves the monitor and the new one enters + * on some systems the signal must be the last statement executed inside the monitor. + * on some systems the old process will block until the monitor is available again. + * on some systems the new process (the one unblocked by the signal) will remain blocked until the monitor is available again. + * If a condition variable is signaled with nobody waiting, the signal is lost. Compare this with semaphores, in which a signal will allow a process that executes a wait in the future to no block. + * You should not think of a condition variable as a variable in the traditional sense. + * It does not have a value. + * Think of it as an object in the OOP sense. + * It has two methods, wait and signal that manipulate the calling process. + * IMPLEMENTATION: + * monitor mt { + * ----------------variable------------------ + * semaphore mutex; + * semaphore next; + * int next_count; + * condvar {int count, sempahore sem} cv[N]; + * other variables in mt; + * --------condvar wait/signal--------------- + * cond_wait (cv) { + * cv.count ++; + * if(mt.next_count>0) + * signal(mt.next) + * else + * signal(mt.mutex); + * wait(cv.sem); + * cv.count --; + * } + * + * cond_signal(cv) { + * if(cv.count>0) { + * mt.next_count ++; + * signal(cv.sem); + * wait(mt.next); + * mt.next_count--; + * } + * } + * --------routines in monitor--------------- + * routineA_in_mt () { + * wait(mt.mutex); + * ... + * real body of routineA + * ... + * if(next_count>0) + * signal(mt.next); + * else + * signal(mt.mutex); + * } + */ + +typedef struct monitor monitor_t; + +typedef struct condvar{ + semaphore_t sem; // the sem semaphore is used to down the waiting proc, and the signaling proc should up the waiting proc + int count; // the number of waiters on condvar + monitor_t * owner; // the owner(monitor) of this condvar +} condvar_t; + +typedef struct monitor{ + semaphore_t mutex; // the mutex lock for going into the routines in monitor, should be initialized to 1 + semaphore_t next; // the next semaphore is used to down the signaling proc itself, and the other OR wakeuped waiting proc should wake up the sleeped signaling proc. + int next_count; // the number of of sleeped signaling proc + condvar_t *cv; // the condvars in monitor +} monitor_t; + +// Initialize variables in monitor. +void monitor_init (monitor_t *cvp, size_t num_cv); +// Unlock one of threads waiting on the condition variable. +void cond_signal (condvar_t *cvp); +// Suspend calling thread on a condition variable waiting for condition atomically unlock mutex in monitor, +// and suspends calling thread on conditional variable after waking up locks mutex. +void cond_wait (condvar_t *cvp); + +#endif /* !__KERN_SYNC_MONITOR_CONDVAR_H__ */ diff --git a/lab8_practice/kern/sync/sem.c b/lab8_practice/kern/sync/sem.c new file mode 100644 index 0000000..62c81db --- /dev/null +++ b/lab8_practice/kern/sync/sem.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void +sem_init(semaphore_t *sem, int value) { + sem->value = value; + wait_queue_init(&(sem->wait_queue)); +} + +static __noinline void __up(semaphore_t *sem, uint32_t wait_state) { + bool intr_flag; + local_intr_save(intr_flag); + { + wait_t *wait; + if ((wait = wait_queue_first(&(sem->wait_queue))) == NULL) { + sem->value ++; + } + else { + assert(wait->proc->wait_state == wait_state); + wakeup_wait(&(sem->wait_queue), wait, wait_state, 1); + } + } + local_intr_restore(intr_flag); +} + +static __noinline uint32_t __down(semaphore_t *sem, uint32_t wait_state) { + bool intr_flag; + local_intr_save(intr_flag); + if (sem->value > 0) { + sem->value --; + local_intr_restore(intr_flag); + return 0; + } + wait_t __wait, *wait = &__wait; + wait_current_set(&(sem->wait_queue), wait, wait_state); + local_intr_restore(intr_flag); + + schedule(); + + local_intr_save(intr_flag); + wait_current_del(&(sem->wait_queue), wait); + local_intr_restore(intr_flag); + + if (wait->wakeup_flags != wait_state) { + return wait->wakeup_flags; + } + return 0; +} + +void +up(semaphore_t *sem) { + __up(sem, WT_KSEM); +} + +void +down(semaphore_t *sem) { + uint32_t flags = __down(sem, WT_KSEM); + assert(flags == 0); +} + +bool +try_down(semaphore_t *sem) { + bool intr_flag, ret = 0; + local_intr_save(intr_flag); + if (sem->value > 0) { + sem->value --, ret = 1; + } + local_intr_restore(intr_flag); + return ret; +} + diff --git a/lab8_practice/kern/sync/sem.h b/lab8_practice/kern/sync/sem.h new file mode 100644 index 0000000..4450fe7 --- /dev/null +++ b/lab8_practice/kern/sync/sem.h @@ -0,0 +1,19 @@ +#ifndef __KERN_SYNC_SEM_H__ +#define __KERN_SYNC_SEM_H__ + +#include +#include +#include + +typedef struct { + int value; + wait_queue_t wait_queue; +} semaphore_t; + +void sem_init(semaphore_t *sem, int value); +void up(semaphore_t *sem); +void down(semaphore_t *sem); +bool try_down(semaphore_t *sem); + +#endif /* !__KERN_SYNC_SEM_H__ */ + diff --git a/lab8_practice/kern/sync/sync.h b/lab8_practice/kern/sync/sync.h new file mode 100644 index 0000000..e30ad31 --- /dev/null +++ b/lab8_practice/kern/sync/sync.h @@ -0,0 +1,29 @@ +#ifndef __KERN_SYNC_SYNC_H__ +#define __KERN_SYNC_SYNC_H__ + +#include +#include +#include +#include +#include +#include + +static inline bool __intr_save(void) { + if (read_csr(sstatus) & SSTATUS_SIE) { + intr_disable(); + return 1; + } + return 0; +} + +static inline void __intr_restore(bool flag) { + if (flag) { + intr_enable(); + } +} + +#define local_intr_save(x) do { x = __intr_save(); } while (0) +#define local_intr_restore(x) __intr_restore(x); + +#endif /* !__KERN_SYNC_SYNC_H__ */ + diff --git a/lab8_practice/kern/sync/wait.c b/lab8_practice/kern/sync/wait.c new file mode 100644 index 0000000..6aea172 --- /dev/null +++ b/lab8_practice/kern/sync/wait.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +void +wait_init(wait_t *wait, struct proc_struct *proc) { + wait->proc = proc; + wait->wakeup_flags = WT_INTERRUPTED; + list_init(&(wait->wait_link)); +} + +void +wait_queue_init(wait_queue_t *queue) { + list_init(&(queue->wait_head)); +} + +void +wait_queue_add(wait_queue_t *queue, wait_t *wait) { + assert(list_empty(&(wait->wait_link)) && wait->proc != NULL); + wait->wait_queue = queue; + list_add_before(&(queue->wait_head), &(wait->wait_link)); +} + +void +wait_queue_del(wait_queue_t *queue, wait_t *wait) { + assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue); + list_del_init(&(wait->wait_link)); +} + +wait_t * +wait_queue_next(wait_queue_t *queue, wait_t *wait) { + assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue); + list_entry_t *le = list_next(&(wait->wait_link)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +wait_t * +wait_queue_prev(wait_queue_t *queue, wait_t *wait) { + assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue); + list_entry_t *le = list_prev(&(wait->wait_link)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +wait_t * +wait_queue_first(wait_queue_t *queue) { + list_entry_t *le = list_next(&(queue->wait_head)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +wait_t * +wait_queue_last(wait_queue_t *queue) { + list_entry_t *le = list_prev(&(queue->wait_head)); + if (le != &(queue->wait_head)) { + return le2wait(le, wait_link); + } + return NULL; +} + +bool +wait_queue_empty(wait_queue_t *queue) { + return list_empty(&(queue->wait_head)); +} + +bool +wait_in_queue(wait_t *wait) { + return !list_empty(&(wait->wait_link)); +} + +void +wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del) { + if (del) { + wait_queue_del(queue, wait); + } + wait->wakeup_flags = wakeup_flags; + wakeup_proc(wait->proc); +} + +void +wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del) { + wait_t *wait; + if ((wait = wait_queue_first(queue)) != NULL) { + wakeup_wait(queue, wait, wakeup_flags, del); + } +} + +void +wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del) { + wait_t *wait; + if ((wait = wait_queue_first(queue)) != NULL) { + if (del) { + do { + wakeup_wait(queue, wait, wakeup_flags, 1); + } while ((wait = wait_queue_first(queue)) != NULL); + } + else { + do { + wakeup_wait(queue, wait, wakeup_flags, 0); + } while ((wait = wait_queue_next(queue, wait)) != NULL); + } + } +} + +void +wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state) { + assert(current != NULL); + wait_init(wait, current); + current->state = PROC_SLEEPING; + current->wait_state = wait_state; + wait_queue_add(queue, wait); +} + diff --git a/lab8_practice/kern/sync/wait.h b/lab8_practice/kern/sync/wait.h new file mode 100644 index 0000000..46758b7 --- /dev/null +++ b/lab8_practice/kern/sync/wait.h @@ -0,0 +1,48 @@ +#ifndef __KERN_SYNC_WAIT_H__ +#define __KERN_SYNC_WAIT_H__ + +#include + +typedef struct { + list_entry_t wait_head; +} wait_queue_t; + +struct proc_struct; + +typedef struct { + struct proc_struct *proc; + uint32_t wakeup_flags; + wait_queue_t *wait_queue; + list_entry_t wait_link; +} wait_t; + +#define le2wait(le, member) \ + to_struct((le), wait_t, member) + +void wait_init(wait_t *wait, struct proc_struct *proc); +void wait_queue_init(wait_queue_t *queue); +void wait_queue_add(wait_queue_t *queue, wait_t *wait); +void wait_queue_del(wait_queue_t *queue, wait_t *wait); + +wait_t *wait_queue_next(wait_queue_t *queue, wait_t *wait); +wait_t *wait_queue_prev(wait_queue_t *queue, wait_t *wait); +wait_t *wait_queue_first(wait_queue_t *queue); +wait_t *wait_queue_last(wait_queue_t *queue); + +bool wait_queue_empty(wait_queue_t *queue); +bool wait_in_queue(wait_t *wait); +void wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del); +void wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del); +void wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del); + +void wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state); + +#define wait_current_del(queue, wait) \ + do { \ + if (wait_in_queue(wait)) { \ + wait_queue_del(queue, wait); \ + } \ + } while (0) + +#endif /* !__KERN_SYNC_WAIT_H__ */ + diff --git a/lab8_practice/kern/syscall/syscall.c b/lab8_practice/kern/syscall/syscall.c new file mode 100644 index 0000000..4079fcd --- /dev/null +++ b/lab8_practice/kern/syscall/syscall.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +static int +sys_exit(uint64_t arg[]) { + int error_code = (int)arg[0]; + return do_exit(error_code); +} + +static int +sys_fork(uint64_t arg[]) { + struct trapframe *tf = current->tf; + uintptr_t stack = tf->gpr.sp; + return do_fork(0, stack, tf); +} + +static int +sys_wait(uint64_t arg[]) { + int pid = (int)arg[0]; + int *store = (int *)arg[1]; + return do_wait(pid, store); +} +static int +sys_exec(uint64_t arg[]) { + const char *name = (const char *)(arg[0]); + int argc = (int)arg[1]; + const char **argv = (const char **)arg[2]; + return do_execve(name, argc, argv); +} + +static int +sys_yield(uint64_t arg[]) { + return do_yield(); +} + +static int +sys_kill(uint64_t arg[]) { + int pid = (int)arg[0]; + return do_kill(pid); +} + +static int +sys_getpid(uint64_t arg[]) { + return current->pid; +} + +static int +sys_putc(uint64_t arg[]) { + int c = (int)arg[0]; + cputchar(c); + return 0; +} + +static int +sys_pgdir(uint64_t arg[]) { + //print_pgdir(); + return 0; +} +static int sys_gettime(uint64_t arg[]){ + return (int)ticks*10; +} +static int sys_lab6_set_priority(uint64_t arg[]){ + uint64_t priority = (uint64_t)arg[0]; + lab6_set_priority(priority); + return 0; +} +static int +sys_sleep(uint64_t arg[]) { + unsigned int time = (unsigned int)arg[0]; + return do_sleep(time); +} +static int +sys_open(uint64_t arg[]) { + const char *path = (const char *)arg[0]; + uint32_t open_flags = (uint32_t)arg[1]; + return sysfile_open(path, open_flags); +} + +static int +sys_close(uint64_t arg[]) { + int fd = (int)arg[0]; + return sysfile_close(fd); +} + +static int +sys_read(uint64_t arg[]) { + int fd = (int)arg[0]; + void *base = (void *)arg[1]; + size_t len = (size_t)arg[2]; + return sysfile_read(fd, base, len); +} + +static int +sys_write(uint64_t arg[]) { + int fd = (int)arg[0]; + void *base = (void *)arg[1]; + size_t len = (size_t)arg[2]; + return sysfile_write(fd, base, len); +} + +static int +sys_seek(uint64_t arg[]) { + int fd = (int)arg[0]; + off_t pos = (off_t)arg[1]; + int whence = (int)arg[2]; + return sysfile_seek(fd, pos, whence); +} + +static int +sys_fstat(uint64_t arg[]) { + int fd = (int)arg[0]; + struct stat *stat = (struct stat *)arg[1]; + return sysfile_fstat(fd, stat); +} + +static int +sys_fsync(uint64_t arg[]) { + int fd = (int)arg[0]; + return sysfile_fsync(fd); +} + +static int +sys_getcwd(uint64_t arg[]) { + char *buf = (char *)arg[0]; + size_t len = (size_t)arg[1]; + return sysfile_getcwd(buf, len); +} + +static int +sys_getdirentry(uint64_t arg[]) { + int fd = (int)arg[0]; + struct dirent *direntp = (struct dirent *)arg[1]; + return sysfile_getdirentry(fd, direntp); +} + +static int +sys_dup(uint64_t arg[]) { + int fd1 = (int)arg[0]; + int fd2 = (int)arg[1]; + return sysfile_dup(fd1, fd2); +} +static int (*syscalls[])(uint64_t arg[]) = { + [SYS_exit] sys_exit, + [SYS_fork] sys_fork, + [SYS_wait] sys_wait, + [SYS_exec] sys_exec, + [SYS_yield] sys_yield, + [SYS_kill] sys_kill, + [SYS_getpid] sys_getpid, + [SYS_putc] sys_putc, + [SYS_pgdir] sys_pgdir, + [SYS_gettime] sys_gettime, + [SYS_lab6_set_priority] sys_lab6_set_priority, + [SYS_sleep] sys_sleep, + [SYS_open] sys_open, + [SYS_close] sys_close, + [SYS_read] sys_read, + [SYS_write] sys_write, + [SYS_seek] sys_seek, + [SYS_fstat] sys_fstat, + [SYS_fsync] sys_fsync, + [SYS_getcwd] sys_getcwd, + [SYS_getdirentry] sys_getdirentry, + [SYS_dup] sys_dup, +}; + +#define NUM_SYSCALLS ((sizeof(syscalls)) / (sizeof(syscalls[0]))) + +void +syscall(void) { + struct trapframe *tf = current->tf; + uint64_t arg[5]; + int num = tf->gpr.a0; + if (num >= 0 && num < NUM_SYSCALLS) { + if (syscalls[num] != NULL) { + arg[0] = tf->gpr.a1; + arg[1] = tf->gpr.a2; + arg[2] = tf->gpr.a3; + arg[3] = tf->gpr.a4; + arg[4] = tf->gpr.a5; + tf->gpr.a0 = syscalls[num](arg); + return ; + } + } + print_trapframe(tf); + panic("undefined syscall %d, pid = %d, name = %s.\n", + num, current->pid, current->name); +} + diff --git a/lab8_practice/kern/syscall/syscall.h b/lab8_practice/kern/syscall/syscall.h new file mode 100644 index 0000000..a8fe843 --- /dev/null +++ b/lab8_practice/kern/syscall/syscall.h @@ -0,0 +1,7 @@ +#ifndef __KERN_SYSCALL_SYSCALL_H__ +#define __KERN_SYSCALL_SYSCALL_H__ + +void syscall(void); + +#endif /* !__KERN_SYSCALL_SYSCALL_H__ */ + diff --git a/lab8_practice/kern/trap/trap.c b/lab8_practice/kern/trap/trap.c new file mode 100644 index 0000000..1fdda7b --- /dev/null +++ b/lab8_practice/kern/trap/trap.c @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TICK_NUM 2 + +static void print_ticks() { + cprintf("%d ticks\n",TICK_NUM); +#ifdef DEBUG_GRADE + cprintf("End of Test.\n"); + panic("EOT: kernel seems ok."); +#endif +} + +/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */ +void +idt_init(void) { + extern void __alltraps(void); + /* Set sscratch register to 0, indicating to exception vector that we are + * presently executing in the kernel */ + write_csr(sscratch, 0); + /* Set the exception vector address */ + write_csr(stvec, &__alltraps); + /* Allow kernel to access user memory */ + set_csr(sstatus, SSTATUS_SUM); +} + +/* trap_in_kernel - test if trap happened in kernel */ +bool trap_in_kernel(struct trapframe *tf) { + return (tf->status & SSTATUS_SPP) != 0; +} + +void +print_trapframe(struct trapframe *tf) { + cprintf("trapframe at %p\n", tf); + print_regs(&tf->gpr); + cprintf(" status 0x%08x\n", tf->status); + cprintf(" epc 0x%08x\n", tf->epc); + cprintf(" tval 0x%08x\n", tf->tval); + cprintf(" cause 0x%08x\n", tf->cause); +} + +void print_regs(struct pushregs* gpr) { + cprintf(" zero 0x%08x\n", gpr->zero); + cprintf(" ra 0x%08x\n", gpr->ra); + cprintf(" sp 0x%08x\n", gpr->sp); + cprintf(" gp 0x%08x\n", gpr->gp); + cprintf(" tp 0x%08x\n", gpr->tp); + cprintf(" t0 0x%08x\n", gpr->t0); + cprintf(" t1 0x%08x\n", gpr->t1); + cprintf(" t2 0x%08x\n", gpr->t2); + cprintf(" s0 0x%08x\n", gpr->s0); + cprintf(" s1 0x%08x\n", gpr->s1); + cprintf(" a0 0x%08x\n", gpr->a0); + cprintf(" a1 0x%08x\n", gpr->a1); + cprintf(" a2 0x%08x\n", gpr->a2); + cprintf(" a3 0x%08x\n", gpr->a3); + cprintf(" a4 0x%08x\n", gpr->a4); + cprintf(" a5 0x%08x\n", gpr->a5); + cprintf(" a6 0x%08x\n", gpr->a6); + cprintf(" a7 0x%08x\n", gpr->a7); + cprintf(" s2 0x%08x\n", gpr->s2); + cprintf(" s3 0x%08x\n", gpr->s3); + cprintf(" s4 0x%08x\n", gpr->s4); + cprintf(" s5 0x%08x\n", gpr->s5); + cprintf(" s6 0x%08x\n", gpr->s6); + cprintf(" s7 0x%08x\n", gpr->s7); + cprintf(" s8 0x%08x\n", gpr->s8); + cprintf(" s9 0x%08x\n", gpr->s9); + cprintf(" s10 0x%08x\n", gpr->s10); + cprintf(" s11 0x%08x\n", gpr->s11); + cprintf(" t3 0x%08x\n", gpr->t3); + cprintf(" t4 0x%08x\n", gpr->t4); + cprintf(" t5 0x%08x\n", gpr->t5); + cprintf(" t6 0x%08x\n", gpr->t6); +} + +static inline void print_pgfault(struct trapframe *tf) { + cprintf("page falut at 0x%08x: %c/%c\n", tf->tval, + trap_in_kernel(tf) ? 'K' : 'U', + tf->cause == CAUSE_STORE_PAGE_FAULT ? 'W' : 'R'); +} + +static int +pgfault_handler(struct trapframe *tf) { + extern struct mm_struct *check_mm_struct; + if(check_mm_struct !=NULL) { //used for test check_swap + print_pgfault(tf); + } + struct mm_struct *mm; + if (check_mm_struct != NULL) { + assert(current == idleproc); + mm = check_mm_struct; + } + else { + if (current == NULL) { + print_trapframe(tf); + print_pgfault(tf); + panic("unhandled page fault.\n"); + } + mm = current->mm; + } + return do_pgfault(mm, tf->cause, tf->tval); +} + +static volatile int in_swap_tick_event = 0; +extern struct mm_struct *check_mm_struct; + +void interrupt_handler(struct trapframe *tf) { + intptr_t cause = (tf->cause << 1) >> 1; + switch (cause) { + case IRQ_U_SOFT: + cprintf("User software interrupt\n"); + break; + case IRQ_S_SOFT: + cprintf("Supervisor software interrupt\n"); + break; + case IRQ_H_SOFT: + cprintf("Hypervisor software interrupt\n"); + break; + case IRQ_M_SOFT: + cprintf("Machine software interrupt\n"); + break; + case IRQ_U_TIMER: + cprintf("User software interrupt\n"); + break; + case IRQ_S_TIMER: + // "All bits besides SSIP and USIP in the sip register are + // read-only." -- privileged spec1.9.1, 4.1.4, p59 + // In fact, Call sbi_set_timer will clear STIP, or you can clear it + // directly. + // clear_csr(sip, SIP_STIP); + clock_set_next_event(); + ++ticks; + run_timer_list(); + dev_stdin_write(cons_getc()); + break; + case IRQ_H_TIMER: + cprintf("Hypervisor software interrupt\n"); + break; + case IRQ_M_TIMER: + cprintf("Machine software interrupt\n"); + break; + case IRQ_U_EXT: + cprintf("User software interrupt\n"); + break; + case IRQ_S_EXT: + cprintf("Supervisor external interrupt\n"); + break; + case IRQ_H_EXT: + cprintf("Hypervisor software interrupt\n"); + break; + case IRQ_M_EXT: + cprintf("Machine software interrupt\n"); + break; + default: + print_trapframe(tf); + break; + } +} + +void exception_handler(struct trapframe *tf) { + int ret; + switch (tf->cause) { + case CAUSE_MISALIGNED_FETCH: + cprintf("Instruction address misaligned\n"); + break; + case CAUSE_FETCH_ACCESS: + cprintf("Instruction access fault\n"); + break; + case CAUSE_ILLEGAL_INSTRUCTION: + cprintf("Illegal instruction\n"); + break; + case CAUSE_BREAKPOINT: + cprintf("Breakpoint\n"); + if(tf->gpr.a7 == 10){ + tf->epc += 4; + syscall(); + } + break; + case CAUSE_MISALIGNED_LOAD: + cprintf("Load address misaligned\n"); + break; + case CAUSE_LOAD_ACCESS: + cprintf("Load access fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + case CAUSE_MISALIGNED_STORE: + panic("AMO address misaligned\n"); + break; + case CAUSE_STORE_ACCESS: + cprintf("Store/AMO access fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + case CAUSE_USER_ECALL: + //cprintf("Environment call from U-mode\n"); + tf->epc += 4; + syscall(); + break; + case CAUSE_SUPERVISOR_ECALL: + cprintf("Environment call from S-mode\n"); + tf->epc += 4; + syscall(); + break; + case CAUSE_HYPERVISOR_ECALL: + cprintf("Environment call from H-mode\n"); + break; + case CAUSE_MACHINE_ECALL: + cprintf("Environment call from M-mode\n"); + break; + case CAUSE_FETCH_PAGE_FAULT: + cprintf("Instruction page fault\n"); + break; + case CAUSE_LOAD_PAGE_FAULT: + cprintf("Load page fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + case CAUSE_STORE_PAGE_FAULT: + cprintf("Store/AMO page fault\n"); + if ((ret = pgfault_handler(tf)) != 0) { + print_trapframe(tf); + panic("handle pgfault failed. %e\n", ret); + } + break; + default: + print_trapframe(tf); + break; + } +} + +static inline void trap_dispatch(struct trapframe* tf) { + if ((intptr_t)tf->cause < 0) { + // interrupts + interrupt_handler(tf); + } else { + // exceptions + exception_handler(tf); + } +} + +/* * + * trap - handles or dispatches an exception/interrupt. if and when trap() returns, + * the code in kern/trap/trapentry.S restores the old CPU state saved in the + * trapframe and then uses the iret instruction to return from the exception. + * */ +void +trap(struct trapframe *tf) { + // dispatch based on what type of trap occurred + if (current == NULL) { + trap_dispatch(tf); + } else { + struct trapframe *otf = current->tf; + current->tf = tf; + + bool in_kernel = trap_in_kernel(tf); + + trap_dispatch(tf); + + current->tf = otf; + if (!in_kernel) { + if (current->flags & PF_EXITING) { + do_exit(-E_KILLED); + } + if (current->need_resched) { + schedule(); + } + } + } +} + + diff --git a/lab8_practice/kern/trap/trap.h b/lab8_practice/kern/trap/trap.h new file mode 100644 index 0000000..40f4816 --- /dev/null +++ b/lab8_practice/kern/trap/trap.h @@ -0,0 +1,56 @@ +#ifndef __KERN_TRAP_TRAP_H__ +#define __KERN_TRAP_TRAP_H__ + +#include + +struct pushregs { + uintptr_t zero; // Hard-wired zero + uintptr_t ra; // Return address + uintptr_t sp; // Stack pointer + uintptr_t gp; // Global pointer + uintptr_t tp; // Thread pointer + uintptr_t t0; // Temporary + uintptr_t t1; // Temporary + uintptr_t t2; // Temporary + uintptr_t s0; // Saved register/frame pointer + uintptr_t s1; // Saved register + uintptr_t a0; // Function argument/return value + uintptr_t a1; // Function argument/return value + uintptr_t a2; // Function argument + uintptr_t a3; // Function argument + uintptr_t a4; // Function argument + uintptr_t a5; // Function argument + uintptr_t a6; // Function argument + uintptr_t a7; // Function argument + uintptr_t s2; // Saved register + uintptr_t s3; // Saved register + uintptr_t s4; // Saved register + uintptr_t s5; // Saved register + uintptr_t s6; // Saved register + uintptr_t s7; // Saved register + uintptr_t s8; // Saved register + uintptr_t s9; // Saved register + uintptr_t s10; // Saved register + uintptr_t s11; // Saved register + uintptr_t t3; // Temporary + uintptr_t t4; // Temporary + uintptr_t t5; // Temporary + uintptr_t t6; // Temporary +}; + +struct trapframe { + struct pushregs gpr; + uintptr_t status; + uintptr_t epc; + uintptr_t tval; + uintptr_t cause; +}; + +void trap(struct trapframe *tf); +void idt_init(void); +void print_trapframe(struct trapframe *tf); +void print_regs(struct pushregs* gpr); +bool trap_in_kernel(struct trapframe *tf); + +#endif /* !__KERN_TRAP_TRAP_H__ */ + diff --git a/lab8_practice/kern/trap/trapentry.S b/lab8_practice/kern/trap/trapentry.S new file mode 100644 index 0000000..f93d21f --- /dev/null +++ b/lab8_practice/kern/trap/trapentry.S @@ -0,0 +1,139 @@ +#include + + .altmacro + .align 2 + .macro SAVE_ALL + LOCAL _restore_kernel_sp + LOCAL _save_context + + # If coming from userspace, preserve the user stack pointer and load + # the kernel stack pointer. If we came from the kernel, sscratch + # will contain 0, and we should continue on the current stack. + csrrw sp, sscratch, sp + bnez sp, _save_context + +_restore_kernel_sp: + csrr sp, sscratch +_save_context: + addi sp, sp, -36 * REGBYTES + # save x registers + STORE x0, 0*REGBYTES(sp) + STORE x1, 1*REGBYTES(sp) + STORE x3, 3*REGBYTES(sp) + STORE x4, 4*REGBYTES(sp) + STORE x5, 5*REGBYTES(sp) + STORE x6, 6*REGBYTES(sp) + STORE x7, 7*REGBYTES(sp) + STORE x8, 8*REGBYTES(sp) + STORE x9, 9*REGBYTES(sp) + STORE x10, 10*REGBYTES(sp) + STORE x11, 11*REGBYTES(sp) + STORE x12, 12*REGBYTES(sp) + STORE x13, 13*REGBYTES(sp) + STORE x14, 14*REGBYTES(sp) + STORE x15, 15*REGBYTES(sp) + STORE x16, 16*REGBYTES(sp) + STORE x17, 17*REGBYTES(sp) + STORE x18, 18*REGBYTES(sp) + STORE x19, 19*REGBYTES(sp) + STORE x20, 20*REGBYTES(sp) + STORE x21, 21*REGBYTES(sp) + STORE x22, 22*REGBYTES(sp) + STORE x23, 23*REGBYTES(sp) + STORE x24, 24*REGBYTES(sp) + STORE x25, 25*REGBYTES(sp) + STORE x26, 26*REGBYTES(sp) + STORE x27, 27*REGBYTES(sp) + STORE x28, 28*REGBYTES(sp) + STORE x29, 29*REGBYTES(sp) + STORE x30, 30*REGBYTES(sp) + STORE x31, 31*REGBYTES(sp) + + # get sr, epc, tval, cause + # Set sscratch register to 0, so that if a recursive exception + # occurs, the exception vector knows it came from the kernel + csrrw s0, sscratch, x0 + csrr s1, sstatus + csrr s2, sepc + csrr s3, 0x143 + csrr s4, scause + + STORE s0, 2*REGBYTES(sp) + STORE s1, 32*REGBYTES(sp) + STORE s2, 33*REGBYTES(sp) + STORE s3, 34*REGBYTES(sp) + STORE s4, 35*REGBYTES(sp) + .endm + + .macro RESTORE_ALL + LOCAL _save_kernel_sp + LOCAL _restore_context + + LOAD s1, 32*REGBYTES(sp) + LOAD s2, 33*REGBYTES(sp) + + andi s0, s1, SSTATUS_SPP + bnez s0, _restore_context + +_save_kernel_sp: + # Save unwound kernel stack pointer in sscratch + addi s0, sp, 36 * REGBYTES + csrw sscratch, s0 +_restore_context: + csrw sstatus, s1 + csrw sepc, s2 + + # restore x registers + LOAD x1, 1*REGBYTES(sp) + LOAD x3, 3*REGBYTES(sp) + LOAD x4, 4*REGBYTES(sp) + LOAD x5, 5*REGBYTES(sp) + LOAD x6, 6*REGBYTES(sp) + LOAD x7, 7*REGBYTES(sp) + LOAD x8, 8*REGBYTES(sp) + LOAD x9, 9*REGBYTES(sp) + LOAD x10, 10*REGBYTES(sp) + LOAD x11, 11*REGBYTES(sp) + LOAD x12, 12*REGBYTES(sp) + LOAD x13, 13*REGBYTES(sp) + LOAD x14, 14*REGBYTES(sp) + LOAD x15, 15*REGBYTES(sp) + LOAD x16, 16*REGBYTES(sp) + LOAD x17, 17*REGBYTES(sp) + LOAD x18, 18*REGBYTES(sp) + LOAD x19, 19*REGBYTES(sp) + LOAD x20, 20*REGBYTES(sp) + LOAD x21, 21*REGBYTES(sp) + LOAD x22, 22*REGBYTES(sp) + LOAD x23, 23*REGBYTES(sp) + LOAD x24, 24*REGBYTES(sp) + LOAD x25, 25*REGBYTES(sp) + LOAD x26, 26*REGBYTES(sp) + LOAD x27, 27*REGBYTES(sp) + LOAD x28, 28*REGBYTES(sp) + LOAD x29, 29*REGBYTES(sp) + LOAD x30, 30*REGBYTES(sp) + LOAD x31, 31*REGBYTES(sp) + # restore sp last + LOAD x2, 2*REGBYTES(sp) + .endm + + .globl __alltraps +__alltraps: + SAVE_ALL + + move a0, sp + jal trap + # sp should be the same as before "jal trap" + + .globl __trapret +__trapret: + RESTORE_ALL + # return from supervisor call + sret + + .globl forkrets +forkrets: + # set stack to this new process's trapframe + move sp, a0 + j __trapret diff --git a/lab8_practice/lab5.md b/lab8_practice/lab5.md new file mode 100644 index 0000000..c340794 --- /dev/null +++ b/lab8_practice/lab5.md @@ -0,0 +1,62 @@ +# 实验五:用户进程管理 + +## 练习一 + +### 实现方法 + +* 将中断帧的保存的cs和ds、es、ss分别指向用户代码段和用户数据段 +* 将esp指向用户栈的栈顶 +* 将eip设置为ELF文件提供的程序入口地址 +* 设置eflags中控制中断的位 + +用户态进程从创建到执行 + +* 调用KERNEL_EXECVE宏,通过kernel_execve函数发出系统调用 +* 经过多次函数调用到达do_execve函数 +* 进入load_icode读入程序的数据段和代码段 +* 修改栈帧 +* 逐步返回,最后iret,恢复栈帧后程序开始运行 +* 首先进入initcode.S,将ebp设为0,esp减小(用于printstackframe) +* 调用umain +* umain中调用main()函数,开始执行程序 + +## 练习二 + +### Copy on Write + +* 在do_fork时不进行内存复制,只将对应内存页的页目录项中的R/W Bit设为只读 +* 一旦发生写操作,就会引发page fault +* 在中断处理程序中恢复内存页的R/W状态 +* 进行程序代码段、数据段的复制 +* 返回继续执行 + +## 练习三 + +### fork/exec/wait/exit + +用户态的fork/exec/wait/exit函数会调用/usrlibs/syscall.h中的相应函数,然后由`static inline int syscall(int num, ...)`中的内联汇编发出中断,通过系统调用实现各个功能。 + +用户态进程的执行状态生命周期图如下: + +``` + alloc_proc RUNNING + | +--<----<--+ + | | proc_run | + V +-->---->--+ +PROC_UNINIT --> proc_init/wakeup_proc --> PROC_RUNNABLE --> try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --+ + ^ | | + | +--- do_exit --> PROC_ZOMBIE | + | | + +----------------------wakeup_proc-----------------------------------+ +``` + +## 总结 + +### 实现与参考答案的区别 + +实现相同 + +### 知识点 + +* 用户进程管理 +* 系统调用实现:系统调用通过中断实现 diff --git a/lab8_practice/libs/atomic.h b/lab8_practice/libs/atomic.h new file mode 100644 index 0000000..394c30d --- /dev/null +++ b/lab8_practice/libs/atomic.h @@ -0,0 +1,109 @@ +#ifndef __LIBS_ATOMIC_H__ +#define __LIBS_ATOMIC_H__ + +/* Atomic operations that C can't guarantee us. Useful for resource counting + * etc.. */ + +static inline void set_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline void clear_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline void change_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline bool test_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline bool test_and_set_bit(int nr, volatile void *addr) + __attribute__((always_inline)); +static inline bool test_and_clear_bit(int nr, volatile void *addr) + __attribute__((always_inline)); + +#define BITS_PER_LONG __riscv_xlen + +#if (BITS_PER_LONG == 64) +#define __AMO(op) "amo" #op ".d" +#elif (BITS_PER_LONG == 32) +#define __AMO(op) "amo" #op ".w" +#else +#error "Unexpected BITS_PER_LONG" +#endif + +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) + +#define __test_and_op_bit(op, mod, nr, addr) \ + ({ \ + unsigned long __res, __mask; \ + __mask = BIT_MASK(nr); \ + __asm__ __volatile__(__AMO(op) " %0, %2, %1" \ + : "=r"(__res), "+A"(addr[BIT_WORD(nr)]) \ + : "r"(mod(__mask))); \ + ((__res & __mask) != 0); \ + }) + +#define __op_bit(op, mod, nr, addr) \ + __asm__ __volatile__(__AMO(op) " zero, %1, %0" \ + : "+A"(addr[BIT_WORD(nr)]) \ + : "r"(mod(BIT_MASK(nr)))) + +/* Bitmask modifiers */ +#define __NOP(x) (x) +#define __NOT(x) (~(x)) + +/* * + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + * */ +static inline void set_bit(int nr, volatile void *addr) { + __op_bit(or, __NOP, nr, ((volatile unsigned long *)addr)); +} + +/* * + * clear_bit - Atomically clears a bit in memory + * @nr: the bit to clear + * @addr: the address to start counting from + * */ +static inline void clear_bit(int nr, volatile void *addr) { + __op_bit(and, __NOT, nr, ((volatile unsigned long *)addr)); +} + +/* * + * change_bit - Atomically toggle a bit in memory + * @nr: the bit to change + * @addr: the address to start counting from + * */ +static inline void change_bit(int nr, volatile void *addr) { + __op_bit (xor, __NOP, nr, ((volatile unsigned long *)addr)); +} + +/* * + * test_bit - Determine whether a bit is set + * @nr: the bit to test + * @addr: the address to count from + * */ +static inline bool test_bit(int nr, volatile void *addr) { + return (((*(volatile unsigned long *)addr) >> nr) & 1); +} + +/* * + * test_and_set_bit - Atomically set a bit and return its old value + * @nr: the bit to set + * @addr: the address to count from + * */ +static inline bool test_and_set_bit(int nr, volatile void *addr) { + return __test_and_op_bit(or, __NOP, nr, ((volatile unsigned long *)addr)); +} + +/* * + * test_and_clear_bit - Atomically clear a bit and return its old value + * @nr: the bit to clear + * @addr: the address to count from + * */ +static inline bool test_and_clear_bit(int nr, volatile void *addr) { + return __test_and_op_bit(and, __NOT, nr, ((volatile unsigned long *)addr)); +} + +#endif /* !__LIBS_ATOMIC_H__ */ diff --git a/lab8_practice/libs/defs.h b/lab8_practice/libs/defs.h new file mode 100644 index 0000000..a19867a --- /dev/null +++ b/lab8_practice/libs/defs.h @@ -0,0 +1,84 @@ +#ifndef __LIBS_DEFS_H__ +#define __LIBS_DEFS_H__ + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define __always_inline inline __attribute__((always_inline)) +#define __noinline __attribute__((noinline)) +#define __noreturn __attribute__((noreturn)) + +#define CHAR_BIT 8 + +/* Represents true-or-false values */ +typedef long long bool; + +/* Explicitly-sized versions of integer types */ +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#if __riscv_xlen == 64 + typedef uint64_t uint_t; + typedef int64_t sint_t; +#elif __riscv_xlen == 32 + typedef uint32_t uint_t; + typedef int32_t sint_t; +#endif + +/* * + * Pointers and addresses are 32 bits long. + * We use pointer types to represent addresses, + * uintptr_t to represent the numerical values of addresses. + * */ +typedef sint_t intptr_t; +typedef uint_t uintptr_t; + +/* size_t is used for memory object sizes */ +typedef uintptr_t size_t; +/* off_t is used for file offsets and lengths */ +typedef intptr_t off_t; +/* used for page numbers */ +typedef size_t ppn_t; + +/* * + * Rounding operations (efficient when n is a power of 2) + * Round down to the nearest multiple of n + * */ +#define ROUNDDOWN(a, n) ({ \ + size_t __a = (size_t)(a); \ + (typeof(a))(__a - __a % (n)); \ + }) + +/* Round up to the nearest multiple of n */ +#define ROUNDUP(a, n) ({ \ + size_t __n = (size_t)(n); \ + (typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \ + }) + +/* Round up the result of dividing of n */ +#define ROUNDUP_DIV(a, n) ({ \ +uint64_t __n = (uint64_t)(n); \ +(typeof(a))(((a) + __n - 1) / __n); \ +}) + +/* Return the offset of 'member' relative to the beginning of a struct type */ +#define offsetof(type, member) \ + ((size_t)(&((type *)0)->member)) + +/* * + * to_struct - get the struct from a ptr + * @ptr: a struct pointer of member + * @type: the type of the struct this is embedded in + * @member: the name of the member within the struct + * */ +#define to_struct(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +#endif /* !__LIBS_DEFS_H__ */ + diff --git a/lab8_practice/libs/dirent.h b/lab8_practice/libs/dirent.h new file mode 100644 index 0000000..429e4fe --- /dev/null +++ b/lab8_practice/libs/dirent.h @@ -0,0 +1,13 @@ +#ifndef __LIBS_DIRENT_H__ +#define __LIBS_DIRENT_H__ + +#include +#include + +struct dirent { + off_t offset; + char name[FS_MAX_FNAME_LEN + 1]; +}; + +#endif /* !__LIBS_DIRENT_H__ */ + diff --git a/lab8_practice/libs/elf.h b/lab8_practice/libs/elf.h new file mode 100644 index 0000000..867066f --- /dev/null +++ b/lab8_practice/libs/elf.h @@ -0,0 +1,48 @@ +#ifndef __LIBS_ELF_H__ +#define __LIBS_ELF_H__ + +#include + +#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian + +/* file header */ +struct elfhdr { + uint32_t e_magic; // must equal ELF_MAGIC + uint8_t e_elf[12]; + uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image + uint16_t e_machine; // 3=x86, 4=68K, etc. + uint32_t e_version; // file version, always 1 + uint64_t e_entry; // entry point if executable + uint64_t e_phoff; // file position of program header or 0 + uint64_t e_shoff; // file position of section header or 0 + uint32_t e_flags; // architecture-specific flags, usually 0 + uint16_t e_ehsize; // size of this elf header + uint16_t e_phentsize; // size of an entry in program header + uint16_t e_phnum; // number of entries in program header or 0 + uint16_t e_shentsize; // size of an entry in section header + uint16_t e_shnum; // number of entries in section header or 0 + uint16_t e_shstrndx; // section number that contains section name strings +}; + +/* program section header */ +struct proghdr { + uint32_t p_type; // loadable code or data, dynamic linking info,etc. + uint32_t p_flags; // read/write/execute bits + uint64_t p_offset; // file offset of segment + uint64_t p_va; // virtual address to map segment + uint64_t p_pa; // physical address, not used + uint64_t p_filesz; // size of segment in file + uint64_t p_memsz; // size of segment in memory (bigger if contains bss) + uint64_t p_align; // required alignment, invariably hardware page size +}; + +/* values for Proghdr::p_type */ +#define ELF_PT_LOAD 1 + +/* flag bits for Proghdr::p_flags */ +#define ELF_PF_X 1 +#define ELF_PF_W 2 +#define ELF_PF_R 4 + +#endif /* !__LIBS_ELF_H__ */ + diff --git a/lab8_practice/libs/error.h b/lab8_practice/libs/error.h new file mode 100644 index 0000000..ddad593 --- /dev/null +++ b/lab8_practice/libs/error.h @@ -0,0 +1,33 @@ +#ifndef __LIBS_ERROR_H__ +#define __LIBS_ERROR_H__ + +/* kernel error codes -- keep in sync with list in lib/printfmt.c */ +#define E_UNSPECIFIED 1 // Unspecified or unknown problem +#define E_BAD_PROC 2 // Process doesn't exist or otherwise +#define E_INVAL 3 // Invalid parameter +#define E_NO_MEM 4 // Request failed due to memory shortage +#define E_NO_FREE_PROC 5 // Attempt to create a new process beyond +#define E_FAULT 6 // Memory fault +#define E_SWAP_FAULT 7 // SWAP READ/WRITE fault +#define E_INVAL_ELF 8 // Invalid elf file +#define E_KILLED 9 // Process is killed +#define E_PANIC 10 // Panic Failure +#define E_TIMEOUT 11 // Timeout +#define E_TOO_BIG 12 // Argument is Too Big +#define E_NO_DEV 13 // No such Device +#define E_NA_DEV 14 // Device Not Available +#define E_BUSY 15 // Device/File is Busy +#define E_NOENT 16 // No Such File or Directory +#define E_ISDIR 17 // Is a Directory +#define E_NOTDIR 18 // Not a Directory +#define E_XDEV 19 // Cross Device-Link +#define E_UNIMP 20 // Unimplemented Feature +#define E_SEEK 21 // Illegal Seek +#define E_MAX_OPEN 22 // Too Many Files are Open +#define E_EXISTS 23 // File/Directory Already Exists +#define E_NOTEMPTY 24 // Directory is Not Empty +/* the maximum allowed */ +#define MAXERROR 24 + +#endif /* !__LIBS_ERROR_H__ */ + diff --git a/lab8_practice/libs/hash.c b/lab8_practice/libs/hash.c new file mode 100644 index 0000000..61bcd88 --- /dev/null +++ b/lab8_practice/libs/hash.c @@ -0,0 +1,18 @@ +#include + +/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ +#define GOLDEN_RATIO_PRIME_32 0x9e370001UL + +/* * + * hash32 - generate a hash value in the range [0, 2^@bits - 1] + * @val: the input value + * @bits: the number of bits in a return value + * + * High bits are more random, so we use them. + * */ +uint32_t +hash32(uint32_t val, unsigned int bits) { + uint32_t hash = val * GOLDEN_RATIO_PRIME_32; + return (hash >> (32 - bits)); +} + diff --git a/lab8_practice/libs/list.h b/lab8_practice/libs/list.h new file mode 100644 index 0000000..3dbf772 --- /dev/null +++ b/lab8_practice/libs/list.h @@ -0,0 +1,163 @@ +#ifndef __LIBS_LIST_H__ +#define __LIBS_LIST_H__ + +#ifndef __ASSEMBLER__ + +#include + +/* * + * Simple 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. + * */ + +struct list_entry { + struct list_entry *prev, *next; +}; + +typedef struct list_entry list_entry_t; + +static inline void list_init(list_entry_t *elm) __attribute__((always_inline)); +static inline void list_add(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); +static inline void list_add_before(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); +static inline void list_add_after(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); +static inline void list_del(list_entry_t *listelm) __attribute__((always_inline)); +static inline void list_del_init(list_entry_t *listelm) __attribute__((always_inline)); +static inline bool list_empty(list_entry_t *list) __attribute__((always_inline)); +static inline list_entry_t *list_next(list_entry_t *listelm) __attribute__((always_inline)); +static inline list_entry_t *list_prev(list_entry_t *listelm) __attribute__((always_inline)); + +static inline void __list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); +static inline void __list_del(list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); + +/* * + * list_init - initialize a new entry + * @elm: new entry to be initialized + * */ +static inline void +list_init(list_entry_t *elm) { + elm->prev = elm->next = elm; +} + +/* * + * list_add - add a new entry + * @listelm: list head to add after + * @elm: new entry to be added + * + * Insert the new element @elm *after* the element @listelm which + * is already in the list. + * */ +static inline void +list_add(list_entry_t *listelm, list_entry_t *elm) { + list_add_after(listelm, elm); +} + +/* * + * list_add_before - add a new entry + * @listelm: list head to add before + * @elm: new entry to be added + * + * Insert the new element @elm *before* the element @listelm which + * is already in the list. + * */ +static inline void +list_add_before(list_entry_t *listelm, list_entry_t *elm) { + __list_add(elm, listelm->prev, listelm); +} + +/* * + * list_add_after - add a new entry + * @listelm: list head to add after + * @elm: new entry to be added + * + * Insert the new element @elm *after* the element @listelm which + * is already in the list. + * */ +static inline void +list_add_after(list_entry_t *listelm, list_entry_t *elm) { + __list_add(elm, listelm, listelm->next); +} + +/* * + * list_del - deletes entry from list + * @listelm: the element to delete from the list + * + * Note: list_empty() on @listelm does not return true after this, the entry is + * in an undefined state. + * */ +static inline void +list_del(list_entry_t *listelm) { + __list_del(listelm->prev, listelm->next); +} + +/* * + * list_del_init - deletes entry from list and reinitialize it. + * @listelm: the element to delete from the list. + * + * Note: list_empty() on @listelm returns true after this. + * */ +static inline void +list_del_init(list_entry_t *listelm) { + list_del(listelm); + list_init(listelm); +} + +/* * + * list_empty - tests whether a list is empty + * @list: the list to test. + * */ +static inline bool +list_empty(list_entry_t *list) { + return list->next == list; +} + +/* * + * list_next - get the next entry + * @listelm: the list head + **/ +static inline list_entry_t * +list_next(list_entry_t *listelm) { + return listelm->next; +} + +/* * + * list_prev - get the previous entry + * @listelm: the list head + **/ +static inline list_entry_t * +list_prev(list_entry_t *listelm) { + return listelm->prev; +} + +/* * + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + * */ +static inline void +__list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) { + prev->next = next->prev = elm; + elm->next = next; + elm->prev = prev; +} + +/* * + * 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(list_entry_t *prev, list_entry_t *next) { + prev->next = next; + next->prev = prev; +} + +#endif /* !__ASSEMBLER__ */ + +#endif /* !__LIBS_LIST_H__ */ + diff --git a/lab8_practice/libs/printfmt.c b/lab8_practice/libs/printfmt.c new file mode 100644 index 0000000..9c11ce5 --- /dev/null +++ b/lab8_practice/libs/printfmt.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include +#include +#include + +/* * + * Space or zero padding and a field width are supported for the numeric + * formats only. + * + * The special format %e takes an integer error code + * and prints a string describing the error. + * The integer may be positive or negative, + * so that -E_NO_MEM and E_NO_MEM are equivalent. + * */ + +static const char * const error_string[MAXERROR + 1] = { + [0] NULL, + [E_UNSPECIFIED] "unspecified error", + [E_BAD_PROC] "bad process", + [E_INVAL] "invalid parameter", + [E_NO_MEM] "out of memory", + [E_NO_FREE_PROC] "out of processes", + [E_FAULT] "segmentation fault", + [E_INVAL_ELF] "invalid elf file", + [E_KILLED] "process is killed", + [E_PANIC] "panic failure", + [E_NO_DEV] "no such device", + [E_NA_DEV] "device not available", + [E_BUSY] "device/file is busy", + [E_NOENT] "no such file or directory", + [E_ISDIR] "is a directory", + [E_NOTDIR] "not a directory", + [E_XDEV] "cross device link", + [E_UNIMP] "unimplemented feature", + [E_SEEK] "illegal seek", + [E_MAX_OPEN] "too many files are open", + [E_EXISTS] "file or directory already exists", + [E_NOTEMPTY] "directory is not empty", +}; + +/* * + * printnum - print a number (base <= 16) in reverse order + * @putch: specified putch function, print a single character + * @fd: file descriptor + * @putdat: used by @putch function + * @num: the number will be printed + * @base: base for print, must be in [1, 16] + * @width: maximum number of digits, if the actual width is less than @width, use @padc instead + * @padc: character that padded on the left if the actual width is less than @width + * */ +static void +printnum(void (*putch)(int, void*, int), int fd, void *putdat, + unsigned long long num, unsigned base, int width, int padc) { + unsigned long long result = num; + unsigned mod = do_div(result, base); + + // first recursively print all preceding (more significant) digits + if (num >= base) { + printnum(putch, fd, putdat, result, base, width - 1, padc); + } else { + // print any needed pad characters before first digit + while (-- width > 0) + putch(padc, putdat, fd); + } + // then print this (the least significant) digit + putch("0123456789abcdef"[mod], putdat, fd); +} + +/* * + * getuint - get an unsigned int of various possible sizes from a varargs list + * @ap: a varargs list pointer + * @lflag: determines the size of the vararg that @ap points to + * */ +static unsigned long long +getuint(va_list *ap, int lflag) { + if (lflag >= 2) { + return va_arg(*ap, unsigned long long); + } + else if (lflag) { + return va_arg(*ap, unsigned long); + } + else { + return va_arg(*ap, unsigned int); + } +} + +/* * + * getint - same as getuint but signed, we can't use getuint because of sign extension + * @ap: a varargs list pointer + * @lflag: determines the size of the vararg that @ap points to + * */ +static long long +getint(va_list *ap, int lflag) { + if (lflag >= 2) { + return va_arg(*ap, long long); + } + else if (lflag) { + return va_arg(*ap, long); + } + else { + return va_arg(*ap, int); + } +} + +/* * + * printfmt - format a string and print it by using putch + * @putch: specified putch function, print a single character + * @fd: file descriptor + * @putdat: used by @putch function + * @fmt: the format string to use + * */ +void +printfmt(void (*putch)(int, void*, int), int fd, void *putdat, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vprintfmt(putch, fd, putdat, fmt, ap); + va_end(ap); +} + +/* * + * vprintfmt - format a string and print it by using putch, it's called with a va_list + * instead of a variable number of arguments + * @fd: file descriptor + * @putch: specified putch function, print a single character + * @putdat: used by @putch function + * @fmt: the format string to use + * @ap: arguments for the format string + * + * Call this function if you are already dealing with a va_list. + * Or you probably want printfmt() instead. + * */ +void +vprintfmt(void (*putch)(int, void*, int), int fd, void *putdat, const char *fmt, va_list ap) { + register const char *p; + register int ch, err; + unsigned long long num; + int base, width, precision, lflag, altflag; + + while (1) { + while ((ch = *(unsigned char *)fmt ++) != '%') { + if (ch == '\0') { + return; + } + putch(ch, putdat, fd); + } + + // Process a %-escape sequence + char padc = ' '; + width = precision = -1; + lflag = altflag = 0; + + reswitch: + switch (ch = *(unsigned char *)fmt ++) { + + // flag to pad on the right + case '-': + padc = '-'; + goto reswitch; + + // flag to pad with 0's instead of spaces + case '0': + padc = '0'; + goto reswitch; + + // width field + case '1' ... '9': + for (precision = 0; ; ++ fmt) { + precision = precision * 10 + ch - '0'; + ch = *fmt; + if (ch < '0' || ch > '9') { + break; + } + } + goto process_precision; + + case '*': + precision = va_arg(ap, int); + goto process_precision; + + case '.': + if (width < 0) + width = 0; + goto reswitch; + + case '#': + altflag = 1; + goto reswitch; + + process_precision: + if (width < 0) + width = precision, precision = -1; + goto reswitch; + + // long flag (doubled for long long) + case 'l': + lflag ++; + goto reswitch; + + // character + case 'c': + putch(va_arg(ap, int), putdat, fd); + break; + + // error message + case 'e': + err = va_arg(ap, int); + if (err < 0) { + err = -err; + } + if (err > MAXERROR || (p = error_string[err]) == NULL) { + printfmt(putch, fd, putdat, "error %d", err); + } + else { + printfmt(putch, fd, putdat, "%s", p); + } + break; + + // string + case 's': + if ((p = va_arg(ap, char *)) == NULL) { + p = "(null)"; + } + if (width > 0 && padc != '-') { + for (width -= strnlen(p, precision); width > 0; width --) { + putch(padc, putdat, fd); + } + } + for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) { + if (altflag && (ch < ' ' || ch > '~')) { + putch('?', putdat, fd); + } + else { + putch(ch, putdat, fd); + } + } + for (; width > 0; width --) { + putch(' ', putdat, fd); + } + break; + + // (signed) decimal + case 'd': + num = getint(&ap, lflag); + if ((long long)num < 0) { + putch('-', putdat, fd); + num = -(long long)num; + } + base = 10; + goto number; + + // unsigned decimal + case 'u': + num = getuint(&ap, lflag); + base = 10; + goto number; + + // (unsigned) octal + case 'o': + num = getuint(&ap, lflag); + base = 8; + goto number; + + // pointer + case 'p': + putch('0', putdat, fd); + putch('x', putdat, fd); + num = (unsigned long long)(uintptr_t)va_arg(ap, void *); + base = 16; + goto number; + + // (unsigned) hexadecimal + case 'x': + num = getuint(&ap, lflag); + base = 16; + number: + printnum(putch, fd, putdat, num, base, width, padc); + break; + + // escaped '%' character + case '%': + putch(ch, putdat, fd); + break; + + // unrecognized escape sequence - just print it literally + default: + putch('%', putdat, fd); + for (fmt --; fmt[-1] != '%'; fmt --) + /* do nothing */; + break; + } + } +} + +/* sprintbuf is used to save enough information of a buffer */ +struct sprintbuf { + char *buf; // address pointer points to the first unused memory + char *ebuf; // points the end of the buffer + int cnt; // the number of characters that have been placed in this buffer +}; + +/* * + * sprintputch - 'print' a single character in a buffer + * @ch: the character will be printed + * @b: the buffer to place the character @ch + * */ +static void +sprintputch(int ch, struct sprintbuf *b) { + b->cnt ++; + if (b->buf < b->ebuf) { + *b->buf ++ = ch; + } +} + +/* * + * snprintf - format a string and place it in a buffer + * @str: the buffer to place the result into + * @size: the size of buffer, including the trailing null space + * @fmt: the format string to use + * */ +int +snprintf(char *str, size_t size, const char *fmt, ...) { + va_list ap; + int cnt; + va_start(ap, fmt); + cnt = vsnprintf(str, size, fmt, ap); + va_end(ap); + return cnt; +} + +/* * + * vsnprintf - format a string and place it in a buffer, it's called with a va_list + * instead of a variable number of arguments + * @str: the buffer to place the result into + * @size: the size of buffer, including the trailing null space + * @fmt: the format string to use + * @ap: arguments for the format string + * + * The return value is the number of characters which would be generated for the + * given input, excluding the trailing '\0'. + * + * Call this function if you are already dealing with a va_list. + * Or you probably want snprintf() instead. + * */ +int +vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { + struct sprintbuf b = {str, str + size - 1, 0}; + if (str == NULL || b.buf > b.ebuf) { + return -E_INVAL; + } + // print the string to the buffer + vprintfmt((void*)sprintputch, NO_FD, &b, fmt, ap); + // null terminate the buffer + *b.buf = '\0'; + return b.cnt; +} + diff --git a/lab8_practice/libs/rand.c b/lab8_practice/libs/rand.c new file mode 100644 index 0000000..ff74c71 --- /dev/null +++ b/lab8_practice/libs/rand.c @@ -0,0 +1,26 @@ +#include +#include + +static unsigned long long next = 1; + +/* * + * rand - returns a pseudo-random integer + * + * The rand() function return a value in the range [0, RAND_MAX]. + * */ +int +rand(void) { + next = (next * 0x5DEECE66DLL + 0xBLL) & ((1LL << 48) - 1); + unsigned long long result = (next >> 12); + return (int)do_div(result, RAND_MAX + 1); +} + +/* * + * srand - seed the random number generator with the given number + * @seed: the required seed number + * */ +void +srand(unsigned int seed) { + next = seed; +} + diff --git a/lab8_practice/libs/riscv.h b/lab8_practice/libs/riscv.h new file mode 100644 index 0000000..ff93c8c --- /dev/null +++ b/lab8_practice/libs/riscv.h @@ -0,0 +1,1502 @@ +// See LICENSE for license details. + +#ifndef __LIBS_RISCV_H__ +#define __LIBS_RISCV_H__ + +#if __riscv_xlen == 64 +# define SLL32 sllw +# define STORE sd +# define LOAD ld +# define LWU lwu +# define LOG_REGBYTES 3 +#else +# define SLL32 sll +# define STORE sw +# define LOAD lw +# define LWU lw +# define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_SUM 0x00040000 +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_TVM 0x00100000 +#define MSTATUS_TW 0x00200000 +#define MSTATUS_TSR 0x00400000 +#define MSTATUS32_SD 0x80000000 +#define MSTATUS_UXL 0x0000000300000000 +#define MSTATUS_SXL 0x0000000C00000000 +#define MSTATUS64_SD 0x8000000000000000 + +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_SUM 0x00040000 +#define SSTATUS_MXR 0x00080000 +#define SSTATUS32_SD 0x80000000 +#define SSTATUS_UXL 0x0000000300000000 +#define SSTATUS64_SD 0x8000000000000000 + +#define DCSR_XDEBUGVER (3U<<30) +#define DCSR_NDRESET (1<<29) +#define DCSR_FULLRESET (1<<28) +#define DCSR_EBREAKM (1<<15) +#define DCSR_EBREAKH (1<<14) +#define DCSR_EBREAKS (1<<13) +#define DCSR_EBREAKU (1<<12) +#define DCSR_STOPCYCLE (1<<10) +#define DCSR_STOPTIME (1<<9) +#define DCSR_CAUSE (7<<6) +#define DCSR_DEBUGINT (1<<5) +#define DCSR_HALT (1<<3) +#define DCSR_STEP (1<<2) +#define DCSR_PRV (3<<0) + +#define DCSR_CAUSE_NONE 0 +#define DCSR_CAUSE_SWBP 1 +#define DCSR_CAUSE_HWBP 2 +#define DCSR_CAUSE_DEBUGINT 3 +#define DCSR_CAUSE_STEP 4 +#define DCSR_CAUSE_HALT 5 + +#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) +#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) +#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11)) + +#define MCONTROL_SELECT (1<<19) +#define MCONTROL_TIMING (1<<18) +#define MCONTROL_ACTION (0x3f<<12) +#define MCONTROL_CHAIN (1<<11) +#define MCONTROL_MATCH (0xf<<7) +#define MCONTROL_M (1<<6) +#define MCONTROL_H (1<<5) +#define MCONTROL_S (1<<4) +#define MCONTROL_U (1<<3) +#define MCONTROL_EXECUTE (1<<2) +#define MCONTROL_STORE (1<<1) +#define MCONTROL_LOAD (1<<0) + +#define MCONTROL_TYPE_NONE 0 +#define MCONTROL_TYPE_MATCH 2 + +#define MCONTROL_ACTION_DEBUG_EXCEPTION 0 +#define MCONTROL_ACTION_DEBUG_MODE 1 +#define MCONTROL_ACTION_TRACE_START 2 +#define MCONTROL_ACTION_TRACE_STOP 3 +#define MCONTROL_ACTION_TRACE_EMIT 4 + +#define MCONTROL_MATCH_EQUAL 0 +#define MCONTROL_MATCH_NAPOT 1 +#define MCONTROL_MATCH_GE 2 +#define MCONTROL_MATCH_LT 3 +#define MCONTROL_MATCH_MASK_LOW 4 +#define MCONTROL_MATCH_MASK_HIGH 5 + +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7FC00000 +#define SATP32_PPN 0x003FFFFF +#define SATP64_MODE 0xF000000000000000 +#define SATP64_ASID 0x0FFFF00000000000 +#define SATP64_PPN 0x00000FFFFFFFFFFF + +#define SATP_MODE_OFF 0 +#define SATP_MODE_SV32 1 +#define SATP_MODE_SV39 8 +#define SATP_MODE_SV48 9 +#define SATP_MODE_SV57 10 +#define SATP_MODE_SV64 11 + +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_L 0x80 +#define PMP_SHIFT 2 + +#define PMP_TOR 0x08 +#define PMP_NA4 0x10 +#define PMP_NAPOT 0x18 + +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_COP 12 +#define IRQ_HOST 13 + +#define DEFAULT_RSTVEC 0x00001000 +#define CLINT_BASE 0x02000000 +#define CLINT_SIZE 0x000c0000 +#define EXT_IO_BASE 0x40000000 +#define DRAM_BASE 0x80000000 + +// page table entry (PTE) fields +#define PTE_V 0x001 // Valid +#define PTE_R 0x002 // Read +#define PTE_W 0x004 // Write +#define PTE_X 0x008 // Execute +#define PTE_U 0x010 // User +#define PTE_G 0x020 // Global +#define PTE_A 0x040 // Accessed +#define PTE_D 0x080 // Dirty +#define PTE_SOFT 0x300 // Reserved for Software + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) + +#ifdef __riscv + +#if __riscv_xlen == 64 +# define MSTATUS_SD MSTATUS64_SD +# define SSTATUS_SD SSTATUS64_SD +# define RISCV_PGLEVEL_BITS 9 +#else +# define MSTATUS_SD MSTATUS32_SD +# define SSTATUS_SD SSTATUS32_SD +# define RISCV_PGLEVEL_BITS 10 +#endif +#define RISCV_PGSHIFT 12 +#define RISCV_PGSIZE (1 << RISCV_PGSHIFT) + +#ifndef __ASSEMBLER__ + +#ifdef __GNUC__ + +#define read_csr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ + asm volatile ("csrw " #reg ", %0" :: "rK"(val)); }) + +#define swap_csr(reg, val) ({ unsigned long __tmp; \ + asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ + __tmp; }) + +#define set_csr(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define clear_csr(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define rdtime() read_csr(time) +#define rdcycle() read_csr(cycle) +#define rdinstret() read_csr(instret) + +#endif + +#define do_div(n, base) \ + ({ \ + int __res; \ + __res = ((unsigned long)n) % (unsigned)base; \ + n = ((unsigned long)n) / (unsigned)base; \ + __res; \ + }) + +#define barrier() __asm__ __volatile__ ("fence" ::: "memory") + +static inline void +lcr3(unsigned long cr3) { + write_csr(satp, 0x8000000000000000 | (cr3 >> RISCV_PGSHIFT)); +} + +#endif + +#endif + +#endif +/* Automatically generated by parse-opcodes. */ +#ifndef RISCV_ENCODING_H +#define RISCV_ENCODING_H +#define MATCH_BEQ 0x63 +#define MASK_BEQ 0x707f +#define MATCH_BNE 0x1063 +#define MASK_BNE 0x707f +#define MATCH_BLT 0x4063 +#define MASK_BLT 0x707f +#define MATCH_BGE 0x5063 +#define MASK_BGE 0x707f +#define MATCH_BLTU 0x6063 +#define MASK_BLTU 0x707f +#define MATCH_BGEU 0x7063 +#define MASK_BGEU 0x707f +#define MATCH_JALR 0x67 +#define MASK_JALR 0x707f +#define MATCH_JAL 0x6f +#define MASK_JAL 0x7f +#define MATCH_LUI 0x37 +#define MASK_LUI 0x7f +#define MATCH_AUIPC 0x17 +#define MASK_AUIPC 0x7f +#define MATCH_ADDI 0x13 +#define MASK_ADDI 0x707f +#define MATCH_SLLI 0x1013 +#define MASK_SLLI 0xfc00707f +#define MATCH_SLTI 0x2013 +#define MASK_SLTI 0x707f +#define MATCH_SLTIU 0x3013 +#define MASK_SLTIU 0x707f +#define MATCH_XORI 0x4013 +#define MASK_XORI 0x707f +#define MATCH_SRLI 0x5013 +#define MASK_SRLI 0xfc00707f +#define MATCH_SRAI 0x40005013 +#define MASK_SRAI 0xfc00707f +#define MATCH_ORI 0x6013 +#define MASK_ORI 0x707f +#define MATCH_ANDI 0x7013 +#define MASK_ANDI 0x707f +#define MATCH_ADD 0x33 +#define MASK_ADD 0xfe00707f +#define MATCH_SUB 0x40000033 +#define MASK_SUB 0xfe00707f +#define MATCH_SLL 0x1033 +#define MASK_SLL 0xfe00707f +#define MATCH_SLT 0x2033 +#define MASK_SLT 0xfe00707f +#define MATCH_SLTU 0x3033 +#define MASK_SLTU 0xfe00707f +#define MATCH_XOR 0x4033 +#define MASK_XOR 0xfe00707f +#define MATCH_SRL 0x5033 +#define MASK_SRL 0xfe00707f +#define MATCH_SRA 0x40005033 +#define MASK_SRA 0xfe00707f +#define MATCH_OR 0x6033 +#define MASK_OR 0xfe00707f +#define MATCH_AND 0x7033 +#define MASK_AND 0xfe00707f +#define MATCH_ADDIW 0x1b +#define MASK_ADDIW 0x707f +#define MATCH_SLLIW 0x101b +#define MASK_SLLIW 0xfe00707f +#define MATCH_SRLIW 0x501b +#define MASK_SRLIW 0xfe00707f +#define MATCH_SRAIW 0x4000501b +#define MASK_SRAIW 0xfe00707f +#define MATCH_ADDW 0x3b +#define MASK_ADDW 0xfe00707f +#define MATCH_SUBW 0x4000003b +#define MASK_SUBW 0xfe00707f +#define MATCH_SLLW 0x103b +#define MASK_SLLW 0xfe00707f +#define MATCH_SRLW 0x503b +#define MASK_SRLW 0xfe00707f +#define MATCH_SRAW 0x4000503b +#define MASK_SRAW 0xfe00707f +#define MATCH_LB 0x3 +#define MASK_LB 0x707f +#define MATCH_LH 0x1003 +#define MASK_LH 0x707f +#define MATCH_LW 0x2003 +#define MASK_LW 0x707f +#define MATCH_LD 0x3003 +#define MASK_LD 0x707f +#define MATCH_LBU 0x4003 +#define MASK_LBU 0x707f +#define MATCH_LHU 0x5003 +#define MASK_LHU 0x707f +#define MATCH_LWU 0x6003 +#define MASK_LWU 0x707f +#define MATCH_SB 0x23 +#define MASK_SB 0x707f +#define MATCH_SH 0x1023 +#define MASK_SH 0x707f +#define MATCH_SW 0x2023 +#define MASK_SW 0x707f +#define MATCH_SD 0x3023 +#define MASK_SD 0x707f +#define MATCH_FENCE 0xf +#define MASK_FENCE 0x707f +#define MATCH_FENCE_I 0x100f +#define MASK_FENCE_I 0x707f +#define MATCH_MUL 0x2000033 +#define MASK_MUL 0xfe00707f +#define MATCH_MULH 0x2001033 +#define MASK_MULH 0xfe00707f +#define MATCH_MULHSU 0x2002033 +#define MASK_MULHSU 0xfe00707f +#define MATCH_MULHU 0x2003033 +#define MASK_MULHU 0xfe00707f +#define MATCH_DIV 0x2004033 +#define MASK_DIV 0xfe00707f +#define MATCH_DIVU 0x2005033 +#define MASK_DIVU 0xfe00707f +#define MATCH_REM 0x2006033 +#define MASK_REM 0xfe00707f +#define MATCH_REMU 0x2007033 +#define MASK_REMU 0xfe00707f +#define MATCH_MULW 0x200003b +#define MASK_MULW 0xfe00707f +#define MATCH_DIVW 0x200403b +#define MASK_DIVW 0xfe00707f +#define MATCH_DIVUW 0x200503b +#define MASK_DIVUW 0xfe00707f +#define MATCH_REMW 0x200603b +#define MASK_REMW 0xfe00707f +#define MATCH_REMUW 0x200703b +#define MASK_REMUW 0xfe00707f +#define MATCH_AMOADD_W 0x202f +#define MASK_AMOADD_W 0xf800707f +#define MATCH_AMOXOR_W 0x2000202f +#define MASK_AMOXOR_W 0xf800707f +#define MATCH_AMOOR_W 0x4000202f +#define MASK_AMOOR_W 0xf800707f +#define MATCH_AMOAND_W 0x6000202f +#define MASK_AMOAND_W 0xf800707f +#define MATCH_AMOMIN_W 0x8000202f +#define MASK_AMOMIN_W 0xf800707f +#define MATCH_AMOMAX_W 0xa000202f +#define MASK_AMOMAX_W 0xf800707f +#define MATCH_AMOMINU_W 0xc000202f +#define MASK_AMOMINU_W 0xf800707f +#define MATCH_AMOMAXU_W 0xe000202f +#define MASK_AMOMAXU_W 0xf800707f +#define MATCH_AMOSWAP_W 0x800202f +#define MASK_AMOSWAP_W 0xf800707f +#define MATCH_LR_W 0x1000202f +#define MASK_LR_W 0xf9f0707f +#define MATCH_SC_W 0x1800202f +#define MASK_SC_W 0xf800707f +#define MATCH_AMOADD_D 0x302f +#define MASK_AMOADD_D 0xf800707f +#define MATCH_AMOXOR_D 0x2000302f +#define MASK_AMOXOR_D 0xf800707f +#define MATCH_AMOOR_D 0x4000302f +#define MASK_AMOOR_D 0xf800707f +#define MATCH_AMOAND_D 0x6000302f +#define MASK_AMOAND_D 0xf800707f +#define MATCH_AMOMIN_D 0x8000302f +#define MASK_AMOMIN_D 0xf800707f +#define MATCH_AMOMAX_D 0xa000302f +#define MASK_AMOMAX_D 0xf800707f +#define MATCH_AMOMINU_D 0xc000302f +#define MASK_AMOMINU_D 0xf800707f +#define MATCH_AMOMAXU_D 0xe000302f +#define MASK_AMOMAXU_D 0xf800707f +#define MATCH_AMOSWAP_D 0x800302f +#define MASK_AMOSWAP_D 0xf800707f +#define MATCH_LR_D 0x1000302f +#define MASK_LR_D 0xf9f0707f +#define MATCH_SC_D 0x1800302f +#define MASK_SC_D 0xf800707f +#define MATCH_ECALL 0x73 +#define MASK_ECALL 0xffffffff +#define MATCH_EBREAK 0x100073 +#define MASK_EBREAK 0xffffffff +#define MATCH_URET 0x200073 +#define MASK_URET 0xffffffff +#define MATCH_SRET 0x10200073 +#define MASK_SRET 0xffffffff +#define MATCH_MRET 0x30200073 +#define MASK_MRET 0xffffffff +#define MATCH_DRET 0x7b200073 +#define MASK_DRET 0xffffffff +#define MATCH_SFENCE_VMA 0x12000073 +#define MASK_SFENCE_VMA 0xfe007fff +#define MATCH_WFI 0x10500073 +#define MASK_WFI 0xffffffff +#define MATCH_CSRRW 0x1073 +#define MASK_CSRRW 0x707f +#define MATCH_CSRRS 0x2073 +#define MASK_CSRRS 0x707f +#define MATCH_CSRRC 0x3073 +#define MASK_CSRRC 0x707f +#define MATCH_CSRRWI 0x5073 +#define MASK_CSRRWI 0x707f +#define MATCH_CSRRSI 0x6073 +#define MASK_CSRRSI 0x707f +#define MATCH_CSRRCI 0x7073 +#define MASK_CSRRCI 0x707f +#define MATCH_FADD_S 0x53 +#define MASK_FADD_S 0xfe00007f +#define MATCH_FSUB_S 0x8000053 +#define MASK_FSUB_S 0xfe00007f +#define MATCH_FMUL_S 0x10000053 +#define MASK_FMUL_S 0xfe00007f +#define MATCH_FDIV_S 0x18000053 +#define MASK_FDIV_S 0xfe00007f +#define MATCH_FSGNJ_S 0x20000053 +#define MASK_FSGNJ_S 0xfe00707f +#define MATCH_FSGNJN_S 0x20001053 +#define MASK_FSGNJN_S 0xfe00707f +#define MATCH_FSGNJX_S 0x20002053 +#define MASK_FSGNJX_S 0xfe00707f +#define MATCH_FMIN_S 0x28000053 +#define MASK_FMIN_S 0xfe00707f +#define MATCH_FMAX_S 0x28001053 +#define MASK_FMAX_S 0xfe00707f +#define MATCH_FSQRT_S 0x58000053 +#define MASK_FSQRT_S 0xfff0007f +#define MATCH_FADD_D 0x2000053 +#define MASK_FADD_D 0xfe00007f +#define MATCH_FSUB_D 0xa000053 +#define MASK_FSUB_D 0xfe00007f +#define MATCH_FMUL_D 0x12000053 +#define MASK_FMUL_D 0xfe00007f +#define MATCH_FDIV_D 0x1a000053 +#define MASK_FDIV_D 0xfe00007f +#define MATCH_FSGNJ_D 0x22000053 +#define MASK_FSGNJ_D 0xfe00707f +#define MATCH_FSGNJN_D 0x22001053 +#define MASK_FSGNJN_D 0xfe00707f +#define MATCH_FSGNJX_D 0x22002053 +#define MASK_FSGNJX_D 0xfe00707f +#define MATCH_FMIN_D 0x2a000053 +#define MASK_FMIN_D 0xfe00707f +#define MATCH_FMAX_D 0x2a001053 +#define MASK_FMAX_D 0xfe00707f +#define MATCH_FCVT_S_D 0x40100053 +#define MASK_FCVT_S_D 0xfff0007f +#define MATCH_FCVT_D_S 0x42000053 +#define MASK_FCVT_D_S 0xfff0007f +#define MATCH_FSQRT_D 0x5a000053 +#define MASK_FSQRT_D 0xfff0007f +#define MATCH_FADD_Q 0x6000053 +#define MASK_FADD_Q 0xfe00007f +#define MATCH_FSUB_Q 0xe000053 +#define MASK_FSUB_Q 0xfe00007f +#define MATCH_FMUL_Q 0x16000053 +#define MASK_FMUL_Q 0xfe00007f +#define MATCH_FDIV_Q 0x1e000053 +#define MASK_FDIV_Q 0xfe00007f +#define MATCH_FSGNJ_Q 0x26000053 +#define MASK_FSGNJ_Q 0xfe00707f +#define MATCH_FSGNJN_Q 0x26001053 +#define MASK_FSGNJN_Q 0xfe00707f +#define MATCH_FSGNJX_Q 0x26002053 +#define MASK_FSGNJX_Q 0xfe00707f +#define MATCH_FMIN_Q 0x2e000053 +#define MASK_FMIN_Q 0xfe00707f +#define MATCH_FMAX_Q 0x2e001053 +#define MASK_FMAX_Q 0xfe00707f +#define MATCH_FCVT_S_Q 0x40300053 +#define MASK_FCVT_S_Q 0xfff0007f +#define MATCH_FCVT_Q_S 0x46000053 +#define MASK_FCVT_Q_S 0xfff0007f +#define MATCH_FCVT_D_Q 0x42300053 +#define MASK_FCVT_D_Q 0xfff0007f +#define MATCH_FCVT_Q_D 0x46100053 +#define MASK_FCVT_Q_D 0xfff0007f +#define MATCH_FSQRT_Q 0x5e000053 +#define MASK_FSQRT_Q 0xfff0007f +#define MATCH_FLE_S 0xa0000053 +#define MASK_FLE_S 0xfe00707f +#define MATCH_FLT_S 0xa0001053 +#define MASK_FLT_S 0xfe00707f +#define MATCH_FEQ_S 0xa0002053 +#define MASK_FEQ_S 0xfe00707f +#define MATCH_FLE_D 0xa2000053 +#define MASK_FLE_D 0xfe00707f +#define MATCH_FLT_D 0xa2001053 +#define MASK_FLT_D 0xfe00707f +#define MATCH_FEQ_D 0xa2002053 +#define MASK_FEQ_D 0xfe00707f +#define MATCH_FLE_Q 0xa6000053 +#define MASK_FLE_Q 0xfe00707f +#define MATCH_FLT_Q 0xa6001053 +#define MASK_FLT_Q 0xfe00707f +#define MATCH_FEQ_Q 0xa6002053 +#define MASK_FEQ_Q 0xfe00707f +#define MATCH_FCVT_W_S 0xc0000053 +#define MASK_FCVT_W_S 0xfff0007f +#define MATCH_FCVT_WU_S 0xc0100053 +#define MASK_FCVT_WU_S 0xfff0007f +#define MATCH_FCVT_L_S 0xc0200053 +#define MASK_FCVT_L_S 0xfff0007f +#define MATCH_FCVT_LU_S 0xc0300053 +#define MASK_FCVT_LU_S 0xfff0007f +#define MATCH_FMV_X_W 0xe0000053 +#define MASK_FMV_X_W 0xfff0707f +#define MATCH_FCLASS_S 0xe0001053 +#define MASK_FCLASS_S 0xfff0707f +#define MATCH_FCVT_W_D 0xc2000053 +#define MASK_FCVT_W_D 0xfff0007f +#define MATCH_FCVT_WU_D 0xc2100053 +#define MASK_FCVT_WU_D 0xfff0007f +#define MATCH_FCVT_L_D 0xc2200053 +#define MASK_FCVT_L_D 0xfff0007f +#define MATCH_FCVT_LU_D 0xc2300053 +#define MASK_FCVT_LU_D 0xfff0007f +#define MATCH_FMV_X_D 0xe2000053 +#define MASK_FMV_X_D 0xfff0707f +#define MATCH_FCLASS_D 0xe2001053 +#define MASK_FCLASS_D 0xfff0707f +#define MATCH_FCVT_W_Q 0xc6000053 +#define MASK_FCVT_W_Q 0xfff0007f +#define MATCH_FCVT_WU_Q 0xc6100053 +#define MASK_FCVT_WU_Q 0xfff0007f +#define MATCH_FCVT_L_Q 0xc6200053 +#define MASK_FCVT_L_Q 0xfff0007f +#define MATCH_FCVT_LU_Q 0xc6300053 +#define MASK_FCVT_LU_Q 0xfff0007f +#define MATCH_FMV_X_Q 0xe6000053 +#define MASK_FMV_X_Q 0xfff0707f +#define MATCH_FCLASS_Q 0xe6001053 +#define MASK_FCLASS_Q 0xfff0707f +#define MATCH_FCVT_S_W 0xd0000053 +#define MASK_FCVT_S_W 0xfff0007f +#define MATCH_FCVT_S_WU 0xd0100053 +#define MASK_FCVT_S_WU 0xfff0007f +#define MATCH_FCVT_S_L 0xd0200053 +#define MASK_FCVT_S_L 0xfff0007f +#define MATCH_FCVT_S_LU 0xd0300053 +#define MASK_FCVT_S_LU 0xfff0007f +#define MATCH_FMV_W_X 0xf0000053 +#define MASK_FMV_W_X 0xfff0707f +#define MATCH_FCVT_D_W 0xd2000053 +#define MASK_FCVT_D_W 0xfff0007f +#define MATCH_FCVT_D_WU 0xd2100053 +#define MASK_FCVT_D_WU 0xfff0007f +#define MATCH_FCVT_D_L 0xd2200053 +#define MASK_FCVT_D_L 0xfff0007f +#define MATCH_FCVT_D_LU 0xd2300053 +#define MASK_FCVT_D_LU 0xfff0007f +#define MATCH_FMV_D_X 0xf2000053 +#define MASK_FMV_D_X 0xfff0707f +#define MATCH_FCVT_Q_W 0xd6000053 +#define MASK_FCVT_Q_W 0xfff0007f +#define MATCH_FCVT_Q_WU 0xd6100053 +#define MASK_FCVT_Q_WU 0xfff0007f +#define MATCH_FCVT_Q_L 0xd6200053 +#define MASK_FCVT_Q_L 0xfff0007f +#define MATCH_FCVT_Q_LU 0xd6300053 +#define MASK_FCVT_Q_LU 0xfff0007f +#define MATCH_FMV_Q_X 0xf6000053 +#define MASK_FMV_Q_X 0xfff0707f +#define MATCH_FLW 0x2007 +#define MASK_FLW 0x707f +#define MATCH_FLD 0x3007 +#define MASK_FLD 0x707f +#define MATCH_FLQ 0x4007 +#define MASK_FLQ 0x707f +#define MATCH_FSW 0x2027 +#define MASK_FSW 0x707f +#define MATCH_FSD 0x3027 +#define MASK_FSD 0x707f +#define MATCH_FSQ 0x4027 +#define MASK_FSQ 0x707f +#define MATCH_FMADD_S 0x43 +#define MASK_FMADD_S 0x600007f +#define MATCH_FMSUB_S 0x47 +#define MASK_FMSUB_S 0x600007f +#define MATCH_FNMSUB_S 0x4b +#define MASK_FNMSUB_S 0x600007f +#define MATCH_FNMADD_S 0x4f +#define MASK_FNMADD_S 0x600007f +#define MATCH_FMADD_D 0x2000043 +#define MASK_FMADD_D 0x600007f +#define MATCH_FMSUB_D 0x2000047 +#define MASK_FMSUB_D 0x600007f +#define MATCH_FNMSUB_D 0x200004b +#define MASK_FNMSUB_D 0x600007f +#define MATCH_FNMADD_D 0x200004f +#define MASK_FNMADD_D 0x600007f +#define MATCH_FMADD_Q 0x6000043 +#define MASK_FMADD_Q 0x600007f +#define MATCH_FMSUB_Q 0x6000047 +#define MASK_FMSUB_Q 0x600007f +#define MATCH_FNMSUB_Q 0x600004b +#define MASK_FNMSUB_Q 0x600007f +#define MATCH_FNMADD_Q 0x600004f +#define MASK_FNMADD_Q 0x600007f +#define MATCH_C_NOP 0x1 +#define MASK_C_NOP 0xffff +#define MATCH_C_ADDI16SP 0x6101 +#define MASK_C_ADDI16SP 0xef83 +#define MATCH_C_JR 0x8002 +#define MASK_C_JR 0xf07f +#define MATCH_C_JALR 0x9002 +#define MASK_C_JALR 0xf07f +#define MATCH_C_EBREAK 0x9002 +#define MASK_C_EBREAK 0xffff +#define MATCH_C_LD 0x6000 +#define MASK_C_LD 0xe003 +#define MATCH_C_SD 0xe000 +#define MASK_C_SD 0xe003 +#define MATCH_C_ADDIW 0x2001 +#define MASK_C_ADDIW 0xe003 +#define MATCH_C_LDSP 0x6002 +#define MASK_C_LDSP 0xe003 +#define MATCH_C_SDSP 0xe002 +#define MASK_C_SDSP 0xe003 +#define MATCH_C_ADDI4SPN 0x0 +#define MASK_C_ADDI4SPN 0xe003 +#define MATCH_C_FLD 0x2000 +#define MASK_C_FLD 0xe003 +#define MATCH_C_LW 0x4000 +#define MASK_C_LW 0xe003 +#define MATCH_C_FLW 0x6000 +#define MASK_C_FLW 0xe003 +#define MATCH_C_FSD 0xa000 +#define MASK_C_FSD 0xe003 +#define MATCH_C_SW 0xc000 +#define MASK_C_SW 0xe003 +#define MATCH_C_FSW 0xe000 +#define MASK_C_FSW 0xe003 +#define MATCH_C_ADDI 0x1 +#define MASK_C_ADDI 0xe003 +#define MATCH_C_JAL 0x2001 +#define MASK_C_JAL 0xe003 +#define MATCH_C_LI 0x4001 +#define MASK_C_LI 0xe003 +#define MATCH_C_LUI 0x6001 +#define MASK_C_LUI 0xe003 +#define MATCH_C_SRLI 0x8001 +#define MASK_C_SRLI 0xec03 +#define MATCH_C_SRAI 0x8401 +#define MASK_C_SRAI 0xec03 +#define MATCH_C_ANDI 0x8801 +#define MASK_C_ANDI 0xec03 +#define MATCH_C_SUB 0x8c01 +#define MASK_C_SUB 0xfc63 +#define MATCH_C_XOR 0x8c21 +#define MASK_C_XOR 0xfc63 +#define MATCH_C_OR 0x8c41 +#define MASK_C_OR 0xfc63 +#define MATCH_C_AND 0x8c61 +#define MASK_C_AND 0xfc63 +#define MATCH_C_SUBW 0x9c01 +#define MASK_C_SUBW 0xfc63 +#define MATCH_C_ADDW 0x9c21 +#define MASK_C_ADDW 0xfc63 +#define MATCH_C_J 0xa001 +#define MASK_C_J 0xe003 +#define MATCH_C_BEQZ 0xc001 +#define MASK_C_BEQZ 0xe003 +#define MATCH_C_BNEZ 0xe001 +#define MASK_C_BNEZ 0xe003 +#define MATCH_C_SLLI 0x2 +#define MASK_C_SLLI 0xe003 +#define MATCH_C_FLDSP 0x2002 +#define MASK_C_FLDSP 0xe003 +#define MATCH_C_LWSP 0x4002 +#define MASK_C_LWSP 0xe003 +#define MATCH_C_FLWSP 0x6002 +#define MASK_C_FLWSP 0xe003 +#define MATCH_C_MV 0x8002 +#define MASK_C_MV 0xf003 +#define MATCH_C_ADD 0x9002 +#define MASK_C_ADD 0xf003 +#define MATCH_C_FSDSP 0xa002 +#define MASK_C_FSDSP 0xe003 +#define MATCH_C_SWSP 0xc002 +#define MASK_C_SWSP 0xe003 +#define MATCH_C_FSWSP 0xe002 +#define MASK_C_FSWSP 0xe003 +#define MATCH_CUSTOM0 0xb +#define MASK_CUSTOM0 0x707f +#define MATCH_CUSTOM0_RS1 0x200b +#define MASK_CUSTOM0_RS1 0x707f +#define MATCH_CUSTOM0_RS1_RS2 0x300b +#define MASK_CUSTOM0_RS1_RS2 0x707f +#define MATCH_CUSTOM0_RD 0x400b +#define MASK_CUSTOM0_RD 0x707f +#define MATCH_CUSTOM0_RD_RS1 0x600b +#define MASK_CUSTOM0_RD_RS1 0x707f +#define MATCH_CUSTOM0_RD_RS1_RS2 0x700b +#define MASK_CUSTOM0_RD_RS1_RS2 0x707f +#define MATCH_CUSTOM1 0x2b +#define MASK_CUSTOM1 0x707f +#define MATCH_CUSTOM1_RS1 0x202b +#define MASK_CUSTOM1_RS1 0x707f +#define MATCH_CUSTOM1_RS1_RS2 0x302b +#define MASK_CUSTOM1_RS1_RS2 0x707f +#define MATCH_CUSTOM1_RD 0x402b +#define MASK_CUSTOM1_RD 0x707f +#define MATCH_CUSTOM1_RD_RS1 0x602b +#define MASK_CUSTOM1_RD_RS1 0x707f +#define MATCH_CUSTOM1_RD_RS1_RS2 0x702b +#define MASK_CUSTOM1_RD_RS1_RS2 0x707f +#define MATCH_CUSTOM2 0x5b +#define MASK_CUSTOM2 0x707f +#define MATCH_CUSTOM2_RS1 0x205b +#define MASK_CUSTOM2_RS1 0x707f +#define MATCH_CUSTOM2_RS1_RS2 0x305b +#define MASK_CUSTOM2_RS1_RS2 0x707f +#define MATCH_CUSTOM2_RD 0x405b +#define MASK_CUSTOM2_RD 0x707f +#define MATCH_CUSTOM2_RD_RS1 0x605b +#define MASK_CUSTOM2_RD_RS1 0x707f +#define MATCH_CUSTOM2_RD_RS1_RS2 0x705b +#define MASK_CUSTOM2_RD_RS1_RS2 0x707f +#define MATCH_CUSTOM3 0x7b +#define MASK_CUSTOM3 0x707f +#define MATCH_CUSTOM3_RS1 0x207b +#define MASK_CUSTOM3_RS1 0x707f +#define MATCH_CUSTOM3_RS1_RS2 0x307b +#define MASK_CUSTOM3_RS1_RS2 0x707f +#define MATCH_CUSTOM3_RD 0x407b +#define MASK_CUSTOM3_RD 0x707f +#define MATCH_CUSTOM3_RD_RS1 0x607b +#define MASK_CUSTOM3_RD_RS1 0x707f +#define MATCH_CUSTOM3_RD_RS1_RS2 0x707b +#define MASK_CUSTOM3_RD_RS1_RS2 0x707f +#define CSR_FFLAGS 0x1 +#define CSR_FRM 0x2 +#define CSR_FCSR 0x3 +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 +#define CSR_SATP 0x180 +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f +#define CAUSE_MISALIGNED_FETCH 0x0 +#define CAUSE_FETCH_ACCESS 0x1 +#define CAUSE_ILLEGAL_INSTRUCTION 0x2 +#define CAUSE_BREAKPOINT 0x3 +#define CAUSE_MISALIGNED_LOAD 0x4 +#define CAUSE_LOAD_ACCESS 0x5 +#define CAUSE_MISALIGNED_STORE 0x6 +#define CAUSE_STORE_ACCESS 0x7 +#define CAUSE_USER_ECALL 0x8 +#define CAUSE_SUPERVISOR_ECALL 0x9 +#define CAUSE_HYPERVISOR_ECALL 0xa +#define CAUSE_MACHINE_ECALL 0xb +#define CAUSE_FETCH_PAGE_FAULT 0xc +#define CAUSE_LOAD_PAGE_FAULT 0xd +#define CAUSE_STORE_PAGE_FAULT 0xf +#endif +#ifdef DECLARE_INSN +DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ) +DECLARE_INSN(bne, MATCH_BNE, MASK_BNE) +DECLARE_INSN(blt, MATCH_BLT, MASK_BLT) +DECLARE_INSN(bge, MATCH_BGE, MASK_BGE) +DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU) +DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU) +DECLARE_INSN(jalr, MATCH_JALR, MASK_JALR) +DECLARE_INSN(jal, MATCH_JAL, MASK_JAL) +DECLARE_INSN(lui, MATCH_LUI, MASK_LUI) +DECLARE_INSN(auipc, MATCH_AUIPC, MASK_AUIPC) +DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI) +DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI) +DECLARE_INSN(slti, MATCH_SLTI, MASK_SLTI) +DECLARE_INSN(sltiu, MATCH_SLTIU, MASK_SLTIU) +DECLARE_INSN(xori, MATCH_XORI, MASK_XORI) +DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI) +DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI) +DECLARE_INSN(ori, MATCH_ORI, MASK_ORI) +DECLARE_INSN(andi, MATCH_ANDI, MASK_ANDI) +DECLARE_INSN(add, MATCH_ADD, MASK_ADD) +DECLARE_INSN(sub, MATCH_SUB, MASK_SUB) +DECLARE_INSN(sll, MATCH_SLL, MASK_SLL) +DECLARE_INSN(slt, MATCH_SLT, MASK_SLT) +DECLARE_INSN(sltu, MATCH_SLTU, MASK_SLTU) +DECLARE_INSN(xor, MATCH_XOR, MASK_XOR) +DECLARE_INSN(srl, MATCH_SRL, MASK_SRL) +DECLARE_INSN(sra, MATCH_SRA, MASK_SRA) +DECLARE_INSN(or, MATCH_OR, MASK_OR) +DECLARE_INSN(and, MATCH_AND, MASK_AND) +DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW) +DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW) +DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW) +DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW) +DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW) +DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW) +DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW) +DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW) +DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW) +DECLARE_INSN(lb, MATCH_LB, MASK_LB) +DECLARE_INSN(lh, MATCH_LH, MASK_LH) +DECLARE_INSN(lw, MATCH_LW, MASK_LW) +DECLARE_INSN(ld, MATCH_LD, MASK_LD) +DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU) +DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU) +DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU) +DECLARE_INSN(sb, MATCH_SB, MASK_SB) +DECLARE_INSN(sh, MATCH_SH, MASK_SH) +DECLARE_INSN(sw, MATCH_SW, MASK_SW) +DECLARE_INSN(sd, MATCH_SD, MASK_SD) +DECLARE_INSN(fence, MATCH_FENCE, MASK_FENCE) +DECLARE_INSN(fence_i, MATCH_FENCE_I, MASK_FENCE_I) +DECLARE_INSN(mul, MATCH_MUL, MASK_MUL) +DECLARE_INSN(mulh, MATCH_MULH, MASK_MULH) +DECLARE_INSN(mulhsu, MATCH_MULHSU, MASK_MULHSU) +DECLARE_INSN(mulhu, MATCH_MULHU, MASK_MULHU) +DECLARE_INSN(div, MATCH_DIV, MASK_DIV) +DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU) +DECLARE_INSN(rem, MATCH_REM, MASK_REM) +DECLARE_INSN(remu, MATCH_REMU, MASK_REMU) +DECLARE_INSN(mulw, MATCH_MULW, MASK_MULW) +DECLARE_INSN(divw, MATCH_DIVW, MASK_DIVW) +DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW) +DECLARE_INSN(remw, MATCH_REMW, MASK_REMW) +DECLARE_INSN(remuw, MATCH_REMUW, MASK_REMUW) +DECLARE_INSN(amoadd_w, MATCH_AMOADD_W, MASK_AMOADD_W) +DECLARE_INSN(amoxor_w, MATCH_AMOXOR_W, MASK_AMOXOR_W) +DECLARE_INSN(amoor_w, MATCH_AMOOR_W, MASK_AMOOR_W) +DECLARE_INSN(amoand_w, MATCH_AMOAND_W, MASK_AMOAND_W) +DECLARE_INSN(amomin_w, MATCH_AMOMIN_W, MASK_AMOMIN_W) +DECLARE_INSN(amomax_w, MATCH_AMOMAX_W, MASK_AMOMAX_W) +DECLARE_INSN(amominu_w, MATCH_AMOMINU_W, MASK_AMOMINU_W) +DECLARE_INSN(amomaxu_w, MATCH_AMOMAXU_W, MASK_AMOMAXU_W) +DECLARE_INSN(amoswap_w, MATCH_AMOSWAP_W, MASK_AMOSWAP_W) +DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W) +DECLARE_INSN(sc_w, MATCH_SC_W, MASK_SC_W) +DECLARE_INSN(amoadd_d, MATCH_AMOADD_D, MASK_AMOADD_D) +DECLARE_INSN(amoxor_d, MATCH_AMOXOR_D, MASK_AMOXOR_D) +DECLARE_INSN(amoor_d, MATCH_AMOOR_D, MASK_AMOOR_D) +DECLARE_INSN(amoand_d, MATCH_AMOAND_D, MASK_AMOAND_D) +DECLARE_INSN(amomin_d, MATCH_AMOMIN_D, MASK_AMOMIN_D) +DECLARE_INSN(amomax_d, MATCH_AMOMAX_D, MASK_AMOMAX_D) +DECLARE_INSN(amominu_d, MATCH_AMOMINU_D, MASK_AMOMINU_D) +DECLARE_INSN(amomaxu_d, MATCH_AMOMAXU_D, MASK_AMOMAXU_D) +DECLARE_INSN(amoswap_d, MATCH_AMOSWAP_D, MASK_AMOSWAP_D) +DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D) +DECLARE_INSN(sc_d, MATCH_SC_D, MASK_SC_D) +DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL) +DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK) +DECLARE_INSN(uret, MATCH_URET, MASK_URET) +DECLARE_INSN(sret, MATCH_SRET, MASK_SRET) +DECLARE_INSN(mret, MATCH_MRET, MASK_MRET) +DECLARE_INSN(dret, MATCH_DRET, MASK_DRET) +DECLARE_INSN(sfence_vma, MATCH_SFENCE_VMA, MASK_SFENCE_VMA) +DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI) +DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW) +DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS) +DECLARE_INSN(csrrc, MATCH_CSRRC, MASK_CSRRC) +DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI) +DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI) +DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI) +DECLARE_INSN(fadd_s, MATCH_FADD_S, MASK_FADD_S) +DECLARE_INSN(fsub_s, MATCH_FSUB_S, MASK_FSUB_S) +DECLARE_INSN(fmul_s, MATCH_FMUL_S, MASK_FMUL_S) +DECLARE_INSN(fdiv_s, MATCH_FDIV_S, MASK_FDIV_S) +DECLARE_INSN(fsgnj_s, MATCH_FSGNJ_S, MASK_FSGNJ_S) +DECLARE_INSN(fsgnjn_s, MATCH_FSGNJN_S, MASK_FSGNJN_S) +DECLARE_INSN(fsgnjx_s, MATCH_FSGNJX_S, MASK_FSGNJX_S) +DECLARE_INSN(fmin_s, MATCH_FMIN_S, MASK_FMIN_S) +DECLARE_INSN(fmax_s, MATCH_FMAX_S, MASK_FMAX_S) +DECLARE_INSN(fsqrt_s, MATCH_FSQRT_S, MASK_FSQRT_S) +DECLARE_INSN(fadd_d, MATCH_FADD_D, MASK_FADD_D) +DECLARE_INSN(fsub_d, MATCH_FSUB_D, MASK_FSUB_D) +DECLARE_INSN(fmul_d, MATCH_FMUL_D, MASK_FMUL_D) +DECLARE_INSN(fdiv_d, MATCH_FDIV_D, MASK_FDIV_D) +DECLARE_INSN(fsgnj_d, MATCH_FSGNJ_D, MASK_FSGNJ_D) +DECLARE_INSN(fsgnjn_d, MATCH_FSGNJN_D, MASK_FSGNJN_D) +DECLARE_INSN(fsgnjx_d, MATCH_FSGNJX_D, MASK_FSGNJX_D) +DECLARE_INSN(fmin_d, MATCH_FMIN_D, MASK_FMIN_D) +DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D) +DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D) +DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S) +DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D) +DECLARE_INSN(fadd_q, MATCH_FADD_Q, MASK_FADD_Q) +DECLARE_INSN(fsub_q, MATCH_FSUB_Q, MASK_FSUB_Q) +DECLARE_INSN(fmul_q, MATCH_FMUL_Q, MASK_FMUL_Q) +DECLARE_INSN(fdiv_q, MATCH_FDIV_Q, MASK_FDIV_Q) +DECLARE_INSN(fsgnj_q, MATCH_FSGNJ_Q, MASK_FSGNJ_Q) +DECLARE_INSN(fsgnjn_q, MATCH_FSGNJN_Q, MASK_FSGNJN_Q) +DECLARE_INSN(fsgnjx_q, MATCH_FSGNJX_Q, MASK_FSGNJX_Q) +DECLARE_INSN(fmin_q, MATCH_FMIN_Q, MASK_FMIN_Q) +DECLARE_INSN(fmax_q, MATCH_FMAX_Q, MASK_FMAX_Q) +DECLARE_INSN(fcvt_s_q, MATCH_FCVT_S_Q, MASK_FCVT_S_Q) +DECLARE_INSN(fcvt_q_s, MATCH_FCVT_Q_S, MASK_FCVT_Q_S) +DECLARE_INSN(fcvt_d_q, MATCH_FCVT_D_Q, MASK_FCVT_D_Q) +DECLARE_INSN(fcvt_q_d, MATCH_FCVT_Q_D, MASK_FCVT_Q_D) +DECLARE_INSN(fsqrt_q, MATCH_FSQRT_Q, MASK_FSQRT_Q) +DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S) +DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S) +DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S) +DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D) +DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D) +DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D) +DECLARE_INSN(fle_q, MATCH_FLE_Q, MASK_FLE_Q) +DECLARE_INSN(flt_q, MATCH_FLT_Q, MASK_FLT_Q) +DECLARE_INSN(feq_q, MATCH_FEQ_Q, MASK_FEQ_Q) +DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S) +DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S) +DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S) +DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S) +DECLARE_INSN(fmv_x_w, MATCH_FMV_X_W, MASK_FMV_X_W) +DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S) +DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D) +DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D) +DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D) +DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D) +DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D) +DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D) +DECLARE_INSN(fcvt_w_q, MATCH_FCVT_W_Q, MASK_FCVT_W_Q) +DECLARE_INSN(fcvt_wu_q, MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q) +DECLARE_INSN(fcvt_l_q, MATCH_FCVT_L_Q, MASK_FCVT_L_Q) +DECLARE_INSN(fcvt_lu_q, MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q) +DECLARE_INSN(fmv_x_q, MATCH_FMV_X_Q, MASK_FMV_X_Q) +DECLARE_INSN(fclass_q, MATCH_FCLASS_Q, MASK_FCLASS_Q) +DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W) +DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU) +DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L) +DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU) +DECLARE_INSN(fmv_w_x, MATCH_FMV_W_X, MASK_FMV_W_X) +DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W) +DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU) +DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L) +DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU) +DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X) +DECLARE_INSN(fcvt_q_w, MATCH_FCVT_Q_W, MASK_FCVT_Q_W) +DECLARE_INSN(fcvt_q_wu, MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU) +DECLARE_INSN(fcvt_q_l, MATCH_FCVT_Q_L, MASK_FCVT_Q_L) +DECLARE_INSN(fcvt_q_lu, MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU) +DECLARE_INSN(fmv_q_x, MATCH_FMV_Q_X, MASK_FMV_Q_X) +DECLARE_INSN(flw, MATCH_FLW, MASK_FLW) +DECLARE_INSN(fld, MATCH_FLD, MASK_FLD) +DECLARE_INSN(flq, MATCH_FLQ, MASK_FLQ) +DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW) +DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD) +DECLARE_INSN(fsq, MATCH_FSQ, MASK_FSQ) +DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S) +DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S) +DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S) +DECLARE_INSN(fnmadd_s, MATCH_FNMADD_S, MASK_FNMADD_S) +DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D) +DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D) +DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D) +DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D) +DECLARE_INSN(fmadd_q, MATCH_FMADD_Q, MASK_FMADD_Q) +DECLARE_INSN(fmsub_q, MATCH_FMSUB_Q, MASK_FMSUB_Q) +DECLARE_INSN(fnmsub_q, MATCH_FNMSUB_Q, MASK_FNMSUB_Q) +DECLARE_INSN(fnmadd_q, MATCH_FNMADD_Q, MASK_FNMADD_Q) +DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP) +DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP) +DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR) +DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR) +DECLARE_INSN(c_ebreak, MATCH_C_EBREAK, MASK_C_EBREAK) +DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD) +DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD) +DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW) +DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP) +DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP) +DECLARE_INSN(c_addi4spn, MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN) +DECLARE_INSN(c_fld, MATCH_C_FLD, MASK_C_FLD) +DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW) +DECLARE_INSN(c_flw, MATCH_C_FLW, MASK_C_FLW) +DECLARE_INSN(c_fsd, MATCH_C_FSD, MASK_C_FSD) +DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW) +DECLARE_INSN(c_fsw, MATCH_C_FSW, MASK_C_FSW) +DECLARE_INSN(c_addi, MATCH_C_ADDI, MASK_C_ADDI) +DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL) +DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI) +DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI) +DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI) +DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI) +DECLARE_INSN(c_andi, MATCH_C_ANDI, MASK_C_ANDI) +DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB) +DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR) +DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR) +DECLARE_INSN(c_and, MATCH_C_AND, MASK_C_AND) +DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW) +DECLARE_INSN(c_addw, MATCH_C_ADDW, MASK_C_ADDW) +DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J) +DECLARE_INSN(c_beqz, MATCH_C_BEQZ, MASK_C_BEQZ) +DECLARE_INSN(c_bnez, MATCH_C_BNEZ, MASK_C_BNEZ) +DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI) +DECLARE_INSN(c_fldsp, MATCH_C_FLDSP, MASK_C_FLDSP) +DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP) +DECLARE_INSN(c_flwsp, MATCH_C_FLWSP, MASK_C_FLWSP) +DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV) +DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD) +DECLARE_INSN(c_fsdsp, MATCH_C_FSDSP, MASK_C_FSDSP) +DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP) +DECLARE_INSN(c_fswsp, MATCH_C_FSWSP, MASK_C_FSWSP) +DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0) +DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1) +DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2) +DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD) +DECLARE_INSN(custom0_rd_rs1, MATCH_CUSTOM0_RD_RS1, MASK_CUSTOM0_RD_RS1) +DECLARE_INSN(custom0_rd_rs1_rs2, MATCH_CUSTOM0_RD_RS1_RS2, MASK_CUSTOM0_RD_RS1_RS2) +DECLARE_INSN(custom1, MATCH_CUSTOM1, MASK_CUSTOM1) +DECLARE_INSN(custom1_rs1, MATCH_CUSTOM1_RS1, MASK_CUSTOM1_RS1) +DECLARE_INSN(custom1_rs1_rs2, MATCH_CUSTOM1_RS1_RS2, MASK_CUSTOM1_RS1_RS2) +DECLARE_INSN(custom1_rd, MATCH_CUSTOM1_RD, MASK_CUSTOM1_RD) +DECLARE_INSN(custom1_rd_rs1, MATCH_CUSTOM1_RD_RS1, MASK_CUSTOM1_RD_RS1) +DECLARE_INSN(custom1_rd_rs1_rs2, MATCH_CUSTOM1_RD_RS1_RS2, MASK_CUSTOM1_RD_RS1_RS2) +DECLARE_INSN(custom2, MATCH_CUSTOM2, MASK_CUSTOM2) +DECLARE_INSN(custom2_rs1, MATCH_CUSTOM2_RS1, MASK_CUSTOM2_RS1) +DECLARE_INSN(custom2_rs1_rs2, MATCH_CUSTOM2_RS1_RS2, MASK_CUSTOM2_RS1_RS2) +DECLARE_INSN(custom2_rd, MATCH_CUSTOM2_RD, MASK_CUSTOM2_RD) +DECLARE_INSN(custom2_rd_rs1, MATCH_CUSTOM2_RD_RS1, MASK_CUSTOM2_RD_RS1) +DECLARE_INSN(custom2_rd_rs1_rs2, MATCH_CUSTOM2_RD_RS1_RS2, MASK_CUSTOM2_RD_RS1_RS2) +DECLARE_INSN(custom3, MATCH_CUSTOM3, MASK_CUSTOM3) +DECLARE_INSN(custom3_rs1, MATCH_CUSTOM3_RS1, MASK_CUSTOM3_RS1) +DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2) +DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD) +DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1) +DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2) +#endif +#ifdef DECLARE_CSR +DECLARE_CSR(fflags, CSR_FFLAGS) +DECLARE_CSR(frm, CSR_FRM) +DECLARE_CSR(fcsr, CSR_FCSR) +DECLARE_CSR(cycle, CSR_CYCLE) +DECLARE_CSR(time, CSR_TIME) +DECLARE_CSR(instret, CSR_INSTRET) +DECLARE_CSR(hpmcounter3, CSR_HPMCOUNTER3) +DECLARE_CSR(hpmcounter4, CSR_HPMCOUNTER4) +DECLARE_CSR(hpmcounter5, CSR_HPMCOUNTER5) +DECLARE_CSR(hpmcounter6, CSR_HPMCOUNTER6) +DECLARE_CSR(hpmcounter7, CSR_HPMCOUNTER7) +DECLARE_CSR(hpmcounter8, CSR_HPMCOUNTER8) +DECLARE_CSR(hpmcounter9, CSR_HPMCOUNTER9) +DECLARE_CSR(hpmcounter10, CSR_HPMCOUNTER10) +DECLARE_CSR(hpmcounter11, CSR_HPMCOUNTER11) +DECLARE_CSR(hpmcounter12, CSR_HPMCOUNTER12) +DECLARE_CSR(hpmcounter13, CSR_HPMCOUNTER13) +DECLARE_CSR(hpmcounter14, CSR_HPMCOUNTER14) +DECLARE_CSR(hpmcounter15, CSR_HPMCOUNTER15) +DECLARE_CSR(hpmcounter16, CSR_HPMCOUNTER16) +DECLARE_CSR(hpmcounter17, CSR_HPMCOUNTER17) +DECLARE_CSR(hpmcounter18, CSR_HPMCOUNTER18) +DECLARE_CSR(hpmcounter19, CSR_HPMCOUNTER19) +DECLARE_CSR(hpmcounter20, CSR_HPMCOUNTER20) +DECLARE_CSR(hpmcounter21, CSR_HPMCOUNTER21) +DECLARE_CSR(hpmcounter22, CSR_HPMCOUNTER22) +DECLARE_CSR(hpmcounter23, CSR_HPMCOUNTER23) +DECLARE_CSR(hpmcounter24, CSR_HPMCOUNTER24) +DECLARE_CSR(hpmcounter25, CSR_HPMCOUNTER25) +DECLARE_CSR(hpmcounter26, CSR_HPMCOUNTER26) +DECLARE_CSR(hpmcounter27, CSR_HPMCOUNTER27) +DECLARE_CSR(hpmcounter28, CSR_HPMCOUNTER28) +DECLARE_CSR(hpmcounter29, CSR_HPMCOUNTER29) +DECLARE_CSR(hpmcounter30, CSR_HPMCOUNTER30) +DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31) +DECLARE_CSR(sstatus, CSR_SSTATUS) +DECLARE_CSR(sie, CSR_SIE) +DECLARE_CSR(stvec, CSR_STVEC) +DECLARE_CSR(scounteren, CSR_SCOUNTEREN) +DECLARE_CSR(sscratch, CSR_SSCRATCH) +DECLARE_CSR(sepc, CSR_SEPC) +DECLARE_CSR(scause, CSR_SCAUSE) +DECLARE_CSR(stval, CSR_STVAL) +DECLARE_CSR(sip, CSR_SIP) +DECLARE_CSR(satp, CSR_SATP) +DECLARE_CSR(mstatus, CSR_MSTATUS) +DECLARE_CSR(misa, CSR_MISA) +DECLARE_CSR(medeleg, CSR_MEDELEG) +DECLARE_CSR(mideleg, CSR_MIDELEG) +DECLARE_CSR(mie, CSR_MIE) +DECLARE_CSR(mtvec, CSR_MTVEC) +DECLARE_CSR(mcounteren, CSR_MCOUNTEREN) +DECLARE_CSR(mscratch, CSR_MSCRATCH) +DECLARE_CSR(mepc, CSR_MEPC) +DECLARE_CSR(mcause, CSR_MCAUSE) +DECLARE_CSR(mtval, CSR_MTVAL) +DECLARE_CSR(mip, CSR_MIP) +DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) +DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) +DECLARE_CSR(pmpcfg2, CSR_PMPCFG2) +DECLARE_CSR(pmpcfg3, CSR_PMPCFG3) +DECLARE_CSR(pmpaddr0, CSR_PMPADDR0) +DECLARE_CSR(pmpaddr1, CSR_PMPADDR1) +DECLARE_CSR(pmpaddr2, CSR_PMPADDR2) +DECLARE_CSR(pmpaddr3, CSR_PMPADDR3) +DECLARE_CSR(pmpaddr4, CSR_PMPADDR4) +DECLARE_CSR(pmpaddr5, CSR_PMPADDR5) +DECLARE_CSR(pmpaddr6, CSR_PMPADDR6) +DECLARE_CSR(pmpaddr7, CSR_PMPADDR7) +DECLARE_CSR(pmpaddr8, CSR_PMPADDR8) +DECLARE_CSR(pmpaddr9, CSR_PMPADDR9) +DECLARE_CSR(pmpaddr10, CSR_PMPADDR10) +DECLARE_CSR(pmpaddr11, CSR_PMPADDR11) +DECLARE_CSR(pmpaddr12, CSR_PMPADDR12) +DECLARE_CSR(pmpaddr13, CSR_PMPADDR13) +DECLARE_CSR(pmpaddr14, CSR_PMPADDR14) +DECLARE_CSR(pmpaddr15, CSR_PMPADDR15) +DECLARE_CSR(tselect, CSR_TSELECT) +DECLARE_CSR(tdata1, CSR_TDATA1) +DECLARE_CSR(tdata2, CSR_TDATA2) +DECLARE_CSR(tdata3, CSR_TDATA3) +DECLARE_CSR(dcsr, CSR_DCSR) +DECLARE_CSR(dpc, CSR_DPC) +DECLARE_CSR(dscratch, CSR_DSCRATCH) +DECLARE_CSR(mcycle, CSR_MCYCLE) +DECLARE_CSR(minstret, CSR_MINSTRET) +DECLARE_CSR(mhpmcounter3, CSR_MHPMCOUNTER3) +DECLARE_CSR(mhpmcounter4, CSR_MHPMCOUNTER4) +DECLARE_CSR(mhpmcounter5, CSR_MHPMCOUNTER5) +DECLARE_CSR(mhpmcounter6, CSR_MHPMCOUNTER6) +DECLARE_CSR(mhpmcounter7, CSR_MHPMCOUNTER7) +DECLARE_CSR(mhpmcounter8, CSR_MHPMCOUNTER8) +DECLARE_CSR(mhpmcounter9, CSR_MHPMCOUNTER9) +DECLARE_CSR(mhpmcounter10, CSR_MHPMCOUNTER10) +DECLARE_CSR(mhpmcounter11, CSR_MHPMCOUNTER11) +DECLARE_CSR(mhpmcounter12, CSR_MHPMCOUNTER12) +DECLARE_CSR(mhpmcounter13, CSR_MHPMCOUNTER13) +DECLARE_CSR(mhpmcounter14, CSR_MHPMCOUNTER14) +DECLARE_CSR(mhpmcounter15, CSR_MHPMCOUNTER15) +DECLARE_CSR(mhpmcounter16, CSR_MHPMCOUNTER16) +DECLARE_CSR(mhpmcounter17, CSR_MHPMCOUNTER17) +DECLARE_CSR(mhpmcounter18, CSR_MHPMCOUNTER18) +DECLARE_CSR(mhpmcounter19, CSR_MHPMCOUNTER19) +DECLARE_CSR(mhpmcounter20, CSR_MHPMCOUNTER20) +DECLARE_CSR(mhpmcounter21, CSR_MHPMCOUNTER21) +DECLARE_CSR(mhpmcounter22, CSR_MHPMCOUNTER22) +DECLARE_CSR(mhpmcounter23, CSR_MHPMCOUNTER23) +DECLARE_CSR(mhpmcounter24, CSR_MHPMCOUNTER24) +DECLARE_CSR(mhpmcounter25, CSR_MHPMCOUNTER25) +DECLARE_CSR(mhpmcounter26, CSR_MHPMCOUNTER26) +DECLARE_CSR(mhpmcounter27, CSR_MHPMCOUNTER27) +DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28) +DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29) +DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30) +DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31) +DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3) +DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4) +DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5) +DECLARE_CSR(mhpmevent6, CSR_MHPMEVENT6) +DECLARE_CSR(mhpmevent7, CSR_MHPMEVENT7) +DECLARE_CSR(mhpmevent8, CSR_MHPMEVENT8) +DECLARE_CSR(mhpmevent9, CSR_MHPMEVENT9) +DECLARE_CSR(mhpmevent10, CSR_MHPMEVENT10) +DECLARE_CSR(mhpmevent11, CSR_MHPMEVENT11) +DECLARE_CSR(mhpmevent12, CSR_MHPMEVENT12) +DECLARE_CSR(mhpmevent13, CSR_MHPMEVENT13) +DECLARE_CSR(mhpmevent14, CSR_MHPMEVENT14) +DECLARE_CSR(mhpmevent15, CSR_MHPMEVENT15) +DECLARE_CSR(mhpmevent16, CSR_MHPMEVENT16) +DECLARE_CSR(mhpmevent17, CSR_MHPMEVENT17) +DECLARE_CSR(mhpmevent18, CSR_MHPMEVENT18) +DECLARE_CSR(mhpmevent19, CSR_MHPMEVENT19) +DECLARE_CSR(mhpmevent20, CSR_MHPMEVENT20) +DECLARE_CSR(mhpmevent21, CSR_MHPMEVENT21) +DECLARE_CSR(mhpmevent22, CSR_MHPMEVENT22) +DECLARE_CSR(mhpmevent23, CSR_MHPMEVENT23) +DECLARE_CSR(mhpmevent24, CSR_MHPMEVENT24) +DECLARE_CSR(mhpmevent25, CSR_MHPMEVENT25) +DECLARE_CSR(mhpmevent26, CSR_MHPMEVENT26) +DECLARE_CSR(mhpmevent27, CSR_MHPMEVENT27) +DECLARE_CSR(mhpmevent28, CSR_MHPMEVENT28) +DECLARE_CSR(mhpmevent29, CSR_MHPMEVENT29) +DECLARE_CSR(mhpmevent30, CSR_MHPMEVENT30) +DECLARE_CSR(mhpmevent31, CSR_MHPMEVENT31) +DECLARE_CSR(mvendorid, CSR_MVENDORID) +DECLARE_CSR(marchid, CSR_MARCHID) +DECLARE_CSR(mimpid, CSR_MIMPID) +DECLARE_CSR(mhartid, CSR_MHARTID) +DECLARE_CSR(cycleh, CSR_CYCLEH) +DECLARE_CSR(timeh, CSR_TIMEH) +DECLARE_CSR(instreth, CSR_INSTRETH) +DECLARE_CSR(hpmcounter3h, CSR_HPMCOUNTER3H) +DECLARE_CSR(hpmcounter4h, CSR_HPMCOUNTER4H) +DECLARE_CSR(hpmcounter5h, CSR_HPMCOUNTER5H) +DECLARE_CSR(hpmcounter6h, CSR_HPMCOUNTER6H) +DECLARE_CSR(hpmcounter7h, CSR_HPMCOUNTER7H) +DECLARE_CSR(hpmcounter8h, CSR_HPMCOUNTER8H) +DECLARE_CSR(hpmcounter9h, CSR_HPMCOUNTER9H) +DECLARE_CSR(hpmcounter10h, CSR_HPMCOUNTER10H) +DECLARE_CSR(hpmcounter11h, CSR_HPMCOUNTER11H) +DECLARE_CSR(hpmcounter12h, CSR_HPMCOUNTER12H) +DECLARE_CSR(hpmcounter13h, CSR_HPMCOUNTER13H) +DECLARE_CSR(hpmcounter14h, CSR_HPMCOUNTER14H) +DECLARE_CSR(hpmcounter15h, CSR_HPMCOUNTER15H) +DECLARE_CSR(hpmcounter16h, CSR_HPMCOUNTER16H) +DECLARE_CSR(hpmcounter17h, CSR_HPMCOUNTER17H) +DECLARE_CSR(hpmcounter18h, CSR_HPMCOUNTER18H) +DECLARE_CSR(hpmcounter19h, CSR_HPMCOUNTER19H) +DECLARE_CSR(hpmcounter20h, CSR_HPMCOUNTER20H) +DECLARE_CSR(hpmcounter21h, CSR_HPMCOUNTER21H) +DECLARE_CSR(hpmcounter22h, CSR_HPMCOUNTER22H) +DECLARE_CSR(hpmcounter23h, CSR_HPMCOUNTER23H) +DECLARE_CSR(hpmcounter24h, CSR_HPMCOUNTER24H) +DECLARE_CSR(hpmcounter25h, CSR_HPMCOUNTER25H) +DECLARE_CSR(hpmcounter26h, CSR_HPMCOUNTER26H) +DECLARE_CSR(hpmcounter27h, CSR_HPMCOUNTER27H) +DECLARE_CSR(hpmcounter28h, CSR_HPMCOUNTER28H) +DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H) +DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H) +DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H) +DECLARE_CSR(mcycleh, CSR_MCYCLEH) +DECLARE_CSR(minstreth, CSR_MINSTRETH) +DECLARE_CSR(mhpmcounter3h, CSR_MHPMCOUNTER3H) +DECLARE_CSR(mhpmcounter4h, CSR_MHPMCOUNTER4H) +DECLARE_CSR(mhpmcounter5h, CSR_MHPMCOUNTER5H) +DECLARE_CSR(mhpmcounter6h, CSR_MHPMCOUNTER6H) +DECLARE_CSR(mhpmcounter7h, CSR_MHPMCOUNTER7H) +DECLARE_CSR(mhpmcounter8h, CSR_MHPMCOUNTER8H) +DECLARE_CSR(mhpmcounter9h, CSR_MHPMCOUNTER9H) +DECLARE_CSR(mhpmcounter10h, CSR_MHPMCOUNTER10H) +DECLARE_CSR(mhpmcounter11h, CSR_MHPMCOUNTER11H) +DECLARE_CSR(mhpmcounter12h, CSR_MHPMCOUNTER12H) +DECLARE_CSR(mhpmcounter13h, CSR_MHPMCOUNTER13H) +DECLARE_CSR(mhpmcounter14h, CSR_MHPMCOUNTER14H) +DECLARE_CSR(mhpmcounter15h, CSR_MHPMCOUNTER15H) +DECLARE_CSR(mhpmcounter16h, CSR_MHPMCOUNTER16H) +DECLARE_CSR(mhpmcounter17h, CSR_MHPMCOUNTER17H) +DECLARE_CSR(mhpmcounter18h, CSR_MHPMCOUNTER18H) +DECLARE_CSR(mhpmcounter19h, CSR_MHPMCOUNTER19H) +DECLARE_CSR(mhpmcounter20h, CSR_MHPMCOUNTER20H) +DECLARE_CSR(mhpmcounter21h, CSR_MHPMCOUNTER21H) +DECLARE_CSR(mhpmcounter22h, CSR_MHPMCOUNTER22H) +DECLARE_CSR(mhpmcounter23h, CSR_MHPMCOUNTER23H) +DECLARE_CSR(mhpmcounter24h, CSR_MHPMCOUNTER24H) +DECLARE_CSR(mhpmcounter25h, CSR_MHPMCOUNTER25H) +DECLARE_CSR(mhpmcounter26h, CSR_MHPMCOUNTER26H) +DECLARE_CSR(mhpmcounter27h, CSR_MHPMCOUNTER27H) +DECLARE_CSR(mhpmcounter28h, CSR_MHPMCOUNTER28H) +DECLARE_CSR(mhpmcounter29h, CSR_MHPMCOUNTER29H) +DECLARE_CSR(mhpmcounter30h, CSR_MHPMCOUNTER30H) +DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H) +#endif +#ifdef DECLARE_CAUSE +DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH) +DECLARE_CAUSE("fetch access", CAUSE_FETCH_ACCESS) +DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION) +DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT) +DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD) +DECLARE_CAUSE("load access", CAUSE_LOAD_ACCESS) +DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE) +DECLARE_CAUSE("store access", CAUSE_STORE_ACCESS) +DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL) +DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL) +DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL) +DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL) +DECLARE_CAUSE("fetch page fault", CAUSE_FETCH_PAGE_FAULT) +DECLARE_CAUSE("load page fault", CAUSE_LOAD_PAGE_FAULT) +DECLARE_CAUSE("store page fault", CAUSE_STORE_PAGE_FAULT) +#endif diff --git a/lab8_practice/libs/sbi.h b/lab8_practice/libs/sbi.h new file mode 100644 index 0000000..516caf1 --- /dev/null +++ b/lab8_practice/libs/sbi.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 Regents of the University of California + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SBI_H__ +#define __SBI_H__ + +#include + +#define SBI_SET_TIMER 0 +#define SBI_CONSOLE_PUTCHAR 1 +#define SBI_CONSOLE_GETCHAR 2 +#define SBI_CLEAR_IPI 3 +#define SBI_SEND_IPI 4 +#define SBI_REMOTE_FENCE_I 5 +#define SBI_REMOTE_SFENCE_VMA 6 +#define SBI_REMOTE_SFENCE_VMA_ASID 7 +#define SBI_SHUTDOWN 8 + +#define SBI_CALL(which, arg0, arg1, arg2) ({ \ + register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \ + register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \ + register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \ + register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \ + asm volatile ("ecall" \ + : "+r" (a0) \ + : "r" (a1), "r" (a2), "r" (a7) \ + : "memory"); \ + a0; \ +}) + +/* Lazy implementations until SBI is finalized */ +#define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0) +#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0) +#define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0) + +static inline void sbi_console_putchar(int ch) +{ + SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch); +} + +static inline int sbi_console_getchar(void) +{ + return SBI_CALL_0(SBI_CONSOLE_GETCHAR); +} + +static inline void sbi_set_timer(uint64_t stime_value) +{ +#if __riscv_xlen == 32 + SBI_CALL_2(SBI_SET_TIMER, stime_value, stime_value >> 32); +#else + SBI_CALL_1(SBI_SET_TIMER, stime_value); +#endif +} + +static inline void sbi_shutdown(void) +{ + SBI_CALL_0(SBI_SHUTDOWN); +} + +static inline void sbi_clear_ipi(void) +{ + SBI_CALL_0(SBI_CLEAR_IPI); +} + +static inline void sbi_send_ipi(const unsigned long *hart_mask) +{ + SBI_CALL_1(SBI_SEND_IPI, hart_mask); +} + +static inline void sbi_remote_fence_i(const unsigned long *hart_mask) +{ + SBI_CALL_1(SBI_REMOTE_FENCE_I, hart_mask); +} + +static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask, + unsigned long start, + unsigned long size) +{ + SBI_CALL_1(SBI_REMOTE_SFENCE_VMA, hart_mask); +} + +static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, + unsigned long start, + unsigned long size, + unsigned long asid) +{ + SBI_CALL_1(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask); +} + +#endif /* !__SBI_H__ */ diff --git a/lab8_practice/libs/skew_heap.h b/lab8_practice/libs/skew_heap.h new file mode 100644 index 0000000..0c216b8 --- /dev/null +++ b/lab8_practice/libs/skew_heap.h @@ -0,0 +1,87 @@ +#ifndef __LIBS_SKEW_HEAP_H__ +#define __LIBS_SKEW_HEAP_H__ + +struct skew_heap_entry { + struct skew_heap_entry *parent, *left, *right; +}; + +typedef struct skew_heap_entry skew_heap_entry_t; + +typedef int(*compare_f)(void *a, void *b); + +static inline void skew_heap_init(skew_heap_entry_t *a) __attribute__((always_inline)); +static inline skew_heap_entry_t *skew_heap_merge( + skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp); +static inline skew_heap_entry_t *skew_heap_insert( + skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) __attribute__((always_inline)); +static inline skew_heap_entry_t *skew_heap_remove( + skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) __attribute__((always_inline)); + +static inline void +skew_heap_init(skew_heap_entry_t *a) +{ + a->left = a->right = a->parent = NULL; +} + +static inline skew_heap_entry_t * +skew_heap_merge(skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) +{ + if (a == NULL) return b; + else if (b == NULL) return a; + + skew_heap_entry_t *l, *r; + if (comp(a, b) == -1) + { + r = a->left; + l = skew_heap_merge(a->right, b, comp); + + a->left = l; + a->right = r; + if (l) l->parent = a; + + return a; + } + else + { + r = b->left; + l = skew_heap_merge(a, b->right, comp); + + b->left = l; + b->right = r; + if (l) l->parent = b; + + return b; + } +} + +static inline skew_heap_entry_t * +skew_heap_insert(skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) +{ + skew_heap_init(b); + return skew_heap_merge(a, b, comp); +} + +static inline skew_heap_entry_t * +skew_heap_remove(skew_heap_entry_t *a, skew_heap_entry_t *b, + compare_f comp) +{ + skew_heap_entry_t *p = b->parent; + skew_heap_entry_t *rep = skew_heap_merge(b->left, b->right, comp); + if (rep) rep->parent = p; + + if (p) + { + if (p->left == b) + p->left = rep; + else p->right = rep; + return a; + } + else return rep; +} + +#endif /* !__LIBS_SKEW_HEAP_H__ */ diff --git a/lab8_practice/libs/stat.h b/lab8_practice/libs/stat.h new file mode 100644 index 0000000..41a392d --- /dev/null +++ b/lab8_practice/libs/stat.h @@ -0,0 +1,27 @@ +#ifndef __LIBS_STAT_H__ +#define __LIBS_STAT_H__ + +#include + +struct stat { + uint32_t st_mode; // protection mode and file type + size_t st_nlinks; // number of hard links + size_t st_blocks; // number of blocks file is using + size_t st_size; // file size (bytes) +}; + +#define S_IFMT 070000 // mask for type of file +#define S_IFREG 010000 // ordinary regular file +#define S_IFDIR 020000 // directory +#define S_IFLNK 030000 // symbolic link +#define S_IFCHR 040000 // character device +#define S_IFBLK 050000 // block device + +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) // regular file +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) // directory +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) // symlink +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) // char device +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) // block device + +#endif /* !__LIBS_STAT_H__ */ + diff --git a/lab8_practice/libs/stdarg.h b/lab8_practice/libs/stdarg.h new file mode 100644 index 0000000..f6e0758 --- /dev/null +++ b/lab8_practice/libs/stdarg.h @@ -0,0 +1,12 @@ +#ifndef __LIBS_STDARG_H__ +#define __LIBS_STDARG_H__ + +/* compiler provides size of save area */ +typedef __builtin_va_list va_list; + +#define va_start(ap, last) (__builtin_va_start(ap, last)) +#define va_arg(ap, type) (__builtin_va_arg(ap, type)) +#define va_end(ap) /*nothing*/ + +#endif /* !__LIBS_STDARG_H__ */ + diff --git a/lab8_practice/libs/stdio.h b/lab8_practice/libs/stdio.h new file mode 100644 index 0000000..ab03960 --- /dev/null +++ b/lab8_practice/libs/stdio.h @@ -0,0 +1,24 @@ +#ifndef __LIBS_STDIO_H__ +#define __LIBS_STDIO_H__ + +#include +#include + +/* kern/libs/stdio.c */ +int cprintf(const char *fmt, ...); +int vcprintf(const char *fmt, va_list ap); +void cputchar(int c); +int cputs(const char *str); +int getchar(void); + +/* kern/libs/readline.c */ +char *readline(const char *prompt); + +/* libs/printfmt.c */ +void printfmt(void (*putch)(int, void *, int), int fd, void *putdat, const char *fmt, ...); +void vprintfmt(void (*putch)(int, void *, int), int fd, void *putdat, const char *fmt, va_list ap); +int snprintf(char *str, size_t size, const char *fmt, ...); +int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); + +#endif /* !__LIBS_STDIO_H__ */ + diff --git a/lab8_practice/libs/stdlib.h b/lab8_practice/libs/stdlib.h new file mode 100644 index 0000000..51878ef --- /dev/null +++ b/lab8_practice/libs/stdlib.h @@ -0,0 +1,17 @@ +#ifndef __LIBS_STDLIB_H__ +#define __LIBS_STDLIB_H__ + +#include + +/* the largest number rand will return */ +#define RAND_MAX 2147483647UL + +/* libs/rand.c */ +int rand(void); +void srand(unsigned int seed); + +/* libs/hash.c */ +uint32_t hash32(uint32_t val, unsigned int bits); + +#endif /* !__LIBS_RAND_H__ */ + diff --git a/lab8_practice/libs/string.c b/lab8_practice/libs/string.c new file mode 100644 index 0000000..8a326d1 --- /dev/null +++ b/lab8_practice/libs/string.c @@ -0,0 +1,379 @@ +#include +#include + +/* * + * strlen - calculate the length of the string @s, not including + * the terminating '\0' character. + * @s: the input string + * + * The strlen() function returns the length of string @s. + * */ +size_t +strlen(const char *s) { + size_t cnt = 0; + while (*s ++ != '\0') { + cnt ++; + } + return cnt; +} + +/* * + * strnlen - calculate the length of the string @s, not including + * the terminating '\0' char acter, but at most @len. + * @s: the input string + * @len: the max-length that function will scan + * + * Note that, this function looks only at the first @len characters + * at @s, and never beyond @s + @len. + * + * The return value is strlen(s), if that is less than @len, or + * @len if there is no '\0' character among the first @len characters + * pointed by @s. + * */ +size_t +strnlen(const char *s, size_t len) { + size_t cnt = 0; + while (cnt < len && *s ++ != '\0') { + cnt ++; + } + return cnt; +} + +/* * + * strcpy - copies the string pointed by @src into the array pointed by @dst, + * including the terminating null character. + * @dst: pointer to the destination array where the content is to be copied + * @src: string to be copied + * + * The return value is @dst. + * + * To avoid overflows, the size of array pointed by @dst should be long enough to + * contain the same string as @src (including the terminating null character), and + * should not overlap in memory with @src. + * */ +char * +strcpy(char *dst, const char *src) { +#ifdef __HAVE_ARCH_STRCPY + return __strcpy(dst, src); +#else + char *p = dst; + while ((*p ++ = *src ++) != '\0') + /* nothing */; + return dst; +#endif /* __HAVE_ARCH_STRCPY */ +} + +/* * + * strncpy - copies the first @len characters of @src to @dst. If the end of string @src + * if found before @len characters have been copied, @dst is padded with '\0' until a + * total of @len characters have been written to it. + * @dst: pointer to the destination array where the content is to be copied + * @src: string to be copied + * @len: maximum number of characters to be copied from @src + * + * The return value is @dst + * */ +char * +strncpy(char *dst, const char *src, size_t len) { + char *p = dst; + while (len > 0) { + if ((*p = *src) != '\0') { + src ++; + } + p ++, len --; + } + return dst; +} +/* * + * strcat - appends a copy of the @src string to the @dst string. The terminating null + * character in @dst is overwritten by the first character of @src, and a new null-character + * is appended at the end of the new string formed by the concatenation of both in @dst. + * @dst: pointer to the @dst array, which should be large enough to contain the concatenated + * resulting string. + * @src: string to be appended, this should not overlap @dst + * */ +char * +strcat(char *dst, const char *src) { + return strcpy(dst + strlen(dst), src); +} + +/* * + * strcmp - compares the string @s1 and @s2 + * @s1: string to be compared + * @s2: string to be compared + * + * This function starts comparing the first character of each string. If + * they are equal to each other, it continues with the following pairs until + * the characters differ or until a terminanting null-character is reached. + * + * Returns an integral value indicating the relationship between the strings: + * - A zero value indicates that both strings are equal; + * - A value greater than zero indicates that the first character that does + * not match has a greater value in @s1 than in @s2; + * - And a value less than zero indicates the opposite. + * */ +int +strcmp(const char *s1, const char *s2) { +#ifdef __HAVE_ARCH_STRCMP + return __strcmp(s1, s2); +#else + while (*s1 != '\0' && *s1 == *s2) { + s1 ++, s2 ++; + } + return (int)((unsigned char)*s1 - (unsigned char)*s2); +#endif /* __HAVE_ARCH_STRCMP */ +} + +/* * + * strncmp - compares up to @n characters of the string @s1 to those of the string @s2 + * @s1: string to be compared + * @s2: string to be compared + * @n: maximum number of characters to compare + * + * This function starts comparing the first character of each string. If + * they are equal to each other, it continues with the following pairs until + * the characters differ, until a terminating null-character is reached, or + * until @n characters match in both strings, whichever happens first. + * */ +int +strncmp(const char *s1, const char *s2, size_t n) { + while (n > 0 && *s1 != '\0' && *s1 == *s2) { + n --, s1 ++, s2 ++; + } + return (n == 0) ? 0 : (int)((unsigned char)*s1 - (unsigned char)*s2); +} + +/* * + * strchr - locates first occurrence of character in string + * @s: the input string + * @c: character to be located + * + * The strchr() function returns a pointer to the first occurrence of + * character in @s. If the value is not found, the function returns 'NULL'. + * */ +char * +strchr(const char *s, char c) { + while (*s != '\0') { + if (*s == c) { + return (char *)s; + } + s ++; + } + return NULL; +} + +/* * + * strfind - locates first occurrence of character in string + * @s: the input string + * @c: character to be located + * + * The strfind() function is like strchr() except that if @c is + * not found in @s, then it returns a pointer to the null byte at the + * end of @s, rather than 'NULL'. + * */ +char * +strfind(const char *s, char c) { + while (*s != '\0') { + if (*s == c) { + break; + } + s ++; + } + return (char *)s; +} + +/* * + * strtol - converts string to long integer + * @s: the input string that contains the representation of an integer number + * @endptr: reference to an object of type char *, whose value is set by the + * function to the next character in @s after the numerical value. This + * parameter can also be a null pointer, in which case it is not used. + * @base: x + * + * The function first discards as many whitespace characters as necessary until + * the first non-whitespace character is found. Then, starting from this character, + * takes as many characters as possible that are valid following a syntax that + * depends on the base parameter, and interprets them as a numerical value. Finally, + * a pointer to the first character following the integer representation in @s + * is stored in the object pointed by @endptr. + * + * If the value of base is zero, the syntax expected is similar to that of + * integer constants, which is formed by a succession of: + * - An optional plus or minus sign; + * - An optional prefix indicating octal or hexadecimal base ("0" or "0x" respectively) + * - A sequence of decimal digits (if no base prefix was specified) or either octal + * or hexadecimal digits if a specific prefix is present + * + * If the base value is between 2 and 36, the format expected for the integral number + * is a succession of the valid digits and/or letters needed to represent integers of + * the specified radix (starting from '0' and up to 'z'/'Z' for radix 36). The + * sequence may optionally be preceded by a plus or minus sign and, if base is 16, + * an optional "0x" or "0X" prefix. + * + * The strtol() function returns the converted integral number as a long int value. + * */ +long +strtol(const char *s, char **endptr, int base) { + int neg = 0; + long val = 0; + + // gobble initial whitespace + while (*s == ' ' || *s == '\t') { + s ++; + } + + // plus/minus sign + if (*s == '+') { + s ++; + } + else if (*s == '-') { + s ++, neg = 1; + } + + // hex or octal base prefix + if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) { + s += 2, base = 16; + } + else if (base == 0 && s[0] == '0') { + s ++, base = 8; + } + else if (base == 0) { + base = 10; + } + + // digits + while (1) { + int dig; + + if (*s >= '0' && *s <= '9') { + dig = *s - '0'; + } + else if (*s >= 'a' && *s <= 'z') { + dig = *s - 'a' + 10; + } + else if (*s >= 'A' && *s <= 'Z') { + dig = *s - 'A' + 10; + } + else { + break; + } + if (dig >= base) { + break; + } + s ++, val = (val * base) + dig; + // we don't properly detect overflow! + } + + if (endptr) { + *endptr = (char *) s; + } + return (neg ? -val : val); +} + +/* * + * memset - sets the first @n bytes of the memory area pointed by @s + * to the specified value @c. + * @s: pointer the the memory area to fill + * @c: value to set + * @n: number of bytes to be set to the value + * + * The memset() function returns @s. + * */ +void * +memset(void *s, char c, size_t n) { +#ifdef __HAVE_ARCH_MEMSET + return __memset(s, c, n); +#else + char *p = s; + while (n -- > 0) { + *p ++ = c; + } + return s; +#endif /* __HAVE_ARCH_MEMSET */ +} + +/* * + * memmove - copies the values of @n bytes from the location pointed by @src to + * the memory area pointed by @dst. @src and @dst are allowed to overlap. + * @dst pointer to the destination array where the content is to be copied + * @src pointer to the source of data to by copied + * @n: number of bytes to copy + * + * The memmove() function returns @dst. + * */ +void * +memmove(void *dst, const void *src, size_t n) { +#ifdef __HAVE_ARCH_MEMMOVE + return __memmove(dst, src, n); +#else + const char *s = src; + char *d = dst; + if (s < d && s + n > d) { + s += n, d += n; + while (n -- > 0) { + *-- d = *-- s; + } + } else { + while (n -- > 0) { + *d ++ = *s ++; + } + } + return dst; +#endif /* __HAVE_ARCH_MEMMOVE */ +} + +/* * + * memcpy - copies the value of @n bytes from the location pointed by @src to + * the memory area pointed by @dst. + * @dst pointer to the destination array where the content is to be copied + * @src pointer to the source of data to by copied + * @n: number of bytes to copy + * + * The memcpy() returns @dst. + * + * Note that, the function does not check any terminating null character in @src, + * it always copies exactly @n bytes. To avoid overflows, the size of arrays pointed + * by both @src and @dst, should be at least @n bytes, and should not overlap + * (for overlapping memory area, memmove is a safer approach). + * */ +void * +memcpy(void *dst, const void *src, size_t n) { +#ifdef __HAVE_ARCH_MEMCPY + return __memcpy(dst, src, n); +#else + const char *s = src; + char *d = dst; + while (n -- > 0) { + *d ++ = *s ++; + } + return dst; +#endif /* __HAVE_ARCH_MEMCPY */ +} + +/* * + * memcmp - compares two blocks of memory + * @v1: pointer to block of memory + * @v2: pointer to block of memory + * @n: number of bytes to compare + * + * The memcmp() functions returns an integral value indicating the + * relationship between the content of the memory blocks: + * - A zero value indicates that the contents of both memory blocks are equal; + * - A value greater than zero indicates that the first byte that does not + * match in both memory blocks has a greater value in @v1 than in @v2 + * as if evaluated as unsigned char values; + * - And a value less than zero indicates the opposite. + * */ +int +memcmp(const void *v1, const void *v2, size_t n) { + const char *s1 = (const char *)v1; + const char *s2 = (const char *)v2; + while (n -- > 0) { + if (*s1 != *s2) { + return (int)((unsigned char)*s1 - (unsigned char)*s2); + } + s1 ++, s2 ++; + } + return 0; +} + diff --git a/lab8_practice/libs/string.h b/lab8_practice/libs/string.h new file mode 100644 index 0000000..76c6f3b --- /dev/null +++ b/lab8_practice/libs/string.h @@ -0,0 +1,28 @@ +#ifndef __LIBS_STRING_H__ +#define __LIBS_STRING_H__ + +#include + +size_t strlen(const char *s); +size_t strnlen(const char *s, size_t len); + +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, size_t len); +char *strcat(char *dst, const char *src); +char *strdup(const char *src); +char *stradd(const char *src1, const char *src2); + +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); + +char *strchr(const char *s, char c); +char *strfind(const char *s, char c); +long strtol(const char *s, char **endptr, int base); + +void *memset(void *s, char c, size_t n); +void *memmove(void *dst, const void *src, size_t n); +void *memcpy(void *dst, const void *src, size_t n); +int memcmp(const void *v1, const void *v2, size_t n); + +#endif /* !__LIBS_STRING_H__ */ + diff --git a/lab8_practice/libs/unistd.h b/lab8_practice/libs/unistd.h new file mode 100644 index 0000000..8599be6 --- /dev/null +++ b/lab8_practice/libs/unistd.h @@ -0,0 +1,68 @@ +#ifndef __LIBS_UNISTD_H__ +#define __LIBS_UNISTD_H__ + +#define T_SYSCALL 0x80 + +/* syscall number */ +#define SYS_exit 1 +#define SYS_fork 2 +#define SYS_wait 3 +#define SYS_exec 4 +#define SYS_clone 5 +#define SYS_yield 10 +#define SYS_sleep 11 +#define SYS_kill 12 +#define SYS_gettime 17 +#define SYS_getpid 18 +#define SYS_mmap 20 +#define SYS_munmap 21 +#define SYS_shmem 22 +#define SYS_putc 30 +#define SYS_pgdir 31 +#define SYS_open 100 +#define SYS_close 101 +#define SYS_read 102 +#define SYS_write 103 +#define SYS_seek 104 +#define SYS_fstat 110 +#define SYS_fsync 111 +#define SYS_getcwd 121 +#define SYS_getdirentry 128 +#define SYS_dup 130 +/* OLNY FOR LAB6 */ +#define SYS_lab6_set_priority 255 + +/* SYS_fork flags */ +#define CLONE_VM 0x00000100 // set if VM shared between processes +#define CLONE_THREAD 0x00000200 // thread group +#define CLONE_FS 0x00000800 // set if shared between processes + +/* VFS flags */ +// flags for open: choose one of these +#define O_RDONLY 0 // open for reading only +#define O_WRONLY 1 // open for writing only +#define O_RDWR 2 // open for reading and writing +// then or in any of these: +#define O_CREAT 0x00000004 // create file if it does not exist +#define O_EXCL 0x00000008 // error if O_CREAT and the file exists +#define O_TRUNC 0x00000010 // truncate file upon open +#define O_APPEND 0x00000020 // append on each write +// additonal related definition +#define O_ACCMODE 3 // mask for O_RDONLY / O_WRONLY / O_RDWR + +#define NO_FD -0x9527 // invalid fd + +/* lseek codes */ +#define LSEEK_SET 0 // seek relative to beginning of file +#define LSEEK_CUR 1 // seek relative to current position in file +#define LSEEK_END 2 // seek relative to end of file + +#define FS_MAX_DNAME_LEN 31 +#define FS_MAX_FNAME_LEN 255 +#define FS_MAX_FPATH_LEN 4095 + +#define EXEC_MAX_ARG_NUM 32 +#define EXEC_MAX_ARG_LEN 4095 + +#endif /* !__LIBS_UNISTD_H__ */ + diff --git a/lab8_practice/tools/boot.ld b/lab8_practice/tools/boot.ld new file mode 100644 index 0000000..dc732b0 --- /dev/null +++ b/lab8_practice/tools/boot.ld @@ -0,0 +1,15 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + . = 0x7C00; + + .startup : { + *bootasm.o(.text) + } + + .text : { *(.text) } + .data : { *(.data .rodata) } + + /DISCARD/ : { *(.eh_*) } +} diff --git a/lab8_practice/tools/function.mk b/lab8_practice/tools/function.mk new file mode 100644 index 0000000..9b8be0c --- /dev/null +++ b/lab8_practice/tools/function.mk @@ -0,0 +1,95 @@ +OBJPREFIX := __objs_ + +.SECONDEXPANSION: +# -------------------- function begin -------------------- + +# list all files in some directories: (#directories, #types) +listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\ + $(wildcard $(addsuffix $(SLASH)*,$(1)))) + +# get .o obj files: (#files[, packet]) +toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\ + $(addsuffix .o,$(basename $(1)))) + +# get .d dependency files: (#files[, packet]) +todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2))) + +totarget = $(addprefix $(BINDIR)$(SLASH),$(1)) + +# change $(name) to $(OBJPREFIX)$(name): (#names) +packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX)) + +# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir]) +define cc_template +$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@) + @$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@ +$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@) + @echo + cc $$< + $(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@ +ALLOBJS += $$(call toobj,$(1),$(4)) +endef + +# compile file: (#files, cc[, flags, dir]) +define do_cc_compile +$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4)))) +endef + +# add files to packet: (#files, cc[, flags, packet, dir]) +define do_add_files_to_packet +__temp_packet__ := $(call packetname,$(4)) +ifeq ($$(origin $$(__temp_packet__)),undefined) +$$(__temp_packet__) := +endif +__temp_objs__ := $(call toobj,$(1),$(5)) +$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5)))) +$$(__temp_packet__) += $$(__temp_objs__) +endef + +# add objs to packet: (#objs, packet) +define do_add_objs_to_packet +__temp_packet__ := $(call packetname,$(2)) +ifeq ($$(origin $$(__temp_packet__)),undefined) +$$(__temp_packet__) := +endif +$$(__temp_packet__) += $(1) +endef + +# add packets and objs to target (target, #packes, #objs[, cc, flags]) +define do_create_target +__temp_target__ = $(call totarget,$(1)) +__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3) +TARGETS += $$(__temp_target__) +ifneq ($(4),) +$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) + $(V)$(4) $(5) $$^ -o $$@ +else +$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) +endif +endef + +# finish all +define do_finish_all +ALLDEPS = $$(ALLOBJS:.o=.d) +$$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)): + @$(MKDIR) $$@ +endef + +# -------------------- function end -------------------- +# compile file: (#files, cc[, flags, dir]) +cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4))) + +# add files to packet: (#files, cc[, flags, packet, dir]) +add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5))) + +# add objs to packet: (#objs, packet) +add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2))) + +# add packets and objs to target (target, #packes, #objs, cc, [, flags]) +create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5))) + +read_packet = $(foreach p,$(call packetname,$(1)),$($(p))) + +add_dependency = $(eval $(1): $(2)) + +finish_all = $(eval $(call do_finish_all)) + diff --git a/lab8_practice/tools/gdbinit b/lab8_practice/tools/gdbinit new file mode 100644 index 0000000..df5df85 --- /dev/null +++ b/lab8_practice/tools/gdbinit @@ -0,0 +1,3 @@ +file bin/kernel +target remote :1234 +break kern_init diff --git a/lab8_practice/tools/grade.sh b/lab8_practice/tools/grade.sh new file mode 100644 index 0000000..4b46529 --- /dev/null +++ b/lab8_practice/tools/grade.sh @@ -0,0 +1,560 @@ +#!/bin/sh + +verbose=false +if [ "x$1" = "x-v" ]; then + verbose=true + out=/dev/stdout + err=/dev/stderr +else + out=/dev/null + err=/dev/null +fi + +## make & makeopts +if gmake --version > /dev/null 2>&1; then + make=gmake; +else + make=make; +fi + +makeopts="--quiet --no-print-directory -j" + +make_print() { + echo `$make $makeopts print-$1` +} + +## command tools +awk='awk' +bc='bc' +date='date' +grep='grep' +rm='rm -f' +sed='sed' + +## symbol table +sym_table='obj/kernel.sym' + +## gdb & gdbopts +gdb="$(make_print GDB)" +gdbport='1234' + +gdb_in="$(make_print GRADE_GDB_IN)" + +## qemu & qemuopts +qemu="$(make_print qemu)" + +qemu_out="$(make_print GRADE_QEMU_OUT)" + +if $qemu -nographic -help | grep -q '^-gdb'; then + qemugdb="-gdb tcp::$gdbport" +else + qemugdb="-s -p $gdbport" +fi + +## default variables +default_timeout=30 +default_pts=5 + +pts=5 +part=0 +part_pos=0 +total=0 +total_pos=0 + +## default functions +update_score() { + total=`expr $total + $part` + total_pos=`expr $total_pos + $part_pos` + part=0 + part_pos=0 +} + +get_time() { + echo `$date +%s.%N 2> /dev/null` +} + +show_part() { + echo "Part $1 Score: $part/$part_pos" + echo + update_score +} + +show_final() { + update_score + echo "Total Score: $total/$total_pos" + if [ $total -lt $total_pos ]; then + exit 1 + fi +} + +show_time() { + t1=$(get_time) + time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null` + echo "(${time}s)" +} + +show_build_tag() { + echo "$1:" | $awk '{printf "%-24s ", $0}' +} + +show_check_tag() { + echo "$1:" | $awk '{printf " -%-40s ", $0}' +} + +show_msg() { + echo $1 + shift + if [ $# -gt 0 ]; then + echo -e "$@" | awk '{printf " %s\n", $0}' + echo + fi +} + +pass() { + show_msg OK "$@" + part=`expr $part + $pts` + part_pos=`expr $part_pos + $pts` +} + +fail() { + show_msg WRONG "$@" + part_pos=`expr $part_pos + $pts` +} + +run_qemu() { + # Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty, + # wait until $brkfun is reached or $timeout expires, then kill QEMU + qemuextra= + if [ "$brkfun" ]; then + qemuextra="-S $qemugdb" + fi + + if [ -z "$timeout" ] || [ $timeout -le 0 ]; then + timeout=$default_timeout; + fi + + t0=$(get_time) + ( + ulimit -t $timeout + exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra + ) > $out 2> $err & + pid=$! + + # wait for QEMU to start + sleep 1 + + if [ -n "$brkfun" ]; then + # find the address of the kernel $brkfun function + brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'` + brkaddr_phys=`echo $brkaddr | sed "s/^c0/00/g"` + ( + echo "target remote localhost:$gdbport" + echo "break *0x$brkaddr" + if [ "$brkaddr" != "$brkaddr_phys" ]; then + echo "break *0x$brkaddr_phys" + fi + echo "continue" + ) > $gdb_in + + $gdb -batch -nx -x $gdb_in > /dev/null 2>&1 + + # make sure that QEMU is dead + # on OS X, exiting gdb doesn't always exit qemu + kill $pid > /dev/null 2>&1 + fi +} + +build_run() { + # usage: build_run + show_build_tag "$1" + shift + + if $verbose; then + echo "$make $@ ..." + fi + $make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err + + if [ $? -ne 0 ]; then + echo $make $@ failed + exit 1 + fi + + # now run qemu and save the output + run_qemu + + show_time + + cp $qemu_out .`echo $tag | tr '[:upper:]' '[:lower:]' | sed 's/ /_/g'`.log +} + +check_result() { + # usage: check_result + show_check_tag "$1" + shift + + # give qemu some time to run (for asynchronous mode) + if [ ! -s $qemu_out ]; then + sleep 4 + fi + + if [ ! -s $qemu_out ]; then + fail > /dev/null + echo 'no $qemu_out' + else + check=$1 + shift + $check "$@" + fi +} + +check_regexps() { + okay=yes + not=0 + reg=0 + error= + for i do + if [ "x$i" = "x!" ]; then + not=1 + elif [ "x$i" = "x-" ]; then + reg=1 + else + if [ $reg -ne 0 ]; then + $grep '-E' "^$i\$" $qemu_out > /dev/null + else + $grep '-F' "$i" $qemu_out > /dev/null + fi + found=$(($? == 0)) + if [ $found -eq $not ]; then + if [ $found -eq 0 ]; then + msg="!! error: missing '$i'" + else + msg="!! error: got unexpected line '$i'" + fi + okay=no + if [ -z "$error" ]; then + error="$msg" + else + error="$error\n$msg" + fi + fi + not=0 + reg=0 + fi + done + if [ "$okay" = "yes" ]; then + pass + else + fail "$error" + if $verbose; then + exit 1 + fi + fi +} + +run_test() { + # usage: run_test [-tag ] [-prog ] [-Ddef...] [-check ] checkargs ... + tag= + prog= + check=check_regexps + while true; do + select= + case $1 in + -tag|-prog) + select=`expr substr $1 2 ${#1}` + eval $select='$2' + ;; + esac + if [ -z "$select" ]; then + break + fi + shift + shift + done + defs= + while expr "x$1" : "x-D.*" > /dev/null; do + defs="DEFS+='$1' $defs" + shift + done + if [ "x$1" = "x-check" ]; then + check=$2 + shift + shift + fi + + if [ -z "$prog" ]; then + $make $makeopts touch > /dev/null 2>&1 + args="$defs" + else + if [ -z "$tag" ]; then + tag="$prog" + fi + args="build-$prog $defs" + fi + + build_run "$tag" "$args" + + check_result 'check result' "$check" "$@" +} + +quick_run() { + # usage: quick_run [-Ddef...] + tag="$1" + shift + defs= + while expr "x$1" : "x-D.*" > /dev/null; do + defs="DEFS+='$1' $defs" + shift + done + + $make $makeopts touch > /dev/null 2>&1 + build_run "$tag" "$defs" +} + +quick_check() { + # usage: quick_check checkargs ... + tag="$1" + shift + check_result "$tag" check_regexps "$@" +} + +## kernel image +osimg=$(make_print ucoreimg) + +## swap image +swapimg=$(make_print swapimg) + +## set default qemu-options +qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback" + +## set break-function, default is readline +brkfun=readline + +default_check() { + pts=7 + check_regexps "$@" + + pts=3 + quick_check 'check output' \ + 'memory management: default_pmm_manager' \ + 'check_alloc_page() succeeded!' \ + 'check_pgdir() succeeded!' \ + 'check_boot_pgdir() succeeded!' \ + 'PDE(0e0) c0000000-f8000000 38000000 urw' \ + ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ + 'PDE(001) fac00000-fb000000 00400000 -rw' \ + ' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \ + ' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \ + 'check_vma_struct() succeeded!' \ + 'page fault at 0x00000100: K/W [no page found].' \ + 'check_pgfault() succeeded!' \ + 'check_vmm() succeeded.' \ + 'page fault at 0x00001000: K/W [no page found].' \ + 'page fault at 0x00002000: K/W [no page found].' \ + 'page fault at 0x00003000: K/W [no page found].' \ + 'page fault at 0x00004000: K/W [no page found].' \ + 'write Virt Page e in fifo_check_swap' \ + 'page fault at 0x00005000: K/W [no page found].' \ + 'page fault at 0x00001000: K/W [no page found]' \ + 'page fault at 0x00002000: K/W [no page found].' \ + 'page fault at 0x00003000: K/W [no page found].' \ + 'page fault at 0x00004000: K/W [no page found].' \ + 'check_swap() succeeded!' \ + '++ setup timer interrupts' +} + +## check now!! + +run_test -prog 'badsegment' -check default_check \ + 'kernel_execve: pid = 2, name = "badsegment".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000d General Protection' \ + ' err 0x00000028' \ + - ' eip 0x008.....' \ + - ' esp 0xaff.....' \ + ' cs 0x----001b' \ + ' ss 0x----0023' \ + ! - 'user panic at .*' + +run_test -prog 'divzero' -check default_check \ + 'kernel_execve: pid = 2, name = "divzero".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x00000000 Divide error' \ + - ' eip 0x008.....' \ + - ' esp 0xaff.....' \ + ' cs 0x----001b' \ + ' ss 0x----0023' \ + ! - 'user panic at .*' + +run_test -prog 'softint' -check default_check \ + 'kernel_execve: pid = 2, name = "softint".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000d General Protection' \ + ' err 0x00000072' \ + - ' eip 0x008.....' \ + - ' esp 0xaff.....' \ + ' cs 0x----001b' \ + ' ss 0x----0023' \ + ! - 'user panic at .*' + +pts=10 + +run_test -prog 'faultread' -check default_check \ + 'kernel_execve: pid = 2, name = "faultread".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000e Page Fault' \ + ' err 0x00000004' \ + - ' eip 0x008.....' \ + ! - 'user panic at .*' + +run_test -prog 'faultreadkernel' -check default_check \ + 'kernel_execve: pid = 2, name = "faultreadkernel".' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000e Page Fault' \ + ' err 0x00000005' \ + - ' eip 0x008.....' \ + ! - 'user panic at .*' + +run_test -prog 'hello' -check default_check \ + 'kernel_execve: pid = 2, name = "hello".' \ + 'Hello world!!.' \ + 'I am process 2.' \ + 'hello pass.' + +run_test -prog 'testbss' -check default_check \ + 'kernel_execve: pid = 2, name = "testbss".' \ + 'Making sure bss works right...' \ + 'Yes, good. Now doing a wild write off the end...' \ + 'testbss may pass.' \ + - 'trapframe at 0xc.......' \ + 'trap 0x0000000e Page Fault' \ + ' err 0x00000006' \ + - ' eip 0x008.....' \ + 'killed by kernel.' \ + ! - 'user panic at .*' + +run_test -prog 'pgdir' -check default_check \ + 'kernel_execve: pid = 2, name = "pgdir".' \ + 'I am 2, print pgdir.' \ + 'PDE(001) 00800000-00c00000 00400000 urw' \ + ' |-- PTE(00002) 00800000-00802000 00002000 ur-' \ + ' |-- PTE(00001) 00802000-00803000 00001000 urw' \ + 'PDE(001) afc00000-b0000000 00400000 urw' \ + ' |-- PTE(00004) afffc000-b0000000 00004000 urw' \ + 'PDE(0e0) c0000000-f8000000 38000000 urw' \ + ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ + 'pgdir pass.' + +run_test -prog 'yield' -check default_check \ + 'kernel_execve: pid = 2, name = "yield".' \ + 'Hello, I am process 2.' \ + 'Back in process 2, iteration 0.' \ + 'Back in process 2, iteration 1.' \ + 'Back in process 2, iteration 2.' \ + 'Back in process 2, iteration 3.' \ + 'Back in process 2, iteration 4.' \ + 'All done in process 2.' \ + 'yield pass.' + + +run_test -prog 'badarg' -check default_check \ + 'kernel_execve: pid = 2, name = "badarg".' \ + 'fork ok.' \ + 'badarg pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +pts=10 + +run_test -prog 'exit' -check default_check \ + 'kernel_execve: pid = 2, name = "exit".' \ + 'I am the parent. Forking the child...' \ + 'I am the parent, waiting now..' \ + 'I am the child.' \ + - 'waitpid [0-9]+ ok\.' \ + 'exit pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +run_test -prog 'spin' -check default_check \ + 'kernel_execve: pid = 2, name = "spin".' \ + 'I am the parent. Forking the child...' \ + 'I am the parent. Running the child...' \ + 'I am the child. spinning ...' \ + 'I am the parent. Killing the child...' \ + 'kill returns 0' \ + 'wait returns 0' \ + 'spin may pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +run_test -prog 'waitkill' -check default_check \ + 'kernel_execve: pid = 2, name = "waitkill".' \ + 'wait child 1.' \ + 'child 2.' \ + 'child 1.' \ + 'kill parent ok.' \ + 'kill child1 ok.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'user panic at .*' + +pts=15 + +run_test -prog 'forktest' -check default_check \ + 'kernel_execve: pid = 2, name = "forktest".' \ + 'I am child 31' \ + 'I am child 19' \ + 'I am child 13' \ + 'I am child 0' \ + 'forktest pass.' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' \ + ! - 'fork claimed to work [0-9]+ times!' \ + ! 'wait stopped early' \ + ! 'wait got too many' \ + ! - 'user panic at .*' + +pts=10 +run_test -prog 'forktree' -check default_check \ + 'kernel_execve: pid = 2, name = "forktree".' \ + - '....: I am '\'''\' \ + - '....: I am '\''0'\' \ + - '....: I am '\'''\' \ + - '....: I am '\''1'\' \ + - '....: I am '\''0'\' \ + - '....: I am '\''01'\' \ + - '....: I am '\''00'\' \ + - '....: I am '\''11'\' \ + - '....: I am '\''10'\' \ + - '....: I am '\''101'\' \ + - '....: I am '\''100'\' \ + - '....: I am '\''111'\' \ + - '....: I am '\''110'\' \ + - '....: I am '\''001'\' \ + - '....: I am '\''000'\' \ + - '....: I am '\''011'\' \ + - '....: I am '\''010'\' \ + - '....: I am '\''0101'\' \ + - '....: I am '\''0100'\' \ + - '....: I am '\''0111'\' \ + - '....: I am '\''0110'\' \ + - '....: I am '\''0001'\' \ + - '....: I am '\''0000'\' \ + - '....: I am '\''0011'\' \ + - '....: I am '\''0010'\' \ + - '....: I am '\''1101'\' \ + - '....: I am '\''1100'\' \ + - '....: I am '\''1111'\' \ + - '....: I am '\''1110'\' \ + - '....: I am '\''1001'\' \ + - '....: I am '\''1000'\' \ + - '....: I am '\''1011'\' \ + - '....: I am '\''1010'\' \ + 'all user-mode processes have quit.' \ + 'init check memory pass.' + +## print final-score +show_final diff --git a/lab8_practice/tools/kernel.ld b/lab8_practice/tools/kernel.ld new file mode 100644 index 0000000..717cfca --- /dev/null +++ b/lab8_practice/tools/kernel.ld @@ -0,0 +1,51 @@ +/* Simple linker script for the ucore kernel. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(kern_entry) + +BASE_ADDRESS = 0xFFFFFFFFC0200000; + +SECTIONS +{ + /* Load the kernel at this address: "." means the current address */ + . = BASE_ADDRESS; + + .text : { + *(.text.kern_entry .text .stub .text.* .gnu.linkonce.t.*) + } + + PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + /* Adjust the address for the data segment to the next page */ + . = ALIGN(0x1000); + + /* The data segment */ + .data : { + *(.data) + *(.data.*) + } + + .sdata : { + *(.sdata) + *(.sdata.*) + } + + PROVIDE(edata = .); + + .bss : { + *(.bss) + *(.bss.*) + *(.sbss*) + } + + PROVIDE(end = .); + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack) + } +} diff --git a/lab8_practice/tools/mksfs.c b/lab8_practice/tools/mksfs.c new file mode 100644 index 0000000..41d2a0e --- /dev/null +++ b/lab8_practice/tools/mksfs.c @@ -0,0 +1,582 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int bool; + +#define __error(msg, quit, ...) \ + do { \ + fprintf(stderr, #msg ": function %s - line %d: ", __FUNCTION__, __LINE__); \ + if (errno != 0) { \ + fprintf(stderr, "[error] %s: ", strerror(errno)); \ + } \ + fprintf(stderr, "\n\t"), fprintf(stderr, __VA_ARGS__); \ + errno = 0; \ + if (quit) { \ + exit(-1); \ + } \ + } while (0) + +#define warn(...) __error(warn, 0, __VA_ARGS__) +#define bug(...) __error(bug, 1, __VA_ARGS__) + +#define static_assert(x) \ + switch (x) {case 0: case (x): ; } + +/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ +#define GOLDEN_RATIO_PRIME_32 0x9e370001UL + +#define HASH_SHIFT 10 +#define HASH_LIST_SIZE (1 << HASH_SHIFT) + +static inline uint32_t +__hash32(uint32_t val, unsigned int bits) { + uint32_t hash = val * GOLDEN_RATIO_PRIME_32; + return (hash >> (32 - bits)); +} + +static uint32_t +hash32(uint32_t val) { + return __hash32(val, HASH_SHIFT); +} + +static uint32_t +hash64(uint64_t val) { + return __hash32((uint32_t)val, HASH_SHIFT); +} + +void * +safe_malloc(size_t size) { + void *ret; + if ((ret = malloc(size)) == NULL) { + bug("malloc %lu bytes failed.\n", (long unsigned)size); + } + return ret; +} + +char * +safe_strdup(const char *str) { + char *ret; + if ((ret = strdup(str)) == NULL) { + bug("strdup failed: %s\n", str); + } + return ret; +} + +struct stat * +safe_stat(const char *filename) { + static struct stat __stat; + if (stat(filename, &__stat) != 0) { + bug("stat %s failed.\n", filename); + } + return &__stat; +} + +struct stat * +safe_fstat(int fd) { + static struct stat __stat; + if (fstat(fd, &__stat) != 0) { + bug("fstat %d failed.\n", fd); + } + return &__stat; +} + +struct stat * +safe_lstat(const char *name) { + static struct stat __stat; + if (lstat(name, &__stat) != 0) { + bug("lstat '%s' failed.\n", name); + } + return &__stat; +} + +void +safe_fchdir(int fd) { + if (fchdir(fd) != 0) { + bug("fchdir failed %d.\n", fd); + } +} + +#define SFS_MAGIC 0x2f8dbe2a +#define SFS_NDIRECT 12 +#define SFS_BLKSIZE 4096 // 4K +#define SFS_MAX_NBLKS (1024UL * 512) // 4K * 512K +#define SFS_MAX_INFO_LEN 31 +#define SFS_MAX_FNAME_LEN 255 +#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) // 128M + +#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT) +#define SFS_TYPE_FILE 1 +#define SFS_TYPE_DIR 2 +#define SFS_TYPE_LINK 3 + +#define SFS_BLKN_SUPER 0 +#define SFS_BLKN_ROOT 1 +#define SFS_BLKN_FREEMAP 2 + +struct cache_block { + uint32_t ino; + struct cache_block *hash_next; + void *cache; +}; + +struct cache_inode { + struct inode { + uint32_t size; + uint16_t type; + uint16_t nlinks; + uint32_t blocks; + uint32_t direct[SFS_NDIRECT]; + uint32_t indirect; + uint32_t db_indirect; + } inode; + ino_t real; + uint32_t ino; + uint32_t nblks; + struct cache_block *l1, *l2; + struct cache_inode *hash_next; +}; + +struct sfs_fs { + struct { + uint32_t magic; + uint32_t blocks; + uint32_t unused_blocks; + char info[SFS_MAX_INFO_LEN + 1]; + } super; + struct subpath { + struct subpath *next, *prev; + char *subname; + } __sp_nil, *sp_root, *sp_end; + int imgfd; + uint32_t ninos, next_ino; + struct cache_inode *root; + struct cache_inode *inodes[HASH_LIST_SIZE]; + struct cache_block *blocks[HASH_LIST_SIZE]; +}; + +struct sfs_entry { + uint32_t ino; + char name[SFS_MAX_FNAME_LEN + 1]; +}; + +static uint32_t +sfs_alloc_ino(struct sfs_fs *sfs) { + if (sfs->next_ino < sfs->ninos) { + sfs->super.unused_blocks --; + return sfs->next_ino ++; + } + bug("out of disk space.\n"); +} + +static struct cache_block * +alloc_cache_block(struct sfs_fs *sfs, uint32_t ino) { + struct cache_block *cb = safe_malloc(sizeof(struct cache_block)); + cb->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs); + cb->cache = memset(safe_malloc(SFS_BLKSIZE), 0, SFS_BLKSIZE); + struct cache_block **head = sfs->blocks + hash32(ino); + cb->hash_next = *head, *head = cb; + return cb; +} + +struct cache_block * +search_cache_block(struct sfs_fs *sfs, uint32_t ino) { + struct cache_block *cb = sfs->blocks[hash32(ino)]; + while (cb != NULL && cb->ino != ino) { + cb = cb->hash_next; + } + return cb; +} + +static struct cache_inode * +alloc_cache_inode(struct sfs_fs *sfs, ino_t real, uint32_t ino, uint16_t type) { + struct cache_inode *ci = safe_malloc(sizeof(struct cache_inode)); + ci->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs); + ci->real = real, ci->nblks = 0, ci->l1 = ci->l2 = NULL; + struct inode *inode = &(ci->inode); + memset(inode, 0, sizeof(struct inode)); + inode->type = type; + struct cache_inode **head = sfs->inodes + hash64(real); + ci->hash_next = *head, *head = ci; + return ci; +} + +struct cache_inode * +search_cache_inode(struct sfs_fs *sfs, ino_t real) { + struct cache_inode *ci = sfs->inodes[hash64(real)]; + while (ci != NULL && ci->real != real) { + ci = ci->hash_next; + } + return ci; +} + +struct sfs_fs * +create_sfs(int imgfd) { + uint32_t ninos, next_ino; + struct stat *stat = safe_fstat(imgfd); + if ((ninos = stat->st_size / SFS_BLKSIZE) > SFS_MAX_NBLKS) { + ninos = SFS_MAX_NBLKS; + warn("img file is too big (%llu bytes, only use %u blocks).\n", + (unsigned long long)stat->st_size, ninos); + } + if ((next_ino = SFS_BLKN_FREEMAP + (ninos + SFS_BLKBITS - 1) / SFS_BLKBITS) >= ninos) { + bug("img file is too small (%llu bytes, %u blocks, bitmap use at least %u blocks).\n", + (unsigned long long)stat->st_size, ninos, next_ino - 2); + } + + struct sfs_fs *sfs = safe_malloc(sizeof(struct sfs_fs)); + sfs->super.magic = SFS_MAGIC; + sfs->super.blocks = ninos, sfs->super.unused_blocks = ninos - next_ino; + snprintf(sfs->super.info, SFS_MAX_INFO_LEN, "simple file system"); + + sfs->ninos = ninos, sfs->next_ino = next_ino, sfs->imgfd = imgfd; + sfs->sp_root = sfs->sp_end = &(sfs->__sp_nil); + sfs->sp_end->prev = sfs->sp_end->next = NULL; + + int i; + for (i = 0; i < HASH_LIST_SIZE; i ++) { + sfs->inodes[i] = NULL; + sfs->blocks[i] = NULL; + } + + sfs->root = alloc_cache_inode(sfs, 0, SFS_BLKN_ROOT, SFS_TYPE_DIR); + return sfs; +} + +static void +subpath_push(struct sfs_fs *sfs, const char *subname) { + struct subpath *subpath = safe_malloc(sizeof(struct subpath)); + subpath->subname = safe_strdup(subname); + sfs->sp_end->next = subpath; + subpath->prev = sfs->sp_end; + subpath->next = NULL; + sfs->sp_end = subpath; +} + +static void +subpath_pop(struct sfs_fs *sfs) { + assert(sfs->sp_root != sfs->sp_end); + struct subpath *subpath = sfs->sp_end; + sfs->sp_end = sfs->sp_end->prev, sfs->sp_end->next = NULL; + free(subpath->subname), free(subpath); +} + +static void +subpath_show(FILE *fout, struct sfs_fs *sfs, const char *name) { + struct subpath *subpath = sfs->sp_root; + fprintf(fout, "current is: /"); + while ((subpath = subpath->next) != NULL) { + fprintf(fout, "%s/", subpath->subname); + } + if (name != NULL) { + fprintf(fout, "%s", name); + } + fprintf(fout, "\n"); +} + +static void +write_block(struct sfs_fs *sfs, void *data, size_t len, uint32_t ino) { + assert(len <= SFS_BLKSIZE && ino < sfs->ninos); + static char buffer[SFS_BLKSIZE]; + if (len != SFS_BLKSIZE) { + memset(buffer, 0, sizeof(buffer)); + data = memcpy(buffer, data, len); + } + off_t offset = (off_t)ino * SFS_BLKSIZE; + ssize_t ret; + if ((ret = pwrite(sfs->imgfd, data, SFS_BLKSIZE, offset)) != SFS_BLKSIZE) { + bug("write %u block failed: (%d/%d).\n", ino, (int)ret, SFS_BLKSIZE); + } +} + +static void +flush_cache_block(struct sfs_fs *sfs, struct cache_block *cb) { + write_block(sfs, cb->cache, SFS_BLKSIZE, cb->ino); +} + +static void +flush_cache_inode(struct sfs_fs *sfs, struct cache_inode *ci) { + write_block(sfs, &(ci->inode), sizeof(ci->inode), ci->ino); +} + +void +close_sfs(struct sfs_fs *sfs) { + static char buffer[SFS_BLKSIZE]; + uint32_t i, j, ino = SFS_BLKN_FREEMAP; + uint32_t ninos = sfs->ninos, next_ino = sfs->next_ino; + for (i = 0; i < ninos; ino ++, i += SFS_BLKBITS) { + memset(buffer, 0, sizeof(buffer)); + if (i + SFS_BLKBITS > next_ino) { + uint32_t start = 0, end = SFS_BLKBITS; + if (i < next_ino) { + start = next_ino - i; + } + if (i + SFS_BLKBITS > ninos) { + end = ninos - i; + } + uint32_t *data = (uint32_t *)buffer; + const uint32_t bits = sizeof(bits) * CHAR_BIT; + for (j = start; j < end; j ++) { + data[j / bits] |= (1 << (j % bits)); + } + } + write_block(sfs, buffer, sizeof(buffer), ino); + } + write_block(sfs, &(sfs->super), sizeof(sfs->super), SFS_BLKN_SUPER); + + for (i = 0; i < HASH_LIST_SIZE; i ++) { + struct cache_block *cb = sfs->blocks[i]; + while (cb != NULL) { + flush_cache_block(sfs, cb); + cb = cb->hash_next; + } + struct cache_inode *ci = sfs->inodes[i]; + while (ci != NULL) { + flush_cache_inode(sfs, ci); + ci = ci->hash_next; + } + } +} + +struct sfs_fs * +open_img(const char *imgname) { + const char *expect = ".img", *ext = imgname + strlen(imgname) - strlen(expect); + if (ext <= imgname || strcmp(ext, expect) != 0) { + bug("invalid .img file name '%s'.\n", imgname); + } + int imgfd; + if ((imgfd = open(imgname, O_WRONLY)) < 0) { + bug("open '%s' failed.\n", imgname); + } + return create_sfs(imgfd); +} + +#define open_bug(sfs, name, ...) \ + do { \ + subpath_show(stderr, sfs, name); \ + bug(__VA_ARGS__); \ + } while (0) + +#define show_fullpath(sfs, name) subpath_show(stderr, sfs, name) + +void open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent); +void open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd); +void open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename); + +#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t)) +#define SFS_L0_NBLKS SFS_NDIRECT +#define SFS_L1_NBLKS (SFS_BLK_NENTRY + SFS_L0_NBLKS) +#define SFS_L2_NBLKS (SFS_BLK_NENTRY * SFS_BLK_NENTRY + SFS_L1_NBLKS) +#define SFS_LN_NBLKS (SFS_MAX_FILE_SIZE / SFS_BLKSIZE) + +static void +update_cache(struct sfs_fs *sfs, struct cache_block **cbp, uint32_t *inop) { + uint32_t ino = *inop; + struct cache_block *cb = *cbp; + if (ino == 0) { + cb = alloc_cache_block(sfs, 0); + ino = cb->ino; + } + else if (cb == NULL || cb->ino != ino) { + cb = search_cache_block(sfs, ino); + assert(cb != NULL && cb->ino == ino); + } + *cbp = cb, *inop = ino; +} + +static void +append_block(struct sfs_fs *sfs, struct cache_inode *file, size_t size, uint32_t ino, const char *filename) { + static_assert(SFS_LN_NBLKS <= SFS_L2_NBLKS); + assert(size <= SFS_BLKSIZE); + uint32_t nblks = file->nblks; + struct inode *inode = &(file->inode); + if (nblks >= SFS_LN_NBLKS) { + open_bug(sfs, filename, "file is too big.\n"); + } + if (nblks < SFS_L0_NBLKS) { + inode->direct[nblks] = ino; + } + else if (nblks < SFS_L1_NBLKS) { + nblks -= SFS_L0_NBLKS; + update_cache(sfs, &(file->l1), &(inode->indirect)); + uint32_t *data = file->l1->cache; + data[nblks] = ino; + } + else if (nblks < SFS_L2_NBLKS) { + nblks -= SFS_L1_NBLKS; + update_cache(sfs, &(file->l2), &(inode->db_indirect)); + uint32_t *data2 = file->l2->cache; + update_cache(sfs, &(file->l1), &data2[nblks / SFS_BLK_NENTRY]); + uint32_t *data1 = file->l1->cache; + data1[nblks % SFS_BLK_NENTRY] = ino; + } + file->nblks ++; + inode->size += size; + inode->blocks ++; +} + +static void +add_entry(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *file, const char *name) { + static struct sfs_entry __entry, *entry = &__entry; + assert(current->inode.type == SFS_TYPE_DIR && strlen(name) <= SFS_MAX_FNAME_LEN); + entry->ino = file->ino, strcpy(entry->name, name); + uint32_t entry_ino = sfs_alloc_ino(sfs); + write_block(sfs, entry, sizeof(entry->name), entry_ino); + append_block(sfs, current, sizeof(entry->name), entry_ino, name); + file->inode.nlinks ++; +} + +static void +add_dir(struct sfs_fs *sfs, struct cache_inode *parent, const char *dirname, int curfd, int fd, ino_t real) { + assert(search_cache_inode(sfs, real) == NULL); + struct cache_inode *current = alloc_cache_inode(sfs, real, 0, SFS_TYPE_DIR); + safe_fchdir(fd), subpath_push(sfs, dirname); + open_dir(sfs, current, parent); + safe_fchdir(curfd), subpath_pop(sfs); + add_entry(sfs, parent, current, dirname); +} + +static void +add_file(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, int fd, ino_t real) { + struct cache_inode *file; + if ((file = search_cache_inode(sfs, real)) == NULL) { + file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_FILE); + open_file(sfs, file, filename, fd); + } + add_entry(sfs, current, file, filename); +} + +static void +add_link(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, ino_t real) { + struct cache_inode *file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_LINK); + open_link(sfs, file, filename); + add_entry(sfs, current, file, filename); +} + +void +open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent) { + DIR *dir; + if ((dir = opendir(".")) == NULL) { + open_bug(sfs, NULL, "opendir failed.\n"); + } + add_entry(sfs, current, current, "."); + add_entry(sfs, current, parent, ".."); + struct dirent *direntp; + while ((direntp = readdir(dir)) != NULL) { + const char *name = direntp->d_name; + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue ; + } + if (name[0] == '.') { + continue ; + } + if (strlen(name) > SFS_MAX_FNAME_LEN) { + open_bug(sfs, NULL, "file name is too long: %s\n", name); + } + struct stat *stat = safe_lstat(name); + if (S_ISLNK(stat->st_mode)) { + add_link(sfs, current, name, stat->st_ino); + } + else { + int fd; + if ((fd = open(name, O_RDONLY)) < 0) { + open_bug(sfs, NULL, "open failed: %s\n", name); + } + if (S_ISDIR(stat->st_mode)) { + add_dir(sfs, current, name, dirfd(dir), fd, stat->st_ino); + } + else if (S_ISREG(stat->st_mode)) { + add_file(sfs, current, name, fd, stat->st_ino); + } + else { + char mode = '?'; + if (S_ISFIFO(stat->st_mode)) mode = 'f'; + if (S_ISSOCK(stat->st_mode)) mode = 's'; + if (S_ISCHR(stat->st_mode)) mode = 'c'; + if (S_ISBLK(stat->st_mode)) mode = 'b'; + show_fullpath(sfs, NULL); + warn("unsupported mode %07x (%c): file %s\n", stat->st_mode, mode, name); + } + close(fd); + } + } + closedir(dir); +} + +void +open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd) { + static char buffer[SFS_BLKSIZE]; + ssize_t ret, last = SFS_BLKSIZE; + while ((ret = read(fd, buffer, sizeof(buffer))) != 0) { + assert(last == SFS_BLKSIZE); + uint32_t ino = sfs_alloc_ino(sfs); + write_block(sfs, buffer, ret, ino); + append_block(sfs, file, ret, ino, filename); + last = ret; + } + if (ret < 0) { + open_bug(sfs, filename, "read file failed.\n"); + } +} + +void +open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename) { + static char buffer[SFS_BLKSIZE]; + uint32_t ino = sfs_alloc_ino(sfs); + ssize_t ret = readlink(filename, buffer, sizeof(buffer)); + if (ret < 0 || ret == SFS_BLKSIZE) { + open_bug(sfs, filename, "read link failed, %d", (int)ret); + } + write_block(sfs, buffer, ret, ino); + append_block(sfs, file, ret, ino, filename); +} + +int +create_img(struct sfs_fs *sfs, const char *home) { + int curfd, homefd; + if ((curfd = open(".", O_RDONLY)) < 0) { + bug("get current fd failed.\n"); + } + if ((homefd = open(home, O_RDONLY | O_NOFOLLOW)) < 0) { + bug("open home directory '%s' failed.\n", home); + } + safe_fchdir(homefd); + open_dir(sfs, sfs->root, sfs->root); + safe_fchdir(curfd); + close(curfd), close(homefd); + close_sfs(sfs); + return 0; +} + +static void +static_check(void) { + static_assert(sizeof(off_t) == 8); + static_assert(sizeof(ino_t) == 8); + static_assert(SFS_MAX_NBLKS <= 0x80000000UL); + static_assert(SFS_MAX_FILE_SIZE <= 0x80000000UL); +} + +int +main(int argc, char **argv) { + static_check(); + if (argc != 3) { + bug("usage: \n"); + } + const char *imgname = argv[1], *home = argv[2]; + if (create_img(open_img(imgname), home) != 0) { + bug("create img failed.\n"); + } + printf("create %s (%s) successfully.\n", imgname, home); + return 0; +} + diff --git a/lab8_practice/tools/sign.c b/lab8_practice/tools/sign.c new file mode 100644 index 0000000..9d81bb6 --- /dev/null +++ b/lab8_practice/tools/sign.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + struct stat st; + if (argc != 3) { + fprintf(stderr, "Usage: \n"); + return -1; + } + if (stat(argv[1], &st) != 0) { + fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno)); + return -1; + } + printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size); + if (st.st_size > 510) { + fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size); + return -1; + } + char buf[512]; + memset(buf, 0, sizeof(buf)); + FILE *ifp = fopen(argv[1], "rb"); + int size = fread(buf, 1, st.st_size, ifp); + if (size != st.st_size) { + fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size); + return -1; + } + fclose(ifp); + buf[510] = 0x55; + buf[511] = 0xAA; + FILE *ofp = fopen(argv[2], "wb+"); + size = fwrite(buf, 1, 512, ofp); + if (size != 512) { + fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size); + return -1; + } + fclose(ofp); + printf("build 512 bytes boot sector: '%s' success!\n", argv[2]); + return 0; +} + diff --git a/lab8_practice/tools/user.ld b/lab8_practice/tools/user.ld new file mode 100644 index 0000000..7b3c185 --- /dev/null +++ b/lab8_practice/tools/user.ld @@ -0,0 +1,39 @@ +/* Simple linker script for ucore user-level programs. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS { + /* Load programs at this address: "." means the current address */ + . = 0x800020; + + .text : { + *(.text .stub .text.* .gnu.linkonce.t.*) + } + + PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + /* Adjust the address for the data segment to the next page */ + . = ALIGN(0x1000); + + .data : { + *(.data) + } + + PROVIDE(edata = .); + + .bss : { + *(.bss) + } + + PROVIDE(end = .); + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack .comment) + } +} diff --git a/lab8_practice/tools/vector.c b/lab8_practice/tools/vector.c new file mode 100644 index 0000000..e24d77e --- /dev/null +++ b/lab8_practice/tools/vector.c @@ -0,0 +1,29 @@ +#include + +int +main(void) { + printf("# handler\n"); + printf(".text\n"); + printf(".globl __alltraps\n"); + + int i; + for (i = 0; i < 256; i ++) { + printf(".globl vector%d\n", i); + printf("vector%d:\n", i); + if ((i < 8 || i > 14) && i != 17) { + printf(" pushl $0\n"); + } + printf(" pushl $%d\n", i); + printf(" jmp __alltraps\n"); + } + printf("\n"); + printf("# vector table\n"); + printf(".data\n"); + printf(".globl __vectors\n"); + printf("__vectors:\n"); + for (i = 0; i < 256; i ++) { + printf(" .long vector%d\n", i); + } + return 0; +} + diff --git a/lab8_practice/user/badarg.c b/lab8_practice/user/badarg.c new file mode 100644 index 0000000..7b4ffad --- /dev/null +++ b/lab8_practice/user/badarg.c @@ -0,0 +1,22 @@ +#include +#include + +int +main(void) { + int pid, exit_code; + if ((pid = fork()) == 0) { + cprintf("fork ok.\n"); + int i; + for (i = 0; i < 10; i ++) { + yield(); + } + exit(0xbeaf); + } + assert(pid > 0); + assert(waitpid(-1, NULL) != 0); + assert(waitpid(pid, (void *)0xC0000000) != 0); + assert(waitpid(pid, &exit_code) == 0 && exit_code == 0xbeaf); + cprintf("badarg pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/badsegment.c b/lab8_practice/user/badsegment.c new file mode 100644 index 0000000..42f1446 --- /dev/null +++ b/lab8_practice/user/badsegment.c @@ -0,0 +1,11 @@ +#include +#include + +/* try to load the kernel's TSS selector into the DS register */ + +int +main(void) { + // asm volatile("movw $0x28,%ax; movw %ax,%ds"); + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/divzero.c b/lab8_practice/user/divzero.c new file mode 100644 index 0000000..16c23d5 --- /dev/null +++ b/lab8_practice/user/divzero.c @@ -0,0 +1,11 @@ +#include +#include + +int zero; + +int +main(void) { + cprintf("value is %d.\n", 1 / zero); + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/exit.c b/lab8_practice/user/exit.c new file mode 100644 index 0000000..c3ac5f8 --- /dev/null +++ b/lab8_practice/user/exit.c @@ -0,0 +1,34 @@ +#include +#include + +int magic = -0x10384; + +int +main(void) { + int pid, code; + cprintf("I am the parent. Forking the child...\n"); + if ((pid = fork()) == 0) { + cprintf("I am the child.\n"); + yield(); + yield(); + yield(); + yield(); + yield(); + yield(); + yield(); + exit(magic); + } + else { + cprintf("I am parent, fork a child pid %d\n",pid); + } + assert(pid > 0); + cprintf("I am the parent, waiting now..\n"); + + assert(waitpid(pid, &code) == 0 && code == magic); + assert(waitpid(pid, &code) != 0 && wait() != 0); + cprintf("waitpid %d ok.\n", pid); + + cprintf("exit pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/faultread.c b/lab8_practice/user/faultread.c new file mode 100644 index 0000000..6d292e1 --- /dev/null +++ b/lab8_practice/user/faultread.c @@ -0,0 +1,9 @@ +#include +#include + +int +main(void) { + cprintf("I read %8x from 0.\n", *(unsigned int *)0); + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/faultreadkernel.c b/lab8_practice/user/faultreadkernel.c new file mode 100644 index 0000000..53457f6 --- /dev/null +++ b/lab8_practice/user/faultreadkernel.c @@ -0,0 +1,9 @@ +#include +#include + +int +main(void) { + cprintf("I read %08x from 0xfac00000!\n", *(unsigned *)0xfac00000); + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/forktest.c b/lab8_practice/user/forktest.c new file mode 100644 index 0000000..3eda228 --- /dev/null +++ b/lab8_practice/user/forktest.c @@ -0,0 +1,34 @@ +#include +#include + +const int max_child = 32; + +int +main(void) { + int n, pid; + for (n = 0; n < max_child; n ++) { + if ((pid = fork()) == 0) { + cprintf("I am child %d\n", n); + exit(0); + } + assert(pid > 0); + } + + if (n > max_child) { + panic("fork claimed to work %d times!\n", n); + } + + for (; n > 0; n --) { + if (wait() != 0) { + panic("wait stopped early\n"); + } + } + + if (wait() == 0) { + panic("wait got too many\n"); + } + + cprintf("forktest pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/forktree.c b/lab8_practice/user/forktree.c new file mode 100644 index 0000000..93f28bb --- /dev/null +++ b/lab8_practice/user/forktree.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#define DEPTH 4 + +void forktree(const char *cur); + +void +forkchild(const char *cur, char branch) { + char nxt[DEPTH + 1]; + + if (strlen(cur) >= DEPTH) + return; + + snprintf(nxt, DEPTH + 1, "%s%c", cur, branch); + if (fork() == 0) { + forktree(nxt); + yield(); + exit(0); + } +} + +void +forktree(const char *cur) { + cprintf("%04x: I am '%s'\n", getpid(), cur); + + forkchild(cur, '0'); + forkchild(cur, '1'); +} + +int +main(void) { + forktree(""); + return 0; +} + diff --git a/lab8_practice/user/hello.c b/lab8_practice/user/hello.c new file mode 100644 index 0000000..0f05251 --- /dev/null +++ b/lab8_practice/user/hello.c @@ -0,0 +1,11 @@ +#include +#include + +int +main(void) { + cprintf("Hello world!!.\n"); + cprintf("I am process %d.\n", getpid()); + cprintf("hello pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/libs/dir.c b/lab8_practice/user/libs/dir.c new file mode 100644 index 0000000..62d926e --- /dev/null +++ b/lab8_practice/user/libs/dir.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DIR dir, *dirp=&dir; +DIR * +opendir(const char *path) { + + if ((dirp->fd = open(path, O_RDONLY)) < 0) { + goto failed; + } + struct stat __stat, *stat = &__stat; + if (fstat(dirp->fd, stat) != 0 || !S_ISDIR(stat->st_mode)) { + goto failed; + } + dirp->dirent.offset = 0; + return dirp; + +failed: + return NULL; +} + +struct dirent * +readdir(DIR *dirp) { + if (sys_getdirentry(dirp->fd, &(dirp->dirent)) == 0) { + return &(dirp->dirent); + } + return NULL; +} + +void +closedir(DIR *dirp) { + close(dirp->fd); +} + +int +getcwd(char *buffer, size_t len) { + return sys_getcwd(buffer, len); +} + diff --git a/lab8_practice/user/libs/dir.h b/lab8_practice/user/libs/dir.h new file mode 100644 index 0000000..4862492 --- /dev/null +++ b/lab8_practice/user/libs/dir.h @@ -0,0 +1,19 @@ +#ifndef __USER_LIBS_DIR_H__ +#define __USER_LIBS_DIR_H__ + +#include +#include + +typedef struct { + int fd; + struct dirent dirent; +} DIR; + +DIR *opendir(const char *path); +struct dirent *readdir(DIR *dirp); +void closedir(DIR *dirp); +int chdir(const char *path); +int getcwd(char *buffer, size_t len); + +#endif /* !__USER_LIBS_DIR_H__ */ + diff --git a/lab8_practice/user/libs/file.c b/lab8_practice/user/libs/file.c new file mode 100644 index 0000000..113e078 --- /dev/null +++ b/lab8_practice/user/libs/file.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include + +int +open(const char *path, uint32_t open_flags) { + return sys_open(path, open_flags); +} + +int +close(int fd) { + return sys_close(fd); +} + +int +read(int fd, void *base, size_t len) { + return sys_read(fd, base, len); +} + +int +write(int fd, void *base, size_t len) { + return sys_write(fd, base, len); +} + +int +seek(int fd, off_t pos, int whence) { + return sys_seek(fd, pos, whence); +} + +int +fstat(int fd, struct stat *stat) { + return sys_fstat(fd, stat); +} + +int +fsync(int fd) { + return sys_fsync(fd); +} + +int +dup2(int fd1, int fd2) { + return sys_dup(fd1, fd2); +} + +static char +transmode(struct stat *stat) { + uint32_t mode = stat->st_mode; + if (S_ISREG(mode)) return 'r'; + if (S_ISDIR(mode)) return 'd'; + if (S_ISLNK(mode)) return 'l'; + if (S_ISCHR(mode)) return 'c'; + if (S_ISBLK(mode)) return 'b'; + return '-'; +} + +void +print_stat(const char *name, int fd, struct stat *stat) { + cprintf("[%03d] %s\n", fd, name); + cprintf(" mode : %c\n", transmode(stat)); + cprintf(" links : %lu\n", stat->st_nlinks); + cprintf(" blocks : %lu\n", stat->st_blocks); + cprintf(" size : %lu\n", stat->st_size); +} + diff --git a/lab8_practice/user/libs/file.h b/lab8_practice/user/libs/file.h new file mode 100644 index 0000000..fc46fb5 --- /dev/null +++ b/lab8_practice/user/libs/file.h @@ -0,0 +1,23 @@ +#ifndef __USER_LIBS_FILE_H__ +#define __USER_LIBS_FILE_H__ + +#include + +struct stat; + +int open(const char *path, uint32_t open_flags); +int close(int fd); +int read(int fd, void *base, size_t len); +int write(int fd, void *base, size_t len); +int seek(int fd, off_t pos, int whence); +int fstat(int fd, struct stat *stat); +int fsync(int fd); +int dup(int fd); +int dup2(int fd1, int fd2); +int pipe(int *fd_store); +int mkfifo(const char *name, uint32_t open_flags); + +void print_stat(const char *name, int fd, struct stat *stat); + +#endif /* !__USER_LIBS_FILE_H__ */ + diff --git a/lab8_practice/user/libs/initcode.S b/lab8_practice/user/libs/initcode.S new file mode 100644 index 0000000..6d122c3 --- /dev/null +++ b/lab8_practice/user/libs/initcode.S @@ -0,0 +1,13 @@ +.text +.globl _start +_start: + # set ebp for backtrace + // movl $0x0, %ebp + + # move down the esp register + # since it may cause page fault in backtrace + // subl $0x20, %esp + + # call user-program function + call umain +1: j 1b diff --git a/lab8_practice/user/libs/lock.h b/lab8_practice/user/libs/lock.h new file mode 100644 index 0000000..e221c12 --- /dev/null +++ b/lab8_practice/user/libs/lock.h @@ -0,0 +1,42 @@ +#ifndef __USER_LIBS_LOCK_H__ +#define __USER_LIBS_LOCK_H__ + +#include +#include +#include + +#define INIT_LOCK {0} + +typedef volatile bool lock_t; + +static inline void +lock_init(lock_t *l) { + *l = 0; +} + +static inline bool +try_lock(lock_t *l) { + return test_and_set_bit(0, l); +} + +static inline void +lock(lock_t *l) { + if (try_lock(l)) { + int step = 0; + do { + yield(); + if (++ step == 100) { + step = 0; + sleep(10); + } + } while (try_lock(l)); + } +} + +static inline void +unlock(lock_t *l) { + test_and_clear_bit(0, l); +} + +#endif /* !__USER_LIBS_LOCK_H__ */ + diff --git a/lab8_practice/user/libs/panic.c b/lab8_practice/user/libs/panic.c new file mode 100644 index 0000000..783be16 --- /dev/null +++ b/lab8_practice/user/libs/panic.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +void +__panic(const char *file, int line, const char *fmt, ...) { + // print the 'message' + va_list ap; + va_start(ap, fmt); + cprintf("user panic at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); + exit(-E_PANIC); +} + +void +__warn(const char *file, int line, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + cprintf("user warning at %s:%d:\n ", file, line); + vcprintf(fmt, ap); + cprintf("\n"); + va_end(ap); +} + diff --git a/lab8_practice/user/libs/stdio.c b/lab8_practice/user/libs/stdio.c new file mode 100644 index 0000000..56c5499 --- /dev/null +++ b/lab8_practice/user/libs/stdio.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +/* * + * cputch - writes a single character @c to stdout, and it will + * increace the value of counter pointed by @cnt. + * */ +static void +cputch(int c, int *cnt) { + sys_putc(c); + (*cnt) ++; +} + +/* * + * vcprintf - format a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * + * Call this function if you are already dealing with a va_list. + * Or you probably want cprintf() instead. + * */ +int +vcprintf(const char *fmt, va_list ap) { + int cnt = 0; + vprintfmt((void*)cputch, NO_FD, &cnt, fmt, ap); + return cnt; +} + +/* * + * cprintf - formats a string and writes it to stdout + * + * The return value is the number of characters which would be + * written to stdout. + * */ +int +cprintf(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + int cnt = vcprintf(fmt, ap); + va_end(ap); + + return cnt; +} + +/* * + * cputs- writes the string pointed by @str to stdout and + * appends a newline character. + * */ +int +cputs(const char *str) { + int cnt = 0; + char c; + while ((c = *str ++) != '\0') { + cputch(c, &cnt); + } + cputch('\n', &cnt); + return cnt; +} + + +static void +fputch(char c, int *cnt, int fd) { + write(fd, &c, sizeof(char)); + (*cnt) ++; +} + +int +vfprintf(int fd, const char *fmt, va_list ap) { + int cnt = 0; + vprintfmt((void*)fputch, fd, &cnt, fmt, ap); + return cnt; +} + +int +fprintf(int fd, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + int cnt = vfprintf(fd, fmt, ap); + va_end(ap); + + return cnt; +} diff --git a/lab8_practice/user/libs/syscall.c b/lab8_practice/user/libs/syscall.c new file mode 100644 index 0000000..a51de90 --- /dev/null +++ b/lab8_practice/user/libs/syscall.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include + + +#define MAX_ARGS 5 + +static inline int +syscall(uint64_t num, ...) { + va_list ap; + va_start(ap, num); + uint64_t a[MAX_ARGS]; + int i, ret; + for (i = 0; i < MAX_ARGS; i ++) { + a[i] = va_arg(ap, uint64_t); + } + va_end(ap); + + asm volatile ( + "lw a0, %1\n" + "lw a1, %2\n" + "lw a2, %3\n" + "lw a3, %4\n" + "lw a4, %5\n" + "lw a5, %6\n" + "ecall\n" + "sw a0, %0" + : "=m" (ret) + : "m" (num), + "m" (a[0]), + "m" (a[1]), + "m" (a[2]), + "m" (a[3]), + "m" (a[4]) + : "memory" + ); + return ret; +} + +int +sys_exit(int64_t error_code) { + return syscall(SYS_exit, error_code); +} + +int +sys_fork(void) { + return syscall(SYS_fork); +} + +int +sys_wait(int64_t pid, int64_t *store) { + return syscall(SYS_wait, pid, store); +} + +int +sys_yield(void) { + return syscall(SYS_yield); +} + +int +sys_kill(int64_t pid) { + return syscall(SYS_kill, pid); +} + +int +sys_getpid(void) { + return syscall(SYS_getpid); +} + +int +sys_putc(int64_t c) { + return syscall(SYS_putc, c); +} + +int +sys_pgdir(void) { + return syscall(SYS_pgdir); +} + +void +sys_lab6_set_priority(uint64_t priority) +{ + syscall(SYS_lab6_set_priority, priority); +} + +int +sys_sleep(int64_t time) { + return syscall(SYS_sleep, time); +} + +int +sys_gettime(void) { + return syscall(SYS_gettime); +} + +int +sys_exec(const char *name, int64_t argc, const char **argv) { + return syscall(SYS_exec, name, argc, argv); +} + +int +sys_open(const char *path, uint64_t open_flags) { + return syscall(SYS_open, path, open_flags); +} + +int +sys_close(int64_t fd) { + return syscall(SYS_close, fd); +} + +int +sys_read(int64_t fd, void *base, size_t len) { + return syscall(SYS_read, fd, base, len); +} + +int +sys_write(int64_t fd, void *base, size_t len) { + return syscall(SYS_write, fd, base, len); +} + +int +sys_seek(int64_t fd, off_t pos, int64_t whence) { + return syscall(SYS_seek, fd, pos, whence); +} + +int +sys_fstat(int64_t fd, struct stat *stat) { + return syscall(SYS_fstat, fd, stat); +} + +int +sys_fsync(int64_t fd) { + return syscall(SYS_fsync, fd); +} + +int +sys_getcwd(char *buffer, size_t len) { + return syscall(SYS_getcwd, buffer, len); +} + +int +sys_getdirentry(int64_t fd, struct dirent *dirent) { + return syscall(SYS_getdirentry, fd, dirent); +} + +int +sys_dup(int64_t fd1, int64_t fd2) { + return syscall(SYS_dup, fd1, fd2); +} diff --git a/lab8_practice/user/libs/syscall.h b/lab8_practice/user/libs/syscall.h new file mode 100644 index 0000000..cb8f12b --- /dev/null +++ b/lab8_practice/user/libs/syscall.h @@ -0,0 +1,33 @@ +#ifndef __USER_LIBS_SYSCALL_H__ +#define __USER_LIBS_SYSCALL_H__ + +int sys_exit(int64_t error_code); +int sys_fork(void); +int sys_wait(int64_t pid, int64_t *store); +int sys_exec(const char *name, int64_t argc, const char **argv); +int sys_yield(void); +int sys_kill(int64_t pid); +int sys_getpid(void); +int sys_putc(int64_t c); +int sys_pgdir(void); +int sys_sleep(int64_t time); +int sys_gettime(void); + +struct stat; +struct dirent; + +int sys_open(const char *path, uint64_t open_flags); +int sys_close(int64_t fd); +int sys_read(int64_t fd, void *base, size_t len); +int sys_write(int64_t fd, void *base, size_t len); +int sys_seek(int64_t fd, off_t pos, int64_t whence); +int sys_fstat(int64_t fd, struct stat *stat); +int sys_fsync(int64_t fd); +int sys_getcwd(char *buffer, size_t len); +int sys_getdirentry(int64_t fd, struct dirent *dirent); +int sys_dup(int64_t fd1, int64_t fd2); +void sys_lab6_set_priority(uint64_t priority); //only for lab6 + + +#endif /* !__USER_LIBS_SYSCALL_H__ */ + diff --git a/lab8_practice/user/libs/ulib.c b/lab8_practice/user/libs/ulib.c new file mode 100644 index 0000000..4b25c73 --- /dev/null +++ b/lab8_practice/user/libs/ulib.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +void +exit(int error_code) { + sys_exit(error_code); + cprintf("BUG: exit failed.\n"); + while (1); +} + +int +fork(void) { + return sys_fork(); +} + +int +wait(void) { + return sys_wait(0, NULL); +} + +int +waitpid(int pid, int *store) { + return sys_wait(pid, store); +} + +void +yield(void) { + sys_yield(); +} + +int +kill(int pid) { + return sys_kill(pid); +} + +int +getpid(void) { + return sys_getpid(); +} + +//print_pgdir - print the PDT&PT +void +print_pgdir(void) { + sys_pgdir(); +} + +unsigned int +gettime_msec(void) { + return (unsigned int)sys_gettime(); +} + +void +lab6_set_priority(uint32_t priority) +{ + sys_lab6_set_priority(priority); +} + +int +sleep(unsigned int time) { + return sys_sleep(time); +} +int +__exec(const char *name, const char **argv) { + int argc = 0; + while (argv[argc] != NULL) { + argc ++; + } + return sys_exec(name, argc, argv); +} diff --git a/lab8_practice/user/libs/ulib.h b/lab8_practice/user/libs/ulib.h new file mode 100644 index 0000000..97c8133 --- /dev/null +++ b/lab8_practice/user/libs/ulib.h @@ -0,0 +1,40 @@ +#ifndef __USER_LIBS_ULIB_H__ +#define __USER_LIBS_ULIB_H__ + +#include + +void __warn(const char *file, int line, const char *fmt, ...); +void __noreturn __panic(const char *file, int line, const char *fmt, ...); + +#define warn(...) \ + __warn(__FILE__, __LINE__, __VA_ARGS__) + +#define panic(...) \ + __panic(__FILE__, __LINE__, __VA_ARGS__) + +#define assert(x) \ + do { \ + if (!(x)) { \ + panic("assertion failed: %s", #x); \ + } \ + } while (0) + +// static_assert(x) will generate a compile-time error if 'x' is false. +#define static_assert(x) \ + switch (x) { case 0: case (x): ; } + +void __noreturn exit(int error_code); +int fork(void); +int wait(void); +int waitpid(int pid, int *store); +void yield(void); +int kill(int pid); +int getpid(void); +void print_pgdir(void); +unsigned int gettime_msec(void); +void lab6_set_priority(uint32_t priority); +int sleep(unsigned int time); +int fprintf(int fd, const char *fmt, ...); +int __exec(const char *name, const char **argv); +#endif /* !__USER_LIBS_ULIB_H__ */ + diff --git a/lab8_practice/user/libs/umain.c b/lab8_practice/user/libs/umain.c new file mode 100644 index 0000000..9bee9ab --- /dev/null +++ b/lab8_practice/user/libs/umain.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +int main(int argc, char *argv[]); + +static int +initfd(int fd2, const char *path, uint32_t open_flags) { + int fd1, ret; + if ((fd1 = open(path, open_flags)) < 0) { + return fd1; + } + if (fd1 != fd2) { + close(fd2); + ret = dup2(fd1, fd2); + close(fd1); + } + return ret; +} + +void +umain(int argc, char *argv[]) { + int fd; + if ((fd = initfd(0, "stdin:", O_RDONLY)) < 0) { + warn("open failed: %e.\n", fd); + } + if ((fd = initfd(1, "stdout:", O_WRONLY)) < 0) { + warn("open failed: %e.\n", fd); + } + int ret = main(argc, argv); + exit(ret); +} + diff --git a/lab8_practice/user/matrix.c b/lab8_practice/user/matrix.c new file mode 100644 index 0000000..c5ec869 --- /dev/null +++ b/lab8_practice/user/matrix.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include + +#define MATSIZE 10 + +static int mata[MATSIZE][MATSIZE]; +static int matb[MATSIZE][MATSIZE]; +static int matc[MATSIZE][MATSIZE]; + +void +work(unsigned int times) { + int i, j, k, size = MATSIZE; + for (i = 0; i < size; i ++) { + for (j = 0; j < size; j ++) { + mata[i][j] = matb[i][j] = 1; + } + } + + yield(); + + cprintf("pid %d is running (%d times)!.\n", getpid(), times); + + while (times -- > 0) { + for (i = 0; i < size; i ++) { + for (j = 0; j < size; j ++) { + matc[i][j] = 0; + for (k = 0; k < size; k ++) { + matc[i][j] += mata[i][k] * matb[k][j]; + } + } + } + for (i = 0; i < size; i ++) { + for (j = 0; j < size; j ++) { + mata[i][j] = matb[i][j] = matc[i][j]; + } + } + } + cprintf("pid %d done!.\n", getpid()); + exit(0); +} + +const int total = 21; + +int +main(void) { + int pids[total]; + memset(pids, 0, sizeof(pids)); + + int i; + for (i = 0; i < total; i ++) { + if ((pids[i] = fork()) == 0) { + srand(i * i); + int times = (((unsigned int)rand()) % total); + times = (times * times + 10) * 100; + work(times); + } + if (pids[i] < 0) { + goto failed; + } + } + + cprintf("fork ok.\n"); + + for (i = 0; i < total; i ++) { + if (wait() != 0) { + cprintf("wait failed.\n"); + goto failed; + } + } + + cprintf("matrix pass.\n"); + return 0; + +failed: + for (i = 0; i < total; i ++) { + if (pids[i] > 0) { + kill(pids[i]); + } + } + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/pgdir.c b/lab8_practice/user/pgdir.c new file mode 100644 index 0000000..09fd7e3 --- /dev/null +++ b/lab8_practice/user/pgdir.c @@ -0,0 +1,11 @@ +#include +#include + +int +main(void) { + cprintf("I am %d, print pgdir.\n", getpid()); + print_pgdir(); + cprintf("pgdir pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/priority.c b/lab8_practice/user/priority.c new file mode 100644 index 0000000..96373fa --- /dev/null +++ b/lab8_practice/user/priority.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +#define TOTAL 5 +/* to get enough accuracy, MAX_TIME (the running time of each process) should >1000 mseconds. */ +#define MAX_TIME 10000 +unsigned int acc[TOTAL]; +int status[TOTAL]; +int pids[TOTAL]; + +static void +spin_delay(void) +{ + int i; + volatile int j; + for (i = 0; i != 200; ++ i) + { + j = !j; + } +} + +int +main(void) { + int i,time; + memset(pids, 0, sizeof(pids)); + lab6_set_priority(TOTAL + 1); + + for (i = 0; i < TOTAL; i ++) { + acc[i]=0; + if ((pids[i] = fork()) == 0) { + lab6_set_priority(i + 1); + acc[i] = 0; + while (1) { + spin_delay(); + ++ acc[i]; + if(acc[i]%4000==0) { + if((time=gettime_msec())>MAX_TIME) { + cprintf("child pid %d, acc %d, time %d\n",getpid(),acc[i],time); + exit(acc[i]); + } + } + } + + } + if (pids[i] < 0) { + goto failed; + } + } + + cprintf("main: fork ok,now need to wait pids.\n"); + + for (i = 0; i < TOTAL; i ++) { + status[i]=0; + waitpid(pids[i],&status[i]); + cprintf("main: pid %d, acc %d, time %d\n",pids[i],status[i],gettime_msec()); + } + cprintf("main: wait pids over\n"); + cprintf("stride sched correct result:"); + for (i = 0; i < TOTAL; i ++) + { + cprintf(" %d", (status[i] * 2 / status[0] + 1) / 2); + } + cprintf("\n"); + + return 0; + +failed: + for (i = 0; i < TOTAL; i ++) { + if (pids[i] > 0) { + kill(pids[i]); + } + } + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/sh.c b/lab8_practice/user/sh.c new file mode 100644 index 0000000..d63d71c --- /dev/null +++ b/lab8_practice/user/sh.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include + +#define printf(...) fprintf(1, __VA_ARGS__) +#define putc(c) printf("%c", c) + +#define BUFSIZE 4096 +#define WHITESPACE " \t\r\n" +#define SYMBOLS "<|>&;" + +char shcwd[BUFSIZE]; + +int +gettoken(char **p1, char **p2) { + char *s; + if ((s = *p1) == NULL) { + return 0; + } + while (strchr(WHITESPACE, *s) != NULL) { + *s ++ = '\0'; + } + if (*s == '\0') { + return 0; + } + + *p2 = s; + int token = 'w'; + if (strchr(SYMBOLS, *s) != NULL) { + token = *s, *s ++ = '\0'; + } + else { + bool flag = 0; + while (*s != '\0' && (flag || strchr(WHITESPACE SYMBOLS, *s) == NULL)) { + if (*s == '"') { + *s = ' ', flag = !flag; + } + s ++; + } + } + *p1 = (*s != '\0' ? s : NULL); + return token; +} + +char * +readline(const char *prompt) { + static char buffer[BUFSIZE]; + if (prompt != NULL) { + printf("%s", prompt); + } + int ret, i = 0; + while (1) { + char c; + if ((ret = read(0, &c, sizeof(char))) < 0) { + return NULL; + } + else if (ret == 0) { + if (i > 0) { + buffer[i] = '\0'; + break; + } + return NULL; + } + + if (c == 3) { + return NULL; + } + else if (c >= ' ' && i < BUFSIZE - 1) { + putc(c); + buffer[i ++] = c; + } + else if (c == '\b' && i > 0) { + putc(c); + i --; + } + else if (c == '\n' || c == '\r') { + putc(c); + buffer[i] = '\0'; + break; + } + } + return buffer; +} + +void +usage(void) { + printf("usage: sh [command-file]\n"); +} + +int +reopen(int fd2, const char *filename, uint32_t open_flags) { + int ret, fd1; + close(fd2); + if ((ret = open(filename, open_flags)) >= 0 && ret != fd2) { + close(fd2); + fd1 = ret, ret = dup2(fd1, fd2); + close(fd1); + } + return ret < 0 ? ret : 0; +} + +int +testfile(const char *name) { + int ret; + if ((ret = open(name, O_RDONLY)) < 0) { + return ret; + } + close(ret); + return 0; +} + +int +runcmd(char *cmd) { + static char argv0[BUFSIZE]; + static const char *argv[EXEC_MAX_ARG_NUM + 1];//must be static! + char *t; + int argc, token, ret, p[2]; +again: + argc = 0; + while (1) { + switch (token = gettoken(&cmd, &t)) { + case 'w': + if (argc == EXEC_MAX_ARG_NUM) { + printf("sh error: too many arguments\n"); + return -1; + } + argv[argc ++] = t; + break; + case '<': + if (gettoken(&cmd, &t) != 'w') { + printf("sh error: syntax error: < not followed by word\n"); + return -1; + } + if ((ret = reopen(0, t, O_RDONLY)) != 0) { + return ret; + } + break; + case '>': + if (gettoken(&cmd, &t) != 'w') { + printf("sh error: syntax error: > not followed by word\n"); + return -1; + } + if ((ret = reopen(1, t, O_RDWR | O_TRUNC | O_CREAT)) != 0) { + return ret; + } + break; + case '|': + // if ((ret = pipe(p)) != 0) { + // return ret; + // } + if ((ret = fork()) == 0) { + close(0); + if ((ret = dup2(p[0], 0)) < 0) { + return ret; + } + close(p[0]), close(p[1]); + goto again; + } + else { + if (ret < 0) { + return ret; + } + close(1); + if ((ret = dup2(p[1], 1)) < 0) { + return ret; + } + close(p[0]), close(p[1]); + goto runit; + } + break; + case 0: + goto runit; + case ';': + if ((ret = fork()) == 0) { + goto runit; + } + else { + if (ret < 0) { + return ret; + } + waitpid(ret, NULL); + goto again; + } + break; + default: + printf("sh error: bad return %d from gettoken\n", token); + return -1; + } + } + +runit: + if (argc == 0) { + return 0; + } + else if (strcmp(argv[0], "cd") == 0) { + if (argc != 2) { + return -1; + } + strcpy(shcwd, argv[1]); + return 0; + } + if ((ret = testfile(argv[0])) != 0) { + if (ret != -E_NOENT) { + return ret; + } + snprintf(argv0, sizeof(argv0), "/%s", argv[0]); + argv[0] = argv0; + } + argv[argc] = NULL; + return __exec(argv[0], argv); +} + +int +main(int argc, char **argv) { + cputs("user sh is running!!!"); + int ret, interactive = 1; + if (argc == 2) { + if ((ret = reopen(0, argv[1], O_RDONLY)) != 0) { + return ret; + } + interactive = 0; + } + else if (argc > 2) { + usage(); + return -1; + } + //shcwd = malloc(BUFSIZE); + assert(shcwd != NULL); + + char *buffer; + while ((buffer = readline((interactive) ? "$ " : NULL)) != NULL) { + shcwd[0] = '\0'; + int pid; + if ((pid = fork()) == 0) { + ret = runcmd(buffer); + exit(ret); + } + assert(pid >= 0); + if (waitpid(pid, &ret) == 0) { + if (ret == 0 && shcwd[0] != '\0') { + ret = 0; + } + if (ret != 0) { + printf("error: %d - %e\n", ret, ret); + } + } + } + return 0; +} + diff --git a/lab8_practice/user/sleep.c b/lab8_practice/user/sleep.c new file mode 100644 index 0000000..5bc56e0 --- /dev/null +++ b/lab8_practice/user/sleep.c @@ -0,0 +1,28 @@ +#include +#include + +void +sleepy(int pid) { + int i, time = 100; + for (i = 0; i < 10; i ++) { + sleep(time); + cprintf("sleep %d x %d slices.\n", i + 1, time); + } + exit(0); +} + +int +main(void) { + unsigned int time = gettime_msec(); + int pid1, exit_code; + + if ((pid1 = fork()) == 0) { + sleepy(pid1); + } + + assert(waitpid(pid1, &exit_code) == 0 && exit_code == 0); + cprintf("use %04d msecs.\n", gettime_msec() - time); + cprintf("sleep pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/sleepkill.c b/lab8_practice/user/sleepkill.c new file mode 100644 index 0000000..01268a2 --- /dev/null +++ b/lab8_practice/user/sleepkill.c @@ -0,0 +1,18 @@ +#include +#include + +int +main(void) { + int pid; + if ((pid = fork()) == 0) { + sleep(~0); + exit(0xdead); + } + assert(pid > 0); + + sleep(100); + assert(kill(pid) == 0); + cprintf("sleepkill pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/softint.c b/lab8_practice/user/softint.c new file mode 100644 index 0000000..bdc9f27 --- /dev/null +++ b/lab8_practice/user/softint.c @@ -0,0 +1,10 @@ +#include +#include + +int +main(void) { + // asm volatile("int $14"); + exit(0); + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/spin.c b/lab8_practice/user/spin.c new file mode 100644 index 0000000..91581e5 --- /dev/null +++ b/lab8_practice/user/spin.c @@ -0,0 +1,29 @@ +#include +#include + +int +main(void) { + int pid, ret; + cprintf("I am the parent. Forking the child...\n"); + if ((pid = fork()) == 0) { + cprintf("I am the child. spinning ...\n"); + while (1); + } + cprintf("I am the parent. Running the child...\n"); + + yield(); + yield(); + yield(); + + cprintf("I am the parent. Killing the child...\n"); + + assert((ret = kill(pid)) == 0); + cprintf("kill returns %d\n", ret); + + assert((ret = waitpid(pid, NULL)) == 0); + cprintf("wait returns %d\n", ret); + + cprintf("spin may pass.\n"); + return 0; +} + diff --git a/lab8_practice/user/testbss.c b/lab8_practice/user/testbss.c new file mode 100644 index 0000000..211d7cd --- /dev/null +++ b/lab8_practice/user/testbss.c @@ -0,0 +1,33 @@ +#include +#include + +#define ARRAYSIZE (1024*1024) + +uint32_t bigarray[ARRAYSIZE]; + +int +main(void) { + cprintf("Making sure bss works right...\n"); + int i; + for (i = 0; i < ARRAYSIZE; i ++) { + if (bigarray[i] != 0) { + panic("bigarray[%d] isn't cleared!\n", i); + } + } + for (i = 0; i < ARRAYSIZE; i ++) { + bigarray[i] = i; + } + for (i = 0; i < ARRAYSIZE; i ++) { + if (bigarray[i] != i) { + panic("bigarray[%d] didn't hold its value!\n", i); + } + } + + cprintf("Yes, good. Now doing a wild write off the end...\n"); + cprintf("testbss may pass.\n"); + + bigarray[ARRAYSIZE + 1024] = 0; + // asm volatile ("int $0x14"); + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/waitkill.c b/lab8_practice/user/waitkill.c new file mode 100644 index 0000000..9bb3f80 --- /dev/null +++ b/lab8_practice/user/waitkill.c @@ -0,0 +1,59 @@ +#include +#include + +void +do_yield(void) { + yield(); + yield(); + yield(); + yield(); + yield(); + yield(); +} + +int parent, pid1, pid2; + +void +loop(void) { + cprintf("child 1.\n"); + while (1); +} + +void +work(void) { + cprintf("child 2.\n"); + do_yield(); + if (kill(parent) == 0) { + cprintf("kill parent ok.\n"); + do_yield(); + if (kill(pid1) == 0) { + cprintf("kill child1 ok.\n"); + exit(0); + } + } + exit(-1); +} + +int +main(void) { + parent = getpid(); + if ((pid1 = fork()) == 0) { + loop(); + } + + assert(pid1 > 0); + + if ((pid2 = fork()) == 0) { + work(); + } + if (pid2 > 0) { + cprintf("wait child 1.\n"); + waitpid(pid1, NULL); + panic("waitpid %d returns\n", pid1); + } + else { + kill(pid1); + } + panic("FAIL: T.T\n"); +} + diff --git a/lab8_practice/user/yield.c b/lab8_practice/user/yield.c new file mode 100644 index 0000000..a19890d --- /dev/null +++ b/lab8_practice/user/yield.c @@ -0,0 +1,16 @@ +#include +#include + +int +main(void) { + int i; + cprintf("Hello, I am process %d.\n", getpid()); + for (i = 0; i < 5; i ++) { + yield(); + cprintf("Back in process %d, iteration %d.\n", getpid(), i); + } + cprintf("All done in process %d.\n", getpid()); + cprintf("yield pass.\n"); + return 0; +} +