Interrupções externas no sistema x86. Parte 1. Interromper a evolução do controlador

Este artigo é sobre o processo de entrega de interrupção de dispositivos externos no sistema x86. Ele tenta responder perguntas como:

  • O que é foto e para que serve?
  • O que é o APIC e para que serve? Qual é o objetivo do LAPIC e do I / O APIC?
  • Quais são as diferenças entre APIC, xAPIC e x2APIC?
  • O que é MSI? Quais são as diferenças entre MSI e MSI-X?
  • Qual é o papel das tabelas $ PIR, MPtable e ACPI?

Se você deseja saber a resposta para uma dessas perguntas ou simplesmente deseja saber sobre a evolução do controlador de interrupção, por favor, seja bem-vindo.

1. Introdução


Para quem não sabe o que é uma interrupção, aqui está uma citação da Wikipedia:
Na programação do sistema, uma interrupção é um sinal para o processador emitido por hardware ou software, indicando um evento que precisa de atenção imediata. Uma interrupção alerta o processador para uma condição de alta prioridade que requer a interrupção do código atual que o processador está executando. O processador responde suspendendo suas atividades atuais, salvando seu estado e executando uma função chamada manipulador de interrupção (ou rotina de serviço de interrupção, ISR) para lidar com o evento. Essa interrupção é temporária e, após a conclusão do manipulador de interrupções, o processador retoma as atividades normais.

Existem dois tipos de interrupções: interrupções de hardware e software (softirqs):

  • As interrupções de hardware são usadas pelos dispositivos para comunicar que requerem atenção do sistema operacional. Internamente, as interrupções de hardware são implementadas usando sinais de alerta eletrônico enviados ao processador a partir de um dispositivo externo, que faz parte do próprio computador, como um controlador de disco ou um periférico externo. Por exemplo, pressionar uma tecla no teclado ou mover o mouse aciona interrupções de hardware que fazem com que o processador leia a pressionamento de tecla ou a posição do mouse. O ato de iniciar uma interrupção de hardware é chamado de solicitação de interrupção (IRQ).
  • Uma interrupção de software é causada por uma condição excepcional no próprio processador ou por uma instrução especial no conjunto de instruções que causa uma interrupção quando é executada. O primeiro é chamado de armadilha ou exceção e é usado para erros ou eventos que ocorrem durante a execução do programa, que são excepcionais o suficiente para que não possam ser manipulados no próprio programa. Por exemplo, uma exceção de divisão por zero será lançada se a unidade lógica aritmética do processador for ordenada a dividir um número por zero, pois esta instrução é um erro e impossível.

Este artigo é sobre hardware / interrupções externas IRQ.

Qual é o propósito das interrupções? Por exemplo, queremos executar uma ação com um pacote recebido da placa de rede assim que o pacote chegar. Se você não quiser perguntar continuamente à placa de rede "Meu pacote chegou?" e desperdice o tempo do processador, você pode usar o IRQ de interrupção de hardware externo. A linha de interrupção de um dispositivo deve ser conectada à linha INTR da CPU e, após o recebimento de cada pacote, a placa de rede fará um sinal nessa linha. A CPU detectará esse sinal e saberá que a placa de rede possui informações. Somente depois disso a CPU lerá o pacote recebido.

Mas o que devemos fazer se houver muitos dispositivos externos? Seria muito improdutivo criar uma tonelada de pinos INTR na CPU para todos eles.



Para resolver esse problema, um chip especial foi inventado - um controlador de interrupção.

Pic


( wiki / osdev )

O primeiro chip controlador de interrupção foi o Intel 8259 PIC . Possui 8 linhas de entrada (IRQ0-7) e 1 linha de saída (que conecta o controlador de interrupção à linha INTR da CPU). Quando houver uma interrupção de um dos dispositivos em suas linhas de entrada, o 8259 fará um sinal pela linha INTR. Depois disso, a CPU saberá que algum dispositivo requer atenção imediata e o processador perguntará ao PIC qual das 8 linhas de entrada (IRQx) foi a fonte dessa interrupção. Há alguma sobrecarga nessa pesquisa, mas agora temos 8 linhas de interrupção em vez de 1.



Logo 8 linhas não eram suficientes. Para aumentar o número total de linhas de interrupção, dois controladores 8259 (mestre e escravo) foram conectados em cascata (Dual PIC).

Os IRQs de 0 a 7 são processados ​​com o primeiro Intel 8259 PIC (mestre) e os IRQs de 8 a 15 são processados ​​com o segundo Intel 8259 PIC (mestre). Somente o mestre está conectado à CPU e pode sinalizar sobre as interrupções recebidas. Se houver uma interrupção nas linhas 8-15, o segundo PIC (escravo) sinalizará sobre isso para o mestre na linha IRQ2 e, depois disso, o mestre sinalizará a CPU. Essa interrupção em cascata remove uma das 16 linhas, mas faz um total de 15 interrupções para todos os dispositivos externos.



Esse esquema foi adotado pela comunidade e, agora, quando alguém fala sobre o PIC (Programm Interrupt Controller), ele se refere ao sistema Dual PIC. Depois de algum tempo, os controladores 8259 foram aprimorados e receberam um novo nome: 8259A. Com esses controladores, o sistema DUAL PIC foi incluído no chipset. No momento em que o barramento principal para conexão de dispositivo externo era o ISA, esse sistema era suficiente. Só era necessário que diferentes dispositivos não se conectassem à mesma linha de IRQ, pois as interrupções do ISA não são compartilháveis.

O mapeamento de interrupção do dispositivo era praticamente padrão:

Exemplo ( daqui ):
IRQ 0 - temporizador do sistema
IRQ 1 - controlador de teclado
IRQ 2 - cascata (interrupção do controlador escravo)
IRQ 3 - porta serial COM2
IRQ 4 - porta serial COM1
IRQ 5 - porta paralela 2 e 3 ou placa de som
IRQ 6 - controlador de disquete
IRQ 7 - porta paralela 1
IRQ 8 - Temporizador RTC
IRQ 9 - ACPI
IRQ 10 - aberto / SCSI / NIC
IRQ 11 - aberto / SCSI / NIC
IRQ 12 - controlador de mouse
IRQ 13 - co-processador matemático
IRQ 14 - canal ATA 1
IRQ 15 - canal ATA 2

A configuração e o trabalho com os chips 8259 são realizados com portas de E / S:
ChipRegistre-sePorta de E / S
Foto principalComando0x0020
Foto principalDados0x0021
EscravoComando0x00A0
EscravoDados0x00A1

A documentação completa do 8259A pode ser encontrada aqui .

O barramento PCI posteriormente substituiu o barramento ISA. Infelizmente, o número de dispositivos começou a exceder o número 15. Além disso, em vez do barramento ISA estático, os dispositivos no barramento PCI podem ser adicionados ao sistema dinamicamente, o que pode levar a problemas ainda maiores. Felizmente, porém, as interrupções no barramento PCI podem ser compartilhadas, portanto, é possível conectar muitos dispositivos a um IRQ de uma linha de interrupção. No final, para resolver o problema da falta de linhas de interrupção, foi decidido agrupar interrupções de todos os dispositivos PCI em linhas PIRQ (solicitação de interrupção programável).

Por exemplo, suponha que tenhamos 4 linhas de interrupção livres no controlador PIC e 20 dispositivos PCI. Podemos combinar interrupções de 5 dispositivos em uma linha PIRQx e conectar essas linhas PIRQx ao controlador PIC. Nesse caso, se houver uma interrupção em uma das linhas PIRQx, o processador precisará solicitar a todos os dispositivos conectados a essa linha sobre a interrupção para saber quem é responsável por ela, mas, no final, resolve o problema. O dispositivo que conecta as linhas de interrupção PCI às linhas PIRQ é geralmente chamado de roteador PIR.

Com esse método, é necessário garantir que as linhas PIRQx não se conectem às linhas com interrupções ISA (pois isso produzirá conflitos) e que as linhas PIRQx sejam equilibradas (quanto mais dispositivos conectamos a uma linha, mais dispositivos a CPU precisará para pesquisar quando precisa verificar qual dispositivo é responsável pela interrupção).



Nota : na imagem, o mapeamento do dispositivo PCI -> PIR é mostrado abstratamente, pois, no caso real, é um pouco mais complicado. No mundo real, cada dispositivo PCI possui 4 linhas de interrupção (INTA, INTB, INTC, INTD) e até 8 funções, onde cada função pode ter apenas uma dessas interrupções INTx. Qual linha INTx será usada por cada função é determinada pela configuração do chipset.

Por sua natureza, as funções são blocos lógicos separados. Por exemplo, um dispositivo PCI pode ter uma função de controlador Smbus, uma função de controlador SATA e uma função de ponte LPC. Do ponto de vista de um sistema operacional (SO), cada função é como um dispositivo separado com seu próprio espaço de configuração (configuração PCI).

As informações sobre o roteamento de interrupção do controlador PIC são enviadas ao sistema operacional pelo BIOS, com a ajuda da tabela $ PIR e pelos registros 3Ch (INT_LN Linha de interrupção (R ​​/ W)) e 3Dh (INT_PN Interrupt Pin (RO)) de o espaço de configuração do PCI para cada função.

Uma especificação para a tabela $ PIR estava recentemente no site da Microsoft , mas atualmente não está disponível. É possível entender o conteúdo da tabela na Especificação do BIOS PCI [4.2.2. Obtenha as opções de roteamento de interrupção PCI] ou a partir daqui (o último link está em russo, mas você pode tentar pesquisar no Google "Especificação da tabela de roteamento PCI IRQ")

Apic


( wiki , osdev )

O último método funcionou até a chegada dos sistemas multiprocessadores. Por natureza, o PIC só pode enviar interrupções para uma CPU e, em um sistema multiprocessador, é desejado carregar as CPUs de maneira equilibrada. A solução para esse problema foi a nova interface APIC (Advanced PIC).

Um controlador especial chamado LAPIC (APIC local) foi adicionado para cada processador, bem como o controlador APIC de E / S para rotear interrupções de dispositivos externos. Todos esses controladores são combinados em um barramento comum com o nome APIC (observe que os sistemas modernos usam um barramento de sistema padrão em vez de um barramento APIC separado para esta tarefa).

Quando uma interrupção externa chega na entrada I / O APIC, o controlador envia uma mensagem de interrupção ao LAPIC de uma das CPUs do sistema. Dessa maneira, o controlador de E / S APIC ajuda a equilibrar a carga de interrupção entre os processadores.

O primeiro chip APIC foi o 82489DX , que era um chip separado que possuía um LAPIC e um E / S APIC conectados. Para um sistema com processador duplo, três desses chips eram necessários: dois para LAPIC e um para E / S APIC. Posteriormente, a funcionalidade LAPIC foi incluída diretamente nos processadores e a parte I / O APIC foi separada no chip 82093AA.

O E / S APIC 82093AA tinha 24 entradas e a arquitetura APIC podia suportar até 16 CPUs. As interrupções de 0 a 15 foram deixadas para interrupções antigas do ISA para compatibilidade com sistemas mais antigos, e as interrupções de 16 a 23 foram feitas para todos os dispositivos PCI. Com essa delimitação, todos os conflitos entre as interrupções ISA e PCI podem ser facilmente evitados. Com o aumento do número de linhas de interrupção livres, também foi possível aumentar o número de linhas PIRQx.



A programação de E / S APIC e LAPIC é feita com a ajuda do MMIO. Os registros LAPIC geralmente são colocados no endereço 0xFEE00000, e os E / S APIC registram no endereço 0xFE0000, embora seja possível reconfigurá-los.

Como no caso do PIC, chips separados no início se tornaram parte do chipset posteriormente.

A arquitetura APIC foi posteriormente modernizada e sua nova variante foi denominada xAPIC (x - extended). Com compatibilidade com versões anteriores, o número total de CPUs possíveis no sistema foi aumentado para 256.

A próxima etapa no desenvolvimento da arquitetura foi denominada x2APIC . O número de CPUs possíveis no sistema foi aumentado para 2 ^ 32. Esses controladores podem funcionar em um modo de compatibilidade com o xAPIC, ou podem funcionar no novo modo x2APIC. Nesse novo modo, a programação do controlador não é feita através do MMIO, mas através dos registros MSR (que são muito mais rápidos). De acordo com este link , o suporte ao IOMMU é necessário para este modo.

Vale a pena notar que é possível ter vários controladores de E / S APIC no sistema. Por exemplo, um por 24 interrupções em uma ponte sul e outro por 32 interrupções em uma ponte norte. No contexto da E / S APIC, as interrupções são geralmente chamadas GSI (Global System Interrupt). Portanto, o sistema mencionado possui GSIs 0-55.

Como podemos determinar se uma CPU possui um LAPIC interno e qual arquitetura APIC suporta? É possível responder a essas perguntas inspecionando sinalizadores de bits do CPUID.
Para ajudar o SO a descobrir o LAPIC e o I / O APIC, o BIOS deve apresentar informações sobre eles através de um MPtable (método antigo) ou através de uma tabela ACPI (neste caso, uma tabela MADT). Além de informações comuns, o MPtable e o ACPI (neste caso, uma tabela DSDT) devem conter informações sobre o roteamento de interrupção. Isso significa informações sobre qual dispositivo usa qual linha de interrupção (semelhante à tabela $ PIR).

Você pode ler sobre o MPtable na especificação oficial. Anteriormente, a especificação estava no site da Intel, mas atualmente só é possível encontrá-la em uma versão de arquivo. A especificação da ACPI pode ser encontrada no site da UEFI (a versão atual é 6.2 ). Vale a pena notar que, com a ACPI, é possível declarar o roteamento de interrupção para sistemas sem APIC (em vez de fornecer uma tabela $ PIR separada).

Msi


( wiki )

A última variante do APIC foi boa, mas não sem desvantagens. Todas as linhas de interrupção dos dispositivos tornaram o sistema muito complicado e, portanto, aumentaram a probabilidade de erro. O barramento PCI Express veio substituir o barramento PCI, o que simplificou completamente todos os sistemas de interrupção. Não possui linhas de interrupção. Para compatibilidade com versões anteriores, os sinais de interrupção (INTx #) são emulados com um tipo separado de mensagens. Com as linhas de interrupção PCI, a conexão era feita com fios físicos. Com as linhas de interrupção PCI Express, a conexão é lógica e é feita por pontes PCI Express. Mas esse suporte a interrupções herdadas do INTx existe apenas para compatibilidade retroativa com o barramento PCI. O PCI Express apresenta um método completamente novo de entrega de interrupção - MSI (interrupções sinalizadas por mensagens). Neste método, um dispositivo sinaliza sobre a interrupção simplesmente escrevendo em um local especial na região MMIO das CPUs LAPIC.



Anteriormente, um único dispositivo PCI (isso significa todas as suas funções) poderia ter apenas 4 interrupções, mas agora tornou-se possível resolver até 32 interrupções.

No caso do MSI, não há compartilhamento de linhas de interrupção: toda interrupção corresponde naturalmente ao seu dispositivo.

As interrupções MSI também resolvem mais um problema. Por exemplo, vamos imaginar uma situação em que um dispositivo faça uma transação de gravação na memória e queira sinalizar sua conclusão através da interrupção. Mas uma transação de gravação pode ser atrasada no barramento no processo de sua transmissão (e o dispositivo não poderia saber sobre isso). Nesse caso, o sinal sobre a interrupção chegará primeiro à CPU, para que o processador leia os dados ainda não válidos. Se o MSI for usado, as informações sobre o MSI são transmitidas da mesma maneira que as mensagens de dados e, portanto, não podem ser fornecidas mais cedo.

Vale a pena notar que as interrupções do MSI não podem funcionar sem o LAPIC, mas os MSI podem substituir o E / S APIC (mais uma simplificação de design).

Após algum tempo, o método MSI foi estendido para o MSI-X. Agora, todos os dispositivos podem ter até 2048 interrupções. Agora também é possível especificar qual CPU deve processar qual interrupção. Pode ser muito útil para dispositivos de alta carga, como placas de rede, por exemplo.

Não há necessidade de uma tabela BIOS separada para suporte a MSI. Mas o dispositivo deve indicar seu suporte MSI através de um dos recursos em seu espaço PCI Config. Além disso, um driver de dispositivo deve incluir todo o suporte necessário para trabalhar com o MSI.

Conclusão


Neste artigo, estudamos informações sobre a evolução do controlador de interrupção e obtivemos um conhecimento teórico comum sobre a entrega de interrupção de dispositivos externos no sistema x86.

Na próxima parte, iremos praticar e ver como envolver cada um dos controladores de interrupção mencionados no Linux.

Na terceira parte, examinaremos o código do coreboot e veremos quais configurações são necessárias no chipset para o roteamento correto de interrupções.

Ligações:



Segmentos reconhecidos


Um agradecimento especial a Jacob Garber, da comunidade coreboot , por me ajudar com a tradução deste artigo.

Source: https://habr.com/ru/post/pt446312/


All Articles