Commit 07545d861ccc54aecbeaa51b264258b90912b856

Authored by Bin Meng
Committed by Simon Glass
1 parent 7f5df8d42d

x86: Generate a valid MultiProcessor (MP) table

Implement write_mp_table() to create a minimal working MP table.
This includes an MP floating table, a configuration table header
and all of the 5 base configuration table entries. The I/O interrupt
assignment table entry is created based on the same information used
in the creation of PIRQ routing table from device tree. A check
duplicated entry logic is applied to prevent writing multiple I/O
interrupt entries with the same information.

Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
need actually write the MP table at the F seg, just like we did for
PIRQ routing and SFI tables. With MP table existence, linux kernel
will switch to I/O APIC and local APIC to process all the peripheral
interrupts instead of 8259 PICs. This takes full advantage of the
multicore hardware and the SMP kernel.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Acked-by: Simon Glass <sjg@chromium.org>

Showing 4 changed files with 181 additions and 0 deletions Side-by-side Diff

... ... @@ -313,6 +313,15 @@
313 313  
314 314 For more information, see http://simplefirmware.org
315 315  
  316 +config GENERATE_MP_TABLE
  317 + bool "Generate an MP (Multi-Processor) table"
  318 + default n
  319 + help
  320 + Generate an MP (Multi-Processor) table for this board. The MP table
  321 + provides a way for the operating system to support for symmetric
  322 + multiprocessing as well as symmetric I/O interrupt handling with
  323 + the local APIC and I/O APIC.
  324 +
316 325 endmenu
317 326  
318 327 config MAX_PIRQ_LINKS
arch/x86/include/asm/mpspec.h
... ... @@ -431,5 +431,15 @@
431 431 */
432 432 u32 mptable_finalize(struct mp_config_table *mc);
433 433  
  434 +/**
  435 + * write_mp_table() - Write MP table
  436 + *
  437 + * This writes MP table at a given address.
  438 + *
  439 + * @addr: start address to write MP table
  440 + * @return: end address of MP table
  441 + */
  442 +u32 write_mp_table(u32 addr);
  443 +
434 444 #endif /* __ASM_MPSPEC_H */
arch/x86/lib/mpspec.c
... ... @@ -9,13 +9,18 @@
9 9 #include <common.h>
10 10 #include <cpu.h>
11 11 #include <dm.h>
  12 +#include <errno.h>
  13 +#include <fdtdec.h>
12 14 #include <asm/cpu.h>
  15 +#include <asm/irq.h>
13 16 #include <asm/ioapic.h>
14 17 #include <asm/lapic.h>
15 18 #include <asm/mpspec.h>
16 19 #include <asm/tables.h>
17 20 #include <dm/uclass-internal.h>
18 21  
  22 +DECLARE_GLOBAL_DATA_PTR;
  23 +
19 24 struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
20 25 {
21 26 u32 mc;
... ... @@ -220,6 +225,158 @@
220 225 end = mp_next_mpe_entry(mc);
221 226  
222 227 debug("Write the MP table at: %x - %x\n", (u32)mc, end);
  228 +
  229 + return end;
  230 +}
  231 +
  232 +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
  233 + int apicid, int external_int2)
  234 +{
  235 + int i;
  236 +
  237 + mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
  238 + MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
  239 + bus_isa, 0, apicid, 0);
  240 + mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
  241 + bus_isa, 1, apicid, 1);
  242 + mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
  243 + MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
  244 + bus_isa, 0, apicid, 2);
  245 +
  246 + for (i = 3; i < 16; i++)
  247 + mp_write_intsrc(mc, MP_INT,
  248 + MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
  249 + bus_isa, i, apicid, i);
  250 +}
  251 +
  252 +/*
  253 + * Check duplicated I/O interrupt assignment table entry, to make sure
  254 + * there is only one entry with the given bus, device and interrupt pin.
  255 + */
  256 +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
  257 + int entry_num, int bus, int device, int pin)
  258 +{
  259 + struct mpc_config_intsrc *intsrc = intsrc_base;
  260 + int i;
  261 +
  262 + for (i = 0; i < entry_num; i++) {
  263 + if (intsrc->mpc_srcbus == bus &&
  264 + intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
  265 + break;
  266 + intsrc++;
  267 + }
  268 +
  269 + return (i == entry_num) ? false : true;
  270 +}
  271 +
  272 +static int mptable_add_intsrc(struct mp_config_table *mc,
  273 + int bus_isa, int apicid)
  274 +{
  275 + struct mpc_config_intsrc *intsrc_base;
  276 + int intsrc_entries = 0;
  277 + const void *blob = gd->fdt_blob;
  278 + int node;
  279 + int len, count;
  280 + const u32 *cell;
  281 + int i;
  282 +
  283 + /* Legacy Interrupts */
  284 + debug("Writing ISA IRQs\n");
  285 + mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
  286 +
  287 + /* Get I/O interrupt information from device tree */
  288 + node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
  289 + if (node < 0) {
  290 + debug("%s: Cannot find irq router node\n", __func__);
  291 + return -ENOENT;
  292 + }
  293 +
  294 + cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
  295 + if (!cell)
  296 + return -ENOENT;
  297 +
  298 + if ((len % sizeof(struct pirq_routing)) == 0)
  299 + count = len / sizeof(struct pirq_routing);
  300 + else
  301 + return -EINVAL;
  302 +
  303 + intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
  304 +
  305 + for (i = 0; i < count; i++) {
  306 + struct pirq_routing pr;
  307 +
  308 + pr.bdf = fdt_addr_to_cpu(cell[0]);
  309 + pr.pin = fdt_addr_to_cpu(cell[1]);
  310 + pr.pirq = fdt_addr_to_cpu(cell[2]);
  311 +
  312 + if (check_dup_entry(intsrc_base, intsrc_entries,
  313 + PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
  314 + debug("found entry for bus %d device %d INT%c, skipping\n",
  315 + PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
  316 + 'A' + pr.pin - 1);
  317 + cell += sizeof(struct pirq_routing) / sizeof(u32);
  318 + continue;
  319 + }
  320 +
  321 + /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
  322 + mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
  323 + PCI_DEV(pr.bdf), pr.pin, apicid,
  324 + pr.pirq + 16);
  325 + intsrc_entries++;
  326 + cell += sizeof(struct pirq_routing) / sizeof(u32);
  327 + }
  328 +
  329 + return 0;
  330 +}
  331 +
  332 +static void mptable_add_lintsrc(struct mp_config_table *mc, int bus_isa)
  333 +{
  334 + mp_write_lintsrc(mc, MP_EXTINT,
  335 + MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
  336 + bus_isa, 0, MP_APIC_ALL, 0);
  337 + mp_write_lintsrc(mc, MP_NMI,
  338 + MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
  339 + bus_isa, 0, MP_APIC_ALL, 1);
  340 +}
  341 +
  342 +u32 write_mp_table(u32 addr)
  343 +{
  344 + struct mp_config_table *mc;
  345 + int ioapic_id, ioapic_ver;
  346 + int bus_isa = 0xff;
  347 + int ret;
  348 + u32 end;
  349 +
  350 + /* 16 byte align the table address */
  351 + addr = ALIGN(addr, 16);
  352 +
  353 + /* Write floating table */
  354 + mc = mp_write_floating_table((struct mp_floating_table *)addr);
  355 +
  356 + /* Write configuration table header */
  357 + mp_config_table_init(mc);
  358 +
  359 + /* Write processor entry */
  360 + mp_write_processor(mc);
  361 +
  362 + /* Write bus entry */
  363 + mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
  364 +
  365 + /* Write I/O APIC entry */
  366 + ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
  367 + ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
  368 + mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
  369 +
  370 + /* Write I/O interrupt assignment entry */
  371 + ret = mptable_add_intsrc(mc, bus_isa, ioapic_id);
  372 + if (ret)
  373 + debug("Failed to write I/O interrupt assignment table\n");
  374 +
  375 + /* Write local interrupt assignment entry */
  376 + mptable_add_lintsrc(mc, bus_isa);
  377 +
  378 + /* Finalize the MP table */
  379 + end = mptable_finalize(mc);
223 380  
224 381 return end;
225 382 }
arch/x86/lib/tables.c
... ... @@ -6,6 +6,7 @@
6 6  
7 7 #include <common.h>
8 8 #include <asm/sfi.h>
  9 +#include <asm/mpspec.h>
9 10 #include <asm/tables.h>
10 11  
11 12 u8 table_compute_checksum(void *v, int len)
... ... @@ -44,6 +45,10 @@
44 45 #endif
45 46 #ifdef CONFIG_GENERATE_SFI_TABLE
46 47 rom_table_end = write_sfi_table(rom_table_end);
  48 + rom_table_end = ALIGN(rom_table_end, 1024);
  49 +#endif
  50 +#ifdef CONFIG_GENERATE_MP_TABLE
  51 + rom_table_end = write_mp_table(rom_table_end);
47 52 rom_table_end = ALIGN(rom_table_end, 1024);
48 53 #endif
49 54 }