From a89063b334fdc0b254f0791fca1812354049b86d Mon Sep 17 00:00:00 2001 From: Minep Date: Tue, 28 Jun 2022 12:52:50 +0100 Subject: [PATCH 1/1] feat: partial PCIe support (MCFG table parsing) fix: stack overflow when performing PCI probing in QEMU Q35 machine type chore: noting some emulator related issues. chore: update readme. --- README.md | 3 +- docs/README_en.md | 3 +- lunaix-os/config/make-debug-tool | 10 +- lunaix-os/hal/acpi/acpi.c | 11 +- lunaix-os/hal/acpi/parser/madt_parser.c | 5 +- lunaix-os/hal/acpi/parser/madt_parser.h | 14 -- lunaix-os/hal/acpi/parser/mcfg_parser.c | 27 +++ lunaix-os/hal/acpi/parser/parser.h | 21 ++ lunaix-os/hal/pci.c | 17 +- lunaix-os/includes/hal/acpi/acpi.h | 28 ++- lunaix-os/includes/hal/acpi/mcfg.h | 30 +++ lunaix-os/kernel/peripheral/ps2kbd.c | 261 ++++++++++++++---------- lunaix-os/makefile | 6 +- 13 files changed, 287 insertions(+), 149 deletions(-) delete mode 100644 lunaix-os/hal/acpi/parser/madt_parser.h create mode 100644 lunaix-os/hal/acpi/parser/mcfg_parser.c create mode 100644 lunaix-os/hal/acpi/parser/parser.h create mode 100644 lunaix-os/includes/hal/acpi/mcfg.h diff --git a/README.md b/README.md index 1575b3c..0338614 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ LunaixOS - 一个简单的,详细的,POSIX兼容的(但愿!),带有 + 17个常见的Linux/POSIX系统调用([附录1](#appendix1)) + 用户模式 + 信号机制 -+ PCI 3.0 (WIP) ++ PCI 3.0 ++ PCIe 2.0 (WIP) ## 目录结构 diff --git a/docs/README_en.md b/docs/README_en.md index 7414ee5..c602fc3 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -28,7 +28,8 @@ The following list presents all features it does have in current stage. + 17 commonly used POSIX syscall([See Appendix 1](#appendix1)) + User Space + Signal -+ PCI 3.0 (WIP) ++ PCI 3.0 ++ PCIe 2.0 (WIP) ## Project Structure diff --git a/lunaix-os/config/make-debug-tool b/lunaix-os/config/make-debug-tool index 2817534..647a456 100644 --- a/lunaix-os/config/make-debug-tool +++ b/lunaix-os/config/make-debug-tool @@ -1,2 +1,10 @@ QEMU_MON_TERM := gnome-terminal -QEMU_MON_PORT := 45454 \ No newline at end of file +QEMU_MON_PORT := 45454 + +QEMU_OPTIONS := -s -S -m 1G \ + -rtc base=utc \ + -no-reboot \ + -no-shutdown \ + -d cpu_reset \ + -device ahci,id=ahci \ + -monitor telnet::$(QEMU_MON_PORT),server,nowait & \ No newline at end of file diff --git a/lunaix-os/hal/acpi/acpi.c b/lunaix-os/hal/acpi/acpi.c index 5713f9a..0b1d988 100644 --- a/lunaix-os/hal/acpi/acpi.c +++ b/lunaix-os/hal/acpi/acpi.c @@ -6,7 +6,7 @@ #include -#include "parser/madt_parser.h" +#include "parser/parser.h" static acpi_context* ctx = NULL; @@ -26,7 +26,7 @@ acpi_init(multiboot_info_t* mb_info) assert_msg(rsdp, "Fail to locate ACPI_RSDP"); assert_msg(acpi_rsdp_validate(rsdp), "Invalid ACPI_RSDP (checksum failed)"); - kprintf(KINFO "RSDP found at %p, RSDT: %p\n", rsdp, rsdp->rsdt); + kprintf(KDEBUG "RSDP found at %p, RSDT: %p\n", rsdp, rsdp->rsdt); acpi_rsdt_t* rsdt = rsdp->rsdt; @@ -47,21 +47,22 @@ acpi_init(multiboot_info_t* mb_info) // FADT just a plain structure, no need to parse. ctx->fadt = *(acpi_fadt_t*)sdthdr; break; + case ACPI_MCFG_SIG: + mcfg_parse(sdthdr, ctx); + break; default: break; } } kprintf(KINFO "OEM: %s\n", ctx->oem_id); - kprintf(KINFO "IOAPIC address: %p\n", ctx->madt.ioapic->ioapic_addr); - kprintf(KINFO "APIC address: %p\n", ctx->madt.apic_addr); for (size_t i = 0; i < 24; i++) { acpi_intso_t* intso = ctx->madt.irq_exception[i]; if (!intso) continue; - kprintf(KINFO "IRQ #%u -> GSI #%u\n", intso->source, intso->gsi); + kprintf(KDEBUG "IRQ #%u -> GSI #%u\n", intso->source, intso->gsi); } } diff --git a/lunaix-os/hal/acpi/parser/madt_parser.c b/lunaix-os/hal/acpi/parser/madt_parser.c index baa866c..971a4a0 100644 --- a/lunaix-os/hal/acpi/parser/madt_parser.c +++ b/lunaix-os/hal/acpi/parser/madt_parser.c @@ -1,4 +1,4 @@ -#include "madt_parser.h" +#include "parser.h" #include void @@ -25,8 +25,7 @@ madt_parse(acpi_madt_t* madt, acpi_context* toc) case ACPI_MADT_IOAPIC: toc->madt.ioapic = (acpi_ioapic_t*)entry; break; - case ACPI_MADT_INTSO: - { + case ACPI_MADT_INTSO: { acpi_intso_t* intso_tbl = (acpi_intso_t*)entry; toc->madt.irq_exception[intso_tbl->source] = intso_tbl; break; diff --git a/lunaix-os/hal/acpi/parser/madt_parser.h b/lunaix-os/hal/acpi/parser/madt_parser.h deleted file mode 100644 index 6b15296..0000000 --- a/lunaix-os/hal/acpi/parser/madt_parser.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __LUNAIX_PARSER_MADT_PARSER_H -#define __LUNAIX_PARSER_MADT_PARSER_H - -#include - -/** - * @brief Parse the MADT and populated into main TOC - * - * @param rsdt RSDT - * @param toc The main TOC - */ -void madt_parse(acpi_madt_t* madt, acpi_context* toc); - -#endif /* __LUNAIX_PARSER_MADT_PARSER_H */ diff --git a/lunaix-os/hal/acpi/parser/mcfg_parser.c b/lunaix-os/hal/acpi/parser/mcfg_parser.c new file mode 100644 index 0000000..0efe0d5 --- /dev/null +++ b/lunaix-os/hal/acpi/parser/mcfg_parser.c @@ -0,0 +1,27 @@ +#include "lunaix/syslog.h" +#include "parser.h" +#include + +LOG_MODULE("MCFG") + +void +mcfg_parse(acpi_sdthdr_t* mcfg, acpi_context* toc) +{ + size_t alloc_num = (mcfg->length - sizeof(acpi_sdthdr_t) - 8) / + sizeof(struct acpi_mcfg_alloc); + struct acpi_mcfg_alloc* allocs = + (struct acpi_mcfg_alloc*)((uintptr_t)mcfg + (sizeof(acpi_sdthdr_t) + 8)); + + toc->mcfg.alloc_num = alloc_num; + toc->mcfg.allocations = + lxmalloc(sizeof(struct mcfg_alloc_info) * alloc_num); + + for (size_t i = 0; i < alloc_num; i++) { + toc->mcfg.allocations[i] = (struct mcfg_alloc_info){ + .base_addr = allocs[i].base_addr_lo, + .pci_bus_start = allocs[i].pci_bus_start, + .pci_bus_end = allocs[i].pci_bus_end, + .pci_seg_num = allocs[i].pci_seg_num, + }; + } +} \ No newline at end of file diff --git a/lunaix-os/hal/acpi/parser/parser.h b/lunaix-os/hal/acpi/parser/parser.h new file mode 100644 index 0000000..f0ddd74 --- /dev/null +++ b/lunaix-os/hal/acpi/parser/parser.h @@ -0,0 +1,21 @@ +#ifndef __LUNAIX_PARSER_PARSER_H +#define __LUNAIX_PARSER_PARSER_H + +#include + +/** + * @brief Parse the MADT and populated into main TOC + * + * @param rsdt RSDT + * @param toc The main TOC + */ +void +madt_parse(acpi_madt_t* madt, acpi_context* toc); + +void +mcfg_parse(acpi_sdthdr_t* madt, acpi_context* toc); + +void +mcfg_parse(acpi_sdthdr_t* mcfg, acpi_context* toc); + +#endif /* __LUNAIX_PARSER_PARSER_H */ diff --git a/lunaix-os/hal/pci.c b/lunaix-os/hal/pci.c index dd4e95b..32299cd 100644 --- a/lunaix-os/hal/pci.c +++ b/lunaix-os/hal/pci.c @@ -1,6 +1,8 @@ +#include #include #include #include +#include #include LOG_MODULE("PCI") @@ -24,7 +26,10 @@ pci_probe_device(int bus, int dev, int funct) pci_reg_t hdr_type = pci_read_cspace(base, 0xc); hdr_type = (hdr_type >> 16) & 0xff; - if ((hdr_type & 0x80)) { + // 防止堆栈溢出 + // QEMU的ICH9/Q35实现似乎有点问题,对于多功能设备的每一个功能的header type + // 都将第七位置位。而virtualbox 就没有这个毛病。 + if ((hdr_type & 0x80) && funct == 0) { hdr_type = hdr_type & ~0x80; // 探测多用途设备(multi-function device) for (int i = 1; i < 7; i++) { @@ -66,6 +71,9 @@ pci_probe() void pci_probe_msi_info(struct pci_device* device) { + // Note that Virtualbox have to use ICH9 chipset for MSI support. + // Qemu seems ok with default PIIX3, Bochs is pending to test... + // See https://www.virtualbox.org/manual/ch03.html (section 3.5.1) pci_reg_t status = pci_read_cspace(device->cspace_base, PCI_REG_STATUS_CMD) >> 16; @@ -205,5 +213,12 @@ void pci_init() { llist_init_head(&pci_devices); + acpi_context* acpi = acpi_get_context(); + assert_msg(acpi, "ACPI not initialized."); + if (acpi->mcfg.alloc_num) { + // PCIe Enhanced Configuration Mechanism is supported. + // TODO: support PCIe addressing mechanism + } + // Otherwise, fallback to use legacy PCI 3.0 method. pci_probe(); } \ No newline at end of file diff --git a/lunaix-os/includes/hal/acpi/acpi.h b/lunaix-os/includes/hal/acpi/acpi.h index 453e9f4..2e788d2 100644 --- a/lunaix-os/includes/hal/acpi/acpi.h +++ b/lunaix-os/includes/hal/acpi/acpi.h @@ -1,24 +1,29 @@ #ifndef __LUNAIX_ACPI_ACPI_H #define __LUNAIX_ACPI_ACPI_H -#include -#include #include +#include +#include -#include "sdt.h" -#include "madt.h" #include "fadt.h" +#include "madt.h" +#include "mcfg.h" +#include "sdt.h" // * for quick conversion from a table name into ACPI favoured signature // * use `echo | xxd -eg4` -#define ACPI_RSDP_SIG_L 0x20445352 // 'RSD ' -#define ACPI_RSDP_SIG_H 0x20525450 // 'PTR ' +#define ACPI_RSDP_SIG_L 0x20445352 // 'RSD ' +#define ACPI_RSDP_SIG_H 0x20525450 // 'PTR ' + +#define ACPI_MADT_SIG 0x43495041 // 'APIC' +#define ACPI_FADT_SIG 0x50434146 // 'FACP' Notice that it is not 'FADT'. -#define ACPI_MADT_SIG 0x43495041 // 'APIC' -#define ACPI_FADT_SIG 0x50434146 // 'FACP' Notice that it is not 'FADT'. +// 'MCFG' (Not part of ACPI standard. See PCI Firmware Spec.) +#define ACPI_MCFG_SIG 0x4746434d -typedef struct { +typedef struct +{ uint32_t signature_l; uint32_t signature_h; uint8_t chksum; @@ -29,12 +34,12 @@ typedef struct { uint32_t length; acpi_sdthdr_t* xsdt; uint8_t x_chksum; - char reserved[3]; // Reserved field + char reserved[3]; // Reserved field } __attribute__((packed)) acpi_rsdp_t; /** * @brief Main TOC of ACPI tables, provide hassle-free access of ACPI info. - * + * */ typedef struct { @@ -42,6 +47,7 @@ typedef struct char oem_id[7]; acpi_madt_toc_t madt; acpi_fadt_t fadt; + struct acpi_mcfg_toc mcfg; } acpi_context; int diff --git a/lunaix-os/includes/hal/acpi/mcfg.h b/lunaix-os/includes/hal/acpi/mcfg.h new file mode 100644 index 0000000..0ff86e0 --- /dev/null +++ b/lunaix-os/includes/hal/acpi/mcfg.h @@ -0,0 +1,30 @@ +#ifndef __LUNAIX_MCFG_H +#define __LUNAIX_MCFG_H + +#include "sdt.h" + +struct acpi_mcfg_alloc +{ + uint32_t base_addr_lo; + uint32_t base_addr_hi; + uint16_t pci_seg_num; + uint8_t pci_bus_start; + uint8_t pci_bus_end; + uint32_t reserve; +} ACPI_TABLE_PACKED; + +struct mcfg_alloc_info +{ + uint32_t base_addr; + uint16_t pci_seg_num; + uint8_t pci_bus_start; + uint8_t pci_bus_end; +}; + +struct acpi_mcfg_toc +{ + size_t alloc_num; + struct mcfg_alloc_info* allocations; +}; + +#endif /* __LUNAIX_MCFG_H */ diff --git a/lunaix-os/kernel/peripheral/ps2kbd.c b/lunaix-os/kernel/peripheral/ps2kbd.c index ece9eab..72f5d1f 100644 --- a/lunaix-os/kernel/peripheral/ps2kbd.c +++ b/lunaix-os/kernel/peripheral/ps2kbd.c @@ -1,13 +1,13 @@ -#include +#include +#include #include -#include #include +#include #include -#include -#include +#include -#include #include +#include #include #include @@ -20,11 +20,13 @@ static struct ps2_cmd_queue cmd_q; static struct ps2_key_buffer key_buf; static struct ps2_kbd_state kbd_state; -#define KEY_NUM(x) (x + 0x30) -#define KEY_NPAD(x) ON_KEYPAD(KEY_NUM(x)) +#define KEY_NUM(x) (x + 0x30) +#define KEY_NPAD(x) ON_KEYPAD(KEY_NUM(x)) // 我们使用 Scancode Set 2 +// clang-format off + // 大部分的扫描码(键码) static kbd_keycode_t scancode_set2[] = { 0, KEY_F9, 0, KEY_F5, KEY_F3, KEY_F1, KEY_F2, KEY_F12, 0, KEY_F10, KEY_F8, KEY_F6, @@ -65,19 +67,24 @@ static kbd_keycode_t scancode_set2_shift[] = { KEY_NPAD(3), ON_KEYPAD('-'), ON_KEYPAD('*'), KEY_SCRLLK, 0, 0, 0, 0, KEY_F7 }; +// clang-format on -#define KBD_STATE_KWAIT 0x00 -#define KBD_STATE_KSPECIAL 0x01 -#define KBD_STATE_KRELEASED 0x02 -#define KBD_STATE_CMDPROCS 0x40 +#define KBD_STATE_KWAIT 0x00 +#define KBD_STATE_KSPECIAL 0x01 +#define KBD_STATE_KRELEASED 0x02 +#define KBD_STATE_CMDPROCS 0x40 #define KBD_ENABLE_SPIRQ_FIX // #define KBD_DBGLOG -void intr_ps2_kbd_handler(const isr_param* param); -static struct kdb_keyinfo_pkt* ps2_keybuffer_next_write(); +void +intr_ps2_kbd_handler(const isr_param* param); +static struct kdb_keyinfo_pkt* +ps2_keybuffer_next_write(); -void ps2_device_post_cmd(char cmd, char arg) { +void +ps2_device_post_cmd(char cmd, char arg) +{ mutex_lock(&cmd_q.mutex); int index = (cmd_q.queue_ptr + cmd_q.queue_len) % PS2_CMD_QUEUE_SIZE; if (index == cmd_q.queue_ptr && cmd_q.queue_len) { @@ -86,7 +93,7 @@ void ps2_device_post_cmd(char cmd, char arg) { return; } - struct ps2_cmd *container = &cmd_q.cmd_queue[index]; + struct ps2_cmd* container = &cmd_q.cmd_queue[index]; container->cmd = cmd; container->arg = arg; cmd_q.queue_len++; @@ -95,16 +102,17 @@ void ps2_device_post_cmd(char cmd, char arg) { mutex_unlock(&cmd_q.mutex); } -void ps2_kbd_init() { +void +ps2_kbd_init() +{ memset(&cmd_q, 0, sizeof(cmd_q)); memset(&key_buf, 0, sizeof(key_buf)); memset(&kbd_state, 0, sizeof(kbd_state)); - + mutex_init(&cmd_q.mutex); mutex_init(&key_buf.mutex); - kbd_state.translation_table = scancode_set2; kbd_state.state = KBD_STATE_KWAIT; @@ -113,33 +121,39 @@ void ps2_kbd_init() { /* * 只有当前ACPI版本大于1时,我们才使用FADT的IAPC_BOOT_ARCH去判断8042是否存在。 * 这是一个坑,在ACPI v1中,这个字段是reserved!而这及至ACPI v2才出现。 - * 需要注意:Bochs 和 QEMU 使用的是ACPI v1,而非 v2 (virtualbox好像是v4) - * + * 需要注意:Bochs 和 QEMU 使用的是ACPI v1,而非 v2 + * (virtualbox好像是v4) + * + * (2022/6/28) + * QEMU似乎在 Q35 + ICH9 支持了 ACPI + * v2。但是对于IAPC_BOOT_ARCH的设置还是 停留在ACPI + * v1的时代。IAPC_ARCH_8042没有被正确的设置。这是一个今年的bug,好像还未修复 + * 参考:https://lore.kernel.org/all/20220304115257.1816983-1-ani@anisinha.ca/T/ + * * 请看Bochs的bios源码(QEMU的BIOS其实是照抄bochs的,所以也是一个德行。。): * https://bochs.sourceforge.io/cgi-bin/lxr/source/bios/rombios32.c#L1314 */ if (!(acpi_ctx->fadt.boot_arch & IAPC_ARCH_8042)) { kprintf(KERROR "No PS/2 controller detected.\n"); - // FUTURE: Some alternative fallback on this? Check PCI bus for USB controller instead? + // FUTURE: Some alternative fallback on this? Check PCI bus for USB + // controller instead? return; } - } - else { + } else { kprintf(KWARN "Outdated FADT used, assuming 8042 always exist.\n"); } - + char result; - + cpu_disable_interrupt(); // 1、禁用任何的PS/2设备 ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT1_DISABLE, PS2_NO_ARG); ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT2_DISABLE, PS2_NO_ARG); - + // 2、清空控制器缓冲区 io_inb(PS2_PORT_ENC_DATA); - // 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); @@ -170,7 +184,8 @@ void ps2_kbd_init() { // 将我们的键盘驱动挂载到第204号中断上(已由IOAPIC映射至IRQ#1), intr_subscribe(PC_KBD_IV, intr_ps2_kbd_handler); - // 搞一个计时器,将我们的 ps2_process_cmd 挂上去。每隔5毫秒执行排在队头的命令。 + // 搞一个计时器,将我们的 ps2_process_cmd + // 挂上去。每隔5毫秒执行排在队头的命令。 // 为什么只执行队头的命令,而不是全部的命令? // 因为我们需要保证isr尽量的简短,运行起来快速。而发送这些命令非常的耗时。 timer_run_ms(5, ps2_process_cmd, NULL, TIMER_MODE_PERIODIC); @@ -182,9 +197,9 @@ void ps2_kbd_init() { * 初始化8042,屏蔽了所有中断,IF=0)。 * 当sti后,这些堆积的中断会紧跟着递送进CPU里,导致我们的键盘handler误认为由按键按下,从而将这个毫无意义的数值加入 * 我们的队列中,以供上层读取。 - * - * 所以,保险的方法是:在初始化后才去设置ioapic,这样一来我们就能有一个稳定的IRQ#1以放心使用。 - */ + * + * 所以,保险的方法是:在初始化后才去设置ioapic,这样一来我们就能有一个稳定的IRQ#1以放心使用。 + */ uint8_t irq_kbd = ioapic_get_irq(acpi_ctx, PC_AT_IRQ_KBD); ioapic_redirect(irq_kbd, PC_KBD_IV, 0, IOAPIC_DELMOD_FIXED); @@ -192,12 +207,15 @@ done: cpu_enable_interrupt(); } -void ps2_process_cmd(void* arg) { - /* +void +ps2_process_cmd(void* arg) +{ + /* * 检查锁是否已被启用,如果启用,则表明该timer中断发生时,某个指令正在入队。 * 如果是这种情况则跳过,留到下一轮再尝试处理。 * 注意,这里其实是ISR的一部分(timer中断),对于单核CPU来说,ISR等同于单个的原子操作。 - * (因为EFLAGS.IF=0,所有可屏蔽中断被屏蔽。对于NMI的情况,那么就直接算是triple fault了,所以也没有讨论的意义) + * (因为EFLAGS.IF=0,所有可屏蔽中断被屏蔽。对于NMI的情况,那么就直接算是triple + * fault了,所以也没有讨论的意义) * 所以,假若我们遵从互斥锁的严格定义(即这里需要阻塞),那么中断将会被阻塞,进而造成死锁。 * 因此,我们这里仅仅进行判断。 * 会不会产生指令堆积?不会,因为指令发送的频率远远低于指令队列清空的频率。在目前,我们发送的唯一指令 @@ -208,7 +226,7 @@ void ps2_process_cmd(void* arg) { } // 处理队列排头的指令 - struct ps2_cmd *pending_cmd = &cmd_q.cmd_queue[cmd_q.queue_ptr]; + struct ps2_cmd* pending_cmd = &cmd_q.cmd_queue[cmd_q.queue_ptr]; char result; int attempts = 0; @@ -221,18 +239,20 @@ void ps2_process_cmd(void* arg) { kbd_state.state += KBD_STATE_CMDPROCS; #endif attempts++; - } while(result == PS2_RESULT_NAK && attempts < PS2_DEV_CMD_MAX_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; cmd_q.queue_len--; } -void kbd_buffer_key_event(kbd_keycode_t key, uint8_t scancode, kbd_kstate_t state) { - /* - forgive me on these ugly bit-level tricks, - I really hate doing branching on these "fliping switch" things +void +kbd_buffer_key_event(kbd_keycode_t key, uint8_t scancode, kbd_kstate_t state) +{ + /* + forgive me on these ugly bit-level tricks, + I really hate doing branching on these "fliping switch" things */ if (key == KEY_CAPSLK) { kbd_state.key_state ^= KBD_KEY_FCAPSLKED & -state; @@ -244,21 +264,22 @@ void kbd_buffer_key_event(kbd_keycode_t key, uint8_t scancode, kbd_kstate_t stat if ((key & MODIFR)) { kbd_kstate_t tmp = (KBD_KEY_FLSHIFT_HELD << (key & 0x00ff)); kbd_state.key_state = (kbd_state.key_state & ~tmp) | (tmp & -state); - } - else if (!(key & 0xff00) && (kbd_state.key_state & (KBD_KEY_FLSHIFT_HELD | KBD_KEY_FRSHIFT_HELD))) { + } else if (!(key & 0xff00) && + (kbd_state.key_state & + (KBD_KEY_FLSHIFT_HELD | KBD_KEY_FRSHIFT_HELD))) { key = scancode_set2_shift[scancode]; } state = state | kbd_state.key_state; - key = key & (0xffdf | -('a' > key || key > 'z' || !(state & KBD_KEY_FCAPSLKED))); + key = key & (0xffdf | + -('a' > key || key > 'z' || !(state & KBD_KEY_FCAPSLKED))); if (!mutex_on_hold(&key_buf.mutex)) { struct kdb_keyinfo_pkt* keyevent_pkt = ps2_keybuffer_next_write(); - *keyevent_pkt = (struct kdb_keyinfo_pkt) { - .keycode = key, - .scancode = scancode, - .state = state, - .timestamp = clock_systime() - }; + *keyevent_pkt = + (struct kdb_keyinfo_pkt){ .keycode = key, + .scancode = scancode, + .state = state, + .timestamp = clock_systime() }; } return; @@ -266,40 +287,49 @@ void kbd_buffer_key_event(kbd_keycode_t key, uint8_t scancode, kbd_kstate_t stat if (state & KBD_KEY_FPRESSED) { // Ooops, this guy generates irq! - ps2_device_post_cmd(PS2_KBD_CMD_SETLED, (kbd_state.key_state >> 1) & 0x00ff); + ps2_device_post_cmd(PS2_KBD_CMD_SETLED, + (kbd_state.key_state >> 1) & 0x00ff); } } -void intr_ps2_kbd_handler(const isr_param* param) { +void +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 it at your own risk - // This is to ensure we've cleared the output buffer everytime, so it won't pile up across irqs. + // 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 + // it at your own risk This is to ensure we've cleared the output buffer + // everytime, so it won't pile up across irqs. uint8_t scancode = io_inb(PS2_PORT_ENC_DATA); kbd_keycode_t key; - /* + /* * 判断键盘是否处在指令发送状态,防止误触发。(伪输入中断) * 这是因为我们需要向ps/2设备发送指令(比如控制led灯),而指令会有返回码。 * 这就会有可能导致ps/2控制器在受到我们的命令后(在ps2_process_cmd中), * 产生IRQ#1中断(虽然说这种情况取决于底层BIOS实现,但还是会发生,比如QEMU和bochs)。 * 所以这就是说,当IRQ#1中断产生时,我们的CPU正处在另一个ISR中。这样就会导致所有的外部中断被缓存在APIC内部的 - * FIFO队列里,进行排队等待(APIC长度为二的队列 {IRR, TMR};参考 Intel Manual Vol.3A 10.8.4) + * FIFO队列里,进行排队等待(APIC长度为二的队列 {IRR, TMR};参考 Intel + * Manual Vol.3A 10.8.4) * 那么当ps2_process_cmd执行完后(内嵌在#APIC_TIMER_IV),CPU返回EOI给APIC,APIC紧接着将排在队里的IRQ#1发送给CPU * 造成误触发。也就是说,我们此时读入的scancode实则上是上一个指令的返回代码。 - * + * * Problem 1 (Fixed): * 但是这种方法有个问题,那就是,假若我们的某一个命令失败了一次,ps/2给出0xfe,我们重传,ps/2收到指令并给出0xfa。 * 那么这样一来,将会由两个连续的IRQ#1产生。而APIC是最多可以缓存两个IRQ,于是我们就会漏掉一个IRQ,依然会误触发。 * Solution: * 累加掩码 ;) - * + * * Problem 2: - * + 这种累加掩码的操作是基于只有一号IRQ产生的中断的假设,万一中间夹杂了别的中断?Race Condition! - * + 不很稳定x1,假如连续4次发送失败,那么就会导致累加的掩码上溢出,从而导致下述判断失败。 + * + + * 这种累加掩码的操作是基于只有一号IRQ产生的中断的假设,万一中间夹杂了别的中断?Race + * Condition! + * + + * 不很稳定x1,假如连续4次发送失败,那么就会导致累加的掩码上溢出,从而导致下述判断失败。 */ #ifdef KBD_ENABLE_SPIRQ_FIX if ((kbd_state.state & 0xc0)) { @@ -312,62 +342,67 @@ void intr_ps2_kbd_handler(const isr_param* param) { #ifdef KBD_DBGLOG kprintf(KDEBUG "%x\n", scancode & 0xff); #endif - - switch (kbd_state.state) - { - case KBD_STATE_KWAIT: - if (scancode == 0xf0) { // release code - kbd_state.state = KBD_STATE_KRELEASED; - } else if (scancode == 0xe0) { - kbd_state.state = KBD_STATE_KSPECIAL; - kbd_state.translation_table = scancode_set2_ex; - } else { - key = kbd_state.translation_table[scancode]; - kbd_buffer_key_event(key, scancode, KBD_KEY_FPRESSED); - } - break; - case KBD_STATE_KSPECIAL: - if (scancode == 0xf0) { //release code - kbd_state.state = KBD_STATE_KRELEASED; - } else { + + switch (kbd_state.state) { + case KBD_STATE_KWAIT: + if (scancode == 0xf0) { // release code + kbd_state.state = KBD_STATE_KRELEASED; + } else if (scancode == 0xe0) { + kbd_state.state = KBD_STATE_KSPECIAL; + kbd_state.translation_table = scancode_set2_ex; + } else { + key = kbd_state.translation_table[scancode]; + kbd_buffer_key_event(key, scancode, KBD_KEY_FPRESSED); + } + break; + case KBD_STATE_KSPECIAL: + if (scancode == 0xf0) { // release code + kbd_state.state = KBD_STATE_KRELEASED; + } else { + key = kbd_state.translation_table[scancode]; + kbd_buffer_key_event(key, scancode, KBD_KEY_FPRESSED); + + kbd_state.state = KBD_STATE_KWAIT; + kbd_state.translation_table = scancode_set2; + } + break; + case KBD_STATE_KRELEASED: key = kbd_state.translation_table[scancode]; - kbd_buffer_key_event(key, scancode, KBD_KEY_FPRESSED); + kbd_buffer_key_event(key, scancode, KBD_KEY_FRELEASED); + // reset the translation table to scancode_set2 kbd_state.state = KBD_STATE_KWAIT; kbd_state.translation_table = scancode_set2; - } - break; - case KBD_STATE_KRELEASED: - key = kbd_state.translation_table[scancode]; - kbd_buffer_key_event(key, scancode, KBD_KEY_FRELEASED); - - // reset the translation table to scancode_set2 - kbd_state.state = KBD_STATE_KWAIT; - kbd_state.translation_table = scancode_set2; - break; - - default: - break; + break; + + default: + break; } } -static uint8_t ps2_issue_cmd(char cmd, uint16_t arg) { +static uint8_t +ps2_issue_cmd(char cmd, uint16_t arg) +{ ps2_post_cmd(PS2_PORT_CTRL_CMDREG, cmd, arg); - + // 等待PS/2控制器返回。通过轮询(polling)状态寄存器的 bit 0 // 如置位,则表明返回代码此时就在 0x60 IO口上等待读取。 - while(!(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_OFULL)); + while (!(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_OFULL)) + ; return io_inb(PS2_PORT_ENC_CMDREG); } -static void ps2_post_cmd(uint8_t port, char cmd, uint16_t arg) { +static void +ps2_post_cmd(uint8_t port, char cmd, uint16_t arg) +{ // 等待PS/2输入缓冲区清空,这样我们才可以写入命令 - while(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_IFULL); + while (io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_IFULL) + ; io_outb(port, cmd); io_delay(PS2_DELAY); - + if (!(arg & PS2_NO_ARG)) { // 所有参数一律通过0x60传入。 io_outb(PS2_PORT_ENC_CMDREG, (uint8_t)(arg & 0x00ff)); @@ -375,17 +410,22 @@ static void ps2_post_cmd(uint8_t port, char cmd, uint16_t arg) { } } -static uint8_t ps2_issue_dev_cmd(char cmd, uint16_t arg) { +static uint8_t +ps2_issue_dev_cmd(char cmd, uint16_t arg) +{ ps2_post_cmd(PS2_PORT_ENC_CMDREG, cmd, arg); - + // 等待PS/2控制器返回。通过轮询(polling)状态寄存器的 bit 0 // 如置位,则表明返回代码此时就在 0x60 IO口上等待读取。 - while(!(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_OFULL)); + while (!(io_inb(PS2_PORT_CTRL_STATUS) & PS2_STATUS_OFULL)) + ; return io_inb(PS2_PORT_ENC_CMDREG); } -int kbd_recv_key(struct kdb_keyinfo_pkt* key_event) { +int +kbd_recv_key(struct kdb_keyinfo_pkt* key_event) +{ if (!key_buf.buffered_len) { return 0; } @@ -401,15 +441,18 @@ int kbd_recv_key(struct kdb_keyinfo_pkt* key_event) { return 1; } -static struct kdb_keyinfo_pkt* ps2_keybuffer_next_write() { - int index = (key_buf.read_ptr + key_buf.buffered_len) % PS2_KBD_RECV_BUFFER_SIZE; +static struct kdb_keyinfo_pkt* +ps2_keybuffer_next_write() +{ + int index = + (key_buf.read_ptr + key_buf.buffered_len) % PS2_KBD_RECV_BUFFER_SIZE; if (index == key_buf.read_ptr && key_buf.buffered_len) { // the reader is lagged so much such that the buffer is full. - // It is suggested to read from beginning for nearly up-to-date readings. + // It is suggested to read from beginning for nearly up-to-date + // readings. key_buf.read_ptr = 0; key_buf.buffered_len = index; - } - else { + } else { key_buf.buffered_len++; } return &key_buf.buffer[index]; diff --git a/lunaix-os/makefile b/lunaix-os/makefile index f1a6dce..9ac9c3e 100644 --- a/lunaix-os/makefile +++ b/lunaix-os/makefile @@ -51,20 +51,20 @@ clean: @sleep 2 run: $(BUILD_DIR)/$(OS_ISO) - @qemu-system-i386 -cdrom $(BUILD_DIR)/$(OS_ISO) -d cpu_reset -monitor telnet::$(QEMU_MON_PORT),server,nowait & + @qemu-system-i386 -cdrom $(BUILD_DIR)/$(OS_ISO) $(QEMU_OPTIONS) @sleep 1 @telnet 127.0.0.1 $(QEMU_MON_PORT) debug-qemu: all-debug @${TOOLCHAIN}/i686-elf-objcopy --only-keep-debug $(BIN_DIR)/$(OS_BIN) $(BUILD_DIR)/kernel.dbg - @qemu-system-i386 -m 1G -rtc base=utc -d cpu_reset -s -S -cdrom $(BUILD_DIR)/$(OS_ISO) -monitor telnet::$(QEMU_MON_PORT),server,nowait & + @qemu-system-i386 -cdrom $(BUILD_DIR)/$(OS_ISO) $(QEMU_OPTIONS) @sleep 1 @$(QEMU_MON_TERM) -- telnet 127.0.0.1 $(QEMU_MON_PORT) @gdb -s $(BUILD_DIR)/kernel.dbg -ex "target remote localhost:1234" debug-qemu-vscode: all-debug @${TOOLCHAIN}/i686-elf-objcopy --only-keep-debug $(BIN_DIR)/$(OS_BIN) $(BUILD_DIR)/kernel.dbg - @qemu-system-i386 -s -S -m 1G -cdrom $(BUILD_DIR)/$(OS_ISO) -monitor telnet::$(QEMU_MON_PORT),server,nowait & + @qemu-system-i386 -cdrom $(BUILD_DIR)/$(OS_ISO) $(QEMU_OPTIONS) @sleep 0.5 @telnet 127.0.0.1 $(QEMU_MON_PORT) -- 2.27.0