Interrompe dispositivos externos em um sistema x86. Parte 3. Configurando o roteamento de interrupção no chipset usando o exemplo coreboot

Continuamos a considerar a configuração de interrupções de dispositivos externos no sistema x86.
Na parte 1 (A evolução dos controladores de interrupção ), examinamos os fundamentos teóricos dos controladores de interrupção e termos gerais; na parte 2 ( opções de inicialização do kernel do Linux ), vimos como o sistema operacional faz uma escolha entre controladores na prática. Nesta parte, veremos como o BIOS configura o roteamento IRQ para controladores de interrupção no chipset.

Nenhuma empresa moderna de desenvolvimento de BIOS (AwardBIOS / AMIBIOS / Insyde) divulga o código fonte de seus programas. Felizmente, porém, existe o Coreboot , um projeto para substituir o BIOS proprietário por software livre. Em seu código, veremos como o roteamento de interrupção no chipset está configurado.




Teoria


Primeiro, atualize e complemente nosso conhecimento teórico. Na Parte 1, identificamos um caminho de interrupção comum para o caso de PIC e APIC.

Pic:



APIC:



Nestas figuras, o mapeamento do dispositivo PCI → PIR é mostrado abstratamente; na verdade, é um pouco mais complicado. Na realidade, cada dispositivo PCI possui 4 linhas de interrupção (INTA #, INTB #, INTC #, INTD #). Cada dispositivo PCI pode ter até 8 funções e cada função já possui uma interrupção INTx #. A linha de INTx # que cada função do dispositivo puxará é fixa no hardware ou é determinada pela configuração do dispositivo.



Em essência, funções são blocos lógicos separados. Por exemplo, em um dispositivo PCI, pode haver uma função de controlador Smbus, uma função de controlador SATA, uma função de ponte LPC. No lado do SO, cada função é um dispositivo separado com seu próprio espaço de configuração PCI Config.

No caso mais simples (e mais comum) de um dispositivo PCI, existe apenas uma função, cuja interrupção ocorre através da linha INTA #. Mas, em geral, o dispositivo pode até ter mais de 4 funções (como dissemos antes das 8), então algumas delas terão que ser plantadas em uma linha INTx # (as interrupções do PCI podem dividir a linha). Além disso, para dispositivos PCI incluídos no chipset gravando em registros especiais, geralmente é possível indicar quais funções usam quais linhas INTx # (e se elas são usadas).

Sistematizando nosso conhecimento, denotamos o caminho (roteamento) de interrupções de qualquer função PCI através de INTx # → PIRQy → IRQz, onde:

  • INTx # - linha INT # (INTA #, INTB #, INTC #, INTD #) do dispositivo PCI que a função usará
  • PIRQy - a linha PIRQ (PIRQA, PIRQB, ...) do PIR ao qual a linha INTx # está conectada
  • IRQz - linha IRQ (0, 1, 2, ...) no controlador de interrupção (APIC / PIC), que está conectado à linha PIRQy

Por que você não pode simplesmente se conectar em qualquer lugar INTA # → PIRQA, INTB # → PIRQB, ...?


Por que se preocupar em configurar o roteamento? Suponha que decidamos não incomodar e obter todas as linhas de interrupção de todos os dispositivos PCI para as mesmas linhas PIRQ. Vamos dizer o seguinte:

  • INTA # → PIRQA
  • INTB # → PIRQB
  • Nº INTC → PIRQC
  • INTD # → PIRQD

Como dissemos acima, o caso mais comum é quando um dispositivo PCI tem uma função e sua interrupção é conectada à linha INTA # (porque por que o desenvolvedor do dispositivo deve iniciá-lo de maneira diferente?). Portanto, se de repente decidirmos iniciar todas as linhas como escrevemos, quase todas as interrupções dos dispositivos serão divididas em linhas PIRQA. Digamos que ela acabou no IRQ16. Então, toda vez que o processador for informado de que ocorreu uma interrupção na linha IRQ16, ele deverá interrogar os drivers de todos os dispositivos conectados à linha IRQ16 (PIRQA) se eles têm uma interrupção. Se houver muitos desses dispositivos, isso naturalmente não acelerará a resposta do sistema à interrupção. E as linhas PIRQB-PIRQD nesse caso ficarão inativas na maior parte do tempo. Para maior clareza, a figura que ilustra o problema:



Mas tudo poderia ser feito assim:



A imagem é um pouco confusa, mas o ponto é que simplesmente conectamos as linhas INTx # com PIRQy ao round-robin (PIRQA, PIRQB, PIRQC, PIRQD, PIRQA, PIRQB, PIRQC, PIRQD, PIRQA, PIRQB, PIRQC, PIRQD. ..)

Note-se que aqui é necessário levar em consideração não apenas o mesmo número de funções PCI em cada linha PIRQ. Afinal, algumas funções podem criar interrupções muito raramente e outras permanentemente (controlador Ethernet, por exemplo). Nesse caso, mesmo a alocação de uma linha PIRQ separada para interrupções com essa função pode ser bastante justificada.

Com base no exposto, o desenvolvedor do BIOS, entre outras coisas, tem a tarefa de garantir que as linhas PIRQ sejam carregadas uniformemente com interrupções.

O que o BIOS deve fazer?


Nós sistematizamos na figura:



  • 1) Indique qual linha do INTx # cada função dos dispositivos PCI extrai
    Para dispositivos PCI externos, esse item não é executado, mas pode ser que, para as funções dos dispositivos PCI incluídos no chipset.
  • 2) Configure o mapeamento INTx # → PIRQy para cada dispositivo PCI
    Vale a pena notar que pode haver mais de quatro sinais PIRQy padrão (PIRQA, PIRQB, PIRQC, PIRQD). Por exemplo 8: PIRQA-PIRQH.

Os sinais PIRQy vão na linha IRQz do controlador de interrupção selecionado (APIC / PIC). Como queremos oferecer suporte a todos os métodos de carregamento possíveis (consulte a parte 2 ), precisamos preencher os dois mapeamentos:

  • 3a) Preencha o mapeamento PIRQy → IRQz1 para comunicação PIR → I / O APIC
    Mas geralmente isso não é necessário, pois as linhas PIRQy são fixadas na linha APIC. A solução comum é PIRQA → IRQ16, PIRQB → IRQ17, ... A solução mais simples, porque Ao colocar as linhas PIRQy nas linhas do controlador ≥16, você não precisa se preocupar com conflitos com interrupções inseparáveis ​​dos dispositivos ISA.
  • 3b) Preencha o mapeamento PIRQy → IRQz2 para comunicação PIR → PIC
    Isso deve ser fornecido caso usemos roteamento através do controlador PIC. Não existe uma solução inequívoca como no caso da APIC, porque no caso da PIC, deve-se lembrar da possibilidade de conflitos com interrupções inseparáveis ​​dos dispositivos ISA.

O último quarto item é necessário para ajudar o sistema operacional a determinar o roteamento de interrupção. O próprio dispositivo geralmente não usa esses registros.

  • 4) Preencha os registros Linha de interrupção / Pin de interrupção para cada função PCI
    Em geral, o registro de pino de interrupção é preenchido automaticamente e geralmente é somente leitura, portanto, o preenchimento provavelmente só exigirá o preenchimento do registro de linha de interrupção. Isso deve ser fornecido caso usemos o roteamento através do controlador PIC sem fornecer ao sistema operacional nenhuma tabela sobre interrupções de roteamento (consulte novamente a parte 2 ). Se forem fornecidas tabelas e esse mapeamento for consistente com as tabelas de roteamento ($ PIR / ACPI), o sistema operacional geralmente o deixa.

Deve-se observar que ainda não tocamos nas tabelas $ PIR / MPtable / ACPI e consideramos como configurar os registros do chipset em termos de interrupções de roteamento antes de transferir o controle para o carregador do sistema. As tabelas de interrupção são um tópico para um artigo separado (possivelmente um futuro).

Então, os fundamentos teóricos são estudados, finalmente começamos a praticar!

Prática


Como exemplo de artigos desta série, uso uma placa personalizada com um processador Intel Haswell i7 e um chipset LynxPoint-LP. Neste fórum, lancei o coreboot em conjunto com o SeaBIOS. O Coreboot fornece inicialização específica do hardware e a carga útil do SeaBIOS fornece uma interface do BIOS para sistemas operacionais. Neste artigo, não descreverei o processo de configuração do coreboot, mas apenas tentarei mostrar com um exemplo que tipo de configurações de BIOS devem ser feitas no chipset para rotear interrupções de IRQ de dispositivos externos.

Como o projeto coreboot está desenvolvendo ativamente para que o artigo esteja sempre atualizado, consideraremos o código usando o exemplo da versão fixa mais recente 4.9 (release 2018-12-20).

A placa-mãe mais próxima da minha é o Google Beltino com variação da Panther. A pasta principal desta placa-mãe é a pasta "src \ mainboard \ google \ beltino" . Todas as configurações estão concentradas aqui e o código específico para esta placa.

Então, vamos começar a resolver onde os itens acima estão configurados:

1) Indique qual linha do INTx # cada função dos dispositivos PCI extrai


Esta informação é definida no arquivo “src / mainboard / google / beltino / romstage.c” na estrutura rcba_config através dos registros DxxIP (Device xx Interrupt Pin Register (IP)). Este registro indica qual pino INTx # (A / B / C / D) cada uma das funções do dispositivo emite uma interrupção.

Opções possíveis (consulte o arquivo "src / southbridge / intel / lynxpoint / pch.h" ):

0h = No interrupt 1h = INTA# 2h = INTB# 3h = INTC# 4h = INTD# 

Supõe-se que várias funções usem o mesmo pino.

Supõe-se que as funções não possam usar o pino para interrupções (sem interrupção).
Tudo como vimos na figura no início do artigo.

O código completo é responsável pelo item designado por nós:

 /* Device interrupt pin register (board specific) */ RCBA_SET_REG_32(D31IP, (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | (INTB << D31IP_SMIP) | (INTA << D31IP_SIP)), RCBA_SET_REG_32(D29IP, (INTA << D29IP_E1P)), RCBA_SET_REG_32(D28IP, (INTA << D28IP_P1IP) | (INTC << D28IP_P3IP) | (INTB << D28IP_P4IP)), RCBA_SET_REG_32(D27IP, (INTA << D27IP_ZIP)), RCBA_SET_REG_32(D26IP, (INTA << D26IP_E2P)), RCBA_SET_REG_32(D22IP, (NOINT << D22IP_MEI1IP)), RCBA_SET_REG_32(D20IP, (INTA << D20IP_XHCI)), 

Para uma melhor compreensão, considere alguns exemplos:

Exemplo 1:

O dispositivo 0x1d (29 em decimal) tem uma função (controlador EHCI).

Nesse caso, atribua uma interrupção ao INTA #.

00: 1d.0 - INTA #

 RCBA_SET_REG_32(D29IP, (INTA << D29IP_E1P)), 

Exemplo 2:
O dispositivo 0x1f (31 em decimal) possui as funções Controlador de sensor térmico (00: 1f.6), Controlador SATA 2 (00: 1f.2), Controlador SMBus (00: 1f.3), Controlador SATA 1 (00: 1f .2). Queremos usar apenas os controladores SMBus, SATA 1 e Thermal Sensor.

00: 1f.2 - INTA # (controlador SATA 1)
00: 1f.3 - INTB # (controlador SMBus)
00: 1f.2 - Sem interrupção (o controlador SATA 2 não é usado)
00: 1f.6 - INTC # (controlador do sensor térmico)

Para esta configuração, você deve escrever:

 RCBA_SET_REG_32(D31IP, (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | (INTB << D31IP_SMIP) | (INTA << D31IP_SIP)), 

Exemplo 3:

Em um dispositivo, o número de funções necessárias é maior que 4. No dispositivo 0x1c, cada função é responsável pela porta PCI Express. Para que as portas 0-5 funcionem e as interrupções sejam distribuídas igualmente entre as linhas, você pode configurar isso:

00: 1c.0 - INTA # (porta PCI Express 0)
00.1c.1 - INTB # (porta PCI Express 1)
00.1c.2 - Nº INTC (porta 2 do PCI Express)
00.1c.3 - INTD # (porta PCI Express 3)
00.1c.4 - INTA # (porta PCI Express 4)
00.1c.5 - INTB # (porta PCI Express 5)
00.1c.6 - Sem interrupção (porta não usada)
00.1c.7 - Sem interrupção (porta não usada)

 RCBA_SET_REG_32(D28IP, (INTA << D28IP_P1IP) | (INTB << D28IP_P2IP) | (INTC << D28IP_P3IP) | (INTD << D28IP_P4IP) | (INTA << D28IP_P5IP) | (INTB << D28IP_P6IP) | (NOINT << D28IP_P7IP) | (NOINT << D28IP_P8IP)), 

2) Configure o mapeamento INTx # → PIRQy para cada dispositivo PCI


Esta informação também é definida no arquivo "src \ mainboard \ google \ beltino \ romstage.c"
na estrutura rcba_config, mas já através dos registros DxxIR (Device xx Interrupt Route Register).

As informações neste registro mostram a qual linha PIRQx (A / B / C / D / E / F / G / H) cada linha de interrupção INTx # está conectada.

 /* Device interrupt route registers */ RCBA_SET_REG_32(D31IR, DIR_ROUTE(PIRQG, PIRQC, PIRQB, PIRQA)),/* LPC */ RCBA_SET_REG_32(D29IR, DIR_ROUTE(PIRQD, PIRQD, PIRQD, PIRQD)),/* EHCI */ RCBA_SET_REG_32(D28IR, DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD)),/* PCIE */ RCBA_SET_REG_32(D27IR, DIR_ROUTE(PIRQG, PIRQG, PIRQG, PIRQG)),/* HDA */ RCBA_SET_REG_32(D22IR, DIR_ROUTE(PIRQA, PIRQA, PIRQA, PIRQA)),/* ME */ RCBA_SET_REG_32(D21IR, DIR_ROUTE(PIRQE, PIRQF, PIRQF, PIRQF)),/* SIO */ RCBA_SET_REG_32(D20IR, DIR_ROUTE(PIRQC, PIRQC, PIRQC, PIRQC)),/* XHCI */ RCBA_SET_REG_32(D23IR, DIR_ROUTE(PIRQH, PIRQH, PIRQH, PIRQH)),/* SDIO */ 

Exemplo 1:

O dispositivo 0x1c (28 no sistema decimal) são as portas PCIe, como já descobrimos.

Estabelecemos uma conexão "direta":

  • INTA # → PIRQA
  • INTB # → PIRQB
  • Nº INTC → PIRQC
  • INTD # → PIRQD

 RCBA_SET_REG_32(D28IR, DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD)) 

Exemplo 2:

Dispositivo 0x1d (29 em decimal) - uma função (controlador EHCI) no INTA #, outras linhas não são usadas.

Conecte a linha INTA # ao PIRQD:

 RCBA_SET_REG_32(D29IR, DIR_ROUTE(PIRQD, PIRQD, PIRQD, PIRQD)) 

Nesse caso, apenas o primeiro registro PIRQD (para INTA #) faz sentido, o restante não faz sentido.

3a) Preencha o mapeamento PIRQy → IRQz1 (PIR → APIC)


Como já dissemos, o mapeamento geralmente é corrigido aqui, e este caso não é uma exceção.

  • PIRQA → IRQ16
  • PIRQB → IRQ17
  • ...
  • PIRQH → IRQ23

3b) Preencha o mapeamento PIRQy → IRQz2 (PIR → PIC)


No coreboot, o conteúdo para preencher esses registros é definido no arquivo devicetree.cb na pasta da placa-mãe "src \ mainboard \ google \ beltino \".

devicetree.cb (o nome devicetree para comunicação com um conceito semelhante no kernel Linux e “cb” é a abreviação de coreboot) é um arquivo especial que reflete a configuração desta placa-mãe: qual processador, chipset é usado, quais dispositivos estão incluídos neles, quais desligado etc. Além disso, informações especiais para a configuração do chipset podem ser especificadas neste arquivo. Este é apenas o caso que precisamos:

 register "pirqa_routing" = "0x8b" register "pirqb_routing" = "0x8a" register "pirqc_routing" = "0x8b" register "pirqd_routing" = "0x8b" register "pirqe_routing" = "0x80" register "pirqf_routing" = "0x80" register "pirqg_routing" = "0x80" register "pirqh_routing" = "0x80" 

Essas linhas especificam o mapeamento PIRQy → IRQz2. No código, depois de analisar o arquivo devicetree.cb, eles são transformados nas variáveis ​​"config-> pirqX_routing".

A variável "config-> pirqa_routing = 0x8b" significa que o PIRQA está conectado à linha de interrupção IRIC11 (0x0b = 11) do controlador PIC, no entanto, o bit mais alto (que é 0x80) significa que o roteamento de interrupção não é executado. Honestamente, na minha experiência, isso é um erro; por padrão, vale a pena ativar o roteamento PIC, o próprio sistema operacional poderá mudar para o I / O APIC definindo esse bit como 1, se necessário.

Ou seja, nesse caso, seria mais correto escrever:

 register "pirqa_routing" = "0x0b" register "pirqb_routing" = "0x0a" register "pirqc_routing" = "0x0b" register "pirqd_routing" = "0x0b" register "pirqe_routing" = "0x80" # not used register "pirqf_routing" = "0x80" # not used register "pirqg_routing" = "0x80" # not used register "pirqh_routing" = "0x80" # not used 

Não ativamos as últimas 4 interrupções, porque A interrupção do IRQ0 é sempre usada no timer do sistema e está claramente indisponível (consulte Informações gerais sobre interrupções compatíveis com IBM-PC ).

Mas se olharmos mais de perto o ponto 2), veremos que alguns dispositivos PCI usam as linhas PIRQE-PIRQH, portanto, deixá-los desconectados é o caminho certo para os dispositivos quebrados.

Portanto, é melhor escrever algo como isto:
 register "pirqa_routing" = "0x03" register "pirqb_routing" = "0x04" register "pirqc_routing" = "0x05" register "pirqd_routing" = "0x06" register "pirqe_routing" = "0x0a" register "pirqf_routing" = "0x0b" register "pirqg_routing" = "0x0e" register "pirqh_routing" = "0x0f" 


O preenchimento real dos registros correspondentes ocorre no arquivo src \ southbridge \ intel \ lynxpoint \ lpc.c na função pch_pirq_init.

Snippet de código responsável pelo preenchimento do registro:

 /* Get the chip configuration */ config_t *config = dev->chip_info; pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing); pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing); pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing); pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing); pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing); pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing); pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing); pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing); 

As constantes de endereço de registro são descritas no mesmo arquivo pch.h

 #define PIRQA_ROUT 0x60 #define PIRQB_ROUT 0x61 #define PIRQC_ROUT 0x62 #define PIRQD_ROUT 0x63 #define PIRQE_ROUT 0x68 #define PIRQF_ROUT 0x69 #define PIRQG_ROUT 0x6A #define PIRQH_ROUT 0x6B 

O mapeamento de PIRQy → IRQz2 para este chipset é gravado no dispositivo LPC PCI (endereço 00: 1f.0) nos registros PIRQy_ROUT. Deve-se notar que geralmente nem todas as 15 linhas IRQz2 por PIC são permitidas para uso, mas apenas uma parte (por exemplo, 3,4,5,6,7,9,10,11,12,14,15). A descrição desses registros deve conter informações sobre quais IRQs estão disponíveis para atribuir interrupções das linhas PIRQ a eles. Portanto, o mapeamento proposto por nós acima só é possível se a atribuição de PIRQ na linha IRQ3, IRQ4, IRQ5, IRQ6, IRQ6, IRQ10, IRQ11, IRQ14, IRQ15 estiver disponível. Mas se observarmos cuidadosamente o comentário antes da função pch_pirq_init, veremos que é:

 /* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control * 0x00 - 0000 = Reserved * 0x01 - 0001 = Reserved * 0x02 - 0010 = Reserved * 0x03 - 0011 = IRQ3 * 0x04 - 0100 = IRQ4 * 0x05 - 0101 = IRQ5 * 0x06 - 0110 = IRQ6 * 0x07 - 0111 = IRQ7 * 0x08 - 1000 = Reserved * 0x09 - 1001 = IRQ9 * 0x0A - 1010 = IRQ10 * 0x0B - 1011 = IRQ11 * 0x0C - 1100 = IRQ12 * 0x0D - 1101 = Reserved * 0x0E - 1110 = IRQ14 * 0x0F - 1111 = IRQ15 * PIRQ[n]_ROUT[7] - PIRQ Routing Control * 0x80 - The PIRQ is not routed. */ 

4) Preencha os registros Linha de interrupção / Pin de interrupção para cada função PCI


No espaço de configuração do PCI (cada PCI possui funções de acordo com o padrão), existem 2 registros de interesse para nós:

  • 3Ch: Linha de interrupção - aqui você precisa escrever o número IRQz2 (um número de 0 a 15), o número de interrupção que a função eventualmente puxa ao usar o controlador PIC
  • 3Dh: Pin de interrupção - mostra qual linha INTx # (A / B / C / D) a função usa

Vamos começar com o último. O registro do pino de interrupção será preenchido automaticamente com base nas configurações do chipset (registros DxxIP) feitas por nós no parágrafo 1 e será somente leitura.

Portanto, tudo o que resta é preencher o registro da linha de interrupção com uma interrupção IRQz2 para cada função PCI.

Conhecendo o mapeamento PIRQy → IRQz2 (item 3b) e o mapeamento INTx # → PIRQy (item 2), é possível preencher facilmente o registro da Linha de Interrupção para cada função, sabendo qual INTx # interrompe (item 1).

No coreboot, os registros da linha de interrupção também são preenchidos no arquivo src \ southbridge \ intel \ lynxpoint \ lpc.c na função pch_pirq_init:

 /* Eric Biederman once said we should let the OS do this. * I am not so sure anymore he was right. */ for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { u8 int_pin=0, int_line=0; if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) continue; int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); switch (int_pin) { case 1: /* INTA# */ int_line = config->pirqa_routing; break; case 2: /* INTB# */ int_line = config->pirqb_routing; break; case 3: /* INTC# */ int_line = config->pirqc_routing; break; case 4: /* INTD# */ int_line = config->pirqd_routing; break; } if (!int_line) continue; pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); } 

Por alguma razão, esse código implica que o mapeamento é, de qualquer forma, INTA # → PIRQA, INTB # → PIRQB, INTC # → PIRQC, INTD # → PIRQD. Embora na prática vimos que pode ser diferente (ver parágrafo 2).

Geralmente "Eric Biederman disse uma vez", e nós o copiamos em qualquer lugar:

 $ grep "Eric Biederman once said" -r src/ src/southbridge/intel/fsp_bd82x6x/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/i82801gx/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/i82801ix/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/lynxpoint/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/sch/lpc.c: /* Eric Biederman once said we should let the OS do this. 

Em geral, o coreboot realmente não se importa com o suporte a interrupções herdadas. Tanta coisa para se surpreender com esse erro não vale a pena. Ao carregar um sistema operacional moderno, isso não vai incomodá-lo, mas se você precisar carregar repentinamente o Linux com as opções “acpi = off nolapic”, isso dificilmente será possível.

Conclusão


Em conclusão, repetiremos as informações típicas que precisam ser configuradas no chipset para rotear interrupções PCI:

  1. Indique qual linha INTx # cada função PCI puxa
  2. Configure o mapeamento INTx # → PIRQy para cada dispositivo PCI
  3. Mapeamento de preenchimento PIRQy → IRQz1 (PIR → APIC) e mapeamento PIRQy → IRQz2 (PIR → PIC)
  4. Preencha os registros Linha de interrupção / Pin de interrupção do espaço de configuração do PCI para cada função do PCI.

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


All Articles