coreboot_x86系统中的外部中断。 第3部分。以coreboot为例,说明芯片组中的中断路由设置

coreboot

We continue to investigate external device interrupt routing setup in the x86 system.

我们将继续研究x86系统中的外部设备中断路由设置。

In Part 1 (Interrupt controller evolution) we looked at the theory behind interrupt controllers and all the necessary terminology. In Part 2 (Linux kernel boot options) we looked at how in practice the OS chooses between different interrupt controllers. In this part we will investigate how the BIOS sets IRQ to the interrupt controllers routing in a chipset.

在第1部分( 中断控制器的发展 )中,我们研究了中断控制器背后的理论和所有必要的术语。 在第2部分( Linux内核引导选项 )中,我们研究了操作系统在实践中如何在不同的中断控制器之间进行选择。 在这一部分中,我们将研究BIOS如何将IRQ设置为芯片组中中断控制器的路由。

None of the modern BIOS developer companies (AwardBIOS/AMIBIOS/Insyde) open their source code. But luсkily there is coreboot — a project aimed at replacing proprietary BIOS with free firmware code. In its source code we'll see what is needed to setup the interrupt routing in a chipset.

现代BIOS开发人员公司(AwardBIOS / AMIBIOS / Insyde)都没有打开其源代码。 但幸运的是有一个coreboot —一个旨在用免费固件代码替换专有BIOS的项目。 在其源代码中,我们将了解在芯片组中设置中断路由所需的条件。

理论 (Theory)

To start let's review and expand our theoretical knowledge of the subject. In Part 1 we highlighted a common path of an interrupt from the external device for the PIC and APIC cases.

首先,让我们回顾并扩展我们对该主题的理论知识。 在第1部分中,我们重点介绍了PIC和APIC情况下来自外部设备的中断的通用路径。

图片: (PIC:)

APIC: (APIC:)

On these pictures the mapping 'PCI device → PIR' is fairly abstract — it is actually a little bit more complicated. In reality every PCI device has four interrupt lanes (INTA#, INTB#, INTC#, INTD#). Also, every PCI device can have up to 8 so called 'functions' (which actually trigger interrupts) and each of these functions can connect to one of the INTx# lanes. Which INTx# interrupt will trigger each of the functions is either fixed in the hardware, or is defined by the device software configuration.

在这些图片上,“ PCI设备→PIR”的映射相当抽象-实际上要复杂一些。 实际上,每个PCI设备都有四个中断通道(INTA#,INTB#,INTC#,INTD#)。 此外,每个PCI设备最多可以具有8个所谓的“功能”(实际上触发中断),并且每个功能都可以连接到INTx#通道之一。 哪个INTx#中断将触发每个功能,要么固定在硬件中,要么由设备软件配置定义。

In a nutshell these functions are like separate logical blocks. For example, one PCI device can have in itself the 'SMBus controller' function, the 'SATA controller' function, and the 'LPC bridge' function. From the point of view of the OS every function is like a separate device with its own configuration space PCI Config.

简而言之,这些功能就像单独的逻辑块。 例如,一个PCI设备本身可以具有“ SMBus控制器”功能,“ SATA控制器”功能和“ LPC桥接器”功能。 从操作系统的角度来看,每个功能都像一个单独的设备,具有自己的配置空间PCI Config。

In the most simple (and most common) case, a PCI device has only one function with its interrupt going to the lane INTA#. In general the device can have even more that 4 functions (as we've already said up to 8), and in this case it has to connect some of them to the same INTx# lane (PCI interrupts can share an interrupt lane). Also for PCI devices that are part of a chipset, by writing to its special registers it is usually possible to declare which functions use which INTx# lanes (or don't use them at all).

在最简单(也是最常见)的情况下,PCI设备只有一种功能,其中断将进入通道INTA#。 通常,该设备可以具有4个以上的功能(我们已经说了多达8个功能),在这种情况下,它必须将其中一些功能连接到同一INTx#通道(PCI中断可以共享一个中断通道)。 同样对于属于芯片组的PCI设备,通过写入其特殊寄存器,通常可以声明哪些功能使用哪些INTx#通道(或根本不使用它们)。

Summarizing all of that, let us write the complete path (routing) for an interrupt from any PCI function through INTx#→PIRQy→IRQz, where:

总结所有这些,让我们通过INTx#→PIRQy→IRQz编写来自任何PCI功能的中断的完整路径(路由),其中:

  • INTx# — INT# lane (INTA#, INTB#, INTC#, INTD#) of the PCI device, which is used by a PCI function

    INTx#— PCI设备使用的INT#通道(INTA#,INTB#,INTC#,INTD#),由PCI功能使用
  • PIRQy — PIRQ lane (PIRQA, PIRQB, ...) from the PIR, which is connected with the INTx# lane

    PIRQy —来自PIR的PIRQ通道(PIRQA,PIRQB等),与INTx#通道连接
  • IRQz — IRQ line (0, 1, 2, ...) from the interrupt controller (APIC/PIC), which is connected with the PIRQy lane

    IRQz —来自中断控制器(APIC / PIC)的IRQ线(0、1、2 ...),与PIRQy通道连接

为什么我们不能仅在各处连接INTA#→PIRQA,INTB#→PIRQB ...? (Why can't we just connect INTA#→PIRQA, INTB#→PIRQB,… everywhere?)

Why should we waste our time paying attention to the interrupt routing setup? Suppose we've decided to not bother at all and have connected all the interrupt lanes from every PCI device to the PIRQ lanes in the same way. For example:

为什么我们要浪费时间关注中断路由设置? 假设我们决定完全不打扰,并以相同的方式将每个PCI设备的所有中断通道连接到PIRQ通道。 例如:

  • INTA#→PIRQA

    INTA#→PIRQA
  • INTB#→PIRQB

    INTB#→PIRQB
  • INTC#→PIRQC

    INTC#→PIRQC
  • INTD#→PIRQD

    INTD#→PIRQD

As we've already said above, the most common case is when a PCI device has only one function, and its interrupt is connected to the INTA# lane (because why would hardware device developer route it in a different way?). Therefore if we decided to route all lanes as we've written, almost all of the devices in a system would share interrupt lane PIRQA. Suppose this lane is connected to the IRQ16. This way, every time a processor has a signal that there is an interrupt on a IRQ16 line, it has to question all of the device drivers of the PCI devices connected to that IRQ16 line (PIRQA) if they have an interrupt. If there are many of those devices, it surely wouldn't increase system response to the interrupt. And in this case lanes PIRQB-PIRQD would stand idle wasted most of the time. Here is a picture that illustrates this problem:

正如我们上面已经说过的,最常见的情况是PCI设备只有一个功能,并且其中断连接到INTA#通道(因为为什么硬件设备开发人员会以不同的方式路由它?)。 因此,如果我们决定按照我们编写的方式路由所有通道,那么系统中的几乎所有设备都将共享中断通道PIRQA。 假设此通道连接到IRQ16。 这样,每当处理器收到IRQ16线路上有中断的信号时,它都必须询问与该IRQ16线路(PIRQA)连接的PCI设备的所有设备驱动程序是否存在中断。 如果这些设备很多,那么肯定不会增加系统对该中断的响应。 在这种情况下,大多数时间,PIRQB-PIRQD通道将处于空闲状态。 这是说明此问题的图片:

Here is what it could be like:

可能是这样的:

This picture is a little bit complicated, but the point is that here we simply connect INTx# lanes with PIRQy lanes in a round-robin way (PIRQA, PIRQB, PIRQC, PIRQD, PIRQA, PIRQB, PIRQC, PIRQD, PIRQA, PIRQB, PIRQC, PIRQD, ...).

这张图片有点复杂,但要点是,这里我们只是以循环方式将INTx#通道与PIRQy通道(PIRQA,PIRQB,PIRQC,PIRQD,PIRQA,PIRQB,PIRQC,PIRQD,PIRQA,PIRQB, PIRQC,PIRQD,...)。

You should take into account that each PIRQ should have fairly the same number of PCI functions connected to it, and you should also consider that some functions trigger interrupts very rarely and some almost constantly (e.g. Ethernet controller). In this case even allocation of a separate PIRQ lane for such function may be completely justified.

您应考虑到每个PIRQ都应连接相当数量的PCI功能,并且还应考虑到某些功能很少触发中断,而几乎总是触发中断(例如以太网控制器)。 在这种情况下,甚至完全有必要为此功能分配单独的PIRQ通道。

From all of the above the BIOS developers have to balance PIRQ lanes with the interrupt load.

通过以上所有操作,BIOS开发人员必须在PIRQ通道与中断负载之间取得平衡。

BIOS应该怎么做? (What should BIOS do anyway?)

Here is a picture that summarizes the answer:

这是总结答案的图片:

  • 1)对于每个PCI设备的每个PCI函数,声明该函数触发哪个INTx# (1) For every PCI function of every PCI device declare which INTx# the function triggers)

    For the external PCI devices you don't do any of this, but for the functions of PCI devices that are a part of the chipset you probably have to.

    对于外部PCI设备,您无需执行任何操作,但对于作为芯片组一部分的PCI设备的功能,则可能必须这样做。

  • 2)对于每个PCI设备设置INTx#→PIRQy映射 (2) For every PCI device set INTx#→PIRQy mapping)

    It is worthwhile to notice that it can be more than the standard 4 PIRQy signals (PIRQA, PIRQB, PIRQC, PIRQD). For example there can be 8 of them: PIRQA-PIRQH.

    值得注意的是,它可以超过标准的4个PIRQy信号(PIRQA,PIRQB,PIRQC,PIRQD)。 例如,可以有8个:PIRQA-PIRQH。

PIRQy signals go to IRQz lanes of the chosen interrupt controller (APIC/PIC). As we want to support all possible boot modes (take a look into Part 2 for that) it is necessary to fill both of these mappings:

PIRQy信号进入所选中断控制器(APIC / PIC)的IRQz通道。 由于我们要支持所有可能的启动模式(为此,请参阅第2部分),有必要填写以下两个映射:

  • 3a)填写PIR→I / O APIC连接的映射PIRQy→IRQz1 (3a) Fill the mapping PIRQy→IRQz1 for the PIR→I/O APIC connection)

    Usually you don't have to do it, because in most cases the PIRQy lanes are hardwired to particular APIC lanes. The most common solution is PIRQA→IRQ16, PIRQB→IRQ17,… It is also the most simple one, because when we connect PIRQy lanes to the interrupt controller lanes ≥16, we don't have to worry about conflicts with non-shareable interrupts from ISA devices.

    通常您不必这样做,因为在大多数情况下,PIRQy通道被硬连线到特定的APIC通道。 最常见的解决方案是PIRQA→IRQ16,PIRQB→IRQ17,…这也是最简单的解决方案,因为当我们将PIRQy通道连接到≥16的中断控制器通道时,我们不必担心与非共享中断的冲突从ISA设备。

  • 3b)填写PIR→PIC连接的映射PIRQy→IRQz2 (3b) Fill the mapping PIRQy→IRQz2 for the PIR→PIC connection)

    This should be done if we want to support the case of routing through the PIC controller. There is no such obvious solution like in an APIC case, because with a PIC you should take into account the possibility of conflicts with non-shareable interrupts from ISA devices.

    如果我们要支持通过PIC控制器进行路由的情况,则应这样做。 没有像APIC这样的明显解决方案,因为使用PIC,您应该考虑与ISA设备的不可共享中断发生冲突的可能性。

The next item that should be done is necessary to help the OS determine the routing mode. Hardware by itself usually doesn't use data from these registers:

为了帮助OS确定路由模式,必须执行的下一项。 硬件本身通常不使用这些寄存器中的数据:

  • 4)为每个PCI功能填充“中断线/中断引脚”寄存器 (4) Fill 'Interrupt Line/Interrupt Pin' registers for every PCI function)

    Actually the 'Interrupt Pin' register is usually filled automatically and in most cases is 'Read-Only', therefore it is probably only necessary to fill the 'Interrupt Line' register. It should be done when we use a routing through the PIC controller without giving the OS any interrupt routing table (again take a look into Part 2 for the explanation). If interrupt tables are present and this mapping is the same as the mapping in those tables ($PIR/ACPI), then the OS usually keeps it.

    实际上,“中断引脚”寄存器通常是自动填充的,并且在大多数情况下是“只读”的,因此可能只需要填充“中断线”寄存器。 当我们使用通过PIC控制器的路由而不给OS任何中断路由表时,应该这样做(再次看一下第2部分的解释)。 如果存在中断表,并且此映射与那些表中的映射相同($ PIR / ACPI),则OS通常会将其保留。

It is worthwhile to notice that we don't touch the topic of $PIR/MPtable/ACPI tables yet, we only look into how the registers of a chipset that are responsible for the interrupt routing should be configured before we handle execution to the OS bootloader. The interrupt tables are the subject of a separate article (which will probably be in the future).

值得注意的是,我们还没有触及$ PIR / MPtable / ACPI表的主题,我们只研究如何配置负责中断路由的芯片组寄存器,然后再处理对OS的执行。引导程序。 中断表是另一篇文章的主题(可能会在将来)。

So, all the theoretical ground is covered, let's go into practice!

因此,涵盖了所有理论基础,让我们开始实践吧!

实践 (Practice)

As an example board in these articles I use custom motherboard with an Intel Haswell i7 CPU and LynxPoint-LP chipset. For this board I've ported and flashed coreboot with the SeaBIOS payload. Coreboot does all the necessary hardware-dependent initialization while its payload (SeaBIOS) provides the BIOS interface for the operating system. In this article I won't go into the coreboot configuration process, I will just try to show by example what settings the BIOS should do in a chipset for the correct IRQ interrupt routing from the external devices.

作为这些文章中的示例板,我使用具有Intel Haswell i7 CPU和LynxPoint-LP芯片组的定制主板。 对于此板,我已经将SeaBIOS负载移植并刷新了coreboot。 Coreboot执行所有必要的依赖于硬件的初始化,而其有效载荷(SeaBIOS)为操作系统提供BIOS接口。 在本文中,我不会介绍coreboot的配置过程,仅尝试通过示例显示BIOS在芯片组中应进行哪些设置,以正确地从外部设备进行IRQ中断路由。

The coreboot project is actively developing, so to keep this article relevant, we will look at the source code for the last fixed version 4.9 (release 2018-12-20).

coreboot项目正在积极开发中,因此,为了保持本文的相关性,我们将查看最后一个固定版本4.9 (版本2018-12-20)的源代码。

The motherboard that is the most similar to mine is a 'Google Beltino' with a 'Panther' variation. The main folder in the source code for this motherboard is «src\mainboard\google\beltino». Here are all the configuration settings and all of the specific code for this particular motherboard.

与我的最相似的主板是带有“ Panther”变体的“ Google Beltino”。 该主板源代码中的主文件夹为«src \ mainboard \ google \ beltino» 。 这是该特定主板的所有配置设置和所有特定代码。

Now, let's find out where the configuration of all the things we have mentioned above is happening:

现在,让我们找出上面提到的所有配置在哪里发生:

1)对于每个PCI设备的每个PCI函数,声明该函数触发哪个INTx# (1) For every PCI function of every PCI device declare which INTx# the function triggers)

This information is defined in the file «src/mainboard/google/beltino/romstage.c» in the structure 'rcba_config' through the 'DxxIP' registers (Device xx Interrupt Pin Register (IP)). This register shows to which of the INTx# (A/B/C/D) pins each PCI function connects its interrupt.

此信息通过“ DxxIP”寄存器(设备xx中断引脚寄存器(IP))在结构“ rcba_config”的“ src / mainboard / google / beltino / romstage.c ”文件中定义。 该寄存器显示每个PCI功能将其中断连接到INTx#(A / B / C / D)引脚中的哪一个。

Possible choices are (see file «src/southbridge/intel/lynxpoint/pch.h»):

可能的选择是(请参见文件“ src / southbridge / intel / lynxpoint / pch.h” ):

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

It is possible that multiple functions would use the same pin.

多种功能可能会使用同一引脚。

It is allowed for a function to not use any interrupt pin at all ('No interrupt'). Everything is like as we saw on a picture in the beginning of the article.

允许某个功能完全不使用任何中断引脚(“无中断”)。 一切都像我们在文章开头看到的那样。

The full code responsible for this paragraph is:

负责此段的完整代码为:

/* 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)),

For better understanding let's look at a few examples:

为了更好地理解,让我们看一些示例:

范例1: (Example 1:)

In PCI device 0x1d (29 in decimal) there is one function (EHCI controller).

在PCI设备0x1d(十进制为29)中,只有一个功能(EHCI控制器)。

In this case we set its interrupt to INTA#.

在这种情况下,我们将其中断设置为INTA#。

00:1d.0 — INTA#

00:1d.0 — INTA#

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

范例2: (Example 2:)

In PCI device 0x1f (31 in decimal) there are several functions: Thermal Sensor controller (00:1f.6), SATA controller 2 (00:1f.2), SMBus controller (00:1f.3), SATA controller 1 (00:1f.2). We want to use SMBus controller, SATA controller 1, and Thermal Sensor controller only.

在PCI设备0x1f(十进制为31)中,有几个功能:热传感器控制器(00:1f.6),SATA控制器2(00:1f.2),SMBus控制器(00:1f.3),SATA控制器1( 00:1f.2)。 我们只想使用SMBus控制器,SATA控制器1和热传感器控制器。


00:1f.2 - INTA# (SATA controller 1)
00:1f.3 - INTB# (SMBus controller)
00:1f.2 - No interrupt (SATA controller 2 is not used)
00:1f.6 - INTC# (Thermal Sensor controller)

For this configuration we should write:

对于此配置,我们应编写:

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

范例3: (Example 3:)

One of the PCI devices has more than 4 functions that need to be enabled. In device 0x1c (decimal 28) every function is responsible for a particular PCI Express port. To enable ports 0-5 and to make the interrupts from them evenly balanced we can set it up like this:

其中一个PCI设备具有超过4个需要启用的功能。 在设备0x1c(十进制28)中,每个功能都负责特定的PCI Express端口。 要启用端口0-5并使它们之间的中断均匀平衡,我们可以这样设置:


00:1c.0 - INTA#	(PCI Express Port 0)
00.1c.1 - INTB#	(PCI Express Port 1)
00.1c.2 - INTC#	(PCI Express Port 2)
00.1c.3 - INTD#	(PCI Express Port 3)
00.1c.4 - INTA#	(PCI Express Port 4)
00.1c.5 - INTB#	(PCI Express Port 5)
00.1c.6 - No interrupt	(port is not used)
00.1c.7 - No interrupt	(port is not used)
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)对于每个PCI设备设置INTx#→PIRQy映射 (2) For every PCI device set INTx#→PIRQy mapping)

This information is also defined in the file «src\mainboard\google\beltino\romstage.c» in the 'rcba_config' structure, but in this case it is done through the 'DxxIR' registers (Device xx Interrupt Route Register).

该信息也在“ rcba_config”结构的“ src \ mainboard \ google \ beltino \ romstage.c ”文件中定义,但在这种情况下,它是通过“ DxxIR”寄存器(设备xx中断路由寄存器)完成的。

Information in these registers shows to which PIRQy lane (A/B/C/D/E/F/G/H) each of the INTx# lanes is connected.

这些寄存器中的信息显示每个INTx#通道连接到哪个PIRQy通道(A / B / C / D / E / F / G / H)。

/* 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 */

范例1: (Example 1:)

The PCI device 0x1c (28 in decimal) presents PCIe ports as we already know.

我们已经知道,PCI设备0x1c(十进制为28)表示PCIe端口。

Let's make a direct connection:

让我们直接建立连接:

  • INTA#→PIRQA

    INTA#→PIRQA
  • INTB#→PIRQB

    INTB#→PIRQB
  • INTC#→PIRQC

    INTC#→PIRQC
  • INTD#→PIRQD

    INTD#→PIRQD
RCBA_SET_REG_32(D28IR, DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD))

范例2: (Example 2:)

The PCI device 0x1d (29 in decimal) has one function (EHCI controller) on its INTA# pin, the rest of the lanes are unused.

PCI设备0x1d(十进制29)在其INTA#引脚上具有一项功能(EHCI控制器),其余通道未使用。

Connect lane INTA# with PIRQD:

将通道INTA#与PIRQD连接:

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

In this case only the first record PIRQD (for INTA#) makes sense, the rest of them don't do anything useful.

在这种情况下,只有第一个记录PIRQD(对于INTA#)才有意义,而其他记录则无济于事。

3a)填写PIR→I / O APIC连接的映射PIRQy→IRQz1 (3a) Fill the mapping PIRQy→IRQz1 for the PIR→I/O APIC connection)

As we've already said, this mapping is usually fixed in the hardware, and this case is not an exception.

正如我们已经说过的,此映射通常在硬件中固定,这种情况也不例外。

  • PIRQA→IRQ16

    PIRQA→IRQ16
  • PIRQB→IRQ17

    PIRQB→IRQ17
  • ...

    ...
  • PIRQH→IRQ23

    PIRQH→IRQ23
3b)填写PIR→PIC连接的映射PIRQy→IRQz2 (3b) Fill the mapping PIRQy→IRQz2 for the PIR→PIC connection)

In coreboot the content for these registers is defined in the file devicetree.cb in the motherboard folder «src\mainboard\google\beltino\».

在coreboot中,这些寄存器的内容在主板文件夹“ src \ mainboard \ google \ beltino \»中的devicetree.cb文件中定义。

'devicetree.cb' (the name 'devicetree' comes from the similar concept in the Linux kernel, and «cb» is an abbreviation for the word «coreboot») is a special file declaring configurations of a particular motherboard such as: which processor is used, which chipset is used, which devices are enabled in the chipset and which are not, and so on. In addition to that this file could contain some special information about chipset configuration. This is exactly what we need in this case:

“ devicetree.cb”(名称“ devicetree”来自Linux内核中的类似概念,“ cb”是“ coreboot”一词的缩写)是一个特殊文件,用于声明特定主板的配置,例如:哪个处理器使用,使用哪个芯片组,在芯片组中启用了哪些设备,哪些未启用,等等。 除此之外,该文件还可以包含有关芯片组配置的一些特殊信息。 在这种情况下,这正是我们需要的:

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"

These strings define PIRQy→IRQz2 mapping. In the code, after 'devicetree.cb' file parsing they would transform into variables «config->pirqX_routing».

这些字符串定义PIRQy→IRQz2映射。 在代码中,“ devicetree.cb”文件解析后,它们将转换为变量“ config-> pirqX_routing»。

The assignment «config->pirqa_routing = 0x8b» will mean that PIRQA is connected to IRQ11 lane (0x0b = 11) of the PIC controller, but the most significant bit (which is 0x80) means that interrupt routing is not active. To be honest, for me this seems like a mistake — by default PIC routing should be enabled, since the operating system can change to I/O APIC use by itself by setting this bit to 1 if it is necessary.

分配“ config-> pirqa_routing = 0x8b”将意味着PIRQA连接到PIC控制器的IRQ11通道(0x0b = 11),但是最高有效位(0x80)表示中断路由无效。 老实说,对我来说,这似乎是一个错误-默认情况下,应启用PIC路由,因为如果需要,操作系统可以通过将此位设置为1来更改为I / O APIC使用。

Therefore in this case it seems more correct to write:

因此,在这种情况下,编写以下内容似乎更正确:

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

We didn't enable the last 4 interrupts, because IRQ0 is always used for the system timer and surely can't be used (see General IBM-PC Compatible Interrupt Information).

我们没有启用最后4个中断,因为IRQ0始终用作系统计时器,并且肯定不能使用(请参阅IBM-PC兼容的通用中断信息 )。

But if we look carefully at paragraph '2)', we see that some PCI devices use PIRQE-PIRQH lanes, therefore keeping them unconnected is a right path to non-working devices.

但是,如果我们仔细查看“ 2)”段,就会发现某些PCI设备使用PIRQE-PIRQH通道,因此保持它们未连接是通向不工作设备的正确途径。

So it is better to write something like this:

所以最好这样写:

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"

Actual filling of the corresponding device registers happens in the file src\southbridge\intel\lynxpoint\lpc.c in the function 'pch_pirq_init'.

相应设备寄存器的实际填充发生在函数“ pch_pirq_init”中的文件src \ southbridge \ intel \ lynxpoint \ lpc.c中。

The code fragment that is responsible for the register setting is:

负责寄存器设置的代码片段为:

/* 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);

The address constants are defined in the same file pch.h

地址常数在同一文件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

PIRQy→IRQz2 mapping for this particular chipset is written in PCI device LPC (address 00:1f.0) into the registers PIRQy_ROUT. It should be taken into account that usually not all the lanes IRQz2 on the PIC are permitted for use, but only part of them (e.g. 3,4,5,6,7,9,10,11,12,14,15). In a description for these registers there should be information about which IRQs are available for the connection of PIRQ lanes to them. So the proposed above mapping is possible only if a connection of PIRQ is possible to the lanes IRQ3, IRQ4, IRQ5, IRQ6, IRQ10, IRQ11, IRQ14, IRQ15. But if we look closely to the comment above the function 'pch_pirq_init', we see that this is the exact situation:

此特定芯片组的PIRQy→IRQz2映射被写入PCI设备LPC(地址00:1f.0)中的寄存器PIRQy_ROUT中。 应该考虑到通常不是PIC上的所有通道IRQz2都被允许使用,而仅允许使用其中的一部分(例如3、4、5、6、7、9、10、11、12、14、15) 。 在这些寄存器的描述中,应该有关于哪些IRQ可用于将PIRQ通道连接到它们的信息。 因此,仅当可能将PIRQ连接到通道IRQ3,IRQ4,IRQ5,IRQ6,IRQ10,IRQ11,IRQ14,IRQ15时,上述提议的映射才可能。 但是,如果我们仔细查看函数“ pch_pirq_init”上方的注释,就会发现这是确切的情况:

/* 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)为每个PCI功能填充“中断线/中断引脚”寄存器 (4) Fill 'Interrupt Line/Interrupt Pin' registers for every PCI function)

There are 2 registers in a PCI config space (which every PCI function has by the PCI standard) that we are interested in:

我们感兴趣的是PCI配置空间中的2个寄存器(每个PCI功能按PCI标准都有):

  • 3Ch: Interrupt Line — here we should write 'IRQz2' (number from 0 to 15), the number of an interrupt which eventually triggers the PIC controller when it is used

    3Ch:中断线-我们应该在这里写“ IRQz2”(从0到15的数字),该中断号最终将在使用PIC控制器时触发它
  • 3Dh: Interrupt Pin — shows which INTx# lane (A/B/C/D) PCI function uses

    3Dh:中断引脚—显示使用哪个INTx#通道(A / B / C / D)PCI功能

Let's start from the latter. The 'Interrupt Pin' register would be filled automatically from the settings of the chipset (registers 'DxxIP') that we made in paragraph 1. We can't even explicitly rewrite these PCI registers, they are 'Read-Only' for the code.

让我们从后者开始。 “中断引脚”寄存器将根据我们在第1段中所做的芯片集(寄存器“ DxxIP”)的设置自动填充。我们甚至无法显式重写这些PCI寄存器,它们对于代码而言是“只读”的。

So the only thing that is left for us to do is to fill the 'Interrupt Line' register with an IRQz2 interrupt for every PCI function.

因此,剩下要做的就是为每个PCI功能的IRQz2中断填充“中断线”寄存器。

If we know the mapping PIRQy→IRQz2 (paragraph 3b), and a mapping INTx#→PIRQy (paragraph 2) we could easily fill the 'Interrupt Line' register for every PCI function, knowing which interrupt INTx# it actually uses (paragraph 1).

如果我们知道映射PIRQy→IRQz2(第3b段)和映射INTx#→PIRQy(第2段),我们可以很容易地为每个PCI功能填充“中断线”寄存器,知道它实际上使用了哪个中断INTx#(第1段) )。

In coreboot, 'Interrupt Line' registers are filled in the same file src\southbridge\intel\lynxpoint\lpc.c of the same 'pch_pirq_init' function:

在coreboot中,将“中断线 ”寄存器填充到相同“ pch_pirq_init”功能的相同文件src \ southbridge \ intel \ lynxpoint \ lpc.c中:

/* 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);
}

This code for some reason implies that the mapping in any case should be INTA#→PIRQA, INTB#→PIRQB, INTC#→PIRQC, INTD#→PIRQD. But as we've seen earlier this easily could be done in a different way (see paragraph 2).

由于某种原因,该代码意味着在任何情况下映射都应为INTA#→PIRQA,INTB#→PIRQB,INTC#→PIRQC,INTD#→PIRQD。 但是,正如我们之前所看到的,可以轻松地以其他方式完成此操作(请参见第2段)。

So it seems like «Eric Biederman once said» and we've copy-pasted it everywhere:

因此,好像«Eric Biederman曾经说过»,我们已经将其复制粘贴到任何地方:

$ 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.

In general coreboot doesn't pay much attention to legacy interrupt modes support. So you shouldn't be very surprised by this situation. It wouldn't prevent modern OS from booting, but if you ever decide to boot Linux with options like «acpi=off nolapic», it probably wouldn't be possible.

通常,coreboot不太关注传统中断模式的支持。 因此,您不必为这种情况感到惊讶。 它不会阻止现代操作系统的启动,但是如果您决定使用«acpi = off nolapic»之类的选项来启动Linux,则可能无法实现。

结论 (Conclusion)

In conclusion let's repeat the typical information that is needed to be configured in a chipset to set PCI interrupt routing:

总之,让我们重复在芯片组中设置PCI中断路由所需的典型信息:

  1. For every PCI function of every PCI device declare which INTx# the function triggers

    对于每个PCI设备的每个PCI函数,声明函数触发哪个INTx#
  2. For every PCI device set INTx#→PIRQy mapping

    对于每个PCI设备集INTx#→PIRQy映射
  3. Fill the mapping PIRQy→IRQz1 (PIR→APIC) and a mapping PIRQy→IRQz2 (PIR→PIC)

    填写映射PIRQy→IRQz1(PIR→APIC)和映射PIRQy→IRQz2(PIR→PIC)
  4. Fill 'Interrupt Line/Interrupt Pin' registers for every PCI function.

    为每个PCI功能填充“中断线/中断引脚”寄存器。
致谢 (Acknowledgments)

Special thanks to Jacob Garber from the coreboot community for helping me with this article translation

特别感谢coreboot社区的Jacob Garber帮助我完成本文翻译

翻译自: https://habr.com/en/post/501912/

coreboot

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
coreboot设置屏幕的timing参数通常需要修改相关的设备树文件和源代码。具体步骤如下: 1. 打开设备树文件,通常在`src/mainboard/<vendor>/<board>/devicetree.cb`,可以使用文本编辑器打开该文件。 2. 找到相关的设备节点,例如`lcd`、`display`等,根据屏幕的型号和接口类型进行修改,以设置正确的timing参数。例如: ``` device pci 1.0 on end # assume the device is connected to PCI1 chip drivers/pci/onboard device pci104c,8039.0 on # assume this is the LCD controller chip drivers/generic/lcd device panel chip drivers/generic/lcd_panel # set timing parameters here config gfx_display_width = 800 config gfx_display_height = 600 config gfx_display_depth = 24 config gfx_display_refresh = 60 config gfx_display_bpp = 32 config gfx_payload_x = 0 config gfx_payload_y = 0 config gfx_payload_offset = 0 config gfx_payload_lfb_base = 0x000a0000 ``` 3. 修改相关的源代码,以支持新的timing参数。例如,在`src/drivers/generic/lcd_panel.c`,可以添加新的timing参数并修改初始化函数,以正确设置屏幕的timing参数。 4. 编译和测试代码,确保屏幕的timing参数被正确设置。 需要注意的是,不同的屏幕型号和接口类型可能有不同的timing参数,需要根据具体情况进行修改。同时,修改设备树文件和源代码需要具备一定的编程经验和技能,建议在熟悉coreboot开发流程和相关技术后进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值