From c6927343795e7da6ca15bcb32d784cc6e7b52096 Mon Sep 17 00:00:00 2001 From: HuSharp <19973658602@163.com> Date: Thu, 24 Sep 2020 23:01:09 +0800 Subject: [PATCH] build my little os! --- "32\344\275\215\351\234\200\347\237\245" | 18 + c10/keyboard/boot/loader.S | 380 +++++++++++ c10/keyboard/boot/mbr.S | 126 ++++ c10/keyboard/build/bitmap.o | Bin 0 -> 2460 bytes c10/keyboard/build/console.o | Bin 0 -> 2424 bytes c10/keyboard/build/debug.o | Bin 0 -> 1804 bytes c10/keyboard/build/init.o | Bin 0 -> 1548 bytes c10/keyboard/build/interrupt.o | Bin 0 -> 5296 bytes c10/keyboard/build/kernel.bin | Bin 0 -> 27120 bytes c10/keyboard/build/kernel.map | 597 ++++++++++++++++++ c10/keyboard/build/kernel.o | Bin 0 -> 3232 bytes c10/keyboard/build/list.o | Bin 0 -> 2884 bytes c10/keyboard/build/loader.bin | Bin 0 -> 1396 bytes c10/keyboard/build/main.o | Bin 0 -> 2184 bytes c10/keyboard/build/mbr.bin | Bin 0 -> 512 bytes c10/keyboard/build/memory.o | Bin 0 -> 5008 bytes c10/keyboard/build/print.o | Bin 0 -> 1872 bytes c10/keyboard/build/string.o | Bin 0 -> 4000 bytes c10/keyboard/build/switch.o | Bin 0 -> 448 bytes c10/keyboard/build/sync.o | Bin 0 -> 3980 bytes c10/keyboard/build/thread.o | Bin 0 -> 6340 bytes c10/keyboard/build/timer.o | Bin 0 -> 2584 bytes c10/keyboard/device/console.c | 43 ++ c10/keyboard/device/console.h | 14 + c10/keyboard/device/timer.c | 64 ++ c10/keyboard/device/timer.h | 6 + c10/keyboard/include/boot.inc | 56 ++ c10/keyboard/kernel/debug.c | 18 + c10/keyboard/kernel/debug.h | 24 + c10/keyboard/kernel/global.h | 39 ++ c10/keyboard/kernel/init.c | 18 + c10/keyboard/kernel/init.h | 4 + c10/keyboard/kernel/interrupt.c | 192 ++++++ c10/keyboard/kernel/interrupt.h | 22 + c10/keyboard/kernel/kernel.S | 90 +++ c10/keyboard/kernel/main.c | 58 ++ c10/keyboard/kernel/memory.c | 257 ++++++++ c10/keyboard/kernel/memory.h | 33 + c10/keyboard/lib/kernel/bitmap.c | 82 +++ c10/keyboard/lib/kernel/bitmap.h | 18 + c10/keyboard/lib/kernel/io.h | 49 ++ c10/keyboard/lib/kernel/list.c | 95 +++ c10/keyboard/lib/kernel/list.h | 40 ++ c10/keyboard/lib/kernel/print.S | 239 +++++++ c10/keyboard/lib/kernel/print.h | 9 + c10/keyboard/lib/stdint.h | 11 + c10/keyboard/lib/string.c | 121 ++++ c10/keyboard/lib/string.h | 14 + c10/keyboard/makefile | 117 ++++ c10/keyboard/thread/switch.S | 37 ++ c10/keyboard/thread/sync.c | 99 +++ c10/keyboard/thread/sync.h | 30 + c10/keyboard/thread/thread.c | 202 ++++++ c10/keyboard/thread/thread.h | 108 ++++ c10/keyboard/tmp | 206 ++++++ ...3\347\240\201\347\273\223\346\236\204.txt" | 220 +++++++ c10/terminal_with_lock/boot/loader.S | 380 +++++++++++ c10/terminal_with_lock/boot/mbr.S | 126 ++++ c10/terminal_with_lock/build/bitmap.o | Bin 0 -> 2460 bytes c10/terminal_with_lock/build/console.o | Bin 0 -> 2424 bytes c10/terminal_with_lock/build/debug.o | Bin 0 -> 1804 bytes c10/terminal_with_lock/build/init.o | Bin 0 -> 1548 bytes c10/terminal_with_lock/build/interrupt.o | Bin 0 -> 5296 bytes c10/terminal_with_lock/build/kernel.bin | Bin 0 -> 27120 bytes c10/terminal_with_lock/build/kernel.map | 597 ++++++++++++++++++ c10/terminal_with_lock/build/kernel.o | Bin 0 -> 3232 bytes c10/terminal_with_lock/build/list.o | Bin 0 -> 2884 bytes c10/terminal_with_lock/build/loader.bin | Bin 0 -> 1396 bytes c10/terminal_with_lock/build/main.o | Bin 0 -> 2184 bytes c10/terminal_with_lock/build/mbr.bin | Bin 0 -> 512 bytes c10/terminal_with_lock/build/memory.o | Bin 0 -> 5008 bytes c10/terminal_with_lock/build/print.o | Bin 0 -> 1872 bytes c10/terminal_with_lock/build/string.o | Bin 0 -> 4000 bytes c10/terminal_with_lock/build/switch.o | Bin 0 -> 448 bytes c10/terminal_with_lock/build/sync.o | Bin 0 -> 3980 bytes c10/terminal_with_lock/build/thread.o | Bin 0 -> 6340 bytes c10/terminal_with_lock/build/timer.o | Bin 0 -> 2584 bytes c10/terminal_with_lock/device/console.c | 43 ++ c10/terminal_with_lock/device/console.h | 14 + c10/terminal_with_lock/device/timer.c | 64 ++ c10/terminal_with_lock/device/timer.h | 6 + c10/terminal_with_lock/include/boot.inc | 56 ++ c10/terminal_with_lock/kernel/debug.c | 18 + c10/terminal_with_lock/kernel/debug.h | 24 + c10/terminal_with_lock/kernel/global.h | 39 ++ c10/terminal_with_lock/kernel/init.c | 18 + c10/terminal_with_lock/kernel/init.h | 4 + c10/terminal_with_lock/kernel/interrupt.c | 192 ++++++ c10/terminal_with_lock/kernel/interrupt.h | 22 + c10/terminal_with_lock/kernel/kernel.S | 90 +++ c10/terminal_with_lock/kernel/main.c | 58 ++ c10/terminal_with_lock/kernel/memory.c | 257 ++++++++ c10/terminal_with_lock/kernel/memory.h | 33 + c10/terminal_with_lock/lib/kernel/bitmap.c | 82 +++ c10/terminal_with_lock/lib/kernel/bitmap.h | 18 + c10/terminal_with_lock/lib/kernel/io.h | 49 ++ c10/terminal_with_lock/lib/kernel/list.c | 95 +++ c10/terminal_with_lock/lib/kernel/list.h | 40 ++ c10/terminal_with_lock/lib/kernel/print.S | 239 +++++++ c10/terminal_with_lock/lib/kernel/print.h | 9 + c10/terminal_with_lock/lib/stdint.h | 11 + c10/terminal_with_lock/lib/string.c | 121 ++++ c10/terminal_with_lock/lib/string.h | 14 + c10/terminal_with_lock/makefile | 117 ++++ c10/terminal_with_lock/thread/switch.S | 37 ++ c10/terminal_with_lock/thread/sync.c | 99 +++ c10/terminal_with_lock/thread/sync.h | 30 + c10/terminal_with_lock/thread/thread.c | 202 ++++++ c10/terminal_with_lock/thread/thread.h | 108 ++++ c10/terminal_with_lock/tmp | 206 ++++++ c2/a/boot/mbr.S | 59 ++ c2/a/boot/mbr.S.comment | 63 ++ c2/a/tool/mbr.sh | 1 + c2/a/tool/xxd.sh | 21 + c3/a/boot/mbr.S | 55 ++ c3/a/tool/mbr.sh | 1 + c3/a/tool/xxd.sh | 21 + c3/b/boot/include/boot.inc | 5 + c3/b/boot/loader.S | 30 + c3/b/boot/mbr.S | 136 ++++ c4/a/boot/bochsrc | 17 + c4/a/boot/build.sh | 18 + c4/a/boot/include/boot.inc | 50 ++ c4/a/boot/loader.S | 107 ++++ c4/a/boot/mbr.S | 136 ++++ c5/detect_memory/bochsrc | 17 + c5/detect_memory/build.sh | 18 + c5/detect_memory/include/boot.inc | 50 ++ c5/detect_memory/loader.S | 166 +++++ c5/detect_memory/mbr.S | 136 ++++ c5/load-kernel/build.sh | 21 + c5/load-kernel/include/boot.inc | 66 ++ c5/load-kernel/kernel/main.c | 4 + c5/load-kernel/loader.S | 320 ++++++++++ c5/load-kernel/mbr.S | 107 ++++ c5/page-memory/build.sh | 15 + c5/page-memory/clean.sh | 3 + c5/page-memory/disk.img | Bin 0 -> 512 bytes c5/page-memory/include/boot.inc | 62 ++ c5/page-memory/loader.S | 251 ++++++++ c5/page-memory/loader.bin | Bin 0 -> 1189 bytes c5/page-memory/mbr.S | 107 ++++ c5/page-memory/mbr.bin | Bin 0 -> 512 bytes c6/boot/include/boot.inc | 56 ++ c6/boot/loader.S | 380 +++++++++++ c6/boot/mbr.S | 126 ++++ c6/build.sh | 26 + c6/clean.sh | 3 + c6/include/boot.inc | 56 ++ c6/kernel/kernel.bin | Bin 0 -> 5692 bytes c6/kernel/main.c | 34 + c6/kernel/main.o | Bin 0 -> 1256 bytes c6/lib/kernel/print.S | 226 +++++++ c6/lib/kernel/print.h | 21 + c6/lib/kernel/print.o | Bin 0 -> 1760 bytes c6/lib/stdint.h | 17 + c6/loader.S | 380 +++++++++++ c6/loader.bin | Bin 0 -> 1396 bytes c6/mbr.S | 126 ++++ c6/mbr.bin | Bin 0 -> 512 bytes c6/pra/base_asm.c | 12 + c6/pra/inlineASM.c | 14 + c6/pra/mach_mode_warn.c | 6 + c6/pra/mem.c | 7 + c6/pra/reg4.c | 15 + c6/pra/reg5.c | 10 + c6/pra/reg6.c | 6 + c6/pra/reg7.c | 13 + c6/pra/reg8.c | 13 + c6/pra/reg9.c | 8 + c6/pra/reg_constraint.c | 6 + c6/pra/syscall_write.S | 44 ++ c7/improve/boot/loader.S | 380 +++++++++++ c7/improve/boot/mbr.S | 126 ++++ c7/improve/build.sh | 33 + c7/improve/build/init.o | Bin 0 -> 1376 bytes c7/improve/build/interrupt.o | Bin 0 -> 4320 bytes c7/improve/build/kernel.o | Bin 0 -> 3232 bytes c7/improve/build/loader.bin | Bin 0 -> 1396 bytes c7/improve/build/main.o | Bin 0 -> 1376 bytes c7/improve/build/mbr.bin | Bin 0 -> 512 bytes c7/improve/build/print.o | Bin 0 -> 1792 bytes c7/improve/include/boot.inc | 56 ++ c7/improve/kernel/global.h | 34 + c7/improve/kernel/init.c | 9 + c7/improve/kernel/init.h | 4 + c7/improve/kernel/interrupt.c | 135 ++++ c7/improve/kernel/interrupt.h | 1 + c7/improve/kernel/io.h | 49 ++ c7/improve/kernel/kernel.S | 90 +++ c7/improve/kernel/main.c | 9 + c7/improve/lib/kernel/print.S | 217 +++++++ c7/improve/lib/kernel/print.h | 8 + c7/improve/lib/stdint.h | 11 + c7/timer/boot/loader.S | 380 +++++++++++ c7/timer/boot/mbr.S | 126 ++++ c7/timer/build.sh | 34 + c7/timer/device/timer.c | 39 ++ c7/timer/device/timer.h | 6 + c7/timer/include/boot.inc | 56 ++ c7/timer/kernel/global.h | 34 + c7/timer/kernel/init.c | 11 + c7/timer/kernel/init.h | 4 + c7/timer/kernel/interrupt.c | 135 ++++ c7/timer/kernel/interrupt.h | 1 + c7/timer/kernel/io.h | 49 ++ c7/timer/kernel/kernel.S | 90 +++ c7/timer/kernel/main.c | 9 + c7/timer/lib/kernel/print.S | 217 +++++++ c7/timer/lib/kernel/print.h | 8 + c7/timer/lib/stdint.h | 11 + c7/with_asm/boot/loader.S | 380 +++++++++++ c7/with_asm/boot/mbr.S | 126 ++++ c7/with_asm/build.sh | 33 + c7/with_asm/include/boot.inc | 56 ++ c7/with_asm/kernel/global.h | 34 + c7/with_asm/kernel/init.c | 9 + c7/with_asm/kernel/init.h | 4 + c7/with_asm/kernel/interrupt.c | 86 +++ c7/with_asm/kernel/interrupt.h | 1 + c7/with_asm/kernel/io.h | 49 ++ c7/with_asm/kernel/kernel.S | 69 ++ c7/with_asm/kernel/main.c | 9 + c7/with_asm/lib/kernel/print.S | 217 +++++++ c7/with_asm/lib/kernel/print.h | 8 + c7/with_asm/lib/stdint.h | 11 + c8/assert/boot/loader.S | 380 +++++++++++ c8/assert/boot/mbr.S | 126 ++++ c8/assert/build.sh | 34 + c8/assert/build/init.o | Bin 0 -> 1416 bytes c8/assert/build/interrupt.o | Bin 0 -> 4812 bytes c8/assert/build/loader.bin | Bin 0 -> 1396 bytes c8/assert/build/main.o | Bin 0 -> 1524 bytes c8/assert/build/mbr.bin | Bin 0 -> 512 bytes c8/assert/device/timer.c | 39 ++ c8/assert/device/timer.h | 6 + c8/assert/include/boot.inc | 56 ++ c8/assert/kernel/debug.c | 18 + c8/assert/kernel/debug.h | 24 + c8/assert/kernel/global.h | 34 + c8/assert/kernel/init.c | 11 + c8/assert/kernel/init.h | 4 + c8/assert/kernel/interrupt.c | 178 ++++++ c8/assert/kernel/interrupt.h | 21 + c8/assert/kernel/kernel.S | 90 +++ c8/assert/kernel/main.c | 12 + c8/assert/lib/kernel/io.h | 49 ++ c8/assert/lib/kernel/print.S | 217 +++++++ c8/assert/lib/kernel/print.h | 8 + c8/assert/lib/stdint.h | 11 + c8/assert/lib/string.c | 121 ++++ c8/assert/lib/string.h | 0 c8/assert/makefile | 79 +++ c8/assert/makefile_2 | 64 ++ c8/memory_manager/boot/loader.S | 380 +++++++++++ c8/memory_manager/boot/mbr.S | 126 ++++ c8/memory_manager/build.sh | 34 + c8/memory_manager/device/timer.c | 39 ++ c8/memory_manager/device/timer.h | 6 + c8/memory_manager/include/boot.inc | 56 ++ c8/memory_manager/kernel/debug.c | 18 + c8/memory_manager/kernel/debug.h | 24 + c8/memory_manager/kernel/global.h | 39 ++ c8/memory_manager/kernel/init.c | 13 + c8/memory_manager/kernel/init.h | 4 + c8/memory_manager/kernel/interrupt.c | 179 ++++++ c8/memory_manager/kernel/interrupt.h | 21 + c8/memory_manager/kernel/kernel.S | 90 +++ c8/memory_manager/kernel/main.c | 20 + c8/memory_manager/kernel/memory.c | 257 ++++++++ c8/memory_manager/kernel/memory.h | 33 + c8/memory_manager/lib/kernel/bitmap.c | 82 +++ c8/memory_manager/lib/kernel/bitmap.h | 18 + c8/memory_manager/lib/kernel/io.h | 49 ++ c8/memory_manager/lib/kernel/print.S | 217 +++++++ c8/memory_manager/lib/kernel/print.h | 8 + c8/memory_manager/lib/stdint.h | 11 + c8/memory_manager/lib/string.c | 121 ++++ c8/memory_manager/lib/string.h | 0 c8/memory_manager/makefile | 94 +++ c8/temp | 87 +++ c9/thread_schedule/boot/loader.S | 380 +++++++++++ c9/thread_schedule/boot/mbr.S | 126 ++++ c9/thread_schedule/build/bitmap.o | Bin 0 -> 2460 bytes c9/thread_schedule/build/debug.o | Bin 0 -> 1804 bytes c9/thread_schedule/build/init.o | Bin 0 -> 1508 bytes c9/thread_schedule/build/interrupt.o | Bin 0 -> 5296 bytes c9/thread_schedule/build/kernel.bin | Bin 0 -> 22328 bytes c9/thread_schedule/build/kernel.map | 551 ++++++++++++++++ c9/thread_schedule/build/kernel.o | Bin 0 -> 3232 bytes c9/thread_schedule/build/list.o | Bin 0 -> 2884 bytes c9/thread_schedule/build/loader.bin | Bin 0 -> 1396 bytes c9/thread_schedule/build/main.o | Bin 0 -> 1932 bytes c9/thread_schedule/build/mbr.bin | Bin 0 -> 512 bytes c9/thread_schedule/build/memory.o | Bin 0 -> 5008 bytes c9/thread_schedule/build/print.o | Bin 0 -> 1872 bytes c9/thread_schedule/build/string.o | Bin 0 -> 3996 bytes c9/thread_schedule/build/switch.o | Bin 0 -> 448 bytes c9/thread_schedule/build/thread.o | Bin 0 -> 5040 bytes c9/thread_schedule/build/timer.o | Bin 0 -> 2584 bytes c9/thread_schedule/device/timer.c | 64 ++ c9/thread_schedule/device/timer.h | 6 + c9/thread_schedule/include/boot.inc | 56 ++ c9/thread_schedule/kernel/debug.c | 18 + c9/thread_schedule/kernel/debug.h | 24 + c9/thread_schedule/kernel/global.h | 39 ++ c9/thread_schedule/kernel/init.c | 16 + c9/thread_schedule/kernel/init.h | 4 + c9/thread_schedule/kernel/interrupt.c | 192 ++++++ c9/thread_schedule/kernel/interrupt.h | 22 + c9/thread_schedule/kernel/kernel.S | 90 +++ c9/thread_schedule/kernel/main.c | 48 ++ c9/thread_schedule/kernel/memory.c | 257 ++++++++ c9/thread_schedule/kernel/memory.h | 33 + c9/thread_schedule/lib/kernel/bitmap.c | 82 +++ c9/thread_schedule/lib/kernel/bitmap.h | 18 + c9/thread_schedule/lib/kernel/io.h | 49 ++ c9/thread_schedule/lib/kernel/list.c | 95 +++ c9/thread_schedule/lib/kernel/list.h | 40 ++ c9/thread_schedule/lib/kernel/print.S | 239 +++++++ c9/thread_schedule/lib/kernel/print.h | 9 + c9/thread_schedule/lib/stdint.h | 11 + c9/thread_schedule/lib/string.c | 121 ++++ c9/thread_schedule/lib/string.h | 0 c9/thread_schedule/makefile | 112 ++++ c9/thread_schedule/test_pra/thread_test.c | 2 + c9/thread_schedule/thread/switch.S | 37 ++ c9/thread_schedule/thread/thread.c | 170 +++++ c9/thread_schedule/thread/thread.h | 106 ++++ c9/thread_schedule/tmp | 206 ++++++ c9/thread_start/boot/loader.S | 380 +++++++++++ c9/thread_start/boot/mbr.S | 126 ++++ c9/thread_start/device/timer.c | 39 ++ c9/thread_start/device/timer.h | 6 + c9/thread_start/include/boot.inc | 56 ++ c9/thread_start/kernel/debug.c | 18 + c9/thread_start/kernel/debug.h | 24 + c9/thread_start/kernel/global.h | 39 ++ c9/thread_start/kernel/init.c | 13 + c9/thread_start/kernel/init.h | 4 + c9/thread_start/kernel/interrupt.c | 201 ++++++ c9/thread_start/kernel/interrupt.h | 21 + c9/thread_start/kernel/kernel.S | 90 +++ c9/thread_start/kernel/main.c | 35 + c9/thread_start/kernel/memory.c | 257 ++++++++ c9/thread_start/kernel/memory.h | 33 + c9/thread_start/lib/kernel/bitmap.c | 82 +++ c9/thread_start/lib/kernel/bitmap.h | 18 + c9/thread_start/lib/kernel/io.h | 49 ++ c9/thread_start/lib/kernel/list.c | 95 +++ c9/thread_start/lib/kernel/list.h | 31 + c9/thread_start/lib/kernel/print.S | 217 +++++++ c9/thread_start/lib/kernel/print.h | 8 + c9/thread_start/lib/stdint.h | 11 + c9/thread_start/lib/string.c | 121 ++++ c9/thread_start/lib/string.h | 0 c9/thread_start/makefile | 100 +++ c9/thread_start/test_pra/thread_test.c | 2 + c9/thread_start/thread/thread.c | 76 +++ c9/thread_start/thread/thread.h | 90 +++ tool/calculator.sh | 1 + tool/kernel.sh | 2 + tool/loader.sh | 1 + tool/mbr.sh | 1 + tool/xxd.sh | 21 + 365 files changed, 23505 insertions(+) create mode 100755 "32\344\275\215\351\234\200\347\237\245" create mode 100755 c10/keyboard/boot/loader.S create mode 100755 c10/keyboard/boot/mbr.S create mode 100644 c10/keyboard/build/bitmap.o create mode 100644 c10/keyboard/build/console.o create mode 100644 c10/keyboard/build/debug.o create mode 100644 c10/keyboard/build/init.o create mode 100644 c10/keyboard/build/interrupt.o create mode 100755 c10/keyboard/build/kernel.bin create mode 100644 c10/keyboard/build/kernel.map create mode 100644 c10/keyboard/build/kernel.o create mode 100644 c10/keyboard/build/list.o create mode 100644 c10/keyboard/build/loader.bin create mode 100644 c10/keyboard/build/main.o create mode 100644 c10/keyboard/build/mbr.bin create mode 100644 c10/keyboard/build/memory.o create mode 100644 c10/keyboard/build/print.o create mode 100644 c10/keyboard/build/string.o create mode 100644 c10/keyboard/build/switch.o create mode 100644 c10/keyboard/build/sync.o create mode 100644 c10/keyboard/build/thread.o create mode 100644 c10/keyboard/build/timer.o create mode 100644 c10/keyboard/device/console.c create mode 100644 c10/keyboard/device/console.h create mode 100755 c10/keyboard/device/timer.c create mode 100755 c10/keyboard/device/timer.h create mode 100755 c10/keyboard/include/boot.inc create mode 100755 c10/keyboard/kernel/debug.c create mode 100755 c10/keyboard/kernel/debug.h create mode 100755 c10/keyboard/kernel/global.h create mode 100755 c10/keyboard/kernel/init.c create mode 100755 c10/keyboard/kernel/init.h create mode 100755 c10/keyboard/kernel/interrupt.c create mode 100755 c10/keyboard/kernel/interrupt.h create mode 100755 c10/keyboard/kernel/kernel.S create mode 100755 c10/keyboard/kernel/main.c create mode 100755 c10/keyboard/kernel/memory.c create mode 100755 c10/keyboard/kernel/memory.h create mode 100755 c10/keyboard/lib/kernel/bitmap.c create mode 100755 c10/keyboard/lib/kernel/bitmap.h create mode 100755 c10/keyboard/lib/kernel/io.h create mode 100755 c10/keyboard/lib/kernel/list.c create mode 100755 c10/keyboard/lib/kernel/list.h create mode 100755 c10/keyboard/lib/kernel/print.S create mode 100755 c10/keyboard/lib/kernel/print.h create mode 100755 c10/keyboard/lib/stdint.h create mode 100755 c10/keyboard/lib/string.c create mode 100755 c10/keyboard/lib/string.h create mode 100644 c10/keyboard/makefile create mode 100755 c10/keyboard/thread/switch.S create mode 100644 c10/keyboard/thread/sync.c create mode 100644 c10/keyboard/thread/sync.h create mode 100755 c10/keyboard/thread/thread.c create mode 100755 c10/keyboard/thread/thread.h create mode 100755 c10/keyboard/tmp create mode 100644 "c10/keyboard/\344\273\243\347\240\201\347\273\223\346\236\204.txt" create mode 100755 c10/terminal_with_lock/boot/loader.S create mode 100755 c10/terminal_with_lock/boot/mbr.S create mode 100644 c10/terminal_with_lock/build/bitmap.o create mode 100644 c10/terminal_with_lock/build/console.o create mode 100644 c10/terminal_with_lock/build/debug.o create mode 100644 c10/terminal_with_lock/build/init.o create mode 100644 c10/terminal_with_lock/build/interrupt.o create mode 100755 c10/terminal_with_lock/build/kernel.bin create mode 100644 c10/terminal_with_lock/build/kernel.map create mode 100644 c10/terminal_with_lock/build/kernel.o create mode 100644 c10/terminal_with_lock/build/list.o create mode 100644 c10/terminal_with_lock/build/loader.bin create mode 100644 c10/terminal_with_lock/build/main.o create mode 100644 c10/terminal_with_lock/build/mbr.bin create mode 100644 c10/terminal_with_lock/build/memory.o create mode 100644 c10/terminal_with_lock/build/print.o create mode 100644 c10/terminal_with_lock/build/string.o create mode 100644 c10/terminal_with_lock/build/switch.o create mode 100644 c10/terminal_with_lock/build/sync.o create mode 100644 c10/terminal_with_lock/build/thread.o create mode 100644 c10/terminal_with_lock/build/timer.o create mode 100644 c10/terminal_with_lock/device/console.c create mode 100644 c10/terminal_with_lock/device/console.h create mode 100755 c10/terminal_with_lock/device/timer.c create mode 100755 c10/terminal_with_lock/device/timer.h create mode 100755 c10/terminal_with_lock/include/boot.inc create mode 100755 c10/terminal_with_lock/kernel/debug.c create mode 100755 c10/terminal_with_lock/kernel/debug.h create mode 100755 c10/terminal_with_lock/kernel/global.h create mode 100755 c10/terminal_with_lock/kernel/init.c create mode 100755 c10/terminal_with_lock/kernel/init.h create mode 100755 c10/terminal_with_lock/kernel/interrupt.c create mode 100755 c10/terminal_with_lock/kernel/interrupt.h create mode 100755 c10/terminal_with_lock/kernel/kernel.S create mode 100755 c10/terminal_with_lock/kernel/main.c create mode 100755 c10/terminal_with_lock/kernel/memory.c create mode 100755 c10/terminal_with_lock/kernel/memory.h create mode 100755 c10/terminal_with_lock/lib/kernel/bitmap.c create mode 100755 c10/terminal_with_lock/lib/kernel/bitmap.h create mode 100755 c10/terminal_with_lock/lib/kernel/io.h create mode 100755 c10/terminal_with_lock/lib/kernel/list.c create mode 100755 c10/terminal_with_lock/lib/kernel/list.h create mode 100755 c10/terminal_with_lock/lib/kernel/print.S create mode 100755 c10/terminal_with_lock/lib/kernel/print.h create mode 100755 c10/terminal_with_lock/lib/stdint.h create mode 100755 c10/terminal_with_lock/lib/string.c create mode 100755 c10/terminal_with_lock/lib/string.h create mode 100644 c10/terminal_with_lock/makefile create mode 100755 c10/terminal_with_lock/thread/switch.S create mode 100644 c10/terminal_with_lock/thread/sync.c create mode 100644 c10/terminal_with_lock/thread/sync.h create mode 100755 c10/terminal_with_lock/thread/thread.c create mode 100755 c10/terminal_with_lock/thread/thread.h create mode 100755 c10/terminal_with_lock/tmp create mode 100755 c2/a/boot/mbr.S create mode 100755 c2/a/boot/mbr.S.comment create mode 100755 c2/a/tool/mbr.sh create mode 100755 c2/a/tool/xxd.sh create mode 100755 c3/a/boot/mbr.S create mode 100755 c3/a/tool/mbr.sh create mode 100755 c3/a/tool/xxd.sh create mode 100755 c3/b/boot/include/boot.inc create mode 100755 c3/b/boot/loader.S create mode 100755 c3/b/boot/mbr.S create mode 100755 c4/a/boot/bochsrc create mode 100755 c4/a/boot/build.sh create mode 100755 c4/a/boot/include/boot.inc create mode 100755 c4/a/boot/loader.S create mode 100755 c4/a/boot/mbr.S create mode 100755 c5/detect_memory/bochsrc create mode 100755 c5/detect_memory/build.sh create mode 100755 c5/detect_memory/include/boot.inc create mode 100755 c5/detect_memory/loader.S create mode 100755 c5/detect_memory/mbr.S create mode 100755 c5/load-kernel/build.sh create mode 100755 c5/load-kernel/include/boot.inc create mode 100755 c5/load-kernel/kernel/main.c create mode 100755 c5/load-kernel/loader.S create mode 100755 c5/load-kernel/mbr.S create mode 100755 c5/page-memory/build.sh create mode 100755 c5/page-memory/clean.sh create mode 100644 c5/page-memory/disk.img create mode 100755 c5/page-memory/include/boot.inc create mode 100755 c5/page-memory/loader.S create mode 100644 c5/page-memory/loader.bin create mode 100755 c5/page-memory/mbr.S create mode 100644 c5/page-memory/mbr.bin create mode 100755 c6/boot/include/boot.inc create mode 100755 c6/boot/loader.S create mode 100755 c6/boot/mbr.S create mode 100755 c6/build.sh create mode 100755 c6/clean.sh create mode 100755 c6/include/boot.inc create mode 100755 c6/kernel/kernel.bin create mode 100755 c6/kernel/main.c create mode 100755 c6/kernel/main.o create mode 100755 c6/lib/kernel/print.S create mode 100755 c6/lib/kernel/print.h create mode 100755 c6/lib/kernel/print.o create mode 100755 c6/lib/stdint.h create mode 100755 c6/loader.S create mode 100755 c6/loader.bin create mode 100755 c6/mbr.S create mode 100755 c6/mbr.bin create mode 100755 c6/pra/base_asm.c create mode 100755 c6/pra/inlineASM.c create mode 100755 c6/pra/mach_mode_warn.c create mode 100755 c6/pra/mem.c create mode 100755 c6/pra/reg4.c create mode 100755 c6/pra/reg5.c create mode 100755 c6/pra/reg6.c create mode 100755 c6/pra/reg7.c create mode 100755 c6/pra/reg8.c create mode 100755 c6/pra/reg9.c create mode 100755 c6/pra/reg_constraint.c create mode 100755 c6/pra/syscall_write.S create mode 100755 c7/improve/boot/loader.S create mode 100755 c7/improve/boot/mbr.S create mode 100755 c7/improve/build.sh create mode 100644 c7/improve/build/init.o create mode 100644 c7/improve/build/interrupt.o create mode 100644 c7/improve/build/kernel.o create mode 100644 c7/improve/build/loader.bin create mode 100644 c7/improve/build/main.o create mode 100644 c7/improve/build/mbr.bin create mode 100644 c7/improve/build/print.o create mode 100755 c7/improve/include/boot.inc create mode 100755 c7/improve/kernel/global.h create mode 100755 c7/improve/kernel/init.c create mode 100755 c7/improve/kernel/init.h create mode 100755 c7/improve/kernel/interrupt.c create mode 100755 c7/improve/kernel/interrupt.h create mode 100755 c7/improve/kernel/io.h create mode 100755 c7/improve/kernel/kernel.S create mode 100755 c7/improve/kernel/main.c create mode 100755 c7/improve/lib/kernel/print.S create mode 100755 c7/improve/lib/kernel/print.h create mode 100755 c7/improve/lib/stdint.h create mode 100755 c7/timer/boot/loader.S create mode 100755 c7/timer/boot/mbr.S create mode 100755 c7/timer/build.sh create mode 100755 c7/timer/device/timer.c create mode 100755 c7/timer/device/timer.h create mode 100755 c7/timer/include/boot.inc create mode 100755 c7/timer/kernel/global.h create mode 100755 c7/timer/kernel/init.c create mode 100755 c7/timer/kernel/init.h create mode 100755 c7/timer/kernel/interrupt.c create mode 100755 c7/timer/kernel/interrupt.h create mode 100755 c7/timer/kernel/io.h create mode 100755 c7/timer/kernel/kernel.S create mode 100755 c7/timer/kernel/main.c create mode 100755 c7/timer/lib/kernel/print.S create mode 100755 c7/timer/lib/kernel/print.h create mode 100755 c7/timer/lib/stdint.h create mode 100755 c7/with_asm/boot/loader.S create mode 100755 c7/with_asm/boot/mbr.S create mode 100755 c7/with_asm/build.sh create mode 100755 c7/with_asm/include/boot.inc create mode 100755 c7/with_asm/kernel/global.h create mode 100755 c7/with_asm/kernel/init.c create mode 100755 c7/with_asm/kernel/init.h create mode 100755 c7/with_asm/kernel/interrupt.c create mode 100755 c7/with_asm/kernel/interrupt.h create mode 100755 c7/with_asm/kernel/io.h create mode 100755 c7/with_asm/kernel/kernel.S create mode 100755 c7/with_asm/kernel/main.c create mode 100755 c7/with_asm/lib/kernel/print.S create mode 100755 c7/with_asm/lib/kernel/print.h create mode 100755 c7/with_asm/lib/stdint.h create mode 100755 c8/assert/boot/loader.S create mode 100755 c8/assert/boot/mbr.S create mode 100755 c8/assert/build.sh create mode 100644 c8/assert/build/init.o create mode 100644 c8/assert/build/interrupt.o create mode 100644 c8/assert/build/loader.bin create mode 100644 c8/assert/build/main.o create mode 100644 c8/assert/build/mbr.bin create mode 100755 c8/assert/device/timer.c create mode 100755 c8/assert/device/timer.h create mode 100755 c8/assert/include/boot.inc create mode 100755 c8/assert/kernel/debug.c create mode 100755 c8/assert/kernel/debug.h create mode 100755 c8/assert/kernel/global.h create mode 100755 c8/assert/kernel/init.c create mode 100755 c8/assert/kernel/init.h create mode 100755 c8/assert/kernel/interrupt.c create mode 100755 c8/assert/kernel/interrupt.h create mode 100755 c8/assert/kernel/kernel.S create mode 100755 c8/assert/kernel/main.c create mode 100755 c8/assert/lib/kernel/io.h create mode 100755 c8/assert/lib/kernel/print.S create mode 100755 c8/assert/lib/kernel/print.h create mode 100755 c8/assert/lib/stdint.h create mode 100755 c8/assert/lib/string.c create mode 100755 c8/assert/lib/string.h create mode 100755 c8/assert/makefile create mode 100755 c8/assert/makefile_2 create mode 100755 c8/memory_manager/boot/loader.S create mode 100755 c8/memory_manager/boot/mbr.S create mode 100755 c8/memory_manager/build.sh create mode 100755 c8/memory_manager/device/timer.c create mode 100755 c8/memory_manager/device/timer.h create mode 100755 c8/memory_manager/include/boot.inc create mode 100755 c8/memory_manager/kernel/debug.c create mode 100755 c8/memory_manager/kernel/debug.h create mode 100755 c8/memory_manager/kernel/global.h create mode 100755 c8/memory_manager/kernel/init.c create mode 100755 c8/memory_manager/kernel/init.h create mode 100755 c8/memory_manager/kernel/interrupt.c create mode 100755 c8/memory_manager/kernel/interrupt.h create mode 100755 c8/memory_manager/kernel/kernel.S create mode 100755 c8/memory_manager/kernel/main.c create mode 100755 c8/memory_manager/kernel/memory.c create mode 100755 c8/memory_manager/kernel/memory.h create mode 100755 c8/memory_manager/lib/kernel/bitmap.c create mode 100755 c8/memory_manager/lib/kernel/bitmap.h create mode 100755 c8/memory_manager/lib/kernel/io.h create mode 100755 c8/memory_manager/lib/kernel/print.S create mode 100755 c8/memory_manager/lib/kernel/print.h create mode 100755 c8/memory_manager/lib/stdint.h create mode 100755 c8/memory_manager/lib/string.c create mode 100755 c8/memory_manager/lib/string.h create mode 100755 c8/memory_manager/makefile create mode 100755 c8/temp create mode 100755 c9/thread_schedule/boot/loader.S create mode 100755 c9/thread_schedule/boot/mbr.S create mode 100644 c9/thread_schedule/build/bitmap.o create mode 100644 c9/thread_schedule/build/debug.o create mode 100644 c9/thread_schedule/build/init.o create mode 100644 c9/thread_schedule/build/interrupt.o create mode 100755 c9/thread_schedule/build/kernel.bin create mode 100644 c9/thread_schedule/build/kernel.map create mode 100644 c9/thread_schedule/build/kernel.o create mode 100644 c9/thread_schedule/build/list.o create mode 100644 c9/thread_schedule/build/loader.bin create mode 100644 c9/thread_schedule/build/main.o create mode 100644 c9/thread_schedule/build/mbr.bin create mode 100644 c9/thread_schedule/build/memory.o create mode 100644 c9/thread_schedule/build/print.o create mode 100644 c9/thread_schedule/build/string.o create mode 100644 c9/thread_schedule/build/switch.o create mode 100644 c9/thread_schedule/build/thread.o create mode 100644 c9/thread_schedule/build/timer.o create mode 100755 c9/thread_schedule/device/timer.c create mode 100755 c9/thread_schedule/device/timer.h create mode 100755 c9/thread_schedule/include/boot.inc create mode 100755 c9/thread_schedule/kernel/debug.c create mode 100755 c9/thread_schedule/kernel/debug.h create mode 100755 c9/thread_schedule/kernel/global.h create mode 100755 c9/thread_schedule/kernel/init.c create mode 100755 c9/thread_schedule/kernel/init.h create mode 100755 c9/thread_schedule/kernel/interrupt.c create mode 100755 c9/thread_schedule/kernel/interrupt.h create mode 100755 c9/thread_schedule/kernel/kernel.S create mode 100755 c9/thread_schedule/kernel/main.c create mode 100755 c9/thread_schedule/kernel/memory.c create mode 100755 c9/thread_schedule/kernel/memory.h create mode 100755 c9/thread_schedule/lib/kernel/bitmap.c create mode 100755 c9/thread_schedule/lib/kernel/bitmap.h create mode 100755 c9/thread_schedule/lib/kernel/io.h create mode 100755 c9/thread_schedule/lib/kernel/list.c create mode 100755 c9/thread_schedule/lib/kernel/list.h create mode 100755 c9/thread_schedule/lib/kernel/print.S create mode 100755 c9/thread_schedule/lib/kernel/print.h create mode 100755 c9/thread_schedule/lib/stdint.h create mode 100755 c9/thread_schedule/lib/string.c create mode 100755 c9/thread_schedule/lib/string.h create mode 100644 c9/thread_schedule/makefile create mode 100755 c9/thread_schedule/test_pra/thread_test.c create mode 100755 c9/thread_schedule/thread/switch.S create mode 100755 c9/thread_schedule/thread/thread.c create mode 100755 c9/thread_schedule/thread/thread.h create mode 100755 c9/thread_schedule/tmp create mode 100755 c9/thread_start/boot/loader.S create mode 100755 c9/thread_start/boot/mbr.S create mode 100755 c9/thread_start/device/timer.c create mode 100755 c9/thread_start/device/timer.h create mode 100755 c9/thread_start/include/boot.inc create mode 100755 c9/thread_start/kernel/debug.c create mode 100755 c9/thread_start/kernel/debug.h create mode 100755 c9/thread_start/kernel/global.h create mode 100755 c9/thread_start/kernel/init.c create mode 100755 c9/thread_start/kernel/init.h create mode 100755 c9/thread_start/kernel/interrupt.c create mode 100755 c9/thread_start/kernel/interrupt.h create mode 100755 c9/thread_start/kernel/kernel.S create mode 100755 c9/thread_start/kernel/main.c create mode 100755 c9/thread_start/kernel/memory.c create mode 100755 c9/thread_start/kernel/memory.h create mode 100755 c9/thread_start/lib/kernel/bitmap.c create mode 100755 c9/thread_start/lib/kernel/bitmap.h create mode 100755 c9/thread_start/lib/kernel/io.h create mode 100755 c9/thread_start/lib/kernel/list.c create mode 100755 c9/thread_start/lib/kernel/list.h create mode 100755 c9/thread_start/lib/kernel/print.S create mode 100755 c9/thread_start/lib/kernel/print.h create mode 100755 c9/thread_start/lib/stdint.h create mode 100755 c9/thread_start/lib/string.c create mode 100755 c9/thread_start/lib/string.h create mode 100755 c9/thread_start/makefile create mode 100755 c9/thread_start/test_pra/thread_test.c create mode 100755 c9/thread_start/thread/thread.c create mode 100755 c9/thread_start/thread/thread.h create mode 100755 tool/calculator.sh create mode 100755 tool/kernel.sh create mode 100755 tool/loader.sh create mode 100755 tool/mbr.sh create mode 100755 tool/xxd.sh diff --git "a/32\344\275\215\351\234\200\347\237\245" "b/32\344\275\215\351\234\200\347\237\245" new file mode 100755 index 0000000..76e3f3d --- /dev/null +++ "b/32\344\275\215\351\234\200\347\237\245" @@ -0,0 +1,18 @@ +hello.c +用gcc编译选项为: gcc -m32 -c -o hello.o hello.c +用-m32指定为32位 +print.asm +用nasm汇编选项为: nasm -f elf -o print.o print.asm +用-f elf 指定为32位,64位则为-f elf64 +ld +用ld 连接目标文件: ld -m elf_i386 -o hello.bin hello.o print.o +hello.c中调用了print.asm中函数,链接时一般采用调用在前,实现在后 + +如果有报错找不到32位相关库,则运行以下命令安装升级: +sudo apt-get install gcc-multilib +sudo apt-get install g++-multilib + +作者:MarvinLe +链接:https://www.jianshu.com/p/293f38918b96 +来源:简书 +著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 \ No newline at end of file diff --git a/c10/keyboard/boot/loader.S b/c10/keyboard/boot/loader.S new file mode 100755 index 0000000..d247cf5 --- /dev/null +++ b/c10/keyboard/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c10/keyboard/boot/mbr.S b/c10/keyboard/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c10/keyboard/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c10/keyboard/build/bitmap.o b/c10/keyboard/build/bitmap.o new file mode 100644 index 0000000000000000000000000000000000000000..d7365d28c5d29cb7939167ad2996de77e1bac85c GIT binary patch literal 2460 zcma)6-A^1<6u+~(z=9Mg_Q9l09FnSF!xW^o)KUYK0n!kQlzmd1>@HJSV3&1wrlA&U z)0KpA+=R#e5kB~YM*9HW5=zx%{dlN}i9|6$*$0|zV$(EPe`n@i7zl};X7y4)l4C<^}JLnl~Ir#3ffh)3djhwK}s=VW~x14cdbNTYW0!YnNEuXUK$)E%tOK|7MI?tIOQ``yZ0 zE28?MQ+>n3PnONc_<-!ORf>VPl(*DEX0BQ?%WSod|K>xTw!k15EVER+@-ozWsU3|;P!BSt*g{5GwQ zCaq2B2{sjroSxKE34Q8xBxc0JNhQh}uZO2hUA}m+rAeNhm3KwCwTZb-S~?m|?Cy1g z*@2Te)_3XBTXJJ4VkQhzKBt^jTAJHTM{2#*+OD*mX>zr6Bgbd~NgoX{X*MVwuMPwj zaH}$OC&&P5qou1RIPdE|Qr464Pp;`6)N3H{9H6(R4>;%8+C?B%Qq8hU)y zV2PEpmw+^8FK`*KOMb?GMqJ}(mAf|D_?>KfDnASE365Q2uW}9byB_tv;qj>VRj_`K zO#S%afUHBNSh@>}zYY|AC;1L!K@^__i=LC64-zI%)2^9`sHQ2cEp2UH>1>Obwpb!& zz%tYRrZT1*S~98`<7Q$~3C}RCZ{SLI*MN4VxA&?#tPOW{52zZ8>+v)u)HAr6p{MsL zSTdZ5MYVJ?wr{AZQ@$F;jsUA_nbW5LKHvh^cXItamp|q517Ibt^;?cVbNm$;c#*MN z7#QDC#=a*+{vUyq|8F40J>mF_qYo{jI2rM@@8LMk@iUHJ5K_@f`bOLcM}S7k#c{Dq z=~IfK&lpS@4I5#mMAB)dq^4bg49elXRie}JxSl{?W@QQs*1?uj6vs3DHMw|{rhzeeb& yfAn7x{iD1h7q(1>VGv4sE5zhi2c}%!l9HJ3yU+;|_0bG?{g!d=9pMBNe*XedLi*_d literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/console.o b/c10/keyboard/build/console.o new file mode 100644 index 0000000000000000000000000000000000000000..f9e15fd34da4c831a0b984520a79eedca0b994d6 GIT binary patch literal 2424 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 literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/interrupt.o b/c10/keyboard/build/interrupt.o new file mode 100644 index 0000000000000000000000000000000000000000..93d0ab3ad1a7b7e3d0e574030c4c27f02a3888e9 GIT binary patch literal 5296 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 RX-`>saVWk{4zeiTe*y2rtIGfY literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/kernel.bin b/c10/keyboard/build/kernel.bin new file mode 100755 index 0000000000000000000000000000000000000000..7d97336631139766f99b2e80bb3e481bf65756c1 GIT binary patch literal 27120 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 literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/kernel.map b/c10/keyboard/build/kernel.map new file mode 100644 index 0000000..047caa8 --- /dev/null +++ b/c10/keyboard/build/kernel.map @@ -0,0 +1,597 @@ + +分配公共符号 +公共符号 大小 文件 + +thread_ready_list 0x10 build/thread.o +thread_all_list 0x10 build/thread.o +user_pool 0x10 build/memory.o +kernel_addr 0xc build/memory.o +intr_name 0x84 build/interrupt.o +main_thread 0x4 build/thread.o +ticks 0x4 build/timer.o +idt_table 0x84 build/interrupt.o +kernel_pool 0x10 build/memory.o + +舍弃的输入节 + + .group 0x0000000000000000 0x8 build/main.o + .note.GNU-stack + 0x0000000000000000 0x0 build/main.o + .group 0x0000000000000000 0x8 build/init.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/init.o + .note.GNU-stack + 0x0000000000000000 0x0 build/init.o + .group 0x0000000000000000 0x8 build/interrupt.o + .group 0x0000000000000000 0x8 build/interrupt.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/interrupt.o + .note.GNU-stack + 0x0000000000000000 0x0 build/interrupt.o + .group 0x0000000000000000 0x8 build/timer.o + .group 0x0000000000000000 0x8 build/timer.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/timer.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/timer.o + .note.GNU-stack + 0x0000000000000000 0x0 build/timer.o + .group 0x0000000000000000 0x8 build/debug.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/debug.o + .note.GNU-stack + 0x0000000000000000 0x0 build/debug.o + .group 0x0000000000000000 0x8 build/memory.o + .group 0x0000000000000000 0x8 build/memory.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/memory.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/memory.o + .note.GNU-stack + 0x0000000000000000 0x0 build/memory.o + .group 0x0000000000000000 0x8 build/bitmap.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/bitmap.o + .note.GNU-stack + 0x0000000000000000 0x0 build/bitmap.o + .group 0x0000000000000000 0x8 build/string.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/string.o + .note.GNU-stack + 0x0000000000000000 0x0 build/string.o + .group 0x0000000000000000 0x8 build/thread.o + .group 0x0000000000000000 0x8 build/thread.o + .group 0x0000000000000000 0x8 build/thread.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/thread.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/thread.o + .note.GNU-stack + 0x0000000000000000 0x0 build/thread.o + .group 0x0000000000000000 0x8 build/list.o + .group 0x0000000000000000 0x8 build/list.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/list.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/list.o + .note.GNU-stack + 0x0000000000000000 0x0 build/list.o + .group 0x0000000000000000 0x8 build/console.o + .group 0x0000000000000000 0x8 build/console.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/console.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/console.o + .note.GNU-stack + 0x0000000000000000 0x0 build/console.o + .group 0x0000000000000000 0x8 build/sync.o + .group 0x0000000000000000 0x8 build/sync.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/sync.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/sync.o + .note.GNU-stack + 0x0000000000000000 0x0 build/sync.o + +内存配置 + +名称 来源 长度 属性 +*default* 0x0000000000000000 0xffffffffffffffff + +链结器命令稿和内存映射 + +段 .text 的地址设置为 0xc0001500 +LOAD build/main.o +LOAD build/init.o +LOAD build/interrupt.o +LOAD build/timer.o +LOAD build/kernel.o +LOAD build/print.o +LOAD build/debug.o +LOAD build/memory.o +LOAD build/bitmap.o +LOAD build/string.o +LOAD build/thread.o +LOAD build/switch.o +LOAD build/list.o +LOAD build/console.o +LOAD build/sync.o + [!provide] PROVIDE (__executable_start = SEGMENT_START ("text-segment", 0x8048000)) + 0x0000000008048094 . = (SEGMENT_START ("text-segment", 0x8048000) + SIZEOF_HEADERS) + +.interp + *(.interp) + +.note.gnu.build-id + *(.note.gnu.build-id) + +.hash + *(.hash) + +.gnu.hash + *(.gnu.hash) + +.dynsym + *(.dynsym) + +.dynstr + *(.dynstr) + +.gnu.version + *(.gnu.version) + +.gnu.version_d + *(.gnu.version_d) + +.gnu.version_r + *(.gnu.version_r) + +.rel.dyn 0x0000000008048094 0x0 + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + .rel.text 0x0000000008048094 0x0 build/main.o + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + .rel.got 0x0000000008048094 0x0 build/main.o + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + *(.rel.ifunc) + +.rel.plt 0x0000000008048094 0x0 + *(.rel.plt) + [!provide] PROVIDE (__rel_iplt_start = .) + *(.rel.iplt) + .rel.iplt 0x0000000008048094 0x0 build/main.o + [!provide] PROVIDE (__rel_iplt_end = .) + +.init + *(SORT_NONE(.init)) + +.plt 0x0000000008048098 0x0 + *(.plt) + *(.iplt) + .iplt 0x0000000008048098 0x0 build/main.o + +.plt.got + *(.plt.got) + +.plt.sec + *(.plt.sec) + +.text 0x00000000c0001500 0x297e + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + .text 0x00000000c0001500 0x11f build/main.o + 0x00000000c0001500 main + 0x00000000c00015a7 k_thread_HuSharp_1 + 0x00000000c00015cf k_thread_HuSharp_2 + 0x00000000c00015f7 k_thread_HuSharp_3 + .text.__x86.get_pc_thunk.bx + 0x00000000c000161f 0x4 build/main.o + 0x00000000c000161f __x86.get_pc_thunk.bx + .text 0x00000000c0001623 0x43 build/init.o + 0x00000000c0001623 init_all + .text 0x00000000c0001666 0x54f build/interrupt.o + 0x00000000c0001a57 intr_enable + 0x00000000c0001a8a intr_disable + 0x00000000c0001abd intr_set_status + 0x00000000c0001ae5 intr_get_status + 0x00000000c0001b0c register_handler + 0x00000000c0001b35 idt_init + .text.__x86.get_pc_thunk.ax + 0x00000000c0001bb5 0x4 build/interrupt.o + 0x00000000c0001bb5 __x86.get_pc_thunk.ax + .text 0x00000000c0001bb9 0x1b5 build/timer.o + 0x00000000c0001d06 timer_init + *fill* 0x00000000c0001d6e 0x2 + .text 0x00000000c0001d70 0x39d build/kernel.o + 0x00000000c0001d70 intr_exit + *fill* 0x00000000c000210d 0x3 + .text 0x00000000c0002110 0x172 build/print.o + 0x00000000c0002110 put_str + 0x00000000c000212e put_char + 0x00000000c00021fb put_int + 0x00000000c000225e set_cursor + .text 0x00000000c0002282 0xf4 build/debug.o + 0x00000000c0002282 panic_spin + .text 0x00000000c0002376 0x64f build/memory.o + 0x00000000c000240f pte_ptr + 0x00000000c0002449 pde_ptr + 0x00000000c000260b malloc_page + 0x00000000c00026e4 get_kernel_pages + 0x00000000c0002971 mem_init + .text 0x00000000c00029c5 0x28d build/bitmap.o + 0x00000000c00029c5 bitmap_init + 0x00000000c00029f8 bitmap_scan_test + 0x00000000c0002a42 bitmap_scan + 0x00000000c0002b8a bitmap_set + .text 0x00000000c0002c52 0x4ac build/string.o + 0x00000000c0002c52 memset + 0x00000000c0002cba memcpy + 0x00000000c0002d30 memcmp + 0x00000000c0002dc9 strcpy + 0x00000000c0002e34 strlen + 0x00000000c0002e95 strcmp + 0x00000000c0002f24 strchr + 0x00000000c0002f92 strrchr + 0x00000000c0003006 strcat + 0x00000000c0003086 strchrs + .text 0x00000000c00030fe 0x659 build/thread.o + 0x00000000c00030fe running_thread + 0x00000000c0003149 thread_create + 0x00000000c00031cd init_thread + 0x00000000c0003272 thread_start + 0x00000000c0003414 schedule + 0x00000000c0003559 thread_block + 0x00000000c00035d1 thread_unblock + 0x00000000c00036f2 thread_environment_init + .text.__x86.get_pc_thunk.dx + 0x00000000c0003757 0x4 build/thread.o + 0x00000000c0003757 __x86.get_pc_thunk.dx + *fill* 0x00000000c000375b 0x5 + .text 0x00000000c0003760 0x15 build/switch.o + 0x00000000c0003760 switch_to + .text 0x00000000c0003775 0x25f build/list.o + 0x00000000c0003775 list_init + 0x00000000c00037ad list_insert_before + 0x00000000c0003801 list_append + 0x00000000c0003829 list_remove + 0x00000000c0003872 list_push + 0x00000000c000389a list_pop + 0x00000000c00038c6 elem_find + 0x00000000c000390b list_traversal + 0x00000000c0003972 list_empty + 0x00000000c0003995 list_len + .text 0x00000000c00039d4 0x119 build/console.o + 0x00000000c00039d4 console_init + 0x00000000c00039ff console_acquire + 0x00000000c0003a2a console_release + 0x00000000c0003a55 console_put_str + 0x00000000c0003a85 console_put_char + 0x00000000c0003abd console_put_int + .text 0x00000000c0003aed 0x391 build/sync.o + 0x00000000c0003aed sema_init + 0x00000000c0003b27 lock_init + 0x00000000c0003b61 sema_down + 0x00000000c0003c7d sema_up + 0x00000000c0003d47 lock_acquire + 0x00000000c0003dcf lock_release + *(.gnu.warning) + +.fini + *(SORT_NONE(.fini)) + [!provide] PROVIDE (__etext = .) + [!provide] PROVIDE (_etext = .) + [!provide] PROVIDE (etext = .) + +.rodata 0x00000000c0003e80 0xa4d + *(.rodata .rodata.* .gnu.linkonce.r.*) + .rodata 0x00000000c0003e80 0x67 build/main.o + .rodata 0x00000000c0003ee7 0x11 build/init.o + .rodata 0x00000000c0003ef8 0x2cd build/interrupt.o + *fill* 0x00000000c00041c5 0x3 + .rodata 0x00000000c00041c8 0x6f build/timer.o + .rodata 0x00000000c0004237 0x41 build/debug.o + .rodata 0x00000000c0004278 0x138 build/memory.o + .rodata 0x00000000c00043b0 0x67 build/bitmap.o + *fill* 0x00000000c0004417 0x1 + .rodata 0x00000000c0004418 0xac build/string.o + .rodata 0x00000000c00044c4 0x2cf build/thread.o + *fill* 0x00000000c0004793 0x1 + .rodata 0x00000000c0004794 0x139 build/sync.o + +.rodata1 + *(.rodata1) + +.eh_frame_hdr + *(.eh_frame_hdr) + *(.eh_frame_entry .eh_frame_entry.*) + +.eh_frame 0x00000000c00048d0 0xad4 + *(.eh_frame) + .eh_frame 0x00000000c00048d0 0xa8 build/main.o + .eh_frame 0x00000000c0004978 0x24 build/init.o + 0x50 (松开之前的大小) + .eh_frame 0x00000000c000499c 0x1b4 build/interrupt.o + 0x1e0 (松开之前的大小) + .eh_frame 0x00000000c0004b50 0x94 build/timer.o + 0xd4 (松开之前的大小) + .eh_frame 0x00000000c0004be4 0x1c build/debug.o + 0x48 (松开之前的大小) + .eh_frame 0x00000000c0004c00 0x13c build/memory.o + 0x17c (松开之前的大小) + .eh_frame 0x00000000c0004d3c 0x90 build/bitmap.o + 0xbc (松开之前的大小) + .eh_frame 0x00000000c0004dcc 0x168 build/string.o + 0x194 (松开之前的大小) + .eh_frame 0x00000000c0004f34 0x174 build/thread.o + 0x1b4 (松开之前的大小) + .eh_frame 0x00000000c00050a8 0x148 build/list.o + 0x188 (松开之前的大小) + .eh_frame 0x00000000c00051f0 0xd8 build/console.o + 0x118 (松开之前的大小) + .eh_frame 0x00000000c00052c8 0xdc build/sync.o + 0x11c (松开之前的大小) + *(.eh_frame.*) + +.gcc_except_table + *(.gcc_except_table .gcc_except_table.*) + +.gnu_extab + *(.gnu_extab*) + +.exception_ranges + *(.exception_ranges .exception_ranges*) + 0x00000000c0007000 . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)) + +.eh_frame + *(.eh_frame) + *(.eh_frame.*) + +.gnu_extab + *(.gnu_extab) + +.gcc_except_table + *(.gcc_except_table .gcc_except_table.*) + +.exception_ranges + *(.exception_ranges .exception_ranges*) + +.tdata + *(.tdata .tdata.* .gnu.linkonce.td.*) + +.tbss + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + +.preinit_array 0x00000000c0007000 0x0 + [!provide] PROVIDE (__preinit_array_start = .) + *(.preinit_array) + [!provide] PROVIDE (__preinit_array_end = .) + +.init_array 0x00000000c0007000 0x0 + [!provide] PROVIDE (__init_array_start = .) + *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)) + *(.init_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors) + [!provide] PROVIDE (__init_array_end = .) + +.fini_array 0x00000000c0007000 0x0 + [!provide] PROVIDE (__fini_array_start = .) + *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)) + *(.fini_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .dtors) + [!provide] PROVIDE (__fini_array_end = .) + +.ctors + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT_BY_NAME(.ctors.*)) + *(.ctors) + +.dtors + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT_BY_NAME(.dtors.*)) + *(.dtors) + +.jcr + *(.jcr) + +.data.rel.ro + *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) + *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) + +.dynamic + *(.dynamic) + +.got 0x00000000c0007000 0x0 + *(.got) + .got 0x00000000c0007000 0x0 build/main.o + *(.igot) + 0x00000000c0007ff4 . = DATA_SEGMENT_RELRO_END (., (SIZEOF (.got.plt) >= 0xc)?0xc:0x0) + +.got.plt 0x00000000c0007000 0xc + *(.got.plt) + .got.plt 0x00000000c0007000 0xc build/main.o + 0x00000000c0007000 _GLOBAL_OFFSET_TABLE_ + *(.igot.plt) + .igot.plt 0x00000000c000700c 0x0 build/main.o + +.data 0x00000000c000700c 0x8c + *(.data .data.* .gnu.linkonce.d.*) + .data 0x00000000c000700c 0x0 build/main.o + .data 0x00000000c000700c 0x0 build/init.o + .data 0x00000000c000700c 0x0 build/interrupt.o + .data 0x00000000c000700c 0x0 build/timer.o + .data 0x00000000c000700c 0x84 build/kernel.o + 0x00000000c000700c intr_entry_table + .data 0x00000000c0007090 0x8 build/print.o + .data 0x00000000c0007098 0x0 build/debug.o + .data 0x00000000c0007098 0x0 build/memory.o + .data 0x00000000c0007098 0x0 build/bitmap.o + .data 0x00000000c0007098 0x0 build/string.o + .data 0x00000000c0007098 0x0 build/thread.o + .data 0x00000000c0007098 0x0 build/list.o + .data 0x00000000c0007098 0x0 build/console.o + .data 0x00000000c0007098 0x0 build/sync.o + +.data1 + *(.data1) + 0x00000000c0007098 _edata = . + [!provide] PROVIDE (edata = .) + 0x00000000c0007098 . = . + 0x00000000c0007098 __bss_start = . + +.bss 0x00000000c00070a0 0x2b8 + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + .bss 0x00000000c00070a0 0x0 build/main.o + .bss 0x00000000c00070a0 0x0 build/init.o + .bss 0x00000000c00070a0 0x108 build/interrupt.o + .bss 0x00000000c00071a8 0x0 build/timer.o + .bss 0x00000000c00071a8 0x0 build/debug.o + .bss 0x00000000c00071a8 0x0 build/memory.o + .bss 0x00000000c00071a8 0x0 build/bitmap.o + .bss 0x00000000c00071a8 0x0 build/string.o + .bss 0x00000000c00071a8 0x4 build/thread.o + .bss 0x00000000c00071ac 0x0 build/list.o + .bss 0x00000000c00071ac 0x1c build/console.o + .bss 0x00000000c00071c8 0x0 build/sync.o + *(COMMON) + *fill* 0x00000000c00071c8 0x18 + COMMON 0x00000000c00071e0 0x124 build/interrupt.o + 0x00000000c00071e0 intr_name + 0x00000000c0007280 idt_table + COMMON 0x00000000c0007304 0x4 build/timer.o + 0x00000000c0007304 ticks + COMMON 0x00000000c0007308 0x2c build/memory.o + 0x00000000c0007308 user_pool + 0x00000000c0007318 kernel_addr + 0x00000000c0007324 kernel_pool + COMMON 0x00000000c0007334 0x24 build/thread.o + 0x00000000c0007334 thread_ready_list + 0x00000000c0007344 thread_all_list + 0x00000000c0007354 main_thread + 0x00000000c0007358 . = ALIGN ((. != 0x0)?0x4:0x1) + 0x00000000c0007358 . = ALIGN (0x4) + 0x00000000c0007358 . = SEGMENT_START ("ldata-segment", .) + 0x00000000c0007358 . = ALIGN (0x4) + 0x00000000c0007358 _end = . + [!provide] PROVIDE (end = .) + 0x00000000c0007358 . = DATA_SEGMENT_END (.) + +.stab + *(.stab) + +.stabstr + *(.stabstr) + +.stab.excl + *(.stab.excl) + +.stab.exclstr + *(.stab.exclstr) + +.stab.index + *(.stab.index) + +.stab.indexstr + *(.stab.indexstr) + +.comment 0x0000000000000000 0x29 + *(.comment) + .comment 0x0000000000000000 0x29 build/main.o + 0x2a (松开之前的大小) + .comment 0x0000000000000029 0x2a build/init.o + .comment 0x0000000000000029 0x2a build/interrupt.o + .comment 0x0000000000000029 0x2a build/timer.o + .comment 0x0000000000000029 0x2a build/debug.o + .comment 0x0000000000000029 0x2a build/memory.o + .comment 0x0000000000000029 0x2a build/bitmap.o + .comment 0x0000000000000029 0x2a build/string.o + .comment 0x0000000000000029 0x2a build/thread.o + .comment 0x0000000000000029 0x2a build/list.o + .comment 0x0000000000000029 0x2a build/console.o + .comment 0x0000000000000029 0x2a build/sync.o + +.debug + *(.debug) + +.line + *(.line) + +.debug_srcinfo + *(.debug_srcinfo) + +.debug_sfnames + *(.debug_sfnames) + +.debug_aranges + *(.debug_aranges) + +.debug_pubnames + *(.debug_pubnames) + +.debug_info + *(.debug_info .gnu.linkonce.wi.*) + +.debug_abbrev + *(.debug_abbrev) + +.debug_line + *(.debug_line .debug_line.* .debug_line_end) + +.debug_frame + *(.debug_frame) + +.debug_str + *(.debug_str) + +.debug_loc + *(.debug_loc) + +.debug_macinfo + *(.debug_macinfo) + +.debug_weaknames + *(.debug_weaknames) + +.debug_funcnames + *(.debug_funcnames) + +.debug_typenames + *(.debug_typenames) + +.debug_varnames + *(.debug_varnames) + +.debug_pubtypes + *(.debug_pubtypes) + +.debug_ranges + *(.debug_ranges) + +.debug_macro + *(.debug_macro) + +.debug_addr + *(.debug_addr) + +.gnu.attributes + *(.gnu.attributes) + +/DISCARD/ + *(.note.GNU-stack) + *(.gnu_debuglink) + *(.gnu.lto_*) +OUTPUT(build/kernel.bin elf32-i386) diff --git a/c10/keyboard/build/kernel.o b/c10/keyboard/build/kernel.o new file mode 100644 index 0000000000000000000000000000000000000000..32eebd5a18a311b1fa92289f8b7fe8d82c8456db GIT binary patch literal 3232 zcmbVOO>9(E7`^it`bW!5vA0yH?P!W(g5$heYeiy4BLq!D2oQs&kq*olTF0gtUd&)L z3{hFIAjA-(F(kkOe-@@8XyQU*h;do5an{tyzkPv)4TFZ&YbVO zbKjkQoj2#sq0Qr45{ZPel~BvnqEad^#z>vlt2X%mr?@9!@2)|--LO$o`c_%jB03!? zb<*C1YiYUO1#Y;k=9VkIqhol1Q?IhUC~HJ_PQMsxAPrDh{xG$&g4y4eVf=D%a%qh@27(VaAoH5*Bz`GH)k z7-O2fLoe`n?M4sLo4>X6Z|N|4J-zQd-U_2P z(EH5ebsC+c_pZn5GMZnsYp;2{l}3-zd(q>ijNU}=NspH{I#2IDkJoMVGxS`Z~dVl@9boPFuXX*Xo@$NA?qW1;7n zLmn?NGFS>)T#w@y0D$7cC_0_z4Ng+1wR8n29 zzlFWvo&?OtK#js=X2lgKK0eH^xB|5UO1?nuCt5#zF^XQq6aRurk8fK-{KcQi6T73l#Gt}xo&@fM}zKMp}YBjT+ zIcs$IrD(wbj$m%xo)cEzfL=T^0@ZCTa2iRMGo|vwF=cnEsbaJTPj0!Sre>nbo)TUTIv1)_QQgW_bGf=}Rp_W;M?K=G z5l21hs8L5f=BP18J?^N2qxL##pQDP7Dmm)qMin~qh0c7TGhgV;7drEW&U~RWU+Byi zI`f6je4#U6=*$;7^M%fQp)+6T%r`9a+2_G>0?$_xg3KQa5c9uXcB-XxqCW{z<^k390Y@OI%{!n=hhgeQfI!aP!Ve_41|I1;W3GxxZz zCJd_Ay?9OFhv`P)ox=6~aQz+8r-i=~z9jsQa5uiE+~*s_+S6j8U)KUvH~- literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/list.o b/c10/keyboard/build/list.o new file mode 100644 index 0000000000000000000000000000000000000000..f9c18d2c085b70d5b9c37591ef36702902fb6f99 GIT binary patch literal 2884 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 literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/memory.o b/c10/keyboard/build/memory.o new file mode 100644 index 0000000000000000000000000000000000000000..7a6b310b44993bb440ab9a6c29f834ecb559a0fe GIT binary patch literal 5008 zcma)Ae{7Uz9sk}B=#SmtsEqx>?LZ(vxm&C9jD(n5Q^T;J=8 zQYRiHJI^MXP0g05i&Nub;(t0!{L!x4V39<~n4-bhn7Z8HqPL|n8raUy=Xsy!?!BXA zKJfPQe1AUQ=lgu0FAsgn>e=t}`NRyL1f)ud6vD2gL&0WQC1E9R1Wmmre6id=9?uq6 z7jBlzP8G|Ut!K%; z-krBf)4jf2tdx%xeHXLE;4xosoe5NSS)ue>E<4}V2-VrYhkO@v)?yxizj*VFIRut1 zhSeg=7;N5ZoGmnOl+qr_y`qTF#&SSpcHZy1xJ{%>a@Nhfbvw&7%j9Nx50pJxbsL9M zY!J~_cea?FS}bQ8+?{R7#}FNKu%pK+O5mssEb0xEusO2|oS*WTFhH_S^oLSV% z1d+fRs*tyedCAWE&dfMZ=BD~G{TRq1YYEAsks7jstO`ol;?Ia4THFm4vIQwtS0TTrfQsfFIc9d4pd;r<{W z>Kc@B4bIGDe$5lrMO{~P${0mgTXW*vjhJTVL&$FLF{zdZ(q6e9@YLa{F2p&VeUxc2 zzSK<)Ba+2sf{;*kaN25>P;dxnwh$W3S=Uy+Y+W0?RKT>LrmYs|Z)08%!kDmWjznhRIW6))Et;v%j? z!*SKmbp_>xrkT7ozfF$(dk+@W@}S!ChXxIuB|W3*PX&x6Ir6mF5kv z=2^J|rHs-+mE#&L7EZg@Kxu|}`Ao67K4snJttlrF*%WD;Y;#BZ7CD|sCle!|7)^|h zrB6irg&OHZDiL?qN$RNGpL8OhinK*GZHj1WcVv5KN1H?MCW%y=Tbq;XYHZwkZOkOsz* ziDn_MCc+AXazvbpA5GX!yl*7INJK{CBO_z|HrbL7ZkWrj-7qHy_U+pl*&OT3B%Ms; z;pmQN+qUf)MYVsWy))X@vBl+L|EuwF_`dX*_)a$Z?hgfnPvcA?;H{+q<2nA=yQ1-o zzx&Q*k7NV>Z@o4LH6`!T@||9tiPtDr@+08CiEoov-sj3A;C&U}fAPif)vWP^yG8`; zAL08vV%mkzs}tDnQ%cl1kMG-fdi)va)$w1d`YA$`42XQTUZ=aN6UG{6p|g+A7|)&q zyZn{4I-qj{?eI{&pB~o_pG=1tD+bb_26-cOJT0n#7Ara^@0Ke9Oal1h9RWcAo)M7c z!g}W5dHJEwvo*OT?HvJWko&f~mr@w1;^t+%Q9eW2%H_ud}+;QsxOTZimJd%JrqTTE~5k0&d0 z2I(a1l(VF(_@tx;Txlwv9PYQrQ^QHdI1W2+^J4&}jiVWkODf}_Pp1_QCmpjMyKAM* zim+<^YeeuQl;*odb^u%Pw%i2_-Xk)k=_KiUMV`|13qU_E(2s$%`-`So1EhU88~SYk zlD-Q_e>;ir)2HP_T7Hr=8t&JL_n{VnO${Qy(D+-8uL6S~#s85s{hr1f8hz+nl&=N0 zenjL^(hp!gApH$%%xL^F<vzH zE@}B+Nv{`qN6Y`E<^R$0AlfM9VIb}Ce>`QsUelX3-40}2pCk?a&uIBUEkCT~16rQa z@(C^f8s*sE)1*Hp@+@iidtTEol1APAM&s)m|ETdQkah(i@%?3&3hA&d2azy zeh-lH-J0&!bcA(^GkiqjfX34rf2#3<#*)U>KG&{O;}?i%v?=19P>PP97{$8;=%ig7 zGP87IBXX^9&-r_qD%A;TzqA;lnnhP+CMfrn!vS=CdZsa^uXt0 z+wfxRKMu!OEiq&drsJati5^XlWl{+5{~h0j;wq1F2j=V%><|3+5@n;ldcED?8$Dcu ziXN_j`aKEKuLkpe&=8m-eQMA*f0xpwDEvT9OE@R4iz}l1(f>w#v>}h<8ILd0#$PMu z)c-KOHf@8Y)OZ&Dc*bUMzZ`rEkiA?hUOKs^ zcS57#e*iu2FzZf($+_7FH_x7GhKZHGJn-hZgvZWd@R(=58>q*;)%XH^5=Omn&bJY+ ivC<0z>C5C8Q@Ma=&UVkbX3CoK>U|o<-=~2dM(=-OZNq{9 literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/print.o b/c10/keyboard/build/print.o new file mode 100644 index 0000000000000000000000000000000000000000..8bef927370394424e7db9b716cec3690f153cb33 GIT binary patch literal 1872 zcmbVLUuauZ7(cn~mhPX~!*l~hD+HYgX)4nRia4^g42x;k;us32H_16|=xuJe_e3@* zEOH-ATF^Iroq}&Z=#zGZPMhIW5ZqMgs}&&{HyxrbDvQ7G{z*=Y_}~ZUe!t)M{l4#f z=j7xiCWqn@;)>RFH>tw)0dQs>XgR#*tM=mNk42)FI(o}@NF1Y)!XkY620G8e_4Z-cG333s97WN8e1 z1cds~KIHXtfo`+usnO3?UP|s?-dF1|wSTHb&Acmvnf8sdFE(f8hez0JEqUqdf%opZ zDKC99@KgPY&k_NXjpoWkGFj^!P*2x7d)4t;XGj_Cd*pNS+IM2^llZXAkc-P}?e?r6 zt0(#)nUSyj^<{i%OpaccjZL{e$>blu%SXPFUUM=?J^J_RN4SXfi?x01GQ;QBch*>f zvDHZoD&*CH;kef{wtkaqwYWBC{Wb=~)WMwf&6{R(w(*CZVfgzwzkH-?vP3>zfQ{WS|_c3&b5vA(^IW)?{vGk zr|}>K)lwTESgAaq#dkXAUcO-(okFhcM{`)f6GK_*(iH%)^&r zff?)#fL9}m{JT5&r{HgD`4;d+jT<=d+ZsO%zOL~k_!k;~68r~^p8=;Hfni?YgvOVE z@7DM-@UX_;0)9~A9{@k3@y~%D*Z3yzag9F{Z4G}A=P$zFAI6C1plxgX2Ke(~J>{8I z%8x7vr!1_Iic>19)HItp#m7(1u+x(hX*Qjm$Yy48r}+z~PG)ngQdJxxp07$N9G~LF zMLsoW+vOfXg4-{Ls=~Y($OxoLF3+2V1-D`rgs=Cb)ZA9{S_KKMQdw{*M6oYKv|u_; z$($415vuA0n?|j7naLTgvB-2T3<&N-&)_KD1o=$pqx?VaRhs|+ literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/string.o b/c10/keyboard/build/string.o new file mode 100644 index 0000000000000000000000000000000000000000..51583620cd536c4493fb0eef68f1ad792a90c0c5 GIT binary patch literal 4000 zcmb7GTWpj?6rR7kZMRTtp(xkjHbDX^OADp6225L6Afa3WY(yhjw%aW&?QZK{q#zgL z7U^cG)}-+gF}x6;h|$C}JgC7ILwqCXi-<_DDshdLWKBpUsoyvMO#gqkRCJQrZ_b== z&Y3xL=KOoo+q~H@456DL9Kw=>*gDJ9)JRw^%ETj5Ukuro`gu?C_oa94PfSb{a#7k> z+V2}n9(RdE<-j0U>>e0=k#NX0Fi1DY3rY!Bv4(u{gzHHgBZ>%5F zHYV=9yR^UwfxXSyeN6RRQg$H`e~JZW>4cM?JZ4t$20TNzVU%3 z14FvqYNWQq?VMb%H>UBL_PMZ2oH6AaPae<89gNEztTT7eC3mo7$__HMa8@>qqm7ph zi@K5cl3e*j@kK^2Q+W}AoX9Lpdb38-J8sxR@%cqC#Qz9KTyEy+DVs6FK8Rz;IFv$` zad62vV1Ip=61TCx>0R+|chLI4;4$f>c3LOa)za*E8sYQ0R*a;sChr(F2hQoPRt2_C zx_Nern~W>zbsYKUpv5H)|?A)bHp$xliJGM-d8J(3GyrL)p!Xo^qM@*s@qCoAtY8O|E^jIzJ1$&7N@BK)v3pO^l- zr}$TAmfIN5EHEFG8E&53X}ui2G(`IceeTv+-0!a3;NIbDZWdh~EvsVjXh-CrCn)S1 z_p)W~STtxc19}ZLEm{y*JUJF3916!mcyGX9&k+;DJ%TJL1G_>IQ`YnCQ7K1*7?2mh zdSZJtCeqHOqhia(jnBB3`&tr_c*4ETv({6!a!o=?)hDX!JXJL-%vnTgE;^fWbhj{$ zxr{}nPG>(ViW_r_sX%MzPvax5Qw2@43O6Mk1qZ$w1x<1ql>X&>PQ>D{y52;8FQ4<4 z#p#FcF#LArbAlG<0(7^*3FLF0w>Vd!+gKpPoB5p67Kgci4IG3u_grljXFmKiID|lG zb2(iWXFWKdg7a!Vr`O^f0H?+&L~lN)$K;e_z7HHW&>q;7TI!tQ?u4D>5IBgfIr6MH zM4?S9@P#fCGcdz1E5E}A8G zp}WxOkFhw5uJx~Dd>&ovF`R4;$(j_g{}M~U$RST-O*y0$yKG3`oM9f%LA>6#)M8FS zvG~L$^FFTR$=4urEpwdfzXR%grhE&s&RxpCLe_ao`47lCCn^60S?3?+`;c|6QFhw$ zithB)vUEk zYjbSY*6KRu_xIM-dJcx-{+^&e-kykbdIG(|zomI+Q)9D#=jP3OynFq78=IQFe$f+% zbOimeo{osFek_70a;#;jg#ea5N@~)qclNqRCCi??^@7KQ;2*^qNa)9bB}n~c3RfxQ zzXIocAy8rk`3WHTr}`6aj$+AmkQKw%}2{F6ZP`L@Y_Tj@Vg z_@Tm&faL!MB>zt!`F|^Y0j`^VW-6=zlD`c|{w^T-ElS^^FskqW=%)lo{#GFQ+kxc!mA*}3x5C3f@;?KT|0R(8Z0Fdj9r zU5}!nE>Aqv8yB9|Ks+EkEwPyJM7vFa3eSVn-4pB%heHvpi*(0Bo-I3kD`W9MuoH`6 zG1TsFiw43W;W-%XPB0JuYkLL6**06{0NuKeH;&!)@OpEQA}zS*spJCy%K}TRL_aie5}q*!=1tbHL8K42Q3A Ifohuf4}>jM3IG5A literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/switch.o b/c10/keyboard/build/switch.o new file mode 100644 index 0000000000000000000000000000000000000000..ad152e9b9abf6a2dd7d27fc0b9e7fe3c65801457 GIT binary patch literal 448 zcmb<-^>JflWMqH=Mh0dE1doBi0V-hvrZpH?8JJ*7Nuoh!f-oCYmjI9>ie!QSl+6yL znUTZ|fa(;H_#iVtKoE$LU6KHlZ~%(205M2Ehz6M{3FQMRFjxRoCk^8OX_$F1p|J4a z&~6tMkxm65DbcME8yz2a7$ym#^-5AJN*MHtGm1-!N)nTRtjb&n4dElJ#ll9mkA>mm ne`F<~kO%n-1_VH^E6FHIO-#`*F3&7U&d>{HfH2}q@*%1K)EOb^ literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/sync.o b/c10/keyboard/build/sync.o new file mode 100644 index 0000000000000000000000000000000000000000..2c1ace4bbf5936f43e3a1e541bf328e4c33243ed GIT binary patch literal 3980 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 Gt@mFMZ@yXp literal 0 HcmV?d00001 diff --git a/c10/keyboard/build/timer.o b/c10/keyboard/build/timer.o new file mode 100644 index 0000000000000000000000000000000000000000..4fbacb5f0c6e8482a6c46187d1de018df33c74db GIT binary patch literal 2584 zcma)7%THWo6hHF-hKl7OKCmPm6Ew69x6o2VBZfx@T!2gpql=2yVdlcjz}&$*_Zl!& zW5(KgtYmy4vh$U_Gf(OgwI(rH60)l;1v_rYdfHAX1+Q)b(@wO1;YD%h6~`SO99 zsa$9;8`!N~#<&^U`)%>NB8bmEGnUzIbZ{+SexfEnQkM@k=3jXrx04O*WGA+>BU{aOp=KU(%L7PQ-!pI0Zir~QszFS^mb-CQnkJCEPq&Yt>8@|!0M8AK>p$UMi8I9D9>yy8E1^n2BLgq`mLBH(Gp8b?_cDW8b5zA1` zn4yKEZBM4naAZbJhNt5Ze@~CUeg0fKq^qNYqS{OGh<4VDCpANfkd10wkDGqXG0caK zIbG3|uE9eT*G(knIchAdM`tyI^4^wWzPY7HLjwbk`&&bkmTp@9N0kdod)s--wmM$z z=vLZ0PnEq$Lyi1_xL&?Qt}8y*i5jmri$m579}kvO{OLRHyXx-0yFyzBgQyj*Cq(QW zXMFIQ=S^>>!hNmS_jTVlea!YF9>d_3Tg2GUbur^{XV;6I&=2=p7$d07+&efglsR>9 zFL4Zl7HnbPn$V?BnESwYMv9kyFG-!zgCD1OsKRL?T4~=wcJey7;W;jJ{=37AoK107CyE=5;_ChH!$QrHlTw~IUt1NUq}PUcKLwg z?58YqlBNvpMN88o3u;<3k2>e7I%Vk*RaH99J=R64I^W%;Olzh(7r||_^cf{QPwLR{ zXn)_ZIyyKw78qB@`}&6iD(XaL(nrmhoF~K5^|(GQE-cN3^>{>0&&730M`Bvkn$;Zl zkmf8?O`8T8+H^c^IxZpauDhP>T{_l4bY9rqK(^P}#-;mS*c(9ay+l6)t1;QW2$NvT(=erE#{81q1NpQT0=xw&~>b}eNgG3*T_ze;FBflH* zUV*a$7X^OCh$HqbBOgU2y^u7+lR(obV@zs>HmjK0yh%zlY=%jhOs7dPQe}e!%F&x; zPdX}wZBZhrWKz?SQ%{+iGW2w)&3;Oe7}d0xI%R~D8Y$C8%9=xa|5x4v5!mVKXh5;~R?uSHod0zPMl5UHMk%{RPP_;>K!EJ5eG{E~28&i^n5 z&ylaoUoruFEYds!o%#R8YZr5{)wb**f%BeoT^2s4oV!vGXMi%saani#5HIyFJl+$Y z!}VFx;GHh3_LpNk76jT(d_EBMr7i0g1W$&qL-|pXfU?&dbmux>$Hs6Eyw3cB%5~> 8); +} + +// 时钟的中断处理函数 +static void intr_timer_handler(void) { + struct task_struct* cur_thread = running_thread();// 获取当前线程 + + ASSERT(cur_thread->stack_magic == 0x20000611);// 检查栈是否溢出 + + cur_thread->elapsed_ticks++; // + //从内核第一次处理时间中断后开始至今的滴哒数,内核态和用户态总共的嘀哒数 + ticks++; + + if(cur_thread->ticks == 0) {// 时间片用完,那就调度新的线程 + schedule(); + }else { // 否则将时间片 -1 + cur_thread->ticks--; + } + +} + +// 初始化 8253 +void timer_init(void) { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + register_handler(0x20, intr_timer_handler);// 将时钟中断置为 0x20 + put_str("timer_init done!\n"); +} + + diff --git a/c10/keyboard/device/timer.h b/c10/keyboard/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c10/keyboard/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c10/keyboard/include/boot.inc b/c10/keyboard/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c10/keyboard/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c10/keyboard/kernel/debug.c b/c10/keyboard/kernel/debug.c new file mode 100755 index 0000000..bef6acf --- /dev/null +++ b/c10/keyboard/kernel/debug.c @@ -0,0 +1,18 @@ +#include "debug.h" +#include "kernel/print.h" +#include "interrupt.h" + +/* 打印文件名,行号,函数名,条件并使程序悬停 */ +void panic_spin(char* filename, \ + int line, \ + const char* func, \ + const char* condition) \ +{ + intr_disable(); /* 打印文件名,行号,函数名,条件并使程序悬停 */ + put_str("\n\n\n!!!!! error !!!!!\n"); + put_str("filename:");put_str(filename);put_str("\n"); + put_str("line:0x");put_int(line);put_str("\n"); + put_str("function:");put_str((char*)func);put_str("\n"); + put_str("condition:");put_str((char*)condition);put_str("\n"); + while(1); +} \ No newline at end of file diff --git a/c10/keyboard/kernel/debug.h b/c10/keyboard/kernel/debug.h new file mode 100755 index 0000000..d4df982 --- /dev/null +++ b/c10/keyboard/kernel/debug.h @@ -0,0 +1,24 @@ +#ifndef __KERNEL_DEBUG_H +#define __KERNEL_DEBUG_H + +void panic_spin(char* filename, int line, const char* func, const char* condition); + +/*************************** __VA_ARGS__ ******************************* + * __VA_ARGS__ 是预处理器所支持的专用标识符。 + * 代表所有与省略号相对应的参数. + * "..."表示定义的宏其参数可变. + */ +#define PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) +/***********************************************************************/ + +#ifdef NDEBUG + #define ASSERT(CONDITION) ((void)0) +#else + #define ASSERT(CONDITION) \ + if (CONDITION) {} else { \ + /* 符号#让编译器将宏的参数转化为字符串字面量 */ \ + PANIC(#CONDITION); \ + } +#endif // __NDEBUG + +#endif // __KERNEL_DEBUG_H \ No newline at end of file diff --git a/c10/keyboard/kernel/global.h b/c10/keyboard/kernel/global.h new file mode 100755 index 0000000..7330902 --- /dev/null +++ b/c10/keyboard/kernel/global.h @@ -0,0 +1,39 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#define NULL ((void*)0) +#define bool int +#define true 1 +#define false 0 + +#endif \ No newline at end of file diff --git a/c10/keyboard/kernel/init.c b/c10/keyboard/kernel/init.c new file mode 100755 index 0000000..4714c27 --- /dev/null +++ b/c10/keyboard/kernel/init.c @@ -0,0 +1,18 @@ +#include "init.h" +#include "print.h" +#include "interrupt.h" +#include "timer.h" +#include "memory.h" +#include "thread.h" +#include "console.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init();// 初始化中断 + + mem_init();//初始化内存管理系统 + thread_environment_init();// 初始化线程相关环境 + timer_init(); // 初始化 PIT + console_init(); // 初始化 console +} \ No newline at end of file diff --git a/c10/keyboard/kernel/init.h b/c10/keyboard/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c10/keyboard/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c10/keyboard/kernel/interrupt.c b/c10/keyboard/kernel/interrupt.c new file mode 100755 index 0000000..cb73d05 --- /dev/null +++ b/c10/keyboard/kernel/interrupt.c @@ -0,0 +1,192 @@ +#include "interrupt.h" +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" + +#define PIC_M_CTRL 0x20 // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define IDT_DESC_CNT 0x21 // 目前总共支持的中断数 + +#define EFLAGS_IF 0x00000200 // eflags寄存器中的if位为1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +/*中断门描述符结构体*/ +struct gate_desc { + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; //此项为双字计数字段,是门描述符中的第4字节。此项固定值,不用考虑 + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 静态函数声明,非必须 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // idt是中断描述符表,本质上就是个中断门描述符数组 + +char* intr_name[IDT_DESC_CNT]; // 用于保存异常的名字 +intr_handler idt_table[IDT_DESC_CNT]; // 定义中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口,最终调用的是ide_table中的处理程序 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; // 声明引用定义在kernel.S中的中断处理函数入口数组 + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + + /* 初始化主片 */ + outb (PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb (PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27. + outb (PIC_M_DATA, 0x04); // ICW3: IR2接从片. + outb (PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb (PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb (PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb (PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb (PIC_S_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 打开主片上IR0,也就是目前只接受时钟产生的中断 */ + outb (PIC_M_DATA, 0xfe); + outb (PIC_S_DATA, 0xff); + + put_str(" pic_init done\n"); +} + +/* 创建中断门描述符 */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16; +} + +/*初始化中断描述符表*/ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++) { + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if (vec_nr == 0x27 || vec_nr == 0x2f) { // 0x2f是从片8259A上的最后一个irq引脚,保留 + return; //IRQ7和IRQ15会产生伪中断(spurious interrupt),无须处理。 + } + /* 将光标置为0,从屏幕左上角清出一片打印异常信息的区域,方便阅读 */ + set_cursor(0); + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + + set_cursor(0); // 重置光标为屏幕左上角 + put_str("!!!!!!! excetion message begin !!!!!!!!\n"); + set_cursor(88); // 从第2行第8个字符开始打印 + put_str(intr_name[vec_nr]); + if (vec_nr == 14) { // 若为Pagefault,将缺失的地址打印出来并悬停 + int page_fault_vaddr = 0; + asm ("movl %%cr2, %0" : "=r" (page_fault_vaddr)); // cr2是存放造成page_fault的地址 + put_str("\npage fault addr is ");put_int(page_fault_vaddr); + } + put_str("\n!!!!!!! excetion message end !!!!!!!!\n"); + // 能进入中断处理程序就表示已经处在关中断情况下, + // 不会出现调度进程的情况。故下面的死循环不会再被中断。 + while(1); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++) { + +/* idt_table数组中的函数是在进入中断后根据中断向量号调用的, + * 见kernel/kernel.S的call [idt_table + %1*4] */ + idt_table[i] = general_intr_handler; // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; + +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if (INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断,sti指令将IF位置1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if (INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli指令将IF位置0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/* 在中断处理程序数组第vector_no个元素中注册安装中断处理程序function */ +void register_handler(uint8_t vector_no, intr_handler function) { +/* idt_table数组中的函数是在进入中断后根据中断向量号调用的, + * 见kernel/kernel.S的call [idt_table + %1*4] */ + idt_table[vector_no] = function; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); // 异常名初始化并注册通常的中断处理函数 + pic_init(); // 初始化8259A + + /* 加载idt */ + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m" (idt_operand)); + put_str("idt_init done\n"); +} diff --git a/c10/keyboard/kernel/interrupt.h b/c10/keyboard/kernel/interrupt.h new file mode 100755 index 0000000..427f7b0 --- /dev/null +++ b/c10/keyboard/kernel/interrupt.h @@ -0,0 +1,22 @@ +#ifndef __KERNEL_INTERRUPT_H +#define __KERNEL_INTERRUPT_H +#include "stdint.h" + +typedef void* intr_handler; +void idt_init(void); + +/* 定义中断的两种状态: + * INTR_OFF值为0,表示关中断, + * INTR_ON值为1,表示开中断 */ +enum intr_status { // 中断状态 + INTR_OFF, // 中断关闭 + INTR_ON // 中断打开 +}; + +enum intr_status intr_get_status(void); +enum intr_status intr_set_status (enum intr_status); +enum intr_status intr_enable (void); +enum intr_status intr_disable (void); +void register_handler(uint8_t vector_no, intr_handler function); + +#endif \ No newline at end of file diff --git a/c10/keyboard/kernel/kernel.S b/c10/keyboard/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c10/keyboard/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c10/keyboard/kernel/main.c b/c10/keyboard/kernel/main.c new file mode 100755 index 0000000..100e91c --- /dev/null +++ b/c10/keyboard/kernel/main.c @@ -0,0 +1,58 @@ +#include "kernel/print.h" +#include "init.h" +#include "debug.h" +#include "memory.h" +#include "thread.h" +#include "interrupt.h" +#include "console.h" + +void k_thread_HuSharp_1(void* args); +void k_thread_HuSharp_2(void* args); +void k_thread_HuSharp_3(void* args); +int main(void) { + put_str("I am kernel!\n"); + init_all(); + //ASSERT(1 == 2); + // asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + + // 进行内存分配 + /* 内核物理页分配 + void* addr = get_kernel_pages(3); + put_str("\n get_kernel_page start vaddr is: "); + put_int((uint32_t) addr); + put_str("\n"); + */ + + // 线程演示 + thread_start("k_thread_HuSharp_1", 31, k_thread_HuSharp_1, "agrA 31 "); + thread_start("k_thread_HuSharp_2", 8, k_thread_HuSharp_2, "agrB 8 "); + thread_start("k_thread_HuSharp_3", 20, k_thread_HuSharp_3, "agrc 20 "); + + intr_enable();// 打开时钟中断 + + while(1) { + console_put_str("Main "); + } + return 0; +} + +void k_thread_HuSharp_1(void* args) { + char* para = args; + while(1) { + console_put_str(para); + } +} + +void k_thread_HuSharp_2(void* args) { + char* para = args; + while(1) { + console_put_str(para); + } +} + +void k_thread_HuSharp_3(void* args) { + char* para = args; + while(1) { + console_put_str(para); + } +} \ No newline at end of file diff --git a/c10/keyboard/kernel/memory.c b/c10/keyboard/kernel/memory.c new file mode 100755 index 0000000..afbb653 --- /dev/null +++ b/c10/keyboard/kernel/memory.c @@ -0,0 +1,257 @@ +#include "memory.h" +#include "stdint.h" +#include "kernel/print.h" +#include "kernel/bitmap.h" +#include "global.h" +#include "debug.h" +#include "kernel/print.h" +#include "string.h" + +#define PG_SIZE 4096 + +/*************** 位图地址 ******************** + * 因为0xc009f000是内核主线程栈顶,0xc009e000是内核主线程的pcb. + * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000, + * 这样本系统最大支持4个页框的位图,即512M */ +#define MEM_BITMAP_BASE 0xc009a000 + +// 返回虚拟地址的 高 10 位(即pde),与中间 10 位(即pte) +#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) +#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) + +/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存, + * 使虚拟地址在逻辑上连续 */ +#define K_HEAP_START 0xc0100000 + +// 内存池结构,生成两个实例用于管理内核内存池和用户内存池 +struct pool { + // 本内存池用到的位图结构,用于管理物理内存 + struct bitmap pool_bitmap; + uint32_t phy_addr_start;// 本内存池所管理物理内存起始地址 + uint32_t pool_size; //本内存池字节容量 +}; + +struct pool kernel_pool, user_pool; // 生成全局内核内存池, 用户内存地址 +struct virtual_addr kernel_addr; // 给内核分配虚拟地址 + + +/* 在pf表示的虚拟内存池中申请pg_cnt个虚拟页, + * 成功则返回虚拟页的起始地址, 失败则返回NULL + */ +static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) { + int vaddr_start = 0;//用于存储分配的虚拟地址 + int bit_idx_start = -1;// 存储位图扫描函数的返回值 + uint32_t cnt = 0; + if(pf == PF_KERNEL) {// 说明在内核虚拟池中 + bit_idx_start = bitmap_scan(&kernel_addr.vaddr_bitmap, pg_cnt); + if(bit_idx_start == -1) {//说明没找到 + return NULL; + } + // 将已经选出的内存置为 1 ,表示已经使用 + while(cnt < pg_cnt) { + bitmap_set(&kernel_addr.vaddr_bitmap, bit_idx_start + cnt++, 1); + } + // 虚拟地址应当加上 页表所占的内存 + vaddr_start = kernel_addr.vaddr_start + bit_idx_start * PG_SIZE; + } else {// 用户进程池 还未完善 + + } + return (void*)vaddr_start; +} + +/********* 以下两个获取虚拟地址 addr 的 pde & pte 地址,为建立映射做准备 ********/ +// 得到虚拟地址 vaddr 对应的 pte 指针 +// 构造一个 访问该 pte 的32位地址,欺骗处理器 +uint32_t* pte_ptr(uint32_t vaddr) { + /* 先访问到页目录表自己(位于1023项目录项中) + \ + * 再用页目录项 pde (页目录内页表的索引)做为pte的索引访问到页表 + \ + * 再用 pte 的索引做为页内偏移 + */ + uint32_t* pte = (uint32_t*) (0xffc00000 + \ + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4); + return pte;// 得到物理页的虚拟地址,即通过这个虚拟地址,可以访问到该物理页(保护模式下必须通过 vaddr) +} + +// 得到虚拟地址 vaddr 对应的 pde 指针 +// 构造一个 访问该 pde 的32位地址,欺骗处理器 +uint32_t* pde_ptr(uint32_t vaddr) { + uint32_t* pde = (uint32_t*) (0xfffff000 + PDE_IDX(vaddr) * 4); + return pde; +} + +/* 在m_pool指向的物理内存池中分配1个物理页, + * 成功则返回页框的物理地址,失败则返回NULL + */ +static void* palloc(struct pool* m_pool) { + // 扫描或设置位图要保证原子操作 + int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); //找一个空闲物理页面 + if(bit_idx == -1) { + return NULL; + } + bitmap_set(&m_pool->pool_bitmap, bit_idx, 1); //表示已经占用 + // 成功则返回页框的物理地址 + uint32_t page_phyaddr = (m_pool->phy_addr_start + (bit_idx * PG_SIZE)); + return (void*)page_phyaddr; +} + +/********使用 pde_ptr 和 pte_ptr 来建立虚拟地址和物理地址的映射 ********/ +// 本质上便是将 pte 写入到 获取的 pde 项中,将 物理地址写入到 pte 中 +static void page_table_add(void* _vaddr, void* _page_phyaddr) { + uint32_t vaddr = (uint32_t)_vaddr; + uint32_t page_phyaddr = (uint32_t)_page_phyaddr; + // 以下两个函数都是通过 虚拟地址来获取 + uint32_t* pde = pde_ptr(vaddr); + uint32_t* pte = pte_ptr(vaddr); + +/************************ 注意 ************************* + * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte, + * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。 + * *********************************************************/ + // 先在页目录内判断目录项的P位,若为1,则表示该表(pte)已存在 + if(*pde & 0x00000001) {// 通过 P 位来判断目录项是否存在 + ASSERT(!(*pte & 0x00000001)); // 表示 pte 不存在 + + // 再判断 pte 是否存在 + if(!(*pte & 0x00000001)) { + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } else { // 并不会执行到此处,因为此处意思是:pde存在,pte也存在 + // 但是之前的 ASSERT 已经判断了 pde 不存在 + PANIC("pte repeat!"); + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } + } else {// pde 不存在 + // 需要分配 pte 物理页,采用 plloc 函数 + uint32_t new_pde_phyaddr = (uint32_t)palloc(&kernel_pool);//页表中的页框一律从内核中分配 + + *pde = (new_pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);// 写入到 pde 中 + + /* 分配到的物理页地址 new_pde_phyaddr 对应的物理内存清0,(使用时清零比回收后清零高效,因为不知道回收后会不会再使用) + * 避免里面的陈旧数据变成了页表项,从而让页表混乱. + * 访问到pde对应的物理地址,用pte取高20位便可. + * 因为pte是基于该pde对应的物理地址内再寻址, + * 把低12位置0便是该pde对应的物理页的起始 + */ + // 现需要获得新创建的这个物理页的虚拟地址(保护模式得看虚拟地址!) + memset(((void*)((int)pte & 0xfffff000)), 0, PG_SIZE);//将该新配物理页清0 + + ASSERT(!(*pte & 0x00000001));// 断言 pte 此时是否存在 + + // pte项 此时更改为新建物理页的位置 + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);//将 物理页地址 写入到 pte项中 + } +} + + +// 分配pg_cnt个页空间(物理页),成功则返回起始虚拟地址,失败时返回NULL +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) { + ASSERT(pg_cnt > 0 && pg_cnt < 3840); +/*********** malloc_page的原理是三个动作的合成: *********** + 1.通过vaddr_get在虚拟内存池中申请虚拟地址 + 2.通过palloc在物理内存池中申请物理页 + 3.通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射 +***************************************************************/ + void* vaddr_start = vaddr_get(pf, pg_cnt); + if(vaddr_start == NULL) { + return NULL; + } + + uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt; + struct pool* mem_pool = (pf & PF_KERNEL)? &kernel_pool : &user_pool; + + // 因为虚拟地址是连续的,但物理地址可以是不连续的,所以逐个做映射 + while(cnt-- > 0) { + void* page_phyaddr = palloc(mem_pool); + if(page_phyaddr == NULL) { + return NULL; + } + page_table_add((void*)vaddr, page_phyaddr);// 在页表建立映射 + vaddr += PG_SIZE;// 下一个虚拟页 + } + return vaddr_start; +} + +// 从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL +void* get_kernel_pages(uint32_t pg_cnt) { + void* vaddr = malloc_page(PF_KERNEL, pg_cnt); + if(vaddr != NULL) {// 虚拟地址是连续的 + memset(vaddr, 0, pg_cnt * PG_SIZE); + } + return vaddr; +} + +// 初始化内存池, 通过all_mem 初始化物理内存池相关结构 +static void mem_pool_init(uint32_t all_mem) { + put_str(" phy_mem_pool_init start!\n"); + // 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+ + // 第769~1022个页目录项共指向254个页表,共256个页框 + uint32_t page_table_size = PG_SIZE * 256; + // 用于记录当前已经使用的内存字节数 + uint32_t used_mem = page_table_size + 0x100000;// 0x100000为低端 1M 内存 + uint32_t free_mem = all_mem - used_mem; + uint16_t all_free_pages = free_mem / PG_SIZE;//转换为 页数 + + // 内核物理内存池 与 用户物理内存池 大小一致 + uint16_t kernel_free_pages = all_free_pages / 2; + uint16_t user_free_pages = all_free_pages - kernel_free_pages; + + /* 为简化位图操作,余数不处理,坏处是这样做会丢内存。 + * 好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存 + */ + uint32_t kbm_length = kernel_free_pages / 8; // Kernel BitMap的长度,位图中的一位表示一页,以字节为单位 + uint32_t ubm_length = user_free_pages / 8; // User BitMap的长度. + + uint32_t kp_start = used_mem; // kernel pool start 内核内存池的起始位置 + uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; + + kernel_pool.phy_addr_start = kp_start; + user_pool.phy_addr_start = up_start; + + kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;// 位图字节长度 + user_pool.pool_bitmap.btmp_bytes_len = ubm_length; + + kernel_pool.pool_size = kernel_free_pages * PG_SIZE; + user_pool.pool_size = user_free_pages * PG_SIZE; + +/********* 内核内存池和用户内存池位图 *********** + * 位图是全局的数据,长度不固定。 + * 全局或静态的数组需要在编译时知道其长度, + * 而我们需要根据总内存大小算出需要多少字节。 + * 所以改为指定一块内存来生成位图. + * ************************************************/ +// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右) +// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处. + kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE; + + // 用户内存池的位图紧跟在内核内存池位图后面 + user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length); + /******************** 输出内存池信息 **********************/ + put_str(" kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits); + put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start); + put_str("\n"); + put_str(" user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits); + put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start); + put_str("\n"); + + /**************** 进行初始化 ***************/ + // 将位图 置为 0 表示还未分配 + bitmap_init(&kernel_pool.pool_bitmap); + bitmap_init(&user_pool.pool_bitmap); + + // 初始化内核的虚拟地址 + // 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致 + kernel_addr.vaddr_bitmap.btmp_bytes_len = kbm_length; + // 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之后 + kernel_addr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); + // 虚拟内存池的起始地址,以达到进程在堆中动态申请内存 + kernel_addr.vaddr_start = K_HEAP_START; //内核堆 + bitmap_init(&kernel_addr.vaddr_bitmap); + put_str(" mem_pool_init done\n"); +} + +// 内存管理部分初始化入口 +void mem_init() { + put_str("mem_init start!\n"); + uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); // 储存机器上物理内存总量 + mem_pool_init(mem_bytes_total); //初始化内存池 + put_str("mem_init done!\n"); +} \ No newline at end of file diff --git a/c10/keyboard/kernel/memory.h b/c10/keyboard/kernel/memory.h new file mode 100755 index 0000000..405f8d7 --- /dev/null +++ b/c10/keyboard/kernel/memory.h @@ -0,0 +1,33 @@ +#ifndef __KERNEL_MEMORY_H +#define __KERNEL_MEMORY_H + +#include "stdint.h" +#include "bitmap.h" + +// 内存池的标记,用来判断使用哪个内存池 +enum pool_flags { + PF_KERNEL = 1, // 内核内存池 + PF_USER = 2 // 用户内存池 +}; + +#define PG_P_1 1 // 页表项或页目录项存在属性位 +#define PG_P_0 0 // 页表项或页目录项存在属性位 +#define PG_RW_R 0 // R/W 属性位值, 读/执行 +#define PG_RW_W 2 // R/W 属性位值, 读/写/执行 +#define PG_US_S 0 // U/S 属性位值, 系统级 +#define PG_US_U 4 // U/S 属性位值, 用户级 + +// 虚拟地址池,用于虚拟地址管理 +struct virtual_addr{ + struct bitmap vaddr_bitmap; // 虚拟地址用到的 bitmap 结构, 页 为单位 + uint32_t vaddr_start; // 虚拟地址起始地址 +}; + +extern struct pool kernel_pool, user_pool; +void mem_init(void); +void* get_kernel_pages(uint32_t pg_cnt); +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt); +void malloc_init(void); +uint32_t* pte_ptr(uint32_t vaddr); +uint32_t* pde_ptr(uint32_t vaddr); +#endif \ No newline at end of file diff --git a/c10/keyboard/lib/kernel/bitmap.c b/c10/keyboard/lib/kernel/bitmap.c new file mode 100755 index 0000000..323b793 --- /dev/null +++ b/c10/keyboard/lib/kernel/bitmap.c @@ -0,0 +1,82 @@ +#include "bitmap.h" +#include "stdint.h" +#include "string.h" +#include "print.h" +#include "interrupt.h" +#include "debug.h" + +// bitmap 的初始化 +void bitmap_init(struct bitmap* btmp) { + memset(btmp->bits, 0, btmp->btmp_bytes_len); +} + +// 判断 btmp 为指针处,bit_idx 位是否为 1,为 1 就返回 true +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) { + uint32_t byte_idx = bit_idx / 8;//用于标注字节 + uint32_t bit_odd = bit_idx % 8; //用于标注字节内的位 + return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd)); +} + +// 在位图中申请连续cnt个位,成功则返回其起始位下标,失败返回-1 +int bitmap_scan(struct bitmap* btmp, uint32_t cnt) { + uint32_t idx_byte = 0; // 用于记录空闲位所在的字节 + // 先逐字节比较,蛮力法 + // 0 表示空闲,若停止,则说明至少有一个空闲位 + while(( 0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) { + idx_byte++; + } + ASSERT(idx_byte < btmp->btmp_bytes_len);// 断言此时在内部 + if(idx_byte == btmp->btmp_bytes_len) { + return -1;// 访问失败,返回 -1 + } + + /* 若在位图数组范围内的某字节内找到了空闲位, + * 在该字节内逐位比对,返回空闲位的索引。 + */ + int idx_bit = 0;//字节内部位数 + while((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) { + idx_bit++; + }// 找到空闲位 + int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在bitmap 下标 + if(cnt == 1) { + return bit_idx_start; + } + + // 至此说明 cnt > 1 + // 因此首先进行剩余位数的判断,以免超过bitmap记录数 + uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start); + uint32_t idx_next_bit = bit_idx_start + 1; + uint32_t free_bit_cnt = 1;// 一定要记得记录一下 之前判断后 还有个空闲位 + + bit_idx_start = -1; // 先将其置为 -1,若找不到连续的位就直接返回 + while(bit_left-- > 0) { + // 调用 scan_test 函数,为 0 则为 false + if(!(bitmap_scan_test(btmp, idx_next_bit))) { + free_bit_cnt++; + } else { // 由于必须是连续的,因此只要遇到 不空闲的 位 便将记录cnt置为 0 + free_bit_cnt = 0; + } + // 若是已经满足空闲数 + if(free_bit_cnt == cnt) { + bit_idx_start = idx_next_bit - cnt + 1; + break; + } + idx_next_bit++;// 继续判断下一位 + } + + return bit_idx_start; +} + +// 将位图btmp的bit_idx位设置为value +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) { + ASSERT((value ==0) || (value == 1));// 只能赋予 0 或 1 + uint32_t byte_idx = bit_idx / 8; //字节位 + uint32_t bit_odd = bit_idx % 8; //位数 位 + + if(value) { + btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); + } else { + btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); + } + +} diff --git a/c10/keyboard/lib/kernel/bitmap.h b/c10/keyboard/lib/kernel/bitmap.h new file mode 100755 index 0000000..ab3d2db --- /dev/null +++ b/c10/keyboard/lib/kernel/bitmap.h @@ -0,0 +1,18 @@ +#ifndef __LIB_KERNEL_BITMAP_H +#define __LIB_KERNEL_BITMAP_H +#include "global.h" +#define BITMAP_MASK 1 // 用在位图中逐位判断,主要通过 按位与 & +struct bitmap{ + uint32_t btmp_bytes_len; + /* 在遍历位图时,整体上以字节为单位,细节上是以位为单位, + 所以此处位图的指针必须是单字节 */ + uint8_t* bits; +}; + +// 函数 +void bitmap_init(struct bitmap* btmp); +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx); +int bitmap_scan(struct bitmap* btmp, uint32_t cnt); +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value); + +#endif \ No newline at end of file diff --git a/c10/keyboard/lib/kernel/io.h b/c10/keyboard/lib/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c10/keyboard/lib/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c10/keyboard/lib/kernel/list.c b/c10/keyboard/lib/kernel/list.c new file mode 100755 index 0000000..0635cc3 --- /dev/null +++ b/c10/keyboard/lib/kernel/list.c @@ -0,0 +1,95 @@ +#include "list.h" +#include "interrupt.h" + +// 初始化双向链表 +void list_init (struct list* list) { + list->head.prev = NULL; + list->tail.next = NULL; + + list->head.next = &list->tail; + list->tail.prev = &list->head; +} + +// 将链表元素 before 插入在元素 elem 前 +void list_insert_before(struct list_elem* elem, struct list_elem* before) { + // 由于队列是公共资源,因此要保证为原子操作 + enum intr_status old_status = intr_disable();//关中断 + + // 更新节点 + elem->prev->next = before; + before->prev = elem->prev; + elem->prev = before; + before->next = elem; + + intr_set_status(old_status); +} + +void list_append(struct list* plist, struct list_elem* elem) { + list_insert_before(&plist->tail, elem);// 队列的FIFO +} + +void list_remove(struct list_elem* pelem) { + enum intr_status old_status = intr_disable(); + + pelem->prev->next = pelem->next; + pelem->next->prev = pelem->prev; + + intr_set_status(old_status);// 返回原状态 +} + +void list_push(struct list* plist, struct list_elem* elem) { + list_insert_before(plist->head.next, elem); +} + +// pop 操作,在唤醒时进行关中断 +struct list_elem* list_pop(struct list* plist) { + struct list_elem* elem = plist->head.next; + list_remove(elem); + return elem; +} + +// 查找元素 +bool elem_find(struct list* plist, struct list_elem* obj_elem) { + struct list_elem* elem = plist->head.next; + while(elem != &plist->tail) { + if(elem == obj_elem) { + return true; + } + elem = elem->next; + } + return false; +} + +// 遍历列表的所有元素,判断是否有 elem 满足条件 +// 判断方法采用 func 回调函数进行判断 +struct list_elem* list_traversal(struct list* plist, function func, int arg) { + struct list_elem* elem = plist->head.next; + // 如果队列为空 直接 return + if(list_empty(plist)) { + return NULL; + } + while(elem != &plist->tail) { + if(func(elem, arg)) { + return elem; + } + elem = elem->next; + } + return NULL; +} + +// 判断空 +bool list_empty(struct list* plist) { + return (plist->head.next == &plist->tail ? true : false); +} + +uint32_t list_len(struct list* plist) { + struct list_elem* elem = plist->head.next; + uint32_t len = 0; + while(elem != &plist->tail) { + elem = elem->next; + len++; + } + return len; +} + + diff --git a/c10/keyboard/lib/kernel/list.h b/c10/keyboard/lib/kernel/list.h new file mode 100755 index 0000000..3bd6ba0 --- /dev/null +++ b/c10/keyboard/lib/kernel/list.h @@ -0,0 +1,40 @@ +#ifndef __LIB_KERNEL_LIST_H +#define __LIB_KERNEL_LIST_H +#include "global.h" + + +// 令基址为 0 ,那么偏移量就等于 结构体中元素的偏移值 +#define offset(struct_type, member) (int)(&((struct_type*)0)->member) +// 通过结构体内部指针 转换为 代表该结构体 的方法: +// 将 elem_ptr(结构体内部元素指针) 的地址减去 elem_ptr 在结构体内部的偏移量 +// 从而获取所在结构体的地址,再将该地址转换为 struct 类型 +// struct_member_name 为内部变量名,主要用于 offset 函数 获取结构体内偏移值 +#define elem2entry(struct_type, struct_member_name, elem_ptr) \ + (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name)) +// 节点,不需要 data 域 +struct list_elem { + struct list_elem* prev;// 前驱 + struct list_elem* next;// 后继 +}; + +// 链表结构,用来实现队列 +struct list { + struct list_elem head;// 头指针 + struct list_elem tail;// 尾指针 +}; + +typedef bool (function)(struct list_elem*, int arg); + +void list_init (struct list*); +void list_insert_before(struct list_elem* elem, struct list_elem* before); +void list_push(struct list* plist, struct list_elem* elem); +void list_iterate(struct list* plist); +void list_append(struct list* plist, struct list_elem* elem); +void list_remove(struct list_elem* pelem); +struct list_elem* list_pop(struct list* plist); +bool list_empty(struct list* plist); +uint32_t list_len(struct list* plist); +struct list_elem* list_traversal(struct list* plist, function func, int arg); +bool elem_find(struct list* plist, struct list_elem* obj_elem); + +#endif \ No newline at end of file diff --git a/c10/keyboard/lib/kernel/print.S b/c10/keyboard/lib/kernel/print.S new file mode 100755 index 0000000..af4424f --- /dev/null +++ b/c10/keyboard/lib/kernel/print.S @@ -0,0 +1,239 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret + +global set_cursor +set_cursor: + pushad + mov bx, [esp+36] +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + popad + ret \ No newline at end of file diff --git a/c10/keyboard/lib/kernel/print.h b/c10/keyboard/lib/kernel/print.h new file mode 100755 index 0000000..97b3896 --- /dev/null +++ b/c10/keyboard/lib/kernel/print.h @@ -0,0 +1,9 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +void set_cursor(uint32_t cursor_pos); +#endif + diff --git a/c10/keyboard/lib/stdint.h b/c10/keyboard/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c10/keyboard/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c10/keyboard/lib/string.c b/c10/keyboard/lib/string.c new file mode 100755 index 0000000..ab47322 --- /dev/null +++ b/c10/keyboard/lib/string.c @@ -0,0 +1,121 @@ +#include "string.h" +#include "global.h" +#include "debug.h" + +/* 进行初始化 将dst_起始的size个字节置为value */ +// 通常用于内存分配时的数据清 0 +void memset(void* dst_, uint8_t value, uint32_t size) { + ASSERT(dst_ != NULL); + uint8_t* dst = (uint8_t*)dst_; + while (size-- > 0){ + *dst++ = value; + } +} + +/* 将src_起始的size个字节复制到dst_ */ +void memcpy(void* dst_, const void* src_, uint32_t size) { + ASSERT(dst_ != NULL && src_ != NULL); + uint8_t* dst = dst_; + const uint8_t* src = src_; + while(size-- > 0) { + *dst++ = *src++; + } +} + +/* 连续比较以地址a_和地址b_开头的size个字节, + 若相等则返回0,若a_大于b_返回+1,否则返回-1 */ +int memcmp(const void* a_, const void* b_, uint32_t size) { + const char* a = a_; + const char* b = b_; + ASSERT(a != NULL && b != NULL); + while(size-- > 0) { + if(*a != *b) { + return *a > *b ? 1 : -1; + } + a++; + b++; + } + // 至此说明相等 + return 0; +} + +/* 将字符串从src_复制到dst_ */ +char* strcpy(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* temp = dst_; // 暂存地址 + // 以 字符串结尾 '0' 作为终止条件 + while((*dst_++ = *src_++)); + return temp; +} + + +/* 返回字符串长度 */ +uint32_t strlen(const char* str) { + ASSERT(str != NULL); + const char* end = str;// 标记结尾处位置 + // 和strcpy 函数一样,遇到 '0'停止 + while(*end++); + return (end - str - 1); +} +/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */ +int8_t strcmp (const char* a, const char* b) { + ASSERT(a != NULL && b != NULL); + while(*a != 0 && *a == *b) { + a++; + b++; + } + // 如果 < 就返回 -1 + // 如果 >= 就前往 布尔式 + // 该布尔式表示,要是 > 就 返回 1, 否则只可能是 = ,布尔值刚好是 0 + return *a < *b ? -1 : *a > *b; +} + +/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + while(*str != 0) { + if(*str == ch) { + return (char*) str;// 强制转换 + } + str++; + } + return NULL; +} + +/* 从右往左查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strrchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + const char* last = NULL; + // 从左往右遍历,将 last 更新即可,这样便不用手动判断 '0' + while(*str != 0) { + if(*str == ch) { + last = str; + } + str++; + } + return (char*)last; +} + +/* 将字符串src_拼接到dst_后,将回拼接的串地址 */ +char* strcat(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* str = dst_; + while(*str++);// 循环到末尾 + --str;//得到拼接位置 + while((*str++ = *src_++)); + return dst_; +} + +/* 在字符串str中查找指定字符ch出现的次数 */ +uint32_t strchrs(const char* str, uint8_t ch) { + ASSERT(str != NULL); + uint32_t cnt = 0; + const char* temp = str; + while(*temp != 0) { + if(*temp == ch) { + cnt++; + } + temp++; + } + return cnt; +} \ No newline at end of file diff --git a/c10/keyboard/lib/string.h b/c10/keyboard/lib/string.h new file mode 100755 index 0000000..516630e --- /dev/null +++ b/c10/keyboard/lib/string.h @@ -0,0 +1,14 @@ +#ifndef __LIB_STRING_H +#define __LIB_STRING_H +#include "stdint.h" +void memset(void* dst_, uint8_t value, uint32_t size); +void memcpy(void* dst_, const void* src_, uint32_t size); +int memcmp(const void* a_, const void* b_, uint32_t size); +char* strcpy(char* dst_, const char* src_); +uint32_t strlen(const char* str); +int8_t strcmp (const char *a, const char *b); +char* strchr(const char* string, const uint8_t ch); +char* strrchr(const char* string, const uint8_t ch); +char* strcat(char* dst_, const char* src_); +uint32_t strchrs(const char* filename, uint8_t ch); +#endif diff --git a/c10/keyboard/makefile b/c10/keyboard/makefile new file mode 100644 index 0000000..27e0899 --- /dev/null +++ b/c10/keyboard/makefile @@ -0,0 +1,117 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o \ + $(BUILD_DIR)/string.o $(BUILD_DIR)/thread.o $(BUILD_DIR)/switch.o \ + $(BUILD_DIR)/list.o $(BUILD_DIR)/console.o $(BUILD_DIR)/sync.o + + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/string.o: lib/string.c lib/string.h lib/stdint.h kernel/global.h \ + lib/stdint.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ + kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ + lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h kernel/global.h lib/stdint.h \ + kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h \ + kernel/global.h kernel/global.h kernel/debug.h lib/kernel/print.h \ + lib/kernel/io.h kernel/interrupt.h lib/string.h lib/stdint.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h lib/stdint.h \ + kernel/global.h lib/kernel/bitmap.h kernel/memory.h lib/string.h \ + lib/stdint.h lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/console.o: device/console.c device/console.h lib/stdint.h \ + lib/kernel/print.h thread/sync.h lib/kernel/list.h kernel/global.h \ + thread/thread.h thread/thread.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h lib/kernel/list.h kernel/global.h \ + lib/stdint.h thread/thread.h lib/string.h lib/stdint.h kernel/debug.h \ + kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: boot/mbr.S + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: boot/loader.S + $(AS) $(ASIB) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/switch.o: thread/switch.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean tran all + +mk_dir: + if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi + +hd: + # echo ".....Writing mbr, loader and kernel to disk....." + dd if=$(BUILD_DIR)/mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +clean: + cd $(BUILD_DIR) && rm -f ./* + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + # echo ".....build kernel now....." + +all: mk_dir build hd tran + +tran: + cd /home/husharp/bochs_hu/bochs/ && bin/bochs -f bochsrc.disk diff --git a/c10/keyboard/thread/switch.S b/c10/keyboard/thread/switch.S new file mode 100755 index 0000000..27705cd --- /dev/null +++ b/c10/keyboard/thread/switch.S @@ -0,0 +1,37 @@ +[bits 32] +section .text +global switch_to +; switch_to 函数接收两个参数 +; 一个是当前线程 cur, 一个是下一个处理器线程 +; 因此该函数的作用是 保存当前线程,将下一个线程放到处理器中 +; +; struct thread_stack { +; uint32_t ebp; +; uint32_t ebx; +; uint32_t edi; +; uint32_t esi; +; +; 易知反向压栈 +switch_to: + ; 第一部分:保存中断前的上下文环境 + ; 压入到内核栈,因此之后将内核栈储存 + push esi + push edi + push ebx + push ebp + +; struct task_struct { +; uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + + mov eax, [esp + 20] ; 得到栈中参数 4*4 + 4 即 4 个压栈 和 1 个返回地址 + mov [eax], esp ; eax 指的是 PCB 的首地址,而第一个参数是 每个线程的内核栈 + + ; 往处理器上装载调度的新线程上下文 + mov eax, [esp + 24] ; 同理 + mov esp, [eax] + + pop ebp ; 此时恢复的是:新线程内核栈内部的上下文 + pop ebx + pop edi + pop esi + ret \ No newline at end of file diff --git a/c10/keyboard/thread/sync.c b/c10/keyboard/thread/sync.c new file mode 100644 index 0000000..87866e4 --- /dev/null +++ b/c10/keyboard/thread/sync.c @@ -0,0 +1,99 @@ +#include "sync.h" +#include "kernel/list.h" +#include "global.h" +#include "debug.h" +#include "interrupt.h" + +// 初始化信号量 +void sema_init(struct semaphore* psema, uint8_t value) { + psema->value = value; + list_init(&psema->waiters); +} + +// 初始化锁 +void lock_init(struct lock* plock) { + plock->holder = NULL; + plock->holder_repeat_nr = 0;//将重复量置为0 + // 本系统采用二元信号量 + sema_init(&plock->semaphore, 1);// 信号量初始化为 1 +} + +// 信号量的 down 操作 +void sema_down(struct semaphore* psema) { + enum intr_status old_status = intr_disable();// 先关中断 保证原子性 + // 用 while 保证一直对信号量进行判断 + while(psema->value == 0) {//循环判断value 要是一直为 0 便一直堵塞 + // 开始阻塞之前,不应该在该锁的等待队列中 + ASSERT(!elem_find(&psema->waiters, &running_thread()->general_tag)); + + if (elem_find(&psema->waiters, &running_thread()->general_tag)) { + PANIC("sema_down: thread bloked has been in block_waiter_list\n"); + } + // 将当前线程加入到该锁的等待队列中 + list_append(&psema->waiters, &running_thread()->general_tag); + // 调用阻塞函数,去调度其他线程 + // 当调度线程为该锁的持有者时,运行完之后,就会将该 堵塞进程唤醒 + thread_block(TASK_BLOCKED);//堵塞态,一直等待到唤醒 + // 醒来后,由于是 while 因此会继续进行判断,发现此时为 1 就跳出循环 进行-1 + } + // 若 value 为1 ,或者是被唤醒后,即此时获取了锁 + psema->value--; + ASSERT(psema->value == 0); + // 恢复之前的中断状态 + intr_set_status(old_status); +} + +// 信号量中的 up 操作 +void sema_up(struct semaphore* psema) { + // 关中断 + enum intr_status old_status = intr_disable(); + ASSERT(psema->value == 0);// 此时应该为 0 ,进行唤醒 + if(!list_empty(&psema->waiters)) {//若锁的等待数组不为 0 ,需要唤醒(由于是二元信号量,唤醒一个即可 + struct task_struct* blocked_thread = elem2entry(struct task_struct, general_tag, list_pop(&psema->waiters)); + // 调用唤醒函数 + thread_unblock(blocked_thread); + }// 所谓唤醒,并非是直接开始执行,而是改变 thread 的 stat 使其加入到就绪队列中。 + // 并且为 push 加入,优先进行调度 + // 至此 锁的等待队列中没有在等待线程了 + psema->value++;// 归还锁 + ASSERT(psema->value == 1); + // 进行开中断 + intr_set_status(old_status); +} + +// 锁的获取函数 +// 主要值得注意的是,不能重复申请锁(以免变为死锁——即自己等待自己释放锁) +// 主要判断点在于 锁 中的 holder_repeat_nr 变量 +void lock_acquire(struct lock* plock) { + if (plock->holder != running_thread()) {//持有者不为当前者 + sema_down(&plock->semaphore);//对信号量执行 P 操作,可能会阻塞 + plock->holder = running_thread();// 此时获取到锁 + // 之前还未获取到锁 + ASSERT(plock->holder_repeat_nr == 0); + // 此时表示第一次获取到锁 + plock->holder_repeat_nr = 1; + } else {//持有者是当前者,未避免死锁,拒绝再获取锁,将申请次数++,直接返回即可 + plock->holder_repeat_nr++; + } +} + +// 释放锁函数 释放参数中的锁 +void lock_release(struct lock* plock) { + // 当前线程应该为锁的持有者 + ASSERT(plock->holder == running_thread()); + if (plock->holder_repeat_nr > 1) {//说明多次申请该锁,还不能进行释放 + plock->holder_repeat_nr--;// 因为要嵌套返回,且之前获取锁并未操作 + return; + } + ASSERT(plock->holder_repeat_nr == 1); + + // 改 plock 需要在 V 操作前面执行 + // 因为 释放锁的操作并未关中断 + // 若将 V 放在 holder 置为 NULL 之前,那么可能出现如下情况: + // 当前线程刚刚执行完 V ,还未置为 NULL 便被调度为新线程 + // 新线程将 holder 置为 新线程的 PCB + // 此时又被调度为 置为 NULL ,易知不得行 + plock->holder = NULL; + plock->holder_repeat_nr = 0; + sema_up(&plock->semaphore);// 信号量的 V 操作 +} \ No newline at end of file diff --git a/c10/keyboard/thread/sync.h b/c10/keyboard/thread/sync.h new file mode 100644 index 0000000..738e00f --- /dev/null +++ b/c10/keyboard/thread/sync.h @@ -0,0 +1,30 @@ +#ifndef __THREAD_SYNC_H +#define __THREAD_SYNC_H +#include "kernel/list.h" +#include "stdint.h" +#include "thread.h" + +// 信号量 +struct semaphore { + uint8_t value;// 信号量的值,会有初始值 + struct list waiters;// 记录此信号量上阻塞的所有线程 +}; + +// 锁结构 +struct lock { + struct task_struct* holder; //锁的持有者 + struct semaphore semaphore; // 用二元信号量来实现锁 + // 由于有时候可能在还未释放锁之前,会再次调用申请锁的函数 + // 因此需要记录是否之前申请过锁 + uint32_t holder_repeat_nr; // 锁的持有者重复申请锁的次数 +}; + +/******* 函数实现 ***************/ +void sema_init(struct semaphore* psema, uint8_t value); +void sema_down(struct semaphore* psema); +void sema_up(struct semaphore* psema); +void lock_init(struct lock* plock); +void lock_acquire(struct lock* plock); +void lock_release(struct lock* plock); + +#endif \ No newline at end of file diff --git a/c10/keyboard/thread/thread.c b/c10/keyboard/thread/thread.c new file mode 100755 index 0000000..fb0fd22 --- /dev/null +++ b/c10/keyboard/thread/thread.c @@ -0,0 +1,202 @@ +#include "thread.h" +#include "stdint.h" +#include "string.h" +#include "global.h" +#include "debug.h" +#include "interrupt.h" +#include "print.h" +#include "memory.h" + +#define PG_SIZE 4096 + +// 定义主线程的 PCB,因为进入内核后,实则上一直执行的是 main 函数 +struct task_struct* main_thread; // 主线程 PCB +struct list thread_ready_list; // 就绪队列 +// 当线程不为就绪态时,会从所有线程队列中找到它 +struct list thread_all_list; // 所有线程队列 +// 队列是以 elem 的形式储存在 list 队列中,因此需要一个 elem 变量来将其取出转换 +static struct list_elem* thread_tag; // 用于保存队列中的线程节点 + +// switch_to函数的外部声明 global +extern void switch_to(struct task_struct* cur, struct task_struct* next); + + +// 取当前线程的 PCB 指针 +struct task_struct* running_thread(void) { + uint32_t esp; + asm("mov %%esp, %0" : "=g"(esp)); + // 取 esp 的前20位,PCB 的栈顶就为 0级栈 + return (struct task_struct*)(esp & 0xfffff000); +} + +// 不能按照以往的函数调用, 比如 kernel_thread(function, func_arg) +// 因为 我们此处采用的是 ret 返回,而不是 call , +// 因此需要采用存储到 kernel_thread 的栈中,存储参数和占位返回地址的方式 + +/* 由kernel_thread去执行function(func_arg) */ +static void kernel_thread(thread_func* function, void* func_arg) { + // 由于线程的运行是由调度器中断调度,进入中断后,处理器会自动关中断。 + // 执行 function 前开中断,避免之后的时钟中断被屏蔽,从而无法调度其他线程 + intr_enable(); + function(func_arg); // 调用 function +} + +// 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) { + // 由于 init_thread 中,已经指向最顶端了 + // 先预留中断使用栈的空间,可见 thread.h 中定义的结构 + // 中断栈用于保存中断时的上下文,实现用户进程时,初始值也会放在中断栈 + pthread->self_kstack -= sizeof(struct intr_stack); + + // 再留出线程栈空间 + pthread->self_kstack -= sizeof(struct thread_stack); + + // 定义线程栈指针 + struct thread_stack* kthread_stack = (struct thread_stack*) pthread->self_kstack; + + // 为 kernel_thread 中 能调用 function 做准备 + // kernel_thread 是第一个函数, eip直接指向它,然后再调用其他的 function + kthread_stack->eip = kernel_thread; + kthread_stack->function = function; + kthread_stack->func_arg = func_arg; + // 初始化为 0 ,在还未执行函数前,寄存器不应该有值 + kthread_stack->ebp = kthread_stack->ebx = \ + kthread_stack->edi = kthread_stack->esi = 0; +} + + +// 初始化线程基本信息 +void init_thread(struct task_struct* pthread, char* name, int prio) { + // + memset(pthread, 0, sizeof(*pthread)); + strcpy(pthread->name, name); + // 将主函数也封装为一个线程,且由于其一直运行,因此状态赋为 Running + if(pthread == main_thread) { + pthread->status = TASK_RUNNING; + } else { + pthread->status = TASK_READY; + } + + + // 此处是为演习,因此直接将 状态置为 running + pthread->priority = prio; + pthread->ticks = prio; + pthread->elapsed_ticks = 0; //表示还未执行过 + pthread->pgdir = NULL; //线程没有自己的地址空间 + pthread->stack_magic = 0x20000611; //自定义一个魔数 + // self_kstack 是线程自己在内核态下使用的栈顶地址,指向最顶端 + pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE); +} + +// 线程创建函数 +//创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) { + // pcb 位于内核空间,包括用户进程的 pcb 也是在内核空间中 + + // 先通过 内核物理页申请函数 申请一页 + struct task_struct* thread = get_kernel_pages(1); + // 由于 get_kernel_page 获取的是起始位置,因此获取的是 pcb 最低地址 + // 初始化新建线程 + init_thread(thread, name, prio); + // 创建线程 + thread_create(thread, function, func_arg); + // 至此 线程得到初始化和创建后,需要加入到就绪队列和全局队列中 + + // 首先需要判断不在就绪队列中 + ASSERT(!elem_find(&thread_ready_list, &thread->general_tag)); + // 加入就绪队列 + list_append(&thread_ready_list, &thread->general_tag); + // 首先需要判断不在全局队列中 + ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag)); + // 加入就绪队列 + list_append(&thread_all_list, &thread->all_list_tag); + + + // 简陋版本(以后改为 switch_to + // 作用为:开启线程 + // 由于 thread_create 中将 self_kstack 指向线程栈的最低处,现在将 self_kstack 作为栈顶 + // ret 操作时,由于 thread_create 函数中,将 eip 指向了 kernel_thread,因此 ret时,会跳转到该函数 + return thread; +} + + +// 将kernel中的main函数完善为主线程 +static void make_main_thread(void) { + // 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000, + // 就是为其预留了tcb,地址为0xc009e000,因此不需要通过get_kernel_page另分配一页 + // 直接 init_thread 即可 + main_thread = running_thread();// 获取当前的PCB指针 + init_thread(main_thread, "main", 31); + + // main函数是当前线程,当前线程不在thread_ready_list中 + // 只用加到 全局线程队列中 + ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag)); + list_append(&thread_all_list, &main_thread->all_list_tag); +} + + +// 实现任务调度 读写就绪队列 +void schedule(void) { + ASSERT(intr_get_status() == INTR_OFF); + + struct task_struct* cur = running_thread();// 取出当前线程的PCB + if(cur->status == TASK_RUNNING) {// 只是时间片为 0 ,而非阻塞 + ASSERT(!elem_find(&thread_ready_list, &cur->general_tag)); + list_append(&thread_ready_list, &cur->general_tag); + // 现将当前线程的 ticks 再次赋为 prio + cur->ticks = cur->priority; + cur->status = TASK_READY; + } + //由于还未实现 idle 线程,因此可能出现 ready_list 中无线程可调度的情况 + // 因此先进行断言 ready 队列中是否存在元素 + ASSERT(!list_empty(&thread_ready_list)); + thread_tag = NULL; // 首先将全局变量清空 + // 将就绪进程中的第一个线程(头结点)弹出 + thread_tag = list_pop(&thread_ready_list); + // 现在获得了 PCB 的 elem 节点,需要将其转换为 PCB + struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag); + next->status = TASK_RUNNING;// 调度 + switch_to(cur, next); +} + +// 当前线程将自己阻塞,标志其状态为stat +void thread_block(enum task_status stat) { + // stat取值为TASK_BLOCKED,TASK_WAITING,TASK_HANGING 指不可运行 + ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING))); + + enum intr_status old_status = intr_disable();// 保存中断前状态 + // 当前运行的必然为 RUNNing态 + struct task_struct* cur_thread = running_thread();// 获取当前线程的PCB + cur_thread->status = stat; + schedule();//进行调度 + // 待当前线程被解除阻塞后才继续运行下面的函数 + intr_set_status(old_status); +} + +// 将线程 pthread 解除阻塞 唤醒 +// 被阻塞的线程必须来等别人唤醒自己 +void thread_unblock(struct task_struct* pthread) { + enum intr_status old_status = intr_disable(); + ASSERT(((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGING))); + if(pthread->status != TASK_READY) { + // 在就绪队列中没有该阻塞线程 + ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag)); + if (elem_find(&thread_ready_list, &pthread->general_tag)) { + PANIC("thread_unblock: blocked thread in ready_list\n"); + } + list_push(&thread_ready_list, &pthread->general_tag);//push 让其优先被调度 + pthread->status = TASK_READY; + } + intr_set_status(old_status); +} + + +// 初始化线程环境 +void thread_environment_init(void) { + put_str("thread_init start!\n"); + list_init(&thread_ready_list); + list_init(&thread_all_list); + // 将 main 函数创建为 线程 + make_main_thread(); + put_str("thread_init done!\n"); +} \ No newline at end of file diff --git a/c10/keyboard/thread/thread.h b/c10/keyboard/thread/thread.h new file mode 100755 index 0000000..add584d --- /dev/null +++ b/c10/keyboard/thread/thread.h @@ -0,0 +1,108 @@ +#ifndef __THREAD_THREAD_H +#define __THREAD_THREAD_H +#include "stdint.h" +#include "list.h" + +// 自定义通用函数类型,它将在很多线程函数中做为形参类型 +typedef void thread_func(void*); + +// 进程或线程的状态 +enum task_status { + TASK_RUNNING, + TASK_READY, + TASK_BLOCKED, + TASK_WAITING, + TASK_HANGING, + TASK_DIED +}; + +/*********** 中断栈intr_stack *********** + * 此结构用于中断发生时保护程序(线程或进程)的上下文环境: + * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 + * 寄存器, intr_exit中的出栈操作是此结构的逆操作 + * 此栈在线程自己的内核栈中位置固定,所在页的最顶端 +********************************************/ +struct intr_stack { + uint32_t vec_no; // kernel.S 宏VECTOR中push %1压入的中断号 + uint32_t edi; + uint32_t esi; + uint32_t ebp; + // 虽然pushad把esp也压入,但esp是不断变化的,所以会被popad忽略 + uint32_t esp_dummy; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + +/* 以下由cpu从低特权级进入高特权级时压入 */ + uint32_t err_code; // err_code会被压入在eip之后 + void (*eip) (void); + uint32_t cs; + uint32_t eflags; + void* esp; + uint32_t ss; +}; + +/*********** 线程栈thread_stack *********** + * 线程自己的栈,用于存储线程中待执行的函数 + * 此结构在线程自己的内核栈中位置不固定, + * 用在switch_to时保存线程环境。 + * 实际位置取决于实际运行情况。 + ******************************************/ +struct thread_stack { + uint32_t ebp; + uint32_t ebx; + uint32_t edi; + uint32_t esi; + + /* 线程第一次执行时,eip指向待调用的函数kernel_thread + 其它时候,eip是指向switch_to的返回地址*/ + void (*eip) (thread_func* func, void* func_arg); + + /***** 以下仅供第一次被调度上cpu时使用 ****/ + // 占位函数,迷惑 ret ,假装为返回地址,实则为获取参数提供栈顶位置 + // 用于找到函数参数位置 + void (*unused_retaddr); + thread_func* function; // 由 Kernel_thread 所调用的函数名 + void* func_arg; // 由 Kernel_thread 所调用函数所需要的的参数 +}; + +// 进程或线程的 pcb, 程序控制块 +// ticks 表示的是中断次数 +struct task_struct { + uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + char name[16]; // 记录任务的名字 + enum task_status status; // 线程状态 + uint8_t priority; // 线程优先级 + uint8_t ticks; // 每次在处理器上执行的时间滴答数 + // 已经执行的滴答数 + uint32_t elapsed_ticks;// elapsed : 流逝 + // 加入到就绪队列中 + struct list_elem general_tag; + // 值得注意的是 由于需要管控所有的线程资源, + // 但是一个 list_elem 只有两个指针,因此只能位于一个队列中 + // 所以创建新的队列 all_list 记录全部线程 + struct list_elem all_list_tag; + + uint32_t* pgdir; // 进程拥有独立地址空间(页表),线程为NULL + + // 通过判断栈的边界溢出魔数值是否发生变化 + uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出 + + +}; + +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg); +void init_thread(struct task_struct* pthread, char* name, int prio); +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg); +struct task_struct* running_thread(void); +void schedule(void); +void thread_environment_init(void); +void thread_block(enum task_status stat); +void thread_unblock(struct task_struct* pthread); + +#endif \ No newline at end of file diff --git a/c10/keyboard/tmp b/c10/keyboard/tmp new file mode 100755 index 0000000..b76e9fe --- /dev/null +++ b/c10/keyboard/tmp @@ -0,0 +1,206 @@ +#include "interrupt.h" +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define EFLAGS_IF 0x00000200 // eflags 寄存器的 if 位为 1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + // 处理伪中断,,无法通过 IMR 寄存器屏蔽,因此单独处理 + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + // 将光标置为 0 ,从屏幕左上角清出一片打印异常信息的区域 + set_cursor(0);// 有时候甚至可能中断错误是由光标错误引起 + // 因此先将左上角空出四行来 + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + // 至此 已经将左上角空出四行,因此将光标再移到 0 位置,进行输出 + set_cursor(0); + put_str("!!!!!! exception message begin !!!!!!"); + set_cursor(88);// 第二行第 8 列 + put_str(intr_name[vec_nr]); + if(vec_nr == 14) { // 若为 pagefault,将缺失的地址打印出来 + int page_fault_vaddr = 0; + // 发生 pagefult + asm("movl %%cr2, %0" : "=r" (page_fault_vaddr));// cr2 是存放造成 page_fault 的地址 + put_str("\npage fault addr is "); + put_int(page_fault_vaddr); + } + put_str("!!!!!! exception message end !!!!!!"); + // 能进入中断处理程序就表示已经处在关中断情况下 + // 不会出现 +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void +exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断, sti 指令将 IF 位 置 1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli 指令将 IF 位 置 0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + // 判断 IF 位的值是否为 1 + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} + +// 在中断处理程序数组第 vector_no 个元素中注册安装中断处理程序 function +void register_handler(uint8_t vector_no, intr_handler function) { + idt_table[vector_no] = function; +} \ No newline at end of file diff --git "a/c10/keyboard/\344\273\243\347\240\201\347\273\223\346\236\204.txt" "b/c10/keyboard/\344\273\243\347\240\201\347\273\223\346\236\204.txt" new file mode 100644 index 0000000..7805be8 --- /dev/null +++ "b/c10/keyboard/\344\273\243\347\240\201\347\273\223\346\236\204.txt" @@ -0,0 +1,220 @@ +DATE: 20200313 +=============== + + +FILE: boot/mbr.asm + | 加载loader,并跳转 + | + | +FILE: boot/loader.asm + | 调用BIOS中断获取内存大小;构建GDT,进入保护模式;加载kernel;创建页目录PD和页表PT,开启分页机制; + | 解析kernel的ELF,将ELF文件中的段segment拷贝到各段自己被编译的虚拟地址处;跳转 + | + | +FILE: kernel/main.c + | + |---- FILE: lib/print.asm put_str("i am kernel\n"); + | | 打印提示信息 + | | 汇编实现打印函数(写入显存0xb_8000,loader阶段设为GDT第3号),C语言调用 + | + |---- FILE: kernel/init.c init_all() + | | 初始化 + | | + | |---- FILE: kernel/interrupt.c idt_init() + | | | 初始化中断 + | | | 构建IDT,这里IDT中的每一项都指向对应的一段汇编代码,再由汇编调用C语言中断处理函数 + | | | 初始化可编程中断控制器8259A,放开时钟中断0x20 + | | | 中断发生时,会根据IDTR中的IDT基地址+中断向量*8,跳转到对应的汇编代码 + | | | + | | |---- FILE: kernel/core_interrupt.asm + | | | + | | |---- FILE: lib/io.h + | | | 内联汇编实现的读写端口函数 + | | | 凡是包含io.h的文件,都会获得一份io.h中所有函数的拷贝 + | | | inline是建议处理器将函数编译为内嵌方式,即在该函数调用处原封不动地展开 + | | + | |---- FILE: kernel/memory.c mem_init() + | | | 初始化内存管理系统 + | | | mem_pool_init()初始化内存池: 内核虚拟地址内存池、内核/用户物理内存池 + | | | 虚拟地址内存池: 虚拟地址bitmap、虚拟地址内存池起始地址 + | | | 物理内存池:物理内存bitmap、物理内存起始地址、物理内存池大小 + | | | + | | |---- FILE: lib/bitmap.c bitmap_init() bitmap_scan() bitmap_set() + | | | | bitmap的基本操作 + | | | | + | | | |---- FILE: lib/string.c memset() + | | | | 内存/字符串的基本操作 + | | | + | | |---- FILE: kernel/debug.h ASSERT() PANIC() + | | | 断言 + | | | + | | |---- FILE: kernel/debug.c panic_spin(__FILE__, __LINE__, __func__, __VA_ARGS__) + | | | 关中断,打印相关信息,while(1)使程序悬停 + | | | + | | |---- FILE: kernel/interrupt.c intr_disable(); + | | | 关中断 + | | + | |---- FILE: thread/thread.c thread_init() + | | | 初始化线程相关结构 + | | | list_init()初始化就绪队列、初始化全部队列 + | | | make_main_thread()将当前内核main函数创建为线程 + | | | running_thread()通过esp得到PCB基地址(main线程的PCB在loader阶段已分配) + | | | init_thread()在PCB中初始化线程基本信息: 状态 优先级 嘀嗒时间数 ... + | | | list_append()加入全部队列 + | | | + | | |---- FILE: list/list.c + | | | 结点: struct list_elem{struct list_elem *prev; struct list_elem *next; }; + | | | 链表: struct list{struct list_elem head; struct list_elem tail; }; + | | | 对链表的一些修改操作需关中断来确保原子操作 + | | + | |---- FILE: device/timer.c timer_init() + | | | frequency_set()初始化可编程定时计时器8253 + | | | 使用8253来给IRQ0引脚上的时钟中断信号“提速”,使其发出的中断信号频率快一些。 + | | | 默认的频率是18.206Hz,即一秒内大约发出18 次中断信号。 + | | | 通过对8253编程,使时钟一秒内发100次中断信号,即中断信号频率为100Hz. + | | | + | | |---- FILE: kernel/interrupt.c register_handler(0x20, intr_timer_handler) + | | | | 注册安装中断处理程序 idt_table[vector_id] = function + | | | | 时钟的中断向量号0x20 + | | | + | | |---- FILE: device/timer.c intr_timer_handler() + | | | 时钟的中断处理程序 + | | | running_thread()通过esp得到PCB基地址 + | | | if(current_thread->ticks == 0) + | | | schedule(); // 若进程时间片用完,就开始调度新的进程上CPU + | | | else + | | | current_thread->ticks--; // 将当前进程的时间片-1 + | | | + | | |---- FILE: thread/thread.c schedule() + | | | 任务调度 (由时钟中断触发) + | | | *** 调度 **************** + | | | 若状态为RUNNING, 则添加到就绪队列尾并重置ticks、状态改为READY + | | | 就绪队列第一个就绪线程弹出, 由结构体成员地址得到首地址, 这里即为PCB基地址 + | | | 修改状态为RUNNING + | | | + | | |---- FILE: thread/switch.asm switch_to(current_thread, next); + | | | 切换栈、切换执行流eip + | | | ********************************************* + | | | 此处的理解需结合线程栈信息struct thread_stack和函数thread_create() + | | | ********************************************* + | | | 保存current线程的寄存器, 将next线程的寄存器装载到处理器 + | | | 传入的2个参数自动压入了current栈中, 这2个参数为2线程PCB基地址 + | | | PCB底部为线程/进程信息struct task_struct, 第一个成员为栈顶地址 + | | | 切换栈: 伪代码 mov [current], esp; mov esp, [next]; + | | | 切换执行线程: ret。这里利用了ret的特性, 自动从栈弹出给eip + | | | 栈中存放eip处, 已由函数thread_create赋值为kernel_thread() + | | | + | | |---- FILE: thread/thread.c kernel_thread(function, func_arg) + | | | 执行创建线程时指定的函数 + | | | 这里2个参数的值已由函数thread_create赋值在栈中 + | | | intr_enable()开中断, 避免时钟中断被屏蔽而无法调度其它线程 + | | | function(func_arg)执行新线程, + | | + | |---- FILE: device/console.c console_init() + | | | 初始化控制台 + | | | 结合lock锁机制,对print族函数加了层封装,如void console_put_str() + | | | { console_acquire(); put_str(str); console_release(); } + | | | static struct lock console_lock; // 声明控制台锁 + | | | + | | |---- FILE: thread/sync.c lock_init(&console_lock) + | | | | 初始化锁 + | | | | 基于二元信号量实现的锁 + | | | | struct semaphore{ + | | | | unsigned char value; struct list waiters; }; + | | | | 信号量初值, 此信号量上阻塞的所有线程 + | | | | struct lock{ + | | | | struct task_struct *holder; struct semaphore sema; unsigned int holder_repeat_num; }; + | | | | 锁的持有者, 信号量, 锁的持有者重复申请锁的次数 + | | | + | | |---- FILE: thread/sync.c lock_acquire() + | | | | 获取锁 + | | | | sema_down(&lock->semaphore)信号量P操作 + | | | | while(sema->value == 0) // value为0表明已经被别人持有 + | | | | list_append()当前线程把自己加入该锁的等待队列 + | | | | thread_block()当前线程阻塞自己,并触发调度,切换线程 + | | | | ************************************* 调度 ********* + | | | | lock->holder = running_thread() + | | | | + | | | |---- FILE: thread/thread.c thread_block() + | | | | 当前线程将自己阻塞 + | | | | 修改线程状态为阻塞、触发调度, 切换线程执行 + | | | | + | | | |---- FILE: thread/thread.c schedule() + | | | | 任务调度 (由当前阻塞线程主动触发) + | | | + | | |---- FILE: thread/sync.c lock_release() + | | | 释放锁 + | | | lock->holder = NULL + | | | sema_up(&lock->semaphore)信号量V操作 + | | | list_pop(&sema->waiters)从等待队列中取出一个线程 + | | | thread_unblock()唤醒该阻塞线程: 将阻塞线程加入就绪队列,并修改状态为READY + | | | sema->value++ + | | | + | | |---- FILE: thread/thread.c thread_unblock() + | | | 唤醒阻塞线程 + | | | 将阻塞线程加入就绪队列,并修改状态为READY + | | + | |---- FILE: device/keyboard.c keyboard_init() + | | 初始化键盘 + | | 初始化键盘的环形缓冲区 ioqueue_init(&keyboard_buf) + | | 注册安装中断处理程序 register_handler(0x21, intr_keyboard_handler) + | | 键盘的中断向量号0x21 + | | + | |---- FILE: device/ioqueue.c ioqueue_init() + | | | 初始化环形缓冲区 + | | | 结合锁机制、生产者消费者模型 + | | | struct ioqueue{ + | | | struct lock lock; // 锁 + | | | struct task_struct *producer, *consumer; // 睡眠的生产者/消费者 + | | | char buf[buffersize]; // 缓冲区 + | | | signed int head, tail; }; // 队首写入, 队尾读出 + | | + | |---- FILE: device/ioqueue.c ioq_getchar() + | | | 消费者消费一个字符 + | | | while(ioq_empty(ioq)) // 缓冲区为空时, 消费者睡眠 + | | | lock_acquire(&ioq->lock); // 获取锁, 每个锁对应的信号量都会有一个阻塞队列 + | | | ioq_wait(&ioq->consumer); // 消费者睡眠 + | | | lock_release(&ioq->lock); // 释放锁 + | | | char byte = ioq->buf[ioq->tail]; // 消费一个字符 + | | | if(ioq->producer !=NULL) wakeup(&ioq->producer); // 唤醒生产者(生产者睡眠是因为缓冲区满) + | | + | |---- FILE: device/ioqueue.c ioq_putchar() + | | | 生产者生产一个字符 + | | | while(ioq_full(ioq)) // 缓冲区满时, 生产者睡眠 + | | | lock_acquire(&ioq->lock); // 获取锁, 每个锁对应的信号量都会有一个阻塞队列 + | | | ioq_wait(&ioq->producer); // 生产者睡眠 + | | | lock_release(&ioq->lock); // 释放锁 + | | | ioq->buf[ioq->head] = byte; // 生产一个字符 + | | | if(ioq->consumer !=NULL) wakeup(&ioq->consumer); // 唤醒消费者(消费者睡眠是因为缓冲区空) + | | + | |---- FILE: device/keyboard.c intr_keyboard_handler + | | 键盘的中断处理程序 + | | 键盘上8048芯片 -> 主板上8042芯片 -> 中断代理8259A + | | 从输出缓冲区寄存器端口0x60读取扫描码 + | + |---- FILE: thread/thread.c thread_start() + | | 创建一个线程并执行 + | | get_kernel_pages(1)内核空间中申请一页内存作为线程PCB(线程栈也在其中) + | | PCB底部为线程/进程信息struct task_struct, PCB顶部为栈空间 + | | + | | init_thread()在PCB底部 初始化线程基本信息: 状态 优先级 嘀嗒时间数 ... + | | thread_create()初始化线程栈: 中断使用的栈信息struct intr_stack, 线程栈信息struct thread_stack, 线程栈 + | | 线程栈信息struct thread_stack: 栈顶为寄存器和eip(指向kernel_thread函数)和返回地址 + | | 跳过返回地址栈顶+4为参数function、栈顶+8为参数func_arg + | | list_append()加入就绪队列、全部队列 + | | + | |---- FILE: kernel/memory.c get_kernel_pages(); + | | 申请内核空间中的内存 + | | 1) 虚拟内存池中申请连续的虚拟页 vaddr_get() + | | 2) 依次为每一个虚拟页在物理内存池中申请物理页 palloc() + | | 3) 在页表中完成虚拟地址和物理地址的映射 page_table_add() + | + |---- FILE: kernel/main.c test_thread_1() + | | 线程1 + | | (测试)作为键盘缓冲区的消费者 + | | if(!ioq_empty(&keyboard_buf)) // 键盘缓冲区不为空时 + | | char byte = ioq_getchar(&keyboard_buf); // 从键盘缓冲区消费一个字符 + | | console_put_char(byte); // 在控制台输出该字符 + | + |---- while(1) + diff --git a/c10/terminal_with_lock/boot/loader.S b/c10/terminal_with_lock/boot/loader.S new file mode 100755 index 0000000..d247cf5 --- /dev/null +++ b/c10/terminal_with_lock/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c10/terminal_with_lock/boot/mbr.S b/c10/terminal_with_lock/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c10/terminal_with_lock/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c10/terminal_with_lock/build/bitmap.o b/c10/terminal_with_lock/build/bitmap.o new file mode 100644 index 0000000000000000000000000000000000000000..d7365d28c5d29cb7939167ad2996de77e1bac85c GIT binary patch literal 2460 zcma)6-A^1<6u+~(z=9Mg_Q9l09FnSF!xW^o)KUYK0n!kQlzmd1>@HJSV3&1wrlA&U z)0KpA+=R#e5kB~YM*9HW5=zx%{dlN}i9|6$*$0|zV$(EPe`n@i7zl};X7y4)l4C<^}JLnl~Ir#3ffh)3djhwK}s=VW~x14cdbNTYW0!YnNEuXUK$)E%tOK|7MI?tIOQ``yZ0 zE28?MQ+>n3PnONc_<-!ORf>VPl(*DEX0BQ?%WSod|K>xTw!k15EVER+@-ozWsU3|;P!BSt*g{5GwQ zCaq2B2{sjroSxKE34Q8xBxc0JNhQh}uZO2hUA}m+rAeNhm3KwCwTZb-S~?m|?Cy1g z*@2Te)_3XBTXJJ4VkQhzKBt^jTAJHTM{2#*+OD*mX>zr6Bgbd~NgoX{X*MVwuMPwj zaH}$OC&&P5qou1RIPdE|Qr464Pp;`6)N3H{9H6(R4>;%8+C?B%Qq8hU)y zV2PEpmw+^8FK`*KOMb?GMqJ}(mAf|D_?>KfDnASE365Q2uW}9byB_tv;qj>VRj_`K zO#S%afUHBNSh@>}zYY|AC;1L!K@^__i=LC64-zI%)2^9`sHQ2cEp2UH>1>Obwpb!& zz%tYRrZT1*S~98`<7Q$~3C}RCZ{SLI*MN4VxA&?#tPOW{52zZ8>+v)u)HAr6p{MsL zSTdZ5MYVJ?wr{AZQ@$F;jsUA_nbW5LKHvh^cXItamp|q517Ibt^;?cVbNm$;c#*MN z7#QDC#=a*+{vUyq|8F40J>mF_qYo{jI2rM@@8LMk@iUHJ5K_@f`bOLcM}S7k#c{Dq z=~IfK&lpS@4I5#mMAB)dq^4bg49elXRie}JxSl{?W@QQs*1?uj6vs3DHMw|{rhzeeb& yfAn7x{iD1h7q(1>VGv4sE5zhi2c}%!l9HJ3yU+;|_0bG?{g!d=9pMBNe*XedLi*_d literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/console.o b/c10/terminal_with_lock/build/console.o new file mode 100644 index 0000000000000000000000000000000000000000..f9e15fd34da4c831a0b984520a79eedca0b994d6 GIT binary patch literal 2424 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 literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/interrupt.o b/c10/terminal_with_lock/build/interrupt.o new file mode 100644 index 0000000000000000000000000000000000000000..93d0ab3ad1a7b7e3d0e574030c4c27f02a3888e9 GIT binary patch literal 5296 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 RX-`>saVWk{4zeiTe*y2rtIGfY literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/kernel.bin b/c10/terminal_with_lock/build/kernel.bin new file mode 100755 index 0000000000000000000000000000000000000000..7d97336631139766f99b2e80bb3e481bf65756c1 GIT binary patch literal 27120 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 literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/kernel.map b/c10/terminal_with_lock/build/kernel.map new file mode 100644 index 0000000..047caa8 --- /dev/null +++ b/c10/terminal_with_lock/build/kernel.map @@ -0,0 +1,597 @@ + +分配公共符号 +公共符号 大小 文件 + +thread_ready_list 0x10 build/thread.o +thread_all_list 0x10 build/thread.o +user_pool 0x10 build/memory.o +kernel_addr 0xc build/memory.o +intr_name 0x84 build/interrupt.o +main_thread 0x4 build/thread.o +ticks 0x4 build/timer.o +idt_table 0x84 build/interrupt.o +kernel_pool 0x10 build/memory.o + +舍弃的输入节 + + .group 0x0000000000000000 0x8 build/main.o + .note.GNU-stack + 0x0000000000000000 0x0 build/main.o + .group 0x0000000000000000 0x8 build/init.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/init.o + .note.GNU-stack + 0x0000000000000000 0x0 build/init.o + .group 0x0000000000000000 0x8 build/interrupt.o + .group 0x0000000000000000 0x8 build/interrupt.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/interrupt.o + .note.GNU-stack + 0x0000000000000000 0x0 build/interrupt.o + .group 0x0000000000000000 0x8 build/timer.o + .group 0x0000000000000000 0x8 build/timer.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/timer.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/timer.o + .note.GNU-stack + 0x0000000000000000 0x0 build/timer.o + .group 0x0000000000000000 0x8 build/debug.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/debug.o + .note.GNU-stack + 0x0000000000000000 0x0 build/debug.o + .group 0x0000000000000000 0x8 build/memory.o + .group 0x0000000000000000 0x8 build/memory.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/memory.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/memory.o + .note.GNU-stack + 0x0000000000000000 0x0 build/memory.o + .group 0x0000000000000000 0x8 build/bitmap.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/bitmap.o + .note.GNU-stack + 0x0000000000000000 0x0 build/bitmap.o + .group 0x0000000000000000 0x8 build/string.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/string.o + .note.GNU-stack + 0x0000000000000000 0x0 build/string.o + .group 0x0000000000000000 0x8 build/thread.o + .group 0x0000000000000000 0x8 build/thread.o + .group 0x0000000000000000 0x8 build/thread.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/thread.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/thread.o + .note.GNU-stack + 0x0000000000000000 0x0 build/thread.o + .group 0x0000000000000000 0x8 build/list.o + .group 0x0000000000000000 0x8 build/list.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/list.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/list.o + .note.GNU-stack + 0x0000000000000000 0x0 build/list.o + .group 0x0000000000000000 0x8 build/console.o + .group 0x0000000000000000 0x8 build/console.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/console.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/console.o + .note.GNU-stack + 0x0000000000000000 0x0 build/console.o + .group 0x0000000000000000 0x8 build/sync.o + .group 0x0000000000000000 0x8 build/sync.o + .text.__x86.get_pc_thunk.ax + 0x0000000000000000 0x4 build/sync.o + .text.__x86.get_pc_thunk.bx + 0x0000000000000000 0x4 build/sync.o + .note.GNU-stack + 0x0000000000000000 0x0 build/sync.o + +内存配置 + +名称 来源 长度 属性 +*default* 0x0000000000000000 0xffffffffffffffff + +链结器命令稿和内存映射 + +段 .text 的地址设置为 0xc0001500 +LOAD build/main.o +LOAD build/init.o +LOAD build/interrupt.o +LOAD build/timer.o +LOAD build/kernel.o +LOAD build/print.o +LOAD build/debug.o +LOAD build/memory.o +LOAD build/bitmap.o +LOAD build/string.o +LOAD build/thread.o +LOAD build/switch.o +LOAD build/list.o +LOAD build/console.o +LOAD build/sync.o + [!provide] PROVIDE (__executable_start = SEGMENT_START ("text-segment", 0x8048000)) + 0x0000000008048094 . = (SEGMENT_START ("text-segment", 0x8048000) + SIZEOF_HEADERS) + +.interp + *(.interp) + +.note.gnu.build-id + *(.note.gnu.build-id) + +.hash + *(.hash) + +.gnu.hash + *(.gnu.hash) + +.dynsym + *(.dynsym) + +.dynstr + *(.dynstr) + +.gnu.version + *(.gnu.version) + +.gnu.version_d + *(.gnu.version_d) + +.gnu.version_r + *(.gnu.version_r) + +.rel.dyn 0x0000000008048094 0x0 + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + .rel.text 0x0000000008048094 0x0 build/main.o + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + .rel.got 0x0000000008048094 0x0 build/main.o + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + *(.rel.ifunc) + +.rel.plt 0x0000000008048094 0x0 + *(.rel.plt) + [!provide] PROVIDE (__rel_iplt_start = .) + *(.rel.iplt) + .rel.iplt 0x0000000008048094 0x0 build/main.o + [!provide] PROVIDE (__rel_iplt_end = .) + +.init + *(SORT_NONE(.init)) + +.plt 0x0000000008048098 0x0 + *(.plt) + *(.iplt) + .iplt 0x0000000008048098 0x0 build/main.o + +.plt.got + *(.plt.got) + +.plt.sec + *(.plt.sec) + +.text 0x00000000c0001500 0x297e + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + .text 0x00000000c0001500 0x11f build/main.o + 0x00000000c0001500 main + 0x00000000c00015a7 k_thread_HuSharp_1 + 0x00000000c00015cf k_thread_HuSharp_2 + 0x00000000c00015f7 k_thread_HuSharp_3 + .text.__x86.get_pc_thunk.bx + 0x00000000c000161f 0x4 build/main.o + 0x00000000c000161f __x86.get_pc_thunk.bx + .text 0x00000000c0001623 0x43 build/init.o + 0x00000000c0001623 init_all + .text 0x00000000c0001666 0x54f build/interrupt.o + 0x00000000c0001a57 intr_enable + 0x00000000c0001a8a intr_disable + 0x00000000c0001abd intr_set_status + 0x00000000c0001ae5 intr_get_status + 0x00000000c0001b0c register_handler + 0x00000000c0001b35 idt_init + .text.__x86.get_pc_thunk.ax + 0x00000000c0001bb5 0x4 build/interrupt.o + 0x00000000c0001bb5 __x86.get_pc_thunk.ax + .text 0x00000000c0001bb9 0x1b5 build/timer.o + 0x00000000c0001d06 timer_init + *fill* 0x00000000c0001d6e 0x2 + .text 0x00000000c0001d70 0x39d build/kernel.o + 0x00000000c0001d70 intr_exit + *fill* 0x00000000c000210d 0x3 + .text 0x00000000c0002110 0x172 build/print.o + 0x00000000c0002110 put_str + 0x00000000c000212e put_char + 0x00000000c00021fb put_int + 0x00000000c000225e set_cursor + .text 0x00000000c0002282 0xf4 build/debug.o + 0x00000000c0002282 panic_spin + .text 0x00000000c0002376 0x64f build/memory.o + 0x00000000c000240f pte_ptr + 0x00000000c0002449 pde_ptr + 0x00000000c000260b malloc_page + 0x00000000c00026e4 get_kernel_pages + 0x00000000c0002971 mem_init + .text 0x00000000c00029c5 0x28d build/bitmap.o + 0x00000000c00029c5 bitmap_init + 0x00000000c00029f8 bitmap_scan_test + 0x00000000c0002a42 bitmap_scan + 0x00000000c0002b8a bitmap_set + .text 0x00000000c0002c52 0x4ac build/string.o + 0x00000000c0002c52 memset + 0x00000000c0002cba memcpy + 0x00000000c0002d30 memcmp + 0x00000000c0002dc9 strcpy + 0x00000000c0002e34 strlen + 0x00000000c0002e95 strcmp + 0x00000000c0002f24 strchr + 0x00000000c0002f92 strrchr + 0x00000000c0003006 strcat + 0x00000000c0003086 strchrs + .text 0x00000000c00030fe 0x659 build/thread.o + 0x00000000c00030fe running_thread + 0x00000000c0003149 thread_create + 0x00000000c00031cd init_thread + 0x00000000c0003272 thread_start + 0x00000000c0003414 schedule + 0x00000000c0003559 thread_block + 0x00000000c00035d1 thread_unblock + 0x00000000c00036f2 thread_environment_init + .text.__x86.get_pc_thunk.dx + 0x00000000c0003757 0x4 build/thread.o + 0x00000000c0003757 __x86.get_pc_thunk.dx + *fill* 0x00000000c000375b 0x5 + .text 0x00000000c0003760 0x15 build/switch.o + 0x00000000c0003760 switch_to + .text 0x00000000c0003775 0x25f build/list.o + 0x00000000c0003775 list_init + 0x00000000c00037ad list_insert_before + 0x00000000c0003801 list_append + 0x00000000c0003829 list_remove + 0x00000000c0003872 list_push + 0x00000000c000389a list_pop + 0x00000000c00038c6 elem_find + 0x00000000c000390b list_traversal + 0x00000000c0003972 list_empty + 0x00000000c0003995 list_len + .text 0x00000000c00039d4 0x119 build/console.o + 0x00000000c00039d4 console_init + 0x00000000c00039ff console_acquire + 0x00000000c0003a2a console_release + 0x00000000c0003a55 console_put_str + 0x00000000c0003a85 console_put_char + 0x00000000c0003abd console_put_int + .text 0x00000000c0003aed 0x391 build/sync.o + 0x00000000c0003aed sema_init + 0x00000000c0003b27 lock_init + 0x00000000c0003b61 sema_down + 0x00000000c0003c7d sema_up + 0x00000000c0003d47 lock_acquire + 0x00000000c0003dcf lock_release + *(.gnu.warning) + +.fini + *(SORT_NONE(.fini)) + [!provide] PROVIDE (__etext = .) + [!provide] PROVIDE (_etext = .) + [!provide] PROVIDE (etext = .) + +.rodata 0x00000000c0003e80 0xa4d + *(.rodata .rodata.* .gnu.linkonce.r.*) + .rodata 0x00000000c0003e80 0x67 build/main.o + .rodata 0x00000000c0003ee7 0x11 build/init.o + .rodata 0x00000000c0003ef8 0x2cd build/interrupt.o + *fill* 0x00000000c00041c5 0x3 + .rodata 0x00000000c00041c8 0x6f build/timer.o + .rodata 0x00000000c0004237 0x41 build/debug.o + .rodata 0x00000000c0004278 0x138 build/memory.o + .rodata 0x00000000c00043b0 0x67 build/bitmap.o + *fill* 0x00000000c0004417 0x1 + .rodata 0x00000000c0004418 0xac build/string.o + .rodata 0x00000000c00044c4 0x2cf build/thread.o + *fill* 0x00000000c0004793 0x1 + .rodata 0x00000000c0004794 0x139 build/sync.o + +.rodata1 + *(.rodata1) + +.eh_frame_hdr + *(.eh_frame_hdr) + *(.eh_frame_entry .eh_frame_entry.*) + +.eh_frame 0x00000000c00048d0 0xad4 + *(.eh_frame) + .eh_frame 0x00000000c00048d0 0xa8 build/main.o + .eh_frame 0x00000000c0004978 0x24 build/init.o + 0x50 (松开之前的大小) + .eh_frame 0x00000000c000499c 0x1b4 build/interrupt.o + 0x1e0 (松开之前的大小) + .eh_frame 0x00000000c0004b50 0x94 build/timer.o + 0xd4 (松开之前的大小) + .eh_frame 0x00000000c0004be4 0x1c build/debug.o + 0x48 (松开之前的大小) + .eh_frame 0x00000000c0004c00 0x13c build/memory.o + 0x17c (松开之前的大小) + .eh_frame 0x00000000c0004d3c 0x90 build/bitmap.o + 0xbc (松开之前的大小) + .eh_frame 0x00000000c0004dcc 0x168 build/string.o + 0x194 (松开之前的大小) + .eh_frame 0x00000000c0004f34 0x174 build/thread.o + 0x1b4 (松开之前的大小) + .eh_frame 0x00000000c00050a8 0x148 build/list.o + 0x188 (松开之前的大小) + .eh_frame 0x00000000c00051f0 0xd8 build/console.o + 0x118 (松开之前的大小) + .eh_frame 0x00000000c00052c8 0xdc build/sync.o + 0x11c (松开之前的大小) + *(.eh_frame.*) + +.gcc_except_table + *(.gcc_except_table .gcc_except_table.*) + +.gnu_extab + *(.gnu_extab*) + +.exception_ranges + *(.exception_ranges .exception_ranges*) + 0x00000000c0007000 . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)) + +.eh_frame + *(.eh_frame) + *(.eh_frame.*) + +.gnu_extab + *(.gnu_extab) + +.gcc_except_table + *(.gcc_except_table .gcc_except_table.*) + +.exception_ranges + *(.exception_ranges .exception_ranges*) + +.tdata + *(.tdata .tdata.* .gnu.linkonce.td.*) + +.tbss + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + +.preinit_array 0x00000000c0007000 0x0 + [!provide] PROVIDE (__preinit_array_start = .) + *(.preinit_array) + [!provide] PROVIDE (__preinit_array_end = .) + +.init_array 0x00000000c0007000 0x0 + [!provide] PROVIDE (__init_array_start = .) + *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)) + *(.init_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors) + [!provide] PROVIDE (__init_array_end = .) + +.fini_array 0x00000000c0007000 0x0 + [!provide] PROVIDE (__fini_array_start = .) + *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)) + *(.fini_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .dtors) + [!provide] PROVIDE (__fini_array_end = .) + +.ctors + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT_BY_NAME(.ctors.*)) + *(.ctors) + +.dtors + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT_BY_NAME(.dtors.*)) + *(.dtors) + +.jcr + *(.jcr) + +.data.rel.ro + *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) + *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) + +.dynamic + *(.dynamic) + +.got 0x00000000c0007000 0x0 + *(.got) + .got 0x00000000c0007000 0x0 build/main.o + *(.igot) + 0x00000000c0007ff4 . = DATA_SEGMENT_RELRO_END (., (SIZEOF (.got.plt) >= 0xc)?0xc:0x0) + +.got.plt 0x00000000c0007000 0xc + *(.got.plt) + .got.plt 0x00000000c0007000 0xc build/main.o + 0x00000000c0007000 _GLOBAL_OFFSET_TABLE_ + *(.igot.plt) + .igot.plt 0x00000000c000700c 0x0 build/main.o + +.data 0x00000000c000700c 0x8c + *(.data .data.* .gnu.linkonce.d.*) + .data 0x00000000c000700c 0x0 build/main.o + .data 0x00000000c000700c 0x0 build/init.o + .data 0x00000000c000700c 0x0 build/interrupt.o + .data 0x00000000c000700c 0x0 build/timer.o + .data 0x00000000c000700c 0x84 build/kernel.o + 0x00000000c000700c intr_entry_table + .data 0x00000000c0007090 0x8 build/print.o + .data 0x00000000c0007098 0x0 build/debug.o + .data 0x00000000c0007098 0x0 build/memory.o + .data 0x00000000c0007098 0x0 build/bitmap.o + .data 0x00000000c0007098 0x0 build/string.o + .data 0x00000000c0007098 0x0 build/thread.o + .data 0x00000000c0007098 0x0 build/list.o + .data 0x00000000c0007098 0x0 build/console.o + .data 0x00000000c0007098 0x0 build/sync.o + +.data1 + *(.data1) + 0x00000000c0007098 _edata = . + [!provide] PROVIDE (edata = .) + 0x00000000c0007098 . = . + 0x00000000c0007098 __bss_start = . + +.bss 0x00000000c00070a0 0x2b8 + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + .bss 0x00000000c00070a0 0x0 build/main.o + .bss 0x00000000c00070a0 0x0 build/init.o + .bss 0x00000000c00070a0 0x108 build/interrupt.o + .bss 0x00000000c00071a8 0x0 build/timer.o + .bss 0x00000000c00071a8 0x0 build/debug.o + .bss 0x00000000c00071a8 0x0 build/memory.o + .bss 0x00000000c00071a8 0x0 build/bitmap.o + .bss 0x00000000c00071a8 0x0 build/string.o + .bss 0x00000000c00071a8 0x4 build/thread.o + .bss 0x00000000c00071ac 0x0 build/list.o + .bss 0x00000000c00071ac 0x1c build/console.o + .bss 0x00000000c00071c8 0x0 build/sync.o + *(COMMON) + *fill* 0x00000000c00071c8 0x18 + COMMON 0x00000000c00071e0 0x124 build/interrupt.o + 0x00000000c00071e0 intr_name + 0x00000000c0007280 idt_table + COMMON 0x00000000c0007304 0x4 build/timer.o + 0x00000000c0007304 ticks + COMMON 0x00000000c0007308 0x2c build/memory.o + 0x00000000c0007308 user_pool + 0x00000000c0007318 kernel_addr + 0x00000000c0007324 kernel_pool + COMMON 0x00000000c0007334 0x24 build/thread.o + 0x00000000c0007334 thread_ready_list + 0x00000000c0007344 thread_all_list + 0x00000000c0007354 main_thread + 0x00000000c0007358 . = ALIGN ((. != 0x0)?0x4:0x1) + 0x00000000c0007358 . = ALIGN (0x4) + 0x00000000c0007358 . = SEGMENT_START ("ldata-segment", .) + 0x00000000c0007358 . = ALIGN (0x4) + 0x00000000c0007358 _end = . + [!provide] PROVIDE (end = .) + 0x00000000c0007358 . = DATA_SEGMENT_END (.) + +.stab + *(.stab) + +.stabstr + *(.stabstr) + +.stab.excl + *(.stab.excl) + +.stab.exclstr + *(.stab.exclstr) + +.stab.index + *(.stab.index) + +.stab.indexstr + *(.stab.indexstr) + +.comment 0x0000000000000000 0x29 + *(.comment) + .comment 0x0000000000000000 0x29 build/main.o + 0x2a (松开之前的大小) + .comment 0x0000000000000029 0x2a build/init.o + .comment 0x0000000000000029 0x2a build/interrupt.o + .comment 0x0000000000000029 0x2a build/timer.o + .comment 0x0000000000000029 0x2a build/debug.o + .comment 0x0000000000000029 0x2a build/memory.o + .comment 0x0000000000000029 0x2a build/bitmap.o + .comment 0x0000000000000029 0x2a build/string.o + .comment 0x0000000000000029 0x2a build/thread.o + .comment 0x0000000000000029 0x2a build/list.o + .comment 0x0000000000000029 0x2a build/console.o + .comment 0x0000000000000029 0x2a build/sync.o + +.debug + *(.debug) + +.line + *(.line) + +.debug_srcinfo + *(.debug_srcinfo) + +.debug_sfnames + *(.debug_sfnames) + +.debug_aranges + *(.debug_aranges) + +.debug_pubnames + *(.debug_pubnames) + +.debug_info + *(.debug_info .gnu.linkonce.wi.*) + +.debug_abbrev + *(.debug_abbrev) + +.debug_line + *(.debug_line .debug_line.* .debug_line_end) + +.debug_frame + *(.debug_frame) + +.debug_str + *(.debug_str) + +.debug_loc + *(.debug_loc) + +.debug_macinfo + *(.debug_macinfo) + +.debug_weaknames + *(.debug_weaknames) + +.debug_funcnames + *(.debug_funcnames) + +.debug_typenames + *(.debug_typenames) + +.debug_varnames + *(.debug_varnames) + +.debug_pubtypes + *(.debug_pubtypes) + +.debug_ranges + *(.debug_ranges) + +.debug_macro + *(.debug_macro) + +.debug_addr + *(.debug_addr) + +.gnu.attributes + *(.gnu.attributes) + +/DISCARD/ + *(.note.GNU-stack) + *(.gnu_debuglink) + *(.gnu.lto_*) +OUTPUT(build/kernel.bin elf32-i386) diff --git a/c10/terminal_with_lock/build/kernel.o b/c10/terminal_with_lock/build/kernel.o new file mode 100644 index 0000000000000000000000000000000000000000..32eebd5a18a311b1fa92289f8b7fe8d82c8456db GIT binary patch literal 3232 zcmbVOO>9(E7`^it`bW!5vA0yH?P!W(g5$heYeiy4BLq!D2oQs&kq*olTF0gtUd&)L z3{hFIAjA-(F(kkOe-@@8XyQU*h;do5an{tyzkPv)4TFZ&YbVO zbKjkQoj2#sq0Qr45{ZPel~BvnqEad^#z>vlt2X%mr?@9!@2)|--LO$o`c_%jB03!? zb<*C1YiYUO1#Y;k=9VkIqhol1Q?IhUC~HJ_PQMsxAPrDh{xG$&g4y4eVf=D%a%qh@27(VaAoH5*Bz`GH)k z7-O2fLoe`n?M4sLo4>X6Z|N|4J-zQd-U_2P z(EH5ebsC+c_pZn5GMZnsYp;2{l}3-zd(q>ijNU}=NspH{I#2IDkJoMVGxS`Z~dVl@9boPFuXX*Xo@$NA?qW1;7n zLmn?NGFS>)T#w@y0D$7cC_0_z4Ng+1wR8n29 zzlFWvo&?OtK#js=X2lgKK0eH^xB|5UO1?nuCt5#zF^XQq6aRurk8fK-{KcQi6T73l#Gt}xo&@fM}zKMp}YBjT+ zIcs$IrD(wbj$m%xo)cEzfL=T^0@ZCTa2iRMGo|vwF=cnEsbaJTPj0!Sre>nbo)TUTIv1)_QQgW_bGf=}Rp_W;M?K=G z5l21hs8L5f=BP18J?^N2qxL##pQDP7Dmm)qMin~qh0c7TGhgV;7drEW&U~RWU+Byi zI`f6je4#U6=*$;7^M%fQp)+6T%r`9a+2_G>0?$_xg3KQa5c9uXcB-XxqCW{z<^k390Y@OI%{!n=hhgeQfI!aP!Ve_41|I1;W3GxxZz zCJd_Ay?9OFhv`P)ox=6~aQz+8r-i=~z9jsQa5uiE+~*s_+S6j8U)KUvH~- literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/list.o b/c10/terminal_with_lock/build/list.o new file mode 100644 index 0000000000000000000000000000000000000000..f9c18d2c085b70d5b9c37591ef36702902fb6f99 GIT binary patch literal 2884 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 literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/memory.o b/c10/terminal_with_lock/build/memory.o new file mode 100644 index 0000000000000000000000000000000000000000..7a6b310b44993bb440ab9a6c29f834ecb559a0fe GIT binary patch literal 5008 zcma)Ae{7Uz9sk}B=#SmtsEqx>?LZ(vxm&C9jD(n5Q^T;J=8 zQYRiHJI^MXP0g05i&Nub;(t0!{L!x4V39<~n4-bhn7Z8HqPL|n8raUy=Xsy!?!BXA zKJfPQe1AUQ=lgu0FAsgn>e=t}`NRyL1f)ud6vD2gL&0WQC1E9R1Wmmre6id=9?uq6 z7jBlzP8G|Ut!K%; z-krBf)4jf2tdx%xeHXLE;4xosoe5NSS)ue>E<4}V2-VrYhkO@v)?yxizj*VFIRut1 zhSeg=7;N5ZoGmnOl+qr_y`qTF#&SSpcHZy1xJ{%>a@Nhfbvw&7%j9Nx50pJxbsL9M zY!J~_cea?FS}bQ8+?{R7#}FNKu%pK+O5mssEb0xEusO2|oS*WTFhH_S^oLSV% z1d+fRs*tyedCAWE&dfMZ=BD~G{TRq1YYEAsks7jstO`ol;?Ia4THFm4vIQwtS0TTrfQsfFIc9d4pd;r<{W z>Kc@B4bIGDe$5lrMO{~P${0mgTXW*vjhJTVL&$FLF{zdZ(q6e9@YLa{F2p&VeUxc2 zzSK<)Ba+2sf{;*kaN25>P;dxnwh$W3S=Uy+Y+W0?RKT>LrmYs|Z)08%!kDmWjznhRIW6))Et;v%j? z!*SKmbp_>xrkT7ozfF$(dk+@W@}S!ChXxIuB|W3*PX&x6Ir6mF5kv z=2^J|rHs-+mE#&L7EZg@Kxu|}`Ao67K4snJttlrF*%WD;Y;#BZ7CD|sCle!|7)^|h zrB6irg&OHZDiL?qN$RNGpL8OhinK*GZHj1WcVv5KN1H?MCW%y=Tbq;XYHZwkZOkOsz* ziDn_MCc+AXazvbpA5GX!yl*7INJK{CBO_z|HrbL7ZkWrj-7qHy_U+pl*&OT3B%Ms; z;pmQN+qUf)MYVsWy))X@vBl+L|EuwF_`dX*_)a$Z?hgfnPvcA?;H{+q<2nA=yQ1-o zzx&Q*k7NV>Z@o4LH6`!T@||9tiPtDr@+08CiEoov-sj3A;C&U}fAPif)vWP^yG8`; zAL08vV%mkzs}tDnQ%cl1kMG-fdi)va)$w1d`YA$`42XQTUZ=aN6UG{6p|g+A7|)&q zyZn{4I-qj{?eI{&pB~o_pG=1tD+bb_26-cOJT0n#7Ara^@0Ke9Oal1h9RWcAo)M7c z!g}W5dHJEwvo*OT?HvJWko&f~mr@w1;^t+%Q9eW2%H_ud}+;QsxOTZimJd%JrqTTE~5k0&d0 z2I(a1l(VF(_@tx;Txlwv9PYQrQ^QHdI1W2+^J4&}jiVWkODf}_Pp1_QCmpjMyKAM* zim+<^YeeuQl;*odb^u%Pw%i2_-Xk)k=_KiUMV`|13qU_E(2s$%`-`So1EhU88~SYk zlD-Q_e>;ir)2HP_T7Hr=8t&JL_n{VnO${Qy(D+-8uL6S~#s85s{hr1f8hz+nl&=N0 zenjL^(hp!gApH$%%xL^F<vzH zE@}B+Nv{`qN6Y`E<^R$0AlfM9VIb}Ce>`QsUelX3-40}2pCk?a&uIBUEkCT~16rQa z@(C^f8s*sE)1*Hp@+@iidtTEol1APAM&s)m|ETdQkah(i@%?3&3hA&d2azy zeh-lH-J0&!bcA(^GkiqjfX34rf2#3<#*)U>KG&{O;}?i%v?=19P>PP97{$8;=%ig7 zGP87IBXX^9&-r_qD%A;TzqA;lnnhP+CMfrn!vS=CdZsa^uXt0 z+wfxRKMu!OEiq&drsJati5^XlWl{+5{~h0j;wq1F2j=V%><|3+5@n;ldcED?8$Dcu ziXN_j`aKEKuLkpe&=8m-eQMA*f0xpwDEvT9OE@R4iz}l1(f>w#v>}h<8ILd0#$PMu z)c-KOHf@8Y)OZ&Dc*bUMzZ`rEkiA?hUOKs^ zcS57#e*iu2FzZf($+_7FH_x7GhKZHGJn-hZgvZWd@R(=58>q*;)%XH^5=Omn&bJY+ ivC<0z>C5C8Q@Ma=&UVkbX3CoK>U|o<-=~2dM(=-OZNq{9 literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/print.o b/c10/terminal_with_lock/build/print.o new file mode 100644 index 0000000000000000000000000000000000000000..8bef927370394424e7db9b716cec3690f153cb33 GIT binary patch literal 1872 zcmbVLUuauZ7(cn~mhPX~!*l~hD+HYgX)4nRia4^g42x;k;us32H_16|=xuJe_e3@* zEOH-ATF^Iroq}&Z=#zGZPMhIW5ZqMgs}&&{HyxrbDvQ7G{z*=Y_}~ZUe!t)M{l4#f z=j7xiCWqn@;)>RFH>tw)0dQs>XgR#*tM=mNk42)FI(o}@NF1Y)!XkY620G8e_4Z-cG333s97WN8e1 z1cds~KIHXtfo`+usnO3?UP|s?-dF1|wSTHb&Acmvnf8sdFE(f8hez0JEqUqdf%opZ zDKC99@KgPY&k_NXjpoWkGFj^!P*2x7d)4t;XGj_Cd*pNS+IM2^llZXAkc-P}?e?r6 zt0(#)nUSyj^<{i%OpaccjZL{e$>blu%SXPFUUM=?J^J_RN4SXfi?x01GQ;QBch*>f zvDHZoD&*CH;kef{wtkaqwYWBC{Wb=~)WMwf&6{R(w(*CZVfgzwzkH-?vP3>zfQ{WS|_c3&b5vA(^IW)?{vGk zr|}>K)lwTESgAaq#dkXAUcO-(okFhcM{`)f6GK_*(iH%)^&r zff?)#fL9}m{JT5&r{HgD`4;d+jT<=d+ZsO%zOL~k_!k;~68r~^p8=;Hfni?YgvOVE z@7DM-@UX_;0)9~A9{@k3@y~%D*Z3yzag9F{Z4G}A=P$zFAI6C1plxgX2Ke(~J>{8I z%8x7vr!1_Iic>19)HItp#m7(1u+x(hX*Qjm$Yy48r}+z~PG)ngQdJxxp07$N9G~LF zMLsoW+vOfXg4-{Ls=~Y($OxoLF3+2V1-D`rgs=Cb)ZA9{S_KKMQdw{*M6oYKv|u_; z$($415vuA0n?|j7naLTgvB-2T3<&N-&)_KD1o=$pqx?VaRhs|+ literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/string.o b/c10/terminal_with_lock/build/string.o new file mode 100644 index 0000000000000000000000000000000000000000..51583620cd536c4493fb0eef68f1ad792a90c0c5 GIT binary patch literal 4000 zcmb7GTWpj?6rR7kZMRTtp(xkjHbDX^OADp6225L6Afa3WY(yhjw%aW&?QZK{q#zgL z7U^cG)}-+gF}x6;h|$C}JgC7ILwqCXi-<_DDshdLWKBpUsoyvMO#gqkRCJQrZ_b== z&Y3xL=KOoo+q~H@456DL9Kw=>*gDJ9)JRw^%ETj5Ukuro`gu?C_oa94PfSb{a#7k> z+V2}n9(RdE<-j0U>>e0=k#NX0Fi1DY3rY!Bv4(u{gzHHgBZ>%5F zHYV=9yR^UwfxXSyeN6RRQg$H`e~JZW>4cM?JZ4t$20TNzVU%3 z14FvqYNWQq?VMb%H>UBL_PMZ2oH6AaPae<89gNEztTT7eC3mo7$__HMa8@>qqm7ph zi@K5cl3e*j@kK^2Q+W}AoX9Lpdb38-J8sxR@%cqC#Qz9KTyEy+DVs6FK8Rz;IFv$` zad62vV1Ip=61TCx>0R+|chLI4;4$f>c3LOa)za*E8sYQ0R*a;sChr(F2hQoPRt2_C zx_Nern~W>zbsYKUpv5H)|?A)bHp$xliJGM-d8J(3GyrL)p!Xo^qM@*s@qCoAtY8O|E^jIzJ1$&7N@BK)v3pO^l- zr}$TAmfIN5EHEFG8E&53X}ui2G(`IceeTv+-0!a3;NIbDZWdh~EvsVjXh-CrCn)S1 z_p)W~STtxc19}ZLEm{y*JUJF3916!mcyGX9&k+;DJ%TJL1G_>IQ`YnCQ7K1*7?2mh zdSZJtCeqHOqhia(jnBB3`&tr_c*4ETv({6!a!o=?)hDX!JXJL-%vnTgE;^fWbhj{$ zxr{}nPG>(ViW_r_sX%MzPvax5Qw2@43O6Mk1qZ$w1x<1ql>X&>PQ>D{y52;8FQ4<4 z#p#FcF#LArbAlG<0(7^*3FLF0w>Vd!+gKpPoB5p67Kgci4IG3u_grljXFmKiID|lG zb2(iWXFWKdg7a!Vr`O^f0H?+&L~lN)$K;e_z7HHW&>q;7TI!tQ?u4D>5IBgfIr6MH zM4?S9@P#fCGcdz1E5E}A8G zp}WxOkFhw5uJx~Dd>&ovF`R4;$(j_g{}M~U$RST-O*y0$yKG3`oM9f%LA>6#)M8FS zvG~L$^FFTR$=4urEpwdfzXR%grhE&s&RxpCLe_ao`47lCCn^60S?3?+`;c|6QFhw$ zithB)vUEk zYjbSY*6KRu_xIM-dJcx-{+^&e-kykbdIG(|zomI+Q)9D#=jP3OynFq78=IQFe$f+% zbOimeo{osFek_70a;#;jg#ea5N@~)qclNqRCCi??^@7KQ;2*^qNa)9bB}n~c3RfxQ zzXIocAy8rk`3WHTr}`6aj$+AmkQKw%}2{F6ZP`L@Y_Tj@Vg z_@Tm&faL!MB>zt!`F|^Y0j`^VW-6=zlD`c|{w^T-ElS^^FskqW=%)lo{#GFQ+kxc!mA*}3x5C3f@;?KT|0R(8Z0Fdj9r zU5}!nE>Aqv8yB9|Ks+EkEwPyJM7vFa3eSVn-4pB%heHvpi*(0Bo-I3kD`W9MuoH`6 zG1TsFiw43W;W-%XPB0JuYkLL6**06{0NuKeH;&!)@OpEQA}zS*spJCy%K}TRL_aie5}q*!=1tbHL8K42Q3A Ifohuf4}>jM3IG5A literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/switch.o b/c10/terminal_with_lock/build/switch.o new file mode 100644 index 0000000000000000000000000000000000000000..ad152e9b9abf6a2dd7d27fc0b9e7fe3c65801457 GIT binary patch literal 448 zcmb<-^>JflWMqH=Mh0dE1doBi0V-hvrZpH?8JJ*7Nuoh!f-oCYmjI9>ie!QSl+6yL znUTZ|fa(;H_#iVtKoE$LU6KHlZ~%(205M2Ehz6M{3FQMRFjxRoCk^8OX_$F1p|J4a z&~6tMkxm65DbcME8yz2a7$ym#^-5AJN*MHtGm1-!N)nTRtjb&n4dElJ#ll9mkA>mm ne`F<~kO%n-1_VH^E6FHIO-#`*F3&7U&d>{HfH2}q@*%1K)EOb^ literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/sync.o b/c10/terminal_with_lock/build/sync.o new file mode 100644 index 0000000000000000000000000000000000000000..2c1ace4bbf5936f43e3a1e541bf328e4c33243ed GIT binary patch literal 3980 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 Gt@mFMZ@yXp literal 0 HcmV?d00001 diff --git a/c10/terminal_with_lock/build/timer.o b/c10/terminal_with_lock/build/timer.o new file mode 100644 index 0000000000000000000000000000000000000000..4fbacb5f0c6e8482a6c46187d1de018df33c74db GIT binary patch literal 2584 zcma)7%THWo6hHF-hKl7OKCmPm6Ew69x6o2VBZfx@T!2gpql=2yVdlcjz}&$*_Zl!& zW5(KgtYmy4vh$U_Gf(OgwI(rH60)l;1v_rYdfHAX1+Q)b(@wO1;YD%h6~`SO99 zsa$9;8`!N~#<&^U`)%>NB8bmEGnUzIbZ{+SexfEnQkM@k=3jXrx04O*WGA+>BU{aOp=KU(%L7PQ-!pI0Zir~QszFS^mb-CQnkJCEPq&Yt>8@|!0M8AK>p$UMi8I9D9>yy8E1^n2BLgq`mLBH(Gp8b?_cDW8b5zA1` zn4yKEZBM4naAZbJhNt5Ze@~CUeg0fKq^qNYqS{OGh<4VDCpANfkd10wkDGqXG0caK zIbG3|uE9eT*G(knIchAdM`tyI^4^wWzPY7HLjwbk`&&bkmTp@9N0kdod)s--wmM$z z=vLZ0PnEq$Lyi1_xL&?Qt}8y*i5jmri$m579}kvO{OLRHyXx-0yFyzBgQyj*Cq(QW zXMFIQ=S^>>!hNmS_jTVlea!YF9>d_3Tg2GUbur^{XV;6I&=2=p7$d07+&efglsR>9 zFL4Zl7HnbPn$V?BnESwYMv9kyFG-!zgCD1OsKRL?T4~=wcJey7;W;jJ{=37AoK107CyE=5;_ChH!$QrHlTw~IUt1NUq}PUcKLwg z?58YqlBNvpMN88o3u;<3k2>e7I%Vk*RaH99J=R64I^W%;Olzh(7r||_^cf{QPwLR{ zXn)_ZIyyKw78qB@`}&6iD(XaL(nrmhoF~K5^|(GQE-cN3^>{>0&&730M`Bvkn$;Zl zkmf8?O`8T8+H^c^IxZpauDhP>T{_l4bY9rqK(^P}#-;mS*c(9ay+l6)t1;QW2$NvT(=erE#{81q1NpQT0=xw&~>b}eNgG3*T_ze;FBflH* zUV*a$7X^OCh$HqbBOgU2y^u7+lR(obV@zs>HmjK0yh%zlY=%jhOs7dPQe}e!%F&x; zPdX}wZBZhrWKz?SQ%{+iGW2w)&3;Oe7}d0xI%R~D8Y$C8%9=xa|5x4v5!mVKXh5;~R?uSHod0zPMl5UHMk%{RPP_;>K!EJ5eG{E~28&i^n5 z&ylaoUoruFEYds!o%#R8YZr5{)wb**f%BeoT^2s4oV!vGXMi%saani#5HIyFJl+$Y z!}VFx;GHh3_LpNk76jT(d_EBMr7i0g1W$&qL-|pXfU?&dbmux>$Hs6Eyw3cB%5~> 8); +} + +// 时钟的中断处理函数 +static void intr_timer_handler(void) { + struct task_struct* cur_thread = running_thread();// 获取当前线程 + + ASSERT(cur_thread->stack_magic == 0x20000611);// 检查栈是否溢出 + + cur_thread->elapsed_ticks++; // + //从内核第一次处理时间中断后开始至今的滴哒数,内核态和用户态总共的嘀哒数 + ticks++; + + if(cur_thread->ticks == 0) {// 时间片用完,那就调度新的线程 + schedule(); + }else { // 否则将时间片 -1 + cur_thread->ticks--; + } + +} + +// 初始化 8253 +void timer_init(void) { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + register_handler(0x20, intr_timer_handler);// 将时钟中断置为 0x20 + put_str("timer_init done!\n"); +} + + diff --git a/c10/terminal_with_lock/device/timer.h b/c10/terminal_with_lock/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c10/terminal_with_lock/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c10/terminal_with_lock/include/boot.inc b/c10/terminal_with_lock/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c10/terminal_with_lock/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c10/terminal_with_lock/kernel/debug.c b/c10/terminal_with_lock/kernel/debug.c new file mode 100755 index 0000000..bef6acf --- /dev/null +++ b/c10/terminal_with_lock/kernel/debug.c @@ -0,0 +1,18 @@ +#include "debug.h" +#include "kernel/print.h" +#include "interrupt.h" + +/* 打印文件名,行号,函数名,条件并使程序悬停 */ +void panic_spin(char* filename, \ + int line, \ + const char* func, \ + const char* condition) \ +{ + intr_disable(); /* 打印文件名,行号,函数名,条件并使程序悬停 */ + put_str("\n\n\n!!!!! error !!!!!\n"); + put_str("filename:");put_str(filename);put_str("\n"); + put_str("line:0x");put_int(line);put_str("\n"); + put_str("function:");put_str((char*)func);put_str("\n"); + put_str("condition:");put_str((char*)condition);put_str("\n"); + while(1); +} \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/debug.h b/c10/terminal_with_lock/kernel/debug.h new file mode 100755 index 0000000..d4df982 --- /dev/null +++ b/c10/terminal_with_lock/kernel/debug.h @@ -0,0 +1,24 @@ +#ifndef __KERNEL_DEBUG_H +#define __KERNEL_DEBUG_H + +void panic_spin(char* filename, int line, const char* func, const char* condition); + +/*************************** __VA_ARGS__ ******************************* + * __VA_ARGS__ 是预处理器所支持的专用标识符。 + * 代表所有与省略号相对应的参数. + * "..."表示定义的宏其参数可变. + */ +#define PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) +/***********************************************************************/ + +#ifdef NDEBUG + #define ASSERT(CONDITION) ((void)0) +#else + #define ASSERT(CONDITION) \ + if (CONDITION) {} else { \ + /* 符号#让编译器将宏的参数转化为字符串字面量 */ \ + PANIC(#CONDITION); \ + } +#endif // __NDEBUG + +#endif // __KERNEL_DEBUG_H \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/global.h b/c10/terminal_with_lock/kernel/global.h new file mode 100755 index 0000000..7330902 --- /dev/null +++ b/c10/terminal_with_lock/kernel/global.h @@ -0,0 +1,39 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#define NULL ((void*)0) +#define bool int +#define true 1 +#define false 0 + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/init.c b/c10/terminal_with_lock/kernel/init.c new file mode 100755 index 0000000..4714c27 --- /dev/null +++ b/c10/terminal_with_lock/kernel/init.c @@ -0,0 +1,18 @@ +#include "init.h" +#include "print.h" +#include "interrupt.h" +#include "timer.h" +#include "memory.h" +#include "thread.h" +#include "console.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init();// 初始化中断 + + mem_init();//初始化内存管理系统 + thread_environment_init();// 初始化线程相关环境 + timer_init(); // 初始化 PIT + console_init(); // 初始化 console +} \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/init.h b/c10/terminal_with_lock/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c10/terminal_with_lock/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c10/terminal_with_lock/kernel/interrupt.c b/c10/terminal_with_lock/kernel/interrupt.c new file mode 100755 index 0000000..cb73d05 --- /dev/null +++ b/c10/terminal_with_lock/kernel/interrupt.c @@ -0,0 +1,192 @@ +#include "interrupt.h" +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" + +#define PIC_M_CTRL 0x20 // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define IDT_DESC_CNT 0x21 // 目前总共支持的中断数 + +#define EFLAGS_IF 0x00000200 // eflags寄存器中的if位为1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +/*中断门描述符结构体*/ +struct gate_desc { + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; //此项为双字计数字段,是门描述符中的第4字节。此项固定值,不用考虑 + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 静态函数声明,非必须 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // idt是中断描述符表,本质上就是个中断门描述符数组 + +char* intr_name[IDT_DESC_CNT]; // 用于保存异常的名字 +intr_handler idt_table[IDT_DESC_CNT]; // 定义中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口,最终调用的是ide_table中的处理程序 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; // 声明引用定义在kernel.S中的中断处理函数入口数组 + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + + /* 初始化主片 */ + outb (PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb (PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27. + outb (PIC_M_DATA, 0x04); // ICW3: IR2接从片. + outb (PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb (PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb (PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb (PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb (PIC_S_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 打开主片上IR0,也就是目前只接受时钟产生的中断 */ + outb (PIC_M_DATA, 0xfe); + outb (PIC_S_DATA, 0xff); + + put_str(" pic_init done\n"); +} + +/* 创建中断门描述符 */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16; +} + +/*初始化中断描述符表*/ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++) { + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if (vec_nr == 0x27 || vec_nr == 0x2f) { // 0x2f是从片8259A上的最后一个irq引脚,保留 + return; //IRQ7和IRQ15会产生伪中断(spurious interrupt),无须处理。 + } + /* 将光标置为0,从屏幕左上角清出一片打印异常信息的区域,方便阅读 */ + set_cursor(0); + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + + set_cursor(0); // 重置光标为屏幕左上角 + put_str("!!!!!!! excetion message begin !!!!!!!!\n"); + set_cursor(88); // 从第2行第8个字符开始打印 + put_str(intr_name[vec_nr]); + if (vec_nr == 14) { // 若为Pagefault,将缺失的地址打印出来并悬停 + int page_fault_vaddr = 0; + asm ("movl %%cr2, %0" : "=r" (page_fault_vaddr)); // cr2是存放造成page_fault的地址 + put_str("\npage fault addr is ");put_int(page_fault_vaddr); + } + put_str("\n!!!!!!! excetion message end !!!!!!!!\n"); + // 能进入中断处理程序就表示已经处在关中断情况下, + // 不会出现调度进程的情况。故下面的死循环不会再被中断。 + while(1); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++) { + +/* idt_table数组中的函数是在进入中断后根据中断向量号调用的, + * 见kernel/kernel.S的call [idt_table + %1*4] */ + idt_table[i] = general_intr_handler; // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; + +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if (INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断,sti指令将IF位置1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if (INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli指令将IF位置0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/* 在中断处理程序数组第vector_no个元素中注册安装中断处理程序function */ +void register_handler(uint8_t vector_no, intr_handler function) { +/* idt_table数组中的函数是在进入中断后根据中断向量号调用的, + * 见kernel/kernel.S的call [idt_table + %1*4] */ + idt_table[vector_no] = function; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); // 异常名初始化并注册通常的中断处理函数 + pic_init(); // 初始化8259A + + /* 加载idt */ + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m" (idt_operand)); + put_str("idt_init done\n"); +} diff --git a/c10/terminal_with_lock/kernel/interrupt.h b/c10/terminal_with_lock/kernel/interrupt.h new file mode 100755 index 0000000..427f7b0 --- /dev/null +++ b/c10/terminal_with_lock/kernel/interrupt.h @@ -0,0 +1,22 @@ +#ifndef __KERNEL_INTERRUPT_H +#define __KERNEL_INTERRUPT_H +#include "stdint.h" + +typedef void* intr_handler; +void idt_init(void); + +/* 定义中断的两种状态: + * INTR_OFF值为0,表示关中断, + * INTR_ON值为1,表示开中断 */ +enum intr_status { // 中断状态 + INTR_OFF, // 中断关闭 + INTR_ON // 中断打开 +}; + +enum intr_status intr_get_status(void); +enum intr_status intr_set_status (enum intr_status); +enum intr_status intr_enable (void); +enum intr_status intr_disable (void); +void register_handler(uint8_t vector_no, intr_handler function); + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/kernel.S b/c10/terminal_with_lock/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c10/terminal_with_lock/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/main.c b/c10/terminal_with_lock/kernel/main.c new file mode 100755 index 0000000..100e91c --- /dev/null +++ b/c10/terminal_with_lock/kernel/main.c @@ -0,0 +1,58 @@ +#include "kernel/print.h" +#include "init.h" +#include "debug.h" +#include "memory.h" +#include "thread.h" +#include "interrupt.h" +#include "console.h" + +void k_thread_HuSharp_1(void* args); +void k_thread_HuSharp_2(void* args); +void k_thread_HuSharp_3(void* args); +int main(void) { + put_str("I am kernel!\n"); + init_all(); + //ASSERT(1 == 2); + // asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + + // 进行内存分配 + /* 内核物理页分配 + void* addr = get_kernel_pages(3); + put_str("\n get_kernel_page start vaddr is: "); + put_int((uint32_t) addr); + put_str("\n"); + */ + + // 线程演示 + thread_start("k_thread_HuSharp_1", 31, k_thread_HuSharp_1, "agrA 31 "); + thread_start("k_thread_HuSharp_2", 8, k_thread_HuSharp_2, "agrB 8 "); + thread_start("k_thread_HuSharp_3", 20, k_thread_HuSharp_3, "agrc 20 "); + + intr_enable();// 打开时钟中断 + + while(1) { + console_put_str("Main "); + } + return 0; +} + +void k_thread_HuSharp_1(void* args) { + char* para = args; + while(1) { + console_put_str(para); + } +} + +void k_thread_HuSharp_2(void* args) { + char* para = args; + while(1) { + console_put_str(para); + } +} + +void k_thread_HuSharp_3(void* args) { + char* para = args; + while(1) { + console_put_str(para); + } +} \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/memory.c b/c10/terminal_with_lock/kernel/memory.c new file mode 100755 index 0000000..afbb653 --- /dev/null +++ b/c10/terminal_with_lock/kernel/memory.c @@ -0,0 +1,257 @@ +#include "memory.h" +#include "stdint.h" +#include "kernel/print.h" +#include "kernel/bitmap.h" +#include "global.h" +#include "debug.h" +#include "kernel/print.h" +#include "string.h" + +#define PG_SIZE 4096 + +/*************** 位图地址 ******************** + * 因为0xc009f000是内核主线程栈顶,0xc009e000是内核主线程的pcb. + * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000, + * 这样本系统最大支持4个页框的位图,即512M */ +#define MEM_BITMAP_BASE 0xc009a000 + +// 返回虚拟地址的 高 10 位(即pde),与中间 10 位(即pte) +#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) +#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) + +/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存, + * 使虚拟地址在逻辑上连续 */ +#define K_HEAP_START 0xc0100000 + +// 内存池结构,生成两个实例用于管理内核内存池和用户内存池 +struct pool { + // 本内存池用到的位图结构,用于管理物理内存 + struct bitmap pool_bitmap; + uint32_t phy_addr_start;// 本内存池所管理物理内存起始地址 + uint32_t pool_size; //本内存池字节容量 +}; + +struct pool kernel_pool, user_pool; // 生成全局内核内存池, 用户内存地址 +struct virtual_addr kernel_addr; // 给内核分配虚拟地址 + + +/* 在pf表示的虚拟内存池中申请pg_cnt个虚拟页, + * 成功则返回虚拟页的起始地址, 失败则返回NULL + */ +static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) { + int vaddr_start = 0;//用于存储分配的虚拟地址 + int bit_idx_start = -1;// 存储位图扫描函数的返回值 + uint32_t cnt = 0; + if(pf == PF_KERNEL) {// 说明在内核虚拟池中 + bit_idx_start = bitmap_scan(&kernel_addr.vaddr_bitmap, pg_cnt); + if(bit_idx_start == -1) {//说明没找到 + return NULL; + } + // 将已经选出的内存置为 1 ,表示已经使用 + while(cnt < pg_cnt) { + bitmap_set(&kernel_addr.vaddr_bitmap, bit_idx_start + cnt++, 1); + } + // 虚拟地址应当加上 页表所占的内存 + vaddr_start = kernel_addr.vaddr_start + bit_idx_start * PG_SIZE; + } else {// 用户进程池 还未完善 + + } + return (void*)vaddr_start; +} + +/********* 以下两个获取虚拟地址 addr 的 pde & pte 地址,为建立映射做准备 ********/ +// 得到虚拟地址 vaddr 对应的 pte 指针 +// 构造一个 访问该 pte 的32位地址,欺骗处理器 +uint32_t* pte_ptr(uint32_t vaddr) { + /* 先访问到页目录表自己(位于1023项目录项中) + \ + * 再用页目录项 pde (页目录内页表的索引)做为pte的索引访问到页表 + \ + * 再用 pte 的索引做为页内偏移 + */ + uint32_t* pte = (uint32_t*) (0xffc00000 + \ + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4); + return pte;// 得到物理页的虚拟地址,即通过这个虚拟地址,可以访问到该物理页(保护模式下必须通过 vaddr) +} + +// 得到虚拟地址 vaddr 对应的 pde 指针 +// 构造一个 访问该 pde 的32位地址,欺骗处理器 +uint32_t* pde_ptr(uint32_t vaddr) { + uint32_t* pde = (uint32_t*) (0xfffff000 + PDE_IDX(vaddr) * 4); + return pde; +} + +/* 在m_pool指向的物理内存池中分配1个物理页, + * 成功则返回页框的物理地址,失败则返回NULL + */ +static void* palloc(struct pool* m_pool) { + // 扫描或设置位图要保证原子操作 + int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); //找一个空闲物理页面 + if(bit_idx == -1) { + return NULL; + } + bitmap_set(&m_pool->pool_bitmap, bit_idx, 1); //表示已经占用 + // 成功则返回页框的物理地址 + uint32_t page_phyaddr = (m_pool->phy_addr_start + (bit_idx * PG_SIZE)); + return (void*)page_phyaddr; +} + +/********使用 pde_ptr 和 pte_ptr 来建立虚拟地址和物理地址的映射 ********/ +// 本质上便是将 pte 写入到 获取的 pde 项中,将 物理地址写入到 pte 中 +static void page_table_add(void* _vaddr, void* _page_phyaddr) { + uint32_t vaddr = (uint32_t)_vaddr; + uint32_t page_phyaddr = (uint32_t)_page_phyaddr; + // 以下两个函数都是通过 虚拟地址来获取 + uint32_t* pde = pde_ptr(vaddr); + uint32_t* pte = pte_ptr(vaddr); + +/************************ 注意 ************************* + * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte, + * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。 + * *********************************************************/ + // 先在页目录内判断目录项的P位,若为1,则表示该表(pte)已存在 + if(*pde & 0x00000001) {// 通过 P 位来判断目录项是否存在 + ASSERT(!(*pte & 0x00000001)); // 表示 pte 不存在 + + // 再判断 pte 是否存在 + if(!(*pte & 0x00000001)) { + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } else { // 并不会执行到此处,因为此处意思是:pde存在,pte也存在 + // 但是之前的 ASSERT 已经判断了 pde 不存在 + PANIC("pte repeat!"); + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } + } else {// pde 不存在 + // 需要分配 pte 物理页,采用 plloc 函数 + uint32_t new_pde_phyaddr = (uint32_t)palloc(&kernel_pool);//页表中的页框一律从内核中分配 + + *pde = (new_pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);// 写入到 pde 中 + + /* 分配到的物理页地址 new_pde_phyaddr 对应的物理内存清0,(使用时清零比回收后清零高效,因为不知道回收后会不会再使用) + * 避免里面的陈旧数据变成了页表项,从而让页表混乱. + * 访问到pde对应的物理地址,用pte取高20位便可. + * 因为pte是基于该pde对应的物理地址内再寻址, + * 把低12位置0便是该pde对应的物理页的起始 + */ + // 现需要获得新创建的这个物理页的虚拟地址(保护模式得看虚拟地址!) + memset(((void*)((int)pte & 0xfffff000)), 0, PG_SIZE);//将该新配物理页清0 + + ASSERT(!(*pte & 0x00000001));// 断言 pte 此时是否存在 + + // pte项 此时更改为新建物理页的位置 + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);//将 物理页地址 写入到 pte项中 + } +} + + +// 分配pg_cnt个页空间(物理页),成功则返回起始虚拟地址,失败时返回NULL +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) { + ASSERT(pg_cnt > 0 && pg_cnt < 3840); +/*********** malloc_page的原理是三个动作的合成: *********** + 1.通过vaddr_get在虚拟内存池中申请虚拟地址 + 2.通过palloc在物理内存池中申请物理页 + 3.通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射 +***************************************************************/ + void* vaddr_start = vaddr_get(pf, pg_cnt); + if(vaddr_start == NULL) { + return NULL; + } + + uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt; + struct pool* mem_pool = (pf & PF_KERNEL)? &kernel_pool : &user_pool; + + // 因为虚拟地址是连续的,但物理地址可以是不连续的,所以逐个做映射 + while(cnt-- > 0) { + void* page_phyaddr = palloc(mem_pool); + if(page_phyaddr == NULL) { + return NULL; + } + page_table_add((void*)vaddr, page_phyaddr);// 在页表建立映射 + vaddr += PG_SIZE;// 下一个虚拟页 + } + return vaddr_start; +} + +// 从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL +void* get_kernel_pages(uint32_t pg_cnt) { + void* vaddr = malloc_page(PF_KERNEL, pg_cnt); + if(vaddr != NULL) {// 虚拟地址是连续的 + memset(vaddr, 0, pg_cnt * PG_SIZE); + } + return vaddr; +} + +// 初始化内存池, 通过all_mem 初始化物理内存池相关结构 +static void mem_pool_init(uint32_t all_mem) { + put_str(" phy_mem_pool_init start!\n"); + // 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+ + // 第769~1022个页目录项共指向254个页表,共256个页框 + uint32_t page_table_size = PG_SIZE * 256; + // 用于记录当前已经使用的内存字节数 + uint32_t used_mem = page_table_size + 0x100000;// 0x100000为低端 1M 内存 + uint32_t free_mem = all_mem - used_mem; + uint16_t all_free_pages = free_mem / PG_SIZE;//转换为 页数 + + // 内核物理内存池 与 用户物理内存池 大小一致 + uint16_t kernel_free_pages = all_free_pages / 2; + uint16_t user_free_pages = all_free_pages - kernel_free_pages; + + /* 为简化位图操作,余数不处理,坏处是这样做会丢内存。 + * 好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存 + */ + uint32_t kbm_length = kernel_free_pages / 8; // Kernel BitMap的长度,位图中的一位表示一页,以字节为单位 + uint32_t ubm_length = user_free_pages / 8; // User BitMap的长度. + + uint32_t kp_start = used_mem; // kernel pool start 内核内存池的起始位置 + uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; + + kernel_pool.phy_addr_start = kp_start; + user_pool.phy_addr_start = up_start; + + kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;// 位图字节长度 + user_pool.pool_bitmap.btmp_bytes_len = ubm_length; + + kernel_pool.pool_size = kernel_free_pages * PG_SIZE; + user_pool.pool_size = user_free_pages * PG_SIZE; + +/********* 内核内存池和用户内存池位图 *********** + * 位图是全局的数据,长度不固定。 + * 全局或静态的数组需要在编译时知道其长度, + * 而我们需要根据总内存大小算出需要多少字节。 + * 所以改为指定一块内存来生成位图. + * ************************************************/ +// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右) +// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处. + kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE; + + // 用户内存池的位图紧跟在内核内存池位图后面 + user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length); + /******************** 输出内存池信息 **********************/ + put_str(" kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits); + put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start); + put_str("\n"); + put_str(" user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits); + put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start); + put_str("\n"); + + /**************** 进行初始化 ***************/ + // 将位图 置为 0 表示还未分配 + bitmap_init(&kernel_pool.pool_bitmap); + bitmap_init(&user_pool.pool_bitmap); + + // 初始化内核的虚拟地址 + // 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致 + kernel_addr.vaddr_bitmap.btmp_bytes_len = kbm_length; + // 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之后 + kernel_addr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); + // 虚拟内存池的起始地址,以达到进程在堆中动态申请内存 + kernel_addr.vaddr_start = K_HEAP_START; //内核堆 + bitmap_init(&kernel_addr.vaddr_bitmap); + put_str(" mem_pool_init done\n"); +} + +// 内存管理部分初始化入口 +void mem_init() { + put_str("mem_init start!\n"); + uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); // 储存机器上物理内存总量 + mem_pool_init(mem_bytes_total); //初始化内存池 + put_str("mem_init done!\n"); +} \ No newline at end of file diff --git a/c10/terminal_with_lock/kernel/memory.h b/c10/terminal_with_lock/kernel/memory.h new file mode 100755 index 0000000..405f8d7 --- /dev/null +++ b/c10/terminal_with_lock/kernel/memory.h @@ -0,0 +1,33 @@ +#ifndef __KERNEL_MEMORY_H +#define __KERNEL_MEMORY_H + +#include "stdint.h" +#include "bitmap.h" + +// 内存池的标记,用来判断使用哪个内存池 +enum pool_flags { + PF_KERNEL = 1, // 内核内存池 + PF_USER = 2 // 用户内存池 +}; + +#define PG_P_1 1 // 页表项或页目录项存在属性位 +#define PG_P_0 0 // 页表项或页目录项存在属性位 +#define PG_RW_R 0 // R/W 属性位值, 读/执行 +#define PG_RW_W 2 // R/W 属性位值, 读/写/执行 +#define PG_US_S 0 // U/S 属性位值, 系统级 +#define PG_US_U 4 // U/S 属性位值, 用户级 + +// 虚拟地址池,用于虚拟地址管理 +struct virtual_addr{ + struct bitmap vaddr_bitmap; // 虚拟地址用到的 bitmap 结构, 页 为单位 + uint32_t vaddr_start; // 虚拟地址起始地址 +}; + +extern struct pool kernel_pool, user_pool; +void mem_init(void); +void* get_kernel_pages(uint32_t pg_cnt); +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt); +void malloc_init(void); +uint32_t* pte_ptr(uint32_t vaddr); +uint32_t* pde_ptr(uint32_t vaddr); +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/lib/kernel/bitmap.c b/c10/terminal_with_lock/lib/kernel/bitmap.c new file mode 100755 index 0000000..323b793 --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/bitmap.c @@ -0,0 +1,82 @@ +#include "bitmap.h" +#include "stdint.h" +#include "string.h" +#include "print.h" +#include "interrupt.h" +#include "debug.h" + +// bitmap 的初始化 +void bitmap_init(struct bitmap* btmp) { + memset(btmp->bits, 0, btmp->btmp_bytes_len); +} + +// 判断 btmp 为指针处,bit_idx 位是否为 1,为 1 就返回 true +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) { + uint32_t byte_idx = bit_idx / 8;//用于标注字节 + uint32_t bit_odd = bit_idx % 8; //用于标注字节内的位 + return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd)); +} + +// 在位图中申请连续cnt个位,成功则返回其起始位下标,失败返回-1 +int bitmap_scan(struct bitmap* btmp, uint32_t cnt) { + uint32_t idx_byte = 0; // 用于记录空闲位所在的字节 + // 先逐字节比较,蛮力法 + // 0 表示空闲,若停止,则说明至少有一个空闲位 + while(( 0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) { + idx_byte++; + } + ASSERT(idx_byte < btmp->btmp_bytes_len);// 断言此时在内部 + if(idx_byte == btmp->btmp_bytes_len) { + return -1;// 访问失败,返回 -1 + } + + /* 若在位图数组范围内的某字节内找到了空闲位, + * 在该字节内逐位比对,返回空闲位的索引。 + */ + int idx_bit = 0;//字节内部位数 + while((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) { + idx_bit++; + }// 找到空闲位 + int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在bitmap 下标 + if(cnt == 1) { + return bit_idx_start; + } + + // 至此说明 cnt > 1 + // 因此首先进行剩余位数的判断,以免超过bitmap记录数 + uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start); + uint32_t idx_next_bit = bit_idx_start + 1; + uint32_t free_bit_cnt = 1;// 一定要记得记录一下 之前判断后 还有个空闲位 + + bit_idx_start = -1; // 先将其置为 -1,若找不到连续的位就直接返回 + while(bit_left-- > 0) { + // 调用 scan_test 函数,为 0 则为 false + if(!(bitmap_scan_test(btmp, idx_next_bit))) { + free_bit_cnt++; + } else { // 由于必须是连续的,因此只要遇到 不空闲的 位 便将记录cnt置为 0 + free_bit_cnt = 0; + } + // 若是已经满足空闲数 + if(free_bit_cnt == cnt) { + bit_idx_start = idx_next_bit - cnt + 1; + break; + } + idx_next_bit++;// 继续判断下一位 + } + + return bit_idx_start; +} + +// 将位图btmp的bit_idx位设置为value +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) { + ASSERT((value ==0) || (value == 1));// 只能赋予 0 或 1 + uint32_t byte_idx = bit_idx / 8; //字节位 + uint32_t bit_odd = bit_idx % 8; //位数 位 + + if(value) { + btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); + } else { + btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); + } + +} diff --git a/c10/terminal_with_lock/lib/kernel/bitmap.h b/c10/terminal_with_lock/lib/kernel/bitmap.h new file mode 100755 index 0000000..ab3d2db --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/bitmap.h @@ -0,0 +1,18 @@ +#ifndef __LIB_KERNEL_BITMAP_H +#define __LIB_KERNEL_BITMAP_H +#include "global.h" +#define BITMAP_MASK 1 // 用在位图中逐位判断,主要通过 按位与 & +struct bitmap{ + uint32_t btmp_bytes_len; + /* 在遍历位图时,整体上以字节为单位,细节上是以位为单位, + 所以此处位图的指针必须是单字节 */ + uint8_t* bits; +}; + +// 函数 +void bitmap_init(struct bitmap* btmp); +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx); +int bitmap_scan(struct bitmap* btmp, uint32_t cnt); +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value); + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/lib/kernel/io.h b/c10/terminal_with_lock/lib/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/lib/kernel/list.c b/c10/terminal_with_lock/lib/kernel/list.c new file mode 100755 index 0000000..0635cc3 --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/list.c @@ -0,0 +1,95 @@ +#include "list.h" +#include "interrupt.h" + +// 初始化双向链表 +void list_init (struct list* list) { + list->head.prev = NULL; + list->tail.next = NULL; + + list->head.next = &list->tail; + list->tail.prev = &list->head; +} + +// 将链表元素 before 插入在元素 elem 前 +void list_insert_before(struct list_elem* elem, struct list_elem* before) { + // 由于队列是公共资源,因此要保证为原子操作 + enum intr_status old_status = intr_disable();//关中断 + + // 更新节点 + elem->prev->next = before; + before->prev = elem->prev; + elem->prev = before; + before->next = elem; + + intr_set_status(old_status); +} + +void list_append(struct list* plist, struct list_elem* elem) { + list_insert_before(&plist->tail, elem);// 队列的FIFO +} + +void list_remove(struct list_elem* pelem) { + enum intr_status old_status = intr_disable(); + + pelem->prev->next = pelem->next; + pelem->next->prev = pelem->prev; + + intr_set_status(old_status);// 返回原状态 +} + +void list_push(struct list* plist, struct list_elem* elem) { + list_insert_before(plist->head.next, elem); +} + +// pop 操作,在唤醒时进行关中断 +struct list_elem* list_pop(struct list* plist) { + struct list_elem* elem = plist->head.next; + list_remove(elem); + return elem; +} + +// 查找元素 +bool elem_find(struct list* plist, struct list_elem* obj_elem) { + struct list_elem* elem = plist->head.next; + while(elem != &plist->tail) { + if(elem == obj_elem) { + return true; + } + elem = elem->next; + } + return false; +} + +// 遍历列表的所有元素,判断是否有 elem 满足条件 +// 判断方法采用 func 回调函数进行判断 +struct list_elem* list_traversal(struct list* plist, function func, int arg) { + struct list_elem* elem = plist->head.next; + // 如果队列为空 直接 return + if(list_empty(plist)) { + return NULL; + } + while(elem != &plist->tail) { + if(func(elem, arg)) { + return elem; + } + elem = elem->next; + } + return NULL; +} + +// 判断空 +bool list_empty(struct list* plist) { + return (plist->head.next == &plist->tail ? true : false); +} + +uint32_t list_len(struct list* plist) { + struct list_elem* elem = plist->head.next; + uint32_t len = 0; + while(elem != &plist->tail) { + elem = elem->next; + len++; + } + return len; +} + + diff --git a/c10/terminal_with_lock/lib/kernel/list.h b/c10/terminal_with_lock/lib/kernel/list.h new file mode 100755 index 0000000..3bd6ba0 --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/list.h @@ -0,0 +1,40 @@ +#ifndef __LIB_KERNEL_LIST_H +#define __LIB_KERNEL_LIST_H +#include "global.h" + + +// 令基址为 0 ,那么偏移量就等于 结构体中元素的偏移值 +#define offset(struct_type, member) (int)(&((struct_type*)0)->member) +// 通过结构体内部指针 转换为 代表该结构体 的方法: +// 将 elem_ptr(结构体内部元素指针) 的地址减去 elem_ptr 在结构体内部的偏移量 +// 从而获取所在结构体的地址,再将该地址转换为 struct 类型 +// struct_member_name 为内部变量名,主要用于 offset 函数 获取结构体内偏移值 +#define elem2entry(struct_type, struct_member_name, elem_ptr) \ + (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name)) +// 节点,不需要 data 域 +struct list_elem { + struct list_elem* prev;// 前驱 + struct list_elem* next;// 后继 +}; + +// 链表结构,用来实现队列 +struct list { + struct list_elem head;// 头指针 + struct list_elem tail;// 尾指针 +}; + +typedef bool (function)(struct list_elem*, int arg); + +void list_init (struct list*); +void list_insert_before(struct list_elem* elem, struct list_elem* before); +void list_push(struct list* plist, struct list_elem* elem); +void list_iterate(struct list* plist); +void list_append(struct list* plist, struct list_elem* elem); +void list_remove(struct list_elem* pelem); +struct list_elem* list_pop(struct list* plist); +bool list_empty(struct list* plist); +uint32_t list_len(struct list* plist); +struct list_elem* list_traversal(struct list* plist, function func, int arg); +bool elem_find(struct list* plist, struct list_elem* obj_elem); + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/lib/kernel/print.S b/c10/terminal_with_lock/lib/kernel/print.S new file mode 100755 index 0000000..af4424f --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/print.S @@ -0,0 +1,239 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret + +global set_cursor +set_cursor: + pushad + mov bx, [esp+36] +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + popad + ret \ No newline at end of file diff --git a/c10/terminal_with_lock/lib/kernel/print.h b/c10/terminal_with_lock/lib/kernel/print.h new file mode 100755 index 0000000..97b3896 --- /dev/null +++ b/c10/terminal_with_lock/lib/kernel/print.h @@ -0,0 +1,9 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +void set_cursor(uint32_t cursor_pos); +#endif + diff --git a/c10/terminal_with_lock/lib/stdint.h b/c10/terminal_with_lock/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c10/terminal_with_lock/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c10/terminal_with_lock/lib/string.c b/c10/terminal_with_lock/lib/string.c new file mode 100755 index 0000000..ab47322 --- /dev/null +++ b/c10/terminal_with_lock/lib/string.c @@ -0,0 +1,121 @@ +#include "string.h" +#include "global.h" +#include "debug.h" + +/* 进行初始化 将dst_起始的size个字节置为value */ +// 通常用于内存分配时的数据清 0 +void memset(void* dst_, uint8_t value, uint32_t size) { + ASSERT(dst_ != NULL); + uint8_t* dst = (uint8_t*)dst_; + while (size-- > 0){ + *dst++ = value; + } +} + +/* 将src_起始的size个字节复制到dst_ */ +void memcpy(void* dst_, const void* src_, uint32_t size) { + ASSERT(dst_ != NULL && src_ != NULL); + uint8_t* dst = dst_; + const uint8_t* src = src_; + while(size-- > 0) { + *dst++ = *src++; + } +} + +/* 连续比较以地址a_和地址b_开头的size个字节, + 若相等则返回0,若a_大于b_返回+1,否则返回-1 */ +int memcmp(const void* a_, const void* b_, uint32_t size) { + const char* a = a_; + const char* b = b_; + ASSERT(a != NULL && b != NULL); + while(size-- > 0) { + if(*a != *b) { + return *a > *b ? 1 : -1; + } + a++; + b++; + } + // 至此说明相等 + return 0; +} + +/* 将字符串从src_复制到dst_ */ +char* strcpy(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* temp = dst_; // 暂存地址 + // 以 字符串结尾 '0' 作为终止条件 + while((*dst_++ = *src_++)); + return temp; +} + + +/* 返回字符串长度 */ +uint32_t strlen(const char* str) { + ASSERT(str != NULL); + const char* end = str;// 标记结尾处位置 + // 和strcpy 函数一样,遇到 '0'停止 + while(*end++); + return (end - str - 1); +} +/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */ +int8_t strcmp (const char* a, const char* b) { + ASSERT(a != NULL && b != NULL); + while(*a != 0 && *a == *b) { + a++; + b++; + } + // 如果 < 就返回 -1 + // 如果 >= 就前往 布尔式 + // 该布尔式表示,要是 > 就 返回 1, 否则只可能是 = ,布尔值刚好是 0 + return *a < *b ? -1 : *a > *b; +} + +/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + while(*str != 0) { + if(*str == ch) { + return (char*) str;// 强制转换 + } + str++; + } + return NULL; +} + +/* 从右往左查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strrchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + const char* last = NULL; + // 从左往右遍历,将 last 更新即可,这样便不用手动判断 '0' + while(*str != 0) { + if(*str == ch) { + last = str; + } + str++; + } + return (char*)last; +} + +/* 将字符串src_拼接到dst_后,将回拼接的串地址 */ +char* strcat(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* str = dst_; + while(*str++);// 循环到末尾 + --str;//得到拼接位置 + while((*str++ = *src_++)); + return dst_; +} + +/* 在字符串str中查找指定字符ch出现的次数 */ +uint32_t strchrs(const char* str, uint8_t ch) { + ASSERT(str != NULL); + uint32_t cnt = 0; + const char* temp = str; + while(*temp != 0) { + if(*temp == ch) { + cnt++; + } + temp++; + } + return cnt; +} \ No newline at end of file diff --git a/c10/terminal_with_lock/lib/string.h b/c10/terminal_with_lock/lib/string.h new file mode 100755 index 0000000..516630e --- /dev/null +++ b/c10/terminal_with_lock/lib/string.h @@ -0,0 +1,14 @@ +#ifndef __LIB_STRING_H +#define __LIB_STRING_H +#include "stdint.h" +void memset(void* dst_, uint8_t value, uint32_t size); +void memcpy(void* dst_, const void* src_, uint32_t size); +int memcmp(const void* a_, const void* b_, uint32_t size); +char* strcpy(char* dst_, const char* src_); +uint32_t strlen(const char* str); +int8_t strcmp (const char *a, const char *b); +char* strchr(const char* string, const uint8_t ch); +char* strrchr(const char* string, const uint8_t ch); +char* strcat(char* dst_, const char* src_); +uint32_t strchrs(const char* filename, uint8_t ch); +#endif diff --git a/c10/terminal_with_lock/makefile b/c10/terminal_with_lock/makefile new file mode 100644 index 0000000..27e0899 --- /dev/null +++ b/c10/terminal_with_lock/makefile @@ -0,0 +1,117 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o \ + $(BUILD_DIR)/string.o $(BUILD_DIR)/thread.o $(BUILD_DIR)/switch.o \ + $(BUILD_DIR)/list.o $(BUILD_DIR)/console.o $(BUILD_DIR)/sync.o + + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/string.o: lib/string.c lib/string.h lib/stdint.h kernel/global.h \ + lib/stdint.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ + kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ + lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h kernel/global.h lib/stdint.h \ + kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h \ + kernel/global.h kernel/global.h kernel/debug.h lib/kernel/print.h \ + lib/kernel/io.h kernel/interrupt.h lib/string.h lib/stdint.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h lib/stdint.h \ + kernel/global.h lib/kernel/bitmap.h kernel/memory.h lib/string.h \ + lib/stdint.h lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/console.o: device/console.c device/console.h lib/stdint.h \ + lib/kernel/print.h thread/sync.h lib/kernel/list.h kernel/global.h \ + thread/thread.h thread/thread.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h lib/kernel/list.h kernel/global.h \ + lib/stdint.h thread/thread.h lib/string.h lib/stdint.h kernel/debug.h \ + kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: boot/mbr.S + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: boot/loader.S + $(AS) $(ASIB) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/switch.o: thread/switch.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean tran all + +mk_dir: + if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi + +hd: + # echo ".....Writing mbr, loader and kernel to disk....." + dd if=$(BUILD_DIR)/mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +clean: + cd $(BUILD_DIR) && rm -f ./* + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + # echo ".....build kernel now....." + +all: mk_dir build hd tran + +tran: + cd /home/husharp/bochs_hu/bochs/ && bin/bochs -f bochsrc.disk diff --git a/c10/terminal_with_lock/thread/switch.S b/c10/terminal_with_lock/thread/switch.S new file mode 100755 index 0000000..27705cd --- /dev/null +++ b/c10/terminal_with_lock/thread/switch.S @@ -0,0 +1,37 @@ +[bits 32] +section .text +global switch_to +; switch_to 函数接收两个参数 +; 一个是当前线程 cur, 一个是下一个处理器线程 +; 因此该函数的作用是 保存当前线程,将下一个线程放到处理器中 +; +; struct thread_stack { +; uint32_t ebp; +; uint32_t ebx; +; uint32_t edi; +; uint32_t esi; +; +; 易知反向压栈 +switch_to: + ; 第一部分:保存中断前的上下文环境 + ; 压入到内核栈,因此之后将内核栈储存 + push esi + push edi + push ebx + push ebp + +; struct task_struct { +; uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + + mov eax, [esp + 20] ; 得到栈中参数 4*4 + 4 即 4 个压栈 和 1 个返回地址 + mov [eax], esp ; eax 指的是 PCB 的首地址,而第一个参数是 每个线程的内核栈 + + ; 往处理器上装载调度的新线程上下文 + mov eax, [esp + 24] ; 同理 + mov esp, [eax] + + pop ebp ; 此时恢复的是:新线程内核栈内部的上下文 + pop ebx + pop edi + pop esi + ret \ No newline at end of file diff --git a/c10/terminal_with_lock/thread/sync.c b/c10/terminal_with_lock/thread/sync.c new file mode 100644 index 0000000..87866e4 --- /dev/null +++ b/c10/terminal_with_lock/thread/sync.c @@ -0,0 +1,99 @@ +#include "sync.h" +#include "kernel/list.h" +#include "global.h" +#include "debug.h" +#include "interrupt.h" + +// 初始化信号量 +void sema_init(struct semaphore* psema, uint8_t value) { + psema->value = value; + list_init(&psema->waiters); +} + +// 初始化锁 +void lock_init(struct lock* plock) { + plock->holder = NULL; + plock->holder_repeat_nr = 0;//将重复量置为0 + // 本系统采用二元信号量 + sema_init(&plock->semaphore, 1);// 信号量初始化为 1 +} + +// 信号量的 down 操作 +void sema_down(struct semaphore* psema) { + enum intr_status old_status = intr_disable();// 先关中断 保证原子性 + // 用 while 保证一直对信号量进行判断 + while(psema->value == 0) {//循环判断value 要是一直为 0 便一直堵塞 + // 开始阻塞之前,不应该在该锁的等待队列中 + ASSERT(!elem_find(&psema->waiters, &running_thread()->general_tag)); + + if (elem_find(&psema->waiters, &running_thread()->general_tag)) { + PANIC("sema_down: thread bloked has been in block_waiter_list\n"); + } + // 将当前线程加入到该锁的等待队列中 + list_append(&psema->waiters, &running_thread()->general_tag); + // 调用阻塞函数,去调度其他线程 + // 当调度线程为该锁的持有者时,运行完之后,就会将该 堵塞进程唤醒 + thread_block(TASK_BLOCKED);//堵塞态,一直等待到唤醒 + // 醒来后,由于是 while 因此会继续进行判断,发现此时为 1 就跳出循环 进行-1 + } + // 若 value 为1 ,或者是被唤醒后,即此时获取了锁 + psema->value--; + ASSERT(psema->value == 0); + // 恢复之前的中断状态 + intr_set_status(old_status); +} + +// 信号量中的 up 操作 +void sema_up(struct semaphore* psema) { + // 关中断 + enum intr_status old_status = intr_disable(); + ASSERT(psema->value == 0);// 此时应该为 0 ,进行唤醒 + if(!list_empty(&psema->waiters)) {//若锁的等待数组不为 0 ,需要唤醒(由于是二元信号量,唤醒一个即可 + struct task_struct* blocked_thread = elem2entry(struct task_struct, general_tag, list_pop(&psema->waiters)); + // 调用唤醒函数 + thread_unblock(blocked_thread); + }// 所谓唤醒,并非是直接开始执行,而是改变 thread 的 stat 使其加入到就绪队列中。 + // 并且为 push 加入,优先进行调度 + // 至此 锁的等待队列中没有在等待线程了 + psema->value++;// 归还锁 + ASSERT(psema->value == 1); + // 进行开中断 + intr_set_status(old_status); +} + +// 锁的获取函数 +// 主要值得注意的是,不能重复申请锁(以免变为死锁——即自己等待自己释放锁) +// 主要判断点在于 锁 中的 holder_repeat_nr 变量 +void lock_acquire(struct lock* plock) { + if (plock->holder != running_thread()) {//持有者不为当前者 + sema_down(&plock->semaphore);//对信号量执行 P 操作,可能会阻塞 + plock->holder = running_thread();// 此时获取到锁 + // 之前还未获取到锁 + ASSERT(plock->holder_repeat_nr == 0); + // 此时表示第一次获取到锁 + plock->holder_repeat_nr = 1; + } else {//持有者是当前者,未避免死锁,拒绝再获取锁,将申请次数++,直接返回即可 + plock->holder_repeat_nr++; + } +} + +// 释放锁函数 释放参数中的锁 +void lock_release(struct lock* plock) { + // 当前线程应该为锁的持有者 + ASSERT(plock->holder == running_thread()); + if (plock->holder_repeat_nr > 1) {//说明多次申请该锁,还不能进行释放 + plock->holder_repeat_nr--;// 因为要嵌套返回,且之前获取锁并未操作 + return; + } + ASSERT(plock->holder_repeat_nr == 1); + + // 改 plock 需要在 V 操作前面执行 + // 因为 释放锁的操作并未关中断 + // 若将 V 放在 holder 置为 NULL 之前,那么可能出现如下情况: + // 当前线程刚刚执行完 V ,还未置为 NULL 便被调度为新线程 + // 新线程将 holder 置为 新线程的 PCB + // 此时又被调度为 置为 NULL ,易知不得行 + plock->holder = NULL; + plock->holder_repeat_nr = 0; + sema_up(&plock->semaphore);// 信号量的 V 操作 +} \ No newline at end of file diff --git a/c10/terminal_with_lock/thread/sync.h b/c10/terminal_with_lock/thread/sync.h new file mode 100644 index 0000000..738e00f --- /dev/null +++ b/c10/terminal_with_lock/thread/sync.h @@ -0,0 +1,30 @@ +#ifndef __THREAD_SYNC_H +#define __THREAD_SYNC_H +#include "kernel/list.h" +#include "stdint.h" +#include "thread.h" + +// 信号量 +struct semaphore { + uint8_t value;// 信号量的值,会有初始值 + struct list waiters;// 记录此信号量上阻塞的所有线程 +}; + +// 锁结构 +struct lock { + struct task_struct* holder; //锁的持有者 + struct semaphore semaphore; // 用二元信号量来实现锁 + // 由于有时候可能在还未释放锁之前,会再次调用申请锁的函数 + // 因此需要记录是否之前申请过锁 + uint32_t holder_repeat_nr; // 锁的持有者重复申请锁的次数 +}; + +/******* 函数实现 ***************/ +void sema_init(struct semaphore* psema, uint8_t value); +void sema_down(struct semaphore* psema); +void sema_up(struct semaphore* psema); +void lock_init(struct lock* plock); +void lock_acquire(struct lock* plock); +void lock_release(struct lock* plock); + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/thread/thread.c b/c10/terminal_with_lock/thread/thread.c new file mode 100755 index 0000000..fb0fd22 --- /dev/null +++ b/c10/terminal_with_lock/thread/thread.c @@ -0,0 +1,202 @@ +#include "thread.h" +#include "stdint.h" +#include "string.h" +#include "global.h" +#include "debug.h" +#include "interrupt.h" +#include "print.h" +#include "memory.h" + +#define PG_SIZE 4096 + +// 定义主线程的 PCB,因为进入内核后,实则上一直执行的是 main 函数 +struct task_struct* main_thread; // 主线程 PCB +struct list thread_ready_list; // 就绪队列 +// 当线程不为就绪态时,会从所有线程队列中找到它 +struct list thread_all_list; // 所有线程队列 +// 队列是以 elem 的形式储存在 list 队列中,因此需要一个 elem 变量来将其取出转换 +static struct list_elem* thread_tag; // 用于保存队列中的线程节点 + +// switch_to函数的外部声明 global +extern void switch_to(struct task_struct* cur, struct task_struct* next); + + +// 取当前线程的 PCB 指针 +struct task_struct* running_thread(void) { + uint32_t esp; + asm("mov %%esp, %0" : "=g"(esp)); + // 取 esp 的前20位,PCB 的栈顶就为 0级栈 + return (struct task_struct*)(esp & 0xfffff000); +} + +// 不能按照以往的函数调用, 比如 kernel_thread(function, func_arg) +// 因为 我们此处采用的是 ret 返回,而不是 call , +// 因此需要采用存储到 kernel_thread 的栈中,存储参数和占位返回地址的方式 + +/* 由kernel_thread去执行function(func_arg) */ +static void kernel_thread(thread_func* function, void* func_arg) { + // 由于线程的运行是由调度器中断调度,进入中断后,处理器会自动关中断。 + // 执行 function 前开中断,避免之后的时钟中断被屏蔽,从而无法调度其他线程 + intr_enable(); + function(func_arg); // 调用 function +} + +// 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) { + // 由于 init_thread 中,已经指向最顶端了 + // 先预留中断使用栈的空间,可见 thread.h 中定义的结构 + // 中断栈用于保存中断时的上下文,实现用户进程时,初始值也会放在中断栈 + pthread->self_kstack -= sizeof(struct intr_stack); + + // 再留出线程栈空间 + pthread->self_kstack -= sizeof(struct thread_stack); + + // 定义线程栈指针 + struct thread_stack* kthread_stack = (struct thread_stack*) pthread->self_kstack; + + // 为 kernel_thread 中 能调用 function 做准备 + // kernel_thread 是第一个函数, eip直接指向它,然后再调用其他的 function + kthread_stack->eip = kernel_thread; + kthread_stack->function = function; + kthread_stack->func_arg = func_arg; + // 初始化为 0 ,在还未执行函数前,寄存器不应该有值 + kthread_stack->ebp = kthread_stack->ebx = \ + kthread_stack->edi = kthread_stack->esi = 0; +} + + +// 初始化线程基本信息 +void init_thread(struct task_struct* pthread, char* name, int prio) { + // + memset(pthread, 0, sizeof(*pthread)); + strcpy(pthread->name, name); + // 将主函数也封装为一个线程,且由于其一直运行,因此状态赋为 Running + if(pthread == main_thread) { + pthread->status = TASK_RUNNING; + } else { + pthread->status = TASK_READY; + } + + + // 此处是为演习,因此直接将 状态置为 running + pthread->priority = prio; + pthread->ticks = prio; + pthread->elapsed_ticks = 0; //表示还未执行过 + pthread->pgdir = NULL; //线程没有自己的地址空间 + pthread->stack_magic = 0x20000611; //自定义一个魔数 + // self_kstack 是线程自己在内核态下使用的栈顶地址,指向最顶端 + pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE); +} + +// 线程创建函数 +//创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) { + // pcb 位于内核空间,包括用户进程的 pcb 也是在内核空间中 + + // 先通过 内核物理页申请函数 申请一页 + struct task_struct* thread = get_kernel_pages(1); + // 由于 get_kernel_page 获取的是起始位置,因此获取的是 pcb 最低地址 + // 初始化新建线程 + init_thread(thread, name, prio); + // 创建线程 + thread_create(thread, function, func_arg); + // 至此 线程得到初始化和创建后,需要加入到就绪队列和全局队列中 + + // 首先需要判断不在就绪队列中 + ASSERT(!elem_find(&thread_ready_list, &thread->general_tag)); + // 加入就绪队列 + list_append(&thread_ready_list, &thread->general_tag); + // 首先需要判断不在全局队列中 + ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag)); + // 加入就绪队列 + list_append(&thread_all_list, &thread->all_list_tag); + + + // 简陋版本(以后改为 switch_to + // 作用为:开启线程 + // 由于 thread_create 中将 self_kstack 指向线程栈的最低处,现在将 self_kstack 作为栈顶 + // ret 操作时,由于 thread_create 函数中,将 eip 指向了 kernel_thread,因此 ret时,会跳转到该函数 + return thread; +} + + +// 将kernel中的main函数完善为主线程 +static void make_main_thread(void) { + // 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000, + // 就是为其预留了tcb,地址为0xc009e000,因此不需要通过get_kernel_page另分配一页 + // 直接 init_thread 即可 + main_thread = running_thread();// 获取当前的PCB指针 + init_thread(main_thread, "main", 31); + + // main函数是当前线程,当前线程不在thread_ready_list中 + // 只用加到 全局线程队列中 + ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag)); + list_append(&thread_all_list, &main_thread->all_list_tag); +} + + +// 实现任务调度 读写就绪队列 +void schedule(void) { + ASSERT(intr_get_status() == INTR_OFF); + + struct task_struct* cur = running_thread();// 取出当前线程的PCB + if(cur->status == TASK_RUNNING) {// 只是时间片为 0 ,而非阻塞 + ASSERT(!elem_find(&thread_ready_list, &cur->general_tag)); + list_append(&thread_ready_list, &cur->general_tag); + // 现将当前线程的 ticks 再次赋为 prio + cur->ticks = cur->priority; + cur->status = TASK_READY; + } + //由于还未实现 idle 线程,因此可能出现 ready_list 中无线程可调度的情况 + // 因此先进行断言 ready 队列中是否存在元素 + ASSERT(!list_empty(&thread_ready_list)); + thread_tag = NULL; // 首先将全局变量清空 + // 将就绪进程中的第一个线程(头结点)弹出 + thread_tag = list_pop(&thread_ready_list); + // 现在获得了 PCB 的 elem 节点,需要将其转换为 PCB + struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag); + next->status = TASK_RUNNING;// 调度 + switch_to(cur, next); +} + +// 当前线程将自己阻塞,标志其状态为stat +void thread_block(enum task_status stat) { + // stat取值为TASK_BLOCKED,TASK_WAITING,TASK_HANGING 指不可运行 + ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING))); + + enum intr_status old_status = intr_disable();// 保存中断前状态 + // 当前运行的必然为 RUNNing态 + struct task_struct* cur_thread = running_thread();// 获取当前线程的PCB + cur_thread->status = stat; + schedule();//进行调度 + // 待当前线程被解除阻塞后才继续运行下面的函数 + intr_set_status(old_status); +} + +// 将线程 pthread 解除阻塞 唤醒 +// 被阻塞的线程必须来等别人唤醒自己 +void thread_unblock(struct task_struct* pthread) { + enum intr_status old_status = intr_disable(); + ASSERT(((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGING))); + if(pthread->status != TASK_READY) { + // 在就绪队列中没有该阻塞线程 + ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag)); + if (elem_find(&thread_ready_list, &pthread->general_tag)) { + PANIC("thread_unblock: blocked thread in ready_list\n"); + } + list_push(&thread_ready_list, &pthread->general_tag);//push 让其优先被调度 + pthread->status = TASK_READY; + } + intr_set_status(old_status); +} + + +// 初始化线程环境 +void thread_environment_init(void) { + put_str("thread_init start!\n"); + list_init(&thread_ready_list); + list_init(&thread_all_list); + // 将 main 函数创建为 线程 + make_main_thread(); + put_str("thread_init done!\n"); +} \ No newline at end of file diff --git a/c10/terminal_with_lock/thread/thread.h b/c10/terminal_with_lock/thread/thread.h new file mode 100755 index 0000000..add584d --- /dev/null +++ b/c10/terminal_with_lock/thread/thread.h @@ -0,0 +1,108 @@ +#ifndef __THREAD_THREAD_H +#define __THREAD_THREAD_H +#include "stdint.h" +#include "list.h" + +// 自定义通用函数类型,它将在很多线程函数中做为形参类型 +typedef void thread_func(void*); + +// 进程或线程的状态 +enum task_status { + TASK_RUNNING, + TASK_READY, + TASK_BLOCKED, + TASK_WAITING, + TASK_HANGING, + TASK_DIED +}; + +/*********** 中断栈intr_stack *********** + * 此结构用于中断发生时保护程序(线程或进程)的上下文环境: + * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 + * 寄存器, intr_exit中的出栈操作是此结构的逆操作 + * 此栈在线程自己的内核栈中位置固定,所在页的最顶端 +********************************************/ +struct intr_stack { + uint32_t vec_no; // kernel.S 宏VECTOR中push %1压入的中断号 + uint32_t edi; + uint32_t esi; + uint32_t ebp; + // 虽然pushad把esp也压入,但esp是不断变化的,所以会被popad忽略 + uint32_t esp_dummy; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + +/* 以下由cpu从低特权级进入高特权级时压入 */ + uint32_t err_code; // err_code会被压入在eip之后 + void (*eip) (void); + uint32_t cs; + uint32_t eflags; + void* esp; + uint32_t ss; +}; + +/*********** 线程栈thread_stack *********** + * 线程自己的栈,用于存储线程中待执行的函数 + * 此结构在线程自己的内核栈中位置不固定, + * 用在switch_to时保存线程环境。 + * 实际位置取决于实际运行情况。 + ******************************************/ +struct thread_stack { + uint32_t ebp; + uint32_t ebx; + uint32_t edi; + uint32_t esi; + + /* 线程第一次执行时,eip指向待调用的函数kernel_thread + 其它时候,eip是指向switch_to的返回地址*/ + void (*eip) (thread_func* func, void* func_arg); + + /***** 以下仅供第一次被调度上cpu时使用 ****/ + // 占位函数,迷惑 ret ,假装为返回地址,实则为获取参数提供栈顶位置 + // 用于找到函数参数位置 + void (*unused_retaddr); + thread_func* function; // 由 Kernel_thread 所调用的函数名 + void* func_arg; // 由 Kernel_thread 所调用函数所需要的的参数 +}; + +// 进程或线程的 pcb, 程序控制块 +// ticks 表示的是中断次数 +struct task_struct { + uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + char name[16]; // 记录任务的名字 + enum task_status status; // 线程状态 + uint8_t priority; // 线程优先级 + uint8_t ticks; // 每次在处理器上执行的时间滴答数 + // 已经执行的滴答数 + uint32_t elapsed_ticks;// elapsed : 流逝 + // 加入到就绪队列中 + struct list_elem general_tag; + // 值得注意的是 由于需要管控所有的线程资源, + // 但是一个 list_elem 只有两个指针,因此只能位于一个队列中 + // 所以创建新的队列 all_list 记录全部线程 + struct list_elem all_list_tag; + + uint32_t* pgdir; // 进程拥有独立地址空间(页表),线程为NULL + + // 通过判断栈的边界溢出魔数值是否发生变化 + uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出 + + +}; + +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg); +void init_thread(struct task_struct* pthread, char* name, int prio); +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg); +struct task_struct* running_thread(void); +void schedule(void); +void thread_environment_init(void); +void thread_block(enum task_status stat); +void thread_unblock(struct task_struct* pthread); + +#endif \ No newline at end of file diff --git a/c10/terminal_with_lock/tmp b/c10/terminal_with_lock/tmp new file mode 100755 index 0000000..b76e9fe --- /dev/null +++ b/c10/terminal_with_lock/tmp @@ -0,0 +1,206 @@ +#include "interrupt.h" +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define EFLAGS_IF 0x00000200 // eflags 寄存器的 if 位为 1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + // 处理伪中断,,无法通过 IMR 寄存器屏蔽,因此单独处理 + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + // 将光标置为 0 ,从屏幕左上角清出一片打印异常信息的区域 + set_cursor(0);// 有时候甚至可能中断错误是由光标错误引起 + // 因此先将左上角空出四行来 + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + // 至此 已经将左上角空出四行,因此将光标再移到 0 位置,进行输出 + set_cursor(0); + put_str("!!!!!! exception message begin !!!!!!"); + set_cursor(88);// 第二行第 8 列 + put_str(intr_name[vec_nr]); + if(vec_nr == 14) { // 若为 pagefault,将缺失的地址打印出来 + int page_fault_vaddr = 0; + // 发生 pagefult + asm("movl %%cr2, %0" : "=r" (page_fault_vaddr));// cr2 是存放造成 page_fault 的地址 + put_str("\npage fault addr is "); + put_int(page_fault_vaddr); + } + put_str("!!!!!! exception message end !!!!!!"); + // 能进入中断处理程序就表示已经处在关中断情况下 + // 不会出现 +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void +exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断, sti 指令将 IF 位 置 1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli 指令将 IF 位 置 0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + // 判断 IF 位的值是否为 1 + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} + +// 在中断处理程序数组第 vector_no 个元素中注册安装中断处理程序 function +void register_handler(uint8_t vector_no, intr_handler function) { + idt_table[vector_no] = function; +} \ No newline at end of file diff --git a/c2/a/boot/mbr.S b/c2/a/boot/mbr.S new file mode 100755 index 0000000..8af6ffe --- /dev/null +++ b/c2/a/boot/mbr.S @@ -0,0 +1,59 @@ +;主引导程序 +;------------------------------------------------------------ +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + +; 清屏 利用0x06号功能,上卷全部行,则可清屏。 +; ----------------------------------------------------------- +;INT 0x10 功能号:0x06 功能描述:上卷窗口 +;------------------------------------------------------ +;输入: +;AH 功能号= 0x06 +;AL = 上卷的行数(如果为0,表示全部) +;BH = 上卷行属性 +;(CL,CH) = 窗口左上角的(X,Y)位置 +;(DL,DH) = 窗口右下角的(X,Y)位置 +;无返回值: + mov ax, 0x600 + mov bx, 0x700 + mov cx, 0 ; 左上角: (0, 0) + mov dx, 0x184f ; 右下角: (80,25), + ; VGA文本模式中,一行只能容纳80个字符,共25行。 + ; 下标从0开始,所以0x18=24,0x4f=79 + int 0x10 ; int 0x10 + +;;;;;;;;; 下面这三行代码是获取光标位置 ;;;;;;;;; +;.get_cursor获取当前光标位置,在光标位置处打印字符. + mov ah, 3 ; 输入: 3号子功能是获取光标位置,需要存入ah寄存器 + mov bh, 0 ; bh寄存器存储的是待获取光标的页号 + + int 0x10 ; 输出: ch=光标开始行,cl=光标结束行 + ; dh=光标所在行号,dl=光标所在列号 + +;;;;;;;;; 获取光标位置结束 ;;;;;;;;;;;;;;;; + +;;;;;;;;; 打印字符串 ;;;;;;;;;;; + ;还是用10h中断,不过这次是调用13号子功能打印字符串 + mov ax, message + mov bp, ax ; es:bp 为串首地址, es此时同cs一致, + ; 开头时已经为sreg初始化 + + ; 光标位置要用到dx寄存器中内容,cx中的光标位置可忽略 + mov cx, 5 ; cx 为串长度,不包括结束符0的字符个数 + mov ax, 0x1301 ; 子功能号13是显示字符及属性,要存入ah寄存器, + ; al设置写字符方式 ah=01: 显示字符串,光标跟随移动 + mov bx, 0x2 ; bh存储要显示的页号,此处是第0页, + ; bl中是字符属性, 属性黑底绿字(bl = 02h) + int 0x10 ; 执行BIOS 0x10 号中断 +;;;;;;;;; 打字字符串结束 ;;;;;;;;;;;;;;; + + jmp $ ; 使程序悬停在此 + + message db "1 MBR" + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c2/a/boot/mbr.S.comment b/c2/a/boot/mbr.S.comment new file mode 100755 index 0000000..e228b95 --- /dev/null +++ b/c2/a/boot/mbr.S.comment @@ -0,0 +1,63 @@ +; +;------------------------------------------------------------ +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; 0x06ŹܣϾȫУ +; ----------------------------------------------------------- +;INT 0x10 ܺ:0x06 :Ͼ +;------------------------------------------------------ +;룺 +;AH ܺ= 0x06 +;AL = Ͼ(Ϊ0,ʾȫ) +;BH = Ͼ +;(CL,CH) = Ͻǵ(X,Y)λ +;(DL,DH) = ½ǵ(X,Y)λ +;޷ֵ + mov ax, 0x600 + mov bx, 0x700 + mov cx, 0 ; Ͻ: (0, 0) + mov dx, 0x184f ; ½: (80,25), + ; ΪVGAıģʽУһֻ80ַ,25С + ; ±0ʼ0x18=24,0x4f=79 + int 0x10 ; int 0x10 + +;;;;;;;;; дǻȡλ ;;;;;;;;; +;.get_cursorȡǰλ,ڹλôӡַ,ĿDZӡַңDZ˵ +; ʵǷӲС˵,һ˼Ҳڹ괦ӡԼӡͬҲᱻ˸ǡ +; ܱˣԼľ + mov ah, 3 ; : 3ӹǻȡλ,ҪahĴ + mov bh, 0 ; bhĴ洢Ǵȡҳ + + int 0x10 ; : ch=꿪ʼ,cl= + ; dh=к,dl=к + +;;;;;;;;;;;;;; ȡλý ;;;;;;;;;;;;;;;; + + +;;;;;;;;; ӡַ ;;;;;;;;;;; + ;10hж,ǵ13ӹܴӡַ + mov ax, message + mov bp, ax ; es:bp Ϊ׵ַ, esʱͬcsһ£ͷʱѾΪsregʼ + + ; λҪõdxĴ,cxеĹλÿɺ + mov cx, 5 ; cx Ϊ,0ַ + mov ax, 0x1301 ; ӹܺ13ʾַ,ҪahĴ, + ; alдַʽ,ֻе2λ + ; ah=01,ʾַƶ + mov bx, 0x2 ; bh洢Ҫʾҳ,˴ǵ0ҳ, blַ, Ժڵ(bl = 02h) + int 0x10 ; ִBIOS 0x10 ж +;;;;;;;;; ַ ;;;;;;;;;;;;;;; + + jmp $; ; ʹͣڴ + + message db "1 MBR" + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c2/a/tool/mbr.sh b/c2/a/tool/mbr.sh new file mode 100755 index 0000000..bc1e450 --- /dev/null +++ b/c2/a/tool/mbr.sh @@ -0,0 +1 @@ +nasm -o mbr.bin mbr.S && dd if=./mbr.bin of=/home/work/my_workspace/bochs/hd60M.img bs=512 count=1 conv=notrunc diff --git a/c2/a/tool/xxd.sh b/c2/a/tool/xxd.sh new file mode 100755 index 0000000..564272a --- /dev/null +++ b/c2/a/tool/xxd.sh @@ -0,0 +1,21 @@ +#usage: sh xxd.sh 文件 起始地址 长度 +xxd -u -a -g 1 -s $2 -l $3 $1 + +#-u use upper case hex letters. Default is lower case. +# +#-a | -autoskip +# toggle autoskip: A single ’*’ replaces nul-lines. Default off. +# +#-g bytes | -groupsize bytes +# separate the output of every bytes (two hex characters or eight bit-digits each) by a whitespace. Specify -g 0 to +# suppress grouping. defaults to 2 in normal mode and 1 in bits mode. Grouping does not apply to postscript or +# include style. +# +#-c cols | -cols cols +# format octets per line. Default 16 (-i: 12, -ps: 30, -b: 6). Max 256. +# +#-s [+][-]seek +# start at bytes abs. (or rel.) infile offset. + indicates that the seek is relative to the current stdin file position +# (meaningless when not reading from stdin). - indicates that the seek should be that many characters from the end of +# the input (or if combined with +: before the current stdin file position). +# Without -s option, xxd starts at the current file position. diff --git a/c3/a/boot/mbr.S b/c3/a/boot/mbr.S new file mode 100755 index 0000000..7b11498 --- /dev/null +++ b/c3/a/boot/mbr.S @@ -0,0 +1,55 @@ +; +; +;LOADER_BASE_ADDR equ 0xA000 +;LOADER_START_SECTOR equ 0x2 +;------------------------------------------------------------ +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; +;0x06ŹܣϾȫУ +; ----------------------------------------------------------- +;INT 0x10 ܺ:0x06 :Ͼ +;------------------------------------------------------ +;룺 +;AH ܺ= 0x06 +;AL = Ͼ(Ϊ0,ʾȫ) +;BH = Ͼ +;(CL,CH) = Ͻǵ(X,Y)λ +;(DL,DH) = ½ǵ(X,Y)λ +;޷ֵ + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; Ͻ: (0, 0) + mov dx, 184fh ; ½: (80,25), + ; ΪVGAıģʽУһֻ80ַ,25С + ; ±0ʼ0x18=24,0x4f=79 + int 10h ; int 10h + + ; ɫɫǰɫɫַ"1 MBR" + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 ; Aʾɫ˸4ʾǰɫΪɫ + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + jmp $ ; ͨѭʹͣڴ + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c3/a/tool/mbr.sh b/c3/a/tool/mbr.sh new file mode 100755 index 0000000..bc1e450 --- /dev/null +++ b/c3/a/tool/mbr.sh @@ -0,0 +1 @@ +nasm -o mbr.bin mbr.S && dd if=./mbr.bin of=/home/work/my_workspace/bochs/hd60M.img bs=512 count=1 conv=notrunc diff --git a/c3/a/tool/xxd.sh b/c3/a/tool/xxd.sh new file mode 100755 index 0000000..564272a --- /dev/null +++ b/c3/a/tool/xxd.sh @@ -0,0 +1,21 @@ +#usage: sh xxd.sh 文件 起始地址 长度 +xxd -u -a -g 1 -s $2 -l $3 $1 + +#-u use upper case hex letters. Default is lower case. +# +#-a | -autoskip +# toggle autoskip: A single ’*’ replaces nul-lines. Default off. +# +#-g bytes | -groupsize bytes +# separate the output of every bytes (two hex characters or eight bit-digits each) by a whitespace. Specify -g 0 to +# suppress grouping. defaults to 2 in normal mode and 1 in bits mode. Grouping does not apply to postscript or +# include style. +# +#-c cols | -cols cols +# format octets per line. Default 16 (-i: 12, -ps: 30, -b: 6). Max 256. +# +#-s [+][-]seek +# start at bytes abs. (or rel.) infile offset. + indicates that the seek is relative to the current stdin file position +# (meaningless when not reading from stdin). - indicates that the seek should be that many characters from the end of +# the input (or if combined with +: before the current stdin file position). +# Without -s option, xxd starts at the current file position. diff --git a/c3/b/boot/include/boot.inc b/c3/b/boot/include/boot.inc new file mode 100755 index 0000000..debf640 --- /dev/null +++ b/c3/b/boot/include/boot.inc @@ -0,0 +1,5 @@ +;------------- loader和kernel ---------- +; 即为 宏名 equ 值 +LOADER_BASE_ADDR equ 0x900 +LOADER_START_SECTOR equ 0x2 + diff --git a/c3/b/boot/loader.S b/c3/b/boot/loader.S new file mode 100755 index 0000000..7d453b4 --- /dev/null +++ b/c3/b/boot/loader.S @@ -0,0 +1,30 @@ +%include "boot.inc" +section loader vstart=LOADER_BASE_ADDR + +; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR" +mov byte [gs:0x00],'2' +mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色 + +mov byte [gs:0x02],' ' +mov byte [gs:0x03],0xA4 + +mov byte [gs:0x04],'L' +mov byte [gs:0x05],0xA4 + +mov byte [gs:0x06],'O' +mov byte [gs:0x07],0xA4 + +mov byte [gs:0x08],'A' +mov byte [gs:0x09],0xA4 + +mov byte [gs:0x0a],'D' +mov byte [gs:0x0b],0xA4 + +mov byte [gs:0x0c],'E' +mov byte [gs:0x0d],0xA4 + +mov byte [gs:0x0e],'R' +mov byte [gs:0x0f],0xA4 + +jmp $ ; 通过死循环使程序悬停在此 + diff --git a/c3/b/boot/mbr.S b/c3/b/boot/mbr.S new file mode 100755 index 0000000..927d0a3 --- /dev/null +++ b/c3/b/boot/mbr.S @@ -0,0 +1,136 @@ +;主引导程序 +; +;LOADER_BASE_ADDR equ 0xA000 +;LOADER_START_SECTOR equ 0x2 +;------------------------------------------------------------ +# %include 为nasm自带的预处理 +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; 清屏 +;利用0x06号功能,上卷全部行,则可清屏。 +; ----------------------------------------------------------- +;INT 0x10 功能号:0x06 功能描述:上卷窗口 +;------------------------------------------------------ +;输入: +;AH 功能号= 0x06 +;AL = 上卷的行数(如果为0,表示全部) +;BH = 上卷行属性 +;(CL,CH) = 窗口左上角的(X,Y)位置 +;(DL,DH) = 窗口右下角的(X,Y)位置 +;无返回值: + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; 左上角: (0, 0) + mov dx, 184fh ; 右下角: (80,25), + ; 因为VGA文本模式中,一行只能容纳80个字符,共25行。 + ; 下标从0开始,所以0x18=24,0x4f=79 + int 10h ; int 10h + + ; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR" + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax, LOADER_START_SECTOR ; 起始扇区的lba地址 + mov bx, LOADER_BASE_ADDR ; 写入的地址 + mov cx, 1 ; 等待读入的扇区数 + + call rd_disk_m_16 ; 读取程序的起始部分 + + jmp LOADER_BASE_ADDR + +;------------------------------------------------------------------------------- +;功能:读取硬盘n个扇区 +; 函数名即为 在 16 位下读硬盘 +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax= LBA扇区号 + ; ebx= 将数据写入的内存地址 + ; ecx= 读入的扇区数 + mov esi, eax ;备份 eax + mov di, cx ; 备份 cx + +; 读写硬盘 +; 第 1 步: 设置要读取的扇区数 + mov dx, 0x1f2 + mov al, cl ; out 指令从 端口输入一个字节或字到AL或AX中 + out dx, al ;读取扇区数 + + mov eax, esi ;恢复备份 + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + ;LBA地址7~0位写入端口0x1f3 + mov dx, 0x1f3 + out dx, al + + ;LBA地址15~8位写入端口0x1f4 + mov cl, 8 + shr eax, cl ;右移8位 + mov dx, 0x1f4 + out dx, al + + ;LBA地址23~16位写入端口0x1f5 + shr eax, cl + mov dx, 0x1f5 + out dx, al + + ; 写入 device 寄存器的 低四位 + shr eax, cl + and al, 0x0f ;第 24~27位 + ; 现需要更改device寄存器的其他位置,设7~4位为1110 + or al, 0xe0 + mov dx, 0x1f6 + out dx, al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx, 0x1f7 + mov al, 0x20 + out dx, al + +;第4步:检测硬盘状态 +.not_ready: + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop ;增加延迟 减少打扰硬盘 + in al, dx + and al,0x88 ;第3位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al, 0x08 + jnz .not_ready ;循环等待 + +;第5步:从0x1f0端口读数据 + mov ax, di ;di 为之前备份的扇区数 + mov dx, 256 + mul dx ;一个扇区有512字节,每次读入一个字, + ; 共需di*512/2次,所以di*256 + mov cx, ax ; 将乘法结果次数写入 cx 为下面的写入做好准备 + ; 且我们知道 该 16 位乘法的结果不会大于16位寄存器 + mov dx, 0x1f0 ; data 寄存器读入数据 +.go_on_read: + in ax, dx ; 写入到 bx 指向的内存,由于 bx = 1~65535,因此写入数据不能超过 64kb + mov [bx], ax + add bx, 2 ;每次传输2个字节 + loop .go_on_read + ret ; 返回到 58 行的 call 指令 + + + times 510-($-$$) db 0 + db 0x55,0xaa \ No newline at end of file diff --git a/c4/a/boot/bochsrc b/c4/a/boot/bochsrc new file mode 100755 index 0000000..6052b65 --- /dev/null +++ b/c4/a/boot/bochsrc @@ -0,0 +1,17 @@ +#Bochs运行中使用的内存,设置为32Mmegs: 32#设置真实机器的BIOS和VGA BIOS +#修改成你们对应的地址 +romimage: file=/home/ahu/install/bochs/share/bochs/BIOS-bochs-latest +vgaromimage: file=/home/ahu/install/bochs/share/bochs/VGABIOS-lgpl-latest +#设置Bochs所使用的磁盘#设置启动盘符 +boot: disk +#设置日志文件的输出 +log: bochs.out +#开启或关闭某些功能,修改成你们对应的地址 +mouse: enabled=0 +#keyboard_mapping: enabled=1, map=/home/ahu/install/bochs/share/bochs/keymaps/x11-pc-us.map +keyboard: keymap=/home/ahu/install/bochs/share/bochs/keymaps/x11-pc-us.map +#硬盘设置 +ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +ata0-master: type=disk, path="hd60M.img", mode=flat, cylinders=121, heads=16, spt=63 +#增加bochs对gdb的支持,我们在这里不使用,所以注释掉了 +#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 \ No newline at end of file diff --git a/c4/a/boot/build.sh b/c4/a/boot/build.sh new file mode 100755 index 0000000..daadc5f --- /dev/null +++ b/c4/a/boot/build.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +echo "Creating disk.img..." +bximage -mode=create -hd=10M -q disk.img + +echo "Compiling..." +nasm -I include/ -o mbr.bin mbr.S +nasm -I include/ -o loader.bin loader.S + +echo "Writing mbr and loader to disk..." +dd if=mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + +echo "Now start bochs and have fun!" + +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c4/a/boot/include/boot.inc b/c4/a/boot/include/boot.inc new file mode 100755 index 0000000..5230bbb --- /dev/null +++ b/c4/a/boot/include/boot.inc @@ -0,0 +1,50 @@ +;------------- loader和kernel ---------- +; 即为 宏名 equ 值 +LOADER_BASE_ADDR equ 0x900 +LOADER_START_SECTOR equ 0x2 + +;-------------- gdt描述符属性 ------------- +; 段描述符的23位 表示粒度为 4K +DESC_G_4K equ 1_00000000000000000000000b ; 23 +DESC_D_32 equ 1_0000000000000000000000b ; 22 位 1表示在32位下 +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +; 第二部分段界限值,由于采用了32位平坦模型,所以段界限为(4GB / 4KB) - 1 = 0xFFFFF,故为全1 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b ; 代码段的 16-19的limit 总的段限界为fffff +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ; 数据段的 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b ; 限界为 bffff +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + + +; 代码段描述符的高32位表示,其中(0x00 << 24表示最高8位的段基址值 +; 由于我们采用的是平坦模型,故基址为零),后面唯一可变的就是段界限值 +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + \ + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +; 由于显存区为 0b8000, 因此段基址高四位为 b +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + + +; 选择子属性 +; 请求特权级 +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +; 表示是 GDT 还是 LDT +TI_GDT equ 000b +TI_LDT equ 100b \ No newline at end of file diff --git a/c4/a/boot/loader.S b/c4/a/boot/loader.S new file mode 100755 index 0000000..d711249 --- /dev/null +++ b/c4/a/boot/loader.S @@ -0,0 +1,107 @@ +%include "boot.inc" +section loader vstart=LOADER_BASE_ADDR +LOADER_STACK_TOP equ LOADER_BASE_ADDR + +jmp loader_start + + +; 加载GDT表 +; 一个dd为4字节,段描述符为8字节,上面为低4字节 +; 第一行的 0描述符不可用 因此直接置为 全 0 +GDT_BASE: dd 0x00000000 + dd 0x00000000 + +; GDT 中 先安放三个有用描述符 +; 代码段、数据段和栈段、显存段 +CODE_DESC: dd 0x0000FFFF ; 低32位表示 2字节base + 2字节段限界 + dd DESC_CODE_HIGH4 + +; 栈段和数据段共用,向上扩展 type的e为0 +DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + +; 显卡段,非平坦,从 0xb8000开始 +VIDEO_DESC: dd 0x80000007 ; 段界限为 (bffff - b8000 ) / 4K = 7 + dd DESC_VIDEO_HIGH4 + +GDT_SIZE equ $ - GDT_BASE ;$ 表示本行标号, 通过地址差获得size +GDT_LIMIT equ GDT_SIZE - 1 + +; 填充空白描述符 60 个 +times 120 dd 0 +; 构建选择子 +SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 +SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0 +SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 + +; GDTR 寄存器 +gdt_ptr dw GDT_LIMIT + dd GDT_BASE + +loadermsg db '2 loader in real.' ; 打印输出 长度为 17 + +;------------------------------------------------------------ +;INT 0x10 功能号:0x13 功能描述:打印字符串 +;------------------------------------------------------------ +;输入: +;AH 子功能号=13H +;BH = 页码 +;BL = 属性(若AL=00H或01H) +;CX=字符串长度 +;(DH、DL)=坐标(行、列) +;ES:BP=字符串地址 +;AL=显示输出方式 +; 0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变 +; 1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变 +; 2——字符串中含显示字符和显示属性。显示后,光标位置不变 +; 3——字符串中含显示字符和显示属性。显示后,光标位置改变 +;无返回值 +loader_start: + ; 调用10号中断 + mov sp, LOADER_BASE_ADDR + mov bp, loadermsg ; ES:BP = 字符串地址 + ; 字符串长度 + mov cx, 17 + ; 子功能号以及 显示状态 + mov ax, 0x1301 ; AH = 13, AL = 01h + mov bx, 0x001f ; 页号为0(BH = 0) 蓝底粉红字(BL = 1fh) + mov dx, 0x1800 ; 0x18 = 24 即最后一行 + int 0x10 + +;---------------------------------------- 准备进入保护模式 ------------------------------------------ + ;1 打开A20 + ;2 加载gdt + ;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al, 0x92 + or al, 0000_0010b ; 将 0x92 的第一位 置 1 + out 0x92, al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0000_0001 + mov cr0, eax + +; 构建选择子 +; SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 + ;jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响 + +; 初始化各个寄存器 +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + + mov byte [gs:160], 'P' + + jmp $ \ No newline at end of file diff --git a/c4/a/boot/mbr.S b/c4/a/boot/mbr.S new file mode 100755 index 0000000..d85b7b7 --- /dev/null +++ b/c4/a/boot/mbr.S @@ -0,0 +1,136 @@ +; +; +;LOADER_BASE_ADDR equ 0xA000 +;LOADER_START_SECTOR equ 0x2 +;------------------------------------------------------------ +# %include ΪnasmԴԤ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; +;0x06ŹܣϾȫУ +; ----------------------------------------------------------- +;INT 0x10 ܺ:0x06 :Ͼ +;------------------------------------------------------ +;룺 +;AH ܺ= 0x06 +;AL = Ͼ(Ϊ0,ʾȫ) +;BH = Ͼ +;(CL,CH) = Ͻǵ(X,Y)λ +;(DL,DH) = ½ǵ(X,Y)λ +;޷ֵ + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; Ͻ: (0, 0) + mov dx, 184fh ; ½: (80,25), + ; ΪVGAıģʽУһֻ80ַ,25С + ; ±0ʼ0x18=24,0x4f=79 + int 10h ; int 10h + + ; ɫɫǰɫɫַ"1 MBR" + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 ; Aʾɫ˸4ʾǰɫΪɫ + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax, LOADER_START_SECTOR ; ʼlbaַ + mov bx, LOADER_BASE_ADDR ; дĵַ + mov cx, 4 ; ȴ + + call rd_disk_m_16 ; ȡʼ + + jmp LOADER_BASE_ADDR + +;------------------------------------------------------------------------------- +;:ȡӲn +; Ϊ 16 λ¶Ӳ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax= LBA + ; ebx= дڴַ + ; ecx= + mov esi, eax ; eax + mov di, cx ; cx + +; дӲ +; 1 Ҫȡ + mov dx, 0x1f2 + mov al, cl ; out ָ ˿һֽڻֵALAX + out dx, al ;ȡ + + mov eax, esi ;ָ + +;2LBAַ0x1f3 ~ 0x1f6 + ;LBAַ7~0λд˿0x1f3 + mov dx, 0x1f3 + out dx, al + + ;LBAַ15~8λд˿0x1f4 + mov cl, 8 + shr eax, cl ;8λ + mov dx, 0x1f4 + out dx, al + + ;LBAַ23~16λд˿0x1f5 + shr eax, cl + mov dx, 0x1f5 + out dx, al + + ; д device Ĵ λ + shr eax, cl + and al, 0x0f ; 24~27λ + ; ҪdeviceĴλã7~4λΪ1110 + or al, 0xe0 + mov dx, 0x1f6 + out dx, al + +;30x1f7˿д0x20 + mov dx, 0x1f7 + mov al, 0x20 + out dx, al + +;4Ӳ״̬ +.not_ready: + ;ͬһ˿ڣдʱʾд֣ʱʾӲ״̬ + nop ;ӳ ٴӲ + in al, dx + and al,0x88 ;3λΪ1ʾӲ̿׼ݴ䣬7λΪ1ʾӲæ + cmp al, 0x08 + jnz .not_ready ;ѭȴ + +;50x1f0˿ڶ + mov ax, di ;di Ϊ֮ǰݵ + mov dx, 256 + mul dx ;һ512ֽڣÿζһ֣ + ; di*512/2Σdi*256 + mov cx, ax ; ˷д cx Ϊд׼ + ; ֪ 16 λ˷Ľ16λĴ + mov dx, 0x1f0 ; data Ĵ +.go_on_read: + in ax, dx ; д뵽 bx ָڴ棬 bx = 1~65535дݲܳ 64kb + mov [bx], ax + add bx, 2 ;ÿδ2ֽ + loop .go_on_read + ret ; ص 58 е call ָ + + + times 510-($-$$) db 0 + db 0x55,0xaa \ No newline at end of file diff --git a/c5/detect_memory/bochsrc b/c5/detect_memory/bochsrc new file mode 100755 index 0000000..6052b65 --- /dev/null +++ b/c5/detect_memory/bochsrc @@ -0,0 +1,17 @@ +#Bochs运行中使用的内存,设置为32Mmegs: 32#设置真实机器的BIOS和VGA BIOS +#修改成你们对应的地址 +romimage: file=/home/ahu/install/bochs/share/bochs/BIOS-bochs-latest +vgaromimage: file=/home/ahu/install/bochs/share/bochs/VGABIOS-lgpl-latest +#设置Bochs所使用的磁盘#设置启动盘符 +boot: disk +#设置日志文件的输出 +log: bochs.out +#开启或关闭某些功能,修改成你们对应的地址 +mouse: enabled=0 +#keyboard_mapping: enabled=1, map=/home/ahu/install/bochs/share/bochs/keymaps/x11-pc-us.map +keyboard: keymap=/home/ahu/install/bochs/share/bochs/keymaps/x11-pc-us.map +#硬盘设置 +ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +ata0-master: type=disk, path="hd60M.img", mode=flat, cylinders=121, heads=16, spt=63 +#增加bochs对gdb的支持,我们在这里不使用,所以注释掉了 +#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 \ No newline at end of file diff --git a/c5/detect_memory/build.sh b/c5/detect_memory/build.sh new file mode 100755 index 0000000..daadc5f --- /dev/null +++ b/c5/detect_memory/build.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +echo "Creating disk.img..." +bximage -mode=create -hd=10M -q disk.img + +echo "Compiling..." +nasm -I include/ -o mbr.bin mbr.S +nasm -I include/ -o loader.bin loader.S + +echo "Writing mbr and loader to disk..." +dd if=mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + +echo "Now start bochs and have fun!" + +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c5/detect_memory/include/boot.inc b/c5/detect_memory/include/boot.inc new file mode 100755 index 0000000..2385009 --- /dev/null +++ b/c5/detect_memory/include/boot.inc @@ -0,0 +1,50 @@ +;------------- loader和kernel ---------- +; 即为 宏名 equ 值 +LOADER_BASE_ADDR equ 0x900 +LOADER_START_SECTOR equ 0x2 + +;-------------- gdt描述符属性 ------------- +; 段描述符的23位 表示粒度为 4K +DESC_G_4K equ 1_00000000000000000000000b ; 23 +DESC_D_32 equ 1_0000000000000000000000b ; 22 位 1表示在32位下 +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +; 第二部分段界限值,由于采用了32位平坦模型,所以段界限为(4GB / 4KB) - 1 = 0xFFFFF,故为全1 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b ; 代码段的 16-19的limit 总的段限界为fffff +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ; 数据段的 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b ; 限界为 bffff +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + + +; 代码段描述符的高32位表示,其中(0x00 << 24表示最高8位的段基址值 +; 由于我们采用的是平坦模型,故基址为零),后面唯一可变的就是段界限值 +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + \ + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +; 由于显存区为 0b8000, 因此段基址高四位为 b +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + + +; 选择子属性 +; 请求特权级 +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +; 表示是 GDT 还是 LDT +TI_GDT equ 000b +TI_LDT equ 100b \ No newline at end of file diff --git a/c5/detect_memory/loader.S b/c5/detect_memory/loader.S new file mode 100755 index 0000000..a814560 --- /dev/null +++ b/c5/detect_memory/loader.S @@ -0,0 +1,166 @@ +%include "boot.inc" +section loader vstart=LOADER_BASE_ADDR +LOADER_STACK_TOP equ LOADER_BASE_ADDR + +; 加载GDT表 +; 一个dd为4字节,段描述符为8字节,上面为低4字节 +; 第一行的 0描述符不可用 因此直接置为 全 0 +GDT_BASE: dd 0x00000000 + dd 0x00000000 + +; GDT 中 先安放三个有用描述符 +; 代码段、数据段和栈段、显存段 +CODE_DESC: dd 0x0000FFFF ; 低32位表示 2字节base + 2字节段限界 + dd DESC_CODE_HIGH4 + +; 栈段和数据段共用,向上扩展 type的e为0 +DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + +; 显卡段,非平坦,从 0xb8000开始 +VIDEO_DESC: dd 0x80000007 ; 段界限为 (bffff - b8000 ) / 4K = 7 + dd DESC_VIDEO_HIGH4 + +GDT_SIZE equ $ - GDT_BASE ;$ 表示本行标号, 通过地址差获得size +GDT_LIMIT equ GDT_SIZE - 1 + +; 填充空白描述符 60 个 +times 120 dd 0 +; 构建选择子 +SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 +SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0 +SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 + + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + +; GDTR 寄存器 +gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + + ards_buf times 244 db 0 ; 一个 ards 为 20字节,因此可以存放12个 + ards_nr dw 0 ; 记录 ards 结构体的数量 + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + +loader_start: + xor ebx, ebx ; xor 表示清 0 + mov edx, 0x534d4150 + ; es 在mbr中赋值 + mov di, ards_buf ;缓冲区 + ; 由于每次 eax ecx ebx 都会更新 +.e820_mem_get_loop: + mov eax, 0x0000e820 + mov ecx, 20 ; ards 大小为 20字节 + int 0x15 + jc .e801_failed_so_try801 ; 通过 cf 位来进行判断 + add di, cx ;使 di 向后20字节 + inc word [ards_nr] + cmp ebx, 0 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] + mov ebx, ards_buf + xor edx, edx + +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ; 取得 base + add eax, [ebx+8] ; len + add ebx, 20 ; 取得下一个 + cmp edx, eax ; edx 存放最大值 + jge .next_ards ; edx > eax 进入循环 ,大于就先赋值后 再循环 + mov edx, eax + +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +.e801_failed_so_try801: + mov ax, 0xe801 + int 0x15 + jc .e801_failed_so_try88 + + ; ax * 1024 单位为 1Kb + ; 计算 15 Mb 以下的 + mov cx, 0x400 + mul cx + ; 积 的 高16位 在dx中, 低16位在ax中 + shl edx, 16 ; 将 edx 低 16 位 置为 0 + and eax, 0x0000FFFF ; 将 eax 高 16 置为0 + or edx, eax + add edx, 0x100000 ; 加上 1Mb + mov esi, edx ; 存放在 esi 中 + + ; bx * 1024 *64 单位为64kb + ; 16 Mb 以上的 + xor eax, eax + mov ax, bx ; bx 存放 + mov ecx, 0x10000 + mul ecx + add esi, eax ; 大小最大为 4G 因此32位的 eax 便足够存放乘积了 + mov edx, esi + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + mov ah, 0x88 + int 0x15 + jc .error_hlt ; 最终出错挂起 + and eax, 0x0000FFFF + + mov cx, 0x400 + mul cx + shl edx, 16 + or edx, eax + add edx, 0x100000 ; 加上 1Mb + +.mem_get_ok: + mov [total_mem_bytes], edx ;全部都由 edx 存储 +;---------------------------------------- 准备进入保护模式 ------------------------------------------ + ;1 打开A20 + ;2 加载gdt + ;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al, 0x92 + or al, 0000_0010b ; 将 0x92 的第一位 置 1 + out 0x92, al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0000_0001 + mov cr0, eax + +; 构建选择子 +; SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 + ;jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响 + + +.error_hlt: ;出错则挂起 + hlt + +; 初始化各个寄存器 +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + + mov byte [gs:160], 'P' + + jmp $ \ No newline at end of file diff --git a/c5/detect_memory/mbr.S b/c5/detect_memory/mbr.S new file mode 100755 index 0000000..1a239bd --- /dev/null +++ b/c5/detect_memory/mbr.S @@ -0,0 +1,136 @@ +; +; +;LOADER_BASE_ADDR equ 0xA000 +;LOADER_START_SECTOR equ 0x2 +;------------------------------------------------------------ +# %include ΪnasmԴԤ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; +;0x06ŹܣϾȫУ +; ----------------------------------------------------------- +;INT 0x10 ܺ:0x06 :Ͼ +;------------------------------------------------------ +;룺 +;AH ܺ= 0x06 +;AL = Ͼ(Ϊ0,ʾȫ) +;BH = Ͼ +;(CL,CH) = Ͻǵ(X,Y)λ +;(DL,DH) = ½ǵ(X,Y)λ +;޷ֵ + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; Ͻ: (0, 0) + mov dx, 184fh ; ½: (80,25), + ; ΪVGAıģʽУһֻ80ַ,25С + ; ±0ʼ0x18=24,0x4f=79 + int 10h ; int 10h + + ; ɫɫǰɫɫַ"1 MBR" + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 ; Aʾɫ˸4ʾǰɫΪɫ + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax, LOADER_START_SECTOR ; ʼlbaַ + mov bx, LOADER_BASE_ADDR ; дĵַ + mov cx, 4 ; ȴ + + call rd_disk_m_16 ; ȡʼ + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;:ȡӲn +; Ϊ 16 λ¶Ӳ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax= LBA + ; ebx= дڴַ + ; ecx= + mov esi, eax ; eax + mov di, cx ; cx + +; дӲ +; 1 Ҫȡ + mov dx, 0x1f2 + mov al, cl ; out ָ ˿һֽڻֵALAX + out dx, al ;ȡ + + mov eax, esi ;ָ + +;2LBAַ0x1f3 ~ 0x1f6 + ;LBAַ7~0λд˿0x1f3 + mov dx, 0x1f3 + out dx, al + + ;LBAַ15~8λд˿0x1f4 + mov cl, 8 + shr eax, cl ;8λ + mov dx, 0x1f4 + out dx, al + + ;LBAַ23~16λд˿0x1f5 + shr eax, cl + mov dx, 0x1f5 + out dx, al + + ; д device Ĵ λ + shr eax, cl + and al, 0x0f ; 24~27λ + ; ҪdeviceĴλã7~4λΪ1110 + or al, 0xe0 + mov dx, 0x1f6 + out dx, al + +;30x1f7˿д0x20 + mov dx, 0x1f7 + mov al, 0x20 + out dx, al + +;4Ӳ״̬ +.not_ready: + ;ͬһ˿ڣдʱʾд֣ʱʾӲ״̬ + nop ;ӳ ٴӲ + in al, dx + and al,0x88 ;3λΪ1ʾӲ̿׼ݴ䣬7λΪ1ʾӲæ + cmp al, 0x08 + jnz .not_ready ;ѭȴ + +;50x1f0˿ڶ + mov ax, di ;di Ϊ֮ǰݵ + mov dx, 256 + mul dx ;һ512ֽڣÿζһ֣ + ; di*512/2Σdi*256 + mov cx, ax ; ˷д cx Ϊд׼ + ; ֪ 16 λ˷Ľ16λĴ + mov dx, 0x1f0 ; data Ĵ +.go_on_read: + in ax, dx ; д뵽 bx ָڴ棬 bx = 1~65535дݲܳ 64kb + mov [bx], ax + add bx, 2 ;ÿδ2ֽ + loop .go_on_read + ret ; ص 58 е call ָ + + + times 510-($-$$) db 0 + db 0x55,0xaa \ No newline at end of file diff --git a/c5/load-kernel/build.sh b/c5/load-kernel/build.sh new file mode 100755 index 0000000..c1fb255 --- /dev/null +++ b/c5/load-kernel/build.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +shopt -s expand_aliases + +echo "Creating disk.img..." +bximage -mode=create -hd=10M -q disk.img + +echo "Compiling..." +gcc -m32 -c -o kernel/main.o kernel/main.c +ld -m elf_i386 kernel/main.o -Ttext 0xc0001500 -e main -o kernel/kernel.bin +nasm -I include/ -o mbr.bin mbr.S +nasm -I include/ -o loader.bin loader.S + +echo "Writing mbr, loader and kernel to disk..." +dd if=mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=kernel/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + +echo "Now start bochs and have fun!" +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk diff --git a/c5/load-kernel/include/boot.inc b/c5/load-kernel/include/boot.inc new file mode 100755 index 0000000..4a40683 --- /dev/null +++ b/c5/load-kernel/include/boot.inc @@ -0,0 +1,66 @@ +;---------------------------loader和kernel------------- +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ------------- +; 段描述符的23位 表示粒度为 4K +DESC_G_4K equ 1_00000000000000000000000b ; 23 +DESC_D_32 equ 1_0000000000000000000000b ; 22 位 1表示在32位下 +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +; 第二部分段界限值,由于采用了32位平坦模型,所以段界限为(4GB / 4KB) - 1 = 0xFFFFF,故为全1 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b ; 代码段的 16-19的limit 总的段限界为fffff +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ; 数据段的 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b ; 限界为 bffff +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + + +; 代码段描述符的高32位表示,其中(0x00 << 24表示最高8位的段基址值 +; 由于我们采用的是平坦模型,故基址为零),后面唯一可变的就是段界限值 +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + \ + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +; 由于显存区为 0b8000, 因此段基址高四位为 b +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 \ No newline at end of file diff --git a/c5/load-kernel/kernel/main.c b/c5/load-kernel/kernel/main.c new file mode 100755 index 0000000..0d9e0e1 --- /dev/null +++ b/c5/load-kernel/kernel/main.c @@ -0,0 +1,4 @@ +int main(void) { + while (1); + return 0; +} \ No newline at end of file diff --git a/c5/load-kernel/loader.S b/c5/load-kernel/loader.S new file mode 100755 index 0000000..b604bb3 --- /dev/null +++ b/c5/load-kernel/loader.S @@ -0,0 +1,320 @@ +%include "boot.inc" +section loader vstart=LOADER_BASE_ADDR +LOADER_STACK_TOP equ LOADER_BASE_ADDR + +; 加载GDT表 +; 一个dd为4字节,段描述符为8字节,上面为低4字节 +; 第一行的 0描述符不可用 因此直接置为 全 0 +GDT_BASE: dd 0x00000000 + dd 0x00000000 + +; GDT 中 先安放三个有用描述符 +; 代码段、数据段和栈段、显存段 +CODE_DESC: dd 0x0000FFFF ; 低32位表示 2字节base + 2字节段限界 + dd DESC_CODE_HIGH4 + +; 栈段和数据段共用,向上扩展 type的e为0 +DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + +; 显卡段,非平坦,从 0xb8000开始 +VIDEO_DESC: dd 0x80000007 ; 段界限为 (bffff - b8000 ) / 4K = 7 + dd DESC_VIDEO_HIGH4 + +GDT_SIZE equ $ - GDT_BASE ;$ 表示本行标号, 通过地址差获得size +GDT_LIMIT equ GDT_SIZE - 1 + +; 填充空白描述符 60 个 +times 120 dd 0 +; 构建选择子 +SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 +SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0 +SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 + + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + +; GDTR 寄存器 +gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + + ards_buf times 244 db 0 ; 一个 ards 为 20字节,因此可以存放12个 + ards_nr dw 0 ; 记录 ards 结构体的数量 + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + +loader_start: + xor ebx, ebx ; xor 表示清 0 + mov edx, 0x534d4150 + ; es 在mbr中赋值 + mov di, ards_buf ;缓冲区 + ; 由于每次 eax ecx ebx 都会更新 +.e820_mem_get_loop: + mov eax, 0x0000e820 + mov ecx, 20 ; ards 大小为 20字节 + int 0x15 + jc .e801_failed_so_try801 ; 通过 cf 位来进行判断 + add di, cx ;使 di 向后20字节 + inc word [ards_nr] + cmp ebx, 0 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] + mov ebx, ards_buf + xor edx, edx + +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ; 取得 base + add eax, [ebx+8] ; len + add ebx, 20 ; 取得下一个 + cmp edx, eax ; edx 存放最大值 + jge .next_ards ; edx > eax 进入循环 ,大于就先赋值后 再循环 + mov edx, eax + +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +.e801_failed_so_try801: + mov ax, 0xe801 + int 0x15 + jc .e801_failed_so_try88 + + ; ax * 1024 单位为 1Kb + ; 计算 15 Mb 以下的 + mov cx, 0x400 + mul cx + ; 积 的 高16位 在dx中, 低16位在ax中 + shl edx, 16 ; 将 edx 低 16 位 置为 0 + and eax, 0x0000FFFF ; 将 eax 高 16 置为0 + or edx, eax + add edx, 0x100000 ; 加上 1Mb + mov esi, edx ; 存放在 esi 中 + + ; bx * 1024 *64 单位为64kb + ; 16 Mb 以上的 + xor eax, eax + mov ax, bx ; bx 存放 + mov ecx, 0x10000 + mul ecx + add esi, eax ; 大小最大为 4G 因此32位的 eax 便足够存放乘积了 + mov edx, esi + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + mov ah, 0x88 + int 0x15 + jc .error_hlt ; 最终出错挂起 + and eax, 0x0000FFFF + + mov cx, 0x400 + mul cx + shl edx, 16 + or edx, eax + add edx, 0x100000 ; 加上 1Mb + +.mem_get_ok: + mov [total_mem_bytes], edx ;全部都由 edx 存储 +;---------------------------------------- 准备进入保护模式 ------------------------------------------ + ;1 打开A20 + ;2 加载gdt + ;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al, 0x92 + or al, 0000_0010b ; 将 0x92 的第一位 置 1 + out 0x92, al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0000_0001 + mov cr0, eax + +; 构建选择子 +; SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 + ;jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响 + + +.error_hlt: ;出错则挂起 + hlt + +; 初始化各个寄存器 +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin 所在扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后, 写入到 ebx 指定的地址 + mov ecx, 200 ;读取扇区数 + + call rd_disk_m_32 + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + mov ebx, [gdt_ptr + 2] ; 先得到 gdt 的地址 + or dword [ebx + 0x18 + 0x4], 0xc0000000 ; 修改显存段地址 + + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 改变栈地址 + + ; 开启分页机制 将地址赋予 cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开 cr0 的 pg 位 + mov eax, cr0 + or eax, 0x80000000 ; pg 位于第 31 位 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +enter_kernel: + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + ; 首先得到每个段的大小,之后便加起来循环遍历 各个段 + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header 大小 + ; 现加载第一个段头 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] + ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + add ebx, KERNEL_BIN_BASE_ADDR ;加上初始地址 + ; 获取段数量 + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; e_phnum + +; 现对每个段进行循环判断 首先判断是否为 PT_NULL,即是否为空 +.each_segment: + cmp byte [ebx + 0], PT_NULL + je .PTNULL + +;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; file_size + mov eax, [ebx + 4] ; 赋值 offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax + push dword [ebx + 8] + call mem_cpy + add esp, 12 ; 清楚栈中压入的三个参数 + +.PTNULL: + ; 直接读取下一个段 + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld ; 清除方向标志,让数据源地址和目的地址逐渐增大 + push ebp + mov ebp, esp + push ecx + mov edi, [ebp + 8] + mov esi, [ebp + 12] + mov ecx, [ebp + 16] + + ; 恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 + +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 第一个页表紧挨着页目录表 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 +; 第 0 个 和 第 768 个页目录表项为第一个页表地址 +; 第 1023 个页目录表项值为 页目录地址 + or eax, PG_US_U | PG_RW_W | PG_P ; 属性赋值 + ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 768*4=0xc00,一个页表项占用4个字节 + + ; 使最后一个目录项指向页目录表自己的地址 + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax + +;下面创建页表项(PTE),即指第一个页 + mov ecx, 256 ; 只用到 1Mb 空间,因此为其分配物理页 1MB/4KB = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: + ; ebx 为第一个页表值 + mov [ebx+esi*4], edx + add edx, 4096 ; 在低端1MB内存中,虚拟地址 == 物理地址,因此连续递增 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时 eax 为第二个页表项 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性US,RW和P位都为1 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 + +.create_kernel_pde: + mov [ebx+esi*4], eax ; 先暂时写满内核pde + inc esi + add eax, 0x1000 + loop .create_kernel_pde + + ret + + diff --git a/c5/load-kernel/mbr.S b/c5/load-kernel/mbr.S new file mode 100755 index 0000000..080d26b --- /dev/null +++ b/c5/load-kernel/mbr.S @@ -0,0 +1,107 @@ +; 主引导程序 +;----------------------------------------------- +%include "boot.inc" +SECTION MBR vstart=0x7c00 + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov sp, 0x7c00 + mov ax, 0xb800 + mov gs, ax + +; 清屏 +;--------------------------------------------------- + mov ax, 0600h + mov bx, 0700h + mov cx, 0 + mov dx, 184fh + int 10h + + ; 显示"MBR" + mov byte [gs:0x00], '1' + mov byte [gs:0x01], 0xA4 + + mov byte [gs:0x02], ' ' + mov byte [gs:0x03], 0xA4 + + mov byte [gs:0x04], 'M' + mov byte [gs:0x05], 0xA4 + + mov byte [gs:0x06], 'B' + mov byte [gs:0x07], 0xA4 + + mov byte [gs:0x08], 'A' + mov byte [gs:0x09], 0xA4 + + mov eax, LOADER_START_SECTOR + mov bx, LOADER_BASE_ADDR + + ; 读取4个扇区 + mov cx, 4 + call rd_disk_m_16 + + ; 直接跳到loader的起始代码执行 + jmp LOADER_BASE_ADDR + 0x300 + +;----------------------------------------------------------- +; 读取磁盘的n个扇区,用于加载loader +; eax保存从硬盘读取到的数据的保存地址,ebx为起始扇区,cx为读取的扇区数 +rd_disk_m_16: +;----------------------------------------------------------- + + mov esi, eax + mov di, cx + + mov dx, 0x1f2 + mov al, cl + out dx, al + + mov eax, esi + + mov dx, 0x1f3 + out dx, al + + mov cl, 8 + shr eax, cl + mov dx, 0x1f4 + out dx, al + + shr eax, cl + mov dx, 0x1f5 + out dx, al + + shr eax, cl + and al, 0x0f + or al, 0xe0 + mov dx, 0x1f6 + out dx, al + + mov dx, 0x1f7 + mov al, 0x20 + out dx, al + +.not_ready: + nop + in al, dx + and al, 0x88 + cmp al, 0x08 + jnz .not_ready + + mov ax, di + mov dx, 256 + mul dx + mov cx, ax + mov dx, 0x1f0 + +.go_on_read: + in ax, dx + mov [bx], ax + add bx, 2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55, 0xaa diff --git a/c5/page-memory/build.sh b/c5/page-memory/build.sh new file mode 100755 index 0000000..fcc9c75 --- /dev/null +++ b/c5/page-memory/build.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +echo "Creating disk.img..." +bximage -mode=create -hd=10M -q disk.img + +echo "Compiling..." +nasm -I include/ -o mbr.bin mbr.S +nasm -I include/ -o loader.bin loader.S + +echo "Writing mbr and loader to disk..." +dd if=mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + +#echo "Now start bochs and have fun!" +#bochs -f bochsrc diff --git a/c5/page-memory/clean.sh b/c5/page-memory/clean.sh new file mode 100755 index 0000000..ecfcb37 --- /dev/null +++ b/c5/page-memory/clean.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rm -f disk.img *.bin diff --git a/c5/page-memory/disk.img b/c5/page-memory/disk.img new file mode 100644 index 0000000000000000000000000000000000000000..29a4ff9de7c081993661f17c0769852c44c27e58 GIT binary patch literal 512 zcmeA<(RZWoK;MPF2YVQ5b};Pdd$EIoZ8rn^P6mcu{t{;eQjf7QFc<pSBHg?AI)sC3wHl>X@a iv5SH6`=ic-yFM_!?PPC0%=GBTVW9D&fPxSRT?GJtc2#Hq literal 0 HcmV?d00001 diff --git a/c5/page-memory/include/boot.inc b/c5/page-memory/include/boot.inc new file mode 100755 index 0000000..e22090d --- /dev/null +++ b/c5/page-memory/include/boot.inc @@ -0,0 +1,62 @@ +;------------- loader和kernel ---------- +; 即为 宏名 equ 值 +LOADER_BASE_ADDR equ 0x900 +LOADER_START_SECTOR equ 0x2 +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_IMAGE_BASE_ADDR equ 0x1500 +KERNEL_START_SECTOR equ 0x9 + +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ------------- +; 段描述符的23位 表示粒度为 4K +DESC_G_4K equ 1_00000000000000000000000b ; 23 +DESC_D_32 equ 1_0000000000000000000000b ; 22 位 1表示在32位下 +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +; 第二部分段界限值,由于采用了32位平坦模型,所以段界限为(4GB / 4KB) - 1 = 0xFFFFF,故为全1 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b ; 代码段的 16-19的limit 总的段限界为fffff +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ; 数据段的 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b ; 限界为 bffff +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + + +; 代码段描述符的高32位表示,其中(0x00 << 24表示最高8位的段基址值 +; 由于我们采用的是平坦模型,故基址为零),后面唯一可变的就是段界限值 +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + \ + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +; 由于显存区为 0b8000, 因此段基址高四位为 b +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \ + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \ + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + + +; 选择子属性 +; 请求特权级 +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +; 表示是 GDT 还是 LDT +TI_GDT equ 000b +TI_LDT equ 100b + +;---------------- 页表相关属性 -------------- +PG_P equ 1b ; 是否在磁盘上 +PG_RW_R equ 00b ; 只读 +PG_RW_W equ 10b ; 只写 +PG_US_S equ 000b ; 第三位为 0 表示为 特权级 3 +PG_US_U equ 100b \ No newline at end of file diff --git a/c5/page-memory/loader.S b/c5/page-memory/loader.S new file mode 100755 index 0000000..114eff4 --- /dev/null +++ b/c5/page-memory/loader.S @@ -0,0 +1,251 @@ +%include "boot.inc" +section loader vstart=LOADER_BASE_ADDR +LOADER_STACK_TOP equ LOADER_BASE_ADDR + +; 加载GDT表 +; 一个dd为4字节,段描述符为8字节,上面为低4字节 +; 第一行的 0描述符不可用 因此直接置为 全 0 +GDT_BASE: dd 0x00000000 + dd 0x00000000 + +; GDT 中 先安放三个有用描述符 +; 代码段、数据段和栈段、显存段 +CODE_DESC: dd 0x0000FFFF ; 低32位表示 2字节base + 2字节段限界 + dd DESC_CODE_HIGH4 + +; 栈段和数据段共用,向上扩展 type的e为0 +DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + +; 显卡段,非平坦,从 0xb8000开始 +VIDEO_DESC: dd 0x80000007 ; 段界限为 (bffff - b8000 ) / 4K = 7 + dd DESC_VIDEO_HIGH4 + +GDT_SIZE equ $ - GDT_BASE ;$ 表示本行标号, 通过地址差获得size +GDT_LIMIT equ GDT_SIZE - 1 + +; 填充空白描述符 60 个 +times 120 dd 0 +; 构建选择子 +SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 +SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0 +SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 + + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + +; GDTR 寄存器 +gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + + ards_buf times 244 db 0 ; 一个 ards 为 20字节,因此可以存放12个 + ards_nr dw 0 ; 记录 ards 结构体的数量 + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + +loader_start: + xor ebx, ebx ; xor 表示清 0 + mov edx, 0x534d4150 + ; es 在mbr中赋值 + mov di, ards_buf ;缓冲区 + ; 由于每次 eax ecx ebx 都会更新 +.e820_mem_get_loop: + mov eax, 0x0000e820 + mov ecx, 20 ; ards 大小为 20字节 + int 0x15 + jc .e801_failed_so_try801 ; 通过 cf 位来进行判断 + add di, cx ;使 di 向后20字节 + inc word [ards_nr] + cmp ebx, 0 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] + mov ebx, ards_buf + xor edx, edx + +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ; 取得 base + add eax, [ebx+8] ; len + add ebx, 20 ; 取得下一个 + cmp edx, eax ; edx 存放最大值 + jge .next_ards ; edx > eax 进入循环 ,大于就先赋值后 再循环 + mov edx, eax + +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +.e801_failed_so_try801: + mov ax, 0xe801 + int 0x15 + jc .e801_failed_so_try88 + + ; ax * 1024 单位为 1Kb + ; 计算 15 Mb 以下的 + mov cx, 0x400 + mul cx + ; 积 的 高16位 在dx中, 低16位在ax中 + shl edx, 16 ; 将 edx 低 16 位 置为 0 + and eax, 0x0000FFFF ; 将 eax 高 16 置为0 + or edx, eax + add edx, 0x100000 ; 加上 1Mb + mov esi, edx ; 存放在 esi 中 + + ; bx * 1024 *64 单位为64kb + ; 16 Mb 以上的 + xor eax, eax + mov ax, bx ; bx 存放 + mov ecx, 0x10000 + mul ecx + add esi, eax ; 大小最大为 4G 因此32位的 eax 便足够存放乘积了 + mov edx, esi + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + mov ah, 0x88 + int 0x15 + jc .error_hlt ; 最终出错挂起 + and eax, 0x0000FFFF + + mov cx, 0x400 + mul cx + shl edx, 16 + or edx, eax + add edx, 0x100000 ; 加上 1Mb + +.mem_get_ok: + mov [total_mem_bytes], edx ;全部都由 edx 存储 +;---------------------------------------- 准备进入保护模式 ------------------------------------------ + ;1 打开A20 + ;2 加载gdt + ;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al, 0x92 + or al, 0000_0010b ; 将 0x92 的第一位 置 1 + out 0x92, al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0000_0001 + mov cr0, eax + +; 构建选择子 +; SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 + ;jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响 + + +.error_hlt: ;出错则挂起 + hlt + +; 初始化各个寄存器 +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + mov ebx, [gdt_ptr + 2] ; 先得到 gdt 的地址 + or dword [ebx + 0x18 + 0x4], 0xc0000000 ; 修改显存段地址 + + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 改变栈地址 + + ; 开启分页机制 将地址赋予 cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开 cr0 的 pg 位 + mov eax, cr0 + or eax, 0x80000000 ; pg 位于第 31 位 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + + mov byte [gs:160], 'V' + + jmp $ + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 + +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 第一个页表紧挨着页目录表 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 +; 第 0 个 和 第 768 个页目录表项为第一个页表地址 +; 第 1023 个页目录表项值为 页目录地址 + or eax, PG_US_U | PG_RW_W | PG_P ; 属性赋值 + ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 768*4=0xc00,一个页表项占用4个字节 + + ; 使最后一个目录项指向页目录表自己的地址 + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax + +;下面创建页表项(PTE),即指第一个页 + mov ecx, 256 ; 只用到 1Mb 空间,因此为其分配物理页 1MB/4KB = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: + ; ebx 为第一个页表值 + mov [ebx+esi*4], edx + add edx, 4096 ; 在低端1MB内存中,虚拟地址 == 物理地址,因此连续递增 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时 eax 为第二个页表项 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性US,RW和P位都为1 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 + +.create_kernel_pde: + mov [ebx+esi*4], eax ; 先暂时写满内核pde + inc esi + add eax, 0x1000 + loop .create_kernel_pde + + ret + + diff --git a/c5/page-memory/loader.bin b/c5/page-memory/loader.bin new file mode 100644 index 0000000000000000000000000000000000000000..3a9f198569c2d0b32b316c1726979810c9c9a349 GIT binary patch literal 1189 zcmeH{JxfAS7{~u7-@VqogGxi82#pQ4MNQvKKTv6Fb#wc|uaFV#0%QgmxC!kqiRREfP$=_>gHCpay|Q27_4pkn zvkpo+*1VLd1!=i@=pen?e(voWqANiY&W}gBY-_Y2-xK+&l&%6ANWZ7JrMJig1c*hV zPWo#va(2wz%1Hi0|Kh?>I{CoVQ(hHer=mtJ$~1%;v*_hI0C>?SLqo2WvvRlR@;1qs zMXuXj=T=nVa`L{j^KPEg8*`kp(k>a;-j$pUxjQx1;=pSBHg?AI)sC3wHl>X@a iv5SH6`=ic-yFM_!?PPC0%=GBTVW9D&fPxSRT?GJtc2#Hq literal 0 HcmV?d00001 diff --git a/c6/boot/include/boot.inc b/c6/boot/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c6/boot/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c6/boot/loader.S b/c6/boot/loader.S new file mode 100755 index 0000000..a9312c9 --- /dev/null +++ b/c6/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c6/boot/mbr.S b/c6/boot/mbr.S new file mode 100755 index 0000000..49fc2a5 --- /dev/null +++ b/c6/boot/mbr.S @@ -0,0 +1,126 @@ +; +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; +;0x06ŹܣϾȫУ +; ----------------------------------------------------------- +;INT 0x10 ܺ:0x06 :Ͼ +;------------------------------------------------------ +;룺 +;AH ܺ= 0x06 +;AL = Ͼ(Ϊ0,ʾȫ) +;BH = Ͼ +;(CL,CH) = Ͻǵ(X,Y)λ +;(DL,DH) = ½ǵ(X,Y)λ +;޷ֵ + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; Ͻ: (0, 0) + mov dx, 184fh ; ½: (80,25), + ; ΪVGAıģʽУһֻ80ַ,25С + ; ±0ʼ0x18=24,0x4f=79 + int 10h ; int 10h + + ; ַ:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;Aʾɫ˸4ʾǰɫΪɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ʼlbaַ + mov bx,LOADER_BASE_ADDR ; дĵַ + mov cx,4 ; + call rd_disk_m_16 ; ¶ȡʼ֣һ + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;:ȡӲn +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA + ; ebx=дڴַ + ; ecx= + mov esi,eax ;eax + mov di,cx ;cx +;дӲ: +;1Ҫȡ + mov dx,0x1f2 + mov al,cl + out dx,al ;ȡ + + mov eax,esi ;ָax + +;2LBAַ0x1f3 ~ 0x1f6 + + ;LBAַ7~0λд˿0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBAַ15~8λд˿0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBAַ23~16λд˿0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba24~27λ + or al,0xe0 ; 74λΪ1110,ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;30x1f7˿д0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;4Ӳ״̬ + .not_ready: + ;ͬһ˿ڣдʱʾд֣ʱʾӲ״̬ + nop + in al,dx + and al,0x88 ;4λΪ1ʾӲ̿׼ݴ䣬7λΪ1ʾӲæ + cmp al,0x08 + jnz .not_ready ;δ׼ãȡ + +;50x1f0˿ڶ + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪȡһ512ֽڣÿζһ֣ + ; di*512/2Σdi*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c6/build.sh b/c6/build.sh new file mode 100755 index 0000000..9012bcf --- /dev/null +++ b/c6/build.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +echo "Creating disk.img..." +# bximage -mode=create -hd=10M -q disk.img + +echo "Compiling..." +nasm -I include/ -o mbr.bin mbr.S +nasm -I include/ -o loader.bin loader.S +# 编译 print.S +nasm -f elf -o lib/kernel/print.o lib/kernel/print.S +# 编译 main.c +gcc -I lib/kernel/ -m32 -c -o kernel/main.o kernel/main.c +#链接 +ld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel/kernel.bin kernel/main.o lib/kernel/print.o + +echo "Writing mbr, loader and kernel to disk..." +dd if=mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=kernel/kernel.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +echo "Now start bochs and have fun!" + +cd /home/husharp/bochs_hu/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c6/clean.sh b/c6/clean.sh new file mode 100755 index 0000000..5131bd2 --- /dev/null +++ b/c6/clean.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rm -f *.bin kernel/*.bin kernel/*.o lib/kernel/*.o diff --git a/c6/include/boot.inc b/c6/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c6/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c6/kernel/kernel.bin b/c6/kernel/kernel.bin new file mode 100755 index 0000000000000000000000000000000000000000..1f5e6856a5762b8e835467e91b62cb642327c522 GIT binary patch literal 5692 zcmeI0U1(fI6o6-c+HBIKiJ{ig3a%1pqIA3YX>3)HWV?yM(EPTbRc`L?&LnI0C)_(L zi4AniBJ4I|qfdQPgggn-;)`h@jY&27)@p@5NTAW+>c-kof4bE+o^$8k-J1o$Cm-aV zFn8uV=ggeB=ibbnozwpA4x7zJ46zdj(dc2a&;rWqyM(2MxWQ6S%85(ZQC&k8u(-L%?vBSQ)S~U}fOH$-wcE^g25W-qd9-_haE%iPR|RT;aM%MEF|bahe1 zeC*7hSIZ_lSmReLbA!$I686XMtl?{xTIv<1r~aBX-XYm6uhhK}-YIW*f z`RQ$`MQ?6<>Zgj-Vq1FGvpt_?oteee)y~wWi&vjLhn}6uyqEvdle1s+_GNEA*xZCX zbqW)BP3GjKF1H+Yk5VN@Bk&OQPZ_bj(NAoXF5A9Jr!x(YW)FmZ6w0snVF;*6+fLV; z_Vz06dxmU@l>sXQRtBsLSQ)S~U}fNcn1N28?-_S}=dt4r?iSBs&mnh{xABm-wW--% z-%msC7i2E<2bzyG{NwGznE~4L69j_$X~A}^$a;Zc1Brs?10W9K#Ht_;I7xtQSwWm+ zV-vTBh9K`VK&v-4pD5w|z__myva#8U4Lu%#cz(e1`g{pQ>j(bO#6jR6OgsU6-^6bL zKce3c_Wz)S=YjW|>?^=t6W;}HGI15$@G%qb1%B1U9^eTR`+?sz@k!wKO#B+~jETpA zFPQio@I@2P0DorUkAas>`~~nSoonEJuL8UDeAYldzXc|cAKX9Jg8e4&w4N{Y|FOjX z4)~e--v;|fCVvu@!!b{gM8biCVgrA0ObN%hXMhZLNu5UrNq=v*mkjv3{l3ATe(6Nl zQGX8^OK=ICq(Fi(s%WTk#WNg_#|i)nDgL@{4UWi)C#*^VIXJ40$w8{QX<8gB&PPfd zBjFg87^R`2BXkeSiV~KGsiaVzP(&qV#0@EE8l3y(|Qf|eyBks@K@s2m)TVu`5XLL@1ms*=iOg=>#iGr*%D5)H{*)(|I7 zh)11>aybAsS42Hxv^X8-#G}Nu9v(U(F-4A2;t?E-N24^x;l=uwf@=uAw%WNQzI*V4 zZv_>)7-x}>1EU&OFPm*SdA^!!uMvj8(1~6zHd|g7`lKfwr!1 zq6Z!opr2PDae?0fy^R&xj3QQ8VLS|eL!iLl)sI2JxS)cwTRkS!DCcVl$>RXJ7|n-( T^%YvPyaPX++YKkE4Zptuf?NWe literal 0 HcmV?d00001 diff --git a/c6/kernel/main.c b/c6/kernel/main.c new file mode 100755 index 0000000..0c6607d --- /dev/null +++ b/c6/kernel/main.c @@ -0,0 +1,34 @@ +#include "print.h" +/* void main(void) { + put_char('k'); + put_char('e'); + put_char('r'); + put_char('n'); + put_char('e'); + put_char('l'); + put_char('\n'); //换行符 + put_char('1'); + put_char('2'); + put_char('\b'); // 回退符 + put_char('3'); + while(1); +} */ +/* +void main(void) { + put_str("I am ZhangLaoTai!!!\n"); + while(1); +} +*/ +void main(void) { + put_str("I am kernel\n"); + put_int(0); + put_char('\n'); + put_int(9); + put_char('\n'); + put_int(0x00021a3f); + put_char('\n'); + put_int(0x12345678); + put_char('\n'); + put_int(0x00000000); + while(1); +} diff --git a/c6/kernel/main.o b/c6/kernel/main.o new file mode 100755 index 0000000000000000000000000000000000000000..ce280170122345b9042cd186c13759d53ef4c5a4 GIT binary patch literal 1256 zcma)6L2DC16n?u|Yt%&Aq99gr4k{_qA-0KBFB*$hK?MsDJ%wpHC8ga&cSlHzq7VfA zMg9T7t3Sbmf}jT#4<5w$z1^8Q-HQ%PzW2TP=FQA|Z}aTht?NM$kQ)J&=r1FBALc8K z3~E%R3bkK-9DVpY`ZD=8I?e7+zsBQn!9M8u4|ulkzkEF0-Rt{@_ZrhKrtjZ9>+|oM zLd9ZJCw;vI#q!e9B~e?x)1DO#xhR`rzJ9)0zc9ZbYAbq8+)yU>&n;Y>MUW#L2qV~8 zq2Nh5n5jh3OFa4*d?&IMq7h^3aQQ`ewYtyxeDHG=(YLX`wl{->jF$Xmu&BWIo=~Fw zh4pfQPmuHQJ*QEzs;8KQeYC(&7C6Ofqc6J|(8I^3lbR&MjlRjS+fxblR4@ZvZweS9 z)f^{0M8tUvvDmb{W%+H(A6Tv}e`NVn%LOC%mQ1%}qgH_?$+73GL~lu>hlb>uGK%DC znvzTgIq44KSmUm;KQLON_H(IG-AxFvOZPhKiHiB~{~V{F>mV?Pkfu`qfFhHa7Fk(ZTPC7#dXt>fgkE#Qy(iK_ z!HoN`q=oHGUsc%K9=4~g7HZeH%{>hK3*wVjgt*K~5&uNR?ECJY+*2R+;0Nb^-|zc= z=XbvICpnWC8VCde#Ipbi5+4!rg`uiAa;_zmV18A3o+T1wg@;17-0KBho zqH7(l-v-{(!qk#NNS|9n=S}H?7)rjuq!2@F~99u)3+B2e?f&mi6e z!?Pu5v>O2G;5z8#eF0o&qa%HP&7F*Fo!wGu5V3WnhGN#0ws`&V@e|c?{%be+uEsCj z3jXxLD!+6)_;2}&jlvG%mFnD3BvNW@69-C-&0=q<(Jqwwhx|)^{Q;Z!BhabJV`DT6z4r1|qY0J`O0-vWp zERZlE^TW_6<9`NwLRM8-dBzt?A*s*uOK1?fcKR&eSyij!m4|bQNM-pl-swCZ$^r~x zt%R*to6c;omSgpe)<2uA0i3y7F^uq;~&p zvkB)k=#+ppQ&+1%ie^-ylBi%)f<(=%DGZ^eAeuhqco!Q?b2upcKjz&tQrgH`Q>L!} zW#D^JNT*ByKE5g6)|x#4@O?P8*6j5{+Xu)1c>ld9WE8Nkp^#I6kJ()OD8zy<<{VSV zSqQ%MAk=?+2VVtzPO8@+J}+SfHhfXSU4R!Q90B~dg!ch{B;jL#QNuCl7vivlXCU4# z;aP}#BzytlPbK^d#JeT@JH&e>e9&78dqPQ-y(hMYOxjdtsv5ZfIY+5ypoI1g%!9>w;7WJgx zsZo|+Kewg0zGCQ_M$NRrm?W3hOn+UI37Re%rY>y-o5qD{M$bEYf%CDa;9>UdNvS5w z(Ee0TI6|0({txXMXpu~z8?C8PgQ=NmDs*aJhfkW!6(GfUZi>cQSzgpMA7L*{O=oGo Y==}!X!d(a8k9P;$JNmfm-6uf)1GUh6_5c6? literal 0 HcmV?d00001 diff --git a/c6/lib/stdint.h b/c6/lib/stdint.h new file mode 100755 index 0000000..0144281 --- /dev/null +++ b/c6/lib/stdint.h @@ -0,0 +1,17 @@ +/** + * 仿照/usr/include/stdint.h定义我们自己的数据类型. + */ +#ifndef _LIB_STDINT_H +#define _LIB_STDINT_H + +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +#endif \ No newline at end of file diff --git a/c6/loader.S b/c6/loader.S new file mode 100755 index 0000000..a9312c9 --- /dev/null +++ b/c6/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c6/loader.bin b/c6/loader.bin new file mode 100755 index 0000000000000000000000000000000000000000..4d87964e24bab684927d92d726def67fa3c95f9d GIT binary patch literal 1396 zcmeHHziSgw9RI#1X)d{|cSBMgS{h0n6dle@+C>Uoq)-t-2iI?RYsNytf#h)zB$Iob zoRliuT}!>@O1Taau^@pi^{8@fHH0=z3YMPVmlW#cA5gsE`|Eu^@B52)0R1nDNWZE> zPgm-Y1SrTA_x$v~y37+814v!w{{Gb)>f7kd-J5;*F)gE&3jxs15aCny*&SAo2G3+P zenWoOyh7G;TBdXT3(lJ{oR3XQXm}aCTU(4FuLXMtk1ZAoq7CuxH(U!eye{4orfOKM z0ce2yE9x$9pbfwP3B)##A0CXoFA;CD55Hjl#Ty2Bx1tREsHki>E~_~g4WFswF79tC z01~v((xBWZyXDU-Vw-4PgK}tzy)Fa2X4@w#yunDkAxmY6IQ3$V4w5Y1AEA(2ObDlE ztqW<1iq-K3B^x5YK-pB56}0^+3P=#9DZB!$l)LvK39jzy+xjL=$IRZOjOOSF$)YE+ zqd9boRACaHUyT+tb2^?7;kjr*3MIP4SXbCqu;ZOR;@=;bi;|gF%vtT|^^>QbO+~;e zVPNBhCnCYA z1U@6kDvJ>=zBJRjI5_q=t)`C7AuIV!-a_YyHQz~FAv$f=6zW*0302uar^A})blKZn h^X1Qp(v*}xL4JY`Fzg1%*U)J({DnMbcmqMl`vWZ_pSBHg?AI)sC3wHl>X@a iv5SH6`=ic-yFM_!?PPC0%=GBTVW9D&fPxSRT?GJ}(N%c> literal 0 HcmV?d00001 diff --git a/c6/pra/base_asm.c b/c6/pra/base_asm.c new file mode 100755 index 0000000..aeba9ce --- /dev/null +++ b/c6/pra/base_asm.c @@ -0,0 +1,12 @@ +#include +int in_a = 1, in_b = 2, out_sum; + +void main() { + asm("pusha; \ + movl in_a, %eax; \ + movl in_b, %ebx; \ + addl %ebx, %eax; \ + movl %eax, out_sum; \ + popa"); + printf("sum is %d\n", out_sum); +} \ No newline at end of file diff --git a/c6/pra/inlineASM.c b/c6/pra/inlineASM.c new file mode 100755 index 0000000..3a498c8 --- /dev/null +++ b/c6/pra/inlineASM.c @@ -0,0 +1,14 @@ +char* str = "hello, world!\n"; + +int count = 0; +void main() { +asm("pusha; \ + movl $4, %eax; \ + movl $1, %ebx; \ + movl str, %ecx; \ + movl $15, %edx; \ + int $0x80; \ + mov %eax, count; \ + popa \ + "); +} \ No newline at end of file diff --git a/c6/pra/mach_mode_warn.c b/c6/pra/mach_mode_warn.c new file mode 100755 index 0000000..06b955c --- /dev/null +++ b/c6/pra/mach_mode_warn.c @@ -0,0 +1,6 @@ +#include +void main() { + int in_a = 0x1234, in_b = 0; + asm("movw %1, %0":"=m"(in_b):"a"(in_a)); + printf("in_b now is 0x%x\n", in_b); +} \ No newline at end of file diff --git a/c6/pra/mem.c b/c6/pra/mem.c new file mode 100755 index 0000000..8f2ba69 --- /dev/null +++ b/c6/pra/mem.c @@ -0,0 +1,7 @@ +#include +void main() { + int in_a = 1, in_b = 2; + printf("in_b is %d\n", in_b); + asm("movb %b0, %1"::"a"(in_a), "m"(in_b)); + printf("in_b now is %d\n", in_b); +} \ No newline at end of file diff --git a/c6/pra/reg4.c b/c6/pra/reg4.c new file mode 100755 index 0000000..bfe2ae5 --- /dev/null +++ b/c6/pra/reg4.c @@ -0,0 +1,15 @@ +#include +void main() { + int in_a = 0x12345678, in_b = 0; + + asm("movw %1, %0;":"=m"(in_b):"a"(in_a)); + printf("word in_b is 0x%x\n", in_b); + in_b = 0; // 将 in_b 恢复为 0,避免上次赋值造成混乱 + + asm("movb %1, %0;":"=m"(in_b):"a"(in_a)); + printf("low byte in_b is 0x%x\n", in_b); + in_b = 0; + + asm("movb %h1, %0;":"=m"(in_b):"a"(in_a)); + printf("high byte in_b is 0x%x\n", in_b); +} \ No newline at end of file diff --git a/c6/pra/reg5.c b/c6/pra/reg5.c new file mode 100755 index 0000000..656012c --- /dev/null +++ b/c6/pra/reg5.c @@ -0,0 +1,10 @@ +// 名称占位符 +#include +void main() { + int in_a = 18, in_b = 3, out_sum; + asm("divb %[divisor]; movb %%al, %[result]" \ + :[result]"=m"(out) \ + :"a"(in_a), [divisor]"m"(in_b) \ + ); + printf("The result is %d\n", out_sum); +} \ No newline at end of file diff --git a/c6/pra/reg6.c b/c6/pra/reg6.c new file mode 100755 index 0000000..3c04acf --- /dev/null +++ b/c6/pra/reg6.c @@ -0,0 +1,6 @@ +#include +void main() { + int in_a = 1, in_b = 2; + asm("addl %%ebx, %%eax;":"+a"(in_a):"b"(in_b)); + printf("in_a is %d\n", in_a); +} \ No newline at end of file diff --git a/c6/pra/reg7.c b/c6/pra/reg7.c new file mode 100755 index 0000000..be1f9cd --- /dev/null +++ b/c6/pra/reg7.c @@ -0,0 +1,13 @@ +#include +void main() { + int ret_cnt = 0, test = 0; + char* fmt = "hello,world\n"; + asm("pushl %1; \ + call printf; \ + addl $4, %%esp; \ + movl $6, %2" \ + : "=a"(ret_cnt) \ + : "m"(fmt), "r"(test) \ + ); + printf("the num of bytes written is %d\n", ret_cnt); +} \ No newline at end of file diff --git a/c6/pra/reg8.c b/c6/pra/reg8.c new file mode 100755 index 0000000..19a57e3 --- /dev/null +++ b/c6/pra/reg8.c @@ -0,0 +1,13 @@ +#include +void main() { + int ret_cnt = 0, test = 0; + char* fmt = "hello,world\n"; + asm("pushl %1; \ + call printf; \ + addl $4, %%esp; \ + movl $6, %2" \ + : "=&a"(ret_cnt) \ + : "m"(fmt), "r"(test) \ + ); + printf("the num of bytes written is %d\n", ret_cnt); +} \ No newline at end of file diff --git a/c6/pra/reg9.c b/c6/pra/reg9.c new file mode 100755 index 0000000..b8123c5 --- /dev/null +++ b/c6/pra/reg9.c @@ -0,0 +1,8 @@ +#include +void main() { + int in_a = 0, sum = 0; + asm("addl %1, %0;" + : "=a"(sum) + : "%I"(2), "0"(in_a)); + printf("sum is %d\n", sum); +} \ No newline at end of file diff --git a/c6/pra/reg_constraint.c b/c6/pra/reg_constraint.c new file mode 100755 index 0000000..d5c595b --- /dev/null +++ b/c6/pra/reg_constraint.c @@ -0,0 +1,6 @@ +#include +void main() { + int in_a = 1, in_b = 2, out_sum; + asm("addl %%ebx, %%eax" : "=a"(out_sum):"a"(in_a), "b"(in_b)); + printf("sum is %d\n", out_sum); +} \ No newline at end of file diff --git a/c6/pra/syscall_write.S b/c6/pra/syscall_write.S new file mode 100755 index 0000000..2199843 --- /dev/null +++ b/c6/pra/syscall_write.S @@ -0,0 +1,44 @@ +section .data +str_c_lib: db "c library says : hello world!", 0xa ;0xa 为 LF ASCII码 +str_c_lib_len equ $-str_c_lib + +str_syscall: db "syscall says : hello world!", 0xa +str_syscall_len equ $-str_syscall + +section .text +global _start +_start: +;;;;;模拟 方式一 利用 c 语言进行调用 +; write 总共三个参数 从右至左压入 + push str_c_lib_len + push str_c_lib + push 1 + + call simu_write + add esp, 12 ; 回收栈空间 + +;;;;;; 模拟方式二 跨过库函数,直接进行系统调用 + mov eax, 4 ; 4 号功能是write的系统调用 + mov ebx, 1 + mov ecx, str_syscall + mov edx, str_syscall_len + int 0x80 ;发起中断 + +;;;;; 退出程序 + mov eax, 1 ; 第 1 号子功能为 exit + int 0x80 + +;;;;;;; 自定义 simu_write 函数 用来模拟系统调用的 write +simu_write: + push ebp + mov ebp, esp + mov eax, 4 + mov ebx, [ebp + 8] + mov ecx, [ebp + 12] + mov edx, [ebp + 16] + int 0x80 + pop ebp ; 恢复ebp + ret + + + diff --git a/c7/improve/boot/loader.S b/c7/improve/boot/loader.S new file mode 100755 index 0000000..a9312c9 --- /dev/null +++ b/c7/improve/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c7/improve/boot/mbr.S b/c7/improve/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c7/improve/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c7/improve/build.sh b/c7/improve/build.sh new file mode 100755 index 0000000..bf2d5b6 --- /dev/null +++ b/c7/improve/build.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# echo "Creating disk.img..." +# bximage -mode=create -hd=10M -q disk.img +rm -rf build +echo "Mkdir build..." +mkdir build + +# 构建一个 build 目录用于放各种生成文件 +echo "Compiling..." +nasm -I include/ -o build/mbr.bin boot/mbr.S +nasm -I include/ -o build/loader.bin boot/loader.S +# 编译 print.S 和 kernel.S +nasm -f elf -o build/print.o lib/kernel/print.S +nasm -f elf -o build/kernel.o kernel/kernel.S +# 编译 main.c interrupt.c init.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/main.o kernel/main.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/interrupt.o kernel/interrupt.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/init.o kernel/init.c +#链接 +ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o + +echo "Writing mbr, loader and kernel to disk..." +dd if=build/mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=build/loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=build/kernel.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +echo "Now start bochs and have fun!" + +cd /home/husharp/bochs_hu/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c7/improve/build/init.o b/c7/improve/build/init.o new file mode 100644 index 0000000000000000000000000000000000000000..8452215fc75f44c0ee7f9276f82457659f308523 GIT binary patch literal 1376 zcma)5+iDY06y1|a+cfrO5qxOH2#QtdF~zDu6nabY653+h$1)`8*n~D0Cnr#~c#DEk z1iwT4i|B(NBKXi3-ve>2$s96-f?aTCU(Pyf_Sv)NO*)scEK3Y5v85l0#O&VAqHpG8 zQYQTU5yU&6e{Z!;qklTxZuea~f=ugl>`RNxO+VTCG3DE@C%c_r?Q*rOo&4cpqM`Gi zz8060+3Rh|uC6{x%Bz+FC;Ct@EXE8|0JEqniG8!VA2q?fn{AUqS{dq?GOH%zjH_v^zRP_bpG zzX{Kl;enVP>%*4OegMyw_&^*Rh%GT0-f=o@|* zah#*n1GVpJr(Se)saZWxg(GpYxs8?OoU@V1Y^ArI?d6qR+L3xwI}Pngc~3jkfi~-l zj0q-0Bt9;38FmzHxdIzS@&@f>?|Vhi&Najlwn;T!RCK-otG%8r8P9VMm3EJ`sJ*<- ziz+l4qP$u!;D$Q8XjQCLDsC0$sx|GZ?E20;#;$mP!%%cf&NDAxaYgNWwPqb9oENV^ z4ELS?1@Db(`-`*1brp;AWTxOTe1A4!2{UUm@Hq)fSVRtenS-SsCyrXwXYY6>_#Nc{ zei%mY4wB^mkCz08XZQ|+df|H+y%h5FhXdjo%PL+~e8}Y><|3 z#tE#^dmiviecusphPbAN3UcB8X&jhBj{V~wW%@^5qXkzwd?1N2l?zzp@f@{G@8C)Y WoOL<@OnvNCnD+tV?+ppo@csdxQ;#$N literal 0 HcmV?d00001 diff --git a/c7/improve/build/interrupt.o b/c7/improve/build/interrupt.o new file mode 100644 index 0000000000000000000000000000000000000000..10fad9d64485e23c187c0ab0fb65921b48214483 GIT binary patch literal 4320 zcma)q!EImTBvlbQ*c0ZW>8c>tv zKbNliH@0_ncIru2N%||%rm1prdnvlr`~vEW$?d1^x2i-Qx65F%S5I!i;>)?#)=-~YhW^d4hhc-qe z?f@N!p zvCy7wRQb5(sb&O22ZUOrWG%XdLD;qS$_mNK$ky`tr?3~wt&h1h$6$h{6x~ATTm0-B zj%|40??OZN->q_WxqyA-Yn>{0kbfxQZ^2n;EFTVO=tUYvyT(0vlmwG)PiSFrz1PSBb8 z+-H&OR@aU(o;gS_5`C-tC9!}*=uus;jc@#CXJ_M!H}H5IN7C8RKY>4MzHl+{@_L~B zdii4c*ZXdxATE_JpS!}!YvtnA@|oW^dnT@m0Z+OIT&YCgGVZRYDeZa@#j>$^@=Myp zDm0w)xm-%i6>>IDn+4-gBdKy}TT2`ByY;z(%@>T6ZCb~8*CHzxW(wx10&9yzc_g=x zOB+0DS*8U=m`9AsVunW-Q^uT~GYf2VWRMRQY{RmOb2e)W$9dQ?^qD!627S-1Z7jye z77S}DZ=Q0g5ocTY1P_lTMNn>sGiA)rn-)(PnOOr-Q=C>2??0ZX zPCK4R?3y!P^`=SVmNAc>tPNfXTTjj0G!Nc$AkGJjf??@-_?tHJjr_R>jmLOg&lsJx zF{1BCj4$>c<*|5@$MU9b=L(t5xRYXMW%~MgUp|*9h!^P38VJFaqLF?+qNlRCg3(!P zV{M<0@xGlRU!9P#dmnM(t3E64fOXPLc80yQY@4{yW)$LO^=~?Y_LT2qSy@NVY$JnmKoy>b+lE z?Yq>sCfoxsx6)WBcwJFQ*HxpFrmB!;!9Mm6`i53Z9ez8wOC76sBdmMaKB=L+dZ9TW z*^hxeu)9kgyN$gc3bh90|6%YtD&?EF6H?`u_&8YQk~j`lc_Vh0I_7sHtPM51w}zW) zxUq)a*d4L=Q5<$$+={YU{M_5WE7|Eh-H ztl>X`)&4ku2^0oizTAt+IU}hII2-3 zmR8Vb4Ka^Rx9zR^PcE{#qOHx_meT__q;*3N17hkb=hgI2oneT;Pz-^tif$-jhbfYWaveIH=#uVjb+b!oo`TY&tJLsI`C z$c_&(_5fr94%)*c(f=_>>i;w(`F|IZ{9lsxOVa)($-5Z)6G>b+|AwUbb|A@rKh~_{ z4#w_)Y(VA2I5bCyG0(k{!;q|zaUwYoRwUVd`j{e$&W~WT=J8WzbW~GN`yuBndl6%Z_61~GN;==f&q{ek%1tRb ztt&3a9+LE*D>#387MmE-wj7yNEz8IUZDY}9!L)AcEI2to&w`fe02zX}K4zlRt!y|# z!IU{mUv(BN;73DnU^Lk&4hROuY(`d_vgjlRGnQGLLwNshc?ym#F6SP!>DHxJ#5NY+ z9;B1i`q8&n`C&P#esupMMb$4w1nqwz_o|SM3XY7p%|iqz0bJ!Fwu2@m0t*c^vzO*%SIO@sWFu(S-y35|2OZ{^VBYU6y_-^mX`!R8X*~Sj%YBS2Ki4 zC5}COA0*|O?ojfhyir#*7MhGh`3{uLXsdk?K%2%=IYyHO@QK}_hAb)s+EZ@7^O*PB J$_Y~W{TGk5oVox2 literal 0 HcmV?d00001 diff --git a/c7/improve/build/kernel.o b/c7/improve/build/kernel.o new file mode 100644 index 0000000000000000000000000000000000000000..32eebd5a18a311b1fa92289f8b7fe8d82c8456db GIT binary patch literal 3232 zcmbVOO>9(E7`^it`bW!5vA0yH?P!W(g5$heYeiy4BLq!D2oQs&kq*olTF0gtUd&)L z3{hFIAjA-(F(kkOe-@@8XyQU*h;do5an{tyzkPv)4TFZ&YbVO zbKjkQoj2#sq0Qr45{ZPel~BvnqEad^#z>vlt2X%mr?@9!@2)|--LO$o`c_%jB03!? zb<*C1YiYUO1#Y;k=9VkIqhol1Q?IhUC~HJ_PQMsxAPrDh{xG$&g4y4eVf=D%a%qh@27(VaAoH5*Bz`GH)k z7-O2fLoe`n?M4sLo4>X6Z|N|4J-zQd-U_2P z(EH5ebsC+c_pZn5GMZnsYp;2{l}3-zd(q>ijNU}=NspH{I#2IDkJoMVGxS`Z~dVl@9boPFuXX*Xo@$NA?qW1;7n zLmn?NGFS>)T#w@y0D$7cC_0_z4Ng+1wR8n29 zzlFWvo&?OtK#js=X2lgKK0eH^xB|5UO1?nuCt5#zF^XQq6aRurk8fK-{KcQi6T73l#Gt}xo&@fM}zKMp}YBjT+ zIcs$IrD(wbj$m%xo)cEzfL=T^0@ZCTa2iRMGo|vwF=cnEsbaJTPj0!Sre>nbo)TUTIv1)_QQgW_bGf=}Rp_W;M?K=G z5l21hs8L5f=BP18J?^N2qxL##pQDP7Dmm)qMin~qh0c7TGhgV;7drEW&U~RWU+Byi zI`f6je4#U6=*$;7^M%fQp)+6T%r`9a+2_G>0?$_xg3KQa5c9uXcB-XxqCW{z<^k390Y@OI%{!n=hhgeQfI!aP!Ve_41|I1;W3GxxZz zCJd_Ay?9OFhv`P)ox=6~aQz+8r-i=~z9jsQa5uiE+~*s_+S6j8U)KUvH~- literal 0 HcmV?d00001 diff --git a/c7/improve/build/loader.bin b/c7/improve/build/loader.bin new file mode 100644 index 0000000000000000000000000000000000000000..4d87964e24bab684927d92d726def67fa3c95f9d GIT binary patch literal 1396 zcmeHHziSgw9RI#1X)d{|cSBMgS{h0n6dle@+C>Uoq)-t-2iI?RYsNytf#h)zB$Iob zoRliuT}!>@O1Taau^@pi^{8@fHH0=z3YMPVmlW#cA5gsE`|Eu^@B52)0R1nDNWZE> zPgm-Y1SrTA_x$v~y37+814v!w{{Gb)>f7kd-J5;*F)gE&3jxs15aCny*&SAo2G3+P zenWoOyh7G;TBdXT3(lJ{oR3XQXm}aCTU(4FuLXMtk1ZAoq7CuxH(U!eye{4orfOKM z0ce2yE9x$9pbfwP3B)##A0CXoFA;CD55Hjl#Ty2Bx1tREsHki>E~_~g4WFswF79tC z01~v((xBWZyXDU-Vw-4PgK}tzy)Fa2X4@w#yunDkAxmY6IQ3$V4w5Y1AEA(2ObDlE ztqW<1iq-K3B^x5YK-pB56}0^+3P=#9DZB!$l)LvK39jzy+xjL=$IRZOjOOSF$)YE+ zqd9boRACaHUyT+tb2^?7;kjr*3MIP4SXbCqu;ZOR;@=;bi;|gF%vtT|^^>QbO+~;e zVPNBhCnCYA z1U@6kDvJ>=zBJRjI5_q=t)`C7AuIV!-a_YyHQz~FAv$f=6zW*0302uar^A})blKZn h^X1Qp(v*}xL4JY`Fzg1%*U)J({DnMbcmqMl`vWZ_4qF1>-OW8&(A zBKuGLFXo@3An1cWJFMs2#AN;6a}OypusExNNE1fnt(i{ zVNvYQV|<|JVw2B5raMQ?_g}Uie?6K`r*95b+?jk&9ZcxNBaxhZ$_V`R?f3nxX=i(u z>sb8@N%*jmJB0ei#_epr+43CjWpA?UtXR0_iD-3iwZw{Rm;Ef9B}Fvl|P)zr|6^YA{7;Lla zu(kj&jB)7(+qSqdY#Y4eIX%`IgHhKv*UP%GS*vYT8%CqNu2&5hdfXUs7rIWD8)muGLX0f}6bRD*Swt0Ed5@4<#6~Y{ZnhA)>tjcHuGMGU8gpRJ zOl|^ejYh!SfgjKYJGp4q9@w_!;5lc&EmnWnER49>?%`onv^vIf*R(BQ+wQ;{VhR6i zuaI~qM5gaR-vqBM7EJ^#x;xF1oPay%i`hgZlXJF=K25=0Tx2@-C67pYG;O3s`qV=@ z6a0=4ty$__4fy}!6$3qb2N~(%C;DqruY?)$;{kCG{qWRFUL)Xmwe$Lz=9?#zeS(91mnLNByI}N%oI)r53t=F_@(P=M*mD(K*tR Yy+cpSBHg?AI)sC3wHl>X@a iv5SH6`=ic-yFM_!?PPC0%=GBTVW9D&fPxSRT?GJ}(N%c> literal 0 HcmV?d00001 diff --git a/c7/improve/build/print.o b/c7/improve/build/print.o new file mode 100644 index 0000000000000000000000000000000000000000..d76cd8774209cd7de7f37b90a4a973dac454fd3b GIT binary patch literal 1792 zcmbVMUuaWT7(cnKrv8l&PKAxxL6{C9O@~uqV@zUN#9~@olriD-COM}Kz2=5{PliIl zjQg;ph3##7RlzqO^l8>Yb&c#F2y;5*p*OxcbYrO+_x35siwc@0CZD>ZjwsCrLOcoc}B7KFPHufquJ z9va;NAa&$A(yRLdTw$Xl{ol`?jBlLTP-zjdaik%|oSWUL=A#cks*m%lJIH4Ze(i48 z<;{=zwV%6wtKM``%tO3ZpB;+FE3Hl9y-I7n*k5V&2(7t|AK|xuVH4j(dwEK_D8Joo zj=Q;PEKKnf|Ky(^qtgSt|0iBs;tRusKYzyGy2G9Nuvgmi@7xU>#KO7C26CO?^T)+` z5+h`87>zRiZC7v9scXxB^7%?s>9hJ58bsG#pVg(udVRe1>ufq+Tf8psbWR@1JO*)= zW6s~}&aQD5lg+ix!}ZSM!Rpp`Gha8lYm3XvgHF3g#AZI1%)v@4DSpp1wJXVy#{C!V zHqL3#D}goL)axK6^19GTLa@_GJDfj^J0|lP|mKvU+U&Jv5Pa}Rs!83?^ z6?`7?8w&mk@tX?%0r5KuUP8QI!S4r4!*9X<^YE|xIf*miHWf^aLbh*;Sw&{_m8?Qh zBu2^DVLEtdj2s&tN|Mp^P&zf1IYy5kK9tUoQdv-tXtvBbvs{Q57Wl-3X%;&G1!{im zvvQNVN0ULKVAHIgo3cxKj=6j{E5%JQsmzc>qflg&GnNmD$mVp*D(Dl8T1=EJZ_z+{ zRgJRZ%DElIl@-f03~J{r#uT}nVTbG5Owe4}vQ1?xvT3<6&6`DUFE}4}iagBzeOcXR zdAc`S5T20CqW^<$4|GVjFs;tigvIpy87fTbUPnlpDwL37yl|Q(JJ|`7nnh}()=BZQ iVT1(P9@BG^v{-h3CNFUJ5&SXl#ygNc<$M20kpBQayM!tL literal 0 HcmV?d00001 diff --git a/c7/improve/include/boot.inc b/c7/improve/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c7/improve/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c7/improve/kernel/global.h b/c7/improve/kernel/global.h new file mode 100755 index 0000000..73ef849 --- /dev/null +++ b/c7/improve/kernel/global.h @@ -0,0 +1,34 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#endif \ No newline at end of file diff --git a/c7/improve/kernel/init.c b/c7/improve/kernel/init.c new file mode 100755 index 0000000..519ea91 --- /dev/null +++ b/c7/improve/kernel/init.c @@ -0,0 +1,9 @@ +#include "init.h" +#include "kernel/print.h" +#include "interrupt.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init(); +} \ No newline at end of file diff --git a/c7/improve/kernel/init.h b/c7/improve/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c7/improve/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c7/improve/kernel/interrupt.c b/c7/improve/kernel/interrupt.c new file mode 100755 index 0000000..89209fe --- /dev/null +++ b/c7/improve/kernel/interrupt.c @@ -0,0 +1,135 @@ +# include "stdint.h" +# include "global.h" +# include "io.h" +# include "interrupt.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + put_str("int vector: 0x"); + put_int(vec_nr); + put_char('\n'); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} \ No newline at end of file diff --git a/c7/improve/kernel/interrupt.h b/c7/improve/kernel/interrupt.h new file mode 100755 index 0000000..3400b81 --- /dev/null +++ b/c7/improve/kernel/interrupt.h @@ -0,0 +1 @@ +typedef void* intr_handler; \ No newline at end of file diff --git a/c7/improve/kernel/io.h b/c7/improve/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c7/improve/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c7/improve/kernel/kernel.S b/c7/improve/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c7/improve/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c7/improve/kernel/main.c b/c7/improve/kernel/main.c new file mode 100755 index 0000000..98aa677 --- /dev/null +++ b/c7/improve/kernel/main.c @@ -0,0 +1,9 @@ +#include "kernel/print.h" +#include "init.h" + +void main(void) { + put_str("I am kernel!\n"); + init_all(); + asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + while(1); +} \ No newline at end of file diff --git a/c7/improve/lib/kernel/print.S b/c7/improve/lib/kernel/print.S new file mode 100755 index 0000000..52b9246 --- /dev/null +++ b/c7/improve/lib/kernel/print.S @@ -0,0 +1,217 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret diff --git a/c7/improve/lib/kernel/print.h b/c7/improve/lib/kernel/print.h new file mode 100755 index 0000000..903039e --- /dev/null +++ b/c7/improve/lib/kernel/print.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +#endif + diff --git a/c7/improve/lib/stdint.h b/c7/improve/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c7/improve/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c7/timer/boot/loader.S b/c7/timer/boot/loader.S new file mode 100755 index 0000000..a9312c9 --- /dev/null +++ b/c7/timer/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c7/timer/boot/mbr.S b/c7/timer/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c7/timer/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c7/timer/build.sh b/c7/timer/build.sh new file mode 100755 index 0000000..c979273 --- /dev/null +++ b/c7/timer/build.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# echo "Creating disk.img..." +# bximage -mode=create -hd=10M -q disk.img +rm -rf build +echo "Mkdir build..." +mkdir build + +# 构建一个 build 目录用于放各种生成文件 +echo "Compiling..." +nasm -I include/ -o build/mbr.bin boot/mbr.S +nasm -I include/ -o build/loader.bin boot/loader.S +# 编译 print.S 和 kernel.S +nasm -f elf -o build/print.o lib/kernel/print.S +nasm -f elf -o build/kernel.o kernel/kernel.S +# 编译 timer.c main.c interrupt.c init.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/timer.o device/timer.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/main.o kernel/main.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/interrupt.o kernel/interrupt.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/init.o kernel/init.c +#链接 +ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o build/timer.o + +echo "Writing mbr, loader and kernel to disk..." +dd if=build/mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=build/loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=build/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +echo "Now start bochs and have fun!" + +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c7/timer/device/timer.c b/c7/timer/device/timer.c new file mode 100755 index 0000000..af7221c --- /dev/null +++ b/c7/timer/device/timer.c @@ -0,0 +1,39 @@ +#include "time.h" +#include "io.h" +#include "kernel/print.h" + + +#define IRQ0_FREQUENCY 100 // 时钟频率 +#define INPUT_FREQUENCY 1193180 // 计数器原本的工作频率 +#define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY //计数器 0 初始值 +#define COUNTRER0_PORT 0x40 // 计数器 0 的接口 +#define COUNTER0_NO 0 // 计数器号码 +#define COUNTER_MODE 2 // 模式 2 +#define READ_WRITE_LATCH 3 // 读写方式,先读写低 8 位,再读写高 8 位 +#define PIT_CONTROL_PORT 0x43 // 控制器寄存器接口 + +/* + * 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode + * 写入模式控制寄存器 + * 并赋予初始值counter_value + * */ +static void frequency_set(uint8_t counter_port, \ + uint8_t counter_no, \ + uint8_t rwl, \ + uint8_t counter_mode, \ + uint16_t counter_value) { +/* 往控制字寄存器端口0x43中写入控制字 */ +// 最后一位为 0 ,即选择二进制 + outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1)); +/* 先写入counter_value的低8位 */ + outb(counter_port, (uint8_t)counter_value); +/* 后写入counter_value的高8位 */ + outb(counter_port, (uint8_t)counter_port >> 8); +} + +// 初始化 8253 +void timer_init() { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + put_str("timer_init done!\n"); +} diff --git a/c7/timer/device/timer.h b/c7/timer/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c7/timer/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c7/timer/include/boot.inc b/c7/timer/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c7/timer/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c7/timer/kernel/global.h b/c7/timer/kernel/global.h new file mode 100755 index 0000000..73ef849 --- /dev/null +++ b/c7/timer/kernel/global.h @@ -0,0 +1,34 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#endif \ No newline at end of file diff --git a/c7/timer/kernel/init.c b/c7/timer/kernel/init.c new file mode 100755 index 0000000..9286b36 --- /dev/null +++ b/c7/timer/kernel/init.c @@ -0,0 +1,11 @@ +#include "init.h" +#include "kernel/print.h" +#include "interrupt.h" +#include "../device/timer.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init(); + timer_init(); +} \ No newline at end of file diff --git a/c7/timer/kernel/init.h b/c7/timer/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c7/timer/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c7/timer/kernel/interrupt.c b/c7/timer/kernel/interrupt.c new file mode 100755 index 0000000..89209fe --- /dev/null +++ b/c7/timer/kernel/interrupt.c @@ -0,0 +1,135 @@ +# include "stdint.h" +# include "global.h" +# include "io.h" +# include "interrupt.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + put_str("int vector: 0x"); + put_int(vec_nr); + put_char('\n'); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} \ No newline at end of file diff --git a/c7/timer/kernel/interrupt.h b/c7/timer/kernel/interrupt.h new file mode 100755 index 0000000..3400b81 --- /dev/null +++ b/c7/timer/kernel/interrupt.h @@ -0,0 +1 @@ +typedef void* intr_handler; \ No newline at end of file diff --git a/c7/timer/kernel/io.h b/c7/timer/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c7/timer/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c7/timer/kernel/kernel.S b/c7/timer/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c7/timer/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c7/timer/kernel/main.c b/c7/timer/kernel/main.c new file mode 100755 index 0000000..98aa677 --- /dev/null +++ b/c7/timer/kernel/main.c @@ -0,0 +1,9 @@ +#include "kernel/print.h" +#include "init.h" + +void main(void) { + put_str("I am kernel!\n"); + init_all(); + asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + while(1); +} \ No newline at end of file diff --git a/c7/timer/lib/kernel/print.S b/c7/timer/lib/kernel/print.S new file mode 100755 index 0000000..52b9246 --- /dev/null +++ b/c7/timer/lib/kernel/print.S @@ -0,0 +1,217 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret diff --git a/c7/timer/lib/kernel/print.h b/c7/timer/lib/kernel/print.h new file mode 100755 index 0000000..903039e --- /dev/null +++ b/c7/timer/lib/kernel/print.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +#endif + diff --git a/c7/timer/lib/stdint.h b/c7/timer/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c7/timer/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c7/with_asm/boot/loader.S b/c7/with_asm/boot/loader.S new file mode 100755 index 0000000..a9312c9 --- /dev/null +++ b/c7/with_asm/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c7/with_asm/boot/mbr.S b/c7/with_asm/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c7/with_asm/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c7/with_asm/build.sh b/c7/with_asm/build.sh new file mode 100755 index 0000000..645f1c2 --- /dev/null +++ b/c7/with_asm/build.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# echo "Creating disk.img..." +# bximage -mode=create -hd=10M -q disk.img +rm -rf build +echo "Mkdir build..." +mkdir build + +# 构建一个 build 目录用于放各种生成文件 +echo "Compiling..." +nasm -I include/ -o build/mbr.bin boot/mbr.S +nasm -I include/ -o build/loader.bin boot/loader.S +# 编译 print.S 和 kernel.S +nasm -f elf -o build/print.o lib/kernel/print.S +nasm -f elf -o build/kernel.o kernel/kernel.S +# 编译 main.c interrupt.c init.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/main.o kernel/main.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/interrupt.o kernel/interrupt.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/init.o kernel/init.c +#链接 +ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o + +echo "Writing mbr, loader and kernel to disk..." +dd if=build/mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=build/loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=build/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +echo "Now start bochs and have fun!" + +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c7/with_asm/include/boot.inc b/c7/with_asm/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c7/with_asm/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c7/with_asm/kernel/global.h b/c7/with_asm/kernel/global.h new file mode 100755 index 0000000..73ef849 --- /dev/null +++ b/c7/with_asm/kernel/global.h @@ -0,0 +1,34 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#endif \ No newline at end of file diff --git a/c7/with_asm/kernel/init.c b/c7/with_asm/kernel/init.c new file mode 100755 index 0000000..519ea91 --- /dev/null +++ b/c7/with_asm/kernel/init.c @@ -0,0 +1,9 @@ +#include "init.h" +#include "kernel/print.h" +#include "interrupt.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init(); +} \ No newline at end of file diff --git a/c7/with_asm/kernel/init.h b/c7/with_asm/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c7/with_asm/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c7/with_asm/kernel/interrupt.c b/c7/with_asm/kernel/interrupt.c new file mode 100755 index 0000000..f55ee46 --- /dev/null +++ b/c7/with_asm/kernel/interrupt.c @@ -0,0 +1,86 @@ +# include "stdint.h" +# include "global.h" +# include "io.h" +# include "interrupt.h" + +#define IDT_DESV_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESV_CNT]; // 中断描述符数组 + +extern intr_handler intr_entry_table[IDT_DESV_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESV_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} \ No newline at end of file diff --git a/c7/with_asm/kernel/interrupt.h b/c7/with_asm/kernel/interrupt.h new file mode 100755 index 0000000..3400b81 --- /dev/null +++ b/c7/with_asm/kernel/interrupt.h @@ -0,0 +1 @@ +typedef void* intr_handler; \ No newline at end of file diff --git a/c7/with_asm/kernel/io.h b/c7/with_asm/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c7/with_asm/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c7/with_asm/kernel/kernel.S b/c7/with_asm/kernel/kernel.S new file mode 100755 index 0000000..6247620 --- /dev/null +++ b/c7/with_asm/kernel/kernel.S @@ -0,0 +1,69 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +extern put_str ; 声明外部函数 + +section .data +intr_str db "interrupt occur!", 0xa, 0 +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + push intr_str + call put_str + add esp, 4 ; 跳过参数 + + ; 中断结束命令 EOI + mov al, 0x20 + out 0xa0, al + out 0x20, al + + add esp, 4 + iret + +section .data + dd intr%1entry + +%endmacro + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c7/with_asm/kernel/main.c b/c7/with_asm/kernel/main.c new file mode 100755 index 0000000..98aa677 --- /dev/null +++ b/c7/with_asm/kernel/main.c @@ -0,0 +1,9 @@ +#include "kernel/print.h" +#include "init.h" + +void main(void) { + put_str("I am kernel!\n"); + init_all(); + asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + while(1); +} \ No newline at end of file diff --git a/c7/with_asm/lib/kernel/print.S b/c7/with_asm/lib/kernel/print.S new file mode 100755 index 0000000..52b9246 --- /dev/null +++ b/c7/with_asm/lib/kernel/print.S @@ -0,0 +1,217 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret diff --git a/c7/with_asm/lib/kernel/print.h b/c7/with_asm/lib/kernel/print.h new file mode 100755 index 0000000..903039e --- /dev/null +++ b/c7/with_asm/lib/kernel/print.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +#endif + diff --git a/c7/with_asm/lib/stdint.h b/c7/with_asm/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c7/with_asm/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c8/assert/boot/loader.S b/c8/assert/boot/loader.S new file mode 100755 index 0000000..a9312c9 --- /dev/null +++ b/c8/assert/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c8/assert/boot/mbr.S b/c8/assert/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c8/assert/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c8/assert/build.sh b/c8/assert/build.sh new file mode 100755 index 0000000..c979273 --- /dev/null +++ b/c8/assert/build.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# echo "Creating disk.img..." +# bximage -mode=create -hd=10M -q disk.img +rm -rf build +echo "Mkdir build..." +mkdir build + +# 构建一个 build 目录用于放各种生成文件 +echo "Compiling..." +nasm -I include/ -o build/mbr.bin boot/mbr.S +nasm -I include/ -o build/loader.bin boot/loader.S +# 编译 print.S 和 kernel.S +nasm -f elf -o build/print.o lib/kernel/print.S +nasm -f elf -o build/kernel.o kernel/kernel.S +# 编译 timer.c main.c interrupt.c init.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/timer.o device/timer.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/main.o kernel/main.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/interrupt.o kernel/interrupt.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/init.o kernel/init.c +#链接 +ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o build/timer.o + +echo "Writing mbr, loader and kernel to disk..." +dd if=build/mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=build/loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=build/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +echo "Now start bochs and have fun!" + +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c8/assert/build/init.o b/c8/assert/build/init.o new file mode 100644 index 0000000000000000000000000000000000000000..cf1f278d4e9a0ff68a1cb2c19e5c545221eb6461 GIT binary patch literal 1416 zcma)5+iDY06y1|aZ?TtH1Rq*4f?`#ANU>@Vg{E!Vm(Uj5KKM|EBput(TH!Oz)ukLAEFO_h~PtCd@sbcCUeLz6m-MM+Lv|q?7h#<+gyIdvMe!Ti7kCgByIO1 z^FGYTgpB*~A=uj=er>i-@nv02m2)B|--rDyLpeQWN`Tt=nmZ}q$)mzW)5y?i3p z>tSnZG#-DCf>ZEKGXU{x&5jQr#}>y1mfLpaM+YpCBw`CBKwzFL;ojr&ryC~O!XEo# zN3z&5&<{ehWiVoMYJIU~q;Ej8B@wZQBeo^R!yYG5&&3d9GBzYPLpJ+;9I;6!*9d)V*hC0Kiof_t2o>_vnh|D`}+%|n0=gr&TU!1p;R!J zG?4kYmCVKboFVT7e_M&xZ1UX+`2XVZ8@$7BP?#^=m&uobpZZ`xJc7=ej2PY)9MqYY za*V_GHo_RsPJqV$)hGd)e9r@(sqY)?jS$z=P=zl%zt6akgpc#%zs}5$c}*^;elbZn kv959fhdkb6E;Bo*lEkH_R01*eaaLj87x3O0643De0*ZB)@Bjb+ literal 0 HcmV?d00001 diff --git a/c8/assert/build/interrupt.o b/c8/assert/build/interrupt.o new file mode 100644 index 0000000000000000000000000000000000000000..1b0a6f0e7057416e687db7ff845e2e2ec33d2fb2 GIT binary patch literal 4812 zcma)=e{5S<700g~r*+b_ak|0^3+#3YEG5JwT1TsPecR1ZEdZVbfsZ$Db#SRIJ`O=T4{U*{ng>szs;5Sa{7hUplg?maSK9Utg$UHnds_`HGiIA@8xGkAAOqyS)N~E8QLv zjj!P9Beih4RMvix4V69E=GcR)p=FwMsjH+WFAXo3JoEUSYxoW6=e|zAt~Vp8uRK zkJan_SaMZ%1gU*Wt+wQ9Xaz~cQd?=BWNBb!?(FlZg<|uw4$WSepeck_u=Evq`d7AX zZZ<%MdQDNHevigcg_FPJu`)y z+t+v6z7;F3d;{tRc~ET2OPjccZUeYp?-jC{L*AvBdX8BSaX!K!Z(-5(^hIl`EtSA45@x%kV?8)>_)6tB%(7sac^{9^IcpBi097FEK-&V=)G#`59M z$Brye4!cPyC)0^oI+M0!GMh1W)C;K6Nh_8#a(C*}8B0zX2`g*vm7UWfpBc|&Ph><( zFeHQNsdUniA=Av7Py}Sq7|D;y&~(C>w9?s(I5^NJ`!becn)yjfv;-nDU>fo9$t)#Z zcWW68%fTtbJT{R%;ZS3#ErCNaFgScLC=bOmqnHY*jiixuc(n`%F~?MVBAt|jlZh-X zsIq{Tg9BL9R61eEgIP=VOvTd^@sWu;T!LX4%;rfMj^`(w>*~!;n%RVr%VkX&HAasc zST(KF%;WlpqLpcfqR~6%j8wd7(uir~5Gh*V6}94t@w?{1b?%SI{YJ(x;}h`DTG(&w zpEGGBEFt;I5I556WO?x&Wv_M>@BwMOi!=unMjXj)CKgW3@pL1 zMFYKZAf8C2Ge$?XO|(27meIa};9ZF;J4`jB$|z^WO$&8YQE*+AJgp0*=GFzVzqfa< zY#ScQXRN&3?SItY+3`qTQP=6NJ^s#JJ8dmh=r+4dh3m;f!u6Eb^+1EiGmCm}hMqdL zo%GYQ#rw27u({TF9UekRs2)Qn+pFjmd$3UB{?6+QTBq?;4_Zy^r;zfE%ew+R_l2O<|8#iJOWwq z%i0dKULWG;|A)O3_66A6obk!^_z3Jvus`gyCu}>#Ov1hu`#0mXhre9&jHgiR&Mov@ z>A9+wOP@XG;7bkes*&zOm5xmn!DhuC@elgOsI(e%;B*WUr_XR*NyhOgf7v~pf`zIYtL2j3t-*0y6eObG(b}PPpbImReYn0 ze-GC6xfLs10_%Drz75v(Nlf=e*CX+Eu&ys+ffs=NR`4f8HgAp4u_5#;NgR)l8!`Ph zQYT8~wIoLEV-kmsWyVtROmf07g`v+Qbry+OY__T=aKQI{R zIS?BRhohmP*icX4Kqy8lF=LtdaYM~x(c9r(@gvh>GH=ClmT5=eL^n^yYKXPA$BwW46+VRzlQXD zK#0GP9sW1j{wVgG{P#gpd_QFS2ZcBaS%-)B3`xX43rX=WK$8ErAj$tC+pnWoji^Q4AL@fnbITEnDAT1v?ct>xD^-vkz7vr&8!V%@Zb9g zP(k;y;qmq-vd8HQN%%AP0Q2uZINX6pHZhLGn9WGVj+t~y_(#obeiF<3f7|nLY;rj7 z!kFG5bcwjV>OG3B71e(99i#nln<{>|l>%E-dFur+{u(9VMmB1ARMnfQn*i+$4wAFj zf0`F1pz=}vkDyZw>8Ras3Qd%q_JN54E!!? zBlLQ$MU3eSpc|c9b0hvhfuwz=vzz>A-<-LCq8mXH$>Cn910TkeOYbp;;wU~Rb~xtM SL#ipK-x(zOCIxWQe*Xb$Q5!=5 literal 0 HcmV?d00001 diff --git a/c8/assert/build/loader.bin b/c8/assert/build/loader.bin new file mode 100644 index 0000000000000000000000000000000000000000..4d87964e24bab684927d92d726def67fa3c95f9d GIT binary patch literal 1396 zcmeHHziSgw9RI#1X)d{|cSBMgS{h0n6dle@+C>Uoq)-t-2iI?RYsNytf#h)zB$Iob zoRliuT}!>@O1Taau^@pi^{8@fHH0=z3YMPVmlW#cA5gsE`|Eu^@B52)0R1nDNWZE> zPgm-Y1SrTA_x$v~y37+814v!w{{Gb)>f7kd-J5;*F)gE&3jxs15aCny*&SAo2G3+P zenWoOyh7G;TBdXT3(lJ{oR3XQXm}aCTU(4FuLXMtk1ZAoq7CuxH(U!eye{4orfOKM z0ce2yE9x$9pbfwP3B)##A0CXoFA;CD55Hjl#Ty2Bx1tREsHki>E~_~g4WFswF79tC z01~v((xBWZyXDU-Vw-4PgK}tzy)Fa2X4@w#yunDkAxmY6IQ3$V4w5Y1AEA(2ObDlE ztqW<1iq-K3B^x5YK-pB56}0^+3P=#9DZB!$l)LvK39jzy+xjL=$IRZOjOOSF$)YE+ zqd9boRACaHUyT+tb2^?7;kjr*3MIP4SXbCqu;ZOR;@=;bi;|gF%vtT|^^>QbO+~;e zVPNBhCnCYA z1U@6kDvJ>=zBJRjI5_q=t)`C7AuIV!-a_YyHQz~FAv$f=6zW*0302uar^A})blKZn h^X1Qp(v*}xL4JY`Fzg1%*U)J({DnMbcmqMl`vWZ_D6_CIZCu#Q zd5-HD*Tke49U1q0Huhv}N{mhn2UemJ{33|Z;hbTv^UbcgJ&fMXp9NlU<#k%gWVsdVOg_Zp_VRmn4-vS61B$ z6`X=A&0>+trc=nvYMF;1=u7Mv(J9t@c)HFqc5}hbGsgRfMu2?y@z6dU;zEea@E<2K z7-QU`n?RISR3F-|nPYS-0q!WRVikpJ)m#$yP1humt5!)=N&$gG{QZ6+U$Sk>;eAfY zwM1rRbEN8;`CZ=38Lb_;RWWUg#CE0RmH7z&D~~ylukc&&t)kmWUR)Y~(lBkP4Q5nL z(hTeV%ripN{0^{=X}ZA+@*%7X@?naR3;9uF>fSVcxI@?|{7F zHSBA_Lwzw*OcB=^2EJom`4FkzyAkTh3wh7$qOS!H#9`eFF|ZPSQA&s^-(DD}&UeTh z?@*%7V6!1wzXV@A#WvIrU!|%a@~T{{(hM66Ug#NO5r=)qrE142-Q`U=D50qHVFsdc MrpSBHg?AI)sC3wHl>X@a iv5SH6`=ic-yFM_!?PPC0%=GBTVW9D&fPxSRT?GJ}(N%c> literal 0 HcmV?d00001 diff --git a/c8/assert/device/timer.c b/c8/assert/device/timer.c new file mode 100755 index 0000000..af7221c --- /dev/null +++ b/c8/assert/device/timer.c @@ -0,0 +1,39 @@ +#include "time.h" +#include "io.h" +#include "kernel/print.h" + + +#define IRQ0_FREQUENCY 100 // 时钟频率 +#define INPUT_FREQUENCY 1193180 // 计数器原本的工作频率 +#define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY //计数器 0 初始值 +#define COUNTRER0_PORT 0x40 // 计数器 0 的接口 +#define COUNTER0_NO 0 // 计数器号码 +#define COUNTER_MODE 2 // 模式 2 +#define READ_WRITE_LATCH 3 // 读写方式,先读写低 8 位,再读写高 8 位 +#define PIT_CONTROL_PORT 0x43 // 控制器寄存器接口 + +/* + * 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode + * 写入模式控制寄存器 + * 并赋予初始值counter_value + * */ +static void frequency_set(uint8_t counter_port, \ + uint8_t counter_no, \ + uint8_t rwl, \ + uint8_t counter_mode, \ + uint16_t counter_value) { +/* 往控制字寄存器端口0x43中写入控制字 */ +// 最后一位为 0 ,即选择二进制 + outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1)); +/* 先写入counter_value的低8位 */ + outb(counter_port, (uint8_t)counter_value); +/* 后写入counter_value的高8位 */ + outb(counter_port, (uint8_t)counter_port >> 8); +} + +// 初始化 8253 +void timer_init() { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + put_str("timer_init done!\n"); +} diff --git a/c8/assert/device/timer.h b/c8/assert/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c8/assert/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c8/assert/include/boot.inc b/c8/assert/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c8/assert/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c8/assert/kernel/debug.c b/c8/assert/kernel/debug.c new file mode 100755 index 0000000..bef6acf --- /dev/null +++ b/c8/assert/kernel/debug.c @@ -0,0 +1,18 @@ +#include "debug.h" +#include "kernel/print.h" +#include "interrupt.h" + +/* 打印文件名,行号,函数名,条件并使程序悬停 */ +void panic_spin(char* filename, \ + int line, \ + const char* func, \ + const char* condition) \ +{ + intr_disable(); /* 打印文件名,行号,函数名,条件并使程序悬停 */ + put_str("\n\n\n!!!!! error !!!!!\n"); + put_str("filename:");put_str(filename);put_str("\n"); + put_str("line:0x");put_int(line);put_str("\n"); + put_str("function:");put_str((char*)func);put_str("\n"); + put_str("condition:");put_str((char*)condition);put_str("\n"); + while(1); +} \ No newline at end of file diff --git a/c8/assert/kernel/debug.h b/c8/assert/kernel/debug.h new file mode 100755 index 0000000..d4df982 --- /dev/null +++ b/c8/assert/kernel/debug.h @@ -0,0 +1,24 @@ +#ifndef __KERNEL_DEBUG_H +#define __KERNEL_DEBUG_H + +void panic_spin(char* filename, int line, const char* func, const char* condition); + +/*************************** __VA_ARGS__ ******************************* + * __VA_ARGS__ 是预处理器所支持的专用标识符。 + * 代表所有与省略号相对应的参数. + * "..."表示定义的宏其参数可变. + */ +#define PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) +/***********************************************************************/ + +#ifdef NDEBUG + #define ASSERT(CONDITION) ((void)0) +#else + #define ASSERT(CONDITION) \ + if (CONDITION) {} else { \ + /* 符号#让编译器将宏的参数转化为字符串字面量 */ \ + PANIC(#CONDITION); \ + } +#endif // __NDEBUG + +#endif // __KERNEL_DEBUG_H \ No newline at end of file diff --git a/c8/assert/kernel/global.h b/c8/assert/kernel/global.h new file mode 100755 index 0000000..73ef849 --- /dev/null +++ b/c8/assert/kernel/global.h @@ -0,0 +1,34 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#endif \ No newline at end of file diff --git a/c8/assert/kernel/init.c b/c8/assert/kernel/init.c new file mode 100755 index 0000000..9286b36 --- /dev/null +++ b/c8/assert/kernel/init.c @@ -0,0 +1,11 @@ +#include "init.h" +#include "kernel/print.h" +#include "interrupt.h" +#include "../device/timer.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init(); + timer_init(); +} \ No newline at end of file diff --git a/c8/assert/kernel/init.h b/c8/assert/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c8/assert/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c8/assert/kernel/interrupt.c b/c8/assert/kernel/interrupt.c new file mode 100755 index 0000000..8416d53 --- /dev/null +++ b/c8/assert/kernel/interrupt.c @@ -0,0 +1,178 @@ +# include "stdint.h" +# include "global.h" +# include "io.h" +# include "interrupt.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define EFLAGS_IF 0x00000200 // eflags 寄存器的 if 位为 1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + put_str("int vector: 0x"); + put_int(vec_nr); + put_char('\n'); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断, sti 指令将 IF 位 置 1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli 指令将 IF 位 置 0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + // 判断 IF 位的值是否为 1 + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} \ No newline at end of file diff --git a/c8/assert/kernel/interrupt.h b/c8/assert/kernel/interrupt.h new file mode 100755 index 0000000..f2882d6 --- /dev/null +++ b/c8/assert/kernel/interrupt.h @@ -0,0 +1,21 @@ +#ifndef __KERNEL_INTERRUPT_H +#define __KERNEL_INTERRUPT_H +#include "stdint.h" + +typedef void* intr_handler; +void idt_init(void); + +/* 定义中断的两种状态: + * INTR_OFF值为0,表示关中断, + * INTR_ON值为1,表示开中断 */ +enum intr_status { // 中断状态 + INTR_OFF, // 中断关闭 + INTR_ON // 中断打开 +}; + +enum intr_status intr_get_status(void); +enum intr_status intr_set_status (enum intr_status); +enum intr_status intr_enable (void); +enum intr_status intr_disable (void); + +#endif \ No newline at end of file diff --git a/c8/assert/kernel/kernel.S b/c8/assert/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c8/assert/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c8/assert/kernel/main.c b/c8/assert/kernel/main.c new file mode 100755 index 0000000..7fdeb02 --- /dev/null +++ b/c8/assert/kernel/main.c @@ -0,0 +1,12 @@ +#include "kernel/print.h" +#include "init.h" +#include "debug.h" + +int main(void) { + put_str("I am kernel!\n"); + init_all(); + ASSERT(1 == 2); + // asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + while(1); + return 0; +} \ No newline at end of file diff --git a/c8/assert/lib/kernel/io.h b/c8/assert/lib/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c8/assert/lib/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c8/assert/lib/kernel/print.S b/c8/assert/lib/kernel/print.S new file mode 100755 index 0000000..52b9246 --- /dev/null +++ b/c8/assert/lib/kernel/print.S @@ -0,0 +1,217 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret diff --git a/c8/assert/lib/kernel/print.h b/c8/assert/lib/kernel/print.h new file mode 100755 index 0000000..903039e --- /dev/null +++ b/c8/assert/lib/kernel/print.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +#endif + diff --git a/c8/assert/lib/stdint.h b/c8/assert/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c8/assert/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c8/assert/lib/string.c b/c8/assert/lib/string.c new file mode 100755 index 0000000..0eda36c --- /dev/null +++ b/c8/assert/lib/string.c @@ -0,0 +1,121 @@ +#include "string.h" +#include "kernel/global.h" +#include "kernel/debug.h" + +/* 进行初始化 将dst_起始的size个字节置为value */ +// 通常用于内存分配时的数据清 0 +void memset(void* dst_, uint8_t value, uint32_t size) { + ASSERT(dst_ != NULL); + uint8_t* dst = (uint8_t*)dst_; + while (size-- > 0){ + *dst++ = value; + } +} + +/* 将src_起始的size个字节复制到dst_ */ +void memcpy(void* dst_, const void* src_, uint32_t size) { + ASSERT(dst_ != NULL && src_ != NULL); + uint8_t* dst = dst_; + const uint8_t* src = src_; + while(size-- > 0) { + *dst++ = *src++; + } +} + +/* 连续比较以地址a_和地址b_开头的size个字节, + 若相等则返回0,若a_大于b_返回+1,否则返回-1 */ +int memcmp(const void* a_, const void* b_, uint32_t size) { + const char* a = a_; + const char* b = b_; + ASSERT(a != NULL && b != NULL); + while(size-- > 0) { + if(*a != *b) { + return *a > *b ? 1 : -1; + } + a++; + b++; + } + // 至此说明相等 + return 0; +} + +/* 将字符串从src_复制到dst_ */ +char* strcpy(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* temp = dst_; // 暂存地址 + // 以 字符串结尾 '0' 作为终止条件 + while((*dst_++ = *src_++)); + return temp; +} + + +/* 返回字符串长度 */ +uint32_t strlen(const char* str) { + ASSERT(str != NULL); + const char* end = str;// 标记结尾处位置 + // 和strcpy 函数一样,遇到 '0'停止 + while(*end++); + return (end - str - 1); +} +/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */ +int8_t strcmp (const char* a, const char* b) { + ASSERT(a != NULL && b != NULL); + while(*a != 0 && *a == *b) { + a++; + b++; + } + // 如果 < 就返回 -1 + // 如果 >= 就前往 布尔式 + // 该布尔式表示,要是 > 就 返回 1, 否则只可能是 = ,布尔值刚好是 0 + return *a < *b ? -1 : *a > *b; +} + +/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + while(*str != 0) { + if(*str == ch) { + return (char*) str;// 强制转换 + } + str++; + } + return NULL; +} + +/* 从右往左查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strrchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + const char* last = NULL; + // 从左往右遍历,将 last 更新即可,这样便不用手动判断 '0' + while(*str != 0) { + if(*str == ch) { + last = str; + } + str++; + } + return (char*)last; +} + +/* 将字符串src_拼接到dst_后,将回拼接的串地址 */ +char* strcat(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* str = dst_; + while(*str++);// 循环到末尾 + --str;//得到拼接位置 + while((*str++ = *src_++)); + return dst_; +} + +/* 在字符串str中查找指定字符ch出现的次数 */ +uint32_t strchrs(const char* str, uint8_t ch) { + ASSERT(str != NULL); + uint32_t cnt = 0; + const char* temp = str; + while(*temp != 0) { + if(*temp == ch) { + cnt++; + } + temp++; + } + return cnt; +} \ No newline at end of file diff --git a/c8/assert/lib/string.h b/c8/assert/lib/string.h new file mode 100755 index 0000000..e69de29 diff --git a/c8/assert/makefile b/c8/assert/makefile new file mode 100755 index 0000000..ebbc06c --- /dev/null +++ b/c8/assert/makefile @@ -0,0 +1,79 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: boot/mbr.S + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: boot/loader.S + $(AS) $(ASIB) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean tran all + +mk_dir: + #if [[ ! -d $(BUILD_DIR) ]];then mkdir $(BUILD_DIR);fi + mkdir $(BUILD_DIR); +hd: + # echo ".....Writing mbr, loader and kernel to disk....." + dd if=$(BUILD_DIR)/mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +clean: + cd $(BUILD_DIR) && rm -f ./* + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + # echo ".....build kernel now....." + +all: mk_dir build hd tran + +tran: + cd /home/husharp/bochs_hu/bochs/ && bin/bochs -f bochsrc.disk \ No newline at end of file diff --git a/c8/assert/makefile_2 b/c8/assert/makefile_2 new file mode 100755 index 0000000..8cb9fa7 --- /dev/null +++ b/c8/assert/makefile_2 @@ -0,0 +1,64 @@ +BUILD_DIR = build +ENTRY_POINT = 0xc0001500 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I kernel/ -I device/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o $(BUILD_DIR)/debug.o + +# C代码编译 +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h lib/stdint.h kernel/global.h kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: mbr.asm + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: loader.asm + $(AS) $(ASIB) $< -o $@ + +# 编译汇编 +$(BUILD_DIR)/kernel.o: kernel/kernel.asm + $(AS) $(ASFLAGS) $< -o $@ + +$(BUILD_DIR)/print.o: lib/kernel/print.asm + $(AS) $(ASFLAGS) $< -o $@ + +# 链接 +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY: mk_dir hd clean all + +mk_dir: + if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi + bximage -hd -mode=flat -size=10 -q disk.img + echo "Create image done." + +hd: + dd if=$(BUILD_DIR)/mbr.bin of=disk.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=disk.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=disk.img bs=512 count=200 seek=9 conv=notrunc + +clean: + rm -rf disk.img $(BUILD_DIR) + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + +all: mk_dir build hd \ No newline at end of file diff --git a/c8/memory_manager/boot/loader.S b/c8/memory_manager/boot/loader.S new file mode 100755 index 0000000..d247cf5 --- /dev/null +++ b/c8/memory_manager/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c8/memory_manager/boot/mbr.S b/c8/memory_manager/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c8/memory_manager/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c8/memory_manager/build.sh b/c8/memory_manager/build.sh new file mode 100755 index 0000000..c979273 --- /dev/null +++ b/c8/memory_manager/build.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# echo "Creating disk.img..." +# bximage -mode=create -hd=10M -q disk.img +rm -rf build +echo "Mkdir build..." +mkdir build + +# 构建一个 build 目录用于放各种生成文件 +echo "Compiling..." +nasm -I include/ -o build/mbr.bin boot/mbr.S +nasm -I include/ -o build/loader.bin boot/loader.S +# 编译 print.S 和 kernel.S +nasm -f elf -o build/print.o lib/kernel/print.S +nasm -f elf -o build/kernel.o kernel/kernel.S +# 编译 timer.c main.c interrupt.c init.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/timer.o device/timer.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/main.o kernel/main.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/interrupt.o kernel/interrupt.c +gcc -I lib/ -I kernel/ -m32 -c -fno-builtin -o build/init.o kernel/init.c +#链接 +ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o build/timer.o + +echo "Writing mbr, loader and kernel to disk..." +dd if=build/mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc +dd if=build/loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc +dd if=build/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +echo "Now start bochs and have fun!" + +cd /home/ahu/install/bochs/ +bin/bochs -f bochsrc.disk + diff --git a/c8/memory_manager/device/timer.c b/c8/memory_manager/device/timer.c new file mode 100755 index 0000000..af7221c --- /dev/null +++ b/c8/memory_manager/device/timer.c @@ -0,0 +1,39 @@ +#include "time.h" +#include "io.h" +#include "kernel/print.h" + + +#define IRQ0_FREQUENCY 100 // 时钟频率 +#define INPUT_FREQUENCY 1193180 // 计数器原本的工作频率 +#define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY //计数器 0 初始值 +#define COUNTRER0_PORT 0x40 // 计数器 0 的接口 +#define COUNTER0_NO 0 // 计数器号码 +#define COUNTER_MODE 2 // 模式 2 +#define READ_WRITE_LATCH 3 // 读写方式,先读写低 8 位,再读写高 8 位 +#define PIT_CONTROL_PORT 0x43 // 控制器寄存器接口 + +/* + * 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode + * 写入模式控制寄存器 + * 并赋予初始值counter_value + * */ +static void frequency_set(uint8_t counter_port, \ + uint8_t counter_no, \ + uint8_t rwl, \ + uint8_t counter_mode, \ + uint16_t counter_value) { +/* 往控制字寄存器端口0x43中写入控制字 */ +// 最后一位为 0 ,即选择二进制 + outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1)); +/* 先写入counter_value的低8位 */ + outb(counter_port, (uint8_t)counter_value); +/* 后写入counter_value的高8位 */ + outb(counter_port, (uint8_t)counter_port >> 8); +} + +// 初始化 8253 +void timer_init() { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + put_str("timer_init done!\n"); +} diff --git a/c8/memory_manager/device/timer.h b/c8/memory_manager/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c8/memory_manager/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c8/memory_manager/include/boot.inc b/c8/memory_manager/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c8/memory_manager/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c8/memory_manager/kernel/debug.c b/c8/memory_manager/kernel/debug.c new file mode 100755 index 0000000..bef6acf --- /dev/null +++ b/c8/memory_manager/kernel/debug.c @@ -0,0 +1,18 @@ +#include "debug.h" +#include "kernel/print.h" +#include "interrupt.h" + +/* 打印文件名,行号,函数名,条件并使程序悬停 */ +void panic_spin(char* filename, \ + int line, \ + const char* func, \ + const char* condition) \ +{ + intr_disable(); /* 打印文件名,行号,函数名,条件并使程序悬停 */ + put_str("\n\n\n!!!!! error !!!!!\n"); + put_str("filename:");put_str(filename);put_str("\n"); + put_str("line:0x");put_int(line);put_str("\n"); + put_str("function:");put_str((char*)func);put_str("\n"); + put_str("condition:");put_str((char*)condition);put_str("\n"); + while(1); +} \ No newline at end of file diff --git a/c8/memory_manager/kernel/debug.h b/c8/memory_manager/kernel/debug.h new file mode 100755 index 0000000..d4df982 --- /dev/null +++ b/c8/memory_manager/kernel/debug.h @@ -0,0 +1,24 @@ +#ifndef __KERNEL_DEBUG_H +#define __KERNEL_DEBUG_H + +void panic_spin(char* filename, int line, const char* func, const char* condition); + +/*************************** __VA_ARGS__ ******************************* + * __VA_ARGS__ 是预处理器所支持的专用标识符。 + * 代表所有与省略号相对应的参数. + * "..."表示定义的宏其参数可变. + */ +#define PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) +/***********************************************************************/ + +#ifdef NDEBUG + #define ASSERT(CONDITION) ((void)0) +#else + #define ASSERT(CONDITION) \ + if (CONDITION) {} else { \ + /* 符号#让编译器将宏的参数转化为字符串字面量 */ \ + PANIC(#CONDITION); \ + } +#endif // __NDEBUG + +#endif // __KERNEL_DEBUG_H \ No newline at end of file diff --git a/c8/memory_manager/kernel/global.h b/c8/memory_manager/kernel/global.h new file mode 100755 index 0000000..7330902 --- /dev/null +++ b/c8/memory_manager/kernel/global.h @@ -0,0 +1,39 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#define NULL ((void*)0) +#define bool int +#define true 1 +#define false 0 + +#endif \ No newline at end of file diff --git a/c8/memory_manager/kernel/init.c b/c8/memory_manager/kernel/init.c new file mode 100755 index 0000000..995b338 --- /dev/null +++ b/c8/memory_manager/kernel/init.c @@ -0,0 +1,13 @@ +#include "init.h" +#include "kernel/print.h" +#include "interrupt.h" +#include "../device/timer.h" +#include "memory.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init(); + timer_init(); + mem_init();//初始化内存管理系统 +} \ No newline at end of file diff --git a/c8/memory_manager/kernel/init.h b/c8/memory_manager/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c8/memory_manager/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c8/memory_manager/kernel/interrupt.c b/c8/memory_manager/kernel/interrupt.c new file mode 100755 index 0000000..882dbac --- /dev/null +++ b/c8/memory_manager/kernel/interrupt.c @@ -0,0 +1,179 @@ +# include "stdint.h" +# include "global.h" +# include "io.h" +# include "interrupt.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define EFLAGS_IF 0x00000200 // eflags 寄存器的 if 位为 1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + put_str("int vector: 0x"); + put_int(vec_nr); + put_char('\n'); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void +exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断, sti 指令将 IF 位 置 1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli 指令将 IF 位 置 0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + // 判断 IF 位的值是否为 1 + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} \ No newline at end of file diff --git a/c8/memory_manager/kernel/interrupt.h b/c8/memory_manager/kernel/interrupt.h new file mode 100755 index 0000000..f2882d6 --- /dev/null +++ b/c8/memory_manager/kernel/interrupt.h @@ -0,0 +1,21 @@ +#ifndef __KERNEL_INTERRUPT_H +#define __KERNEL_INTERRUPT_H +#include "stdint.h" + +typedef void* intr_handler; +void idt_init(void); + +/* 定义中断的两种状态: + * INTR_OFF值为0,表示关中断, + * INTR_ON值为1,表示开中断 */ +enum intr_status { // 中断状态 + INTR_OFF, // 中断关闭 + INTR_ON // 中断打开 +}; + +enum intr_status intr_get_status(void); +enum intr_status intr_set_status (enum intr_status); +enum intr_status intr_enable (void); +enum intr_status intr_disable (void); + +#endif \ No newline at end of file diff --git a/c8/memory_manager/kernel/kernel.S b/c8/memory_manager/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c8/memory_manager/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c8/memory_manager/kernel/main.c b/c8/memory_manager/kernel/main.c new file mode 100755 index 0000000..7949f47 --- /dev/null +++ b/c8/memory_manager/kernel/main.c @@ -0,0 +1,20 @@ +#include "kernel/print.h" +#include "init.h" +#include "debug.h" +#include "memory.h" + +int main(void) { + put_str("I am kernel!\n"); + init_all(); + //ASSERT(1 == 2); + // asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + + // 进行内存分配 + void* addr = get_kernel_pages(3); + put_str("\n get_kernel_page start vaddr is: "); + put_int((uint32_t) addr); + put_str("\n"); + + while(1); + return 0; +} \ No newline at end of file diff --git a/c8/memory_manager/kernel/memory.c b/c8/memory_manager/kernel/memory.c new file mode 100755 index 0000000..afbb653 --- /dev/null +++ b/c8/memory_manager/kernel/memory.c @@ -0,0 +1,257 @@ +#include "memory.h" +#include "stdint.h" +#include "kernel/print.h" +#include "kernel/bitmap.h" +#include "global.h" +#include "debug.h" +#include "kernel/print.h" +#include "string.h" + +#define PG_SIZE 4096 + +/*************** 位图地址 ******************** + * 因为0xc009f000是内核主线程栈顶,0xc009e000是内核主线程的pcb. + * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000, + * 这样本系统最大支持4个页框的位图,即512M */ +#define MEM_BITMAP_BASE 0xc009a000 + +// 返回虚拟地址的 高 10 位(即pde),与中间 10 位(即pte) +#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) +#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) + +/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存, + * 使虚拟地址在逻辑上连续 */ +#define K_HEAP_START 0xc0100000 + +// 内存池结构,生成两个实例用于管理内核内存池和用户内存池 +struct pool { + // 本内存池用到的位图结构,用于管理物理内存 + struct bitmap pool_bitmap; + uint32_t phy_addr_start;// 本内存池所管理物理内存起始地址 + uint32_t pool_size; //本内存池字节容量 +}; + +struct pool kernel_pool, user_pool; // 生成全局内核内存池, 用户内存地址 +struct virtual_addr kernel_addr; // 给内核分配虚拟地址 + + +/* 在pf表示的虚拟内存池中申请pg_cnt个虚拟页, + * 成功则返回虚拟页的起始地址, 失败则返回NULL + */ +static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) { + int vaddr_start = 0;//用于存储分配的虚拟地址 + int bit_idx_start = -1;// 存储位图扫描函数的返回值 + uint32_t cnt = 0; + if(pf == PF_KERNEL) {// 说明在内核虚拟池中 + bit_idx_start = bitmap_scan(&kernel_addr.vaddr_bitmap, pg_cnt); + if(bit_idx_start == -1) {//说明没找到 + return NULL; + } + // 将已经选出的内存置为 1 ,表示已经使用 + while(cnt < pg_cnt) { + bitmap_set(&kernel_addr.vaddr_bitmap, bit_idx_start + cnt++, 1); + } + // 虚拟地址应当加上 页表所占的内存 + vaddr_start = kernel_addr.vaddr_start + bit_idx_start * PG_SIZE; + } else {// 用户进程池 还未完善 + + } + return (void*)vaddr_start; +} + +/********* 以下两个获取虚拟地址 addr 的 pde & pte 地址,为建立映射做准备 ********/ +// 得到虚拟地址 vaddr 对应的 pte 指针 +// 构造一个 访问该 pte 的32位地址,欺骗处理器 +uint32_t* pte_ptr(uint32_t vaddr) { + /* 先访问到页目录表自己(位于1023项目录项中) + \ + * 再用页目录项 pde (页目录内页表的索引)做为pte的索引访问到页表 + \ + * 再用 pte 的索引做为页内偏移 + */ + uint32_t* pte = (uint32_t*) (0xffc00000 + \ + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4); + return pte;// 得到物理页的虚拟地址,即通过这个虚拟地址,可以访问到该物理页(保护模式下必须通过 vaddr) +} + +// 得到虚拟地址 vaddr 对应的 pde 指针 +// 构造一个 访问该 pde 的32位地址,欺骗处理器 +uint32_t* pde_ptr(uint32_t vaddr) { + uint32_t* pde = (uint32_t*) (0xfffff000 + PDE_IDX(vaddr) * 4); + return pde; +} + +/* 在m_pool指向的物理内存池中分配1个物理页, + * 成功则返回页框的物理地址,失败则返回NULL + */ +static void* palloc(struct pool* m_pool) { + // 扫描或设置位图要保证原子操作 + int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); //找一个空闲物理页面 + if(bit_idx == -1) { + return NULL; + } + bitmap_set(&m_pool->pool_bitmap, bit_idx, 1); //表示已经占用 + // 成功则返回页框的物理地址 + uint32_t page_phyaddr = (m_pool->phy_addr_start + (bit_idx * PG_SIZE)); + return (void*)page_phyaddr; +} + +/********使用 pde_ptr 和 pte_ptr 来建立虚拟地址和物理地址的映射 ********/ +// 本质上便是将 pte 写入到 获取的 pde 项中,将 物理地址写入到 pte 中 +static void page_table_add(void* _vaddr, void* _page_phyaddr) { + uint32_t vaddr = (uint32_t)_vaddr; + uint32_t page_phyaddr = (uint32_t)_page_phyaddr; + // 以下两个函数都是通过 虚拟地址来获取 + uint32_t* pde = pde_ptr(vaddr); + uint32_t* pte = pte_ptr(vaddr); + +/************************ 注意 ************************* + * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte, + * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。 + * *********************************************************/ + // 先在页目录内判断目录项的P位,若为1,则表示该表(pte)已存在 + if(*pde & 0x00000001) {// 通过 P 位来判断目录项是否存在 + ASSERT(!(*pte & 0x00000001)); // 表示 pte 不存在 + + // 再判断 pte 是否存在 + if(!(*pte & 0x00000001)) { + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } else { // 并不会执行到此处,因为此处意思是:pde存在,pte也存在 + // 但是之前的 ASSERT 已经判断了 pde 不存在 + PANIC("pte repeat!"); + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } + } else {// pde 不存在 + // 需要分配 pte 物理页,采用 plloc 函数 + uint32_t new_pde_phyaddr = (uint32_t)palloc(&kernel_pool);//页表中的页框一律从内核中分配 + + *pde = (new_pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);// 写入到 pde 中 + + /* 分配到的物理页地址 new_pde_phyaddr 对应的物理内存清0,(使用时清零比回收后清零高效,因为不知道回收后会不会再使用) + * 避免里面的陈旧数据变成了页表项,从而让页表混乱. + * 访问到pde对应的物理地址,用pte取高20位便可. + * 因为pte是基于该pde对应的物理地址内再寻址, + * 把低12位置0便是该pde对应的物理页的起始 + */ + // 现需要获得新创建的这个物理页的虚拟地址(保护模式得看虚拟地址!) + memset(((void*)((int)pte & 0xfffff000)), 0, PG_SIZE);//将该新配物理页清0 + + ASSERT(!(*pte & 0x00000001));// 断言 pte 此时是否存在 + + // pte项 此时更改为新建物理页的位置 + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);//将 物理页地址 写入到 pte项中 + } +} + + +// 分配pg_cnt个页空间(物理页),成功则返回起始虚拟地址,失败时返回NULL +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) { + ASSERT(pg_cnt > 0 && pg_cnt < 3840); +/*********** malloc_page的原理是三个动作的合成: *********** + 1.通过vaddr_get在虚拟内存池中申请虚拟地址 + 2.通过palloc在物理内存池中申请物理页 + 3.通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射 +***************************************************************/ + void* vaddr_start = vaddr_get(pf, pg_cnt); + if(vaddr_start == NULL) { + return NULL; + } + + uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt; + struct pool* mem_pool = (pf & PF_KERNEL)? &kernel_pool : &user_pool; + + // 因为虚拟地址是连续的,但物理地址可以是不连续的,所以逐个做映射 + while(cnt-- > 0) { + void* page_phyaddr = palloc(mem_pool); + if(page_phyaddr == NULL) { + return NULL; + } + page_table_add((void*)vaddr, page_phyaddr);// 在页表建立映射 + vaddr += PG_SIZE;// 下一个虚拟页 + } + return vaddr_start; +} + +// 从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL +void* get_kernel_pages(uint32_t pg_cnt) { + void* vaddr = malloc_page(PF_KERNEL, pg_cnt); + if(vaddr != NULL) {// 虚拟地址是连续的 + memset(vaddr, 0, pg_cnt * PG_SIZE); + } + return vaddr; +} + +// 初始化内存池, 通过all_mem 初始化物理内存池相关结构 +static void mem_pool_init(uint32_t all_mem) { + put_str(" phy_mem_pool_init start!\n"); + // 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+ + // 第769~1022个页目录项共指向254个页表,共256个页框 + uint32_t page_table_size = PG_SIZE * 256; + // 用于记录当前已经使用的内存字节数 + uint32_t used_mem = page_table_size + 0x100000;// 0x100000为低端 1M 内存 + uint32_t free_mem = all_mem - used_mem; + uint16_t all_free_pages = free_mem / PG_SIZE;//转换为 页数 + + // 内核物理内存池 与 用户物理内存池 大小一致 + uint16_t kernel_free_pages = all_free_pages / 2; + uint16_t user_free_pages = all_free_pages - kernel_free_pages; + + /* 为简化位图操作,余数不处理,坏处是这样做会丢内存。 + * 好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存 + */ + uint32_t kbm_length = kernel_free_pages / 8; // Kernel BitMap的长度,位图中的一位表示一页,以字节为单位 + uint32_t ubm_length = user_free_pages / 8; // User BitMap的长度. + + uint32_t kp_start = used_mem; // kernel pool start 内核内存池的起始位置 + uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; + + kernel_pool.phy_addr_start = kp_start; + user_pool.phy_addr_start = up_start; + + kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;// 位图字节长度 + user_pool.pool_bitmap.btmp_bytes_len = ubm_length; + + kernel_pool.pool_size = kernel_free_pages * PG_SIZE; + user_pool.pool_size = user_free_pages * PG_SIZE; + +/********* 内核内存池和用户内存池位图 *********** + * 位图是全局的数据,长度不固定。 + * 全局或静态的数组需要在编译时知道其长度, + * 而我们需要根据总内存大小算出需要多少字节。 + * 所以改为指定一块内存来生成位图. + * ************************************************/ +// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右) +// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处. + kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE; + + // 用户内存池的位图紧跟在内核内存池位图后面 + user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length); + /******************** 输出内存池信息 **********************/ + put_str(" kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits); + put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start); + put_str("\n"); + put_str(" user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits); + put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start); + put_str("\n"); + + /**************** 进行初始化 ***************/ + // 将位图 置为 0 表示还未分配 + bitmap_init(&kernel_pool.pool_bitmap); + bitmap_init(&user_pool.pool_bitmap); + + // 初始化内核的虚拟地址 + // 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致 + kernel_addr.vaddr_bitmap.btmp_bytes_len = kbm_length; + // 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之后 + kernel_addr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); + // 虚拟内存池的起始地址,以达到进程在堆中动态申请内存 + kernel_addr.vaddr_start = K_HEAP_START; //内核堆 + bitmap_init(&kernel_addr.vaddr_bitmap); + put_str(" mem_pool_init done\n"); +} + +// 内存管理部分初始化入口 +void mem_init() { + put_str("mem_init start!\n"); + uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); // 储存机器上物理内存总量 + mem_pool_init(mem_bytes_total); //初始化内存池 + put_str("mem_init done!\n"); +} \ No newline at end of file diff --git a/c8/memory_manager/kernel/memory.h b/c8/memory_manager/kernel/memory.h new file mode 100755 index 0000000..405f8d7 --- /dev/null +++ b/c8/memory_manager/kernel/memory.h @@ -0,0 +1,33 @@ +#ifndef __KERNEL_MEMORY_H +#define __KERNEL_MEMORY_H + +#include "stdint.h" +#include "bitmap.h" + +// 内存池的标记,用来判断使用哪个内存池 +enum pool_flags { + PF_KERNEL = 1, // 内核内存池 + PF_USER = 2 // 用户内存池 +}; + +#define PG_P_1 1 // 页表项或页目录项存在属性位 +#define PG_P_0 0 // 页表项或页目录项存在属性位 +#define PG_RW_R 0 // R/W 属性位值, 读/执行 +#define PG_RW_W 2 // R/W 属性位值, 读/写/执行 +#define PG_US_S 0 // U/S 属性位值, 系统级 +#define PG_US_U 4 // U/S 属性位值, 用户级 + +// 虚拟地址池,用于虚拟地址管理 +struct virtual_addr{ + struct bitmap vaddr_bitmap; // 虚拟地址用到的 bitmap 结构, 页 为单位 + uint32_t vaddr_start; // 虚拟地址起始地址 +}; + +extern struct pool kernel_pool, user_pool; +void mem_init(void); +void* get_kernel_pages(uint32_t pg_cnt); +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt); +void malloc_init(void); +uint32_t* pte_ptr(uint32_t vaddr); +uint32_t* pde_ptr(uint32_t vaddr); +#endif \ No newline at end of file diff --git a/c8/memory_manager/lib/kernel/bitmap.c b/c8/memory_manager/lib/kernel/bitmap.c new file mode 100755 index 0000000..323b793 --- /dev/null +++ b/c8/memory_manager/lib/kernel/bitmap.c @@ -0,0 +1,82 @@ +#include "bitmap.h" +#include "stdint.h" +#include "string.h" +#include "print.h" +#include "interrupt.h" +#include "debug.h" + +// bitmap 的初始化 +void bitmap_init(struct bitmap* btmp) { + memset(btmp->bits, 0, btmp->btmp_bytes_len); +} + +// 判断 btmp 为指针处,bit_idx 位是否为 1,为 1 就返回 true +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) { + uint32_t byte_idx = bit_idx / 8;//用于标注字节 + uint32_t bit_odd = bit_idx % 8; //用于标注字节内的位 + return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd)); +} + +// 在位图中申请连续cnt个位,成功则返回其起始位下标,失败返回-1 +int bitmap_scan(struct bitmap* btmp, uint32_t cnt) { + uint32_t idx_byte = 0; // 用于记录空闲位所在的字节 + // 先逐字节比较,蛮力法 + // 0 表示空闲,若停止,则说明至少有一个空闲位 + while(( 0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) { + idx_byte++; + } + ASSERT(idx_byte < btmp->btmp_bytes_len);// 断言此时在内部 + if(idx_byte == btmp->btmp_bytes_len) { + return -1;// 访问失败,返回 -1 + } + + /* 若在位图数组范围内的某字节内找到了空闲位, + * 在该字节内逐位比对,返回空闲位的索引。 + */ + int idx_bit = 0;//字节内部位数 + while((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) { + idx_bit++; + }// 找到空闲位 + int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在bitmap 下标 + if(cnt == 1) { + return bit_idx_start; + } + + // 至此说明 cnt > 1 + // 因此首先进行剩余位数的判断,以免超过bitmap记录数 + uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start); + uint32_t idx_next_bit = bit_idx_start + 1; + uint32_t free_bit_cnt = 1;// 一定要记得记录一下 之前判断后 还有个空闲位 + + bit_idx_start = -1; // 先将其置为 -1,若找不到连续的位就直接返回 + while(bit_left-- > 0) { + // 调用 scan_test 函数,为 0 则为 false + if(!(bitmap_scan_test(btmp, idx_next_bit))) { + free_bit_cnt++; + } else { // 由于必须是连续的,因此只要遇到 不空闲的 位 便将记录cnt置为 0 + free_bit_cnt = 0; + } + // 若是已经满足空闲数 + if(free_bit_cnt == cnt) { + bit_idx_start = idx_next_bit - cnt + 1; + break; + } + idx_next_bit++;// 继续判断下一位 + } + + return bit_idx_start; +} + +// 将位图btmp的bit_idx位设置为value +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) { + ASSERT((value ==0) || (value == 1));// 只能赋予 0 或 1 + uint32_t byte_idx = bit_idx / 8; //字节位 + uint32_t bit_odd = bit_idx % 8; //位数 位 + + if(value) { + btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); + } else { + btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); + } + +} diff --git a/c8/memory_manager/lib/kernel/bitmap.h b/c8/memory_manager/lib/kernel/bitmap.h new file mode 100755 index 0000000..ab3d2db --- /dev/null +++ b/c8/memory_manager/lib/kernel/bitmap.h @@ -0,0 +1,18 @@ +#ifndef __LIB_KERNEL_BITMAP_H +#define __LIB_KERNEL_BITMAP_H +#include "global.h" +#define BITMAP_MASK 1 // 用在位图中逐位判断,主要通过 按位与 & +struct bitmap{ + uint32_t btmp_bytes_len; + /* 在遍历位图时,整体上以字节为单位,细节上是以位为单位, + 所以此处位图的指针必须是单字节 */ + uint8_t* bits; +}; + +// 函数 +void bitmap_init(struct bitmap* btmp); +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx); +int bitmap_scan(struct bitmap* btmp, uint32_t cnt); +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value); + +#endif \ No newline at end of file diff --git a/c8/memory_manager/lib/kernel/io.h b/c8/memory_manager/lib/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c8/memory_manager/lib/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c8/memory_manager/lib/kernel/print.S b/c8/memory_manager/lib/kernel/print.S new file mode 100755 index 0000000..52b9246 --- /dev/null +++ b/c8/memory_manager/lib/kernel/print.S @@ -0,0 +1,217 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret diff --git a/c8/memory_manager/lib/kernel/print.h b/c8/memory_manager/lib/kernel/print.h new file mode 100755 index 0000000..903039e --- /dev/null +++ b/c8/memory_manager/lib/kernel/print.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +#endif + diff --git a/c8/memory_manager/lib/stdint.h b/c8/memory_manager/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c8/memory_manager/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c8/memory_manager/lib/string.c b/c8/memory_manager/lib/string.c new file mode 100755 index 0000000..ab47322 --- /dev/null +++ b/c8/memory_manager/lib/string.c @@ -0,0 +1,121 @@ +#include "string.h" +#include "global.h" +#include "debug.h" + +/* 进行初始化 将dst_起始的size个字节置为value */ +// 通常用于内存分配时的数据清 0 +void memset(void* dst_, uint8_t value, uint32_t size) { + ASSERT(dst_ != NULL); + uint8_t* dst = (uint8_t*)dst_; + while (size-- > 0){ + *dst++ = value; + } +} + +/* 将src_起始的size个字节复制到dst_ */ +void memcpy(void* dst_, const void* src_, uint32_t size) { + ASSERT(dst_ != NULL && src_ != NULL); + uint8_t* dst = dst_; + const uint8_t* src = src_; + while(size-- > 0) { + *dst++ = *src++; + } +} + +/* 连续比较以地址a_和地址b_开头的size个字节, + 若相等则返回0,若a_大于b_返回+1,否则返回-1 */ +int memcmp(const void* a_, const void* b_, uint32_t size) { + const char* a = a_; + const char* b = b_; + ASSERT(a != NULL && b != NULL); + while(size-- > 0) { + if(*a != *b) { + return *a > *b ? 1 : -1; + } + a++; + b++; + } + // 至此说明相等 + return 0; +} + +/* 将字符串从src_复制到dst_ */ +char* strcpy(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* temp = dst_; // 暂存地址 + // 以 字符串结尾 '0' 作为终止条件 + while((*dst_++ = *src_++)); + return temp; +} + + +/* 返回字符串长度 */ +uint32_t strlen(const char* str) { + ASSERT(str != NULL); + const char* end = str;// 标记结尾处位置 + // 和strcpy 函数一样,遇到 '0'停止 + while(*end++); + return (end - str - 1); +} +/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */ +int8_t strcmp (const char* a, const char* b) { + ASSERT(a != NULL && b != NULL); + while(*a != 0 && *a == *b) { + a++; + b++; + } + // 如果 < 就返回 -1 + // 如果 >= 就前往 布尔式 + // 该布尔式表示,要是 > 就 返回 1, 否则只可能是 = ,布尔值刚好是 0 + return *a < *b ? -1 : *a > *b; +} + +/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + while(*str != 0) { + if(*str == ch) { + return (char*) str;// 强制转换 + } + str++; + } + return NULL; +} + +/* 从右往左查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strrchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + const char* last = NULL; + // 从左往右遍历,将 last 更新即可,这样便不用手动判断 '0' + while(*str != 0) { + if(*str == ch) { + last = str; + } + str++; + } + return (char*)last; +} + +/* 将字符串src_拼接到dst_后,将回拼接的串地址 */ +char* strcat(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* str = dst_; + while(*str++);// 循环到末尾 + --str;//得到拼接位置 + while((*str++ = *src_++)); + return dst_; +} + +/* 在字符串str中查找指定字符ch出现的次数 */ +uint32_t strchrs(const char* str, uint8_t ch) { + ASSERT(str != NULL); + uint32_t cnt = 0; + const char* temp = str; + while(*temp != 0) { + if(*temp == ch) { + cnt++; + } + temp++; + } + return cnt; +} \ No newline at end of file diff --git a/c8/memory_manager/lib/string.h b/c8/memory_manager/lib/string.h new file mode 100755 index 0000000..e69de29 diff --git a/c8/memory_manager/makefile b/c8/memory_manager/makefile new file mode 100755 index 0000000..2e525d0 --- /dev/null +++ b/c8/memory_manager/makefile @@ -0,0 +1,94 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o \ + $(BUILD_DIR)/string.o + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/string.o: lib/string.c lib/string.h lib/stdint.h kernel/global.h \ + lib/stdint.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ + kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ + lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h \ + kernel/global.h kernel/global.h kernel/debug.h lib/kernel/print.h \ + lib/kernel/io.h kernel/interrupt.h lib/string.h lib/stdint.h + $(CC) $(CFLAGS) $< -o $@ + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: boot/mbr.S + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: boot/loader.S + $(AS) $(ASIB) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean tran all + +mk_dir: + if [[ ! -d $(BUILD_DIR) ]];then mkdir $(BUILD_DIR);fi + +hd: + # echo ".....Writing mbr, loader and kernel to disk....." + dd if=$(BUILD_DIR)/mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +clean: + cd $(BUILD_DIR) && rm -f ./* + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + # echo ".....build kernel now....." + +all: mk_dir build hd tran + +tran: + cd /home/ahu/install/bochs/ && bin/bochs -f bochsrc.disk \ No newline at end of file diff --git a/c8/temp b/c8/temp new file mode 100755 index 0000000..6271fbe --- /dev/null +++ b/c8/temp @@ -0,0 +1,87 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o \ + $(BUILD_DIR)/string.o + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/string.o: lib/string.c lib/string.h lib/stdint.h kernel/global.h \ + lib/stdint.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ + kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ + lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h \ + kernel/global.h kernel/global.h kernel/debug.h lib/kernel/print.h \ + lib/kernel/io.h kernel/interrupt.h lib/string.h lib/stdint.h + $(CC) $(CFLAGS) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean all tran + +mk_dir: + if [[ ! -d $(BUILD_DIR) ]];then mkdir $(BUILD_DIR);fi + +hd: + dd if=$(BUILD_DIR)/kernel.bin \ + of=/home/work/my_workspace/bochs/hd60M.img \ + bs=512 count=200 seek=9 conv=notrunc + +clean: + cd $(BUILD_DIR) && rm -f ./* + +build: $(BUILD_DIR)/kernel.bin + +all: mk_dir build hd tran + +tran: + cd /home/ahu/install/bochs/ && bin/bochs -f bochsrc.disk + + + dd if=$(BUILD_DIR)/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc diff --git a/c9/thread_schedule/boot/loader.S b/c9/thread_schedule/boot/loader.S new file mode 100755 index 0000000..d247cf5 --- /dev/null +++ b/c9/thread_schedule/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c9/thread_schedule/boot/mbr.S b/c9/thread_schedule/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c9/thread_schedule/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c9/thread_schedule/build/bitmap.o b/c9/thread_schedule/build/bitmap.o new file mode 100644 index 0000000000000000000000000000000000000000..9c10850e8482eac4625d2c6e0e01426133f49b6b GIT binary patch literal 2460 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 RX-`>saVWk{4zeiTe*y2rtIGfY literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/kernel.bin b/c9/thread_schedule/build/kernel.bin new file mode 100755 index 0000000000000000000000000000000000000000..fd2e6d459ce0adce95cd9600839cef03c3f4e91f GIT binary patch literal 22328 zcmeHvdwf*Ywf~-Ek_;i50TT_1aFB>WBqZS#o`#U&VL?V9)?OdOB$>&BBr}}{1_c`K zpk|yqn%kd&@FeV|nvG1{Y((r8l~DfQg%+WYJ|XU6Eg z|J={#{=wmM=A7?dYp=ET+G{`04&QOCTV=6Wn7p!BHsh$6CFUWqyIxS{F$I(|HlF1R zIwjj#g6^xT8KXN#qzQTrz!uPP-;LMmY{qhR`V}99n&>t2%0q%ZGx0P&?Sa!CIPHP| zArHjYm0BZ%!`in`)kiUB*~ss30C0c|+5j>cis!58Ks$2QePWI)=1( zr017-v`vji@(8Qbrf{Bk4tw+v zT9@Zo%SJWve2tYJh5J!@XAYPSs*=x{oAN=Cdeax=>YqYfzheZJuw-R_q5xxH ztbQbB?Zt1r;5AB*e{U~-EiXA+Zo_k<+R2SAh&VzOV;yoio$d@SRO`yxH5>DdKCgDXK7D!mbp$gt!cq<^B&^|Zo`h}=t0mmQ zVU2|MaabqeQyi|*VfQhDwd;D(w_Sa$Xz(TKCWJ!H#}2f=?eKGC)DkX0=pYddU?e$S z52L>W=zTWydv$sF}z zJao}^^*Q3Z27LXFQ+Q?fUKOQAdg`D$4%u`ztNKG?n@{5EZ9Vm;sGasFM0^xP3wlKh z{pOVjrOE1hC#j8&12Yf1lW>AlJ)FavgF5$=rm46513wr3UDM)`y%78)ZdUlfA^X7r zd-Rp)q3Dq_@=)UF^U)W&UuMz%Xm}vHbFk2I?En{;|9cV`6astWo{_7sb6wjPv+@p^ zy3|ilz?>fR^k3Jda$}3s=ZN(!q6!RV+r z>KH($Jalj%H+l#!>cIi)IC>ehB*q;n+@El^tyFhF82Z=31H_9&$M8m{+N>u69UMgu zC(}2G^i%=l$aDYaKhB$qx8Jt0DY=Dv3m@6CPx)}~he{jMiYe%lZ)OrKT5%H* z5}AZ7t+<5<4`&jxwc>Un+?GkOYQ=sKJ_?|IGHE$lafoPFjzP=Sio1xmcnn&eR=k5~ zQ^ug#wBl}}WsN}_rxouZ+J|s!rY-qeaSzd68iO`oEB+Uv{cH?cfmYm0wC|2VJ3}kJ zgJ|2vpq;4|-%YfC8iQ7-72iv=!(h(Q?P2P1K4XA=<|XpUkqK zr4|1N(S9=qtw<~0N3>szK`YjZA0ygbW6;jliXSK14P(&G(TblY+I3^lO0?nx(Q3z_ zP11@F6K%#Av~#uM7l@WW2JJkp_+_Gns@8c!xHr4y&tJ(X`JmG!vwd|pu!yFX~l)|Hlae||=2Y4_*(p@rR_+d^5% zv%TN&zVg1O`T0D>yOJJx-dB>zEj+EK&`9#G^zQ!qk-QzNyp>12(PQ3coXq>T&%85# z=Z*9^1!>l)Clcshp4rG6<4V5A;jUQ)q(`B9Z9J0!k49AZ-!iD z-1-+97k;q};@V1?0?tS(sfNRP!%n!LKA7WCi7?xET@)WF zyZ|G50ftdHx$5N0o26VqlTg45Sb&8DErmDns<*;{gvLvx#vDU2CZ>|t#~iAB$Asi% zs@!-)eVfL6e?0OYuk1QenFeckBCSeZ$RkIoQTs;^pbb_7ecc? zVQr*p{+$TNsADkZ7}SLh(d0%&Wl-y|w$NT@8*{2L>5`Pq7o^+V66KZ%v*YOJL@*ep znkPi1ss-|Q_sv|4pxIf#LgRMtEYQwmY#XHhLlkC|Jkjz!lhlPmst8qTljn*8iZEre zGW~FJsxneDcbh15GPggWBF3W*HMtP3(88+vvJ^8I**TaJGZXVTu4>S!ju(#FjrIe7 z@MfT)8QhczKTS2N2Q?46vOiP7oQqH}YElR~)yAtIt%+ieh0dXPA3zsjbz&}t@R(!F zIt@wl)OSyiq+Biuozf)ohk6hbNqCmGWIHW(kk^j(f!MUoaty;2FbE9@Q|*s2h5p$4 zii%g2zs_>d`-gZ}y$99o!UOQa0MLgi^frnXbsQrOSV%E;j9Tui!gsOyV^JskNdAk} zzslb!kYe=%QM*ijJ8Dm57ullD0#d>S>dtrSA=r-{i@oC#F~`7cw&k-+qlYBj%KPDJ zUgY15iX!u%jnxpzsXie5z}c?iY!9cip)rGPr-?1h*}4TA&v-pg>696Hxpet;ri^!T zw$ygGNz0r~0T!(fdg99$Xl6yiL8G?gr|IqfV<}>94`S zX-{woqJ#FOWX2==xOtDzaCX>kZe>O4575!??(17X!H5v*=bXC)=i~TH!eR&=xqY`s zGy~3gjB{@OvP`Rb44qz%4VuSJDP?gKAn>A& zQ5qr#@^~w?#hlg{wm`6?V_4WBq@SX7eDvV&-j5!eb_Bx2vY+?3SCgbq=ck6Uq?PuD z$cW3V=))~974DzdeNqc^yCyEG9|`<{i2Q9@yx01UrfrP(Zl*`uy};^sL_w*; zXNw#o;rz$ZWVMf8gdO@QNs5NC9vpsBH|}`!Nc13OAnQlC_0wL!dhi%fyy!XPL}`bq z^K+w65PF>42$+(2 zU==zBFy4mGL@~qFIB3Zh3+@!|IVXIv}0+G?JyR) z2)~vPyi_f`w1spIq7sLB_POcKz#KKrS>yVmZ<;uwFZI}ynOV@^ z44W$dZ%3l9Of&nKGMNLsRQ(2e+nnQbsT>6;2j(&?j1shq61>1mV2k%|rx(=oU;nC? zfOf5ug;Z5EpTSzbey|7Qo}GKl4v!6*^%SY^qc6_60eVP7D3``8(sQ%WbA;=eVCadP z^}tQ&pOfdr>WAaCs2_CIBk?ts=z6G~n6CBhbgdnv)yhj}gOw;F1|2?zz=C0J!B2z* z$G8Pk3=75!3z7?En*Rjs6o>8i-*Pl^(& zT*(4M$)t28M%9v7%vfoHN9`@5T1*P8s*M!siN+ff*+fC*asoT+I=h$Hmk7&#o5Yfw0r5@$x ze8Vtjx^B+7!i>UaXlHC>(IM(@Y{|38NI`)U-p;JZQgu9OyT2l{{pdbs?@~YcO+(u+ zv6MjJ^>sw@JhRe4D9uFz4Jc-nY))5n3mA4BazsErPYg$kcz^EC~hIR1H%#_Dsxpg#DKreU)gtAY|(gH=KMA81v8 zx$y$RYPm2;y&r=VA49RVS%glj9V#OU+Bn3rtcXwX9~N^Dk{9dZL^byIc7P3)Qt#3W+DIXL;JuB zGWK+Pj(nkE%hndfheN_^x(y)eDgNC_N8x8DJame>U~&as?ARH`OMguP8AwS^Xgq~k zF@L9DOyKxC{YLF#e;;*iRkPoxx}F6ifZD2Q8DLY(#9XpJF^}fdn1|SS5p)kE^I@0n zdb~vvm~_#S9ed;WRKTlAeC+EX`{XO?3z_IUEETuE2CYtin@2n0NXwxW4PSP{LQ^{_ zQm;EsM&5i0uWBq~&J|q^weKM-G{a_Ey_6vcp`ND|VH1PWdf`(MIgvz}2**+$jwPb@ z5RM4udqt#lQE<0JV3-V+%Q5banY~ENF25YA2|*BYkC^55nB;zju3pMbYb8%3CpJ=pr@s6F+Fc_wQ*c3=9$R{g zu*In@*r*j?4o)w&Uj7A-_jekU?e4n>nbm(n*T?%YG(g_M&|qva8cF#FZb~8mDUik6 z_K5l|ytG!>Qm6hdna@RwndIp;dl7ht_vwq=aP_@%D5*ut61dO)Rm&n(NOeFy}8^= z)N0{j-d%!*$-@YZaTr&ihHs6Lnvw8PLyh`dI3&lL(t+BE9I`DsQg}xKU-I-XwRZL2 zh^r>W1*7H1@iS7zpfBL4alI%iR)a70tySC|%2rRH)6+gJpWV>3U13{Yp_YKh-Q-#m z-qhj__+6E(rLAR*^eVRA-P)_klgj#)_N{1&HbT@mH22V4TC^{3Q=ldzy>khYv6n9foKxqvs zEdT3;csiR%xFICmxwX@GeJ7h<=TPcex3xBT6h|Q73m~CZsq-|1n-vEv@sla+^7U(# zwVfePAQ1M4*z{VbQVR!d_4`^oLngNA8&)YBws`{HcHi};WMtT=)NZK1yiVEZ?u6+i z*wf@`GVx5WuY-nd?)KIuWrM%bN9Lr|PrrOUOxo7k=us~Bg_ITB+^y~IhW0d;x>ZV@ zFHDK6xMPgmD}DZeuMy7l1(Z#m<_-^RCMyGBlz#K3l-kXkHl^uwrnpI|GvEm#Cz}qg zO(A#V)-UN{Z2D@avf9(>3Ao$A?+bYvc|{tgOn0tQoT$9nnKmNt`c+C-^+IKpvtC)% z?sJD)JDX=ag@Z-CtXQe6Xm4%q)nklt(~6PnKU;2s#VISwd?D? zBsk?^3QQiVJc9FC3X9N1j5dY?db60l988T{T^;V`)<$L7GNq!cssh)7%1YM6YiUlX zwZjuAZ)E&w5{%$AC6R32SP(@JPI+7{?#`xmPk`m;i-1OZpwjD+o6o$h?Ve6|hi5U4 z-nOH#iz~XAH{8h`y_hxnI-6R_=Zo32vWxv8k1_++k#V>xXEM>w=X7{Fe1Yw-k2nGz zzsDV##{A8$MnuMPr9zo8L($PvWp4Gn3Wk>BZ`tlbK9}F;Yfmo`Z|mR?!UaP^YpBER zcX4knW@#i6M6Fg5dB=e4;UJ37rDtR|2$@-tE8QefTa;#0COn)<&^=E5hbu%ah%8~O z1AUjT(M8Fq`7T$(cKBtf(h%zK&t6WyJSFIY>*4fBR@S`0zvfeh|gwEh^j0U4wlWN zs9$^e=8dimt5z{FSj-U**pL3)(~c_ewstm^&CrJu8c(*n+FOI6OOzRCOS6|Zi;e>I z(2PNtO(UAi)8P+oA0yMuv1Fpwq(q@*_Hvmdr2bcS$XscrbYMsuy9`n|N)A6#Vw${tu)rrxcT{ZO`?=GGWtWAUzKrs^1ik=Q&PS zkn}3<--3Ps&lDZ@Wc^$Sp>mDlZg^eO)|+{<)^Z^V(n{ci+d*$L(@92ykU>04!Sfn; z{+!A4N5Mlr+yb7xZ)n<9Jk7RbH(2^s2$Q*=$>;AM(=_ZB8hk#%K=#tQ`?Ig1zX1BU zH#6#>$;hvQJ^}QL%<>xzx(&;l641x;Db;Hk=+i*&Hs@b;OZLsyNKRI;Z^iQ~`gw7v zUN?g`3|?8UWPc0NJYOTf2lP(R?-)z|Erxsr^vA$UryD6&%s$QPG3w?RcxrL#t|gOa zi{PQyJO!SIu#_0d;8B2SrMn9&vwJdVS^p~XQ<;{6=P%%)g;>gtZH7;;0sRcDL1&oh z4T7#9Z3pOQf$lcbDaO8Klw&V=oZwl4`b+5mPxf33XCgU=!PAV@{+pR{UNPjT;F*lM z{AHOuwFXZCDy$YfE8&+kT}!f3x+=iq#yV|trkr(#oXz0*SMa38GHl)|SeSymUEtXZ zo-f(6LNp8FxfeXogJ(@<*=vllC&2UZhnn^R_{}zFB@BB8!E+n7mA)z#NEbFz*irCo z%aqe;$hibOrRrD51j%uN=N|BE&6Lw<$O(aG06bq;?{|aed~A!|o+;-RL(bFS=>ZRo zD`{o-8axBwISQUoCQrM;W5wZs3$VS}oypT>@RWgP7kIiddHjNh+QT~V90d=~_#5_I zZNz&!=&yiI{ifO8?0H6g_JZdl>|kKqDvhT}sNsF(W8nD&JbTPMS>0Tbg0xpbugA{# zgJwFltNV=djDqKN@MLA!l6BJHnT#_rk6}aSp-eeHGDo=l!=1rPOyP2jm6 zJP1R6k*~Utwi9X9lp@Eukm0MlLBA99uWDx`<7x0b2OjF5Q!?~EUqRB_p#K{5wPrf` zVTG`bWH1|JpMHf5qAQ@E1U;=jT7;cMr%&6t_=@KsS+X2ZK!Y*Sw}3ti^yW0W71MjikiQr7FzA<>={E{GC7r%bd*HMOPJ7_A2Tpt7v!#!~dOO)fj47V5eKjZ!(?k91-j{8&GXHI2_i*c{Q-GF-+?*D^(KklQr zPvFj1SYih5THM#+z5(~0xF5wWy|jAe%Ed}qeM7i26jm0N&o8f-JvYoz<*v%=@``yg zMXKcg(i8uG0j25xn*keHC^0RduztBzVl2Ms+9I)-2^ORV46cd3QT()M86Kty(KI``x=BhfaN9icbsSF4}*9Y@Ev#(e+18qC$j{t z;R)Y`XV(&zI15h;QXarlUL@}^0E!)vW?_f$LUAPV5xh`LN&GHeD83{f!VATk#3%7W zaVIhPgmnBr988eoc9i7yAH_?7q?V2Wjl+kh#qCGG;I7?=2F zV2XE%?*OLQm-v2QY6lYU1Ew}1@iV~GJ|un_nA(cOZvj)gk(l;}s0~T{88EdciLD4} zYFiRd1g3T-u>wqOPU3Q4YJUgKwe+oQo z!sE~-ePY5>fj=|hD&U+8jPln2=b3Olu)L@~uFb$+;04oJg7(v>z59XZlrsD=vcT5^ z_X5*C7twD9R+q8_^=s50+yOiaOlK1a-w%9Z220Rh74h!_mKW)N0(kQ>gTEhm3-EY7 z|7#ii?*hL7I+n~_eiHbI36BCFGvOR`*#jm#5m+_hsldY~OnXP8CcGG!Ek}Q>>steC z1D5)!ymk})O5)cqDxVAZ5jUglFW8&Q+JM_HWr=YdXR$8eNmVRC>vrx>;4aY5(CK#o z`{%L5LpuFI;2zK?=ydu+8+nnwz6>mB^d$OAMAzGs1u3-e6rGLn0m&0#;U9r}FkY7F z_$2Ue5f8L?O7yH;#=g72pcepVUCa`+_eb;+;8hhYLHkdH72qeqPvs*#6Zn@He`%k9 za24*{f+?dD>nRdAKLE}z5P-CJOs|hIV=&<^A`et1be!5Tmp>GhKbEOo(B9R z#@`klUjqD;-rh;xJm9yXkG{i@{mX!#hx~3G*8z`*eA?qBx)Yd=B}sd?0Mj32NZbZI z3-!~i^KS=UZpwc%@B?VivOc~Kd<^=*&i(yU;9=A!^&h0~G2n{x1)c1D2KXMxyGW=1 z2Ka709_{eoTfh^MUzYbhUVNbD~C2rUGZNT)m9Mm6C z{S*Py|E#b_$5VjsM1I=eBYVq$JLj?l?Kcvh5BwqG*P+ud1%46j^+6rqgZ4p()Tn)v z{BM9h2z?S?0esmkmUuzur~mU}nyLM?0Dl+$p03k_z~6>{%5)q7HvE&rZUw#x@geK8 z1MyW2{b0ul!(4V3=+7XYr|S711l|IBWO;uMJc9P_*6I6!t)_T51Uv};(Oxx`|3zR2 z%1ikPzX7}!_R~C*@cY0g6saFhqwkjZ3QXTW`9nW7gLhvTe~*rn6-*us5+6F{p&)+f zB6T{5$&a%9M*m;#c$0nfL?p3cVYF8c00#&HUliw;S+T;-Kji>l-? z2=N)ed=t+=$0TsRC9qwh$~07!hUTWBd1+{V8d{Ks7N()QmluQIKCm1*UxOeJIDCPV zU`84lO^RJ~Fo7qN&ng!*xPzW1*F2nG5dtU`cCV;fm0}8pd;ueiIN}lVxwzL2rmd}h zBze58U9O5$YO~Mf>vRP{OC@>3?d=8wndQNOE>~x`1IMax9uMst2U*0yrEUBK5ssuV zKTcoxaFQwgNRQFpE2`a1g~!mHMXF&&`BI}$m(?)YFDhIGd`Ofn_Zh%)UI>5_?aM_DH`L9C=Qdk z{GkBP?(&C&EvfTPboNP}CE~|?{64>6qhm<=DJ8^L0P&0-SRBnV=XQlW!4QgJ9MjVM z;OX4f8u0NWfnr2(xw@(sl+#%ye8Eyb<8ex3TKRbOIaihPc*vn^HF-wC~Ro$#L+$UpdK%E2UO9JscWZpDx}h>MafTY5J!yp2|<=R zF9uc|B^ESz3O#Hp44|6Fj@$32{?IrsCd5I*ZH#~suXPeRAV`C|}y#sRa3!3rem{I-ruJgc#nHFYyxM=Q;Iq1j*1jI#idAg?I zCO#~^`JU&= 0xc)?0xc:0x0) + +.got.plt 0x00000000c0006000 0xc + *(.got.plt) + .got.plt 0x00000000c0006000 0xc build/main.o + 0x00000000c0006000 _GLOBAL_OFFSET_TABLE_ + *(.igot.plt) + .igot.plt 0x00000000c000600c 0x0 build/main.o + +.data 0x00000000c000600c 0x8c + *(.data .data.* .gnu.linkonce.d.*) + .data 0x00000000c000600c 0x0 build/main.o + .data 0x00000000c000600c 0x0 build/init.o + .data 0x00000000c000600c 0x0 build/interrupt.o + .data 0x00000000c000600c 0x0 build/timer.o + .data 0x00000000c000600c 0x84 build/kernel.o + 0x00000000c000600c intr_entry_table + .data 0x00000000c0006090 0x8 build/print.o + .data 0x00000000c0006098 0x0 build/debug.o + .data 0x00000000c0006098 0x0 build/memory.o + .data 0x00000000c0006098 0x0 build/bitmap.o + .data 0x00000000c0006098 0x0 build/string.o + .data 0x00000000c0006098 0x0 build/thread.o + .data 0x00000000c0006098 0x0 build/list.o + +.data1 + *(.data1) + 0x00000000c0006098 _edata = . + [!provide] PROVIDE (edata = .) + 0x00000000c0006098 . = . + 0x00000000c0006098 __bss_start = . + +.bss 0x00000000c00060a0 0x298 + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + .bss 0x00000000c00060a0 0x0 build/main.o + .bss 0x00000000c00060a0 0x0 build/init.o + .bss 0x00000000c00060a0 0x108 build/interrupt.o + .bss 0x00000000c00061a8 0x0 build/timer.o + .bss 0x00000000c00061a8 0x0 build/debug.o + .bss 0x00000000c00061a8 0x0 build/memory.o + .bss 0x00000000c00061a8 0x0 build/bitmap.o + .bss 0x00000000c00061a8 0x0 build/string.o + .bss 0x00000000c00061a8 0x4 build/thread.o + .bss 0x00000000c00061ac 0x0 build/list.o + *(COMMON) + *fill* 0x00000000c00061ac 0x14 + COMMON 0x00000000c00061c0 0x124 build/interrupt.o + 0x00000000c00061c0 intr_name + 0x00000000c0006260 idt_table + COMMON 0x00000000c00062e4 0x4 build/timer.o + 0x00000000c00062e4 ticks + COMMON 0x00000000c00062e8 0x2c build/memory.o + 0x00000000c00062e8 user_pool + 0x00000000c00062f8 kernel_addr + 0x00000000c0006304 kernel_pool + COMMON 0x00000000c0006314 0x24 build/thread.o + 0x00000000c0006314 thread_ready_list + 0x00000000c0006324 thread_all_list + 0x00000000c0006334 main_thread + 0x00000000c0006338 . = ALIGN ((. != 0x0)?0x4:0x1) + 0x00000000c0006338 . = ALIGN (0x4) + 0x00000000c0006338 . = SEGMENT_START ("ldata-segment", .) + 0x00000000c0006338 . = ALIGN (0x4) + 0x00000000c0006338 _end = . + [!provide] PROVIDE (end = .) + 0x00000000c0006338 . = DATA_SEGMENT_END (.) + +.stab + *(.stab) + +.stabstr + *(.stabstr) + +.stab.excl + *(.stab.excl) + +.stab.exclstr + *(.stab.exclstr) + +.stab.index + *(.stab.index) + +.stab.indexstr + *(.stab.indexstr) + +.comment 0x0000000000000000 0x29 + *(.comment) + .comment 0x0000000000000000 0x29 build/main.o + 0x2a (松开之前的大小) + .comment 0x0000000000000029 0x2a build/init.o + .comment 0x0000000000000029 0x2a build/interrupt.o + .comment 0x0000000000000029 0x2a build/timer.o + .comment 0x0000000000000029 0x2a build/debug.o + .comment 0x0000000000000029 0x2a build/memory.o + .comment 0x0000000000000029 0x2a build/bitmap.o + .comment 0x0000000000000029 0x2a build/string.o + .comment 0x0000000000000029 0x2a build/thread.o + .comment 0x0000000000000029 0x2a build/list.o + +.debug + *(.debug) + +.line + *(.line) + +.debug_srcinfo + *(.debug_srcinfo) + +.debug_sfnames + *(.debug_sfnames) + +.debug_aranges + *(.debug_aranges) + +.debug_pubnames + *(.debug_pubnames) + +.debug_info + *(.debug_info .gnu.linkonce.wi.*) + +.debug_abbrev + *(.debug_abbrev) + +.debug_line + *(.debug_line .debug_line.* .debug_line_end) + +.debug_frame + *(.debug_frame) + +.debug_str + *(.debug_str) + +.debug_loc + *(.debug_loc) + +.debug_macinfo + *(.debug_macinfo) + +.debug_weaknames + *(.debug_weaknames) + +.debug_funcnames + *(.debug_funcnames) + +.debug_typenames + *(.debug_typenames) + +.debug_varnames + *(.debug_varnames) + +.debug_pubtypes + *(.debug_pubtypes) + +.debug_ranges + *(.debug_ranges) + +.debug_macro + *(.debug_macro) + +.debug_addr + *(.debug_addr) + +.gnu.attributes + *(.gnu.attributes) + +/DISCARD/ + *(.note.GNU-stack) + *(.gnu_debuglink) + *(.gnu.lto_*) +OUTPUT(build/kernel.bin elf32-i386) diff --git a/c9/thread_schedule/build/kernel.o b/c9/thread_schedule/build/kernel.o new file mode 100644 index 0000000000000000000000000000000000000000..32eebd5a18a311b1fa92289f8b7fe8d82c8456db GIT binary patch literal 3232 zcmbVOO>9(E7`^it`bW!5vA0yH?P!W(g5$heYeiy4BLq!D2oQs&kq*olTF0gtUd&)L z3{hFIAjA-(F(kkOe-@@8XyQU*h;do5an{tyzkPv)4TFZ&YbVO zbKjkQoj2#sq0Qr45{ZPel~BvnqEad^#z>vlt2X%mr?@9!@2)|--LO$o`c_%jB03!? zb<*C1YiYUO1#Y;k=9VkIqhol1Q?IhUC~HJ_PQMsxAPrDh{xG$&g4y4eVf=D%a%qh@27(VaAoH5*Bz`GH)k z7-O2fLoe`n?M4sLo4>X6Z|N|4J-zQd-U_2P z(EH5ebsC+c_pZn5GMZnsYp;2{l}3-zd(q>ijNU}=NspH{I#2IDkJoMVGxS`Z~dVl@9boPFuXX*Xo@$NA?qW1;7n zLmn?NGFS>)T#w@y0D$7cC_0_z4Ng+1wR8n29 zzlFWvo&?OtK#js=X2lgKK0eH^xB|5UO1?nuCt5#zF^XQq6aRurk8fK-{KcQi6T73l#Gt}xo&@fM}zKMp}YBjT+ zIcs$IrD(wbj$m%xo)cEzfL=T^0@ZCTa2iRMGo|vwF=cnEsbaJTPj0!Sre>nbo)TUTIv1)_QQgW_bGf=}Rp_W;M?K=G z5l21hs8L5f=BP18J?^N2qxL##pQDP7Dmm)qMin~qh0c7TGhgV;7drEW&U~RWU+Byi zI`f6je4#U6=*$;7^M%fQp)+6T%r`9a+2_G>0?$_xg3KQa5c9uXcB-XxqCW{z<^k390Y@OI%{!n=hhgeQfI!aP!Ve_41|I1;W3GxxZz zCJd_Ay?9OFhv`P)ox=6~aQz+8r-i=~z9jsQa5uiE+~*s_+S6j8U)KUvH~- literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/list.o b/c9/thread_schedule/build/list.o new file mode 100644 index 0000000000000000000000000000000000000000..f9c18d2c085b70d5b9c37591ef36702902fb6f99 GIT binary patch literal 2884 zcma)7U1%It6u$eDW~`fL4S8ru!GI~onhpt#nrhKann@HFo3;t~5+=LZNp|V}xieF? zTC8OaG>l?EDL&|nii$qygOmzBgltxva&f0)YUNE5Jgmi5S~ZTxOK3K^A4*obLptIHKoP|JQxA9KA_vzLCb0o80&}YB!swGh{sP>Lc#d0Dg{yg0^}{oAjFGFHEX;9_cxFiARbRO#+x-h0d)_u zTMg>50Xc#_4?%l1(rypp|A3qUulad2kb4Y+y9QZq;TJuA0Ng#uBYs|Q)}I8|kJs`! zKmVG`N8w9>r-iZcMZR*IPf(r`G{<4~cf%uKgz)EIA=crCVE46Q`U*8yh<}azcz+YX z3b6;<@B?kwzdMQ#dKKwD^uW{O%jgMqx@RYR^!(34v0Lwc=_)}ncM+kofEbj#2Z-M051{UetK%^oHnb^j!(ojU1pg^$nr(C;a!NFBjXb}n!7j$=)b}4?ECwCK(5D1`5jy-3W0y(epx-4P@$U=% z3&P!ueMcI3H-(@6-%;LQq>)GO9q$88efkI^Z%Ft@gkK}v%h=1pKQH_r3jC44*9jlO zw+!zh)!he8bsrUYNZ@|-55kTKof4Yv6!||Fn(hGMO`#R44?HOJ^FpUd;}yI_nqEEC zyqL2RNocF&>a?^(5Nj67=P3znfyzchCY zH)HWUQ>lxQ)|X0&oWa!PQo$}_Tlc%(K_HIFTcfZP2u8|a&7X2lbeOE-BqUoq)-t-2iI?RYsNytf#h)zB$Iob zoRliuT}!>@O1Taau^@pi^{8@fHH0=z3YMPVmlW#cA5gsE`|Eu^@B52)0R1nDNWZE> zPgm-Y1SrTA_x$v~y37+814v!w{{Gb)>f7kd-J5;*F)gE&3jxs15aCny*&SAo2G3+P zenWoOyh7G;TBdXT3(lJ{oR3XQXm}aCTU(4FuLXMtk1ZAoq7CuxH(U!eye{4orfOKM z0ce2yE9x$9pbfwP3B)##A0CXoFA;CD55Hjl#Ty2Bx1tREsHki>E~_~g4WFswF79tC z01~v((xBWZyXDU-Vw-4PgK}tzy)Fa2X4@w#yunDkAxmY6IQ3$V4w5Y1AEA(2ObDlE ztqW<1iq-K3B^x5YK-pB56}0^+3P=#9DZB!$l)LvK39jzy+xjL=$IRZOjOOSF$)YE+ zqd9boRACaHUyT+tb2^?7;kjr*3MIP4SXbCqu;ZOR;@=;bi;|gF%vtT|^^>QbO+~;e zVPNBhCnCYA z1U@6kDvJ>=zBJRjI5_q=t)`C7AuIV!-a_YyHQz~FAv$f=6zW*0302uar^A})blKZn h^X1Qp(v*}xL4JY`Fzg1%*U)J({DnMbcmqMl`vWZ_+@`dlHB3OLMPiKnSkPvztw5rQ&2$!~EU>%a?2G|x z{57$G*iC%uztC47`_vcv-d7)e(4>hxXu>1O_WNe;b;p&Yp5)9q-}%lxbLZT1=Wf0* zV;F{LF~pQ65*clanbF7$NVjxF{2}0ap+6nIc-^?Mzp(Ri?uUQ(8;!>OrztSuZf8B@ zitnRf_;)t4^{|UEMXjEwhaV?%>fskjI#Ul%Ch52oE1ye-U+wbk;cjo@{g3jQo%|kl z(FpeR4qm*jJ=20GTJX2PtTg~-y&qfn)fRjcSofCsQo#sVi;)rA z-W)q^ExW$Gv4|lGs%utpQ|#Ho<;jVHeR*bPEoa4e(ou5li!=_6gdXlfySJGJ&Ysy95#*HI7r(qGNR(+v{*Ey z@I~@RMXo4(P4PvAON!s1?Gf2h@w>#R{}`6<{~ea^|3k(9RPn!5{2vw1F^(2qPp*L~NT z{cd3p?Oj~MVpMcX_RpSEam8Bp)&qX$hsrNVMC;xP|DhQu+7T&l3YBNVJEm{OS@_SO zTJ-(vLx8vAyds{P@!YWe|ba5;Tpb!!g|xO8$ETs zF(CTwD)A}+zfX(Kn@4~?>r$=*d=rglY$-E~h@E~I1 HhtB&CF1H~x literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/mbr.bin b/c9/thread_schedule/build/mbr.bin new file mode 100644 index 0000000000000000000000000000000000000000..75da76ed1d9f0a4bfc76d948cfabd74d3f86e527 GIT binary patch literal 512 zcmeA<(RZWoK;MPF2YVQ5b};Pdd$EIoZ8rn^P6mcu{t{;eQjf7QFc<pSBHg?AI)sC3wHl>X@a iv5SH6`=ic-yFM_!?PPC0%=GBTVW9D&fPxSRT?GJ}(N%c> literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/memory.o b/c9/thread_schedule/build/memory.o new file mode 100644 index 0000000000000000000000000000000000000000..b9bc14f8aad99a4d59f34c74bacf8f2566d7d8f0 GIT binary patch literal 5008 zcma)Ae{9re8Gr5v^xECvsEqx>?LZ(v~=QMY;Ms+U7Q*h6aUj;;*WOS28$#@#uN?4#?<8o7riZw(ZF_oKJWK^@4k1G z%oo1>ywA_~d7t-rA71*T)wAE@@rW57@kx~uDTEwJhk~uLMnXy+08PC{e9_!L9?KTj z7H*Ww<&*PXaL!sw_z5V2RX%H#NM)x3p3FeD7$|3g`7AlT$|U=0C}$P3)5UUT`x&yY zb?2?pOs^*wE#;#{&xLH!KjP`FGl9x3E0})EVduL7P@VgG&~qVYE#>j|OE=z_M_}1v zNG-C0!RD>NT%l>Rl=evO6-5L$mwh603trEK9U@(lvu@}*>;is+z&9X(o60!M9NNpGNp&6y2gflaQ=LRD=rn6pad%#vQl zj|A3Gg}hbFOLoC?de(j-Zxzh^{uw9nGuib>Nbg8$CVPkbAgZdFW(1Kv*T6&-a-nyA z0=1d!W-1%wCvGx!8C;vn7Awd6<_G1nI_AZ^wWysIb0K)fVL(*$523DPq*@WxK7ih~v<3 z9QCtZetDsBHg7HLkR$)zg9WwRueLnu-m+exi=s*%l-f6zQ^RGhQ+vs+rVM}P=G@x< z8_TJExPQl?JJdRkUNOq-MHlkc6?VJYG&m|}yU^D|vxvFQgWAQSJ3RL-bGLq_dEKpf zMs7nXqjXT^xCTpwQ_eL|nn7MZQ*4e;*|2qc%1(s0gj=Utozb>UjwRB`#ONo+5@X}( zr$og}z~XES`!fm7P)}qNDi0K-vhKL|KP36N&V)##KSRDGX~v8W>L| znuNTX2rCTA5pgPhG!e7oeWM9RA~F^q9UbqFku7=uruqE(P4jYK-@cvUt$Q2PDS4Nc?{w=-zDBW<9|8YOd|TY|K1Ut~?`!z}i!Y9^W{t<4HNs&3 z2;UbF(=L2&9bdagDN*MfzHj5{@n@i0$9u8rrwCEfC-S*^o$jhm2y2{y&OSb4TzmHI z@>bUBfX;Qa!$b9cdK^D|G96~D7)XN}zRY+<%K@a*5sD7d-$Y5?ghi-mGbSN`7Brs^g!MTtQvZF>rXESJauopW*VWPY z)zRBQn>wUDyNA;pCsmg^ny}F?*dOZKMl3d%N%qHLk+#-PJ}54#y+gI2N~hFqS2Z`O zbTh-{DzKjFd@zEwVyXU^J(Nivi}X!N>_E@K?!7&+gZuYCW*v$h+S}b@#l-a1{&=!7 zXOMOxma><16`zvSfFn)Clf(V7iPUhCF;2kF-TWAUY2#>y6Ozi<=+kM1!%5q$$L?Bb zvm&fo?>Z4Y38m>ykq3Y+cw6oQ`tK4M(sYva-6BtF`URjD7wE@8+Wke-tO3#5gq`#d+`03N~AuT^a8V&bp;ytKEU}J;GFEsvE?nfJ?JOrdY{*R~ZH)?vTrrUsw>r(G8DO(}~aXT)NzKIEmq{khCg2*yG1Q%Z!Ehj^NiS&<;jU{lcBgt_)5jpVr z=nlNt`j5deR!a=U2GjAeghY;}$1^E}_y3OPp}5AS+<`fJ1p5R3y+qlluU>CA_(l)c zprVH>pngw+^sB+VA2bB!NS_+?&EKVTDGEQ3(-O{!>*9(ifAqf@A8p9vc*f<6wDH%1 zIrTqGuT|S%DK(yjKc2A}+%E^;0%Wh$isw;dMn<_YIaH^2;TC#46WVZnj!V$v`@;;% z_#M}1_#Z&eJobzpj jYpnD_K>9K{##GMZnbYoC*GyScZoSXI`1>@_!|44FCLzOm literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/print.o b/c9/thread_schedule/build/print.o new file mode 100644 index 0000000000000000000000000000000000000000..8bef927370394424e7db9b716cec3690f153cb33 GIT binary patch literal 1872 zcmbVLUuauZ7(cn~mhPX~!*l~hD+HYgX)4nRia4^g42x;k;us32H_16|=xuJe_e3@* zEOH-ATF^Iroq}&Z=#zGZPMhIW5ZqMgs}&&{HyxrbDvQ7G{z*=Y_}~ZUe!t)M{l4#f z=j7xiCWqn@;)>RFH>tw)0dQs>XgR#*tM=mNk42)FI(o}@NF1Y)!XkY620G8e_4Z-cG333s97WN8e1 z1cds~KIHXtfo`+usnO3?UP|s?-dF1|wSTHb&Acmvnf8sdFE(f8hez0JEqUqdf%opZ zDKC99@KgPY&k_NXjpoWkGFj^!P*2x7d)4t;XGj_Cd*pNS+IM2^llZXAkc-P}?e?r6 zt0(#)nUSyj^<{i%OpaccjZL{e$>blu%SXPFUUM=?J^J_RN4SXfi?x01GQ;QBch*>f zvDHZoD&*CH;kef{wtkaqwYWBC{Wb=~)WMwf&6{R(w(*CZVfgzwzkH-?vP3>zfQ{WS|_c3&b5vA(^IW)?{vGk zr|}>K)lwTESgAaq#dkXAUcO-(okFhcM{`)f6GK_*(iH%)^&r zff?)#fL9}m{JT5&r{HgD`4;d+jT<=d+ZsO%zOL~k_!k;~68r~^p8=;Hfni?YgvOVE z@7DM-@UX_;0)9~A9{@k3@y~%D*Z3yzag9F{Z4G}A=P$zFAI6C1plxgX2Ke(~J>{8I z%8x7vr!1_Iic>19)HItp#m7(1u+x(hX*Qjm$Yy48r}+z~PG)ngQdJxxp07$N9G~LF zMLsoW+vOfXg4-{Ls=~Y($OxoLF3+2V1-D`rgs=Cb)ZA9{S_KKMQdw{*M6oYKv|u_; z$($415vuA0n?|j7naLTgvB-2T3<&N-&)_KD1o=$pqx?VaRhs|+ literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/string.o b/c9/thread_schedule/build/string.o new file mode 100644 index 0000000000000000000000000000000000000000..7064f8a1d06466866bc0da9ea58069517ed17754 GIT binary patch literal 3996 zcmb7GTWnNC7@pJJ(k&LYT$F2Yn;-#|rG-LU1Eei1kWelGHlmR%+wGRtcDHpeQjm*b zOXy~;)}-;0Xm}w$q4B{qJgBKH#`t20mls4dPz_;CT#|htk)(d#oSB}}T`D@s*>Ar2 zznOpLKmYt^&-z++7=|HqGlWA}Oo;7sOiQhV)xs_2N_!c&KmGf`)SoNwJeZuEEajw( zzhb~Ykvi=X$*RF2&NwhQ^b+A^*WeIWI*=Zh^3>JJjL((!<+JXLFF&~7Nc;2d5&y&h zhARj6Pp18q=SPzhLzyEFz8yFHEVTU4Lt<9ybcOV@)b!IP{mh%ePsUe~_T{pRGXC6P zvw<$%?lRJQ;CB9WuQz7!n(@1^OPn$7&!tZ1U(T?H;)^R#i2o6egxt)tMVm3hK7?b)I8=bk zIJjgSu)qG%ka~tk2uenEj`G&D%DF(6&xJlE3AlxW?ku%;O72{#)?fD{a-}v&di0m@E z(+N18g#p~GXpmp+g_!$m&Lm4~pHzOwSYrXqR6o>BIjHI-2=TZA8V=1bCl zPmzChX1R@Ac8PhR%y9GMPV46IrXkwD=ySKl69G@v7SCRPON;34Y+V;m#5$wLydhz? zcvh|Q#A6{#8Ps!VY1Kqv@>E}lNH`J?}CfDN~G09^gbf^nr zKCvSf7a8Zuaj|pTw&y&n{jJGpBI&8~ZuC~K-H?>5=1fh!x4L$XIf_Uv#KUHsJRpoy zE@N4R(>Z{O;=$N*8qhlU(=^w0zNC3h>GqVPQ6nvkWr4BIVef`ytFEX_68wv`C+R-xvCrD5)W4-LXP{ao#qW-z=gmC*Kc=wHI4?dVQ5hg3~b?7!S1=sDzRtRaV7#m)^O|4m?sc|0HSde<_G z`6XrIV;krD7%$-8fpac%ob!JI>U<`@0j_hG{5H7GQ}VmuIw#5Rf$RJuzYnf+jT}Lm z$}93RTMkJ(F1uNO0)h5qG!zJU8)_SbMK;<=-Flm)zS>UeZKR>jA~n_38||!SgPpCn zEm%{%p;p(hK%lRFllNFS5$FvC5*^8Cmp9la0y|swH8-^c_U+hl(03?ssHwTd7ZAO{ zXlE!8@9m80%EtnT0>?UrItXCdBU6KBt+UrW8d>t>8!vf62>wZ&jbwfjSdO$`rEr}> z{v&WM7NQqOeG*9h1!aFzA@hUtF9NCm14w-qNPRip2kn&#mnf_PQhydmJ?}O3?QokQaeXFu}DvT*S38elykosSN)ZbI~ z`w9)zF#0J6QojpG{T?9o0cCGj*rV_`kowPo)PDt}{s(2huJE?PERg!mPE+3mr2Zg~ z{$ErWQrHEg-oyA~uiF*!E|Rmx5I+~8G_U_#4UmH&ZLtU7R z6~i5Y_E<0y7T#mAo+R_|zqZ#PoM#i)qR)C+fd`?NRUhG!xsT(a%9?F6;4%%W(Z;q6 z4=ZUS9<~8|6n=w|>mg)vJ+A*09?rq5lkJXeEe`1QoJflWMqH=Mh0dE1doBi0V-hvrZpH?8JJ*7Nuoh!f-oCYmjI9>ie!QSl+6yL znUTZ|fa(;H_#iVtKoE$LU6KHlZ~%(205M2Ehz6M{3FQMRFjxRoCk^8OX_$F1p|J4a z&~6tMkxm65DbcME8yz2a7$ym#^-5AJN*MHtGm1-!N)nTRtjb&n4dElJ#ll9mkA>mm ne`F<~kO%n-1_VH^E6FHIO-#`*F3&7U&d>{HfH2}q@*%1K)EOb^ literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/build/thread.o b/c9/thread_schedule/build/thread.o new file mode 100644 index 0000000000000000000000000000000000000000..8e50997b477d08528cf6131135edc632cf54d93e GIT binary patch literal 5040 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 literal 0 HcmV?d00001 diff --git a/c9/thread_schedule/device/timer.c b/c9/thread_schedule/device/timer.c new file mode 100755 index 0000000..f8fd00f --- /dev/null +++ b/c9/thread_schedule/device/timer.c @@ -0,0 +1,64 @@ +#include "time.h" +#include "io.h" +#include "kernel/print.h" +#include "interrupt.h" +#include "thread.h" +#include "debug.h" + +#define IRQ0_FREQUENCY 100 // 时钟频率 +#define INPUT_FREQUENCY 1193180 // 计数器原本的工作频率 +#define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY //计数器 0 初始值 +#define COUNTRER0_PORT 0x40 // 计数器 0 的接口 +#define COUNTER0_NO 0 // 计数器号码 +#define COUNTER_MODE 2 // 模式 2 +#define READ_WRITE_LATCH 3 // 读写方式,先读写低 8 位,再读写高 8 位 +#define PIT_CONTROL_PORT 0x43 // 控制器寄存器接口 + +uint32_t ticks; // ticks 是内核自中断开启以来总共的滴答数 + +/* + * 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode + * 写入模式控制寄存器 + * 并赋予初始值counter_value + * */ +static void frequency_set(uint8_t counter_port, \ + uint8_t counter_no, \ + uint8_t rwl, \ + uint8_t counter_mode, \ + uint16_t counter_value) { +/* 往控制字寄存器端口0x43中写入控制字 */ +// 最后一位为 0 ,即选择二进制 + outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1)); +/* 先写入counter_value的低8位 */ + outb(counter_port, (uint8_t)counter_value); +/* 后写入counter_value的高8位 */ + outb(counter_port, (uint8_t)counter_port >> 8); +} + +// 时钟的中断处理函数 +static void intr_timer_handler(void) { + struct task_struct* cur_thread = running_thread();// 获取当前线程 + + ASSERT(cur_thread->stack_magic == 0x20000611);// 检查栈是否溢出 + + cur_thread->elapsed_ticks++; // + //从内核第一次处理时间中断后开始至今的滴哒数,内核态和用户态总共的嘀哒数 + ticks++; + + if(cur_thread->ticks == 0) {// 时间片用完,那就调度新的线程 + schedule(); + }else { // 否则将时间片 -1 + cur_thread->ticks--; + } + +} + +// 初始化 8253 +void timer_init() { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + register_handler(0x20, intr_timer_handler);// 将时钟中断置为 0x20 + put_str("timer_init done!\n"); +} + + diff --git a/c9/thread_schedule/device/timer.h b/c9/thread_schedule/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c9/thread_schedule/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c9/thread_schedule/include/boot.inc b/c9/thread_schedule/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c9/thread_schedule/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c9/thread_schedule/kernel/debug.c b/c9/thread_schedule/kernel/debug.c new file mode 100755 index 0000000..bef6acf --- /dev/null +++ b/c9/thread_schedule/kernel/debug.c @@ -0,0 +1,18 @@ +#include "debug.h" +#include "kernel/print.h" +#include "interrupt.h" + +/* 打印文件名,行号,函数名,条件并使程序悬停 */ +void panic_spin(char* filename, \ + int line, \ + const char* func, \ + const char* condition) \ +{ + intr_disable(); /* 打印文件名,行号,函数名,条件并使程序悬停 */ + put_str("\n\n\n!!!!! error !!!!!\n"); + put_str("filename:");put_str(filename);put_str("\n"); + put_str("line:0x");put_int(line);put_str("\n"); + put_str("function:");put_str((char*)func);put_str("\n"); + put_str("condition:");put_str((char*)condition);put_str("\n"); + while(1); +} \ No newline at end of file diff --git a/c9/thread_schedule/kernel/debug.h b/c9/thread_schedule/kernel/debug.h new file mode 100755 index 0000000..d4df982 --- /dev/null +++ b/c9/thread_schedule/kernel/debug.h @@ -0,0 +1,24 @@ +#ifndef __KERNEL_DEBUG_H +#define __KERNEL_DEBUG_H + +void panic_spin(char* filename, int line, const char* func, const char* condition); + +/*************************** __VA_ARGS__ ******************************* + * __VA_ARGS__ 是预处理器所支持的专用标识符。 + * 代表所有与省略号相对应的参数. + * "..."表示定义的宏其参数可变. + */ +#define PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) +/***********************************************************************/ + +#ifdef NDEBUG + #define ASSERT(CONDITION) ((void)0) +#else + #define ASSERT(CONDITION) \ + if (CONDITION) {} else { \ + /* 符号#让编译器将宏的参数转化为字符串字面量 */ \ + PANIC(#CONDITION); \ + } +#endif // __NDEBUG + +#endif // __KERNEL_DEBUG_H \ No newline at end of file diff --git a/c9/thread_schedule/kernel/global.h b/c9/thread_schedule/kernel/global.h new file mode 100755 index 0000000..7330902 --- /dev/null +++ b/c9/thread_schedule/kernel/global.h @@ -0,0 +1,39 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#define NULL ((void*)0) +#define bool int +#define true 1 +#define false 0 + +#endif \ No newline at end of file diff --git a/c9/thread_schedule/kernel/init.c b/c9/thread_schedule/kernel/init.c new file mode 100755 index 0000000..7075f31 --- /dev/null +++ b/c9/thread_schedule/kernel/init.c @@ -0,0 +1,16 @@ +#include "init.h" +#include "print.h" +#include "interrupt.h" +#include "timer.h" +#include "memory.h" +#include "thread.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init();// 初始化中断 + + mem_init();//初始化内存管理系统 + thread_environment_init();// 初始化线程相关环境 + timer_init(); // 初始化 PIT +} \ No newline at end of file diff --git a/c9/thread_schedule/kernel/init.h b/c9/thread_schedule/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c9/thread_schedule/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c9/thread_schedule/kernel/interrupt.c b/c9/thread_schedule/kernel/interrupt.c new file mode 100755 index 0000000..cb73d05 --- /dev/null +++ b/c9/thread_schedule/kernel/interrupt.c @@ -0,0 +1,192 @@ +#include "interrupt.h" +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" + +#define PIC_M_CTRL 0x20 // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define IDT_DESC_CNT 0x21 // 目前总共支持的中断数 + +#define EFLAGS_IF 0x00000200 // eflags寄存器中的if位为1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +/*中断门描述符结构体*/ +struct gate_desc { + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; //此项为双字计数字段,是门描述符中的第4字节。此项固定值,不用考虑 + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 静态函数声明,非必须 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // idt是中断描述符表,本质上就是个中断门描述符数组 + +char* intr_name[IDT_DESC_CNT]; // 用于保存异常的名字 +intr_handler idt_table[IDT_DESC_CNT]; // 定义中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口,最终调用的是ide_table中的处理程序 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; // 声明引用定义在kernel.S中的中断处理函数入口数组 + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + + /* 初始化主片 */ + outb (PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb (PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27. + outb (PIC_M_DATA, 0x04); // ICW3: IR2接从片. + outb (PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb (PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb (PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb (PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb (PIC_S_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 打开主片上IR0,也就是目前只接受时钟产生的中断 */ + outb (PIC_M_DATA, 0xfe); + outb (PIC_S_DATA, 0xff); + + put_str(" pic_init done\n"); +} + +/* 创建中断门描述符 */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16; +} + +/*初始化中断描述符表*/ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++) { + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + if (vec_nr == 0x27 || vec_nr == 0x2f) { // 0x2f是从片8259A上的最后一个irq引脚,保留 + return; //IRQ7和IRQ15会产生伪中断(spurious interrupt),无须处理。 + } + /* 将光标置为0,从屏幕左上角清出一片打印异常信息的区域,方便阅读 */ + set_cursor(0); + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + + set_cursor(0); // 重置光标为屏幕左上角 + put_str("!!!!!!! excetion message begin !!!!!!!!\n"); + set_cursor(88); // 从第2行第8个字符开始打印 + put_str(intr_name[vec_nr]); + if (vec_nr == 14) { // 若为Pagefault,将缺失的地址打印出来并悬停 + int page_fault_vaddr = 0; + asm ("movl %%cr2, %0" : "=r" (page_fault_vaddr)); // cr2是存放造成page_fault的地址 + put_str("\npage fault addr is ");put_int(page_fault_vaddr); + } + put_str("\n!!!!!!! excetion message end !!!!!!!!\n"); + // 能进入中断处理程序就表示已经处在关中断情况下, + // 不会出现调度进程的情况。故下面的死循环不会再被中断。 + while(1); +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++) { + +/* idt_table数组中的函数是在进入中断后根据中断向量号调用的, + * 见kernel/kernel.S的call [idt_table + %1*4] */ + idt_table[i] = general_intr_handler; // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; + +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if (INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断,sti指令将IF位置1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if (INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli指令将IF位置0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/* 在中断处理程序数组第vector_no个元素中注册安装中断处理程序function */ +void register_handler(uint8_t vector_no, intr_handler function) { +/* idt_table数组中的函数是在进入中断后根据中断向量号调用的, + * 见kernel/kernel.S的call [idt_table + %1*4] */ + idt_table[vector_no] = function; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); // 异常名初始化并注册通常的中断处理函数 + pic_init(); // 初始化8259A + + /* 加载idt */ + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m" (idt_operand)); + put_str("idt_init done\n"); +} diff --git a/c9/thread_schedule/kernel/interrupt.h b/c9/thread_schedule/kernel/interrupt.h new file mode 100755 index 0000000..427f7b0 --- /dev/null +++ b/c9/thread_schedule/kernel/interrupt.h @@ -0,0 +1,22 @@ +#ifndef __KERNEL_INTERRUPT_H +#define __KERNEL_INTERRUPT_H +#include "stdint.h" + +typedef void* intr_handler; +void idt_init(void); + +/* 定义中断的两种状态: + * INTR_OFF值为0,表示关中断, + * INTR_ON值为1,表示开中断 */ +enum intr_status { // 中断状态 + INTR_OFF, // 中断关闭 + INTR_ON // 中断打开 +}; + +enum intr_status intr_get_status(void); +enum intr_status intr_set_status (enum intr_status); +enum intr_status intr_enable (void); +enum intr_status intr_disable (void); +void register_handler(uint8_t vector_no, intr_handler function); + +#endif \ No newline at end of file diff --git a/c9/thread_schedule/kernel/kernel.S b/c9/thread_schedule/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c9/thread_schedule/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c9/thread_schedule/kernel/main.c b/c9/thread_schedule/kernel/main.c new file mode 100755 index 0000000..7e4f780 --- /dev/null +++ b/c9/thread_schedule/kernel/main.c @@ -0,0 +1,48 @@ +#include "kernel/print.h" +#include "init.h" +#include "debug.h" +#include "memory.h" +#include "thread.h" +#include "interrupt.h" + +void k_thread_HuSharp_1(void* args); + +int main(void) { + put_str("I am kernel!\n"); + init_all(); + //ASSERT(1 == 2); + // asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + + // 进行内存分配 + /* 内核物理页分配 + void* addr = get_kernel_pages(3); + put_str("\n get_kernel_page start vaddr is: "); + put_int((uint32_t) addr); + put_str("\n"); + */ + + // 线程演示 + thread_start("k_thread_HuSharp_1", 31, k_thread_HuSharp_1, "zdy "); + thread_start("k_thread_HuSharp_2", 8, k_thread_HuSharp_1, "hjh "); + + intr_enable();// 打开时钟中断 + + while(1) { + put_str("Main "); + } + return 0; +} + +void k_thread_HuSharp_1(void* args) { + char* para = args; + while(1) { + put_str(para); + } +} + +void k_thread_HuSharp_2(void* args) { + char* para = args; + while(1) { + put_str(para); + } +} \ No newline at end of file diff --git a/c9/thread_schedule/kernel/memory.c b/c9/thread_schedule/kernel/memory.c new file mode 100755 index 0000000..afbb653 --- /dev/null +++ b/c9/thread_schedule/kernel/memory.c @@ -0,0 +1,257 @@ +#include "memory.h" +#include "stdint.h" +#include "kernel/print.h" +#include "kernel/bitmap.h" +#include "global.h" +#include "debug.h" +#include "kernel/print.h" +#include "string.h" + +#define PG_SIZE 4096 + +/*************** 位图地址 ******************** + * 因为0xc009f000是内核主线程栈顶,0xc009e000是内核主线程的pcb. + * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000, + * 这样本系统最大支持4个页框的位图,即512M */ +#define MEM_BITMAP_BASE 0xc009a000 + +// 返回虚拟地址的 高 10 位(即pde),与中间 10 位(即pte) +#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) +#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) + +/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存, + * 使虚拟地址在逻辑上连续 */ +#define K_HEAP_START 0xc0100000 + +// 内存池结构,生成两个实例用于管理内核内存池和用户内存池 +struct pool { + // 本内存池用到的位图结构,用于管理物理内存 + struct bitmap pool_bitmap; + uint32_t phy_addr_start;// 本内存池所管理物理内存起始地址 + uint32_t pool_size; //本内存池字节容量 +}; + +struct pool kernel_pool, user_pool; // 生成全局内核内存池, 用户内存地址 +struct virtual_addr kernel_addr; // 给内核分配虚拟地址 + + +/* 在pf表示的虚拟内存池中申请pg_cnt个虚拟页, + * 成功则返回虚拟页的起始地址, 失败则返回NULL + */ +static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) { + int vaddr_start = 0;//用于存储分配的虚拟地址 + int bit_idx_start = -1;// 存储位图扫描函数的返回值 + uint32_t cnt = 0; + if(pf == PF_KERNEL) {// 说明在内核虚拟池中 + bit_idx_start = bitmap_scan(&kernel_addr.vaddr_bitmap, pg_cnt); + if(bit_idx_start == -1) {//说明没找到 + return NULL; + } + // 将已经选出的内存置为 1 ,表示已经使用 + while(cnt < pg_cnt) { + bitmap_set(&kernel_addr.vaddr_bitmap, bit_idx_start + cnt++, 1); + } + // 虚拟地址应当加上 页表所占的内存 + vaddr_start = kernel_addr.vaddr_start + bit_idx_start * PG_SIZE; + } else {// 用户进程池 还未完善 + + } + return (void*)vaddr_start; +} + +/********* 以下两个获取虚拟地址 addr 的 pde & pte 地址,为建立映射做准备 ********/ +// 得到虚拟地址 vaddr 对应的 pte 指针 +// 构造一个 访问该 pte 的32位地址,欺骗处理器 +uint32_t* pte_ptr(uint32_t vaddr) { + /* 先访问到页目录表自己(位于1023项目录项中) + \ + * 再用页目录项 pde (页目录内页表的索引)做为pte的索引访问到页表 + \ + * 再用 pte 的索引做为页内偏移 + */ + uint32_t* pte = (uint32_t*) (0xffc00000 + \ + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4); + return pte;// 得到物理页的虚拟地址,即通过这个虚拟地址,可以访问到该物理页(保护模式下必须通过 vaddr) +} + +// 得到虚拟地址 vaddr 对应的 pde 指针 +// 构造一个 访问该 pde 的32位地址,欺骗处理器 +uint32_t* pde_ptr(uint32_t vaddr) { + uint32_t* pde = (uint32_t*) (0xfffff000 + PDE_IDX(vaddr) * 4); + return pde; +} + +/* 在m_pool指向的物理内存池中分配1个物理页, + * 成功则返回页框的物理地址,失败则返回NULL + */ +static void* palloc(struct pool* m_pool) { + // 扫描或设置位图要保证原子操作 + int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); //找一个空闲物理页面 + if(bit_idx == -1) { + return NULL; + } + bitmap_set(&m_pool->pool_bitmap, bit_idx, 1); //表示已经占用 + // 成功则返回页框的物理地址 + uint32_t page_phyaddr = (m_pool->phy_addr_start + (bit_idx * PG_SIZE)); + return (void*)page_phyaddr; +} + +/********使用 pde_ptr 和 pte_ptr 来建立虚拟地址和物理地址的映射 ********/ +// 本质上便是将 pte 写入到 获取的 pde 项中,将 物理地址写入到 pte 中 +static void page_table_add(void* _vaddr, void* _page_phyaddr) { + uint32_t vaddr = (uint32_t)_vaddr; + uint32_t page_phyaddr = (uint32_t)_page_phyaddr; + // 以下两个函数都是通过 虚拟地址来获取 + uint32_t* pde = pde_ptr(vaddr); + uint32_t* pte = pte_ptr(vaddr); + +/************************ 注意 ************************* + * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte, + * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。 + * *********************************************************/ + // 先在页目录内判断目录项的P位,若为1,则表示该表(pte)已存在 + if(*pde & 0x00000001) {// 通过 P 位来判断目录项是否存在 + ASSERT(!(*pte & 0x00000001)); // 表示 pte 不存在 + + // 再判断 pte 是否存在 + if(!(*pte & 0x00000001)) { + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } else { // 并不会执行到此处,因为此处意思是:pde存在,pte也存在 + // 但是之前的 ASSERT 已经判断了 pde 不存在 + PANIC("pte repeat!"); + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } + } else {// pde 不存在 + // 需要分配 pte 物理页,采用 plloc 函数 + uint32_t new_pde_phyaddr = (uint32_t)palloc(&kernel_pool);//页表中的页框一律从内核中分配 + + *pde = (new_pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);// 写入到 pde 中 + + /* 分配到的物理页地址 new_pde_phyaddr 对应的物理内存清0,(使用时清零比回收后清零高效,因为不知道回收后会不会再使用) + * 避免里面的陈旧数据变成了页表项,从而让页表混乱. + * 访问到pde对应的物理地址,用pte取高20位便可. + * 因为pte是基于该pde对应的物理地址内再寻址, + * 把低12位置0便是该pde对应的物理页的起始 + */ + // 现需要获得新创建的这个物理页的虚拟地址(保护模式得看虚拟地址!) + memset(((void*)((int)pte & 0xfffff000)), 0, PG_SIZE);//将该新配物理页清0 + + ASSERT(!(*pte & 0x00000001));// 断言 pte 此时是否存在 + + // pte项 此时更改为新建物理页的位置 + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);//将 物理页地址 写入到 pte项中 + } +} + + +// 分配pg_cnt个页空间(物理页),成功则返回起始虚拟地址,失败时返回NULL +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) { + ASSERT(pg_cnt > 0 && pg_cnt < 3840); +/*********** malloc_page的原理是三个动作的合成: *********** + 1.通过vaddr_get在虚拟内存池中申请虚拟地址 + 2.通过palloc在物理内存池中申请物理页 + 3.通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射 +***************************************************************/ + void* vaddr_start = vaddr_get(pf, pg_cnt); + if(vaddr_start == NULL) { + return NULL; + } + + uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt; + struct pool* mem_pool = (pf & PF_KERNEL)? &kernel_pool : &user_pool; + + // 因为虚拟地址是连续的,但物理地址可以是不连续的,所以逐个做映射 + while(cnt-- > 0) { + void* page_phyaddr = palloc(mem_pool); + if(page_phyaddr == NULL) { + return NULL; + } + page_table_add((void*)vaddr, page_phyaddr);// 在页表建立映射 + vaddr += PG_SIZE;// 下一个虚拟页 + } + return vaddr_start; +} + +// 从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL +void* get_kernel_pages(uint32_t pg_cnt) { + void* vaddr = malloc_page(PF_KERNEL, pg_cnt); + if(vaddr != NULL) {// 虚拟地址是连续的 + memset(vaddr, 0, pg_cnt * PG_SIZE); + } + return vaddr; +} + +// 初始化内存池, 通过all_mem 初始化物理内存池相关结构 +static void mem_pool_init(uint32_t all_mem) { + put_str(" phy_mem_pool_init start!\n"); + // 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+ + // 第769~1022个页目录项共指向254个页表,共256个页框 + uint32_t page_table_size = PG_SIZE * 256; + // 用于记录当前已经使用的内存字节数 + uint32_t used_mem = page_table_size + 0x100000;// 0x100000为低端 1M 内存 + uint32_t free_mem = all_mem - used_mem; + uint16_t all_free_pages = free_mem / PG_SIZE;//转换为 页数 + + // 内核物理内存池 与 用户物理内存池 大小一致 + uint16_t kernel_free_pages = all_free_pages / 2; + uint16_t user_free_pages = all_free_pages - kernel_free_pages; + + /* 为简化位图操作,余数不处理,坏处是这样做会丢内存。 + * 好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存 + */ + uint32_t kbm_length = kernel_free_pages / 8; // Kernel BitMap的长度,位图中的一位表示一页,以字节为单位 + uint32_t ubm_length = user_free_pages / 8; // User BitMap的长度. + + uint32_t kp_start = used_mem; // kernel pool start 内核内存池的起始位置 + uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; + + kernel_pool.phy_addr_start = kp_start; + user_pool.phy_addr_start = up_start; + + kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;// 位图字节长度 + user_pool.pool_bitmap.btmp_bytes_len = ubm_length; + + kernel_pool.pool_size = kernel_free_pages * PG_SIZE; + user_pool.pool_size = user_free_pages * PG_SIZE; + +/********* 内核内存池和用户内存池位图 *********** + * 位图是全局的数据,长度不固定。 + * 全局或静态的数组需要在编译时知道其长度, + * 而我们需要根据总内存大小算出需要多少字节。 + * 所以改为指定一块内存来生成位图. + * ************************************************/ +// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右) +// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处. + kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE; + + // 用户内存池的位图紧跟在内核内存池位图后面 + user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length); + /******************** 输出内存池信息 **********************/ + put_str(" kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits); + put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start); + put_str("\n"); + put_str(" user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits); + put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start); + put_str("\n"); + + /**************** 进行初始化 ***************/ + // 将位图 置为 0 表示还未分配 + bitmap_init(&kernel_pool.pool_bitmap); + bitmap_init(&user_pool.pool_bitmap); + + // 初始化内核的虚拟地址 + // 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致 + kernel_addr.vaddr_bitmap.btmp_bytes_len = kbm_length; + // 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之后 + kernel_addr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); + // 虚拟内存池的起始地址,以达到进程在堆中动态申请内存 + kernel_addr.vaddr_start = K_HEAP_START; //内核堆 + bitmap_init(&kernel_addr.vaddr_bitmap); + put_str(" mem_pool_init done\n"); +} + +// 内存管理部分初始化入口 +void mem_init() { + put_str("mem_init start!\n"); + uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); // 储存机器上物理内存总量 + mem_pool_init(mem_bytes_total); //初始化内存池 + put_str("mem_init done!\n"); +} \ No newline at end of file diff --git a/c9/thread_schedule/kernel/memory.h b/c9/thread_schedule/kernel/memory.h new file mode 100755 index 0000000..405f8d7 --- /dev/null +++ b/c9/thread_schedule/kernel/memory.h @@ -0,0 +1,33 @@ +#ifndef __KERNEL_MEMORY_H +#define __KERNEL_MEMORY_H + +#include "stdint.h" +#include "bitmap.h" + +// 内存池的标记,用来判断使用哪个内存池 +enum pool_flags { + PF_KERNEL = 1, // 内核内存池 + PF_USER = 2 // 用户内存池 +}; + +#define PG_P_1 1 // 页表项或页目录项存在属性位 +#define PG_P_0 0 // 页表项或页目录项存在属性位 +#define PG_RW_R 0 // R/W 属性位值, 读/执行 +#define PG_RW_W 2 // R/W 属性位值, 读/写/执行 +#define PG_US_S 0 // U/S 属性位值, 系统级 +#define PG_US_U 4 // U/S 属性位值, 用户级 + +// 虚拟地址池,用于虚拟地址管理 +struct virtual_addr{ + struct bitmap vaddr_bitmap; // 虚拟地址用到的 bitmap 结构, 页 为单位 + uint32_t vaddr_start; // 虚拟地址起始地址 +}; + +extern struct pool kernel_pool, user_pool; +void mem_init(void); +void* get_kernel_pages(uint32_t pg_cnt); +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt); +void malloc_init(void); +uint32_t* pte_ptr(uint32_t vaddr); +uint32_t* pde_ptr(uint32_t vaddr); +#endif \ No newline at end of file diff --git a/c9/thread_schedule/lib/kernel/bitmap.c b/c9/thread_schedule/lib/kernel/bitmap.c new file mode 100755 index 0000000..323b793 --- /dev/null +++ b/c9/thread_schedule/lib/kernel/bitmap.c @@ -0,0 +1,82 @@ +#include "bitmap.h" +#include "stdint.h" +#include "string.h" +#include "print.h" +#include "interrupt.h" +#include "debug.h" + +// bitmap 的初始化 +void bitmap_init(struct bitmap* btmp) { + memset(btmp->bits, 0, btmp->btmp_bytes_len); +} + +// 判断 btmp 为指针处,bit_idx 位是否为 1,为 1 就返回 true +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) { + uint32_t byte_idx = bit_idx / 8;//用于标注字节 + uint32_t bit_odd = bit_idx % 8; //用于标注字节内的位 + return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd)); +} + +// 在位图中申请连续cnt个位,成功则返回其起始位下标,失败返回-1 +int bitmap_scan(struct bitmap* btmp, uint32_t cnt) { + uint32_t idx_byte = 0; // 用于记录空闲位所在的字节 + // 先逐字节比较,蛮力法 + // 0 表示空闲,若停止,则说明至少有一个空闲位 + while(( 0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) { + idx_byte++; + } + ASSERT(idx_byte < btmp->btmp_bytes_len);// 断言此时在内部 + if(idx_byte == btmp->btmp_bytes_len) { + return -1;// 访问失败,返回 -1 + } + + /* 若在位图数组范围内的某字节内找到了空闲位, + * 在该字节内逐位比对,返回空闲位的索引。 + */ + int idx_bit = 0;//字节内部位数 + while((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) { + idx_bit++; + }// 找到空闲位 + int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在bitmap 下标 + if(cnt == 1) { + return bit_idx_start; + } + + // 至此说明 cnt > 1 + // 因此首先进行剩余位数的判断,以免超过bitmap记录数 + uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start); + uint32_t idx_next_bit = bit_idx_start + 1; + uint32_t free_bit_cnt = 1;// 一定要记得记录一下 之前判断后 还有个空闲位 + + bit_idx_start = -1; // 先将其置为 -1,若找不到连续的位就直接返回 + while(bit_left-- > 0) { + // 调用 scan_test 函数,为 0 则为 false + if(!(bitmap_scan_test(btmp, idx_next_bit))) { + free_bit_cnt++; + } else { // 由于必须是连续的,因此只要遇到 不空闲的 位 便将记录cnt置为 0 + free_bit_cnt = 0; + } + // 若是已经满足空闲数 + if(free_bit_cnt == cnt) { + bit_idx_start = idx_next_bit - cnt + 1; + break; + } + idx_next_bit++;// 继续判断下一位 + } + + return bit_idx_start; +} + +// 将位图btmp的bit_idx位设置为value +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) { + ASSERT((value ==0) || (value == 1));// 只能赋予 0 或 1 + uint32_t byte_idx = bit_idx / 8; //字节位 + uint32_t bit_odd = bit_idx % 8; //位数 位 + + if(value) { + btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); + } else { + btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); + } + +} diff --git a/c9/thread_schedule/lib/kernel/bitmap.h b/c9/thread_schedule/lib/kernel/bitmap.h new file mode 100755 index 0000000..ab3d2db --- /dev/null +++ b/c9/thread_schedule/lib/kernel/bitmap.h @@ -0,0 +1,18 @@ +#ifndef __LIB_KERNEL_BITMAP_H +#define __LIB_KERNEL_BITMAP_H +#include "global.h" +#define BITMAP_MASK 1 // 用在位图中逐位判断,主要通过 按位与 & +struct bitmap{ + uint32_t btmp_bytes_len; + /* 在遍历位图时,整体上以字节为单位,细节上是以位为单位, + 所以此处位图的指针必须是单字节 */ + uint8_t* bits; +}; + +// 函数 +void bitmap_init(struct bitmap* btmp); +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx); +int bitmap_scan(struct bitmap* btmp, uint32_t cnt); +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value); + +#endif \ No newline at end of file diff --git a/c9/thread_schedule/lib/kernel/io.h b/c9/thread_schedule/lib/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c9/thread_schedule/lib/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c9/thread_schedule/lib/kernel/list.c b/c9/thread_schedule/lib/kernel/list.c new file mode 100755 index 0000000..17dbd76 --- /dev/null +++ b/c9/thread_schedule/lib/kernel/list.c @@ -0,0 +1,95 @@ +#include "list.h" +#include "interrupt.h" + +// 初始化双向链表 +void list_init (struct list* list) { + list->head.prev = NULL; + list->tail.next = NULL; + + list->head.next = &list->tail; + list->tail.prev = &list->head; +} + +// 将链表元素 before 插入在元素 elem 前 +void list_insert_before(struct list_elem* elem, struct list_elem* before) { + // 由于队列是公共资源,因此要保证为原子操作 + enum intr_status old_status = intr_disable();//关中断 + + // 更新节点 + elem->prev->next = before; + before->prev = elem->prev; + elem->prev = before; + before->next = elem; + + intr_set_status(old_status); +} + +void list_append(struct list* plist, struct list_elem* elem) { + list_insert_before(&plist->tail, elem);// 队列的FIFO +} + +void list_remove(struct list_elem* pelem) { + enum intr_status old_status = intr_disable(); + + pelem->prev->next = pelem->next; + pelem->next->prev = pelem->prev; + + intr_set_status(old_status);// 返回原状态 +} + +void list_push(struct list* plist, struct list_elem* elem) { + list_insert_before(plist->head.next, elem); +} + +// pop 操作,为啥没有关中断? +struct list_elem* list_pop(struct list* plist) { + struct list_elem* elem = plist->head.next; + list_remove(elem); + return elem; +} + +// 查找元素 +bool elem_find(struct list* plist, struct list_elem* obj_elem) { + struct list_elem* elem = plist->head.next; + while(elem != &plist->tail) { + if(elem == obj_elem) { + return true; + } + elem = elem->next; + } + return false; +} + +// 遍历列表的所有元素,判断是否有 elem 满足条件 +// 判断方法采用 func 回调函数进行判断 +struct list_elem* list_traversal(struct list* plist, function func, int arg) { + struct list_elem* elem = plist->head.next; + // 如果队列为空 直接 return + if(list_empty(plist)) { + return NULL; + } + while(elem != &plist->tail) { + if(func(elem, arg)) { + return elem; + } + elem = elem->next; + } + return NULL; +} + +// 判断空 +bool list_empty(struct list* plist) { + return (plist->head.next == &plist->tail ? true : false); +} + +uint32_t list_len(struct list* plist) { + struct list_elem* elem = plist->head.next; + uint32_t len = 0; + while(elem != &plist->tail) { + elem = elem->next; + len++; + } + return len; +} + + diff --git a/c9/thread_schedule/lib/kernel/list.h b/c9/thread_schedule/lib/kernel/list.h new file mode 100755 index 0000000..3bd6ba0 --- /dev/null +++ b/c9/thread_schedule/lib/kernel/list.h @@ -0,0 +1,40 @@ +#ifndef __LIB_KERNEL_LIST_H +#define __LIB_KERNEL_LIST_H +#include "global.h" + + +// 令基址为 0 ,那么偏移量就等于 结构体中元素的偏移值 +#define offset(struct_type, member) (int)(&((struct_type*)0)->member) +// 通过结构体内部指针 转换为 代表该结构体 的方法: +// 将 elem_ptr(结构体内部元素指针) 的地址减去 elem_ptr 在结构体内部的偏移量 +// 从而获取所在结构体的地址,再将该地址转换为 struct 类型 +// struct_member_name 为内部变量名,主要用于 offset 函数 获取结构体内偏移值 +#define elem2entry(struct_type, struct_member_name, elem_ptr) \ + (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name)) +// 节点,不需要 data 域 +struct list_elem { + struct list_elem* prev;// 前驱 + struct list_elem* next;// 后继 +}; + +// 链表结构,用来实现队列 +struct list { + struct list_elem head;// 头指针 + struct list_elem tail;// 尾指针 +}; + +typedef bool (function)(struct list_elem*, int arg); + +void list_init (struct list*); +void list_insert_before(struct list_elem* elem, struct list_elem* before); +void list_push(struct list* plist, struct list_elem* elem); +void list_iterate(struct list* plist); +void list_append(struct list* plist, struct list_elem* elem); +void list_remove(struct list_elem* pelem); +struct list_elem* list_pop(struct list* plist); +bool list_empty(struct list* plist); +uint32_t list_len(struct list* plist); +struct list_elem* list_traversal(struct list* plist, function func, int arg); +bool elem_find(struct list* plist, struct list_elem* obj_elem); + +#endif \ No newline at end of file diff --git a/c9/thread_schedule/lib/kernel/print.S b/c9/thread_schedule/lib/kernel/print.S new file mode 100755 index 0000000..af4424f --- /dev/null +++ b/c9/thread_schedule/lib/kernel/print.S @@ -0,0 +1,239 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret + +global set_cursor +set_cursor: + pushad + mov bx, [esp+36] +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + popad + ret \ No newline at end of file diff --git a/c9/thread_schedule/lib/kernel/print.h b/c9/thread_schedule/lib/kernel/print.h new file mode 100755 index 0000000..97b3896 --- /dev/null +++ b/c9/thread_schedule/lib/kernel/print.h @@ -0,0 +1,9 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +void set_cursor(uint32_t cursor_pos); +#endif + diff --git a/c9/thread_schedule/lib/stdint.h b/c9/thread_schedule/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c9/thread_schedule/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c9/thread_schedule/lib/string.c b/c9/thread_schedule/lib/string.c new file mode 100755 index 0000000..ab47322 --- /dev/null +++ b/c9/thread_schedule/lib/string.c @@ -0,0 +1,121 @@ +#include "string.h" +#include "global.h" +#include "debug.h" + +/* 进行初始化 将dst_起始的size个字节置为value */ +// 通常用于内存分配时的数据清 0 +void memset(void* dst_, uint8_t value, uint32_t size) { + ASSERT(dst_ != NULL); + uint8_t* dst = (uint8_t*)dst_; + while (size-- > 0){ + *dst++ = value; + } +} + +/* 将src_起始的size个字节复制到dst_ */ +void memcpy(void* dst_, const void* src_, uint32_t size) { + ASSERT(dst_ != NULL && src_ != NULL); + uint8_t* dst = dst_; + const uint8_t* src = src_; + while(size-- > 0) { + *dst++ = *src++; + } +} + +/* 连续比较以地址a_和地址b_开头的size个字节, + 若相等则返回0,若a_大于b_返回+1,否则返回-1 */ +int memcmp(const void* a_, const void* b_, uint32_t size) { + const char* a = a_; + const char* b = b_; + ASSERT(a != NULL && b != NULL); + while(size-- > 0) { + if(*a != *b) { + return *a > *b ? 1 : -1; + } + a++; + b++; + } + // 至此说明相等 + return 0; +} + +/* 将字符串从src_复制到dst_ */ +char* strcpy(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* temp = dst_; // 暂存地址 + // 以 字符串结尾 '0' 作为终止条件 + while((*dst_++ = *src_++)); + return temp; +} + + +/* 返回字符串长度 */ +uint32_t strlen(const char* str) { + ASSERT(str != NULL); + const char* end = str;// 标记结尾处位置 + // 和strcpy 函数一样,遇到 '0'停止 + while(*end++); + return (end - str - 1); +} +/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */ +int8_t strcmp (const char* a, const char* b) { + ASSERT(a != NULL && b != NULL); + while(*a != 0 && *a == *b) { + a++; + b++; + } + // 如果 < 就返回 -1 + // 如果 >= 就前往 布尔式 + // 该布尔式表示,要是 > 就 返回 1, 否则只可能是 = ,布尔值刚好是 0 + return *a < *b ? -1 : *a > *b; +} + +/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + while(*str != 0) { + if(*str == ch) { + return (char*) str;// 强制转换 + } + str++; + } + return NULL; +} + +/* 从右往左查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strrchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + const char* last = NULL; + // 从左往右遍历,将 last 更新即可,这样便不用手动判断 '0' + while(*str != 0) { + if(*str == ch) { + last = str; + } + str++; + } + return (char*)last; +} + +/* 将字符串src_拼接到dst_后,将回拼接的串地址 */ +char* strcat(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* str = dst_; + while(*str++);// 循环到末尾 + --str;//得到拼接位置 + while((*str++ = *src_++)); + return dst_; +} + +/* 在字符串str中查找指定字符ch出现的次数 */ +uint32_t strchrs(const char* str, uint8_t ch) { + ASSERT(str != NULL); + uint32_t cnt = 0; + const char* temp = str; + while(*temp != 0) { + if(*temp == ch) { + cnt++; + } + temp++; + } + return cnt; +} \ No newline at end of file diff --git a/c9/thread_schedule/lib/string.h b/c9/thread_schedule/lib/string.h new file mode 100755 index 0000000..e69de29 diff --git a/c9/thread_schedule/makefile b/c9/thread_schedule/makefile new file mode 100644 index 0000000..4a634ad --- /dev/null +++ b/c9/thread_schedule/makefile @@ -0,0 +1,112 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o \ + $(BUILD_DIR)/string.o $(BUILD_DIR)/thread.o $(BUILD_DIR)/switch.o \ + $(BUILD_DIR)/list.o + + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/string.o: lib/string.c lib/string.h lib/stdint.h kernel/global.h \ + lib/stdint.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ +kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ + lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h kernel/global.h lib/stdint.h \ + kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h \ +kernel/global.h kernel/global.h kernel/debug.h lib/kernel/print.h \ +lib/kernel/io.h kernel/interrupt.h lib/string.h lib/stdint.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h lib/stdint.h \ +kernel/global.h lib/kernel/bitmap.h kernel/memory.h lib/string.h \ + lib/stdint.h lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + + + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: boot/mbr.S + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: boot/loader.S + $(AS) $(ASIB) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/switch.o: thread/switch.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean tran all + +MK_JS := $(shell mkdir $(BUILD_DIR)) + +mk_dir: + if [[ ! -d $(BUILD_DIR) ]];then MK_JS;fi + +hd: + # echo ".....Writing mbr, loader and kernel to disk....." + dd if=$(BUILD_DIR)/mbr.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=/home/husharp/bochs_hu/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + +CP_JS := $(shell cd $(BUILD_DIR) && rm -f ./*) + +clean: + $(CP_JS) + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + # echo ".....build kernel now....." + +all: mk_dir build hd tran + +tran: + cd /home/husharp/bochs_hu/bochs/ && bin/bochs -f bochsrc.disk diff --git a/c9/thread_schedule/test_pra/thread_test.c b/c9/thread_schedule/test_pra/thread_test.c new file mode 100755 index 0000000..11567c1 --- /dev/null +++ b/c9/thread_schedule/test_pra/thread_test.c @@ -0,0 +1,2 @@ +#include +#include diff --git a/c9/thread_schedule/thread/switch.S b/c9/thread_schedule/thread/switch.S new file mode 100755 index 0000000..27705cd --- /dev/null +++ b/c9/thread_schedule/thread/switch.S @@ -0,0 +1,37 @@ +[bits 32] +section .text +global switch_to +; switch_to 函数接收两个参数 +; 一个是当前线程 cur, 一个是下一个处理器线程 +; 因此该函数的作用是 保存当前线程,将下一个线程放到处理器中 +; +; struct thread_stack { +; uint32_t ebp; +; uint32_t ebx; +; uint32_t edi; +; uint32_t esi; +; +; 易知反向压栈 +switch_to: + ; 第一部分:保存中断前的上下文环境 + ; 压入到内核栈,因此之后将内核栈储存 + push esi + push edi + push ebx + push ebp + +; struct task_struct { +; uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + + mov eax, [esp + 20] ; 得到栈中参数 4*4 + 4 即 4 个压栈 和 1 个返回地址 + mov [eax], esp ; eax 指的是 PCB 的首地址,而第一个参数是 每个线程的内核栈 + + ; 往处理器上装载调度的新线程上下文 + mov eax, [esp + 24] ; 同理 + mov esp, [eax] + + pop ebp ; 此时恢复的是:新线程内核栈内部的上下文 + pop ebx + pop edi + pop esi + ret \ No newline at end of file diff --git a/c9/thread_schedule/thread/thread.c b/c9/thread_schedule/thread/thread.c new file mode 100755 index 0000000..0a0e61f --- /dev/null +++ b/c9/thread_schedule/thread/thread.c @@ -0,0 +1,170 @@ +#include "thread.h" +#include "stdint.h" +#include "string.h" +#include "global.h" +#include "debug.h" +#include "interrupt.h" +#include "print.h" +#include "memory.h" + +#define PG_SIZE 4096 + +// 定义主线程的 PCB,因为进入内核后,实则上一直执行的是 main 函数 +struct task_struct* main_thread; // 主线程 PCB +struct list thread_ready_list; // 就绪队列 +// 当线程不为就绪态时,会从所有线程队列中找到它 +struct list thread_all_list; // 所有线程队列 +// 队列是以 elem 的形式储存在 list 队列中,因此需要一个 elem 变量来将其取出转换 +static struct list_elem* thread_tag; // 用于保存队列中的线程节点 + +// switch_to函数的外部声明 global +extern void switch_to(struct task_struct* cur, struct task_struct* next); + + +// 取当前线程的 PCB 指针 +struct task_struct* running_thread(void) { + uint32_t esp; + asm("mov %%esp, %0" : "=g"(esp)); + // 取 esp 的前20位,PCB 的栈顶就为 0级栈 + return (struct task_struct*)(esp & 0xfffff000); +} + +// 实现任务调度 读写就绪队列 +void schedule(void) { + ASSERT(intr_get_status() == INTR_OFF); + + struct task_struct* cur = running_thread();// 取出当前线程的PCB + if(cur->status == TASK_RUNNING) {// 只是时间片为 0 ,而非阻塞 + ASSERT(!elem_find(&thread_ready_list, &cur->general_tag)); + list_append(&thread_ready_list, &cur->general_tag); + // 现将当前线程的 ticks 再次赋为 prio + cur->ticks = cur->priority; + cur->status = TASK_READY; + } + //由于还未实现 idle 线程,因此可能出现 ready_list 中无线程可调度的情况 + // 因此先进行断言 ready 队列中是否存在元素 + ASSERT(!list_empty(&thread_ready_list)); + thread_tag = NULL; // 首先将全局变量清空 + // 将就绪进程中的第一个线程(头结点)弹出 + thread_tag = list_pop(&thread_ready_list); + // 现在获得了 PCB 的 elem 节点,需要将其转换为 PCB + struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag); + next->status = TASK_RUNNING;// 调度 + switch_to(cur, next); + +} + +// 不能按照以往的函数调用, 比如 kernel_thread(function, func_arg) +// 因为 我们此处采用的是 ret 返回,而不是 call , +// 因此需要采用存储到 kernel_thread 的栈中,存储参数和占位返回地址的方式 + +/* 由kernel_thread去执行function(func_arg) */ +static void kernel_thread(thread_func* function, void* func_arg) { + // 由于线程的运行是由调度器中断调度,进入中断后,处理器会自动关中断。 + // 执行 function 前开中断,避免之后的时钟中断被屏蔽,从而无法调度其他线程 + intr_enable(); + function(func_arg); // 调用 function +} + +// 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) { + // 由于 init_thread 中,已经指向最顶端了 + // 先预留中断使用栈的空间,可见 thread.h 中定义的结构 + // 中断栈用于保存中断时的上下文,实现用户进程时,初始值也会放在中断栈 + pthread->self_kstack -= sizeof(struct intr_stack); + + // 再留出线程栈空间 + pthread->self_kstack -= sizeof(struct thread_stack); + + // 定义线程栈指针 + struct thread_stack* kthread_stack = (struct thread_stack*) pthread->self_kstack; + + // 为 kernel_thread 中 能调用 function 做准备 + // kernel_thread 是第一个函数, eip直接指向它,然后再调用其他的 function + kthread_stack->eip = kernel_thread; + kthread_stack->function = function; + kthread_stack->func_arg = func_arg; + // 初始化为 0 ,在还未执行函数前,寄存器不应该有值 + kthread_stack->ebp = kthread_stack->ebx = \ + kthread_stack->edi = kthread_stack->esi = 0; +} + + +// 初始化线程基本信息 +void init_thread(struct task_struct* pthread, char* name, int prio) { + // + memset(pthread, 0, sizeof(*pthread)); + + // 将主函数也封装为一个线程,且由于其一直运行,因此状态赋为 Running + if(pthread == main_thread) { + pthread->status = TASK_RUNNING; + } else { + pthread->status = TASK_READY; + } + + strcpy(pthread->name, name); + // 此处是为演习,因此直接将 状态置为 running + pthread->priority = prio; + pthread->ticks = prio; + pthread->elapsed_ticks = 0; //表示还未执行过 + pthread->pgdir = NULL; //线程没有自己的地址空间 + pthread->stack_magic = 0x20000611; //自定义一个魔数 + // self_kstack 是线程自己在内核态下使用的栈顶地址,指向最顶端 + pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE); +} + +// 线程创建函数 +//创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) { + // pcb 位于内核空间,包括用户进程的 pcb 也是在内核空间中 + + // 先通过 内核物理页申请函数 申请一页 + struct task_struct* thread = get_kernel_pages(1); + // 由于 get_kernel_page 获取的是起始位置,因此获取的是 pcb 最低地址 + // 初始化新建线程 + init_thread(thread, name, prio); + // 创建线程 + thread_create(thread, function, func_arg); + // 至此 线程得到初始化和创建后,需要加入到就绪队列和全局队列中 + + // 首先需要判断不在全局队列中 + ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag)); + // 加入就绪队列 + list_append(&thread_all_list, &thread->all_list_tag); + + // 首先需要判断不在就绪队列中 + ASSERT(!elem_find(&thread_ready_list, &thread->general_tag)); + // 加入就绪队列 + list_append(&thread_ready_list, &thread->general_tag); + + // 简陋版本(以后改为 switch_to + // 作用为:开启线程 + // 由于 thread_create 中将 self_kstack 指向线程栈的最低处,现在将 self_kstack 作为栈顶 + // ret 操作时,由于 thread_create 函数中,将 eip 指向了 kernel_thread,因此 ret时,会跳转到该函数 + return thread; +} + + +// 将kernel中的main函数完善为主线程 +static void make_main_thread(void) { + // 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000, + // 就是为其预留了tcb,地址为0xc009e000,因此不需要通过get_kernel_page另分配一页 + // 直接 init_thread 即可 + main_thread = running_thread();// 获取当前的PCB指针 + init_thread(main_thread, "main", 31); + + // main函数是当前线程,当前线程不在thread_ready_list中 + // 只用加到 全局线程队列中 + ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag)); + list_append(&thread_all_list, &main_thread->all_list_tag); +} + +// 初始化线程环境 +void thread_environment_init(void) { + put_str("thread_init start!\n"); + list_init(&thread_ready_list); + list_init(&thread_all_list); + // 将 main 函数创建为 线程 + make_main_thread(); + put_str("thread_init done!\n"); +} \ No newline at end of file diff --git a/c9/thread_schedule/thread/thread.h b/c9/thread_schedule/thread/thread.h new file mode 100755 index 0000000..abe4250 --- /dev/null +++ b/c9/thread_schedule/thread/thread.h @@ -0,0 +1,106 @@ +#ifndef __THREAD_THREAD_H +#define __THREAD_THREAD_H +#include "stdint.h" +#include "list.h" + +// 自定义通用函数类型,它将在很多线程函数中做为形参类型 +typedef void thread_func(void*); + +// 进程或线程的状态 +enum task_status { + TASK_RUNNING, + TASK_READY, + TASK_BLOCKED, + TASK_WAITING, + TASK_HANGING, + TASK_DIED +}; + +/*********** 中断栈intr_stack *********** + * 此结构用于中断发生时保护程序(线程或进程)的上下文环境: + * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 + * 寄存器, intr_exit中的出栈操作是此结构的逆操作 + * 此栈在线程自己的内核栈中位置固定,所在页的最顶端 +********************************************/ +struct intr_stack { + uint32_t vec_no; // kernel.S 宏VECTOR中push %1压入的中断号 + uint32_t edi; + uint32_t esi; + uint32_t ebp; + // 虽然pushad把esp也压入,但esp是不断变化的,所以会被popad忽略 + uint32_t esp_dummy; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + +/* 以下由cpu从低特权级进入高特权级时压入 */ + uint32_t err_code; // err_code会被压入在eip之后 + void (*eip) (void); + uint32_t cs; + uint32_t eflags; + void* esp; + uint32_t ss; +}; + +/*********** 线程栈thread_stack *********** + * 线程自己的栈,用于存储线程中待执行的函数 + * 此结构在线程自己的内核栈中位置不固定, + * 用在switch_to时保存线程环境。 + * 实际位置取决于实际运行情况。 + ******************************************/ +struct thread_stack { + uint32_t ebp; + uint32_t ebx; + uint32_t edi; + uint32_t esi; + + /* 线程第一次执行时,eip指向待调用的函数kernel_thread + 其它时候,eip是指向switch_to的返回地址*/ + void (*eip) (thread_func* func, void* func_arg); + + /***** 以下仅供第一次被调度上cpu时使用 ****/ + // 占位函数,迷惑 ret ,假装为返回地址,实则为获取参数提供栈顶位置 + // 用于找到函数参数位置 + void (*unused_retaddr); + thread_func* function; // 由 Kernel_thread 所调用的函数名 + void* func_arg; // 由 Kernel_thread 所调用函数所需要的的参数 +}; + +// 进程或线程的 pcb, 程序控制块 +// ticks 表示的是中断次数 +struct task_struct { + uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + char name[16]; // 记录任务的名字 + enum task_status status; // 线程状态 + uint8_t priority; // 线程优先级 + uint8_t ticks; // 每次在处理器上执行的时间滴答数 + // 已经执行的滴答数 + uint32_t elapsed_ticks;// elapsed : 流逝 + // 加入到就绪队列中 + struct list_elem general_tag; + // 值得注意的是 由于需要管控所有的线程资源, + // 但是一个 list_elem 只有两个指针,因此只能位于一个队列中 + // 所以创建新的队列 all_list 记录全部线程 + struct list_elem all_list_tag; + + uint32_t* pgdir; // 进程拥有独立地址空间(页表),线程为NULL + + // 通过判断栈的边界溢出魔数值是否发生变化 + uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出 + + +}; + +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg); +void init_thread(struct task_struct* pthread, char* name, int prio); +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg); +struct task_struct* running_thread(void); +void schedule(void); +void thread_environment_init(void); + +#endif \ No newline at end of file diff --git a/c9/thread_schedule/tmp b/c9/thread_schedule/tmp new file mode 100755 index 0000000..b76e9fe --- /dev/null +++ b/c9/thread_schedule/tmp @@ -0,0 +1,206 @@ +#include "interrupt.h" +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define EFLAGS_IF 0x00000200 // eflags 寄存器的 if 位为 1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + // 处理伪中断,,无法通过 IMR 寄存器屏蔽,因此单独处理 + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + // 将光标置为 0 ,从屏幕左上角清出一片打印异常信息的区域 + set_cursor(0);// 有时候甚至可能中断错误是由光标错误引起 + // 因此先将左上角空出四行来 + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + // 至此 已经将左上角空出四行,因此将光标再移到 0 位置,进行输出 + set_cursor(0); + put_str("!!!!!! exception message begin !!!!!!"); + set_cursor(88);// 第二行第 8 列 + put_str(intr_name[vec_nr]); + if(vec_nr == 14) { // 若为 pagefault,将缺失的地址打印出来 + int page_fault_vaddr = 0; + // 发生 pagefult + asm("movl %%cr2, %0" : "=r" (page_fault_vaddr));// cr2 是存放造成 page_fault 的地址 + put_str("\npage fault addr is "); + put_int(page_fault_vaddr); + } + put_str("!!!!!! exception message end !!!!!!"); + // 能进入中断处理程序就表示已经处在关中断情况下 + // 不会出现 +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void +exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断, sti 指令将 IF 位 置 1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli 指令将 IF 位 置 0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + // 判断 IF 位的值是否为 1 + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} + +// 在中断处理程序数组第 vector_no 个元素中注册安装中断处理程序 function +void register_handler(uint8_t vector_no, intr_handler function) { + idt_table[vector_no] = function; +} \ No newline at end of file diff --git a/c9/thread_start/boot/loader.S b/c9/thread_start/boot/loader.S new file mode 100755 index 0000000..d247cf5 --- /dev/null +++ b/c9/thread_start/boot/loader.S @@ -0,0 +1,380 @@ + %include "boot.inc" + section loader vstart=LOADER_BASE_ADDR +;构建gdt及其内部的描述符 + GDT_BASE: dd 0x00000000 + dd 0x00000000 + + CODE_DESC: dd 0x0000FFFF + dd DESC_CODE_HIGH4 + + DATA_STACK_DESC: dd 0x0000FFFF + dd DESC_DATA_HIGH4 + + VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7 + dd DESC_VIDEO_HIGH4 ; 此时dpl为0 + + GDT_SIZE equ $ - GDT_BASE + GDT_LIMIT equ GDT_SIZE - 1 + times 60 dq 0 ; 此处预留60个描述符的空位(slot) + SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 + SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 + SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 + + ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。 + ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900, + ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址 + total_mem_bytes dd 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址 + gdt_ptr dw GDT_LIMIT + dd GDT_BASE + + ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节 + ards_buf times 244 db 0 + ards_nr dw 0 ;用于记录ards结构体数量 + + loader_start: + +;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 ------- + + xor ebx, ebx ;第一次调用时,ebx值要为0 + mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变 + mov di, ards_buf ;ards结构缓冲区 +.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构 + mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。 + mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节 + int 0x15 + jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能 + add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置 + inc word [ards_nr] ;记录ARDS数量 + cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个 + jnz .e820_mem_get_loop + +;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。 + mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量 + mov ebx, ards_buf + xor edx, edx ;edx为最大的内存容量,在此先清0 +.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用 + mov eax, [ebx] ;base_add_low + add eax, [ebx+8] ;length_low + add ebx, 20 ;指向缓冲区中下一个ARDS结构 + cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 + jge .next_ards + mov edx, eax ;edx为总内存大小 +.next_ards: + loop .find_max_mem_area + jmp .mem_get_ok + +;------ int 15h ax = E801h 获取内存大小,最大支持4G ------ +; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位 +; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。 +.e820_failed_so_try_e801: + mov ax,0xe801 + int 0x15 + jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法 + +;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位 + mov cx,0x400 ;cx和ax值一样,cx用做乘数 + mul cx + shl edx,16 + and eax,0x0000FFFF + or edx,eax + add edx, 0x100000 ;ax只是15MB,故要加1MB + mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份 + +;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量 + xor eax,eax + mov ax,bx + mov ecx, 0x10000 ;0x10000十进制为64KB + mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax. + add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可 + mov edx,esi ;edx为总内存大小 + jmp .mem_get_ok + +;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ---------- +.e801_failed_so_try88: + ;int 15后,ax存入的是以kb为单位的内存容量 + mov ah, 0x88 + int 0x15 + jc .error_hlt + and eax,0x0000FFFF + + ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中 + mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位 + mul cx + shl edx, 16 ;把dx移到高16位 + or edx, eax ;把积的低16位组合到edx,为32位的积 + add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB + +.mem_get_ok: + mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。 + + +;----------------- 准备进入保护模式 ------------------- +;1 打开A20 +;2 加载gdt +;3 将cr0的pe位置1 + + ;----------------- 打开A20 ---------------- + in al,0x92 + or al,0000_0010B + out 0x92,al + + ;----------------- 加载GDT ---------------- + lgdt [gdt_ptr] + + ;----------------- cr0第0位置1 ---------------- + mov eax, cr0 + or eax, 0x00000001 + mov cr0, eax + + jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转, + ; 这将导致之前做的预测失效,从而起到了刷新的作用。 +.error_hlt: ;出错则挂起 + hlt + +[bits 32] +p_mode_start: + mov ax, SELECTOR_DATA + mov ds, ax + mov es, ax + mov ss, ax + mov esp,LOADER_STACK_TOP + mov ax, SELECTOR_VIDEO + mov gs, ax + +; ------------------------- 加载kernel ---------------------- + mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 + mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 + mov ecx, 200 ; 读入的扇区数 + + call rd_disk_m_32 + + ; 创建页目录及页表并初始化页内存位图 + call setup_page + + ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 + sgdt [gdt_ptr] ; 存储到原来gdt所有的位置 + + ;将gdt描述符中视频段描述符中的段基址+0xc0000000 + mov ebx, [gdt_ptr + 2] + or dword [ebx + 0x18 + 4], 0xc0000000 ;视频段是第3个段描述符,每个描述符是8字节,故0x18。 + ;段描述符的高4字节的最高位是段基址的31~24位 + + ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 + add dword [gdt_ptr + 2], 0xc0000000 + + add esp, 0xc0000000 ; 将栈指针同样映射到内核地址 + + ; 把页目录地址赋给cr3 + mov eax, PAGE_DIR_TABLE_POS + mov cr3, eax + + ; 打开cr0的pg位(第31位) + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + ;在开启分页后,用gdt新的地址重新加载 + lgdt [gdt_ptr] ; 重新加载 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此时不刷新流水线也没问题 ;;;;;;;;;;;;;;;;;;;;;;;; +;由于一直处在32位下,原则上不需要强制刷新,经过实际测试没有以下这两句也没问题. +;但以防万一,还是加上啦,免得将来出来莫句奇妙的问题. + jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt +enter_kernel: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call kernel_init + mov esp, 0xc009f000 + jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok + + +;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- +kernel_init: + xor eax, eax + xor ebx, ebx ;ebx记录程序头表地址 + xor ecx, ecx ;cx记录程序头表中的program header数量 + xor edx, edx ;dx 记录program header尺寸,即e_phentsize + + mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 + mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 + ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 + add ebx, KERNEL_BIN_BASE_ADDR + mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header +.each_segment: + cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 + je .PTNULL + + ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) + push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size + mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset + add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 + push eax ; 压入函数memcpy的第二个参数:源地址 + push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 + call mem_cpy ; 调用mem_cpy完成段复制 + add esp,12 ; 清理栈中压入的三个参数 +.PTNULL: + add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header + loop .each_segment + ret + +;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ +;输入:栈中三个参数(dst,src,size) +;输出:无 +;--------------------------------------------------------- +mem_cpy: + cld + push ebp + mov ebp, esp + push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 + mov edi, [ebp + 8] ; dst + mov esi, [ebp + 12] ; src + mov ecx, [ebp + 16] ; size + rep movsb ; 逐字节拷贝 + + ;恢复环境 + pop ecx + pop ebp + ret + + +;------------- 创建页目录及页表 --------------- +setup_page: +;先把页目录占用的空间逐字节清0 + mov ecx, 4096 + mov esi, 0 +.clear_page_dir: + mov byte [PAGE_DIR_TABLE_POS + esi], 0 + inc esi + loop .clear_page_dir + +;开始创建页目录项(PDE) +.create_pde: ; 创建Page Directory Entry + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 + mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 + +; 下面将页目录项0和0xc00都存为第一个页表的地址, +; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, +; 这是为将地址映射为内核地址做准备 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. + mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3) + mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, + ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. + sub eax, 0x1000 + mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 + +;下面创建页表项(PTE) + mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 + mov esi, 0 + mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 +.create_pte: ; 创建Page Table Entry + mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 + add edx,4096 + inc esi + loop .create_pte + +;创建内核其它页表的PDE + mov eax, PAGE_DIR_TABLE_POS + add eax, 0x2000 ; 此时eax为第二个页表的位置 + or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为0 + mov ebx, PAGE_DIR_TABLE_POS + mov ecx, 254 ; 范围为第769~1022的所有目录项数量 + mov esi, 769 +.create_kernel_pde: + mov [ebx+esi*4], eax + inc esi + add eax, 0x1000 + loop .create_kernel_pde + ret + + +;------------------------------------------------------------------------------- + ;功能:读取硬盘n个扇区 +rd_disk_m_32: +;------------------------------------------------------------------------------- + ; eax=LBA扇区号 + ; ebx=将数据写入的内存地址 + ; ecx=读入的扇区数 + mov esi,eax ; 备份eax + mov di,cx ; 备份扇区数到di +;读写硬盘: +;第1步:设置要读取的扇区数 + mov dx,0x1f2 + mov al,cl + out dx,al ;读取的扇区数 + + mov eax,esi ;恢复ax + +;第2步:将LBA地址存入0x1f3 ~ 0x1f6 + + ;LBA地址7~0位写入端口0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA地址15~8位写入端口0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA地址23~16位写入端口0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba第24~27位 + or al,0xe0 ; 设置7~4位为1110,表示lba模式 + mov dx,0x1f6 + out dx,al + +;第3步:向0x1f7端口写入读命令,0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就能把这cx个扇区的数据读出来 + +;第4步:检测硬盘状态 + .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 + ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 + nop + in al,dx + and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 + cmp al,0x08 + jnz .not_ready ;若未准备好,继续等。 + +;第5步:从0x1f0端口读数据 + mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示命令使用, + ;在此先用这种方法,在后面内容会用到insw和outsw等 + + mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 + mul dx + mov cx, ax + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [ebx], ax + add ebx, 2 + ; 由于在实模式下偏移地址为16位,所以用bx只会访问到0~FFFFh的偏移。 + ; loader的栈指针为0x900,bx为指向的数据输出缓冲区,且为16位, + ; 超过0xffff后,bx部分会从0开始,所以当要读取的扇区数过大,待写入的地址超过bx的范围时, + ; 从硬盘上读出的数据会把0x0000~0xffff的覆盖, + ; 造成栈被破坏,所以ret返回时,返回地址被破坏了,已经不是之前正确的地址, + ; 故程序出会错,不知道会跑到哪里去。 + ; 所以改为ebx代替bx指向缓冲区,这样生成的机器码前面会有0x66和0x67来反转。 + ; 0X66用于反转默认的操作数大小! 0X67用于反转默认的寻址方式. + ; cpu处于16位模式时,会理所当然的认为操作数和寻址都是16位,处于32位模式时, + ; 也会认为要执行的指令是32位. + ; 当我们在其中任意模式下用了另外模式的寻址方式或操作数大小(姑且认为16位模式用16位字节操作数, + ; 32位模式下用32字节的操作数)时,编译器会在指令前帮我们加上0x66或0x67, + ; 临时改变当前cpu模式到另外的模式下. + ; 假设当前运行在16位模式,遇到0X66时,操作数大小变为32位. + ; 假设当前运行在32位模式,遇到0X66时,操作数大小变为16位. + ; 假设当前运行在16位模式,遇到0X67时,寻址方式变为32位寻址 + ; 假设当前运行在32位模式,遇到0X67时,寻址方式变为16位寻址. + + loop .go_on_read + ret diff --git a/c9/thread_start/boot/mbr.S b/c9/thread_start/boot/mbr.S new file mode 100755 index 0000000..7efd523 --- /dev/null +++ b/c9/thread_start/boot/mbr.S @@ -0,0 +1,126 @@ +;���������� +;------------------------------------------------------------ +%include "boot.inc" +SECTION MBR vstart=0x7c00 + mov ax,cs + mov ds,ax + mov es,ax + mov ss,ax + mov fs,ax + mov sp,0x7c00 + mov ax,0xb800 + mov gs,ax + +; ���� +;����0x06�Ź��ܣ��Ͼ�ȫ���У���������� +; ----------------------------------------------------------- +;INT 0x10 ���ܺ�:0x06 ��������:�Ͼ����� +;------------------------------------------------------ +;���룺 +;AH ���ܺ�= 0x06 +;AL = �Ͼ�������(���Ϊ0,��ʾȫ��) +;BH = �Ͼ������� +;(CL,CH) = �������Ͻǵ�(X,Y)λ�� +;(DL,DH) = �������½ǵ�(X,Y)λ�� +;�޷���ֵ�� + mov ax, 0600h + mov bx, 0700h + mov cx, 0 ; ���Ͻ�: (0, 0) + mov dx, 184fh ; ���½�: (80,25), + ; ��ΪVGA�ı�ģʽ�У�һ��ֻ������80���ַ�,��25�С� + ; �±��0��ʼ������0x18=24,0x4f=79 + int 10h ; int 10h + + ; ����ַ���:MBR + mov byte [gs:0x00],'1' + mov byte [gs:0x01],0xA4 + + mov byte [gs:0x02],' ' + mov byte [gs:0x03],0xA4 + + mov byte [gs:0x04],'M' + mov byte [gs:0x05],0xA4 ;A��ʾ��ɫ������˸��4��ʾǰ��ɫΪ��ɫ + + mov byte [gs:0x06],'B' + mov byte [gs:0x07],0xA4 + + mov byte [gs:0x08],'R' + mov byte [gs:0x09],0xA4 + + mov eax,LOADER_START_SECTOR ; ��ʼ����lba��ַ + mov bx,LOADER_BASE_ADDR ; д��ĵ�ַ + mov cx,4 ; ������������� + call rd_disk_m_16 ; ���¶�ȡ�������ʼ���֣�һ�������� + + jmp LOADER_BASE_ADDR + 0x300 + +;------------------------------------------------------------------------------- +;����:��ȡӲ��n������ +rd_disk_m_16: +;------------------------------------------------------------------------------- + ; eax=LBA������ + ; ebx=������д����ڴ��ַ + ; ecx=����������� + mov esi,eax ;����eax + mov di,cx ;����cx +;��дӲ��: +;��1��������Ҫ��ȡ�������� + mov dx,0x1f2 + mov al,cl + out dx,al ;��ȡ�������� + + mov eax,esi ;�ָ�ax + +;��2������LBA��ַ����0x1f3 ~ 0x1f6 + + ;LBA��ַ7~0λд��˿�0x1f3 + mov dx,0x1f3 + out dx,al + + ;LBA��ַ15~8λд��˿�0x1f4 + mov cl,8 + shr eax,cl + mov dx,0x1f4 + out dx,al + + ;LBA��ַ23~16λд��˿�0x1f5 + shr eax,cl + mov dx,0x1f5 + out dx,al + + shr eax,cl + and al,0x0f ;lba��24~27λ + or al,0xe0 ; ����7��4λΪ1110,��ʾlbaģʽ + mov dx,0x1f6 + out dx,al + +;��3������0x1f7�˿�д������0x20 + mov dx,0x1f7 + mov al,0x20 + out dx,al + +;��4�������Ӳ��״̬ + .not_ready: + ;ͬһ�˿ڣ�дʱ��ʾд�������֣���ʱ��ʾ����Ӳ��״̬ + nop + in al,dx + and al,0x88 ;��4λΪ1��ʾӲ�̿�������׼�������ݴ��䣬��7λΪ1��ʾӲ��æ + cmp al,0x08 + jnz .not_ready ;��δ׼���ã������ȡ� + +;��5������0x1f0�˿ڶ����� + mov ax, di + mov dx, 256 + mul dx + mov cx, ax ; diΪҪ��ȡ����������һ��������512�ֽڣ�ÿ�ζ���һ���֣� + ; ����di*512/2�Σ�����di*256 + mov dx, 0x1f0 + .go_on_read: + in ax,dx + mov [bx],ax + add bx,2 + loop .go_on_read + ret + + times 510-($-$$) db 0 + db 0x55,0xaa diff --git a/c9/thread_start/device/timer.c b/c9/thread_start/device/timer.c new file mode 100755 index 0000000..af7221c --- /dev/null +++ b/c9/thread_start/device/timer.c @@ -0,0 +1,39 @@ +#include "time.h" +#include "io.h" +#include "kernel/print.h" + + +#define IRQ0_FREQUENCY 100 // 时钟频率 +#define INPUT_FREQUENCY 1193180 // 计数器原本的工作频率 +#define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY //计数器 0 初始值 +#define COUNTRER0_PORT 0x40 // 计数器 0 的接口 +#define COUNTER0_NO 0 // 计数器号码 +#define COUNTER_MODE 2 // 模式 2 +#define READ_WRITE_LATCH 3 // 读写方式,先读写低 8 位,再读写高 8 位 +#define PIT_CONTROL_PORT 0x43 // 控制器寄存器接口 + +/* + * 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode + * 写入模式控制寄存器 + * 并赋予初始值counter_value + * */ +static void frequency_set(uint8_t counter_port, \ + uint8_t counter_no, \ + uint8_t rwl, \ + uint8_t counter_mode, \ + uint16_t counter_value) { +/* 往控制字寄存器端口0x43中写入控制字 */ +// 最后一位为 0 ,即选择二进制 + outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1)); +/* 先写入counter_value的低8位 */ + outb(counter_port, (uint8_t)counter_value); +/* 后写入counter_value的高8位 */ + outb(counter_port, (uint8_t)counter_port >> 8); +} + +// 初始化 8253 +void timer_init() { + put_str("timer_init start!\n"); + frequency_set(COUNTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); + put_str("timer_init done!\n"); +} diff --git a/c9/thread_start/device/timer.h b/c9/thread_start/device/timer.h new file mode 100755 index 0000000..b83ed74 --- /dev/null +++ b/c9/thread_start/device/timer.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_TIME_H +#define __DEVICE_TIME_H +#include "stdint.h" +void timer_init(void); +#endif + diff --git a/c9/thread_start/include/boot.inc b/c9/thread_start/include/boot.inc new file mode 100755 index 0000000..6723479 --- /dev/null +++ b/c9/thread_start/include/boot.inc @@ -0,0 +1,56 @@ +;------------- loader和kernel ---------- + +LOADER_BASE_ADDR equ 0x900 +LOADER_STACK_TOP equ LOADER_BASE_ADDR +LOADER_START_SECTOR equ 0x2 + +KERNEL_BIN_BASE_ADDR equ 0x70000 +KERNEL_START_SECTOR equ 0x9 +KERNEL_ENTRY_POINT equ 0xc0001500 + +;------------- 页表配置 ---------------- +PAGE_DIR_TABLE_POS equ 0x100000 + +;-------------- gdt描述符属性 ----------- +DESC_G_4K equ 1_00000000000000000000000b +DESC_D_32 equ 1_0000000000000000000000b +DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 +DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 +DESC_LIMIT_CODE2 equ 1111_0000000000000000b +DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 +DESC_LIMIT_VIDEO2 equ 0000_000000000000000b +DESC_P equ 1_000000000000000b +DESC_DPL_0 equ 00_0000000000000b +DESC_DPL_1 equ 01_0000000000000b +DESC_DPL_2 equ 10_0000000000000b +DESC_DPL_3 equ 11_0000000000000b +DESC_S_CODE equ 1_000000000000b +DESC_S_DATA equ DESC_S_CODE +DESC_S_sys equ 0_000000000000b +DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. +DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. + +DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 +DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 +DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b + +;-------------- 选择子属性 --------------- +RPL0 equ 00b +RPL1 equ 01b +RPL2 equ 10b +RPL3 equ 11b +TI_GDT equ 000b +TI_LDT equ 100b + + +;---------------- 页表相关属性 -------------- +PG_P equ 1b +PG_RW_R equ 00b +PG_RW_W equ 10b +PG_US_S equ 000b +PG_US_U equ 100b + + +;------------- program type 定义 -------------- +PT_NULL equ 0 + diff --git a/c9/thread_start/kernel/debug.c b/c9/thread_start/kernel/debug.c new file mode 100755 index 0000000..bef6acf --- /dev/null +++ b/c9/thread_start/kernel/debug.c @@ -0,0 +1,18 @@ +#include "debug.h" +#include "kernel/print.h" +#include "interrupt.h" + +/* 打印文件名,行号,函数名,条件并使程序悬停 */ +void panic_spin(char* filename, \ + int line, \ + const char* func, \ + const char* condition) \ +{ + intr_disable(); /* 打印文件名,行号,函数名,条件并使程序悬停 */ + put_str("\n\n\n!!!!! error !!!!!\n"); + put_str("filename:");put_str(filename);put_str("\n"); + put_str("line:0x");put_int(line);put_str("\n"); + put_str("function:");put_str((char*)func);put_str("\n"); + put_str("condition:");put_str((char*)condition);put_str("\n"); + while(1); +} \ No newline at end of file diff --git a/c9/thread_start/kernel/debug.h b/c9/thread_start/kernel/debug.h new file mode 100755 index 0000000..d4df982 --- /dev/null +++ b/c9/thread_start/kernel/debug.h @@ -0,0 +1,24 @@ +#ifndef __KERNEL_DEBUG_H +#define __KERNEL_DEBUG_H + +void panic_spin(char* filename, int line, const char* func, const char* condition); + +/*************************** __VA_ARGS__ ******************************* + * __VA_ARGS__ 是预处理器所支持的专用标识符。 + * 代表所有与省略号相对应的参数. + * "..."表示定义的宏其参数可变. + */ +#define PANIC(...) panic_spin (__FILE__, __LINE__, __func__, __VA_ARGS__) +/***********************************************************************/ + +#ifdef NDEBUG + #define ASSERT(CONDITION) ((void)0) +#else + #define ASSERT(CONDITION) \ + if (CONDITION) {} else { \ + /* 符号#让编译器将宏的参数转化为字符串字面量 */ \ + PANIC(#CONDITION); \ + } +#endif // __NDEBUG + +#endif // __KERNEL_DEBUG_H \ No newline at end of file diff --git a/c9/thread_start/kernel/global.h b/c9/thread_start/kernel/global.h new file mode 100755 index 0000000..7330902 --- /dev/null +++ b/c9/thread_start/kernel/global.h @@ -0,0 +1,39 @@ +#ifndef _KERNEL_GLOBAL_H +#define _KERNEL_GLOBAL_H + +#include "stdint.h" + +# define RPL0 0 +# define RPL1 1 +# define RPL2 2 +# define RPL3 3 + +# define TI_GDT 0 +# define TI_LDT 1 + + +# define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) // 指向内核代码段的选择子 +# define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) +# define SELECTOR_K_STACK SELECTOR_K_DATA +# define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) + +/* IDT描述符属性 */ +# define IDT_DESC_P 1 +# define IDT_DESC_DPL0 0 +# define IDT_DESC_DPL3 3 +// S 都为 0 +# define IDT_DESC_32_TYPE 0xE // 32 位的门 +# define IDT_DESC_16_TYPE 0x6 // 16 位的门 + +# define IDT_DESC_ATTR_DPL0 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) + +# define IDT_DESC_ATTR_DPL3 \ + ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) + +#define NULL ((void*)0) +#define bool int +#define true 1 +#define false 0 + +#endif \ No newline at end of file diff --git a/c9/thread_start/kernel/init.c b/c9/thread_start/kernel/init.c new file mode 100755 index 0000000..995b338 --- /dev/null +++ b/c9/thread_start/kernel/init.c @@ -0,0 +1,13 @@ +#include "init.h" +#include "kernel/print.h" +#include "interrupt.h" +#include "../device/timer.h" +#include "memory.h" + +/*负责初始化所有模块 */ +void init_all() { + put_str("init_all start!\n"); + idt_init(); + timer_init(); + mem_init();//初始化内存管理系统 +} \ No newline at end of file diff --git a/c9/thread_start/kernel/init.h b/c9/thread_start/kernel/init.h new file mode 100755 index 0000000..bf575c9 --- /dev/null +++ b/c9/thread_start/kernel/init.h @@ -0,0 +1,4 @@ +#ifndef __KERNEL_INIT_H +#define __KERNEL_INIT_H +void init_all(void); +#endif diff --git a/c9/thread_start/kernel/interrupt.c b/c9/thread_start/kernel/interrupt.c new file mode 100755 index 0000000..c037c36 --- /dev/null +++ b/c9/thread_start/kernel/interrupt.c @@ -0,0 +1,201 @@ +#include "stdint.h" +#include "global.h" +#include "io.h" +#include "print.h" +#include "interrupt.h" + +#define IDT_DESC_CNT 0x21 // 现在支持的中断数 33 + +// 这里用的可编程中断控制器是8259A +#define PIC_M_CTRL 0x20 // 主片的控制端口是0x20 +#define PIC_M_DATA 0x21 // 主片的数据端口是0x21 +#define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 +#define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 + +#define EFLAGS_IF 0x00000200 // eflags 寄存器的 if 位为 1 +#define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAG_VAR)) + +// 中断门描述符结构体 +struct gate_desc{ + uint16_t func_offset_low_word; + uint16_t selector; + uint8_t dcount; // 表示双字计数段门描述符的第 4 字节,为固定值 + + uint8_t attribute; + uint16_t func_offset_high_word; +}; + +// 创建中断门描述符函数,参数:指针、属性、描述符对应的中断函数 +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function); +static struct gate_desc idt[IDT_DESC_CNT]; // 中断描述符数组 + +/** + * 中断的名称. + */ +char* intr_name[IDT_DESC_CNT]; // 用于保存异常名字的数组 +// 中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口, +// 最终调用的是ide_table中的处理程序 +intr_handler idt_table[IDT_DESC_CNT]; +// 声明引用定义在kernel.S中的中断处理函数入口数组 +extern intr_handler intr_entry_table[IDT_DESC_CNT]; + +/* 初始化可编程中断控制器8259A */ +static void pic_init(void) { + // 初始化主片 + outb(PIC_M_CTRL, 0x11); // ICW1:边沿触发, 级联8259, 需要 ICW4 + outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20, 也就是IR[0-7] 为 0x20 ~ 0x27. + outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 + outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI + + /* 初始化从片 */ + outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. + outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. + outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 + outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式,正常 EOI + + // 打开主片上 IR0 ,也就是目前只接受时钟中断 + outb(PIC_M_DATA, 0xfe); + outb(PIC_S_DATA, 0xff); + + put_str(" pic_init done!\n"); +} + +/** + * 创建中断门描述符. + */ +static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { + p_gdesc->func_offset_low_word = (uint32_t) function & 0x0000FFFF; + p_gdesc->selector = SELECTOR_K_CODE; + p_gdesc->dcount = 0; + p_gdesc->attribute = attr; + p_gdesc->func_offset_high_word = ((uint32_t) function & 0xFFFF0000) >> 16; +} + +/** + * 初始化中断描述符表. + */ +static void idt_desc_init(void) { + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); + } + put_str(" idt_desc_init done!\n"); +} + +/* 通用的中断处理函数,一般用在异常出现时的处理 */ +static void general_intr_handler(uint8_t vec_nr) { + // 处理伪中断,,无法通过 IMR 寄存器屏蔽,因此单独处理 + if(vec_nr == 0x27 || vec_nr == 0x2f) { + return; + } + // 将光标置为 0 ,从屏幕左上角清出一片打印异常信息的区域 + set_cursor(0);// 有时候甚至可能中断错误是由光标错误引起 + // 因此先将左上角空出四行来 + int cursor_pos = 0; + while(cursor_pos < 320) { + put_char(' '); + cursor_pos++; + } + // 至此 已经将左上角空出四行,因此将光标再移到 0 位置,进行输出 + set_cursor(0); + put_str("!!!!!! exception message begin !!!!!!"); + set_cursor(88);// 第二行第 8 列 + put_str(intr_name[vec_nr]); + if(vec_nr == 14) { // 若为 pagefault,将缺失的地址打印出来 + int page_fault_vaddr = 0; + // 发生 pagefult + asm("movl %%cr2, %0" : "=r" (page_fault_vaddr));// cr2 是存放造成 page_fault 的地址 + put_str("\npage fault addr is "); + put_int(page_fault_vaddr); + } + put_str("!!!!!! exception message end !!!!!!"); + // 能进入中断处理程序就表示已经处在关中断情况下 + // 不会出现 +} + +/* 完成一般中断处理函数注册及异常名称注册 */ +static void +exception_init(void) { // 完成一般中断处理函数注册及异常名称注册 + int i; + for (i = 0; i < IDT_DESC_CNT; i++){ + // 默认为general_intr_handler。 + // 以后会由register_handler来注册具体处理函数。 + idt_table[i] = general_intr_handler; + intr_name[i] = "unknown"; // 先统一赋值为unknown + } + intr_name[0] = "#DE Divide Error"; + intr_name[1] = "#DB Debug Exception"; + intr_name[2] = "NMI Interrupt"; + intr_name[3] = "#BP Breakpoint Exception"; + intr_name[4] = "#OF Overflow Exception"; + intr_name[5] = "#BR BOUND Range Exceeded Exception"; + intr_name[6] = "#UD Invalid Opcode Exception"; + intr_name[7] = "#NM Device Not Available Exception"; + intr_name[8] = "#DF Double Fault Exception"; + intr_name[9] = "Coprocessor Segment Overrun"; + intr_name[10] = "#TS Invalid TSS Exception"; + intr_name[11] = "#NP Segment Not Present"; + intr_name[12] = "#SS Stack Fault Exception"; + intr_name[13] = "#GP General Protection Exception"; + intr_name[14] = "#PF Page-Fault Exception"; + // intr_name[15] 第15项是intel保留项,未使用 + intr_name[16] = "#MF x87 FPU Floating-Point Error"; + intr_name[17] = "#AC Alignment Check Exception"; + intr_name[18] = "#MC Machine-Check Exception"; + intr_name[19] = "#XF SIMD Floating-Point Exception"; +} + +/* 开中断并返回开中断前的状态*/ +enum intr_status intr_enable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + return old_status; + } else { + old_status = INTR_OFF; + asm volatile("sti"); // 开中断, sti 指令将 IF 位 置 1 + return old_status; + } +} + +/* 关中断,并且返回关中断前的状态 */ +enum intr_status intr_disable() { + enum intr_status old_status; + if(INTR_ON == intr_get_status()) { + old_status = INTR_ON; + asm volatile("cli" : : : "memory"); // 关中断,cli 指令将 IF 位 置 0 + return old_status; + } else { + old_status = INTR_OFF; + return old_status; + } +} + + +/* 将中断状态设置为status */ +enum intr_status intr_set_status(enum intr_status status) { + return status & INTR_ON ? intr_enable() : intr_disable(); +} + +/* 获取当前中断状态 */ +enum intr_status intr_get_status() { + uint32_t eflags = 0; + GET_EFLAGS(eflags); + // 判断 IF 位的值是否为 1 + return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; +} + +/*完成有关中断的所有初始化工作*/ +void idt_init() { + put_str("idt_init start!\n"); + idt_desc_init(); // 初始化中断描述符表 + exception_init(); + pic_init(); // 初始化 8259A + + // 加载 idt + // 尽管 LDTR 为 48位,我们没有 48 位变量,但是,可以采用 64 位,提取前 48 位即可 + // 指针只能转换为 对应长度 整型,因此需要先转换为 32 位 + uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); + asm volatile("lidt %0" : : "m"(idt_operand));// m 表示 内存约束,实则是将idt_operand 地址传给 lidt + put_str("idt_init done!\n"); +} \ No newline at end of file diff --git a/c9/thread_start/kernel/interrupt.h b/c9/thread_start/kernel/interrupt.h new file mode 100755 index 0000000..f2882d6 --- /dev/null +++ b/c9/thread_start/kernel/interrupt.h @@ -0,0 +1,21 @@ +#ifndef __KERNEL_INTERRUPT_H +#define __KERNEL_INTERRUPT_H +#include "stdint.h" + +typedef void* intr_handler; +void idt_init(void); + +/* 定义中断的两种状态: + * INTR_OFF值为0,表示关中断, + * INTR_ON值为1,表示开中断 */ +enum intr_status { // 中断状态 + INTR_OFF, // 中断关闭 + INTR_ON // 中断打开 +}; + +enum intr_status intr_get_status(void); +enum intr_status intr_set_status (enum intr_status); +enum intr_status intr_enable (void); +enum intr_status intr_disable (void); + +#endif \ No newline at end of file diff --git a/c9/thread_start/kernel/kernel.S b/c9/thread_start/kernel/kernel.S new file mode 100755 index 0000000..e5010ca --- /dev/null +++ b/c9/thread_start/kernel/kernel.S @@ -0,0 +1,90 @@ +[bits 32] +; 对于CPU会自动压入错误码的中断类型,无需额外的操作 +%define ERROR_CODE nop +; 如果CPU没有压入错误码,为了保持处理逻辑的一致性,我们需要手动压入一个0 +%define ZERO push 0 + +# 调用 interrupt 中的函数来中断处理 +# extern put_str ; 声明外部函数 +extern idt_table ; idt_table 是 C 中注册的中断处理程序数组 + +section .data +global intr_entry_table +intr_entry_table: + +; 中断处理程序宏定义 +; 接受两个参数 +%macro VECTOR 2 +section .text +intr%1entry: ; %1 表示中断向量号,用于确定中断位置 + %2 ; 需要首先进行压入操作 nop / push 0 + + ; 保护上下文 + push ds + push es + push fs + push gs +; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + pushad + + ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI + mov al, 0x20 ; 中断结束命令 EOI + out 0xa0, al ; 向从片发送 + out 0x20, al ; 向主片发送 + +; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便 + push %1 ; 向量号 + call [idt_table + %1*4] ; 调用 idt_table 中的中断处理函数 + jmp intr_exit + +section .data + dd intr%1entry + +%endmacro + +section .text +global intr_exit +intr_exit: +; 以下是恢复上下文环境 + add esp, 4 ; 跳过中断号 + popad + pop gs + pop fs + pop es + pop ds + add esp, 4 ; 跳过error_code + iretd + +VECTOR 0x00, ZERO +VECTOR 0x01, ZERO +VECTOR 0x02, ZERO +VECTOR 0x03, ZERO +VECTOR 0x04, ZERO +VECTOR 0x05, ZERO +VECTOR 0x06, ZERO +VECTOR 0x07, ZERO +VECTOR 0x08, ZERO +VECTOR 0x09, ZERO +VECTOR 0x0a, ZERO +VECTOR 0x0b, ZERO +VECTOR 0x0c, ZERO +VECTOR 0x0d, ZERO +VECTOR 0x0e, ZERO +VECTOR 0x0f, ZERO +VECTOR 0x10, ZERO +VECTOR 0x11, ZERO +VECTOR 0x12, ZERO +VECTOR 0x13, ZERO +VECTOR 0x14, ZERO +VECTOR 0x15, ZERO +VECTOR 0x16, ZERO +VECTOR 0x17, ZERO +VECTOR 0x18, ZERO +VECTOR 0x19, ZERO +VECTOR 0x1a, ZERO +VECTOR 0x1b, ZERO +VECTOR 0x1c, ZERO +VECTOR 0x1d, ZERO +VECTOR 0x1e, ERROR_CODE +VECTOR 0x1f, ZERO +VECTOR 0x20, ZERO \ No newline at end of file diff --git a/c9/thread_start/kernel/main.c b/c9/thread_start/kernel/main.c new file mode 100755 index 0000000..d03300e --- /dev/null +++ b/c9/thread_start/kernel/main.c @@ -0,0 +1,35 @@ +#include "print.h" +#include "init.h" +#include "debug.h" +#include "memory.h" +#include "thread.h" + +void k_thread_HuSharp_1(void* args); + +int main(void) { + put_str("I am kernel!\n"); + init_all(); + //ASSERT(1 == 2); + // asm volatile("sti"); // 打开中断 即将 EFLAGS 的 IF置为 1 + + // 进行内存分配 + /* 内核物理页分配 + void* addr = get_kernel_pages(3); + put_str("\n get_kernel_page start vaddr is: "); + put_int((uint32_t) addr); + put_str("\n"); + */ + + // 线程演示 + thread_start("k_thread_HuSharp_1", 31, k_thread_HuSharp_1, "zdy is a shazi! "); + + while(1); + return 0; +} + +void k_thread_HuSharp_1(void* args) { + char* para = args; + while(1) { + put_str(para); + } +} \ No newline at end of file diff --git a/c9/thread_start/kernel/memory.c b/c9/thread_start/kernel/memory.c new file mode 100755 index 0000000..afbb653 --- /dev/null +++ b/c9/thread_start/kernel/memory.c @@ -0,0 +1,257 @@ +#include "memory.h" +#include "stdint.h" +#include "kernel/print.h" +#include "kernel/bitmap.h" +#include "global.h" +#include "debug.h" +#include "kernel/print.h" +#include "string.h" + +#define PG_SIZE 4096 + +/*************** 位图地址 ******************** + * 因为0xc009f000是内核主线程栈顶,0xc009e000是内核主线程的pcb. + * 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000, + * 这样本系统最大支持4个页框的位图,即512M */ +#define MEM_BITMAP_BASE 0xc009a000 + +// 返回虚拟地址的 高 10 位(即pde),与中间 10 位(即pte) +#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) +#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) + +/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存, + * 使虚拟地址在逻辑上连续 */ +#define K_HEAP_START 0xc0100000 + +// 内存池结构,生成两个实例用于管理内核内存池和用户内存池 +struct pool { + // 本内存池用到的位图结构,用于管理物理内存 + struct bitmap pool_bitmap; + uint32_t phy_addr_start;// 本内存池所管理物理内存起始地址 + uint32_t pool_size; //本内存池字节容量 +}; + +struct pool kernel_pool, user_pool; // 生成全局内核内存池, 用户内存地址 +struct virtual_addr kernel_addr; // 给内核分配虚拟地址 + + +/* 在pf表示的虚拟内存池中申请pg_cnt个虚拟页, + * 成功则返回虚拟页的起始地址, 失败则返回NULL + */ +static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) { + int vaddr_start = 0;//用于存储分配的虚拟地址 + int bit_idx_start = -1;// 存储位图扫描函数的返回值 + uint32_t cnt = 0; + if(pf == PF_KERNEL) {// 说明在内核虚拟池中 + bit_idx_start = bitmap_scan(&kernel_addr.vaddr_bitmap, pg_cnt); + if(bit_idx_start == -1) {//说明没找到 + return NULL; + } + // 将已经选出的内存置为 1 ,表示已经使用 + while(cnt < pg_cnt) { + bitmap_set(&kernel_addr.vaddr_bitmap, bit_idx_start + cnt++, 1); + } + // 虚拟地址应当加上 页表所占的内存 + vaddr_start = kernel_addr.vaddr_start + bit_idx_start * PG_SIZE; + } else {// 用户进程池 还未完善 + + } + return (void*)vaddr_start; +} + +/********* 以下两个获取虚拟地址 addr 的 pde & pte 地址,为建立映射做准备 ********/ +// 得到虚拟地址 vaddr 对应的 pte 指针 +// 构造一个 访问该 pte 的32位地址,欺骗处理器 +uint32_t* pte_ptr(uint32_t vaddr) { + /* 先访问到页目录表自己(位于1023项目录项中) + \ + * 再用页目录项 pde (页目录内页表的索引)做为pte的索引访问到页表 + \ + * 再用 pte 的索引做为页内偏移 + */ + uint32_t* pte = (uint32_t*) (0xffc00000 + \ + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4); + return pte;// 得到物理页的虚拟地址,即通过这个虚拟地址,可以访问到该物理页(保护模式下必须通过 vaddr) +} + +// 得到虚拟地址 vaddr 对应的 pde 指针 +// 构造一个 访问该 pde 的32位地址,欺骗处理器 +uint32_t* pde_ptr(uint32_t vaddr) { + uint32_t* pde = (uint32_t*) (0xfffff000 + PDE_IDX(vaddr) * 4); + return pde; +} + +/* 在m_pool指向的物理内存池中分配1个物理页, + * 成功则返回页框的物理地址,失败则返回NULL + */ +static void* palloc(struct pool* m_pool) { + // 扫描或设置位图要保证原子操作 + int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); //找一个空闲物理页面 + if(bit_idx == -1) { + return NULL; + } + bitmap_set(&m_pool->pool_bitmap, bit_idx, 1); //表示已经占用 + // 成功则返回页框的物理地址 + uint32_t page_phyaddr = (m_pool->phy_addr_start + (bit_idx * PG_SIZE)); + return (void*)page_phyaddr; +} + +/********使用 pde_ptr 和 pte_ptr 来建立虚拟地址和物理地址的映射 ********/ +// 本质上便是将 pte 写入到 获取的 pde 项中,将 物理地址写入到 pte 中 +static void page_table_add(void* _vaddr, void* _page_phyaddr) { + uint32_t vaddr = (uint32_t)_vaddr; + uint32_t page_phyaddr = (uint32_t)_page_phyaddr; + // 以下两个函数都是通过 虚拟地址来获取 + uint32_t* pde = pde_ptr(vaddr); + uint32_t* pte = pte_ptr(vaddr); + +/************************ 注意 ************************* + * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte, + * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。 + * *********************************************************/ + // 先在页目录内判断目录项的P位,若为1,则表示该表(pte)已存在 + if(*pde & 0x00000001) {// 通过 P 位来判断目录项是否存在 + ASSERT(!(*pte & 0x00000001)); // 表示 pte 不存在 + + // 再判断 pte 是否存在 + if(!(*pte & 0x00000001)) { + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } else { // 并不会执行到此处,因为此处意思是:pde存在,pte也存在 + // 但是之前的 ASSERT 已经判断了 pde 不存在 + PANIC("pte repeat!"); + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); + } + } else {// pde 不存在 + // 需要分配 pte 物理页,采用 plloc 函数 + uint32_t new_pde_phyaddr = (uint32_t)palloc(&kernel_pool);//页表中的页框一律从内核中分配 + + *pde = (new_pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);// 写入到 pde 中 + + /* 分配到的物理页地址 new_pde_phyaddr 对应的物理内存清0,(使用时清零比回收后清零高效,因为不知道回收后会不会再使用) + * 避免里面的陈旧数据变成了页表项,从而让页表混乱. + * 访问到pde对应的物理地址,用pte取高20位便可. + * 因为pte是基于该pde对应的物理地址内再寻址, + * 把低12位置0便是该pde对应的物理页的起始 + */ + // 现需要获得新创建的这个物理页的虚拟地址(保护模式得看虚拟地址!) + memset(((void*)((int)pte & 0xfffff000)), 0, PG_SIZE);//将该新配物理页清0 + + ASSERT(!(*pte & 0x00000001));// 断言 pte 此时是否存在 + + // pte项 此时更改为新建物理页的位置 + *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);//将 物理页地址 写入到 pte项中 + } +} + + +// 分配pg_cnt个页空间(物理页),成功则返回起始虚拟地址,失败时返回NULL +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) { + ASSERT(pg_cnt > 0 && pg_cnt < 3840); +/*********** malloc_page的原理是三个动作的合成: *********** + 1.通过vaddr_get在虚拟内存池中申请虚拟地址 + 2.通过palloc在物理内存池中申请物理页 + 3.通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射 +***************************************************************/ + void* vaddr_start = vaddr_get(pf, pg_cnt); + if(vaddr_start == NULL) { + return NULL; + } + + uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt; + struct pool* mem_pool = (pf & PF_KERNEL)? &kernel_pool : &user_pool; + + // 因为虚拟地址是连续的,但物理地址可以是不连续的,所以逐个做映射 + while(cnt-- > 0) { + void* page_phyaddr = palloc(mem_pool); + if(page_phyaddr == NULL) { + return NULL; + } + page_table_add((void*)vaddr, page_phyaddr);// 在页表建立映射 + vaddr += PG_SIZE;// 下一个虚拟页 + } + return vaddr_start; +} + +// 从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL +void* get_kernel_pages(uint32_t pg_cnt) { + void* vaddr = malloc_page(PF_KERNEL, pg_cnt); + if(vaddr != NULL) {// 虚拟地址是连续的 + memset(vaddr, 0, pg_cnt * PG_SIZE); + } + return vaddr; +} + +// 初始化内存池, 通过all_mem 初始化物理内存池相关结构 +static void mem_pool_init(uint32_t all_mem) { + put_str(" phy_mem_pool_init start!\n"); + // 页表大小= 1页的页目录表+第0和第768个页目录项指向同一个页表+ + // 第769~1022个页目录项共指向254个页表,共256个页框 + uint32_t page_table_size = PG_SIZE * 256; + // 用于记录当前已经使用的内存字节数 + uint32_t used_mem = page_table_size + 0x100000;// 0x100000为低端 1M 内存 + uint32_t free_mem = all_mem - used_mem; + uint16_t all_free_pages = free_mem / PG_SIZE;//转换为 页数 + + // 内核物理内存池 与 用户物理内存池 大小一致 + uint16_t kernel_free_pages = all_free_pages / 2; + uint16_t user_free_pages = all_free_pages - kernel_free_pages; + + /* 为简化位图操作,余数不处理,坏处是这样做会丢内存。 + * 好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存 + */ + uint32_t kbm_length = kernel_free_pages / 8; // Kernel BitMap的长度,位图中的一位表示一页,以字节为单位 + uint32_t ubm_length = user_free_pages / 8; // User BitMap的长度. + + uint32_t kp_start = used_mem; // kernel pool start 内核内存池的起始位置 + uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; + + kernel_pool.phy_addr_start = kp_start; + user_pool.phy_addr_start = up_start; + + kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;// 位图字节长度 + user_pool.pool_bitmap.btmp_bytes_len = ubm_length; + + kernel_pool.pool_size = kernel_free_pages * PG_SIZE; + user_pool.pool_size = user_free_pages * PG_SIZE; + +/********* 内核内存池和用户内存池位图 *********** + * 位图是全局的数据,长度不固定。 + * 全局或静态的数组需要在编译时知道其长度, + * 而我们需要根据总内存大小算出需要多少字节。 + * 所以改为指定一块内存来生成位图. + * ************************************************/ +// 内核使用的最高地址是0xc009f000,这是主线程的栈地址.(内核的大小预计为70K左右) +// 32M内存占用的位图是2k.内核内存池的位图先定在MEM_BITMAP_BASE(0xc009a000)处. + kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE; + + // 用户内存池的位图紧跟在内核内存池位图后面 + user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length); + /******************** 输出内存池信息 **********************/ + put_str(" kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits); + put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start); + put_str("\n"); + put_str(" user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits); + put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start); + put_str("\n"); + + /**************** 进行初始化 ***************/ + // 将位图 置为 0 表示还未分配 + bitmap_init(&kernel_pool.pool_bitmap); + bitmap_init(&user_pool.pool_bitmap); + + // 初始化内核的虚拟地址 + // 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致 + kernel_addr.vaddr_bitmap.btmp_bytes_len = kbm_length; + // 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之后 + kernel_addr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); + // 虚拟内存池的起始地址,以达到进程在堆中动态申请内存 + kernel_addr.vaddr_start = K_HEAP_START; //内核堆 + bitmap_init(&kernel_addr.vaddr_bitmap); + put_str(" mem_pool_init done\n"); +} + +// 内存管理部分初始化入口 +void mem_init() { + put_str("mem_init start!\n"); + uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); // 储存机器上物理内存总量 + mem_pool_init(mem_bytes_total); //初始化内存池 + put_str("mem_init done!\n"); +} \ No newline at end of file diff --git a/c9/thread_start/kernel/memory.h b/c9/thread_start/kernel/memory.h new file mode 100755 index 0000000..405f8d7 --- /dev/null +++ b/c9/thread_start/kernel/memory.h @@ -0,0 +1,33 @@ +#ifndef __KERNEL_MEMORY_H +#define __KERNEL_MEMORY_H + +#include "stdint.h" +#include "bitmap.h" + +// 内存池的标记,用来判断使用哪个内存池 +enum pool_flags { + PF_KERNEL = 1, // 内核内存池 + PF_USER = 2 // 用户内存池 +}; + +#define PG_P_1 1 // 页表项或页目录项存在属性位 +#define PG_P_0 0 // 页表项或页目录项存在属性位 +#define PG_RW_R 0 // R/W 属性位值, 读/执行 +#define PG_RW_W 2 // R/W 属性位值, 读/写/执行 +#define PG_US_S 0 // U/S 属性位值, 系统级 +#define PG_US_U 4 // U/S 属性位值, 用户级 + +// 虚拟地址池,用于虚拟地址管理 +struct virtual_addr{ + struct bitmap vaddr_bitmap; // 虚拟地址用到的 bitmap 结构, 页 为单位 + uint32_t vaddr_start; // 虚拟地址起始地址 +}; + +extern struct pool kernel_pool, user_pool; +void mem_init(void); +void* get_kernel_pages(uint32_t pg_cnt); +void* malloc_page(enum pool_flags pf, uint32_t pg_cnt); +void malloc_init(void); +uint32_t* pte_ptr(uint32_t vaddr); +uint32_t* pde_ptr(uint32_t vaddr); +#endif \ No newline at end of file diff --git a/c9/thread_start/lib/kernel/bitmap.c b/c9/thread_start/lib/kernel/bitmap.c new file mode 100755 index 0000000..323b793 --- /dev/null +++ b/c9/thread_start/lib/kernel/bitmap.c @@ -0,0 +1,82 @@ +#include "bitmap.h" +#include "stdint.h" +#include "string.h" +#include "print.h" +#include "interrupt.h" +#include "debug.h" + +// bitmap 的初始化 +void bitmap_init(struct bitmap* btmp) { + memset(btmp->bits, 0, btmp->btmp_bytes_len); +} + +// 判断 btmp 为指针处,bit_idx 位是否为 1,为 1 就返回 true +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) { + uint32_t byte_idx = bit_idx / 8;//用于标注字节 + uint32_t bit_odd = bit_idx % 8; //用于标注字节内的位 + return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd)); +} + +// 在位图中申请连续cnt个位,成功则返回其起始位下标,失败返回-1 +int bitmap_scan(struct bitmap* btmp, uint32_t cnt) { + uint32_t idx_byte = 0; // 用于记录空闲位所在的字节 + // 先逐字节比较,蛮力法 + // 0 表示空闲,若停止,则说明至少有一个空闲位 + while(( 0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) { + idx_byte++; + } + ASSERT(idx_byte < btmp->btmp_bytes_len);// 断言此时在内部 + if(idx_byte == btmp->btmp_bytes_len) { + return -1;// 访问失败,返回 -1 + } + + /* 若在位图数组范围内的某字节内找到了空闲位, + * 在该字节内逐位比对,返回空闲位的索引。 + */ + int idx_bit = 0;//字节内部位数 + while((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) { + idx_bit++; + }// 找到空闲位 + int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在bitmap 下标 + if(cnt == 1) { + return bit_idx_start; + } + + // 至此说明 cnt > 1 + // 因此首先进行剩余位数的判断,以免超过bitmap记录数 + uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start); + uint32_t idx_next_bit = bit_idx_start + 1; + uint32_t free_bit_cnt = 1;// 一定要记得记录一下 之前判断后 还有个空闲位 + + bit_idx_start = -1; // 先将其置为 -1,若找不到连续的位就直接返回 + while(bit_left-- > 0) { + // 调用 scan_test 函数,为 0 则为 false + if(!(bitmap_scan_test(btmp, idx_next_bit))) { + free_bit_cnt++; + } else { // 由于必须是连续的,因此只要遇到 不空闲的 位 便将记录cnt置为 0 + free_bit_cnt = 0; + } + // 若是已经满足空闲数 + if(free_bit_cnt == cnt) { + bit_idx_start = idx_next_bit - cnt + 1; + break; + } + idx_next_bit++;// 继续判断下一位 + } + + return bit_idx_start; +} + +// 将位图btmp的bit_idx位设置为value +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) { + ASSERT((value ==0) || (value == 1));// 只能赋予 0 或 1 + uint32_t byte_idx = bit_idx / 8; //字节位 + uint32_t bit_odd = bit_idx % 8; //位数 位 + + if(value) { + btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); + } else { + btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); + } + +} diff --git a/c9/thread_start/lib/kernel/bitmap.h b/c9/thread_start/lib/kernel/bitmap.h new file mode 100755 index 0000000..ab3d2db --- /dev/null +++ b/c9/thread_start/lib/kernel/bitmap.h @@ -0,0 +1,18 @@ +#ifndef __LIB_KERNEL_BITMAP_H +#define __LIB_KERNEL_BITMAP_H +#include "global.h" +#define BITMAP_MASK 1 // 用在位图中逐位判断,主要通过 按位与 & +struct bitmap{ + uint32_t btmp_bytes_len; + /* 在遍历位图时,整体上以字节为单位,细节上是以位为单位, + 所以此处位图的指针必须是单字节 */ + uint8_t* bits; +}; + +// 函数 +void bitmap_init(struct bitmap* btmp); +bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx); +int bitmap_scan(struct bitmap* btmp, uint32_t cnt); +void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value); + +#endif \ No newline at end of file diff --git a/c9/thread_start/lib/kernel/io.h b/c9/thread_start/lib/kernel/io.h new file mode 100755 index 0000000..7a30b8f --- /dev/null +++ b/c9/thread_start/lib/kernel/io.h @@ -0,0 +1,49 @@ +/************** 机器模式 *************** + b -- 输出寄存器QImode名称,即寄存器中的最低8位:[a-d]l。 + w -- 输出寄存器HImode名称,即寄存器中2个字节的部分,如[a-d]x。 + + HImode + “Half-Integer”模式,表示一个两字节的整数。 + QImode + “Quarter-Integer”模式,表示一个一字节的整数。 +*******************************************/ +#ifndef _LIB_IO_H +# define _LIB_IO_H + +#include "stdint.h" + +/** + * 向指定的端口写入一个字节的数据. + * port 为 16 位即可容纳 65535 所有端口号 + * N 为立即数约束 + */ +static inline void outb(uint16_t port, uint8_t data) { + asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port)); +} +/* + * 将addr处起始的word_cnt个字写入端口port + * insw是将从端口port处读入的16位内容写入es:edi指向的内存, + * 我们在设置段描述符时, 已经将ds,es,ss段的选择子都设置为相同的值了, + * 此时不用担心数据错乱。 + */ +static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) { + asm volatile("cld; rep outsw" : "+S"(addr), "+c"(word_cnt) : "d"(port)); +} + +/** + * 将从端口port读入的一个字节返回. + */ +static inline uint8_t inb(uint16_t port) { + uint8_t data; + asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port)); + return data; +} + +/** + * 将从port读取的word_cnt字节写入addr. + */ +static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) { + asm volatile("cld; rep insw" : "+D"(addr), "+c"(word_cnt) : "d"(port) : "memory"); +} + +#endif \ No newline at end of file diff --git a/c9/thread_start/lib/kernel/list.c b/c9/thread_start/lib/kernel/list.c new file mode 100755 index 0000000..17dbd76 --- /dev/null +++ b/c9/thread_start/lib/kernel/list.c @@ -0,0 +1,95 @@ +#include "list.h" +#include "interrupt.h" + +// 初始化双向链表 +void list_init (struct list* list) { + list->head.prev = NULL; + list->tail.next = NULL; + + list->head.next = &list->tail; + list->tail.prev = &list->head; +} + +// 将链表元素 before 插入在元素 elem 前 +void list_insert_before(struct list_elem* elem, struct list_elem* before) { + // 由于队列是公共资源,因此要保证为原子操作 + enum intr_status old_status = intr_disable();//关中断 + + // 更新节点 + elem->prev->next = before; + before->prev = elem->prev; + elem->prev = before; + before->next = elem; + + intr_set_status(old_status); +} + +void list_append(struct list* plist, struct list_elem* elem) { + list_insert_before(&plist->tail, elem);// 队列的FIFO +} + +void list_remove(struct list_elem* pelem) { + enum intr_status old_status = intr_disable(); + + pelem->prev->next = pelem->next; + pelem->next->prev = pelem->prev; + + intr_set_status(old_status);// 返回原状态 +} + +void list_push(struct list* plist, struct list_elem* elem) { + list_insert_before(plist->head.next, elem); +} + +// pop 操作,为啥没有关中断? +struct list_elem* list_pop(struct list* plist) { + struct list_elem* elem = plist->head.next; + list_remove(elem); + return elem; +} + +// 查找元素 +bool elem_find(struct list* plist, struct list_elem* obj_elem) { + struct list_elem* elem = plist->head.next; + while(elem != &plist->tail) { + if(elem == obj_elem) { + return true; + } + elem = elem->next; + } + return false; +} + +// 遍历列表的所有元素,判断是否有 elem 满足条件 +// 判断方法采用 func 回调函数进行判断 +struct list_elem* list_traversal(struct list* plist, function func, int arg) { + struct list_elem* elem = plist->head.next; + // 如果队列为空 直接 return + if(list_empty(plist)) { + return NULL; + } + while(elem != &plist->tail) { + if(func(elem, arg)) { + return elem; + } + elem = elem->next; + } + return NULL; +} + +// 判断空 +bool list_empty(struct list* plist) { + return (plist->head.next == &plist->tail ? true : false); +} + +uint32_t list_len(struct list* plist) { + struct list_elem* elem = plist->head.next; + uint32_t len = 0; + while(elem != &plist->tail) { + elem = elem->next; + len++; + } + return len; +} + + diff --git a/c9/thread_start/lib/kernel/list.h b/c9/thread_start/lib/kernel/list.h new file mode 100755 index 0000000..f78992b --- /dev/null +++ b/c9/thread_start/lib/kernel/list.h @@ -0,0 +1,31 @@ +#ifndef __LIB_KERNEL_LIST_H +#define __LIB_KERNEL_LIST_H +#include "global.h" + +// 节点,不需要 data 域 +struct list_elem { + struct list_elem* prev;// 前驱 + struct list_elem* next;// 后继 +}; + +// 链表结构,用来实现队列 +struct list { + struct list_elem head;// 头指针 + struct list_elem tail;// 尾指针 +}; + +typedef bool (function)(struct list_elem*, int arg); + +void list_init (struct list*); +void list_insert_before(struct list_elem* elem, struct list_elem* before); +void list_push(struct list* plist, struct list_elem* elem); +void list_iterate(struct list* plist); +void list_append(struct list* plist, struct list_elem* elem); +void list_remove(struct list_elem* pelem); +struct list_elem* list_pop(struct list* plist); +bool list_empty(struct list* plist); +uint32_t list_len(struct list* plist); +struct list_elem* list_traversal(struct list* plist, function func, int arg); +bool elem_find(struct list* plist, struct list_elem* obj_elem); + +#endif \ No newline at end of file diff --git a/c9/thread_start/lib/kernel/print.S b/c9/thread_start/lib/kernel/print.S new file mode 100755 index 0000000..52b9246 --- /dev/null +++ b/c9/thread_start/lib/kernel/print.S @@ -0,0 +1,217 @@ +TI_GDT equ 0 +RPL0 equ 0 +SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 + +section .data +put_int_buffer dq 0 ; 定义8字节缓冲区用于数字到字符的转换 + +[bits 32] +section .text +;-------------------------------------------- +;put_str 通过put_char来打印以0字符结尾的字符串 +;-------------------------------------------- +;输入:栈中参数为打印的字符串 +;输出:无 + +global put_str +put_str: +;由于本函数中只用到了ebx和ecx,只备份这两个寄存器 + push ebx + push ecx + xor ecx, ecx ; 准备用ecx存储参数,清空 + mov ebx, [esp + 12] ; 从栈中得到待打印的字符串地址 +.goon: + mov cl, [ebx] + cmp cl, 0 ; 如果处理到了字符串尾,跳到结束处返回 + jz .str_over + push ecx ; 为put_char函数传递参数 + call put_char + add esp, 4 ; 回收参数所占的栈空间 + inc ebx ; 使ebx指向下一个字符 + jmp .goon +.str_over: + pop ecx + pop ebx + ret + +;------------------------ put_char ----------------------------- +;功能描述:把栈中的1个字符写入光标所在处 +;------------------------------------------------------------------- +global put_char +put_char: + pushad ;备份32位寄存器环境 + ;需要保证gs中为正确的视频段选择子,为保险起见,每次打印时都为gs赋值 + mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器 + mov gs, ax + +;;;;;;;;; 获取当前光标位置 ;;;;;;;;; + ;先获得高8位 + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + in al, dx ;得到了光标位置的高8位 + mov ah, al + + ;再获取低8位 + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + in al, dx + + ;将光标存入bx + mov bx, ax + ;下面这行是在栈中获取待打印的字符 + mov ecx, [esp + 36] ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节 + cmp cl, 0xd ;CR是0x0d,LF是0x0a + jz .is_carriage_return + cmp cl, 0xa + jz .is_line_feed + + cmp cl, 0x8 ;BS(backspace)的asc码是8 + jz .is_backspace + jmp .put_other +;;;;;;;;;;;;;;;;;; + + .is_backspace: +;;;;;;;;;;;; backspace的一点说明 ;;;;;;;;;; +; 当为backspace时,本质上只要将光标移向前一个显存位置即可.后面再输入的字符自然会覆盖此处的字符 +; 但有可能在键入backspace后并不再键入新的字符,这时在光标已经向前移动到待删除的字符位置,但字符还在原处, +; 这就显得好怪异,所以此处添加了空格或空字符0 + dec bx + shl bx,1 + mov byte [gs:bx], 0x20 ;将待删除的字节补为0或空格皆可 + inc bx + mov byte [gs:bx], 0x07 + shr bx,1 + jmp .set_cursor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .put_other: + shl bx, 1 ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节 + mov [gs:bx], cl ; ascii字符本身 + inc bx + mov byte [gs:bx],0x07 ; 字符属性 + shr bx, 1 ; 恢复老的光标值 + inc bx ; 下一个光标值 + cmp bx, 2000 + jl .set_cursor ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值 + ; 若超出屏幕字符数大小(2000)则换行处理 + .is_line_feed: ; 是换行符LF(\n) + .is_carriage_return: ; 是回车符CR(\r) + ; 如果是CR(\r),只要把光标移到行首就行了。 + xor dx, dx ; dx是被除数的高16位,清0. + mov ax, bx ; ax是被除数的低16位. + mov si, 80 ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中, + div si ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。 + sub bx, dx ; 光标值减去除80的余数便是取整 + ; 以上4行处理\r的代码 + + .is_carriage_return_end: ; 回车符CR处理结束 + add bx, 80 + cmp bx, 2000 + .is_line_feed_end: ; 若是LF(\n),将光标移+80便可。 + jl .set_cursor + +;屏幕行范围是0~24,滚屏的原理是将屏幕的1~24行搬运到0~23行,再将第24行用空格填充 + .roll_screen: ; 若超出屏幕大小,开始滚屏 + cld + mov ecx, 960 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 + mov esi, 0xb80a0 ; 第1行行首 + mov edi, 0xb8000 ; 第0行行首 + rep movsd + +;;;;;;;将最后一行填充为空白 + mov ebx, 3840 ; 最后一行首字符的第一个字节偏移= 1920 * 2 + mov ecx, 80 ;一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次 + .cls: + mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 + add ebx, 2 + loop .cls + mov bx,1920 ;将光标值重置为1920,最后一行的首字符. + + .set_cursor: + ;将光标设为bx值 +;;;;;;; 1 先设置高8位 ;;;;;;;; + mov dx, 0x03d4 ;索引寄存器 + mov al, 0x0e ;用于提供光标位置的高8位 + out dx, al + mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 + mov al, bh + out dx, al + +;;;;;;; 2 再设置低8位 ;;;;;;;;; + mov dx, 0x03d4 + mov al, 0x0f + out dx, al + mov dx, 0x03d5 + mov al, bl + out dx, al + .put_char_done: + popad + ret + +;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- +;输入:栈中参数为待打印的数字 +;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf +;------------------------------------------------------------------------------------------ +global put_int +put_int: + pushad + mov ebp, esp + mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 + mov edx, eax + mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 + mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 + mov ebx, put_int_buffer + +;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 +.16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 + and edx, 0x0000000F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 + cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 + jg .is_A2F + add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 + jmp .store +.is_A2F: + sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 + add edx, 'A' + +;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer +;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. +.store: +; 此时dl中应该是数字对应的字符的ascii码 + mov [ebx+edi], dl + dec edi + shr eax, 4 + mov edx, eax + loop .16based_4bits + +;现在put_int_buffer中已全是字符,打印之前, +;把高位连续的字符去掉,比如把字符000123变成123 +.ready_to_print: + inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 +.skip_prefix_0: + cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 + je .full0 +;找出连续的0字符, edi做为非0的最高位字符的偏移 +.go_on_skip: + mov cl, [put_int_buffer+edi] + inc edi + cmp cl, '0' + je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) + dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 + jmp .put_each_num + +.full0: + mov cl,'0' ; 输入的数字为全0时,则只打印0 +.put_each_num: + push ecx ; 此时cl中为可打印的字符 + call put_char + add esp, 4 + inc edi ; 使edi指向下一个字符 + mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 + cmp edi,8 + jl .put_each_num + popad + ret diff --git a/c9/thread_start/lib/kernel/print.h b/c9/thread_start/lib/kernel/print.h new file mode 100755 index 0000000..903039e --- /dev/null +++ b/c9/thread_start/lib/kernel/print.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_PRINT_H +#define __LIB_KERNEL_PRINT_H +#include "stdint.h" +void put_char(uint8_t char_asci); +void put_str(char* message); +void put_int(uint32_t num); // 以16进制打印 +#endif + diff --git a/c9/thread_start/lib/stdint.h b/c9/thread_start/lib/stdint.h new file mode 100755 index 0000000..af563e0 --- /dev/null +++ b/c9/thread_start/lib/stdint.h @@ -0,0 +1,11 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/c9/thread_start/lib/string.c b/c9/thread_start/lib/string.c new file mode 100755 index 0000000..ab47322 --- /dev/null +++ b/c9/thread_start/lib/string.c @@ -0,0 +1,121 @@ +#include "string.h" +#include "global.h" +#include "debug.h" + +/* 进行初始化 将dst_起始的size个字节置为value */ +// 通常用于内存分配时的数据清 0 +void memset(void* dst_, uint8_t value, uint32_t size) { + ASSERT(dst_ != NULL); + uint8_t* dst = (uint8_t*)dst_; + while (size-- > 0){ + *dst++ = value; + } +} + +/* 将src_起始的size个字节复制到dst_ */ +void memcpy(void* dst_, const void* src_, uint32_t size) { + ASSERT(dst_ != NULL && src_ != NULL); + uint8_t* dst = dst_; + const uint8_t* src = src_; + while(size-- > 0) { + *dst++ = *src++; + } +} + +/* 连续比较以地址a_和地址b_开头的size个字节, + 若相等则返回0,若a_大于b_返回+1,否则返回-1 */ +int memcmp(const void* a_, const void* b_, uint32_t size) { + const char* a = a_; + const char* b = b_; + ASSERT(a != NULL && b != NULL); + while(size-- > 0) { + if(*a != *b) { + return *a > *b ? 1 : -1; + } + a++; + b++; + } + // 至此说明相等 + return 0; +} + +/* 将字符串从src_复制到dst_ */ +char* strcpy(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* temp = dst_; // 暂存地址 + // 以 字符串结尾 '0' 作为终止条件 + while((*dst_++ = *src_++)); + return temp; +} + + +/* 返回字符串长度 */ +uint32_t strlen(const char* str) { + ASSERT(str != NULL); + const char* end = str;// 标记结尾处位置 + // 和strcpy 函数一样,遇到 '0'停止 + while(*end++); + return (end - str - 1); +} +/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */ +int8_t strcmp (const char* a, const char* b) { + ASSERT(a != NULL && b != NULL); + while(*a != 0 && *a == *b) { + a++; + b++; + } + // 如果 < 就返回 -1 + // 如果 >= 就前往 布尔式 + // 该布尔式表示,要是 > 就 返回 1, 否则只可能是 = ,布尔值刚好是 0 + return *a < *b ? -1 : *a > *b; +} + +/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + while(*str != 0) { + if(*str == ch) { + return (char*) str;// 强制转换 + } + str++; + } + return NULL; +} + +/* 从右往左查找字符串str中首次出现字符ch的地址(不是下标,是地址) */ +char* strrchr(const char* str, const uint8_t ch) { + ASSERT(str != NULL); + const char* last = NULL; + // 从左往右遍历,将 last 更新即可,这样便不用手动判断 '0' + while(*str != 0) { + if(*str == ch) { + last = str; + } + str++; + } + return (char*)last; +} + +/* 将字符串src_拼接到dst_后,将回拼接的串地址 */ +char* strcat(char* dst_, const char* src_) { + ASSERT(dst_ != NULL && src_ != NULL); + char* str = dst_; + while(*str++);// 循环到末尾 + --str;//得到拼接位置 + while((*str++ = *src_++)); + return dst_; +} + +/* 在字符串str中查找指定字符ch出现的次数 */ +uint32_t strchrs(const char* str, uint8_t ch) { + ASSERT(str != NULL); + uint32_t cnt = 0; + const char* temp = str; + while(*temp != 0) { + if(*temp == ch) { + cnt++; + } + temp++; + } + return cnt; +} \ No newline at end of file diff --git a/c9/thread_start/lib/string.h b/c9/thread_start/lib/string.h new file mode 100755 index 0000000..e69de29 diff --git a/c9/thread_start/makefile b/c9/thread_start/makefile new file mode 100755 index 0000000..ec4b481 --- /dev/null +++ b/c9/thread_start/makefile @@ -0,0 +1,100 @@ +BUILD_DIR = ./build +# 存储编译的所有文件 +ENTRY_POINT = 0xc0001500 +# ld 链接 -Ttext参数 +# 编译器及编译参数 +AS = nasm +CC = gcc +LD = ld +LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ +ASFLAGS = -f elf +ASIB = -I include/ +CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin \ + -W -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map +OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ + $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ + $(BUILD_DIR)/debug.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o \ + $(BUILD_DIR)/string.o $(BUILD_DIR)/thread.o + + +############## c代码编译 ############### +$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ + lib/stdint.h kernel/init.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ + lib/stdint.h kernel/interrupt.h device/timer.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ + lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ + lib/kernel/io.h lib/kernel/print.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ + lib/kernel/print.h lib/stdint.h kernel/interrupt.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/string.o: lib/string.c lib/string.h lib/stdint.h kernel/global.h \ + lib/stdint.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ + kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ + lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h \ + kernel/global.h kernel/global.h kernel/debug.h lib/kernel/print.h \ + lib/kernel/io.h kernel/interrupt.h lib/string.h lib/stdint.h + $(CC) $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h lib/stdint.h \ + kernel/global.h lib/kernel/bitmap.h kernel/memory.h lib/string.h \ + lib/stdint.h lib/kernel/print.h kernel/interrupt.h kernel/debug.h + $(CC) $(CFLAGS) $< -o $@ + + +# 编译loader和mbr +$(BUILD_DIR)/mbr.bin: boot/mbr.S + $(AS) $(ASIB) $< -o $@ + +$(BUILD_DIR)/loader.bin: boot/loader.S + $(AS) $(ASIB) $< -o $@ + +############## 汇编代码编译 ############### +$(BUILD_DIR)/kernel.o: kernel/kernel.S + $(AS) $(ASFLAGS) $< -o $@ +$(BUILD_DIR)/print.o: lib/kernel/print.S + $(AS) $(ASFLAGS) $< -o $@ + +############## 链接所有目标文件 ############# +$(BUILD_DIR)/kernel.bin: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ + +.PHONY : mk_dir hd clean tran all + +mk_dir: + if [[ ! -d $(BUILD_DIR) ]];then mkdir $(BUILD_DIR);fi + +hd: + # echo ".....Writing mbr, loader and kernel to disk....." + dd if=$(BUILD_DIR)/mbr.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=1 conv=notrunc + dd if=$(BUILD_DIR)/loader.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=4 seek=2 conv=notrunc + dd if=$(BUILD_DIR)/kernel.bin of=/home/ahu/install/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + + +clean: + cd $(BUILD_DIR) && rm -f ./* + +build: $(BUILD_DIR)/mbr.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/kernel.bin + # echo ".....build kernel now....." + +all: mk_dir build hd tran + +tran: + cd /home/ahu/install/bochs/ && bin/bochs -f bochsrc.disk \ No newline at end of file diff --git a/c9/thread_start/test_pra/thread_test.c b/c9/thread_start/test_pra/thread_test.c new file mode 100755 index 0000000..11567c1 --- /dev/null +++ b/c9/thread_start/test_pra/thread_test.c @@ -0,0 +1,2 @@ +#include +#include diff --git a/c9/thread_start/thread/thread.c b/c9/thread_start/thread/thread.c new file mode 100755 index 0000000..34d4ee6 --- /dev/null +++ b/c9/thread_start/thread/thread.c @@ -0,0 +1,76 @@ +#include "thread.h" +#include "stdint.h" +#include "string.h" +#include "global.h" +#include "memory.h" + +#define PG_SIZE 4096 + +// 不能按照以往的函数调用, 比如 kernel_thread(function, func_arg) +// 因为 我们此处采用的是 ret 返回,而不是 call , +// 因此需要采用存储到 kernel_thread 的栈中,存储参数和占位返回地址的方式 + + +/* 由kernel_thread去执行function(func_arg) */ +static void kernel_thread(thread_func* function, void* func_arg) { + function(func_arg); // 调用 function +} + +// 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) { + // 由于 init_thread 中,已经指向最顶端了 + // 先预留中断使用栈的空间,可见 thread.h 中定义的结构 + // 中断栈用于保存中断时的上下文,实现用户进程时,初始值也会放在中断栈 + pthread->self_kstack -= sizeof(struct intr_stack); + + // 再留出线程栈空间 + pthread->self_kstack -= sizeof(struct thread_stack); + + // 定义线程栈指针 + struct thread_stack* kthread_stack = (struct thread_stack*) pthread->self_kstack; + + // 为 kernel_thread 中 能调用 function 做准备 + // kernel_thread 是第一个函数, eip直接指向它,然后再调用其他的 function + kthread_stack->eip = kernel_thread; + kthread_stack->function = function; + kthread_stack->func_arg = func_arg; + // 初始化为 0 ,在还未执行函数前,寄存器不应该有值 + kthread_stack->ebp = kthread_stack->ebx = \ + kthread_stack->edi = kthread_stack->esi = 0; +} + + +// 初始化线程基本信息 +void init_thread(struct task_struct* pthread, char* name, int prio) { + // + memset(pthread, 0, sizeof(*pthread)); + + strcpy(pthread->name, name); + // 此处是为演习,因此直接将 状态置为 running + pthread->status = TASK_RUNNING; + pthread->priority = prio; + pthread->stack_magic = 0x20000611; //自定义一个魔数 + // self_kstack 是线程自己在内核态下使用的栈顶地址,指向最顶端 + pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE); +} + +// 线程创建函数 +//创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) { + // pcb 位于内核空间,包括用户进程的 pcb 也是在内核空间中 + + // 先通过 内核物理页申请函数 申请一页 + struct task_struct* thread = get_kernel_pages(1); + // 由于 get_kernel_page 获取的是起始位置,因此获取的是 pcb 最低地址 + // 初始化新建线程 + init_thread(thread, name, prio); + // 创建线程 + thread_create(thread, function, func_arg); + + // 简陋版本(以后改为 switch_to + // 作用为:开启线程 + // 由于 thread_create 中将 self_kstack 指向线程栈的最低处,现在将 self_kstack 作为栈顶 + // ret 操作时,由于 thread_create 函数中,将 eip 指向了 kernel_thread,因此 ret时,会跳转到该函数 + asm volatile("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret" : : "g"(thread->self_kstack) : "memory"); + return thread; +} \ No newline at end of file diff --git a/c9/thread_start/thread/thread.h b/c9/thread_start/thread/thread.h new file mode 100755 index 0000000..14bd23f --- /dev/null +++ b/c9/thread_start/thread/thread.h @@ -0,0 +1,90 @@ +#ifndef __THREAD_THREAD_H +#define __THREAD_THREAD_H +#include "stdint.h" + +// 自定义通用函数类型,它将在很多线程函数中做为形参类型 +typedef void thread_func(void*); + +// 进程或线程的状态 +enum task_status { + TASK_RUNNING, + TASK_READY, + TASK_BLOCKED, + TASK_WAITING, + TASK_HANGING, + TASK_DIED +}; + +/*********** 中断栈intr_stack *********** + * 此结构用于中断发生时保护程序(线程或进程)的上下文环境: + * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 + * 寄存器, intr_exit中的出栈操作是此结构的逆操作 + * 此栈在线程自己的内核栈中位置固定,所在页的最顶端 +********************************************/ +struct intr_stack { + uint32_t vec_no; // kernel.S 宏VECTOR中push %1压入的中断号 + uint32_t edi; + uint32_t esi; + uint32_t ebp; + // 虽然pushad把esp也压入,但esp是不断变化的,所以会被popad忽略 + uint32_t esp_dummy; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + +/* 以下由cpu从低特权级进入高特权级时压入 */ + uint32_t err_code; // err_code会被压入在eip之后 + void (*eip) (void); + uint32_t cs; + uint32_t eflags; + void* esp; + uint32_t ss; +}; + +/*********** 线程栈thread_stack *********** + * 线程自己的栈,用于存储线程中待执行的函数 + * 此结构在线程自己的内核栈中位置不固定, + * 用在switch_to时保存线程环境。 + * 实际位置取决于实际运行情况。 + ******************************************/ +struct thread_stack { + uint32_t ebp; + uint32_t ebx; + uint32_t edi; + uint32_t esi; + + /* 线程第一次执行时,eip指向待调用的函数kernel_thread + 其它时候,eip是指向switch_to的返回地址*/ + void (*eip) (thread_func* func, void* func_arg); + + /***** 以下仅供第一次被调度上cpu时使用 ****/ + // 占位函数,迷惑 ret ,假装为返回地址,实则为获取参数提供栈顶位置 + // 用于找到函数参数位置 + void (*unused_retaddr); + thread_func* function; // 由 Kernel_thread 所调用的函数名 + void* func_arg; // 由 Kernel_thread 所调用函数所需要的的参数 +}; + +// 进程或线程的 pcb, 程序控制块 +struct task_struct { + uint32_t* self_kstack; // 各内核线程都用自己的内核栈 + char name[16]; // 记录任务的名字 + enum task_status status; // 线程状态 + uint8_t priority; // 线程优先级 + + // 通过判断栈的边界溢出魔数值是否发生变化 + uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出 + + +}; + +void thread_create(struct task_struct* pthread, thread_func function, void* func_arg); +void init_thread(struct task_struct* pthread, char* name, int prio); +struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg); + +#endif \ No newline at end of file diff --git a/tool/calculator.sh b/tool/calculator.sh new file mode 100755 index 0000000..2c6627f --- /dev/null +++ b/tool/calculator.sh @@ -0,0 +1 @@ +echo |awk "{printf(\"%$2\n\",$1)}" diff --git a/tool/kernel.sh b/tool/kernel.sh new file mode 100755 index 0000000..caf1a8d --- /dev/null +++ b/tool/kernel.sh @@ -0,0 +1,2 @@ +gcc -c -o kernel/main.o kernel/main.c && ld kernel/main.o -Ttext 0xc0001500 -e main -o kernel/kernel.bin && dd if=kernel/kernel.bin of=/home/work/my_workspace/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc + diff --git a/tool/loader.sh b/tool/loader.sh new file mode 100755 index 0000000..08489ea --- /dev/null +++ b/tool/loader.sh @@ -0,0 +1 @@ +nasm -I include/ -o loader.bin loader.S && dd if=./loader.bin of=/home/work/my_workspace/bochs/hd60M.img bs=512 count=3 seek=2 conv=notrunc diff --git a/tool/mbr.sh b/tool/mbr.sh new file mode 100755 index 0000000..fb6f63e --- /dev/null +++ b/tool/mbr.sh @@ -0,0 +1 @@ +nasm -I include/ -o mbr.bin mbr.S && dd if=./mbr.bin of=/home/work/my_workspace/bochs/hd60M.img bs=512 count=1 conv=notrunc diff --git a/tool/xxd.sh b/tool/xxd.sh new file mode 100755 index 0000000..564272a --- /dev/null +++ b/tool/xxd.sh @@ -0,0 +1,21 @@ +#usage: sh xxd.sh 文件 起始地址 长度 +xxd -u -a -g 1 -s $2 -l $3 $1 + +#-u use upper case hex letters. Default is lower case. +# +#-a | -autoskip +# toggle autoskip: A single ’*’ replaces nul-lines. Default off. +# +#-g bytes | -groupsize bytes +# separate the output of every bytes (two hex characters or eight bit-digits each) by a whitespace. Specify -g 0 to +# suppress grouping. defaults to 2 in normal mode and 1 in bits mode. Grouping does not apply to postscript or +# include style. +# +#-c cols | -cols cols +# format octets per line. Default 16 (-i: 12, -ps: 30, -b: 6). Max 256. +# +#-s [+][-]seek +# start at bytes abs. (or rel.) infile offset. + indicates that the seek is relative to the current stdin file position +# (meaningless when not reading from stdin). - indicates that the seek should be that many characters from the end of +# the input (or if combined with +: before the current stdin file position). +# Without -s option, xxd starts at the current file position.