Фрагмент 5-го уроку захищеного режиму, вартий уваги сам по собі. Інформація місцями не до кінця перевірена, багато що опущено, так як не має відношення до типової архітектури x86, але може комусь згодиться :-)
IRQ та PIC
Про зовнішні події процесор отримує інформацію за допомогою переривань. Переривання асинхронні по своїй природі. До прикладу це може бути переривання від диску, що закінчив операцію або від таймера про закінчення чергового інтервалу часу. "Керує" зовнішніми перериваннями та передає їх процесору спеціальний пристрій. В архітектурі IBM-PC сумісних комп'ютерів це був програмований контролер переривань (Programmable Interrupt Controllers, PIC) Intel 8259. Хоча з виникнення сімейства в 1981 минуло вже три десятиліття та навіть на x86-сумісних платформах існують багато досконаліших технологій роботи з перериваннями, таких як APIC (Advanced PIC, див. наприклад http://wiki.osdev.org/APIC), однак для забезпечення зворотної сумісності програмні способи роботи з контролером переривань епохи Intel 8259 тим чи іншим чином підтримуються. А потреба перепрограмовувати його при роботі з захищеним режимом є. Навіщо - стане зрозумілим далі.
Так як переривання можуть надходити від різних джерел, однією з функції PIC є їх мультиплексування та послідовне передання процесору згідно пріоритетів. Запити на переривання - Interrupt requests (IRQs) приходить на один з входів PIC, він перевіряє чи переривання не замасковане, і чи немає ще необроблених переривань з вищим приорітетом. Якщо відповідно не замасковано і немає більш приорітетних - переривання передається процесору1. Перші моделі (IBM PC та XT) мали один PIC, що може обслуговувати максимум 8 ліній IRQ. Це досить мало, тому починаючи з AT використовується два PIC, з'єднаних каскадно. Головний, Master PIC, під'єднано безпосередньо до процесора та обслуговує IRQ з номерами 0,1, 3-7, а вторинний, Slave PIC - до лінії IRQ2 головного2 та обслуговує IRQ 8-15. Тобто, якщо виникають IRQ з номером 8 та більше то воно викличе IRQ2 головного PIC, який і передасть його процесору. Тут теж є ряд тонкощів, у які ми не заглиблюватимемося3, але разом вони можуть обслуговувати до 15 ліній переривань.
Вся ця інформація, на жаль, потрібна тому що розробники IBM PC не прислухалися до розробників процесора Intel 8086/88 та під IRQ0-IRQ7 використали номери переривань процесора, зарезервовані для внутрішнього використання: int 08h - int 0Fh. Тому в захищеному режимі переривання таймера (IRQ0) викличе обробник int 08h (Double Fault!), що явно нас не влаштовує. Щоб виправити ситуацію доведеться перепрограмовувати PIC4 (а при поверненні в реальний режим повертати все назад).
Відповідність між IRQ та перериваннями по замовчуванню така:
Лінія | Переривання | Функція |
Master PIC | ||
IRQ0 | int 08h | Системний таймер |
IRQ1 | int 09h | Контролер клавіатури |
IRQ2 | int 0Ah | Служить для зв'язку з Slave PIC. Апаратні перенаправляються на IRQ9 |
IRQ3 | int 0Bh | COM2 (разом з COM4, якщо він присутній) |
IRQ4 | int 0Ch | COM1 (разом з COM3, якщо він присутній) |
IRQ5 | int 0Dh | LPT2/звукова карта/доступне іншій периферії |
IRQ6 | int 0Eh | контролер гнучких дисків |
IRQ7 | int 0Fh | LPT1/звукова карта/доступне іншій периферії |
Slave PIC | ||
IRQ8 | int 70h | CMOS RTC таймер |
IRQ9 | int 71h | Доступне/SCSI/викликається при "появі" IRQ2 |
IRQ10 | int 72h | Доступне/SCSI/мережева карта |
IRQ11 | int 73h | Доступне/SCSI/мережева карта |
IRQ12 | int 74h | Мишка |
IRQ13 | int 75h | Математичний сопроцесор (FPU) або міжпроцесорне переривання (inter-processor interrupt, IPI) |
IRQ14 | int 76h | Головний канал IDE (ATA) |
IRQ15 | int 77h | Вторинний канал IDE (ATA) |
З таблиці видно, що PIC використовує послідовні номери переривань для обробки IRQ, які він обслуговує. Номер, з якого починати, ми можемо запрограмувати. Цей номер має бути кратним 8, так як молодших три біти використовуються для вибору номеру переривання конкретного IRQ.
Робота з PIC здійснюється за допомогою командних слів (Operation Command Word - OCW та Initialization Control Word - ICW), які виводяться у відповідні порти. Кожен з PIC має два порти - порт команди та порт даних. Запис в командний порт передає команду, читання - повертає статус PIC. Читання порту даних повертає маску переривань (регістр IMR, див. нижче), запис в нього її змінює.
Порт | |
Master PIC - команди | 0x20 |
Master PIC - дані | 0x21 |
Slave PIC - команди | 0xA0 |
Slave PIC - дані | 0xA1 |
PIC має три регістри:
- Interrupt Mask Register (IMR) - регістр маскування запитів на переривання, показує які рівні IRQ зараз заблоковані.
- Interrupt Request Register (IRR) - регістр запитів на переривання, описує які переривання зараз очікують підтвердження.
- In-Service Register (ISR) - регістр перериваннь, що обробляються, описує які переривання зараз обробляються і вимагатимуть команди про завершення переривання (EOI — End Of Interrupt)5.
Для перепрограмування відображення переривань слід передати ICW1 для ініціалізації та потім послідовність з трьох командних слів6:
- ICW2 - зсув вектора (переривань),
- ICW3 - зв'язок між головним та вторинним,
- ICW4 - додаткова інформація про середовище.
Формат їх детально тут не описуватиметься, за додатковою інформацією можна звертатися до офіційного мануала 8259 http://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf, "Operating Systems Development - PIC, PIT, and exceptions by Mike,
2008", http://www.brokenthorn.com/Resources/OSDevPic.html та "PIC tutorial, 2007" того ж автора: http://www.brokenthorn.com/Resources/OSDevPic.html.
Важливі для нас біти ICW1:
- біт 0 - чи потрібне ICW4
- біт 4 - запит на ініціалізацію
- решта - в більшості випадків для архітектури x86 нульові.
Важливі для нас біти ICW2:
- біти 0-2 - 0
- біти 3-7 - початковий номер переривання для цього PIC
ICW3 - вказує яке IRQ використовується для зв'язку головного з вторинним PIC. Для головного єдиний біт, що дорівнює 1, вказує яке IRQ використано для взаємодії з вторинним. Тобто, типовому для IBM-PC IRQ2 відповідає 0x100b = 0x4h = 1<<2. Для вторинного біти 0-2 містять номер IRQ гловного, що використовується, решта мають бути нуль. Тобто IRQ2 відповідатиме 0x2h7.
Важливі для нас біти ICW4:
- біт 0 - 1 якщо працюємо з x86. Для нас - завжди.
- біт 1 - 1 відповідає Auto EOI, зазвичай має бути нульовим.
- решта - в більшості випадків для архітектури x86 нульові.
Разом роботу цієї машинерії продемонструємо на прикладі задачі, яку нам ставить дефект архітекури IBM PC, перепрограмування векторів переривань:
#define PIC1 0x20 /* базова адреса порта головного PIC */ #define PIC2 0xA0 /* базова адреса порта вторинного PIC */ #define PIC1_COMMAND PIC1 #define PIC1_DATA (PIC1+1) #define PIC2_COMMAND PIC2 #define PIC2_DATA (PIC2+1) #define ICW1_ICW4 0x01 /* Біт 0, чи потрібен ICW4 */ #define ICW1_INIT 0x10 /* Біт запиту на ініціалізацію */ #define ICW4_8086 0x01 /* Режим 8086/88 (проти MCS-80/85)*/ /*Пересилаємо ICW1 */ outportb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); /*Запит на ініціалізацію/ outportb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4); /*Пересилаємо ICW2 --- Початковий вектор переривання */ outportb(PIC1_DATA, new_PIC1_interrupt_offset); outportb(PIC2_DATA, new_PIC2_interrupt_offset); /*Пересилаємо ICW3 --- взаємодія головного і вторинного PIC*/ outportb(PIC1_DATA, 0x04);/*0x100b - IRQ2, як її хоче головний*/ outportb(PIC2_DATA, 0x02);/*просто 2 - IRQ2, як її хоче вторинний*/ /*Пересилаємо ICW4 --- вмикаємо режим x86*/ outportb(PIC1_DATA, ICW4_8086); outportb(PIC1_DATA, ICW4_8086);
Після кожної команди PIC можна вставити невелику затримку на час реакції. На сучасних комп'ютерах вона часто не потрібна, однак її відсутність може служити джерелом тонких збоїв у роботі.
Щоб передати сигнал неспецифічну команду EOI використовується OCW2. Його формат:
- біти 0-2 - рівень активного IRQ.
- біти 3-4 - 0
- біт 5 - EOI
- біти 6 і 7 - команди Selection i Rotation, ми їх не розглядатимемо.
Здійснюється її передача очевидним чином:
#define PIC_EOI 0x20 /*Тільки 5-й біт 20h дорівнює 1*/ outportb(PIC1_COMMAND, PIC_EOI);
Якщо переривання прийшло від вторинного контролера, то головний теж слід повідомити про завершення обробки переривання у підлеглого:
#define PIC_EOI 0x20 /*Тільки 5-й біт 20h дорівнює 1*/ void PIC_sendEOI(unsigned char irq) { if(irq >= 8) outportb(PIC2_COMMAND,PIC_EOI); outportb(PIC1_COMMAND,PIC_EOI); }
Для маскування переривань використовується OCW1. Це - байт, кожен біт якого відповідає IRQ з відповідним номером. Щоб замаскувати IRQ номер maskIRQline8:
uint16_t port; uint8_t value; if(IRQline < 8) { port = PIC1_DATA; } else { port = PIC2_DATA; IRQline -= 8; } value = inb(port) & ~(1 << IRQline); outportb(port, value);
Всі переривання можна замаскувати так:
outportb(PIC1_DATA, 0FFh); outportb(PIC2_DATA, 0FFh);
Footnotes:
1Тут є ряд тонкощів, у які не хотілося б заглиблюватися. Наприклад, коли є переривання, що очікує обробки, PIC повідомляє про це процесор. При цьому йому передається фальшивий номер IRQ з найменшим приорітетом. Процесор підтверджує та очікує справжній номер переривання, який і передається на наступному етапі. Ця здавалося б дрібна тонкість приводить до того що за певних умов між першим та другим етапом переривання може зникнути. Наприклад через передчасний сигнал EOI (End of interrupt), описаний нижче. В результаті виникають так-звані фальшиві переривання. Боротися з ними можна, перевіряючи відповідний біт регістра ISR (In Service Register) PIC. Детальніше - див. http://wiki.osdev.org/PIC/Spurious_IRQs
2Тому трішки раніше номер 2 був пропущений.
3Наприклад, IRQ2, яке прийшло по зовнішній лінії (а не від Slave PIC) відображається на IRQ9.
Детальніше про цю плутану архітектуру див. http://www.osdever.net/tutorials/view/irqs
4В деяких підручниках рекомендують замість перепрограмування PIC в обробнику переривання аналізувати яке його походження - від PIC чи від процесора. Таке рішення є неелегантним, приводить до переускладнених обробників і його хоча б теоретична працезадтінсть неочевидна. Не робіть так!
5Буває специфічна, неспецифічна та автоматична. Специфічна вказує на переривання, обробка якого завершена, фактично - який біт обнулити в ISR, неспецифічна EOI завершує переривання з найвищим приорітетом з активних. Автоматичний скидує біти в ISR зразу після підтвердження
переривання. Драйвери у Windows та DOS типово використовують неспецифічну EOI.
6Передавати їх слід обов'язково, ICW1 без решти приведе до невизначеної поведіки.
7Просто, правда?...
Translated from TEX by TTHgold, version 4.00.
Немає коментарів:
Дописати коментар