2013-09-22 22:45:29

by Roy Franz

[permalink] [raw]
Subject: [PATCH V5 00/18] ARM EFI stub common code

This patch is the common/x86 portion of the ARM EFI stub
patchset broken out. These changes support the addition
of EFI stub support for the ARM and ARM64 architectures.
The common code that is now shared in efi-stub-helper.c
is based on code in the x86 stub that has been generalized
to support other architectures.



(Previously these changes were submitted as part of
the "EFI stub for ARM" patch series that included the
common/x86 changes as well as the ARM changes.)


Changes since V4:
* Fix relocate_kernel() on x86/x86_64 by adding alignment parameter.
The alignment was previously read from the setup_header structure
passed to relocate_kernel was was missed when converting to passing
all values as parameters.
* Undo comment reformatting in efi-stub-helper.c
* Fix incorrect comment in eboot.c (obsolete, based on older intermediate
version.
* Include HPA's patch for command line conversion.

Changes since V3:
* Made relocate_kernel() a shared function.
* Made command line unicode to ASCII conversion ASCII
a shared function
* Updated efi_get_memory_map() to return descriptor version,
and updated x86 stub to use it to retrieve final memory map.
* removed min_address argument of efi_low_alloc(), as it is
no longer used.
* Squashed very tiny patch moving EFI_READ_CHUNK_SIZE into
related patch.


Changes since v2:
* EFI bugfix "correct call to free_pages" that patch series
depends on now in mainline
* remove unnecessary zimage_size variable from relocate_kernel()
* correct return types on EFI functions - should be efi_status_t, not int.

Changes since V1:
* Broke up changes to x86 and common code into more patches.
10 more patches in this series.

H. Peter Anvin (1):
Do proper conversion from UTF-16 to UTF-8

Roy Franz (17):
EFI stub documentation updates
Add proper definitions for some EFI function pointers.
Move common EFI stub code from x86 arch code to common location
Add system table pointer argument to shared functions.
Rename memory allocation/free functions
Enforce minimum alignment of 1 page on allocations.
Move relocate_kernel() to shared file.
Generalize relocate_kernel() for use by other architectures.
Move unicode to ASCII conversion to shared function.
Rename __get_map() to efi_get_memory_map()
generalize efi_get_memory_map()
use efi_get_memory_map() to get final map for x86
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.
Fix types in EFI calls to match EFI function definitions.
resolve warnings found on ARM compile

Documentation/efi-stub.txt | 65 +++
Documentation/x86/efi-stub.txt | 65 ---
arch/x86/Kconfig | 2 +-
arch/x86/boot/compressed/eboot.c | 579 ++-------------------------
arch/x86/boot/compressed/eboot.h | 9 -
drivers/firmware/efi/efi-stub-helper.c | 679 ++++++++++++++++++++++++++++++++
include/linux/efi.h | 50 ++-
7 files changed, 817 insertions(+), 632 deletions(-)
create mode 100644 Documentation/efi-stub.txt
delete mode 100644 Documentation/x86/efi-stub.txt
create mode 100644 drivers/firmware/efi/efi-stub-helper.c

--
1.7.10.4


2013-09-22 22:45:31

by Roy Franz

[permalink] [raw]
Subject: [PATCH 01/18] EFI stub documentation updates

Move efi-stub.txt out of x86 directory and into common directory
in preparation for adding ARM EFI stub support.

Signed-off-by: Roy Franz <[email protected]>
---
Documentation/efi-stub.txt | 65 ++++++++++++++++++++++++++++++++++++++++
Documentation/x86/efi-stub.txt | 65 ----------------------------------------
arch/x86/Kconfig | 2 +-
3 files changed, 66 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..44e6bb6
--- /dev/null
+++ b/Documentation/efi-stub.txt
@@ -0,0 +1,65 @@
+ 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/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

2013-09-22 22:45:36

by Roy Franz

[permalink] [raw]
Subject: [PATCH 06/18] 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.
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 <[email protected]>
Acked-by: Mark Salter <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
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 2f528fb..9e451c6 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -101,6 +101,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++) {
@@ -179,6 +186,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

2013-09-22 22:45:46

by Roy Franz

[permalink] [raw]
Subject: [PATCH 18/18] resolve warnings found on ARM compile

warnings from gcc:
warning: label 'free_pool' defined but not used [-Wunused-label]
warning: value computed is not used [-Wunused-value]




Signed-off-by: Roy Franz <[email protected]>
---
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 4e4127b..2d22aa3 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -169,7 +169,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;
@@ -358,7 +356,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

2013-09-22 22:45:41

by Roy Franz

[permalink] [raw]
Subject: [PATCH 12/18] generalize efi_get_memory_map()

Add arguments for returning the descriptor version and also
the memory map key. The key is required for calling
exit_boot_services().

Signed-off-by: Roy Franz <[email protected]>
---
drivers/firmware/efi/efi-stub-helper.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 4ac274b..b0b2f15 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -49,7 +49,9 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
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 *desc_size,
+ u32 *desc_ver,
+ unsigned long *key_ptr)
{
efi_memory_desc_t *m = NULL;
efi_status_t status;
@@ -77,6 +79,10 @@ again:

if (status != EFI_SUCCESS)
efi_call_phys1(sys_table_arg->boottime->free_pool, m);
+ if (key_ptr && status == EFI_SUCCESS)
+ *key_ptr = key;
+ if (desc_ver && status == EFI_SUCCESS)
+ *desc_ver = desc_version;

fail:
*map = m;
@@ -97,7 +103,8 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
u64 max_addr = 0;
int i;

- status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size);
+ status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size,
+ NULL, NULL);
if (status != EFI_SUCCESS)
goto fail;

@@ -182,7 +189,8 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
unsigned long nr_pages;
int i;

- status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size);
+ status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size,
+ NULL, NULL);
if (status != EFI_SUCCESS)
goto fail;

--
1.7.10.4

2013-09-22 22:46:03

by Roy Franz

[permalink] [raw]
Subject: [PATCH 17/18] Fix types in EFI calls to match EFI function definitions.

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. The wrappers used by x86 prevent
any type checking.
Correct the type of chunksize to be based on native
width as specified by the EFI_FILE_PROTOCOL read()
function.

Signed-off-by: Roy Franz <[email protected]>
---
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 e2ff076..4e4127b 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -322,7 +322,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;
@@ -373,7 +373,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;
@@ -407,7 +408,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;
@@ -457,18 +459,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

2013-09-22 22:46:39

by Roy Franz

[permalink] [raw]
Subject: [PATCH 16/18] Renames in handle_cmdline_files() to complete generalization.

Rename variables to be not initrd specific, as now the function
loads arbitrary files. This change is exclusively renames
and comment changes to reflect the generalization.

Signed-off-by: Roy Franz <[email protected]>
Acked-by: Mark Salter <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
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 d2d845f..e2ff076 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -11,7 +11,7 @@
*/
#define EFI_READ_CHUNK_SIZE (1024 * 1024)

-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,
@@ -274,19 +274,19 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
unsigned long *load_addr,
unsigned long *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;

@@ -301,7 +301,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;
@@ -316,21 +316,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];
@@ -345,7 +345,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 */
@@ -376,13 +376,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;
}
}

@@ -395,7 +395,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,
@@ -429,37 +429,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)
@@ -467,36 +467,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

2013-09-22 22:46:56

by Roy Franz

[permalink] [raw]
Subject: [PATCH 13/18] use efi_get_memory_map() to get final map for x86

Replace the open-coded memory map getting with the
efi_get_memory_map() that is now general enough to use.

Signed-off-by: Roy Franz <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 22 +++++-----------------
1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 4723dc89..05f11c7 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -526,25 +526,12 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
u8 nr_entries;
int i;

- size = sizeof(*mem_map) * 32;
-
-again:
- size += sizeof(*mem_map) * 2;
- _size = size;
- status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map);
- if (status != EFI_SUCCESS)
- return status;
-
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) {
- efi_free(sys_table, _size, (unsigned long)mem_map);
- goto again;
- }
+ status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size,
+ &desc_version, &key);

if (status != EFI_SUCCESS)
- goto free_mem_map;
+ return status;

memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
efi->efi_systab = (unsigned long)sys_table;
@@ -573,6 +560,7 @@ get_map:
goto free_mem_map;

called_exit = true;
+ efi_call_phys1(sys_table->boottime->free_pool, mem_map);
goto get_map;
}

@@ -641,7 +629,7 @@ get_map:
return EFI_SUCCESS;

free_mem_map:
- efi_free(sys_table, _size, (unsigned long)mem_map);
+ efi_call_phys1(sys_table->boottime->free_pool, mem_map);
return status;
}

--
1.7.10.4

2013-09-22 22:46:55

by Roy Franz

[permalink] [raw]
Subject: [PATCH 14/18] Allow efi_free() to be called with size of 0, and do nothing in that case.

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 <[email protected]>
---
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 05f11c7..8b86772 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -505,8 +505,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 b0b2f15..73cf86b 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

2013-09-22 22:46:53

by Roy Franz

[permalink] [raw]
Subject: [PATCH 15/18] Generalize handle_ramdisks() and rename to handle_cmdline_files().

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 <[email protected]>
Acked-by: Mark Salter <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 9 +++++-
drivers/firmware/efi/efi-stub-helper.c | 51 +++++++++++++++++++-------------
2 files changed, 38 insertions(+), 22 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 8b86772..a5628cd 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -442,6 +442,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
u16 *s2;
u8 *s1;
int i;
+ unsigned long ramdisk_addr;
+ unsigned long ramdisk_size;

sys_table = _table;

@@ -499,9 +501,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 73cf86b..d2d845f 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -267,9 +267,12 @@ 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,
+ unsigned long max_addr,
+ unsigned long *load_addr,
+ unsigned long *load_size)
{
struct initrd *initrds;
unsigned long initrd_addr;
@@ -285,19 +288,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 +324,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 +339,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 +389,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: ");
efi_char16_printk(sys_table_arg, filename_16);
efi_printk(sys_table_arg, "\n");
goto close_handles;
@@ -392,7 +401,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 +409,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 +425,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 +442,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 +470,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 +484,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 +498,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

2013-09-22 22:45:35

by Roy Franz

[permalink] [raw]
Subject: [PATCH 07/18] Move relocate_kernel() to shared file.

The relocate_kernel() function will be generalized and used
by all architectures, as they all have similar requirements.

Signed-off-by: Roy Franz <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 34 -------------------------------
drivers/firmware/efi/efi-stub-helper.c | 35 ++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+), 34 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 2a4430a..5bbba86 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -677,40 +677,6 @@ free_mem_map:
return status;
}

-static efi_status_t relocate_kernel(struct setup_header *hdr)
-{
- unsigned long start, nr_pages;
- efi_status_t status;
-
- /*
- * The EFI firmware loader could have placed the kernel image
- * anywhere in memory, but the kernel has various restrictions
- * on the max physical address it can run at. Attempt to move
- * the kernel to boot_params.pref_address, or as low as
- * possible.
- */
- start = hdr->pref_address;
- nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-
- status = efi_call_phys4(sys_table->boottime->allocate_pages,
- EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
- nr_pages, &start);
- if (status != EFI_SUCCESS) {
- 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");
- }
-
- if (status == EFI_SUCCESS)
- memcpy((void *)start, (void *)(unsigned long)hdr->code32_start,
- hdr->init_size);
-
- hdr->pref_address = hdr->code32_start;
- hdr->code32_start = (__u32)start;
-
- return status;
-}

/*
* On success we return a pointer to a boot_params structure, and NULL
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 9e451c6..1d5f219 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -483,3 +483,38 @@ fail:

return status;
}
+
+static efi_status_t relocate_kernel(struct setup_header *hdr)
+{
+ unsigned long start, nr_pages;
+ efi_status_t status;
+
+ /*
+ * The EFI firmware loader could have placed the kernel image
+ * anywhere in memory, but the kernel has various restrictions
+ * on the max physical address it can run at. Attempt to move
+ * the kernel to boot_params.pref_address, or as low as
+ * possible.
+ */
+ start = hdr->pref_address;
+ nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+ status = efi_call_phys4(sys_table->boottime->allocate_pages,
+ EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+ nr_pages, &start);
+ if (status != EFI_SUCCESS) {
+ 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");
+ }
+
+ if (status == EFI_SUCCESS)
+ memcpy((void *)start, (void *)(unsigned long)hdr->code32_start,
+ hdr->init_size);
+
+ hdr->pref_address = hdr->code32_start;
+ hdr->code32_start = (__u32)start;
+
+ return status;
+}
--
1.7.10.4

2013-09-22 22:47:49

by Roy Franz

[permalink] [raw]
Subject: [PATCH 10/18] Do proper conversion from UTF-16 to UTF-8

From: "H. Peter Anvin" <[email protected]>

Improve the conversion of the UTF-16 EFI command line
to UTF-8 for passing to the kernel.

Signed-off-by: Roy Franz <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 3 +-
drivers/firmware/efi/efi-stub-helper.c | 92 ++++++++++++++++++++++++--------
2 files changed, 72 insertions(+), 23 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 5e708c0..4723dc89 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -486,8 +486,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
hdr->type_of_loader = 0x21;

/* Convert unicode cmdline to ascii */
- cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
- &options_size);
+ cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size);
if (!cmdline_ptr)
goto fail;
hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 335d17d..8331892 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -548,61 +548,111 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,

return status;
}
-/* Convert the unicode UEFI command line to ASCII to pass to kernel.
+
+/*
+ * Get the number of UTF-8 bytes corresponding to an UTF-16 character.
+ * This overestimates for surrogates, but that is okay.
+ */
+static int efi_utf8_bytes(u16 c)
+{
+ return 1 + (c >= 0x80) + (c >= 0x800);
+}
+
+/*
+ * Convert an UTF-16 string, not necessarily null terminated, to UTF-8.
+ */
+static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
+{
+ unsigned int c;
+
+ while (n--) {
+ c = *src++;
+ if (n && c >= 0xd800 && c <= 0xdbff &&
+ *src >= 0xdc00 && *src <= 0xdfff) {
+ c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff);
+ src++;
+ n--;
+ }
+ if (c >= 0xd800 && c <= 0xdfff)
+ c = 0xfffd; /* Unmatched surrogate */
+ if (c < 0x80) {
+ *dst++ = c;
+ continue;
+ }
+ if (c < 0x800) {
+ *dst++ = 0xc0 + (c >> 6);
+ goto t1;
+ }
+ if (c < 0x10000) {
+ *dst++ = 0xe0 + (c >> 12);
+ goto t2;
+ }
+ *dst++ = 0xf0 + (c >> 18);
+ *dst++ = 0x80 + ((c >> 12) & 0x3f);
+t2:
+ *dst++ = 0x80 + ((c >> 6) & 0x3f);
+t1:
+ *dst++ = 0x80 + (c & 0x3f);
+ }
+
+ return dst;
+}
+
+/*
+ * 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 *efi_convert_cmdline_to_ascii(efi_system_table_t *sys_table_arg,
- efi_loaded_image_t *image,
- int *cmd_line_len)
+static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
+ efi_loaded_image_t *image,
+ int *cmd_line_len)
{
- u16 *s2;
+ const u16 *s2;
u8 *s1 = NULL;
unsigned long cmdline_addr = 0;
int load_options_size = image->load_options_size / 2; /* ASCII */
- void *options = image->load_options;
- int options_size = 0;
+ const u16 *options = image->load_options;
+ int options_bytes = 0; /* UTF-8 bytes */
+ int options_chars = 0; /* UTF-16 chars */
efi_status_t status;
- int i;
u16 zero = 0;

if (options) {
s2 = options;
- while (*s2 && *s2 != '\n' && options_size < load_options_size) {
+ while (*s2 && *s2 != '\n' && options_bytes < load_options_size) {
+ options_bytes += efi_utf8_bytes(*s2);
s2++;
- options_size++;
}
+ options_chars = s2 - options;
}

- if (options_size == 0) {
- /* No command line options, so return empty string*/
- options_size = 1;
+ if (!options_chars) {
+ /* No command line options, so return empty string */
options = &zero;
}

- options_size++; /* NUL termination */
+ options_bytes++; /* NUL termination */
+
#ifdef CONFIG_ARM
/* For ARM, allocate at a high address to avoid reserved
* regions at low addresses that we don't know the specfics of
* at the time we are processing the command line.
*/
- status = efi_high_alloc(sys_table_arg, options_size, 0,
+ status = efi_high_alloc(sys_table_arg, options_bytes, 0,
&cmdline_addr, 0xfffff000);
#else
- status = efi_low_alloc(sys_table_arg, options_size, 0,
+ status = efi_low_alloc(sys_table_arg, options_bytes, 0,
&cmdline_addr);
#endif
if (status != EFI_SUCCESS)
return NULL;

s1 = (u8 *)cmdline_addr;
- s2 = (u16 *)options;
-
- for (i = 0; i < options_size - 1; i++)
- *s1++ = *s2++;
+ s2 = (const u16 *)options;

+ s1 = efi_utf16_to_utf8(s1, s2, options_chars);
*s1 = '\0';

- *cmd_line_len = options_size;
+ *cmd_line_len = options_bytes;
return (char *)cmdline_addr;
}
--
1.7.10.4

2013-09-22 22:48:15

by Roy Franz

[permalink] [raw]
Subject: [PATCH 09/18] Move unicode to ASCII conversion to shared function.

Move the open-coded conversion to a shared function for
use by all architectures. Change the allocation to prefer
a high address for ARM, as this is required to avoid conflicts
with reserved regions in low memory. We don't know the specifics
of these regions until after we process the command line and
device tree.

Signed-off-by: Roy Franz <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 43 ++++-------------------
drivers/firmware/efi/efi-stub-helper.c | 58 ++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+), 37 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 2e997b6..5e708c0 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -435,11 +435,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
struct efi_info *efi;
efi_loaded_image_t *image;
void *options;
- u32 load_options_size;
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
int options_size = 0;
efi_status_t status;
- unsigned long cmdline;
+ char *cmdline_ptr;
u16 *s2;
u8 *s1;
int i;
@@ -487,41 +486,11 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
hdr->type_of_loader = 0x21;

/* Convert unicode cmdline to ascii */
- options = image->load_options;
- load_options_size = image->load_options_size / 2; /* ASCII */
- cmdline = 0;
- s2 = (u16 *)options;
-
- if (s2) {
- while (*s2 && *s2 != '\n' && options_size < load_options_size) {
- s2++;
- options_size++;
- }
-
- if (options_size) {
- if (options_size > hdr->cmdline_size)
- options_size = hdr->cmdline_size;
-
- options_size++; /* NUL termination */
-
- 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");
- goto fail;
- }
-
- s1 = (u8 *)(unsigned long)cmdline;
- s2 = (u16 *)options;
-
- for (i = 0; i < options_size - 1; i++)
- *s1++ = *s2++;
-
- *s1 = '\0';
- }
- }
-
- hdr->cmd_line_ptr = cmdline;
+ cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
+ &options_size);
+ if (!cmdline_ptr)
+ goto fail;
+ hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;

hdr->ramdisk_image = 0;
hdr->ramdisk_size = 0;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 9a92129..335d17d 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -548,3 +548,61 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,

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 *efi_convert_cmdline_to_ascii(efi_system_table_t *sys_table_arg,
+ efi_loaded_image_t *image,
+ int *cmd_line_len)
+{
+ u16 *s2;
+ u8 *s1 = NULL;
+ unsigned long cmdline_addr = 0;
+ int load_options_size = image->load_options_size / 2; /* ASCII */
+ void *options = image->load_options;
+ int options_size = 0;
+ efi_status_t 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;
+ }
+
+ options_size++; /* NUL termination */
+#ifdef CONFIG_ARM
+ /* For ARM, allocate at a high address to avoid reserved
+ * regions at low addresses that we don't know the specfics of
+ * at the time we are processing the command line.
+ */
+ status = efi_high_alloc(sys_table_arg, options_size, 0,
+ &cmdline_addr, 0xfffff000);
+#else
+ status = efi_low_alloc(sys_table_arg, options_size, 0,
+ &cmdline_addr);
+#endif
+ if (status != EFI_SUCCESS)
+ return NULL;
+
+ s1 = (u8 *)cmdline_addr;
+ s2 = (u16 *)options;
+
+ for (i = 0; i < options_size - 1; i++)
+ *s1++ = *s2++;
+
+ *s1 = '\0';
+
+ *cmd_line_len = options_size;
+ return (char *)cmdline_addr;
+}
--
1.7.10.4

2013-09-22 22:48:34

by Roy Franz

[permalink] [raw]
Subject: [PATCH 08/18] Generalize relocate_kernel() for use by other architectures.

Rename relocate_kernel() to efi_relocate_kernel(), and take
parameters rather than x86 specific structure. Add max_addr
argument as for ARM we have some address constraints that we
need to enforce when relocating the kernel. Add alloc_size
parameter for use by ARM64 which uses an uncompressed kernel,
and needs to allocate space for BSS.

Signed-off-by: Roy Franz <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 10 ++++-
drivers/firmware/efi/efi-stub-helper.c | 72 ++++++++++++++++++++++----------
2 files changed, 59 insertions(+), 23 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 5bbba86..2e997b6 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -733,10 +733,16 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
* address, relocate it.
*/
if (hdr->pref_address != hdr->code32_start) {
- status = relocate_kernel(hdr);
-
+ unsigned long bzimage_addr = hdr->code32_start;
+ status = efi_relocate_kernel(sys_table, &bzimage_addr,
+ hdr->init_size, hdr->init_size,
+ hdr->pref_address,
+ hdr->kernel_alignment);
if (status != EFI_SUCCESS)
goto fail;
+
+ hdr->pref_address = hdr->code32_start;
+ hdr->code32_start = bzimage_addr;
}

status = exit_boot(boot_params, handle);
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 1d5f219..9a92129 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -483,38 +483,68 @@ fail:

return status;
}
-
-static efi_status_t relocate_kernel(struct setup_header *hdr)
+/* Relocate a kernel image, either compressed or uncompressed.
+ * In the ARM64 case, all kernel images are currently
+ * uncompressed, and as such when we relocate it we need to
+ * allocate additional space for the BSS segment. Any low
+ * memory that this function should avoid needs to be
+ * unavailable in the EFI memory map, as if the preferred
+ * address is not available the lowest available address will
+ * be used.
+ */
+static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
+ unsigned long *image_addr,
+ unsigned long image_size,
+ unsigned long alloc_size,
+ unsigned long preferred_addr,
+ unsigned long alignment)
{
- unsigned long start, nr_pages;
+ unsigned long cur_image_addr;
+ unsigned long new_addr = 0;
efi_status_t status;
+ unsigned long nr_pages;
+ efi_physical_addr_t efi_addr = preferred_addr;
+
+ if (!image_addr || !image_size || !alloc_size)
+ return EFI_INVALID_PARAMETER;
+ if (alloc_size < image_size)
+ return EFI_INVALID_PARAMETER;
+
+ cur_image_addr = *image_addr;

/*
* The EFI firmware loader could have placed the kernel image
- * anywhere in memory, but the kernel has various restrictions
- * on the max physical address it can run at. Attempt to move
- * the kernel to boot_params.pref_address, or as low as
- * possible.
+ * anywhere in memory, but the kernel has restrictions on the
+ * max physical address it can run at. Some architectures
+ * also have a prefered address, so first try to relocate
+ * to the preferred address. If that fails, allocate as low
+ * as possible while respecting the required alignment.
*/
- start = hdr->pref_address;
- nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
-
- status = efi_call_phys4(sys_table->boottime->allocate_pages,
+ nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+ status = efi_call_phys4(sys_table_arg->boottime->allocate_pages,
EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
- nr_pages, &start);
+ nr_pages, &efi_addr);
+ new_addr = efi_addr;
+ /* If preferred address allocation failed allocate as low as
+ * possible. */
+ if (status != EFI_SUCCESS) {
+ status = efi_low_alloc(sys_table_arg, alloc_size, alignment,
+ &new_addr);
+ }
if (status != EFI_SUCCESS) {
- 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");
+ efi_printk(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n");
+ return status;
}

- if (status == EFI_SUCCESS)
- memcpy((void *)start, (void *)(unsigned long)hdr->code32_start,
- hdr->init_size);
+ /* 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 *)cur_image_addr, image_size);
+ /* Zero any extra space we may have allocated for BSS. */
+ memset((void *)(new_addr + image_size), alloc_size - image_size, 0);

- hdr->pref_address = hdr->code32_start;
- hdr->code32_start = (__u32)start;
+ /* Return the new address of the relocated image. */
+ *image_addr = new_addr;

return status;
}
--
1.7.10.4

2013-09-22 22:48:53

by Roy Franz

[permalink] [raw]
Subject: [PATCH 05/18] Rename memory allocation/free functions

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 <[email protected]>
Acked-by: Mark Salter <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 19 ++++++++++---------
drivers/firmware/efi/efi-stub-helper.c | 14 +++++++-------
2 files changed, 17 insertions(+), 16 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 05c539e..2f528fb 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)
{
@@ -165,8 +165,8 @@ fail:
/*
* Allocate at the lowest possible address.
*/
-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

2013-09-22 22:49:12

by Roy Franz

[permalink] [raw]
Subject: [PATCH 04/18] Add system table pointer argument to shared functions.

Add system table pointer argument to shared EFI stub related functions
so they no longer use a global system table pointer as they did when part
of eboot.c. For the ARM EFI stub this allows us to avoid global
variables completely and thereby not have to deal with GOT fixups.
Not having the EFI stub fixup its GOT, which is shared with the
decompressor, simplifies the relocating of the zImage to a
bootable address.


Signed-off-by: Roy Franz <[email protected]>
---
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 8a83387..05c539e 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

2013-09-22 22:49:37

by Roy Franz

[permalink] [raw]
Subject: [PATCH 03/18] Move common EFI stub code from x86 arch code to common location

No code changes made, just moving functions and #define 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 <[email protected]>
Acked-by: Mark Salter <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 442 +-----------------------------
arch/x86/boot/compressed/eboot.h | 1 -
drivers/firmware/efi/efi-stub-helper.c | 463 ++++++++++++++++++++++++++++++++
3 files changed, 464 insertions(+), 442 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 0226510..81b6b65 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -10,7 +10,6 @@
#define SEG_GRANULARITY_4KB (1 << 0)

#define DESC_TYPE_CODE_DATA (1 << 0)
-#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
new file mode 100644
index 0000000..8a83387
--- /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.
+ *
+ */
+#define EFI_READ_CHUNK_SIZE (1024 * 1024)
+
+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;
+}
--
1.7.10.4

2013-09-22 22:49:54

by Roy Franz

[permalink] [raw]
Subject: [PATCH 02/18] Add proper definitions for some EFI function pointers.

The x86/AMD64 EFI stubs must use 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.
Also add some EFI types used by EFI functions.

Signed-off-by: Roy Franz <[email protected]>
Acked-by: Mark Salter <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
arch/x86/boot/compressed/eboot.h | 8 ------
include/linux/efi.h | 50 ++++++++++++++++++++++++++------------
2 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index e5b0a8f..0226510 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -10,8 +10,6 @@
#define SEG_GRANULARITY_4KB (1 << 0)

#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 \
@@ -62,10 +60,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/include/linux/efi.h b/include/linux/efi.h
index 5f8f176..840ab28 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,13 @@ 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;
+ efi_status_t (*allocate_pages)(int, int, unsigned long,
+ efi_physical_addr_t *);
+ efi_status_t (*free_pages)(efi_physical_addr_t, unsigned long);
+ efi_status_t (*get_memory_map)(unsigned long *, void *, unsigned long *,
+ unsigned long *, u32 *);
+ efi_status_t (*allocate_pool)(int, unsigned long, void **);
+ efi_status_t (*free_pool)(void *);
void *create_event;
void *set_timer;
void *wait_for_event;
@@ -171,7 +176,7 @@ typedef struct {
void *install_protocol_interface;
void *reinstall_protocol_interface;
void *uninstall_protocol_interface;
- void *handle_protocol;
+ efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
void *__reserved;
void *register_protocol_notify;
void *locate_handle;
@@ -181,7 +186,7 @@ typedef struct {
void *start_image;
void *exit;
void *unload_image;
- void *exit_boot_services;
+ efi_status_t (*exit_boot_services)(efi_handle_t, unsigned long);
void *get_next_monotonic_count;
void *stall;
void *set_watchdog_timer;
@@ -488,10 +493,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 +505,30 @@ typedef struct {
efi_char16_t filename[1];
} efi_file_info_t;

-typedef struct {
+typedef struct _efi_file_handle {
u64 revision;
- void *open;
- void *close;
+ efi_status_t (*open)(struct _efi_file_handle *,
+ struct _efi_file_handle **,
+ efi_char16_t *, u64, u64);
+ efi_status_t (*close)(struct _efi_file_handle *);
void *delete;
- void *read;
+ efi_status_t (*read)(struct _efi_file_handle *, unsigned long *,
+ void *);
void *write;
void *get_position;
void *set_position;
- void *get_info;
+ efi_status_t (*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
@@ -784,6 +795,13 @@ struct efivar_entry {
struct kobject kobj;
};

+
+struct efi_simple_text_output_protocol {
+ void *reset;
+ efi_status_t (*output_string)(void *, void *);
+ void *test_string;
+};
+
extern struct list_head efivar_sysfs_list;

static inline void
--
1.7.10.4

2013-09-22 22:52:41

by Roy Franz

[permalink] [raw]
Subject: [PATCH 11/18] Rename __get_map() to efi_get_memory_map()

Rename function in preparation for making it more flexible
and sharing it.

Signed-off-by: Roy Franz <[email protected]>
---
drivers/firmware/efi/efi-stub-helper.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8331892..4ac274b 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -46,10 +46,10 @@ 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)
{
efi_memory_desc_t *m = NULL;
efi_status_t status;
@@ -97,7 +97,7 @@ 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);
if (status != EFI_SUCCESS)
goto fail;

@@ -182,7 +182,7 @@ 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);
if (status != EFI_SUCCESS)
goto fail;

--
1.7.10.4

2013-09-22 22:54:15

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 10/18] Do proper conversion from UTF-16 to UTF-8

Sorry this version is broken and doesn't even compile due to remaining options_size references.

Roy Franz <[email protected]> wrote:
>From: "H. Peter Anvin" <[email protected]>
>
>Improve the conversion of the UTF-16 EFI command line
>to UTF-8 for passing to the kernel.
>
>Signed-off-by: Roy Franz <[email protected]>
>---
> arch/x86/boot/compressed/eboot.c | 3 +-
>drivers/firmware/efi/efi-stub-helper.c | 92
>++++++++++++++++++++++++--------
> 2 files changed, 72 insertions(+), 23 deletions(-)
>
>diff --git a/arch/x86/boot/compressed/eboot.c
>b/arch/x86/boot/compressed/eboot.c
>index 5e708c0..4723dc89 100644
>--- a/arch/x86/boot/compressed/eboot.c
>+++ b/arch/x86/boot/compressed/eboot.c
>@@ -486,8 +486,7 @@ struct boot_params *make_boot_params(void *handle,
>efi_system_table_t *_table)
> hdr->type_of_loader = 0x21;
>
> /* Convert unicode cmdline to ascii */
>- cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
>- &options_size);
>+ cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size);
> if (!cmdline_ptr)
> goto fail;
> hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
>diff --git a/drivers/firmware/efi/efi-stub-helper.c
>b/drivers/firmware/efi/efi-stub-helper.c
>index 335d17d..8331892 100644
>--- a/drivers/firmware/efi/efi-stub-helper.c
>+++ b/drivers/firmware/efi/efi-stub-helper.c
>@@ -548,61 +548,111 @@ static efi_status_t
>efi_relocate_kernel(efi_system_table_t *sys_table_arg,
>
> return status;
> }
>-/* Convert the unicode UEFI command line to ASCII to pass to kernel.
>+
>+/*
>+ * Get the number of UTF-8 bytes corresponding to an UTF-16 character.
>+ * This overestimates for surrogates, but that is okay.
>+ */
>+static int efi_utf8_bytes(u16 c)
>+{
>+ return 1 + (c >= 0x80) + (c >= 0x800);
>+}
>+
>+/*
>+ * Convert an UTF-16 string, not necessarily null terminated, to
>UTF-8.
>+ */
>+static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
>+{
>+ unsigned int c;
>+
>+ while (n--) {
>+ c = *src++;
>+ if (n && c >= 0xd800 && c <= 0xdbff &&
>+ *src >= 0xdc00 && *src <= 0xdfff) {
>+ c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff);
>+ src++;
>+ n--;
>+ }
>+ if (c >= 0xd800 && c <= 0xdfff)
>+ c = 0xfffd; /* Unmatched surrogate */
>+ if (c < 0x80) {
>+ *dst++ = c;
>+ continue;
>+ }
>+ if (c < 0x800) {
>+ *dst++ = 0xc0 + (c >> 6);
>+ goto t1;
>+ }
>+ if (c < 0x10000) {
>+ *dst++ = 0xe0 + (c >> 12);
>+ goto t2;
>+ }
>+ *dst++ = 0xf0 + (c >> 18);
>+ *dst++ = 0x80 + ((c >> 12) & 0x3f);
>+t2:
>+ *dst++ = 0x80 + ((c >> 6) & 0x3f);
>+t1:
>+ *dst++ = 0x80 + (c & 0x3f);
>+ }
>+
>+ return dst;
>+}
>+
>+/*
>+ * 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 *efi_convert_cmdline_to_ascii(efi_system_table_t
>*sys_table_arg,
>- efi_loaded_image_t *image,
>- int *cmd_line_len)
>+static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
>+ efi_loaded_image_t *image,
>+ int *cmd_line_len)
> {
>- u16 *s2;
>+ const u16 *s2;
> u8 *s1 = NULL;
> unsigned long cmdline_addr = 0;
> int load_options_size = image->load_options_size / 2; /* ASCII */
>- void *options = image->load_options;
>- int options_size = 0;
>+ const u16 *options = image->load_options;
>+ int options_bytes = 0; /* UTF-8 bytes */
>+ int options_chars = 0; /* UTF-16 chars */
> efi_status_t status;
>- int i;
> u16 zero = 0;
>
> if (options) {
> s2 = options;
>- while (*s2 && *s2 != '\n' && options_size < load_options_size) {
>+ while (*s2 && *s2 != '\n' && options_bytes < load_options_size) {
>+ options_bytes += efi_utf8_bytes(*s2);
> s2++;
>- options_size++;
> }
>+ options_chars = s2 - options;
> }
>
>- if (options_size == 0) {
>- /* No command line options, so return empty string*/
>- options_size = 1;
>+ if (!options_chars) {
>+ /* No command line options, so return empty string */
> options = &zero;
> }
>
>- options_size++; /* NUL termination */
>+ options_bytes++; /* NUL termination */
>+
> #ifdef CONFIG_ARM
> /* For ARM, allocate at a high address to avoid reserved
> * regions at low addresses that we don't know the specfics of
> * at the time we are processing the command line.
> */
>- status = efi_high_alloc(sys_table_arg, options_size, 0,
>+ status = efi_high_alloc(sys_table_arg, options_bytes, 0,
> &cmdline_addr, 0xfffff000);
> #else
>- status = efi_low_alloc(sys_table_arg, options_size, 0,
>+ status = efi_low_alloc(sys_table_arg, options_bytes, 0,
> &cmdline_addr);
> #endif
> if (status != EFI_SUCCESS)
> return NULL;
>
> s1 = (u8 *)cmdline_addr;
>- s2 = (u16 *)options;
>-
>- for (i = 0; i < options_size - 1; i++)
>- *s1++ = *s2++;
>+ s2 = (const u16 *)options;
>
>+ s1 = efi_utf16_to_utf8(s1, s2, options_chars);
> *s1 = '\0';
>
>- *cmd_line_len = options_size;
>+ *cmd_line_len = options_bytes;
> return (char *)cmdline_addr;
> }

--
Sent from my mobile phone. Please pardon brevity and lack of formatting.

2013-09-22 23:07:55

by Roy Franz

[permalink] [raw]
Subject: Re: [PATCH 10/18] Do proper conversion from UTF-16 to UTF-8

On Sun, Sep 22, 2013 at 3:54 PM, H. Peter Anvin <[email protected]> wrote:
> Sorry this version is broken and doesn't even compile due to remaining options_size references.

I compiled and tested this series on both x86_64 (using OVMF) and on
the ARM simulator. I just doubled checked
my kernel .config to verify this was not being omitted and I'm pretty
sure this doesn't have any compilation problems.
I did make a few changes to get the untested version you sent out to
compile, but they all seemed to be straightforward typo type fixes.
I'll gladly address any defects in this patch, but I don't see an
compilation problems.

Thanks,
Roy


>
> Roy Franz <[email protected]> wrote:
>>From: "H. Peter Anvin" <[email protected]>
>>
>>Improve the conversion of the UTF-16 EFI command line
>>to UTF-8 for passing to the kernel.
>>
>>Signed-off-by: Roy Franz <[email protected]>
>>---
>> arch/x86/boot/compressed/eboot.c | 3 +-
>>drivers/firmware/efi/efi-stub-helper.c | 92
>>++++++++++++++++++++++++--------
>> 2 files changed, 72 insertions(+), 23 deletions(-)
>>
>>diff --git a/arch/x86/boot/compressed/eboot.c
>>b/arch/x86/boot/compressed/eboot.c
>>index 5e708c0..4723dc89 100644
>>--- a/arch/x86/boot/compressed/eboot.c
>>+++ b/arch/x86/boot/compressed/eboot.c
>>@@ -486,8 +486,7 @@ struct boot_params *make_boot_params(void *handle,
>>efi_system_table_t *_table)
>> hdr->type_of_loader = 0x21;
>>
>> /* Convert unicode cmdline to ascii */
>>- cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
>>- &options_size);
>>+ cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size);
>> if (!cmdline_ptr)
>> goto fail;
>> hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
>>diff --git a/drivers/firmware/efi/efi-stub-helper.c
>>b/drivers/firmware/efi/efi-stub-helper.c
>>index 335d17d..8331892 100644
>>--- a/drivers/firmware/efi/efi-stub-helper.c
>>+++ b/drivers/firmware/efi/efi-stub-helper.c
>>@@ -548,61 +548,111 @@ static efi_status_t
>>efi_relocate_kernel(efi_system_table_t *sys_table_arg,
>>
>> return status;
>> }
>>-/* Convert the unicode UEFI command line to ASCII to pass to kernel.
>>+
>>+/*
>>+ * Get the number of UTF-8 bytes corresponding to an UTF-16 character.
>>+ * This overestimates for surrogates, but that is okay.
>>+ */
>>+static int efi_utf8_bytes(u16 c)
>>+{
>>+ return 1 + (c >= 0x80) + (c >= 0x800);
>>+}
>>+
>>+/*
>>+ * Convert an UTF-16 string, not necessarily null terminated, to
>>UTF-8.
>>+ */
>>+static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
>>+{
>>+ unsigned int c;
>>+
>>+ while (n--) {
>>+ c = *src++;
>>+ if (n && c >= 0xd800 && c <= 0xdbff &&
>>+ *src >= 0xdc00 && *src <= 0xdfff) {
>>+ c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff);
>>+ src++;
>>+ n--;
>>+ }
>>+ if (c >= 0xd800 && c <= 0xdfff)
>>+ c = 0xfffd; /* Unmatched surrogate */
>>+ if (c < 0x80) {
>>+ *dst++ = c;
>>+ continue;
>>+ }
>>+ if (c < 0x800) {
>>+ *dst++ = 0xc0 + (c >> 6);
>>+ goto t1;
>>+ }
>>+ if (c < 0x10000) {
>>+ *dst++ = 0xe0 + (c >> 12);
>>+ goto t2;
>>+ }
>>+ *dst++ = 0xf0 + (c >> 18);
>>+ *dst++ = 0x80 + ((c >> 12) & 0x3f);
>>+t2:
>>+ *dst++ = 0x80 + ((c >> 6) & 0x3f);
>>+t1:
>>+ *dst++ = 0x80 + (c & 0x3f);
>>+ }
>>+
>>+ return dst;
>>+}
>>+
>>+/*
>>+ * 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 *efi_convert_cmdline_to_ascii(efi_system_table_t
>>*sys_table_arg,
>>- efi_loaded_image_t *image,
>>- int *cmd_line_len)
>>+static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
>>+ efi_loaded_image_t *image,
>>+ int *cmd_line_len)
>> {
>>- u16 *s2;
>>+ const u16 *s2;
>> u8 *s1 = NULL;
>> unsigned long cmdline_addr = 0;
>> int load_options_size = image->load_options_size / 2; /* ASCII */
>>- void *options = image->load_options;
>>- int options_size = 0;
>>+ const u16 *options = image->load_options;
>>+ int options_bytes = 0; /* UTF-8 bytes */
>>+ int options_chars = 0; /* UTF-16 chars */
>> efi_status_t status;
>>- int i;
>> u16 zero = 0;
>>
>> if (options) {
>> s2 = options;
>>- while (*s2 && *s2 != '\n' && options_size < load_options_size) {
>>+ while (*s2 && *s2 != '\n' && options_bytes < load_options_size) {
>>+ options_bytes += efi_utf8_bytes(*s2);
>> s2++;
>>- options_size++;
>> }
>>+ options_chars = s2 - options;
>> }
>>
>>- if (options_size == 0) {
>>- /* No command line options, so return empty string*/
>>- options_size = 1;
>>+ if (!options_chars) {
>>+ /* No command line options, so return empty string */
>> options = &zero;
>> }
>>
>>- options_size++; /* NUL termination */
>>+ options_bytes++; /* NUL termination */
>>+
>> #ifdef CONFIG_ARM
>> /* For ARM, allocate at a high address to avoid reserved
>> * regions at low addresses that we don't know the specfics of
>> * at the time we are processing the command line.
>> */
>>- status = efi_high_alloc(sys_table_arg, options_size, 0,
>>+ status = efi_high_alloc(sys_table_arg, options_bytes, 0,
>> &cmdline_addr, 0xfffff000);
>> #else
>>- status = efi_low_alloc(sys_table_arg, options_size, 0,
>>+ status = efi_low_alloc(sys_table_arg, options_bytes, 0,
>> &cmdline_addr);
>> #endif
>> if (status != EFI_SUCCESS)
>> return NULL;
>>
>> s1 = (u8 *)cmdline_addr;
>>- s2 = (u16 *)options;
>>-
>>- for (i = 0; i < options_size - 1; i++)
>>- *s1++ = *s2++;
>>+ s2 = (const u16 *)options;
>>
>>+ s1 = efi_utf16_to_utf8(s1, s2, options_chars);
>> *s1 = '\0';
>>
>>- *cmd_line_len = options_size;
>>+ *cmd_line_len = options_bytes;
>> return (char *)cmdline_addr;
>> }
>
> --
> Sent from my mobile phone. Please pardon brevity and lack of formatting.

2013-09-23 00:25:22

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 10/18] Do proper conversion from UTF-16 to UTF-8

On 09/22/2013 04:07 PM, Roy Franz wrote:
> On Sun, Sep 22, 2013 at 3:54 PM, H. Peter Anvin <[email protected]> wrote:
>> Sorry this version is broken and doesn't even compile due to remaining options_size references.
>
> I compiled and tested this series on both x86_64 (using OVMF) and on
> the ARM simulator. I just doubled checked
> my kernel .config to verify this was not being omitted and I'm pretty
> sure this doesn't have any compilation problems.
> I did make a few changes to get the untested version you sent out to
> compile, but they all seemed to be straightforward typo type fixes.
> I'll gladly address any defects in this patch, but I don't see an
> compilation problems.
>

Ah yes, I see now... you fixed up the compile problem but did so
incorrectly.

int load_options_size = image->load_options_size / 2; /* ASCII */

This is a number of UTF-16 chars, the comment is completely wrong;

- while (*s2 && *s2 != '\n' && options_size <
load_options_size) {
+ while (*s2 && *s2 != '\n' && options_bytes <
load_options_size) {
+ options_bytes += efi_utf8_bytes(*s2);
s2++;
- options_size++;
}
+ options_chars = s2 - options;

You can't compare options_bytes against load_options_size; the latter
being a UTF-16 shortword count.

So the loop really needs to update options_chars in the loop to compare
it against load_options_size:

while (*s2 && *s2 != '\n' && options_chars < load_options_size) {
options_bytes += efi_utf8_bytes(*s2++);
option_chars++;
}

-hpa

2013-09-25 12:01:24

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 06/18] Enforce minimum alignment of 1 page on allocations.

On Sun, 22 Sep, at 03:45:30PM, Roy Franz wrote:
> 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.
> 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 <[email protected]>
> Acked-by: Mark Salter <[email protected]>
> Reviewed-by: Grant Likely <[email protected]>
> ---
> drivers/firmware/efi/efi-stub-helper.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)

[...]

> + /* 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;
> +

FYI I manually fixed up these comments to use the multi-line form, e.g.

/*
* 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.
*/

so that they match the rest of the comments in the file.

--
Matt Fleming, Intel Open Source Technology Center

2013-09-25 12:01:29

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 08/18] Generalize relocate_kernel() for use by other architectures.

On Sun, 22 Sep, at 03:45:32PM, Roy Franz wrote:
> Rename relocate_kernel() to efi_relocate_kernel(), and take
> parameters rather than x86 specific structure. Add max_addr
> argument as for ARM we have some address constraints that we
> need to enforce when relocating the kernel. Add alloc_size
> parameter for use by ARM64 which uses an uncompressed kernel,
> and needs to allocate space for BSS.
>
> Signed-off-by: Roy Franz <[email protected]>
> ---
> arch/x86/boot/compressed/eboot.c | 10 ++++-
> drivers/firmware/efi/efi-stub-helper.c | 72 ++++++++++++++++++++++----------
> 2 files changed, 59 insertions(+), 23 deletions(-)

[...]

I fixed up these multi-line comments too.

--
Matt Fleming, Intel Open Source Technology Center

2013-09-25 12:01:34

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 09/18] Move unicode to ASCII conversion to shared function.

On Sun, 22 Sep, at 03:45:33PM, Roy Franz wrote:
> Move the open-coded conversion to a shared function for
> use by all architectures. Change the allocation to prefer
> a high address for ARM, as this is required to avoid conflicts
> with reserved regions in low memory. We don't know the specifics
> of these regions until after we process the command line and
> device tree.
>
> Signed-off-by: Roy Franz <[email protected]>
> ---
> arch/x86/boot/compressed/eboot.c | 43 ++++-------------------
> drivers/firmware/efi/efi-stub-helper.c | 58 ++++++++++++++++++++++++++++++++
> 2 files changed, 64 insertions(+), 37 deletions(-)

[...]

> +/* 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.
> + */

I futzed with the comments in this patch too.

--
Matt Fleming, Intel Open Source Technology Center

2013-09-25 12:01:54

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 10/18] Do proper conversion from UTF-16 to UTF-8

On Sun, 22 Sep, at 05:24:28PM, H. Peter Anvin wrote:
> On 09/22/2013 04:07 PM, Roy Franz wrote:
> > On Sun, Sep 22, 2013 at 3:54 PM, H. Peter Anvin <[email protected]> wrote:
> >> Sorry this version is broken and doesn't even compile due to remaining options_size references.
> >
> > I compiled and tested this series on both x86_64 (using OVMF) and on
> > the ARM simulator. I just doubled checked
> > my kernel .config to verify this was not being omitted and I'm pretty
> > sure this doesn't have any compilation problems.
> > I did make a few changes to get the untested version you sent out to
> > compile, but they all seemed to be straightforward typo type fixes.
> > I'll gladly address any defects in this patch, but I don't see an
> > compilation problems.
> >
>
> Ah yes, I see now... you fixed up the compile problem but did so
> incorrectly.

Folks, I'm gonna drop this patch for now. Feel free to resend it once
everyone's happy with it. There's plenty of time to get this patch
applied, it just doesn't make sense to hold up the rest of the series.

--
Matt Fleming, Intel Open Source Technology Center

2013-09-25 12:11:25

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH V5 00/18] ARM EFI stub common code

On Sun, 22 Sep, at 03:45:24PM, Roy Franz wrote:
> This patch is the common/x86 portion of the ARM EFI stub
> patchset broken out. These changes support the addition
> of EFI stub support for the ARM and ARM64 architectures.
> The common code that is now shared in efi-stub-helper.c
> is based on code in the x86 stub that has been generalized
> to support other architectures.

OK, I've applied this patch series (excluding PATCH 10) to the
'arm/common' branch in the EFI repository at,

git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git

At the request of Grant, the patches series from both Roy and Leif are
available in a clean branch with a linear history at 'efi-arm', which is
based on v3.11. Grant, feel free to pull that branch into wherever it is
required. If this layout doesn't work for you guys then just shout and
we can come up with a scheme that does.

I'm going to send a pull request for the 'next' branch to the tip folks,
so that these patches can get some testing via linux-next.

--
Matt Fleming, Intel Open Source Technology Center

2013-09-25 12:21:05

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH V5 00/18] ARM EFI stub common code

On Wed, 25 Sep, at 01:11:20PM, Matt Fleming wrote:
> At the request of Grant, the patches series from both Roy and Leif are
> available in a clean branch with a linear history at 'efi-arm', which is
> based on v3.11. Grant, feel free to pull that branch into wherever it is
> required.

... but please make sure that branch doesn't get sent to Linus.

--
Matt Fleming, Intel Open Source Technology Center

2013-09-25 14:15:26

by Roy Franz

[permalink] [raw]
Subject: Re: [PATCH 10/18] Do proper conversion from UTF-16 to UTF-8

On Wed, Sep 25, 2013 at 5:01 AM, Matt Fleming <[email protected]> wrote:
> On Sun, 22 Sep, at 05:24:28PM, H. Peter Anvin wrote:
>> On 09/22/2013 04:07 PM, Roy Franz wrote:
>> > On Sun, Sep 22, 2013 at 3:54 PM, H. Peter Anvin <[email protected]> wrote:
>> >> Sorry this version is broken and doesn't even compile due to remaining options_size references.
>> >
>> > I compiled and tested this series on both x86_64 (using OVMF) and on
>> > the ARM simulator. I just doubled checked
>> > my kernel .config to verify this was not being omitted and I'm pretty
>> > sure this doesn't have any compilation problems.
>> > I did make a few changes to get the untested version you sent out to
>> > compile, but they all seemed to be straightforward typo type fixes.
>> > I'll gladly address any defects in this patch, but I don't see an
>> > compilation problems.
>> >
>>
>> Ah yes, I see now... you fixed up the compile problem but did so
>> incorrectly.
>
> Folks, I'm gonna drop this patch for now. Feel free to resend it once
> everyone's happy with it. There's plenty of time to get this patch
> applied, it just doesn't make sense to hold up the rest of the series.

I'll get an updated (and independent) version out in the next couple days that
addresses HPA's feedback.

Thanks,
Roy
>
> --
> Matt Fleming, Intel Open Source Technology Center