OSDev

для всех
Текущее время: 09 май 2024, 17:53

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу Пред.  1, 2, 3  След.
Автор Сообщение
СообщениеДобавлено: 30 июн 2010, 20:23 

Зарегистрирован: 21 сен 2007, 17:24
Сообщения: 1088
Откуда: Балаково
IOAPIC сам по-себе постоянно включен также как и PIC, его включать не надо. Но он работает через Local APIC который может быть выключен, вот его и надо включать. Весь код буду приводить на С++. В общем, включается он так:
Код:
//Enable Local APIC by Spurious Interrupt Vector Register
*(long*)0xFEE000F0 = *(long*)0xFEE000F0 | 0x100;
//Set LVT LINT0 Register
long LVT0 = *(long*)0xFEE00350;
LVT0 &= 0xFFFE58FF; //Delivery Mode = Fixed
*(long*)0xFEE00350 = LVT0;
//Set LVT LINT1 Register
long LVT1 = *(long*)0xFEE00360;
LVT1 &= 0xFFFE58FF;
LVT1 |= 0x400; //Delivery Mode = NMI
*(long*)0xFEE00360 = LVT1;
//Connect INTR and NMI lines to Local APIC
outpb(0x22, 0x70);
outpb(0x23, 1);

Local APIC у каждого ядра процессора отдельный, поэтому эту процедуру надо выполнять в каждом ядре (если ОС использует несколько ядер).

Local APIC иногда генерирует некое "неопределённое" прерывание, когда неправильно определяется источник прерывания. Вектор обработчика задаётся через Spurious Interrupt Vector Register FEE000F0 в младших 8 битах. Его тоже надо явно задавать, и ставить хотя бы пустой обработчик. Кроме вектора в этом регистре есть ещё различные атрибуты, поэтому перед записью в регистр сначала надо прочитать его текущее содержимое, и объединить с вектором.

Базового вектора у IOAPIC нет, там все вектора настраиваются по отдельности. Доступ к настройкам производится через 2 ячейки памяти: FEC00000 и FEC00010. В первую ячеку вводится индекс регистра, во вторую ячейку вводятся данные.

Параметры каждого вектора состоят из 2-х 32-битных значений, итого 64 бита. Индексы параметров IRQ0 равны 10h и 11h. Индексы IRQ1 равны 12h и 13h, и.т.д. У последнего IRQ23 индексы 3Eh и 3Fh.

Младшие 8 бит первого значения содержат собственно говоря вектор прерывания. Ещё некоторые атрибуты я уже описывал. Бит 16 - маскирование. Там ещё много разных атрибутов, но для начала можно оставить нули, работать будет и так. Описание всех атрибутов IOAPIC в файле http://www.intel.com/design/chipsets/da ... 290566.htm на страницах 12 и 13.

Примеры настройки векторов:
Код:
//Маскирование IRQ 0 (PIC line)
*(long*)0xFEC00000 = 0x10;//Index
*(long*)0xFEC00010 = 0x10000; //Data bits 0-31
*(long*)0xFEC00000 = 0x11;//Index
*(long*)0xFEC00010 = 0; //Data bits 32-63
//Настройка IRQ 1 (Keyboard) на Int 21h
*(long*)0xFEC00000 = 0x10 + 1 * 2;//Index 12h
*(long*)0xFEC00010 = 0x21; //Data
*(long*)0xFEC00000 = 0x11 + 1 * 2;//Index 13h
*(long*)0xFEC00010 = 0; //Data
//Настройка IRQ16 (PCI pin 1) на Int 30h
*(long*)0xFEC00000 = 0x10 + 16 * 2;//Index 30h
*(long*)0xFEC00010 = 0xA030; //Data
*(long*)0xFEC00000 = 0x11 + 16 * 2;//Index 31h
*(long*)0xFEC00010 = 0; //Data

Что касается PIC, то все его каналы должны быть замаскированы через порты 21h и A1h.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 02 июл 2010, 12:07 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Не работает.
Код:
        ; Включим и настроим Local APIC
        mov eax, [local_apic_registers + 0xF0]
        or eax, 0x100
        mov al, IRQ_BASE ; Вместо прерывания PIC, которое я замаскирую, будет "неопределённое"
        mov [local_apic_registers + 0xF0], eax
        mov eax, [local_apic_registers + 0x350]
        and eax, 0xFFFE58FF
        mov [local_apic_registers + 0x350], eax
        mov eax, [local_apic_registers + 0x360]
        and eax, 0xFFFE58FF
        or eax, 0x400
        mov [local_apic_registers + 0x360], eax
        mov al, 0x70
        out 0x22, al
        mov al, 1
        out 0x23, al
        ; Примонтируем регистры IO APIC к адресному пространству
        mov rax, 0xFEC00000 + 10011b
        mov rdx, io_apic_registers
        mov rbx, cr3
        mov rcx, 1
        call map_virtual_pages
        ; Замаскируем прерывание от PIC
        mov dword[io_apic_registers + 0x00], 0x10
        mov dword[io_apic_registers + 0x10], 0x10000
        mov dword[io_apic_registers + 0x00], 0x11
        mov dword[io_apic_registers + 0x10], 0
        ; Переадресуем остальные прерывания ISA
        mov rcx, 15
        mov edx, IRQ_BASE + 1
        mov ebx, 0x12
@@:
        mov [io_apic_registers + 0x00], ebx
        mov [io_apic_registers + 0x10], edx
        inc ebx
        mov [io_apic_registers + 0x00], ebx
        mov dword[io_apic_registers + 0x10], 0
        inc ebx
        inc edx
        loop @b
        ; Переадресуем прерывания PCI
        mov rcx, 8
        or edx, 0xA000
@@:
        mov [io_apic_registers + 0x00], ebx
        mov [io_apic_registers + 0x10], edx
        inc ebx
        mov [io_apic_registers + 0x00], ebx
        mov dword[io_apic_registers + 0x10], 0
        inc ebx
        inc edx
        loop @b                   

Пустой обработчик прерывания выглядит так:
Код:
empty_irq_handler:
        mov dword[local_apic_registers + 0xB0], 0
        iretq

А ещё у меня обработчик таймерного прерывания выполняет переключение задач. Я потом ставлю его на IRQ2. Ну так вот. Обработчик IRQ2 ни разу не вызывается. Однако два раза вызывается пустой обработчик (а потом вообще обработчики прерываний не вызываются. Например, я нажимаю на клавишу клавиатуры - должен вызваться пустой обработчик, но этого не происходит). Что я сделал не так? Проверял с помощью Bochs. Там можно в отладчике включить останов по xchg bx, bx. Я поставил в начале обработчиков эту команду. Останов как я уже сказал выше происходит два раза на пустом обработчике и больше никогда не происходит. При этом флаг IF установлен.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 02 июл 2010, 19:18 

Зарегистрирован: 21 сен 2007, 17:24
Сообщения: 1088
Откуда: Балаково
Пробуй не только в Bochs, но и в VMware или реальном железе, могут быть разные результаты, хотя в Bochs у меня работает нормально. Но я проверял в 32-битном режиме, а 64-битный может как-то не так эмулируется, всё может быть.

Можно проверить IO APIC в неком режиме совместимости, включив прохождение прерываний от PIC через IRQ0, а остальные линии IO APIC оставить замаскированными.
Код:
*(long*)0xFEC00000 = 0x10; //Index IRQ0
*(long*)0xFEC00010 = 0x700; //Delivery Mode = ExtINT
*(long*)0xFEC00000 = 0x11;
*(long*)0xFEC00010 = 0;

PIC должен быть настроен как обычно, и сигнал конца обработки посылать тоже в PIC.
Хотя PIC и подключен к IRQ0 IOAPIC, но прерывания будут идти все 16, как при обычной работе. Это просто каскадное соединение контроллеров, по тому же принципу, как PIC2 подключен к IRQ2 PIC1.

Ещё одну вещь можно проверить. В принципе, хотя совершенно невероятно, но Local APIC может быть неактивен из-за регистра MSR. Надо прочитать регистр MSR и посмотреть один бит.
Код:
mov ecx, 0x1B ;MSR IA32_APIC_BASE
rdmsr
and eax, 0x800

В eax должно получиться 0x800. Если результат 0, то надо пробовать установить этот бит с помощью wrmsr.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 03 июл 2010, 18:50 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Бит MSR устанавливал ещё с самого начала (прочитал в OSDev Wiki).
Это не баг Bochs - в VirtualBox тоже не работает.
Если включаю прерывание от PIC (и соответственно не маскирую его прерывания), то прерывание от таймера приходит (на базовый вектор PIC), а вот IRQ0 (от APIC) не происходит. Local APIC вроде как включился - Bochs пишет на консоль, что Local APIC активирован, когда стартует ядро (значит он был не активен до этого).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 03 июл 2010, 19:19 

Зарегистрирован: 21 сен 2007, 17:24
Сообщения: 1088
Откуда: Балаково
Всё-же стоит проверить наличие IOAPIC в системе. В настройках Bochs должен быть параметр
i440fxsupport: enabled=1

И программно проверить так. Записать 0 в регистр версии IOAPIC, затем прочитать его, результат должен быть не 0.
Код:
*(long*)0xFEC00000 = 1;//Index = IOAPICVER
*(long*)0xFEC00010 = 0;
long HaveIoAPIC = *(long*)0xFEC00010;


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 03 июл 2010, 20:22 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Опцию Bochs поставил (хотя и так по умолчанию APIC включен. по крайней мере в версии 2.4.1). Программно тоже проверил - APIC есть.
Bochs при включенном отладочном выводе пишет вот что:
Код:
00016466396i[CPU0 ] RDMSR: Read 00000000:fee00900 from MSR_APICBASE
00016466500d[APIC0] LAPIC read from register 0x00f0
00016466500d[APIC0] read from APIC address 0xfee000f0 = 000001ff
00016466503d[APIC0] LAPIC write 0x00000120 to register 0x00f0
00016466503d[APIC0] write of 00000120 to spurious interrupt register
00016466504d[APIC0] LAPIC read from register 0x0350
00016466504d[APIC0] read from APIC address 0xfee00350 = 00010000
00016466506d[APIC0] LAPIC write 0x00000000 to register 0x0350
00016466507d[APIC0] LAPIC read from register 0x0360
00016466507d[APIC0] read from APIC address 0xfee00360 = 00010000
00016466510d[APIC0] LAPIC write 0x00000400 to register 0x0360
00016466661d[APIC0] Deliver lowest priority of fixed interrupt vector 26
00016466661d[APIC0] trigger interrupt vector=0x26
00016466661d[APIC0] triggered vector 0x26
00016466661d[APIC0] service_local_apic(): setting INTR=1 for vector 0x26
00016466815d[APIC0] acknowledge_int() returning vector 0x26
00016466815d[APIC0] LAPIC write 0x00000000 to register 0x00b0
00016466815d[APIC0] Wrote 0x0 to EOI
00016466815d[APIC0] local apic received EOI, hopefully for vector 0x26

Как я уже сказал выше - происходит одно прерывание. Как мы теперь знаем IRQ6 (у меня IRQ0 - 0x20, IRQ1 - 0x21 и т. д.). После этого прерывания больше не происходят. Хотя должно происходить периодически таймерное прерывание. IRQ1 - клавиатура при нажатии на клавиши тоже не происходит.
UPD: Вру. Один раз происходит. Но потом перестаёт. Хотя это как раз понятно - я не читаю символ из порта клавиатуры. Сейчас добавлю чтение и посмотрю будет ли оно происходить ещё раз.
UPD2: Клавиатурное прерывание происходит столько раз сколько нужно. Проблема лишь с таймерным прерыванием. Вы сказали оно на IRQ2. На него ничего не приходит. Как впрочем и на другие IRQ (ну кроме IRQ1, но к нему вопросов нет).
UPD3: Ради эксперимента убрал маскировку IRQ0 и на него поставил обработчик таймера. И всё заработало! Но почему не IRQ2 (я не говорю, что вы сказали не правильно. возможно, это ошибка Bochs или ещё что-то)?

Кстати, у меня возник ещё один вопрос: как узнать базовый адрес IO APIC? Local APIC можно прочитать из MSR 0x1B. А IO APIC? Я конечно понимаю, что по умолчанию адрес определён, но мало ли что.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 03 июл 2010, 21:04 

Зарегистрирован: 21 сен 2007, 17:24
Сообщения: 1088
Откуда: Балаково
KIV, ты прав, Bochs не умеет генерировать IRQ2. Ошибка Bochs.
Про определение адреса IOAPIC сам не знаю. Думаю, с ним ситуация точно такая же, как и с PIC, тоесть стандартное расположение.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 03 июл 2010, 21:22 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Да. А в VirtualBox IRQ2 работает. Может стоит постать bug report команде разработчиков Bochs? Только я не смогу им объяснить нормально ошибку по-английски - лучше уж вы... А то не хочется отказываться от такого удобного в плане отладки своей ОС инструмента как Bochs.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 04 июл 2010, 10:26 

Зарегистрирован: 21 сен 2007, 17:24
Сообщения: 1088
Откуда: Балаково
KIV писал(а):
Может стоит постать bug report команде разработчиков Bochs?

Послал http://sourceforge.net/tracker/?func=de ... tid=112580

Могу посоветовать использовать CMOS таймер с частотой 1024Гц.
Включение:
Код:
//Init CMOS timer
outpb(0x70, 0xA); //State reg A
outpb(0x71, 0x26); //Set Interrupt rate 1024Hz

В обработчике прерывания IRQ8:
Код:
outpb(0x70, 0xC); //State reg C
inpb(0x71); //Разрешаем новое прерывание, прочитав из порта


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 22 июл 2010, 20:34 

Зарегистрирован: 16 фев 2010, 22:03
Сообщения: 101
Увы. Таймер CMOS не заработал. Перед установкой обработчика IRQ8:
Код:
        mov al, 0xA
        out 0x70, al
        mov al, 0x26
        out 0x71, al

В конце обработчика прерывания:
Код:
        mov al, 0xC
        out 0x70, al
        in al, 0x71

И не работает (таймерное прерывание ни разу не вызывается). Кстати, это вы предлагаете как временный вариант (сейчас мой временный вариант - закоментрованный конец строки "+ 2", который я раскоментирую при необходимости запуска на рельной машие или виртуальной, но бех этого бага) или такой таймер можно спокойно использовать вместо обычного?

P. S. У меня возник ещё маленький вопросик: можно ли поставить на почти все IRQ один и тот же обработчик, а потом в обработчике получить номер IRQ откуда-нибудь?


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу Пред.  1, 2, 3  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 85


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB