[syslinux:firmware] efi: Add Auxiliary Data Vector support

syslinux-bot for Chandramouli Narayanan chandramouli.narayanan at intel.com
Fri Nov 9 09:06:14 PST 2012


Commit-ID:  e5b9b7e91515ecf01fa4d6df06e6408ec48da0d2
Gitweb:     http://www.syslinux.org/commit/e5b9b7e91515ecf01fa4d6df06e6408ec48da0d2
Author:     Chandramouli Narayanan <chandramouli.narayanan at intel.com>
AuthorDate: Thu, 2 Feb 2012 15:19:17 +0000
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Thu, 2 Feb 2012 16:13:12 +0000

efi: Add Auxiliary Data Vector support

Including deleting some ADV code that found its way into core/elflink.

Also, move the __syslinux_adv_ptr and __syslinux_adv_size symbols out
of ldlinux.c32 and into the core. Normally we don't want to move
symbols into the core (as it increases the size), but we do in this
case because the values for these symbols are firmware dependent.

Signed-off-by: Chandramouli Narayanan <chandramouli.narayanan at intel.com>
Signed-off-by: Matt Fleming <matt.fleming at intel.com>

---
 com32/elflink/ldlinux/adv.c       |   19 +--
 com32/elflink/ldlinux/advwrite.c  |    9 +-
 com32/include/syslinux/firmware.h |    7 +
 core/bios.c                       |   31 +++
 core/elflink/advwrite.c           |   45 -----
 core/elflink/setadv.c             |  116 ------------
 efi/adv.c                         |  362 +++++++++++++++++++++++++++++++++++++
 efi/adv.h                         |   29 +++
 efi/efi.h                         |    4 +-
 efi/fio.c                         |  283 +++++++++++++++++++++++++++++
 efi/fio.h                         |   43 +++++
 efi/main.c                        |   25 +++-
 12 files changed, 784 insertions(+), 189 deletions(-)

diff --git a/com32/elflink/ldlinux/adv.c b/com32/elflink/ldlinux/adv.c
index 7869547..cf02d12 100644
--- a/com32/elflink/ldlinux/adv.c
+++ b/com32/elflink/ldlinux/adv.c
@@ -32,24 +32,9 @@
  */
 
 #include <syslinux/adv.h>
-#include <klibc/compiler.h>
-#include <inttypes.h>
-#include <com32.h>
+#include <syslinux/firmware.h>
 
-void *__syslinux_adv_ptr;
-size_t __syslinux_adv_size;
-
-extern void adv_init(void);
 void __syslinux_init(void)
 {
-    static com32sys_t reg;
-
-    /* Initialize the ADV structure */
-    reg.eax.w[0] = 0x0025;
-    __intcall(0x22, &reg, NULL);
-
-    reg.eax.w[0] = 0x001c;
-    __intcall(0x22, &reg, &reg);
-    __syslinux_adv_ptr = MK_PTR(reg.es, reg.ebx.w[0]);
-    __syslinux_adv_size = reg.ecx.w[0];
+	firmware->adv_ops->init();
 }
diff --git a/com32/elflink/ldlinux/advwrite.c b/com32/elflink/ldlinux/advwrite.c
index 4152eea..95a311a 100644
--- a/com32/elflink/ldlinux/advwrite.c
+++ b/com32/elflink/ldlinux/advwrite.c
@@ -32,14 +32,9 @@
  */
 
 #include <syslinux/adv.h>
-#include <klibc/compiler.h>
-#include <com32.h>
+#include <syslinux/firmware.h>
 
 int syslinux_adv_write(void)
 {
-    static com32sys_t reg;
-
-    reg.eax.w[0] = 0x001d;
-    __intcall(0x22, &reg, &reg);
-    return (reg.eflags.l & EFLAGS_CF) ? -1 : 0;
+    return firmware->adv_ops->write();
 }
diff --git a/com32/include/syslinux/firmware.h b/com32/include/syslinux/firmware.h
index d52e34a..3877d62 100644
--- a/com32/include/syslinux/firmware.h
+++ b/com32/include/syslinux/firmware.h
@@ -19,6 +19,11 @@ struct input_ops {
 	char (*getchar)(void);
 };
 
+struct adv_ops {
+	void (*init)(void);
+	int (*write)(void);
+};
+
 struct firmware {
 	void (*init)(void);
 	int (*scan_memory)(scan_memory_callback_t, void *);
@@ -30,10 +35,12 @@ struct firmware {
 	char *(*get_config_file_name)(void);
 	void (*get_serial_console_info)(uint16_t *, uint16_t *, uint16_t *);
 	bool (*ipappend_strings)(char **, int *);
+	struct adv_ops *adv_ops;
 };
 
 extern struct firmware *firmware;
 
 extern void syslinux_register_bios(void);
+extern void init(void);
 
 #endif /* _SYSLINUX_FIRMWARE_H */
diff --git a/core/bios.c b/core/bios.c
index 8725eda..447268d 100644
--- a/core/bios.c
+++ b/core/bios.c
@@ -63,6 +63,36 @@ bool bios_ipappend_strings(char **list, int *count)
 extern char *bios_get_config_file_name(void);
 extern void bios_get_serial_console_info(uint16_t *, uint16_t *, uint16_t *);
 
+void *__syslinux_adv_ptr;
+size_t __syslinux_adv_size;
+
+void bios_adv_init(void)
+{
+    static com32sys_t reg;
+
+    reg.eax.w[0] = 0x0025;
+    __intcall(0x22, &reg, &reg);
+
+    reg.eax.w[0] = 0x001c;
+    __intcall(0x22, &reg, &reg);
+    __syslinux_adv_ptr = MK_PTR(reg.es, reg.ebx.w[0]);
+    __syslinux_adv_size = reg.ecx.w[0];
+}
+
+int bios_adv_write(void)
+{
+    static com32sys_t reg;
+
+    reg.eax.w[0] = 0x001d;
+    __intcall(0x22, &reg, &reg);
+    return (reg.eflags.l & EFLAGS_CF) ? -1 : 0;
+}
+
+struct adv_ops bios_adv_ops = {
+	.init = bios_adv_init,
+	.write = bios_adv_write,
+};
+
 struct firmware bios_fw = {
 	.init = bios_init,
 	.scan_memory = bios_scan_memory,
@@ -74,6 +104,7 @@ struct firmware bios_fw = {
 	.ipappend_strings = bios_ipappend_strings,
 	.get_config_file_name = bios_get_config_file_name,
 	.get_serial_console_info = bios_get_serial_console_info,
+	.adv_ops = &bios_adv_ops,
 };
 
 void syslinux_register_bios(void)
diff --git a/core/elflink/advwrite.c b/core/elflink/advwrite.c
deleted file mode 100644
index 4152eea..0000000
--- a/core/elflink/advwrite.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
- *
- *   Permission is hereby granted, free of charge, to any person
- *   obtaining a copy of this software and associated documentation
- *   files (the "Software"), to deal in the Software without
- *   restriction, including without limitation the rights to use,
- *   copy, modify, merge, publish, distribute, sublicense, and/or
- *   sell copies of the Software, and to permit persons to whom
- *   the Software is furnished to do so, subject to the following
- *   conditions:
- *
- *   The above copyright notice and this permission notice shall
- *   be included in all copies or substantial portions of the Software.
- *
- *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- *   OTHER DEALINGS IN THE SOFTWARE.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * syslinux/advwrite.c
- *
- * Write back the ADV
- */
-
-#include <syslinux/adv.h>
-#include <klibc/compiler.h>
-#include <com32.h>
-
-int syslinux_adv_write(void)
-{
-    static com32sys_t reg;
-
-    reg.eax.w[0] = 0x001d;
-    __intcall(0x22, &reg, &reg);
-    return (reg.eflags.l & EFLAGS_CF) ? -1 : 0;
-}
diff --git a/core/elflink/setadv.c b/core/elflink/setadv.c
deleted file mode 100644
index 40f00a4..0000000
--- a/core/elflink/setadv.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
- *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
- *
- *   Permission is hereby granted, free of charge, to any person
- *   obtaining a copy of this software and associated documentation
- *   files (the "Software"), to deal in the Software without
- *   restriction, including without limitation the rights to use,
- *   copy, modify, merge, publish, distribute, sublicense, and/or
- *   sell copies of the Software, and to permit persons to whom
- *   the Software is furnished to do so, subject to the following
- *   conditions:
- *
- *   The above copyright notice and this permission notice shall
- *   be included in all copies or substantial portions of the Software.
- *
- *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- *   OTHER DEALINGS IN THE SOFTWARE.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * syslinux/setadv.c
- *
- * (Over)write a data item in the auxilliary data vector.  To
- * delete an item, set its length to zero.
- *
- * Return 0 on success, -1 on error, and set errno.
- *
- * NOTE: Data is not written to disk unless
- * syslinux_adv_write() is called.
- */
-
-#include <syslinux/adv.h>
-#include <klibc/compiler.h>
-#include <inttypes.h>
-#include <string.h>
-#include <errno.h>
-#include <alloca.h>
-
-int syslinux_setadv(int tag, size_t size, const void *data)
-{
-    uint8_t *p, *advtmp;
-    size_t rleft, left;
-
-    if ((unsigned)tag - 1 > 254) {
-	errno = EINVAL;
-	return -1;		/* Impossible tag value */
-    }
-
-    if (size > 255) {
-	errno = ENOSPC;		/* Max 255 bytes for a data item */
-	return -1;
-    }
-
-    rleft = left = syslinux_adv_size();
-    p = advtmp = alloca(left);
-    memcpy(p, syslinux_adv_ptr(), left);	/* Make working copy */
-
-    while (rleft >= 2) {
-	uint8_t ptag = p[0];
-	size_t plen = p[1] + 2;
-
-	if (ptag == ADV_END)
-	    break;
-
-	if (ptag == tag) {
-	    /* Found our tag.  Delete it. */
-
-	    if (plen >= rleft) {
-		/* Entire remainder is our tag */
-		break;
-	    }
-	    memmove(p, p + plen, rleft - plen);
-	    rleft -= plen;	/* Fewer bytes to read, but not to write */
-	} else {
-	    /* Not our tag */
-	    if (plen > rleft)
-		break;		/* Corrupt tag (overrun) - overwrite it */
-
-	    left -= plen;
-	    rleft -= plen;
-	    p += plen;
-	}
-    }
-
-    /* Now (p, left) reflects the position to write in and how much space
-       we have for our data. */
-
-    if (size) {
-	if (left < size + 2) {
-	    errno = ENOSPC;	/* Not enough space for data */
-	    return -1;
-	}
-
-	*p++ = tag;
-	*p++ = size;
-	memcpy(p, data, size);
-	p += size;
-	left -= size + 2;
-    }
-
-    memset(p, 0, left);
-
-    /* If we got here, everything went OK, commit the write to low memory */
-    memcpy(syslinux_adv_ptr(), advtmp, syslinux_adv_size());
-
-    return 0;
-}
diff --git a/efi/adv.c b/efi/adv.c
new file mode 100644
index 0000000..3dec3cc
--- /dev/null
+++ b/efi/adv.c
@@ -0,0 +1,362 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2012 Intel Corporation; author: H. Peter Anvin
+ *   Chandramouli Narayanan
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * adv.c
+ *
+ * Core ADV I/O
+ * Code consolidated from libinstaller/adv*.c and core/adv.inc with the
+ * addition of EFI support
+ *
+ * Return 0 on success, -1 on error, and set errno.
+ *
+ */
+#define  _GNU_SOURCE
+
+#include "adv.h"
+
+#define IS_SYSLINUX	/* remove this: test build only */
+
+unsigned char syslinux_adv[2 * ADV_SIZE];
+
+static void cleanup_adv(unsigned char *advbuf)
+{
+    int i;
+    uint32_t csum;
+
+    /* Make sure both copies agree, and update the checksum */
+    *(uint32_t *)advbuf =  ADV_MAGIC1;
+
+    csum = ADV_MAGIC2;
+    for (i = 8; i < ADV_SIZE - 4; i += 4)
+	csum -= *(uint32_t *)(advbuf + i);
+
+    *(uint32_t *)(advbuf + 4) =  csum;
+    *(uint32_t *)(advbuf + ADV_SIZE - 4) =  ADV_MAGIC3;
+
+    memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
+}
+
+void syslinux_reset_adv(unsigned char *advbuf)
+{
+    /* Create an all-zero ADV */
+    memset(advbuf + 2 * 4, 0, ADV_LEN);
+    cleanup_adv(advbuf);
+}
+
+static int adv_consistent(const unsigned char *p)
+{
+    int i;
+    uint32_t csum;
+
+    if (*(uint32_t *)p != ADV_MAGIC1 ||
+	*(uint32_t *)(p + ADV_SIZE - 4) != ADV_MAGIC3)
+	return 0;
+
+    csum = 0;
+    for (i = 4; i < ADV_SIZE - 4; i += 4)
+	csum += *(uint32_t *)(p + i);
+
+    return csum == ADV_MAGIC2;
+}
+
+/*
+ * Verify that an in-memory ADV is consistent, making the copies consistent.
+ * If neither copy is OK, return -1 and call syslinux_reset_adv().
+ */
+int syslinux_validate_adv(unsigned char *advbuf)
+{
+    if (adv_consistent(advbuf + 0 * ADV_SIZE)) {
+	memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
+	return 0;
+    } else if (adv_consistent(advbuf + 1 * ADV_SIZE)) {
+	memcpy(advbuf, advbuf + ADV_SIZE, ADV_SIZE);
+	return 0;
+    } else {
+	syslinux_reset_adv(advbuf);
+	return -1;
+    }
+}
+
+/*
+ * Read the ADV from an existing instance, or initialize if invalid.
+ * Returns -1 on fatal errors, 0 if ADV is okay, 1 if the ADV is
+ * invalid, and 2 if the file does not exist.
+ */
+
+/* make_filespec
+ * Take the ASCII pathname and filename and concatenate them
+ * into an allocated memory space as unicode file specification string.
+ * The path and cfg ASCII strings are assumed to be null-terminated.
+ * For EFI, the separation character in the path name is '\'
+ * and therefore it is assumed that the file spec uses '\' as separation char
+ *
+ * The function returns
+ * 	 0  if successful and fspec is a valid allocated CHAR16 pointer
+ * 	    Caller is responsible to free up the allocated filespec string
+ * 	-1  otherwise
+ *
+ */
+static int make_filespec(CHAR16 **fspec, const char *path, const char *cfg)
+{
+	CHAR16 *p;
+	int size, append;
+
+	/* allocate size for a CHAR16 string */
+	size = sizeof(CHAR16) * (strlena((CHAR8 *)path)+strlena((CHAR8 *)cfg)+2);	/* including null */
+	*fspec = malloc(size);
+	if (!*fspec) return -1;
+
+	append = path[strlena((CHAR8 *)path) - 1] != '\\';
+	for (p = *fspec; *path; path++, p++)
+		*p = (CHAR16)*path;
+	/* append the separation character to the path if need be */
+	if (append) *p++ = (CHAR16)'\\';
+	for (; *cfg; cfg++, p++)
+		*p = (CHAR16)*cfg;
+	*p = (CHAR16)CHAR_NULL;
+
+	return 0;
+}
+
+
+/* TODO:
+ * set_attributes() and clear_attributes() are supported for VFAT only
+ */
+int read_adv(const char *path, const char *cfg)
+{
+    CHAR16 *file;
+    EFI_FILE_HANDLE fd;
+    EFI_FILE_INFO st;
+    int err = 0;
+    int rv;
+
+    rv = make_filespec(&file, path, cfg);
+    if (rv < 0 || !file) {
+	efi_perror(L"read_adv");
+	return -1;
+    }
+
+    /* TBD: Not sure if EFI accepts the attribute read only
+     * even if an existing file is opened for read access
+     */
+    fd = efi_open(file, EFI_FILE_MODE_READ);
+    if (!fd) {
+	if (efi_errno != EFI_NOT_FOUND) {
+	    err = -1;
+	} else {
+	    syslinux_reset_adv(syslinux_adv);
+	    err = 2;		/* Nonexistence is not a fatal error */
+	}
+    } else if (!efi_fstat(fd, &st)) {
+	err = -1;
+    } else if (st.FileSize < 2 * ADV_SIZE) {
+	/* Too small to be useful */
+	syslinux_reset_adv(syslinux_adv);
+	err = 0;		/* Nothing to read... */
+    } else if (efi_xpread(fd, syslinux_adv, 2 * ADV_SIZE,
+		      st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
+	err = -1;
+    } else {
+	/* We got it... maybe? */
+	err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
+    }
+
+    if (err < 0)
+	efi_perror(file);
+    if (fd)
+	efi_close(fd);
+    free(file);
+
+    return err;
+}
+
+/* For EFI platform, initialize ADV by opening ldlinux.sys or extlinux.sys
+ * as configured and return the primary (adv0) and alternate (adv1)
+ * data into caller's buffer. File remains open for subsequent
+ * operations. This routine is to be called from comboot
+ * vector. Currently only IS_SYSLINUX or IS_EXTLINUX is supported
+ *
+ * TODO:
+ * 1. Need to set the path to ldlinux.sys or extlinux.sys; currently null
+ * 2. What if there are errors?
+ */
+void efi_adv_init(void)
+{
+    char *name;
+    int rv;
+    int err = 0;
+    unsigned char *advbuf = syslinux_adv;
+    EFI_FILE_HANDLE	fd;	/* handle to ldlinux.sys or extlinux.sys */
+    CHAR16 *file;
+    EFI_FILE_INFO st, xst;
+
+#if defined IS_SYSLINUX
+    name = SYSLINUX_FILE;
+#elif defined IS_EXTLINUX
+    name = EXTLINUX_FILE;
+#else
+	#error "IS_SYSLINUX or IS_EXTLINUX must be specified to build ADV"
+#endif
+	/* FIXME: No path defined to syslinux/extlinux file */
+    rv = make_filespec(&file, "", name);
+    if (rv < 0 || !file) {
+	efi_errno = EFI_OUT_OF_RESOURCES;
+	efi_perror(L"efi_adv_init:");
+	return;
+    }
+
+    fd = efi_open(file, EFI_FILE_MODE_READ);
+    if (fd == (EFI_FILE_HANDLE)NULL) {
+	err = -1;
+	efi_printerr(L"efi_adv_init: Unable to open file %s\n", file);
+    } else if (efi_fstat(fd, &st)) {
+	err = -1;
+	efi_printerr(L"efi_adv_init: Unable to get info for file %s\n", file);
+    } else if (st.FileSize < 2 * ADV_SIZE) {
+	/* Too small to be useful */
+	err = -2;
+	efi_printerr(L"efi_adv_init: File %s size too small to be useful %s\n", file);
+    } else if (efi_xpread(fd, advbuf, 2 * ADV_SIZE,
+		      st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
+	err = -1;
+	efi_printerr(L"efi_adv_init: Error reading ADV data from file %s\n", file);
+    } else {
+	/* We got it... maybe? */
+	__syslinux_adv_ptr = &syslinux_adv[8]; /* skip head, csum */
+	__syslinux_adv_size = ADV_LEN;
+
+	err = syslinux_validate_adv(advbuf) ? -2 : 0;
+	if (!err) {
+	    /* Got a good one*/
+	    efi_clear_attributes(fd);
+
+	    /* Need to re-open read-write */
+	    efi_close(fd);
+		/* There is no SYNC attribute with EFI open */
+	    fd = efi_open(file, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE);
+		/* on error, only explicit comparison with null handle works */
+	    if (fd == (EFI_FILE_HANDLE)NULL) {
+		err = -1;
+		efi_perror(L"efi_adv_init:");
+	    } else if (efi_fstat(fd, &xst) || xst.FileSize != st.FileSize) {
+			/* device/inode info don't exist in the EFI file info structure */
+		efi_perror(L"efi_adv_init: file status error/mismatch");
+		err = -2;
+	    }
+		/* TODO: Do we need to set attributes of the sys file? */
+	}
+     }
+     if (file)
+	free(file);
+     if (fd != 0)
+	efi_close(fd);
+	/* TODO: In case of errors, we could set efi_errno to EFI_LOAD_ERROR
+	 * to mean that ADV could not be loaded up
+	 */
+}
+
+/* For EFI platform, write 2 * ADV_SIZE data to the file opened
+ * at ADV initialization. (i.e ldlinux.sys or extlinux.sys).
+ *
+ * TODO:
+ * 1. Validate assumption: write back to file from __syslinux_adv_ptr
+ * 2. What if there errors?
+ * 3. Do we need to set the attributes of the sys file?
+ *
+ */
+int efi_adv_write(void)
+{
+    char *name;
+    unsigned char advtmp[2 * ADV_SIZE];
+    unsigned char *advbuf = syslinux_adv;
+    int rv;
+    int err = 0;
+    EFI_FILE_HANDLE	fd;	/* handle to ldlinux.sys or extlinux.sys */
+    CHAR16 *file;
+    EFI_FILE_INFO st, xst;
+
+#if defined IS_SYSLINUX
+    name = SYSLINUX_FILE;
+#elif defined IS_EXTLINUX
+    name = EXTLINUX_FILE;
+#else
+	#error "IS_SYSLINUX or IS_EXTLINUX must be specified to build ADV"
+#endif
+    rv = make_filespec(&file, "", name);
+    if (rv < 0 || !file) {
+	efi_errno = EFI_OUT_OF_RESOURCES;
+	efi_perror(L"efi_adv_write:");
+	return -1;
+    }
+
+    fd = efi_open(file, EFI_FILE_MODE_READ);
+    if (fd == (EFI_FILE_HANDLE)NULL) {
+	err = -1;
+	efi_printerr(L"efi_adv_write: Unable to open file %s\n", file);
+    } else if (efi_fstat(fd, &st)) {
+	err = -1;
+	efi_printerr(L"efi_adv_write: Unable to get info for file %s\n", file);
+    } else if (st.FileSize < 2 * ADV_SIZE) {
+	/* Too small to be useful */
+	err = -2;
+	efi_printerr(L"efi_adv_write: File size too small to be useful for file %s\n", file);
+    } else if (efi_xpread(fd, advtmp, 2 * ADV_SIZE,
+		      st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
+	err = -1;
+	efi_printerr(L"efi_adv_write: Error reading ADV data from file %s\n", file);
+    } else {
+	cleanup_adv(advbuf);
+	err = syslinux_validate_adv(advbuf) ? -2 : 0;
+
+	if (!err) {
+	    /* Got a good one, write our own ADV here */
+	    efi_clear_attributes(fd);
+
+	    /* Need to re-open read-write */
+	    efi_close(fd);
+		/* There is no SYNC attribute with EFI open */
+	    fd = efi_open(file, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE);
+	    if (fd == (EFI_FILE_HANDLE)NULL) {
+		err = -1;
+	    } else if (efi_fstat(fd, &xst) || xst.FileSize != st.FileSize) {
+		efi_perror(L"efi_adv_write: file status error/mismatch");
+		err = -2;
+	    }
+	    /* Write our own version ... */
+	    if (efi_xpwrite(fd, advbuf, 2 * ADV_SIZE,
+			st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
+		err = -1;
+		efi_printerr(L"efi_adv_write: Error write ADV data to file %s\n", file);
+	    }
+	    if (!err) {
+		efi_sync(fd);
+		efi_set_attributes(fd);
+	    }
+	}
+    }
+
+    if (err == -2)
+	efi_printerr(L"%s: cannot write auxilliary data (need --update)?\n",
+		file);
+    else if (err == -1)
+	efi_perror(L"efi_adv_write:");
+
+    if (fd)
+	efi_close(fd);
+    if (file)
+	free(file);
+
+    return err;
+}
diff --git a/efi/adv.h b/efi/adv.h
new file mode 100644
index 0000000..e8ccb35
--- /dev/null
+++ b/efi/adv.h
@@ -0,0 +1,29 @@
+#ifndef _H_EFI_ADV_
+#define _H_EFI_ADV_
+
+#include "efi.h"
+#include "fio.h"
+#include <syslinux/firmware.h>
+
+/* ADV information */
+#define ADV_SIZE	512	/* Total size */
+#define ADV_LEN		(ADV_SIZE-3*4)	/* Usable data size */
+/* Currently, one of IS_SYSLINUX or IS_EXTLINUX	must be defined for ADV */
+#define SYSLINUX_FILE	"ldlinux.sys"
+#define EXTLINUX_FILE	"extlinux.sys"
+
+#define ADV_MAGIC1	0x5a2d2fa5	/* Head signature */
+#define ADV_MAGIC2	0xa3041767	/* Total checksum */
+#define ADV_MAGIC3	0xdd28bf64	/* Tail signature */
+
+extern unsigned char syslinux_adv[2 * ADV_SIZE];
+extern void *__syslinux_adv_ptr;
+extern ssize_t __syslinux_adv_size;
+
+/* TODO: Revisit to ensure if these functions need to be exported */
+void syslinux_reset_adv(unsigned char *advbuf);
+int syslinux_validate_adv(unsigned char *advbuf);
+int read_adv(const char *path, const char *cfg);
+int write_adv(const char *path, const char *cfg);
+
+#endif
diff --git a/efi/efi.h b/efi/efi.h
index ac5ca3f..7e53fb2 100644
--- a/efi/efi.h
+++ b/efi/efi.h
@@ -2,8 +2,10 @@
 #define _SYSLINUX_EFI_H
 
 #include <core.h>
-#include <syslinux/version.h>
+#include <sys/types.h>	/* needed for off_t */
+//#include <syslinux/version.h> /* avoid redefinition of __STDC_VERSION__ */
 #include <efi.h>
 #include <efilib.h>
+#include <efistdarg.h>
 
 #endif /* _SYSLINUX_EFI_H */
diff --git a/efi/fio.c b/efi/fio.c
new file mode 100644
index 0000000..f56cd5b
--- /dev/null
+++ b/efi/fio.c
@@ -0,0 +1,283 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2012 Intel Corporation; author: H. Peter Anvin
+ *   Chandramouli Narayanan
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/* Miscellaneous functions for UEFI support
+ * We assume that EFI library initialization has completed
+ * and we have access to the global EFI exported variables
+ *
+ */
+#include "efi.h"
+#include "fio.h"
+
+/* Variables that need to be exported
+ * efi_errno - maintains the errors from EFI calls to display error messages.
+ */
+EFI_STATUS efi_errno = EFI_SUCCESS;
+
+/* Locals
+ * vol_root - handle to the root device for file operations
+ */
+static EFI_FILE_HANDLE vol_root;
+
+/* Table of UEFI error messages to be indexed with the EFI errno
+ * Update error message list as needed
+ */
+static CHAR16 *uefi_errmsg[] = {
+	L"EFI_UNDEFINED",	/* should not get here */
+	L"EFI_LOAD_ERROR",
+	L"EFI_INVALID_PARAMETER",
+	L"EFI_UNSUPPORTED",
+	L"EFI_BAD_BUFFER_SIZE",
+	L"EFI_BUFFER_TOO_SMALL",
+	L"EFI_NOT_READY",
+	L"EFI_DEVICE_ERROR",
+	L"EFI_WRITE_PROTECTED",
+	L"EFI_OUT_OF_RESOURCES",
+	L"EFI_VOLUME_CORRUPTED",
+	L"EFI_VOLUME_FULL",
+	L"EFI_NO_MEDIA",
+	L"EFI_MEDIA_CHANGED",
+	L"EFI_NOT_FOUND",
+	L"EFI_ACCESS_DENIED",
+	L"EFI_NO_RESPONSE",
+	L"EFI_NO_MAPPING",
+	L"EFI_TIMEOUT",
+	L"EFI_NOT_STARTED",
+	L"EFI_ALREADY_STARTED",
+	L"EFI_ABORTED",
+	L"EFI_ICMP_ERROR",
+	L"EFI_TFTP_ERROR",
+	L"EFI_PROTOCOL_ERROR"
+};
+
+static UINTN nerrs = sizeof(uefi_errmsg)/sizeof(CHAR16 *);
+
+
+/* Generic write error message; there is no gnu lib api to write to StdErr
+ * For now, everything goes ConOut
+ */
+void efi_printerr(
+    CHAR16   *fmt,
+    ...
+    )
+{
+    va_list     args;
+    va_start (args, fmt);
+    VPrint (fmt, args);
+    va_end (args);
+}
+
+/* Simple console logger of efi-specific error messages. It uses
+ * gnu-efi library Print function to do the job.
+ */
+
+void efi_perror(CHAR16 *prog)
+{
+	/* Ensure that the err number lies within range
+	 * Beware: unsigned comparisons fail on efi, signed comparisons work
+	 */
+	if (EFI_ERROR(efi_errno) && (INTN)efi_errno < (INTN)nerrs)
+		efi_printerr(L"%s: %s\n", prog, uefi_errmsg[efi_errno]);
+}
+
+/* Write to UEFI ConOut */
+void efi_printout(
+    CHAR16   *fmt,
+    ...
+    )
+{
+    va_list     args;
+    va_start (args, fmt);
+    VPrint (fmt, args);
+    va_end (args);
+}
+
+/* IMPORTANT:
+ * efi_setvol_root() needs to be called from efi main.
+ * The rest of the ADV support relies on the file i/o environment
+ * setup here. In order to use the EFI file support, we need
+ * to set up the volume root. Subsequent file operations need the root to
+ * access the interface routines.
+ *
+ */
+
+EFI_STATUS efi_set_volroot(EFI_HANDLE device_handle)
+{
+	vol_root = LibOpenRoot(device_handle);
+	if (!vol_root) {
+		return EFI_DEVICE_ERROR;
+	}
+	return EFI_SUCCESS;
+}
+
+/* File operations using EFI runtime services */
+
+/* Open the file using EFI runtime service
+ * Opening a file in EFI requires a handle to the device
+ * root in order to use the interface to the file operations supported by UEFI.
+ * For now, assume device volume root handle from the loaded image
+ *
+ * Return a valid handle if open succeeded and null otherwise.
+ * UEFI returns a bogus handle on error, so return null handle on error.
+ *
+ * TODO:
+ * 1. Validate the assumption about the root device
+ * 2. Can EFI open a file with full path name specification?
+ * 3. Look into gnu-efi helper functions for dealing with device path/file path
+ * 4. Consider utilizing EFI file open attributes.
+ * 5. In EFI, file attributes can be specified only at the time of creation.
+ * How do we support the equivalent of set_attributes() and clear_attributes()
+ */
+EFI_FILE_HANDLE efi_open(CHAR16 *file, UINT64 mode)
+{
+	/* initialize with NULL handle since EFI open returns bogus */
+	EFI_FILE_HANDLE	fd = NULL;
+
+	ASSERT(vol_root);
+
+	/* Note that the attributes parameter is none for now */
+	efi_errno = uefi_call_wrapper(vol_root->Open,
+					5,
+					vol_root,
+					&fd,
+					file,
+					mode,
+					0);
+	return fd;
+}
+
+/*
+ * read/write wrapper functions for UEFI
+ *
+ * Read or write the specified number of bytes starting at the
+ * offset specified.
+ *
+ * Returns:
+ * number of bytes read/written on success
+ * -1 on error
+ */
+/* Wrapper function to read from a file */
+size_t efi_xpread(EFI_FILE_HANDLE fd, void *buf, size_t count, off_t offset)
+{
+	ASSERT(fd);
+	efi_errno = uefi_call_wrapper(fd->SetPosition,
+					2,
+				    fd,
+				    offset);
+	if (EFI_ERROR(efi_errno)) return -1;
+	efi_errno = uefi_call_wrapper(fd->Read,
+					3,
+				    fd,
+				    &count,
+					buf);
+	if (EFI_ERROR(efi_errno)) return -1;
+	return count;
+}
+
+/* Wrapper function to write */
+size_t efi_xpwrite(EFI_FILE_HANDLE fd, void *buf, size_t count, off_t offset)
+{
+	ASSERT(fd);
+	efi_errno = uefi_call_wrapper(fd->SetPosition,
+					2,
+				    fd,
+				    offset);
+	if (EFI_ERROR(efi_errno)) return -1;
+	efi_errno = uefi_call_wrapper(fd->Write,
+					3,
+				    fd,
+				    &count,
+					buf);
+	if (EFI_ERROR(efi_errno)) return -1;
+	return count;
+}
+
+/* For an open handle, return the generic file info excluding
+ * the variable-length filename in the EFI_FILE_INFO structure.
+ */
+int efi_fstat(EFI_FILE_HANDLE fd, EFI_FILE_INFO *st)
+{
+	EFI_FILE_INFO *finfo;
+
+	ASSERT(fd);
+	finfo = LibFileInfo(fd);
+	if (finfo) {
+		uefi_call_wrapper(BS->CopyMem, 3, (VOID *)st, (VOID *)finfo, SIZE_OF_EFI_FILE_INFO);
+		FreePool(finfo);
+		return 0;
+	}
+	/* gnu-efi lib does not return EFI status; export a generic device error for now */
+	efi_errno = EFI_DEVICE_ERROR;
+	return -1;
+}
+
+/* set/clear_attributes()
+ * 	Currently handles only VFAT filesystem
+ * TODO:
+ *    1. Assumes VFAT file system.
+ *    2. How do we support other file systems?
+ */
+void efi_set_attributes(EFI_FILE_HANDLE fd)
+{
+	EFI_FILE_INFO *finfo;
+
+	ASSERT(fd);
+	finfo = LibFileInfo(fd);
+	if (finfo) {
+		/* Hidden+System+Readonly */
+		finfo->Attribute = EFI_FILE_READ_ONLY|EFI_FILE_HIDDEN|EFI_FILE_SYSTEM;
+		efi_errno = uefi_call_wrapper(fd->SetInfo,
+					4,
+					fd,
+					&GenericFileInfo,
+					finfo->Size,
+					finfo);
+		FreePool(finfo);
+	} else efi_errno = EFI_NOT_FOUND;
+}
+
+void efi_clear_attributes(EFI_FILE_HANDLE fd)
+{
+	EFI_FILE_INFO *finfo;
+
+	ASSERT(fd);
+	finfo = LibFileInfo(fd);
+	if (finfo) {
+		finfo->Attribute = 0; /* no attributes */
+		efi_errno = uefi_call_wrapper(fd->SetInfo, 
+					4, 
+					fd,
+					&GenericFileInfo,
+					finfo->Size,
+					finfo);
+		FreePool(finfo);
+	} else efi_errno = EFI_NOT_FOUND;
+}
+
+/* Implement the sync operation using the EFI Flush file operation*/
+void efi_sync(EFI_FILE_HANDLE fd)
+{
+	ASSERT(fd);
+	efi_errno = uefi_call_wrapper(fd->Flush, 1, fd);
+	return;
+}
+
+/* Close the file */
+void efi_close(EFI_FILE_HANDLE fd)
+{
+
+	ASSERT(fd);
+	efi_errno = uefi_call_wrapper(fd->Close, 1, fd);
+	return;
+}
diff --git a/efi/fio.h b/efi/fio.h
new file mode 100644
index 0000000..65fff8d
--- /dev/null
+++ b/efi/fio.h
@@ -0,0 +1,43 @@
+#ifndef _H_EFI_FIO_
+#define _H_EFI_FIO_
+
+/*
+ * Friendly interfaces for EFI file I/O and various EFI support functions
+ */
+
+/* MAX_EFI_ARGS - command line args for EFI executable
+ * WS(c16) 	- check for CHAR16 white space
+ */
+#define MAX_EFI_ARGS		64
+#define WS(c16)         (c16 == L' ' || c16 == CHAR_TAB)
+
+/* VPrint is not in export declarations in gnu-efi lib yet
+ * although it is a global function; declare it here
+ */
+extern UINTN
+VPrint (
+    IN CHAR16   *fmt,
+    va_list     args
+    );
+
+extern EFI_STATUS efi_errno;
+
+void efi_memcpy(unsigned char *dst, unsigned char *src, size_t len);
+void efi_memmove(unsigned char *dst, unsigned char *src, size_t len);
+void efi_memset(unsigned char *dst, unsigned char val, size_t len);
+void *efi_alloc(int size);
+void efi_free(void *ptr);
+void efi_perror(CHAR16 *str);
+void efi_printerr(IN CHAR16 *fmt, ...);
+void efi_printout(IN CHAR16 *fmt, ...);
+EFI_STATUS efi_set_volroot(EFI_HANDLE device_handle);
+EFI_FILE_HANDLE efi_open(CHAR16 *file, UINT64 mode);
+void efi_close(EFI_FILE_HANDLE fd);
+void efi_sync(EFI_FILE_HANDLE fd);
+size_t efi_xpread(EFI_FILE_HANDLE fd, void *buf, size_t count, off_t offset);
+size_t efi_xpwrite(EFI_FILE_HANDLE fd, void *buf, size_t count, off_t offset);
+int efi_fstat(EFI_FILE_HANDLE fd, EFI_FILE_INFO *st);
+void efi_set_attributes(EFI_FILE_HANDLE fd);
+void efi_clear_attributes(EFI_FILE_HANDLE fd);
+
+#endif
diff --git a/efi/main.c b/efi/main.c
index 5b92acd..4afa7d4 100644
--- a/efi/main.c
+++ b/efi/main.c
@@ -6,6 +6,7 @@
 #include <sys/ansi.h>
 
 #include "efi.h"
+#include "fio.h"
 
 char KernelName[FILENAME_MAX];
 uint16_t PXERetry;
@@ -52,8 +53,9 @@ void __cdecl core_farcall(uint32_t c, const com32sys_t *a, com32sys_t *b)
 {
 }
 
-void *__syslinux_adv_ptr; /* definitely needs to die: is in ldlinux now */
-size_t __syslinux_adv_size; /* definitely needs to die: is in ldlinux now */
+struct firmware *firmware = NULL;
+void *__syslinux_adv_ptr;
+size_t __syslinux_adv_size;
 char core_xfer_buf[65536];
 struct iso_boot_info {
 	uint32_t pvd;               /* LBA of primary volume descriptor */
@@ -260,6 +262,14 @@ bool efi_ipappend_strings(char **list, int *count)
 	*list = (char *)IPAppends;
 }
 
+extern void efi_adv_init(void);
+extern int efi_adv_write(void);
+
+struct adv_ops efi_adv_ops = {
+	.init = efi_adv_init,
+	.write = efi_adv_write,
+};
+
 extern struct disk *efi_disk_init(com32sys_t *);
 extern void serialcfg(uint16_t *, uint16_t *, uint16_t *);
 
@@ -272,6 +282,7 @@ struct firmware efi_fw = {
 	.get_config_file_name = efi_get_config_file_name,
 	.get_serial_console_info = serialcfg,
 	.ipappend_strings = efi_ipappend_strings,
+	.adv_ops = &efi_adv_ops,
 };
 
 static inline void syslinux_register_efi(void)
@@ -302,10 +313,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 	status = uefi_call_wrapper(BS->HandleProtocol, 3, image,
 				   &LoadedImageProtocol, (void **)&info);
 	if (status != EFI_SUCCESS) {
-		printf("Failed to lookup LoadedImageProtocol\n");
+		Print(L"Failed to lookup LoadedImageProtocol\n");
 		goto out;
 	}
 
+	/* Use device handle to set up the volume root to proceed with ADV init */
+	if (EFI_ERROR(efi_set_volroot(info->DeviceHandle))) {
+		Print(L"Failed to locate root device to prep for file operations & ADV initialization\n");
+		goto out;
+	}
+
+	/* TODO: once all errors are captured in efi_errno, bail out if necessary */
+
 	/* XXX figure out what file system we're on */
 	fs_init(ops, info->DeviceHandle);
 	load_env32();


More information about the Syslinux-commits mailing list