All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 00/17] EFI stub for ARM
@ 2013-08-07  3:44 ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

This patch series adds EFI stub support for the ARM architecture.
Some code that was previously only used by x86/x86_64 is now shared
and has been made more general.  The stub for ARM is implemented in
a similar manner to x86 in that it is a shim layer between EFI and
the normal zImage/bzImage boot process, and that an image with the
stub configured is bootable as both a zImage and EFI application.

This patchset depends (trivially) on a separately submitted bugfix to the
EFI stub: "correct call to free_pages"
http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?id=df981edcb9bce00b9c5e4f3cc33f3f98bc9a2394


Changes since V1:
* Updated head.S based on feedback from Dave Martin.  ARM/THUMB
  switches now much cleaner.
* Broke up changes to x86 and common code into more patches.
  10 more patches in this series.



Roy Franz (17):
  EFI stub documentation updates
  Move common EFI stub code from x86 arch code to common location
  Add system pointer argument to shared EFI stub related     functions
    so they no longer use global system table     pointer as they did
    when part of eboot.c.
  Rename memory allocation/free functions
  Add minimum address parameter to efi_low_alloc()
  rename __get_map() to efi_get_memory_map(), add parameter     to
    optionally return mmap key. The mmap key is required     to exit
    EFI boot services, and allows efi_get_memory_map()     to be used
    for getting final memory map.
  Enforce minimum alignment of 1 page on allocations.     The
    efi_high_alloc() and efi_low_alloc() functions     use the
    EFI_ALLOCATE_ADDRESS option to the EFI     function
    allocate_pages(), which requires a minimum     of page alignment,
    and rejects all other requests.
  Allow efi_free() to be called with size of 0, and do nothing in that
    case.
  Generalize handle_ramdisks() and rename to handle_cmdline_files().
  Renames in handle_cmdline_files() to complete generalization.
  Move EFI_READ_CHUNK_SIZE define to shared location.
  Add proper definitions for some EFI function pointers.
  Fix types in EFI calls to match EFI function definitions.
  resolve warnings found on ARM compile
  Add strstr to compressed string.c for ARM.
  Add EFI stub for ARM
  Add config EFI_STUB for ARM to Kconfig

 Documentation/efi-stub.txt             |   78 +++++
 Documentation/x86/efi-stub.txt         |   65 ----
 arch/arm/Kconfig                       |   10 +
 arch/arm/boot/compressed/Makefile      |   18 +-
 arch/arm/boot/compressed/efi-header.S  |  114 +++++++
 arch/arm/boot/compressed/efi-stub.c    |  514 ++++++++++++++++++++++++++++++++
 arch/arm/boot/compressed/head.S        |   90 +++++-
 arch/arm/boot/compressed/string.c      |   21 ++
 arch/x86/Kconfig                       |    2 +-
 arch/x86/boot/compressed/eboot.c       |  490 ++----------------------------
 arch/x86/boot/compressed/eboot.h       |    7 -
 drivers/firmware/efi/efi-stub-helper.c |  505 +++++++++++++++++++++++++++++++
 include/linux/efi.h                    |   48 ++-
 13 files changed, 1405 insertions(+), 557 deletions(-)
 create mode 100644 Documentation/efi-stub.txt
 delete mode 100644 Documentation/x86/efi-stub.txt
 create mode 100644 arch/arm/boot/compressed/efi-header.S
 create mode 100644 arch/arm/boot/compressed/efi-stub.c
 create mode 100644 drivers/firmware/efi/efi-stub-helper.c

-- 
1.7.10.4


^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH V2 00/17] EFI stub for ARM
@ 2013-08-07  3:44 ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series adds EFI stub support for the ARM architecture.
Some code that was previously only used by x86/x86_64 is now shared
and has been made more general.  The stub for ARM is implemented in
a similar manner to x86 in that it is a shim layer between EFI and
the normal zImage/bzImage boot process, and that an image with the
stub configured is bootable as both a zImage and EFI application.

This patchset depends (trivially) on a separately submitted bugfix to the
EFI stub: "correct call to free_pages"
http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?id=df981edcb9bce00b9c5e4f3cc33f3f98bc9a2394


Changes since V1:
* Updated head.S based on feedback from Dave Martin.  ARM/THUMB
  switches now much cleaner.
* Broke up changes to x86 and common code into more patches.
  10 more patches in this series.



Roy Franz (17):
  EFI stub documentation updates
  Move common EFI stub code from x86 arch code to common location
  Add system pointer argument to shared EFI stub related     functions
    so they no longer use global system table     pointer as they did
    when part of eboot.c.
  Rename memory allocation/free functions
  Add minimum address parameter to efi_low_alloc()
  rename __get_map() to efi_get_memory_map(), add parameter     to
    optionally return mmap key. The mmap key is required     to exit
    EFI boot services, and allows efi_get_memory_map()     to be used
    for getting final memory map.
  Enforce minimum alignment of 1 page on allocations.     The
    efi_high_alloc() and efi_low_alloc() functions     use the
    EFI_ALLOCATE_ADDRESS option to the EFI     function
    allocate_pages(), which requires a minimum     of page alignment,
    and rejects all other requests.
  Allow efi_free() to be called with size of 0, and do nothing in that
    case.
  Generalize handle_ramdisks() and rename to handle_cmdline_files().
  Renames in handle_cmdline_files() to complete generalization.
  Move EFI_READ_CHUNK_SIZE define to shared location.
  Add proper definitions for some EFI function pointers.
  Fix types in EFI calls to match EFI function definitions.
  resolve warnings found on ARM compile
  Add strstr to compressed string.c for ARM.
  Add EFI stub for ARM
  Add config EFI_STUB for ARM to Kconfig

 Documentation/efi-stub.txt             |   78 +++++
 Documentation/x86/efi-stub.txt         |   65 ----
 arch/arm/Kconfig                       |   10 +
 arch/arm/boot/compressed/Makefile      |   18 +-
 arch/arm/boot/compressed/efi-header.S  |  114 +++++++
 arch/arm/boot/compressed/efi-stub.c    |  514 ++++++++++++++++++++++++++++++++
 arch/arm/boot/compressed/head.S        |   90 +++++-
 arch/arm/boot/compressed/string.c      |   21 ++
 arch/x86/Kconfig                       |    2 +-
 arch/x86/boot/compressed/eboot.c       |  490 ++----------------------------
 arch/x86/boot/compressed/eboot.h       |    7 -
 drivers/firmware/efi/efi-stub-helper.c |  505 +++++++++++++++++++++++++++++++
 include/linux/efi.h                    |   48 ++-
 13 files changed, 1405 insertions(+), 557 deletions(-)
 create mode 100644 Documentation/efi-stub.txt
 delete mode 100644 Documentation/x86/efi-stub.txt
 create mode 100644 arch/arm/boot/compressed/efi-header.S
 create mode 100644 arch/arm/boot/compressed/efi-stub.c
 create mode 100644 drivers/firmware/efi/efi-stub-helper.c

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 01/17] EFI stub documentation updates
  2013-08-07  3:44 ` Roy Franz
  (?)
@ 2013-08-07  3:44   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

The ARM kernel also has an EFI stub which works largely the same way
as the x86 stub, so move the documentation out of x86 directory and
update to reflect that it is generic, and add ARM specific text.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 Documentation/efi-stub.txt     |   78 ++++++++++++++++++++++++++++++++++++++++
 Documentation/x86/efi-stub.txt |   65 ---------------------------------
 arch/x86/Kconfig               |    2 +-
 3 files changed, 79 insertions(+), 66 deletions(-)
 create mode 100644 Documentation/efi-stub.txt
 delete mode 100644 Documentation/x86/efi-stub.txt

diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt
new file mode 100644
index 0000000..19e897c
--- /dev/null
+++ b/Documentation/efi-stub.txt
@@ -0,0 +1,78 @@
+			  The EFI Boot Stub
+		     ---------------------------
+
+On the x86 and ARM platforms, a kernel zImage/bzImage can masquerade
+as a PE/COFF image, thereby convincing EFI firmware loaders to load
+it as an EFI executable. The code that modifies the bzImage header,
+along with the EFI-specific entry point that the firmware loader
+jumps to are collectively known as the "EFI boot stub", and live in
+arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
+respectively.  For ARM the EFI stub is implemented in
+arch/arm/boot/compressed/efi-header.S and
+arch/arm/boot/compressed/efi-stub.c.  EFI stub code that is shared
+between architectures is in drivers/firmware/efi/efi-stub-helper.c.
+
+By using the EFI boot stub it's possible to boot a Linux kernel
+without the use of a conventional EFI boot loader, such as grub or
+elilo. Since the EFI boot stub performs the jobs of a boot loader, in
+a certain sense it *IS* the boot loader.
+
+The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option.
+
+
+**** How to install bzImage.efi
+
+The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
+System Partiion (ESP) and renamed with the extension ".efi". Without
+the extension the EFI firmware loader will refuse to execute it. It's
+not possible to execute bzImage.efi from the usual Linux file systems
+because EFI firmware doesn't have support for them.  For ARM the
+arch/arm/boot/zImage should be copied to the system partition, and it
+may not need to be renamed.
+
+
+**** Passing kernel parameters from the EFI shell
+
+Arguments to the kernel can be passed after bzImage.efi, e.g.
+
+	fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
+
+
+**** The "initrd=" option
+
+Like most boot loaders, the EFI stub allows the user to specify
+multiple initrd files using the "initrd=" option. This is the only EFI
+stub-specific command line parameter, everything else is passed to the
+kernel when it boots.
+
+The path to the initrd file must be an absolute path from the
+beginning of the ESP, relative path names do not work. Also, the path
+is an EFI-style path and directory elements must be separated with
+backslashes (\). For example, given the following directory layout,
+
+fs0:>
+	Kernels\
+			bzImage.efi
+			initrd-large.img
+
+	Ramdisks\
+			initrd-small.img
+			initrd-medium.img
+
+to boot with the initrd-large.img file if the current working
+directory is fs0:\Kernels, the following command must be used,
+
+	fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img
+
+Notice how bzImage.efi can be specified with a relative path. That's
+because the image we're executing is interpreted by the EFI shell,
+which understands relative paths, whereas the rest of the command line
+is passed to bzImage.efi.
+
+
+**** The "dtb=" option
+
+For the ARM architecture, we also need to be able to provide a device
+tree to the kernel.  This is done with the "dtb=" command line option,
+and is process in the same manner as the "initrd=" option that is described
+above.
diff --git a/Documentation/x86/efi-stub.txt b/Documentation/x86/efi-stub.txt
deleted file mode 100644
index 44e6bb6..0000000
--- a/Documentation/x86/efi-stub.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-			  The EFI Boot Stub
-		     ---------------------------
-
-On the x86 platform, a bzImage can masquerade as a PE/COFF image,
-thereby convincing EFI firmware loaders to load it as an EFI
-executable. The code that modifies the bzImage header, along with the
-EFI-specific entry point that the firmware loader jumps to are
-collectively known as the "EFI boot stub", and live in
-arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
-respectively.
-
-By using the EFI boot stub it's possible to boot a Linux kernel
-without the use of a conventional EFI boot loader, such as grub or
-elilo. Since the EFI boot stub performs the jobs of a boot loader, in
-a certain sense it *IS* the boot loader.
-
-The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option.
-
-
-**** How to install bzImage.efi
-
-The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
-System Partiion (ESP) and renamed with the extension ".efi". Without
-the extension the EFI firmware loader will refuse to execute it. It's
-not possible to execute bzImage.efi from the usual Linux file systems
-because EFI firmware doesn't have support for them.
-
-
-**** Passing kernel parameters from the EFI shell
-
-Arguments to the kernel can be passed after bzImage.efi, e.g.
-
-	fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
-
-
-**** The "initrd=" option
-
-Like most boot loaders, the EFI stub allows the user to specify
-multiple initrd files using the "initrd=" option. This is the only EFI
-stub-specific command line parameter, everything else is passed to the
-kernel when it boots.
-
-The path to the initrd file must be an absolute path from the
-beginning of the ESP, relative path names do not work. Also, the path
-is an EFI-style path and directory elements must be separated with
-backslashes (\). For example, given the following directory layout,
-
-fs0:>
-	Kernels\
-			bzImage.efi
-			initrd-large.img
-
-	Ramdisks\
-			initrd-small.img
-			initrd-medium.img
-
-to boot with the initrd-large.img file if the current working
-directory is fs0:\Kernels, the following command must be used,
-
-	fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img
-
-Notice how bzImage.efi can be specified with a relative path. That's
-because the image we're executing is interpreted by the EFI shell,
-which understands relative paths, whereas the rest of the command line
-is passed to bzImage.efi.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b32ebf9..ec65b51 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1579,7 +1579,7 @@ config EFI_STUB
           This kernel feature allows a bzImage to be loaded directly
 	  by EFI firmware without the use of a bootloader.
 
-	  See Documentation/x86/efi-stub.txt for more information.
+	  See Documentation/efi-stub.txt for more information.
 
 config SECCOMP
 	def_bool y
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 01/17] EFI stub documentation updates
@ 2013-08-07  3:44   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: Roy Franz, dave.martin, leif.lindholm

The ARM kernel also has an EFI stub which works largely the same way
as the x86 stub, so move the documentation out of x86 directory and
update to reflect that it is generic, and add ARM specific text.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 Documentation/efi-stub.txt     |   78 ++++++++++++++++++++++++++++++++++++++++
 Documentation/x86/efi-stub.txt |   65 ---------------------------------
 arch/x86/Kconfig               |    2 +-
 3 files changed, 79 insertions(+), 66 deletions(-)
 create mode 100644 Documentation/efi-stub.txt
 delete mode 100644 Documentation/x86/efi-stub.txt

diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt
new file mode 100644
index 0000000..19e897c
--- /dev/null
+++ b/Documentation/efi-stub.txt
@@ -0,0 +1,78 @@
+			  The EFI Boot Stub
+		     ---------------------------
+
+On the x86 and ARM platforms, a kernel zImage/bzImage can masquerade
+as a PE/COFF image, thereby convincing EFI firmware loaders to load
+it as an EFI executable. The code that modifies the bzImage header,
+along with the EFI-specific entry point that the firmware loader
+jumps to are collectively known as the "EFI boot stub", and live in
+arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
+respectively.  For ARM the EFI stub is implemented in
+arch/arm/boot/compressed/efi-header.S and
+arch/arm/boot/compressed/efi-stub.c.  EFI stub code that is shared
+between architectures is in drivers/firmware/efi/efi-stub-helper.c.
+
+By using the EFI boot stub it's possible to boot a Linux kernel
+without the use of a conventional EFI boot loader, such as grub or
+elilo. Since the EFI boot stub performs the jobs of a boot loader, in
+a certain sense it *IS* the boot loader.
+
+The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option.
+
+
+**** How to install bzImage.efi
+
+The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
+System Partiion (ESP) and renamed with the extension ".efi". Without
+the extension the EFI firmware loader will refuse to execute it. It's
+not possible to execute bzImage.efi from the usual Linux file systems
+because EFI firmware doesn't have support for them.  For ARM the
+arch/arm/boot/zImage should be copied to the system partition, and it
+may not need to be renamed.
+
+
+**** Passing kernel parameters from the EFI shell
+
+Arguments to the kernel can be passed after bzImage.efi, e.g.
+
+	fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
+
+
+**** The "initrd=" option
+
+Like most boot loaders, the EFI stub allows the user to specify
+multiple initrd files using the "initrd=" option. This is the only EFI
+stub-specific command line parameter, everything else is passed to the
+kernel when it boots.
+
+The path to the initrd file must be an absolute path from the
+beginning of the ESP, relative path names do not work. Also, the path
+is an EFI-style path and directory elements must be separated with
+backslashes (\). For example, given the following directory layout,
+
+fs0:>
+	Kernels\
+			bzImage.efi
+			initrd-large.img
+
+	Ramdisks\
+			initrd-small.img
+			initrd-medium.img
+
+to boot with the initrd-large.img file if the current working
+directory is fs0:\Kernels, the following command must be used,
+
+	fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img
+
+Notice how bzImage.efi can be specified with a relative path. That's
+because the image we're executing is interpreted by the EFI shell,
+which understands relative paths, whereas the rest of the command line
+is passed to bzImage.efi.
+
+
+**** The "dtb=" option
+
+For the ARM architecture, we also need to be able to provide a device
+tree to the kernel.  This is done with the "dtb=" command line option,
+and is process in the same manner as the "initrd=" option that is described
+above.
diff --git a/Documentation/x86/efi-stub.txt b/Documentation/x86/efi-stub.txt
deleted file mode 100644
index 44e6bb6..0000000
--- a/Documentation/x86/efi-stub.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-			  The EFI Boot Stub
-		     ---------------------------
-
-On the x86 platform, a bzImage can masquerade as a PE/COFF image,
-thereby convincing EFI firmware loaders to load it as an EFI
-executable. The code that modifies the bzImage header, along with the
-EFI-specific entry point that the firmware loader jumps to are
-collectively known as the "EFI boot stub", and live in
-arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
-respectively.
-
-By using the EFI boot stub it's possible to boot a Linux kernel
-without the use of a conventional EFI boot loader, such as grub or
-elilo. Since the EFI boot stub performs the jobs of a boot loader, in
-a certain sense it *IS* the boot loader.
-
-The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option.
-
-
-**** How to install bzImage.efi
-
-The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
-System Partiion (ESP) and renamed with the extension ".efi". Without
-the extension the EFI firmware loader will refuse to execute it. It's
-not possible to execute bzImage.efi from the usual Linux file systems
-because EFI firmware doesn't have support for them.
-
-
-**** Passing kernel parameters from the EFI shell
-
-Arguments to the kernel can be passed after bzImage.efi, e.g.
-
-	fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
-
-
-**** The "initrd=" option
-
-Like most boot loaders, the EFI stub allows the user to specify
-multiple initrd files using the "initrd=" option. This is the only EFI
-stub-specific command line parameter, everything else is passed to the
-kernel when it boots.
-
-The path to the initrd file must be an absolute path from the
-beginning of the ESP, relative path names do not work. Also, the path
-is an EFI-style path and directory elements must be separated with
-backslashes (\). For example, given the following directory layout,
-
-fs0:>
-	Kernels\
-			bzImage.efi
-			initrd-large.img
-
-	Ramdisks\
-			initrd-small.img
-			initrd-medium.img
-
-to boot with the initrd-large.img file if the current working
-directory is fs0:\Kernels, the following command must be used,
-
-	fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img
-
-Notice how bzImage.efi can be specified with a relative path. That's
-because the image we're executing is interpreted by the EFI shell,
-which understands relative paths, whereas the rest of the command line
-is passed to bzImage.efi.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b32ebf9..ec65b51 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1579,7 +1579,7 @@ config EFI_STUB
           This kernel feature allows a bzImage to be loaded directly
 	  by EFI firmware without the use of a bootloader.
 
-	  See Documentation/x86/efi-stub.txt for more information.
+	  See Documentation/efi-stub.txt for more information.
 
 config SECCOMP
 	def_bool y
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 01/17] EFI stub documentation updates
@ 2013-08-07  3:44   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

The ARM kernel also has an EFI stub which works largely the same way
as the x86 stub, so move the documentation out of x86 directory and
update to reflect that it is generic, and add ARM specific text.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 Documentation/efi-stub.txt     |   78 ++++++++++++++++++++++++++++++++++++++++
 Documentation/x86/efi-stub.txt |   65 ---------------------------------
 arch/x86/Kconfig               |    2 +-
 3 files changed, 79 insertions(+), 66 deletions(-)
 create mode 100644 Documentation/efi-stub.txt
 delete mode 100644 Documentation/x86/efi-stub.txt

diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt
new file mode 100644
index 0000000..19e897c
--- /dev/null
+++ b/Documentation/efi-stub.txt
@@ -0,0 +1,78 @@
+			  The EFI Boot Stub
+		     ---------------------------
+
+On the x86 and ARM platforms, a kernel zImage/bzImage can masquerade
+as a PE/COFF image, thereby convincing EFI firmware loaders to load
+it as an EFI executable. The code that modifies the bzImage header,
+along with the EFI-specific entry point that the firmware loader
+jumps to are collectively known as the "EFI boot stub", and live in
+arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
+respectively.  For ARM the EFI stub is implemented in
+arch/arm/boot/compressed/efi-header.S and
+arch/arm/boot/compressed/efi-stub.c.  EFI stub code that is shared
+between architectures is in drivers/firmware/efi/efi-stub-helper.c.
+
+By using the EFI boot stub it's possible to boot a Linux kernel
+without the use of a conventional EFI boot loader, such as grub or
+elilo. Since the EFI boot stub performs the jobs of a boot loader, in
+a certain sense it *IS* the boot loader.
+
+The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option.
+
+
+**** How to install bzImage.efi
+
+The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
+System Partiion (ESP) and renamed with the extension ".efi". Without
+the extension the EFI firmware loader will refuse to execute it. It's
+not possible to execute bzImage.efi from the usual Linux file systems
+because EFI firmware doesn't have support for them.  For ARM the
+arch/arm/boot/zImage should be copied to the system partition, and it
+may not need to be renamed.
+
+
+**** Passing kernel parameters from the EFI shell
+
+Arguments to the kernel can be passed after bzImage.efi, e.g.
+
+	fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
+
+
+**** The "initrd=" option
+
+Like most boot loaders, the EFI stub allows the user to specify
+multiple initrd files using the "initrd=" option. This is the only EFI
+stub-specific command line parameter, everything else is passed to the
+kernel when it boots.
+
+The path to the initrd file must be an absolute path from the
+beginning of the ESP, relative path names do not work. Also, the path
+is an EFI-style path and directory elements must be separated with
+backslashes (\). For example, given the following directory layout,
+
+fs0:>
+	Kernels\
+			bzImage.efi
+			initrd-large.img
+
+	Ramdisks\
+			initrd-small.img
+			initrd-medium.img
+
+to boot with the initrd-large.img file if the current working
+directory is fs0:\Kernels, the following command must be used,
+
+	fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img
+
+Notice how bzImage.efi can be specified with a relative path. That's
+because the image we're executing is interpreted by the EFI shell,
+which understands relative paths, whereas the rest of the command line
+is passed to bzImage.efi.
+
+
+**** The "dtb=" option
+
+For the ARM architecture, we also need to be able to provide a device
+tree to the kernel.  This is done with the "dtb=" command line option,
+and is process in the same manner as the "initrd=" option that is described
+above.
diff --git a/Documentation/x86/efi-stub.txt b/Documentation/x86/efi-stub.txt
deleted file mode 100644
index 44e6bb6..0000000
--- a/Documentation/x86/efi-stub.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-			  The EFI Boot Stub
-		     ---------------------------
-
-On the x86 platform, a bzImage can masquerade as a PE/COFF image,
-thereby convincing EFI firmware loaders to load it as an EFI
-executable. The code that modifies the bzImage header, along with the
-EFI-specific entry point that the firmware loader jumps to are
-collectively known as the "EFI boot stub", and live in
-arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
-respectively.
-
-By using the EFI boot stub it's possible to boot a Linux kernel
-without the use of a conventional EFI boot loader, such as grub or
-elilo. Since the EFI boot stub performs the jobs of a boot loader, in
-a certain sense it *IS* the boot loader.
-
-The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option.
-
-
-**** How to install bzImage.efi
-
-The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
-System Partiion (ESP) and renamed with the extension ".efi". Without
-the extension the EFI firmware loader will refuse to execute it. It's
-not possible to execute bzImage.efi from the usual Linux file systems
-because EFI firmware doesn't have support for them.
-
-
-**** Passing kernel parameters from the EFI shell
-
-Arguments to the kernel can be passed after bzImage.efi, e.g.
-
-	fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
-
-
-**** The "initrd=" option
-
-Like most boot loaders, the EFI stub allows the user to specify
-multiple initrd files using the "initrd=" option. This is the only EFI
-stub-specific command line parameter, everything else is passed to the
-kernel when it boots.
-
-The path to the initrd file must be an absolute path from the
-beginning of the ESP, relative path names do not work. Also, the path
-is an EFI-style path and directory elements must be separated with
-backslashes (\). For example, given the following directory layout,
-
-fs0:>
-	Kernels\
-			bzImage.efi
-			initrd-large.img
-
-	Ramdisks\
-			initrd-small.img
-			initrd-medium.img
-
-to boot with the initrd-large.img file if the current working
-directory is fs0:\Kernels, the following command must be used,
-
-	fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img
-
-Notice how bzImage.efi can be specified with a relative path. That's
-because the image we're executing is interpreted by the EFI shell,
-which understands relative paths, whereas the rest of the command line
-is passed to bzImage.efi.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b32ebf9..ec65b51 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1579,7 +1579,7 @@ config EFI_STUB
           This kernel feature allows a bzImage to be loaded directly
 	  by EFI firmware without the use of a bootloader.
 
-	  See Documentation/x86/efi-stub.txt for more information.
+	  See Documentation/efi-stub.txt for more information.
 
 config SECCOMP
 	def_bool y
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 02/17] Move common EFI stub code from x86 arch code to common location
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:44   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

No code changes made, just moving functions from x86 arch directory
to common location.
Code is shared using #include, similar to how decompression code
is shared among architectures.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |  442 +-----------------------------
 arch/x86/boot/compressed/eboot.h       |    6 -
 drivers/firmware/efi/efi-stub-helper.c |  463 ++++++++++++++++++++++++++++++++
 include/linux/efi.h                    |    8 +
 4 files changed, 472 insertions(+), 447 deletions(-)
 create mode 100644 drivers/firmware/efi/efi-stub-helper.c

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index b7388a4..ab0eefc 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -19,214 +19,10 @@
 
 static efi_system_table_t *sys_table;
 
-static void efi_char16_printk(efi_char16_t *str)
-{
-	struct efi_simple_text_output_protocol *out;
-
-	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
-	efi_call_phys2(out->output_string, out, str);
-}
-
-static void efi_printk(char *str)
-{
-	char *s8;
-
-	for (s8 = str; *s8; s8++) {
-		efi_char16_t ch[2] = { 0 };
-
-		ch[0] = *s8;
-		if (*s8 == '\n') {
-			efi_char16_t nl[2] = { '\r', 0 };
-			efi_char16_printk(nl);
-		}
-
-		efi_char16_printk(ch);
-	}
-}
-
-static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
-			      unsigned long *desc_size)
-{
-	efi_memory_desc_t *m = NULL;
-	efi_status_t status;
-	unsigned long key;
-	u32 desc_version;
-
-	*map_size = sizeof(*m) * 32;
-again:
-	/*
-	 * Add an additional efi_memory_desc_t because we're doing an
-	 * allocation which may be in a new descriptor region.
-	 */
-	*map_size += sizeof(*m);
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
-				EFI_LOADER_DATA, *map_size, (void **)&m);
-	if (status != EFI_SUCCESS)
-		goto fail;
-
-	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
-				m, &key, desc_size, &desc_version);
-	if (status == EFI_BUFFER_TOO_SMALL) {
-		efi_call_phys1(sys_table->boottime->free_pool, m);
-		goto again;
-	}
-
-	if (status != EFI_SUCCESS)
-		efi_call_phys1(sys_table->boottime->free_pool, m);
-
-fail:
-	*map = m;
-	return status;
-}
-
-/*
- * Allocate at the highest possible address that is not above 'max'.
- */
-static efi_status_t high_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr, unsigned long max)
-{
-	unsigned long map_size, desc_size;
-	efi_memory_desc_t *map;
-	efi_status_t status;
-	unsigned long nr_pages;
-	u64 max_addr = 0;
-	int i;
-
-	status = __get_map(&map, &map_size, &desc_size);
-	if (status != EFI_SUCCESS)
-		goto fail;
-
-	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-again:
-	for (i = 0; i < map_size / desc_size; i++) {
-		efi_memory_desc_t *desc;
-		unsigned long m = (unsigned long)map;
-		u64 start, end;
-
-		desc = (efi_memory_desc_t *)(m + (i * desc_size));
-		if (desc->type != EFI_CONVENTIONAL_MEMORY)
-			continue;
-
-		if (desc->num_pages < nr_pages)
-			continue;
 
-		start = desc->phys_addr;
-		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
 
-		if ((start + size) > end || (start + size) > max)
-			continue;
-
-		if (end - size > max)
-			end = max;
-
-		if (round_down(end - size, align) < start)
-			continue;
-
-		start = round_down(end - size, align);
-
-		/*
-		 * Don't allocate at 0x0. It will confuse code that
-		 * checks pointers against NULL.
-		 */
-		if (start == 0x0)
-			continue;
-
-		if (start > max_addr)
-			max_addr = start;
-	}
-
-	if (!max_addr)
-		status = EFI_NOT_FOUND;
-	else {
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
-					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
-					nr_pages, &max_addr);
-		if (status != EFI_SUCCESS) {
-			max = max_addr;
-			max_addr = 0;
-			goto again;
-		}
-
-		*addr = max_addr;
-	}
-
-free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
-
-fail:
-	return status;
-}
-
-/*
- * Allocate at the lowest possible address.
- */
-static efi_status_t low_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr)
-{
-	unsigned long map_size, desc_size;
-	efi_memory_desc_t *map;
-	efi_status_t status;
-	unsigned long nr_pages;
-	int i;
-
-	status = __get_map(&map, &map_size, &desc_size);
-	if (status != EFI_SUCCESS)
-		goto fail;
-
-	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	for (i = 0; i < map_size / desc_size; i++) {
-		efi_memory_desc_t *desc;
-		unsigned long m = (unsigned long)map;
-		u64 start, end;
-
-		desc = (efi_memory_desc_t *)(m + (i * desc_size));
-
-		if (desc->type != EFI_CONVENTIONAL_MEMORY)
-			continue;
-
-		if (desc->num_pages < nr_pages)
-			continue;
-
-		start = desc->phys_addr;
-		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
-
-		/*
-		 * Don't allocate at 0x0. It will confuse code that
-		 * checks pointers against NULL. Skip the first 8
-		 * bytes so we start at a nice even number.
-		 */
-		if (start == 0x0)
-			start += 8;
-
-		start = round_up(start, align);
-		if ((start + size) > end)
-			continue;
-
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
-					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
-					nr_pages, &start);
-		if (status == EFI_SUCCESS) {
-			*addr = start;
-			break;
-		}
-	}
 
-	if (i == map_size / desc_size)
-		status = EFI_NOT_FOUND;
-
-free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
-fail:
-	return status;
-}
-
-static void low_free(unsigned long size, unsigned long addr)
-{
-	unsigned long nr_pages;
-
-	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
-}
 
 static void find_bits(unsigned long mask, u8 *pos, u8 *size)
 {
@@ -624,242 +420,6 @@ void setup_graphics(struct boot_params *boot_params)
 	}
 }
 
-struct initrd {
-	efi_file_handle_t *handle;
-	u64 size;
-};
-
-/*
- * Check the cmdline for a LILO-style initrd= arguments.
- *
- * We only support loading an initrd from the same filesystem as the
- * kernel image.
- */
-static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
-				    struct setup_header *hdr)
-{
-	struct initrd *initrds;
-	unsigned long initrd_addr;
-	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
-	u64 initrd_total;
-	efi_file_io_interface_t *io;
-	efi_file_handle_t *fh;
-	efi_status_t status;
-	int nr_initrds;
-	char *str;
-	int i, j, k;
-
-	initrd_addr = 0;
-	initrd_total = 0;
-
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
-
-	j = 0;			/* See close_handles */
-
-	if (!str || !*str)
-		return EFI_SUCCESS;
-
-	for (nr_initrds = 0; *str; nr_initrds++) {
-		str = strstr(str, "initrd=");
-		if (!str)
-			break;
-
-		str += 7;
-
-		/* Skip any leading slashes */
-		while (*str == '/' || *str == '\\')
-			str++;
-
-		while (*str && *str != ' ' && *str != '\n')
-			str++;
-	}
-
-	if (!nr_initrds)
-		return EFI_SUCCESS;
-
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
-				EFI_LOADER_DATA,
-				nr_initrds * sizeof(*initrds),
-				&initrds);
-	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for initrds\n");
-		goto fail;
-	}
-
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
-	for (i = 0; i < nr_initrds; i++) {
-		struct initrd *initrd;
-		efi_file_handle_t *h;
-		efi_file_info_t *info;
-		efi_char16_t filename_16[256];
-		unsigned long info_sz;
-		efi_guid_t info_guid = EFI_FILE_INFO_ID;
-		efi_char16_t *p;
-		u64 file_sz;
-
-		str = strstr(str, "initrd=");
-		if (!str)
-			break;
-
-		str += 7;
-
-		initrd = &initrds[i];
-		p = filename_16;
-
-		/* Skip any leading slashes */
-		while (*str == '/' || *str == '\\')
-			str++;
-
-		while (*str && *str != ' ' && *str != '\n') {
-			if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
-				break;
-
-			if (*str == '/') {
-				*p++ = '\\';
-				*str++;
-			} else {
-				*p++ = *str++;
-			}
-		}
-
-		*p = '\0';
-
-		/* Only open the volume once. */
-		if (!i) {
-			efi_boot_services_t *boottime;
-
-			boottime = sys_table->boottime;
-
-			status = efi_call_phys3(boottime->handle_protocol,
-					image->device_handle, &fs_proto, &io);
-			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to handle fs_proto\n");
-				goto free_initrds;
-			}
-
-			status = efi_call_phys2(io->open_volume, io, &fh);
-			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to open volume\n");
-				goto free_initrds;
-			}
-		}
-
-		status = efi_call_phys5(fh->open, fh, &h, filename_16,
-					EFI_FILE_MODE_READ, (u64)0);
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to open initrd file: ");
-			efi_char16_printk(filename_16);
-			efi_printk("\n");
-			goto close_handles;
-		}
-
-		initrd->handle = h;
-
-		info_sz = 0;
-		status = efi_call_phys4(h->get_info, h, &info_guid,
-					&info_sz, NULL);
-		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk("Failed to get initrd info size\n");
-			goto close_handles;
-		}
-
-grow:
-		status = efi_call_phys3(sys_table->boottime->allocate_pool,
-					EFI_LOADER_DATA, info_sz, &info);
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc mem for initrd info\n");
-			goto close_handles;
-		}
-
-		status = efi_call_phys4(h->get_info, h, &info_guid,
-					&info_sz, info);
-		if (status == EFI_BUFFER_TOO_SMALL) {
-			efi_call_phys1(sys_table->boottime->free_pool, info);
-			goto grow;
-		}
-
-		file_sz = info->file_size;
-		efi_call_phys1(sys_table->boottime->free_pool, info);
-
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to get initrd info\n");
-			goto close_handles;
-		}
-
-		initrd->size = file_sz;
-		initrd_total += file_sz;
-	}
-
-	if (initrd_total) {
-		unsigned long addr;
-
-		/*
-		 * Multiple initrd's need to be at consecutive
-		 * addresses in memory, so allocate enough memory for
-		 * all the initrd's.
-		 */
-		status = high_alloc(initrd_total, 0x1000,
-				   &initrd_addr, hdr->initrd_addr_max);
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc highmem for initrds\n");
-			goto close_handles;
-		}
-
-		/* We've run out of free low memory. */
-		if (initrd_addr > hdr->initrd_addr_max) {
-			efi_printk("We've run out of free low memory\n");
-			status = EFI_INVALID_PARAMETER;
-			goto free_initrd_total;
-		}
-
-		addr = initrd_addr;
-		for (j = 0; j < nr_initrds; j++) {
-			u64 size;
-
-			size = initrds[j].size;
-			while (size) {
-				u64 chunksize;
-				if (size > EFI_READ_CHUNK_SIZE)
-					chunksize = EFI_READ_CHUNK_SIZE;
-				else
-					chunksize = size;
-				status = efi_call_phys3(fh->read,
-							initrds[j].handle,
-							&chunksize, addr);
-				if (status != EFI_SUCCESS) {
-					efi_printk("Failed to read initrd\n");
-					goto free_initrd_total;
-				}
-				addr += chunksize;
-				size -= chunksize;
-			}
-
-			efi_call_phys1(fh->close, initrds[j].handle);
-		}
-
-	}
-
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
-
-	hdr->ramdisk_image = initrd_addr;
-	hdr->ramdisk_size = initrd_total;
-
-	return status;
-
-free_initrd_total:
-	low_free(initrd_total, initrd_addr);
-
-close_handles:
-	for (k = j; k < i; k++)
-		efi_call_phys1(fh->close, initrds[k].handle);
-free_initrds:
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
-fail:
-	hdr->ramdisk_image = 0;
-	hdr->ramdisk_size = 0;
-
-	return status;
-}
 
 /*
  * Because the x86 boot code expects to be passed a boot_params we
diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index e5b0a8f..faa0bdf 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -62,10 +62,4 @@ struct efi_uga_draw_protocol {
 	void *blt;
 };
 
-struct efi_simple_text_output_protocol {
-	void *reset;
-	void *output_string;
-	void *test_string;
-};
-
 #endif /* BOOT_COMPRESSED_EBOOT_H */
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
new file mode 100644
index 0000000..47891bd
--- /dev/null
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -0,0 +1,463 @@
+/*
+ * Helper functions used by the EFI stub on multiple
+ * architectures. This should be #included by the EFI stub
+ * implementation files.
+ *
+ * Copyright 2011 Intel Corporation; author Matt Fleming
+ *
+ * This file is part of the Linux kernel, and is made available
+ * under the terms of the GNU General Public License version 2.
+ *
+ */
+
+
+struct initrd {
+	efi_file_handle_t *handle;
+	u64 size;
+};
+
+
+
+
+static void efi_char16_printk(efi_char16_t *str)
+{
+	struct efi_simple_text_output_protocol *out;
+
+	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
+	efi_call_phys2(out->output_string, out, str);
+}
+
+static void efi_printk(char *str)
+{
+	char *s8;
+
+	for (s8 = str; *s8; s8++) {
+		efi_char16_t ch[2] = { 0 };
+
+		ch[0] = *s8;
+		if (*s8 == '\n') {
+			efi_char16_t nl[2] = { '\r', 0 };
+			efi_char16_printk(nl);
+		}
+
+		efi_char16_printk(ch);
+	}
+}
+
+
+static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
+			      unsigned long *desc_size)
+{
+	efi_memory_desc_t *m = NULL;
+	efi_status_t status;
+	unsigned long key;
+	u32 desc_version;
+
+	*map_size = sizeof(*m) * 32;
+again:
+	/*
+	 * Add an additional efi_memory_desc_t because we're doing an
+	 * allocation which may be in a new descriptor region.
+	 */
+	*map_size += sizeof(*m);
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, *map_size, (void **)&m);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
+				m, &key, desc_size, &desc_version);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		efi_call_phys1(sys_table->boottime->free_pool, m);
+		goto again;
+	}
+
+	if (status != EFI_SUCCESS)
+		efi_call_phys1(sys_table->boottime->free_pool, m);
+
+fail:
+	*map = m;
+	return status;
+}
+
+/*
+ * Allocate at the highest possible address that is not above 'max'.
+ */
+static efi_status_t high_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr, unsigned long max)
+{
+	unsigned long map_size, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	u64 max_addr = 0;
+	int i;
+
+	status = __get_map(&map, &map_size, &desc_size);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+again:
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)map;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		if ((start + size) > end || (start + size) > max)
+			continue;
+
+		if (end - size > max)
+			end = max;
+
+		if (round_down(end - size, align) < start)
+			continue;
+
+		start = round_down(end - size, align);
+
+		/*
+		 * Don't allocate at 0x0. It will confuse code that
+		 * checks pointers against NULL.
+		 */
+		if (start == 0x0)
+			continue;
+
+		if (start > max_addr)
+			max_addr = start;
+	}
+
+	if (!max_addr)
+		status = EFI_NOT_FOUND;
+	else {
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &max_addr);
+		if (status != EFI_SUCCESS) {
+			max = max_addr;
+			max_addr = 0;
+			goto again;
+		}
+
+		*addr = max_addr;
+	}
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+
+fail:
+	return status;
+}
+
+/*
+ * Allocate at the lowest possible address.
+ */
+static efi_status_t low_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr)
+{
+	unsigned long map_size, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	int i;
+
+	status = __get_map(&map, &map_size, &desc_size);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)map;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		/*
+		 * Don't allocate at 0x0. It will confuse code that
+		 * checks pointers against NULL. Skip the first 8
+		 * bytes so we start at a nice even number.
+		 */
+		if (start == 0x0)
+			start += 8;
+
+		start = round_up(start, align);
+		if ((start + size) > end)
+			continue;
+
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &start);
+		if (status == EFI_SUCCESS) {
+			*addr = start;
+			break;
+		}
+	}
+
+	if (i == map_size / desc_size)
+		status = EFI_NOT_FOUND;
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+fail:
+	return status;
+}
+
+static void low_free(unsigned long size, unsigned long addr)
+{
+	unsigned long nr_pages;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
+}
+
+
+/*
+ * Check the cmdline for a LILO-style initrd= arguments.
+ *
+ * We only support loading an initrd from the same filesystem as the
+ * kernel image.
+ */
+static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+				    struct setup_header *hdr)
+{
+	struct initrd *initrds;
+	unsigned long initrd_addr;
+	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
+	u64 initrd_total;
+	efi_file_io_interface_t *io;
+	efi_file_handle_t *fh;
+	efi_status_t status;
+	int nr_initrds;
+	char *str;
+	int i, j, k;
+
+	initrd_addr = 0;
+	initrd_total = 0;
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+
+	j = 0;			/* See close_handles */
+
+	if (!str || !*str)
+		return EFI_SUCCESS;
+
+	for (nr_initrds = 0; *str; nr_initrds++) {
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n')
+			str++;
+	}
+
+	if (!nr_initrds)
+		return EFI_SUCCESS;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA,
+				nr_initrds * sizeof(*initrds),
+				&initrds);
+	if (status != EFI_SUCCESS) {
+		efi_printk("Failed to alloc mem for initrds\n");
+		goto fail;
+	}
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	for (i = 0; i < nr_initrds; i++) {
+		struct initrd *initrd;
+		efi_file_handle_t *h;
+		efi_file_info_t *info;
+		efi_char16_t filename_16[256];
+		unsigned long info_sz;
+		efi_guid_t info_guid = EFI_FILE_INFO_ID;
+		efi_char16_t *p;
+		u64 file_sz;
+
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		initrd = &initrds[i];
+		p = filename_16;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n') {
+			if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
+				break;
+
+			if (*str == '/') {
+				*p++ = '\\';
+				*str++;
+			} else {
+				*p++ = *str++;
+			}
+		}
+
+		*p = '\0';
+
+		/* Only open the volume once. */
+		if (!i) {
+			efi_boot_services_t *boottime;
+
+			boottime = sys_table->boottime;
+
+			status = efi_call_phys3(boottime->handle_protocol,
+					image->device_handle, &fs_proto, &io);
+			if (status != EFI_SUCCESS) {
+				efi_printk("Failed to handle fs_proto\n");
+				goto free_initrds;
+			}
+
+			status = efi_call_phys2(io->open_volume, io, &fh);
+			if (status != EFI_SUCCESS) {
+				efi_printk("Failed to open volume\n");
+				goto free_initrds;
+			}
+		}
+
+		status = efi_call_phys5(fh->open, fh, &h, filename_16,
+					EFI_FILE_MODE_READ, (u64)0);
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to open initrd file: ");
+			efi_char16_printk(filename_16);
+			efi_printk("\n");
+			goto close_handles;
+		}
+
+		initrd->handle = h;
+
+		info_sz = 0;
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, NULL);
+		if (status != EFI_BUFFER_TOO_SMALL) {
+			efi_printk("Failed to get initrd info size\n");
+			goto close_handles;
+		}
+
+grow:
+		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+					EFI_LOADER_DATA, info_sz, &info);
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to alloc mem for initrd info\n");
+			goto close_handles;
+		}
+
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, info);
+		if (status == EFI_BUFFER_TOO_SMALL) {
+			efi_call_phys1(sys_table->boottime->free_pool, info);
+			goto grow;
+		}
+
+		file_sz = info->file_size;
+		efi_call_phys1(sys_table->boottime->free_pool, info);
+
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to get initrd info\n");
+			goto close_handles;
+		}
+
+		initrd->size = file_sz;
+		initrd_total += file_sz;
+	}
+
+	if (initrd_total) {
+		unsigned long addr;
+
+		/*
+		 * Multiple initrd's need to be at consecutive
+		 * addresses in memory, so allocate enough memory for
+		 * all the initrd's.
+		 */
+		status = high_alloc(initrd_total, 0x1000,
+				   &initrd_addr, hdr->initrd_addr_max);
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to alloc highmem for initrds\n");
+			goto close_handles;
+		}
+
+		/* We've run out of free low memory. */
+		if (initrd_addr > hdr->initrd_addr_max) {
+			efi_printk("We've run out of free low memory\n");
+			status = EFI_INVALID_PARAMETER;
+			goto free_initrd_total;
+		}
+
+		addr = initrd_addr;
+		for (j = 0; j < nr_initrds; j++) {
+			u64 size;
+
+			size = initrds[j].size;
+			while (size) {
+				u64 chunksize;
+				if (size > EFI_READ_CHUNK_SIZE)
+					chunksize = EFI_READ_CHUNK_SIZE;
+				else
+					chunksize = size;
+				status = efi_call_phys3(fh->read,
+							initrds[j].handle,
+							&chunksize, addr);
+				if (status != EFI_SUCCESS) {
+					efi_printk("Failed to read initrd\n");
+					goto free_initrd_total;
+				}
+				addr += chunksize;
+				size -= chunksize;
+			}
+
+			efi_call_phys1(fh->close, initrds[j].handle);
+		}
+
+	}
+
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+
+	hdr->ramdisk_image = initrd_addr;
+	hdr->ramdisk_size = initrd_total;
+
+	return status;
+
+free_initrd_total:
+	low_free(initrd_total, initrd_addr);
+
+close_handles:
+	for (k = j; k < i; k++)
+		efi_call_phys1(fh->close, initrds[k].handle);
+free_initrds:
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+fail:
+	hdr->ramdisk_image = 0;
+	hdr->ramdisk_size = 0;
+
+	return status;
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5f8f176..51f5641 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -784,6 +784,14 @@ struct efivar_entry {
 	struct kobject kobj;
 };
 
+
+struct efi_simple_text_output_protocol {
+	void *reset;
+	void *output_string;
+	void *test_string;
+};
+
+
 extern struct list_head efivar_sysfs_list;
 
 static inline void
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 02/17] Move common EFI stub code from x86 arch code to common location
@ 2013-08-07  3:44   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

No code changes made, just moving functions from x86 arch directory
to common location.
Code is shared using #include, similar to how decompression code
is shared among architectures.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |  442 +-----------------------------
 arch/x86/boot/compressed/eboot.h       |    6 -
 drivers/firmware/efi/efi-stub-helper.c |  463 ++++++++++++++++++++++++++++++++
 include/linux/efi.h                    |    8 +
 4 files changed, 472 insertions(+), 447 deletions(-)
 create mode 100644 drivers/firmware/efi/efi-stub-helper.c

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index b7388a4..ab0eefc 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -19,214 +19,10 @@
 
 static efi_system_table_t *sys_table;
 
-static void efi_char16_printk(efi_char16_t *str)
-{
-	struct efi_simple_text_output_protocol *out;
-
-	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
-	efi_call_phys2(out->output_string, out, str);
-}
-
-static void efi_printk(char *str)
-{
-	char *s8;
-
-	for (s8 = str; *s8; s8++) {
-		efi_char16_t ch[2] = { 0 };
-
-		ch[0] = *s8;
-		if (*s8 == '\n') {
-			efi_char16_t nl[2] = { '\r', 0 };
-			efi_char16_printk(nl);
-		}
-
-		efi_char16_printk(ch);
-	}
-}
-
-static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
-			      unsigned long *desc_size)
-{
-	efi_memory_desc_t *m = NULL;
-	efi_status_t status;
-	unsigned long key;
-	u32 desc_version;
-
-	*map_size = sizeof(*m) * 32;
-again:
-	/*
-	 * Add an additional efi_memory_desc_t because we're doing an
-	 * allocation which may be in a new descriptor region.
-	 */
-	*map_size += sizeof(*m);
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
-				EFI_LOADER_DATA, *map_size, (void **)&m);
-	if (status != EFI_SUCCESS)
-		goto fail;
-
-	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
-				m, &key, desc_size, &desc_version);
-	if (status == EFI_BUFFER_TOO_SMALL) {
-		efi_call_phys1(sys_table->boottime->free_pool, m);
-		goto again;
-	}
-
-	if (status != EFI_SUCCESS)
-		efi_call_phys1(sys_table->boottime->free_pool, m);
-
-fail:
-	*map = m;
-	return status;
-}
-
-/*
- * Allocate at the highest possible address that is not above 'max'.
- */
-static efi_status_t high_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr, unsigned long max)
-{
-	unsigned long map_size, desc_size;
-	efi_memory_desc_t *map;
-	efi_status_t status;
-	unsigned long nr_pages;
-	u64 max_addr = 0;
-	int i;
-
-	status = __get_map(&map, &map_size, &desc_size);
-	if (status != EFI_SUCCESS)
-		goto fail;
-
-	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-again:
-	for (i = 0; i < map_size / desc_size; i++) {
-		efi_memory_desc_t *desc;
-		unsigned long m = (unsigned long)map;
-		u64 start, end;
-
-		desc = (efi_memory_desc_t *)(m + (i * desc_size));
-		if (desc->type != EFI_CONVENTIONAL_MEMORY)
-			continue;
-
-		if (desc->num_pages < nr_pages)
-			continue;
 
-		start = desc->phys_addr;
-		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
 
-		if ((start + size) > end || (start + size) > max)
-			continue;
-
-		if (end - size > max)
-			end = max;
-
-		if (round_down(end - size, align) < start)
-			continue;
-
-		start = round_down(end - size, align);
-
-		/*
-		 * Don't allocate@0x0. It will confuse code that
-		 * checks pointers against NULL.
-		 */
-		if (start == 0x0)
-			continue;
-
-		if (start > max_addr)
-			max_addr = start;
-	}
-
-	if (!max_addr)
-		status = EFI_NOT_FOUND;
-	else {
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
-					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
-					nr_pages, &max_addr);
-		if (status != EFI_SUCCESS) {
-			max = max_addr;
-			max_addr = 0;
-			goto again;
-		}
-
-		*addr = max_addr;
-	}
-
-free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
-
-fail:
-	return status;
-}
-
-/*
- * Allocate at the lowest possible address.
- */
-static efi_status_t low_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr)
-{
-	unsigned long map_size, desc_size;
-	efi_memory_desc_t *map;
-	efi_status_t status;
-	unsigned long nr_pages;
-	int i;
-
-	status = __get_map(&map, &map_size, &desc_size);
-	if (status != EFI_SUCCESS)
-		goto fail;
-
-	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	for (i = 0; i < map_size / desc_size; i++) {
-		efi_memory_desc_t *desc;
-		unsigned long m = (unsigned long)map;
-		u64 start, end;
-
-		desc = (efi_memory_desc_t *)(m + (i * desc_size));
-
-		if (desc->type != EFI_CONVENTIONAL_MEMORY)
-			continue;
-
-		if (desc->num_pages < nr_pages)
-			continue;
-
-		start = desc->phys_addr;
-		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
-
-		/*
-		 * Don't allocate at 0x0. It will confuse code that
-		 * checks pointers against NULL. Skip the first 8
-		 * bytes so we start@a nice even number.
-		 */
-		if (start == 0x0)
-			start += 8;
-
-		start = round_up(start, align);
-		if ((start + size) > end)
-			continue;
-
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
-					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
-					nr_pages, &start);
-		if (status == EFI_SUCCESS) {
-			*addr = start;
-			break;
-		}
-	}
 
-	if (i == map_size / desc_size)
-		status = EFI_NOT_FOUND;
-
-free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
-fail:
-	return status;
-}
-
-static void low_free(unsigned long size, unsigned long addr)
-{
-	unsigned long nr_pages;
-
-	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
-}
 
 static void find_bits(unsigned long mask, u8 *pos, u8 *size)
 {
@@ -624,242 +420,6 @@ void setup_graphics(struct boot_params *boot_params)
 	}
 }
 
-struct initrd {
-	efi_file_handle_t *handle;
-	u64 size;
-};
-
-/*
- * Check the cmdline for a LILO-style initrd= arguments.
- *
- * We only support loading an initrd from the same filesystem as the
- * kernel image.
- */
-static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
-				    struct setup_header *hdr)
-{
-	struct initrd *initrds;
-	unsigned long initrd_addr;
-	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
-	u64 initrd_total;
-	efi_file_io_interface_t *io;
-	efi_file_handle_t *fh;
-	efi_status_t status;
-	int nr_initrds;
-	char *str;
-	int i, j, k;
-
-	initrd_addr = 0;
-	initrd_total = 0;
-
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
-
-	j = 0;			/* See close_handles */
-
-	if (!str || !*str)
-		return EFI_SUCCESS;
-
-	for (nr_initrds = 0; *str; nr_initrds++) {
-		str = strstr(str, "initrd=");
-		if (!str)
-			break;
-
-		str += 7;
-
-		/* Skip any leading slashes */
-		while (*str == '/' || *str == '\\')
-			str++;
-
-		while (*str && *str != ' ' && *str != '\n')
-			str++;
-	}
-
-	if (!nr_initrds)
-		return EFI_SUCCESS;
-
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
-				EFI_LOADER_DATA,
-				nr_initrds * sizeof(*initrds),
-				&initrds);
-	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for initrds\n");
-		goto fail;
-	}
-
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
-	for (i = 0; i < nr_initrds; i++) {
-		struct initrd *initrd;
-		efi_file_handle_t *h;
-		efi_file_info_t *info;
-		efi_char16_t filename_16[256];
-		unsigned long info_sz;
-		efi_guid_t info_guid = EFI_FILE_INFO_ID;
-		efi_char16_t *p;
-		u64 file_sz;
-
-		str = strstr(str, "initrd=");
-		if (!str)
-			break;
-
-		str += 7;
-
-		initrd = &initrds[i];
-		p = filename_16;
-
-		/* Skip any leading slashes */
-		while (*str == '/' || *str == '\\')
-			str++;
-
-		while (*str && *str != ' ' && *str != '\n') {
-			if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
-				break;
-
-			if (*str == '/') {
-				*p++ = '\\';
-				*str++;
-			} else {
-				*p++ = *str++;
-			}
-		}
-
-		*p = '\0';
-
-		/* Only open the volume once. */
-		if (!i) {
-			efi_boot_services_t *boottime;
-
-			boottime = sys_table->boottime;
-
-			status = efi_call_phys3(boottime->handle_protocol,
-					image->device_handle, &fs_proto, &io);
-			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to handle fs_proto\n");
-				goto free_initrds;
-			}
-
-			status = efi_call_phys2(io->open_volume, io, &fh);
-			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to open volume\n");
-				goto free_initrds;
-			}
-		}
-
-		status = efi_call_phys5(fh->open, fh, &h, filename_16,
-					EFI_FILE_MODE_READ, (u64)0);
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to open initrd file: ");
-			efi_char16_printk(filename_16);
-			efi_printk("\n");
-			goto close_handles;
-		}
-
-		initrd->handle = h;
-
-		info_sz = 0;
-		status = efi_call_phys4(h->get_info, h, &info_guid,
-					&info_sz, NULL);
-		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk("Failed to get initrd info size\n");
-			goto close_handles;
-		}
-
-grow:
-		status = efi_call_phys3(sys_table->boottime->allocate_pool,
-					EFI_LOADER_DATA, info_sz, &info);
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc mem for initrd info\n");
-			goto close_handles;
-		}
-
-		status = efi_call_phys4(h->get_info, h, &info_guid,
-					&info_sz, info);
-		if (status == EFI_BUFFER_TOO_SMALL) {
-			efi_call_phys1(sys_table->boottime->free_pool, info);
-			goto grow;
-		}
-
-		file_sz = info->file_size;
-		efi_call_phys1(sys_table->boottime->free_pool, info);
-
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to get initrd info\n");
-			goto close_handles;
-		}
-
-		initrd->size = file_sz;
-		initrd_total += file_sz;
-	}
-
-	if (initrd_total) {
-		unsigned long addr;
-
-		/*
-		 * Multiple initrd's need to be at consecutive
-		 * addresses in memory, so allocate enough memory for
-		 * all the initrd's.
-		 */
-		status = high_alloc(initrd_total, 0x1000,
-				   &initrd_addr, hdr->initrd_addr_max);
-		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc highmem for initrds\n");
-			goto close_handles;
-		}
-
-		/* We've run out of free low memory. */
-		if (initrd_addr > hdr->initrd_addr_max) {
-			efi_printk("We've run out of free low memory\n");
-			status = EFI_INVALID_PARAMETER;
-			goto free_initrd_total;
-		}
-
-		addr = initrd_addr;
-		for (j = 0; j < nr_initrds; j++) {
-			u64 size;
-
-			size = initrds[j].size;
-			while (size) {
-				u64 chunksize;
-				if (size > EFI_READ_CHUNK_SIZE)
-					chunksize = EFI_READ_CHUNK_SIZE;
-				else
-					chunksize = size;
-				status = efi_call_phys3(fh->read,
-							initrds[j].handle,
-							&chunksize, addr);
-				if (status != EFI_SUCCESS) {
-					efi_printk("Failed to read initrd\n");
-					goto free_initrd_total;
-				}
-				addr += chunksize;
-				size -= chunksize;
-			}
-
-			efi_call_phys1(fh->close, initrds[j].handle);
-		}
-
-	}
-
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
-
-	hdr->ramdisk_image = initrd_addr;
-	hdr->ramdisk_size = initrd_total;
-
-	return status;
-
-free_initrd_total:
-	low_free(initrd_total, initrd_addr);
-
-close_handles:
-	for (k = j; k < i; k++)
-		efi_call_phys1(fh->close, initrds[k].handle);
-free_initrds:
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
-fail:
-	hdr->ramdisk_image = 0;
-	hdr->ramdisk_size = 0;
-
-	return status;
-}
 
 /*
  * Because the x86 boot code expects to be passed a boot_params we
diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index e5b0a8f..faa0bdf 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -62,10 +62,4 @@ struct efi_uga_draw_protocol {
 	void *blt;
 };
 
-struct efi_simple_text_output_protocol {
-	void *reset;
-	void *output_string;
-	void *test_string;
-};
-
 #endif /* BOOT_COMPRESSED_EBOOT_H */
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
new file mode 100644
index 0000000..47891bd
--- /dev/null
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -0,0 +1,463 @@
+/*
+ * Helper functions used by the EFI stub on multiple
+ * architectures. This should be #included by the EFI stub
+ * implementation files.
+ *
+ * Copyright 2011 Intel Corporation; author Matt Fleming
+ *
+ * This file is part of the Linux kernel, and is made available
+ * under the terms of the GNU General Public License version 2.
+ *
+ */
+
+
+struct initrd {
+	efi_file_handle_t *handle;
+	u64 size;
+};
+
+
+
+
+static void efi_char16_printk(efi_char16_t *str)
+{
+	struct efi_simple_text_output_protocol *out;
+
+	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
+	efi_call_phys2(out->output_string, out, str);
+}
+
+static void efi_printk(char *str)
+{
+	char *s8;
+
+	for (s8 = str; *s8; s8++) {
+		efi_char16_t ch[2] = { 0 };
+
+		ch[0] = *s8;
+		if (*s8 == '\n') {
+			efi_char16_t nl[2] = { '\r', 0 };
+			efi_char16_printk(nl);
+		}
+
+		efi_char16_printk(ch);
+	}
+}
+
+
+static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
+			      unsigned long *desc_size)
+{
+	efi_memory_desc_t *m = NULL;
+	efi_status_t status;
+	unsigned long key;
+	u32 desc_version;
+
+	*map_size = sizeof(*m) * 32;
+again:
+	/*
+	 * Add an additional efi_memory_desc_t because we're doing an
+	 * allocation which may be in a new descriptor region.
+	 */
+	*map_size += sizeof(*m);
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, *map_size, (void **)&m);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
+				m, &key, desc_size, &desc_version);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		efi_call_phys1(sys_table->boottime->free_pool, m);
+		goto again;
+	}
+
+	if (status != EFI_SUCCESS)
+		efi_call_phys1(sys_table->boottime->free_pool, m);
+
+fail:
+	*map = m;
+	return status;
+}
+
+/*
+ * Allocate at the highest possible address that is not above 'max'.
+ */
+static efi_status_t high_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr, unsigned long max)
+{
+	unsigned long map_size, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	u64 max_addr = 0;
+	int i;
+
+	status = __get_map(&map, &map_size, &desc_size);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+again:
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)map;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		if ((start + size) > end || (start + size) > max)
+			continue;
+
+		if (end - size > max)
+			end = max;
+
+		if (round_down(end - size, align) < start)
+			continue;
+
+		start = round_down(end - size, align);
+
+		/*
+		 * Don't allocate@0x0. It will confuse code that
+		 * checks pointers against NULL.
+		 */
+		if (start == 0x0)
+			continue;
+
+		if (start > max_addr)
+			max_addr = start;
+	}
+
+	if (!max_addr)
+		status = EFI_NOT_FOUND;
+	else {
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &max_addr);
+		if (status != EFI_SUCCESS) {
+			max = max_addr;
+			max_addr = 0;
+			goto again;
+		}
+
+		*addr = max_addr;
+	}
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+
+fail:
+	return status;
+}
+
+/*
+ * Allocate at the lowest possible address.
+ */
+static efi_status_t low_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr)
+{
+	unsigned long map_size, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	int i;
+
+	status = __get_map(&map, &map_size, &desc_size);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)map;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		/*
+		 * Don't allocate at 0x0. It will confuse code that
+		 * checks pointers against NULL. Skip the first 8
+		 * bytes so we start@a nice even number.
+		 */
+		if (start == 0x0)
+			start += 8;
+
+		start = round_up(start, align);
+		if ((start + size) > end)
+			continue;
+
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &start);
+		if (status == EFI_SUCCESS) {
+			*addr = start;
+			break;
+		}
+	}
+
+	if (i == map_size / desc_size)
+		status = EFI_NOT_FOUND;
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+fail:
+	return status;
+}
+
+static void low_free(unsigned long size, unsigned long addr)
+{
+	unsigned long nr_pages;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
+}
+
+
+/*
+ * Check the cmdline for a LILO-style initrd= arguments.
+ *
+ * We only support loading an initrd from the same filesystem as the
+ * kernel image.
+ */
+static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+				    struct setup_header *hdr)
+{
+	struct initrd *initrds;
+	unsigned long initrd_addr;
+	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
+	u64 initrd_total;
+	efi_file_io_interface_t *io;
+	efi_file_handle_t *fh;
+	efi_status_t status;
+	int nr_initrds;
+	char *str;
+	int i, j, k;
+
+	initrd_addr = 0;
+	initrd_total = 0;
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+
+	j = 0;			/* See close_handles */
+
+	if (!str || !*str)
+		return EFI_SUCCESS;
+
+	for (nr_initrds = 0; *str; nr_initrds++) {
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n')
+			str++;
+	}
+
+	if (!nr_initrds)
+		return EFI_SUCCESS;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA,
+				nr_initrds * sizeof(*initrds),
+				&initrds);
+	if (status != EFI_SUCCESS) {
+		efi_printk("Failed to alloc mem for initrds\n");
+		goto fail;
+	}
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	for (i = 0; i < nr_initrds; i++) {
+		struct initrd *initrd;
+		efi_file_handle_t *h;
+		efi_file_info_t *info;
+		efi_char16_t filename_16[256];
+		unsigned long info_sz;
+		efi_guid_t info_guid = EFI_FILE_INFO_ID;
+		efi_char16_t *p;
+		u64 file_sz;
+
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		initrd = &initrds[i];
+		p = filename_16;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n') {
+			if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
+				break;
+
+			if (*str == '/') {
+				*p++ = '\\';
+				*str++;
+			} else {
+				*p++ = *str++;
+			}
+		}
+
+		*p = '\0';
+
+		/* Only open the volume once. */
+		if (!i) {
+			efi_boot_services_t *boottime;
+
+			boottime = sys_table->boottime;
+
+			status = efi_call_phys3(boottime->handle_protocol,
+					image->device_handle, &fs_proto, &io);
+			if (status != EFI_SUCCESS) {
+				efi_printk("Failed to handle fs_proto\n");
+				goto free_initrds;
+			}
+
+			status = efi_call_phys2(io->open_volume, io, &fh);
+			if (status != EFI_SUCCESS) {
+				efi_printk("Failed to open volume\n");
+				goto free_initrds;
+			}
+		}
+
+		status = efi_call_phys5(fh->open, fh, &h, filename_16,
+					EFI_FILE_MODE_READ, (u64)0);
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to open initrd file: ");
+			efi_char16_printk(filename_16);
+			efi_printk("\n");
+			goto close_handles;
+		}
+
+		initrd->handle = h;
+
+		info_sz = 0;
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, NULL);
+		if (status != EFI_BUFFER_TOO_SMALL) {
+			efi_printk("Failed to get initrd info size\n");
+			goto close_handles;
+		}
+
+grow:
+		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+					EFI_LOADER_DATA, info_sz, &info);
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to alloc mem for initrd info\n");
+			goto close_handles;
+		}
+
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, info);
+		if (status == EFI_BUFFER_TOO_SMALL) {
+			efi_call_phys1(sys_table->boottime->free_pool, info);
+			goto grow;
+		}
+
+		file_sz = info->file_size;
+		efi_call_phys1(sys_table->boottime->free_pool, info);
+
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to get initrd info\n");
+			goto close_handles;
+		}
+
+		initrd->size = file_sz;
+		initrd_total += file_sz;
+	}
+
+	if (initrd_total) {
+		unsigned long addr;
+
+		/*
+		 * Multiple initrd's need to be at consecutive
+		 * addresses in memory, so allocate enough memory for
+		 * all the initrd's.
+		 */
+		status = high_alloc(initrd_total, 0x1000,
+				   &initrd_addr, hdr->initrd_addr_max);
+		if (status != EFI_SUCCESS) {
+			efi_printk("Failed to alloc highmem for initrds\n");
+			goto close_handles;
+		}
+
+		/* We've run out of free low memory. */
+		if (initrd_addr > hdr->initrd_addr_max) {
+			efi_printk("We've run out of free low memory\n");
+			status = EFI_INVALID_PARAMETER;
+			goto free_initrd_total;
+		}
+
+		addr = initrd_addr;
+		for (j = 0; j < nr_initrds; j++) {
+			u64 size;
+
+			size = initrds[j].size;
+			while (size) {
+				u64 chunksize;
+				if (size > EFI_READ_CHUNK_SIZE)
+					chunksize = EFI_READ_CHUNK_SIZE;
+				else
+					chunksize = size;
+				status = efi_call_phys3(fh->read,
+							initrds[j].handle,
+							&chunksize, addr);
+				if (status != EFI_SUCCESS) {
+					efi_printk("Failed to read initrd\n");
+					goto free_initrd_total;
+				}
+				addr += chunksize;
+				size -= chunksize;
+			}
+
+			efi_call_phys1(fh->close, initrds[j].handle);
+		}
+
+	}
+
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+
+	hdr->ramdisk_image = initrd_addr;
+	hdr->ramdisk_size = initrd_total;
+
+	return status;
+
+free_initrd_total:
+	low_free(initrd_total, initrd_addr);
+
+close_handles:
+	for (k = j; k < i; k++)
+		efi_call_phys1(fh->close, initrds[k].handle);
+free_initrds:
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+fail:
+	hdr->ramdisk_image = 0;
+	hdr->ramdisk_size = 0;
+
+	return status;
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5f8f176..51f5641 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -784,6 +784,14 @@ struct efivar_entry {
 	struct kobject kobj;
 };
 
+
+struct efi_simple_text_output_protocol {
+	void *reset;
+	void *output_string;
+	void *test_string;
+};
+
+
 extern struct list_head efivar_sysfs_list;
 
 static inline void
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
  2013-08-07  3:44 ` Roy Franz
  (?)
@ 2013-08-07  3:44   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   38 +++++++------
 drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
 2 files changed, 72 insertions(+), 62 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index ab0eefc..65b6a34 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -453,13 +453,13 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	status = efi_call_phys3(sys_table->boottime->handle_protocol,
 				handle, &proto, (void *)&image);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
 		return NULL;
 	}
 
-	status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
+	status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc lowmem for boot params\n");
+		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
 	}
 
@@ -503,9 +503,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 			options_size++;	/* NUL termination */
 
-			status = low_alloc(options_size, 1, &cmdline);
+			status = low_alloc(sys_table, options_size, 1,
+					   &cmdline);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to alloc mem for cmdline\n");
+				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
 				goto fail;
 			}
 
@@ -529,16 +530,16 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	status = handle_ramdisks(image, hdr);
+	status = handle_ramdisks(sys_table, image, hdr);
 	if (status != EFI_SUCCESS)
 		goto fail2;
 
 	return boot_params;
 fail2:
 	if (options_size)
-		low_free(options_size, hdr->cmd_line_ptr);
+		low_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
-	low_free(0x4000, (unsigned long)boot_params);
+	low_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
 }
 
@@ -561,7 +562,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = low_alloc(size, 1, (unsigned long *)&mem_map);
+	status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -569,7 +570,7 @@ get_map:
 	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(_size, (unsigned long)mem_map);
+		low_free(sys_table, _size, (unsigned long)mem_map);
 		goto again;
 	}
 
@@ -671,7 +672,7 @@ get_map:
 	return EFI_SUCCESS;
 
 free_mem_map:
-	low_free(_size, (unsigned long)mem_map);
+	low_free(sys_table, _size, (unsigned long)mem_map);
 	return status;
 }
 
@@ -694,10 +695,10 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
-		status = low_alloc(hdr->init_size, hdr->kernel_alignment,
-				   &start);
+		status = low_alloc(sys_table, hdr->init_size,
+				   hdr->kernel_alignment, &start);
 		if (status != EFI_SUCCESS)
-			efi_printk("Failed to alloc mem for kernel\n");
+			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
 	}
 
 	if (status == EFI_SUCCESS)
@@ -737,14 +738,15 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 				EFI_LOADER_DATA, sizeof(*gdt),
 				(void **)&gdt);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for gdt structure\n");
+		efi_printk(sys_table, "Failed to alloc mem for gdt structure\n");
 		goto fail;
 	}
 
 	gdt->size = 0x800;
-	status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
+	status = low_alloc(sys_table, gdt->size, 8,
+			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for gdt\n");
+		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
 		goto fail;
 	}
 
@@ -752,7 +754,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 				EFI_LOADER_DATA, sizeof(*idt),
 				(void **)&idt);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for idt structure\n");
+		efi_printk(sys_table, "Failed to alloc mem for idt structure\n");
 		goto fail;
 	}
 
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 47891bd..bd6c1a2 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -19,15 +19,16 @@ struct initrd {
 
 
 
-static void efi_char16_printk(efi_char16_t *str)
+static void efi_char16_printk(efi_system_table_t *sys_table_arg,
+			      efi_char16_t *str)
 {
 	struct efi_simple_text_output_protocol *out;
 
-	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
+	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
 	efi_call_phys2(out->output_string, out, str);
 }
 
-static void efi_printk(char *str)
+static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 {
 	char *s8;
 
@@ -37,15 +38,17 @@ static void efi_printk(char *str)
 		ch[0] = *s8;
 		if (*s8 == '\n') {
 			efi_char16_t nl[2] = { '\r', 0 };
-			efi_char16_printk(nl);
+			efi_char16_printk(sys_table_arg, nl);
 		}
 
-		efi_char16_printk(ch);
+		efi_char16_printk(sys_table_arg, ch);
 	}
 }
 
 
-static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
+static efi_status_t __get_map(efi_system_table_t *sys_table_arg,
+			      efi_memory_desc_t **map,
+			      unsigned long *map_size,
 			      unsigned long *desc_size)
 {
 	efi_memory_desc_t *m = NULL;
@@ -60,20 +63,20 @@ again:
 	 * allocation which may be in a new descriptor region.
 	 */
 	*map_size += sizeof(*m);
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA, *map_size, (void **)&m);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
-	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
-				m, &key, desc_size, &desc_version);
+	status = efi_call_phys5(sys_table_arg->boottime->get_memory_map,
+				map_size, m, &key, desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		efi_call_phys1(sys_table->boottime->free_pool, m);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
 		goto again;
 	}
 
 	if (status != EFI_SUCCESS)
-		efi_call_phys1(sys_table->boottime->free_pool, m);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
 
 fail:
 	*map = m;
@@ -83,8 +86,9 @@ fail:
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t high_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr, unsigned long max)
+static efi_status_t high_alloc(efi_system_table_t *sys_table_arg,
+			       unsigned long size, unsigned long align,
+			       unsigned long *addr, unsigned long max)
 {
 	unsigned long map_size, desc_size;
 	efi_memory_desc_t *map;
@@ -93,7 +97,7 @@ static efi_status_t high_alloc(unsigned long size, unsigned long align,
 	u64 max_addr = 0;
 	int i;
 
-	status = __get_map(&map, &map_size, &desc_size);
+	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -139,7 +143,7 @@ again:
 	if (!max_addr)
 		status = EFI_NOT_FOUND;
 	else {
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+		status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
 					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 					nr_pages, &max_addr);
 		if (status != EFI_SUCCESS) {
@@ -152,7 +156,7 @@ again:
 	}
 
 free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 
 fail:
 	return status;
@@ -161,7 +165,8 @@ fail:
 /*
  * Allocate at the lowest possible address.
  */
-static efi_status_t low_alloc(unsigned long size, unsigned long align,
+static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
+		unsigned long size, unsigned long align,
 			      unsigned long *addr)
 {
 	unsigned long map_size, desc_size;
@@ -170,7 +175,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 	unsigned long nr_pages;
 	int i;
 
-	status = __get_map(&map, &map_size, &desc_size);
+	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -203,7 +208,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 		if ((start + size) > end)
 			continue;
 
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+		status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
 					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 					nr_pages, &start);
 		if (status == EFI_SUCCESS) {
@@ -216,17 +221,18 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 		status = EFI_NOT_FOUND;
 
 free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 fail:
 	return status;
 }
 
-static void low_free(unsigned long size, unsigned long addr)
+static void low_free(efi_system_table_t *sys_table_arg, unsigned long size,
+		     unsigned long addr)
 {
 	unsigned long nr_pages;
 
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
+	efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages);
 }
 
 
@@ -236,7 +242,8 @@ static void low_free(unsigned long size, unsigned long addr)
  * We only support loading an initrd from the same filesystem as the
  * kernel image.
  */
-static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
+				    efi_loaded_image_t *image,
 				    struct setup_header *hdr)
 {
 	struct initrd *initrds;
@@ -278,12 +285,12 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 	if (!nr_initrds)
 		return EFI_SUCCESS;
 
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
 				nr_initrds * sizeof(*initrds),
 				&initrds);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for initrds\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n");
 		goto fail;
 	}
 
@@ -329,18 +336,18 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		if (!i) {
 			efi_boot_services_t *boottime;
 
-			boottime = sys_table->boottime;
+			boottime = sys_table_arg->boottime;
 
 			status = efi_call_phys3(boottime->handle_protocol,
 					image->device_handle, &fs_proto, &io);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to handle fs_proto\n");
+				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
 				goto free_initrds;
 			}
 
 			status = efi_call_phys2(io->open_volume, io, &fh);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to open volume\n");
+				efi_printk(sys_table_arg, "Failed to open volume\n");
 				goto free_initrds;
 			}
 		}
@@ -348,9 +355,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		status = efi_call_phys5(fh->open, fh, &h, filename_16,
 					EFI_FILE_MODE_READ, (u64)0);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to open initrd file: ");
-			efi_char16_printk(filename_16);
-			efi_printk("\n");
+			efi_printk(sys_table_arg, "Failed to open initrd file: ");
+			efi_char16_printk(sys_table_arg, filename_16);
+			efi_printk(sys_table_arg, "\n");
 			goto close_handles;
 		}
 
@@ -360,30 +367,31 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, NULL);
 		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk("Failed to get initrd info size\n");
+			efi_printk(sys_table_arg, "Failed to get initrd info size\n");
 			goto close_handles;
 		}
 
 grow:
-		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 					EFI_LOADER_DATA, info_sz, &info);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc mem for initrd info\n");
+			efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n");
 			goto close_handles;
 		}
 
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, info);
 		if (status == EFI_BUFFER_TOO_SMALL) {
-			efi_call_phys1(sys_table->boottime->free_pool, info);
+			efi_call_phys1(sys_table_arg->boottime->free_pool,
+				       info);
 			goto grow;
 		}
 
 		file_sz = info->file_size;
-		efi_call_phys1(sys_table->boottime->free_pool, info);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, info);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to get initrd info\n");
+			efi_printk(sys_table_arg, "Failed to get initrd info\n");
 			goto close_handles;
 		}
 
@@ -399,16 +407,16 @@ grow:
 		 * addresses in memory, so allocate enough memory for
 		 * all the initrd's.
 		 */
-		status = high_alloc(initrd_total, 0x1000,
+		status = high_alloc(sys_table_arg, initrd_total, 0x1000,
 				   &initrd_addr, hdr->initrd_addr_max);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc highmem for initrds\n");
+			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
 		if (initrd_addr > hdr->initrd_addr_max) {
-			efi_printk("We've run out of free low memory\n");
+			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_initrd_total;
 		}
@@ -428,7 +436,7 @@ grow:
 							initrds[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
-					efi_printk("Failed to read initrd\n");
+					efi_printk(sys_table_arg, "Failed to read initrd\n");
 					goto free_initrd_total;
 				}
 				addr += chunksize;
@@ -440,7 +448,7 @@ grow:
 
 	}
 
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 
 	hdr->ramdisk_image = initrd_addr;
 	hdr->ramdisk_size = initrd_total;
@@ -448,13 +456,13 @@ grow:
 	return status;
 
 free_initrd_total:
-	low_free(initrd_total, initrd_addr);
+	low_free(sys_table_arg, initrd_total, initrd_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
 		efi_call_phys1(fh->close, initrds[k].handle);
 free_initrds:
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 fail:
 	hdr->ramdisk_image = 0;
 	hdr->ramdisk_size = 0;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07  3:44   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: Roy Franz, dave.martin, leif.lindholm

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   38 +++++++------
 drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
 2 files changed, 72 insertions(+), 62 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index ab0eefc..65b6a34 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -453,13 +453,13 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	status = efi_call_phys3(sys_table->boottime->handle_protocol,
 				handle, &proto, (void *)&image);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
 		return NULL;
 	}
 
-	status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
+	status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc lowmem for boot params\n");
+		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
 	}
 
@@ -503,9 +503,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 			options_size++;	/* NUL termination */
 
-			status = low_alloc(options_size, 1, &cmdline);
+			status = low_alloc(sys_table, options_size, 1,
+					   &cmdline);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to alloc mem for cmdline\n");
+				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
 				goto fail;
 			}
 
@@ -529,16 +530,16 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	status = handle_ramdisks(image, hdr);
+	status = handle_ramdisks(sys_table, image, hdr);
 	if (status != EFI_SUCCESS)
 		goto fail2;
 
 	return boot_params;
 fail2:
 	if (options_size)
-		low_free(options_size, hdr->cmd_line_ptr);
+		low_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
-	low_free(0x4000, (unsigned long)boot_params);
+	low_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
 }
 
@@ -561,7 +562,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = low_alloc(size, 1, (unsigned long *)&mem_map);
+	status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -569,7 +570,7 @@ get_map:
 	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(_size, (unsigned long)mem_map);
+		low_free(sys_table, _size, (unsigned long)mem_map);
 		goto again;
 	}
 
@@ -671,7 +672,7 @@ get_map:
 	return EFI_SUCCESS;
 
 free_mem_map:
-	low_free(_size, (unsigned long)mem_map);
+	low_free(sys_table, _size, (unsigned long)mem_map);
 	return status;
 }
 
@@ -694,10 +695,10 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
-		status = low_alloc(hdr->init_size, hdr->kernel_alignment,
-				   &start);
+		status = low_alloc(sys_table, hdr->init_size,
+				   hdr->kernel_alignment, &start);
 		if (status != EFI_SUCCESS)
-			efi_printk("Failed to alloc mem for kernel\n");
+			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
 	}
 
 	if (status == EFI_SUCCESS)
@@ -737,14 +738,15 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 				EFI_LOADER_DATA, sizeof(*gdt),
 				(void **)&gdt);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for gdt structure\n");
+		efi_printk(sys_table, "Failed to alloc mem for gdt structure\n");
 		goto fail;
 	}
 
 	gdt->size = 0x800;
-	status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
+	status = low_alloc(sys_table, gdt->size, 8,
+			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for gdt\n");
+		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
 		goto fail;
 	}
 
@@ -752,7 +754,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 				EFI_LOADER_DATA, sizeof(*idt),
 				(void **)&idt);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for idt structure\n");
+		efi_printk(sys_table, "Failed to alloc mem for idt structure\n");
 		goto fail;
 	}
 
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 47891bd..bd6c1a2 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -19,15 +19,16 @@ struct initrd {
 
 
 
-static void efi_char16_printk(efi_char16_t *str)
+static void efi_char16_printk(efi_system_table_t *sys_table_arg,
+			      efi_char16_t *str)
 {
 	struct efi_simple_text_output_protocol *out;
 
-	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
+	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
 	efi_call_phys2(out->output_string, out, str);
 }
 
-static void efi_printk(char *str)
+static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 {
 	char *s8;
 
@@ -37,15 +38,17 @@ static void efi_printk(char *str)
 		ch[0] = *s8;
 		if (*s8 == '\n') {
 			efi_char16_t nl[2] = { '\r', 0 };
-			efi_char16_printk(nl);
+			efi_char16_printk(sys_table_arg, nl);
 		}
 
-		efi_char16_printk(ch);
+		efi_char16_printk(sys_table_arg, ch);
 	}
 }
 
 
-static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
+static efi_status_t __get_map(efi_system_table_t *sys_table_arg,
+			      efi_memory_desc_t **map,
+			      unsigned long *map_size,
 			      unsigned long *desc_size)
 {
 	efi_memory_desc_t *m = NULL;
@@ -60,20 +63,20 @@ again:
 	 * allocation which may be in a new descriptor region.
 	 */
 	*map_size += sizeof(*m);
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA, *map_size, (void **)&m);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
-	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
-				m, &key, desc_size, &desc_version);
+	status = efi_call_phys5(sys_table_arg->boottime->get_memory_map,
+				map_size, m, &key, desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		efi_call_phys1(sys_table->boottime->free_pool, m);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
 		goto again;
 	}
 
 	if (status != EFI_SUCCESS)
-		efi_call_phys1(sys_table->boottime->free_pool, m);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
 
 fail:
 	*map = m;
@@ -83,8 +86,9 @@ fail:
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t high_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr, unsigned long max)
+static efi_status_t high_alloc(efi_system_table_t *sys_table_arg,
+			       unsigned long size, unsigned long align,
+			       unsigned long *addr, unsigned long max)
 {
 	unsigned long map_size, desc_size;
 	efi_memory_desc_t *map;
@@ -93,7 +97,7 @@ static efi_status_t high_alloc(unsigned long size, unsigned long align,
 	u64 max_addr = 0;
 	int i;
 
-	status = __get_map(&map, &map_size, &desc_size);
+	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -139,7 +143,7 @@ again:
 	if (!max_addr)
 		status = EFI_NOT_FOUND;
 	else {
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+		status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
 					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 					nr_pages, &max_addr);
 		if (status != EFI_SUCCESS) {
@@ -152,7 +156,7 @@ again:
 	}
 
 free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 
 fail:
 	return status;
@@ -161,7 +165,8 @@ fail:
 /*
  * Allocate at the lowest possible address.
  */
-static efi_status_t low_alloc(unsigned long size, unsigned long align,
+static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
+		unsigned long size, unsigned long align,
 			      unsigned long *addr)
 {
 	unsigned long map_size, desc_size;
@@ -170,7 +175,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 	unsigned long nr_pages;
 	int i;
 
-	status = __get_map(&map, &map_size, &desc_size);
+	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -203,7 +208,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 		if ((start + size) > end)
 			continue;
 
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+		status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
 					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 					nr_pages, &start);
 		if (status == EFI_SUCCESS) {
@@ -216,17 +221,18 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 		status = EFI_NOT_FOUND;
 
 free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 fail:
 	return status;
 }
 
-static void low_free(unsigned long size, unsigned long addr)
+static void low_free(efi_system_table_t *sys_table_arg, unsigned long size,
+		     unsigned long addr)
 {
 	unsigned long nr_pages;
 
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
+	efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages);
 }
 
 
@@ -236,7 +242,8 @@ static void low_free(unsigned long size, unsigned long addr)
  * We only support loading an initrd from the same filesystem as the
  * kernel image.
  */
-static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
+				    efi_loaded_image_t *image,
 				    struct setup_header *hdr)
 {
 	struct initrd *initrds;
@@ -278,12 +285,12 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 	if (!nr_initrds)
 		return EFI_SUCCESS;
 
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
 				nr_initrds * sizeof(*initrds),
 				&initrds);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for initrds\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n");
 		goto fail;
 	}
 
@@ -329,18 +336,18 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		if (!i) {
 			efi_boot_services_t *boottime;
 
-			boottime = sys_table->boottime;
+			boottime = sys_table_arg->boottime;
 
 			status = efi_call_phys3(boottime->handle_protocol,
 					image->device_handle, &fs_proto, &io);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to handle fs_proto\n");
+				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
 				goto free_initrds;
 			}
 
 			status = efi_call_phys2(io->open_volume, io, &fh);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to open volume\n");
+				efi_printk(sys_table_arg, "Failed to open volume\n");
 				goto free_initrds;
 			}
 		}
@@ -348,9 +355,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		status = efi_call_phys5(fh->open, fh, &h, filename_16,
 					EFI_FILE_MODE_READ, (u64)0);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to open initrd file: ");
-			efi_char16_printk(filename_16);
-			efi_printk("\n");
+			efi_printk(sys_table_arg, "Failed to open initrd file: ");
+			efi_char16_printk(sys_table_arg, filename_16);
+			efi_printk(sys_table_arg, "\n");
 			goto close_handles;
 		}
 
@@ -360,30 +367,31 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, NULL);
 		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk("Failed to get initrd info size\n");
+			efi_printk(sys_table_arg, "Failed to get initrd info size\n");
 			goto close_handles;
 		}
 
 grow:
-		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 					EFI_LOADER_DATA, info_sz, &info);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc mem for initrd info\n");
+			efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n");
 			goto close_handles;
 		}
 
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, info);
 		if (status == EFI_BUFFER_TOO_SMALL) {
-			efi_call_phys1(sys_table->boottime->free_pool, info);
+			efi_call_phys1(sys_table_arg->boottime->free_pool,
+				       info);
 			goto grow;
 		}
 
 		file_sz = info->file_size;
-		efi_call_phys1(sys_table->boottime->free_pool, info);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, info);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to get initrd info\n");
+			efi_printk(sys_table_arg, "Failed to get initrd info\n");
 			goto close_handles;
 		}
 
@@ -399,16 +407,16 @@ grow:
 		 * addresses in memory, so allocate enough memory for
 		 * all the initrd's.
 		 */
-		status = high_alloc(initrd_total, 0x1000,
+		status = high_alloc(sys_table_arg, initrd_total, 0x1000,
 				   &initrd_addr, hdr->initrd_addr_max);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc highmem for initrds\n");
+			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
 		if (initrd_addr > hdr->initrd_addr_max) {
-			efi_printk("We've run out of free low memory\n");
+			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_initrd_total;
 		}
@@ -428,7 +436,7 @@ grow:
 							initrds[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
-					efi_printk("Failed to read initrd\n");
+					efi_printk(sys_table_arg, "Failed to read initrd\n");
 					goto free_initrd_total;
 				}
 				addr += chunksize;
@@ -440,7 +448,7 @@ grow:
 
 	}
 
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 
 	hdr->ramdisk_image = initrd_addr;
 	hdr->ramdisk_size = initrd_total;
@@ -448,13 +456,13 @@ grow:
 	return status;
 
 free_initrd_total:
-	low_free(initrd_total, initrd_addr);
+	low_free(sys_table_arg, initrd_total, initrd_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
 		efi_call_phys1(fh->close, initrds[k].handle);
 free_initrds:
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 fail:
 	hdr->ramdisk_image = 0;
 	hdr->ramdisk_size = 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07  3:44   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   38 +++++++------
 drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
 2 files changed, 72 insertions(+), 62 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index ab0eefc..65b6a34 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -453,13 +453,13 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	status = efi_call_phys3(sys_table->boottime->handle_protocol,
 				handle, &proto, (void *)&image);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
 		return NULL;
 	}
 
-	status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
+	status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc lowmem for boot params\n");
+		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
 	}
 
@@ -503,9 +503,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 			options_size++;	/* NUL termination */
 
-			status = low_alloc(options_size, 1, &cmdline);
+			status = low_alloc(sys_table, options_size, 1,
+					   &cmdline);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to alloc mem for cmdline\n");
+				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
 				goto fail;
 			}
 
@@ -529,16 +530,16 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	status = handle_ramdisks(image, hdr);
+	status = handle_ramdisks(sys_table, image, hdr);
 	if (status != EFI_SUCCESS)
 		goto fail2;
 
 	return boot_params;
 fail2:
 	if (options_size)
-		low_free(options_size, hdr->cmd_line_ptr);
+		low_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
-	low_free(0x4000, (unsigned long)boot_params);
+	low_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
 }
 
@@ -561,7 +562,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = low_alloc(size, 1, (unsigned long *)&mem_map);
+	status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -569,7 +570,7 @@ get_map:
 	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(_size, (unsigned long)mem_map);
+		low_free(sys_table, _size, (unsigned long)mem_map);
 		goto again;
 	}
 
@@ -671,7 +672,7 @@ get_map:
 	return EFI_SUCCESS;
 
 free_mem_map:
-	low_free(_size, (unsigned long)mem_map);
+	low_free(sys_table, _size, (unsigned long)mem_map);
 	return status;
 }
 
@@ -694,10 +695,10 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
-		status = low_alloc(hdr->init_size, hdr->kernel_alignment,
-				   &start);
+		status = low_alloc(sys_table, hdr->init_size,
+				   hdr->kernel_alignment, &start);
 		if (status != EFI_SUCCESS)
-			efi_printk("Failed to alloc mem for kernel\n");
+			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
 	}
 
 	if (status == EFI_SUCCESS)
@@ -737,14 +738,15 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 				EFI_LOADER_DATA, sizeof(*gdt),
 				(void **)&gdt);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for gdt structure\n");
+		efi_printk(sys_table, "Failed to alloc mem for gdt structure\n");
 		goto fail;
 	}
 
 	gdt->size = 0x800;
-	status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
+	status = low_alloc(sys_table, gdt->size, 8,
+			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for gdt\n");
+		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
 		goto fail;
 	}
 
@@ -752,7 +754,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 				EFI_LOADER_DATA, sizeof(*idt),
 				(void **)&idt);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for idt structure\n");
+		efi_printk(sys_table, "Failed to alloc mem for idt structure\n");
 		goto fail;
 	}
 
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 47891bd..bd6c1a2 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -19,15 +19,16 @@ struct initrd {
 
 
 
-static void efi_char16_printk(efi_char16_t *str)
+static void efi_char16_printk(efi_system_table_t *sys_table_arg,
+			      efi_char16_t *str)
 {
 	struct efi_simple_text_output_protocol *out;
 
-	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
+	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
 	efi_call_phys2(out->output_string, out, str);
 }
 
-static void efi_printk(char *str)
+static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 {
 	char *s8;
 
@@ -37,15 +38,17 @@ static void efi_printk(char *str)
 		ch[0] = *s8;
 		if (*s8 == '\n') {
 			efi_char16_t nl[2] = { '\r', 0 };
-			efi_char16_printk(nl);
+			efi_char16_printk(sys_table_arg, nl);
 		}
 
-		efi_char16_printk(ch);
+		efi_char16_printk(sys_table_arg, ch);
 	}
 }
 
 
-static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
+static efi_status_t __get_map(efi_system_table_t *sys_table_arg,
+			      efi_memory_desc_t **map,
+			      unsigned long *map_size,
 			      unsigned long *desc_size)
 {
 	efi_memory_desc_t *m = NULL;
@@ -60,20 +63,20 @@ again:
 	 * allocation which may be in a new descriptor region.
 	 */
 	*map_size += sizeof(*m);
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA, *map_size, (void **)&m);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
-	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
-				m, &key, desc_size, &desc_version);
+	status = efi_call_phys5(sys_table_arg->boottime->get_memory_map,
+				map_size, m, &key, desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		efi_call_phys1(sys_table->boottime->free_pool, m);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
 		goto again;
 	}
 
 	if (status != EFI_SUCCESS)
-		efi_call_phys1(sys_table->boottime->free_pool, m);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
 
 fail:
 	*map = m;
@@ -83,8 +86,9 @@ fail:
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t high_alloc(unsigned long size, unsigned long align,
-			      unsigned long *addr, unsigned long max)
+static efi_status_t high_alloc(efi_system_table_t *sys_table_arg,
+			       unsigned long size, unsigned long align,
+			       unsigned long *addr, unsigned long max)
 {
 	unsigned long map_size, desc_size;
 	efi_memory_desc_t *map;
@@ -93,7 +97,7 @@ static efi_status_t high_alloc(unsigned long size, unsigned long align,
 	u64 max_addr = 0;
 	int i;
 
-	status = __get_map(&map, &map_size, &desc_size);
+	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -139,7 +143,7 @@ again:
 	if (!max_addr)
 		status = EFI_NOT_FOUND;
 	else {
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+		status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
 					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 					nr_pages, &max_addr);
 		if (status != EFI_SUCCESS) {
@@ -152,7 +156,7 @@ again:
 	}
 
 free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 
 fail:
 	return status;
@@ -161,7 +165,8 @@ fail:
 /*
  * Allocate at the lowest possible address.
  */
-static efi_status_t low_alloc(unsigned long size, unsigned long align,
+static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
+		unsigned long size, unsigned long align,
 			      unsigned long *addr)
 {
 	unsigned long map_size, desc_size;
@@ -170,7 +175,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 	unsigned long nr_pages;
 	int i;
 
-	status = __get_map(&map, &map_size, &desc_size);
+	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -203,7 +208,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 		if ((start + size) > end)
 			continue;
 
-		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+		status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
 					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 					nr_pages, &start);
 		if (status == EFI_SUCCESS) {
@@ -216,17 +221,18 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align,
 		status = EFI_NOT_FOUND;
 
 free_pool:
-	efi_call_phys1(sys_table->boottime->free_pool, map);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 fail:
 	return status;
 }
 
-static void low_free(unsigned long size, unsigned long addr)
+static void low_free(efi_system_table_t *sys_table_arg, unsigned long size,
+		     unsigned long addr)
 {
 	unsigned long nr_pages;
 
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-	efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
+	efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages);
 }
 
 
@@ -236,7 +242,8 @@ static void low_free(unsigned long size, unsigned long addr)
  * We only support loading an initrd from the same filesystem as the
  * kernel image.
  */
-static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
+				    efi_loaded_image_t *image,
 				    struct setup_header *hdr)
 {
 	struct initrd *initrds;
@@ -278,12 +285,12 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 	if (!nr_initrds)
 		return EFI_SUCCESS;
 
-	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
 				nr_initrds * sizeof(*initrds),
 				&initrds);
 	if (status != EFI_SUCCESS) {
-		efi_printk("Failed to alloc mem for initrds\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n");
 		goto fail;
 	}
 
@@ -329,18 +336,18 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		if (!i) {
 			efi_boot_services_t *boottime;
 
-			boottime = sys_table->boottime;
+			boottime = sys_table_arg->boottime;
 
 			status = efi_call_phys3(boottime->handle_protocol,
 					image->device_handle, &fs_proto, &io);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to handle fs_proto\n");
+				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
 				goto free_initrds;
 			}
 
 			status = efi_call_phys2(io->open_volume, io, &fh);
 			if (status != EFI_SUCCESS) {
-				efi_printk("Failed to open volume\n");
+				efi_printk(sys_table_arg, "Failed to open volume\n");
 				goto free_initrds;
 			}
 		}
@@ -348,9 +355,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		status = efi_call_phys5(fh->open, fh, &h, filename_16,
 					EFI_FILE_MODE_READ, (u64)0);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to open initrd file: ");
-			efi_char16_printk(filename_16);
-			efi_printk("\n");
+			efi_printk(sys_table_arg, "Failed to open initrd file: ");
+			efi_char16_printk(sys_table_arg, filename_16);
+			efi_printk(sys_table_arg, "\n");
 			goto close_handles;
 		}
 
@@ -360,30 +367,31 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, NULL);
 		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk("Failed to get initrd info size\n");
+			efi_printk(sys_table_arg, "Failed to get initrd info size\n");
 			goto close_handles;
 		}
 
 grow:
-		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 					EFI_LOADER_DATA, info_sz, &info);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc mem for initrd info\n");
+			efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n");
 			goto close_handles;
 		}
 
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, info);
 		if (status == EFI_BUFFER_TOO_SMALL) {
-			efi_call_phys1(sys_table->boottime->free_pool, info);
+			efi_call_phys1(sys_table_arg->boottime->free_pool,
+				       info);
 			goto grow;
 		}
 
 		file_sz = info->file_size;
-		efi_call_phys1(sys_table->boottime->free_pool, info);
+		efi_call_phys1(sys_table_arg->boottime->free_pool, info);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to get initrd info\n");
+			efi_printk(sys_table_arg, "Failed to get initrd info\n");
 			goto close_handles;
 		}
 
@@ -399,16 +407,16 @@ grow:
 		 * addresses in memory, so allocate enough memory for
 		 * all the initrd's.
 		 */
-		status = high_alloc(initrd_total, 0x1000,
+		status = high_alloc(sys_table_arg, initrd_total, 0x1000,
 				   &initrd_addr, hdr->initrd_addr_max);
 		if (status != EFI_SUCCESS) {
-			efi_printk("Failed to alloc highmem for initrds\n");
+			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
 		if (initrd_addr > hdr->initrd_addr_max) {
-			efi_printk("We've run out of free low memory\n");
+			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_initrd_total;
 		}
@@ -428,7 +436,7 @@ grow:
 							initrds[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
-					efi_printk("Failed to read initrd\n");
+					efi_printk(sys_table_arg, "Failed to read initrd\n");
 					goto free_initrd_total;
 				}
 				addr += chunksize;
@@ -440,7 +448,7 @@ grow:
 
 	}
 
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 
 	hdr->ramdisk_image = initrd_addr;
 	hdr->ramdisk_size = initrd_total;
@@ -448,13 +456,13 @@ grow:
 	return status;
 
 free_initrd_total:
-	low_free(initrd_total, initrd_addr);
+	low_free(sys_table_arg, initrd_total, initrd_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
 		efi_call_phys1(fh->close, initrds[k].handle);
 free_initrds:
-	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 fail:
 	hdr->ramdisk_image = 0;
 	hdr->ramdisk_size = 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 04/17] Rename memory allocation/free functions
  2013-08-07  3:44 ` Roy Franz
  (?)
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

Rename them to be more similar, as low_free() could be used to free
memory allocated by both high_alloc() and low_alloc().
high_alloc() -> efi_high_alloc()
low_alloc()  -> efi_low_alloc()
low_free()   -> efi_free()

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
 drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 65b6a34..2a4430a 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -457,7 +457,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 		return NULL;
 	}
 
-	status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params);
+	status = efi_low_alloc(sys_table, 0x4000, 1,
+			       (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
@@ -503,7 +504,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 			options_size++;	/* NUL termination */
 
-			status = low_alloc(sys_table, options_size, 1,
+			status = efi_low_alloc(sys_table, options_size, 1,
 					   &cmdline);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
@@ -537,9 +538,9 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	return boot_params;
 fail2:
 	if (options_size)
-		low_free(sys_table, options_size, hdr->cmd_line_ptr);
+		efi_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
-	low_free(sys_table, 0x4000, (unsigned long)boot_params);
+	efi_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
 }
 
@@ -562,7 +563,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
+	status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -570,7 +571,7 @@ get_map:
 	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(sys_table, _size, (unsigned long)mem_map);
+		efi_free(sys_table, _size, (unsigned long)mem_map);
 		goto again;
 	}
 
@@ -672,7 +673,7 @@ get_map:
 	return EFI_SUCCESS;
 
 free_mem_map:
-	low_free(sys_table, _size, (unsigned long)mem_map);
+	efi_free(sys_table, _size, (unsigned long)mem_map);
 	return status;
 }
 
@@ -695,7 +696,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
-		status = low_alloc(sys_table, hdr->init_size,
+		status = efi_low_alloc(sys_table, hdr->init_size,
 				   hdr->kernel_alignment, &start);
 		if (status != EFI_SUCCESS)
 			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
@@ -743,7 +744,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 	}
 
 	gdt->size = 0x800;
-	status = low_alloc(sys_table, gdt->size, 8,
+	status = efi_low_alloc(sys_table, gdt->size, 8,
 			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bd6c1a2..8e6a7f9 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -86,7 +86,7 @@ fail:
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t high_alloc(efi_system_table_t *sys_table_arg,
+static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 			       unsigned long size, unsigned long align,
 			       unsigned long *addr, unsigned long max)
 {
@@ -163,10 +163,10 @@ fail:
 }
 
 /*
- * Allocate at the lowest possible address.
+ * Allocate at the lowest possible address, that is not below 'min'
  */
-static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
-		unsigned long size, unsigned long align,
+static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
+			      unsigned long size, unsigned long align,
 			      unsigned long *addr)
 {
 	unsigned long map_size, desc_size;
@@ -226,7 +226,7 @@ fail:
 	return status;
 }
 
-static void low_free(efi_system_table_t *sys_table_arg, unsigned long size,
+static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 		     unsigned long addr)
 {
 	unsigned long nr_pages;
@@ -407,8 +407,8 @@ grow:
 		 * addresses in memory, so allocate enough memory for
 		 * all the initrd's.
 		 */
-		status = high_alloc(sys_table_arg, initrd_total, 0x1000,
-				   &initrd_addr, hdr->initrd_addr_max);
+		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
+				    &initrd_addr, hdr->initrd_addr_max);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
@@ -456,7 +456,7 @@ grow:
 	return status;
 
 free_initrd_total:
-	low_free(sys_table_arg, initrd_total, initrd_addr);
+	efi_free(sys_table_arg, initrd_total, initrd_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 04/17] Rename memory allocation/free functions
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: Roy Franz, dave.martin, leif.lindholm

Rename them to be more similar, as low_free() could be used to free
memory allocated by both high_alloc() and low_alloc().
high_alloc() -> efi_high_alloc()
low_alloc()  -> efi_low_alloc()
low_free()   -> efi_free()

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
 drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 65b6a34..2a4430a 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -457,7 +457,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 		return NULL;
 	}
 
-	status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params);
+	status = efi_low_alloc(sys_table, 0x4000, 1,
+			       (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
@@ -503,7 +504,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 			options_size++;	/* NUL termination */
 
-			status = low_alloc(sys_table, options_size, 1,
+			status = efi_low_alloc(sys_table, options_size, 1,
 					   &cmdline);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
@@ -537,9 +538,9 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	return boot_params;
 fail2:
 	if (options_size)
-		low_free(sys_table, options_size, hdr->cmd_line_ptr);
+		efi_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
-	low_free(sys_table, 0x4000, (unsigned long)boot_params);
+	efi_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
 }
 
@@ -562,7 +563,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
+	status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -570,7 +571,7 @@ get_map:
 	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(sys_table, _size, (unsigned long)mem_map);
+		efi_free(sys_table, _size, (unsigned long)mem_map);
 		goto again;
 	}
 
@@ -672,7 +673,7 @@ get_map:
 	return EFI_SUCCESS;
 
 free_mem_map:
-	low_free(sys_table, _size, (unsigned long)mem_map);
+	efi_free(sys_table, _size, (unsigned long)mem_map);
 	return status;
 }
 
@@ -695,7 +696,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
-		status = low_alloc(sys_table, hdr->init_size,
+		status = efi_low_alloc(sys_table, hdr->init_size,
 				   hdr->kernel_alignment, &start);
 		if (status != EFI_SUCCESS)
 			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
@@ -743,7 +744,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 	}
 
 	gdt->size = 0x800;
-	status = low_alloc(sys_table, gdt->size, 8,
+	status = efi_low_alloc(sys_table, gdt->size, 8,
 			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bd6c1a2..8e6a7f9 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -86,7 +86,7 @@ fail:
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t high_alloc(efi_system_table_t *sys_table_arg,
+static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 			       unsigned long size, unsigned long align,
 			       unsigned long *addr, unsigned long max)
 {
@@ -163,10 +163,10 @@ fail:
 }
 
 /*
- * Allocate at the lowest possible address.
+ * Allocate at the lowest possible address, that is not below 'min'
  */
-static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
-		unsigned long size, unsigned long align,
+static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
+			      unsigned long size, unsigned long align,
 			      unsigned long *addr)
 {
 	unsigned long map_size, desc_size;
@@ -226,7 +226,7 @@ fail:
 	return status;
 }
 
-static void low_free(efi_system_table_t *sys_table_arg, unsigned long size,
+static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 		     unsigned long addr)
 {
 	unsigned long nr_pages;
@@ -407,8 +407,8 @@ grow:
 		 * addresses in memory, so allocate enough memory for
 		 * all the initrd's.
 		 */
-		status = high_alloc(sys_table_arg, initrd_total, 0x1000,
-				   &initrd_addr, hdr->initrd_addr_max);
+		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
+				    &initrd_addr, hdr->initrd_addr_max);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
@@ -456,7 +456,7 @@ grow:
 	return status;
 
 free_initrd_total:
-	low_free(sys_table_arg, initrd_total, initrd_addr);
+	efi_free(sys_table_arg, initrd_total, initrd_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 04/17] Rename memory allocation/free functions
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

Rename them to be more similar, as low_free() could be used to free
memory allocated by both high_alloc() and low_alloc().
high_alloc() -> efi_high_alloc()
low_alloc()  -> efi_low_alloc()
low_free()   -> efi_free()

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
 drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 65b6a34..2a4430a 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -457,7 +457,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 		return NULL;
 	}
 
-	status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params);
+	status = efi_low_alloc(sys_table, 0x4000, 1,
+			       (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
@@ -503,7 +504,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 			options_size++;	/* NUL termination */
 
-			status = low_alloc(sys_table, options_size, 1,
+			status = efi_low_alloc(sys_table, options_size, 1,
 					   &cmdline);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
@@ -537,9 +538,9 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	return boot_params;
 fail2:
 	if (options_size)
-		low_free(sys_table, options_size, hdr->cmd_line_ptr);
+		efi_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
-	low_free(sys_table, 0x4000, (unsigned long)boot_params);
+	efi_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
 }
 
@@ -562,7 +563,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
+	status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -570,7 +571,7 @@ get_map:
 	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
 				mem_map, &key, &desc_size, &desc_version);
 	if (status == EFI_BUFFER_TOO_SMALL) {
-		low_free(sys_table, _size, (unsigned long)mem_map);
+		efi_free(sys_table, _size, (unsigned long)mem_map);
 		goto again;
 	}
 
@@ -672,7 +673,7 @@ get_map:
 	return EFI_SUCCESS;
 
 free_mem_map:
-	low_free(sys_table, _size, (unsigned long)mem_map);
+	efi_free(sys_table, _size, (unsigned long)mem_map);
 	return status;
 }
 
@@ -695,7 +696,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
-		status = low_alloc(sys_table, hdr->init_size,
+		status = efi_low_alloc(sys_table, hdr->init_size,
 				   hdr->kernel_alignment, &start);
 		if (status != EFI_SUCCESS)
 			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
@@ -743,7 +744,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 	}
 
 	gdt->size = 0x800;
-	status = low_alloc(sys_table, gdt->size, 8,
+	status = efi_low_alloc(sys_table, gdt->size, 8,
 			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bd6c1a2..8e6a7f9 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -86,7 +86,7 @@ fail:
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t high_alloc(efi_system_table_t *sys_table_arg,
+static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 			       unsigned long size, unsigned long align,
 			       unsigned long *addr, unsigned long max)
 {
@@ -163,10 +163,10 @@ fail:
 }
 
 /*
- * Allocate at the lowest possible address.
+ * Allocate at the lowest possible address, that is not below 'min'
  */
-static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
-		unsigned long size, unsigned long align,
+static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
+			      unsigned long size, unsigned long align,
 			      unsigned long *addr)
 {
 	unsigned long map_size, desc_size;
@@ -226,7 +226,7 @@ fail:
 	return status;
 }
 
-static void low_free(efi_system_table_t *sys_table_arg, unsigned long size,
+static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 		     unsigned long addr)
 {
 	unsigned long nr_pages;
@@ -407,8 +407,8 @@ grow:
 		 * addresses in memory, so allocate enough memory for
 		 * all the initrd's.
 		 */
-		status = high_alloc(sys_table_arg, initrd_total, 0x1000,
-				   &initrd_addr, hdr->initrd_addr_max);
+		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
+				    &initrd_addr, hdr->initrd_addr_max);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
@@ -456,7 +456,7 @@ grow:
 	return status;
 
 free_initrd_total:
-	low_free(sys_table_arg, initrd_total, initrd_addr);
+	efi_free(sys_table_arg, initrd_total, initrd_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 05/17] Add minimum address parameter to efi_low_alloc()
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

This allows allocations to be made low in memory while
avoiding allocations at the base of memory.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   11 ++++++-----
 drivers/firmware/efi/efi-stub-helper.c |    5 ++++-
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 2a4430a..f44ef2f 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -458,7 +458,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	}
 
 	status = efi_low_alloc(sys_table, 0x4000, 1,
-			       (unsigned long *)&boot_params);
+			       (unsigned long *)&boot_params, 0);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
@@ -505,7 +505,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 			options_size++;	/* NUL termination */
 
 			status = efi_low_alloc(sys_table, options_size, 1,
-					   &cmdline);
+					   &cmdline, 0);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
 				goto fail;
@@ -563,7 +563,8 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
+	status = efi_low_alloc(sys_table, size, 1,
+			       (unsigned long *)&mem_map, 0);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -697,7 +698,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
 		status = efi_low_alloc(sys_table, hdr->init_size,
-				   hdr->kernel_alignment, &start);
+				   hdr->kernel_alignment, &start, 0);
 		if (status != EFI_SUCCESS)
 			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
 	}
@@ -745,7 +746,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 
 	gdt->size = 0x800;
 	status = efi_low_alloc(sys_table, gdt->size, 8,
-			   (unsigned long *)&gdt->address);
+			   (unsigned long *)&gdt->address, 0);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
 		goto fail;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8e6a7f9..63e3556 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -167,7 +167,7 @@ fail:
  */
 static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 			      unsigned long size, unsigned long align,
-			      unsigned long *addr)
+			      unsigned long *addr, unsigned long min)
 {
 	unsigned long map_size, desc_size;
 	efi_memory_desc_t *map;
@@ -204,6 +204,9 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 		if (start == 0x0)
 			start += 8;
 
+		if (start < min)
+			start = min;
+
 		start = round_up(start, align);
 		if ((start + size) > end)
 			continue;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 05/17] Add minimum address parameter to efi_low_alloc()
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8, Roy Franz

This allows allocations to be made low in memory while
avoiding allocations at the base of memory.

Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 arch/x86/boot/compressed/eboot.c       |   11 ++++++-----
 drivers/firmware/efi/efi-stub-helper.c |    5 ++++-
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 2a4430a..f44ef2f 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -458,7 +458,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	}
 
 	status = efi_low_alloc(sys_table, 0x4000, 1,
-			       (unsigned long *)&boot_params);
+			       (unsigned long *)&boot_params, 0);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
@@ -505,7 +505,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 			options_size++;	/* NUL termination */
 
 			status = efi_low_alloc(sys_table, options_size, 1,
-					   &cmdline);
+					   &cmdline, 0);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
 				goto fail;
@@ -563,7 +563,8 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
+	status = efi_low_alloc(sys_table, size, 1,
+			       (unsigned long *)&mem_map, 0);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -697,7 +698,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
 		status = efi_low_alloc(sys_table, hdr->init_size,
-				   hdr->kernel_alignment, &start);
+				   hdr->kernel_alignment, &start, 0);
 		if (status != EFI_SUCCESS)
 			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
 	}
@@ -745,7 +746,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 
 	gdt->size = 0x800;
 	status = efi_low_alloc(sys_table, gdt->size, 8,
-			   (unsigned long *)&gdt->address);
+			   (unsigned long *)&gdt->address, 0);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
 		goto fail;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8e6a7f9..63e3556 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -167,7 +167,7 @@ fail:
  */
 static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 			      unsigned long size, unsigned long align,
-			      unsigned long *addr)
+			      unsigned long *addr, unsigned long min)
 {
 	unsigned long map_size, desc_size;
 	efi_memory_desc_t *map;
@@ -204,6 +204,9 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 		if (start == 0x0)
 			start += 8;
 
+		if (start < min)
+			start = min;
+
 		start = round_up(start, align);
 		if ((start + size) > end)
 			continue;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 05/17] Add minimum address parameter to efi_low_alloc()
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

This allows allocations to be made low in memory while
avoiding allocations at the base of memory.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |   11 ++++++-----
 drivers/firmware/efi/efi-stub-helper.c |    5 ++++-
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 2a4430a..f44ef2f 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -458,7 +458,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	}
 
 	status = efi_low_alloc(sys_table, 0x4000, 1,
-			       (unsigned long *)&boot_params);
+			       (unsigned long *)&boot_params, 0);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc lowmem for boot params\n");
 		return NULL;
@@ -505,7 +505,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 			options_size++;	/* NUL termination */
 
 			status = efi_low_alloc(sys_table, options_size, 1,
-					   &cmdline);
+					   &cmdline, 0);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table, "Failed to alloc mem for cmdline\n");
 				goto fail;
@@ -563,7 +563,8 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
 again:
 	size += sizeof(*mem_map) * 2;
 	_size = size;
-	status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
+	status = efi_low_alloc(sys_table, size, 1,
+			       (unsigned long *)&mem_map, 0);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -697,7 +698,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr)
 				nr_pages, &start);
 	if (status != EFI_SUCCESS) {
 		status = efi_low_alloc(sys_table, hdr->init_size,
-				   hdr->kernel_alignment, &start);
+				   hdr->kernel_alignment, &start, 0);
 		if (status != EFI_SUCCESS)
 			efi_printk(sys_table, "Failed to alloc mem for kernel\n");
 	}
@@ -745,7 +746,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 
 	gdt->size = 0x800;
 	status = efi_low_alloc(sys_table, gdt->size, 8,
-			   (unsigned long *)&gdt->address);
+			   (unsigned long *)&gdt->address, 0);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to alloc mem for gdt\n");
 		goto fail;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8e6a7f9..63e3556 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -167,7 +167,7 @@ fail:
  */
 static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 			      unsigned long size, unsigned long align,
-			      unsigned long *addr)
+			      unsigned long *addr, unsigned long min)
 {
 	unsigned long map_size, desc_size;
 	efi_memory_desc_t *map;
@@ -204,6 +204,9 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 		if (start == 0x0)
 			start += 8;
 
+		if (start < min)
+			start = min;
+
 		start = round_up(start, align);
 		if ((start + size) > end)
 			continue;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 06/17] rename __get_map() to efi_get_memory_map(), add parameter to optionally return mmap key. The mmap key is required to exit EFI boot services, and allows efi_get_memory_map() to be used for getting final memory map.
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 63e3556..cd7701f 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -46,10 +46,11 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 }
 
 
-static efi_status_t __get_map(efi_system_table_t *sys_table_arg,
-			      efi_memory_desc_t **map,
-			      unsigned long *map_size,
-			      unsigned long *desc_size)
+static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
+				       efi_memory_desc_t **map,
+				       unsigned long *map_size,
+				       unsigned long *desc_size,
+				       unsigned long *key_ptr)
 {
 	efi_memory_desc_t *m = NULL;
 	efi_status_t status;
@@ -77,6 +78,8 @@ again:
 
 	if (status != EFI_SUCCESS)
 		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
+	if (key_ptr && status == EFI_SUCCESS)
+		*key_ptr = key;
 
 fail:
 	*map = m;
@@ -97,7 +100,8 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 	u64 max_addr = 0;
 	int i;
 
-	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
+	status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size,
+				    NULL);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -175,7 +179,8 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 	unsigned long nr_pages;
 	int i;
 
-	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
+	status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size,
+				    NULL);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 06/17] rename __get_map() to efi_get_memory_map(), add parameter to optionally return mmap key. The mmap key is required to exit EFI boot services, and allows efi_get_memory_map() to be used for getting final memory map.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 63e3556..cd7701f 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -46,10 +46,11 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 }
 
 
-static efi_status_t __get_map(efi_system_table_t *sys_table_arg,
-			      efi_memory_desc_t **map,
-			      unsigned long *map_size,
-			      unsigned long *desc_size)
+static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
+				       efi_memory_desc_t **map,
+				       unsigned long *map_size,
+				       unsigned long *desc_size,
+				       unsigned long *key_ptr)
 {
 	efi_memory_desc_t *m = NULL;
 	efi_status_t status;
@@ -77,6 +78,8 @@ again:
 
 	if (status != EFI_SUCCESS)
 		efi_call_phys1(sys_table_arg->boottime->free_pool, m);
+	if (key_ptr && status == EFI_SUCCESS)
+		*key_ptr = key;
 
 fail:
 	*map = m;
@@ -97,7 +100,8 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 	u64 max_addr = 0;
 	int i;
 
-	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
+	status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size,
+				    NULL);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
@@ -175,7 +179,8 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 	unsigned long nr_pages;
 	int i;
 
-	status = __get_map(sys_table_arg, &map, &map_size, &desc_size);
+	status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size,
+				    NULL);
 	if (status != EFI_SUCCESS)
 		goto fail;
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 07/17] Enforce minimum alignment of 1 page on allocations. The efi_high_alloc() and efi_low_alloc() functions use the EFI_ALLOCATE_ADDRESS option to the EFI function allocate_pages(), which requires a minimum of page alignment, and rejects all other requests.
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

The existing code could fail to allocate depending
on allocation size, as although repeated allocation
attempts were made, none were guaranteed to be page
aligned.



Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index cd7701f..8582650 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -105,6 +105,13 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 	if (status != EFI_SUCCESS)
 		goto fail;
 
+	/* Enforce minimum alignment that EFI requires when requesting
+	 * a specific address.  We are doing page-based allocations,
+	 * so we must be aligned to a page.
+	 */
+	if (align < EFI_PAGE_SIZE)
+		align = EFI_PAGE_SIZE;
+
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
 again:
 	for (i = 0; i < map_size / desc_size; i++) {
@@ -184,6 +191,13 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 	if (status != EFI_SUCCESS)
 		goto fail;
 
+	/* Enforce minimum alignment that EFI requires when requesting
+	 * a specific address.  We are doing page-based allocations,
+	 * so we must be aligned to a page.
+	 */
+	if (align < EFI_PAGE_SIZE)
+		align = EFI_PAGE_SIZE;
+
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
 	for (i = 0; i < map_size / desc_size; i++) {
 		efi_memory_desc_t *desc;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 07/17] Enforce minimum alignment of 1 page on allocations. The efi_high_alloc() and efi_low_alloc() functions use the EFI_ALLOCATE_ADDRESS option to the EFI function allocate_pages(), which requires a minimum of page alignment, and rejects all other requests.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

The existing code could fail to allocate depending
on allocation size, as although repeated allocation
attempts were made, none were guaranteed to be page
aligned.



Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index cd7701f..8582650 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -105,6 +105,13 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
 	if (status != EFI_SUCCESS)
 		goto fail;
 
+	/* Enforce minimum alignment that EFI requires when requesting
+	 * a specific address.  We are doing page-based allocations,
+	 * so we must be aligned to a page.
+	 */
+	if (align < EFI_PAGE_SIZE)
+		align = EFI_PAGE_SIZE;
+
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
 again:
 	for (i = 0; i < map_size / desc_size; i++) {
@@ -184,6 +191,13 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 	if (status != EFI_SUCCESS)
 		goto fail;
 
+	/* Enforce minimum alignment that EFI requires when requesting
+	 * a specific address.  We are doing page-based allocations,
+	 * so we must be aligned to a page.
+	 */
+	if (align < EFI_PAGE_SIZE)
+		align = EFI_PAGE_SIZE;
+
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
 	for (i = 0; i < map_size / desc_size; i++) {
 		efi_memory_desc_t *desc;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 08/17] Allow efi_free() to be called with size of 0, and do nothing in that case.
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

Make efi_free() safely callable with size of 0, similar to free() being
callable with NULL pointers.
Remove size checks that this makes redundant.  This also avoids some
size checks in the ARM EFI stub code that will be added as well.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |    3 +--
 drivers/firmware/efi/efi-stub-helper.c |    3 +++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index f44ef2f..bcfdcc2 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -537,8 +537,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	return boot_params;
 fail2:
-	if (options_size)
-		efi_free(sys_table, options_size, hdr->cmd_line_ptr);
+	efi_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
 	efi_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8582650..bce0473 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -253,6 +253,9 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 {
 	unsigned long nr_pages;
 
+	if (!size)
+		return;
+
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
 	efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages);
 }
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 08/17] Allow efi_free() to be called with size of 0, and do nothing in that case.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

Make efi_free() safely callable with size of 0, similar to free() being
callable with NULL pointers.
Remove size checks that this makes redundant.  This also avoids some
size checks in the ARM EFI stub code that will be added as well.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |    3 +--
 drivers/firmware/efi/efi-stub-helper.c |    3 +++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index f44ef2f..bcfdcc2 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -537,8 +537,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	return boot_params;
 fail2:
-	if (options_size)
-		efi_free(sys_table, options_size, hdr->cmd_line_ptr);
+	efi_free(sys_table, options_size, hdr->cmd_line_ptr);
 fail:
 	efi_free(sys_table, 0x4000, (unsigned long)boot_params);
 	return NULL;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8582650..bce0473 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -253,6 +253,9 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 {
 	unsigned long nr_pages;
 
+	if (!size)
+		return;
+
 	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
 	efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages);
 }
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 09/17] Generalize handle_ramdisks() and rename to handle_cmdline_files().
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

The handle_cmdline_files now takes the option to handle as a string,
and returns the loaded data through parameters, rather than taking
an x86 specific setup_header structure.  For ARM, this will be used
to load a device tree blob in addition to initrd images.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |    9 +++++-
 drivers/firmware/efi/efi-stub-helper.c |   50 ++++++++++++++++++--------------
 2 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index bcfdcc2..7011e33 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -443,6 +443,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	u16 *s2;
 	u8 *s1;
 	int i;
+	u64 ramdisk_addr;
+	u64 ramdisk_size;
 
 	sys_table = _table;
 
@@ -531,9 +533,14 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	status = handle_ramdisks(sys_table, image, hdr);
+	status = handle_cmdline_files(sys_table, image,
+				      (char *)(unsigned long)hdr->cmd_line_ptr,
+				      "initrd=", hdr->initrd_addr_max,
+				      &ramdisk_addr, &ramdisk_size);
 	if (status != EFI_SUCCESS)
 		goto fail2;
+	hdr->ramdisk_image = ramdisk_addr;
+	hdr->ramdisk_size = ramdisk_size;
 
 	return boot_params;
 fail2:
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bce0473..854aada 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -267,9 +267,11 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
  * We only support loading an initrd from the same filesystem as the
  * kernel image.
  */
-static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
-				    efi_loaded_image_t *image,
-				    struct setup_header *hdr)
+static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
+					 efi_loaded_image_t *image,
+					 char *cmd_line, char *option_string,
+					 u64 max_addr,
+					 u64 *load_addr, u64 *load_size)
 {
 	struct initrd *initrds;
 	unsigned long initrd_addr;
@@ -285,19 +287,25 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 	initrd_addr = 0;
 	initrd_total = 0;
 
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	str = cmd_line;
 
 	j = 0;			/* See close_handles */
 
+	if (!load_addr || !load_size)
+		return EFI_INVALID_PARAMETER;
+
+	*load_addr = 0;
+	*load_size = 0;
+
 	if (!str || !*str)
 		return EFI_SUCCESS;
 
 	for (nr_initrds = 0; *str; nr_initrds++) {
-		str = strstr(str, "initrd=");
+		str = strstr(str, option_string);
 		if (!str)
 			break;
 
-		str += 7;
+		str += strlen(option_string);
 
 		/* Skip any leading slashes */
 		while (*str == '/' || *str == '\\')
@@ -315,11 +323,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 				nr_initrds * sizeof(*initrds),
 				&initrds);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for file load\n");
 		goto fail;
 	}
 
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	str = cmd_line;
 	for (i = 0; i < nr_initrds; i++) {
 		struct initrd *initrd;
 		efi_file_handle_t *h;
@@ -330,11 +338,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		efi_char16_t *p;
 		u64 file_sz;
 
-		str = strstr(str, "initrd=");
+		str = strstr(str, option_string);
 		if (!str)
 			break;
 
-		str += 7;
+		str += strlen(option_string);
 
 		initrd = &initrds[i];
 		p = filename_16;
@@ -380,7 +388,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		status = efi_call_phys5(fh->open, fh, &h, filename_16,
 					EFI_FILE_MODE_READ, (u64)0);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to open initrd file: ");
+			efi_printk(sys_table_arg, "Failed to open file file: ");
 			efi_char16_printk(sys_table_arg, filename_16);
 			efi_printk(sys_table_arg, "\n");
 			goto close_handles;
@@ -392,7 +400,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, NULL);
 		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk(sys_table_arg, "Failed to get initrd info size\n");
+			efi_printk(sys_table_arg, "Failed to get file info size\n");
 			goto close_handles;
 		}
 
@@ -400,7 +408,7 @@ grow:
 		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 					EFI_LOADER_DATA, info_sz, &info);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n");
+			efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
 			goto close_handles;
 		}
 
@@ -416,7 +424,7 @@ grow:
 		efi_call_phys1(sys_table_arg->boottime->free_pool, info);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to get initrd info\n");
+			efi_printk(sys_table_arg, "Failed to get file info\n");
 			goto close_handles;
 		}
 
@@ -433,14 +441,14 @@ grow:
 		 * all the initrd's.
 		 */
 		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
-				    &initrd_addr, hdr->initrd_addr_max);
+				    &initrd_addr, max_addr);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
-		if (initrd_addr > hdr->initrd_addr_max) {
+		if (initrd_addr > max_addr) {
 			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_initrd_total;
@@ -461,7 +469,7 @@ grow:
 							initrds[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
-					efi_printk(sys_table_arg, "Failed to read initrd\n");
+					efi_printk(sys_table_arg, "Failed to read file\n");
 					goto free_initrd_total;
 				}
 				addr += chunksize;
@@ -475,8 +483,8 @@ grow:
 
 	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 
-	hdr->ramdisk_image = initrd_addr;
-	hdr->ramdisk_size = initrd_total;
+	*load_addr = initrd_addr;
+	*load_size = initrd_total;
 
 	return status;
 
@@ -489,8 +497,8 @@ close_handles:
 free_initrds:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 fail:
-	hdr->ramdisk_image = 0;
-	hdr->ramdisk_size = 0;
+	*load_addr = 0;
+	*load_size = 0;
 
 	return status;
 }
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 09/17] Generalize handle_ramdisks() and rename to handle_cmdline_files().
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8, Roy Franz

The handle_cmdline_files now takes the option to handle as a string,
and returns the loaded data through parameters, rather than taking
an x86 specific setup_header structure.  For ARM, this will be used
to load a device tree blob in addition to initrd images.

Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 arch/x86/boot/compressed/eboot.c       |    9 +++++-
 drivers/firmware/efi/efi-stub-helper.c |   50 ++++++++++++++++++--------------
 2 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index bcfdcc2..7011e33 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -443,6 +443,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	u16 *s2;
 	u8 *s1;
 	int i;
+	u64 ramdisk_addr;
+	u64 ramdisk_size;
 
 	sys_table = _table;
 
@@ -531,9 +533,14 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	status = handle_ramdisks(sys_table, image, hdr);
+	status = handle_cmdline_files(sys_table, image,
+				      (char *)(unsigned long)hdr->cmd_line_ptr,
+				      "initrd=", hdr->initrd_addr_max,
+				      &ramdisk_addr, &ramdisk_size);
 	if (status != EFI_SUCCESS)
 		goto fail2;
+	hdr->ramdisk_image = ramdisk_addr;
+	hdr->ramdisk_size = ramdisk_size;
 
 	return boot_params;
 fail2:
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bce0473..854aada 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -267,9 +267,11 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
  * We only support loading an initrd from the same filesystem as the
  * kernel image.
  */
-static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
-				    efi_loaded_image_t *image,
-				    struct setup_header *hdr)
+static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
+					 efi_loaded_image_t *image,
+					 char *cmd_line, char *option_string,
+					 u64 max_addr,
+					 u64 *load_addr, u64 *load_size)
 {
 	struct initrd *initrds;
 	unsigned long initrd_addr;
@@ -285,19 +287,25 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 	initrd_addr = 0;
 	initrd_total = 0;
 
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	str = cmd_line;
 
 	j = 0;			/* See close_handles */
 
+	if (!load_addr || !load_size)
+		return EFI_INVALID_PARAMETER;
+
+	*load_addr = 0;
+	*load_size = 0;
+
 	if (!str || !*str)
 		return EFI_SUCCESS;
 
 	for (nr_initrds = 0; *str; nr_initrds++) {
-		str = strstr(str, "initrd=");
+		str = strstr(str, option_string);
 		if (!str)
 			break;
 
-		str += 7;
+		str += strlen(option_string);
 
 		/* Skip any leading slashes */
 		while (*str == '/' || *str == '\\')
@@ -315,11 +323,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 				nr_initrds * sizeof(*initrds),
 				&initrds);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for file load\n");
 		goto fail;
 	}
 
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	str = cmd_line;
 	for (i = 0; i < nr_initrds; i++) {
 		struct initrd *initrd;
 		efi_file_handle_t *h;
@@ -330,11 +338,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		efi_char16_t *p;
 		u64 file_sz;
 
-		str = strstr(str, "initrd=");
+		str = strstr(str, option_string);
 		if (!str)
 			break;
 
-		str += 7;
+		str += strlen(option_string);
 
 		initrd = &initrds[i];
 		p = filename_16;
@@ -380,7 +388,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		status = efi_call_phys5(fh->open, fh, &h, filename_16,
 					EFI_FILE_MODE_READ, (u64)0);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to open initrd file: ");
+			efi_printk(sys_table_arg, "Failed to open file file: ");
 			efi_char16_printk(sys_table_arg, filename_16);
 			efi_printk(sys_table_arg, "\n");
 			goto close_handles;
@@ -392,7 +400,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, NULL);
 		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk(sys_table_arg, "Failed to get initrd info size\n");
+			efi_printk(sys_table_arg, "Failed to get file info size\n");
 			goto close_handles;
 		}
 
@@ -400,7 +408,7 @@ grow:
 		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 					EFI_LOADER_DATA, info_sz, &info);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n");
+			efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
 			goto close_handles;
 		}
 
@@ -416,7 +424,7 @@ grow:
 		efi_call_phys1(sys_table_arg->boottime->free_pool, info);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to get initrd info\n");
+			efi_printk(sys_table_arg, "Failed to get file info\n");
 			goto close_handles;
 		}
 
@@ -433,14 +441,14 @@ grow:
 		 * all the initrd's.
 		 */
 		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
-				    &initrd_addr, hdr->initrd_addr_max);
+				    &initrd_addr, max_addr);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
-		if (initrd_addr > hdr->initrd_addr_max) {
+		if (initrd_addr > max_addr) {
 			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_initrd_total;
@@ -461,7 +469,7 @@ grow:
 							initrds[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
-					efi_printk(sys_table_arg, "Failed to read initrd\n");
+					efi_printk(sys_table_arg, "Failed to read file\n");
 					goto free_initrd_total;
 				}
 				addr += chunksize;
@@ -475,8 +483,8 @@ grow:
 
 	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 
-	hdr->ramdisk_image = initrd_addr;
-	hdr->ramdisk_size = initrd_total;
+	*load_addr = initrd_addr;
+	*load_size = initrd_total;
 
 	return status;
 
@@ -489,8 +497,8 @@ close_handles:
 free_initrds:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 fail:
-	hdr->ramdisk_image = 0;
-	hdr->ramdisk_size = 0;
+	*load_addr = 0;
+	*load_size = 0;
 
 	return status;
 }
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 09/17] Generalize handle_ramdisks() and rename to handle_cmdline_files().
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

The handle_cmdline_files now takes the option to handle as a string,
and returns the loaded data through parameters, rather than taking
an x86 specific setup_header structure.  For ARM, this will be used
to load a device tree blob in addition to initrd images.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.c       |    9 +++++-
 drivers/firmware/efi/efi-stub-helper.c |   50 ++++++++++++++++++--------------
 2 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index bcfdcc2..7011e33 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -443,6 +443,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 	u16 *s2;
 	u8 *s1;
 	int i;
+	u64 ramdisk_addr;
+	u64 ramdisk_size;
 
 	sys_table = _table;
 
@@ -531,9 +533,14 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	status = handle_ramdisks(sys_table, image, hdr);
+	status = handle_cmdline_files(sys_table, image,
+				      (char *)(unsigned long)hdr->cmd_line_ptr,
+				      "initrd=", hdr->initrd_addr_max,
+				      &ramdisk_addr, &ramdisk_size);
 	if (status != EFI_SUCCESS)
 		goto fail2;
+	hdr->ramdisk_image = ramdisk_addr;
+	hdr->ramdisk_size = ramdisk_size;
 
 	return boot_params;
 fail2:
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bce0473..854aada 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -267,9 +267,11 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
  * We only support loading an initrd from the same filesystem as the
  * kernel image.
  */
-static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
-				    efi_loaded_image_t *image,
-				    struct setup_header *hdr)
+static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
+					 efi_loaded_image_t *image,
+					 char *cmd_line, char *option_string,
+					 u64 max_addr,
+					 u64 *load_addr, u64 *load_size)
 {
 	struct initrd *initrds;
 	unsigned long initrd_addr;
@@ -285,19 +287,25 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 	initrd_addr = 0;
 	initrd_total = 0;
 
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	str = cmd_line;
 
 	j = 0;			/* See close_handles */
 
+	if (!load_addr || !load_size)
+		return EFI_INVALID_PARAMETER;
+
+	*load_addr = 0;
+	*load_size = 0;
+
 	if (!str || !*str)
 		return EFI_SUCCESS;
 
 	for (nr_initrds = 0; *str; nr_initrds++) {
-		str = strstr(str, "initrd=");
+		str = strstr(str, option_string);
 		if (!str)
 			break;
 
-		str += 7;
+		str += strlen(option_string);
 
 		/* Skip any leading slashes */
 		while (*str == '/' || *str == '\\')
@@ -315,11 +323,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 				nr_initrds * sizeof(*initrds),
 				&initrds);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for file load\n");
 		goto fail;
 	}
 
-	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	str = cmd_line;
 	for (i = 0; i < nr_initrds; i++) {
 		struct initrd *initrd;
 		efi_file_handle_t *h;
@@ -330,11 +338,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		efi_char16_t *p;
 		u64 file_sz;
 
-		str = strstr(str, "initrd=");
+		str = strstr(str, option_string);
 		if (!str)
 			break;
 
-		str += 7;
+		str += strlen(option_string);
 
 		initrd = &initrds[i];
 		p = filename_16;
@@ -380,7 +388,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		status = efi_call_phys5(fh->open, fh, &h, filename_16,
 					EFI_FILE_MODE_READ, (u64)0);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to open initrd file: ");
+			efi_printk(sys_table_arg, "Failed to open file file: ");
 			efi_char16_printk(sys_table_arg, filename_16);
 			efi_printk(sys_table_arg, "\n");
 			goto close_handles;
@@ -392,7 +400,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg,
 		status = efi_call_phys4(h->get_info, h, &info_guid,
 					&info_sz, NULL);
 		if (status != EFI_BUFFER_TOO_SMALL) {
-			efi_printk(sys_table_arg, "Failed to get initrd info size\n");
+			efi_printk(sys_table_arg, "Failed to get file info size\n");
 			goto close_handles;
 		}
 
@@ -400,7 +408,7 @@ grow:
 		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 					EFI_LOADER_DATA, info_sz, &info);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n");
+			efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
 			goto close_handles;
 		}
 
@@ -416,7 +424,7 @@ grow:
 		efi_call_phys1(sys_table_arg->boottime->free_pool, info);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to get initrd info\n");
+			efi_printk(sys_table_arg, "Failed to get file info\n");
 			goto close_handles;
 		}
 
@@ -433,14 +441,14 @@ grow:
 		 * all the initrd's.
 		 */
 		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
-				    &initrd_addr, hdr->initrd_addr_max);
+				    &initrd_addr, max_addr);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
-		if (initrd_addr > hdr->initrd_addr_max) {
+		if (initrd_addr > max_addr) {
 			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_initrd_total;
@@ -461,7 +469,7 @@ grow:
 							initrds[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
-					efi_printk(sys_table_arg, "Failed to read initrd\n");
+					efi_printk(sys_table_arg, "Failed to read file\n");
 					goto free_initrd_total;
 				}
 				addr += chunksize;
@@ -475,8 +483,8 @@ grow:
 
 	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 
-	hdr->ramdisk_image = initrd_addr;
-	hdr->ramdisk_size = initrd_total;
+	*load_addr = initrd_addr;
+	*load_size = initrd_total;
 
 	return status;
 
@@ -489,8 +497,8 @@ close_handles:
 free_initrds:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
 fail:
-	hdr->ramdisk_image = 0;
-	hdr->ramdisk_size = 0;
+	*load_addr = 0;
+	*load_size = 0;
 
 	return status;
 }
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 10/17] Renames in handle_cmdline_files() to complete generalization.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

Rename variables to be not initrd specific, as now the function
loads arbitrary files.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   92 ++++++++++++++++----------------
 1 file changed, 46 insertions(+), 46 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 854aada..0a41638 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -11,7 +11,7 @@
  */
 
 
-struct initrd {
+struct file_info {
 	efi_file_handle_t *handle;
 	u64 size;
 };
@@ -262,10 +262,10 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 
 
 /*
- * Check the cmdline for a LILO-style initrd= arguments.
+ * Check the cmdline for a LILO-style file= arguments.
  *
- * We only support loading an initrd from the same filesystem as the
- * kernel image.
+ * We only support loading a file from the same filesystem as
+ * the kernel image.
  */
 static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					 efi_loaded_image_t *image,
@@ -273,19 +273,19 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					 u64 max_addr,
 					 u64 *load_addr, u64 *load_size)
 {
-	struct initrd *initrds;
-	unsigned long initrd_addr;
+	struct file_info *files;
+	unsigned long file_addr;
 	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
-	u64 initrd_total;
+	u64 file_size_total;
 	efi_file_io_interface_t *io;
 	efi_file_handle_t *fh;
 	efi_status_t status;
-	int nr_initrds;
+	int nr_files;
 	char *str;
 	int i, j, k;
 
-	initrd_addr = 0;
-	initrd_total = 0;
+	file_addr = 0;
+	file_size_total = 0;
 
 	str = cmd_line;
 
@@ -300,7 +300,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	if (!str || !*str)
 		return EFI_SUCCESS;
 
-	for (nr_initrds = 0; *str; nr_initrds++) {
+	for (nr_files = 0; *str; nr_files++) {
 		str = strstr(str, option_string);
 		if (!str)
 			break;
@@ -315,21 +315,21 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			str++;
 	}
 
-	if (!nr_initrds)
+	if (!nr_files)
 		return EFI_SUCCESS;
 
 	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
-				nr_initrds * sizeof(*initrds),
-				&initrds);
+				nr_files * sizeof(*files),
+				&files);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for file load\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n");
 		goto fail;
 	}
 
 	str = cmd_line;
-	for (i = 0; i < nr_initrds; i++) {
-		struct initrd *initrd;
+	for (i = 0; i < nr_files; i++) {
+		struct file_info *file;
 		efi_file_handle_t *h;
 		efi_file_info_t *info;
 		efi_char16_t filename_16[256];
@@ -344,7 +344,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 		str += strlen(option_string);
 
-		initrd = &initrds[i];
+		file = &files[i];
 		p = filename_16;
 
 		/* Skip any leading slashes */
@@ -375,13 +375,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					image->device_handle, &fs_proto, &io);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
-				goto free_initrds;
+				goto free_files;
 			}
 
 			status = efi_call_phys2(io->open_volume, io, &fh);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to open volume\n");
-				goto free_initrds;
+				goto free_files;
 			}
 		}
 
@@ -394,7 +394,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			goto close_handles;
 		}
 
-		initrd->handle = h;
+		file->handle = h;
 
 		info_sz = 0;
 		status = efi_call_phys4(h->get_info, h, &info_guid,
@@ -428,37 +428,37 @@ grow:
 			goto close_handles;
 		}
 
-		initrd->size = file_sz;
-		initrd_total += file_sz;
+		file->size = file_sz;
+		file_size_total += file_sz;
 	}
 
-	if (initrd_total) {
+	if (file_size_total) {
 		unsigned long addr;
 
 		/*
-		 * Multiple initrd's need to be at consecutive
-		 * addresses in memory, so allocate enough memory for
-		 * all the initrd's.
+		 * Multiple files need to be at consecutive addresses in memory,
+		 * so allocate enough memory for all the files.  This is used
+		 * for loading multiple files.
 		 */
-		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
-				    &initrd_addr, max_addr);
+		status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000,
+				    &file_addr, max_addr);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
+			efi_printk(sys_table_arg, "Failed to alloc highmem for files\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
-		if (initrd_addr > max_addr) {
+		if (file_addr > max_addr) {
 			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
-			goto free_initrd_total;
+			goto free_file_total;
 		}
 
-		addr = initrd_addr;
-		for (j = 0; j < nr_initrds; j++) {
+		addr = file_addr;
+		for (j = 0; j < nr_files; j++) {
 			u64 size;
 
-			size = initrds[j].size;
+			size = files[j].size;
 			while (size) {
 				u64 chunksize;
 				if (size > EFI_READ_CHUNK_SIZE)
@@ -466,36 +466,36 @@ grow:
 				else
 					chunksize = size;
 				status = efi_call_phys3(fh->read,
-							initrds[j].handle,
+							files[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
 					efi_printk(sys_table_arg, "Failed to read file\n");
-					goto free_initrd_total;
+					goto free_file_total;
 				}
 				addr += chunksize;
 				size -= chunksize;
 			}
 
-			efi_call_phys1(fh->close, initrds[j].handle);
+			efi_call_phys1(fh->close, files[j].handle);
 		}
 
 	}
 
-	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, files);
 
-	*load_addr = initrd_addr;
-	*load_size = initrd_total;
+	*load_addr = file_addr;
+	*load_size = file_size_total;
 
 	return status;
 
-free_initrd_total:
-	efi_free(sys_table_arg, initrd_total, initrd_addr);
+free_file_total:
+	efi_free(sys_table_arg, file_size_total, file_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
-		efi_call_phys1(fh->close, initrds[k].handle);
-free_initrds:
-	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
+		efi_call_phys1(fh->close, files[k].handle);
+free_files:
+	efi_call_phys1(sys_table_arg->boottime->free_pool, files);
 fail:
 	*load_addr = 0;
 	*load_size = 0;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 10/17] Renames in handle_cmdline_files() to complete generalization.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8, Roy Franz

Rename variables to be not initrd specific, as now the function
loads arbitrary files.

Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   92 ++++++++++++++++----------------
 1 file changed, 46 insertions(+), 46 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 854aada..0a41638 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -11,7 +11,7 @@
  */
 
 
-struct initrd {
+struct file_info {
 	efi_file_handle_t *handle;
 	u64 size;
 };
@@ -262,10 +262,10 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 
 
 /*
- * Check the cmdline for a LILO-style initrd= arguments.
+ * Check the cmdline for a LILO-style file= arguments.
  *
- * We only support loading an initrd from the same filesystem as the
- * kernel image.
+ * We only support loading a file from the same filesystem as
+ * the kernel image.
  */
 static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					 efi_loaded_image_t *image,
@@ -273,19 +273,19 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					 u64 max_addr,
 					 u64 *load_addr, u64 *load_size)
 {
-	struct initrd *initrds;
-	unsigned long initrd_addr;
+	struct file_info *files;
+	unsigned long file_addr;
 	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
-	u64 initrd_total;
+	u64 file_size_total;
 	efi_file_io_interface_t *io;
 	efi_file_handle_t *fh;
 	efi_status_t status;
-	int nr_initrds;
+	int nr_files;
 	char *str;
 	int i, j, k;
 
-	initrd_addr = 0;
-	initrd_total = 0;
+	file_addr = 0;
+	file_size_total = 0;
 
 	str = cmd_line;
 
@@ -300,7 +300,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	if (!str || !*str)
 		return EFI_SUCCESS;
 
-	for (nr_initrds = 0; *str; nr_initrds++) {
+	for (nr_files = 0; *str; nr_files++) {
 		str = strstr(str, option_string);
 		if (!str)
 			break;
@@ -315,21 +315,21 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			str++;
 	}
 
-	if (!nr_initrds)
+	if (!nr_files)
 		return EFI_SUCCESS;
 
 	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
-				nr_initrds * sizeof(*initrds),
-				&initrds);
+				nr_files * sizeof(*files),
+				&files);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for file load\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n");
 		goto fail;
 	}
 
 	str = cmd_line;
-	for (i = 0; i < nr_initrds; i++) {
-		struct initrd *initrd;
+	for (i = 0; i < nr_files; i++) {
+		struct file_info *file;
 		efi_file_handle_t *h;
 		efi_file_info_t *info;
 		efi_char16_t filename_16[256];
@@ -344,7 +344,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 		str += strlen(option_string);
 
-		initrd = &initrds[i];
+		file = &files[i];
 		p = filename_16;
 
 		/* Skip any leading slashes */
@@ -375,13 +375,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					image->device_handle, &fs_proto, &io);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
-				goto free_initrds;
+				goto free_files;
 			}
 
 			status = efi_call_phys2(io->open_volume, io, &fh);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to open volume\n");
-				goto free_initrds;
+				goto free_files;
 			}
 		}
 
@@ -394,7 +394,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			goto close_handles;
 		}
 
-		initrd->handle = h;
+		file->handle = h;
 
 		info_sz = 0;
 		status = efi_call_phys4(h->get_info, h, &info_guid,
@@ -428,37 +428,37 @@ grow:
 			goto close_handles;
 		}
 
-		initrd->size = file_sz;
-		initrd_total += file_sz;
+		file->size = file_sz;
+		file_size_total += file_sz;
 	}
 
-	if (initrd_total) {
+	if (file_size_total) {
 		unsigned long addr;
 
 		/*
-		 * Multiple initrd's need to be at consecutive
-		 * addresses in memory, so allocate enough memory for
-		 * all the initrd's.
+		 * Multiple files need to be at consecutive addresses in memory,
+		 * so allocate enough memory for all the files.  This is used
+		 * for loading multiple files.
 		 */
-		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
-				    &initrd_addr, max_addr);
+		status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000,
+				    &file_addr, max_addr);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
+			efi_printk(sys_table_arg, "Failed to alloc highmem for files\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
-		if (initrd_addr > max_addr) {
+		if (file_addr > max_addr) {
 			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
-			goto free_initrd_total;
+			goto free_file_total;
 		}
 
-		addr = initrd_addr;
-		for (j = 0; j < nr_initrds; j++) {
+		addr = file_addr;
+		for (j = 0; j < nr_files; j++) {
 			u64 size;
 
-			size = initrds[j].size;
+			size = files[j].size;
 			while (size) {
 				u64 chunksize;
 				if (size > EFI_READ_CHUNK_SIZE)
@@ -466,36 +466,36 @@ grow:
 				else
 					chunksize = size;
 				status = efi_call_phys3(fh->read,
-							initrds[j].handle,
+							files[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
 					efi_printk(sys_table_arg, "Failed to read file\n");
-					goto free_initrd_total;
+					goto free_file_total;
 				}
 				addr += chunksize;
 				size -= chunksize;
 			}
 
-			efi_call_phys1(fh->close, initrds[j].handle);
+			efi_call_phys1(fh->close, files[j].handle);
 		}
 
 	}
 
-	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, files);
 
-	*load_addr = initrd_addr;
-	*load_size = initrd_total;
+	*load_addr = file_addr;
+	*load_size = file_size_total;
 
 	return status;
 
-free_initrd_total:
-	efi_free(sys_table_arg, initrd_total, initrd_addr);
+free_file_total:
+	efi_free(sys_table_arg, file_size_total, file_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
-		efi_call_phys1(fh->close, initrds[k].handle);
-free_initrds:
-	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
+		efi_call_phys1(fh->close, files[k].handle);
+free_files:
+	efi_call_phys1(sys_table_arg->boottime->free_pool, files);
 fail:
 	*load_addr = 0;
 	*load_size = 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 10/17] Renames in handle_cmdline_files() to complete generalization.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

Rename variables to be not initrd specific, as now the function
loads arbitrary files.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   92 ++++++++++++++++----------------
 1 file changed, 46 insertions(+), 46 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 854aada..0a41638 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -11,7 +11,7 @@
  */
 
 
-struct initrd {
+struct file_info {
 	efi_file_handle_t *handle;
 	u64 size;
 };
@@ -262,10 +262,10 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 
 
 /*
- * Check the cmdline for a LILO-style initrd= arguments.
+ * Check the cmdline for a LILO-style file= arguments.
  *
- * We only support loading an initrd from the same filesystem as the
- * kernel image.
+ * We only support loading a file from the same filesystem as
+ * the kernel image.
  */
 static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					 efi_loaded_image_t *image,
@@ -273,19 +273,19 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					 u64 max_addr,
 					 u64 *load_addr, u64 *load_size)
 {
-	struct initrd *initrds;
-	unsigned long initrd_addr;
+	struct file_info *files;
+	unsigned long file_addr;
 	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
-	u64 initrd_total;
+	u64 file_size_total;
 	efi_file_io_interface_t *io;
 	efi_file_handle_t *fh;
 	efi_status_t status;
-	int nr_initrds;
+	int nr_files;
 	char *str;
 	int i, j, k;
 
-	initrd_addr = 0;
-	initrd_total = 0;
+	file_addr = 0;
+	file_size_total = 0;
 
 	str = cmd_line;
 
@@ -300,7 +300,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	if (!str || !*str)
 		return EFI_SUCCESS;
 
-	for (nr_initrds = 0; *str; nr_initrds++) {
+	for (nr_files = 0; *str; nr_files++) {
 		str = strstr(str, option_string);
 		if (!str)
 			break;
@@ -315,21 +315,21 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			str++;
 	}
 
-	if (!nr_initrds)
+	if (!nr_files)
 		return EFI_SUCCESS;
 
 	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
-				nr_initrds * sizeof(*initrds),
-				&initrds);
+				nr_files * sizeof(*files),
+				&files);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for file load\n");
+		efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n");
 		goto fail;
 	}
 
 	str = cmd_line;
-	for (i = 0; i < nr_initrds; i++) {
-		struct initrd *initrd;
+	for (i = 0; i < nr_files; i++) {
+		struct file_info *file;
 		efi_file_handle_t *h;
 		efi_file_info_t *info;
 		efi_char16_t filename_16[256];
@@ -344,7 +344,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 		str += strlen(option_string);
 
-		initrd = &initrds[i];
+		file = &files[i];
 		p = filename_16;
 
 		/* Skip any leading slashes */
@@ -375,13 +375,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 					image->device_handle, &fs_proto, &io);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
-				goto free_initrds;
+				goto free_files;
 			}
 
 			status = efi_call_phys2(io->open_volume, io, &fh);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to open volume\n");
-				goto free_initrds;
+				goto free_files;
 			}
 		}
 
@@ -394,7 +394,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			goto close_handles;
 		}
 
-		initrd->handle = h;
+		file->handle = h;
 
 		info_sz = 0;
 		status = efi_call_phys4(h->get_info, h, &info_guid,
@@ -428,37 +428,37 @@ grow:
 			goto close_handles;
 		}
 
-		initrd->size = file_sz;
-		initrd_total += file_sz;
+		file->size = file_sz;
+		file_size_total += file_sz;
 	}
 
-	if (initrd_total) {
+	if (file_size_total) {
 		unsigned long addr;
 
 		/*
-		 * Multiple initrd's need to be at consecutive
-		 * addresses in memory, so allocate enough memory for
-		 * all the initrd's.
+		 * Multiple files need to be at consecutive addresses in memory,
+		 * so allocate enough memory for all the files.  This is used
+		 * for loading multiple files.
 		 */
-		status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000,
-				    &initrd_addr, max_addr);
+		status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000,
+				    &file_addr, max_addr);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n");
+			efi_printk(sys_table_arg, "Failed to alloc highmem for files\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
-		if (initrd_addr > max_addr) {
+		if (file_addr > max_addr) {
 			efi_printk(sys_table_arg, "We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
-			goto free_initrd_total;
+			goto free_file_total;
 		}
 
-		addr = initrd_addr;
-		for (j = 0; j < nr_initrds; j++) {
+		addr = file_addr;
+		for (j = 0; j < nr_files; j++) {
 			u64 size;
 
-			size = initrds[j].size;
+			size = files[j].size;
 			while (size) {
 				u64 chunksize;
 				if (size > EFI_READ_CHUNK_SIZE)
@@ -466,36 +466,36 @@ grow:
 				else
 					chunksize = size;
 				status = efi_call_phys3(fh->read,
-							initrds[j].handle,
+							files[j].handle,
 							&chunksize, addr);
 				if (status != EFI_SUCCESS) {
 					efi_printk(sys_table_arg, "Failed to read file\n");
-					goto free_initrd_total;
+					goto free_file_total;
 				}
 				addr += chunksize;
 				size -= chunksize;
 			}
 
-			efi_call_phys1(fh->close, initrds[j].handle);
+			efi_call_phys1(fh->close, files[j].handle);
 		}
 
 	}
 
-	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
+	efi_call_phys1(sys_table_arg->boottime->free_pool, files);
 
-	*load_addr = initrd_addr;
-	*load_size = initrd_total;
+	*load_addr = file_addr;
+	*load_size = file_size_total;
 
 	return status;
 
-free_initrd_total:
-	efi_free(sys_table_arg, initrd_total, initrd_addr);
+free_file_total:
+	efi_free(sys_table_arg, file_size_total, file_addr);
 
 close_handles:
 	for (k = j; k < i; k++)
-		efi_call_phys1(fh->close, initrds[k].handle);
-free_initrds:
-	efi_call_phys1(sys_table_arg->boottime->free_pool, initrds);
+		efi_call_phys1(fh->close, files[k].handle);
+free_files:
+	efi_call_phys1(sys_table_arg->boottime->free_pool, files);
 fail:
 	*load_addr = 0;
 	*load_size = 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 11/17] Move EFI_READ_CHUNK_SIZE define to shared location.
  2013-08-07  3:44 ` Roy Franz
  (?)
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

This #define is only used the the shared code, so move
it there.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.h       |    1 -
 drivers/firmware/efi/efi-stub-helper.c |    2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index faa0bdf..bafbd94 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -12,7 +12,6 @@
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
 #define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
-#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 #define EFI_CONSOLE_OUT_DEVICE_GUID    \
 	EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 0a41638..521a4c4 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -9,7 +9,7 @@
  * under the terms of the GNU General Public License version 2.
  *
  */
-
+#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 struct file_info {
 	efi_file_handle_t *handle;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 11/17] Move EFI_READ_CHUNK_SIZE define to shared location.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: Roy Franz, dave.martin, leif.lindholm

This #define is only used the the shared code, so move
it there.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.h       |    1 -
 drivers/firmware/efi/efi-stub-helper.c |    2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index faa0bdf..bafbd94 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -12,7 +12,6 @@
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
 #define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
-#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 #define EFI_CONSOLE_OUT_DEVICE_GUID    \
 	EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 0a41638..521a4c4 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -9,7 +9,7 @@
  * under the terms of the GNU General Public License version 2.
  *
  */
-
+#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 struct file_info {
 	efi_file_handle_t *handle;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 11/17] Move EFI_READ_CHUNK_SIZE define to shared location.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

This #define is only used the the shared code, so move
it there.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/x86/boot/compressed/eboot.h       |    1 -
 drivers/firmware/efi/efi-stub-helper.c |    2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index faa0bdf..bafbd94 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -12,7 +12,6 @@
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
 #define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
-#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 #define EFI_CONSOLE_OUT_DEVICE_GUID    \
 	EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 0a41638..521a4c4 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -9,7 +9,7 @@
  * under the terms of the GNU General Public License version 2.
  *
  */
-
+#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 struct file_info {
 	efi_file_handle_t *handle;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

The x86/AMD64 EFI stubs must us a call wrapper to convert between
the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
the ABIs are compatible, so we can directly invoke the function
pointers.  The functions that are used by the ARM stub are updated
to match the EFI definitions.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 51f5641..96bb866 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -39,6 +39,8 @@
 typedef unsigned long efi_status_t;
 typedef u8 efi_bool_t;
 typedef u16 efi_char16_t;		/* UNICODE character */
+typedef u64 efi_physical_addr_t;
+typedef void *efi_handle_t;
 
 
 typedef struct {
@@ -96,6 +98,7 @@ typedef	struct {
 #define EFI_MEMORY_DESCRIPTOR_VERSION	1
 
 #define EFI_PAGE_SHIFT		12
+#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
 
 typedef struct {
 	u32 type;
@@ -157,11 +160,12 @@ typedef struct {
 	efi_table_hdr_t hdr;
 	void *raise_tpl;
 	void *restore_tpl;
-	void *allocate_pages;
-	void *free_pages;
-	void *get_memory_map;
-	void *allocate_pool;
-	void *free_pool;
+	int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
+	int (*free_pages)(efi_physical_addr_t, unsigned long);
+	int (*get_memory_map)(unsigned long *, void *, unsigned long *,
+			      unsigned long *, u32 *);
+	int (*allocate_pool)(int, unsigned long, void **);
+	int (*free_pool)(void *);
 	void *create_event;
 	void *set_timer;
 	void *wait_for_event;
@@ -171,7 +175,7 @@ typedef struct {
 	void *install_protocol_interface;
 	void *reinstall_protocol_interface;
 	void *uninstall_protocol_interface;
-	void *handle_protocol;
+	int (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
 	void *__reserved;
 	void *register_protocol_notify;
 	void *locate_handle;
@@ -181,7 +185,7 @@ typedef struct {
 	void *start_image;
 	void *exit;
 	void *unload_image;
-	void *exit_boot_services;
+	int (*exit_boot_services)(efi_handle_t, unsigned long);
 	void *get_next_monotonic_count;
 	void *stall;
 	void *set_watchdog_timer;
@@ -488,10 +492,6 @@ typedef struct {
 	unsigned long unload;
 } efi_loaded_image_t;
 
-typedef struct {
-	u64 revision;
-	void *open_volume;
-} efi_file_io_interface_t;
 
 typedef struct {
 	u64 size;
@@ -504,20 +504,28 @@ typedef struct {
 	efi_char16_t filename[1];
 } efi_file_info_t;
 
-typedef struct {
+typedef struct _efi_file_handle {
 	u64 revision;
-	void *open;
-	void *close;
+	int (*open)(struct _efi_file_handle *, struct _efi_file_handle **,
+		    efi_char16_t *, u64, u64);
+	int (*close)(struct _efi_file_handle *);
 	void *delete;
-	void *read;
+	int (*read)(struct _efi_file_handle *, unsigned long *, void *);
 	void *write;
 	void *get_position;
 	void *set_position;
-	void *get_info;
+	int (*get_info)(struct _efi_file_handle *, efi_guid_t *,
+			unsigned long *, void *);
 	void *set_info;
 	void *flush;
 } efi_file_handle_t;
 
+typedef struct _efi_file_io_interface {
+	u64 revision;
+	int (*open_volume)(struct _efi_file_io_interface *,
+			   efi_file_handle_t **);
+} efi_file_io_interface_t;
+
 #define EFI_FILE_MODE_READ	0x0000000000000001
 #define EFI_FILE_MODE_WRITE	0x0000000000000002
 #define EFI_FILE_MODE_CREATE	0x8000000000000000
@@ -787,7 +795,7 @@ struct efivar_entry {
 
 struct efi_simple_text_output_protocol {
 	void *reset;
-	void *output_string;
+	int (*output_string)(void *, void *);
 	void *test_string;
 };
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8, Roy Franz

The x86/AMD64 EFI stubs must us a call wrapper to convert between
the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
the ABIs are compatible, so we can directly invoke the function
pointers.  The functions that are used by the ARM stub are updated
to match the EFI definitions.

Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 51f5641..96bb866 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -39,6 +39,8 @@
 typedef unsigned long efi_status_t;
 typedef u8 efi_bool_t;
 typedef u16 efi_char16_t;		/* UNICODE character */
+typedef u64 efi_physical_addr_t;
+typedef void *efi_handle_t;
 
 
 typedef struct {
@@ -96,6 +98,7 @@ typedef	struct {
 #define EFI_MEMORY_DESCRIPTOR_VERSION	1
 
 #define EFI_PAGE_SHIFT		12
+#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
 
 typedef struct {
 	u32 type;
@@ -157,11 +160,12 @@ typedef struct {
 	efi_table_hdr_t hdr;
 	void *raise_tpl;
 	void *restore_tpl;
-	void *allocate_pages;
-	void *free_pages;
-	void *get_memory_map;
-	void *allocate_pool;
-	void *free_pool;
+	int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
+	int (*free_pages)(efi_physical_addr_t, unsigned long);
+	int (*get_memory_map)(unsigned long *, void *, unsigned long *,
+			      unsigned long *, u32 *);
+	int (*allocate_pool)(int, unsigned long, void **);
+	int (*free_pool)(void *);
 	void *create_event;
 	void *set_timer;
 	void *wait_for_event;
@@ -171,7 +175,7 @@ typedef struct {
 	void *install_protocol_interface;
 	void *reinstall_protocol_interface;
 	void *uninstall_protocol_interface;
-	void *handle_protocol;
+	int (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
 	void *__reserved;
 	void *register_protocol_notify;
 	void *locate_handle;
@@ -181,7 +185,7 @@ typedef struct {
 	void *start_image;
 	void *exit;
 	void *unload_image;
-	void *exit_boot_services;
+	int (*exit_boot_services)(efi_handle_t, unsigned long);
 	void *get_next_monotonic_count;
 	void *stall;
 	void *set_watchdog_timer;
@@ -488,10 +492,6 @@ typedef struct {
 	unsigned long unload;
 } efi_loaded_image_t;
 
-typedef struct {
-	u64 revision;
-	void *open_volume;
-} efi_file_io_interface_t;
 
 typedef struct {
 	u64 size;
@@ -504,20 +504,28 @@ typedef struct {
 	efi_char16_t filename[1];
 } efi_file_info_t;
 
-typedef struct {
+typedef struct _efi_file_handle {
 	u64 revision;
-	void *open;
-	void *close;
+	int (*open)(struct _efi_file_handle *, struct _efi_file_handle **,
+		    efi_char16_t *, u64, u64);
+	int (*close)(struct _efi_file_handle *);
 	void *delete;
-	void *read;
+	int (*read)(struct _efi_file_handle *, unsigned long *, void *);
 	void *write;
 	void *get_position;
 	void *set_position;
-	void *get_info;
+	int (*get_info)(struct _efi_file_handle *, efi_guid_t *,
+			unsigned long *, void *);
 	void *set_info;
 	void *flush;
 } efi_file_handle_t;
 
+typedef struct _efi_file_io_interface {
+	u64 revision;
+	int (*open_volume)(struct _efi_file_io_interface *,
+			   efi_file_handle_t **);
+} efi_file_io_interface_t;
+
 #define EFI_FILE_MODE_READ	0x0000000000000001
 #define EFI_FILE_MODE_WRITE	0x0000000000000002
 #define EFI_FILE_MODE_CREATE	0x8000000000000000
@@ -787,7 +795,7 @@ struct efivar_entry {
 
 struct efi_simple_text_output_protocol {
 	void *reset;
-	void *output_string;
+	int (*output_string)(void *, void *);
 	void *test_string;
 };
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

The x86/AMD64 EFI stubs must us a call wrapper to convert between
the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
the ABIs are compatible, so we can directly invoke the function
pointers.  The functions that are used by the ARM stub are updated
to match the EFI definitions.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 51f5641..96bb866 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -39,6 +39,8 @@
 typedef unsigned long efi_status_t;
 typedef u8 efi_bool_t;
 typedef u16 efi_char16_t;		/* UNICODE character */
+typedef u64 efi_physical_addr_t;
+typedef void *efi_handle_t;
 
 
 typedef struct {
@@ -96,6 +98,7 @@ typedef	struct {
 #define EFI_MEMORY_DESCRIPTOR_VERSION	1
 
 #define EFI_PAGE_SHIFT		12
+#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
 
 typedef struct {
 	u32 type;
@@ -157,11 +160,12 @@ typedef struct {
 	efi_table_hdr_t hdr;
 	void *raise_tpl;
 	void *restore_tpl;
-	void *allocate_pages;
-	void *free_pages;
-	void *get_memory_map;
-	void *allocate_pool;
-	void *free_pool;
+	int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
+	int (*free_pages)(efi_physical_addr_t, unsigned long);
+	int (*get_memory_map)(unsigned long *, void *, unsigned long *,
+			      unsigned long *, u32 *);
+	int (*allocate_pool)(int, unsigned long, void **);
+	int (*free_pool)(void *);
 	void *create_event;
 	void *set_timer;
 	void *wait_for_event;
@@ -171,7 +175,7 @@ typedef struct {
 	void *install_protocol_interface;
 	void *reinstall_protocol_interface;
 	void *uninstall_protocol_interface;
-	void *handle_protocol;
+	int (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
 	void *__reserved;
 	void *register_protocol_notify;
 	void *locate_handle;
@@ -181,7 +185,7 @@ typedef struct {
 	void *start_image;
 	void *exit;
 	void *unload_image;
-	void *exit_boot_services;
+	int (*exit_boot_services)(efi_handle_t, unsigned long);
 	void *get_next_monotonic_count;
 	void *stall;
 	void *set_watchdog_timer;
@@ -488,10 +492,6 @@ typedef struct {
 	unsigned long unload;
 } efi_loaded_image_t;
 
-typedef struct {
-	u64 revision;
-	void *open_volume;
-} efi_file_io_interface_t;
 
 typedef struct {
 	u64 size;
@@ -504,20 +504,28 @@ typedef struct {
 	efi_char16_t filename[1];
 } efi_file_info_t;
 
-typedef struct {
+typedef struct _efi_file_handle {
 	u64 revision;
-	void *open;
-	void *close;
+	int (*open)(struct _efi_file_handle *, struct _efi_file_handle **,
+		    efi_char16_t *, u64, u64);
+	int (*close)(struct _efi_file_handle *);
 	void *delete;
-	void *read;
+	int (*read)(struct _efi_file_handle *, unsigned long *, void *);
 	void *write;
 	void *get_position;
 	void *set_position;
-	void *get_info;
+	int (*get_info)(struct _efi_file_handle *, efi_guid_t *,
+			unsigned long *, void *);
 	void *set_info;
 	void *flush;
 } efi_file_handle_t;
 
+typedef struct _efi_file_io_interface {
+	u64 revision;
+	int (*open_volume)(struct _efi_file_io_interface *,
+			   efi_file_handle_t **);
+} efi_file_io_interface_t;
+
 #define EFI_FILE_MODE_READ	0x0000000000000001
 #define EFI_FILE_MODE_WRITE	0x0000000000000002
 #define EFI_FILE_MODE_CREATE	0x8000000000000000
@@ -787,7 +795,7 @@ struct efivar_entry {
 
 struct efi_simple_text_output_protocol {
 	void *reset;
-	void *output_string;
+	int (*output_string)(void *, void *);
 	void *test_string;
 };
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 13/17] Fix types in EFI calls to match EFI function definitions.
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

EFI calls can made directly on ARM, so the function pointers
are directly invoked.  This allows types to be checked at
compile time, so here we ensure that the parameters match
the function signature.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 521a4c4..bbe02fd 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -321,7 +321,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
 				nr_files * sizeof(*files),
-				&files);
+				(void **)&files);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n");
 		goto fail;
@@ -372,7 +372,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			boottime = sys_table_arg->boottime;
 
 			status = efi_call_phys3(boottime->handle_protocol,
-					image->device_handle, &fs_proto, &io);
+					image->device_handle, &fs_proto,
+						(void **)&io);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
 				goto free_files;
@@ -406,7 +407,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 grow:
 		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
-					EFI_LOADER_DATA, info_sz, &info);
+					EFI_LOADER_DATA, info_sz,
+					(void **)&info);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
 			goto close_handles;
@@ -456,18 +458,19 @@ grow:
 
 		addr = file_addr;
 		for (j = 0; j < nr_files; j++) {
-			u64 size;
+			unsigned long size;
 
 			size = files[j].size;
 			while (size) {
-				u64 chunksize;
+				unsigned long chunksize;
 				if (size > EFI_READ_CHUNK_SIZE)
 					chunksize = EFI_READ_CHUNK_SIZE;
 				else
 					chunksize = size;
 				status = efi_call_phys3(fh->read,
 							files[j].handle,
-							&chunksize, addr);
+							&chunksize,
+							(void *)addr);
 				if (status != EFI_SUCCESS) {
 					efi_printk(sys_table_arg, "Failed to read file\n");
 					goto free_file_total;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 13/17] Fix types in EFI calls to match EFI function definitions.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

EFI calls can made directly on ARM, so the function pointers
are directly invoked.  This allows types to be checked at
compile time, so here we ensure that the parameters match
the function signature.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |   15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 521a4c4..bbe02fd 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -321,7 +321,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
 				EFI_LOADER_DATA,
 				nr_files * sizeof(*files),
-				&files);
+				(void **)&files);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n");
 		goto fail;
@@ -372,7 +372,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 			boottime = sys_table_arg->boottime;
 
 			status = efi_call_phys3(boottime->handle_protocol,
-					image->device_handle, &fs_proto, &io);
+					image->device_handle, &fs_proto,
+						(void **)&io);
 			if (status != EFI_SUCCESS) {
 				efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
 				goto free_files;
@@ -406,7 +407,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 grow:
 		status = efi_call_phys3(sys_table_arg->boottime->allocate_pool,
-					EFI_LOADER_DATA, info_sz, &info);
+					EFI_LOADER_DATA, info_sz,
+					(void **)&info);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
 			goto close_handles;
@@ -456,18 +458,19 @@ grow:
 
 		addr = file_addr;
 		for (j = 0; j < nr_files; j++) {
-			u64 size;
+			unsigned long size;
 
 			size = files[j].size;
 			while (size) {
-				u64 chunksize;
+				unsigned long chunksize;
 				if (size > EFI_READ_CHUNK_SIZE)
 					chunksize = EFI_READ_CHUNK_SIZE;
 				else
 					chunksize = size;
 				status = efi_call_phys3(fh->read,
 							files[j].handle,
-							&chunksize, addr);
+							&chunksize,
+							(void *)addr);
 				if (status != EFI_SUCCESS) {
 					efi_printk(sys_table_arg, "Failed to read file\n");
 					goto free_file_total;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 14/17] resolve warnings found on ARM compile
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

2 unused labels
1 "value computed is not used"


Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bbe02fd..3515c3a 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -166,7 +166,6 @@ again:
 		*addr = max_addr;
 	}
 
-free_pool:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 
 fail:
@@ -242,7 +241,6 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 	if (i == map_size / desc_size)
 		status = EFI_NOT_FOUND;
 
-free_pool:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 fail:
 	return status;
@@ -357,7 +355,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 			if (*str == '/') {
 				*p++ = '\\';
-				*str++;
+				str++;
 			} else {
 				*p++ = *str++;
 			}
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 14/17] resolve warnings found on ARM compile
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

2 unused labels
1 "value computed is not used"


Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 drivers/firmware/efi/efi-stub-helper.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index bbe02fd..3515c3a 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -166,7 +166,6 @@ again:
 		*addr = max_addr;
 	}
 
-free_pool:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 
 fail:
@@ -242,7 +241,6 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
 	if (i == map_size / desc_size)
 		status = EFI_NOT_FOUND;
 
-free_pool:
 	efi_call_phys1(sys_table_arg->boottime->free_pool, map);
 fail:
 	return status;
@@ -357,7 +355,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 			if (*str == '/') {
 				*p++ = '\\';
-				*str++;
+				str++;
 			} else {
 				*p++ = *str++;
 			}
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 15/17] Add strstr to compressed string.c for ARM.
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

The shared efi-stub-helper.c functions require a strstr
implementation.
Implementation copied from arch/x86/boot/string.c

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/arm/boot/compressed/string.c |   21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm/boot/compressed/string.c b/arch/arm/boot/compressed/string.c
index 36e53ef..5397792 100644
--- a/arch/arm/boot/compressed/string.c
+++ b/arch/arm/boot/compressed/string.c
@@ -111,6 +111,27 @@ char *strchr(const char *s, int c)
 	return (char *)s;
 }
 
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char *strstr(const char *s1, const char *s2)
+{
+	size_t l1, l2;
+
+	l2 = strlen(s2);
+	if (!l2)
+		return (char *)s1;
+	l1 = strlen(s1);
+	while (l1 >= l2) {
+		l1--;
+		if (!memcmp(s1, s2, l2))
+			return (char *)s1;
+		s1++;
+	}
+	return NULL;
+}
 #undef memset
 
 void *memset(void *s, int c, size_t count)
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 15/17] Add strstr to compressed string.c for ARM.
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

The shared efi-stub-helper.c functions require a strstr
implementation.
Implementation copied from arch/x86/boot/string.c

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/arm/boot/compressed/string.c |   21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm/boot/compressed/string.c b/arch/arm/boot/compressed/string.c
index 36e53ef..5397792 100644
--- a/arch/arm/boot/compressed/string.c
+++ b/arch/arm/boot/compressed/string.c
@@ -111,6 +111,27 @@ char *strchr(const char *s, int c)
 	return (char *)s;
 }
 
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char *strstr(const char *s1, const char *s2)
+{
+	size_t l1, l2;
+
+	l2 = strlen(s2);
+	if (!l2)
+		return (char *)s1;
+	l1 = strlen(s1);
+	while (l1 >= l2) {
+		l1--;
+		if (!memcmp(s1, s2, l2))
+			return (char *)s1;
+		s1++;
+	}
+	return NULL;
+}
 #undef memset
 
 void *memset(void *s, int c, size_t count)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
operations similarly to the x86 stub: it is a shim between the EFI firmware
and the normal zImage entry point, and sets up the environment that the
zImage is expecting.  This includes loading the initrd (optionaly) and
device tree from the system partition based on the kernel command line.
The stub updates the device tree as necessary, including adding reserved
memory regions and adding entries for EFI runtime services. The PE/COFF
"MZ" header at offset 0 results in the first instruction being an add
that corrupts r5, which is not used by the zImage interface.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/arm/boot/compressed/Makefile     |   18 +-
 arch/arm/boot/compressed/efi-header.S |  114 ++++++++
 arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
 arch/arm/boot/compressed/head.S       |   90 +++++-
 4 files changed, 728 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/compressed/efi-header.S
 create mode 100644 arch/arm/boot/compressed/efi-stub.c

diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 7ac1610..c62826a 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
 $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
 	$(addprefix $(obj)/,$(libfdt_hdrs))
 
+$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
+	$(addprefix $(obj)/,$(libfdt_hdrs))
+
 ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
-OBJS	+= $(libfdt_objs) atags_to_fdt.o
+OBJS	+= atags_to_fdt.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(CONFIG_EFI_STUB),y)
+CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
+OBJS	+= efi-stub.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(USE_LIBFDT),y)
+OBJS	+= $(libfdt_objs)
 endif
 
 targets       := vmlinux vmlinux.lds \
@@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
 KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
 endif
 
-ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
+ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
 asflags-y := -DZIMAGE
 
 # Supply kernel BSS size to the decompressor via a linker symbol.
diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
new file mode 100644
index 0000000..6ff32cc
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-header.S
@@ -0,0 +1,114 @@
+@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
+@
+@ This file contains the PE/COFF header that is part of the
+@ EFI stub.
+@
+
+	.org	0x3c
+	@
+	@ The PE header can be anywhere in the file, but for
+	@ simplicity we keep it together with the MSDOS header
+	@ The offset to the PE/COFF header needs to be at offset
+	@ 0x3C in the MSDOS header.
+	@ The only 2 fields of the MSDOS header that are used are this
+	@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
+	@
+	.long	pe_header			@ Offset to the PE header.
+
+      .align 3
+pe_header:
+
+
+pe_header:
+	.ascii	"PE"
+	.short 	0
+
+coff_header:
+	.short	0x01c2				@ ARM or Thumb
+	.short	2				@ nr_sections
+	.long	0 				@ TimeDateStamp
+	.long	0				@ PointerToSymbolTable
+	.long	1				@ NumberOfSymbols
+	.short	section_table - optional_header	@ SizeOfOptionalHeader
+	.short	0x306				@ Characteristics.
+						@ IMAGE_FILE_32BIT_MACHINE |
+						@ IMAGE_FILE_DEBUG_STRIPPED |
+						@ IMAGE_FILE_EXECUTABLE_IMAGE |
+						@ IMAGE_FILE_LINE_NUMS_STRIPPED
+
+optional_header:
+	.short	0x10b				@ PE32 format
+	.byte	0x02				@ MajorLinkerVersion
+	.byte	0x14				@ MinorLinkerVersion
+
+	.long	0				@ SizeOfCode
+
+	.long	0				@ SizeOfInitializedData
+	.long	0				@ SizeOfUninitializedData
+
+	.long	efi_stub_entry			@ AddressOfEntryPoint
+	.long	efi_stub_entry			@ BaseOfCode
+	.long	0				@ data
+
+extra_header_fields:
+	.long	0				@ ImageBase
+	.long	0x20				@ SectionAlignment
+	.long	0x20				@ FileAlignment
+	.short	0				@ MajorOperatingSystemVersion
+	.short	0				@ MinorOperatingSystemVersion
+	.short	0				@ MajorImageVersion
+	.short	0				@ MinorImageVersion
+	.short	0				@ MajorSubsystemVersion
+	.short	0				@ MinorSubsystemVersion
+	.long	0				@ Win32VersionValue
+
+	.long	_edata				@ SizeOfImage
+
+	@ Everything before the entry point is considered part of the header
+	.long	efi_stub_entry			@ SizeOfHeaders
+	.long	0				@ CheckSum
+	.short	0xa				@ Subsystem (EFI application)
+	.short	0				@ DllCharacteristics
+	.long	0				@ SizeOfStackReserve
+	.long	0				@ SizeOfStackCommit
+	.long	0				@ SizeOfHeapReserve
+	.long	0				@ SizeOfHeapCommit
+	.long	0				@ LoaderFlags
+	.long	0x0				@ NumberOfRvaAndSizes
+
+	# Section table
+section_table:
+
+	#
+	# The EFI application loader requires a relocation section
+	# because EFI applications must be relocatable.  This is a
+	# dummy section as far as we are concerned.
+	#
+	.ascii	".reloc"
+	.byte	0
+	.byte	0			@ end of 0 padding of section name
+	.long	0
+	.long	0
+	.long	0			@ SizeOfRawData
+	.long	0			@ PointerToRawData
+	.long	0			@ PointerToRelocations
+	.long	0			@ PointerToLineNumbers
+	.short	0			@ NumberOfRelocations
+	.short	0			@ NumberOfLineNumbers
+	.long	0x42100040		@ Characteristics (section flags)
+
+
+	.ascii	".text"
+	.byte	0
+	.byte	0
+	.byte	0        		@ end of 0 padding of section name
+	.long	_edata - efi_stub_entry		@ VirtualSize
+	.long	efi_stub_entry			@ VirtualAddress
+	.long	_edata - efi_stub_entry		@ SizeOfRawData
+	.long	efi_stub_entry			@ PointerToRawData
+
+	.long	0		@ PointerToRelocations (0 for executables)
+	.long	0		@ PointerToLineNumbers (0 for executables)
+	.short	0		@ NumberOfRelocations  (0 for executables)
+	.short	0		@ NumberOfLineNumbers  (0 for executables)
+	.long	0xe0500020	@ Characteristics (section flags)
diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
new file mode 100644
index 0000000..b817ea3
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-stub.c
@@ -0,0 +1,514 @@
+/*
+ * linux/arch/arm/boot/compressed/efi-stub.c
+ *
+ * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
+ *
+ * This file implements the EFI boot stub for the ARM kernel
+ *
+ * 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 <libfdt.h>
+
+
+/* Error code returned to ASM code instead of valid FDT address. */
+#define EFI_STUB_ERROR		(~0)
+
+/* EFI function call wrappers.  These are not required for
+ * ARM, but wrappers are required for X86 to convert between
+ * ABIs.  These wrappers are provided to allow code sharing
+ * between X86 and ARM.  Since these wrappers directly invoke the
+ * EFI function pointer, the function pointer type must be properly
+ * defined, which is not the case for X86  One advantage of this is
+ * it allows for type checking of arguments, which is not
+ * possible with the X86 wrappers.
+ */
+#define efi_call_phys0(f)			f()
+#define efi_call_phys1(f, a1)			f(a1)
+#define efi_call_phys2(f, a1, a2)		f(a1, a2)
+#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
+#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
+#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
+
+/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
+ * that for the decompressed kernel.  We have no easy way to tell what
+ * the actuall size of code + data the uncompressed kernel will use.
+ */
+#define MAX_UNCOMP_KERNEL_SIZE	0x02000000
+
+/* The kernel zImage should be located between 32 Mbytes
+ * and 128 MBytes from the base of DRAM.  The min
+ * address leaves space for a maximal size uncompressed image,
+ * and the max address is due to how the zImage decompressor
+ * picks a destination address.
+ */
+#define MAX_ZIMAGE_OFFSET	0x08000000
+#define MIN_ZIMAGE_OFFSET	MAX_UNCOMP_KERNEL_SIZE
+
+#define MAX_CMDLINE_LEN		500
+
+struct fdt_region {
+	u64 base;
+	u64 size;
+};
+
+/*
+ * Additional size that could be used for FDT entries added by
+ * the UEFI OS Loader Estimation based on:
+ * EDID (300bytes) + bootargs  + initrd region (20bytes)
+ * + system memory region (20bytes) + mp_core entries (200
+ * bytes)
+ */
+#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
+
+/* Include shared EFI stub code */
+#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
+
+
+static int is_linux_reserved_region(int memory_type)
+{
+	switch (memory_type) {
+	case EFI_RUNTIME_SERVICES_CODE:
+	case EFI_RUNTIME_SERVICES_DATA:
+	case EFI_UNUSABLE_MEMORY:
+	case EFI_ACPI_RECLAIM_MEMORY:
+	case EFI_ACPI_MEMORY_NVS:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+
+static int relocate_kernel(efi_system_table_t *sys_table,
+			   unsigned long *load_addr, unsigned long *load_size,
+			   unsigned long min_addr, unsigned long max_addr)
+{
+	/* Get current address of kernel. */
+	unsigned long cur_zimage_addr = *load_addr;
+	unsigned long zimage_size = *load_size;
+	unsigned long new_addr = 0;
+	unsigned long nr_pages;
+
+	efi_status_t status;
+
+	if (!load_addr || !load_size)
+		return EFI_INVALID_PARAMETER;
+
+	*load_size = 0;
+	if (cur_zimage_addr > min_addr
+	    && (cur_zimage_addr + zimage_size) < max_addr) {
+		/* We don't need to do anything, as kernel at an acceptable
+		 * address already.
+		 */
+		return EFI_SUCCESS;
+	}
+	/*
+	 * The EFI firmware loader could have placed the kernel image
+	 * anywhere in memory, but the kernel has restrictions on the
+	 * min and max physical address it can run at.
+	 */
+	nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+	status = efi_low_alloc(sys_table, zimage_size, 0,
+			   &new_addr, min_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
+		return status;
+	}
+
+	if (new_addr > (max_addr - zimage_size)) {
+		efi_free(sys_table, zimage_size, new_addr);
+		efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
+		return EFI_INVALID_PARAMETER;
+	}
+
+	/* We know source/dest won't overlap since both memory ranges
+	 * have been allocated by UEFI, so we can safely use memcpy.
+	 */
+	memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
+	       zimage_size);
+
+	/* Return the load address and size */
+	*load_addr = new_addr;
+	*load_size = zimage_size;
+
+
+	return status;
+}
+
+
+/* Convert the unicode UEFI command line to ASCII to pass to kernel.
+ * Size of memory allocated return in *cmd_line_len.
+ * Returns NULL on error.
+ */
+static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
+				      efi_loaded_image_t *image,
+				      unsigned long *cmd_line_len,
+				      u32 max_addr)
+{
+	u16 *s2;
+	u8 *s1 = NULL;
+	unsigned long cmdline_addr = 0;
+	int load_options_size = image->load_options_size / 2; /* ASCII */
+	void *options = (u16 *)image->load_options;
+	int options_size = 0;
+	int status;
+	int i;
+	u16 zero = 0;
+
+	if (options) {
+		s2 = options;
+		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
+			s2++;
+			options_size++;
+		}
+	}
+
+	if (options_size == 0) {
+		/* No command line options, so return empty string*/
+		options_size = 1;
+		options = &zero;
+	}
+
+	if (options_size > MAX_CMDLINE_LEN)
+		options_size = MAX_CMDLINE_LEN;
+
+	options_size++;  /* NUL termination */
+
+	status = efi_high_alloc(sys_table, options_size, 0,
+			    &cmdline_addr, max_addr);
+	if (status != EFI_SUCCESS)
+		return NULL;
+
+	s1 = (u8 *)(unsigned long)cmdline_addr;
+	s2 = (u16 *)options;
+
+	for (i = 0; i < options_size - 1; i++)
+		*s1++ = *s2++;
+
+	*s1 = '\0';
+
+	*cmd_line_len = options_size;
+	return (char *)(unsigned long)cmdline_addr;
+}
+
+static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
+				    void *handle, unsigned long dram_base,
+				    void *orig_fdt, u64 *orig_fdt_size,
+				    char *cmdline_ptr,
+				    unsigned long *cmdline_size,
+				    u64 initrd_addr, u64 initrd_size)
+{
+	unsigned long new_fdt_size;
+	unsigned long new_fdt_addr;
+	void *fdt;
+	int node;
+	int status;
+	int i;
+	unsigned long map_size, desc_size;
+	unsigned long mmap_key;
+	efi_memory_desc_t *memory_map;
+	unsigned long fdt_val;
+
+	new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
+	status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
+			    dram_base + MAX_ZIMAGE_OFFSET);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
+		goto fail;
+	}
+
+
+	fdt = (void *)new_fdt_addr;
+	status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
+	if (status != 0) {
+		efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
+		goto fail_free_new_fdt;
+	}
+	/* We are done with the original DTB, so free it. */
+	efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
+	*orig_fdt_size = 0;
+
+	node = fdt_subnode_offset(fdt, 0, "chosen");
+	if (node < 0) {
+		node = fdt_add_subnode(fdt, 0, "chosen");
+		if (node < 0) {
+			efi_printk(sys_table, "Error on finding 'chosen' node\n");
+			goto fail_free_new_fdt;
+		}
+	}
+
+	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
+		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
+				     strlen(cmdline_ptr) + 1);
+		if (status) {
+			efi_printk(sys_table, "Failed to set new bootarg\n");
+			goto fail_free_new_fdt;
+		}
+	}
+	/* We are done with original command line, so free it. */
+	efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
+	*cmdline_size = 0;
+
+	/* Set intird address/end in device tree, if present */
+	if (initrd_size != 0) {
+		u64 initrd_image_end;
+		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
+		status = fdt_setprop(fdt, node, "linux,initrd-start",
+				     &initrd_image_start, sizeof(u64));
+		if (status) {
+			efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
+			goto fail_free_new_fdt;
+		}
+		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
+		status = fdt_setprop(fdt, node, "linux,initrd-end",
+				     &initrd_image_end, sizeof(u64));
+		if (status) {
+			efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
+			goto fail_free_new_fdt;
+		}
+	}
+
+	/* Update memory map in the device tree. The memory node must
+	 * be present in the tree.*/
+	node = fdt_subnode_offset(fdt, 0, "memory");
+	if (node < 0) {
+		efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
+		goto fail_free_new_fdt;
+	}
+
+	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
+				    &desc_size, &mmap_key);
+	if (status != EFI_SUCCESS)
+		goto fail_free_new_fdt;
+
+	for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)memory_map;
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+
+		if (is_linux_reserved_region(desc->type)) {
+			status = fdt_add_mem_rsv(fdt, desc->phys_addr,
+						 desc->num_pages * EFI_PAGE_SIZE);
+			if (status != 0) {
+				efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
+				goto fail_free_mmap;
+			}
+		}
+	}
+
+
+	/* Add FDT entries for EFI runtime services in chosen node.
+	 * We need to add the final memory map, so this is done at
+	 * the very end.
+	 */
+	node = fdt_subnode_offset(fdt, 0, "chosen");
+	fdt_val = cpu_to_fdt32((unsigned long)sys_table);
+	status = fdt_setprop(fdt, node, "efi-system-table",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32(desc_size);
+	status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32(map_size);
+	status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32((unsigned long)memory_map);
+	status = fdt_setprop(fdt, node, "efi-runtime-mmap",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
+		goto fail_free_new_fdt;
+	}
+
+	/* Now we need to exit boot services.  We need the key from
+	 * the most recent read of the memory map to do this.  We can't
+	 * free this buffer in the normal case, but do free it when
+	 * exit_boot_services() fails or adding the memory map to the FDT
+	 * fails.
+	 */
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, mmap_key);
+
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "exit boot services failed.\n");
+		goto fail_free_mmap;
+	}
+
+	return new_fdt_addr;
+
+fail_free_mmap:
+	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
+
+fail_free_new_fdt:
+	efi_free(sys_table, new_fdt_size, new_fdt_addr);
+
+fail:
+	return 0;
+}
+
+
+int efi_entry(void *handle, efi_system_table_t *sys_table,
+	      unsigned long *zimage_addr)
+{
+	efi_loaded_image_t *image;
+	int status;
+	unsigned long nr_pages;
+	const struct fdt_region *region;
+
+	void *fdt;
+	int err;
+	int node;
+	unsigned long zimage_size = 0;
+	unsigned long dram_base;
+	/* addr/point and size pairs for memory management*/
+	u64 initrd_addr;
+	u64 initrd_size = 0;
+	u64 fdt_addr;  /* Original DTB */
+	u64 fdt_size = 0;
+	u64 kernel_reserve_addr;
+	u64 kernel_reserve_size = 0;
+	char *cmdline_ptr;
+	unsigned long cmdline_size = 0;
+	unsigned long new_fdt_addr;
+
+	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	efi_printk(sys_table, "Booting Linux using EFI stub.\n");
+
+
+	/* get the command line from EFI, using the LOADED_IMAGE protocol */
+	status = efi_call_phys3(sys_table->boottime->handle_protocol,
+				handle, &proto, (void *)&image);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		goto fail;
+	}
+
+	/* We are going to copy this into device tree, so we don't care where in
+	 * memory it is.
+	 */
+	cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
+					       &cmdline_size, 0xFFFFFFFF);
+	if (!cmdline_ptr) {
+		efi_printk(sys_table, "ERROR converting command line to ascii.\n");
+		goto fail;
+	}
+
+	/* We first load the device tree, as we need to get the base address of
+	 * DRAM from the device tree.  The zImage, device tree, and initrd
+	 * have address restrictions that are relative to the base of DRAM.
+	 */
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
+				      0xffffffff, &fdt_addr, &fdt_size);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Error loading dtb blob\n");
+		goto fail_free_cmdline;
+	}
+
+	err = fdt_check_header((void *)(unsigned long)fdt_addr);
+	if (err != 0) {
+		efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
+		goto fail_free_dtb;
+	}
+	if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
+		efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
+		goto fail_free_dtb;
+
+	}
+
+
+	/* Look up the base of DRAM from the device tree.*/
+	fdt = (void *)(u32)fdt_addr;
+	node = fdt_subnode_offset(fdt, 0, "memory");
+	region = fdt_getprop(fdt, node, "reg", NULL);
+	if (region) {
+		dram_base = fdt64_to_cpu(region->base);
+	} else {
+		efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
+		goto fail_free_dtb;
+	}
+
+	/* Reserve memory for the uncompressed kernel image. */
+	kernel_reserve_addr = dram_base;
+	kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
+	nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	status = efi_call_phys4(sys_table->boottime->allocate_pages,
+				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+				nr_pages, &kernel_reserve_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
+		goto fail_free_dtb;
+	}
+
+	/* Relocate the zImage, if required. */
+	zimage_size = image->image_size;
+	status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
+				 dram_base + MIN_ZIMAGE_OFFSET,
+				 dram_base + MAX_ZIMAGE_OFFSET);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to relocate kernel\n");
+		goto fail_free_kernel_reserve;
+	}
+
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
+				      dram_base + MAX_ZIMAGE_OFFSET,
+				      &initrd_addr, &initrd_size);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Error loading initrd\n");
+		goto fail_free_zimage;
+	}
+
+	new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
+						dram_base, fdt, &fdt_size,
+						cmdline_ptr, &cmdline_size,
+						initrd_addr, initrd_size);
+
+	if (new_fdt_addr == 0) {
+		efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
+		goto fail_free_initrd;
+	}
+
+
+	/* Now we need to return the FDT address to the calling
+	 * assembly to this can be used as part of normal boot.
+	 */
+	return new_fdt_addr;
+
+fail_free_initrd:
+	efi_free(sys_table, initrd_size, initrd_addr);
+
+fail_free_zimage:
+	efi_free(sys_table, zimage_size, *zimage_addr);
+
+fail_free_kernel_reserve:
+	efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
+
+fail_free_dtb:
+	efi_free(sys_table, fdt_size, fdt_addr);
+
+fail_free_cmdline:
+	efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
+
+fail:
+	return EFI_STUB_ERROR;
+}
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 75189f1..491e752 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -120,21 +120,100 @@
  */
 		.align
 		.arm				@ Always enter in ARM state
+		.text
 start:
 		.type	start,#function
-		.rept	7
+#ifdef CONFIG_EFI_STUB
+		@ Magic MSDOS signature for PE/COFF + ADD opcode
+		.word	0x62805a4d
+#else
+		mov	r0, r0
+#endif
+		.rept	5
 		mov	r0, r0
 		.endr
-   ARM(		mov	r0, r0		)
-   ARM(		b	1f		)
- THUMB(		adr	r12, BSYM(1f)	)
- THUMB(		bx	r12		)
+
+		@ zimage_continue will be in ARM or thumb mode as configured
+ THUMB(		adrl	r12, BSYM(zimage_continue))
+ ARM(		adrl	r12, zimage_continue)
+		bx	r12
+ THUMB(		.thumb			)
 
 		.word	0x016f2818		@ Magic numbers to help the loader
 		.word	start			@ absolute load/run zImage address
 		.word	_edata			@ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+		@ Portions of the MSDOS file header must be at offset
+		@ 0x3c from the start of the file.  All PE/COFF headers
+		@ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+		@ The EFI stub entry point is not at a fixed address, however
+		@ this address must be set in the PE/COFF header.
+		@ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB(		.arm			)
+ THUMB(		adr	r12, BSYM(1f)	)
+ THUMB(		bx	r12		)
  THUMB(		.thumb			)
 1:
+		@ Save lr on stack for possible return to EFI firmware.
+		@ Don't care about fp, but need 64 bit alignment....
+		stmfd	sp!, {fp, lr}
+
+		@ Save args to EFI app across got fixup call
+		stmfd	sp!, {r0, r1}
+		ldmfd	sp!, {r0, r1}
+
+		@ allocate space on stack for return of new entry point of
+		@ zImage, as EFI stub may copy the kernel.  Pass address
+		@ of space in r2 - EFI stub will fill in the pointer.
+
+		sub	sp, #8			@ we only need 4 bytes,
+						@ but keep stack 8 byte aligned.
+		mov	r2, sp
+		@ Pass our actual runtime start address in pointer data
+		adr	r11, LC0		@ address of LC0 at run time
+		ldr	r12, [r11, #0]		@ address of LC0 at link time
+
+		sub	r3, r11, r12		@ calculate the delta offset
+		str	r3, [r2, #0]
+		bl	efi_entry
+
+		@ get new zImage entry address from stack, put into r3
+		ldr	r3, [sp, #0]
+		add	sp, #8  @ restore stack
+
+		@ Check for error return from EFI stub (0xFFFFFFFF)
+		ldr	r1, =0xffffffff
+		cmp	r0, r1
+		beq	efi_load_fail
+
+
+		@ Save return values of efi_entry
+		stmfd	sp!, {r0, r3}
+		bl	cache_clean_flush
+		bl	cache_off
+		ldmfd   sp!, {r0, r3}
+
+		@ put DTB address in r2, it was returned by EFI entry
+		mov	r2, r0
+		ldr	r1, =0xffffffff		@ DTB machine type
+		mov	r0, #0  @ r0 is 0
+
+		@ Branch to (possibly) relocated zImage entry that is in r3
+		bx	r3
+
+efi_load_fail:
+		@ Return EFI_LOAD_ERROR to EFI firmware on error.
+		@ Switch back to ARM mode for EFI is done based on
+		@ return address on stack
+		ldr	r0, =0x80000001
+		ldmfd	sp!, {fp, pc}
+#endif
+
+zimage_continue:
 		mrs	r9, cpsr
 #ifdef CONFIG_ARM_VIRT_EXT
 		bl	__hyp_stub_install	@ get into SVC mode, reversibly
@@ -167,7 +246,6 @@ not_angel:
 		 * by the linker here, but it should preserve r7, r8, and r9.
 		 */
 
-		.text
 
 #ifdef CONFIG_AUTO_ZRELADDR
 		@ determine final kernel image address
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8, Roy Franz

This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
operations similarly to the x86 stub: it is a shim between the EFI firmware
and the normal zImage entry point, and sets up the environment that the
zImage is expecting.  This includes loading the initrd (optionaly) and
device tree from the system partition based on the kernel command line.
The stub updates the device tree as necessary, including adding reserved
memory regions and adding entries for EFI runtime services. The PE/COFF
"MZ" header at offset 0 results in the first instruction being an add
that corrupts r5, which is not used by the zImage interface.

Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 arch/arm/boot/compressed/Makefile     |   18 +-
 arch/arm/boot/compressed/efi-header.S |  114 ++++++++
 arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
 arch/arm/boot/compressed/head.S       |   90 +++++-
 4 files changed, 728 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/compressed/efi-header.S
 create mode 100644 arch/arm/boot/compressed/efi-stub.c

diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 7ac1610..c62826a 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
 $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
 	$(addprefix $(obj)/,$(libfdt_hdrs))
 
+$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
+	$(addprefix $(obj)/,$(libfdt_hdrs))
+
 ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
-OBJS	+= $(libfdt_objs) atags_to_fdt.o
+OBJS	+= atags_to_fdt.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(CONFIG_EFI_STUB),y)
+CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
+OBJS	+= efi-stub.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(USE_LIBFDT),y)
+OBJS	+= $(libfdt_objs)
 endif
 
 targets       := vmlinux vmlinux.lds \
@@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
 KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
 endif
 
-ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
+ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
 asflags-y := -DZIMAGE
 
 # Supply kernel BSS size to the decompressor via a linker symbol.
diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
new file mode 100644
index 0000000..6ff32cc
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-header.S
@@ -0,0 +1,114 @@
+@ Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+@
+@ This file contains the PE/COFF header that is part of the
+@ EFI stub.
+@
+
+	.org	0x3c
+	@
+	@ The PE header can be anywhere in the file, but for
+	@ simplicity we keep it together with the MSDOS header
+	@ The offset to the PE/COFF header needs to be at offset
+	@ 0x3C in the MSDOS header.
+	@ The only 2 fields of the MSDOS header that are used are this
+	@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
+	@
+	.long	pe_header			@ Offset to the PE header.
+
+      .align 3
+pe_header:
+
+
+pe_header:
+	.ascii	"PE"
+	.short 	0
+
+coff_header:
+	.short	0x01c2				@ ARM or Thumb
+	.short	2				@ nr_sections
+	.long	0 				@ TimeDateStamp
+	.long	0				@ PointerToSymbolTable
+	.long	1				@ NumberOfSymbols
+	.short	section_table - optional_header	@ SizeOfOptionalHeader
+	.short	0x306				@ Characteristics.
+						@ IMAGE_FILE_32BIT_MACHINE |
+						@ IMAGE_FILE_DEBUG_STRIPPED |
+						@ IMAGE_FILE_EXECUTABLE_IMAGE |
+						@ IMAGE_FILE_LINE_NUMS_STRIPPED
+
+optional_header:
+	.short	0x10b				@ PE32 format
+	.byte	0x02				@ MajorLinkerVersion
+	.byte	0x14				@ MinorLinkerVersion
+
+	.long	0				@ SizeOfCode
+
+	.long	0				@ SizeOfInitializedData
+	.long	0				@ SizeOfUninitializedData
+
+	.long	efi_stub_entry			@ AddressOfEntryPoint
+	.long	efi_stub_entry			@ BaseOfCode
+	.long	0				@ data
+
+extra_header_fields:
+	.long	0				@ ImageBase
+	.long	0x20				@ SectionAlignment
+	.long	0x20				@ FileAlignment
+	.short	0				@ MajorOperatingSystemVersion
+	.short	0				@ MinorOperatingSystemVersion
+	.short	0				@ MajorImageVersion
+	.short	0				@ MinorImageVersion
+	.short	0				@ MajorSubsystemVersion
+	.short	0				@ MinorSubsystemVersion
+	.long	0				@ Win32VersionValue
+
+	.long	_edata				@ SizeOfImage
+
+	@ Everything before the entry point is considered part of the header
+	.long	efi_stub_entry			@ SizeOfHeaders
+	.long	0				@ CheckSum
+	.short	0xa				@ Subsystem (EFI application)
+	.short	0				@ DllCharacteristics
+	.long	0				@ SizeOfStackReserve
+	.long	0				@ SizeOfStackCommit
+	.long	0				@ SizeOfHeapReserve
+	.long	0				@ SizeOfHeapCommit
+	.long	0				@ LoaderFlags
+	.long	0x0				@ NumberOfRvaAndSizes
+
+	# Section table
+section_table:
+
+	#
+	# The EFI application loader requires a relocation section
+	# because EFI applications must be relocatable.  This is a
+	# dummy section as far as we are concerned.
+	#
+	.ascii	".reloc"
+	.byte	0
+	.byte	0			@ end of 0 padding of section name
+	.long	0
+	.long	0
+	.long	0			@ SizeOfRawData
+	.long	0			@ PointerToRawData
+	.long	0			@ PointerToRelocations
+	.long	0			@ PointerToLineNumbers
+	.short	0			@ NumberOfRelocations
+	.short	0			@ NumberOfLineNumbers
+	.long	0x42100040		@ Characteristics (section flags)
+
+
+	.ascii	".text"
+	.byte	0
+	.byte	0
+	.byte	0        		@ end of 0 padding of section name
+	.long	_edata - efi_stub_entry		@ VirtualSize
+	.long	efi_stub_entry			@ VirtualAddress
+	.long	_edata - efi_stub_entry		@ SizeOfRawData
+	.long	efi_stub_entry			@ PointerToRawData
+
+	.long	0		@ PointerToRelocations (0 for executables)
+	.long	0		@ PointerToLineNumbers (0 for executables)
+	.short	0		@ NumberOfRelocations  (0 for executables)
+	.short	0		@ NumberOfLineNumbers  (0 for executables)
+	.long	0xe0500020	@ Characteristics (section flags)
diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
new file mode 100644
index 0000000..b817ea3
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-stub.c
@@ -0,0 +1,514 @@
+/*
+ * linux/arch/arm/boot/compressed/efi-stub.c
+ *
+ * Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ *
+ * This file implements the EFI boot stub for the ARM kernel
+ *
+ * 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 <libfdt.h>
+
+
+/* Error code returned to ASM code instead of valid FDT address. */
+#define EFI_STUB_ERROR		(~0)
+
+/* EFI function call wrappers.  These are not required for
+ * ARM, but wrappers are required for X86 to convert between
+ * ABIs.  These wrappers are provided to allow code sharing
+ * between X86 and ARM.  Since these wrappers directly invoke the
+ * EFI function pointer, the function pointer type must be properly
+ * defined, which is not the case for X86  One advantage of this is
+ * it allows for type checking of arguments, which is not
+ * possible with the X86 wrappers.
+ */
+#define efi_call_phys0(f)			f()
+#define efi_call_phys1(f, a1)			f(a1)
+#define efi_call_phys2(f, a1, a2)		f(a1, a2)
+#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
+#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
+#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
+
+/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
+ * that for the decompressed kernel.  We have no easy way to tell what
+ * the actuall size of code + data the uncompressed kernel will use.
+ */
+#define MAX_UNCOMP_KERNEL_SIZE	0x02000000
+
+/* The kernel zImage should be located between 32 Mbytes
+ * and 128 MBytes from the base of DRAM.  The min
+ * address leaves space for a maximal size uncompressed image,
+ * and the max address is due to how the zImage decompressor
+ * picks a destination address.
+ */
+#define MAX_ZIMAGE_OFFSET	0x08000000
+#define MIN_ZIMAGE_OFFSET	MAX_UNCOMP_KERNEL_SIZE
+
+#define MAX_CMDLINE_LEN		500
+
+struct fdt_region {
+	u64 base;
+	u64 size;
+};
+
+/*
+ * Additional size that could be used for FDT entries added by
+ * the UEFI OS Loader Estimation based on:
+ * EDID (300bytes) + bootargs  + initrd region (20bytes)
+ * + system memory region (20bytes) + mp_core entries (200
+ * bytes)
+ */
+#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
+
+/* Include shared EFI stub code */
+#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
+
+
+static int is_linux_reserved_region(int memory_type)
+{
+	switch (memory_type) {
+	case EFI_RUNTIME_SERVICES_CODE:
+	case EFI_RUNTIME_SERVICES_DATA:
+	case EFI_UNUSABLE_MEMORY:
+	case EFI_ACPI_RECLAIM_MEMORY:
+	case EFI_ACPI_MEMORY_NVS:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+
+static int relocate_kernel(efi_system_table_t *sys_table,
+			   unsigned long *load_addr, unsigned long *load_size,
+			   unsigned long min_addr, unsigned long max_addr)
+{
+	/* Get current address of kernel. */
+	unsigned long cur_zimage_addr = *load_addr;
+	unsigned long zimage_size = *load_size;
+	unsigned long new_addr = 0;
+	unsigned long nr_pages;
+
+	efi_status_t status;
+
+	if (!load_addr || !load_size)
+		return EFI_INVALID_PARAMETER;
+
+	*load_size = 0;
+	if (cur_zimage_addr > min_addr
+	    && (cur_zimage_addr + zimage_size) < max_addr) {
+		/* We don't need to do anything, as kernel at an acceptable
+		 * address already.
+		 */
+		return EFI_SUCCESS;
+	}
+	/*
+	 * The EFI firmware loader could have placed the kernel image
+	 * anywhere in memory, but the kernel has restrictions on the
+	 * min and max physical address it can run at.
+	 */
+	nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+	status = efi_low_alloc(sys_table, zimage_size, 0,
+			   &new_addr, min_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
+		return status;
+	}
+
+	if (new_addr > (max_addr - zimage_size)) {
+		efi_free(sys_table, zimage_size, new_addr);
+		efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
+		return EFI_INVALID_PARAMETER;
+	}
+
+	/* We know source/dest won't overlap since both memory ranges
+	 * have been allocated by UEFI, so we can safely use memcpy.
+	 */
+	memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
+	       zimage_size);
+
+	/* Return the load address and size */
+	*load_addr = new_addr;
+	*load_size = zimage_size;
+
+
+	return status;
+}
+
+
+/* Convert the unicode UEFI command line to ASCII to pass to kernel.
+ * Size of memory allocated return in *cmd_line_len.
+ * Returns NULL on error.
+ */
+static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
+				      efi_loaded_image_t *image,
+				      unsigned long *cmd_line_len,
+				      u32 max_addr)
+{
+	u16 *s2;
+	u8 *s1 = NULL;
+	unsigned long cmdline_addr = 0;
+	int load_options_size = image->load_options_size / 2; /* ASCII */
+	void *options = (u16 *)image->load_options;
+	int options_size = 0;
+	int status;
+	int i;
+	u16 zero = 0;
+
+	if (options) {
+		s2 = options;
+		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
+			s2++;
+			options_size++;
+		}
+	}
+
+	if (options_size == 0) {
+		/* No command line options, so return empty string*/
+		options_size = 1;
+		options = &zero;
+	}
+
+	if (options_size > MAX_CMDLINE_LEN)
+		options_size = MAX_CMDLINE_LEN;
+
+	options_size++;  /* NUL termination */
+
+	status = efi_high_alloc(sys_table, options_size, 0,
+			    &cmdline_addr, max_addr);
+	if (status != EFI_SUCCESS)
+		return NULL;
+
+	s1 = (u8 *)(unsigned long)cmdline_addr;
+	s2 = (u16 *)options;
+
+	for (i = 0; i < options_size - 1; i++)
+		*s1++ = *s2++;
+
+	*s1 = '\0';
+
+	*cmd_line_len = options_size;
+	return (char *)(unsigned long)cmdline_addr;
+}
+
+static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
+				    void *handle, unsigned long dram_base,
+				    void *orig_fdt, u64 *orig_fdt_size,
+				    char *cmdline_ptr,
+				    unsigned long *cmdline_size,
+				    u64 initrd_addr, u64 initrd_size)
+{
+	unsigned long new_fdt_size;
+	unsigned long new_fdt_addr;
+	void *fdt;
+	int node;
+	int status;
+	int i;
+	unsigned long map_size, desc_size;
+	unsigned long mmap_key;
+	efi_memory_desc_t *memory_map;
+	unsigned long fdt_val;
+
+	new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
+	status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
+			    dram_base + MAX_ZIMAGE_OFFSET);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
+		goto fail;
+	}
+
+
+	fdt = (void *)new_fdt_addr;
+	status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
+	if (status != 0) {
+		efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
+		goto fail_free_new_fdt;
+	}
+	/* We are done with the original DTB, so free it. */
+	efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
+	*orig_fdt_size = 0;
+
+	node = fdt_subnode_offset(fdt, 0, "chosen");
+	if (node < 0) {
+		node = fdt_add_subnode(fdt, 0, "chosen");
+		if (node < 0) {
+			efi_printk(sys_table, "Error on finding 'chosen' node\n");
+			goto fail_free_new_fdt;
+		}
+	}
+
+	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
+		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
+				     strlen(cmdline_ptr) + 1);
+		if (status) {
+			efi_printk(sys_table, "Failed to set new bootarg\n");
+			goto fail_free_new_fdt;
+		}
+	}
+	/* We are done with original command line, so free it. */
+	efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
+	*cmdline_size = 0;
+
+	/* Set intird address/end in device tree, if present */
+	if (initrd_size != 0) {
+		u64 initrd_image_end;
+		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
+		status = fdt_setprop(fdt, node, "linux,initrd-start",
+				     &initrd_image_start, sizeof(u64));
+		if (status) {
+			efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
+			goto fail_free_new_fdt;
+		}
+		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
+		status = fdt_setprop(fdt, node, "linux,initrd-end",
+				     &initrd_image_end, sizeof(u64));
+		if (status) {
+			efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
+			goto fail_free_new_fdt;
+		}
+	}
+
+	/* Update memory map in the device tree. The memory node must
+	 * be present in the tree.*/
+	node = fdt_subnode_offset(fdt, 0, "memory");
+	if (node < 0) {
+		efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
+		goto fail_free_new_fdt;
+	}
+
+	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
+				    &desc_size, &mmap_key);
+	if (status != EFI_SUCCESS)
+		goto fail_free_new_fdt;
+
+	for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)memory_map;
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+
+		if (is_linux_reserved_region(desc->type)) {
+			status = fdt_add_mem_rsv(fdt, desc->phys_addr,
+						 desc->num_pages * EFI_PAGE_SIZE);
+			if (status != 0) {
+				efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
+				goto fail_free_mmap;
+			}
+		}
+	}
+
+
+	/* Add FDT entries for EFI runtime services in chosen node.
+	 * We need to add the final memory map, so this is done at
+	 * the very end.
+	 */
+	node = fdt_subnode_offset(fdt, 0, "chosen");
+	fdt_val = cpu_to_fdt32((unsigned long)sys_table);
+	status = fdt_setprop(fdt, node, "efi-system-table",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32(desc_size);
+	status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32(map_size);
+	status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32((unsigned long)memory_map);
+	status = fdt_setprop(fdt, node, "efi-runtime-mmap",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
+		goto fail_free_new_fdt;
+	}
+
+	/* Now we need to exit boot services.  We need the key from
+	 * the most recent read of the memory map to do this.  We can't
+	 * free this buffer in the normal case, but do free it when
+	 * exit_boot_services() fails or adding the memory map to the FDT
+	 * fails.
+	 */
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, mmap_key);
+
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "exit boot services failed.\n");
+		goto fail_free_mmap;
+	}
+
+	return new_fdt_addr;
+
+fail_free_mmap:
+	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
+
+fail_free_new_fdt:
+	efi_free(sys_table, new_fdt_size, new_fdt_addr);
+
+fail:
+	return 0;
+}
+
+
+int efi_entry(void *handle, efi_system_table_t *sys_table,
+	      unsigned long *zimage_addr)
+{
+	efi_loaded_image_t *image;
+	int status;
+	unsigned long nr_pages;
+	const struct fdt_region *region;
+
+	void *fdt;
+	int err;
+	int node;
+	unsigned long zimage_size = 0;
+	unsigned long dram_base;
+	/* addr/point and size pairs for memory management*/
+	u64 initrd_addr;
+	u64 initrd_size = 0;
+	u64 fdt_addr;  /* Original DTB */
+	u64 fdt_size = 0;
+	u64 kernel_reserve_addr;
+	u64 kernel_reserve_size = 0;
+	char *cmdline_ptr;
+	unsigned long cmdline_size = 0;
+	unsigned long new_fdt_addr;
+
+	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	efi_printk(sys_table, "Booting Linux using EFI stub.\n");
+
+
+	/* get the command line from EFI, using the LOADED_IMAGE protocol */
+	status = efi_call_phys3(sys_table->boottime->handle_protocol,
+				handle, &proto, (void *)&image);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		goto fail;
+	}
+
+	/* We are going to copy this into device tree, so we don't care where in
+	 * memory it is.
+	 */
+	cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
+					       &cmdline_size, 0xFFFFFFFF);
+	if (!cmdline_ptr) {
+		efi_printk(sys_table, "ERROR converting command line to ascii.\n");
+		goto fail;
+	}
+
+	/* We first load the device tree, as we need to get the base address of
+	 * DRAM from the device tree.  The zImage, device tree, and initrd
+	 * have address restrictions that are relative to the base of DRAM.
+	 */
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
+				      0xffffffff, &fdt_addr, &fdt_size);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Error loading dtb blob\n");
+		goto fail_free_cmdline;
+	}
+
+	err = fdt_check_header((void *)(unsigned long)fdt_addr);
+	if (err != 0) {
+		efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
+		goto fail_free_dtb;
+	}
+	if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
+		efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
+		goto fail_free_dtb;
+
+	}
+
+
+	/* Look up the base of DRAM from the device tree.*/
+	fdt = (void *)(u32)fdt_addr;
+	node = fdt_subnode_offset(fdt, 0, "memory");
+	region = fdt_getprop(fdt, node, "reg", NULL);
+	if (region) {
+		dram_base = fdt64_to_cpu(region->base);
+	} else {
+		efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
+		goto fail_free_dtb;
+	}
+
+	/* Reserve memory for the uncompressed kernel image. */
+	kernel_reserve_addr = dram_base;
+	kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
+	nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	status = efi_call_phys4(sys_table->boottime->allocate_pages,
+				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+				nr_pages, &kernel_reserve_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
+		goto fail_free_dtb;
+	}
+
+	/* Relocate the zImage, if required. */
+	zimage_size = image->image_size;
+	status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
+				 dram_base + MIN_ZIMAGE_OFFSET,
+				 dram_base + MAX_ZIMAGE_OFFSET);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to relocate kernel\n");
+		goto fail_free_kernel_reserve;
+	}
+
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
+				      dram_base + MAX_ZIMAGE_OFFSET,
+				      &initrd_addr, &initrd_size);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Error loading initrd\n");
+		goto fail_free_zimage;
+	}
+
+	new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
+						dram_base, fdt, &fdt_size,
+						cmdline_ptr, &cmdline_size,
+						initrd_addr, initrd_size);
+
+	if (new_fdt_addr == 0) {
+		efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
+		goto fail_free_initrd;
+	}
+
+
+	/* Now we need to return the FDT address to the calling
+	 * assembly to this can be used as part of normal boot.
+	 */
+	return new_fdt_addr;
+
+fail_free_initrd:
+	efi_free(sys_table, initrd_size, initrd_addr);
+
+fail_free_zimage:
+	efi_free(sys_table, zimage_size, *zimage_addr);
+
+fail_free_kernel_reserve:
+	efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
+
+fail_free_dtb:
+	efi_free(sys_table, fdt_size, fdt_addr);
+
+fail_free_cmdline:
+	efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
+
+fail:
+	return EFI_STUB_ERROR;
+}
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 75189f1..491e752 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -120,21 +120,100 @@
  */
 		.align
 		.arm				@ Always enter in ARM state
+		.text
 start:
 		.type	start,#function
-		.rept	7
+#ifdef CONFIG_EFI_STUB
+		@ Magic MSDOS signature for PE/COFF + ADD opcode
+		.word	0x62805a4d
+#else
+		mov	r0, r0
+#endif
+		.rept	5
 		mov	r0, r0
 		.endr
-   ARM(		mov	r0, r0		)
-   ARM(		b	1f		)
- THUMB(		adr	r12, BSYM(1f)	)
- THUMB(		bx	r12		)
+
+		@ zimage_continue will be in ARM or thumb mode as configured
+ THUMB(		adrl	r12, BSYM(zimage_continue))
+ ARM(		adrl	r12, zimage_continue)
+		bx	r12
+ THUMB(		.thumb			)
 
 		.word	0x016f2818		@ Magic numbers to help the loader
 		.word	start			@ absolute load/run zImage address
 		.word	_edata			@ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+		@ Portions of the MSDOS file header must be at offset
+		@ 0x3c from the start of the file.  All PE/COFF headers
+		@ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+		@ The EFI stub entry point is not at a fixed address, however
+		@ this address must be set in the PE/COFF header.
+		@ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB(		.arm			)
+ THUMB(		adr	r12, BSYM(1f)	)
+ THUMB(		bx	r12		)
  THUMB(		.thumb			)
 1:
+		@ Save lr on stack for possible return to EFI firmware.
+		@ Don't care about fp, but need 64 bit alignment....
+		stmfd	sp!, {fp, lr}
+
+		@ Save args to EFI app across got fixup call
+		stmfd	sp!, {r0, r1}
+		ldmfd	sp!, {r0, r1}
+
+		@ allocate space on stack for return of new entry point of
+		@ zImage, as EFI stub may copy the kernel.  Pass address
+		@ of space in r2 - EFI stub will fill in the pointer.
+
+		sub	sp, #8			@ we only need 4 bytes,
+						@ but keep stack 8 byte aligned.
+		mov	r2, sp
+		@ Pass our actual runtime start address in pointer data
+		adr	r11, LC0		@ address of LC0 at run time
+		ldr	r12, [r11, #0]		@ address of LC0 at link time
+
+		sub	r3, r11, r12		@ calculate the delta offset
+		str	r3, [r2, #0]
+		bl	efi_entry
+
+		@ get new zImage entry address from stack, put into r3
+		ldr	r3, [sp, #0]
+		add	sp, #8  @ restore stack
+
+		@ Check for error return from EFI stub (0xFFFFFFFF)
+		ldr	r1, =0xffffffff
+		cmp	r0, r1
+		beq	efi_load_fail
+
+
+		@ Save return values of efi_entry
+		stmfd	sp!, {r0, r3}
+		bl	cache_clean_flush
+		bl	cache_off
+		ldmfd   sp!, {r0, r3}
+
+		@ put DTB address in r2, it was returned by EFI entry
+		mov	r2, r0
+		ldr	r1, =0xffffffff		@ DTB machine type
+		mov	r0, #0  @ r0 is 0
+
+		@ Branch to (possibly) relocated zImage entry that is in r3
+		bx	r3
+
+efi_load_fail:
+		@ Return EFI_LOAD_ERROR to EFI firmware on error.
+		@ Switch back to ARM mode for EFI is done based on
+		@ return address on stack
+		ldr	r0, =0x80000001
+		ldmfd	sp!, {fp, pc}
+#endif
+
+zimage_continue:
 		mrs	r9, cpsr
 #ifdef CONFIG_ARM_VIRT_EXT
 		bl	__hyp_stub_install	@ get into SVC mode, reversibly
@@ -167,7 +246,6 @@ not_angel:
 		 * by the linker here, but it should preserve r7, r8, and r9.
 		 */
 
-		.text
 
 #ifdef CONFIG_AUTO_ZRELADDR
 		@ determine final kernel image address
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
operations similarly to the x86 stub: it is a shim between the EFI firmware
and the normal zImage entry point, and sets up the environment that the
zImage is expecting.  This includes loading the initrd (optionaly) and
device tree from the system partition based on the kernel command line.
The stub updates the device tree as necessary, including adding reserved
memory regions and adding entries for EFI runtime services. The PE/COFF
"MZ" header at offset 0 results in the first instruction being an add
that corrupts r5, which is not used by the zImage interface.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/arm/boot/compressed/Makefile     |   18 +-
 arch/arm/boot/compressed/efi-header.S |  114 ++++++++
 arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
 arch/arm/boot/compressed/head.S       |   90 +++++-
 4 files changed, 728 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/compressed/efi-header.S
 create mode 100644 arch/arm/boot/compressed/efi-stub.c

diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 7ac1610..c62826a 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
 $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
 	$(addprefix $(obj)/,$(libfdt_hdrs))
 
+$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
+	$(addprefix $(obj)/,$(libfdt_hdrs))
+
 ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
-OBJS	+= $(libfdt_objs) atags_to_fdt.o
+OBJS	+= atags_to_fdt.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(CONFIG_EFI_STUB),y)
+CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
+OBJS	+= efi-stub.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(USE_LIBFDT),y)
+OBJS	+= $(libfdt_objs)
 endif
 
 targets       := vmlinux vmlinux.lds \
@@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
 KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
 endif
 
-ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
+ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
 asflags-y := -DZIMAGE
 
 # Supply kernel BSS size to the decompressor via a linker symbol.
diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
new file mode 100644
index 0000000..6ff32cc
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-header.S
@@ -0,0 +1,114 @@
+@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
+@
+@ This file contains the PE/COFF header that is part of the
+@ EFI stub.
+@
+
+	.org	0x3c
+	@
+	@ The PE header can be anywhere in the file, but for
+	@ simplicity we keep it together with the MSDOS header
+	@ The offset to the PE/COFF header needs to be at offset
+	@ 0x3C in the MSDOS header.
+	@ The only 2 fields of the MSDOS header that are used are this
+	@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
+	@
+	.long	pe_header			@ Offset to the PE header.
+
+      .align 3
+pe_header:
+
+
+pe_header:
+	.ascii	"PE"
+	.short 	0
+
+coff_header:
+	.short	0x01c2				@ ARM or Thumb
+	.short	2				@ nr_sections
+	.long	0 				@ TimeDateStamp
+	.long	0				@ PointerToSymbolTable
+	.long	1				@ NumberOfSymbols
+	.short	section_table - optional_header	@ SizeOfOptionalHeader
+	.short	0x306				@ Characteristics.
+						@ IMAGE_FILE_32BIT_MACHINE |
+						@ IMAGE_FILE_DEBUG_STRIPPED |
+						@ IMAGE_FILE_EXECUTABLE_IMAGE |
+						@ IMAGE_FILE_LINE_NUMS_STRIPPED
+
+optional_header:
+	.short	0x10b				@ PE32 format
+	.byte	0x02				@ MajorLinkerVersion
+	.byte	0x14				@ MinorLinkerVersion
+
+	.long	0				@ SizeOfCode
+
+	.long	0				@ SizeOfInitializedData
+	.long	0				@ SizeOfUninitializedData
+
+	.long	efi_stub_entry			@ AddressOfEntryPoint
+	.long	efi_stub_entry			@ BaseOfCode
+	.long	0				@ data
+
+extra_header_fields:
+	.long	0				@ ImageBase
+	.long	0x20				@ SectionAlignment
+	.long	0x20				@ FileAlignment
+	.short	0				@ MajorOperatingSystemVersion
+	.short	0				@ MinorOperatingSystemVersion
+	.short	0				@ MajorImageVersion
+	.short	0				@ MinorImageVersion
+	.short	0				@ MajorSubsystemVersion
+	.short	0				@ MinorSubsystemVersion
+	.long	0				@ Win32VersionValue
+
+	.long	_edata				@ SizeOfImage
+
+	@ Everything before the entry point is considered part of the header
+	.long	efi_stub_entry			@ SizeOfHeaders
+	.long	0				@ CheckSum
+	.short	0xa				@ Subsystem (EFI application)
+	.short	0				@ DllCharacteristics
+	.long	0				@ SizeOfStackReserve
+	.long	0				@ SizeOfStackCommit
+	.long	0				@ SizeOfHeapReserve
+	.long	0				@ SizeOfHeapCommit
+	.long	0				@ LoaderFlags
+	.long	0x0				@ NumberOfRvaAndSizes
+
+	# Section table
+section_table:
+
+	#
+	# The EFI application loader requires a relocation section
+	# because EFI applications must be relocatable.  This is a
+	# dummy section as far as we are concerned.
+	#
+	.ascii	".reloc"
+	.byte	0
+	.byte	0			@ end of 0 padding of section name
+	.long	0
+	.long	0
+	.long	0			@ SizeOfRawData
+	.long	0			@ PointerToRawData
+	.long	0			@ PointerToRelocations
+	.long	0			@ PointerToLineNumbers
+	.short	0			@ NumberOfRelocations
+	.short	0			@ NumberOfLineNumbers
+	.long	0x42100040		@ Characteristics (section flags)
+
+
+	.ascii	".text"
+	.byte	0
+	.byte	0
+	.byte	0        		@ end of 0 padding of section name
+	.long	_edata - efi_stub_entry		@ VirtualSize
+	.long	efi_stub_entry			@ VirtualAddress
+	.long	_edata - efi_stub_entry		@ SizeOfRawData
+	.long	efi_stub_entry			@ PointerToRawData
+
+	.long	0		@ PointerToRelocations (0 for executables)
+	.long	0		@ PointerToLineNumbers (0 for executables)
+	.short	0		@ NumberOfRelocations  (0 for executables)
+	.short	0		@ NumberOfLineNumbers  (0 for executables)
+	.long	0xe0500020	@ Characteristics (section flags)
diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
new file mode 100644
index 0000000..b817ea3
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-stub.c
@@ -0,0 +1,514 @@
+/*
+ * linux/arch/arm/boot/compressed/efi-stub.c
+ *
+ * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
+ *
+ * This file implements the EFI boot stub for the ARM kernel
+ *
+ * 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 <libfdt.h>
+
+
+/* Error code returned to ASM code instead of valid FDT address. */
+#define EFI_STUB_ERROR		(~0)
+
+/* EFI function call wrappers.  These are not required for
+ * ARM, but wrappers are required for X86 to convert between
+ * ABIs.  These wrappers are provided to allow code sharing
+ * between X86 and ARM.  Since these wrappers directly invoke the
+ * EFI function pointer, the function pointer type must be properly
+ * defined, which is not the case for X86  One advantage of this is
+ * it allows for type checking of arguments, which is not
+ * possible with the X86 wrappers.
+ */
+#define efi_call_phys0(f)			f()
+#define efi_call_phys1(f, a1)			f(a1)
+#define efi_call_phys2(f, a1, a2)		f(a1, a2)
+#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
+#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
+#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
+
+/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
+ * that for the decompressed kernel.  We have no easy way to tell what
+ * the actuall size of code + data the uncompressed kernel will use.
+ */
+#define MAX_UNCOMP_KERNEL_SIZE	0x02000000
+
+/* The kernel zImage should be located between 32 Mbytes
+ * and 128 MBytes from the base of DRAM.  The min
+ * address leaves space for a maximal size uncompressed image,
+ * and the max address is due to how the zImage decompressor
+ * picks a destination address.
+ */
+#define MAX_ZIMAGE_OFFSET	0x08000000
+#define MIN_ZIMAGE_OFFSET	MAX_UNCOMP_KERNEL_SIZE
+
+#define MAX_CMDLINE_LEN		500
+
+struct fdt_region {
+	u64 base;
+	u64 size;
+};
+
+/*
+ * Additional size that could be used for FDT entries added by
+ * the UEFI OS Loader Estimation based on:
+ * EDID (300bytes) + bootargs  + initrd region (20bytes)
+ * + system memory region (20bytes) + mp_core entries (200
+ * bytes)
+ */
+#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
+
+/* Include shared EFI stub code */
+#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
+
+
+static int is_linux_reserved_region(int memory_type)
+{
+	switch (memory_type) {
+	case EFI_RUNTIME_SERVICES_CODE:
+	case EFI_RUNTIME_SERVICES_DATA:
+	case EFI_UNUSABLE_MEMORY:
+	case EFI_ACPI_RECLAIM_MEMORY:
+	case EFI_ACPI_MEMORY_NVS:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+
+static int relocate_kernel(efi_system_table_t *sys_table,
+			   unsigned long *load_addr, unsigned long *load_size,
+			   unsigned long min_addr, unsigned long max_addr)
+{
+	/* Get current address of kernel. */
+	unsigned long cur_zimage_addr = *load_addr;
+	unsigned long zimage_size = *load_size;
+	unsigned long new_addr = 0;
+	unsigned long nr_pages;
+
+	efi_status_t status;
+
+	if (!load_addr || !load_size)
+		return EFI_INVALID_PARAMETER;
+
+	*load_size = 0;
+	if (cur_zimage_addr > min_addr
+	    && (cur_zimage_addr + zimage_size) < max_addr) {
+		/* We don't need to do anything, as kernel@an acceptable
+		 * address already.
+		 */
+		return EFI_SUCCESS;
+	}
+	/*
+	 * The EFI firmware loader could have placed the kernel image
+	 * anywhere in memory, but the kernel has restrictions on the
+	 * min and max physical address it can run at.
+	 */
+	nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+	status = efi_low_alloc(sys_table, zimage_size, 0,
+			   &new_addr, min_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
+		return status;
+	}
+
+	if (new_addr > (max_addr - zimage_size)) {
+		efi_free(sys_table, zimage_size, new_addr);
+		efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
+		return EFI_INVALID_PARAMETER;
+	}
+
+	/* We know source/dest won't overlap since both memory ranges
+	 * have been allocated by UEFI, so we can safely use memcpy.
+	 */
+	memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
+	       zimage_size);
+
+	/* Return the load address and size */
+	*load_addr = new_addr;
+	*load_size = zimage_size;
+
+
+	return status;
+}
+
+
+/* Convert the unicode UEFI command line to ASCII to pass to kernel.
+ * Size of memory allocated return in *cmd_line_len.
+ * Returns NULL on error.
+ */
+static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
+				      efi_loaded_image_t *image,
+				      unsigned long *cmd_line_len,
+				      u32 max_addr)
+{
+	u16 *s2;
+	u8 *s1 = NULL;
+	unsigned long cmdline_addr = 0;
+	int load_options_size = image->load_options_size / 2; /* ASCII */
+	void *options = (u16 *)image->load_options;
+	int options_size = 0;
+	int status;
+	int i;
+	u16 zero = 0;
+
+	if (options) {
+		s2 = options;
+		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
+			s2++;
+			options_size++;
+		}
+	}
+
+	if (options_size == 0) {
+		/* No command line options, so return empty string*/
+		options_size = 1;
+		options = &zero;
+	}
+
+	if (options_size > MAX_CMDLINE_LEN)
+		options_size = MAX_CMDLINE_LEN;
+
+	options_size++;  /* NUL termination */
+
+	status = efi_high_alloc(sys_table, options_size, 0,
+			    &cmdline_addr, max_addr);
+	if (status != EFI_SUCCESS)
+		return NULL;
+
+	s1 = (u8 *)(unsigned long)cmdline_addr;
+	s2 = (u16 *)options;
+
+	for (i = 0; i < options_size - 1; i++)
+		*s1++ = *s2++;
+
+	*s1 = '\0';
+
+	*cmd_line_len = options_size;
+	return (char *)(unsigned long)cmdline_addr;
+}
+
+static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
+				    void *handle, unsigned long dram_base,
+				    void *orig_fdt, u64 *orig_fdt_size,
+				    char *cmdline_ptr,
+				    unsigned long *cmdline_size,
+				    u64 initrd_addr, u64 initrd_size)
+{
+	unsigned long new_fdt_size;
+	unsigned long new_fdt_addr;
+	void *fdt;
+	int node;
+	int status;
+	int i;
+	unsigned long map_size, desc_size;
+	unsigned long mmap_key;
+	efi_memory_desc_t *memory_map;
+	unsigned long fdt_val;
+
+	new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
+	status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
+			    dram_base + MAX_ZIMAGE_OFFSET);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
+		goto fail;
+	}
+
+
+	fdt = (void *)new_fdt_addr;
+	status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
+	if (status != 0) {
+		efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
+		goto fail_free_new_fdt;
+	}
+	/* We are done with the original DTB, so free it. */
+	efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
+	*orig_fdt_size = 0;
+
+	node = fdt_subnode_offset(fdt, 0, "chosen");
+	if (node < 0) {
+		node = fdt_add_subnode(fdt, 0, "chosen");
+		if (node < 0) {
+			efi_printk(sys_table, "Error on finding 'chosen' node\n");
+			goto fail_free_new_fdt;
+		}
+	}
+
+	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
+		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
+				     strlen(cmdline_ptr) + 1);
+		if (status) {
+			efi_printk(sys_table, "Failed to set new bootarg\n");
+			goto fail_free_new_fdt;
+		}
+	}
+	/* We are done with original command line, so free it. */
+	efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
+	*cmdline_size = 0;
+
+	/* Set intird address/end in device tree, if present */
+	if (initrd_size != 0) {
+		u64 initrd_image_end;
+		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
+		status = fdt_setprop(fdt, node, "linux,initrd-start",
+				     &initrd_image_start, sizeof(u64));
+		if (status) {
+			efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
+			goto fail_free_new_fdt;
+		}
+		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
+		status = fdt_setprop(fdt, node, "linux,initrd-end",
+				     &initrd_image_end, sizeof(u64));
+		if (status) {
+			efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
+			goto fail_free_new_fdt;
+		}
+	}
+
+	/* Update memory map in the device tree. The memory node must
+	 * be present in the tree.*/
+	node = fdt_subnode_offset(fdt, 0, "memory");
+	if (node < 0) {
+		efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
+		goto fail_free_new_fdt;
+	}
+
+	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
+				    &desc_size, &mmap_key);
+	if (status != EFI_SUCCESS)
+		goto fail_free_new_fdt;
+
+	for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
+		efi_memory_desc_t *desc;
+		unsigned long m = (unsigned long)memory_map;
+		desc = (efi_memory_desc_t *)(m + (i * desc_size));
+
+		if (is_linux_reserved_region(desc->type)) {
+			status = fdt_add_mem_rsv(fdt, desc->phys_addr,
+						 desc->num_pages * EFI_PAGE_SIZE);
+			if (status != 0) {
+				efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
+				goto fail_free_mmap;
+			}
+		}
+	}
+
+
+	/* Add FDT entries for EFI runtime services in chosen node.
+	 * We need to add the final memory map, so this is done at
+	 * the very end.
+	 */
+	node = fdt_subnode_offset(fdt, 0, "chosen");
+	fdt_val = cpu_to_fdt32((unsigned long)sys_table);
+	status = fdt_setprop(fdt, node, "efi-system-table",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32(desc_size);
+	status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32(map_size);
+	status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
+		goto fail_free_new_fdt;
+	}
+	fdt_val = cpu_to_fdt32((unsigned long)memory_map);
+	status = fdt_setprop(fdt, node, "efi-runtime-mmap",
+			     &fdt_val, sizeof(fdt_val));
+	if (status) {
+		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
+		goto fail_free_new_fdt;
+	}
+
+	/* Now we need to exit boot services.  We need the key from
+	 * the most recent read of the memory map to do this.  We can't
+	 * free this buffer in the normal case, but do free it when
+	 * exit_boot_services() fails or adding the memory map to the FDT
+	 * fails.
+	 */
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, mmap_key);
+
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "exit boot services failed.\n");
+		goto fail_free_mmap;
+	}
+
+	return new_fdt_addr;
+
+fail_free_mmap:
+	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
+
+fail_free_new_fdt:
+	efi_free(sys_table, new_fdt_size, new_fdt_addr);
+
+fail:
+	return 0;
+}
+
+
+int efi_entry(void *handle, efi_system_table_t *sys_table,
+	      unsigned long *zimage_addr)
+{
+	efi_loaded_image_t *image;
+	int status;
+	unsigned long nr_pages;
+	const struct fdt_region *region;
+
+	void *fdt;
+	int err;
+	int node;
+	unsigned long zimage_size = 0;
+	unsigned long dram_base;
+	/* addr/point and size pairs for memory management*/
+	u64 initrd_addr;
+	u64 initrd_size = 0;
+	u64 fdt_addr;  /* Original DTB */
+	u64 fdt_size = 0;
+	u64 kernel_reserve_addr;
+	u64 kernel_reserve_size = 0;
+	char *cmdline_ptr;
+	unsigned long cmdline_size = 0;
+	unsigned long new_fdt_addr;
+
+	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	efi_printk(sys_table, "Booting Linux using EFI stub.\n");
+
+
+	/* get the command line from EFI, using the LOADED_IMAGE protocol */
+	status = efi_call_phys3(sys_table->boottime->handle_protocol,
+				handle, &proto, (void *)&image);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		goto fail;
+	}
+
+	/* We are going to copy this into device tree, so we don't care where in
+	 * memory it is.
+	 */
+	cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
+					       &cmdline_size, 0xFFFFFFFF);
+	if (!cmdline_ptr) {
+		efi_printk(sys_table, "ERROR converting command line to ascii.\n");
+		goto fail;
+	}
+
+	/* We first load the device tree, as we need to get the base address of
+	 * DRAM from the device tree.  The zImage, device tree, and initrd
+	 * have address restrictions that are relative to the base of DRAM.
+	 */
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
+				      0xffffffff, &fdt_addr, &fdt_size);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Error loading dtb blob\n");
+		goto fail_free_cmdline;
+	}
+
+	err = fdt_check_header((void *)(unsigned long)fdt_addr);
+	if (err != 0) {
+		efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
+		goto fail_free_dtb;
+	}
+	if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
+		efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
+		goto fail_free_dtb;
+
+	}
+
+
+	/* Look up the base of DRAM from the device tree.*/
+	fdt = (void *)(u32)fdt_addr;
+	node = fdt_subnode_offset(fdt, 0, "memory");
+	region = fdt_getprop(fdt, node, "reg", NULL);
+	if (region) {
+		dram_base = fdt64_to_cpu(region->base);
+	} else {
+		efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
+		goto fail_free_dtb;
+	}
+
+	/* Reserve memory for the uncompressed kernel image. */
+	kernel_reserve_addr = dram_base;
+	kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
+	nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	status = efi_call_phys4(sys_table->boottime->allocate_pages,
+				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+				nr_pages, &kernel_reserve_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
+		goto fail_free_dtb;
+	}
+
+	/* Relocate the zImage, if required. */
+	zimage_size = image->image_size;
+	status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
+				 dram_base + MIN_ZIMAGE_OFFSET,
+				 dram_base + MAX_ZIMAGE_OFFSET);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Failed to relocate kernel\n");
+		goto fail_free_kernel_reserve;
+	}
+
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
+				      dram_base + MAX_ZIMAGE_OFFSET,
+				      &initrd_addr, &initrd_size);
+	if (status != EFI_SUCCESS) {
+		efi_printk(sys_table, "Error loading initrd\n");
+		goto fail_free_zimage;
+	}
+
+	new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
+						dram_base, fdt, &fdt_size,
+						cmdline_ptr, &cmdline_size,
+						initrd_addr, initrd_size);
+
+	if (new_fdt_addr == 0) {
+		efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
+		goto fail_free_initrd;
+	}
+
+
+	/* Now we need to return the FDT address to the calling
+	 * assembly to this can be used as part of normal boot.
+	 */
+	return new_fdt_addr;
+
+fail_free_initrd:
+	efi_free(sys_table, initrd_size, initrd_addr);
+
+fail_free_zimage:
+	efi_free(sys_table, zimage_size, *zimage_addr);
+
+fail_free_kernel_reserve:
+	efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
+
+fail_free_dtb:
+	efi_free(sys_table, fdt_size, fdt_addr);
+
+fail_free_cmdline:
+	efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
+
+fail:
+	return EFI_STUB_ERROR;
+}
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 75189f1..491e752 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -120,21 +120,100 @@
  */
 		.align
 		.arm				@ Always enter in ARM state
+		.text
 start:
 		.type	start,#function
-		.rept	7
+#ifdef CONFIG_EFI_STUB
+		@ Magic MSDOS signature for PE/COFF + ADD opcode
+		.word	0x62805a4d
+#else
+		mov	r0, r0
+#endif
+		.rept	5
 		mov	r0, r0
 		.endr
-   ARM(		mov	r0, r0		)
-   ARM(		b	1f		)
- THUMB(		adr	r12, BSYM(1f)	)
- THUMB(		bx	r12		)
+
+		@ zimage_continue will be in ARM or thumb mode as configured
+ THUMB(		adrl	r12, BSYM(zimage_continue))
+ ARM(		adrl	r12, zimage_continue)
+		bx	r12
+ THUMB(		.thumb			)
 
 		.word	0x016f2818		@ Magic numbers to help the loader
 		.word	start			@ absolute load/run zImage address
 		.word	_edata			@ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+		@ Portions of the MSDOS file header must be at offset
+		@ 0x3c from the start of the file.  All PE/COFF headers
+		@ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+		@ The EFI stub entry point is not at a fixed address, however
+		@ this address must be set in the PE/COFF header.
+		@ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB(		.arm			)
+ THUMB(		adr	r12, BSYM(1f)	)
+ THUMB(		bx	r12		)
  THUMB(		.thumb			)
 1:
+		@ Save lr on stack for possible return to EFI firmware.
+		@ Don't care about fp, but need 64 bit alignment....
+		stmfd	sp!, {fp, lr}
+
+		@ Save args to EFI app across got fixup call
+		stmfd	sp!, {r0, r1}
+		ldmfd	sp!, {r0, r1}
+
+		@ allocate space on stack for return of new entry point of
+		@ zImage, as EFI stub may copy the kernel.  Pass address
+		@ of space in r2 - EFI stub will fill in the pointer.
+
+		sub	sp, #8			@ we only need 4 bytes,
+						@ but keep stack 8 byte aligned.
+		mov	r2, sp
+		@ Pass our actual runtime start address in pointer data
+		adr	r11, LC0		@ address of LC0 at run time
+		ldr	r12, [r11, #0]		@ address of LC0 at link time
+
+		sub	r3, r11, r12		@ calculate the delta offset
+		str	r3, [r2, #0]
+		bl	efi_entry
+
+		@ get new zImage entry address from stack, put into r3
+		ldr	r3, [sp, #0]
+		add	sp, #8  @ restore stack
+
+		@ Check for error return from EFI stub (0xFFFFFFFF)
+		ldr	r1, =0xffffffff
+		cmp	r0, r1
+		beq	efi_load_fail
+
+
+		@ Save return values of efi_entry
+		stmfd	sp!, {r0, r3}
+		bl	cache_clean_flush
+		bl	cache_off
+		ldmfd   sp!, {r0, r3}
+
+		@ put DTB address in r2, it was returned by EFI entry
+		mov	r2, r0
+		ldr	r1, =0xffffffff		@ DTB machine type
+		mov	r0, #0  @ r0 is 0
+
+		@ Branch to (possibly) relocated zImage entry that is in r3
+		bx	r3
+
+efi_load_fail:
+		@ Return EFI_LOAD_ERROR to EFI firmware on error.
+		@ Switch back to ARM mode for EFI is done based on
+		@ return address on stack
+		ldr	r0, =0x80000001
+		ldmfd	sp!, {fp, pc}
+#endif
+
+zimage_continue:
 		mrs	r9, cpsr
 #ifdef CONFIG_ARM_VIRT_EXT
 		bl	__hyp_stub_install	@ get into SVC mode, reversibly
@@ -167,7 +246,6 @@ not_angel:
 		 * by the linker here, but it should preserve r7, r8, and r9.
 		 */
 
-		.text
 
 #ifdef CONFIG_AUTO_ZRELADDR
 		@ determine final kernel image address
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 17/17] Add config EFI_STUB for ARM to Kconfig
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  3:45   ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux
  Cc: leif.lindholm, dave.martin, Roy Franz

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/arm/Kconfig |   10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 37c0f4e..192968c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1805,6 +1805,16 @@ config UACCESS_WITH_MEMCPY
 	  However, if the CPU data cache is using a write-allocate mode,
 	  this option is unlikely to provide any performance gain.
 
+config EFI_STUB
+	bool "EFI stub support"
+	---help---
+	  This kernel feature allows a zImage to be loaded directly
+	  by EFI firmware without the use of a bootloader.  A PE/COFF
+	  header is added to the zImage in a way that makes the binary
+	  both a Linux zImage and an PE/COFF executable that can be
+	  executed directly by EFI firmware.
+	  See Documentation/efi-stub.txt for more information.
+
 config SECCOMP
 	bool
 	prompt "Enable seccomp to safely compute untrusted bytecode"
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 17/17] Add config EFI_STUB for ARM to Kconfig
@ 2013-08-07  3:45   ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Roy Franz <roy.franz@linaro.org>
---
 arch/arm/Kconfig |   10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 37c0f4e..192968c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1805,6 +1805,16 @@ config UACCESS_WITH_MEMCPY
 	  However, if the CPU data cache is using a write-allocate mode,
 	  this option is unlikely to provide any performance gain.
 
+config EFI_STUB
+	bool "EFI stub support"
+	---help---
+	  This kernel feature allows a zImage to be loaded directly
+	  by EFI firmware without the use of a bootloader.  A PE/COFF
+	  header is added to the zImage in a way that makes the binary
+	  both a Linux zImage and an PE/COFF executable that can be
+	  executed directly by EFI firmware.
+	  See Documentation/efi-stub.txt for more information.
+
 config SECCOMP
 	bool
 	prompt "Enable seccomp to safely compute untrusted bytecode"
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH V2 00/17] EFI stub for ARM
  2013-08-07  3:44 ` Roy Franz
@ 2013-08-07  7:44   ` Matt Fleming
  -1 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07  7:44 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm, dave.martin

On Tue, 06 Aug, at 08:44:56PM, Roy Franz wrote:
> This patchset depends (trivially) on a separately submitted bugfix to the
> EFI stub: "correct call to free_pages"
> http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?id=df981edcb9bce00b9c5e4f3cc33f3f98bc9a2394

For those playing at home, this bugfix is now in Linus' tree.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH V2 00/17] EFI stub for ARM
@ 2013-08-07  7:44   ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07  7:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 06 Aug, at 08:44:56PM, Roy Franz wrote:
> This patchset depends (trivially) on a separately submitted bugfix to the
> EFI stub: "correct call to free_pages"
> http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?id=df981edcb9bce00b9c5e4f3cc33f3f98bc9a2394

For those playing at home, this bugfix is now in Linus' tree.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 13:08     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:08 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm, dave.martin

On Tue, 06 Aug, at 08:44:59PM, Roy Franz wrote:
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  arch/x86/boot/compressed/eboot.c       |   38 +++++++------
>  drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
>  2 files changed, 72 insertions(+), 62 deletions(-)

For future reference you should really use a shorter first line in your
git commit message, which would produe a shorter subject when mailing
your patches.

I'll fix up the commit messages when I apply these patches, so don't
worry about it for now.

[...]

> @@ -19,15 +19,16 @@ struct initrd {
>  
>  
>  
> -static void efi_char16_printk(efi_char16_t *str)
> +static void efi_char16_printk(efi_system_table_t *sys_table_arg,
> +			      efi_char16_t *str)
>  {
>  	struct efi_simple_text_output_protocol *out;
>  
> -	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
> +	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
>  	efi_call_phys2(out->output_string, out, str);
>  }
>  
> -static void efi_printk(char *str)
> +static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
>  {
>  	char *s8;
>  

Hmm... I'm not necessarily convinced this is an improvement over using
some kind of a global pointer to the EFI System Table.

Parameterizing stuff like this is useful when the argument changes at
runtime from call to call, but that isn't the case for the boot stubs. I
don't think there's anything wrong with a global in this scenario, and
this patch is a fair amount of churn for no real improvement.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 13:08     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:08 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ,
	leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8

On Tue, 06 Aug, at 08:44:59PM, Roy Franz wrote:
> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  arch/x86/boot/compressed/eboot.c       |   38 +++++++------
>  drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
>  2 files changed, 72 insertions(+), 62 deletions(-)

For future reference you should really use a shorter first line in your
git commit message, which would produe a shorter subject when mailing
your patches.

I'll fix up the commit messages when I apply these patches, so don't
worry about it for now.

[...]

> @@ -19,15 +19,16 @@ struct initrd {
>  
>  
>  
> -static void efi_char16_printk(efi_char16_t *str)
> +static void efi_char16_printk(efi_system_table_t *sys_table_arg,
> +			      efi_char16_t *str)
>  {
>  	struct efi_simple_text_output_protocol *out;
>  
> -	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
> +	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
>  	efi_call_phys2(out->output_string, out, str);
>  }
>  
> -static void efi_printk(char *str)
> +static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
>  {
>  	char *s8;
>  

Hmm... I'm not necessarily convinced this is an improvement over using
some kind of a global pointer to the EFI System Table.

Parameterizing stuff like this is useful when the argument changes at
runtime from call to call, but that isn't the case for the boot stubs. I
don't think there's anything wrong with a global in this scenario, and
this patch is a fair amount of churn for no real improvement.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 13:08     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 06 Aug, at 08:44:59PM, Roy Franz wrote:
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  arch/x86/boot/compressed/eboot.c       |   38 +++++++------
>  drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
>  2 files changed, 72 insertions(+), 62 deletions(-)

For future reference you should really use a shorter first line in your
git commit message, which would produe a shorter subject when mailing
your patches.

I'll fix up the commit messages when I apply these patches, so don't
worry about it for now.

[...]

> @@ -19,15 +19,16 @@ struct initrd {
>  
>  
>  
> -static void efi_char16_printk(efi_char16_t *str)
> +static void efi_char16_printk(efi_system_table_t *sys_table_arg,
> +			      efi_char16_t *str)
>  {
>  	struct efi_simple_text_output_protocol *out;
>  
> -	out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
> +	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
>  	efi_call_phys2(out->output_string, out, str);
>  }
>  
> -static void efi_printk(char *str)
> +static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
>  {
>  	char *s8;
>  

Hmm... I'm not necessarily convinced this is an improvement over using
some kind of a global pointer to the EFI System Table.

Parameterizing stuff like this is useful when the argument changes at
runtime from call to call, but that isn't the case for the boot stubs. I
don't think there's anything wrong with a global in this scenario, and
this patch is a fair amount of churn for no real improvement.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/17] Rename memory allocation/free functions
  2013-08-07  3:45   ` Roy Franz
@ 2013-08-07 13:09     ` Matt Fleming
  -1 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm, dave.martin

On Tue, 06 Aug, at 08:45:00PM, Roy Franz wrote:
> Rename them to be more similar, as low_free() could be used to free
> memory allocated by both high_alloc() and low_alloc().
> high_alloc() -> efi_high_alloc()
> low_alloc()  -> efi_low_alloc()
> low_free()   -> efi_free()
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
>  drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
>  2 files changed, 18 insertions(+), 17 deletions(-)

[...]

> @@ -163,10 +163,10 @@ fail:
>  }
>  
>  /*
> - * Allocate at the lowest possible address.
> + * Allocate at the lowest possible address, that is not below 'min'
>   */
> -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
> -		unsigned long size, unsigned long align,
> +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
> +			      unsigned long size, unsigned long align,
>  			      unsigned long *addr)
>  {
>  	unsigned long map_size, desc_size;

Whoops, did this change to the comment slip in from some other patch?

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 04/17] Rename memory allocation/free functions
@ 2013-08-07 13:09     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 06 Aug, at 08:45:00PM, Roy Franz wrote:
> Rename them to be more similar, as low_free() could be used to free
> memory allocated by both high_alloc() and low_alloc().
> high_alloc() -> efi_high_alloc()
> low_alloc()  -> efi_low_alloc()
> low_free()   -> efi_free()
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
>  drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
>  2 files changed, 18 insertions(+), 17 deletions(-)

[...]

> @@ -163,10 +163,10 @@ fail:
>  }
>  
>  /*
> - * Allocate at the lowest possible address.
> + * Allocate at the lowest possible address, that is not below 'min'
>   */
> -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
> -		unsigned long size, unsigned long align,
> +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
> +			      unsigned long size, unsigned long align,
>  			      unsigned long *addr)
>  {
>  	unsigned long map_size, desc_size;

Whoops, did this change to the comment slip in from some other patch?

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 10/17] Renames in handle_cmdline_files() to complete generalization.
  2013-08-07  3:45   ` Roy Franz
@ 2013-08-07 13:09     ` Matt Fleming
  -1 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm, dave.martin

On Tue, 06 Aug, at 08:45:06PM, Roy Franz wrote:
> Rename variables to be not initrd specific, as now the function
> loads arbitrary files.
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  drivers/firmware/efi/efi-stub-helper.c |   92 ++++++++++++++++----------------
>  1 file changed, 46 insertions(+), 46 deletions(-)

This looks nice, thanks.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 10/17] Renames in handle_cmdline_files() to complete generalization.
@ 2013-08-07 13:09     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 06 Aug, at 08:45:06PM, Roy Franz wrote:
> Rename variables to be not initrd specific, as now the function
> loads arbitrary files.
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  drivers/firmware/efi/efi-stub-helper.c |   92 ++++++++++++++++----------------
>  1 file changed, 46 insertions(+), 46 deletions(-)

This looks nice, thanks.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07 13:09     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm, dave.martin

On Tue, 06 Aug, at 08:45:08PM, Roy Franz wrote:
> The x86/AMD64 EFI stubs must us a call wrapper to convert between
> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
> the ABIs are compatible, so we can directly invoke the function
> pointers.  The functions that are used by the ARM stub are updated
> to match the EFI definitions.
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 51f5641..96bb866 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,8 @@
>  typedef unsigned long efi_status_t;
>  typedef u8 efi_bool_t;
>  typedef u16 efi_char16_t;		/* UNICODE character */
> +typedef u64 efi_physical_addr_t;
> +typedef void *efi_handle_t;
>  
>  
>  typedef struct {
> @@ -96,6 +98,7 @@ typedef	struct {
>  #define EFI_MEMORY_DESCRIPTOR_VERSION	1
>  
>  #define EFI_PAGE_SHIFT		12
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)

It would be worth getting rid of the EFI_PAGE_SIZE definition in
arch/x86/boot/compressed/eboot.h now that we have one here.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07 13:09     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ,
	leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8

On Tue, 06 Aug, at 08:45:08PM, Roy Franz wrote:
> The x86/AMD64 EFI stubs must us a call wrapper to convert between
> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
> the ABIs are compatible, so we can directly invoke the function
> pointers.  The functions that are used by the ARM stub are updated
> to match the EFI definitions.
> 
> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 51f5641..96bb866 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,8 @@
>  typedef unsigned long efi_status_t;
>  typedef u8 efi_bool_t;
>  typedef u16 efi_char16_t;		/* UNICODE character */
> +typedef u64 efi_physical_addr_t;
> +typedef void *efi_handle_t;
>  
>  
>  typedef struct {
> @@ -96,6 +98,7 @@ typedef	struct {
>  #define EFI_MEMORY_DESCRIPTOR_VERSION	1
>  
>  #define EFI_PAGE_SHIFT		12
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)

It would be worth getting rid of the EFI_PAGE_SIZE definition in
arch/x86/boot/compressed/eboot.h now that we have one here.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07 13:09     ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 13:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 06 Aug, at 08:45:08PM, Roy Franz wrote:
> The x86/AMD64 EFI stubs must us a call wrapper to convert between
> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
> the ABIs are compatible, so we can directly invoke the function
> pointers.  The functions that are used by the ARM stub are updated
> to match the EFI definitions.
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 51f5641..96bb866 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,8 @@
>  typedef unsigned long efi_status_t;
>  typedef u8 efi_bool_t;
>  typedef u16 efi_char16_t;		/* UNICODE character */
> +typedef u64 efi_physical_addr_t;
> +typedef void *efi_handle_t;
>  
>  
>  typedef struct {
> @@ -96,6 +98,7 @@ typedef	struct {
>  #define EFI_MEMORY_DESCRIPTOR_VERSION	1
>  
>  #define EFI_PAGE_SHIFT		12
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)

It would be worth getting rid of the EFI_PAGE_SIZE definition in
arch/x86/boot/compressed/eboot.h now that we have one here.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 17:10       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:10 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

On Wed, Aug 7, 2013 at 6:08 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:44:59PM, Roy Franz wrote:
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/x86/boot/compressed/eboot.c       |   38 +++++++------
>>  drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
>>  2 files changed, 72 insertions(+), 62 deletions(-)
>
> For future reference you should really use a shorter first line in your
> git commit message, which would produe a shorter subject when mailing
> your patches.
>
> I'll fix up the commit messages when I apply these patches, so don't
> worry about it for now.
>
> [...]
>
>> @@ -19,15 +19,16 @@ struct initrd {
>>
>>
>>
>> -static void efi_char16_printk(efi_char16_t *str)
>> +static void efi_char16_printk(efi_system_table_t *sys_table_arg,
>> +                           efi_char16_t *str)
>>  {
>>       struct efi_simple_text_output_protocol *out;
>>
>> -     out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
>> +     out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
>>       efi_call_phys2(out->output_string, out, str);
>>  }
>>
>> -static void efi_printk(char *str)
>> +static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
>>  {
>>       char *s8;
>>
>
> Hmm... I'm not necessarily convinced this is an improvement over using
> some kind of a global pointer to the EFI System Table.
>
> Parameterizing stuff like this is useful when the argument changes at
> runtime from call to call, but that isn't the case for the boot stubs. I
> don't think there's anything wrong with a global in this scenario, and
> this patch is a fair amount of churn for no real improvement.
>
> --
> Matt Fleming, Intel Open Source Technology Center

Hi Matt,

I went this way since the shared code is in a separate file - I really
didn't like using a global variable as part of the interface to
the shared code.  This has the nice side benefit of allowing the ARM
stub to not use any global variables, so we don't have to do
any GOT fixups to relocate the code - it is position independent if we
don't use global variables.

Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 17:10       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:10 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w, Russell King - ARM Linux,
	Leif Lindholm, Dave Martin

On Wed, Aug 7, 2013 at 6:08 AM, Matt Fleming <matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org> wrote:
> On Tue, 06 Aug, at 08:44:59PM, Roy Franz wrote:
>> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  arch/x86/boot/compressed/eboot.c       |   38 +++++++------
>>  drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
>>  2 files changed, 72 insertions(+), 62 deletions(-)
>
> For future reference you should really use a shorter first line in your
> git commit message, which would produe a shorter subject when mailing
> your patches.
>
> I'll fix up the commit messages when I apply these patches, so don't
> worry about it for now.
>
> [...]
>
>> @@ -19,15 +19,16 @@ struct initrd {
>>
>>
>>
>> -static void efi_char16_printk(efi_char16_t *str)
>> +static void efi_char16_printk(efi_system_table_t *sys_table_arg,
>> +                           efi_char16_t *str)
>>  {
>>       struct efi_simple_text_output_protocol *out;
>>
>> -     out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
>> +     out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
>>       efi_call_phys2(out->output_string, out, str);
>>  }
>>
>> -static void efi_printk(char *str)
>> +static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
>>  {
>>       char *s8;
>>
>
> Hmm... I'm not necessarily convinced this is an improvement over using
> some kind of a global pointer to the EFI System Table.
>
> Parameterizing stuff like this is useful when the argument changes at
> runtime from call to call, but that isn't the case for the boot stubs. I
> don't think there's anything wrong with a global in this scenario, and
> this patch is a fair amount of churn for no real improvement.
>
> --
> Matt Fleming, Intel Open Source Technology Center

Hi Matt,

I went this way since the shared code is in a separate file - I really
didn't like using a global variable as part of the interface to
the shared code.  This has the nice side benefit of allowing the ARM
stub to not use any global variables, so we don't have to do
any GOT fixups to relocate the code - it is position independent if we
don't use global variables.

Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 17:10       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 7, 2013 at 6:08 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:44:59PM, Roy Franz wrote:
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/x86/boot/compressed/eboot.c       |   38 +++++++------
>>  drivers/firmware/efi/efi-stub-helper.c |   96 +++++++++++++++++---------------
>>  2 files changed, 72 insertions(+), 62 deletions(-)
>
> For future reference you should really use a shorter first line in your
> git commit message, which would produe a shorter subject when mailing
> your patches.
>
> I'll fix up the commit messages when I apply these patches, so don't
> worry about it for now.
>
> [...]
>
>> @@ -19,15 +19,16 @@ struct initrd {
>>
>>
>>
>> -static void efi_char16_printk(efi_char16_t *str)
>> +static void efi_char16_printk(efi_system_table_t *sys_table_arg,
>> +                           efi_char16_t *str)
>>  {
>>       struct efi_simple_text_output_protocol *out;
>>
>> -     out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
>> +     out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
>>       efi_call_phys2(out->output_string, out, str);
>>  }
>>
>> -static void efi_printk(char *str)
>> +static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
>>  {
>>       char *s8;
>>
>
> Hmm... I'm not necessarily convinced this is an improvement over using
> some kind of a global pointer to the EFI System Table.
>
> Parameterizing stuff like this is useful when the argument changes at
> runtime from call to call, but that isn't the case for the boot stubs. I
> don't think there's anything wrong with a global in this scenario, and
> this patch is a fair amount of churn for no real improvement.
>
> --
> Matt Fleming, Intel Open Source Technology Center

Hi Matt,

I went this way since the shared code is in a separate file - I really
didn't like using a global variable as part of the interface to
the shared code.  This has the nice side benefit of allowing the ARM
stub to not use any global variables, so we don't have to do
any GOT fixups to relocate the code - it is position independent if we
don't use global variables.

Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/17] Rename memory allocation/free functions
  2013-08-07 13:09     ` Matt Fleming
  (?)
@ 2013-08-07 17:12       ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:12 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

On Wed, Aug 7, 2013 at 6:09 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:45:00PM, Roy Franz wrote:
>> Rename them to be more similar, as low_free() could be used to free
>> memory allocated by both high_alloc() and low_alloc().
>> high_alloc() -> efi_high_alloc()
>> low_alloc()  -> efi_low_alloc()
>> low_free()   -> efi_free()
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
>>  drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
>>  2 files changed, 18 insertions(+), 17 deletions(-)
>
> [...]
>
>> @@ -163,10 +163,10 @@ fail:
>>  }
>>
>>  /*
>> - * Allocate at the lowest possible address.
>> + * Allocate at the lowest possible address, that is not below 'min'
>>   */
>> -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
>> -             unsigned long size, unsigned long align,
>> +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
>> +                           unsigned long size, unsigned long align,
>>                             unsigned long *addr)
>>  {
>>       unsigned long map_size, desc_size;
>
> Whoops, did this change to the comment slip in from some other patch?
>
> --
> Matt Fleming, Intel Open Source Technology Center

Yes it did - that should be in a later patch that makes that change.

Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/17] Rename memory allocation/free functions
@ 2013-08-07 17:12       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:12 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

On Wed, Aug 7, 2013 at 6:09 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:45:00PM, Roy Franz wrote:
>> Rename them to be more similar, as low_free() could be used to free
>> memory allocated by both high_alloc() and low_alloc().
>> high_alloc() -> efi_high_alloc()
>> low_alloc()  -> efi_low_alloc()
>> low_free()   -> efi_free()
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
>>  drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
>>  2 files changed, 18 insertions(+), 17 deletions(-)
>
> [...]
>
>> @@ -163,10 +163,10 @@ fail:
>>  }
>>
>>  /*
>> - * Allocate at the lowest possible address.
>> + * Allocate at the lowest possible address, that is not below 'min'
>>   */
>> -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
>> -             unsigned long size, unsigned long align,
>> +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
>> +                           unsigned long size, unsigned long align,
>>                             unsigned long *addr)
>>  {
>>       unsigned long map_size, desc_size;
>
> Whoops, did this change to the comment slip in from some other patch?
>
> --
> Matt Fleming, Intel Open Source Technology Center

Yes it did - that should be in a later patch that makes that change.

Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 04/17] Rename memory allocation/free functions
@ 2013-08-07 17:12       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 7, 2013 at 6:09 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:45:00PM, Roy Franz wrote:
>> Rename them to be more similar, as low_free() could be used to free
>> memory allocated by both high_alloc() and low_alloc().
>> high_alloc() -> efi_high_alloc()
>> low_alloc()  -> efi_low_alloc()
>> low_free()   -> efi_free()
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/x86/boot/compressed/eboot.c       |   19 ++++++++++---------
>>  drivers/firmware/efi/efi-stub-helper.c |   16 ++++++++--------
>>  2 files changed, 18 insertions(+), 17 deletions(-)
>
> [...]
>
>> @@ -163,10 +163,10 @@ fail:
>>  }
>>
>>  /*
>> - * Allocate at the lowest possible address.
>> + * Allocate at the lowest possible address, that is not below 'min'
>>   */
>> -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg,
>> -             unsigned long size, unsigned long align,
>> +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
>> +                           unsigned long size, unsigned long align,
>>                             unsigned long *addr)
>>  {
>>       unsigned long map_size, desc_size;
>
> Whoops, did this change to the comment slip in from some other patch?
>
> --
> Matt Fleming, Intel Open Source Technology Center

Yes it did - that should be in a later patch that makes that change.

Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07 17:20       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:20 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

On Wed, Aug 7, 2013 at 6:09 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:45:08PM, Roy Franz wrote:
>> The x86/AMD64 EFI stubs must us a call wrapper to convert between
>> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
>> the ABIs are compatible, so we can directly invoke the function
>> pointers.  The functions that are used by the ARM stub are updated
>> to match the EFI definitions.
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>>  1 file changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/efi.h b/include/linux/efi.h
>> index 51f5641..96bb866 100644
>> --- a/include/linux/efi.h
>> +++ b/include/linux/efi.h
>> @@ -39,6 +39,8 @@
>>  typedef unsigned long efi_status_t;
>>  typedef u8 efi_bool_t;
>>  typedef u16 efi_char16_t;            /* UNICODE character */
>> +typedef u64 efi_physical_addr_t;
>> +typedef void *efi_handle_t;
>>
>>
>>  typedef struct {
>> @@ -96,6 +98,7 @@ typedef     struct {
>>  #define EFI_MEMORY_DESCRIPTOR_VERSION        1
>>
>>  #define EFI_PAGE_SHIFT               12
>> +#define EFI_PAGE_SIZE                (1UL << EFI_PAGE_SHIFT)
>
> It would be worth getting rid of the EFI_PAGE_SIZE definition in
> arch/x86/boot/compressed/eboot.h now that we have one here.
>
> --
> Matt Fleming, Intel Open Source Technology Center

I'll get this in the next version.

Thanks,
Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07 17:20       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:20 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w, Russell King - ARM Linux,
	Leif Lindholm, Dave Martin

On Wed, Aug 7, 2013 at 6:09 AM, Matt Fleming <matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org> wrote:
> On Tue, 06 Aug, at 08:45:08PM, Roy Franz wrote:
>> The x86/AMD64 EFI stubs must us a call wrapper to convert between
>> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
>> the ABIs are compatible, so we can directly invoke the function
>> pointers.  The functions that are used by the ARM stub are updated
>> to match the EFI definitions.
>>
>> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>>  1 file changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/efi.h b/include/linux/efi.h
>> index 51f5641..96bb866 100644
>> --- a/include/linux/efi.h
>> +++ b/include/linux/efi.h
>> @@ -39,6 +39,8 @@
>>  typedef unsigned long efi_status_t;
>>  typedef u8 efi_bool_t;
>>  typedef u16 efi_char16_t;            /* UNICODE character */
>> +typedef u64 efi_physical_addr_t;
>> +typedef void *efi_handle_t;
>>
>>
>>  typedef struct {
>> @@ -96,6 +98,7 @@ typedef     struct {
>>  #define EFI_MEMORY_DESCRIPTOR_VERSION        1
>>
>>  #define EFI_PAGE_SHIFT               12
>> +#define EFI_PAGE_SIZE                (1UL << EFI_PAGE_SHIFT)
>
> It would be worth getting rid of the EFI_PAGE_SIZE definition in
> arch/x86/boot/compressed/eboot.h now that we have one here.
>
> --
> Matt Fleming, Intel Open Source Technology Center

I'll get this in the next version.

Thanks,
Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-07 17:20       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-07 17:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 7, 2013 at 6:09 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Tue, 06 Aug, at 08:45:08PM, Roy Franz wrote:
>> The x86/AMD64 EFI stubs must us a call wrapper to convert between
>> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
>> the ABIs are compatible, so we can directly invoke the function
>> pointers.  The functions that are used by the ARM stub are updated
>> to match the EFI definitions.
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>>  1 file changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/efi.h b/include/linux/efi.h
>> index 51f5641..96bb866 100644
>> --- a/include/linux/efi.h
>> +++ b/include/linux/efi.h
>> @@ -39,6 +39,8 @@
>>  typedef unsigned long efi_status_t;
>>  typedef u8 efi_bool_t;
>>  typedef u16 efi_char16_t;            /* UNICODE character */
>> +typedef u64 efi_physical_addr_t;
>> +typedef void *efi_handle_t;
>>
>>
>>  typedef struct {
>> @@ -96,6 +98,7 @@ typedef     struct {
>>  #define EFI_MEMORY_DESCRIPTOR_VERSION        1
>>
>>  #define EFI_PAGE_SHIFT               12
>> +#define EFI_PAGE_SIZE                (1UL << EFI_PAGE_SHIFT)
>
> It would be worth getting rid of the EFI_PAGE_SIZE definition in
> arch/x86/boot/compressed/eboot.h now that we have one here.
>
> --
> Matt Fleming, Intel Open Source Technology Center

I'll get this in the next version.

Thanks,
Roy

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07 18:05     ` Dave Martin
  0 siblings, 0 replies; 88+ messages in thread
From: Dave Martin @ 2013-08-07 18:05 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm

On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
> operations similarly to the x86 stub: it is a shim between the EFI firmware
> and the normal zImage entry point, and sets up the environment that the
> zImage is expecting.  This includes loading the initrd (optionaly) and
> device tree from the system partition based on the kernel command line.
> The stub updates the device tree as necessary, including adding reserved
> memory regions and adding entries for EFI runtime services. The PE/COFF
> "MZ" header at offset 0 results in the first instruction being an add
> that corrupts r5, which is not used by the zImage interface.

Some more comments below ... note that I haven't really looked at the C
code in depth.

Cheers
---Dave

> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  arch/arm/boot/compressed/Makefile     |   18 +-
>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
>  arch/arm/boot/compressed/head.S       |   90 +++++-
>  4 files changed, 728 insertions(+), 8 deletions(-)
>  create mode 100644 arch/arm/boot/compressed/efi-header.S
>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
> 
> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> index 7ac1610..c62826a 100644
> --- a/arch/arm/boot/compressed/Makefile
> +++ b/arch/arm/boot/compressed/Makefile
> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
>  	$(addprefix $(obj)/,$(libfdt_hdrs))
>  
> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
> +	$(addprefix $(obj)/,$(libfdt_hdrs))
> +

Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?

Would it make sense just to add efi-stub.o to the list of targets in the
original rule?

>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
> -OBJS	+= $(libfdt_objs) atags_to_fdt.o
> +OBJS	+= atags_to_fdt.o
> +USE_LIBFDT = y
> +endif
> +
> +ifeq ($(CONFIG_EFI_STUB),y)
> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
> +OBJS	+= efi-stub.o
> +USE_LIBFDT = y
> +endif
> +
> +ifeq ($(USE_LIBFDT),y)
> +OBJS	+= $(libfdt_objs)
>  endif
>  
>  targets       := vmlinux vmlinux.lds \
> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
>  endif
>  
> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector

You don't appear to explain this change anywhere.

>  asflags-y := -DZIMAGE
>  
>  # Supply kernel BSS size to the decompressor via a linker symbol.
> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
> new file mode 100644
> index 0000000..6ff32cc
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-header.S
> @@ -0,0 +1,114 @@
> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> +@
> +@ This file contains the PE/COFF header that is part of the
> +@ EFI stub.
> +@
> +
> +	.org	0x3c
> +	@
> +	@ The PE header can be anywhere in the file, but for
> +	@ simplicity we keep it together with the MSDOS header
> +	@ The offset to the PE/COFF header needs to be at offset
> +	@ 0x3C in the MSDOS header.
> +	@ The only 2 fields of the MSDOS header that are used are this
> +	@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
> +	@
> +	.long	pe_header			@ Offset to the PE header.

Is there any chance of merging this with the equivalent x86 code?

The PE/COFF header is much the same in both cases, although there
are some differences.  Maybe it would be more trouble than it is
worth...
 
> +
> +      .align 3
> +pe_header:
> +
> +
> +pe_header:

Duplicate label?

> +	.ascii	"PE"
> +	.short 	0
> +
> +coff_header:
> +	.short	0x01c2				@ ARM or Thumb
> +	.short	2				@ nr_sections
> +	.long	0 				@ TimeDateStamp
> +	.long	0				@ PointerToSymbolTable
> +	.long	1				@ NumberOfSymbols
> +	.short	section_table - optional_header	@ SizeOfOptionalHeader
> +	.short	0x306				@ Characteristics.
> +						@ IMAGE_FILE_32BIT_MACHINE |
> +						@ IMAGE_FILE_DEBUG_STRIPPED |
> +						@ IMAGE_FILE_EXECUTABLE_IMAGE |
> +						@ IMAGE_FILE_LINE_NUMS_STRIPPED
> +
> +optional_header:
> +	.short	0x10b				@ PE32 format
> +	.byte	0x02				@ MajorLinkerVersion
> +	.byte	0x14				@ MinorLinkerVersion
> +
> +	.long	0				@ SizeOfCode

Do we need to fill in SizeOfCode with a real value?  It looks like x86
does.

We should probably fill this in unless there's a documented ABI for EFI
boot on ARM which explicitly doesn't require these.

> +
> +	.long	0				@ SizeOfInitializedData
> +	.long	0				@ SizeOfUninitializedData
> +
> +	.long	efi_stub_entry			@ AddressOfEntryPoint
> +	.long	efi_stub_entry			@ BaseOfCode
> +	.long	0				@ data
> +
> +extra_header_fields:
> +	.long	0				@ ImageBase
> +	.long	0x20				@ SectionAlignment
> +	.long	0x20				@ FileAlignment
> +	.short	0				@ MajorOperatingSystemVersion
> +	.short	0				@ MinorOperatingSystemVersion
> +	.short	0				@ MajorImageVersion
> +	.short	0				@ MinorImageVersion
> +	.short	0				@ MajorSubsystemVersion
> +	.short	0				@ MinorSubsystemVersion
> +	.long	0				@ Win32VersionValue
> +
> +	.long	_edata				@ SizeOfImage
> +
> +	@ Everything before the entry point is considered part of the header
> +	.long	efi_stub_entry			@ SizeOfHeaders
> +	.long	0				@ CheckSum
> +	.short	0xa				@ Subsystem (EFI application)
> +	.short	0				@ DllCharacteristics
> +	.long	0				@ SizeOfStackReserve
> +	.long	0				@ SizeOfStackCommit
> +	.long	0				@ SizeOfHeapReserve
> +	.long	0				@ SizeOfHeapCommit
> +	.long	0				@ LoaderFlags
> +	.long	0x0				@ NumberOfRvaAndSizes
> +
> +	# Section table
> +section_table:
> +
> +	#
> +	# The EFI application loader requires a relocation section
> +	# because EFI applications must be relocatable.  This is a
> +	# dummy section as far as we are concerned.
> +	#
> +	.ascii	".reloc"
> +	.byte	0
> +	.byte	0			@ end of 0 padding of section name
> +	.long	0
> +	.long	0
> +	.long	0			@ SizeOfRawData
> +	.long	0			@ PointerToRawData
> +	.long	0			@ PointerToRelocations
> +	.long	0			@ PointerToLineNumbers
> +	.short	0			@ NumberOfRelocations
> +	.short	0			@ NumberOfLineNumbers
> +	.long	0x42100040		@ Characteristics (section flags)
> +
> +
> +	.ascii	".text"
> +	.byte	0
> +	.byte	0
> +	.byte	0        		@ end of 0 padding of section name
> +	.long	_edata - efi_stub_entry		@ VirtualSize
> +	.long	efi_stub_entry			@ VirtualAddress
> +	.long	_edata - efi_stub_entry		@ SizeOfRawData
> +	.long	efi_stub_entry			@ PointerToRawData
> +
> +	.long	0		@ PointerToRelocations (0 for executables)
> +	.long	0		@ PointerToLineNumbers (0 for executables)
> +	.short	0		@ NumberOfRelocations  (0 for executables)
> +	.short	0		@ NumberOfLineNumbers  (0 for executables)
> +	.long	0xe0500020	@ Characteristics (section flags)

Can you explain why x86 needs an extra section (the .setup thing)?
I haven't dug into that in enough detail to understand it yet...

> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> new file mode 100644
> index 0000000..b817ea3
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-stub.c
> @@ -0,0 +1,514 @@
> +/*
> + * linux/arch/arm/boot/compressed/efi-stub.c
> + *
> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> + *
> + * This file implements the EFI boot stub for the ARM kernel
> + *
> + * 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 <libfdt.h>
> +
> +
> +/* Error code returned to ASM code instead of valid FDT address. */
> +#define EFI_STUB_ERROR		(~0)

Can we put that into a suitable hedaer and use it in compressed/head.S,
instead of the magic 0xffffffff?  (Assuming that value is supposed to
match EFI_STUB_ERROR)

> +
> +/* EFI function call wrappers.  These are not required for
> + * ARM, but wrappers are required for X86 to convert between
> + * ABIs.  These wrappers are provided to allow code sharing
> + * between X86 and ARM.  Since these wrappers directly invoke the
> + * EFI function pointer, the function pointer type must be properly
> + * defined, which is not the case for X86  One advantage of this is
> + * it allows for type checking of arguments, which is not
> + * possible with the X86 wrappers.
> + */
> +#define efi_call_phys0(f)			f()
> +#define efi_call_phys1(f, a1)			f(a1)
> +#define efi_call_phys2(f, a1, a2)		f(a1, a2)
> +#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
> +#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
> +
> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> + * that for the decompressed kernel.  We have no easy way to tell what
> + * the actuall size of code + data the uncompressed kernel will use.
> + */
> +#define MAX_UNCOMP_KERNEL_SIZE	0x02000000

Can we fish the decompressed data size out of zImage, like the existing
zImage code does?  (see compressed/head.S:207).  I don't see why this
needs to be compile-time constant.

Someday, someone may try to grow the kernel image beyond 32M.  It would
be nice to keep the number of things that breaks to a minimum, to ease
potential pain later.

> +
> +/* The kernel zImage should be located between 32 Mbytes
> + * and 128 MBytes from the base of DRAM.  The min
> + * address leaves space for a maximal size uncompressed image,
> + * and the max address is due to how the zImage decompressor
> + * picks a destination address.
> + */
> +#define MAX_ZIMAGE_OFFSET	0x08000000

The maximum zImage offset is actually 1 less than this.  I think it's
just the name of the macro that is misleading, since you use it
correctly as an upper bound for memory allocation, so far as I can
see.

Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.

> +#define MIN_ZIMAGE_OFFSET	MAX_UNCOMP_KERNEL_SIZE
> +
> +#define MAX_CMDLINE_LEN		500

This is a random looking number.  Is this supposed to match something
somewhere?  Does it serve any purpose other than acting as a sanity
limit?

If this limit doesn't exist, then an unreasonably large command-line
passed by EFI would just lead to a memory allocation failure somewhere,
which feels like the right behaviour...

If we can avoid building in arbitrary limits, it helps avoid surprises
later.

> +
> +struct fdt_region {
> +	u64 base;
> +	u64 size;
> +};
> +
> +/*
> + * Additional size that could be used for FDT entries added by
> + * the UEFI OS Loader Estimation based on:
> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
> + * + system memory region (20bytes) + mp_core entries (200
> + * bytes)
> + */

What does 0x300 have to do with those numbers?

When you say "estimate", are we guaranteed never to exceed that?
What happens if we do?

> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
> +
> +/* Include shared EFI stub code */
> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> +
> +
> +static int is_linux_reserved_region(int memory_type)
> +{
> +	switch (memory_type) {
> +	case EFI_RUNTIME_SERVICES_CODE:
> +	case EFI_RUNTIME_SERVICES_DATA:
> +	case EFI_UNUSABLE_MEMORY:
> +	case EFI_ACPI_RECLAIM_MEMORY:
> +	case EFI_ACPI_MEMORY_NVS:
> +		return 1;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +
> +static int relocate_kernel(efi_system_table_t *sys_table,
> +			   unsigned long *load_addr, unsigned long *load_size,
> +			   unsigned long min_addr, unsigned long max_addr)
> +{
> +	/* Get current address of kernel. */
> +	unsigned long cur_zimage_addr = *load_addr;
> +	unsigned long zimage_size = *load_size;
> +	unsigned long new_addr = 0;
> +	unsigned long nr_pages;
> +
> +	efi_status_t status;
> +
> +	if (!load_addr || !load_size)
> +		return EFI_INVALID_PARAMETER;
> +
> +	*load_size = 0;
> +	if (cur_zimage_addr > min_addr
> +	    && (cur_zimage_addr + zimage_size) < max_addr) {
> +		/* We don't need to do anything, as kernel at an acceptable
> +		 * address already.
> +		 */
> +		return EFI_SUCCESS;
> +	}
> +	/*
> +	 * The EFI firmware loader could have placed the kernel image
> +	 * anywhere in memory, but the kernel has restrictions on the
> +	 * min and max physical address it can run at.
> +	 */
> +	nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;

It looks like nr_pages is never used in this function.

> +
> +	status = efi_low_alloc(sys_table, zimage_size, 0,
> +			   &new_addr, min_addr);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to alloc memory for kernel.\n");

Does efi_printk automatically prepend a suitable prefix?  If not,
it might be useful to define a macro to add a standard prefix to all
efi_printks here ("zImage: " or similar).

Minor nit: can we have "allocate" instead of "alloc"?

I think both messages should say "failed to allocate usable memory".
EFI has already allocated memory for the kernel after all: it's
just in the wrong place initially.

> +		return status;
> +	}
> +
> +	if (new_addr > (max_addr - zimage_size)) {
> +		efi_free(sys_table, zimage_size, new_addr);
> +		efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
> +		return EFI_INVALID_PARAMETER;
> +	}
> +
> +	/* We know source/dest won't overlap since both memory ranges
> +	 * have been allocated by UEFI, so we can safely use memcpy.
> +	 */
> +	memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
> +	       zimage_size);

Is it possible for this allocation to fail -- i.e., because UEFI has
put us in an unsuitable location which is within the first 128MB of
RAM, such that we can't pick a suitable location without overlap?

For the time being though, I think this is impossible because the
decompressed Image can't exceed ~32MB (so the zImage should not
exceed that either, and both can fit inside 128MB.  It doesn't
matter if UEFI's initial load location overlaps the decompressed
Image).

If UEFI put reserved regions with the first 128MB we're likely to
be dead anyway, so we shouldn't assume we'll have to cope with that
for now...

> +
> +	/* Return the load address and size */
> +	*load_addr = new_addr;
> +	*load_size = zimage_size;

Is zimage_size ever changed?  It looks like it is still equal to the
initial value of *load_size at this point.

> +
> +
> +	return status;
> +}
> +
> +
> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
> + * Size of memory allocated return in *cmd_line_len.
> + * Returns NULL on error.
> + */
> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
> +				      efi_loaded_image_t *image,
> +				      unsigned long *cmd_line_len,
> +				      u32 max_addr)
> +{
> +	u16 *s2;
> +	u8 *s1 = NULL;
> +	unsigned long cmdline_addr = 0;
> +	int load_options_size = image->load_options_size / 2; /* ASCII */
> +	void *options = (u16 *)image->load_options;
> +	int options_size = 0;
> +	int status;
> +	int i;
> +	u16 zero = 0;
> +
> +	if (options) {
> +		s2 = options;
> +		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> +			s2++;
> +			options_size++;
> +		}
> +	}
> +
> +	if (options_size == 0) {
> +		/* No command line options, so return empty string*/
> +		options_size = 1;
> +		options = &zero;
> +	}
> +
> +	if (options_size > MAX_CMDLINE_LEN)
> +		options_size = MAX_CMDLINE_LEN;
> +
> +	options_size++;  /* NUL termination */

Do we care that options_size can now be > load_options_size?

I guess image->load_options isn't realistically going to be right at
the end of a RAM bank, so probably nothing disastrous will happen if
we read off the end of it.

It would be tidier to avoid this, though.

> +
> +	status = efi_high_alloc(sys_table, options_size, 0,
> +			    &cmdline_addr, max_addr);
> +	if (status != EFI_SUCCESS)
> +		return NULL;
> +
> +	s1 = (u8 *)(unsigned long)cmdline_addr;
> +	s2 = (u16 *)options;
> +
> +	for (i = 0; i < options_size - 1; i++)
> +		*s1++ = *s2++;
> +
> +	*s1 = '\0';
> +
> +	*cmd_line_len = options_size;
> +	return (char *)(unsigned long)cmdline_addr;
> +}
> +
> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
> +				    void *handle, unsigned long dram_base,
> +				    void *orig_fdt, u64 *orig_fdt_size,
> +				    char *cmdline_ptr,
> +				    unsigned long *cmdline_size,
> +				    u64 initrd_addr, u64 initrd_size)
> +{
> +	unsigned long new_fdt_size;
> +	unsigned long new_fdt_addr;
> +	void *fdt;
> +	int node;
> +	int status;
> +	int i;
> +	unsigned long map_size, desc_size;
> +	unsigned long mmap_key;
> +	efi_memory_desc_t *memory_map;
> +	unsigned long fdt_val;
> +
> +	new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
> +	status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
> +			    dram_base + MAX_ZIMAGE_OFFSET);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
> +		goto fail;
> +	}

There are too many error messages in this function (and elsewhere).
Many of them are only useful for debugging: for real use, the only
interesting kinds of failure for the DT which will be meaningful to the
user are "bad device tree" and "out of memory".

Also, it would be desirable to make the error messages more consistent;
currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
and more.

We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
which mean basically the same thing.

You could try wrapping fdt_setprop() with a function which tries to set
the property and prints a suitable message if it fails, without having
to put explicit efi_printks all over the place.

> +
> +
> +	fdt = (void *)new_fdt_addr;
> +	status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
> +	if (status != 0) {
> +		efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
> +		goto fail_free_new_fdt;
> +	}
> +	/* We are done with the original DTB, so free it. */
> +	efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
> +	*orig_fdt_size = 0;
> +
> +	node = fdt_subnode_offset(fdt, 0, "chosen");
> +	if (node < 0) {
> +		node = fdt_add_subnode(fdt, 0, "chosen");
> +		if (node < 0) {
> +			efi_printk(sys_table, "Error on finding 'chosen' node\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +
> +	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
> +		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
> +				     strlen(cmdline_ptr) + 1);
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new bootarg\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +	/* We are done with original command line, so free it. */
> +	efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
> +	*cmdline_size = 0;
> +
> +	/* Set intird address/end in device tree, if present */
> +	if (initrd_size != 0) {
> +		u64 initrd_image_end;
> +		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
> +		status = fdt_setprop(fdt, node, "linux,initrd-start",
> +				     &initrd_image_start, sizeof(u64));
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
> +			goto fail_free_new_fdt;
> +		}
> +		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
> +		status = fdt_setprop(fdt, node, "linux,initrd-end",
> +				     &initrd_image_end, sizeof(u64));
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +
> +	/* Update memory map in the device tree. The memory node must
> +	 * be present in the tree.*/
> +	node = fdt_subnode_offset(fdt, 0, "memory");
> +	if (node < 0) {
> +		efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
> +		goto fail_free_new_fdt;
> +	}
> +
> +	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> +				    &desc_size, &mmap_key);
> +	if (status != EFI_SUCCESS)
> +		goto fail_free_new_fdt;
> +
> +	for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> +		efi_memory_desc_t *desc;
> +		unsigned long m = (unsigned long)memory_map;
> +		desc = (efi_memory_desc_t *)(m + (i * desc_size));
> +
> +		if (is_linux_reserved_region(desc->type)) {
> +			status = fdt_add_mem_rsv(fdt, desc->phys_addr,
> +						 desc->num_pages * EFI_PAGE_SIZE);
> +			if (status != 0) {
> +				efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
> +				goto fail_free_mmap;
> +			}
> +		}
> +	}
> +
> +
> +	/* Add FDT entries for EFI runtime services in chosen node.
> +	 * We need to add the final memory map, so this is done at
> +	 * the very end.
> +	 */
> +	node = fdt_subnode_offset(fdt, 0, "chosen");
> +	fdt_val = cpu_to_fdt32((unsigned long)sys_table);
> +	status = fdt_setprop(fdt, node, "efi-system-table",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32(desc_size);
> +	status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32(map_size);
> +	status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32((unsigned long)memory_map);
> +	status = fdt_setprop(fdt, node, "efi-runtime-mmap",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
> +		goto fail_free_new_fdt;
> +	}

We have one function doing two completely different jobs here (as
documented by the name).  Can it be split?

> +
> +	/* Now we need to exit boot services.  We need the key from
> +	 * the most recent read of the memory map to do this.  We can't
> +	 * free this buffer in the normal case, but do free it when
> +	 * exit_boot_services() fails or adding the memory map to the FDT
> +	 * fails.
> +	 */
> +	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> +				handle, mmap_key);
> +
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "exit boot services failed.\n");
> +		goto fail_free_mmap;
> +	}
> +
> +	return new_fdt_addr;
> +
> +fail_free_mmap:
> +	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> +
> +fail_free_new_fdt:
> +	efi_free(sys_table, new_fdt_size, new_fdt_addr);
> +
> +fail:
> +	return 0;
> +}
> +
> +
> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> +	      unsigned long *zimage_addr)
> +{
> +	efi_loaded_image_t *image;
> +	int status;
> +	unsigned long nr_pages;
> +	const struct fdt_region *region;
> +
> +	void *fdt;
> +	int err;
> +	int node;
> +	unsigned long zimage_size = 0;
> +	unsigned long dram_base;
> +	/* addr/point and size pairs for memory management*/
> +	u64 initrd_addr;
> +	u64 initrd_size = 0;
> +	u64 fdt_addr;  /* Original DTB */
> +	u64 fdt_size = 0;
> +	u64 kernel_reserve_addr;
> +	u64 kernel_reserve_size = 0;
> +	char *cmdline_ptr;
> +	unsigned long cmdline_size = 0;
> +	unsigned long new_fdt_addr;
> +
> +	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> +
> +	/* Check if we were booted by the EFI firmware */
> +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> +		goto fail;
> +
> +	efi_printk(sys_table, "Booting Linux using EFI stub.\n");
> +
> +
> +	/* get the command line from EFI, using the LOADED_IMAGE protocol */
> +	status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +				handle, &proto, (void *)&image);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> +		goto fail;
> +	}
> +
> +	/* We are going to copy this into device tree, so we don't care where in
> +	 * memory it is.
> +	 */
> +	cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
> +					       &cmdline_size, 0xFFFFFFFF);
> +	if (!cmdline_ptr) {
> +		efi_printk(sys_table, "ERROR converting command line to ascii.\n");
> +		goto fail;
> +	}
> +
> +	/* We first load the device tree, as we need to get the base address of
> +	 * DRAM from the device tree.  The zImage, device tree, and initrd
> +	 * have address restrictions that are relative to the base of DRAM.
> +	 */
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> +				      0xffffffff, &fdt_addr, &fdt_size);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Error loading dtb blob\n");
> +		goto fail_free_cmdline;
> +	}
> +
> +	err = fdt_check_header((void *)(unsigned long)fdt_addr);
> +	if (err != 0) {
> +		efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
> +		goto fail_free_dtb;
> +	}
> +	if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
> +		efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
> +		goto fail_free_dtb;
> +
> +	}
> +
> +
> +	/* Look up the base of DRAM from the device tree.*/
> +	fdt = (void *)(u32)fdt_addr;
> +	node = fdt_subnode_offset(fdt, 0, "memory");
> +	region = fdt_getprop(fdt, node, "reg", NULL);
> +	if (region) {
> +		dram_base = fdt64_to_cpu(region->base);
> +	} else {
> +		efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Reserve memory for the uncompressed kernel image. */
> +	kernel_reserve_addr = dram_base;
> +	kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> +	nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> +	status = efi_call_phys4(sys_table->boottime->allocate_pages,
> +				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> +				nr_pages, &kernel_reserve_addr);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Relocate the zImage, if required. */
> +	zimage_size = image->image_size;
> +	status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
> +				 dram_base + MIN_ZIMAGE_OFFSET,
> +				 dram_base + MAX_ZIMAGE_OFFSET);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to relocate kernel\n");
> +		goto fail_free_kernel_reserve;
> +	}
> +
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> +				      dram_base + MAX_ZIMAGE_OFFSET,
> +				      &initrd_addr, &initrd_size);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Error loading initrd\n");
> +		goto fail_free_zimage;
> +	}
> +
> +	new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
> +						dram_base, fdt, &fdt_size,
> +						cmdline_ptr, &cmdline_size,
> +						initrd_addr, initrd_size);
> +
> +	if (new_fdt_addr == 0) {
> +		efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
> +		goto fail_free_initrd;
> +	}

Ideally, we shouldn't have one error message for two completely
different causes.

The printk could move into update_fdt_and_exit_boot() and split
into more specific cases.

> +
> +
> +	/* Now we need to return the FDT address to the calling
> +	 * assembly to this can be used as part of normal boot.
> +	 */
> +	return new_fdt_addr;
> +
> +fail_free_initrd:
> +	efi_free(sys_table, initrd_size, initrd_addr);
> +
> +fail_free_zimage:
> +	efi_free(sys_table, zimage_size, *zimage_addr);
> +
> +fail_free_kernel_reserve:
> +	efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
> +
> +fail_free_dtb:
> +	efi_free(sys_table, fdt_size, fdt_addr);
> +
> +fail_free_cmdline:
> +	efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
> +
> +fail:
> +	return EFI_STUB_ERROR;
> +}
> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
> index 75189f1..491e752 100644
> --- a/arch/arm/boot/compressed/head.S
> +++ b/arch/arm/boot/compressed/head.S
> @@ -120,21 +120,100 @@
>   */
>  		.align
>  		.arm				@ Always enter in ARM state
> +		.text
>  start:
>  		.type	start,#function
> -		.rept	7
> +#ifdef CONFIG_EFI_STUB
> +		@ Magic MSDOS signature for PE/COFF + ADD opcode
> +		.word	0x62805a4d

Did you get a chance to respond to the endianness issue I raised?

> +#else
> +		mov	r0, r0
> +#endif
> +		.rept	5
>  		mov	r0, r0
>  		.endr
> -   ARM(		mov	r0, r0		)
> -   ARM(		b	1f		)
> - THUMB(		adr	r12, BSYM(1f)	)
> - THUMB(		bx	r12		)
> +
> +		@ zimage_continue will be in ARM or thumb mode as configured
> + THUMB(		adrl	r12, BSYM(zimage_continue))
> + ARM(		adrl	r12, zimage_continue)
> +		bx	r12

Note that BSYM() can be used both in ARM and Thumb kernels.

In any case, ARM kernels cannot contain BX instructions because we still
support ARMv4 (which doesn't have it).

I'm presuming you found zimage_continue is too far away for adr here,
which is why you changed it.  Assuming that't the case, this might make
sense:

	adrl	r12, BSYM(zimage_continue)
 ARM(	mov	pc, r12	)
 THUMB(	bx	r12	)

> + THUMB(		.thumb			)

For tidiness, it's better to avoid this dangling .thumb ... move it
to just before zimage_continue instead, since efi_stub_entry has to be
ARM anyway.

>  
>  		.word	0x016f2818		@ Magic numbers to help the loader
>  		.word	start			@ absolute load/run zImage address
>  		.word	_edata			@ zImage end address
> +
> +#ifdef CONFIG_EFI_STUB
> +		@ Portions of the MSDOS file header must be at offset
> +		@ 0x3c from the start of the file.  All PE/COFF headers
> +		@ are kept contiguous for simplicity.
> +#include "efi-header.S"
> +
> +efi_stub_entry:
> +		@ The EFI stub entry point is not at a fixed address, however
> +		@ this address must be set in the PE/COFF header.
> +		@ EFI entry point is in A32 mode, switch to T32 if configured.
> + THUMB(		.arm			)

^So, you can lose .arm here too (but keep the comment -- that's valuable
info)

> + THUMB(		adr	r12, BSYM(1f)	)
> + THUMB(		bx	r12		)
>   THUMB(		.thumb			)
>  1:
> +		@ Save lr on stack for possible return to EFI firmware.
> +		@ Don't care about fp, but need 64 bit alignment....
> +		stmfd	sp!, {fp, lr}
> +
> +		@ Save args to EFI app across got fixup call
> +		stmfd	sp!, {r0, r1}

Mostly minor coding nits follow...


stmfd sp!, {r0, r1, fp, lr} ?

> +		ldmfd	sp!, {r0, r1}
> +
> +		@ allocate space on stack for return of new entry point of
> +		@ zImage, as EFI stub may copy the kernel.  Pass address
> +		@ of space in r2 - EFI stub will fill in the pointer.
> +
> +		sub	sp, #8			@ we only need 4 bytes,

I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?

kernel asm is written in the traditional syntax, which means explicit
source and destination registers for instructions like this:

	sub	sp, sp, #8

Since the EFI stub code will only be built with new toolchains it
probably doesn't matter, but it's best to be consistent for readability
purposes.

> +						@ but keep stack 8 byte aligned.
> +		mov	r2, sp
> +		@ Pass our actual runtime start address in pointer data
> +		adr	r11, LC0		@ address of LC0 at run time
> +		ldr	r12, [r11, #0]		@ address of LC0 at link time
> +
> +		sub	r3, r11, r12		@ calculate the delta offset
> +		str	r3, [r2, #0]
> +		bl	efi_entry
> +
> +		@ get new zImage entry address from stack, put into r3
> +		ldr	r3, [sp, #0]
> +		add	sp, #8  @ restore stack

	add	sp, sp, #8

> +
> +		@ Check for error return from EFI stub (0xFFFFFFFF)
> +		ldr	r1, =0xffffffff

Minor nit, but ldr= is wasteful for this.

You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
smart enough to translate this)...

> +		cmp	r0, r1

...alternatively, don't use r1 at all and do:

	cmn	r0, #1

> +		beq	efi_load_fail
> +
> +
> +		@ Save return values of efi_entry
> +		stmfd	sp!, {r0, r3}
> +		bl	cache_clean_flush
> +		bl	cache_off

Why turn the cache off?  Does that mean that EFI may launch images with
the cache enabled?

If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
off is not safe.

(Hmm, the UEFI spec seems to suggest "yes" for these questions)

> +		ldmfd   sp!, {r0, r3}
> +
> +		@ put DTB address in r2, it was returned by EFI entry
> +		mov	r2, r0
> +		ldr	r1, =0xffffffff		@ DTB machine type

mov/mvn: see above

> +		mov	r0, #0  @ r0 is 0

Useless comment: maybe say why you're doing this ("r0 is 0, as required
by the kernel boot protocol", or something like that).

> +
> +		@ Branch to (possibly) relocated zImage entry that is in r3
> +		bx	r3
> +
> +efi_load_fail:
> +		@ Return EFI_LOAD_ERROR to EFI firmware on error.
> +		@ Switch back to ARM mode for EFI is done based on
> +		@ return address on stack
> +		ldr	r0, =0x80000001
> +		ldmfd	sp!, {fp, pc}
> +#endif
> +
> +zimage_continue:
>  		mrs	r9, cpsr
>  #ifdef CONFIG_ARM_VIRT_EXT
>  		bl	__hyp_stub_install	@ get into SVC mode, reversibly
> @@ -167,7 +246,6 @@ not_angel:
>  		 * by the linker here, but it should preserve r7, r8, and r9.
>  		 */
>  
> -		.text
>  
>  #ifdef CONFIG_AUTO_ZRELADDR
>  		@ determine final kernel image address
> -- 
> 1.7.10.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07 18:05     ` Dave Martin
  0 siblings, 0 replies; 88+ messages in thread
From: Dave Martin @ 2013-08-07 18:05 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ,
	leif.lindholm-QSEj5FYQhm4dnm+yROfE0A

On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
> operations similarly to the x86 stub: it is a shim between the EFI firmware
> and the normal zImage entry point, and sets up the environment that the
> zImage is expecting.  This includes loading the initrd (optionaly) and
> device tree from the system partition based on the kernel command line.
> The stub updates the device tree as necessary, including adding reserved
> memory regions and adding entries for EFI runtime services. The PE/COFF
> "MZ" header at offset 0 results in the first instruction being an add
> that corrupts r5, which is not used by the zImage interface.

Some more comments below ... note that I haven't really looked at the C
code in depth.

Cheers
---Dave

> 
> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  arch/arm/boot/compressed/Makefile     |   18 +-
>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
>  arch/arm/boot/compressed/head.S       |   90 +++++-
>  4 files changed, 728 insertions(+), 8 deletions(-)
>  create mode 100644 arch/arm/boot/compressed/efi-header.S
>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
> 
> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> index 7ac1610..c62826a 100644
> --- a/arch/arm/boot/compressed/Makefile
> +++ b/arch/arm/boot/compressed/Makefile
> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
>  	$(addprefix $(obj)/,$(libfdt_hdrs))
>  
> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
> +	$(addprefix $(obj)/,$(libfdt_hdrs))
> +

Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?

Would it make sense just to add efi-stub.o to the list of targets in the
original rule?

>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
> -OBJS	+= $(libfdt_objs) atags_to_fdt.o
> +OBJS	+= atags_to_fdt.o
> +USE_LIBFDT = y
> +endif
> +
> +ifeq ($(CONFIG_EFI_STUB),y)
> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
> +OBJS	+= efi-stub.o
> +USE_LIBFDT = y
> +endif
> +
> +ifeq ($(USE_LIBFDT),y)
> +OBJS	+= $(libfdt_objs)
>  endif
>  
>  targets       := vmlinux vmlinux.lds \
> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
>  endif
>  
> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector

You don't appear to explain this change anywhere.

>  asflags-y := -DZIMAGE
>  
>  # Supply kernel BSS size to the decompressor via a linker symbol.
> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
> new file mode 100644
> index 0000000..6ff32cc
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-header.S
> @@ -0,0 +1,114 @@
> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> +@
> +@ This file contains the PE/COFF header that is part of the
> +@ EFI stub.
> +@
> +
> +	.org	0x3c
> +	@
> +	@ The PE header can be anywhere in the file, but for
> +	@ simplicity we keep it together with the MSDOS header
> +	@ The offset to the PE/COFF header needs to be at offset
> +	@ 0x3C in the MSDOS header.
> +	@ The only 2 fields of the MSDOS header that are used are this
> +	@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
> +	@
> +	.long	pe_header			@ Offset to the PE header.

Is there any chance of merging this with the equivalent x86 code?

The PE/COFF header is much the same in both cases, although there
are some differences.  Maybe it would be more trouble than it is
worth...
 
> +
> +      .align 3
> +pe_header:
> +
> +
> +pe_header:

Duplicate label?

> +	.ascii	"PE"
> +	.short 	0
> +
> +coff_header:
> +	.short	0x01c2				@ ARM or Thumb
> +	.short	2				@ nr_sections
> +	.long	0 				@ TimeDateStamp
> +	.long	0				@ PointerToSymbolTable
> +	.long	1				@ NumberOfSymbols
> +	.short	section_table - optional_header	@ SizeOfOptionalHeader
> +	.short	0x306				@ Characteristics.
> +						@ IMAGE_FILE_32BIT_MACHINE |
> +						@ IMAGE_FILE_DEBUG_STRIPPED |
> +						@ IMAGE_FILE_EXECUTABLE_IMAGE |
> +						@ IMAGE_FILE_LINE_NUMS_STRIPPED
> +
> +optional_header:
> +	.short	0x10b				@ PE32 format
> +	.byte	0x02				@ MajorLinkerVersion
> +	.byte	0x14				@ MinorLinkerVersion
> +
> +	.long	0				@ SizeOfCode

Do we need to fill in SizeOfCode with a real value?  It looks like x86
does.

We should probably fill this in unless there's a documented ABI for EFI
boot on ARM which explicitly doesn't require these.

> +
> +	.long	0				@ SizeOfInitializedData
> +	.long	0				@ SizeOfUninitializedData
> +
> +	.long	efi_stub_entry			@ AddressOfEntryPoint
> +	.long	efi_stub_entry			@ BaseOfCode
> +	.long	0				@ data
> +
> +extra_header_fields:
> +	.long	0				@ ImageBase
> +	.long	0x20				@ SectionAlignment
> +	.long	0x20				@ FileAlignment
> +	.short	0				@ MajorOperatingSystemVersion
> +	.short	0				@ MinorOperatingSystemVersion
> +	.short	0				@ MajorImageVersion
> +	.short	0				@ MinorImageVersion
> +	.short	0				@ MajorSubsystemVersion
> +	.short	0				@ MinorSubsystemVersion
> +	.long	0				@ Win32VersionValue
> +
> +	.long	_edata				@ SizeOfImage
> +
> +	@ Everything before the entry point is considered part of the header
> +	.long	efi_stub_entry			@ SizeOfHeaders
> +	.long	0				@ CheckSum
> +	.short	0xa				@ Subsystem (EFI application)
> +	.short	0				@ DllCharacteristics
> +	.long	0				@ SizeOfStackReserve
> +	.long	0				@ SizeOfStackCommit
> +	.long	0				@ SizeOfHeapReserve
> +	.long	0				@ SizeOfHeapCommit
> +	.long	0				@ LoaderFlags
> +	.long	0x0				@ NumberOfRvaAndSizes
> +
> +	# Section table
> +section_table:
> +
> +	#
> +	# The EFI application loader requires a relocation section
> +	# because EFI applications must be relocatable.  This is a
> +	# dummy section as far as we are concerned.
> +	#
> +	.ascii	".reloc"
> +	.byte	0
> +	.byte	0			@ end of 0 padding of section name
> +	.long	0
> +	.long	0
> +	.long	0			@ SizeOfRawData
> +	.long	0			@ PointerToRawData
> +	.long	0			@ PointerToRelocations
> +	.long	0			@ PointerToLineNumbers
> +	.short	0			@ NumberOfRelocations
> +	.short	0			@ NumberOfLineNumbers
> +	.long	0x42100040		@ Characteristics (section flags)
> +
> +
> +	.ascii	".text"
> +	.byte	0
> +	.byte	0
> +	.byte	0        		@ end of 0 padding of section name
> +	.long	_edata - efi_stub_entry		@ VirtualSize
> +	.long	efi_stub_entry			@ VirtualAddress
> +	.long	_edata - efi_stub_entry		@ SizeOfRawData
> +	.long	efi_stub_entry			@ PointerToRawData
> +
> +	.long	0		@ PointerToRelocations (0 for executables)
> +	.long	0		@ PointerToLineNumbers (0 for executables)
> +	.short	0		@ NumberOfRelocations  (0 for executables)
> +	.short	0		@ NumberOfLineNumbers  (0 for executables)
> +	.long	0xe0500020	@ Characteristics (section flags)

Can you explain why x86 needs an extra section (the .setup thing)?
I haven't dug into that in enough detail to understand it yet...

> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> new file mode 100644
> index 0000000..b817ea3
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-stub.c
> @@ -0,0 +1,514 @@
> +/*
> + * linux/arch/arm/boot/compressed/efi-stub.c
> + *
> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> + *
> + * This file implements the EFI boot stub for the ARM kernel
> + *
> + * 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 <libfdt.h>
> +
> +
> +/* Error code returned to ASM code instead of valid FDT address. */
> +#define EFI_STUB_ERROR		(~0)

Can we put that into a suitable hedaer and use it in compressed/head.S,
instead of the magic 0xffffffff?  (Assuming that value is supposed to
match EFI_STUB_ERROR)

> +
> +/* EFI function call wrappers.  These are not required for
> + * ARM, but wrappers are required for X86 to convert between
> + * ABIs.  These wrappers are provided to allow code sharing
> + * between X86 and ARM.  Since these wrappers directly invoke the
> + * EFI function pointer, the function pointer type must be properly
> + * defined, which is not the case for X86  One advantage of this is
> + * it allows for type checking of arguments, which is not
> + * possible with the X86 wrappers.
> + */
> +#define efi_call_phys0(f)			f()
> +#define efi_call_phys1(f, a1)			f(a1)
> +#define efi_call_phys2(f, a1, a2)		f(a1, a2)
> +#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
> +#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
> +
> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> + * that for the decompressed kernel.  We have no easy way to tell what
> + * the actuall size of code + data the uncompressed kernel will use.
> + */
> +#define MAX_UNCOMP_KERNEL_SIZE	0x02000000

Can we fish the decompressed data size out of zImage, like the existing
zImage code does?  (see compressed/head.S:207).  I don't see why this
needs to be compile-time constant.

Someday, someone may try to grow the kernel image beyond 32M.  It would
be nice to keep the number of things that breaks to a minimum, to ease
potential pain later.

> +
> +/* The kernel zImage should be located between 32 Mbytes
> + * and 128 MBytes from the base of DRAM.  The min
> + * address leaves space for a maximal size uncompressed image,
> + * and the max address is due to how the zImage decompressor
> + * picks a destination address.
> + */
> +#define MAX_ZIMAGE_OFFSET	0x08000000

The maximum zImage offset is actually 1 less than this.  I think it's
just the name of the macro that is misleading, since you use it
correctly as an upper bound for memory allocation, so far as I can
see.

Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.

> +#define MIN_ZIMAGE_OFFSET	MAX_UNCOMP_KERNEL_SIZE
> +
> +#define MAX_CMDLINE_LEN		500

This is a random looking number.  Is this supposed to match something
somewhere?  Does it serve any purpose other than acting as a sanity
limit?

If this limit doesn't exist, then an unreasonably large command-line
passed by EFI would just lead to a memory allocation failure somewhere,
which feels like the right behaviour...

If we can avoid building in arbitrary limits, it helps avoid surprises
later.

> +
> +struct fdt_region {
> +	u64 base;
> +	u64 size;
> +};
> +
> +/*
> + * Additional size that could be used for FDT entries added by
> + * the UEFI OS Loader Estimation based on:
> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
> + * + system memory region (20bytes) + mp_core entries (200
> + * bytes)
> + */

What does 0x300 have to do with those numbers?

When you say "estimate", are we guaranteed never to exceed that?
What happens if we do?

> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
> +
> +/* Include shared EFI stub code */
> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> +
> +
> +static int is_linux_reserved_region(int memory_type)
> +{
> +	switch (memory_type) {
> +	case EFI_RUNTIME_SERVICES_CODE:
> +	case EFI_RUNTIME_SERVICES_DATA:
> +	case EFI_UNUSABLE_MEMORY:
> +	case EFI_ACPI_RECLAIM_MEMORY:
> +	case EFI_ACPI_MEMORY_NVS:
> +		return 1;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +
> +static int relocate_kernel(efi_system_table_t *sys_table,
> +			   unsigned long *load_addr, unsigned long *load_size,
> +			   unsigned long min_addr, unsigned long max_addr)
> +{
> +	/* Get current address of kernel. */
> +	unsigned long cur_zimage_addr = *load_addr;
> +	unsigned long zimage_size = *load_size;
> +	unsigned long new_addr = 0;
> +	unsigned long nr_pages;
> +
> +	efi_status_t status;
> +
> +	if (!load_addr || !load_size)
> +		return EFI_INVALID_PARAMETER;
> +
> +	*load_size = 0;
> +	if (cur_zimage_addr > min_addr
> +	    && (cur_zimage_addr + zimage_size) < max_addr) {
> +		/* We don't need to do anything, as kernel at an acceptable
> +		 * address already.
> +		 */
> +		return EFI_SUCCESS;
> +	}
> +	/*
> +	 * The EFI firmware loader could have placed the kernel image
> +	 * anywhere in memory, but the kernel has restrictions on the
> +	 * min and max physical address it can run at.
> +	 */
> +	nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;

It looks like nr_pages is never used in this function.

> +
> +	status = efi_low_alloc(sys_table, zimage_size, 0,
> +			   &new_addr, min_addr);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to alloc memory for kernel.\n");

Does efi_printk automatically prepend a suitable prefix?  If not,
it might be useful to define a macro to add a standard prefix to all
efi_printks here ("zImage: " or similar).

Minor nit: can we have "allocate" instead of "alloc"?

I think both messages should say "failed to allocate usable memory".
EFI has already allocated memory for the kernel after all: it's
just in the wrong place initially.

> +		return status;
> +	}
> +
> +	if (new_addr > (max_addr - zimage_size)) {
> +		efi_free(sys_table, zimage_size, new_addr);
> +		efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
> +		return EFI_INVALID_PARAMETER;
> +	}
> +
> +	/* We know source/dest won't overlap since both memory ranges
> +	 * have been allocated by UEFI, so we can safely use memcpy.
> +	 */
> +	memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
> +	       zimage_size);

Is it possible for this allocation to fail -- i.e., because UEFI has
put us in an unsuitable location which is within the first 128MB of
RAM, such that we can't pick a suitable location without overlap?

For the time being though, I think this is impossible because the
decompressed Image can't exceed ~32MB (so the zImage should not
exceed that either, and both can fit inside 128MB.  It doesn't
matter if UEFI's initial load location overlaps the decompressed
Image).

If UEFI put reserved regions with the first 128MB we're likely to
be dead anyway, so we shouldn't assume we'll have to cope with that
for now...

> +
> +	/* Return the load address and size */
> +	*load_addr = new_addr;
> +	*load_size = zimage_size;

Is zimage_size ever changed?  It looks like it is still equal to the
initial value of *load_size at this point.

> +
> +
> +	return status;
> +}
> +
> +
> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
> + * Size of memory allocated return in *cmd_line_len.
> + * Returns NULL on error.
> + */
> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
> +				      efi_loaded_image_t *image,
> +				      unsigned long *cmd_line_len,
> +				      u32 max_addr)
> +{
> +	u16 *s2;
> +	u8 *s1 = NULL;
> +	unsigned long cmdline_addr = 0;
> +	int load_options_size = image->load_options_size / 2; /* ASCII */
> +	void *options = (u16 *)image->load_options;
> +	int options_size = 0;
> +	int status;
> +	int i;
> +	u16 zero = 0;
> +
> +	if (options) {
> +		s2 = options;
> +		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> +			s2++;
> +			options_size++;
> +		}
> +	}
> +
> +	if (options_size == 0) {
> +		/* No command line options, so return empty string*/
> +		options_size = 1;
> +		options = &zero;
> +	}
> +
> +	if (options_size > MAX_CMDLINE_LEN)
> +		options_size = MAX_CMDLINE_LEN;
> +
> +	options_size++;  /* NUL termination */

Do we care that options_size can now be > load_options_size?

I guess image->load_options isn't realistically going to be right at
the end of a RAM bank, so probably nothing disastrous will happen if
we read off the end of it.

It would be tidier to avoid this, though.

> +
> +	status = efi_high_alloc(sys_table, options_size, 0,
> +			    &cmdline_addr, max_addr);
> +	if (status != EFI_SUCCESS)
> +		return NULL;
> +
> +	s1 = (u8 *)(unsigned long)cmdline_addr;
> +	s2 = (u16 *)options;
> +
> +	for (i = 0; i < options_size - 1; i++)
> +		*s1++ = *s2++;
> +
> +	*s1 = '\0';
> +
> +	*cmd_line_len = options_size;
> +	return (char *)(unsigned long)cmdline_addr;
> +}
> +
> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
> +				    void *handle, unsigned long dram_base,
> +				    void *orig_fdt, u64 *orig_fdt_size,
> +				    char *cmdline_ptr,
> +				    unsigned long *cmdline_size,
> +				    u64 initrd_addr, u64 initrd_size)
> +{
> +	unsigned long new_fdt_size;
> +	unsigned long new_fdt_addr;
> +	void *fdt;
> +	int node;
> +	int status;
> +	int i;
> +	unsigned long map_size, desc_size;
> +	unsigned long mmap_key;
> +	efi_memory_desc_t *memory_map;
> +	unsigned long fdt_val;
> +
> +	new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
> +	status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
> +			    dram_base + MAX_ZIMAGE_OFFSET);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
> +		goto fail;
> +	}

There are too many error messages in this function (and elsewhere).
Many of them are only useful for debugging: for real use, the only
interesting kinds of failure for the DT which will be meaningful to the
user are "bad device tree" and "out of memory".

Also, it would be desirable to make the error messages more consistent;
currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
and more.

We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
which mean basically the same thing.

You could try wrapping fdt_setprop() with a function which tries to set
the property and prints a suitable message if it fails, without having
to put explicit efi_printks all over the place.

> +
> +
> +	fdt = (void *)new_fdt_addr;
> +	status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
> +	if (status != 0) {
> +		efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
> +		goto fail_free_new_fdt;
> +	}
> +	/* We are done with the original DTB, so free it. */
> +	efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
> +	*orig_fdt_size = 0;
> +
> +	node = fdt_subnode_offset(fdt, 0, "chosen");
> +	if (node < 0) {
> +		node = fdt_add_subnode(fdt, 0, "chosen");
> +		if (node < 0) {
> +			efi_printk(sys_table, "Error on finding 'chosen' node\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +
> +	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
> +		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
> +				     strlen(cmdline_ptr) + 1);
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new bootarg\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +	/* We are done with original command line, so free it. */
> +	efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
> +	*cmdline_size = 0;
> +
> +	/* Set intird address/end in device tree, if present */
> +	if (initrd_size != 0) {
> +		u64 initrd_image_end;
> +		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
> +		status = fdt_setprop(fdt, node, "linux,initrd-start",
> +				     &initrd_image_start, sizeof(u64));
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
> +			goto fail_free_new_fdt;
> +		}
> +		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
> +		status = fdt_setprop(fdt, node, "linux,initrd-end",
> +				     &initrd_image_end, sizeof(u64));
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +
> +	/* Update memory map in the device tree. The memory node must
> +	 * be present in the tree.*/
> +	node = fdt_subnode_offset(fdt, 0, "memory");
> +	if (node < 0) {
> +		efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
> +		goto fail_free_new_fdt;
> +	}
> +
> +	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> +				    &desc_size, &mmap_key);
> +	if (status != EFI_SUCCESS)
> +		goto fail_free_new_fdt;
> +
> +	for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> +		efi_memory_desc_t *desc;
> +		unsigned long m = (unsigned long)memory_map;
> +		desc = (efi_memory_desc_t *)(m + (i * desc_size));
> +
> +		if (is_linux_reserved_region(desc->type)) {
> +			status = fdt_add_mem_rsv(fdt, desc->phys_addr,
> +						 desc->num_pages * EFI_PAGE_SIZE);
> +			if (status != 0) {
> +				efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
> +				goto fail_free_mmap;
> +			}
> +		}
> +	}
> +
> +
> +	/* Add FDT entries for EFI runtime services in chosen node.
> +	 * We need to add the final memory map, so this is done at
> +	 * the very end.
> +	 */
> +	node = fdt_subnode_offset(fdt, 0, "chosen");
> +	fdt_val = cpu_to_fdt32((unsigned long)sys_table);
> +	status = fdt_setprop(fdt, node, "efi-system-table",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32(desc_size);
> +	status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32(map_size);
> +	status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32((unsigned long)memory_map);
> +	status = fdt_setprop(fdt, node, "efi-runtime-mmap",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
> +		goto fail_free_new_fdt;
> +	}

We have one function doing two completely different jobs here (as
documented by the name).  Can it be split?

> +
> +	/* Now we need to exit boot services.  We need the key from
> +	 * the most recent read of the memory map to do this.  We can't
> +	 * free this buffer in the normal case, but do free it when
> +	 * exit_boot_services() fails or adding the memory map to the FDT
> +	 * fails.
> +	 */
> +	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> +				handle, mmap_key);
> +
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "exit boot services failed.\n");
> +		goto fail_free_mmap;
> +	}
> +
> +	return new_fdt_addr;
> +
> +fail_free_mmap:
> +	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> +
> +fail_free_new_fdt:
> +	efi_free(sys_table, new_fdt_size, new_fdt_addr);
> +
> +fail:
> +	return 0;
> +}
> +
> +
> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> +	      unsigned long *zimage_addr)
> +{
> +	efi_loaded_image_t *image;
> +	int status;
> +	unsigned long nr_pages;
> +	const struct fdt_region *region;
> +
> +	void *fdt;
> +	int err;
> +	int node;
> +	unsigned long zimage_size = 0;
> +	unsigned long dram_base;
> +	/* addr/point and size pairs for memory management*/
> +	u64 initrd_addr;
> +	u64 initrd_size = 0;
> +	u64 fdt_addr;  /* Original DTB */
> +	u64 fdt_size = 0;
> +	u64 kernel_reserve_addr;
> +	u64 kernel_reserve_size = 0;
> +	char *cmdline_ptr;
> +	unsigned long cmdline_size = 0;
> +	unsigned long new_fdt_addr;
> +
> +	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> +
> +	/* Check if we were booted by the EFI firmware */
> +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> +		goto fail;
> +
> +	efi_printk(sys_table, "Booting Linux using EFI stub.\n");
> +
> +
> +	/* get the command line from EFI, using the LOADED_IMAGE protocol */
> +	status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +				handle, &proto, (void *)&image);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> +		goto fail;
> +	}
> +
> +	/* We are going to copy this into device tree, so we don't care where in
> +	 * memory it is.
> +	 */
> +	cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
> +					       &cmdline_size, 0xFFFFFFFF);
> +	if (!cmdline_ptr) {
> +		efi_printk(sys_table, "ERROR converting command line to ascii.\n");
> +		goto fail;
> +	}
> +
> +	/* We first load the device tree, as we need to get the base address of
> +	 * DRAM from the device tree.  The zImage, device tree, and initrd
> +	 * have address restrictions that are relative to the base of DRAM.
> +	 */
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> +				      0xffffffff, &fdt_addr, &fdt_size);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Error loading dtb blob\n");
> +		goto fail_free_cmdline;
> +	}
> +
> +	err = fdt_check_header((void *)(unsigned long)fdt_addr);
> +	if (err != 0) {
> +		efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
> +		goto fail_free_dtb;
> +	}
> +	if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
> +		efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
> +		goto fail_free_dtb;
> +
> +	}
> +
> +
> +	/* Look up the base of DRAM from the device tree.*/
> +	fdt = (void *)(u32)fdt_addr;
> +	node = fdt_subnode_offset(fdt, 0, "memory");
> +	region = fdt_getprop(fdt, node, "reg", NULL);
> +	if (region) {
> +		dram_base = fdt64_to_cpu(region->base);
> +	} else {
> +		efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Reserve memory for the uncompressed kernel image. */
> +	kernel_reserve_addr = dram_base;
> +	kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> +	nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> +	status = efi_call_phys4(sys_table->boottime->allocate_pages,
> +				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> +				nr_pages, &kernel_reserve_addr);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Relocate the zImage, if required. */
> +	zimage_size = image->image_size;
> +	status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
> +				 dram_base + MIN_ZIMAGE_OFFSET,
> +				 dram_base + MAX_ZIMAGE_OFFSET);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to relocate kernel\n");
> +		goto fail_free_kernel_reserve;
> +	}
> +
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> +				      dram_base + MAX_ZIMAGE_OFFSET,
> +				      &initrd_addr, &initrd_size);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Error loading initrd\n");
> +		goto fail_free_zimage;
> +	}
> +
> +	new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
> +						dram_base, fdt, &fdt_size,
> +						cmdline_ptr, &cmdline_size,
> +						initrd_addr, initrd_size);
> +
> +	if (new_fdt_addr == 0) {
> +		efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
> +		goto fail_free_initrd;
> +	}

Ideally, we shouldn't have one error message for two completely
different causes.

The printk could move into update_fdt_and_exit_boot() and split
into more specific cases.

> +
> +
> +	/* Now we need to return the FDT address to the calling
> +	 * assembly to this can be used as part of normal boot.
> +	 */
> +	return new_fdt_addr;
> +
> +fail_free_initrd:
> +	efi_free(sys_table, initrd_size, initrd_addr);
> +
> +fail_free_zimage:
> +	efi_free(sys_table, zimage_size, *zimage_addr);
> +
> +fail_free_kernel_reserve:
> +	efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
> +
> +fail_free_dtb:
> +	efi_free(sys_table, fdt_size, fdt_addr);
> +
> +fail_free_cmdline:
> +	efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
> +
> +fail:
> +	return EFI_STUB_ERROR;
> +}
> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
> index 75189f1..491e752 100644
> --- a/arch/arm/boot/compressed/head.S
> +++ b/arch/arm/boot/compressed/head.S
> @@ -120,21 +120,100 @@
>   */
>  		.align
>  		.arm				@ Always enter in ARM state
> +		.text
>  start:
>  		.type	start,#function
> -		.rept	7
> +#ifdef CONFIG_EFI_STUB
> +		@ Magic MSDOS signature for PE/COFF + ADD opcode
> +		.word	0x62805a4d

Did you get a chance to respond to the endianness issue I raised?

> +#else
> +		mov	r0, r0
> +#endif
> +		.rept	5
>  		mov	r0, r0
>  		.endr
> -   ARM(		mov	r0, r0		)
> -   ARM(		b	1f		)
> - THUMB(		adr	r12, BSYM(1f)	)
> - THUMB(		bx	r12		)
> +
> +		@ zimage_continue will be in ARM or thumb mode as configured
> + THUMB(		adrl	r12, BSYM(zimage_continue))
> + ARM(		adrl	r12, zimage_continue)
> +		bx	r12

Note that BSYM() can be used both in ARM and Thumb kernels.

In any case, ARM kernels cannot contain BX instructions because we still
support ARMv4 (which doesn't have it).

I'm presuming you found zimage_continue is too far away for adr here,
which is why you changed it.  Assuming that't the case, this might make
sense:

	adrl	r12, BSYM(zimage_continue)
 ARM(	mov	pc, r12	)
 THUMB(	bx	r12	)

> + THUMB(		.thumb			)

For tidiness, it's better to avoid this dangling .thumb ... move it
to just before zimage_continue instead, since efi_stub_entry has to be
ARM anyway.

>  
>  		.word	0x016f2818		@ Magic numbers to help the loader
>  		.word	start			@ absolute load/run zImage address
>  		.word	_edata			@ zImage end address
> +
> +#ifdef CONFIG_EFI_STUB
> +		@ Portions of the MSDOS file header must be at offset
> +		@ 0x3c from the start of the file.  All PE/COFF headers
> +		@ are kept contiguous for simplicity.
> +#include "efi-header.S"
> +
> +efi_stub_entry:
> +		@ The EFI stub entry point is not at a fixed address, however
> +		@ this address must be set in the PE/COFF header.
> +		@ EFI entry point is in A32 mode, switch to T32 if configured.
> + THUMB(		.arm			)

^So, you can lose .arm here too (but keep the comment -- that's valuable
info)

> + THUMB(		adr	r12, BSYM(1f)	)
> + THUMB(		bx	r12		)
>   THUMB(		.thumb			)
>  1:
> +		@ Save lr on stack for possible return to EFI firmware.
> +		@ Don't care about fp, but need 64 bit alignment....
> +		stmfd	sp!, {fp, lr}
> +
> +		@ Save args to EFI app across got fixup call
> +		stmfd	sp!, {r0, r1}

Mostly minor coding nits follow...


stmfd sp!, {r0, r1, fp, lr} ?

> +		ldmfd	sp!, {r0, r1}
> +
> +		@ allocate space on stack for return of new entry point of
> +		@ zImage, as EFI stub may copy the kernel.  Pass address
> +		@ of space in r2 - EFI stub will fill in the pointer.
> +
> +		sub	sp, #8			@ we only need 4 bytes,

I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?

kernel asm is written in the traditional syntax, which means explicit
source and destination registers for instructions like this:

	sub	sp, sp, #8

Since the EFI stub code will only be built with new toolchains it
probably doesn't matter, but it's best to be consistent for readability
purposes.

> +						@ but keep stack 8 byte aligned.
> +		mov	r2, sp
> +		@ Pass our actual runtime start address in pointer data
> +		adr	r11, LC0		@ address of LC0 at run time
> +		ldr	r12, [r11, #0]		@ address of LC0 at link time
> +
> +		sub	r3, r11, r12		@ calculate the delta offset
> +		str	r3, [r2, #0]
> +		bl	efi_entry
> +
> +		@ get new zImage entry address from stack, put into r3
> +		ldr	r3, [sp, #0]
> +		add	sp, #8  @ restore stack

	add	sp, sp, #8

> +
> +		@ Check for error return from EFI stub (0xFFFFFFFF)
> +		ldr	r1, =0xffffffff

Minor nit, but ldr= is wasteful for this.

You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
smart enough to translate this)...

> +		cmp	r0, r1

...alternatively, don't use r1 at all and do:

	cmn	r0, #1

> +		beq	efi_load_fail
> +
> +
> +		@ Save return values of efi_entry
> +		stmfd	sp!, {r0, r3}
> +		bl	cache_clean_flush
> +		bl	cache_off

Why turn the cache off?  Does that mean that EFI may launch images with
the cache enabled?

If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
off is not safe.

(Hmm, the UEFI spec seems to suggest "yes" for these questions)

> +		ldmfd   sp!, {r0, r3}
> +
> +		@ put DTB address in r2, it was returned by EFI entry
> +		mov	r2, r0
> +		ldr	r1, =0xffffffff		@ DTB machine type

mov/mvn: see above

> +		mov	r0, #0  @ r0 is 0

Useless comment: maybe say why you're doing this ("r0 is 0, as required
by the kernel boot protocol", or something like that).

> +
> +		@ Branch to (possibly) relocated zImage entry that is in r3
> +		bx	r3
> +
> +efi_load_fail:
> +		@ Return EFI_LOAD_ERROR to EFI firmware on error.
> +		@ Switch back to ARM mode for EFI is done based on
> +		@ return address on stack
> +		ldr	r0, =0x80000001
> +		ldmfd	sp!, {fp, pc}
> +#endif
> +
> +zimage_continue:
>  		mrs	r9, cpsr
>  #ifdef CONFIG_ARM_VIRT_EXT
>  		bl	__hyp_stub_install	@ get into SVC mode, reversibly
> @@ -167,7 +246,6 @@ not_angel:
>  		 * by the linker here, but it should preserve r7, r8, and r9.
>  		 */
>  
> -		.text
>  
>  #ifdef CONFIG_AUTO_ZRELADDR
>  		@ determine final kernel image address
> -- 
> 1.7.10.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07 18:05     ` Dave Martin
  0 siblings, 0 replies; 88+ messages in thread
From: Dave Martin @ 2013-08-07 18:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
> operations similarly to the x86 stub: it is a shim between the EFI firmware
> and the normal zImage entry point, and sets up the environment that the
> zImage is expecting.  This includes loading the initrd (optionaly) and
> device tree from the system partition based on the kernel command line.
> The stub updates the device tree as necessary, including adding reserved
> memory regions and adding entries for EFI runtime services. The PE/COFF
> "MZ" header at offset 0 results in the first instruction being an add
> that corrupts r5, which is not used by the zImage interface.

Some more comments below ... note that I haven't really looked at the C
code in depth.

Cheers
---Dave

> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  arch/arm/boot/compressed/Makefile     |   18 +-
>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
>  arch/arm/boot/compressed/head.S       |   90 +++++-
>  4 files changed, 728 insertions(+), 8 deletions(-)
>  create mode 100644 arch/arm/boot/compressed/efi-header.S
>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
> 
> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> index 7ac1610..c62826a 100644
> --- a/arch/arm/boot/compressed/Makefile
> +++ b/arch/arm/boot/compressed/Makefile
> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
>  	$(addprefix $(obj)/,$(libfdt_hdrs))
>  
> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
> +	$(addprefix $(obj)/,$(libfdt_hdrs))
> +

Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?

Would it make sense just to add efi-stub.o to the list of targets in the
original rule?

>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
> -OBJS	+= $(libfdt_objs) atags_to_fdt.o
> +OBJS	+= atags_to_fdt.o
> +USE_LIBFDT = y
> +endif
> +
> +ifeq ($(CONFIG_EFI_STUB),y)
> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
> +OBJS	+= efi-stub.o
> +USE_LIBFDT = y
> +endif
> +
> +ifeq ($(USE_LIBFDT),y)
> +OBJS	+= $(libfdt_objs)
>  endif
>  
>  targets       := vmlinux vmlinux.lds \
> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
>  endif
>  
> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector

You don't appear to explain this change anywhere.

>  asflags-y := -DZIMAGE
>  
>  # Supply kernel BSS size to the decompressor via a linker symbol.
> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
> new file mode 100644
> index 0000000..6ff32cc
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-header.S
> @@ -0,0 +1,114 @@
> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> +@
> +@ This file contains the PE/COFF header that is part of the
> +@ EFI stub.
> +@
> +
> +	.org	0x3c
> +	@
> +	@ The PE header can be anywhere in the file, but for
> +	@ simplicity we keep it together with the MSDOS header
> +	@ The offset to the PE/COFF header needs to be at offset
> +	@ 0x3C in the MSDOS header.
> +	@ The only 2 fields of the MSDOS header that are used are this
> +	@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
> +	@
> +	.long	pe_header			@ Offset to the PE header.

Is there any chance of merging this with the equivalent x86 code?

The PE/COFF header is much the same in both cases, although there
are some differences.  Maybe it would be more trouble than it is
worth...
 
> +
> +      .align 3
> +pe_header:
> +
> +
> +pe_header:

Duplicate label?

> +	.ascii	"PE"
> +	.short 	0
> +
> +coff_header:
> +	.short	0x01c2				@ ARM or Thumb
> +	.short	2				@ nr_sections
> +	.long	0 				@ TimeDateStamp
> +	.long	0				@ PointerToSymbolTable
> +	.long	1				@ NumberOfSymbols
> +	.short	section_table - optional_header	@ SizeOfOptionalHeader
> +	.short	0x306				@ Characteristics.
> +						@ IMAGE_FILE_32BIT_MACHINE |
> +						@ IMAGE_FILE_DEBUG_STRIPPED |
> +						@ IMAGE_FILE_EXECUTABLE_IMAGE |
> +						@ IMAGE_FILE_LINE_NUMS_STRIPPED
> +
> +optional_header:
> +	.short	0x10b				@ PE32 format
> +	.byte	0x02				@ MajorLinkerVersion
> +	.byte	0x14				@ MinorLinkerVersion
> +
> +	.long	0				@ SizeOfCode

Do we need to fill in SizeOfCode with a real value?  It looks like x86
does.

We should probably fill this in unless there's a documented ABI for EFI
boot on ARM which explicitly doesn't require these.

> +
> +	.long	0				@ SizeOfInitializedData
> +	.long	0				@ SizeOfUninitializedData
> +
> +	.long	efi_stub_entry			@ AddressOfEntryPoint
> +	.long	efi_stub_entry			@ BaseOfCode
> +	.long	0				@ data
> +
> +extra_header_fields:
> +	.long	0				@ ImageBase
> +	.long	0x20				@ SectionAlignment
> +	.long	0x20				@ FileAlignment
> +	.short	0				@ MajorOperatingSystemVersion
> +	.short	0				@ MinorOperatingSystemVersion
> +	.short	0				@ MajorImageVersion
> +	.short	0				@ MinorImageVersion
> +	.short	0				@ MajorSubsystemVersion
> +	.short	0				@ MinorSubsystemVersion
> +	.long	0				@ Win32VersionValue
> +
> +	.long	_edata				@ SizeOfImage
> +
> +	@ Everything before the entry point is considered part of the header
> +	.long	efi_stub_entry			@ SizeOfHeaders
> +	.long	0				@ CheckSum
> +	.short	0xa				@ Subsystem (EFI application)
> +	.short	0				@ DllCharacteristics
> +	.long	0				@ SizeOfStackReserve
> +	.long	0				@ SizeOfStackCommit
> +	.long	0				@ SizeOfHeapReserve
> +	.long	0				@ SizeOfHeapCommit
> +	.long	0				@ LoaderFlags
> +	.long	0x0				@ NumberOfRvaAndSizes
> +
> +	# Section table
> +section_table:
> +
> +	#
> +	# The EFI application loader requires a relocation section
> +	# because EFI applications must be relocatable.  This is a
> +	# dummy section as far as we are concerned.
> +	#
> +	.ascii	".reloc"
> +	.byte	0
> +	.byte	0			@ end of 0 padding of section name
> +	.long	0
> +	.long	0
> +	.long	0			@ SizeOfRawData
> +	.long	0			@ PointerToRawData
> +	.long	0			@ PointerToRelocations
> +	.long	0			@ PointerToLineNumbers
> +	.short	0			@ NumberOfRelocations
> +	.short	0			@ NumberOfLineNumbers
> +	.long	0x42100040		@ Characteristics (section flags)
> +
> +
> +	.ascii	".text"
> +	.byte	0
> +	.byte	0
> +	.byte	0        		@ end of 0 padding of section name
> +	.long	_edata - efi_stub_entry		@ VirtualSize
> +	.long	efi_stub_entry			@ VirtualAddress
> +	.long	_edata - efi_stub_entry		@ SizeOfRawData
> +	.long	efi_stub_entry			@ PointerToRawData
> +
> +	.long	0		@ PointerToRelocations (0 for executables)
> +	.long	0		@ PointerToLineNumbers (0 for executables)
> +	.short	0		@ NumberOfRelocations  (0 for executables)
> +	.short	0		@ NumberOfLineNumbers  (0 for executables)
> +	.long	0xe0500020	@ Characteristics (section flags)

Can you explain why x86 needs an extra section (the .setup thing)?
I haven't dug into that in enough detail to understand it yet...

> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> new file mode 100644
> index 0000000..b817ea3
> --- /dev/null
> +++ b/arch/arm/boot/compressed/efi-stub.c
> @@ -0,0 +1,514 @@
> +/*
> + * linux/arch/arm/boot/compressed/efi-stub.c
> + *
> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> + *
> + * This file implements the EFI boot stub for the ARM kernel
> + *
> + * 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 <libfdt.h>
> +
> +
> +/* Error code returned to ASM code instead of valid FDT address. */
> +#define EFI_STUB_ERROR		(~0)

Can we put that into a suitable hedaer and use it in compressed/head.S,
instead of the magic 0xffffffff?  (Assuming that value is supposed to
match EFI_STUB_ERROR)

> +
> +/* EFI function call wrappers.  These are not required for
> + * ARM, but wrappers are required for X86 to convert between
> + * ABIs.  These wrappers are provided to allow code sharing
> + * between X86 and ARM.  Since these wrappers directly invoke the
> + * EFI function pointer, the function pointer type must be properly
> + * defined, which is not the case for X86  One advantage of this is
> + * it allows for type checking of arguments, which is not
> + * possible with the X86 wrappers.
> + */
> +#define efi_call_phys0(f)			f()
> +#define efi_call_phys1(f, a1)			f(a1)
> +#define efi_call_phys2(f, a1, a2)		f(a1, a2)
> +#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
> +#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
> +
> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> + * that for the decompressed kernel.  We have no easy way to tell what
> + * the actuall size of code + data the uncompressed kernel will use.
> + */
> +#define MAX_UNCOMP_KERNEL_SIZE	0x02000000

Can we fish the decompressed data size out of zImage, like the existing
zImage code does?  (see compressed/head.S:207).  I don't see why this
needs to be compile-time constant.

Someday, someone may try to grow the kernel image beyond 32M.  It would
be nice to keep the number of things that breaks to a minimum, to ease
potential pain later.

> +
> +/* The kernel zImage should be located between 32 Mbytes
> + * and 128 MBytes from the base of DRAM.  The min
> + * address leaves space for a maximal size uncompressed image,
> + * and the max address is due to how the zImage decompressor
> + * picks a destination address.
> + */
> +#define MAX_ZIMAGE_OFFSET	0x08000000

The maximum zImage offset is actually 1 less than this.  I think it's
just the name of the macro that is misleading, since you use it
correctly as an upper bound for memory allocation, so far as I can
see.

Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.

> +#define MIN_ZIMAGE_OFFSET	MAX_UNCOMP_KERNEL_SIZE
> +
> +#define MAX_CMDLINE_LEN		500

This is a random looking number.  Is this supposed to match something
somewhere?  Does it serve any purpose other than acting as a sanity
limit?

If this limit doesn't exist, then an unreasonably large command-line
passed by EFI would just lead to a memory allocation failure somewhere,
which feels like the right behaviour...

If we can avoid building in arbitrary limits, it helps avoid surprises
later.

> +
> +struct fdt_region {
> +	u64 base;
> +	u64 size;
> +};
> +
> +/*
> + * Additional size that could be used for FDT entries added by
> + * the UEFI OS Loader Estimation based on:
> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
> + * + system memory region (20bytes) + mp_core entries (200
> + * bytes)
> + */

What does 0x300 have to do with those numbers?

When you say "estimate", are we guaranteed never to exceed that?
What happens if we do?

> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
> +
> +/* Include shared EFI stub code */
> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> +
> +
> +static int is_linux_reserved_region(int memory_type)
> +{
> +	switch (memory_type) {
> +	case EFI_RUNTIME_SERVICES_CODE:
> +	case EFI_RUNTIME_SERVICES_DATA:
> +	case EFI_UNUSABLE_MEMORY:
> +	case EFI_ACPI_RECLAIM_MEMORY:
> +	case EFI_ACPI_MEMORY_NVS:
> +		return 1;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +
> +static int relocate_kernel(efi_system_table_t *sys_table,
> +			   unsigned long *load_addr, unsigned long *load_size,
> +			   unsigned long min_addr, unsigned long max_addr)
> +{
> +	/* Get current address of kernel. */
> +	unsigned long cur_zimage_addr = *load_addr;
> +	unsigned long zimage_size = *load_size;
> +	unsigned long new_addr = 0;
> +	unsigned long nr_pages;
> +
> +	efi_status_t status;
> +
> +	if (!load_addr || !load_size)
> +		return EFI_INVALID_PARAMETER;
> +
> +	*load_size = 0;
> +	if (cur_zimage_addr > min_addr
> +	    && (cur_zimage_addr + zimage_size) < max_addr) {
> +		/* We don't need to do anything, as kernel at an acceptable
> +		 * address already.
> +		 */
> +		return EFI_SUCCESS;
> +	}
> +	/*
> +	 * The EFI firmware loader could have placed the kernel image
> +	 * anywhere in memory, but the kernel has restrictions on the
> +	 * min and max physical address it can run at.
> +	 */
> +	nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;

It looks like nr_pages is never used in this function.

> +
> +	status = efi_low_alloc(sys_table, zimage_size, 0,
> +			   &new_addr, min_addr);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to alloc memory for kernel.\n");

Does efi_printk automatically prepend a suitable prefix?  If not,
it might be useful to define a macro to add a standard prefix to all
efi_printks here ("zImage: " or similar).

Minor nit: can we have "allocate" instead of "alloc"?

I think both messages should say "failed to allocate usable memory".
EFI has already allocated memory for the kernel after all: it's
just in the wrong place initially.

> +		return status;
> +	}
> +
> +	if (new_addr > (max_addr - zimage_size)) {
> +		efi_free(sys_table, zimage_size, new_addr);
> +		efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
> +		return EFI_INVALID_PARAMETER;
> +	}
> +
> +	/* We know source/dest won't overlap since both memory ranges
> +	 * have been allocated by UEFI, so we can safely use memcpy.
> +	 */
> +	memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
> +	       zimage_size);

Is it possible for this allocation to fail -- i.e., because UEFI has
put us in an unsuitable location which is within the first 128MB of
RAM, such that we can't pick a suitable location without overlap?

For the time being though, I think this is impossible because the
decompressed Image can't exceed ~32MB (so the zImage should not
exceed that either, and both can fit inside 128MB.  It doesn't
matter if UEFI's initial load location overlaps the decompressed
Image).

If UEFI put reserved regions with the first 128MB we're likely to
be dead anyway, so we shouldn't assume we'll have to cope with that
for now...

> +
> +	/* Return the load address and size */
> +	*load_addr = new_addr;
> +	*load_size = zimage_size;

Is zimage_size ever changed?  It looks like it is still equal to the
initial value of *load_size at this point.

> +
> +
> +	return status;
> +}
> +
> +
> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
> + * Size of memory allocated return in *cmd_line_len.
> + * Returns NULL on error.
> + */
> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
> +				      efi_loaded_image_t *image,
> +				      unsigned long *cmd_line_len,
> +				      u32 max_addr)
> +{
> +	u16 *s2;
> +	u8 *s1 = NULL;
> +	unsigned long cmdline_addr = 0;
> +	int load_options_size = image->load_options_size / 2; /* ASCII */
> +	void *options = (u16 *)image->load_options;
> +	int options_size = 0;
> +	int status;
> +	int i;
> +	u16 zero = 0;
> +
> +	if (options) {
> +		s2 = options;
> +		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> +			s2++;
> +			options_size++;
> +		}
> +	}
> +
> +	if (options_size == 0) {
> +		/* No command line options, so return empty string*/
> +		options_size = 1;
> +		options = &zero;
> +	}
> +
> +	if (options_size > MAX_CMDLINE_LEN)
> +		options_size = MAX_CMDLINE_LEN;
> +
> +	options_size++;  /* NUL termination */

Do we care that options_size can now be > load_options_size?

I guess image->load_options isn't realistically going to be right at
the end of a RAM bank, so probably nothing disastrous will happen if
we read off the end of it.

It would be tidier to avoid this, though.

> +
> +	status = efi_high_alloc(sys_table, options_size, 0,
> +			    &cmdline_addr, max_addr);
> +	if (status != EFI_SUCCESS)
> +		return NULL;
> +
> +	s1 = (u8 *)(unsigned long)cmdline_addr;
> +	s2 = (u16 *)options;
> +
> +	for (i = 0; i < options_size - 1; i++)
> +		*s1++ = *s2++;
> +
> +	*s1 = '\0';
> +
> +	*cmd_line_len = options_size;
> +	return (char *)(unsigned long)cmdline_addr;
> +}
> +
> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
> +				    void *handle, unsigned long dram_base,
> +				    void *orig_fdt, u64 *orig_fdt_size,
> +				    char *cmdline_ptr,
> +				    unsigned long *cmdline_size,
> +				    u64 initrd_addr, u64 initrd_size)
> +{
> +	unsigned long new_fdt_size;
> +	unsigned long new_fdt_addr;
> +	void *fdt;
> +	int node;
> +	int status;
> +	int i;
> +	unsigned long map_size, desc_size;
> +	unsigned long mmap_key;
> +	efi_memory_desc_t *memory_map;
> +	unsigned long fdt_val;
> +
> +	new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
> +	status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
> +			    dram_base + MAX_ZIMAGE_OFFSET);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
> +		goto fail;
> +	}

There are too many error messages in this function (and elsewhere).
Many of them are only useful for debugging: for real use, the only
interesting kinds of failure for the DT which will be meaningful to the
user are "bad device tree" and "out of memory".

Also, it would be desirable to make the error messages more consistent;
currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
and more.

We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
which mean basically the same thing.

You could try wrapping fdt_setprop() with a function which tries to set
the property and prints a suitable message if it fails, without having
to put explicit efi_printks all over the place.

> +
> +
> +	fdt = (void *)new_fdt_addr;
> +	status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
> +	if (status != 0) {
> +		efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
> +		goto fail_free_new_fdt;
> +	}
> +	/* We are done with the original DTB, so free it. */
> +	efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
> +	*orig_fdt_size = 0;
> +
> +	node = fdt_subnode_offset(fdt, 0, "chosen");
> +	if (node < 0) {
> +		node = fdt_add_subnode(fdt, 0, "chosen");
> +		if (node < 0) {
> +			efi_printk(sys_table, "Error on finding 'chosen' node\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +
> +	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
> +		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
> +				     strlen(cmdline_ptr) + 1);
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new bootarg\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +	/* We are done with original command line, so free it. */
> +	efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
> +	*cmdline_size = 0;
> +
> +	/* Set intird address/end in device tree, if present */
> +	if (initrd_size != 0) {
> +		u64 initrd_image_end;
> +		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
> +		status = fdt_setprop(fdt, node, "linux,initrd-start",
> +				     &initrd_image_start, sizeof(u64));
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
> +			goto fail_free_new_fdt;
> +		}
> +		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
> +		status = fdt_setprop(fdt, node, "linux,initrd-end",
> +				     &initrd_image_end, sizeof(u64));
> +		if (status) {
> +			efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
> +			goto fail_free_new_fdt;
> +		}
> +	}
> +
> +	/* Update memory map in the device tree. The memory node must
> +	 * be present in the tree.*/
> +	node = fdt_subnode_offset(fdt, 0, "memory");
> +	if (node < 0) {
> +		efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
> +		goto fail_free_new_fdt;
> +	}
> +
> +	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> +				    &desc_size, &mmap_key);
> +	if (status != EFI_SUCCESS)
> +		goto fail_free_new_fdt;
> +
> +	for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> +		efi_memory_desc_t *desc;
> +		unsigned long m = (unsigned long)memory_map;
> +		desc = (efi_memory_desc_t *)(m + (i * desc_size));
> +
> +		if (is_linux_reserved_region(desc->type)) {
> +			status = fdt_add_mem_rsv(fdt, desc->phys_addr,
> +						 desc->num_pages * EFI_PAGE_SIZE);
> +			if (status != 0) {
> +				efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
> +				goto fail_free_mmap;
> +			}
> +		}
> +	}
> +
> +
> +	/* Add FDT entries for EFI runtime services in chosen node.
> +	 * We need to add the final memory map, so this is done at
> +	 * the very end.
> +	 */
> +	node = fdt_subnode_offset(fdt, 0, "chosen");
> +	fdt_val = cpu_to_fdt32((unsigned long)sys_table);
> +	status = fdt_setprop(fdt, node, "efi-system-table",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32(desc_size);
> +	status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32(map_size);
> +	status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
> +		goto fail_free_new_fdt;
> +	}
> +	fdt_val = cpu_to_fdt32((unsigned long)memory_map);
> +	status = fdt_setprop(fdt, node, "efi-runtime-mmap",
> +			     &fdt_val, sizeof(fdt_val));
> +	if (status) {
> +		efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
> +		goto fail_free_new_fdt;
> +	}

We have one function doing two completely different jobs here (as
documented by the name).  Can it be split?

> +
> +	/* Now we need to exit boot services.  We need the key from
> +	 * the most recent read of the memory map to do this.  We can't
> +	 * free this buffer in the normal case, but do free it when
> +	 * exit_boot_services() fails or adding the memory map to the FDT
> +	 * fails.
> +	 */
> +	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> +				handle, mmap_key);
> +
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "exit boot services failed.\n");
> +		goto fail_free_mmap;
> +	}
> +
> +	return new_fdt_addr;
> +
> +fail_free_mmap:
> +	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> +
> +fail_free_new_fdt:
> +	efi_free(sys_table, new_fdt_size, new_fdt_addr);
> +
> +fail:
> +	return 0;
> +}
> +
> +
> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> +	      unsigned long *zimage_addr)
> +{
> +	efi_loaded_image_t *image;
> +	int status;
> +	unsigned long nr_pages;
> +	const struct fdt_region *region;
> +
> +	void *fdt;
> +	int err;
> +	int node;
> +	unsigned long zimage_size = 0;
> +	unsigned long dram_base;
> +	/* addr/point and size pairs for memory management*/
> +	u64 initrd_addr;
> +	u64 initrd_size = 0;
> +	u64 fdt_addr;  /* Original DTB */
> +	u64 fdt_size = 0;
> +	u64 kernel_reserve_addr;
> +	u64 kernel_reserve_size = 0;
> +	char *cmdline_ptr;
> +	unsigned long cmdline_size = 0;
> +	unsigned long new_fdt_addr;
> +
> +	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> +
> +	/* Check if we were booted by the EFI firmware */
> +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> +		goto fail;
> +
> +	efi_printk(sys_table, "Booting Linux using EFI stub.\n");
> +
> +
> +	/* get the command line from EFI, using the LOADED_IMAGE protocol */
> +	status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +				handle, &proto, (void *)&image);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> +		goto fail;
> +	}
> +
> +	/* We are going to copy this into device tree, so we don't care where in
> +	 * memory it is.
> +	 */
> +	cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
> +					       &cmdline_size, 0xFFFFFFFF);
> +	if (!cmdline_ptr) {
> +		efi_printk(sys_table, "ERROR converting command line to ascii.\n");
> +		goto fail;
> +	}
> +
> +	/* We first load the device tree, as we need to get the base address of
> +	 * DRAM from the device tree.  The zImage, device tree, and initrd
> +	 * have address restrictions that are relative to the base of DRAM.
> +	 */
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> +				      0xffffffff, &fdt_addr, &fdt_size);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Error loading dtb blob\n");
> +		goto fail_free_cmdline;
> +	}
> +
> +	err = fdt_check_header((void *)(unsigned long)fdt_addr);
> +	if (err != 0) {
> +		efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
> +		goto fail_free_dtb;
> +	}
> +	if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
> +		efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
> +		goto fail_free_dtb;
> +
> +	}
> +
> +
> +	/* Look up the base of DRAM from the device tree.*/
> +	fdt = (void *)(u32)fdt_addr;
> +	node = fdt_subnode_offset(fdt, 0, "memory");
> +	region = fdt_getprop(fdt, node, "reg", NULL);
> +	if (region) {
> +		dram_base = fdt64_to_cpu(region->base);
> +	} else {
> +		efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Reserve memory for the uncompressed kernel image. */
> +	kernel_reserve_addr = dram_base;
> +	kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> +	nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> +	status = efi_call_phys4(sys_table->boottime->allocate_pages,
> +				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> +				nr_pages, &kernel_reserve_addr);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Relocate the zImage, if required. */
> +	zimage_size = image->image_size;
> +	status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
> +				 dram_base + MIN_ZIMAGE_OFFSET,
> +				 dram_base + MAX_ZIMAGE_OFFSET);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Failed to relocate kernel\n");
> +		goto fail_free_kernel_reserve;
> +	}
> +
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> +				      dram_base + MAX_ZIMAGE_OFFSET,
> +				      &initrd_addr, &initrd_size);
> +	if (status != EFI_SUCCESS) {
> +		efi_printk(sys_table, "Error loading initrd\n");
> +		goto fail_free_zimage;
> +	}
> +
> +	new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
> +						dram_base, fdt, &fdt_size,
> +						cmdline_ptr, &cmdline_size,
> +						initrd_addr, initrd_size);
> +
> +	if (new_fdt_addr == 0) {
> +		efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
> +		goto fail_free_initrd;
> +	}

Ideally, we shouldn't have one error message for two completely
different causes.

The printk could move into update_fdt_and_exit_boot() and split
into more specific cases.

> +
> +
> +	/* Now we need to return the FDT address to the calling
> +	 * assembly to this can be used as part of normal boot.
> +	 */
> +	return new_fdt_addr;
> +
> +fail_free_initrd:
> +	efi_free(sys_table, initrd_size, initrd_addr);
> +
> +fail_free_zimage:
> +	efi_free(sys_table, zimage_size, *zimage_addr);
> +
> +fail_free_kernel_reserve:
> +	efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
> +
> +fail_free_dtb:
> +	efi_free(sys_table, fdt_size, fdt_addr);
> +
> +fail_free_cmdline:
> +	efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
> +
> +fail:
> +	return EFI_STUB_ERROR;
> +}
> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
> index 75189f1..491e752 100644
> --- a/arch/arm/boot/compressed/head.S
> +++ b/arch/arm/boot/compressed/head.S
> @@ -120,21 +120,100 @@
>   */
>  		.align
>  		.arm				@ Always enter in ARM state
> +		.text
>  start:
>  		.type	start,#function
> -		.rept	7
> +#ifdef CONFIG_EFI_STUB
> +		@ Magic MSDOS signature for PE/COFF + ADD opcode
> +		.word	0x62805a4d

Did you get a chance to respond to the endianness issue I raised?

> +#else
> +		mov	r0, r0
> +#endif
> +		.rept	5
>  		mov	r0, r0
>  		.endr
> -   ARM(		mov	r0, r0		)
> -   ARM(		b	1f		)
> - THUMB(		adr	r12, BSYM(1f)	)
> - THUMB(		bx	r12		)
> +
> +		@ zimage_continue will be in ARM or thumb mode as configured
> + THUMB(		adrl	r12, BSYM(zimage_continue))
> + ARM(		adrl	r12, zimage_continue)
> +		bx	r12

Note that BSYM() can be used both in ARM and Thumb kernels.

In any case, ARM kernels cannot contain BX instructions because we still
support ARMv4 (which doesn't have it).

I'm presuming you found zimage_continue is too far away for adr here,
which is why you changed it.  Assuming that't the case, this might make
sense:

	adrl	r12, BSYM(zimage_continue)
 ARM(	mov	pc, r12	)
 THUMB(	bx	r12	)

> + THUMB(		.thumb			)

For tidiness, it's better to avoid this dangling .thumb ... move it
to just before zimage_continue instead, since efi_stub_entry has to be
ARM anyway.

>  
>  		.word	0x016f2818		@ Magic numbers to help the loader
>  		.word	start			@ absolute load/run zImage address
>  		.word	_edata			@ zImage end address
> +
> +#ifdef CONFIG_EFI_STUB
> +		@ Portions of the MSDOS file header must be at offset
> +		@ 0x3c from the start of the file.  All PE/COFF headers
> +		@ are kept contiguous for simplicity.
> +#include "efi-header.S"
> +
> +efi_stub_entry:
> +		@ The EFI stub entry point is not at a fixed address, however
> +		@ this address must be set in the PE/COFF header.
> +		@ EFI entry point is in A32 mode, switch to T32 if configured.
> + THUMB(		.arm			)

^So, you can lose .arm here too (but keep the comment -- that's valuable
info)

> + THUMB(		adr	r12, BSYM(1f)	)
> + THUMB(		bx	r12		)
>   THUMB(		.thumb			)
>  1:
> +		@ Save lr on stack for possible return to EFI firmware.
> +		@ Don't care about fp, but need 64 bit alignment....
> +		stmfd	sp!, {fp, lr}
> +
> +		@ Save args to EFI app across got fixup call
> +		stmfd	sp!, {r0, r1}

Mostly minor coding nits follow...


stmfd sp!, {r0, r1, fp, lr} ?

> +		ldmfd	sp!, {r0, r1}
> +
> +		@ allocate space on stack for return of new entry point of
> +		@ zImage, as EFI stub may copy the kernel.  Pass address
> +		@ of space in r2 - EFI stub will fill in the pointer.
> +
> +		sub	sp, #8			@ we only need 4 bytes,

I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?

kernel asm is written in the traditional syntax, which means explicit
source and destination registers for instructions like this:

	sub	sp, sp, #8

Since the EFI stub code will only be built with new toolchains it
probably doesn't matter, but it's best to be consistent for readability
purposes.

> +						@ but keep stack 8 byte aligned.
> +		mov	r2, sp
> +		@ Pass our actual runtime start address in pointer data
> +		adr	r11, LC0		@ address of LC0 at run time
> +		ldr	r12, [r11, #0]		@ address of LC0 at link time
> +
> +		sub	r3, r11, r12		@ calculate the delta offset
> +		str	r3, [r2, #0]
> +		bl	efi_entry
> +
> +		@ get new zImage entry address from stack, put into r3
> +		ldr	r3, [sp, #0]
> +		add	sp, #8  @ restore stack

	add	sp, sp, #8

> +
> +		@ Check for error return from EFI stub (0xFFFFFFFF)
> +		ldr	r1, =0xffffffff

Minor nit, but ldr= is wasteful for this.

You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
smart enough to translate this)...

> +		cmp	r0, r1

...alternatively, don't use r1 at all and do:

	cmn	r0, #1

> +		beq	efi_load_fail
> +
> +
> +		@ Save return values of efi_entry
> +		stmfd	sp!, {r0, r3}
> +		bl	cache_clean_flush
> +		bl	cache_off

Why turn the cache off?  Does that mean that EFI may launch images with
the cache enabled?

If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
off is not safe.

(Hmm, the UEFI spec seems to suggest "yes" for these questions)

> +		ldmfd   sp!, {r0, r3}
> +
> +		@ put DTB address in r2, it was returned by EFI entry
> +		mov	r2, r0
> +		ldr	r1, =0xffffffff		@ DTB machine type

mov/mvn: see above

> +		mov	r0, #0  @ r0 is 0

Useless comment: maybe say why you're doing this ("r0 is 0, as required
by the kernel boot protocol", or something like that).

> +
> +		@ Branch to (possibly) relocated zImage entry that is in r3
> +		bx	r3
> +
> +efi_load_fail:
> +		@ Return EFI_LOAD_ERROR to EFI firmware on error.
> +		@ Switch back to ARM mode for EFI is done based on
> +		@ return address on stack
> +		ldr	r0, =0x80000001
> +		ldmfd	sp!, {fp, pc}
> +#endif
> +
> +zimage_continue:
>  		mrs	r9, cpsr
>  #ifdef CONFIG_ARM_VIRT_EXT
>  		bl	__hyp_stub_install	@ get into SVC mode, reversibly
> @@ -167,7 +246,6 @@ not_angel:
>  		 * by the linker here, but it should preserve r7, r8, and r9.
>  		 */
>  
> -		.text
>  
>  #ifdef CONFIG_AUTO_ZRELADDR
>  		@ determine final kernel image address
> -- 
> 1.7.10.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
  2013-08-07 18:05     ` Dave Martin
@ 2013-08-07 18:33       ` Leif Lindholm
  -1 siblings, 0 replies; 88+ messages in thread
From: Leif Lindholm @ 2013-08-07 18:33 UTC (permalink / raw)
  To: Dave Martin
  Cc: Roy Franz, linux-kernel, linux-efi, linux-arm-kernel,
	matt.fleming, linux

On Wed, Aug 07, 2013 at 07:05:54PM +0100, Dave Martin wrote:
> Is it possible for this allocation to fail -- i.e., because UEFI has
> put us in an unsuitable location which is within the first 128MB of
> RAM, such that we can't pick a suitable location without overlap?
> 
> For the time being though, I think this is impossible because the
> decompressed Image can't exceed ~32MB (so the zImage should not
> exceed that either, and both can fit inside 128MB.  It doesn't
> matter if UEFI's initial load location overlaps the decompressed
> Image).
> 
> If UEFI put reserved regions with the first 128MB we're likely to
> be dead anyway, so we shouldn't assume we'll have to cope with that
> for now...

There is no reason we would be dead unless we end up not being able
to allocate regions for FDT and initrd within range.

There could be some boot services region lying around, that will
be reusable before we even jump to the decompressor. Or there could
be some badly placed runtime code lying where it can safely remain.

Are there additional kernel regions to worry about?

> > +		@ Save return values of efi_entry
> > +		stmfd	sp!, {r0, r3}
> > +		bl	cache_clean_flush
> > +		bl	cache_off
> 
> Why turn the cache off?  Does that mean that EFI may launch images with
> the cache enabled?
 
Yup.

> If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> off is not safe.

Yes, VA=PA is guaranteed.
 
> (Hmm, the UEFI spec seems to suggest "yes" for these questions)
 
:)

/
    Leif

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-07 18:33       ` Leif Lindholm
  0 siblings, 0 replies; 88+ messages in thread
From: Leif Lindholm @ 2013-08-07 18:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 07, 2013 at 07:05:54PM +0100, Dave Martin wrote:
> Is it possible for this allocation to fail -- i.e., because UEFI has
> put us in an unsuitable location which is within the first 128MB of
> RAM, such that we can't pick a suitable location without overlap?
> 
> For the time being though, I think this is impossible because the
> decompressed Image can't exceed ~32MB (so the zImage should not
> exceed that either, and both can fit inside 128MB.  It doesn't
> matter if UEFI's initial load location overlaps the decompressed
> Image).
> 
> If UEFI put reserved regions with the first 128MB we're likely to
> be dead anyway, so we shouldn't assume we'll have to cope with that
> for now...

There is no reason we would be dead unless we end up not being able
to allocate regions for FDT and initrd within range.

There could be some boot services region lying around, that will
be reusable before we even jump to the decompressor. Or there could
be some badly placed runtime code lying where it can safely remain.

Are there additional kernel regions to worry about?

> > +		@ Save return values of efi_entry
> > +		stmfd	sp!, {r0, r3}
> > +		bl	cache_clean_flush
> > +		bl	cache_off
> 
> Why turn the cache off?  Does that mean that EFI may launch images with
> the cache enabled?
 
Yup.

> If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> off is not safe.

Yes, VA=PA is guaranteed.
 
> (Hmm, the UEFI spec seems to suggest "yes" for these questions)
 
:)

/
    Leif

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
  2013-08-07 17:10       ` Roy Franz
@ 2013-08-07 21:55         ` Matt Fleming
  -1 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 21:55 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

On Wed, 07 Aug, at 10:10:54AM, Roy Franz wrote:
> I went this way since the shared code is in a separate file - I really
> didn't like using a global variable as part of the interface to
> the shared code.  This has the nice side benefit of allowing the ARM
> stub to not use any global variables, so we don't have to do
> any GOT fixups to relocate the code - it is position independent if we
> don't use global variables.

OK, that would be a win. Please include this rationale in the patch.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c.
@ 2013-08-07 21:55         ` Matt Fleming
  0 siblings, 0 replies; 88+ messages in thread
From: Matt Fleming @ 2013-08-07 21:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 07 Aug, at 10:10:54AM, Roy Franz wrote:
> I went this way since the shared code is in a separate file - I really
> didn't like using a global variable as part of the interface to
> the shared code.  This has the nice side benefit of allowing the ARM
> stub to not use any global variables, so we don't have to do
> any GOT fixups to relocate the code - it is position independent if we
> don't use global variables.

OK, that would be a win. Please include this rationale in the patch.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-08 21:57       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-08 21:57 UTC (permalink / raw)
  To: Dave Martin
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm

On Wed, Aug 7, 2013 at 11:05 AM, Dave Martin <Dave.Martin@arm.com> wrote:
> On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
>> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
>> operations similarly to the x86 stub: it is a shim between the EFI firmware
>> and the normal zImage entry point, and sets up the environment that the
>> zImage is expecting.  This includes loading the initrd (optionaly) and
>> device tree from the system partition based on the kernel command line.
>> The stub updates the device tree as necessary, including adding reserved
>> memory regions and adding entries for EFI runtime services. The PE/COFF
>> "MZ" header at offset 0 results in the first instruction being an add
>> that corrupts r5, which is not used by the zImage interface.
>
> Some more comments below ... note that I haven't really looked at the C
> code in depth.

Responses below, and I'm working on incorporating suggested changes
for the next version.

Thanks,
Roy

>
> Cheers
> ---Dave
>
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/arm/boot/compressed/Makefile     |   18 +-
>>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
>>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
>>  arch/arm/boot/compressed/head.S       |   90 +++++-
>>  4 files changed, 728 insertions(+), 8 deletions(-)
>>  create mode 100644 arch/arm/boot/compressed/efi-header.S
>>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
>>
>> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
>> index 7ac1610..c62826a 100644
>> --- a/arch/arm/boot/compressed/Makefile
>> +++ b/arch/arm/boot/compressed/Makefile
>> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
>>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
>>       $(addprefix $(obj)/,$(libfdt_hdrs))
>>
>> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
>> +     $(addprefix $(obj)/,$(libfdt_hdrs))
>> +
>
> Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?
>
> Would it make sense just to add efi-stub.o to the list of targets in the
> original rule?

Yes, change made.
>
>>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
>> -OBJS += $(libfdt_objs) atags_to_fdt.o
>> +OBJS += atags_to_fdt.o
>> +USE_LIBFDT = y
>> +endif
>> +
>> +ifeq ($(CONFIG_EFI_STUB),y)
>> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
>> +OBJS += efi-stub.o
>> +USE_LIBFDT = y
>> +endif
>> +
>> +ifeq ($(USE_LIBFDT),y)
>> +OBJS += $(libfdt_objs)
>>  endif
>>
>>  targets       := vmlinux vmlinux.lds \
>> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
>>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
>>  endif
>>
>> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
>> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
>
> You don't appear to explain this change anywhere.

Prior to my changes, even though the stack protector was not disabled,
it was not actually used. GCC uses a heuristic
based on the size of the stack whether to enable the stack protector,
and the threshold to trigger its use was not met, so no stack checking
was actually being done.  In order to do stack protection, a few
__stack_chk_* functions/variable need to be provided by the
application.  I worked a bit on adding these, but could not get them
working in the stub/decompressor.  The x86 arch also has
"-fno-stack-protector" defined for its compressed boot stub, so I
decided to go that route as well.

>
>>  asflags-y := -DZIMAGE
>>
>>  # Supply kernel BSS size to the decompressor via a linker symbol.
>> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
>> new file mode 100644
>> index 0000000..6ff32cc
>> --- /dev/null
>> +++ b/arch/arm/boot/compressed/efi-header.S
>> @@ -0,0 +1,114 @@
>> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
>> +@
>> +@ This file contains the PE/COFF header that is part of the
>> +@ EFI stub.
>> +@
>> +
>> +     .org    0x3c
>> +     @
>> +     @ The PE header can be anywhere in the file, but for
>> +     @ simplicity we keep it together with the MSDOS header
>> +     @ The offset to the PE/COFF header needs to be at offset
>> +     @ 0x3C in the MSDOS header.
>> +     @ The only 2 fields of the MSDOS header that are used are this
>> +     @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
>> +     @
>> +     .long   pe_header                       @ Offset to the PE header.
>
> Is there any chance of merging this with the equivalent x86 code?
>
> The PE/COFF header is much the same in both cases, although there
> are some differences.  Maybe it would be more trouble than it is
> worth...

I think it would be more pain than gain.  We are planning to add arm64 stub
support next, so we'd end up with 4 architectures sharing this assembly file,
which I think would be painful from a patch submission/review point of view.

>
>> +
>> +      .align 3
>> +pe_header:
>> +
>> +
>> +pe_header:
>
> Duplicate label?

Yup, fixed.
>
>> +     .ascii  "PE"
>> +     .short  0
>> +
>> +coff_header:
>> +     .short  0x01c2                          @ ARM or Thumb
>> +     .short  2                               @ nr_sections
>> +     .long   0                               @ TimeDateStamp
>> +     .long   0                               @ PointerToSymbolTable
>> +     .long   1                               @ NumberOfSymbols
>> +     .short  section_table - optional_header @ SizeOfOptionalHeader
>> +     .short  0x306                           @ Characteristics.
>> +                                             @ IMAGE_FILE_32BIT_MACHINE |
>> +                                             @ IMAGE_FILE_DEBUG_STRIPPED |
>> +                                             @ IMAGE_FILE_EXECUTABLE_IMAGE |
>> +                                             @ IMAGE_FILE_LINE_NUMS_STRIPPED
>> +
>> +optional_header:
>> +     .short  0x10b                           @ PE32 format
>> +     .byte   0x02                            @ MajorLinkerVersion
>> +     .byte   0x14                            @ MinorLinkerVersion
>> +
>> +     .long   0                               @ SizeOfCode
>
> Do we need to fill in SizeOfCode with a real value?  It looks like x86
> does.
>
> We should probably fill this in unless there's a documented ABI for EFI
> boot on ARM which explicitly doesn't require these.

I will investigate/fix this.

>
>> +
>> +     .long   0                               @ SizeOfInitializedData
>> +     .long   0                               @ SizeOfUninitializedData
>> +
>> +     .long   efi_stub_entry                  @ AddressOfEntryPoint
>> +     .long   efi_stub_entry                  @ BaseOfCode
>> +     .long   0                               @ data
>> +
>> +extra_header_fields:
>> +     .long   0                               @ ImageBase
>> +     .long   0x20                            @ SectionAlignment
>> +     .long   0x20                            @ FileAlignment
>> +     .short  0                               @ MajorOperatingSystemVersion
>> +     .short  0                               @ MinorOperatingSystemVersion
>> +     .short  0                               @ MajorImageVersion
>> +     .short  0                               @ MinorImageVersion
>> +     .short  0                               @ MajorSubsystemVersion
>> +     .short  0                               @ MinorSubsystemVersion
>> +     .long   0                               @ Win32VersionValue
>> +
>> +     .long   _edata                          @ SizeOfImage
>> +
>> +     @ Everything before the entry point is considered part of the header
>> +     .long   efi_stub_entry                  @ SizeOfHeaders
>> +     .long   0                               @ CheckSum
>> +     .short  0xa                             @ Subsystem (EFI application)
>> +     .short  0                               @ DllCharacteristics
>> +     .long   0                               @ SizeOfStackReserve
>> +     .long   0                               @ SizeOfStackCommit
>> +     .long   0                               @ SizeOfHeapReserve
>> +     .long   0                               @ SizeOfHeapCommit
>> +     .long   0                               @ LoaderFlags
>> +     .long   0x0                             @ NumberOfRvaAndSizes
>> +
>> +     # Section table
>> +section_table:
>> +
>> +     #
>> +     # The EFI application loader requires a relocation section
>> +     # because EFI applications must be relocatable.  This is a
>> +     # dummy section as far as we are concerned.
>> +     #
>> +     .ascii  ".reloc"
>> +     .byte   0
>> +     .byte   0                       @ end of 0 padding of section name
>> +     .long   0
>> +     .long   0
>> +     .long   0                       @ SizeOfRawData
>> +     .long   0                       @ PointerToRawData
>> +     .long   0                       @ PointerToRelocations
>> +     .long   0                       @ PointerToLineNumbers
>> +     .short  0                       @ NumberOfRelocations
>> +     .short  0                       @ NumberOfLineNumbers
>> +     .long   0x42100040              @ Characteristics (section flags)
>> +
>> +
>> +     .ascii  ".text"
>> +     .byte   0
>> +     .byte   0
>> +     .byte   0                       @ end of 0 padding of section name
>> +     .long   _edata - efi_stub_entry         @ VirtualSize
>> +     .long   efi_stub_entry                  @ VirtualAddress
>> +     .long   _edata - efi_stub_entry         @ SizeOfRawData
>> +     .long   efi_stub_entry                  @ PointerToRawData
>> +
>> +     .long   0               @ PointerToRelocations (0 for executables)
>> +     .long   0               @ PointerToLineNumbers (0 for executables)
>> +     .short  0               @ NumberOfRelocations  (0 for executables)
>> +     .short  0               @ NumberOfLineNumbers  (0 for executables)
>> +     .long   0xe0500020      @ Characteristics (section flags)
>
> Can you explain why x86 needs an extra section (the .setup thing)?
> I haven't dug into that in enough detail to understand it yet...

I will look into that, I don't know off hand.  I simplified the header
for ARM as much as I could
for booting with EDK2.
>
>> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
>> new file mode 100644
>> index 0000000..b817ea3
>> --- /dev/null
>> +++ b/arch/arm/boot/compressed/efi-stub.c
>> @@ -0,0 +1,514 @@
>> +/*
>> + * linux/arch/arm/boot/compressed/efi-stub.c
>> + *
>> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
>> + *
>> + * This file implements the EFI boot stub for the ARM kernel
>> + *
>> + * 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 <libfdt.h>
>> +
>> +
>> +/* Error code returned to ASM code instead of valid FDT address. */
>> +#define EFI_STUB_ERROR               (~0)
>
> Can we put that into a suitable hedaer and use it in compressed/head.S,
> instead of the magic 0xffffffff?  (Assuming that value is supposed to
> match EFI_STUB_ERROR)

Yes, I will do this.
>
>> +
>> +/* EFI function call wrappers.  These are not required for
>> + * ARM, but wrappers are required for X86 to convert between
>> + * ABIs.  These wrappers are provided to allow code sharing
>> + * between X86 and ARM.  Since these wrappers directly invoke the
>> + * EFI function pointer, the function pointer type must be properly
>> + * defined, which is not the case for X86  One advantage of this is
>> + * it allows for type checking of arguments, which is not
>> + * possible with the X86 wrappers.
>> + */
>> +#define efi_call_phys0(f)                    f()
>> +#define efi_call_phys1(f, a1)                        f(a1)
>> +#define efi_call_phys2(f, a1, a2)            f(a1, a2)
>> +#define efi_call_phys3(f, a1, a2, a3)                f(a1, a2, a3)
>> +#define efi_call_phys4(f, a1, a2, a3, a4)    f(a1, a2, a3, a4)
>> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)        f(a1, a2, a3, a4, a5)
>> +
>> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
>> + * that for the decompressed kernel.  We have no easy way to tell what
>> + * the actuall size of code + data the uncompressed kernel will use.
>> + */
>> +#define MAX_UNCOMP_KERNEL_SIZE       0x02000000
>
> Can we fish the decompressed data size out of zImage, like the existing
> zImage code does?  (see compressed/head.S:207).  I don't see why this
> needs to be compile-time constant.

I am attempting to make sure all the memory used is accounted for in
the EFI memory map,
so I care not only about the uncompressed size, but also the BSS.  If
I get the uncompressed
image size, and use that for the allocation, the kernel will overwrite
memory immediately following it.
I had implemented what you suggested and ran into this problem.




>
> Someday, someone may try to grow the kernel image beyond 32M.  It would
> be nice to keep the number of things that breaks to a minimum, to ease
> potential pain later.

I picked 32 MBytes based on some discussions of the boot process, and
my understanding
is that 32 MBytes is a somewhat hard upper limit on kernel size.

>
>> +
>> +/* The kernel zImage should be located between 32 Mbytes
>> + * and 128 MBytes from the base of DRAM.  The min
>> + * address leaves space for a maximal size uncompressed image,
>> + * and the max address is due to how the zImage decompressor
>> + * picks a destination address.
>> + */
>> +#define MAX_ZIMAGE_OFFSET    0x08000000
>
> The maximum zImage offset is actually 1 less than this.  I think it's
> just the name of the macro that is misleading, since you use it
> correctly as an upper bound for memory allocation, so far as I can
> see.
>
> Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.

I'll rename this.

>
>> +#define MIN_ZIMAGE_OFFSET    MAX_UNCOMP_KERNEL_SIZE
>> +
>> +#define MAX_CMDLINE_LEN              500
>
> This is a random looking number.  Is this supposed to match something
> somewhere?  Does it serve any purpose other than acting as a sanity
> limit?
>
> If this limit doesn't exist, then an unreasonably large command-line
> passed by EFI would just lead to a memory allocation failure somewhere,
> which feels like the right behaviour...
>
> If we can avoid building in arbitrary limits, it helps avoid surprises
> later.


This is just a sanity check, which should be easy to remove.  I think
the failure mode will be a huge device tree being created,
rather than an allocation failure.  In reality I think the limit will
set by the EFI firmware - I doubt it is possible to pass a
multi-megabyte command line.

>
>> +
>> +struct fdt_region {
>> +     u64 base;
>> +     u64 size;
>> +};
>> +
>> +/*
>> + * Additional size that could be used for FDT entries added by
>> + * the UEFI OS Loader Estimation based on:
>> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
>> + * + system memory region (20bytes) + mp_core entries (200
>> + * bytes)
>> + */
>
> What does 0x300 have to do with those numbers?
>
> When you say "estimate", are we guaranteed never to exceed that?
> What happens if we do?

No guarantees, and we fail to boot if we run out of space in the new
device tree.  This greatly simplifies the code,
but I agree that it is not that nice.

>
>> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
>> +
>> +/* Include shared EFI stub code */
>> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
>> +
>> +
>> +static int is_linux_reserved_region(int memory_type)
>> +{
>> +     switch (memory_type) {
>> +     case EFI_RUNTIME_SERVICES_CODE:
>> +     case EFI_RUNTIME_SERVICES_DATA:
>> +     case EFI_UNUSABLE_MEMORY:
>> +     case EFI_ACPI_RECLAIM_MEMORY:
>> +     case EFI_ACPI_MEMORY_NVS:
>> +             return 1;
>> +     default:
>> +             return 0;
>> +     }
>> +}
>> +
>> +
>> +static int relocate_kernel(efi_system_table_t *sys_table,
>> +                        unsigned long *load_addr, unsigned long *load_size,
>> +                        unsigned long min_addr, unsigned long max_addr)
>> +{
>> +     /* Get current address of kernel. */
>> +     unsigned long cur_zimage_addr = *load_addr;
>> +     unsigned long zimage_size = *load_size;
>> +     unsigned long new_addr = 0;
>> +     unsigned long nr_pages;
>> +
>> +     efi_status_t status;
>> +
>> +     if (!load_addr || !load_size)
>> +             return EFI_INVALID_PARAMETER;
>> +
>> +     *load_size = 0;
>> +     if (cur_zimage_addr > min_addr
>> +         && (cur_zimage_addr + zimage_size) < max_addr) {
>> +             /* We don't need to do anything, as kernel at an acceptable
>> +              * address already.
>> +              */
>> +             return EFI_SUCCESS;
>> +     }
>> +     /*
>> +      * The EFI firmware loader could have placed the kernel image
>> +      * anywhere in memory, but the kernel has restrictions on the
>> +      * min and max physical address it can run at.
>> +      */
>> +     nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
>
> It looks like nr_pages is never used in this function.

Yup, removed.
>
>> +
>> +     status = efi_low_alloc(sys_table, zimage_size, 0,
>> +                        &new_addr, min_addr);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
>
> Does efi_printk automatically prepend a suitable prefix?  If not,
> it might be useful to define a macro to add a standard prefix to all
> efi_printks here ("zImage: " or similar).

It doesn't, but I can add one.  Maybe "EFIstub"?  This is really
separate from the zImage boot, so I think
it would be helpful to differentiate it.

>
> Minor nit: can we have "allocate" instead of "alloc"?
Sure.
>
> I think both messages should say "failed to allocate usable memory".
> EFI has already allocated memory for the kernel after all: it's
> just in the wrong place initially.
>
>> +             return status;
>> +     }
>> +
>> +     if (new_addr > (max_addr - zimage_size)) {
>> +             efi_free(sys_table, zimage_size, new_addr);
>> +             efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
>> +             return EFI_INVALID_PARAMETER;
>> +     }
>> +
>> +     /* We know source/dest won't overlap since both memory ranges
>> +      * have been allocated by UEFI, so we can safely use memcpy.
>> +      */
>> +     memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
>> +            zimage_size);
>
> Is it possible for this allocation to fail -- i.e., because UEFI has
> put us in an unsuitable location which is within the first 128MB of
> RAM, such that we can't pick a suitable location without overlap?

I think so, since (in theory at least), other EFI applications could have run
before us and allocated arbitrary amounts of memory.

>
> For the time being though, I think this is impossible because the
> decompressed Image can't exceed ~32MB (so the zImage should not
> exceed that either, and both can fit inside 128MB.  It doesn't
> matter if UEFI's initial load location overlaps the decompressed
> Image).

The reason I am avoiding the zImage overlapping the decompressed image
even though
the zImage decompressor handles that case is that I want to ensure that
all memory used during early boot is represented in the EFI memory map.
By avoiding overlap, I only have to deal with predicting the final
destination of the
decompressed kernel.


>
> If UEFI put reserved regions with the first 128MB we're likely to
> be dead anyway, so we shouldn't assume we'll have to cope with that
> for now...

For these cases I'd like to be able to return an error message and
refuse to boot, rather
than dying during boot.

In principle, I like the EFI stub being a shim between the EFI
firmware and the normal zImage boot.  In practice,
I don't really like having to predict/guess what memory the zImage
decompressor will use so that we can account for that
in the EFI memory map.

>
>> +
>> +     /* Return the load address and size */
>> +     *load_addr = new_addr;
>> +     *load_size = zimage_size;
>
> Is zimage_size ever changed?  It looks like it is still equal to the
> initial value of *load_size at this point.

Nope, I can get rid of zimage_size and just use *load_size throughout.

>
>> +
>> +
>> +     return status;
>> +}
>> +
>> +
>> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
>> + * Size of memory allocated return in *cmd_line_len.
>> + * Returns NULL on error.
>> + */
>> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
>> +                                   efi_loaded_image_t *image,
>> +                                   unsigned long *cmd_line_len,
>> +                                   u32 max_addr)
>> +{
>> +     u16 *s2;
>> +     u8 *s1 = NULL;
>> +     unsigned long cmdline_addr = 0;
>> +     int load_options_size = image->load_options_size / 2; /* ASCII */
>> +     void *options = (u16 *)image->load_options;
>> +     int options_size = 0;
>> +     int status;
>> +     int i;
>> +     u16 zero = 0;
>> +
>> +     if (options) {
>> +             s2 = options;
>> +             while (*s2 && *s2 != '\n' && options_size < load_options_size) {
>> +                     s2++;
>> +                     options_size++;
>> +             }
>> +     }
>> +
>> +     if (options_size == 0) {
>> +             /* No command line options, so return empty string*/
>> +             options_size = 1;
>> +             options = &zero;
>> +     }
>> +
>> +     if (options_size > MAX_CMDLINE_LEN)
>> +             options_size = MAX_CMDLINE_LEN;
>> +
>> +     options_size++;  /* NUL termination */
>
> Do we care that options_size can now be > load_options_size?
>
> I guess image->load_options isn't realistically going to be right at
> the end of a RAM bank, so probably nothing disastrous will happen if
> we read off the end of it.
>
> It would be tidier to avoid this, though.

I'll update this to avoid reading past the end of the EFI option string.

>
>> +
>> +     status = efi_high_alloc(sys_table, options_size, 0,
>> +                         &cmdline_addr, max_addr);
>> +     if (status != EFI_SUCCESS)
>> +             return NULL;
>> +
>> +     s1 = (u8 *)(unsigned long)cmdline_addr;
>> +     s2 = (u16 *)options;
>> +
>> +     for (i = 0; i < options_size - 1; i++)
>> +             *s1++ = *s2++;
>> +
>> +     *s1 = '\0';
>> +
>> +     *cmd_line_len = options_size;
>> +     return (char *)(unsigned long)cmdline_addr;
>> +}
>> +
>> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
>> +                                 void *handle, unsigned long dram_base,
>> +                                 void *orig_fdt, u64 *orig_fdt_size,
>> +                                 char *cmdline_ptr,
>> +                                 unsigned long *cmdline_size,
>> +                                 u64 initrd_addr, u64 initrd_size)
>> +{
>> +     unsigned long new_fdt_size;
>> +     unsigned long new_fdt_addr;
>> +     void *fdt;
>> +     int node;
>> +     int status;
>> +     int i;
>> +     unsigned long map_size, desc_size;
>> +     unsigned long mmap_key;
>> +     efi_memory_desc_t *memory_map;
>> +     unsigned long fdt_val;
>> +
>> +     new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
>> +     status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
>> +                         dram_base + MAX_ZIMAGE_OFFSET);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
>> +             goto fail;
>> +     }
>
> There are too many error messages in this function (and elsewhere).
> Many of them are only useful for debugging: for real use, the only
> interesting kinds of failure for the DT which will be meaningful to the
> user are "bad device tree" and "out of memory".
>
> Also, it would be desirable to make the error messages more consistent;
> currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
> and more.
>
> We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
> which mean basically the same thing.
>
> You could try wrapping fdt_setprop() with a function which tries to set
> the property and prints a suitable message if it fails, without having
> to put explicit efi_printks all over the place.

I will review all of the messages, and add a consistent prefix as you
suggested above.
>
>> +
>> +
>> +     fdt = (void *)new_fdt_addr;
>> +     status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
>> +     if (status != 0) {
>> +             efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     /* We are done with the original DTB, so free it. */
>> +     efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
>> +     *orig_fdt_size = 0;
>> +
>> +     node = fdt_subnode_offset(fdt, 0, "chosen");
>> +     if (node < 0) {
>> +             node = fdt_add_subnode(fdt, 0, "chosen");
>> +             if (node < 0) {
>> +                     efi_printk(sys_table, "Error on finding 'chosen' node\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +
>> +     if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
>> +             status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
>> +                                  strlen(cmdline_ptr) + 1);
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new bootarg\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +     /* We are done with original command line, so free it. */
>> +     efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
>> +     *cmdline_size = 0;
>> +
>> +     /* Set intird address/end in device tree, if present */
>> +     if (initrd_size != 0) {
>> +             u64 initrd_image_end;
>> +             u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
>> +             status = fdt_setprop(fdt, node, "linux,initrd-start",
>> +                                  &initrd_image_start, sizeof(u64));
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +             initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
>> +             status = fdt_setprop(fdt, node, "linux,initrd-end",
>> +                                  &initrd_image_end, sizeof(u64));
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +
>> +     /* Update memory map in the device tree. The memory node must
>> +      * be present in the tree.*/
>> +     node = fdt_subnode_offset(fdt, 0, "memory");
>> +     if (node < 0) {
>> +             efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +
>> +     status = efi_get_memory_map(sys_table, &memory_map, &map_size,
>> +                                 &desc_size, &mmap_key);
>> +     if (status != EFI_SUCCESS)
>> +             goto fail_free_new_fdt;
>> +
>> +     for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
>> +             efi_memory_desc_t *desc;
>> +             unsigned long m = (unsigned long)memory_map;
>> +             desc = (efi_memory_desc_t *)(m + (i * desc_size));
>> +
>> +             if (is_linux_reserved_region(desc->type)) {
>> +                     status = fdt_add_mem_rsv(fdt, desc->phys_addr,
>> +                                              desc->num_pages * EFI_PAGE_SIZE);
>> +                     if (status != 0) {
>> +                             efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
>> +                             goto fail_free_mmap;
>> +                     }
>> +             }
>> +     }
>> +
>> +
>> +     /* Add FDT entries for EFI runtime services in chosen node.
>> +      * We need to add the final memory map, so this is done at
>> +      * the very end.
>> +      */
>> +     node = fdt_subnode_offset(fdt, 0, "chosen");
>> +     fdt_val = cpu_to_fdt32((unsigned long)sys_table);
>> +     status = fdt_setprop(fdt, node, "efi-system-table",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32(desc_size);
>> +     status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32(map_size);
>> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32((unsigned long)memory_map);
>> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>
> We have one function doing two completely different jobs here (as
> documented by the name).  Can it be split?

I had it split, but due to the address/size pairs that needed to be
passed around
to free the allocated memory on error I combined them.  I'll take
another look at it.
I think pulling the allocations out of the function may make this
cleaner, and could
also make the removal of the guessed new FTD size easier to remove.
I'll need to handle
re-trying the FTD allocation in order to gracefully handle significant
growth in the DTB.


>
>> +
>> +     /* Now we need to exit boot services.  We need the key from
>> +      * the most recent read of the memory map to do this.  We can't
>> +      * free this buffer in the normal case, but do free it when
>> +      * exit_boot_services() fails or adding the memory map to the FDT
>> +      * fails.
>> +      */
>> +     status = efi_call_phys2(sys_table->boottime->exit_boot_services,
>> +                             handle, mmap_key);
>> +
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "exit boot services failed.\n");
>> +             goto fail_free_mmap;
>> +     }
>> +
>> +     return new_fdt_addr;
>> +
>> +fail_free_mmap:
>> +     efi_call_phys1(sys_table->boottime->free_pool, memory_map);
>> +
>> +fail_free_new_fdt:
>> +     efi_free(sys_table, new_fdt_size, new_fdt_addr);
>> +
>> +fail:
>> +     return 0;
>> +}
>> +
>> +
>> +int efi_entry(void *handle, efi_system_table_t *sys_table,
>> +           unsigned long *zimage_addr)
>> +{
>> +     efi_loaded_image_t *image;
>> +     int status;
>> +     unsigned long nr_pages;
>> +     const struct fdt_region *region;
>> +
>> +     void *fdt;
>> +     int err;
>> +     int node;
>> +     unsigned long zimage_size = 0;
>> +     unsigned long dram_base;
>> +     /* addr/point and size pairs for memory management*/
>> +     u64 initrd_addr;
>> +     u64 initrd_size = 0;
>> +     u64 fdt_addr;  /* Original DTB */
>> +     u64 fdt_size = 0;
>> +     u64 kernel_reserve_addr;
>> +     u64 kernel_reserve_size = 0;
>> +     char *cmdline_ptr;
>> +     unsigned long cmdline_size = 0;
>> +     unsigned long new_fdt_addr;
>> +
>> +     efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
>> +
>> +     /* Check if we were booted by the EFI firmware */
>> +     if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
>> +             goto fail;
>> +
>> +     efi_printk(sys_table, "Booting Linux using EFI stub.\n");
>> +
>> +
>> +     /* get the command line from EFI, using the LOADED_IMAGE protocol */
>> +     status = efi_call_phys3(sys_table->boottime->handle_protocol,
>> +                             handle, &proto, (void *)&image);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
>> +             goto fail;
>> +     }
>> +
>> +     /* We are going to copy this into device tree, so we don't care where in
>> +      * memory it is.
>> +      */
>> +     cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
>> +                                            &cmdline_size, 0xFFFFFFFF);
>> +     if (!cmdline_ptr) {
>> +             efi_printk(sys_table, "ERROR converting command line to ascii.\n");
>> +             goto fail;
>> +     }
>> +
>> +     /* We first load the device tree, as we need to get the base address of
>> +      * DRAM from the device tree.  The zImage, device tree, and initrd
>> +      * have address restrictions that are relative to the base of DRAM.
>> +      */
>> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
>> +                                   0xffffffff, &fdt_addr, &fdt_size);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Error loading dtb blob\n");
>> +             goto fail_free_cmdline;
>> +     }
>> +
>> +     err = fdt_check_header((void *)(unsigned long)fdt_addr);
>> +     if (err != 0) {
>> +             efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
>> +             goto fail_free_dtb;
>> +     }
>> +     if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
>> +             efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
>> +             goto fail_free_dtb;
>> +
>> +     }
>> +
>> +
>> +     /* Look up the base of DRAM from the device tree.*/
>> +     fdt = (void *)(u32)fdt_addr;
>> +     node = fdt_subnode_offset(fdt, 0, "memory");
>> +     region = fdt_getprop(fdt, node, "reg", NULL);
>> +     if (region) {
>> +             dram_base = fdt64_to_cpu(region->base);
>> +     } else {
>> +             efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
>> +             goto fail_free_dtb;
>> +     }
>> +
>> +     /* Reserve memory for the uncompressed kernel image. */
>> +     kernel_reserve_addr = dram_base;
>> +     kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
>> +     nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
>> +     status = efi_call_phys4(sys_table->boottime->allocate_pages,
>> +                             EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
>> +                             nr_pages, &kernel_reserve_addr);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
>> +             goto fail_free_dtb;
>> +     }
>> +
>> +     /* Relocate the zImage, if required. */
>> +     zimage_size = image->image_size;
>> +     status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
>> +                              dram_base + MIN_ZIMAGE_OFFSET,
>> +                              dram_base + MAX_ZIMAGE_OFFSET);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to relocate kernel\n");
>> +             goto fail_free_kernel_reserve;
>> +     }
>> +
>> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
>> +                                   dram_base + MAX_ZIMAGE_OFFSET,
>> +                                   &initrd_addr, &initrd_size);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Error loading initrd\n");
>> +             goto fail_free_zimage;
>> +     }
>> +
>> +     new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
>> +                                             dram_base, fdt, &fdt_size,
>> +                                             cmdline_ptr, &cmdline_size,
>> +                                             initrd_addr, initrd_size);
>> +
>> +     if (new_fdt_addr == 0) {
>> +             efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
>> +             goto fail_free_initrd;
>> +     }
>
> Ideally, we shouldn't have one error message for two completely
> different causes.
>
> The printk could move into update_fdt_and_exit_boot() and split
> into more specific cases.
>
>> +
>> +
>> +     /* Now we need to return the FDT address to the calling
>> +      * assembly to this can be used as part of normal boot.
>> +      */
>> +     return new_fdt_addr;
>> +
>> +fail_free_initrd:
>> +     efi_free(sys_table, initrd_size, initrd_addr);
>> +
>> +fail_free_zimage:
>> +     efi_free(sys_table, zimage_size, *zimage_addr);
>> +
>> +fail_free_kernel_reserve:
>> +     efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
>> +
>> +fail_free_dtb:
>> +     efi_free(sys_table, fdt_size, fdt_addr);
>> +
>> +fail_free_cmdline:
>> +     efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
>> +
>> +fail:
>> +     return EFI_STUB_ERROR;
>> +}
>> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
>> index 75189f1..491e752 100644
>> --- a/arch/arm/boot/compressed/head.S
>> +++ b/arch/arm/boot/compressed/head.S
>> @@ -120,21 +120,100 @@
>>   */
>>               .align
>>               .arm                            @ Always enter in ARM state
>> +             .text
>>  start:
>>               .type   start,#function
>> -             .rept   7
>> +#ifdef CONFIG_EFI_STUB
>> +             @ Magic MSDOS signature for PE/COFF + ADD opcode
>> +             .word   0x62805a4d
>
> Did you get a chance to respond to the endianness issue I raised?
For now the EFI stub only supports LE, and I need to update
the Kconfig to reflect this.  Adding BE should be possible, but I don't
plan to work on that at this time.



>> +#else
>> +             mov     r0, r0
>> +#endif
>> +             .rept   5
>>               mov     r0, r0
>>               .endr
>> -   ARM(              mov     r0, r0          )
>> -   ARM(              b       1f              )
>> - THUMB(              adr     r12, BSYM(1f)   )
>> - THUMB(              bx      r12             )
>> +
>> +             @ zimage_continue will be in ARM or thumb mode as configured
>> + THUMB(              adrl    r12, BSYM(zimage_continue))
>> + ARM(                adrl    r12, zimage_continue)
>> +             bx      r12
>
> Note that BSYM() can be used both in ARM and Thumb kernels.
>
> In any case, ARM kernels cannot contain BX instructions because we still
> support ARMv4 (which doesn't have it).
>
> I'm presuming you found zimage_continue is too far away for adr here,
> which is why you changed it.  Assuming that't the case, this might make
> sense:
>
>         adrl    r12, BSYM(zimage_continue)
>  ARM(   mov     pc, r12 )
>  THUMB( bx      r12     )

Yes, I changed this due to lack of range.

>
>> + THUMB(              .thumb                  )
>
> For tidiness, it's better to avoid this dangling .thumb ... move it
> to just before zimage_continue instead, since efi_stub_entry has to be
> ARM anyway.

OK
>
>>
>>               .word   0x016f2818              @ Magic numbers to help the loader
>>               .word   start                   @ absolute load/run zImage address
>>               .word   _edata                  @ zImage end address
>> +
>> +#ifdef CONFIG_EFI_STUB
>> +             @ Portions of the MSDOS file header must be at offset
>> +             @ 0x3c from the start of the file.  All PE/COFF headers
>> +             @ are kept contiguous for simplicity.
>> +#include "efi-header.S"
>> +
>> +efi_stub_entry:
>> +             @ The EFI stub entry point is not at a fixed address, however
>> +             @ this address must be set in the PE/COFF header.
>> +             @ EFI entry point is in A32 mode, switch to T32 if configured.
>> + THUMB(              .arm                    )
>
> ^So, you can lose .arm here too (but keep the comment -- that's valuable
> info)
>
>> + THUMB(              adr     r12, BSYM(1f)   )
>> + THUMB(              bx      r12             )
>>   THUMB(              .thumb                  )
>>  1:
>> +             @ Save lr on stack for possible return to EFI firmware.
>> +             @ Don't care about fp, but need 64 bit alignment....
>> +             stmfd   sp!, {fp, lr}
>> +
>> +             @ Save args to EFI app across got fixup call
>> +             stmfd   sp!, {r0, r1}
>
> Mostly minor coding nits follow...

I'll go through these and update the code.  I appreciate your review,
as I am new to ARM assembly.

>
>
> stmfd sp!, {r0, r1, fp, lr} ?
>
>> +             ldmfd   sp!, {r0, r1}
>> +
>> +             @ allocate space on stack for return of new entry point of
>> +             @ zImage, as EFI stub may copy the kernel.  Pass address
>> +             @ of space in r2 - EFI stub will fill in the pointer.
>> +
>> +             sub     sp, #8                  @ we only need 4 bytes,
>
> I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?
>
> kernel asm is written in the traditional syntax, which means explicit
> source and destination registers for instructions like this:
>
>         sub     sp, sp, #8
>
> Since the EFI stub code will only be built with new toolchains it
> probably doesn't matter, but it's best to be consistent for readability
> purposes.
>
>> +                                             @ but keep stack 8 byte aligned.
>> +             mov     r2, sp
>> +             @ Pass our actual runtime start address in pointer data
>> +             adr     r11, LC0                @ address of LC0 at run time
>> +             ldr     r12, [r11, #0]          @ address of LC0 at link time
>> +
>> +             sub     r3, r11, r12            @ calculate the delta offset
>> +             str     r3, [r2, #0]
>> +             bl      efi_entry
>> +
>> +             @ get new zImage entry address from stack, put into r3
>> +             ldr     r3, [sp, #0]
>> +             add     sp, #8  @ restore stack
>
>         add     sp, sp, #8
>
>> +
>> +             @ Check for error return from EFI stub (0xFFFFFFFF)
>> +             ldr     r1, =0xffffffff
>
> Minor nit, but ldr= is wasteful for this.
>
> You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
> smart enough to translate this)...
>
>> +             cmp     r0, r1
>
> ...alternatively, don't use r1 at all and do:
>
>         cmn     r0, #1
>
>> +             beq     efi_load_fail
>> +
>> +
>> +             @ Save return values of efi_entry
>> +             stmfd   sp!, {r0, r3}
>> +             bl      cache_clean_flush
>> +             bl      cache_off
>
> Why turn the cache off?  Does that mean that EFI may launch images with
> the cache enabled?
>
> If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> off is not safe.
>
> (Hmm, the UEFI spec seems to suggest "yes" for these questions)
>
>> +             ldmfd   sp!, {r0, r3}
>> +
>> +             @ put DTB address in r2, it was returned by EFI entry
>> +             mov     r2, r0
>> +             ldr     r1, =0xffffffff         @ DTB machine type
>
> mov/mvn: see above
>
>> +             mov     r0, #0  @ r0 is 0
>
> Useless comment: maybe say why you're doing this ("r0 is 0, as required
> by the kernel boot protocol", or something like that).
>
>> +
>> +             @ Branch to (possibly) relocated zImage entry that is in r3
>> +             bx      r3
>> +
>> +efi_load_fail:
>> +             @ Return EFI_LOAD_ERROR to EFI firmware on error.
>> +             @ Switch back to ARM mode for EFI is done based on
>> +             @ return address on stack
>> +             ldr     r0, =0x80000001
>> +             ldmfd   sp!, {fp, pc}
>> +#endif
>> +
>> +zimage_continue:
>>               mrs     r9, cpsr
>>  #ifdef CONFIG_ARM_VIRT_EXT
>>               bl      __hyp_stub_install      @ get into SVC mode, reversibly
>> @@ -167,7 +246,6 @@ not_angel:
>>                * by the linker here, but it should preserve r7, r8, and r9.
>>                */
>>
>> -             .text
>>
>>  #ifdef CONFIG_AUTO_ZRELADDR
>>               @ determine final kernel image address
>> --
>> 1.7.10.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-08 21:57       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-08 21:57 UTC (permalink / raw)
  To: Dave Martin
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w, Russell King - ARM Linux,
	Leif Lindholm

On Wed, Aug 7, 2013 at 11:05 AM, Dave Martin <Dave.Martin-5wv7dgnIgG8@public.gmane.org> wrote:
> On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
>> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
>> operations similarly to the x86 stub: it is a shim between the EFI firmware
>> and the normal zImage entry point, and sets up the environment that the
>> zImage is expecting.  This includes loading the initrd (optionaly) and
>> device tree from the system partition based on the kernel command line.
>> The stub updates the device tree as necessary, including adding reserved
>> memory regions and adding entries for EFI runtime services. The PE/COFF
>> "MZ" header at offset 0 results in the first instruction being an add
>> that corrupts r5, which is not used by the zImage interface.
>
> Some more comments below ... note that I haven't really looked at the C
> code in depth.

Responses below, and I'm working on incorporating suggested changes
for the next version.

Thanks,
Roy

>
> Cheers
> ---Dave
>
>>
>> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  arch/arm/boot/compressed/Makefile     |   18 +-
>>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
>>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
>>  arch/arm/boot/compressed/head.S       |   90 +++++-
>>  4 files changed, 728 insertions(+), 8 deletions(-)
>>  create mode 100644 arch/arm/boot/compressed/efi-header.S
>>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
>>
>> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
>> index 7ac1610..c62826a 100644
>> --- a/arch/arm/boot/compressed/Makefile
>> +++ b/arch/arm/boot/compressed/Makefile
>> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
>>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
>>       $(addprefix $(obj)/,$(libfdt_hdrs))
>>
>> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
>> +     $(addprefix $(obj)/,$(libfdt_hdrs))
>> +
>
> Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?
>
> Would it make sense just to add efi-stub.o to the list of targets in the
> original rule?

Yes, change made.
>
>>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
>> -OBJS += $(libfdt_objs) atags_to_fdt.o
>> +OBJS += atags_to_fdt.o
>> +USE_LIBFDT = y
>> +endif
>> +
>> +ifeq ($(CONFIG_EFI_STUB),y)
>> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
>> +OBJS += efi-stub.o
>> +USE_LIBFDT = y
>> +endif
>> +
>> +ifeq ($(USE_LIBFDT),y)
>> +OBJS += $(libfdt_objs)
>>  endif
>>
>>  targets       := vmlinux vmlinux.lds \
>> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
>>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
>>  endif
>>
>> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
>> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
>
> You don't appear to explain this change anywhere.

Prior to my changes, even though the stack protector was not disabled,
it was not actually used. GCC uses a heuristic
based on the size of the stack whether to enable the stack protector,
and the threshold to trigger its use was not met, so no stack checking
was actually being done.  In order to do stack protection, a few
__stack_chk_* functions/variable need to be provided by the
application.  I worked a bit on adding these, but could not get them
working in the stub/decompressor.  The x86 arch also has
"-fno-stack-protector" defined for its compressed boot stub, so I
decided to go that route as well.

>
>>  asflags-y := -DZIMAGE
>>
>>  # Supply kernel BSS size to the decompressor via a linker symbol.
>> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
>> new file mode 100644
>> index 0000000..6ff32cc
>> --- /dev/null
>> +++ b/arch/arm/boot/compressed/efi-header.S
>> @@ -0,0 +1,114 @@
>> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> +@
>> +@ This file contains the PE/COFF header that is part of the
>> +@ EFI stub.
>> +@
>> +
>> +     .org    0x3c
>> +     @
>> +     @ The PE header can be anywhere in the file, but for
>> +     @ simplicity we keep it together with the MSDOS header
>> +     @ The offset to the PE/COFF header needs to be at offset
>> +     @ 0x3C in the MSDOS header.
>> +     @ The only 2 fields of the MSDOS header that are used are this
>> +     @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
>> +     @
>> +     .long   pe_header                       @ Offset to the PE header.
>
> Is there any chance of merging this with the equivalent x86 code?
>
> The PE/COFF header is much the same in both cases, although there
> are some differences.  Maybe it would be more trouble than it is
> worth...

I think it would be more pain than gain.  We are planning to add arm64 stub
support next, so we'd end up with 4 architectures sharing this assembly file,
which I think would be painful from a patch submission/review point of view.

>
>> +
>> +      .align 3
>> +pe_header:
>> +
>> +
>> +pe_header:
>
> Duplicate label?

Yup, fixed.
>
>> +     .ascii  "PE"
>> +     .short  0
>> +
>> +coff_header:
>> +     .short  0x01c2                          @ ARM or Thumb
>> +     .short  2                               @ nr_sections
>> +     .long   0                               @ TimeDateStamp
>> +     .long   0                               @ PointerToSymbolTable
>> +     .long   1                               @ NumberOfSymbols
>> +     .short  section_table - optional_header @ SizeOfOptionalHeader
>> +     .short  0x306                           @ Characteristics.
>> +                                             @ IMAGE_FILE_32BIT_MACHINE |
>> +                                             @ IMAGE_FILE_DEBUG_STRIPPED |
>> +                                             @ IMAGE_FILE_EXECUTABLE_IMAGE |
>> +                                             @ IMAGE_FILE_LINE_NUMS_STRIPPED
>> +
>> +optional_header:
>> +     .short  0x10b                           @ PE32 format
>> +     .byte   0x02                            @ MajorLinkerVersion
>> +     .byte   0x14                            @ MinorLinkerVersion
>> +
>> +     .long   0                               @ SizeOfCode
>
> Do we need to fill in SizeOfCode with a real value?  It looks like x86
> does.
>
> We should probably fill this in unless there's a documented ABI for EFI
> boot on ARM which explicitly doesn't require these.

I will investigate/fix this.

>
>> +
>> +     .long   0                               @ SizeOfInitializedData
>> +     .long   0                               @ SizeOfUninitializedData
>> +
>> +     .long   efi_stub_entry                  @ AddressOfEntryPoint
>> +     .long   efi_stub_entry                  @ BaseOfCode
>> +     .long   0                               @ data
>> +
>> +extra_header_fields:
>> +     .long   0                               @ ImageBase
>> +     .long   0x20                            @ SectionAlignment
>> +     .long   0x20                            @ FileAlignment
>> +     .short  0                               @ MajorOperatingSystemVersion
>> +     .short  0                               @ MinorOperatingSystemVersion
>> +     .short  0                               @ MajorImageVersion
>> +     .short  0                               @ MinorImageVersion
>> +     .short  0                               @ MajorSubsystemVersion
>> +     .short  0                               @ MinorSubsystemVersion
>> +     .long   0                               @ Win32VersionValue
>> +
>> +     .long   _edata                          @ SizeOfImage
>> +
>> +     @ Everything before the entry point is considered part of the header
>> +     .long   efi_stub_entry                  @ SizeOfHeaders
>> +     .long   0                               @ CheckSum
>> +     .short  0xa                             @ Subsystem (EFI application)
>> +     .short  0                               @ DllCharacteristics
>> +     .long   0                               @ SizeOfStackReserve
>> +     .long   0                               @ SizeOfStackCommit
>> +     .long   0                               @ SizeOfHeapReserve
>> +     .long   0                               @ SizeOfHeapCommit
>> +     .long   0                               @ LoaderFlags
>> +     .long   0x0                             @ NumberOfRvaAndSizes
>> +
>> +     # Section table
>> +section_table:
>> +
>> +     #
>> +     # The EFI application loader requires a relocation section
>> +     # because EFI applications must be relocatable.  This is a
>> +     # dummy section as far as we are concerned.
>> +     #
>> +     .ascii  ".reloc"
>> +     .byte   0
>> +     .byte   0                       @ end of 0 padding of section name
>> +     .long   0
>> +     .long   0
>> +     .long   0                       @ SizeOfRawData
>> +     .long   0                       @ PointerToRawData
>> +     .long   0                       @ PointerToRelocations
>> +     .long   0                       @ PointerToLineNumbers
>> +     .short  0                       @ NumberOfRelocations
>> +     .short  0                       @ NumberOfLineNumbers
>> +     .long   0x42100040              @ Characteristics (section flags)
>> +
>> +
>> +     .ascii  ".text"
>> +     .byte   0
>> +     .byte   0
>> +     .byte   0                       @ end of 0 padding of section name
>> +     .long   _edata - efi_stub_entry         @ VirtualSize
>> +     .long   efi_stub_entry                  @ VirtualAddress
>> +     .long   _edata - efi_stub_entry         @ SizeOfRawData
>> +     .long   efi_stub_entry                  @ PointerToRawData
>> +
>> +     .long   0               @ PointerToRelocations (0 for executables)
>> +     .long   0               @ PointerToLineNumbers (0 for executables)
>> +     .short  0               @ NumberOfRelocations  (0 for executables)
>> +     .short  0               @ NumberOfLineNumbers  (0 for executables)
>> +     .long   0xe0500020      @ Characteristics (section flags)
>
> Can you explain why x86 needs an extra section (the .setup thing)?
> I haven't dug into that in enough detail to understand it yet...

I will look into that, I don't know off hand.  I simplified the header
for ARM as much as I could
for booting with EDK2.
>
>> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
>> new file mode 100644
>> index 0000000..b817ea3
>> --- /dev/null
>> +++ b/arch/arm/boot/compressed/efi-stub.c
>> @@ -0,0 +1,514 @@
>> +/*
>> + * linux/arch/arm/boot/compressed/efi-stub.c
>> + *
>> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> + *
>> + * This file implements the EFI boot stub for the ARM kernel
>> + *
>> + * 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 <libfdt.h>
>> +
>> +
>> +/* Error code returned to ASM code instead of valid FDT address. */
>> +#define EFI_STUB_ERROR               (~0)
>
> Can we put that into a suitable hedaer and use it in compressed/head.S,
> instead of the magic 0xffffffff?  (Assuming that value is supposed to
> match EFI_STUB_ERROR)

Yes, I will do this.
>
>> +
>> +/* EFI function call wrappers.  These are not required for
>> + * ARM, but wrappers are required for X86 to convert between
>> + * ABIs.  These wrappers are provided to allow code sharing
>> + * between X86 and ARM.  Since these wrappers directly invoke the
>> + * EFI function pointer, the function pointer type must be properly
>> + * defined, which is not the case for X86  One advantage of this is
>> + * it allows for type checking of arguments, which is not
>> + * possible with the X86 wrappers.
>> + */
>> +#define efi_call_phys0(f)                    f()
>> +#define efi_call_phys1(f, a1)                        f(a1)
>> +#define efi_call_phys2(f, a1, a2)            f(a1, a2)
>> +#define efi_call_phys3(f, a1, a2, a3)                f(a1, a2, a3)
>> +#define efi_call_phys4(f, a1, a2, a3, a4)    f(a1, a2, a3, a4)
>> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)        f(a1, a2, a3, a4, a5)
>> +
>> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
>> + * that for the decompressed kernel.  We have no easy way to tell what
>> + * the actuall size of code + data the uncompressed kernel will use.
>> + */
>> +#define MAX_UNCOMP_KERNEL_SIZE       0x02000000
>
> Can we fish the decompressed data size out of zImage, like the existing
> zImage code does?  (see compressed/head.S:207).  I don't see why this
> needs to be compile-time constant.

I am attempting to make sure all the memory used is accounted for in
the EFI memory map,
so I care not only about the uncompressed size, but also the BSS.  If
I get the uncompressed
image size, and use that for the allocation, the kernel will overwrite
memory immediately following it.
I had implemented what you suggested and ran into this problem.




>
> Someday, someone may try to grow the kernel image beyond 32M.  It would
> be nice to keep the number of things that breaks to a minimum, to ease
> potential pain later.

I picked 32 MBytes based on some discussions of the boot process, and
my understanding
is that 32 MBytes is a somewhat hard upper limit on kernel size.

>
>> +
>> +/* The kernel zImage should be located between 32 Mbytes
>> + * and 128 MBytes from the base of DRAM.  The min
>> + * address leaves space for a maximal size uncompressed image,
>> + * and the max address is due to how the zImage decompressor
>> + * picks a destination address.
>> + */
>> +#define MAX_ZIMAGE_OFFSET    0x08000000
>
> The maximum zImage offset is actually 1 less than this.  I think it's
> just the name of the macro that is misleading, since you use it
> correctly as an upper bound for memory allocation, so far as I can
> see.
>
> Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.

I'll rename this.

>
>> +#define MIN_ZIMAGE_OFFSET    MAX_UNCOMP_KERNEL_SIZE
>> +
>> +#define MAX_CMDLINE_LEN              500
>
> This is a random looking number.  Is this supposed to match something
> somewhere?  Does it serve any purpose other than acting as a sanity
> limit?
>
> If this limit doesn't exist, then an unreasonably large command-line
> passed by EFI would just lead to a memory allocation failure somewhere,
> which feels like the right behaviour...
>
> If we can avoid building in arbitrary limits, it helps avoid surprises
> later.


This is just a sanity check, which should be easy to remove.  I think
the failure mode will be a huge device tree being created,
rather than an allocation failure.  In reality I think the limit will
set by the EFI firmware - I doubt it is possible to pass a
multi-megabyte command line.

>
>> +
>> +struct fdt_region {
>> +     u64 base;
>> +     u64 size;
>> +};
>> +
>> +/*
>> + * Additional size that could be used for FDT entries added by
>> + * the UEFI OS Loader Estimation based on:
>> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
>> + * + system memory region (20bytes) + mp_core entries (200
>> + * bytes)
>> + */
>
> What does 0x300 have to do with those numbers?
>
> When you say "estimate", are we guaranteed never to exceed that?
> What happens if we do?

No guarantees, and we fail to boot if we run out of space in the new
device tree.  This greatly simplifies the code,
but I agree that it is not that nice.

>
>> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
>> +
>> +/* Include shared EFI stub code */
>> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
>> +
>> +
>> +static int is_linux_reserved_region(int memory_type)
>> +{
>> +     switch (memory_type) {
>> +     case EFI_RUNTIME_SERVICES_CODE:
>> +     case EFI_RUNTIME_SERVICES_DATA:
>> +     case EFI_UNUSABLE_MEMORY:
>> +     case EFI_ACPI_RECLAIM_MEMORY:
>> +     case EFI_ACPI_MEMORY_NVS:
>> +             return 1;
>> +     default:
>> +             return 0;
>> +     }
>> +}
>> +
>> +
>> +static int relocate_kernel(efi_system_table_t *sys_table,
>> +                        unsigned long *load_addr, unsigned long *load_size,
>> +                        unsigned long min_addr, unsigned long max_addr)
>> +{
>> +     /* Get current address of kernel. */
>> +     unsigned long cur_zimage_addr = *load_addr;
>> +     unsigned long zimage_size = *load_size;
>> +     unsigned long new_addr = 0;
>> +     unsigned long nr_pages;
>> +
>> +     efi_status_t status;
>> +
>> +     if (!load_addr || !load_size)
>> +             return EFI_INVALID_PARAMETER;
>> +
>> +     *load_size = 0;
>> +     if (cur_zimage_addr > min_addr
>> +         && (cur_zimage_addr + zimage_size) < max_addr) {
>> +             /* We don't need to do anything, as kernel at an acceptable
>> +              * address already.
>> +              */
>> +             return EFI_SUCCESS;
>> +     }
>> +     /*
>> +      * The EFI firmware loader could have placed the kernel image
>> +      * anywhere in memory, but the kernel has restrictions on the
>> +      * min and max physical address it can run at.
>> +      */
>> +     nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
>
> It looks like nr_pages is never used in this function.

Yup, removed.
>
>> +
>> +     status = efi_low_alloc(sys_table, zimage_size, 0,
>> +                        &new_addr, min_addr);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
>
> Does efi_printk automatically prepend a suitable prefix?  If not,
> it might be useful to define a macro to add a standard prefix to all
> efi_printks here ("zImage: " or similar).

It doesn't, but I can add one.  Maybe "EFIstub"?  This is really
separate from the zImage boot, so I think
it would be helpful to differentiate it.

>
> Minor nit: can we have "allocate" instead of "alloc"?
Sure.
>
> I think both messages should say "failed to allocate usable memory".
> EFI has already allocated memory for the kernel after all: it's
> just in the wrong place initially.
>
>> +             return status;
>> +     }
>> +
>> +     if (new_addr > (max_addr - zimage_size)) {
>> +             efi_free(sys_table, zimage_size, new_addr);
>> +             efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
>> +             return EFI_INVALID_PARAMETER;
>> +     }
>> +
>> +     /* We know source/dest won't overlap since both memory ranges
>> +      * have been allocated by UEFI, so we can safely use memcpy.
>> +      */
>> +     memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
>> +            zimage_size);
>
> Is it possible for this allocation to fail -- i.e., because UEFI has
> put us in an unsuitable location which is within the first 128MB of
> RAM, such that we can't pick a suitable location without overlap?

I think so, since (in theory at least), other EFI applications could have run
before us and allocated arbitrary amounts of memory.

>
> For the time being though, I think this is impossible because the
> decompressed Image can't exceed ~32MB (so the zImage should not
> exceed that either, and both can fit inside 128MB.  It doesn't
> matter if UEFI's initial load location overlaps the decompressed
> Image).

The reason I am avoiding the zImage overlapping the decompressed image
even though
the zImage decompressor handles that case is that I want to ensure that
all memory used during early boot is represented in the EFI memory map.
By avoiding overlap, I only have to deal with predicting the final
destination of the
decompressed kernel.


>
> If UEFI put reserved regions with the first 128MB we're likely to
> be dead anyway, so we shouldn't assume we'll have to cope with that
> for now...

For these cases I'd like to be able to return an error message and
refuse to boot, rather
than dying during boot.

In principle, I like the EFI stub being a shim between the EFI
firmware and the normal zImage boot.  In practice,
I don't really like having to predict/guess what memory the zImage
decompressor will use so that we can account for that
in the EFI memory map.

>
>> +
>> +     /* Return the load address and size */
>> +     *load_addr = new_addr;
>> +     *load_size = zimage_size;
>
> Is zimage_size ever changed?  It looks like it is still equal to the
> initial value of *load_size at this point.

Nope, I can get rid of zimage_size and just use *load_size throughout.

>
>> +
>> +
>> +     return status;
>> +}
>> +
>> +
>> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
>> + * Size of memory allocated return in *cmd_line_len.
>> + * Returns NULL on error.
>> + */
>> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
>> +                                   efi_loaded_image_t *image,
>> +                                   unsigned long *cmd_line_len,
>> +                                   u32 max_addr)
>> +{
>> +     u16 *s2;
>> +     u8 *s1 = NULL;
>> +     unsigned long cmdline_addr = 0;
>> +     int load_options_size = image->load_options_size / 2; /* ASCII */
>> +     void *options = (u16 *)image->load_options;
>> +     int options_size = 0;
>> +     int status;
>> +     int i;
>> +     u16 zero = 0;
>> +
>> +     if (options) {
>> +             s2 = options;
>> +             while (*s2 && *s2 != '\n' && options_size < load_options_size) {
>> +                     s2++;
>> +                     options_size++;
>> +             }
>> +     }
>> +
>> +     if (options_size == 0) {
>> +             /* No command line options, so return empty string*/
>> +             options_size = 1;
>> +             options = &zero;
>> +     }
>> +
>> +     if (options_size > MAX_CMDLINE_LEN)
>> +             options_size = MAX_CMDLINE_LEN;
>> +
>> +     options_size++;  /* NUL termination */
>
> Do we care that options_size can now be > load_options_size?
>
> I guess image->load_options isn't realistically going to be right at
> the end of a RAM bank, so probably nothing disastrous will happen if
> we read off the end of it.
>
> It would be tidier to avoid this, though.

I'll update this to avoid reading past the end of the EFI option string.

>
>> +
>> +     status = efi_high_alloc(sys_table, options_size, 0,
>> +                         &cmdline_addr, max_addr);
>> +     if (status != EFI_SUCCESS)
>> +             return NULL;
>> +
>> +     s1 = (u8 *)(unsigned long)cmdline_addr;
>> +     s2 = (u16 *)options;
>> +
>> +     for (i = 0; i < options_size - 1; i++)
>> +             *s1++ = *s2++;
>> +
>> +     *s1 = '\0';
>> +
>> +     *cmd_line_len = options_size;
>> +     return (char *)(unsigned long)cmdline_addr;
>> +}
>> +
>> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
>> +                                 void *handle, unsigned long dram_base,
>> +                                 void *orig_fdt, u64 *orig_fdt_size,
>> +                                 char *cmdline_ptr,
>> +                                 unsigned long *cmdline_size,
>> +                                 u64 initrd_addr, u64 initrd_size)
>> +{
>> +     unsigned long new_fdt_size;
>> +     unsigned long new_fdt_addr;
>> +     void *fdt;
>> +     int node;
>> +     int status;
>> +     int i;
>> +     unsigned long map_size, desc_size;
>> +     unsigned long mmap_key;
>> +     efi_memory_desc_t *memory_map;
>> +     unsigned long fdt_val;
>> +
>> +     new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
>> +     status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
>> +                         dram_base + MAX_ZIMAGE_OFFSET);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
>> +             goto fail;
>> +     }
>
> There are too many error messages in this function (and elsewhere).
> Many of them are only useful for debugging: for real use, the only
> interesting kinds of failure for the DT which will be meaningful to the
> user are "bad device tree" and "out of memory".
>
> Also, it would be desirable to make the error messages more consistent;
> currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
> and more.
>
> We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
> which mean basically the same thing.
>
> You could try wrapping fdt_setprop() with a function which tries to set
> the property and prints a suitable message if it fails, without having
> to put explicit efi_printks all over the place.

I will review all of the messages, and add a consistent prefix as you
suggested above.
>
>> +
>> +
>> +     fdt = (void *)new_fdt_addr;
>> +     status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
>> +     if (status != 0) {
>> +             efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     /* We are done with the original DTB, so free it. */
>> +     efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
>> +     *orig_fdt_size = 0;
>> +
>> +     node = fdt_subnode_offset(fdt, 0, "chosen");
>> +     if (node < 0) {
>> +             node = fdt_add_subnode(fdt, 0, "chosen");
>> +             if (node < 0) {
>> +                     efi_printk(sys_table, "Error on finding 'chosen' node\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +
>> +     if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
>> +             status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
>> +                                  strlen(cmdline_ptr) + 1);
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new bootarg\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +     /* We are done with original command line, so free it. */
>> +     efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
>> +     *cmdline_size = 0;
>> +
>> +     /* Set intird address/end in device tree, if present */
>> +     if (initrd_size != 0) {
>> +             u64 initrd_image_end;
>> +             u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
>> +             status = fdt_setprop(fdt, node, "linux,initrd-start",
>> +                                  &initrd_image_start, sizeof(u64));
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +             initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
>> +             status = fdt_setprop(fdt, node, "linux,initrd-end",
>> +                                  &initrd_image_end, sizeof(u64));
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +
>> +     /* Update memory map in the device tree. The memory node must
>> +      * be present in the tree.*/
>> +     node = fdt_subnode_offset(fdt, 0, "memory");
>> +     if (node < 0) {
>> +             efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +
>> +     status = efi_get_memory_map(sys_table, &memory_map, &map_size,
>> +                                 &desc_size, &mmap_key);
>> +     if (status != EFI_SUCCESS)
>> +             goto fail_free_new_fdt;
>> +
>> +     for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
>> +             efi_memory_desc_t *desc;
>> +             unsigned long m = (unsigned long)memory_map;
>> +             desc = (efi_memory_desc_t *)(m + (i * desc_size));
>> +
>> +             if (is_linux_reserved_region(desc->type)) {
>> +                     status = fdt_add_mem_rsv(fdt, desc->phys_addr,
>> +                                              desc->num_pages * EFI_PAGE_SIZE);
>> +                     if (status != 0) {
>> +                             efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
>> +                             goto fail_free_mmap;
>> +                     }
>> +             }
>> +     }
>> +
>> +
>> +     /* Add FDT entries for EFI runtime services in chosen node.
>> +      * We need to add the final memory map, so this is done at
>> +      * the very end.
>> +      */
>> +     node = fdt_subnode_offset(fdt, 0, "chosen");
>> +     fdt_val = cpu_to_fdt32((unsigned long)sys_table);
>> +     status = fdt_setprop(fdt, node, "efi-system-table",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32(desc_size);
>> +     status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32(map_size);
>> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32((unsigned long)memory_map);
>> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>
> We have one function doing two completely different jobs here (as
> documented by the name).  Can it be split?

I had it split, but due to the address/size pairs that needed to be
passed around
to free the allocated memory on error I combined them.  I'll take
another look at it.
I think pulling the allocations out of the function may make this
cleaner, and could
also make the removal of the guessed new FTD size easier to remove.
I'll need to handle
re-trying the FTD allocation in order to gracefully handle significant
growth in the DTB.


>
>> +
>> +     /* Now we need to exit boot services.  We need the key from
>> +      * the most recent read of the memory map to do this.  We can't
>> +      * free this buffer in the normal case, but do free it when
>> +      * exit_boot_services() fails or adding the memory map to the FDT
>> +      * fails.
>> +      */
>> +     status = efi_call_phys2(sys_table->boottime->exit_boot_services,
>> +                             handle, mmap_key);
>> +
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "exit boot services failed.\n");
>> +             goto fail_free_mmap;
>> +     }
>> +
>> +     return new_fdt_addr;
>> +
>> +fail_free_mmap:
>> +     efi_call_phys1(sys_table->boottime->free_pool, memory_map);
>> +
>> +fail_free_new_fdt:
>> +     efi_free(sys_table, new_fdt_size, new_fdt_addr);
>> +
>> +fail:
>> +     return 0;
>> +}
>> +
>> +
>> +int efi_entry(void *handle, efi_system_table_t *sys_table,
>> +           unsigned long *zimage_addr)
>> +{
>> +     efi_loaded_image_t *image;
>> +     int status;
>> +     unsigned long nr_pages;
>> +     const struct fdt_region *region;
>> +
>> +     void *fdt;
>> +     int err;
>> +     int node;
>> +     unsigned long zimage_size = 0;
>> +     unsigned long dram_base;
>> +     /* addr/point and size pairs for memory management*/
>> +     u64 initrd_addr;
>> +     u64 initrd_size = 0;
>> +     u64 fdt_addr;  /* Original DTB */
>> +     u64 fdt_size = 0;
>> +     u64 kernel_reserve_addr;
>> +     u64 kernel_reserve_size = 0;
>> +     char *cmdline_ptr;
>> +     unsigned long cmdline_size = 0;
>> +     unsigned long new_fdt_addr;
>> +
>> +     efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
>> +
>> +     /* Check if we were booted by the EFI firmware */
>> +     if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
>> +             goto fail;
>> +
>> +     efi_printk(sys_table, "Booting Linux using EFI stub.\n");
>> +
>> +
>> +     /* get the command line from EFI, using the LOADED_IMAGE protocol */
>> +     status = efi_call_phys3(sys_table->boottime->handle_protocol,
>> +                             handle, &proto, (void *)&image);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
>> +             goto fail;
>> +     }
>> +
>> +     /* We are going to copy this into device tree, so we don't care where in
>> +      * memory it is.
>> +      */
>> +     cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
>> +                                            &cmdline_size, 0xFFFFFFFF);
>> +     if (!cmdline_ptr) {
>> +             efi_printk(sys_table, "ERROR converting command line to ascii.\n");
>> +             goto fail;
>> +     }
>> +
>> +     /* We first load the device tree, as we need to get the base address of
>> +      * DRAM from the device tree.  The zImage, device tree, and initrd
>> +      * have address restrictions that are relative to the base of DRAM.
>> +      */
>> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
>> +                                   0xffffffff, &fdt_addr, &fdt_size);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Error loading dtb blob\n");
>> +             goto fail_free_cmdline;
>> +     }
>> +
>> +     err = fdt_check_header((void *)(unsigned long)fdt_addr);
>> +     if (err != 0) {
>> +             efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
>> +             goto fail_free_dtb;
>> +     }
>> +     if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
>> +             efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
>> +             goto fail_free_dtb;
>> +
>> +     }
>> +
>> +
>> +     /* Look up the base of DRAM from the device tree.*/
>> +     fdt = (void *)(u32)fdt_addr;
>> +     node = fdt_subnode_offset(fdt, 0, "memory");
>> +     region = fdt_getprop(fdt, node, "reg", NULL);
>> +     if (region) {
>> +             dram_base = fdt64_to_cpu(region->base);
>> +     } else {
>> +             efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
>> +             goto fail_free_dtb;
>> +     }
>> +
>> +     /* Reserve memory for the uncompressed kernel image. */
>> +     kernel_reserve_addr = dram_base;
>> +     kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
>> +     nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
>> +     status = efi_call_phys4(sys_table->boottime->allocate_pages,
>> +                             EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
>> +                             nr_pages, &kernel_reserve_addr);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
>> +             goto fail_free_dtb;
>> +     }
>> +
>> +     /* Relocate the zImage, if required. */
>> +     zimage_size = image->image_size;
>> +     status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
>> +                              dram_base + MIN_ZIMAGE_OFFSET,
>> +                              dram_base + MAX_ZIMAGE_OFFSET);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to relocate kernel\n");
>> +             goto fail_free_kernel_reserve;
>> +     }
>> +
>> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
>> +                                   dram_base + MAX_ZIMAGE_OFFSET,
>> +                                   &initrd_addr, &initrd_size);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Error loading initrd\n");
>> +             goto fail_free_zimage;
>> +     }
>> +
>> +     new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
>> +                                             dram_base, fdt, &fdt_size,
>> +                                             cmdline_ptr, &cmdline_size,
>> +                                             initrd_addr, initrd_size);
>> +
>> +     if (new_fdt_addr == 0) {
>> +             efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
>> +             goto fail_free_initrd;
>> +     }
>
> Ideally, we shouldn't have one error message for two completely
> different causes.
>
> The printk could move into update_fdt_and_exit_boot() and split
> into more specific cases.
>
>> +
>> +
>> +     /* Now we need to return the FDT address to the calling
>> +      * assembly to this can be used as part of normal boot.
>> +      */
>> +     return new_fdt_addr;
>> +
>> +fail_free_initrd:
>> +     efi_free(sys_table, initrd_size, initrd_addr);
>> +
>> +fail_free_zimage:
>> +     efi_free(sys_table, zimage_size, *zimage_addr);
>> +
>> +fail_free_kernel_reserve:
>> +     efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
>> +
>> +fail_free_dtb:
>> +     efi_free(sys_table, fdt_size, fdt_addr);
>> +
>> +fail_free_cmdline:
>> +     efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
>> +
>> +fail:
>> +     return EFI_STUB_ERROR;
>> +}
>> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
>> index 75189f1..491e752 100644
>> --- a/arch/arm/boot/compressed/head.S
>> +++ b/arch/arm/boot/compressed/head.S
>> @@ -120,21 +120,100 @@
>>   */
>>               .align
>>               .arm                            @ Always enter in ARM state
>> +             .text
>>  start:
>>               .type   start,#function
>> -             .rept   7
>> +#ifdef CONFIG_EFI_STUB
>> +             @ Magic MSDOS signature for PE/COFF + ADD opcode
>> +             .word   0x62805a4d
>
> Did you get a chance to respond to the endianness issue I raised?
For now the EFI stub only supports LE, and I need to update
the Kconfig to reflect this.  Adding BE should be possible, but I don't
plan to work on that at this time.



>> +#else
>> +             mov     r0, r0
>> +#endif
>> +             .rept   5
>>               mov     r0, r0
>>               .endr
>> -   ARM(              mov     r0, r0          )
>> -   ARM(              b       1f              )
>> - THUMB(              adr     r12, BSYM(1f)   )
>> - THUMB(              bx      r12             )
>> +
>> +             @ zimage_continue will be in ARM or thumb mode as configured
>> + THUMB(              adrl    r12, BSYM(zimage_continue))
>> + ARM(                adrl    r12, zimage_continue)
>> +             bx      r12
>
> Note that BSYM() can be used both in ARM and Thumb kernels.
>
> In any case, ARM kernels cannot contain BX instructions because we still
> support ARMv4 (which doesn't have it).
>
> I'm presuming you found zimage_continue is too far away for adr here,
> which is why you changed it.  Assuming that't the case, this might make
> sense:
>
>         adrl    r12, BSYM(zimage_continue)
>  ARM(   mov     pc, r12 )
>  THUMB( bx      r12     )

Yes, I changed this due to lack of range.

>
>> + THUMB(              .thumb                  )
>
> For tidiness, it's better to avoid this dangling .thumb ... move it
> to just before zimage_continue instead, since efi_stub_entry has to be
> ARM anyway.

OK
>
>>
>>               .word   0x016f2818              @ Magic numbers to help the loader
>>               .word   start                   @ absolute load/run zImage address
>>               .word   _edata                  @ zImage end address
>> +
>> +#ifdef CONFIG_EFI_STUB
>> +             @ Portions of the MSDOS file header must be at offset
>> +             @ 0x3c from the start of the file.  All PE/COFF headers
>> +             @ are kept contiguous for simplicity.
>> +#include "efi-header.S"
>> +
>> +efi_stub_entry:
>> +             @ The EFI stub entry point is not at a fixed address, however
>> +             @ this address must be set in the PE/COFF header.
>> +             @ EFI entry point is in A32 mode, switch to T32 if configured.
>> + THUMB(              .arm                    )
>
> ^So, you can lose .arm here too (but keep the comment -- that's valuable
> info)
>
>> + THUMB(              adr     r12, BSYM(1f)   )
>> + THUMB(              bx      r12             )
>>   THUMB(              .thumb                  )
>>  1:
>> +             @ Save lr on stack for possible return to EFI firmware.
>> +             @ Don't care about fp, but need 64 bit alignment....
>> +             stmfd   sp!, {fp, lr}
>> +
>> +             @ Save args to EFI app across got fixup call
>> +             stmfd   sp!, {r0, r1}
>
> Mostly minor coding nits follow...

I'll go through these and update the code.  I appreciate your review,
as I am new to ARM assembly.

>
>
> stmfd sp!, {r0, r1, fp, lr} ?
>
>> +             ldmfd   sp!, {r0, r1}
>> +
>> +             @ allocate space on stack for return of new entry point of
>> +             @ zImage, as EFI stub may copy the kernel.  Pass address
>> +             @ of space in r2 - EFI stub will fill in the pointer.
>> +
>> +             sub     sp, #8                  @ we only need 4 bytes,
>
> I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?
>
> kernel asm is written in the traditional syntax, which means explicit
> source and destination registers for instructions like this:
>
>         sub     sp, sp, #8
>
> Since the EFI stub code will only be built with new toolchains it
> probably doesn't matter, but it's best to be consistent for readability
> purposes.
>
>> +                                             @ but keep stack 8 byte aligned.
>> +             mov     r2, sp
>> +             @ Pass our actual runtime start address in pointer data
>> +             adr     r11, LC0                @ address of LC0 at run time
>> +             ldr     r12, [r11, #0]          @ address of LC0 at link time
>> +
>> +             sub     r3, r11, r12            @ calculate the delta offset
>> +             str     r3, [r2, #0]
>> +             bl      efi_entry
>> +
>> +             @ get new zImage entry address from stack, put into r3
>> +             ldr     r3, [sp, #0]
>> +             add     sp, #8  @ restore stack
>
>         add     sp, sp, #8
>
>> +
>> +             @ Check for error return from EFI stub (0xFFFFFFFF)
>> +             ldr     r1, =0xffffffff
>
> Minor nit, but ldr= is wasteful for this.
>
> You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
> smart enough to translate this)...
>
>> +             cmp     r0, r1
>
> ...alternatively, don't use r1 at all and do:
>
>         cmn     r0, #1
>
>> +             beq     efi_load_fail
>> +
>> +
>> +             @ Save return values of efi_entry
>> +             stmfd   sp!, {r0, r3}
>> +             bl      cache_clean_flush
>> +             bl      cache_off
>
> Why turn the cache off?  Does that mean that EFI may launch images with
> the cache enabled?
>
> If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> off is not safe.
>
> (Hmm, the UEFI spec seems to suggest "yes" for these questions)
>
>> +             ldmfd   sp!, {r0, r3}
>> +
>> +             @ put DTB address in r2, it was returned by EFI entry
>> +             mov     r2, r0
>> +             ldr     r1, =0xffffffff         @ DTB machine type
>
> mov/mvn: see above
>
>> +             mov     r0, #0  @ r0 is 0
>
> Useless comment: maybe say why you're doing this ("r0 is 0, as required
> by the kernel boot protocol", or something like that).
>
>> +
>> +             @ Branch to (possibly) relocated zImage entry that is in r3
>> +             bx      r3
>> +
>> +efi_load_fail:
>> +             @ Return EFI_LOAD_ERROR to EFI firmware on error.
>> +             @ Switch back to ARM mode for EFI is done based on
>> +             @ return address on stack
>> +             ldr     r0, =0x80000001
>> +             ldmfd   sp!, {fp, pc}
>> +#endif
>> +
>> +zimage_continue:
>>               mrs     r9, cpsr
>>  #ifdef CONFIG_ARM_VIRT_EXT
>>               bl      __hyp_stub_install      @ get into SVC mode, reversibly
>> @@ -167,7 +246,6 @@ not_angel:
>>                * by the linker here, but it should preserve r7, r8, and r9.
>>                */
>>
>> -             .text
>>
>>  #ifdef CONFIG_AUTO_ZRELADDR
>>               @ determine final kernel image address
>> --
>> 1.7.10.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-08 21:57       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-08 21:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 7, 2013 at 11:05 AM, Dave Martin <Dave.Martin@arm.com> wrote:
> On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
>> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
>> operations similarly to the x86 stub: it is a shim between the EFI firmware
>> and the normal zImage entry point, and sets up the environment that the
>> zImage is expecting.  This includes loading the initrd (optionaly) and
>> device tree from the system partition based on the kernel command line.
>> The stub updates the device tree as necessary, including adding reserved
>> memory regions and adding entries for EFI runtime services. The PE/COFF
>> "MZ" header at offset 0 results in the first instruction being an add
>> that corrupts r5, which is not used by the zImage interface.
>
> Some more comments below ... note that I haven't really looked at the C
> code in depth.

Responses below, and I'm working on incorporating suggested changes
for the next version.

Thanks,
Roy

>
> Cheers
> ---Dave
>
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  arch/arm/boot/compressed/Makefile     |   18 +-
>>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
>>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
>>  arch/arm/boot/compressed/head.S       |   90 +++++-
>>  4 files changed, 728 insertions(+), 8 deletions(-)
>>  create mode 100644 arch/arm/boot/compressed/efi-header.S
>>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
>>
>> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
>> index 7ac1610..c62826a 100644
>> --- a/arch/arm/boot/compressed/Makefile
>> +++ b/arch/arm/boot/compressed/Makefile
>> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
>>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
>>       $(addprefix $(obj)/,$(libfdt_hdrs))
>>
>> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
>> +     $(addprefix $(obj)/,$(libfdt_hdrs))
>> +
>
> Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?
>
> Would it make sense just to add efi-stub.o to the list of targets in the
> original rule?

Yes, change made.
>
>>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
>> -OBJS += $(libfdt_objs) atags_to_fdt.o
>> +OBJS += atags_to_fdt.o
>> +USE_LIBFDT = y
>> +endif
>> +
>> +ifeq ($(CONFIG_EFI_STUB),y)
>> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
>> +OBJS += efi-stub.o
>> +USE_LIBFDT = y
>> +endif
>> +
>> +ifeq ($(USE_LIBFDT),y)
>> +OBJS += $(libfdt_objs)
>>  endif
>>
>>  targets       := vmlinux vmlinux.lds \
>> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
>>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
>>  endif
>>
>> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
>> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
>
> You don't appear to explain this change anywhere.

Prior to my changes, even though the stack protector was not disabled,
it was not actually used. GCC uses a heuristic
based on the size of the stack whether to enable the stack protector,
and the threshold to trigger its use was not met, so no stack checking
was actually being done.  In order to do stack protection, a few
__stack_chk_* functions/variable need to be provided by the
application.  I worked a bit on adding these, but could not get them
working in the stub/decompressor.  The x86 arch also has
"-fno-stack-protector" defined for its compressed boot stub, so I
decided to go that route as well.

>
>>  asflags-y := -DZIMAGE
>>
>>  # Supply kernel BSS size to the decompressor via a linker symbol.
>> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
>> new file mode 100644
>> index 0000000..6ff32cc
>> --- /dev/null
>> +++ b/arch/arm/boot/compressed/efi-header.S
>> @@ -0,0 +1,114 @@
>> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
>> +@
>> +@ This file contains the PE/COFF header that is part of the
>> +@ EFI stub.
>> +@
>> +
>> +     .org    0x3c
>> +     @
>> +     @ The PE header can be anywhere in the file, but for
>> +     @ simplicity we keep it together with the MSDOS header
>> +     @ The offset to the PE/COFF header needs to be at offset
>> +     @ 0x3C in the MSDOS header.
>> +     @ The only 2 fields of the MSDOS header that are used are this
>> +     @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
>> +     @
>> +     .long   pe_header                       @ Offset to the PE header.
>
> Is there any chance of merging this with the equivalent x86 code?
>
> The PE/COFF header is much the same in both cases, although there
> are some differences.  Maybe it would be more trouble than it is
> worth...

I think it would be more pain than gain.  We are planning to add arm64 stub
support next, so we'd end up with 4 architectures sharing this assembly file,
which I think would be painful from a patch submission/review point of view.

>
>> +
>> +      .align 3
>> +pe_header:
>> +
>> +
>> +pe_header:
>
> Duplicate label?

Yup, fixed.
>
>> +     .ascii  "PE"
>> +     .short  0
>> +
>> +coff_header:
>> +     .short  0x01c2                          @ ARM or Thumb
>> +     .short  2                               @ nr_sections
>> +     .long   0                               @ TimeDateStamp
>> +     .long   0                               @ PointerToSymbolTable
>> +     .long   1                               @ NumberOfSymbols
>> +     .short  section_table - optional_header @ SizeOfOptionalHeader
>> +     .short  0x306                           @ Characteristics.
>> +                                             @ IMAGE_FILE_32BIT_MACHINE |
>> +                                             @ IMAGE_FILE_DEBUG_STRIPPED |
>> +                                             @ IMAGE_FILE_EXECUTABLE_IMAGE |
>> +                                             @ IMAGE_FILE_LINE_NUMS_STRIPPED
>> +
>> +optional_header:
>> +     .short  0x10b                           @ PE32 format
>> +     .byte   0x02                            @ MajorLinkerVersion
>> +     .byte   0x14                            @ MinorLinkerVersion
>> +
>> +     .long   0                               @ SizeOfCode
>
> Do we need to fill in SizeOfCode with a real value?  It looks like x86
> does.
>
> We should probably fill this in unless there's a documented ABI for EFI
> boot on ARM which explicitly doesn't require these.

I will investigate/fix this.

>
>> +
>> +     .long   0                               @ SizeOfInitializedData
>> +     .long   0                               @ SizeOfUninitializedData
>> +
>> +     .long   efi_stub_entry                  @ AddressOfEntryPoint
>> +     .long   efi_stub_entry                  @ BaseOfCode
>> +     .long   0                               @ data
>> +
>> +extra_header_fields:
>> +     .long   0                               @ ImageBase
>> +     .long   0x20                            @ SectionAlignment
>> +     .long   0x20                            @ FileAlignment
>> +     .short  0                               @ MajorOperatingSystemVersion
>> +     .short  0                               @ MinorOperatingSystemVersion
>> +     .short  0                               @ MajorImageVersion
>> +     .short  0                               @ MinorImageVersion
>> +     .short  0                               @ MajorSubsystemVersion
>> +     .short  0                               @ MinorSubsystemVersion
>> +     .long   0                               @ Win32VersionValue
>> +
>> +     .long   _edata                          @ SizeOfImage
>> +
>> +     @ Everything before the entry point is considered part of the header
>> +     .long   efi_stub_entry                  @ SizeOfHeaders
>> +     .long   0                               @ CheckSum
>> +     .short  0xa                             @ Subsystem (EFI application)
>> +     .short  0                               @ DllCharacteristics
>> +     .long   0                               @ SizeOfStackReserve
>> +     .long   0                               @ SizeOfStackCommit
>> +     .long   0                               @ SizeOfHeapReserve
>> +     .long   0                               @ SizeOfHeapCommit
>> +     .long   0                               @ LoaderFlags
>> +     .long   0x0                             @ NumberOfRvaAndSizes
>> +
>> +     # Section table
>> +section_table:
>> +
>> +     #
>> +     # The EFI application loader requires a relocation section
>> +     # because EFI applications must be relocatable.  This is a
>> +     # dummy section as far as we are concerned.
>> +     #
>> +     .ascii  ".reloc"
>> +     .byte   0
>> +     .byte   0                       @ end of 0 padding of section name
>> +     .long   0
>> +     .long   0
>> +     .long   0                       @ SizeOfRawData
>> +     .long   0                       @ PointerToRawData
>> +     .long   0                       @ PointerToRelocations
>> +     .long   0                       @ PointerToLineNumbers
>> +     .short  0                       @ NumberOfRelocations
>> +     .short  0                       @ NumberOfLineNumbers
>> +     .long   0x42100040              @ Characteristics (section flags)
>> +
>> +
>> +     .ascii  ".text"
>> +     .byte   0
>> +     .byte   0
>> +     .byte   0                       @ end of 0 padding of section name
>> +     .long   _edata - efi_stub_entry         @ VirtualSize
>> +     .long   efi_stub_entry                  @ VirtualAddress
>> +     .long   _edata - efi_stub_entry         @ SizeOfRawData
>> +     .long   efi_stub_entry                  @ PointerToRawData
>> +
>> +     .long   0               @ PointerToRelocations (0 for executables)
>> +     .long   0               @ PointerToLineNumbers (0 for executables)
>> +     .short  0               @ NumberOfRelocations  (0 for executables)
>> +     .short  0               @ NumberOfLineNumbers  (0 for executables)
>> +     .long   0xe0500020      @ Characteristics (section flags)
>
> Can you explain why x86 needs an extra section (the .setup thing)?
> I haven't dug into that in enough detail to understand it yet...

I will look into that, I don't know off hand.  I simplified the header
for ARM as much as I could
for booting with EDK2.
>
>> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
>> new file mode 100644
>> index 0000000..b817ea3
>> --- /dev/null
>> +++ b/arch/arm/boot/compressed/efi-stub.c
>> @@ -0,0 +1,514 @@
>> +/*
>> + * linux/arch/arm/boot/compressed/efi-stub.c
>> + *
>> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
>> + *
>> + * This file implements the EFI boot stub for the ARM kernel
>> + *
>> + * 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 <libfdt.h>
>> +
>> +
>> +/* Error code returned to ASM code instead of valid FDT address. */
>> +#define EFI_STUB_ERROR               (~0)
>
> Can we put that into a suitable hedaer and use it in compressed/head.S,
> instead of the magic 0xffffffff?  (Assuming that value is supposed to
> match EFI_STUB_ERROR)

Yes, I will do this.
>
>> +
>> +/* EFI function call wrappers.  These are not required for
>> + * ARM, but wrappers are required for X86 to convert between
>> + * ABIs.  These wrappers are provided to allow code sharing
>> + * between X86 and ARM.  Since these wrappers directly invoke the
>> + * EFI function pointer, the function pointer type must be properly
>> + * defined, which is not the case for X86  One advantage of this is
>> + * it allows for type checking of arguments, which is not
>> + * possible with the X86 wrappers.
>> + */
>> +#define efi_call_phys0(f)                    f()
>> +#define efi_call_phys1(f, a1)                        f(a1)
>> +#define efi_call_phys2(f, a1, a2)            f(a1, a2)
>> +#define efi_call_phys3(f, a1, a2, a3)                f(a1, a2, a3)
>> +#define efi_call_phys4(f, a1, a2, a3, a4)    f(a1, a2, a3, a4)
>> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)        f(a1, a2, a3, a4, a5)
>> +
>> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
>> + * that for the decompressed kernel.  We have no easy way to tell what
>> + * the actuall size of code + data the uncompressed kernel will use.
>> + */
>> +#define MAX_UNCOMP_KERNEL_SIZE       0x02000000
>
> Can we fish the decompressed data size out of zImage, like the existing
> zImage code does?  (see compressed/head.S:207).  I don't see why this
> needs to be compile-time constant.

I am attempting to make sure all the memory used is accounted for in
the EFI memory map,
so I care not only about the uncompressed size, but also the BSS.  If
I get the uncompressed
image size, and use that for the allocation, the kernel will overwrite
memory immediately following it.
I had implemented what you suggested and ran into this problem.




>
> Someday, someone may try to grow the kernel image beyond 32M.  It would
> be nice to keep the number of things that breaks to a minimum, to ease
> potential pain later.

I picked 32 MBytes based on some discussions of the boot process, and
my understanding
is that 32 MBytes is a somewhat hard upper limit on kernel size.

>
>> +
>> +/* The kernel zImage should be located between 32 Mbytes
>> + * and 128 MBytes from the base of DRAM.  The min
>> + * address leaves space for a maximal size uncompressed image,
>> + * and the max address is due to how the zImage decompressor
>> + * picks a destination address.
>> + */
>> +#define MAX_ZIMAGE_OFFSET    0x08000000
>
> The maximum zImage offset is actually 1 less than this.  I think it's
> just the name of the macro that is misleading, since you use it
> correctly as an upper bound for memory allocation, so far as I can
> see.
>
> Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.

I'll rename this.

>
>> +#define MIN_ZIMAGE_OFFSET    MAX_UNCOMP_KERNEL_SIZE
>> +
>> +#define MAX_CMDLINE_LEN              500
>
> This is a random looking number.  Is this supposed to match something
> somewhere?  Does it serve any purpose other than acting as a sanity
> limit?
>
> If this limit doesn't exist, then an unreasonably large command-line
> passed by EFI would just lead to a memory allocation failure somewhere,
> which feels like the right behaviour...
>
> If we can avoid building in arbitrary limits, it helps avoid surprises
> later.


This is just a sanity check, which should be easy to remove.  I think
the failure mode will be a huge device tree being created,
rather than an allocation failure.  In reality I think the limit will
set by the EFI firmware - I doubt it is possible to pass a
multi-megabyte command line.

>
>> +
>> +struct fdt_region {
>> +     u64 base;
>> +     u64 size;
>> +};
>> +
>> +/*
>> + * Additional size that could be used for FDT entries added by
>> + * the UEFI OS Loader Estimation based on:
>> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
>> + * + system memory region (20bytes) + mp_core entries (200
>> + * bytes)
>> + */
>
> What does 0x300 have to do with those numbers?
>
> When you say "estimate", are we guaranteed never to exceed that?
> What happens if we do?

No guarantees, and we fail to boot if we run out of space in the new
device tree.  This greatly simplifies the code,
but I agree that it is not that nice.

>
>> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
>> +
>> +/* Include shared EFI stub code */
>> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
>> +
>> +
>> +static int is_linux_reserved_region(int memory_type)
>> +{
>> +     switch (memory_type) {
>> +     case EFI_RUNTIME_SERVICES_CODE:
>> +     case EFI_RUNTIME_SERVICES_DATA:
>> +     case EFI_UNUSABLE_MEMORY:
>> +     case EFI_ACPI_RECLAIM_MEMORY:
>> +     case EFI_ACPI_MEMORY_NVS:
>> +             return 1;
>> +     default:
>> +             return 0;
>> +     }
>> +}
>> +
>> +
>> +static int relocate_kernel(efi_system_table_t *sys_table,
>> +                        unsigned long *load_addr, unsigned long *load_size,
>> +                        unsigned long min_addr, unsigned long max_addr)
>> +{
>> +     /* Get current address of kernel. */
>> +     unsigned long cur_zimage_addr = *load_addr;
>> +     unsigned long zimage_size = *load_size;
>> +     unsigned long new_addr = 0;
>> +     unsigned long nr_pages;
>> +
>> +     efi_status_t status;
>> +
>> +     if (!load_addr || !load_size)
>> +             return EFI_INVALID_PARAMETER;
>> +
>> +     *load_size = 0;
>> +     if (cur_zimage_addr > min_addr
>> +         && (cur_zimage_addr + zimage_size) < max_addr) {
>> +             /* We don't need to do anything, as kernel at an acceptable
>> +              * address already.
>> +              */
>> +             return EFI_SUCCESS;
>> +     }
>> +     /*
>> +      * The EFI firmware loader could have placed the kernel image
>> +      * anywhere in memory, but the kernel has restrictions on the
>> +      * min and max physical address it can run at.
>> +      */
>> +     nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
>
> It looks like nr_pages is never used in this function.

Yup, removed.
>
>> +
>> +     status = efi_low_alloc(sys_table, zimage_size, 0,
>> +                        &new_addr, min_addr);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
>
> Does efi_printk automatically prepend a suitable prefix?  If not,
> it might be useful to define a macro to add a standard prefix to all
> efi_printks here ("zImage: " or similar).

It doesn't, but I can add one.  Maybe "EFIstub"?  This is really
separate from the zImage boot, so I think
it would be helpful to differentiate it.

>
> Minor nit: can we have "allocate" instead of "alloc"?
Sure.
>
> I think both messages should say "failed to allocate usable memory".
> EFI has already allocated memory for the kernel after all: it's
> just in the wrong place initially.
>
>> +             return status;
>> +     }
>> +
>> +     if (new_addr > (max_addr - zimage_size)) {
>> +             efi_free(sys_table, zimage_size, new_addr);
>> +             efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
>> +             return EFI_INVALID_PARAMETER;
>> +     }
>> +
>> +     /* We know source/dest won't overlap since both memory ranges
>> +      * have been allocated by UEFI, so we can safely use memcpy.
>> +      */
>> +     memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
>> +            zimage_size);
>
> Is it possible for this allocation to fail -- i.e., because UEFI has
> put us in an unsuitable location which is within the first 128MB of
> RAM, such that we can't pick a suitable location without overlap?

I think so, since (in theory at least), other EFI applications could have run
before us and allocated arbitrary amounts of memory.

>
> For the time being though, I think this is impossible because the
> decompressed Image can't exceed ~32MB (so the zImage should not
> exceed that either, and both can fit inside 128MB.  It doesn't
> matter if UEFI's initial load location overlaps the decompressed
> Image).

The reason I am avoiding the zImage overlapping the decompressed image
even though
the zImage decompressor handles that case is that I want to ensure that
all memory used during early boot is represented in the EFI memory map.
By avoiding overlap, I only have to deal with predicting the final
destination of the
decompressed kernel.


>
> If UEFI put reserved regions with the first 128MB we're likely to
> be dead anyway, so we shouldn't assume we'll have to cope with that
> for now...

For these cases I'd like to be able to return an error message and
refuse to boot, rather
than dying during boot.

In principle, I like the EFI stub being a shim between the EFI
firmware and the normal zImage boot.  In practice,
I don't really like having to predict/guess what memory the zImage
decompressor will use so that we can account for that
in the EFI memory map.

>
>> +
>> +     /* Return the load address and size */
>> +     *load_addr = new_addr;
>> +     *load_size = zimage_size;
>
> Is zimage_size ever changed?  It looks like it is still equal to the
> initial value of *load_size at this point.

Nope, I can get rid of zimage_size and just use *load_size throughout.

>
>> +
>> +
>> +     return status;
>> +}
>> +
>> +
>> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
>> + * Size of memory allocated return in *cmd_line_len.
>> + * Returns NULL on error.
>> + */
>> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
>> +                                   efi_loaded_image_t *image,
>> +                                   unsigned long *cmd_line_len,
>> +                                   u32 max_addr)
>> +{
>> +     u16 *s2;
>> +     u8 *s1 = NULL;
>> +     unsigned long cmdline_addr = 0;
>> +     int load_options_size = image->load_options_size / 2; /* ASCII */
>> +     void *options = (u16 *)image->load_options;
>> +     int options_size = 0;
>> +     int status;
>> +     int i;
>> +     u16 zero = 0;
>> +
>> +     if (options) {
>> +             s2 = options;
>> +             while (*s2 && *s2 != '\n' && options_size < load_options_size) {
>> +                     s2++;
>> +                     options_size++;
>> +             }
>> +     }
>> +
>> +     if (options_size == 0) {
>> +             /* No command line options, so return empty string*/
>> +             options_size = 1;
>> +             options = &zero;
>> +     }
>> +
>> +     if (options_size > MAX_CMDLINE_LEN)
>> +             options_size = MAX_CMDLINE_LEN;
>> +
>> +     options_size++;  /* NUL termination */
>
> Do we care that options_size can now be > load_options_size?
>
> I guess image->load_options isn't realistically going to be right at
> the end of a RAM bank, so probably nothing disastrous will happen if
> we read off the end of it.
>
> It would be tidier to avoid this, though.

I'll update this to avoid reading past the end of the EFI option string.

>
>> +
>> +     status = efi_high_alloc(sys_table, options_size, 0,
>> +                         &cmdline_addr, max_addr);
>> +     if (status != EFI_SUCCESS)
>> +             return NULL;
>> +
>> +     s1 = (u8 *)(unsigned long)cmdline_addr;
>> +     s2 = (u16 *)options;
>> +
>> +     for (i = 0; i < options_size - 1; i++)
>> +             *s1++ = *s2++;
>> +
>> +     *s1 = '\0';
>> +
>> +     *cmd_line_len = options_size;
>> +     return (char *)(unsigned long)cmdline_addr;
>> +}
>> +
>> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
>> +                                 void *handle, unsigned long dram_base,
>> +                                 void *orig_fdt, u64 *orig_fdt_size,
>> +                                 char *cmdline_ptr,
>> +                                 unsigned long *cmdline_size,
>> +                                 u64 initrd_addr, u64 initrd_size)
>> +{
>> +     unsigned long new_fdt_size;
>> +     unsigned long new_fdt_addr;
>> +     void *fdt;
>> +     int node;
>> +     int status;
>> +     int i;
>> +     unsigned long map_size, desc_size;
>> +     unsigned long mmap_key;
>> +     efi_memory_desc_t *memory_map;
>> +     unsigned long fdt_val;
>> +
>> +     new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
>> +     status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
>> +                         dram_base + MAX_ZIMAGE_OFFSET);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
>> +             goto fail;
>> +     }
>
> There are too many error messages in this function (and elsewhere).
> Many of them are only useful for debugging: for real use, the only
> interesting kinds of failure for the DT which will be meaningful to the
> user are "bad device tree" and "out of memory".
>
> Also, it would be desirable to make the error messages more consistent;
> currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
> and more.
>
> We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
> which mean basically the same thing.
>
> You could try wrapping fdt_setprop() with a function which tries to set
> the property and prints a suitable message if it fails, without having
> to put explicit efi_printks all over the place.

I will review all of the messages, and add a consistent prefix as you
suggested above.
>
>> +
>> +
>> +     fdt = (void *)new_fdt_addr;
>> +     status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
>> +     if (status != 0) {
>> +             efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     /* We are done with the original DTB, so free it. */
>> +     efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
>> +     *orig_fdt_size = 0;
>> +
>> +     node = fdt_subnode_offset(fdt, 0, "chosen");
>> +     if (node < 0) {
>> +             node = fdt_add_subnode(fdt, 0, "chosen");
>> +             if (node < 0) {
>> +                     efi_printk(sys_table, "Error on finding 'chosen' node\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +
>> +     if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
>> +             status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
>> +                                  strlen(cmdline_ptr) + 1);
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new bootarg\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +     /* We are done with original command line, so free it. */
>> +     efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
>> +     *cmdline_size = 0;
>> +
>> +     /* Set intird address/end in device tree, if present */
>> +     if (initrd_size != 0) {
>> +             u64 initrd_image_end;
>> +             u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
>> +             status = fdt_setprop(fdt, node, "linux,initrd-start",
>> +                                  &initrd_image_start, sizeof(u64));
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +             initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
>> +             status = fdt_setprop(fdt, node, "linux,initrd-end",
>> +                                  &initrd_image_end, sizeof(u64));
>> +             if (status) {
>> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
>> +                     goto fail_free_new_fdt;
>> +             }
>> +     }
>> +
>> +     /* Update memory map in the device tree. The memory node must
>> +      * be present in the tree.*/
>> +     node = fdt_subnode_offset(fdt, 0, "memory");
>> +     if (node < 0) {
>> +             efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +
>> +     status = efi_get_memory_map(sys_table, &memory_map, &map_size,
>> +                                 &desc_size, &mmap_key);
>> +     if (status != EFI_SUCCESS)
>> +             goto fail_free_new_fdt;
>> +
>> +     for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
>> +             efi_memory_desc_t *desc;
>> +             unsigned long m = (unsigned long)memory_map;
>> +             desc = (efi_memory_desc_t *)(m + (i * desc_size));
>> +
>> +             if (is_linux_reserved_region(desc->type)) {
>> +                     status = fdt_add_mem_rsv(fdt, desc->phys_addr,
>> +                                              desc->num_pages * EFI_PAGE_SIZE);
>> +                     if (status != 0) {
>> +                             efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
>> +                             goto fail_free_mmap;
>> +                     }
>> +             }
>> +     }
>> +
>> +
>> +     /* Add FDT entries for EFI runtime services in chosen node.
>> +      * We need to add the final memory map, so this is done at
>> +      * the very end.
>> +      */
>> +     node = fdt_subnode_offset(fdt, 0, "chosen");
>> +     fdt_val = cpu_to_fdt32((unsigned long)sys_table);
>> +     status = fdt_setprop(fdt, node, "efi-system-table",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32(desc_size);
>> +     status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32(map_size);
>> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>> +     fdt_val = cpu_to_fdt32((unsigned long)memory_map);
>> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap",
>> +                          &fdt_val, sizeof(fdt_val));
>> +     if (status) {
>> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
>> +             goto fail_free_new_fdt;
>> +     }
>
> We have one function doing two completely different jobs here (as
> documented by the name).  Can it be split?

I had it split, but due to the address/size pairs that needed to be
passed around
to free the allocated memory on error I combined them.  I'll take
another look at it.
I think pulling the allocations out of the function may make this
cleaner, and could
also make the removal of the guessed new FTD size easier to remove.
I'll need to handle
re-trying the FTD allocation in order to gracefully handle significant
growth in the DTB.


>
>> +
>> +     /* Now we need to exit boot services.  We need the key from
>> +      * the most recent read of the memory map to do this.  We can't
>> +      * free this buffer in the normal case, but do free it when
>> +      * exit_boot_services() fails or adding the memory map to the FDT
>> +      * fails.
>> +      */
>> +     status = efi_call_phys2(sys_table->boottime->exit_boot_services,
>> +                             handle, mmap_key);
>> +
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "exit boot services failed.\n");
>> +             goto fail_free_mmap;
>> +     }
>> +
>> +     return new_fdt_addr;
>> +
>> +fail_free_mmap:
>> +     efi_call_phys1(sys_table->boottime->free_pool, memory_map);
>> +
>> +fail_free_new_fdt:
>> +     efi_free(sys_table, new_fdt_size, new_fdt_addr);
>> +
>> +fail:
>> +     return 0;
>> +}
>> +
>> +
>> +int efi_entry(void *handle, efi_system_table_t *sys_table,
>> +           unsigned long *zimage_addr)
>> +{
>> +     efi_loaded_image_t *image;
>> +     int status;
>> +     unsigned long nr_pages;
>> +     const struct fdt_region *region;
>> +
>> +     void *fdt;
>> +     int err;
>> +     int node;
>> +     unsigned long zimage_size = 0;
>> +     unsigned long dram_base;
>> +     /* addr/point and size pairs for memory management*/
>> +     u64 initrd_addr;
>> +     u64 initrd_size = 0;
>> +     u64 fdt_addr;  /* Original DTB */
>> +     u64 fdt_size = 0;
>> +     u64 kernel_reserve_addr;
>> +     u64 kernel_reserve_size = 0;
>> +     char *cmdline_ptr;
>> +     unsigned long cmdline_size = 0;
>> +     unsigned long new_fdt_addr;
>> +
>> +     efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
>> +
>> +     /* Check if we were booted by the EFI firmware */
>> +     if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
>> +             goto fail;
>> +
>> +     efi_printk(sys_table, "Booting Linux using EFI stub.\n");
>> +
>> +
>> +     /* get the command line from EFI, using the LOADED_IMAGE protocol */
>> +     status = efi_call_phys3(sys_table->boottime->handle_protocol,
>> +                             handle, &proto, (void *)&image);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
>> +             goto fail;
>> +     }
>> +
>> +     /* We are going to copy this into device tree, so we don't care where in
>> +      * memory it is.
>> +      */
>> +     cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
>> +                                            &cmdline_size, 0xFFFFFFFF);
>> +     if (!cmdline_ptr) {
>> +             efi_printk(sys_table, "ERROR converting command line to ascii.\n");
>> +             goto fail;
>> +     }
>> +
>> +     /* We first load the device tree, as we need to get the base address of
>> +      * DRAM from the device tree.  The zImage, device tree, and initrd
>> +      * have address restrictions that are relative to the base of DRAM.
>> +      */
>> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
>> +                                   0xffffffff, &fdt_addr, &fdt_size);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Error loading dtb blob\n");
>> +             goto fail_free_cmdline;
>> +     }
>> +
>> +     err = fdt_check_header((void *)(unsigned long)fdt_addr);
>> +     if (err != 0) {
>> +             efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
>> +             goto fail_free_dtb;
>> +     }
>> +     if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
>> +             efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
>> +             goto fail_free_dtb;
>> +
>> +     }
>> +
>> +
>> +     /* Look up the base of DRAM from the device tree.*/
>> +     fdt = (void *)(u32)fdt_addr;
>> +     node = fdt_subnode_offset(fdt, 0, "memory");
>> +     region = fdt_getprop(fdt, node, "reg", NULL);
>> +     if (region) {
>> +             dram_base = fdt64_to_cpu(region->base);
>> +     } else {
>> +             efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
>> +             goto fail_free_dtb;
>> +     }
>> +
>> +     /* Reserve memory for the uncompressed kernel image. */
>> +     kernel_reserve_addr = dram_base;
>> +     kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
>> +     nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
>> +     status = efi_call_phys4(sys_table->boottime->allocate_pages,
>> +                             EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
>> +                             nr_pages, &kernel_reserve_addr);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
>> +             goto fail_free_dtb;
>> +     }
>> +
>> +     /* Relocate the zImage, if required. */
>> +     zimage_size = image->image_size;
>> +     status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
>> +                              dram_base + MIN_ZIMAGE_OFFSET,
>> +                              dram_base + MAX_ZIMAGE_OFFSET);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Failed to relocate kernel\n");
>> +             goto fail_free_kernel_reserve;
>> +     }
>> +
>> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
>> +                                   dram_base + MAX_ZIMAGE_OFFSET,
>> +                                   &initrd_addr, &initrd_size);
>> +     if (status != EFI_SUCCESS) {
>> +             efi_printk(sys_table, "Error loading initrd\n");
>> +             goto fail_free_zimage;
>> +     }
>> +
>> +     new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
>> +                                             dram_base, fdt, &fdt_size,
>> +                                             cmdline_ptr, &cmdline_size,
>> +                                             initrd_addr, initrd_size);
>> +
>> +     if (new_fdt_addr == 0) {
>> +             efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
>> +             goto fail_free_initrd;
>> +     }
>
> Ideally, we shouldn't have one error message for two completely
> different causes.
>
> The printk could move into update_fdt_and_exit_boot() and split
> into more specific cases.
>
>> +
>> +
>> +     /* Now we need to return the FDT address to the calling
>> +      * assembly to this can be used as part of normal boot.
>> +      */
>> +     return new_fdt_addr;
>> +
>> +fail_free_initrd:
>> +     efi_free(sys_table, initrd_size, initrd_addr);
>> +
>> +fail_free_zimage:
>> +     efi_free(sys_table, zimage_size, *zimage_addr);
>> +
>> +fail_free_kernel_reserve:
>> +     efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
>> +
>> +fail_free_dtb:
>> +     efi_free(sys_table, fdt_size, fdt_addr);
>> +
>> +fail_free_cmdline:
>> +     efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
>> +
>> +fail:
>> +     return EFI_STUB_ERROR;
>> +}
>> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
>> index 75189f1..491e752 100644
>> --- a/arch/arm/boot/compressed/head.S
>> +++ b/arch/arm/boot/compressed/head.S
>> @@ -120,21 +120,100 @@
>>   */
>>               .align
>>               .arm                            @ Always enter in ARM state
>> +             .text
>>  start:
>>               .type   start,#function
>> -             .rept   7
>> +#ifdef CONFIG_EFI_STUB
>> +             @ Magic MSDOS signature for PE/COFF + ADD opcode
>> +             .word   0x62805a4d
>
> Did you get a chance to respond to the endianness issue I raised?
For now the EFI stub only supports LE, and I need to update
the Kconfig to reflect this.  Adding BE should be possible, but I don't
plan to work on that at this time.



>> +#else
>> +             mov     r0, r0
>> +#endif
>> +             .rept   5
>>               mov     r0, r0
>>               .endr
>> -   ARM(              mov     r0, r0          )
>> -   ARM(              b       1f              )
>> - THUMB(              adr     r12, BSYM(1f)   )
>> - THUMB(              bx      r12             )
>> +
>> +             @ zimage_continue will be in ARM or thumb mode as configured
>> + THUMB(              adrl    r12, BSYM(zimage_continue))
>> + ARM(                adrl    r12, zimage_continue)
>> +             bx      r12
>
> Note that BSYM() can be used both in ARM and Thumb kernels.
>
> In any case, ARM kernels cannot contain BX instructions because we still
> support ARMv4 (which doesn't have it).
>
> I'm presuming you found zimage_continue is too far away for adr here,
> which is why you changed it.  Assuming that't the case, this might make
> sense:
>
>         adrl    r12, BSYM(zimage_continue)
>  ARM(   mov     pc, r12 )
>  THUMB( bx      r12     )

Yes, I changed this due to lack of range.

>
>> + THUMB(              .thumb                  )
>
> For tidiness, it's better to avoid this dangling .thumb ... move it
> to just before zimage_continue instead, since efi_stub_entry has to be
> ARM anyway.

OK
>
>>
>>               .word   0x016f2818              @ Magic numbers to help the loader
>>               .word   start                   @ absolute load/run zImage address
>>               .word   _edata                  @ zImage end address
>> +
>> +#ifdef CONFIG_EFI_STUB
>> +             @ Portions of the MSDOS file header must be at offset
>> +             @ 0x3c from the start of the file.  All PE/COFF headers
>> +             @ are kept contiguous for simplicity.
>> +#include "efi-header.S"
>> +
>> +efi_stub_entry:
>> +             @ The EFI stub entry point is not at a fixed address, however
>> +             @ this address must be set in the PE/COFF header.
>> +             @ EFI entry point is in A32 mode, switch to T32 if configured.
>> + THUMB(              .arm                    )
>
> ^So, you can lose .arm here too (but keep the comment -- that's valuable
> info)
>
>> + THUMB(              adr     r12, BSYM(1f)   )
>> + THUMB(              bx      r12             )
>>   THUMB(              .thumb                  )
>>  1:
>> +             @ Save lr on stack for possible return to EFI firmware.
>> +             @ Don't care about fp, but need 64 bit alignment....
>> +             stmfd   sp!, {fp, lr}
>> +
>> +             @ Save args to EFI app across got fixup call
>> +             stmfd   sp!, {r0, r1}
>
> Mostly minor coding nits follow...

I'll go through these and update the code.  I appreciate your review,
as I am new to ARM assembly.

>
>
> stmfd sp!, {r0, r1, fp, lr} ?
>
>> +             ldmfd   sp!, {r0, r1}
>> +
>> +             @ allocate space on stack for return of new entry point of
>> +             @ zImage, as EFI stub may copy the kernel.  Pass address
>> +             @ of space in r2 - EFI stub will fill in the pointer.
>> +
>> +             sub     sp, #8                  @ we only need 4 bytes,
>
> I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?
>
> kernel asm is written in the traditional syntax, which means explicit
> source and destination registers for instructions like this:
>
>         sub     sp, sp, #8
>
> Since the EFI stub code will only be built with new toolchains it
> probably doesn't matter, but it's best to be consistent for readability
> purposes.
>
>> +                                             @ but keep stack 8 byte aligned.
>> +             mov     r2, sp
>> +             @ Pass our actual runtime start address in pointer data
>> +             adr     r11, LC0                @ address of LC0 at run time
>> +             ldr     r12, [r11, #0]          @ address of LC0 at link time
>> +
>> +             sub     r3, r11, r12            @ calculate the delta offset
>> +             str     r3, [r2, #0]
>> +             bl      efi_entry
>> +
>> +             @ get new zImage entry address from stack, put into r3
>> +             ldr     r3, [sp, #0]
>> +             add     sp, #8  @ restore stack
>
>         add     sp, sp, #8
>
>> +
>> +             @ Check for error return from EFI stub (0xFFFFFFFF)
>> +             ldr     r1, =0xffffffff
>
> Minor nit, but ldr= is wasteful for this.
>
> You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
> smart enough to translate this)...
>
>> +             cmp     r0, r1
>
> ...alternatively, don't use r1 at all and do:
>
>         cmn     r0, #1
>
>> +             beq     efi_load_fail
>> +
>> +
>> +             @ Save return values of efi_entry
>> +             stmfd   sp!, {r0, r3}
>> +             bl      cache_clean_flush
>> +             bl      cache_off
>
> Why turn the cache off?  Does that mean that EFI may launch images with
> the cache enabled?
>
> If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> off is not safe.
>
> (Hmm, the UEFI spec seems to suggest "yes" for these questions)
>
>> +             ldmfd   sp!, {r0, r3}
>> +
>> +             @ put DTB address in r2, it was returned by EFI entry
>> +             mov     r2, r0
>> +             ldr     r1, =0xffffffff         @ DTB machine type
>
> mov/mvn: see above
>
>> +             mov     r0, #0  @ r0 is 0
>
> Useless comment: maybe say why you're doing this ("r0 is 0, as required
> by the kernel boot protocol", or something like that).
>
>> +
>> +             @ Branch to (possibly) relocated zImage entry that is in r3
>> +             bx      r3
>> +
>> +efi_load_fail:
>> +             @ Return EFI_LOAD_ERROR to EFI firmware on error.
>> +             @ Switch back to ARM mode for EFI is done based on
>> +             @ return address on stack
>> +             ldr     r0, =0x80000001
>> +             ldmfd   sp!, {fp, pc}
>> +#endif
>> +
>> +zimage_continue:
>>               mrs     r9, cpsr
>>  #ifdef CONFIG_ARM_VIRT_EXT
>>               bl      __hyp_stub_install      @ get into SVC mode, reversibly
>> @@ -167,7 +246,6 @@ not_angel:
>>                * by the linker here, but it should preserve r7, r8, and r9.
>>                */
>>
>> -             .text
>>
>>  #ifdef CONFIG_AUTO_ZRELADDR
>>               @ determine final kernel image address
>> --
>> 1.7.10.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
  2013-08-08 21:57       ` Roy Franz
  (?)
@ 2013-08-09  0:53         ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-09  0:53 UTC (permalink / raw)
  To: Dave Martin
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm

Dave - here is an updated patch for head.S, I'm still working on the
rest.  I will make the EFI stub depend on !CPU_ENDIAN_BE32 to make the
stub LE only for now.  I think I have addressed the rest of your
comments in head.S in the patch below.  I will send a complete new
series once I have the other changes, mostly in efi-stub.c, complete.

Thanks,
Roy



diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 75189f1..5401a3a 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -10,6 +10,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include "efi-stub.h"

  .arch armv7-a
 /*
@@ -120,21 +121,99 @@
  */
  .align
  .arm @ Always enter in ARM state
+ .text
 start:
  .type start,#function
- .rept 7
+#ifdef CONFIG_EFI_STUB
+ @ Magic MSDOS signature for PE/COFF + ADD opcode
+ .word 0x62805a4d
+#else
+ mov r0, r0
+#endif
+ .rept 5
  mov r0, r0
  .endr
-   ARM( mov r0, r0 )
-   ARM( b 1f )
- THUMB( adr r12, BSYM(1f) )
- THUMB( bx r12 )
+
+ adrl r12, BSYM(zimage_continue)
+ ARM( mov     pc, r12 )
+ THUMB( bx r12     )
+ @ zimage_continue will be in ARM or thumb mode as configured

  .word 0x016f2818 @ Magic numbers to help the loader
  .word start @ absolute load/run zImage address
  .word _edata @ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+ @ Portions of the MSDOS file header must be at offset
+ @ 0x3c from the start of the file.  All PE/COFF headers
+ @ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+ @ The EFI stub entry point is not at a fixed address, however
+ @ this address must be set in the PE/COFF header.
+ @ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB( adr r12, BSYM(1f) )
+ THUMB( bx r12 )
  THUMB( .thumb )
 1:
+ @ Save lr on stack for possible return to EFI firmware.
+ @ Don't care about fp, but need 64 bit alignment....
+ stmfd sp!, {fp, lr}
+
+ @ allocate space on stack for return of new entry point of
+ @ zImage, as EFI stub may copy the kernel.  Pass address
+ @ of space in r2 - EFI stub will fill in the pointer.
+
+ sub sp, sp, #8 @ we only need 4 bytes,
+ @ but keep stack 8 byte aligned.
+ mov r2, sp
+ @ Pass our actual runtime start address in pointer data
+ adr r11, LC0 @ address of LC0 at run time
+ ldr r12, [r11, #0] @ address of LC0 at link time
+
+ sub r3, r11, r12 @ calculate the delta offset
+ str r3, [r2, #0]
+ bl efi_entry
+
+ @ get new zImage entry address from stack, put into r3
+ ldr r3, [sp, #0]
+ add sp, sp, #8   @ restore stack
+
+ @ Check for error return from EFI stub
+ mov r1, #EFI_STUB_ERROR
+ cmp r0, r1
+ beq efi_load_fail
+
+
+ @ Save return values of efi_entry
+ stmfd sp!, {r0, r3}
+ bl cache_clean_flush
+ bl cache_off
+ ldmfd   sp!, {r0, r3}
+
+ @ Set parameters for booting zImage according to boot protocol
+ @ put FDT address in r2, it was returned by efi_entry()
+ @ r1 is FDT machine type, and r0 needs to be 0
+ mov r2, r0
+ mov r1, #0xFFFFFFFF
+ mov r0, #0
+
+ @ Branch to (possibly) relocated zImage that is in r3
+ @ Make sure we are in A32 mode, as zImage requires
+ THUMB( bx r3 )
+ ARM( mov pc, r3 )
+
+efi_load_fail:
+ @ Return EFI_LOAD_ERROR to EFI firmware on error.
+ @ Switch back to ARM mode for EFI is done based on
+ @ return address on stack
+ ldr r0, =0x80000001
+ ldmfd sp!, {fp, pc}
+#endif
+
+ THUMB( .thumb )
+zimage_continue:
  mrs r9, cpsr
 #ifdef CONFIG_ARM_VIRT_EXT
  bl __hyp_stub_install @ get into SVC mode, reversibly
@@ -167,7 +246,6 @@ not_angel:
  * by the linker here, but it should preserve r7, r8, and r9.
  */

- .text

 #ifdef CONFIG_AUTO_ZRELADDR
  @ determine final kernel image address

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-09  0:53         ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-09  0:53 UTC (permalink / raw)
  To: Dave Martin
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm

Dave - here is an updated patch for head.S, I'm still working on the
rest.  I will make the EFI stub depend on !CPU_ENDIAN_BE32 to make the
stub LE only for now.  I think I have addressed the rest of your
comments in head.S in the patch below.  I will send a complete new
series once I have the other changes, mostly in efi-stub.c, complete.

Thanks,
Roy



diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 75189f1..5401a3a 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -10,6 +10,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include "efi-stub.h"

  .arch armv7-a
 /*
@@ -120,21 +121,99 @@
  */
  .align
  .arm @ Always enter in ARM state
+ .text
 start:
  .type start,#function
- .rept 7
+#ifdef CONFIG_EFI_STUB
+ @ Magic MSDOS signature for PE/COFF + ADD opcode
+ .word 0x62805a4d
+#else
+ mov r0, r0
+#endif
+ .rept 5
  mov r0, r0
  .endr
-   ARM( mov r0, r0 )
-   ARM( b 1f )
- THUMB( adr r12, BSYM(1f) )
- THUMB( bx r12 )
+
+ adrl r12, BSYM(zimage_continue)
+ ARM( mov     pc, r12 )
+ THUMB( bx r12     )
+ @ zimage_continue will be in ARM or thumb mode as configured

  .word 0x016f2818 @ Magic numbers to help the loader
  .word start @ absolute load/run zImage address
  .word _edata @ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+ @ Portions of the MSDOS file header must be at offset
+ @ 0x3c from the start of the file.  All PE/COFF headers
+ @ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+ @ The EFI stub entry point is not at a fixed address, however
+ @ this address must be set in the PE/COFF header.
+ @ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB( adr r12, BSYM(1f) )
+ THUMB( bx r12 )
  THUMB( .thumb )
 1:
+ @ Save lr on stack for possible return to EFI firmware.
+ @ Don't care about fp, but need 64 bit alignment....
+ stmfd sp!, {fp, lr}
+
+ @ allocate space on stack for return of new entry point of
+ @ zImage, as EFI stub may copy the kernel.  Pass address
+ @ of space in r2 - EFI stub will fill in the pointer.
+
+ sub sp, sp, #8 @ we only need 4 bytes,
+ @ but keep stack 8 byte aligned.
+ mov r2, sp
+ @ Pass our actual runtime start address in pointer data
+ adr r11, LC0 @ address of LC0 at run time
+ ldr r12, [r11, #0] @ address of LC0 at link time
+
+ sub r3, r11, r12 @ calculate the delta offset
+ str r3, [r2, #0]
+ bl efi_entry
+
+ @ get new zImage entry address from stack, put into r3
+ ldr r3, [sp, #0]
+ add sp, sp, #8   @ restore stack
+
+ @ Check for error return from EFI stub
+ mov r1, #EFI_STUB_ERROR
+ cmp r0, r1
+ beq efi_load_fail
+
+
+ @ Save return values of efi_entry
+ stmfd sp!, {r0, r3}
+ bl cache_clean_flush
+ bl cache_off
+ ldmfd   sp!, {r0, r3}
+
+ @ Set parameters for booting zImage according to boot protocol
+ @ put FDT address in r2, it was returned by efi_entry()
+ @ r1 is FDT machine type, and r0 needs to be 0
+ mov r2, r0
+ mov r1, #0xFFFFFFFF
+ mov r0, #0
+
+ @ Branch to (possibly) relocated zImage that is in r3
+ @ Make sure we are in A32 mode, as zImage requires
+ THUMB( bx r3 )
+ ARM( mov pc, r3 )
+
+efi_load_fail:
+ @ Return EFI_LOAD_ERROR to EFI firmware on error.
+ @ Switch back to ARM mode for EFI is done based on
+ @ return address on stack
+ ldr r0, =0x80000001
+ ldmfd sp!, {fp, pc}
+#endif
+
+ THUMB( .thumb )
+zimage_continue:
  mrs r9, cpsr
 #ifdef CONFIG_ARM_VIRT_EXT
  bl __hyp_stub_install @ get into SVC mode, reversibly
@@ -167,7 +246,6 @@ not_angel:
  * by the linker here, but it should preserve r7, r8, and r9.
  */

- .text

 #ifdef CONFIG_AUTO_ZRELADDR
  @ determine final kernel image address

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-09  0:53         ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-09  0:53 UTC (permalink / raw)
  To: linux-arm-kernel

Dave - here is an updated patch for head.S, I'm still working on the
rest.  I will make the EFI stub depend on !CPU_ENDIAN_BE32 to make the
stub LE only for now.  I think I have addressed the rest of your
comments in head.S in the patch below.  I will send a complete new
series once I have the other changes, mostly in efi-stub.c, complete.

Thanks,
Roy



diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 75189f1..5401a3a 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -10,6 +10,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include "efi-stub.h"

  .arch armv7-a
 /*
@@ -120,21 +121,99 @@
  */
  .align
  .arm @ Always enter in ARM state
+ .text
 start:
  .type start,#function
- .rept 7
+#ifdef CONFIG_EFI_STUB
+ @ Magic MSDOS signature for PE/COFF + ADD opcode
+ .word 0x62805a4d
+#else
+ mov r0, r0
+#endif
+ .rept 5
  mov r0, r0
  .endr
-   ARM( mov r0, r0 )
-   ARM( b 1f )
- THUMB( adr r12, BSYM(1f) )
- THUMB( bx r12 )
+
+ adrl r12, BSYM(zimage_continue)
+ ARM( mov     pc, r12 )
+ THUMB( bx r12     )
+ @ zimage_continue will be in ARM or thumb mode as configured

  .word 0x016f2818 @ Magic numbers to help the loader
  .word start @ absolute load/run zImage address
  .word _edata @ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+ @ Portions of the MSDOS file header must be at offset
+ @ 0x3c from the start of the file.  All PE/COFF headers
+ @ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+ @ The EFI stub entry point is not at a fixed address, however
+ @ this address must be set in the PE/COFF header.
+ @ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB( adr r12, BSYM(1f) )
+ THUMB( bx r12 )
  THUMB( .thumb )
 1:
+ @ Save lr on stack for possible return to EFI firmware.
+ @ Don't care about fp, but need 64 bit alignment....
+ stmfd sp!, {fp, lr}
+
+ @ allocate space on stack for return of new entry point of
+ @ zImage, as EFI stub may copy the kernel.  Pass address
+ @ of space in r2 - EFI stub will fill in the pointer.
+
+ sub sp, sp, #8 @ we only need 4 bytes,
+ @ but keep stack 8 byte aligned.
+ mov r2, sp
+ @ Pass our actual runtime start address in pointer data
+ adr r11, LC0 @ address of LC0 at run time
+ ldr r12, [r11, #0] @ address of LC0 at link time
+
+ sub r3, r11, r12 @ calculate the delta offset
+ str r3, [r2, #0]
+ bl efi_entry
+
+ @ get new zImage entry address from stack, put into r3
+ ldr r3, [sp, #0]
+ add sp, sp, #8   @ restore stack
+
+ @ Check for error return from EFI stub
+ mov r1, #EFI_STUB_ERROR
+ cmp r0, r1
+ beq efi_load_fail
+
+
+ @ Save return values of efi_entry
+ stmfd sp!, {r0, r3}
+ bl cache_clean_flush
+ bl cache_off
+ ldmfd   sp!, {r0, r3}
+
+ @ Set parameters for booting zImage according to boot protocol
+ @ put FDT address in r2, it was returned by efi_entry()
+ @ r1 is FDT machine type, and r0 needs to be 0
+ mov r2, r0
+ mov r1, #0xFFFFFFFF
+ mov r0, #0
+
+ @ Branch to (possibly) relocated zImage that is in r3
+ @ Make sure we are in A32 mode, as zImage requires
+ THUMB( bx r3 )
+ ARM( mov pc, r3 )
+
+efi_load_fail:
+ @ Return EFI_LOAD_ERROR to EFI firmware on error.
+ @ Switch back to ARM mode for EFI is done based on
+ @ return address on stack
+ ldr r0, =0x80000001
+ ldmfd sp!, {fp, pc}
+#endif
+
+ THUMB( .thumb )
+zimage_continue:
  mrs r9, cpsr
 #ifdef CONFIG_ARM_VIRT_EXT
  bl __hyp_stub_install @ get into SVC mode, reversibly
@@ -167,7 +246,6 @@ not_angel:
  * by the linker here, but it should preserve r7, r8, and r9.
  */

- .text

 #ifdef CONFIG_AUTO_ZRELADDR
  @ determine final kernel image address

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-09 14:10     ` Mark Salter
  0 siblings, 0 replies; 88+ messages in thread
From: Mark Salter @ 2013-08-09 14:10 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming, linux,
	leif.lindholm, dave.martin

On Tue, 2013-08-06 at 20:45 -0700, Roy Franz wrote:
> The x86/AMD64 EFI stubs must us a call wrapper to convert between
> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
> the ABIs are compatible, so we can directly invoke the function
> pointers.  The functions that are used by the ARM stub are updated
> to match the EFI definitions.
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 51f5641..96bb866 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,8 @@
>  typedef unsigned long efi_status_t;
>  typedef u8 efi_bool_t;
>  typedef u16 efi_char16_t;		/* UNICODE character */
> +typedef u64 efi_physical_addr_t;
> +typedef void *efi_handle_t;
>  
> 
>  typedef struct {
> @@ -96,6 +98,7 @@ typedef	struct {
>  #define EFI_MEMORY_DESCRIPTOR_VERSION	1
>  
>  #define EFI_PAGE_SHIFT		12
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
>  
>  typedef struct {
>  	u32 type;
> @@ -157,11 +160,12 @@ typedef struct {
>  	efi_table_hdr_t hdr;
>  	void *raise_tpl;
>  	void *restore_tpl;
> -	void *allocate_pages;
> -	void *free_pages;
> -	void *get_memory_map;
> -	void *allocate_pool;
> -	void *free_pool;
> +	int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
> +	int (*free_pages)(efi_physical_addr_t, unsigned long);

All of the actual function pointers that were added should return an
efi_status_t instead of int. On arm64, I was seeing an unrecognizable
error status because the truncation from 64 to 32 bits.

--Mark



^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-09 14:10     ` Mark Salter
  0 siblings, 0 replies; 88+ messages in thread
From: Mark Salter @ 2013-08-09 14:10 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w,
	linux-lFZ/pmaqli7XmaaqVzeoHQ,
	leif.lindholm-QSEj5FYQhm4dnm+yROfE0A, dave.martin-5wv7dgnIgG8

On Tue, 2013-08-06 at 20:45 -0700, Roy Franz wrote:
> The x86/AMD64 EFI stubs must us a call wrapper to convert between
> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
> the ABIs are compatible, so we can directly invoke the function
> pointers.  The functions that are used by the ARM stub are updated
> to match the EFI definitions.
> 
> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 51f5641..96bb866 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,8 @@
>  typedef unsigned long efi_status_t;
>  typedef u8 efi_bool_t;
>  typedef u16 efi_char16_t;		/* UNICODE character */
> +typedef u64 efi_physical_addr_t;
> +typedef void *efi_handle_t;
>  
> 
>  typedef struct {
> @@ -96,6 +98,7 @@ typedef	struct {
>  #define EFI_MEMORY_DESCRIPTOR_VERSION	1
>  
>  #define EFI_PAGE_SHIFT		12
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
>  
>  typedef struct {
>  	u32 type;
> @@ -157,11 +160,12 @@ typedef struct {
>  	efi_table_hdr_t hdr;
>  	void *raise_tpl;
>  	void *restore_tpl;
> -	void *allocate_pages;
> -	void *free_pages;
> -	void *get_memory_map;
> -	void *allocate_pool;
> -	void *free_pool;
> +	int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
> +	int (*free_pages)(efi_physical_addr_t, unsigned long);

All of the actual function pointers that were added should return an
efi_status_t instead of int. On arm64, I was seeing an unrecognizable
error status because the truncation from 64 to 32 bits.

--Mark

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-09 14:10     ` Mark Salter
  0 siblings, 0 replies; 88+ messages in thread
From: Mark Salter @ 2013-08-09 14:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-08-06 at 20:45 -0700, Roy Franz wrote:
> The x86/AMD64 EFI stubs must us a call wrapper to convert between
> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
> the ABIs are compatible, so we can directly invoke the function
> pointers.  The functions that are used by the ARM stub are updated
> to match the EFI definitions.
> 
> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> ---
>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 51f5641..96bb866 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,8 @@
>  typedef unsigned long efi_status_t;
>  typedef u8 efi_bool_t;
>  typedef u16 efi_char16_t;		/* UNICODE character */
> +typedef u64 efi_physical_addr_t;
> +typedef void *efi_handle_t;
>  
> 
>  typedef struct {
> @@ -96,6 +98,7 @@ typedef	struct {
>  #define EFI_MEMORY_DESCRIPTOR_VERSION	1
>  
>  #define EFI_PAGE_SHIFT		12
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
>  
>  typedef struct {
>  	u32 type;
> @@ -157,11 +160,12 @@ typedef struct {
>  	efi_table_hdr_t hdr;
>  	void *raise_tpl;
>  	void *restore_tpl;
> -	void *allocate_pages;
> -	void *free_pages;
> -	void *get_memory_map;
> -	void *allocate_pool;
> -	void *free_pool;
> +	int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
> +	int (*free_pages)(efi_physical_addr_t, unsigned long);

All of the actual function pointers that were added should return an
efi_status_t instead of int. On arm64, I was seeing an unrecognizable
error status because the truncation from 64 to 32 bits.

--Mark

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
  2013-08-09 14:10     ` Mark Salter
  (?)
@ 2013-08-09 14:13       ` Roy Franz
  -1 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-09 14:13 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

Thanks Mark - I'll get that in the next version of the patches.

On Fri, Aug 9, 2013 at 7:10 AM, Mark Salter <msalter@redhat.com> wrote:
> On Tue, 2013-08-06 at 20:45 -0700, Roy Franz wrote:
>> The x86/AMD64 EFI stubs must us a call wrapper to convert between
>> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
>> the ABIs are compatible, so we can directly invoke the function
>> pointers.  The functions that are used by the ARM stub are updated
>> to match the EFI definitions.
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>>  1 file changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/efi.h b/include/linux/efi.h
>> index 51f5641..96bb866 100644
>> --- a/include/linux/efi.h
>> +++ b/include/linux/efi.h
>> @@ -39,6 +39,8 @@
>>  typedef unsigned long efi_status_t;
>>  typedef u8 efi_bool_t;
>>  typedef u16 efi_char16_t;            /* UNICODE character */
>> +typedef u64 efi_physical_addr_t;
>> +typedef void *efi_handle_t;
>>
>>
>>  typedef struct {
>> @@ -96,6 +98,7 @@ typedef     struct {
>>  #define EFI_MEMORY_DESCRIPTOR_VERSION        1
>>
>>  #define EFI_PAGE_SHIFT               12
>> +#define EFI_PAGE_SIZE                (1UL << EFI_PAGE_SHIFT)
>>
>>  typedef struct {
>>       u32 type;
>> @@ -157,11 +160,12 @@ typedef struct {
>>       efi_table_hdr_t hdr;
>>       void *raise_tpl;
>>       void *restore_tpl;
>> -     void *allocate_pages;
>> -     void *free_pages;
>> -     void *get_memory_map;
>> -     void *allocate_pool;
>> -     void *free_pool;
>> +     int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
>> +     int (*free_pages)(efi_physical_addr_t, unsigned long);
>
> All of the actual function pointers that were added should return an
> efi_status_t instead of int. On arm64, I was seeing an unrecognizable
> error status because the truncation from 64 to 32 bits.
>
> --Mark
>
>

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-09 14:13       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-09 14:13 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm, Dave Martin

Thanks Mark - I'll get that in the next version of the patches.

On Fri, Aug 9, 2013 at 7:10 AM, Mark Salter <msalter@redhat.com> wrote:
> On Tue, 2013-08-06 at 20:45 -0700, Roy Franz wrote:
>> The x86/AMD64 EFI stubs must us a call wrapper to convert between
>> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
>> the ABIs are compatible, so we can directly invoke the function
>> pointers.  The functions that are used by the ARM stub are updated
>> to match the EFI definitions.
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>>  1 file changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/efi.h b/include/linux/efi.h
>> index 51f5641..96bb866 100644
>> --- a/include/linux/efi.h
>> +++ b/include/linux/efi.h
>> @@ -39,6 +39,8 @@
>>  typedef unsigned long efi_status_t;
>>  typedef u8 efi_bool_t;
>>  typedef u16 efi_char16_t;            /* UNICODE character */
>> +typedef u64 efi_physical_addr_t;
>> +typedef void *efi_handle_t;
>>
>>
>>  typedef struct {
>> @@ -96,6 +98,7 @@ typedef     struct {
>>  #define EFI_MEMORY_DESCRIPTOR_VERSION        1
>>
>>  #define EFI_PAGE_SHIFT               12
>> +#define EFI_PAGE_SIZE                (1UL << EFI_PAGE_SHIFT)
>>
>>  typedef struct {
>>       u32 type;
>> @@ -157,11 +160,12 @@ typedef struct {
>>       efi_table_hdr_t hdr;
>>       void *raise_tpl;
>>       void *restore_tpl;
>> -     void *allocate_pages;
>> -     void *free_pages;
>> -     void *get_memory_map;
>> -     void *allocate_pool;
>> -     void *free_pool;
>> +     int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
>> +     int (*free_pages)(efi_physical_addr_t, unsigned long);
>
> All of the actual function pointers that were added should return an
> efi_status_t instead of int. On arm64, I was seeing an unrecognizable
> error status because the truncation from 64 to 32 bits.
>
> --Mark
>
>

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 12/17] Add proper definitions for some EFI function pointers.
@ 2013-08-09 14:13       ` Roy Franz
  0 siblings, 0 replies; 88+ messages in thread
From: Roy Franz @ 2013-08-09 14:13 UTC (permalink / raw)
  To: linux-arm-kernel

Thanks Mark - I'll get that in the next version of the patches.

On Fri, Aug 9, 2013 at 7:10 AM, Mark Salter <msalter@redhat.com> wrote:
> On Tue, 2013-08-06 at 20:45 -0700, Roy Franz wrote:
>> The x86/AMD64 EFI stubs must us a call wrapper to convert between
>> the Linux and EFI ABIs, so void pointers are sufficient.  For ARM,
>> the ABIs are compatible, so we can directly invoke the function
>> pointers.  The functions that are used by the ARM stub are updated
>> to match the EFI definitions.
>>
>> Signed-off-by: Roy Franz <roy.franz@linaro.org>
>> ---
>>  include/linux/efi.h |   42 +++++++++++++++++++++++++-----------------
>>  1 file changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/efi.h b/include/linux/efi.h
>> index 51f5641..96bb866 100644
>> --- a/include/linux/efi.h
>> +++ b/include/linux/efi.h
>> @@ -39,6 +39,8 @@
>>  typedef unsigned long efi_status_t;
>>  typedef u8 efi_bool_t;
>>  typedef u16 efi_char16_t;            /* UNICODE character */
>> +typedef u64 efi_physical_addr_t;
>> +typedef void *efi_handle_t;
>>
>>
>>  typedef struct {
>> @@ -96,6 +98,7 @@ typedef     struct {
>>  #define EFI_MEMORY_DESCRIPTOR_VERSION        1
>>
>>  #define EFI_PAGE_SHIFT               12
>> +#define EFI_PAGE_SIZE                (1UL << EFI_PAGE_SHIFT)
>>
>>  typedef struct {
>>       u32 type;
>> @@ -157,11 +160,12 @@ typedef struct {
>>       efi_table_hdr_t hdr;
>>       void *raise_tpl;
>>       void *restore_tpl;
>> -     void *allocate_pages;
>> -     void *free_pages;
>> -     void *get_memory_map;
>> -     void *allocate_pool;
>> -     void *free_pool;
>> +     int (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *);
>> +     int (*free_pages)(efi_physical_addr_t, unsigned long);
>
> All of the actual function pointers that were added should return an
> efi_status_t instead of int. On arm64, I was seeing an unrecognizable
> error status because the truncation from 64 to 32 bits.
>
> --Mark
>
>

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-13 14:21         ` Dave P Martin
  0 siblings, 0 replies; 88+ messages in thread
From: Dave P Martin @ 2013-08-13 14:21 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel, linux-efi, linux-arm-kernel, matt.fleming,
	Russell King - ARM Linux, Leif Lindholm

On Thu, Aug 08, 2013 at 10:57:29PM +0100, Roy Franz wrote:
> On Wed, Aug 7, 2013 at 11:05 AM, Dave Martin <Dave.Martin@arm.com> wrote:
> > On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
> >> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
> >> operations similarly to the x86 stub: it is a shim between the EFI firmware
> >> and the normal zImage entry point, and sets up the environment that the
> >> zImage is expecting.  This includes loading the initrd (optionaly) and
> >> device tree from the system partition based on the kernel command line.
> >> The stub updates the device tree as necessary, including adding reserved
> >> memory regions and adding entries for EFI runtime services. The PE/COFF
> >> "MZ" header at offset 0 results in the first instruction being an add
> >> that corrupts r5, which is not used by the zImage interface.
> >
> > Some more comments below ... note that I haven't really looked at the C
> > code in depth.
> 
> Responses below, and I'm working on incorporating suggested changes
> for the next version.

I few responses-to-responses from me inline.  Your repose supersedes
most of this anyhow.

Cheers
---Dave

> 
> Thanks,
> Roy
> 
> >
> > Cheers
> > ---Dave
> >
> >>
> >> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> >> ---
> >>  arch/arm/boot/compressed/Makefile     |   18 +-
> >>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
> >>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
> >>  arch/arm/boot/compressed/head.S       |   90 +++++-
> >>  4 files changed, 728 insertions(+), 8 deletions(-)
> >>  create mode 100644 arch/arm/boot/compressed/efi-header.S
> >>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
> >>
> >> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> >> index 7ac1610..c62826a 100644
> >> --- a/arch/arm/boot/compressed/Makefile
> >> +++ b/arch/arm/boot/compressed/Makefile
> >> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
> >>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
> >>       $(addprefix $(obj)/,$(libfdt_hdrs))
> >>
> >> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
> >> +     $(addprefix $(obj)/,$(libfdt_hdrs))
> >> +
> >
> > Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?
> >
> > Would it make sense just to add efi-stub.o to the list of targets in the
> > original rule?
> 
> Yes, change made.
> >
> >>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
> >> -OBJS += $(libfdt_objs) atags_to_fdt.o
> >> +OBJS += atags_to_fdt.o
> >> +USE_LIBFDT = y
> >> +endif
> >> +
> >> +ifeq ($(CONFIG_EFI_STUB),y)
> >> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
> >> +OBJS += efi-stub.o
> >> +USE_LIBFDT = y
> >> +endif
> >> +
> >> +ifeq ($(USE_LIBFDT),y)
> >> +OBJS += $(libfdt_objs)
> >>  endif
> >>
> >>  targets       := vmlinux vmlinux.lds \
> >> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
> >>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
> >>  endif
> >>
> >> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
> >> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
> >
> > You don't appear to explain this change anywhere.
> 
> Prior to my changes, even though the stack protector was not disabled,
> it was not actually used. GCC uses a heuristic
> based on the size of the stack whether to enable the stack protector,
> and the threshold to trigger its use was not met, so no stack checking
> was actually being done.  In order to do stack protection, a few
> __stack_chk_* functions/variable need to be provided by the
> application.  I worked a bit on adding these, but could not get them
> working in the stub/decompressor.  The x86 arch also has
> "-fno-stack-protector" defined for its compressed boot stub, so I
> decided to go that route as well.
> 
> >
> >>  asflags-y := -DZIMAGE
> >>
> >>  # Supply kernel BSS size to the decompressor via a linker symbol.
> >> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
> >> new file mode 100644
> >> index 0000000..6ff32cc
> >> --- /dev/null
> >> +++ b/arch/arm/boot/compressed/efi-header.S
> >> @@ -0,0 +1,114 @@
> >> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> >> +@
> >> +@ This file contains the PE/COFF header that is part of the
> >> +@ EFI stub.
> >> +@
> >> +
> >> +     .org    0x3c
> >> +     @
> >> +     @ The PE header can be anywhere in the file, but for
> >> +     @ simplicity we keep it together with the MSDOS header
> >> +     @ The offset to the PE/COFF header needs to be at offset
> >> +     @ 0x3C in the MSDOS header.
> >> +     @ The only 2 fields of the MSDOS header that are used are this
> >> +     @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
> >> +     @
> >> +     .long   pe_header                       @ Offset to the PE header.
> >
> > Is there any chance of merging this with the equivalent x86 code?
> >
> > The PE/COFF header is much the same in both cases, although there
> > are some differences.  Maybe it would be more trouble than it is
> > worth...
> 
> I think it would be more pain than gain.  We are planning to add arm64 stub
> support next, so we'd end up with 4 architectures sharing this assembly file,
> which I think would be painful from a patch submission/review point of view.
> 
> >
> >> +
> >> +      .align 3
> >> +pe_header:
> >> +
> >> +
> >> +pe_header:
> >
> > Duplicate label?
> 
> Yup, fixed.
> >
> >> +     .ascii  "PE"
> >> +     .short  0
> >> +
> >> +coff_header:
> >> +     .short  0x01c2                          @ ARM or Thumb
> >> +     .short  2                               @ nr_sections
> >> +     .long   0                               @ TimeDateStamp
> >> +     .long   0                               @ PointerToSymbolTable
> >> +     .long   1                               @ NumberOfSymbols
> >> +     .short  section_table - optional_header @ SizeOfOptionalHeader
> >> +     .short  0x306                           @ Characteristics.
> >> +                                             @ IMAGE_FILE_32BIT_MACHINE |
> >> +                                             @ IMAGE_FILE_DEBUG_STRIPPED |
> >> +                                             @ IMAGE_FILE_EXECUTABLE_IMAGE |
> >> +                                             @ IMAGE_FILE_LINE_NUMS_STRIPPED
> >> +
> >> +optional_header:
> >> +     .short  0x10b                           @ PE32 format
> >> +     .byte   0x02                            @ MajorLinkerVersion
> >> +     .byte   0x14                            @ MinorLinkerVersion
> >> +
> >> +     .long   0                               @ SizeOfCode
> >
> > Do we need to fill in SizeOfCode with a real value?  It looks like x86
> > does.
> >
> > We should probably fill this in unless there's a documented ABI for EFI
> > boot on ARM which explicitly doesn't require these.
> 
> I will investigate/fix this.
> 
> >
> >> +
> >> +     .long   0                               @ SizeOfInitializedData
> >> +     .long   0                               @ SizeOfUninitializedData
> >> +
> >> +     .long   efi_stub_entry                  @ AddressOfEntryPoint
> >> +     .long   efi_stub_entry                  @ BaseOfCode
> >> +     .long   0                               @ data
> >> +
> >> +extra_header_fields:
> >> +     .long   0                               @ ImageBase
> >> +     .long   0x20                            @ SectionAlignment
> >> +     .long   0x20                            @ FileAlignment
> >> +     .short  0                               @ MajorOperatingSystemVersion
> >> +     .short  0                               @ MinorOperatingSystemVersion
> >> +     .short  0                               @ MajorImageVersion
> >> +     .short  0                               @ MinorImageVersion
> >> +     .short  0                               @ MajorSubsystemVersion
> >> +     .short  0                               @ MinorSubsystemVersion
> >> +     .long   0                               @ Win32VersionValue
> >> +
> >> +     .long   _edata                          @ SizeOfImage
> >> +
> >> +     @ Everything before the entry point is considered part of the header
> >> +     .long   efi_stub_entry                  @ SizeOfHeaders
> >> +     .long   0                               @ CheckSum
> >> +     .short  0xa                             @ Subsystem (EFI application)
> >> +     .short  0                               @ DllCharacteristics
> >> +     .long   0                               @ SizeOfStackReserve
> >> +     .long   0                               @ SizeOfStackCommit
> >> +     .long   0                               @ SizeOfHeapReserve
> >> +     .long   0                               @ SizeOfHeapCommit
> >> +     .long   0                               @ LoaderFlags
> >> +     .long   0x0                             @ NumberOfRvaAndSizes
> >> +
> >> +     # Section table
> >> +section_table:
> >> +
> >> +     #
> >> +     # The EFI application loader requires a relocation section
> >> +     # because EFI applications must be relocatable.  This is a
> >> +     # dummy section as far as we are concerned.
> >> +     #
> >> +     .ascii  ".reloc"
> >> +     .byte   0
> >> +     .byte   0                       @ end of 0 padding of section name
> >> +     .long   0
> >> +     .long   0
> >> +     .long   0                       @ SizeOfRawData
> >> +     .long   0                       @ PointerToRawData
> >> +     .long   0                       @ PointerToRelocations
> >> +     .long   0                       @ PointerToLineNumbers
> >> +     .short  0                       @ NumberOfRelocations
> >> +     .short  0                       @ NumberOfLineNumbers
> >> +     .long   0x42100040              @ Characteristics (section flags)
> >> +
> >> +
> >> +     .ascii  ".text"
> >> +     .byte   0
> >> +     .byte   0
> >> +     .byte   0                       @ end of 0 padding of section name
> >> +     .long   _edata - efi_stub_entry         @ VirtualSize
> >> +     .long   efi_stub_entry                  @ VirtualAddress
> >> +     .long   _edata - efi_stub_entry         @ SizeOfRawData
> >> +     .long   efi_stub_entry                  @ PointerToRawData
> >> +
> >> +     .long   0               @ PointerToRelocations (0 for executables)
> >> +     .long   0               @ PointerToLineNumbers (0 for executables)
> >> +     .short  0               @ NumberOfRelocations  (0 for executables)
> >> +     .short  0               @ NumberOfLineNumbers  (0 for executables)
> >> +     .long   0xe0500020      @ Characteristics (section flags)
> >
> > Can you explain why x86 needs an extra section (the .setup thing)?
> > I haven't dug into that in enough detail to understand it yet...
> 
> I will look into that, I don't know off hand.  I simplified the header
> for ARM as much as I could
> for booting with EDK2.
> >
> >> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> >> new file mode 100644
> >> index 0000000..b817ea3
> >> --- /dev/null
> >> +++ b/arch/arm/boot/compressed/efi-stub.c
> >> @@ -0,0 +1,514 @@
> >> +/*
> >> + * linux/arch/arm/boot/compressed/efi-stub.c
> >> + *
> >> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> >> + *
> >> + * This file implements the EFI boot stub for the ARM kernel
> >> + *
> >> + * 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 <libfdt.h>
> >> +
> >> +
> >> +/* Error code returned to ASM code instead of valid FDT address. */
> >> +#define EFI_STUB_ERROR               (~0)
> >
> > Can we put that into a suitable hedaer and use it in compressed/head.S,
> > instead of the magic 0xffffffff?  (Assuming that value is supposed to
> > match EFI_STUB_ERROR)
> 
> Yes, I will do this.
> >
> >> +
> >> +/* EFI function call wrappers.  These are not required for
> >> + * ARM, but wrappers are required for X86 to convert between
> >> + * ABIs.  These wrappers are provided to allow code sharing
> >> + * between X86 and ARM.  Since these wrappers directly invoke the
> >> + * EFI function pointer, the function pointer type must be properly
> >> + * defined, which is not the case for X86  One advantage of this is
> >> + * it allows for type checking of arguments, which is not
> >> + * possible with the X86 wrappers.
> >> + */
> >> +#define efi_call_phys0(f)                    f()
> >> +#define efi_call_phys1(f, a1)                        f(a1)
> >> +#define efi_call_phys2(f, a1, a2)            f(a1, a2)
> >> +#define efi_call_phys3(f, a1, a2, a3)                f(a1, a2, a3)
> >> +#define efi_call_phys4(f, a1, a2, a3, a4)    f(a1, a2, a3, a4)
> >> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)        f(a1, a2, a3, a4, a5)
> >> +
> >> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> >> + * that for the decompressed kernel.  We have no easy way to tell what
> >> + * the actuall size of code + data the uncompressed kernel will use.
> >> + */
> >> +#define MAX_UNCOMP_KERNEL_SIZE       0x02000000
> >
> > Can we fish the decompressed data size out of zImage, like the existing
> > zImage code does?  (see compressed/head.S:207).  I don't see why this
> > needs to be compile-time constant.
> 
> I am attempting to make sure all the memory used is accounted for in
> the EFI memory map,
> so I care not only about the uncompressed size, but also the BSS.  If
> I get the uncompressed
> image size, and use that for the allocation, the kernel will overwrite
> memory immediately following it.
> I had implemented what you suggested and ran into this problem.

Hmmm, it looks like I misunderstood what gets appended to the compressed
data.

However, it looks like the size of the kernel's bss is also made
available, via a link-time symbol _kernel_bss_size:

	KBSS_SZ = $(shell $(CROSS_COMPILE)size $(obj)/../../../../vmlinux | \
	                awk 'END{print $$3}')
	LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ)

You could get at that by

	extern char _kernel_bss_size;

	/* ... */

	 ... (unsigned long)&_kernel_bss_size ...

> 
> >
> > Someday, someone may try to grow the kernel image beyond 32M.  It would
> > be nice to keep the number of things that breaks to a minimum, to ease
> > potential pain later.
> 
> I picked 32 MBytes based on some discussions of the boot process, and
> my understanding
> is that 32 MBytes is a somewhat hard upper limit on kernel size.

I guess we can address this one as and when.

I suspect that growth beyond 32MB may happen eventually, but it's
going to involve a bit of pain whatever.

So long as efi_stub barfs if the decompressed kernel + BSS doesn't
fit in the available space (you can refer to _kernel_bss_size to
check that).

> 
> >
> >> +
> >> +/* The kernel zImage should be located between 32 Mbytes
> >> + * and 128 MBytes from the base of DRAM.  The min
> >> + * address leaves space for a maximal size uncompressed image,
> >> + * and the max address is due to how the zImage decompressor
> >> + * picks a destination address.
> >> + */
> >> +#define MAX_ZIMAGE_OFFSET    0x08000000
> >
> > The maximum zImage offset is actually 1 less than this.  I think it's
> > just the name of the macro that is misleading, since you use it
> > correctly as an upper bound for memory allocation, so far as I can
> > see.
> >
> > Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.
> 
> I'll rename this.
> 
> >
> >> +#define MIN_ZIMAGE_OFFSET    MAX_UNCOMP_KERNEL_SIZE
> >> +
> >> +#define MAX_CMDLINE_LEN              500
> >
> > This is a random looking number.  Is this supposed to match something
> > somewhere?  Does it serve any purpose other than acting as a sanity
> > limit?
> >
> > If this limit doesn't exist, then an unreasonably large command-line
> > passed by EFI would just lead to a memory allocation failure somewhere,
> > which feels like the right behaviour...
> >
> > If we can avoid building in arbitrary limits, it helps avoid surprises
> > later.
> 
> 
> This is just a sanity check, which should be easy to remove.  I think
> the failure mode will be a huge device tree being created,
> rather than an allocation failure.  In reality I think the limit will
> set by the EFI firmware - I doubt it is possible to pass a
> multi-megabyte command line.
> 
> >
> >> +
> >> +struct fdt_region {
> >> +     u64 base;
> >> +     u64 size;
> >> +};
> >> +
> >> +/*
> >> + * Additional size that could be used for FDT entries added by
> >> + * the UEFI OS Loader Estimation based on:
> >> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
> >> + * + system memory region (20bytes) + mp_core entries (200
> >> + * bytes)
> >> + */
> >
> > What does 0x300 have to do with those numbers?
> >
> > When you say "estimate", are we guaranteed never to exceed that?
> > What happens if we do?
> 
> No guarantees, and we fail to boot if we run out of space in the new
> device tree.  This greatly simplifies the code,
> but I agree that it is not that nice.
> 
> >
> >> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
> >> +
> >> +/* Include shared EFI stub code */
> >> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> >> +
> >> +
> >> +static int is_linux_reserved_region(int memory_type)
> >> +{
> >> +     switch (memory_type) {
> >> +     case EFI_RUNTIME_SERVICES_CODE:
> >> +     case EFI_RUNTIME_SERVICES_DATA:
> >> +     case EFI_UNUSABLE_MEMORY:
> >> +     case EFI_ACPI_RECLAIM_MEMORY:
> >> +     case EFI_ACPI_MEMORY_NVS:
> >> +             return 1;
> >> +     default:
> >> +             return 0;
> >> +     }
> >> +}
> >> +
> >> +
> >> +static int relocate_kernel(efi_system_table_t *sys_table,
> >> +                        unsigned long *load_addr, unsigned long *load_size,
> >> +                        unsigned long min_addr, unsigned long max_addr)
> >> +{
> >> +     /* Get current address of kernel. */
> >> +     unsigned long cur_zimage_addr = *load_addr;
> >> +     unsigned long zimage_size = *load_size;
> >> +     unsigned long new_addr = 0;
> >> +     unsigned long nr_pages;
> >> +
> >> +     efi_status_t status;
> >> +
> >> +     if (!load_addr || !load_size)
> >> +             return EFI_INVALID_PARAMETER;
> >> +
> >> +     *load_size = 0;
> >> +     if (cur_zimage_addr > min_addr
> >> +         && (cur_zimage_addr + zimage_size) < max_addr) {
> >> +             /* We don't need to do anything, as kernel at an acceptable
> >> +              * address already.
> >> +              */
> >> +             return EFI_SUCCESS;
> >> +     }
> >> +     /*
> >> +      * The EFI firmware loader could have placed the kernel image
> >> +      * anywhere in memory, but the kernel has restrictions on the
> >> +      * min and max physical address it can run at.
> >> +      */
> >> +     nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> >
> > It looks like nr_pages is never used in this function.
> 
> Yup, removed.
> >
> >> +
> >> +     status = efi_low_alloc(sys_table, zimage_size, 0,
> >> +                        &new_addr, min_addr);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
> >
> > Does efi_printk automatically prepend a suitable prefix?  If not,
> > it might be useful to define a macro to add a standard prefix to all
> > efi_printks here ("zImage: " or similar).
> 
> It doesn't, but I can add one.  Maybe "EFIstub"?  This is really
> separate from the zImage boot, so I think
> it would be helpful to differentiate it.

Sure, just something to disambiguate it.

> >
> > Minor nit: can we have "allocate" instead of "alloc"?
> Sure.
> >
> > I think both messages should say "failed to allocate usable memory".
> > EFI has already allocated memory for the kernel after all: it's
> > just in the wrong place initially.
> >
> >> +             return status;
> >> +     }
> >> +
> >> +     if (new_addr > (max_addr - zimage_size)) {
> >> +             efi_free(sys_table, zimage_size, new_addr);
> >> +             efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
> >> +             return EFI_INVALID_PARAMETER;
> >> +     }
> >> +
> >> +     /* We know source/dest won't overlap since both memory ranges
> >> +      * have been allocated by UEFI, so we can safely use memcpy.
> >> +      */
> >> +     memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
> >> +            zimage_size);
> >
> > Is it possible for this allocation to fail -- i.e., because UEFI has
> > put us in an unsuitable location which is within the first 128MB of
> > RAM, such that we can't pick a suitable location without overlap?
> 
> I think so, since (in theory at least), other EFI applications could have run
> before us and allocated arbitrary amounts of memory.
> 
> >
> > For the time being though, I think this is impossible because the
> > decompressed Image can't exceed ~32MB (so the zImage should not
> > exceed that either, and both can fit inside 128MB.  It doesn't
> > matter if UEFI's initial load location overlaps the decompressed
> > Image).
> 
> The reason I am avoiding the zImage overlapping the decompressed image
> even though
> the zImage decompressor handles that case is that I want to ensure that
> all memory used during early boot is represented in the EFI memory map.
> By avoiding overlap, I only have to deal with predicting the final
> destination of the
> decompressed kernel.

I guess that makes sense.  If it becomes a constraint, it can be fixed
later, but that probably won't happen for a while.

> 
> >
> > If UEFI put reserved regions with the first 128MB we're likely to
> > be dead anyway, so we shouldn't assume we'll have to cope with that
> > for now...
> 
> For these cases I'd like to be able to return an error message and
> refuse to boot, rather
> than dying during boot.
> 
> In principle, I like the EFI stub being a shim between the EFI
> firmware and the normal zImage boot.  In practice,
> I don't really like having to predict/guess what memory the zImage
> decompressor will use so that we can account for that
> in the EFI memory map.

zImage already suffers from that: you "just have to know" how to
arrange the zImage, initramfs and dtb, per board and per bootlodaer.
AUTO_ZRELADDR provides some extra flexibility, but there are still
arbitrary, unknown constraints which prevent for bootloader from
doing the right thing automatically.

efi_stub should avoid being worse than that, but if we can have
cleaner failures, that's definitely a bonus.

> 
> >
> >> +
> >> +     /* Return the load address and size */
> >> +     *load_addr = new_addr;
> >> +     *load_size = zimage_size;
> >
> > Is zimage_size ever changed?  It looks like it is still equal to the
> > initial value of *load_size at this point.
> 
> Nope, I can get rid of zimage_size and just use *load_size throughout.
> 
> >
> >> +
> >> +
> >> +     return status;
> >> +}
> >> +
> >> +
> >> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
> >> + * Size of memory allocated return in *cmd_line_len.
> >> + * Returns NULL on error.
> >> + */
> >> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
> >> +                                   efi_loaded_image_t *image,
> >> +                                   unsigned long *cmd_line_len,
> >> +                                   u32 max_addr)
> >> +{
> >> +     u16 *s2;
> >> +     u8 *s1 = NULL;
> >> +     unsigned long cmdline_addr = 0;
> >> +     int load_options_size = image->load_options_size / 2; /* ASCII */
> >> +     void *options = (u16 *)image->load_options;
> >> +     int options_size = 0;
> >> +     int status;
> >> +     int i;
> >> +     u16 zero = 0;
> >> +
> >> +     if (options) {
> >> +             s2 = options;
> >> +             while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> >> +                     s2++;
> >> +                     options_size++;
> >> +             }
> >> +     }
> >> +
> >> +     if (options_size == 0) {
> >> +             /* No command line options, so return empty string*/
> >> +             options_size = 1;
> >> +             options = &zero;
> >> +     }
> >> +
> >> +     if (options_size > MAX_CMDLINE_LEN)
> >> +             options_size = MAX_CMDLINE_LEN;
> >> +
> >> +     options_size++;  /* NUL termination */
> >
> > Do we care that options_size can now be > load_options_size?
> >
> > I guess image->load_options isn't realistically going to be right at
> > the end of a RAM bank, so probably nothing disastrous will happen if
> > we read off the end of it.
> >
> > It would be tidier to avoid this, though.
> 
> I'll update this to avoid reading past the end of the EFI option string.

OK, fine

> >
> >> +
> >> +     status = efi_high_alloc(sys_table, options_size, 0,
> >> +                         &cmdline_addr, max_addr);
> >> +     if (status != EFI_SUCCESS)
> >> +             return NULL;
> >> +
> >> +     s1 = (u8 *)(unsigned long)cmdline_addr;
> >> +     s2 = (u16 *)options;
> >> +
> >> +     for (i = 0; i < options_size - 1; i++)
> >> +             *s1++ = *s2++;
> >> +
> >> +     *s1 = '\0';
> >> +
> >> +     *cmd_line_len = options_size;
> >> +     return (char *)(unsigned long)cmdline_addr;
> >> +}
> >> +
> >> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
> >> +                                 void *handle, unsigned long dram_base,
> >> +                                 void *orig_fdt, u64 *orig_fdt_size,
> >> +                                 char *cmdline_ptr,
> >> +                                 unsigned long *cmdline_size,
> >> +                                 u64 initrd_addr, u64 initrd_size)
> >> +{
> >> +     unsigned long new_fdt_size;
> >> +     unsigned long new_fdt_addr;
> >> +     void *fdt;
> >> +     int node;
> >> +     int status;
> >> +     int i;
> >> +     unsigned long map_size, desc_size;
> >> +     unsigned long mmap_key;
> >> +     efi_memory_desc_t *memory_map;
> >> +     unsigned long fdt_val;
> >> +
> >> +     new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
> >> +     status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
> >> +                         dram_base + MAX_ZIMAGE_OFFSET);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
> >> +             goto fail;
> >> +     }
> >
> > There are too many error messages in this function (and elsewhere).
> > Many of them are only useful for debugging: for real use, the only
> > interesting kinds of failure for the DT which will be meaningful to the
> > user are "bad device tree" and "out of memory".
> >
> > Also, it would be desirable to make the error messages more consistent;
> > currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
> > and more.
> >
> > We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
> > which mean basically the same thing.
> >
> > You could try wrapping fdt_setprop() with a function which tries to set
> > the property and prints a suitable message if it fails, without having
> > to put explicit efi_printks all over the place.
> 
> I will review all of the messages, and add a consistent prefix as you
> suggested above.

OK (I confess to being a bit pedantic here)

> >
> >> +
> >> +
> >> +     fdt = (void *)new_fdt_addr;
> >> +     status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
> >> +     if (status != 0) {
> >> +             efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     /* We are done with the original DTB, so free it. */
> >> +     efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
> >> +     *orig_fdt_size = 0;
> >> +
> >> +     node = fdt_subnode_offset(fdt, 0, "chosen");
> >> +     if (node < 0) {
> >> +             node = fdt_add_subnode(fdt, 0, "chosen");
> >> +             if (node < 0) {
> >> +                     efi_printk(sys_table, "Error on finding 'chosen' node\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +
> >> +     if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
> >> +             status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
> >> +                                  strlen(cmdline_ptr) + 1);
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new bootarg\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +     /* We are done with original command line, so free it. */
> >> +     efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
> >> +     *cmdline_size = 0;
> >> +
> >> +     /* Set intird address/end in device tree, if present */
> >> +     if (initrd_size != 0) {
> >> +             u64 initrd_image_end;
> >> +             u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
> >> +             status = fdt_setprop(fdt, node, "linux,initrd-start",
> >> +                                  &initrd_image_start, sizeof(u64));
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +             initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
> >> +             status = fdt_setprop(fdt, node, "linux,initrd-end",
> >> +                                  &initrd_image_end, sizeof(u64));
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +
> >> +     /* Update memory map in the device tree. The memory node must
> >> +      * be present in the tree.*/
> >> +     node = fdt_subnode_offset(fdt, 0, "memory");
> >> +     if (node < 0) {
> >> +             efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +
> >> +     status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> >> +                                 &desc_size, &mmap_key);
> >> +     if (status != EFI_SUCCESS)
> >> +             goto fail_free_new_fdt;
> >> +
> >> +     for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> >> +             efi_memory_desc_t *desc;
> >> +             unsigned long m = (unsigned long)memory_map;
> >> +             desc = (efi_memory_desc_t *)(m + (i * desc_size));
> >> +
> >> +             if (is_linux_reserved_region(desc->type)) {
> >> +                     status = fdt_add_mem_rsv(fdt, desc->phys_addr,
> >> +                                              desc->num_pages * EFI_PAGE_SIZE);
> >> +                     if (status != 0) {
> >> +                             efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
> >> +                             goto fail_free_mmap;
> >> +                     }
> >> +             }
> >> +     }
> >> +
> >> +
> >> +     /* Add FDT entries for EFI runtime services in chosen node.
> >> +      * We need to add the final memory map, so this is done at
> >> +      * the very end.
> >> +      */
> >> +     node = fdt_subnode_offset(fdt, 0, "chosen");
> >> +     fdt_val = cpu_to_fdt32((unsigned long)sys_table);
> >> +     status = fdt_setprop(fdt, node, "efi-system-table",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32(desc_size);
> >> +     status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32(map_size);
> >> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32((unsigned long)memory_map);
> >> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >
> > We have one function doing two completely different jobs here (as
> > documented by the name).  Can it be split?
> 
> I had it split, but due to the address/size pairs that needed to be
> passed around
> to free the allocated memory on error I combined them.  I'll take
> another look at it.
> I think pulling the allocations out of the function may make this
> cleaner, and could
> also make the removal of the guessed new FTD size easier to remove.
> I'll need to handle
> re-trying the FTD allocation in order to gracefully handle significant
> growth in the DTB.
> 
> 
> >
> >> +
> >> +     /* Now we need to exit boot services.  We need the key from
> >> +      * the most recent read of the memory map to do this.  We can't
> >> +      * free this buffer in the normal case, but do free it when
> >> +      * exit_boot_services() fails or adding the memory map to the FDT
> >> +      * fails.
> >> +      */
> >> +     status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> >> +                             handle, mmap_key);
> >> +
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "exit boot services failed.\n");
> >> +             goto fail_free_mmap;
> >> +     }
> >> +
> >> +     return new_fdt_addr;
> >> +
> >> +fail_free_mmap:
> >> +     efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> >> +
> >> +fail_free_new_fdt:
> >> +     efi_free(sys_table, new_fdt_size, new_fdt_addr);
> >> +
> >> +fail:
> >> +     return 0;
> >> +}
> >> +
> >> +
> >> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> >> +           unsigned long *zimage_addr)
> >> +{
> >> +     efi_loaded_image_t *image;
> >> +     int status;
> >> +     unsigned long nr_pages;
> >> +     const struct fdt_region *region;
> >> +
> >> +     void *fdt;
> >> +     int err;
> >> +     int node;
> >> +     unsigned long zimage_size = 0;
> >> +     unsigned long dram_base;
> >> +     /* addr/point and size pairs for memory management*/
> >> +     u64 initrd_addr;
> >> +     u64 initrd_size = 0;
> >> +     u64 fdt_addr;  /* Original DTB */
> >> +     u64 fdt_size = 0;
> >> +     u64 kernel_reserve_addr;
> >> +     u64 kernel_reserve_size = 0;
> >> +     char *cmdline_ptr;
> >> +     unsigned long cmdline_size = 0;
> >> +     unsigned long new_fdt_addr;
> >> +
> >> +     efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> >> +
> >> +     /* Check if we were booted by the EFI firmware */
> >> +     if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> >> +             goto fail;
> >> +
> >> +     efi_printk(sys_table, "Booting Linux using EFI stub.\n");
> >> +
> >> +
> >> +     /* get the command line from EFI, using the LOADED_IMAGE protocol */
> >> +     status = efi_call_phys3(sys_table->boottime->handle_protocol,
> >> +                             handle, &proto, (void *)&image);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> >> +             goto fail;
> >> +     }
> >> +
> >> +     /* We are going to copy this into device tree, so we don't care where in
> >> +      * memory it is.
> >> +      */
> >> +     cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
> >> +                                            &cmdline_size, 0xFFFFFFFF);
> >> +     if (!cmdline_ptr) {
> >> +             efi_printk(sys_table, "ERROR converting command line to ascii.\n");
> >> +             goto fail;
> >> +     }
> >> +
> >> +     /* We first load the device tree, as we need to get the base address of
> >> +      * DRAM from the device tree.  The zImage, device tree, and initrd
> >> +      * have address restrictions that are relative to the base of DRAM.
> >> +      */
> >> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> >> +                                   0xffffffff, &fdt_addr, &fdt_size);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Error loading dtb blob\n");
> >> +             goto fail_free_cmdline;
> >> +     }
> >> +
> >> +     err = fdt_check_header((void *)(unsigned long)fdt_addr);
> >> +     if (err != 0) {
> >> +             efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +     if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
> >> +             efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
> >> +             goto fail_free_dtb;
> >> +
> >> +     }
> >> +
> >> +
> >> +     /* Look up the base of DRAM from the device tree.*/
> >> +     fdt = (void *)(u32)fdt_addr;
> >> +     node = fdt_subnode_offset(fdt, 0, "memory");
> >> +     region = fdt_getprop(fdt, node, "reg", NULL);
> >> +     if (region) {
> >> +             dram_base = fdt64_to_cpu(region->base);
> >> +     } else {
> >> +             efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +
> >> +     /* Reserve memory for the uncompressed kernel image. */
> >> +     kernel_reserve_addr = dram_base;
> >> +     kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> >> +     nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> >> +     status = efi_call_phys4(sys_table->boottime->allocate_pages,
> >> +                             EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> >> +                             nr_pages, &kernel_reserve_addr);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +
> >> +     /* Relocate the zImage, if required. */
> >> +     zimage_size = image->image_size;
> >> +     status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
> >> +                              dram_base + MIN_ZIMAGE_OFFSET,
> >> +                              dram_base + MAX_ZIMAGE_OFFSET);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to relocate kernel\n");
> >> +             goto fail_free_kernel_reserve;
> >> +     }
> >> +
> >> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> >> +                                   dram_base + MAX_ZIMAGE_OFFSET,
> >> +                                   &initrd_addr, &initrd_size);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Error loading initrd\n");
> >> +             goto fail_free_zimage;
> >> +     }
> >> +
> >> +     new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
> >> +                                             dram_base, fdt, &fdt_size,
> >> +                                             cmdline_ptr, &cmdline_size,
> >> +                                             initrd_addr, initrd_size);
> >> +
> >> +     if (new_fdt_addr == 0) {
> >> +             efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
> >> +             goto fail_free_initrd;
> >> +     }
> >
> > Ideally, we shouldn't have one error message for two completely
> > different causes.
> >
> > The printk could move into update_fdt_and_exit_boot() and split
> > into more specific cases.
> >
> >> +
> >> +
> >> +     /* Now we need to return the FDT address to the calling
> >> +      * assembly to this can be used as part of normal boot.
> >> +      */
> >> +     return new_fdt_addr;
> >> +
> >> +fail_free_initrd:
> >> +     efi_free(sys_table, initrd_size, initrd_addr);
> >> +
> >> +fail_free_zimage:
> >> +     efi_free(sys_table, zimage_size, *zimage_addr);
> >> +
> >> +fail_free_kernel_reserve:
> >> +     efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
> >> +
> >> +fail_free_dtb:
> >> +     efi_free(sys_table, fdt_size, fdt_addr);
> >> +
> >> +fail_free_cmdline:
> >> +     efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
> >> +
> >> +fail:
> >> +     return EFI_STUB_ERROR;
> >> +}
> >> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
> >> index 75189f1..491e752 100644
> >> --- a/arch/arm/boot/compressed/head.S
> >> +++ b/arch/arm/boot/compressed/head.S
> >> @@ -120,21 +120,100 @@
> >>   */
> >>               .align
> >>               .arm                            @ Always enter in ARM state
> >> +             .text
> >>  start:
> >>               .type   start,#function
> >> -             .rept   7
> >> +#ifdef CONFIG_EFI_STUB
> >> +             @ Magic MSDOS signature for PE/COFF + ADD opcode
> >> +             .word   0x62805a4d
> >
> > Did you get a chance to respond to the endianness issue I raised?
> For now the EFI stub only supports LE, and I need to update
> the Kconfig to reflect this.  Adding BE should be possible, but I don't
> plan to work on that at this time.

OK, so long as that is made explicit in Kconfig, that sounds
reasonable.
> 
> 
> 
> >> +#else
> >> +             mov     r0, r0
> >> +#endif
> >> +             .rept   5
> >>               mov     r0, r0
> >>               .endr
> >> -   ARM(              mov     r0, r0          )
> >> -   ARM(              b       1f              )
> >> - THUMB(              adr     r12, BSYM(1f)   )
> >> - THUMB(              bx      r12             )
> >> +
> >> +             @ zimage_continue will be in ARM or thumb mode as configured
> >> + THUMB(              adrl    r12, BSYM(zimage_continue))
> >> + ARM(                adrl    r12, zimage_continue)
> >> +             bx      r12
> >
> > Note that BSYM() can be used both in ARM and Thumb kernels.
> >
> > In any case, ARM kernels cannot contain BX instructions because we still
> > support ARMv4 (which doesn't have it).
> >
> > I'm presuming you found zimage_continue is too far away for adr here,
> > which is why you changed it.  Assuming that't the case, this might make
> > sense:
> >
> >         adrl    r12, BSYM(zimage_continue)
> >  ARM(   mov     pc, r12 )
> >  THUMB( bx      r12     )
> 
> Yes, I changed this due to lack of range.
> 
> >
> >> + THUMB(              .thumb                  )
> >
> > For tidiness, it's better to avoid this dangling .thumb ... move it
> > to just before zimage_continue instead, since efi_stub_entry has to be
> > ARM anyway.
> 
> OK
> >
> >>
> >>               .word   0x016f2818              @ Magic numbers to help the loader
> >>               .word   start                   @ absolute load/run zImage address
> >>               .word   _edata                  @ zImage end address
> >> +
> >> +#ifdef CONFIG_EFI_STUB
> >> +             @ Portions of the MSDOS file header must be at offset
> >> +             @ 0x3c from the start of the file.  All PE/COFF headers
> >> +             @ are kept contiguous for simplicity.
> >> +#include "efi-header.S"
> >> +
> >> +efi_stub_entry:
> >> +             @ The EFI stub entry point is not at a fixed address, however
> >> +             @ this address must be set in the PE/COFF header.
> >> +             @ EFI entry point is in A32 mode, switch to T32 if configured.
> >> + THUMB(              .arm                    )
> >
> > ^So, you can lose .arm here too (but keep the comment -- that's valuable
> > info)
> >
> >> + THUMB(              adr     r12, BSYM(1f)   )
> >> + THUMB(              bx      r12             )
> >>   THUMB(              .thumb                  )
> >>  1:
> >> +             @ Save lr on stack for possible return to EFI firmware.
> >> +             @ Don't care about fp, but need 64 bit alignment....
> >> +             stmfd   sp!, {fp, lr}
> >> +
> >> +             @ Save args to EFI app across got fixup call
> >> +             stmfd   sp!, {r0, r1}
> >
> > Mostly minor coding nits follow...
> 
> I'll go through these and update the code.  I appreciate your review,
> as I am new to ARM assembly.

No problem -- it's already not far off.

I think my comments were all tidiness rather than correctness issues.

Cheers
---Dave

> 
> >
> >
> > stmfd sp!, {r0, r1, fp, lr} ?
> >
> >> +             ldmfd   sp!, {r0, r1}
> >> +
> >> +             @ allocate space on stack for return of new entry point of
> >> +             @ zImage, as EFI stub may copy the kernel.  Pass address
> >> +             @ of space in r2 - EFI stub will fill in the pointer.
> >> +
> >> +             sub     sp, #8                  @ we only need 4 bytes,
> >
> > I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?
> >
> > kernel asm is written in the traditional syntax, which means explicit
> > source and destination registers for instructions like this:
> >
> >         sub     sp, sp, #8
> >
> > Since the EFI stub code will only be built with new toolchains it
> > probably doesn't matter, but it's best to be consistent for readability
> > purposes.
> >
> >> +                                             @ but keep stack 8 byte aligned.
> >> +             mov     r2, sp
> >> +             @ Pass our actual runtime start address in pointer data
> >> +             adr     r11, LC0                @ address of LC0 at run time
> >> +             ldr     r12, [r11, #0]          @ address of LC0 at link time
> >> +
> >> +             sub     r3, r11, r12            @ calculate the delta offset
> >> +             str     r3, [r2, #0]
> >> +             bl      efi_entry
> >> +
> >> +             @ get new zImage entry address from stack, put into r3
> >> +             ldr     r3, [sp, #0]
> >> +             add     sp, #8  @ restore stack
> >
> >         add     sp, sp, #8
> >
> >> +
> >> +             @ Check for error return from EFI stub (0xFFFFFFFF)
> >> +             ldr     r1, =0xffffffff
> >
> > Minor nit, but ldr= is wasteful for this.
> >
> > You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
> > smart enough to translate this)...
> >
> >> +             cmp     r0, r1
> >
> > ...alternatively, don't use r1 at all and do:
> >
> >         cmn     r0, #1
> >
> >> +             beq     efi_load_fail
> >> +
> >> +
> >> +             @ Save return values of efi_entry
> >> +             stmfd   sp!, {r0, r3}
> >> +             bl      cache_clean_flush
> >> +             bl      cache_off
> >
> > Why turn the cache off?  Does that mean that EFI may launch images with
> > the cache enabled?
> >
> > If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> > off is not safe.
> >
> > (Hmm, the UEFI spec seems to suggest "yes" for these questions)
> >
> >> +             ldmfd   sp!, {r0, r3}
> >> +
> >> +             @ put DTB address in r2, it was returned by EFI entry
> >> +             mov     r2, r0
> >> +             ldr     r1, =0xffffffff         @ DTB machine type
> >
> > mov/mvn: see above
> >
> >> +             mov     r0, #0  @ r0 is 0
> >
> > Useless comment: maybe say why you're doing this ("r0 is 0, as required
> > by the kernel boot protocol", or something like that).
> >
> >> +
> >> +             @ Branch to (possibly) relocated zImage entry that is in r3
> >> +             bx      r3
> >> +
> >> +efi_load_fail:
> >> +             @ Return EFI_LOAD_ERROR to EFI firmware on error.
> >> +             @ Switch back to ARM mode for EFI is done based on
> >> +             @ return address on stack
> >> +             ldr     r0, =0x80000001
> >> +             ldmfd   sp!, {fp, pc}
> >> +#endif
> >> +
> >> +zimage_continue:
> >>               mrs     r9, cpsr
> >>  #ifdef CONFIG_ARM_VIRT_EXT
> >>               bl      __hyp_stub_install      @ get into SVC mode, reversibly
> >> @@ -167,7 +246,6 @@ not_angel:
> >>                * by the linker here, but it should preserve r7, r8, and r9.
> >>                */
> >>
> >> -             .text
> >>
> >>  #ifdef CONFIG_AUTO_ZRELADDR
> >>               @ determine final kernel image address
> >> --
> >> 1.7.10.4
> >>
> >>
> >> _______________________________________________
> >> linux-arm-kernel mailing list
> >> linux-arm-kernel@lists.infradead.org
> >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-13 14:21         ` Dave P Martin
  0 siblings, 0 replies; 88+ messages in thread
From: Dave P Martin @ 2013-08-13 14:21 UTC (permalink / raw)
  To: Roy Franz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-efi-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	matt.fleming-ral2JQCrhuEAvxtiuMwx3w, Russell King - ARM Linux,
	Leif Lindholm

On Thu, Aug 08, 2013 at 10:57:29PM +0100, Roy Franz wrote:
> On Wed, Aug 7, 2013 at 11:05 AM, Dave Martin <Dave.Martin-5wv7dgnIgG8@public.gmane.org> wrote:
> > On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
> >> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
> >> operations similarly to the x86 stub: it is a shim between the EFI firmware
> >> and the normal zImage entry point, and sets up the environment that the
> >> zImage is expecting.  This includes loading the initrd (optionaly) and
> >> device tree from the system partition based on the kernel command line.
> >> The stub updates the device tree as necessary, including adding reserved
> >> memory regions and adding entries for EFI runtime services. The PE/COFF
> >> "MZ" header at offset 0 results in the first instruction being an add
> >> that corrupts r5, which is not used by the zImage interface.
> >
> > Some more comments below ... note that I haven't really looked at the C
> > code in depth.
> 
> Responses below, and I'm working on incorporating suggested changes
> for the next version.

I few responses-to-responses from me inline.  Your repose supersedes
most of this anyhow.

Cheers
---Dave

> 
> Thanks,
> Roy
> 
> >
> > Cheers
> > ---Dave
> >
> >>
> >> Signed-off-by: Roy Franz <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> >> ---
> >>  arch/arm/boot/compressed/Makefile     |   18 +-
> >>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
> >>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
> >>  arch/arm/boot/compressed/head.S       |   90 +++++-
> >>  4 files changed, 728 insertions(+), 8 deletions(-)
> >>  create mode 100644 arch/arm/boot/compressed/efi-header.S
> >>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
> >>
> >> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> >> index 7ac1610..c62826a 100644
> >> --- a/arch/arm/boot/compressed/Makefile
> >> +++ b/arch/arm/boot/compressed/Makefile
> >> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
> >>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
> >>       $(addprefix $(obj)/,$(libfdt_hdrs))
> >>
> >> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
> >> +     $(addprefix $(obj)/,$(libfdt_hdrs))
> >> +
> >
> > Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?
> >
> > Would it make sense just to add efi-stub.o to the list of targets in the
> > original rule?
> 
> Yes, change made.
> >
> >>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
> >> -OBJS += $(libfdt_objs) atags_to_fdt.o
> >> +OBJS += atags_to_fdt.o
> >> +USE_LIBFDT = y
> >> +endif
> >> +
> >> +ifeq ($(CONFIG_EFI_STUB),y)
> >> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
> >> +OBJS += efi-stub.o
> >> +USE_LIBFDT = y
> >> +endif
> >> +
> >> +ifeq ($(USE_LIBFDT),y)
> >> +OBJS += $(libfdt_objs)
> >>  endif
> >>
> >>  targets       := vmlinux vmlinux.lds \
> >> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
> >>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
> >>  endif
> >>
> >> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
> >> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
> >
> > You don't appear to explain this change anywhere.
> 
> Prior to my changes, even though the stack protector was not disabled,
> it was not actually used. GCC uses a heuristic
> based on the size of the stack whether to enable the stack protector,
> and the threshold to trigger its use was not met, so no stack checking
> was actually being done.  In order to do stack protection, a few
> __stack_chk_* functions/variable need to be provided by the
> application.  I worked a bit on adding these, but could not get them
> working in the stub/decompressor.  The x86 arch also has
> "-fno-stack-protector" defined for its compressed boot stub, so I
> decided to go that route as well.
> 
> >
> >>  asflags-y := -DZIMAGE
> >>
> >>  # Supply kernel BSS size to the decompressor via a linker symbol.
> >> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
> >> new file mode 100644
> >> index 0000000..6ff32cc
> >> --- /dev/null
> >> +++ b/arch/arm/boot/compressed/efi-header.S
> >> @@ -0,0 +1,114 @@
> >> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> >> +@
> >> +@ This file contains the PE/COFF header that is part of the
> >> +@ EFI stub.
> >> +@
> >> +
> >> +     .org    0x3c
> >> +     @
> >> +     @ The PE header can be anywhere in the file, but for
> >> +     @ simplicity we keep it together with the MSDOS header
> >> +     @ The offset to the PE/COFF header needs to be at offset
> >> +     @ 0x3C in the MSDOS header.
> >> +     @ The only 2 fields of the MSDOS header that are used are this
> >> +     @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
> >> +     @
> >> +     .long   pe_header                       @ Offset to the PE header.
> >
> > Is there any chance of merging this with the equivalent x86 code?
> >
> > The PE/COFF header is much the same in both cases, although there
> > are some differences.  Maybe it would be more trouble than it is
> > worth...
> 
> I think it would be more pain than gain.  We are planning to add arm64 stub
> support next, so we'd end up with 4 architectures sharing this assembly file,
> which I think would be painful from a patch submission/review point of view.
> 
> >
> >> +
> >> +      .align 3
> >> +pe_header:
> >> +
> >> +
> >> +pe_header:
> >
> > Duplicate label?
> 
> Yup, fixed.
> >
> >> +     .ascii  "PE"
> >> +     .short  0
> >> +
> >> +coff_header:
> >> +     .short  0x01c2                          @ ARM or Thumb
> >> +     .short  2                               @ nr_sections
> >> +     .long   0                               @ TimeDateStamp
> >> +     .long   0                               @ PointerToSymbolTable
> >> +     .long   1                               @ NumberOfSymbols
> >> +     .short  section_table - optional_header @ SizeOfOptionalHeader
> >> +     .short  0x306                           @ Characteristics.
> >> +                                             @ IMAGE_FILE_32BIT_MACHINE |
> >> +                                             @ IMAGE_FILE_DEBUG_STRIPPED |
> >> +                                             @ IMAGE_FILE_EXECUTABLE_IMAGE |
> >> +                                             @ IMAGE_FILE_LINE_NUMS_STRIPPED
> >> +
> >> +optional_header:
> >> +     .short  0x10b                           @ PE32 format
> >> +     .byte   0x02                            @ MajorLinkerVersion
> >> +     .byte   0x14                            @ MinorLinkerVersion
> >> +
> >> +     .long   0                               @ SizeOfCode
> >
> > Do we need to fill in SizeOfCode with a real value?  It looks like x86
> > does.
> >
> > We should probably fill this in unless there's a documented ABI for EFI
> > boot on ARM which explicitly doesn't require these.
> 
> I will investigate/fix this.
> 
> >
> >> +
> >> +     .long   0                               @ SizeOfInitializedData
> >> +     .long   0                               @ SizeOfUninitializedData
> >> +
> >> +     .long   efi_stub_entry                  @ AddressOfEntryPoint
> >> +     .long   efi_stub_entry                  @ BaseOfCode
> >> +     .long   0                               @ data
> >> +
> >> +extra_header_fields:
> >> +     .long   0                               @ ImageBase
> >> +     .long   0x20                            @ SectionAlignment
> >> +     .long   0x20                            @ FileAlignment
> >> +     .short  0                               @ MajorOperatingSystemVersion
> >> +     .short  0                               @ MinorOperatingSystemVersion
> >> +     .short  0                               @ MajorImageVersion
> >> +     .short  0                               @ MinorImageVersion
> >> +     .short  0                               @ MajorSubsystemVersion
> >> +     .short  0                               @ MinorSubsystemVersion
> >> +     .long   0                               @ Win32VersionValue
> >> +
> >> +     .long   _edata                          @ SizeOfImage
> >> +
> >> +     @ Everything before the entry point is considered part of the header
> >> +     .long   efi_stub_entry                  @ SizeOfHeaders
> >> +     .long   0                               @ CheckSum
> >> +     .short  0xa                             @ Subsystem (EFI application)
> >> +     .short  0                               @ DllCharacteristics
> >> +     .long   0                               @ SizeOfStackReserve
> >> +     .long   0                               @ SizeOfStackCommit
> >> +     .long   0                               @ SizeOfHeapReserve
> >> +     .long   0                               @ SizeOfHeapCommit
> >> +     .long   0                               @ LoaderFlags
> >> +     .long   0x0                             @ NumberOfRvaAndSizes
> >> +
> >> +     # Section table
> >> +section_table:
> >> +
> >> +     #
> >> +     # The EFI application loader requires a relocation section
> >> +     # because EFI applications must be relocatable.  This is a
> >> +     # dummy section as far as we are concerned.
> >> +     #
> >> +     .ascii  ".reloc"
> >> +     .byte   0
> >> +     .byte   0                       @ end of 0 padding of section name
> >> +     .long   0
> >> +     .long   0
> >> +     .long   0                       @ SizeOfRawData
> >> +     .long   0                       @ PointerToRawData
> >> +     .long   0                       @ PointerToRelocations
> >> +     .long   0                       @ PointerToLineNumbers
> >> +     .short  0                       @ NumberOfRelocations
> >> +     .short  0                       @ NumberOfLineNumbers
> >> +     .long   0x42100040              @ Characteristics (section flags)
> >> +
> >> +
> >> +     .ascii  ".text"
> >> +     .byte   0
> >> +     .byte   0
> >> +     .byte   0                       @ end of 0 padding of section name
> >> +     .long   _edata - efi_stub_entry         @ VirtualSize
> >> +     .long   efi_stub_entry                  @ VirtualAddress
> >> +     .long   _edata - efi_stub_entry         @ SizeOfRawData
> >> +     .long   efi_stub_entry                  @ PointerToRawData
> >> +
> >> +     .long   0               @ PointerToRelocations (0 for executables)
> >> +     .long   0               @ PointerToLineNumbers (0 for executables)
> >> +     .short  0               @ NumberOfRelocations  (0 for executables)
> >> +     .short  0               @ NumberOfLineNumbers  (0 for executables)
> >> +     .long   0xe0500020      @ Characteristics (section flags)
> >
> > Can you explain why x86 needs an extra section (the .setup thing)?
> > I haven't dug into that in enough detail to understand it yet...
> 
> I will look into that, I don't know off hand.  I simplified the header
> for ARM as much as I could
> for booting with EDK2.
> >
> >> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> >> new file mode 100644
> >> index 0000000..b817ea3
> >> --- /dev/null
> >> +++ b/arch/arm/boot/compressed/efi-stub.c
> >> @@ -0,0 +1,514 @@
> >> +/*
> >> + * linux/arch/arm/boot/compressed/efi-stub.c
> >> + *
> >> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> >> + *
> >> + * This file implements the EFI boot stub for the ARM kernel
> >> + *
> >> + * 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 <libfdt.h>
> >> +
> >> +
> >> +/* Error code returned to ASM code instead of valid FDT address. */
> >> +#define EFI_STUB_ERROR               (~0)
> >
> > Can we put that into a suitable hedaer and use it in compressed/head.S,
> > instead of the magic 0xffffffff?  (Assuming that value is supposed to
> > match EFI_STUB_ERROR)
> 
> Yes, I will do this.
> >
> >> +
> >> +/* EFI function call wrappers.  These are not required for
> >> + * ARM, but wrappers are required for X86 to convert between
> >> + * ABIs.  These wrappers are provided to allow code sharing
> >> + * between X86 and ARM.  Since these wrappers directly invoke the
> >> + * EFI function pointer, the function pointer type must be properly
> >> + * defined, which is not the case for X86  One advantage of this is
> >> + * it allows for type checking of arguments, which is not
> >> + * possible with the X86 wrappers.
> >> + */
> >> +#define efi_call_phys0(f)                    f()
> >> +#define efi_call_phys1(f, a1)                        f(a1)
> >> +#define efi_call_phys2(f, a1, a2)            f(a1, a2)
> >> +#define efi_call_phys3(f, a1, a2, a3)                f(a1, a2, a3)
> >> +#define efi_call_phys4(f, a1, a2, a3, a4)    f(a1, a2, a3, a4)
> >> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)        f(a1, a2, a3, a4, a5)
> >> +
> >> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> >> + * that for the decompressed kernel.  We have no easy way to tell what
> >> + * the actuall size of code + data the uncompressed kernel will use.
> >> + */
> >> +#define MAX_UNCOMP_KERNEL_SIZE       0x02000000
> >
> > Can we fish the decompressed data size out of zImage, like the existing
> > zImage code does?  (see compressed/head.S:207).  I don't see why this
> > needs to be compile-time constant.
> 
> I am attempting to make sure all the memory used is accounted for in
> the EFI memory map,
> so I care not only about the uncompressed size, but also the BSS.  If
> I get the uncompressed
> image size, and use that for the allocation, the kernel will overwrite
> memory immediately following it.
> I had implemented what you suggested and ran into this problem.

Hmmm, it looks like I misunderstood what gets appended to the compressed
data.

However, it looks like the size of the kernel's bss is also made
available, via a link-time symbol _kernel_bss_size:

	KBSS_SZ = $(shell $(CROSS_COMPILE)size $(obj)/../../../../vmlinux | \
	                awk 'END{print $$3}')
	LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ)

You could get at that by

	extern char _kernel_bss_size;

	/* ... */

	 ... (unsigned long)&_kernel_bss_size ...

> 
> >
> > Someday, someone may try to grow the kernel image beyond 32M.  It would
> > be nice to keep the number of things that breaks to a minimum, to ease
> > potential pain later.
> 
> I picked 32 MBytes based on some discussions of the boot process, and
> my understanding
> is that 32 MBytes is a somewhat hard upper limit on kernel size.

I guess we can address this one as and when.

I suspect that growth beyond 32MB may happen eventually, but it's
going to involve a bit of pain whatever.

So long as efi_stub barfs if the decompressed kernel + BSS doesn't
fit in the available space (you can refer to _kernel_bss_size to
check that).

> 
> >
> >> +
> >> +/* The kernel zImage should be located between 32 Mbytes
> >> + * and 128 MBytes from the base of DRAM.  The min
> >> + * address leaves space for a maximal size uncompressed image,
> >> + * and the max address is due to how the zImage decompressor
> >> + * picks a destination address.
> >> + */
> >> +#define MAX_ZIMAGE_OFFSET    0x08000000
> >
> > The maximum zImage offset is actually 1 less than this.  I think it's
> > just the name of the macro that is misleading, since you use it
> > correctly as an upper bound for memory allocation, so far as I can
> > see.
> >
> > Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.
> 
> I'll rename this.
> 
> >
> >> +#define MIN_ZIMAGE_OFFSET    MAX_UNCOMP_KERNEL_SIZE
> >> +
> >> +#define MAX_CMDLINE_LEN              500
> >
> > This is a random looking number.  Is this supposed to match something
> > somewhere?  Does it serve any purpose other than acting as a sanity
> > limit?
> >
> > If this limit doesn't exist, then an unreasonably large command-line
> > passed by EFI would just lead to a memory allocation failure somewhere,
> > which feels like the right behaviour...
> >
> > If we can avoid building in arbitrary limits, it helps avoid surprises
> > later.
> 
> 
> This is just a sanity check, which should be easy to remove.  I think
> the failure mode will be a huge device tree being created,
> rather than an allocation failure.  In reality I think the limit will
> set by the EFI firmware - I doubt it is possible to pass a
> multi-megabyte command line.
> 
> >
> >> +
> >> +struct fdt_region {
> >> +     u64 base;
> >> +     u64 size;
> >> +};
> >> +
> >> +/*
> >> + * Additional size that could be used for FDT entries added by
> >> + * the UEFI OS Loader Estimation based on:
> >> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
> >> + * + system memory region (20bytes) + mp_core entries (200
> >> + * bytes)
> >> + */
> >
> > What does 0x300 have to do with those numbers?
> >
> > When you say "estimate", are we guaranteed never to exceed that?
> > What happens if we do?
> 
> No guarantees, and we fail to boot if we run out of space in the new
> device tree.  This greatly simplifies the code,
> but I agree that it is not that nice.
> 
> >
> >> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
> >> +
> >> +/* Include shared EFI stub code */
> >> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> >> +
> >> +
> >> +static int is_linux_reserved_region(int memory_type)
> >> +{
> >> +     switch (memory_type) {
> >> +     case EFI_RUNTIME_SERVICES_CODE:
> >> +     case EFI_RUNTIME_SERVICES_DATA:
> >> +     case EFI_UNUSABLE_MEMORY:
> >> +     case EFI_ACPI_RECLAIM_MEMORY:
> >> +     case EFI_ACPI_MEMORY_NVS:
> >> +             return 1;
> >> +     default:
> >> +             return 0;
> >> +     }
> >> +}
> >> +
> >> +
> >> +static int relocate_kernel(efi_system_table_t *sys_table,
> >> +                        unsigned long *load_addr, unsigned long *load_size,
> >> +                        unsigned long min_addr, unsigned long max_addr)
> >> +{
> >> +     /* Get current address of kernel. */
> >> +     unsigned long cur_zimage_addr = *load_addr;
> >> +     unsigned long zimage_size = *load_size;
> >> +     unsigned long new_addr = 0;
> >> +     unsigned long nr_pages;
> >> +
> >> +     efi_status_t status;
> >> +
> >> +     if (!load_addr || !load_size)
> >> +             return EFI_INVALID_PARAMETER;
> >> +
> >> +     *load_size = 0;
> >> +     if (cur_zimage_addr > min_addr
> >> +         && (cur_zimage_addr + zimage_size) < max_addr) {
> >> +             /* We don't need to do anything, as kernel at an acceptable
> >> +              * address already.
> >> +              */
> >> +             return EFI_SUCCESS;
> >> +     }
> >> +     /*
> >> +      * The EFI firmware loader could have placed the kernel image
> >> +      * anywhere in memory, but the kernel has restrictions on the
> >> +      * min and max physical address it can run at.
> >> +      */
> >> +     nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> >
> > It looks like nr_pages is never used in this function.
> 
> Yup, removed.
> >
> >> +
> >> +     status = efi_low_alloc(sys_table, zimage_size, 0,
> >> +                        &new_addr, min_addr);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
> >
> > Does efi_printk automatically prepend a suitable prefix?  If not,
> > it might be useful to define a macro to add a standard prefix to all
> > efi_printks here ("zImage: " or similar).
> 
> It doesn't, but I can add one.  Maybe "EFIstub"?  This is really
> separate from the zImage boot, so I think
> it would be helpful to differentiate it.

Sure, just something to disambiguate it.

> >
> > Minor nit: can we have "allocate" instead of "alloc"?
> Sure.
> >
> > I think both messages should say "failed to allocate usable memory".
> > EFI has already allocated memory for the kernel after all: it's
> > just in the wrong place initially.
> >
> >> +             return status;
> >> +     }
> >> +
> >> +     if (new_addr > (max_addr - zimage_size)) {
> >> +             efi_free(sys_table, zimage_size, new_addr);
> >> +             efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
> >> +             return EFI_INVALID_PARAMETER;
> >> +     }
> >> +
> >> +     /* We know source/dest won't overlap since both memory ranges
> >> +      * have been allocated by UEFI, so we can safely use memcpy.
> >> +      */
> >> +     memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
> >> +            zimage_size);
> >
> > Is it possible for this allocation to fail -- i.e., because UEFI has
> > put us in an unsuitable location which is within the first 128MB of
> > RAM, such that we can't pick a suitable location without overlap?
> 
> I think so, since (in theory at least), other EFI applications could have run
> before us and allocated arbitrary amounts of memory.
> 
> >
> > For the time being though, I think this is impossible because the
> > decompressed Image can't exceed ~32MB (so the zImage should not
> > exceed that either, and both can fit inside 128MB.  It doesn't
> > matter if UEFI's initial load location overlaps the decompressed
> > Image).
> 
> The reason I am avoiding the zImage overlapping the decompressed image
> even though
> the zImage decompressor handles that case is that I want to ensure that
> all memory used during early boot is represented in the EFI memory map.
> By avoiding overlap, I only have to deal with predicting the final
> destination of the
> decompressed kernel.

I guess that makes sense.  If it becomes a constraint, it can be fixed
later, but that probably won't happen for a while.

> 
> >
> > If UEFI put reserved regions with the first 128MB we're likely to
> > be dead anyway, so we shouldn't assume we'll have to cope with that
> > for now...
> 
> For these cases I'd like to be able to return an error message and
> refuse to boot, rather
> than dying during boot.
> 
> In principle, I like the EFI stub being a shim between the EFI
> firmware and the normal zImage boot.  In practice,
> I don't really like having to predict/guess what memory the zImage
> decompressor will use so that we can account for that
> in the EFI memory map.

zImage already suffers from that: you "just have to know" how to
arrange the zImage, initramfs and dtb, per board and per bootlodaer.
AUTO_ZRELADDR provides some extra flexibility, but there are still
arbitrary, unknown constraints which prevent for bootloader from
doing the right thing automatically.

efi_stub should avoid being worse than that, but if we can have
cleaner failures, that's definitely a bonus.

> 
> >
> >> +
> >> +     /* Return the load address and size */
> >> +     *load_addr = new_addr;
> >> +     *load_size = zimage_size;
> >
> > Is zimage_size ever changed?  It looks like it is still equal to the
> > initial value of *load_size at this point.
> 
> Nope, I can get rid of zimage_size and just use *load_size throughout.
> 
> >
> >> +
> >> +
> >> +     return status;
> >> +}
> >> +
> >> +
> >> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
> >> + * Size of memory allocated return in *cmd_line_len.
> >> + * Returns NULL on error.
> >> + */
> >> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
> >> +                                   efi_loaded_image_t *image,
> >> +                                   unsigned long *cmd_line_len,
> >> +                                   u32 max_addr)
> >> +{
> >> +     u16 *s2;
> >> +     u8 *s1 = NULL;
> >> +     unsigned long cmdline_addr = 0;
> >> +     int load_options_size = image->load_options_size / 2; /* ASCII */
> >> +     void *options = (u16 *)image->load_options;
> >> +     int options_size = 0;
> >> +     int status;
> >> +     int i;
> >> +     u16 zero = 0;
> >> +
> >> +     if (options) {
> >> +             s2 = options;
> >> +             while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> >> +                     s2++;
> >> +                     options_size++;
> >> +             }
> >> +     }
> >> +
> >> +     if (options_size == 0) {
> >> +             /* No command line options, so return empty string*/
> >> +             options_size = 1;
> >> +             options = &zero;
> >> +     }
> >> +
> >> +     if (options_size > MAX_CMDLINE_LEN)
> >> +             options_size = MAX_CMDLINE_LEN;
> >> +
> >> +     options_size++;  /* NUL termination */
> >
> > Do we care that options_size can now be > load_options_size?
> >
> > I guess image->load_options isn't realistically going to be right at
> > the end of a RAM bank, so probably nothing disastrous will happen if
> > we read off the end of it.
> >
> > It would be tidier to avoid this, though.
> 
> I'll update this to avoid reading past the end of the EFI option string.

OK, fine

> >
> >> +
> >> +     status = efi_high_alloc(sys_table, options_size, 0,
> >> +                         &cmdline_addr, max_addr);
> >> +     if (status != EFI_SUCCESS)
> >> +             return NULL;
> >> +
> >> +     s1 = (u8 *)(unsigned long)cmdline_addr;
> >> +     s2 = (u16 *)options;
> >> +
> >> +     for (i = 0; i < options_size - 1; i++)
> >> +             *s1++ = *s2++;
> >> +
> >> +     *s1 = '\0';
> >> +
> >> +     *cmd_line_len = options_size;
> >> +     return (char *)(unsigned long)cmdline_addr;
> >> +}
> >> +
> >> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
> >> +                                 void *handle, unsigned long dram_base,
> >> +                                 void *orig_fdt, u64 *orig_fdt_size,
> >> +                                 char *cmdline_ptr,
> >> +                                 unsigned long *cmdline_size,
> >> +                                 u64 initrd_addr, u64 initrd_size)
> >> +{
> >> +     unsigned long new_fdt_size;
> >> +     unsigned long new_fdt_addr;
> >> +     void *fdt;
> >> +     int node;
> >> +     int status;
> >> +     int i;
> >> +     unsigned long map_size, desc_size;
> >> +     unsigned long mmap_key;
> >> +     efi_memory_desc_t *memory_map;
> >> +     unsigned long fdt_val;
> >> +
> >> +     new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
> >> +     status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
> >> +                         dram_base + MAX_ZIMAGE_OFFSET);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
> >> +             goto fail;
> >> +     }
> >
> > There are too many error messages in this function (and elsewhere).
> > Many of them are only useful for debugging: for real use, the only
> > interesting kinds of failure for the DT which will be meaningful to the
> > user are "bad device tree" and "out of memory".
> >
> > Also, it would be desirable to make the error messages more consistent;
> > currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
> > and more.
> >
> > We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
> > which mean basically the same thing.
> >
> > You could try wrapping fdt_setprop() with a function which tries to set
> > the property and prints a suitable message if it fails, without having
> > to put explicit efi_printks all over the place.
> 
> I will review all of the messages, and add a consistent prefix as you
> suggested above.

OK (I confess to being a bit pedantic here)

> >
> >> +
> >> +
> >> +     fdt = (void *)new_fdt_addr;
> >> +     status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
> >> +     if (status != 0) {
> >> +             efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     /* We are done with the original DTB, so free it. */
> >> +     efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
> >> +     *orig_fdt_size = 0;
> >> +
> >> +     node = fdt_subnode_offset(fdt, 0, "chosen");
> >> +     if (node < 0) {
> >> +             node = fdt_add_subnode(fdt, 0, "chosen");
> >> +             if (node < 0) {
> >> +                     efi_printk(sys_table, "Error on finding 'chosen' node\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +
> >> +     if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
> >> +             status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
> >> +                                  strlen(cmdline_ptr) + 1);
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new bootarg\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +     /* We are done with original command line, so free it. */
> >> +     efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
> >> +     *cmdline_size = 0;
> >> +
> >> +     /* Set intird address/end in device tree, if present */
> >> +     if (initrd_size != 0) {
> >> +             u64 initrd_image_end;
> >> +             u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
> >> +             status = fdt_setprop(fdt, node, "linux,initrd-start",
> >> +                                  &initrd_image_start, sizeof(u64));
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +             initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
> >> +             status = fdt_setprop(fdt, node, "linux,initrd-end",
> >> +                                  &initrd_image_end, sizeof(u64));
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +
> >> +     /* Update memory map in the device tree. The memory node must
> >> +      * be present in the tree.*/
> >> +     node = fdt_subnode_offset(fdt, 0, "memory");
> >> +     if (node < 0) {
> >> +             efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +
> >> +     status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> >> +                                 &desc_size, &mmap_key);
> >> +     if (status != EFI_SUCCESS)
> >> +             goto fail_free_new_fdt;
> >> +
> >> +     for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> >> +             efi_memory_desc_t *desc;
> >> +             unsigned long m = (unsigned long)memory_map;
> >> +             desc = (efi_memory_desc_t *)(m + (i * desc_size));
> >> +
> >> +             if (is_linux_reserved_region(desc->type)) {
> >> +                     status = fdt_add_mem_rsv(fdt, desc->phys_addr,
> >> +                                              desc->num_pages * EFI_PAGE_SIZE);
> >> +                     if (status != 0) {
> >> +                             efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
> >> +                             goto fail_free_mmap;
> >> +                     }
> >> +             }
> >> +     }
> >> +
> >> +
> >> +     /* Add FDT entries for EFI runtime services in chosen node.
> >> +      * We need to add the final memory map, so this is done at
> >> +      * the very end.
> >> +      */
> >> +     node = fdt_subnode_offset(fdt, 0, "chosen");
> >> +     fdt_val = cpu_to_fdt32((unsigned long)sys_table);
> >> +     status = fdt_setprop(fdt, node, "efi-system-table",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32(desc_size);
> >> +     status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32(map_size);
> >> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32((unsigned long)memory_map);
> >> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >
> > We have one function doing two completely different jobs here (as
> > documented by the name).  Can it be split?
> 
> I had it split, but due to the address/size pairs that needed to be
> passed around
> to free the allocated memory on error I combined them.  I'll take
> another look at it.
> I think pulling the allocations out of the function may make this
> cleaner, and could
> also make the removal of the guessed new FTD size easier to remove.
> I'll need to handle
> re-trying the FTD allocation in order to gracefully handle significant
> growth in the DTB.
> 
> 
> >
> >> +
> >> +     /* Now we need to exit boot services.  We need the key from
> >> +      * the most recent read of the memory map to do this.  We can't
> >> +      * free this buffer in the normal case, but do free it when
> >> +      * exit_boot_services() fails or adding the memory map to the FDT
> >> +      * fails.
> >> +      */
> >> +     status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> >> +                             handle, mmap_key);
> >> +
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "exit boot services failed.\n");
> >> +             goto fail_free_mmap;
> >> +     }
> >> +
> >> +     return new_fdt_addr;
> >> +
> >> +fail_free_mmap:
> >> +     efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> >> +
> >> +fail_free_new_fdt:
> >> +     efi_free(sys_table, new_fdt_size, new_fdt_addr);
> >> +
> >> +fail:
> >> +     return 0;
> >> +}
> >> +
> >> +
> >> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> >> +           unsigned long *zimage_addr)
> >> +{
> >> +     efi_loaded_image_t *image;
> >> +     int status;
> >> +     unsigned long nr_pages;
> >> +     const struct fdt_region *region;
> >> +
> >> +     void *fdt;
> >> +     int err;
> >> +     int node;
> >> +     unsigned long zimage_size = 0;
> >> +     unsigned long dram_base;
> >> +     /* addr/point and size pairs for memory management*/
> >> +     u64 initrd_addr;
> >> +     u64 initrd_size = 0;
> >> +     u64 fdt_addr;  /* Original DTB */
> >> +     u64 fdt_size = 0;
> >> +     u64 kernel_reserve_addr;
> >> +     u64 kernel_reserve_size = 0;
> >> +     char *cmdline_ptr;
> >> +     unsigned long cmdline_size = 0;
> >> +     unsigned long new_fdt_addr;
> >> +
> >> +     efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> >> +
> >> +     /* Check if we were booted by the EFI firmware */
> >> +     if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> >> +             goto fail;
> >> +
> >> +     efi_printk(sys_table, "Booting Linux using EFI stub.\n");
> >> +
> >> +
> >> +     /* get the command line from EFI, using the LOADED_IMAGE protocol */
> >> +     status = efi_call_phys3(sys_table->boottime->handle_protocol,
> >> +                             handle, &proto, (void *)&image);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> >> +             goto fail;
> >> +     }
> >> +
> >> +     /* We are going to copy this into device tree, so we don't care where in
> >> +      * memory it is.
> >> +      */
> >> +     cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
> >> +                                            &cmdline_size, 0xFFFFFFFF);
> >> +     if (!cmdline_ptr) {
> >> +             efi_printk(sys_table, "ERROR converting command line to ascii.\n");
> >> +             goto fail;
> >> +     }
> >> +
> >> +     /* We first load the device tree, as we need to get the base address of
> >> +      * DRAM from the device tree.  The zImage, device tree, and initrd
> >> +      * have address restrictions that are relative to the base of DRAM.
> >> +      */
> >> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> >> +                                   0xffffffff, &fdt_addr, &fdt_size);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Error loading dtb blob\n");
> >> +             goto fail_free_cmdline;
> >> +     }
> >> +
> >> +     err = fdt_check_header((void *)(unsigned long)fdt_addr);
> >> +     if (err != 0) {
> >> +             efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +     if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
> >> +             efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
> >> +             goto fail_free_dtb;
> >> +
> >> +     }
> >> +
> >> +
> >> +     /* Look up the base of DRAM from the device tree.*/
> >> +     fdt = (void *)(u32)fdt_addr;
> >> +     node = fdt_subnode_offset(fdt, 0, "memory");
> >> +     region = fdt_getprop(fdt, node, "reg", NULL);
> >> +     if (region) {
> >> +             dram_base = fdt64_to_cpu(region->base);
> >> +     } else {
> >> +             efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +
> >> +     /* Reserve memory for the uncompressed kernel image. */
> >> +     kernel_reserve_addr = dram_base;
> >> +     kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> >> +     nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> >> +     status = efi_call_phys4(sys_table->boottime->allocate_pages,
> >> +                             EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> >> +                             nr_pages, &kernel_reserve_addr);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +
> >> +     /* Relocate the zImage, if required. */
> >> +     zimage_size = image->image_size;
> >> +     status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
> >> +                              dram_base + MIN_ZIMAGE_OFFSET,
> >> +                              dram_base + MAX_ZIMAGE_OFFSET);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to relocate kernel\n");
> >> +             goto fail_free_kernel_reserve;
> >> +     }
> >> +
> >> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> >> +                                   dram_base + MAX_ZIMAGE_OFFSET,
> >> +                                   &initrd_addr, &initrd_size);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Error loading initrd\n");
> >> +             goto fail_free_zimage;
> >> +     }
> >> +
> >> +     new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
> >> +                                             dram_base, fdt, &fdt_size,
> >> +                                             cmdline_ptr, &cmdline_size,
> >> +                                             initrd_addr, initrd_size);
> >> +
> >> +     if (new_fdt_addr == 0) {
> >> +             efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
> >> +             goto fail_free_initrd;
> >> +     }
> >
> > Ideally, we shouldn't have one error message for two completely
> > different causes.
> >
> > The printk could move into update_fdt_and_exit_boot() and split
> > into more specific cases.
> >
> >> +
> >> +
> >> +     /* Now we need to return the FDT address to the calling
> >> +      * assembly to this can be used as part of normal boot.
> >> +      */
> >> +     return new_fdt_addr;
> >> +
> >> +fail_free_initrd:
> >> +     efi_free(sys_table, initrd_size, initrd_addr);
> >> +
> >> +fail_free_zimage:
> >> +     efi_free(sys_table, zimage_size, *zimage_addr);
> >> +
> >> +fail_free_kernel_reserve:
> >> +     efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
> >> +
> >> +fail_free_dtb:
> >> +     efi_free(sys_table, fdt_size, fdt_addr);
> >> +
> >> +fail_free_cmdline:
> >> +     efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
> >> +
> >> +fail:
> >> +     return EFI_STUB_ERROR;
> >> +}
> >> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
> >> index 75189f1..491e752 100644
> >> --- a/arch/arm/boot/compressed/head.S
> >> +++ b/arch/arm/boot/compressed/head.S
> >> @@ -120,21 +120,100 @@
> >>   */
> >>               .align
> >>               .arm                            @ Always enter in ARM state
> >> +             .text
> >>  start:
> >>               .type   start,#function
> >> -             .rept   7
> >> +#ifdef CONFIG_EFI_STUB
> >> +             @ Magic MSDOS signature for PE/COFF + ADD opcode
> >> +             .word   0x62805a4d
> >
> > Did you get a chance to respond to the endianness issue I raised?
> For now the EFI stub only supports LE, and I need to update
> the Kconfig to reflect this.  Adding BE should be possible, but I don't
> plan to work on that at this time.

OK, so long as that is made explicit in Kconfig, that sounds
reasonable.
> 
> 
> 
> >> +#else
> >> +             mov     r0, r0
> >> +#endif
> >> +             .rept   5
> >>               mov     r0, r0
> >>               .endr
> >> -   ARM(              mov     r0, r0          )
> >> -   ARM(              b       1f              )
> >> - THUMB(              adr     r12, BSYM(1f)   )
> >> - THUMB(              bx      r12             )
> >> +
> >> +             @ zimage_continue will be in ARM or thumb mode as configured
> >> + THUMB(              adrl    r12, BSYM(zimage_continue))
> >> + ARM(                adrl    r12, zimage_continue)
> >> +             bx      r12
> >
> > Note that BSYM() can be used both in ARM and Thumb kernels.
> >
> > In any case, ARM kernels cannot contain BX instructions because we still
> > support ARMv4 (which doesn't have it).
> >
> > I'm presuming you found zimage_continue is too far away for adr here,
> > which is why you changed it.  Assuming that't the case, this might make
> > sense:
> >
> >         adrl    r12, BSYM(zimage_continue)
> >  ARM(   mov     pc, r12 )
> >  THUMB( bx      r12     )
> 
> Yes, I changed this due to lack of range.
> 
> >
> >> + THUMB(              .thumb                  )
> >
> > For tidiness, it's better to avoid this dangling .thumb ... move it
> > to just before zimage_continue instead, since efi_stub_entry has to be
> > ARM anyway.
> 
> OK
> >
> >>
> >>               .word   0x016f2818              @ Magic numbers to help the loader
> >>               .word   start                   @ absolute load/run zImage address
> >>               .word   _edata                  @ zImage end address
> >> +
> >> +#ifdef CONFIG_EFI_STUB
> >> +             @ Portions of the MSDOS file header must be at offset
> >> +             @ 0x3c from the start of the file.  All PE/COFF headers
> >> +             @ are kept contiguous for simplicity.
> >> +#include "efi-header.S"
> >> +
> >> +efi_stub_entry:
> >> +             @ The EFI stub entry point is not at a fixed address, however
> >> +             @ this address must be set in the PE/COFF header.
> >> +             @ EFI entry point is in A32 mode, switch to T32 if configured.
> >> + THUMB(              .arm                    )
> >
> > ^So, you can lose .arm here too (but keep the comment -- that's valuable
> > info)
> >
> >> + THUMB(              adr     r12, BSYM(1f)   )
> >> + THUMB(              bx      r12             )
> >>   THUMB(              .thumb                  )
> >>  1:
> >> +             @ Save lr on stack for possible return to EFI firmware.
> >> +             @ Don't care about fp, but need 64 bit alignment....
> >> +             stmfd   sp!, {fp, lr}
> >> +
> >> +             @ Save args to EFI app across got fixup call
> >> +             stmfd   sp!, {r0, r1}
> >
> > Mostly minor coding nits follow...
> 
> I'll go through these and update the code.  I appreciate your review,
> as I am new to ARM assembly.

No problem -- it's already not far off.

I think my comments were all tidiness rather than correctness issues.

Cheers
---Dave

> 
> >
> >
> > stmfd sp!, {r0, r1, fp, lr} ?
> >
> >> +             ldmfd   sp!, {r0, r1}
> >> +
> >> +             @ allocate space on stack for return of new entry point of
> >> +             @ zImage, as EFI stub may copy the kernel.  Pass address
> >> +             @ of space in r2 - EFI stub will fill in the pointer.
> >> +
> >> +             sub     sp, #8                  @ we only need 4 bytes,
> >
> > I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?
> >
> > kernel asm is written in the traditional syntax, which means explicit
> > source and destination registers for instructions like this:
> >
> >         sub     sp, sp, #8
> >
> > Since the EFI stub code will only be built with new toolchains it
> > probably doesn't matter, but it's best to be consistent for readability
> > purposes.
> >
> >> +                                             @ but keep stack 8 byte aligned.
> >> +             mov     r2, sp
> >> +             @ Pass our actual runtime start address in pointer data
> >> +             adr     r11, LC0                @ address of LC0 at run time
> >> +             ldr     r12, [r11, #0]          @ address of LC0 at link time
> >> +
> >> +             sub     r3, r11, r12            @ calculate the delta offset
> >> +             str     r3, [r2, #0]
> >> +             bl      efi_entry
> >> +
> >> +             @ get new zImage entry address from stack, put into r3
> >> +             ldr     r3, [sp, #0]
> >> +             add     sp, #8  @ restore stack
> >
> >         add     sp, sp, #8
> >
> >> +
> >> +             @ Check for error return from EFI stub (0xFFFFFFFF)
> >> +             ldr     r1, =0xffffffff
> >
> > Minor nit, but ldr= is wasteful for this.
> >
> > You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
> > smart enough to translate this)...
> >
> >> +             cmp     r0, r1
> >
> > ...alternatively, don't use r1 at all and do:
> >
> >         cmn     r0, #1
> >
> >> +             beq     efi_load_fail
> >> +
> >> +
> >> +             @ Save return values of efi_entry
> >> +             stmfd   sp!, {r0, r3}
> >> +             bl      cache_clean_flush
> >> +             bl      cache_off
> >
> > Why turn the cache off?  Does that mean that EFI may launch images with
> > the cache enabled?
> >
> > If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> > off is not safe.
> >
> > (Hmm, the UEFI spec seems to suggest "yes" for these questions)
> >
> >> +             ldmfd   sp!, {r0, r3}
> >> +
> >> +             @ put DTB address in r2, it was returned by EFI entry
> >> +             mov     r2, r0
> >> +             ldr     r1, =0xffffffff         @ DTB machine type
> >
> > mov/mvn: see above
> >
> >> +             mov     r0, #0  @ r0 is 0
> >
> > Useless comment: maybe say why you're doing this ("r0 is 0, as required
> > by the kernel boot protocol", or something like that).
> >
> >> +
> >> +             @ Branch to (possibly) relocated zImage entry that is in r3
> >> +             bx      r3
> >> +
> >> +efi_load_fail:
> >> +             @ Return EFI_LOAD_ERROR to EFI firmware on error.
> >> +             @ Switch back to ARM mode for EFI is done based on
> >> +             @ return address on stack
> >> +             ldr     r0, =0x80000001
> >> +             ldmfd   sp!, {fp, pc}
> >> +#endif
> >> +
> >> +zimage_continue:
> >>               mrs     r9, cpsr
> >>  #ifdef CONFIG_ARM_VIRT_EXT
> >>               bl      __hyp_stub_install      @ get into SVC mode, reversibly
> >> @@ -167,7 +246,6 @@ not_angel:
> >>                * by the linker here, but it should preserve r7, r8, and r9.
> >>                */
> >>
> >> -             .text
> >>
> >>  #ifdef CONFIG_AUTO_ZRELADDR
> >>               @ determine final kernel image address
> >> --
> >> 1.7.10.4
> >>
> >>
> >> _______________________________________________
> >> linux-arm-kernel mailing list
> >> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 16/17] Add EFI stub for ARM
@ 2013-08-13 14:21         ` Dave P Martin
  0 siblings, 0 replies; 88+ messages in thread
From: Dave P Martin @ 2013-08-13 14:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 08, 2013 at 10:57:29PM +0100, Roy Franz wrote:
> On Wed, Aug 7, 2013 at 11:05 AM, Dave Martin <Dave.Martin@arm.com> wrote:
> > On Tue, Aug 06, 2013 at 08:45:12PM -0700, Roy Franz wrote:
> >> This patch adds EFI stub support for the ARM Linux kernel.  The EFI stub
> >> operations similarly to the x86 stub: it is a shim between the EFI firmware
> >> and the normal zImage entry point, and sets up the environment that the
> >> zImage is expecting.  This includes loading the initrd (optionaly) and
> >> device tree from the system partition based on the kernel command line.
> >> The stub updates the device tree as necessary, including adding reserved
> >> memory regions and adding entries for EFI runtime services. The PE/COFF
> >> "MZ" header at offset 0 results in the first instruction being an add
> >> that corrupts r5, which is not used by the zImage interface.
> >
> > Some more comments below ... note that I haven't really looked at the C
> > code in depth.
> 
> Responses below, and I'm working on incorporating suggested changes
> for the next version.

I few responses-to-responses from me inline.  Your repose supersedes
most of this anyhow.

Cheers
---Dave

> 
> Thanks,
> Roy
> 
> >
> > Cheers
> > ---Dave
> >
> >>
> >> Signed-off-by: Roy Franz <roy.franz@linaro.org>
> >> ---
> >>  arch/arm/boot/compressed/Makefile     |   18 +-
> >>  arch/arm/boot/compressed/efi-header.S |  114 ++++++++
> >>  arch/arm/boot/compressed/efi-stub.c   |  514 +++++++++++++++++++++++++++++++++
> >>  arch/arm/boot/compressed/head.S       |   90 +++++-
> >>  4 files changed, 728 insertions(+), 8 deletions(-)
> >>  create mode 100644 arch/arm/boot/compressed/efi-header.S
> >>  create mode 100644 arch/arm/boot/compressed/efi-stub.c
> >>
> >> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> >> index 7ac1610..c62826a 100644
> >> --- a/arch/arm/boot/compressed/Makefile
> >> +++ b/arch/arm/boot/compressed/Makefile
> >> @@ -106,8 +106,22 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
> >>  $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
> >>       $(addprefix $(obj)/,$(libfdt_hdrs))
> >>
> >> +$(addprefix $(obj)/,$(libfdt_objs) efi-stub.o): \
> >> +     $(addprefix $(obj)/,$(libfdt_hdrs))
> >> +
> >
> > Don't we make $(libfdt_objs) depend on $(libfdt_hdrs) twice, now?
> >
> > Would it make sense just to add efi-stub.o to the list of targets in the
> > original rule?
> 
> Yes, change made.
> >
> >>  ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
> >> -OBJS += $(libfdt_objs) atags_to_fdt.o
> >> +OBJS += atags_to_fdt.o
> >> +USE_LIBFDT = y
> >> +endif
> >> +
> >> +ifeq ($(CONFIG_EFI_STUB),y)
> >> +CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
> >> +OBJS += efi-stub.o
> >> +USE_LIBFDT = y
> >> +endif
> >> +
> >> +ifeq ($(USE_LIBFDT),y)
> >> +OBJS += $(libfdt_objs)
> >>  endif
> >>
> >>  targets       := vmlinux vmlinux.lds \
> >> @@ -125,7 +139,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
> >>  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
> >>  endif
> >>
> >> -ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
> >> +ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) -fno-stack-protector
> >
> > You don't appear to explain this change anywhere.
> 
> Prior to my changes, even though the stack protector was not disabled,
> it was not actually used. GCC uses a heuristic
> based on the size of the stack whether to enable the stack protector,
> and the threshold to trigger its use was not met, so no stack checking
> was actually being done.  In order to do stack protection, a few
> __stack_chk_* functions/variable need to be provided by the
> application.  I worked a bit on adding these, but could not get them
> working in the stub/decompressor.  The x86 arch also has
> "-fno-stack-protector" defined for its compressed boot stub, so I
> decided to go that route as well.
> 
> >
> >>  asflags-y := -DZIMAGE
> >>
> >>  # Supply kernel BSS size to the decompressor via a linker symbol.
> >> diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
> >> new file mode 100644
> >> index 0000000..6ff32cc
> >> --- /dev/null
> >> +++ b/arch/arm/boot/compressed/efi-header.S
> >> @@ -0,0 +1,114 @@
> >> +@ Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> >> +@
> >> +@ This file contains the PE/COFF header that is part of the
> >> +@ EFI stub.
> >> +@
> >> +
> >> +     .org    0x3c
> >> +     @
> >> +     @ The PE header can be anywhere in the file, but for
> >> +     @ simplicity we keep it together with the MSDOS header
> >> +     @ The offset to the PE/COFF header needs to be at offset
> >> +     @ 0x3C in the MSDOS header.
> >> +     @ The only 2 fields of the MSDOS header that are used are this
> >> +     @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
> >> +     @
> >> +     .long   pe_header                       @ Offset to the PE header.
> >
> > Is there any chance of merging this with the equivalent x86 code?
> >
> > The PE/COFF header is much the same in both cases, although there
> > are some differences.  Maybe it would be more trouble than it is
> > worth...
> 
> I think it would be more pain than gain.  We are planning to add arm64 stub
> support next, so we'd end up with 4 architectures sharing this assembly file,
> which I think would be painful from a patch submission/review point of view.
> 
> >
> >> +
> >> +      .align 3
> >> +pe_header:
> >> +
> >> +
> >> +pe_header:
> >
> > Duplicate label?
> 
> Yup, fixed.
> >
> >> +     .ascii  "PE"
> >> +     .short  0
> >> +
> >> +coff_header:
> >> +     .short  0x01c2                          @ ARM or Thumb
> >> +     .short  2                               @ nr_sections
> >> +     .long   0                               @ TimeDateStamp
> >> +     .long   0                               @ PointerToSymbolTable
> >> +     .long   1                               @ NumberOfSymbols
> >> +     .short  section_table - optional_header @ SizeOfOptionalHeader
> >> +     .short  0x306                           @ Characteristics.
> >> +                                             @ IMAGE_FILE_32BIT_MACHINE |
> >> +                                             @ IMAGE_FILE_DEBUG_STRIPPED |
> >> +                                             @ IMAGE_FILE_EXECUTABLE_IMAGE |
> >> +                                             @ IMAGE_FILE_LINE_NUMS_STRIPPED
> >> +
> >> +optional_header:
> >> +     .short  0x10b                           @ PE32 format
> >> +     .byte   0x02                            @ MajorLinkerVersion
> >> +     .byte   0x14                            @ MinorLinkerVersion
> >> +
> >> +     .long   0                               @ SizeOfCode
> >
> > Do we need to fill in SizeOfCode with a real value?  It looks like x86
> > does.
> >
> > We should probably fill this in unless there's a documented ABI for EFI
> > boot on ARM which explicitly doesn't require these.
> 
> I will investigate/fix this.
> 
> >
> >> +
> >> +     .long   0                               @ SizeOfInitializedData
> >> +     .long   0                               @ SizeOfUninitializedData
> >> +
> >> +     .long   efi_stub_entry                  @ AddressOfEntryPoint
> >> +     .long   efi_stub_entry                  @ BaseOfCode
> >> +     .long   0                               @ data
> >> +
> >> +extra_header_fields:
> >> +     .long   0                               @ ImageBase
> >> +     .long   0x20                            @ SectionAlignment
> >> +     .long   0x20                            @ FileAlignment
> >> +     .short  0                               @ MajorOperatingSystemVersion
> >> +     .short  0                               @ MinorOperatingSystemVersion
> >> +     .short  0                               @ MajorImageVersion
> >> +     .short  0                               @ MinorImageVersion
> >> +     .short  0                               @ MajorSubsystemVersion
> >> +     .short  0                               @ MinorSubsystemVersion
> >> +     .long   0                               @ Win32VersionValue
> >> +
> >> +     .long   _edata                          @ SizeOfImage
> >> +
> >> +     @ Everything before the entry point is considered part of the header
> >> +     .long   efi_stub_entry                  @ SizeOfHeaders
> >> +     .long   0                               @ CheckSum
> >> +     .short  0xa                             @ Subsystem (EFI application)
> >> +     .short  0                               @ DllCharacteristics
> >> +     .long   0                               @ SizeOfStackReserve
> >> +     .long   0                               @ SizeOfStackCommit
> >> +     .long   0                               @ SizeOfHeapReserve
> >> +     .long   0                               @ SizeOfHeapCommit
> >> +     .long   0                               @ LoaderFlags
> >> +     .long   0x0                             @ NumberOfRvaAndSizes
> >> +
> >> +     # Section table
> >> +section_table:
> >> +
> >> +     #
> >> +     # The EFI application loader requires a relocation section
> >> +     # because EFI applications must be relocatable.  This is a
> >> +     # dummy section as far as we are concerned.
> >> +     #
> >> +     .ascii  ".reloc"
> >> +     .byte   0
> >> +     .byte   0                       @ end of 0 padding of section name
> >> +     .long   0
> >> +     .long   0
> >> +     .long   0                       @ SizeOfRawData
> >> +     .long   0                       @ PointerToRawData
> >> +     .long   0                       @ PointerToRelocations
> >> +     .long   0                       @ PointerToLineNumbers
> >> +     .short  0                       @ NumberOfRelocations
> >> +     .short  0                       @ NumberOfLineNumbers
> >> +     .long   0x42100040              @ Characteristics (section flags)
> >> +
> >> +
> >> +     .ascii  ".text"
> >> +     .byte   0
> >> +     .byte   0
> >> +     .byte   0                       @ end of 0 padding of section name
> >> +     .long   _edata - efi_stub_entry         @ VirtualSize
> >> +     .long   efi_stub_entry                  @ VirtualAddress
> >> +     .long   _edata - efi_stub_entry         @ SizeOfRawData
> >> +     .long   efi_stub_entry                  @ PointerToRawData
> >> +
> >> +     .long   0               @ PointerToRelocations (0 for executables)
> >> +     .long   0               @ PointerToLineNumbers (0 for executables)
> >> +     .short  0               @ NumberOfRelocations  (0 for executables)
> >> +     .short  0               @ NumberOfLineNumbers  (0 for executables)
> >> +     .long   0xe0500020      @ Characteristics (section flags)
> >
> > Can you explain why x86 needs an extra section (the .setup thing)?
> > I haven't dug into that in enough detail to understand it yet...
> 
> I will look into that, I don't know off hand.  I simplified the header
> for ARM as much as I could
> for booting with EDK2.
> >
> >> diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
> >> new file mode 100644
> >> index 0000000..b817ea3
> >> --- /dev/null
> >> +++ b/arch/arm/boot/compressed/efi-stub.c
> >> @@ -0,0 +1,514 @@
> >> +/*
> >> + * linux/arch/arm/boot/compressed/efi-stub.c
> >> + *
> >> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> >> + *
> >> + * This file implements the EFI boot stub for the ARM kernel
> >> + *
> >> + * 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 <libfdt.h>
> >> +
> >> +
> >> +/* Error code returned to ASM code instead of valid FDT address. */
> >> +#define EFI_STUB_ERROR               (~0)
> >
> > Can we put that into a suitable hedaer and use it in compressed/head.S,
> > instead of the magic 0xffffffff?  (Assuming that value is supposed to
> > match EFI_STUB_ERROR)
> 
> Yes, I will do this.
> >
> >> +
> >> +/* EFI function call wrappers.  These are not required for
> >> + * ARM, but wrappers are required for X86 to convert between
> >> + * ABIs.  These wrappers are provided to allow code sharing
> >> + * between X86 and ARM.  Since these wrappers directly invoke the
> >> + * EFI function pointer, the function pointer type must be properly
> >> + * defined, which is not the case for X86  One advantage of this is
> >> + * it allows for type checking of arguments, which is not
> >> + * possible with the X86 wrappers.
> >> + */
> >> +#define efi_call_phys0(f)                    f()
> >> +#define efi_call_phys1(f, a1)                        f(a1)
> >> +#define efi_call_phys2(f, a1, a2)            f(a1, a2)
> >> +#define efi_call_phys3(f, a1, a2, a3)                f(a1, a2, a3)
> >> +#define efi_call_phys4(f, a1, a2, a3, a4)    f(a1, a2, a3, a4)
> >> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)        f(a1, a2, a3, a4, a5)
> >> +
> >> +/* The maximum uncompressed kernel size is 32 MBytes, so we will reserve
> >> + * that for the decompressed kernel.  We have no easy way to tell what
> >> + * the actuall size of code + data the uncompressed kernel will use.
> >> + */
> >> +#define MAX_UNCOMP_KERNEL_SIZE       0x02000000
> >
> > Can we fish the decompressed data size out of zImage, like the existing
> > zImage code does?  (see compressed/head.S:207).  I don't see why this
> > needs to be compile-time constant.
> 
> I am attempting to make sure all the memory used is accounted for in
> the EFI memory map,
> so I care not only about the uncompressed size, but also the BSS.  If
> I get the uncompressed
> image size, and use that for the allocation, the kernel will overwrite
> memory immediately following it.
> I had implemented what you suggested and ran into this problem.

Hmmm, it looks like I misunderstood what gets appended to the compressed
data.

However, it looks like the size of the kernel's bss is also made
available, via a link-time symbol _kernel_bss_size:

	KBSS_SZ = $(shell $(CROSS_COMPILE)size $(obj)/../../../../vmlinux | \
	                awk 'END{print $$3}')
	LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ)

You could get at that by

	extern char _kernel_bss_size;

	/* ... */

	 ... (unsigned long)&_kernel_bss_size ...

> 
> >
> > Someday, someone may try to grow the kernel image beyond 32M.  It would
> > be nice to keep the number of things that breaks to a minimum, to ease
> > potential pain later.
> 
> I picked 32 MBytes based on some discussions of the boot process, and
> my understanding
> is that 32 MBytes is a somewhat hard upper limit on kernel size.

I guess we can address this one as and when.

I suspect that growth beyond 32MB may happen eventually, but it's
going to involve a bit of pain whatever.

So long as efi_stub barfs if the decompressed kernel + BSS doesn't
fit in the available space (you can refer to _kernel_bss_size to
check that).

> 
> >
> >> +
> >> +/* The kernel zImage should be located between 32 Mbytes
> >> + * and 128 MBytes from the base of DRAM.  The min
> >> + * address leaves space for a maximal size uncompressed image,
> >> + * and the max address is due to how the zImage decompressor
> >> + * picks a destination address.
> >> + */
> >> +#define MAX_ZIMAGE_OFFSET    0x08000000
> >
> > The maximum zImage offset is actually 1 less than this.  I think it's
> > just the name of the macro that is misleading, since you use it
> > correctly as an upper bound for memory allocation, so far as I can
> > see.
> >
> > Maybe ZIMAGE_OFFSET_LIMIT or something similar would work.
> 
> I'll rename this.
> 
> >
> >> +#define MIN_ZIMAGE_OFFSET    MAX_UNCOMP_KERNEL_SIZE
> >> +
> >> +#define MAX_CMDLINE_LEN              500
> >
> > This is a random looking number.  Is this supposed to match something
> > somewhere?  Does it serve any purpose other than acting as a sanity
> > limit?
> >
> > If this limit doesn't exist, then an unreasonably large command-line
> > passed by EFI would just lead to a memory allocation failure somewhere,
> > which feels like the right behaviour...
> >
> > If we can avoid building in arbitrary limits, it helps avoid surprises
> > later.
> 
> 
> This is just a sanity check, which should be easy to remove.  I think
> the failure mode will be a huge device tree being created,
> rather than an allocation failure.  In reality I think the limit will
> set by the EFI firmware - I doubt it is possible to pass a
> multi-megabyte command line.
> 
> >
> >> +
> >> +struct fdt_region {
> >> +     u64 base;
> >> +     u64 size;
> >> +};
> >> +
> >> +/*
> >> + * Additional size that could be used for FDT entries added by
> >> + * the UEFI OS Loader Estimation based on:
> >> + * EDID (300bytes) + bootargs  + initrd region (20bytes)
> >> + * + system memory region (20bytes) + mp_core entries (200
> >> + * bytes)
> >> + */
> >
> > What does 0x300 have to do with those numbers?
> >
> > When you say "estimate", are we guaranteed never to exceed that?
> > What happens if we do?
> 
> No guarantees, and we fail to boot if we run out of space in the new
> device tree.  This greatly simplifies the code,
> but I agree that it is not that nice.
> 
> >
> >> +#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x300 + MAX_CMDLINE_LEN)
> >> +
> >> +/* Include shared EFI stub code */
> >> +#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
> >> +
> >> +
> >> +static int is_linux_reserved_region(int memory_type)
> >> +{
> >> +     switch (memory_type) {
> >> +     case EFI_RUNTIME_SERVICES_CODE:
> >> +     case EFI_RUNTIME_SERVICES_DATA:
> >> +     case EFI_UNUSABLE_MEMORY:
> >> +     case EFI_ACPI_RECLAIM_MEMORY:
> >> +     case EFI_ACPI_MEMORY_NVS:
> >> +             return 1;
> >> +     default:
> >> +             return 0;
> >> +     }
> >> +}
> >> +
> >> +
> >> +static int relocate_kernel(efi_system_table_t *sys_table,
> >> +                        unsigned long *load_addr, unsigned long *load_size,
> >> +                        unsigned long min_addr, unsigned long max_addr)
> >> +{
> >> +     /* Get current address of kernel. */
> >> +     unsigned long cur_zimage_addr = *load_addr;
> >> +     unsigned long zimage_size = *load_size;
> >> +     unsigned long new_addr = 0;
> >> +     unsigned long nr_pages;
> >> +
> >> +     efi_status_t status;
> >> +
> >> +     if (!load_addr || !load_size)
> >> +             return EFI_INVALID_PARAMETER;
> >> +
> >> +     *load_size = 0;
> >> +     if (cur_zimage_addr > min_addr
> >> +         && (cur_zimage_addr + zimage_size) < max_addr) {
> >> +             /* We don't need to do anything, as kernel at an acceptable
> >> +              * address already.
> >> +              */
> >> +             return EFI_SUCCESS;
> >> +     }
> >> +     /*
> >> +      * The EFI firmware loader could have placed the kernel image
> >> +      * anywhere in memory, but the kernel has restrictions on the
> >> +      * min and max physical address it can run at.
> >> +      */
> >> +     nr_pages = round_up(zimage_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> >
> > It looks like nr_pages is never used in this function.
> 
> Yup, removed.
> >
> >> +
> >> +     status = efi_low_alloc(sys_table, zimage_size, 0,
> >> +                        &new_addr, min_addr);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to alloc memory for kernel.\n");
> >
> > Does efi_printk automatically prepend a suitable prefix?  If not,
> > it might be useful to define a macro to add a standard prefix to all
> > efi_printks here ("zImage: " or similar).
> 
> It doesn't, but I can add one.  Maybe "EFIstub"?  This is really
> separate from the zImage boot, so I think
> it would be helpful to differentiate it.

Sure, just something to disambiguate it.

> >
> > Minor nit: can we have "allocate" instead of "alloc"?
> Sure.
> >
> > I think both messages should say "failed to allocate usable memory".
> > EFI has already allocated memory for the kernel after all: it's
> > just in the wrong place initially.
> >
> >> +             return status;
> >> +     }
> >> +
> >> +     if (new_addr > (max_addr - zimage_size)) {
> >> +             efi_free(sys_table, zimage_size, new_addr);
> >> +             efi_printk(sys_table, "Failed to alloc usable memory for kernel.\n");
> >> +             return EFI_INVALID_PARAMETER;
> >> +     }
> >> +
> >> +     /* We know source/dest won't overlap since both memory ranges
> >> +      * have been allocated by UEFI, so we can safely use memcpy.
> >> +      */
> >> +     memcpy((void *)new_addr, (void *)(unsigned long)cur_zimage_addr,
> >> +            zimage_size);
> >
> > Is it possible for this allocation to fail -- i.e., because UEFI has
> > put us in an unsuitable location which is within the first 128MB of
> > RAM, such that we can't pick a suitable location without overlap?
> 
> I think so, since (in theory at least), other EFI applications could have run
> before us and allocated arbitrary amounts of memory.
> 
> >
> > For the time being though, I think this is impossible because the
> > decompressed Image can't exceed ~32MB (so the zImage should not
> > exceed that either, and both can fit inside 128MB.  It doesn't
> > matter if UEFI's initial load location overlaps the decompressed
> > Image).
> 
> The reason I am avoiding the zImage overlapping the decompressed image
> even though
> the zImage decompressor handles that case is that I want to ensure that
> all memory used during early boot is represented in the EFI memory map.
> By avoiding overlap, I only have to deal with predicting the final
> destination of the
> decompressed kernel.

I guess that makes sense.  If it becomes a constraint, it can be fixed
later, but that probably won't happen for a while.

> 
> >
> > If UEFI put reserved regions with the first 128MB we're likely to
> > be dead anyway, so we shouldn't assume we'll have to cope with that
> > for now...
> 
> For these cases I'd like to be able to return an error message and
> refuse to boot, rather
> than dying during boot.
> 
> In principle, I like the EFI stub being a shim between the EFI
> firmware and the normal zImage boot.  In practice,
> I don't really like having to predict/guess what memory the zImage
> decompressor will use so that we can account for that
> in the EFI memory map.

zImage already suffers from that: you "just have to know" how to
arrange the zImage, initramfs and dtb, per board and per bootlodaer.
AUTO_ZRELADDR provides some extra flexibility, but there are still
arbitrary, unknown constraints which prevent for bootloader from
doing the right thing automatically.

efi_stub should avoid being worse than that, but if we can have
cleaner failures, that's definitely a bonus.

> 
> >
> >> +
> >> +     /* Return the load address and size */
> >> +     *load_addr = new_addr;
> >> +     *load_size = zimage_size;
> >
> > Is zimage_size ever changed?  It looks like it is still equal to the
> > initial value of *load_size at this point.
> 
> Nope, I can get rid of zimage_size and just use *load_size throughout.
> 
> >
> >> +
> >> +
> >> +     return status;
> >> +}
> >> +
> >> +
> >> +/* Convert the unicode UEFI command line to ASCII to pass to kernel.
> >> + * Size of memory allocated return in *cmd_line_len.
> >> + * Returns NULL on error.
> >> + */
> >> +static char *convert_cmdline_to_ascii(efi_system_table_t *sys_table,
> >> +                                   efi_loaded_image_t *image,
> >> +                                   unsigned long *cmd_line_len,
> >> +                                   u32 max_addr)
> >> +{
> >> +     u16 *s2;
> >> +     u8 *s1 = NULL;
> >> +     unsigned long cmdline_addr = 0;
> >> +     int load_options_size = image->load_options_size / 2; /* ASCII */
> >> +     void *options = (u16 *)image->load_options;
> >> +     int options_size = 0;
> >> +     int status;
> >> +     int i;
> >> +     u16 zero = 0;
> >> +
> >> +     if (options) {
> >> +             s2 = options;
> >> +             while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> >> +                     s2++;
> >> +                     options_size++;
> >> +             }
> >> +     }
> >> +
> >> +     if (options_size == 0) {
> >> +             /* No command line options, so return empty string*/
> >> +             options_size = 1;
> >> +             options = &zero;
> >> +     }
> >> +
> >> +     if (options_size > MAX_CMDLINE_LEN)
> >> +             options_size = MAX_CMDLINE_LEN;
> >> +
> >> +     options_size++;  /* NUL termination */
> >
> > Do we care that options_size can now be > load_options_size?
> >
> > I guess image->load_options isn't realistically going to be right at
> > the end of a RAM bank, so probably nothing disastrous will happen if
> > we read off the end of it.
> >
> > It would be tidier to avoid this, though.
> 
> I'll update this to avoid reading past the end of the EFI option string.

OK, fine

> >
> >> +
> >> +     status = efi_high_alloc(sys_table, options_size, 0,
> >> +                         &cmdline_addr, max_addr);
> >> +     if (status != EFI_SUCCESS)
> >> +             return NULL;
> >> +
> >> +     s1 = (u8 *)(unsigned long)cmdline_addr;
> >> +     s2 = (u16 *)options;
> >> +
> >> +     for (i = 0; i < options_size - 1; i++)
> >> +             *s1++ = *s2++;
> >> +
> >> +     *s1 = '\0';
> >> +
> >> +     *cmd_line_len = options_size;
> >> +     return (char *)(unsigned long)cmdline_addr;
> >> +}
> >> +
> >> +static u32 update_fdt_and_exit_boot(efi_system_table_t *sys_table,
> >> +                                 void *handle, unsigned long dram_base,
> >> +                                 void *orig_fdt, u64 *orig_fdt_size,
> >> +                                 char *cmdline_ptr,
> >> +                                 unsigned long *cmdline_size,
> >> +                                 u64 initrd_addr, u64 initrd_size)
> >> +{
> >> +     unsigned long new_fdt_size;
> >> +     unsigned long new_fdt_addr;
> >> +     void *fdt;
> >> +     int node;
> >> +     int status;
> >> +     int i;
> >> +     unsigned long map_size, desc_size;
> >> +     unsigned long mmap_key;
> >> +     efi_memory_desc_t *memory_map;
> >> +     unsigned long fdt_val;
> >> +
> >> +     new_fdt_size = *orig_fdt_size + FDT_ADDITIONAL_ENTRIES_SIZE;
> >> +     status = efi_high_alloc(sys_table, new_fdt_size, 0, &new_fdt_addr,
> >> +                         dram_base + MAX_ZIMAGE_OFFSET);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "ERROR: Unable to allocate memory for new device tree.\n");
> >> +             goto fail;
> >> +     }
> >
> > There are too many error messages in this function (and elsewhere).
> > Many of them are only useful for debugging: for real use, the only
> > interesting kinds of failure for the DT which will be meaningful to the
> > user are "bad device tree" and "out of memory".
> >
> > Also, it would be desirable to make the error messages more consistent;
> > currently we have "Failed to foo", "ERROR: bar", "ERROR moo", "Error baz",
> > and more.
> >
> > We also have "FDT", "fdt", "DTB", "Device Tree", "device tree", all of
> > which mean basically the same thing.
> >
> > You could try wrapping fdt_setprop() with a function which tries to set
> > the property and prints a suitable message if it fails, without having
> > to put explicit efi_printks all over the place.
> 
> I will review all of the messages, and add a consistent prefix as you
> suggested above.

OK (I confess to being a bit pedantic here)

> >
> >> +
> >> +
> >> +     fdt = (void *)new_fdt_addr;
> >> +     status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
> >> +     if (status != 0) {
> >> +             efi_printk(sys_table, "ERROR: Device Tree open_int failed.\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     /* We are done with the original DTB, so free it. */
> >> +     efi_free(sys_table, *orig_fdt_size, (u32)orig_fdt);
> >> +     *orig_fdt_size = 0;
> >> +
> >> +     node = fdt_subnode_offset(fdt, 0, "chosen");
> >> +     if (node < 0) {
> >> +             node = fdt_add_subnode(fdt, 0, "chosen");
> >> +             if (node < 0) {
> >> +                     efi_printk(sys_table, "Error on finding 'chosen' node\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +
> >> +     if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
> >> +             status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
> >> +                                  strlen(cmdline_ptr) + 1);
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new bootarg\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +     /* We are done with original command line, so free it. */
> >> +     efi_free(sys_table, *cmdline_size, (u32)cmdline_ptr);
> >> +     *cmdline_size = 0;
> >> +
> >> +     /* Set intird address/end in device tree, if present */
> >> +     if (initrd_size != 0) {
> >> +             u64 initrd_image_end;
> >> +             u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
> >> +             status = fdt_setprop(fdt, node, "linux,initrd-start",
> >> +                                  &initrd_image_start, sizeof(u64));
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-start'\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +             initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
> >> +             status = fdt_setprop(fdt, node, "linux,initrd-end",
> >> +                                  &initrd_image_end, sizeof(u64));
> >> +             if (status) {
> >> +                     efi_printk(sys_table, "Failed to set new 'linux,initrd-end'\n");
> >> +                     goto fail_free_new_fdt;
> >> +             }
> >> +     }
> >> +
> >> +     /* Update memory map in the device tree. The memory node must
> >> +      * be present in the tree.*/
> >> +     node = fdt_subnode_offset(fdt, 0, "memory");
> >> +     if (node < 0) {
> >> +             efi_printk(sys_table, "ERROR: FDT memory node does not exist in DTB.\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +
> >> +     status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> >> +                                 &desc_size, &mmap_key);
> >> +     if (status != EFI_SUCCESS)
> >> +             goto fail_free_new_fdt;
> >> +
> >> +     for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> >> +             efi_memory_desc_t *desc;
> >> +             unsigned long m = (unsigned long)memory_map;
> >> +             desc = (efi_memory_desc_t *)(m + (i * desc_size));
> >> +
> >> +             if (is_linux_reserved_region(desc->type)) {
> >> +                     status = fdt_add_mem_rsv(fdt, desc->phys_addr,
> >> +                                              desc->num_pages * EFI_PAGE_SIZE);
> >> +                     if (status != 0) {
> >> +                             efi_printk(sys_table, "ERROR: Failed to add 'memreserve' to fdt.\n");
> >> +                             goto fail_free_mmap;
> >> +                     }
> >> +             }
> >> +     }
> >> +
> >> +
> >> +     /* Add FDT entries for EFI runtime services in chosen node.
> >> +      * We need to add the final memory map, so this is done at
> >> +      * the very end.
> >> +      */
> >> +     node = fdt_subnode_offset(fdt, 0, "chosen");
> >> +     fdt_val = cpu_to_fdt32((unsigned long)sys_table);
> >> +     status = fdt_setprop(fdt, node, "efi-system-table",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-system-table'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32(desc_size);
> >> +     status = fdt_setprop(fdt, node, "efi-mmap-desc-size",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-mmap-desc-size'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32(map_size);
> >> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap-size",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap-size'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >> +     fdt_val = cpu_to_fdt32((unsigned long)memory_map);
> >> +     status = fdt_setprop(fdt, node, "efi-runtime-mmap",
> >> +                          &fdt_val, sizeof(fdt_val));
> >> +     if (status) {
> >> +             efi_printk(sys_table, "Failed to set new 'efi-runtime-mmap'\n");
> >> +             goto fail_free_new_fdt;
> >> +     }
> >
> > We have one function doing two completely different jobs here (as
> > documented by the name).  Can it be split?
> 
> I had it split, but due to the address/size pairs that needed to be
> passed around
> to free the allocated memory on error I combined them.  I'll take
> another look at it.
> I think pulling the allocations out of the function may make this
> cleaner, and could
> also make the removal of the guessed new FTD size easier to remove.
> I'll need to handle
> re-trying the FTD allocation in order to gracefully handle significant
> growth in the DTB.
> 
> 
> >
> >> +
> >> +     /* Now we need to exit boot services.  We need the key from
> >> +      * the most recent read of the memory map to do this.  We can't
> >> +      * free this buffer in the normal case, but do free it when
> >> +      * exit_boot_services() fails or adding the memory map to the FDT
> >> +      * fails.
> >> +      */
> >> +     status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> >> +                             handle, mmap_key);
> >> +
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "exit boot services failed.\n");
> >> +             goto fail_free_mmap;
> >> +     }
> >> +
> >> +     return new_fdt_addr;
> >> +
> >> +fail_free_mmap:
> >> +     efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> >> +
> >> +fail_free_new_fdt:
> >> +     efi_free(sys_table, new_fdt_size, new_fdt_addr);
> >> +
> >> +fail:
> >> +     return 0;
> >> +}
> >> +
> >> +
> >> +int efi_entry(void *handle, efi_system_table_t *sys_table,
> >> +           unsigned long *zimage_addr)
> >> +{
> >> +     efi_loaded_image_t *image;
> >> +     int status;
> >> +     unsigned long nr_pages;
> >> +     const struct fdt_region *region;
> >> +
> >> +     void *fdt;
> >> +     int err;
> >> +     int node;
> >> +     unsigned long zimage_size = 0;
> >> +     unsigned long dram_base;
> >> +     /* addr/point and size pairs for memory management*/
> >> +     u64 initrd_addr;
> >> +     u64 initrd_size = 0;
> >> +     u64 fdt_addr;  /* Original DTB */
> >> +     u64 fdt_size = 0;
> >> +     u64 kernel_reserve_addr;
> >> +     u64 kernel_reserve_size = 0;
> >> +     char *cmdline_ptr;
> >> +     unsigned long cmdline_size = 0;
> >> +     unsigned long new_fdt_addr;
> >> +
> >> +     efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> >> +
> >> +     /* Check if we were booted by the EFI firmware */
> >> +     if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> >> +             goto fail;
> >> +
> >> +     efi_printk(sys_table, "Booting Linux using EFI stub.\n");
> >> +
> >> +
> >> +     /* get the command line from EFI, using the LOADED_IMAGE protocol */
> >> +     status = efi_call_phys3(sys_table->boottime->handle_protocol,
> >> +                             handle, &proto, (void *)&image);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> >> +             goto fail;
> >> +     }
> >> +
> >> +     /* We are going to copy this into device tree, so we don't care where in
> >> +      * memory it is.
> >> +      */
> >> +     cmdline_ptr = convert_cmdline_to_ascii(sys_table, image,
> >> +                                            &cmdline_size, 0xFFFFFFFF);
> >> +     if (!cmdline_ptr) {
> >> +             efi_printk(sys_table, "ERROR converting command line to ascii.\n");
> >> +             goto fail;
> >> +     }
> >> +
> >> +     /* We first load the device tree, as we need to get the base address of
> >> +      * DRAM from the device tree.  The zImage, device tree, and initrd
> >> +      * have address restrictions that are relative to the base of DRAM.
> >> +      */
> >> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> >> +                                   0xffffffff, &fdt_addr, &fdt_size);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Error loading dtb blob\n");
> >> +             goto fail_free_cmdline;
> >> +     }
> >> +
> >> +     err = fdt_check_header((void *)(unsigned long)fdt_addr);
> >> +     if (err != 0) {
> >> +             efi_printk(sys_table, "ERROR: Device Tree header not valid\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +     if (fdt_totalsize((void *)(unsigned long)fdt_addr) > fdt_size) {
> >> +             efi_printk(sys_table, "ERROR: Incomplete device tree.\n");
> >> +             goto fail_free_dtb;
> >> +
> >> +     }
> >> +
> >> +
> >> +     /* Look up the base of DRAM from the device tree.*/
> >> +     fdt = (void *)(u32)fdt_addr;
> >> +     node = fdt_subnode_offset(fdt, 0, "memory");
> >> +     region = fdt_getprop(fdt, node, "reg", NULL);
> >> +     if (region) {
> >> +             dram_base = fdt64_to_cpu(region->base);
> >> +     } else {
> >> +             efi_printk(sys_table, "Error: no 'memory' node in device tree.\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +
> >> +     /* Reserve memory for the uncompressed kernel image. */
> >> +     kernel_reserve_addr = dram_base;
> >> +     kernel_reserve_size = MAX_UNCOMP_KERNEL_SIZE;
> >> +     nr_pages = round_up(kernel_reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> >> +     status = efi_call_phys4(sys_table->boottime->allocate_pages,
> >> +                             EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> >> +                             nr_pages, &kernel_reserve_addr);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "ERROR allocating memory for uncompressed kernel.\n");
> >> +             goto fail_free_dtb;
> >> +     }
> >> +
> >> +     /* Relocate the zImage, if required. */
> >> +     zimage_size = image->image_size;
> >> +     status = relocate_kernel(sys_table, zimage_addr, &zimage_size,
> >> +                              dram_base + MIN_ZIMAGE_OFFSET,
> >> +                              dram_base + MAX_ZIMAGE_OFFSET);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Failed to relocate kernel\n");
> >> +             goto fail_free_kernel_reserve;
> >> +     }
> >> +
> >> +     status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> >> +                                   dram_base + MAX_ZIMAGE_OFFSET,
> >> +                                   &initrd_addr, &initrd_size);
> >> +     if (status != EFI_SUCCESS) {
> >> +             efi_printk(sys_table, "Error loading initrd\n");
> >> +             goto fail_free_zimage;
> >> +     }
> >> +
> >> +     new_fdt_addr = update_fdt_and_exit_boot(sys_table, handle,
> >> +                                             dram_base, fdt, &fdt_size,
> >> +                                             cmdline_ptr, &cmdline_size,
> >> +                                             initrd_addr, initrd_size);
> >> +
> >> +     if (new_fdt_addr == 0) {
> >> +             efi_printk(sys_table, "Error updating device tree and exiting boot services.\n");
> >> +             goto fail_free_initrd;
> >> +     }
> >
> > Ideally, we shouldn't have one error message for two completely
> > different causes.
> >
> > The printk could move into update_fdt_and_exit_boot() and split
> > into more specific cases.
> >
> >> +
> >> +
> >> +     /* Now we need to return the FDT address to the calling
> >> +      * assembly to this can be used as part of normal boot.
> >> +      */
> >> +     return new_fdt_addr;
> >> +
> >> +fail_free_initrd:
> >> +     efi_free(sys_table, initrd_size, initrd_addr);
> >> +
> >> +fail_free_zimage:
> >> +     efi_free(sys_table, zimage_size, *zimage_addr);
> >> +
> >> +fail_free_kernel_reserve:
> >> +     efi_free(sys_table, kernel_reserve_addr, kernel_reserve_size);
> >> +
> >> +fail_free_dtb:
> >> +     efi_free(sys_table, fdt_size, fdt_addr);
> >> +
> >> +fail_free_cmdline:
> >> +     efi_free(sys_table, cmdline_size, (u32)cmdline_ptr);
> >> +
> >> +fail:
> >> +     return EFI_STUB_ERROR;
> >> +}
> >> diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
> >> index 75189f1..491e752 100644
> >> --- a/arch/arm/boot/compressed/head.S
> >> +++ b/arch/arm/boot/compressed/head.S
> >> @@ -120,21 +120,100 @@
> >>   */
> >>               .align
> >>               .arm                            @ Always enter in ARM state
> >> +             .text
> >>  start:
> >>               .type   start,#function
> >> -             .rept   7
> >> +#ifdef CONFIG_EFI_STUB
> >> +             @ Magic MSDOS signature for PE/COFF + ADD opcode
> >> +             .word   0x62805a4d
> >
> > Did you get a chance to respond to the endianness issue I raised?
> For now the EFI stub only supports LE, and I need to update
> the Kconfig to reflect this.  Adding BE should be possible, but I don't
> plan to work on that at this time.

OK, so long as that is made explicit in Kconfig, that sounds
reasonable.
> 
> 
> 
> >> +#else
> >> +             mov     r0, r0
> >> +#endif
> >> +             .rept   5
> >>               mov     r0, r0
> >>               .endr
> >> -   ARM(              mov     r0, r0          )
> >> -   ARM(              b       1f              )
> >> - THUMB(              adr     r12, BSYM(1f)   )
> >> - THUMB(              bx      r12             )
> >> +
> >> +             @ zimage_continue will be in ARM or thumb mode as configured
> >> + THUMB(              adrl    r12, BSYM(zimage_continue))
> >> + ARM(                adrl    r12, zimage_continue)
> >> +             bx      r12
> >
> > Note that BSYM() can be used both in ARM and Thumb kernels.
> >
> > In any case, ARM kernels cannot contain BX instructions because we still
> > support ARMv4 (which doesn't have it).
> >
> > I'm presuming you found zimage_continue is too far away for adr here,
> > which is why you changed it.  Assuming that't the case, this might make
> > sense:
> >
> >         adrl    r12, BSYM(zimage_continue)
> >  ARM(   mov     pc, r12 )
> >  THUMB( bx      r12     )
> 
> Yes, I changed this due to lack of range.
> 
> >
> >> + THUMB(              .thumb                  )
> >
> > For tidiness, it's better to avoid this dangling .thumb ... move it
> > to just before zimage_continue instead, since efi_stub_entry has to be
> > ARM anyway.
> 
> OK
> >
> >>
> >>               .word   0x016f2818              @ Magic numbers to help the loader
> >>               .word   start                   @ absolute load/run zImage address
> >>               .word   _edata                  @ zImage end address
> >> +
> >> +#ifdef CONFIG_EFI_STUB
> >> +             @ Portions of the MSDOS file header must be at offset
> >> +             @ 0x3c from the start of the file.  All PE/COFF headers
> >> +             @ are kept contiguous for simplicity.
> >> +#include "efi-header.S"
> >> +
> >> +efi_stub_entry:
> >> +             @ The EFI stub entry point is not at a fixed address, however
> >> +             @ this address must be set in the PE/COFF header.
> >> +             @ EFI entry point is in A32 mode, switch to T32 if configured.
> >> + THUMB(              .arm                    )
> >
> > ^So, you can lose .arm here too (but keep the comment -- that's valuable
> > info)
> >
> >> + THUMB(              adr     r12, BSYM(1f)   )
> >> + THUMB(              bx      r12             )
> >>   THUMB(              .thumb                  )
> >>  1:
> >> +             @ Save lr on stack for possible return to EFI firmware.
> >> +             @ Don't care about fp, but need 64 bit alignment....
> >> +             stmfd   sp!, {fp, lr}
> >> +
> >> +             @ Save args to EFI app across got fixup call
> >> +             stmfd   sp!, {r0, r1}
> >
> > Mostly minor coding nits follow...
> 
> I'll go through these and update the code.  I appreciate your review,
> as I am new to ARM assembly.

No problem -- it's already not far off.

I think my comments were all tidiness rather than correctness issues.

Cheers
---Dave

> 
> >
> >
> > stmfd sp!, {r0, r1, fp, lr} ?
> >
> >> +             ldmfd   sp!, {r0, r1}
> >> +
> >> +             @ allocate space on stack for return of new entry point of
> >> +             @ zImage, as EFI stub may copy the kernel.  Pass address
> >> +             @ of space in r2 - EFI stub will fill in the pointer.
> >> +
> >> +             sub     sp, #8                  @ we only need 4 bytes,
> >
> > I presume EFI guarantees a valid stack with 8-byte-aligned sp on entry?
> >
> > kernel asm is written in the traditional syntax, which means explicit
> > source and destination registers for instructions like this:
> >
> >         sub     sp, sp, #8
> >
> > Since the EFI stub code will only be built with new toolchains it
> > probably doesn't matter, but it's best to be consistent for readability
> > purposes.
> >
> >> +                                             @ but keep stack 8 byte aligned.
> >> +             mov     r2, sp
> >> +             @ Pass our actual runtime start address in pointer data
> >> +             adr     r11, LC0                @ address of LC0 at run time
> >> +             ldr     r12, [r11, #0]          @ address of LC0 at link time
> >> +
> >> +             sub     r3, r11, r12            @ calculate the delta offset
> >> +             str     r3, [r2, #0]
> >> +             bl      efi_entry
> >> +
> >> +             @ get new zImage entry address from stack, put into r3
> >> +             ldr     r3, [sp, #0]
> >> +             add     sp, #8  @ restore stack
> >
> >         add     sp, sp, #8
> >
> >> +
> >> +             @ Check for error return from EFI stub (0xFFFFFFFF)
> >> +             ldr     r1, =0xffffffff
> >
> > Minor nit, but ldr= is wasteful for this.
> >
> > You could use mvn r1, #0 (or mov r1, #0xffffffff -- the assembler is
> > smart enough to translate this)...
> >
> >> +             cmp     r0, r1
> >
> > ...alternatively, don't use r1 at all and do:
> >
> >         cmn     r0, #1
> >
> >> +             beq     efi_load_fail
> >> +
> >> +
> >> +             @ Save return values of efi_entry
> >> +             stmfd   sp!, {r0, r3}
> >> +             bl      cache_clean_flush
> >> +             bl      cache_off
> >
> > Why turn the cache off?  Does that mean that EFI may launch images with
> > the cache enabled?
> >
> > If so, are we guaranteed that VA=PA?  Otherwise simply turning the MMU
> > off is not safe.
> >
> > (Hmm, the UEFI spec seems to suggest "yes" for these questions)
> >
> >> +             ldmfd   sp!, {r0, r3}
> >> +
> >> +             @ put DTB address in r2, it was returned by EFI entry
> >> +             mov     r2, r0
> >> +             ldr     r1, =0xffffffff         @ DTB machine type
> >
> > mov/mvn: see above
> >
> >> +             mov     r0, #0  @ r0 is 0
> >
> > Useless comment: maybe say why you're doing this ("r0 is 0, as required
> > by the kernel boot protocol", or something like that).
> >
> >> +
> >> +             @ Branch to (possibly) relocated zImage entry that is in r3
> >> +             bx      r3
> >> +
> >> +efi_load_fail:
> >> +             @ Return EFI_LOAD_ERROR to EFI firmware on error.
> >> +             @ Switch back to ARM mode for EFI is done based on
> >> +             @ return address on stack
> >> +             ldr     r0, =0x80000001
> >> +             ldmfd   sp!, {fp, pc}
> >> +#endif
> >> +
> >> +zimage_continue:
> >>               mrs     r9, cpsr
> >>  #ifdef CONFIG_ARM_VIRT_EXT
> >>               bl      __hyp_stub_install      @ get into SVC mode, reversibly
> >> @@ -167,7 +246,6 @@ not_angel:
> >>                * by the linker here, but it should preserve r7, r8, and r9.
> >>                */
> >>
> >> -             .text
> >>
> >>  #ifdef CONFIG_AUTO_ZRELADDR
> >>               @ determine final kernel image address
> >> --
> >> 1.7.10.4
> >>
> >>
> >> _______________________________________________
> >> linux-arm-kernel mailing list
> >> linux-arm-kernel at lists.infradead.org
> >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

end of thread, other threads:[~2013-08-13 14:22 UTC | newest]

Thread overview: 88+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-07  3:44 [PATCH V2 00/17] EFI stub for ARM Roy Franz
2013-08-07  3:44 ` Roy Franz
2013-08-07  3:44 ` [PATCH 01/17] EFI stub documentation updates Roy Franz
2013-08-07  3:44   ` Roy Franz
2013-08-07  3:44   ` Roy Franz
2013-08-07  3:44 ` [PATCH 02/17] Move common EFI stub code from x86 arch code to common location Roy Franz
2013-08-07  3:44   ` Roy Franz
2013-08-07  3:44 ` [PATCH 03/17] Add system pointer argument to shared EFI stub related functions so they no longer use global system table pointer as they did when part of eboot.c Roy Franz
2013-08-07  3:44   ` Roy Franz
2013-08-07  3:44   ` Roy Franz
2013-08-07 13:08   ` Matt Fleming
2013-08-07 13:08     ` Matt Fleming
2013-08-07 13:08     ` Matt Fleming
2013-08-07 17:10     ` Roy Franz
2013-08-07 17:10       ` Roy Franz
2013-08-07 17:10       ` Roy Franz
2013-08-07 21:55       ` Matt Fleming
2013-08-07 21:55         ` Matt Fleming
2013-08-07  3:45 ` [PATCH 04/17] Rename memory allocation/free functions Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07 13:09   ` Matt Fleming
2013-08-07 13:09     ` Matt Fleming
2013-08-07 17:12     ` Roy Franz
2013-08-07 17:12       ` Roy Franz
2013-08-07 17:12       ` Roy Franz
2013-08-07  3:45 ` [PATCH 05/17] Add minimum address parameter to efi_low_alloc() Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 06/17] rename __get_map() to efi_get_memory_map(), add parameter to optionally return mmap key. The mmap key is required to exit EFI boot services, and allows efi_get_memory_map() to be used for getting final memory map Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 07/17] Enforce minimum alignment of 1 page on allocations. The efi_high_alloc() and efi_low_alloc() functions use the EFI_ALLOCATE_ADDRESS option to the EFI function allocate_pages(), which requires a minimum of page alignment, and rejects all other requests Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 08/17] Allow efi_free() to be called with size of 0, and do nothing in that case Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 09/17] Generalize handle_ramdisks() and rename to handle_cmdline_files() Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 10/17] Renames in handle_cmdline_files() to complete generalization Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07 13:09   ` Matt Fleming
2013-08-07 13:09     ` Matt Fleming
2013-08-07  3:45 ` [PATCH 11/17] Move EFI_READ_CHUNK_SIZE define to shared location Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 12/17] Add proper definitions for some EFI function pointers Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07 13:09   ` Matt Fleming
2013-08-07 13:09     ` Matt Fleming
2013-08-07 13:09     ` Matt Fleming
2013-08-07 17:20     ` Roy Franz
2013-08-07 17:20       ` Roy Franz
2013-08-07 17:20       ` Roy Franz
2013-08-09 14:10   ` Mark Salter
2013-08-09 14:10     ` Mark Salter
2013-08-09 14:10     ` Mark Salter
2013-08-09 14:13     ` Roy Franz
2013-08-09 14:13       ` Roy Franz
2013-08-09 14:13       ` Roy Franz
2013-08-07  3:45 ` [PATCH 13/17] Fix types in EFI calls to match EFI function definitions Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 14/17] resolve warnings found on ARM compile Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 15/17] Add strstr to compressed string.c for ARM Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45 ` [PATCH 16/17] Add EFI stub " Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07 18:05   ` Dave Martin
2013-08-07 18:05     ` Dave Martin
2013-08-07 18:05     ` Dave Martin
2013-08-07 18:33     ` Leif Lindholm
2013-08-07 18:33       ` Leif Lindholm
2013-08-08 21:57     ` Roy Franz
2013-08-08 21:57       ` Roy Franz
2013-08-08 21:57       ` Roy Franz
2013-08-09  0:53       ` Roy Franz
2013-08-09  0:53         ` Roy Franz
2013-08-09  0:53         ` Roy Franz
2013-08-13 14:21       ` Dave P Martin
2013-08-13 14:21         ` Dave P Martin
2013-08-13 14:21         ` Dave P Martin
2013-08-07  3:45 ` [PATCH 17/17] Add config EFI_STUB for ARM to Kconfig Roy Franz
2013-08-07  3:45   ` Roy Franz
2013-08-07  7:44 ` [PATCH V2 00/17] EFI stub for ARM Matt Fleming
2013-08-07  7:44   ` Matt Fleming

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.