PIC e APIC
Dispositivos de hardware sinalizam a CPU de forma assíncrona através de linhas de interrupção, e algo precisa decidir quais dessas linhas de fato chegam à CPU, em qual prioridade, e como qual vetor de interrupção. No hardware compatível com PC mais antigo, esse papel pertencia ao 8259 Programmable Interrupt Controller (PIC); em qualquer máquina construída para multiprocessamento, ele pertence em vez disso ao Advanced Programmable Interrupt Controller (APIC), dividido em um Local APIC por CPU e um I/O APIC compartilhado.
O PIC 8259
Seção intitulada “O PIC 8259”Um único 8259 lida com oito linhas de interrupção, numeradas IRQ0 a IRQ7. Máquinas compatíveis com IBM PC conectam duas delas em uma cascata master/slave (a saída do slave alimenta uma das próprias linhas de entrada do master, IRQ2 por convenção), estendendo o total para quinze linhas utilizáveis, IRQ0 a IRQ15, com a própria IRQ2 não mais disponível para um dispositivo, já que é consumida pela cascata.
Ambos os chips são programados através de um par de portas de I/O cada um: 0x20/0x21 para o master, 0xA0/0xA1 para o slave. A inicialização envia uma sequência de Initialization Command Words (ICW1 a ICW4) estabelecendo, entre outras coisas, para qual vetor de interrupção a IRQ0 de cada chip é mapeada: crítico em x86, porque o mapeamento padrão de fábrica do PIC coloca IRQ0 a IRQ7 nos vetores 8 a 15, colidindo diretamente com os próprios vetores de exceção reservados da CPU, 0 a 31. Todo kernel compatível com PC remapeia o PIC durante a inicialização, comumente para os vetores 32 e 40 para master e slave respectivamente, especificamente para evitar que uma interrupção de hardware e uma exceção de CPU jamais reivindiquem o mesmo número de vetor.
Depois da inicialização, Operation Command Words (OCW1 a OCW3) controlam o comportamento do dia a dia: OCW1 define a máscara de interrupção, um bit por linha, permitindo que o software desabilite IRQs individuais sem tocar na flag global de interrupção da CPU; um comando End-Of-Interrupt, enviado à porta de comando depois de atender uma interrupção, diz ao PIC que ele pode entregar mais interrupções de prioridade igual ou menor. Omiti-lo deixa o PIC acreditando que uma interrupção ainda está sendo atendida, e ele não entregará outra. Uma interrupção espúria (IRQ7 no master ou IRQ15 no slave, chegando sem nenhuma linha de fato ativada) é um artefato conhecido do design do 8259 sob condições específicas de timing, não uma falha de hardware, e uma IRQ15 espúria em particular ainda exige um EOI enviado ao master (mas não ao slave, que nunca de fato levantou nada), um detalhe fácil de inverter.
Por que o APIC existe
Seção intitulada “Por que o APIC existe”O modelo do 8259 tem um controlador de interrupção entregando a uma CPU, o que é fundamentalmente incompatível com um sistema que tem mais de uma CPU: não há como rotear uma determinada interrupção para qual CPU deveria tratá-la, ou fazer com que CPUs diferentes tratem interrupções diferentes. A arquitetura do APIC divide o tratamento de interrupções em duas peças cooperantes para resolver isso. Um Local APIC existe uma vez por núcleo de CPU, integrado ao próprio processador, e é o que de fato entrega um vetor de interrupção àquele núcleo específico, além de lidar com as próprias fontes locais de interrupção daquele núcleo (o timer do APIC, e interrupções entre processadores descritas abaixo). Um I/O APIC (um ou mais por sistema, externo a qualquer CPU específica) recebe interrupções de dispositivos e, através de uma tabela de redirecionamento programável, decide para qual vetor e qual Local APIC (ou qual subconjunto de CPUs) cada linha de entrada deve ser entregue.
O Local APIC
Seção intitulada “O Local APIC”O Local APIC de cada CPU é controlado através de um conjunto de registradores mapeados em memória, cujo endereço-base físico é lido da model-specific register IA32_APIC_BASE, por convenção 0xFEE00000, a menos que o firmware o tenha realocado. Registradores-chave incluem o APIC ID (identificando a qual CPU este Local APIC pertence, usado como destino ao rotear uma interrupção), o registrador End-Of-Interrupt (escrito, não lido, para confirmar a interrupção atual; diferente do comando dedicado do PIC, qualquer valor escrito aqui é suficiente), o registrador Spurious Interrupt Vector (que também contém o bit de habilitação global do APIC), e os registradores de configuração e contagem do APIC Timer, fornecendo um timer de propósito geral local a cada CPU, independente de qualquer timer de sistema compartilhado.
O I/O APIC
Seção intitulada “O I/O APIC”Onde o 8259 tem fixo por hardware qual linha mapeia para qual vetor, a tabela de redirecionamento de um I/O APIC (uma entrada de 64 bits por linha de entrada, tipicamente 24 entradas) é totalmente programável: cada entrada especifica o vetor de interrupção alvo, modo de entrega, CPU ou CPUs de destino, modo de disparo (edge ou level), e polaridade, permitindo que o sistema operacional roteie uma determinada linha física de interrupção para qualquer vetor e qualquer CPU que escolher, e mude esse roteamento em tempo de execução. Um kernel descobre quantos I/O APICs existem, seus endereços-base, e como números de IRQ ISA legados correspondem a seus pinos de entrada analisando a tabela ACPI MADT (Multiple APIC Description Table), em vez de por qualquer convenção fixa, já que esse mapeamento não tem garantia de ser idêntico entre chipsets diferentes.
Desabilitando o PIC legado
Seção intitulada “Desabilitando o PIC legado”Um kernel que já trocou para o APIC ainda precisa lidar com o par 8259, que permanece presente e, se deixado em seu estado padrão, ainda pode gerar interrupções espúrias nas mesmas linhas que a configuração do APIC agora espera que estejam quietas. A abordagem direta mascara toda linha em ambos os PICs escrevendo 0xFF em ambos os registradores de máscara, deixando os chips fisicamente presentes mas permanentemente silenciados, em vez de tentar desabilitá-los por completo, algo para o qual o 8259 não fornece mecanismo limpo algum.
Interrupções entre processadores
Seção intitulada “Interrupções entre processadores”Além de rotear interrupções de dispositivos, o Local APIC fornece uma capacidade para a qual o 8259 não tem equivalente algum: uma CPU pode enviar uma interrupção diretamente para outra CPU, ou para um grupo de CPUs, escrevendo em seu próprio Interrupt Command Register. É assim que um kernel implementa um TLB shootdown (interrompendo outras CPUs especificamente para que cada uma descarte suas próprias entradas desatualizadas de cache de tradução), e é assim que uma CPU bootstrap coloca núcleos de CPU adicionais online, através de uma sequência específica de IPIs conhecida como INIT-SIPI-SIPI: um IPI INIT reseta o núcleo alvo, seguido de um ou dois Startup IPIs (SIPI) carregando o endereço físico do código que o núcleo recém-iniciado deve começar a executar, já que um processador de aplicação, de outra forma, inicia em um estado sem como ser informado sozinho por onde começar.
Notas de implementação
Seção intitulada “Notas de implementação”Habilitar o APIC sem também configurar corretamente, ou mascarar explicitamente, o PIC legado é uma fonte comum de interrupções chegando duas vezes ou nenhuma durante o bring-up: ambos os controladores podem estar simultaneamente ativos e ambos tentando entregar a mesma linha física, a menos que o PIC legado seja mascarado primeiro. Ler a MADT é efetivamente obrigatório, não opcional, para correção: presumir o mapeamento convencional de IRQ0 a IRQ15 para vetor sem verificar as entradas de interrupt source override da MADT vai funcionar em muitas máquinas por coincidência e falhar silenciosamente, de formas difíceis de distinguir de outros bugs, em outras onde o firmware remapeou uma IRQ legada para uma entrada diferente de I/O APIC.
Referências
Seção intitulada “Referências”- ^ Intel, 8259A Programmable Interrupt Controller Datasheet: define o modelo de programação ICW/OCW referenciado acima.
- ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A, Capítulo 10: define o conjunto de registradores do Local APIC e o modelo de entrega de IPI.
- ^ Especificação ACPI, MADT (Multiple APIC Description Table): a fonte fornecida pelo firmware para informação de I/O APIC e roteamento de interrupção.
Ver também
Seção intitulada “Ver também”- The IDT: a tabela através da qual um vetor de interrupção é despachado uma vez entregue.
- System Calls: um caminho de interrupção disparado por software, não relacionado ao roteamento de hardware descrito aqui.
- Bring-Up de Multiprocessador: a sequência INIT-SIPI-SIPI enviada como uma IPI através do Local APIC coberto aqui.