diff mbox

[U-Boot,1/5] efi_loader: Allow boards to implement get_time and reset_system

Message ID 1471374529-61610-2-git-send-email-agraf@suse.de
State Accepted
Delegated to: Alexander Graf
Headers show

Commit Message

Alexander Graf Aug. 16, 2016, 7:08 p.m. UTC
EFI allows an OS to leverage firmware drivers while the OS is running. In the
generic code we so far had to stub those implementations out, because we would
need board specific knowledge about MMIO setups for it.

However, boards can easily implement those themselves. This patch provides the
framework so that a board can implement its own versions of get_time and
reset_system which would actually do something useful.

While at it we also introduce a simple way for code to reserve MMIO pointers
as runtime available.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 cmd/bootefi.c                |   4 ++
 include/efi_loader.h         |  18 ++++++++
 lib/efi_loader/efi_runtime.c | 101 ++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 112 insertions(+), 11 deletions(-)

Comments

Simon Glass Aug. 18, 2016, 3:44 a.m. UTC | #1
Hi Alex,

On 16 August 2016 at 13:08, Alexander Graf <agraf@suse.de> wrote:
> EFI allows an OS to leverage firmware drivers while the OS is running. In the
> generic code we so far had to stub those implementations out, because we would
> need board specific knowledge about MMIO setups for it.
>
> However, boards can easily implement those themselves. This patch provides the
> framework so that a board can implement its own versions of get_time and
> reset_system which would actually do something useful.
>
> While at it we also introduce a simple way for code to reserve MMIO pointers
> as runtime available.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  cmd/bootefi.c                |   4 ++
>  include/efi_loader.h         |  18 ++++++++
>  lib/efi_loader/efi_runtime.c | 101 ++++++++++++++++++++++++++++++++++++++-----
>  3 files changed, 112 insertions(+), 11 deletions(-)

Instead of weak functions, can you use the existing driver model
sysreset uclass?

Regards,
Simon
Alexander Graf Aug. 18, 2016, 4:49 a.m. UTC | #2
> Am 18.08.2016 um 05:44 schrieb Simon Glass <sjg@chromium.org>:
> 
> Hi Alex,
> 
>> On 16 August 2016 at 13:08, Alexander Graf <agraf@suse.de> wrote:
>> EFI allows an OS to leverage firmware drivers while the OS is running. In the
>> generic code we so far had to stub those implementations out, because we would
>> need board specific knowledge about MMIO setups for it.
>> 
>> However, boards can easily implement those themselves. This patch provides the
>> framework so that a board can implement its own versions of get_time and
>> reset_system which would actually do something useful.
>> 
>> While at it we also introduce a simple way for code to reserve MMIO pointers
>> as runtime available.
>> 
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>> ---
>> cmd/bootefi.c                |   4 ++
>> include/efi_loader.h         |  18 ++++++++
>> lib/efi_loader/efi_runtime.c | 101 ++++++++++++++++++++++++++++++++++++++-----
>> 3 files changed, 112 insertions(+), 11 deletions(-)
> 
> Instead of weak functions, can you use the existing driver model
> sysreset uclass?

Unfortunately not, at least not generically.

The EFI RTS code gets dynamically relocated by the executing OS (like Linux) and expects any pointers to dynamically move to where the OS remaps things to. So memory at 0x1234 during boot time might become 0x75995234 in RTS.

While we can automatically catch all linker relocations, dynamically created objects or pointers that rely on the 1:1 map will fail to work once we're in Linux because of the above.

So the "obvious" path forward would be for systems that implement the sysreset uclass to provide an efi rts reset function which calls into their sysreset device. In a patch that enables this, whoever does it would also have to go through all the the dm code, annotate it as efi runtime and wrap all pointers in the dynamic efi relocation call. The same goes for pointers to device memory that needs to get accessed, which then also needs to get annotated as efi mmio regions in the efi memory map.

This is insanely tedious and very hard to get right. The less complexity we have in RTS code, the better we are off.

The less obvious - but imho more sane - route would be to just implement PSCI on armv8 systems. York has patches in the works to make U-Boot be a PSCI provider on armv8, armv7 already has PSCI provider code. In EL3 / TZ you can maintain your 1:1 map, don't need to swizzle pointers and could just reuse dm as much as you like. Linux gets secondary smp bootup (and core power off for power saving) for free and efi reset would "just work" with this patch set. Even better yet, it would "just work" without efi too ;).


Alex
Simon Glass Aug. 18, 2016, 12:43 p.m. UTC | #3
Hi Alex,

On 17 August 2016 at 22:49, Alexander Graf <agraf@suse.de> wrote:
>
>
>> Am 18.08.2016 um 05:44 schrieb Simon Glass <sjg@chromium.org>:
>>
>> Hi Alex,
>>
>>> On 16 August 2016 at 13:08, Alexander Graf <agraf@suse.de> wrote:
>>> EFI allows an OS to leverage firmware drivers while the OS is running. In the
>>> generic code we so far had to stub those implementations out, because we would
>>> need board specific knowledge about MMIO setups for it.
>>>
>>> However, boards can easily implement those themselves. This patch provides the
>>> framework so that a board can implement its own versions of get_time and
>>> reset_system which would actually do something useful.
>>>
>>> While at it we also introduce a simple way for code to reserve MMIO pointers
>>> as runtime available.
>>>
>>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>> ---
>>> cmd/bootefi.c                |   4 ++
>>> include/efi_loader.h         |  18 ++++++++
>>> lib/efi_loader/efi_runtime.c | 101 ++++++++++++++++++++++++++++++++++++++-----
>>> 3 files changed, 112 insertions(+), 11 deletions(-)
>>
>> Instead of weak functions, can you use the existing driver model
>> sysreset uclass?
>
> Unfortunately not, at least not generically.
>
> The EFI RTS code gets dynamically relocated by the executing OS (like Linux) and expects any pointers to dynamically move to where the OS remaps things to. So memory at 0x1234 during boot time might become 0x75995234 in RTS.
>
> While we can automatically catch all linker relocations, dynamically created objects or pointers that rely on the 1:1 map will fail to work once we're in Linux because of the above.
>
> So the "obvious" path forward would be for systems that implement the sysreset uclass to provide an efi rts reset function which calls into their sysreset device. In a patch that enables this, whoever does it would also have to go through all the the dm code, annotate it as efi runtime and wrap all pointers in the dynamic efi relocation call. The same goes for pointers to device memory that needs to get accessed, which then also needs to get annotated as efi mmio regions in the efi memory map.
>
> This is insanely tedious and very hard to get right. The less complexity we have in RTS code, the better we are off.

Yes I thought this would be the issue. I've done something similar
before where part of U-Boot was linked to be in one image and another
part was loaded later only if needed. It was pretty brittle.

I agree that annotating functions that are used is tedious. In fact as
mentioned I'm really not keen on that in any case. It would be great
to drop the runtime annotation altogether.

Some options:
- Include all of U-Boot in the run-time portion (at least for now)
- Include drivers/{core,sysreset,sysreset} (which should be enough)
- link the image once with just the EFI loader binary and its
dependencies (using --gc-sections) and then used the list of included
object files as your list to put in the EFI runtime region

I suspect the middle one might be quite easy?

My concern with what you have is that you are working around driver
model, creating a second ad-hoc driver approach. I can certainly see
your reasons (short-term expediency mostly :-), but it might not be
that hard to just bring in the DM code that is needed. Remember that
each board should keep its driver in the correct drivers/...
directory, so this does not need to be board-specific.

>
> The less obvious - but imho more sane - route would be to just implement PSCI on armv8 systems. York has patches in the works to make U-Boot be a PSCI provider on armv8, armv7 already has PSCI provider code. In EL3 / TZ you can maintain your 1:1 map, don't need to swizzle pointers and could just reuse dm as much as you like. Linux gets secondary smp bootup (and core power off for power saving) for free and efi reset would "just work" with this patch set. Even better yet, it would "just work" without efi too ;).

Interesting - then ATF would not provide any run-time services? I
suppose that means that the code is maintained in two places, but it
sounds reasonable to me. It's probably not a lot of code.

Regards,
Simon
Alexander Graf Aug. 18, 2016, 9:02 p.m. UTC | #4
> On 18 Aug 2016, at 08:43, Simon Glass <sjg@chromium.org> wrote:
> 
> Hi Alex,
> 
> On 17 August 2016 at 22:49, Alexander Graf <agraf@suse.de> wrote:
>> 
>> 
>>> Am 18.08.2016 um 05:44 schrieb Simon Glass <sjg@chromium.org>:
>>> 
>>> Hi Alex,
>>> 
>>>> On 16 August 2016 at 13:08, Alexander Graf <agraf@suse.de> wrote:
>>>> EFI allows an OS to leverage firmware drivers while the OS is running. In the
>>>> generic code we so far had to stub those implementations out, because we would
>>>> need board specific knowledge about MMIO setups for it.
>>>> 
>>>> However, boards can easily implement those themselves. This patch provides the
>>>> framework so that a board can implement its own versions of get_time and
>>>> reset_system which would actually do something useful.
>>>> 
>>>> While at it we also introduce a simple way for code to reserve MMIO pointers
>>>> as runtime available.
>>>> 
>>>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>>> ---
>>>> cmd/bootefi.c                |   4 ++
>>>> include/efi_loader.h         |  18 ++++++++
>>>> lib/efi_loader/efi_runtime.c | 101 ++++++++++++++++++++++++++++++++++++++-----
>>>> 3 files changed, 112 insertions(+), 11 deletions(-)
>>> 
>>> Instead of weak functions, can you use the existing driver model
>>> sysreset uclass?
>> 
>> Unfortunately not, at least not generically.
>> 
>> The EFI RTS code gets dynamically relocated by the executing OS (like Linux) and expects any pointers to dynamically move to where the OS remaps things to. So memory at 0x1234 during boot time might become 0x75995234 in RTS.
>> 
>> While we can automatically catch all linker relocations, dynamically created objects or pointers that rely on the 1:1 map will fail to work once we're in Linux because of the above.
>> 
>> So the "obvious" path forward would be for systems that implement the sysreset uclass to provide an efi rts reset function which calls into their sysreset device. In a patch that enables this, whoever does it would also have to go through all the the dm code, annotate it as efi runtime and wrap all pointers in the dynamic efi relocation call. The same goes for pointers to device memory that needs to get accessed, which then also needs to get annotated as efi mmio regions in the efi memory map.
>> 
>> This is insanely tedious and very hard to get right. The less complexity we have in RTS code, the better we are off.
> 
> Yes I thought this would be the issue. I've done something similar
> before where part of U-Boot was linked to be in one image and another
> part was loaded later only if needed. It was pretty brittle.
> 
> I agree that annotating functions that are used is tedious. In fact as
> mentioned I'm really not keen on that in any case. It would be great
> to drop the runtime annotation altogether.
> 
> Some options:
> - Include all of U-Boot in the run-time portion (at least for now)
> - Include drivers/{core,sysreset,sysreset} (which should be enough)
> - link the image once with just the EFI loader binary and its
> dependencies (using --gc-sections) and then used the list of included
> object files as your list to put in the EFI runtime region
> 
> I suspect the middle one might be quite easy?

All of the above would be reasonably easy to do with linker scripts. The hard part are pointers that are not tracked by relocations, so we can’t easily relocate them when going into Linux land. And without that solved, I don’t think it’s worth thinking about over engineering run time support. I’d much prefer to put as much as we can in very trivial helpers, generically with PSCI where possible.

> My concern with what you have is that you are working around driver
> model, creating a second ad-hoc driver approach. I can certainly see
> your reasons (short-term expediency mostly :-), but it might not be
> that hard to just bring in the DM code that is needed. Remember that
> each board should keep its driver in the correct drivers/...
> directory, so this does not need to be board-specific.
> 
>> 
>> The less obvious - but imho more sane - route would be to just implement PSCI on armv8 systems. York has patches in the works to make U-Boot be a PSCI provider on armv8, armv7 already has PSCI provider code. In EL3 / TZ you can maintain your 1:1 map, don't need to swizzle pointers and could just reuse dm as much as you like. Linux gets secondary smp bootup (and core power off for power saving) for free and efi reset would "just work" with this patch set. Even better yet, it would "just work" without efi too ;).
> 
> Interesting - then ATF would not provide any run-time services? I
> suppose that means that the code is maintained in two places, but it
> sounds reasonable to me. It's probably not a lot of code.

Well, ATF would still provide PSCI which Linux can directly call into ;). But U-Boot wouldn’t have to maintain anything internally for that functionality.


Alex
Simon Glass Aug. 20, 2016, 11:54 p.m. UTC | #5
Hi Alex,

On 18 August 2016 at 15:02, Alexander Graf <agraf@suse.de> wrote:
>
>> On 18 Aug 2016, at 08:43, Simon Glass <sjg@chromium.org> wrote:
>>
>> Hi Alex,
>>
>> On 17 August 2016 at 22:49, Alexander Graf <agraf@suse.de> wrote:
>>>
>>>
>>>> Am 18.08.2016 um 05:44 schrieb Simon Glass <sjg@chromium.org>:
>>>>
>>>> Hi Alex,
>>>>
>>>>> On 16 August 2016 at 13:08, Alexander Graf <agraf@suse.de> wrote:
>>>>> EFI allows an OS to leverage firmware drivers while the OS is running. In the
>>>>> generic code we so far had to stub those implementations out, because we would
>>>>> need board specific knowledge about MMIO setups for it.
>>>>>
>>>>> However, boards can easily implement those themselves. This patch provides the
>>>>> framework so that a board can implement its own versions of get_time and
>>>>> reset_system which would actually do something useful.
>>>>>
>>>>> While at it we also introduce a simple way for code to reserve MMIO pointers
>>>>> as runtime available.
>>>>>
>>>>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>>>> ---
>>>>> cmd/bootefi.c                |   4 ++
>>>>> include/efi_loader.h         |  18 ++++++++
>>>>> lib/efi_loader/efi_runtime.c | 101 ++++++++++++++++++++++++++++++++++++++-----
>>>>> 3 files changed, 112 insertions(+), 11 deletions(-)
>>>>
>>>> Instead of weak functions, can you use the existing driver model
>>>> sysreset uclass?
>>>
>>> Unfortunately not, at least not generically.
>>>
>>> The EFI RTS code gets dynamically relocated by the executing OS (like Linux) and expects any pointers to dynamically move to where the OS remaps things to. So memory at 0x1234 during boot time might become 0x75995234 in RTS.
>>>
>>> While we can automatically catch all linker relocations, dynamically created objects or pointers that rely on the 1:1 map will fail to work once we're in Linux because of the above.
>>>
>>> So the "obvious" path forward would be for systems that implement the sysreset uclass to provide an efi rts reset function which calls into their sysreset device. In a patch that enables this, whoever does it would also have to go through all the the dm code, annotate it as efi runtime and wrap all pointers in the dynamic efi relocation call. The same goes for pointers to device memory that needs to get accessed, which then also needs to get annotated as efi mmio regions in the efi memory map.
>>>
>>> This is insanely tedious and very hard to get right. The less complexity we have in RTS code, the better we are off.
>>
>> Yes I thought this would be the issue. I've done something similar
>> before where part of U-Boot was linked to be in one image and another
>> part was loaded later only if needed. It was pretty brittle.
>>
>> I agree that annotating functions that are used is tedious. In fact as
>> mentioned I'm really not keen on that in any case. It would be great
>> to drop the runtime annotation altogether.
>>
>> Some options:
>> - Include all of U-Boot in the run-time portion (at least for now)
>> - Include drivers/{core,sysreset,sysreset} (which should be enough)
>> - link the image once with just the EFI loader binary and its
>> dependencies (using --gc-sections) and then used the list of included
>> object files as your list to put in the EFI runtime region
>>
>> I suspect the middle one might be quite easy?
>
> All of the above would be reasonably easy to do with linker scripts. The hard part are pointers that are not tracked by relocations, so we can’t easily relocate them when going into Linux land. And without that solved, I don’t think it’s worth thinking about over engineering run time support. I’d much prefer to put as much as we can in very trivial helpers, generically with PSCI where possible.

Yes that's a whole other problem that I hadn't considered. It means
that the currently implementation is a barrier to adoption of driver
model. This really need some thought. How do you deal with pointers at
present? It may need do be a new execution phase, where we set up a
new global_data and init driver model. Pretty simple I suppose (we do
this in spl_init() at present), but I'm sure there would be fishhooks.

If we continue down the path you have created, it is only going to get
harder to fix this up. If I had known we would end up here I would
have been much less comfortable with the implementation.

Let's figure this out now. I'm happy to help with refactoring or
whatever is needed.

>
>> My concern with what you have is that you are working around driver
>> model, creating a second ad-hoc driver approach. I can certainly see
>> your reasons (short-term expediency mostly :-), but it might not be
>> that hard to just bring in the DM code that is needed. Remember that
>> each board should keep its driver in the correct drivers/...
>> directory, so this does not need to be board-specific.
>>
>>>
>>> The less obvious - but imho more sane - route would be to just implement PSCI on armv8 systems. York has patches in the works to make U-Boot be a PSCI provider on armv8, armv7 already has PSCI provider code. In EL3 / TZ you can maintain your 1:1 map, don't need to swizzle pointers and could just reuse dm as much as you like. Linux gets secondary smp bootup (and core power off for power saving) for free and efi reset would "just work" with this patch set. Even better yet, it would "just work" without efi too ;).
>>
>> Interesting - then ATF would not provide any run-time services? I
>> suppose that means that the code is maintained in two places, but it
>> sounds reasonable to me. It's probably not a lot of code.
>
> Well, ATF would still provide PSCI which Linux can directly call into ;). But U-Boot wouldn’t have to maintain anything internally for that functionality.

Ah I see, so in what way is U-Boot the provider in this case? Does it
mean U-Boot does not have run-time code anymore?

Regards,
Simon
Alexander Graf Oct. 13, 2016, 2:35 p.m. UTC | #6
> EFI allows an OS to leverage firmware drivers while the OS is running. In the
> generic code we so far had to stub those implementations out, because we would
> need board specific knowledge about MMIO setups for it.
> 
> However, boards can easily implement those themselves. This patch provides the
> framework so that a board can implement its own versions of get_time and
> reset_system which would actually do something useful.
> 
> While at it we also introduce a simple way for code to reserve MMIO pointers
> as runtime available.
> 
> Signed-off-by: Alexander Graf <agraf@suse.de>

Thanks, applied to
diff mbox

Patch

diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index b52ba9c..53a6ee3 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -206,6 +206,10 @@  static unsigned long do_bootefi_exec(void *efi, void *fdt)
 		loaded_image_info.device_handle = nethandle;
 #endif
 
+	/* Initialize EFI runtime services */
+	efi_reset_system_init();
+	efi_get_time_init();
+
 	/* Call our payload! */
 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
 
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 9738835..91d6a84 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -150,11 +150,29 @@  static inline void ascii2unicode(u16 *unicode, const char *ascii)
 #define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data")))
 #define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text")))
 
+/* Call this with mmio_ptr as the _pointer_ to a pointer to an MMIO region
+ * to make it available at runtime */
+void efi_add_runtime_mmio(void *mmio_ptr, u64 len);
+
+/* Boards may provide the functions below to implement RTS functionality */
+
+void EFI_RUNTIME_TEXT EFIAPI efi_reset_system(
+			enum efi_reset_type reset_type,
+			efi_status_t reset_status,
+			unsigned long data_size, void *reset_data);
+void efi_reset_system_init(void);
+
+efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_get_time(
+			struct efi_time *time,
+			struct efi_time_cap *capabilities);
+void efi_get_time_init(void);
+
 #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
 #define EFI_RUNTIME_DATA
 #define EFI_RUNTIME_TEXT
+static inline void efi_add_runtime_mmio(void **mmio_ptr, u64 len) { }
 
 /* No loader configured, stub out EFI_ENTRY */
 static inline void efi_restore_gd(void) { }
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 99b5ef1..e697226 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -16,6 +16,16 @@ 
 /* For manual relocation support */
 DECLARE_GLOBAL_DATA_PTR;
 
+struct efi_runtime_mmio_list {
+	struct list_head link;
+	void **ptr;
+	u64 paddr;
+	u64 len;
+};
+
+/* This list contains all runtime available mmio regions */
+LIST_HEAD(efi_runtime_mmio);
+
 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void);
 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void);
 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void);
@@ -55,9 +65,10 @@  struct elf_rela {
  * handle a good number of runtime callbacks
  */
 
-static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
-				    efi_status_t reset_status,
-				    unsigned long data_size, void *reset_data)
+static void EFIAPI efi_reset_system_boottime(
+			enum efi_reset_type reset_type,
+			efi_status_t reset_status,
+			unsigned long data_size, void *reset_data)
 {
 	EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
 		  reset_data);
@@ -72,11 +83,12 @@  static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
 		break;
 	}
 
-	EFI_EXIT(EFI_SUCCESS);
+	while (1) { }
 }
 
-static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
-					struct efi_time_cap *capabilities)
+static efi_status_t EFIAPI efi_get_time_boottime(
+			struct efi_time *time,
+			struct efi_time_cap *capabilities)
 {
 #if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC)
 	struct rtc_time tm;
@@ -107,6 +119,33 @@  static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
 #endif
 }
 
+/* Boards may override the helpers below to implement RTS functionality */
+
+void __weak EFI_RUNTIME_TEXT EFIAPI efi_reset_system(
+			enum efi_reset_type reset_type,
+			efi_status_t reset_status,
+			unsigned long data_size, void *reset_data)
+{
+	/* Nothing we can do */
+	while (1) { }
+}
+
+void __weak efi_reset_system_init(void)
+{
+}
+
+efi_status_t __weak EFI_RUNTIME_TEXT EFIAPI efi_get_time(
+			struct efi_time *time,
+			struct efi_time_cap *capabilities)
+{
+	/* Nothing we can do */
+	return EFI_DEVICE_ERROR;
+}
+
+void __weak efi_get_time_init(void)
+{
+}
+
 struct efi_runtime_detach_list_struct {
 	void *ptr;
 	void *patchto;
@@ -116,7 +155,7 @@  static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 	{
 		/* do_reset is gone */
 		.ptr = &efi_runtime_services.reset_system,
-		.patchto = NULL,
+		.patchto = efi_reset_system,
 	}, {
 		/* invalidate_*cache_all are gone */
 		.ptr = &efi_runtime_services.set_virtual_address_map,
@@ -124,7 +163,7 @@  static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 	}, {
 		/* RTC accessors are gone */
 		.ptr = &efi_runtime_services.get_time,
-		.patchto = &efi_device_error,
+		.patchto = &efi_get_time,
 	}, {
 		/* Clean up system table */
 		.ptr = &systab.con_in,
@@ -233,12 +272,38 @@  static efi_status_t EFIAPI efi_set_virtual_address_map(
 	EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
 		  descriptor_version, virtmap);
 
+	/* Rebind mmio pointers */
+	for (i = 0; i < n; i++) {
+		struct efi_mem_desc *map = (void*)virtmap +
+					   (descriptor_size * i);
+		struct list_head *lhandle;
+		efi_physical_addr_t map_start = map->physical_start;
+		efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT;
+		efi_physical_addr_t map_end = map_start + map_len;
+
+		/* Adjust all mmio pointers in this region */
+		list_for_each(lhandle, &efi_runtime_mmio) {
+			struct efi_runtime_mmio_list *lmmio;
+
+			lmmio = list_entry(lhandle,
+					   struct efi_runtime_mmio_list,
+					   link);
+			if ((map_start <= lmmio->paddr) &&
+			    (map_end >= lmmio->paddr)) {
+				u64 off = map->virtual_start - map_start;
+				*lmmio->ptr = (void*)(lmmio->paddr + off);
+			}
+		}
+	}
+
+	/* Move the actual runtime code over */
 	for (i = 0; i < n; i++) {
 		struct efi_mem_desc *map;
 
 		map = (void*)virtmap + (descriptor_size * i);
 		if (map->type == EFI_RUNTIME_SERVICES_CODE) {
-			ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr);
+			ulong new_offset = map->virtual_start -
+					   (runtime_start - gd->relocaddr);
 
 			efi_runtime_relocate(new_offset, map);
 			/* Once we're virtual, we can no longer handle
@@ -251,6 +316,20 @@  static efi_status_t EFIAPI efi_set_virtual_address_map(
 	return EFI_EXIT(EFI_INVALID_PARAMETER);
 }
 
+void efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+{
+	struct efi_runtime_mmio_list *newmmio;
+
+	u64 pages = (len + EFI_PAGE_SIZE - 1) >> EFI_PAGE_SHIFT;
+	efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false);
+
+	newmmio = calloc(1, sizeof(*newmmio));
+	newmmio->ptr = mmio_ptr;
+	newmmio->paddr = *(uintptr_t *)mmio_ptr;
+	newmmio->len = len;
+	list_add_tail(&newmmio->link, &efi_runtime_mmio);
+}
+
 /*
  * In the second stage, U-Boot has disappeared. To isolate our runtime code
  * that at this point still exists from the rest, we put it into a special
@@ -292,7 +371,7 @@  struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
 		.revision = EFI_RUNTIME_SERVICES_REVISION,
 		.headersize = sizeof(struct efi_table_hdr),
 	},
-	.get_time = &efi_get_time,
+	.get_time = &efi_get_time_boottime,
 	.set_time = (void *)&efi_device_error,
 	.get_wakeup_time = (void *)&efi_unimplemented,
 	.set_wakeup_time = (void *)&efi_unimplemented,
@@ -302,5 +381,5 @@  struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
 	.get_next_variable = (void *)&efi_device_error,
 	.set_variable = (void *)&efi_device_error,
 	.get_next_high_mono_count = (void *)&efi_device_error,
-	.reset_system = &efi_reset_system,
+	.reset_system = &efi_reset_system_boottime,
 };