implement mptable generation

This is specially useful for machines lacking ACPI.

Signed-off-by: Sergio Lopez <slp@redhat.com>
diff --git a/Makefile b/Makefile
index b96dd09..ee707a4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
 obj-y = code16.o entry.o main.o string.o printf.o cstart.o fw_cfg.o
 obj-y += linuxboot.o malloc.o pflash.o tables.o hwsetup.o pci.o code32seg.o
+obj-y += mptable.o
 
 all-y = bios.bin
 all: $(all-y)
diff --git a/include/bios.h b/include/bios.h
index 1469cb6..f36638b 100644
--- a/include/bios.h
+++ b/include/bios.h
@@ -66,6 +66,7 @@
 extern void setup_pci(void);
 extern bool setup_hw(void);
 extern bool setup_mmconfig(void);
+extern void setup_mptable(void);
 extern void extract_acpi(void);
 extern void boot_from_fwcfg(void);
 
diff --git a/include/mpspec_def.h b/include/mpspec_def.h
new file mode 100644
index 0000000..6fb923a
--- /dev/null
+++ b/include/mpspec_def.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_MPSPEC_DEF_H
+#define _ASM_X86_MPSPEC_DEF_H
+
+/*
+ * Structure definitions for SMP machines following the
+ * Intel Multiprocessing Specification 1.1 and 1.4.
+ */
+
+/*
+ * This tag identifies where the SMP configuration
+ * information is.
+ */
+
+#define SMP_MAGIC_IDENT	(('_'<<24) | ('P'<<16) | ('M'<<8) | '_')
+
+#ifdef CONFIG_X86_32
+# define MAX_MPC_ENTRY 1024
+#endif
+
+/* Intel MP Floating Pointer Structure */
+struct mpf_intel {
+	char signature[4];		/* "_MP_"			*/
+	unsigned int physptr;		/* Configuration table address	*/
+	unsigned char length;		/* Our length (paragraphs)	*/
+	unsigned char specification;	/* Specification version	*/
+	unsigned char checksum;		/* Checksum (makes sum 0)	*/
+	unsigned char feature1;		/* Standard or configuration ?	*/
+	unsigned char feature2;		/* Bit7 set for IMCR|PIC	*/
+	unsigned char feature3;		/* Unused (0)			*/
+	unsigned char feature4;		/* Unused (0)			*/
+	unsigned char feature5;		/* Unused (0)			*/
+};
+
+#define MPC_SIGNATURE "PCMP"
+
+struct mpc_table {
+	char signature[4];
+	unsigned short length;		/* Size of table */
+	char spec;			/* 0x01 */
+	char checksum;
+	char oem[8];
+	char productid[12];
+	unsigned int oemptr;		/* 0 if not present */
+	unsigned short oemsize;		/* 0 if not present */
+	unsigned short oemcount;
+	unsigned int lapic;		/* APIC address */
+	unsigned int reserved;
+};
+
+/* Followed by entries */
+
+#define	MP_PROCESSOR		0
+#define	MP_BUS			1
+#define	MP_IOAPIC		2
+#define	MP_INTSRC		3
+#define	MP_LINTSRC		4
+/* Used by IBM NUMA-Q to describe node locality */
+#define	MP_TRANSLATION		192
+
+#define CPU_ENABLED		1	/* Processor is available */
+#define CPU_BOOTPROCESSOR	2	/* Processor is the boot CPU */
+
+#define CPU_STEPPING_MASK	0x000F
+#define CPU_MODEL_MASK		0x00F0
+#define CPU_FAMILY_MASK		0x0F00
+
+struct mpc_cpu {
+	unsigned char type;
+	unsigned char apicid;		/* Local APIC number */
+	unsigned char apicver;		/* Its versions */
+	unsigned char cpuflag;
+	unsigned int cpufeature;
+	unsigned int featureflag;	/* CPUID feature value */
+	unsigned int reserved[2];
+};
+
+struct mpc_bus {
+	unsigned char type;
+	unsigned char busid;
+	unsigned char bustype[6];
+};
+
+/* List of Bus Type string values, Intel MP Spec. */
+#define BUSTYPE_EISA	"EISA"
+#define BUSTYPE_ISA	"ISA"
+#define BUSTYPE_INTERN	"INTERN"	/* Internal BUS */
+#define BUSTYPE_MCA	"MCA"		/* Obsolete */
+#define BUSTYPE_VL	"VL"		/* Local bus */
+#define BUSTYPE_PCI	"PCI"
+#define BUSTYPE_PCMCIA	"PCMCIA"
+#define BUSTYPE_CBUS	"CBUS"
+#define BUSTYPE_CBUSII	"CBUSII"
+#define BUSTYPE_FUTURE	"FUTURE"
+#define BUSTYPE_MBI	"MBI"
+#define BUSTYPE_MBII	"MBII"
+#define BUSTYPE_MPI	"MPI"
+#define BUSTYPE_MPSA	"MPSA"
+#define BUSTYPE_NUBUS	"NUBUS"
+#define BUSTYPE_TC	"TC"
+#define BUSTYPE_VME	"VME"
+#define BUSTYPE_XPRESS	"XPRESS"
+
+#define MPC_APIC_USABLE		0x01
+
+struct mpc_ioapic {
+	unsigned char type;
+	unsigned char apicid;
+	unsigned char apicver;
+	unsigned char flags;
+	unsigned int apicaddr;
+};
+
+struct mpc_intsrc {
+	unsigned char type;
+	unsigned char irqtype;
+	unsigned short irqflag;
+	unsigned char srcbus;
+	unsigned char srcbusirq;
+	unsigned char dstapic;
+	unsigned char dstirq;
+};
+
+enum mp_irq_source_types {
+	mp_INT = 0,
+	mp_NMI = 1,
+	mp_SMI = 2,
+	mp_ExtINT = 3
+};
+
+#define MP_IRQPOL_DEFAULT	0x0
+#define MP_IRQPOL_ACTIVE_HIGH	0x1
+#define MP_IRQPOL_RESERVED	0x2
+#define MP_IRQPOL_ACTIVE_LOW	0x3
+#define MP_IRQPOL_MASK		0x3
+
+#define MP_IRQTRIG_DEFAULT	0x0
+#define MP_IRQTRIG_EDGE		0x4
+#define MP_IRQTRIG_RESERVED	0x8
+#define MP_IRQTRIG_LEVEL	0xc
+#define MP_IRQTRIG_MASK		0xc
+
+#define MP_APIC_ALL	0xFF
+
+struct mpc_lintsrc {
+	unsigned char type;
+	unsigned char irqtype;
+	unsigned short irqflag;
+	unsigned char srcbusid;
+	unsigned char srcbusirq;
+	unsigned char destapic;
+	unsigned char destapiclint;
+};
+
+#define MPC_OEM_SIGNATURE "_OEM"
+
+struct mpc_oemtable {
+	char signature[4];
+	unsigned short length;		/* Size of table */
+	char  rev;			/* 0x01 */
+	char  checksum;
+	char  mpc[8];
+};
+
+/*
+ *	Default configurations
+ *
+ *	1	2 CPU ISA 82489DX
+ *	2	2 CPU EISA 82489DX neither IRQ 0 timer nor IRQ 13 DMA chaining
+ *	3	2 CPU EISA 82489DX
+ *	4	2 CPU MCA 82489DX
+ *	5	2 CPU ISA+PCI
+ *	6	2 CPU EISA+PCI
+ *	7	2 CPU MCA+PCI
+ */
+
+enum mp_bustype {
+	MP_BUS_ISA = 1,
+	MP_BUS_EISA,
+	MP_BUS_PCI,
+};
+#endif /* _ASM_X86_MPSPEC_DEF_H */
diff --git a/main.c b/main.c
index b20a7de..2f6fc78 100644
--- a/main.c
+++ b/main.c
@@ -98,6 +98,7 @@
 	fw_cfg_setup();
 	extract_acpi();
 	extract_e820();
+	setup_mptable();
 	// extract_smbios();
 	boot_from_fwcfg();
 	panic();
diff --git a/mptable.c b/mptable.c
new file mode 100644
index 0000000..6d62cef
--- /dev/null
+++ b/mptable.c
@@ -0,0 +1,186 @@
+#include "include/string.h"
+#include "bios.h"
+#include "fw_cfg.h"
+#include "include/mpspec_def.h"
+
+#define MPTABLE_START    0x9fc00
+#define APIC_VERSION     0x14
+#define MPC_SPEC         0x4
+
+#define MP_IRQDIR_DEFAULT 0
+#define MP_IRQDIR_HIGH    1
+#define MP_IRQDIR_LOW     3
+
+static const char MPC_OEM[]        = "QBOOT   ";
+static const char MPC_PRODUCT_ID[] = "000000000000";
+static const char BUS_TYPE_ISA[]   = "ISA   ";
+
+#define IO_APIC_DEFAULT_PHYS_BASE 0xfec00000
+#define APIC_DEFAULT_PHYS_BASE    0xfee00000
+#define APIC_VERSION              0x14
+
+static int mptable_checksum(char *buf, int size)
+{
+	int i;
+	int sum = 0;
+
+	for (i = 0; i < size; i++) {
+		sum += buf[i];
+	}
+
+	return sum;
+}
+
+static void mptable_get_cpuid(int *signature, int *features)
+{
+	int ebx, ecx;
+
+	asm("cpuid"
+	    : "=a" (*signature), "=b" (ebx), "=c" (ecx), "=d" (*features)
+	    : "0" (1));
+}
+
+void setup_mptable(void)
+{
+	struct mpf_intel *mpf;
+	struct mpc_table *table;
+	struct mpc_cpu *cpu;
+	struct mpc_bus *bus;
+	struct mpc_ioapic *ioapic;
+	struct mpc_intsrc *intsrc;
+	struct mpc_lintsrc *lintsrc;
+	const char mpc_signature[] = MPC_SIGNATURE;
+	const char smp_magic_ident[] = "_MP_";
+	int cpuid_stepping, cpuid_features;
+	int irq0_override = 0;
+	int checksum = 0;
+	int offset = 0;
+	int num_cpus;
+	int ssize;
+	int i;
+
+	ssize = sizeof(struct mpf_intel);
+
+	mpf = (struct mpf_intel *) MPTABLE_START;
+	memset(mpf, 0, ssize);
+	memcpy(mpf->signature, smp_magic_ident, sizeof(smp_magic_ident) - 1);
+	mpf->length = 1;
+	mpf->specification = 4;
+	mpf->physptr = MPTABLE_START + ssize;
+	mpf->checksum -= mptable_checksum((char *) mpf, ssize);
+
+	offset += ssize;
+	ssize = sizeof(struct mpc_table);
+
+	table = (struct mpc_table *) (MPTABLE_START + offset);
+	memset(table, 0, ssize);
+	memcpy(table->signature, mpc_signature, sizeof(mpc_signature) - 1);
+	table->spec = MPC_SPEC;
+	memcpy(table->oem, MPC_OEM, sizeof(MPC_OEM) - 1);
+	memcpy(table->productid, MPC_PRODUCT_ID, sizeof(MPC_PRODUCT_ID) - 1);
+	table->lapic = APIC_DEFAULT_PHYS_BASE;
+
+	offset += ssize;
+	ssize = sizeof(struct mpc_cpu);
+
+	fw_cfg_select(FW_CFG_NB_CPUS);
+	num_cpus = fw_cfg_readl_le();
+	mptable_get_cpuid(&cpuid_stepping, &cpuid_features);
+
+	for (i = 0; i < num_cpus; i++) {
+		cpu = (struct mpc_cpu *) (MPTABLE_START + offset);
+		memset(cpu, 0, ssize);
+		cpu->type = MP_PROCESSOR;
+		cpu->apicid = i;
+		cpu->apicver = APIC_VERSION;
+		cpu->cpuflag = CPU_ENABLED;
+		if (i == 0) {
+			cpu->cpuflag |= CPU_BOOTPROCESSOR;
+		}
+		cpu->cpufeature = cpuid_stepping;
+		cpu->featureflag = cpuid_features;
+		checksum += mptable_checksum((char *) cpu, ssize);
+		offset += ssize;
+	}
+
+	ssize = sizeof(struct mpc_bus);
+
+	bus = (struct mpc_bus *) (MPTABLE_START + offset);
+	memset(bus, 0, ssize);
+	bus->type = MP_BUS;
+	bus->busid = 0;
+	memcpy(bus->bustype, BUS_TYPE_ISA, sizeof(BUS_TYPE_ISA) - 1);
+	checksum += mptable_checksum((char *) bus, ssize);
+
+	offset += ssize;
+	ssize = sizeof(struct mpc_ioapic);
+
+	ioapic = (struct mpc_ioapic *) (MPTABLE_START + offset);
+	memset(ioapic, 0, ssize);
+	ioapic->type = MP_IOAPIC;
+	ioapic->apicid = num_cpus + 1;
+	ioapic->apicver = APIC_VERSION;
+	ioapic->flags = MPC_APIC_USABLE;
+	ioapic->apicaddr = IO_APIC_DEFAULT_PHYS_BASE;
+	checksum += mptable_checksum((char *) ioapic, ssize);
+
+	offset += ssize;
+	ssize = sizeof(struct mpc_intsrc);
+
+	fw_cfg_select(FW_CFG_IRQ0_OVERRIDE);
+	irq0_override = fw_cfg_readl_le();
+
+	for (i = 0; i < 16; i++) {
+		intsrc = (struct mpc_intsrc *) (MPTABLE_START + offset);
+		memset(intsrc, 0, ssize);
+		intsrc->type = MP_INTSRC;
+		intsrc->irqtype = mp_INT;
+		intsrc->irqflag = MP_IRQDIR_DEFAULT;
+		intsrc->srcbus = 0;
+		intsrc->srcbusirq = i;
+		intsrc->dstapic = num_cpus + 1;
+		intsrc->dstirq = i;
+		if (irq0_override) {
+			if (i == 0) {
+				intsrc->dstirq = 2;
+			} else if (i == 2) {
+				// Don't update offset nor checksum
+				continue;
+			}
+		}
+		checksum += mptable_checksum((char *) intsrc, ssize);
+		offset += ssize;
+	}
+
+	ssize = sizeof(struct mpc_lintsrc);
+
+	lintsrc = (struct mpc_lintsrc *) (MPTABLE_START + offset);
+	memset(lintsrc, 0, ssize);
+	lintsrc->type = MP_LINTSRC;
+	lintsrc->irqtype = mp_ExtINT;
+	lintsrc->irqflag = MP_IRQDIR_DEFAULT;
+	lintsrc->srcbusid = 0;
+	lintsrc->srcbusirq = 0;
+	lintsrc->destapic = 0;
+	lintsrc->destapiclint = 0;
+	checksum += mptable_checksum((char *) lintsrc, ssize);
+
+	offset += ssize;
+
+	lintsrc = (struct mpc_lintsrc *) (MPTABLE_START + offset);
+	lintsrc->type = MP_LINTSRC;
+	lintsrc->irqtype = mp_NMI;
+	lintsrc->irqflag = MP_IRQDIR_DEFAULT;
+	lintsrc->srcbusid = 0;
+	lintsrc->srcbusirq = 0;
+	lintsrc->destapic = 0xFF;
+	lintsrc->destapiclint = 1;
+	checksum += mptable_checksum((char *) lintsrc, ssize);
+
+	offset += ssize;
+	ssize = sizeof(struct mpc_table);
+
+	table->length = offset - sizeof(struct mpf_intel);
+	checksum += mptable_checksum((char *) table, ssize);
+	table->checksum -= checksum;
+}