Задать вопрос
KernelBuilder
@KernelBuilder
Увлекаюсь программированием с 12 лет

Почему зависают прерывания клавиатуры (IRQ1) после первого нажатия при переходе на GOP в самописной ОС (x86_64)?

Пишу свою ОС (загрузка через UEFI, переход в Long Mode, вывод графики через GOP). Столкнулся со следующей проблемой при настройке прерываний (IDT).

Симптомы:
При старте экран чёрный (так и задумано на этом этапе), но клавиатура реагирует на клавишу ESC — код отрабатывает корректно. Однако, если нажать любую другую клавишу, экран переключается в графический режим, но после этого клавиатура полностью перестает реагировать на любые нажатия (включая ESC). Прерывания будто блокируются после первого-второго вызова.

Что я уже проверил:
1. Базовый обработчик IDT вызывается.
2. Стек при выходе из прерывания (iretq) восстанавливается (по крайней мере, явных Triple Fault нет).

Предположение:
Подозреваю, что я либо некорректно отправляю EOI (End of Interrupt) в PIC/APIC, из-за чего контроллер ждёт завершения и блокирует линию, либо где-то затираются регистры общего назначения при обработке прерывания.

Ссылка на репозиторий с кодом обработки IDT: https://github.com/larcenkokirill91-max/AnalOS.git

На что обратить внимание в коде ISR-заглушек и инициализации контроллера прерываний, чтобы ввод не блокировался?
  • Вопрос задан
  • 348 просмотров
Подписаться 1 Сложный 2 комментария
Помогут разобраться в теме Все курсы
  • Нетология
    Разработчик на C++
    12 месяцев
    Далее
  • Яндекс Практикум
    Разработчик C++
    9 месяцев
    Далее
  • Академия Эдюсон
    Разработчик игр на Unreal Engine + ИИ
    9 месяцев
    Далее
Пригласить эксперта
Ответы на вопрос 2
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
При старте экран чёрный (так и задумано на этом этапе), но клавиатура реагирует на клавишу ESC — код отрабатывает корректно.

Это потому, что код крутится вот в этом месте, опрашивая клавиатуру и ожидая нажатия клавиши без участия прерываний.

Прерывания будто блокируются после первого-второго вызова.

Это так, но они блокируются не из-за обработчика прерываний клавиатуры, а из-за dummy_handler_asm, который вызывается по таймеру, но не завершается отправкой кода окончания обработки прерывания в PIC.
Вот такое изменение фиксит эту проблему:
diff --git a/system/drivers/interrupts.asm b/system/drivers/interrupts.asm
index 75ea06c4bc2f..17ac87fa6b06 100644
--- a/system/drivers/interrupts.asm
+++ b/system/drivers/interrupts.asm
@@ -98,5 +98,7 @@ dummy_handler_asm:
     push rax
     mov rax, 0xFEE000B0
     mov dword [rax], 0
+    mov al, 0x20
+    out 0x20, al
     pop rax
     iretq


Кроме того
твой код выделяет память вызовами bs->AllocatePages(2, 4, ...), а 2 -- это AllocateAddress, т.е. запрос на выделение памяти по фиксированному тобой адресу. У меня этот код сразу возвращает ошибку, а вот bs->AllocatePages(0, 4, ...) -- работает:
diff --git a/boot/bootloader.c b/boot/bootloader.c
index 377e907c1728..5ae82dca7fc0 100644
--- a/boot/bootloader.c
+++ b/boot/bootloader.c
@@ -59,7 +59,7 @@ EFIAPI long long efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     // 1. Выделяем память под скрытый буфер экрана (1024 * 768 * 4 байта = 3145728 байт = 768 страниц по 4КБ)
     unsigned long long v_buffer_addr = 0;
     // 2 = EfiAllocateAnyPages, 4 = EfiRuntimeServicesData (чтобы ядро гарантированно видело эту память)
-    long long status = bs->AllocatePages(2, 4, 768, &v_buffer_addr);
+    long long status = bs->AllocatePages(0, 4, 768, &v_buffer_addr);
     if (status != 0) {
         while(1) { __asm__ __volatile__("hlt"); }
     }
@@ -85,7 +85,7 @@ EFIAPI long long efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
 
     unsigned long long map_buffer = 0;
     // Выделяем временную память под саму карту памяти (округлим map_size до страниц)
-    bs->AllocatePages(2, 4, (map_size / 4096) + 1, &map_buffer);
+    bs->AllocatePages(0, 4, (map_size / 4096) + 1, &map_buffer);
 
     // Получаем финальную карту памяти и MapKey
     status = bs->GetMemoryMap(&map_size, (void*)map_buffer, &map_key, &desc_size, &desc_ver);
Ответ написан
Комментировать
opium
@opium
Просто люблю качественно работать
в коде два реальных подозрительных места. dummy_handler_asm (висит на всех векторах IDT кроме 33 и 44) шлёт EOI в LAPIC (0xFEE000B0) и делает голый iretq без снятия error code, а для #GP/#PF/#DF это ломает кадр возврата. При этом keyboard_handler_c шлёт EOI только в старый PIC, LAPIC-EOI закомментирован — то есть в проекте намешаны PIC и APIC одновременно, а маски IRQ на PIC (0x21/0xA1) нигде явно не выставлены. Выбери одну схему и убери вторую целиком, и добавь для исключений с error code отдельные заглушки: снять код со стека, а при настоящем фолте cli;hlt с выводом vector/CR2, а не слепой iretq.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы