linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: ard.biesheuvel@linaro.org (Ard Biesheuvel)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 3/3] efi: implement MMC proxy support for the UEFI variable store
Date: Thu, 22 Sep 2016 12:30:06 +0100	[thread overview]
Message-ID: <1474543806-19210-4-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1474543806-19210-1-git-send-email-ard.biesheuvel@linaro.org>

---
 drivers/firmware/efi/Kconfig     |   9 +
 drivers/firmware/efi/Makefile    |   1 +
 drivers/firmware/efi/arm-init.c  |   2 +
 drivers/firmware/efi/mmc-proxy.c | 222 ++++++++++++++++++++
 include/linux/efi.h              |   1 +
 5 files changed, 235 insertions(+)

diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index c981be17d3c0..1933e186a9c2 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -129,6 +129,15 @@ config EFI_TEST
 	  Say Y here to enable the runtime services support via /dev/efi_test.
 	  If unsure, say N.
 
+config EFI_MMC_PROXY
+	bool "Expose Embedded MMC host protocol to the firmware"
+	depends on EFI && (ARM || ARM64)
+	help
+	  This driver exposes the MMC host whose DT node is annotated with the
+	  "linux,uefi-varstore" property to the firmware via a UEFI compatible
+	  protocol. This allows the EFI variable store to reside on an MMC
+	  volume that is owned by the OS at runtime.
+
 endmenu
 
 config UEFI_CPER
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index c8a439f6d715..88fdf0e13d6b 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -26,3 +26,4 @@ arm-obj-$(CONFIG_EFI)			:= arm-init.o arm-runtime.o
 obj-$(CONFIG_ARM)			+= $(arm-obj-y)
 obj-$(CONFIG_ARM64)			+= $(arm-obj-y)
 obj-$(CONFIG_EFI_CAPSULE_LOADER)	+= capsule-loader.o
+obj-$(CONFIG_EFI_MMC_PROXY)		+= mmc-proxy.o
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
index 8efe13075c92..7a57253975a5 100644
--- a/drivers/firmware/efi/arm-init.c
+++ b/drivers/firmware/efi/arm-init.c
@@ -56,9 +56,11 @@ static phys_addr_t efi_to_phys(unsigned long addr)
 }
 
 static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
+unsigned long __initdata mmc_fv_emulation_table = EFI_INVALID_TABLE_ADDR;
 
 static __initdata efi_config_table_type_t arch_tables[] = {
 	{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
+	{LINUX_MMC_FV_EMULATION_TABLE_GUID, NULL, &mmc_fv_emulation_table},
 	{NULL_GUID, NULL, NULL}
 };
 
diff --git a/drivers/firmware/efi/mmc-proxy.c b/drivers/firmware/efi/mmc-proxy.c
new file mode 100644
index 000000000000..3702bb2a5af0
--- /dev/null
+++ b/drivers/firmware/efi/mmc-proxy.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <asm/efi.h>
+
+extern unsigned long mmc_fv_emulation_table;
+
+enum emmc_state {
+	mmc_invalid_state = 0,
+	mmc_hw_initialization_state,
+	mmc_idle_state,
+	mmc_ready_state,
+	mmc_identification_state,
+	mmc_standby_state,
+	mmc_transfer_state,
+	mmc_sending_data_state,
+	mmc_receive_data_state,
+	mmc_programming_state,
+	mmc_disconnect_state,
+};
+
+static char const emmc_state_string[][28] = {
+	"mmc_invalid_state",
+	"mmc_hw_initialization_state",
+	"mmc_idle_state",
+	"mmc_ready_state",
+	"mmc_identification_state",
+	"mmc_standby_state",
+	"mmc_transfer_state",
+	"mmc_sending_data_state",
+	"mmc_receive_data_state",
+	"mmc_programming_state",
+	"mmc_disconnect_state",
+};
+
+/*
+ * This MMC host protocol version is not in the UEFI spec, and lives under
+ * EmbeddedPkg/ in Tianocore/EDK2.
+ */
+struct emmc_host_protocol
+{
+	u32		revision;
+	bool		(*is_card_present)(struct emmc_host_protocol*);
+	bool		(*is_read_only)(struct emmc_host_protocol*);
+	efi_status_t	(*build_device_path)(struct emmc_host_protocol*,
+					     void*);
+	efi_status_t	(*notify_state)(struct emmc_host_protocol*,
+					enum emmc_state);
+	efi_status_t	(*send_command)(struct emmc_host_protocol*,
+					u32, u32);
+	efi_status_t	(*receive_response)(struct emmc_host_protocol*,
+					    u32, u32*);
+	efi_status_t	(*read_block_data)(struct emmc_host_protocol*,
+					   u64, unsigned long, u32*);
+	efi_status_t	(*write_block_data)(struct emmc_host_protocol*,
+					    u64, unsigned long, u32*);
+};
+
+static bool mmc_is_card_present(struct emmc_host_protocol *this)
+{
+	return true;
+}
+
+static bool mmc_is_read_only(struct emmc_host_protocol *this)
+{
+	return false;
+}
+
+static efi_status_t mmc_build_device_path(struct emmc_host_protocol *this,
+					  void *dpp)
+{
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t mmc_notify_state(struct emmc_host_protocol *this,
+				     enum emmc_state state)
+{
+	struct efi_simd_reg_stash simd_stash;
+
+	arch_efi_stash_simd_regs(&simd_stash);
+	arch_efi_call_virt_teardown();
+
+	pr_warn("mmc-proxy: entered %s state\n", emmc_state_string[state]);
+	might_sleep();
+
+	arch_efi_call_virt_setup();
+	arch_efi_unstash_simd_regs(&simd_stash);
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t mmc_send_command(struct emmc_host_protocol *this, u32 cmd,
+				     u32 arg)
+{
+	struct efi_simd_reg_stash simd_stash;
+
+	arch_efi_stash_simd_regs(&simd_stash);
+	arch_efi_call_virt_teardown();
+
+	pr_warn("mmc-proxy: send command %d (0x%x)\n", cmd, arg);
+	might_sleep();
+
+	arch_efi_call_virt_setup();
+	arch_efi_unstash_simd_regs(&simd_stash);
+	return EFI_NOT_FOUND;
+}
+
+static efi_status_t mmc_receive_response(struct emmc_host_protocol *this,
+					 u32 mmc_response_type, u32 *buffer)
+{
+	struct efi_simd_reg_stash simd_stash;
+	u32 response[4] = {};
+
+	arch_efi_stash_simd_regs(&simd_stash);
+	arch_efi_call_virt_teardown();
+
+	pr_warn("mmc-proxy: receive response %d\n", mmc_response_type);
+	might_sleep();
+
+	arch_efi_call_virt_setup();
+	arch_efi_unstash_simd_regs(&simd_stash);
+	memcpy(buffer, response, sizeof(response));
+
+	return EFI_NOT_FOUND;
+}
+
+static efi_status_t mmc_read_block_data(struct emmc_host_protocol *this,
+					u64 lba, unsigned long len,
+					u32 *buffer)
+{
+	struct efi_simd_reg_stash simd_stash;
+	void *bounce;
+
+	arch_efi_stash_simd_regs(&simd_stash);
+	arch_efi_call_virt_teardown();
+
+	bounce = kmalloc(len, GFP_KERNEL);
+
+	pr_warn("mmc-proxy: read block data lba==%lld, len==%ld\n", lba, len);
+	might_sleep();
+
+	arch_efi_call_virt_setup();
+	arch_efi_unstash_simd_regs(&simd_stash);
+	memcpy(buffer, bounce, len);
+	kfree(bounce);
+
+	return EFI_NOT_FOUND;
+}
+
+static efi_status_t mmc_write_block_data(struct emmc_host_protocol *this,
+					 u64 lba, unsigned long len,
+					 u32 *buffer)
+{
+	struct efi_simd_reg_stash simd_stash;
+	void *bounce;
+
+	/*
+	 * 'buffer' may point to memory that is mapped via the EFI page tables,
+	 * so copy the data before switching back to the ordinary mappings.
+	 */
+	bounce = kmalloc(len, GFP_ATOMIC);
+	if (!bounce)
+		return EFI_OUT_OF_RESOURCES;
+	memcpy(bounce, buffer, len);
+
+	arch_efi_stash_simd_regs(&simd_stash);
+	arch_efi_call_virt_teardown();
+
+	pr_warn("mmc-proxy: write block data lba==%lld, len==%ld\n", lba, len);
+	might_sleep();
+
+	kfree(bounce);
+	arch_efi_call_virt_setup();
+	arch_efi_unstash_simd_regs(&simd_stash);
+	return EFI_NOT_FOUND;
+}
+
+static const struct emmc_host_protocol emmc_proxy =
+{
+	0x00010001,		// revision 1.1
+	mmc_is_card_present,
+	mmc_is_read_only,
+	mmc_build_device_path,
+	mmc_notify_state,
+	mmc_send_command,
+	mmc_receive_response,
+	mmc_read_block_data,
+	mmc_write_block_data
+};
+
+static int __init emmc_proxy_install(void)
+{
+	unsigned long *p;
+
+	if (mmc_fv_emulation_table == EFI_INVALID_TABLE_ADDR)
+		return 0;
+
+	p = memremap(mmc_fv_emulation_table, sizeof(*p), MEMREMAP_WB);
+	if (!p)
+		return -ENOMEM;
+
+	/*
+	 * We expect a NULL value here, since the firmware should have cleared
+	 * this value in its ExitBootServices() handler.
+	 */
+	if (!WARN_ON(*p != 0))
+		*p = (unsigned long)&emmc_proxy;
+
+	memunmap(p);
+	return 0;
+}
+late_initcall(emmc_proxy_install);
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 2d089487d2da..25a4ae648b6f 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -599,6 +599,7 @@ void efi_native_runtime_setup(void);
  */
 #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID	EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
 #define LINUX_EFI_LOADER_ENTRY_GUID		EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
+#define LINUX_MMC_FV_EMULATION_TABLE_GUID	EFI_GUID(0x44cd9912, 0xfda8, 0x4b7d,  0xa6, 0x13, 0x1b, 0x61, 0x34, 0xfc, 0xdc, 0x4a)
 
 typedef struct {
 	efi_guid_t guid;
-- 
2.7.4

  parent reply	other threads:[~2016-09-22 11:30 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-22 11:30 [RFC PATCH 0/3] efi: MMC proxy support for the UEFI varstore Ard Biesheuvel
2016-09-22 11:30 ` [RFC PATCH 1/3] efi/arm64: add SIMD stash/unstash operations Ard Biesheuvel
2016-09-22 11:30 ` [RFC PATCH 2/3] efi/arm: " Ard Biesheuvel
2016-09-22 11:30 ` Ard Biesheuvel [this message]
2016-09-22 12:58 ` [RFC PATCH 0/3] efi: MMC proxy support for the UEFI varstore Mark Rutland
2016-09-22 13:37   ` Ard Biesheuvel
2016-09-23  9:19     ` Mark Rutland
2016-09-26 15:53   ` Mark Rutland
2016-09-28 23:54 ` Arnd Bergmann
2016-09-29  0:13   ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1474543806-19210-4-git-send-email-ard.biesheuvel@linaro.org \
    --to=ard.biesheuvel@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).