fix: PS/2 keyboard initialization sequence
authorMinep <zelong56@gmail.com>
Wed, 19 Oct 2022 22:30:32 +0000 (23:30 +0100)
committerMinep <zelong56@gmail.com>
Wed, 19 Oct 2022 22:30:32 +0000 (23:30 +0100)
fix: TLB should be invalidated after L2 page table is hooked.
change readme

README.md
docs/README_en.md
lunaix-os/debug/sdbg.c
lunaix-os/hal/pci.c
lunaix-os/includes/arch/x86/vectors.h
lunaix-os/kernel/mm/pmm.c
lunaix-os/kernel/mm/vmm.c
lunaix-os/kernel/peripheral/ps2kbd.c

index 77920e7a3a539c5bd0d97d7b0bedf8f466b58def..0cc76fe2d3865d187e9bdb38c2e865791a143672 100644 (file)
--- a/README.md
+++ b/README.md
@@ -29,6 +29,14 @@ LunaixOS - 一个简单的,详细的,POSIX兼容的(但愿!),带有
 + PCIe 1.1 (WIP)
 + Serial ATA AHCI
 + 文件系统 (WIP)
++ 远程GDB串口调试 (COM1@9600Bd)
+
+已经测试过的环境:
+
++ QEMU (>=7.0.0)
++ Bochs(SATA功能不支持)
++ Virtualbox
++ Dell G3 3779
 
 ## 目录结构
 
@@ -82,7 +90,7 @@ qemu-img create -f vdi machine/disk0.vdi 128M
 
 有很多办法去创建一个虚拟磁盘,比如[qemu-img](https://qemu-project.gitlab.io/qemu/system/images.html)。
 
-在大多数情况下,我都会尽量保证本机运行无误后,push到仓库中。同时,该系统是经过本机测试,能够在Bochs,QEMU (`= 7.0`),VirtualBox下正常的运行(暂时没试过真机)。如果发现在使用`make all`之后,虚拟机中运行报错,则一般是编译器优化问题。这个问题笔者一般很快就会修复,如果你使用别的版本的gcc(笔者版本11.2),出现了此问题,欢迎提issue。请参考[附录3:Issue的提交](#appendix3)
+在大多数情况下,我都会尽量保证本机运行无误后,push到仓库中。同时,该系统是经过虚拟机和真机测试。如果发现在使用`make all`之后,虚拟机中运行报错,则一般是编译器优化问题。这个问题笔者一般很快就会修复,如果你使用别的版本的gcc(笔者版本11.2),出现了此问题,欢迎提issue。请参考[附录3:Issue的提交](#appendix3)
 
 下面列出一些可能会出现的问题。
 
@@ -94,7 +102,7 @@ qemu-img create -f vdi machine/disk0.vdi 128M
 
 这很大概率是出现了竞态条件。虽然是相当不可能的。但如果出现了,还是请提issue。
 
-#### 问题#3:Bochs运行时,提示找不到AHCI控制器
+#### 问题#3:Bochs无法运行,提示找不到AHCI控制器
 
 正常,**因为Bochs不支持SATA**。请使用QEMU或VirtualBox。
 
@@ -278,4 +286,20 @@ make all-gcc &&\
 + 错误消息(如果给出)
 + 寄存器状态的dump
 + (如可能)提供错误发生时,EIP附近的指令(精确到函数)。如果使用`make all-debug`,会提供`build/kdump.txt`,你可以在这里面定位。或者也可以直接`objdump`
-+ (如可能)虚拟内存映射信息(QEMU下可使用`info mem`查看)。
\ No newline at end of file
++ (如可能)虚拟内存映射信息(QEMU下可使用`info mem`查看)。
+
+## 附录4:串口GDB远程调试
+
+LunaixOS内核集成了最基本的GDB远程调试服务器。可通过串口COM1在9600波特率上与之建立链接。但是,在将GDB与内核链接起来之前,还需要让内核处在调试模式下。
+
+要进入调试模式,需要往串口(波特率如上)写入字节串 `0x40` `0x63` `0x6D` `0x63`。此时,如果屏幕底部出现一条品红色背景的`DEBUG` 字样,那么就说明LunaixOS已处在调试模式下。
+
+注意,在这个时候,LunaixOS会开始在`COM1`上监听GDB协议信息,并且暂停一切的活动(如调度,以及对外部中断的一切响应)。用户此时需要将GDB与其挂载,并使用GDB的工作流来指示内核下一步的动作。
+
+在目前,为了防止代码过于臃肿,LunaixOS实现的是GDB远程协议要求的最小服务端命令子集:`g`, `G`, `p`, `P`, `Q`, `S`, `k`, `?`, `m`, `M`, `X`。足以满足大部分的调试需求。
+
+当结束调试的时候,请使用GDB的`kill`指令进行连接的断开。注意,这个指令会使得LunaixOS恢复所有暂停的活动,进入正常的运行序列,但并不会退出调试模式。GDB的挂载请求依然在LunaixOS中享有最高优先权。如果需要退出调试模式,需要往串口写入字节串:`0x40` `0x79` `0x61` `0x79`。
+
+### GDB调试注意事项
+
+在调试中,请避免使用`info stack`,`bt`或者任何涉及 **栈展开(Stack Unwinding)** 或者 **栈回溯(Stack Backtracing)** 的指令。否则,LunaixOS很有可能会出现 **不可预料的行为** 。
\ No newline at end of file
index b853e0eb11896a149e91cf65fc1c55990d13b137..2a115ca8db43eb7eac765f493bf90e54f53479a1 100644 (file)
@@ -25,12 +25,21 @@ The following list presents all features it does have in current stage.
 + Memory management & demand paging
 + PS/2 Keyboard support
 + Muti-tasking and task management
-+ 17 commonly used POSIX syscall([See Appendix 1](#appendix1))
++ 47 commonly used POSIX syscall([See Appendix 1](#appendix1))
 + User Space
 + Signal
 + PCI 3.0
 + PCIe 1.1 (WIP)
-+ Serial ATA AHCI (WIP)
++ Serial ATA AHCI
++ Virtual File System
++ GDB Remote debugger (via UART)
+  
+The OS has been tested in the following environments, including both virtual and bare-metal.
+
++ QEMU (>=7.0.0)
++ Bochs (Not really, as it lacks support on SATA)
++ Virtualbox
++ Dell G3 3775 Laptop
 
 ## Project Structure
 
@@ -40,6 +49,66 @@ The following list presents all features it does have in current stage.
 | [slides](../slides/) | Slides used in my videos |
 | [reference-material](../reference-material/)| References |
 
+## Compile and Build
+
+You will need following dependencies in order to build LunaixOS
+
++ gcc **(target=i686-elf)**
++ binutils
++ make
++ xorriso
++ grub-mkrescue
+
+The following `make` actions are available to use.
+
+| Action | Description |
+|---|---|
+| `make all` | Build bootable(`-O2`) |
+| `make all-debug` | Build debuggable bootable(`-Og`) |
+| `make run` | Boot the OS with QEMU |
+| `make debug-qemu` | Build and debug with QEMU |
+| `make debug-bochs` | Build and debug with Bochs |
+| `make debug-qemu-vscode` | Used for integrated with vscode for better debug experience |
+| `make clean` | Delete recent build |
+
+When a bootable is built, it is an `.iso` image file and can be found under `build/` directory.
+
+## Running and Issue
+
+To run LunaixOS, you will be required to attach a usable virtual disk image. You can use the following handy command to create one:
+
+```bash
+qemu-img create -f vdi machine/disk0.vdi 128M
+```
+
+However, if you have image other than standard `.vdi`, you shall change `configs/make-debug-tool` accordingly.
+
+Locate this particular line:
+
+```
+-drive id=disk,file="machine/disk0.vdi",if=none \
+```
+
+and replace the default path with your preferred.
+
+There are also many other ways to create disk image, such as the aforementioned `qemu-img`.
+
+The next thing is about issue. In the most common situation, the master branch should be stable in aforementioned running environments. However, one might encounter compiler-optimization related issue when they compiled with `-O2`. Such condition will usually be addressed and fixed in the following commits. Should the issue remains, please post your issue here.
+
+To maximize the value of this section, we will provide some FAQ below that hopefully resolve some "not working" complains:
+
+#### Q1: Prompting "i8042: not found" after boot in QEMU
+
+This is a issue related to misconfiguration of ACPI table in QEMU, and has been addressed in version 7.0.0.
+
+#### Q2: General Protection exception get triggered.
+
+It is possible a race condition result from multiprogramming. This is not possible in current stage, and we however encourage you to report in case of it.
+
+#### Q3: Prompting "AHCI: Not found." after boot in Bochs
+
+This is an expected behaviour, as Bochs does not support SATA!
+
 ## Referenced Tutorial
 
 **THERE IS NONE !** The project is based solely on my understanding on operating system concepts and relevant research. And has referenced no third party tutorial nor open source project like the Linux or other hobbyist project.
@@ -51,6 +120,7 @@ You can find most of aforementioned materials in [reference-material](../referen
 The following list also enumerated such materials the author has used:
 
 #### Manuals, Technical References and Standards
+
 + [Intel 64 and IA-32 Architecture Software Developer's Manual (Full Volume Bundle)](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html)
 + [ACPI Specification (version 6.4)](https://uefi.org/sites/default/files/resources/ACPI_Spec_6_4_Jan22.pdf)
 + IBM PC/AT Technical Reference
@@ -63,6 +133,8 @@ The following list also enumerated such materials the author has used:
 + PCI Firmware Specification, Revision 3.0
 + Serial ATA - Advanced Host Controller Interface (AHCI), Revision 1.3.1
 + Serial ATA: HIgh Speed Serialized AT Attachment, Revision 3.2
++ SCSI Command Reference Manual
++ ATA/ATAPI Command Set - 3 (ACS-3)
 
 **DISCLAIMER: All rights of PCI-related specification is reserved by PCI-SIG. It is provided ONLY for learning purpose. Any commercial use should purchase a copy from PCI-SIG**
 
@@ -83,7 +155,7 @@ The following list also enumerated such materials the author has used:
 
 ## Appendix 1: Supported System Call<a id="appendix1"></a>
 
-### Unix/Linux/POSIX
+**Unix/Linux/POSIX**
 1. `sleep(3)`
 1. `wait(2)`
 1. `waitpid(2)`
@@ -101,7 +173,57 @@ The following list also enumerated such materials the author has used:
 1. `kill(2)`
 1. `sigpending(2)`
 1. `sigsuspend(2)`
-
-### Unique to LunaixOS
+2. `read(2)`
+2. `write(2)`
+2. `open(2)`
+2. `close(2)`
+2. `mkdir(2)`※
+2. `lseek(2)`
+2. `readdir(2)`
+2. `readlink(2)`※
+2. `readlinkat(2)`※
+2. `rmdir(2)`※
+2. `unlink(2)`※
+2. `unlinkat(2)`※
+2. `link(2)`※
+2. `fsync(2)`※
+2. `dup(2)`
+2. `dup2(2)`
+2. `symlink(2)`※
+2. `chdir(2)`
+2. `fchdir(2)`
+2. `getcwd(2)`
+2. `rename(2)`※
+2. `mount(2)`
+2. `unmount` (a.k.a `umount(2)`)※
+2. `getxattr(2)`※
+2. `setxattr(2)`※
+2. `fgetxattr(2)`※
+2. `fsetxattr(2)`※
+2. `ioctl(2)`※
+2. `getpgid(2)`
+2. `setpgid(2)`
+
+**LunaixOS**
 
 1. `yield`
+2. `geterrno`
+3. `realpathat`
+
+( **※**:Indicate syscall is not tested )
+
+## Appendix 2: Debugging with GDB remotely via UART
+
+The LunaixOS kernel comes with a built-in GDB debugging server, which runs on COM1@9600Bd. However, LunaixOS must be in debug mode before involving GDB.
+
+One could trigger the debug mode by writing a byte sequence `0x40` `0x63` `0x6D` `0x63`, to the same serial port. A text "DEBUG MODE" with magenta-coloured background shall be present at the bottom of the screen.
+
+Note that, whenever the text appears, the LunaixOS always halt all activities other than the debugging server, which means no scheduling and no external interrupt servicing. Users are now recommended to attach their GDB and drive the kernel with the debugging workflow.
+
+Currently, LunaixOS implements the required minimal server-side command subset required by GDB Remote Protocol, namely, `g`, `G`, `p`, `P`, `Q`, `S`, `k`, `?`, `m`, `M`, `X`. Which should be enough to cover most debugging activities.
+
+When debugging is finished, one shall disconnect with `kill` command. This command will not force LunaixOS to power down the computer, instead it just resume the execution (identical behavior as `c` command). However, disconnecting does not means exiting of debug mode. The debug mode is still actived and any subsequent GDB attaching request shall remain the highest priority amongst all other activity. One shall deactivate the debug mode by writing byte sequence `0x40` `0x79` `0x61` `0x79` to the port, after GDB detached.
+
+### Limitations
+
+Currently, one should avoid the use of `info stack`, `bt` or any other command that involves stack unwinding or stack backtracing. As it will somehow corrupt the stack layout and result in undefined behaviour. This issue should be addressed in future releases.
index 537e9cde07416d97bc5054737ac8eaa91f7662de..2309ece105496d9e09416f9a7f4e468ee49f0592 100644 (file)
@@ -10,6 +10,8 @@
 
 // #define USE_LSDBG_BACKEND
 
+LOG_MODULE("SDBG")
+
 volatile int debug_mode = 0;
 
 void
@@ -68,6 +70,36 @@ done:
     serial_enable_irq(SERIAL_COM1);
 }
 
+void
+sdbg_imm(const isr_param* param)
+{
+    kprintf(KDEBUG "Quick debug mode\n");
+    kprintf(KDEBUG "cs=%p eip=%p eax=%p ebx=%p\n",
+            param->cs,
+            param->eip,
+            param->registers.eax,
+            param->registers.ebx);
+    kprintf(KDEBUG "ecx=%p edx=%p edi=%p esi=%p\n",
+            param->registers.ecx,
+            param->registers.edx,
+            param->registers.edi,
+            param->registers.esi);
+    kprintf(KDEBUG "u.esp=%p k.esp=%p ebp=%p ps=%p\n",
+            param->registers.esp,
+            param->esp,
+            param->registers.ebp,
+            param->eflags);
+    kprintf(KDEBUG "ss=%p ds=%p es=%p fs=%p gs=%p\n",
+            param->ss,
+            param->registers.ds,
+            param->registers.es,
+            param->registers.fs,
+            param->registers.gs);
+    console_flush();
+    while (1)
+        ;
+}
+
 extern uint8_t
 ioapic_get_irq(acpi_context* acpi_ctx, uint8_t old_irq);
 
@@ -76,6 +108,7 @@ sdbg_init()
 {
     intr_subscribe(UART_COM1, sdbg_loop);
     intr_subscribe(INSTR_DEBUG, sdbg_loop); // #DB
+    intr_subscribe(INSTR_BREAK, sdbg_loop); // #BRK
 
     acpi_context* acpi_ctx = acpi_get_context();
     uint8_t irq = ioapic_get_irq(acpi_ctx, COM1_IRQ);
index aa6acd16f036ff0adb8b2c864a0370a54ae750a9..04c75752180358108afb4e34f4d8261756df480c 100644 (file)
@@ -73,7 +73,7 @@ pci_probe()
 {
     // 暴力扫描所有PCI设备
     // XXX: 尽管最多会有256条PCI总线,但就目前而言,只考虑bus #0就足够了
-    for (int bus = 0; bus < 1; bus++) {
+    for (int bus = 0; bus < 256; bus++) {
         for (int dev = 0; dev < 32; dev++) {
             pci_probe_device(bus, dev, 0);
         }
index acf28b4ac02cdddcf831bcce876c63a0bdfdf871..7f27542c20a98e7021c871700a8ab27e2e9cfb64 100644 (file)
@@ -6,7 +6,7 @@
 #define FAULT_DIVISION_ERROR            0
 #define INSTR_DEBUG                     1
 #define INT_NMI                         2
-#define TRAP_BREAKPOINT                 3
+#define INSTR_BREAK                     3
 #define TRAP_OVERFLOW                   4
 #define FAULT_BOUND_EXCEED              5
 #define FAULT_INVALID_OPCODE            6
index 450353538d6d17d38ad02ec7a3ee88d2fdec5488..91f5460ab175680273ccb5e8082ce979dcc09188 100644 (file)
@@ -43,7 +43,7 @@ pmm_mark_chunk_occupied(pid_t owner,
 // 我们跳过位于0x0的页。我们不希望空指针是指向一个有效的内存空间。
 #define LOOKUP_START 1
 
-size_t pg_lookup_ptr;
+volatile size_t pg_lookup_ptr;
 
 void
 pmm_init(uintptr_t mem_upper_lim)
index 6e134fd0e2ec4da15a1590fe0ccb971cde1a3d8e..3b28bd980eefb5bdb54d6439871c8c964d3ad75a 100644 (file)
@@ -3,6 +3,9 @@
 #include <lunaix/mm/pmm.h>
 #include <lunaix/mm/vmm.h>
 #include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+
+LOG_MODULE("VMM")
 
 void
 vmm_init()
@@ -54,6 +57,9 @@ vmm_set_mapping(uintptr_t mnt,
         l1pt->entry[l1_inx] =
           NEW_L1_ENTRY(attr | PG_WRITE | PG_PRESENT, new_l1pt_pa);
 
+        // make sure our new l2 table is visible to CPU
+        cpu_invplg(l2pt);
+
         memset((void*)l2pt, 0, PG_SIZE);
     } else {
         x86_pte_t pte = l2pt->entry[l2_inx];
index 6f17176c422a0f27c4b92b81b15f64997bd15f02..df6ffc1ade4ed728e19a87fa9906d21ca6284e97 100644 (file)
@@ -85,6 +85,9 @@ static struct input_device* kbd_idev;
 void
 intr_ps2_kbd_handler(const isr_param* param);
 
+static uint8_t
+ps2_issue_cmd_wretry(char cmd, uint16_t arg);
+
 void
 ps2_device_post_cmd(char cmd, char arg)
 {
@@ -157,27 +160,30 @@ ps2_kbd_init()
 
     // 3、屏蔽所有PS/2设备(端口1&2)IRQ,并且禁用键盘键码转换功能
     result = ps2_issue_cmd(PS2_CMD_READ_CFG, PS2_NO_ARG);
-    result = result & ~(PS2_CFG_P1INT | PS2_CFG_P2INT | PS2_CFG_TRANSLATION);
+    result = result & ~(PS2_CFG_P1INT | PS2_CFG_P2INT);
     ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_WRITE_CFG, result);
 
     // 4、控制器自检
-    result = ps2_issue_cmd(PS2_CMD_SELFTEST, PS2_NO_ARG);
+    result = ps2_issue_cmd_wretry(PS2_CMD_SELFTEST, PS2_NO_ARG);
     if (result != PS2_RESULT_TEST_OK) {
-        kprintf(KERROR "Controller self-test failed.");
-        goto done;
+        kprintf(KWARN "Controller self-test failed. (%x)\n", result);
+        // goto done;
     }
 
     // 5、设备自检(端口1自检,通常是我们的键盘)
-    result = ps2_issue_cmd(PS2_CMD_SELFTEST_PORT1, PS2_NO_ARG);
+    result = ps2_issue_cmd_wretry(PS2_CMD_SELFTEST_PORT1, PS2_NO_ARG);
     if (result != 0) {
-        kprintf(KERROR "Interface test on port 1 failed.");
-        goto done;
+        kprintf(KERROR "Interface test on port 1 failed. (%x)\n", result);
+        // goto done;
     }
 
+    ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT2_DISABLE, PS2_NO_ARG);
+
     // 6、开启位于端口1的 IRQ,并启用端口1。不用理会端口2,那儿一般是鼠标。
     ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT1_ENABLE, PS2_NO_ARG);
     result = ps2_issue_cmd(PS2_CMD_READ_CFG, PS2_NO_ARG);
-    result = result | PS2_CFG_P1INT;
+    // 重新设置配置字节,因为控制器自检有可能重置我们先前做的改动。
+    result = (result | PS2_CFG_P1INT) & ~(PS2_CFG_TRANSLATION | PS2_CFG_P2INT);
     ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_WRITE_CFG, result);
 
     // 至此,PS/2控制器和设备已完成初始化,可以正常使用。
@@ -241,7 +247,6 @@ ps2_process_cmd(void* arg)
 #endif
         attempts++;
     } while (result == PS2_RESULT_NAK && attempts < PS2_DEV_CMD_MAX_ATTEMPTS);
-
     // XXX: 是否需要处理不成功的指令?
 
     cmd_q.queue_ptr = (cmd_q.queue_ptr + 1) % PS2_CMD_QUEUE_SIZE;
@@ -297,8 +302,8 @@ intr_ps2_kbd_handler(const isr_param* param)
 {
 
     // This is important! Don't believe me? try comment it out and run on Bochs!
-    while (!(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_OFULL))
-        ;
+    // while (!(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_OFULL))
+    //    ;
 
     // I know you are tempting to move this chunk after the keyboard state
     // check. But DO NOT. This chunk is in right place and right order. Moving
@@ -414,6 +419,19 @@ ps2_issue_cmd(char cmd, uint16_t arg)
     return io_inb(PS2_PORT_ENC_CMDREG);
 }
 
+static uint8_t
+ps2_issue_cmd_wretry(char cmd, uint16_t arg)
+{
+    uint8_t r, c = 0;
+    while ((r = ps2_issue_cmd(cmd, arg)) == PS2_RESULT_NAK && c < 5) {
+        c++;
+    }
+    if (c >= 5) {
+        kprintf(KWARN "Max attempt reached.\n");
+    }
+    return r;
+}
+
 static void
 ps2_post_cmd(uint8_t port, char cmd, uint16_t arg)
 {
@@ -426,6 +444,8 @@ ps2_post_cmd(uint8_t port, char cmd, uint16_t arg)
 
     if (!(arg & PS2_NO_ARG)) {
         // 所有参数一律通过0x60传入。
+        while (io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_IFULL)
+            ;
         io_outb(PS2_PORT_ENC_CMDREG, (uint8_t)(arg & 0x00ff));
         io_delay(PS2_DELAY);
     }