From patchwork Mon Apr 27 22:48:10 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 465274 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id BE90E1400DE for ; Tue, 28 Apr 2015 08:50:18 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 152EB4BBF0; Tue, 28 Apr 2015 00:49:58 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id sAzpsrWEY_V8; Tue, 28 Apr 2015 00:49:57 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 19C644BBFD; Tue, 28 Apr 2015 00:49:29 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id DACED4BBA0 for ; Tue, 28 Apr 2015 00:49:13 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Rd1Ht3HRuwNb for ; Tue, 28 Apr 2015 00:49:13 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-qg0-f74.google.com (mail-qg0-f74.google.com [209.85.192.74]) by theia.denx.de (Postfix) with ESMTPS id 4D2524BBB1 for ; Tue, 28 Apr 2015 00:49:09 +0200 (CEST) Received: by qgdz60 with SMTP id z60so3888235qgd.0 for ; Mon, 27 Apr 2015 15:49:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=SEDJTRm4qX6dQej2pjB1M++Jv1zuDJ+PBAfEqQQDtdI=; b=FIO/8fliPzO/RYNAwljwHp7L/F8OACNEP2VIwPehVpivI7SwP7Mj3QU1ap7wuRdZZC A8nfuHi4z8c+Th/WKPeujYctypyxHlkl5AtlyqKIieLKCerqAWPBoOdYTD5oG9QFRZb5 Y4IVe+bkk5hCzG6h2D6Wt4sOWydVFwK5Pdoq7+HACpX4dxT48ftlz2dtZhnu5QnxJQO7 az6Oaq1KmXGoi24oyP4ci2Hpa9Uv93Eds6yHG1Sipk2bfJoJkhVh8F5J2dcsaFcaB5dX zAnb2mOxnPds8/B40SXDXNx3qT0kk+hULZRYX6v7isyAQyE4RrZWHCjN3+Ng1cx9XCms y9Ag== X-Gm-Message-State: ALoCoQm2+163l/F95GnzcZ5zDl10J0Q4AX/o3uXlbLckc/75ZQmLQi8+Jcq3so8bYwcAxD6Vb2FL X-Received: by 10.52.30.129 with SMTP id s1mr23773370vdh.3.1430174948367; Mon, 27 Apr 2015 15:49:08 -0700 (PDT) Received: from corpmail-nozzle1-2.hot.corp.google.com ([100.108.1.103]) by gmr-mx.google.com with ESMTPS id l36si1189024yhb.1.2015.04.27.15.49.07 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 27 Apr 2015 15:49:08 -0700 (PDT) Received: from kaki.bld.corp.google.com ([172.29.216.32]) by corpmail-nozzle1-2.hot.corp.google.com with ESMTP id 6TkEEgGI.1; Mon, 27 Apr 2015 15:49:08 -0700 Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 6A82022101F; Mon, 27 Apr 2015 16:49:07 -0600 (MDT) From: Simon Glass To: U-Boot Mailing List Date: Mon, 27 Apr 2015 16:48:10 -0600 Message-Id: <1430174911-27538-8-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.2.0.rc0.207.ga3a616c In-Reply-To: <1430174911-27538-1-git-send-email-sjg@chromium.org> References: <1430174911-27538-1-git-send-email-sjg@chromium.org> Cc: Tom Rini , Graeme Russ , Thomas Beaman , Masahiro Yamada Subject: [U-Boot] [PATCH 07/20] x86: Add support for the Simple Firmware Interface (SFI) X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 --- arch/x86/Kconfig | 28 +++++++++ arch/x86/lib/Makefile | 1 + arch/x86/lib/sfi.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/lib/zimage.c | 7 +++ include/linux/sfi.h | 139 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 346 insertions(+) create mode 100644 arch/x86/lib/sfi.c create mode 100644 include/linux/sfi.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index aaceaef..30a08ec 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -499,6 +499,34 @@ config PCIE_ECAM_BASE assigned to PCI devices - i.e. the memory and prefetch regions, as passed to pci_set_region(). +config SFI + 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. + + For more information, see http://simplefirmware.org + + Say 'Y' here to enable the kernel to boot properly on SFI-only + platforms. + +config SFI_BASE + hex "SFI base address (0xe0000 to 0xff000)" + default 0xe0000 + depends on SFI + help + The OS searches addresses in the range 0xe00000 to 0xffff0 for the + Simple Firmware Interface (SFI) header. Use this option to determine + where the table will be placed. It must be a multiple of 16 bytes and + the header part (which U-Boot places at the end) must not cross a 4KB + boundary. A 4KB-aligned address is recommended for these reasons. + + U-Boot writes this table in sfi_write_tables() just before booting + the OS. + config BOOTSTAGE default y diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 0178fe1..c55b9be 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-$(CONFIG_SFI) += 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..060651b --- /dev/null +++ b/arch/x86/lib/sfi.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * 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 +#include +#include +#include +#include +#include +#include + +struct table_info { + u32 base; + int ptr; + u32 entry_start; + u64 table[16]; + int count; +}; + +void *get_entry_start(struct table_info *tab) +{ + if (tab->count == ARRAY_SIZE(tab->table)) + 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; + uint8_t *p; + int sum; + + 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; + for (sum = 0, p = (uint8_t *)hdr; (void *)p < entry; p++) + sum += *p; + hdr->csum = 256 - (sum & 255); + 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_rtc(struct table_info *tab) +{ + struct sfi_rtc_table_entry *entry = get_entry_start(tab); + + if (!entry) + return -ENOSPC; + + entry->phys_addr = CONFIG_SYS_ISA_IO_BASE_ADDRESS + 0x70; + entry->irq = 0; /* Should be 8? */ + entry++; + finish_table(tab, SFI_SIG_MRTC, 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; +} + +int sfi_write_tables(void) +{ + struct table_info table; + + table.base = CONFIG_SFI_BASE; + table.ptr = 0; + table.count = 0; + sfi_write_cpus(&table); + sfi_write_apic(&table); + sfi_write_rtc(&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 0; +} diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c index c3f8a73..d2c68f6 100644 --- a/arch/x86/lib/zimage.c +++ b/arch/x86/lib/zimage.c @@ -24,6 +24,7 @@ #include #endif #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -232,9 +233,15 @@ int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot, { struct setup_header *hdr = &setup_base->hdr; int bootproto = get_boot_protocol(hdr); + int ret; setup_base->e820_entries = install_e820_map( ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map); + ret = sfi_write_tables(); + if (ret && ret != -ENOSYS) { + printf("Failed to write SFI tables: err=%d\n", ret); + return ret; + } if (bootproto == 0x0100) { setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC; diff --git a/include/linux/sfi.h b/include/linux/sfi.h new file mode 100644 index 0000000..4094fc7 --- /dev/null +++ b/include/linux/sfi.h @@ -0,0 +1,139 @@ +/* + * 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 +#include + +/* Table signatures reserved by the SFI specification */ +#define SFI_SIG_SYST "SYST" +#define SFI_SIG_FREQ "FREQ" +#define SFI_SIG_IDLE "IDLE" +#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_SYST_SEARCH_BEGIN 0x000e0000 +#define SFI_SYST_SEARCH_END 0x000fffff + +#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 */ +struct 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]; +} __packed; + +struct sfi_table_simple { + struct sfi_table_header header; + u64 pentry[1]; +} __packed; + +/* Comply with UEFI spec 2.1 */ +struct sfi_mem_entry { + u32 type; + u64 phys_start; + u64 virt_start; + u64 pages; + u64 attrib; +} __packed; + +struct sfi_cpu_table_entry { + u32 apic_id; +} __packed; + +struct sfi_cstate_table_entry { + u32 hint; /* MWAIT hint */ + u32 latency; /* latency in ms */ +} __packed; + +struct sfi_apic_table_entry { + u64 phys_addr; /* phy base addr for APIC reg */ +} __packed; + +struct sfi_freq_table_entry { + u32 freq_mhz; /* in MHZ */ + u32 latency; /* transition latency in ms */ + u32 ctrl_val; /* value to write to PERF_CTL */ +} __packed; + +struct sfi_wake_table_entry { + u64 phys_addr; /* pointer to where the wake vector locates */ +} __packed; + +struct sfi_timer_table_entry { + u64 phys_addr; /* phy base addr for the timer */ + u32 freq_hz; /* in HZ */ + u32 irq; +} __packed; + +struct sfi_rtc_table_entry { + u64 phys_addr; /* phy base addr for the RTC */ + u32 irq; +} __packed; + +struct sfi_device_table_entry { + u8 type; /* bus type, I2C, SPI or ...*/ +#define SFI_DEV_TYPE_SPI 0 +#define SFI_DEV_TYPE_I2C 1 +#define SFI_DEV_TYPE_UART 2 +#define SFI_DEV_TYPE_HSI 3 +#define SFI_DEV_TYPE_IPC 4 + + u8 host_num; /* attached to host 0, 1...*/ + u16 addr; + u8 irq; + u32 max_freq; + char name[SFI_NAME_LEN]; +} __packed; + +struct sfi_gpio_table_entry { + char controller_name[SFI_NAME_LEN]; + u16 pin_no; + char pin_name[SFI_NAME_LEN]; +} __packed; + +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); + +#ifdef CONFIG_SFI +/** + * sfi_write_tables() - Write Simple Firmware Interface tables + */ +int sfi_write_tables(void); + +#else + +static inline int sfi_write_tables(void) { return -ENOSYS; } + +#endif + +#endif /*_LINUX_SFI_H */