Я следую книге, чтобы написать Linux-подобное ядро, однако столкнулся с проблемами с главой APIC.
Прежде всего, я перечислю свою платформу. Я использую Windows 10, использую Virtual Box для запуска Ubuntu 18.04 и запускаю в нем тестовые коды на bochs.
В настоящее время мое понимание APIC следующее:
1. На каждом ядре встроен локальный APIC, а на материнской плате — I/O APIC.
2. Доступ к локальному APIC можно получить с помощью сопоставления памяти или ссылки на MSR.
3, I/O APIC доступны через 3 регистра IOREGSEL, IOWIN, EOI. Основная идея состоит в том, чтобы установить значение для IOREGSEL и получить доступ к соответствующему регистру с помощью IOWIN.
4, есть 3 режима, интересующий режим симметричного ввода-вывода
5, I/O APIC имеют 24 контакта, контакт 1 связан с клавиатурой
6. Чтобы включить APIC и I/O APIC, необходимо выполнить ряд работ:
а) Прерывание по маске 8529A
б) Включите xAPIC и 2xAPIC, чтобы был возможен доступ к MSR
c) Маскировать все LVT (если локальные прерывания не нужны)
d) Настройка записей RTE для I/O APIC
д) Установка регистра IMCR в 0x01h, принудительно передает сигнал прерывания 8529A на I/O APIC.
f) Найдите другой регистр управления прерываниями (OIC) через корневой комплексный базовый регистр адреса (RCBA) и установите OIC[8]=1b, чтобы включить I/O APIC.
Теперь я представлю свои вопросы:
1. Как на bochs, так и на Virtual Box максимальное число записей LVT определяется как 6 (согласно руководству, существует 6 + 1 = 7 записей LVT), а запись LVT_CMCI недоступна (ошибка gp).
2. Говорят, что разные чипы на материнской плате будут отображать RCBA на разные порты, и мне придется искать это в руководствах. Но был бы способ обнаружить это самим программным обеспечением, иначе как коммерческая ОС подходила для другой платформы.
3. Поскольку я на виртуальной машине, как я могу определить доступность RCBA?
Спасибо всем, кто может ответить на мои вопросы или помочь мне лучше понять эту главу.
Я представлю часть своего кода по настройке APIC для простого прерывания клавиатуры.
Сначала будет функция обработки прерываний
void IRQ0x21_interrupt(Int_Info_No_Err STK)
{
Ent_Int;
color_printk(RED,BLACK,"do_IRQ: 0x21\t");
unsigned char x;
x = io_in8(0x60);
color_printk(RED,BLACK,"key code:%#08x\n",x);
wrmsr(0x80b, 0UL);
//io_out8(0x20,0x20);
Ret_Int;
}
Ret_Int и Ent_Int — это макросы, определенные для обработки стека прерываний, функция wrmsr() записывает 0 в адрес MSR 0x80b(EOI)
Далее будет функция настройки для LAPIC и I/O APIC, предполагая, что физический адрес 0xFEC00000 уже отображен в таблице страниц.
void APIC_init(void)
{
int i;
int virtual_index_address;
int virtual_data_address;
int virtual_EOI_address;
unsigned long tmp;
//Set interrupt, note No.33 link to IRQ0x21_interrupt() function
for(i = 32;i < 56;i++)
{
_Set_INT(IDT_PTR.Offset + i, ATTR_INTR_GATE, 2, interrupt[i - 32]);
}
//Mask 8529A
io_out8(0x21,0xff);
io_out8(0xa1,0xff);
//enable IMCR
io_out8(0x22,0x70);
io_out8(0x23,0x01);
#pragma region Init_LAPIC
//Enabling xAPIC(IA32_APIC_BASE[10]) and 2xAPIC(IA32_APIC_BASE[11])
tmp = rdmsr(0x1b);
tmp |= ((1UL << 10) | (1UL << 11));
wrmsr(0x1b,tmp);
//Enabling LAPIC(SVR[8])
tmp = rdmsr(0x80f);
tmp |= (1UL << 8); //No support for EOI broadcast, no need to set bit SVR[12]
wrmsr(0x80f,tmp);
//Mask all LVT
tmp = 0x10000;
//wrmsr(0x82F, tmp); Virtual machine do not support
wrmsr(0x832, tmp);
wrmsr(0x833, tmp);
wrmsr(0x834, tmp);
wrmsr(0x835, tmp);
wrmsr(0x836, tmp);
wrmsr(0x837, tmp);
#pragma endregion
#pragma region Init_IOAPIC
virtual_index_address = (unsigned char*)(0xFEC00000 + PAGE_OFFSET);
virtual_data_address = (unsigned int*)(0xFEC00000 + PAGE_OFFSET + 0x10);
virtual_EOI_address = (unsigned int*)(0xFEC00000 + PAGE_OFFSET + 0x40);
//Setting RTEs, mask all but 0x01 RTE table for keyboard
for(i = 0x10;i < 0x40;i += 2){
*virtual_index_address = i;
io_mfence;
*IOAPIC_MAP.virtual_data_address = 0x10020 + ((i - 0x10) >> 1) & 0xffffffff;
io_mfence;
*IOAPIC_MAP.virtual_index_address = i + 1;
io_mfence;
*IOAPIC_MAP.virtual_data_address = ((0x10020 + ((i - 0x10) >> 1)) >> 32) & 0xffffffff;
io_mfence;
}
*virtual_index_address = 0x12;
io_mfence;
*IOAPIC_MAP.virtual_data_address = 0x10020 + (2 >> 1) & 0xffffffff;
io_mfence;
*IOAPIC_MAP.virtual_index_address = i + 1;
io_mfence;
*IOAPIC_MAP.virtual_data_address = ((0x10020 + (2 >> 1)) >> 32) & 0xffffffff;
io_mfence;
#pragma endregion
}
Итак, согласно ответам, I/O APIC настроен на открытие после завершения инициализации для RTE. Если кто-нибудь может быть так любезен, скажите мне, будет ли приведенный выше код работать или нет (для простого прерывания клавиатуры). Большое спасибо.