diff mbox

[U-Boot,v2,08/20] x86: Add support for the Simple Firmware Interface (SFI)

Message ID 1430274322-14383-9-git-send-email-sjg@chromium.org
State Accepted
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass April 29, 2015, 2:25 a.m. UTC
This provides a way of passing information to Linux without requiring the
full ACPI horror. Provide a rudimentary implementation sufficient to be
recognised and parsed by Linux.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

Changes in v2:
- Rename CONFIG_SFI to CONFIG_GENERATE_SFI_TABLE and move within Kconfig
- Correct Kconfig help indentation and text
- Drop SFI_BASE config option
- Always build sfi.o
- Use SFI_TABLE_MAX_ENTRIES instead of 16 and ARRAY_SIZE()
- Make get_entry_start() static
- Use table_compute_checksum() to computer checksum
- Add a few blank lines
- Move patch to after the CPU uclass patch
- Drop the RTC table as it is not needed
- Move SFI calling code to write_tables()
- Remove IDLE table
- Remove SFI_SYST_SEARCH_BEGIN and SFI_SYST_SEARCH_END
- Move '__packed' to immediately after 'struct'
- Add SFI_DEV_TYPE_SD and convert to enum
- Remove #ifdef CONFIG_SFI from hedaer file
- Move sfi.h header file to arch/x86/include/asm

 arch/x86/Kconfig           |  14 +++++
 arch/x86/include/asm/sfi.h | 137 ++++++++++++++++++++++++++++++++++++++++
 arch/x86/lib/Makefile      |   1 +
 arch/x86/lib/sfi.c         | 154 +++++++++++++++++++++++++++++++++++++++++++++
 arch/x86/lib/tables.c      |   5 ++
 5 files changed, 311 insertions(+)
 create mode 100644 arch/x86/include/asm/sfi.h
 create mode 100644 arch/x86/lib/sfi.c

Comments

Bin Meng April 29, 2015, 5:16 a.m. UTC | #1
Hi Simon,

On Wed, Apr 29, 2015 at 10:25 AM, Simon Glass <sjg@chromium.org> wrote:
> This provides a way of passing information to Linux without requiring the
> full ACPI horror. Provide a rudimentary implementation sufficient to be
> recognised and parsed by Linux.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---

Looks good, thanks!

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

But some nits below :)

>
> Changes in v2:
> - Rename CONFIG_SFI to CONFIG_GENERATE_SFI_TABLE and move within Kconfig
> - Correct Kconfig help indentation and text
> - Drop SFI_BASE config option
> - Always build sfi.o
> - Use SFI_TABLE_MAX_ENTRIES instead of 16 and ARRAY_SIZE()
> - Make get_entry_start() static
> - Use table_compute_checksum() to computer checksum
> - Add a few blank lines
> - Move patch to after the CPU uclass patch
> - Drop the RTC table as it is not needed
> - Move SFI calling code to write_tables()
> - Remove IDLE table
> - Remove SFI_SYST_SEARCH_BEGIN and SFI_SYST_SEARCH_END
> - Move '__packed' to immediately after 'struct'
> - Add SFI_DEV_TYPE_SD and convert to enum
> - Remove #ifdef CONFIG_SFI from hedaer file
> - Move sfi.h header file to arch/x86/include/asm
>
>  arch/x86/Kconfig           |  14 +++++
>  arch/x86/include/asm/sfi.h | 137 ++++++++++++++++++++++++++++++++++++++++
>  arch/x86/lib/Makefile      |   1 +
>  arch/x86/lib/sfi.c         | 154 +++++++++++++++++++++++++++++++++++++++++++++
>  arch/x86/lib/tables.c      |   5 ++
>  5 files changed, 311 insertions(+)
>  create mode 100644 arch/x86/include/asm/sfi.h
>  create mode 100644 arch/x86/lib/sfi.c
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index f3a600e..f38e9ba 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -393,6 +393,20 @@ config GENERATE_PIRQ_TABLE
>           It specifies the interrupt router information as well how all the PCI
>           devices' interrupt pins are wired to PIRQs.
>
> +config GENERATE_SFI_TABLE
> +       bool "SFI (Simple Firmware Interface) Support"

Should we say: Generate an SFI (Simple Firmware Interface) table? This
is to match 'Generate a PIRQ table'.

> +       help
> +         The Simple Firmware Interface (SFI) provides a lightweight method
> +         for platform firmware to pass information to the operating system
> +         via static tables in memory.  Kernel SFI support is required to
> +         boot on SFI-only platforms.  If you have ACPI tables then these are
> +         used instead.
> +
> +         U-Boot writes this table in sfi_write_tables() just before booting

Should be: write_sfi_table

> +         the OS.
> +
> +         For more information, see http://simplefirmware.org
> +
>  endmenu
>

[snip]

Regards,
Bin
Simon Glass April 30, 2015, 4:22 a.m. UTC | #2
On 28 April 2015 at 23:16, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Wed, Apr 29, 2015 at 10:25 AM, Simon Glass <sjg@chromium.org> wrote:
>> This provides a way of passing information to Linux without requiring the
>> full ACPI horror. Provide a rudimentary implementation sufficient to be
>> recognised and parsed by Linux.
>>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>
> Looks good, thanks!
>
> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

Applied to u-boot-x86.

(fixed nits)

>
> But some nits below :)
>
>>
>> Changes in v2:
>> - Rename CONFIG_SFI to CONFIG_GENERATE_SFI_TABLE and move within Kconfig
>> - Correct Kconfig help indentation and text
>> - Drop SFI_BASE config option
>> - Always build sfi.o
>> - Use SFI_TABLE_MAX_ENTRIES instead of 16 and ARRAY_SIZE()
>> - Make get_entry_start() static
>> - Use table_compute_checksum() to computer checksum
>> - Add a few blank lines
>> - Move patch to after the CPU uclass patch
>> - Drop the RTC table as it is not needed
>> - Move SFI calling code to write_tables()
>> - Remove IDLE table
>> - Remove SFI_SYST_SEARCH_BEGIN and SFI_SYST_SEARCH_END
>> - Move '__packed' to immediately after 'struct'
>> - Add SFI_DEV_TYPE_SD and convert to enum
>> - Remove #ifdef CONFIG_SFI from hedaer file
>> - Move sfi.h header file to arch/x86/include/asm
>>
>>  arch/x86/Kconfig           |  14 +++++
>>  arch/x86/include/asm/sfi.h | 137 ++++++++++++++++++++++++++++++++++++++++
>>  arch/x86/lib/Makefile      |   1 +
>>  arch/x86/lib/sfi.c         | 154 +++++++++++++++++++++++++++++++++++++++++++++
>>  arch/x86/lib/tables.c      |   5 ++
>>  5 files changed, 311 insertions(+)
>>  create mode 100644 arch/x86/include/asm/sfi.h
>>  create mode 100644 arch/x86/lib/sfi.c
>>
>> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>> index f3a600e..f38e9ba 100644
>> --- a/arch/x86/Kconfig
>> +++ b/arch/x86/Kconfig
>> @@ -393,6 +393,20 @@ config GENERATE_PIRQ_TABLE
>>           It specifies the interrupt router information as well how all the PCI
>>           devices' interrupt pins are wired to PIRQs.
>>
>> +config GENERATE_SFI_TABLE
>> +       bool "SFI (Simple Firmware Interface) Support"
>
> Should we say: Generate an SFI (Simple Firmware Interface) table? This
> is to match 'Generate a PIRQ table'.
>
>> +       help
>> +         The Simple Firmware Interface (SFI) provides a lightweight method
>> +         for platform firmware to pass information to the operating system
>> +         via static tables in memory.  Kernel SFI support is required to
>> +         boot on SFI-only platforms.  If you have ACPI tables then these are
>> +         used instead.
>> +
>> +         U-Boot writes this table in sfi_write_tables() just before booting
>
> Should be: write_sfi_table
>
>> +         the OS.
>> +
>> +         For more information, see http://simplefirmware.org
>> +
>>  endmenu
>>
>
> [snip]
>
> Regards,
> Bin
diff mbox

Patch

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f3a600e..f38e9ba 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -393,6 +393,20 @@  config GENERATE_PIRQ_TABLE
 	  It specifies the interrupt router information as well how all the PCI
 	  devices' interrupt pins are wired to PIRQs.
 
+config GENERATE_SFI_TABLE
+	bool "SFI (Simple Firmware Interface) Support"
+	help
+	  The Simple Firmware Interface (SFI) provides a lightweight method
+	  for platform firmware to pass information to the operating system
+	  via static tables in memory.  Kernel SFI support is required to
+	  boot on SFI-only platforms.  If you have ACPI tables then these are
+	  used instead.
+
+	  U-Boot writes this table in sfi_write_tables() just before booting
+	  the OS.
+
+	  For more information, see http://simplefirmware.org
+
 endmenu
 
 config MAX_PIRQ_LINKS
diff --git a/arch/x86/include/asm/sfi.h b/arch/x86/include/asm/sfi.h
new file mode 100644
index 0000000..d1f0f0c
--- /dev/null
+++ b/arch/x86/include/asm/sfi.h
@@ -0,0 +1,137 @@ 
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+	BSD-3-Clause
+ */
+
+#ifndef _LINUX_SFI_H
+#define _LINUX_SFI_H
+
+#include <errno.h>
+#include <linux/types.h>
+
+/* Table signatures reserved by the SFI specification */
+#define SFI_SIG_SYST		"SYST"
+#define SFI_SIG_FREQ		"FREQ"
+#define SFI_SIG_CPUS		"CPUS"
+#define SFI_SIG_MTMR		"MTMR"
+#define SFI_SIG_MRTC		"MRTC"
+#define SFI_SIG_MMAP		"MMAP"
+#define SFI_SIG_APIC		"APIC"
+#define SFI_SIG_XSDT		"XSDT"
+#define SFI_SIG_WAKE		"WAKE"
+#define SFI_SIG_DEVS		"DEVS"
+#define SFI_SIG_GPIO		"GPIO"
+
+#define SFI_SIGNATURE_SIZE	4
+#define SFI_OEM_ID_SIZE		6
+#define SFI_OEM_TABLE_ID_SIZE	8
+
+#define SFI_NAME_LEN		16
+#define SFI_TABLE_MAX_ENTRIES	16
+
+#define SFI_GET_NUM_ENTRIES(ptable, entry_type) \
+	((ptable->header.len - sizeof(struct sfi_table_header)) / \
+	(sizeof(entry_type)))
+/*
+ * Table structures must be byte-packed to match the SFI specification,
+ * as they are provided by the BIOS.
+ */
+struct __packed sfi_table_header {
+	char	sig[SFI_SIGNATURE_SIZE];
+	u32	len;
+	u8	rev;
+	u8	csum;
+	char	oem_id[SFI_OEM_ID_SIZE];
+	char	oem_table_id[SFI_OEM_TABLE_ID_SIZE];
+};
+
+struct __packed sfi_table_simple {
+	struct sfi_table_header		header;
+	u64				pentry[1];
+};
+
+/* Comply with UEFI spec 2.1 */
+struct __packed sfi_mem_entry {
+	u32	type;
+	u64	phys_start;
+	u64	virt_start;
+	u64	pages;
+	u64	attrib;
+};
+
+struct __packed sfi_cpu_table_entry {
+	u32	apic_id;
+};
+
+struct __packed sfi_cstate_table_entry {
+	u32	hint;		/* MWAIT hint */
+	u32	latency;	/* latency in ms */
+};
+
+struct __packed sfi_apic_table_entry {
+	u64	phys_addr;	/* phy base addr for APIC reg */
+};
+
+struct __packed sfi_freq_table_entry {
+	u32	freq_mhz;	/* in MHZ */
+	u32	latency;	/* transition latency in ms */
+	u32	ctrl_val;	/* value to write to PERF_CTL */
+};
+
+struct __packed sfi_wake_table_entry {
+	u64	phys_addr;	/* pointer to where the wake vector locates */
+};
+
+struct __packed sfi_timer_table_entry {
+	u64	phys_addr;	/* phy base addr for the timer */
+	u32	freq_hz;	/* in HZ */
+	u32	irq;
+};
+
+struct __packed sfi_rtc_table_entry {
+	u64	phys_addr;	/* phy base addr for the RTC */
+	u32	irq;
+};
+
+struct __packed sfi_device_table_entry {
+	u8	type;		/* bus type, I2C, SPI or ...*/
+	u8	host_num;	/* attached to host 0, 1...*/
+	u16	addr;
+	u8	irq;
+	u32	max_freq;
+	char	name[SFI_NAME_LEN];
+};
+
+enum {
+	SFI_DEV_TYPE_SPI	= 0,
+	SFI_DEV_TYPE_I2C,
+	SFI_DEV_TYPE_UART,
+	SFI_DEV_TYPE_HSI,
+	SFI_DEV_TYPE_IPC,
+	SFI_DEV_TYPE_SD,
+};
+
+struct __packed sfi_gpio_table_entry {
+	char	controller_name[SFI_NAME_LEN];
+	u16	pin_no;
+	char	pin_name[SFI_NAME_LEN];
+};
+
+struct sfi_xsdt_header {
+	uint32_t oem_revision;
+	uint32_t creator_id;
+	uint32_t creator_revision;
+};
+
+typedef int (*sfi_table_handler) (struct sfi_table_header *table);
+
+/**
+ * write_sfi_table() - Write Simple Firmware Interface tables
+ *
+ * @base:	Address to write table to
+ * @return address to use for the next table
+ */
+u32 write_sfi_table(u32 base);
+
+#endif /*_LINUX_SFI_H */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 0178fe1..70ad19b 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -26,6 +26,7 @@  obj-y	+= pirq_routing.o
 obj-y	+= relocate.o
 obj-y += physmem.o
 obj-$(CONFIG_X86_RAMTEST) += ramtest.o
+obj-y += sfi.o
 obj-y	+= string.o
 obj-y	+= tables.o
 obj-$(CONFIG_SYS_X86_TSC_TIMER)	+= tsc_timer.o
diff --git a/arch/x86/lib/sfi.c b/arch/x86/lib/sfi.c
new file mode 100644
index 0000000..3d36580
--- /dev/null
+++ b/arch/x86/lib/sfi.c
@@ -0,0 +1,154 @@ 
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/*
+ * Intel Simple Firmware Interface (SFI)
+ *
+ * Yet another way to pass information to the Linux kernel.
+ *
+ * See https://simplefirmware.org/ for details
+ */
+
+#include <common.h>
+#include <cpu.h>
+#include <dm.h>
+#include <asm/cpu.h>
+#include <asm/ioapic.h>
+#include <asm/sfi.h>
+#include <asm/tables.h>
+#include <dm/uclass-internal.h>
+
+struct table_info {
+	u32 base;
+	int ptr;
+	u32 entry_start;
+	u64 table[SFI_TABLE_MAX_ENTRIES];
+	int count;
+};
+
+static void *get_entry_start(struct table_info *tab)
+{
+	if (tab->count == SFI_TABLE_MAX_ENTRIES)
+		return NULL;
+	tab->entry_start = tab->base + tab->ptr;
+	tab->table[tab->count] = tab->entry_start;
+	tab->entry_start += sizeof(struct sfi_table_header);
+
+	return (void *)tab->entry_start;
+}
+
+static void finish_table(struct table_info *tab, const char *sig, void *entry)
+{
+	struct sfi_table_header *hdr;
+
+	hdr = (struct sfi_table_header *)(tab->base + tab->ptr);
+	strcpy(hdr->sig, sig);
+	hdr->len = sizeof(*hdr) + ((ulong)entry - tab->entry_start);
+	hdr->rev = 1;
+	strncpy(hdr->oem_id, "U-Boot", SFI_OEM_ID_SIZE);
+	strncpy(hdr->oem_table_id, "Table v1", SFI_OEM_TABLE_ID_SIZE);
+	hdr->csum = 0;
+	hdr->csum = table_compute_checksum(hdr, hdr->len);
+	tab->ptr += hdr->len;
+	tab->ptr = ALIGN(tab->ptr, 16);
+	tab->count++;
+}
+
+static int sfi_write_system_header(struct table_info *tab)
+{
+	u64 *entry = get_entry_start(tab);
+	int i;
+
+	if (!entry)
+		return -ENOSPC;
+
+	for (i = 0; i < tab->count; i++)
+		*entry++ = tab->table[i];
+	finish_table(tab, SFI_SIG_SYST, entry);
+
+	return 0;
+}
+
+static int sfi_write_cpus(struct table_info *tab)
+{
+	struct sfi_cpu_table_entry *entry = get_entry_start(tab);
+	struct udevice *dev;
+	int count = 0;
+
+	if (!entry)
+		return -ENOSPC;
+
+	for (uclass_find_first_device(UCLASS_CPU, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		struct cpu_platdata *plat = dev_get_parent_platdata(dev);
+
+		if (!device_active(dev))
+			continue;
+		entry->apic_id = plat->cpu_id;
+		entry++;
+		count++;
+	}
+
+	/* Omit the table if there is only one CPU */
+	if (count > 1)
+		finish_table(tab, SFI_SIG_CPUS, entry);
+
+	return 0;
+}
+
+static int sfi_write_apic(struct table_info *tab)
+{
+	struct sfi_apic_table_entry *entry = get_entry_start(tab);
+
+	if (!entry)
+		return -ENOSPC;
+
+	entry->phys_addr = IO_APIC_ADDR;
+	entry++;
+	finish_table(tab, SFI_SIG_APIC, entry);
+
+	return 0;
+}
+
+static int sfi_write_xsdt(struct table_info *tab)
+{
+	struct sfi_xsdt_header *entry = get_entry_start(tab);
+
+	if (!entry)
+		return -ENOSPC;
+
+	entry->oem_revision = 1;
+	entry->creator_id = 1;
+	entry->creator_revision = 1;
+	entry++;
+	finish_table(tab, SFI_SIG_XSDT, entry);
+
+	return 0;
+}
+
+u32 write_sfi_table(u32 base)
+{
+	struct table_info table;
+
+	table.base = base;
+	table.ptr = 0;
+	table.count = 0;
+	sfi_write_cpus(&table);
+	sfi_write_apic(&table);
+
+	/*
+	 * The SFI specification marks the XSDT table as option, but Linux 4.0
+	 * crashes on start-up when it is not provided.
+	 */
+	sfi_write_xsdt(&table);
+
+	/* Finally, write out the system header which points to the others */
+	sfi_write_system_header(&table);
+
+	return base + table.ptr;
+}
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index 0836e1e..8031201 100644
--- a/arch/x86/lib/tables.c
+++ b/arch/x86/lib/tables.c
@@ -5,6 +5,7 @@ 
  */
 
 #include <common.h>
+#include <asm/sfi.h>
 #include <asm/tables.h>
 
 u8 table_compute_checksum(void *v, int len)
@@ -27,4 +28,8 @@  void write_tables(void)
 	rom_table_end = write_pirq_routing_table(rom_table_end);
 	rom_table_end = ALIGN(rom_table_end, 1024);
 #endif
+#ifdef CONFIG_GENERATE_SFI_TABLE
+	rom_table_end = write_sfi_table(rom_table_end);
+	rom_table_end = ALIGN(rom_table_end, 1024);
+#endif
 }