summaryrefslogtreecommitdiffstats
path: root/arch/efi/efi
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-07-07 18:02:53 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-07-16 10:46:18 +0200
commit1dff7e414d78d5fd8ff62a57b67617aa1f73e532 (patch)
tree48c3360c0536ce6096a50b774ed8c7d121b46dc2 /arch/efi/efi
parent3475ba2a1da1358697297a3cf9337eed10b69695 (diff)
downloadbarebox-1dff7e414d78d5fd8ff62a57b67617aa1f73e532.tar.gz
barebox-1dff7e414d78d5fd8ff62a57b67617aa1f73e532.tar.xz
Add initial EFI architecture support
This adds support for running barebox in an EFI environment on X86 PC hardware. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/efi/efi')
-rw-r--r--arch/efi/efi/Makefile2
-rw-r--r--arch/efi/efi/clocksource.c60
-rw-r--r--arch/efi/efi/efi-block-io.c174
-rw-r--r--arch/efi/efi/efi-device.c348
-rw-r--r--arch/efi/efi/efi-image.c105
-rw-r--r--arch/efi/efi/efi.c342
-rw-r--r--arch/efi/efi/env-efi/network/eth0-discover5
7 files changed, 1036 insertions, 0 deletions
diff --git a/arch/efi/efi/Makefile b/arch/efi/efi/Makefile
new file mode 100644
index 0000000000..a856e5907c
--- /dev/null
+++ b/arch/efi/efi/Makefile
@@ -0,0 +1,2 @@
+obj-y += efi.o clocksource.o efi-block-io.o efi-device.o efi-image.o
+bbenv-y += env-efi
diff --git a/arch/efi/efi/clocksource.c b/arch/efi/efi/clocksource.c
new file mode 100644
index 0000000000..2f33b43cce
--- /dev/null
+++ b/arch/efi/efi/clocksource.c
@@ -0,0 +1,60 @@
+#include <common.h>
+#include <efi.h>
+#include <mach/efi.h>
+#include <clock.h>
+
+#ifdef __x86_64__
+uint64_t ticks_read(void)
+{
+ uint64_t a, d;
+
+ __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
+
+ return (d << 32) | a;
+}
+#else
+uint64_t ticks_read(void)
+{
+ uint64_t val;
+
+ __asm__ volatile ("rdtsc" : "=A" (val));
+
+ return val;
+}
+#endif
+
+static uint64_t freq;
+
+/* count TSC ticks during a millisecond delay */
+static uint64_t ticks_freq(void)
+{
+ uint64_t ticks_start, ticks_end;
+
+ ticks_start = ticks_read();
+ BS->stall(1000);
+ ticks_end = ticks_read();
+
+ return (ticks_end - ticks_start) * 1000;
+}
+
+static uint64_t efi_clocksource_read(void)
+{
+ return 1000 * 1000 * ticks_read() / freq;
+}
+
+static struct clocksource cs = {
+ .read = efi_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .shift = 0,
+};
+
+int efi_clocksource_init(void)
+{
+ cs.mult = clocksource_hz2mult(1000 * 1000, cs.shift);
+
+ freq = ticks_freq();
+
+ init_clock(&cs);
+
+ return 0;
+}
diff --git a/arch/efi/efi/efi-block-io.c b/arch/efi/efi/efi-block-io.c
new file mode 100644
index 0000000000..00115317fc
--- /dev/null
+++ b/arch/efi/efi/efi-block-io.c
@@ -0,0 +1,174 @@
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <malloc.h>
+#include <fs.h>
+#include <string.h>
+#include <command.h>
+#include <errno.h>
+#include <linux/stat.h>
+#include <xfuncs.h>
+#include <fcntl.h>
+#include <efi.h>
+#include <block.h>
+#include <disks.h>
+#include <mach/efi.h>
+#include <mach/efi-device.h>
+
+#define EFI_BLOCK_IO_PROTOCOL_REVISION2 0x00020001
+#define EFI_BLOCK_IO_PROTOCOL_REVISION3 ((2<<16) | (31))
+
+struct efi_block_io_media{
+ u32 media_id;
+ bool removable_media;
+ bool media_present;
+ bool logical_partition;
+ bool read_only;
+ bool write_caching;
+ u32 block_size;
+ u32 io_align;
+ u64 last_block;
+ u64 lowest_aligned_lba; /* added in Revision 2 */
+ u32 logical_blocks_per_physical_block; /* added in Revision 2 */
+ u32 optimal_transfer_length_granularity; /* added in Revision 3 */
+};
+
+struct efi_block_io_protocol {
+ u64 revision;
+ struct efi_block_io_media *media;
+ efi_status_t(EFIAPI *reset)(struct efi_block_io_protocol *this,
+ bool ExtendedVerification);
+ efi_status_t(EFIAPI *read)(struct efi_block_io_protocol *this, u32 media_id,
+ u64 lba, unsigned long buffer_size, void *buf);
+ efi_status_t(EFIAPI *write)(struct efi_block_io_protocol *this, u32 media_id,
+ u64 lba, unsigned long buffer_size, void *buf);
+ efi_status_t(EFIAPI *flush)(struct efi_block_io_protocol *this);
+};
+
+struct efi_bio_priv {
+ struct efi_block_io_protocol *protocol;
+ struct device_d *dev;
+ struct block_device blk;
+ u32 media_id;
+};
+
+static int efi_bio_read(struct block_device *blk, void *buffer, int block,
+ int num_blocks)
+{
+ struct efi_bio_priv *priv = container_of(blk, struct efi_bio_priv, blk);
+ efi_status_t efiret;
+
+ efiret = priv->protocol->read(priv->protocol, priv->media_id,
+ block, num_blocks * 512, buffer);
+
+ if (EFI_ERROR(efiret))
+ return -efi_errno(efiret);
+
+ return 0;
+}
+
+static int efi_bio_write(struct block_device *blk,
+ const void *buffer, int block, int num_blocks)
+{
+ struct efi_bio_priv *priv = container_of(blk, struct efi_bio_priv, blk);
+ efi_status_t efiret;
+
+ efiret = priv->protocol->write(priv->protocol, priv->media_id,
+ block, num_blocks * 512, (void *)buffer);
+ if (EFI_ERROR(efiret))
+ return -efi_errno(efiret);
+
+ return 0;
+}
+
+static int efi_bio_flush(struct block_device *blk)
+{
+ struct efi_bio_priv *priv = container_of(blk, struct efi_bio_priv, blk);
+ efi_status_t efiret;
+
+ efiret = priv->protocol->flush(priv->protocol);
+ if (EFI_ERROR(efiret))
+ return -efi_errno(efiret);
+
+ return 0;
+}
+
+static struct block_device_ops efi_bio_ops = {
+ .read = efi_bio_read,
+ .write = efi_bio_write,
+ .flush = efi_bio_flush,
+};
+
+static void efi_bio_print_info(struct efi_bio_priv *priv)
+{
+ struct efi_block_io_media *media = priv->protocol->media;
+ u64 revision = priv->protocol->revision;
+
+ dev_dbg(priv->dev, "revision: 0x%016llx\n", revision);
+ dev_dbg(priv->dev, "media_id: 0x%08x\n", media->media_id);
+ dev_dbg(priv->dev, "removable_media: %d\n", media->removable_media);
+ dev_dbg(priv->dev, "media_present: %d\n", media->media_present);
+ dev_dbg(priv->dev, "logical_partition: %d\n", media->logical_partition);
+ dev_dbg(priv->dev, "read_only: %d\n", media->read_only);
+ dev_dbg(priv->dev, "write_caching: %d\n", media->write_caching);
+ dev_dbg(priv->dev, "block_size: 0x%08x\n", media->block_size);
+ dev_dbg(priv->dev, "io_align: 0x%08x\n", media->io_align);
+ dev_dbg(priv->dev, "last_block: 0x%016llx\n", media->last_block);
+
+ if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION2)
+ return;
+
+ dev_dbg(priv->dev, "u64 lowest_aligned_lba: 0x%08llx\n",
+ media->lowest_aligned_lba);
+ dev_dbg(priv->dev, "logical_blocks_per_physical_block: 0x%08x\n",
+ media->logical_blocks_per_physical_block);
+
+ if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION3)
+ return;
+
+ dev_dbg(priv->dev, "optimal_transfer_length_granularity: 0x%08x\n",
+ media->optimal_transfer_length_granularity);
+}
+
+int efi_bio_probe(struct efi_device *efidev)
+{
+ int ret;
+ struct efi_bio_priv *priv;
+ struct efi_block_io_media *media;
+
+ priv = xzalloc(sizeof(*priv));
+
+ BS->handle_protocol(efidev->handle, &efi_block_io_protocol_guid,
+ (void **)&priv->protocol);
+ if (!priv->protocol)
+ return -ENODEV;
+
+ media = priv->protocol->media;
+ efi_bio_print_info(priv);
+ priv->dev = &efidev->dev;
+
+ priv->blk.cdev.name = asprintf("disk%d", cdev_find_free_index("disk"));
+ priv->blk.blockbits = ffs(media->block_size) - 1;
+ priv->blk.num_blocks = media->last_block;
+ priv->blk.ops = &efi_bio_ops;
+ priv->blk.dev = &efidev->dev;
+
+ priv->media_id = media->media_id;
+
+ ret = blockdevice_register(&priv->blk);
+ if (ret)
+ return ret;
+
+ parse_partition_table(&priv->blk);
+
+ return 0;
+}
+
+static struct efi_driver efi_fs_driver = {
+ .driver = {
+ .name = "efi-block-io",
+ },
+ .probe = efi_bio_probe,
+ .guid = EFI_BLOCK_IO_PROTOCOL_GUID,
+};
+device_efi_driver(efi_fs_driver);
diff --git a/arch/efi/efi/efi-device.c b/arch/efi/efi/efi-device.c
new file mode 100644
index 0000000000..71526b999f
--- /dev/null
+++ b/arch/efi/efi/efi-device.c
@@ -0,0 +1,348 @@
+/*
+ * efi-device.c - barebox EFI payload support
+ *
+ * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <command.h>
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <memory.h>
+#include <string.h>
+#include <sizes.h>
+#include <wchar.h>
+#include <init.h>
+#include <efi.h>
+#include <mach/efi.h>
+#include <mach/efi-device.h>
+#include <linux/err.h>
+
+int efi_locate_handle(enum efi_locate_search_type search_type,
+ efi_guid_t *protocol,
+ void *search_key,
+ unsigned long *no_handles,
+ efi_handle_t **buffer)
+{
+ efi_status_t efiret;
+ unsigned long buffer_size = 0;
+ efi_handle_t *buf;
+
+ efiret = BS->locate_handle(search_type, protocol, search_key, &buffer_size,
+ NULL);
+ if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL)
+ return -efi_errno(efiret);
+
+ buf = malloc(buffer_size);
+ if (!buf)
+ return -ENOMEM;
+
+ efiret = BS->locate_handle(search_type, protocol, search_key, &buffer_size,
+ buf);
+ if (EFI_ERROR(efiret)) {
+ free(buf);
+ return -efi_errno(efiret);
+ }
+
+ *no_handles = buffer_size / sizeof(efi_handle_t);
+ *buffer = buf;
+
+ return 0;
+}
+
+static struct efi_device *efi_find_device(efi_handle_t *handle)
+{
+ struct device_d *dev;
+ struct efi_device *efidev;
+
+ bus_for_each_device(&efi_bus, dev) {
+ efidev = container_of(dev, struct efi_device, dev);
+
+ if (efidev->handle == handle)
+ return efidev;
+ }
+
+ return NULL;
+}
+
+static void efi_devinfo(struct device_d *dev)
+{
+ struct efi_device *efidev = to_efi_device(dev);
+ int i;
+
+ printf("Protocols:\n");
+
+ for (i = 0; i < efidev->num_guids; i++)
+ printf(" %d: %pUl\n", i, &efidev->guids[i]);
+}
+
+static efi_handle_t *efi_find_parent(efi_handle_t *handle)
+{
+ unsigned long handle_count = 0;
+ efi_handle_t *handles = NULL, *parent;
+ unsigned long num_guids;
+ efi_guid_t **guids;
+ int ret, i, j, k;
+ efi_status_t efiret;
+ struct efi_open_protocol_information_entry *entry_buffer;
+ unsigned long entry_count;
+
+ ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles);
+ if (ret)
+ return NULL;
+
+ /*
+ * Normally one would expect a function/pointer to retrieve the parent.
+ * With EFI we have to:
+ * - get all handles
+ * - for each handle get the registered protocols
+ * - for each protocol get the users
+ * - the user which matches the input handle is the parent
+ */
+ for (i = 0; i < handle_count; i++) {
+ efiret = BS->open_protocol(handles[i], &efi_device_path_protocol_guid,
+ NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+ if (EFI_ERROR(efiret))
+ continue;
+
+ BS->protocols_per_handle(handles[i], &guids, &num_guids);
+ for (j = 0; j < num_guids; j++) {
+ efiret = BS->open_protocol_information(handles[i], guids[j],
+ &entry_buffer, &entry_count);
+ for (k = 0; k < entry_count; k++) {
+ if (entry_buffer[k].controller_handle == NULL)
+ continue;
+ if (entry_buffer[k].controller_handle == handles[i])
+ continue;
+ if (entry_buffer[k].controller_handle == handle) {
+ parent = handles[i];
+ goto out;
+ }
+ }
+ }
+ }
+
+ parent = NULL;
+
+ free(handles);
+out:
+ return parent;
+}
+
+static struct efi_device *efi_add_device(efi_handle_t *handle, efi_guid_t **guids,
+ int num_guids)
+{
+ struct efi_device *efidev;
+ int i;
+ efi_guid_t *guidarr;
+ efi_status_t efiret;
+ void *devpath;
+
+ efidev = efi_find_device(handle);
+ if (efidev)
+ return ERR_PTR(-EEXIST);
+
+ efiret = BS->open_protocol(handle, &efi_device_path_protocol_guid,
+ NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+ if (EFI_ERROR(efiret))
+ return ERR_PTR(-EINVAL);
+
+ guidarr = malloc(sizeof(efi_guid_t) * num_guids);
+
+ for (i = 0; i < num_guids; i++)
+ memcpy(&guidarr[i], guids[i], sizeof(efi_guid_t));
+
+ efiret = BS->open_protocol(handle, &efi_device_path_protocol_guid,
+ &devpath, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(efiret))
+ return ERR_PTR(-EINVAL);
+
+ efidev = xzalloc(sizeof(*efidev));
+
+ efidev->guids = guidarr;
+ efidev->num_guids = num_guids;
+ efidev->handle = handle;
+ efidev->dev.bus = &efi_bus;
+ efidev->dev.id = DEVICE_ID_SINGLE;
+ efidev->dev.info = efi_devinfo;
+ efidev->devpath = devpath;
+
+ BS->handle_protocol(handle, &guidarr[0], &efidev->protocol);
+
+ sprintf(efidev->dev.name, "handle-%p", handle);
+
+ efidev->parent_handle = efi_find_parent(efidev->handle);
+
+ return efidev;
+}
+
+
+static int efi_register_device(struct efi_device *efidev)
+{
+ char *dev_path_str;
+ struct efi_device *parent;
+ int ret;
+
+ if (efi_find_device(efidev->handle))
+ return -EEXIST;
+
+ if (efidev->parent_handle) {
+ parent = efi_find_device(efidev->parent_handle);
+ if (!parent)
+ return -EINVAL;
+
+ efidev->dev.parent = &parent->dev;
+ }
+
+ ret = register_device(&efidev->dev);
+ if (ret)
+ return ret;
+
+ dev_path_str = device_path_to_str(efidev->devpath);
+ if (dev_path_str) {
+ dev_add_param_fixed(&efidev->dev, "devpath", dev_path_str);
+ free(dev_path_str);
+ }
+
+ debug("registered efi device %s\n", dev_name(&efidev->dev));
+
+ return 0;
+}
+
+/**
+ * efi_register_devices - iterate over all EFI handles and register
+ * the devices found
+ *
+ * in barebox we treat all EFI handles which support the device_path
+ * protocol as devices. This function iterates over all handles and
+ * registers the corresponding devices. efi_register_devices is safe
+ * to call multiple times. Already registered devices will be ignored.
+ *
+ */
+void efi_register_devices(void)
+{
+ unsigned long handle_count = 0;
+ efi_handle_t *handles = NULL;
+ unsigned long num_guids;
+ efi_guid_t **guids;
+ int ret, i;
+ struct efi_device **efidevs;
+ int registered;
+
+ ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles);
+ if (ret)
+ return;
+
+ efidevs = xzalloc(handle_count * sizeof(struct efi_device *));
+
+ for (i = 0; i < handle_count; i++) {
+ BS->protocols_per_handle(handles[i], &guids, &num_guids);
+
+ efidevs[i] = efi_add_device(handles[i], guids, num_guids);
+ }
+
+ /*
+ * We have a list of devices we want to register, but can only
+ * register a device when all parents are registered already.
+ * Do this by continiously iterating over the list until no
+ * further devices are registered.
+ */
+ do {
+ registered = 0;
+
+ for (i = 0; i < handle_count; i++) {
+ if (IS_ERR(efidevs[i]))
+ continue;
+
+ ret = efi_register_device(efidevs[i]);
+ if (!ret) {
+ efidevs[i] = ERR_PTR(-EEXIST);
+ registered = 1;
+ }
+ }
+ } while (registered);
+
+ free(efidevs);
+ free(handles);
+}
+
+int efi_connect_all(void)
+{
+ efi_status_t efiret;
+ unsigned long handle_count;
+ efi_handle_t *handle_buffer;
+ int i;
+
+ efiret = BS->locate_handle_buffer(all_handles, NULL, NULL, &handle_count,
+ &handle_buffer);
+ if (EFI_ERROR(efiret))
+ return -efi_errno(efiret);
+
+ for (i = 0; i < handle_count; i++)
+ efiret = BS->connect_controller(handle_buffer[i], NULL, NULL, true);
+
+ if (handle_buffer)
+ BS->free_pool(handle_buffer);
+
+ return 0;
+}
+
+static int efi_bus_match(struct device_d *dev, struct driver_d *drv)
+{
+ struct efi_driver *efidrv = to_efi_driver(drv);
+ struct efi_device *efidev = to_efi_device(dev);
+ int i;
+
+ for (i = 0; i < efidev->num_guids; i++) {
+ if (!memcmp(&efidrv->guid, &efidev->guids[i], sizeof(efi_guid_t)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int efi_bus_probe(struct device_d *dev)
+{
+ struct efi_driver *efidrv = to_efi_driver(dev->driver);
+ struct efi_device *efidev = to_efi_device(dev);
+
+ return efidrv->probe(efidev);
+}
+
+static void efi_bus_remove(struct device_d *dev)
+{
+ struct efi_driver *efidrv = to_efi_driver(dev->driver);
+ struct efi_device *efidev = to_efi_device(dev);
+
+ return efidrv->remove(efidev);
+}
+
+struct bus_type efi_bus = {
+ .name = "efi",
+ .match = efi_bus_match,
+ .probe = efi_bus_probe,
+ .remove = efi_bus_remove,
+};
+
+static int efi_init_devices(void)
+{
+ bus_register(&efi_bus);
+
+ efi_register_devices();
+
+ return 0;
+}
+core_initcall(efi_init_devices);
diff --git a/arch/efi/efi/efi-image.c b/arch/efi/efi/efi-image.c
new file mode 100644
index 0000000000..18757d2697
--- /dev/null
+++ b/arch/efi/efi/efi-image.c
@@ -0,0 +1,105 @@
+/*
+ * efi-image.c - barebox EFI payload support
+ *
+ * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <sizes.h>
+#include <memory.h>
+#include <command.h>
+#include <magicvar.h>
+#include <init.h>
+#include <driver.h>
+#include <io.h>
+#include <efi.h>
+#include <malloc.h>
+#include <string.h>
+#include <linux/err.h>
+#include <boot.h>
+#include <fs.h>
+#include <binfmt.h>
+#include <wchar.h>
+#include <mach/efi.h>
+#include <mach/efi-device.h>
+
+static int efi_execute_image(const char *file)
+{
+ void *exe;
+ size_t size;
+ efi_handle_t handle;
+ efi_status_t efiret;
+ const char *options;
+ efi_loaded_image_t *loaded_image;
+
+ exe = read_file(file, &size);
+ if (!exe)
+ return -EINVAL;
+
+ efiret = BS->load_image(false, efi_parent_image, efi_device_path, exe, size,
+ &handle);
+ if (EFI_ERROR(efiret)) {
+ pr_err("failed to LoadImage: %s\n", efi_strerror(efiret));
+ return -efi_errno(efiret);;
+ };
+
+ efiret = BS->open_protocol(handle, &efi_loaded_image_protocol_guid,
+ (void **)&loaded_image,
+ efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(efiret))
+ return -efi_errno(efiret);
+
+ options = linux_bootargs_get();
+ loaded_image->load_options = strdup_char_to_wchar(options);
+ loaded_image->load_options_size = (strlen(options) + 1) * sizeof(wchar_t);
+
+ efiret = BS->start_image(handle, NULL, NULL);
+
+ efi_connect_all();
+ efi_register_devices();
+
+ return 0;
+}
+
+static int do_bootm_efi(struct image_data *data)
+{
+ return efi_execute_image(data->os_file);
+}
+
+static struct image_handler efi_handle_tr = {
+ .name = "EFI Application",
+ .bootm = do_bootm_efi,
+ .filetype = filetype_exe,
+};
+
+static int efi_execute(struct binfmt_hook *b, char *file, int argc, char **argv)
+{
+ return efi_execute_image(file);
+}
+
+static struct binfmt_hook binfmt_efi_hook = {
+ .type = filetype_exe,
+ .hook = efi_execute,
+};
+
+static int efi_register_image_handler(void)
+{
+ register_image_handler(&efi_handle_tr);
+ binfmt_register(&binfmt_efi_hook);
+
+ return 0;
+}
+late_initcall(efi_register_image_handler);
diff --git a/arch/efi/efi/efi.c b/arch/efi/efi/efi.c
new file mode 100644
index 0000000000..48b7d9a438
--- /dev/null
+++ b/arch/efi/efi/efi.c
@@ -0,0 +1,342 @@
+/*
+ * efi.c - barebox EFI payload support
+ *
+ * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <sizes.h>
+#include <memory.h>
+#include <clock.h>
+#include <command.h>
+#include <magicvar.h>
+#include <init.h>
+#include <driver.h>
+#include <ns16550.h>
+#include <io.h>
+#include <efi.h>
+#include <malloc.h>
+#include <string.h>
+#include <linux/err.h>
+#include <boot.h>
+#include <fs.h>
+#include <binfmt.h>
+#include <wchar.h>
+#include <envfs.h>
+#include <mach/efi.h>
+#include <mach/efi-device.h>
+
+efi_runtime_services_t *RT;
+efi_boot_services_t *BS;
+efi_system_table_t *efi_sys_table;
+efi_handle_t efi_parent_image;
+struct efi_device_path *efi_device_path;
+efi_loaded_image_t *efi_loaded_image;
+
+void *efi_get_variable(char *name, efi_guid_t *vendor, int *var_size)
+{
+ efi_status_t efiret;
+ void *buf;
+ unsigned long size = 0;
+ s16 *name16 = strdup_char_to_wchar(name);
+
+ efiret = RT->get_variable(name16, vendor, NULL, &size, NULL);
+
+ if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL) {
+ buf = ERR_PTR(-efi_errno(efiret));
+ goto out;
+ }
+
+ buf = malloc(size);
+ if (!buf) {
+ buf = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ efiret = RT->get_variable(name16, vendor, NULL, &size, buf);
+ if (EFI_ERROR(efiret)) {
+ free(buf);
+ buf = ERR_PTR(-efi_errno(efiret));
+ goto out;
+ }
+
+ if (var_size)
+ *var_size = size;
+
+out:
+ free(name16);
+
+ return buf;
+}
+
+struct efi_boot {
+ u32 attributes;
+ u16 file_path_len;
+ char *description;
+ struct efi_device_path *path;
+ void *binary;
+};
+
+struct efi_boot *efi_get_boot(int num)
+{
+ struct efi_boot *boot = xzalloc(sizeof(*boot));
+ void *buf, *ptr;
+ int size;
+ char *name;
+
+ name = asprintf("Boot%04X", num);
+
+ buf = efi_get_global_var(name, &size);
+
+ free(name);
+
+ if (!buf) {
+ free(boot);
+ return NULL;
+ }
+
+ ptr = buf;
+
+ boot->attributes = *(u32 *)ptr;
+
+ ptr += sizeof(u32);
+
+ boot->file_path_len = *(u16 *)ptr;
+
+ ptr += sizeof(u16);
+
+ boot->description = strdup_wchar_to_char(ptr);
+
+ ptr += (strlen(boot->description) + 1) * 2;
+
+ printf("description: %s\n", boot->description);
+
+ boot->path = memdup(ptr, boot->file_path_len);
+
+ printf("path: %s\n", device_path_to_str(boot->path));
+
+ return boot;
+}
+
+static int misc_init(void)
+{
+ efi_get_boot(1);
+ efi_get_boot(2);
+ efi_get_boot(3);
+
+ return 0;
+}
+late_initcall(misc_init);
+
+const char *efi_strerror(efi_status_t err)
+{
+ const char *str;
+
+ switch (err) {
+ case EFI_SUCCESS: str = "Success"; break;
+ case EFI_LOAD_ERROR: str = "Load Error"; break;
+ case EFI_INVALID_PARAMETER: str = "Invalid Parameter"; break;
+ case EFI_UNSUPPORTED: str = "Unsupported"; break;
+ case EFI_BAD_BUFFER_SIZE: str = "Bad Buffer Size"; break;
+ case EFI_BUFFER_TOO_SMALL: str = "Buffer Too Small"; break;
+ case EFI_NOT_READY: str = "Not Ready"; break;
+ case EFI_DEVICE_ERROR: str = "Device Error"; break;
+ case EFI_WRITE_PROTECTED: str = "Write Protected"; break;
+ case EFI_OUT_OF_RESOURCES: str = "Out of Resources"; break;
+ case EFI_VOLUME_CORRUPTED: str = "Volume Corrupt"; break;
+ case EFI_VOLUME_FULL: str = "Volume Full"; break;
+ case EFI_NO_MEDIA: str = "No Media"; break;
+ case EFI_MEDIA_CHANGED: str = "Media changed"; break;
+ case EFI_NOT_FOUND: str = "Not Found"; break;
+ case EFI_ACCESS_DENIED: str = "Access Denied"; break;
+ case EFI_NO_RESPONSE: str = "No Response"; break;
+ case EFI_NO_MAPPING: str = "No mapping"; break;
+ case EFI_TIMEOUT: str = "Time out"; break;
+ case EFI_NOT_STARTED: str = "Not started"; break;
+ case EFI_ALREADY_STARTED: str = "Already started"; break;
+ case EFI_ABORTED: str = "Aborted"; break;
+ case EFI_ICMP_ERROR: str = "ICMP Error"; break;
+ case EFI_TFTP_ERROR: str = "TFTP Error"; break;
+ case EFI_PROTOCOL_ERROR: str = "Protocol Error"; break;
+ case EFI_INCOMPATIBLE_VERSION: str = "Incompatible Version"; break;
+ case EFI_SECURITY_VIOLATION: str = "Security Violation"; break;
+ case EFI_CRC_ERROR: str = "CRC Error"; break;
+ case EFI_END_OF_MEDIA: str = "End of Media"; break;
+ case EFI_END_OF_FILE: str = "End of File"; break;
+ case EFI_INVALID_LANGUAGE: str = "Invalid Language"; break;
+ case EFI_COMPROMISED_DATA: str = "Compromised Data"; break;
+ default: str = "unknown error";
+ }
+
+ return str;
+}
+
+int efi_errno(efi_status_t err)
+{
+ int ret;
+
+ switch (err) {
+ case EFI_SUCCESS: ret = 0; break;
+ case EFI_LOAD_ERROR: ret = EIO; break;
+ case EFI_INVALID_PARAMETER: ret = EINVAL; break;
+ case EFI_UNSUPPORTED: ret = ENOTSUPP; break;
+ case EFI_BAD_BUFFER_SIZE: ret = EINVAL; break;
+ case EFI_BUFFER_TOO_SMALL: ret = EINVAL; break;
+ case EFI_NOT_READY: ret = EAGAIN; break;
+ case EFI_DEVICE_ERROR: ret = EIO; break;
+ case EFI_WRITE_PROTECTED: ret = EROFS; break;
+ case EFI_OUT_OF_RESOURCES: ret = ENOMEM; break;
+ case EFI_VOLUME_CORRUPTED: ret = EIO; break;
+ case EFI_VOLUME_FULL: ret = ENOSPC; break;
+ case EFI_NO_MEDIA: ret = ENOMEDIUM; break;
+ case EFI_MEDIA_CHANGED: ret = ENOMEDIUM; break;
+ case EFI_NOT_FOUND: ret = ENODEV; break;
+ case EFI_ACCESS_DENIED: ret = EACCES; break;
+ case EFI_NO_RESPONSE: ret = ETIMEDOUT; break;
+ case EFI_NO_MAPPING: ret = EINVAL; break;
+ case EFI_TIMEOUT: ret = ETIMEDOUT; break;
+ case EFI_NOT_STARTED: ret = EINVAL; break;
+ case EFI_ALREADY_STARTED: ret = EINVAL; break;
+ case EFI_ABORTED: ret = EINTR; break;
+ case EFI_ICMP_ERROR: ret = EINVAL; break;
+ case EFI_TFTP_ERROR: ret = EINVAL; break;
+ case EFI_PROTOCOL_ERROR: ret = EPROTO; break;
+ case EFI_INCOMPATIBLE_VERSION: ret = EINVAL; break;
+ case EFI_SECURITY_VIOLATION: ret = EINVAL; break;
+ case EFI_CRC_ERROR: ret = EINVAL; break;
+ case EFI_END_OF_MEDIA: ret = EINVAL; break;
+ case EFI_END_OF_FILE: ret = EINVAL; break;
+ case EFI_INVALID_LANGUAGE: ret = EINVAL; break;
+ case EFI_COMPROMISED_DATA: ret = EINVAL; break;
+ default: ret = EINVAL;
+ }
+
+ return ret;
+}
+
+static struct NS16550_plat ns16550_plat = {
+ .clock = 115200 * 16,
+};
+
+static int efi_console_init(void)
+{
+ add_generic_device("efi-stdio", DEVICE_ID_SINGLE, NULL, 0 , 0, 0, NULL);
+
+ if (IS_ENABLED(CONFIG_ARCH_EFI_REGISTER_COM1))
+ add_ns16550_device(0, 0x3f8, 0x10, IORESOURCE_IO | IORESOURCE_MEM_8BIT,
+ &ns16550_plat);
+
+ return 0;
+}
+console_initcall(efi_console_init);
+
+void reset_cpu(unsigned long addr)
+{
+ BS->exit(efi_parent_image, EFI_SUCCESS, 0, NULL);
+
+ while(1);
+}
+
+extern char image_base[];
+extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[],
+ __barebox_initcalls_end[];
+
+/*
+ * We have a position independent binary generated with -fpic. This function
+ * fixes the linker generated tables.
+ */
+static void fixup_tables(void)
+{
+ initcall_t *initcall;
+ unsigned long offset = (unsigned long)image_base;
+ struct command *cmdtp;
+ struct magicvar *m;
+
+ for (initcall = __barebox_initcalls_start;
+ initcall < __barebox_initcalls_end; initcall++)
+ *initcall += offset;
+
+ for (cmdtp = &__barebox_cmd_start;
+ cmdtp != &__barebox_cmd_end;
+ cmdtp++) {
+ cmdtp->name += offset;
+ cmdtp->cmd += offset;
+ if (cmdtp->complete)
+ cmdtp->complete += offset;
+ if (cmdtp->desc)
+ cmdtp->desc += offset;
+ if (cmdtp->help)
+ cmdtp->help += offset;
+ if (cmdtp->opts)
+ cmdtp->opts += offset;
+ if (cmdtp->aliases)
+ cmdtp->aliases = (void *)cmdtp->aliases + offset;
+ }
+
+ for (m = &__barebox_magicvar_start;
+ m != &__barebox_magicvar_end;
+ m++) {
+ m->name += offset;
+ m->description += offset;
+ }
+}
+
+static int efi_init(void)
+{
+ barebox_set_model("barebox EFI payload");
+
+ defaultenv_append_directory(env_efi);
+
+ return 0;
+}
+device_initcall(efi_init);
+
+/**
+ * efi-main - Entry point for EFI images
+ */
+efi_status_t efi_main(efi_handle_t image, efi_system_table_t *sys_table)
+{
+ void *mem;
+ efi_status_t efiret;
+
+#ifdef DEBUG
+ sys_table->con_out->output_string(sys_table->con_out, L"barebox\n");
+#endif
+
+ BS = sys_table->boottime;
+
+ efi_parent_image = image;
+ efi_sys_table = sys_table;
+ RT = sys_table->runtime;
+
+ efiret = BS->open_protocol(efi_parent_image, &efi_loaded_image_protocol_guid,
+ (void **)&efi_loaded_image,
+ efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (!EFI_ERROR(efiret))
+ BS->handle_protocol(efi_loaded_image->device_handle,
+ &efi_device_path_protocol_guid, (void **)&efi_device_path);
+
+ fixup_tables();
+
+ BS->allocate_pool(efi_loaded_image->image_data_type, SZ_16M, &mem);
+ mem_malloc_init(mem, mem + SZ_16M);
+
+ efi_clocksource_init();
+
+ start_barebox();
+
+ return EFI_SUCCESS;
+}
diff --git a/arch/efi/efi/env-efi/network/eth0-discover b/arch/efi/efi/env-efi/network/eth0-discover
new file mode 100644
index 0000000000..62c31a553c
--- /dev/null
+++ b/arch/efi/efi/env-efi/network/eth0-discover
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+for i in /boot/network-drivers/*; do
+ $i;
+done